Skip to content

fix: show monitor name for HDMI/DP audio endpoints#46

Open
okhomenko wants to merge 4 commits intotsowell:mainfrom
okhomenko:fix/hdmi-endpoint-name
Open

fix: show monitor name for HDMI/DP audio endpoints#46
okhomenko wants to merge 4 commits intotsowell:mainfrom
okhomenko:fix/hdmi-endpoint-name

Conversation

@okhomenko
Copy link
Copy Markdown

@okhomenko okhomenko commented Mar 18, 2026

Problem

image image

When using NVIDIA GPU audio in pro-audio mode, or any HDMI/DP output, all sinks share the same PipeWire device. The default endpoint name template resolves {device:device.nick} first — which returns the GPU/card name (e.g. HDA NVidia) for every output — making multiple HDMI/DP sinks completely indistinguishable in wiremix.

Additionally, the Configuration tab shows generic names for both the selected profile and the profile dropdown, with no indication of which physical monitor each profile corresponds to.

Reproducing steps:

  1. Have a machine with an NVIDIA GPU and multiple monitors connected via HDMI/DisplayPort
  2. Enable pro-audio profile: pactl set-card-profile alsa_card.pci-0000_01_00.1 pro-audio
  3. Open wiremix → all HDMI/DP sinks appear as HDA NVidia in Output Devices tab
  4. Switch to the Configuration tab → selected profile shows Digital Stereo (HDMI 2) Output and the dropdown lists all profiles without monitor names

Expected: each sink, selected profile, and profile dropdown entry shows the name of the connected monitor (e.g. PA32QCV, DELL U2723QE)

Actual: all sinks show HDA NVidia; selected profile and dropdown show generic HDMI port numbers only

Root cause

Output Devices tab: PipeWire reads the monitor name from EDID/ELD data and stores it in node.nick (e.g. PA32QCV, DELL U2723QE). The device-level device.nick is the GPU name shared by all HDMI outputs on that card. The default endpoint template tried {device:device.nick} first — it always succeeds — so {node:node.description} was never reached.

Configuration tab: Profile descriptions (Digital Stereo (HDMI) Output) come from PipeWire and don't include monitor names. The SPA_PARAM_ROUTE_info dict on each EnumRoute contains device.product.name (the EDID monitor name) but was not being parsed. The selected profile display was also derived independently from the raw profile description, so it missed the enrichment too.

This can be verified:

$ aplay -l | grep NVidia
card 0: NVidia [HDA NVidia], device 3: HDMI 0 [DELL U2723QE]
card 0: NVidia [HDA NVidia], device 7: HDMI 1 [PA32QCV]

$ pactl list sinks | grep "node.nick"
  node.nick = "DELL U2723QE"
  node.nick = "PA32QCV"

Fix

image image

Output Devices tab: Add {node:node.nick} as the first template in default_endpoint. When node.nick is set (HDMI/DP monitors), it is used. When absent, falls through to {device:device.nick} as before.

 pub fn default_endpoint() -> Vec<NameTemplate> {
     vec![
+        "{node:node.nick}".parse().unwrap(),
         "{device:device.nick}".parse().unwrap(),
         "{node:node.description}".parse().unwrap(),
     ]
 }

Configuration tab: Parse SPA_PARAM_ROUTE_info in device_enum_route to extract device.product.name. When a profile maps to exactly one route with a known monitor name, append it to the profile description. The selected profile display reuses this enriched title so it stays consistent with the dropdown.

Before: Digital Stereo (HDMI 2) Output
After:  Digital Stereo (HDMI 2) Output — PA32QCV

Profiles that map to multiple routes (e.g. Pro Audio) or no routes (e.g. Off) are left unchanged.

When using NVIDIA GPU audio in pro-audio mode (or any HDMI/DP output),
all sinks share the same PipeWire device. The previous default endpoint
template resolved {device:device.nick} first, which returns the GPU
name ("HDA NVidia") for every output, making them indistinguishable.

PipeWire populates node.nick with the monitor name sourced from EDID/ELD
data (e.g. "PA32QCV", "DELL U2723QE"). By trying {node:node.nick} first,
each HDMI/DP sink now shows its connected monitor's name. For devices
where node.nick is absent, the template falls through to device.nick as
before.
@okhomenko okhomenko force-pushed the fix/hdmi-endpoint-name branch from 7343a46 to 3b39bf1 Compare March 18, 2026 16:57
Parse SPA_PARAM_ROUTE_info from EnumRoute params to extract
device.product.name (sourced from EDID/ELD data). When a profile maps
to exactly one route with a known monitor name, append it to the profile
description in the Configuration tab dropdown.

Before: Digital Stereo (HDMI 2) Output
After:  Digital Stereo (HDMI 2) Output — PA32QCV

Profiles that map to multiple routes (e.g. Pro Audio) or no routes
(e.g. Off) are left unchanged.
Reuse the enriched profile title (which includes the monitor name) for
the target_title shown below the device name in the Configuration tab,
instead of re-deriving it from the raw profile description.
@okhomenko
Copy link
Copy Markdown
Author

Brainstorming on how we could make this PR stronger for long-term quality: instead of treating this as just an HDMI/NVIDIA naming fix, maybe we should establish a reusable "physical endpoint label" concept and have both the Output Devices tab and Configuration tab build on that.

What I mean by that is:

  • move the route/profile/device matching heuristics out of view.rs and into a small helper/domain layer owned by PipeWire state
  • define one source of truth for "what physical thing does this endpoint/profile correspond to?"
  • make the default endpoint naming use that concept, rather than hard-coding node.nick
  • reuse the same helper when enriching Configuration profile titles

That would give us one place to handle cases like:

  • one profile mapping cleanly to one physical output
  • shared device ids across multiple routes/profiles
  • multi-route profiles like pro-audio
  • fallback when no monitor/product name exists

If we go that route, I think the cleanest version may be to expose a computed label through the naming system itself, so templates/overrides can use it too, instead of keeping it as a view-only special case.

@tsowell curious what you think — is that too much scope for this PR, or does it feel like the right direction while we're touching this area?

@tsowell
Copy link
Copy Markdown
Owner

tsowell commented Mar 22, 2026

Thanks for looking into this. I've been using this in my wiremix.toml to show the monitor name in the Output Devices tab:

[names]
endpoint = [ "{node:node.nick}", "{device:device.nick}", "{node:node.description}" ]

But I can see how it would be useful to have it available in the profile and route drop-downs too.

Here's where I see the monitor name in the PipeWire state:

"params": {
  "EnumProfile": [
    ...
    {
      "index": 4,
      "name": "output:hdmi-stereo-extra3",
      "description": "Digital Stereo (HDMI 4) Output",
      "priority": 5700,
      "available": "yes",
      "classes": [
        1,
        [
          "Audio/Sink",
          1,
          "card.profile.devices",
          [ 14 ]
        ]
      ]
    },
    ...
  ]
  "EnumRoute": [
    ...
    {
      "index": 3,
      "direction": "Output",
      "name": "hdmi-output-3",
      "description": "HDMI / DisplayPort 4",
      "priority": 5600,
      "available": "yes",
      "info": [
        6,
        "port.type",
        "hdmi",
        "port.availability-group",
        "Legacy 4",
        "device.icon_name",
        "video-display",
        "card.profile.port",
        "3",
        "device.product.name",
        "UltraFine Pro",
        "iec958.codecs.detected",
        "[\"PCM\"]"
      ],
      "profiles": [ 4 ],
      "devices": [ 14 ]
    },
    ...
  ]
}

It looks like EnumProfiles and EnumRoutes are linked by EnumRoutes' profiles list. There can be more than one route associated with each profile, but maybe we could follow the link only when it's one-to-one? So then you could do this to include the product name in the profile:

[names]
profile = [ "{profile:description}: {route:device.product.name}", "{profile:description}" ]

Or for routes:

[names]
route = [ "{route:description}: {route:device.product.name}", "{route:description}" ]

It seems to me like we would just need to add a little plumbing to get all of the the EnumRoute and EnumProfile information into State, and then the naming system can be easily adapted to handle the rest of the work.

@okhomenko
Copy link
Copy Markdown
Author

Thanks, this makes sense to me.

I’m going to rework this PR in the direction you suggested:

  • add configurable profile / route naming
  • plumb the needed EnumProfile / EnumRoute fields through State
  • keep the defaults conservative
  • keep (unavailable) as UI suffixes rather than making availability part of templates

So instead of special-casing monitor names in view.rs, the Configuration and route dropdown labels will come from the naming system.

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.

2 participants