summaryrefslogtreecommitdiff
path: root/gtk
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2010-05-25 18:38:44 -0400
committerMatthias Clasen <mclasen@redhat.com>2010-05-25 18:38:44 -0400
commitbd4609b14042a91646cd9057764eecfbc6faf42b (patch)
tree8721405d2b45a998f87cccc672b4070780907fb8 /gtk
parenta538f639b69a39d7bb85b39af2dfd296d28fc0aa (diff)
downloadgtk+-bd4609b14042a91646cd9057764eecfbc6faf42b.tar.gz
Merge the xi2-for-master branch
Diffstat (limited to 'gtk')
-rw-r--r--gtk/gtk.symbols12
-rw-r--r--gtk/gtkaboutdialog.c42
-rw-r--r--gtk/gtkbutton.c34
-rw-r--r--gtk/gtkcellrendereraccel.c89
-rw-r--r--gtk/gtkcolorsel.c98
-rw-r--r--gtk/gtkcombobox.c122
-rw-r--r--gtk/gtkcombobox.h2
-rw-r--r--gtk/gtkdnd.c155
-rw-r--r--gtk/gtkentry.c15
-rw-r--r--gtk/gtkentrycompletion.c34
-rw-r--r--gtk/gtkentryprivate.h5
-rw-r--r--gtk/gtkhandlebox.c38
-rw-r--r--gtk/gtkiconview.c21
-rw-r--r--gtk/gtklabel.c9
-rw-r--r--gtk/gtkmain.c258
-rw-r--r--gtk/gtkmain.h7
-rw-r--r--gtk/gtkmarshalers.list1
-rw-r--r--gtk/gtkmenu.c246
-rw-r--r--gtk/gtkmenu.h9
-rw-r--r--gtk/gtkmenushell.c51
-rw-r--r--gtk/gtkmenushell.h8
-rw-r--r--gtk/gtknotebook.c13
-rw-r--r--gtk/gtkpaned.c31
-rw-r--r--gtk/gtkplug-x11.c72
-rw-r--r--gtk/gtkprivate.h1
-rw-r--r--gtk/gtkrange.c77
-rw-r--r--gtk/gtkscalebutton.c144
-rw-r--r--gtk/gtksocket.c2
-rw-r--r--gtk/gtkspinbutton.c6
-rw-r--r--gtk/gtktextview.c56
-rw-r--r--gtk/gtktooltip.c14
-rw-r--r--gtk/gtktreeprivate.h3
-rw-r--r--gtk/gtktreeview.c86
-rw-r--r--gtk/gtktreeviewcolumn.c3
-rw-r--r--gtk/gtkwidget.c516
-rw-r--r--gtk/gtkwidget.h30
-rw-r--r--gtk/gtkwindow.c232
-rw-r--r--gtk/gtkwindow.h14
38 files changed, 2029 insertions, 527 deletions
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index 7a9644dca4..8f3340277f 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -804,6 +804,7 @@ gtk_combo_box_new_text
gtk_combo_box_new_with_model
gtk_combo_box_popdown
gtk_combo_box_popup
+gtk_combo_box_popup_for_device
gtk_combo_box_prepend_text
gtk_combo_box_remove_text
gtk_combo_box_set_active
@@ -1950,6 +1951,7 @@ gtk_list_store_swap
#if IN_FILE(__GTK_MAIN_C__)
gtk_get_option_group
gtk_get_current_event
+gtk_get_current_event_device
gtk_get_current_event_state
gtk_get_current_event_time
gtk_false G_GNUC_CONST
@@ -1963,6 +1965,8 @@ gtk_get_event_widget
gtk_grab_add
gtk_grab_get_current
gtk_grab_remove
+gtk_device_grab_add
+gtk_device_grab_remove
gtk_propagate_event
gtk_quit_add
gtk_quit_add_destroy
@@ -2020,6 +2024,7 @@ gtk_menu_get_type G_GNUC_CONST
gtk_menu_new
gtk_menu_popdown
gtk_menu_popup
+gtk_menu_popup_for_device
gtk_menu_reorder_child
gtk_menu_reposition
gtk_menu_set_accel_group
@@ -4304,6 +4309,7 @@ gtk_widget_activate
gtk_widget_is_composited
gtk_widget_add_accelerator
gtk_widget_add_events
+gtk_widget_add_device_events
gtk_widget_add_mnemonic_label
gtk_widget_can_activate_accel
gtk_widget_child_focus
@@ -4338,6 +4344,7 @@ gtk_widget_get_direction
gtk_widget_get_display
gtk_widget_get_double_buffered
gtk_widget_get_events
+gtk_widget_get_device_events
gtk_widget_get_extension_events
gtk_widget_get_has_tooltip
gtk_widget_get_modifier_style
@@ -4424,6 +4431,7 @@ gtk_widget_set_default_direction
gtk_widget_set_direction
gtk_widget_set_double_buffered
gtk_widget_set_events
+gtk_widget_set_device_events
gtk_widget_set_extension_events
gtk_widget_set_has_tooltip
gtk_widget_set_name
@@ -4470,6 +4478,9 @@ gtk_widget_set_realized
gtk_widget_get_realized
gtk_widget_set_mapped
gtk_widget_get_mapped
+gtk_widget_get_support_multidevice
+gtk_widget_set_support_multidevice
+gtk_widget_device_is_shadowed
#endif
#endif
@@ -4523,6 +4534,7 @@ gtk_window_group_get_type G_GNUC_CONST
gtk_window_group_new
gtk_window_group_remove_window
gtk_window_group_list_windows
+gtk_window_group_get_current_device_grab
gtk_window_has_toplevel_focus
gtk_window_iconify
gtk_window_is_active
diff --git a/gtk/gtkaboutdialog.c b/gtk/gtkaboutdialog.c
index 54e70d603c..6b17f1c2ec 100644
--- a/gtk/gtkaboutdialog.c
+++ b/gtk/gtkaboutdialog.c
@@ -194,9 +194,10 @@ static void follow_if_link (GtkAboutDialog
GtkTextView *text_view,
GtkTextIter *iter);
static void set_cursor_if_appropriate (GtkAboutDialog *about,
- GtkTextView *text_view,
- gint x,
- gint y);
+ GtkTextView *text_view,
+ GdkDevice *device,
+ gint x,
+ gint y);
static void display_credits_dialog (GtkWidget *button,
gpointer data);
static void display_license_dialog (GtkWidget *button,
@@ -1866,9 +1867,10 @@ text_view_event_after (GtkWidget *text_view,
static void
set_cursor_if_appropriate (GtkAboutDialog *about,
- GtkTextView *text_view,
- gint x,
- gint y)
+ GtkTextView *text_view,
+ GdkDevice *device,
+ gint x,
+ gint y)
{
GtkAboutDialogPrivate *priv = about->priv;
GSList *tags = NULL, *tagp = NULL;
@@ -1896,9 +1898,9 @@ set_cursor_if_appropriate (GtkAboutDialog *about,
priv->hovering_over_link = hovering_over_link;
if (hovering_over_link)
- gdk_window_set_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), priv->hand_cursor);
+ gdk_window_set_device_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), device, priv->hand_cursor);
else
- gdk_window_set_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), priv->regular_cursor);
+ gdk_window_set_device_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), device, priv->regular_cursor);
}
if (tags)
@@ -1916,7 +1918,7 @@ text_view_motion_notify_event (GtkWidget *text_view,
GTK_TEXT_WINDOW_WIDGET,
event->x, event->y, &x, &y);
- set_cursor_if_appropriate (about, GTK_TEXT_VIEW (text_view), x, y);
+ set_cursor_if_appropriate (about, GTK_TEXT_VIEW (text_view), event->device, x, y);
gdk_event_request_motions (event);
@@ -1929,15 +1931,27 @@ text_view_visibility_notify_event (GtkWidget *text_view,
GdkEventVisibility *event,
GtkAboutDialog *about)
{
+ GdkDeviceManager *device_manager;
+ GdkDisplay *display;
+ GList *devices, *d;
gint wx, wy, bx, by;
- gdk_window_get_pointer (gtk_widget_get_window (text_view), &wx, &wy, NULL);
+ display = gdk_drawable_get_display (event->window);
+ device_manager = gdk_display_get_device_manager (display);
+ devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
- gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
- GTK_TEXT_WINDOW_WIDGET,
- wx, wy, &bx, &by);
+ for (d = devices; d; d = d->next)
+ {
+ GdkDevice *dev = d->data;
- set_cursor_if_appropriate (about, GTK_TEXT_VIEW (text_view), bx, by);
+ gdk_window_get_device_position (text_view->window, dev, &wx, &wy, NULL);
+
+ gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
+ GTK_TEXT_WINDOW_WIDGET,
+ wx, wy, &bx, &by);
+
+ set_cursor_if_appropriate (about, GTK_TEXT_VIEW (text_view), dev, bx, by);
+ }
return FALSE;
}
diff --git a/gtk/gtkbutton.c b/gtk/gtkbutton.c
index 37367b3fb3..4ebe9a0ba8 100644
--- a/gtk/gtkbutton.c
+++ b/gtk/gtkbutton.c
@@ -88,9 +88,9 @@ struct _GtkButtonPrivate
GtkWidget *image;
guint align_set : 1;
guint image_is_stock : 1;
- guint has_grab : 1;
guint use_action_appearance : 1;
guint32 grab_time;
+ GdkDevice *grab_keyboard;
GtkPositionType image_position;
GtkAction *action;
};
@@ -1715,22 +1715,28 @@ gtk_real_button_activate (GtkButton *button)
{
GtkWidget *widget = GTK_WIDGET (button);
GtkButtonPrivate *priv;
+ GdkDevice *device;
guint32 time;
priv = GTK_BUTTON_GET_PRIVATE (button);
+ device = gtk_get_current_event_device ();
+
+ g_return_if_fail (device && device->source == GDK_SOURCE_KEYBOARD);
if (gtk_widget_get_realized (widget) && !button->activate_timeout)
{
time = gtk_get_current_event_time ();
- if (gdk_keyboard_grab (button->event_window, TRUE, time) ==
- GDK_GRAB_SUCCESS)
- {
- priv->has_grab = TRUE;
+
+ if (gdk_device_grab (device, button->event_window,
+ GDK_OWNERSHIP_WINDOW, TRUE,
+ GDK_KEY_PRESS | GDK_KEY_RELEASE,
+ NULL, time) == GDK_GRAB_SUCCESS)
+ {
+ gtk_device_grab_add (widget, device, TRUE);
+ priv->grab_keyboard = device;
priv->grab_time = time;
}
- gtk_grab_add (widget);
-
button->activate_timeout = gdk_threads_add_timeout (ACTIVATE_TIMEOUT,
button_activate_timeout,
button);
@@ -1752,12 +1758,12 @@ gtk_button_finish_activate (GtkButton *button,
g_source_remove (button->activate_timeout);
button->activate_timeout = 0;
- if (priv->has_grab)
+ if (priv->grab_keyboard)
{
- gdk_display_keyboard_ungrab (gtk_widget_get_display (widget),
- priv->grab_time);
+ gdk_device_ungrab (priv->grab_keyboard, priv->grab_time);
+ gtk_device_grab_remove (widget, priv->grab_keyboard);
+ priv->grab_keyboard = NULL;
}
- gtk_grab_remove (widget);
button->button_down = FALSE;
@@ -2247,8 +2253,14 @@ gtk_button_grab_notify (GtkWidget *widget,
gboolean was_grabbed)
{
GtkButton *button = GTK_BUTTON (widget);
+ GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
gboolean save_in;
+ if (button->activate_timeout &&
+ priv->grab_keyboard &&
+ gtk_widget_device_is_shadowed (widget, priv->grab_keyboard))
+ gtk_button_finish_activate (button, FALSE);
+
if (!was_grabbed)
{
save_in = button->in_button;
diff --git a/gtk/gtkcellrendereraccel.c b/gtk/gtkcellrendereraccel.c
index dedbfdae64..9dfd2c2a8a 100644
--- a/gtk/gtkcellrendereraccel.c
+++ b/gtk/gtkcellrendereraccel.c
@@ -30,6 +30,8 @@
#include "gtkalias.h"
+#define GTK_CELL_RENDERER_ACCEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_CELL_RENDERER_ACCEL, GtkCellRendererAccelPrivate))
+
static void gtk_cell_renderer_accel_get_property (GObject *object,
guint param_id,
GValue *value,
@@ -72,6 +74,14 @@ enum {
PROP_ACCEL_MODE
};
+typedef struct GtkCellRendererAccelPrivate GtkCellRendererAccelPrivate;
+
+struct GtkCellRendererAccelPrivate
+{
+ GdkDevice *grab_keyboard;
+ GdkDevice *grab_pointer;
+};
+
static guint signals[LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE (GtkCellRendererAccel, gtk_cell_renderer_accel, GTK_TYPE_CELL_RENDERER_TEXT)
@@ -213,6 +223,8 @@ gtk_cell_renderer_accel_class_init (GtkCellRendererAccelClass *cell_accel_class)
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1,
G_TYPE_STRING);
+
+ g_type_class_add_private (object_class, sizeof (GtkCellRendererAccelPrivate));
}
@@ -405,6 +417,7 @@ grab_key_callback (GtkWidget *widget,
GdkEventKey *event,
GtkCellRendererAccel *accel)
{
+ GtkCellRendererAccelPrivate *priv;
GdkModifierType accel_mods = 0;
guint accel_key;
gchar *path;
@@ -413,6 +426,7 @@ grab_key_callback (GtkWidget *widget,
GdkModifierType consumed_modifiers;
GdkDisplay *display;
+ priv = GTK_CELL_RENDERER_ACCEL_GET_PRIVATE (accel);
display = gtk_widget_get_display (widget);
if (event->is_modifier)
@@ -471,9 +485,9 @@ grab_key_callback (GtkWidget *widget,
edited = TRUE;
out:
- gtk_grab_remove (accel->grab_widget);
- gdk_display_keyboard_ungrab (display, event->time);
- gdk_display_pointer_ungrab (display, event->time);
+ gtk_device_grab_remove (accel->grab_widget, priv->grab_pointer);
+ gdk_device_ungrab (priv->grab_keyboard, event->time);
+ gdk_device_ungrab (priv->grab_pointer, event->time);
path = g_strdup (g_object_get_data (G_OBJECT (accel->edit_widget), "gtk-cell-renderer-text"));
@@ -481,7 +495,9 @@ grab_key_callback (GtkWidget *widget,
gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (accel->edit_widget));
accel->edit_widget = NULL;
accel->grab_widget = NULL;
-
+ priv->grab_keyboard = NULL;
+ priv->grab_pointer = NULL;
+
if (edited)
g_signal_emit (accel, signals[ACCEL_EDITED], 0, path,
accel_key, accel_mods, event->hardware_keycode);
@@ -497,11 +513,16 @@ static void
ungrab_stuff (GtkWidget *widget,
GtkCellRendererAccel *accel)
{
- GdkDisplay *display = gtk_widget_get_display (widget);
+ GtkCellRendererAccelPrivate *priv;
+
+ priv = GTK_CELL_RENDERER_ACCEL_GET_PRIVATE (accel);
+
+ gtk_device_grab_remove (accel->grab_widget, priv->grab_pointer);
+ gdk_device_ungrab (priv->grab_keyboard, GDK_CURRENT_TIME);
+ gdk_device_ungrab (priv->grab_pointer, GDK_CURRENT_TIME);
- gtk_grab_remove (accel->grab_widget);
- gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
- gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
+ priv->grab_keyboard = NULL;
+ priv->grab_pointer = NULL;
g_signal_handlers_disconnect_by_func (G_OBJECT (accel->grab_widget),
G_CALLBACK (grab_key_callback),
@@ -548,34 +569,62 @@ gtk_cell_renderer_accel_start_editing (GtkCellRenderer *cell,
GdkRectangle *cell_area,
GtkCellRendererState flags)
{
+ GtkCellRendererAccelPrivate *priv;
GtkCellRendererText *celltext;
GtkCellRendererAccel *accel;
GtkWidget *label;
GtkWidget *eventbox;
-
+ GdkDevice *device, *keyb, *pointer;
+ guint32 time;
+
celltext = GTK_CELL_RENDERER_TEXT (cell);
accel = GTK_CELL_RENDERER_ACCEL (cell);
+ priv = GTK_CELL_RENDERER_ACCEL_GET_PRIVATE (cell);
/* If the cell isn't editable we return NULL. */
if (celltext->editable == FALSE)
return NULL;
g_return_val_if_fail (widget->window != NULL, NULL);
-
- if (gdk_keyboard_grab (widget->window, FALSE,
- gdk_event_get_time (event)) != GDK_GRAB_SUCCESS)
+
+ if (event)
+ device = gdk_event_get_device (event);
+ else
+ device = gtk_get_current_event_device ();
+
+ if (!device)
+ return NULL;
+
+ if (device->source == GDK_SOURCE_KEYBOARD)
+ {
+ keyb = device;
+ pointer = gdk_device_get_associated_device (device);
+ }
+ else
+ {
+ pointer = device;
+ keyb = gdk_device_get_associated_device (device);
+ }
+
+ time = gdk_event_get_time (event);
+
+ if (gdk_device_grab (keyb, widget->window,
+ GDK_OWNERSHIP_WINDOW, FALSE,
+ GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+ NULL, time) != GDK_GRAB_SUCCESS)
return NULL;
- if (gdk_pointer_grab (widget->window, FALSE,
- GDK_BUTTON_PRESS_MASK,
- NULL, NULL,
- gdk_event_get_time (event)) != GDK_GRAB_SUCCESS)
+ if (gdk_device_grab (pointer, widget->window,
+ GDK_OWNERSHIP_WINDOW, FALSE,
+ GDK_BUTTON_PRESS_MASK,
+ NULL, time) != GDK_GRAB_SUCCESS)
{
- gdk_display_keyboard_ungrab (gtk_widget_get_display (widget),
- gdk_event_get_time (event));
+ gdk_device_ungrab (keyb, time);
return NULL;
}
-
+
+ priv->grab_keyboard = keyb;
+ priv->grab_pointer = pointer;
accel->grab_widget = widget;
g_signal_connect (G_OBJECT (widget), "key-press-event",
@@ -609,7 +658,7 @@ gtk_cell_renderer_accel_start_editing (GtkCellRenderer *cell,
gtk_widget_show_all (accel->edit_widget);
- gtk_grab_add (accel->grab_widget);
+ gtk_device_grab_add (accel->grab_widget, pointer, TRUE);
g_signal_connect (G_OBJECT (accel->edit_widget), "unrealize",
G_CALLBACK (ungrab_stuff), accel);
diff --git a/gtk/gtkcolorsel.c b/gtk/gtkcolorsel.c
index 34d2d4a7d9..10ec2724f7 100644
--- a/gtk/gtkcolorsel.c
+++ b/gtk/gtkcolorsel.c
@@ -149,6 +149,8 @@ struct _ColorSelectionPrivate
/* Window for grabbing on */
GtkWidget *dropper_grab_widget;
guint32 grab_time;
+ GdkDevice *keyboard_device;
+ GdkDevice *pointer_device;
/* Connection to settings */
gulong settings_connection;
@@ -1631,10 +1633,11 @@ make_picker_cursor (GdkScreen *screen)
}
static void
-grab_color_at_mouse (GdkScreen *screen,
- gint x_root,
- gint y_root,
- gpointer data)
+grab_color_at_pointer (GdkScreen *screen,
+ GdkDevice *device,
+ gint x_root,
+ gint y_root,
+ gpointer data)
{
GdkImage *image;
guint32 pixel;
@@ -1651,7 +1654,7 @@ grab_color_at_mouse (GdkScreen *screen,
{
gint x, y;
GdkDisplay *display = gdk_screen_get_display (screen);
- GdkWindow *window = gdk_display_get_window_at_pointer (display, &x, &y);
+ GdkWindow *window = gdk_display_get_window_at_device_position (display, device, &x, &y);
if (!window)
return;
image = gdk_drawable_get_image (window, x, y, 1, 1);
@@ -1682,18 +1685,19 @@ shutdown_eyedropper (GtkWidget *widget)
{
GtkColorSelection *colorsel;
ColorSelectionPrivate *priv;
- GdkDisplay *display = gtk_widget_get_display (widget);
colorsel = GTK_COLOR_SELECTION (widget);
- priv = colorsel->private_data;
+ priv = colorsel->private_data;
if (priv->has_grab)
{
- gdk_display_keyboard_ungrab (display, priv->grab_time);
- gdk_display_pointer_ungrab (display, priv->grab_time);
- gtk_grab_remove (priv->dropper_grab_widget);
+ gdk_device_ungrab (priv->keyboard_device, priv->grab_time);
+ gdk_device_ungrab (priv->pointer_device, priv->grab_time);
+ gtk_device_grab_remove (priv->dropper_grab_widget, priv->pointer_device);
priv->has_grab = FALSE;
+ priv->keyboard_device = NULL;
+ priv->pointer_device = NULL;
}
}
@@ -1702,8 +1706,9 @@ mouse_motion (GtkWidget *invisible,
GdkEventMotion *event,
gpointer data)
{
- grab_color_at_mouse (gdk_event_get_screen ((GdkEvent *)event),
- event->x_root, event->y_root, data);
+ grab_color_at_pointer (gdk_event_get_screen ((GdkEvent *) event),
+ gdk_event_get_device ((GdkEvent *) event),
+ event->x_root, event->y_root, data);
}
static gboolean
@@ -1716,8 +1721,9 @@ mouse_release (GtkWidget *invisible,
if (event->button != 1)
return FALSE;
- grab_color_at_mouse (gdk_event_get_screen ((GdkEvent *)event),
- event->x_root, event->y_root, data);
+ grab_color_at_pointer (gdk_event_get_screen ((GdkEvent *) event),
+ gdk_event_get_device ((GdkEvent *) event),
+ event->x_root, event->y_root, data);
shutdown_eyedropper (GTK_WIDGET (data));
@@ -1739,12 +1745,15 @@ key_press (GtkWidget *invisible,
gpointer data)
{
GdkDisplay *display = gtk_widget_get_display (invisible);
- GdkScreen *screen = gdk_event_get_screen ((GdkEvent *)event);
+ GdkScreen *screen = gdk_event_get_screen ((GdkEvent *) event);
+ GdkDevice *device, *pointer_device;
guint state = event->state & gtk_accelerator_get_default_mod_mask ();
gint x, y;
gint dx, dy;
- gdk_display_get_pointer (display, NULL, &x, &y, NULL);
+ device = gdk_event_get_device ((GdkEvent * ) event);
+ pointer_device = gdk_device_get_associated_device (device);
+ gdk_display_get_device_state (display, pointer_device, NULL, &x, &y, NULL);
dx = 0;
dy = 0;
@@ -1756,7 +1765,7 @@ key_press (GtkWidget *invisible,
case GDK_ISO_Enter:
case GDK_KP_Enter:
case GDK_KP_Space:
- grab_color_at_mouse (screen, x, y, data);
+ grab_color_at_pointer (screen, pointer_device, x, y, data);
/* fall through */
case GDK_Escape:
@@ -1797,8 +1806,8 @@ key_press (GtkWidget *invisible,
return FALSE;
}
- gdk_display_warp_pointer (display, screen, x + dx, y + dy);
-
+ gdk_display_warp_device (display, pointer_device, screen, x + dx, y + dy);
+
return TRUE;
}
@@ -1838,12 +1847,26 @@ get_screen_color (GtkWidget *button)
GtkColorSelection *colorsel = g_object_get_data (G_OBJECT (button), "COLORSEL");
ColorSelectionPrivate *priv = colorsel->private_data;
GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (button));
+ GdkDevice *device, *keyb_device, *pointer_device;
GdkCursor *picker_cursor;
GdkGrabStatus grab_status;
GtkWidget *grab_widget, *toplevel;
guint32 time = gtk_get_current_event_time ();
-
+
+ device = gtk_get_current_event_device ();
+
+ if (device->source == GDK_SOURCE_KEYBOARD)
+ {
+ keyb_device = device;
+ pointer_device = gdk_device_get_associated_device (device);
+ }
+ else
+ {
+ pointer_device = device;
+ keyb_device = gdk_device_get_associated_device (device);
+ }
+
if (priv->dropper_grab_widget == NULL)
{
grab_widget = gtk_window_new (GTK_WINDOW_POPUP);
@@ -1867,29 +1890,38 @@ get_screen_color (GtkWidget *button)
priv->dropper_grab_widget = grab_widget;
}
- if (gdk_keyboard_grab (priv->dropper_grab_widget->window,
- FALSE, time) != GDK_GRAB_SUCCESS)
+ if (gdk_device_grab (keyb_device,
+ priv->dropper_grab_widget->window,
+ GDK_OWNERSHIP_APPLICATION, FALSE,
+ GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+ NULL, time) != GDK_GRAB_SUCCESS)
return;
-
+
picker_cursor = make_picker_cursor (screen);
- grab_status = gdk_pointer_grab (priv->dropper_grab_widget->window,
- FALSE,
- GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK,
- NULL,
- picker_cursor,
- time);
+ grab_status = gdk_device_grab (pointer_device,
+ priv->dropper_grab_widget->window,
+ GDK_OWNERSHIP_APPLICATION,
+ FALSE,
+ GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK,
+ picker_cursor,
+ time);
gdk_cursor_unref (picker_cursor);
-
+
if (grab_status != GDK_GRAB_SUCCESS)
{
- gdk_display_keyboard_ungrab (gtk_widget_get_display (button), time);
+ gdk_device_ungrab (keyb_device, time);
return;
}
- gtk_grab_add (priv->dropper_grab_widget);
+ gtk_device_grab_add (priv->dropper_grab_widget,
+ pointer_device,
+ TRUE);
+
priv->grab_time = time;
priv->has_grab = TRUE;
-
+ priv->keyboard_device = keyb_device;
+ priv->pointer_device = pointer_device;
+
g_signal_connect (priv->dropper_grab_widget, "button-press-event",
G_CALLBACK (mouse_press), colorsel);
g_signal_connect (priv->dropper_grab_widget, "key-press-event",
diff --git a/gtk/gtkcombobox.c b/gtk/gtkcombobox.c
index 23b159b88b..64c8e05658 100644
--- a/gtk/gtkcombobox.c
+++ b/gtk/gtkcombobox.c
@@ -127,6 +127,9 @@ struct _GtkComboBoxPrivate
gpointer row_separator_data;
GDestroyNotify row_separator_destroy;
+ GdkDevice *grab_pointer;
+ GdkDevice *grab_keyboard;
+
gchar *tearoff_title;
};
@@ -1861,27 +1864,31 @@ gtk_combo_box_menu_popup (GtkComboBox *combo_box,
static gboolean
popup_grab_on_window (GdkWindow *window,
- guint32 activate_time,
- gboolean grab_keyboard)
-{
- if ((gdk_pointer_grab (window, TRUE,
- GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
- GDK_POINTER_MOTION_MASK,
- NULL, NULL, activate_time) == 0))
+ GdkDevice *keyboard,
+ GdkDevice *pointer,
+ guint32 activate_time)
+{
+ if (keyboard &&
+ gdk_device_grab (keyboard, window,
+ GDK_OWNERSHIP_WINDOW, TRUE,
+ GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+ NULL, activate_time) != GDK_GRAB_SUCCESS)
+ return FALSE;
+
+ if (pointer &&
+ gdk_device_grab (pointer, window,
+ GDK_OWNERSHIP_WINDOW, TRUE,
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK,
+ NULL, activate_time) != GDK_GRAB_SUCCESS)
{
- if (!grab_keyboard ||
- gdk_keyboard_grab (window, TRUE,
- activate_time) == 0)
- return TRUE;
- else
- {
- gdk_display_pointer_ungrab (gdk_drawable_get_display (window),
- activate_time);
- return FALSE;
- }
+ if (keyboard)
+ gdk_device_ungrab (keyboard, activate_time);
+
+ return FALSE;
}
- return FALSE;
+ return TRUE;
}
/**
@@ -1903,13 +1910,30 @@ gtk_combo_box_popup (GtkComboBox *combo_box)
g_signal_emit (combo_box, combo_box_signals[POPUP], 0);
}
-static void
-gtk_combo_box_real_popup (GtkComboBox *combo_box)
+/**
+ * gtk_combo_box_popup_for_device:
+ * @combo_box: a #GtkComboBox
+ * @device: a #GdkDevice
+ *
+ * Pops up the menu or dropdown list of @combo_box, the popup window
+ * will be grabbed so only @device and its associated pointer/keyboard
+ * are the only #GdkDevice<!-- -->s able to send events to it.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_combo_box_popup_for_device (GtkComboBox *combo_box,
+ GdkDevice *device)
{
GtkComboBoxPrivate *priv = combo_box->priv;
gint x, y, width, height;
GtkTreePath *path = NULL, *ppath;
GtkWidget *toplevel;
+ GdkDevice *keyboard, *pointer;
+ guint32 time;
+
+ g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
+ g_return_if_fail (GDK_IS_DEVICE (device));
if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
return;
@@ -1917,6 +1941,22 @@ gtk_combo_box_real_popup (GtkComboBox *combo_box)
if (gtk_widget_get_mapped (priv->popup_widget))
return;
+ if (priv->grab_pointer && priv->grab_keyboard)
+ return;
+
+ time = gtk_get_current_event_time ();
+
+ if (device->source == GDK_SOURCE_KEYBOARD)
+ {
+ keyboard = device;
+ pointer = gdk_device_get_associated_device (device);
+ }
+ else
+ {
+ pointer = device;
+ keyboard = gdk_device_get_associated_device (device);
+ }
+
if (GTK_IS_MENU (priv->popup_widget))
{
gtk_combo_box_menu_popup (combo_box,
@@ -1966,13 +2006,40 @@ gtk_combo_box_real_popup (GtkComboBox *combo_box)
gtk_widget_grab_focus (priv->tree_view);
if (!popup_grab_on_window (priv->popup_window->window,
- GDK_CURRENT_TIME, TRUE))
+ keyboard, pointer, time))
{
gtk_widget_hide (priv->popup_window);
return;
}
- gtk_grab_add (priv->popup_window);
+ gtk_device_grab_add (priv->popup_window, pointer, TRUE);
+ priv->grab_pointer = pointer;
+ priv->grab_keyboard = keyboard;
+}
+
+static void
+gtk_combo_box_real_popup (GtkComboBox *combo_box)
+{
+ GdkDevice *device;
+
+ device = gtk_get_current_event_device ();
+
+ if (!device)
+ {
+ GdkDeviceManager *device_manager;
+ GdkDisplay *display;
+ GList *devices;
+
+ display = gtk_widget_get_display (GTK_WIDGET (combo_box));
+ device_manager = gdk_display_get_device_manager (display);
+
+ /* No device was set, pick the first master device */
+ devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+ device = devices->data;
+ g_list_free (devices);
+ }
+
+ gtk_combo_box_popup_for_device (combo_box, device);
}
static gboolean
@@ -2014,10 +2081,13 @@ gtk_combo_box_popdown (GtkComboBox *combo_box)
if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
return;
- gtk_grab_remove (priv->popup_window);
+ gtk_device_grab_remove (priv->popup_window, priv->grab_pointer);
gtk_widget_hide_all (priv->popup_window);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
FALSE);
+
+ priv->grab_pointer = NULL;
+ priv->grab_keyboard = NULL;
}
static gint
@@ -3896,7 +3966,7 @@ gtk_combo_box_list_button_pressed (GtkWidget *widget,
!gtk_widget_has_focus (priv->button))
gtk_widget_grab_focus (priv->button);
- gtk_combo_box_popup (combo_box);
+ gtk_combo_box_popup_for_device (combo_box, event->device);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), TRUE);
@@ -4092,7 +4162,9 @@ gtk_combo_box_list_scroll_timeout (GtkComboBox *combo_box)
if (priv->auto_scroll)
{
- gdk_window_get_pointer (priv->tree_view->window, &x, &y, NULL);
+ gdk_window_get_device_position (priv->tree_view->window,
+ priv->grab_pointer,
+ &x, &y, NULL);
gtk_combo_box_list_auto_scroll (combo_box, x, y);
}
diff --git a/gtk/gtkcombobox.h b/gtk/gtkcombobox.h
index cbe40f9ca5..f5141a7ded 100644
--- a/gtk/gtkcombobox.h
+++ b/gtk/gtkcombobox.h
@@ -133,6 +133,8 @@ gchar *gtk_combo_box_get_active_text (GtkComboBox *combo_box);
/* programmatic control */
void gtk_combo_box_popup (GtkComboBox *combo_box);
+void gtk_combo_box_popup_for_device (GtkComboBox *combo_box,
+ GdkDevice *device);
void gtk_combo_box_popdown (GtkComboBox *combo_box);
AtkObject* gtk_combo_box_get_popup_accessible (GtkComboBox *combo_box);
diff --git a/gtk/gtkdnd.c b/gtk/gtkdnd.c
index 0c73e18e45..03f04363d5 100644
--- a/gtk/gtkdnd.c
+++ b/gtk/gtkdnd.c
@@ -398,8 +398,12 @@ gtk_drag_get_ipc_widget (GtkWidget *widget)
return result;
}
-
-#ifdef GDK_WINDOWING_X11
+/* FIXME: modifying the XEvent window as in root_key_filter() isn't
+ * going to work with XGE/XI2, since the actual event to handle would
+ * be allocated/freed before GDK gets to translate the event.
+ * Active grabs on the keyboard are used instead at the moment...
+ */
+#if defined (GDK_WINDOWING_X11) && !defined (XINPUT_2)
/*
* We want to handle a handful of keys during DND, e.g. Escape to abort.
@@ -418,7 +422,7 @@ root_key_filter (GdkXEvent *xevent,
GdkEvent *event,
gpointer data)
{
- XEvent *ev = (XEvent *)xevent;
+ XEvent *ev = (XEvent *) xevent;
if ((ev->type == KeyPress || ev->type == KeyRelease) &&
ev->xkey.root == ev->xkey.window)
@@ -458,6 +462,7 @@ static GrabKey grab_keys[] = {
static void
grab_dnd_keys (GtkWidget *widget,
+ GdkDevice *device,
guint32 time)
{
guint i;
@@ -488,6 +493,7 @@ grab_dnd_keys (GtkWidget *widget,
static void
ungrab_dnd_keys (GtkWidget *widget,
+ GdkDevice *device,
guint32 time)
{
guint i;
@@ -513,23 +519,28 @@ ungrab_dnd_keys (GtkWidget *widget,
gdk_error_trap_pop ();
}
-#else
+#else /* GDK_WINDOWING_X11 && !XINPUT_2 */
static void
grab_dnd_keys (GtkWidget *widget,
+ GdkDevice *device,
guint32 time)
{
- gdk_keyboard_grab (widget->window, FALSE, time);
+ gdk_device_grab (device, widget->window,
+ GDK_OWNERSHIP_APPLICATION, FALSE,
+ GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+ NULL, time);
}
static void
ungrab_dnd_keys (GtkWidget *widget,
+ GdkDevice *device,
guint32 time)
{
- gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), time);
+ gdk_device_ungrab (device, time);
}
-#endif
+#endif /* GDK_WINDOWING_X11 */
/***************************************************************
@@ -545,9 +556,20 @@ gtk_drag_release_ipc_widget (GtkWidget *widget)
{
GtkWindow *window = GTK_WINDOW (widget);
GdkScreen *screen = gtk_widget_get_screen (widget);
+ GdkDragContext *context = g_object_get_data (G_OBJECT (widget), "drag-context");
GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
"gtk-dnd-ipc-widgets");
- ungrab_dnd_keys (widget, GDK_CURRENT_TIME);
+ GdkDevice *pointer, *keyboard;
+
+ if (context)
+ {
+ pointer = gdk_drag_context_get_device (context);
+ keyboard = gdk_device_get_associated_device (pointer);
+
+ if (keyboard)
+ ungrab_dnd_keys (widget, keyboard, GDK_CURRENT_TIME);
+ }
+
if (window->group)
gtk_window_group_remove_window (window->group, window);
drag_widgets = g_slist_prepend (drag_widgets, widget);
@@ -940,11 +962,13 @@ gtk_drag_update_cursor (GtkDragSourceInfo *info)
if (cursor != info->cursor)
{
- gdk_pointer_grab (info->ipc_widget->window, FALSE,
- GDK_POINTER_MOTION_MASK |
- GDK_BUTTON_RELEASE_MASK,
- NULL,
- cursor, info->grab_time);
+ GdkDevice *pointer;
+
+ pointer = gdk_drag_context_get_device (info->context);
+ gdk_device_grab (pointer, info->ipc_widget->window,
+ GDK_OWNERSHIP_APPLICATION, FALSE,
+ GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
+ cursor, info->grab_time);
info->cursor = cursor;
}
}
@@ -2379,7 +2403,9 @@ gtk_drag_begin_internal (GtkWidget *widget,
GdkDragContext *context;
GtkWidget *ipc_widget;
GdkCursor *cursor;
-
+ GdkDevice *pointer, *keyboard;
+
+ pointer = keyboard = NULL;
ipc_widget = gtk_drag_get_ipc_widget (widget);
gtk_drag_get_event_actions (event, button, actions,
@@ -2390,24 +2416,45 @@ gtk_drag_begin_internal (GtkWidget *widget,
NULL);
if (event)
- time = gdk_event_get_time (event);
+ {
+ time = gdk_event_get_time (event);
+ pointer = gdk_event_get_device (event);
+
+ if (pointer->source == GDK_SOURCE_KEYBOARD)
+ {
+ keyboard = pointer;
+ pointer = gdk_device_get_associated_device (keyboard);
+ }
+ else
+ keyboard = gdk_device_get_associated_device (pointer);
+ }
+ else
+ {
+ pointer = gdk_display_get_core_pointer (gtk_widget_get_display (widget));
+ keyboard = gdk_device_get_associated_device (pointer);
+ }
- if (gdk_pointer_grab (ipc_widget->window, FALSE,
- GDK_POINTER_MOTION_MASK |
- GDK_BUTTON_RELEASE_MASK, NULL,
- cursor, time) != GDK_GRAB_SUCCESS)
+ if (!pointer)
+ return NULL;
+
+ if (gdk_device_grab (pointer, ipc_widget->window,
+ GDK_OWNERSHIP_APPLICATION, FALSE,
+ GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_RELEASE_MASK,
+ cursor, time) != GDK_GRAB_SUCCESS)
{
gtk_drag_release_ipc_widget (ipc_widget);
return NULL;
}
- grab_dnd_keys (ipc_widget, time);
+ if (keyboard)
+ grab_dnd_keys (ipc_widget, keyboard, time);
/* We use a GTK grab here to override any grabs that the widget
* we are dragging from might have held
*/
- gtk_grab_add (ipc_widget);
-
+ gtk_device_grab_add (ipc_widget, pointer, FALSE);
+
tmp_list = g_list_last (target_list->list);
while (tmp_list)
{
@@ -2420,6 +2467,7 @@ gtk_drag_begin_internal (GtkWidget *widget,
source_widgets = g_slist_prepend (source_widgets, ipc_widget);
context = gdk_drag_begin (ipc_widget->window, targets);
+ gdk_drag_context_set_device (context, pointer);
g_list_free (targets);
info = gtk_drag_get_source_info (context, TRUE);
@@ -2451,10 +2499,10 @@ gtk_drag_begin_internal (GtkWidget *widget,
info->cur_x = event->motion.x_root;
info->cur_y = event->motion.y_root;
}
- else
+ else
{
- gdk_display_get_pointer (gtk_widget_get_display (widget),
- &info->cur_screen, &info->cur_x, &info->cur_y, NULL);
+ gdk_display_get_device_state (gtk_widget_get_display (widget), pointer,
+ &info->cur_screen, &info->cur_x, &info->cur_y, NULL);
}
g_signal_emit_by_name (widget, "drag-begin", info->context);
@@ -2510,11 +2558,11 @@ gtk_drag_begin_internal (GtkWidget *widget,
if (cursor != info->cursor)
{
- gdk_pointer_grab (widget->window, FALSE,
- GDK_POINTER_MOTION_MASK |
- GDK_BUTTON_RELEASE_MASK,
- NULL,
- cursor, time);
+ gdk_device_grab (pointer, widget->window,
+ GDK_OWNERSHIP_APPLICATION, FALSE,
+ GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_RELEASE_MASK,
+ cursor, time);
info->cursor = cursor;
}
}
@@ -3468,11 +3516,13 @@ _gtk_drag_source_handle_event (GtkWidget *widget,
info);
if (info->cursor != cursor)
{
- gdk_pointer_grab (widget->window, FALSE,
- GDK_POINTER_MOTION_MASK |
- GDK_BUTTON_RELEASE_MASK,
- NULL,
- cursor, info->grab_time);
+ GdkDevice *pointer;
+
+ pointer = gdk_drag_context_get_device (context);
+ gdk_device_grab (pointer, widget->window,
+ GDK_OWNERSHIP_APPLICATION, FALSE,
+ GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
+ cursor, info->grab_time);
info->cursor = cursor;
}
@@ -4062,7 +4112,10 @@ gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
{
GdkEvent *send_event;
GtkWidget *source_widget = info->widget;
- GdkDisplay *display = gtk_widget_get_display (source_widget);
+ GdkDevice *pointer, *keyboard;
+
+ pointer = gdk_drag_context_get_device (info->context);
+ keyboard = gdk_device_get_associated_device (pointer);
if (info->update_idle)
{
@@ -4094,9 +4147,9 @@ gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
gtk_drag_key_cb,
info);
- gdk_display_pointer_ungrab (display, time);
- ungrab_dnd_keys (info->ipc_widget, time);
- gtk_grab_remove (info->ipc_widget);
+ gdk_device_ungrab (pointer, time);
+ ungrab_dnd_keys (info->ipc_widget, keyboard, time);
+ gtk_device_grab_remove (info->ipc_widget, pointer);
/* Send on a release pair to the original
* widget to convince it to release its grab. We need to
@@ -4114,7 +4167,7 @@ gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
send_event->button.axes = NULL;
send_event->button.state = 0;
send_event->button.button = info->button;
- send_event->button.device = gdk_display_get_core_pointer (display);
+ send_event->button.device = pointer;
send_event->button.x_root = 0;
send_event->button.y_root = 0;
@@ -4160,15 +4213,16 @@ gtk_drag_motion_cb (GtkWidget *widget,
if (event->is_hint)
{
GdkDisplay *display = gtk_widget_get_display (widget);
-
- gdk_display_get_pointer (display, &screen, &x_root, &y_root, NULL);
+
+ gdk_display_get_device_state (display, event->device,
+ &screen, &x_root, &y_root, NULL);
event->x_root = x_root;
event->y_root = y_root;
}
else
screen = gdk_event_get_screen ((GdkEvent *)event);
- gtk_drag_update (info, screen, event->x_root, event->y_root, (GdkEvent *)event);
+ gtk_drag_update (info, screen, event->x_root, event->y_root, (GdkEvent *) event);
return TRUE;
}
@@ -4192,10 +4246,12 @@ gtk_drag_key_cb (GtkWidget *widget,
GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
GdkModifierType state;
GdkWindow *root_window;
+ GdkDevice *pointer;
gint dx, dy;
dx = dy = 0;
state = event->state & gtk_accelerator_get_default_mod_mask ();
+ pointer = gdk_device_get_associated_device (gdk_event_get_device ((GdkEvent *) event));
if (event->type == GDK_KEY_PRESS)
{
@@ -4244,16 +4300,16 @@ gtk_drag_key_cb (GtkWidget *widget,
* that would be overkill.
*/
root_window = gtk_widget_get_root_window (widget);
- gdk_window_get_pointer (root_window, NULL, NULL, &state);
+ gdk_window_get_device_position (root_window, pointer, NULL, NULL, &state);
event->state = state;
if (dx != 0 || dy != 0)
{
info->cur_x += dx;
info->cur_y += dy;
- gdk_display_warp_pointer (gtk_widget_get_display (widget),
- gtk_widget_get_screen (widget),
- info->cur_x, info->cur_y);
+ gdk_display_warp_device (gtk_widget_get_display (widget), pointer,
+ gtk_widget_get_screen (widget),
+ info->cur_x, info->cur_y);
}
gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, (GdkEvent *)event);
@@ -4287,8 +4343,11 @@ gtk_drag_grab_notify_cb (GtkWidget *widget,
gpointer data)
{
GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
+ GdkDevice *pointer;
+
+ pointer = gdk_drag_context_get_device (info->context);
- if (!was_grabbed)
+ if (gtk_widget_device_is_shadowed (widget, pointer))
{
/* We have to block callbacks to avoid recursion here, because
gtk_drag_cancel calls gtk_grab_remove (via gtk_drag_end) */
diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c
index 963cf33f53..3c7aa25f94 100644
--- a/gtk/gtkentry.c
+++ b/gtk/gtkentry.c
@@ -139,6 +139,8 @@ struct _GtkEntryPrivate
gint start_y;
gchar *im_module;
+
+ GdkDevice *completion_device;
};
typedef struct _GtkEntryPasswordHint GtkEntryPasswordHint;
@@ -9157,6 +9159,7 @@ static gint
gtk_entry_completion_timeout (gpointer data)
{
GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
+ GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (completion->priv->entry);
completion->priv->completion_timeout = 0;
@@ -9186,7 +9189,7 @@ gtk_entry_completion_timeout (gpointer data)
if (gtk_widget_get_visible (completion->priv->popup_window))
_gtk_entry_completion_resize_popup (completion);
else
- _gtk_entry_completion_popup (completion);
+ _gtk_entry_completion_popup (completion, priv->completion_device);
}
else
_gtk_entry_completion_popdown (completion);
@@ -9491,7 +9494,9 @@ static void
gtk_entry_completion_changed (GtkWidget *entry,
gpointer user_data)
{
+ GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry);
GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
+ GdkDevice *device;
/* (re)install completion timeout */
if (completion->priv->completion_timeout)
@@ -9509,6 +9514,14 @@ gtk_entry_completion_changed (GtkWidget *entry,
return;
}
+ device = gtk_get_current_event_device ();
+
+ if (device && device->source == GDK_SOURCE_KEYBOARD)
+ device = gdk_device_get_associated_device (device);
+
+ if (device)
+ priv->completion_device = device;
+
completion->priv->completion_timeout =
gdk_threads_add_timeout (COMPLETION_TIMEOUT,
gtk_entry_completion_timeout,
diff --git a/gtk/gtkentrycompletion.c b/gtk/gtkentrycompletion.c
index 8237387391..6225b41dda 100644
--- a/gtk/gtkentrycompletion.c
+++ b/gtk/gtkentrycompletion.c
@@ -1473,7 +1473,8 @@ _gtk_entry_completion_resize_popup (GtkEntryCompletion *completion)
}
void
-_gtk_entry_completion_popup (GtkEntryCompletion *completion)
+_gtk_entry_completion_popup (GtkEntryCompletion *completion,
+ GdkDevice *device)
{
GtkTreeViewColumn *column;
GList *renderers;
@@ -1488,6 +1489,9 @@ _gtk_entry_completion_popup (GtkEntryCompletion *completion)
if (!gtk_widget_has_focus (completion->priv->entry))
return;
+ if (completion->priv->grab_device)
+ return;
+
completion->priv->ignore_enter = TRUE;
column = gtk_tree_view_get_column (GTK_TREE_VIEW (completion->priv->action_view), 0);
@@ -1520,13 +1524,16 @@ _gtk_entry_completion_popup (GtkEntryCompletion *completion)
gtk_widget_get_screen (completion->priv->entry));
gtk_widget_show (completion->priv->popup_window);
-
- gtk_grab_add (completion->priv->popup_window);
- gdk_pointer_grab (completion->priv->popup_window->window, TRUE,
- GDK_BUTTON_PRESS_MASK |
- GDK_BUTTON_RELEASE_MASK |
- GDK_POINTER_MOTION_MASK,
- NULL, NULL, GDK_CURRENT_TIME);
+
+ gtk_device_grab_add (completion->priv->popup_window, device, TRUE);
+ gdk_device_grab (device, completion->priv->popup_window->window,
+ GDK_OWNERSHIP_WINDOW, TRUE,
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK,
+ NULL, GDK_CURRENT_TIME);
+
+ completion->priv->grab_device = device;
}
void
@@ -1536,9 +1543,14 @@ _gtk_entry_completion_popdown (GtkEntryCompletion *completion)
return;
completion->priv->ignore_enter = FALSE;
-
- gdk_pointer_ungrab (GDK_CURRENT_TIME);
- gtk_grab_remove (completion->priv->popup_window);
+
+ if (completion->priv->grab_device)
+ {
+ gdk_device_ungrab (completion->priv->grab_device, GDK_CURRENT_TIME);
+ gtk_device_grab_remove (completion->priv->popup_window,
+ completion->priv->grab_device);
+ completion->priv->grab_device = NULL;
+ }
gtk_widget_hide (completion->priv->popup_window);
}
diff --git a/gtk/gtkentryprivate.h b/gtk/gtkentryprivate.h
index a7678002b8..baf0b9fe1f 100644
--- a/gtk/gtkentryprivate.h
+++ b/gtk/gtkentryprivate.h
@@ -69,10 +69,13 @@ struct _GtkEntryCompletionPrivate
gchar *completion_prefix;
GSource *check_completion_idle;
+
+ GdkDevice *grab_device;
};
gboolean _gtk_entry_completion_resize_popup (GtkEntryCompletion *completion);
-void _gtk_entry_completion_popup (GtkEntryCompletion *completion);
+void _gtk_entry_completion_popup (GtkEntryCompletion *completion,
+ GdkDevice *device);
void _gtk_entry_completion_popdown (GtkEntryCompletion *completion);
void _gtk_entry_get_borders (GtkEntry *entry,
diff --git a/gtk/gtkhandlebox.c b/gtk/gtkhandlebox.c
index 5ff6050a9f..119ba107ed 100644
--- a/gtk/gtkhandlebox.c
+++ b/gtk/gtkhandlebox.c
@@ -42,6 +42,7 @@ struct _GtkHandleBoxPrivate
{
gint orig_x;
gint orig_y;
+ GdkDevice *grab_device;
};
enum {
@@ -1114,22 +1115,25 @@ gtk_handle_box_button_press (GtkWidget *widget,
hb->attach_allocation.height = 0;
}
hb->in_drag = TRUE;
+ private->grab_device = event->device;
fleur = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
GDK_FLEUR);
- if (gdk_pointer_grab (invisible->window,
- FALSE,
- (GDK_BUTTON1_MOTION_MASK |
- GDK_POINTER_MOTION_HINT_MASK |
- GDK_BUTTON_RELEASE_MASK),
- NULL,
- fleur,
- event->time) != 0)
+ if (gdk_device_grab (event->device,
+ invisible->window,
+ GDK_OWNERSHIP_WINDOW,
+ FALSE,
+ (GDK_BUTTON1_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK |
+ GDK_BUTTON_RELEASE_MASK),
+ fleur,
+ event->time) != GDK_GRAB_SUCCESS)
{
hb->in_drag = FALSE;
+ private->grab_device = NULL;
}
else
{
- gtk_grab_add (invisible);
+ gtk_device_grab_add (invisible, private->grab_device, TRUE);
g_signal_connect (invisible, "event",
G_CALLBACK (gtk_handle_box_grab_event), hb);
}
@@ -1169,9 +1173,10 @@ gtk_handle_box_motion (GtkWidget *widget,
new_x = 0;
new_y = 0;
screen = gtk_widget_get_screen (widget);
- gdk_display_get_pointer (gdk_screen_get_display (screen),
- &pointer_screen,
- &new_x, &new_y, NULL);
+ gdk_display_get_device_state (gdk_screen_get_display (screen),
+ event->device,
+ &pointer_screen,
+ &new_x, &new_y, NULL);
if (pointer_screen != screen)
{
GtkHandleBoxPrivate *private = gtk_handle_box_get_private (hb);
@@ -1418,15 +1423,18 @@ static void
gtk_handle_box_end_drag (GtkHandleBox *hb,
guint32 time)
{
+ GtkHandleBoxPrivate *private = gtk_handle_box_get_private (hb);
GtkWidget *invisible = gtk_handle_box_get_invisible ();
-
+
hb->in_drag = FALSE;
- gtk_grab_remove (invisible);
- gdk_pointer_ungrab (time);
+ gtk_device_grab_remove (invisible, private->grab_device);
+ gdk_device_ungrab (private->grab_device, time);
g_signal_handlers_disconnect_by_func (invisible,
G_CALLBACK (gtk_handle_box_grab_event),
hb);
+
+ private->grab_device = NULL;
}
#define __GTK_HANDLE_BOX_C__
diff --git a/gtk/gtkiconview.c b/gtk/gtkiconview.c
index 4f07208930..0942ef5593 100644
--- a/gtk/gtkiconview.c
+++ b/gtk/gtkiconview.c
@@ -124,6 +124,7 @@ struct _GtkIconViewPrivate
gboolean doing_rubberband;
gint rubberband_x1, rubberband_y1;
gint rubberband_x2, rubberband_y2;
+ GdkDevice *rubberband_device;
guint scroll_timeout_id;
gint scroll_value_diff;
@@ -308,6 +309,7 @@ static void gtk_icon_view_set_cursor_item (GtkIco
GtkIconViewItem *item,
gint cursor_cell);
static void gtk_icon_view_start_rubberbanding (GtkIconView *icon_view,
+ GdkDevice *device,
gint x,
gint y);
static void gtk_icon_view_stop_rubberbanding (GtkIconView *icon_view);
@@ -2217,7 +2219,7 @@ gtk_icon_view_button_press (GtkWidget *widget,
}
if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE)
- gtk_icon_view_start_rubberbanding (icon_view, event->x, event->y);
+ gtk_icon_view_start_rubberbanding (icon_view, event->device, event->x, event->y);
}
/* don't draw keyboard focus around an clicked-on item */
@@ -2309,7 +2311,9 @@ gtk_icon_view_update_rubberband (gpointer data)
icon_view = GTK_ICON_VIEW (data);
- gdk_window_get_pointer (icon_view->priv->bin_window, &x, &y, NULL);
+ gdk_window_get_device_position (icon_view->priv->bin_window,
+ icon_view->priv->rubberband_device,
+ &x, &y, NULL);
x = MAX (x, 0);
y = MAX (y, 0);
@@ -2360,12 +2364,14 @@ gtk_icon_view_update_rubberband (gpointer data)
static void
gtk_icon_view_start_rubberbanding (GtkIconView *icon_view,
+ GdkDevice *device,
gint x,
gint y)
{
GList *items;
- g_assert (!icon_view->priv->doing_rubberband);
+ if (icon_view->priv->rubberband_device)
+ return;
for (items = icon_view->priv->items; items; items = items->next)
{
@@ -2380,8 +2386,9 @@ gtk_icon_view_start_rubberbanding (GtkIconView *icon_view,
icon_view->priv->rubberband_y2 = y;
icon_view->priv->doing_rubberband = TRUE;
+ icon_view->priv->rubberband_device = device;
- gtk_grab_add (GTK_WIDGET (icon_view));
+ gtk_device_grab_add (GTK_WIDGET (icon_view), device, TRUE);
}
static void
@@ -2390,10 +2397,12 @@ gtk_icon_view_stop_rubberbanding (GtkIconView *icon_view)
if (!icon_view->priv->doing_rubberband)
return;
+ gtk_device_grab_remove (GTK_WIDGET (icon_view),
+ icon_view->priv->rubberband_device);
+
icon_view->priv->doing_rubberband = FALSE;
+ icon_view->priv->rubberband_device = NULL;
- gtk_grab_remove (GTK_WIDGET (icon_view));
-
gtk_widget_queue_draw (GTK_WIDGET (icon_view));
}
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index 611cebbf80..a027f857e9 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -4786,7 +4786,6 @@ gtk_label_motion (GtkWidget *widget,
GtkLabel *label = GTK_LABEL (widget);
GtkLabelSelectionInfo *info = label->select_info;
gint index;
- gint x, y;
if (info == NULL)
return FALSE;
@@ -4799,8 +4798,7 @@ gtk_label_motion (GtkWidget *widget,
if (info->selection_anchor == info->selection_end)
{
- gdk_window_get_pointer (event->window, &x, &y, NULL);
- if (get_layout_index (label, x, y, &index))
+ if (get_layout_index (label, event->x, event->y, &index))
{
for (l = info->links; l != NULL; l = l->next)
{
@@ -4842,8 +4840,6 @@ gtk_label_motion (GtkWidget *widget,
if ((event->state & GDK_BUTTON1_MASK) == 0)
return FALSE;
- gdk_window_get_pointer (info->window, &x, &y, NULL);
-
if (info->in_drag)
{
if (gtk_drag_check_threshold (widget,
@@ -4868,6 +4864,9 @@ gtk_label_motion (GtkWidget *widget,
}
else
{
+ gint x, y;
+
+ gdk_window_get_device_position (info->window, event->device, &x, &y, NULL);
get_layout_index (label, x, y, &index);
if (info->select_words)
diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
index ed88d3898d..ea54a4b4e1 100644
--- a/gtk/gtkmain.c
+++ b/gtk/gtkmain.c
@@ -1316,12 +1316,10 @@ gtk_main_iteration_do (gboolean blocking)
/* private libgtk to libgdk interfaces
*/
-gboolean gdk_pointer_grab_info_libgtk_only (GdkDisplay *display,
- GdkWindow **grab_window,
- gboolean *owner_events);
-gboolean gdk_keyboard_grab_info_libgtk_only (GdkDisplay *display,
- GdkWindow **grab_window,
- gboolean *owner_events);
+gboolean gdk_device_grab_info_libgtk_only (GdkDisplay *display,
+ GdkDevice *device,
+ GdkWindow **grab_window,
+ gboolean *owner_events);
static void
rewrite_events_translate (GdkWindow *old_window,
@@ -1396,6 +1394,7 @@ rewrite_event_for_grabs (GdkEvent *event)
gpointer grab_widget_ptr;
gboolean owner_events;
GdkDisplay *display;
+ GdkDevice *device;
switch (event->type)
{
@@ -1407,20 +1406,15 @@ rewrite_event_for_grabs (GdkEvent *event)
case GDK_MOTION_NOTIFY:
case GDK_PROXIMITY_IN:
case GDK_PROXIMITY_OUT:
- display = gdk_drawable_get_display (event->proximity.window);
- if (!gdk_pointer_grab_info_libgtk_only (display, &grab_window, &owner_events) ||
- !owner_events)
- return NULL;
- break;
-
case GDK_KEY_PRESS:
case GDK_KEY_RELEASE:
- display = gdk_drawable_get_display (event->key.window);
- if (!gdk_keyboard_grab_info_libgtk_only (display, &grab_window, &owner_events) ||
+ display = gdk_drawable_get_display (event->any.window);
+ device = gdk_event_get_device (event);
+
+ if (!gdk_device_grab_info_libgtk_only (display, device, &grab_window, &owner_events) ||
!owner_events)
- return NULL;
+ return NULL;
break;
-
default:
return NULL;
}
@@ -1440,9 +1434,10 @@ void
gtk_main_do_event (GdkEvent *event)
{
GtkWidget *event_widget;
- GtkWidget *grab_widget;
+ GtkWidget *grab_widget = NULL;
GtkWindowGroup *window_group;
GdkEvent *rewritten_event = NULL;
+ GdkDevice *device;
GList *tmp_list;
if (event->type == GDK_SETTING)
@@ -1489,13 +1484,9 @@ gtk_main_do_event (GdkEvent *event)
event = rewritten_event;
event_widget = gtk_get_event_widget (event);
}
-
- window_group = gtk_main_get_window_group (event_widget);
- /* Push the event onto a stack of current events for
- * gtk_current_event_get().
- */
- current_events = g_list_prepend (current_events, event);
+ window_group = gtk_main_get_window_group (event_widget);
+ device = gdk_event_get_device (event);
/* If there is a grab in effect...
*/
@@ -1511,11 +1502,34 @@ gtk_main_do_event (GdkEvent *event)
gtk_widget_is_ancestor (event_widget, grab_widget))
grab_widget = event_widget;
}
- else
+ else if (device)
{
- grab_widget = event_widget;
+ grab_widget = gtk_window_group_get_current_device_grab (window_group, device);
+
+ if (grab_widget &&
+ gtk_widget_get_sensitive (event_widget) &&
+ gtk_widget_is_ancestor (event_widget, grab_widget))
+ grab_widget = event_widget;
+ }
+
+ if (!grab_widget)
+ grab_widget = event_widget;
+
+ /* If the widget receiving events is actually blocked by another device GTK+ grab */
+ if (device &&
+ _gtk_window_group_widget_is_blocked_for_device (window_group, grab_widget, device))
+ {
+ if (rewritten_event)
+ gdk_event_free (rewritten_event);
+
+ return;
}
+ /* Push the event onto a stack of current events for
+ * gtk_current_event_get().
+ */
+ current_events = g_list_prepend (current_events, event);
+
/* Not all events get sent to the grabbing widget.
* The delete, destroy, expose, focus change and resize
* events still get sent to the event widget because
@@ -1636,14 +1650,17 @@ gtk_main_do_event (GdkEvent *event)
break;
case GDK_ENTER_NOTIFY:
- GTK_PRIVATE_SET_FLAG (event_widget, GTK_HAS_POINTER);
- _gtk_widget_set_pointer_window (event_widget, event->any.window);
+ _gtk_widget_set_device_window (event_widget,
+ gdk_event_get_device (event),
+ event->any.window);
if (gtk_widget_is_sensitive (grab_widget))
gtk_widget_event (grab_widget, event);
break;
case GDK_LEAVE_NOTIFY:
- GTK_PRIVATE_UNSET_FLAG (event_widget, GTK_HAS_POINTER);
+ _gtk_widget_set_device_window (event_widget,
+ gdk_event_get_device (event),
+ NULL);
if (gtk_widget_is_sensitive (grab_widget))
gtk_widget_event (grab_widget, event);
break;
@@ -1718,16 +1735,73 @@ typedef struct
gboolean was_grabbed;
gboolean is_grabbed;
gboolean from_grab;
+ GList *notified_windows;
+ GdkDevice *device;
} GrabNotifyInfo;
static void
+synth_crossing_for_grab_notify (GtkWidget *from,
+ GtkWidget *to,
+ GrabNotifyInfo *info,
+ GList *devices,
+ GdkCrossingMode mode)
+{
+ while (devices)
+ {
+ GdkDevice *device = devices->data;
+ GdkWindow *from_window, *to_window;
+
+ /* Do not propagate events more than once to
+ * the same windows if non-multidevice aware.
+ */
+ if (!from)
+ from_window = NULL;
+ else
+ {
+ from_window = _gtk_widget_get_device_window (from, device);
+
+ if (from_window &&
+ !gdk_window_get_support_multidevice (from_window) &&
+ g_list_find (info->notified_windows, from_window))
+ from_window = NULL;
+ }
+
+ if (!to)
+ to_window = NULL;
+ else
+ {
+ to_window = _gtk_widget_get_device_window (to, device);
+
+ if (to_window &&
+ !gdk_window_get_support_multidevice (to_window) &&
+ g_list_find (info->notified_windows, to_window))
+ to_window = NULL;
+ }
+
+ if (from_window || to_window)
+ {
+ _gtk_widget_synthesize_crossing ((from_window) ? from : NULL,
+ (to_window) ? to : NULL,
+ device, mode);
+
+ if (from_window)
+ info->notified_windows = g_list_prepend (info->notified_windows, from_window);
+
+ if (to_window)
+ info->notified_windows = g_list_prepend (info->notified_windows, to_window);
+ }
+
+ devices = devices->next;
+ }
+}
+
+static void
gtk_grab_notify_foreach (GtkWidget *child,
gpointer data)
-
{
GrabNotifyInfo *info = data;
-
gboolean was_grabbed, is_grabbed, was_shadowed, is_shadowed;
+ GList *devices;
was_grabbed = info->was_grabbed;
is_grabbed = info->is_grabbed;
@@ -1742,42 +1816,55 @@ gtk_grab_notify_foreach (GtkWidget *child,
if ((was_shadowed || is_shadowed) && GTK_IS_CONTAINER (child))
gtk_container_forall (GTK_CONTAINER (child), gtk_grab_notify_foreach, info);
-
+
+ if (info->device &&
+ _gtk_widget_get_device_window (child, info->device))
+ {
+ /* Device specified and is on widget */
+ devices = g_list_prepend (NULL, info->device);
+ }
+ else
+ devices = _gtk_widget_list_devices (child);
+
if (is_shadowed)
{
GTK_PRIVATE_SET_FLAG (child, GTK_SHADOWED);
- if (!was_shadowed && GTK_WIDGET_HAS_POINTER (child)
- && gtk_widget_is_sensitive (child))
- _gtk_widget_synthesize_crossing (child, info->new_grab_widget,
- GDK_CROSSING_GTK_GRAB);
+ if (!was_shadowed && devices &&
+ gtk_widget_is_sensitive (child))
+ synth_crossing_for_grab_notify (child, info->new_grab_widget,
+ info, devices,
+ GDK_CROSSING_GTK_GRAB);
}
else
{
GTK_PRIVATE_UNSET_FLAG (child, GTK_SHADOWED);
- if (was_shadowed && GTK_WIDGET_HAS_POINTER (child)
- && gtk_widget_is_sensitive (child))
- _gtk_widget_synthesize_crossing (info->old_grab_widget, child,
- info->from_grab ? GDK_CROSSING_GTK_GRAB
- : GDK_CROSSING_GTK_UNGRAB);
+ if (was_shadowed && devices &&
+ gtk_widget_is_sensitive (child))
+ synth_crossing_for_grab_notify (info->old_grab_widget, child,
+ info, devices,
+ info->from_grab ? GDK_CROSSING_GTK_GRAB :
+ GDK_CROSSING_GTK_UNGRAB);
}
if (was_shadowed != is_shadowed)
_gtk_widget_grab_notify (child, was_shadowed);
-
+
g_object_unref (child);
-
+ g_list_free (devices);
+
info->was_grabbed = was_grabbed;
info->is_grabbed = is_grabbed;
}
static void
gtk_grab_notify (GtkWindowGroup *group,
+ GdkDevice *device,
GtkWidget *old_grab_widget,
GtkWidget *new_grab_widget,
gboolean from_grab)
{
GList *toplevels;
- GrabNotifyInfo info;
+ GrabNotifyInfo info = { 0 };
if (old_grab_widget == new_grab_widget)
return;
@@ -1785,12 +1872,13 @@ gtk_grab_notify (GtkWindowGroup *group,
info.old_grab_widget = old_grab_widget;
info.new_grab_widget = new_grab_widget;
info.from_grab = from_grab;
+ info.device = device;
g_object_ref (group);
toplevels = gtk_window_list_toplevels ();
g_list_foreach (toplevels, (GFunc)g_object_ref, NULL);
-
+
while (toplevels)
{
GtkWindow *toplevel = toplevels->data;
@@ -1804,6 +1892,7 @@ gtk_grab_notify (GtkWindowGroup *group,
g_object_unref (toplevel);
}
+ g_list_free (info.notified_windows);
g_object_unref (group);
}
@@ -1829,7 +1918,7 @@ gtk_grab_add (GtkWidget *widget)
g_object_ref (widget);
group->grabs = g_slist_prepend (group->grabs, widget);
- gtk_grab_notify (group, old_grab_widget, widget, TRUE);
+ gtk_grab_notify (group, NULL, old_grab_widget, widget, TRUE);
}
}
@@ -1865,12 +1954,72 @@ gtk_grab_remove (GtkWidget *widget)
else
new_grab_widget = NULL;
- gtk_grab_notify (group, widget, new_grab_widget, FALSE);
+ gtk_grab_notify (group, NULL, widget, new_grab_widget, FALSE);
g_object_unref (widget);
}
}
+/**
+ * gtk_device_grab_add:
+ * @widget: a #GtkWidget
+ * @device: a #GtkDevice to grab on.
+ * @block_others: %TRUE to prevent other devices to interact with @widget.
+ *
+ * Adds a GTK+ grab on @device, so all the events on @device and its
+ * associated pointer or keyboard (if any) are delivered to @widget.
+ * If the @block_others parameter is %TRUE, any other devices will be
+ * unable to interact with @widget during the grab.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_device_grab_add (GtkWidget *widget,
+ GdkDevice *device,
+ gboolean block_others)
+{
+ GtkWindowGroup *group;
+ GtkWidget *old_grab_widget;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (GDK_IS_DEVICE (device));
+
+ group = gtk_main_get_window_group (widget);
+ old_grab_widget = gtk_window_group_get_current_device_grab (group, device);
+
+ if (old_grab_widget != widget)
+ _gtk_window_group_add_device_grab (group, widget, device, block_others);
+
+ gtk_grab_notify (group, device, old_grab_widget, widget, TRUE);
+}
+
+/**
+ * gtk_device_grab_remove:
+ * @widget: a #GtkWidget
+ * @device: a #GdkDevice
+ *
+ * Removes a device grab from the given widget. You have to pair calls
+ * to gtk_device_grab_add() and gtk_device_grab_remove().
+ *
+ * Since: 3.0
+ **/
+void
+gtk_device_grab_remove (GtkWidget *widget,
+ GdkDevice *device)
+{
+ GtkWindowGroup *group;
+ GtkWidget *new_grab_widget;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (GDK_IS_DEVICE (device));
+
+ group = gtk_main_get_window_group (widget);
+ _gtk_window_group_remove_device_grab (group, widget, device);
+ new_grab_widget = gtk_window_group_get_current_device_grab (group, device);
+
+ gtk_grab_notify (group, device, widget, new_grab_widget, FALSE);
+}
+
void
gtk_init_add (GtkFunction function,
gpointer data)
@@ -2125,6 +2274,23 @@ gtk_get_current_event_state (GdkModifierType *state)
}
/**
+ * gtk_get_current_event_device:
+ *
+ * If there is a current event and it has a device, return that
+ * device, otherwise return %NULL.
+ *
+ * Returns: a #GdkDevice, or %NULL
+ **/
+GdkDevice *
+gtk_get_current_event_device (void)
+{
+ if (current_events)
+ return gdk_event_get_device (current_events->data);
+ else
+ return NULL;
+}
+
+/**
* gtk_get_event_widget:
* @event: a #GdkEvent
*
diff --git a/gtk/gtkmain.h b/gtk/gtkmain.h
index 13f0fcb44a..9b4c686469 100644
--- a/gtk/gtkmain.h
+++ b/gtk/gtkmain.h
@@ -138,6 +138,12 @@ void gtk_grab_add (GtkWidget *widget);
GtkWidget* gtk_grab_get_current (void);
void gtk_grab_remove (GtkWidget *widget);
+void gtk_device_grab_add (GtkWidget *widget,
+ GdkDevice *device,
+ gboolean block_others);
+void gtk_device_grab_remove (GtkWidget *widget,
+ GdkDevice *device);
+
void gtk_init_add (GtkFunction function,
gpointer data);
void gtk_quit_add_destroy (guint main_level,
@@ -160,6 +166,7 @@ void gtk_key_snooper_remove (guint snooper_handler_id);
GdkEvent* gtk_get_current_event (void);
guint32 gtk_get_current_event_time (void);
gboolean gtk_get_current_event_state (GdkModifierType *state);
+GdkDevice * gtk_get_current_event_device (void);
GtkWidget* gtk_get_event_widget (GdkEvent *event);
diff --git a/gtk/gtkmarshalers.list b/gtk/gtkmarshalers.list
index 77873cb022..22af46d610 100644
--- a/gtk/gtkmarshalers.list
+++ b/gtk/gtkmarshalers.list
@@ -83,6 +83,7 @@ VOID:OBJECT,INT,OBJECT
VOID:OBJECT,INT,INT
VOID:OBJECT,INT,INT,BOXED,UINT,UINT
VOID:OBJECT,OBJECT
+VOID:OBJECT,POINTER
VOID:OBJECT,STRING
VOID:OBJECT,STRING,STRING
VOID:OBJECT,UINT
diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c
index 0bbfae42ac..e9999946d1 100644
--- a/gtk/gtkmenu.c
+++ b/gtk/gtkmenu.c
@@ -64,6 +64,7 @@
typedef struct _GtkMenuAttachData GtkMenuAttachData;
typedef struct _GtkMenuPrivate GtkMenuPrivate;
+typedef struct _GtkMenuPopdownData GtkMenuPopdownData;
struct _GtkMenuAttachData
{
@@ -100,6 +101,12 @@ struct _GtkMenuPrivate
guint no_toggle_size : 1;
};
+struct _GtkMenuPopdownData
+{
+ GtkMenu *menu;
+ GdkDevice *device;
+};
+
typedef struct
{
gint left_attach;
@@ -1370,33 +1377,38 @@ gtk_menu_tearoff_bg_copy (GtkMenu *menu)
static gboolean
popup_grab_on_window (GdkWindow *window,
- guint32 activate_time,
- gboolean grab_keyboard)
+ GdkDevice *keyboard,
+ GdkDevice *pointer,
+ guint32 activate_time)
{
- if ((gdk_pointer_grab (window, TRUE,
- GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
- GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
- GDK_POINTER_MOTION_MASK,
- NULL, NULL, activate_time) == 0))
+ if (keyboard &&
+ gdk_device_grab (keyboard, window,
+ GDK_OWNERSHIP_WINDOW, TRUE,
+ GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+ NULL, activate_time) != GDK_GRAB_SUCCESS)
+ return FALSE;
+
+ if (pointer &&
+ gdk_device_grab (pointer, window,
+ GDK_OWNERSHIP_WINDOW, TRUE,
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+ GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
+ GDK_POINTER_MOTION_MASK,
+ NULL, activate_time) != GDK_GRAB_SUCCESS)
{
- if (!grab_keyboard ||
- gdk_keyboard_grab (window, TRUE,
- activate_time) == 0)
- return TRUE;
- else
- {
- gdk_display_pointer_ungrab (gdk_drawable_get_display (window),
- activate_time);
- return FALSE;
- }
+ if (keyboard)
+ gdk_device_ungrab (keyboard, activate_time);
+
+ return FALSE;
}
- return FALSE;
+ return TRUE;
}
/**
- * gtk_menu_popup:
+ * gtk_menu_popup_for_device:
* @menu: a #GtkMenu.
+ * @device: (allow-none): a #GdkDevice
* @parent_menu_shell: (allow-none): the menu shell containing the triggering menu item, or %NULL
* @parent_menu_item: (allow-none): the menu item whose activation triggered the popup, or %NULL
* @func: (allow-none): a user supplied function used to position the menu, or %NULL
@@ -1406,9 +1418,9 @@ popup_grab_on_window (GdkWindow *window,
*
* Displays a menu and makes it available for selection. Applications can use
* this function to display context-sensitive menus, and will typically supply
- * %NULL for the @parent_menu_shell, @parent_menu_item, @func and @data
+ * %NULL for the @parent_menu_shell, @parent_menu_item, @func and @data
* parameters. The default menu positioning function will position the menu
- * at the current mouse cursor position.
+ * at the current position of @device (or its corresponding pointer).
*
* The @button parameter should be the mouse button pressed to initiate
* the menu popup. If the menu popup was initiated by something other than
@@ -1421,15 +1433,18 @@ popup_grab_on_window (GdkWindow *window,
* a mouse click or key press) that caused the initiation of the popup.
* Only if no such event is available, gtk_get_current_event_time() can
* be used instead.
+ *
+ * Since: 3.0
*/
void
-gtk_menu_popup (GtkMenu *menu,
- GtkWidget *parent_menu_shell,
- GtkWidget *parent_menu_item,
- GtkMenuPositionFunc func,
- gpointer data,
- guint button,
- guint32 activate_time)
+gtk_menu_popup_for_device (GtkMenu *menu,
+ GdkDevice *device,
+ GtkWidget *parent_menu_shell,
+ GtkWidget *parent_menu_item,
+ GtkMenuPositionFunc func,
+ gpointer data,
+ guint button,
+ guint32 activate_time)
{
GtkWidget *widget;
GtkWidget *xgrab_shell;
@@ -1439,13 +1454,26 @@ gtk_menu_popup (GtkMenu *menu,
gboolean grab_keyboard;
GtkMenuPrivate *priv;
GtkWidget *parent_toplevel;
+ GdkDevice *keyboard, *pointer;
g_return_if_fail (GTK_IS_MENU (menu));
+ g_return_if_fail (GDK_IS_DEVICE (device));
widget = GTK_WIDGET (menu);
menu_shell = GTK_MENU_SHELL (menu);
priv = gtk_menu_get_private (menu);
+ if (device->source == GDK_SOURCE_KEYBOARD)
+ {
+ keyboard = device;
+ pointer = gdk_device_get_associated_device (device);
+ }
+ else
+ {
+ pointer = device;
+ keyboard = gdk_device_get_associated_device (device);
+ }
+
menu_shell->parent_menu_shell = parent_menu_shell;
priv->seen_item_enter = FALSE;
@@ -1493,10 +1521,16 @@ gtk_menu_popup (GtkMenu *menu,
grab_keyboard = gtk_menu_shell_get_take_focus (menu_shell);
gtk_window_set_accept_focus (GTK_WINDOW (menu->toplevel), grab_keyboard);
+ if (!grab_keyboard)
+ keyboard = NULL;
+
if (xgrab_shell && xgrab_shell != widget)
{
- if (popup_grab_on_window (xgrab_shell->window, activate_time, grab_keyboard))
- GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
+ if (popup_grab_on_window (xgrab_shell->window, keyboard, pointer, activate_time))
+ {
+ _gtk_menu_shell_set_grab_devices (GTK_MENU_SHELL (xgrab_shell), keyboard, pointer);
+ GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
+ }
}
else
{
@@ -1504,8 +1538,11 @@ gtk_menu_popup (GtkMenu *menu,
xgrab_shell = widget;
transfer_window = menu_grab_transfer_window_get (menu);
- if (popup_grab_on_window (transfer_window, activate_time, grab_keyboard))
- GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
+ if (popup_grab_on_window (transfer_window, keyboard, pointer, activate_time))
+ {
+ _gtk_menu_shell_set_grab_devices (GTK_MENU_SHELL (xgrab_shell), keyboard, pointer);
+ GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
+ }
}
if (!GTK_MENU_SHELL (xgrab_shell)->have_xgrab)
@@ -1519,6 +1556,7 @@ gtk_menu_popup (GtkMenu *menu,
return;
}
+ _gtk_menu_shell_set_grab_devices (GTK_MENU_SHELL (menu), keyboard, pointer);
menu_shell->active = TRUE;
menu_shell->button = button;
@@ -1614,8 +1652,9 @@ gtk_menu_popup (GtkMenu *menu,
gtk_widget_show (menu->toplevel);
if (xgrab_shell == widget)
- popup_grab_on_window (widget->window, activate_time, grab_keyboard); /* Should always succeed */
- gtk_grab_add (GTK_WIDGET (menu));
+ popup_grab_on_window (widget->window, keyboard, pointer, activate_time); /* Should always succeed */
+
+ gtk_device_grab_add (GTK_WIDGET (menu), pointer, TRUE);
if (parent_menu_shell)
{
@@ -1630,11 +1669,77 @@ gtk_menu_popup (GtkMenu *menu,
_gtk_menu_shell_update_mnemonics (menu_shell);
}
+/**
+ * gtk_menu_popup:
+ * @menu: a #GtkMenu.
+ * @parent_menu_shell: (allow-none): the menu shell containing the triggering menu item, or %NULL
+ * @parent_menu_item: (allow-none): the menu item whose activation triggered the popup, or %NULL
+ * @func: (allow-none): a user supplied function used to position the menu, or %NULL
+ * @data: (allow-none): user supplied data to be passed to @func.
+ * @button: the mouse button which was pressed to initiate the event.
+ * @activate_time: the time at which the activation event occurred.
+ *
+ * Displays a menu and makes it available for selection. Applications can use
+ * this function to display context-sensitive menus, and will typically supply
+ * %NULL for the @parent_menu_shell, @parent_menu_item, @func and @data
+ * parameters. The default menu positioning function will position the menu
+ * at the current mouse cursor position.
+ *
+ * The @button parameter should be the mouse button pressed to initiate
+ * the menu popup. If the menu popup was initiated by something other than
+ * a mouse button press, such as a mouse button release or a keypress,
+ * @button should be 0.
+ *
+ * The @activate_time parameter is used to conflict-resolve initiation of
+ * concurrent requests for mouse/keyboard grab requests. To function
+ * properly, this needs to be the time stamp of the user event (such as
+ * a mouse click or key press) that caused the initiation of the popup.
+ * Only if no such event is available, gtk_get_current_event_time() can
+ * be used instead.
+ */
+void
+gtk_menu_popup (GtkMenu *menu,
+ GtkWidget *parent_menu_shell,
+ GtkWidget *parent_menu_item,
+ GtkMenuPositionFunc func,
+ gpointer data,
+ guint button,
+ guint32 activate_time)
+{
+ GdkDevice *device;
+
+ g_return_if_fail (GTK_IS_MENU (menu));
+
+ device = gtk_get_current_event_device ();
+
+ if (!device)
+ {
+ GdkDisplay *display;
+ GdkDeviceManager *device_manager;
+ GList *devices;
+
+ display = gtk_widget_get_display (GTK_WIDGET (menu));
+ device_manager = gdk_display_get_device_manager (display);
+ devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+
+ device = devices->data;
+
+ g_list_free (devices);
+ }
+
+ gtk_menu_popup_for_device (menu, device,
+ parent_menu_shell,
+ parent_menu_item,
+ func, data,
+ button, activate_time);
+}
+
void
gtk_menu_popdown (GtkMenu *menu)
{
GtkMenuPrivate *private;
GtkMenuShell *menu_shell;
+ GdkDevice *pointer;
g_return_if_fail (GTK_IS_MENU (menu));
@@ -1676,15 +1781,16 @@ gtk_menu_popdown (GtkMenu *menu)
}
else
{
- /* We popped up the menu from the tearoff, so we need to
+ GdkDevice *keyboard, *pointer;
+
+ /* We popped up the menu from the tearoff, so we need to
* release the grab - we aren't actually hiding the menu.
*/
- if (menu_shell->have_xgrab)
+ if (menu_shell->have_xgrab &&
+ _gtk_menu_shell_get_grab_devices (menu_shell, &keyboard, &pointer))
{
- GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (menu));
-
- gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
- gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
+ gdk_device_ungrab (keyboard, GDK_CURRENT_TIME);
+ gdk_device_ungrab (pointer, GDK_CURRENT_TIME);
}
}
@@ -1700,7 +1806,13 @@ gtk_menu_popdown (GtkMenu *menu)
gtk_widget_hide (GTK_WIDGET (menu));
menu_shell->have_xgrab = FALSE;
- gtk_grab_remove (GTK_WIDGET (menu));
+
+ _gtk_menu_shell_get_grab_devices (menu_shell, NULL, &pointer);
+
+ if (pointer)
+ gtk_device_grab_remove (GTK_WIDGET (menu), pointer);
+
+ _gtk_menu_shell_set_grab_devices (menu_shell, NULL, NULL);
menu_grab_transfer_window_destroy (menu);
}
@@ -3293,6 +3405,7 @@ gtk_menu_motion_notify (GtkWidget *widget,
send_event->crossing.x = event->x;
send_event->crossing.y = event->y;
send_event->crossing.state = event->state;
+ gdk_event_set_device (send_event, gdk_event_get_device ((GdkEvent *) event));
/* We send the event to 'widget', the currently active menu,
* instead of 'menu', the menu that the pointer is in. This
@@ -3967,14 +4080,17 @@ gtk_menu_stop_navigating_submenu (GtkMenu *menu)
static gboolean
gtk_menu_stop_navigating_submenu_cb (gpointer user_data)
{
- GtkMenu *menu = user_data;
+ GtkMenuPopdownData *popdown_data = user_data;
+ GtkMenu *menu = popdown_data->menu;
GdkWindow *child_window;
gtk_menu_stop_navigating_submenu (menu);
if (gtk_widget_get_realized (GTK_WIDGET (menu)))
{
- child_window = gdk_window_get_pointer (menu->bin_window, NULL, NULL, NULL);
+ child_window = gdk_window_get_device_position (menu->bin_window,
+ popdown_data->device,
+ NULL, NULL, NULL);
if (child_window)
{
@@ -3983,6 +4099,7 @@ gtk_menu_stop_navigating_submenu_cb (gpointer user_data)
send_event->crossing.window = g_object_ref (child_window);
send_event->crossing.time = GDK_CURRENT_TIME; /* Bogus */
send_event->crossing.send_event = TRUE;
+ gdk_event_set_device (send_event, popdown_data->device);
GTK_WIDGET_CLASS (gtk_menu_parent_class)->enter_notify_event (GTK_WIDGET (menu), (GdkEventCrossing *)send_event);
@@ -4089,6 +4206,7 @@ gtk_menu_set_submenu_navigation_region (GtkMenu *menu,
gint height = 0;
GdkPoint point[3];
GtkWidget *event_widget;
+ GtkMenuPopdownData *popdown_data;
g_return_if_fail (menu_item->submenu != NULL);
g_return_if_fail (event != NULL);
@@ -4163,9 +4281,15 @@ gtk_menu_set_submenu_navigation_region (GtkMenu *menu,
"gtk-menu-popdown-delay", &popdown_delay,
NULL);
- menu->navigation_timeout = gdk_threads_add_timeout (popdown_delay,
- gtk_menu_stop_navigating_submenu_cb,
- menu);
+ popdown_data = g_new (GtkMenuPopdownData, 1);
+ popdown_data->menu = menu;
+ popdown_data->device = gdk_event_get_device ((GdkEvent *) event);
+
+ menu->navigation_timeout = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT,
+ popdown_delay,
+ gtk_menu_stop_navigating_submenu_cb,
+ popdown_data,
+ (GDestroyNotify) g_free);
#ifdef DRAW_STAY_UP_TRIANGLE
draw_stay_up_triangle (gdk_get_default_root_window(),
@@ -4202,15 +4326,17 @@ gtk_menu_position (GtkMenu *menu)
GdkScreen *screen;
GdkScreen *pointer_screen;
GdkRectangle monitor;
-
+ GdkDevice *pointer;
+
g_return_if_fail (GTK_IS_MENU (menu));
widget = GTK_WIDGET (menu);
screen = gtk_widget_get_screen (widget);
- gdk_display_get_pointer (gdk_screen_get_display (screen),
- &pointer_screen, &x, &y, NULL);
-
+ _gtk_menu_shell_get_grab_devices (GTK_MENU_SHELL (menu), NULL, &pointer);
+ gdk_display_get_device_state (gdk_screen_get_display (screen),
+ pointer, &pointer_screen, &x, &y, NULL);
+
/* We need the requisition to figure out the right place to
* popup the menu. In fact, we always need to ask here, since
* if a size_request was queued while we weren't popped up,
@@ -5324,16 +5450,24 @@ gtk_menu_grab_notify (GtkWidget *widget,
GtkWidget *toplevel;
GtkWindowGroup *group;
GtkWidget *grab;
+ GdkDevice *pointer;
+
+ _gtk_menu_shell_get_grab_devices (GTK_MENU_SHELL (widget), NULL, &pointer);
+
+ if (!pointer ||
+ !gtk_widget_device_is_shadowed (widget, pointer))
+ return;
toplevel = gtk_widget_get_toplevel (widget);
+
+ if (!GTK_IS_WINDOW (toplevel))
+ return;
+
group = gtk_window_get_group (GTK_WINDOW (toplevel));
- grab = _gtk_window_group_get_current_grab (group);
+ grab = gtk_window_group_get_current_device_grab (group, pointer);
- if (!was_grabbed)
- {
- if (GTK_MENU_SHELL (widget)->active && !GTK_IS_MENU_SHELL (grab))
- gtk_menu_shell_cancel (GTK_MENU_SHELL (widget));
- }
+ if (GTK_MENU_SHELL (widget)->active && !GTK_IS_MENU_SHELL (grab))
+ gtk_menu_shell_cancel (GTK_MENU_SHELL (widget));
}
/**
diff --git a/gtk/gtkmenu.h b/gtk/gtkmenu.h
index 6a944c0584..0929472af7 100644
--- a/gtk/gtkmenu.h
+++ b/gtk/gtkmenu.h
@@ -133,6 +133,15 @@ void gtk_menu_popup (GtkMenu *menu,
gpointer data,
guint button,
guint32 activate_time);
+void gtk_menu_popup_for_device (GtkMenu *menu,
+ GdkDevice *device,
+ GtkWidget *parent_menu_shell,
+ GtkWidget *parent_menu_item,
+ GtkMenuPositionFunc func,
+ gpointer data,
+ guint button,
+ guint32 activate_time);
+
/* Position the menu according to its position function. Called
* from gtkmenuitem.c when a menu-item changes its allocation
diff --git a/gtk/gtkmenushell.c b/gtk/gtkmenushell.c
index c3e107fa1c..615f9a45c5 100644
--- a/gtk/gtkmenushell.c
+++ b/gtk/gtkmenushell.c
@@ -135,6 +135,9 @@ struct _GtkMenuShellPrivate
GtkMnemonicHash *mnemonic_hash;
GtkKeyHash *key_hash;
+ GdkDevice *grab_keyboard;
+ GdkDevice *grab_pointer;
+
guint take_focus : 1;
guint activated_submenu : 1;
/* This flag is a crutch to keep mnemonics in the same menu
@@ -548,7 +551,9 @@ _gtk_menu_shell_activate (GtkMenuShell *menu_shell)
{
if (!menu_shell->active)
{
- gtk_grab_add (GTK_WIDGET (menu_shell));
+ gtk_device_grab_add (GTK_WIDGET (menu_shell),
+ gtk_get_current_event_device (),
+ TRUE);
menu_shell->have_grab = TRUE;
menu_shell->active = TRUE;
}
@@ -1073,6 +1078,8 @@ gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
{
if (menu_shell->active)
{
+ GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
+
menu_shell->button = 0;
menu_shell->active = FALSE;
menu_shell->activate_time = 0;
@@ -1086,15 +1093,16 @@ gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
if (menu_shell->have_grab)
{
menu_shell->have_grab = FALSE;
- gtk_grab_remove (GTK_WIDGET (menu_shell));
+ gtk_device_grab_remove (GTK_WIDGET (menu_shell), priv->grab_pointer);
}
if (menu_shell->have_xgrab)
{
- GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (menu_shell));
+ gdk_device_ungrab (priv->grab_pointer, GDK_CURRENT_TIME);
+ gdk_device_ungrab (priv->grab_keyboard, GDK_CURRENT_TIME);
menu_shell->have_xgrab = FALSE;
- gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
- gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
+ priv->grab_pointer = NULL;
+ priv->grab_keyboard = NULL;
}
menu_shell->keyboard_mode = FALSE;
@@ -1743,6 +1751,39 @@ _gtk_menu_shell_remove_mnemonic (GtkMenuShell *menu_shell,
gtk_menu_shell_reset_key_hash (menu_shell);
}
+void
+_gtk_menu_shell_set_grab_devices (GtkMenuShell *menu_shell,
+ GdkDevice *keyboard,
+ GdkDevice *pointer)
+{
+ GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
+
+ g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
+ g_return_if_fail (!keyboard || GDK_IS_DEVICE (keyboard));
+ g_return_if_fail (!pointer || GDK_IS_DEVICE (pointer));
+
+ priv->grab_keyboard = keyboard;
+ priv->grab_pointer = pointer;
+}
+
+gboolean
+_gtk_menu_shell_get_grab_devices (GtkMenuShell *menu_shell,
+ GdkDevice **keyboard,
+ GdkDevice **pointer)
+{
+ GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
+
+ g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
+
+ if (keyboard)
+ *keyboard = priv->grab_keyboard;
+
+ if (pointer)
+ *pointer = priv->grab_pointer;
+
+ return TRUE;
+}
+
/**
* gtk_menu_shell_get_take_focus:
* @menu_shell: a #GtkMenuShell
diff --git a/gtk/gtkmenushell.h b/gtk/gtkmenushell.h
index e0d042bf17..f8c5a32c54 100644
--- a/gtk/gtkmenushell.h
+++ b/gtk/gtkmenushell.h
@@ -118,6 +118,14 @@ void _gtk_menu_shell_select_last (GtkMenuShell *menu_shell,
gboolean search_sensitive);
void _gtk_menu_shell_activate (GtkMenuShell *menu_shell);
gint _gtk_menu_shell_get_popup_delay (GtkMenuShell *menu_shell);
+
+void _gtk_menu_shell_set_grab_devices (GtkMenuShell *menu_shell,
+ GdkDevice *keyboard,
+ GdkDevice *pointer);
+gboolean _gtk_menu_shell_get_grab_devices (GtkMenuShell *menu_shell,
+ GdkDevice **keyboard,
+ GdkDevice **pointer);
+
void gtk_menu_shell_cancel (GtkMenuShell *menu_shell);
void _gtk_menu_shell_add_mnemonic (GtkMenuShell *menu_shell,
diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c
index 62008bafd3..bb528fdf07 100644
--- a/gtk/gtknotebook.c
+++ b/gtk/gtknotebook.c
@@ -2751,7 +2751,8 @@ get_drop_position (GtkNotebook *notebook,
static void
show_drag_window (GtkNotebook *notebook,
GtkNotebookPrivate *priv,
- GtkNotebookPage *page)
+ GtkNotebookPage *page,
+ GdkDevice *device)
{
GtkWidget *widget = GTK_WIDGET (notebook);
@@ -2786,10 +2787,10 @@ show_drag_window (GtkNotebook *notebook,
gdk_window_show (priv->drag_window);
/* the grab will dissapear when the window is hidden */
- gdk_pointer_grab (priv->drag_window,
- FALSE,
- GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
- NULL, NULL, GDK_CURRENT_TIME);
+ gdk_device_grab (device, priv->drag_window,
+ GDK_OWNERSHIP_WINDOW, FALSE,
+ GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
+ NULL, GDK_CURRENT_TIME);
}
/* This function undoes the reparenting that happens both when drag_window
@@ -3129,7 +3130,7 @@ gtk_notebook_motion_notify (GtkWidget *widget,
if (priv->operation != DRAG_OPERATION_REORDER)
{
priv->operation = DRAG_OPERATION_REORDER;
- show_drag_window (notebook, priv, page);
+ show_drag_window (notebook, priv, page, event->device);
}
gtk_notebook_pages_allocate (notebook);
diff --git a/gtk/gtkpaned.c b/gtk/gtkpaned.c
index d9c9177770..69e766d8a3 100644
--- a/gtk/gtkpaned.c
+++ b/gtk/gtkpaned.c
@@ -150,6 +150,7 @@ struct _GtkPanedPrivate
GtkWidget *saved_focus;
GtkPaned *first_paned;
guint32 grab_time;
+ GdkDevice *grab_device;
};
@@ -1215,18 +1216,20 @@ gtk_paned_button_press (GtkWidget *widget,
{
/* We need a server grab here, not gtk_grab_add(), since
* we don't want to pass events on to the widget's children */
- if (gdk_pointer_grab (paned->handle, FALSE,
- GDK_POINTER_MOTION_HINT_MASK
- | GDK_BUTTON1_MOTION_MASK
- | GDK_BUTTON_RELEASE_MASK
- | GDK_ENTER_NOTIFY_MASK
- | GDK_LEAVE_NOTIFY_MASK,
- NULL, NULL,
- event->time) != GDK_GRAB_SUCCESS)
+ if (gdk_device_grab (event->device,
+ paned->handle,
+ GDK_OWNERSHIP_WINDOW, FALSE,
+ GDK_POINTER_MOTION_HINT_MASK
+ | GDK_BUTTON1_MOTION_MASK
+ | GDK_BUTTON_RELEASE_MASK
+ | GDK_ENTER_NOTIFY_MASK
+ | GDK_LEAVE_NOTIFY_MASK,
+ NULL, event->time) != GDK_GRAB_SUCCESS)
return FALSE;
paned->in_drag = TRUE;
paned->priv->grab_time = event->time;
+ paned->priv->grab_device = event->device;
if (paned->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
paned->drag_pos = event->x;
@@ -1258,8 +1261,10 @@ stop_drag (GtkPaned *paned)
paned->in_drag = FALSE;
paned->drag_pos = -1;
paned->position_set = TRUE;
- gdk_display_pointer_ungrab (gtk_widget_get_display (GTK_WIDGET (paned)),
- paned->priv->grab_time);
+
+ gdk_device_ungrab (paned->priv->grab_device,
+ paned->priv->grab_time);
+ paned->priv->grab_device = NULL;
}
static void
@@ -1267,8 +1272,12 @@ gtk_paned_grab_notify (GtkWidget *widget,
gboolean was_grabbed)
{
GtkPaned *paned = GTK_PANED (widget);
+ GdkDevice *grab_device;
+
+ grab_device = paned->priv->grab_device;
- if (!was_grabbed && paned->in_drag)
+ if (paned->in_drag && grab_device &&
+ gtk_widget_device_is_shadowed (widget, grab_device))
stop_drag (paned);
}
diff --git a/gtk/gtkplug-x11.c b/gtk/gtkplug-x11.c
index 95dcf1ab6f..d0df6f54c5 100644
--- a/gtk/gtkplug-x11.c
+++ b/gtk/gtkplug-x11.c
@@ -25,6 +25,21 @@
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
+#include "config.h"
+
+#ifdef XINPUT_2
+
+/* Hack to have keyboard events interpreted
+ * regardless of the default device manager
+ */
+#define GDK_COMPILATION
+#include "x11/gdkdevicemanager-core.h"
+#include "x11/gdkdevicemanager-xi2.h"
+#include "x11/gdkeventtranslator.h"
+#undef GDK_COMPILATION
+
+#endif /* XINPUT_2 */
+
#include "gtkmain.h"
#include "gtkmarshalers.h"
#include "gtkplug.h"
@@ -208,7 +223,7 @@ _gtk_plug_windowing_filter_func (GdkXEvent *gdk_xevent,
XEvent *xevent = (XEvent *)gdk_xevent;
GdkFilterReturn return_val;
-
+
return_val = GDK_FILTER_CONTINUE;
switch (xevent->type)
@@ -326,6 +341,61 @@ _gtk_plug_windowing_filter_func (GdkXEvent *gdk_xevent,
break;
}
+
+#ifdef XINPUT_2
+ case KeyPress:
+ case KeyRelease:
+ {
+ static GdkDeviceManager *core_device_manager = NULL;
+ GdkDeviceManager *device_manager;
+ GdkEvent *translated_event;
+ GList *devices, *d;
+ GdkDevice *keyboard = NULL;
+
+ device_manager = gdk_display_get_device_manager (display);
+
+ /* bail out if the device manager already
+ * interprets core keyboard events.
+ */
+ if (!GDK_IS_DEVICE_MANAGER_XI2 (device_manager))
+ return GDK_FILTER_CONTINUE;
+
+ /* Find out the first keyboard device, the
+ * generated event will be assigned to it.
+ */
+ devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+
+ for (d = devices; d; d = d->next)
+ {
+ GdkDevice *device = d->data;
+
+ if (device->source == GDK_SOURCE_KEYBOARD)
+ keyboard = device;
+ }
+
+ g_list_free (devices);
+
+ if (!keyboard)
+ return GDK_FILTER_CONTINUE;
+
+ /* This is a crude hack so key events
+ * are interpreted as if there was a
+ * GdkDeviceManagerCore available.
+ */
+ if (G_UNLIKELY (!core_device_manager))
+ core_device_manager = g_object_new (GDK_TYPE_DEVICE_MANAGER_CORE,
+ "display", display,
+ NULL);
+
+ translated_event = gdk_event_translator_translate (GDK_EVENT_TRANSLATOR (core_device_manager), display, xevent);
+ gdk_event_set_device (translated_event, keyboard);
+
+ gtk_main_do_event (translated_event);
+ gdk_event_free (translated_event);
+
+ return_val = GDK_FILTER_REMOVE;
+ }
+#endif
}
return return_val;
diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h
index 4c5efb374b..8db5c48518 100644
--- a/gtk/gtkprivate.h
+++ b/gtk/gtkprivate.h
@@ -50,7 +50,6 @@ typedef enum
PRIVATE_GTK_REQUEST_NEEDED = 1 << 13, /* Whether we need to call gtk_widget_size_request */
PRIVATE_GTK_WIDTH_REQUEST_NEEDED = 1 << 14, /* Whether we need to call gtk_extended_layout_get_desired_width */
PRIVATE_GTK_HEIGHT_REQUEST_NEEDED = 1 << 15 /* Whether we need to call gtk_extended_layout_get_desired_height */
-
} GtkPrivateFlags;
/* Macros for extracting a widgets private_flags from GtkWidget.
diff --git a/gtk/gtkrange.c b/gtk/gtkrange.c
index 744871d065..fa92d25651 100644
--- a/gtk/gtkrange.c
+++ b/gtk/gtkrange.c
@@ -139,6 +139,8 @@ struct _GtkRangeLayout
gint *mark_pos;
gint n_marks;
gboolean recalc_marks;
+
+ GdkDevice *grab_device;
};
@@ -2032,16 +2034,28 @@ gtk_range_expose (GtkWidget *widget,
static void
range_grab_add (GtkRange *range,
+ GdkDevice *device,
MouseLocation location,
gint button)
{
- /* we don't actually gtk_grab, since a button is down */
+ GtkRangeLayout *layout = range->layout;
+
+ if (device == layout->grab_device)
+ return;
+
+ if (layout->grab_device != NULL)
+ {
+ g_warning ("GtkRange already had a grab device, releasing device grab");
+ gtk_device_grab_remove (GTK_WIDGET (range), layout->grab_device);
+ }
+
+ /* we don't actually gdk_grab, since a button is down */
+ gtk_device_grab_add (GTK_WIDGET (range), device, TRUE);
- gtk_grab_add (GTK_WIDGET (range));
-
range->layout->grab_location = location;
range->layout->grab_button = button;
-
+ range->layout->grab_device = device;
+
if (gtk_range_update_mouse_location (range))
gtk_widget_queue_draw (GTK_WIDGET (range));
}
@@ -2049,10 +2063,16 @@ range_grab_add (GtkRange *range,
static void
range_grab_remove (GtkRange *range)
{
+ GtkRangeLayout *layout = range->layout;
MouseLocation location;
- gtk_grab_remove (GTK_WIDGET (range));
-
+ if (layout->grab_device)
+ {
+ gtk_device_grab_remove (GTK_WIDGET (range),
+ layout->grab_device);
+ layout->grab_device = NULL;
+ }
+
location = range->layout->grab_location;
range->layout->grab_location = MOUSE_OUTSIDE;
range->layout->grab_button = 0;
@@ -2177,9 +2197,15 @@ static gboolean
gtk_range_key_press (GtkWidget *widget,
GdkEventKey *event)
{
+ GdkDevice *device;
GtkRange *range = GTK_RANGE (widget);
+ GtkRangeLayout *layout = range->layout;
+
+ device = gdk_event_get_device ((GdkEvent *) event);
+ device = gdk_device_get_associated_device (device);
- if (event->keyval == GDK_Escape &&
+ if (device == layout->grab_device &&
+ event->keyval == GDK_Escape &&
range->layout->grab_location != MOUSE_OUTSIDE)
{
stop_scrolling (range);
@@ -2199,6 +2225,7 @@ gtk_range_button_press (GtkWidget *widget,
GdkEventButton *event)
{
GtkRange *range = GTK_RANGE (widget);
+ GdkDevice *device;
if (!gtk_widget_has_focus (widget))
gtk_widget_grab_focus (widget);
@@ -2207,8 +2234,10 @@ gtk_range_button_press (GtkWidget *widget,
if (range->layout->grab_location != MOUSE_OUTSIDE)
return FALSE;
+ device = gdk_event_get_device ((GdkEvent *) event);
range->layout->mouse_x = event->x;
range->layout->mouse_y = event->y;
+
if (gtk_range_update_mouse_location (range))
gtk_widget_queue_draw (widget);
@@ -2225,7 +2254,7 @@ gtk_range_button_press (GtkWidget *widget,
event->y : event->x);
range->trough_click_forward = click_value > range->adjustment->value;
- range_grab_add (range, MOUSE_TROUGH, event->button);
+ range_grab_add (range, device, MOUSE_TROUGH, event->button);
scroll = range_get_scroll_for_grab (range);
@@ -2242,7 +2271,7 @@ gtk_range_button_press (GtkWidget *widget,
GdkRectangle *stepper_area;
GtkScrollType scroll;
- range_grab_add (range, range->layout->mouse_location, event->button);
+ range_grab_add (range, device, range->layout->mouse_location, event->button);
stepper_area = get_area (range, range->layout->mouse_location);
gtk_widget_queue_draw_area (widget,
@@ -2309,7 +2338,7 @@ gtk_range_button_press (GtkWidget *widget,
range->slide_initial_coordinate = event->x;
}
- range_grab_add (range, MOUSE_SLIDER, event->button);
+ range_grab_add (range, device, MOUSE_SLIDER, event->button);
gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL);
@@ -2386,8 +2415,12 @@ gtk_range_grab_broken (GtkWidget *widget,
GdkEventGrabBroken *event)
{
GtkRange *range = GTK_RANGE (widget);
+ GdkDevice *device;
- if (range->layout->grab_location != MOUSE_OUTSIDE)
+ device = gdk_event_get_device ((GdkEvent *) event);
+
+ if (device == range->layout->grab_device &&
+ range->layout->grab_location != MOUSE_OUTSIDE)
{
if (range->layout->grab_location == MOUSE_SLIDER)
update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
@@ -2405,6 +2438,7 @@ gtk_range_button_release (GtkWidget *widget,
GdkEventButton *event)
{
GtkRange *range = GTK_RANGE (widget);
+ GdkDevice *device;
if (event->window == range->event_window)
{
@@ -2413,13 +2447,17 @@ gtk_range_button_release (GtkWidget *widget,
}
else
{
- gdk_window_get_pointer (range->event_window,
- &range->layout->mouse_x,
- &range->layout->mouse_y,
- NULL);
+ gdk_window_get_device_position (range->event_window,
+ event->device,
+ &range->layout->mouse_x,
+ &range->layout->mouse_y,
+ NULL);
}
-
- if (range->layout->grab_button == event->button)
+
+ device = gdk_event_get_device ((GdkEvent *) event);
+
+ if (range->layout->grab_device == device &&
+ range->layout->grab_button == event->button)
{
if (range->layout->grab_location == MOUSE_SLIDER)
update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
@@ -2551,7 +2589,10 @@ static void
gtk_range_grab_notify (GtkWidget *widget,
gboolean was_grabbed)
{
- if (!was_grabbed)
+ GtkRangeLayout *layout = GTK_RANGE (widget)->layout;
+
+ if (layout->grab_device &&
+ gtk_widget_device_is_shadowed (widget, layout->grab_device))
stop_scrolling (GTK_RANGE (widget));
}
diff --git a/gtk/gtkscalebutton.c b/gtk/gtkscalebutton.c
index f994d6e92f..dce3937a25 100644
--- a/gtk/gtkscalebutton.c
+++ b/gtk/gtkscalebutton.c
@@ -116,6 +116,9 @@ struct _GtkScaleButtonPrivate
gchar **icon_list;
+ GdkDevice *grab_pointer;
+ GdkDevice *grab_keyboard;
+
GtkAdjustment *adjustment; /* needed because it must be settable in init() */
};
@@ -901,6 +904,7 @@ gtk_scale_popup (GtkWidget *widget,
GdkDisplay *display;
GdkScreen *screen;
gboolean is_moved;
+ GdkDevice *device, *keyboard, *pointer;
is_moved = FALSE;
button = GTK_SCALE_BUTTON (widget);
@@ -982,21 +986,27 @@ gtk_scale_popup (GtkWidget *widget,
/* Move the dock, but set is_moved so we
* don't forward the first click later on,
* as it could make the scale go to the bottom */
- if (y < rect.y) {
- y = rect.y;
- is_moved = TRUE;
- } else if (y + d->allocation.height > rect.height + rect.y) {
- y = rect.y + rect.height - d->allocation.height;
- is_moved = TRUE;
- }
+ if (y < rect.y)
+ {
+ y = rect.y;
+ is_moved = TRUE;
+ }
+ else if (y + d->allocation.height > rect.height + rect.y)
+ {
+ y = rect.y + rect.height - d->allocation.height;
+ is_moved = TRUE;
+ }
- if (x < rect.x) {
- x = rect.x;
- is_moved = TRUE;
- } else if (x + d->allocation.width > rect.width + rect.x) {
- x = rect.x + rect.width - d->allocation.width;
- is_moved = TRUE;
- }
+ if (x < rect.x)
+ {
+ x = rect.x;
+ is_moved = TRUE;
+ }
+ else if (x + d->allocation.width > rect.width + rect.x)
+ {
+ x = rect.x + rect.width - d->allocation.width;
+ is_moved = TRUE;
+ }
}
gtk_window_move (GTK_WINDOW (priv->dock), x, y);
@@ -1004,28 +1014,46 @@ gtk_scale_popup (GtkWidget *widget,
if (event->type == GDK_BUTTON_PRESS)
GTK_WIDGET_CLASS (gtk_scale_button_parent_class)->button_press_event (widget, (GdkEventButton *) event);
+ device = gdk_event_get_device (event);
+
+ if (device->source == GDK_SOURCE_KEYBOARD)
+ {
+ keyboard = device;
+ pointer = gdk_device_get_associated_device (device);
+ }
+ else
+ {
+ pointer = device;
+ keyboard = gdk_device_get_associated_device (device);
+ }
+
/* grab focus */
- gtk_grab_add (priv->dock);
+ gtk_device_grab_add (priv->dock, pointer, TRUE);
- if (gdk_pointer_grab (priv->dock->window, TRUE,
- GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
- GDK_POINTER_MOTION_MASK, NULL, NULL, time)
- != GDK_GRAB_SUCCESS)
+ if (gdk_device_grab (pointer, priv->dock->window,
+ GDK_OWNERSHIP_WINDOW, TRUE,
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK, NULL, time) != GDK_GRAB_SUCCESS)
{
- gtk_grab_remove (priv->dock);
+ gtk_device_grab_remove (priv->dock, pointer);
gtk_widget_hide (priv->dock);
return FALSE;
}
- if (gdk_keyboard_grab (priv->dock->window, TRUE, time) != GDK_GRAB_SUCCESS)
+ if (gdk_device_grab (keyboard, priv->dock->window,
+ GDK_OWNERSHIP_WINDOW, TRUE,
+ GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+ NULL, time) != GDK_GRAB_SUCCESS)
{
- gdk_display_pointer_ungrab (display, time);
- gtk_grab_remove (priv->dock);
+ gdk_device_ungrab (pointer, time);
+ gtk_device_grab_remove (priv->dock, pointer);
gtk_widget_hide (priv->dock);
return FALSE;
}
gtk_widget_grab_focus (priv->dock);
+ priv->grab_keyboard = keyboard;
+ priv->grab_pointer = pointer;
if (event->type == GDK_BUTTON_PRESS && !is_moved)
{
@@ -1080,8 +1108,21 @@ gtk_scale_button_popup (GtkWidget *widget)
{
GdkEvent *ev;
- ev = gdk_event_new (GDK_KEY_RELEASE);
- gtk_scale_popup (widget, ev, GDK_CURRENT_TIME);
+ /* This is a callback for a keybinding signal,
+ * current event should be the key event that
+ * triggered it.
+ */
+ ev = gtk_get_current_event ();
+
+ if (ev->type != GDK_KEY_PRESS &&
+ ev->type != GDK_KEY_RELEASE)
+ {
+ gdk_event_free (ev);
+ ev = gdk_event_new (GDK_KEY_RELEASE);
+ ev->key.time = GDK_CURRENT_TIME;
+ }
+
+ gtk_scale_popup (widget, ev, ev->key.time);
gdk_event_free (ev);
}
@@ -1100,22 +1141,35 @@ gtk_scale_button_grab_notify (GtkScaleButton *button,
{
GdkDisplay *display;
GtkScaleButtonPrivate *priv;
-
- if (was_grabbed != FALSE)
- return;
+ GtkWidget *toplevel, *grab_widget;
+ GtkWindowGroup *group;
priv = button->priv;
- if (!gtk_widget_has_grab (priv->dock))
+ if (!priv->grab_pointer ||
+ !gtk_widget_device_is_shadowed (GTK_WIDGET (button), priv->grab_pointer))
return;
- if (gtk_widget_is_ancestor (gtk_grab_get_current (), priv->dock))
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
+
+ if (GTK_IS_WINDOW (toplevel))
+ group = gtk_window_get_group (GTK_WINDOW (toplevel));
+ else
+ group = gtk_window_get_group (NULL);
+
+ grab_widget = gtk_window_group_get_current_device_grab (group, priv->grab_pointer);
+
+ if (grab_widget &&
+ gtk_widget_is_ancestor (grab_widget, priv->dock))
return;
display = gtk_widget_get_display (priv->dock);
- gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
- gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
- gtk_grab_remove (priv->dock);
+ gdk_device_ungrab (priv->grab_keyboard, GDK_CURRENT_TIME);
+ gdk_device_ungrab (priv->grab_pointer, GDK_CURRENT_TIME);
+ gtk_device_grab_remove (priv->dock, priv->grab_pointer);
+
+ priv->grab_keyboard = NULL;
+ priv->grab_pointer = NULL;
/* hide again */
gtk_widget_hide (priv->dock);
@@ -1227,7 +1281,7 @@ cb_dock_grab_notify (GtkWidget *widget,
static gboolean
cb_dock_grab_broken_event (GtkWidget *widget,
- gboolean was_grabbed,
+ gboolean was_grabbed,
gpointer user_data)
{
GtkScaleButton *button = (GtkScaleButton *) user_data;
@@ -1253,9 +1307,12 @@ gtk_scale_button_release_grab (GtkScaleButton *button,
/* ungrab focus */
display = gtk_widget_get_display (GTK_WIDGET (button));
- gdk_display_keyboard_ungrab (display, event->time);
- gdk_display_pointer_ungrab (display, event->time);
- gtk_grab_remove (priv->dock);
+ gdk_device_ungrab (priv->grab_keyboard, event->time);
+ gdk_device_ungrab (priv->grab_pointer, event->time);
+ gtk_device_grab_remove (priv->dock, priv->grab_pointer);
+
+ priv->grab_keyboard = NULL;
+ priv->grab_pointer = NULL;
/* hide again */
gtk_widget_hide (priv->dock);
@@ -1297,9 +1354,12 @@ gtk_scale_button_popdown (GtkWidget *widget)
/* ungrab focus */
display = gtk_widget_get_display (widget);
- gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
- gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
- gtk_grab_remove (priv->dock);
+ gdk_device_ungrab (priv->grab_keyboard, GDK_CURRENT_TIME);
+ gdk_device_ungrab (priv->grab_pointer, GDK_CURRENT_TIME);
+ gtk_device_grab_remove (priv->dock, priv->grab_pointer);
+
+ priv->grab_keyboard = NULL;
+ priv->grab_pointer = NULL;
/* hide again */
gtk_widget_hide (priv->dock);
@@ -1418,7 +1478,7 @@ gtk_scale_button_scale_press (GtkWidget *widget,
/* the scale will grab input; if we have input grabbed, all goes
* horribly wrong, so let's not do that.
*/
- gtk_grab_remove (priv->dock);
+ gtk_device_grab_remove (priv->dock, event->device);
return GTK_WIDGET_CLASS (_gtk_scale_button_scale_parent_class)->button_press_event (widget, event);
}
@@ -1453,7 +1513,7 @@ gtk_scale_button_scale_release (GtkWidget *widget,
* find that, so we do this complex 'first-call-parent-then-do-actual-
* action' thingy...
*/
- gtk_grab_add (button->priv->dock);
+ gtk_device_grab_add (button->priv->dock, event->device, TRUE);
return res;
}
diff --git a/gtk/gtksocket.c b/gtk/gtksocket.c
index dfb20f001d..5e6a1fb8ec 100644
--- a/gtk/gtksocket.c
+++ b/gtk/gtksocket.c
@@ -558,7 +558,7 @@ activate_key (GtkAccelGroup *accel_group,
if (gdk_event && gdk_event->type == GDK_KEY_PRESS && socket->plug_window)
{
- _gtk_socket_windowing_send_key_event (socket, gdk_event, TRUE);
+ _gtk_socket_windowing_send_key_event (socket, gdk_event, FALSE);
retval = TRUE;
}
diff --git a/gtk/gtkspinbutton.c b/gtk/gtkspinbutton.c
index 32934bcf89..71d7934bf0 100644
--- a/gtk/gtkspinbutton.c
+++ b/gtk/gtkspinbutton.c
@@ -903,10 +903,12 @@ gtk_spin_button_enter_notify (GtkWidget *widget,
if (event->window == spin->panel)
{
+ GdkDevice *device;
gint x;
gint y;
- gdk_window_get_pointer (spin->panel, &x, &y, NULL);
+ device = gdk_event_get_device ((GdkEvent *) event);
+ gdk_window_get_device_position (spin->panel, device, &x, &y, NULL);
if (y <= widget->requisition.height / 2)
spin->in_child = GTK_ARROW_UP;
@@ -915,7 +917,7 @@ gtk_spin_button_enter_notify (GtkWidget *widget,
gtk_widget_queue_draw (GTK_WIDGET (spin));
}
-
+
if (GTK_WIDGET_CLASS (gtk_spin_button_parent_class)->enter_notify_event)
return GTK_WIDGET_CLASS (gtk_spin_button_parent_class)->enter_notify_event (widget, event);
diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c
index 2819c793c0..2c629f07b7 100644
--- a/gtk/gtktextview.c
+++ b/gtk/gtktextview.c
@@ -109,6 +109,7 @@ struct _GtkTextViewPrivate
guint blink_time; /* time in msec the cursor has blinked since last user event */
guint im_spot_idle;
gchar *im_module;
+ GdkDevice *grab_device;
guint scroll_after_paste : 1;
};
@@ -4084,7 +4085,12 @@ static void
gtk_text_view_grab_notify (GtkWidget *widget,
gboolean was_grabbed)
{
- if (!was_grabbed)
+ GtkTextViewPrivate *priv;
+
+ priv = GTK_TEXT_VIEW_GET_PRIVATE (widget);
+
+ if (priv->grab_device &&
+ gtk_widget_device_is_shadowed (widget, priv->grab_device))
{
gtk_text_view_end_selection_drag (GTK_TEXT_VIEW (widget));
gtk_text_view_unobscure_mouse_cursor (GTK_TEXT_VIEW (widget));
@@ -5992,6 +5998,7 @@ gtk_text_view_unselect (GtkTextView *text_view)
static void
get_iter_at_pointer (GtkTextView *text_view,
+ GdkDevice *device,
GtkTextIter *iter,
gint *x,
gint *y)
@@ -5999,9 +6006,9 @@ get_iter_at_pointer (GtkTextView *text_view,
gint xcoord, ycoord;
GdkModifierType state;
- gdk_window_get_pointer (text_view->text_window->bin_window,
- &xcoord, &ycoord, &state);
-
+ gdk_window_get_device_position (text_view->text_window->bin_window,
+ device, &xcoord, &ycoord, &state);
+
gtk_text_layout_get_iter_at_pixel (text_view->layout,
iter,
xcoord + text_view->xoffset,
@@ -6015,12 +6022,13 @@ get_iter_at_pointer (GtkTextView *text_view,
static void
move_mark_to_pointer_and_scroll (GtkTextView *text_view,
- const gchar *mark_name)
+ const gchar *mark_name,
+ GdkDevice *device)
{
GtkTextIter newplace;
GtkTextMark *mark;
- get_iter_at_pointer (text_view, &newplace, NULL, NULL);
+ get_iter_at_pointer (text_view, device, &newplace, NULL, NULL);
mark = gtk_text_buffer_get_mark (get_buffer (text_view), mark_name);
@@ -6045,7 +6053,6 @@ selection_scan_timeout (gpointer data)
text_view = GTK_TEXT_VIEW (data);
- DV(g_print (G_STRLOC": calling move_mark_to_pointer_and_scroll\n"));
gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_insert (get_buffer (text_view)));
@@ -6074,10 +6081,12 @@ drag_scan_timeout (gpointer data)
GtkTextIter newplace;
gint x, y, width, height;
gdouble pointer_xoffset, pointer_yoffset;
+ GdkDevice *device;
text_view = GTK_TEXT_VIEW (data);
+ device = gdk_display_get_core_pointer (gtk_widget_get_display (GTK_WIDGET (data)));
- get_iter_at_pointer (text_view, &newplace, &x, &y);
+ get_iter_at_pointer (text_view, device, &newplace, &x, &y);
gdk_drawable_get_size (text_view->text_window->bin_window, &width, &height);
gtk_text_buffer_move_mark (get_buffer (text_view),
@@ -6214,11 +6223,17 @@ selection_motion_event_handler (GtkTextView *text_view,
GdkEventMotion *event,
SelectionData *data)
{
+ GtkTextViewPrivate *priv;
+
+ priv = GTK_TEXT_VIEW_GET_PRIVATE (text_view);
gdk_event_request_motions (event);
+ if (priv->grab_device != event->device)
+ return FALSE;
+
if (data->granularity == SELECT_CHARACTERS)
{
- move_mark_to_pointer_and_scroll (text_view, "insert");
+ move_mark_to_pointer_and_scroll (text_view, "insert", event->device);
}
else
{
@@ -6231,7 +6246,7 @@ selection_motion_event_handler (GtkTextView *text_view,
gtk_text_buffer_get_iter_at_mark (buffer, &orig_start, data->orig_start);
gtk_text_buffer_get_iter_at_mark (buffer, &orig_end, data->orig_end);
- get_iter_at_pointer (text_view, &cursor, NULL, NULL);
+ get_iter_at_pointer (text_view, event->device, &cursor, NULL, NULL);
start = cursor;
extend_selection (text_view, data->granularity, &start, &end);
@@ -6265,6 +6280,7 @@ gtk_text_view_start_selection_drag (GtkTextView *text_view,
const GtkTextIter *iter,
GdkEventButton *button)
{
+ GtkTextViewPrivate *priv;
GtkTextIter cursor, ins, bound;
GtkTextIter orig_start, orig_end;
GtkTextBuffer *buffer;
@@ -6272,7 +6288,8 @@ gtk_text_view_start_selection_drag (GtkTextView *text_view,
if (text_view->selection_drag_handler != 0)
return;
-
+
+ priv = GTK_TEXT_VIEW_GET_PRIVATE (text_view);
data = g_new0 (SelectionData, 1);
if (button->type == GDK_2BUTTON_PRESS)
@@ -6282,7 +6299,10 @@ gtk_text_view_start_selection_drag (GtkTextView *text_view,
else
data->granularity = SELECT_CHARACTERS;
- gtk_grab_add (GTK_WIDGET (text_view));
+ priv->grab_device = button->device;
+ gtk_device_grab_add (GTK_WIDGET (text_view),
+ priv->grab_device,
+ TRUE);
buffer = get_buffer (text_view);
@@ -6332,7 +6352,6 @@ gtk_text_view_start_selection_drag (GtkTextView *text_view,
&orig_start, TRUE);
data->orig_end = gtk_text_buffer_create_mark (buffer, NULL,
&orig_end, TRUE);
-
gtk_text_view_check_cursor_blink (text_view);
text_view->selection_drag_handler = g_signal_connect_data (text_view,
@@ -6346,6 +6365,13 @@ gtk_text_view_start_selection_drag (GtkTextView *text_view,
static gboolean
gtk_text_view_end_selection_drag (GtkTextView *text_view)
{
+ GtkTextViewPrivate *priv;
+
+ priv = GTK_TEXT_VIEW_GET_PRIVATE (text_view);
+
+ if (!priv->grab_device)
+ return FALSE;
+
if (text_view->selection_drag_handler == 0)
return FALSE;
@@ -6358,7 +6384,9 @@ gtk_text_view_end_selection_drag (GtkTextView *text_view)
text_view->scroll_timeout = 0;
}
- gtk_grab_remove (GTK_WIDGET (text_view));
+ gtk_device_grab_remove (GTK_WIDGET (text_view),
+ priv->grab_device);
+ priv->grab_device = NULL;
return TRUE;
}
diff --git a/gtk/gtktooltip.c b/gtk/gtktooltip.c
index 204a2b6bba..d5a301be4e 100644
--- a/gtk/gtktooltip.c
+++ b/gtk/gtktooltip.c
@@ -1246,6 +1246,7 @@ _gtk_tooltip_focus_in (GtkWidget *widget)
gboolean return_value = FALSE;
GdkDisplay *display;
GtkTooltip *tooltip;
+ GdkDevice *device;
/* Get current tooltip for this display */
display = gtk_widget_get_display (widget);
@@ -1256,12 +1257,23 @@ _gtk_tooltip_focus_in (GtkWidget *widget)
if (!tooltip || !tooltip->keyboard_mode_enabled)
return;
+ device = gtk_get_current_event_device ();
+
+ if (device && device->source == GDK_SOURCE_KEYBOARD)
+ device = gdk_device_get_associated_device (device);
+
+ /* This function should be called by either a focus in event,
+ * or a key binding. In either case there should be a device.
+ */
+ if (!device)
+ return;
+
if (tooltip->keyboard_widget)
g_object_unref (tooltip->keyboard_widget);
tooltip->keyboard_widget = g_object_ref (widget);
- gdk_window_get_pointer (widget->window, &x, &y, NULL);
+ gdk_window_get_device_position (widget->window, device, &x, &y, NULL);
return_value = gtk_tooltip_run_requery (&widget, tooltip, &x, &y);
if (!return_value)
diff --git a/gtk/gtktreeprivate.h b/gtk/gtktreeprivate.h
index c44d61813d..e8887e0b17 100644
--- a/gtk/gtktreeprivate.h
+++ b/gtk/gtktreeprivate.h
@@ -406,7 +406,8 @@ void _gtk_tree_view_column_unset_tree_view (GtkTreeViewColumn *column);
void _gtk_tree_view_column_set_width (GtkTreeViewColumn *column,
gint width);
void _gtk_tree_view_column_start_drag (GtkTreeView *tree_view,
- GtkTreeViewColumn *column);
+ GtkTreeViewColumn *column,
+ GdkDevice *device);
gboolean _gtk_tree_view_column_cell_event (GtkTreeViewColumn *tree_column,
GtkCellEditable **editable_widget,
GdkEvent *event,
diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c
index 533bb17e84..b75c75d218 100644
--- a/gtk/gtktreeview.c
+++ b/gtk/gtktreeview.c
@@ -396,8 +396,9 @@ static void update_prelight (GtkTreeView
/* interactive search */
static void gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view);
-static void gtk_tree_view_search_dialog_hide (GtkWidget *search_dialog,
- GtkTreeView *tree_view);
+static void gtk_tree_view_search_dialog_hide (GtkWidget *search_dialog,
+ GtkTreeView *tree_view,
+ GdkDevice *device);
static void gtk_tree_view_search_position_func (GtkTreeView *tree_view,
GtkWidget *search_dialog,
gpointer user_data);
@@ -457,6 +458,7 @@ static void gtk_tree_view_real_start_editing (GtkTreeView *tree_view,
static void gtk_tree_view_stop_editing (GtkTreeView *tree_view,
gboolean cancel_editing);
static gboolean gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
+ GdkDevice *device,
gboolean keybinding);
static gboolean gtk_tree_view_start_interactive_search (GtkTreeView *tree_view);
static GtkTreeViewColumn *gtk_tree_view_get_drop_column (GtkTreeView *tree_view,
@@ -5493,7 +5495,9 @@ gtk_tree_view_key_press (GtkWidget *widget,
if (tree_view->priv->imcontext_changed || /* we're in a preedit */
(retval && text_modified)) /* ...or the text was modified */
{
- if (gtk_tree_view_real_start_interactive_search (tree_view, FALSE))
+ if (gtk_tree_view_real_start_interactive_search (tree_view,
+ gdk_event_get_device ((GdkEvent *) event),
+ FALSE))
{
gtk_widget_grab_focus (GTK_WIDGET (tree_view));
return TRUE;
@@ -5602,7 +5606,8 @@ gtk_tree_view_focus_out (GtkWidget *widget,
/* destroy interactive search dialog */
if (tree_view->priv->search_window)
- gtk_tree_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
+ gtk_tree_view_search_dialog_hide (tree_view->priv->search_window, tree_view,
+ gdk_event_get_device ((GdkEvent *) event));
return FALSE;
}
@@ -9265,7 +9270,8 @@ gtk_tree_view_set_column_drag_info (GtkTreeView *tree_view,
void
_gtk_tree_view_column_start_drag (GtkTreeView *tree_view,
- GtkTreeViewColumn *column)
+ GtkTreeViewColumn *column,
+ GdkDevice *device)
{
GdkEvent *send_event;
GtkAllocation allocation;
@@ -9314,6 +9320,7 @@ _gtk_tree_view_column_start_drag (GtkTreeView *tree_view,
send_event->crossing.subwindow = NULL;
send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
send_event->crossing.time = GDK_CURRENT_TIME;
+ gdk_event_set_device (send_event, device);
gtk_propagate_event (column->button, send_event);
gdk_event_free (send_event);
@@ -9327,9 +9334,9 @@ _gtk_tree_view_column_start_drag (GtkTreeView *tree_view,
send_event->button.axes = NULL;
send_event->button.state = 0;
send_event->button.button = 1;
- send_event->button.device = gdk_display_get_core_pointer (display);
send_event->button.x_root = 0;
send_event->button.y_root = 0;
+ gdk_event_set_device (send_event, device);
gtk_propagate_event (column->button, send_event);
gdk_event_free (send_event);
@@ -10249,7 +10256,7 @@ gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view)
static gboolean
gtk_tree_view_search_entry_flush_timeout (GtkTreeView *tree_view)
{
- gtk_tree_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
+ gtk_tree_view_search_dialog_hide (tree_view->priv->search_window, tree_view, NULL);
tree_view->priv->typeselect_flush_timeout = 0;
return FALSE;
@@ -10258,17 +10265,43 @@ gtk_tree_view_search_entry_flush_timeout (GtkTreeView *tree_view)
/* Cut and paste from gtkwindow.c */
static void
send_focus_change (GtkWidget *widget,
+ GdkDevice *device,
gboolean in)
{
- GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
+ GdkDeviceManager *device_manager;
+ GList *devices, *d;
+
+ device_manager = gdk_display_get_device_manager (gtk_widget_get_display (widget));
+ devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+ devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_SLAVE));
+ devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_FLOATING));
+
+ for (d = devices; d; d = d->next)
+ {
+ GdkDevice *dev = d->data;
+ GdkEvent *fevent;
+
+ if (dev->source != GDK_SOURCE_KEYBOARD)
+ continue;
+
+ /* Skip non-master keyboards that haven't
+ * selected for events from this window
+ */
+ if (gdk_device_get_device_type (dev) != GDK_DEVICE_TYPE_MASTER &&
+ !gdk_window_get_device_events (widget->window, dev))
+ continue;
- fevent->focus_change.type = GDK_FOCUS_CHANGE;
- fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget));
- fevent->focus_change.in = in;
+ fevent = gdk_event_new (GDK_FOCUS_CHANGE);
- gtk_widget_send_focus_change (widget, fevent);
+ fevent->focus_change.type = GDK_FOCUS_CHANGE;
+ fevent->focus_change.window = g_object_ref (widget->window);
+ fevent->focus_change.in = in;
+ gdk_event_set_device (fevent, device);
- gdk_event_free (fevent);
+ gtk_widget_send_focus_change (widget, fevent);
+
+ gdk_event_free (fevent);
+ }
}
static void
@@ -10352,6 +10385,7 @@ gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view)
*/
static gboolean
gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
+ GdkDevice *device,
gboolean keybinding)
{
/* We only start interactive search if we have focus or the columns
@@ -10424,7 +10458,7 @@ gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
(entry_parent_class->grab_focus) (tree_view->priv->search_entry);
/* send focus-in event */
- send_focus_change (tree_view->priv->search_entry, TRUE);
+ send_focus_change (tree_view->priv->search_entry, device, TRUE);
/* search first matching iter */
gtk_tree_view_search_init (tree_view->priv->search_entry, tree_view);
@@ -10435,7 +10469,9 @@ gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
static gboolean
gtk_tree_view_start_interactive_search (GtkTreeView *tree_view)
{
- return gtk_tree_view_real_start_interactive_search (tree_view, TRUE);
+ return gtk_tree_view_real_start_interactive_search (tree_view,
+ gtk_get_current_event_device (),
+ TRUE);
}
/* this function returns the new width of the column being resized given
@@ -14075,7 +14111,8 @@ gtk_tree_view_get_search_position_func (GtkTreeView *tree_view)
static void
gtk_tree_view_search_dialog_hide (GtkWidget *search_dialog,
- GtkTreeView *tree_view)
+ GtkTreeView *tree_view,
+ GdkDevice *device)
{
if (tree_view->priv->disable_popdown)
return;
@@ -14095,10 +14132,10 @@ gtk_tree_view_search_dialog_hide (GtkWidget *search_dialog,
if (gtk_widget_get_visible (search_dialog))
{
/* send focus-in event */
- send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
+ send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), device, FALSE);
gtk_widget_hide (search_dialog);
gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
- send_focus_change (GTK_WIDGET (tree_view), TRUE);
+ send_focus_change (GTK_WIDGET (tree_view), device, TRUE);
}
}
@@ -14184,7 +14221,8 @@ gtk_tree_view_search_activate (GtkEntry *entry,
GtkRBTree *tree;
gtk_tree_view_search_dialog_hide (tree_view->priv->search_window,
- tree_view);
+ tree_view,
+ gtk_get_current_event_device ());
/* If we have a row selected and it's the cursor row, we activate
* the row XXX */
@@ -14225,7 +14263,7 @@ gtk_tree_view_search_delete_event (GtkWidget *widget,
{
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
- gtk_tree_view_search_dialog_hide (widget, tree_view);
+ gtk_tree_view_search_dialog_hide (widget, tree_view, NULL);
return TRUE;
}
@@ -14235,9 +14273,12 @@ gtk_tree_view_search_button_press_event (GtkWidget *widget,
GdkEventButton *event,
GtkTreeView *tree_view)
{
+ GdkDevice *keyb_device;
+
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
- gtk_tree_view_search_dialog_hide (widget, tree_view);
+ keyb_device = gdk_device_get_associated_device (event->device);
+ gtk_tree_view_search_dialog_hide (widget, tree_view, keyb_device);
if (event->window == tree_view->priv->bin_window)
gtk_tree_view_button_press (GTK_WIDGET (tree_view), event);
@@ -14294,7 +14335,8 @@ gtk_tree_view_search_key_press_event (GtkWidget *widget,
event->keyval == GDK_KP_Tab ||
event->keyval == GDK_ISO_Left_Tab))
{
- gtk_tree_view_search_dialog_hide (widget, tree_view);
+ gtk_tree_view_search_dialog_hide (widget, tree_view,
+ gdk_event_get_device ((GdkEvent *) event));
return TRUE;
}
diff --git a/gtk/gtktreeviewcolumn.c b/gtk/gtktreeviewcolumn.c
index 5c36375321..0d953de7af 100644
--- a/gtk/gtktreeviewcolumn.c
+++ b/gtk/gtktreeviewcolumn.c
@@ -1097,7 +1097,8 @@ gtk_tree_view_column_button_event (GtkWidget *widget,
(gint) ((GdkEventMotion *)event)->y)))
{
column->maybe_reordered = FALSE;
- _gtk_tree_view_column_start_drag (GTK_TREE_VIEW (column->tree_view), column);
+ _gtk_tree_view_column_start_drag (GTK_TREE_VIEW (column->tree_view), column,
+ event->motion.device);
return TRUE;
}
if (column->clickable == FALSE)
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 6cd6ba6250..b70cb2b693 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -376,6 +376,7 @@ static GQuark quark_aux_info = 0;
static GQuark quark_accel_path = 0;
static GQuark quark_accel_closures = 0;
static GQuark quark_event_mask = 0;
+static GQuark quark_device_event_mask = 0;
static GQuark quark_extension_event_mode = 0;
static GQuark quark_parent_window = 0;
static GQuark quark_pointer_window = 0;
@@ -472,6 +473,7 @@ gtk_widget_class_init (GtkWidgetClass *klass)
quark_accel_path = g_quark_from_static_string ("gtk-accel-path");
quark_accel_closures = g_quark_from_static_string ("gtk-accel-closures");
quark_event_mask = g_quark_from_static_string ("gtk-event-mask");
+ quark_device_event_mask = g_quark_from_static_string ("gtk-device-event-mask");
quark_extension_event_mode = g_quark_from_static_string ("gtk-extension-event-mode");
quark_parent_window = g_quark_from_static_string ("gtk-parent-window");
quark_pointer_window = g_quark_from_static_string ("gtk-pointer-window");
@@ -3521,6 +3523,9 @@ gtk_widget_realize (GtkWidget *widget)
mode = gtk_widget_get_extension_events (widget);
if (mode != GDK_EXTENSION_EVENTS_NONE)
gtk_widget_set_extension_events_internal (widget, mode, NULL);
+
+ if ((GTK_WIDGET_FLAGS (widget) & GTK_MULTIDEVICE) != 0)
+ gdk_window_set_support_multidevice (widget->window, TRUE);
}
}
@@ -5655,6 +5660,62 @@ _gtk_widget_set_has_grab (GtkWidget *widget,
}
/**
+ * gtk_widget_device_is_shadowed:
+ * @widget: a #GtkWidget
+ * @device: a #GdkDevice
+ *
+ * Returns %TRUE if @device has been shadowed by a GTK+
+ * device grab on another widget, so it would stop sending
+ * events to @widget. This may be used in the
+ * #GtkWidget::grab-notify signal to check for specific
+ * devices. See gtk_device_grab_add().
+ *
+ * Returns: %TRUE if there is an ongoing grab on @device
+ * by another #GtkWidget than @widget.
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_widget_device_is_shadowed (GtkWidget *widget,
+ GdkDevice *device)
+{
+ GtkWindowGroup *group;
+ GtkWidget *grab_widget, *toplevel;
+
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+ g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
+
+ if (!gtk_widget_get_realized (widget))
+ return TRUE;
+
+ toplevel = gtk_widget_get_toplevel (widget);
+
+ if (GTK_IS_WINDOW (toplevel))
+ group = gtk_window_get_group (GTK_WINDOW (toplevel));
+ else
+ group = gtk_window_get_group (NULL);
+
+ grab_widget = gtk_window_group_get_current_device_grab (group, device);
+
+ /* Widget not inside the hierarchy of grab_widget */
+ if (grab_widget &&
+ widget != grab_widget &&
+ !gtk_widget_is_ancestor (widget, grab_widget))
+ return TRUE;
+
+ if (group->grabs)
+ {
+ grab_widget = group->grabs->data;
+
+ if (widget != grab_widget &&
+ !gtk_widget_is_ancestor (widget, grab_widget))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
* gtk_widget_set_name:
* @widget: a #GtkWidget
* @name: name for the widget
@@ -7890,10 +7951,53 @@ gtk_widget_set_events (GtkWidget *widget,
g_object_notify (G_OBJECT (widget), "events");
}
+/**
+ * gtk_widget_set_device_events:
+ * @widget: a #GtkWidget
+ * #device: a #GdkDevice
+ * @events: event mask
+ *
+ * Sets the device event mask (see #GdkEventMask) for a widget. The event
+ * mask determines which events a widget will receive from @device. Keep
+ * in mind that different widgets have different default event masks, and by
+ * changing the event mask you may disrupt a widget's functionality,
+ * so be careful. This function must be called while a widget is
+ * unrealized. Consider gtk_widget_add_device_events() for widgets that are
+ * already realized, or if you want to preserve the existing event
+ * mask. This function can't be used with #GTK_NO_WINDOW widgets;
+ * to get events on those widgets, place them inside a #GtkEventBox
+ * and receive events on the event box.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_set_device_events (GtkWidget *widget,
+ GdkDevice *device,
+ GdkEventMask events)
+{
+ GHashTable *device_events;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (GDK_IS_DEVICE (device));
+ g_return_if_fail (!gtk_widget_get_realized (widget));
+
+ device_events = g_object_get_qdata (G_OBJECT (widget), quark_device_event_mask);
+
+ if (G_UNLIKELY (!device_events))
+ {
+ device_events = g_hash_table_new (NULL, NULL);
+ g_object_set_qdata_full (G_OBJECT (widget), quark_device_event_mask, device_events,
+ (GDestroyNotify) g_hash_table_unref);
+ }
+
+ g_hash_table_insert (device_events, device, GUINT_TO_POINTER (events));
+}
+
static void
gtk_widget_add_events_internal (GtkWidget *widget,
- gint events,
- GList *window_list)
+ GdkDevice *device,
+ gint events,
+ GList *window_list)
{
GList *l;
@@ -7904,15 +8008,18 @@ gtk_widget_add_events_internal (GtkWidget *widget,
gdk_window_get_user_data (window, &user_data);
if (user_data == widget)
- {
- GList *children;
+ {
+ GList *children;
- gdk_window_set_events (window, gdk_window_get_events (window) | events);
+ if (device)
+ gdk_window_set_device_events (window, device, gdk_window_get_events (window) | events);
+ else
+ gdk_window_set_events (window, gdk_window_get_events (window) | events);
- children = gdk_window_get_children (window);
- gtk_widget_add_events_internal (widget, events, children);
- g_list_free (children);
- }
+ children = gdk_window_get_children (window);
+ gtk_widget_add_events_internal (widget, device, events, children);
+ g_list_free (children);
+ }
}
}
@@ -7945,7 +8052,60 @@ gtk_widget_add_events (GtkWidget *widget,
else
window_list = g_list_prepend (NULL, widget->window);
- gtk_widget_add_events_internal (widget, events, window_list);
+ gtk_widget_add_events_internal (widget, NULL, events, window_list);
+
+ g_list_free (window_list);
+ }
+
+ g_object_notify (G_OBJECT (widget), "events");
+}
+
+/**
+ * gtk_widget_add_device_events:
+ * @widget: a #GtkWidget
+ * @device: a #GdkDevice
+ * @events: an event mask, see #GdkEventMask
+ *
+ * Adds the device events in the bitfield @events to the event mask for
+ * @widget. See gtk_widget_set_device_events() for details.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_add_device_events (GtkWidget *widget,
+ GdkDevice *device,
+ GdkEventMask events)
+{
+ GdkEventMask old_events;
+ GHashTable *device_events;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (GDK_IS_DEVICE (device));
+
+ old_events = gtk_widget_get_device_events (widget, device);
+
+ device_events = g_object_get_qdata (G_OBJECT (widget), quark_device_event_mask);
+
+ if (G_UNLIKELY (!device_events))
+ {
+ device_events = g_hash_table_new (NULL, NULL);
+ g_object_set_qdata_full (G_OBJECT (widget), quark_device_event_mask, device_events,
+ (GDestroyNotify) g_hash_table_unref);
+ }
+
+ g_hash_table_insert (device_events, device,
+ GUINT_TO_POINTER (old_events | events));
+
+ if (gtk_widget_get_realized (widget))
+ {
+ GList *window_list;
+
+ if (!gtk_widget_get_has_window (widget))
+ window_list = gdk_window_get_children (widget->window);
+ else
+ window_list = g_list_prepend (NULL, widget->window);
+
+ gtk_widget_add_events_internal (widget, device, events, window_list);
g_list_free (window_list);
}
@@ -8170,6 +8330,35 @@ gtk_widget_get_events (GtkWidget *widget)
}
/**
+ * gtk_widget_get_device_events:
+ * @widget: a #GtkWidget
+ * @device: a #GdkDevice
+ *
+ * Returns the events mask for the widget corresponding to an specific device. These
+ * are the events that the widget will receive when @device operates on it.
+ *
+ * Returns: device event mask for @widget
+ *
+ * Since: 3.0
+ **/
+GdkEventMask
+gtk_widget_get_device_events (GtkWidget *widget,
+ GdkDevice *device)
+{
+ GHashTable *device_events;
+
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
+ g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
+
+ device_events = g_object_get_qdata (G_OBJECT (widget), quark_device_event_mask);
+
+ if (!device_events)
+ return 0;
+
+ return GPOINTER_TO_UINT (g_hash_table_lookup (device_events, device));
+}
+
+/**
* gtk_widget_get_extension_events:
* @widget: a #GtkWidget
*
@@ -8755,59 +8944,147 @@ _gtk_widget_peek_colormap (void)
}
/*
- * _gtk_widget_set_pointer_window:
+ * _gtk_widget_set_device_window:
* @widget: a #GtkWidget.
- * @pointer_window: the new pointer window.
+ * @device: a #GdkDevice.
+ * @window: the new device window.
*
- * Sets pointer window for @widget. Does not ref @pointer_window.
+ * Sets pointer window for @widget and @device. Does not ref @window.
* Actually stores it on the #GdkScreen, but you don't need to know that.
*/
void
-_gtk_widget_set_pointer_window (GtkWidget *widget,
- GdkWindow *pointer_window)
+_gtk_widget_set_device_window (GtkWidget *widget,
+ GdkDevice *device,
+ GdkWindow *window)
{
+ GdkScreen *screen;
+ GHashTable *device_window;
+
g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (GDK_IS_DEVICE (device));
+ g_return_if_fail (!window || GDK_IS_WINDOW (window));
- if (gtk_widget_get_realized (widget))
- {
- GdkScreen *screen = gdk_drawable_get_screen (widget->window);
+ if (!gtk_widget_get_realized (widget))
+ return;
+
+ screen = gdk_drawable_get_screen (widget->window);
+ device_window = g_object_get_qdata (G_OBJECT (screen), quark_pointer_window);
- g_object_set_qdata (G_OBJECT (screen), quark_pointer_window,
- pointer_window);
+ if (G_UNLIKELY (!device_window))
+ {
+ device_window = g_hash_table_new (NULL, NULL);
+ g_object_set_qdata_full (G_OBJECT (screen),
+ quark_pointer_window,
+ device_window,
+ (GDestroyNotify) g_hash_table_destroy);
}
+
+ if (window)
+ g_hash_table_insert (device_window, device, window);
+ else
+ g_hash_table_remove (device_window, device);
}
/*
- * _gtk_widget_get_pointer_window:
+ * _gtk_widget_get_device_window:
* @widget: a #GtkWidget.
+ * @device: a #GdkDevice.
*
- * Return value: the pointer window set on the #GdkScreen @widget is attached
+ * Return value: the device window set on the #GdkScreen @widget is attached
* to, or %NULL.
*/
GdkWindow *
-_gtk_widget_get_pointer_window (GtkWidget *widget)
+_gtk_widget_get_device_window (GtkWidget *widget,
+ GdkDevice *device)
{
+ GdkScreen *screen;
+ GHashTable *device_window;
+ GdkWindow *window;
+ GtkWidget *w;
+
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+ g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
- if (gtk_widget_get_realized (widget))
+ if (!gtk_widget_get_realized (widget))
+ return NULL;
+
+ screen = gdk_drawable_get_screen (widget->window);
+ device_window = g_object_get_qdata (G_OBJECT (screen), quark_pointer_window);
+
+ if (G_UNLIKELY (!device_window))
+ return NULL;
+
+ window = g_hash_table_lookup (device_window, device);
+
+ if (!window)
+ return NULL;
+
+ gdk_window_get_user_data (window, (gpointer *) &w);
+
+ if (widget != w)
+ return NULL;
+
+ return window;
+}
+
+/*
+ * _gtk_widget_list_devices:
+ * @widget: a #GtkWidget.
+ *
+ * Returns the list of #GdkDevices that is currently on top of any widget #GdkWindow.
+ * Free the list with g_list_free(), the elements are owned by GTK+ and must not
+ * be freed.
+ */
+GList *
+_gtk_widget_list_devices (GtkWidget *widget)
+{
+ GdkScreen *screen;
+ GHashTableIter iter;
+ GHashTable *device_window;
+ GList *devices = NULL;
+ gpointer key, value;
+
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+ if (!gtk_widget_get_realized (widget))
+ return NULL;
+
+ screen = gdk_drawable_get_screen (widget->window);
+ device_window = g_object_get_qdata (G_OBJECT (screen), quark_pointer_window);
+
+ if (G_UNLIKELY (!device_window))
+ return NULL;
+
+ g_hash_table_iter_init (&iter, device_window);
+
+ while (g_hash_table_iter_next (&iter, &key, &value))
{
- GdkScreen *screen = gdk_drawable_get_screen (widget->window);
+ GdkDevice *device = key;
+ GdkWindow *window = value;
+ GtkWidget *w;
- return g_object_get_qdata (G_OBJECT (screen), quark_pointer_window);
+ if (window)
+ {
+ gdk_window_get_user_data (window, (gpointer *) &w);
+
+ if (widget == w)
+ devices = g_list_prepend (devices, device);
+ }
}
- return NULL;
+ return devices;
}
static void
-synth_crossing (GtkWidget *widget,
- GdkEventType type,
- GdkWindow *window,
- GdkCrossingMode mode,
- GdkNotifyType detail)
+synth_crossing (GtkWidget *widget,
+ GdkEventType type,
+ GdkWindow *window,
+ GdkDevice *device,
+ GdkCrossingMode mode,
+ GdkNotifyType detail)
{
GdkEvent *event;
-
+
event = gdk_event_new (type);
event->crossing.window = g_object_ref (window);
@@ -8820,6 +9097,7 @@ synth_crossing (GtkWidget *widget,
event->crossing.detail = detail;
event->crossing.focus = FALSE;
event->crossing.state = 0;
+ gdk_event_set_device (event, device);
if (!widget)
widget = gtk_get_event_widget (event);
@@ -8831,32 +9109,6 @@ synth_crossing (GtkWidget *widget,
}
/*
- * _gtk_widget_is_pointer_widget:
- * @widget: a #GtkWidget
- *
- * Returns %TRUE if the pointer window belongs to @widget.
- */
-gboolean
-_gtk_widget_is_pointer_widget (GtkWidget *widget)
-{
- if (GTK_WIDGET_HAS_POINTER (widget))
- {
- GdkWindow *win;
- GtkWidget *wid;
-
- win = _gtk_widget_get_pointer_window (widget);
- if (win)
- {
- gdk_window_get_user_data (win, (gpointer *)&wid);
- if (wid == widget)
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-/*
* _gtk_widget_synthesize_crossing:
* @from: the #GtkWidget the virtual pointer is leaving.
* @to: the #GtkWidget the virtual pointer is moving to.
@@ -8888,20 +9140,30 @@ _gtk_widget_is_pointer_widget (GtkWidget *widget)
* - enter notify on real pointer window, detail Ancestor
*/
void
-_gtk_widget_synthesize_crossing (GtkWidget *from,
- GtkWidget *to,
- GdkCrossingMode mode)
+_gtk_widget_synthesize_crossing (GtkWidget *from,
+ GtkWidget *to,
+ GdkDevice *device,
+ GdkCrossingMode mode)
{
GdkWindow *from_window = NULL, *to_window = NULL;
g_return_if_fail (from != NULL || to != NULL);
if (from != NULL)
- from_window = GTK_WIDGET_HAS_POINTER (from)
- ? _gtk_widget_get_pointer_window (from) : from->window;
+ {
+ from_window = _gtk_widget_get_device_window (from, device);
+
+ if (!from_window)
+ from_window = from->window;
+ }
+
if (to != NULL)
- to_window = GTK_WIDGET_HAS_POINTER (to)
- ? _gtk_widget_get_pointer_window (to) : to->window;
+ {
+ to_window = _gtk_widget_get_device_window (to, device);
+
+ if (!to_window)
+ to_window = to->window;
+ }
if (from_window == NULL && to_window == NULL)
;
@@ -8919,11 +9181,11 @@ _gtk_widget_synthesize_crossing (GtkWidget *from,
}
synth_crossing (from, GDK_LEAVE_NOTIFY, from_window,
- mode, GDK_NOTIFY_ANCESTOR);
+ device, mode, GDK_NOTIFY_ANCESTOR);
for (list = g_list_last (from_ancestors); list; list = list->prev)
{
synth_crossing (NULL, GDK_LEAVE_NOTIFY, (GdkWindow *) list->data,
- mode, GDK_NOTIFY_VIRTUAL);
+ device, mode, GDK_NOTIFY_VIRTUAL);
}
/* XXX: enter/inferior on root window? */
@@ -8948,10 +9210,10 @@ _gtk_widget_synthesize_crossing (GtkWidget *from,
for (list = to_ancestors; list; list = list->next)
{
synth_crossing (NULL, GDK_ENTER_NOTIFY, (GdkWindow *) list->data,
- mode, GDK_NOTIFY_VIRTUAL);
+ device, mode, GDK_NOTIFY_VIRTUAL);
}
synth_crossing (to, GDK_ENTER_NOTIFY, to_window,
- mode, GDK_NOTIFY_ANCESTOR);
+ device, mode, GDK_NOTIFY_ANCESTOR);
g_list_free (to_ancestors);
}
@@ -8985,25 +9247,25 @@ _gtk_widget_synthesize_crossing (GtkWidget *from,
{
if (mode != GDK_CROSSING_GTK_UNGRAB)
synth_crossing (from, GDK_LEAVE_NOTIFY, from_window,
- mode, GDK_NOTIFY_INFERIOR);
+ device, mode, GDK_NOTIFY_INFERIOR);
for (list = to_ancestors; list; list = list->next)
synth_crossing (NULL, GDK_ENTER_NOTIFY, (GdkWindow *) list->data,
- mode, GDK_NOTIFY_VIRTUAL);
+ device, mode, GDK_NOTIFY_VIRTUAL);
synth_crossing (to, GDK_ENTER_NOTIFY, to_window,
- mode, GDK_NOTIFY_ANCESTOR);
+ device, mode, GDK_NOTIFY_ANCESTOR);
}
else if (from_ancestor == to_window)
{
synth_crossing (from, GDK_LEAVE_NOTIFY, from_window,
- mode, GDK_NOTIFY_ANCESTOR);
+ device, mode, GDK_NOTIFY_ANCESTOR);
for (list = g_list_last (from_ancestors); list; list = list->prev)
{
synth_crossing (NULL, GDK_LEAVE_NOTIFY, (GdkWindow *) list->data,
- mode, GDK_NOTIFY_VIRTUAL);
+ device, mode, GDK_NOTIFY_VIRTUAL);
}
if (mode != GDK_CROSSING_GTK_GRAB)
synth_crossing (to, GDK_ENTER_NOTIFY, to_window,
- mode, GDK_NOTIFY_INFERIOR);
+ device, mode, GDK_NOTIFY_INFERIOR);
}
else
{
@@ -9016,20 +9278,20 @@ _gtk_widget_synthesize_crossing (GtkWidget *from,
}
synth_crossing (from, GDK_LEAVE_NOTIFY, from_window,
- mode, GDK_NOTIFY_NONLINEAR);
+ device, mode, GDK_NOTIFY_NONLINEAR);
for (list = g_list_last (from_ancestors); list; list = list->prev)
{
synth_crossing (NULL, GDK_LEAVE_NOTIFY, (GdkWindow *) list->data,
- mode, GDK_NOTIFY_NONLINEAR_VIRTUAL);
+ device, mode, GDK_NOTIFY_NONLINEAR_VIRTUAL);
}
for (list = to_ancestors; list; list = list->next)
{
synth_crossing (NULL, GDK_ENTER_NOTIFY, (GdkWindow *) list->data,
- mode, GDK_NOTIFY_NONLINEAR_VIRTUAL);
+ device, mode, GDK_NOTIFY_NONLINEAR_VIRTUAL);
}
synth_crossing (to, GDK_ENTER_NOTIFY, to_window,
- mode, GDK_NOTIFY_NONLINEAR);
+ device, mode, GDK_NOTIFY_NONLINEAR);
}
g_list_free (from_ancestors);
g_list_free (to_ancestors);
@@ -9091,15 +9353,41 @@ gtk_widget_propagate_state (GtkWidget *widget,
g_signal_emit (widget, widget_signals[STATE_CHANGED], 0, old_state);
- if (GTK_WIDGET_HAS_POINTER (widget) && !GTK_WIDGET_SHADOWED (widget))
- {
- if (!gtk_widget_is_sensitive (widget))
- _gtk_widget_synthesize_crossing (widget, NULL,
- GDK_CROSSING_STATE_CHANGED);
- else if (old_state == GTK_STATE_INSENSITIVE)
- _gtk_widget_synthesize_crossing (NULL, widget,
- GDK_CROSSING_STATE_CHANGED);
- }
+ if (!GTK_WIDGET_SHADOWED (widget))
+ {
+ GList *event_windows = NULL;
+ GList *devices, *d;
+
+ devices = _gtk_widget_list_devices (widget);
+
+ for (d = devices; d; d = d->next)
+ {
+ GdkWindow *window;
+ GdkDevice *device;
+
+ device = d->data;
+ window = _gtk_widget_get_device_window (widget, device);
+
+ /* Do not propagate more than once to the
+ * same window if non-multidevice aware.
+ */
+ if (!gdk_window_get_support_multidevice (window) &&
+ g_list_find (event_windows, window))
+ continue;
+
+ if (!gtk_widget_is_sensitive (widget))
+ _gtk_widget_synthesize_crossing (widget, NULL, d->data,
+ GDK_CROSSING_STATE_CHANGED);
+ else if (old_state == GTK_STATE_INSENSITIVE)
+ _gtk_widget_synthesize_crossing (NULL, widget, d->data,
+ GDK_CROSSING_STATE_CHANGED);
+
+ event_windows = g_list_prepend (event_windows, window);
+ }
+
+ g_list_free (event_windows);
+ g_list_free (devices);
+ }
if (GTK_IS_CONTAINER (widget))
{
@@ -11175,6 +11463,56 @@ gtk_widget_get_window (GtkWidget *widget)
return widget->window;
}
+/**
+ * gtk_widget_get_support_multidevice:
+ * @widget: a #GtkWidget
+ *
+ * Returns %TRUE if @widget is multiple pointer aware. See
+ * gtk_widget_set_support_multidevice() for more information.
+ *
+ * Returns: %TRUE is @widget is multidevice aware.
+ **/
+gboolean
+gtk_widget_get_support_multidevice (GtkWidget *widget)
+{
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+
+ return GTK_WIDGET_FLAGS (widget) & GTK_MULTIDEVICE;
+}
+
+/**
+ * gtk_widget_set_support_multidevice:
+ * @widget: a #GtkWidget
+ * @support_multidevice: %TRUE to support input from multiple devices.
+ *
+ * Enables or disables multiple pointer awareness. If this setting is %TRUE,
+ * @widget will start receiving multiple, per device enter/leave events. Note
+ * that if custom #GdkWindow<!-- -->s are created in #GtkWidget::realize,
+ * gdk_window_set_support_multidevice() will have to be called manually on them.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_set_support_multidevice (GtkWidget *widget,
+ gboolean support_multidevice)
+{
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ if (support_multidevice)
+ {
+ GTK_WIDGET_SET_FLAGS (widget, GTK_MULTIDEVICE);
+ gtk_widget_set_extension_events (widget, GDK_EXTENSION_EVENTS_ALL);
+ }
+ else
+ {
+ GTK_WIDGET_UNSET_FLAGS (widget, GTK_MULTIDEVICE);
+ gtk_widget_set_extension_events (widget, GDK_EXTENSION_EVENTS_NONE);
+ }
+
+ if (gtk_widget_get_realized (widget))
+ gdk_window_set_support_multidevice (widget->window, support_multidevice);
+}
+
static void
_gtk_widget_set_has_focus (GtkWidget *widget,
gboolean has_focus)
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index 38b2dca9c6..f42fa252c2 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -118,7 +118,8 @@ typedef enum
GTK_APP_PAINTABLE = 1 << 19,
GTK_RECEIVES_DEFAULT = 1 << 20,
GTK_DOUBLE_BUFFERED = 1 << 21,
- GTK_NO_SHOW_ALL = 1 << 22
+ GTK_NO_SHOW_ALL = 1 << 22,
+ GTK_MULTIDEVICE = 1 << 23
} GtkWidgetFlags;
/* Kinds of widget-specific help */
@@ -652,6 +653,10 @@ gboolean gtk_widget_get_receives_default (GtkWidget *widget);
gboolean gtk_widget_has_grab (GtkWidget *widget);
+gboolean gtk_widget_device_is_shadowed (GtkWidget *widget,
+ GdkDevice *device);
+
+
void gtk_widget_set_name (GtkWidget *widget,
const gchar *name);
G_CONST_RETURN gchar* gtk_widget_get_name (GtkWidget *widget);
@@ -733,6 +738,12 @@ void gtk_widget_set_events (GtkWidget *widget,
gint events);
void gtk_widget_add_events (GtkWidget *widget,
gint events);
+void gtk_widget_set_device_events (GtkWidget *widget,
+ GdkDevice *device,
+ GdkEventMask events);
+void gtk_widget_add_device_events (GtkWidget *widget,
+ GdkDevice *device,
+ GdkEventMask events);
void gtk_widget_set_extension_events (GtkWidget *widget,
GdkExtensionMode mode);
@@ -753,6 +764,11 @@ GtkClipboard *gtk_widget_get_clipboard (GtkWidget *widget,
GdkPixmap * gtk_widget_get_snapshot (GtkWidget *widget,
GdkRectangle *clip_rect);
+/* Multidevice support */
+gboolean gtk_widget_get_support_multidevice (GtkWidget *widget);
+void gtk_widget_set_support_multidevice (GtkWidget *widget,
+ gboolean support_multidevice);
+
/* Accessibility support */
AtkObject* gtk_widget_get_accessible (GtkWidget *widget);
@@ -766,6 +782,8 @@ void gtk_widget_set_colormap (GtkWidget *widget,
GdkColormap *colormap);
gint gtk_widget_get_events (GtkWidget *widget);
+GdkEventMask gtk_widget_get_device_events (GtkWidget *widget,
+ GdkDevice *device);
void gtk_widget_get_pointer (GtkWidget *widget,
gint *x,
gint *y);
@@ -959,12 +977,16 @@ void _gtk_widget_propagate_screen_changed (GtkWidget *widget,
GdkScreen *previous_screen);
void _gtk_widget_propagate_composited_changed (GtkWidget *widget);
-void _gtk_widget_set_pointer_window (GtkWidget *widget,
+void _gtk_widget_set_device_window (GtkWidget *widget,
+ GdkDevice *device,
GdkWindow *pointer_window);
-GdkWindow *_gtk_widget_get_pointer_window (GtkWidget *widget);
-gboolean _gtk_widget_is_pointer_widget (GtkWidget *widget);
+GdkWindow *_gtk_widget_get_device_window (GtkWidget *widget,
+ GdkDevice *device);
+GList * _gtk_widget_list_devices (GtkWidget *widget);
+
void _gtk_widget_synthesize_crossing (GtkWidget *from,
GtkWidget *to,
+ GdkDevice *device,
GdkCrossingMode mode);
GdkColormap* _gtk_widget_peek_colormap (void);
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index 6ac97069d6..eab1ff5132 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -167,6 +167,7 @@ struct _GtkWindowGeometryInfo
};
#define GTK_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_WINDOW, GtkWindowPrivate))
+#define GTK_WINDOW_GROUP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_WINDOW_GROUP, GtkWindowGroupPrivate))
typedef struct _GtkWindowPrivate GtkWindowPrivate;
@@ -199,6 +200,21 @@ struct _GtkWindowPrivate
gchar *startup_id;
};
+typedef struct _GtkDeviceGrabInfo GtkDeviceGrabInfo;
+typedef struct _GtkWindowGroupPrivate GtkWindowGroupPrivate;
+
+struct _GtkDeviceGrabInfo
+{
+ GtkWidget *widget;
+ GdkDevice *device;
+ guint block_others : 1;
+};
+
+struct _GtkWindowGroupPrivate
+{
+ GSList *device_grabs;
+};
+
static void gtk_window_dispose (GObject *object);
static void gtk_window_destroy (GtkObject *object);
static void gtk_window_finalize (GObject *object);
@@ -5251,17 +5267,43 @@ static void
do_focus_change (GtkWidget *widget,
gboolean in)
{
- GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
-
- fevent->focus_change.type = GDK_FOCUS_CHANGE;
- fevent->focus_change.window = widget->window;
- fevent->focus_change.in = in;
- if (widget->window)
- g_object_ref (widget->window);
+ GdkDeviceManager *device_manager;
+ GList *devices, *d;
+
+ device_manager = gdk_display_get_device_manager (gtk_widget_get_display (widget));
+ devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+ devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_SLAVE));
+ devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_FLOATING));
+
+ for (d = devices; d; d = d->next)
+ {
+ GdkDevice *dev = d->data;
+ GdkEvent *fevent;
- gtk_widget_send_focus_change (widget, fevent);
+ if (dev->source != GDK_SOURCE_KEYBOARD)
+ continue;
- gdk_event_free (fevent);
+ /* Skip non-master keyboards that haven't
+ * selected for events from this window
+ */
+ if (gdk_device_get_device_type (dev) != GDK_DEVICE_TYPE_MASTER &&
+ widget->window &&
+ !gdk_window_get_device_events (widget->window, dev))
+ continue;
+
+ fevent = gdk_event_new (GDK_FOCUS_CHANGE);
+
+ fevent->focus_change.type = GDK_FOCUS_CHANGE;
+ fevent->focus_change.window = widget->window;
+ if (widget->window)
+ g_object_ref (widget->window);
+ fevent->focus_change.in = in;
+ gdk_event_set_device (fevent, dev);
+
+ gtk_widget_send_focus_change (widget, fevent);
+
+ gdk_event_free (fevent);
+ }
}
static gint
@@ -5753,11 +5795,16 @@ get_monitor_containing_pointer (GtkWindow *window)
gint monitor_num;
GdkScreen *window_screen;
GdkScreen *pointer_screen;
+ GdkDisplay *display;
+ GdkDevice *pointer;
window_screen = gtk_window_check_screen (window);
- gdk_display_get_pointer (gdk_screen_get_display (window_screen),
- &pointer_screen,
- &px, &py, NULL);
+ display = gdk_screen_get_display (window_screen);
+ pointer = gdk_display_get_core_pointer (display);
+
+ gdk_display_get_device_state (display, pointer,
+ &pointer_screen,
+ &px, &py, NULL);
if (pointer_screen == window_screen)
monitor_num = gdk_screen_get_monitor_at_point (pointer_screen, px, py);
@@ -5940,12 +5987,16 @@ gtk_window_compute_configure_request (GtkWindow *window,
gint screen_height = gdk_screen_get_height (screen);
gint monitor_num;
GdkRectangle monitor;
+ GdkDisplay *display;
+ GdkDevice *pointer;
GdkScreen *pointer_screen;
gint px, py;
-
- gdk_display_get_pointer (gdk_screen_get_display (screen),
- &pointer_screen,
- &px, &py, NULL);
+
+ display = gdk_screen_get_display (screen);
+ pointer = gdk_display_get_core_pointer (display);
+ gdk_display_get_device_state (display, pointer,
+ &pointer_screen,
+ &px, &py, NULL);
if (pointer_screen == screen)
monitor_num = gdk_screen_get_monitor_at_point (screen, px, py);
@@ -7594,6 +7645,7 @@ gtk_window_has_toplevel_focus (GtkWindow *window)
static void
gtk_window_group_class_init (GtkWindowGroupClass *klass)
{
+ g_type_class_add_private (klass, sizeof (GtkWindowGroupPrivate));
}
GType
@@ -7641,6 +7693,8 @@ static void
window_group_cleanup_grabs (GtkWindowGroup *group,
GtkWindow *window)
{
+ GtkWindowGroupPrivate *priv;
+ GtkDeviceGrabInfo *info;
GSList *tmp_list;
GSList *to_remove = NULL;
@@ -7658,6 +7712,27 @@ window_group_cleanup_grabs (GtkWindowGroup *group,
g_object_unref (to_remove->data);
to_remove = g_slist_delete_link (to_remove, to_remove);
}
+
+ priv = GTK_WINDOW_GROUP_GET_PRIVATE (group);
+ tmp_list = priv->device_grabs;
+
+ while (tmp_list)
+ {
+ info = tmp_list->data;
+
+ if (gtk_widget_get_toplevel (info->widget) == (GtkWidget *) window)
+ to_remove = g_slist_prepend (to_remove, info);
+
+ tmp_list = tmp_list->next;
+ }
+
+ while (to_remove)
+ {
+ info = to_remove->data;
+
+ gtk_device_grab_remove (info->widget, info->device);
+ to_remove = g_slist_delete_link (to_remove, to_remove);
+ }
}
/**
@@ -7784,6 +7859,131 @@ _gtk_window_group_get_current_grab (GtkWindowGroup *window_group)
return NULL;
}
+void
+_gtk_window_group_add_device_grab (GtkWindowGroup *window_group,
+ GtkWidget *widget,
+ GdkDevice *device,
+ gboolean block_others)
+{
+ GtkWindowGroupPrivate *priv;
+ GtkDeviceGrabInfo *info;
+
+ priv = GTK_WINDOW_GROUP_GET_PRIVATE (window_group);
+
+ info = g_slice_new0 (GtkDeviceGrabInfo);
+ info->widget = widget;
+ info->device = device;
+ info->block_others = block_others;
+
+ priv->device_grabs = g_slist_prepend (priv->device_grabs, info);
+}
+
+void
+_gtk_window_group_remove_device_grab (GtkWindowGroup *window_group,
+ GtkWidget *widget,
+ GdkDevice *device)
+{
+ GtkWindowGroupPrivate *priv;
+ GtkDeviceGrabInfo *info;
+ GSList *list, *node = NULL;
+ GdkDevice *other_device;
+
+ priv = GTK_WINDOW_GROUP_GET_PRIVATE (window_group);
+ other_device = gdk_device_get_associated_device (device);
+ list = priv->device_grabs;
+
+ while (list)
+ {
+ info = list->data;
+
+ if (info->widget == widget &&
+ (info->device == device ||
+ info->device == other_device))
+ {
+ node = list;
+ break;
+ }
+
+ list = list->next;
+ }
+
+ if (node)
+ {
+ info = node->data;
+
+ priv->device_grabs = g_slist_delete_link (priv->device_grabs, node);
+ g_slice_free (GtkDeviceGrabInfo, info);
+ }
+}
+
+/**
+ * gtk_window_group_get_current_device_grab:
+ * @window_group: a #GtkWindowGroup
+ * @device: a #GdkDevice
+ *
+ * Returns the current grab widget for @device, or %NULL if none.
+ *
+ * Returns: The grab widget, or %NULL
+ **/
+GtkWidget *
+gtk_window_group_get_current_device_grab (GtkWindowGroup *window_group,
+ GdkDevice *device)
+{
+ GtkWindowGroupPrivate *priv;
+ GtkDeviceGrabInfo *info;
+ GdkDevice *other_device;
+ GSList *list;
+
+ priv = GTK_WINDOW_GROUP_GET_PRIVATE (window_group);
+ list = priv->device_grabs;
+ other_device = gdk_device_get_associated_device (device);
+
+ while (list)
+ {
+ info = list->data;
+ list = list->next;
+
+ if (info->device == device ||
+ info->device == other_device)
+ return info->widget;
+ }
+
+ return NULL;
+}
+
+gboolean
+_gtk_window_group_widget_is_blocked_for_device (GtkWindowGroup *window_group,
+ GtkWidget *widget,
+ GdkDevice *device)
+{
+ GtkWindowGroupPrivate *priv;
+ GtkDeviceGrabInfo *info;
+ GdkDevice *other_device;
+ GSList *list;
+
+ priv = GTK_WINDOW_GROUP_GET_PRIVATE (window_group);
+ other_device = gdk_device_get_associated_device (device);
+ list = priv->device_grabs;
+
+ while (list)
+ {
+ info = list->data;
+ list = list->next;
+
+ /* Look for blocking grabs on other device pairs
+ * that have the passed widget within the GTK+ grab.
+ */
+ if (info->block_others &&
+ info->device != device &&
+ info->device != other_device &&
+ (info->widget == widget ||
+ gtk_widget_is_ancestor (widget, info->widget)))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
/*
Derived from XParseGeometry() in XFree86
diff --git a/gtk/gtkwindow.h b/gtk/gtkwindow.h
index 22753fcd0e..fec9d99f19 100644
--- a/gtk/gtkwindow.h
+++ b/gtk/gtkwindow.h
@@ -395,6 +395,9 @@ void gtk_window_group_remove_window (GtkWindowGroup *window_grou
GtkWindow *window);
GList * gtk_window_group_list_windows (GtkWindowGroup *window_group);
+GtkWidget * gtk_window_group_get_current_device_grab (GtkWindowGroup *window_group,
+ GdkDevice *device);
+
/* --- internal functions --- */
void _gtk_window_internal_set_focus (GtkWindow *window,
@@ -412,6 +415,17 @@ void _gtk_window_constrain_size (GtkWindow *window,
gint *new_width,
gint *new_height);
GtkWidget *_gtk_window_group_get_current_grab (GtkWindowGroup *window_group);
+void _gtk_window_group_add_device_grab (GtkWindowGroup *window_group,
+ GtkWidget *widget,
+ GdkDevice *device,
+ gboolean block_others);
+void _gtk_window_group_remove_device_grab (GtkWindowGroup *window_group,
+ GtkWidget *widget,
+ GdkDevice *device);
+
+gboolean _gtk_window_group_widget_is_blocked_for_device (GtkWindowGroup *window_group,
+ GtkWidget *widget,
+ GdkDevice *device);
void _gtk_window_set_has_toplevel_focus (GtkWindow *window,
gboolean has_toplevel_focus);