• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

.builds/H16-Dec-2021-1110

cargo-crates/H03-May-2022-628,919524,648

misc/H03-May-2022-

src/H03-May-2022-2,9172,414

.gitignoreH A D16-Dec-20218 21

Cargo.lockH A D16-Dec-202112 KiB484429

Cargo.tomlH A D16-Dec-2021571 2220

LICENSEH A D16-Dec-202134.3 KiB675553

NEWS.mdH A D16-Dec-20214.9 KiB9785

README.mdH A D16-Dec-202118.8 KiB404340

rustfmt.tomlH A D16-Dec-2021183 87

README.md

1# Swayr is a window switcher (and more) for sway
2
3[![builds.sr.ht status](https://builds.sr.ht/~tsdh/swayr.svg)](https://builds.sr.ht/~tsdh/swayr?)
4[![latest release](https://img.shields.io/crates/v/swayr.svg)](https://crates.io/crates/swayr)
5[![License GPL 3 or later](https://img.shields.io/crates/l/swayr.svg)](https://www.gnu.org/licenses/gpl-3.0.en.html)
6[![dependency status](https://deps.rs/repo/sourcehut/~tsdh/swayr/status.svg)](https://deps.rs/repo/sourcehut/~tsdh/swayr)
7[![Hits-of-Code](https://hitsofcode.com/sourcehut/~tsdh/swayr?branch=main)](https://hitsofcode.com/sourcehut/~tsdh/swayr/view?branch=main)
8
9Swayr consists of a demon, and a client.  The demon `swayrd` records
10window/workspace creations, deletions, and focus changes using sway's JSON IPC
11interface.  The client `swayr` offers subcommands, see `swayr --help`, and
12sends them to the demon which executes them.
13
14Right now, there are these subcommands:
15* `switch-to-urgent-or-lru-window` switches to the next window with urgency
16  hint (if any) or to the last recently used window.
17* `switch-window` displays all windows in the order urgent first, then
18  last-recently-used, focused last and focuses the selected.
19* `switch-workspace` displays all workspaces in LRU order and switches to the
20  selected one.
21* `switch-output` shows all outputs in the menu and focuses the selected one.
22* `switch-workspace-or-window` displays all workspaces and their windows and
23   switches to the selected workspace or window.
24* `switch-workspace-container-or-window` shows workspaces, containers, and
25  their windows in the menu program and switches to the selected one.
26* `switch-to` shows outputs, workspaces, containers, and their windows in the
27  menu program and switches to the selected one.
28* `quit-window` displays all windows and quits the selected one.
29* `quit-workspace-or-window` displays all workspaces and their windows and
30  allows to quit either the selected workspace (all its windows) or the
31  selected window.
32* `quit-workspace-container-or-window` shows workspaces, containers, and their
33  windows and quits all windows of the selected workspace/container or the
34  selected window.
35* `move-focused-to-workspace` moves the currently focused window or container
36  to another workspace selected with the menu program.  Non-matching input of
37  the form `#w:<workspace>` where the hash and `w:` shortcut are optional can
38  be used to move it to a new workspace.
39* `move-focused-to` moves the currently focused container or window to the
40  selected output, workspace, container, window.  Non-matching input is handled
41  like with `move-focused-to-workspace`.
42* `swap-focused-with` swaps the currently focused window or container with the
43  one selected from the menu program.
44* `next-window (all-workspaces|current-workspace)` & `prev-window
45  (all-workspaces|current-workspace)` focus the next/previous window in
46  depth-first iteration order of the tree.  The argument `all-workspaces` or
47  `current-workspace` define if all windows of all workspaces or only those of
48  the current workspace are considered.
49* `next-tiled-window` & `prev-tiled-window` do the same as `next-window` &
50  `prev-window` but switch only between windows contained in a tiled container.
51* `next-tabbed-or-stacked-window` & `prev-tabbed-or-stacked-window` do the same
52  as `next-window` & `prev-window` but switch only between windows contained in
53  a tabbed or stacked container.
54* `next-floating-window` & `prev-floating-window` do the same as `next-window`
55  & `prev-window` but switch only between floating windows.
56* `next-window-of-same-layout` & `prev-window-of-same-layout` is like
57  `next-floating-window` / `prev-floating-window` if the current window is
58  floating, it is like `next-tabbed-or-stacked-window` /
59  `prev-tabbed-or-stacked-window` if the current window is in a tabbed or
60  stacked container, it is like `next-tiled-window` / `prev-tiled-window` if
61  the current windows is in a tiled container, and is like `next-window` /
62  `prev-window` otherwise.
63* `tile-workspace exclude-floating|include-floating` tiles all windows on the
64  current workspace (excluding or including floating ones).  That's done by
65  moving all windows away to some special workspace, setting the current
66  workspace to `splith` layout, and then moving the windows back.  If the
67  `auto_tile` feature is used, see the Configuration section below, it'll
68  change from splitting horizontally to vertically during re-insertion.
69* `shuffle-tile-workspace exclude-floating|include-floating` shuffles & tiles
70  all windows on the current workspace.  The shuffle part means that (a) the
71  windows are shuffled before re-insertion, and (b) a randomly chosen already
72  re-inserted window is focused before re-inserting another window.  So while
73  `tile-workspace` on a typical horizontally oriented screen and 5 windows will
74  usually result in a layout with one window on the left and all four others
75  tiled vertially on the right, `shuffle-tile-workspace` in combination with
76  `auto_tile` usually results in a more balanced layout, i.e., 2 windows tiled
77  vertically on the right and the other 4 tiled vertially on the left.  If you
78  have less than a handful of windows, just repeat `shuffle-tile-workspace` a
79  few times until happenstance creates the layout you wanted.
80* `tab-workspace exclude-floating|include-floating` puts all windows of the
81  current workspace into a tabbed container.
82* `toggle-tab-shuffle-tile-workspace exclude-floating|include-floating` toggles
83  between a tabbed and tiled layout, i.e., it calls `shuffle-tile-workspace` if
84  it is currently tabbed, and calls `shuffle-tile-workspace` if it is currently
85  tiled.
86* `configure-outputs` lets you repeatedly issue output configuration commands
87  until you abort the menu program.
88* `execute-swaymsg-command` displays most swaymsg which don't require
89  additional input and executes the selected one.  That's handy especially for
90  less often used commands not bound to a key.  Non-matching input will be
91  executed executed as-is with `swaymsg`.
92* `execute-swayr-command` displays all commands above and executes the selected
93  one.  (This is useful for accessing swayr commands which are not bound to a
94  key.)
95
96### Menu shortcuts for non-matching input
97
98All menu switching commands (`switch-window`, `switch-workspace`, and
99`switch-workspace-or-window`) now handle non-matching input instead of doing
100nothing.  The input should start with any number of `#` (in order to be able to
101force a non-match), a shortcut followed by a colon, and some string as required
102by the shortcut.  The following shortcuts are supported.
103- `w:<workspace>`: Switches to a possibly non-existing workspace.
104  `<workspace>` must be a digit, a name or `<digit>:<name>`.  The
105  `<digit>:<name>` format is explained in `man 5 sway`.  If that format is
106  given, `swayr` will create the workspace using `workspace number
107  <digit>:<name>`.  If just a digit or name is given, the `number` argument is
108  not used.
109- `s:<cmd>`: Executes the sway command `<cmd>` using `swaymsg`.
110- Any other input is assumed to be a workspace name and thus handled as
111  `w:<input>` would do.
112
113## Screenshots
114
115![A screenshot of swayr switch-window](misc/switch-window.png "swayr
116switch-window")
117
118![A screenshot of swayr
119switch-workspace-or-window](misc/switch-workspace-or-window.png "swayr
120switch-workspace-or-window")
121
122## Installation
123
124Some distros have packaged swayr so that you can install it using your distro's
125package manager.  Alternatively, it's easy to build and install it yourself
126using `cargo`.
127
128### Distro packages
129
130The following GNU/Linux and BSD distros package swayr.  Thanks a lot to the
131respective package maintainers!  Refer to the [repology
132site](https://repology.org/project/swayr/versions) for details.
133
134[![Packaging status](https://repology.org/badge/vertical-allrepos/swayr.svg)](https://repology.org/project/swayr/versions)
135[![AUR swayr-git package status](https://repology.org/badge/version-for-repo/aur/swayr.svg?allow_ignored=yes&header=AUR%20swayr-git)](https://repology.org/project/swayr/versions)
136
137### Building with cargo
138
139You'll need to install the current stable rust toolchain using the one-liner
140shown at the [official rust installation
141page](https://www.rust-lang.org/tools/install).
142
143Then you can install swayr like so:
144```sh
145cargo install swayr
146```
147
148For getting updates easily, I recommend the cargo `install-update` plugin.
149```sh
150# Install it once.
151cargo install install-update
152
153# Then you can update all installed rust binary crates including swayr using:
154cargo install-update --all
155
156# If you only want to update swayr, you can do so using:
157cargo install-update -- swayr
158```
159
160## Usage
161
162You need to start the swayr demon `swayrd` in your sway config
163(`~/.config/sway/config`) like so:
164
165```
166exec env RUST_BACKTRACE=1 swayrd > /tmp/swayrd.log 2>&1
167```
168
169The setting of `RUST_BACKTRACE=1` and the redirection of the output to some
170logfile is optional but helps a lot when something doesn't work.  Especially,
171if you encounter a crash in certain situations and you want to report a bug, it
172would be utmost helpful if you could reproduce the issue with backtrace and
173logging and attach that to your bug report.
174
175Next to starting the demon, you want to bind swayr commands to some keys like
176so:
177
178```
179bindsym $mod+Space exec env RUST_BACKTRACE=1 \
180    swayr switch-window >> /tmp/swayr.log 2>&1
181
182bindsym $mod+Delete exec env RUST_BACKTRACE=1 \
183    swayr quit-window > /tmp/swayr.log 2>&1
184
185bindsym $mod+Tab exec env RUST_BACKTRACE=1 \
186    swayr switch-to-urgent-or-lru-window >> /tmp/swayr.log 2>&1
187
188bindsym $mod+Next exec env RUST_BACKTRACE=1 \
189    swayr next-window all-workspaces >> /tmp/swayr.log 2>&2
190
191bindsym $mod+Prior exec env RUST_BACKTRACE=1 \
192    swayr prev-window all-workspaces >> /tmp/swayr.log 2>&2
193
194bindsym $mod+Shift+Space exec env RUST_BACKTRACE=1 \
195    swayr switch-workspace-or-window >> /tmp/swayr.log 2>&1
196
197bindsym $mod+c exec env RUST_BACKTRACE=1 \
198    swayr execute-swaymsg-command >> /tmp/swayr.log 2>&1
199
200bindsym $mod+Shift+c exec env RUST_BACKTRACE=1 \
201    swayr execute-swayr-command >> /tmp/swayr.log 2>&1
202```
203
204Of course, configure the keys to your liking.  Again, enabling rust backtraces
205and logging are optional.
206
207## Configuration
208
209Swayr can be configured using the `~/.config/swayr/config.toml` or
210`/etc/xdg/swayr/config.toml` config file.
211
212If no config files exists, a simple default configuration will be created on the
213first invocation for use with the [wofi](https://todo.sr.ht/~scoopta/wofi)
214menu program.
215
216It should be easy to adapt that default config for usage with other menu
217programs such as [dmenu](https://tools.suckless.org/dmenu/),
218[bemenu](https://github.com/Cloudef/bemenu),
219[rofi](https://github.com/davatorium/rofi), a script spawning a terminal with
220[fzf](https://github.com/junegunn/fzf), or whatever.  The only requirement is
221that the launcher needs to be able to read the items to choose from from stdin
222and spit out the selected item to stdout.
223
224The default config looks like this:
225
226```toml
227[menu]
228executable = 'wofi'
229args = [
230    '--show=dmenu',
231    '--allow-markup',
232    '--allow-images',
233    '--insensitive',
234    '--cache-file=/dev/null',
235    '--parse-search',
236    '--height=40%',
237    '--prompt={prompt}',
238]
239
240[format]
241output_format = '{indent}<b>Output {name}</b>    <span alpha=\"20000\">({id})</span>'
242workspace_format = '{indent}<b>Workspace {name} [{layout}]</b>    <span alpha="20000">({id})</span>'
243container_format = '{indent}<b>Container [{layout}]</b> on workspace {workspace_name} <i>{marks}</i>    <span alpha="20000">({id})</span>'
244window_format = 'img:{app_icon}:text:{indent}<i>{app_name}</i> — {urgency_start}<b>“{title}”</b>{urgency_end} on workspace {workspace_name} <i>{marks}</i>    <span alpha="20000">({id})</span>'
245indent = '    '
246urgency_start = '<span background="darkred" foreground="yellow">'
247urgency_end = '</span>'
248html_escape = true
249icon_dirs = [
250    '/usr/share/icons/hicolor/scalable/apps',
251    '/usr/share/icons/hicolor/64x64/apps',
252    '/usr/share/icons/hicolor/48x48/apps',
253    '/usr/share/icons/Adwaita/64x64/apps',
254    '/usr/share/icons/Adwaita/48x48/apps',
255    '/usr/share/pixmaps',
256]
257
258[layout]
259auto_tile = false
260auto_tile_min_window_width_per_output_width = [
261    [1024, 500],
262    [1280, 600],
263    [1400, 680],
264    [1440, 700],
265    [1600, 780],
266    [1920, 920],
267    [2560, 1000],
268    [3440, 1000],
269    [4096, 1200],
270]
271```
272
273In the following, all sections are explained.
274
275### The menu section
276
277In the `[menu]` section, you can specify the menu program using the
278`executable` name or full path and the `args` (flags and options) it should get
279passed.  If some argument contains the placeholder `{prompt}`, it is replaced
280with a prompt such as "Switch to window" depending on context.
281
282### The format section
283
284In the `[format]` section, format strings are specified defining how selection
285choices are to be layed out.  `wofi` supports [pango
286markup](https://docs.gtk.org/Pango/pango_markup.html) which makes it possible
287to style the text using HTML and CSS.  The following formats are supported
288right now.
289* `output_format` defines how outputs (monitors) are displayed in the menu
290  program, `workspace_format` defines how workspaces are displayed,
291  `container_format` defines how non-workspace containers are displayed, and
292  `window_format` defines how application windows are displayed.
293* In these formats, the following placeholders can be used:
294  * `{name}` gets replaced by the output name, the workspace number or name or
295    a window's title.  The placeholder `{title}` is an obsolete synonym which
296    will be removed in a later version.
297  * `{layout}` shows the workspace or container's layout.
298  * `{id}` gets replaced by the sway-internal con id.
299  * `{indent}` gets replaced with N times the new `format.indent` value where N
300    is the depth in the shown menu input.
301  * `{app_name}` gets replaced with a window's application name.
302  * `{marks}` shows a comma-separated list of the container's or window's
303     marks.
304  * `{app_icon}` shows the application's icon (a path to a PNG or SVG file).
305  * `{workspace_name}` gets replaced with the name or number of the workspace
306    the container or window belongs to.
307  * The placeholders `{urgency_start}` and `{urgency_end}` get replaced by the
308    empty string if the window has no urgency flag and with the values of the
309    same-named formats if the window has the urgency flag set.  That makes it
310    possible to highlight urgent windows as shown in the default config.
311* `indent` is a string which is repeatedly inserted at the `{indent}`
312  placeholder in formats.
313* `html_escape` defines if the strings replacing the placeholders above (except
314  for `{urgency_start}` and `{urgency_end}`) should be HTML-escaped.
315* `urgency_start` is a string which replaces the `{urgency_start}` placeholder
316  in `window_format`.
317* `urgency_end` is a string which replaces the `{urgency_end}` placeholder in
318  `window_format`.
319* `icon_dirs` is a vector of directories in which to look for application icons
320  in order to compute the `{app_icon}` replacement.
321* `fallback_icon` is a path to some PNG/SVG icon which will be used as
322  `{app_icon}` if no application-specific icon can be determined.
323
324It is crucial that during selection (using wofi or some other menu program)
325each window has a different display string.  Therefore, it is highly
326recommended to include the `{id}` placeholder at least in `container_format`
327and `window_format`.  Otherwise, e.g., two vertical splits on the same
328workspace or two terminals (of the same terminal app) with the same working
329directory (and therefore, the same title) wouldn't be distinguishable.
330
331**Hint for wofi**: `wofi` supports icons with the syntax
332`'img:<image-file>:text:<text>'`, so a suitable `window_format` with
333application icon should start with `img:{app_icon}:text:`.
334
335**Hint for rofi**: `rofi` supports icons with the syntax
336`"<text>\u0000icon\u00001f<image-file>"`, so a suitable `window_format` with
337application icon should end with `"\u0000icon\u001f<image-file>"`.  Also note
338that you must enclose your `window_format` value with double-quotes and not
339with single-quotes.  Singe-quote strings are literal strings in
340[TOML](https://toml.io/en/v1.0.0#string) where no escape-sequences are
341processed whereas for double-quoted strings (so-called basic strings)
342escape-sequences are processed.  `rofi` requires a null character and a
343PARAGRAPH SEPARATOR for image sequences.
344
345### The layout section
346
347In the `[layout]` section, you can enable auto-tiling by setting `auto_tile` to
348`true` (the default is `false`).  The option
349`auto_tile_min_window_width_per_output_width` defines the minimum width in
350pixels which your windows should have per output width.  For example, the
351example setting above says that on an output which is 1600 pixels wide, each
352window should have at least a width of 780 pixels, thus there may be at most
353two side-by-side windows (Caution, include your borders and gaps in your
354calculation!).  There will be no auto-tiling doesn't include your output's
355exact width.
356
357If `auto_tile` is enabled, swayr will automatically split either vertically or
358horizontally according to this algorithm:
359- For all outputs:
360  + For all (nested) containers on that output (except the scratchpad):
361    - For all child windows of that container:
362      + If the container is split horizontally and creating another window
363        would make the current child window smaller than the minimum width,
364        execute `split vertical` (the `swaymsg` command over IPC) on the child.
365      + Else if the container is split vertically and now there is enough space
366        so that creating another window would still leave the current child
367        window above or equal to the minimum width, call `split horizontal` on
368        the child.
369      + Otherwise, do nothing for this container.  This means that stacked or
370        tabbed containers will never be affected by auto-tiling.
371
372There is one caveat: it would be nice to also trigger auto-tiling when windows
373or containers are resized but unfortunately, resizing doesn't issue any events
374over IPC.  Therefore, auto-tiling is triggered by new-window events,
375close-events, move-events, floating-events, and also focus-events.  The latter
376are a workaround and wouldn't be required if there were resize-events.
377
378## Version Changes
379
380Since version 0.8.0, I've started writing a [NEWS](NEWS.md) file listing the
381news, and changes to `swayr` commands or configuration options.  If something
382doesn't seem to work as expected after an update, please consult this file to
383check if there has been some (possibly incompatible) change requiring an update
384of your config.
385
386## Questions & Patches
387
388For asking questions, sending feedback, or patches, refer to [my public inbox
389(mailinglist)](https://lists.sr.ht/~tsdh/public-inbox).  Please mention the
390project you are referring to in the subject.
391
392## Bugs
393
394Bugs and requests can be reported [here](https://todo.sr.ht/~tsdh/swayr).
395
396## Build status
397
398[![builds.sr.ht status](https://builds.sr.ht/~tsdh/swayr.svg)](https://builds.sr.ht/~tsdh/swayr?)
399
400## License
401
402Swayr is licensed under the
403[GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html) (or later).
404