about summary refs log tree commit diff
diff options
context:
space:
mode:
authorChris Feng <chris.w.feng@gmail.com>2016-02-20T06·52+0800
committerChris Feng <chris.w.feng@gmail.com>2016-02-20T06·52+0800
commit1c79e1c2384128915357ea629fc2a0503bd36733 (patch)
treeea513389fc4628128d78b6d6a4080b93dba6e945
parent33254c37df052996d63ff2a55efeb2b6e8799694 (diff)
Prevent/Reduce flickering issues with floating X windows
* exwm-floating.el (exwm-floating--set-floating)
(exwm-floating--unset-floating): Prevent flickering when creating/removing
a floating X window.
* exwm-layout.el (exwm-layout--show): Show X windows after resizing to
prevent flickering.
* exwm-manage.el (exwm-manage--unmanage-window): Reduce flickering by
hiding the container.
(exwm-manage--kill-buffer-query-function): Prevent flickering by hiding the
container (except that the X window destroys itself after receiving the
WM_DELETE_WINDOW client message).
-rw-r--r--exwm-floating.el35
-rw-r--r--exwm-layout.el19
-rw-r--r--exwm-manage.el53
3 files changed, 78 insertions, 29 deletions
diff --git a/exwm-floating.el b/exwm-floating.el
index 209539eb4a6c..276948801d95 100644
--- a/exwm-floating.el
+++ b/exwm-floating.el
@@ -193,17 +193,42 @@
       (remove-hook 'window-configuration-change-hook #'exwm-layout--refresh)
       (set-window-buffer window (current-buffer)) ;this changes current buffer
       (add-hook 'window-configuration-change-hook #'exwm-layout--refresh)
