master ohmu/fastfetch-openbsd / src / detection / displayserver / linux / xlib.c
ohmu  ·  2026-10-15
  1#include "displayserver_linux.h"
  2
  3#ifdef FF_HAVE_XRANDR
  4
  5    #include "common/library.h"
  6    #include "common/properties.h"
  7    #include "common/edidHelper.h"
  8    #include "common/strutil.h"
  9
 10    #include <X11/extensions/Xrandr.h>
 11    #include <X11/Xlib.h>
 12
 13typedef struct XrandrData {
 14    FF_LIBRARY_SYMBOL(XInternAtom)
 15    FF_LIBRARY_SYMBOL(XGetAtomName)
 16    FF_LIBRARY_SYMBOL(XGetWindowProperty)
 17    FF_LIBRARY_SYMBOL(XServerVendor)
 18    FF_LIBRARY_SYMBOL(XFree)
 19    FF_LIBRARY_SYMBOL(XRRGetMonitors)
 20    FF_LIBRARY_SYMBOL(XRRGetScreenResourcesCurrent)
 21    FF_LIBRARY_SYMBOL(XRRGetOutputInfo)
 22    FF_LIBRARY_SYMBOL(XRRGetOutputProperty)
 23    FF_LIBRARY_SYMBOL(XRRGetCrtcInfo)
 24    FF_LIBRARY_SYMBOL(XRRFreeCrtcInfo)
 25    FF_LIBRARY_SYMBOL(XRRFreeOutputInfo)
 26    FF_LIBRARY_SYMBOL(XRRFreeScreenResources)
 27    FF_LIBRARY_SYMBOL(XRRFreeMonitors)
 28
 29    // Init once
 30    Display* display;
 31    FFDisplayServerResult* result;
 32} XrandrData;
 33
 34static unsigned char* x11GetProperty(XrandrData* data, Display* display, Window window, const char* request) {
 35    Atom requestAtom = data->ffXInternAtom(display, request, False);
 36    if (requestAtom == None) {
 37        return nullptr;
 38    }
 39
 40    Atom actualType;
 41    unsigned long unused;
 42    unsigned char* result = nullptr;
 43
 44    if (data->ffXGetWindowProperty(display, window, requestAtom, 0, 64, False, AnyPropertyType, &actualType, (int*) &unused, &unused, &unused, &result) != Success) {
 45        return nullptr;
 46    }
 47
 48    return result;
 49}
 50
 51static uint8_t* xrandrGetProperty(XrandrData* data, RROutput output, const char* name, uint32_t* bufSize) {
 52    unsigned long size = 0;
 53    uint8_t* result = nullptr;
 54    Atom atomEdid = data->ffXInternAtom(data->display, name, true);
 55    if (atomEdid != None) {
 56        int actual_format = 0;
 57        unsigned long bytes_after = 0;
 58        Atom actual_type = None;
 59        if (data->ffXRRGetOutputProperty(data->display, output, atomEdid, 0, 100, false, false, AnyPropertyType, &actual_type, &actual_format, &size, &bytes_after, &result) == Success) {
 60            if (size == 0) {
 61                data->ffXFree(result);
 62            } else {
 63                if (bufSize) {
 64                    *bufSize = (uint32_t) size;
 65                }
 66                return result;
 67            }
 68        }
 69    }
 70
 71    return nullptr;
 72}
 73
 74static void x11DetectWMFromEWMH(XrandrData* data, FFDisplayServerResult* result) {
 75    if (result->wmProcessName.length > 0 || ffStrbufEqualS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND)) {
 76        return;
 77    }
 78
 79    Window* wmWindow = (Window*) x11GetProperty(data, data->display, DefaultRootWindow(data->display), "_NET_SUPPORTING_WM_CHECK");
 80    if (wmWindow == nullptr) {
 81        return;
 82    }
 83
 84    char* wmName = (char*) x11GetProperty(data, data->display, *wmWindow, "WM_NAME");
 85    if (!ffStrSet(wmName)) {
 86        wmName = (char*) x11GetProperty(data, data->display, *wmWindow, "_NET_WM_NAME");
 87    }
 88
 89    if (ffStrSet(wmName)) {
 90        ffStrbufSetS(&result->wmProcessName, wmName);
 91    }
 92
 93    data->ffXFree(wmName);
 94    data->ffXFree(wmWindow);
 95}
 96
 97static void x11FetchServerVendor(XrandrData* data, FFDisplayServerResult* result) {
 98    const char* serverVendor = data->ffXServerVendor(data->display);
 99    if (serverVendor && !ffStrEquals(serverVendor, "The X.Org Foundation")) {
100        ffStrbufSetS(&result->wmProtocolName, serverVendor);
101    }
102}
103
104static bool xrandrHandleCrtc(XrandrData* data, XRROutputInfo* output, FFstrbuf* name, bool primary, FFDisplayType displayType, uint8_t* edidData, uint32_t edidLength, XRRScreenResources* screenResources, uint8_t bitDepth, uint32_t dpi, bool randrEmulation) {
105    // We do the check here, because we want the best fallback display if this call failed
106    if (screenResources == nullptr) {
107        return false;
108    }
109
110    XRRCrtcInfo* crtcInfo = data->ffXRRGetCrtcInfo(data->display, screenResources, output->crtc);
111    if (crtcInfo == nullptr) {
112        return false;
113    }
114
115    uint32_t rotation;
116    switch (crtcInfo->rotation) {
117        case RR_Rotate_90:
118            rotation = 90;
119            break;
120        case RR_Rotate_180:
121            rotation = 180;
122            break;
123        case RR_Rotate_270:
124            rotation = 270;
125            break;
126        default:
127            rotation = 0;
128            break;
129    }
130
131    XRRModeInfo* currentMode = nullptr;
132    for (int i = 0; i < screenResources->nmode; i++) {
133        if (screenResources->modes[i].id == crtcInfo->mode) {
134            currentMode = &screenResources->modes[i];
135            break;
136        }
137    }
138
139    XRRModeInfo* preferredMode = output->npreferred > 0 ? &screenResources->modes[0] : nullptr;
140
141    FFDisplayResult* item = ffdsAppendDisplay(
142        data->result,
143        (uint32_t) (currentMode ? currentMode->width : crtcInfo->width),
144        (uint32_t) (currentMode ? currentMode->height : crtcInfo->height),
145        currentMode ? (double) currentMode->dotClock / (double) ((uint32_t) currentMode->hTotal * currentMode->vTotal) : 0,
146        dpi,
147        preferredMode ? (uint32_t) preferredMode->width : 0,
148        preferredMode ? (uint32_t) preferredMode->height : 0,
149        preferredMode ? (double) preferredMode->dotClock / (double) ((uint32_t) preferredMode->hTotal * preferredMode->vTotal) : 0,
150        rotation,
151        name,
152        displayType,
153        primary,
154        0,
155        (uint32_t) output->mm_width,
156        (uint32_t) output->mm_height,
157        randrEmulation
158            ? (currentMode ? "xlib-randr-emu-mode" : "xlib-randr-emu-crtc")
159            : (currentMode ? "xlib-randr-mode" : "xlib-randr-crtc"));
160
161    if (item) {
162        if (edidLength) {
163            item->hdrStatus = ffEdidGetHdrCompatible(edidData, edidLength) ? FF_DISPLAY_HDR_STATUS_SUPPORTED : FF_DISPLAY_HDR_STATUS_UNSUPPORTED;
164            ffEdidGetManufactureDate(edidData, &item->manufactureYear, &item->manufactureWeek);
165            ffEdidGetSerial(edidData, &item->serial);
166        }
167        item->bitDepth = bitDepth;
168        if ((rotation == 90 || rotation == 180) && !randrEmulation) {
169            // In XWayland mode, width / height has been swapped out of box
170            uint32_t tmp = item->width;
171            item->width = item->height;
172            item->height = tmp;
173        }
174    }
175
176    data->ffXRRFreeCrtcInfo(crtcInfo);
177    return !!item;
178}
179
180static bool xrandrHandleOutput(XrandrData* data, RROutput output, FFstrbuf* name, bool primary, FFDisplayType displayType, XRRScreenResources* screenResources, uint8_t bitDepth, uint32_t dpi) {
181    XRROutputInfo* outputInfo = data->ffXRRGetOutputInfo(data->display, screenResources, output);
182    if (outputInfo == nullptr) {
183        return false;
184    }
185
186    uint32_t edidLength = 0;
187    uint8_t* edidData = xrandrGetProperty(data, output, RR_PROPERTY_RANDR_EDID, &edidLength);
188
189    if (edidLength >= 128) {
190        ffStrbufClear(name);
191        ffEdidGetName(edidData, name);
192    } else {
193        edidLength = 0;
194    }
195
196    uint8_t* randrEmulation = xrandrGetProperty(data, output, "RANDR Emulation", nullptr);
197
198    bool res = xrandrHandleCrtc(data, outputInfo, name, primary, displayType, edidData, edidLength, screenResources, bitDepth, dpi, randrEmulation ? !!randrEmulation[0] : false);
199
200    if (edidData) {
201        data->ffXFree(edidData);
202    }
203    if (randrEmulation) {
204        data->ffXFree(randrEmulation);
205    }
206    data->ffXRRFreeOutputInfo(outputInfo);
207
208    return res;
209}
210
211static bool xrandrHandleMonitor(XrandrData* data, XRRMonitorInfo* monitorInfo, XRRScreenResources* screenResources, uint8_t bitDepth, uint32_t dpi) {
212    bool foundOutput = false;
213    char* xname = data->ffXGetAtomName(data->display, monitorInfo->name);
214    FF_STRBUF_AUTO_DESTROY name = ffStrbufCreateS(xname);
215    data->ffXFree(xname);
216    FFDisplayType displayType = ffdsGetDisplayType(name.chars);
217    for (int i = 0; i < monitorInfo->noutput; i++) {
218        if (xrandrHandleOutput(data, monitorInfo->outputs[i], &name, monitorInfo->primary, displayType, screenResources, bitDepth, dpi)) {
219            foundOutput = true;
220        }
221    }
222
223    if (foundOutput) {
224        return true;
225    }
226
227    FFDisplayResult* display = ffdsAppendDisplay(
228        data->result,
229        (uint32_t) monitorInfo->width,
230        (uint32_t) monitorInfo->height,
231        0,
232        dpi,
233        0,
234        0,
235        0,
236        0,
237        &name,
238        displayType,
239        !!monitorInfo->primary,
240        0,
241        (uint32_t) monitorInfo->mwidth,
242        (uint32_t) monitorInfo->mheight,
243        "xlib-randr-monitor");
244    if (display) {
245        display->bitDepth = bitDepth;
246    }
247    return !!display;
248}
249
250static bool xrandrHandleMonitors(XrandrData* data, Screen* screen) {
251    int numberOfMonitors;
252    XRRMonitorInfo* monitorInfos = data->ffXRRGetMonitors(data->display, RootWindowOfScreen(screen), True, &numberOfMonitors);
253    if (monitorInfos == nullptr) {
254        return false;
255    }
256
257    XRRScreenResources* screenResources = data->ffXRRGetScreenResourcesCurrent(data->display, RootWindowOfScreen(screen));
258
259    uint32_t dpi = 1;
260    char* resourceManager = (char*) x11GetProperty(data, data->display, screen->root, "RESOURCE_MANAGER");
261    if (resourceManager) {
262        FF_STRBUF_AUTO_DESTROY dpiStr = ffStrbufCreate();
263        if (ffParsePropLines(resourceManager, "Xft.dpi:", &dpiStr)) {
264            dpi = (uint32_t) ffStrbufToUInt(&dpiStr, 96);
265        }
266        data->ffXFree(resourceManager);
267    }
268    uint8_t bitDepth = (uint8_t) (screen->root_depth / 3);
269
270    bool foundAMonitor = false;
271
272    for (int i = 0; i < numberOfMonitors; i++) {
273        if (xrandrHandleMonitor(data, &monitorInfos[i], screenResources, bitDepth, dpi)) {
274            foundAMonitor = true;
275        }
276    }
277
278    data->ffXRRFreeMonitors(monitorInfos);
279    data->ffXRRFreeScreenResources(screenResources);
280
281    return foundAMonitor;
282}
283
284static void xrandrHandleScreen(XrandrData* data, Screen* screen) {
285    if (xrandrHandleMonitors(data, screen)) {
286        return;
287    }
288
289    // Fallback to screen
290    ffdsAppendDisplay(
291        data->result,
292        (uint32_t) WidthOfScreen(screen),
293        (uint32_t) HeightOfScreen(screen),
294        0,
295        0,
296        0,
297        0,
298        0,
299        0,
300        nullptr,
301        FF_DISPLAY_TYPE_UNKNOWN,
302        false,
303        RootWindowOfScreen(screen),
304        (uint32_t) WidthMMOfScreen(screen),
305        (uint32_t) HeightMMOfScreen(screen),
306        "xlib-randr-screen");
307}
308
309const char* ffdsConnectXrandr(FFDisplayServerResult* result) {
310    FF_LIBRARY_LOAD_MESSAGE(xrandr, "libXrandr" FF_LIBRARY_EXTENSION, 3)
311
312    FF_LIBRARY_LOAD_SYMBOL_MESSAGE(xrandr, XOpenDisplay)
313    FF_LIBRARY_LOAD_SYMBOL_MESSAGE(xrandr, XCloseDisplay)
314
315    XrandrData data;
316
317    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XInternAtom);
318    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XGetAtomName);
319    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XGetWindowProperty);
320    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XServerVendor);
321    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XFree);
322    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XRRGetMonitors);
323    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XRRGetScreenResourcesCurrent);
324    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XRRGetOutputInfo);
325    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XRRGetOutputProperty);
326    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XRRGetCrtcInfo);
327    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XRRFreeCrtcInfo);
328    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XRRFreeOutputInfo);
329    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XRRFreeScreenResources);
330    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XRRFreeMonitors);
331
332    data.display = ffXOpenDisplay(nullptr);
333    if (data.display == nullptr) {
334        return "XOpenDisplay() failed";
335    }
336
337    if (ScreenCount(data.display) > 0) {
338        x11DetectWMFromEWMH(&data, result);
339        x11FetchServerVendor(&data, result);
340    }
341
342    data.result = result;
343
344    for (int i = 0; i < ScreenCount(data.display); i++) {
345        xrandrHandleScreen(&data, ScreenOfDisplay(data.display, i));
346    }
347
348    ffXCloseDisplay(data.display);
349
350    // If wayland hasn't set this, connection failed for it. So we are running only a X Server, not XWayland.
351    if (result->wmProtocolName.length == 0) {
352        ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_X11);
353    }
354
355    return nullptr;
356}
357
358#else
359
360const char* ffdsConnectXrandr(FFDisplayServerResult* result) {
361    // Do nothing here. There are more x11 implementations to come.
362    FF_UNUSED(result);
363    return "Fastfetch was compiled without libXrandr support";
364}
365
366#endif // FF_HAVE_XRANDR