From 95ea28e6b059e65a328bc4dc273e1b33e316a186 Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Thu, 7 May 2026 00:19:47 -0400 Subject: [PATCH] displayinfo_linux: parse resolution after the name token, validate bounds, accept any modern resolution --- src/wg/displayinfo_linux.cpp | 101 +++++++++++++++++------------------ 1 file changed, 50 insertions(+), 51 deletions(-) diff --git a/src/wg/displayinfo_linux.cpp b/src/wg/displayinfo_linux.cpp index 696927c..ce0e95f 100644 --- a/src/wg/displayinfo_linux.cpp +++ b/src/wg/displayinfo_linux.cpp @@ -1,25 +1,58 @@ -// src/wg/displayinfo_linux.cpp — Linux implementation of display enumeration. +// src/wg/displayinfo_linux.cpp - Linux implementation of display enumeration. // // Uses xrandr subprocess to query connected displays. -// Parses lines matching: connected [primary] ++ +// Parses lines matching: connected [primary] ++ ... #include "displayinfo.h" #if defined(__linux__) +#include #include +#include #include #include #include namespace wg { +// Parse "WxH" out of a region of a string. Returns true on success and fills +// (w, h) with values that satisfy 640 <= w <= 10000, 480 <= h <= 10000. +// +// Searches the whole input. Caller is expected to have already stripped the +// leading output-name token so we don't pick up digits in names like "DP-1". +static bool parseResolution(const char *s, int *w, int *h) { + while (*s) { + if (std::isdigit(static_cast(*s))) { + int width = 0; + int height = 0; + int consumed = 0; + if (std::sscanf(s, "%dx%d%n", &width, &height, &consumed) == 2 + && consumed > 0) { + // Make sure the next char is end, '+', ' ' or '_' so we don't + // accept things like "12x3" mid-token. + char next = s[consumed]; + bool boundary = (next == '\0' || next == ' ' || next == '+' + || next == '_' || next == '\t' || next == ','); + if (boundary + && width >= 640 && width <= 10000 + && height >= 480 && height <= 10000) { + *w = width; + *h = height; + return true; + } + } + } + ++s; + } + return false; +} + std::vector enumerateDisplays() { std::vector displays; FILE *fp = popen("xrandr --query 2>/dev/null", "r"); if (!fp) { - // Fallback: return default display DisplayInfo def; def.name = "default"; def.friendlyName = "default"; @@ -32,78 +65,44 @@ std::vector enumerateDisplays() { char line[512]; while (fgets(line, sizeof(line), fp) != nullptr) { - // Remove trailing newline size_t len = strlen(line); - if (len > 0 && line[len - 1] == '\n') { + if (len > 0 && line[len - 1] == '\n') line[len - 1] = '\0'; - } - // Skip lines that don't look like output lines (no "connected") - if (strstr(line, "connected") == nullptr) { + // Only consider lines reporting a connected output. + if (strstr(line, " connected") == nullptr) continue; - } - // Parse: connected [primary] + // Extract the output name (first whitespace-delimited token). char name[128] = {}; - char resolution[64] = {}; - int isPrimary = 0; - - // Extract name (first token before space) char *space = strchr(line, ' '); if (!space) continue; - size_t nameLen = space - line; + size_t nameLen = static_cast(space - line); if (nameLen >= sizeof(name)) nameLen = sizeof(name) - 1; memcpy(name, line, nameLen); name[nameLen] = '\0'; - // Check if "primary" is in the line - if (strstr(line, "primary") != nullptr) { - isPrimary = 1; - } - - // Extract resolution: look for pattern like "1920x1080" - char *res = strstr(line, "1280x720"); - if (!res) res = strstr(line, "1920x1080"); - if (!res) res = strstr(line, "2560x1440"); - if (!res) res = strstr(line, "3840x2160"); - if (!res) { - // More general: look for digits followed by 'x' and more digits - res = line; - while (*res) { - if (isdigit(*res)) { - char *xpos = strchr(res, 'x'); - if (xpos && isdigit(xpos[1])) { - break; - } - } - res++; - } - if (!*res) continue; - } + const bool isPrimary = (strstr(line, " primary") != nullptr); + // Search for the resolution AFTER the name token so we don't pick up + // digits embedded in names like "DP-1" or "eDP-1". + const char *afterName = space; int w = 0, h = 0; - if (sscanf(res, "%dx%d", &w, &h) != 2) { + if (!parseResolution(afterName, &w, &h)) continue; - } - - // Only add if we have valid dimensions - if (w <= 0 || h <= 0) { - continue; - } DisplayInfo info; - info.name = std::string(name); + info.name = std::string(name); info.friendlyName = std::string(name); - info.width = w; - info.height = h; - info.isPrimary = isPrimary != 0; + info.width = w; + info.height = h; + info.isPrimary = isPrimary; displays.push_back(info); } pclose(fp); - // If xrandr returned nothing, fallback to default if (displays.empty()) { DisplayInfo def; def.name = "default";