diff options
Diffstat (limited to 'users/tazjin/emacs/config/desktop.el')
-rw-r--r-- | users/tazjin/emacs/config/desktop.el | 331 |
1 files changed, 199 insertions, 132 deletions
diff --git a/users/tazjin/emacs/config/desktop.el b/users/tazjin/emacs/config/desktop.el index 38da8f75bc..aa232fec2f 100644 --- a/users/tazjin/emacs/config/desktop.el +++ b/users/tazjin/emacs/config/desktop.el @@ -4,13 +4,27 @@ ;; window-management (EXWM) as well as additional system-wide ;; commands. -(require 's) -(require 'f) -(require 'dash) (require 'exwm) (require 'exwm-config) (require 'exwm-randr) (require 'exwm-systemtray) +(require 'exwm-xim ) +(require 'f) +(require 'ring) +(require 's) +(require 'seq) + +(defcustom tazjin--screen-lock-command "tazjin-screen-lock" + "Command to execute for locking the screen." + :group 'tazjin) + +(defcustom tazjin--backlight-increase-command "light -A 4" + "Command to increase screen brightness." + :group 'tazjin) + +(defcustom tazjin--backlight-decrease-command "light -U 4" + "Command to decrease screen brightness." + :group 'tazjin) (defun pactl (cmd) (shell-command (concat "pactl " cmd)) @@ -22,24 +36,14 @@ (defun brightness-up () (interactive) - (shell-command "xbacklight -inc 5") + (shell-command tazjin--backlight-increase-command) (message "Brightness increased")) (defun brightness-down () (interactive) - (shell-command "xbacklight -dec 5") + (shell-command tazjin--backlight-decrease-command) (message "Brightness decreased")) -(defun lock-screen () - (interactive) - ;; A sudoers configuration is in place that lets me execute this - ;; particular command without having to enter a password. - ;; - ;; The reason for things being set up this way is that I want - ;; xsecurelock.service to be started as a system-wide service that - ;; is tied to suspend.target. - (shell-command "/usr/bin/sudo /usr/bin/systemctl start xsecurelock.service")) - (defun set-xkb-layout (layout) "Set the current X keyboard layout." @@ -47,6 +51,12 @@ (shell-command "setxkbmap -option caps:super") (message "Set X11 keyboard layout to '%s'" layout)) +(defun lock-screen () + (interactive) + (set-xkb-layout "us") + (deactivate-input-method) + (shell-command tazjin--screen-lock-command)) + (defun create-window-name () "Construct window names to be used for EXWM buffers by inspecting the window's X11 class and title. @@ -60,39 +70,22 @@ human-accessible titles." (pcase (list (or exwm-class-name "unknown") (or exwm-title "unknown")) - ;; In Cider windows, rename the class and keep the workspace/file - ;; as the title. - (`("Google-chrome" ,(and (pred (lambda (title) (s-ends-with? " - Cider" title))) title)) - (format "Cider<%s>" (s-chop-suffix " - Cider" title))) + ;; Yandex.Music -> `Я.Music<... stuff ...>' + (`("Chromium-browser" ,(and (pred (lambda (title) (s-starts-with? "Yandex.Music - " title))) title)) + (format "Я.Music<%s>" (s-chop-prefix "Yandex.Music - " title))) - ;; Attempt to detect IRCCloud windows via their title, which is a - ;; combination of the channel name and network. - ;; - ;; This is what would often be referred to as a "hack". The regexp - ;; will not work if a network connection buffer is selected in - ;; IRCCloud, but since the title contains no other indication that - ;; we're dealing with an IRCCloud window - (`("Google-chrome" - ,(and (pred (lambda (title) - (s-matches? "^[\*\+]\s#[a-zA-Z0-9/\-]+\s\|\s[a-zA-Z\.]+$" title))) - title)) - (format "IRCCloud<%s>" title)) - - ;; For other Chrome windows, make the title shorter. - (`("Google-chrome" ,title) - (format "Chrome<%s>" (s-truncate 42 (s-chop-suffix " - Google Chrome" title)))) - - ;; Gnome-terminal -> Term - (`("Gnome-terminal" ,title) - ;; fish-shell buffers contain some unnecessary whitespace and - ;; such before the current working directory. This can be - ;; stripped since most of my terminals are fish shells anyways. - (format "Term<%s>" (s-trim-left (s-chop-prefix "fish" title)))) + ;; For other Chromium windows, make the title shorter. + (`("Chromium-browser" ,title) + (format "Chromium<%s>" (s-truncate 42 (s-chop-suffix " - Chromium" title)))) + + ;; similarly for Firefox + (`("firefox" ,title) + (format "FF<%s>" title)) ;; Quassel buffers ;; ;; These have a title format that looks like: - ;; "Quassel IRC - ##tvl (Freenode) — Quassel IRC" + ;; "Quassel IRC - #tvl (hackint) — Quassel IRC" (`("quassel" ,title) (progn (if (string-match @@ -109,9 +102,6 @@ (`(,class ,title) (format "%s<%s>" class (s-truncate 12 title))))) ;; EXWM launch configuration -;; -;; This used to use use-package, but when something breaks use-package -;; it doesn't exactly make debugging any easier. (let ((titlef (lambda () (exwm-workspace-rename-buffer (create-window-name))))) @@ -119,23 +109,57 @@ (add-hook 'exwm-update-title-hook titlef)) (fringe-mode 3) -(exwm-enable) -;; 's-N': Switch to certain workspace -(setq exwm-workspace-number 10) -(dotimes (i 10) - (exwm-input-set-key (kbd (format "s-%d" i)) - `(lambda () - (interactive) - (exwm-workspace-switch-create ,i)))) +;; tab-bar related config +(setq tab-bar-show 1) +(setq tab-bar-tab-hints t) -;; Launch applications / any command with completion (dmenu style!) -(exwm-input-set-key (kbd "s-d") #'counsel-linux-app) -(exwm-input-set-key (kbd "s-x") #'ivy-run-external-command) -(exwm-input-set-key (kbd "s-p") #'ivy-password-store) +(setq tab-bar-format + '(tab-bar-format-history + tab-bar-format-tabs tab-bar-separator + tab-bar-format-align-right tab-bar-format-global)) -;; Add X11 terminal selector to a key -(exwm-input-set-key (kbd "C-x t") #'ts/switch-to-terminal) +(setq tab-bar-new-tab-choice + (lambda () (get-buffer-create "*scratch*"))) + +(tab-bar-mode 1) + +(setq x-no-window-manager t) ;; TODO(tazjin): figure out when to remove this +(exwm-enable) +(exwm-randr-enable) + +;; Tab-management shortcuts + +(defun tab-bar-select-or-return () + "This function behaves like `tab-bar-select-tab', except it calls +`tab-recent' if asked to jump to the current tab. This simulates +the back&forth behaviour of i3." + (interactive) + (let* ((key (event-basic-type last-command-event)) + (tab (if (and (characterp key) (>= key ?1) (<= key ?9)) + (- key ?0) + 0)) + (current (1+ (tab-bar--current-tab-index)))) + (if (eq tab current) + (tab-recent) + (tab-bar-select-tab tab)))) + +(dotimes (i 8) + (exwm-input-set-key (kbd (format "s-%d" (+ 1 i))) #'tab-bar-select-or-return)) + +(exwm-input-set-key (kbd "s-9") #'tab-last) +(exwm-input-set-key (kbd "s-f") #'tab-next) +(exwm-input-set-key (kbd "s-b") #'tab-recent) +(exwm-input-set-key (kbd "s-w") #'tab-close) +(exwm-input-set-key (kbd "s-n") #'tab-new) + +;; Launch applications / any command with completion (dmenu style!) +(exwm-input-set-key (kbd "s-d") #'run-xdg-app) +(exwm-input-set-key (kbd "s-x") #'run-external-command) +(exwm-input-set-key (kbd "s-p") #'password-store-lookup) + +;; Add vterm selector to a key +(exwm-input-set-key (kbd "s-v") #'ts/switch-to-terminal) ;; Toggle between line-mode / char-mode (exwm-input-set-key (kbd "C-c C-t C-t") #'exwm-input-toggle-keyboard) @@ -162,19 +186,19 @@ (bind-xkb "no" "k n") (bind-xkb "ru" "k r") (bind-xkb "se" "k s") +(bind-xkb "us" "л г") +(bind-xkb "de" "л в") +(bind-xkb "no" "л т") +(bind-xkb "ru" "л к") -;; These are commented out because Emacs no longer starts (??) if -;; they're set at launch. -;; -;; (bind-xkb "us" "л г") -;; (bind-xkb "de" "л в") -;; (bind-xkb "no" "л т") -;; (bind-xkb "ru" "л к") +;; Configuration of EXWM input method handling for X applications +(exwm-xim-enable) +(setq default-input-method "russian-computer") +(push ?\C-\\ exwm-input-prefix-keys) ;; Line-editing shortcuts -(exwm-input-set-simulation-keys - '(([?\C-d] . delete) - ([?\C-w] . ?\C-c))) +(exwm-input-set-simulation-key (kbd "C-d") (kbd "DEL")) +(exwm-input-set-simulation-key (kbd "C-w") (kbd "C-c")) ;; Show time & battery status in the mode line (display-time-mode) @@ -183,80 +207,123 @@ ;; enable display of X11 system tray within Emacs (exwm-systemtray-enable) -;; Configure xrandr (multi-monitor setup). +;; Multi-monitor configuration. ;; -;; This makes some assumptions about how my machines are connected to -;; my home setup during the COVID19 isolation period. - -(defun set-randr-config (screens) - (setq exwm-randr-workspace-monitor-plist - (-flatten (-map (lambda (screen) - (-map (lambda (screen-id) (list screen-id (car screen))) (cdr screen))) - screens)))) - -;; Layouts for Vauxhall (laptop) +;; With tab-bar-mode, each monitor only displays at most one +;; workspace. Workspaces are only created, never deleted, meaning that +;; the number of workspaces will be equivalent to the maximum number +;; of displays that were connected during a session. +;; +;; The first workspace is special: It is kept on the primary monitor. -(defun randr-vauxhall-layout-single () - "Laptop screen only!" +(defun exwm-assign-workspaces () + "Assigns workspaces to the currently existing monitors, putting +the first one on the primary display and allocating the others +dynamically if needed in no particular order." (interactive) - (set-randr-config '(("eDP1" (number-sequence 0 9)))) - (shell-command "xrandr --output eDP1 --auto --primary") - (shell-command "xrandr --output HDMI1 --off") - (shell-command "xrandr --output DP2 --off") - (exwm-randr-refresh)) - -(defun randr-vauxhall-layout-all () - "Use all screens at home." + (let* ((randr-monitors (exwm-randr--get-monitors)) + (primary (car randr-monitors)) + (all-monitors (seq-map #'car (cadr randr-monitors))) + (sorted-primary-first (seq-sort (lambda (a b) + (or (equal a primary) + (< a b))) + all-monitors)) + ;; assign workspace numbers to each monitor ... + (workspace-assignments + (flatten-list (seq-map-indexed (lambda (monitor idx) + (list idx monitor)) + sorted-primary-first)))) + ;; ensure that the required workspaces exist + (exwm-workspace-switch-create (- (seq-length all-monitors) 1)) + + ;; update randr config + (setq exwm-randr-workspace-monitor-plist workspace-assignments) + (exwm-randr-refresh) + + ;; leave focus on primary workspace + (exwm-workspace-switch 0))) + +(defun list-available-monitors () + "List connected, but unused monitors." + (let* ((all-connected + (seq-map (lambda (line) (car (s-split " " line))) + (s-lines (s-trim (shell-command-to-string "xrandr | grep connected | grep -v disconnected"))))) + (all-active (seq-map #'car (cadr (exwm-randr--get-monitors))))) + (seq-filter (lambda (s) (not (seq-contains-p all-active s))) + all-connected))) + +(defun exwm-enable-monitor () + "Interactively construct an EXWM invocation that enable the +given monitor and assigns a workspace to it." (interactive) - (set-randr-config - '(("eDP1" 0) - ("HDMI1" 1 2 3 4 5) - ("DP2" 6 7 8 9))) - - (shell-command "xrandr --output HDMI1 --right-of eDP1 --auto --primary") - (shell-command "xrandr --output DP2 --right-of HDMI1 --auto --rotate left") - (exwm-randr-refresh)) -(defun randr-vauxhall-layout-wide-only () - "Use only the wide screen at home." + (let* ((monitors (list-available-monitors)) + (primary (car (exwm-randr--get-monitors))) + (monitor (pcase (seq-length monitors) + (0 (error "No available monitors.")) + (1 (car monitors)) + (_ + (completing-read "Which monitor? " (list-available-monitors) nil t)))) + + (configurations `(("secondary (left)" . ,(format "--left-of %s" primary)) + ("secondary (right)" . ,(format "--right-of %s" primary)) + ("primary (left)" . ,(format "--left-of %s --primary" primary)) + ("primary (right)" . ,(format "--right-of %s --primary" primary)) + ("mirror" . ,(format "--same-as %s" primary)))) + + (where (completing-read (format "%s should be " monitor) + (seq-map #'car configurations) + nil t)) + (xrandr-pos (cdr (assoc where configurations))) + (xrandr-cmd (format "xrandr --output %s --auto %s" monitor xrandr-pos))) + (message "Invoking '%s'" xrandr-cmd) + (shell-command xrandr-cmd) + (exwm-assign-workspaces))) + +(defun exwm-disable-monitor () + "Interactively choose a monitor to disable." (interactive) - (set-randr-config - '(("eDP1" 8 9 0) - ("HDMI1" 1 2 4 5 6 7))) - (shell-command "xrandr --output DP2 --off") - (shell-command "xrandr --output HDMI1 --right-of eDP1 --auto --primary") - (exwm-randr-refresh)) + (let* ((all (exwm-randr--get-monitors)) + (active (seq-map #'car (cadr all))) + (monitor (if (> (seq-length active) 1) + (completing-read "Disable which monitor? " active nil t) + (error "Only one monitor is active!"))) -;; Layouts for frog (desktop) + ;; If this monitor was primary, pick another active one instead. + (remaining (seq-filter (lambda (s) (not (equal s monitor))) active)) + (new-primary + (when (equal monitor (car all)) + (pcase (seq-length remaining) + (1 (car remaining)) + (_ (completing-read "New primary? " remaining nil t)))))) -(defun randr-frog-layout-right-only () - "Use only the right screen on frog." - (interactive) - (set-randr-config `(("DisplayPort-0" ,(number-sequence 0 9)))) - (shell-command "xrandr --output DisplayPort-0 --off") - (shell-command "xrandr --output DisplayPort-1 --auto --primary")) + (when new-primary + (shell-command (format "xrandr --output %s --primary" new-primary))) -(defun randr-frog-layout-both () - "Use the left and right screen on frog." - (interactive) - (set-randr-config `(("DisplayPort-0" 1 2 3 4 5) - ("DisplayPort-1" 6 7 8 9 0))) + (shell-command (format "xrandr --output %s --off" monitor)) + (exwm-assign-workspaces))) - (shell-command "xrandr --output DisplayPort-0 --auto --primary --left-of DisplayPort-1") - (shell-command "xrandr --output DisplayPort-1 --auto --right-of DisplayPort-0 --rotate left")) - -(pcase (s-trim (shell-command-to-string "hostname")) - ("vauxhall" - (exwm-input-set-key (kbd "s-m s") #'randr-vauxhall-layout-single) - (exwm-input-set-key (kbd "s-m a") #'randr-vauxhall-layout-all) - (exwm-input-set-key (kbd "s-m w") #'randr-vauxhall-layout-wide-only)) - - ("frog" - (exwm-input-set-key (kbd "s-m b") #'randr-frog-layout-both) - (exwm-input-set-key (kbd "s-m r") #'randr-frog-layout-right-only))) +(defun exwm-switch-monitor () + "Switch focus to another monitor by name." + (interactive) -(exwm-randr-enable) + ;; TODO: Filter out currently active? How to determine it? + (let* ((target (completing-read "Switch to monitor: " + (seq-map #'car (cadr (exwm-randr--get-monitors))) + nil t)) + (target-workspace + (cl-loop for (workspace screen) on exwm-randr-workspace-monitor-plist by #'cddr + when (equal screen target) return workspace))) + (exwm-workspace-switch target-workspace))) + +(exwm-input-set-key (kbd "s-m e") #'exwm-enable-monitor) +(exwm-input-set-key (kbd "s-m d") #'exwm-disable-monitor) +(exwm-input-set-key (kbd "s-m o") #'exwm-switch-monitor) + +;; Notmuch shortcuts as EXWM globals +;; (g m => gmail) +(exwm-input-set-key (kbd "s-g m") #'notmuch) ;; Let buffers move seamlessly between workspaces by making them ;; accessible in selectors on all frames. |