Last active
March 3, 2018 19:02
-
-
Save vbuaraujo/470c9c88764ef13f66f561601f3578fb to your computer and use it in GitHub Desktop.
My EXWM init file
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;;; init-exwm.el -*- lexical-binding: t; -*- | |
(require 'exwm) | |
;; (require 'exwm-config) | |
;; Commented out because I'm using stalonetray instead of the EXWM tray. | |
;; (require 'exwm-systemtray) | |
;; (exwm-systemtray-enable) | |
;; (exwm-config-default) | |
(require 'exwm-randr) | |
;;;; Workspaces. | |
(setq exwm-workspace-number 10) | |
(setq exwm-workspace-show-all-buffers t) | |
(when (equal (system-name) "pythagoras") | |
;; Give workspaces 1~5 to my left monitor, and leave 6~0 to my right one. | |
(setq exwm-randr-workspace-output-plist | |
'(1 "VGA-1" | |
2 "VGA-1" | |
3 "VGA-1" | |
4 "VGA-1" | |
5 "VGA-1")) | |
(exwm-randr-enable)) | |
;; Update buffer name based on class and title. | |
(defvar elmord/exwm-buffer-name-limit 50) | |
(defun elmord/exwm-compute-buffer-name () | |
(let ((class (or exwm-class-name "")) | |
(title (or exwm-title ""))) | |
(cond | |
((and (member class '("Firefox" "Firefox-esr" "Icedove")) | |
(string-match "\\`\\(.*\\)\\( - [^-]*\\)\\'" title)) | |
(concat class ": " (match-string 1 title))) | |
((member class '("Telegram" "TelegramDesktop")) | |
(if (equal title "") "Telegram" title)) | |
((equal class "skypeforlinux") "Skype") | |
(t (concat class ": " title))))) | |
(defun elmord/exwm-update-buffer-name () | |
(exwm-workspace-rename-buffer | |
(truncate-string-to-width | |
(elmord/exwm-compute-buffer-name) | |
elmord/exwm-buffer-name-limit | |
nil ; start | |
nil ; padding | |
"…") | |
)) | |
(add-hook 'exwm-update-class-hook 'elmord/exwm-update-buffer-name) | |
(add-hook 'exwm-update-title-hook 'elmord/exwm-update-buffer-name) | |
;; Pass on C-u intact to applications. | |
(setq exwm-input-prefix-keys | |
(delq ?\C-u exwm-input-prefix-keys)) | |
;;;; Global bindings. | |
(exwm-input-set-key (kbd "s-r") 'rename-buffer) | |
(exwm-input-set-key (kbd "s-R") 'exwm-reset) | |
(exwm-input-set-key (kbd "s-w") 'exwm-workspace-switch) | |
(exwm-input-set-key (kbd "s-o") 'other-window) | |
(exwm-input-set-key (kbd "s-m") 'exwm-workspace-move-window) | |
;; Switch workspace with Super+number | |
(dotimes (i 10) | |
(exwm-input-set-key (kbd (format "s-%d" i)) | |
`(lambda () | |
(interactive) | |
(exwm-workspace-switch-create ,i)))) | |
;; 's-&': Launch application | |
(exwm-input-set-key (kbd "s-&") | |
(defun elmord/exwm-shell-command (command) | |
(interactive (list (read-shell-command "$ "))) | |
(start-process "exwm-subproc" nil "nohup" "sh" "-c" command))) | |
;;;; EXWM line mode bindings. | |
;; C-x C-x to send C-x, C-c C-c to send C-c. | |
(define-key exwm-mode-map (kbd "C-x C-x") | |
(lambda () (interactive) (exwm-input--fake-key ?\C-x))) | |
(define-key exwm-mode-map (kbd "C-c C-c") | |
(lambda () (interactive) (exwm-input--fake-key ?\C-c))) | |
;; Line-editing shortcuts | |
;; (exwm-input-set-simulation-keys | |
;; '(([?\C-b] . left) | |
;; ([?\C-f] . right) | |
;; ([?\C-p] . up) | |
;; ([?\C-n] . down) | |
;; ([?\C-a] . home) | |
;; ([?\C-e] . end) | |
;; ([?\M-v] . prior) | |
;; ([?\C-v] . next) | |
;; ([?\C-d] . delete) | |
;; ([?\C-k] . (S-end delete)))) | |
(defun elmord/exwm-next-workspace (&optional incr) | |
(interactive "p") | |
(when (not incr) (setq incr 1)) | |
(let* ((count (exwm-workspace--count)) | |
(index (mod (+ exwm-workspace-current-index incr) count))) | |
(exwm-workspace-switch index))) | |
(defun elmord/exwm-previous-workspace (&optional incr) | |
(interactive "p") | |
(when (not incr) (setq incr 1)) | |
(elmord/exwm-next-workspace (- incr))) | |
(exwm-input-set-key (kbd "s-<left>") 'elmord/exwm-previous-workspace) | |
(exwm-input-set-key (kbd "s-<right>") 'elmord/exwm-next-workspace) | |
(defun elmord/exwm-buffer-list (&optional predicate) | |
(cl-remove-if-not | |
(lambda (buffer) | |
(and (eq (buffer-local-value 'major-mode buffer) 'exwm-mode) | |
(if predicate (funcall predicate buffer) t))) | |
(buffer-list))) | |
(defun elmord/ido-read-exwm-buffer (prompt) | |
"Read an EXWM buffer name from the user." | |
(let ((buffers (elmord/exwm-buffer-list))) | |
(ido-completing-read prompt (mapcar 'buffer-name buffers)))) | |
(defun elmord/ido-read-buffer-maybe-exwm (pre-prompt only-exwm) | |
"Read a buffer name from the user. if ONLY-EXWM is non-nil, | |
only present EXWM buffers as options." | |
(let ((prompt (format "%s%s: " pre-prompt (if only-exwm " (EXWM)" ""))) | |
(prompter (if only-exwm 'elmord/ido-read-exwm-buffer 'ido-read-buffer))) | |
(funcall prompter prompt))) | |
(defun elmord/exwm-ido-jump-to-buffer (only-exwm) | |
"Jump to the workspace containing an EXWM buffer." | |
(interactive "P") | |
(let ((bufname (elmord/ido-read-buffer-maybe-exwm "Jump to buffer" only-exwm))) | |
(exwm-workspace-switch-to-buffer bufname))) | |
(defun elmord/exwm-ido-bring-buffer (only-exwm) | |
"Bring an EXWM buffer to the current workspace." | |
(interactive "P") | |
(let* ((bufname (elmord/ido-read-buffer-maybe-exwm "Bring buffer" only-exwm)) | |
(id (exwm--buffer->id (get-buffer bufname)))) | |
(when id | |
;; EXWM buffer: move to current workspace. | |
(exwm-workspace-move-window exwm-workspace-current-index id)) | |
;; Whether EXWM buffer or not, bring it up. | |
(switch-to-buffer bufname))) | |
(defun elmord/exwm-ido-jump-to-exwm-buffer () | |
(interactive) | |
(elmord/exwm-ido-jump-to-buffer t)) | |
(defun elmord/exwm-ido-bring-exwm-buffer () | |
(interactive) | |
(elmord/exwm-ido-bring-buffer t)) | |
(exwm-input-set-key (kbd "s-j") 'elmord/exwm-ido-jump-to-buffer) | |
(exwm-input-set-key (kbd "s-J") 'elmord/exwm-ido-jump-to-exwm-buffer) | |
(exwm-input-set-key (kbd "s-b") 'elmord/exwm-ido-bring-buffer) | |
(exwm-input-set-key (kbd "s-B") 'elmord/exwm-ido-bring-exwm-buffer) | |
(display-time-mode 1) | |
;; Shortcuts. | |
(defun elmord/exwm-shortcut (key command) | |
(exwm-input-set-key key | |
`(lambda () | |
(interactive) | |
(start-process "exwm-shortcut" nil "sh" "-c" ,command)))) | |
(elmord/exwm-shortcut (kbd "s--") "amixer set Master 3%-") | |
(elmord/exwm-shortcut (kbd "s-=") "amixer set Master 3%+") | |
(elmord/exwm-shortcut (kbd "s-\\") "musicctl play") | |
(elmord/exwm-shortcut (kbd "s-(") "musicctl prev") | |
(elmord/exwm-shortcut (kbd "s-)") "musicctl next") | |
(elmord/exwm-shortcut (kbd "s-x") "sleep 0.2; xset dpms force suspend") | |
(elmord/exwm-shortcut (kbd "s-<prior>") "backlight-brightness +") | |
(elmord/exwm-shortcut (kbd "s-<next>") "backlight-brightness -") | |
(elmord/exwm-shortcut (kbd "s-t") "xterm") | |
(elmord/exwm-shortcut (kbd "s-L") "gnome-screensaver-command -l") | |
;;;; Start pidgin in background | |
(fset '~exwm-floating--unset-floating (symbol-function 'exwm-floating--unset-floating)) | |
(fset '~exwm-layout--iconic-state-p (symbol-function 'exwm-layout--iconic-state-p)) | |
;; This is a terrible kludge to make EXWM think the window is | |
;; iconified when it is created. | |
(defadvice exwm-manage--manage-window (around elmord/start-in-background activate) | |
(cl-letf (((symbol-function 'exwm-floating--unset-floating) | |
(lambda (id) | |
(cl-letf (((symbol-function 'exwm-layout--iconic-state-p) | |
(lambda (&optional id) | |
(or (~exwm-layout--iconic-state-p id) | |
(elmord/exwm-start-in-background-p id))))) | |
(~exwm-floating--unset-floating id))))) | |
ad-do-it | |
(exwm-layout--refresh))) | |
(defun elmord/exwm-start-in-background-p (id) | |
;; TODO: Do whatever other window managers do to figure out that a | |
;; window should be started in background, rather than tying it to Pidgin. | |
(with-current-buffer (if id (exwm--id->buffer id) (current-buffer)) | |
(equal exwm-class-name "Pidgin"))) | |
;;;; Urgency handling. | |
;; Telegram notifications hack. | |
;; (defun elmord/exwm-telegram-title-hook () | |
;; ;; (when (equal exwm-class-name "TelegramDesktop") | |
;; (run-hooks 'elmord/exwm-update-hints-hook))) | |
(defun elmord/exwm-telegram-urgent-p (buffer) | |
(and (equal (buffer-local-value 'exwm-class-name buffer) "TelegramDesktop") | |
(string-match "([0-9]+)" (or (buffer-local-value 'exwm-title buffer) "")))) | |
(defun elmord/exwm-evolution-urgent-p (buffer) | |
(and (equal (buffer-local-value 'exwm-class-name buffer) "Evolution") | |
(string-match "([0-9]+ unread)" (or (buffer-local-value 'exwm-title buffer) "")))) | |
(defun elmord/exwm-buffer-urgent-p (buffer) | |
(or | |
(buffer-local-value 'exwm--hints-urgency buffer) | |
;;(elmord/exwm-telegram-urgent-p buffer) | |
(elmord/exwm-evolution-urgent-p buffer) | |
)) | |
(defun elmord/exwm-urgent-buffer-list () | |
(elmord/exwm-buffer-list 'elmord/exwm-buffer-urgent-p)) | |
(defun elmord/exwm-jump-to-urgent-buffer () | |
"Jump to the first urgent window." | |
(interactive) | |
(let ((urgent-buffers (elmord/exwm-urgent-buffer-list))) | |
(if (not urgent-buffers) | |
(message "No urgent buffers") | |
(let ((buffer (car urgent-buffers))) | |
(exwm-workspace-switch-to-buffer buffer) | |
(elmord/update-mode-line-exwm-urgency))))) | |
;; When visiting a window, remove its urgency. | |
(add-hook 'buffer-list-update-hook | |
(defun elmord/exwm-remove-urgency () | |
(when (and (eq major-mode 'exwm-mode) | |
(eq (current-buffer) (window-buffer)) | |
exwm--hints-urgency) | |
(setf exwm--hints-urgency nil) | |
(run-hooks 'elmord/exwm-update-hints-hook)))) | |
(exwm-input-set-key (kbd "s-u") 'elmord/exwm-jump-to-urgent-buffer) | |
;; A hook for tracking when windows become urgent. | |
(defvar elmord/exwm-update-hints-hook nil | |
"Normal hook run when window hints are updated.") | |
(defadvice exwm--update-hints (after elmord/update-hints-hook activate) | |
(run-hooks 'elmord/exwm-update-hints-hook)) | |
;; Indicate urgent windows in the mode line. | |
(defvar elmord/mode-line-exwm-urgency "") | |
(put 'elmord/mode-line-exwm-urgency 'risky-local-variable t) | |
(setq mode-line-misc-info | |
(append mode-line-misc-info (list 'elmord/mode-line-exwm-urgency))) | |
(defun elmord/update-mode-line-exwm-urgency () | |
(let ((text (string-join | |
(mapcar 'buffer-name (elmord/exwm-urgent-buffer-list)) | |
", "))) | |
(setq elmord/mode-line-exwm-urgency | |
`(:propertize ,text | |
face | |
(:foreground "white" | |
:background "#0000a0"))) | |
(force-mode-line-update t))) | |
(add-hook 'elmord/exwm-update-hints-hook 'elmord/update-mode-line-exwm-urgency) | |
(add-hook 'exwm-update-title-hook 'elmord/update-mode-line-exwm-urgency) | |
;; Make C-x 8 RET work with any program supporting paste from clipboard with C-v. | |
(defun elmord/exwm-get-paste-key () | |
(cond ((member exwm-class-name '("XTerm")) | |
(aref (kbd "S-C-v") 0)) | |
(t ?\C-v))) | |
(defun elmord/exwm-send-string (string) | |
(let ((saved-clipboard (gui-backend-get-selection 'CLIPBOARD 'STRING)) | |
(paste-key (elmord/exwm-get-paste-key))) | |
(gui-backend-set-selection 'CLIPBOARD string) | |
(exwm-input--fake-key paste-key) | |
(run-at-time 0.5 nil | |
`(lambda () | |
(gui-backend-set-selection 'CLIPBOARD ,saved-clipboard)))) | |
t) | |
;; (defun elmord/exwm-send-character (char) | |
;; (interactive | |
;; ;; Copied from `insert-char' source. | |
;; (list (read-char-by-name "Insert character (Unicode name or hex): "))) | |
;; (elmord/exwm-send-string (string char))) | |
;; (define-key exwm-mode-map (kbd "C-x 8 RET") 'elmord/exwm-send-character) | |
;; Much better (but with possible unknown side-effects?): allow the | |
;; buffer to be modified, and transfer any modification to the | |
;; application via clipboard. | |
(defun elmord/exwm-after-change-function (begin end length) | |
(elmord/exwm-send-string (buffer-substring begin end)) | |
(delete-region begin end) | |
(set-buffer-modified-p nil)) | |
(add-hook 'exwm-manage-finish-hook | |
(defun elmord/exwm-manage-finish-hook () | |
(setq buffer-read-only nil) | |
(add-hook 'after-change-functions | |
'elmord/exwm-after-change-function | |
nil ;; append hook? | |
t ;; buffer-local? | |
))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment