src/clfswm.lisp (main-unprotected): Create the configuration menu only once at startup.
[clfswm.git] / src / xlib-util.lisp
blob7d7df53c2e17a153d5c9c57e87dec8c0b3fe3d13
1 ;;; --------------------------------------------------------------------------
2 ;;; CLFSWM - FullScreen Window Manager
3 ;;;
4 ;;; --------------------------------------------------------------------------
5 ;;; Documentation: Utility functions
6 ;;; --------------------------------------------------------------------------
7 ;;;
8 ;;; (C) 2010 Philippe Brochard <hocwp@free.fr>
9 ;;;
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.
14 ;;;
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.
19 ;;;
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.
23 ;;;
24 ;;; --------------------------------------------------------------------------
26 (in-package :clfswm)
28 ;; Window states
29 (defconstant +withdrawn-state+ 0)
30 (defconstant +normal-state+ 1)
31 (defconstant +iconic-state+ 3)
34 (defparameter *window-events* '(:structure-notify
35 :property-change
36 :colormap-change
37 :focus-change
38 :enter-window
39 :exposure)
40 "The events to listen for on managed windows.")
43 (defparameter +netwm-supported+
44 '(:_NET_SUPPORTING_WM_CHECK
45 :_NET_NUMBER_OF_DESKTOPS
46 :_NET_DESKTOP_GEOMETRY
47 :_NET_DESKTOP_VIEWPORT
48 :_NET_CURRENT_DESKTOP
49 :_NET_WM_WINDOW_TYPE
50 :_NET_CLIENT_LIST)
51 "Supported NETWM properties.
52 Window types are in +WINDOW-TYPES+.")
54 (defparameter +netwm-window-types+
55 '((:_NET_WM_WINDOW_TYPE_DESKTOP . :desktop)
56 (:_NET_WM_WINDOW_TYPE_DOCK . :dock)
57 (:_NET_WM_WINDOW_TYPE_TOOLBAR . :toolbar)
58 (:_NET_WM_WINDOW_TYPE_MENU . :menu)
59 (:_NET_WM_WINDOW_TYPE_UTILITY . :utility)
60 (:_NET_WM_WINDOW_TYPE_SPLASH . :splash)
61 (:_NET_WM_WINDOW_TYPE_DIALOG . :dialog)
62 (:_NET_WM_WINDOW_TYPE_NORMAL . :normal))
63 "Alist mapping NETWM window types to keywords.")
66 (defmacro with-xlib-protect (&body body)
67 "Prevent Xlib errors"
68 `(handler-case
69 (with-simple-restart (top-level "Return to clfswm's top level")
70 ,@body)
71 ((or xlib:match-error xlib:window-error xlib:drawable-error) (c)
72 (progn
73 (dbg "Ignore Xlib Error" c ',body)
74 (unassoc-keyword-handle-event)
75 (assoc-keyword-handle-event 'main-mode)
76 (setf *in-second-mode* nil)))))
79 (defmacro with-x-pointer (&body body)
80 "Bind (x y) to mouse pointer positions"
81 `(multiple-value-bind (x y)
82 (xlib:query-pointer *root*)
83 ,@body))
87 ;;;
88 ;;; Events management functions.
89 ;;;
90 (defparameter *unhandled-events* nil)
91 (defparameter *current-event-mode* nil)
93 (eval-when (:compile-toplevel :load-toplevel :execute)
94 (defun keyword->handle-event (mode keyword)
95 (symb 'handle-event-fun "-" mode "-" keyword)))
97 (defun handle-event->keyword (symbol)
98 (let* ((name (string-downcase (symbol-name symbol)))
99 (pos (search "handle-event-fun-" name)))
100 (when (and pos (zerop pos))
101 (let ((pos-mod (search "mode" name)))
102 (when pos-mod
103 (values (intern (string-upcase (subseq name (+ pos-mod 5))) :keyword)
104 (subseq name (length "handle-event-fun-") (1- pos-mod))))))))
106 (defparameter *handle-event-fun-symbols* nil)
108 (defun fill-handle-event-fun-symbols ()
109 (with-all-internal-symbols (symbol :clfswm)
110 (let ((pos (symbol-search "handle-event-fun-" symbol)))
111 (when (and pos (zerop pos))
112 (pushnew symbol *handle-event-fun-symbols*)))))
115 (defmacro with-handle-event-symbol ((mode) &body body)
116 "Bind symbol to all handle event functions available in mode"
117 `(let ((pattern (format nil "handle-event-fun-~A" ,mode)))
118 (dolist (symbol *handle-event-fun-symbols*)
119 (let ((pos (symbol-search pattern symbol)))
120 (when (and pos (zerop pos))
121 ,@body)))))
124 (defun find-handle-event-function (&optional (mode ""))
125 "Print all handle event functions available in mode"
126 (with-handle-event-symbol (mode)
127 (print symbol)))
129 (defun assoc-keyword-handle-event (mode)
130 "Associate all keywords in mode to their corresponding handle event functions.
131 For example: main-mode :key-press is bound to handle-event-fun-main-mode-key-press"
132 (setf *current-event-mode* mode)
133 (with-handle-event-symbol (mode)
134 (let ((keyword (handle-event->keyword symbol)))
135 (when (fboundp symbol)
136 #+:event-debug
137 (progn
138 (format t "~&Associating: ~S with ~S~%" symbol keyword)
139 (force-output))
140 (setf (symbol-function keyword) (symbol-function symbol))))))
142 (defun unassoc-keyword-handle-event (&optional (mode ""))
143 "Unbound all keywords from their corresponding handle event functions."
144 (setf *current-event-mode* nil)
145 (with-handle-event-symbol (mode)
146 (let ((keyword (handle-event->keyword symbol)))
147 (when (fboundp keyword)
148 #+:event-debug
149 (progn
150 (format t "~&Unassociating: ~S ~S~%" symbol keyword)
151 (force-output))
152 (fmakunbound keyword)))))
154 (defmacro define-handler (mode keyword args &body body)
155 "Like a defun but with a name expanded as handle-event-fun-'mode'-'keyword'
156 For example (define-handler main-mode :key-press (args) ...)
157 Expand in handle-event-fun-main-mode-key-press"
158 `(defun ,(keyword->handle-event mode keyword) (&rest event-slots &key #+:event-debug event-key ,@args &allow-other-keys)
159 (declare (ignorable event-slots))
160 #+:event-debug (print (list *current-event-mode* event-key))
161 ,@body))
165 (defun handle-event (&rest event-slots &key event-key &allow-other-keys)
166 (with-xlib-protect
167 (if (fboundp event-key)
168 (apply event-key event-slots)
169 #+:event-debug (pushnew (list *current-event-mode* event-key) *unhandled-events* :test #'equal))
170 (xlib:display-finish-output *display*))
177 (defun parse-display-string (display)
178 "Parse an X11 DISPLAY string and return the host and display from it."
179 (let* ((colon (position #\: display))
180 (host (subseq display 0 colon))
181 (rest (subseq display (1+ colon)))
182 (dot (position #\. rest))
183 (num (parse-integer (subseq rest 0 dot))))
184 (values host num)))
187 (defun banish-pointer ()
188 "Move the pointer to the lower right corner of the screen"
189 (with-placement (*banish-pointer-placement* x y)
190 (xlib:warp-pointer *root* x y)))
194 (defun window-state (win)
195 "Get the state (iconic, normal, withdraw of a window."
196 (first (xlib:get-property win :WM_STATE)))
199 (defun set-window-state (win state)
200 "Set the state (iconic, normal, withdrawn) of a window."
201 (xlib:change-property win
202 :WM_STATE
203 (list state)
204 :WM_STATE
205 32))
207 (defsetf window-state set-window-state)
211 (defun window-hidden-p (window)
212 (eql (window-state window) +iconic-state+))
215 (defun null-size-window-p (window)
216 (let ((hints (xlib:wm-normal-hints window)))
217 (and hints
218 (not (or (xlib:wm-size-hints-width hints)
219 (xlib:wm-size-hints-height hints)
220 (xlib:wm-size-hints-win-gravity hints)))
221 (xlib:wm-size-hints-user-specified-position-p hints))))
228 (defun unhide-window (window)
229 (when window
230 (when (window-hidden-p window)
231 (xlib:map-window window)
232 (setf (window-state window) +normal-state+
233 (xlib:window-event-mask window) *window-events*))))
236 (defun map-window (window)
237 (when window
238 (xlib:map-window window)))
241 (defun delete-window (window)
242 (send-client-message window :WM_PROTOCOLS
243 (xlib:intern-atom *display* "WM_DELETE_WINDOW")))
245 (defun destroy-window (window)
246 (xlib:kill-client *display* (xlib:window-id window)))
250 ;;(defconstant +exwm-atoms+
251 ;; (list "_NET_SUPPORTED" "_NET_CLIENT_LIST"
252 ;; "_NET_CLIENT_LIST_STACKING" "_NET_NUMBER_OF_DESKTOPS"
253 ;; "_NET_CURRENT_DESKTOP" "_NET_DESKTOP_GEOMETRY"
254 ;; "_NET_DESKTOP_VIEWPORT" "_NET_DESKTOP_NAMES"
255 ;; "_NET_ACTIVE_WINDOW" "_NET_WORKAREA"
256 ;; "_NET_SUPPORTING_WM_CHECK" "_NET_VIRTUAL_ROOTS"
257 ;; "_NET_DESKTOP_LAYOUT"
259 ;; "_NET_RESTACK_WINDOW" "_NET_REQUEST_FRAME_EXTENTS"
260 ;; "_NET_MOVERESIZE_WINDOW" "_NET_CLOSE_WINDOW"
261 ;; "_NET_WM_MOVERESIZE"
263 ;; "_NET_WM_SYNC_REQUEST" "_NET_WM_PING"
265 ;; "_NET_WM_NAME" "_NET_WM_VISIBLE_NAME"
266 ;; "_NET_WM_ICON_NAME" "_NET_WM_VISIBLE_ICON_NAME"
267 ;; "_NET_WM_DESKTOP" "_NET_WM_WINDOW_TYPE"
268 ;; "_NET_WM_STATE" "_NET_WM_STRUT"
269 ;; "_NET_WM_ICON_GEOMETRY" "_NET_WM_ICON"
270 ;; "_NET_WM_PID" "_NET_WM_HANDLED_ICONS"
271 ;; "_NET_WM_USER_TIME" "_NET_FRAME_EXTENTS"
272 ;; ;; "_NET_WM_MOVE_ACTIONS"
274 ;; "_NET_WM_WINDOW_TYPE_DESKTOP" "_NET_WM_STATE_MODAL"
275 ;; "_NET_WM_WINDOW_TYPE_DOCK" "_NET_WM_STATE_STICKY"
276 ;; "_NET_WM_WINDOW_TYPE_TOOLBAR" "_NET_WM_STATE_MAXIMIZED_VERT"
277 ;; "_NET_WM_WINDOW_TYPE_MENU" "_NET_WM_STATE_MAXIMIZED_HORZ"
278 ;; "_NET_WM_WINDOW_TYPE_UTILITY" "_NET_WM_STATE_SHADED"
279 ;; "_NET_WM_WINDOW_TYPE_SPLASH" "_NET_WM_STATE_SKIP_TASKBAR"
280 ;; "_NET_WM_WINDOW_TYPE_DIALOG" "_NET_WM_STATE_SKIP_PAGER"
281 ;; "_NET_WM_WINDOW_TYPE_NORMAL" "_NET_WM_STATE_HIDDEN"
282 ;; "_NET_WM_STATE_FULLSCREEN"
283 ;; "_NET_WM_STATE_ABOVE"
284 ;; "_NET_WM_STATE_BELOW"
285 ;; "_NET_WM_STATE_DEMANDS_ATTENTION"
287 ;; "_NET_WM_ALLOWED_ACTIONS"
288 ;; "_NET_WM_ACTION_MOVE"
289 ;; "_NET_WM_ACTION_RESIZE"
290 ;; "_NET_WM_ACTION_SHADE"
291 ;; "_NET_WM_ACTION_STICK"
292 ;; "_NET_WM_ACTION_MAXIMIZE_HORZ"
293 ;; "_NET_WM_ACTION_MAXIMIZE_VERT"
294 ;; "_NET_WM_ACTION_FULLSCREEN"
295 ;; "_NET_WM_ACTION_CHANGE_DESKTOP"
296 ;; "_NET_WM_ACTION_CLOSE"
298 ;; ))
301 ;;(defun intern-atoms (display)
302 ;; (declare (type xlib:display display))
303 ;; (mapcar #'(lambda (atom-name) (xlib:intern-atom display atom-name))
304 ;; +exwm-atoms+)
305 ;; (values))
309 ;;(defun get-atoms-property (window property-atom atom-list-p)
310 ;; "Returns a list of atom-name (if atom-list-p is t) otherwise returns
311 ;; a list of atom-id."
312 ;; (xlib:get-property window property-atom
313 ;; :transform (when atom-list-p
314 ;; (lambda (id)
315 ;; (xlib:atom-name (xlib:drawable-display window) id)))))
317 ;;(defun set-atoms-property (window atoms property-atom &key (mode :replace))
318 ;; "Sets the property designates by `property-atom'. ATOMS is a list of atom-id
319 ;; or a list of keyword atom-names."
320 ;; (xlib:change-property window property-atom atoms :ATOM 32
321 ;; :mode mode
322 ;; :transform (unless (integerp (car atoms))
323 ;; (lambda (atom-key)
324 ;; (xlib:find-atom (xlib:drawable-display window) atom-key)))))
329 ;;(defun net-wm-state (window)
330 ;; (get-atoms-property window :_NET_WM_STATE t))
332 ;;(defsetf net-wm-state (window &key (mode :replace)) (states)
333 ;; `(set-atoms-property ,window ,states :_NET_WM_STATE :mode ,mode))
336 (defun hide-window (window)
337 (when window
338 (setf (window-state window) +iconic-state+
339 (xlib:window-event-mask window) (remove :structure-notify *window-events*))
340 (xlib:unmap-window window)
341 (setf (xlib:window-event-mask window) *window-events*)))
345 (defun window-type (window)
346 "Return one of :desktop, :dock, :toolbar, :utility, :splash,
347 :dialog, :transient, :maxsize and :normal."
348 (or (and (let ((hints (xlib:wm-normal-hints window)))
349 (and hints (or (xlib:wm-size-hints-max-width hints)
350 (xlib:wm-size-hints-max-height hints)
351 (xlib:wm-size-hints-min-aspect hints)
352 (xlib:wm-size-hints-max-aspect hints))))
353 :maxsize)
354 (let ((net-wm-window-type (xlib:get-property window :_NET_WM_WINDOW_TYPE)))
355 (when net-wm-window-type
356 (dolist (type-atom net-wm-window-type)
357 (when (assoc (xlib:atom-name *display* type-atom) +netwm-window-types+)
358 (return (cdr (assoc (xlib:atom-name *display* type-atom) +netwm-window-types+)))))))
359 (and (xlib:get-property window :WM_TRANSIENT_FOR)
360 :transient)
361 :normal))
367 ;;; Stolen from Eclipse
368 (defun send-configuration-notify (window x y w h bw)
369 "Send a synthetic configure notify event to the given window (ICCCM 4.1.5)"
370 (xlib:send-event window :configure-notify (xlib:make-event-mask :structure-notify)
371 :event-window window
372 :window window
373 :x x :y y
374 :width w
375 :height h
376 :border-width bw
377 :propagate-p nil))
381 (defun send-client-message (window type &rest data)
382 "Send a client message to a client's window."
383 (xlib:send-event window
384 :client-message nil
385 :window window
386 :type type
387 :format 32
388 :data data))
394 (defun raise-window (window)
395 "Map the window if needed and bring it to the top of the stack. Does not affect focus."
396 (when (xlib:window-p window)
397 (when (window-hidden-p window)
398 (unhide-window window))
399 (setf (xlib:window-priority window) :above)))
401 (defun focus-window (window)
402 "Give the window focus."
403 (when (xlib:window-p window)
404 (xlib:set-input-focus *display* window :parent)))
406 (defun raise-and-focus-window (window)
407 "Raise and focus."
408 (raise-window window)
409 (focus-window window))
411 (defun no-focus ()
412 "don't focus any window but still read keyboard events."
413 (xlib:set-input-focus *display* *no-focus-window* :pointer-root))
418 (let ((cursor-font nil)
419 (cursor nil)
420 (pointer-grabbed nil))
421 (defun free-grab-pointer ()
422 (when cursor
423 (xlib:free-cursor cursor)
424 (setf cursor nil))
425 (when cursor-font
426 (xlib:close-font cursor-font)
427 (setf cursor-font nil)))
429 (defun xgrab-init-pointer ()
430 (setf pointer-grabbed nil))
432 (defun xgrab-pointer-p ()
433 pointer-grabbed)
435 (defun xgrab-pointer (root cursor-char cursor-mask-char
436 &optional (pointer-mask '(:enter-window :pointer-motion
437 :button-press :button-release)) owner-p)
438 "Grab the pointer and set the pointer shape."
439 (when pointer-grabbed
440 (xungrab-pointer))
441 (setf pointer-grabbed t)
442 (let* ((white (xlib:make-color :red 1.0 :green 1.0 :blue 1.0))
443 (black (xlib:make-color :red 0.0 :green 0.0 :blue 0.0)))
444 (cond (cursor-char
445 (setf cursor-font (xlib:open-font *display* "cursor")
446 cursor (xlib:create-glyph-cursor :source-font cursor-font
447 :source-char (or cursor-char 68)
448 :mask-font cursor-font
449 :mask-char (or cursor-mask-char 69)
450 :foreground black
451 :background white))
452 (xlib:grab-pointer root pointer-mask
453 :owner-p owner-p :sync-keyboard-p nil :sync-pointer-p nil :cursor cursor))
455 (xlib:grab-pointer root pointer-mask
456 :owner-p owner-p :sync-keyboard-p nil :sync-pointer-p nil)))))
458 (defun xungrab-pointer ()
459 "Remove the grab on the cursor and restore the cursor shape."
460 (setf pointer-grabbed nil)
461 (xlib:ungrab-pointer *display*)
462 (xlib:display-finish-output *display*)
463 (free-grab-pointer)))
466 (let ((keyboard-grabbed nil))
467 (defun xgrab-init-keyboard ()
468 (setf keyboard-grabbed nil))
470 (defun xgrab-keyboard-p ()
471 keyboard-grabbed)
473 (defun xgrab-keyboard (root)
474 (setf keyboard-grabbed t)
475 (xlib:grab-keyboard root :owner-p nil :sync-keyboard-p nil :sync-pointer-p nil))
478 (defun xungrab-keyboard ()
479 (setf keyboard-grabbed nil)
480 (xlib:ungrab-keyboard *display*)))
487 (defun ungrab-all-buttons (window)
488 (xlib:ungrab-button window :any :modifiers :any))
490 (defun grab-all-buttons (window)
491 (ungrab-all-buttons window)
492 (xlib:grab-button window :any '(:button-press :button-release :pointer-motion)
493 :modifiers :any
494 :owner-p nil
495 :sync-pointer-p t
496 :sync-keyboard-p nil))
498 (defun ungrab-all-keys (window)
499 (xlib:ungrab-key window :any :modifiers :any))
502 (defun stop-button-event ()
503 (xlib:allow-events *display* :sync-pointer))
505 (defun replay-button-event ()
506 (xlib:allow-events *display* :replay-pointer))
516 ;;; Mouse action on window
517 (let (add-fn add-arg dx dy window)
518 (define-handler move-window-mode :motion-notify (root-x root-y)
519 (unless (compress-motion-notify)
520 (setf (xlib:drawable-x window) (+ root-x dx)
521 (xlib:drawable-y window) (+ root-y dy))
522 (when add-fn
523 (apply add-fn add-arg))))
525 (define-handler move-window-mode :button-release ()
526 (throw 'exit-move-window-mode nil))
528 (defun move-window (orig-window orig-x orig-y &optional additional-fn additional-arg)
529 (setf window orig-window
530 add-fn additional-fn
531 add-arg additional-arg
532 dx (- (xlib:drawable-x window) orig-x)
533 dy (- (xlib:drawable-y window) orig-y))
534 (raise-window window)
535 (let ((pointer-grabbed-p (xgrab-pointer-p)))
536 (unless pointer-grabbed-p
537 (xgrab-pointer *root* nil nil))
538 (when additional-fn
539 (apply additional-fn additional-arg))
540 (generic-mode 'move-window-mode 'exit-move-window-mode
541 :original-mode '(main-mode))
542 (unless pointer-grabbed-p
543 (xungrab-pointer)))))
546 (let (add-fn add-arg window
547 o-x o-y
548 orig-width orig-height
549 min-width max-width
550 min-height max-height)
551 (define-handler resize-window-mode :motion-notify (root-x root-y)
552 (unless (compress-motion-notify)
553 (setf (xlib:drawable-width window) (min (max (+ orig-width (- root-x o-x)) 10 min-width) max-width)
554 (xlib:drawable-height window) (min (max (+ orig-height (- root-y o-y)) 10 min-height) max-height))
555 (when add-fn
556 (apply add-fn add-arg))))
558 (define-handler resize-window-mode :button-release ()
559 (throw 'exit-resize-window-mode nil))
561 (defun resize-window (orig-window orig-x orig-y &optional additional-fn additional-arg)
562 (let* ((pointer-grabbed-p (xgrab-pointer-p))
563 (hints (xlib:wm-normal-hints orig-window)))
564 (setf window orig-window
565 add-fn additional-fn
566 add-arg additional-arg
567 o-x orig-x
568 o-y orig-y
569 orig-width (xlib:drawable-width window)
570 orig-height (xlib:drawable-height window)
571 min-width (or (and hints (xlib:wm-size-hints-min-width hints)) 0)
572 min-height (or (and hints (xlib:wm-size-hints-min-height hints)) 0)
573 max-width (or (and hints (xlib:wm-size-hints-max-width hints)) most-positive-fixnum)
574 max-height (or (and hints (xlib:wm-size-hints-max-height hints)) most-positive-fixnum))
575 (raise-window window)
576 (unless pointer-grabbed-p
577 (xgrab-pointer *root* nil nil))
578 (when additional-fn
579 (apply additional-fn additional-arg))
580 (generic-mode 'resize-window-mode 'exit-resize-window-mode
581 :original-mode '(main-mode))
582 (unless pointer-grabbed-p
583 (xungrab-pointer)))))
586 (define-handler wait-mouse-button-release-mode :button-release ()
587 (throw 'exit-wait-mouse-button-release-mode nil))
589 (defun wait-mouse-button-release (&optional cursor-char cursor-mask-char)
590 (let ((pointer-grabbed-p (xgrab-pointer-p)))
591 (unless pointer-grabbed-p
592 (xgrab-pointer *root* cursor-char cursor-mask-char))
593 (generic-mode 'wait-mouse-button-release 'exit-wait-mouse-button-release-mode)
594 (unless pointer-grabbed-p
595 (xungrab-pointer))))
600 (let ((color-hash (make-hash-table :test 'equal)))
601 (defun get-color (color)
602 (multiple-value-bind (val foundp)
603 (gethash color color-hash)
604 (if foundp
606 (setf (gethash color color-hash)
607 (xlib:alloc-color (xlib:screen-default-colormap *screen*) color))))))
611 (defgeneric ->color (color))
613 (defmethod ->color ((color-name string))
614 color-name)
616 (defmethod ->color ((color integer))
617 (labels ((hex->float (color)
618 (/ (logand color #xFF) 256.0)))
619 (xlib:make-color :blue (hex->float color)
620 :green (hex->float (ash color -8))
621 :red (hex->float (ash color -16)))))
623 (defmethod ->color ((color list))
624 (destructuring-bind (red green blue) color
625 (xlib:make-color :blue red :green green :red blue)))
627 (defmethod ->color ((color xlib:color))
628 color)
630 (defmethod ->color (color)
631 (format t "Wrong color type: ~A~%" color)
632 "White")
635 (defun color->rgb (color)
636 (multiple-value-bind (r g b)
637 (xlib:color-rgb color)
638 (+ (ash (round (* 256 r)) +16)
639 (ash (round (* 256 g)) +8)
640 (round (* 256 b)))))
646 (defmacro my-character->keysyms (ch)
647 "Convert a char to a keysym"
648 ;; XLIB:CHARACTER->KEYSYMS should probably be implemented in NEW-CLX
649 ;; some day. Or just copied from MIT-CLX or some other CLX
650 ;; implementation (see translate.lisp and keysyms.lisp). For now,
651 ;; we do like this. It suffices for modifiers and ASCII symbols.
652 (if (fboundp 'xlib:character->keysyms)
653 `(xlib:character->keysyms ,ch)
654 `(list
655 (case ,ch
656 (:character-set-switch #xFF7E)
657 (:left-shift #xFFE1)
658 (:right-shift #xFFE2)
659 (:left-control #xFFE3)
660 (:right-control #xFFE4)
661 (:caps-lock #xFFE5)
662 (:shift-lock #xFFE6)
663 (:left-meta #xFFE7)
664 (:right-meta #xFFE8)
665 (:left-alt #xFFE9)
666 (:right-alt #xFFEA)
667 (:left-super #xFFEB)
668 (:right-super #xFFEC)
669 (:left-hyper #xFFED)
670 (:right-hyper #xFFEE)
672 (etypecase ,ch
673 (character
674 ;; Latin-1 characters have their own value as keysym
675 (if (< 31 (char-code ,ch) 256)
676 (char-code ,ch)
677 (error "Don't know how to get keysym from ~A" ,ch)))))))))
682 (defun char->keycode (char)
683 "Convert a character to a keycode"
684 (xlib:keysym->keycodes *display* (first (my-character->keysyms char))))
687 (defun keycode->char (code state)
688 (xlib:keysym->character *display* (xlib:keycode->keysym *display* code 0) state))
690 (defun modifiers->state (modifier-list)
691 (apply #'xlib:make-state-mask modifier-list))
693 (defun state->modifiers (state)
694 (xlib:make-state-keys state))
696 (defun keycode->keysym (code modifiers)
697 (xlib:keycode->keysym *display* code (cond ((member :shift modifiers) 1)
698 ((member :mod-5 modifiers) 4)
699 (t 0))))
702 (defmacro with-grab-keyboard-and-pointer ((cursor mask old-cursor old-mask) &body body)
703 `(let ((pointer-grabbed (xgrab-pointer-p))
704 (keyboard-grabbed (xgrab-keyboard-p)))
705 (xgrab-pointer *root* ,cursor ,mask)
706 (unless keyboard-grabbed
707 (xgrab-keyboard *root*))
708 (unwind-protect
709 (progn
710 ,@body)
711 (if pointer-grabbed
712 (xgrab-pointer *root* ,old-cursor ,old-mask)
713 (xungrab-pointer))
714 (unless keyboard-grabbed
715 (xungrab-keyboard)))))
722 (let ((modifier-list nil))
723 (defun init-modifier-list ()
724 (dolist (name '("Shift_L" "Shift_R" "Control_L" "Control_R"
725 "Alt_L" "Alt_R" "Meta_L" "Meta_R" "Hyper_L" "Hyper_R"
726 "Mode_switch" "script_switch" "ISO_Level3_Shift"
727 "Caps_Lock" "Scroll_Lock" "Num_Lock"))
728 (awhen (xlib:keysym->keycodes *display* (keysym-name->keysym name))
729 (push it modifier-list))))
731 (defun modifier-p (code)
732 (member code modifier-list)))
734 (defun wait-no-key-or-button-press ()
735 (with-grab-keyboard-and-pointer (66 67 66 67)
736 (loop
737 (let ((key (loop for k across (xlib:query-keymap *display*)
738 for code from 0
739 when (and (plusp k) (not (modifier-p code)))
740 return t))
741 (button (loop for b in (xlib:make-state-keys (nth-value 4 (xlib:query-pointer *root*)))
742 when (member b '(:button-1 :button-2 :button-3 :button-4 :button-5))
743 return t)))
744 (when (and (not key) (not button))
745 (loop while (xlib:event-case (*display* :discard-p t :peek-p nil :timeout 0)
746 (:motion-notify () t)
747 (:key-press () t)
748 (:key-release () t)
749 (:button-press () t)
750 (:button-release () t)
751 (t nil)))
752 (return))))))
755 (defun wait-a-key-or-button-press ()
756 (with-grab-keyboard-and-pointer (24 25 66 67)
757 (loop
758 (let ((key (loop for k across (xlib:query-keymap *display*)
759 unless (zerop k) return t))
760 (button (loop for b in (xlib:make-state-keys (nth-value 4 (xlib:query-pointer *root*)))
761 when (member b '(:button-1 :button-2 :button-3 :button-4 :button-5))
762 return t)))
763 (when (or key button)
764 (return))))))
768 (defun compress-motion-notify ()
769 (when *have-to-compress-notify*
770 (loop while (xlib:event-cond (*display* :timeout 0)
771 (:motion-notify () t)))))
774 (defun display-all-cursors (&optional (display-time 1))
775 "Display all X11 cursors for display-time seconds"
776 (loop for i from 0 to 152 by 2
777 do (xgrab-pointer *root* i (1+ i))
778 (dbg i)
779 (sleep display-time)
780 (xungrab-pointer)))
785 ;;; Double buffering tools
786 (defun clear-pixmap-buffer (window gc)
787 (rotatef (xlib:gcontext-foreground gc) (xlib:gcontext-background gc))
788 (xlib:draw-rectangle *pixmap-buffer* gc
789 0 0 (xlib:drawable-width window) (xlib:drawable-height window)
791 (rotatef (xlib:gcontext-foreground gc) (xlib:gcontext-background gc)))
793 (defun copy-pixmap-buffer (window gc)
794 (xlib:copy-area *pixmap-buffer* gc
795 0 0 (xlib:drawable-width window) (xlib:drawable-height window)
796 window 0 0))
799 (defun is-a-key-pressed-p ()
800 (loop for k across (xlib:query-keymap *display*)
801 when (plusp k)
802 return t))
804 ;;; Windows wm class and name tests
805 (defun equal-wm-class-fun (class)
806 (lambda (win)
807 (when (xlib:window-p win)
808 (string-equal (xlib:get-wm-class win) class))))
810 (defun equal-wm-name-fun (name)
811 (lambda (win)
812 (when (xlib:window-p win)
813 (string-equal (xlib:wm-name win) name))))