#+TITLE: Desktop #+AUTHOR: Christopher James Hayward #+EMAIL: chris@chrishayward.xyz #+PROPERTY: header-args:emacs-lisp :tangle desktop.el :comments org #+PROPERTY: header-args :results silent :eval no-export :comments org #+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil #+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil I use Emacs as a Desktop Environment with the *EXWM*[fn:1] package. It allows Emacs to function as a complete tiling window manager for *X11*[fn:2]. * Profile :PROPERTIES: :header-args: :tangle ../config/profile :comments org :END: Ensure that ~~/.local/bin~ is added to the =$PATH= variable. #+begin_src shell PATH=$PATH:~/.local/bin export PATH #+end_src When launching into a new session on ~TTY1~, if the display server is not running, run *StartX*[fn:3]. This will launch the window manager. #+begin_src shell if [ -z "${DISPLAY}" ] && [ "${XDG_VTNR}" -eq 1 ]; then exec startx fi #+end_src * Startup :PROPERTIES: :header-args: :tangle ../config/xinitrc :comments org :END: My workflow includes launching the window manager with *Xinit*[fn:3], without the use of a display manager, controlling *everything* within Emacs. #+begin_src conf exec dbus-launch --exit-with-session emacs -mm --debug-init #+end_src * Browser Write out the ~$BROWSER~ variable so other applications can pick up the custom browser. #+begin_src emacs-lisp (setenv "BROWSER" dotfiles/browser) #+end_src * Displays When the window manager first launches the ~init-hook~ executes, allowing us to define some custom logic. + Display time and date + Display battery info (if available) In my personal configuration, I do not want the battery or time displayed within Emacs when it's not running as desktop environment because that information is typically already available. #+begin_src emacs-lisp (defun dotfiles/init-hook () (exwm-workspace-switch-create 1) (setq display-time-and-date t) (display-battery-mode 1) (display-time-mode 1)) #+end_src Using =autorandr= with pre configured profiles, switching screens (AKA hot plugging) is also handled through a hook. #+begin_src emacs-lisp (defun dotfiles/update-display () "Update the displays by forcing a change through autorandr." (dotfiles/run-in-background "autorandr --change --force")) #+end_src * Methods Define a method to run an external process, allowing us to launch any application on a new process without interferring with Emacs. #+begin_src emacs-lisp (defun dotfiles/run (command) "Run an external process." (interactive (list (read-shell-command "λ "))) (start-process-shell-command command nil command)) #+end_src Apply methods to the current call process to avoid issues with hooks. #+begin_src emacs-lisp (defun dotfiles/run-in-background (command) (let ((command-parts (split-string command "[ ]+"))) (apply #'call-process `(,(car command-parts) nil 0 nil ,@(cdr command-parts))))) #+end_src Place keybindings for executing shell commands behind =SPC x=. + Run shell commands with =x= + Run async shell commands with =z= #+begin_src emacs-lisp (dotfiles/leader "x" '(:ignore t :which-key "Run") "xx" '(dotfiles/run :which-key "Run") "xz" '(async-shell-command :which-key "Async")) #+end_src * Initialization Connect our custom hooks and configure the input keys, a custom layer for key capture layers. + Enable =randr= support + Pass through to Emacs + =M-x= to Emacs + =C-g= to Emacs + =C-SPC= to Emacs + Bindings with =S= (Super / Win) + Reset =S-r= + Launch =S-&= + Workspace =S-[1..9]= #+begin_src emacs-lisp (use-package exwm :custom (exwm-workspace-show-all-buffers t) (exwm-input-prefix-keys '(?\M-x ?\C-c ?\C-g ?\C-\ )) (exwm-input-global-keys `(([?\s-r] . exwm-reset) ,@(mapcar (lambda (i) `(,(kbd (format "s-%d" i)) . (lambda () (interactive) (exwm-workspace-switch-create ,i)))) (number-sequence 1 9)))) :config (require 'exwm-randr) (exwm-randr-enable) (add-hook 'exwm-init-hook #'dotfiles/init-hook) (add-hook 'exwm-randr-screen-change-hook #'dotfiles/update-display) (dotfiles/update-display) (exwm-enable)) #+end_src * Resources [fn:1] https://github.com/ch11ng/exwm [fn:2] https://en.wikipedia.org/wiki/X_Window_System [fn:3] https://en.wikipedia.org/wiki/Xinit