Update timestamp for WM_TAKE_FOCUS ClientMessage

* exwm-input.el (exwm-input--timestamp-window)
(exwm-input--timestamp-atom, exwm-input--timestamp-callback): New
variables for updating timestamp.
(exwm-input--set-focus): Send WM_TAKE_FOCUS ClientMessage with updated
timestamp.
(exwm-input--update-timestamp): New utility function for fetching
timestamp.
(exwm-input--on-PropertyNotify): New function for handling
PropertyNotify event to extract the timestamp.
(exwm-input--init): Create resources for updating timestamp; attach the
event listener.
(exwm-input--on-ButtonPress, exwm-input--on-KeyPress):
* exwm.el (exwm--on-PropertyNotify): No longer update timestamp.

* exwm-input.el (exwm-input--set-focus): Avoid setting input focus on
already focused X windows, or when the input focus in not on a Emacs
frame if globally active model is in use.

* exwm-floating.el (exwm-floating--set-floating):
* exwm-workspace.el (exwm-workspace-move-window)
(exwm-workspace--add-frame-as-workspace, exwm-workspace--init):
Set 'exwm-id' frame parameter as the numerical (inner) frame X ID.
This commit is contained in:
Chris Feng 2016-08-09 13:34:29 +08:00
parent 767abdf9e6
commit 810b4716a1
4 changed files with 112 additions and 34 deletions

View file

