Grab global keys on top-level X windows

* exwm-input.el (exwm-input--on-CreateNotify): New function for
grabbing global keys on newly created X windows.
(exwm-input--update-global-prefix-keys): Grab global keys on top-level
X windows instead of the root window.
(exwm-input--grab-global-prefix-keys): New function for grabbing
global keys on X windows.
(exwm-input--release-keyboard): Grab global keys in char-mode.
(exwm-input--init): Select CreateNotify events.

* exwm-core.el (exwm--unlock):
* exwm-input.el (exwm-input--on-FocusIn, exwm-input--init): Do not
handle FocusIn events on the root window.
This commit is contained in:
Chris Feng 2018-02-22 00:26:04 +08:00
parent 86f2215be3
commit 6200417697
2 changed files with 41 additions and 42 deletions

View file

@ -93,8 +93,7 @@
:window exwm--root :window exwm--root
:value-mask xcb:CW:EventMask :value-mask xcb:CW:EventMask
:event-mask (eval-when-compile :event-mask (eval-when-compile
(logior xcb:EventMask:FocusChange (logior xcb:EventMask:SubstructureRedirect
xcb:EventMask:SubstructureRedirect
xcb:EventMask:StructureNotify)))) xcb:EventMask:StructureNotify))))
(xcb:flush exwm--connection)) (xcb:flush exwm--connection))

View file

