Skip to content

Commit

Permalink
Add Emacs Window Manager (EXWM) Layer
Browse files Browse the repository at this point in the history
The time has come of SpacemacsOS
  • Loading branch information
CestDiego committed Oct 14, 2015
1 parent c96183f commit f8d4ca6
Show file tree
Hide file tree
Showing 6 changed files with 392 additions and 0 deletions.
93 changes: 93 additions & 0 deletions layers/+window-management/exwm/README.org
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
* EXWM contribution layer for Spacemacs

** Table of Contents :TOC@4:
- [[#exwm-contribution-layer-for-spacemacs][EXWM contribution layer for Spacemacs]]
- [[#description][Description]]
- [[#install][Install]]
- [[#note-about-display-managers][Note about Display Managers]]
- [[#not-having-display-managers][Not having Display Managers]]
- [[#osx][OSX]]
- [[#key-bindings][Key bindings]]

** Description
The time has arrived when one can finally use Emacs as a Window Manager, long
ago the concepts of *windows* and *frames* made so much sense when one was
working in a TTY, and basically that's all the window management you got. In
these *modern* times though, it sounds silly to have those names. But not
Anymore!

Thanks to @ch11ng and his [[https://github.com/ch11ng/exwm][EXWM]] project we can now use Emacs as our window
manager, and all those *windows* that didn't make sense before, now can not only
hold buffers, but X Windows, that means that you can spawn a Browser Window, or
your music player, or anything.

I urge you to read the [[https://github.com/ch11ng/exwm/wiki][EXWM Wiki]] for a more in depth explanation and if you can
contribute, please do so! The purpose of me making this layer is that I find it
awesome and having nice defaults would make more people dive into it and the
project would receive more attention and contributions which will only make it
more awesome, so if you try it and like it, share it!

This is how it looks like:

[[img/spacemacsOS.jpg]]

** Install
To use this contribution add it to your =~/.spacemacs=

#+begin_src emacs-lisp
(setq-default dotspacemacs-configuration-layers '(exwm))
#+end_src

*** Note about Display Managers

This is most common among Ubuntu and derivative users:

LightDM, GDM, and other Display managers that need a session file will need you
to copy the [[file:files/exwm.desktop][desktop file]] I bundled with this layer to
~/usr/local/xsessions/exwm.desktop~, that's what I used on my Ubuntu box, but
make sure the [[file:files/exwm-start][exwm-start script]] is in your PATH, you can put it in
~/usr/local/bin/exwm-start~ and that should be enough, next time you log out,
select the EXWM session instead of the Ubuntu one and you should be alright.

*** Not having Display Managers

If you use Arch, Gentoo, or any other Linux, most probably you'll start your
configurations via a ~.xinitrc~ file, just append ~exwm-start~ to the end of
that file and you should be fine. I do recommend to check the [[https://github.com/ch11ng/exwm/wiki][EXWM Wiki]] for more
details.

*** OSX

If you are an OSX user, please report back on whether this works with xQuartz,
always back up your data before attempting to try stuff like this.

** Key bindings

As other window managers the ~s~ or *Super* key (Windows Key) is the one that
is the prefix to every action. We'll use a lot of ~s~.

I have, for convenience, disabled the use of ~C-x~ and ~C-c~ in X windows,
mainly because that's something we could use for Cutting and Copying text, but if
you want you can enable it, this layer focuses on less Emacs-y bindings.

| Key Binding | Description |
|-----------------------+------------------------------------------------|
| ~C-q~ | Send next key pressed to the X window |
| ~C-'~ | Pop shell |
| ~C-g~ | Universal GetMeOuttaHere Key from Emacs |
| ~C-u~ | Universal Argument |
| ~C-[0-9]~ | Universal Prefix for [0-9] |
| ~M-m~ | Spacemacs Leader Key |
| ~s-[1 2 3 ... 8 9 0]~ | Switch to workspace [1 2 3 ... 8 9 10] |
| ~s-TAB~ | Switch to last workspace |
| ~s-w~ | Workspace Switch Menu (kinda like micro-state) |
| ~s-r~ | Reset window state |
| ~s-SPC~ | App Launcher |
| ~s-ESC~ | Lock Screen |
| ~s-:~ and ~s-;~ | Helm M-x (same as ~SCP :~) |
| ~s-u,U~ | Undo, Redo window configurations |
| ~s-b~ | Show all opened buffers |
| ~s-h,j,k,l~ | Switch to left,lower,upper,right window |
| ~s-H,J,K,L~ | Move window to far left,down,lower,upper,right |
| ~M-s-h,j,k,l~ | Resizing (try them, it's too hard to explain) |
| ~s-[,]~ | Switch to prev,next workspace |
11 changes: 11 additions & 0 deletions layers/+window-management/exwm/config.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
(defvar exwm--terminal-command "xterm"
"Terminal command to run.")

(defvar exwm--locking-command "lock"
"Command to run when locking session")

(defvar exwm-app-launcher--prompt "$ "
"Prompt for the EXWM application launcher")

(defvar exwm--hide-tiling-modeline nil
"Whether to hide modeline.")
23 changes: 23 additions & 0 deletions layers/+window-management/exwm/files/exwm-start
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/sh

# this makes it work in Ubuntu
xhost +
## you might need to append the TTY you are working on
xinit

wmname LG3D

# Remap caps lock to left control. This is not strictly speaking
# exwm related, but it's handy
setxkbmap -option 'ctrl:no caps'

# Set fallback cursor
xsetroot -cursor_name left_ptr

# If Emacs is started in server mode, `emacsclient` is a convenient way to edit
# files in place (used by e.g. `git commit`)
export VISUAL=emacsclient
export EDITOR="$VISUAL"

# Finally launch emacs and enable exwm
exec dbus-launch --exit-with-session emacs --eval "(exwm-enable)"
5 changes: 5 additions & 0 deletions layers/+window-management/exwm/files/exwm.desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[Desktop Entry]
Name=EXWM
Comment=Emacs Window Manager
Exec=exec exwm-start
Type=Application
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
260 changes: 260 additions & 0 deletions layers/+window-management/exwm/packages.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
;;; packages.el --- exwm Layer packages File for Spacemacs
;;
;; Copyright (c) 2012-2014 Sylvain Benner
;; Copyright (c) 2014-2015 Sylvain Benner & Contributors
;;
;; Author: Sylvain Benner <sylvain.benner@gmail.com>
;; URL: https://github.com/syl20bnr/spacemacs
;;
;; This file is not part of GNU Emacs.
;;
;;; License: GPLv3

;; List of all packages to install and/or initialize. Built-in packages
;; which require an initialization must be listed explicitly in the list.
(setq exwm-packages
'(cl-generic
(xelb :location (recipe :fetcher github
:repo "ch11ng/xelb")
:step pre)
(exwm :location (recipe :fetcher github
:repo "ch11ng/exwm")
:step pre)))

(defun exwm/init-cl-generic ()
(use-package cl-generic
:demand))

(defun exwm/init-xelb ()
(use-package xelb))

(defun exwm/init-exwm ()
(use-package exwm
:init
;; Disable dialog boxes since they are unusable in EXWM
(setq use-dialog-box nil)
;; 10 Worskpaces please
(setq exwm-workspace-number 10)
;; You may want Emacs to show you the time
(display-time-mode t)
(when exwm--hide-tiling-modeline
(add-hook 'exwm-mode-hook #'hidden-mode-line-mode))
;; Trying to make shell-pop with a real terminal :P
;; (defun exwm-launch-term ()
;; (start-process-shell-command exwm--terminal-command
;; nil exwm--terminal-command))
;; (defun shell-pop-exwm-term (index)
;; (interactive "P")
;; (require 'shell-pop)
;; (shell-pop--set-shell-type
;; 'shell-pop-shell-type
;; '("exwm-term"
;; "Termite" #'exwm-launch-term))
;; (shell-pop index))
:config
(when dotspacemacs-use-ido
(exwm-enable-ido-workaround))
(defun spacemacs/exwm-bind-command (key command &rest bindings)
(while key
(exwm-input-set-key (kbd key)
`(lambda ()
(interactive)
(start-process-shell-command ,command nil ,command)))
(setq key (pop bindings)
command (pop bindings))))

(spacemacs/exwm-bind-command
"<s-return>" exwm--terminal-command)

;; All buffers created in EXWM mode are named "*EXWM*". You may want to change
;; it in `exwm-update-class-hook' and `exwm-update-title-hook', which are run
;; when a new window class name or title is available. Here's some advice on
;; this subject:
;; + Always use `exwm-workspace-rename-buffer` to avoid naming conflict.
;; + Only renaming buffer in one hook and avoid it in the other. There's no
;; guarantee on the order in which they are run.
;; + For applications with multiple windows (e.g. GIMP), the class names of all
;; windows are probably the same. Using window titles for them makes more
;; sense.
;; + Some application change its title frequently (e.g. browser, terminal).
;; Its class name may be more suitable for such case.
;; In the following example, we use class names for all windows expect for
;; Java applications and GIMP.
(add-hook 'exwm-update-class-hook
(lambda ()
(unless (or (string-prefix-p "sun-awt-X11-" exwm-instance-name)
(string= "gimp" exwm-instance-name))
(exwm-workspace-rename-buffer exwm-class-name))))
(add-hook 'exwm-update-title-hook
(lambda ()
(when (or (not exwm-instance-name)
(string-prefix-p "sun-awt-X11-" exwm-instance-name)
(string= "gimp" exwm-instance-name))
(exwm-workspace-rename-buffer exwm-title))))

(defvar exwm-workspace-switch-wrap t
"Whether `spacemacs/exwm-workspace-next' and `spacemacs/exwm-workspace-prev' should wrap.")

(defun spacemacs/exwm-workspace-next ()
"Switch to next exwm-workspaceective (to the right)."
(interactive)
(let* ((only-workspace? (equal exwm-workspace-number 1))
(overflow? (= exwm-workspace-current-index
(1- exwm-workspace-number))))
(cond
(only-workspace? nil)
(overflow?
(when exwm-workspace-switch-wrap
(exwm-workspace-switch 0)))
(t (exwm-workspace-switch (1+ exwm-workspace-current-index))))))
(defun spacemacs/exwm-workspace-prev ()
"Switch to next exwm-workspaceective (to the right)."
(interactive)
(let* ((only-workspace? (equal exwm-workspace-number 1))
(overflow? (= exwm-workspace-current-index 0)))
(cond
(only-workspace? nil)
(overflow?
(when exwm-workspace-switch-wrap
(exwm-workspace-switch (1- exwm-workspace-number))))
(t (exwm-workspace-switch (1- exwm-workspace-current-index))))))
(defun spacemacs/exwm-layout-toggle-fullscreen ()
"Togggles full screen for Emacs and X windows"
(interactive)
(if exwm--id
(if exwm--fullscreen
(exwm-reset)
(exwm-layout-set-fullscreen))
(spacemacs/toggle-maximize-buffer)))

;; Quick swtiching between workspaces
(defvar exwm-toggle-workspace 0
"Previously selected workspace. Used with `exwm-jump-to-last-exwm'.")
(defun exwm-jump-to-last-exwm ()
(interactive)
(exwm-workspace-switch exwm-toggle-workspace))
(defadvice exwm-workspace-switch (before save-toggle-workspace activate)
(setq exwm-toggle-workspace exwm-workspace-current-index))

(defun spacemacs/exwm-app-launcher (command)
"Launches an application in your PATH.
Can show completions at point for COMMAND using helm or ido"
(interactive (list (read-shell-command exwm-app-launcher--prompt)))
(start-process-shell-command command nil command))

;; `exwm-input-set-key' allows you to set a global key binding (available in
;; any case). Following are a few examples.
;; + We always need a way to go back to line-mode from char-mode
(exwm-input-set-key (kbd "s-r") 'exwm-reset)

(exwm-input-set-key (kbd "s-f") #'spacemacs/exwm-layout-toggle-fullscreen)
(exwm-input-set-key (kbd "<s-tab>") #'exwm-jump-to-last-exwm)
;; + Bind a key to switch workspace interactively
(exwm-input-set-key (kbd "s-w") 'exwm-workspace-switch)
;; + Set shortcuts to switch to a certain workspace.
(exwm-input-set-key (kbd "s-1")
(lambda () (interactive) (exwm-workspace-switch 0)))
(exwm-input-set-key (kbd "s-2")
(lambda () (interactive) (exwm-workspace-switch 1)))
(exwm-input-set-key (kbd "s-3")
(lambda () (interactive) (exwm-workspace-switch 2)))
(exwm-input-set-key (kbd "s-4")
(lambda () (interactive) (exwm-workspace-switch 3)))
(exwm-input-set-key (kbd "s-5")
(lambda () (interactive) (exwm-workspace-switch 4)))
(exwm-input-set-key (kbd "s-6")
(lambda () (interactive) (exwm-workspace-switch 5)))
(exwm-input-set-key (kbd "s-7")
(lambda () (interactive) (exwm-workspace-switch 6)))
(exwm-input-set-key (kbd "s-8")
(lambda () (interactive) (exwm-workspace-switch 7)))
(exwm-input-set-key (kbd "s-9")
(lambda () (interactive) (exwm-workspace-switch 8)))
(exwm-input-set-key (kbd "s-0")
(lambda () (interactive) (exwm-workspace-switch 9)))
;; + Application launcher ('M-&' also works if the output buffer does not
;; bother you). Note that there is no need for processes to be created by
;; Emacs.
(exwm-input-set-key (kbd "s-SPC") #'spacemacs/exwm-app-launcher)
;; + 'slock' is a simple X display locker provided by suckless tools. 'i3lock'
;; is a more feature-rich alternative.
(exwm-input-set-key (kbd "<s-escape>")
(lambda () (interactive) (start-process "" nil exwm--locking-command)))
;; The following example demonstrates how to set a key binding only available
;; in line mode. It's simply done by first push the prefix key to
;; `exwm-input-prefix-keys' and then add the key sequence to `exwm-mode-map'.
;; The example shorten 'C-c q' to 'C-q'.
(push ?\C-q exwm-input-prefix-keys)
(define-key exwm-mode-map [?\C-q] 'exwm-input-send-next-key)

;; M-m leader, sorry Space Folks
(push ?\M-m exwm-input-prefix-keys)
;; Universal Get-me-outta-here
(push ?\C-g exwm-input-prefix-keys)
;; Universal Arguments
(push ?\C-u exwm-input-prefix-keys)
(push ?\C-0 exwm-input-prefix-keys)
(push ?\C-1 exwm-input-prefix-keys)
(push ?\C-2 exwm-input-prefix-keys)
(push ?\C-3 exwm-input-prefix-keys)
(push ?\C-4 exwm-input-prefix-keys)
(push ?\C-5 exwm-input-prefix-keys)
(push ?\C-6 exwm-input-prefix-keys)
(push ?\C-7 exwm-input-prefix-keys)
(push ?\C-8 exwm-input-prefix-keys)
(push ?\C-9 exwm-input-prefix-keys)
;; C-c, C-x are needed for copying and pasting
(delete ?\C-x exwm-input-prefix-keys)
(delete ?\C-c exwm-input-prefix-keys)
;; We can use `M-m h' to access help
(delete ?\C-h exwm-input-prefix-keys)

;; Preserve the habit
(exwm-input-set-key (kbd "s-:") 'helm-M-x)
(exwm-input-set-key (kbd "s-;") 'evil-ex)
;; Shell (not a real one for the moment)
(exwm-input-set-key (kbd "C-'") #'spacemacs/default-pop-shell)
;; Undo window configurations
(exwm-input-set-key (kbd "s-u") #'winner-undo)
(exwm-input-set-key (kbd "S-s-U") #'winner-redo)
;; Change buffers
(exwm-input-set-key (kbd "s-b") #'helm-mini)
;; Focusing windows
(exwm-input-set-key (kbd "s-h") #'evil-window-left)
(exwm-input-set-key (kbd "s-j") #'evil-window-down)
(exwm-input-set-key (kbd "s-k") #'evil-window-up)
(exwm-input-set-key (kbd "s-l") #'evil-window-right)
;; Moving Windows
(exwm-input-set-key (kbd "s-H") #'evil-window-move-far-left)
(exwm-input-set-key (kbd "s-J") #'evil-window-move-very-bottom)
(exwm-input-set-key (kbd "s-K") #'evil-window-move-very-top)
(exwm-input-set-key (kbd "s-L") #'evil-window-move-far-right)
;; Resize
(exwm-input-set-key (kbd "M-s-h") #'spacemacs/shrink-window-horizontally)
(exwm-input-set-key (kbd "M-s-j") #'spacemacs/shrink-window)
(exwm-input-set-key (kbd "M-s-k") #'spacemacs/enlarge-window)
(exwm-input-set-key (kbd "M-s-l") #'spacemacs/enlarge-window-horizontally)
;; Workspaces
(exwm-input-set-key (kbd "s-]") #'spacemacs/exwm-workspace-next)
(exwm-input-set-key (kbd "s-[") #'spacemacs/exwm-workspace-prev)

(require 'exwm-randr)
(setq exwm-randr-workspace-output-plist '(0 "VGA1"))
(exwm-randr-enable)
;; The following example demonstrates how to use simulation keys to mimic the
;; behavior of Emacs. The argument to `exwm-input-set-simulation-keys' is a
;; list of cons cells (SRC . DEST), where SRC is the key sequence you press and
;; DEST is what EXWM actually sends to application. Note that SRC must be a key
;; sequence (of type vector or string), while DEST can also be a single key.

;; (exwm-input-set-simulation-keys
;; '(([?\C-b] . left)
;; ([?\C-f] . right)
;; ([?\C-p] . up)
;; ([?\C-n] . down)
;; ([?\M-v] . prior)
;; ))

;; Do not forget to enable EXWM. It will start by itself when things are ready.
;; (exwm-enable)
))

0 comments on commit f8d4ca6

Please sign in to comment.