xwin-tools.lisp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. ;;;; xwin-tools.lisp
  2. (in-package #:xwin)
  3. (defvar *display* nil)
  4. (defmacro ensure-display (&body body)
  5. (let ((display-sym (gensym "DISPLAY-")))
  6. `(let ((,display-sym nil))
  7. (when (null *display*)
  8. (setf ,display-sym (xlib:open-default-display)))
  9. (unwind-protect
  10. (let ((*display* (or *display* ,display-sym)))
  11. (progn
  12. ,@body))
  13. (when ,display-sym
  14. (xlib:close-display ,display-sym))))))
  15. (defun root ()
  16. (xlib:screen-root (xlib:display-default-screen *display*)))
  17. (defstruct winfo
  18. id
  19. desktop
  20. pid
  21. x
  22. y
  23. width
  24. height
  25. class
  26. machine
  27. title)
  28. (defun parse-wmctrl-line (string)
  29. (destructuring-bind (id desktop pid x y width height class machine title) (cl-ppcre:split " +" string :limit 10)
  30. (make-winfo :id (parse-integer id :start 2 :radix 16)
  31. :desktop (parse-integer desktop)
  32. :pid (parse-integer pid)
  33. :x (parse-integer x)
  34. :y (parse-integer y)
  35. :width (parse-integer width)
  36. :height (parse-integer height)
  37. :class class
  38. :machine machine
  39. :title title)))
  40. (defun xwin:all-windows ()
  41. (mapcar #'parse-wmctrl-line (uiop:run-program '("wmctrl" "-pGlx") :output :lines)))
  42. (defun id-for-xlib (id)
  43. (xlib::make-window :id id :display *display*))
  44. (defun xwin:raise (id)
  45. (ensure-display
  46. (let ((w (id-for-xlib id)))
  47. (xlib:set-input-focus *display* w :parent)
  48. (setf (xlib:window-priority w) :above))
  49. (xlib:display-finish-output *display*)
  50. id))
  51. (defun xwin:active ()
  52. (first (ensure-display
  53. (xlib:get-property (root) :_NET_ACTIVE_WINDOW))))
  54. (defmacro while-timeout ((timeout &optional (delay 0.1)) &body body)
  55. (let ((result-sym (gensym "RESULT-"))
  56. (delay-sym (gensym "DELAY-")))
  57. `(loop with ,delay-sym = ,delay
  58. repeat (1+ (floor ,timeout ,delay-sym))
  59. for ,result-sym = (progn
  60. ,@body)
  61. if ,result-sym do (return ,result-sym) else do (sleep ,delay-sym)
  62. finally (return ,result-sym))))
  63. (defun xwin:by-pid (pid &key (machine (machine-instance)) (timeout 0))
  64. (while-timeout (timeout)
  65. (loop for w in (all-windows)
  66. when (and (= (winfo-pid w) pid)
  67. (string= (winfo-machine w) machine))
  68. do (return (winfo-id w))
  69. finally (return nil))))
  70. (defun xwin:maximize (id)
  71. (ensure-display
  72. (xlib:send-event (root) :client-message '(:substructure-notify)
  73. :window (id-for-xlib id)
  74. :format 32
  75. :data '(2 394 395 1)
  76. :type :_NET_WM_STATE)
  77. (xlib:display-finish-output *display*)))
  78. (defun xwin:all-clients ()
  79. (ensure-display
  80. (values (xlib:get-property (root) :_NET_CLIENT_LIST))))
  81. (defun xwin:user-time (window)
  82. (ensure-display
  83. (let* ((time-window (first (xlib:get-property (id-for-xlib window)
  84. :_NET_WM_USER_TIME_WINDOW)))
  85. (true-time-window (or time-window window)))
  86. (or (first (xlib:get-property (id-for-xlib true-time-window)
  87. :_NET_WM_USER_TIME))
  88. 0))))
  89. (defun xwin:clients-stacking ()
  90. (ensure-display
  91. (values (xlib:get-property (root) :_NET_CLIENT_LIST_STACKING))))
  92. (defun xwin:pid (id)
  93. (ensure-display
  94. (first (xlib:get-property (id-for-xlib id) :_NET_WM_PID))))
  95. (defun xwin:title (id)
  96. (let ((octets (ensure-display
  97. (or (xlib:get-property (id-for-xlib id) :_NET_WM_NAME
  98. :result-type '(vector (unsigned-byte 8)))
  99. (xlib:get-property (id-for-xlib id) :WM_NAME
  100. :result-type '(vector (unsigned-byte 8)))))))
  101. (if (null octets)
  102. ""
  103. (babel:octets-to-string octets :encoding :utf-8))))
  104. (defun xwin:name (id)
  105. (let* ((octets (ensure-display (xlib:get-property (id-for-xlib id) :WM_CLASS
  106. :result-type '(vector (unsigned-byte 8)))))
  107. (zero-pos (position 0 octets)))
  108. (babel:octets-to-string octets :start (1+ zero-pos) :end (1- (length octets)) :encoding :utf-8)))