@ -212,16 +212,6 @@ ARGS are additional arguments to CALLBACK."
(cdr exwm-input--timestamp-callback)) (cdr exwm-input--timestamp-callback))
(setq exwm-input--timestamp-callback nil))))) (setq exwm-input--timestamp-callback nil)))))
(defun exwm-input--on-FocusIn (data _synthetic)
"Handle FocusIn events."
(let ((obj (make-instance 'xcb:FocusIn)))
(xcb:unmarshal obj data)
(with-slots (mode) obj
;; Revert input focus back to Emacs frame / X window when it's set on
;; the root window.
(x-focus-frame exwm-workspace--current)
(select-window (frame-selected-window exwm-workspace--current)))))
(defun exwm-input--on-EnterNotify (data _synthetic) (defun exwm-input--on-EnterNotify (data _synthetic)
"Handle EnterNotify events." "Handle EnterNotify events."
(let ((evt (make-instance 'xcb:EnterNotify)) (let ((evt (make-instance 'xcb:EnterNotify))
@ -428,42 +418,51 @@ ARGS are additional arguments to CALLBACK."
(funcall exwm--on-KeyPress obj data) (funcall exwm--on-KeyPress obj data)
(exwm-input--on-KeyPress-char-mode obj)))) (exwm-input--on-KeyPress-char-mode obj))))
(defun exwm-input--on-CreateNotify (data _synthetic)
"Handle CreateNotify events."
(let ((evt (make-instance 'xcb:CreateNotify)))
(xcb:unmarshal evt data)
(with-slots (window) evt
(exwm-input--grab-global-prefix-keys window))))
(defun exwm-input--update-global-prefix-keys () (defun exwm-input--update-global-prefix-keys ()
"Update `exwm-input--global-prefix-keys'." "Update `exwm-input--global-prefix-keys'."
(when exwm--connection (when exwm--connection
(let ((original exwm-input--global-prefix-keys) (let ((original exwm-input--global-prefix-keys))
keysym keycode grab-key)
(setq exwm-input--global-prefix-keys nil) (setq exwm-input--global-prefix-keys nil)
(dolist (i exwm-input--global-keys) (dolist (i exwm-input--global-keys)
(cl-pushnew (elt i 0) exwm-input--global-prefix-keys)) (cl-pushnew (elt i 0) exwm-input--global-prefix-keys))
;; Stop here if the global prefix keys are update-to-date and
;; there's no new workspace.
(unless (equal original exwm-input--global-prefix-keys) (unless (equal original exwm-input--global-prefix-keys)
(setq grab-key (make-instance 'xcb:GrabKey (apply #'exwm-input--grab-global-prefix-keys
:owner-events 0 (slot-value (xcb:+request-unchecked+reply exwm--connection
:grab-window exwm--root (make-instance 'xcb:QueryTree
:modifiers nil :window exwm--root))
:key nil 'children))))))
:pointer-mode xcb:GrabMode:Async
:keyboard-mode xcb:GrabMode:Async)) (defun exwm-input--grab-global-prefix-keys (&rest xwins)
(dolist (k exwm-input--global-prefix-keys) (let ((req (make-instance 'xcb:GrabKey
(setq keysym (xcb:keysyms:event->keysym exwm--connection k) :owner-events 0
keycode (xcb:keysyms:keysym->keycode exwm--connection :grab-window nil
(car keysym))) :modifiers nil
(setf (slot-value grab-key 'modifiers) (cdr keysym) :key nil
(slot-value grab-key 'key) keycode) :pointer-mode xcb:GrabMode:Async
(when (or (= 0 keycode) :keyboard-mode xcb:GrabMode:Async))
(xcb:+request-checked+request-check exwm--connection keysym keycode)
grab-key) (dolist (k exwm-input--global-prefix-keys)
;; Also grab this key with num-lock mask set. (setq keysym (xcb:keysyms:event->keysym exwm--connection k)
(when (/= 0 xcb:keysyms:num-lock-mask) keycode (xcb:keysyms:keysym->keycode exwm--connection
(setf (slot-value grab-key 'modifiers) (car keysym)))
(logior (cdr keysym) (setf (slot-value req 'modifiers) (cdr keysym)
xcb:keysyms:num-lock-mask)) (slot-value req 'key) keycode)
(xcb:+request-checked+request-check exwm--connection (dolist (xwin xwins)
grab-key))) (setf (slot-value req 'grab-window) xwin)
(user-error "[EXWM] Failed to grab key: %s" (xcb:+request exwm--connection req)
(single-key-description k)))))))) ;; Also grab this key with num-lock mask set.
(when (/= 0 xcb:keysyms:num-lock-mask)
(setf (slot-value req 'modifiers)
(logior (cdr keysym) xcb:keysyms:num-lock-mask))
(xcb:+request exwm--connection req))))
(xcb:flush exwm--connection)))
;;;###autoload ;;;###autoload
(defun exwm-input-set-key (key command) (defun exwm-input-set-key (key command)
@ -635,6 +634,7 @@ called interactively. Only invoke it non-interactively in configuration."
:grab-window id :grab-window id
:modifiers xcb:ModMask:Any)) :modifiers xcb:ModMask:Any))
(exwm--log "Failed to release keyboard for #x%x" id)) (exwm--log "Failed to release keyboard for #x%x" id))
(exwm-input--grab-global-prefix-keys id)
(with-current-buffer (exwm--id->buffer id) (with-current-buffer (exwm--id->buffer id)
(setq exwm--on-KeyPress #'exwm-input--on-KeyPress-char-mode)))) (setq exwm--on-KeyPress #'exwm-input--on-KeyPress-char-mode))))
@ -877,13 +877,13 @@ Its usage is the same with `exwm-input-set-simulation-keys'."
;; Attach event listeners ;; Attach event listeners
(xcb:+event exwm--connection 'xcb:PropertyNotify (xcb:+event exwm--connection 'xcb:PropertyNotify
#'exwm-input--on-PropertyNotify) #'exwm-input--on-PropertyNotify)
(xcb:+event exwm--connection 'xcb:CreateNotify #'exwm-input--on-CreateNotify)
(xcb:+event exwm--connection 'xcb:KeyPress #'exwm-input--on-KeyPress) (xcb:+event exwm--connection 'xcb:KeyPress #'exwm-input--on-KeyPress)
(xcb:+event exwm--connection 'xcb:ButtonPress #'exwm-input--on-ButtonPress) (xcb:+event exwm--connection 'xcb:ButtonPress #'exwm-input--on-ButtonPress)
(xcb:+event exwm--connection 'xcb:ButtonRelease (xcb:+event exwm--connection 'xcb:ButtonRelease
#'exwm-floating--stop-moveresize) #'exwm-floating--stop-moveresize)
(xcb:+event exwm--connection 'xcb:MotionNotify (xcb:+event exwm--connection 'xcb:MotionNotify
#'exwm-floating--do-moveresize) #'exwm-floating--do-moveresize)
(xcb:+event exwm--connection 'xcb:FocusIn #'exwm-input--on-FocusIn)
(when mouse-autoselect-window (when mouse-autoselect-window
(xcb:+event exwm--connection 'xcb:EnterNotify (xcb:+event exwm--connection 'xcb:EnterNotify
#'exwm-input--on-EnterNotify)) #'exwm-input--on-EnterNotify))