Add auto-hiding minibuffer support

* exwm-floating.el (exwm-floating--set-floating): Take auto-hiding
minibuffer into account when calculating available height.
(exwm-floating--unset-floating): Restack the container to avoid further
restacking.
* exwm-input.el (exwm-input--update-focus): Use more accurate restacking.
(exwm-input--on-minibuffer-setup): Replaced by
`exwm-layout--on-minibuffer-setup' and `exwm-workspace--on-minibuffer-setup'.
(exwm-input-command-whitelist, exwm-input--during-command)
(exwm-input--on-KeyPress-line-mode): The functionality of
`exwm-input-command-whitelist' is replaced by `exwm-input--during-command',
which can automatically tell whether functions like `read-event' are being
called.
(exwm-input--init): Add/remove corresponding hooks.
* exwm-layout.el (exwm-layout--on-minibuffer-setup): Also set input focus.
(exwm-layout--on-echo-area-change): New function for refreshing layout when
the size of echo area changes.
(exwm-layout--init): Track size changes for fixed minibuffer and echo area.
* exwm-manage.el (exwm-manage--on-ConfigureRequest): Never grant restacking
requests initiated by other clients.
* exwm-workspace.el (exwm-workspace--minibuffer): New variable for the
auto-hiding minibuffer.
(exwm-workspace-minibuffer-position): For setting the position of the
auto-hiding minibuffer.
(exwm-workspace-display-echo-area-timeout): Seconds before echo area
auto-hides.
(exwm-workspace--display-echo-area-timer): The corresponding timer.
(exwm-workspace-switch): Configure the auto-hiding minibuffer when
switching workspace.
(exwm-workspace--update-minibuffer): New function for adjusting the height
of the auto-hiding minibuffer.
(exwm-workspace--on-ConfigureNotify): New function for configuring the
container of the auto-hiding minibuffer.
(exwm-workspace--display-buffer): New function for forcing
`minibuffer-completion-help' to use the workspace frame.
(exwm-workspace--show-minibuffer, exwm-workspace--hide-minibuffer):
New functions for showing/hiding the auto-hiding minibuffer (container).
(exwm-workspace--on-minibuffer-setup, exwm-workspace--on-minibuffer-exit):
New functions called when the auto-hiding minibuffer entered/exists.
(exwm-workspace--on-echo-area-dirty, exwm-workspace--on-echo-area-clear):
New functions when the auto-hiding echo area is ready to show/hide.
(exwm-workspace--init): Set up the auto-hiding minibuffer and workspace
frames.  Track sizes changes for auto-hiding minibuffer and echo area.
No need to set OverrideRedirect on workspace frames.
* exwm.el (exwm--init-icccm-ewmh): Correct the value of _NET_WORKAREA.
This commit is contained in:
Chris Feng 2016-02-06 12:59:33 +08:00
parent 2d42fee327
commit 0e4055d339
6 changed files with 320 additions and 71 deletions

View file

@ -106,8 +106,10 @@
;; FIXME: check normal hints restrictions ;; FIXME: check normal hints restrictions
(let* ((display-width (frame-pixel-width original-frame)) (let* ((display-width (frame-pixel-width original-frame))
(display-height (- (frame-pixel-height original-frame) (display-height (- (frame-pixel-height original-frame)
(if exwm-workspace-minibuffer-position
0
(window-pixel-height (minibuffer-window (window-pixel-height (minibuffer-window
original-frame)) original-frame)))
(* 2 (window-mode-line-height)) (* 2 (window-mode-line-height))
(window-header-line-height window) (window-header-line-height window)
(* 2 exwm-floating-border-width))) (* 2 exwm-floating-border-width)))
@ -180,12 +182,22 @@
(interactive) (interactive)
(let ((buffer (exwm--id->buffer id))) (let ((buffer (exwm--id->buffer id)))
(with-current-buffer buffer (with-current-buffer buffer
;; Reparent the container to the workspace
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow (make-instance 'xcb:ReparentWindow
:window exwm--container :window exwm--container
:parent (frame-parameter exwm-workspace--current :parent (frame-parameter exwm-workspace--current
'exwm-workspace) 'exwm-workspace)
:x 0 :y 0))) ;temporary position :x 0 :y 0)) ;temporary position
;; Put the container just above the Emacs frame
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window exwm--container
:value-mask (logior xcb:ConfigWindow:Sibling
xcb:ConfigWindow:StackMode)
:sibling (frame-parameter exwm-workspace--current
'exwm-outer-id)
:stack-mode xcb:StackMode:Above)))
(xcb:flush exwm--connection) (xcb:flush exwm--connection)
(with-current-buffer buffer (with-current-buffer buffer
(when exwm--floating-frame ;from floating to non-floating (when exwm--floating-frame ;from floating to non-floating

View file

@ -110,26 +110,25 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
(exwm--log "Set focus on #x%x" exwm--id) (exwm--log "Set focus on #x%x" exwm--id)
(exwm-input--set-focus exwm--id) (exwm-input--set-focus exwm--id)
;; Adjust stacking orders ;; Adjust stacking orders
(if exwm--floating-frame (when exwm--floating-frame
(if (memq exwm-workspace-minibuffer-position '(top bottom))
;; Put this floating X window just below the minibuffer.
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window exwm--container
:value-mask
(logior xcb:ConfigWindow:Sibling
xcb:ConfigWindow:StackMode)
:sibling (frame-parameter
exwm-workspace--minibuffer
'exwm-container)
:stack-mode xcb:StackMode:Below))
;; Put this floating X window at top. ;; Put this floating X window at top.
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow (make-instance 'xcb:ConfigureWindow
:window exwm--container :window exwm--container
:value-mask xcb:ConfigWindow:StackMode :value-mask xcb:ConfigWindow:StackMode
:stack-mode xcb:StackMode:TopIf)) :stack-mode xcb:StackMode:Above))))
;; This should be the last X window but one in the siblings.
(with-slots (children)
(xcb:+request-unchecked+reply exwm--connection
(make-instance 'xcb:QueryTree
:window
(frame-parameter exwm--frame
'exwm-workspace)))
(unless (eq (cadr children) exwm--container)
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window exwm--container
:value-mask xcb:ConfigWindow:StackMode
:stack-mode xcb:StackMode:Below)))))
;; Make sure Emacs frames are at bottom. ;; Make sure Emacs frames are at bottom.
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow (make-instance 'xcb:ConfigureWindow
@ -142,21 +141,9 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
(when (eq (selected-window) exwm-input--focus-window) (when (eq (selected-window) exwm-input--focus-window)
(exwm--log "Focus on %s" exwm-input--focus-window) (exwm--log "Focus on %s" exwm-input--focus-window)
(select-frame-set-input-focus (window-frame exwm-input--focus-window) (select-frame-set-input-focus (window-frame exwm-input--focus-window)
t) t)))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window (frame-parameter
(window-frame exwm-input--focus-window)
'exwm-outer-id)
:value-mask xcb:ConfigWindow:StackMode
:stack-mode xcb:StackMode:Below))
(xcb:flush exwm--connection)))
(setq exwm-input--focus-window nil)))) (setq exwm-input--focus-window nil))))
(defun exwm-input--on-minibuffer-setup ()
"Run in minibuffer-setup-hook to set input focus to the frame."
(x-focus-frame (selected-frame)))
(defvar exwm-input--during-key-sequence nil (defvar exwm-input--during-key-sequence nil
"Non-nil indicates Emacs is waiting for more keys to form a key sequence.") "Non-nil indicates Emacs is waiting for more keys to form a key sequence.")
(defvar exwm-input--temp-line-mode nil (defvar exwm-input--temp-line-mode nil
@ -278,12 +265,13 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
(global-set-key key command) (global-set-key key command)
(cl-pushnew key exwm-input--global-keys)) (cl-pushnew key exwm-input--global-keys))
;; These commands usually call something like `read-char' without using the
;; minibuffer, so they will not get inputs after invoked. It'd be better if we
;; can determine whether there's a command waiting for input so that this
;; variable can be removed.
(defvar exwm-input-command-whitelist nil (defvar exwm-input-command-whitelist nil
"A list of commands that when active all keys should be forwarded to Emacs.") "A list of commands that when active all keys should be forwarded to Emacs.")
(make-obsolete-variable 'exwm-input-command-whitelist
"This variable can be safely removed." "25.1")
(defvar exwm-input--during-command nil
"Indicate whether between `pre-command-hook' and `post-command-hook'.")
;;;###autoload ;;;###autoload
(defun exwm-input--on-KeyPress-line-mode (key-press) (defun exwm-input--on-KeyPress-line-mode (key-press)
@ -295,8 +283,8 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
(setq event (xcb:keysyms:keysym->event exwm--connection (setq event (xcb:keysyms:keysym->event exwm--connection
keysym state)) keysym state))
(or exwm-input--during-key-sequence (or exwm-input--during-key-sequence
exwm-input--during-command
(setq minibuffer-window (active-minibuffer-window)) (setq minibuffer-window (active-minibuffer-window))
(memq real-this-command exwm-input-command-whitelist)
(memq event exwm-input--global-prefix-keys) (memq event exwm-input--global-prefix-keys)
(memq event exwm-input-prefix-keys) (memq event exwm-input-prefix-keys)
(memq event exwm-input--simulation-prefix-keys))) (memq event exwm-input--simulation-prefix-keys)))
@ -501,9 +489,12 @@ SIMULATION-KEYS is a list of alist (key-sequence1 . key-sequence2)."
#'exwm-floating--do-moveresize) #'exwm-floating--do-moveresize)
;; `pre-command-hook' marks the end of a key sequence (existing or not) ;; `pre-command-hook' marks the end of a key sequence (existing or not)
(add-hook 'pre-command-hook #'exwm-input--finish-key-sequence) (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)))
;; Update focus when buffer list updates ;; Update focus when buffer list updates
(add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) (add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update)
(add-hook 'minibuffer-setup-hook #'exwm-input--on-minibuffer-setup)
;; Update prefix keys for global keys ;; Update prefix keys for global keys
(exwm-input--update-global-prefix-keys)) (exwm-input--update-global-prefix-keys))

