From e75eb375decfc84ca3e3fffc1b5e04bdd64439e9 Mon Sep 17 00:00:00 2001 From: xad6 Date: Mon, 15 Aug 2022 01:29:35 +0000 Subject: [PATCH] detect rotation directly via X or Wayland Fixes #44 This commit changes the way that screen rotation is detected. Instead of using Mutter's DBus interface, we now listen directly for X and Wayland events indicating the screen has been rotated (RRScreenChangeNotify and wl_output::geometry events). This has the advantage of working outside of Phosh. --- .gitlab-ci.yml | 4 +- meson.build | 14 ++- src/main.c | 236 +++++++++++++++++++++++++++++++++---------------- 3 files changed, 177 insertions(+), 77 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 98acabd..a738b25 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,7 @@ build:debian: image: debian:bookworm-slim before_script: - - apt-get update && apt-get -y install gcc meson ninja-build git clang-format-14 libgtk-4-dev libtiff-dev libzbar-dev libfeedback-dev + - apt-get update && apt-get -y install gcc meson ninja-build git clang-format-14 libgtk-4-dev libtiff-dev libzbar-dev libfeedback-dev libwayland-dev libx11-dev libxrandr-dev script: - meson build - ninja -C build @@ -11,7 +11,7 @@ build:debian: build:alpine: image: alpine:edge before_script: - - apk add --no-cache build-base meson samurai gtk4.0-dev tiff-dev zbar-dev feedbackd-dev + - apk add --no-cache build-base meson samurai gtk4.0-dev tiff-dev zbar-dev feedbackd-dev wayland-dev libx11-dev libxrandr-dev script: - meson build - ninja -C build diff --git a/meson.build b/meson.build index 5d17d3a..3b98f19 100644 --- a/meson.build +++ b/meson.build @@ -9,6 +9,18 @@ threads = dependency('threads') # gl = dependency('gl') epoxy = dependency('epoxy') +# We only build in support for Wayland/X11 if GTK did so +optdeps = [] +if gtkdep.get_variable('targets').contains('wayland') + optdeps += dependency('gtk4-wayland') + optdeps += dependency('wayland-client') +endif +if gtkdep.get_variable('targets').contains('x11') + optdeps += dependency('gtk4-x11') + optdeps += dependency('x11') + optdeps += dependency('xrandr') +endif + cc = meson.get_compiler('c') libm = cc.find_library('m', required: false) @@ -51,7 +63,7 @@ executable('megapixels', 'src/zbar_pipeline.c', resources, include_directories: 'src/', - dependencies: [gtkdep, libfeedback, libm, tiff, zbar, threads, epoxy], + dependencies: [gtkdep, libfeedback, libm, tiff, zbar, threads, epoxy] + optdeps, install: true, link_args: '-Wl,-ldl') diff --git a/src/main.c b/src/main.c index 092b24c..b2f983b 100644 --- a/src/main.c +++ b/src/main.c @@ -12,6 +12,15 @@ #include #define LIBFEEDBACK_USE_UNSTABLE_API #include +#ifdef GDK_WINDOWING_WAYLAND +#include +#include +#endif +#ifdef GDK_WINDOWING_X11 +#include +#include +#include +#endif #include #include #include @@ -918,66 +927,6 @@ update_ui_rotation() } } -static void -display_config_received(GDBusConnection *conn, GAsyncResult *res, gpointer user_data) -{ - g_autoptr(GError) error = NULL; - g_autoptr(GVariant) result = - g_dbus_connection_call_finish(conn, res, &error); - - if (!result) { - printf("Failed to get display configuration: %s\n", error->message); - return; - } - - g_autoptr(GVariant) configs = g_variant_get_child_value(result, 1); - if (g_variant_n_children(configs) == 0) { - return; - } - - g_autoptr(GVariant) config = g_variant_get_child_value(configs, 0); - g_autoptr(GVariant) rot_config = g_variant_get_child_value(config, 7); - uint32_t rotation_index = g_variant_get_uint32(rot_config); - - assert(rotation_index < 4); - int new_rotation = rotation_index * 90; - - if (new_rotation != device_rotation) { - device_rotation = new_rotation; - update_io_pipeline(); - update_ui_rotation(); - } -} - -static void -update_screen_rotation(GDBusConnection *conn) -{ - g_dbus_connection_call(conn, - "org.gnome.Mutter.DisplayConfig", - "/org/gnome/Mutter/DisplayConfig", - "org.gnome.Mutter.DisplayConfig", - "GetResources", - NULL, - NULL, - G_DBUS_CALL_FLAGS_NO_AUTO_START, - -1, - NULL, - (GAsyncReadyCallback)display_config_received, - NULL); -} - -static void -on_screen_rotate(GDBusConnection *conn, - const gchar *sender_name, - const gchar *object_path, - const gchar *interface_name, - const gchar *signal_name, - GVariant *parameters, - gpointer user_data) -{ - update_screen_rotation(conn); -} - char * munge_app_id(const char *app_id) { @@ -1068,6 +1017,109 @@ setup_fb_switch(GtkBuilder *builder) NULL); } +#ifdef GDK_WINDOWING_WAYLAND +static void +wl_handle_geometry(void *data, + struct wl_output *wl_output, + int32_t x, + int32_t y, + int32_t physical_width, + int32_t physical_height, + int32_t subpixel, + const char *make, + const char *model, + int32_t transform) +{ + assert(transform < 4); + int new_rotation = transform * 90; + + if (new_rotation != device_rotation) { + device_rotation = new_rotation; + update_io_pipeline(); + update_ui_rotation(); + } +} + +static void +wl_handle_mode(void *data, + struct wl_output *wl_output, + uint32_t flags, + int32_t width, + int32_t height, + int32_t refresh) +{ + // Do nothing +} + +static const struct wl_output_listener output_listener = { + .geometry = wl_handle_geometry, + .mode = wl_handle_mode +}; + +static void +wl_handle_global(void *data, + struct wl_registry *wl_registry, + uint32_t name, + const char *interface, + uint32_t version) +{ + if (strcmp(interface, wl_output_interface.name) == 0) { + struct wl_output *output = + wl_registry_bind(wl_registry, name, &wl_output_interface, 1); + wl_output_add_listener(output, &output_listener, NULL); + } +} + +static void +wl_handle_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name) +{ + // Do nothing +} + +static const struct wl_registry_listener registry_listener = { + .global = wl_handle_global, + .global_remove = wl_handle_global_remove +}; +#endif // GDK_WINDOWING_WAYLAND + +#ifdef GDK_WINDOWING_X11 +static gboolean +xevent_handler(GdkDisplay *display, XEvent *xevent, gpointer data) +{ + Display *xdisplay = gdk_x11_display_get_xdisplay(display); + int event_base, error_base; + XRRQueryExtension(xdisplay, &event_base, &error_base); + if (xevent->type - event_base == RRScreenChangeNotify) { + Rotation xrotation = + ((XRRScreenChangeNotifyEvent *)xevent)->rotation; + int new_rotation = 0; + switch (xrotation) { + case RR_Rotate_0: + new_rotation = 0; + break; + case RR_Rotate_90: + new_rotation = 90; + break; + case RR_Rotate_180: + new_rotation = 180; + break; + case RR_Rotate_270: + new_rotation = 270; + break; + } + if (new_rotation != device_rotation) { + device_rotation = new_rotation; + update_io_pipeline(); + update_ui_rotation(); + } + } + + // The return value of this function should always be FALSE; if it's + // TRUE, we prevent GTK/GDK from handling the event. + return FALSE; +} +#endif // GDK_WINDOWING_X11 + static void activate(GtkApplication *app, gpointer data) { @@ -1184,22 +1236,58 @@ activate(GtkApplication *app, gpointer data) "active-id", G_SETTINGS_BIND_DEFAULT); - // Listen for phosh rotation - GDBusConnection *conn = - g_application_get_dbus_connection(G_APPLICATION(app)); - g_dbus_connection_signal_subscribe(conn, - NULL, - "org.gnome.Mutter.DisplayConfig", - "MonitorsChanged", - "/org/gnome/Mutter/DisplayConfig", - NULL, - G_DBUS_SIGNAL_FLAGS_NONE, - &on_screen_rotate, - NULL, - NULL); - update_screen_rotation(conn); +#ifdef GDK_WINDOWING_WAYLAND + // Listen for Wayland rotation + if (GDK_IS_WAYLAND_DISPLAY(display)) { + struct wl_display *wl_display = + gdk_wayland_display_get_wl_display(display); + struct wl_registry *wl_registry = + wl_display_get_registry(wl_display); + // The registry listener will bind to our wl_output and add our + // listeners + wl_registry_add_listener(wl_registry, ®istry_listener, NULL); + // GTK will take care of dispatching wayland events for us. + // Wayland sends us a geometry event as soon as we bind to the + // wl_output, so we don't need to manually check the initial + // rotation here. + } +#endif +#ifdef GDK_WINDOWING_X11 + // Listen for X rotation + if (GDK_IS_X11_DISPLAY(display)) { + g_signal_connect( + display, "xevent", G_CALLBACK(xevent_handler), NULL); + // Set initial rotation + Display *xdisplay = gdk_x11_display_get_xdisplay(display); + int screen = + XScreenNumberOfScreen(gdk_x11_display_get_xscreen(display)); + Rotation xrotation; + XRRRotations(xdisplay, screen, &xrotation); + int new_rotation = 0; + switch (xrotation) { + case RR_Rotate_0: + new_rotation = 0; + break; + case RR_Rotate_90: + new_rotation = 90; + break; + case RR_Rotate_180: + new_rotation = 180; + break; + case RR_Rotate_270: + new_rotation = 270; + break; + } + if (new_rotation != device_rotation) { + device_rotation = new_rotation; + update_ui_rotation(); + } + } +#endif // Initialize display flash + GDBusConnection *conn = + g_application_get_dbus_connection(G_APPLICATION(app)); mp_flash_gtk_init(conn); mp_io_pipeline_start();