[gimp/gtk3-port: 245/249] app: make input devices, grabs and therefore generally tools work again
- From: Michael Natterer <mitch src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/gtk3-port: 245/249] app: make input devices, grabs and therefore generally tools work again
- Date: Mon, 21 Mar 2011 20:57:51 +0000 (UTC)
commit e8afc02b90a2612898612a6089baa2d45b598b4f
Author: Michael Natterer <mitch gimp org>
Date: Sun Feb 27 16:38:51 2011 +0100
app: make input devices, grabs and therefore generally tools work again
- add new "device from event" apparatus that works on GTK+ 3.x
- fix the active device selection mechanism
- use the new device grabbing functions
- make sure we don't process events while we have a grab on
another device
- compensate for some really obscure (and likely broken) behavior
of XI2, it feels like we are the first real users...
app/display/gimpdisplayshell-grab.c | 158 ++++++++++++++++++++--------
app/display/gimpdisplayshell-tool-events.c | 94 ++++++++++++++---
app/display/gimpdisplayshell.h | 7 +-
app/widgets/gimpdevices.c | 116 +++++++++++++++++----
app/widgets/gimpdevices.h | 34 ++++---
5 files changed, 311 insertions(+), 98 deletions(-)
---
diff --git a/app/display/gimpdisplayshell-grab.c b/app/display/gimpdisplayshell-grab.c
index 1320a47..ec77eb6 100644
--- a/app/display/gimpdisplayshell-grab.c
+++ b/app/display/gimpdisplayshell-grab.c
@@ -23,58 +23,107 @@
#include "display-types.h"
-#include "widgets/gimpdeviceinfo.h"
#include "widgets/gimpdevices.h"
-#include "widgets/gimpdevicemanager.h"
#include "gimpdisplay.h"
#include "gimpdisplayshell.h"
#include "gimpdisplayshell-grab.h"
+static GdkDevice *
+get_associated_pointer (GdkDevice *device)
+{
+ switch (gdk_device_get_device_type (device))
+ {
+ case GDK_DEVICE_TYPE_SLAVE:
+ device = gdk_device_get_associated_device (device);
+ break;
+
+ case GDK_DEVICE_TYPE_FLOATING:
+ {
+ GdkDisplay *display = gdk_device_get_display (device);
+ GdkDeviceManager *manager = gdk_display_get_device_manager (display);
+
+ return gdk_device_manager_get_client_pointer (manager);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
+ device = gdk_device_get_associated_device (device);
+
+ return device;
+}
+
+static GdkDevice *
+get_associated_keyboard (GdkDevice *device)
+{
+ switch (gdk_device_get_device_type (device))
+ {
+ case GDK_DEVICE_TYPE_SLAVE:
+ device = gdk_device_get_associated_device (device);
+ break;
+
+ case GDK_DEVICE_TYPE_FLOATING:
+ {
+ GdkDisplay *display = gdk_device_get_display (device);
+ GdkDeviceManager *manager = gdk_display_get_device_manager (display);
+
+ device = gdk_device_manager_get_client_pointer (manager);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD)
+ device = gdk_device_get_associated_device (device);
+
+ return device;
+}
+
gboolean
gimp_display_shell_pointer_grab (GimpDisplayShell *shell,
GdkEvent *event,
GdkEventMask event_mask)
{
- GdkGrabStatus status;
+ GdkDevice *device;
+ GdkDevice *source_device;
+ GdkGrabStatus status;
g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
- g_return_val_if_fail (shell->pointer_grabbed == FALSE, FALSE);
+ g_return_val_if_fail (shell->grab_pointer == NULL, FALSE);
+
+ source_device = gimp_devices_get_from_event (shell->display->gimp,
+ event, &device);
- status = gdk_pointer_grab (gtk_widget_get_window (shell->canvas),
- FALSE, event_mask, NULL, NULL,
- gdk_event_get_time (event));
+ if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
+ {
+ device = get_associated_pointer (device);
+ source_device = NULL;
+ }
+
+ status = gdk_device_grab (device,
+ gtk_widget_get_window (shell->canvas),
+ GDK_OWNERSHIP_APPLICATION,
+ FALSE, event_mask, NULL,
+ gdk_event_get_time (event));
if (status == GDK_GRAB_SUCCESS)
{
- shell->pointer_grabbed = TRUE;
+ shell->grab_pointer = device;
+ shell->grab_pointer_source = source_device;
return TRUE;
}
- else if (status == GDK_GRAB_ALREADY_GRABBED)
- {
- GimpDeviceManager *manager;
- GdkDisplay *gdk_display;
-
- manager = gimp_devices_get_manager (shell->display->gimp);
- gdk_display = gtk_widget_get_display (GTK_WIDGET (shell));
-
- /* EEK: trying to grab an extended device always returns
- * ALREADY_GRABBED, so simply assume the grab succeeded anyway
- */
- if (gimp_device_manager_get_current_device (manager)->device !=
- gdk_display_get_core_pointer (gdk_display))
- {
- shell->pointer_grabbed = TRUE;
-
- return TRUE;
- }
- }
- g_printerr ("%s: gdk_pointer_grab failed with status %d\n",
- G_STRFUNC, status);
+ g_printerr ("%s: gdk_device_grab(%s) failed with status %d\n",
+ G_STRFUNC, gdk_device_get_name (device), status);
return FALSE;
}
@@ -85,37 +134,55 @@ gimp_display_shell_pointer_ungrab (GimpDisplayShell *shell,
{
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
g_return_if_fail (event != NULL);
- g_return_if_fail (shell->pointer_grabbed == TRUE);
+ g_return_if_fail (shell->grab_pointer != NULL);
- gdk_display_pointer_ungrab (gtk_widget_get_display (shell->canvas),
- gdk_event_get_time (event));
+ gdk_device_ungrab (shell->grab_pointer,
+ gdk_event_get_time (event));
- shell->pointer_grabbed = FALSE;
+ shell->grab_pointer = NULL;
+ shell->grab_pointer_source = NULL;
}
gboolean
gimp_display_shell_keyboard_grab (GimpDisplayShell *shell,
GdkEvent *event)
{
- GdkGrabStatus status;
+ GdkDevice *device;
+ GdkDevice *source_device;
+ GdkGrabStatus status;
g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
- g_return_val_if_fail (shell->keyboard_grabbed == FALSE, FALSE);
+ g_return_val_if_fail (shell->grab_keyboard == NULL, FALSE);
+
+ source_device = gimp_devices_get_from_event (shell->display->gimp,
+ event, &device);
+
+ if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD)
+ {
+ device = get_associated_keyboard (device);
+ source_device = NULL;
+ }
- status = gdk_keyboard_grab (gtk_widget_get_window (shell->canvas),
- FALSE,
- gdk_event_get_time (event));
+ status = gdk_device_grab (device,
+ gtk_widget_get_window (shell->canvas),
+ GDK_OWNERSHIP_APPLICATION,
+ FALSE,
+ GDK_KEY_PRESS_MASK |
+ GDK_KEY_RELEASE_MASK |
+ GDK_FOCUS_CHANGE_MASK,
+ NULL, gdk_event_get_time (event));
if (status == GDK_GRAB_SUCCESS)
{
- shell->keyboard_grabbed = TRUE;
+ shell->grab_keyboard = device;
+ shell->grab_keyboard_source = source_device;
return TRUE;
}
- g_printerr ("%s: gdk_keyboard_grab failed with status %d\n",
- G_STRFUNC, status);
+ g_printerr ("%s: gdk_device_grab(%s) failed with status %d\n",
+ G_STRFUNC, gdk_device_get_name (device), status);
return FALSE;
}
@@ -126,10 +193,11 @@ gimp_display_shell_keyboard_ungrab (GimpDisplayShell *shell,
{
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
g_return_if_fail (event != NULL);
- g_return_if_fail (shell->keyboard_grabbed == TRUE);
+ g_return_if_fail (shell->grab_keyboard != NULL);
- gdk_display_keyboard_ungrab (gtk_widget_get_display (shell->canvas),
- gdk_event_get_time (event));
+ gdk_device_ungrab (shell->grab_keyboard,
+ gdk_event_get_time (event));
- shell->keyboard_grabbed = FALSE;
+ shell->grab_keyboard = NULL;
+ shell->grab_keyboard_source = NULL;
}
diff --git a/app/display/gimpdisplayshell-tool-events.c b/app/display/gimpdisplayshell-tool-events.c
index 0e1e6bf..5f6a786 100644
--- a/app/display/gimpdisplayshell-tool-events.c
+++ b/app/display/gimpdisplayshell-tool-events.c
@@ -70,6 +70,9 @@ static GdkModifierType
static void gimp_display_shell_proximity_in (GimpDisplayShell *shell);
static void gimp_display_shell_proximity_out (GimpDisplayShell *shell);
+static gboolean gimp_display_shell_check_device (GimpDisplayShell *shell,
+ GdkEvent *event,
+ gboolean *device_changed);
static void gimp_display_shell_check_device_cursor (GimpDisplayShell *shell);
static void gimp_display_shell_start_scrolling (GimpDisplayShell *shell,
@@ -319,12 +322,8 @@ gimp_display_shell_canvas_tool_events (GtkWidget *canvas,
GIMP_LOG (TOOL_EVENTS, "event (display %p): %s",
display, gimp_print_event (event));
- /* Find out what device the event occurred upon */
- if (! gimp->busy && gimp_devices_check_change (gimp, event))
- {
- gimp_display_shell_check_device_cursor (shell);
- device_changed = TRUE;
- }
+ if (gimp_display_shell_check_device (shell, event, &device_changed))
+ return TRUE;
gimp_display_shell_get_event_coords (shell, event,
&display_coords,
@@ -418,6 +417,13 @@ gimp_display_shell_canvas_tool_events (GtkWidget *canvas,
if (G_UNLIKELY (gtk_widget_has_focus (canvas)))
g_warning ("%s: FOCUS_OUT but canvas has focus", G_STRFUNC);
+ if (shell->grab_keyboard)
+ {
+ g_printerr ("%s: ignoring FOCUS_OUT while we have a grab\n",
+ G_STRFUNC);
+ return TRUE;
+ }
+
/* reset it here to be prepared for the next
* FOCUS_IN / BUTTON_PRESS confusion
*/
@@ -688,12 +694,12 @@ gimp_display_shell_canvas_tool_events (GtkWidget *canvas,
case 1:
state &= ~GDK_BUTTON1_MASK;
- if (! shell->space_pressed && ! shell->space_release_pending)
- gimp_display_shell_keyboard_ungrab (shell, event);
-
- gimp_display_shell_pointer_ungrab (shell, event);
-
- gtk_grab_add (canvas);
+ /* If we don't have a grab, this is a release paired with
+ * a button press we intentionally ignored because we had
+ * a grab on another device at the time of the press
+ */
+ if (! shell->grab_pointer)
+ return TRUE;
if (active_tool &&
(! gimp_image_is_empty (image) ||
@@ -716,7 +722,10 @@ gimp_display_shell_canvas_tool_events (GtkWidget *canvas,
*/
gimp_display_shell_update_focus (shell, &image_coords, state);
- gtk_grab_remove (canvas);
+ if (! shell->space_pressed && ! shell->space_release_pending)
+ gimp_display_shell_keyboard_ungrab (shell, event);
+
+ gimp_display_shell_pointer_ungrab (shell, event);
if (shell->space_release_pending)
gimp_display_shell_space_released (shell, event);
@@ -1398,6 +1407,61 @@ gimp_display_shell_proximity_out (GimpDisplayShell *shell)
}
}
+static gboolean
+gimp_display_shell_check_device (GimpDisplayShell *shell,
+ GdkEvent *event,
+ gboolean *device_changed)
+{
+ Gimp *gimp = gimp_display_get_gimp (shell->display);
+ GdkDevice *device;
+ GdkDevice *grab_device;
+
+ /* Find out what device the event occurred upon */
+ device = gimp_devices_get_from_event (gimp, event, &grab_device);
+
+ if (device)
+ {
+ /* While we have a grab, ignore all events from all other devices
+ * of the same type
+ */
+ if (event->type == GDK_KEY_PRESS ||
+ event->type == GDK_KEY_RELEASE ||
+ event->type == GDK_FOCUS_CHANGE)
+ {
+ if ((shell->grab_keyboard && (shell->grab_keyboard != grab_device)) ||
+ (shell->grab_keyboard_source && (shell->grab_keyboard_source != device)))
+ {
+ GIMP_LOG (TOOL_EVENTS,
+ "ignoring key event from '%s' while waiting for event from '%s'\n",
+ gdk_device_get_name (device),
+ gdk_device_get_name (shell->grab_keyboard_source));
+ return TRUE;
+ }
+ }
+ else
+ {
+ if ((shell->grab_pointer && (shell->grab_pointer != grab_device)) ||
+ (shell->grab_pointer_source && (shell->grab_pointer_source != device)))
+ {
+ GIMP_LOG (TOOL_EVENTS,
+ "ignoring pointer event from '%s' while waiting for event from '%s'\n",
+ gdk_device_get_name (device),
+ gdk_device_get_name (shell->grab_pointer_source));
+ return TRUE;
+ }
+ }
+
+ if (! gimp->busy && gimp_devices_check_change (gimp, device))
+ {
+ gimp_display_shell_check_device_cursor (shell);
+
+ *device_changed = TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
static void
gimp_display_shell_check_device_cursor (GimpDisplayShell *shell)
{
@@ -1434,8 +1498,6 @@ gimp_display_shell_start_scrolling (GimpDisplayShell *shell,
shell->scroll_start_y = y + shell->offset_y;
gimp_display_shell_set_override_cursor (shell, GDK_FLEUR);
-
- gtk_grab_add (shell->canvas);
}
static void
@@ -1448,8 +1510,6 @@ gimp_display_shell_stop_scrolling (GimpDisplayShell *shell)
shell->scroll_start_y = 0;
gimp_display_shell_unset_override_cursor (shell);
-
- gtk_grab_remove (shell->canvas);
}
static void
diff --git a/app/display/gimpdisplayshell.h b/app/display/gimpdisplayshell.h
index 1f89087..ab7f68f 100644
--- a/app/display/gimpdisplayshell.h
+++ b/app/display/gimpdisplayshell.h
@@ -184,8 +184,11 @@ struct _GimpDisplayShell
gboolean size_allocate_from_configure_event;
/* the state of gimp_display_shell_tool_events() */
- gboolean pointer_grabbed;
- gboolean keyboard_grabbed;
+ GdkDevice *grab_pointer;
+ GdkDevice *grab_pointer_source;
+
+ GdkDevice *grab_keyboard;
+ GdkDevice *grab_keyboard_source;
gboolean space_pressed;
gboolean space_release_pending;
diff --git a/app/widgets/gimpdevices.c b/app/widgets/gimpdevices.c
index 1799cc5..f541f20 100644
--- a/app/widgets/gimpdevices.c
+++ b/app/widgets/gimpdevices.c
@@ -231,6 +231,89 @@ gimp_devices_get_manager (Gimp *gimp)
return manager;
}
+GdkDevice *
+gimp_devices_get_from_event (Gimp *gimp,
+ GdkEvent *event,
+ GdkDevice **grab_device)
+{
+ GdkDevice *device;
+
+ g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
+ g_return_val_if_fail (event != NULL, NULL);
+
+ device = gdk_event_get_source_device (event);
+
+ /* initialize the default grab device to the event's device,
+ * because that is always either a master or a floating device,
+ * which is the types of devices we can make grabs on without
+ * disturbing side effects.
+ */
+ if (grab_device)
+ *grab_device = gdk_event_get_device (event);
+
+ if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
+ {
+ switch (gdk_device_get_device_type (device))
+ {
+ case GDK_DEVICE_TYPE_MASTER:
+ /* this happens on focus synthesized focus changed events,
+ * and we can't do anything with the returned device, so
+ * return NULL
+ */
+ return NULL;
+
+ case GDK_DEVICE_TYPE_SLAVE:
+ /* it makes no sense for us to distinguigh between
+ * different slave keyboards, so just always return
+ * their respective master
+ */
+ return gdk_device_get_associated_device (device);
+
+ case GDK_DEVICE_TYPE_FLOATING:
+ /* we have no way of explicitly enabling floating
+ * keyboards, so we cannot get their events
+ */
+ g_return_val_if_reached (device);
+ }
+ }
+ else
+ {
+ switch (gdk_device_get_device_type (device))
+ {
+ case GDK_DEVICE_TYPE_MASTER:
+ /* this can only happen for synthesized events which have
+ * no actual source, so return NULL to indicate that we
+ * cannot do anything with the event's device information
+ */
+ return NULL;
+
+ case GDK_DEVICE_TYPE_SLAVE:
+ /* this is the tricky part: we do want to distingiugh slave
+ * devices, but only if we actually enabled them ourselves
+ * explicitely (like the pens of a tablet); however we
+ * usually don't enable the different incarnations of the
+ * mouse itself (like touchpad, trackpoint, usb mouse
+ * etc.), so for these return their respective master so
+ * its settings are used
+ */
+ if (gdk_device_get_mode (device) == GDK_MODE_DISABLED)
+ {
+ return gdk_device_get_associated_device (device);
+ }
+
+ return device;
+
+ case GDK_DEVICE_TYPE_FLOATING:
+ /* we only get events for floating devices which have
+ * enabled ourselves, so return the floating device
+ */
+ return device;
+ }
+ }
+
+ g_return_val_if_reached (device);
+}
+
void
gimp_devices_add_widget (Gimp *gimp,
GtkWidget *widget)
@@ -252,44 +335,39 @@ gimp_devices_check_callback (GtkWidget *widget,
g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE);
if (! gimp->busy)
- gimp_devices_check_change (gimp, event);
+ {
+ GdkDevice *device;
+
+ device = gimp_devices_get_from_event (gimp, event, NULL);
+
+ if (device)
+ gimp_devices_check_change (gimp, device);
+ }
return FALSE;
}
gboolean
-gimp_devices_check_change (Gimp *gimp,
- GdkEvent *event)
+gimp_devices_check_change (Gimp *gimp,
+ GdkDevice *device)
{
GimpDeviceManager *manager;
- GdkDevice *device;
GimpDeviceInfo *device_info;
g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE);
- g_return_val_if_fail (event != NULL, FALSE);
+ g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
manager = gimp_devices_get_manager (gimp);
g_return_val_if_fail (GIMP_IS_DEVICE_MANAGER (manager), FALSE);
- device = gdk_event_get_source_device (event);
-
- if (! device)
- device = gimp_device_manager_get_current_device (manager)->device;
-
device_info = gimp_device_info_get_by_device (device);
if (! device_info)
- {
- device = gdk_event_get_device (event);
-
- if (! device)
- device = gimp_device_manager_get_current_device (manager)->device;
-
- device_info = gimp_device_info_get_by_device (device);
- }
+ device_info = gimp_device_manager_get_current_device (manager);
- if (device_info && device_info != gimp_device_manager_get_current_device (manager))
+ if (device_info &&
+ device_info != gimp_device_manager_get_current_device (manager))
{
gimp_device_manager_set_current_device (manager, device_info);
return TRUE;
diff --git a/app/widgets/gimpdevices.h b/app/widgets/gimpdevices.h
index a2cdbc8..060f38b 100644
--- a/app/widgets/gimpdevices.h
+++ b/app/widgets/gimpdevices.h
@@ -19,26 +19,30 @@
#define __GIMP_DEVICES_H__
-void gimp_devices_init (Gimp *gimp);
-void gimp_devices_exit (Gimp *gimp);
+void gimp_devices_init (Gimp *gimp);
+void gimp_devices_exit (Gimp *gimp);
-void gimp_devices_restore (Gimp *gimp);
-void gimp_devices_save (Gimp *gimp,
- gboolean always_save);
+void gimp_devices_restore (Gimp *gimp);
+void gimp_devices_save (Gimp *gimp,
+ gboolean always_save);
-gboolean gimp_devices_clear (Gimp *gimp,
- GError **error);
+gboolean gimp_devices_clear (Gimp *gimp,
+ GError **error);
-GimpDeviceManager * gimp_devices_get_manager (Gimp *gimp);
+GimpDeviceManager * gimp_devices_get_manager (Gimp *gimp);
-void gimp_devices_add_widget (Gimp *gimp,
- GtkWidget *widget);
+GdkDevice * gimp_devices_get_from_event (Gimp *gimp,
+ GdkEvent *event,
+ GdkDevice **grab_device);
-gboolean gimp_devices_check_callback (GtkWidget *widget,
- GdkEvent *event,
- Gimp *gimp);
-gboolean gimp_devices_check_change (Gimp *gimp,
- GdkEvent *event);
+void gimp_devices_add_widget (Gimp *gimp,
+ GtkWidget *widget);
+
+gboolean gimp_devices_check_callback (GtkWidget *widget,
+ GdkEvent *event,
+ Gimp *gimp);
+gboolean gimp_devices_check_change (Gimp *gimp,
+ GdkDevice *device);
#endif /* __GIMP_DEVICES_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]