about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAdrián Medraño Calvo <adrian@medranocalvo.com>2021-12-09T00·00+0000
committerAdrián Medraño Calvo <adrian@medranocalvo.com>2021-12-09T00·00+0000
commit1aa4ca781dff7853bf6ed01e5e397793fb3ee661 (patch)
treecad70bafebe98bbe0f184360f8a7f0e00d8312d1
parenta11bb099fbd0b755d9a6d80a63a0dcd275280e33 (diff)
Support for killing the X terminal
Check whether frames are alive upon teardown, as it might not be
the case when the terminal is killed as
`delete-terminal-functions' might be invoked after the terminal is
deleted.

* exwm-core.el (exwm--terminal): New variable holding the terminal
EXWM runs under.
(exwm-init, exwm-exit): Set and unset it.

* exwm.el (exwm--on-delete-terminal): New function for exiting
EXWM when the terminal is deleted.
(exwm-init): Use it.

* exwm.el (exwm--confirm-kill-terminal, exwm-init): Ask for
confirmation before deleting terminal.

* exwm-workspace.el (exwm-workspace--fullscreen-workspace): New
function.  Ensure the frame is alive.
(exwm-workspace--add-frame-as-workspace): Use it.
(exwm-workspace--exit-minibuffer-frame): Cancel
`exwm-workspace--display-echo-area-timer'.
(exwm-workspace--exit-minibuffer-frame): Ensure the minibuffer
frame is alive.
(exwm-workspace--exit): Ignore dead workspace frames.
-rw-r--r--exwm-core.el3
-rw-r--r--exwm-workspace.el61
-rw-r--r--exwm.el24
3 files changed, 63 insertions, 25 deletions
diff --git a/exwm-core.el b/exwm-core.el
index 5356ef9b97e1..5bcf943858b6 100644
--- a/exwm-core.el
+++ b/exwm-core.el
@@ -59,6 +59,9 @@ Here are some predefined candidates:
 
 (defvar exwm--connection nil "X connection.")
 
+(defvar exwm--terminal nil
+  "Terminal corresponding to `exwm--connection'.")
+
 (defvar exwm--wmsn-window nil
   "An X window owning the WM_S0 selection.")
 
diff --git a/exwm-workspace.el b/exwm-workspace.el
index 3bdbd472f4d2..ff9920d16125 100644
--- a/exwm-workspace.el
+++ b/exwm-workspace.el
@@ -1372,7 +1372,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
         (make-instance 'xcb:MapWindow :window container)))
   (xcb:flush exwm--connection)
   ;; Delay making the workspace fullscreen until Emacs becomes idle
-  (exwm--defer 0 #'set-frame-parameter frame 'fullscreen 'fullboth)
+  (exwm--defer 0 #'exwm-workspace--fullscreen-workspace frame)
   ;; Update EWMH properties.
   (exwm-workspace--update-ewmh-props)
   (if exwm-workspace--create-silently
@@ -1463,6 +1463,12 @@ Return nil if FRAME is the only workspace."
     (exwm-workspace--remove-frame-as-workspace frame)
     (remhash frame exwm-workspace--client-p-hash-table))))
 
+(defun exwm-workspace--fullscreen-workspace (frame)
+  "Make workspace FRAME fullscreen.
+Called from a timer."
+  (when (frame-live-p frame)
+    (set-frame-parameter frame 'fullscreen 'fullboth)))
+
 (defun exwm-workspace--on-after-make-frame (frame)
   "Hook run upon `make-frame' that configures FRAME as a workspace."
   (cond
@@ -1606,6 +1612,8 @@ applied to all subsequently created X frames."
   (remove-hook 'minibuffer-setup-hook #'exwm-workspace--on-minibuffer-setup)
   (remove-hook 'minibuffer-exit-hook #'exwm-workspace--on-minibuffer-exit)
   (remove-hook 'echo-area-clear-hook #'exwm-workspace--on-echo-area-clear)
+  (when exwm-workspace--display-echo-area-timer
+    (cancel-timer exwm-workspace--display-echo-area-timer))
   (when exwm-workspace--timer
     (cancel-timer exwm-workspace--timer)
     (setq exwm-workspace--timer nil))
@@ -1613,15 +1621,16 @@ applied to all subsequently created X frames."
         (cl-delete '(exwm-workspace--display-buffer) display-buffer-alist
                    :test #'equal))
   (setq default-minibuffer-frame nil)
-  (let ((id (frame-parameter exwm-workspace--minibuffer 'exwm-outer-id)))
-    (when (and exwm-workspace--minibuffer id)
-      (xcb:+request exwm--connection
-          (make-instance 'xcb:ReparentWindow
-                         :window id
-                         :parent exwm--root
-                         :x 0
-                         :y 0)))
-    (setq exwm-workspace--minibuffer nil)))
+  (when (frame-live-p exwm-workspace--minibuffer) ; might be already dead
+    (let ((id (frame-parameter exwm-workspace--minibuffer 'exwm-outer-id)))
+      (when (and exwm-workspace--minibuffer id)
+        (xcb:+request exwm--connection
+            (make-instance 'xcb:ReparentWindow
+                           :window id
+                           :parent exwm--root
+                           :x 0
+                           :y 0)))
+      (setq exwm-workspace--minibuffer nil))))
 
 (defun exwm-workspace--init ()
   "Initialize workspace module."
@@ -1712,24 +1721,28 @@ applied to all subsequently created X frames."
   ;; X windows will be re-mapped).
   (setq exwm-workspace--current nil)
   (dolist (i exwm-workspace--list)
-    (exwm-workspace--remove-frame-as-workspace i)
-    (modify-frame-parameters i '((exwm-selected-window . nil)
-                                 (exwm-urgency . nil)
-                                 (exwm-outer-id . nil)
-                                 (exwm-id . nil)
-                                 (exwm-container . nil)
-                                 ;; (internal-border-width . nil) ; integerp
-                                 ;; (client . nil)
-                                 (fullscreen . nil)
-                                 (buffer-predicate . nil))))
+    (when (frame-live-p i)                    ; might be already dead
+      (exwm-workspace--remove-frame-as-workspace i)
+      (modify-frame-parameters i '((exwm-selected-window . nil)
+                                   (exwm-urgency . nil)
+                                   (exwm-outer-id . nil)
+                                   (exwm-id . nil)
+                                   (exwm-container . nil)
+                                   ;; (internal-border-width . nil) ; integerp
+                                   (fullscreen . nil)
+                                   (buffer-predicate . nil)))
+      ;; Restore the 'client' frame parameter (before `exwm-exit').
+      (when exwm-workspace--client
+        (set-frame-parameter f 'client exwm-workspace--client))))
   ;; Restore the 'client' frame parameter (before `exwm-exit').
   (when exwm-workspace--client
-    (dolist (f exwm-workspace--list)
-      (set-frame-parameter f 'client exwm-workspace--client))
-    (when (exwm-workspace--minibuffer-own-frame-p)
+    (when (and exwm-workspace--minibuffer-own-frame-p
+               (frame-live-p exwm-workspace--minibuffer))
       (set-frame-parameter exwm-workspace--minibuffer 'client
                            exwm-workspace--client))
-    (setq exwm-workspace--client nil)))
+    (setq exwm-workspace--client nil))
+  ;; Don't let dead frames linger.
+  (setq exwm-workspace--list nil))
 
 (defun exwm-workspace--post-init ()
   "The second stage in the initialization of the workspace module."
diff --git a/exwm.el b/exwm.el
index 4c1689a55615..9e2df1ad6411 100644
--- a/exwm.el
+++ b/exwm.el
@@ -604,6 +604,13 @@
                (eq selection xcb:Atom:WM_S0))
       (exwm-exit))))
 
+(defun exwm--on-delete-terminal (terminal)
+  "Handle terminal being deleted without Emacs being killed.
+This may happen when invoking `save-buffers-kill-terminal' within an emacsclient
+session."
+  (when (eq terminal exwm--terminal)
+    (exwm-exit)))
+
 (defun exwm--init-icccm-ewmh ()
   "Initialize ICCCM/EWMH support."
   (exwm--log)
@@ -840,6 +847,7 @@ manager.  If t, replace it, if nil, abort and ask the user if `ask'."
   (condition-case err
       (progn
         (exwm-enable 'undo)               ;never initialize again
+        (setq exwm--terminal (frame-terminal frame))
         (setq exwm--connection (xcb:connect))
         (set-process-query-on-exit-flag (slot-value exwm--connection 'process)
                                         nil) ;prevent query message on exit
@@ -862,6 +870,10 @@ manager.  If t, replace it, if nil, abort and ask the user if `ask'."
         ;; Disable some features not working well with EXWM
         (setq use-dialog-box nil
               confirm-kill-emacs #'exwm--confirm-kill-emacs)
+        (advice-add 'save-buffers-kill-terminal
+                    :before-while #'exwm--confirm-kill-terminal)
+        ;; Clean up if the terminal is deleted.
+        (add-hook 'delete-terminal-functions 'exwm--on-delete-terminal)
         (exwm--lock)
         (exwm--init-icccm-ewmh)
         (exwm-layout--init)
@@ -898,7 +910,9 @@ manager.  If t, replace it, if nil, abort and ask the user if `ask'."
   (when exwm--connection
     (xcb:flush exwm--connection)
     (xcb:disconnect exwm--connection))
-  (setq exwm--connection nil))
+  (setq exwm--connection nil)
+  (setq exwm--terminal nil)
+  (exwm--log "Exited"))
 
 ;;;###autoload
 (defun exwm-enable (&optional undo)
@@ -977,6 +991,14 @@ manager.  If t, replace it, if nil, abort and ask the user if `ask'."
                ;; For other types, return the value as-is.
                (t result))))))
 
+(defun exwm--confirm-kill-terminal (&optional _)
+  "Confirm before killing terminal."
+  ;; This is invoked instead of `save-buffers-kill-emacs' (C-x C-c) on client
+  ;; frames.
+  (if (eq (frame-terminal) exwm--terminal)
+      (exwm--confirm-kill-emacs "[EXWM] Kill terminal?")
+    t))
+
 (defun exwm--confirm-kill-emacs (prompt &optional force)
   "Confirm before exiting Emacs."
   (exwm--log)