[gtk/gtk-3-24: 1/2] wayland: Add support for xdg-output



commit 1f64689c3191af4c986c2ba9bad166fb3b6cad54
Author: Olivier Fourdan <ofourdan redhat com>
Date:   Fri Apr 12 15:14:50 2019 +0200

    wayland: Add support for xdg-output
    
    Previously, the GDK backend for Wayland would deduce the logical size
    of the monitors from the wl_output size and scale.
    
    With the addition of fractional scaling which advertises a larger scale
    value and then scale down the client surface, the computed logical size
    of the monitors in GDK would be wrong and confuse applications which
    insist on using the monitor size and position (like Firefox).
    
    The xdg-output protocol aims at describing outputs in a way which is more
    in line with the concept of an output on desktop oriented systems by
    presenting the outputs using their logical size and position appropriately
    transformed.
    
    Add support for the optional xdg-output protocol so that the size and
    position of the monitors as reported by GDK is correct even when using
    fractional scaling.
    
    Fixes: https://gitlab.gnome.org/GNOME/gtk/issues/1828

 gdk/wayland/Makefile.am          |   2 +
 gdk/wayland/gdkdisplay-wayland.c |   7 ++
 gdk/wayland/gdkdisplay-wayland.h |   2 +
 gdk/wayland/gdkmonitor-wayland.h |   9 ++
 gdk/wayland/gdkprivate-wayland.h |   2 +
 gdk/wayland/gdkscreen-wayland.c  | 190 +++++++++++++++++++++++++++++++--------
 gdk/wayland/meson.build          |   1 +
 7 files changed, 176 insertions(+), 37 deletions(-)
---
diff --git a/gdk/wayland/Makefile.am b/gdk/wayland/Makefile.am
index 956e5c92f5..c7e31beb7c 100644
--- a/gdk/wayland/Makefile.am
+++ b/gdk/wayland/Makefile.am
@@ -33,6 +33,8 @@ BUILT_SOURCES =                               \
        gtk-primary-selection-protocol.c                        \
        tablet-unstable-v2-client-protocol.h    \
        tablet-unstable-v2-protocol.c           \
+       xdg-output-unstable-v1-protocol.c                       \
+       xdg-output-unstable-v1-client-protocol.h                \
        keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h \
        keyboard-shortcuts-inhibit-unstable-v1-protocol.c       \
        server-decoration-client-protocol.h             \
diff --git a/gdk/wayland/gdkdisplay-wayland.c b/gdk/wayland/gdkdisplay-wayland.c
index d35dddf329..4668557cea 100644
--- a/gdk/wayland/gdkdisplay-wayland.c
+++ b/gdk/wayland/gdkdisplay-wayland.c
@@ -513,6 +513,13 @@ gdk_registry_handle_global (void               *data,
                                                            &server_decoration_listener,
                                                            display_wayland);
     }
+  else if (strcmp(interface, "zxdg_output_manager_v1") == 0)
+    {
+      display_wayland->xdg_output_manager =
+            wl_registry_bind (registry, id, &zxdg_output_manager_v1_interface, 1);
+      _gdk_wayland_screen_init_xdg_output (display_wayland->screen);
+      _gdk_wayland_display_async_roundtrip (display_wayland);
+    }
 
   g_hash_table_insert (display_wayland->known_globals,
                        GUINT_TO_POINTER (id), g_strdup (interface));
diff --git a/gdk/wayland/gdkdisplay-wayland.h b/gdk/wayland/gdkdisplay-wayland.h
index d8b4a8d7d1..fb935a6dba 100644
--- a/gdk/wayland/gdkdisplay-wayland.h
+++ b/gdk/wayland/gdkdisplay-wayland.h
@@ -34,6 +34,7 @@
 #include <gdk/wayland/xdg-foreign-unstable-v1-client-protocol.h>
 #include <gdk/wayland/keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h>
 #include <gdk/wayland/server-decoration-client-protocol.h>
+#include <gdk/wayland/xdg-output-unstable-v1-client-protocol.h>
 
 #include <glib.h>
 #include <gdk/gdkkeys.h>
@@ -93,6 +94,7 @@ struct _GdkWaylandDisplay
   struct zxdg_importer_v1 *xdg_importer;
   struct zwp_keyboard_shortcuts_inhibit_manager_v1 *keyboard_shortcuts_inhibit;
   struct org_kde_kwin_server_decoration_manager *server_decoration_manager;
