mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-13 02:42:09 +02:00
Display (Linux): wlr-randr for hyprland
which support reporting fractional scale factors
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user