#hyprland #workspace #window #switch #gui #switching #keyboard-shortcuts

bin+lib hyprswitch

A CLI/GUI that allows switching between windows in Hyprland

9 stable releases

1.3.0 May 14, 2024
1.2.4 May 5, 2024
1.2.3 Apr 26, 2024
1.2.2 Mar 9, 2024
1.1.1 Feb 25, 2024

#165 in Command line utilities

Download history 185/week @ 2024-02-21 66/week @ 2024-02-28 403/week @ 2024-03-06 102/week @ 2024-03-13 32/week @ 2024-03-20 28/week @ 2024-03-27 41/week @ 2024-04-03 29/week @ 2024-04-10 16/week @ 2024-04-17 179/week @ 2024-04-24 216/week @ 2024-05-01 131/week @ 2024-05-08 130/week @ 2024-05-15

658 downloads per month

MIT and GPL-3.0-or-later

3.5MB
5K SLoC

C 3.5K SLoC // 0.0% comments Rust 1.5K SLoC // 0.0% comments Python 324 SLoC // 0.1% comments Lua 29 SLoC // 0.1% comments Vala 22 SLoC

Contains (ELF exe/lib, 105KB) libgtk4-layer-shell.so.1.0.2, (ELF lib, 105KB) libgtk4-layer-shell.so, (ELF lib, 105KB) libgtk4-layer-shell.so.0, (ELF exe/lib, 61KB) layer-surface.c.o, (ELF exe/lib, 25KB) simple-example-vala, (ELF exe/lib, 19KB) meson-generated_simple-example.c.o and 6 more.

hyprswitch

crates.io Docs Tests

A rust CLI/GUI to switch between windows in Hyprland

It can cycle through windows using keyboard shortcuts or/and a GUI.

Windows are sorted by their position on the screen, and can be filtered by class or workspace.

To use the GUI, you need to pass the --daemon flag to the script which will start a socket server and a GUI. Subsequent calls to the script (with the --daemon flag) will send the command to the daemon which will execute the command and update the GUI.

image.png

Installation

From Source

Arch

  • paru -S hyprswitch / yay -S hyprswitch

Nixos

  • add hyprswitch.url = "github:h3rmt/hyprswitch/release"; to flake inputs
  • add specialArgs = { inherit inputs; }; to nixpkgs.lib.nixosSystem
  • add inputs.hyprswitch.packages.x86_64-linux.default to your environment.systemPackages
  • available systems: aarch64-linux, i686-linux, riscv32-linux, riscv64-linux, x86_64-linux

Usage

Once the binary is installed, you can modify your ~/.config/hypr/hyprland.conf.

The script accepts these parameters:

  • Sorting related

    • --reverse/-r Reverse the order of windows / switch backwards

    • --filter-same-class/-s Only switch between windows that have the same class/type as the currently focused window

    • --filter-current-workspace/-w Only switch between windows that are on the same workspace as the currently focused window

    • --filter-current-monitor/-m Only switch between windows that are on the same monitor as the currently focused window

    • --sort-recent Sort windows by most recently focused (when used with --daemon it will use the order of windows when the daemon was started)

    • --ignore-workspaces Sort all windows on every monitor like one contiguous workspace

    • --ignore-monitors Sort all windows on matching workspaces on monitors like one big monitor, workspaces must have offset of 10 for each monitor

  • GUI related

    • --daemon Starts as daemon, creates socket server and GUI, sends Command to the daemon if already running
    • --stop-daemon Stops the daemon, sends stop to socket server, doesn't execute current window switch, executes the command to switch window if --switch-on-close is true
    • --do-initial-execute Also execute the initial command when starting the daemon
    • --switch-ws-on-hover Switch to workspaces when hovering over them in GUI
    • --switch-on-close Execute the command to switch windows on close of daemon instead of switching for every command
    • --custom-css Path to a css file to add custom styles
  • --offset/-o Switch to a specific window offset (default 1)

  • --ignore-special-workspaces Hide special workspaces (e.g. scratchpad)

  • --dry-run/-d Print the command that would be executed

  • -v Increase the verbosity level (-vv ist max)

Here are some examples:

(Modify the $... variables to your liking)

No-GUI Config

Just use 2 keybindings to switch to 'next' or 'previous' window

$key = TAB
$modifier = CTRL
$reverse = SHIFT

bind = $modifier, $key, exec, hyprswitch
bind = $modifier $reverse, $key, exec, hyprswitch -r

