Display (Linux): wlr-randr for hyprland

which support reporting fractional scale factors
This commit is contained in:
李通洲
2024-04-17 22:09:43 +08:00
parent 073d6a9337
commit a561a96a5f
5 changed files with 168 additions and 42 deletions
+1
View File
@@ -400,6 +400,7 @@ if(LINUX)
src/detection/diskio/diskio_linux.c
src/detection/displayserver/linux/displayserver_linux.c
src/detection/displayserver/linux/drm.c
src/detection/displayserver/linux/wlroots.c
src/detection/displayserver/linux/wayland.c
src/detection/displayserver/linux/wmde.c
src/detection/displayserver/linux/xcb.c
@@ -1,4 +1,7 @@
#include "displayserver_linux.h"
#include "common/io/io.h"
#include "util/edidHelper.h"
#include "util/stringUtils.h"
#ifdef __FreeBSD__
#include "common/settings.h"
@@ -8,6 +11,20 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds)
{
if (instance.config.general.dsForceDrm == FF_DS_FORCE_DRM_TYPE_FALSE)
{
#ifdef __linux__
{
const char* desktopSession = getenv("DESKTOP_SESSION");
if (desktopSession && ffStrEquals(desktopSession, "hyprland"))
{
ffStrbufSetStatic(&ds->wmProcessName, "xdg-desktop-portal-hyprland");
ffStrbufSetStatic(&ds->wmPrettyName, FF_WM_PRETTY_HYPRLAND);
ffStrbufSetStatic(&ds->wmProtocolName, FF_WM_PROTOCOL_WAYLAND);
if (ffdsConnectWlroots(ds) == NULL)
return;
}
}
#endif
//We try wayland as our preferred display server, as it supports the most features.
//This method can't detect the name of our WM / DE
ffdsConnectWayland(ds);
@@ -57,3 +74,43 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds)
//This fills in missing information about WM / DE by using env vars and iterating processes
ffdsDetectWMDE(ds);
}
bool ffdsMatchDrmConnector(const char* connName, FFstrbuf* edidName)
{
// https://wayland.freedesktop.org/docs/html/apa.html#protocol-spec-wl_output-event-name
// The doc says that "do not assume that the name is a reflection of an underlying DRM connector, X11 connection, etc."
// However I can't find a better method to get the edid data
const char* drmDirPath = "/sys/class/drm/";
DIR* dirp = opendir(drmDirPath);
if(dirp == NULL)
return false;
struct dirent* entry;
while((entry = readdir(dirp)) != NULL)
{
const char* plainName = entry->d_name;
if (ffStrStartsWith(plainName, "card"))
{
const char* tmp = strchr(plainName + strlen("card"), '-');
if (tmp) plainName = tmp + 1;
}
if (ffStrEquals(plainName, connName))
{
ffStrbufAppendF(edidName, "%s%s/edid", drmDirPath, entry->d_name);
uint8_t edidData[128];
if(ffReadFileData(edidName->chars, sizeof(edidData), edidData) == sizeof(edidData))
{
ffStrbufClear(edidName);
ffEdidGetName(edidData, edidName);
closedir(dirp);
return true;
}
break;
}
}
ffStrbufClear(edidName);
closedir(dirp);
return false;
}
@@ -5,6 +5,9 @@
#include "detection/displayserver/displayserver.h"
bool ffdsMatchDrmConnector(const char* connName, FFstrbuf* edidName);
const char* ffdsConnectWlroots(FFDisplayServerResult* result);
void ffdsConnectWayland(FFDisplayServerResult* result);
void ffdsConnectXcbRandr(FFDisplayServerResult* result);
+2 -42
View File
@@ -8,7 +8,7 @@
#include "common/library.h"
#include "common/io/io.h"
#include "common/thread.h"
#include "util/edidHelper.h"
#include <wayland-client.h>
#include <sys/socket.h>
#include <assert.h>
@@ -103,46 +103,6 @@ static void waylandOutputGeometryListener(void *data,
}
}
static bool matchDrmConnector(const char* wlName, FFstrbuf* edidName)
{
// https://wayland.freedesktop.org/docs/html/apa.html#protocol-spec-wl_output-event-name
// The doc says that "do not assume that the name is a reflection of an underlying DRM connector, X11 connection, etc."
// However I can't find a better method to get the edid data
const char* drmDirPath = "/sys/class/drm/";
DIR* dirp = opendir(drmDirPath);
if(dirp == NULL)
return false;
struct dirent* entry;
while((entry = readdir(dirp)) != NULL)
{
const char* plainName = entry->d_name;
if (ffStrStartsWith(plainName, "card"))
{
const char* tmp = strchr(plainName + strlen("card"), '-');
if (tmp) plainName = tmp + 1;
}
if (ffStrEquals(plainName, wlName))
{
ffStrbufAppendF(edidName, "%s%s/edid", drmDirPath, entry->d_name);
uint8_t edidData[128];
if(ffReadFileData(edidName->chars, sizeof(edidData), edidData) == sizeof(edidData))
{
ffStrbufClear(edidName);
ffEdidGetName(edidData, edidName);
closedir(dirp);
return true;
}
break;
}
}
ffStrbufClear(edidName);
closedir(dirp);
return false;
}
static void waylandOutputNameListener(void *data, FF_MAYBE_UNUSED struct wl_output *output, const char *name)
{
WaylandDisplay* display = data;
@@ -150,7 +110,7 @@ static void waylandOutputNameListener(void *data, FF_MAYBE_UNUSED struct wl_outp
display->type = FF_DISPLAY_TYPE_BUILTIN;
else if(ffStrStartsWith(name, "HDMI-"))
display->type = FF_DISPLAY_TYPE_EXTERNAL;
matchDrmConnector(name, &display->edidName);
ffdsMatchDrmConnector(name, &display->edidName);
ffStrbufAppendS(&display->name, name);
}
+105
View File
@@ -0,0 +1,105 @@
#include "displayserver_linux.h"
#include "util/stringUtils.h"
#include "common/processing.h"
static inline void wrapYyjsonFree(yyjson_doc** doc)
{
assert(doc);
if (*doc)
yyjson_doc_free(*doc);
}
const char* ffdsConnectWlroots(FFDisplayServerResult* result)
{
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
if (ffProcessAppendStdOut(&buffer, (char* const[]){
"wlr-randr",
"--json",
NULL
}) != NULL || buffer.length == 0)
return "Running wlr-randr failed";
yyjson_doc* __attribute__((__cleanup__(wrapYyjsonFree))) doc = yyjson_read_opts(buffer.chars, buffer.length, 0, NULL, NULL);
if (!doc)
return "Failed to parse wlr-randr info";
yyjson_val* root = yyjson_doc_get_root(doc);
if (!yyjson_is_arr(root))
return "Battery info result is not a JSON array";
yyjson_val* device;
size_t idev, mdev;
yyjson_arr_foreach(root, idev, mdev, device)
{
yyjson_val* modes = yyjson_obj_get(device, "modes");
if (!yyjson_is_arr(modes))
continue;
if (!yyjson_get_bool(yyjson_obj_get(device, "enabled")))
continue;
yyjson_val* mode;
size_t imode, mmode;
yyjson_arr_foreach(modes, imode, mmode, mode)
{
if (!yyjson_is_obj(mode))
continue;
if (!yyjson_get_bool(yyjson_obj_get(mode, "current")))
continue;
uint32_t width = (uint32_t) yyjson_get_uint(yyjson_obj_get(mode, "width"));
uint32_t height = (uint32_t) yyjson_get_uint(yyjson_obj_get(mode, "height"));
if (width == 0 || height == 0)
continue;
double refreshRate = yyjson_get_real(yyjson_obj_get(mode, "refresh"));
double scale = (double) yyjson_get_real(yyjson_obj_get(device, "scale"));
const char* connName = yyjson_get_str(yyjson_obj_get(device, "name"));
FFDisplayType type = FF_DISPLAY_TYPE_UNKNOWN;
if(ffStrStartsWith(connName, "eDP-"))
type = FF_DISPLAY_TYPE_BUILTIN;
else if(ffStrStartsWith(connName, "HDMI-"))
type = FF_DISPLAY_TYPE_EXTERNAL;
FF_STRBUF_AUTO_DESTROY displayName = ffStrbufCreate();
if (!ffdsMatchDrmConnector(connName, &displayName))
ffStrbufSetS(&displayName, yyjson_get_str(yyjson_obj_get(device, "description")));
uint32_t rotation = 0;
const char* transform = yyjson_get_str(yyjson_obj_get(device, "transform"));
if (!ffStrEquals(transform, "normal"))
{
// 90 | flipped-90
if (ffStrEndsWith(transform, "90"))
rotation = 90;
else if (ffStrEndsWith(transform, "180"))
rotation = 180;
else if (ffStrEndsWith(transform, "270"))
rotation = 270;
}
if (rotation % 180 != 0)
{
uint32_t temp = width;
width = height;
height = temp;
}
ffdsAppendDisplay(result,
width,
height,
refreshRate,
(uint32_t) (width / scale),
(uint32_t) (height / scale),
rotation,
&displayName,
type,
false,
0
);
break;
}
}
return NULL;
}