From 6647980ce34bd379f34aa742663520a4b45ae32c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 21 Apr 2024 23:21:13 +0800 Subject: [PATCH] DisplayServer (Linux): refactor & support wlroots protocol --- CHANGELOG.md | 2 +- CMakeLists.txt | 11 +- .../displayserver/linux/displayserver_linux.c | 14 - src/detection/displayserver/linux/wayland.c | 312 ---- .../linux/wayland/global-output.c | 134 ++ .../displayserver/linux/wayland/wayland.c | 149 ++ .../displayserver/linux/wayland/wayland.h | 57 + ...t-management-unstable-v1-client-protocol.h | 1298 +++++++++++++++++ ...r-output-management-unstable-v1-protocol.c | 160 ++ .../displayserver/linux/wayland/zwlr-output.c | 191 +++ src/detection/displayserver/linux/wlroots.c | 111 -- 11 files changed, 1998 insertions(+), 441 deletions(-) delete mode 100644 src/detection/displayserver/linux/wayland.c create mode 100644 src/detection/displayserver/linux/wayland/global-output.c create mode 100644 src/detection/displayserver/linux/wayland/wayland.c create mode 100644 src/detection/displayserver/linux/wayland/wayland.h create mode 100644 src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-client-protocol.h create mode 100644 src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-protocol.c create mode 100644 src/detection/displayserver/linux/wayland/zwlr-output.c delete mode 100644 src/detection/displayserver/linux/wlroots.c diff --git a/CHANGELOG.md b/CHANGELOG.md index fb1081aa8..29e1d7fff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Changes: * We now always detect max frequency of GPUs for consistent, instead of current frequency Features: -* We now use `wlr-randr` to detect displays for hyprland, which correctly reports fractional scale factors (Display, Linux) +* We now use `wlr-randr` to detect displays for wlroots based WMs, which correctly reports fractional scale factors (Display, Linux) * Improve GPU detection on Linux (GPU, Linux) * Support GPU memory usage detection for AMD GPUs * Support GPU frequency detection for Intel GPUs diff --git a/CMakeLists.txt b/CMakeLists.txt index be5a66486..05e7ad500 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -400,8 +400,10 @@ if(LINUX) src/detection/diskio/diskio_linux.c src/detection/displayserver/linux/displayserver_linux.c src/detection/displayserver/linux/drm.c - src/detection/displayserver/linux/wlroots.c - src/detection/displayserver/linux/wayland.c + src/detection/displayserver/linux/wayland/wayland.c + src/detection/displayserver/linux/wayland/global-output.c + src/detection/displayserver/linux/wayland/zwlr-output.c + src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-protocol.c src/detection/displayserver/linux/wmde.c src/detection/displayserver/linux/xcb.c src/detection/displayserver/linux/xlib.c @@ -519,7 +521,10 @@ elseif(BSD) src/detection/diskio/diskio_bsd.c src/detection/displayserver/linux/displayserver_linux.c src/detection/displayserver/linux/drm.c - src/detection/displayserver/linux/wayland.c + src/detection/displayserver/linux/wayland/wayland.c + src/detection/displayserver/linux/wayland/global-output.c + src/detection/displayserver/linux/wayland/zwlr-output.c + src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-protocol.c src/detection/displayserver/linux/wmde.c src/detection/displayserver/linux/xcb.c src/detection/displayserver/linux/xlib.c diff --git a/src/detection/displayserver/linux/displayserver_linux.c b/src/detection/displayserver/linux/displayserver_linux.c index 713a2626a..807facd9e 100644 --- a/src/detection/displayserver/linux/displayserver_linux.c +++ b/src/detection/displayserver/linux/displayserver_linux.c @@ -11,20 +11,6 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds) { if (instance.config.general.dsForceDrm == FF_DS_FORCE_DRM_TYPE_FALSE) { - #ifdef __linux__ - { - const char* desktopSession = getenv("DESKTOP_SESSION"); - if (desktopSession && ffStrEquals(desktopSession, "hyprland")) - { - ffStrbufSetStatic(&ds->wmProcessName, "xdg-desktop-portal-hyprland"); - ffStrbufSetStatic(&ds->wmPrettyName, FF_WM_PRETTY_HYPRLAND); - ffStrbufSetStatic(&ds->wmProtocolName, FF_WM_PROTOCOL_WAYLAND); - if (ffdsConnectWlroots(ds) == NULL) - return; - } - } - #endif - //We try wayland as our preferred display server, as it supports the most features. //This method can't detect the name of our WM / DE ffdsConnectWayland(ds); diff --git a/src/detection/displayserver/linux/wayland.c b/src/detection/displayserver/linux/wayland.c deleted file mode 100644 index f98703617..000000000 --- a/src/detection/displayserver/linux/wayland.c +++ /dev/null @@ -1,312 +0,0 @@ -#include "displayserver_linux.h" -#include "util/stringUtils.h" - -#include -#include - -#ifdef FF_HAVE_WAYLAND -#include "common/library.h" -#include "common/io/io.h" -#include "common/thread.h" - -#include -#include -#include -#include - -typedef struct WaylandData -{ - FFDisplayServerResult* result; - FF_LIBRARY_SYMBOL(wl_proxy_marshal_constructor_versioned) - FF_LIBRARY_SYMBOL(wl_proxy_add_listener) - FF_LIBRARY_SYMBOL(wl_proxy_destroy) - FF_LIBRARY_SYMBOL(wl_display_roundtrip) - struct wl_display* display; - const struct wl_interface* ffwl_output_interface; -} WaylandData; - -typedef struct WaylandDisplay -{ - int32_t width; - int32_t height; - int32_t refreshRate; - int32_t scale; - enum wl_output_transform transform; - FFDisplayType type; - FFstrbuf name; - FFstrbuf description; - FFstrbuf vendorAndModelId; - FFstrbuf edidName; -} WaylandDisplay; - -#ifndef __FreeBSD__ -static void waylandDetectWM(int fd, FFDisplayServerResult* result) -{ - struct ucred ucred; - socklen_t len = sizeof(struct ucred); - if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) - return; - - FF_STRBUF_AUTO_DESTROY procPath = ffStrbufCreate(); - ffStrbufAppendF(&procPath, "/proc/%d/cmdline", ucred.pid); //We check the cmdline for the process name, because it is not trimmed. - ffReadFileBuffer(procPath.chars, &result->wmProcessName); - ffStrbufTrimRightSpace(&result->wmProcessName); - ffStrbufSubstrBeforeFirstC(&result->wmProcessName, '\0'); //Trim the arguments - ffStrbufSubstrAfterLastC(&result->wmProcessName, '/'); //Trim the path -} -#else -static void waylandDetectWM(int fd, FFDisplayServerResult* result) -{ - FF_UNUSED(fd, result); -} -#endif - -static void stubListener(void* data, ...) -{ - (void) data; -} - -static void waylandOutputModeListener(void* data, FF_MAYBE_UNUSED struct wl_output* output, uint32_t flags, int32_t width, int32_t height, int32_t refreshRate) -{ - if(!(flags & WL_OUTPUT_MODE_CURRENT)) - return; - - WaylandDisplay* display = data; - display->width = width; - display->height = height; - display->refreshRate = refreshRate; -} - -static void waylandOutputScaleListener(void* data, FF_MAYBE_UNUSED struct wl_output* output, int32_t scale) -{ - WaylandDisplay* display = data; - display->scale = scale; -} - -static void waylandOutputGeometryListener(void *data, - FF_MAYBE_UNUSED struct wl_output *output, - FF_MAYBE_UNUSED int32_t x, - FF_MAYBE_UNUSED int32_t y, - FF_MAYBE_UNUSED int32_t physical_width, - FF_MAYBE_UNUSED int32_t physical_height, - FF_MAYBE_UNUSED int32_t subpixel, - const char *make, - const char *model, - int32_t transform) -{ - WaylandDisplay* display = data; - display->transform = (enum wl_output_transform) transform; - if (make && !ffStrEqualsIgnCase(make, "unknown") && model && !ffStrEqualsIgnCase(model, "unknown")) - { - ffStrbufAppendS(&display->vendorAndModelId, make); - ffStrbufAppendS(&display->vendorAndModelId, model); - } -} - -static void waylandOutputNameListener(void *data, FF_MAYBE_UNUSED struct wl_output *output, const char *name) -{ - WaylandDisplay* display = data; - if(ffStrStartsWith(name, "eDP-")) - display->type = FF_DISPLAY_TYPE_BUILTIN; - else if(ffStrStartsWith(name, "HDMI-")) - display->type = FF_DISPLAY_TYPE_EXTERNAL; - ffdsMatchDrmConnector(name, &display->edidName); - ffStrbufAppendS(&display->name, name); -} - -static void waylandOutputDescriptionListener(void* data, FF_MAYBE_UNUSED struct wl_output* wl_output, const char* description) -{ - WaylandDisplay* display = data; - while (*description == ' ') ++description; - if (!ffStrEquals(description, "Unknown Display")) - ffStrbufAppendS(&display->description, description); -} - -static void waylandOutputHandler(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version) -{ - struct wl_proxy* output = wldata->ffwl_proxy_marshal_constructor_versioned((struct wl_proxy*) registry, WL_REGISTRY_BIND, wldata->ffwl_output_interface, version, name, wldata->ffwl_output_interface->name, version, NULL); - if(output == NULL) - return; - - WaylandDisplay display = { - .width = 0, - .height = 0, - .refreshRate = 0, - .scale = 1, - .transform = WL_OUTPUT_TRANSFORM_NORMAL, - .type = FF_DISPLAY_TYPE_UNKNOWN, - }; - ffStrbufInit(&display.name); - ffStrbufInit(&display.description); - ffStrbufInit(&display.vendorAndModelId); - ffStrbufInit(&display.edidName); - - // Dirty hack for #477 - // The order of these callbacks MUST follow `struct wl_output_listener` - void* outputListener[] = { - waylandOutputGeometryListener, // geometry - waylandOutputModeListener, // mode - stubListener, // done - waylandOutputScaleListener, // scale - waylandOutputNameListener, // name - waylandOutputDescriptionListener, // description - }; - static_assert( - sizeof(outputListener) >= sizeof(struct wl_output_listener), - "sizeof(outputListener) is too small. Please report it to fastfetch github issue" - ); - - wldata->ffwl_proxy_add_listener(output, (void(**)(void)) &outputListener, &display); - wldata->ffwl_display_roundtrip(wldata->display); - wldata->ffwl_proxy_destroy(output); - - if(display.width <= 0 || display.height <= 0) - return; - - switch(display.transform) - { - case WL_OUTPUT_TRANSFORM_90: - case WL_OUTPUT_TRANSFORM_270: - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - case WL_OUTPUT_TRANSFORM_FLIPPED_270: { - int32_t temp = display.width; - display.width = display.height; - display.height = temp; - break; - } - default: - break; - } - - uint32_t rotation; - switch(display.transform) - { - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - case WL_OUTPUT_TRANSFORM_90: - rotation = 90; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - case WL_OUTPUT_TRANSFORM_180: - rotation = 180; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - case WL_OUTPUT_TRANSFORM_270: - rotation = 270; - break; - default: - rotation = 0; - break; - } - - ffdsAppendDisplay(wldata->result, - (uint32_t) display.width, - (uint32_t) display.height, - display.refreshRate / 1000.0, - (uint32_t) (display.width / display.scale), - (uint32_t) (display.height / display.scale), - rotation, - display.edidName.length - ? &display.edidName - : display.description.length - ? &display.description - : display.vendorAndModelId.length - ? &display.vendorAndModelId : &display.name, - display.type, - false, - 0 - ); - - ffStrbufDestroy(&display.description); - ffStrbufDestroy(&display.vendorAndModelId); - ffStrbufDestroy(&display.name); - ffStrbufDestroy(&display.edidName); -} - -static void waylandGlobalAddListener(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) -{ - WaylandData* wldata = data; - - if(ffStrEquals(interface, wldata->ffwl_output_interface->name)) - waylandOutputHandler(wldata, registry, name, version); -} - -bool detectWayland(FFDisplayServerResult* result) -{ - FF_LIBRARY_LOAD(wayland, &instance.config.library.libWayland, false, "libwayland-client" FF_LIBRARY_EXTENSION, 1) - - FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_connect, false) - FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_get_fd, false) - FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_dispatch, false) - FF_LIBRARY_LOAD_SYMBOL(wayland, wl_proxy_marshal_constructor, false) - FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_disconnect, false) - FF_LIBRARY_LOAD_SYMBOL(wayland, wl_registry_interface, false) - - WaylandData data; - - FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_proxy_marshal_constructor_versioned, false) - FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_proxy_add_listener, false) - FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_proxy_destroy, false) - FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_display_roundtrip, false) - FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_output_interface, false) - - data.display = ffwl_display_connect(NULL); - if(data.display == NULL) - return false; - - waylandDetectWM(ffwl_display_get_fd(data.display), result); - - struct wl_proxy* registry = ffwl_proxy_marshal_constructor((struct wl_proxy*) data.display, WL_DISPLAY_GET_REGISTRY, ffwl_registry_interface, NULL); - if(registry == NULL) - { - ffwl_display_disconnect(data.display); - return false; - } - - data.result = result; - - struct wl_registry_listener registry_listener = { - .global = waylandGlobalAddListener, - .global_remove = (void*) stubListener - }; - - data.ffwl_proxy_add_listener(registry, (void(**)(void)) ®istry_listener, &data); - ffwl_display_dispatch(data.display); - data.ffwl_display_roundtrip(data.display); - - data.ffwl_proxy_destroy(registry); - ffwl_display_disconnect(data.display); - - //We successfully connected to wayland and detected the display. - //So we can set set the session type to wayland. - //This is used as an indicator that we are running wayland by the x11 backends. - ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND); - return true; -} -#endif - -void ffdsConnectWayland(FFDisplayServerResult* result) -{ - //Wayland requires this to be set - if(getenv("XDG_RUNTIME_DIR") == NULL) - return; - - #ifdef FF_HAVE_WAYLAND - if(detectWayland(result)) - return; - #endif - - const char* xdgSessionType = getenv("XDG_SESSION_TYPE"); - - //If XDG_SESSION_TYPE is set, and doesn't contain "wayland", we are probably not running in a wayland session. - if(xdgSessionType != NULL && strcasecmp(xdgSessionType, "wayland") != 0) - return; - - //If XDG_SESSION_TYPE is not set, check if WAYLAND_DISPLAY or WAYLAND_SOCKET is set. - //If not, there is no indicator for a wayland session - if(xdgSessionType == NULL && getenv("WAYLAND_DISPLAY") == NULL && getenv("WAYLAND_SOCKET") == NULL) - return; - - //We are probably running a wayland compositor at this point, - //but fastfetch was compiled without the required library, or loading the library failed. - ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND); -} diff --git a/src/detection/displayserver/linux/wayland/global-output.c b/src/detection/displayserver/linux/wayland/global-output.c new file mode 100644 index 000000000..c38ceee03 --- /dev/null +++ b/src/detection/displayserver/linux/wayland/global-output.c @@ -0,0 +1,134 @@ +#ifdef FF_HAVE_WAYLAND + +#include "wayland.h" +#include "util/stringUtils.h" + +static void waylandOutputModeListener(void* data, FF_MAYBE_UNUSED struct wl_output* output, uint32_t flags, int32_t width, int32_t height, int32_t refreshRate) +{ + if(!(flags & WL_OUTPUT_MODE_CURRENT)) + return; + + WaylandDisplay* display = data; + display->width = width; + display->height = height; + display->refreshRate = refreshRate; +} + +static void waylandOutputScaleListener(void* data, FF_MAYBE_UNUSED struct wl_output* output, int32_t scale) +{ + WaylandDisplay* display = data; + display->scale = scale; +} + +static void waylandOutputGeometryListener(void *data, + FF_MAYBE_UNUSED struct wl_output *output, + FF_MAYBE_UNUSED int32_t x, + FF_MAYBE_UNUSED int32_t y, + FF_MAYBE_UNUSED int32_t physical_width, + FF_MAYBE_UNUSED int32_t physical_height, + FF_MAYBE_UNUSED int32_t subpixel, + FF_MAYBE_UNUSED const char *make, + FF_MAYBE_UNUSED const char *model, + int32_t transform) +{ + WaylandDisplay* display = data; + display->transform = (enum wl_output_transform) transform; +} + +void ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version) +{ + struct wl_proxy* output = wldata->ffwl_proxy_marshal_constructor_versioned((struct wl_proxy*) registry, WL_REGISTRY_BIND, wldata->ffwl_output_interface, version, name, wldata->ffwl_output_interface->name, version, NULL); + if(output == NULL) + return; + + WaylandDisplay display = { + .parent = wldata, + .width = 0, + .height = 0, + .refreshRate = 0, + .scale = 1, + .transform = WL_OUTPUT_TRANSFORM_NORMAL, + .type = FF_DISPLAY_TYPE_UNKNOWN, + .name = ffStrbufCreate(), + .description = ffStrbufCreate(), + .edidName = ffStrbufCreate(), + }; + + // Dirty hack for #477 + // The order of these callbacks MUST follow `struct wl_output_listener` + void* outputListener[] = { + waylandOutputGeometryListener, // geometry + waylandOutputModeListener, // mode + stubListener, // done + waylandOutputScaleListener, // scale + ffWaylandOutputNameListener, // name + ffWaylandOutputDescriptionListener, // description + }; + static_assert( + sizeof(outputListener) >= sizeof(struct wl_output_listener), + "sizeof(outputListener) is too small. Please report it to fastfetch github issue" + ); + + wldata->ffwl_proxy_add_listener(output, (void(**)(void)) &outputListener, &display); + wldata->ffwl_display_roundtrip(wldata->display); + wldata->ffwl_proxy_destroy(output); + + if(display.width <= 0 || display.height <= 0) + return; + + uint32_t rotation; + switch(display.transform) + { + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_90: + rotation = 90; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + case WL_OUTPUT_TRANSFORM_180: + rotation = 180; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + case WL_OUTPUT_TRANSFORM_270: + rotation = 270; + break; + default: + rotation = 0; + break; + } + + switch(rotation) + { + case 90: + case 270: { + int32_t temp = display.width; + display.width = display.height; + display.height = temp; + break; + } + default: + break; + } + + ffdsAppendDisplay(wldata->result, + (uint32_t) display.width, + (uint32_t) display.height, + display.refreshRate / 1000.0, + (uint32_t) (display.width / display.scale), + (uint32_t) (display.height / display.scale), + rotation, + display.edidName.length + ? &display.edidName + : display.description.length + ? &display.description + : &display.name, + display.type, + false, + 0 + ); + + ffStrbufDestroy(&display.description); + ffStrbufDestroy(&display.name); + ffStrbufDestroy(&display.edidName); +} + +#endif diff --git a/src/detection/displayserver/linux/wayland/wayland.c b/src/detection/displayserver/linux/wayland/wayland.c new file mode 100644 index 000000000..09ae7ae8e --- /dev/null +++ b/src/detection/displayserver/linux/wayland/wayland.c @@ -0,0 +1,149 @@ +#include "../displayserver_linux.h" +#include "common/io/io.h" +#include "util/stringUtils.h" + +#include +#include + +#ifdef FF_HAVE_WAYLAND + +#include + +#include "wayland.h" +#include "wlr-output-management-unstable-v1-client-protocol.h" + +#ifndef __FreeBSD__ +static void waylandDetectWM(int fd, FFDisplayServerResult* result) +{ + struct ucred ucred; + socklen_t len = sizeof(struct ucred); + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) + return; + + FF_STRBUF_AUTO_DESTROY procPath = ffStrbufCreate(); + ffStrbufAppendF(&procPath, "/proc/%d/cmdline", ucred.pid); //We check the cmdline for the process name, because it is not trimmed. + ffReadFileBuffer(procPath.chars, &result->wmProcessName); + ffStrbufTrimRightSpace(&result->wmProcessName); + ffStrbufSubstrBeforeFirstC(&result->wmProcessName, '\0'); //Trim the arguments + ffStrbufSubstrAfterLastC(&result->wmProcessName, '/'); //Trim the path +} +#else +static void waylandDetectWM(int fd, FFDisplayServerResult* result) +{ + FF_UNUSED(fd, result); +} +#endif + +static void waylandGlobalAddListener(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) +{ + WaylandData* wldata = data; + + if((wldata->protocolType == FF_WAYLAND_PROTOCOL_TYPE_NONE || wldata->protocolType == FF_WAYLAND_PROTOCOL_TYPE_GLOBAL) && ffStrEquals(interface, wldata->ffwl_output_interface->name)) + { + wldata->protocolType = FF_WAYLAND_PROTOCOL_TYPE_GLOBAL; + ffWaylandHandleGlobalOutput(wldata, registry, name, version); + } + else if((wldata->protocolType == FF_WAYLAND_PROTOCOL_TYPE_NONE || wldata->protocolType == FF_WAYLAND_PROTOCOL_TYPE_ZWLR) && ffStrEquals(interface, zwlr_output_manager_v1_interface.name)) + { + wldata->protocolType = FF_WAYLAND_PROTOCOL_TYPE_ZWLR; + ffWaylandHandleZwlrOutput(wldata, registry, name, version); + } +} + +bool detectWayland(FFDisplayServerResult* result) +{ + FF_LIBRARY_LOAD(wayland, &instance.config.library.libWayland, false, "libwayland-client" FF_LIBRARY_EXTENSION, 1) + + FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_connect, false) + FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_get_fd, false) + FF_LIBRARY_LOAD_SYMBOL(wayland, wl_proxy_marshal_constructor, false) + FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_disconnect, false) + FF_LIBRARY_LOAD_SYMBOL(wayland, wl_registry_interface, false) + + WaylandData data = {}; + + FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_proxy_marshal_constructor_versioned, false) + FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_proxy_add_listener, false) + FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_proxy_destroy, false) + FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_display_roundtrip, false) + FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_output_interface, false) + + data.display = ffwl_display_connect(NULL); + if(data.display == NULL) + return false; + + waylandDetectWM(ffwl_display_get_fd(data.display), result); + + struct wl_proxy* registry = ffwl_proxy_marshal_constructor((struct wl_proxy*) data.display, WL_DISPLAY_GET_REGISTRY, ffwl_registry_interface, NULL); + if(registry == NULL) + { + ffwl_display_disconnect(data.display); + return false; + } + + data.result = result; + + struct wl_registry_listener registry_listener = { + .global = waylandGlobalAddListener, + .global_remove = (void*) stubListener + }; + + data.ffwl_proxy_add_listener(registry, (void(**)(void)) ®istry_listener, &data); + data.ffwl_display_roundtrip(data.display); + + data.ffwl_proxy_destroy(registry); + ffwl_display_disconnect(data.display); + + //We successfully connected to wayland and detected the display. + //So we can set set the session type to wayland. + //This is used as an indicator that we are running wayland by the x11 backends. + ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND); + return true; +} + +void ffWaylandOutputNameListener(void* data, FF_MAYBE_UNUSED void* output, const char *name) +{ + WaylandDisplay* display = data; + if(ffStrStartsWith(name, "eDP-")) + display->type = FF_DISPLAY_TYPE_BUILTIN; + else if(ffStrStartsWith(name, "HDMI-")) + display->type = FF_DISPLAY_TYPE_EXTERNAL; + ffdsMatchDrmConnector(name, &display->edidName); + ffStrbufAppendS(&display->name, name); +} + +void ffWaylandOutputDescriptionListener(void* data, FF_MAYBE_UNUSED void* output, const char* description) +{ + WaylandDisplay* display = data; + while (*description == ' ') ++description; + if (!ffStrEquals(description, "Unknown Display") && !ffStrContains(description, "(null)")) + ffStrbufAppendS(&display->description, description); +} +#endif + +void ffdsConnectWayland(FFDisplayServerResult* result) +{ + //Wayland requires this to be set + if(getenv("XDG_RUNTIME_DIR") == NULL) + return; + + #ifdef FF_HAVE_WAYLAND + if(detectWayland(result)) + return; + #endif + + const char* xdgSessionType = getenv("XDG_SESSION_TYPE"); + + //If XDG_SESSION_TYPE is set, and doesn't contain "wayland", we are probably not running in a wayland session. + if(xdgSessionType != NULL && ffStrEqualsIgnCase(xdgSessionType, "wayland")) + return; + + //If XDG_SESSION_TYPE is not set, check if WAYLAND_DISPLAY or WAYLAND_SOCKET is set. + //If not, there is no indicator for a wayland session + if(xdgSessionType == NULL && getenv("WAYLAND_DISPLAY") == NULL && getenv("WAYLAND_SOCKET") == NULL) + return; + + //We are probably running a wayland compositor at this point, + //but fastfetch was compiled without the required library, or loading the library failed. + ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND); +} diff --git a/src/detection/displayserver/linux/wayland/wayland.h b/src/detection/displayserver/linux/wayland/wayland.h new file mode 100644 index 000000000..4a65a98ad --- /dev/null +++ b/src/detection/displayserver/linux/wayland/wayland.h @@ -0,0 +1,57 @@ +#pragma once + +#ifdef FF_HAVE_WAYLAND + +#include "common/library.h" +#include "util/stringUtils.h" + +#include + +#include "../displayserver_linux.h" + +typedef enum WaylandProtocolType +{ + FF_WAYLAND_PROTOCOL_TYPE_NONE, + FF_WAYLAND_PROTOCOL_TYPE_GLOBAL, + FF_WAYLAND_PROTOCOL_TYPE_ZWLR, +} WaylandProtocolType; + +typedef struct WaylandData +{ + FFDisplayServerResult* result; + FF_LIBRARY_SYMBOL(wl_proxy_marshal_constructor_versioned) + FF_LIBRARY_SYMBOL(wl_proxy_add_listener) + FF_LIBRARY_SYMBOL(wl_proxy_destroy) + FF_LIBRARY_SYMBOL(wl_display_roundtrip) + struct wl_display* display; + const struct wl_interface* ffwl_output_interface; + WaylandProtocolType protocolType; +} WaylandData; + +typedef struct WaylandDisplay +{ + WaylandData* parent; + int32_t width; + int32_t height; + int32_t refreshRate; + double scale; + enum wl_output_transform transform; + FFDisplayType type; + FFstrbuf name; + FFstrbuf description; + FFstrbuf edidName; + void* internal; +} WaylandDisplay; + +inline static void stubListener(void* data, ...) +{ + (void) data; +} + +void ffWaylandOutputNameListener(void* data, FF_MAYBE_UNUSED void* output, const char *name); +void ffWaylandOutputDescriptionListener(void* data, FF_MAYBE_UNUSED void* output, const char* description); + +void ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version); +void ffWaylandHandleZwlrOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version); + +#endif diff --git a/src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-client-protocol.h b/src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-client-protocol.h new file mode 100644 index 000000000..e00668ffd --- /dev/null +++ b/src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-client-protocol.h @@ -0,0 +1,1298 @@ +/* Generated by wayland-scanner 1.22.0 */ + +#ifndef WLR_OUTPUT_MANAGEMENT_UNSTABLE_V1_CLIENT_PROTOCOL_H +#define WLR_OUTPUT_MANAGEMENT_UNSTABLE_V1_CLIENT_PROTOCOL_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_wlr_output_management_unstable_v1 The wlr_output_management_unstable_v1 protocol + * protocol to configure output devices + * + * @section page_desc_wlr_output_management_unstable_v1 Description + * + * This protocol exposes interfaces to obtain and modify output device + * configuration. + * + * Warning! The protocol described in this file is experimental and + * backward incompatible changes may be made. Backward compatible changes + * may be added together with the corresponding interface version bump. + * Backward incompatible changes are done by bumping the version number in + * the protocol and interface names and resetting the interface version. + * Once the protocol is to be declared stable, the 'z' prefix and the + * version number in the protocol and interface names are removed and the + * interface version number is reset. + * + * @section page_ifaces_wlr_output_management_unstable_v1 Interfaces + * - @subpage page_iface_zwlr_output_manager_v1 - output device configuration manager + * - @subpage page_iface_zwlr_output_head_v1 - output device + * - @subpage page_iface_zwlr_output_mode_v1 - output mode + * - @subpage page_iface_zwlr_output_configuration_v1 - output configuration + * - @subpage page_iface_zwlr_output_configuration_head_v1 - head configuration + * @section page_copyright_wlr_output_management_unstable_v1 Copyright + *
+ *
+ * Copyright © 2019 Purism SPC
+ *
+ * Permission to use, copy, modify, distribute, and sell this
+ * software and its documentation for any purpose is hereby granted
+ * without fee, provided that the above copyright notice appear in
+ * all copies and that both that copyright notice and this permission
+ * notice appear in supporting documentation, and that the name of
+ * the copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ * THIS SOFTWARE.
+ * 
+ */ +struct zwlr_output_configuration_head_v1; +struct zwlr_output_configuration_v1; +struct zwlr_output_head_v1; +struct zwlr_output_manager_v1; +struct zwlr_output_mode_v1; + +#ifndef ZWLR_OUTPUT_MANAGER_V1_INTERFACE +#define ZWLR_OUTPUT_MANAGER_V1_INTERFACE +/** + * @page page_iface_zwlr_output_manager_v1 zwlr_output_manager_v1 + * @section page_iface_zwlr_output_manager_v1_desc Description + * + * This interface is a manager that allows reading and writing the current + * output device configuration. + * + * Output devices that display pixels (e.g. a physical monitor or a virtual + * output in a window) are represented as heads. Heads cannot be created nor + * destroyed by the client, but they can be enabled or disabled and their + * properties can be changed. Each head may have one or more available modes. + * + * Whenever a head appears (e.g. a monitor is plugged in), it will be + * advertised via the head event. Immediately after the output manager is + * bound, all current heads are advertised. + * + * Whenever a head's properties change, the relevant wlr_output_head events + * will be sent. Not all head properties will be sent: only properties that + * have changed need to. + * + * Whenever a head disappears (e.g. a monitor is unplugged), a + * wlr_output_head.finished event will be sent. + * + * After one or more heads appear, change or disappear, the done event will + * be sent. It carries a serial which can be used in a create_configuration + * request to update heads properties. + * + * The information obtained from this protocol should only be used for output + * configuration purposes. This protocol is not designed to be a generic + * output property advertisement protocol for regular clients. Instead, + * protocols such as xdg-output should be used. + * @section page_iface_zwlr_output_manager_v1_api API + * See @ref iface_zwlr_output_manager_v1. + */ +/** + * @defgroup iface_zwlr_output_manager_v1 The zwlr_output_manager_v1 interface + * + * This interface is a manager that allows reading and writing the current + * output device configuration. + * + * Output devices that display pixels (e.g. a physical monitor or a virtual + * output in a window) are represented as heads. Heads cannot be created nor + * destroyed by the client, but they can be enabled or disabled and their + * properties can be changed. Each head may have one or more available modes. + * + * Whenever a head appears (e.g. a monitor is plugged in), it will be + * advertised via the head event. Immediately after the output manager is + * bound, all current heads are advertised. + * + * Whenever a head's properties change, the relevant wlr_output_head events + * will be sent. Not all head properties will be sent: only properties that + * have changed need to. + * + * Whenever a head disappears (e.g. a monitor is unplugged), a + * wlr_output_head.finished event will be sent. + * + * After one or more heads appear, change or disappear, the done event will + * be sent. It carries a serial which can be used in a create_configuration + * request to update heads properties. + * + * The information obtained from this protocol should only be used for output + * configuration purposes. This protocol is not designed to be a generic + * output property advertisement protocol for regular clients. Instead, + * protocols such as xdg-output should be used. + */ +extern const struct wl_interface zwlr_output_manager_v1_interface; +#endif +#ifndef ZWLR_OUTPUT_HEAD_V1_INTERFACE +#define ZWLR_OUTPUT_HEAD_V1_INTERFACE +/** + * @page page_iface_zwlr_output_head_v1 zwlr_output_head_v1 + * @section page_iface_zwlr_output_head_v1_desc Description + * + * A head is an output device. The difference between a wl_output object and + * a head is that heads are advertised even if they are turned off. A head + * object only advertises properties and cannot be used directly to change + * them. + * + * A head has some read-only properties: modes, name, description and + * physical_size. These cannot be changed by clients. + * + * Other properties can be updated via a wlr_output_configuration object. + * + * Properties sent via this interface are applied atomically via the + * wlr_output_manager.done event. No guarantees are made regarding the order + * in which properties are sent. + * @section page_iface_zwlr_output_head_v1_api API + * See @ref iface_zwlr_output_head_v1. + */ +/** + * @defgroup iface_zwlr_output_head_v1 The zwlr_output_head_v1 interface + * + * A head is an output device. The difference between a wl_output object and + * a head is that heads are advertised even if they are turned off. A head + * object only advertises properties and cannot be used directly to change + * them. + * + * A head has some read-only properties: modes, name, description and + * physical_size. These cannot be changed by clients. + * + * Other properties can be updated via a wlr_output_configuration object. + * + * Properties sent via this interface are applied atomically via the + * wlr_output_manager.done event. No guarantees are made regarding the order + * in which properties are sent. + */ +extern const struct wl_interface zwlr_output_head_v1_interface; +#endif +#ifndef ZWLR_OUTPUT_MODE_V1_INTERFACE +#define ZWLR_OUTPUT_MODE_V1_INTERFACE +/** + * @page page_iface_zwlr_output_mode_v1 zwlr_output_mode_v1 + * @section page_iface_zwlr_output_mode_v1_desc Description + * + * This object describes an output mode. + * + * Some heads don't support output modes, in which case modes won't be + * advertised. + * + * Properties sent via this interface are applied atomically via the + * wlr_output_manager.done event. No guarantees are made regarding the order + * in which properties are sent. + * @section page_iface_zwlr_output_mode_v1_api API + * See @ref iface_zwlr_output_mode_v1. + */ +/** + * @defgroup iface_zwlr_output_mode_v1 The zwlr_output_mode_v1 interface + * + * This object describes an output mode. + * + * Some heads don't support output modes, in which case modes won't be + * advertised. + * + * Properties sent via this interface are applied atomically via the + * wlr_output_manager.done event. No guarantees are made regarding the order + * in which properties are sent. + */ +extern const struct wl_interface zwlr_output_mode_v1_interface; +#endif +#ifndef ZWLR_OUTPUT_CONFIGURATION_V1_INTERFACE +#define ZWLR_OUTPUT_CONFIGURATION_V1_INTERFACE +/** + * @page page_iface_zwlr_output_configuration_v1 zwlr_output_configuration_v1 + * @section page_iface_zwlr_output_configuration_v1_desc Description + * + * This object is used by the client to describe a full output configuration. + * + * First, the client needs to setup the output configuration. Each head can + * be either enabled (and configured) or disabled. It is a protocol error to + * send two enable_head or disable_head requests with the same head. It is a + * protocol error to omit a head in a configuration. + * + * Then, the client can apply or test the configuration. The compositor will + * then reply with a succeeded, failed or cancelled event. Finally the client + * should destroy the configuration object. + * @section page_iface_zwlr_output_configuration_v1_api API + * See @ref iface_zwlr_output_configuration_v1. + */ +/** + * @defgroup iface_zwlr_output_configuration_v1 The zwlr_output_configuration_v1 interface + * + * This object is used by the client to describe a full output configuration. + * + * First, the client needs to setup the output configuration. Each head can + * be either enabled (and configured) or disabled. It is a protocol error to + * send two enable_head or disable_head requests with the same head. It is a + * protocol error to omit a head in a configuration. + * + * Then, the client can apply or test the configuration. The compositor will + * then reply with a succeeded, failed or cancelled event. Finally the client + * should destroy the configuration object. + */ +extern const struct wl_interface zwlr_output_configuration_v1_interface; +#endif +#ifndef ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_INTERFACE +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_INTERFACE +/** + * @page page_iface_zwlr_output_configuration_head_v1 zwlr_output_configuration_head_v1 + * @section page_iface_zwlr_output_configuration_head_v1_desc Description + * + * This object is used by the client to update a single head's configuration. + * + * It is a protocol error to set the same property twice. + * @section page_iface_zwlr_output_configuration_head_v1_api API + * See @ref iface_zwlr_output_configuration_head_v1. + */ +/** + * @defgroup iface_zwlr_output_configuration_head_v1 The zwlr_output_configuration_head_v1 interface + * + * This object is used by the client to update a single head's configuration. + * + * It is a protocol error to set the same property twice. + */ +extern const struct wl_interface zwlr_output_configuration_head_v1_interface; +#endif + +/** + * @ingroup iface_zwlr_output_manager_v1 + * @struct zwlr_output_manager_v1_listener + */ +struct zwlr_output_manager_v1_listener { + /** + * introduce a new head + * + * This event introduces a new head. This happens whenever a new + * head appears (e.g. a monitor is plugged in) or after the output + * manager is bound. + */ + void (*head)(void *data, + struct zwlr_output_manager_v1 *zwlr_output_manager_v1, + struct zwlr_output_head_v1 *head); + /** + * sent all information about current configuration + * + * This event is sent after all information has been sent after + * binding to the output manager object and after any subsequent + * changes. This applies to child head and mode objects as well. In + * other words, this event is sent whenever a head or mode is + * created or destroyed and whenever one of their properties has + * been changed. Not all state is re-sent each time the current + * configuration changes: only the actual changes are sent. + * + * This allows changes to the output configuration to be seen as + * atomic, even if they happen via multiple events. + * + * A serial is sent to be used in a future create_configuration + * request. + * @param serial current configuration serial + */ + void (*done)(void *data, + struct zwlr_output_manager_v1 *zwlr_output_manager_v1, + uint32_t serial); + /** + * the compositor has finished with the manager + * + * This event indicates that the compositor is done sending + * manager events. The compositor will destroy the object + * immediately after sending this event, so it will become invalid + * and the client should release any resources associated with it. + */ + void (*finished)(void *data, + struct zwlr_output_manager_v1 *zwlr_output_manager_v1); +}; + +/** + * @ingroup iface_zwlr_output_manager_v1 + */ +static inline int +zwlr_output_manager_v1_add_listener(struct zwlr_output_manager_v1 *zwlr_output_manager_v1, + const struct zwlr_output_manager_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwlr_output_manager_v1, + (void (**)(void)) listener, data); +} + +#define ZWLR_OUTPUT_MANAGER_V1_CREATE_CONFIGURATION 0 +#define ZWLR_OUTPUT_MANAGER_V1_STOP 1 + +/** + * @ingroup iface_zwlr_output_manager_v1 + */ +#define ZWLR_OUTPUT_MANAGER_V1_HEAD_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_manager_v1 + */ +#define ZWLR_OUTPUT_MANAGER_V1_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_manager_v1 + */ +#define ZWLR_OUTPUT_MANAGER_V1_FINISHED_SINCE_VERSION 1 + +/** + * @ingroup iface_zwlr_output_manager_v1 + */ +#define ZWLR_OUTPUT_MANAGER_V1_CREATE_CONFIGURATION_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_manager_v1 + */ +#define ZWLR_OUTPUT_MANAGER_V1_STOP_SINCE_VERSION 1 + +/** @ingroup iface_zwlr_output_manager_v1 */ +static inline void +zwlr_output_manager_v1_set_user_data(struct zwlr_output_manager_v1 *zwlr_output_manager_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwlr_output_manager_v1, user_data); +} + +/** @ingroup iface_zwlr_output_manager_v1 */ +static inline void * +zwlr_output_manager_v1_get_user_data(struct zwlr_output_manager_v1 *zwlr_output_manager_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_output_manager_v1); +} + +static inline uint32_t +zwlr_output_manager_v1_get_version(struct zwlr_output_manager_v1 *zwlr_output_manager_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwlr_output_manager_v1); +} + +/** @ingroup iface_zwlr_output_manager_v1 */ +static inline void +zwlr_output_manager_v1_destroy(struct zwlr_output_manager_v1 *zwlr_output_manager_v1) +{ + wl_proxy_destroy((struct wl_proxy *) zwlr_output_manager_v1); +} + +/** + * @ingroup iface_zwlr_output_manager_v1 + * + * Create a new output configuration object. This allows to update head + * properties. + */ +static inline struct zwlr_output_configuration_v1 * +zwlr_output_manager_v1_create_configuration(struct zwlr_output_manager_v1 *zwlr_output_manager_v1, uint32_t serial) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_manager_v1, + ZWLR_OUTPUT_MANAGER_V1_CREATE_CONFIGURATION, &zwlr_output_configuration_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwlr_output_manager_v1), 0, NULL, serial); + + return (struct zwlr_output_configuration_v1 *) id; +} + +/** + * @ingroup iface_zwlr_output_manager_v1 + * + * Indicates the client no longer wishes to receive events for output + * configuration changes. However the compositor may emit further events, + * until the finished event is emitted. + * + * The client must not send any more requests after this one. + */ +static inline void +zwlr_output_manager_v1_stop(struct zwlr_output_manager_v1 *zwlr_output_manager_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_manager_v1, + ZWLR_OUTPUT_MANAGER_V1_STOP, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_manager_v1), 0); +} + +#ifndef ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENUM +#define ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENUM +enum zwlr_output_head_v1_adaptive_sync_state { + /** + * adaptive sync is disabled + */ + ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED = 0, + /** + * adaptive sync is enabled + */ + ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED = 1, +}; +#endif /* ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENUM */ + +/** + * @ingroup iface_zwlr_output_head_v1 + * @struct zwlr_output_head_v1_listener + */ +struct zwlr_output_head_v1_listener { + /** + * head name + * + * This event describes the head name. + * + * The naming convention is compositor defined, but limited to + * alphanumeric characters and dashes (-). Each name is unique + * among all wlr_output_head objects, but if a wlr_output_head + * object is destroyed the same name may be reused later. The names + * will also remain consistent across sessions with the same + * hardware and software configuration. + * + * Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. + * However, do not assume that the name is a reflection of an + * underlying DRM connector, X11 connection, etc. + * + * If the compositor implements the xdg-output protocol and this + * head is enabled, the xdg_output.name event must report the same + * name. + * + * The name event is sent after a wlr_output_head object is + * created. This event is only sent once per object, and the name + * does not change over the lifetime of the wlr_output_head object. + */ + void (*name)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + const char *name); + /** + * head description + * + * This event describes a human-readable description of the head. + * + * The description is a UTF-8 string with no convention defined for + * its contents. Examples might include 'Foocorp 11" Display' or + * 'Virtual X11 output via :1'. However, do not assume that the + * name is a reflection of the make, model, serial of the + * underlying DRM connector or the display name of the underlying + * X11 connection, etc. + * + * If the compositor implements xdg-output and this head is + * enabled, the xdg_output.description must report the same + * description. + * + * The description event is sent after a wlr_output_head object is + * created. This event is only sent once per object, and the + * description does not change over the lifetime of the + * wlr_output_head object. + */ + void (*description)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + const char *description); + /** + * head physical size + * + * This event describes the physical size of the head. This event + * is only sent if the head has a physical size (e.g. is not a + * projector or a virtual device). + * @param width width in millimeters of the output + * @param height height in millimeters of the output + */ + void (*physical_size)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + int32_t width, + int32_t height); + /** + * introduce a mode + * + * This event introduces a mode for this head. It is sent once + * per supported mode. + */ + void (*mode)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + struct zwlr_output_mode_v1 *mode); + /** + * head is enabled or disabled + * + * This event describes whether the head is enabled. A disabled + * head is not mapped to a region of the global compositor space. + * + * When a head is disabled, some properties (current_mode, + * position, transform and scale) are irrelevant. + * @param enabled zero if disabled, non-zero if enabled + */ + void (*enabled)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + int32_t enabled); + /** + * current mode + * + * This event describes the mode currently in use for this head. + * It is only sent if the output is enabled. + */ + void (*current_mode)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + struct zwlr_output_mode_v1 *mode); + /** + * current position + * + * This events describes the position of the head in the global + * compositor space. It is only sent if the output is enabled. + * @param x x position within the global compositor space + * @param y y position within the global compositor space + */ + void (*position)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + int32_t x, + int32_t y); + /** + * current transformation + * + * This event describes the transformation currently applied to + * the head. It is only sent if the output is enabled. + */ + void (*transform)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + int32_t transform); + /** + * current scale + * + * This events describes the scale of the head in the global + * compositor space. It is only sent if the output is enabled. + */ + void (*scale)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + wl_fixed_t scale); + /** + * the head has disappeared + * + * This event indicates that the head is no longer available. The + * head object becomes inert. Clients should send a destroy request + * and release any resources associated with it. + */ + void (*finished)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1); + /** + * head manufacturer + * + * This event describes the manufacturer of the head. + * + * This must report the same make as the wl_output interface does + * in its geometry event. + * + * Together with the model and serial_number events the purpose is + * to allow clients to recognize heads from previous sessions and + * for example load head-specific configurations back. + * + * It is not guaranteed this event will be ever sent. A reason for + * that can be that the compositor does not have information about + * the make of the head or the definition of a make is not sensible + * in the current setup, for example in a virtual session. Clients + * can still try to identify the head by available information from + * other events but should be aware that there is an increased risk + * of false positives. + * + * It is not recommended to display the make string in UI to users. + * For that the string provided by the description event should be + * preferred. + * @since 2 + */ + void (*make)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + const char *make); + /** + * head model + * + * This event describes the model of the head. + * + * This must report the same model as the wl_output interface does + * in its geometry event. + * + * Together with the make and serial_number events the purpose is + * to allow clients to recognize heads from previous sessions and + * for example load head-specific configurations back. + * + * It is not guaranteed this event will be ever sent. A reason for + * that can be that the compositor does not have information about + * the model of the head or the definition of a model is not + * sensible in the current setup, for example in a virtual session. + * Clients can still try to identify the head by available + * information from other events but should be aware that there is + * an increased risk of false positives. + * + * It is not recommended to display the model string in UI to + * users. For that the string provided by the description event + * should be preferred. + * @since 2 + */ + void (*model)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + const char *model); + /** + * head serial number + * + * This event describes the serial number of the head. + * + * Together with the make and model events the purpose is to allow + * clients to recognize heads from previous sessions and for + * example load head- specific configurations back. + * + * It is not guaranteed this event will be ever sent. A reason for + * that can be that the compositor does not have information about + * the serial number of the head or the definition of a serial + * number is not sensible in the current setup. Clients can still + * try to identify the head by available information from other + * events but should be aware that there is an increased risk of + * false positives. + * + * It is not recommended to display the serial_number string in UI + * to users. For that the string provided by the description event + * should be preferred. + * @since 2 + */ + void (*serial_number)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + const char *serial_number); + /** + * current adaptive sync state + * + * This event describes whether adaptive sync is currently + * enabled for the head or not. Adaptive sync is also known as + * Variable Refresh Rate or VRR. + * @since 4 + */ + void (*adaptive_sync)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + uint32_t state); +}; + +/** + * @ingroup iface_zwlr_output_head_v1 + */ +static inline int +zwlr_output_head_v1_add_listener(struct zwlr_output_head_v1 *zwlr_output_head_v1, + const struct zwlr_output_head_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwlr_output_head_v1, + (void (**)(void)) listener, data); +} + +#define ZWLR_OUTPUT_HEAD_V1_RELEASE 0 + +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_NAME_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_DESCRIPTION_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_PHYSICAL_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_MODE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_ENABLED_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_CURRENT_MODE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_POSITION_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_TRANSFORM_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_SCALE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_FINISHED_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_MAKE_SINCE_VERSION 2 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_MODEL_SINCE_VERSION 2 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_SERIAL_NUMBER_SINCE_VERSION 2 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_SINCE_VERSION 4 + +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_RELEASE_SINCE_VERSION 3 + +/** @ingroup iface_zwlr_output_head_v1 */ +static inline void +zwlr_output_head_v1_set_user_data(struct zwlr_output_head_v1 *zwlr_output_head_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwlr_output_head_v1, user_data); +} + +/** @ingroup iface_zwlr_output_head_v1 */ +static inline void * +zwlr_output_head_v1_get_user_data(struct zwlr_output_head_v1 *zwlr_output_head_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_output_head_v1); +} + +static inline uint32_t +zwlr_output_head_v1_get_version(struct zwlr_output_head_v1 *zwlr_output_head_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwlr_output_head_v1); +} + +/** @ingroup iface_zwlr_output_head_v1 */ +static inline void +zwlr_output_head_v1_destroy(struct zwlr_output_head_v1 *zwlr_output_head_v1) +{ + wl_proxy_destroy((struct wl_proxy *) zwlr_output_head_v1); +} + +/** + * @ingroup iface_zwlr_output_head_v1 + * + * This request indicates that the client will no longer use this head + * object. + */ +static inline void +zwlr_output_head_v1_release(struct zwlr_output_head_v1 *zwlr_output_head_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_head_v1, + ZWLR_OUTPUT_HEAD_V1_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_head_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zwlr_output_mode_v1 + * @struct zwlr_output_mode_v1_listener + */ +struct zwlr_output_mode_v1_listener { + /** + * mode size + * + * This event describes the mode size. The size is given in + * physical hardware units of the output device. This is not + * necessarily the same as the output size in the global compositor + * space. For instance, the output may be scaled or transformed. + * @param width width of the mode in hardware units + * @param height height of the mode in hardware units + */ + void (*size)(void *data, + struct zwlr_output_mode_v1 *zwlr_output_mode_v1, + int32_t width, + int32_t height); + /** + * mode refresh rate + * + * This event describes the mode's fixed vertical refresh rate. + * It is only sent if the mode has a fixed refresh rate. + * @param refresh vertical refresh rate in mHz + */ + void (*refresh)(void *data, + struct zwlr_output_mode_v1 *zwlr_output_mode_v1, + int32_t refresh); + /** + * mode is preferred + * + * This event advertises this mode as preferred. + */ + void (*preferred)(void *data, + struct zwlr_output_mode_v1 *zwlr_output_mode_v1); + /** + * the mode has disappeared + * + * This event indicates that the mode is no longer available. The + * mode object becomes inert. Clients should send a destroy request + * and release any resources associated with it. + */ + void (*finished)(void *data, + struct zwlr_output_mode_v1 *zwlr_output_mode_v1); +}; + +/** + * @ingroup iface_zwlr_output_mode_v1 + */ +static inline int +zwlr_output_mode_v1_add_listener(struct zwlr_output_mode_v1 *zwlr_output_mode_v1, + const struct zwlr_output_mode_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwlr_output_mode_v1, + (void (**)(void)) listener, data); +} + +#define ZWLR_OUTPUT_MODE_V1_RELEASE 0 + +/** + * @ingroup iface_zwlr_output_mode_v1 + */ +#define ZWLR_OUTPUT_MODE_V1_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_mode_v1 + */ +#define ZWLR_OUTPUT_MODE_V1_REFRESH_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_mode_v1 + */ +#define ZWLR_OUTPUT_MODE_V1_PREFERRED_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_mode_v1 + */ +#define ZWLR_OUTPUT_MODE_V1_FINISHED_SINCE_VERSION 1 + +/** + * @ingroup iface_zwlr_output_mode_v1 + */ +#define ZWLR_OUTPUT_MODE_V1_RELEASE_SINCE_VERSION 3 + +/** @ingroup iface_zwlr_output_mode_v1 */ +static inline void +zwlr_output_mode_v1_set_user_data(struct zwlr_output_mode_v1 *zwlr_output_mode_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwlr_output_mode_v1, user_data); +} + +/** @ingroup iface_zwlr_output_mode_v1 */ +static inline void * +zwlr_output_mode_v1_get_user_data(struct zwlr_output_mode_v1 *zwlr_output_mode_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_output_mode_v1); +} + +static inline uint32_t +zwlr_output_mode_v1_get_version(struct zwlr_output_mode_v1 *zwlr_output_mode_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwlr_output_mode_v1); +} + +/** @ingroup iface_zwlr_output_mode_v1 */ +static inline void +zwlr_output_mode_v1_destroy(struct zwlr_output_mode_v1 *zwlr_output_mode_v1) +{ + wl_proxy_destroy((struct wl_proxy *) zwlr_output_mode_v1); +} + +/** + * @ingroup iface_zwlr_output_mode_v1 + * + * This request indicates that the client will no longer use this mode + * object. + */ +static inline void +zwlr_output_mode_v1_release(struct zwlr_output_mode_v1 *zwlr_output_mode_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_mode_v1, + ZWLR_OUTPUT_MODE_V1_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_mode_v1), WL_MARSHAL_FLAG_DESTROY); +} + +#ifndef ZWLR_OUTPUT_CONFIGURATION_V1_ERROR_ENUM +#define ZWLR_OUTPUT_CONFIGURATION_V1_ERROR_ENUM +enum zwlr_output_configuration_v1_error { + /** + * head has been configured twice + */ + ZWLR_OUTPUT_CONFIGURATION_V1_ERROR_ALREADY_CONFIGURED_HEAD = 1, + /** + * head has not been configured + */ + ZWLR_OUTPUT_CONFIGURATION_V1_ERROR_UNCONFIGURED_HEAD = 2, + /** + * request sent after configuration has been applied or tested + */ + ZWLR_OUTPUT_CONFIGURATION_V1_ERROR_ALREADY_USED = 3, +}; +#endif /* ZWLR_OUTPUT_CONFIGURATION_V1_ERROR_ENUM */ + +/** + * @ingroup iface_zwlr_output_configuration_v1 + * @struct zwlr_output_configuration_v1_listener + */ +struct zwlr_output_configuration_v1_listener { + /** + * configuration changes succeeded + * + * Sent after the compositor has successfully applied the changes + * or tested them. + * + * Upon receiving this event, the client should destroy this + * object. + * + * If the current configuration has changed, events to describe the + * changes will be sent followed by a wlr_output_manager.done + * event. + */ + void (*succeeded)(void *data, + struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1); + /** + * configuration changes failed + * + * Sent if the compositor rejects the changes or failed to apply + * them. The compositor should revert any changes made by the apply + * request that triggered this event. + * + * Upon receiving this event, the client should destroy this + * object. + */ + void (*failed)(void *data, + struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1); + /** + * configuration has been cancelled + * + * Sent if the compositor cancels the configuration because the + * state of an output changed and the client has outdated + * information (e.g. after an output has been hotplugged). + * + * The client can create a new configuration with a newer serial + * and try again. + * + * Upon receiving this event, the client should destroy this + * object. + */ + void (*cancelled)(void *data, + struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1); +}; + +/** + * @ingroup iface_zwlr_output_configuration_v1 + */ +static inline int +zwlr_output_configuration_v1_add_listener(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1, + const struct zwlr_output_configuration_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwlr_output_configuration_v1, + (void (**)(void)) listener, data); +} + +#define ZWLR_OUTPUT_CONFIGURATION_V1_ENABLE_HEAD 0 +#define ZWLR_OUTPUT_CONFIGURATION_V1_DISABLE_HEAD 1 +#define ZWLR_OUTPUT_CONFIGURATION_V1_APPLY 2 +#define ZWLR_OUTPUT_CONFIGURATION_V1_TEST 3 +#define ZWLR_OUTPUT_CONFIGURATION_V1_DESTROY 4 + +/** + * @ingroup iface_zwlr_output_configuration_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_V1_SUCCEEDED_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_V1_FAILED_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_V1_CANCELLED_SINCE_VERSION 1 + +/** + * @ingroup iface_zwlr_output_configuration_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_V1_ENABLE_HEAD_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_V1_DISABLE_HEAD_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_V1_APPLY_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_V1_TEST_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_V1_DESTROY_SINCE_VERSION 1 + +/** @ingroup iface_zwlr_output_configuration_v1 */ +static inline void +zwlr_output_configuration_v1_set_user_data(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwlr_output_configuration_v1, user_data); +} + +/** @ingroup iface_zwlr_output_configuration_v1 */ +static inline void * +zwlr_output_configuration_v1_get_user_data(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_output_configuration_v1); +} + +static inline uint32_t +zwlr_output_configuration_v1_get_version(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1); +} + +/** + * @ingroup iface_zwlr_output_configuration_v1 + * + * Enable a head. This request creates a head configuration object that can + * be used to change the head's properties. + */ +static inline struct zwlr_output_configuration_head_v1 * +zwlr_output_configuration_v1_enable_head(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1, struct zwlr_output_head_v1 *head) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_v1, + ZWLR_OUTPUT_CONFIGURATION_V1_ENABLE_HEAD, &zwlr_output_configuration_head_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1), 0, NULL, head); + + return (struct zwlr_output_configuration_head_v1 *) id; +} + +/** + * @ingroup iface_zwlr_output_configuration_v1 + * + * Disable a head. + */ +static inline void +zwlr_output_configuration_v1_disable_head(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1, struct zwlr_output_head_v1 *head) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_v1, + ZWLR_OUTPUT_CONFIGURATION_V1_DISABLE_HEAD, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1), 0, head); +} + +/** + * @ingroup iface_zwlr_output_configuration_v1 + * + * Apply the new output configuration. + * + * In case the configuration is successfully applied, there is no guarantee + * that the new output state matches completely the requested + * configuration. For instance, a compositor might round the scale if it + * doesn't support fractional scaling. + * + * After this request has been sent, the compositor must respond with an + * succeeded, failed or cancelled event. Sending a request that isn't the + * destructor is a protocol error. + */ +static inline void +zwlr_output_configuration_v1_apply(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_v1, + ZWLR_OUTPUT_CONFIGURATION_V1_APPLY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1), 0); +} + +/** + * @ingroup iface_zwlr_output_configuration_v1 + * + * Test the new output configuration. The configuration won't be applied, + * but will only be validated. + * + * Even if the compositor succeeds to test a configuration, applying it may + * fail. + * + * After this request has been sent, the compositor must respond with an + * succeeded, failed or cancelled event. Sending a request that isn't the + * destructor is a protocol error. + */ +static inline void +zwlr_output_configuration_v1_test(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_v1, + ZWLR_OUTPUT_CONFIGURATION_V1_TEST, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1), 0); +} + +/** + * @ingroup iface_zwlr_output_configuration_v1 + * + * Using this request a client can tell the compositor that it is not going + * to use the configuration object anymore. Any changes to the outputs + * that have not been applied will be discarded. + * + * This request also destroys wlr_output_configuration_head objects created + * via this object. + */ +static inline void +zwlr_output_configuration_v1_destroy(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_v1, + ZWLR_OUTPUT_CONFIGURATION_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1), WL_MARSHAL_FLAG_DESTROY); +} + +#ifndef ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ENUM +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ENUM +enum zwlr_output_configuration_head_v1_error { + /** + * property has already been set + */ + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET = 1, + /** + * mode doesn't belong to head + */ + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_MODE = 2, + /** + * mode is invalid + */ + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_CUSTOM_MODE = 3, + /** + * transform value outside enum + */ + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_TRANSFORM = 4, + /** + * scale negative or zero + */ + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_SCALE = 5, + /** + * invalid enum value used in the set_adaptive_sync request + * @since 4 + */ + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_ADAPTIVE_SYNC_STATE = 6, +}; +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_ADAPTIVE_SYNC_STATE_SINCE_VERSION 4 +#endif /* ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ENUM */ + +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_MODE 0 +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_CUSTOM_MODE 1 +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_POSITION 2 +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_TRANSFORM 3 +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_SCALE 4 +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_ADAPTIVE_SYNC 5 + + +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_MODE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_CUSTOM_MODE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_POSITION_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_TRANSFORM_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_SCALE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_ADAPTIVE_SYNC_SINCE_VERSION 4 + +/** @ingroup iface_zwlr_output_configuration_head_v1 */ +static inline void +zwlr_output_configuration_head_v1_set_user_data(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwlr_output_configuration_head_v1, user_data); +} + +/** @ingroup iface_zwlr_output_configuration_head_v1 */ +static inline void * +zwlr_output_configuration_head_v1_get_user_data(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_output_configuration_head_v1); +} + +static inline uint32_t +zwlr_output_configuration_head_v1_get_version(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1); +} + +/** @ingroup iface_zwlr_output_configuration_head_v1 */ +static inline void +zwlr_output_configuration_head_v1_destroy(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1) +{ + wl_proxy_destroy((struct wl_proxy *) zwlr_output_configuration_head_v1); +} + +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + * + * This request sets the head's mode. + */ +static inline void +zwlr_output_configuration_head_v1_set_mode(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, struct zwlr_output_mode_v1 *mode) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_MODE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, mode); +} + +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + * + * This request assigns a custom mode to the head. The size is given in + * physical hardware units of the output device. If set to zero, the + * refresh rate is unspecified. + * + * It is a protocol error to set both a mode and a custom mode. + */ +static inline void +zwlr_output_configuration_head_v1_set_custom_mode(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, int32_t width, int32_t height, int32_t refresh) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_CUSTOM_MODE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, width, height, refresh); +} + +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + * + * This request sets the head's position in the global compositor space. + */ +static inline void +zwlr_output_configuration_head_v1_set_position(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, int32_t x, int32_t y) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_POSITION, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, x, y); +} + +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + * + * This request sets the head's transform. + */ +static inline void +zwlr_output_configuration_head_v1_set_transform(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, int32_t transform) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_TRANSFORM, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, transform); +} + +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + * + * This request sets the head's scale. + */ +static inline void +zwlr_output_configuration_head_v1_set_scale(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, wl_fixed_t scale) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_SCALE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, scale); +} + +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + * + * This request enables/disables adaptive sync. Adaptive sync is also + * known as Variable Refresh Rate or VRR. + */ +static inline void +zwlr_output_configuration_head_v1_set_adaptive_sync(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, uint32_t state) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_ADAPTIVE_SYNC, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, state); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-protocol.c b/src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-protocol.c new file mode 100644 index 000000000..aaa2663e6 --- /dev/null +++ b/src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-protocol.c @@ -0,0 +1,160 @@ +#ifdef FF_HAVE_WAYLAND + +/* Generated by wayland-scanner 1.22.0 */ + +/* + * Copyright © 2019 Purism SPC + * + * Permission to use, copy, modify, distribute, and sell this + * software and its documentation for any purpose is hereby granted + * without fee, provided that the above copyright notice appear in + * all copies and that both that copyright notice and this permission + * notice appear in supporting documentation, and that the name of + * the copyright holders not be used in advertising or publicity + * pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + * THIS SOFTWARE. + */ + +#include +#include +#include + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface zwlr_output_configuration_head_v1_interface; +extern const struct wl_interface zwlr_output_configuration_v1_interface; +extern const struct wl_interface zwlr_output_head_v1_interface; +extern const struct wl_interface zwlr_output_mode_v1_interface; + +static const struct wl_interface *wlr_output_management_unstable_v1_types[] = { + NULL, + NULL, + NULL, + &zwlr_output_configuration_v1_interface, + NULL, + &zwlr_output_head_v1_interface, + &zwlr_output_mode_v1_interface, + &zwlr_output_mode_v1_interface, + &zwlr_output_configuration_head_v1_interface, + &zwlr_output_head_v1_interface, + &zwlr_output_head_v1_interface, + &zwlr_output_mode_v1_interface, +}; + +static const struct wl_message zwlr_output_manager_v1_requests[] = { + { "create_configuration", "nu", wlr_output_management_unstable_v1_types + 3 }, + { "stop", "", wlr_output_management_unstable_v1_types + 0 }, +}; + +static const struct wl_message zwlr_output_manager_v1_events[] = { + { "head", "n", wlr_output_management_unstable_v1_types + 5 }, + { "done", "u", wlr_output_management_unstable_v1_types + 0 }, + { "finished", "", wlr_output_management_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwlr_output_manager_v1_interface = { + "zwlr_output_manager_v1", 4, + 2, zwlr_output_manager_v1_requests, + 3, zwlr_output_manager_v1_events, +}; + +static const struct wl_message zwlr_output_head_v1_requests[] = { + { "release", "3", wlr_output_management_unstable_v1_types + 0 }, +}; + +static const struct wl_message zwlr_output_head_v1_events[] = { + { "name", "s", wlr_output_management_unstable_v1_types + 0 }, + { "description", "s", wlr_output_management_unstable_v1_types + 0 }, + { "physical_size", "ii", wlr_output_management_unstable_v1_types + 0 }, + { "mode", "n", wlr_output_management_unstable_v1_types + 6 }, + { "enabled", "i", wlr_output_management_unstable_v1_types + 0 }, + { "current_mode", "o", wlr_output_management_unstable_v1_types + 7 }, + { "position", "ii", wlr_output_management_unstable_v1_types + 0 }, + { "transform", "i", wlr_output_management_unstable_v1_types + 0 }, + { "scale", "f", wlr_output_management_unstable_v1_types + 0 }, + { "finished", "", wlr_output_management_unstable_v1_types + 0 }, + { "make", "2s", wlr_output_management_unstable_v1_types + 0 }, + { "model", "2s", wlr_output_management_unstable_v1_types + 0 }, + { "serial_number", "2s", wlr_output_management_unstable_v1_types + 0 }, + { "adaptive_sync", "4u", wlr_output_management_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwlr_output_head_v1_interface = { + "zwlr_output_head_v1", 4, + 1, zwlr_output_head_v1_requests, + 14, zwlr_output_head_v1_events, +}; + +static const struct wl_message zwlr_output_mode_v1_requests[] = { + { "release", "3", wlr_output_management_unstable_v1_types + 0 }, +}; + +static const struct wl_message zwlr_output_mode_v1_events[] = { + { "size", "ii", wlr_output_management_unstable_v1_types + 0 }, + { "refresh", "i", wlr_output_management_unstable_v1_types + 0 }, + { "preferred", "", wlr_output_management_unstable_v1_types + 0 }, + { "finished", "", wlr_output_management_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwlr_output_mode_v1_interface = { + "zwlr_output_mode_v1", 3, + 1, zwlr_output_mode_v1_requests, + 4, zwlr_output_mode_v1_events, +}; + +static const struct wl_message zwlr_output_configuration_v1_requests[] = { + { "enable_head", "no", wlr_output_management_unstable_v1_types + 8 }, + { "disable_head", "o", wlr_output_management_unstable_v1_types + 10 }, + { "apply", "", wlr_output_management_unstable_v1_types + 0 }, + { "test", "", wlr_output_management_unstable_v1_types + 0 }, + { "destroy", "", wlr_output_management_unstable_v1_types + 0 }, +}; + +static const struct wl_message zwlr_output_configuration_v1_events[] = { + { "succeeded", "", wlr_output_management_unstable_v1_types + 0 }, + { "failed", "", wlr_output_management_unstable_v1_types + 0 }, + { "cancelled", "", wlr_output_management_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwlr_output_configuration_v1_interface = { + "zwlr_output_configuration_v1", 4, + 5, zwlr_output_configuration_v1_requests, + 3, zwlr_output_configuration_v1_events, +}; + +static const struct wl_message zwlr_output_configuration_head_v1_requests[] = { + { "set_mode", "o", wlr_output_management_unstable_v1_types + 11 }, + { "set_custom_mode", "iii", wlr_output_management_unstable_v1_types + 0 }, + { "set_position", "ii", wlr_output_management_unstable_v1_types + 0 }, + { "set_transform", "i", wlr_output_management_unstable_v1_types + 0 }, + { "set_scale", "f", wlr_output_management_unstable_v1_types + 0 }, + { "set_adaptive_sync", "4u", wlr_output_management_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwlr_output_configuration_head_v1_interface = { + "zwlr_output_configuration_head_v1", 4, + 6, zwlr_output_configuration_head_v1_requests, + 0, NULL, +}; + +#endif diff --git a/src/detection/displayserver/linux/wayland/zwlr-output.c b/src/detection/displayserver/linux/wayland/zwlr-output.c new file mode 100644 index 000000000..62ea41ed2 --- /dev/null +++ b/src/detection/displayserver/linux/wayland/zwlr-output.c @@ -0,0 +1,191 @@ +#ifdef FF_HAVE_WAYLAND + +#include "wayland.h" +#include "wlr-output-management-unstable-v1-client-protocol.h" + +static void waylandZwlrTransformListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_head_v1 *zwlr_output_head_v1, int32_t transform) +{ + WaylandDisplay* wldata = (WaylandDisplay*) data; + wldata->transform = transform; +} + +static void waylandZwlrScaleListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_head_v1 *zwlr_output_head_v1, wl_fixed_t scale) +{ + WaylandDisplay* wldata = (WaylandDisplay*) data; + wldata->scale = wl_fixed_to_double(scale); +} + +typedef struct WaylandZwlrMode +{ + int32_t width; + int32_t height; + int32_t refreshRate; + struct zwlr_output_mode_v1* pMode; +} WaylandZwlrMode; + +static void waylandZwlrSizeListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_mode_v1 *zwlr_output_mode_v1, int32_t width, int32_t height) +{ + WaylandZwlrMode* mode = (WaylandZwlrMode*) data; + mode->width = width; + mode->height = height; +} + +static void waylandZwlrRefreshListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_mode_v1 *zwlr_output_mode_v1, int32_t rate) +{ + WaylandZwlrMode* mode = (WaylandZwlrMode*) data; + mode->refreshRate = rate; +} + +static void waylandZwlrModeListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_head_v1 *zwlr_output_head_v1, struct zwlr_output_mode_v1 *mode) +{ + WaylandDisplay* wldata = (WaylandDisplay*) data; + WaylandZwlrMode* newMode = ffListAdd((FFlist*) wldata->internal); + newMode->pMode = mode; + const struct zwlr_output_mode_v1_listener listener = { + .size = waylandZwlrSizeListener, + .refresh = waylandZwlrRefreshListener, + .preferred = (void*) stubListener, + .finished = (void*) stubListener, + }; + + // Strangely, the listener is called only in this function, but not in `waylandZwlrCurrentModeListener` + wldata->parent->ffwl_proxy_add_listener((struct wl_proxy *) mode, (void (**)(void)) &listener, newMode); + wldata->parent->ffwl_display_roundtrip(wldata->parent->display); +} + +static void waylandZwlrCurrentModeListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_head_v1 *zwlr_output_head_v1, struct zwlr_output_mode_v1 *mode) +{ + // waylandZwlrModeListener is always run before this + WaylandDisplay* wldata = (WaylandDisplay*) data; + WaylandZwlrMode* current = NULL; + FF_LIST_FOR_EACH(WaylandZwlrMode, m, *(FFlist*) wldata->internal) + { + if (m->pMode == mode) + { + current = m; + break; + } + } + wldata->width = current->width; + wldata->height = current->height; + wldata->refreshRate = current->refreshRate; +} + +static void waylandHandleZwlrHead(void *data, FF_MAYBE_UNUSED struct zwlr_output_manager_v1 *zwlr_output_manager_v1, struct zwlr_output_head_v1 *head) +{ + WaylandData* wldata = data; + + const struct zwlr_output_head_v1_listener headListener = { + .name = (void*) ffWaylandOutputNameListener, + .description = (void*) ffWaylandOutputDescriptionListener, + .physical_size = (void*) stubListener, + .mode = waylandZwlrModeListener, + .enabled = (void*) stubListener, + .current_mode = waylandZwlrCurrentModeListener, + .position = (void*) stubListener, + .transform = waylandZwlrTransformListener, + .scale = waylandZwlrScaleListener, + .finished = (void*) stubListener, + .serial_number = (void*) stubListener, + .adaptive_sync = (void*) stubListener, + }; + + FF_LIST_AUTO_DESTROY modes = ffListCreate(sizeof(WaylandZwlrMode)); + WaylandDisplay display = { + .parent = wldata, + .width = 0, + .height = 0, + .refreshRate = 0, + .scale = 1, + .transform = WL_OUTPUT_TRANSFORM_NORMAL, + .type = FF_DISPLAY_TYPE_UNKNOWN, + .name = ffStrbufCreate(), + .description = ffStrbufCreate(), + .edidName = ffStrbufCreate(), + .internal = &modes, + }; + + wldata->ffwl_proxy_add_listener((struct wl_proxy*) head, (void(**)(void)) &headListener, &display); + wldata->ffwl_display_roundtrip(wldata->display); + + if(display.width <= 0 || display.height <= 0) + return; + + uint32_t rotation; + switch(display.transform) + { + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_90: + rotation = 90; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + case WL_OUTPUT_TRANSFORM_180: + rotation = 180; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + case WL_OUTPUT_TRANSFORM_270: + rotation = 270; + break; + default: + rotation = 0; + break; + } + + switch(rotation) + { + case 90: + case 270: { + int32_t temp = display.width; + display.width = display.height; + display.height = temp; + break; + } + default: + break; + } + + ffdsAppendDisplay(wldata->result, + (uint32_t) display.width, + (uint32_t) display.height, + display.refreshRate / 1000.0, + (uint32_t) (display.width / display.scale), + (uint32_t) (display.height / display.scale), + rotation, + display.edidName.length + ? &display.edidName + : display.description.length + ? &display.description + : &display.name, + display.type, + false, + 0 + ); + + ffStrbufDestroy(&display.description); + ffStrbufDestroy(&display.name); + ffStrbufDestroy(&display.edidName); + + // These must be released manually + FF_LIST_FOR_EACH(WaylandZwlrMode, m, modes) + wldata->ffwl_proxy_destroy((void*) m->pMode); + wldata->ffwl_proxy_destroy((void*) head); +} + +void ffWaylandHandleZwlrOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version) +{ + struct wl_proxy* output = wldata->ffwl_proxy_marshal_constructor_versioned((struct wl_proxy*) registry, WL_REGISTRY_BIND, &zwlr_output_manager_v1_interface, version, name, zwlr_output_manager_v1_interface.name, version, NULL); + if(output == NULL) + return; + + const struct zwlr_output_manager_v1_listener outputListener = { + .head = waylandHandleZwlrHead, + .done = (void*) stubListener, + .finished = (void*) stubListener, + }; + + wldata->ffwl_proxy_add_listener(output, (void(**)(void)) &outputListener, wldata); + wldata->ffwl_display_roundtrip(wldata->display); + wldata->ffwl_proxy_destroy(output); +} + +#endif diff --git a/src/detection/displayserver/linux/wlroots.c b/src/detection/displayserver/linux/wlroots.c deleted file mode 100644 index dadb9c4c3..000000000 --- a/src/detection/displayserver/linux/wlroots.c +++ /dev/null @@ -1,111 +0,0 @@ -#include "displayserver_linux.h" -#include "util/stringUtils.h" -#include "common/processing.h" - -static inline void wrapYyjsonFree(yyjson_doc** doc) -{ - assert(doc); - if (*doc) - yyjson_doc_free(*doc); -} - -const char* ffdsConnectWlroots(FFDisplayServerResult* result) -{ - FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); - if (ffProcessAppendStdOut(&buffer, (char* const[]){ - "wlr-randr", - "--json", - NULL - }) != NULL || buffer.length == 0) - return "Running wlr-randr failed"; - - yyjson_doc* __attribute__((__cleanup__(wrapYyjsonFree))) doc = yyjson_read_opts(buffer.chars, buffer.length, 0, NULL, NULL); - if (!doc) - return "Failed to parse wlr-randr info"; - - yyjson_val* root = yyjson_doc_get_root(doc); - if (!yyjson_is_arr(root)) - return "Battery info result is not a JSON array"; - - yyjson_val* device; - size_t idev, mdev; - yyjson_arr_foreach(root, idev, mdev, device) - { - yyjson_val* modes = yyjson_obj_get(device, "modes"); - if (!yyjson_is_arr(modes)) - continue; - - if (!yyjson_get_bool(yyjson_obj_get(device, "enabled"))) - continue; - - yyjson_val* mode; - size_t imode, mmode; - yyjson_arr_foreach(modes, imode, mmode, mode) - { - if (!yyjson_is_obj(mode)) - continue; - - if (!yyjson_get_bool(yyjson_obj_get(mode, "current"))) - continue; - - uint32_t width = (uint32_t) yyjson_get_uint(yyjson_obj_get(mode, "width")); - uint32_t height = (uint32_t) yyjson_get_uint(yyjson_obj_get(mode, "height")); - - if (width == 0 || height == 0) - continue; - - double refreshRate = yyjson_get_real(yyjson_obj_get(mode, "refresh")); - - double scale = (double) yyjson_get_real(yyjson_obj_get(device, "scale")); - const char* connName = yyjson_get_str(yyjson_obj_get(device, "name")); - FFDisplayType type = FF_DISPLAY_TYPE_UNKNOWN; - if(ffStrStartsWith(connName, "eDP-")) - type = FF_DISPLAY_TYPE_BUILTIN; - else if(ffStrStartsWith(connName, "HDMI-")) - type = FF_DISPLAY_TYPE_EXTERNAL; - - FF_STRBUF_AUTO_DESTROY displayName = ffStrbufCreate(); - if (!ffdsMatchDrmConnector(connName, &displayName)) - { - const char* desc = yyjson_get_str(yyjson_obj_get(device, "description")); - if (ffStrContains(desc, "(null)")) - ffStrbufSetS(&displayName, connName); - else - ffStrbufSetS(&displayName, desc); - } - uint32_t rotation = 0; - const char* transform = yyjson_get_str(yyjson_obj_get(device, "transform")); - if (!ffStrEquals(transform, "normal")) - { - // 90 | flipped-90 - if (ffStrEndsWith(transform, "90")) - rotation = 90; - else if (ffStrEndsWith(transform, "180")) - rotation = 180; - else if (ffStrEndsWith(transform, "270")) - rotation = 270; - } - if (rotation % 180 != 0) - { - uint32_t temp = width; - width = height; - height = temp; - } - ffdsAppendDisplay(result, - width, - height, - refreshRate, - (uint32_t) (width / scale), - (uint32_t) (height / scale), - rotation, - &displayName, - type, - false, - 0 - ); - break; - } - } - - return NULL; -}