Skip to content

feat(ipc): add per-client tag event to zdwl_ipc_output_v2 v3#887

Open
dashdashRod wants to merge 1 commit into
mangowm:mainfrom
dashdashRod:Experimental
Open

feat(ipc): add per-client tag event to zdwl_ipc_output_v2 v3#887
dashdashRod wants to merge 1 commit into
mangowm:mainfrom
dashdashRod:Experimental

Conversation

@dashdashRod
Copy link
Copy Markdown

Proposal: per-client tag identification via new client event in zdwl_ipc_output_v2

The gap

Mango's IPC currently exposes per-tag client counts via the existing tag event
on zdwl_ipc_output_v2, but no way to associate clients with their tags.
zwlr_foreign_toplevel_manager_v1 reports all toplevels with their appid/title
but no tag information; ext_workspace_manager_v1 reports tag state but no
toplevel association. The two streams are disjoint, so consumers (status bars,
custom launchers, scripted layouts) can know "tag 3 has 2 windows" or
"these toplevels exist," but not "which windows are on tag 3."

The information exists internally — every Client struct has a tags bitmask —
it just isn't exposed.

Reproduction

Diagnostic client binding both protocols against an unmodified mango:

Tag 1 active, three windows open across multiple tags:

toplevel: title="Mozilla Firefox"     state=[]
toplevel: title="firefox ~/D/p/mtags" state=[]
toplevel: title="~/D/p/mtags"         state=[activated]
workspace 1: state=active

Tag 4 active, same three windows:

toplevel: title="Mozilla Firefox"     state=[]
toplevel: title="firefox ~/D/p/mtags" state=[]
toplevel: title="~/D/p/mtags"         state=[]
workspace 4: state=active

The toplevel list is identical. There's no way for a consumer to determine which
window lives on which tag. The output came via mtags,
a small Wayland test client that connects to the compositor, binds the available
workspace/toplevel/dwl-ipc protocols, listens for events, and prints workspace, window,
and tag/client state for debugging.

Proposal

Add a client event to zdwl_ipc_output_v2 (bumped to interface version 3)
that fires once per managed client per output during the existing state-update
batch, after the per-tag events and before frame:

<event name="client" since="3">
  <description summary="Reports a client and its tag mask.">
    Reports a single managed client on this output, with its appid, title,
    and tag bitmask.
  </description>
  <arg name="appid" type="string"/>
  <arg name="title" type="string"/>
  <arg name="tagmask" type="uint"/>
</event>

Verification

Working prototype on the fork. Same diagnostic client, this time bound to zdwl_ipc_manager_v2 v3. Two foot terminals on tag 1:
Implementation is localized, one event definition in the XML, one loop in
dwl_ipc_output_printstatus_to, plus the version bump where the global is
created. Checkout the diff in this fork fork

ipc client: appid="foot" title="~/D/m/mango" tagmask=0x1
ipc client: appid="foot" title="./mtags > NEWBRANDRE ~/D/p/m/build" tagmask=0x1

After moving one to tag 4 via mmsg -d tag,4:

ipc client: appid="foot" title="~/D/m/mango" tagmask=0x8

Bitmask correlates correctly with tag membership (0x1 = tag 1, 0x8 = tag 4).

Full mtags output
bound: zdwl_ipc_manager_v2 v3
bound: zwlr_foreign_toplevel_manager_v1 v3
bound: ext_workspace_manager_v1 v1
bound: wl_output v4
ipc_output: bound
ipc: tags amount=9
new toplevel: 0x2892e7d0
new toplevel: 0x289314d0
  toplevel 0x2892e7d0: title="./mtags > NEWBRANDRE ~/D/p/m/build"
  toplevel 0x2892e7d0: app_id="foot"
  toplevel 0x2892e7d0: state=[activated ]
  toplevel 0x2892e7d0: done
  toplevel 0x289314d0: title="~/D/m/mango"
  toplevel 0x289314d0: app_id="foot"
  toplevel 0x289314d0: state=[]
  toplevel 0x289314d0: done
new group: 0x289336e0
new workspace: 0x28933930
  workspace 0x28933930: name=9
  workspace 0x28933930: id=9
  workspace 0x28933930: state=hidden 
group 0x289336e0: workspace 0x28933930 entered
new workspace: 0x28933f40
  workspace 0x28933f40: name=8
  workspace 0x28933f40: id=8
  workspace 0x28933f40: state=hidden 
