about summary refs log tree commit diff
diff options
context:
space:
mode:
authorChris Feng <chris.w.feng@gmail.com>2016-08-09T05·34+0800
committerChris Feng <chris.w.feng@gmail.com>2016-08-09T05·34+0800
commit810b4716a10169e1de29c52cf2e3aeb2e79f2018 (patch)
tree5626c3a971a6addbc56994fce84dc0417206bd78
parent767abdf9e6fa20699faccf7a3388ad2fa6a52b9f (diff)
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.
-rw-r--r--exwm-floating.el5
-rw-r--r--exwm-input.el129
-rw-r--r--exwm-workspace.el9
-rw-r--r--exwm.el3
4 files changed, 112 insertions, 34 deletions
diff --git a/exwm-floating.el b/exwm-floating.el
index 56d2932d2c96..7c5d811bf5ca 100644
--- a/exwm-floating.el
+++ b/exwm-floating.el
@@ -99,7 +99,9 @@ context of the corresponding buffer.")
                      (height . ,window-min-height)
                      (unsplittable . t))))) ;and fix the size later
          (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))
          (window (frame-first-window frame)) ;and it's the only window
          (x (slot-value exwm--geometry 'x))
@@ -118,6 +120,7 @@ context of the corresponding buffer.")
                width height x y)
     ;; Save frame parameters.
     (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)
     ;; Fix illegal parameters
     ;; FIXME: check normal hints restrictions
diff --git a/exwm-input.el b/exwm-input.el
index 7506267ee77c..668e49585e55 100644
--- a/exwm-input.el
+++ b/exwm-input.el
@@ -48,10 +48,9 @@
 (defvar exwm-input--resize-keysym nil)
 (defvar exwm-input--resize-mask nil)
 
-(defvar exwm-input--timestamp xcb:Time:CurrentTime
-  "A recent timestamp received from X server.
-
-It's updated in several occasions, and only used by `exwm-input--set-focus'.")
+(defvar exwm-input--timestamp-window nil)
+(defvar exwm-input--timestamp-atom nil)
+(defvar exwm-input--timestamp-callback nil)
 
 (defvar exwm-workspace--current)
 (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)
   "Set input focus to window ID in a proper way."
   (when (exwm--id->buffer id)
-    (with-current-buffer (exwm--id->buffer id)
-      (if (and (not exwm--hints-input)
-               (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols))
-          (progn
-            (exwm--log "Focus on #x%x with WM_TAKE_FOCUS" 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)
+          (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
-                (make-instance 'xcb:icccm:SendEvent
-                               :destination id
-                               :event (xcb:marshal
-                                       (make-instance 'xcb:icccm:WM_TAKE_FOCUS
-                                                      :window id
-                                                      :time
-                                                      exwm-input--timestamp)
-                                       exwm--connection))))
-        (exwm--log "Focus on #x%x with SetInputFocus" id)
-        (xcb:+request exwm--connection
-            (make-instance 'xcb:SetInputFocus
-                           :revert-to xcb:InputFocus:Parent
-                           :focus id
-                           :time xcb:Time:CurrentTime)))
-      (exwm-input--set-active-window id)
-      (xcb:flush exwm--connection))))
+                (make-instance 'xcb:SetInputFocus
+                               :revert-to xcb:InputFocus:Parent
+                               :focus id
+                               :time xcb:Time:CurrentTime))
+            (exwm-input--set-active-window id)
+            (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)
   "Handle FocusIn events."
   (let ((obj (make-instance 'xcb:FocusIn)))
     (xcb:unmarshal obj data)
-    (when (= (slot-value obj 'detail) xcb:NotifyDetail:Inferior)
-      ;; Transfer input focus back to the workspace when the workspace
-      ;; container unexpectedly receives it.
-      (x-focus-frame exwm-workspace--current))))
+    ;; Not sure if this is the right thing to do but the point is the
+    ;; input focus should not stay at the root window or any container,
+    ;; or the result would be unpredictable.  `x-focus-frame' would
+    ;; 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 ()
   "Run in `exwm-input--update-global-prefix-keys'."
@@ -247,7 +290,6 @@ This value should always be overwritten.")
         window buffer frame)
     (xcb:unmarshal obj data)
     (with-slots (detail time event state) obj
-      (setq exwm-input--timestamp time)
       (setq window (get-buffer-window (exwm--id->buffer event) t)
             buffer (window-buffer window))
       (cond ((and (= state exwm-input--move-mask)
@@ -296,7 +338,6 @@ This value should always be overwritten.")
   "Handle KeyPress event."
   (let ((obj (make-instance 'xcb:KeyPress)))
     (xcb:unmarshal obj data)
-    (setq exwm-input--timestamp (slot-value obj 'time))
     (if (eq major-mode 'exwm-mode)
         (funcall exwm--on-KeyPress obj data)
       (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--resize-keysym (car 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
+  (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:ButtonPress #'exwm-input--on-ButtonPress)
   (xcb:+event exwm--connection 'xcb:ButtonRelease
diff --git a/exwm-workspace.el b/exwm-workspace.el
index 4789aeb18ee9..977cfe64bbcb 100644
--- a/exwm-workspace.el
+++ b/exwm-workspace.el
@@ -691,10 +691,13 @@ INDEX must not exceed the current number of workspaces."
                        (outer-id (string-to-number
                                   (frame-parameter new-frame
                                                    'outer-window-id)))
+                       (window-id (string-to-number
+                                   (frame-parameter new-frame 'window-id)))
                        (frame-container (frame-parameter old-frame
                                                          'exwm-container))
                        (window (frame-root-window new-frame)))
                   (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
                                        frame-container)
                   (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)))
     (let ((outer-id (string-to-number (frame-parameter frame
                                                        'outer-window-id)))
+          (window-id (string-to-number (frame-parameter frame 'window-id)))
           (container (xcb:generate-id exwm--connection))
           (workspace (xcb:generate-id exwm--connection)))
       ;; Save window IDs
       (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-workspace workspace)
       ;; In case it's created by emacsclient.
@@ -1356,9 +1361,13 @@ applied to all subsequently created X frames."
       (let ((outer-id (string-to-number
                        (frame-parameter exwm-workspace--minibuffer
                                         'outer-window-id)))
+            (window-id (string-to-number
+                        (frame-parameter exwm-workspace--minibuffer
+                                         'window-id)))
             (container (xcb:generate-id exwm--connection)))
         (set-frame-parameter exwm-workspace--minibuffer
                              'exwm-outer-id outer-id)
+        (set-frame-parameter exwm-workspace--minibuffer 'exwm-id window-id)
         (set-frame-parameter exwm-workspace--minibuffer 'exwm-container
                              container)
         (xcb:+request exwm--connection
diff --git a/exwm.el b/exwm.el
index dd279bbcd091..afec1526ce3d 100644
--- a/exwm.el
+++ b/exwm.el
@@ -315,8 +315,7 @@
         atom id buffer)
     (xcb:unmarshal obj data)
     (setq id (slot-value obj 'window)
-          atom (slot-value obj 'atom)
-          exwm-input--timestamp (slot-value obj 'time))
+          atom (slot-value obj 'atom))
     (setq buffer (exwm--id->buffer id))
     (if (not (buffer-live-p buffer))
         ;; Properties of unmanaged X windows.