WindowsManager.kt 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. /*
  2. * Copyright (c) Milan Jurkulák 2024.
  3. * Contact:
  4. * e: mimoccc@gmail.com
  5. * e: mj@mjdev.org
  6. * w: https://mjdev.org
  7. */
  8. @file:Suppress("LocalVariableName")
  9. package eu.mjdev.desktop.managers
  10. import com.sun.jna.platform.unix.X11.*
  11. import eu.mjdev.desktop.data.App
  12. import eu.mjdev.desktop.provider.DesktopProvider
  13. import kotlinx.coroutines.*
  14. @Suppress("unused")
  15. class WindowsManager(
  16. val api: DesktopProvider,
  17. val scope: CoroutineScope = api.scope,
  18. ) {
  19. // private var loop: Job? = null
  20. // private val x11: X11 by lazy { Native.load("X11", X11::class.java) }
  21. // private val x11Ext by lazy { Native.load("X11", X11Ext::class.java) }
  22. // private val display: Display by lazy { x11.XOpenDisplay(null) }
  23. // private val rootWindow: Window
  24. // get() = x11.XDefaultRootWindow(display)
  25. // val activeWindow
  26. // get() = get_active_window()
  27. // val windows = getAllWindows(rootWindow, 0)
  28. // val environment: Environment = Environment()
  29. // val currentDisplay
  30. // get() = environment["WAYLAND_DISPLAY"]
  31. // val WlRegistryEvents = object : WlRegistryEvents {
  32. // override fun global(emitter: WlRegistryProxy?, name: Int, interface_: String, version: Int) {
  33. // println("Got global event: $name")
  34. // }
  35. //
  36. // override fun globalRemove(emitter: WlRegistryProxy?, name: Int) {
  37. // println("Got global remove event: $name")
  38. // }
  39. // }
  40. // val display: WlDisplayProxy by lazy { WlDisplayProxy.connect(currentDisplay) }
  41. // val registry: WlRegistryProxy by lazy { display.getRegistry(WlRegistryEvents) }
  42. // todo
  43. val windows
  44. get() = findWindows()
  45. // todo
  46. private val activeWindow: SystemWindow?
  47. get() = null
  48. // todo
  49. private fun findWindows(app: App? = null): List<SystemWindow> = listOf<SystemWindow>().let { list ->
  50. if (app == null) list else list.filter { w -> w.windowClass == app.windowClass }
  51. }
  52. // init {
  53. // println("Current display : $currentDisplay")
  54. // println("Registry : ${registry.id}")
  55. // }
  56. fun dispose() {
  57. // loop?.cancel()
  58. // x11.XCloseDisplay(display)
  59. }
  60. // fun findWindows(app: App): List<SystemWindow> = windows.filter { w ->
  61. // println("w: (${w.id},${w.windowClass}, ${w.command})")
  62. // w.windowClass.isNotEmpty()
  63. // }.filter { w ->
  64. // w.windowClass.trim().contains(app.windowClass, true) ||
  65. // w.command.trim().contains(app.windowClass, true) ||
  66. // w.command.trim().contains(app.fullAppName, true) ||
  67. // w.command.trim().contentEquals(app.cmd, true)
  68. // }.apply {
  69. // println("app[${app.windowClass}] : ${count()}")
  70. // }
  71. fun hasWindow(app: App): Boolean =
  72. findWindows(app).isNotEmpty()
  73. fun isWindowFocus(app: App): Boolean {
  74. val appWindow = windows.firstOrNull { it.windowClass.contentEquals(app.windowClass, true) }
  75. return (appWindow != null && activeWindow != null && appWindow.id == activeWindow?.id)
  76. }
  77. @Suppress("UNUSED_PARAMETER", "CanBeVal", "KotlinConstantConditions")
  78. fun minimizeWindows(app: App): Boolean {
  79. var isMinimized = false
  80. // findWindows(app).forEach { sw ->
  81. // isMinimized = isMinimized or minimizeWindow(sw.window)
  82. // }
  83. return isMinimized
  84. }
  85. @Suppress("UNUSED_PARAMETER", "CanBeVal", "KotlinConstantConditions")
  86. fun requestWindowsFocus(app: App): Boolean {
  87. var isRequested = false
  88. // findWindows(app).forEach { sw ->
  89. // isRequested = isRequested or activateWindow(sw.window)
  90. // }
  91. return isRequested
  92. }
  93. @Suppress("unused", "UNUSED_PARAMETER", "KotlinConstantConditions", "CanBeVal")
  94. fun closeWindows(app: App): Boolean {
  95. var isClosed = false
  96. // findWindows(app).forEach { sw ->
  97. // isClosed = isClosed or closeWindow(sw.window)
  98. // }
  99. return isClosed
  100. }
  101. // @Suppress("DEPRECATED_IDENTITY_EQUALS")
  102. // private fun getAllWindows(root: Window, depth: Int): List<SystemWindow> {
  103. // val windowList = mutableListOf<SystemWindow>()
  104. // val windowRef = WindowByReference()
  105. // val parentRef = WindowByReference()
  106. // val childrenRef = PointerByReference()
  107. // val childCountRef = IntByReference()
  108. // x11.XQueryTree(display, root, windowRef, parentRef, childrenRef, childCountRef)
  109. // if (childrenRef.value == null) return emptyList()
  110. // val ids: LongArray
  111. // if (Native.LONG_SIZE === java.lang.Long.BYTES) {
  112. // ids = childrenRef.value.getLongArray(0, childCountRef.value)
  113. // } else if (Native.LONG_SIZE === Integer.BYTES) {
  114. // val intIds: IntArray = childrenRef.value.getIntArray(0, childCountRef.value)
  115. // ids = LongArray(intIds.size)
  116. // for (i in intIds.indices) {
  117. // ids[i] = intIds[i].toLong()
  118. // }
  119. // } else return emptyList()
  120. // for (id in ids) {
  121. // if (id == 0L) continue
  122. // Window(id).also { window ->
  123. // val pid = getWindowPid(window) ?: 0L
  124. //// val name = getWindowName(window) ?: ""
  125. // val cls = getWindowClass(window) ?: ""
  126. // val desk = getWindowDesktop(window) ?: 0L
  127. // val process = ProcessHandle.allProcesses().toList().firstOrNull { it.pid() == pid }
  128. //// val icon = getWindowIconName(window) ?: ""
  129. // windowList.add(
  130. // SystemWindow(
  131. // id,
  132. //// name,
  133. //// icon,
  134. // pid,
  135. // cls,
  136. // desk,
  137. // process?.info()?.command()?.get().orEmpty(),
  138. // )
  139. // )
  140. // getAllWindows(window, depth + 1)
  141. // }
  142. // }
  143. // return windowList
  144. // }
  145. // private fun getWindowPid(
  146. // window: Window
  147. // ) = get_property_as_long(
  148. // window,
  149. // XA_CARDINAL,
  150. // "_NET_WM_PID"
  151. // )
  152. // @Suppress("unused")
  153. // private fun getWindowName(
  154. // window: Window
  155. // ) = get_property_as_utf8_string(
  156. // window,
  157. // x11.XInternAtom(display, "UTF8_STRING", false),
  158. // "_NET_WM_NAME"
  159. // )
  160. // private fun getWindowClass(
  161. // window: Window
  162. // ) = get_property_as_utf8_string(
  163. // window,
  164. // x11.XInternAtom(display, "UTF8_STRING", false),
  165. // "WM_CLASS"
  166. // ) ?: get_property_as_string(
  167. // window,
  168. // XA_STRING,
  169. // "WM_CLASS"
  170. // )
  171. // @Suppress("unused")
  172. // private fun getWindowCommand(
  173. // pid: Long
  174. // ): String = ProcessHandle.allProcesses().toList().firstOrNull {
  175. // it.pid() == pid
  176. // }?.info()?.command()?.getOrNull().orEmpty()
  177. // private fun getWindowDesktop(
  178. // window: Window
  179. // ) = get_property_as_long(
  180. // window,
  181. // XA_CARDINAL,
  182. // "_NET_SHOWING_DESKTOP"
  183. // )
  184. // @Suppress("unused")
  185. // private fun getWindowIconName(
  186. // window: Window
  187. // ): String? = PointerByReference().let { icon_name_return ->
  188. // if (x11Ext.XGetIconName(display, window, icon_name_return) == 0) {
  189. // g_free(icon_name_return.pointer)
  190. // null
  191. // } else {
  192. // g_strdup(icon_name_return.pointer)
  193. // }
  194. // }
  195. // private fun minimizeWindow(
  196. // window: Window
  197. // ): Boolean = x11Ext.XIconifyWindow(display, window, x11.XDefaultScreen(display)) == TRUE
  198. // private fun activateWindow(
  199. // window: Window,
  200. // switchDesktop: Boolean = true
  201. // ) = activate_window(window, switchDesktop)
  202. // private fun closeWindow(
  203. // window: Window
  204. // ) = client_msg(window, "_NET_CLOSE_WINDOW", 0, 0, 0, 0, 0)
  205. // @Suppress("unused")
  206. // private fun g_free(
  207. // pointer: Pointer?
  208. // ) {
  209. // if (pointer != null) x11.XFree(pointer)
  210. // }
  211. // @Suppress("SameParameterValue")
  212. // private fun get_property_as_long(
  213. // window: Window,
  214. // xa_prop_type: Atom,
  215. // prop_name: String
  216. // ): Long? = get_property_as_long(window, xa_prop_type, prop_name, null)
  217. // @Suppress("SameParameterValue")
  218. // private fun get_property_as_long(
  219. // window: Window,
  220. // xa_prop_type: Atom,
  221. // prop_name: String,
  222. // size: NativeLongByReference? = null
  223. // ): Long? {
  224. // var longProp: Long? = null
  225. // val prop = get_property(window, xa_prop_type, prop_name, size)
  226. // if (prop != null) {
  227. // longProp = prop.getLong(0)
  228. //// g_free(prop)
  229. // }
  230. // return longProp
  231. // }
  232. // @Suppress("SameParameterValue")
  233. // private fun get_property_as_int(
  234. // win: Window,
  235. // xa_prop_type: Atom,
  236. // prop_name: String
  237. // ): Int? {
  238. // var intProp: Int? = null
  239. // val prop = get_property(win, xa_prop_type, prop_name, null)
  240. // if (prop != null) {
  241. // intProp = prop.getInt(0)
  242. //// g_free(prop)
  243. // }
  244. // return intProp
  245. // }
  246. // @Suppress("SameParameterValue")
  247. // private fun get_property_as_string(
  248. // win: Window,
  249. // xa_prop_type: Atom,
  250. // prop_name: String
  251. // ): String? {
  252. // var strProp: String? = null
  253. // val prop = get_property(
  254. // win, xa_prop_type, prop_name,
  255. // null
  256. // )
  257. // if (prop != null) {
  258. // strProp = g_strdup(prop)
  259. //// g_free(prop)
  260. // }
  261. // return strProp
  262. // }
  263. // private fun g_strdup(
  264. // pointer: Pointer
  265. // ): String {
  266. // val value = pointer.getString(0)
  267. // // g_free(pointer);
  268. // return value
  269. // }
  270. // private fun g_locale_to_utf8(
  271. // pointer: Pointer
  272. // ): String = g_strdup(pointer)
  273. // @Suppress("SameParameterValue")
  274. // private fun get_property_as_utf8_string(
  275. // window: Window,
  276. // xa_prop_type: Atom,
  277. // prop_name: String
  278. // ): String? {
  279. // var strProp: String? = null
  280. // val prop = get_property(window, xa_prop_type, prop_name, null)
  281. // if (prop != null) {
  282. // strProp = g_locale_to_utf8(prop)
  283. //// g_free(prop)
  284. // }
  285. // return strProp
  286. // }
  287. // @Suppress("SameParameterValue")
  288. // private fun get_property_as_window(
  289. // window: Window,
  290. // xa_prop_type: Atom,
  291. // prop_name: String
  292. // ): Window? {
  293. // var ret: Window? = null
  294. // val prop = get_property(window, xa_prop_type, prop_name, null)
  295. // if (prop != null) {
  296. // ret = Window(prop.getLong(0))
  297. //// g_free(prop)
  298. // }
  299. // return ret
  300. // }
  301. // private fun get_property(
  302. // window: Window?,
  303. // xa_prop_type: Atom,
  304. // prop_name: String?,
  305. // size: NativeLongByReference?
  306. // ): Pointer? {
  307. // val xa_ret_type = AtomByReference()
  308. // val ret_format = IntByReference()
  309. // val ret_n_items = NativeLongByReference()
  310. // val ret_bytes_after = NativeLongByReference()
  311. // val ret_prop = PointerByReference()
  312. // val xa_prop_name = x11.XInternAtom(display, prop_name, false)
  313. // if (x11.XGetWindowProperty(
  314. // display,
  315. // window,
  316. // xa_prop_name,
  317. // NativeLong(0),
  318. // NativeLong((MAX_PROPERTY_VALUE_LEN / 4).toLong()),
  319. // false,
  320. // xa_prop_type,
  321. // xa_ret_type,
  322. // ret_format,
  323. // ret_n_items,
  324. // ret_bytes_after,
  325. // ret_prop
  326. // ) != Success
  327. // ) {
  328. // return null
  329. // }
  330. // if ((xa_ret_type.value == null) || (xa_ret_type.value.toLong() != xa_prop_type.toLong())
  331. // ) {
  332. //// g_free(ret_prop.pointer)
  333. // return null
  334. // }
  335. // if (size != null) {
  336. // var tmp_size = ((ret_format.value / 8) * ret_n_items.value.toLong())
  337. // if (ret_format.value == 32) {
  338. // tmp_size *= (NativeLong.SIZE / 4).toLong()
  339. // }
  340. // size.value = NativeLong(tmp_size)
  341. // }
  342. // return ret_prop.value
  343. // }
  344. // @Suppress("SameParameterValue")
  345. // private fun client_msg(
  346. // window: Window,
  347. // msg: String,
  348. // data0: Long,
  349. // data1: Long,
  350. // data2: Long,
  351. // data3: Long,
  352. // data4: Long
  353. // ): Boolean {
  354. // val mask = NativeLong((SubstructureRedirectMask or SubstructureNotifyMask).toLong())
  355. // val xclient = XClientMessageEvent()
  356. // xclient.type = ClientMessage
  357. // xclient.serial = NativeLong(0)
  358. // xclient.send_event = TRUE
  359. // xclient.message_type = x11.XInternAtom(display, msg, false)
  360. // xclient.window = window
  361. // xclient.format = 32
  362. // xclient.data.setType(Array<NativeLong>::class.java)
  363. // xclient.data.l[0] = NativeLong(data0)
  364. // xclient.data.l[1] = NativeLong(data1)
  365. // xclient.data.l[2] = NativeLong(data2)
  366. // xclient.data.l[3] = NativeLong(data3)
  367. // xclient.data.l[4] = NativeLong(data4)
  368. // val event = XEvent()
  369. // event.setTypedValue(xclient)
  370. // return if (x11.XSendEvent(
  371. // display,
  372. // x11.XDefaultRootWindow(display),
  373. // FALSE,
  374. // mask,
  375. // event
  376. // ) != FALSE
  377. // ) EXIT_SUCCESS else EXIT_FAILURE
  378. // }
  379. // private fun activate_window(
  380. // window: Window,
  381. // switch_desktop: Boolean = true
  382. // ): Boolean {
  383. // var desktop: Int? = get_property_as_int(window, XA_CARDINAL, "_NET_WM_DESKTOP")
  384. // if (desktop == null) {
  385. // desktop = get_property_as_int(window, XA_CARDINAL, "_WIN_WORKSPACE")
  386. // }
  387. // if (switch_desktop && (desktop != null)) {
  388. // client_msg(
  389. // x11.XDefaultRootWindow(display),
  390. // "_NET_CURRENT_DESKTOP",
  391. // desktop.toLong(),
  392. // 0,
  393. // 0,
  394. // 0,
  395. // 0
  396. // )
  397. // }
  398. // client_msg(window, "_NET_ACTIVE_WINDOW", 0, 0, 0, 0, 0)
  399. // x11.XMapRaised(display, window)
  400. // return EXIT_SUCCESS
  401. // }
  402. // private fun get_active_window(): Window? = get_property_as_window(
  403. // x11.XDefaultRootWindow(display),
  404. // XA_WINDOW,
  405. // "_NET_ACTIVE_WINDOW"
  406. // )
  407. // companion object {
  408. // const val MAX_PROPERTY_VALUE_LEN = 4096
  409. // const val TRUE: Int = 1
  410. // const val FALSE: Int = 0
  411. // const val EXIT_SUCCESS: Boolean = true
  412. // const val EXIT_FAILURE: Boolean = false
  413. // }
  414. data class SystemWindow(
  415. val id: Long,
  416. val pid: Long,
  417. val windowClass: String,
  418. val desktop: Long,
  419. // val name: String,
  420. // val iconName: String,
  421. val command: String,
  422. ) {
  423. companion object {
  424. val SystemWindow.window: Window get() = Window(id)
  425. }
  426. }
  427. // private interface Xmu : Library {
  428. // fun XmuClientWindow(display: Display?, window: Window?): Window?
  429. // }
  430. // private interface X11Ext : Library {
  431. // fun XMoveWindow(
  432. // display: Display?,
  433. // window: Window?,
  434. // x: Long,
  435. // y: Long
  436. // )
  437. // fun XResizeWindow(
  438. // display: Display?,
  439. // window: Window?,
  440. // width: Long,
  441. // height: Long
  442. // )
  443. // fun XMoveResizeWindow(
  444. // display: Display?,
  445. // window: Window?,
  446. // x: Long,
  447. // y: Long,
  448. // width: Long,
  449. // height: Long
  450. // )
  451. // fun XCreateFontCursor(
  452. // display: Display?,
  453. // shape: Long
  454. // ): Cursor?
  455. // fun XGrabPointer(
  456. // display: Display?,
  457. // grab_window: Window?,
  458. // owner_events: Int,
  459. // event_mask: NativeLong?,
  460. // pointer_mode: Int,
  461. // keyboard_mode: Int,
  462. // confine_to: Window?,
  463. // cursor: Cursor?,
  464. // time: Int
  465. // ): Int
  466. // fun XAllowEvents(
  467. // display: Display?,
  468. // event_mode: Int,
  469. // time: Int
  470. // ): Int
  471. // fun XUngrabPointer(
  472. // display: Display?,
  473. // time: Int
  474. // ): Int
  475. // fun XmuClientWindow(
  476. // display: Display?,
  477. // window: Window?
  478. // ): Window?
  479. // fun XGetIconName(
  480. // display: Display?,
  481. // window: Window?,
  482. // icon_name_return: PointerByReference?
  483. // ): Int
  484. // fun XIconifyWindow(
  485. // display: Display?,
  486. // window: Window?,
  487. // screen: Int
  488. // ): Int
  489. // }
  490. }