diff options
-rw-r--r-- | gdk/gdksurfaceprivate.h | 3 | ||||
-rw-r--r-- | gdk/gdktoplevel.c | 73 | ||||
-rw-r--r-- | gdk/gdktoplevel.h | 9 | ||||
-rw-r--r-- | gdk/gdktoplevelprivate.h | 4 | ||||
-rw-r--r-- | tests/meson.build | 1 | ||||
-rw-r--r-- | tests/testinhibitshortcuts.c | 107 |
6 files changed, 197 insertions, 0 deletions
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 @@ -74,6 +74,17 @@ gdk_toplevel_default_supports_edge_constraints (GdkToplevel *toplevel) } 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) { iface->present = gdk_toplevel_default_present; @@ -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; +} |