diff --git a/exwm-floating.el b/exwm-floating.el index 770976d..b3a5b18 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -622,6 +622,9 @@ Both DELTA-X and DELTA-Y default to 1. This command should be bound locally." exwm-floating--cursor-left (xcb:cursor:load-cursor exwm--connection "left_side"))) +(defun exwm-floating--exit () + "Exit the floating module.") + (provide 'exwm-floating) diff --git a/exwm-input.el b/exwm-input.el index dc7f2b5..4bf2cd1 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -476,6 +476,14 @@ SIMULATION-KEYS is an alist of the form (original-key . simulated-key)." (dolist (j pair) (exwm-input--fake-key j)))))) +(defun exwm-input--on-pre-command () + "Run in `pre-command-hook'." + (setq exwm-input--during-command t)) + +(defun exwm-input--on-post-command () + "Run in `post-command-hook'." + (setq exwm-input--during-command nil)) + (declare-function exwm-floating--stop-moveresize "exwm-floating.el" (&rest _args)) (declare-function exwm-floating--do-moveresize "exwm-floating.el" @@ -510,14 +518,20 @@ SIMULATION-KEYS is an alist of the form (original-key . simulated-key)." ;; `pre-command-hook' marks the end of a key sequence (existing or not) (add-hook 'pre-command-hook #'exwm-input--finish-key-sequence) ;; Control `exwm-input--during-command' - (add-hook 'pre-command-hook (lambda () (setq exwm-input--during-command t))) - (add-hook 'post-command-hook - (lambda () (setq exwm-input--during-command nil))) + (add-hook 'pre-command-hook #'exwm-input--on-pre-command) + (add-hook 'post-command-hook #'exwm-input--on-post-command) ;; Update focus when buffer list updates (add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) ;; Update prefix keys for global keys (exwm-input--update-global-prefix-keys)) +(defun exwm-input--exit () + "Exit the input module." + (remove-hook 'pre-command-hook #'exwm-input--finish-key-sequence) + (remove-hook 'pre-command-hook #'exwm-input--on-pre-command) + (remove-hook 'post-command-hook #'exwm-input--on-post-command) + (remove-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update)) + (provide 'exwm-input) diff --git a/exwm-layout.el b/exwm-layout.el index 9cbdd2b..0069767 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -528,6 +528,8 @@ See also `exwm-layout-enlarge-window'." (exwm-layout-hide-mode-line) (exwm-layout-show-mode-line)))) +(defvar exwm-layout--timer nil "Timer used to track echo area changes.") + (defun exwm-layout--init () "Initialize layout module." ;; Auto refresh layout @@ -535,9 +537,19 @@ See also `exwm-layout-enlarge-window'." (unless (exwm-workspace--minibuffer-own-frame-p) ;; Refresh when minibuffer grows (add-hook 'minibuffer-setup-hook #'exwm-layout--on-minibuffer-setup t) - (run-with-idle-timer 0 t #'exwm-layout--on-echo-area-change t) + (setq exwm-layout--timer + (run-with-idle-timer 0 t #'exwm-layout--on-echo-area-change t)) (add-hook 'echo-area-clear-hook #'exwm-layout--on-echo-area-change))) +(defun exwm-layout--exit () + "Exit the layout module." + (remove-hook 'window-configuration-change-hook #'exwm-layout--refresh) + (remove-hook 'minibuffer-setup-hook #'exwm-layout--on-minibuffer-setup) + (when exwm-layout--timer + (cancel-timer exwm-layout--timer) + (setq exwm-layout--timer nil)) + (remove-hook 'echo-area-clear-hook #'exwm-layout--on-echo-area-change)) + (provide 'exwm-layout) diff --git a/exwm-manage.el b/exwm-manage.el index 1877033..1c63134 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -555,6 +555,10 @@ border-width: %d; sibling: #x%x; stack-mode: %d" (xcb:+event exwm--connection 'xcb:DestroyNotify #'exwm-manage--on-DestroyNotify)) +(defun exwm-manage--exit () + "Exit the manage module." + (setq exwm-manage--_MOTIF_WM_HINTS nil)) + (provide 'exwm-manage) diff --git a/exwm-randr.el b/exwm-randr.el index e761d8f..0e4469d 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -164,9 +164,13 @@ )) (xcb:flush exwm--connection))))) +(defun exwm-randr--exit () + "Exit the RandR module.") + (defun exwm-randr-enable () "Enable RandR support for EXWM." - (add-hook 'exwm-init-hook #'exwm-randr--init)) + (add-hook 'exwm-init-hook #'exwm-randr--init) + (add-hook 'exwm-exit-hook #'exwm-randr--exit)) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index cb08ba9..d1783de 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -311,8 +311,8 @@ You shall use the default value if using auto-hide minibuffer.") 'process) nil) ;; Initialize XELB modules. - (xcb:xembed:init exwm-systemtray--connection) - (xcb:systemtray:init exwm-systemtray--connection) + (xcb:xembed:init exwm-systemtray--connection t) + (xcb:systemtray:init exwm-systemtray--connection t) ;; Acquire the manager selection _NET_SYSTEM_TRAY_S0. (with-slots (owner) (xcb:+request-unchecked+reply exwm-systemtray--connection @@ -399,11 +399,27 @@ You shall use the default value if using auto-hide minibuffer.") #'exwm-systemtray--on-ClientMessage) ;; Add hook to move/reparent the embedder. (add-hook 'exwm-workspace-switch-hook #'exwm-systemtray--on-workspace-switch) - (add-hook 'exwm-randr-refresh-hook #'exwm-systemtray--on-randr-refresh)) + (when (boundp 'exwm-randr-refresh-hook) + (add-hook 'exwm-randr-refresh-hook #'exwm-systemtray--on-randr-refresh))) + +(defun exwm-systemtray--exit () + "Exit the systemtray module." + (when exwm-systemtray--connection + (xcb:disconnect exwm-systemtray--connection) + (setq exwm-systemtray--connection nil + exwm-systemtray--list nil + exwm-systemtray--selection-owner-window nil + exwm-systemtray--embedder nil) + (remove-hook 'exwm-workspace-switch-hook + #'exwm-systemtray--on-workspace-switch) + (when (boundp 'exwm-randr-refresh-hook) + (remove-hook 'exwm-randr-refresh-hook + #'exwm-systemtray--on-randr-refresh)))) (defun exwm-systemtray-enable () "Enable system tray support for EXWM." - (add-hook 'exwm-init-hook #'exwm-systemtray--init)) + (add-hook 'exwm-init-hook #'exwm-systemtray--init) + (add-hook 'exwm-exit-hook #'exwm-systemtray--exit)) diff --git a/exwm-workspace.el b/exwm-workspace.el index 124c681..b61b81c 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -584,7 +584,11 @@ The optional FORCE option is for internal use only." (cancel-timer exwm-workspace--display-echo-area-timer) (setq exwm-workspace--display-echo-area-timer nil)))) +(defvar exwm-workspace--client nil + "The 'client' frame parameter of emacsclient frames.") + (declare-function exwm-manage--unmanage-window "exwm-manage.el") +(declare-function exwm--exit "exwm.el") (defun exwm-workspace--confirm-kill-emacs (prompt) "Confirm before exiting Emacs." @@ -615,9 +619,26 @@ The optional FORCE option is for internal use only." :x 0 :y 0))) (xcb:flush exwm--connection) - ;; Destroy all resources created by this connection. - (xcb:disconnect exwm--connection) - t)) + (if (not exwm-workspace--client) + (progn + ;; Destroy all resources created by this connection. + (xcb:disconnect exwm--connection) + t) + ;; Extra cleanups for emacsclient. + (dolist (f exwm-workspace--list) + (set-frame-parameter f 'client exwm-workspace--client)) + (when (exwm-workspace--minibuffer-own-frame-p) + (set-frame-parameter exwm-workspace--minibuffer 'client + exwm-workspace--client)) + (let ((connection exwm--connection)) + (exwm--exit) + ;; Destroy all resources created by this connection. + (xcb:disconnect connection)) + ;; Kill the client. + (server-save-buffers-kill-terminal nil) + nil))) + +(defvar exwm-workspace--timer nil "Timer used to track echo area changes.") (defun exwm-workspace--init () "Initialize workspace module." @@ -629,11 +650,13 @@ The optional FORCE option is for internal use only." (progn (setq exwm-workspace--list (frame-list)) (when (< 1 (length exwm-workspace--list)) - ;; Emacs client creates an extra (but unusable) frame. + ;; Exclude the initial frame. (dolist (i exwm-workspace--list) (unless (frame-parameter i 'window-id) (setq exwm-workspace--list (delq i exwm-workspace--list)))) (cl-assert (= 1 (length exwm-workspace--list))) + (setq exwm-workspace--client + (frame-parameter (car exwm-workspace--list) 'client)) ;; Prevent user from deleting this frame by accident. (set-frame-parameter (car exwm-workspace--list) 'client nil)) ;; Create remaining frames. @@ -645,12 +668,17 @@ The optional FORCE option is for internal use only." (setq exwm-workspace--minibuffer (make-frame '((window-system . x) (minibuffer . only) (left . 10000) (right . 10000) - (width . 0) (height . 0)))) + (width . 0) (height . 0) + (client . nil)))) ;; Remove/hide existing frames. (dolist (f old-frames) (if (frame-parameter f 'client) - (make-frame-invisible f) - (delete-frame f)))) + (progn + (unless exwm-workspace--client + (setq exwm-workspace--client (frame-parameter f 'client))) + (make-frame-invisible f)) + (when (eq 'x (framep f)) ;do not delete the initial frame. + (delete-frame f))))) ;; This is the only usable minibuffer frame. (setq default-minibuffer-frame exwm-workspace--minibuffer) (let ((outer-id (string-to-number @@ -687,17 +715,20 @@ The optional FORCE option is for internal use only." ;; Show/hide minibuffer / echo area when they're active/inactive. (add-hook 'minibuffer-setup-hook #'exwm-workspace--on-minibuffer-setup) (add-hook 'minibuffer-exit-hook #'exwm-workspace--on-minibuffer-exit) - (run-with-idle-timer 0 t #'exwm-workspace--on-echo-area-dirty) + (setq exwm-workspace--timer + (run-with-idle-timer 0 t #'exwm-workspace--on-echo-area-dirty)) (add-hook 'echo-area-clear-hook #'exwm-workspace--on-echo-area-clear) ;; Create workspace frames. (dotimes (_ exwm-workspace-number) (push (make-frame `((window-system . x) (minibuffer . ,(minibuffer-window - exwm-workspace--minibuffer)))) + exwm-workspace--minibuffer)) + (client . nil))) exwm-workspace--list)) ;; The default behavior of `display-buffer' (indirectly called by ;; `minibuffer-completion-help') is not correct here. - (cl-pushnew '(exwm-workspace--display-buffer) display-buffer-alist)) + (cl-pushnew '(exwm-workspace--display-buffer) display-buffer-alist + :test #'equal)) ;; Handle unexpected frame switch. (add-hook 'focus-in-hook #'exwm-workspace--on-focus-in) ;; Prevent `other-buffer' from selecting already displayed EXWM buffers. @@ -768,6 +799,25 @@ The optional FORCE option is for internal use only." ;; Switch to the first workspace (exwm-workspace-switch 0 t)) +(defun exwm-workspace--exit () + "Exit the workspace module." + (setq confirm-kill-emacs nil + exwm-workspace--list nil + exwm-workspace--client nil + exwm-workspace--minibuffer nil + default-minibuffer-frame nil) + (remove-hook 'minibuffer-setup-hook #'exwm-workspace--on-minibuffer-setup) + (remove-hook 'minibuffer-exit-hook #'exwm-workspace--on-minibuffer-exit) + (when exwm-workspace--timer + (cancel-timer exwm-workspace--timer) + (setq exwm-workspace--timer nil)) + (remove-hook 'echo-area-clear-hook #'exwm-workspace--on-echo-area-clear) + (setq display-buffer-alist + (cl-delete '(exwm-workspace--display-buffer) display-buffer-alist + :test #'equal)) + (remove-hook 'focus-in-hook #'exwm-workspace--on-focus-in) + (advice-remove 'x-create-frame #'exwm-workspace--x-create-frame)) + (defvar exwm-layout--fullscreen-frame-count) (defun exwm-workspace--post-init () diff --git a/exwm.el b/exwm.el index c9dc6cd..0bc27d7 100644 --- a/exwm.el +++ b/exwm.el @@ -477,7 +477,11 @@ (defun exwm-init (&optional frame) "Initialize EXWM." - (if (not (eq 'x (framep (or frame (selected-frame))))) + (if frame + ;; The frame might not be selected if it's created by emacslicnet. + (select-frame-set-input-focus frame) + (setq frame (selected-frame))) + (if (not (eq 'x (framep frame))) (exwm--log "Not running under X environment") (unless exwm--connection (exwm-enable 'undo) ;never initialize again @@ -499,8 +503,8 @@ ;; Disable some features not working well with EXWM (setq use-dialog-box nil) ;; Initialize ICCCM/EWMH support - ;; (xcb:icccm:init exwm--connection) - (xcb:ewmh:init exwm--connection) + (xcb:icccm:init exwm--connection t) + (xcb:ewmh:init exwm--connection t) (exwm--lock) (exwm--init-icccm-ewmh) (exwm-layout--init) @@ -514,6 +518,26 @@ (exwm-manage--scan) (run-hooks 'exwm-init-hook))))) +(defvar exwm-exit-hook nil + "Normal hook run just before EXWM is about to exit. + +This hook is only run when EXWM is started with emacsclient.") + +(defun exwm--exit () + "Exit EXWM." + (run-hooks 'exwm-exit-hook) + ;; Exit modules. + (exwm-input--exit) + (exwm-workspace--exit) + (exwm-manage--exit) + (exwm-floating--exit) + (exwm-layout--exit) + ;; Reset several import variables. + (setq exwm--connection nil + exwm--root nil + exwm--id-buffer-alist nil) + (exwm-enable)) + (defvar exwm-blocking-subrs '(x-file-dialog x-popup-dialog x-select-font) "Subrs (primitives) that would normally block EXWM.")