-      (set-window-dedicated-p window t)
-      (exwm-layout--show id window))
-    (select-frame-set-input-focus frame))
-  (run-hooks 'exwm-floating-setup-hook))
+      (set-window-dedicated-p window t))
+    (select-frame-set-input-focus frame)
+    ;; `x_make_frame_visible' autoraises the frame.  Force lowering it.
+    (xcb:+request exwm--connection
+        (make-instance 'xcb:ConfigureWindow
+                       :window outer-id
+                       :value-mask xcb:ConfigWindow:StackMode
+                       :stack-mode xcb:StackMode:Below))
+    ;; Show the X window with its container (and flush).
+    (exwm-layout--show id window))
+  (run-hooks 'exwm-floating-setup-hook)
+  ;; Redraw the frame.
+  (redisplay))
 
 (defun exwm-floating--unset-floating (id)
   "Make window ID non-floating."
   (let ((buffer (exwm--id->buffer id)))
     (with-current-buffer buffer
-      ;; Reparent the frame back to the root window.
       (when exwm--floating-frame
+        ;; The X window is already mapped.
+        ;; Unmap the container to prevent flickering.
+        (xcb:+request exwm--connection
+            (make-instance 'xcb:UnmapWindow :window exwm--container))
+        (xcb:flush exwm--connection)
+        ;; Unmap the X window.
+        (xcb:+request exwm--connection
+            (make-instance 'xcb:ChangeWindowAttributes
+                           :window id :value-mask xcb:CW:EventMask
+                           :event-mask xcb:EventMask:NoEvent))
+        (xcb:+request exwm--connection
+            (make-instance 'xcb:UnmapWindow :window id))
+        (xcb:+request exwm--connection
+            (make-instance 'xcb:ChangeWindowAttributes
+                           :window id :value-mask xcb:CW:EventMask
+                           :event-mask exwm--client-event-mask))
+        ;; Reparent the floating frame back to the root window.
         (let ((frame-id (frame-parameter exwm--floating-frame 'exwm-outer-id)))
           (xcb:+request exwm--connection
               (make-instance 'xcb:UnmapWindow :window frame-id))
diff --git a/exwm-layout.el b/exwm-layout.el
index c0f3c6147289..e3c1febc4478 100644
--- a/exwm-layout.el
+++ b/exwm-layout.el
@@ -53,14 +53,6 @@
 (defun exwm-layout--show (id &optional window)
   "Show window ID exactly fit in the Emacs window WINDOW."
   (exwm--log "Show #x%x in %s" id window)
-  (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id))
-  (with-current-buffer (exwm--id->buffer id)
-    (xcb:+request exwm--connection
-        (make-instance 'xcb:MapWindow :window exwm--container)))
-  (xcb:+request exwm--connection
-      (make-instance 'xcb:icccm:set-WM_STATE
-                     :window id :state xcb:icccm:WM_STATE:NormalState
-                     :icon xcb:Window:None))
   (let* ((edges (window-inside-absolute-pixel-edges window))
          (width (- (elt edges 2) (elt edges 0)))
          (height (- (elt edges 3) (elt edges 1))))
@@ -93,7 +85,16 @@
                                          (elt relative-edges 0)
                                          (elt relative-edges 1)
                                          width height
-                                         (active-minibuffer-window)))))
+                                         (active-minibuffer-window))))
+      ;; Make the resizing take effect.
+      (xcb:flush exwm--connection)
+      (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id))
+      (xcb:+request exwm--connection
+          (make-instance 'xcb:MapWindow :window exwm--container))
+      (xcb:+request exwm--connection
+          (make-instance 'xcb:icccm:set-WM_STATE
+                         :window id :state xcb:icccm:WM_STATE:NormalState
+                         :icon xcb:Window:None)))
     (xcb:+request exwm--connection
         (make-instance 'xcb:SendEvent
                        :propagate 0 :destination id
diff --git a/exwm-manage.el b/exwm-manage.el
index 224ee16aa890..c9c6ff9d2c6c 100644
--- a/exwm-manage.el
+++ b/exwm-manage.el
@@ -213,14 +213,18 @@ corresponding buffer.")
 (defun exwm-manage--unmanage-window (id &optional withdraw-only)
   "Unmanage window ID."
   (let ((buffer (exwm--id->buffer id)))
-    (exwm--log "Unmanage #x%x (buffer: %s)" id buffer)
+    (exwm--log "Unmanage #x%x (buffer: %s, widthdraw: %s)"
+               id buffer withdraw-only)
     (setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist))
     (when (buffer-live-p buffer)
       (with-current-buffer buffer
-        ;; Hide the floating frame as early as possible.
-        (when exwm--floating-frame (make-frame-invisible exwm--floating-frame))
+        ;; Flickering seems unavoidable here if the DestroyWindow request is
+        ;; not initiated by us.
+        ;; What we can do is to hide the its container ASAP.
         (xcb:+request exwm--connection
             (make-instance 'xcb:UnmapWindow :window exwm--container))
+        (xcb:flush exwm--connection)
+        ;;
         (setq exwm-workspace--switch-history-outdated t)
         ;;
         (when withdraw-only
@@ -254,12 +258,13 @@ corresponding buffer.")
                              :window id :property xcb:Atom:WM_STATE)))
         ;; Destroy the container (it seems it has to be delayed).
         (when exwm--floating-frame
-          (xcb:+request exwm--connection
-              (make-instance 'xcb:ReparentWindow
-                             :window (frame-parameter exwm--floating-frame
-                                                      'exwm-outer-id)
-                             :parent exwm--root
-                             :x 0 :y 0)))
+          ;; Unmap the floating frame.
+          (let ((window (frame-parameter exwm--floating-frame 'exwm-outer-id)))
+            (xcb:+request exwm--connection
+                (make-instance 'xcb:UnmapWindow :window window))
+            (xcb:+request exwm--connection
+                (make-instance 'xcb:ReparentWindow
+                               :window window :parent exwm--root :x 0 :y 0))))
         (xcb:+request exwm--connection
             (make-instance 'xcb:DestroyWindow :window exwm--container))
         (xcb:flush exwm--connection)
@@ -268,12 +273,12 @@ corresponding buffer.")
           (kill-buffer)
           (when floating
             (select-window
-             (frame-selected-window exwm-workspace--current))))))
-    (xcb:+request exwm--connection      ;update _NET_CLIENT_LIST
-        (make-instance 'xcb:ewmh:set-_NET_CLIENT_LIST
-                       :window exwm--root
-                       :data (vconcat (mapcar #'car exwm--id-buffer-alist))))
-    (xcb:flush exwm--connection)))
+             (frame-selected-window exwm-workspace--current)))))
+      (xcb:+request exwm--connection    ;update _NET_CLIENT_LIST
+          (make-instance 'xcb:ewmh:set-_NET_CLIENT_LIST
+                         :window exwm--root
+                         :data (vconcat (mapcar #'car exwm--id-buffer-alist))))
+      (xcb:flush exwm--connection))))
 
 (defun exwm-manage--scan ()
   "Search for existing windows and try to manage them."
@@ -300,6 +305,10 @@ corresponding buffer.")
               (make-instance 'xcb:MapWindow :window exwm--id))
       ;; The X window is no longer alive so just close the buffer.
       ;; Destroy the container.
+      ;; Hide the container to prevent flickering.
+      (xcb:+request exwm--connection
+          (make-instance 'xcb:UnmapWindow :window exwm--container))
+      (xcb:flush exwm--connection)
       (when exwm--floating-frame
         (xcb:+request exwm--connection
             (make-instance 'xcb:ReparentWindow
@@ -313,6 +322,9 @@ corresponding buffer.")
       (throw 'return t))
     (unless (memq xcb:Atom:WM_DELETE_WINDOW exwm--protocols)
       ;; The X window does not support WM_DELETE_WINDOW; destroy it.
+      ;; Hide the container to prevent flickering.
+      (xcb:+request exwm--connection
+          (make-instance 'xcb:UnmapWindow :window exwm--container))
       (xcb:+request exwm--connection
           (make-instance 'xcb:DestroyWindow :window exwm--id))
       (xcb:flush exwm--connection)
@@ -331,6 +343,10 @@ corresponding buffer.")
     (unless (memq xcb:Atom:_NET_WM_PING exwm--protocols)
       ;; The window does not support _NET_WM_PING.  To make sure it'll die,
       ;; kill it after the time runs out.
+      ;; Hide the container to prevent flickering.
+      (xcb:+request exwm--connection
+          (make-instance 'xcb:UnmapWindow :window exwm--container))
+      (xcb:flush exwm--connection)
       (run-with-timer exwm-manage-ping-timeout nil
                       `(lambda () (exwm-manage--kill-client ,exwm--id)))
       ;; Wait for DestroyNotify event.
@@ -368,6 +384,13 @@ Would you like to kill it? "
 (defun exwm-manage--kill-client (&optional id)
   "Kill an X client."
   (unless id (setq id (exwm--buffer->id (current-buffer))))
+  ;; Hide the container to prevent flickering.
+  (let ((buffer (exwm--id->buffer id)))
+    (when buffer
+      (with-current-buffer buffer
+        (xcb:+request exwm--connection
+            (make-instance 'xcb:UnmapWindow :window exwm--container))
+        (xcb:flush exwm--connection))))
   (let* ((response (xcb:+request-unchecked+reply exwm--connection
                        (make-instance 'xcb:ewmh:get-_NET_WM_PID :window id)))
          (pid (and response (slot-value response 'value)))