Swayr & Swayrbar

builds.sr.ht status License GPL 3 or later dependency status Hits-of-Code

Table of Contents

Swayr, a window-switcher & more for sway

latest release

Swayr consists of a daemon, and a client. The swayrd daemon records window/workspace creations, deletions, and focus changes using sway's JSON IPC interface. The swayr client offers subcommands, see swayr --help, and sends them to the daemon which executes them.

Swayr commands

The swayr binary provides many subcommands of different categories.

Non-menu switchers

Those are commands which switch through a sequence of windows where the sequence is: 1. All windows with urgency hints. 2. All matching windows where which windows match is specific to the command. 3. The last recently used window at the time of the sequence start. 4. Back to the origin window, i.e., the window which had the focus at the time of the sequence start.

During each sequence no window will be visited twice, e.g., if some window has an urgency hint, matches the commands specification, and is also the LRU window, it's not visited once in each step 1, 2, and 3 but just in step 1.

The steps 1, 3, and 4 can be inhibited with the flags --skip-urgent, --skip-lru, and --skip-origin, respectively.

As said, which windows match is specific to each command:

Menu switchers

Those spawn a menu program where you can select a window (or workspace, or output, etc.) and act on that.

Menu shortcuts for non-matching input

All menu switching commands (switch-window, switch-workspace, and switch-workspace-or-window) now handle non-matching input instead of doing nothing. The input should start with any number of # (in order to be able to force a non-match), a shortcut followed by a colon, and some string as required by the shortcut. The following shortcuts are supported. - w:<workspace>: Switches to a possibly non-existing workspace. <workspace> must be a digit, a name or <digit>:<name>. The <digit>:<name> format is explained in man 5 sway. If that format is given, swayr will create the workspace using workspace number <digit>:<name>. If just a digit or name is given, the number argument is not used. - s:<cmd>: Executes the sway command <cmd> using swaymsg. - Any other input is assumed to be a workspace name and thus handled as w:<input> would do.

Cycling commands

Those commands cycle through (a subset of windows) in last-recently-used order.

Layout modification commands

These commands change the layout of the current workspace.

Miscellaneous commands

Screenshots

A screenshot of swayr switch-window

A screenshot of swayr
switch-workspace-or-window

Installation

Some distros have packaged swayr so that you can install it using your distro's package manager. Alternatively, it's easy to build and install it yourself using cargo.

Distro packages

The following GNU/Linux and BSD distros package swayr. Thanks a lot to the respective package maintainers! Refer to the repology site for details.

Packaging status AUR swayr-git package status

Building with cargo

You'll need to install the current stable rust toolchain using the one-liner shown at the official rust installation page.

Then you can install swayr like so: sh cargo install swayr

For getting updates easily, I recommend the cargo install-update plugin. ```sh

Install it once.

cargo install install-update

Then you can update all installed rust binary crates including swayr using:

cargo install-update --all

If you only want to update swayr, you can do so using:

cargo install-update -- swayr ```

Usage

You need to start the swayr daemon (swayrd) in your sway config (~/.config/sway/config) like so:

exec env RUST_BACKTRACE=1 RUST_LOG=swayr=debug swayrd > /tmp/swayrd.log 2>&1

The setting of RUST_BACKTRACE=1, RUST_LOG=swayr=debug and the redirection of the output to some logfile is optional but helps a lot when something doesn't work. Especially, if you encounter a crash in certain situations and you want to report a bug, it would be utmost helpful if you could reproduce the issue with backtrace and logging at the debug level and attach that to your bug report. Valid log levels in the order from logging more to logging less are: trace, debug, info, warn, error, off.

Beyond starting the daemon, you will want to bind swayr commands to some keys like so:

``` bindsym $mod+Space exec env RUST_BACKTRACE=1 \ swayr switch-window >> /tmp/swayr.log 2>&1

bindsym $mod+Delete exec env RUST_BACKTRACE=1 \ swayr quit-window >> /tmp/swayr.log 2>&1

bindsym $mod+Tab exec env RUST_BACKTRACE=1 \ swayr switch-to-urgent-or-lru-window >> /tmp/swayr.log 2>&1

bindsym $mod+Next exec env RUST_BACKTRACE=1 \ swayr next-window all-workspaces >> /tmp/swayr.log 2>&2

bindsym $mod+Prior exec env RUST_BACKTRACE=1 \ swayr prev-window all-workspaces >> /tmp/swayr.log 2>&2

bindsym $mod+Shift+Space exec env RUST_BACKTRACE=1 \ swayr switch-workspace-or-window >> /tmp/swayr.log 2>&1

bindsym $mod+c exec env RUST_BACKTRACE=1 \ swayr execute-swaymsg-command >> /tmp/swayr.log 2>&1

bindsym $mod+Shift+c exec env RUST_BACKTRACE=1 \ swayr execute-swayr-command >> /tmp/swayr.log 2>&1 ```

Of course, configure the keys to your liking. Again, enabling rust backtraces and logging are optional.

Pending a fix for [Sway issue

6456](https://github.com/swaywm/sway/issues/6456) or a merge of [Sway PR

6920](https://github.com/swaywm/sway/pull/6920), it will be possible to close

a sequence of non-menu switching commands or window cycling commands using a nop command bound to the release of the $mod key. Assuming your $mod is bound to Super_L it could look something like this:

bindsym --release Super_L exec swayr nop

Until then, there's the focus.auto_nop_delay option which see below in the Configuration section.

Configuration

Swayr can be configured using the ~/.config/swayr/config.toml or /etc/xdg/swayr/config.toml config file.

If no config files exists, a simple default configuration will be created on the first invocation for use with the wofi menu program.

It should be easy to adapt that default config for usage with other menu programs such as dmenu, bemenu, rofi, a script spawning a terminal with fzf, or whatever. The only requirement is that the launcher needs to be able to read the items to choose from from stdin and spit out the selected item to stdout.

The default config looks like this:

```toml [menu] executable = 'wofi' args = [ '--show=dmenu', '--allow-markup', '--allow-images', '--insensitive', '--cache-file=/dev/null', '--parse-search', '--height=40%', '--prompt={prompt}', ]

[format] outputformat = '{indent}Output {name} ({id})' workspaceformat = '{indent}Workspace {name} [{layout}] ({id})' containerformat = '{indent}Container [{layout}] on workspace {workspacename} {marks} ({id})' windowformat = 'img:{appicon}:text:{indent}{appname} — {urgencystart}“{title}”{urgencyend} on workspace {workspacename} {marks} ({id})' indent = ' ' urgencystart = '' urgencyend = '' htmlescape = true icondirs = [ '/usr/share/icons/hicolor/scalable/apps', '/usr/share/icons/hicolor/64x64/apps', '/usr/share/icons/hicolor/48x48/apps', '/usr/share/icons/Adwaita/64x64/apps', '/usr/share/icons/Adwaita/48x48/apps', '/usr/share/pixmaps', ]

[layout] autotile = false autotileminwindowwidthperoutputwidth = [ [1024, 500], [1280, 600], [1400, 680], [1440, 700], [1600, 780], [1920, 920], [2560, 1000], [3440, 1000], [4096, 1200], ]

[focus] lockin_delay = 750

[misc] autonopdelay = 3000 seq_inhibit = false ```

In the following, all sections are explained.

The menu section

In the [menu] section, you can specify the menu program using the executable name or full path and the args (flags and options) it should get passed. If some argument contains the placeholder {prompt}, it is replaced with a prompt such as "Switch to window" depending on context.

The format section

In the [format] section, format strings are specified defining how selection choices are to be layed out. wofi supports pango markup which makes it possible to style the text using HTML and CSS. The following formats are supported right now. * output_format defines how outputs (monitors) are displayed in the menu program, workspace_format defines how workspaces are displayed, container_format defines how non-workspace containers are displayed, and window_format defines how application windows are displayed. * In these formats, the following placeholders can be used: * {name} gets replaced by the output name, the workspace number or name or a window's title. The placeholder {title} is an obsolete synonym which will be removed in a later version. * {layout} shows the workspace or container's layout. * {id} gets replaced by the sway-internal con id. * {indent} gets replaced with N times the new format.indent value where N is the depth in the shown menu input. * {app_name} gets replaced with a window's application name. * {marks} shows a comma-separated list of the container's or window's marks. * {app_icon} shows the application's icon (a path to a PNG or SVG file). * {workspace_name} gets replaced with the name or number of the workspace the container or window belongs to. * The placeholders {urgency_start} and {urgency_end} get replaced by the empty string if the window has no urgency flag and with the values of the same-named formats if the window has the urgency flag set. That makes it possible to highlight urgent windows as shown in the default config. * indent is a string which is repeatedly inserted at the {indent} placeholder in formats. * html_escape defines if the strings replacing the placeholders above (except for {urgency_start} and {urgency_end}) should be HTML-escaped. * urgency_start is a string which replaces the {urgency_start} placeholder in window_format. * urgency_end is a string which replaces the {urgency_end} placeholder in window_format. * icon_dirs is a vector of directories in which to look for application icons in order to compute the {app_icon} replacement. * fallback_icon is a path to some PNG/SVG icon which will be used as {app_icon} if no application-specific icon can be determined.

All the placeholders except {app_icon}, {indent}, {urgency_start}, and {urgency_end} may optionally provide a format string as specified by Rust's std::fmt. The syntax is {<placeholder>:<fmt_str><clipped_str>}. For example, {app_name:{:>10.10}} would mean that the application name is printed with exactly 10 characters. If it's shorter, it will be right-aligned (the >) and padded with spaces, if it's longer, it'll be cut after the 10th character. Another example, {app_name:{:.10}...} would mean that the application name is truncated at 10 characters. If it's shorter, it will be printed as-is (no padding), if it's longer, it'll be cut after the 10th character and the last 3 characters of that substring will be replaced with ... (<clipped_str>).

It is crucial that during selection (using wofi or some other menu program) each window has a different display string. Therefore, it is highly recommended to include the {id} placeholder at least in container_format and window_format. Otherwise, e.g., two vertical splits on the same workspace or two terminals (of the same terminal app) with the same working directory (and therefore, the same title) wouldn't be distinguishable.

Hint for wofi: wofi supports icons with the syntax 'img:<image-file>:text:<text>', so a suitable window_format with application icon should start with img:{app_icon}:text:.

Hint for rofi: rofi supports icons with the syntax "<text>\u0000icon\u00001f<image-file>", so a suitable window_format with application icon should end with "\u0000icon\u001f<image-file>". Also note that you must enclose your window_format value with double-quotes and not with single-quotes. Singe-quote strings are literal strings in TOML where no escape-sequences are processed whereas for double-quoted strings (so-called basic strings) escape-sequences are processed. rofi requires a null character and a PARAGRAPH SEPARATOR for image sequences.

The layout section

In the [layout] section, you can enable auto-tiling by setting auto_tile to true (the default is false). The option auto_tile_min_window_width_per_output_width defines the minimum width in pixels which your windows should have per output width. For example, the example setting above says that on an output which is 1600 pixels wide, each window should have at least a width of 780 pixels, thus there may be at most two side-by-side windows (Caution, include your borders and gaps in your calculation!). There will be no auto-tiling doesn't include your output's exact width.

If auto_tile is enabled, swayr will automatically split either vertically or horizontally according to this algorithm: - For all outputs: + For all (nested) containers on that output (except the scratchpad): - For all child windows of that container: + If the container is split horizontally and creating another window would make the current child window smaller than the minimum width, execute split vertical (the swaymsg command over IPC) on the child. + Else if the container is split vertically and now there is enough space so that creating another window would still leave the current child window above or equal to the minimum width, call split horizontal on the child. + Otherwise, do nothing for this container. This means that stacked or tabbed containers will never be affected by auto-tiling.

There is one caveat: it would be nice to also trigger auto-tiling when windows or containers are resized but unfortunately, resizing doesn't issue any events over IPC. Therefore, auto-tiling is triggered by new-window events, close-events, move-events, floating-events, and also focus-events. The latter are a workaround and wouldn't be required if there were resize-events.

The focus section

In the [focus] section, you can configure the amount of time a window has to keep the focus in order to affect the LRU order, the lockin_delay (specified in milliseconds). If a given window is only briefly focused, e.g., by moving the mouse over it on the way to another window with sway's focus_follows_mouse set to yes or always, then its position in the LRU order will not be modified.

The misc section

In the [misc] section, there's the auto_nop_delay option. When some swayr command is executed, this amount of milliseconds is waited before a nop command (see the commands documentation) is executed in order to break out of a next-*-window/prev-*-window sequence or a switch-to-*or-urgent-or-lru-window cycle automatically. If another swayr command is executed within this time frame, the auto-nop execution will be delayed for another auto_nop_delay milliseconds. If this option is not specified explicitly, no automatic nop commands will be executed.

A more elegant solution using a key release binding is discussed at the end of the Usage section. However, that requires a PR to sway which has not been merged so far.

The seq_inhibit boolean controls how swayrd behaves during a sequence of window cycling commands.

Note that the key release binding solution lends itself to using seq_inhibit=true.

Version changes

Since version 0.8.0, I've started writing a NEWS file listing the news, and changes to swayr commands or configuration options. If something doesn't seem to work as expected after an update, please consult this file to check if there has been some (possibly incompatible) change requiring an update of your config.

Swayrbar

latest release

swayrbar is a status command for sway's swaybar implementing the swaybar-protocol(7). This means, you would setup your swaybar like so in your ~/.config/sway/config:

```conf bar { swaybarcommand swaybar # Use swayrbar as status command with some logging output which # is redirected to /tmp/swayrbar.log. Be sure to only redirect # stderr because the swaybar protocol requires the statuscommand # to emit JSON to stdout which swaybar reads. statuscommand env RUSTBACKTRACE=1 RUST_LOG=swayr=debug swayrbar 2> /tmp/swayrbar.log position top font pango:Iosevka 11 height 20

colors {
    statusline #f8c500
    background #33333390
}

} ```

swayrbar, like waybar, consists of a set of modules which you can enable and configure via its config file, either the one specified via the command line option --config-file, the user-specific (~/.config/swayrbar/config.toml), or the system-wide (/etc/xdg/swayrbar/config.toml). Modules emit information which swaybar then displays and mouse clicks on a module's space in swaybar are propagated back and trigger some action (e.g., a shell command).

Right now, there are the following modules:

  1. The window module can show the title and application name of the current window in sway.
  2. The sysinfo module can show things like CPU/memory utilization or system load.
  3. The battery module can show the current state of charge, the state (e.g., charging), and the state of health.
  4. The date module can show, you guess it, the current date and time!
  5. The pactl module can show the current volume percentage and muted state. Clicks can increase/decrease the volume or toggle the mute state.

I guess there will be more modules in the future as time permits. Patches are certainly very welcome!

Screenshots

A screenshot of swaybar running with swayrbar

Installation

Some distros have a swayrbar package so that you can install it using your distro's package manager, see the repology site for details. Alternatively, it's easy to build and install it yourself using cargo.

Packaging status

Installation via Cargo

You'll need to install the current stable rust toolchain using the one-liner shown at the official rust installation page.

Then you can install swayrbar like so: sh cargo install swayrbar

For getting updates easily, I recommend the cargo install-update plugin. ```sh

Install it once.

cargo install install-update

Then you can update all installed rust binary crates including swayr using:

cargo install-update --all

If you only want to update swayr, you can do so using:

cargo install-update -- swayrbar ```

Configuration

When swayrbar is run for the very first time and doesn't find an existing configuration file at ~/.config/swayrbar/config.toml (user-specific) or /etc/xdg/swayrbar/config.toml (system-wide), it'll create a new user-specific one where all modules are enabled and set up with some reasonable (according to the author) default values. Adapt it to your needs.

The syntax of the config file is TOML. Here's a short example with all top-level options (one!) and one module.

```toml refresh_interval = 1000

[[modules]] name = 'window' instance = '0' format = '🪟 {title} — {appname}' htmlescape = false

[modules.on_click] Left = ['swayr', 'switch-to-urgent-or-lru-window'] Right = ['kill', '{pid}'] ```

The refresh_interval defines the number of milliseconds between refreshes of swaybar.

The remainder of the configuration defines a list of modules with their configuration (which is an array of tables in TOML where a module's on_click).

The on_click table can also be written as inline table

toml on_click = { Left = ['swayr', 'switch-to-urgent-or-lru-window'], Right = ['kill', '{pid}'] }

but then it has to be on one single line.

The window module

The window module supports the following placeholders: * {title} or {name} expand to the currently focused window's title. * {app_name} is the application name. * {pid} is the process id.

By default, it has the following click bindings: * Left executes swayr switch-to-urgent-or-lru-window. * Right kills the process of the window.

The sysinfo module

The sysinfo module supports the following placeholders: * {cpu_usage} is the percentage of CPU utilization. * {mem_usage} is the percentage of memory utilization. * {load_avg_1} is the average system load in the last minute. * {load_avg_5} is the average system load in the last five minutes. * {load_avg_15} is the average system load in the last fifteen minutes.

By default, it has the following click bindings: * Left executes foot htop.

The battery module

The battery module supports the following placeholders: * {state_of_charge} is the percentage of charge wrt. the battery's current capacity. * {state_of_health} is the percentage of the battery's remaining capacity compared to its original capacity. * {state} is the current state, e.g., something like Discharging or Full.

The pactl module

The pactl module requires the pulse-audio command line tool of the same name to be installed. It supports the following placeholders: * {volume} is the current volume percentage of the default sink. * {muted} is the string " muted" if the default sink is currently muted, otherwise it is the empty string.

By default, it has the following click bindings: * Left calls the pavucontrol program (PulseAudio GUI control). * Right toggles the default sink's mute state. * WheelUp and WheelDown increase/decrease the volume of the default sink.

The date module

The date module shows the date and time by defining the format using chrono's strftime format.

Version changes

Version changes are summarized in the NEWS file. If something doesn't seem to work as expected after an update, please consult this file to check if there has been some (possibly incompatible) change requiring an update of your config.

Questions & Patches

For asking questions, sending feedback, or patches, refer to my public inbox (mailinglist). Please mention the project you are referring to in the subject, e.g., swayr or swayrbar (or other projects in different repositories).

Bugs

It compiles, therefore there are no bugs. Oh well, if you still found one or want to request a feature, you can do so here.

Build status

builds.sr.ht status

License

Swayr & Swayrbar are licensed under the GPLv3 (or later).