group 0x289336e0: workspace 0x28933f40 entered
new workspace: 0x28934550
  workspace 0x28934550: name=7
  workspace 0x28934550: id=7
  workspace 0x28934550: state=hidden 
group 0x289336e0: workspace 0x28934550 entered
new workspace: 0x28934b60
  workspace 0x28934b60: name=6
  workspace 0x28934b60: id=6
  workspace 0x28934b60: state=hidden 
group 0x289336e0: workspace 0x28934b60 entered
new workspace: 0x28935170
  workspace 0x28935170: name=5
  workspace 0x28935170: id=5
  workspace 0x28935170: state=hidden 
group 0x289336e0: workspace 0x28935170 entered
new workspace: 0x28935810
  workspace 0x28935810: name=4
  workspace 0x28935810: id=4
  workspace 0x28935810: state=
group 0x289336e0: workspace 0x28935810 entered
new workspace: 0x28935e20
  workspace 0x28935e20: name=3
  workspace 0x28935e20: id=3
  workspace 0x28935e20: state=
group 0x289336e0: workspace 0x28935e20 entered
new workspace: 0x28936430
  workspace 0x28936430: name=2
  workspace 0x28936430: id=2
  workspace 0x28936430: state=
group 0x289336e0: workspace 0x28936430 entered
new workspace: 0x28936a40
  workspace 0x28936a40: name=1
  workspace 0x28936a40: id=1
  workspace 0x28936a40: state=active 
group 0x289336e0: workspace 0x28936a40 entered
--- workspace state complete ---
  toplevel 0x289314d0: output_enter 0x289311f0
  toplevel 0x2892e7d0: output_enter 0x289311f0
  --- ipc frame ---
  ipc client: appid="foot" title="~/D/m/mango" tagmask=0x1
  ipc client: appid="foot" title="./mtags > NEWBRANDRE ~/D/p/m/build" tagmask=0x1
--- workspace state complete ---
  toplevel 0x289314d0: done
  toplevel 0x2892e7d0: done

Notes

The event reports appid/title/tagmask. More fields (pid, floating/fullscreen, monitor) can be added later — Wayland version bumps are backward-compatible, so older clients just ignore newer events.

Adds `client` event reporting appid/title/tagmask once per managed
client per output during state-update batches. Closes the gap where
consumers could see per-tag client counts but not per-tag client
identities.

Bumps zdwl_ipc_output_v2 interface to v3 and zdwl_ipc_manager_v2
global advertisement to v3. Implementation localized to
dwl_ipc_output_printstatus_to in src/ext-protocol/dwl-ipc.h.
@DreamMaoMao
Copy link
Copy Markdown
Collaborator

This dwl ipc protocol is planned to be abandoned and I have no intention of changing it again

@fishman
Copy link
Copy Markdown

fishman commented May 11, 2026

This dwl ipc protocol is planned to be abandoned and I have no intention of changing it again

That's probably good, but this is basically just a version bump temporary

zzonez pushed a commit to zzonez/mango that referenced this pull request May 11, 2026
Adds `client` event reporting appid/title/tagmask once per managed
client per output during state-update batches. Closes the gap where
consumers could see per-tag client counts but not per-tag client
identities.

Bumps zdwl_ipc_output_v2 interface to v3 and zdwl_ipc_manager_v2
global advertisement to v3. Implementation localized to
dwl_ipc_output_printstatus_to in src/ext-protocol/dwl-ipc.h.

fix(ipc): correct opcode order, frame placement, and add stable client id

Three issues with the original PR mangowm#887 commit:

1. The new `client` event was inserted between `appid` and `layout_symbol`
   in the XML, which shifts opcodes for every event after it and breaks
   wire compatibility with v2 consumers. Wayland convention is to append
   new events at the end of the interface. Moved `client` to the end under
   a `<!-- Version 3 -->` marker.

2. `zdwl_ipc_output_v2_send_frame()` was called before the per-client emit
   loop, but `frame` is documented as the end-of-batch marker ("the update
   sequence is done"). Consumers that rely on it to commit a fresh window
   list per output would see stale data. Moved the loop above the frame send.

3. The event carried only appid/title/tagmask, which can't distinguish two
   windows with identical metadata. Added a `uint id` field, sourced from a
   new monotonic `Client.ipc_id` assigned at creation (skipping 0 so consumers
   can treat 0 as "unset"). This pairs with the focusclient_byid dispatch
   in a follow-up commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
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.

3 participants