WayfireWM / wayfire

A modular and extensible wayland compositor
https://wayfire.org/
MIT License
2.36k stars 175 forks source link

Disabling video output without also disabling audio? #2340

Closed flexibeast closed 4 months ago

flexibeast commented 5 months ago

This is not a bug; it might be a feature request, but if so, it's more likely to be a feature request for wlroots or wlr-randr.

When at home, i connect my laptop to an amplifier via an HDMI-to-analog converter, so that i can play the music on my laptop through the amp. (And i'm using PipeWire + WirePlumber on my laptop.) However, this results in the creation of an HDMI output, HDMI-A-1, in addition to the laptop screen output, eDP-1. By default, this means that there's an unused video output the size of the laptop screen attached to each workspace: with my layout configuration, the HDMI-A-1 output gets attached to the right of the eDP-1 output, and is the size of the laptop screen. This in turn results in my pointer being able to go off to the right of the actually-used workspace.

i currently work around that particular issue by doing:

[output:eDP-1]
mode = 1366x768@60000
position = 0,0
transform = normal
scale = 1.000000

[output:HDMI-A-1]
# Position HDMI-A-1 outside space accessible via screen edges,
# to support audio-only usage.
position = 1366,768

However, there's still another issue: when i take screencaps of "the full screen" - which i do regularly - the screencap doesn't just include eDP-1, but of course includes HDMI-A-1 as well. With the above output configuration, i get a huge screencap: the actual content i want - eDP-1 - is in the upper-left, the empty workspace of HDMI-A-1 in the lower-right, and the lower-left and upper-right is just empty space.

What i would like to be able to do is simply say: "Disable HDMI-A-1 video output, but not audio output, unless otherwise specified". Is there currently some way of doing this? If not, where should i direct the relevant feature request?

soreau commented 5 months ago

Since wayfire itself does nothing with audio, it's probably pipewire or drivers controlling this behavior. Though, you might try mode = 0x0 or see if wlopm helps.

flexibeast commented 5 months ago

Thanks for the prompt response, and the suggestions!

Setting mode = 0x0 didn't work, and using wlopm (unsurprisingly) disabled audio output as well as video output.

i'll dig around in PipeWire and report back. :-)

ammen99 commented 5 months ago

Isn't there a way to tell the driver not to create an output at all? Sounds to me weird that there should be an output to begin with.

Otherwise, maybe try creating a separate logind seat and assign this virtual output to it, so that Wayfire doesn't see it? I have never done this but iirc it is possible.

ammen99 commented 5 months ago

Actually nevermind, this isn't really the case, with loginctl seats you can only assign input devices or whole GPUs. In case that your HDMI output uses a different GPU it would be easy to work it around with existing stuff, otherwise, Wayfire could also simply ignore your device. That would be basically the same as allowing output leasing for normal outputs, it would require a bit more work in core.

If you are looking for a quick and dirty solution however, you can patch Wayfire like this:

diff --git a/src/core/output-layout.cpp b/src/core/output-layout.cpp
index 7497cf49..ada4445e 100644
--- a/src/core/output-layout.cpp
+++ b/src/core/output-layout.cpp
@@ -1171,7 +1171,7 @@ class output_layout_t::impl
         LOGI("new output: ", output->name,
             " (\"", output->make, " ", output->model, " ", output->serial, "\")");

-        if (output->non_desktop)
+        if (output->non_desktop || !strcmp(nonull(output->name), "HDMI-A-1"))
         {
             LOGD("Non-desktop output ", output->name, " found");
             if (get_core().protocols.drm_v1)
ammen99 commented 5 months ago

Which also gives me an idea, maybe you can try to set the non-desktop property on your output? This way wayfire will automatically ignore it.

For example with tools like this https://github.com/NickCis/drm_tool (haven't tried it myself though)

flexibeast commented 5 months ago

Ah okay - i'll give that a try when i'm next home. :-)

flexibeast commented 4 months ago

Hmm ....

$ ./drm_tool list                               
Card /dev/dri/card0
 capability 'DUMP BUFFER' (1) : 1
 capability 'VBLANK HIGH CRTC' (2) : 1
 capability 'DUMB PREFERED DEPTH' (3) : 24
 capability 'DUMB PREFER SHADOW' (4) : 1
 capability 'PRIME' (5) : 3
 capability 'TIMESTAMP MONOTONIC' (6) : 1
  Connector: 87
    mode  0: 1366x768        60Hz 1366x768
    mode  1: 1280x720        60Hz 1280x720
    mode  2: 1024x768        60Hz 1024x768
    mode  3: 800x600         60Hz 800x600
    mode  4: 640x480         60Hz 640x480
    property (#1):  EDID = 94
    property (#2):  DPMS = 0 (On)
    property (#5):  link-status = 0 (Good)
    property (#6):  non-desktop = 0
    property (#4):  TILE = 0
    property (#33):  scaling mode = 0 (None)
    property (#34):  underscan = 0 (off)
    property (#35):  underscan hborder = 0
    property (#36):  underscan vborder = 0
    property (#88):  max bpc = 8
    property (#39):  abm level = 0
    property (#89):  Colorspace = 0 (Default)
    property (#7):  HDR_OUTPUT_METADATA = 0
    property (#90):  vrr_capable = 0
    property (#91):  Content Protection = 0 (Undesired)
    property (#92):  HDCP Content Type = 0 (HDCP Type0)
  Connector: 95
    mode  0: 1920x1080       60Hz 1920x1080
    mode  1: 1920x1080       60Hz 1920x1080
    mode  2: 1920x1080       50Hz 1920x1080
    mode  3: 1600x1200       60Hz 1600x1200
    mode  4: 1680x1050       60Hz 1680x1050
    mode  5: 1280x1024       75Hz 1280x1024
    mode  6: 1280x1024       60Hz 1280x1024
    mode  7: 1440x900        75Hz 1440x900
    mode  8: 1440x900        60Hz 1440x900
    mode  9: 1280x960        60Hz 1280x960
    mode 10: 1280x800        60Hz 1280x800
    mode 11: 1152x864        75Hz 1152x864
    mode 12: 1280x720        60Hz 1280x720
    mode 13: 1280x720        60Hz 1280x720
    mode 14: 1280x720        50Hz 1280x720
    mode 15: 1024x768        75Hz 1024x768
    mode 16: 1024x768        70Hz 1024x768
    mode 17: 1024x768        60Hz 1024x768
    mode 18: 832x624         75Hz 832x624
    mode 19: 800x600         75Hz 800x600
    mode 20: 800x600         72Hz 800x600
    mode 21: 800x600         60Hz 800x600
    mode 22: 800x600         56Hz 800x600
    mode 23: 720x576         50Hz 720x576
    mode 24: 720x480         60Hz 720x480
    mode 25: 720x480         60Hz 720x480
    mode 26: 640x480         75Hz 640x480
    mode 27: 640x480         73Hz 640x480
    mode 28: 640x480         67Hz 640x480
    mode 29: 640x480         60Hz 640x480
    mode 30: 640x480         60Hz 640x480
    mode 31: 720x400         70Hz 720x400
    property (#1):  EDID = 113
    property (#2):  DPMS = 0 (On)
    property (#5):  link-status = 0 (Good)
    property (#6):  non-desktop = 0
    property (#4):  TILE = 0
    property (#33):  scaling mode = 0 (None)
    property (#34):  underscan = 0 (off)
    property (#35):  underscan hborder = 0
    property (#36):  underscan vborder = 0
    property (#96):  max bpc = 8
    property (#97):  content type = 1 (Graphics)
    property (#98):  Colorspace = 0 (Default)
    property (#7):  HDR_OUTPUT_METADATA = 0
    property (#99):  vrr_capable = 0
    property (#91):  Content Protection = 0 (Undesired)
    property (#92):  HDCP Content Type = 0 (HDCP Type0)
$ ./drm_tool set /dev/dri/card0 95 non-desktop 1
Setting 'non-desktop' -> 1 - ret: -13 :: connector_id: 95 prop_id: 6

So trying to set the non-desktop property apparently fails, and taking a screencap confirms that the HDMI output is still active.

i've tried spelunking through the libdrm source starting at line 880 of xf86drmMode.c to see what a return value of -13 might mean, but i got to the #define for _IOWR without finding anything (although i certainly might have missed something, given my lack of familiarity with DRM in general and ioctl facilities in particular).

Any suggestions?

ammen99 commented 4 months ago

Strace seems to say that -13 means permission denied, here it is the same:

ioctl(3, DRM_IOCTL_MODE_SETPROPERTY, 0x7ffe0bd9d1f0) = -1 EACCES (Permission denied)

Maybe the driver needs to be configured in a way to enable this, or maybe it simply is not possible to change this value from userspace. Unfortunately I do not know how to proceed except for potentially patching Wayfire (or the kernel, but unless you're already doing that, I assume patching Wayfire will be way easier).

flexibeast commented 4 months ago

Argh, sorry, didn't think to use strace ....

Okay, thanks for your help - i'll try patching Wayfire and reporting back, but in any case, i'm closing this issue.

ammen99 commented 4 months ago

By the way, when you take screenshots, you ought to be able to say which output you want a screenshot from (looks like you'd hardcode eDP-1 there). What other issues are there in practice?

flexibeast commented 4 months ago

In practice, i've not noticed any issues besides the "continually losing my pointer to the unused workspace" thing and the screencap issue.

i've written a script to use grim and slurp to get screencaps - grim for when i want the entire screen, slurp + grim when i just want a region. grim certainly has the ability to specify an output (via the -o option), but scanning the slurp man page, i'm not sure if i can directly specify "a region on this particular output"? Like, there are predefined rectangles to get the entirety of an output, but might i have to do my own calculations to specifiy a particular rectangle within an output?

ammen99 commented 4 months ago

i've written a script to use grim and slurp to get screencaps - grim for when i want the entire screen, slurp + grim when i just want a region. grim certainly has the ability to specify an output (via the -o option), but scanning the slurp man page, i'm not sure if i can directly specify "a region on this particular output"? Like, there are predefined rectangles to get the entirety of an output, but might i have to do my own calculations to specifiy a particular rectangle within an output?

I would workaround those issues like this: configure your eDP-1 output to be at position 0,0. Configure the HDMI-A-1 output to be far away and disconnected from it, say at position 10000,10000. Wayfire will print a warning because these setups most likely will not work well, but in your case you want exactly this: you will not be able to move the pointer between the outputs.

As a note, if it happens that you turn off the eDP-1 output from time to time, make sure to enable the oswitch plugin. With it, you can have a keybinding to change output focus, but it will also make the cursor jump between outputs (iirc). This would be useful in case you turn off eDP-1 (so the cursor will go to the invisible HDMI-A-1) and then you wouldn't be able to bring it back except via oswitch.

flexibeast commented 4 months ago

Ah, okay, thanks for the tips! i definitely already have oswitch enabled. 🙂

i'm currently in the middle of various bits of work for the Gentoo wiki, but once those are done, perhaps i could add something about this stuff to the Wayfire FAQ? (Unless you think that's a bad idea for some reason?)

ammen99 commented 4 months ago

i'm currently in the middle of various bits of work for the Gentoo wiki, but once those are done, perhaps i could add something about this stuff to the Wayfire FAQ? (Unless you think that's a bad idea for some reason?)

We have Tips & Tricks, sounds like it would be a good fit if you get a well-working setup :)

flexibeast commented 4 months ago

So, i finally got around to applying and testing the patch; unfortunately, it disables the entire output, as wlr-randr does, rather than just the video component, as i need.

i'll try to play around with PipeWire / WirePlumber a bit to see if there some way i can use them to do what i need. But in any case, i'll also try adding something to the "Tips and Tricks" section about positioning the video output. A question i had regarding that: is there any particular reason to position the undesired video output so far away, and/or is there some particular problem that can be created by positioning it as i described in my original comment?

ammen99 commented 4 months ago

So, i finally got around to applying and testing the patch; unfortunately, it disables the entire output, as wlr-randr does, rather than just the video component, as i need.

Ah, so you'll need to enable the output (and commit that action) before returning early.

But in any case, i'll also try adding something to the "Tips and Tricks" section about positioning the video output. A question i had regarding that: is there any particular reason to position the undesired video output so far away, and/or is there some particular problem that can be created by positioning it as i described in my original comment?

There is no real problem in positining the output where you had it initially, I suggested putting it very far away so that in case you for example plugin in another output, the output doesn't clash with the existing one or stuff like that. Also I think if you make your mouse move very fast you might be able to jump to the next output if it is just a few pixels away, if you set a distance of 10000 pixels or so you'll need FTL travel for your mouse to jump over the distance :)

flexibeast commented 4 months ago

Ah, so you'll need to enable the output (and commit that action) before returning early.

Er .... Sorry, i don't really follow, but anyway, probably don't want to get into having to learn C++ and the Wayland API in order to maintain this myself. 🙂 Hence wanting to see what can be done with PipeWire and WirePlumber.

Re. positioning, ah okay, good points - i'll be sure to mention those issues in the "Tips and Tricks" entry. 🙂 Thanks!

ammen99 commented 4 months ago

Er .... Sorry, i don't really follow, but anyway, probably don't want to get into having to learn C++ and the Wayland API in order to maintain this myself. 🙂 Hence wanting to see what can be done with PipeWire and WirePlumber.

It is just two additional calls, nothing complicated:

diff --git a/src/core/output-layout.cpp b/src/core/output-layout.cpp
index e8c8ea2e..577a6d8c 100644
--- a/src/core/output-layout.cpp
+++ b/src/core/output-layout.cpp
@@ -1171,8 +1171,11 @@ class output_layout_t::impl
         LOGI("new output: ", output->name,
             " (\"", output->make, " ", output->model, " ", output->serial, "\")");

-        if (output->non_desktop)
+        if (output->non_desktop || !strcmp(nonull(output->name), "HDMI-A-1"))
         {
+            wlr_output_enable(output, true);
+            wlr_output_commit(output);
+
             LOGD("Non-desktop output ", output->name, " found");
             if (get_core().protocols.drm_v1)
             {
flexibeast commented 4 months ago

Oh okay - thanks! i'll see if i can try that out a bit later.

flexibeast commented 3 months ago

Finally reporting back: Unfortunately, that disabled the entire output, à la wlr-randr --output HDMI-A-1 --off, rather than only the video component of the output. i've not yet started looking into what PipeWire / WirePlumber can do in this regard.