Rework the X windows hierarchy model

This commit add workspace and X window containers support to avoid using
Emacs frames as the parents of X windows.  This should make it easier to
set input focus.

* exwm-core.el (exwm--container, exwm--floating-frame-position): New file
local variables.
(exwm--floating-frame-geometry): Removed file local variable.
* exwm-floating.el (exwm-floating--set-floating)
(exwm-floating--unset-floating, exwm-floating--do-moveresize)
(exwm-floating-move): Use container.
(exwm-floating--fit-frame-to-window): No longer adjust stacking order.
(exwm-floating--fit-frame-to-window): The first member is changed to
buffer.
(exwm-floating--start-moveresize): Use container.  Correctly set input
focus.
* exwm-input.el (exwm-input--redirected, exwm-input--on-focus-in): Removed.
(exwm-input--on-buffer-list-update): Remove the restriction on floating
frames which is no longer valid.
(exwm-input--update-focus): Adjust stacking order.
(exwm-input--on-minibuffer-setup): New function for setting focus on the
Emacs frame when entering minibuffer.
(exwm-input--on-KeyPress-line-mode): No longer compensate FocusOut event.
(exwm-input--grab-keyboard, exwm-input--release-keyboard): Local keys are
now grabbed on the X window container.
(exwm-input--init): Add `exwm-input--on-minibuffer-setup' to
`minibuffer-setup-hook'.
* exwm-layout.el (exwm-layout--resize-container): New function to
resize/reposition both the X window and its container.
(exwm-layout--show, exwm-layout--hide): Use container.
(exwm-layout-set-fullscreen): Use container.  No longer save width and
height.
(exwm-layout-unset-fullscreen, exwm-layout--set-frame-fullscreen): Use
container.
(exwm-layout--refresh): Update a frame parameter.  Remove dead code.
* exwm-manage.el (exwm-manage--manage-window): Reparent unmanaged X windows
to the workspace.  Create X window container as the parent of the X window.
(exwm-manage--unmanage-window): Unmap/destroy container when appropriate.
Use the position of container.
(exwm-manage--unmanage-window): Destroy the container.
* exwm-randr.el (exwm-randr--refresh): Resize workspace using container.
* exwm-workspace.el (exwm-workspace-switch): Raise workspace.
Correctly set input focus.
(exwm-workspace--on-focus-in): Removed.
(exwm-workspace-move-window): Reparent to workspace container.
(exwm-workspace--init): Create workspace frames as visible.
Create workspace containers.
Remove exwm-workspace--on-focus-in from focus-in-hook.
Update _NET_VIRTUAL_ROOTS.
* exwm.el (exwm-init): No longer disable hourglass window.
Initialize workspace module before input.

* exwm-core.el (exwm--debug): New macro for setting debug forms.

* exwm-floating.el (exwm-floating--set-floating): No longer do `exwm--lock'
and `exwm--unlock' since `make-frame' is already adviced to take care of
everything.  Correctly set input focus to the newly created floating
X window.

* exwm-core.el (exwm--floating-edges): Removed file local variable.
* exwm-floating.el (exwm-floating--set-floating)
(exwm-floating--unset-floating):
* exwm-layout.el (exwm-layout--show, exwm-layout-enlarge-window):
* exwm-manage.el (exwm-manage--on-ConfigureRequest):
No longer use floating geometry.

* exwm-input.el (exwm-input--update-global-prefix-keys): Grab global keys
on workspaces containers instead of the root window (or input focus would
transfer to the workspace containing the pointer when the grab is active).
* exwm-workspace.el (exwm-workspace-switch): No longer move mouse.
This commit is contained in:
Chris Feng 2016-02-03 12:12:24 +08:00
parent 07921a3731
commit 9c95c03e18
8 changed files with 356 additions and 354 deletions

View file

