hyprwm / Hyprland

Hyprland is an independent, highly customizable, dynamic tiling Wayland compositor that doesn't sacrifice on its looks.
https://hyprland.org
BSD 3-Clause "New" or "Revised" License
19.35k stars 806 forks source link

Allow using model number in monitor config #5210

Closed gardk closed 1 month ago

gardk commented 5 months ago

Description

Hi,

I'm using a display which has a "normal" and a "game" mode. The only difference shown by hyprctl monitors between these two is the availability of 3440x1440@175 in game mode. This includes the "model" field being the same. The EDID information however tells me that these modes present different model numbers.

Would it be possible to add the ability to specify something like monitor = modelnr:29426, ... to Hyprland? As far as I can tell from the wiki and my own testing there's no way for me to explicitly configure different refresh rates for each mode here, please do correct me if I'm wrong. That being said I am using the highrr option and that works flawlessly, so if you don't feel it necessary to implement this then that would be completely understandable.

NORMAL MODE

$ sudo edid-decode /sys/class/drm/card0-DP-1/edid | rg '^\s*(Model: \d+)' -r '$1'
Model: 29428
$ hyprctl monitors
Monitor DP-1 (ID 0):
    3440x1440@119.96100 at 0x0
    description: Samsung Electric Company Odyssey G85SB H1AK500000
    make: Samsung Electric Company
    model: Odyssey G85SB
    serial: H1AK500000
    active workspace: 2 (2)
    special workspace: 0 ()
    reserved: 0 32 0 0
    scale: 1.00
    transform: 0
    focused: yes
    dpmsStatus: 1
    vrr: 0
    activelyTearing: false
    currentFormat: XRGB8888
    availableModes: 3440x1440@119.96Hz 3440x1440@59.96Hz 3840x2160@60.00Hz 3840x2160@59.94Hz 3840x2160@60.00Hz 3840x2160@50.00Hz 3840x2160@30.00Hz 3840x2160@29.97Hz 2560x1440@120.00Hz 2560x1440@59.95Hz 1920x1200@119.96Hz 1920x1080@120.00Hz 1920x1080@120.00Hz 1920x1080@119.88Hz 1920x1080@60.00Hz 1920x1080@60.00Hz 1920x1080@59.94Hz 1920x1080@50.00Hz 1600x1200@119.96Hz 1680x1050@59.95Hz 1600x900@60.00Hz 1280x1024@75.03Hz 1280x1024@60.02Hz 1440x900@59.89Hz 1280x800@59.81Hz 1152x864@75.00Hz 1280x720@60.00Hz 1280x720@60.00Hz 1280x720@59.94Hz 1280x720@50.00Hz 1024x768@75.03Hz 1024x768@70.07Hz 1024x768@60.00Hz 800x600@75.00Hz 800x600@72.19Hz 800x600@60.32Hz 720x576@50.00Hz 720x480@60.00Hz 720x480@59.94Hz 640x480@75.00Hz 640x480@60.00Hz 640x480@59.94Hz

GAME MODE

$ sudo edid-decode /sys/class/drm/card0-DP-1/edid | rg '^\s*(Model: \d+)' -r '$1'
Model: 29426
$ hyprctl monitors
Monitor DP-1 (ID 0):
    3440x1440@174.96201 at 0x0
    description: Samsung Electric Company Odyssey G85SB H1AK500000
    make: Samsung Electric Company
    model: Odyssey G85SB
    serial: H1AK500000
    active workspace: 2 (2)
    special workspace: 0 ()
    reserved: 0 32 0 0
    scale: 1.00
    transform: 0
    focused: yes
    dpmsStatus: 1
    vrr: 0
    activelyTearing: false
    currentFormat: XRGB8888
    availableModes: 3440x1440@119.96Hz 3440x1440@59.96Hz 3440x1440@174.96Hz 3440x1440@120.03Hz 3440x1440@96.04Hz 3440x1440@72.02Hz 3440x1440@60.02Hz 3440x1440@50.01Hz 3440x1440@48.01Hz 2560x1440@174.97Hz 2560x1440@120.00Hz 2560x1440@59.95Hz 1920x1200@119.96Hz 1920x1080@174.92Hz 1920x1080@120.00Hz 1920x1080@120.00Hz 1920x1080@119.88Hz 1920x1080@60.00Hz 1920x1080@59.94Hz 1600x1200@119.96Hz 1680x1050@59.95Hz 1600x900@60.00Hz 1280x1024@60.02Hz 1440x900@59.89Hz 1280x800@59.81Hz 1280x720@60.00Hz 1280x720@60.00Hz 1280x720@59.94Hz 1024x768@60.00Hz 800x600@60.32Hz 720x480@60.00Hz 720x480@59.94Hz 640x480@60.00Hz 640x480@59.94Hz
vaxerski commented 5 months ago

use desc: and the description

gardk commented 5 months ago

I can't use desc: with the same string twice, and I wouldn't expect to be able to.

But I already tried it, the snippet below does not make changing the mode on the monitor change the refresh rate:

monitor=desc:Samsung Electric Company Odyssey G85SB H1AK500000,3440x1440@175,auto,1
monitor=desc:Samsung Electric Company Odyssey G85SB H1AK500000,3440x1440@120,auto,1

If I use a single stanza with monitor=DP-1,highrr,auto,1 it does work as expected, but I would consider that a "workaround" of sorts.

