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:
		
							parent
							
								
									2d42fee327
								
							
						
					
					
						commit
						0e4055d339
					
				
					 6 changed files with 320 additions and 71 deletions
				
			
		|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -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)) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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))) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
							
								
								
									
										8
									
								
								exwm.el
									
										
									
									
									
								
							
							
						
						
									
										8
									
								
								exwm.el
									
										
									
									
									
								
							|  | @ -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 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue