diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c9a7030b..8e8885b4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,12 +68,26 @@ else(VULKAN_FOUND) message(WARNING "Package vulkan not found. Building without support.") endif(VULKAN_FOUND) -pkg_check_modules (X11 x11) -if(X11_FOUND) - add_compile_definitions(FF_HAVE_X11=1) -else(X11_FOUND) - message(WARNING "Package x11 not found. Building without support.") -endif(X11_FOUND) +pkg_check_modules (WAYLAND wayland-client) +if(WAYLAND_FOUND) + add_compile_definitions(FF_HAVE_WAYLAND=1) +else(WAYLAND_FOUND) + message(WARNING "Package wayland-client not found. Building without support.") +endif(WAYLAND_FOUND) + +pkg_check_modules (XCB_RANDR xcb-randr) +if(XCB_RANDR_FOUND) + add_compile_definitions(FF_HAVE_XCB_RANDR=1) +else(XCB_RANDR_FOUND) + message(WARNING "Package xcb-randr not found. Building without support.") +endif(XCB_RANDR_FOUND) + +pkg_check_modules (XCB xcb) +if(XCB_FOUND) + add_compile_definitions(FF_HAVE_XCB=1) +else(XCB_FOUND) + message(WARNING "Package xcb not found. Building without support.") +endif(XCB_FOUND) pkg_check_modules (XRANDR xrandr) if(XRANDR_FOUND) @@ -82,12 +96,12 @@ else(XRANDR_FOUND) message(WARNING "Package xrandr not found. Building without support.") endif(XRANDR_FOUND) -pkg_check_modules (WAYLAND wayland-client) -if(WAYLAND_FOUND) - add_compile_definitions(FF_HAVE_WAYLAND=1) -else(WAYLAND_FOUND) - message(WARNING "Package wayland-client not found. Building without support.") -endif(WAYLAND_FOUND) +pkg_check_modules (X11 x11) +if(X11_FOUND) + add_compile_definitions(FF_HAVE_X11=1) +else(X11_FOUND) + message(WARNING "Package x11 not found. Building without support.") +endif(X11_FOUND) pkg_check_modules (GIO gio-2.0) if(GIO_FOUND) @@ -121,9 +135,12 @@ include_directories( ${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR}/src ${LIBPCI_INCLUDE_DIRS} - ${X11_INCLUDE_DIRS} - ${XRANDR_INCLUDE_DIRS} + ${VULKAN_INCLUDE_DIRS} ${WAYLAND_INCLUDE_DIRS} + ${XCB_RANDR_INCLUDE_DIRS} + ${XCB_INCLUDE_DIRS} + ${XRANDR_INCLUDE_DIRS} + ${X11_INCLUDE_DIRS} ${GIO_INCLUDE_DIRS} ${DCONF_INCLUDE_DIRS} ${XFCONF_INCLUDE_DIRS} @@ -151,6 +168,7 @@ set(SRCS src/detection/terminalShell.c src/detection/displayserver/displayServer.c src/detection/displayserver/wayland.c + src/detection/displayserver/xcb.c src/detection/displayserver/xlib.c src/detection/displayserver/wmde.c src/modules/break.c diff --git a/README.md b/README.md index 91e59f30e..e77e6a5c0 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,11 @@ In order to run properly on every machine, fastfetch dynamically loads needed li The following libraries are used if present: * [`libpci`](https://github.com/pciutils/pciutils): GPU output. * [`libvulkan`](https://www.vulkan.org/): Fallback for GPU output. -* [`libX11`](https://gitlab.freedesktop.org/xorg/lib/libx11): Needed for resolution output -* [`libXrandr`](https://gitlab.freedesktop.org/xorg/lib/libxrandr): Needed for appending refresh rate to resolution output. -* [`libwayland-client`](https://wayland.freedesktop.org/): Better resolution performance in wayland sessions. +* [`libxcb-randr`](https://xcb.freedesktop.org/), + [`libXrandr`](https://gitlab.freedesktop.org/xorg/lib/libxrandr), + [`libxcb`](https://xcb.freedesktop.org/), + [`libX11`](https://gitlab.freedesktop.org/xorg/lib/libx11): At least one of them sould be present in X11 sessions for better resolution detection and faster WM detection. The `*randr` ones provide multi monitor support. The `libxcb*` ones usually have better performance. +* [`libwayland-client`](https://wayland.freedesktop.org/): Better resolution performance and output in wayland sessions. Supports different refresh rates per monitor. * [`libGIO`](https://developer.gnome.org/gio/unstable/): Needed for values that are only stored GSettings. * [`libDConf`](https://developer.gnome.org/dconf/unstable/): Needed for values that are only stored in DConf + Fallback for GSettings. * [`libXFConf`](https://gitlab.xfce.org/xfce/xfconf): Needed for XFWM theme and XFCE Terminal font. diff --git a/completions/bash b/completions/bash index f96cf3cd7..f06dc7b48 100644 --- a/completions/bash +++ b/completions/bash @@ -196,11 +196,13 @@ __fastfetch_completion() local FF_OPTIONS_PATH=( "--lib-PCI" "--lib-vulkan" - "--lib-X11" + "--lib-wayland" + "--lib-xcb-randr" + "--lib-xcb" "--lib-Xrandr" + "--lib-X11" "--lib-gio" "--lib-DConf" - "--lib-wayland" "--lib-XFConf" "--lib-RPM" "--battery-dir" diff --git a/src/common/init.c b/src/common/init.c index cd4c8609a..77a4dfdf7 100644 --- a/src/common/init.c +++ b/src/common/init.c @@ -174,11 +174,13 @@ static void defaultConfig(FFinstance* instance) ffStrbufInitA(&instance->config.libPCI, 0); ffStrbufInitA(&instance->config.libVulkan, 0); - ffStrbufInitA(&instance->config.libX11, 0); + ffStrbufInitA(&instance->config.libWayland, 0); + ffStrbufInitA(&instance->config.libXcbRandr, 0); + ffStrbufInitA(&instance->config.libXcb, 0); ffStrbufInitA(&instance->config.libXrandr, 0); + ffStrbufInitA(&instance->config.libX11, 0); ffStrbufInitA(&instance->config.libGIO, 0); ffStrbufInitA(&instance->config.libDConf, 0); - ffStrbufInitA(&instance->config.libWayland, 0); ffStrbufInitA(&instance->config.libXFConf, 0); ffStrbufInitA(&instance->config.librpm, 0); diff --git a/src/data/config.txt b/src/data/config.txt index 879aef827..750b5fabe 100644 --- a/src/data/config.txt +++ b/src/data/config.txt @@ -185,10 +185,12 @@ # Must be a valid path to a library. #--lib-PCI /usr/lib/libpci.so #--lib-vulkan /usr/lib/libvulkan.so -#--lib-X11 /usr/lib/libX11.so +#--lib-wayland /usr/lib/libwayland-client.so +#--lib-xcb-randr /usr/lib/libxcb-randr.so +#--lib-xcb /usr/lib/libxcb.so #--lib-Xrandr /usr/lib/libXrandr.so +#--lib-X11 /usr/lib/libX11.so #--lib-gio /usr/lib/libgio-2.0.so #--lib-DConf /usr/lib/libdconf.so -#--lib-wayland /usr/lib/libwayland-client.so #--lib-XFConf /usr/lib/libxfconf-0.so #--lib-rpm /usr/lib/librpm.so diff --git a/src/data/help.txt b/src/data/help.txt index 3b447bb42..7553c1039 100644 --- a/src/data/help.txt +++ b/src/data/help.txt @@ -92,11 +92,13 @@ Key options: Provide a custom key for an output Library options: Set the path of a library to load --lib-PCI --lib-vulkan - --lib-X11 + --lib-wayland + --lib-xcb-randr + --lib-xcb --lib-Xrandr + --lib-X11 --lib-gio --lib-DConf - --lib-wayland --lib-XFConf --lib-RPM diff --git a/src/detection/displayserver/displayServer.c b/src/detection/displayserver/displayServer.c index 4fe1d4dde..af8a331d6 100644 --- a/src/detection/displayserver/displayServer.c +++ b/src/detection/displayserver/displayServer.c @@ -20,6 +20,19 @@ uint32_t ffdsParseRefreshRate(int32_t refreshRate) return (uint32_t) refreshRate; } +bool ffdsAppendResolution(FFDisplayServerResult* result, uint32_t width, uint32_t height, uint32_t refreshRate) +{ + if(width == 0 || height == 0) + return false; + + FFResolutionResult* resolution = ffListAdd(&result->resolutions); + resolution->width = width; + resolution->height = height; + resolution->refreshRate = refreshRate; + + return true; +} + static void parseDRM(FFDisplayServerResult* result) { const char* drmDirPath = "/sys/class/drm/"; @@ -94,9 +107,15 @@ const FFDisplayServerResult* ffConnectDisplayServer(const FFinstance* instance) //We use the resolution list to detect if a connection is needed. //They respect wmProtocolName, and only detect resolution if it is set. + if(result.resolutions.length == 0) + ffdsConnectXcbRandr(instance, &result); + if(result.resolutions.length == 0) ffdsConnectXrandr(instance, &result); + if(result.resolutions.length == 0) + ffdsConnectXcb(instance, &result); + if(result.resolutions.length == 0) ffdsConnectXlib(instance, &result); diff --git a/src/detection/displayserver/displayServer.h b/src/detection/displayserver/displayServer.h index 70684468f..88a99bf46 100644 --- a/src/detection/displayserver/displayServer.h +++ b/src/detection/displayserver/displayServer.h @@ -10,8 +10,13 @@ #define FF_DISPLAYSERVER_PROTOCOL_TTY "TTY" uint32_t ffdsParseRefreshRate(int32_t refreshRate); +bool ffdsAppendResolution(FFDisplayServerResult* result, uint32_t width, uint32_t height, uint32_t refreshRate); void ffdsConnectWayland(const FFinstance* instance, FFDisplayServerResult* result); + +void ffdsConnectXcbRandr(const FFinstance* instance, FFDisplayServerResult* result); +void ffdsConnectXcb(const FFinstance* instance, FFDisplayServerResult* result); + void ffdsConnectXrandr(const FFinstance* instance, FFDisplayServerResult* result); void ffdsConnectXlib(const FFinstance* instance, FFDisplayServerResult* result); diff --git a/src/detection/displayserver/xcb.c b/src/detection/displayserver/xcb.c new file mode 100644 index 000000000..de9fd1d90 --- /dev/null +++ b/src/detection/displayserver/xcb.c @@ -0,0 +1,389 @@ +#include "displayServer.h" + +#ifdef FF_HAVE_XCB +#include +#include + +typedef struct XcbPropertyData +{ + FF_LIBRARY_SYMBOL(xcb_intern_atom) + FF_LIBRARY_SYMBOL(xcb_intern_atom_reply) + FF_LIBRARY_SYMBOL(xcb_get_property) + FF_LIBRARY_SYMBOL(xcb_get_property_reply) + FF_LIBRARY_SYMBOL(xcb_get_property_value) + FF_LIBRARY_SYMBOL(xcb_get_property_value_length) +} XcbPropertyData; + +static bool xcbInitPropertyData(void* libraryHandle, XcbPropertyData* propertyData) +{ + FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffxcb_intern_atom, xcb_intern_atom, false) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffxcb_intern_atom_reply, xcb_intern_atom_reply, false) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffxcb_get_property, xcb_get_property, false) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffxcb_get_property_reply, xcb_get_property_reply, false) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffxcb_get_property_value, xcb_get_property_value, false) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffxcb_get_property_value_length, xcb_get_property_value_length, false) + + return true; +} + +static void* xcbGetProperty(XcbPropertyData* data, xcb_connection_t* connection, xcb_window_t window, const char* request) +{ + xcb_intern_atom_cookie_t requestAtomCookie = data->ffxcb_intern_atom(connection, true, (uint16_t) strlen(request), request); + xcb_intern_atom_reply_t* requestAtomReply = data->ffxcb_intern_atom_reply(connection, requestAtomCookie, NULL); + if(requestAtomReply == NULL) + return NULL; + + xcb_get_property_cookie_t propertyCookie = data->ffxcb_get_property(connection, false, window, requestAtomReply->atom, XCB_ATOM_ANY, 0, 64); + + free(requestAtomReply); + + xcb_get_property_reply_t* propertyReply = data->ffxcb_get_property_reply(connection, propertyCookie, NULL); + if(propertyReply == NULL) + return NULL; + + int length = data->ffxcb_get_property_value_length(propertyReply); + if(length <= 0) + { + free(propertyReply); + return NULL; + } + + //Why are xcb property strings not null terminated??? + void* replyValue = malloc((size_t) (length + 1)); + memcpy(replyValue, data->ffxcb_get_property_value(propertyReply), (size_t) length); + ((char*) replyValue)[length] = '\0'; + + free(propertyReply); + + return replyValue; +} + +static void xcbDetectWMfromEWMH(XcbPropertyData* data, xcb_connection_t* connection, xcb_window_t rootWindow, FFDisplayServerResult* result) +{ + if(result->wmProcessName.length > 0) + return; + + xcb_window_t* wmWindow = (xcb_window_t*) xcbGetProperty(data, connection, rootWindow, "_NET_SUPPORTING_WM_CHECK"); + if(wmWindow == NULL) + return; + + char* wmName = (char*) xcbGetProperty(data, connection, *wmWindow, "_NET_WM_NAME"); + if(wmName == NULL) + wmName = (char*) xcbGetProperty(data, connection, *wmWindow, "WM_NAME"); + + free(wmWindow); + + if(wmName == NULL) + return; + + if(*wmName == '\0') + { + free(wmName); + return; + } + + ffStrbufSetS(&result->wmProcessName, wmName); + + free(wmName); +} + +void ffdsConnectXcb(const FFinstance* instance, FFDisplayServerResult* result) +{ + FF_LIBRARY_LOAD(xcb, "libxcb.so", instance->config.libXcb,) + FF_LIBRARY_LOAD_SYMBOL(xcb, xcb_connect,) + FF_LIBRARY_LOAD_SYMBOL(xcb, xcb_get_setup,) + FF_LIBRARY_LOAD_SYMBOL(xcb, xcb_setup_roots_iterator,) + FF_LIBRARY_LOAD_SYMBOL(xcb, xcb_screen_next,) + FF_LIBRARY_LOAD_SYMBOL(xcb, xcb_disconnect,) + + XcbPropertyData propertyData; + bool propertyDataInitialized = xcbInitPropertyData(xcb, &propertyData); + + xcb_connection_t* connection = ffxcb_connect(NULL, NULL); + if(connection == NULL) + { + dlclose(xcb); + return; + } + + xcb_screen_iterator_t iterator = ffxcb_setup_roots_iterator(ffxcb_get_setup(connection)); + + if(iterator.rem > 0 && propertyDataInitialized) + xcbDetectWMfromEWMH(&propertyData, connection, iterator.data->root, result); + + while(iterator.rem > 0) + { + ffdsAppendResolution( + result, + (uint32_t) iterator.data->width_in_pixels, + (uint32_t) iterator.data->height_in_pixels, + 0 + ); + ffxcb_screen_next(&iterator); + } + + ffxcb_disconnect(connection); + dlclose(xcb); +} + +#else + +void ffdsConnectXcb(const FFinstance* instance, FFDisplayServerResult* result) +{ + //Do nothing. There are other implementations coming + FF_UNUSED(instance, result) +} + +#endif + +#ifdef FF_HAVE_XCB_RANDR +#include + +typedef struct XcbRandrData +{ + FF_LIBRARY_SYMBOL(xcb_randr_get_screen_resources) + FF_LIBRARY_SYMBOL(xcb_randr_get_screen_resources_reply) + FF_LIBRARY_SYMBOL(xcb_randr_get_screen_resources_modes_iterator) + FF_LIBRARY_SYMBOL(xcb_randr_get_screen_info) + FF_LIBRARY_SYMBOL(xcb_randr_get_screen_info_reply) + FF_LIBRARY_SYMBOL(xcb_randr_mode_info_next) + FF_LIBRARY_SYMBOL(xcb_randr_get_monitors) + FF_LIBRARY_SYMBOL(xcb_randr_get_monitors_reply) + FF_LIBRARY_SYMBOL(xcb_randr_get_monitors_monitors_iterator) + FF_LIBRARY_SYMBOL(xcb_randr_monitor_info_next) + FF_LIBRARY_SYMBOL(xcb_randr_monitor_info_outputs_length) + FF_LIBRARY_SYMBOL(xcb_randr_monitor_info_outputs) + FF_LIBRARY_SYMBOL(xcb_randr_output_next) + FF_LIBRARY_SYMBOL(xcb_randr_get_output_info) + FF_LIBRARY_SYMBOL(xcb_randr_get_output_info_reply) + FF_LIBRARY_SYMBOL(xcb_randr_get_crtc_info) + FF_LIBRARY_SYMBOL(xcb_randr_get_crtc_info_reply) + + //init once + xcb_connection_t* connection; + FFDisplayServerResult* result; + + //init per screen + uint32_t defaultRefreshRate; + xcb_randr_get_screen_resources_reply_t* screenResources; +} XcbRandrData; + +static bool xcbRandrHandleModeInfo(XcbRandrData* data, xcb_randr_mode_info_t* modeInfo) +{ + uint32_t refreshRate = ffdsParseRefreshRate((int32_t) ( + modeInfo->dot_clock / (modeInfo->htotal * modeInfo->vtotal) + )); + + return ffdsAppendResolution( + data->result, + (uint32_t) modeInfo->width, + (uint32_t) modeInfo->height, + refreshRate == 0 ? data->defaultRefreshRate : refreshRate + ); +} + +static bool xcbRandrHandleMode(XcbRandrData* data, xcb_randr_mode_t mode) +{ + //We do the check here, because we want the best fallback resolution if this call failed + if(data->screenResources == NULL) + return false; + + xcb_randr_mode_info_iterator_t modesIterator = data->ffxcb_randr_get_screen_resources_modes_iterator(data->screenResources); + + while(modesIterator.rem > 0) + { + if(modesIterator.data->id == mode) + return xcbRandrHandleModeInfo(data, modesIterator.data); + + data->ffxcb_randr_mode_info_next(&modesIterator); + } + + return false; +} + +static bool xcbRandrHandleCrtc(XcbRandrData* data, xcb_randr_crtc_t crtc) +{ + xcb_randr_get_crtc_info_cookie_t crtcInfoCookie = data->ffxcb_randr_get_crtc_info(data->connection, crtc, XCB_CURRENT_TIME); + xcb_randr_get_crtc_info_reply_t* crtcInfoReply = data->ffxcb_randr_get_crtc_info_reply(data->connection, crtcInfoCookie, NULL); + if(crtcInfoReply == NULL) + return false; + + bool res = xcbRandrHandleMode(data, crtcInfoReply->mode); + + free(crtcInfoReply); + + return res ? true : ffdsAppendResolution( + data->result, + (uint32_t) crtcInfoReply->width, + (uint32_t) crtcInfoReply->height, + data->defaultRefreshRate + ); +} + +static bool xcbRandrHandleOutput(XcbRandrData* data, xcb_randr_output_t output) +{ + xcb_randr_get_output_info_cookie_t outputInfoCookie = data->ffxcb_randr_get_output_info(data->connection, output, XCB_CURRENT_TIME); + xcb_randr_get_output_info_reply_t* outputInfoReply = data->ffxcb_randr_get_output_info_reply(data->connection, outputInfoCookie, NULL); + if(outputInfoReply == NULL) + return false; + + bool res = xcbRandrHandleCrtc(data, outputInfoReply->crtc); + + free(outputInfoReply); + + return res; +} + +static bool xcbRandrHandleMonitor(XcbRandrData* data, xcb_randr_monitor_info_t* monitor) +{ + //for some reasons, we have to construct this our self + xcb_randr_output_iterator_t outputIterator = { + .index = 0, + .data = data->ffxcb_randr_monitor_info_outputs(monitor), + .rem = data->ffxcb_randr_monitor_info_outputs_length(monitor) + }; + + bool foundOutput = false; + + while(outputIterator.rem > 0) + { + if(xcbRandrHandleOutput(data, *outputIterator.data)) + foundOutput = true; + data->ffxcb_randr_output_next(&outputIterator); + }; + + return foundOutput ? true : ffdsAppendResolution( + data->result, + (uint32_t) monitor->width, + (uint32_t) monitor->height, + data->defaultRefreshRate + ); +} + +static bool xcbRandrHandleMonitors(XcbRandrData* data, xcb_screen_t* screen) +{ + xcb_randr_get_monitors_cookie_t monitorsCookie = data->ffxcb_randr_get_monitors(data->connection, screen->root, true); + xcb_randr_get_monitors_reply_t* monitorsReply = data->ffxcb_randr_get_monitors_reply(data->connection, monitorsCookie, NULL); + if(monitorsReply == NULL) + return false; + + xcb_randr_monitor_info_iterator_t monitorInfoIterator = data->ffxcb_randr_get_monitors_monitors_iterator(monitorsReply); + + bool foundMonitor = false; + + while(monitorInfoIterator.rem > 0) + { + if(xcbRandrHandleMonitor(data, monitorInfoIterator.data)) + foundMonitor = true; + data->ffxcb_randr_monitor_info_next(&monitorInfoIterator); + } + + free(monitorsReply); + + return foundMonitor; +} + +static void xcbRandrHandleScreen(XcbRandrData* data, xcb_screen_t* screen) +{ + //Init screen info. This is used to get the default refresh rate. If this fails, default refresh rate is simply 0. + xcb_randr_get_screen_info_cookie_t screenInfoCookie = data->ffxcb_randr_get_screen_info(data->connection, screen->root); + xcb_randr_get_screen_info_reply_t* screenInfoReply = data->ffxcb_randr_get_screen_info_reply(data->connection, screenInfoCookie, NULL); + + if(screenInfoReply != NULL) + { + data->defaultRefreshRate = screenInfoReply->rate; + free(screenInfoReply); + } + else + data->defaultRefreshRate = 0; + + //Init screen resources. They are used to iterate over all modes. xcbRandrHandleMode checks for " == NULL", to fail as late as possible. + xcb_randr_get_screen_resources_cookie_t screenResourcesCookie = data->ffxcb_randr_get_screen_resources(data->connection, screen->root); + data->screenResources = data->ffxcb_randr_get_screen_resources_reply(data->connection, screenResourcesCookie, NULL); + + //With all the initialisation done, start the detection + bool ret = xcbRandrHandleMonitors(data, screen); + + free(data->screenResources); + + if(ret) + return; + + //If detetction failed, fallback to screen = monitor, like in the libxcb.so implementation + ffdsAppendResolution( + data->result, + (uint32_t) screen->width_in_pixels, + (uint32_t) screen->height_in_pixels, + data->defaultRefreshRate + ); +} + +void ffdsConnectXcbRandr(const FFinstance* instance, FFDisplayServerResult* result) +{ + FF_LIBRARY_LOAD(xcbRandr, "libxcb-randr.so", instance->config.libXcbRandr,) + FF_LIBRARY_LOAD_SYMBOL(xcbRandr, xcb_connect,) + FF_LIBRARY_LOAD_SYMBOL(xcbRandr, xcb_get_setup,) + FF_LIBRARY_LOAD_SYMBOL(xcbRandr, xcb_setup_roots_iterator,) + FF_LIBRARY_LOAD_SYMBOL(xcbRandr, xcb_screen_next,) + FF_LIBRARY_LOAD_SYMBOL(xcbRandr, xcb_disconnect,) + + XcbRandrData data; + + FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_screen_resources, xcb_randr_get_screen_resources,) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_screen_resources_reply, xcb_randr_get_screen_resources_reply,) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_screen_resources_modes_iterator, xcb_randr_get_screen_resources_modes_iterator,) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_screen_info, xcb_randr_get_screen_info,) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_screen_info_reply, xcb_randr_get_screen_info_reply,) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_mode_info_next, xcb_randr_mode_info_next,) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_monitors, xcb_randr_get_monitors,) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_monitors_reply, xcb_randr_get_monitors_reply,) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_monitors_monitors_iterator, xcb_randr_get_monitors_monitors_iterator,) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_monitor_info_next, xcb_randr_monitor_info_next,) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_monitor_info_outputs_length, xcb_randr_monitor_info_outputs_length,) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_monitor_info_outputs, xcb_randr_monitor_info_outputs,) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_output_next, xcb_randr_output_next,) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_output_info, xcb_randr_get_output_info,) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_output_info_reply, xcb_randr_get_output_info_reply,) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_crtc_info, xcb_randr_get_crtc_info,) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_crtc_info_reply, xcb_randr_get_crtc_info_reply,) + + XcbPropertyData propertyData; + bool propertyDataInitialized = xcbInitPropertyData(xcbRandr, &propertyData); + + data.connection = ffxcb_connect(NULL, NULL); + if(data.connection == NULL) + { + dlclose(xcbRandr); + return; + } + + data.result = result; + + xcb_screen_iterator_t iterator = ffxcb_setup_roots_iterator(ffxcb_get_setup(data.connection)); + + if(iterator.rem > 0 && propertyDataInitialized) + xcbDetectWMfromEWMH(&propertyData, data.connection, iterator.data->root, result); + + while(iterator.rem > 0) + { + xcbRandrHandleScreen(&data, iterator.data); + ffxcb_screen_next(&iterator); + } + + ffxcb_disconnect(data.connection); + dlclose(xcbRandr); + + //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_DISPLAYSERVER_PROTOCOL_X11); +} + +#else + +void ffdsConnectXcbRandr(const FFinstance* instance, FFDisplayServerResult* result) +{ + //Do nothing. There are other implementations coming + FF_UNUSED(instance, result) +} + +#endif diff --git a/src/detection/displayserver/xlib.c b/src/detection/displayserver/xlib.c index fbe2fb888..4717e97a2 100644 --- a/src/detection/displayserver/xlib.c +++ b/src/detection/displayserver/xlib.c @@ -9,6 +9,14 @@ typedef struct X11PropertyData FF_LIBRARY_SYMBOL(XGetWindowProperty) } X11PropertyData; +static bool x11InitPropertyData(void* libraryHandle, X11PropertyData* propertyData) +{ + FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffXInternAtom, XInternAtom, false) + FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffXGetWindowProperty, XGetWindowProperty, false) + + return true; +} + static unsigned char* x11GetProperty(X11PropertyData* data, Display* display, Window window, const char* request) { Atom requestAtom = data->ffXInternAtom(display, request, False); @@ -19,20 +27,7 @@ static unsigned char* x11GetProperty(X11PropertyData* data, Display* display, Wi unsigned long unused; unsigned char* result = NULL; - data->ffXGetWindowProperty( - display, - window, - requestAtom, - 0, - 64, - False, - AnyPropertyType, - &actualType, - (int*) &unused, - &unused, - &unused, - &result - ); + data->ffXGetWindowProperty(display, window, requestAtom, 0, 64, False, AnyPropertyType, &actualType, (int*) &unused, &unused, &unused, &result); return result; } @@ -42,40 +37,17 @@ static void x11DetectWMFromEWMH(X11PropertyData* data, Display* display, FFDispl if(result->wmProcessName.length > 0) return; - Window root = DefaultRootWindow(display); - if(root == None) + Window* wmWindow = (Window*) x11GetProperty(data, display, DefaultRootWindow(display), "_NET_SUPPORTING_WM_CHECK"); + if(wmWindow == NULL) return; - unsigned char* wmWindowID = x11GetProperty(data, display, root, "_NET_SUPPORTING_WM_CHECK"); - if(wmWindowID == NULL) - return; - - Window wmWindow = *(Window*) wmWindowID; - if(wmWindow == None) - return; - - unsigned char* wmName = x11GetProperty(data, display, wmWindow, "_NET_WM_NAME"); - if(wmName == NULL) - wmName = x11GetProperty(data, display, wmWindow, "WM_NAME"); + const char* wmName = (const char*) x11GetProperty(data, display, *wmWindow, "_NET_WM_NAME"); if(wmName == NULL) + wmName = (const char*) x11GetProperty(data, display, *wmWindow, "WM_NAME"); + if(wmName == NULL || *wmName == '\0') return; - const char* wmNameString = (const char*) wmName; - if(*wmNameString == '\0') - return; - - ffStrbufSetS(&result->wmProcessName, wmNameString); -} - -static void x11AddScreenAsResult(FFlist* results, Screen* screen, uint32_t refreshRate) -{ - if(WidthOfScreen(screen) == 0 || HeightOfScreen(screen) == 0) - return; - - FFResolutionResult* result = ffListAdd(results); - result->width = (uint32_t) WidthOfScreen(screen); - result->height = (uint32_t) HeightOfScreen(screen); - result->refreshRate = refreshRate; + ffStrbufSetS(&result->wmProcessName, wmName); } void ffdsConnectXlib(const FFinstance* instance, FFDisplayServerResult* result) @@ -85,8 +57,7 @@ void ffdsConnectXlib(const FFinstance* instance, FFDisplayServerResult* result) FF_LIBRARY_LOAD_SYMBOL(x11, XCloseDisplay,) X11PropertyData propertyData; - FF_LIBRARY_LOAD_SYMBOL_ADRESS(x11, propertyData.ffXInternAtom, XInternAtom,) - FF_LIBRARY_LOAD_SYMBOL_ADRESS(x11, propertyData.ffXGetWindowProperty, XGetWindowProperty,) + bool propertyDataInitialized = x11InitPropertyData(x11, &propertyData); Display* display = ffXOpenDisplay(x11); if(display == NULL) @@ -95,10 +66,19 @@ void ffdsConnectXlib(const FFinstance* instance, FFDisplayServerResult* result) return; } - for(int i = 0; i < ScreenCount(display); i++) - x11AddScreenAsResult(&result->resolutions, ScreenOfDisplay(display, i), 0); + if(propertyDataInitialized && ScreenCount(display) > 0) + x11DetectWMFromEWMH(&propertyData, display, result); - x11DetectWMFromEWMH(&propertyData, display, result); + for(int i = 0; i < ScreenCount(display); i++) + { + Screen* screen = ScreenOfDisplay(display, i); + ffdsAppendResolution( + result, + (uint32_t) screen->width, + (uint32_t) screen->height, + 0 + ); + } ffXCloseDisplay(display); dlclose(x11); @@ -138,101 +118,115 @@ typedef struct XrandrData //Init once Display* display; - FFlist* results; + FFDisplayServerResult* result; //Init per screen uint32_t defaultRefreshRate; XRRScreenResources* screenResources; } XrandrData; -static XRRModeInfo* xrandrGetModeInfo(XRRScreenResources* screenResources, RRMode mode) +static bool xrandrHandleModeInfo(XrandrData* data, XRRModeInfo* modeInfo) { - for(int i = 0; i < screenResources->nmode; i++) - { - if(screenResources->modes[i].id == mode) - return &screenResources->modes[i]; - } - return NULL; + uint32_t refreshRate = ffdsParseRefreshRate((int32_t) ( + modeInfo->dotClock / (modeInfo->hTotal * modeInfo->vTotal) + )); + + return ffdsAppendResolution( + data->result, + (uint32_t) modeInfo->width, + (uint32_t) modeInfo->height, + refreshRate == 0 ? data->defaultRefreshRate : refreshRate + ); } -static bool xrandrHandleOutputInfo(XrandrData* data, XRROutputInfo* outputInfo) +static bool xrandrHandleMode(XrandrData* data, RRMode mode) { - XRRCrtcInfo* crtcInfo = data->ffXRRGetCrtcInfo(data->display, data->screenResources, outputInfo->crtc); + for(int i = 0; i < data->screenResources->nmode; i++) + { + if(data->screenResources->modes[i].id == mode) + return xrandrHandleModeInfo(data, &data->screenResources->modes[i]); + } + return false; +} + +static bool xrandrHandleCrtc(XrandrData* data, RRCrtc crtc) +{ + //We do the check here, because we want the best fallback resolution if this call failed + if(data->screenResources == NULL) + return false; + + XRRCrtcInfo* crtcInfo = data->ffXRRGetCrtcInfo(data->display, data->screenResources, crtc); if(crtcInfo == NULL) return false; - XRRModeInfo* modeInfo = xrandrGetModeInfo(data->screenResources, crtcInfo->mode); - if(modeInfo == NULL || modeInfo->width == 0 || modeInfo->height == 0) - { - data->ffXRRFreeCrtcInfo(crtcInfo); - return false; - } - - FFResolutionResult* result = ffListAdd(data->results); - result->width = modeInfo->width; - result->height = modeInfo->height; - result->refreshRate = ffdsParseRefreshRate((int32_t) (modeInfo->dotClock / (modeInfo->hTotal * modeInfo->vTotal))); - - if(result->refreshRate == 0) - result->refreshRate = data->defaultRefreshRate; + bool res = xrandrHandleMode(data, crtcInfo->mode); data->ffXRRFreeCrtcInfo(crtcInfo); - return true; + return res ? true : ffdsAppendResolution( + data->result, + (uint32_t) crtcInfo->width, + (uint32_t) crtcInfo->height, + data->defaultRefreshRate + ); } -static bool xrandrHandleMonitorFallback(XrandrData* data, XRRMonitorInfo* monitorInfo) +static bool xrandrHandleOutput(XrandrData* data, RROutput output) { - if(monitorInfo->width == 0 || monitorInfo->height == 0) + XRROutputInfo* outputInfo = data->ffXRRGetOutputInfo(data->display, data->screenResources, output); + if(outputInfo == NULL) return false; - FFResolutionResult* result = ffListAdd(data->results); - result->width = (uint32_t) monitorInfo->width; - result->height = (uint32_t) monitorInfo->height; - result->refreshRate = (uint32_t) data->defaultRefreshRate; - return true; + bool res = xrandrHandleCrtc(data, outputInfo->crtc); + + data->ffXRRFreeOutputInfo(outputInfo); + + return res; } static bool xrandrHandleMonitor(XrandrData* data, XRRMonitorInfo* monitorInfo) { - bool foundMode = false; + bool foundOutput = false; for(int i = 0; i < monitorInfo->noutput; i++) { - XRROutputInfo* outputInfo = data->ffXRRGetOutputInfo(data->display, data->screenResources, monitorInfo->outputs[i]); - if(outputInfo == NULL) - continue; - - if(xrandrHandleOutputInfo(data, outputInfo)) - foundMode = true; - - data->ffXRRFreeOutputInfo(outputInfo); + if(xrandrHandleOutput(data, monitorInfo->outputs[i])) + foundOutput = true; } - if(!foundMode) - return xrandrHandleMonitorFallback(data, monitorInfo); - - return true; + return foundOutput ? true : ffdsAppendResolution( + data->result, + (uint32_t) monitorInfo->width, + (uint32_t) monitorInfo->height, + data->defaultRefreshRate + ); } -static inline bool xrandrLoopMonitors(XrandrData* data, XRRMonitorInfo* monitorInfos, int numberOfMonitors, bool(*monitorFunc)(XrandrData* data, XRRMonitorInfo* monitorInfo)) +static bool xrandrHandleMonitors(XrandrData* data, Screen* screen) { - bool foundAMonitor = false; + int numberOfMonitors; + XRRMonitorInfo* monitorInfos = data->ffXRRGetMonitors(data->display, RootWindowOfScreen(screen), True, &numberOfMonitors); + if(monitorInfos == NULL) + return false; + + bool foundAMonitor; for(int i = 0; i < numberOfMonitors; i++) { - if(monitorFunc(data, &monitorInfos[i])) + if(xrandrHandleMonitor(data, &monitorInfos[i])) foundAMonitor = true; } + data->ffXRRFreeMonitors(monitorInfos); + return foundAMonitor; } static void xrandrHandleScreen(XrandrData* data, Screen* screen) { - Window window = RootWindowOfScreen(screen); + //Init screen configuration. This is used to get the default refresh rate. If this fails, default refresh rate is simply 0. + XRRScreenConfiguration* screenConfiguration = data->ffXRRGetScreenInfo(data->display, RootWindowOfScreen(screen)); - XRRScreenConfiguration* screenConfiguration = data->ffXRRGetScreenInfo(data->display, window); if(screenConfiguration != NULL) { data->defaultRefreshRate = (uint32_t) data->ffXRRConfigCurrentRate(screenConfiguration); @@ -241,28 +235,23 @@ static void xrandrHandleScreen(XrandrData* data, Screen* screen) else data->defaultRefreshRate = 0; - int numberOfMonitors; - XRRMonitorInfo* monitorInfos = data->ffXRRGetMonitors(data->display, window, True, &numberOfMonitors); - if(monitorInfos == NULL) - { - x11AddScreenAsResult(data->results, screen, data->defaultRefreshRate); - return; - } + //Init screen resources + data->screenResources = data->ffXRRGetScreenResources(data->display, RootWindowOfScreen(screen)); - data->screenResources = data->ffXRRGetScreenResources(data->display, window); - - bool foundAMonitor; - - if(data->screenResources != NULL) - foundAMonitor = xrandrLoopMonitors(data, monitorInfos, numberOfMonitors, xrandrHandleMonitor); - else - foundAMonitor = xrandrLoopMonitors(data, monitorInfos, numberOfMonitors, xrandrHandleMonitorFallback); - - if(!foundAMonitor) - x11AddScreenAsResult(data->results, screen, data->defaultRefreshRate); + bool ret = xrandrHandleMonitors(data, screen); data->ffXRRFreeScreenResources(data->screenResources); - data->ffXRRFreeMonitors(monitorInfos); + + if(ret) + return; + + //Fallback to screen + ffdsAppendResolution( + data->result, + (uint32_t) WidthOfScreen(screen), + (uint32_t) HeightOfScreen(screen), + data->defaultRefreshRate + ); } void ffdsConnectXrandr(const FFinstance* instance, FFDisplayServerResult* result) @@ -287,8 +276,7 @@ void ffdsConnectXrandr(const FFinstance* instance, FFDisplayServerResult* result FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRFreeScreenConfigInfo, XRRFreeScreenConfigInfo,); X11PropertyData propertyData; - FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, propertyData.ffXInternAtom, XInternAtom,) - FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, propertyData.ffXGetWindowProperty, XGetWindowProperty,) + bool propertyDataInitialized = x11InitPropertyData(xrandr, &propertyData); data.display = ffXOpenDisplay(NULL); if(data.display == NULL) @@ -297,13 +285,14 @@ void ffdsConnectXrandr(const FFinstance* instance, FFDisplayServerResult* result return; } - data.results = &result->resolutions; + if(propertyDataInitialized && ScreenCount(data.display) > 0) + x11DetectWMFromEWMH(&propertyData, data.display, result); + + data.result = result; for(int i = 0; i < ScreenCount(data.display); i++) xrandrHandleScreen(&data, ScreenOfDisplay(data.display, i)); - x11DetectWMFromEWMH(&propertyData, data.display, result); - ffXCloseDisplay(data.display); dlclose(xrandr); diff --git a/src/fastfetch.c b/src/fastfetch.c index 83e843048..7224c37bf 100644 --- a/src/fastfetch.c +++ b/src/fastfetch.c @@ -354,14 +354,20 @@ static inline void listSupportedFeatures() #ifdef FF_HAVE_VULKAN "vulkan\n" #endif - #ifdef FF_HAVE_X11 - "x11\n" + #ifdef FF_HAVE_WAYLAND + "wayland\n" + #endif + #ifdef FF_HAVE_XCB_RANDR + "xcb-randr\n" + #endif + #ifdef FF_HAVE_XCB + "xcb\n" #endif #ifdef FF_HAVE_XRANDR "xrandr\n" #endif - #ifdef FF_HAVE_WAYLAND - "wayland\n" + #ifdef FF_HAVE_X11 + "x11\n" #endif #ifdef FF_HAVE_GIO "gio\n" @@ -826,16 +832,20 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con optionParseString(key, value, &instance->config.libPCI); else if(strcasecmp(key, "--lib-vulkan") == 0) optionParseString(key, value, &instance->config.libVulkan); - else if(strcasecmp(key, "--lib-X11") == 0) - optionParseString(key, value, &instance->config.libX11); + else if(strcasecmp(key, "--lib-wayland") == 0) + optionParseString(key, value, &instance->config.libWayland); + else if(strcasecmp(key, "--lib-xcb-randr") == 0) + optionCheckString(key, value, &instance->config.libXcbRandr); + else if(strcasecmp(key, "--lib-xcb") == 0) + optionParseString(key, value, &instance->config.libXcb); else if(strcasecmp(key, "--lib-Xrandr") == 0) optionParseString(key, value, &instance->config.libXrandr); + else if(strcasecmp(key, "--lib-X11") == 0) + optionParseString(key, value, &instance->config.libX11); else if(strcasecmp(key, "--lib-gio") == 0) optionParseString(key, value, &instance->config.libGIO); else if(strcasecmp(key, "--lib-DConf") == 0) optionParseString(key, value, &instance->config.libDConf); - else if(strcasecmp(key, "--lib-wayland") == 0) - optionParseString(key, value, &instance->config.libWayland); else if(strcasecmp(key, "--lib-XFConf") == 0) optionParseString(key, value, &instance->config.libXFConf); else if(strcasecmp(key, "--lib-rpm") == 0) diff --git a/src/fastfetch.h b/src/fastfetch.h index 6d3c4809d..ea5d5ea63 100644 --- a/src/fastfetch.h +++ b/src/fastfetch.h @@ -108,9 +108,11 @@ typedef struct FFconfig FFstrbuf libPCI; FFstrbuf libVulkan; - FFstrbuf libX11; - FFstrbuf libXrandr; FFstrbuf libWayland; + FFstrbuf libXcbRandr; + FFstrbuf libXcb; + FFstrbuf libXrandr; + FFstrbuf libX11; FFstrbuf libGIO; FFstrbuf libDConf; FFstrbuf libXFConf;