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:
@@ -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
|
||||
|
14
meson.build
14
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')
|
||||
|
||||
|
236
src/main.c
236
src/main.c
@@ -12,6 +12,15 @@
|
||||
#include <gtk/gtk.h>
|
||||
#define LIBFEEDBACK_USE_UNSTABLE_API
|
||||
#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 <linux/kdev_t.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 *
|
||||
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();
|
||||
|
Reference in New Issue
Block a user