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 start the daemon once at the start of Hyprland with exec-once = hyprswitch init &
in your
config.
Subsequent calls to hyprswitch (with the gui
command) will send the command to the daemon which will execute the
command and update the GUI.
- gtk4 and gtk4-layer-shell must be installed
cargo install hyprswitch
paru -S hyprswitch
/yay -S hyprswitch
- add
hyprswitch.url = "github:h3rmt/hyprswitch/release";
to flake inputs - add
specialArgs = { inherit inputs; };
tonixpkgs.lib.nixosSystem
- add
inputs.hyprswitch.packages.x86_64-linux.default
to yourenvironment.systemPackages
- available systems:
aarch64-linux
,i686-linux
,riscv32-linux
,riscv64-linux
,x86_64-linux
Once the binary is installed, you can modify your ~/.config/hypr/hyprland.conf
.
-
--dry-run
/-d
Print the command that would be executed -
-v
Increase the verbosity level (max: -vv) -
init
Initialize and start the Daemon--switch-ws-on-hover
Switch to workspaces when hovering over them in the GUI--stay-open-on-close
Don't close GUI when clicking on client (only close withhyprswitch close
)--custom-css
Specify a path to custom css file--show-title
Show the window title in the GUI (fallback to class if title is empty)
-
simple
Switch without using the GUI / Daemon (switches directly)--reverse
/-r
Reverse the order of windows / switch backwards--offset
/-o
Switch to a specific window offset (default 1)--include-special-workspaces
Include special workspaces (e.g., scratchpad)--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--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
-
gui
Starts/Opens the GUI + sends the command to daemon of GUI is already opened--do-initial-execute
If the GUI isn't open, also execute the first switch immediately, otherwise just open the GUI- Same options as
simple
-
close
Close the GUI, executes the command to switch window--kill
Don't switch to the selected window, just close the GUI
(Modify the $... variables to use the keys you prefer)
# 2 Keybindings to switch to 'next' or 'previous' window
$key = TAB
$modifier = CTRL
$reverse = SHIFT
bind = $modifier, $key, exec, hyprswitch simple
bind = $modifier $reverse, $key, exec, hyprswitch simple -r
# 1 Keybinding to switch to previously focused application
$key = TAB
$modifier = CTRL
bind = $modifier, $key, exec, hyprswitch simple --sort-recent
# 2 Keybindings to switch to next' or 'previous' window of same class/type
$key = TAB
$modifier = CTRL
$reverse = SHIFT
bind = $modifier, $key, exec, hyprswitch simple -s
bind = $modifier $reverse, $key, exec, hyprswitch simple -s -r
Add exec-once = hyprswitch init &
to your ~/.config/hypr/hyprland.conf
to start the daemon on startup
exec-once = hyprswitch init --show-title &
......
$key = TAB
$modifier = SUPER
$switch_release = SUPER_L
# open hyprswitch
bind = $modifier, $key, exec, hyprswitch gui
# close hyprswitch
bindr = $modifier, $switch_release, exec, hyprswitch close
# if it somehow doesn't close on releasing $switch_release, escape can kill (doesnt switch)
bindrn = ,escape, exec, hyprswitch close --kill
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 windows
- 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
exec-once = hyprswitch init --show-title &
......
$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 gui --do-initial-execute
bind = $modifier, $key, submap, switch
# allows repeated switching with same keypress that starts the submap
binde = $modifier $reverse, $key, exec, hyprswitch gui --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 gui
binde = $modifier $reverse, $key, exec, hyprswitch gui -r
# switch to specific window offset (TODO replace with a more dynamic solution)
bind = $modifier, 1, exec, hyprswitch gui --offset=1
bind = $modifier, 2, exec, hyprswitch gui --offset=2
bind = $modifier, 3, exec, hyprswitch gui --offset=3
bind = $modifier, 4, exec, hyprswitch gui --offset=4
bind = $modifier, 5, exec, hyprswitch gui --offset=5
bind = $modifier $reverse, 1, exec, hyprswitch gui --offset=1 -r
bind = $modifier $reverse, 2, exec, hyprswitch gui --offset=2 -r
bind = $modifier $reverse, 3, exec, hyprswitch gui --offset=3 -r
bind = $modifier $reverse, 4, exec, hyprswitch gui --offset=4 -r
bind = $modifier $reverse, 5, exec, hyprswitch gui --offset=5 -r
# exit submap and stop hyprswitch
bindrt = $modifier, $modifier_release, exec, hyprswitch close
bindrt = $modifier, $modifier_release, submap, reset
# if it somehow doesn't close on releasing $switch_release, escape can kill (doesnt switch)
bindr = ,escape, exec, hyprswitch close --kill
bindr = ,escape, submap, reset
submap = reset
-
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(80, 90, 120, 0.80); background-color: rgba(20, 20, 20, 1); }
-
client + client_active
client_active is the client that is currently focused / will be focused when exiting hyprswitch
-
workspace_frame + workspace_frame_special
workspace_frame_special is added when workspaceId is < 0 (e.g., scratchpad)
-
workspaces
.workspaces { margin: 10px; }
-
window
window { border-radius: 15px; opacity: 0.85; border: 6px solid rgba(15, 170, 190, 0.85); }
.client-image {
margin: 15px;
}
.client-index {
margin: 6px;
padding: 5px;
font-size: 30px;
font-weight: bold;
border-radius: 15px;
border: 3px solid rgba(80, 90, 120, 0.80);
background-color: rgba(20, 20, 20, 1);
}
.client {
border-radius: 15px;
border: 3px solid rgba(80, 90, 120, 0.80);
background-color: rgba(25, 25, 25, 0.90);
}
.client:hover {
background-color: rgba(40, 40, 50, 1);
}
.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(70, 80, 90, 0.80);
background-color: rgba(20, 20, 25, 0.90);
}
.workspace_special {
border: 3px solid rgba(0, 255, 0, 0.4);
}
.workspaces {
margin: 10px;
}
window {
border-radius: 15px;
opacity: 0.85;
border: 6px solid rgba(17, 171, 192, 0.85);
}
.client_active {
border: 3px solid rgba(239, 9, 9, 0.94);
background-color: rgba(200, 9, 9, 0.80);
}
.client-image {
margin: 10px;
}
window {
opacity: 1;
border: 6px solid rgba(0, 0, 0, 0.85);
}
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
- 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
- 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
SIZE_FACTOR
i16 [default: 7]: Factor window and workspace size get divided by to shrink themICON_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 indicatorWORKSPACES_PER_ROW
u32 [default: 5]: Number of workspaces per row in the GUI