diff options
Diffstat (limited to 'users/tazjin/emacs/config/desktop.el')
-rw-r--r-- | users/tazjin/emacs/config/desktop.el | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/users/tazjin/emacs/config/desktop.el b/users/tazjin/emacs/config/desktop.el new file mode 100644 index 000000000000..51a3f208481d --- /dev/null +++ b/users/tazjin/emacs/config/desktop.el @@ -0,0 +1,360 @@ +;; -*- lexical-binding: t; -*- +;; +;; Configure desktop environment settings, including both +;; window-management (EXWM) as well as additional system-wide +;; commands. + +(require 'dash) +(require 'exwm) +(require 'exwm-config) +(require 'exwm-randr) +(require 'exwm-systemtray) +(require 'exwm-xim ) +(require 'f) +(require 'ring) +(require 's) + +(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)) + (message "Volume command: %s" cmd)) + +(defun volume-mute () (interactive) (pactl "set-sink-mute @DEFAULT_SINK@ toggle")) +(defun volume-up () (interactive) (pactl "set-sink-volume @DEFAULT_SINK@ +5%")) +(defun volume-down () (interactive) (pactl "set-sink-volume @DEFAULT_SINK@ -5%")) + +(defun brightness-up () + (interactive) + (shell-command tazjin--backlight-increase-command) + (message "Brightness increased")) + +(defun brightness-down () + (interactive) + (shell-command tazjin--backlight-decrease-command) + (message "Brightness decreased")) + +(defun set-xkb-layout (layout) + "Set the current X keyboard layout." + + (shell-command (format "setxkbmap %s" layout)) + (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. + + A lot of commonly used applications either create titles that + are too long by default, or in the case of web + applications (such as Cider) end up being constructed in + awkward ways. + + To avoid this issue, some rewrite rules are applied for more + human-accessible titles." + + (pcase (list (or exwm-class-name "unknown") (or exwm-title "unknown")) + ;; 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))) + + ;; For other Chromium windows, make the title shorter. + (`("Chromium-browser" ,title) + (format "Chromium<%s>" (s-truncate 42 (s-chop-suffix " - Chromium" title)))) + + ;; Quassel buffers + ;; + ;; These have a title format that looks like: + ;; "Quassel IRC - #tvl (hackint) — Quassel IRC" + (`("quassel" ,title) + (progn + (if (string-match + (rx "Quassel IRC - " + (group (one-or-more (any alnum "[" "]" "&" "-" "#"))) ;; <-- channel name + " (" (group (one-or-more (any ascii space))) ")" ;; <-- network name + " — Quassel IRC") + title) + (format "Quassel<%s>" (match-string 2 title)) + title))) + + ;; For any other application, a name is constructed from the + ;; window's class and name. + (`(,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))))) + (add-hook 'exwm-update-class-hook titlef) + (add-hook 'exwm-update-title-hook titlef)) + +(fringe-mode 3) +(exwm-enable) + +;; Create 10 EXWM workspaces +(setq exwm-workspace-number 10) + +;; 's-N': Switch to certain workspace, but switch back to the previous +;; one when tapping twice (emulates i3's `back_and_forth' feature) +(defvar *exwm-workspace-from-to* '(-1 . -1)) +(defun exwm-workspace-switch-back-and-forth (target-idx) + ;; If the current workspace is the one we last jumped to, and we are + ;; asked to jump to it again, set the target back to the previous + ;; one. + (when (and (eq exwm-workspace-current-index (cdr *exwm-workspace-from-to*)) + (eq target-idx exwm-workspace-current-index)) + (setq target-idx (car *exwm-workspace-from-to*))) + + (setq *exwm-workspace-from-to* + (cons exwm-workspace-current-index target-idx)) + + (exwm-workspace-switch-create target-idx)) + +(dotimes (i 10) + (exwm-input-set-key (kbd (format "s-%d" i)) + `(lambda () + (interactive) + (exwm-workspace-switch-back-and-forth ,i)))) + +;; Implement MRU functionality for EXWM workspaces, making it possible +;; to jump to the previous/next workspace very easily. +(defvar *recent-workspaces-ring* (make-ring 5) + "Ring of recently used EXWM workspaces.") + +(defvar *workspace-ring-is-rotating* nil + "Variable used to track whether the workspace ring is rotating, +and suppress insertions into the ring in that case.") + +(defun update-recent-workspaces () + "Hook run on EXWM workspace switches, adding new workspaces to the +ring." + + (unless *workspace-ring-is-rotating* + (ring-remove+insert+extend *recent-workspaces-ring* exwm-workspace-current-index))) + +(add-to-list 'exwm-workspace-switch-hook #'update-recent-workspaces) + +(defun switch-to-previous-workspace () + "Switch to the previous workspace in the workspace ring." + (interactive) + + (when-let ((*workspace-ring-is-rotating* t) + (previous (condition-case err (ring-next *recent-workspaces-ring* + exwm-workspace-current-index) + ('error (message "No previous workspace in history!") nil)))) + (exwm-workspace-switch previous))) + +(exwm-input-set-key (kbd "s-b") #'switch-to-previous-workspace) + +(defun switch-to-next-workspace () + "Switch to the next workspace in the MRU workspace list." + (interactive) + + (when-let ((*workspace-ring-is-rotating* t) + (next (condition-case err (ring-previous *recent-workspaces-ring* + exwm-workspace-current-index) + ('error (message "No next workspace in history!") nil)))) + (exwm-workspace-switch next))) + +(exwm-input-set-key (kbd "s-f") #'switch-to-next-workspace) + +;; Provide a binding for jumping to a buffer on a workspace. +(defun exwm-jump-to-buffer () + "Jump to a workspace on which the target buffer is displayed." + (interactive) + (let ((exwm-layout-show-all-buffers nil) + (initial exwm-workspace-current-index)) + (call-interactively #'exwm-workspace-switch-to-buffer) + ;; After jumping, update the back-and-forth list like on a direct + ;; index jump. + (when (not (eq initial exwm-workspace-current-index)) + (setq *exwm-workspace-from-to* + (cons initial exwm-workspace-current-index))))) + +(exwm-input-set-key (kbd "C-c j") #'exwm-jump-to-buffer) + +;; 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 X11 terminal selector to a key +(exwm-input-set-key (kbd "C-x t") #'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) + +;; Volume keys +(exwm-input-set-key (kbd "<XF86AudioMute>") #'volume-mute) +(exwm-input-set-key (kbd "<XF86AudioRaiseVolume>") #'volume-up) +(exwm-input-set-key (kbd "<XF86AudioLowerVolume>") #'volume-down) + +;; Brightness keys +(exwm-input-set-key (kbd "<XF86MonBrightnessDown>") #'brightness-down) +(exwm-input-set-key (kbd "<XF86MonBrightnessUp>") #'brightness-up) +(exwm-input-set-key (kbd "<XF86Display>") #'lock-screen) + +;; Shortcuts for switching between keyboard layouts +(defmacro bind-xkb (lang key) + `(exwm-input-set-key (kbd (format "s-%s" ,key)) + (lambda () + (interactive) + (set-xkb-layout ,lang)))) + +(bind-xkb "us" "k u") +(bind-xkb "de" "k d") +(bind-xkb "no" "k n") +(bind-xkb "ru" "k r") +(bind-xkb "se" "k s") + +;; 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))) + +;; Show time & battery status in the mode line +(display-time-mode) +(display-battery-mode) + +;; enable display of X11 system tray within Emacs +(exwm-systemtray-enable) + +;; Configure xrandr (multi-monitor setup). + +(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 Tverskoy (X13 AMD laptop) +(defun randr-tverskoy-layout-single () + "Laptop screen only!" + (interactive) + (set-randr-config '(("eDP" (number-sequence 0 9)))) + (shell-command "xrandr --output eDP --auto --primary") + (shell-command "xrandr --output HDMI-A-0 --off") + (exwm-randr-refresh)) + +(defun randr-tverskoy-split-workspace () + "Split the workspace across two screens, assuming external to the left." + (interactive) + (set-randr-config + '(("HDMI-A-0" 1 2 3 4 5 6 7 8) + ("eDP" 9 0))) + + (shell-command "xrandr --output HDMI-A-0 --left-of eDP --auto") + (exwm-randr-refresh)) + +(defun randr-tverskoy-tv () + "Split off a workspace to the TV over HDMI." + (interactive) + (set-randr-config + '(("eDP" 1 2 3 4 5 6 7 8 9) + ("HDMI-A-0" 0))) + + (shell-command "xrandr --output HDMI-A-0 --left-of eDP --mode 1920x1080") + (exwm-randr-refresh)) + +;; Layouts for frog (desktop) + +(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")) + +(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 "xrandr --output DisplayPort-0 --auto --primary --left-of DisplayPort-1") + (shell-command "xrandr --output DisplayPort-1 --auto --right-of DisplayPort-0 --rotate left")) + +(defun randr-khamovnik-layout-office () + "Use the left and right screen on khamovnik, in the office." + (interactive) + (set-randr-config `(("eDP-1" 1 2) + ("DP-2" 3 4 5 6 7 8 9 0))) + + (shell-command "xrandr --output DP-2 --mode 2560x1440 --primary --right-of eDP-1") + (exwm-randr-refresh)) + +(defun randr-khamovnik-layout-home () + "Use the left and right screen on khamovnik, at home." + (interactive) + (set-randr-config `(("HDMI-1" 1 2 3 4 5 6 7 8) + ("eDP-1" 9 0))) + + (shell-command "xrandr --output HDMI-1 --auto --primary --left-of eDP-1") + (exwm-randr-refresh)) + +(defun randr-khamovnik-layout-single () + "Use only the internal screen." + (interactive) + (set-randr-config '(("eDP-1" (number-sequence 0 9)))) + (shell-command "xrandr --output eDP-1 --auto --primary") + (shell-command "xrandr --output DP-2 --off") + (shell-command "xrandr --output HDMI-1 --off") + (exwm-randr-refresh)) + +(pcase (s-trim (shell-command-to-string "hostname")) + ("tverskoy" + (exwm-input-set-key (kbd "s-m s") #'randr-tverskoy-layout-single) + (exwm-input-set-key (kbd "s-m 2") #'randr-tverskoy-split-workspace)) + + ("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)) + + ("khamovnik" + (exwm-input-set-key (kbd "s-m 2") #'randr-khamovnik-layout-office) + (exwm-input-set-key (kbd "s-m s") #'randr-khamovnik-layout-single))) + +;; Notmuch shortcuts as EXWM globals +;; (g m => gmail) +(exwm-input-set-key (kbd "s-g m") #'notmuch) + +(exwm-randr-enable) + +;; Let buffers move seamlessly between workspaces by making them +;; accessible in selectors on all frames. +(setq exwm-workspace-show-all-buffers t) +(setq exwm-layout-show-all-buffers t) + +(provide 'desktop) |