From bd4609b14042a91646cd9057764eecfbc6faf42b Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 25 May 2010 18:38:44 -0400 Subject: Merge the xi2-for-master branch --- gtk/gtk.symbols | 12 ++ gtk/gtkaboutdialog.c | 42 ++-- gtk/gtkbutton.c | 34 ++- gtk/gtkcellrendereraccel.c | 89 ++++++-- gtk/gtkcolorsel.c | 98 ++++++--- gtk/gtkcombobox.c | 122 ++++++++--- gtk/gtkcombobox.h | 2 + gtk/gtkdnd.c | 155 +++++++++----- gtk/gtkentry.c | 15 +- gtk/gtkentrycompletion.c | 34 ++- gtk/gtkentryprivate.h | 5 +- gtk/gtkhandlebox.c | 38 ++-- gtk/gtkiconview.c | 21 +- gtk/gtklabel.c | 9 +- gtk/gtkmain.c | 258 +++++++++++++++++++---- gtk/gtkmain.h | 7 + gtk/gtkmarshalers.list | 1 + gtk/gtkmenu.c | 246 ++++++++++++++++----- gtk/gtkmenu.h | 9 + gtk/gtkmenushell.c | 51 ++++- gtk/gtkmenushell.h | 8 + gtk/gtknotebook.c | 13 +- gtk/gtkpaned.c | 31 ++- gtk/gtkplug-x11.c | 72 ++++++- gtk/gtkprivate.h | 1 - gtk/gtkrange.c | 77 +++++-- gtk/gtkscalebutton.c | 144 +++++++++---- gtk/gtksocket.c | 2 +- gtk/gtkspinbutton.c | 6 +- gtk/gtktextview.c | 56 +++-- gtk/gtktooltip.c | 14 +- gtk/gtktreeprivate.h | 3 +- gtk/gtktreeview.c | 86 ++++++-- gtk/gtktreeviewcolumn.c | 3 +- gtk/gtkwidget.c | 516 +++++++++++++++++++++++++++++++++++++-------- gtk/gtkwidget.h | 30 ++- gtk/gtkwindow.c | 232 ++++++++++++++++++-- gtk/gtkwindow.h | 14 ++ 38 files changed, 2029 insertions(+), 527 deletions(-) (limited to 'gtk') 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 #GdkDevices 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) @@ -2124,6 +2273,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); } } @@ -5654,6 +5659,62 @@ _gtk_widget_set_has_grab (GtkWidget *widget, GTK_OBJECT_FLAGS (widget) &= ~(GTK_HAS_GRAB); } +/** + * 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 @@ -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); } @@ -8169,6 +8329,35 @@ gtk_widget_get_events (GtkWidget *widget) return GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (widget), quark_event_mask)); } +/** + * 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); @@ -8830,32 +9108,6 @@ synth_crossing (GtkWidget *widget, gdk_event_free (event); } -/* - * _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. @@ -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 #GdkWindows 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); -- cgit v1.2.1