Skip to content

niri-taskbar: Initial Module Implementation#4164

Draft
Nitepone wants to merge 20 commits into
Alexays:masterfrom
Nitepone:dev/niri-taskbar
Draft

niri-taskbar: Initial Module Implementation#4164
Nitepone wants to merge 20 commits into
Alexays:masterfrom
Nitepone:dev/niri-taskbar

Conversation

@Nitepone

@Nitepone Nitepone commented Jun 4, 2025

Copy link
Copy Markdown

This PR implements a taskbar that is specific for Niri. Unlike wlr-taskbar, this taskbar provides the ability to order applications by position in the Niri workspace; Groups applications by their workspace; and uses exclusively Niri IPC building on the implementation of the existing niri-workspaces module.

NOTE: This depends on an unmerged Niri PR, niri-wm/niri#1265

@3ulalia

3ulalia commented Aug 18, 2025

Copy link
Copy Markdown

the PR got merged! how is this looking in terms of review readiness?

@Nitepone

Copy link
Copy Markdown
Author

Well, I've been using this module for a couple months now. There are 2 major outstanding issues to resolve..

  • Update IPC handling to use the field names from the final Niri PR
  • Fix crashes on monitor disconnect/reconnect/sleep
    • Either error handling for accessing the IPC JSON values, or some guarantee that they won't disappear on these events seems like a must

I'll see if I can get it polished up this weekend!

@Nitepone

Nitepone commented Aug 19, 2025

Copy link
Copy Markdown
Author

Oh also, I have some aspirations to add support for multiple icon theme packs; and support for allowing user config of using characters in place of icons of certain applications (For things like font awesome icons).

These are enhancements that should probably crawl in through a later MR.

Nitepone and others added 15 commits August 28, 2025 11:18
Bugs:
- Old Niri Untested.
- It seems like IPC handling in niri/backend needs to be updated to
  support the new indicies properly. Moving windows often leads to stale
  data.
- Icons for some flatpak apps (Like obsidian on my laptop) do not work.
- Using niri dev branch.
- Add separators when showing multiple workspaces
- Add failover if app icon not found
X Applications often use their "StartupWMClass" names from their desktop
files. In these cases, we need to parse through the desktop files to get
the "Icon" name (which is typically their real app id).

This occurs with all X based flatpak apps, since the underlying app id
(e.g. "com.myapp.desktop") almost never aligns with the StartupWMClass.
Keep track of buttons and workspaces with their own classes. This should
help to allow us to better apply workspace specific styles.

It should also allow us to more easily optimize performance.
@moving-hawk

moving-hawk commented Sep 2, 2025

Copy link
Copy Markdown

Hi, I'm an arch Linux user who wants to try niri/taskbar earlier. I compiled the code using the PKGBUILD from the AUR after modifying the source URL.

It seems that "WindowsLocationsChanged" has been changed to "WindowsLocationChanged", and "tile_pos_in_scrolling_layout" has been shortened to "pos_in_scrolling_layout".

niri --version
niri 25.08 (v25.05.1-202-ga62b3879)
[2025-09-02 13:02:15.107] [debug] Niri IPC: received {"WindowLayoutsChanged":{"changes":[[3,{"pos_in_scrolling_layout":[1,1],"tile_size":[2042.4,1079.2],"window_size":[2039,1076],"tile_pos_in_workspace_view":null,"window_offset_in_tile":[1.6,1.6]}],[2,{"pos_in_scrolling_layout":[2,1],"tile_size":[1020.0,1079.2],"window_size":[1017,1076],"tile_pos_in_workspace_view":null,"window_offset_in_tile":[1.6,1.6]}]]}}

When I connect an external display and close the laptop lid, Waybar stops running.

[2025-09-02 13:14:57.183] [info] Bar configured (width: 1703, height: 30) for output: eDP-1

** (waybar:15151): ERROR **: 13:14:57.186:
unhandled exception (type unknown) in signal handler

Trace/breakpoint trap      (core dumped) waybar -c ~/.config/waybar/config_niri.jsonc -s ~/.config/waybar/style_niri.css -l debug

@Nitepone

Nitepone commented Sep 2, 2025

Copy link
Copy Markdown
Author

It seems that "WindowsLocationsChanged" has been changed to "WindowsLocationChanged", and "tile_pos_in_scrolling_layout" has been shortened to "pos_in_scrolling_layout".

This has been updated in my latest commit.

When I connect an external display and close the laptop lid, Waybar stops running

I have seen this a few times. I also recently saw it with the other niri modules on mainline waybar. I suspect there is something up with niri backend setting some values to Null from a Niri IPC, then those are improperly handled by modules on bar refresh.

I swear I'll actually work on this over this week.. The joys of life becoming busy ^^'

@moving-hawk

moving-hawk commented Sep 3, 2025

Copy link
Copy Markdown

It seems that "WindowsLocationsChanged" has been changed to "WindowsLocationChanged", and "tile_pos_in_scrolling_layout" has been shortened to "pos_in_scrolling_layout".

This has been updated in my latest commit.

I believe I was using the latest commit (b0ad031). If the two places mentioned below have been updated, I might have missed something.