View file

@ -279,10 +279,20 @@
"Refresh layout when minibuffer grows." "Refresh layout when minibuffer grows."
(run-with-idle-timer 0.01 nil ;FIXME (run-with-idle-timer 0.01 nil ;FIXME
(lambda () (lambda ()
(when (and (< 1 (window-height (minibuffer-window))) (when (< 1 (window-height (minibuffer-window)))
(not (and (eq major-mode 'exwm-mode) (exwm-layout--refresh))))
exwm--floating-frame))) ;; Set input focus on the Emacs frame
(exwm-layout--refresh))))) (x-focus-frame (window-frame (minibuffer-selected-window))))
(defun exwm-layout--on-echo-area-change (&optional dirty)
"Run when message arrives or in `echo-area-clear-hook' to refresh layout."
(when (and (current-message)
(or (cl-position ?\n (current-message))
(> (length (current-message))
(frame-width exwm-workspace--current))))
(if dirty
(exwm-layout--refresh)
(run-with-idle-timer 0.01 nil #'exwm-layout--refresh)))) ;FIXME
(defun exwm-layout-enlarge-window (delta &optional horizontal) (defun exwm-layout-enlarge-window (delta &optional horizontal)
"Make the selected window DELTA pixels taller. "Make the selected window DELTA pixels taller.
@ -383,8 +393,11 @@ See also `exwm-layout-enlarge-window'."
"Initialize layout module." "Initialize layout module."
;; Auto refresh layout ;; Auto refresh layout
(add-hook 'window-configuration-change-hook #'exwm-layout--refresh) (add-hook 'window-configuration-change-hook #'exwm-layout--refresh)
(unless (memq exwm-workspace-minibuffer-position '(top bottom))
;; Refresh when minibuffer grows ;; Refresh when minibuffer grows
(add-hook 'minibuffer-setup-hook #'exwm-layout--on-minibuffer-setup t)) (add-hook 'minibuffer-setup-hook #'exwm-layout--on-minibuffer-setup t)
(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)))

View file

@ -360,8 +360,7 @@ Would you like to kill it? "
(let ((obj (make-instance 'xcb:ConfigureRequest)) (let ((obj (make-instance 'xcb:ConfigureRequest))
buffer edges) buffer edges)
(xcb:unmarshal obj data) (xcb:unmarshal obj data)
(with-slots (stack-mode window sibling x y width height border-width (with-slots (window x y width height border-width value-mask)
value-mask)
obj obj
(exwm--log "ConfigureRequest from #x%x (#x%x) @%dx%d%+d%+d, border: %d" (exwm--log "ConfigureRequest from #x%x (#x%x) @%dx%d%+d%+d, border: %d"
window value-mask width height x y border-width) window value-mask width height x y border-width)
@ -391,14 +390,16 @@ Would you like to kill it? "
:border-width 0 :override-redirect 0) :border-width 0 :override-redirect 0)
exwm--connection)))) exwm--connection))))
(exwm--log "ConfigureWindow (preserve geometry)") (exwm--log "ConfigureWindow (preserve geometry)")
;; Configure unmanaged windows ;; Configure the unmanaged window without changing the stacking order.
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow (make-instance 'xcb:ConfigureWindow
:window window :window window
:value-mask value-mask :value-mask
(logand value-mask
(lognot xcb:ConfigWindow:Sibling)
(lognot xcb:ConfigWindow:StackMode))
:x x :y y :width width :height height :x x :y y :width width :height height
:border-width border-width :border-width border-width)))))
:sibling sibling :stack-mode stack-mode)))))
(xcb:flush exwm--connection)) (xcb:flush exwm--connection))
(defun exwm-manage--on-MapRequest (data _synthetic) (defun exwm-manage--on-MapRequest (data _synthetic)

View file

@ -24,7 +24,6 @@
;; This module adds workspace support for EXWM. ;; This module adds workspace support for EXWM.
;; Todo: ;; Todo:
;; + Auto hide minibuffer, or allow users to place it elsewhere.
;; + Add system tray support. ;; + Add system tray support.
;;; Code: ;;; Code:
@ -100,6 +99,17 @@
(defvar exwm-workspace-current-index 0 "Index of current active workspace.") (defvar exwm-workspace-current-index 0 "Index of current active workspace.")
(defvar exwm-workspace-show-all-buffers nil (defvar exwm-workspace-show-all-buffers nil
"Non-nil to show buffers on other workspaces.") "Non-nil to show buffers on other workspaces.")
(defvar exwm-workspace--minibuffer nil
"The minibuffer frame shared among all frames.")
(defvar exwm-workspace-minibuffer-position nil
"Position of the minibuffer frame.
Value nil means to use the default position which is fixed at bottom, while
'top and 'bottom mean to use an auto-hiding minibuffer.")
(defvar exwm-workspace-display-echo-area-timeout 1
"Timeout for displaying echo area.")
(defvar exwm-workspace--display-echo-area-timer nil
"Timer for auto-hiding echo area.")
;;;###autoload ;;;###autoload
(defun exwm-workspace-switch (index &optional force) (defun exwm-workspace-switch (index &optional force)
@ -135,7 +145,30 @@ The optional FORCE option is for internal use only."
;; Close the (possible) active minibuffer ;; Close the (possible) active minibuffer
(when (active-minibuffer-window) (when (active-minibuffer-window)
(run-with-idle-timer 0 nil (lambda () (abort-recursive-edit)))) (run-with-idle-timer 0 nil (lambda () (abort-recursive-edit))))
(if (not (memq exwm-workspace-minibuffer-position '(top bottom)))
(setq default-minibuffer-frame frame) (setq default-minibuffer-frame frame)
;; Resize/reposition the minibuffer frame
(let ((x 0)
(y (if (eq exwm-workspace-minibuffer-position 'top)
0
(- (frame-pixel-height frame)
(frame-pixel-height exwm-workspace--minibuffer))))
(width (x-display-pixel-width))
(container (frame-parameter exwm-workspace--minibuffer
'exwm-container)))
(xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow
:window container
:parent (frame-parameter frame 'exwm-workspace)
:x x :y y))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window container
:value-mask (logior xcb:ConfigWindow:Width
xcb:ConfigWindow:StackMode)
:width width
:stack-mode xcb:StackMode:Above))
(set-frame-width exwm-workspace--minibuffer width nil t)))
;; Hide windows in other workspaces by preprending a space ;; Hide windows in other workspaces by preprending a space
(unless exwm-workspace-show-all-buffers (unless exwm-workspace-show-all-buffers
(dolist (i exwm--id-buffer-alist) (dolist (i exwm--id-buffer-alist)
@ -266,6 +299,146 @@ The optional FORCE option is for internal use only."
(xcb:flush exwm--connection) (xcb:flush exwm--connection)
frame)) frame))
(defun exwm-workspace--update-minibuffer (&optional echo-area)
"Update the minibuffer frame."
(let ((height
(with-current-buffer
(window-buffer (minibuffer-window exwm-workspace--minibuffer))
(max 1
(if echo-area
(let ((width (frame-width exwm-workspace--minibuffer))
(result 0))
(mapc (lambda (i)
(setq result
(+ result
(ceiling (1+ (length i)) width))))
(split-string (current-message) "\n"))
result)
(count-screen-lines))))))
(when (and (integerp max-mini-window-height)
(> height max-mini-window-height))
(setq height max-mini-window-height))
(set-frame-height exwm-workspace--minibuffer height)))
(defun exwm-workspace--on-ConfigureNotify (data _synthetic)
"Adjust the container to fit the minibuffer frame."
(let ((obj (make-instance 'xcb:ConfigureNotify))
value-mask y)
(xcb:unmarshal obj data)
(with-slots (window height) obj
(when (eq (frame-parameter exwm-workspace--minibuffer 'exwm-outer-id)
window)
(when (and (floatp max-mini-window-height)
(> height (* max-mini-window-height
(frame-pixel-height exwm-workspace--current))))
(setq height (floor
(* max-mini-window-height
(frame-pixel-height exwm-workspace--current))))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window window
:value-mask xcb:ConfigWindow:Height
:height height)))
(if (eq exwm-workspace-minibuffer-position 'top)
(setq value-mask xcb:ConfigWindow:Height
y 0)
(setq value-mask (logior xcb:ConfigWindow:Y xcb:ConfigWindow:Height)
y (- (frame-pixel-height exwm-workspace--current) height)))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window (frame-parameter exwm-workspace--minibuffer
'exwm-container)
:value-mask value-mask
:y y
:height height))
(xcb:flush exwm--connection)))))
(defun exwm-workspace--display-buffer (buffer alist)
"Display buffer in the current workspace frame.
This functions is modified from `display-buffer-reuse-window' and
`display-buffer-pop-up-window'."
(when (eq (selected-frame) exwm-workspace--minibuffer)
(setq buffer (get-buffer buffer)) ;convert string to buffer.
(let (window)
(if (setq window (get-buffer-window buffer exwm-workspace--current))
(window--display-buffer buffer window 'reuse alist)
(when (setq window (or (window--try-to-split-window
(get-largest-window exwm-workspace--current t)
alist)
(window--try-to-split-window
(get-lru-window exwm-workspace--current t)
alist)))
(window--display-buffer
buffer window 'window alist display-buffer-mark-dedicated))))))
(defun exwm-workspace--show-minibuffer ()
"Show the minibuffer frame."
;; Cancel pending timer.
(when exwm-workspace--display-echo-area-timer
(cancel-timer exwm-workspace--display-echo-area-timer)
(setq exwm-workspace--display-echo-area-timer nil))
;; Show the minibuffer frame.
(xcb:+request exwm--connection
(make-instance 'xcb:MapWindow
:window (frame-parameter exwm-workspace--minibuffer
'exwm-container)))
(xcb:flush exwm--connection)
;; Unfortunately we need the following lines to workaround a cursor
;; flickering issue for line-mode floating X windows. They just make the
;; minibuffer appear to be focused.
(with-current-buffer (window-buffer (minibuffer-window
exwm-workspace--minibuffer))
(setq cursor-in-non-selected-windows
(frame-parameter exwm-workspace--minibuffer 'cursor-type))))
(defun exwm-workspace--hide-minibuffer ()
"Hide the minibuffer frame."
;; Hide the minibuffer frame.
(xcb:+request exwm--connection
(make-instance 'xcb:UnmapWindow
:window (frame-parameter exwm-workspace--minibuffer
'exwm-container)))
(xcb:flush exwm--connection))
(defun exwm-workspace--on-minibuffer-setup ()
"Run in minibuffer-setup-hook to show the minibuffer and its container."
(unless (> -1 (minibuffer-depth))
(add-hook 'post-command-hook #'exwm-workspace--update-minibuffer)
(exwm-workspace--show-minibuffer)
;; Set input focus on the Emacs frame
(x-focus-frame (window-frame (minibuffer-selected-window)))))
(defun exwm-workspace--on-minibuffer-exit ()
"Run in minibuffer-exit-hook to hide the minibuffer container."
(unless (> -1 (minibuffer-depth))
(remove-hook 'post-command-hook #'exwm-workspace--update-minibuffer)
(exwm-workspace--hide-minibuffer)))
(defvar exwm-input--during-command)
(defun exwm-workspace--on-echo-area-dirty ()
"Run when new message arrives to show the echo area and its container."
(when (and (not (active-minibuffer-window))
(or (current-message)
cursor-in-echo-area))
(exwm-workspace--update-minibuffer t)
(exwm-workspace--show-minibuffer)
(unless (or (not exwm-workspace-display-echo-area-timeout)
exwm-input--during-command ;e.g. read-event
input-method-use-echo-area)
(setq exwm-workspace--display-echo-area-timer
(run-with-timer exwm-workspace-display-echo-area-timeout nil
#'exwm-workspace--on-echo-area-clear)))))
(defun exwm-workspace--on-echo-area-clear ()
"Run in echo-area-clear-hook to hide echo area container."
(unless (active-minibuffer-window)
(exwm-workspace--hide-minibuffer))
(when exwm-workspace--display-echo-area-timer
(cancel-timer exwm-workspace--display-echo-area-timer)
(setq exwm-workspace--display-echo-area-timer nil)))
(defun exwm-workspace--init () (defun exwm-workspace--init ()
"Initialize workspace module." "Initialize workspace module."
(cl-assert (and (< 0 exwm-workspace-number) (>= 10 exwm-workspace-number))) (cl-assert (and (< 0 exwm-workspace-number) (>= 10 exwm-workspace-number)))
@ -276,19 +449,79 @@ The optional FORCE option is for internal use only."
(0 (y-or-n-p prompt)) (0 (y-or-n-p prompt))
(x (yes-or-no-p (format "[EXWM] %d window%s currently alive. %s" (x (yes-or-no-p (format "[EXWM] %d window%s currently alive. %s"
x (if (= x 1) "" "s") prompt)))))) x (if (= x 1) "" "s") prompt))))))
;; Initialize workspaces (if (not (memq exwm-workspace-minibuffer-position '(top bottom)))
;; Initialize workspaces with minibuffers.
(progn
(setq exwm-workspace--list (frame-list)) (setq exwm-workspace--list (frame-list))
(when (< 1 (length exwm-workspace--list)) (when (< 1 (length exwm-workspace--list))
;; Emacs client creates an extra (but unusable) frame ;; Emacs client creates an extra (but unusable) frame.
(dolist (i exwm-workspace--list) (dolist (i exwm-workspace--list)
(unless (frame-parameter i 'window-id) (unless (frame-parameter i 'window-id)
(setq exwm-workspace--list (delq i exwm-workspace--list)))) (setq exwm-workspace--list (delq i exwm-workspace--list))))
(cl-assert (= 1 (length exwm-workspace--list))) (cl-assert (= 1 (length exwm-workspace--list)))
;; Prevent user from deleting this frame by accident ;; Prevent user from deleting this frame by accident.
(set-frame-parameter (car exwm-workspace--list) 'client nil)) (set-frame-parameter (car exwm-workspace--list) 'client nil))
;; Create remaining frames ;; Create remaining frames.
(dotimes (_ (1- exwm-workspace-number)) (dotimes (_ (1- exwm-workspace-number))
(nconc exwm-workspace--list (list (make-frame '((window-system . x)))))) (nconc exwm-workspace--list
(list (make-frame '((window-system . x)))))))
;; Initialize workspaces without minibuffers.
(let ((old-frames (frame-list)))
(setq exwm-workspace--minibuffer
(make-frame '((window-system . x) (minibuffer . only)
(left . 10000) (right . 10000)
(width . 0) (height . 0)))
default-minibuffer-frame exwm-workspace--minibuffer)
;; Remove/hide existing frames.
(dolist (f old-frames)
(if (frame-parameter f 'client)
(make-frame-invisible f)
(delete-frame f))))
(let ((outer-id (string-to-number
(frame-parameter exwm-workspace--minibuffer
'outer-window-id)))
(container (xcb:generate-id exwm--connection)))
(set-frame-parameter exwm-workspace--minibuffer 'exwm-outer-id outer-id)
(set-frame-parameter exwm-workspace--minibuffer 'exwm-container
container)
(xcb:+request exwm--connection
(make-instance 'xcb:CreateWindow
:depth 0 :wid container :parent exwm--root
:x -1 :y -1 :width 1 :height 1
:border-width 0 :class xcb:WindowClass:CopyFromParent
:visual 0 ;CopyFromParent
:value-mask xcb:CW:OverrideRedirect
:override-redirect 1))
(exwm--debug
(xcb:+request exwm--connection
(make-instance 'xcb:ewmh:set-_NET_WM_NAME
:window container
:data "Minibuffer container")))
(xcb:+request exwm--connection
(make-instance 'xcb:ReparentWindow
:window outer-id :parent container :x 0 :y 0))
;; Attach event listener for monitoring the frame
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window outer-id
:value-mask xcb:CW:EventMask
:event-mask xcb:EventMask:StructureNotify))
(xcb:+event exwm--connection 'xcb:ConfigureNotify
#'exwm-workspace--on-ConfigureNotify))
;; 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)
(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--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))
;; Configure workspaces ;; Configure workspaces
(dolist (i exwm-workspace--list) (dolist (i exwm-workspace--list)
(let ((outer-id (string-to-number (frame-parameter i 'outer-window-id))) (let ((outer-id (string-to-number (frame-parameter i 'outer-window-id)))
@ -296,11 +529,6 @@ The optional FORCE option is for internal use only."
;; Save window IDs ;; Save window IDs
(set-frame-parameter i 'exwm-outer-id outer-id) (set-frame-parameter i 'exwm-outer-id outer-id)
(set-frame-parameter i 'exwm-workspace workspace) (set-frame-parameter i 'exwm-workspace workspace)
;; Set OverrideRedirect on all frames
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window outer-id :value-mask xcb:CW:OverrideRedirect
:override-redirect 1))
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:CreateWindow (make-instance 'xcb:CreateWindow
:depth 0 :wid workspace :parent exwm--root :depth 0 :wid workspace :parent exwm--root

View file

@ -466,9 +466,13 @@
(make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT
:window exwm--root :window exwm--root
:data (make-vector (* 2 exwm-workspace-number) 0))) :data (make-vector (* 2 exwm-workspace-number) 0)))
;; Set _NET_WORKAREA (with minibuffer and bottom mode-line excluded) ;; Set _NET_WORKAREA (with minibuffer excluded)
(let* ((workareas (let* ((workareas
(vector 0 0 (x-display-pixel-width) (x-display-pixel-height))) (vector 0 0 (x-display-pixel-width)
(- (x-display-pixel-height)
(if exwm-workspace-minibuffer-position
0
(window-pixel-height (minibuffer-window))))))
(workareas (mapconcat (lambda (_) workareas) (workareas (mapconcat (lambda (_) workareas)
(make-list exwm-workspace-number 0) []))) (make-list exwm-workspace-number 0) [])))
(xcb:+request exwm--connection (xcb:+request exwm--connection