mirror of
https://github.com/levnikmyskin/hyprland-virtual-desktops.git
synced 2024-07-14 15:00:24 +03:00
327 lines
18 KiB
Markdown
327 lines
18 KiB
Markdown
# Virtual desktops for Hyprland ![hyprico](.github/hyprland.ico)
|
|
`virtual-desktops` is a plugin for the [Hyprland](https://github.com/hyprwm/Hyprland) compositor. `virtual-desktops` manages multiple screens workspaces as if they were a single virtual desktop.
|
|
|
|
This plugin **only supports official releases of Hyprland** (e.g., v0.39.x, v0.40.x).
|
|
If you are on `hyprland-git`, please try compiling this plugin from the [dev branch](https://github.com/levnikmyskin/hyprland-virtual-desktops/tree/dev).
|
|
There is **NO GUARANTEE** that the plugin will compile succesfully on the latest Hyprland commit, but we try our best to keep it updated. Also, always check the [PR section](https://github.com/levnikmyskin/hyprland-virtual-desktops/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-desc),
|
|
as there might be a draft PR for the next Hyprland release, where you can check the status of development.
|
|
|
|
|
|
Feel free to join our [matrix room](https://matrix.to/#/#hypr-virtual-desktops:matrix.org)!
|
|
|
|
## Table of contents
|
|
- [Virtual desktops for Hyprland ](#virtual-desktops-for-hyprland-)
|
|
- [Table of contents](#table-of-contents)
|
|
- [What is this exactly?](#what-is-this-exactly)
|
|
- [How does this work?](#how-does-this-work)
|
|
- [It's just workspaces, really](#its-just-workspaces-really)
|
|
- [Hyprctl dispatchers](#hyprctl-dispatchers)
|
|
- [Mix with Hyprland native workspaces](#mix-with-hyprland-native-workspaces)
|
|
- [Hyprctl commands](#hyprctl-commands)
|
|
- [Hyprland keywords](#hyprland-keywords)
|
|
- [Syntax](#syntax)
|
|
- [Examples](#examples)
|
|
- [Configuration values](#configuration-values)
|
|
- [Example config](#example-config)
|
|
- [Layouts](#layouts)
|
|
- [Example](#example)
|
|
- [Layouts are cached and restored if you disconnect/reconnect monitors](#layouts-are-cached-and-restored-if-you-disconnectreconnect-monitors)
|
|
- [Example](#example-1)
|
|
- [Choosing how to remember, or choosing to forget](#choosing-how-to-remember-or-choosing-to-forget)
|
|
- [Example](#example-2)
|
|
- [Install](#install)
|
|
- [Installing on NixOS with home—manager](#installing-on-nixos-with-homemanager)
|
|
- [Help, Hyprland is being weird!](#help-hyprland-is-being-weird)
|
|
- [It's actually the plugin 😱](#its-actually-the-plugin-)
|
|
- [Thanks to](#thanks-to)
|
|
|
|
|
|
## What is this exactly?
|
|
|
|
In Hyprland, each screen has its own set of workspaces. For instance, say you have two monitors, with workspace 1 on screen 1
|
|
and workspace 2 on screen 2:
|
|
- When you switch from workspace 1 to 2, Hyprland will simply focus your second screen;
|
|
- If you switch to workspace 3, your active screen will go to workspace 3, whereas the other screen will stay on whichever workspace it is currently on.
|
|
|
|
You may think of a virtual desktop, instead, as a "single"
|
|
workspace which extends across your screens (even though, internally, you will still have _n_ different workspaces on your _n_ monitors). If you've ever used KDE Plasma (or Gnome, I think) with
|
|
multiple screens, this plugin basically replicates that
|
|
functionality.
|
|
Taking the previous example:
|
|
- You will be on virtual desktop 1. Let's say you open your web browser on your first screen and an IDE on your second screen;
|
|
- When you switch to virtual desktop 2, both screens will switch to empty workspaces. Let's say here you open your email client and your favourite chat application;
|
|
- If you switch back to virtual desktop 1, you will get back your web browser and the IDE on screen 1 and 2; and viceversa when you go back to virtual desktop 2.
|
|
|
|
## How does this work?
|
|
|
|
### It's just workspaces, really
|
|
Internally, this simply ties _n_ workspaces to your _n_ screens, for each virtual desktop. That is, on virtual desktop 1 you will have workspace 1 on screen 1 and workspace 2 on screen 2;
|
|
on virtual desktop 2, you will have workspace 3 on screen 1 and workspace 4 on screen 2, and so on.
|
|
|
|
However, if you focus another workspace on a given virtual desktop, the plugin will remember this and you will keep this layout (see [Layouts](#Layouts)).
|
|
|
|
**Notice**: screen 1 and screen 2 are not necessarily what you expect your first and second screen to be, e.g., screen 1 is not necessarily your left screen, and screen 2 is not necessarily your right screen.
|
|
|
|
### Hyprctl dispatchers
|
|
|
|
This plugin exposes a few hyprctl dispatchers:
|
|
|
|
| Dispatcher | description | type | example|
|
|
|------------|-------------|------|--------|
|
|
| vdesk [vdesk] | Changes to virtual desktop `vdesk` | see below | `vdesk 3` or `vdesk coding`|
|
|
| lastdesk | Changes to last visited virtual desktop | `none` | `lastdesk`|
|
|
| movetodesk vdesk(, window) | Moves the active/selected window to the specified `vdesk` | `vdesk`, optional window, see below | `movetodesk 2` or `movetodesk 2,title:kitty` |
|
|
| movetodesksilent vdesk(, window) | same as `movetodesk`, but doesn't switch to desk | same as above | same as above |
|
|
| movetolastdesk (window) | Moves the active/selected window to the last visited `vdesk` | optional window | `movetolastdesk` or `movetolastdesk title:kitty` |
|
|
| movetolastdesksilent (window) | same as `movetolastdesk`, but doesn't switch to desk | same as above | same as above |
|
|
| movetoprevdesk (cycleback)(,window) | Moves the active/selected window to the previous `vdesk`. If `cycleback == 1 and vdesk == 1`, moves to vdesk with max id | optional 1 for true, optional window | `movetoprevdesk 1` or `movetodesk ,title:kitty` |
|
|
| movetoprevdesksilent (cycleback)(,window) | same as `movetoprevdesk`, but doesn't switch to desk | same as above | same as above |
|
|
| movetonextdesk (cycle)(, window) | Moves the active/selected window to the next `vdesk`. If `cycle == 1 and vdesk == max`, moves to vdesk 1 | optional 1 for true, optional window | `movetonextdesk 1` or `movetonextdesk ,title:kitty` |
|
|
| movetonextdesksilent (cycle)(, window) | same as `movetonextdesk`, but doesn't switch to desk | same as above | same as above |
|
|
| vdeskreset (vdesk) | reset layouts on `vdesk` or on all vdesks if no argument is given (see [Layouts](#Layouts)) | optional vdesk, see below | `vdeskreset` or `vdeskreset 2` or `vdeskreset coding` |
|
|
| prevdesk | go to previous vdesk. If less than 1, will simply execute `vdesk 1` | `none` | `prevdesk` |
|
|
| nextdesk | go to next vdesk. Creates it if it doesn't exist | `none` | `nextdesk` |
|
|
| backcyclevdesks | backward cycle between currently existing vdesks. Goes back to vdesk with max id when at vdesk 1 | `none` | `backcyclevdesks` |
|
|
| cyclevdesks | cycle between currently existing vdesks. Goes back to vdesk 1 if next vdesk does not exist | `none` | `cyclevdesks` |
|
|
|
|
|
|
> BREAKING v2.1.0: `prevdesk` dispatcher was renamed to `lastdesk`. `prevdesk` has a new functionality: it goes to the previous desk. If you were using `prevdesk`, please update your config.
|
|
|
|
For `vdesk` names, you can use:
|
|
- ID: e.g., `1`, `2` etc;
|
|
- Name: e.g., `coding`, `internet`, `mail and chats`
|
|
|
|
If a `vdesk` with a given ID or name does not exist, it'll be created on the fly. If you give a (non configured, see [below](#configuration-values))
|
|
name, it will be assigned to the next available vdesk id: the virtual-desktops
|
|
plugin will remember this association even if Hyprland kills the related workspaces.
|
|
|
|
The `movetodesk` and `movetodesksilent` dispatchers work similarly to
|
|
Hyprland's `movetoworkspace` and `movetoworkspacesilent` dispatchers. See [Hyprland's wiki](https://wiki.hyprland.org/Configuring/Dispatchers/#list-of-dispatchers). Of course, make sure to use the `vdesk` syntax above instead of Hyprland's.
|
|
|
|
#### Mix with Hyprland native workspaces
|
|
You can use `hyprctl dispatch vdesk n`, even if you have
|
|
no secondary screen connected at the moment (the behaviour would be identical to native workspaces). Also, I would REMOVE
|
|
any workspace related configuration, such as `wsbind`. If you want to leverage [workspace-specific rules](https://wiki.hyprland.org/Configuring/Workspace-Rules/), you can: workspaces are always assigned
|
|
to the same vdesk given the same number of monitors, unless you focus (e.g. with hyprctl) another workspace (see [Layouts](#Layouts)). For instance:
|
|
- Given two monitors:
|
|
- vdesk 1 has workspaces 1 and 2;
|
|
- vdesk 2 has workspaces 3 and 4, and so on;
|
|
- Given three monitors:
|
|
- vdesk 1 has workspaces 1, 2 and 3;
|
|
- vdesk 2 has workspaces 4, 5 and 6, and so on.
|
|
- Given four monitors...
|
|
|
|
The vdesk a workspace will end up to is easily computed by doing `ceil(workspace_id / n_monitors)`. You know where I'm going with this one...you can easily script it.
|
|
|
|
### Hyprctl commands
|
|
Since version 2.2, this plugin exposes a couple of `hyprctl` commands. That is, you can use them by calling `hyprctl {command} {args}`.
|
|
**NOTICE**: some of these used to be dispatchers.
|
|
|
|
| Command | description | args | example|
|
|
|------------|-------------|------|--------|
|
|
| printdesk (vdesk)| Prints to Hyprland log the specified vdesk or the currently active vdesk* (if no argument is given) | optional vdesk, see [above](#hyprctl-dispatchers) | `hyprctl printdesk` or `hyprctl printdesk 2` or `hyprctl printdesk coding`|
|
|
| printstate | Prints state of all vdesks | `none` | `hyprctl printstate` |
|
|
| printlayout | print to Hyprland logs the current layout | `none` | `hyprctl printlayout` |
|
|
|
|
|
|
### Hyprland keywords
|
|
Since version 2.2, this plugin exposes one keyword: `stickyrule`.
|
|
A sticky rule is composed of a window identifier and a vdesk identifier.
|
|
A window matched by a sticky rule will be moved to the matched vdesk:
|
|
1. When the window is created (similar to [Hyprland's `workspace` windowrule](https://wiki.hyprland.org/Configuring/Window-Rules/#window-rules-v2), but with virtual desks);
|
|
2. Every time a monitor is connected/disconnected.
|
|
|
|
**BE CAREFUL**:
|
|
1. **NOT** to mix this with Hyprland's `workspace` windowrule (it wouldn't make sense right?);
|
|
2. This is not a plugin config, but an Hyprland keyword. Place it in the top level of Hyprland's config (i.e., where you'd put windowrules too).
|
|
|
|
#### Syntax
|
|
```bash
|
|
stickyrule = window,vdesk
|
|
```
|
|
- `window` identifier has the same syntax as [Hyprland's windowrule window](https://wiki.hyprland.org/Configuring/Window-Rules/#window-rules-v2) identifier;
|
|
- `vdesk` identifier has the same syntax specified above.
|
|
|
|
#### Examples
|
|
`stickyrule = class:^(kittysticky)$,3`
|
|
`stickyrule = title:thunderbird,mail`
|
|
|
|
|
|
|
|
### Configuration values
|
|
|
|
This plugin exposes a few configuration options, under the `plugin:virtual-desktops:` category, namely:
|
|
|
|
| Name | description | type | example|
|
|
|------|-------------|------|--------|
|
|
| names | map a vdesk id with a name | map[int:string], see below| `names = 1:coding, 2:internet, 3:mail and chats`|
|
|
| cycleworkspaces | if set to 1 and switching to the currently active vdesk, workspaces will be swapped between your monitors (see [swapactiveworkspaces](https://wiki.hyprland.org/Configuring/Dispatchers/#list-of-dispatchers))| `0` or `1`| `cycleworkspaces = 1`|
|
|
| rememberlayout | chooses how layouts should be remembered (see [Layouts](#Layouts)), defaults to `size` | `none`, `size` or `monitors` | `remember = size` |
|
|
| notifyinit | chooses whether to display the startup notification, defaults to 1 | `0` or `1` | `notifyinit = 0` |
|
|
| verbose_logging | whether to log more stuff, defaults to 0 | `0` or `1` | `verbose_logging = 0` |
|
|
|
|
* The `names` config option maps virtual desktop IDs to a name (you can then use this with the hyprctl [dispatchers](#hyprctl-dispatchers));
|
|
* `cycleworkspaces`: THIS CURRENTLY DOES NOT WORK WITH MORE THAN 2 MONITORS. If you need this feature, please feel welcome to submit a PR ^^.
|
|
|
|
|
|
#### Example config
|
|
```ini
|
|
stickyrule = class:^(kittysticky)$,3
|
|
stickyrule = title:thunderbird,mail
|
|
|
|
plugin {
|
|
virtual-desktops {
|
|
names = 1:coding, 2:internet, 3:mail and chats
|
|
cycleworkspaces = 1
|
|
rememberlayout = size
|
|
notifyinit = 0
|
|
verbose_logging = 0
|
|
}
|
|
}
|
|
```
|
|
|
|
## Layouts
|
|
Version 2.0 of this plugin introduced the concept of a *layout*, with the meaning of "a specific combination of workspaces on a (more or less) specific combination of monitors".
|
|
In other words, `virtual-desktops` remembers if you focused another workspace on your vdesk, even if you switch to another vdesk and then come back to this one
|
|
|
|
#### Example
|
|
Say you have 2 monitors A and B, and you're on vdesk 1:
|
|
- On monitor A you have workspace 1, and on monitor B you have workspace 2;
|
|
- Now, say you focus workspace 4 with `hyprctl dispatch workspace 4` on monitor B.
|
|
- If you switch to vdesk 2 and back to vdesk 1, you will see workspace 4 on monitor B instead of workspace 2.
|
|
|
|
**Notice** that, in this case, workspace 4 would also be shown on vdesk 2.
|
|
|
|
### Layouts are cached and restored if you disconnect/reconnect monitors
|
|
Internally, every vdesk will cache all the previous layouts. Once a monitor is connected/disconnected, the plugin will try to look for a previously existing layout for this configuration
|
|
and it will apply it.
|
|
|
|
#### Example
|
|
Continuing from the previous example, say you now disconnect monitor B and then you reconnect it, while being on vdesk 1:
|
|
- the previous layout will be matched and you will get back workspace 1 on monitor A and workspace 4 on monitor B.
|
|
|
|
This would work also if instead of disconnecting and reconnecting monitor B, you connected a third monitor C and then disconnect it.
|
|
|
|
|
|
### Choosing how to remember, or choosing to forget
|
|
Version 2.0 also introduced the `rememberlayout` config option: with this option, we can choose if we want to match a layout by number of monitors (`size`) or by unique monitor descriptions (`monitors`). There is also the third and final option to not remember layouts at all (`none`).
|
|
|
|
|
|
#### Example
|
|
Continuing from the previous example. Say we now disconnect monitor B and connect monitor C: our connected monitors are A and C.
|
|
|
|
- if `rememberlayout = size` (the default), the existing layout will still be matched, we will have workspace 1 on monitor A, workspace 4 on monitor C;
|
|
- if `rememberlayout = monitors`, a new layout will be created with defaults: workspace 1 on monitor A, workspace 2 on monitor C;
|
|
- if `rememberlayout = none`, same as above.
|
|
|
|
If we now disconnect monitor C and reconnect monitor B: our connected monitors are A and B.
|
|
|
|
- if `rememberlayout = size` (the default), the existing layout will be matched, we will have workspace 1 on monitor A, workspace 4 on monitor B;
|
|
- if `rememberlayout = monitors`, the existing layout will be matched, we will have workspace 1 on monitor A, workspace 4 on monitor B;
|
|
- if `rememberlayout = none`, a new layout will be created with defaults: workspace 1 on monitor A, workspace 2 on monitor C.
|
|
|
|
|
|
## Install
|
|
In order to use plugins, you should compile Hyprland yourself. See [Hyprland Wiki#Using Plugins](https://wiki.hyprland.org/Plugins/Using-Plugins/).
|
|
|
|
You can use:
|
|
```bash
|
|
HYPRLAND_HEADERS=path/to/hyprlandrepo make all
|
|
```
|
|
this will compile and copy the compiled `.so` plugin in the `$HOME/.local/share/hyprload/plugins/bin` path (useful if you use [hyprload](https://github.com/Duckonaut/hyprload)).
|
|
You can also use `make virtual-desktops.so` to output the compiled plugin in the repo directory.
|
|
|
|
Once compiled, you can tell Hyprland to load the plugin as described in the [Hyprland wiki](https://wiki.hyprland.org/Plugins/Using-Plugins/#installing--using-plugins).
|
|
|
|
|
|
### Installing on NixOS with home—manager
|
|
|
|
Here is an example flake that you can modify to add hyprland-virtual-desktops to your configuration
|
|
```nix
|
|
# flake.nix
|
|
|
|
{
|
|
inputs = {
|
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
|
|
home-manager = {
|
|
url = "github:nix-community/home-manager";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
};
|
|
|
|
hyprland = {
|
|
url = "git+https://github.com/hyprwm/Hyprland?submodules=1";
|
|
follows = "hyprland-virtual-desktops/hyprland"; # To make sure we run the same version of hyprland that the plugin was built against
|
|
};
|
|
hyprland-virtual-desktops.url = "github:levnikmyskin/hyprland-virtual-desktops";
|
|
|
|
};
|
|
|
|
outputs = { nixpkgs, home-manager, hyprland, hyprland-virtual-desktops, ... }:
|
|
let
|
|
system = "x86_64-linux";
|
|
pkgs = nixpkgs.legacyPackages.${system};
|
|
in
|
|
{
|
|
homeConfigurations."user@hostname" = home-manager.lib.homeManagerConfiguration {
|
|
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
|
|
|
# You can optionally move this module to its own .nix file and source it
|
|
# here if you want to modularise your configuration
|
|
modules = [
|
|
hyprland.homeManagerModules.default
|
|
{
|
|
wayland.windowManager.hyprland = {
|
|
enable = true;
|
|
package = hyprland.packages.${pkgs.system}.hyprland;
|
|
plugins = [
|
|
hyprland-virtual-desktops.packages.${pkgs.system}.virtual-desktops
|
|
];
|
|
# extraConfig is a string that becomes hyprland.conf
|
|
extraConfig = ''
|
|
stickyrule = class:^(kittysticky)$,3
|
|
stickyrule = title:thunderbird,mail
|
|
|
|
plugin {
|
|
virtual-desktops {
|
|
names = 1:coding, 2:internet, 3:mail and chats
|
|
cycleworkspaces = 1
|
|
rememberlayout = size
|
|
notifyinit = 0
|
|
verbose_logging = 0
|
|
}
|
|
}
|
|
'' + ''
|
|
# your other configuration for hyprland
|
|
'';
|
|
};
|
|
}
|
|
# ...
|
|
# NOTE:
|
|
# You will want to enable the Hyprland module in your NixOS configuration
|
|
# too, since that also enables critical components like xdg-desktop-portal,
|
|
# xwayland, polkit, etc
|
|
#
|
|
# # Have this somewhere in your NixOS configuration
|
|
# programs.hyprland = {
|
|
# enabled = true;
|
|
# package = inputs.hyprland.packages.${pkgs.system}.hyprland;
|
|
# };
|
|
];
|
|
};
|
|
};
|
|
}
|
|
```
|
|
|
|
## Help, Hyprland is being weird!
|
|
I've noticed that, sometimes, when disconnecting or reconnecting monitors, there might be weird artifacts or similar. Try running:
|
|
`hyprctl reload`
|
|
|
|
### It's actually the plugin 😱
|
|
If instead you're seeing weird behaviour with the plugin itself, remember you can always run:
|
|
`hyprtl dispatch vdeskreset`
|
|
|
|
## Thanks to
|
|
[split-workspaces](https://github.com/Duckonaut/split-monitor-workspaces/), from which I borrowed the Makefile,
|
|
and the general idea of how to write Hyprland plugins.
|