Merge branch 'feat/systemtray' into externals/exwm
A simple system tray based on the X11 'System Tray' and XEmbed protocol.
This commit is contained in:
		
						commit
						fdfdabf95a
					
				
					 10 changed files with 517 additions and 72 deletions
				
			
		|  | @ -3,11 +3,12 @@ | |||
| EXWM (Emacs X Window Manager) is a full-featured tiling X window manager for | ||||
| Emacs built on top of [XELB](https://github.com/ch11ng/xelb). | ||||
| It features: | ||||
| + Fully keyboard-driven operation | ||||
| + Fully keyboard-driven operations | ||||
| + Hybrid layout modes (tiling & stacking) | ||||
| + Workspace support | ||||
| + ICCCM/EWMH compliance | ||||
| + Basic RandR support (optional) | ||||
| + (Optional) RandR (multi-monitor) support | ||||
| + (Optional) system tray | ||||
| 
 | ||||
| Please check the [User Guide](https://github.com/ch11ng/exwm/wiki) | ||||
| for more details. | ||||
|  |  | |||
|  | @ -78,6 +78,9 @@ | |||
|     (logior xcb:EventMask:StructureNotify xcb:EventMask:PropertyChange)) | ||||
|   "Event mask set on all managed windows.") | ||||
| 
 | ||||
| (declare-function exwm-input--on-KeyPress-line-mode "exwm-input.el" | ||||
|                   (key-press)) | ||||
| 
 | ||||
| ;; Internal variables | ||||
| (defvar-local exwm--id nil)               ;window ID | ||||
| (defvar-local exwm--container nil)        ;container | ||||
|  | @ -110,7 +113,7 @@ | |||
| (defvar-local exwm--normal-hints-max-height nil) | ||||
| ;; (defvar-local exwm--normal-hints-win-gravity nil) | ||||
| ;; WM_HINTS | ||||
| (defvar-local exwm--hints-input nil)    ;FIXME | ||||
| (defvar-local exwm--hints-input nil) | ||||
| (defvar-local exwm--hints-urgency nil) | ||||
| ;; _MOTIF_WM_HINTS | ||||
| (defvar-local exwm--mwm-hints nil) | ||||
|  | @ -126,6 +129,8 @@ | |||
|     map) | ||||
|   "Keymap for `exwm-mode'.") | ||||
| 
 | ||||
| (declare-function exwm-manage--kill-buffer-query-function "exwm-manage.el") | ||||
| 
 | ||||
