Fix floating X window bugs introduced by 9c95c03e

* exwm-floating.el (exwm-floating--set-floating): Make floating frames
invisible before resizing them.
(exwm-floating--fit-frame-to-window): Removed since unused.
* exwm-layout.el (exwm-layout-hide-mode-line, exwm-layout-show-mode-line):
Use set frame height instead of exwm-floating--fit-frame-to-window.

* exwm-core.el (exwm-mode): Replace `exwm-manage--close-window' with
`exwm-manage--kill-buffer-query-function'.
* exwm-floating.el (exwm-floating--unset-floating): Reparent out floating
frames.
* exwm-manage.el (exwm-manage--unmanage-window): Reparent out floating
frames.  Hide floating frames.
(exwm-manage--close-window, exwm-manage--kill-buffer-query-function):
Rename `exwm-manage--close-window' since it's only used in
`kill-buffer-query-functions'.  Reparent out floating frames.
This commit is contained in:
Chris Feng 2016-02-18 19:56:01 +08:00
parent 12e2d574c9
commit 3f7722079c
4 changed files with 137 additions and 114 deletions

View file

@ -142,9 +142,7 @@
(add-hook 'change-major-mode-hook #'kill-buffer nil t) (add-hook 'change-major-mode-hook #'kill-buffer nil t)
;; Kill buffer -> close window ;; Kill buffer -> close window
(add-hook 'kill-buffer-query-functions (add-hook 'kill-buffer-query-functions
(lambda () #'exwm-manage--kill-buffer-query-function nil t)
(exwm-manage--close-window exwm--id (current-buffer)))
nil t)
(setq buffer-read-only t (setq buffer-read-only t
left-margin-width nil left-margin-width nil
right-margin-width nil right-margin-width nil

View file

@ -50,6 +50,8 @@
(defvar exwm-floating--cursor-bottom-left nil) (defvar exwm-floating--cursor-bottom-left nil)
(defvar exwm-floating--cursor-left nil) (defvar exwm-floating--cursor-left nil)
(declare-function exwm-layout--refresh "exwm-layout.el")
;;;###autoload ;;;###autoload
(defun exwm-floating--set-floating (id) (defun exwm-floating--set-floating (id)
"Make window ID floating." "Make window ID floating."
@ -78,6 +80,8 @@
(internal-border-width . ,exwm-floating-border-width) (internal-border-width . ,exwm-floating-border-width)
(left . 10000) (left . 10000)
(top . 10000) (top . 10000)
(width . ,window-min-width)
(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 (with-current-buffer (exwm--id->buffer id) (container (with-current-buffer (exwm--id->buffer id)
@ -151,7 +155,17 @@
y (/ (- display-height height) 2)))))) y (/ (- display-height height) 2))))))
(exwm--log "Floating geometry (corrected): %dx%d%+d%+d" width height x y) (exwm--log "Floating geometry (corrected): %dx%d%+d%+d" width height x y)
;; Fit frame to client ;; Fit frame to client
(exwm-floating--fit-frame-to-window outer-id width height) ;; It seems we have to make the frame invisible in order to resize it
;; timely.
;; The frame will be made visible by `select-frame-set-input-focus'.
(make-frame-invisible frame)
(let ((edges (window-inside-pixel-edges window)))
(set-frame-size frame
(+ width (- (frame-pixel-width frame)
(- (elt edges 2) (elt edges 0))))
(+ height (- (frame-pixel-height frame)
(- (elt edges 3) (elt edges 1))))
t))
;; Reparent this frame to the container ;; Reparent this frame to the container
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow (make-instance 'xcb:ReparentWindow
@ -171,8 +185,12 @@
(setq window-size-fixed exwm--fixed-size (setq window-size-fixed exwm--fixed-size
exwm--frame original-frame exwm--frame original-frame
exwm--floating-frame frame) exwm--floating-frame frame)
;; Do the refresh manually.
(remove-hook 'window-configuration-change-hook #'exwm-layout--refresh)
(set-window-buffer window (current-buffer)) ;this changes current buffer (set-window-buffer window (current-buffer)) ;this changes current buffer
(set-window-dedicated-p window t)) (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)) (select-frame-set-input-focus frame))
(run-hooks 'exwm-floating-setup-hook)) (run-hooks 'exwm-floating-setup-hook))
@ -182,6 +200,16 @@
(interactive) (interactive)
(let ((buffer (exwm--id->buffer id))) (let ((buffer (exwm--id->buffer id)))
(with-current-buffer buffer (with-current-buffer buffer
;; Reparent the frame back to the root window.
(when exwm--floating-frame
(let ((frame-id (frame-parameter exwm--floating-frame 'exwm-outer-id)))
(xcb:+request exwm--connection
(make-instance 'xcb:UnmapWindow :window frame-id))
(xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow
:window frame-id
:parent exwm--root
:x 0 :y 0))))
;; Reparent the container to the workspace ;; Reparent the container to the workspace
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow (make-instance 'xcb:ReparentWindow
@ -221,34 +249,6 @@
(exwm-floating--unset-floating exwm--id) (exwm-floating--unset-floating exwm--id)
(exwm-floating--set-floating exwm--id)))) (exwm-floating--set-floating exwm--id))))
;;;###autoload
(defun exwm-floating--fit-frame-to-window (&optional frame-outer-id
width height)
"Resize a floating frame to make it fit the size of the window.
Default to resize `exwm--floating-frame' unless FRAME-OUTER-ID is non-nil.
This function will issue an `xcb:GetGeometry' request unless WIDTH and HEIGHT
are provided. You should call `xcb:flush' and restore the value of
`window-size-fixed' afterwards."
(setq window-size-fixed nil)
(unless (and width height)
(let ((geometry (xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:GetGeometry :drawable exwm--id))))
(setq width (slot-value geometry 'width)
height (slot-value geometry 'height))))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window (or frame-outer-id
(frame-parameter exwm--floating-frame
'exwm-outer-id))
:value-mask (eval-when-compile
(logior xcb:ConfigWindow:Width
xcb:ConfigWindow:Height))
:width (+ width (* 2 exwm-floating-border-width))
:height (+ height (* 2 exwm-floating-border-width)
(window-mode-line-height)
(window-header-line-height)))))
(define-obsolete-function-alias 'exwm-floating-hide-mode-line (define-obsolete-function-alias 'exwm-floating-hide-mode-line
'exwm-layout-hide-mode-line "25.1" "Hide mode-line of a floating frame.") 'exwm-layout-hide-mode-line "25.1" "Hide mode-line of a floating frame.")
(define-obsolete-function-alias 'exwm-floating-show-mode-line (define-obsolete-function-alias 'exwm-floating-show-mode-line

View file

@ -396,13 +396,18 @@ See also `exwm-layout-enlarge-window'."
"Hide mode-line." "Hide mode-line."
(interactive) (interactive)
(when (and (eq major-mode 'exwm-mode) mode-line-format) (when (and (eq major-mode 'exwm-mode) mode-line-format)
(let (mode-line-height)
(when exwm--floating-frame
(setq mode-line-height (window-mode-line-height
(frame-root-window exwm--floating-frame))))
(setq exwm--mode-line-format mode-line-format (setq exwm--mode-line-format mode-line-format
mode-line-format nil) mode-line-format nil)
(if (not exwm--floating-frame) (if (not exwm--floating-frame)
(exwm-layout--show exwm--id) (exwm-layout--show exwm--id)
(exwm-floating--fit-frame-to-window) (set-frame-height exwm--floating-frame
(xcb:flush exwm--connection) (- (frame-pixel-height exwm--floating-frame)
(setq window-size-fixed exwm--fixed-size)))) mode-line-height)
nil t)))))
(defun exwm-layout-show-mode-line () (defun exwm-layout-show-mode-line ()
"Show mode-line." "Show mode-line."
@ -412,10 +417,12 @@ See also `exwm-layout-enlarge-window'."
exwm--mode-line-format nil) exwm--mode-line-format nil)
(if (not exwm--floating-frame) (if (not exwm--floating-frame)
(exwm-layout--show exwm--id) (exwm-layout--show exwm--id)
(exwm-floating--fit-frame-to-window) (set-frame-height exwm--floating-frame
(exwm-input-grab-keyboard) (+ (frame-pixel-height exwm--floating-frame)
(xcb:flush exwm--connection) (window-mode-line-height (frame-root-window
(setq window-size-fixed exwm--fixed-size)))) exwm--floating-frame)))
nil t)
(exwm-input-grab-keyboard))))
;;;###autoload ;;;###autoload
(defun exwm-layout-toggle-mode-line () (defun exwm-layout-toggle-mode-line ()

View file

@ -103,7 +103,7 @@ corresponding buffer.")
exwm-window-type)))) exwm-window-type))))
(exwm--log "No need to manage #x%x" id) (exwm--log "No need to manage #x%x" id)
;; Remove all events ;; Remove all events
(xcb:+request-checked+request-check exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes (make-instance 'xcb:ChangeWindowAttributes
:window id :value-mask xcb:CW:EventMask :window id :value-mask xcb:CW:EventMask
:event-mask xcb:EventMask:NoEvent)) :event-mask xcb:EventMask:NoEvent))
@ -173,7 +173,7 @@ corresponding buffer.")
:border-width 0)) :border-width 0))
(dolist (button ;grab buttons to set focus / move / resize (dolist (button ;grab buttons to set focus / move / resize
(list xcb:ButtonIndex:1 xcb:ButtonIndex:2 xcb:ButtonIndex:3)) (list xcb:ButtonIndex:1 xcb:ButtonIndex:2 xcb:ButtonIndex:3))
(xcb:+request-checked+request-check exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:GrabButton (make-instance 'xcb:GrabButton
:owner-events 0 :grab-window id :owner-events 0 :grab-window id
:event-mask xcb:EventMask:ButtonPress :event-mask xcb:EventMask:ButtonPress
@ -206,13 +206,10 @@ corresponding buffer.")
(let ((buffer (exwm--id->buffer id))) (let ((buffer (exwm--id->buffer id)))
(exwm--log "Unmanage #x%x (buffer: %s)" id buffer) (exwm--log "Unmanage #x%x (buffer: %s)" id buffer)
(setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist)) (setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist))
(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)
(when (buffer-live-p buffer) (when (buffer-live-p buffer)
(with-current-buffer buffer (with-current-buffer buffer
;; Hide the floating frame as early as possible.
(when exwm--floating-frame (make-frame-invisible exwm--floating-frame))
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:UnmapWindow :window exwm--container)) (make-instance 'xcb:UnmapWindow :window exwm--container))
(setq exwm-workspace--switch-history-outdated t) (setq exwm-workspace--switch-history-outdated t)
@ -245,21 +242,29 @@ corresponding buffer.")
;; Delete WM_STATE property ;; Delete WM_STATE property
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:DeleteProperty (make-instance 'xcb:DeleteProperty
:window id :property xcb:Atom:WM_STATE)) :window id :property xcb:Atom:WM_STATE)))
(xcb:flush exwm--connection))
;; Destroy the container (it seems it has to be delayed). ;; Destroy the container (it seems it has to be delayed).
(run-with-idle-timer 0 nil (when exwm--floating-frame
`(lambda ()
(xcb:+request exwm--connection (xcb:+request exwm--connection
,(make-instance 'xcb:DestroyWindow (make-instance 'xcb:ReparentWindow
:window exwm--container)) :window (frame-parameter exwm--floating-frame
(xcb:flush exwm--connection))) 'exwm-outer-id)
:parent exwm--root
:x 0 :y 0)))
(xcb:+request exwm--connection
(make-instance 'xcb:DestroyWindow :window exwm--container))
(xcb:flush exwm--connection)
(let ((kill-buffer-query-functions nil) (let ((kill-buffer-query-functions nil)
(floating exwm--floating-frame)) (floating exwm--floating-frame))
(kill-buffer) (kill-buffer)
(when floating (when floating
(select-window (select-window
(frame-selected-window exwm-workspace--current)))))))) (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 () (defun exwm-manage--scan ()
"Search for existing windows and try to manage them." "Search for existing windows and try to manage them."
@ -280,64 +285,77 @@ corresponding buffer.")
(defvar exwm-manage-ping-timeout 3 "Seconds to wait before killing a client.") (defvar exwm-manage-ping-timeout 3 "Seconds to wait before killing a client.")
;;;###autoload ;;;###autoload
(defun exwm-manage--close-window (id &optional buffer) (defun exwm-manage--kill-buffer-query-function ()
"Close window ID in a proper way." "Run in `kill-buffer-query-functions'."
(let (container)
(catch 'return (catch 'return
(unless (exwm--id->buffer id) (when (xcb:+request-checked+request-check exwm--connection
(throw 'return t)) (make-instance 'xcb:MapWindow :window exwm--id))
(unless buffer (setq buffer (exwm--id->buffer id))) ;; The X window is no longer alive so just close the buffer.
(when (buffer-live-p buffer) ;; Destroy the container.
(setq container exwm--container)) (when exwm--floating-frame
;; Destroy the client window if it does not support WM_DELETE_WINDOW
(unless (and (buffer-live-p buffer)
(with-current-buffer buffer
(memq xcb:Atom:WM_DELETE_WINDOW exwm--protocols)))
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:DestroyWindow :window id)) (make-instance 'xcb:ReparentWindow
:window (frame-parameter exwm--floating-frame
'exwm-outer-id)
:parent exwm--root
:x 0 :y 0)))
(xcb:+request exwm--connection
(make-instance 'xcb:DestroyWindow :window exwm--container))
(xcb:flush exwm--connection) (xcb:flush exwm--connection)
(throw 'return t))
(unless (memq xcb:Atom:WM_DELETE_WINDOW exwm--protocols)
;; The X window does not support WM_DELETE_WINDOW; destroy it.
(xcb:+request exwm--connection
(make-instance 'xcb:DestroyWindow :window exwm--id))
(xcb:flush exwm--connection)
;; Wait for DestroyNotify event.
(throw 'return nil)) (throw 'return nil))
;; Try to close the window with WM_DELETE_WINDOW client message ;; Try to close the X window with WM_DELETE_WINDOW client message.
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:icccm:SendEvent (make-instance 'xcb:icccm:SendEvent
:destination id :destination exwm--id
:event (xcb:marshal :event (xcb:marshal
(make-instance 'xcb:icccm:WM_DELETE_WINDOW (make-instance 'xcb:icccm:WM_DELETE_WINDOW
:window id) :window exwm--id)
exwm--connection))) exwm--connection)))
(xcb:flush exwm--connection) (xcb:flush exwm--connection)
;; Try to determine if the client stop responding ;;
(with-current-buffer buffer
(unless (memq xcb:Atom:_NET_WM_PING exwm--protocols) (unless (memq xcb:Atom:_NET_WM_PING exwm--protocols)
;; Ensure it's dead ;; The window does not support _NET_WM_PING. To make sure it'll die,
;; kill it after the time runs out.
(run-with-timer exwm-manage-ping-timeout nil (run-with-timer exwm-manage-ping-timeout nil
`(lambda () (exwm-manage--kill-client ,id))) `(lambda () (exwm-manage--kill-client ,exwm--id)))
;; Wait for DestroyNotify event.
(throw 'return nil)) (throw 'return nil))
;; Try to determine if the X window is dead with _NET_WM_PING.
(setq exwm-manage--ping-lock t) (setq exwm-manage--ping-lock t)
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:SendEvent (make-instance 'xcb:SendEvent
:propagate 0 :destination id :propagate 0
:destination exwm--id
:event-mask xcb:EventMask:NoEvent :event-mask xcb:EventMask:NoEvent
:event (xcb:marshal :event (xcb:marshal
(make-instance 'xcb:ewmh:_NET_WM_PING (make-instance 'xcb:ewmh:_NET_WM_PING
:window id :timestamp 0 :window exwm--id
:client-window id) :timestamp 0
:client-window exwm--id)
exwm--connection))) exwm--connection)))
(xcb:flush exwm--connection) (xcb:flush exwm--connection)
(with-timeout (exwm-manage-ping-timeout (with-timeout (exwm-manage-ping-timeout
(if (yes-or-no-p (format "`%s' is not responding. \ (if (yes-or-no-p (format "'%s' is not responding. \
Would you like to kill it? " Would you like to kill it? "
(buffer-name buffer))) (buffer-name)))
(progn (exwm-manage--kill-client id) (progn (exwm-manage--kill-client exwm--id)
;; Kill the unresponsive X window and
;; wait for DestroyNotify event.
(throw 'return nil)) (throw 'return nil))
;; Give up.
(throw 'return nil))) (throw 'return nil)))
(while (and exwm-manage--ping-lock (while (and exwm-manage--ping-lock
(exwm--id->buffer id)) ;may have been destroyed (exwm--id->buffer exwm--id)) ;may have been destroyed.
(accept-process-output nil 0.1))))) (accept-process-output nil 0.1))
;; Finally destroy the container ;; Give up.
(xcb:+request exwm--connection (throw 'return nil))))
(make-instance 'xcb:DestroyWindow :window container))
(xcb:flush exwm--connection)))
(defun exwm-manage--kill-client (&optional id) (defun exwm-manage--kill-client (&optional id)
"Kill an X client." "Kill an X client."