about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--README.md5
-rw-r--r--exwm-core.el7
-rw-r--r--exwm-floating.el17
-rw-r--r--exwm-input.el21
-rw-r--r--exwm-layout.el24
-rw-r--r--exwm-manage.el29
-rw-r--r--exwm-randr.el25
-rw-r--r--exwm-systemtray.el59
-rw-r--r--exwm-workspace.el46
-rw-r--r--exwm.el16
10 files changed, 159 insertions, 90 deletions
diff --git a/README.md b/README.md
index 09fe470d392c..7f918bd2975e 100644
--- a/README.md
+++ b/README.md
@@ -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.
diff --git a/exwm-core.el b/exwm-core.el
index b09ca52c0c5a..4d936ed75217 100644
--- a/exwm-core.el
+++ b/exwm-core.el
@@ -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.
 
diff --git a/exwm-floating.el b/exwm-floating.el
index 82b4487f15c2..209539eb4a6c 100644
--- a/exwm-floating.el
+++ b/exwm-floating.el
@@ -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)
 
+(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
diff --git a/exwm-input.el b/exwm-input.el
index 85be1efb2023..5e078030c2ef 100644
--- a/exwm-input.el
+++ b/exwm-input.el
@@ -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
diff --git a/exwm-layout.el b/exwm-layout.el
index 52a84b0fe1fd..c0f3c6147289 100644
--- a/exwm-layout.el
+++ b/exwm-layout.el
@@ -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)
diff --git a/exwm-manage.el b/exwm-manage.el
index 50784ce3016b..224ee16aa890 100644
--- a/exwm-manage.el
+++ b/exwm-manage.el
@@ -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)
diff --git a/exwm-randr.el b/exwm-randr.el
index 716d5218e257..7f9b443c862b 100644
--- a/exwm-randr.el
+++ b/exwm-randr.el
@@ -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.")
diff --git a/exwm-systemtray.el b/exwm-systemtray.el
index c892fcc1a76a..11d9be647486 100644
--- a/exwm-systemtray.el
+++ b/exwm-systemtray.el
@@ -33,7 +33,6 @@
 (require 'xcb-xembed)
 (require 'xcb-systemtray)
 (require 'exwm-core)
-(require 'exwm-workspace)
 
 (defclass exwm-systemtray--icon ()
   ((width :initarg :width)
@@ -161,7 +160,7 @@ You shall use the default value if using auto-hide minibuffer.")
                        :window exwm-systemtray--embedder
                        :value-mask (logior xcb:ConfigWindow:X
                                            xcb:ConfigWindow:Width)
-                       :x (- (frame-pixel-width exwm-workspace--current) x)
+                       :x (- (exwm-workspace--current-width) x)
                        :width x))
     (when map
       (xcb:+request exwm-systemtray--connection
@@ -173,7 +172,7 @@ You shall use the default value if using auto-hide minibuffer.")
   (let ((obj (make-instance 'xcb:DestroyNotify)))
     (xcb:unmarshal obj data)
     (with-slots (window) obj
-      (when (alist-get window exwm-systemtray--list)
+      (when (assoc window exwm-systemtray--list)
         (exwm-systemtray--unembed window)))))
 
 (defun exwm-systemtray--on-ReparentNotify (data _synthetic)
@@ -182,7 +181,7 @@ You shall use the default value if using auto-hide minibuffer.")
     (xcb:unmarshal obj data)
     (with-slots (window parent) obj
       (when (and (/= parent exwm-systemtray--embedder)
-                 (alist-get window exwm-systemtray--list))
+                 (assoc window exwm-systemtray--list))
         (exwm-systemtray--unembed window)))))
 
 (defun exwm-systemtray--on-ResizeRequest (data _synthetic)
@@ -191,7 +190,7 @@ You shall use the default value if using auto-hide minibuffer.")
         attr)
     (xcb:unmarshal obj data)
     (with-slots (window width height) obj
-      (when (setq attr (alist-get window exwm-systemtray--list))
+      (when (setq attr (cdr (assoc window exwm-systemtray--list)))
         (with-slots ((width* width)
                      (height* height))
             attr
@@ -220,7 +219,7 @@ You shall use the default value if using auto-hide minibuffer.")
     (with-slots (window atom state) obj
       (when (and (eq state xcb:Property:NewValue)
                  (eq atom xcb:Atom:_XEMBED_INFO)
-                 (setq attr (alist-get window exwm-systemtray--list)))
+                 (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)))
@@ -256,20 +255,36 @@ You shall use the default value if using auto-hide minibuffer.")
               (t
                (exwm--log "(System Tray) Unknown opcode message: %s" obj)))))))
 
-(defun exwm-systemtray--on-exwm-workspace-switch ()
-  "Reparent the system tray in `exwm-workspace-switch-hook'."
-  (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 (- (frame-pixel-height exwm-workspace--current)
-                           exwm-systemtray-height)))
+(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."
@@ -335,8 +350,7 @@ You shall use the default value if using auto-hide minibuffer.")
       (setq parent (string-to-number (frame-parameter exwm-workspace--current
                                                       'window-id))
             ;; Bottom aligned.
-            y (- (frame-pixel-height exwm-workspace--current)
-                 exwm-systemtray-height)))
+            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))
@@ -356,10 +370,9 @@ You shall use the default value if using auto-hide minibuffer.")
               #'exwm-systemtray--on-PropertyNotify)
   (xcb:+event exwm-systemtray--connection 'xcb:ClientMessage
               #'exwm-systemtray--on-ClientMessage)
-  ;; Add hook to reparent the embedder.
-  (unless exwm-workspace-minibuffer-position
-    (add-hook 'exwm-workspace-switch-hook
-              #'exwm-systemtray--on-exwm-workspace-switch)))
+  ;; 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."
diff --git a/exwm-workspace.el b/exwm-workspace.el
index e9bab305e321..99e3b5510ea5 100644
--- a/exwm-workspace.el
+++ b/exwm-workspace.el
@@ -62,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
@@ -109,6 +108,22 @@ Value nil means to use the default position which is fixed at bottom, while
   "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."
   (memq exwm-workspace-minibuffer-position '(top bottom)))
@@ -122,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
@@ -206,6 +221,8 @@ The optional FORCE option is for internal use only."
         (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)
   "Move window ID to workspace INDEX."
@@ -266,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)
@@ -348,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
@@ -361,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
@@ -457,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)
@@ -611,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))))
 
 
 
diff --git a/exwm.el b/exwm.el
index 85c905e6efd6..b425acf7b538 100644
--- a/exwm.el
+++ b/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)