No-GUI sort-recent Config

Just use 1 keybinding to switch to previously focused application

$key = TAB
$modifier = CTRL
$reverse = SHIFT

bind = $modifier, $key, exec, hyprswitch --sort-recent

Same class No-GUI Config

Just use 2 keybindings to switch to 'next' or 'previous' window of same class/type

$key = TAB
$modifier = CTRL
$reverse = SHIFT

bind = $modifier, $key, exec, hyprswitch -s
bind = $modifier $reverse, $key, exec, hyprswitch -s -r

GUI Config

Press $modifier + $key to open the GUI, use mouse to click on window

$key = TAB
$modifier = SUPER
$switch_release = SUPER_L

# open hyprswitch
bind = $modifier, $key, exec, hyprswitch --daemon

# close hyprswitch
bindr = $modifier, $switch_release, exec, hyprswitch --stop-daemon
# if it somehow doesn't close on releasing $switch_release, escape can kill
bindrn = ,escape, exec, pkill hyprswitch

GUI + Keyboard Config

Complex Config with submap to allow for many different keybindings when opening hyprswitch ( run hyprctl dispatch submap reset if stuck in switch submap)

  • Press (and hold) $modifier + $key to open the GUI and switch trough window
  • Release $key and press 3 to switch to the third next window
  • Release $key and press/hold $reverse + $key to traverse in reverse order
  • Release $modifier ($modifier_release) to execute the switch and close the gui
$key = TAB
$modifier = ALT
$modifier_release = ALT_L
$reverse = SHIFT

# allows repeated switching with same keypress that starts the submap
binde = $modifier, $key, exec, hyprswitch --daemon --do-initial-execute
bind = $modifier, $key, submap, switch

# allows repeated switching with same keypress that starts the submap
binde = $modifier $reverse, $key, exec, hyprswitch --daemon --do-initial-execute -r
bind = $modifier $reverse, $key, submap, switch

submap = switch
# allow repeated window switching in submap (same keys as repeating while starting)
binde = $modifier, $key, exec, hyprswitch --daemon
binde = $modifier $reverse, $key, exec, hyprswitch --daemon -r

# switch to specific window offset
bind = $modifier, 1, exec, hyprswitch --daemon --offset=1
bind = $modifier, 2, exec, hyprswitch --daemon --offset=2
bind = $modifier, 3, exec, hyprswitch --daemon --offset=3
bind = $modifier, 4, exec, hyprswitch --daemon --offset=4
bind = $modifier, 5, exec, hyprswitch --daemon --offset=5

bind = $modifier $reverse, 1, exec, hyprswitch --daemon --offset=1 -r
bind = $modifier $reverse, 2, exec, hyprswitch --daemon --offset=2 -r
bind = $modifier $reverse, 3, exec, hyprswitch --daemon --offset=3 -r
bind = $modifier $reverse, 4, exec, hyprswitch --daemon --offset=4 -r
bind = $modifier $reverse, 5, exec, hyprswitch --daemon --offset=5 -r


# exit submap and stop hyprswitch
bindrt = $modifier, $modifier_release, exec, hyprswitch --stop-daemon
bindrt = $modifier, $modifier_release, submap, reset

# if it somehow doesn't close on releasing $switch_release, escape can kill
bindr = ,escape, exec, pkill hyprswitch
bindr = ,escape, submap, reset
submap = reset

Rust Features

GUI functionality is included by default, but can be disabled with --no-default-features or enabled with --features gui when installing via cargo

if the gui should use libadwaita pass --features libadwaita to the cargo install command

Sorting of windows

See tests for more details on how windows get sorted

   1      2  3      4
1  +------+  +------+
2  |  1   |  |  2   |
3  |      |  +------+
4  +------+  +------+
5  +------+  |  4   |
6  |  3   |  |      |
7  +------+  +------+
   1      2  3      4
                  Monitor 1
      Workspace 1           Workspace 2
1  +------+  +------+ | +------+  +------+
2  |  1   |  |  2   |   |  5   |  |  6   |
3  |      |  |      | | |      |  +------+
4  +------+  +------+   +------+  +------+
5  +------+  +------+ | +------+  |  8   |
6  |  3   |  |  4   |   |  7   |  |      |
7  +------+  +------+ | +------+  +------+
   1      2  3      4   1      2  3      4
      1       3    5   6     8   10  11  12
   +----------------------------------------+
