Fix workspace creation and deletion

* exwm-workspace.el (exwm-workspace-switch)
(exwm-workspace-switch-create): Move support for creating missing
workspaces from the former to the latter..
(exwm-workspace-switch-create-limit): New variable limiting the number
of new workspaces allowed to create each time.

* exwm-workspace.el (exwm-workspace--prompt-add)
(exwm-workspace--prompt-delete): New commands for adding and deleting
workspaces from the `read-from-minibuffer' prompt.
(exwm-workspace--prompt-add-allowed)
(exwm-workspace--prompt-delete-allowed): New variables telling whether
the above two commands are allowed to run.
(exwm-workspace--switch-map): Change "+" / "-" to use the new commands.
* exwm-workspace.el (exwm-workspace-switch, exwm-workspace-swap)
(exwm-workspace-move-window): Use this new feature.

* exwm-workspace.el (exwm-workspace-add, exwm-workspace-delete): Since
they are not used by the keymap any more, drop the use of idle timer.

* exwm-workspace.el (exwm-workspace--create-silently): New variable
indicating whether new workspaces should be created in the background.
(exwm-workspace--add-frame-as-workspace): Support creating new
workspaces in the background.

* exwm-workspace.el (exwm-workspace--on-ConfigureNotify):
Update workareas if it's not up to date.

* exwm-randr.el (exwm-randr--refresh): Raise the standalone minibuffer
when refreshed.

* exwm-config.el (exwm-config-default): Add `exwm-workspace-number' and
`exwm-workspace-switch-create'.
This commit is contained in:
Chris Feng 2016-07-21 12:48:12 +08:00
parent f48b8eafb0
commit 0c114d97b7
3 changed files with 96 additions and 43 deletions

View file

@ -30,6 +30,8 @@
(defun exwm-config-default () (defun exwm-config-default ()
"Default configuration of EXWM." "Default configuration of EXWM."
;; Set the initial workspace number.
(setq exwm-workspace-number 4)
;; Make class name the buffer name ;; Make class name the buffer name
(add-hook 'exwm-update-class-hook (add-hook 'exwm-update-class-hook
(lambda () (lambda ()
@ -41,7 +43,9 @@
;; 's-N': Switch to certain workspace ;; 's-N': Switch to certain workspace
(dotimes (i 10) (dotimes (i 10)
(exwm-input-set-key (kbd (format "s-%d" i)) (exwm-input-set-key (kbd (format "s-%d" i))
`(lambda () (interactive) (exwm-workspace-switch ,i)))) `(lambda ()
(interactive)
(exwm-workspace-switch-create ,i))))
;; 's-&': Launch application ;; 's-&': Launch application
(exwm-input-set-key (kbd "s-&") (exwm-input-set-key (kbd "s-&")
(lambda (command) (lambda (command)

View file

@ -60,6 +60,7 @@
(declare-function exwm-workspace--count "exwm-workspace.el") (declare-function exwm-workspace--count "exwm-workspace.el")
(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) (declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame))
(declare-function exwm-workspace--update-workareas "exwm-workspace.el" ()) (declare-function exwm-workspace--update-workareas "exwm-workspace.el" ())
(declare-function exwm-workspace--show-minibuffer "exwm-workspace.el" ())
(declare-function exwm-workspace--set-desktop-geometry "exwm-workspace.el" ()) (declare-function exwm-workspace--set-desktop-geometry "exwm-workspace.el" ())
(defun exwm-randr--refresh () (defun exwm-randr--refresh ()
@ -111,6 +112,10 @@
;; Resize workspace. ;; Resize workspace.
(dolist (f exwm-workspace--list) (dolist (f exwm-workspace--list)
(exwm-workspace--set-fullscreen f)) (exwm-workspace--set-fullscreen f))
;; Raise the minibuffer if it's active.
(when (and (active-minibuffer-window)
(exwm-workspace--minibuffer-own-frame-p))
(exwm-workspace--show-minibuffer))
;; Set _NET_DESKTOP_GEOMETRY. ;; Set _NET_DESKTOP_GEOMETRY.
(exwm-workspace--set-desktop-geometry) (exwm-workspace--set-desktop-geometry)
(xcb:flush exwm--connection) (xcb:flush exwm--connection)

View file

@ -63,8 +63,8 @@ NIL if FRAME is not a workspace"
(defvar exwm-workspace--switch-map (defvar exwm-workspace--switch-map
(let ((map (make-sparse-keymap))) (let ((map (make-sparse-keymap)))
(define-key map [t] (lambda () (interactive))) (define-key map [t] (lambda () (interactive)))
(define-key map "+" #'exwm-workspace-add) (define-key map "+" #'exwm-workspace--prompt-add)
(define-key map "-" #'exwm-workspace-delete) (define-key map "-" #'exwm-workspace--prompt-delete)
(dotimes (i 10) (dotimes (i 10)
(define-key map (int-to-string i) (define-key map (int-to-string i)
#'exwm-workspace--switch-map-nth-prefix)) #'exwm-workspace--switch-map-nth-prefix))
@ -105,6 +105,38 @@ NIL if FRAME is not a workspace"
:test #'equal))) :test #'equal)))
(elt exwm-workspace--list workspace-idx))) (elt exwm-workspace--list workspace-idx)))
(defvar exwm-workspace--prompt-add-allowed nil
"Non-nil to allow adding workspace from the prompt.")
(defvar exwm-workspace--prompt-delete-allowed nil
"Non-nil to allow deleting workspace from the prompt")
(defvar exwm-workspace--create-silently nil
"When non-nil workspaces are created in the background (not switched to).")
(defun exwm-workspace--prompt-add ()
"Add workspace from the prompt."
(interactive)
(when exwm-workspace--prompt-add-allowed
(let ((exwm-workspace--create-silently t))
(make-frame))
(exwm-workspace--update-switch-history)
(goto-history-element minibuffer-history-position)))
(defun exwm-workspace--prompt-delete ()
"Delete workspace from the prompt."
(interactive)
(when (and exwm-workspace--prompt-delete-allowed
(< 1 (exwm-workspace--count)))
(let ((frame (elt exwm-workspace--list (1- minibuffer-history-position))))
(if (eq frame exwm-workspace--current)
;; Abort the recursive minibuffer if deleting the current workspace.
(progn
(run-with-idle-timer 0 nil #'delete-frame frame)
(abort-recursive-edit))
(delete-frame frame)
(exwm-workspace--update-switch-history)
(goto-history-element (min minibuffer-history-position
(exwm-workspace--count)))))))
(defun exwm-workspace--update-switch-history () (defun exwm-workspace--update-switch-history ()
"Update the history for switching workspace to reflect the latest status." "Update the history for switching workspace to reflect the latest status."
(when exwm-workspace--switch-history-outdated (when exwm-workspace--switch-history-outdated
@ -390,25 +422,16 @@ PREFIX-DIGITS is a list of the digits introduced so far."
"Normal hook run after switching workspace.") "Normal hook run after switching workspace.")
;;;###autoload ;;;###autoload
(cl-defun exwm-workspace-switch (frame-or-index &optional force) (defun exwm-workspace-switch (frame-or-index &optional force)
"Switch to workspace INDEX. Query for FRAME-OR-INDEX if it's not specified. "Switch to workspace INDEX. Query for FRAME-OR-INDEX if it's not specified.
The optional FORCE option is for internal use only." The optional FORCE option is for internal use only."
(interactive (interactive
(list (list
(unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible (unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible
(exwm-workspace--prompt-for-workspace "Workspace [+/-]: ")))) (let ((exwm-workspace--prompt-add-allowed t)
;; Try to create workspace(s) when INDEX is out-of-range. (exwm-workspace--prompt-delete-allowed t))
(when (and (integerp frame-or-index) (exwm-workspace--prompt-for-workspace "Switch to [+/-]: ")))))
(>= frame-or-index (exwm-workspace--count)))
(run-with-idle-timer 0 nil
(lambda (times)
(dotimes (_ times)
(make-frame)))
;; Create no more than 9 workspaces.
(min 9
(1+ (- frame-or-index (exwm-workspace--count)))))
(cl-return-from exwm-workspace-switch))
(let* ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index)) (let* ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index))
(index (exwm-workspace--position frame)) (index (exwm-workspace--position frame))
(workspace (frame-parameter frame 'exwm-workspace)) (workspace (frame-parameter frame 'exwm-workspace))
@ -469,6 +492,23 @@ The optional FORCE option is for internal use only."
(xcb:flush exwm--connection)) (xcb:flush exwm--connection))
(run-hooks 'exwm-workspace-switch-hook))) (run-hooks 'exwm-workspace-switch-hook)))
(defvar exwm-workspace-switch-create-limit 10
"Number of workspaces `exwm-workspace-switch-create' allowed to create
each time.")
(defun exwm-workspace-switch-create (frame-or-index)
"Switch to workspace FRAME-OR-INDEX, creating it if it does not exist yet."
(interactive)
(if (or (framep frame-or-index)
(< frame-or-index (exwm-workspace--count)))
(exwm-workspace-switch frame-or-index)
(let ((exwm-workspace--create-silently t))
(dotimes (_ (min exwm-workspace-switch-create-limit
(1+ (- frame-or-index
(exwm-workspace--count)))))
(make-frame)))
(exwm-workspace-switch (car (last exwm-workspace--list)))))
(defvar exwm-workspace-list-change-hook nil (defvar exwm-workspace-list-change-hook nil
"Normal hook run when the workspace list is changed (workspace added, "Normal hook run when the workspace list is changed (workspace added,
deleted, moved, etc).") deleted, moved, etc).")
@ -478,10 +518,14 @@ deleted, moved, etc).")
"Interchange position of WORKSPACE1 with that of WORKSPACE2." "Interchange position of WORKSPACE1 with that of WORKSPACE2."
(interactive (interactive
(unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible (unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible
(let* ((w1 (exwm-workspace--prompt-for-workspace "Pick a workspace: ")) (let (w1 w2)
(w2 (exwm-workspace--prompt-for-workspace (let ((exwm-workspace--prompt-add-allowed t)
(exwm-workspace--prompt-delete-allowed t))
(setq w1 (exwm-workspace--prompt-for-workspace
"Pick a workspace [+/-]: ")))
(setq w2 (exwm-workspace--prompt-for-workspace
(format "Swap workspace %d with: " (format "Swap workspace %d with: "
(exwm-workspace--position w1))))) (exwm-workspace--position w1))))
(list w1 w2)))) (list w1 w2))))
(let ((pos1 (exwm-workspace--position workspace1)) (let ((pos1 (exwm-workspace--position workspace1))
(pos2 (exwm-workspace--position workspace2))) (pos2 (exwm-workspace--position workspace2)))
@ -544,31 +588,20 @@ before it."
INDEX must not exceed the current number of workspaces." INDEX must not exceed the current number of workspaces."
(interactive) (interactive)
(run-with-idle-timer (if (and index
0 nil ;; No need to move if it's the last one.
(lambda (index) (< index (exwm-workspace--count)))
(if (and index (exwm-workspace-move (make-frame) index)
;; No need to move if it's the last one. (make-frame)))
(< index (exwm-workspace--count)))
(exwm-workspace-move (make-frame) index)
(make-frame)))
index)
(when (active-minibuffer-window)
(abort-recursive-edit)))
;;;###autoload ;;;###autoload
(defun exwm-workspace-delete (&optional frame-or-index) (defun exwm-workspace-delete (&optional frame-or-index)
"Delete the workspace FRAME-OR-INDEX." "Delete the workspace FRAME-OR-INDEX."
(interactive) (interactive)
(run-with-idle-timer 0 nil #'delete-frame (delete-frame
;; We should not simply delete the selected frame (if frame-or-index
;; since it can be e.g. a floating frame. (exwm-workspace--workspace-from-frame-or-index frame-or-index)
(if frame-or-index exwm-workspace--current)))
(exwm-workspace--workspace-from-frame-or-index
frame-or-index)
exwm-workspace--current))
(when (active-minibuffer-window)
(abort-recursive-edit)))
(defun exwm-workspace--on-focus-in () (defun exwm-workspace--on-focus-in ()
"Handle unexpected frame switch." "Handle unexpected frame switch."
@ -601,7 +634,9 @@ INDEX must not exceed the current number of workspaces."
(defun exwm-workspace-move-window (frame-or-index &optional id) (defun exwm-workspace-move-window (frame-or-index &optional id)
"Move window ID to workspace FRAME-OR-INDEX." "Move window ID to workspace FRAME-OR-INDEX."
(interactive (list (interactive (list
(exwm-workspace--prompt-for-workspace "Move to: "))) (let ((exwm-workspace--prompt-add-allowed t)
(exwm-workspace--prompt-delete-allowed t))
(exwm-workspace--prompt-for-workspace "Move to [+/-]: "))))
(let ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index))) (let ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index)))
(unless id (setq id (exwm--buffer->id (window-buffer)))) (unless id (setq id (exwm--buffer->id (window-buffer))))
(with-current-buffer (exwm--id->buffer id) (with-current-buffer (exwm--id->buffer id)
@ -874,6 +909,9 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
:window window :window window
:value-mask xcb:ConfigWindow:Height :value-mask xcb:ConfigWindow:Height
:height height))) :height height)))
(when (/= (exwm-workspace--count) (length exwm-workspace--workareas))
;; There is a chance the workareas are not updated timely.
(exwm-workspace--update-workareas))
(setq workarea (elt exwm-workspace--workareas (setq workarea (elt exwm-workspace--workareas
exwm-workspace-current-index) exwm-workspace-current-index)
y (if (eq exwm-workspace-minibuffer-position 'top) y (if (eq exwm-workspace-minibuffer-position 'top)
@ -1064,8 +1102,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
(exwm--log "Frame `%s' is floating" frame)) (exwm--log "Frame `%s' is floating" frame))
(t (t
(exwm--log "Adding frame `%s' as workspace" frame) (exwm--log "Adding frame `%s' as workspace" frame)
(setq exwm-workspace--list (nconc exwm-workspace--list (list frame)) (setq exwm-workspace--list (nconc exwm-workspace--list (list frame)))
exwm-workspace--current frame)
(let ((outer-id (string-to-number (frame-parameter frame (let ((outer-id (string-to-number (frame-parameter frame
'outer-window-id))) 'outer-window-id)))
(container (xcb:generate-id exwm--connection)) (container (xcb:generate-id exwm--connection))
@ -1093,6 +1130,11 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
xcb:CW:EventMask) xcb:CW:EventMask)
:override-redirect 1 :override-redirect 1
:event-mask xcb:EventMask:SubstructureRedirect)) :event-mask xcb:EventMask:SubstructureRedirect))
(xcb:+request exwm--connection
(make-instance 'xcb:ConfigureWindow
:window workspace
:value-mask xcb:ConfigWindow:StackMode
:stack-mode xcb:StackMode:Below))
(xcb:+request exwm--connection (xcb:+request exwm--connection
(make-instance 'xcb:CreateWindow (make-instance 'xcb:CreateWindow
:depth 0 :wid container :parent workspace :depth 0 :wid container :parent workspace
@ -1129,7 +1171,9 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first."
frame 'fullscreen 'fullboth) frame 'fullscreen 'fullboth)
;; Update EWMH properties. ;; Update EWMH properties.
(exwm-workspace--update-ewmh-props) (exwm-workspace--update-ewmh-props)
(exwm-workspace-switch frame t) (if exwm-workspace--create-silently
(setq exwm-workspace--switch-history-outdated t)
(exwm-workspace-switch frame t))
(run-hooks 'exwm-workspace-list-change-hook)))) (run-hooks 'exwm-workspace-list-change-hook))))
(defun exwm-workspace--remove-frame-as-workspace (frame) (defun exwm-workspace--remove-frame-as-workspace (frame)