Files
fastfetch/src/detection/displayserver/linux/xlib.c
T

357 lines
11 KiB
C
Raw Normal View History

2022-09-06 15:42:37 +02:00
#include "displayserver_linux.h"
2022-01-02 16:55:26 +01:00
#ifdef FF_HAVE_X11
#include "common/library.h"
#include "common/parsing.h"
2023-02-01 15:56:25 +01:00
#include "util/stringUtils.h"
2022-01-02 16:55:26 +01:00
#include <X11/Xlib.h>
2022-01-02 18:42:58 +01:00
typedef struct X11PropertyData
{
FF_LIBRARY_SYMBOL(XInternAtom)
FF_LIBRARY_SYMBOL(XGetWindowProperty)
2022-12-02 11:29:33 +08:00
FF_LIBRARY_SYMBOL(XFree)
2022-01-02 18:42:58 +01:00
} X11PropertyData;
2022-01-05 14:40:57 +01:00
static bool x11InitPropertyData(void* libraryHandle, X11PropertyData* propertyData)
{
2022-09-10 13:36:15 +08:00
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, XInternAtom, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, XGetWindowProperty, false)
2022-12-02 11:29:33 +08:00
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, XFree, false)
2022-01-05 14:40:57 +01:00
return true;
}
2022-01-02 18:42:58 +01:00
static unsigned char* x11GetProperty(X11PropertyData* data, Display* display, Window window, const char* request)
{
Atom requestAtom = data->ffXInternAtom(display, request, False);
if(requestAtom == None)
return NULL;
Atom actualType;
unsigned long unused;
unsigned char* result = NULL;
2022-12-02 11:29:33 +08:00
if(data->ffXGetWindowProperty(display, window, requestAtom, 0, 64, False, AnyPropertyType, &actualType, (int*) &unused, &unused, &unused, &result) != Success)
return NULL;
2022-01-02 18:42:58 +01:00
return result;
}
static void x11DetectWMFromEWMH(X11PropertyData* data, Display* display, FFDisplayServerResult* result)
{
if(result->wmProcessName.length > 0 || ffStrbufCompS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND) == 0)
2022-01-02 18:42:58 +01:00
return;
2022-01-05 14:40:57 +01:00
Window* wmWindow = (Window*) x11GetProperty(data, display, DefaultRootWindow(display), "_NET_SUPPORTING_WM_CHECK");
if(wmWindow == NULL)
2022-01-02 18:42:58 +01:00
return;
2022-12-02 11:29:33 +08:00
char* wmName = (char*) x11GetProperty(data, display, *wmWindow, "_NET_WM_NAME");
2022-01-02 18:42:58 +01:00
if(wmName == NULL)
2022-12-02 11:29:33 +08:00
wmName = (char*) x11GetProperty(data, display, *wmWindow, "WM_NAME");
2022-12-02 11:29:33 +08:00
if(ffStrSet(wmName))
ffStrbufSetS(&result->wmProcessName, wmName);
2022-01-02 16:55:26 +01:00
2022-12-02 11:29:33 +08:00
data->ffXFree(wmName);
data->ffXFree(wmWindow);
2022-01-02 16:55:26 +01:00
}
void ffdsConnectXlib(FFDisplayServerResult* result)
2022-01-02 16:55:26 +01:00
{
2023-10-25 16:24:24 +08:00
FF_LIBRARY_LOAD(x11, &instance.config.library.libX11, , "libX11" FF_LIBRARY_EXTENSION, 7, "libX11-xcb" FF_LIBRARY_EXTENSION, 2)
2022-01-02 16:55:26 +01:00
FF_LIBRARY_LOAD_SYMBOL(x11, XOpenDisplay,)
FF_LIBRARY_LOAD_SYMBOL(x11, XCloseDisplay,)
2022-01-02 18:42:58 +01:00
X11PropertyData propertyData;
2022-01-05 14:40:57 +01:00
bool propertyDataInitialized = x11InitPropertyData(x11, &propertyData);
2022-01-02 18:42:58 +01:00
2022-01-02 16:55:26 +01:00
Display* display = ffXOpenDisplay(x11);
if(display == NULL)
return;
2022-01-05 14:40:57 +01:00
if(propertyDataInitialized && ScreenCount(display) > 0)
x11DetectWMFromEWMH(&propertyData, display, result);
2022-01-02 16:55:26 +01:00
2022-01-05 14:40:57 +01:00
for(int i = 0; i < ScreenCount(display); i++)
{
Screen* screen = ScreenOfDisplay(display, i);
ffdsAppendDisplay(result,
(uint32_t) WidthOfScreen(screen),
(uint32_t) HeightOfScreen(screen),
0,
(uint32_t) WidthOfScreen(screen),
(uint32_t) HeightOfScreen(screen),
2023-06-12 10:35:28 +08:00
0,
NULL,
2023-06-16 14:28:31 +08:00
FF_DISPLAY_TYPE_UNKNOWN,
2023-07-08 10:06:04 +08:00
false,
0
2022-01-05 14:40:57 +01:00
);
}
2022-01-02 18:42:58 +01:00
2022-01-02 16:55:26 +01:00
ffXCloseDisplay(display);
//If wayland hasn't set this, connection failed for it. So we are running only a X Server, not XWayland.
if(result->wmProtocolName.length == 0)
ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_X11);
2022-01-02 16:55:26 +01:00
}
#else
void ffdsConnectXlib(FFDisplayServerResult* result)
2022-01-02 16:55:26 +01:00
{
//Do nothing. WM / DE detection will use environment vars to detect as much as possible.
FF_UNUSED(result);
2022-01-02 16:55:26 +01:00
}
#endif //FF_HAVE_X11
#ifdef FF_HAVE_XRANDR
2023-08-10 09:35:00 +08:00
#include "util/edidHelper.h"
2022-01-02 16:55:26 +01:00
#include <X11/extensions/Xrandr.h>
typedef struct XrandrData
{
2023-08-10 09:35:00 +08:00
FF_LIBRARY_SYMBOL(XInternAtom)
FF_LIBRARY_SYMBOL(XGetAtomName);
2023-08-26 05:27:31 -04:00
FF_LIBRARY_SYMBOL(XFree);
2022-01-02 16:55:26 +01:00
FF_LIBRARY_SYMBOL(XRRConfigCurrentRate)
FF_LIBRARY_SYMBOL(XRRGetMonitors)
FF_LIBRARY_SYMBOL(XRRGetScreenResourcesCurrent)
2022-01-02 16:55:26 +01:00
FF_LIBRARY_SYMBOL(XRRGetOutputInfo)
2023-08-10 09:35:00 +08:00
FF_LIBRARY_SYMBOL(XRRGetOutputProperty)
2022-01-02 16:55:26 +01:00
FF_LIBRARY_SYMBOL(XRRGetCrtcInfo)
FF_LIBRARY_SYMBOL(XRRFreeCrtcInfo)
FF_LIBRARY_SYMBOL(XRRFreeOutputInfo)
FF_LIBRARY_SYMBOL(XRRFreeScreenResources)
FF_LIBRARY_SYMBOL(XRRFreeMonitors)
//Init once
Display* display;
2022-01-05 14:40:57 +01:00
FFDisplayServerResult* result;
2022-01-02 16:55:26 +01:00
//Init per screen
XRRScreenResources* screenResources;
} XrandrData;
2023-06-16 10:21:53 +08:00
static double xrandrHandleMode(XrandrData* data, RRMode mode)
2022-01-02 16:55:26 +01:00
{
2022-01-05 14:40:57 +01:00
for(int i = 0; i < data->screenResources->nmode; i++)
2022-01-02 16:55:26 +01:00
{
2022-01-05 14:40:57 +01:00
if(data->screenResources->modes[i].id == mode)
2023-06-16 10:21:53 +08:00
{
XRRModeInfo* modeInfo = &data->screenResources->modes[i];
return (double) modeInfo->dotClock / (double) (modeInfo->hTotal * modeInfo->vTotal);
}
2022-01-02 16:55:26 +01:00
}
return 0;
2022-01-02 16:55:26 +01:00
}
2023-08-10 09:35:00 +08:00
static bool xrandrHandleCrtc(XrandrData* data, RRCrtc crtc, FFstrbuf* name, bool primary)
2022-01-02 16:55:26 +01:00
{
2023-01-18 01:37:47 +08:00
//We do the check here, because we want the best fallback display if this call failed
2022-01-05 14:40:57 +01:00
if(data->screenResources == NULL)
2022-01-02 16:55:26 +01:00
return false;
2022-01-05 14:40:57 +01:00
XRRCrtcInfo* crtcInfo = data->ffXRRGetCrtcInfo(data->display, data->screenResources, crtc);
if(crtcInfo == NULL)
2022-01-02 16:55:26 +01:00
return false;
2023-06-16 10:21:53 +08:00
uint32_t rotation;
switch (crtcInfo->rotation)
{
case RR_Rotate_90:
rotation = 90;
break;
case RR_Rotate_180:
rotation = 180;
break;
case RR_Rotate_270:
rotation = 270;
break;
default:
rotation = 0;
break;
}
bool res = ffdsAppendDisplay(
2022-01-05 14:40:57 +01:00
data->result,
(uint32_t) crtcInfo->width,
(uint32_t) crtcInfo->height,
2023-06-16 10:21:53 +08:00
xrandrHandleMode(data, crtcInfo->mode),
(uint32_t) crtcInfo->width,
(uint32_t) crtcInfo->height,
2023-06-16 10:21:53 +08:00
rotation,
2023-08-10 09:35:00 +08:00
name,
2023-06-16 14:28:31 +08:00
FF_DISPLAY_TYPE_UNKNOWN,
2023-07-08 10:06:04 +08:00
primary,
0
2022-01-05 14:40:57 +01:00
);
2022-05-23 15:50:25 +02:00
data->ffXRRFreeCrtcInfo(crtcInfo);
return res;
2022-01-02 16:55:26 +01:00
}
2023-08-10 09:35:00 +08:00
static bool xrandrHandleOutput(XrandrData* data, RROutput output, FFstrbuf* name, bool primary)
2022-01-02 16:55:26 +01:00
{
2022-01-05 14:40:57 +01:00
XRROutputInfo* outputInfo = data->ffXRRGetOutputInfo(data->display, data->screenResources, output);
if(outputInfo == NULL)
2022-01-02 16:55:26 +01:00
return false;
2023-08-10 09:35:00 +08:00
Atom atomEdid = data->ffXInternAtom(data->display, "EDID", true);
if (atomEdid != None)
{
2023-08-26 05:27:31 -04:00
int actual_format = 0;
unsigned long nitems = 0, bytes_after = 0;
Atom actual_type = None;
2023-08-10 09:35:00 +08:00
uint8_t* edidData = NULL;
2023-08-26 05:27:31 -04:00
if (data->ffXRRGetOutputProperty(data->display, output, atomEdid, 0, 100, false, false, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &edidData) == Success)
2023-08-10 09:35:00 +08:00
{
if (nitems >= 128)
{
ffStrbufClear(name);
ffEdidGetName(edidData, name);
}
}
2023-08-26 05:52:06 -04:00
if (edidData)
data->ffXFree(edidData);
2023-08-10 09:35:00 +08:00
}
bool res = xrandrHandleCrtc(data, outputInfo->crtc, name, primary);
2022-01-05 14:40:57 +01:00
data->ffXRRFreeOutputInfo(outputInfo);
return res;
2022-01-02 16:55:26 +01:00
}
static bool xrandrHandleMonitor(XrandrData* data, XRRMonitorInfo* monitorInfo)
{
2022-01-05 14:40:57 +01:00
bool foundOutput = false;
2023-08-26 05:27:31 -04:00
char* xname = data->ffXGetAtomName(data->display, monitorInfo->name);
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreateS(xname);
data->ffXFree(xname);
2022-01-02 16:55:26 +01:00
for(int i = 0; i < monitorInfo->noutput; i++)
{
2023-08-10 09:35:00 +08:00
if(xrandrHandleOutput(data, monitorInfo->outputs[i], &name, monitorInfo->primary))
2022-01-05 14:40:57 +01:00
foundOutput = true;
2022-01-02 16:55:26 +01:00
}
2023-01-18 01:37:47 +08:00
return foundOutput ? true : ffdsAppendDisplay(
2022-01-05 14:40:57 +01:00
data->result,
(uint32_t) monitorInfo->width,
(uint32_t) monitorInfo->height,
0,
(uint32_t) monitorInfo->width,
(uint32_t) monitorInfo->height,
0,
2023-08-10 09:35:00 +08:00
&name,
2023-06-16 14:28:31 +08:00
FF_DISPLAY_TYPE_UNKNOWN,
2023-07-08 10:06:04 +08:00
!!monitorInfo->primary,
0
2022-01-05 14:40:57 +01:00
);
2022-01-02 16:55:26 +01:00
}
2022-01-05 14:40:57 +01:00
static bool xrandrHandleMonitors(XrandrData* data, Screen* screen)
2022-01-02 16:55:26 +01:00
{
2022-01-05 14:40:57 +01:00
int numberOfMonitors;
XRRMonitorInfo* monitorInfos = data->ffXRRGetMonitors(data->display, RootWindowOfScreen(screen), True, &numberOfMonitors);
if(monitorInfos == NULL)
return false;
bool foundAMonitor;
2022-01-02 16:55:26 +01:00
for(int i = 0; i < numberOfMonitors; i++)
{
2022-01-05 14:40:57 +01:00
if(xrandrHandleMonitor(data, &monitorInfos[i]))
2022-01-02 16:55:26 +01:00
foundAMonitor = true;
}
2022-01-05 14:40:57 +01:00
data->ffXRRFreeMonitors(monitorInfos);
2022-01-02 16:55:26 +01:00
return foundAMonitor;
}
static void xrandrHandleScreen(XrandrData* data, Screen* screen)
{
2022-01-05 14:40:57 +01:00
//Init screen resources
data->screenResources = data->ffXRRGetScreenResourcesCurrent(data->display, RootWindowOfScreen(screen));
2022-01-02 16:55:26 +01:00
2022-01-05 14:40:57 +01:00
bool ret = xrandrHandleMonitors(data, screen);
2022-01-02 16:55:26 +01:00
2022-01-05 14:40:57 +01:00
data->ffXRRFreeScreenResources(data->screenResources);
2022-01-02 16:55:26 +01:00
2022-01-05 14:40:57 +01:00
if(ret)
return;
2022-01-02 16:55:26 +01:00
2022-01-05 14:40:57 +01:00
//Fallback to screen
2023-01-18 01:37:47 +08:00
ffdsAppendDisplay(
2022-01-05 14:40:57 +01:00
data->result,
(uint32_t) WidthOfScreen(screen),
(uint32_t) HeightOfScreen(screen),
0,
(uint32_t) WidthOfScreen(screen),
(uint32_t) HeightOfScreen(screen),
0,
NULL,
2023-06-16 14:28:31 +08:00
FF_DISPLAY_TYPE_UNKNOWN,
2023-07-08 10:06:04 +08:00
false,
0
2022-01-05 14:40:57 +01:00
);
2022-01-02 16:55:26 +01:00
}
void ffdsConnectXrandr(FFDisplayServerResult* result)
2022-01-02 16:55:26 +01:00
{
2023-10-25 16:24:24 +08:00
FF_LIBRARY_LOAD(xrandr, &instance.config.library.libXrandr, , "libXrandr" FF_LIBRARY_EXTENSION, 3)
2022-01-02 16:55:26 +01:00
FF_LIBRARY_LOAD_SYMBOL(xrandr, XOpenDisplay,)
FF_LIBRARY_LOAD_SYMBOL(xrandr, XCloseDisplay,)
XrandrData data;
2023-08-10 09:35:00 +08:00
FF_LIBRARY_LOAD_SYMBOL_VAR(xrandr, data, XInternAtom,);
FF_LIBRARY_LOAD_SYMBOL_VAR(xrandr, data, XGetAtomName,);
2023-08-26 05:27:31 -04:00
FF_LIBRARY_LOAD_SYMBOL_VAR(xrandr, data, XFree,);
2022-09-10 13:36:15 +08:00
FF_LIBRARY_LOAD_SYMBOL_VAR(xrandr, data, XRRConfigCurrentRate,);
FF_LIBRARY_LOAD_SYMBOL_VAR(xrandr, data, XRRGetMonitors,);
FF_LIBRARY_LOAD_SYMBOL_VAR(xrandr, data, XRRGetScreenResourcesCurrent,);
2022-09-10 13:36:15 +08:00
FF_LIBRARY_LOAD_SYMBOL_VAR(xrandr, data, XRRGetOutputInfo,);
2023-08-10 09:35:00 +08:00
FF_LIBRARY_LOAD_SYMBOL_VAR(xrandr, data, XRRGetOutputProperty,);
2022-09-10 13:36:15 +08:00
FF_LIBRARY_LOAD_SYMBOL_VAR(xrandr, data, XRRGetCrtcInfo,);
FF_LIBRARY_LOAD_SYMBOL_VAR(xrandr, data, XRRFreeCrtcInfo,);
FF_LIBRARY_LOAD_SYMBOL_VAR(xrandr, data, XRRFreeOutputInfo,);
FF_LIBRARY_LOAD_SYMBOL_VAR(xrandr, data, XRRFreeScreenResources,);
FF_LIBRARY_LOAD_SYMBOL_VAR(xrandr, data, XRRFreeMonitors,);
2022-01-02 16:55:26 +01:00
2022-01-02 18:42:58 +01:00
X11PropertyData propertyData;
2022-01-05 14:40:57 +01:00
bool propertyDataInitialized = x11InitPropertyData(xrandr, &propertyData);
2022-01-02 18:42:58 +01:00
2022-01-02 16:55:26 +01:00
data.display = ffXOpenDisplay(NULL);
if(data.display == NULL)
return;
2022-01-05 14:40:57 +01:00
if(propertyDataInitialized && ScreenCount(data.display) > 0)
x11DetectWMFromEWMH(&propertyData, data.display, result);
data.result = result;
2022-01-02 16:55:26 +01:00
for(int i = 0; i < ScreenCount(data.display); i++)
xrandrHandleScreen(&data, ScreenOfDisplay(data.display, i));
ffXCloseDisplay(data.display);
//If wayland hasn't set this, connection failed for it. So we are running only a X Server, not XWayland.
if(result->wmProtocolName.length == 0)
ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_X11);
2022-01-02 16:55:26 +01:00
}
#else
void ffdsConnectXrandr(FFDisplayServerResult* result)
2022-01-02 16:55:26 +01:00
{
//Do nothing here. There are more x11 implementations to come.
FF_UNUSED(result);
2022-01-02 16:55:26 +01:00
}
#endif // FF_HAVE_XRANDR