about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--README.md3
-rw-r--r--exwm-cm.el2
-rw-r--r--exwm-config.el62
-rw-r--r--exwm-core.el11
-rw-r--r--exwm-floating.el13
-rw-r--r--exwm-input.el74
-rw-r--r--exwm-layout.el39
-rw-r--r--exwm-manage.el91
-rw-r--r--exwm-randr.el237
-rw-r--r--exwm-systemtray.el35
-rw-r--r--exwm-workspace.el51
-rw-r--r--exwm-xim.el781
-rw-r--r--exwm.el55
-rw-r--r--xinitrc19
14 files changed, 1234 insertions, 239 deletions
diff --git a/README.md b/README.md
index 103948c63379..6d7e0dd1ff17 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,8 @@ It features:
 + Dynamic workspace support
 + ICCCM/EWMH compliance
 + (Optional) RandR (multi-monitor) support
-+ (Optional) Built-in system tray
++ (Optional) Builtin system tray
++ (Optional) Builtin input method
 
 Please check out the
 [screenshots](https://github.com/ch11ng/exwm/wiki/Screenshots)
diff --git a/exwm-cm.el b/exwm-cm.el
index ff556fb21921..1c33f875a677 100644
--- a/exwm-cm.el
+++ b/exwm-cm.el
@@ -1,6 +1,6 @@
 ;;; exwm-cm.el --- Compositing Manager for EXWM  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2016-2018 Free Software Foundation, Inc.
+;; Copyright (C) 2016-2019 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
diff --git a/exwm-config.el b/exwm-config.el
index 89320bc1e6eb..6635e4345db3 100644
--- a/exwm-config.el
+++ b/exwm-config.el
@@ -1,6 +1,6 @@
 ;;; exwm-config.el --- Predefined configurations  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2018 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2019 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -31,38 +31,44 @@
 (defun exwm-config-default ()
   "Default configuration of EXWM."
   ;; Set the initial workspace number.
-  (setq exwm-workspace-number 4)
+  (unless (get 'exwm-workspace-number 'saved-value)
+    (setq exwm-workspace-number 4))
   ;; Make class name the buffer name
   (add-hook 'exwm-update-class-hook
             (lambda ()
               (exwm-workspace-rename-buffer exwm-class-name)))
-  ;; 's-r': Reset
-  (exwm-input-set-key (kbd "s-r") #'exwm-reset)
-  ;; 's-w': Switch workspace
-  (exwm-input-set-key (kbd "s-w") #'exwm-workspace-switch)
-  ;; 's-N': Switch to certain workspace
-  (dotimes (i 10)
-    (exwm-input-set-key (kbd (format "s-%d" i))
-                        `(lambda ()
-                           (interactive)
-                           (exwm-workspace-switch-create ,i))))
-  ;; 's-&': Launch application
-  (exwm-input-set-key (kbd "s-&")
-                      (lambda (command)
-                        (interactive (list (read-shell-command "$ ")))
-                        (start-process-shell-command command nil command)))
+  ;; Global keybindings.
+  (unless (get 'exwm-input-global-keys 'saved-value)
+    (setq exwm-input-global-keys
+          `(
+            ;; 's-r': Reset (to line-mode).
+            ([?\s-r] . exwm-reset)
+            ;; 's-w': Switch workspace.
+            ([?\s-w] . exwm-workspace-switch)
+            ;; 's-&': Launch application.
+            ([?\s-&] . (lambda (command)
+		         (interactive (list (read-shell-command "$ ")))
+		         (start-process-shell-command command nil command)))
+            ;; 's-N': Switch to certain workspace.
+            ,@(mapcar (lambda (i)
+                        `(,(kbd (format "s-%d" i)) .
+                          (lambda ()
+                            (interactive)
+                            (exwm-workspace-switch-create ,i))))
+                      (number-sequence 0 9)))))
   ;; Line-editing shortcuts
-  (setq exwm-input-simulation-keys
-        '(([?\C-b] . [left])
-          ([?\C-f] . [right])
-          ([?\C-p] . [up])
-          ([?\C-n] . [down])
-          ([?\C-a] . [home])
-          ([?\C-e] . [end])
-          ([?\M-v] . [prior])
-          ([?\C-v] . [next])
-          ([?\C-d] . [delete])
-          ([?\C-k] . [S-end delete])))
+  (unless (get 'exwm-input-simulation-keys 'saved-value)
+    (setq exwm-input-simulation-keys
+          '(([?\C-b] . [left])
+            ([?\C-f] . [right])
+            ([?\C-p] . [up])
+            ([?\C-n] . [down])
+            ([?\C-a] . [home])
+            ([?\C-e] . [end])
+            ([?\M-v] . [prior])
+            ([?\C-v] . [next])
+            ([?\C-d] . [delete])
+            ([?\C-k] . [S-end delete]))))
   ;; Enable EXWM
   (exwm-enable)
   ;; Configure Ido
diff --git a/exwm-core.el b/exwm-core.el
index 1f939491c5ef..9b6877b83f11 100644
--- a/exwm-core.el
+++ b/exwm-core.el
@@ -1,6 +1,6 @@
 ;;; exwm-core.el --- Core definitions  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2018 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2019 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -131,6 +131,15 @@ Nil can be passed as placeholder."
                                          (if height xcb:ConfigWindow:Height 0))
                      :x x :y y :width width :height height)))
 
+(defun exwm--intern-atom (atom)
+  "Intern X11 ATOM."
+  (slot-value (xcb:+request-unchecked+reply exwm--connection
+                  (make-instance 'xcb:InternAtom
+                                 :only-if-exists 0
+                                 :name-len (length atom)
+                                 :name atom))
+              'atom))
+
 (defmacro exwm--defer (secs function &rest args)
   "Defer the execution of FUNCTION.
 
diff --git a/exwm-floating.el b/exwm-floating.el
index aa2f98822a2e..b7430d719e17 100644
--- a/exwm-floating.el
+++ b/exwm-floating.el
@@ -1,6 +1,6 @@
 ;;; exwm-floating.el --- Floating Module for EXWM  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2018 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2019 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -83,6 +83,7 @@ This is also used by X window containers.")
 
 (defun exwm-floating--set-allowed-actions (id tilling)
   "Set _NET_WM_ALLOWED_ACTIONS."
+  (exwm--log "#x%x" id)
   (xcb:+request exwm--connection
       (make-instance 'xcb:ewmh:set-_NET_WM_ALLOWED_ACTIONS
                      :window id
@@ -326,6 +327,7 @@ This is also used by X window containers.")
 
 (defun exwm-floating--unset-floating (id)
   "Make window ID non-floating."
+  (exwm--log "#x%x" id)
   (let ((buffer (exwm--id->buffer id)))
     (with-current-buffer buffer
       (when exwm--floating-frame
@@ -400,6 +402,7 @@ This is also used by X window containers.")
 (cl-defun exwm-floating-toggle-floating ()
   "Toggle the current window between floating and non-floating states."
   (interactive)
+  (exwm--log)
   (unless (derived-mode-p 'exwm-mode)
     (cl-return-from exwm-floating-toggle-floating))
   (with-current-buffer (window-buffer)
@@ -411,6 +414,7 @@ This is also used by X window containers.")
 (defun exwm-floating-hide ()
   "Hide the current floating X window (which would show again when selected)."
   (interactive)
+  (exwm--log)
   (when (and (derived-mode-p 'exwm-mode)
              exwm--floating-frame)
     (exwm-layout--hide exwm--id)
@@ -418,6 +422,7 @@ This is also used by X window containers.")
 
 (defun exwm-floating--start-moveresize (id &optional type)
   "Start move/resize."
+  (exwm--log "#x%x" id)
   (let ((buffer-or-id (or (exwm--id->buffer id) id))
         frame container-or-id x y width height cursor)
     (if (bufferp buffer-or-id)
@@ -581,6 +586,7 @@ This is also used by X window containers.")
 
 (defun exwm-floating--stop-moveresize (&rest _args)
   "Stop move/resize."
+  (exwm--log)
   (xcb:+request exwm--connection
       (make-instance 'xcb:UngrabPointer :time xcb:Time:CurrentTime))
   (when exwm-floating--moveresize-calculate
@@ -641,6 +647,7 @@ This is also used by X window containers.")
   "Move a floating window right by DELTA-X pixels and down by DELTA-Y pixels.
 
 Both DELTA-X and DELTA-Y default to 1.  This command should be bound locally."
+  (exwm--log "delta-x: %s, delta-y: %s" delta-x delta-y)
   (unless (and (derived-mode-p 'exwm-mode) exwm--floating-frame)
     (user-error "[EXWM] `exwm-floating-move' is only for floating X windows"))
   (unless delta-x (setq delta-x 1))
@@ -663,6 +670,7 @@ Both DELTA-X and DELTA-Y default to 1.  This command should be bound locally."
 
 (defun exwm-floating--init ()
   "Initialize floating module."
+  (exwm--log)
   ;; Check border width.
   (unless (and (integerp exwm-floating-border-width)
                (> exwm-floating-border-width 0))
@@ -708,7 +716,8 @@ Both DELTA-X and DELTA-Y default to 1.  This command should be bound locally."
         (xcb:cursor:load-cursor exwm--connection "left_side")))
 
 (defun exwm-floating--exit ()
-  "Exit the floating module.")
+  "Exit the floating module."
+  (exwm--log))
 
 
 
diff --git a/exwm-input.el b/exwm-input.el
index a7fb16a1755f..d0ae4ad81250 100644
--- a/exwm-input.el
+++ b/exwm-input.el
@@ -1,6 +1,6 @@
 ;;; exwm-input.el --- Input Module for EXWM  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2018 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2019 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -117,6 +117,9 @@ defined in `exwm-mode-map' here."
 
 (defvar exwm-input--simulation-keys nil "Simulation keys in line-mode.")
 
+(defvar exwm-input--skip-buffer-list-update nil
+  "Skip the upcoming 'buffer-list-update'.")
+
 (defvar exwm-input--temp-line-mode nil
   "Non-nil indicates it's in temporary line-mode for char-mode.")
 
@@ -224,7 +227,7 @@ ARGS are additional arguments to CALLBACK."
     (with-slots (time root event root-x root-y event-x event-y state) evt
       (setq buffer (exwm--id->buffer event)
             window (get-buffer-window buffer t))
-      (exwm--log "EnterNotify: buffer=%s; window=%s" buffer window)
+      (exwm--log "buffer=%s; window=%s" buffer window)
       (when (and buffer window (not (eq window (selected-window))))
         (setq frame (window-frame window)
               frame-xid (frame-parameter frame 'exwm-id))
@@ -260,12 +263,14 @@ ARGS are additional arguments to CALLBACK."
         (xcb:flush exwm--connection)))))
 
 (defun exwm-input--on-keysyms-update ()
+  (exwm--log)
   (let ((exwm-input--global-prefix-keys nil))
     (exwm-input--update-global-prefix-keys)))
 
 (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))
+             (not exwm-input--skip-buffer-list-update)
              (not (exwm-workspace--client-p))
              ;; The following conditions filter out events relating to temp
              ;; buffers.
@@ -380,6 +385,7 @@ ARGS are additional arguments to CALLBACK."
             buffer (exwm--id->buffer event)
             window (get-buffer-window buffer t))
       (cond ((and (eq button-event exwm-input-move-event)
+                  buffer
                   ;; Either an undecorated or a floating X window.
                   (with-current-buffer buffer
                     (or (not (derived-mode-p 'exwm-mode))
@@ -388,12 +394,13 @@ ARGS are additional arguments to CALLBACK."
              (exwm-floating--start-moveresize
               event xcb:ewmh:_NET_WM_MOVERESIZE_MOVE))
             ((and (eq button-event exwm-input-resize-event)
+                  buffer
                   (with-current-buffer buffer
                     (or (not (derived-mode-p 'exwm-mode))
                         exwm--floating-frame)))
              ;; Resize
              (exwm-floating--start-moveresize event))
-            (t
+            (buffer
              ;; Click to focus
              (unless (eq window (selected-window))
                (setq frame (window-frame window))
@@ -412,13 +419,18 @@ ARGS are additional arguments to CALLBACK."
                    (select-window window)
                  (setq window (get-buffer-window buffer t))
                  (when window (select-window window))))
+             ;; Also process keybindings.
              (with-current-buffer buffer
                (when (derived-mode-p 'exwm-mode)
                  (cl-case exwm--input-mode
                    (line-mode
-                    (setq mode (exwm-input--on-ButtonPress-line-mode buffer button-event)))
+                    (setq mode (exwm-input--on-ButtonPress-line-mode
+                                buffer button-event)))
                    (char-mode
-                    (setq mode (exwm-input--on-ButtonPress-char-mode)))))))))
+                    (setq mode (exwm-input--on-ButtonPress-char-mode)))))))
+            (t
+             ;; Replay this event by default.
+             (setq mode xcb:Allow:ReplayPointer))))
     (xcb:+request exwm--connection
         (make-instance 'xcb:AllowEvents :mode mode :time xcb:Time:CurrentTime))
     (xcb:flush exwm--connection))
@@ -491,6 +503,7 @@ ARGS are additional arguments to CALLBACK."
     (xcb:flush exwm--connection)))
 
 (defun exwm-input--set-key (key command)
+  (exwm--log "key: %s, command: %s" key command)
   (global-set-key key command)
   (cl-pushnew key exwm-input--global-keys))
 
@@ -534,9 +547,9 @@ instead."
     (exwm-input--update-global-prefix-keys)))
 
 ;; Putting (t . EVENT) into `unread-command-events' does not really work
-;; as documented for Emacs < 27.
+;; as documented for Emacs < 26.2.
 (eval-and-compile
-  (if (< emacs-major-version 27)
+  (if (string-version-lessp emacs-version "26.2")
       (defsubst exwm-input--unread-event (event)
         (setq unread-command-events
               (append unread-command-events (list event))))
@@ -570,8 +583,9 @@ instead."
           (cl-return-from exwm-input--translate translation)))))
   key)
 
-(defun exwm-input--cache-event (event)
+(defun exwm-input--cache-event (event &optional temp-line-mode)
   "Cache EVENT."
+  (exwm--log "%s" event)
   (setq exwm-input--line-mode-cache
         (vconcat exwm-input--line-mode-cache (vector event)))
   ;; Attempt to translate this key sequence.
@@ -580,8 +594,12 @@ instead."
   ;; When the key sequence is complete (not a keymap).
   ;; Note that `exwm-input--line-mode-cache' might get translated to nil, for
   ;; example 'mouse--down-1-maybe-follows-link' does this.
-  (unless (and exwm-input--line-mode-cache
-               (keymapp (key-binding exwm-input--line-mode-cache)))
+  (if (and exwm-input--line-mode-cache
+           (keymapp (key-binding exwm-input--line-mode-cache)))
+      ;; Grab keyboard temporarily to intercept the complete key sequence.
+      (when temp-line-mode
+        (setq exwm-input--temp-line-mode t)
+        (exwm-input--grab-keyboard))
     (setq exwm-input--line-mode-cache nil)
     (when exwm-input--temp-line-mode
       (setq exwm-input--temp-line-mode nil)
@@ -657,10 +675,7 @@ Current buffer must be an `exwm-mode' buffer."
                  (setq event (exwm-input--mimic-read-event raw-event)))
         (if (not (derived-mode-p 'exwm-mode))
             (exwm-input--unread-event raw-event)
-          ;; Grab keyboard temporarily.
-          (setq exwm-input--temp-line-mode t)
-          (exwm-input--grab-keyboard)
-          (exwm-input--cache-event event)
+          (exwm-input--cache-event event t)
           (exwm-input--unread-event raw-event)))))
   (xcb:+request exwm--connection
       (make-instance 'xcb:AllowEvents
@@ -677,6 +692,7 @@ The return value is used as event_mode to release the original
 button event."
   (with-current-buffer buffer
     (let ((read-event (exwm-input--mimic-read-event button-event)))
+      (exwm--log "%s" read-event)
       (if (and read-event
                (exwm-input--event-passthrough-p read-event))
           ;; The event should be forwarded to emacs
@@ -691,10 +707,12 @@ button event."
   "Handle button events in char-mode.
 The return value is used as event_mode to release the original
 button event."
+  (exwm--log)
   xcb:Allow:ReplayPointer)
 
 (defun exwm-input--update-mode-line (id)
   "Update the propertized `mode-line-process' for window ID."
+  (exwm--log "#x%x" id)
   (let (help-echo cmd mode)
     (cl-case exwm--input-mode
       (line-mode
@@ -826,6 +844,7 @@ button event."
 EXWM will prompt for the key to send.  This command can be prefixed to send
 multiple keys."
   (interactive "p")
+  (exwm--log)
   (unless (derived-mode-p 'exwm-mode)
     (cl-return-from exwm-input-send-next-key))
   (when (> times 12) (setq times 12))
@@ -844,6 +863,7 @@ multiple keys."
 
 (defun exwm-input--set-simulation-keys (simulation-keys &optional no-refresh)
   "Set simulation keys."
+  (exwm--log "%s" simulation-keys)
   (unless no-refresh
     ;; Unbind simulation keys.
     (let ((hash (buffer-local-value 'exwm-input--simulation-keys
@@ -894,14 +914,15 @@ Notes:
 * Setting the value directly (rather than customizing it) after EXWM
   finishes initialization has no effect.
 * Original-keys consist of multiple key events are only supported in Emacs
-  27 and later.
+  26.2 and later.
 * A minority of applications do not accept simulated keys by default.  It's
   required to customize them to accept events sent by SendEvent.
 * The predefined examples in the Customize interface are not guaranteed to
   work for all applications.  This can be tweaked on a per application basis
   with `exwm-input-set-local-simulation-keys'."
-  :type '(alist :key-type (choice (key-sequence :tag "Original"))
-                :value-type (choice (key-sequence :tag "Move left" [left])
+  :type '(alist :key-type (key-sequence :tag "Original")
+                :value-type (choice (key-sequence :tag "User-defined")
+                                    (key-sequence :tag "Move left" [left])
                                     (key-sequence :tag "Move right" [right])
                                     (key-sequence :tag "Move up" [up])
                                     (key-sequence :tag "Move down" [down])
@@ -913,8 +934,7 @@ Notes:
                                     (key-sequence :tag "Paste" [C-v])
                                     (key-sequence :tag "Delete" [delete])
                                     (key-sequence :tag "Delete to EOL"
-                                                  [S-end delete])
-                                    (key-sequence :tag "User-defined")))
+                                                  [S-end delete])))
   :set (lambda (symbol value)
          (set symbol value)
          (exwm-input--set-simulation-keys value)))
@@ -945,6 +965,7 @@ ends unless it's specifically saved in the Customize interface for
                         (format "Simulate %s as" (key-description original))
                         ?\C-g)))
      (list original simulated)))
+  (exwm--log "original: %s, simulated: %s" original-key simulated-key)
   (when (and original-key simulated-key)
     (let ((entry `((,original-key . ,simulated-key))))
       (setq exwm-input-simulation-keys (append exwm-input-simulation-keys
@@ -953,6 +974,7 @@ ends unless it's specifically saved in the Customize interface for
 
 (defun exwm-input--unset-simulation-keys ()
   "Clear simulation keys and key bindings defined."
+  (exwm--log)
   (when (hash-table-p exwm-input--simulation-keys)
     (maphash (lambda (key _value)
                (when (sequencep key)
@@ -965,6 +987,7 @@ ends unless it's specifically saved in the Customize interface for
 
 SIMULATION-KEYS is an alist of the form (original-key . simulated-key),
 where both ORIGINAL-KEY and SIMULATED-KEY are key sequences."
+  (exwm--log)
   (make-local-variable 'exwm-input--simulation-keys)
   (use-local-map (copy-keymap exwm-mode-map))
   (let ((exwm-input--local-simulation-keys t))
@@ -974,6 +997,7 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences."
 (cl-defun exwm-input-send-simulation-key (times)
   "Fake a key event according to the last input key sequence."
   (interactive "p")
+  (exwm--log)
   (unless (derived-mode-p 'exwm-mode)
     (cl-return-from exwm-input-send-simulation-key))
   (let ((keys (gethash (this-single-command-keys)
@@ -993,6 +1017,7 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences."
 
 (defun exwm-input--init ()
   "Initialize the keyboard module."
+  (exwm--log)
   ;; Refresh keyboard mapping
   (xcb:keysyms:init exwm--connection #'exwm-input--on-keysyms-update)
   ;; Create the X window and intern the atom used to fetch timestamp.
@@ -1015,14 +1040,7 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences."
       (make-instance 'xcb:ewmh:set-_NET_WM_NAME
                      :window exwm-input--timestamp-window
                      :data "EXWM: exwm-input--timestamp-window"))
-  (let ((atom "_TIME"))
-    (setq exwm-input--timestamp-atom
-          (slot-value (xcb:+request-unchecked+reply exwm--connection
-                          (make-instance 'xcb:InternAtom
-                                         :only-if-exists 0
-                                         :name-len (length atom)
-                                         :name atom))
-                      'atom)))
+  (setq exwm-input--timestamp-atom (exwm--intern-atom "_TIME"))
   ;; Initialize global keys.
   (dolist (i exwm-input-global-keys)
     (exwm-input--set-key (car i) (cdr i)))
@@ -1050,10 +1068,12 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences."
 
 (defun exwm-input--post-init ()
   "The second stage in the initialization of the input module."
+  (exwm--log)
   (exwm-input--update-global-prefix-keys))
 
 (defun exwm-input--exit ()
   "Exit the input module."
+  (exwm--log)
   (exwm-input--unset-simulation-keys)
   (remove-hook 'pre-command-hook #'exwm-input--on-pre-command)
   (remove-hook 'post-command-hook #'exwm-input--on-post-command)
diff --git a/exwm-layout.el b/exwm-layout.el
index bee6901f8291..52fd08e15274 100644
--- a/exwm-layout.el
+++ b/exwm-layout.el
@@ -1,6 +1,6 @@
 ;;; exwm-layout.el --- Layout Module for EXWM  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2018 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2019 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -32,6 +32,10 @@
   :version "25.3"
   :group 'exwm)
 
+(defcustom exwm-layout-auto-iconify t
+  "Non-nil to automatically iconify unused X windows when possible."
+  :type 'boolean)
+
 (defcustom exwm-layout-show-all-buffers nil
   "Non-nil to allow switching to buffers on other workspaces."
   :type 'boolean)
@@ -76,6 +80,20 @@
   (when (derived-mode-p 'exwm-mode)
     (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)))
 
+(defun exwm-layout--auto-iconify ()
+  (when (and exwm-layout-auto-iconify
+             (not exwm-transient-for))
+    (let ((xwin exwm--id)
+          (state exwm-state))
+      (dolist (pair exwm--id-buffer-alist)
+        (with-current-buffer (cdr pair)
+          (when (and exwm--floating-frame
+                     (eq exwm-transient-for xwin)
+                     (not (eq exwm-state state)))
+            (if (eq state xcb:icccm:WM_STATE:NormalState)
+                (exwm-layout--refresh-floating exwm--floating-frame)
+              (exwm-layout--hide exwm--id))))))))
+
 (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)
@@ -111,7 +129,8 @@
                 height height*)))
       (exwm--set-geometry id x y width height)
       (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id))
-      (exwm-layout--set-state id xcb:icccm:WM_STATE:NormalState)))
+      (exwm-layout--set-state id xcb:icccm:WM_STATE:NormalState)
+      (exwm-layout--auto-iconify)))
   (xcb:flush exwm--connection))
 
 (defun exwm-layout--hide (id)
@@ -141,6 +160,7 @@
                          :window id :value-mask xcb:CW:EventMask
                          :event-mask exwm--client-event-mask))
       (exwm-layout--set-state id xcb:icccm:WM_STATE:IconicState)
+      (exwm-layout--auto-iconify)
       (xcb:flush exwm--connection))))
 
 ;;;###autoload
@@ -296,7 +316,9 @@ selected by `other-buffer'."
     (dolist (window windows)
       (with-current-buffer (window-buffer window)
         (when (derived-mode-p 'exwm-mode)
-          (switch-to-prev-buffer window))))))
+          (if (window-prev-buffers window)
+              (switch-to-prev-buffer window)
+            (switch-to-next-buffer window)))))))
 
 (defun exwm-layout--refresh-workspace (frame)
   "Refresh workspace frame FRAME."
@@ -346,14 +368,18 @@ selected by `other-buffer'."
     ;; Set some sensible buffer to vacated windows.
     (let ((exwm-layout--other-buffer-exclude-buffers covered-buffers))
       (dolist (window vacated-windows)
-        (switch-to-prev-buffer window)))
+        (if (window-prev-buffers window)
+            (switch-to-prev-buffer window)
+          (switch-to-next-buffer window))))
     ;; Make sure windows floating / on other workspaces are excluded
     (let ((exwm-layout--other-buffer-exclude-exwm-mode-buffers t))
       (dolist (window (window-list frame 'nomini))
         (with-current-buffer (window-buffer window)
           (when (and (derived-mode-p 'exwm-mode)
                      (or exwm--floating-frame (not (eq frame exwm--frame))))
-            (switch-to-prev-buffer window)))))
+            (if (window-prev-buffers window)
+                (switch-to-prev-buffer window)
+              (switch-to-next-buffer window))))))
     (exwm-layout--set-client-list-stacking)
     (xcb:flush exwm--connection)))
 
@@ -473,6 +499,7 @@ windows."
 
 See also `exwm-layout-enlarge-window'."
   (interactive "p")
+  (exwm--log "%s" delta)
   (exwm-layout-enlarge-window delta t))
 
 ;;;###autoload
@@ -481,6 +508,7 @@ See also `exwm-layout-enlarge-window'."
 
 See also `exwm-layout-enlarge-window'."
   (interactive "p")
+  (exwm--log "%s" delta)
   (exwm-layout-enlarge-window (- delta)))
 
 ;;;###autoload
@@ -489,6 +517,7 @@ See also `exwm-layout-enlarge-window'."
 
 See also `exwm-layout-enlarge-window'."
   (interactive "p")
+  (exwm--log "%s" delta)
   (exwm-layout-enlarge-window (- delta) t))
 
 ;;;###autoload
diff --git a/exwm-manage.el b/exwm-manage.el
index 79c5405fffe8..b41512c485c0 100644
--- a/exwm-manage.el
+++ b/exwm-manage.el
@@ -1,7 +1,7 @@
 ;;; exwm-manage.el --- Window Management Module for  -*- lexical-binding: t -*-
 ;;;                    EXWM
 
-;; Copyright (C) 2015-2018 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2019 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -70,6 +70,7 @@ You can still make the X windows floating afterwards."
                         (const :tag "Prefix keys" prefix-keys)
                         (const :tag "Simulation keys" simulation-keys)
                         (const :tag "Workspace" workspace)
+                        (const :tag "Managed" managed)
                         ;; For forward compatibility.
                         (other))
                        :value-type (sexp :tag "Value" nil))))
@@ -90,6 +91,7 @@ You can still make the X windows floating afterwards."
 (defvar exwm-manage--ping-lock nil
   "Non-nil indicates EXWM is pinging a window.")
 
+(defvar exwm-input--skip-buffer-list-update)
 (defvar exwm-input-prefix-keys)
 (defvar exwm-workspace--current)
 (defvar exwm-workspace--id-struts-alist)
@@ -200,7 +202,8 @@ You can still make the X windows floating afterwards."
         (make-instance 'xcb:ChangeSaveSet
                        :mode xcb:SetMode:Insert
                        :window id))
-    (with-current-buffer (generate-new-buffer "*EXWM*")
+    (with-current-buffer (let ((exwm-input--skip-buffer-list-update t))
+                           (generate-new-buffer "*EXWM*"))
       ;; Keep the oldest X window first.
       (setq exwm--id-buffer-alist
             (nconc exwm--id-buffer-alist `((,id . ,(current-buffer)))))
@@ -214,22 +217,33 @@ You can still make the X windows floating afterwards."
       (exwm--update-hints id)
       (exwm-manage--update-geometry id)
       (exwm-manage--update-mwm-hints id)
-      ;; No need to manage (please check OverrideRedirect outside)
-      (when (or
-             (not
-              (or (not exwm-window-type)
-                  (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY exwm-window-type)
-                  (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG exwm-window-type)
-                  (memq xcb:Atom:_NET_WM_WINDOW_TYPE_NORMAL exwm-window-type)))
-             ;; Check the _MOTIF_WM_HINTS property.
-             (and (not exwm--mwm-hints-decorations)
-                  (not exwm--hints-input)
-                  ;; Floating windows only
-                  (or exwm-transient-for exwm--fixed-size
-                      (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY
-                            exwm-window-type)
-                      (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG
-                            exwm-window-type))))
+      (exwm--update-title id)
+      (exwm--update-protocols id)
+      (setq exwm--configurations (exwm-manage--get-configurations))
+      ;; OverrideRedirect is not checked here.
+      (when (and
+             ;; The user has specified to manage it.
+             (not (plist-get exwm--configurations 'managed))
+             (or
+              ;; The user has specified not to manage it.
+              (plist-member exwm--configurations 'managed)
+              ;; This is not a type of X window we can manage.
+              (and exwm-window-type
+                   (not (cl-intersection
+                         exwm-window-type
+                         (list xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY
+                               xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG
+                               xcb:Atom:_NET_WM_WINDOW_TYPE_NORMAL))))
+              ;; Check the _MOTIF_WM_HINTS property to not manage floating X
+              ;; windows without decoration.
+              (and (not exwm--mwm-hints-decorations)
+                   (not exwm--hints-input)
+                   ;; Floating windows only
+                   (or exwm-transient-for exwm--fixed-size
+                       (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY
+                             exwm-window-type)
+                       (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG
+                             exwm-window-type)))))
         (exwm--log "No need to manage #x%x" id)
         ;; Update struts.
         (when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DOCK exwm-window-type)
@@ -274,10 +288,10 @@ You can still make the X windows floating afterwards."
                              :stack-mode xcb:StackMode:Below)))
         (xcb:flush exwm--connection)
         (setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist))
-        (let ((kill-buffer-query-functions nil))
+        (let ((kill-buffer-query-functions nil)
+              (exwm-input--skip-buffer-list-update t))
           (kill-buffer (current-buffer)))
         (throw 'return 'ignored))
-      (setq exwm--configurations (exwm-manage--get-configurations))
       (let ((index (plist-get exwm--configurations 'workspace)))
         (when (and index (< index (length exwm-workspace--list)))
           (setq exwm--frame (elt exwm-workspace--list index))))
@@ -299,8 +313,6 @@ You can still make the X windows floating afterwards."
                            :button button :modifiers xcb:ModMask:Any)))
       (exwm-manage--set-client-list)
       (xcb:flush exwm--connection)
-      (exwm--update-title id)
-      (exwm--update-protocols id)
       (if (plist-member exwm--configurations 'floating)
           ;; User has specified whether it should be floating.
           (if (plist-get exwm--configurations 'floating)
@@ -556,7 +568,7 @@ Would you like to kill it? "
     (with-slots (window x y width height
                         border-width sibling stack-mode value-mask)
         obj
-      (exwm--log "ConfigureRequest from #x%x (#x%x) @%dx%d%+d%+d; \
+      (exwm--log "#x%x (#x%x) @%dx%d%+d%+d; \
 border-width: %d; sibling: #x%x; stack-mode: %d"
                  window value-mask width height x y
                  border-width sibling stack-mode)
@@ -654,7 +666,7 @@ border-width: %d; sibling: #x%x; stack-mode: %d"
             (progn (xcb:+request exwm--connection
                        (make-instance 'xcb:MapWindow :window window))
                    (xcb:flush exwm--connection))
-          (exwm--log "MapRequest from #x%x" window)
+          (exwm--log "#x%x" window)
           (exwm-manage--manage-window window))))))
 
 (defun exwm-manage--on-UnmapNotify (data _synthetic)
@@ -674,11 +686,20 @@ border-width: %d; sibling: #x%x; stack-mode: %d"
         (exwm--log "id=#x%x" window)
         ;; With this we ensure that a "window hierarchy change" happens after
         ;; mapping the window, as some servers (XQuartz) do not generate it.
-        (xcb:+request exwm--connection
-            (make-instance 'xcb:ConfigureWindow
-                           :window window
-                           :value-mask xcb:ConfigWindow:StackMode
-                           :stack-mode xcb:StackMode:Above))
+        (with-current-buffer (exwm--id->buffer window)
+          (if exwm--floating-frame
+              (xcb:+request exwm--connection
+                  (make-instance 'xcb:ConfigureWindow
+                                 :window window
+                                 :value-mask xcb:ConfigWindow:StackMode
+                                 :stack-mode xcb:StackMode:Above))
+            (xcb:+request exwm--connection
+                (make-instance 'xcb:ConfigureWindow
+                               :window window
+                               :value-mask (logior xcb:ConfigWindow:Sibling
+                                                   xcb:ConfigWindow:StackMode)
+                               :sibling exwm--guide-window
+                               :stack-mode xcb:StackMode:Above))))
         (xcb:flush exwm--connection)))))
 
 (defun exwm-manage--on-DestroyNotify (data synthetic)
@@ -687,21 +708,14 @@ border-width: %d; sibling: #x%x; stack-mode: %d"
     (exwm--log)
     (let ((obj (make-instance 'xcb:DestroyNotify)))
       (xcb:unmarshal obj data)
-      (exwm--log "DestroyNotify from #x%x" (slot-value obj 'window))
+      (exwm--log "#x%x" (slot-value obj 'window))
       (exwm-manage--unmanage-window (slot-value obj 'window)))))
 
 (defun exwm-manage--init ()
   "Initialize manage module."
   ;; Intern _MOTIF_WM_HINTS
   (exwm--log)
-  (let ((atom-name "_MOTIF_WM_HINTS"))
-    (setq exwm-manage--_MOTIF_WM_HINTS
-          (slot-value (xcb:+request-unchecked+reply exwm--connection
-                          (make-instance 'xcb:InternAtom
-                                         :only-if-exists 0
-                                         :name-len (length atom-name)
-                                         :name atom-name))
-                      'atom)))
+  (setq exwm-manage--_MOTIF_WM_HINTS (exwm--intern-atom "_MOTIF_WM_HINTS"))
   (add-hook 'after-make-frame-functions #'exwm-manage--add-frame)
   (add-hook 'delete-frame-functions #'exwm-manage--remove-frame)
   (xcb:+event exwm--connection 'xcb:ConfigureRequest
@@ -714,6 +728,7 @@ border-width: %d; sibling: #x%x; stack-mode: %d"
 
 (defun exwm-manage--exit ()
   "Exit the manage module."
+  (exwm--log)
   (dolist (pair exwm--id-buffer-alist)
     (exwm-manage--unmanage-window (car pair) 'quit))
   (remove-hook 'after-make-frame-functions #'exwm-manage--add-frame)
diff --git a/exwm-randr.el b/exwm-randr.el
index af900ab36f0d..7d20022e9e53 100644
--- a/exwm-randr.el
+++ b/exwm-randr.el
@@ -1,6 +1,6 @@
 ;;; exwm-randr.el --- RandR Module for EXWM  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2018 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2019 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -27,11 +27,11 @@
 ;; that.
 
 ;; To use this module, load, enable it and configure
-;; `exwm-randr-workspace-output-plist' and `exwm-randr-screen-change-hook'
+;; `exwm-randr-workspace-monitor-plist' and `exwm-randr-screen-change-hook'
 ;; as follows:
 ;;
 ;;   (require 'exwm-randr)
-;;   (setq exwm-randr-workspace-output-plist '(0 "VGA1"))
+;;   (setq exwm-randr-workspace-monitor-plist '(0 "VGA1"))
 ;;   (add-hook 'exwm-randr-screen-change-hook
 ;;             (lambda ()
 ;;               (start-process-shell-command
@@ -48,7 +48,9 @@
 ;;; Code:
 
 (require 'xcb-randr)
+
 (require 'exwm-core)
+(require 'exwm-workspace)
 
 (defgroup exwm-randr nil
   "RandR."
@@ -63,83 +65,94 @@
   "Normal hook run when screen changes."
   :type 'hook)
 
-(defcustom exwm-randr-workspace-output-plist nil
-  "Plist mapping workspace to output.
+(defcustom exwm-randr-workspace-monitor-plist nil
+  "Plist mapping workspaces to monitors.
+
+In RandR 1.5 a monitor is a rectangle region decoupled from the physical
+size of screens, and can be identified with `xrandr --listmonitors' (name of
+the primary monitor is prefixed with an `*').  When no monitor is created it
+automatically fallback to RandR 1.2 output which represents the physical
+screen size.  RandR 1.5 monitors can be created with `xrandr --setmonitor'.
+For example, to split an output (`LVDS-1') of size 1280x800 into two
+side-by-side monitors one could invoke (the digits after `/' are size in mm)
 
-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.
+    xrandr --setmonitor *LVDS-1-L 640/135x800/163+0+0 LVDS-1
+    xrandr --setmonitor LVDS-1-R 640/135x800/163+640+0 none
 
-  '(1 \"HDMI-1\" 3 \"DP-1\")
+If a monitor is not active, the workspaces mapped to it are displayed on the
+primary monitor until it becomes active (if ever).  Unspecified workspaces
+are all mapped to the primary monitor.  For example, with the following
+setting workspace other than 1 and 3 would always be displayed on the
+primary monitor where workspace 1 and 3 would be displayed on their
+corresponding monitors whenever the monitors are active.
 
-The outputs available can be identified by running the 'xrandr' utility with
-the first one in result being the primary output."
+  \\='(1 \"HDMI-1\" 3 \"DP-1\")"
   :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-active "exwm-workspace.el"
-                  (frame active))
-(declare-function exwm-workspace--set-desktop-geometry "exwm-workspace.el" ())
-(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame))
-(declare-function exwm-workspace--show-minibuffer "exwm-workspace.el" ())
-(declare-function exwm-workspace--update-workareas "exwm-workspace.el" ())
-
-(defun exwm-randr--refresh ()
-  "Refresh workspaces according to the updated RandR info."
-  (let (output-name geometry output-plist primary-output default-geometry
-                    container-output-alist container-frame-alist)
-    ;; Query all outputs
-    (with-slots (config-timestamp outputs)
+(with-no-warnings
+  (define-obsolete-variable-alias 'exwm-randr-workspace-output-plist
+    'exwm-randr-workspace-monitor-plist "27.1"))
+
+(defvar exwm-randr--last-timestamp 0 "Used for debouncing events.")
+
+(defvar exwm-randr--prev-screen-change-seqnum nil
+  "The most recent ScreenChangeNotify sequence number.")
+
+(defun exwm-randr--get-monitors ()
+  "Get RandR monitors."
+  (exwm--log)
+  (let (monitor-name geometry monitor-plist primary-monitor)
+    (with-slots (timestamp monitors)
         (xcb:+request-unchecked+reply exwm--connection
-            (make-instance 'xcb:randr:GetScreenResourcesCurrent
-                           :window exwm--root))
-      (dolist (output outputs)
-        (with-slots (crtc connection name)
-            (xcb:+request-unchecked+reply exwm--connection
-                (make-instance 'xcb:randr:GetOutputInfo
-                               :output output
-                               :config-timestamp config-timestamp))
-          (setf output-name             ;UTF-8 encoded
-                (decode-coding-string (apply #'unibyte-string name) 'utf-8))
-          (if (or (/= connection xcb:randr:Connection:Connected)
-                  (= 0 crtc))           ;FIXME
-              (plist-put output-plist output-name nil)
-            (with-slots (x y width height)
-                (xcb:+request-unchecked+reply exwm--connection
-                    (make-instance 'xcb:randr:GetCrtcInfo
-                                   :crtc crtc
-                                   :config-timestamp config-timestamp))
-              (setq geometry (make-instance 'xcb:RECTANGLE
-                                            :x x :y y
-                                            :width width :height height)
-                    output-plist (plist-put output-plist output-name geometry))
-              (unless primary-output
-                (setq primary-output output-name
-                      default-geometry geometry)))))))
-    (exwm--log "(randr) outputs: %s" output-plist)
-    (when output-plist
+            (make-instance 'xcb:randr:GetMonitors
+                           :window exwm--root
+                           :get-active 1))
+      (when (> timestamp exwm-randr--last-timestamp)
+        (setq exwm-randr--last-timestamp timestamp))
+      (dolist (monitor monitors)
+        (with-slots (name primary x y width height) monitor
+          (setq monitor-name (x-get-atom-name name)
+                geometry (make-instance 'xcb:RECTANGLE
+                                        :x x
+                                        :y y
+                                        :width width
+                                        :height height)
+                monitor-plist (plist-put monitor-plist monitor-name geometry))
+          (exwm--log "%s: %sx%s+%s+%s" monitor-name x y width height)
+          ;; Save primary monitor when available (fallback to the first one).
+          (when (or (/= 0 primary)
+                    (not primary-monitor))
+            (setq primary-monitor monitor-name)))))
+    (exwm--log "Primary monitor: %s" primary-monitor)
+    (list primary-monitor monitor-plist)))
+
+;;;###autoload
+(defun exwm-randr-refresh ()
+  "Refresh workspaces according to the updated RandR info."
+  (interactive)
+  (exwm--log)
+  (let* ((result (exwm-randr--get-monitors))
+         (primary-monitor (elt result 0))
+         (monitor-plist (elt result 1))
+         container-monitor-alist container-frame-alist)
+    (when (and primary-monitor monitor-plist)
       (when exwm-workspace--fullscreen-frame-count
         ;; Not all workspaces are fullscreen; reset this counter.
         (setq exwm-workspace--fullscreen-frame-count 0))
       (dotimes (i (exwm-workspace--count))
-        (let* ((output (plist-get exwm-randr-workspace-output-plist i))
-               (geometry (lax-plist-get output-plist output))
+        (let* ((monitor (plist-get exwm-randr-workspace-monitor-plist i))
+               (geometry (lax-plist-get monitor-plist monitor))
                (frame (elt exwm-workspace--list i))
                (container (frame-parameter frame 'exwm-container)))
           (unless geometry
-            (setq geometry default-geometry
-                  output primary-output))
-          (setq container-output-alist (nconc
-                                        `((,container . ,(intern output)))
-                                        container-output-alist)
+            (setq monitor primary-monitor
+                  geometry (lax-plist-get monitor-plist primary-monitor)))
+          (setq container-monitor-alist (nconc
+                                         `((,container . ,(intern monitor)))
+                                         container-monitor-alist)
                 container-frame-alist (nconc `((,container . ,frame))
                                              container-frame-alist))
-          (set-frame-parameter frame 'exwm-randr-output output)
+          (set-frame-parameter frame 'exwm-randr-monitor monitor)
           (set-frame-parameter frame 'exwm-geometry geometry)))
       ;; Update workareas.
       (exwm-workspace--update-workareas)
@@ -156,72 +169,114 @@ the first one in result being the primary output."
       ;; Update active/inactive workspaces.
       (dolist (w exwm-workspace--list)
         (exwm-workspace--set-active w nil))
+      ;; Mark the workspace on the top of each monitor as active.
       (dolist (xwin
                (reverse
                 (slot-value (xcb:+request-unchecked+reply exwm--connection
                                 (make-instance 'xcb:QueryTree
                                                :window exwm--root))
                             'children)))
-        (let ((output (cdr (assq xwin container-output-alist))))
-          (when output
-            (setq container-output-alist
-                  (rassq-delete-all output container-output-alist))
+        (let ((monitor (cdr (assq xwin container-monitor-alist))))
+          (when monitor
+            (setq container-monitor-alist
+                  (rassq-delete-all monitor container-monitor-alist))
             (exwm-workspace--set-active (cdr (assq xwin container-frame-alist))
                                         t))))
       (xcb:flush exwm--connection)
       (run-hooks 'exwm-randr-refresh-hook))))
 
-(defun exwm-randr--on-ScreenChangeNotify (_data _synthetic)
-  (exwm--log "(RandR) ScreenChangeNotify")
-  (run-hooks 'exwm-randr-screen-change-hook)
-  (exwm-randr--refresh))
+(define-obsolete-function-alias 'exwm-randr--refresh #'exwm-randr-refresh
+  "27.1")
+
+(defun exwm-randr--on-ScreenChangeNotify (data _synthetic)
+  "Handle `ScreenChangeNotify' event.
+
+Run `exwm-randr-screen-change-hook' (usually user scripts to configure RandR)."
+  (exwm--log)
+  (let ((evt (make-instance 'xcb:randr:ScreenChangeNotify)))
+    (xcb:unmarshal evt data)
+    (let ((seqnum (slot-value evt '~sequence)))
+      (unless (equal seqnum exwm-randr--prev-screen-change-seqnum)
+        (setq exwm-randr--prev-screen-change-seqnum seqnum)
+        (run-hooks 'exwm-randr-screen-change-hook)))))
+
+(defun exwm-randr--on-Notify (data _synthetic)
+  "Handle `CrtcChangeNotify' and `OutputChangeNotify' events.
+
+Refresh when any CRTC/output changes."
+  (exwm--log)
+  (let ((evt (make-instance 'xcb:randr:Notify))
+        notify)
+    (xcb:unmarshal evt data)
+    (with-slots (subCode u) evt
+      (cl-case subCode
+        (xcb:randr:Notify:CrtcChange
+         (setq notify (slot-value u 'cc)))
+        (xcb:randr:Notify:OutputChange
+         (setq notify (slot-value u 'oc))))
+      (when notify
+        (with-slots (timestamp) notify
+          (when (> timestamp exwm-randr--last-timestamp)
+            (exwm-randr-refresh)
+            (setq exwm-randr--last-timestamp timestamp)))))))
+
+(defun exwm-randr--on-ConfigureNotify (data _synthetic)
+  "Handle `ConfigureNotify' event.
+
+Refresh when any RandR 1.5 monitor changes."
+  (exwm--log)
+  (let ((evt (make-instance 'xcb:ConfigureNotify)))
+    (xcb:unmarshal evt data)
+    (with-slots (window) evt
+      (when (eq window exwm--root)
+        (exwm-randr-refresh)))))
 
 (defun exwm-randr--init ()
   "Initialize RandR extension and EXWM RandR module."
+  (exwm--log)
   (if (= 0 (slot-value (xcb:get-extension-data exwm--connection 'xcb:randr)
                        'present))
       (error "[EXWM] RandR extension is not supported by the server")
     (with-slots (major-version minor-version)
         (xcb:+request-unchecked+reply exwm--connection
             (make-instance 'xcb:randr:QueryVersion
-                           :major-version 1 :minor-version 3))
-      (if (or (/= major-version 1) (< minor-version 3))
+                           :major-version 1 :minor-version 5))
+      (if (or (/= major-version 1) (< minor-version 5))
           (error "[EXWM] The server only support RandR version up to %d.%d"
                  major-version minor-version)
         ;; External monitor(s) may already be connected.
         (run-hooks 'exwm-randr-screen-change-hook)
-        (exwm-randr--refresh)
+        (exwm-randr-refresh)
+        ;; Listen for `ScreenChangeNotify' to notify external tools to
+        ;; configure RandR and `CrtcChangeNotify/OutputChangeNotify' to
+        ;; refresh the workspace layout.
         (xcb:+event exwm--connection 'xcb:randr:ScreenChangeNotify
                     #'exwm-randr--on-ScreenChangeNotify)
-        ;; (xcb:+event exwm--connection 'xcb:randr:Notify
-        ;;             (lambda (_data _synthetic)
-        ;;               (exwm--log "(RandR) Notify")
-        ;;               (exwm-randr--refresh)))
+        (xcb:+event exwm--connection 'xcb:randr:Notify #'exwm-randr--on-Notify)
+        (xcb:+event exwm--connection 'xcb:ConfigureNotify
+                    #'exwm-randr--on-ConfigureNotify)
         (xcb:+request exwm--connection
             (make-instance 'xcb:randr:SelectInput
                            :window exwm--root
-                           :enable xcb:randr:NotifyMask:ScreenChange
-                           ;; :enable (eval-when-compile
-                           ;;           (logior
-                           ;;            xcb:randr:NotifyMask:ScreenChange
-                           ;;            xcb:randr:NotifyMask:OutputChange
-                           ;;            xcb:randr:NotifyMask:OutputProperty
-                           ;;            xcb:randr:NotifyMask:CrtcChange))
-                           ))
+                           :enable (logior xcb:randr:NotifyMask:ScreenChange
+                                           xcb:randr:NotifyMask:CrtcChange
+                                           xcb:randr:NotifyMask:OutputChange)))
         (xcb:flush exwm--connection)
-        (add-hook 'exwm-workspace-list-change-hook #'exwm-randr--refresh))))
+        (add-hook 'exwm-workspace-list-change-hook #'exwm-randr-refresh))))
   ;; Prevent frame parameters introduced by this module from being
   ;; saved/restored.
-  (dolist (i '(exwm-randr-output))
+  (dolist (i '(exwm-randr-monitor))
     (unless (assq i frameset-filter-alist)
       (push (cons i :never) frameset-filter-alist))))
 
 (defun exwm-randr--exit ()
   "Exit the RandR module."
-  (remove-hook 'exwm-workspace-list-change-hook #'exwm-randr--refresh))
+  (exwm--log)
+  (remove-hook 'exwm-workspace-list-change-hook #'exwm-randr-refresh))
 
 (defun exwm-randr-enable ()
   "Enable RandR support for EXWM."
+  (exwm--log)
   (add-hook 'exwm-init-hook #'exwm-randr--init)
   (add-hook 'exwm-exit-hook #'exwm-randr--exit))
 
diff --git a/exwm-systemtray.el b/exwm-systemtray.el
index d3244ab8d088..80505c22a555 100644
--- a/exwm-systemtray.el
+++ b/exwm-systemtray.el
@@ -1,7 +1,7 @@
 ;;; exwm-systemtray.el --- System Tray Module for  -*- lexical-binding: t -*-
 ;;;                        EXWM
 
-;; Copyright (C) 2016-2018 Free Software Foundation, Inc.
+;; Copyright (C) 2016-2019 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -33,7 +33,9 @@
 (require 'xcb-icccm)
 (require 'xcb-xembed)
 (require 'xcb-systemtray)
+
 (require 'exwm-core)
+(require 'exwm-workspace)
 
 (defclass exwm-systemtray--icon ()
   ((width :initarg :width)
@@ -77,22 +79,17 @@ You shall use the default value if using auto-hide minibuffer."
 (defvar exwm-systemtray--selection-owner-window nil
   "The selection owner 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--minibuffer-own-frame-p "exwm-workspace.el")
 
 (defun exwm-systemtray--embed (icon)
   "Embed an icon."
-  (exwm--log "(System Tray) Try to embed #x%x" icon)
+  (exwm--log "Try to embed #x%x" icon)
   (let ((info (xcb:+request-unchecked+reply exwm-systemtray--connection
                   (make-instance 'xcb:xembed:get-_XEMBED_INFO
                                  :window icon)))
         width* height* visible)
     (when info
-      (exwm--log "(System Tray) Embed #x%x" icon)
+      (exwm--log "Embed #x%x" icon)
       (with-slots (width height)
           (xcb:+request-unchecked+reply exwm-systemtray--connection
               (make-instance 'xcb:GetGeometry :drawable icon))
@@ -101,7 +98,7 @@ You shall use the default value if using auto-hide minibuffer."
         (when (< width* exwm-systemtray--icon-min-size)
           (setq width* exwm-systemtray--icon-min-size
                 height* (round (* height (/ (float width*) width)))))
-        (exwm--log "(System Tray) Resize from %dx%d to %dx%d"
+        (exwm--log "Resize from %dx%d to %dx%d"
                    width height width* height*))
       ;; Add this icon to save-set.
       (xcb:+request exwm-systemtray--connection
@@ -151,7 +148,7 @@ You shall use the default value if using auto-hide minibuffer."
         ;; Default to visible.
         (setq visible t))
       (when visible
-        (exwm--log "(System Tray) Map the window")
+        (exwm--log "Map the window")
         (xcb:+request exwm-systemtray--connection
             (make-instance 'xcb:MapWindow :window icon)))
       (xcb:+request exwm-systemtray--connection
@@ -175,7 +172,7 @@ You shall use the default value if using auto-hide minibuffer."
 
 (defun exwm-systemtray--unembed (icon)
   "Unembed an icon."
-  (exwm--log "(System Tray) Unembed #x%x" icon)
+  (exwm--log "Unembed #x%x" icon)
   (xcb:+request exwm-systemtray--connection
       (make-instance 'xcb:UnmapWindow :window icon))
   (xcb:+request exwm-systemtray--connection
@@ -189,6 +186,7 @@ You shall use the default value if using auto-hide minibuffer."
 
 (defun exwm-systemtray--refresh ()
   "Refresh the system tray."
+  (exwm--log)
   ;; Make sure to redraw the embedder.
   (xcb:+request exwm-systemtray--connection
       (make-instance 'xcb:UnmapWindow
@@ -222,6 +220,7 @@ You shall use the default value if using auto-hide minibuffer."
 
 (defun exwm-systemtray--on-DestroyNotify (data _synthetic)
   "Unembed icons on DestroyNotify."
+  (exwm--log)
   (let ((obj (make-instance 'xcb:DestroyNotify)))
     (xcb:unmarshal obj data)
     (with-slots (window) obj
@@ -230,6 +229,7 @@ You shall use the default value if using auto-hide minibuffer."
 
 (defun exwm-systemtray--on-ReparentNotify (data _synthetic)
   "Unembed icons on ReparentNotify."
+  (exwm--log)
   (let ((obj (make-instance 'xcb:ReparentNotify)))
     (xcb:unmarshal obj data)
     (with-slots (window parent) obj
@@ -239,6 +239,7 @@ You shall use the default value if using auto-hide minibuffer."
 
 (defun exwm-systemtray--on-ResizeRequest (data _synthetic)
   "Resize the tray icon on ResizeRequest."
+  (exwm--log)
   (let ((obj (make-instance 'xcb:ResizeRequest))
         attr)
     (xcb:unmarshal obj data)
@@ -266,6 +267,7 @@ You shall use the default value if using auto-hide minibuffer."
 
 (defun exwm-systemtray--on-PropertyNotify (data _synthetic)
   "Map/Unmap the tray icon on PropertyNotify."
+  (exwm--log)
   (let ((obj (make-instance 'xcb:PropertyNotify))
         attr info visible)
     (xcb:unmarshal obj data)
@@ -279,7 +281,7 @@ You shall use the default value if using auto-hide minibuffer."
         (when info
           (setq visible (/= 0 (logand (slot-value info 'flags)
                                       xcb:xembed:MAPPED)))
-          (exwm--log "(System Tray) #x%x visible? %s" window visible)
+          (exwm--log "#x%x visible? %s" window visible)
           (if visible
               (xcb:+request exwm-systemtray--connection
                   (make-instance 'xcb:MapWindow :window window))
@@ -297,6 +299,7 @@ You shall use the default value if using auto-hide minibuffer."
       (when (eq type xcb:Atom:_NET_SYSTEM_TRAY_OPCODE)
         (setq data32 (slot-value data 'data32)
               opcode (elt data32 1))
+        (exwm--log "opcode: %s" opcode)
         (cond ((= opcode xcb:systemtray:opcode:REQUEST-DOCK)
                (unless (assoc (elt data32 2) exwm-systemtray--list)
                  (exwm-systemtray--embed (elt data32 2))))
@@ -304,10 +307,11 @@ You shall use the default value if using auto-hide minibuffer."
               ((or (= opcode xcb:systemtray:opcode:BEGIN-MESSAGE)
                    (= opcode xcb:systemtray:opcode:CANCEL-MESSAGE)))
               (t
-               (exwm--log "(System Tray) Unknown opcode message: %s" obj)))))))
+               (exwm--log "Unknown opcode message: %s" obj)))))))
 
 (defun exwm-systemtray--on-KeyPress (data _synthetic)
   "Forward all KeyPress events to Emacs frame."
+  (exwm--log)
   ;; This function is only executed when there's no autohide minibuffer,
   ;; a workspace frame has the input focus and the pointer is over a
   ;; tray icon.
@@ -325,6 +329,7 @@ You shall use the default value if using auto-hide minibuffer."
 
 (defun exwm-systemtray--on-workspace-switch ()
   "Reparent/Refresh the system tray in `exwm-workspace-switch-hook'."
+  (exwm--log)
   (unless (exwm-workspace--minibuffer-own-frame-p)
     (xcb:+request exwm-systemtray--connection
         (make-instance 'xcb:ReparentWindow
@@ -339,6 +344,7 @@ You shall use the default value if using auto-hide minibuffer."
 
 (defun exwm-systemtray--on-randr-refresh ()
   "Reposition/Refresh the system tray in `exwm-randr-refresh-hook'."
+  (exwm--log)
   (unless (exwm-workspace--minibuffer-own-frame-p)
     (xcb:+request exwm-systemtray--connection
         (make-instance 'xcb:ConfigureWindow
@@ -353,6 +359,7 @@ You shall use the default value if using auto-hide minibuffer."
 
 (cl-defun exwm-systemtray--init ()
   "Initialize system tray module."
+  (exwm--log)
   (cl-assert (not exwm-systemtray--connection))
   (cl-assert (not exwm-systemtray--list))
   (cl-assert (not exwm-systemtray--selection-owner-window))
@@ -493,6 +500,7 @@ You shall use the default value if using auto-hide minibuffer."
 
 (defun exwm-systemtray--exit ()
   "Exit the systemtray module."
+  (exwm--log)
   (when exwm-systemtray--connection
     ;; Hide & reparent out the embedder before disconnection to prevent
     ;; embedded icons from being reparented to an Emacs frame (which is the
@@ -521,6 +529,7 @@ You shall use the default value if using auto-hide minibuffer."
 
 (defun exwm-systemtray-enable ()
   "Enable system tray support for EXWM."
+  (exwm--log)
   (add-hook 'exwm-init-hook #'exwm-systemtray--init)
   (add-hook 'exwm-exit-hook #'exwm-systemtray--exit))
 
diff --git a/exwm-workspace.el b/exwm-workspace.el
index d58758fc1f01..783287366aff 100644
--- a/exwm-workspace.el
+++ b/exwm-workspace.el
@@ -1,6 +1,6 @@
 ;;; exwm-workspace.el --- Workspace Module for EXWM  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2018 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2019 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -308,7 +308,8 @@ NIL if FRAME is not a workspace"
                 ;; Make left/top processed first.
                 (push struts* exwm-workspace--struts)
               (setq exwm-workspace--struts
-                    (append exwm-workspace--struts (list struts*))))))))))
+                    (append exwm-workspace--struts (list struts*))))))))
+    (exwm--log "%s" exwm-workspace--struts)))
 
 (defun exwm-workspace--update-workareas ()
   "Update `exwm-workspace--workareas'."
@@ -372,10 +373,11 @@ NIL if FRAME is not a workspace"
     ;; Save the result.
     (setq exwm-workspace--workareas workareas)
     (xcb:flush exwm--connection))
+  (exwm--log "%s" exwm-workspace--workareas)
   (run-hooks 'exwm-workspace--update-workareas-hook))
 
 (defun exwm-workspace--set-active (frame active)
-  "Make frame FRAME active on its output."
+  "Make frame FRAME active on its monitor."
   (exwm--log "active=%s; frame=%s" frame active)
   (set-frame-parameter frame 'exwm-active active)
   (if active
@@ -452,7 +454,8 @@ NIL if FRAME is not a workspace"
                        :window (frame-parameter exwm-workspace--minibuffer
                                                 'exwm-outer-id)
                        :value-mask xcb:ConfigWindow:Width
-                       :width width))))
+                       :width width))
+    (exwm--log "y: %s, width: %s" y width)))
 
 (defun exwm-workspace--switch-map-nth-prefix (&optional prefix-digits)
   "Allow selecting a workspace by number.
@@ -544,15 +547,15 @@ for internal use only."
         (set-frame-parameter (buffer-local-value 'exwm--frame (window-buffer))
                              'exwm-selected-window (selected-window)))
       ;; Show/Hide X windows.
-      (let ((output-old (frame-parameter old-frame 'exwm-randr-output))
-            (output-new (frame-parameter frame 'exwm-randr-output))
+      (let ((monitor-old (frame-parameter old-frame 'exwm-randr-monitor))
+            (monitor-new (frame-parameter frame 'exwm-randr-monitor))
             (active-old (exwm-workspace--active-p old-frame))
             (active-new (exwm-workspace--active-p frame))
             workspaces-to-hide)
         (cond
          ((not active-old)
           (exwm-workspace--set-active frame t))
-         ((equal output-old output-new)
+         ((equal monitor-old monitor-new)
           (exwm-workspace--set-active frame t)
           (unless (eq frame old-frame)
             (exwm-workspace--set-active old-frame nil)
@@ -561,8 +564,8 @@ for internal use only."
          (t
           (dolist (w exwm-workspace--list)
             (when (and (exwm-workspace--active-p w)
-                       (equal output-new
-                              (frame-parameter w 'exwm-randr-output)))
+                       (equal monitor-new
+                              (frame-parameter w 'exwm-randr-monitor)))
               (exwm-workspace--set-active w nil)
               (setq workspaces-to-hide (append workspaces-to-hide (list w)))))
           (exwm-workspace--set-active frame t)))
@@ -648,6 +651,7 @@ Passing a workspace frame as the first option is for internal use only."
      (t 0))))
   (unless frame-or-index
     (setq frame-or-index 0))
+  (exwm--log "%s" frame-or-index)
   (if (or (framep frame-or-index)
           (< frame-or-index (exwm-workspace--count)))
       (exwm-workspace-switch frame-or-index)
@@ -748,6 +752,7 @@ before it."
 
 INDEX must not exceed the current number of workspaces."
   (interactive)
+  (exwm--log "%s" index)
   (if (and index
            ;; No need to move if it's the last one.
            (< index (exwm-workspace--count)))
@@ -758,6 +763,7 @@ INDEX must not exceed the current number of workspaces."
 (defun exwm-workspace-delete (&optional frame-or-index)
   "Delete the workspace FRAME-OR-INDEX."
   (interactive)
+  (exwm--log "%s" frame-or-index)
   (when (< 1 (exwm-workspace--count))
     (delete-frame
      (if frame-or-index
@@ -766,6 +772,7 @@ INDEX must not exceed the current number of workspaces."
 
 (defun exwm-workspace--set-desktop (id)
   "Set _NET_WM_DESKTOP for X window ID."
+  (exwm--log "#x%x" id)
   (with-current-buffer (exwm--id->buffer id)
     (let ((desktop (exwm-workspace--position exwm--frame)))
       (setq exwm--desktop desktop)
@@ -790,6 +797,7 @@ INDEX must not exceed the current number of workspaces."
   (let ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index))
         old-frame container)
     (unless id (setq id (exwm--buffer->id (window-buffer))))
+    (exwm--log "Moving #x%x to %s" id frame-or-index)
     (with-current-buffer (exwm--id->buffer id)
       (unless (eq exwm--frame frame)
         (unless exwm-workspace-show-all-buffers
@@ -819,8 +827,8 @@ INDEX must not exceed the current number of workspaces."
           ;; Floating.
           (setq container (frame-parameter exwm--floating-frame
                                            'exwm-container))
-          (unless (equal (frame-parameter old-frame 'exwm-randr-output)
-                         (frame-parameter frame 'exwm-randr-output))
+          (unless (equal (frame-parameter old-frame 'exwm-randr-monitor)
+                         (frame-parameter frame 'exwm-randr-monitor))
             (with-slots (x y)
                 (xcb:+request-unchecked+reply exwm--connection
                     (make-instance 'xcb:GetGeometry
@@ -985,6 +993,7 @@ INDEX must not exceed the current number of workspaces."
 
 (defun exwm-workspace--x-create-frame (orig-fun params)
   "Set override-redirect on the frame created by `x-create-frame'."
+  (exwm--log)
   (let ((frame (funcall orig-fun params)))
     (xcb:+request exwm--connection
         (make-instance 'xcb:ChangeWindowAttributes
@@ -1006,6 +1015,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
 (defun exwm-workspace-attach-minibuffer ()
   "Attach the minibuffer so that it always shows."
   (interactive)
+  (exwm--log)
   (when (and (exwm-workspace--minibuffer-own-frame-p)
              (not (exwm-workspace--minibuffer-attached-p)))
     ;; Reset the frame size.
@@ -1030,6 +1040,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
 (defun exwm-workspace-detach-minibuffer ()
   "Detach the minibuffer so that it automatically hides."
   (interactive)
+  (exwm--log)
   (when (and (exwm-workspace--minibuffer-own-frame-p)
              (exwm-workspace--minibuffer-attached-p))
     (setq exwm-workspace--attached-minibuffer-height 0)
@@ -1047,6 +1058,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
 (defun exwm-workspace-toggle-minibuffer ()
   "Attach the minibuffer if it's detached, or detach it if it's attached."
   (interactive)
+  (exwm--log)
   (when (exwm-workspace--minibuffer-own-frame-p)
     (if (exwm-workspace--minibuffer-attached-p)
         (exwm-workspace-detach-minibuffer)
@@ -1072,6 +1084,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
       (when (and (integerp max-mini-window-height)
                  (> height max-mini-window-height))
         (setq height max-mini-window-height))
+      (exwm--log "%s" height)
       (set-frame-height exwm-workspace--minibuffer height))))
 
 (defun exwm-workspace--on-ConfigureNotify (data _synthetic)
@@ -1082,6 +1095,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
     (with-slots (window height) obj
       (when (eq (frame-parameter exwm-workspace--minibuffer 'exwm-outer-id)
                 window)
+        (exwm--log)
         (when (and (floatp max-mini-window-height)
                    (> height (* max-mini-window-height
                                 (exwm-workspace--current-height))))
@@ -1123,6 +1137,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
 
 (defun exwm-workspace--show-minibuffer ()
   "Show the minibuffer frame."
+  (exwm--log)
   ;; Cancel pending timer.
   (when exwm-workspace--display-echo-area-timer
     (cancel-timer exwm-workspace--display-echo-area-timer)
@@ -1144,6 +1159,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
 
 (defun exwm-workspace--hide-minibuffer ()
   "Hide the minibuffer frame."
+  (exwm--log)
   ;; Hide the minibuffer frame.
   (if (exwm-workspace--minibuffer-attached-p)
       (xcb:+request exwm--connection
@@ -1165,6 +1181,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
 
 (defun exwm-workspace--on-minibuffer-setup ()
   "Run in minibuffer-setup-hook to show the minibuffer and its container."
+  (exwm--log)
   (when (and (= 1 (minibuffer-depth))
              (not (exwm-workspace--client-p)))
     (add-hook 'post-command-hook #'exwm-workspace--update-minibuffer-height)
@@ -1186,6 +1203,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
 
 (defun exwm-workspace--on-minibuffer-exit ()
   "Run in minibuffer-exit-hook to hide the minibuffer container."
+  (exwm--log)
   (when (and (= 1 (minibuffer-depth))
              (not (exwm-workspace--client-p)))
     (remove-hook 'post-command-hook #'exwm-workspace--update-minibuffer-height)
@@ -1228,6 +1246,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
 
 (defun exwm-workspace--set-desktop-geometry ()
   "Set _NET_DESKTOP_GEOMETRY."
+  (exwm--log)
   ;; We don't support large desktop so it's the same with screen size.
   (xcb:+request exwm--connection
       (make-instance 'xcb:ewmh:set-_NET_DESKTOP_GEOMETRY
@@ -1237,6 +1256,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
 
 (defun exwm-workspace--add-frame-as-workspace (frame)
   "Configure frame FRAME to be treated as a workspace."
+  (exwm--log "%s" frame)
   (setq exwm-workspace--list (nconc exwm-workspace--list (list frame)))
   (let ((outer-id (string-to-number (frame-parameter frame
                                                      'outer-window-id)))
@@ -1252,7 +1272,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
     ;; prevent potential problems.  The values do not matter here as
     ;; they'll be updated by the RandR module later.
     (let ((w (car exwm-workspace--list)))
-      (dolist (param '(exwm-randr-output
+      (dolist (param '(exwm-randr-monitor
                        exwm-geometry))
         (set-frame-parameter frame param (frame-parameter w param))))
     (xcb:+request exwm--connection
@@ -1401,6 +1421,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
 
 (defun exwm-workspace--update-ewmh-props ()
   "Update EWMH properties to match the workspace list."
+  (exwm--log)
   (let ((num-workspaces (exwm-workspace--count)))
     ;; Avoid setting 0 desktops.
     (when (= 0 num-workspaces)
@@ -1420,6 +1441,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
 NEW-X-PARAMETERS is an alist of frame parameters, merged into current
 `window-system-default-frame-alist' for the X Window System.  The parameters are
 applied to all subsequently created X frames."
+  (exwm--log)
   ;; The parameters are modified in place; take current
   ;; ones or insert a new X-specific list.
   (let ((x-parameters (or (assq 'x window-system-default-frame-alist)
@@ -1439,6 +1461,7 @@ applied to all subsequently created X frames."
   (interactive "e"))
 
 (defun exwm-workspace--init-minibuffer-frame ()
+  (exwm--log)
   ;; Initialize workspaces without minibuffers.
   (setq exwm-workspace--minibuffer
         (make-frame '((window-system . x) (minibuffer . only)
@@ -1509,6 +1532,7 @@ applied to all subsequently created X frames."
               :test #'equal))
 
 (defun exwm-workspace--exit-minibuffer-frame ()
+  (exwm--log)
   ;; Only on minibuffer-frame.
   (remove-hook 'minibuffer-setup-hook #'exwm-workspace--on-minibuffer-setup)
   (remove-hook 'minibuffer-exit-hook #'exwm-workspace--on-minibuffer-exit)
@@ -1532,6 +1556,7 @@ applied to all subsequently created X frames."
 
 (defun exwm-workspace--init ()
   "Initialize workspace module."
+  (exwm--log)
   ;; Prevent unexpected exit
   (setq exwm-workspace--fullscreen-frame-count 0)
   (exwm-workspace--modify-all-x-frames-parameters
@@ -1599,6 +1624,7 @@ applied to all subsequently created X frames."
 
 (defun exwm-workspace--exit ()
   "Exit the workspace module."
+  (exwm--log)
   (when (exwm-workspace--minibuffer-own-frame-p)
     (exwm-workspace--exit-minibuffer-frame))
   (advice-remove 'x-create-frame #'exwm-workspace--x-create-frame)
@@ -1636,6 +1662,7 @@ applied to all subsequently created X frames."
 
 (defun exwm-workspace--post-init ()
   "The second stage in the initialization of the workspace module."
+  (exwm--log)
   (when exwm-workspace--client
     ;; Reset the 'fullscreen' frame parameter to make emacsclinet frames
     ;; fullscreen (even without the RandR module enabled).
diff --git a/exwm-xim.el b/exwm-xim.el
new file mode 100644
index 000000000000..6a213acc0cb5
--- /dev/null
+++ b/exwm-xim.el
@@ -0,0 +1,781 @@
+;;; exwm-xim.el --- XIM Module for EXWM  -*- lexical-binding: t -*-
+
+;; Copyright (C) 2019 Free Software Foundation, Inc.
+
+;; Author: Chris Feng <chris.w.feng@gmail.com>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This module adds XIM support for EXWM and allows sending characters
+;; generated by any Emacs's builtin input method (info node `Input Methods')
+;; to X windows.
+
+;; This module is essentially an X input method server utilizing Emacs as
+;; its backend.  It talks with X windows through the XIM protocol.  The XIM
+;; protocol is quite flexible by itself, stating that an implementation can
+;; create network connections of various types as well as make use of an
+;; existing X connection for communication, and that an IM server may
+;; support multiple transport versions, various input styles and several
+;; event flow modals, etc.  Here we only make choices that are most popular
+;; among other IM servers and more importantly, practical for Emacs to act
+;; as an IM server:
+;;
+;; + Packets are transported on top of an X connection like most IMEs.
+;; + Only transport version 0.0 (i.e. only-CM & Property-with-CM) is
+;;   supported (same as "IM Server Developers Kit", adopted by most IMEs).
+;; + Only support static event flow, on-demand-synchronous method.
+;; + Only "root-window" input style is supported.
+
+;; To use this module, first load and enable it as follows:
+;;
+;;    (require 'exwm-xim)
+;;    (exwm-xim-enable)
+;;
+;; A keybinding for `toggle-input-method' is probably required to turn on &
+;; off an input method (default to `default-input-method').  It's bound to
+;; 'C-\' by default and can be made reachable when working with X windows:
+;;
+;;    (push ?\C-\\ exwm-input-prefix-keys)
+;;
+;; It's also required (and error-prone) to setup environment variables to
+;; make applications actually use this input method.  Typically the
+;; following lines should be inserted into '~/.xinitrc'.
+;;
+;;    export XMODIFIERS=@im=exwm-xim
+;;    export GTK_IM_MODULE=xim
+;;    export QT_IM_MODULE=xim
+;;    export CLUTTER_IM_MODULE=xim
+
+;; References:
+;; + XIM (http://www.x.org/releases/X11R7.6/doc/libX11/specs/XIM/xim.html)
+;; + IMdkit (http://xorg.freedesktop.org/archive/unsupported/lib/IMdkit/)
+;; + UIM (https://github.com/uim/uim)
+
+;;; Code:
+
+(eval-when-compile (require 'cl-lib))
+
+(require 'xcb-keysyms)
+(require 'xcb-xim)
+
+(require 'exwm-core)
+(require 'exwm-input)
+
+(defconst exwm-xim--locales
+  "@locale=\
+aa,af,ak,am,an,anp,ar,as,ast,ayc,az,be,bem,ber,bg,bhb,bho,bn,bo,br,brx,bs,byn,\
+ca,ce,cmn,crh,cs,csb,cv,cy,da,de,doi,dv,dz,el,en,es,et,eu,fa,ff,fi,fil,fo,fr,\
+fur,fy,ga,gd,gez,gl,gu,gv,ha,hak,he,hi,hne,hr,hsb,ht,hu,hy,ia,id,ig,ik,is,it,\
+iu,iw,ja,ka,kk,kl,km,kn,ko,kok,ks,ku,kw,ky,lb,lg,li,li,lij,lo,lt,lv,lzh,mag,\
+mai,mg,mhr,mi,mk,ml,mn,mni,mr,ms,mt,my,nan,nb,nds,ne,nhn,niu,nl,nn,nr,nso,oc,\
+om,or,os,pa,pa,pap,pl,ps,pt,quz,raj,ro,ru,rw,sa,sat,sc,sd,se,shs,si,sid,sk,sl,\
+so,sq,sr,ss,st,sv,sw,szl,ta,tcy,te,tg,th,the,ti,tig,tk,tl,tn,tr,ts,tt,ug,uk,\
+unm,ur,uz,ve,vi,wa,wae,wal,wo,xh,yi,yo,yue,zh,zu,\
+C,no"
+  "All supported locales (stolen from glibc).")
+
+(defconst exwm-xim--default-error
+  (make-instance 'xim:error
+                 :im-id 0
+                 :ic-id 0
+                 :flag xim:error-flag:invalid-both
+                 :error-code xim:error-code:bad-something
+                 :length 0
+                 :type 0
+                 :detail nil)
+  "Default error returned to clients.")
+
+(defconst exwm-xim--default-im-attrs
+  (list (make-instance 'xim:XIMATTR
+                       :id 0
+                       :type xim:ATTRIBUTE-VALUE-TYPE:xim-styles
+                       :length (length xlib:XNQueryInputStyle)
+                       :attribute xlib:XNQueryInputStyle))
+  "Default IM attrs returned to clients.")
+
+(defconst exwm-xim--default-ic-attrs
+  (list (make-instance 'xim:XICATTR
+                       :id 0
+                       :type xim:ATTRIBUTE-VALUE-TYPE:long-data
+                       :length (length xlib:XNInputStyle)
+                       :attribute xlib:XNInputStyle)
+        (make-instance 'xim:XICATTR
+                       :id 1
+                       :type xim:ATTRIBUTE-VALUE-TYPE:window
+                       :length (length xlib:XNClientWindow)
+                       :attribute xlib:XNClientWindow)
+        ;; Required by e.g. xterm.
+        (make-instance 'xim:XICATTR
+                       :id 2
+                       :type xim:ATTRIBUTE-VALUE-TYPE:window
+                       :length (length xlib:XNFocusWindow)
+                       :attribute xlib:XNFocusWindow))
+  "Default IC attrs returned to clients.")
+
+(defconst exwm-xim--default-styles
+  (make-instance 'xim:XIMStyles
+                 :number nil
+                 :styles (list (logior xlib:XIMPreeditNothing
+                                       xlib:XIMStatusNothing)))
+  "Default styles: root-window, i.e. no preediting or status display support.")
+
+(defconst exwm-xim--default-attributes
+  (list (make-instance 'xim:XIMATTRIBUTE
+                       :id 0
+                       :length nil
+                       :value exwm-xim--default-styles))
+  "Default IM/IC attributes returned to clients.")
+
+(defvar exwm-xim--conn nil
+  "The X connection for initiating other XIM connections.")
+(defvar exwm-xim--event-xwin nil
+  "X window for initiating new XIM connections.")
+(defvar exwm-xim--server-client-plist '(nil nil)
+  "Plist mapping server window to [X connection, client window, byte-order].")
+(defvar exwm-xim--client-server-plist '(nil nil)
+  "Plist mapping client window to server window.")
+(defvar exwm-xim--property-index 0 "For generating a unique property name.")
+(defvar exwm-xim--im-id 0 "Last IM ID.")
+(defvar exwm-xim--ic-id 0 "Last IC ID.")
+(defvar exwm-xim--event-pending nil
+  "Indicating whether Emacs requires more events.")
+
+;; X11 atoms.
+(defvar exwm-xim--@server nil)
+(defvar exwm-xim--LOCALES nil)
+(defvar exwm-xim--TRANSPORT nil)
+(defvar exwm-xim--XIM_SERVERS nil)
+(defvar exwm-xim--_XIM_PROTOCOL nil)
+(defvar exwm-xim--_XIM_XCONNECT nil)
+
+(defun exwm-xim--on-SelectionRequest (data _synthetic)
+  "Handle SelectionRequest events on IMS window.
+
+Such events would be received when clients query for LOCALES or TRANSPORT."
+  (exwm--log)
+  (let ((evt (make-instance 'xcb:SelectionRequest))
+        value fake-event)
+    (xcb:unmarshal evt data)
+    (with-slots (time requestor selection target property) evt
+      (setq value (cond ((= target exwm-xim--LOCALES)
+                         ;; Return supported locales.
+                         exwm-xim--locales)
+                        ((= target exwm-xim--TRANSPORT)
+                         ;; Use XIM over an X connection.
+                         "@transport=X/")))
+      (when value
+        ;; Change the property.
+        (xcb:+request exwm-xim--conn
+            (make-instance 'xcb:ChangeProperty
+                           :mode xcb:PropMode:Replace
+                           :window requestor
+                           :property property
+                           :type target
+                           :format 8
+                           :data-len (length value)
+                           :data value))
+        ;; Send a SelectionNotify event.
+        (setq fake-event (make-instance 'xcb:SelectionNotify
+                                        :time time
+                                        :requestor requestor
+                                        :selection selection
+                                        :target target
+                                        :property property))
+        (xcb:+request exwm-xim--conn
+            (make-instance 'xcb:SendEvent
+                           :propagate 0
+                           :destination requestor
+                           :event-mask xcb:EventMask:NoEvent
+                           :event (xcb:marshal fake-event exwm-xim--conn)))
+        (xcb:flush exwm-xim--conn)))))
+
+(cl-defun exwm-xim--on-ClientMessage-0 (data _synthetic)
+  "Handle ClientMessage event on IMS window (new connection).
+
+Such events would be received when clients request for _XIM_XCONNECT.
+A new X connection and server window would be created to communicate with
+this client."
+  (exwm--log)
+  (let ((evt (make-instance 'xcb:ClientMessage))
+        conn client-xwin server-xwin)
+    (xcb:unmarshal evt data)
+    (with-slots (window type data) evt
+      (unless (= type exwm-xim--_XIM_XCONNECT)
+        ;; Only handle _XIM_XCONNECT.
+        (exwm--log "Ignore ClientMessage %s" type)
+        (cl-return-from exwm-xim--on-ClientMessage-0))
+      (setq client-xwin (elt (slot-value data 'data32) 0)
+            ;; Create a new X connection and a new server window.
+            conn (xcb:connect)
+            server-xwin (xcb:generate-id conn))
+      (set-process-query-on-exit-flag (slot-value conn 'process) nil)
+      ;; Store this client.
+      (plist-put exwm-xim--server-client-plist server-xwin
+                 `[,conn ,client-xwin nil])
+      (plist-put exwm-xim--client-server-plist client-xwin server-xwin)
+      ;; Select DestroyNotify events on this client window.
+      (xcb:+request exwm-xim--conn
+          (make-instance 'xcb:ChangeWindowAttributes
+                         :window client-xwin
+                         :value-mask xcb:CW:EventMask
+                         :event-mask xcb:EventMask:StructureNotify))
+      (xcb:flush exwm-xim--conn)
+      ;; Handle ClientMessage events from this new connection.
+      (xcb:+event conn 'xcb:ClientMessage #'exwm-xim--on-ClientMessage)
+      ;; Create a communication window.
+      (xcb:+request conn
+          (make-instance 'xcb:CreateWindow
+                         :depth 0
+                         :wid server-xwin
+                         :parent exwm--root
+                         :x 0
+                         :y 0
+                         :width 1
+                         :height 1
+                         :border-width 0
+                         :class xcb:WindowClass:InputOutput
+                         :visual 0
+                         :value-mask xcb:CW:OverrideRedirect
+                         :override-redirect 1))
+      (xcb:flush conn)
+      ;; Send connection establishment ClientMessage.
+      (setf window client-xwin
+            (slot-value data 'data32) `(,server-xwin 0 0 0 0))
+      (slot-makeunbound data 'data8)
+      (slot-makeunbound data 'data16)
+      (xcb:+request exwm-xim--conn
+          (make-instance 'xcb:SendEvent
+                         :propagate 0
+                         :destination client-xwin
+                         :event-mask xcb:EventMask:NoEvent
+                         :event (xcb:marshal evt exwm-xim--conn)))
+      (xcb:flush exwm-xim--conn))))
+
+(cl-defun exwm-xim--on-ClientMessage (data _synthetic)
+  "Handle ClientMessage event on IMS communication window (request).
+
+Such events would be received when clients request for _XIM_PROTOCOL.
+The actual XIM request is in client message data or a property."
+  (exwm--log)
+  (let ((evt (make-instance 'xcb:ClientMessage))
+        conn client-xwin server-xwin)
+    (xcb:unmarshal evt data)
+    (with-slots (format window type data) evt
+      (unless (= type exwm-xim--_XIM_PROTOCOL)
+        (exwm--log "Ignore ClientMessage %s" type)
+        (cl-return-from exwm-xim--on-ClientMessage))
+      (setq server-xwin window
+            conn (plist-get exwm-xim--server-client-plist server-xwin)
+            client-xwin (elt conn 1)
+            conn (elt conn 0))
+      (cond ((= format 8)
+             ;; Data.
+             (exwm-xim--on-request (vconcat (slot-value data 'data8))
+                                   conn client-xwin server-xwin))
+            ((= format 32)
+             ;; Atom.
+             (with-slots (data32) data
+               (with-slots (value)
+                   (xcb:+request-unchecked+reply conn
+                       (make-instance 'xcb:GetProperty
+                                      :delete 1
+                                      :window server-xwin
+                                      :property (elt data32 1)
+                                      :type xcb:GetPropertyType:Any
+                                      :long-offset 0
+                                      :long-length (elt data32 0)))
+                 (when (> (length value) 0)
+                   (exwm-xim--on-request value conn client-xwin
+                                         server-xwin)))))))))
+
+(defun exwm-xim--on-request (data conn client-xwin server-xwin)
+  "Handle an XIM reuqest."
+  (exwm--log)
+  (let ((opcode (elt data 0))
+        ;; Let-bind `xim:lsb' to make pack/unpack functions work correctly.
+        (xim:lsb (elt (plist-get exwm-xim--server-client-plist server-xwin) 2))
+        req replies)
+    (cond ((= opcode xim:opcode:error)
+           (exwm--log "ERROR: %s" data))
+          ((= opcode xim:opcode:connect)
+           (exwm--log "CONNECT")
+           (setq xim:lsb (= (elt data 4) xim:connect-byte-order:lsb-first))
+           ;; Store byte-order.
+           (setf (elt (plist-get exwm-xim--server-client-plist server-xwin) 2)
+                 xim:lsb)
+           (setq req (make-instance 'xim:connect))
+           (xcb:unmarshal req data)
+           (if (and (= (slot-value req 'major-version) 1)
+                    (= (slot-value req 'minor-version) 0)
+                    ;; Do not support authentication.
+                    (= (slot-value req 'number) 0))
+               ;; Accept the connection.
+               (push (make-instance 'xim:connect-reply) replies)
+             ;; Deny it.
+             (push exwm-xim--default-error replies)))
+          ((memq opcode (list xim:opcode:auth-required
+                              xim:opcode:auth-reply
+                              xim:opcode:auth-next
+                              xim:opcode:auth-ng))
+           (exwm--log "AUTH: %d" opcode)
+           ;; Deny any attempt to make authentication.
+           (push exwm-xim--default-error replies))
+          ((= opcode xim:opcode:disconnect)
+           (exwm--log "DISCONNECT")
+           ;; Gracefully disconnect from the client.
+           (exwm-xim--make-request (make-instance 'xim:disconnect-reply)
+                                   conn client-xwin)
+           ;; Destroy the communication window & connection.
+           (xcb:+request conn
+               (make-instance 'xcb:DestroyWindow
+                              :window server-xwin))
+           (xcb:disconnect conn)
+           ;; Clean up cache.
+           (cl-remf exwm-xim--server-client-plist server-xwin)
+           (cl-remf exwm-xim--client-server-plist client-xwin))
+          ((= opcode xim:opcode:open)
+           (exwm--log "OPEN")
+           ;; Note: We make no check here.
+           (setq exwm-xim--im-id (if (< exwm-xim--im-id #xffff)
+                                     (1+ exwm-xim--im-id)
+                                   1))
+           (setq replies
+                 (list
+                  (make-instance 'xim:open-reply
+                                 :im-id exwm-xim--im-id
+                                 :im-attrs-length nil
+                                 :im-attrs exwm-xim--default-im-attrs
+                                 :ic-attrs-length nil
+                                 :ic-attrs exwm-xim--default-ic-attrs)
+                  (make-instance 'xim:set-event-mask
+                                 :im-id exwm-xim--im-id
+                                 :ic-id 0
+                                 ;; Static event flow.
+                                 :forward-event-mask xcb:EventMask:KeyPress
+                                 ;; on-demand-synchronous method.
+                                 :synchronous-event-mask
+                                 xcb:EventMask:NoEvent))))
+          ((= opcode xim:opcode:close)
+           (exwm--log "CLOSE")
+           (setq req (make-instance 'xim:close))
+           (xcb:unmarshal req data)
+           (push (make-instance 'xim:close-reply
+                                :im-id (slot-value req 'im-id))
+                 replies))
+          ((= opcode xim:opcode:trigger-notify)
+           (exwm--log "TRIGGER-NOTIFY")
+           ;; Only static event flow modal is supported.
+           (push exwm-xim--default-error replies))
+          ((= opcode xim:opcode:encoding-negotiation)
+           (exwm--log "ENCODING-NEGOTIATION")
+           (setq req (make-instance 'xim:encoding-negotiation))
+           (xcb:unmarshal req data)
+           (let ((index (cl-position "COMPOUND_TEXT"
+                                     (mapcar (lambda (i) (slot-value i 'name))
+                                             (slot-value req 'names))
+                                     :test #'equal)))
+             (unless index
+               ;; Fallback to portable character encoding (a subset of ASCII).
+               (setq index -1))
+             (push (make-instance 'xim:encoding-negotiation-reply
+                                  :im-id (slot-value req 'im-id)
+                                  :category
+                                  xim:encoding-negotiation-reply-category:name
+                                  :index index)
+                   replies)))
+          ((= opcode xim:opcode:query-extension)
+           (exwm--log "QUERY-EXTENSION")
+           (setq req (make-instance 'xim:query-extension))
+           (xcb:unmarshal req data)
+           (push (make-instance 'xim:query-extension-reply
+                                :im-id (slot-value req 'im-id)
+                                ;; No extension support.
+                                :length 0
+                                :extensions nil)
+                 replies))
+          ((= opcode xim:opcode:set-im-values)
+           (exwm--log "SET-IM-VALUES")
+           ;; There's only one possible input method attribute.
+           (setq req (make-instance 'xim:set-im-values))
+           (xcb:unmarshal req data)
+           (push (make-instance 'xim:set-im-values-reply
+                                :im-id (slot-value req 'im-id))
+                 replies))
+          ((= opcode xim:opcode:get-im-values)
+           (exwm--log "GET-IM-VALUES")
+           (setq req (make-instance 'xim:get-im-values))
+           (let (im-attributes-id)
+             (xcb:unmarshal req data)
+             (setq im-attributes-id (slot-value req 'im-attributes-id))
+             (if (cl-notevery (lambda (i) (= i 0)) im-attributes-id)
+                 ;; Only support one IM attributes.
+                 (push (make-instance 'xim:error
+                                      :im-id (slot-value req 'im-id)
+                                      :ic-id 0
+                                      :flag xim:error-flag:invalid-ic-id
+                                      :error-code xim:error-code:bad-something
+                                      :length 0
+                                      :type 0
+                                      :detail nil)
+                       replies)
+               (push
+                (make-instance 'xim:get-im-values-reply
+                               :im-id (slot-value req 'im-id)
+                               :length nil
+                               :im-attributes exwm-xim--default-attributes)
+                replies))))
+          ((= opcode xim:opcode:create-ic)
+           (exwm--log "CREATE-IC")
+           (setq req (make-instance 'xim:create-ic))
+           (xcb:unmarshal req data)
+           ;; Note: The ic-attributes slot is ignored.
+           (setq exwm-xim--ic-id (if (< exwm-xim--ic-id #xffff)
+                                     (1+ exwm-xim--ic-id)
+                                   1))
+           (push (make-instance 'xim:create-ic-reply
+                                :im-id (slot-value req 'im-id)
+                                :ic-id exwm-xim--ic-id)
+                 replies))
+          ((= opcode xim:opcode:destroy-ic)
+           (exwm--log "DESTROY-IC")
+           (setq req (make-instance 'xim:destroy-ic))
+           (xcb:unmarshal req data)
+           (push (make-instance 'xim:destroy-ic-reply
+                                :im-id (slot-value req 'im-id)
+                                :ic-id (slot-value req 'ic-id))
+                 replies))
+          ((= opcode xim:opcode:set-ic-values)
+           (exwm--log "SET-IC-VALUES")
+           (setq req (make-instance 'xim:set-ic-values))
+           (xcb:unmarshal req data)
+           ;; We don't distinguish between input contexts.
+           (push (make-instance 'xim:set-ic-values-reply
+                                :im-id (slot-value req 'im-id)
+                                :ic-id (slot-value req 'ic-id))
+                 replies))
+          ((= opcode xim:opcode:get-ic-values)
+           (exwm--log "GET-IC-VALUES")
+           (setq req (make-instance 'xim:get-ic-values))
+           (xcb:unmarshal req data)
+           (push (make-instance 'xim:get-ic-values-reply
+                                :im-id (slot-value req 'im-id)
+                                :ic-id (slot-value req 'ic-id)
+                                :length nil
+                                :ic-attributes exwm-xim--default-attributes)
+                 replies))
+          ((= opcode xim:opcode:set-ic-focus)
+           (exwm--log "SET-IC-FOCUS")
+           ;; All input contexts are the same.
+           )
+          ((= opcode xim:opcode:unset-ic-focus)
+           (exwm--log "UNSET-IC-FOCUS")
+           ;; All input contexts are the same.
+           )
+          ((= opcode xim:opcode:forward-event)
+           (exwm--log "FORWARD-EVENT")
+           (setq req (make-instance 'xim:forward-event))
+           (xcb:unmarshal req data)
+           (let ((im-func (with-current-buffer (window-buffer)
+                            input-method-function))
+                 key-event keysym event result)
+             ;; Note: The flag slot is ignored.
+             ;; Do conversion in client's byte-order.
+             (let ((xcb:lsb xim:lsb))
+               (setq key-event (make-instance 'xcb:KeyPress))
+               (xcb:unmarshal key-event (slot-value req 'event)))
+             (with-slots (detail state) key-event
+               (setq keysym (xcb:keysyms:keycode->keysym exwm-xim--conn detail
+                                                         state))
+               (when (/= (car keysym) 0)
+                 (setq event (xcb:keysyms:keysym->event
+                              exwm-xim--conn
+                              (car keysym)
+                              (logand state (lognot (cdr keysym)))))))
+             (if exwm-xim--event-pending
+                 ;; In case any event reaches here, it should be forwarded
+                 ;; to Emacs.
+                 (when event
+                   (setq unread-command-events
+                         (append unread-command-events (list event))))
+               (setq exwm-xim--event-pending t)
+               (if (or (not im-func)
+                       ;; `list' is the default method.
+                       (eq im-func #'list)
+                       (not event)
+                       ;; Select only printable keys.
+                       (not (integerp event)) (> #x20 event) (< #x7e event))
+                   ;; Either there is no active input method, or invalid key
+                   ;; is detected.
+                   (with-slots (im-id ic-id serial-number event) req
+                     (push (make-instance 'xim:forward-event
+                                          :im-id im-id
+                                          :ic-id ic-id
+                                          :flag xim:commit-flag:synchronous
+                                          :serial-number serial-number
+                                          :event event)
+                           replies))
+                 (when (eq exwm--selected-input-mode 'char-mode)
+                   ;; Grab keyboard temporarily for char-mode.
+                   (exwm-input--grab-keyboard))
+                 (unwind-protect
+                     (with-temp-buffer
+                       ;; Always show key strokes.
+                       (let ((input-method-use-echo-area t))
+                         (setq result (funcall im-func event))))
+                   (when (eq exwm--selected-input-mode 'char-mode)
+                     (exwm-input--release-keyboard)))
+                 ;; This also works for portable character encoding.
+                 (setq result
+                       (encode-coding-string (concat result)
+                                             'compound-text-with-extensions))
+                 (message "")
+                 (push
+                  (make-instance 'xim:commit-x-lookup-chars
+                                 :im-id (slot-value req 'im-id)
+                                 :ic-id (slot-value req 'ic-id)
+                                 :flag (logior xim:commit-flag:synchronous
+                                               xim:commit-flag:x-lookup-chars)
+                                 :length (length result)
+                                 :string result)
+                  replies))
+               (setq exwm-xim--event-pending nil))))
+          ((= opcode xim:opcode:sync)
+           (exwm--log "SYNC")
+           (setq req (make-instance 'xim:sync))
+           (xcb:unmarshal req data)
+           (push (make-instance 'xim:sync-reply
+                                :im-id (slot-value req 'im-id)
+                                :ic-id (slot-value req 'ic-id))
+                 replies))
+          ((= opcode xim:opcode:sync-reply)
+           (exwm--log "SYNC-REPLY"))
+          ((= opcode xim:opcode:reset-ic)
+           (exwm--log "RESET-IC")
+           ;; No context-specific data saved.
+           (setq req (make-instance 'xim:reset-ic))
+           (xcb:unmarshal req data)
+           (push (make-instance 'xim:reset-ic-reply
+                                :im-id (slot-value req 'im-id)
+                                :ic-id (slot-value req 'ic-id)
+                                :length 0
+                                :string "")
+                 replies))
+          ((memq opcode (list xim:opcode:str-conversion-reply
+                              xim:opcode:preedit-start-reply
+                              xim:opcode:preedit-caret-reply))
+           (exwm--log "PREEDIT: %d" opcode)
+           ;; No preedit support.
+           (push exwm-xim--default-error replies))
+          (t
+           (exwm--log "Bad protocol")
+           (push exwm-xim--default-error replies)))
+    ;; Actually send the replies.
+    (when replies
+      (mapc (lambda (reply)
+              (exwm-xim--make-request reply conn client-xwin))
+            replies)
+      (xcb:flush conn))))
+
+(defun exwm-xim--make-request (req conn client-xwin)
+  "Make an XIM request REQ via connection CONN.
+
+CLIENT-XWIN would receive a ClientMessage event either telling the client
+the request data or where to fetch the data."
+  (exwm--log)
+  (let ((data (xcb:marshal req))
+        property format client-message-data client-message)
+    (if (<= (length data) 20)
+        ;; Send short requests directly with client messages.
+        (setq format 8
+              ;; Pad to 20 bytes.
+              data (append data (make-list (- 20 (length data)) 0))
+              client-message-data (make-instance 'xcb:ClientMessageData
+                                                 :data8 data))
+      ;; Send long requests with properties.
+      (setq property (exwm--intern-atom (format "_EXWM_XIM_%x"
+                                                exwm-xim--property-index)))
+      (cl-incf exwm-xim--property-index)
+      (xcb:+request conn
+          (make-instance 'xcb:ChangeProperty
+                         :mode xcb:PropMode:Append
+                         :window client-xwin
+                         :property property
+                         :type xcb:Atom:STRING
+                         :format 8
+                         :data-len (length data)
+                         :data data))
+      ;; Also send a client message to notify the client about this property.
+      (setq format 32
+            client-message-data (make-instance 'xcb:ClientMessageData
+                                               :data32 `(,(length data)
+                                                         ,property
+                                                         ;; Pad to 20 bytes.
+                                                         0 0 0))))
+    ;; Send the client message.
+    (setq client-message (make-instance 'xcb:ClientMessage
+                                        :format format
+                                        :window client-xwin
+                                        :type exwm-xim--_XIM_PROTOCOL
+                                        :data client-message-data))
+    (xcb:+request conn
+        (make-instance 'xcb:SendEvent
+                       :propagate 0
+                       :destination client-xwin
+                       :event-mask xcb:EventMask:NoEvent
+                       :event (xcb:marshal client-message conn)))))
+
+(defun exwm-xim--on-DestroyNotify (data synthetic)
+  "Do cleanups on receiving DestroyNotify event.
+
+Such event would be received when the client window is destroyed."
+  (exwm--log)
+  (unless synthetic
+    (let ((evt (make-instance 'xcb:DestroyNotify))
+          conn client-xwin server-xwin)
+      (xcb:unmarshal evt data)
+      (setq client-xwin (slot-value evt 'window)
+            server-xwin (plist-get exwm-xim--client-server-plist client-xwin))
+      (when server-xwin
+        (setq conn (aref (plist-get exwm-xim--server-client-plist server-xwin)
+                         0))
+        (cl-remf exwm-xim--server-client-plist server-xwin)
+        (cl-remf exwm-xim--client-server-plist client-xwin)
+        ;; Destroy the communication window & connection.
+        (xcb:+request conn
+            (make-instance 'xcb:DestroyWindow
+                           :window server-xwin))
+        (xcb:disconnect conn)))))
+
+(cl-defun exwm-xim--init ()
+  "Initialize the XIM module."
+  (exwm--log)
+  (when exwm-xim--conn
+    (cl-return-from exwm-xim--init))
+  ;; Initialize atoms.
+  (setq exwm-xim--@server (exwm--intern-atom "@server=exwm-xim")
+        exwm-xim--LOCALES (exwm--intern-atom "LOCALES")
+        exwm-xim--TRANSPORT (exwm--intern-atom "TRANSPORT")
+        exwm-xim--XIM_SERVERS (exwm--intern-atom "XIM_SERVERS")
+        exwm-xim--_XIM_PROTOCOL (exwm--intern-atom "_XIM_PROTOCOL")
+        exwm-xim--_XIM_XCONNECT (exwm--intern-atom "_XIM_XCONNECT"))
+  ;; Create a new connection and event window.
+  (setq exwm-xim--conn (xcb:connect)
+        exwm-xim--event-xwin (xcb:generate-id exwm-xim--conn))
+  (set-process-query-on-exit-flag (slot-value exwm-xim--conn 'process) nil)
+  ;; Initialize xcb:keysyms module.
+  (xcb:keysyms:init exwm-xim--conn)
+  ;; Listen to SelectionRequest event for connection establishment.
+  (xcb:+event exwm-xim--conn 'xcb:SelectionRequest
+              #'exwm-xim--on-SelectionRequest)
+  ;; Listen to ClientMessage event on IMS window for new XIM connection.
+  (xcb:+event exwm-xim--conn 'xcb:ClientMessage #'exwm-xim--on-ClientMessage-0)
+  ;; Listen to DestroyNotify event to do cleanups.
+  (xcb:+event exwm-xim--conn 'xcb:DestroyNotify #'exwm-xim--on-DestroyNotify)
+  ;; Create the event window.
+  (xcb:+request exwm-xim--conn
+      (make-instance 'xcb:CreateWindow
+                     :depth 0
+                     :wid exwm-xim--event-xwin
+                     :parent exwm--root
+                     :x 0
+                     :y 0
+                     :width 1
+                     :height 1
+                     :border-width 0
+                     :class xcb:WindowClass:InputOutput
+                     :visual 0
+                     :value-mask xcb:CW:OverrideRedirect
+                     :override-redirect 1))
+  ;; Set the selection owner.
+  (xcb:+request exwm-xim--conn
+      (make-instance 'xcb:SetSelectionOwner
+                     :owner exwm-xim--event-xwin
+                     :selection exwm-xim--@server
+                     :time xcb:Time:CurrentTime))
+  ;; Set XIM_SERVERS property on the root window.
+  (xcb:+request exwm-xim--conn
+      (make-instance 'xcb:ChangeProperty
+                     :mode xcb:PropMode:Prepend
+                     :window exwm--root
+                     :property exwm-xim--XIM_SERVERS
+                     :type xcb:Atom:ATOM
+                     :format 32
+                     :data-len 1
+                     :data (funcall (if xcb:lsb
+                                        #'xcb:-pack-u4-lsb
+                                      #'xcb:-pack-u4)
+                                    exwm-xim--@server)))
+  (xcb:flush exwm-xim--conn))
+
+(cl-defun exwm-xim--exit ()
+  "Exit the XIM module."
+  (exwm--log)
+  ;; Close IMS communication connections.
+  (mapc (lambda (i)
+          (when (vectorp i)
+            (xcb:disconnect (elt i 0))))
+        exwm-xim--server-client-plist)
+  ;; Close the IMS connection.
+  (unless exwm-xim--conn
+    (cl-return-from exwm-xim--exit))
+  ;; Remove exwm-xim from XIM_SERVERS.
+  (let ((reply (xcb:+request-unchecked+reply exwm-xim--conn
+                   (make-instance 'xcb:GetProperty
+                                  :delete 1
+                                  :window exwm--root
+                                  :property exwm-xim--XIM_SERVERS
+                                  :type xcb:Atom:ATOM
+                                  :long-offset 0
+                                  :long-length 1000)))
+        unpacked-reply pack unpack)
+    (unless reply
+      (cl-return-from exwm-xim--exit))
+    (setq reply (slot-value reply 'value))
+    (unless (> (length reply) 4)
+      (cl-return-from exwm-xim--exit))
+    (setq reply (vconcat reply)
+          pack (if xcb:lsb #'xcb:-pack-u4-lsb #'xcb:-pack-u4)
+          unpack (if xcb:lsb #'xcb:-unpack-u4-lsb #'xcb:-unpack-u4))
+    (dotimes (i (/ (length reply) 4))
+      (push (funcall unpack reply (* i 4)) unpacked-reply))
+    (setq unpacked-reply (delq exwm-xim--@server unpacked-reply)
+          reply (mapcar pack unpacked-reply))
+    (xcb:+request exwm-xim--conn
+        (make-instance 'xcb:ChangeProperty
+                       :mode xcb:PropMode:Replace
+                       :window exwm--root
+                       :property exwm-xim--XIM_SERVERS
+                       :type xcb:Atom:ATOM
+                       :format 32
+                       :data-len (length reply)
+                       :data reply))
+    (xcb:flush exwm-xim--conn))
+  (xcb:disconnect exwm-xim--conn)
+  (setq exwm-xim--conn nil))
+
+(defun exwm-xim-enable ()
+  "Enable XIM support for EXWM."
+  (exwm--log)
+  (add-hook 'exwm-init-hook #'exwm-xim--init)
+  (add-hook 'exwm-exit-hook #'exwm-xim--exit))
+
+
+
+(provide 'exwm-xim)
+
+;;; exwm-xim.el ends here
diff --git a/exwm.el b/exwm.el
index 6021f85b012c..7affebfff2ba 100644
--- a/exwm.el
+++ b/exwm.el
@@ -1,10 +1,10 @@
 ;;; exwm.el --- Emacs X Window Manager  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2018 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2019 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 ;; Maintainer: Chris Feng <chris.w.feng@gmail.com>
-;; Version: 0.20
+;; Version: 0.21
 ;; Package-Requires: ((xelb "0.16"))
 ;; Keywords: unix
 ;; URL: https://github.com/ch11ng/exwm
@@ -107,6 +107,7 @@
 (defun exwm-reset ()
   "Reset the state of the selected window (non-fullscreen, line-mode, etc)."
   (interactive)
+  (exwm--log)
   (with-current-buffer (window-buffer)
     (when (derived-mode-p 'exwm-mode)
       (when (exwm-layout--fullscreen-p)
@@ -119,6 +120,7 @@
 (defun exwm-restart ()
   "Restart EXWM."
   (interactive)
+  (exwm--log)
   (when (exwm--confirm-kill-emacs "[EXWM] Restart? " 'no-check)
     (let* ((attr (process-attributes (emacs-pid)))
            (args (cdr (assq 'args attr)))
@@ -146,6 +148,7 @@
 
 (defun exwm--update-desktop (xwin)
   "Update _NET_WM_DESKTOP."
+  (exwm--log "#x%x" xwin)
   (with-current-buffer (exwm--id->buffer xwin)
     (let ((reply (xcb:+request-unchecked+reply exwm--connection
                      (make-instance 'xcb:ewmh:get-_NET_WM_DESKTOP
@@ -172,6 +175,7 @@
 
 (defun exwm--update-window-type (id &optional force)
   "Update _NET_WM_WINDOW_TYPE."
+  (exwm--log "#x%x" id)
   (with-current-buffer (exwm--id->buffer id)
     (unless (and exwm-window-type (not force))
       (let ((reply (xcb:+request-unchecked+reply exwm--connection
@@ -182,6 +186,7 @@
 
 (defun exwm--update-class (id &optional force)
   "Update WM_CLASS."
+  (exwm--log "#x%x" id)
   (with-current-buffer (exwm--id->buffer id)
     (unless (and exwm-instance-name exwm-class-name (not force))
       (let ((reply (xcb:+request-unchecked+reply exwm--connection
@@ -194,6 +199,7 @@
 
 (defun exwm--update-utf8-title (id &optional force)
   "Update _NET_WM_NAME."
+  (exwm--log "#x%x" id)
   (with-current-buffer (exwm--id->buffer id)
     (when (or force (not exwm-title))
       (let ((reply (xcb:+request-unchecked+reply exwm--connection
@@ -206,6 +212,7 @@
 
 (defun exwm--update-ctext-title (id &optional force)
   "Update WM_NAME."
+  (exwm--log "#x%x" id)
   (with-current-buffer (exwm--id->buffer id)
     (unless (or exwm--title-is-utf8
                 (and exwm-title (not force)))
@@ -218,11 +225,13 @@
 
 (defun exwm--update-title (id)
   "Update _NET_WM_NAME or WM_NAME."
+  (exwm--log "#x%x" id)
   (exwm--update-utf8-title id)
   (exwm--update-ctext-title id))
 
 (defun exwm--update-transient-for (id &optional force)
   "Update WM_TRANSIENT_FOR."
+  (exwm--log "#x%x" id)
   (with-current-buffer (exwm--id->buffer id)
     (unless (and exwm-transient-for (not force))
       (let ((reply (xcb:+request-unchecked+reply exwm--connection
@@ -233,6 +242,7 @@
 
 (defun exwm--update-normal-hints (id &optional force)
   "Update WM_NORMAL_HINTS."
+  (exwm--log "#x%x" id)
   (with-current-buffer (exwm--id->buffer id)
     (unless (and (not force)
                  (or exwm--normal-hints-x exwm--normal-hints-y
@@ -280,6 +290,7 @@
 
 (defun exwm--update-hints (id &optional force)
   "Update WM_HINTS."
+  (exwm--log "#x%x" id)
   (with-current-buffer (exwm--id->buffer id)
     (unless (and (not force) exwm--hints-input exwm--hints-urgency)
       (let ((reply (xcb:+request-unchecked+reply exwm--connection
@@ -301,6 +312,7 @@
 
 (defun exwm--update-protocols (id &optional force)
   "Update WM_PROTOCOLS."
+  (exwm--log "#x%x" id)
   (with-current-buffer (exwm--id->buffer id)
     (unless (and exwm--protocols (not force))
       (let ((reply (xcb:+request-unchecked+reply exwm--connection
@@ -311,6 +323,7 @@
 
 (defun exwm--update-struts-legacy (id)
   "Update _NET_WM_STRUT."
+  (exwm--log "#x%x" id)
   (let ((pair (assq id exwm-workspace--id-struts-alist))
         reply struts)
     (unless (and pair (< 4 (length (cdr pair))))
@@ -331,6 +344,7 @@
 
 (defun exwm--update-struts-partial (id)
   "Update _NET_WM_STRUT_PARTIAL."
+  (exwm--log "#x%x" id)
   (let ((reply (xcb:+request-unchecked+reply exwm--connection
                    (make-instance 'xcb:ewmh:get-_NET_WM_STRUT_PARTIAL
                                   :window id)))
@@ -350,6 +364,7 @@
 
 (defun exwm--update-struts (id)
   "Update _NET_WM_STRUT_PARTIAL or _NET_WM_STRUT."
+  (exwm--log "#x%x" id)
   (exwm--update-struts-partial id)
   (exwm--update-struts-legacy id))
 
@@ -386,9 +401,10 @@
               ((= atom xcb:Atom:WM_PROTOCOLS)
                (exwm--update-protocols id t))
               ((= atom xcb:Atom:_NET_WM_USER_TIME)) ;ignored
-              (t (exwm--log "Unhandled PropertyNotify: %s(%d)"
-                            (x-get-atom-name atom exwm-workspace--current)
-                            atom)))))))
+              (t
+               (exwm--log "Unhandled: %s(%d)"
+                          (x-get-atom-name atom exwm-workspace--current)
+                          atom)))))))
 
 (defun exwm--on-ClientMessage (raw-data _synthetic)
   "Handle ClientMessage event."
@@ -416,16 +432,21 @@
       (exwm-workspace-switch (elt data 0)))
      ;; _NET_ACTIVE_WINDOW.
      ((= type xcb:Atom:_NET_ACTIVE_WINDOW)
-      (let ((buffer (exwm--id->buffer id)))
+      (let ((buffer (exwm--id->buffer id))
+            iconic window)
         (when (buffer-live-p buffer)
           (with-current-buffer buffer
             (when (eq exwm--frame exwm-workspace--current)
-              (when (exwm-layout--iconic-state-p)
+              (setq iconic (exwm-layout--iconic-state-p))
+              (when iconic
                 ;; State change: iconic => normal.
                 (set-window-buffer (frame-selected-window exwm--frame)
                                    (current-buffer)))
               ;; Focus transfer.
-              (select-window (get-buffer-window nil t)))))))
+              (setq window (get-buffer-window nil t))
+              (when (or iconic
+                        (not (eq window (selected-window))))
+                (select-window window)))))))
      ;; _NET_CLOSE_WINDOW.
      ((= type xcb:Atom:_NET_CLOSE_WINDOW)
       (let ((buffer (exwm--id->buffer id)))
@@ -529,11 +550,13 @@
                    (= (elt data 0) xcb:icccm:WM_STATE:IconicState))
           (with-current-buffer buffer
             (bury-buffer)))))
-     (t (exwm--log "Unhandled client message: %s" obj)))))
+     (t
+      (exwm--log "Unhandled: %s(%d)"
+                 (x-get-atom-name type exwm-workspace--current) type)))))
 
 (defun exwm--on-SelectionClear (data _synthetic)
   "Handle SelectionClear events."
-  (exwm--log "SelectionClear")
+  (exwm--log)
   (let ((obj (make-instance 'xcb:SelectionClear))
         owner selection)
     (xcb:unmarshal obj data)
@@ -545,6 +568,7 @@
 
 (defun exwm--init-icccm-ewmh ()
   "Initialize ICCCM/EWMH support."
+  (exwm--log)
   ;; Handle PropertyNotify event
   (xcb:+event exwm--connection 'xcb:PropertyNotify #'exwm--on-PropertyNotify)
   ;; Handle relevant client messages
@@ -688,6 +712,7 @@
 
 REPLACE specifies what to do in case there already is a window
 manager.  If t, replace it, if nil, abort and ask the user if `ask'."
+  (exwm--log "%s" replace)
   (with-slots (owner)
       (xcb:+request-unchecked+reply exwm--connection
           (make-instance 'xcb:GetSelectionOwner
@@ -762,6 +787,7 @@ manager.  If t, replace it, if nil, abort and ask the user if `ask'."
 (cl-defun exwm-init (&optional frame)
   "Initialize EXWM."
   (interactive)
+  (exwm--log "%s" frame)
   (if frame
       ;; The frame might not be selected if it's created by emacslicnet.
       (select-frame-set-input-focus frame)
@@ -807,9 +833,9 @@ manager.  If t, replace it, if nil, abort and ask the user if `ask'."
         (exwm--unlock)
         (exwm-workspace--post-init)
         (exwm-input--post-init)
+        (run-hooks 'exwm-init-hook)
         ;; Manage existing windows
-        (exwm-manage--scan)
-        (run-hooks 'exwm-init-hook))
+        (exwm-manage--scan))
     ((quit error)
      (exwm-exit)
      ;; Rethrow error
@@ -820,6 +846,7 @@ manager.  If t, replace it, if nil, abort and ask the user if `ask'."
 (defun exwm-exit ()
   "Exit EXWM."
   (interactive)
+  (exwm--log)
   (run-hooks 'exwm-exit-hook)
   (setq confirm-kill-emacs nil)
   ;; Exit modules.
@@ -835,6 +862,7 @@ manager.  If t, replace it, if nil, abort and ask the user if `ask'."
 
 (defun exwm-enable (&optional undo)
   "Enable/Disable EXWM."
+  (exwm--log "%s" undo)
   (pcase undo
     (`undo                              ;prevent reinitialization
      (remove-hook 'window-setup-hook #'exwm-init)
@@ -861,6 +889,7 @@ manager.  If t, replace it, if nil, abort and ask the user if `ask'."
 
 (defun exwm--server-stop ()
   "Stop the subordinate Emacs server."
+  (exwm--log)
   (server-force-delete exwm--server-name)
   (when exwm--server-process
     (delete-process exwm--server-process)
@@ -869,6 +898,7 @@ manager.  If t, replace it, if nil, abort and ask the user if `ask'."
 (defun exwm--server-eval-at (&rest args)
   "Wrapper of `server-eval-at' used to advice subrs."
   ;; Start the subordinate Emacs server if it's not alive
+  (exwm--log "%s" args)
   (unless (server-running-p exwm--server-name)
     (when exwm--server-process (delete-process exwm--server-process))
     (setq exwm--server-process
@@ -907,6 +937,7 @@ manager.  If t, replace it, if nil, abort and ask the user if `ask'."
 
 (defun exwm--confirm-kill-emacs (prompt &optional force)
   "Confirm before exiting Emacs."
+  (exwm--log)
   (when (cond
          ((and force (not (eq force 'no-check)))
           ;; Force killing Emacs.
diff --git a/xinitrc b/xinitrc
index 0adc06845078..591e4199144f 100644
--- a/xinitrc
+++ b/xinitrc
@@ -1,17 +1,20 @@
-# Disable access control
+# Disable access control for the current user.
 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 &
-
-# Fallback cursor
+# Set default cursor.
 xsetroot -cursor_name left_ptr
 
-# Keyboard repeat rate
+# Set keyboard repeat rate.
 xset r rate 200 60
 
-# Start Emacs
-exec dbus-launch --exit-with-session emacs
+# Uncomment the following block to use the exwm-xim module.
+#export XMODIFIERS=@im=exwm-xim
+#export GTK_IM_MODULE=xim
+#export QT_IM_MODULE=xim
+#export CLUTTER_IM_MODULE=xim
+
+# Finally start Emacs
+exec emacs