summaryrefslogtreecommitdiffstats
path: root/src/detection/displayserver/linux/wayland/zwlr-output.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/detection/displayserver/linux/wayland/zwlr-output.c')
-rw-r--r--src/detection/displayserver/linux/wayland/zwlr-output.c212
1 files changed, 212 insertions, 0 deletions
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 0000000..632bd19
--- /dev/null
+++ b/src/detection/displayserver/linux/wayland/zwlr-output.c
@@ -0,0 +1,212 @@
+#ifdef FF_HAVE_WAYLAND
+
+ #include "wayland.h"
+ #include "wlr-output-management-unstable-v1-client-protocol.h"
+
+static void waylandZwlrTransformListener(void* data, FF_A_UNUSED struct zwlr_output_head_v1* zwlr_output_head_v1, int32_t transform) {
+ WaylandDisplay* wldata = (WaylandDisplay*) data;
+ wldata->transform = (enum wl_output_transform) transform;
+}
+
+static void waylandZwlrScaleListener(void* data, FF_A_UNUSED struct zwlr_output_head_v1* zwlr_output_head_v1, wl_fixed_t scale) {
+ WaylandDisplay* wldata = (WaylandDisplay*) data;
+ wldata->dpi = (uint32_t) scale * 3 / 8; // wl_fixed_to_double(scale) * 96;
+}
+
+typedef struct WaylandZwlrMode {
+ int32_t width;
+ int32_t height;
+ int32_t refreshRate;
+ bool preferred;
+ struct zwlr_output_mode_v1* pMode;
+} WaylandZwlrMode;
+
+static void waylandZwlrModeSizeListener(void* data, FF_A_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 waylandZwlrModeRefreshListener(void* data, FF_A_UNUSED struct zwlr_output_mode_v1* zwlr_output_mode_v1, int32_t rate) {
+ WaylandZwlrMode* mode = (WaylandZwlrMode*) data;
+ mode->refreshRate = rate;
+}
+
+static void waylandZwlrModePreferredListener(void* data, FF_A_UNUSED struct zwlr_output_mode_v1* zwlr_output_mode_v1) {
+ WaylandZwlrMode* mode = (WaylandZwlrMode*) data;
+ mode->preferred = true;
+}
+
+static const struct zwlr_output_mode_v1_listener modeListener = {
+ .size = waylandZwlrModeSizeListener,
+ .refresh = waylandZwlrModeRefreshListener,
+ .preferred = waylandZwlrModePreferredListener,
+ .finished = (void*) stubListener,
+};
+
+static void waylandZwlrModeListener(void* data, FF_A_UNUSED struct zwlr_output_head_v1* zwlr_output_head_v1, struct zwlr_output_mode_v1* mode) {
+ WaylandDisplay* wldata = (WaylandDisplay*) data;
+ if (!wldata->internal) {
+ return;
+ }
+
+ WaylandZwlrMode* newMode = FF_LIST_ADD(WaylandZwlrMode, *(FFlist*) wldata->internal);
+ *newMode = (WaylandZwlrMode) { .pMode = mode };
+
+ // 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)) &modeListener, newMode);
+}
+
+static void waylandZwlrCurrentModeListener(void* data, FF_A_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;
+ if (!wldata->internal) {
+ return;
+ }
+
+ int set = 0;
+ FF_LIST_FOR_EACH (WaylandZwlrMode, m, *(FFlist*) wldata->internal) {
+ if (m->pMode == mode) {
+ wldata->width = m->width;
+ wldata->height = m->height;
+ wldata->refreshRate = m->refreshRate;
+ if (++set == 2) {
+ break;
+ }
+ }
+ if (m->preferred) {
+ wldata->preferredWidth = m->width;
+ wldata->preferredHeight = m->height;
+ wldata->preferredRefreshRate = m->refreshRate;
+ if (++set == 2) {
+ break;
+ }
+ }
+ }
+}
+
+static void waylandZwlrPhysicalSizeListener(void* data, FF_A_UNUSED struct zwlr_output_head_v1* zwlr_output_head_v1, int32_t width, int32_t height) {
+ WaylandDisplay* wldata = (WaylandDisplay*) data;
+ wldata->physicalWidth = width;
+ wldata->physicalHeight = height;
+}
+
+static void waylandZwlrEnabledListener(void* data, FF_A_UNUSED struct zwlr_output_head_v1* zwlr_output_head_v1, bool enabled) {
+ WaylandDisplay* wldata = (WaylandDisplay*) data;
+ if (!enabled) {
+ wldata->internal = NULL;
+ }
+}
+
+static const struct zwlr_output_head_v1_listener headListener = {
+ .name = (void*) ffWaylandOutputNameListener,
+ .description = (void*) ffWaylandOutputDescriptionListener,
+ .physical_size = waylandZwlrPhysicalSizeListener,
+ .mode = waylandZwlrModeListener,
+ .enabled = (void*) waylandZwlrEnabledListener,
+ .current_mode = waylandZwlrCurrentModeListener,
+ .position = (void*) stubListener,
+ .transform = waylandZwlrTransformListener,
+ .scale = waylandZwlrScaleListener,
+ .finished = (void*) stubListener,
+ .make = (void*) stubListener,
+ .model = (void*) stubListener,
+ .serial_number = (void*) stubListener,
+ .adaptive_sync = (void*) stubListener,
+};
+
+static void waylandHandleZwlrHead(void* data, FF_A_UNUSED struct zwlr_output_manager_v1* zwlr_output_manager_v1, struct zwlr_output_head_v1* head) {
+ WaylandData* wldata = data;
+
+ FF_LIST_AUTO_DESTROY modes = ffListCreate();
+ WaylandDisplay display = {
+ .parent = wldata,
+ .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 || !display.internal) {
+ return;
+ }
+
+ uint32_t rotation = ffWaylandHandleRotation(&display);
+
+ FFDisplayResult* item = ffdsAppendDisplay(wldata->result,
+ (uint32_t) display.width,
+ (uint32_t) display.height,
+ display.refreshRate / 1000.0,
+ (uint32_t) display.dpi,
+ (uint32_t) display.preferredWidth,
+ (uint32_t) display.preferredHeight,
+ display.preferredRefreshRate / 1000.0,
+ rotation,
+ display.edidName.length
+ ? &display.edidName
+ : 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,
+ "wayland-zwlr");
+ 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);
+
+ // 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);
+}
+
+static const struct zwlr_output_manager_v1_listener outputListener = {
+ .head = waylandHandleZwlrHead,
+ .done = (void*) stubListener,
+ .finished = (void*) stubListener,
+};
+
+const char* ffWaylandHandleZwlrOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version) {
+ uint32_t bindVersion = min(version, ZWLR_OUTPUT_MANAGER_V1_HEAD_SINCE_VERSION);
+ struct wl_proxy* output = wldata->ffwl_proxy_marshal_constructor_versioned((struct wl_proxy*) registry, WL_REGISTRY_BIND, &zwlr_output_manager_v1_interface, bindVersion, name, zwlr_output_manager_v1_interface.name, bindVersion, NULL);
+ if (output == NULL) {
+ return "Failed to bind zwlr_output_manager_v1";
+ }
+
+ if (wldata->ffwl_proxy_add_listener(output, (void (**)(void)) &outputListener, wldata) < 0) {
+ wldata->ffwl_proxy_destroy(output);
+ return "Failed to add listener to zwlr_output_manager_v1";
+ }
+ if (wldata->ffwl_display_roundtrip(wldata->display) < 0) {
+ wldata->ffwl_proxy_destroy(output);
+ return "Failed to roundtrip display";
+ }
+ wldata->ffwl_proxy_destroy(output);
+
+ return NULL;
+}
+
+#endif