Allow adding and removing workspaces

Making and deleting frames adds them as new workspaces.
This commit is contained in:
Urbano Ustero 2015-08-26 00:40:28 +02:00
parent 5222dc17d6
commit 0ec34061b9
2 changed files with 77 additions and 37 deletions

View file

@ -76,7 +76,7 @@
(unless default-geometry ;assume the first output as primary (unless default-geometry ;assume the first output as primary
(setq default-geometry geometry))))))) (setq default-geometry geometry)))))))
(cl-assert (<= 2 (length output-plist))) (cl-assert (<= 2 (length output-plist)))
(dotimes (i exwm-workspace-number) (dotimes (i (length exwm-workspace--list))
(let* ((output (plist-get exwm-randr-workspace-output-plist i)) (let* ((output (plist-get exwm-randr-workspace-output-plist i))
(geometry (lax-plist-get output-plist output)) (geometry (lax-plist-get output-plist output))
(frame (elt exwm-workspace--list i))) (frame (elt exwm-workspace--list i)))

View file

@ -41,7 +41,7 @@
(define-key map "\C-a" (lambda () (interactive) (goto-history-element 1))) (define-key map "\C-a" (lambda () (interactive) (goto-history-element 1)))
(define-key map "\C-e" (lambda () (define-key map "\C-e" (lambda ()
(interactive) (interactive)
(goto-history-element exwm-workspace-number))) (goto-history-element (length exwm-workspace--list))))
(define-key map "\C-g" 'abort-recursive-edit) (define-key map "\C-g" 'abort-recursive-edit)
(define-key map "\C-]" 'abort-recursive-edit) (define-key map "\C-]" 'abort-recursive-edit)
(define-key map "\C-j" 'exit-minibuffer) (define-key map "\C-j" 'exit-minibuffer)
@ -61,8 +61,9 @@
(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."
(let ((sequence (number-sequence 0 (1- exwm-workspace-number))) (let* ((num (length exwm-workspace--list))
(not-empty (make-vector exwm-workspace-number nil))) (sequence (number-sequence 0 (1- num)))
(not-empty (make-vector num nil)))
(dolist (i exwm--id-buffer-alist) (dolist (i exwm--id-buffer-alist)
(with-current-buffer (cdr i) (with-current-buffer (cdr i)
(when exwm--frame (when exwm--frame
@ -104,7 +105,7 @@ The optional FORCE option is for internal use only."
. ,(1+ exwm-workspace-current-index))))) . ,(1+ exwm-workspace-current-index)))))
(cl-position idx exwm-workspace--switch-history :test 'equal))))) (cl-position idx exwm-workspace--switch-history :test 'equal)))))
(when index (when index
(unless (and (<= 0 index) (< index exwm-workspace-number)) (unless (and (<= 0 index) (< index (length exwm-workspace--list)))
(user-error "[EXWM] Workspace index out of range: %d" index)) (user-error "[EXWM] Workspace index out of range: %d" index))
(when (or force (/= exwm-workspace-current-index index)) (when (or force (/= exwm-workspace-current-index index))
(let ((frame (elt exwm-workspace--list index))) (let ((frame (elt exwm-workspace--list index)))
@ -163,7 +164,7 @@ The optional FORCE option is for internal use only."
. ,(1+ exwm-workspace-current-index))))) . ,(1+ exwm-workspace-current-index)))))
(cl-position idx exwm-workspace--switch-history :test 'equal)))) (cl-position idx exwm-workspace--switch-history :test 'equal))))
(unless id (setq id (exwm--buffer->id (window-buffer)))) (unless id (setq id (exwm--buffer->id (window-buffer))))
(unless (and (<= 0 index) (< index exwm-workspace-number)) (unless (and (<= 0 index) (< index (length exwm-workspace--list)))
(user-error "[EXWM] Workspace index out of range: %d" index)) (user-error "[EXWM] Workspace index out of range: %d" index))
(with-current-buffer (exwm--id->buffer id) (with-current-buffer (exwm--id->buffer id)
(let ((frame (elt exwm-workspace--list index))) (let ((frame (elt exwm-workspace--list index)))
@ -211,6 +212,71 @@ The optional FORCE option is for internal use only."
(setq newname (format "%s<%d>" basename (cl-incf counter)))) (setq newname (format "%s<%d>" basename (cl-incf counter))))
(rename-buffer (concat (and hidden " ") newname)))) (rename-buffer (concat (and hidden " ") newname))))
(defun exwm-workspace--add-frame-as-workspace (frame)
"Configure frame FRAME to be treated as a workspace."
(cond
((>= (length exwm-workspace--list) exwm-workspace-number)
(delete-frame frame)
(user-error "[EXWM] Too many workspaces: maximum is %d" exwm-workspace-number))
((memq frame exwm-workspace--list)
(exwm--log "Frame is already a workspace: %s" frame))
(t
(exwm--log "Adding workspace: %s" frame)
(setq exwm-workspace--list (nconc exwm-workspace--list (list frame)))
(let ((window-id (string-to-number (frame-parameter frame 'window-id)))
(outer-id (string-to-number (frame-parameter frame 'outer-window-id))))
;; Save window IDs
(set-frame-parameter frame 'exwm-window-id window-id)
(set-frame-parameter frame 'exwm-outer-id outer-id)
;; Set OverrideRedirect on all frames
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window outer-id :value-mask xcb:CW:OverrideRedirect
:override-redirect 1))
;; Select events on all virtual roots
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window window-id :value-mask xcb:CW:EventMask
:event-mask xcb:EventMask:SubstructureRedirect)))
(xcb:flush exwm--connection)
;; We have to delay making the frame visible until the
;; override-redirect flag has been set.
(set-frame-parameter frame 'visibility t)
(lower-frame frame)
(set-frame-parameter frame 'fullscreen 'fullboth)
;; Update EWMH properties.
(exwm-workspace--update-ewmh-props)
;; Update switch history.
(exwm-workspace--update-switch-history))))
(defun exwm-workspace--remove-frame-as-workspace (frame)
"Stop treating frame FRAME as a workspace."
(cond
((= 1 (llength exwm-workspace--list))
(exwm--log "Cannot remove last workspace"))
((not (memq frame exwm-workspace--list))
(exwm--log "Frame is not a workspace: %s" frame))
(t
(exwm--log "Removing workspace: %s" frame)
(setq exwm-workspace--list (delete frame exwm-workspace--list))
;; Update EWMH properties.
(exwm-workspace--update-ewmh-props)
;; Update switch history.
(exwm-workspace--update-switch-history))))
(defun exwm-workspace--update-ewmh-props ()
"Update EWMH properties to match the workspace list."
;; Set _NET_VIRTUAL_ROOTS
(let ((num-workspaces (exwm-workspace--count)))
(xcb:+request exwm--connection
(make-instance 'xcb:ewmh:set-_NET_VIRTUAL_ROOTS
:window exwm--root
:data (vconcat (mapcar
(lambda (i)
(frame-parameter i 'exwm-window-id))
exwm-workspace--list))))
(xcb:flush exwm--connection)))
(defun exwm-workspace--init () (defun exwm-workspace--init ()
"Initialize workspace module." "Initialize workspace module."
(cl-assert (and (< 0 exwm-workspace-number) (>= 10 exwm-workspace-number))) (cl-assert (and (< 0 exwm-workspace-number) (>= 10 exwm-workspace-number)))
@ -238,40 +304,14 @@ The optional FORCE option is for internal use only."
(visibility . nil)))))) (visibility . nil))))))
;; Configure workspaces ;; Configure workspaces
(dolist (i exwm-workspace--list) (dolist (i exwm-workspace--list)
(let ((window-id (string-to-int (frame-parameter i 'window-id))) (exwm-workspace--add-frame-as-workspace i))
(outer-id (string-to-int (frame-parameter i 'outer-window-id))))
;; Save window IDs
(set-frame-parameter i 'exwm-window-id window-id)
(set-frame-parameter i 'exwm-outer-id outer-id)
;; Set OverrideRedirect on all frames
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window outer-id :value-mask xcb:CW:OverrideRedirect
:override-redirect 1))
;; Select events on all virtual roots
(xcb:+request exwm--connection
(make-instance 'xcb:ChangeWindowAttributes
:window window-id :value-mask xcb:CW:EventMask
:event-mask xcb:EventMask:SubstructureRedirect))))
(xcb:flush exwm--connection)
;; We have to delay making the frame visible until the
;; override-redirect flag has been set.
(select-frame-set-input-focus (car exwm-workspace--list)) (select-frame-set-input-focus (car exwm-workspace--list))
(dolist (i exwm-workspace--list)
(set-frame-parameter i 'visibility t)
(lower-frame i)
(set-frame-parameter i 'fullscreen 'fullboth))
(raise-frame (car exwm-workspace--list))
;; Handle unexpected frame switch ;; Handle unexpected frame switch
(add-hook 'focus-in-hook 'exwm-workspace--on-focus-in) (add-hook 'focus-in-hook 'exwm-workspace--on-focus-in)
;; Set _NET_VIRTUAL_ROOTS ;; Make new frames create new workspaces.
(xcb:+request exwm--connection (setq window-system-default-frame-alist '((x . ((visibility . nil)))))
(make-instance 'xcb:ewmh:set-_NET_VIRTUAL_ROOTS (add-hook 'after-make-frame-functions #'exwm-workspace--add-frame-as-workspace)
:window exwm--root (add-hook 'delete-frame-functions #'exwm-workspace--remove-frame-as-workspace)
:data (vconcat (mapcar
(lambda (i)
(frame-parameter i 'exwm-window-id))
exwm-workspace--list))))
;; Switch to the first workspace ;; Switch to the first workspace
(exwm-workspace-switch 0 t)) (exwm-workspace-switch 0 t))