As you can see from the command outputs, Hyprland is reporting all the same information for both modes while the EDID output doesn't. If Hyprland exposed that "Model" field from the EDID then I could configure this monitor per mode but as it stands I can't.

vaxerski commented 5 months ago

ah. My bad.

gardk commented 5 months ago

So I just had a look at this and it needs fixing upstream in wlroots first. It's a tiny patch for wlroots but I'm not allowed to fork on the freedesktop gitlab to make a PR. Would it be easy for you to try getting this merged upstream or should I go through their verification process?

Patched my local copy of Hyprland to use this as well and it seems to work from my 2 minutes of testing. I'm including that patch here, would you prefer a PR maybe? I was thinking that might not be worthwile when the wlroots submodule isn't patched.

Patches ```patch diff --git a/backend/drm/util.c b/backend/drm/util.c index a14e5e1e..cfac5cc8 100644 --- a/backend/drm/util.c +++ b/backend/drm/util.c @@ -78,10 +78,11 @@ void parse_edid(struct wlr_drm_connector *conn, size_t len, const uint8_t *data) if (!manu) { manu = pnp_id; } - output->make = strdup(manu); + output->make = strdup(manu); output->model = di_info_get_model(info); output->serial = di_info_get_serial(info); + output->product_code = vendor_product->product; di_info_destroy(info); } diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index d349f8ff..f1e4ebb3 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -135,6 +135,7 @@ struct wlr_output { char *name; char *description; // may be NULL char *make, *model, *serial; // may be NULL + uint16_t product_code; int32_t phys_width, phys_height; // mm // Note: some backends may have zero modes ``` ```patch diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index 903b7d7f..8acaa4e0 100644 --- a/src/debug/HyprCtl.cpp +++ b/src/debug/HyprCtl.cpp @@ -132,12 +132,12 @@ std::string monitorsRequest(eHyprCtlOutputFormat format, std::string request) { continue; result += - std::format("Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\tspecial " + std::format("Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tproduct_code: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\tspecial " "workspace: {} ({})\n\treserved: {} " "{} {} {}\n\tscale: {:.2f}\n\ttransform: " "{}\n\tfocused: {}\n\tdpmsStatus: {}\n\tvrr: {}\n\tactivelyTearing: {}\n\tcurrentFormat: {}\n\tavailableModes: {}\n\n", m->szName, m->ID, (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y, m->szShortDescription, - (m->output->make ? m->output->make : ""), (m->output->model ? m->output->model : ""), (m->output->serial ? m->output->serial : ""), m->activeWorkspace, + (m->output->make ? m->output->make : ""), (m->output->model ? m->output->model : ""), m->output->product_code, (m->output->serial ? m->output->serial : ""), m->activeWorkspace, (m->activeWorkspace == -1 ? "" : g_pCompositor->getWorkspaceByID(m->activeWorkspace)->m_szName), m->specialWorkspaceID, getWorkspaceNameFromSpecialID(m->specialWorkspaceID), (int)m->vecReservedTopLeft.x, (int)m->vecReservedTopLeft.y, (int)m->vecReservedBottomRight.x, (int)m->vecReservedBottomRight.y, m->scale, (int)m->transform, (m.get() == g_pCompositor->m_pLastMonitor ? "yes" : "no"), (int)m->dpmsStatus, diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index 3edb7ca4..b9bab59f 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -52,6 +52,7 @@ void CMonitor::onConnect(bool noRule) { return; } + productNumber = output->product_code; szName = output->name; szDescription = output->description ? output->description : ""; @@ -362,6 +363,13 @@ bool CMonitor::matchesStaticSelector(const std::string& selector) const { const auto DESCRIPTIONSELECTOR = selector.substr(5); return DESCRIPTIONSELECTOR == szShortDescription || DESCRIPTIONSELECTOR == szDescription; + } else if (selector.starts_with("product_code:")) { + try { + return std::stoi(selector.substr(13)) == productNumber; + } catch (std::exception &e) { + Debug::log(ERR, "Error in matchesStaticselector: invalid monitor product number"); + return false; + } } else { // match by selector return szName == selector; diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index 051c5305..67346673 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -62,6 +62,7 @@ class CMonitor { float setScale = 1; // scale set by cfg float scale = 1; // real scale + uint16_t productNumber = 0; std::string szName = ""; std::string szDescription = ""; std::string szShortDescription = ""; diff --git a/subprojects/wlroots b/subprojects/wlroots index 50eae512..c573cc33 160000 --- a/subprojects/wlroots +++ b/subprojects/wlroots @@ -1 +1 @@ -Subproject commit 50eae512d9cecbf0b3b1898bb1f0b40fa05fe19b +Subproject commit c573cc33edf392da8e39df11dac52258d2f4ee0f ```
gardk commented 5 months ago

I'll try getting myself verified and make a PR over at wlroots, then if it gets accepted I'll make a PR here.

izmyname commented 1 month ago

I'll try getting myself verified and make a PR over at wlroots, then if it gets accepted I'll make a PR here.

Again, wlroots. Probably, move to the aquamarine repo?

gardk commented 1 month ago

Oh wow I forgot about this.

Decided to go for a more dynamic approach after activity on my wlroots MR died, and I don't use Hyprland anymore so I'm not interested in working on this. Closing.

That said, if you landed here because you're interested in something similar @izmyname, I recommend you open an issue about specifying monitors using a hash of their output name and EDID. That approach would be a lot more generally useful, and also allows handling the specific case of this issue as well.