From 5184f0d7c1b540a6241904528d068dce288a911e Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 27 Sep 2015 19:31:00 +0800 Subject: [PATCH] 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. --- README.org => README.md | 9 ++--- exwm-core.el | 8 +++-- exwm-floating.el | 62 ++++++++++++++++++-------------- exwm-layout.el | 47 +++++++++++++----------- exwm-manage.el | 5 +-- exwm-randr.el | 20 ++++++----- exwm.el | 79 +++++++++++++++++++++++++++++++++++++---- 7 files changed, 158 insertions(+), 72 deletions(-) rename README.org => README.md (52%) diff --git a/README.org b/README.md similarity index 52% rename from README.org rename to README.md index 49ba04b..09fe470 100644 --- a/README.org +++ b/README.md @@ -1,7 +1,7 @@ -#+TITLE: Emacs X Window Manager +# Emacs X Window Manager EXWM (Emacs X Window Manager) is a full-featured tiling X window manager for -Emacs built on top of [[https://github.com/ch11ng/xelb][XELB]]. +Emacs built on top of [XELB](https://github.com/ch11ng/xelb). It features: + Fully keyboard-driven operation + Hybrid layout modes (tiling & stacking) @@ -9,7 +9,8 @@ It features: + ICCCM/EWMH compliance + Basic RandR support (optional) -Please check the [[https://github.com/ch11ng/exwm/wiki][User Guide]] for more details. +Please check the [User Guide](https://github.com/ch11ng/exwm/wiki) +for more details. -*Note*: If you install EXWM from source, you need to manually install XELB +**Note**: If you install EXWM from source, you need to manually install XELB (either from source or GNU ELPA). diff --git a/exwm-core.el b/exwm-core.el index 0f39c2d..1ac8678 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -69,12 +69,14 @@ (make-instance 'xcb:ChangeWindowAttributes :window exwm--root :value-mask xcb:CW:EventMask - :event-mask (logior xcb:EventMask:StructureNotify - xcb:EventMask:SubstructureRedirect))) + :event-mask (eval-when-compile + (logior xcb:EventMask:SubstructureRedirect + xcb:EventMask:StructureNotify)))) (xcb:flush exwm--connection)) (defconst exwm--client-event-mask - (logior xcb:EventMask:StructureNotify xcb:EventMask:PropertyChange) + (eval-when-compile + (logior xcb:EventMask:StructureNotify xcb:EventMask:PropertyChange)) "Event mask set on all managed windows.") ;; Internal variables diff --git a/exwm-floating.el b/exwm-floating.el index 0994cb9..59b4e44 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -247,9 +247,10 @@ are provided. You should call `xcb:flush' and restore the value of :window (or frame-outer-id (frame-parameter exwm--floating-frame 'exwm-outer-id)) - :value-mask (logior xcb:ConfigWindow:Width - xcb:ConfigWindow:Height - xcb:ConfigWindow:StackMode) + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height + xcb:ConfigWindow:StackMode)) :width (+ width (* 2 exwm-floating-border-width)) :height (+ height (* 2 exwm-floating-border-width) (window-mode-line-height) @@ -336,18 +337,20 @@ are provided. You should call `xcb:flush' and restore the value of exwm-floating--moveresize-calculate `(lambda (x y) (vector ,frame-id - ,(logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y) + ,(eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y)) (- x ,win-x) (- y ,win-y) 0 0)))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPLEFT) (setq cursor exwm-floating--cursor-top-left exwm-floating--moveresize-calculate `(lambda (x y) (vector ,frame-id - ,(logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) + ,(eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) (- x ,win-x) (- y ,win-y) (- ,(+ root-x width) x) (- ,(+ root-y height) y))))) @@ -356,17 +359,19 @@ are provided. You should call `xcb:flush' and restore the value of exwm-floating--moveresize-calculate `(lambda (x y) (vector ,frame-id - ,(logior xcb:ConfigWindow:Y - xcb:ConfigWindow:Height) + ,(eval-when-compile + (logior xcb:ConfigWindow:Y + xcb:ConfigWindow:Height)) 0 (- y ,win-y) 0 (- ,(+ root-y height) y))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPRIGHT) (setq cursor exwm-floating--cursor-top-right exwm-floating--moveresize-calculate `(lambda (x y) (vector ,frame-id - ,(logior xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) + ,(eval-when-compile + (logior xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) 0 (- y ,win-y) (- x ,(- root-x width)) (- ,(+ root-y height) y))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_RIGHT) @@ -380,8 +385,9 @@ are provided. You should call `xcb:flush' and restore the value of exwm-floating--moveresize-calculate `(lambda (x y) (vector ,frame-id - ,(logior xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) + ,(eval-when-compile + (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) 0 0 (- x ,(- root-x width)) (- y ,(- root-y height)))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOM) @@ -396,9 +402,10 @@ are provided. You should call `xcb:flush' and restore the value of exwm-floating--moveresize-calculate `(lambda (x y) (vector ,frame-id - ,(logior xcb:ConfigWindow:X - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) + ,(eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) (- x ,win-x) 0 (- ,(+ root-x width) x) @@ -408,15 +415,17 @@ are provided. You should call `xcb:flush' and restore the value of exwm-floating--moveresize-calculate `(lambda (x y) (vector ,frame-id - ,(logior xcb:ConfigWindow:X - xcb:ConfigWindow:Width) + ,(eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Width)) (- x ,win-x) 0 (- ,(+ root-x width) x) 0))))) ;; Select events and change cursor (should always succeed) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GrabPointer :owner-events 0 :grab-window frame-id - :event-mask (logior xcb:EventMask:ButtonRelease - xcb:EventMask:ButtonMotion) + :event-mask (eval-when-compile + (logior xcb:EventMask:ButtonRelease + xcb:EventMask:ButtonMotion)) :pointer-mode xcb:GrabMode:Async :keyboard-mode xcb:GrabMode:Async :confine-to xcb:Window:None @@ -472,7 +481,7 @@ are provided. You should call `xcb:flush' and restore the value of (make-instance 'xcb:ConfigureWindow :window (elt result 0) :value-mask (elt result 1) :x (- (elt result 2) frame-x) - :y (- (elt result 3) frame-y) + :y (- (elt result 3) frame-y) :width (elt result 4) :height (elt result 5))) (xcb:flush exwm--connection)))) @@ -492,8 +501,9 @@ Both DELTA-X and DELTA-Y default to 1. This command should be bound locally." (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window id - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y) + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y)) :x (+ (slot-value geometry 'x) delta-x) :y (+ (slot-value geometry 'y) delta-y))) ;; Inform the X window that its absolute position is changed diff --git a/exwm-layout.el b/exwm-layout.el index 4ccaef7..1d11dbc 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -56,11 +56,12 @@ (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window id - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height - xcb:ConfigWindow:StackMode) + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height + xcb:ConfigWindow:StackMode)) :x x :y y :width width :height height ;; In order to put non-floating window at bottom :stack-mode xcb:StackMode:Below)) @@ -122,10 +123,11 @@ (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window outer-id - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) :x 0 :y 0 :width (frame-pixel-width exwm-workspace--current) :height (frame-pixel-height @@ -134,10 +136,11 @@ (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window exwm--id - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) :x 0 :y 0 :width (frame-pixel-width exwm-workspace--current) :height (frame-pixel-height exwm-workspace--current))) @@ -161,10 +164,11 @@ (make-instance 'xcb:ConfigureWindow :window (frame-parameter exwm--floating-frame 'exwm-outer-id) - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) :x (elt exwm--floating-frame-geometry 0) :y (elt exwm--floating-frame-geometry 1) :width (elt exwm--floating-frame-geometry 2) @@ -194,10 +198,11 @@ (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window id - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) :x x :y y :width width :height height)) diff --git a/exwm-manage.el b/exwm-manage.el index d83fc44..ae707f7 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -124,8 +124,9 @@ corresponding buffer.") (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window id - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y) + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y)) :x (/ (- (frame-pixel-width exwm-workspace--current) width) diff --git a/exwm-randr.el b/exwm-randr.el index fc8477d..26d15dc 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -90,10 +90,11 @@ (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window (frame-parameter frame 'exwm-outer-id) - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) :x x :y y :width width :height height)) (setq workareas (nconc workareas (list x y width height)) viewports (nconc viewports (list x y)))))) @@ -133,11 +134,12 @@ (make-instance 'xcb:randr:SelectInput :window exwm--root :enable xcb:randr:NotifyMask:ScreenChange - ;; :enable (logior - ;; xcb:randr:NotifyMask:ScreenChange - ;; xcb:randr:NotifyMask:OutputChange - ;; xcb:randr:NotifyMask:OutputProperty - ;; xcb:randr:NotifyMask:CrtcChange) + ;; :enable (eval-when-compile + ;; (logior + ;; xcb:randr:NotifyMask:ScreenChange + ;; xcb:randr:NotifyMask:OutputChange + ;; xcb:randr:NotifyMask:OutputProperty + ;; xcb:randr:NotifyMask:CrtcChange)) )) (xcb:flush exwm--connection))))) diff --git a/exwm.el b/exwm.el index d2a0ecf..b5d246e 100644 --- a/exwm.el +++ b/exwm.el @@ -5,7 +5,7 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng ;; 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."