mirror of
https://github.com/emacs-exwm/exwm.git
synced 2024-11-23 13:07:59 +01:00
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:
parent
12e2d574c9
commit
3f7722079c
4 changed files with 137 additions and 114 deletions
|
@ -142,9 +142,7 @@
|
|||
(add-hook 'change-major-mode-hook #'kill-buffer nil t)
|
||||
;; Kill buffer -> close window
|
||||
(add-hook 'kill-buffer-query-functions
|
||||
(lambda ()
|
||||
(exwm-manage--close-window exwm--id (current-buffer)))
|
||||
nil t)
|
||||
#'exwm-manage--kill-buffer-query-function nil t)
|
||||
(setq buffer-read-only t
|
||||
left-margin-width nil
|
||||
right-margin-width nil
|
||||
|
|
|
@ -50,6 +50,8 @@
|
|||
(defvar exwm-floating--cursor-bottom-left nil)
|
||||
(defvar exwm-floating--cursor-left nil)
|
||||
|
||||
(declare-function exwm-layout--refresh "exwm-layout.el")
|
||||
|
||||
;;;###autoload
|
||||
(defun exwm-floating--set-floating (id)
|
||||
"Make window ID floating."
|
||||
|
@ -78,6 +80,8 @@
|
|||
(internal-border-width . ,exwm-floating-border-width)
|
||||
(left . 10000)
|
||||
(top . 10000)
|
||||
(width . ,window-min-width)
|
||||
(height . ,window-min-height)
|
||||
(unsplittable . t))))) ;and fix the size later
|
||||
(outer-id (string-to-number (frame-parameter frame 'outer-window-id)))
|
||||
(container (with-current-buffer (exwm--id->buffer id)
|
||||
|
@ -151,7 +155,17 @@
|
|||
y (/ (- display-height height) 2))))))
|
||||
(exwm--log "Floating geometry (corrected): %dx%d%+d%+d" width height x y)
|
||||
;; 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
|
||||
(xcb:+request exwm--connection
|
||||
(make-instance 'xcb:ReparentWindow
|
||||
|
@ -171,8 +185,12 @@
|
|||
(setq window-size-fixed exwm--fixed-size
|
||||
exwm--frame original-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-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))
|
||||
(run-hooks 'exwm-floating-setup-hook))
|
||||
|
||||
|
@ -182,6 +200,16 @@
|
|||
(interactive)
|
||||
(let ((buffer (exwm--id->buffer id)))
|
||||
(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
|
||||
(xcb:+request exwm--connection
|
||||
(make-instance 'xcb:ReparentWindow
|
||||
|
@ -221,34 +249,6 @@
|
|||
(exwm-floating--unset-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
|
||||
'exwm-layout-hide-mode-line "25.1" "Hide mode-line of a floating frame.")
|
||||
(define-obsolete-function-alias 'exwm-floating-show-mode-line
|
||||
|
|
|
@ -396,13 +396,18 @@ See also `exwm-layout-enlarge-window'."
|
|||
"Hide mode-line."
|
||||
(interactive)
|
||||
(when (and (eq major-mode 'exwm-mode) mode-line-format)
|
||||
(setq exwm--mode-line-format mode-line-format
|
||||
mode-line-format nil)
|
||||
(if (not exwm--floating-frame)
|
||||
(exwm-layout--show exwm--id)
|
||||
(exwm-floating--fit-frame-to-window)
|
||||
(xcb:flush exwm--connection)
|
||||
(setq window-size-fixed exwm--fixed-size))))
|
||||
(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
|
||||
mode-line-format nil)
|
||||
(if (not exwm--floating-frame)
|
||||
(exwm-layout--show exwm--id)
|
||||
(set-frame-height exwm--floating-frame
|
||||
(- (frame-pixel-height exwm--floating-frame)
|
||||
mode-line-height)
|
||||
nil t)))))
|
||||
|
||||
(defun exwm-layout-show-mode-line ()
|
||||
"Show mode-line."
|
||||
|
@ -412,10 +417,12 @@ See also `exwm-layout-enlarge-window'."
|
|||
exwm--mode-line-format nil)
|
||||
(if (not exwm--floating-frame)
|
||||
(exwm-layout--show exwm--id)
|
||||
(exwm-floating--fit-frame-to-window)
|
||||
(exwm-input-grab-keyboard)
|
||||
(xcb:flush exwm--connection)
|
||||
(setq window-size-fixed exwm--fixed-size))))
|
||||
(set-frame-height exwm--floating-frame
|
||||
(+ (frame-pixel-height exwm--floating-frame)
|
||||
(window-mode-line-height (frame-root-window
|
||||
exwm--floating-frame)))
|
||||
nil t)
|
||||
(exwm-input-grab-keyboard))))
|
||||
|
||||
;;;###autoload
|
||||
(defun exwm-layout-toggle-mode-line ()
|
||||
|
|
158
exwm-manage.el
158
exwm-manage.el
|
@ -103,7 +103,7 @@ corresponding buffer.")
|
|||
exwm-window-type))))
|
||||
(exwm--log "No need to manage #x%x" id)
|
||||
;; Remove all events
|
||||
(xcb:+request-checked+request-check exwm--connection
|
||||
(xcb:+request exwm--connection
|
||||
(make-instance 'xcb:ChangeWindowAttributes
|
||||
:window id :value-mask xcb:CW:EventMask
|
||||
:event-mask xcb:EventMask:NoEvent))
|
||||
|
@ -173,7 +173,7 @@ corresponding buffer.")
|
|||
:border-width 0))
|
||||
(dolist (button ;grab buttons to set focus / move / resize
|
||||
(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
|
||||
:owner-events 0 :grab-window id
|
||||
:event-mask xcb:EventMask:ButtonPress
|
||||
|
@ -206,13 +206,10 @@ corresponding buffer.")
|
|||
(let ((buffer (exwm--id->buffer id)))
|
||||
(exwm--log "Unmanage #x%x (buffer: %s)" id buffer)
|
||||
(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)
|
||||
(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
|
||||
(make-instance 'xcb:UnmapWindow :window exwm--container))
|
||||
(setq exwm-workspace--switch-history-outdated t)
|
||||
|
@ -245,21 +242,29 @@ corresponding buffer.")
|
|||
;; Delete WM_STATE property
|
||||
(xcb:+request exwm--connection
|
||||
(make-instance 'xcb:DeleteProperty
|
||||
:window id :property xcb:Atom:WM_STATE))
|
||||
(xcb:flush exwm--connection))
|
||||
:window id :property xcb:Atom:WM_STATE)))
|
||||
;; Destroy the container (it seems it has to be delayed).
|
||||
(run-with-idle-timer 0 nil
|
||||
`(lambda ()
|
||||
(xcb:+request exwm--connection
|
||||
,(make-instance 'xcb:DestroyWindow
|
||||
:window exwm--container))
|
||||
(xcb:flush exwm--connection)))
|
||||
(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)))
|
||||
(xcb:+request exwm--connection
|
||||
(make-instance 'xcb:DestroyWindow :window exwm--container))
|
||||
(xcb:flush exwm--connection)
|
||||
(let ((kill-buffer-query-functions nil)
|
||||
(floating exwm--floating-frame))
|
||||
(kill-buffer)
|
||||
(when floating
|
||||
(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 ()
|
||||
"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.")
|
||||
|
||||
;;;###autoload
|
||||
(defun exwm-manage--close-window (id &optional buffer)
|
||||
"Close window ID in a proper way."
|
||||
(let (container)
|
||||
(catch 'return
|
||||
(unless (exwm--id->buffer id)
|
||||
(throw 'return t))
|
||||
(unless buffer (setq buffer (exwm--id->buffer id)))
|
||||
(when (buffer-live-p buffer)
|
||||
(setq container exwm--container))
|
||||
;; 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)))
|
||||
(defun exwm-manage--kill-buffer-query-function ()
|
||||
"Run in `kill-buffer-query-functions'."
|
||||
(catch 'return
|
||||
(when (xcb:+request-checked+request-check exwm--connection
|
||||
(make-instance 'xcb:MapWindow :window exwm--id))
|
||||
;; The X window is no longer alive so just close the buffer.
|
||||
;; Destroy the container.
|
||||
(when exwm--floating-frame
|
||||
(xcb:+request exwm--connection
|
||||
(make-instance 'xcb:DestroyWindow :window id))
|
||||
(xcb:flush exwm--connection)
|
||||
(throw 'return nil))
|
||||
;; Try to close the window with WM_DELETE_WINDOW client message
|
||||
(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:icccm:SendEvent
|
||||
:destination id
|
||||
:event (xcb:marshal
|
||||
(make-instance 'xcb:icccm:WM_DELETE_WINDOW
|
||||
:window id)
|
||||
exwm--connection)))
|
||||
(make-instance 'xcb:DestroyWindow :window exwm--container))
|
||||
(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)
|
||||
;; Ensure it's dead
|
||||
(run-with-timer exwm-manage-ping-timeout nil
|
||||
`(lambda () (exwm-manage--kill-client ,id)))
|
||||
(throw 'return nil))
|
||||
(setq exwm-manage--ping-lock t)
|
||||
(xcb:+request exwm--connection
|
||||
(make-instance 'xcb:SendEvent
|
||||
:propagate 0 :destination id
|
||||
:event-mask xcb:EventMask:NoEvent
|
||||
:event (xcb:marshal
|
||||
(make-instance 'xcb:ewmh:_NET_WM_PING
|
||||
:window id :timestamp 0
|
||||
:client-window id)
|
||||
exwm--connection)))
|
||||
(xcb:flush exwm--connection)
|
||||
(with-timeout (exwm-manage-ping-timeout
|
||||
(if (yes-or-no-p (format "`%s' is not responding. \
|
||||
Would you like to kill it? "
|
||||
(buffer-name buffer)))
|
||||
(progn (exwm-manage--kill-client id)
|
||||
(throw 'return nil))
|
||||
(throw 'return nil)))
|
||||
(while (and exwm-manage--ping-lock
|
||||
(exwm--id->buffer id)) ;may have been destroyed
|
||||
(accept-process-output nil 0.1)))))
|
||||
;; Finally destroy the container
|
||||
(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))
|
||||
;; Try to close the X window with WM_DELETE_WINDOW client message.
|
||||
(xcb:+request exwm--connection
|
||||
(make-instance 'xcb:DestroyWindow :window container))
|
||||
(xcb:flush exwm--connection)))
|
||||
(make-instance 'xcb:icccm:SendEvent
|
||||
:destination exwm--id
|
||||
:event (xcb:marshal
|
||||
(make-instance 'xcb:icccm:WM_DELETE_WINDOW
|
||||
:window exwm--id)
|
||||
exwm--connection)))
|
||||
(xcb:flush exwm--connection)
|
||||
;;
|
||||
(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.
|
||||
(run-with-timer exwm-manage-ping-timeout nil
|
||||
`(lambda () (exwm-manage--kill-client ,exwm--id)))
|
||||
;; Wait for DestroyNotify event.
|
||||
(throw 'return nil))
|
||||
;; Try to determine if the X window is dead with _NET_WM_PING.
|
||||
(setq exwm-manage--ping-lock t)
|
||||
(xcb:+request exwm--connection
|
||||
(make-instance 'xcb:SendEvent
|
||||
:propagate 0
|
||||
:destination exwm--id
|
||||
:event-mask xcb:EventMask:NoEvent
|
||||
:event (xcb:marshal
|
||||
(make-instance 'xcb:ewmh:_NET_WM_PING
|
||||
:window exwm--id
|
||||
:timestamp 0
|
||||
:client-window exwm--id)
|
||||
exwm--connection)))
|
||||
(xcb:flush exwm--connection)
|
||||
(with-timeout (exwm-manage-ping-timeout
|
||||
(if (yes-or-no-p (format "'%s' is not responding. \
|
||||
Would you like to kill it? "
|
||||
(buffer-name)))
|
||||
(progn (exwm-manage--kill-client exwm--id)
|
||||
;; Kill the unresponsive X window and
|
||||
;; wait for DestroyNotify event.
|
||||
(throw 'return nil))
|
||||
;; Give up.
|
||||
(throw 'return nil)))
|
||||
(while (and exwm-manage--ping-lock
|
||||
(exwm--id->buffer exwm--id)) ;may have been destroyed.
|
||||
(accept-process-output nil 0.1))
|
||||
;; Give up.
|
||||
(throw 'return nil))))
|
||||
|
||||
(defun exwm-manage--kill-client (&optional id)
|
||||
"Kill an X client."
|
||||
|
|
Loading…
Reference in a new issue