Waybar
Waybar copied to clipboard
[hyprland/workspaces] Implement workspace taskbars
Summary
This PR expands the functionality of hyprland/workspaces to make it behave more like the wlr/taskbar module. See #2656 for a high-level description.
A full fix of #2656 would require porting those changes to the sway/workspaces module as well. I don't use Sway and I'm not familiar on how it works, but if anyone is interested in giving it a go in a separate PR feel free to ping me and I'll do my best to help.
Config changes
Adds the following changes to the hyprland/workspaces config:
"hyprland/workspaces": {
// EXISTING SETTINGS
"format": "{icon}: {windows}", // Unchanged.
"format-window-separator": "", // Unchanged.
"window-rewrite-default": "", // Will be IGNORED if "workspace-taskbar.enable" is true
"window-rewrite": {}, // Will be IGNORED if "workspace-taskbar.enable" is true
// NEW SETTINGS
"workspace-taskbar": {
// Enable the workspace taskbar. Default: false
"enable": true,
// If true, the active window will have an `active` class. Could cause higher CPU usage due to more frequent redraws. Default: false
"update-active-window": true,
// Format of the windows in the taskbar. Default: "{icon}". Allowed variables: {icon}, {title}
"format": "{icon} {title:.20}",
// Icon size in pixels. Default: 16
"icon-size": 16,
// Either the name of an installed icon theme or an array of themes (ordered by priority). If not set, the default icon theme is used.
"icon-theme": "some_icon_theme",
// Orientation of the taskbar (see screenshots below). Default: "horizontal".
"orientation": "horizontal",
// List of regexes. A window will NOT be shown if its window class or title match one or more items. Default: []
"ignore-list": [ "code", "Firefox - .*" ],
// Command to run when a window is clicked. Default: "" (switch to the workspace as usual). Allowed variables: {address}, {button}
"on-click-window": "/some/arbitrary/script {address} {button}"
}
}
Notice how workspace-taskbar.enable defaults to false, so existing configs shouldn't be affected by these changes without opting in.
What's missing
I'm mainly waiting for your feedback on the config json names and structure. Once that's done I will update the wiki with the finalized spec.
Screenshots
Old config file (defaults to old, text-based icons). Using default CSS.
"hyprland/workspaces": {
"format": "{icon}: {windows}",
"format-window-separator": ",",
"window-rewrite-default": "?",
"window-rewrite": {
"terminator": "",
"code": ""
}
},
New config file (default format is just the icon). Using default CSS.
"hyprland/workspaces": {
"format": "{icon}: {windows}",
"workspace-taskbar": {
"enable": true
}
},
Example: windows with first 10 chars of title and bigger icon
"hyprland/workspaces": {
"format": "{icon}: {windows}",
"workspace-taskbar": {
"enable": true,
"format": "{icon} {title:.10}",
"icon-size": 18
}
},
Tooltip shows full window title.
- Note: The screenshot doesn't show my cursor, which was hovering the "Comparing" window.
Allows an arbitrary format
"hyprland/workspaces": {
"format": "[{icon}] (( {windows} ))",
"workspace-taskbar": {
"enable": true,
"format": " [{title:.5} | {icon}] "
}
},
My setup with custom CSS
- Background indicates selected workspace
Same CSS as above, vertical orientation
"hyprland/workspaces": {
"format": "{icon}: {windows}",
"workspace-taskbar": {
"enable": true,
"format": "{icon} {title:.20}",
"icon-size": 18,
"orientation": "vertical"
}
},
Action when clicking window
You can use the on-click-window config to set the command that will be executed when a specific window is clicked.
- The pattern
{address}will be replaced with the address of the clicked window. - The pattern
{button}will be replaced with the pressed button number. See GdkEventButton.button.
For example:
"on-click-window": "hyprctl dispatch focuswindow address:{address}"
I personally require a more complex logic, so I use on-click-window to call an external script.
@Alexays could you take a look at this PR?
@Alexays I've been using this feature daily for 2 months and haven't encountered any issues. Could you review this PR? It seems there are other people interested in this functionality.
@Alexays Please
@pol-rivero
Thanks for this PR! Is it possible to have such functionality in the improved hyprland/workspaces like this wlr/taskbar config below?
"wlr/taskbar" :
{
// "sort-by-app-id": "true"
"icon-size" : 16,
"icon-theme" : "Numix-Circle",
"on-click" : "activate",
"on-click-middle" : "maximize",
"on-click-right" : "close",
"tooltip-format" : "{title}"
}
@n-connect Yes, this config should be what you are looking for:
"hyprland/workspaces": {
"format": "{icon}: {windows}", // Change to your preferred format
"workspace-taskbar": {
"enable": true,
"format": "{icon} {title:.20}", // Change to your preferred format
"icon-size": 16,
"icon-theme": "Numix-Circle",
"on-click-window": "windowClick.sh {address} {button}"
}
}
Where windowClick.sh points to a script like this:
#!/bin/bash
address=$1
# https://api.gtkd.org/gdk.c.types.GdkEventButton.button.html
button=$2
if [ $button -eq 1 ]; then
# Left click: focus window. The cursor:no_warps lines are optional.
hyprctl keyword cursor:no_warps true
hyprctl dispatch focuswindow address:$address
hyprctl keyword cursor:no_warps false
elif [ $button -eq 2 ]; then
# Middle click: maximize window
# TODO: Use the corresponding hyprctl dispatch command. I don't know what would 'maximize' mean in a tiling window manager like hyprland
elif [ $button -eq 3 ]; then
# Right click: close window
hyprctl dispatch closewindow address:$address
fi
@pol-rivero,
Thanks for the full example, making local backup for test. Looking forward to this PR to come through. It really helps making a cleaner and smaller footprint - less occupied pixels on screen, by avoiding duplicated icons, and giving space for other possible stuff.
Another question, should this PR allow icon rewriting such as in the below pic? The 4 icons at right side of 1 ...
Yepp, this capability within wlr/taskbar is priceless - if one is looking maximizing possibility in an otherwise tiling WM - in an ultralight small screen laptop. I was not able to get work the fullscreenstate, 1 hyprland yet, but fullscreen binds only. Not the same as maximize :D
elif [ $button -eq 2 ]; then # Middle click: maximize window # TODO: Use the corresponding hyprctl dispatch command. I don't know what would 'maximize' mean in a tiling window manager like hyprland
@n-connect
The taskbar shows the app icon and therefore does not support rewriting (if workspace-taskbar.enable is set to true, the window-rewrite config will be ignored).
If you want to change the icon that is shown for a specific app, you can do the following:
- Find the
.desktopfile for that app. You can find it in one of these directories:~/.local/share/applications/usr/share/applications/usr/local/share/applications
- Open it with a text editor
- Find the line that starts with
Icon=and replace it with another icon from your theme. You can use a tool like nwg-icon-picker to find the available icon names.
@pol-rivero
Fair enough. Either possibility of getting the same icons as in war/taskbar, or one with your samples above is perfect.
Default CSS, new config file (default format is just the icon)
"hyprland/workspaces": { "format": "{icon}: {windows}", "workspace-taskbar": { "enable": true } },
I just need to git magic the latest numbered release, plus your PR into one local copy to do compile it. I hope I can find find my previous notes about cmus and its https stream PR :)
I just need to git magic the latest numbered release, plus your PR into one local copy to do compile it.
This PR is already up to date, you can just git clone https://github.com/pol-rivero/waybar.
Also let me know if you are using arch, then it's much easier to install just by modifying the PKGBUILD.
@pol-rivero,
Thx, not Arc, but Debian stable, only the nwg-menu has and Arc icon/char. Following your base git clone, and build gave the workspace taskbar. On the other hand its version is below the actual release number (fixme, it the build# still marks it as 0.12.0)
waybar -v
Waybar v0.11.0-153-ge541936d (branch 'master')
So I'll try the tag/version clone and PR checkout for rebuild later.
The look of the bar with your code, with the same cut as above:
Working parts of the script:
- button -eq 3 ->
hyprctl dispatch closewindow address:$address
For some reason the activation do not works:
- button -eq 1 ->
hyprctl dispatch focuswindow address:$address - after the normal mouse click the mouse jumps into the middle of the screen and seeable, that the focused window is a different one, but that one do not got on the top, tried to fix, no joy yet (see script below)
- my guess is that the
focuswindowask for awindowparameter instead of address
Edit: after a sleep/session lock, a new error emerged:
- all my clients (as hyprctl clients got hidden), however only one workspace (1) exist
- no workspace change has helped
- finally the wlr/taskbar maximise function could bring up the windows of the running [hyprland/hyprctl] clients.
I'm not sure if there a connection to this PR, just a background info
How should I get the address/window value for cli tests? Checked hyprctl clients output...
Can you please check out the below configs, what could be the issue in your opinion?
"hyprland/workspaces": {
"format": "{icon}: {windows}", // Change to your preferred format
"workspace-taskbar": {
"enable": true,
"format": "{icon}", // Change to your preferred format
"icon-size": 16,
"icon-theme": "Numix-Circle",
"on-click-window": "$HOME/.config/waybar/windowClick.sh {address} {button}"
}
},
Edit2: fixed typo below
#!/bin/bash
address=$1
# https://api.gtkd.org/gdk.c.types.GdkEventButton.button.html
button=$2
if [ $button -eq 1 ]; then
# Left click: focus window. The cursor:no_warps lines are optional.
#hyprctl keyword cursor:no_warps true
hyprctl dispatch focuswindow address:$address
hyprctl dispatch bringactivetotop
#hyprctl keyword cursor:no_warps false
elif [ $button -eq 2 ]; then
# Middle click: maximize window
# TODO: Use the corresponding hyprctl dispatch command. I don't know what would 'maximize' mean in a tiling window manager like hyprland
echo 'middle click'
elif [ $button -eq 3 ]; then
# Right click: close window
hyprctl dispatch closewindow address:$address
fi
Sorry, I don't understand what you mean. Is there a bug?
Sorry, I don't understand what you mean. Is there a bug?
Sorry, short version:
- The normal mouse click is not working. This supposed to bring a window into the front and focus it (or in reversed order does not matter). The focus removed from the original window/client, but the icon(its windows) I've clicked on, has not brought on top. Eg. it looks like nothing changed - but I can see the focus actually changed, but only just that. The window did not bringed up compared to
wlr/taskbarI'm using an ALT-Tab bind which quite similar, which serves the ALT-Tab purpose, but can't we used here unfortunally:
bind = ALT, Tab, cyclenext
bind = ALT, Tab, bringactivetotop
- The windows closing right click works.
These two above are related to the shellscript you've sent earlier.
- New interesting possible bug: all my windows "got hidden somewhere" within the workspace. The are not seeable in workspace 1, after lid close/sleep.
Hyprctl clientstells me they are on/in wp1. Not 100% sure waybar (and hpyrland workspace/taskbar) related, , although this never happened before, only since I've replaced the waybar with local build this moring. In the cli, where I've started waybar no separate errors.
This one is a completely generic one. I'll crosscheck with 0.12.0 packaged version if it happens again.
Ah I see. Since closing the window works, that means the script is being called correctly with the address and button number.
You seem to have a typo on your script: hyprctl dispatch bringactivetotp is missing an O: bringactivetotOp. That's probably why it isn't working.
I don't know why the windows are being hidden, this change should not affect it at all. Let me know if it also happens with the 0.12.0 base.
You seem to have a typo on your script:
hyprctl dispatch bringactivetotpis missing an O: bringactivetotOp. That's probably why it isn't working.
Good catch, thx! For a "fresh" client/window it works now. I'll fix in the previos one, if someone get here from Google/etc search have a working script...
I'm still in the same XDG session where the clients got hidden. One of them with many tabs, a kitty in "hidden phase". That kitty can be bringed on top, so there must be some other issue :)
Thx again.
Glad to help :)
@pol-rivero
Made another build with latest release version + this PR merged:
git clone --branch 0.12.0 https://github.com/Alexays/Waybar.git
cd Waybar;git pull origin pull/3868/head
As a result got Waybar v0.12.0-16-ge541936d (branch 'HEAD') .
Edit:
From now on I'm using it as normal w/o wlr/taskbar. Let you know if something comes up.
Final, fully working windowClick.sh:
#!/bin/bash
address=$1
# https://api.gtkd.org/gdk.c.types.GdkEventButton.button.html
button=$2
if [ $button -eq 1 ]; then
# Left click: focus window.
# The cursor:no_warps are optional. Set to not catch and move the cursor into the middle of screen as a result of "focuswindow"
hyprctl keyword cursor:no_warps true
hyprctl dispatch focuswindow address:$address
hyprctl dispatch bringactivetotop
hyprctl keyword cursor:no_warps false
elif [ $button -eq 2 ]; then
# Middle click: maximize window
hyprctl dispatch fullscreenstate 1
elif [ $button -eq 3 ]; then
# Right click: close window
hyprctl dispatch closewindow address:$address
fi
@pol-rivero
Because of missing lib/dev packages the following modules has not been compiled in:
[2025-03-10 10:58:16.726] [warning] module privacy: Unknown module: privacy
[2025-03-10 10:58:16.726] [warning] module mpris: Unknown module: mpris
[2025-03-10 10:58:16.731] [warning] Waybar has been built without rfkill support.
@Alexays , The following additional packages are necessary to install to build waybar, at least on Debian. Would you accept a README.md update with the below packages? (edit)
apt install libfftw3-dev libiniparser-dev libupower-glib-dev libpipewire-0.3-dev libplayerctl-dev libjack-dev libwireplumber-0.5-dev libsndio-dev libgtk-layer-shell-dev libasound2-dev portaudio19-dev libsdl2-dev
With the above only epoll-shim and libinotify are missing during meson setup build its possible both are FreeBSD related.
"Original" / debian package based waybar with mpris/audio/privacy:
Fixed dependency built waybar v0.12.0 (PR3868 enabled) with mpris/audio/privacy:
@pol-rivero
Glad to help :)
Thanks for all the info setting up workspace taskbars. Today run into a problem again in clamshell mode - with external monitor. First it correctly moved all windows into the same kind of positifion on the external monitor as it were in the notebook's screen, but closing the lid messed it up.
- the existing windows (hyprctl clients) just thrown everywhere,
- in a way not even part of the windows can be seen, so I can drag them
Edit: I've set up a keyboard windows' moving bind set, but I've got also an idea (beside I've fixed my clamshell.sh's logic/way :) ):
- Can workspace taskbars recognize, if I do mouse click with a modifier key - Super, SHIFT, ALT, CTRL?
- if yes, I would use this or something simliar to move windows top left
hyprctl dispatch moveactive exact 0 0with a mouse click + modifier. What "number" should I use az 1-2-3 used for LMC, RMC, MMC ?
Thx
@n-connect
Unfortunately, I don't think it's possible to get the keyboard state from a GDK mouse-click event. Feel free to correct me if I'm wrong, though.
Similarly, getting the keyboard state inside the script is hard, especially without root access. I found this answer, but I wasn't able to make it work in wayland.
However, you could easily do something like a double-click or triple-click to bring back the window:
#!/bin/bash
address=$1
# https://api.gtkd.org/gdk.c.types.GdkEventButton.button.html
button=$2
# Globals for double-click detection
LAST_CLICK_FILE="/tmp/waybar_taskbar_last_click"
DOUBLE_CLICK_THRESHOLD=0.3 # seconds
is_double_click() {
current_time=$(date +%s.%N)
if [ -f "$LAST_CLICK_FILE" ]; then
last_time=$(cat "$LAST_CLICK_FILE")
time_diff=$(awk "BEGIN {print $current_time - $last_time}")
if [ "$(echo "$time_diff < $DOUBLE_CLICK_THRESHOLD" | bc -l)" -eq 1 ]; then
# Double-click detected
rm -f "$LAST_CLICK_FILE"
return 0
fi
fi
echo "$current_time" > "$LAST_CLICK_FILE"
return 1 # Not a double-click.
}
on_double_click() {
# Put your double-click action here. For example:
hyprctl dispatch moveactive exact 0 0
}
if [ "$button" -eq 1 ]; then
if is_double_click; then
on_double_click
exit 0
fi
# Left click: focus window.
# The cursor:no_warps are optional. Set to not catch and move the cursor into the middle of screen as a result of "focuswindow"
hyprctl keyword cursor:no_warps true
hyprctl dispatch focuswindow address:$address
hyprctl dispatch bringactivetotop
hyprctl keyword cursor:no_warps false
elif [ "$button" -eq 2 ]; then
# Middle click: maximize window.
hyprctl dispatch fullscreenstate 1
elif [ "$button" -eq 3 ]; then
# Right click: close window.
hyprctl dispatch closewindow address:$address
fi
@pol-rivero
Unfortunately, I don't think it's possible to get the keyboard state from a GDK mouse-click event. Feel free to correct me if I'm wrong, though.
Similarly, getting the keyboard state inside the script is hard, especially without root access. I found this answer, but I wasn't able to make it work in wayland.
Thx for the answer and the details. Based on your answer I recon there's no keyboard-press detection code in waybar at all. Thinking about it, it makes sense, maybe I'm the first one tries to solve an issue coming from a different part of hyprland, via a 4th type of click.
(The root cause why I've had the whole modkey + mouse-click idea is, that the hyprctl keyword monitor,disable works as intended in clamshell [mode[close lid] , but the monitor,enable for the same monitor fails and throws and error [open lid]. Moved to dpms off/dpms on while it gets fixed / find out why it fails/wrong).
Edit: checked the stackoverflow link, I'm not sure if its the only direction, eg. you need root level access for this. I've checked all "hypr" processes - All of them running in my user's name. Still the various binds works well. About mouse I'm using 'autowaybar' which polls mouse position to determine if waybar should be unhidden or not. So I'm guessing none of them needs root access - there's should be a different solution, I'll just need to check Hyprland's code around 'bind'. Anyway the real problem is still with monitor disable/enable.
Thx again.
@Alexays,
Do you need something else to merge this PR into the mainline code? I've been using it since March 10, works without error, and really helps having a streamlined waybar :)
The auto-check fail about clang-format is independent from the PR code:
run-clang-format.py: error: Command '/clang-format/clang-format19 --version' failed to start: [Errno 2] No such file or directory: '/clang-format/clang-format19'
Detailed configs:
- The related waybar.jsonc part is here in this post
- The window-click.sh included in my post here 2.a The additional packages needed to install for waybar build in the same post above
If you like I can put the above into the wiki/readme.md whereever they need the update. Or maybe @pol-rivero wants to do the honors, I don't know.
@n-connect I have no preference one way or another. If you want to, feel free to update the wiki once this is merged, otherwise I'll do it myself.
When I find some time, I'll update the PR and solve the current conflict. There seems to have been many changes on the hyprland/workspaces module, so I might rewrite some parts. Either way, I would really appreciate it if you could recompile and test that everything still works on your machine. I'll let you know when it's done.
@pol-rivero
Sure, happy to test once those conflicts fixed/updated.
@n-connect Thank you! The PR is now ready to be tested again.
@pol-rivero ,
Thanks for the update, it works!
Version, stdout...
waybar -v;waybar &
Waybar v0.12.0-81-ga816812f (branch 'master')
[1] 3321453
attis@dbox:~$ [2025-04-30 21:31:38.494] [info] Using configuration file /home/<user>/.config/waybar/config.jsonc
[2025-04-30 21:31:38.496] [info] Unable to receive desktop appearance: GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such interface “org.freedesktop.portal.Settings” on object at path /org/freedesktop/portal/desktop
[2025-04-30 21:31:38.496] [info] Using CSS file /home/<user>/.config/waybar/style.css
[2025-04-30 21:31:38.508] [info] Hyprland IPC starting
[2025-04-30 21:31:38.509] [info] Loading persistent workspaces from Hyprland workspace rules
[2025-04-30 21:31:38.527] [warning] Waybar has been built without rfkill support.
error: no modem was specified
[2025-04-30 21:31:38.707] [info] Bar configured (width: 1280, height: 37) for output: eDP-1
Note to self: The build has had some difficulties this time. Following the main README.md build instructions (meson/ninja type): from this Waybar issue #2447, this post helped, TLDR comment out 3 lines about catch2 in meson.build . There are circumstances, when that issue still live :)
awesome work, if possible can you guys please look into this as well #2993 @pol-rivero @n-connect
@Iss-in
awesome work, if possible can you guys please look into this as well #2993 @pol-rivero @n-connect
Actually I've ditched wlr/taskbar because of hyprland/workspaces' taskbar functionality because of this very PR came from @pol-rivero - and never looked back.
//"wlr/taskbar",
"hyprland/workspaces"
Checked the issue as I recall I've have had the same problem more than one occasion. I suggest to use this one, I'm using it in clamshell mode or in multimonitor.
git clone https://github.com/Alexays/Waybar.git
cd Waybar;git pull origin pull/3868/head
Edit: Then compile it. If you happen to have catch2 installed, check my previous post howto move around it.
Also for clamshell purposes focusworkspaceoncurrentmonitor is better to use like this:
bind = $mainMod, 1, focusworkspaceoncurrentmonitor, 1
For multimonitor setup it can be as good as well, maybe in a selective way: your own-defined workspaces use it with focuschange, others don't
@Alexays,
Would you check this PR if it can be merged into mainline? @pol-rivero updated the PR code. The updated one also works (even some icon complain errors gone in CLI's stdout), AND now all checks passed / green too. Perhaps it can help people by transiting from wlr/taskbar seeing #2993 - for the ones are running Hyprland
Thx
@pol-rivero thanks for the work, I wanted to know the following,
- is there a way to style active window icon only , i dont see a way to separate active from non active windows in a workspace
@Iss-in Unfortunately no :(
The reason is that the hyprland/workspaces module does not listen on activewindow events, only movewindow and windowtitle, which are far less common. Listening to activewindow would mean that the module gets redrawn whenever the focused window changes, which could increase CPU and power usage slightly.
I originally decided against making this change in order to increase the chance the PR would get merged. However, since this PR seems to be dead in the water anyway, I could implement it if you want.
Regarding #2993, wlr/taskbar uses generic Wayland events instead of Hyprland's socket, and I'm not familiar on how it works or how to fix it.