1  |  +-------+                      +---+  |
2  |  |   1   |              +---+   | 5 |  |
3  |  |       |    +---+     | 3 |   |   |  |
4  |  +-------+    | 2 |     +---+   |   |  |
5  |               +---+     +---+   |   |  |
6  |                         | 4 |   |   |  |
7  |    +-------+            +---+   +---+  |
8  |    |   6   |         +----+            |
9  |    |       |         | 7  |            |
10 |    +-------+         +----+            |
   +----------------------------------------+
        2       4         7    9

CSS

Class used:

  • client-image

    .client-image {
      margin: 15px;
    }
    
  • client-index

    .client-index {
      margin: 6px;
      padding: 5px;
      font-size: 30px;
      font-weight: bold;
      border-radius: 15px;
      border: 3px solid rgba(130, 130, 180, 0.4);
      background-color: rgba(20, 20, 20, 0.99);
    }
    
  • client + client_active

    client_active is the client that is currently focused / will be focused when exiting hyprswitch

    .client {
      border-radius: 15px;
      border: 3px solid rgba(130, 130, 180, 0.4);
      background-color: rgba(20, 20, 25, 0.85);
    }
    .client:hover {
      background-color: rgba(30, 30, 30, 0.99);
    }
    .client_active {
      border: 3px solid rgba(239, 9, 9, 0.94);
    }
    
  • workspace_frame + workspace_frame_special

    workspace_frame_special is added when workspaceId is < 0 (e.g., scratchpad)

    .workspace {
      font-size: 25px;
      font-weight: bold;
      border-radius: 15px;
      border: 3px solid rgba(80, 80, 80, 0.4);
      background-color: rgba(20, 20, 25, 0.85);
    }
    .workspace_special {
      border: 3px solid rgba(0, 255, 0, 0.4);
    }
    
  • workspaces

    .workspaces {
      margin: 10px;
    }
    
  • window

    window {
      border-radius: 15px;
      opacity: 0.9;
      border: 6px solid rgba(0, 0, 0, 0.4);
    }
    

Complete config:

.client-image {
    margin: 15px;
}
.client-index {
    margin: 6px;
    padding: 5px;
    font-size: 30px;
    font-weight: bold;
    border-radius: 15px;
    border: 3px solid rgba(130, 130, 180, 0.4);
    background-color: rgba(20, 20, 20, 0.99);
}
.client {
    border-radius: 15px;
    border: 3px solid rgba(130, 130, 180, 0.4);
    background-color: rgba(20, 20, 25, 0.85);
}
.client:hover {
    background-color: rgba(30, 30, 30, 0.99);
}
.client_active {
    border: 3px solid rgba(239, 9, 9, 0.94);
}
.workspace {
    font-size: 25px;
    font-weight: bold;
    border-radius: 15px;
    border: 3px solid rgba(80, 80, 80, 0.4);
    background-color: rgba(20, 20, 25, 0.85);
}
.workspace_special {
    border: 3px solid rgba(0, 255, 0, 0.4);
}
.workspaces {
    margin: 10px;
}
window {
    border-radius: 15px;
    opacity: 0.9;
    border: 6px solid rgba(0, 0, 0, 0.4);
}

Example:

.client_active {
    border: 3px solid rgba(239, 9, 9, 0.94);
    background-color: rgba(200, 9, 9, 0.80);
}

Other

Ignore monitors flag

This flag requires that workspaces have an offset of 10 for each monitor. (TODO, make this configurable)

This means that if you have 2 monitors, the workspaces on the second monitor must start at 11 if the first workspace on the first monitor is 1 to allow the scrip to map the correct workspaces together.

