Skip to content

Commit 48db217

Browse files
committed
fix(display): query active CRTC mode for refresh rate on sysfs path
On non-Wayland consoles (kmscon), fastfetch reads EDID and reports the preferred timing refresh rate (e.g. 60Hz) even when the display is actually running at a different mode (e.g. 240Hz). This happens when fastfetch is compiled without libdrm support (FF_HAVE_DRM not defined), causing it to fall through to the sysfs-only drmParseSysfs path which only reads EDID. Add a raw DRM ioctl helper that queries the active CRTC mode via DRM_IOCTL_MODE_GETCRTC without requiring libdrm linkage. The helper navigates connector -> encoder -> CRTC using minimal inline struct definitions matching the kernel UAPI drm_mode.h. Fixes #2392
1 parent 4594734 commit 48db217

1 file changed

Lines changed: 55 additions & 0 deletions

File tree

  • src/detection/displayserver/linux

src/detection/displayserver/linux/drm.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@
55

66
#ifdef __linux__
77
#include <dirent.h>
8+
// cppcheck-suppress missingIncludeSystem
9+
#include <sys/ioctl.h>
10+
#include <fcntl.h>
11+
// cppcheck-suppress missingIncludeSystem
12+
#include <stdlib.h>
13+
14+
#include <drm/drm.h>
15+
#include <drm/drm_mode.h>
816

917
static const char* drmParseSysfs(FFDisplayServerResult* result) {
1018
const char* drmDirPath = "/sys/class/drm/";
@@ -76,6 +84,53 @@ static const char* drmParseSysfs(FFDisplayServerResult* result) {
7684
ffEdidGetPhysicalSize(edidData, &physicalWidth, &physicalHeight);
7785
}
7886

87+
// Try to get the actual active refresh rate from the CRTC.
88+
// EDID preferred timing (e.g. 60Hz) can differ from the active mode (e.g. 240Hz)
89+
// when the display is set to a non-preferred mode with the same resolution.
90+
if (width > 0 && height > 0) {
91+
// Extract card number from entry name like "card1-eDP-1"
92+
const char* entryName = entry->d_name;
93+
if (ffStrStartsWith(entryName, "card")) {
94+
entryName += sizeof("card") - 1;
95+
// Parse card number directly from d_name
96+
int cardNum = 0;
97+
for (const char* p = entryName; *p >= '0' && *p <= '9'; p++)
98+
cardNum = cardNum * 10 + (*p - '0');
99+
100+
if (cardNum >= 0) {
101+
FF_STRBUF_AUTO_DESTROY cardPath = ffStrbufCreateF("/dev/dri/card%d", cardNum);
102+
103+
// Read connector_id from sysfs
104+
FF_STRBUF_AUTO_DESTROY connIdPath = ffStrbufCreateA(64);
105+
ffStrbufAppendS(&connIdPath, drmDirPath);
106+
ffStrbufAppendS(&connIdPath, entry->d_name);
107+
ffStrbufAppendS(&connIdPath, "/connector_id");
108+
char connIdBuf[16] = {};
109+
if (ffReadFileData(connIdPath.chars, ARRAY_SIZE(connIdBuf), connIdBuf) > 0) {
110+
uint32_t connId = (uint32_t)strtoul(connIdBuf, NULL, 10);
111+
if (connId > 0) {
112+
int fd = open(cardPath.chars, O_RDONLY | O_CLOEXEC);
113+
if (fd >= 0) {
114+
// Query active CRTC mode via raw DRM ioctls
115+
struct drm_mode_get_connector conn = { .connector_id = connId };
116+
if (ioctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn) >= 0 && conn.encoder_id) {
117+
struct drm_mode_get_encoder enc = { .encoder_id = conn.encoder_id };
118+
if (ioctl(fd, DRM_IOCTL_MODE_GETENCODER, &enc) >= 0 && enc.crtc_id) {
119+
struct drm_mode_crtc crtc = { .crtc_id = enc.crtc_id };
120+
if (ioctl(fd, DRM_IOCTL_MODE_GETCRTC, &crtc) >= 0) {
121+
if (crtc.mode_valid && crtc.mode.hdisplay == (uint16_t)width && crtc.mode.vdisplay == (uint16_t)height)
122+
refreshRate = (double)crtc.mode.vrefresh;
123+
}
124+
}
125+
}
126+
close(fd);
127+
}
128+
}
129+
}
130+
}
131+
}
132+
}
133+
79134
FFDisplayResult* item = ffdsAppendDisplay(
80135
result,
81136
width,

0 commit comments

Comments
 (0)