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.
This commit is contained in:
xad6
2022-08-15 01:29:35 +00:00
committed by Martijn Braam
parent 9cb23ee076
commit e75eb375de
3 changed files with 177 additions and 77 deletions

View File

@@ -1,7 +1,7 @@
build:debian: build:debian:
image: debian:bookworm-slim image: debian:bookworm-slim
before_script: 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: script:
- meson build - meson build
- ninja -C build - ninja -C build
@@ -11,7 +11,7 @@ build:debian:
build:alpine: build:alpine:
image: alpine:edge image: alpine:edge
before_script: 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: script:
- meson build - meson build
- ninja -C build - ninja -C build

View File

@@ -9,6 +9,18 @@ threads = dependency('threads')
# gl = dependency('gl') # gl = dependency('gl')
epoxy = dependency('epoxy') 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') cc = meson.get_compiler('c')
libm = cc.find_library('m', required: false) libm = cc.find_library('m', required: false)
@@ -51,7 +63,7 @@ executable('megapixels',
'src/zbar_pipeline.c', 'src/zbar_pipeline.c',
resources, resources,
include_directories: 'src/', include_directories: 'src/',
dependencies: [gtkdep, libfeedback, libm, tiff, zbar, threads, epoxy], dependencies: [gtkdep, libfeedback, libm, tiff, zbar, threads, epoxy] + optdeps,
install: true, install: true,
link_args: '-Wl,-ldl') link_args: '-Wl,-ldl')

View File

@@ -12,6 +12,15 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
#define LIBFEEDBACK_USE_UNSTABLE_API #define LIBFEEDBACK_USE_UNSTABLE_API
#include <libfeedback.h> #include <libfeedback.h>
#ifdef GDK_WINDOWING_WAYLAND
#include <gdk/wayland/gdkwayland.h>
#include <wayland-client.h>
#endif
#ifdef GDK_WINDOWING_X11
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>
#include <gdk/x11/gdkx.h>
#endif
#include <limits.h> #include <limits.h>
#include <linux/kdev_t.h> #include <linux/kdev_t.h>
#include <linux/media.h> #include <linux/media.h>
@@ -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 * char *
munge_app_id(const char *app_id) munge_app_id(const char *app_id)
{ {
@@ -1068,6 +1017,109 @@ setup_fb_switch(GtkBuilder *builder)
NULL); 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 static void
activate(GtkApplication *app, gpointer data) activate(GtkApplication *app, gpointer data)
{ {
@@ -1184,22 +1236,58 @@ activate(GtkApplication *app, gpointer data)
"active-id", "active-id",
G_SETTINGS_BIND_DEFAULT); G_SETTINGS_BIND_DEFAULT);
// Listen for phosh rotation #ifdef GDK_WINDOWING_WAYLAND
GDBusConnection *conn = // Listen for Wayland rotation
g_application_get_dbus_connection(G_APPLICATION(app)); if (GDK_IS_WAYLAND_DISPLAY(display)) {
g_dbus_connection_signal_subscribe(conn, struct wl_display *wl_display =
NULL, gdk_wayland_display_get_wl_display(display);
"org.gnome.Mutter.DisplayConfig", struct wl_registry *wl_registry =
"MonitorsChanged", wl_display_get_registry(wl_display);
"/org/gnome/Mutter/DisplayConfig", // The registry listener will bind to our wl_output and add our
NULL, // listeners
G_DBUS_SIGNAL_FLAGS_NONE, wl_registry_add_listener(wl_registry, &registry_listener, NULL);
&on_screen_rotate, // GTK will take care of dispatching wayland events for us.
NULL, // Wayland sends us a geometry event as soon as we bind to the
NULL); // wl_output, so we don't need to manually check the initial
update_screen_rotation(conn); // 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 // Initialize display flash
GDBusConnection *conn =
g_application_get_dbus_connection(G_APPLICATION(app));
mp_flash_gtk_init(conn); mp_flash_gtk_init(conn);
mp_io_pipeline_start(); mp_io_pipeline_start();