1 ;;; --------------------------------------------------------------------------
2 ;;; CLFSWM - FullScreen Window Manager
4 ;;; --------------------------------------------------------------------------
5 ;;; Documentation: Utility functions
6 ;;; --------------------------------------------------------------------------
8 ;;; (C) 2012 Philippe Brochard <pbrochard@common-lisp.net>
10 ;;; This program is free software; you can redistribute it and/or modify
11 ;;; it under the terms of the GNU General Public License as published by
12 ;;; the Free Software Foundation; either version 3 of the License, or
13 ;;; (at your option) any later version.
15 ;;; This program is distributed in the hope that it will be useful,
16 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;;; GNU General Public License for more details.
20 ;;; You should have received a copy of the GNU General Public License
21 ;;; along with this program; if not, write to the Free Software
22 ;;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 ;;; --------------------------------------------------------------------------
31 (defconstant +withdrawn-state
+ 0)
32 (defconstant +normal-state
+ 1)
33 (defconstant +iconic-state
+ 3)
36 (defparameter *window-events
* '(:structure-notify
44 "The events to listen for on managed windows.")
47 (defparameter +netwm-supported
+
48 '(:_NET_SUPPORTING_WM_CHECK
49 :_NET_NUMBER_OF_DESKTOPS
50 :_NET_DESKTOP_GEOMETRY
51 :_NET_DESKTOP_VIEWPORT
55 "Supported NETWM properties.
56 Window types are in +WINDOW-TYPES+.")
58 (defparameter +netwm-window-types
+
59 '((:_NET_WM_WINDOW_TYPE_DESKTOP .
:desktop
)
60 (:_NET_WM_WINDOW_TYPE_DOCK .
:dock
)
61 (:_NET_WM_WINDOW_TYPE_TOOLBAR .
:toolbar
)
62 (:_NET_WM_WINDOW_TYPE_MENU .
:menu
)
63 (:_NET_WM_WINDOW_TYPE_UTILITY .
:utility
)
64 (:_NET_WM_WINDOW_TYPE_SPLASH .
:splash
)
65 (:_NET_WM_WINDOW_TYPE_DIALOG .
:dialog
)
66 (:_NET_WM_WINDOW_TYPE_NORMAL .
:normal
))
67 "Alist mapping NETWM window types to keywords.")
72 (defparameter *x-error-count
* 0)
73 (defparameter *max-x-error-count
* 10000)
74 (defparameter *clfswm-x-error-filename
* "/tmp/clfswm-backtrace.error")
77 (defmacro with-xlib-protect
((&optional name tag
) &body body
)
78 "Ignore Xlib errors in body."
80 (with-simple-restart (top-level "Return to clfswm's top level")
83 (incf *x-error-count
*)
84 (when (> *x-error-count
* *max-x-error-count
*)
85 (format t
"Xlib error: ~A ~A: ~A~%" ,name
(if ,tag
,tag
',body
) c
)
87 (write-backtrace *clfswm-x-error-filename
*
88 (format nil
"~%------- Additional information ---------
95 (error "Too many X errors: ~A (logged in ~A)" c
*clfswm-x-error-filename
*))
98 (format t
"Xlib error: ~A ~A: ~A~%" ,name
(if ,tag
,tag
',body
) c
)
102 ;;(defmacro with-xlib-protect ((&optional name tag) &body body)
108 (defmacro with-x-pointer
(&body body
)
109 "Bind (x y) to mouse pointer positions"
110 `(multiple-value-bind (x y
)
111 (xlib:query-pointer
*root
*)
116 (declaim (inline window-x2 window-y2
))
117 (defun window-x2 (window)
118 (+ (x-drawable-x window
) (x-drawable-width window
)))
120 (defun window-y2 (window)
121 (+ (x-drawable-y window
) (x-drawable-height window
)))
126 ;;; Events management functions.
128 (defparameter *unhandled-events
* nil
)
129 (defparameter *current-event-mode
* nil
)
131 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
132 (defun keyword->handle-event
(mode keyword
)
133 (create-symbol 'handle-event-fun
"-" mode
"-" keyword
)))
135 (defun handle-event->keyword
(symbol)
136 (let* ((name (string-downcase (symbol-name symbol
)))
137 (pos (search "handle-event-fun-" name
)))
138 (when (and pos
(zerop pos
))
139 (let ((pos-mod (search "mode" name
)))
141 (intern (string-upcase (subseq name
(+ pos-mod
5))) :keyword
))))))
142 ;; (values (intern (string-upcase (subseq name (+ pos-mod 5))) :keyword)
143 ;; (subseq name (length "handle-event-fun-") (1- pos-mod))))))))
145 (defparameter *handle-event-fun-symbols
* nil
)
147 (defun fill-handle-event-fun-symbols ()
148 (with-all-internal-symbols (symbol :clfswm
)
149 (let ((pos (symbol-search "handle-event-fun-" symbol
)))
150 (when (and pos
(zerop pos
))
151 (pushnew symbol
*handle-event-fun-symbols
*)))))
154 (defmacro with-handle-event-symbol
((mode) &body body
)
155 "Bind symbol to all handle event functions available in mode"
156 `(let ((pattern (format nil
"handle-event-fun-~A" ,mode
)))
157 (dolist (symbol *handle-event-fun-symbols
*)
158 (let ((pos (symbol-search pattern symbol
)))
159 (when (and pos
(zerop pos
))
163 (defun find-handle-event-function (&optional
(mode ""))
164 "Print all handle event functions available in mode"
165 (with-handle-event-symbol (mode)
168 (defun assoc-keyword-handle-event (mode)
169 "Associate all keywords in mode to their corresponding handle event functions.
170 For example: main-mode :key-press is bound to handle-event-fun-main-mode-key-press"
171 (setf *current-event-mode
* mode
)
172 (with-handle-event-symbol (mode)
173 (let ((keyword (handle-event->keyword symbol
)))
174 (when (fboundp symbol
)
177 (format t
"~&Associating: ~S with ~S~%" symbol keyword
)
179 (setf (symbol-function keyword
) (symbol-function symbol
))))))
181 (defun unassoc-keyword-handle-event (&optional
(mode ""))
182 "Unbound all keywords from their corresponding handle event functions."
183 (setf *current-event-mode
* nil
)
184 (with-handle-event-symbol (mode)
185 (let ((keyword (handle-event->keyword symbol
)))
186 (when (fboundp keyword
)
189 (format t
"~&Unassociating: ~S ~S~%" symbol keyword
)
191 (fmakunbound keyword
)))))
193 (defmacro define-handler
(mode keyword args
&body body
)
194 "Like a defun but with a name expanded as handle-event-fun-'mode'-'keyword'
195 For example (define-handler main-mode :key-press (args) ...)
196 Expand in handle-event-fun-main-mode-key-press"
197 `(defun ,(keyword->handle-event mode keyword
) (&rest event-slots
&key
#+:event-debug event-key
,@args
&allow-other-keys
)
198 (declare (ignorable event-slots
))
199 #+:event-debug
(print (list *current-event-mode
* event-key
))
203 (defun event-hook-name (event-keyword)
204 (create-symbol-in-package :clfswm
'*event- event-keyword
'-hook
*))
206 (let ((event-hook-list nil
))
207 (defun get-event-hook-list ()
210 (defmacro use-event-hook
(event-keyword)
211 (let ((symb (event-hook-name event-keyword
)))
212 (pushnew symb event-hook-list
)
213 `(defvar ,symb nil
)))
215 (defun unuse-event-hook (event-keyword)
216 (let ((symb (event-hook-name event-keyword
)))
217 (setf event-hook-list
(remove symb event-hook-list
))
220 (defmacro add-event-hook
(name &rest value
)
221 (let ((symb (event-hook-name name
)))
222 `(add-hook ,symb
,@value
)))
224 (defmacro remove-event-hook
(name &rest value
)
225 (let ((symb (event-hook-name name
)))
226 `(remove-hook ,symb
,@value
)))
228 (defun clear-event-hooks ()
229 (dolist (symb event-hook-list
)
233 (defun optimize-event-hook ()
234 "Remove unused event hooks"
235 (dolist (symb event-hook-list
)
236 (when (and (boundp symb
)
237 (null (symbol-value symb
)))
239 (setf event-hook-list
(remove symb event-hook-list
))))))
243 (defmacro define-event-hook
(event-keyword args
&body body
)
244 (let ((event-fun (gensym)))
245 `(let ((,event-fun
(lambda (&rest event-slots
&key
#+:event-debug event-key
,@args
&allow-other-keys
)
246 (declare (ignorable event-slots
))
247 #+:event-debug
(print (list ,event-keyword event-key
))
249 (add-event-hook ,event-keyword
,event-fun
)
253 (defmacro event-defun
(name args
&body body
)
254 `(defun ,name
(&rest event-slots
&key
#+:event-debug event-key
,@args
&allow-other-keys
)
255 (declare (ignorable event-slots
))
256 #+:event-debug
(print (list ,event-keyword event-key
))
259 (defun exit-handle-event ()
260 (throw 'exit-handle-event nil
))
263 (defun handle-event (&rest event-slots
&key event-key
&allow-other-keys
)
264 (labels ((make-xlib-window (xobject)
265 "For some reason the clx xid cache screws up returns pixmaps when
266 they should be windows. So use this function to make a window out of them."
267 ;; Workaround for pixmap error taken from STUMPWM - thanks:
268 ;; XXX: In both the clisp and sbcl clx libraries, sometimes what
269 ;; should be a window will be a pixmap instead. In this case, we
270 ;; need to manually translate it to a window to avoid breakage
271 ;; in stumpwm. So far the only slot that seems to be affected is
272 ;; the :window slot for configure-request and reparent-notify
273 ;; events. It appears as though the hash table of XIDs and clx
274 ;; structures gets out of sync with X or perhaps X assigns a
275 ;; duplicate ID for a pixmap and a window.
276 #+clisp
(make-instance 'xlib
:window
:id
(slot-value xobject
'xlib
::id
) :display
*display
*)
277 #+(or sbcl ecl openmcl
) (xlib::make-window
:id
(slot-value xobject
'xlib
::id
) :display
*display
*)
278 #-
(or sbcl clisp ecl openmcl
)
279 (error 'not-implemented
)))
281 (catch 'exit-handle-event
282 (let ((win (getf event-slots
:window
)))
283 (when (and win
(not (xlib:window-p win
)))
284 (dbg "Pixmap Workaround! Should be a window: " win
)
285 (setf (getf event-slots
:window
) (make-xlib-window win
))))
286 (let ((hook-symbol (event-hook-name event-key
)))
287 (when (boundp hook-symbol
)
288 (apply #'call-hook
(symbol-value hook-symbol
) event-slots
)))
289 (if (fboundp event-key
)
290 (apply event-key event-slots
)
291 #+:event-debug
(pushnew (list *current-event-mode
* event-key
) *unhandled-events
* :test
#'equal
))
292 (xlib:display-finish-output
*display
*))
293 ((or xlib
:window-error xlib
:drawable-error
) (c)
294 #-xlib-debug
(declare (ignore c
))
295 #+xlib-debug
(format t
"Ignore Xlib synchronous error: ~a~%" c
)))
302 (defun parse-display-string (display)
303 "Parse an X11 DISPLAY string and return the host and display from it."
304 (let* ((colon (position #\
: display
))
305 (host (subseq display
0 colon
))
306 (rest (subseq display
(1+ colon
)))
307 (dot (position #\. rest
))
308 (num (parse-integer (subseq rest
0 dot
))))
312 ;;; Transparency support
313 (let ((opaque #xFFFFFFFF
))
314 (defun window-transparency (window)
315 "Return the window transparency"
316 (float (/ (or (first (xlib:get-property window
:_NET_WM_WINDOW_OPACITY
)) opaque
) opaque
)))
318 (defun set-window-transparency (window value
)
319 "Set the window transparency"
320 (when (numberp value
)
321 (xlib:change-property window
:_NET_WM_WINDOW_OPACITY
322 (list (max (min (round (* opaque
(if (equal *transparent-background
* t
) value
1)))
327 (defsetf window-transparency set-window-transparency
))
331 (defun maxmin-size-equal-p (window)
332 (when (xlib:window-p window
)
333 (let ((hints (xlib:wm-normal-hints window
)))
335 (let ((hint-x (xlib:wm-size-hints-x hints
))
336 (hint-y (xlib:wm-size-hints-y hints
))
337 (user-specified-position-p (xlib:wm-size-hints-user-specified-position-p hints
))
338 (min-width (xlib:wm-size-hints-min-width hints
))
339 (min-height (xlib:wm-size-hints-min-height hints
))
340 (max-width (xlib:wm-size-hints-max-width hints
))
341 (max-height (xlib:wm-size-hints-max-height hints
)))
342 (and hint-x hint-y min-width max-width min-height max-height
343 user-specified-position-p
344 (= hint-x
0) (= hint-y
0)
345 (= min-width max-width
)
346 (= min-height max-height
)))))))
348 (defun maxmin-size-equal-window-in-tree ()
349 (dolist (win (xlib:query-tree
*root
*))
350 (when (maxmin-size-equal-p win
)
356 (defun window-state (win)
357 "Get the state (iconic, normal, withdrawn) of a window."
358 (first (xlib:get-property win
:WM_STATE
)))
361 (defun set-window-state (win state
)
362 "Set the state (iconic, normal, withdrawn) of a window."
363 (xlib:change-property win
369 (defsetf window-state set-window-state
)
373 (defun window-hidden-p (window)
374 (eql (window-state window
) +iconic-state
+))
379 (defun unhide-window (window)
381 (when (window-hidden-p window
)
382 (xlib:map-window window
)
383 (setf (window-state window
) +normal-state
+
384 (xlib:window-event-mask window
) *window-events
*))))
387 (defun map-window (window)
389 (xlib:map-window window
)))
392 (defun delete-window (window)
393 (send-client-message window
:WM_PROTOCOLS
394 (xlib:intern-atom
*display
* :WM_DELETE_WINDOW
)))
396 (defun destroy-window (window)
397 (xlib:kill-client
*display
* (xlib:window-id window
)))
400 ;;(defconstant +exwm-atoms+
401 ;; (list "_NET_SUPPORTED" "_NET_CLIENT_LIST"
402 ;; "_NET_CLIENT_LIST_STACKING" "_NET_NUMBER_OF_DESKTOPS"
403 ;; "_NET_CURRENT_DESKTOP" "_NET_DESKTOP_GEOMETRY"
404 ;; "_NET_DESKTOP_VIEWPORT" "_NET_DESKTOP_NAMES"
405 ;; "_NET_ACTIVE_WINDOW" "_NET_WORKAREA"
406 ;; "_NET_SUPPORTING_WM_CHECK" "_NET_VIRTUAL_ROOTS"
407 ;; "_NET_DESKTOP_LAYOUT"
409 ;; "_NET_RESTACK_WINDOW" "_NET_REQUEST_FRAME_EXTENTS"
410 ;; "_NET_MOVERESIZE_WINDOW" "_NET_CLOSE_WINDOW"
411 ;; "_NET_WM_MOVERESIZE"
413 ;; "_NET_WM_SYNC_REQUEST" "_NET_WM_PING"
415 ;; "_NET_WM_NAME" "_NET_WM_VISIBLE_NAME"
416 ;; "_NET_WM_ICON_NAME" "_NET_WM_VISIBLE_ICON_NAME"
417 ;; "_NET_WM_DESKTOP" "_NET_WM_WINDOW_TYPE"
418 ;; "_NET_WM_STATE" "_NET_WM_STRUT"
419 ;; "_NET_WM_ICON_GEOMETRY" "_NET_WM_ICON"
420 ;; "_NET_WM_PID" "_NET_WM_HANDLED_ICONS"
421 ;; "_NET_WM_USER_TIME" "_NET_FRAME_EXTENTS"
422 ;; ;; "_NET_WM_MOVE_ACTIONS"
424 ;; "_NET_WM_WINDOW_TYPE_DESKTOP" "_NET_WM_STATE_MODAL"
425 ;; "_NET_WM_WINDOW_TYPE_DOCK" "_NET_WM_STATE_STICKY"
426 ;; "_NET_WM_WINDOW_TYPE_TOOLBAR" "_NET_WM_STATE_MAXIMIZED_VERT"
427 ;; "_NET_WM_WINDOW_TYPE_MENU" "_NET_WM_STATE_MAXIMIZED_HORZ"
428 ;; "_NET_WM_WINDOW_TYPE_UTILITY" "_NET_WM_STATE_SHADED"
429 ;; "_NET_WM_WINDOW_TYPE_SPLASH" "_NET_WM_STATE_SKIP_TASKBAR"
430 ;; "_NET_WM_WINDOW_TYPE_DIALOG" "_NET_WM_STATE_SKIP_PAGER"
431 ;; "_NET_WM_WINDOW_TYPE_NORMAL" "_NET_WM_STATE_HIDDEN"
432 ;; "_NET_WM_STATE_FULLSCREEN"
433 ;; "_NET_WM_STATE_ABOVE"
434 ;; "_NET_WM_STATE_BELOW"
435 ;; "_NET_WM_STATE_DEMANDS_ATTENTION"
437 ;; "_NET_WM_ALLOWED_ACTIONS"
438 ;; "_NET_WM_ACTION_MOVE"
439 ;; "_NET_WM_ACTION_RESIZE"
440 ;; "_NET_WM_ACTION_SHADE"
441 ;; "_NET_WM_ACTION_STICK"
442 ;; "_NET_WM_ACTION_MAXIMIZE_HORZ"
443 ;; "_NET_WM_ACTION_MAXIMIZE_VERT"
444 ;; "_NET_WM_ACTION_FULLSCREEN"
445 ;; "_NET_WM_ACTION_CHANGE_DESKTOP"
446 ;; "_NET_WM_ACTION_CLOSE"
451 ;;(defun intern-atoms (display)
452 ;; (declare (type xlib:display display))
453 ;; (mapcar #'(lambda (atom-name) (xlib:intern-atom display atom-name))
459 ;;(defun get-atoms-property (window property-atom atom-list-p)
460 ;; "Returns a list of atom-name (if atom-list-p is t) otherwise returns
461 ;; a list of atom-id."
462 ;; (xlib:get-property window property-atom
463 ;; :transform (when atom-list-p
465 ;; (xlib:atom-name (xlib:drawable-display window) id)))))
467 ;;(defun set-atoms-property (window atoms property-atom &key (mode :replace))
468 ;; "Sets the property designates by `property-atom'. ATOMS is a list of atom-id
469 ;; or a list of keyword atom-names."
470 ;; (xlib:change-property window property-atom atoms :ATOM 32
472 ;; :transform (unless (integerp (car atoms))
473 ;; (lambda (atom-key)
474 ;; (xlib:find-atom (xlib:drawable-display window) atom-key)))))
479 ;;(defun net-wm-state (window)
480 ;; (get-atoms-property window :_NET_WM_STATE t))
482 ;;(defsetf net-wm-state (window &key (mode :replace)) (states)
483 ;; `(set-atoms-property ,window ,states :_NET_WM_STATE :mode ,mode))
486 (defun hide-window (window)
488 (setf (window-state window
) +iconic-state
+
489 (xlib:window-event-mask window
) (remove :structure-notify
*window-events
*))
490 (xlib:unmap-window window
)
491 (setf (xlib:window-event-mask window
) *window-events
*)))
495 (defun window-type (window)
496 "Return one of :desktop, :dock, :toolbar, :utility, :splash,
497 :dialog, :transient, :maxsize and :normal."
498 (or (and (let ((hints (xlib:wm-normal-hints window
)))
499 (and hints
(or (and (xlib:wm-size-hints-max-width hints
)
500 (< (xlib:wm-size-hints-max-width hints
) (x-drawable-width *root
*)))
501 (and (xlib:wm-size-hints-max-height hints
)
502 (< (xlib:wm-size-hints-max-height hints
) (x-drawable-height *root
*)))
503 (xlib:wm-size-hints-min-aspect hints
)
504 (xlib:wm-size-hints-max-aspect hints
))))
506 (let ((net-wm-window-type (xlib:get-property window
:_NET_WM_WINDOW_TYPE
)))
507 (when net-wm-window-type
508 (dolist (type-atom net-wm-window-type
)
509 (when (assoc (xlib:atom-name
*display
* type-atom
) +netwm-window-types
+)
510 (return (cdr (assoc (xlib:atom-name
*display
* type-atom
) +netwm-window-types
+)))))))
511 (and (xlib:get-property window
:WM_TRANSIENT_FOR
)
518 ;;; Stolen from Eclipse
519 (defun send-configuration-notify (window x y w h bw
)
520 "Send a synthetic configure notify event to the given window (ICCCM 4.1.5)"
521 (xlib:send-event window
:configure-notify
(xlib:make-event-mask
:structure-notify
)
532 (defun send-client-message (window type
&rest data
)
533 "Send a client message to a client's window."
534 (xlib:send-event window
545 (defun raise-window (window)
546 "Map the window if needed and bring it to the top of the stack. Does not affect focus."
547 (when (xlib:window-p window
)
548 (when (window-hidden-p window
)
549 (unhide-window window
))
550 (setf (xlib:window-priority window
) :above
)))
554 "don't focus any window but still read keyboard events."
555 (xlib:set-input-focus
*display
* *no-focus-window
* :pointer-root
))
557 (defun focus-window (window)
558 "Give the window focus."
560 (when (xlib:window-p window
)
561 (xlib:set-input-focus
*display
* window
:parent
)))
563 (defun raise-and-focus-window (window)
565 (raise-window window
)
566 (focus-window window
))
569 (defun lower-window (window sibling
)
570 "Map the window if needed and bring it just above sibling. Does not affect focus."
571 (when (xlib:window-p window
)
572 (when (window-hidden-p window
)
573 (unhide-window window
))
574 (setf (xlib:window-priority window sibling
) :below
)))
579 (let ((cursor-font nil
)
581 (pointer-grabbed nil
))
582 (defun free-grab-pointer ()
584 (xlib:free-cursor cursor
)
587 (xlib:close-font cursor-font
)
588 (setf cursor-font nil
)))
590 (defun xgrab-init-pointer ()
591 (setf pointer-grabbed nil
))
593 (defun xgrab-pointer-p ()
596 (defun xgrab-pointer (root cursor-char cursor-mask-char
597 &optional
(pointer-mask '(:enter-window
:pointer-motion
598 :button-press
:button-release
)) owner-p
)
599 "Grab the pointer and set the pointer shape."
600 (when pointer-grabbed
602 (setf pointer-grabbed t
)
603 (let* ((white (xlib:make-color
:red
1.0 :green
1.0 :blue
1.0))
604 (black (xlib:make-color
:red
0.0 :green
0.0 :blue
0.0)))
606 (setf cursor-font
(xlib:open-font
*display
* "cursor")
607 cursor
(xlib:create-glyph-cursor
:source-font cursor-font
608 :source-char
(or cursor-char
68)
609 :mask-font cursor-font
610 :mask-char
(or cursor-mask-char
69)
613 (xlib:grab-pointer root pointer-mask
614 :owner-p owner-p
:sync-keyboard-p nil
:sync-pointer-p nil
:cursor cursor
))
616 (xlib:grab-pointer root pointer-mask
617 :owner-p owner-p
:sync-keyboard-p nil
:sync-pointer-p nil
)))))
619 (defun xungrab-pointer ()
620 "Remove the grab on the cursor and restore the cursor shape."
621 (setf pointer-grabbed nil
)
622 (xlib:ungrab-pointer
*display
*)
623 (xlib:display-finish-output
*display
*)
624 (free-grab-pointer)))
627 (let ((keyboard-grabbed nil
))
628 (defun xgrab-init-keyboard ()
629 (setf keyboard-grabbed nil
))
631 (defun xgrab-keyboard-p ()
634 (defun xgrab-keyboard (root)
635 (setf keyboard-grabbed t
)
636 (xlib:grab-keyboard root
:owner-p nil
:sync-keyboard-p nil
:sync-pointer-p nil
))
639 (defun xungrab-keyboard ()
640 (setf keyboard-grabbed nil
)
641 (xlib:ungrab-keyboard
*display
*)))
648 (defun ungrab-all-buttons (window)
649 (xlib:ungrab-button window
:any
:modifiers
:any
))
651 (defun grab-all-buttons (window)
652 (ungrab-all-buttons window
)
653 (xlib:grab-button window
:any
'(:button-press
:button-release
:pointer-motion
)
657 :sync-keyboard-p nil
))
659 (defun ungrab-all-keys (window)
660 (xlib:ungrab-key window
:any
:modifiers
:any
))
664 (defmacro with-grab-keyboard-and-pointer
((cursor mask old-cursor old-mask
&optional ungrab-main
) &body body
)
665 `(let ((pointer-grabbed (xgrab-pointer-p))
666 (keyboard-grabbed (xgrab-keyboard-p)))
667 (xgrab-pointer *root
* ,cursor
,mask
)
668 (unless keyboard-grabbed
671 (xgrab-keyboard *root
*))
677 (xgrab-pointer *root
* ,old-cursor
,old-mask
)
679 (unless keyboard-grabbed
682 (xungrab-keyboard))))))
685 (defmacro with-grab-pointer
((&optional cursor-char cursor-mask-char
) &body body
)
686 `(let ((pointer-grabbed-p (xgrab-pointer-p)))
687 (unless pointer-grabbed-p
688 (xgrab-pointer *root
* ,cursor-char
,cursor-mask-char
))
692 (unless pointer-grabbed-p
693 (xungrab-pointer)))))
699 (defun stop-button-event ()
700 (xlib:allow-events
*display
* :sync-pointer
))
702 (defun replay-button-event ()
703 (xlib:allow-events
*display
* :replay-pointer
))
713 ;;; Mouse action on window
714 (let (add-fn add-arg dx dy window
)
715 (define-handler move-window-mode
:motion-notify
(root-x root-y
)
716 (unless (compress-motion-notify)
718 (multiple-value-bind (move-x move-y
)
719 (apply add-fn add-arg
)
721 (setf (x-drawable-x window
) (+ root-x dx
)))
723 (setf (x-drawable-y window
) (+ root-y dy
))))
724 (setf (x-drawable-x window
) (+ root-x dx
)
725 (x-drawable-y window
) (+ root-y dy
)))))
727 (define-handler move-window-mode
:key-release
()
728 (throw 'exit-move-window-mode nil
))
730 (define-handler move-window-mode
:button-release
()
731 (throw 'exit-move-window-mode nil
))
733 (defun move-window (orig-window orig-x orig-y
&optional additional-fn additional-arg
)
734 (setf window orig-window
736 add-arg additional-arg
737 dx
(- (x-drawable-x window
) orig-x
)
738 dy
(- (x-drawable-y window
) orig-y
)
739 (xlib:window-border window
) (get-color *color-move-window
*))
740 (raise-window window
)
741 (with-grab-pointer ()
743 (apply additional-fn additional-arg
))
744 (generic-mode 'move-window-mode
'exit-move-window-mode
745 :original-mode
'(main-mode)))))
748 (let (add-fn add-arg window
750 orig-width orig-height
752 min-height max-height
)
753 (define-handler resize-window-mode
:motion-notify
(root-x root-y
)
754 (unless (compress-motion-notify)
756 (multiple-value-bind (resize-w resize-h
)
757 (apply add-fn add-arg
)
759 (setf (x-drawable-width window
) (min (max (+ orig-width
(- root-x o-x
)) 10 min-width
) max-width
)))
761 (setf (x-drawable-height window
) (min (max (+ orig-height
(- root-y o-y
)) 10 min-height
) max-height
))))
762 (setf (x-drawable-width window
) (min (max (+ orig-width
(- root-x o-x
)) 10 min-width
) max-width
)
763 (x-drawable-height window
) (min (max (+ orig-height
(- root-y o-y
)) 10 min-height
) max-height
)))))
765 (define-handler resize-window-mode
:key-release
()
766 (throw 'exit-resize-window-mode nil
))
768 (define-handler resize-window-mode
:button-release
()
769 (throw 'exit-resize-window-mode nil
))
771 (defun resize-window (orig-window orig-x orig-y
&optional additional-fn additional-arg
)
772 (let* ((hints (xlib:wm-normal-hints orig-window
)))
773 (setf window orig-window
775 add-arg additional-arg
778 orig-width
(x-drawable-width window
)
779 orig-height
(x-drawable-height window
)
780 min-width
(or (and hints
(xlib:wm-size-hints-min-width hints
)) 0)
781 min-height
(or (and hints
(xlib:wm-size-hints-min-height hints
)) 0)
782 max-width
(or (and hints
(xlib:wm-size-hints-max-width hints
)) most-positive-fixnum
)
783 max-height
(or (and hints
(xlib:wm-size-hints-max-height hints
)) most-positive-fixnum
)
784 (xlib:window-border window
) (get-color *color-move-window
*))
785 (raise-window window
)
786 (with-grab-pointer ()
788 (apply additional-fn additional-arg
))
789 (generic-mode 'resize-window-mode
'exit-resize-window-mode
790 :original-mode
'(main-mode))))))
794 (define-handler wait-mouse-button-release-mode
:button-release
()
795 (throw 'exit-wait-mouse-button-release-mode nil
))
797 (defun wait-mouse-button-release (&optional cursor-char cursor-mask-char
)
798 (with-grab-pointer (cursor-char cursor-mask-char
)
799 (generic-mode 'wait-mouse-button-release
'exit-wait-mouse-button-release-mode
)))
805 (let ((color-hash (make-hash-table :test
'equal
)))
806 (defun get-color (color)
807 (multiple-value-bind (val foundp
)
808 (gethash color color-hash
)
811 (setf (gethash color color-hash
)
812 (xlib:alloc-color
(xlib:screen-default-colormap
*screen
*) color
))))))
816 (defgeneric -
>color
(color))
818 (defmethod ->color
((color-name string
))
821 (defmethod ->color
((color integer
))
822 (labels ((hex->float
(color)
823 (/ (logand color
#xFF
) 256.0)))
824 (xlib:make-color
:blue
(hex->float color
)
825 :green
(hex->float
(ash color -
8))
826 :red
(hex->float
(ash color -
16)))))
828 (defmethod ->color
((color list
))
829 (destructuring-bind (red green blue
) color
830 (xlib:make-color
:blue red
:green green
:red blue
)))
832 (defmethod ->color
((color xlib
:color
))
835 (defmethod ->color
(color)
836 (format t
"Wrong color type: ~A~%" color
)
840 (defun color->rgb
(color)
841 (multiple-value-bind (r g b
)
842 (xlib:color-rgb color
)
843 (+ (ash (round (* 256 r
)) +16)
844 (ash (round (* 256 g
)) +8)
851 (defmacro my-character-
>keysyms
(ch)
852 "Convert a char to a keysym"
853 ;; XLIB:CHARACTER->KEYSYMS should probably be implemented in NEW-CLX
854 ;; some day. Or just copied from MIT-CLX or some other CLX
855 ;; implementation (see translate.lisp and keysyms.lisp). For now,
856 ;; we do like this. It suffices for modifiers and ASCII symbols.
857 (if (fboundp 'xlib
:character-
>keysyms
)
858 `(xlib:character-
>keysyms
,ch
)
861 (:character-set-switch
#xFF7E
)
863 (:right-shift
#xFFE2
)
864 (:left-control
#xFFE3
)
865 (:right-control
#xFFE4
)
873 (:right-super
#xFFEC
)
875 (:right-hyper
#xFFEE
)
879 ;; Latin-1 characters have their own value as keysym
880 (if (< 31 (char-code ,ch
) 256)
882 (error "Don't know how to get keysym from ~A" ,ch
)))))))))
887 (defun char->keycode
(char)
888 "Convert a character to a keycode"
889 (xlib:keysym-
>keycodes
*display
* (first (my-character->keysyms char
))))
892 (defun keycode->char
(code state
)
893 (xlib:keysym-
>character
*display
* (xlib:keycode-
>keysym
*display
* code
0) state
))
895 (defun modifiers->state
(modifier-list)
896 (apply #'xlib
:make-state-mask modifier-list
))
898 (defun state->modifiers
(state)
899 (xlib:make-state-keys state
))
901 (defun keycode->keysym
(code modifiers
)
902 (xlib:keycode-
>keysym
*display
* code
(cond ((member :shift modifiers
) 1)
903 ((member :mod-5 modifiers
) 4)
910 (let ((modifier-list nil
))
911 (defun init-modifier-list ()
912 (dolist (name '("Shift_L" "Shift_R" "Control_L" "Control_R"
913 "Alt_L" "Alt_R" "Meta_L" "Meta_R" "Hyper_L" "Hyper_R"
914 "Mode_switch" "script_switch" "ISO_Level3_Shift"
915 "Caps_Lock" "Scroll_Lock" "Num_Lock"))
916 (awhen (xlib:keysym-
>keycodes
*display
* (keysym-name->keysym name
))
917 (push it modifier-list
))))
919 (defun modifier-p (code)
920 (member code modifier-list
)))
922 (defun wait-no-key-or-button-press ()
923 (with-grab-keyboard-and-pointer (66 67 66 67)
925 (let ((key (loop for k across
(xlib:query-keymap
*display
*)
927 when
(and (plusp k
) (not (modifier-p code
)))
929 (button (loop for b in
(xlib:make-state-keys
(nth-value 4 (xlib:query-pointer
*root
*)))
930 when
(member b
'(:button-1
:button-2
:button-3
:button-4
:button-5
))
932 (when (and (not key
) (not button
))
933 (loop while
(xlib:event-case
(*display
* :discard-p t
:peek-p nil
:timeout
0)
934 (:motion-notify
() t
)
938 (:button-release
() t
)
943 (defun wait-a-key-or-button-press ()
944 (with-grab-keyboard-and-pointer (24 25 66 67)
946 (let ((key (loop for k across
(xlib:query-keymap
*display
*)
947 unless
(zerop k
) return t
))
948 (button (loop for b in
(xlib:make-state-keys
(nth-value 4 (xlib:query-pointer
*root
*)))
949 when
(member b
'(:button-1
:button-2
:button-3
:button-4
:button-5
))
951 (when (or key button
)
956 (defun compress-motion-notify ()
957 (when *have-to-compress-notify
*
958 (loop while
(xlib:event-cond
(*display
* :timeout
0)
959 (:motion-notify
() t
)))))
962 (defun display-all-cursors (&optional
(display-time 1))
963 "Display all X11 cursors for display-time seconds"
964 (loop for i from
0 to
152 by
2
965 do
(xgrab-pointer *root
* i
(1+ i
))
972 ;;; Double buffering tools
973 (defun clear-pixmap-buffer (window gc
)
974 (if (equal *transparent-background
* :pseudo
)
975 (xlib:copy-area
*background-image
* *background-gc
*
976 (x-drawable-x window
) (x-drawable-y window
)
977 (x-drawable-width window
) (x-drawable-height window
)
979 (xlib:with-gcontext
(gc :foreground
(xlib:gcontext-background gc
)
980 :background
(xlib:gcontext-foreground gc
))
981 (xlib:draw-rectangle
*pixmap-buffer
* gc
982 0 0 (x-drawable-width window
) (x-drawable-height window
)
986 (defun copy-pixmap-buffer (window gc
)
987 (xlib:copy-area
*pixmap-buffer
* gc
988 0 0 (x-drawable-width window
) (x-drawable-height window
)
993 (defun is-a-key-pressed-p ()
994 (loop for k across
(xlib:query-keymap
*display
*)
998 ;;; Windows wm class and name tests
999 (defmacro defun-equal-wm-class
(symbol class
)
1000 `(defun ,symbol
(window)
1001 (when (xlib:window-p window
)
1002 (string-equal (xlib:get-wm-class window
) ,class
))))
1004 (defmacro defun-equal-wm-name
(symbol name
)
1005 `(defun ,symbol
(window)
1006 (when (xlib:window-p window
)
1007 (string-equal (xlib:wm-name window
) ,name
))))