Skip to content

Commit 9a6b9e5

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 9a6b9e5

1 file changed

Lines changed: 60 additions & 0 deletions

File tree

  • src/detection/displayserver/linux

src/detection/displayserver/linux/drm.c

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

66
#ifdef __linux__
77
#include <dirent.h>
8+
#include <sys/ioctl.h>
9+
#include <fcntl.h>
10+
#include <stdlib.h>
11+
12+
13+
#if __has_include(<drm/drm.h>) && __has_include(<drm/drm_mode.h>)
14+
#include <drm/drm.h>
15+
#include <drm/drm_mode.h>
16+
#define FF_HAVE_DRM_UAPI
17+
#endif
18+
819

920
static const char* drmParseSysfs(FFDisplayServerResult* result) {
1021
const char* drmDirPath = "/sys/class/drm/";
@@ -76,6 +87,55 @@ static const char* drmParseSysfs(FFDisplayServerResult* result) {
7687
ffEdidGetPhysicalSize(edidData, &physicalWidth, &physicalHeight);
7788
}
7889

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

0 commit comments

Comments
 (0)