about summary refs log tree commit diff
diff options
context:
space:
mode:
authorChris Feng <chris.w.feng@gmail.com>2018-02-18T16·04+0800
committerChris Feng <chris.w.feng@gmail.com>2018-02-18T16·04+0800
commitd22e6740d761bd2c67e928579502a6c2816516a9 (patch)
tree3edd721d4e0d1e3ec7778d30ec0f63fb115ea8fa
parent7823eb988c22f5dc804ef862d91a0fcf474ca718 (diff)
Add customization settings
; Also fix documentations.
-rw-r--r--exwm-cm.el2
-rw-r--r--exwm-core.el52
-rw-r--r--exwm-floating.el73
-rw-r--r--exwm-input.el207
-rw-r--r--exwm-layout.el75
-rw-r--r--exwm-manage.el106
-rw-r--r--exwm-randr.el38
-rw-r--r--exwm-systemtray.el34
-rw-r--r--exwm-workspace.el218
-rw-r--r--exwm.el59
-rw-r--r--xinitrc3
11 files changed, 495 insertions, 372 deletions
diff --git a/exwm-cm.el b/exwm-cm.el
index 77dd2774a11d..ff556fb21921 100644
--- a/exwm-cm.el
+++ b/exwm-cm.el
@@ -21,7 +21,7 @@
 
 ;;; Commentary:
 
-;; This module is obsolete since EXWM now supports third-porty compositors.
+;; This module is obsolete since EXWM now supports third-party compositors.
 
 ;;; Code:
 
diff --git a/exwm-core.el b/exwm-core.el
index 4e9a3899e4df..68ec53b03e4a 100644
--- a/exwm-core.el
+++ b/exwm-core.el
@@ -35,6 +35,32 @@
 (eval-and-compile
   (defvar exwm-debug-on nil "Non-nil to turn on debug for EXWM."))
 