this can be configured in ~/.config/hypr/hyprland.conf (https://wiki.hyprland.org/Configuring/Workspace-Rules/)

--ignore-workspaces

  • Order without --ignore-workspaces
                   Monitor 1                                   Monitor 2
       Workspace 0           Workspace 1           Workspace 10          Workspace 11
 1  +------+  +------+ | +------+  +------+  |  +------+  +------+ | +------+  +------+
 2  |  1   |  |  2   | | |  5   |  |  6   |  |  |  9   |  |  10  | | |  13  |  |  14  |
 3  |      |  |      | | |      |  +------+  |  |      |  |      | | |      |  +------+
 4  +------+  +------+ | +------+  +------+  |  +------+  +------+ | +------+  +------+
 5  +------+  +------+ | +------+  |  8   |  |  +---------+  +---+ | +------+  |  16  |
 6  |  3   |  |  4   | | |  7   |  |      |  |  |   11    |  |12 | | |  15  |  |      |
 7  +------+  +------+ | +------+  +------+  |  +---------+  +---+ | +------+  +------+
    1      2  3      4   1      2  3      4     5      6  7  8   9   5      6  7   8  9
  • Order with --ignore-workspaces
                   Monitor 1                                   Monitor 2
       Workspace 0           Workspace 1           Workspace 10         Workspace 11
 1  +------+  +------+ | +------+  +------+  |  +------+  +------+ | +------+  +------+
 2  |  1   |  |  2   | | |  3   |  |  4   |  |  |  9   |  |  10  | | |  11  |  |  12  |
 3  |      |  |      | | |      |  +------+  |  |      |  |      | | |      |  +------+
 4  +------+  +------+ | +------+  +------+  |  +------+  +------+ | +------+  +------+
 5  +------+  +------+ | +------+  |  8   |  |  +---------+  +---+ | +------+  |  16  |
 6  |  5   |  |  6   | | |  7   |  |      |  |  |   13    |  |14 | | |  15  |  |      |
 7  +------+  +------+ | +------+  +------+  |  +---------+  +---+ | +------+  +------+
    1      2  3      4   1      2  3      4     5      6  7  8   9   5      6  7   8  9

--ignore-monitors

  • Order without --ignore-monitors
                   Monitor 1                                   Monitor 2
       Workspace 0           Workspace 1           Workspace 10          Workspace 11
 1  +------+  +------+ | +------+  +------+  |  +------+  +------+ | +------+  +------+
 2  |  1   |  |  2   | | |  5   |  |  6   |  |  |  9   |  |  10  | | |  13  |  |  14  |
 3  |      |  |      | | |      |  +------+  |  |      |  |      | | |      |  +------+
 4  +------+  +------+ | +------+  +------+  |  +------+  +------+ | +------+  +------+
 5  +------+  +------+ | +------+  |  8   |  |  +---------+  +---+ | +------+  |  16  |
 6  |  3   |  |  4   | | |  7   |  |      |  |  |   11    |  |12 | | |  15  |  |      |
 7  +------+  +------+ | +------+  +------+  |  +---------+  +---+ | +------+  +------+
    1      2  3      4   1      2  3      4     5      6  7  8   9   5      6  7   8  9
  • Order with --ignore-monitors
                   Monitor 1                                   Monitor 2
       Workspace 0           Workspace 1           Workspace 10          Workspace 11
 1  +------+  +------+ | +------+  +------+  |  +------+  +------+ | +------+  +------+
 2  |  1   |  |  2   | | |  9   |  |  10  |  |  |  3   |  |  4   | | |  11  |  |  12  |
 3  |      |  |      | | |      |  +------+  |  |      |  |      | | |      |  +------+
 4  +------+  +------+ | +------+  +------+  |  +------+  +------+ | +------+  +------+
 5  +------+  +------+ | +------+  |  14  |  |  +---------+  +---+ | +------+  |  16  |
 6  |  5   |  |  6   | | |  13  |  |      |  |  |   7     |  | 8 | | |  15  |  |      |
 7  +------+  +------+ | +------+  +------+  |  +---------+  +---+ | +------+  +------+
    1      2  3      4   1      2  3      4     5      6  7  8   9   5      6  7  8   9

Experimental Environment Variables

  • SIZE_FACTOR i16 [default: 7]: Factor window and workspace size get divided by to shrink them
  • ICON_SIZE i32 [default: 128]: Argument passed to the theme.lookup_icon function (Determines the resolution of the Icon, as it gets scaled to the windowsize regardless of the resolution of the icon)
  • ICON_SCALE i32 [default: 1]: Argument passed to the theme.lookup_icon function (IDK what this does, setting it to anything other than 1 changes nothing)
  • NEXT_INDEX_MAX i32 [default: 5]: Maximum number of windows to display the next index for (can be used to show the next index for the first 5 windows if you have -u bindings for the next/last 5 windows). Setting it to -1 will disable the next index indicator
  • EXIT_ON_CLICK bool [default: true]: Exit the GUI when clicking on a window
  • WORKSPACE_GAP usize [default: 15]: Gap between workspaces in the GUI (cant be configured via CSS as the workspace positions are calculated from the real workspace positions)

Dependencies

~8–23MB
~334K SLoC