summaryrefslogtreecommitdiffstats
path: root/src/detection/displayserver/linux/wayland/global-output.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/detection/displayserver/linux/wayland/global-output.c')
-rw-r--r--src/detection/displayserver/linux/wayland/global-output.c173
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