[gtk: 1/9] gdk: Add gdk_toplevel_inhibit_system_shortcuts API



commit 3e1f59af61e339cfa990c2f776aa0c625b3377f2
Author: Olivier Fourdan <ofourdan redhat com>
Date:   Fri Mar 20 15:17:41 2020 +0100

    gdk: Add gdk_toplevel_inhibit_system_shortcuts API
    
    With the removal of grabs from the public API, we need a replacement API
    to let applications bypass system keyboard shortcuts.
    
    A typical use case for this API is remote desktop or virtual machine
    viewers which need to inhibit the default system keyboard shortcuts so
    that the remote session or virtual host gets those instead of the local
    environment.
    
    Close: https://gitlab.gnome.org/GNOME/gtk/issues/982

 gdk/gdksurfaceprivate.h      |   3 ++
 gdk/gdktoplevel.c            |  73 +++++++++++++++++++++++++++++
 gdk/gdktoplevel.h            |   9 ++++
 gdk/gdktoplevelprivate.h     |   4 ++
 tests/meson.build            |   1 +
 tests/testinhibitshortcuts.c | 107 +++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 197 insertions(+)
---
diff --git a/gdk/gdksurfaceprivate.h b/gdk/gdksurfaceprivate.h
index 0c94b5a80f..cbc6e53094 100644
--- a/gdk/gdksurfaceprivate.h
+++ b/gdk/gdksurfaceprivate.h
@@ -102,6 +102,9 @@ struct _GdkSurface
   GdkDrawContext *paint_context;
 
   cairo_region_t *opaque_region;
+
+  guint shortcuts_inhibited : 1;
+  GdkSeat *current_shortcuts_inhibited_seat;
 };
 
 struct _GdkSurfaceClass
diff --git a/gdk/gdktoplevel.c b/gdk/gdktoplevel.c
index 35ea7f52f6..96e4982627 100644
--- a/gdk/gdktoplevel.c
+++ b/gdk/gdktoplevel.c
@@ -73,6 +73,17 @@ gdk_toplevel_default_supports_edge_constraints (GdkToplevel *toplevel)
   return FALSE;
 }
 
+static void
+gdk_toplevel_default_inhibit_system_shortcuts (GdkToplevel *toplevel,
+                                               GdkEvent    *event)
+{
+}
+
+static void
+gdk_toplevel_default_restore_system_shortcuts (GdkToplevel *toplevel)
+{
+}
+
 static void
 gdk_toplevel_default_init (GdkToplevelInterface *iface)
 {
@@ -82,6 +93,8 @@ gdk_toplevel_default_init (GdkToplevelInterface *iface)
   iface->focus = gdk_toplevel_default_focus;
   iface->show_window_menu = gdk_toplevel_default_show_window_menu;
   iface->supports_edge_constraints = gdk_toplevel_default_supports_edge_constraints;
+  iface->inhibit_system_shortcuts = gdk_toplevel_default_inhibit_system_shortcuts;
+  iface->restore_system_shortcuts = gdk_toplevel_default_restore_system_shortcuts;
 
   g_object_interface_install_property (iface,
       g_param_spec_flags ("state",
@@ -137,6 +150,12 @@ gdk_toplevel_default_init (GdkToplevelInterface *iface)
                          GDK_TYPE_FULLSCREEN_MODE,
                          GDK_FULLSCREEN_ON_CURRENT_MONITOR,
                          G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY));
+  g_object_interface_install_property (iface,
+      g_param_spec_boolean ("shortcuts-inhibited",
+                            "Shortcuts inhibited",
+                            "Whether keyboard shortcuts are inhibited",
+                            FALSE,
+                            G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY));
 }
 
 guint
@@ -152,6 +171,7 @@ gdk_toplevel_install_properties (GObjectClass *object_class,
   g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_DECORATED, "decorated");
   g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_DELETABLE, "deletable");
   g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_FULLSCREEN_MODE, 
"fullscreen-mode");
+  g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_SHORTCUTS_INHIBITED, 
"shortcuts-inhibited");
 
   return GDK_TOPLEVEL_NUM_PROPERTIES;
 }
@@ -439,3 +459,56 @@ gdk_toplevel_supports_edge_constraints (GdkToplevel *toplevel)
 
   return GDK_TOPLEVEL_GET_IFACE (toplevel)->supports_edge_constraints (toplevel);
 }