| (define-derived-mode exwm-mode nil "EXWM" | ||||
|   "Major mode for managing X windows. | ||||
| 
 | ||||
|  |  | |||
|  | @ -28,7 +28,6 @@ | |||
| 
 | ||||
| (require 'xcb-cursor) | ||||
| (require 'exwm-core) | ||||
| (eval-when-compile (require 'exwm-workspace)) | ||||
| 
 | ||||
| (defvar exwm-floating-border-width 1 "Border width of the floating window.") | ||||
| (defvar exwm-floating-border-color "navy" | ||||
|  | @ -50,12 +49,17 @@ | |||
| (defvar exwm-floating--cursor-bottom-left nil) | ||||
| (defvar exwm-floating--cursor-left nil) | ||||
| 
 | ||||
| (declare-function exwm-layout--refresh "exwm-layout.el") | ||||
| (defvar exwm-workspace--current) | ||||
| (defvar exwm-workspace--list) | ||||
| (defvar exwm-workspace-current-index) | ||||
| (defvar exwm-workspace--switch-history-outdated) | ||||
| (defvar exwm-workspace-minibuffer-position) | ||||
| 
 | ||||
| (declare-function exwm-layout--refresh "exwm-layout.el") | ||||
| (declare-function exwm-layout--show "exwm-layout.el") | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-floating--set-floating (id) | ||||
|   "Make window ID floating." | ||||
|   (interactive) | ||||
|   (let ((window (get-buffer-window (exwm--id->buffer id)))) | ||||
|     (when window                        ;window in non-floating state | ||||
|       (set-window-buffer window (other-buffer)))) ;hide it first | ||||
|  | @ -85,7 +89,7 @@ | |||
|                      (unsplittable . t))))) ;and fix the size later | ||||
|          (outer-id (string-to-number (frame-parameter frame 'outer-window-id))) | ||||
|          (container (with-current-buffer (exwm--id->buffer id) | ||||
|                           exwm--container)) | ||||
|                       exwm--container)) | ||||
|          (window (frame-first-window frame)) ;and it's the only window | ||||
|          (x (slot-value exwm--geometry 'x)) | ||||
|          (y (slot-value exwm--geometry 'y)) | ||||
|  | @ -194,10 +198,8 @@ | |||
|     (select-frame-set-input-focus frame)) | ||||
|   (run-hooks 'exwm-floating-setup-hook)) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-floating--unset-floating (id) | ||||
|   "Make window ID non-floating." | ||||
|   (interactive) | ||||
|   (let ((buffer (exwm--id->buffer id))) | ||||
|     (with-current-buffer buffer | ||||
|       ;; Reparent the frame back to the root window. | ||||
|  | @ -257,7 +259,6 @@ | |||
| (defvar exwm-floating--moveresize-calculate nil | ||||
|   "Calculate move/resize parameters [buffer event-mask x y width height].") | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-floating--start-moveresize (id &optional type) | ||||
|   "Start move/resize." | ||||
|   (let ((buffer (exwm--id->buffer id)) | ||||
|  | @ -404,7 +405,6 @@ | |||
|                              :cursor cursor | ||||
|                              :time xcb:Time:CurrentTime))))))) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-floating--stop-moveresize (&rest _args) | ||||
|   "Stop move/resize." | ||||
|   (xcb:+request exwm--connection | ||||
|  | @ -434,7 +434,6 @@ | |||
|   (xcb:flush exwm--connection) | ||||
|   (setq exwm-floating--moveresize-calculate nil)) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-floating--do-moveresize (data _synthetic) | ||||
|   "Perform move/resize." | ||||
|   (when exwm-floating--moveresize-calculate | ||||
|  |  | |||
|  | @ -37,7 +37,6 @@ | |||
| 
 | ||||
| (require 'xcb-keysyms) | ||||
| (require 'exwm-core) | ||||
| (eval-when-compile (require 'exwm-workspace)) | ||||
| 
 | ||||
| (defvar exwm-input-move-event 's-down-mouse-1 | ||||
|   "Emacs event to start moving a window.") | ||||
|  | @ -94,6 +93,11 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") | |||
|             exwm-input--timer | ||||
|             (run-with-idle-timer 0.01 nil #'exwm-input--update-focus))))) | ||||
| 
 | ||||
| (defvar exwm-workspace--current) | ||||
| (defvar exwm-workspace--switch-history-outdated) | ||||
| (defvar exwm-workspace-current-index) | ||||
| (defvar exwm-workspace--minibuffer) | ||||
| 
 | ||||
| (defun exwm-input--update-focus () | ||||
|   "Update input focus." | ||||
|   (when (window-live-p exwm-input--focus-window) | ||||
|  | @ -158,6 +162,11 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") | |||
|       (setq exwm-input--temp-line-mode nil) | ||||
|       (exwm-input--release-keyboard)))) | ||||
| 
 | ||||
| (declare-function exwm-floating--start-moveresize "exwm-floating.el" | ||||
|                   (id &optional type)) | ||||
| 
 | ||||
| (defvar exwm-workspace--list) | ||||
| 
 | ||||
| (defun exwm-input--on-ButtonPress (data _synthetic) | ||||
|   "Handle ButtonPress event." | ||||
|   (let ((obj (make-instance 'xcb:ButtonPress)) | ||||
|  | @ -262,6 +271,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") | |||
| 
 | ||||
| (defun exwm-input-set-key (key command) | ||||
|   "Set a global key binding." | ||||
|   (interactive "KSet key globally: \nCSet key %s to command: ") | ||||
|   (global-set-key key command) | ||||
|   (cl-pushnew key exwm-input--global-keys)) | ||||
| 
 | ||||
|  | @ -273,7 +283,6 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") | |||
| (defvar exwm-input--during-command nil | ||||
|   "Indicate whether between `pre-command-hook' and `post-command-hook'.") | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-input--on-KeyPress-line-mode (key-press) | ||||
|   "Parse X KeyPress event to Emacs key event and then feed the command loop." | ||||
|   (with-slots (detail state) key-press | ||||
|  | @ -443,12 +452,13 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") | |||
| (defun exwm-input-set-simulation-keys (simulation-keys) | ||||
|   "Set simulation keys. | ||||
| 
 | ||||
| SIMULATION-KEYS is a list of alist (key-sequence1 . key-sequence2)." | ||||
| SIMULATION-KEYS is an alist of the form (original-key . simulated-key)." | ||||
|   (setq exwm-input--simulation-keys nil) | ||||
|   (dolist (i simulation-keys) | ||||
|     (cl-pushnew `(,(vconcat (car i)) . ,(cdr i)) exwm-input--simulation-keys)) | ||||
|   (exwm-input--update-simulation-prefix-keys)) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-input-send-simulation-key (times) | ||||
|   "Fake a key event according to last input key sequence." | ||||
|   (interactive "p") | ||||
|  | @ -461,6 +471,11 @@ SIMULATION-KEYS is a list of alist (key-sequence1 . key-sequence2)." | |||
|         (dolist (j pair) | ||||
|           (exwm-input--fake-key j)))))) | ||||
| 
 | ||||
| (declare-function exwm-floating--stop-moveresize "exwm-floating.el" | ||||
|                   (&rest _args)) | ||||
| (declare-function exwm-floating--do-moveresize "exwm-floating.el" | ||||
|                   (data _synthetic)) | ||||
| 
 | ||||
| (defun exwm-input--init () | ||||
|   "Initialize the keyboard module." | ||||
|   ;; Refresh keyboard mapping | ||||
|  |  | |||
|  | @ -26,7 +26,6 @@ | |||
| ;;; Code: | ||||
| 
 | ||||
| (require 'exwm-core) | ||||
| (eval-when-compile (require 'exwm-workspace)) | ||||
| 
 | ||||
| (defvar exwm-floating-border-width) | ||||
| 
 | ||||
|  | @ -51,7 +50,6 @@ | |||
|                                              xcb:ConfigWindow:Height)) | ||||
|                        :width width :height height)))) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-layout--show (id &optional window) | ||||
|   "Show window ID exactly fit in the Emacs window WINDOW." | ||||
|   (exwm--log "Show #x%x in %s" id window) | ||||
|  | @ -112,7 +110,6 @@ | |||
|                                exwm--connection)))) | ||||
|   (xcb:flush exwm--connection)) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-layout--hide (id) | ||||
|   "Hide window ID." | ||||
|   (unless (eq xcb:icccm:WM_STATE:IconicState ;already hidden | ||||
|  | @ -137,6 +134,9 @@ | |||
|                        :icon xcb:Window:None)) | ||||
|     (xcb:flush exwm--connection))) | ||||
| 
 | ||||
| (defvar exwm-workspace--current) | ||||
| (defvar exwm-workspace--list) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-layout-set-fullscreen (&optional id) | ||||
|   "Make window ID fullscreen." | ||||
|  | @ -153,9 +153,8 @@ | |||
|               (vector (slot-value geometry 'x) (slot-value geometry 'y)))) | ||||
|       (xcb:flush exwm--connection)) | ||||
|     (exwm-layout--resize-container exwm--id exwm--container 0 0 | ||||
|                                    (frame-pixel-width exwm-workspace--current) | ||||
|                                    (frame-pixel-height | ||||
|                                     exwm-workspace--current)) | ||||
|                                    (exwm-workspace--current-width) | ||||
|                                    (exwm-workspace--current-height)) | ||||
|     (xcb:+request exwm--connection | ||||
|         (make-instance 'xcb:ewmh:set-_NET_WM_STATE | ||||
|                        :window exwm--id | ||||
|  | @ -164,6 +163,7 @@ | |||
|     (setq exwm--fullscreen t) | ||||
|     (exwm-input-release-keyboard))) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-layout-unset-fullscreen (&optional id) | ||||
|   "Restore window from fullscreen state." | ||||
|   (interactive) | ||||
|  | @ -187,6 +187,9 @@ | |||
|     (setq exwm--fullscreen nil) | ||||
|     (exwm-input-grab-keyboard))) | ||||
| 
 | ||||
| (defvar exwm-layout--fullscreen-frame-count 0 | ||||
|   "Count the fullscreen workspace frames.") | ||||
| 
 | ||||
| ;; This function is superficially similar to `exwm-layout-set-fullscreen', but | ||||
| ;; they do very different things: `exwm-layout--set-frame-fullscreen' resizes a | ||||
| ;; frame to the actual monitor size, `exwm-layout-set-fullscreen' resizes an X | ||||
|  | @ -207,7 +210,8 @@ | |||
|                  (exwm-workspace--minibuffer-own-frame-p)) | ||||
|         (exwm-workspace--resize-minibuffer-frame width height)) | ||||
|       (exwm-layout--resize-container id workspace x y width height) | ||||
|       (xcb:flush exwm--connection)))) | ||||
|       (xcb:flush exwm--connection))) | ||||
|   (cl-incf exwm-layout--fullscreen-frame-count)) | ||||
| 
 | ||||
| (defvar exwm-layout-show-all-buffers nil | ||||
|   "Non-nil to allow switching to buffers on other workspaces.") | ||||
|  | @ -297,6 +301,7 @@ | |||
|         (exwm-layout--refresh) | ||||
|       (run-with-idle-timer 0.01 nil #'exwm-layout--refresh)))) ;FIXME | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-layout-enlarge-window (delta &optional horizontal) | ||||
|   "Make the selected window DELTA pixels taller. | ||||
| 
 | ||||
|  | @ -371,6 +376,7 @@ windows." | |||
|                            :height height)) | ||||
|         (xcb:flush exwm--connection)))))) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-layout-enlarge-window-horizontally (delta) | ||||
|   "Make the selected window DELTA pixels wider. | ||||
| 
 | ||||
|  | @ -378,6 +384,7 @@ See also `exwm-layout-enlarge-window'." | |||
|   (interactive "p") | ||||
|   (exwm-layout-enlarge-window delta t)) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-layout-shrink-window (delta) | ||||
|   "Make the selected window DELTA pixels lower. | ||||
| 
 | ||||
|  | @ -385,6 +392,7 @@ See also `exwm-layout-enlarge-window'." | |||
|   (interactive "p") | ||||
|   (exwm-layout-enlarge-window (- delta))) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-layout-shrink-window-horizontally (delta) | ||||
|   "Make the selected window DELTA pixels narrower. | ||||
| 
 | ||||
|  | @ -392,6 +400,7 @@ See also `exwm-layout-enlarge-window'." | |||
|   (interactive "p") | ||||
|   (exwm-layout-enlarge-window (- delta) t)) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-layout-hide-mode-line () | ||||
|   "Hide mode-line." | ||||
|   (interactive) | ||||
|  | @ -409,6 +418,7 @@ See also `exwm-layout-enlarge-window'." | |||
|                              mode-line-height) | ||||
|                           nil t))))) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-layout-show-mode-line () | ||||
|   "Show mode-line." | ||||
|   (interactive) | ||||
|  |  | |||
|  | @ -27,7 +27,6 @@ | |||
| ;;; Code: | ||||
| 
 | ||||
