about summary refs log tree commit diff
path: root/exwm.el
diff options
context:
space:
mode:
authorChris Feng <chris.w.feng@gmail.com>2015-09-27T11·31+0800
committerChris Feng <chris.w.feng@gmail.com>2015-09-27T11·43+0800
commit5184f0d7c1b540a6241904528d068dce288a911e (patch)
tree1f7cecd5399eb739293104f8e5dc9c6cab50b5d6 /exwm.el
parentf685de12d464b334ba7efdfe67e989dd63a96fa0 (diff)
Work around subrs that block EXWM; other minor fixes
Some subrs (e.g. x-file-dialog) create X windows and block the execution of
EXWM, so they won't work normally.  This commit partly fixes this issue by
invoking them in a subordinate Emacs instance and trying to fetch the
result back.

* exwm.el (exwm-blocking-subrs): New variable for specify such subrs.
* exwm.el (exwm-enable, exwm--server-name, exwm--server-stop)
  (exwm--server-eval-at): The implementation.

* exwm-core.el:
* exwm-floating.el:
* exwm-layout.el:
* exwm-manage.el:
* exwm-randr.el:
  Evaluate constants at compile-time.

* README.md: Renamed from README.org to make the 'Commentary:' section
  used by GNU ELPA instead.

* exwm.el: Depends on XELB version 0.3.
Diffstat (limited to 'exwm.el')
-rw-r--r--exwm.el79
1 files changed, 72 insertions, 7 deletions
diff --git a/exwm.el b/exwm.el
index d2a0ecf144..b5d246ed40 100644
--- a/exwm.el
+++ b/exwm.el
@@ -5,7 +5,7 @@
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 ;; Maintainer: Chris Feng <chris.w.feng@gmail.com>
 ;; Version: 0
-;; Package-Requires: ((xelb "0.1"))
+;; Package-Requires: ((xelb "0.3"))
 ;; Keywords: unix
 ;; URL: https://github.com/ch11ng/exwm
 
@@ -74,6 +74,7 @@
 
 ;;; Code:
 
+(require 'server)
 (require 'exwm-core)
 (require 'exwm-workspace)
 (require 'exwm-layout)
@@ -526,14 +527,78 @@
         (exwm-manage--scan)
         (run-hooks 'exwm-init-hook)))))
 
+(defvar exwm-blocking-subrs '(x-file-dialog x-popup-dialog x-select-font)
+  "Subrs (primitives) that would normally block EXWM.")
+
 (defun exwm-enable (&optional undo)
   "Enable/Disable EXWM."
-  (if (eq undo 'undo)
-      (progn (remove-hook 'window-setup-hook #'exwm-init)
-             (remove-hook 'after-make-frame-functions #'exwm-init))
-    (setq frame-resize-pixelwise t)     ;mandatory; before init
-    (add-hook 'window-setup-hook #'exwm-init t)            ;for Emacs
-    (add-hook 'after-make-frame-functions #'exwm-init t))) ;for Emacs Client
+  (pcase undo
+    (`undo                              ;prevent reinitialization
+     (remove-hook 'window-setup-hook #'exwm-init)
+     (remove-hook 'after-make-frame-functions #'exwm-init))
+    (`undo-all                          ;attempt to revert everything
+     (remove-hook 'window-setup-hook #'exwm-init)
+     (remove-hook 'after-make-frame-functions #'exwm-init)
+     (remove-hook 'kill-emacs-hook #'exwm--server-stop)
+     (dolist (i exwm-blocking-subrs)
+       (advice-remove i #'exwm--server-eval-at)))
+    (_                                  ;enable EXWM
+     (setq frame-resize-pixelwise t)    ;mandatory; before init
+     (add-hook 'window-setup-hook #'exwm-init t)          ;for Emacs
+     (add-hook 'after-make-frame-functions #'exwm-init t) ;for Emacs Client
+     (add-hook 'kill-emacs-hook #'exwm--server-stop)
+     (dolist (i exwm-blocking-subrs)
+       (advice-add i :around #'exwm--server-eval-at)))))
+
+(defconst exwm--server-name "server-exwm"
+  "Name of the subordinate Emacs server.")
+(defvar exwm--server-process nil "Process of the subordinate Emacs server.")
+
+(defun exwm--server-stop ()
+  "Stop the subordinate Emacs server."
+  (server-force-delete exwm--server-name)
+  (when exwm--server-process
+    (delete-process exwm--server-process)
+    (setq exwm--server-process nil)))
+
+(defun exwm--server-eval-at (&rest args)
+  "Wrapper of `server-eval-at' used to advice subrs."
+  ;; Start the subordinate Emacs server if it's not alive
+  (unless (server-running-p exwm--server-name)
+    (when exwm--server-process (delete-process exwm--server-process))
+    (setq exwm--server-process
+          (start-process exwm--server-name
+                         nil
+                         (car command-line-args) ;The executable file
+                         "-d" x-display-name
+                         "-Q"
+                         (concat "--daemon=" exwm--server-name)
+                         "--eval"
+                         ;; Create an invisible frame
+                         "(make-frame '((window-system . x) (visibility)))"))
+    (while (not (server-running-p exwm--server-name))
+      (sit-for 0.001)))
+  (server-eval-at
+   exwm--server-name
+   `(progn (select-frame (car (frame-list)))
+           (let ((result ,(nconc (list (make-symbol (subr-name (car args))))
+                                 (cdr args))))
+             (pcase (type-of result)
+               ;; Return the name of a buffer
+               (`buffer (buffer-name result))
+               ;; We blindly convert all font objects to their XLFD names. This
+               ;; might cause problems of course, but it still has a chance to
+               ;; work (whereas directly passing font objects would merely
+               ;; raise errors).
+               ((or `font-entity `font-object `font-spec)
+                (font-xlfd-name result))
+               ;; Passing following types makes little sense
+               ((or `compiled-function `finalizer `frame `hash-table `marker
+                    `overlay `process `window `window-configuration))
+               ;; Passing the name of a subr
+               (`subr (make-symbol (subr-name result)))
+               ;; For other types, return the value as-is.
+               (t result))))))
 
 (defun exwm--ido-buffer-window-other-frame (orig-fun buffer)
   "Wrapper for `ido-buffer-window-other-frame' to exclude invisible windows."