Setup π | Advanced Usage π | Implementation π οΈ | Related Work π
Move seamlessly between Emacs windows and external windows managed by an external window manager / compositor like KDE's KWin,
- π switching to the Emacs or desktop window at left/right/above/below or
- π·οΈ switching to another application like firefox by name, as well as
- β§ reposition and resize desktop windows with keys from within emacs instead of with the mouse.
In your init.el (see example):
-
Ensure emacs server is running
(require 'server) (unless (server-running-p) (server-start))
Also make sure:
- your are running KDE/KWin under Wayland or X11.
emacsclientis installed
--- otherwise install via your Linux package manager,kdotoolis installed
--- can be installed viacargo install kdotool, if cargo and rust is installed; rust and cargo you can install via your Linux package manager.
-
For β§ arranging desktop windows from within emacs, add to your
init.el:(use-package dwin :straight (dwin :type git :repo "https://github.com/lsth/dwin.git") :config (dwin-setup))
Then with
M-x dwin-grabyou can grab any desktop window and resize and reposition it. -
For π directional navigation, add to
use-package::config (global-set-key (kbd "M-<left>") #'dwin-windmove-left) (global-set-key (kbd "M-<right>") #'dwin-windmove-right) (global-set-key (kbd "M-<up>") #'dwin-windmove-up) (global-set-key (kbd "M-<down>") #'dwin-windmove-down))
In βοΈ KDE/System Settings/Shortcuts you have to add shortcut keys manually
Alt-lefttoetc/bin/dwin-left,Alt-righttoetc/bin/dwin-right,Alt-uptoetc/bin/dwin-up, andAlt-downtoetc/bin/dwin-down.
(alternatively, you can
- copy the contents of etc/_config/kglobalshortcutsrc at the end
of your
~/.config/kglobalshortcutsrcand - the files etc/_local-share-applications/*.desktop into your
~/.local/share/applicationsand then - ask kwin to reload its config:
qdbus6 org.kde.KWin /KWin reconfigure.)
Also ensure that _emacs-key is on your PATH, e.g., by copying it to
~/binor~/.local/bin(if in your PATH).Then with
M-<left>/<right>/<up>/<down>you can move between Emacs windows and between desktop windows seamlessly. -
For basic π·οΈ named navigation, add to
use-package::config (global-set-key (kbd "C-<f11>") #'dwin-switch-to-emacs-or)
In βοΈ KDE/System Settings/Shortcuts you have to add the shortcut key
Ctrl-f11toetc/bin/dwin-emacs,
Then with
<f11>you can switch from emacs to firefox and withC-<f11>you can switch back from firefox to emacs.
- β§ Arranging desktop windows can do the following:
| Key | Action |
|---|---|
a |
activate window |
c |
close window |
M |
minimize window (there is no maximize window) |
r |
raise window |
left |
move to window left |
right |
move to window right |
up |
move to window up |
down |
move to window down |
+ |
resize window horizontally (increase) |
- |
resize window horizontally (decrease) |
M-+ |
resize window vertically (increase) |
M-- |
resize window vertically (decrease) |
W |
pick another window to arrange |
. |
pick the Emacs window to arrange |
D<number> |
move window to desktop |
q |
quit |
-
For toggling π·οΈ named navigation with a single key, add to
use-package::config (defun my/firefox (&optional prefix) (interactive "P") (dwin-switch-to-app "firefox" prefix)) (global-set-key (kbd "<f11>") #'my/firefox) (defun my/zotero (&optional prefix) (interactive "P") (dwin-switch-to-app "zotero" prefix)) (global-set-key (kbd "M-<f11>") #'my/zotero)
In βοΈ KDE/System Settings/Shortcuts you have to add shortcut keys
f11toetc/bin/dwin-firefox, andAlt-f11toetc/bin/dwin-zotero.
Then you can toggle with
<f11>between firefox and emacs. -
π·οΈ Named navigation with apps with multiple windows:
- Commands with different arguments count as different apps:
okular b.pdfwill start a new window, even ifokular a.pdfruns already. - If there are multiple windows for the very same command, e.g., you started
several
okularprocesses and then opened pdfs with its file dialog, the window to switch to will be chosen as follows:- if you opened some app windows outside emacs, some from within emacs, the app windows from within emacs will be preferred.
- if there are multiple app windows started from emacs, then the first one will be activated.
- With
C-<number> M-x dwin-switch-to-app(the prefix argument) you can pick a specific window of the app. Once picked, it will be chosen automatically again when you switch to it next time.
- Generic global keys:
- You can use the global key mechanism to let emacs handle any key globally and execute some command.
dwin provides two types of navigation:
- π·οΈ navigation by name, e.g., switching to firefox, zotero, back to emacs, and
- π directional navigation, e.g., moving to the window to the right or below.
That navigation works globally, also outside emacs, requires that
the window manager forwards some keys globally to emacs;
for KDE one can bind a one-line script that uses emacsclient
to forward the key to emacs, using dwin-input-key defined in code sect. 1.
See etc/bin/dwin-firefox for an example. See
Further Details / 1
for why we cannot use tools like ydotool
for sending keys.
Emacs then can use functions provided by the window manager
to implement window navigation globally. We captured all required
methods in a window manager proxy object dwin-proxy whose
methods can be called via dwin-call. The proxy has to be created
once before use, e.g., during emacs initialization, using
dwin-setup. Currently only a proxy for KDE is implemented.
But it should be possible to implement further ones.
The KDE proxy uses
- dbus calls to org.kde.kglobalaccel (KDE's shortcuts application), esp. for directional navigation, and
- kdotool for navigation by name.
π·οΈ Navigation by name is provided by dwin-switch-to-app that will
- start an app, if it has no window yet,
- switch to its window, if it is not active, or
- switch back to emacs, if it is already active (toggle).
For each app (firefox, zotero etc.) one needs to
- define one emacs command (e.g., `my/firefox`),
- bind it to a key in emacs and
- make sure this key is forwarded globally to emacs,
i.e.,
- create a small script (e.g., `etc/bin/dwin-firefox`) and
- bind it globally to this key.
Switching back to emacs is handled by dwin-switch-to-emacs-or.
It needs the same handling as the other apps above.
By default, navigation by name will switch to the first window of an application,
if it has several. You can use a prefix arg to switch to a specific one,
e.g., C-2 M-x my/firefox or C-2 <f11> to switch to the second one.
See code sect. 3.
For π directional navigation, we defined a short function
dwin-windmove-left for each direction. The function tries to
move inside emacs via windmove, and if this fails, uses the
window manager to move out of emacs.
The same method also uses the window manager to move
directional from desktop windows. See code sect. 4.
Code sect. 5 contains function dwin-grab to β§ arrange desktop windows, i.e.,
to resize them, reposition them etc.
-
Requesting help for a global key binding with
describe keywill not work.Emacs will get the key forwarded, but not as input for the already running describe-key function. In effect, if you say
M-x describe-keyand then type a global key likeM-<left>, its action is executed and help for the next key the user types is shown. To view the help, you have to know how the key is called in emacs and sayM-: (describe-key (kbd "M-<left>"))instead.One could instrument
dwin-input-keyto detect ifdescribe-keyis running by checking(describe-key (kbd "M-<left>"))and then rundescribe-keywith the key instead of the command the key is bound to. However, I found no way to cancel the already running describe-key command. So the second issue will persist.
-
Why cannot we just use ydotool to send keys to emacs?
Tools like ydotool seem to be able to send key events only to the active/focused window and would be able only to implement the cases where one wants to move from within emacs, not the cases where one wants to move back to emacs from other applications (or between other applications). One could reimplement dwin in bash, then there is no need to input keys into emacs anymore.Unfortunately sending dbus events to kwin from bash was unstable for me: running
qdbus6 org.kde.kglobalaccel /component/kwin \ org.kde.kglobalaccel.Component invokeShortcut "Switch Window Left"often (not always) yielded
Cannot find org.kde.kglobalaccel.Component in object /component/kwin at org.kde.kglobalaccelThis never happened when sending the same events from emacs.
| package | X11 | Wayland | use wm | β§ arrange | π·οΈ named nav | π directional nav |
|---|---|---|---|---|---|---|
| exwm | β | π« | π« | β | β | β |
| ewmctrl | β | π« | β | β | π« | π« |
| dwin (this one) | β
(KDE) |
β
(KDE) |
β
(KDE) |
β | β | β |
-
exwm: exwm is a full X11 window manager implemented in Emacs.
- dwin uses an existing window manager like KDE's KWin instead.
- π Integration with Emacs is way tighter: desktop windows are buffers.
- π You have to setup a lot of basic things yourself (a panel, network manager, sync services etc. etc.), that usually KDE does for you.
- π exwm currently works only for X11, not for Wayland.
-
ewmctrl lets you arrange X11 desktop windows with wmctrl.
- limited to X11 and arranging windows.
-
kwin-minor-mode allows you to communicate with KDE's X11 window manager and wayland compositor KWin to implement a window management API on your own with its builtin javascript engine. In this context, it is comparable to tools like kdotool.
- Provides no user facing functionality directly.
-
sway.el integrates the window manager sway with Emacs.
- "The only directly usable feature as of now is the sway-shackle-display-buffer-frame function, which either creates a new frame with a given buffer or focuses that buffer in the frame itβs already displayed" (from their website).
-
emacs-tmux-pane integrates emacs windows with tmux panes.
- So it works with a text user interface, not a graphical user interface.
-
ace-window, e2wm, edwina, elscreen, elwm, es-windows, ewm and many other packages allow you to manage Emacs windows.
- dwin is about managing Emacs external, desktop windows.