From 1c79e1c2384128915357ea629fc2a0503bd36733 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 20 Feb 2016 14:52:53 +0800 Subject: [PATCH] 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). --- exwm-floating.el | 35 +++++++++++++++++++++++++++----- exwm-layout.el | 19 +++++++++-------- exwm-manage.el | 53 ++++++++++++++++++++++++++++++++++-------------- 3 files changed, 78 insertions(+), 29 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 209539e..2769488 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 c0f3c61..e3c1feb 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 224ee16..c9c6ff9 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)))