mirror of
https://github.com/emacs-exwm/exwm.git
synced 2024-11-27 14:57:59 +01:00
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:
parent
767abdf9e6
commit
810b4716a1
4 changed files with 112 additions and 34 deletions
|
@ -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
|
||||||
|
|
129
exwm-input.el
129
exwm-input.el
|
@ -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)
|
||||||
(with-current-buffer (exwm--id->buffer id)
|
(let ((focus (slot-value (xcb:+request-unchecked+reply exwm--connection
|
||||||
(if (and (not exwm--hints-input)
|
(make-instance 'xcb:GetInputFocus))
|
||||||
(memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols))
|
'focus)))
|
||||||
(progn
|
(unless (= focus id)
|
||||||
(exwm--log "Focus on #x%x with WM_TAKE_FOCUS" id)
|
(with-current-buffer (exwm--id->buffer id)
|
||||||
|
(cond
|
||||||
|
((and (not exwm--hints-input)
|
||||||
|
(memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols))
|
||||||
|
(when (= focus (frame-parameter nil 'exwm-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
|
||||||
|
(make-instance 'xcb:icccm:SendEvent
|
||||||
|
:destination id
|
||||||
|
:event event))
|
||||||
|
(exwm-input--set-active-window id)
|
||||||
|
(xcb:flush exwm--connection)))
|
||||||
|
id)))
|
||||||
|
(t
|
||||||
|
(exwm--log "Focus on #x%x with SetInputFocus" id)
|
||||||
(xcb:+request exwm--connection
|
(xcb:+request exwm--connection
|
||||||
(make-instance 'xcb:icccm:SendEvent
|
(make-instance 'xcb:SetInputFocus
|
||||||
:destination id
|
:revert-to xcb:InputFocus:Parent
|
||||||
:event (xcb:marshal
|
:focus id
|
||||||
(make-instance 'xcb:icccm:WM_TAKE_FOCUS
|
:time xcb:Time:CurrentTime))
|
||||||
:window id
|
(exwm-input--set-active-window id)
|
||||||
:time
|
(xcb:flush exwm--connection))))))))
|
||||||
exwm-input--timestamp)
|
|
||||||
exwm--connection))))
|
(defun exwm-input--update-timestamp (callback &rest args)
|
||||||
(exwm--log "Focus on #x%x with SetInputFocus" id)
|
"Fetch the latest timestamp from the server and feed it to CALLBACK.
|
||||||
(xcb:+request exwm--connection
|
|
||||||
(make-instance 'xcb:SetInputFocus
|
ARGS are additional arguments to CALLBACK."
|
||||||
:revert-to xcb:InputFocus:Parent
|
(setq exwm-input--timestamp-callback (cons callback args))
|
||||||
:focus id
|
(xcb:+request exwm--connection
|
||||||
:time xcb:Time:CurrentTime)))
|
(make-instance 'xcb:ChangeProperty
|
||||||
(exwm-input--set-active-window id)
|
:mode xcb:PropMode:Replace
|
||||||
(xcb:flush exwm--connection))))
|
: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
|
||||||
|
|
|
@ -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
|
||||||
|
|
3
exwm.el
3
exwm.el
|
@ -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.
|
||||||
|
|
Loading…
Reference in a new issue