+(defvar exwm--connection nil "X connection.")
+
+(defvar exwm--guide-window nil
+  "An X window separating workspaces and X windows.")
+
+(defvar exwm--id-buffer-alist nil "Alist of (<X window ID> . <Emacs buffer>).")
+
+(defvar exwm--root nil "Root window.")
+
+(defvar exwm-input--global-prefix-keys)
+(defvar exwm-input--simulation-prefix-keys)
+(defvar exwm-input-line-mode-passthrough)
+(defvar exwm-input-prefix-keys)
+(declare-function exwm-input--fake-key "exwm-input.el" (event))
+(declare-function exwm-input--on-KeyPress-line-mode "exwm-input.el"
+                  (key-press raw-data))
+(declare-function exwm-floating-hide "exwm-floating.el")
+(declare-function exwm-floating-toggle-floating "exwm-floating.el")
+(declare-function exwm-input-release-keyboard "exwm-input.el")
+(declare-function exwm-input-send-next-key "exwm-input.el" (times))
+(declare-function exwm-layout-set-fullscreen "exwm-layout.el" (&optional id))
+(declare-function exwm-layout-toggle-mode-line "exwm-layout.el")
+(declare-function exwm-manage--kill-buffer-query-function "exwm-manage.el")
+(declare-function exwm-workspace-move-window "exwm-workspace.el"
+                  (frame-or-index &optional id))
+
 (defmacro exwm--log (format-string &rest args)
   "Print debug message."
   (when exwm-debug-on
@@ -43,12 +69,6 @@
 (defmacro exwm--debug (&rest forms)
   (when exwm-debug-on `(progn ,@forms)))
 
-(defvar exwm--connection nil "X connection.")
-(defvar exwm--root nil "Root window.")
-(defvar exwm--id-buffer-alist nil "Alist of (<X window ID> . <Emacs buffer>).")
-(defvar exwm--guide-window nil
-  "An X window separating workspaces and X windows.")
-
 (defsubst exwm--id->buffer (id)
   "X window ID => Emacs buffer."
   (cdr (assoc id exwm--id-buffer-alist)))
@@ -108,15 +128,6 @@ least SECS seconds later."
                                               xcb:EventMask:EnterWindow 0))
   "Event mask set on all managed windows.")
 
-(defvar exwm-input-line-mode-passthrough)
-(defvar exwm-input--global-prefix-keys)
-(defvar exwm-input-prefix-keys)
-(defvar exwm-input--simulation-prefix-keys)
-
-(declare-function exwm-input--fake-key "exwm-input.el" (event))
-(declare-function exwm-input--on-KeyPress-line-mode "exwm-input.el"
-                  (key-press raw-data))
-
 ;; Internal variables
 (defvar-local exwm--id nil)               ;window ID
 (defvar-local exwm--frame nil)            ;workspace frame
@@ -154,15 +165,6 @@ least SECS seconds later."
 ;; _MOTIF_WM_HINTS
 (defvar-local exwm--mwm-hints-decorations t)
 
-(declare-function exwm-floating-hide "exwm-floating.el")
-(declare-function exwm-floating-toggle-floating "exwm-floating.el")
-(declare-function exwm-input-release-keyboard "exwm-input.el")
-(declare-function exwm-input-send-next-key "exwm-input.el" (times))
-(declare-function exwm-layout-set-fullscreen "exwm-layout.el" (&optional id))
-(declare-function exwm-layout-toggle-mode-line "exwm-layout.el")
-(declare-function exwm-workspace-move-window "exwm-workspace.el"
-                  (frame-or-index &optional id))
-
 (defvar exwm-mode-map
   (let ((map (make-sparse-keymap)))
     (define-key map "\C-c\C-f" #'exwm-layout-set-fullscreen)
@@ -264,8 +266,6 @@ least SECS seconds later."
                    (/= ,i exwm-workspace-current-index)])
                (number-sequence 0 (1- (exwm-workspace--count))))))))
 
-(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 b0afc1dad373..8cd0491beae8 100644
--- a/exwm-floating.el
+++ b/exwm-floating.el
@@ -29,22 +29,35 @@
 (require 'xcb-cursor)
 (require 'exwm-core)
 
-(defvar exwm-floating-border-width 1 "Border width of the floating window.")
-(defvar exwm-floating-border-color "navy"
-  "Border color of the floating window.")
-(defvar exwm-floating--border-pixel nil
-  "Border pixel drawn around floating X windows.")
+(defgroup exwm-floating nil
+  "Floating."
+  :version "25.3"
+  :group 'exwm)
+
+(defcustom exwm-floating-setup-hook nil
+  "Normal hook run when an X window has been made floating, in the
+context of the corresponding buffer."
+  :type 'hook)
+
+(defcustom exwm-floating-exit-hook nil
+  "Normal hook run when an X window has exited floating state, in the
+context of the corresponding buffer."
+  :type 'hook)
+
+(defcustom exwm-floating-border-color "navy"
+  "Border color of floating windows."
+  :type 'color)
+
+(defcustom exwm-floating-border-width 1
+  "Border width of floating windows."
+  :type 'integer)
+
 (defvar exwm-floating--border-colormap nil
   "Colormap used by the border pixel.
 
 This is also used by X window containers.")
-
-(defvar exwm-floating-setup-hook nil
-  "Normal hook run when an X window has been made floating, in the
-context of the corresponding buffer.")
-(defvar exwm-floating-exit-hook nil
-  "Normal hook run when an X window has exited floating state, in the
-context of the corresponding buffer.")
+(defvar exwm-floating--border-pixel nil
+  "Border pixel drawn around floating X windows.")
 
 ;; Cursors for moving/resizing a window
 (defvar exwm-floating--cursor-move nil)
@@ -57,6 +70,18 @@ context of the corresponding buffer.")
 (defvar exwm-floating--cursor-bottom-left nil)
 (defvar exwm-floating--cursor-left nil)
 
+(defvar exwm-floating--moveresize-calculate nil
+  "Calculate move/resize parameters [buffer event-mask x y width height].")
+
+(defvar exwm-workspace--current)
+(defvar exwm-workspace--workareas)
+(declare-function exwm-layout--hide "exwm-layout.el" (id))
+(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id))
+(declare-function exwm-layout--refresh "exwm-layout.el" ())
+(declare-function exwm-layout--show "exwm-layout.el" (id &optional window))
+(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el")
+(declare-function exwm-workspace--position "exwm-workspace.el" (frame))
+
 (defun exwm-floating--set-allowed-actions (id tilling)
   "Set _NET_WM_ALLOWED_ACTIONS."
   (xcb:+request exwm--connection
@@ -74,16 +99,6 @@ context of the corresponding buffer.")
                                      xcb:Atom:_NET_WM_ACTION_CHANGE_DESKTOP
                                      xcb:Atom:_NET_WM_ACTION_CLOSE)))))
 
-(defvar exwm-workspace--current)
-(defvar exwm-workspace--workareas)
-
-(declare-function exwm-layout--refresh "exwm-layout.el" ())
-(declare-function exwm-layout--show "exwm-layout.el" (id &optional window))
-(declare-function exwm-layout--hide "exwm-layout.el" (id))
-(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id))
-(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el")
-(declare-function exwm-workspace--position "exwm-workspace.el" (frame))
-
 (defun exwm-floating--set-floating (id)
   "Make window ID floating."
   (let ((window (get-buffer-window (exwm--id->buffer id))))
@@ -331,16 +346,16 @@ context of the corresponding buffer.")
     (run-hooks 'exwm-floating-exit-hook)))
 
 ;;;###autoload
-(defun exwm-floating-toggle-floating ()
+(cl-defun exwm-floating-toggle-floating ()
   "Toggle the current window between floating and non-floating states."
   (interactive)
+  (unless (derived-mode-p 'exwm-mode)
+    (cl-return-from 'exwm-floating-toggle-floating))
   (with-current-buffer (window-buffer)
     (if exwm--floating-frame
         (exwm-floating--unset-floating exwm--id)
       (exwm-floating--set-floating exwm--id))))
 
-(declare-function exwm-layout--set-state "exwm-layout.el" (id state))
-
 ;;;###autoload
 (defun exwm-floating-hide ()
   "Hide the current floating X window (which would show again when selected)."
@@ -350,14 +365,6 @@ context of the corresponding buffer.")
     (exwm-layout--hide exwm--id)
     (select-frame-set-input-focus exwm-workspace--current)))
 
-(define-obsolete-function-alias 'exwm-floating-hide-mode-line
-  'exwm-layout-hide-mode-line "25.1" "Hide mode-line of a floating frame.")
-(define-obsolete-function-alias 'exwm-floating-show-mode-line
-  'exwm-layout-show-mode-line "25.1" "Show mode-line of a floating frame.")
-
-(defvar exwm-floating--moveresize-calculate nil
-  "Calculate move/resize parameters [buffer event-mask x y width height].")
-
 (defun exwm-floating--start-moveresize (id &optional type)
   "Start move/resize."
   (let ((buffer-or-id (or (exwm--id->buffer id) id))
diff --git a/exwm-input.el b/exwm-input.el
index eaddf6b25268..e536a14144b6 100644
--- a/exwm-input.el
+++ b/exwm-input.el
@@ -38,32 +38,119 @@
 (require 'xcb-keysyms)
 (require 'exwm-core)
 
-(defvar exwm-input-move-event 's-down-mouse-1
-  "Emacs event to start moving a window.")
-(defvar exwm-input-resize-event 's-down-mouse-3
-  "Emacs event to start resizing a window.")
+(defgroup exwm-input nil
+  "Input."
+  :version "25.3"
+  :group 'exwm)
 
-(defvar exwm-input--timestamp-window nil)
-(defvar exwm-input--timestamp-atom nil)
-(defvar exwm-input--timestamp-callback nil)
+(defcustom exwm-input-prefix-keys
+  '(?\C-c ?\C-x ?\C-u ?\C-h ?\M-x ?\M-` ?\M-& ?\M-:)
+  "List of prefix keys EXWM should forward to Emacs when in line-mode."
+  :type '(repeat key-sequence)
+  :get (lambda (symbol)
+         (mapcar #'vector (default-value symbol)))
+  :set (lambda (symbol value)
+         (set symbol (mapcar (lambda (i)
+                               (if (sequencep i)
+                                   (aref i 0)
+                                 i))
+                             value))))
+
+(defcustom exwm-input-move-event 's-down-mouse-1
+  "Emacs event to start moving a window."
+  :type 'key-sequence
+  :get (lambda (symbol)
+         (let ((value (default-value symbol)))
+           (if (mouse-event-p value)
+               value
+             (vector value))))
+  :set (lambda (symbol value)
+         (set symbol (if (sequencep value)
+                         (aref value 0)
+                       value))))
+
+(defcustom exwm-input-resize-event 's-down-mouse-3
+  "Emacs event to start resizing a window."
+  :type 'key-sequence
+  :get (lambda (symbol)
+         (let ((value (default-value symbol)))
+           (if (mouse-event-p value)
+               value
+             (vector value))))
+  :set (lambda (symbol value)
+         (set symbol (if (sequencep value)
+                         (aref value 0)
+                       value))))
+
+(defcustom exwm-input-line-mode-passthrough nil
+  "Non-nil makes 'line-mode' forwards all events to Emacs."
+  :type 'boolean)
 
-(defvar exwm-workspace--current)
-(defvar exwm-workspace--switch-history-outdated)
-(defvar exwm-workspace-current-index)
-(defvar exwm-workspace--minibuffer)
-(defvar exwm-workspace--list)
+;; Input focus update requests should be accumulated for a short time
+;; interval so that only the last one need to be processed.  This not
+;; improves the overall performance, but avoids the problem of input
+;; focus loop, which is a result of the interaction with Emacs frames.
+;;
+;; FIXME: The time interval is hard to decide and perhaps machine-dependent.
+;;        A value too small can cause redundant updates of input focus,
+;;        and even worse, dead loops.  OTOH a large value would bring
+;;        laggy experience.
+(defconst exwm-input--update-focus-interval 0.01
+  "Time interval (in seconds) for accumulating input focus update requests.")
+
+(defvar exwm-input--during-command nil
+  "Indicate whether between `pre-command-hook' and `post-command-hook'.")
 
 (defvar exwm-input--global-keys nil "Global key bindings.")
+
 (defvar exwm-input--global-prefix-keys nil
   "List of prefix keys of global key bindings.")
-(defvar exwm-input-prefix-keys
-  '(?\C-c ?\C-x ?\C-u ?\C-h ?\M-x ?\M-` ?\M-& ?\M-:)
-  "List of prefix keys EXWM should forward to Emacs when in line-mode.")
+
+(defvar exwm-input--line-mode-cache nil "Cache for incomplete key sequence.")
+
+(defvar exwm-input--local-simulation-keys nil
+  "Whether simulation keys are local.")
+
 (defvar exwm-input--simulation-keys nil "Simulation keys in line-mode.")
+
 (defvar exwm-input--simulation-prefix-keys nil
   "List of prefix keys of simulation keys in line-mode.")
 
+(defvar exwm-input--temp-line-mode nil
+  "Non-nil indicates it's in temporary line-mode for char-mode.")
+
+(defvar exwm-input--timestamp-atom nil)
+
+(defvar exwm-input--timestamp-callback nil)
+
+(defvar exwm-input--timestamp-window nil)
+
+(defvar exwm-input--update-focus-defer-timer nil "Timer for polling the lock.")
+
+(defvar exwm-input--update-focus-lock nil
+  "Lock for solving input focus update contention.")
+
+(defvar exwm-input--update-focus-timer nil
+  "Timer for deferring the update of input focus.")
+
+(defvar exwm-input--update-focus-window nil "The (Emacs) window to be focused.
+This value should always be overwritten.")
+
+(defvar exwm-workspace--current)
+(declare-function exwm-floating--do-moveresize "exwm-floating.el"
+                  (data _synthetic))
+(declare-function exwm-floating--start-moveresize "exwm-floating.el"
+                  (id &optional type))
+(declare-function exwm-floating--stop-moveresize "exwm-floating.el"
+                  (&rest _args))
+(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id))
 (declare-function exwm-layout--show "exwm-layout.el" (id &optional window))
+(declare-function exwm-workspace--client-p "exwm-workspace.el"
+                  (&optional frame))
+(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el")
+(declare-function exwm-workspace--workspace-p "exwm-workspace.el" (workspace))
+(declare-function exwm-workspace-switch "exwm-workspace.el"
+                  (frame-or-index &optional force))
 
 (defun exwm-input--set-focus (id)
   "Set input focus to window ID in a proper way."
@@ -185,13 +272,6 @@ ARGS are additional arguments to CALLBACK."
   (let ((exwm-input--global-prefix-keys nil))
     (exwm-input--update-global-prefix-keys)))
 
-(declare-function exwm-workspace--client-p "exwm-workspace.el"
-                  (&optional frame))
-
-(defvar exwm-input--update-focus-window nil "The (Emacs) window to be focused.
-
-This value should always be overwritten.")
-
 (defun exwm-input--on-buffer-list-update ()
   "Run in `buffer-list-update-hook' to track input focus."
   (when (and (not (eq this-command #'handle-switch-frame))
@@ -205,24 +285,6 @@ This value should always be overwritten.")
     (setq exwm-input--update-focus-window (selected-window))
     (exwm-input--update-focus-defer)))
 
-;; Input focus update requests should be accumulated for a short time
-;; interval so that only the last one need to be processed.  This not
-;; improves the overall performance, but avoids the problem of input
-;; focus loop, which is a result of the interaction with Emacs frames.
-;;
-;; FIXME: The time interval is hard to decide and perhaps machine-dependent.
-;;        A value too small can cause redundant updates of input focus,
-;;        and even worse, dead loops.  OTOH a large value would bring
-;;        laggy experience.
-(defconst exwm-input--update-focus-interval 0.01
-  "Time interval (in seconds) for accumulating input focus update requests.")
-
-(defvar exwm-input--update-focus-lock nil
-  "Lock for solving input focus update contention.")
-(defvar exwm-input--update-focus-defer-timer nil "Timer for polling the lock.")
-(defvar exwm-input--update-focus-timer nil
-  "Timer for deferring the update of input focus.")
-
 (defun exwm-input--update-focus-defer ()
   "Defer updating input focus."
   (when exwm-input--update-focus-defer-timer
@@ -240,12 +302,6 @@ This value should always be overwritten.")
                           #'exwm-input--update-focus-commit
                           exwm-input--update-focus-window))))
 
-(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id))
-(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el")
-(declare-function exwm-workspace-switch "exwm-workspace.el"
-                  (frame-or-index &optional force))
-(declare-function exwm-workspace--workspace-p "exwm-workspace.el" (workspace))
-
 (defun exwm-input--update-focus-commit (window)
   "Commit updating input focus."
   (setq exwm-input--update-focus-lock t)
@@ -317,10 +373,6 @@ This value should always be overwritten.")
                      :window exwm--root
                      :data (or id xcb:Window:None))))
 
-(declare-function exwm-floating--start-moveresize "exwm-floating.el"
-                  (id &optional type))
-(declare-function exwm-workspace--position "exwm-workspace.el" (frame))
-
 (defun exwm-input--on-ButtonPress (data _synthetic)
   "Handle ButtonPress event."
   (let ((obj (make-instance 'xcb:ButtonPress))
@@ -419,7 +471,10 @@ This value should always be overwritten.")
 
 ;;;###autoload
 (defun exwm-input-set-key (key command)
-  "Set a global key binding."
+  "Set a global key binding.
+
+The new key binding only takes effect in real time when this command is
+called interactively.  Only invoke it non-interactively in configuration."
   (interactive "KSet key globally: \nCSet key %s to command: ")
   (global-set-key key command)
   (cl-pushnew key exwm-input--global-keys)
@@ -437,22 +492,6 @@ This value should always be overwritten.")
       (setq unread-command-events
             (append unread-command-events `((t . ,event)))))))
 
-(defvar exwm-input-command-whitelist nil
-  "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'.")
-
-(defvar exwm-input-line-mode-passthrough nil
-  "Non-nil makes 'line-mode' forwards all events to Emacs.")
-
-(defvar exwm-input--line-mode-cache nil "Cache for incomplete key sequence.")
-
-(defvar exwm-input--temp-line-mode nil
-  "Non-nil indicates it's in temporary line-mode for char-mode.")
-
 (cl-defun exwm-input--translate (key)
   (let (translation)
     (dolist (map (list input-decode-map
@@ -606,7 +645,8 @@ This value should always be overwritten.")
 ;;;###autoload
 (defun exwm-input-grab-keyboard (&optional id)
   "Switch to line-mode."
-  (interactive (list (exwm--buffer->id (window-buffer))))
+  (interactive (list (when (derived-mode-p 'exwm-mode)
+                       (exwm--buffer->id (window-buffer)))))
   (when id
     (with-current-buffer (exwm--id->buffer id)
       (exwm-input--grab-keyboard id)
@@ -617,7 +657,8 @@ This value should always be overwritten.")
 ;;;###autoload
 (defun exwm-input-release-keyboard (&optional id)
   "Switch to char-mode."
-  (interactive (list (exwm--buffer->id (window-buffer))))
+  (interactive (list (when (derived-mode-p 'exwm-mode)
+                       (exwm--buffer->id (window-buffer)))))
   (when id
     (with-current-buffer (exwm--id->buffer id)
       (exwm-input--release-keyboard id)
@@ -628,7 +669,8 @@ This value should always be overwritten.")
 ;;;###autoload
 (defun exwm-input-toggle-keyboard (&optional id)
   "Toggle between 'line-mode' and 'char-mode'."
-  (interactive (list (exwm--buffer->id (window-buffer))))
+  (interactive (list (when (derived-mode-p 'exwm-mode)
+                       (exwm--buffer->id (window-buffer)))))
   (when id
     (with-current-buffer (exwm--id->buffer id)
       (if exwm--keyboard-grabbed
@@ -664,9 +706,14 @@ This value should always be overwritten.")
     (xcb:flush exwm--connection)))
 
 ;;;###autoload
-(defun exwm-input-send-next-key (times)
-  "Send next key to client window."
+(cl-defun exwm-input-send-next-key (times)
+  "Send next key to client window.
+
+EXWM will prompt for the key to send.  This command can be prefixed to send
+multiple keys."
   (interactive "p")
+  (unless (derived-mode-p 'exwm-mode)
+    (cl-return-from 'exwm-input-send-next-key))
   (when (> times 12) (setq times 12))
   (let (key keys)
     (dotimes (i times)
@@ -686,9 +733,6 @@ This value should always be overwritten.")
 ;;   (unless (listp last-input-event)      ;not a key event
 ;;     (exwm-input--fake-key last-input-event)))
 
-(defvar exwm-input--local-simulation-keys nil
-  "Whether simulation keys are local.")
-
 (defun exwm-input--update-simulation-prefix-keys ()
   "Update the list of prefix keys of simulation keys."
   (setq exwm-input--simulation-prefix-keys nil)
@@ -718,9 +762,13 @@ Its usage is the same with `exwm-input-set-simulation-keys'."
     (exwm-input-set-simulation-keys simulation-keys)))
 
 ;;;###autoload
-(defun exwm-input-send-simulation-key (times)
-  "Fake a key event according to last input key sequence."
+(cl-defun exwm-input-send-simulation-key (times)
+  "Fake a key event according to the last input key sequence.
+
+Sending multiple fake keys at once is only supported by Emacs 27 and later."
   (interactive "p")
+  (unless (derived-mode-p 'exwm-mode)
+    (cl-return-from 'exwm-input-send-simulation-key))
   (let ((pair (assoc (this-single-command-keys) exwm-input--simulation-keys)))
     (when pair
       (setq pair (cdr pair))
@@ -738,11 +786,6 @@ Its usage is the same with `exwm-input-set-simulation-keys'."
   "Run in `post-command-hook'."
   (setq exwm-input--during-command nil))
 
-(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 cda942e47df3..a98e2611173b 100644
--- a/exwm-layout.el
+++ b/exwm-layout.el
@@ -27,8 +27,35 @@
 
 (require 'exwm-core)
 
-(defvar exwm-floating-border-width)
-(defvar exwm-workspace--id-struts-alist)
+(defgroup exwm-layout nil
+  "Layout."
+  :version "25.3"
+  :group 'exwm)
+
+(defcustom exwm-layout-show-all-buffers nil
+  "Non-nil to allow switching to buffers on other workspaces."
+  :type 'boolean)
+
+(defvar exwm-layout--other-buffer-exclude-buffers nil
+  "List of buffers that should not be selected by `other-buffer'.")
+
+(defvar exwm-layout--other-buffer-exclude-exwm-mode-buffers nil
+  "When non-nil, prevent EXWM buffers from being selected by `other-buffer'.")
+
+(defvar exwm-layout--timer nil "Timer used to track echo area changes.")
+
+(defvar exwm-workspace--current)
+(declare-function exwm-input-grab-keyboard "exwm-input.el")
+(declare-function exwm-input-release-keyboard "exwm-input.el")
+(declare-function exwm-workspace--client-p "exwm-workspace.el"
+                  (&optional frame))
+(declare-function exwm-workspace--current-height "exwm-workspace.el")
+(declare-function exwm-workspace--current-width  "exwm-workspace.el")
+(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el")
+(declare-function exwm-workspace--workspace-p "exwm-workspace.el"
+                  (workspace))
+(declare-function exwm-workspace-move-window "exwm-workspace.el"
+                  (frame-or-index &optional id))
 
 (defun exwm-layout--set-state (id state)
   "Set WM_STATE."
@@ -99,23 +126,14 @@
       (exwm-layout--set-state id xcb:icccm:WM_STATE:IconicState)
       (xcb:flush exwm--connection))))
 
-(defvar exwm-workspace--current)
-
-(declare-function exwm-input-grab-keyboard "exwm-input.el")
-(declare-function exwm-input-release-keyboard "exwm-input.el")
-(declare-function exwm-workspace--current-height "exwm-workspace.el")
-(declare-function exwm-workspace--current-width  "exwm-workspace.el")
-(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el")
-(declare-function exwm-workspace-move-window "exwm-workspace.el"
-                  (frame-or-index &optional id))
-
 ;;;###autoload
-(defun exwm-layout-set-fullscreen (&optional id)
+(cl-defun exwm-layout-set-fullscreen (&optional id)
   "Make window ID fullscreen."
   (interactive)
+  (unless (and (or id (derived-mode-p 'exwm-mode))
+               (not (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)))
+    (cl-return-from 'exwm-layout-set-fullscreen))
   (with-current-buffer (if id (exwm--id->buffer id) (window-buffer))
-    (when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)
-      (user-error "Already in full-screen mode"))
     ;; Expand the X window to fill the whole screen.
     ;; Rationale: Floating X windows may not be positioned at (0, 0)
     ;; due to the extra border.
@@ -139,12 +157,13 @@
     (call-interactively #'exwm-input-release-keyboard)))
 
 ;;;###autoload
-(defun exwm-layout-unset-fullscreen (&optional id)
+(cl-defun exwm-layout-unset-fullscreen (&optional id)
   "Restore window from fullscreen state."
   (interactive)
+  (unless (and (or id (derived-mode-p 'exwm-mode))
+               (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state))
+    (cl-return-from 'exwm-layout-unset-fullscreen))
   (with-current-buffer (if id (exwm--id->buffer id) (window-buffer))
-    (unless (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)
-      (user-error "Not in full-screen mode"))
     (if exwm--floating-frame
         (exwm-layout--show exwm--id (frame-root-window exwm--floating-frame))
       (xcb:+request exwm--connection
@@ -165,21 +184,17 @@
     (call-interactively #'exwm-input-grab-keyboard)))
 
 ;;;###autoload
-(defun exwm-layout-toggle-fullscreen (&optional id)
+(cl-defun exwm-layout-toggle-fullscreen (&optional id)
   "Toggle fullscreen mode."
   (interactive (list (exwm--buffer->id (window-buffer))))
+  (unless (or id (derived-mode-p 'exwm-mode))
+    (cl-return-from 'exwm-layout-toggle-fullscreen))
   (when id
     (with-current-buffer (exwm--id->buffer id)
       (if (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)
           (exwm-reset)
         (exwm-layout-set-fullscreen id)))))
 
-(defvar exwm-layout--other-buffer-exclude-exwm-mode-buffers nil
-  "When non-nil, prevent EXWM buffers from being selected by `other-buffer'.")
-
-(defvar exwm-layout--other-buffer-exclude-buffers nil
-  "List of buffers that should not be selected by `other-buffer'.")
-
 (defun exwm-layout--other-buffer-predicate (buffer)
   "Return non-nil when the BUFFER may be displayed in selected frame.
 
@@ -201,11 +216,6 @@ selected by `other-buffer'."
            ;; Do not select if already shown in some window.
            (not (get-buffer-window buffer t)))))
 
-(defvar exwm-layout-show-all-buffers nil
-  "Non-nil to allow switching to buffers on other workspaces.")
-(declare-function exwm-workspace--workspace-p "exwm-workspace.el"
-                  (workspace))
-
 (defun exwm-layout--set-client-list-stacking ()
   "Set _NET_CLIENT_LIST_STACKING."
   (let (id clients-floating clients clients-iconic clients-other)
@@ -301,9 +311,6 @@ selected by `other-buffer'."
       (exwm-layout--set-client-list-stacking)
       (xcb:flush exwm--connection))))
 
-(declare-function exwm-workspace--client-p "exwm-workspace.el"
-                  (&optional frame))
-
 (defun exwm-layout--on-minibuffer-setup ()
   "Refresh layout when minibuffer grows."
   (unless (exwm-workspace--client-p)
@@ -479,8 +486,6 @@ See also `exwm-layout-enlarge-window'."
         (exwm-layout-hide-mode-line)
       (exwm-layout-show-mode-line))))
 
-(defvar exwm-layout--timer nil "Timer used to track echo area changes.")
-
 (defun exwm-layout--init ()
   "Initialize layout module."
   ;; Auto refresh layout
diff --git a/exwm-manage.el b/exwm-manage.el
index 0a9d92fc024b..b983ebf80de4 100644
--- a/exwm-manage.el
+++ b/exwm-manage.el
@@ -28,17 +28,66 @@
 
 (require 'exwm-core)
 
-(defvar exwm-manage-force-tiling nil
+(defgroup exwm-manage nil
+  "Manage."
+  :version "25.3"
+  :group 'exwm)
+
+(defcustom exwm-manage-finish-hook nil
+  "Normal hook run after a window is just managed, in the context of the
+corresponding buffer."
+  :type 'hook)
+
+(defcustom exwm-manage-force-tiling nil
   "Non-nil to force managing all X windows in tiling layout.
+You can still make the X windows floating afterwards."
+  :type 'boolean)
 
-You can still make the X windows floating afterwards.")
+;; FIXME: Make the following values as small as possible.
+(defconst exwm-manage--height-delta-min 5)
+(defconst exwm-manage--width-delta-min 5)
 
-(defvar exwm-manage-finish-hook nil
-  "Normal hook run after a window is just managed, in the context of the
-corresponding buffer.")
+;; The _MOTIF_WM_HINTS atom (see <Xm/MwmUtil.h> for more details)
+;; It's currently only used in 'exwm-manage' module
+(defvar exwm-manage--_MOTIF_WM_HINTS nil "_MOTIF_WM_HINTS atom.")
 
 (defvar exwm-manage--desktop nil "The desktop X window.")
 
+(defvar exwm-manage--frame-outer-id-list nil
+  "List of window-outer-id's of all frames.")
+
+(defvar exwm-manage--ping-lock nil
+  "Non-nil indicates EXWM is pinging a window.")
+
+(defvar exwm-manage-ping-timeout 3 "Seconds to wait before killing a client.")
+
+(defvar exwm-workspace--current)
+(defvar exwm-workspace--id-struts-alist)
+(defvar exwm-workspace--list)
+(defvar exwm-workspace--switch-history-outdated)
+(defvar exwm-workspace-current-index)
+(declare-function exwm--update-class "exwm.el" (id &optional force))
+(declare-function exwm--update-hints "exwm.el" (id &optional force))
+(declare-function exwm--update-normal-hints "exwm.el" (id &optional force))
+(declare-function exwm--update-protocols "exwm.el" (id &optional force))
+(declare-function exwm--update-struts "exwm.el" (id))
+(declare-function exwm--update-title "exwm.el" (id))
+(declare-function exwm--update-transient-for "exwm.el" (id &optional force))
+(declare-function exwm--update-window-type "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))
+(declare-function exwm-input-grab-keyboard "exwm-input.el")
+(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id))
+(declare-function exwm-workspace--count "exwm-workspace.el" ())
+(declare-function exwm-workspace--current-height "exwm-workspace.el")
+(declare-function exwm-workspace--current-width  "exwm-workspace.el")
+(declare-function exwm-workspace--set-desktop "exwm-workspace.el" (id))
+(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame))
+(declare-function exwm-workspace--update-struts "exwm-workspace.el" ())
+(declare-function exwm-workspace--update-workareas "exwm-workspace.el" ())
+(declare-function exwm-workspace-move-window "exwm-workspace.el"
+                  (frame-or-index &optional id))
+
 (defun exwm-manage--update-geometry (id &optional force)
   "Update window geometry."
   (with-current-buffer (exwm--id->buffer id)
@@ -58,10 +107,6 @@ corresponding buffer.")
         (when reply
           (setq exwm--ewmh-state (append (slot-value reply 'value) nil)))))))
 
-;; The _MOTIF_WM_HINTS atom (see <Xm/MwmUtil.h> for more details)
-;; It's currently only used in 'exwm-manage' module
-(defvar exwm-manage--_MOTIF_WM_HINTS nil "_MOTIF_WM_HINTS atom.")
-
 (defun exwm-manage--update-mwm-hints (id &optional force)
   "Update _MOTIF_WM_HINTS."
   (with-current-buffer (exwm--id->buffer id)
@@ -92,29 +137,6 @@ corresponding buffer.")
                      :window exwm--root
                      :data (vconcat (mapcar #'car exwm--id-buffer-alist)))))
 
-(defvar exwm-workspace--current)
-(defvar exwm-workspace--switch-history-outdated)
-(defvar exwm-workspace-current-index)
-(defvar exwm-workspace--workareas)
-
-(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-struts "exwm.el" (id))
-(declare-function exwm-floating--set-floating "exwm-floating.el" (id))
-(declare-function exwm-floating--unset-floating "exwm-floating.el" (id))
-(declare-function exwm-input-grab-keyboard "exwm-input.el")
-(declare-function exwm-workspace--current-height "exwm-workspace.el")
-(declare-function exwm-workspace--current-width  "exwm-workspace.el")
-(declare-function exwm-workspace--set-desktop "exwm-workspace.el" (id))
-(declare-function exwm-workspace--count "exwm-workspace.el" ())
-(declare-function exwm-workspace-move-window "exwm-workspace.el"
-                  (frame-or-index &optional id))
-
 (defun exwm-manage--manage-window (id)
   "Manage window ID."
   (exwm--log "Try to manage #x%x" id)
@@ -251,13 +273,6 @@ corresponding buffer.")
           (exwm-layout-set-fullscreen id))
         (run-hooks 'exwm-manage-finish-hook)))))
 
-(defvar exwm-workspace--id-struts-alist)
-(defvar exwm-workspace--list)
-
-(declare-function exwm-workspace--update-struts "exwm-workspace.el" ())
-(declare-function exwm-workspace--update-workareas "exwm-workspace.el" ())
-(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame))
-
 (defun exwm-manage--unmanage-window (id &optional withdraw-only)
   "Unmanage window ID.
 
@@ -350,10 +365,6 @@ manager is shutting down."
             (xcb:flush exwm--connection)
             (exwm-manage--manage-window i)))))))
 
-(defvar exwm-manage--ping-lock nil
-  "Non-nil indicates EXWM is pinging a window.")
-(defvar exwm-manage-ping-timeout 3 "Seconds to wait before killing a client.")
-
 (defun exwm-manage--kill-buffer-query-function ()
   "Run in `kill-buffer-query-functions'."
   (catch 'return
@@ -447,13 +458,6 @@ Would you like to kill it? "
                          (xcb:+request exwm--connection ,request))))
     (xcb:flush exwm--connection)))
 
-;; FIXME: Make the following values as small as possible.
-(defconst exwm-manage--width-delta-min 5)
-(defconst exwm-manage--height-delta-min 5)
-
-(defvar exwm-manage--frame-outer-id-list nil
-  "List of window-outer-id's of all frames.")
-
 (defun exwm-manage--add-frame (frame)
   "Run in `after-make-frame-functions'."
   (when (display-graphic-p frame)
@@ -560,8 +564,6 @@ border-width: %d; sibling: #x%x; stack-mode: %d"
                                :stack-mode stack-mode)))))))
   (xcb:flush exwm--connection))
 
-(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id))
-
 (defun exwm-manage--on-MapRequest (data _synthetic)
   "Handle MapRequest event."
   (let ((obj (make-instance 'xcb:MapRequest)))
diff --git a/exwm-randr.el b/exwm-randr.el
index 74938d6b71e1..f49073c45540 100644
--- a/exwm-randr.el
+++ b/exwm-randr.el
@@ -50,19 +50,42 @@
 (require 'xcb-randr)
 (require 'exwm-core)
 
-(defvar exwm-randr-workspace-output-plist nil)
+(defgroup exwm-randr nil
+  "RandR."
+  :version "25.3"
+  :group 'exwm)
 
-(defvar exwm-randr-refresh-hook nil
-  "Normal hook run when the RandR module just refreshed.")
+(defcustom exwm-randr-refresh-hook nil
+  "Normal hook run when the RandR module just refreshed."
+  :type 'hook)
+
+(defcustom exwm-randr-screen-change-hook nil
+  "Normal hook run when screen changes."
+  :type 'hook)
+
+(defcustom exwm-randr-workspace-output-plist nil
+  "Plist mapping workspace to output.
+
+If an output is not available, the workspaces mapped to it are displayed on
+the primary output until it becomes available.  Unspecified workspaces are
+all mapped to the primary output.  For example, with the following value
+workspace other than 1 and 3 would always be displayed on the primary output
+where workspace 1 and 3 would be displayed on their corresponding output
+whenever the outputs are available.
+
+  '(1 \"HDMI-1\" 3 \"DP-1\")
+
+The outputs available can be identified by running the 'xrandr' utility with
+the first one in result being the primary output."
+  :type '(plist :key-type integer :value-type string))
 
 (defvar exwm-workspace--fullscreen-frame-count)
 (defvar exwm-workspace--list)
-
 (declare-function exwm-workspace--count "exwm-workspace.el")
+(declare-function exwm-workspace--set-desktop-geometry "exwm-workspace.el" ())
 (declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame))
-(declare-function exwm-workspace--update-workareas "exwm-workspace.el" ())
 (declare-function exwm-workspace--show-minibuffer "exwm-workspace.el" ())
-(declare-function exwm-workspace--set-desktop-geometry "exwm-workspace.el" ())
+(declare-function exwm-workspace--update-workareas "exwm-workspace.el" ())
 
 (defun exwm-randr--refresh ()
   "Refresh workspaces according to the updated RandR info."
@@ -122,9 +145,6 @@
       (xcb:flush exwm--connection)
       (run-hooks 'exwm-randr-refresh-hook))))
 
-(defvar exwm-randr-screen-change-hook nil
-  "Normal hook run when screen changes.")
-
 (defun exwm-randr--init ()
   "Initialize RandR extension and EXWM RandR module."
   (if (= 0 (slot-value (xcb:get-extension-data exwm--connection 'xcb:randr)
diff --git a/exwm-systemtray.el b/exwm-systemtray.el
index 33e97623c0b2..5377ef8ed617 100644
--- a/exwm-systemtray.el
+++ b/exwm-systemtray.el
@@ -50,22 +50,38 @@
    (owner :initarg :owner :type xcb:WINDOW))      ;new slot
   :documentation "A systemtray client message.")
 
-;; GTK icons require at least 16 pixels to show normally.
-(defconst exwm-systemtray--icon-min-size 16 "Minimum icon size.")
+(defgroup exwm-systemtray nil
+  "System tray."
+  :version "25.3"
+  :group 'exwm)
+
+(defcustom exwm-systemtray-height nil
+  "System tray height.
 
-(defvar exwm-systemtray-height nil "System tray height.
+You shall use the default value if using auto-hide minibuffer."
+  :type 'integer)
 
-You shall use the default value if using auto-hide minibuffer.")
+(defcustom exwm-systemtray-icon-gap 2
+  "Gap between icons."
+  :type 'integer)
 
-(defvar exwm-systemtray-icon-gap 2 "Gap between icons.")
+;; GTK icons require at least 16 pixels to show normally.
+(defconst exwm-systemtray--icon-min-size 16 "Minimum icon size.")
 
 (defvar exwm-systemtray--connection nil "The X connection.")
+
+(defvar exwm-systemtray--embedder nil "The embedder window.")
+
 (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.")
 
 (defvar exwm-workspace--current)
+(defvar exwm-workspace--minibuffer)
+(defvar exwm-workspace--workareas)
+(defvar exwm-workspace-current-index)
+(defvar xcb:Atom:_NET_SYSTEM_TRAY_S0)
 (declare-function exwm-workspace--current-height "exwm-workspace.el")
 (declare-function exwm-workspace--current-width  "exwm-workspace.el")
 (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el")
@@ -304,9 +320,6 @@ You shall use the default value if using auto-hide minibuffer.")
                        :event (xcb:marshal obj exwm-systemtray--connection))))
   (xcb:flush exwm-systemtray--connection))
 
-(defvar exwm-workspace--workareas)
-(defvar exwm-workspace-current-index)
-
 (defun exwm-systemtray--on-workspace-switch ()
   "Reparent/Refresh the system tray in `exwm-workspace-switch-hook'."
   (unless (exwm-workspace--minibuffer-own-frame-p)
@@ -339,9 +352,6 @@ You shall use the default value if using auto-hide minibuffer.")
 (defalias 'exwm-systemtray--on-struts-update
   #'exwm-systemtray--on-randr-refresh)
 
-(defvar xcb:Atom:_NET_SYSTEM_TRAY_S0)
-(defvar exwm-workspace--minibuffer)
-
 (cl-defun exwm-systemtray--init ()
   "Initialize system tray module."
   (cl-assert (not exwm-systemtray--connection))
diff --git a/exwm-workspace.el b/exwm-workspace.el
index 2917c6910b6a..b9cda2516051 100644
--- a/exwm-workspace.el
+++ b/exwm-workspace.el
@@ -29,16 +29,110 @@
 
 (require 'exwm-core)
 
-(defvar exwm-manage--desktop)
+(defgroup exwm-workspace nil
+  "Workspace."
+  :version "25.3"
+  :group 'exwm)
 
-(defvar exwm-workspace-number 1 "Initial number of workspaces.")
-(defvar exwm-workspace--list nil "List of all workspaces (Emacs frames).")
-(defvar exwm-workspace--current nil "Current active workspace.")
-(defvar exwm-workspace-current-index 0 "Index of current active workspace.")
-(defvar exwm-workspace-index-map #'number-to-string
+(defcustom exwm-workspace-switch-hook nil
+  "Normal hook run after switching workspace."
+  :type 'hook)
+
+(defcustom exwm-workspace-list-change-hook nil
+  "Normal hook run when the workspace list is changed (workspace added,
+deleted, moved, etc)."
+  :type 'hook)
+
+(defcustom exwm-workspace-show-all-buffers nil
+  "Non-nil to show buffers on other workspaces."
+  :type 'boolean)
+
+(defcustom exwm-workspace-number 1
+  "Initial number of workspaces."
+  :type 'integer)
+
+(defcustom exwm-workspace-index-map #'number-to-string
   "Function for mapping a workspace index to a string for display.
 
-By default `number-to-string' is applied which yields 0 1 2 ... .")
+By default `number-to-string' is applied which yields 0 1 2 ... ."
+  :type 'function)
+
+(defcustom exwm-workspace-minibuffer-position nil
+  "Position of the minibuffer frame."
+  :type '(choice (const :tag "Bottom (fixed)" nil)
+                 (const :tag "Bottom (auto-hide)" bottom)
+                 (const :tag "Top (auto-hide)" top)))
+
+(defcustom exwm-workspace-display-echo-area-timeout 1
+  "Timeout for displaying echo area."
+  :type 'integer)
+
+(defcustom exwm-workspace-switch-create-limit 10
+  "Number of workspaces `exwm-workspace-switch-create' allowed to create
+each time."
+  :type 'integer)
+
+(defvar exwm-workspace-current-index 0 "Index of current active workspace.")
+
+(defvar exwm-workspace--attached-minibuffer-height 0
+  "Height (in pixel) of the attached minibuffer.
+
+If the minibuffer is detached, this value is 0.")
+
+(defvar exwm-workspace--client nil
+  "The 'client' frame parameter of emacsclient frames.")
+
+(defvar exwm-workspace--create-silently nil
+  "When non-nil workspaces are created in the background (not switched to).
+
+Please manually run the hook `exwm-workspace-list-change-hook' afterwards.")
+
+(defvar exwm-workspace--current nil "Current active workspace.")
+
+(defvar exwm-workspace--display-echo-area-timer nil
+  "Timer for auto-hiding echo area.")
+
+(defvar exwm-workspace--id-struts-alist nil "Alist of X window and struts.")
+
+(defvar exwm-workspace--fullscreen-frame-count 0
+  "Count the fullscreen workspace frames.")
+
+(defvar exwm-workspace--list nil "List of all workspaces (Emacs frames).")
+
+(defvar exwm-workspace--minibuffer nil
+  "The minibuffer frame shared among all frames.")
+
+(defvar exwm-workspace--prompt-add-allowed nil
+  "Non-nil to allow adding workspace from the prompt.")
+
+(defvar exwm-workspace--prompt-delete-allowed nil
+  "Non-nil to allow deleting workspace from the prompt.")
+
+(defvar exwm-workspace--struts nil "Areas occupied by struts.")
+
+(defvar exwm-workspace--switch-history nil
+  "History for `read-from-minibuffer' to interactively switch workspace.")
+
+(defvar exwm-workspace--switch-history-outdated nil
+  "Non-nil to indicate `exwm-workspace--switch-history' is outdated.")
+
+(defvar exwm-workspace--timer nil "Timer used to track echo area changes.")
+
+(defvar exwm-workspace--update-workareas-hook nil
+  "Normal hook run when workareas get updated.")
+
+(defvar exwm-workspace--workareas nil "Workareas (struts excluded).")
+
+(defvar exwm-input--during-command)
+(defvar exwm-layout-show-all-buffers)
+(defvar exwm-manage--desktop)
+(declare-function exwm--exit "exwm.el")
+(declare-function exwm-input--on-buffer-list-update "exwm-input.el" ())
+(declare-function exwm-layout--hide "exwm-layout.el" (id))
+(declare-function exwm-layout--other-buffer-predicate "exwm-layout.el"
+                  (buffer))
+(declare-function exwm-layout--refresh "exwm-layout.el")
+(declare-function exwm-layout--show "exwm-layout.el" (id &optional window))
 
 (defsubst exwm-workspace--position (frame)
   "Retrieve index of given FRAME in workspace list.
@@ -58,20 +152,6 @@ NIL if FRAME is not a workspace"
   "Return non-nil if FRAME is an emacsclient frame."
   (frame-parameter frame 'client))
 
-(defun exwm-workspace--workspace-from-frame-or-index (frame-or-index)
-  "Retrieve the workspace frame from FRAME-OR-INDEX."
-  (cond
-   ((framep frame-or-index)
-    (unless (exwm-workspace--position frame-or-index)
-      (user-error "[EXWM] Frame is not a workspace %S" frame-or-index))
-    frame-or-index)
-   ((integerp frame-or-index)
-    (unless (and (<= 0 frame-or-index)
-                 (< frame-or-index (exwm-workspace--count)))
-      (user-error "[EXWM] Workspace index out of range: %d" frame-or-index))
-    (elt exwm-workspace--list frame-or-index))
-   (t (user-error "[EXWM] Invalid workspace: %s" frame-or-index))))
-
 (defvar exwm-workspace--switch-map
   (let ((map (make-sparse-keymap)))
     (define-key map [t] (lambda () (interactive)))
@@ -98,10 +178,19 @@ NIL if FRAME is not a workspace"
     map)
   "Keymap used for interactively switch workspace.")
 
-(defvar exwm-workspace--switch-history nil
-  "History for `read-from-minibuffer' to interactively switch workspace.")
-(defvar exwm-workspace--switch-history-outdated nil
-  "Non-nil to indicate `exwm-workspace--switch-history' is outdated.")
+(defun exwm-workspace--workspace-from-frame-or-index (frame-or-index)
+  "Retrieve the workspace frame from FRAME-OR-INDEX."
+  (cond
+   ((framep frame-or-index)
+    (unless (exwm-workspace--position frame-or-index)
+      (user-error "[EXWM] Frame is not a workspace %S" frame-or-index))
+    frame-or-index)
+   ((integerp frame-or-index)
+    (unless (and (<= 0 frame-or-index)
+                 (< frame-or-index (exwm-workspace--count)))
+      (user-error "[EXWM] Workspace index out of range: %d" frame-or-index))
+    (elt exwm-workspace--list frame-or-index))
+   (t (user-error "[EXWM] Invalid workspace: %s" frame-or-index))))
 
 (defun exwm-workspace--prompt-for-workspace (&optional prompt)
   "Prompt for a workspace, returning the workspace frame."
@@ -117,15 +206,6 @@ NIL if FRAME is not a workspace"
                                      :test #'equal)))
     (elt exwm-workspace--list workspace-idx)))
 
-(defvar exwm-workspace--prompt-add-allowed nil
-  "Non-nil to allow adding workspace from the prompt.")
-(defvar exwm-workspace--prompt-delete-allowed nil
-  "Non-nil to allow deleting workspace from the prompt.")
-(defvar exwm-workspace--create-silently nil
-  "When non-nil workspaces are created in the background (not switched to).
-
-Please manually run the hook `exwm-workspace-list-change-hook' afterwards.")
-
 (defun exwm-workspace--prompt-add ()
   "Add workspace from the prompt."
   (interactive)
@@ -182,20 +262,6 @@ Please manually run the hook `exwm-workspace-list-change-hook' afterwards.")
                 sequence ""))
              sequence)))))
 
-(defvar exwm-workspace-show-all-buffers nil
-  "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
 (defun exwm-workspace--get-geometry (frame)
   "Return the geometry of frame FRAME."
@@ -227,9 +293,6 @@ Value nil means to use the default position which is fixed at bottom, while
   "Reports whether the minibuffer is displayed in its own frame."
   (memq exwm-workspace-minibuffer-position '(top bottom)))
 
-(defvar exwm-workspace--id-struts-alist nil "Alist of X window and struts.")
-(defvar exwm-workspace--struts nil "Areas occupied by struts.")
-
 (defun exwm-workspace--update-struts ()
   "Update `exwm-workspace--struts'."
   (setq exwm-workspace--struts nil)
@@ -250,10 +313,6 @@ Value nil means to use the default position which is fixed at bottom, while
               (setq exwm-workspace--struts
                     (append exwm-workspace--struts (list struts*))))))))))
 
-(defvar exwm-workspace--workareas nil "Workareas (struts excluded).")
-(defvar exwm-workspace--update-workareas-hook nil
-  "Normal hook run when workareas get updated.")
-
 (defun exwm-workspace--update-workareas ()
   "Update `exwm-workspace--workareas'."
   (let ((root-width (x-display-pixel-width))
@@ -318,9 +377,6 @@ Value nil means to use the default position which is fixed at bottom, while
     (xcb:flush exwm--connection))
   (run-hooks 'exwm-workspace--update-workareas-hook))
 
-(defvar exwm-workspace--fullscreen-frame-count 0
-  "Count the fullscreen workspace frames.")
-
 (defun exwm-workspace--set-fullscreen (frame)
   "Make frame FRAME fullscreen according to `exwm-workspace--workareas'."
   (let ((workarea (elt exwm-workspace--workareas
@@ -342,11 +398,6 @@ Value nil means to use the default position which is fixed at bottom, while
   (when exwm-workspace--fullscreen-frame-count
     (cl-incf exwm-workspace--fullscreen-frame-count)))
 
-(defvar exwm-workspace--attached-minibuffer-height 0
-  "Height (in pixel) of the attached minibuffer.
-
-If the minibuffer is detached, this value is 0.")
-
 (defun exwm-workspace--resize-minibuffer-frame ()
   "Resize minibuffer (and its container) to fit the size of workspace."
   (cl-assert (exwm-workspace--minibuffer-own-frame-p))
@@ -434,14 +485,13 @@ PREFIX-DIGITS is a list of the digits introduced so far."
   (goto-history-element (1+ n))
   (exit-minibuffer))
 
-(defvar exwm-workspace-switch-hook nil
-  "Normal hook run after switching workspace.")
-
 ;;;###autoload
 (defun exwm-workspace-switch (frame-or-index &optional force)
-  "Switch to workspace INDEX.  Query for FRAME-OR-INDEX if it's not specified.
+  "Switch to workspace INDEX (0-based).
 
-The optional FORCE option is for internal use only."
+Query for the index if not specified when called interactively.  Passing a
+workspace frame as the first option or making use of the rest options are
+for internal use only."
   (interactive
    (list
     (unless (and (eq major-mode 'exwm-mode)
@@ -518,13 +568,11 @@ The optional FORCE option is for internal use only."
     (run-hooks 'focus-in-hook)
     (run-hooks 'exwm-workspace-switch-hook)))
 
-(defvar exwm-workspace-switch-create-limit 10
-  "Number of workspaces `exwm-workspace-switch-create' allowed to create
-each time.")
-
 ;;;###autoload
 (defun exwm-workspace-switch-create (frame-or-index)
-  "Switch to workspace FRAME-OR-INDEX, creating it if it does not exist yet."
+  "Switch to workspace INDEX or creating it first if it does not exist yet.
+
+Passing a workspace frame as the first option is for internal use only."
   (interactive)
   (if (or (framep frame-or-index)
           (< frame-or-index (exwm-workspace--count)))
@@ -537,10 +585,6 @@ each time.")
       (run-hooks 'exwm-workspace-list-change-hook))
     (exwm-workspace-switch frame-or-index)))
 
-(defvar exwm-workspace-list-change-hook nil
-  "Normal hook run when the workspace list is changed (workspace added,
-deleted, moved, etc).")
-
 ;;;###autoload
 (defun exwm-workspace-swap (workspace1 workspace2)
   "Interchange position of WORKSPACE1 with that of WORKSPACE2."
@@ -579,6 +623,7 @@ deleted, moved, etc).")
 ;;;###autoload
 (defun exwm-workspace-move (workspace nth)
   "Move WORKSPACE to the NTH position.
+
 When called interactively, prompt for a workspace and move current one just
 before it."
   (interactive
@@ -645,13 +690,6 @@ INDEX must not exceed the current number of workspaces."
                        :window id
                        :data (exwm-workspace--position exwm--frame)))))
 
-(declare-function exwm-input--on-buffer-list-update "exwm-input.el" ())
-(declare-function exwm-layout--show "exwm-layout.el" (id &optional window))
-(declare-function exwm-layout--hide "exwm-layout.el" (id))
-(declare-function exwm-layout--refresh "exwm-layout.el")
-(declare-function exwm-layout--other-buffer-predicate "exwm-layout.el"
-                  (buffer))
-
 ;;;###autoload
 (defun exwm-workspace-move-window (frame-or-index &optional id)
   "Move window ID to workspace FRAME-OR-INDEX."
@@ -780,8 +818,6 @@ INDEX must not exceed the current number of workspaces."
         (xcb:flush exwm--connection)))
     (setq exwm-workspace--switch-history-outdated t)))
 
-(defvar exwm-layout-show-all-buffers)
-
 ;;;###autoload
 (defun exwm-workspace-switch-to-buffer (buffer-or-name)
   "Make the current Emacs window display another buffer."
@@ -1049,8 +1085,6 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
     (remove-hook 'post-command-hook #'exwm-workspace--update-minibuffer-height)
     (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))
@@ -1075,12 +1109,6 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
       (cancel-timer exwm-workspace--display-echo-area-timer)
       (setq exwm-workspace--display-echo-area-timer nil))))
 
-(defvar exwm-workspace--client nil
-  "The 'client' frame parameter of emacsclient frames.")
-
-(declare-function exwm-manage--unmanage-window "exwm-manage.el")
-(declare-function exwm--exit "exwm.el")
-
 (defun exwm-workspace--confirm-kill-emacs (prompt &optional force)
   "Confirm before exiting Emacs."
   (when (cond
@@ -1157,8 +1185,6 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
                      :width (x-display-pixel-width)
                      :height (x-display-pixel-height))))
 
-(defvar exwm-workspace--timer nil "Timer used to track echo area changes.")
-
 (defun exwm-workspace--add-frame-as-workspace (frame)
   "Configure frame FRAME to be treated as a workspace."
   (cond
diff --git a/exwm.el b/exwm.el
index 02e9152ed0e6..3e445b21eec7 100644
--- a/exwm.el
+++ b/exwm.el
@@ -72,9 +72,41 @@
 (require 'exwm-manage)
 (require 'exwm-input)
 
+(defgroup exwm nil
+  "Emacs X Window Manager."
+  :tag "EXWM"
+  :version "25.3"
+  :group 'applications
+  :prefix "exwm-")
+
+(defcustom exwm-init-hook nil
+  "Normal hook run when EXWM has just finished initialization."
+  :type 'hook)
+
+(defcustom exwm-exit-hook nil
+  "Normal hook run just before EXWM exits."
+  :type 'hook)
+
+(defcustom exwm-update-class-hook nil
+  "Normal hook run when window class is updated."
+  :type 'hook)
+
+(defcustom exwm-update-title-hook nil
+  "Normal hook run when window title is updated."
+  :type 'hook)
+
+(defcustom exwm-blocking-subrs '(x-file-dialog x-popup-dialog x-select-font)
+  "Subrs (primitives) that would normally block EXWM."
+  :type '(repeat function))
+
+(defconst exwm--server-name "server-exwm"
+  "Name of the subordinate Emacs server.")
+
+(defvar exwm--server-process nil "Process of the subordinate Emacs server.")
+
 ;;;###autoload
 (defun exwm-reset ()
-  "Reset window to standard state: non-fullscreen, line-mode."
+  "Reset the state of the selected window (non-fullscreen, line-mode, etc)."
   (interactive)
   (with-current-buffer (window-buffer)
     (when (eq major-mode 'exwm-mode)
@@ -123,9 +155,6 @@
         (when reply                     ;nil when destroyed
           (setq exwm-window-type (append (slot-value reply 'value) nil)))))))
 
-(defvar exwm-update-class-hook nil
-  "Normal hook run when window class is updated.")
-
 (defun exwm--update-class (id &optional force)
   "Update WM_CLASS."
   (with-current-buffer (exwm--id->buffer id)
@@ -138,9 +167,6 @@
           (when (and exwm-instance-name exwm-class-name)
             (run-hooks 'exwm-update-class-hook)))))))
 
-(defvar exwm-update-title-hook nil
-  "Normal hook run when window title is updated.")
-
 (defun exwm--update-utf8-title (id &optional force)
   "Update _NET_WM_NAME."
   (with-current-buffer (exwm--id->buffer id)
@@ -645,9 +671,6 @@
                        :property p))
     (xcb:flush exwm--connection)))
 
-(defvar exwm-init-hook nil
-  "Normal hook run when EXWM has just finished initialization.")
-
 (defun exwm-init (&optional frame)
   "Initialize EXWM."
   (if frame
@@ -692,8 +715,6 @@
         (exwm-manage--scan)
         (run-hooks 'exwm-init-hook)))))
 
-(defvar exwm-exit-hook nil "Normal hook run just before EXWM exits.")
-
 (defun exwm--exit ()
   "Exit EXWM."
   (run-hooks 'exwm-exit-hook)
@@ -705,9 +726,6 @@
   (exwm-layout--exit)
   (exwm--exit-icccm-ewmh))
 
-(defvar exwm-blocking-subrs '(x-file-dialog x-popup-dialog x-select-font)
-  "Subrs (primitives) that would normally block EXWM.")
-
 (defun exwm-enable (&optional undo)
   "Enable/Disable EXWM."
   (pcase undo
@@ -734,10 +752,6 @@
      (dolist (i exwm-blocking-subrs)
        (advice-add i :around #'exwm--server-eval-at)))))
 
-(defconst exwm--server-name "server-exwm"
-  "Name of the subordinate Emacs server.")
-(defvar exwm--server-process nil "Process of the subordinate Emacs server.")
-
 (defun exwm--server-stop ()
   "Stop the subordinate Emacs server."
   (server-force-delete exwm--server-name)
@@ -784,13 +798,6 @@
                ;; For other types, return the value as-is.
                (t result))))))
 
-(define-obsolete-function-alias 'exwm-enable-ido-workaround 'exwm-config-ido
-  "25.1" "Enable workarounds for Ido.")
-
-(defun exwm-disable-ido-workaround ()
-  "This function does nothing actually."
-  (declare (obsolete nil "25.1")))
-
 
 
 (provide 'exwm)
diff --git a/xinitrc b/xinitrc
index 873265fb644b..0adc06845078 100644
--- a/xinitrc
+++ b/xinitrc
@@ -1,6 +1,9 @@
 # Disable access control
 xhost +SI:localuser:$USER
 
+# Make Java applications aware this is a non-reparenting window manager.
+export _JAVA_AWT_WM_NONREPARENTING=1
+
 # Themes, etc
 gnome-settings-daemon &