@ -99,7 +99,9 @@ context of the corresponding buffer.")
(height . ,window-min-height) (height . ,window-min-height)
(unsplittable . t))))) ;and fix the size later (unsplittable . t))))) ;and fix the size later
(outer-id (string-to-number (frame-parameter frame 'outer-window-id))) (outer-id (string-to-number (frame-parameter frame 'outer-window-id)))
(container (buffer-local-value 'exwm--container (exwm--id->buffer id))) (window-id (string-to-number (frame-parameter frame 'window-id)))
(container (buffer-local-value 'exwm--container
(exwm--id->buffer id)))
(frame-container (xcb:generate-id exwm--connection)) (frame-container (xcb:generate-id exwm--connection))
(window (frame-first-window frame)) ;and it's the only window (window (frame-first-window frame)) ;and it's the only window
(x (slot-value exwm--geometry 'x)) (x (slot-value exwm--geometry 'x))
@ -118,6 +120,7 @@ context of the corresponding buffer.")
width height x y) width height x y)
;; Save frame parameters. ;; Save frame parameters.
(set-frame-parameter frame 'exwm-outer-id outer-id) (set-frame-parameter frame 'exwm-outer-id outer-id)
(set-frame-parameter frame 'exwm-id window-id)
(set-frame-parameter frame 'exwm-container frame-container) (set-frame-parameter frame 'exwm-container frame-container)
;; Fix illegal parameters ;; Fix illegal parameters
;; FIXME: check normal hints restrictions ;; FIXME: check normal hints restrictions

View file

@ -48,10 +48,9 @@
(defvar exwm-input--resize-keysym nil) (defvar exwm-input--resize-keysym nil)
(defvar exwm-input--resize-mask nil) (defvar exwm-input--resize-mask nil)
(defvar exwm-input--timestamp xcb:Time:CurrentTime (defvar exwm-input--timestamp-window nil)
"A recent timestamp received from X server. (defvar exwm-input--timestamp-atom nil)
(defvar exwm-input--timestamp-callback nil)
It's updated in several occasions, and only used by `exwm-input--set-focus'.")
(defvar exwm-workspace--current) (defvar exwm-workspace--current)
(defvar exwm-workspace--switch-history-outdated) (defvar exwm-workspace--switch-history-outdated)
@ -62,37 +61,81 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
(defun exwm-input--set-focus (id) (defun exwm-input--set-focus (id)
"Set input focus to window ID in a proper way." "Set input focus to window ID in a proper way."
(when (exwm--id->buffer id) (when (exwm--id->buffer id)
(let ((focus (slot-value (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:GetInputFocus))
'focus)))
(unless (= focus id)
(with-current-buffer (exwm--id->buffer id) (with-current-buffer (exwm--id->buffer id)
(if (and (not exwm--hints-input) (cond
((and (not exwm--hints-input)
(memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols)) (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols))
(progn (when (= focus (frame-parameter nil 'exwm-id))
(exwm--log "Focus on #x%x with WM_TAKE_FOCUS" id) (exwm--log "Focus on #x%x with WM_TAKE_FOCUS" id)
(exwm-input--update-timestamp
(lambda (timestamp id)
(let ((event (make-instance 'xcb:icccm:WM_TAKE_FOCUS
:window id
:time timestamp)))
(setq event (xcb:marshal event exwm--connection))
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:icccm:SendEvent (make-instance 'xcb:icccm:SendEvent
:destination id :destination id
:event (xcb:marshal :event event))
(make-instance 'xcb:icccm:WM_TAKE_FOCUS (exwm-input--set-active-window id)
:window id (xcb:flush exwm--connection)))
:time id)))
exwm-input--timestamp) (t
exwm--connection))))
(exwm--log "Focus on #x%x with SetInputFocus" id) (exwm--log "Focus on #x%x with SetInputFocus" id)
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:SetInputFocus (make-instance 'xcb:SetInputFocus
:revert-to xcb:InputFocus:Parent :revert-to xcb:InputFocus:Parent
:focus id :focus id
:time xcb:Time:CurrentTime))) :time xcb:Time:CurrentTime))
(exwm-input--set-active-window id) (exwm-input--set-active-window id)
(xcb:flush exwm--connection)))) (xcb:flush exwm--connection))))))))
(defun exwm-input--update-timestamp (callback &rest args)
"Fetch the latest timestamp from the server and feed it to CALLBACK.
ARGS are additional arguments to CALLBACK."
(setq exwm-input--timestamp-callback (cons callback args))
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeProperty
:mode xcb:PropMode:Replace
:window exwm-input--timestamp-window
:property exwm-input--timestamp-atom
:type xcb:Atom:CARDINAL
:format 32
:data-len 0
:data nil))
(xcb:flush exwm--connection))
(defun exwm-input--on-PropertyNotify (data _synthetic)
"Handle PropertyNotify events."
(when exwm-input--timestamp-callback
(let ((obj (make-instance 'xcb:PropertyNotify)))
(xcb:unmarshal obj data)
(when (= exwm-input--timestamp-window
(slot-value obj 'window))
(apply (car exwm-input--timestamp-callback)
(slot-value obj 'time)
(cdr exwm-input--timestamp-callback))
(setq exwm-input--timestamp-callback nil)))))
(defun exwm-input--on-FocusIn (data _synthetic) (defun exwm-input--on-FocusIn (data _synthetic)
"Handle FocusIn events." "Handle FocusIn events."
(let ((obj (make-instance 'xcb:FocusIn))) (let ((obj (make-instance 'xcb:FocusIn)))
(xcb:unmarshal obj data) (xcb:unmarshal obj data)
(when (= (slot-value obj 'detail) xcb:NotifyDetail:Inferior) ;; Not sure if this is the right thing to do but the point is the
;; Transfer input focus back to the workspace when the workspace ;; input focus should not stay at the root window or any container,
;; container unexpectedly receives it. ;; or the result would be unpredictable. `x-focus-frame' would
(x-focus-frame exwm-workspace--current)))) ;; first set the input focus to the (previously) selected frame, and
;; then `select-window' would further update the input focus if the
;; selected window is displaying an `exwm-mode' buffer. Perhaps we
;; should carefully filter out FocusIn events with certain 'detail'
;; and 'mode' combinations, but this just works.
(x-focus-frame (selected-frame))
(select-window (selected-window))))
(defun exwm-input--on-workspace-list-change () (defun exwm-input--on-workspace-list-change ()
"Run in `exwm-input--update-global-prefix-keys'." "Run in `exwm-input--update-global-prefix-keys'."
@ -247,7 +290,6 @@ This value should always be overwritten.")
window buffer frame) window buffer frame)
(xcb:unmarshal obj data) (xcb:unmarshal obj data)
(with-slots (detail time event state) obj (with-slots (detail time event state) obj
(setq exwm-input--timestamp time)
(setq window (get-buffer-window (exwm--id->buffer event) t) (setq window (get-buffer-window (exwm--id->buffer event) t)
buffer (window-buffer window)) buffer (window-buffer window))
(cond ((and (= state exwm-input--move-mask) (cond ((and (= state exwm-input--move-mask)
@ -296,7 +338,6 @@ This value should always be overwritten.")
"Handle KeyPress event." "Handle KeyPress event."
(let ((obj (make-instance 'xcb:KeyPress))) (let ((obj (make-instance 'xcb:KeyPress)))
(xcb:unmarshal obj data) (xcb:unmarshal obj data)
(setq exwm-input--timestamp (slot-value obj 'time))
(if (eq major-mode 'exwm-mode) (if (eq major-mode 'exwm-mode)
(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))))
@ -657,7 +698,33 @@ Its usage is the same with `exwm-input-set-simulation-keys'."
exwm-input--move-mask (cdr move-key) exwm-input--move-mask (cdr move-key)
exwm-input--resize-keysym (car resize-key) exwm-input--resize-keysym (car resize-key)
exwm-input--resize-mask (cdr resize-key))) exwm-input--resize-mask (cdr resize-key)))
;; Create the X window and intern the atom used to fetch timestamp.
(setq exwm-input--timestamp-window (xcb:generate-id exwm--connection))
(xcb:+request exwm--connection
(make-instance 'xcb:CreateWindow
:depth 0
:wid exwm-input--timestamp-window
:parent exwm--root
:x -1
:y -1
:width 1
:height 1
:border-width 0
:class xcb:WindowClass:CopyFromParent
:visual 0
:value-mask xcb:CW:EventMask
:event-mask xcb:EventMask:PropertyChange))
(let ((atom "_TIME"))
(setq exwm-input--timestamp-atom
(slot-value (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:InternAtom
:only-if-exists 0
:name-len (length atom)
:name atom))
'atom)))
;; Attach event listeners ;; Attach event listeners
(xcb:+event exwm--connection 'xcb:PropertyNotify
#'exwm-input--on-PropertyNotify)
(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

View file

@ -691,10 +691,13 @@ INDEX must not exceed the current number of workspaces."
(outer-id (string-to-number (outer-id (string-to-number
(frame-parameter new-frame (frame-parameter new-frame
'outer-window-id))) 'outer-window-id)))
(window-id (string-to-number
(frame-parameter new-frame 'window-id)))
(frame-container (frame-parameter old-frame (frame-container (frame-parameter old-frame
'exwm-container)) 'exwm-container))
(window (frame-root-window new-frame))) (window (frame-root-window new-frame)))
(set-frame-parameter new-frame 'exwm-outer-id outer-id) (set-frame-parameter new-frame 'exwm-outer-id outer-id)
(set-frame-parameter new-frame 'exwm-id window-id)
(set-frame-parameter new-frame 'exwm-container (set-frame-parameter new-frame 'exwm-container
frame-container) frame-container)
(make-frame-invisible new-frame) (make-frame-invisible new-frame)
@ -1149,10 +1152,12 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
(setq exwm-workspace--list (nconc exwm-workspace--list (list frame))) (setq exwm-workspace--list (nconc exwm-workspace--list (list frame)))
(let ((outer-id (string-to-number (frame-parameter frame (let ((outer-id (string-to-number (frame-parameter frame
'outer-window-id))) 'outer-window-id)))
(window-id (string-to-number (frame-parameter frame 'window-id)))
(container (xcb:generate-id exwm--connection)) (container (xcb:generate-id exwm--connection))
(workspace (xcb:generate-id exwm--connection))) (workspace (xcb:generate-id exwm--connection)))
;; Save window IDs ;; Save window IDs
(set-frame-parameter frame 'exwm-outer-id outer-id) (set-frame-parameter frame 'exwm-outer-id outer-id)
(set-frame-parameter frame 'exwm-id window-id)
(set-frame-parameter frame 'exwm-container container) (set-frame-parameter frame 'exwm-container container)
(set-frame-parameter frame 'exwm-workspace workspace) (set-frame-parameter frame 'exwm-workspace workspace)
;; In case it's created by emacsclient. ;; In case it's created by emacsclient.
@ -1356,9 +1361,13 @@ applied to all subsequently created X frames."
(let ((outer-id (string-to-number (let ((outer-id (string-to-number
(frame-parameter exwm-workspace--minibuffer (frame-parameter exwm-workspace--minibuffer
'outer-window-id))) 'outer-window-id)))
(window-id (string-to-number
(frame-parameter exwm-workspace--minibuffer
'window-id)))
(container (xcb:generate-id exwm--connection))) (container (xcb:generate-id exwm--connection)))
(set-frame-parameter exwm-workspace--minibuffer (set-frame-parameter exwm-workspace--minibuffer
'exwm-outer-id outer-id) 'exwm-outer-id outer-id)
(set-frame-parameter exwm-workspace--minibuffer 'exwm-id window-id)
(set-frame-parameter exwm-workspace--minibuffer 'exwm-container (set-frame-parameter exwm-workspace--minibuffer 'exwm-container
container) container)
(xcb:+request exwm--connection (xcb:+request exwm--connection

View file

@ -315,8 +315,7 @@
atom id buffer) atom id buffer)
(xcb:unmarshal obj data) (xcb:unmarshal obj data)
(setq id (slot-value obj 'window) (setq id (slot-value obj 'window)
atom (slot-value obj 'atom) atom (slot-value obj 'atom))
exwm-input--timestamp (slot-value obj 'time))
(setq buffer (exwm--id->buffer id)) (setq buffer (exwm--id->buffer id))
(if (not (buffer-live-p buffer)) (if (not (buffer-live-p buffer))
;; Properties of unmanaged X windows. ;; Properties of unmanaged X windows.