2020-08-31 15:36:55 +02:00
|
|
|
;;; window-manager.el --- Functions augmenting my usage of EXWM -*- lexical-binding: t -*-
|
|
|
|
|
2019-10-09 13:13:56 +02:00
|
|
|
;; Author: William Carroll <wpcarro@gmail.com>
|
2020-08-31 15:36:55 +02:00
|
|
|
;; Version: 0.0.1
|
|
|
|
;; Package-Requires: ((emacs "25.1"))
|
2019-10-09 13:13:56 +02:00
|
|
|
|
|
|
|
;;; Commentary:
|
2019-12-23 12:19:42 +01:00
|
|
|
;; I switched to EXWM from i3, and I haven't looked back. One day I may write a
|
|
|
|
;; poem declaring my love for Emacs and EXWM. For now, I haven't the time.
|
|
|
|
|
2022-01-21 00:53:19 +01:00
|
|
|
;; Wist List:
|
|
|
|
;; - TODO: Consider supporting MRU cache of worksapces.
|
2019-10-09 13:13:56 +02:00
|
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
2020-01-08 16:16:26 +01:00
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; Dependencies
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
2020-01-15 23:11:53 +01:00
|
|
|
(require 'alert)
|
2020-09-02 15:00:43 +02:00
|
|
|
(require 'al)
|
2019-10-09 13:13:56 +02:00
|
|
|
(require 'prelude)
|
|
|
|
(require 'string)
|
|
|
|
(require 'cycle)
|
|
|
|
(require 'set)
|
|
|
|
(require 'kbd)
|
|
|
|
(require 'ivy-helpers)
|
|
|
|
(require 'display)
|
2020-02-08 17:00:31 +01:00
|
|
|
(require 'vterm-mgt)
|
2020-02-02 14:20:55 +01:00
|
|
|
(require 'dash)
|
2019-10-09 13:13:56 +02:00
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; Library
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
2020-09-29 12:21:09 +02:00
|
|
|
(cl-defstruct window-manager--named-workspace label kbd display)
|
2022-01-21 00:47:23 +01:00
|
|
|
(defgroup window-manager)
|
2019-10-09 13:13:56 +02:00
|
|
|
|
2020-08-31 15:36:55 +02:00
|
|
|
(defconst window-manager--install-kbds? t
|
2019-10-09 13:13:56 +02:00
|
|
|
"When t, install the keybindings to switch between named-workspaces.")
|
|
|
|
|
2020-08-31 15:36:55 +02:00
|
|
|
(defconst window-manager--named-workspaces
|
|
|
|
(list (make-window-manager--named-workspace
|
2020-10-03 15:48:46 +02:00
|
|
|
:label "Web Browsing"
|
2020-09-29 12:21:09 +02:00
|
|
|
:kbd "c"
|
|
|
|
:display display-4k-horizontal)
|
2020-08-31 15:36:55 +02:00
|
|
|
(make-window-manager--named-workspace
|
2020-10-03 15:48:46 +02:00
|
|
|
:label "Coding"
|
2020-09-29 12:21:09 +02:00
|
|
|
:kbd "d"
|
2021-10-08 04:02:29 +02:00
|
|
|
:display display-4k-horizontal)
|
|
|
|
(make-window-manager--named-workspace
|
|
|
|
:label "Vertical"
|
|
|
|
:kbd "h"
|
|
|
|
:display display-4k-vertical)
|
|
|
|
(make-window-manager--named-workspace
|
|
|
|
:label "Laptop"
|
|
|
|
:kbd "p"
|
|
|
|
:display display-laptop))
|
2020-08-31 15:36:55 +02:00
|
|
|
"List of `window-manager--named-workspace' structs.")
|
2019-10-09 13:13:56 +02:00
|
|
|
|
|
|
|
;; Assert that no two workspaces share KBDs.
|
2020-09-01 11:17:43 +02:00
|
|
|
(prelude-assert (= (list-length window-manager--named-workspaces)
|
2020-08-31 15:36:55 +02:00
|
|
|
(->> window-manager--named-workspaces
|
2020-09-01 11:17:43 +02:00
|
|
|
(list-map #'window-manager--named-workspace-kbd)
|
|
|
|
set-from-list
|
|
|
|
set-count)))
|
2019-10-09 13:13:56 +02:00
|
|
|
|
2020-08-31 15:36:55 +02:00
|
|
|
(defun window-manager--alert (x)
|
2019-10-09 13:13:56 +02:00
|
|
|
"Message X with a structured format."
|
2020-09-01 00:28:47 +02:00
|
|
|
(alert (string-concat "[exwm] " x)))
|
2019-10-09 13:13:56 +02:00
|
|
|
|
|
|
|
;; Use Emacs as my primary window manager.
|
|
|
|
(use-package exwm
|
|
|
|
:config
|
|
|
|
(require 'exwm-config)
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; Multiple Displays
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(require 'exwm-randr)
|
|
|
|
(exwm-randr-enable)
|
2020-01-11 21:01:57 +01:00
|
|
|
(setq exwm-randr-workspace-monitor-plist
|
2020-09-29 12:21:09 +02:00
|
|
|
(->> window-manager--named-workspaces
|
|
|
|
(-map-indexed (lambda (i x)
|
|
|
|
(list i (window-manager--named-workspace-display x))))
|
|
|
|
-flatten))
|
2020-10-04 14:56:01 +02:00
|
|
|
(setq exwm-workspace-number (list-length window-manager--named-workspaces))
|
2019-10-09 13:13:56 +02:00
|
|
|
(setq exwm-input-simulation-keys
|
|
|
|
'(([?\C-b] . [left])
|
|
|
|
([?\M-b] . [C-left])
|
|
|
|
([?\C-f] . [right])
|
|
|
|
([?\M-f] . [C-right])
|
|
|
|
([?\C-p] . [up])
|
|
|
|
([?\C-n] . [down])
|
|
|
|
([?\C-a] . [home])
|
|
|
|
([?\C-e] . [end])
|
|
|
|
([?\C-d] . [delete])
|
|
|
|
([?\C-c] . [C-c])))
|
|
|
|
(exwm-enable))
|
2022-01-21 00:47:23 +01:00
|
|
|
(defcustom window-manager-screenlocker "xsecurelock"
|
|
|
|
"Reference to a screen-locking executable."
|
|
|
|
:group 'window-manager)
|
2019-10-09 13:13:56 +02:00
|
|
|
|
|
|
|
;; Here is the code required to allow EXWM to cycle workspaces.
|
2020-08-31 15:36:55 +02:00
|
|
|
(defconst window-manager--workspaces
|
|
|
|
(->> window-manager--named-workspaces
|
2020-09-01 11:17:43 +02:00
|
|
|
cycle-from-list)
|
2019-10-09 13:13:56 +02:00
|
|
|
"Cycle of the my EXWM workspaces.")
|
|
|
|
|
2020-08-31 18:05:31 +02:00
|
|
|
(prelude-assert
|
2019-10-09 13:13:56 +02:00
|
|
|
(= exwm-workspace-number
|
2020-09-01 11:17:43 +02:00
|
|
|
(list-length window-manager--named-workspaces)))
|
2019-10-09 13:13:56 +02:00
|
|
|
|
2020-08-31 15:36:55 +02:00
|
|
|
(defun window-manager-next-workspace ()
|
2019-10-09 13:13:56 +02:00
|
|
|
"Cycle forwards to the next workspace."
|
|
|
|
(interactive)
|
2020-09-01 11:17:43 +02:00
|
|
|
(window-manager--change-workspace (cycle-next window-manager--workspaces)))
|
2019-10-09 13:13:56 +02:00
|
|
|
|
2020-08-31 15:36:55 +02:00
|
|
|
(defun window-manager-prev-workspace ()
|
2019-10-09 13:13:56 +02:00
|
|
|
"Cycle backwards to the previous workspace."
|
|
|
|
(interactive)
|
2020-09-01 11:17:43 +02:00
|
|
|
(window-manager--change-workspace (cycle-prev window-manager--workspaces)))
|
2019-10-09 13:13:56 +02:00
|
|
|
|
|
|
|
;; Here is the code required to toggle EXWM's modes.
|
2020-08-31 15:36:55 +02:00
|
|
|
(defun window-manager--line-mode ()
|
2019-10-09 13:13:56 +02:00
|
|
|
"Switch exwm to line-mode."
|
|
|
|
(call-interactively #'exwm-input-grab-keyboard)
|
2020-08-31 15:36:55 +02:00
|
|
|
(window-manager--alert "Switched to line-mode"))
|
2019-10-09 13:13:56 +02:00
|
|
|
|
2020-08-31 15:36:55 +02:00
|
|
|
(defun window-manager--char-mode ()
|
2019-10-09 13:13:56 +02:00
|
|
|
"Switch exwm to char-mode."
|
|
|
|
(call-interactively #'exwm-input-release-keyboard)
|
2020-08-31 15:36:55 +02:00
|
|
|
(window-manager--alert "Switched to char-mode"))
|
2019-10-09 13:13:56 +02:00
|
|
|
|
2020-08-31 15:36:55 +02:00
|
|
|
(defconst window-manager--modes
|
2020-09-01 11:17:43 +02:00
|
|
|
(cycle-from-list (list #'window-manager--char-mode
|
2020-08-31 15:36:55 +02:00
|
|
|
#'window-manager--line-mode))
|
2019-10-09 13:13:56 +02:00
|
|
|
"Functions to switch exwm modes.")
|
|
|
|
|
2020-08-31 15:36:55 +02:00
|
|
|
(defun window-manager-toggle-mode ()
|
2019-10-09 13:13:56 +02:00
|
|
|
"Switch between line- and char- mode."
|
|
|
|
(interactive)
|
|
|
|
(with-current-buffer (window-buffer)
|
|
|
|
(when (eq major-mode 'exwm-mode)
|
2020-09-01 11:17:43 +02:00
|
|
|
(funcall (cycle-next window-manager--modes)))))
|
2019-10-09 13:13:56 +02:00
|
|
|
|
|
|
|
;; Ensure exwm apps open in char-mode.
|
2020-08-31 15:36:55 +02:00
|
|
|
(add-hook 'exwm-manage-finish-hook #'window-manager--char-mode)
|
2019-10-09 13:13:56 +02:00
|
|
|
|
|
|
|
|
2020-08-31 15:36:55 +02:00
|
|
|
(defun window-manager--label->index (label workspaces)
|
2019-10-09 13:13:56 +02:00
|
|
|
"Return the index of the workspace in WORKSPACES named LABEL."
|
2020-08-31 17:07:11 +02:00
|
|
|
(let ((index (-elem-index label (-map #'window-manager--named-workspace-label
|
|
|
|
workspaces))))
|
2020-04-02 19:36:51 +02:00
|
|
|
(if index index (error (format "No workspace found for label: %s" label)))))
|
2019-10-09 13:13:56 +02:00
|
|
|
|
2020-08-31 15:36:55 +02:00
|
|
|
(defun window-manager--register-kbd (workspace)
|
2019-10-09 13:13:56 +02:00
|
|
|
"Registers a keybinding for WORKSPACE struct.
|
|
|
|
Currently using super- as the prefix for switching workspaces."
|
|
|
|
(let ((handler (lambda ()
|
|
|
|
(interactive)
|
2020-08-31 17:07:11 +02:00
|
|
|
(window-manager--switch
|
|
|
|
(window-manager--named-workspace-label workspace))))
|
2020-08-31 15:36:55 +02:00
|
|
|
(key (window-manager--named-workspace-kbd workspace)))
|
2019-10-09 13:13:56 +02:00
|
|
|
(exwm-input-set-key
|
2020-09-01 11:17:43 +02:00
|
|
|
(kbd-for 'workspace key)
|
2019-10-09 13:13:56 +02:00
|
|
|
handler)))
|
|
|
|
|
2020-08-31 15:36:55 +02:00
|
|
|
(defun window-manager--change-workspace (workspace)
|
2020-01-09 15:01:49 +01:00
|
|
|
"Switch EXWM workspaces to the WORKSPACE struct."
|
2020-04-02 19:36:51 +02:00
|
|
|
(exwm-workspace-switch
|
2020-08-31 17:07:11 +02:00
|
|
|
(window-manager--label->index
|
|
|
|
(window-manager--named-workspace-label workspace)
|
|
|
|
window-manager--named-workspaces))
|
2020-08-31 15:36:55 +02:00
|
|
|
(window-manager--alert
|
2020-09-01 00:28:47 +02:00
|
|
|
(string-format "Switched to: %s"
|
2020-08-31 17:07:11 +02:00
|
|
|
(window-manager--named-workspace-label workspace))))
|
2020-01-09 15:01:49 +01:00
|
|
|
|
2020-08-31 15:36:55 +02:00
|
|
|
(defun window-manager--switch (label)
|
2019-10-09 13:13:56 +02:00
|
|
|
"Switch to a named workspaces using LABEL."
|
2020-09-01 11:17:43 +02:00
|
|
|
(cycle-focus (lambda (x)
|
2020-01-09 15:01:49 +01:00
|
|
|
(equal label
|
2020-08-31 15:36:55 +02:00
|
|
|
(window-manager--named-workspace-label x)))
|
|
|
|
window-manager--workspaces)
|
2020-09-01 11:17:43 +02:00
|
|
|
(window-manager--change-workspace (cycle-current window-manager--workspaces)))
|
2020-01-09 15:01:49 +01:00
|
|
|
|
2020-08-31 15:36:55 +02:00
|
|
|
(defun window-manager-toggle-previous ()
|
2020-01-09 15:01:49 +01:00
|
|
|
"Focus the previously active EXWM workspace."
|
|
|
|
(interactive)
|
2020-08-31 17:07:11 +02:00
|
|
|
(window-manager--change-workspace
|
2020-09-01 11:17:43 +02:00
|
|
|
(cycle-focus-previous! window-manager--workspaces)))
|
2019-10-09 13:13:56 +02:00
|
|
|
|
2020-08-31 15:36:55 +02:00
|
|
|
(defun window-manager--exwm-buffer? (x)
|
2020-02-02 14:20:55 +01:00
|
|
|
"Return t if buffer X is an EXWM buffer."
|
|
|
|
(equal 'exwm-mode (buffer-local-value 'major-mode x)))
|
|
|
|
|
2020-08-31 15:36:55 +02:00
|
|
|
(defun window-manager--application-name (buffer)
|
2020-02-02 14:20:55 +01:00
|
|
|
"Return the name of the application running in the EXWM BUFFER.
|
2020-08-31 17:07:11 +02:00
|
|
|
This function asssumes that BUFFER passes the `window-manager--exwm-buffer?'
|
|
|
|
predicate."
|
2020-02-02 14:20:55 +01:00
|
|
|
(with-current-buffer buffer exwm-class-name))
|
|
|
|
|
|
|
|
;; TODO: Support disambiguating between two or more instances of the same
|
|
|
|
;; application. For instance if two `exwm-class-name' values are
|
|
|
|
;; "Google-chrome", find a encode this information in the `buffer-alist'.
|
2020-08-31 15:36:55 +02:00
|
|
|
(defun window-manager-switch-to-exwm-buffer ()
|
2020-02-02 14:20:55 +01:00
|
|
|
"Use `completing-read' to focus an EXWM buffer."
|
|
|
|
(interactive)
|
|
|
|
(let* ((buffer-alist (->> (buffer-list)
|
2020-08-31 15:36:55 +02:00
|
|
|
(-filter #'window-manager--exwm-buffer?)
|
2020-08-31 17:07:11 +02:00
|
|
|
(-map
|
|
|
|
(lambda (buffer)
|
|
|
|
(cons (window-manager--application-name buffer)
|
|
|
|
buffer)))))
|
2020-02-02 14:20:55 +01:00
|
|
|
(label (completing-read "Switch to EXWM buffer: " buffer-alist)))
|
|
|
|
(exwm-workspace-switch-to-buffer
|
2020-09-02 15:00:43 +02:00
|
|
|
(al-get label buffer-alist))))
|
2020-02-02 14:20:55 +01:00
|
|
|
|
2020-08-31 15:36:55 +02:00
|
|
|
(when window-manager--install-kbds?
|
2019-10-09 13:13:56 +02:00
|
|
|
(progn
|
2020-08-31 15:36:55 +02:00
|
|
|
(->> window-manager--named-workspaces
|
2020-09-01 11:17:43 +02:00
|
|
|
(list-map #'window-manager--register-kbd))
|
2020-08-31 15:36:55 +02:00
|
|
|
(window-manager--alert "Registered workspace KBDs!")))
|
2019-10-09 13:13:56 +02:00
|
|
|
|
2020-10-03 15:49:02 +02:00
|
|
|
(defun window-manager-current-workspace ()
|
|
|
|
"Output the label of the currently active workspace."
|
|
|
|
(->> window-manager--workspaces
|
|
|
|
cycle-current
|
|
|
|
window-manager--named-workspace-label))
|
|
|
|
|
2020-10-03 15:49:33 +02:00
|
|
|
(defun window-manager-swap-workspaces ()
|
|
|
|
"Prompt the user to switch the current workspace with another."
|
|
|
|
(interactive)
|
|
|
|
(let* ((selection (->> window-manager--named-workspaces
|
|
|
|
(-map #'window-manager--named-workspace-label)
|
|
|
|
(-reject
|
|
|
|
(lambda (x)
|
|
|
|
(s-equals? x (window-manager-current-workspace))))
|
|
|
|
(completing-read
|
|
|
|
(format "Swap current workspace (i.e. \"%s\") with: "
|
|
|
|
(window-manager-current-workspace)))))
|
|
|
|
(i (-find-index (lambda (x)
|
|
|
|
(s-equals? selection (window-manager--named-workspace-label x)))
|
|
|
|
window-manager--named-workspaces)))
|
|
|
|
(exwm-workspace-swap exwm-workspace--current (elt exwm-workspace--list i))))
|
|
|
|
|
2019-10-09 13:13:56 +02:00
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2020-08-31 15:36:55 +02:00
|
|
|
;; Startup Applications in `window-manager--named-workspaces'
|
2019-10-09 13:13:56 +02:00
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
2021-10-08 04:02:29 +02:00
|
|
|
(add-hook 'exwm-init-hook
|
|
|
|
(lambda ()
|
2021-12-16 06:11:06 +01:00
|
|
|
;; (display-arrange-primary)
|
2021-10-08 04:02:29 +02:00
|
|
|
(window-manager--switch "Coding")))
|
2020-01-08 16:16:26 +01:00
|
|
|
|
2019-10-09 13:13:56 +02:00
|
|
|
(provide 'window-manager)
|
|
|
|
;;; window-manager.el ends here
|