@ -79,8 +79,6 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
(xcb:flush exwm--connection))))
(defvar exwm-input--focus-window nil "The (Emacs) window to be focused.")
(defvar exwm-input--redirected nil
"Indicate next update on buffer list is actually a result of redirection.")
(defvar exwm-input--timer nil "Currently running timer.")
(defun exwm-input--on-buffer-list-update ()
@ -89,25 +87,12 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
(window (selected-window))
(buffer (current-buffer)))
(when (and (not (minibufferp buffer))
(frame-parameter frame 'exwm-window-id) ;e.g. emacsclient frame
(frame-parameter frame 'exwm-outer-id) ;e.g. emacsclient frame
(eq buffer (window-buffer))) ;e.g. `with-temp-buffer'
(unless (and exwm-input--redirected
exwm-input--focus-window
(with-current-buffer (window-buffer
exwm-input--focus-window)
exwm--floating-frame))
(setq exwm-input--focus-window window)
(when exwm-input--timer (cancel-timer exwm-input--timer))
(setq exwm-input--timer
(run-with-idle-timer 0.01 nil #'exwm-input--update-focus)))
(setq exwm-input--redirected nil))))
(defun exwm-input--on-focus-in ()
"Run in focus-in-hook to remove redirected focus on frame."
(let ((frame (selected-frame)))
(when (and (frame-parameter frame 'exwm-window-id)
(not (memq frame exwm-workspace--list)))
(setq exwm-input--redirected t))))
(when exwm-input--timer (cancel-timer exwm-input--timer))
(setq exwm-input--focus-window window
exwm-input--timer
(run-with-idle-timer 0.01 nil #'exwm-input--update-focus)))))
(defun exwm-input--update-focus ()
"Update input focus."
@ -122,22 +107,42 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
(force-mode-line-update)
;; The application may have changed its input focus
(exwm-workspace-switch exwm-workspace-current-index t))
(when exwm--floating-frame
(redirect-frame-focus exwm--floating-frame nil)
(select-frame-set-input-focus exwm--floating-frame t))
(exwm--log "Set focus on #x%x" exwm--id)
(exwm-input--set-focus exwm--id))
(exwm-input--set-focus exwm--id)
;; Adjust stacking orders
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window exwm--container
:value-mask xcb:ConfigWindow:StackMode
:stack-mode (if exwm--floating-frame
xcb:StackMode:Above
xcb:StackMode:Below)))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window (frame-parameter
(or exwm--floating-frame exwm--frame)
'exwm-outer-id)
:value-mask xcb:ConfigWindow:StackMode
:stack-mode xcb:StackMode:Below))
(xcb:flush exwm--connection))
(when (eq (selected-window) exwm-input--focus-window)
(exwm--log "Focus on %s" exwm-input--focus-window)
(select-frame-set-input-focus (window-frame exwm-input--focus-window)
t)
(dolist (pair exwm--id-buffer-alist)
(with-current-buffer (cdr pair)
(when (and exwm--floating-frame
(eq exwm--frame exwm-workspace--current))
(redirect-frame-focus exwm--floating-frame exwm--frame))))))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window (frame-parameter
(window-frame exwm-input--focus-window)
'exwm-outer-id)
:value-mask xcb:ConfigWindow:StackMode
:stack-mode xcb:StackMode:Below))
(xcb:flush exwm--connection)))
(setq exwm-input--focus-window nil))))
(defun exwm-input--on-minibuffer-setup ()
"Run in minibuffer-setup-hook to set input focus to the frame."
(x-focus-frame (selected-frame)))
(defvar exwm-input--during-key-sequence nil
"Non-nil indicates Emacs is waiting for more keys to form a key sequence.")
(defvar exwm-input--temp-line-mode nil
@ -221,32 +226,38 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
"Update `exwm-input--global-prefix-keys'."
(when exwm--connection
(let ((original exwm-input--global-prefix-keys)
keysym keycode)
keysym keycode ungrab-key grab-key workspace)
(setq exwm-input--global-prefix-keys nil)
(dolist (i exwm-input--global-keys)
(cl-pushnew (elt i 0) exwm-input--global-prefix-keys))
(unless (equal original exwm-input--global-prefix-keys)
;; Grab global keys on root window
(if (xcb:+request-checked+request-check exwm--connection
(make-instance 'xcb:UngrabKey
:key xcb:Grab:Any :grab-window exwm--root
:modifiers xcb:ModMask:Any))
(exwm--log "Failed to ungrab keys")
(dolist (i exwm-input--global-prefix-keys)
(setq keysym (xcb:keysyms:event->keysym exwm--connection i))
(when (or (not keysym)
(not (setq keycode (xcb:keysyms:keysym->keycode
exwm--connection (car keysym))))
(xcb:+request-checked+request-check exwm--connection
(make-instance 'xcb:GrabKey
:owner-events 0
:grab-window exwm--root
:modifiers (cadr keysym)
:key keycode
:pointer-mode xcb:GrabMode:Async
:keyboard-mode xcb:GrabMode:Async)))
(user-error "[EXWM] Failed to grab key: %s"
(single-key-description i)))))))))
(setq ungrab-key (make-instance 'xcb:UngrabKey
:key xcb:Grab:Any :grab-window nil
:modifiers xcb:ModMask:Any)
grab-key (make-instance 'xcb:GrabKey
:owner-events 0
:grab-window nil
:modifiers nil
:key nil
:pointer-mode xcb:GrabMode:Async
:keyboard-mode xcb:GrabMode:Async))
(dolist (w exwm-workspace--list)
(setq workspace (frame-parameter w 'exwm-workspace))
(setf (slot-value ungrab-key 'grab-window) workspace)
(if (xcb:+request-checked+request-check exwm--connection ungrab-key)
(exwm--log "Failed to ungrab keys")
(dolist (k exwm-input--global-prefix-keys)
(setq keysym (xcb:keysyms:event->keysym exwm--connection k)
keycode (xcb:keysyms:keysym->keycode exwm--connection
(car keysym)))
(setf (slot-value grab-key 'grab-window) workspace
(slot-value grab-key 'modifiers) (cadr keysym)
(slot-value grab-key 'key) keycode)
(when (or (not keycode)
(xcb:+request-checked+request-check exwm--connection
grab-key))
(user-error "[EXWM] Failed to grab key: %s"
(single-key-description k))))))))))
(defun exwm-input-set-key (key command)
"Set a global key binding."
@ -289,21 +300,6 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
(with-slots (detail state) key-press
(let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state))
event)
;; Compensate FocusOut event (prevent cursor style change)
(unless (eq major-mode 'exwm-mode)
(let ((id (frame-parameter nil 'exwm-window-id)))
(xcb:+request exwm--connection
(make-instance 'xcb:SendEvent
:propagate 0
:destination id
:event-mask xcb:EventMask:StructureNotify
:event
(xcb:marshal
(make-instance 'xcb:FocusIn
:detail xcb:NotifyDetail:Inferior
:event id
:mode xcb:NotifyMode:Normal)
exwm--connection)))))
(when (and keysym
(setq event (xcb:keysyms:keysym->event exwm--connection
keysym state)))
@ -324,7 +320,10 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
(when id
(when (xcb:+request-checked+request-check exwm--connection
(make-instance 'xcb:GrabKey
:owner-events 0 :grab-window id
:owner-events 0
:grab-window
(with-current-buffer (exwm--id->buffer id)
exwm--container)
:modifiers xcb:ModMask:Any
:key xcb:Grab:Any
:pointer-mode xcb:GrabMode:Async
@ -338,7 +337,10 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.")
(when id
(when (xcb:+request-checked+request-check exwm--connection
(make-instance 'xcb:UngrabKey
:key xcb:Grab:Any :grab-window id
:key xcb:Grab:Any
:grab-window
(with-current-buffer (exwm--id->buffer id)
exwm--container)
:modifiers xcb:ModMask:Any))
(exwm--log "Failed to release keyboard for #x%x" id))
(setq exwm--on-KeyPress #'exwm-input--on-KeyPress-char-mode)))
@ -487,7 +489,7 @@ SIMULATION-KEYS is a list of alist (key-sequence1 . key-sequence2)."
(add-hook 'pre-command-hook #'exwm-input--finish-key-sequence)
;; Update focus when buffer list updates
(add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update)
(add-hook 'focus-in-hook #'exwm-input--on-focus-in)
(add-hook 'minibuffer-setup-hook #'exwm-input--on-minibuffer-setup)
;; Update prefix keys for global keys
(exwm-input--update-global-prefix-keys))