diff --git a/src/modules/niri/taskbar.cpp b/src/modules/niri/taskbar.cpp
index 4f4dfd29..81ee0ca8 100644
--- a/src/modules/niri/taskbar.cpp
+++ b/src/modules/niri/taskbar.cpp
@@ -421,7 +421,7 @@ Taskbar::Taskbar(const std::string &id, const Bar &bar, const Json::Value &confi
 
   gIPC->registerForIPC("WindowsChanged", this);
   gIPC->registerForIPC("WindowOpenedOrChanged", this);
-  gIPC->registerForIPC("WindowsLocationsChanged", this);
+  gIPC->registerForIPC("WindowLayoutsChanged", this);
   gIPC->registerForIPC("WindowFocusChanged", this);
   gIPC->registerForIPC("WindowClosed", this);
diff --git a/src/modules/niri/backend.cpp b/src/modules/niri/backend.cpp
index 2a1bdb07..f353eef1 100644
--- a/src/modules/niri/backend.cpp
+++ b/src/modules/niri/backend.cpp
@@ -171,16 +171,16 @@ void IPC::parseIPC(const std::string &line) {
      windows_.clear();
      const auto &values = payload["windows"];
      std::copy(values.begin(), values.end(), std::back_inserter(windows_));
-    } else if (const auto &payload = ev["WindowsLocationsChanged"]) {
+    } else if (const auto &payload = ev["WindowLayoutsChanged"]) {
      // TODO THIS RELIES ON UNMERGED AN NIRI PR!!! CHECK AFTER IT IS MERGED https://github.com/YaLTeR/niri/pull/1265
      for (const auto &win_changes : payload["changes"]) {
        for (auto &win : windows_) {
          if (win["id"] == win_changes[0]) {
            // XXX We are deliberately dropping tile_size data for the sake of
            // ease of filtering spammed IPCs from moused based window resizing.
-            win["location"] = Json::Value(Json::objectValue);
-            win["location"]["tile_pos_in_scrolling_layout"]
-                = win_changes[1]["tile_pos_in_scrolling_layout"];
+            //win["location"] = Json::Value(Json::objectValue);
+            win["location"]["pos_in_scrolling_layout"]
+                = win_changes[1]["pos_in_scrolling_layout"];
            break;
          }
        }

Another question is how can I use the format 'icon-and-text'/'text'. My config doesn't work now.

    "niri/taskbar":{
        "format": "{icon-and-text}",
        "icon-size" : 24,
        "on-click": "activate",
        //"active-workspace-format": "label-ws-name", 
        "inactive-workspace-format": "",
        "empty-ws-button-label": ""
    },

When I connect an external display and close the laptop lid, Waybar stops running

I have seen this a few times. I also recently saw it with the other niri modules on mainline waybar. I suspect there is something up with niri backend setting some values to Null from a Niri IPC, then those are improperly handled by modules on bar refresh.

I swear I'll actually work on this over this week.. The joys of life becoming busy ^^'

It's fine. Life can be very busy owo. Thank you for your consideration!

@Nitepone

Nitepone commented Sep 3, 2025

Copy link
Copy Markdown
Author

Oops, yeah it looks like I simply didn't properly test my original update to the IPCs. That should be fixed now, with a bonus of having fixed on of the intermittent crashes on monitor add/remove.

Another question is how can I use the format 'icon-and-text'/'text'. My config doesn't work now.

This PR also needs docs for this. In lieu of that, here is a snippet from my config with some comments.

  "niri/taskbar": {
    // This sets the style of workspace groups.
    // Value is an enum of { "no-label", "label-idx", "label-ws-name" }
    "active-workspace-format" : "label-idx",
    "inactive-workspace-format" : "label-idx",

    // This sets the style of window buttons.
    // Value is an enum of { "icon", "text", "icon-and-text" }
    "active-button-format" : "icon-and-text",
    "inactive-button-format" : "icon",

    // The application icon size in px.
    "icon-size": 24,

    // The label used for the empty workspace indicator.
    "empty-ws-button-label": "+",
  },

As for css, the outer module element is named "taskbar" and each workspace has it's own names "workspace" div.

Thus css theming looks vaguely like..

#workspace {
  padding: 0 .4em;
  border-radius: .5em;
  background-color: #404040;
}

#workspace.active {
  background-color: #aa2020;
}

#workspace label {
  padding: 0 .4em;
}

#taskbar button {
  padding: 0 .2em;
  border-radius: .5em;
}

#taskbar button.active {
  background-color: #808080;
}

#taskbar button:hover {
  background: #2020aa;
}

#taskbar separator {
  background-color: #505050;
}

@moving-hawk

Copy link
Copy Markdown

Thanks for the quick fix! I'm still seeing a small error.

[error] Error parsing IPC: {
        "WorkspaceActiveWindowChanged" :
        {
                "active_window_id" : null,
                "workspace_id" : 4
        }
}

Seems the text part can only be app_id. It would be nice to include title!

@Nitepone

Nitepone commented Sep 4, 2025

Copy link
Copy Markdown
Author

Fixed that error message. Introduced it with my backend refactoring.

Updated the "button-format" enum to support using the window title instead of app id. "icon", "app-id", "title, "icon-and-app-id", "icon-and-title". Though, the extremely long titles of some windows make this sometimes problematic. I went ahead and added a new config property "label-max-length" to fix this issue. By default button labels are limited to 40 characters.

@moving-hawk

Copy link
Copy Markdown

Great! Thank you so much!

@ADIX7

ADIX7 commented Sep 16, 2025

Copy link
Copy Markdown

Is there a way to hide the new-workspace-button?

@ADIX7

ADIX7 commented Sep 16, 2025

Copy link
Copy Markdown

Also, the wlr/taskbar uses class active instead of focused, I don't know if it is intentional.

@ADIX7

ADIX7 commented Sep 16, 2025

Copy link
Copy Markdown

And a bug: the label-ws-name option does not fallback to the workspace id but displays an empty string, I guess because the key is present in the json but the value is null.

Edit: I managed to fix it:

case WorkspaceFormat::LabelWsName: {
	Json::Value name = ws.get("name", Json::Value::null);
	if (name.isNull()) {
			this->label_.set_label(ws.get("idx", "error").asString());
	} else {
			this->label_.set_label(name.asString());
	}
	break;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants