diff options
Diffstat (limited to 'src/detection/displayserver/linux/wayland/global-output.c')
| -rw-r--r-- | src/detection/displayserver/linux/wayland/global-output.c | 173 |
1 files changed, 173 insertions, 0 deletions
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 0000000..51fb425 --- /dev/null +++ b/src/detection/displayserver/linux/wayland/global-output.c @@ -0,0 +1,173 @@ +#ifdef FF_HAVE_WAYLAND + + #include "wayland.h" + #include "common/strutil.h" + #include "xdg-output-unstable-v1-client-protocol.h" + +static void waylandOutputModeListener(void* data, FF_A_UNUSED struct wl_output* output, uint32_t flags, int32_t width, int32_t height, int32_t refreshRate) { + WaylandDisplay* display = data; + + if (flags & WL_OUTPUT_MODE_CURRENT) { + display->width = width; + display->height = height; + display->refreshRate = refreshRate; + } + if (flags & WL_OUTPUT_MODE_PREFERRED) { + display->preferredWidth = width; + display->preferredHeight = height; + display->preferredRefreshRate = refreshRate; + } +} + +static void waylandOutputScaleListener(void* data, FF_A_UNUSED struct wl_output* output, int32_t scale) { + WaylandDisplay* display = data; + display->dpi = 96 * (uint32_t) scale; +} + +static void waylandOutputGeometryListener(void* data, + FF_A_UNUSED struct wl_output* output, + FF_A_UNUSED int32_t x, + FF_A_UNUSED int32_t y, + int32_t physical_width, + int32_t physical_height, + FF_A_UNUSED int32_t subpixel, + FF_A_UNUSED const char* make, + FF_A_UNUSED const char* model, + int32_t transform) { + WaylandDisplay* display = data; + display->physicalWidth = physical_width; + display->physicalHeight = physical_height; + display->transform = (enum wl_output_transform) transform; +} + +static void handleXdgLogicalSize(void* data, FF_A_UNUSED struct zxdg_output_v1* _, int32_t width, FF_A_UNUSED int32_t height) { + WaylandDisplay* display = data; + // Seems the values are only useful when ractional scale is enabled + if (width < display->width) { + display->dpi = (uint32_t) (display->width * 96 / width); + } +} + +// Dirty hack for #477 +// The order of these callbacks MUST follow `struct wl_output_listener` +static 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"); + +static struct zxdg_output_v1_listener zxdgOutputListener = { + .logical_position = (void*) stubListener, + .logical_size = handleXdgLogicalSize, + .done = (void*) stubListener, + .name = (void*) ffWaylandOutputNameListener, + .description = (void*) ffWaylandOutputDescriptionListener, +}; + +const char* ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version) { + const char* api = "wayland-global"; + uint32_t bindVersion = min(version, WL_OUTPUT_DESCRIPTION_SINCE_VERSION); + struct wl_proxy* output = wldata->ffwl_proxy_marshal_constructor_versioned((struct wl_proxy*) registry, WL_REGISTRY_BIND, wldata->ffwl_output_interface, bindVersion, name, wldata->ffwl_output_interface->name, bindVersion, NULL); + if (output == NULL) { + return "Failed to create wl_output"; + } + + WaylandDisplay display = { + .parent = wldata, + .transform = WL_OUTPUT_TRANSFORM_NORMAL, + .type = FF_DISPLAY_TYPE_UNKNOWN, + .name = ffStrbufCreate(), + .description = ffStrbufCreate(), + .edidName = ffStrbufCreate(), + }; + + if (wldata->ffwl_proxy_add_listener(output, (void (**)(void)) &outputListener, &display) < 0) { + wldata->ffwl_proxy_destroy(output); + return "Failed to add listener to wl_output"; + } + if (wldata->ffwl_display_roundtrip(wldata->display) < 0) { + wldata->ffwl_proxy_destroy(output); + return "Failed to roundtrip wl_output"; + } + + if (wldata->zxdgOutputManager) { + uint32_t bindVersion = min(version, ZXDG_OUTPUT_V1_DESCRIPTION_SINCE_VERSION); + struct wl_proxy* zxdgOutput = wldata->ffwl_proxy_marshal_constructor_versioned(wldata->zxdgOutputManager, ZXDG_OUTPUT_MANAGER_V1_GET_XDG_OUTPUT, &zxdg_output_v1_interface, bindVersion, NULL, output); + + if (zxdgOutput) { + wldata->ffwl_proxy_add_listener(zxdgOutput, (void (**)(void)) &zxdgOutputListener, &display); + wldata->ffwl_display_roundtrip(wldata->display); + wldata->ffwl_proxy_destroy(zxdgOutput); + api = "wayland-global-zxdg"; + } + } + + wldata->ffwl_proxy_destroy(output); + + if (display.width <= 0 || display.height <= 0) { + return "Failed to get display information from wl_output"; + } + + uint32_t rotation = ffWaylandHandleRotation(&display); + + FFDisplayResult* item = ffdsAppendDisplay(wldata->result, + (uint32_t) display.width, + (uint32_t) display.height, + display.refreshRate / 1000.0, + display.dpi, + (uint32_t) display.preferredWidth, + (uint32_t) display.preferredHeight, + display.preferredRefreshRate / 1000.0, + rotation, + display.edidName.length + ? &display.edidName + // Try ignoring `eDP-1-unknown`, where `unknown` is localized + : display.description.length && !ffStrbufContain(&display.description, &display.name) + ? &display.description + : &display.name, + display.type, + false, + display.id, + (uint32_t) display.physicalWidth, + (uint32_t) display.physicalHeight, + api); + if (item) { + if (display.hdrSupported) { + item->hdrStatus = FF_DISPLAY_HDR_STATUS_SUPPORTED; + } else if (display.hdrInfoAvailable) { + item->hdrStatus = FF_DISPLAY_HDR_STATUS_UNSUPPORTED; + } else { + item->hdrStatus = FF_DISPLAY_HDR_STATUS_UNKNOWN; + } + + item->manufactureYear = display.myear; + item->manufactureWeek = display.mweek; + item->serial = display.serial; + } + + ffStrbufDestroy(&display.description); + ffStrbufDestroy(&display.name); + ffStrbufDestroy(&display.edidName); + + return NULL; +} + +const char* ffWaylandHandleZxdgOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version) { + uint32_t bindVersion = min(version, ZXDG_OUTPUT_MANAGER_V1_GET_XDG_OUTPUT_SINCE_VERSION); + struct wl_proxy* manager = wldata->ffwl_proxy_marshal_constructor_versioned((struct wl_proxy*) registry, WL_REGISTRY_BIND, &zxdg_output_manager_v1_interface, bindVersion, name, zxdg_output_manager_v1_interface.name, bindVersion, NULL); + if (manager == NULL) { + return "Failed to create zxdg_output_manager_v1"; + } + + wldata->zxdgOutputManager = manager; + + return NULL; +} + +#endif |