| (require 'exwm-core) | ||||
| (eval-when-compile (require 'exwm-workspace)) | ||||
| 
 | ||||
| (defvar exwm-manage-finish-hook nil | ||||
|   "Normal hook run after a window is just managed, in the context of the | ||||
|  | @ -59,6 +58,20 @@ corresponding buffer.") | |||
|         (when reply | ||||
|           (setq exwm--mwm-hints (append (slot-value reply 'value) nil))))))) | ||||
| 
 | ||||
| (defvar exwm-workspace--current) | ||||
| (defvar exwm-workspace--switch-history-outdated) | ||||
| 
 | ||||
| (declare-function exwm--update-window-type "exwm.el" (id &optional force)) | ||||
| (declare-function exwm--update-class "exwm.el" (id &optional force)) | ||||
| (declare-function exwm--update-transient-for "exwm.el" (id &optional force)) | ||||
| (declare-function exwm--update-normal-hints "exwm.el" (id &optional force)) | ||||
| (declare-function exwm--update-title "exwm.el" (id)) | ||||
| (declare-function exwm--update-hints "exwm.el" (id &optional force)) | ||||
| (declare-function exwm--update-protocols "exwm.el" (id &optional force)) | ||||
| (declare-function exwm--update-state "exwm.el" (id &optional force)) | ||||
| (declare-function exwm-floating--set-floating "exwm-floating.el" (id)) | ||||
| (declare-function exwm-floating--unset-floating "exwm-floating.el" (id)) | ||||
| 
 | ||||
| (defun exwm-manage--manage-window (id) | ||||
|   "Manage window ID." | ||||
|   (exwm--log "Try to manage #x%x" id) | ||||
|  | @ -130,12 +143,9 @@ corresponding buffer.") | |||
|                                :value-mask (eval-when-compile | ||||
|                                              (logior xcb:ConfigWindow:X | ||||
|                                                      xcb:ConfigWindow:Y)) | ||||
|                                :x (/ (- (frame-pixel-width | ||||
|                                          exwm-workspace--current) | ||||
|                                         width) | ||||
|                                :x (/ (- (exwm-workspace--current-width) width) | ||||
|                                      2) | ||||
|                                :y (/ (- (frame-pixel-height | ||||
|                                          exwm-workspace--current) | ||||
|                                :y (/ (- (exwm-workspace--current-height) | ||||
|                                         height) | ||||
|                                      2))))) | ||||
|         (xcb:flush exwm--connection) | ||||
|  | @ -200,7 +210,6 @@ corresponding buffer.") | |||
|       (with-current-buffer (exwm--id->buffer id) | ||||
|         (run-hooks 'exwm-manage-finish-hook))))) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-manage--unmanage-window (id &optional withdraw-only) | ||||
|   "Unmanage window ID." | ||||
|   (let ((buffer (exwm--id->buffer id))) | ||||
|  | @ -284,7 +293,6 @@ corresponding buffer.") | |||
|   "Non-nil indicates EXWM is pinging a window.") | ||||
| (defvar exwm-manage-ping-timeout 3 "Seconds to wait before killing a client.") | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-manage--kill-buffer-query-function () | ||||
|   "Run in `kill-buffer-query-functions'." | ||||
|   (catch 'return | ||||
|  | @ -359,7 +367,6 @@ Would you like to kill it? " | |||
| 
 | ||||
| (defun exwm-manage--kill-client (&optional id) | ||||
|   "Kill an X client." | ||||
|   (interactive) | ||||
|   (unless id (setq id (exwm--buffer->id (current-buffer)))) | ||||
|   (let* ((response (xcb:+request-unchecked+reply exwm--connection | ||||
|                        (make-instance 'xcb:ewmh:get-_NET_WM_PID :window id))) | ||||
|  | @ -390,8 +397,8 @@ Would you like to kill it? " | |||
|             (setq edges | ||||
|                   (if exwm--fullscreen | ||||
|                       (list 0 0 | ||||
|                             (frame-pixel-width exwm-workspace--current) | ||||
|                             (frame-pixel-height exwm-workspace--current)) | ||||
|                             (exwm-workspace--current-width) | ||||
|                             (exwm-workspace--current-height)) | ||||
|                     (window-inside-absolute-pixel-edges | ||||
|                      (get-buffer-window buffer t)))) | ||||
|             (exwm--log "Reply with ConfigureNotify (edges): %s" edges) | ||||
|  |  | |||
|  | @ -48,11 +48,19 @@ | |||
| 
 | ||||
| (require 'xcb-randr) | ||||
| (require 'exwm-core) | ||||
| (require 'exwm-layout) | ||||
| (eval-when-compile (require 'exwm-workspace)) | ||||
| 
 | ||||
| (defvar exwm-randr-workspace-output-plist nil) | ||||
| 
 | ||||
| (defvar exwm-randr-refresh-hook nil | ||||
|   "Normal hook run when the RandR module just refreshed.") | ||||
| 
 | ||||
| (defvar exwm-workspace-minibuffer-position) | ||||
| (defvar exwm-layout--fullscreen-frame-count) | ||||
| (defvar exwm-workspace-number) | ||||
| (defvar exwm-workspace--list) | ||||
| 
 | ||||
| (declare-function exwm-layout--set-frame-fullscreen "exwm-layout.el" (frame)) | ||||
| 
 | ||||
| (defun exwm-randr--refresh () | ||||
|   "Refresh workspaces according to the updated RandR info." | ||||
|   (let (output-name geometry output-plist default-geometry workareas | ||||
|  | @ -89,6 +97,7 @@ | |||
|       (setq workarea-offset (if exwm-workspace-minibuffer-position | ||||
|                                 0 | ||||
|                               (window-pixel-height (minibuffer-window)))) | ||||
|       (setq exwm-layout--fullscreen-frame-count 0) | ||||
|       (dotimes (i exwm-workspace-number) | ||||
|         (let* ((output (plist-get exwm-randr-workspace-output-plist i)) | ||||
|                (geometry (lax-plist-get output-plist output)) | ||||
|  | @ -98,15 +107,8 @@ | |||
|                   output nil)) | ||||
|           (set-frame-parameter frame 'exwm-randr-output output) | ||||
|           (set-frame-parameter frame 'exwm-geometry geometry) | ||||
|           (exwm-layout--set-frame-fullscreen frame) | ||||
|           (with-slots (x y width height) geometry | ||||
|             (exwm-layout--resize-container (frame-parameter frame | ||||
|                                                             'exwm-outer-id) | ||||
|                                            (frame-parameter frame | ||||
|                                                             'exwm-workspace) | ||||
|                                            x y width height) | ||||
|             (when (and (eq frame exwm-workspace--current) | ||||
|                        (exwm-workspace--minibuffer-own-frame-p)) | ||||
|               (exwm-workspace--resize-minibuffer-frame width height)) | ||||
|             (setq workareas | ||||
|                   (nconc workareas (list x y width (- height | ||||
|                                                       workarea-offset))) | ||||
|  | @ -120,7 +122,8 @@ | |||
|           (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT | ||||
|                          :window exwm--root | ||||
|                          :data (vconcat viewports))) | ||||
|       (xcb:flush exwm--connection)))) | ||||
|       (xcb:flush exwm--connection) | ||||
|       (run-hooks 'exwm-randr-refresh-hook)))) | ||||
| 
 | ||||
| (defvar exwm-randr-screen-change-hook nil | ||||
|   "Normal hook run when screen changes.") | ||||
|  |  | |||
							
								
								
									
										388
									
								
								exwm-systemtray.el
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										388
									
								
								exwm-systemtray.el
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,388 @@ | |||
| ;;; exwm-systemtray.el --- System Tray Module for  -*- lexical-binding: t -*- | ||||
| ;;;                        EXWM | ||||
| 
 | ||||
| ;; Copyright (C) 2016 Free Software Foundation, Inc. | ||||
| 
 | ||||
| ;; Author: Chris Feng <chris.w.feng@gmail.com> | ||||
| 
 | ||||
| ;; This file is part of GNU Emacs. | ||||
| 
 | ||||
| ;; GNU Emacs is free software: you can redistribute it and/or modify | ||||
| ;; it under the terms of the GNU General Public License as published by | ||||
| ;; the Free Software Foundation, either version 3 of the License, or | ||||
| ;; (at your option) any later version. | ||||
| 
 | ||||
| ;; GNU Emacs is distributed in the hope that it will be useful, | ||||
| ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| ;; GNU General Public License for more details. | ||||
| 
 | ||||
| ;; You should have received a copy of the GNU General Public License | ||||
| ;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| ;;; Commentary: | ||||
| 
 | ||||
| ;; This module adds system tray support for EXWM. | ||||
| 
 | ||||
| ;; To use this module, load and enable it as follows: | ||||
| ;;   (require 'exwm-systemtray) | ||||
| ;;   (exwm-systemtray-enable) | ||||
| 
 | ||||
| ;;; Code: | ||||
| 
 | ||||
| (require 'xcb-xembed) | ||||
| (require 'xcb-systemtray) | ||||
| (require 'exwm-core) | ||||
| 
 | ||||
| (defclass exwm-systemtray--icon () | ||||
|   ((width :initarg :width) | ||||
|    (height :initarg :height) | ||||
|    (visible :initarg :visible)) | ||||
|   :documentation "Attributes of a system tray icon.") | ||||
| 
 | ||||
| ;; GTK icons require at least 16 pixels to show normally. | ||||
| (defconst exwm-systemtray--icon-min-size 16 "Minimum icon size.") | ||||
| 
 | ||||
| (defvar exwm-systemtray-height (max exwm-systemtray--icon-min-size | ||||
|                                     (line-pixel-height)) | ||||
|   "System tray height. | ||||
| 
 | ||||
| You shall use the default value if using auto-hide minibuffer.") | ||||
| 
 | ||||
| (defvar exwm-systemtray-icon-gap 2 "Gap between icons.") | ||||
| 
 | ||||
| (defvar exwm-systemtray--connection nil "The X connection.") | ||||
| (defvar exwm-systemtray--list nil "The icon list.") | ||||
| (defvar exwm-systemtray--selection-owner-window nil | ||||
|   "The selection owner window.") | ||||
| (defvar exwm-systemtray--embedder nil "The embedder window.") | ||||
| 
 | ||||
| (defun exwm-systemtray--embed (icon) | ||||
|   "Embed an icon." | ||||
|   (exwm--log "(System Tray) Try to embed #x%x" icon) | ||||
|   (let ((info (xcb:+request-unchecked+reply exwm-systemtray--connection | ||||
|                   (make-instance 'xcb:xembed:get-_XEMBED_INFO | ||||
|                                  :window icon))) | ||||
|         width* height* visible) | ||||
|     (when info | ||||
|       (exwm--log "(System Tray) Embed #x%x" icon) | ||||
|       (with-slots (width height) | ||||
|           (xcb:+request-unchecked+reply exwm-systemtray--connection | ||||
|               (make-instance 'xcb:GetGeometry :drawable icon)) | ||||
|         (setq height* exwm-systemtray-height | ||||
|               width* (round (* width (/ (float height*) height)))) | ||||
|         (when (< width* exwm-systemtray--icon-min-size) | ||||
|           (setq width* exwm-systemtray--icon-min-size | ||||
|                 height* (round (* height (/ (float width*) width))))) | ||||
|         (exwm--log "(System Tray) Resize from %dx%d to %dx%d" | ||||
|                    width height width* height*)) | ||||
|       ;; Reparent to the embedder. | ||||
|       (xcb:+request exwm-systemtray--connection | ||||
|           (make-instance 'xcb:ReparentWindow | ||||
|                          :window icon | ||||
|                          :parent exwm-systemtray--embedder | ||||
|                          :x 0 | ||||
|                          ;; Vertically centered. | ||||
|                          :y (/ (- exwm-systemtray-height height*) 2))) | ||||
|       ;; Resize the icon. | ||||
|       (xcb:+request exwm-systemtray--connection | ||||
|           (make-instance 'xcb:ConfigureWindow | ||||
|                          :window icon | ||||
|                          :value-mask (logior xcb:ConfigWindow:Width | ||||
|                                              xcb:ConfigWindow:Height | ||||
|                                              xcb:ConfigWindow:BorderWidth) | ||||
|                          :width width* | ||||
|                          :height height* | ||||
|                          :border-width 0)) | ||||
|       ;; Set event mask. | ||||
|       (xcb:+request exwm-systemtray--connection | ||||
|           (make-instance 'xcb:ChangeWindowAttributes | ||||
|                          :window icon | ||||
|                          :value-mask xcb:CW:EventMask | ||||
|                          :event-mask (logior xcb:EventMask:ResizeRedirect | ||||
|                                              xcb:EventMask:PropertyChange))) | ||||
|       (setq visible (slot-value info 'flags)) | ||||
|       (if visible | ||||
|           (setq visible | ||||
|                 (/= 0 (logand (slot-value info 'flags) xcb:xembed:MAPPED))) | ||||
|         ;; Default to visible. | ||||
|         (setq visible t)) | ||||
|       (when visible | ||||
|         (exwm--log "(System Tray) Map the window") | ||||
|         (xcb:+request exwm-systemtray--connection | ||||
|             (make-instance 'xcb:MapWindow :window icon))) | ||||
|       (xcb:+request exwm-systemtray--connection | ||||
|           (make-instance 'xcb:xembed:SendEvent | ||||
|                          :destination icon | ||||
|                          :event | ||||
|                          (xcb:marshal | ||||
|                           (make-instance 'xcb:xembed:EMBEDDED-NOTIFY | ||||
|                                          :window icon | ||||
|                                          :time xcb:Time:CurrentTime | ||||
|                                          :embedder exwm-systemtray--embedder | ||||
|                                          :version 0) | ||||
|                           exwm-systemtray--connection))) | ||||
|       (push `(,icon . ,(make-instance 'exwm-systemtray--icon | ||||
|                                       :width width* | ||||
|                                       :height height* | ||||
|                                       :visible visible)) | ||||
|             exwm-systemtray--list) | ||||
|       (exwm-systemtray--refresh)))) | ||||
| 
 | ||||
| (defun exwm-systemtray--unembed (icon) | ||||
|   "Unembed an icon." | ||||
|   (exwm--log "(System Tray) Unembed #x%x" icon) | ||||
|   (xcb:+request exwm-systemtray--connection | ||||
|       (make-instance 'xcb:UnmapWindow :window icon)) | ||||
|   (xcb:+request exwm-systemtray--connection | ||||
|       (make-instance 'xcb:ReparentWindow | ||||
|                      :window icon | ||||
|                      :parent exwm--root | ||||
|                      :x 0 :y 0)) | ||||
|   (setq exwm-systemtray--list | ||||
|         (assq-delete-all icon exwm-systemtray--list)) | ||||
|   (exwm-systemtray--refresh)) | ||||
| 
 | ||||
| (defun exwm-systemtray--refresh () | ||||
|   "Refresh the system tray." | ||||
|   ;; Make sure to redraw the embedder. | ||||
|   (xcb:+request exwm-systemtray--connection | ||||
|       (make-instance 'xcb:UnmapWindow :window exwm-systemtray--embedder)) | ||||
|   (let ((x exwm-systemtray-icon-gap) | ||||
|         map) | ||||
|     (dolist (pair exwm-systemtray--list) | ||||
|       (when (slot-value (cdr pair) 'visible) | ||||
|         (xcb:+request exwm-systemtray--connection | ||||
|             (make-instance 'xcb:ConfigureWindow | ||||
|                            :window (car pair) | ||||
|                            :value-mask xcb:ConfigWindow:X | ||||
|                            :x x)) | ||||
|         (setq x (+ x (slot-value (cdr pair) 'width) | ||||
|                    exwm-systemtray-icon-gap)) | ||||
|         (setq map t))) | ||||
|     (xcb:+request exwm-systemtray--connection | ||||
|         (make-instance 'xcb:ConfigureWindow | ||||
|                        :window exwm-systemtray--embedder | ||||
|                        :value-mask (logior xcb:ConfigWindow:X | ||||
|                                            xcb:ConfigWindow:Width) | ||||
|                        :x (- (exwm-workspace--current-width) x) | ||||
|                        :width x)) | ||||
|     (when map | ||||
|       (xcb:+request exwm-systemtray--connection | ||||
|           (make-instance 'xcb:MapWindow :window exwm-systemtray--embedder)))) | ||||
|   (xcb:flush exwm-systemtray--connection)) | ||||
| 
 | ||||
| (defun exwm-systemtray--on-DestroyNotify (data _synthetic) | ||||
|   "Unembed icons on DestroyNotify." | ||||
|   (let ((obj (make-instance 'xcb:DestroyNotify))) | ||||
|     (xcb:unmarshal obj data) | ||||
|     (with-slots (window) obj | ||||
|       (when (assoc window exwm-systemtray--list) | ||||
|         (exwm-systemtray--unembed window))))) | ||||
| 
 | ||||
| (defun exwm-systemtray--on-ReparentNotify (data _synthetic) | ||||
|   "Unembed icons on ReparentNotify." | ||||
|   (let ((obj (make-instance 'xcb:ReparentNotify))) | ||||
|     (xcb:unmarshal obj data) | ||||
|     (with-slots (window parent) obj | ||||
|       (when (and (/= parent exwm-systemtray--embedder) | ||||
|                  (assoc window exwm-systemtray--list)) | ||||
|         (exwm-systemtray--unembed window))))) | ||||
| 
 | ||||
| (defun exwm-systemtray--on-ResizeRequest (data _synthetic) | ||||
|   "Resize the tray icon on ResizeRequest." | ||||
|   (let ((obj (make-instance 'xcb:ResizeRequest)) | ||||
|         attr) | ||||
|     (xcb:unmarshal obj data) | ||||
|     (with-slots (window width height) obj | ||||
|       (when (setq attr (cdr (assoc window exwm-systemtray--list))) | ||||
|         (with-slots ((width* width) | ||||
|                      (height* height)) | ||||
|             attr | ||||
|           (setq height* exwm-systemtray-height | ||||
|                 width* (round (* width (/ (float height*) height)))) | ||||
|           (when (< width* exwm-systemtray--icon-min-size) | ||||
|             (setq width* exwm-systemtray--icon-min-size | ||||
|                   height* (round (* height (/ (float width*) width))))) | ||||
|           (xcb:+request exwm-systemtray--connection | ||||
|               (make-instance 'xcb:ConfigureWindow | ||||
|                              :window window | ||||
|                              :value-mask (logior xcb:ConfigWindow:Y | ||||
|                                                  xcb:ConfigWindow:Width | ||||
|                                                  xcb:ConfigWindow:Height) | ||||
|                              ;; Vertically centered. | ||||
|                              :y (/ (- exwm-systemtray-height height*) 2) | ||||
|                              :width width* | ||||
|                              :height height*))) | ||||
|         (exwm-systemtray--refresh))))) | ||||
| 
 | ||||
| (defun exwm-systemtray--on-PropertyNotify (data _synthetic) | ||||
|   "Map/Unmap the tray icon on PropertyNotify." | ||||
|   (let ((obj (make-instance 'xcb:PropertyNotify)) | ||||
|         attr info visible) | ||||
|     (xcb:unmarshal obj data) | ||||
|     (with-slots (window atom state) obj | ||||
|       (when (and (eq state xcb:Property:NewValue) | ||||
|                  (eq atom xcb:Atom:_XEMBED_INFO) | ||||
|                  (setq attr (cdr (assoc window exwm-systemtray--list)))) | ||||
|         (setq info (xcb:+request-unchecked+reply exwm-systemtray--connection | ||||
|                        (make-instance 'xcb:xembed:get-_XEMBED_INFO | ||||
|                                       :window window))) | ||||
|         (when info | ||||
|           (setq visible (/= 0 (logand (slot-value info 'flags) | ||||
|                                       xcb:xembed:MAPPED))) | ||||
|           (exwm--log "(System Tray) #x%x visible? %s" window visible) | ||||
|           (if visible | ||||
|               (xcb:+request exwm-systemtray--connection | ||||
|                   (make-instance 'xcb:MapWindow :window window)) | ||||
|             (xcb:+request exwm-systemtray--connection | ||||
|                 (make-instance 'xcb:UnmapWindow :window window))) | ||||
|           (setf (slot-value attr 'visible) visible) | ||||
|           (exwm-systemtray--refresh)))))) | ||||
| 
 | ||||
| (defun exwm-systemtray--on-ClientMessage (data _synthetic) | ||||
|   "Handle client messages." | ||||
|   (let ((obj (make-instance 'xcb:ClientMessage)) | ||||
|         opcode data32) | ||||
|     (xcb:unmarshal obj data) | ||||
|     (with-slots (window type data) obj | ||||
|       (when (eq type xcb:Atom:_NET_SYSTEM_TRAY_OPCODE) | ||||
|         (setq data32 (slot-value data 'data32) | ||||
|               opcode (elt data32 1)) | ||||
|         (cond ((= opcode xcb:systemtray:opcode:REQUEST-DOCK) | ||||
|                (unless (assoc (elt data32 2) exwm-systemtray--list) | ||||
|                  (exwm-systemtray--embed (elt data32 2)))) | ||||
|               ;; Not implemented (rarely used nowadays). | ||||
|               ((or (= opcode xcb:systemtray:opcode:BEGIN-MESSAGE) | ||||
|                    (= opcode xcb:systemtray:opcode:CANCEL-MESSAGE))) | ||||
|               (t | ||||
|                (exwm--log "(System Tray) Unknown opcode message: %s" obj))))))) | ||||
| 
 | ||||
| (defvar exwm-workspace-minibuffer-position) | ||||
| (defvar exwm-workspace--current) | ||||
| 
 | ||||
| (defun exwm-systemtray--on-workspace-switch () | ||||
|   "Reparent/Refresh the system tray in `exwm-workspace-switch-hook'." | ||||
|   (unless exwm-workspace-minibuffer-position | ||||
|     (xcb:+request exwm-systemtray--connection | ||||
|         (make-instance 'xcb:ReparentWindow | ||||
|                        :window exwm-systemtray--embedder | ||||
|                        :parent (string-to-number | ||||
|                                 (frame-parameter exwm-workspace--current | ||||
|                                                  'window-id)) | ||||
|                        :x 0 | ||||
|                        :y (- (exwm-workspace--current-height) | ||||
|                              exwm-systemtray-height)))) | ||||
|   (exwm-systemtray--refresh)) | ||||
| 
 | ||||
| (defun exwm-systemtray--on-randr-refresh () | ||||
|   "Reposition/Refresh the system tray in `exwm-randr-refresh-hook'." | ||||
|   (unless exwm-workspace-minibuffer-position | ||||
|     (xcb:+request exwm-systemtray--connection | ||||
|         (make-instance 'xcb:ConfigureWindow | ||||
|                        :window exwm-systemtray--embedder | ||||
|                        :value-mask xcb:ConfigWindow:Y | ||||
|                        :y (- (exwm-workspace--current-height) | ||||
|                              exwm-systemtray-height)))) | ||||
|   (exwm-systemtray--refresh)) | ||||
| 
 | ||||
| (defvar xcb:Atom:_NET_SYSTEM_TRAY_S0) | ||||
| (defvar exwm-workspace--minibuffer) | ||||
| 
 | ||||
| (defun exwm-systemtray--init () | ||||
|   "Initialize system tray module." | ||||
|   (cl-assert (not exwm-systemtray--connection)) | ||||
|   (cl-assert (not exwm-systemtray--list)) | ||||
|   (cl-assert (not exwm-systemtray--selection-owner-window)) | ||||
|   (cl-assert (not exwm-systemtray--embedder)) | ||||
|   ;; Create a new connection. | ||||
|   (setq exwm-systemtray--connection (xcb:connect-to-socket)) | ||||
|   (set-process-query-on-exit-flag (slot-value exwm-systemtray--connection | ||||
|                                               'process) | ||||
|                                   nil) | ||||
|   ;; Initialize XELB modules. | ||||
|   (xcb:xembed:init exwm-systemtray--connection) | ||||
|   (xcb:systemtray:init exwm-systemtray--connection) | ||||
|   ;; Acquire the manager selection _NET_SYSTEM_TRAY_S0. | ||||
|   (with-slots (owner) | ||||
|       (xcb:+request-unchecked+reply exwm-systemtray--connection | ||||
|           (make-instance 'xcb:GetSelectionOwner | ||||
|                          :selection xcb:Atom:_NET_SYSTEM_TRAY_S0)) | ||||
|     (when (/= owner xcb:Window:None) | ||||
|       (error "[EXWM] Other system tray detected"))) | ||||
|   (let ((id (xcb:generate-id exwm-systemtray--connection))) | ||||
|     (setq exwm-systemtray--selection-owner-window id) | ||||
|     (xcb:+request exwm-systemtray--connection | ||||
|         (make-instance 'xcb:CreateWindow | ||||
|                        :depth 0 :wid id :parent exwm--root | ||||
|                        :x 0 :y 0 :width 1 :height 1 | ||||
|                        :border-width 0 :class xcb:WindowClass:InputOnly | ||||
|                        :visual 0 :value-mask xcb:CW:OverrideRedirect | ||||
|                        :override-redirect 1)) | ||||
|     ;; Get the selection ownership. | ||||
|     (xcb:+request exwm-systemtray--connection | ||||
|         (make-instance 'xcb:SetSelectionOwner | ||||
|                        :owner id | ||||
|                        :selection xcb:Atom:_NET_SYSTEM_TRAY_S0 | ||||
|                        :time xcb:Time:CurrentTime)) | ||||
|     ;; Set _NET_WM_NAME. | ||||
|     (xcb:+request exwm-systemtray--connection | ||||
|         (make-instance 'xcb:ewmh:set-_NET_WM_NAME | ||||
|                        :window id :data "EXWM system tray selection owner")) | ||||
|     ;; Set the _NET_SYSTEM_TRAY_ORIENTATION property. | ||||
|     (xcb:+request exwm-systemtray--connection | ||||
|         (make-instance 'xcb:xembed:set-_NET_SYSTEM_TRAY_ORIENTATION | ||||
|                        :window id | ||||
|                        :data xcb:systemtray:ORIENTATION:HORZ))) | ||||
|   ;; Create the embedder. | ||||
|   (let ((id (xcb:generate-id exwm-systemtray--connection)) | ||||
|         parent y) | ||||
|     (setq exwm-systemtray--embedder id) | ||||
|     (xcb:+request exwm-systemtray--connection | ||||
|         (make-instance 'xcb:CreateWindow | ||||
|                        :depth 0 :wid id :parent exwm--root | ||||
|                        :x 0 :y 0 :width 1 :height exwm-systemtray-height | ||||
|                        :border-width 0 :class xcb:WindowClass:CopyFromParent | ||||
|                        :visual 0 :value-mask xcb:CW:EventMask | ||||
|                        :event-mask xcb:EventMask:SubstructureNotify)) | ||||
|     (if exwm-workspace-minibuffer-position | ||||
|         (setq parent (frame-parameter exwm-workspace--minibuffer | ||||
|                                       'exwm-container) | ||||
|               ;; Vertically centered. | ||||
|               y (/ (- (line-pixel-height) exwm-systemtray-height) 2)) | ||||
|       (setq parent (string-to-number (frame-parameter exwm-workspace--current | ||||
|                                                       'window-id)) | ||||
|             ;; Bottom aligned. | ||||
|             y (- (exwm-workspace--current-height) exwm-systemtray-height))) | ||||
|     (xcb:+request exwm-systemtray--connection | ||||
|         (make-instance 'xcb:ReparentWindow | ||||
|                        :window id :parent parent :x 0 :y y)) | ||||
|     ;; Set _NET_WM_NAME. | ||||
|     (xcb:+request exwm-systemtray--connection | ||||
|         (make-instance 'xcb:ewmh:set-_NET_WM_NAME | ||||
|                        :window id :data "EXWM system tray embedder"))) | ||||
|   (xcb:flush exwm-systemtray--connection) | ||||
|   ;; Attach event listeners. | ||||
|   (xcb:+event exwm-systemtray--connection 'xcb:DestroyNotify | ||||
|               #'exwm-systemtray--on-DestroyNotify) | ||||
|   (xcb:+event exwm-systemtray--connection 'xcb:ReparentNotify | ||||
|               #'exwm-systemtray--on-ReparentNotify) | ||||
|   (xcb:+event exwm-systemtray--connection 'xcb:ResizeRequest | ||||
|               #'exwm-systemtray--on-ResizeRequest) | ||||
|   (xcb:+event exwm-systemtray--connection 'xcb:PropertyNotify | ||||
|               #'exwm-systemtray--on-PropertyNotify) | ||||
|   (xcb:+event exwm-systemtray--connection 'xcb:ClientMessage | ||||
|               #'exwm-systemtray--on-ClientMessage) | ||||
|   ;; Add hook to move/reparent the embedder. | ||||
|   (add-hook 'exwm-workspace-switch-hook #'exwm-systemtray--on-workspace-switch) | ||||
|   (add-hook 'exwm-randr-refresh-hook #'exwm-systemtray--on-randr-refresh)) | ||||
| 
 | ||||
| (defun exwm-systemtray-enable () | ||||
|   "Enable system tray support for EXWM." | ||||
|   (add-hook 'exwm-init-hook #'exwm-systemtray--init)) | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| (provide 'exwm-systemtray) | ||||
| 
 | ||||
| ;; exwm-systemtray.el ends here | ||||
|  | @ -23,9 +23,6 @@ | |||
| 
 | ||||
| ;; This module adds workspace support for EXWM. | ||||
| 
 | ||||
| ;; Todo: | ||||
| ;; + Add system tray support. | ||||
| 
 | ||||
| ;;; Code: | ||||
| 
 | ||||
| (require 'exwm-core) | ||||
|  | @ -65,7 +62,6 @@ | |||
| (defvar exwm-workspace--switch-history-outdated nil | ||||
|   "Non-nil to indicate `exwm-workspace--switch-history' is outdated.") | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-workspace--update-switch-history () | ||||
|   "Update the history for switching workspace to reflect the latest status." | ||||
|   (when exwm-workspace--switch-history-outdated | ||||
|  | @ -111,6 +107,22 @@ Value nil means to use the default position which is fixed at bottom, while | |||
| (defvar exwm-workspace--display-echo-area-timer nil | ||||
|   "Timer for auto-hiding echo area.") | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-workspace--current-width () | ||||
|   "Return the width of current workspace." | ||||
|   (let ((geometry (frame-parameter exwm-workspace--current 'exwm-geometry))) | ||||
|     (if geometry | ||||
|         (slot-value geometry 'width) | ||||
|       (x-display-pixel-width)))) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-workspace--current-height () | ||||
|   "Return the height of current workspace." | ||||
|   (let ((geometry (frame-parameter exwm-workspace--current 'exwm-geometry))) | ||||
|     (if geometry | ||||
|         (slot-value geometry 'height) | ||||
|       (x-display-pixel-height)))) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-workspace--minibuffer-own-frame-p () | ||||
|   "Reports whether the minibuffer is displayed in its own frame." | ||||
|  | @ -125,9 +137,9 @@ workspace frame." | |||
|   (cl-assert (exwm-workspace--minibuffer-own-frame-p)) | ||||
|   (let ((y (if (eq exwm-workspace-minibuffer-position 'top) | ||||
|                0 | ||||
|              (- (or height (frame-pixel-height exwm-workspace--current)) | ||||
|              (- (or height (exwm-workspace--current-height)) | ||||
|                 (frame-pixel-height exwm-workspace--minibuffer)))) | ||||
|         (width (or width (frame-pixel-width exwm-workspace--current))) | ||||
|         (width (or width (exwm-workspace--current-width))) | ||||
|         (container (frame-parameter exwm-workspace--minibuffer | ||||
|                                     'exwm-container))) | ||||
|     (xcb:+request exwm--connection | ||||
|  | @ -141,6 +153,9 @@ workspace frame." | |||
|                        :stack-mode xcb:StackMode:Above)) | ||||
|     (set-frame-width exwm-workspace--minibuffer width nil t))) | ||||
| 
 | ||||
| (defvar exwm-workspace-switch-hook nil | ||||
|   "Normal hook run after switching workspace.") | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-workspace-switch (index &optional force) | ||||
|   "Switch to workspace INDEX. Query for INDEX if it's not specified. | ||||
|  | @ -203,7 +218,10 @@ The optional FORCE option is for internal use only." | |||
|         (xcb:+request exwm--connection | ||||
|             (make-instance 'xcb:ewmh:set-_NET_CURRENT_DESKTOP | ||||
|                            :window exwm--root :data index)) | ||||
|         (xcb:flush exwm--connection))))) | ||||
|         (xcb:flush exwm--connection)) | ||||
|       (run-hooks 'exwm-workspace-switch-hook)))) | ||||
| 
 | ||||
| (declare-function exwm-layout--hide "exwm-layout.el" (id)) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-workspace-move-window (index &optional id) | ||||
|  | @ -265,6 +283,7 @@ The optional FORCE option is for internal use only." | |||
|                              (exwm--id->buffer id))))) | ||||
|     (setq exwm-workspace--switch-history-outdated t))) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-workspace-switch-to-buffer () | ||||
|   "Make the current Emacs window display another buffer." | ||||
|   (interactive) | ||||
|  | @ -347,10 +366,10 @@ The optional FORCE option is for internal use only." | |||
|                 window) | ||||
|         (when (and (floatp max-mini-window-height) | ||||
|                    (> height (* max-mini-window-height | ||||
|                                 (frame-pixel-height exwm-workspace--current)))) | ||||
|                                 (exwm-workspace--current-height)))) | ||||
|           (setq height (floor | ||||
|                         (* max-mini-window-height | ||||
|                            (frame-pixel-height exwm-workspace--current)))) | ||||
|                            (exwm-workspace--current-height)))) | ||||
|           (xcb:+request exwm--connection | ||||
|               (make-instance 'xcb:ConfigureWindow | ||||
|                              :window window | ||||
|  | @ -360,7 +379,7 @@ The optional FORCE option is for internal use only." | |||
|             (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))) | ||||
|                 y (- (exwm-workspace--current-height) height))) | ||||
|         (xcb:+request exwm--connection | ||||
|             (make-instance 'xcb:ConfigureWindow | ||||
|                            :window (frame-parameter exwm-workspace--minibuffer | ||||
|  | @ -456,6 +475,8 @@ This functions is modified from `display-buffer-reuse-window' and | |||
|     (cancel-timer exwm-workspace--display-echo-area-timer) | ||||
|     (setq exwm-workspace--display-echo-area-timer nil))) | ||||
| 
 | ||||
| (declare-function exwm-manage--unmanage-window "exwm-manage.el") | ||||
| 
 | ||||
| (defun exwm-workspace--confirm-kill-emacs (prompt) | ||||
|   "Confirm before exiting Emacs." | ||||
|   (when (pcase (length exwm--id-buffer-alist) | ||||
|  | @ -610,13 +631,17 @@ This functions is modified from `display-buffer-reuse-window' and | |||
|   ;; Switch to the first workspace | ||||
|   (exwm-workspace-switch 0 t)) | ||||
| 
 | ||||
| (defvar exwm-layout--fullscreen-frame-count) | ||||
| 
 | ||||
| (defun exwm-workspace--post-init () | ||||
|   "The second stage in the initialization of the workspace module." | ||||
|   ;; Delay making the workspaces fullscreen until Emacs becomes idle | ||||
|   (run-with-idle-timer 0 nil | ||||
|                        (lambda () | ||||
|                          (dolist (i exwm-workspace--list) | ||||
|                            (set-frame-parameter i 'fullscreen 'fullboth))))) | ||||
|   ;; Make the workspaces fullscreen. | ||||
|   (dolist (i exwm-workspace--list) | ||||
|     (set-frame-parameter i 'fullscreen 'fullboth)) | ||||
|   ;; Wait until all workspace frames are resized. | ||||
|   (with-timeout (1) | ||||
|     (while (< exwm-layout--fullscreen-frame-count exwm-workspace-number) | ||||
|       (accept-process-output nil 0.1)))) | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										16
									
								
								exwm.el
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								exwm.el
									
										
									
									
									
								
							|  | @ -30,11 +30,12 @@ | |||
| ;; -------- | ||||
| ;; EXWM (Emacs X Window Manager) is a full-featured tiling X window manager for | ||||
| ;; Emacs built on top of XELB.  It features: | ||||
| ;; + Fully keyboard-driven operation | ||||
| ;; + Fully keyboard-driven operations | ||||
| ;; + Hybrid layout modes (tiling & stacking) | ||||
| ;; + Workspace support | ||||
| ;; + ICCCM/EWMH compliance | ||||
| ;; + Basic RandR support (optional) | ||||
| ;; ++ (Optional) RandR (multi-monitor) support | ||||
| ;; ++ (Optional) system tray | ||||
| 
 | ||||
| ;; Installation & configuration | ||||
| ;; ---------------------------- | ||||
|  | @ -70,6 +71,7 @@ | |||
| (require 'exwm-manage) | ||||
| (require 'exwm-input) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm-reset () | ||||
|   "Reset window to standard state: non-fullscreen, line-mode." | ||||
|   (interactive) | ||||
|  | @ -80,7 +82,6 @@ | |||
|       (exwm-layout--refresh) | ||||
|       (exwm-input-grab-keyboard)))) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm--update-window-type (id &optional force) | ||||
|   "Update _NET_WM_WINDOW_TYPE." | ||||
|   (with-current-buffer (exwm--id->buffer id) | ||||
|  | @ -94,7 +95,6 @@ | |||
| (defvar exwm-update-class-hook nil | ||||
|   "Normal hook run when window class is updated.") | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm--update-class (id &optional force) | ||||
|   "Update WM_CLASS." | ||||
|   (with-current-buffer (exwm--id->buffer id) | ||||
|  | @ -110,7 +110,6 @@ | |||
| (defvar exwm-update-title-hook nil | ||||
|   "Normal hook run when window title is updated.") | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm--update-utf8-title (id &optional force) | ||||
|   "Update _NET_WM_NAME." | ||||
|   (with-current-buffer (exwm--id->buffer id) | ||||
|  | @ -123,7 +122,6 @@ | |||
|             (setq exwm--title-is-utf8 t) | ||||
|             (run-hooks 'exwm-update-title-hook))))))) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm--update-ctext-title (id &optional force) | ||||
|   "Update WM_NAME." | ||||
|   (with-current-buffer (exwm--id->buffer id) | ||||
|  | @ -136,13 +134,11 @@ | |||
|           (when exwm-title | ||||
|             (run-hooks 'exwm-update-title-hook))))))) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm--update-title (id) | ||||
|   "Update _NET_WM_NAME or WM_NAME." | ||||
|   (exwm--update-utf8-title id) | ||||
|   (exwm--update-ctext-title id)) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm--update-transient-for (id &optional force) | ||||
|   "Update WM_TRANSIENT_FOR." | ||||
|   (with-current-buffer (exwm--id->buffer id) | ||||
|  | @ -153,7 +149,6 @@ | |||
|         (when reply                     ;nil when destroyed | ||||
|           (setq exwm-transient-for (slot-value reply 'value))))))) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm--update-normal-hints (id &optional force) | ||||
|   "Update WM_NORMAL_HINTS." | ||||
|   (with-current-buffer (exwm--id->buffer id) | ||||
|  | @ -201,7 +196,6 @@ | |||
|                        (= exwm--normal-hints-min-height | ||||
|                           exwm--normal-hints-max-height))))))))) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm--update-hints (id &optional force) | ||||
|   "Update WM_HINTS." | ||||
|   (with-current-buffer (exwm--id->buffer id) | ||||
|  | @ -221,7 +215,6 @@ | |||
|               (set-frame-parameter exwm--frame 'exwm--urgency t) | ||||
|               (setq exwm-workspace--switch-history-outdated t)))))))) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm--update-protocols (id &optional force) | ||||
|   "Update WM_PROTOCOLS." | ||||
|   (with-current-buffer (exwm--id->buffer id) | ||||
|  | @ -232,7 +225,6 @@ | |||
|         (when reply                     ;nil when destroyed | ||||
|           (setq exwm--protocols (append (slot-value reply 'value) nil))))))) | ||||
| 
 | ||||
| ;;;###autoload | ||||
| (defun exwm--update-state (id &optional force) | ||||
|   "Update WM_STATE." | ||||
|   (with-current-buffer (exwm--id->buffer id) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue