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.
This commit is contained in:
Chris Feng 2015-09-27 19:31:00 +08:00
parent f685de12d4
commit 5184f0d7c1
7 changed files with 158 additions and 72 deletions

View file

@ -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).

View file

@ -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

View file

@ -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

View file

@ -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))

View file

@ -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)

View file

@ -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)))))

79
exwm.el
View file

@ -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."