+
+/**
+ * gdk_toplevel_inhibit_system_shortcuts:
+ * @toplevel: the #GdkToplevel requesting system keyboard shortcuts
+ * @event: (nullable): the #GdkEvent that is triggering the inhibit
+ *         request, or %NULL if none is available.
+ *
+ * Requests that the @toplevel inhibit the system shortcuts, asking the
+ * desktop environment/windowing system to let all keyboard events reach
+ * the surface, as long as it is focused, instead of triggering system
+ * actions.
+ *
+ * If granted, the rerouting remains active until the default shortcuts
+ * processing is restored with gdk_toplevel_restore_system_shortcuts(),
+ * or the request is revoked by the desktop enviroment, windowing system
+ * or the user.
+ *
+ * A typical use case for this API is remote desktop or virtual machine
+ * viewers which need to inhibit the default system keyboard shortcuts
+ * so that the remote session or virtual host gets those instead of the
+ * local environment.
+ *
+ * The windowing system or desktop environment may ask the user to grant
+ * or deny the request or even choose to ignore the request entirely.
+ *
+ * The caller can be notified whenever the request is granted or revoked
+ * by listening to the GdkToplevel::shortcuts-inhibited property.
+ *
+ */
+void
+gdk_toplevel_inhibit_system_shortcuts (GdkToplevel *toplevel,
+                                       GdkEvent    *event)
+{
+  g_return_if_fail (GDK_IS_TOPLEVEL (toplevel));
+
+  GDK_TOPLEVEL_GET_IFACE (toplevel)->inhibit_system_shortcuts (toplevel,
+                                                               event);
+}
+
+/**
+ * gdk_toplevel_restore_system_shortcuts:
+ * @toplevel: a #GdkToplevel
+ *
+ * Restore default system keyboard shortcuts which were previously
+ * requested to be inhibited by gdk_toplevel_inhibit_system_shortcuts().
+ */
+void
+gdk_toplevel_restore_system_shortcuts (GdkToplevel *toplevel)
+{
+  g_return_if_fail (GDK_IS_TOPLEVEL (toplevel));
+
+  GDK_TOPLEVEL_GET_IFACE (toplevel)->restore_system_shortcuts (toplevel);
+}
diff --git a/gdk/gdktoplevel.h b/gdk/gdktoplevel.h
index 720d0dea72..863c26f3e8 100644
--- a/gdk/gdktoplevel.h
+++ b/gdk/gdktoplevel.h
@@ -24,6 +24,7 @@
 #error "Only <gdk/gdk.h> can be included directly."
 #endif
 
+#include <gdk/gdkseat.h>
 #include <gdk/gdksurface.h>
 #include <gdk/gdktoplevellayout.h>
 
@@ -87,6 +88,14 @@ void          gdk_toplevel_set_deletable         (GdkToplevel      *toplevel,
 GDK_AVAILABLE_IN_ALL
 gboolean      gdk_toplevel_supports_edge_constraints (GdkToplevel *toplevel);
 
+GDK_AVAILABLE_IN_ALL
+void          gdk_toplevel_inhibit_system_shortcuts  (GdkToplevel *toplevel,
+                                                      GdkEvent    *event);
+
+GDK_AVAILABLE_IN_ALL
+void          gdk_toplevel_restore_system_shortcuts  (GdkToplevel *toplevel);
+
+
 G_END_DECLS
 
 #endif /* __GDK_TOPLEVEL_H__ */
diff --git a/gdk/gdktoplevelprivate.h b/gdk/gdktoplevelprivate.h
index ac3c5a4ab3..c54d0ac503 100644
--- a/gdk/gdktoplevelprivate.h
+++ b/gdk/gdktoplevelprivate.h
@@ -21,6 +21,9 @@ struct _GdkToplevelInterface
   gboolean      (* show_window_menu)    (GdkToplevel       *toplevel,
                                          GdkEvent          *event);
   gboolean      (* supports_edge_constraints) (GdkToplevel *toplevel);
+  void          (* inhibit_system_shortcuts)  (GdkToplevel *toplevel,
+                                               GdkEvent    *event);
+  void          (* restore_system_shortcuts)  (GdkToplevel *toplevel);
 };
 
 typedef enum