+  struct zxdg_output_manager_v1 *xdg_output_manager;
 
   GList *async_roundtrips;
 
diff --git a/gdk/wayland/gdkmonitor-wayland.h b/gdk/wayland/gdkmonitor-wayland.h
index 581908c513..593ab1d85c 100644
--- a/gdk/wayland/gdkmonitor-wayland.h
+++ b/gdk/wayland/gdkmonitor-wayland.h
@@ -30,6 +30,15 @@ struct _GdkWaylandMonitor {
   guint32 version;
   struct wl_output *output;
   gboolean added;
+
+  struct zxdg_output_v1 *xdg_output;
+  /* Size and position, can be either from wl_output or xdg_output */
+  int32_t x;
+  int32_t y;
+  int32_t width;
+  int32_t height;
+  gboolean wl_output_done;
+  gboolean xdg_output_done;
 };
 
 struct _GdkWaylandMonitorClass {
diff --git a/gdk/wayland/gdkprivate-wayland.h b/gdk/wayland/gdkprivate-wayland.h
index 860e72c260..49db8d5f19 100644
--- a/gdk/wayland/gdkprivate-wayland.h
+++ b/gdk/wayland/gdkprivate-wayland.h
@@ -227,6 +227,8 @@ struct wl_output *_gdk_wayland_screen_get_wl_output (GdkScreen *screen,
 
 void _gdk_wayland_screen_set_has_gtk_shell (GdkScreen       *screen);
 
+void _gdk_wayland_screen_init_xdg_output (GdkScreen *screen);
+
 void _gdk_wayland_window_set_grab_seat (GdkWindow      *window,
                                         GdkSeat        *seat);
 
diff --git a/gdk/wayland/gdkscreen-wayland.c b/gdk/wayland/gdkscreen-wayland.c
index 5935bc697e..2457ecea2a 100644
--- a/gdk/wayland/gdkscreen-wayland.c
+++ b/gdk/wayland/gdkscreen-wayland.c
@@ -1422,6 +1422,117 @@ transform_to_string (int transform)
 
 #endif
 
+static gboolean
+screen_has_xdg_output_support (GdkScreen *screen)
+{
+  GdkDisplay *display = gdk_screen_get_display (screen);
+  GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
+
+  return (display_wayland->xdg_output_manager != NULL);
+}
+
+static gboolean
+monitor_has_xdg_output (GdkWaylandMonitor *monitor)
+{
+  return (monitor->xdg_output != NULL);
+}
+
+static gboolean
+should_update_monitor (GdkWaylandMonitor *monitor)
+{
+  return (GDK_MONITOR (monitor)->geometry.width != 0 &&
+          monitor->version < OUTPUT_VERSION_WITH_DONE);
+}
+
+static void
+apply_monitor_change (GdkWaylandMonitor *monitor)
+{
+  GdkDisplay *display = GDK_MONITOR (monitor)->display;
+  GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (gdk_display_get_default_screen (display));
+
+  GDK_NOTE (MISC,
+            g_message ("monitor %d changed position %d %d, size %d %d",
+                       monitor->id,
+                       monitor->x, monitor->y,
+                       monitor->width, monitor->height));
+
+  gdk_monitor_set_position (GDK_MONITOR (monitor), monitor->x, monitor->y);
+  gdk_monitor_set_size (GDK_MONITOR (monitor), monitor->width, monitor->height);
+  monitor->wl_output_done = FALSE;
+  monitor->xdg_output_done = FALSE;
+
+  g_signal_emit_by_name (screen_wayland, "monitors-changed");
+  update_screen_size (screen_wayland);
+}
+
+static void
+xdg_output_handle_logical_position (void                  *data,
+                                    struct zxdg_output_v1 *xdg_output,
+                                    int32_t                x,
+                                    int32_t                y)
+{
+  GdkWaylandMonitor *monitor = (GdkWaylandMonitor *) data;
+
+  GDK_NOTE (MISC,
+            g_message ("handle logical position xdg-output %d, position %d %d",
+                       monitor->id, x, y));
+  monitor->x = x;
+  monitor->y = y;
+}
+
+static void
+xdg_output_handle_logical_size (void                  *data,
+                                struct zxdg_output_v1 *xdg_output,
+                                int32_t                width,
+                                int32_t                height)
+{
+  GdkWaylandMonitor *monitor = (GdkWaylandMonitor *) data;
+
+  GDK_NOTE (MISC,
+            g_message ("handle logical size xdg-output %d, size %d %d",
+                       monitor->id, width, height));
+  monitor->width = width;
+  monitor->height = height;
+}
+
+static void
+xdg_output_handle_done (void                  *data,
+                        struct zxdg_output_v1 *xdg_output)
+{
+  GdkWaylandMonitor *monitor = (GdkWaylandMonitor *) data;
+
+  GDK_NOTE (MISC,
+            g_message ("handle done xdg-output %d", monitor->id));
+
+  monitor->xdg_output_done = TRUE;
+  if (monitor->wl_output_done)
+    apply_monitor_change (monitor);
+}
+
+static const struct zxdg_output_v1_listener xdg_output_listener = {
+    xdg_output_handle_logical_position,
+    xdg_output_handle_logical_size,
+    xdg_output_handle_done,
+};
+
+static void
+gdk_wayland_screen_get_xdg_output (GdkWaylandMonitor *monitor)
+{
+  GdkDisplay *display = GDK_MONITOR (monitor)->display;
+  GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
+
+  GDK_NOTE (MISC,
+            g_message ("get xdg-output for monitor %d", monitor->id));
+
+  monitor->xdg_output =
+    zxdg_output_manager_v1_get_xdg_output (display_wayland->xdg_output_manager,
+                                           monitor->output);
+
+  zxdg_output_v1_add_listener (monitor->xdg_output,
+                               &xdg_output_listener,
+                               monitor);
+}
+
 static void
 output_handle_geometry (void             *data,
                         struct wl_output *wl_output,
@@ -1440,19 +1551,15 @@ output_handle_geometry (void             *data,
             g_message ("handle geometry output %d, position %d %d, phys. size %d %d, subpixel layout %s, 
manufacturer %s, model %s, transform %s",
                        monitor->id, x, y, physical_width, physical_height, subpixel_to_string (subpixel), 
make, model, transform_to_string (transform)));
 
-  gdk_monitor_set_position (GDK_MONITOR (monitor), x, y);
+  monitor->x = x;
+  monitor->y = y;
   gdk_monitor_set_physical_size (GDK_MONITOR (monitor), physical_width, physical_height);
   gdk_monitor_set_subpixel_layout (GDK_MONITOR (monitor), subpixel);
   gdk_monitor_set_manufacturer (GDK_MONITOR (monitor), make);
   gdk_monitor_set_model (GDK_MONITOR (monitor), model);
 
-  if (GDK_MONITOR (monitor)->geometry.width != 0 && monitor->version < OUTPUT_VERSION_WITH_DONE)
-    {
-      GdkDisplay *display = GDK_MONITOR (monitor)->display;
-      GdkWaylandScreen *screen = GDK_WAYLAND_SCREEN (gdk_display_get_default_screen (display));
-      g_signal_emit_by_name (screen, "monitors-changed");
-      update_screen_size (screen);
-    }
+  if (should_update_monitor (monitor) || !monitor_has_xdg_output (monitor))
+    apply_monitor_change (monitor);
 }
 
 static void
@@ -1460,21 +1567,14 @@ output_handle_done (void             *data,
                     struct wl_output *wl_output)
 {
   GdkWaylandMonitor *monitor = (GdkWaylandMonitor *)data;
-  GdkDisplay *display = gdk_monitor_get_display (GDK_MONITOR (monitor));
-  GdkWaylandScreen *screen_wayland = GDK_WAYLAND_SCREEN (gdk_display_get_default_screen (display));
 
   GDK_NOTE (MISC,
             g_message ("handle done output %d", monitor->id));
 
-  if (!monitor->added)
-    {
-      monitor->added = TRUE;
-      g_ptr_array_add (GDK_WAYLAND_DISPLAY (display)->monitors, monitor);
-      gdk_display_monitor_added (display, GDK_MONITOR (monitor));
-    }
+  monitor->wl_output_done = TRUE;
 
-  g_signal_emit_by_name (screen_wayland, "monitors-changed");
-  update_screen_size (screen_wayland);
+  if (!monitor_has_xdg_output (monitor) || monitor->xdg_output_done)
+    apply_monitor_change (monitor);
 }
 
 static void
@@ -1491,6 +1591,9 @@ output_handle_scale (void             *data,
   GDK_NOTE (MISC,
             g_message ("handle scale output %d, scale %d", monitor->id, scale));
 
+  if (monitor_has_xdg_output (monitor))
+    return;
+
   gdk_monitor_get_geometry (GDK_MONITOR (monitor), &previous_geometry);
   previous_scale = gdk_monitor_get_scale_factor (GDK_MONITOR (monitor));
 
@@ -1498,13 +1601,11 @@ output_handle_scale (void             *data,
   height = previous_geometry.height * previous_scale;
 
   gdk_monitor_set_scale_factor (GDK_MONITOR (monitor), scale);
-  gdk_monitor_set_size (GDK_MONITOR (monitor), width / scale, height / scale);
+  monitor->width = width / scale;
+  monitor->height = height / scale;
 
-  if (GDK_MONITOR (monitor)->geometry.width != 0 && monitor->version < OUTPUT_VERSION_WITH_DONE)
-    {
-      GdkScreen *screen = gdk_display_get_default_screen (GDK_MONITOR (monitor)->display);
-      g_signal_emit_by_name (screen, "monitors-changed");
-    }
+  if (should_update_monitor (monitor))
+    apply_monitor_change (monitor);
 }
 
 static void
@@ -1526,15 +1627,12 @@ output_handle_mode (void             *data,
     return;
 
   scale = gdk_monitor_get_scale_factor (GDK_MONITOR (monitor));
-  gdk_monitor_set_size (GDK_MONITOR (monitor), width / scale, height / scale);
+  monitor->width = width / scale;
+  monitor->height = height / scale;
   gdk_monitor_set_refresh_rate (GDK_MONITOR (monitor), refresh);
 
-  if (width != 0 && monitor->version < OUTPUT_VERSION_WITH_DONE)
-    {
-      GdkScreen *screen = gdk_display_get_default_screen (GDK_MONITOR (monitor)->display);
-      g_signal_emit_by_name (screen, "monitors-changed");
-      update_screen_size (GDK_WAYLAND_SCREEN (screen));
-    }
+  if (should_update_monitor (monitor) || !monitor_has_xdg_output (monitor))
+    apply_monitor_change (monitor);
 }
 
 static const struct wl_output_listener output_listener =
@@ -1562,13 +1660,16 @@ _gdk_wayland_screen_add_output (GdkScreen        *screen,
   monitor->output = output;
   monitor->version = version;
 
-  if (monitor->version < OUTPUT_VERSION_WITH_DONE)
-    {
-      g_ptr_array_add (GDK_WAYLAND_DISPLAY (display)->monitors, monitor);
-      gdk_display_monitor_added (display, GDK_MONITOR (monitor));
-    }
-
+  g_ptr_array_add (GDK_WAYLAND_DISPLAY (display)->monitors, monitor);
+  gdk_display_monitor_added (display, GDK_MONITOR (monitor));
   wl_output_add_listener (output, &output_listener, monitor);
+
+  GDK_NOTE (MISC,
+            g_message ("xdg_output_manager %p",
+                       GDK_WAYLAND_DISPLAY (display)->xdg_output_manager));
+
+  if (screen_has_xdg_output_support (screen))
+    gdk_wayland_screen_get_xdg_output (monitor);
 }
 
 struct wl_output *
@@ -1666,3 +1767,18 @@ _gdk_wayland_screen_get_output_scale (GdkScreen        *screen,
 
   return 0;
 }
+
+void
+_gdk_wayland_screen_init_xdg_output (GdkScreen *screen)
+{
+  GdkDisplay *display = gdk_screen_get_display (screen);
+  GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
+  int i;
+
+  GDK_NOTE (MISC,
+            g_message ("init xdg-output support, %d monitor(s) already present",
+                       display_wayland->monitors->len));
+
+  for (i = 0; i < display_wayland->monitors->len; i++)
+    gdk_wayland_screen_get_xdg_output (display_wayland->monitors->pdata[i]);
+}
diff --git a/gdk/wayland/meson.build b/gdk/wayland/meson.build
index 559e02ba3d..8f6b7faf31 100644
--- a/gdk/wayland/meson.build
+++ b/gdk/wayland/meson.build
@@ -55,6 +55,7 @@ proto_sources = [
   ['tablet', 'unstable', 'v2', ],
   ['keyboard-shortcuts-inhibit', 'unstable', 'v1', ],
   ['server-decoration', 'private' ],
+  ['xdg-output', 'unstable', 'v1', ],
 ]
 
 gdk_wayland_gen_headers = []


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]