@@ -34,6 +37,7 @@ typedef enum
   GDK_TOPLEVEL_PROP_DECORATED,
   GDK_TOPLEVEL_PROP_DELETABLE,
   GDK_TOPLEVEL_PROP_FULLSCREEN_MODE,
+  GDK_TOPLEVEL_PROP_SHORTCUTS_INHIBITED,
   GDK_TOPLEVEL_NUM_PROPERTIES
 } GdkToplevelProperties;
 
diff --git a/tests/meson.build b/tests/meson.build
index abf681fcc3..6d57a63c9f 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -125,6 +125,7 @@ gtk_tests = [
   ['testblur'],
   ['testtexture'],
   ['testwindowdrag'],
+  ['testinhibitshortcuts'],
   ['testtexthistory', ['../gtk/gtktexthistory.c']],
 ]
 
diff --git a/tests/testinhibitshortcuts.c b/tests/testinhibitshortcuts.c
new file mode 100644
index 0000000000..fa746ed3e4
--- /dev/null
+++ b/tests/testinhibitshortcuts.c
@@ -0,0 +1,107 @@
+/* testinhibitshortcuts.c
+
+   Copyright (C) 2017 Red Hat
+   Author: Olivier Fourdan <ofourdan redhat com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtk/gtk.h>
+
+static void
+on_shortcuts_inhibit_change (GdkSurface *surface, GParamSpec *pspec, gpointer data)
+{
+  GtkWidget *button = GTK_WIDGET (data);
+  gboolean button_active;
+  gboolean shortcuts_inhibited;
+
+  g_object_get (GDK_TOPLEVEL (surface), "shortcuts-inhibited", &shortcuts_inhibited, NULL);
+
+  gtk_check_button_set_inconsistent (GTK_CHECK_BUTTON (button), FALSE);
+
+  button_active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+
+  if (button_active != shortcuts_inhibited)
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), shortcuts_inhibited);
+}
+
+static void
+on_button_toggle (GtkWidget *button, gpointer data)
+{
+  GdkSurface *surface = GDK_SURFACE (data);
+  GdkEvent *event;
+
+  if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
+    {
+      gdk_toplevel_restore_system_shortcuts (GDK_TOPLEVEL (surface));
+      return;
+    }
+
+  gtk_check_button_set_inconsistent (GTK_CHECK_BUTTON (button), TRUE);
+  event = gtk_get_current_event ();
+  gdk_toplevel_inhibit_system_shortcuts (GDK_TOPLEVEL (surface), event);
+}
+
+static void
+quit_cb (GtkWidget *widget,
+         gpointer   user_data)
+{
+  gboolean *done = user_data;
+
+  *done = TRUE;
+
+  g_main_context_wakeup (NULL);
+}
+
+int
+main (int argc, char *argv[])
+{
+  GdkSurface *surface;
+  GtkWidget *window;
+  GtkWidget *button;
+  GtkWidget *vbox;
+  GtkWidget *text_view;
+  gboolean done = FALSE;
+
+  gtk_init ();
+
+  window = gtk_window_new ();
+  g_signal_connect (window, "destroy", G_CALLBACK (quit_cb), &done);
+  gtk_widget_realize (window);
+  surface = gtk_native_get_surface (gtk_widget_get_native (window));
+
+  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+  gtk_container_add (GTK_CONTAINER (window), vbox);
+
+  text_view = gtk_text_view_new ();
+  gtk_widget_set_hexpand (text_view, TRUE);
+  gtk_widget_set_vexpand (text_view, TRUE);
+  gtk_container_add (GTK_CONTAINER (vbox), text_view);
+
+  button = gtk_check_button_new_with_label ("Inhibit system keyboard shorcuts");
+
+  gtk_container_add (GTK_CONTAINER (vbox), button);
+  g_signal_connect (G_OBJECT (button), "toggled",
+                    G_CALLBACK (on_button_toggle), surface);
+
+  g_signal_connect (G_OBJECT (surface), "notify::shortcuts-inhibited",
+                    G_CALLBACK (on_shortcuts_inhibit_change), button);
+
+  gtk_widget_show (window);
+
+  while (!done)
+    g_main_context_iteration (NULL, TRUE);
+
+  return 0;
+}


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