Compare commits
8 Commits
wip/matthi
...
xi2-playgr
Author | SHA1 | Date | |
---|---|---|---|
|
d5dda9a2f0 | ||
|
ab8242c522 | ||
|
80bf8c3999 | ||
|
4212943079 | ||
|
0ac8f1686f | ||
|
a771703f0f | ||
|
e3a3ba57f0 | ||
|
712672f1d1 |
@@ -1693,6 +1693,7 @@ demos/Makefile
|
||||
demos/gtk-demo/Makefile
|
||||
demos/gtk-demo/geninclude.pl
|
||||
tests/Makefile
|
||||
tests/multidevice/Makefile
|
||||
docs/Makefile
|
||||
docs/reference/Makefile
|
||||
docs/reference/gdk/Makefile
|
||||
|
@@ -706,6 +706,8 @@ gdk_window_get_device_cursor
|
||||
gdk_window_set_device_cursor
|
||||
gdk_window_get_device_events
|
||||
gdk_window_set_device_events
|
||||
gdk_window_get_source_events
|
||||
gdk_window_set_source_events
|
||||
|
||||
<SUBSECTION>
|
||||
GdkPointerHooks
|
||||
|
16
gdk/gdk.c
16
gdk/gdk.c
@@ -788,26 +788,26 @@ gdk_set_program_class (const char *program_class)
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_enable_multidevice:
|
||||
* gdk_disable_multidevice:
|
||||
*
|
||||
* Enables multidevice support in GDK. This call must happen prior
|
||||
* Disables multidevice support in GDK. This call must happen prior
|
||||
* to gdk_display_open(), gtk_init(), gtk_init_with_args() or
|
||||
* gtk_init_check() in order to take effect.
|
||||
*
|
||||
* Note that individual #GdkWindow<!-- -->s still need to explicitly
|
||||
* enable multidevice awareness through gdk_window_set_support_multidevice().
|
||||
*
|
||||
* This function must be called before initializing GDK.
|
||||
* Most common GTK+ applications won't ever need to call this. Only
|
||||
* applications that do mixed GDK/Xlib calls could want to disable
|
||||
* multidevice support if such Xlib code deals with input devices in
|
||||
* any way and doesn't observe the presence of XInput 2.
|
||||
*
|
||||
* Since: 3.0
|
||||
**/
|
||||
void
|
||||
gdk_enable_multidevice (void)
|
||||
gdk_disable_multidevice (void)
|
||||
{
|
||||
if (gdk_initialized)
|
||||
return;
|
||||
|
||||
_gdk_enable_multidevice = TRUE;
|
||||
_gdk_disable_multidevice = TRUE;
|
||||
}
|
||||
|
||||
#define __GDK_C__
|
||||
|
@@ -66,7 +66,7 @@ G_BEGIN_DECLS
|
||||
/* Initialization, exit and events
|
||||
*/
|
||||
#define GDK_PRIORITY_EVENTS (G_PRIORITY_DEFAULT)
|
||||
void gdk_enable_multidevice (void);
|
||||
void gdk_disable_multidevice (void);
|
||||
void gdk_parse_args (gint *argc,
|
||||
gchar ***argv);
|
||||
void gdk_init (gint *argc,
|
||||
|
@@ -177,7 +177,7 @@ gdk_get_display_arg_name
|
||||
gdk_get_program_class
|
||||
gdk_init
|
||||
gdk_init_check
|
||||
gdk_enable_multidevice
|
||||
gdk_disable_multidevice
|
||||
gdk_pre_parse_libgtk_only
|
||||
gdk_parse_args
|
||||
gdk_set_program_class
|
||||
@@ -738,6 +738,8 @@ gdk_window_get_device_events
|
||||
gdk_window_set_device_cursor
|
||||
gdk_window_get_device_cursor
|
||||
gdk_window_get_device_position
|
||||
gdk_window_set_source_events
|
||||
gdk_window_get_source_events
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
@@ -66,6 +66,7 @@ typedef enum
|
||||
* of a stylus on a graphics tablet.
|
||||
* @GDK_SOURCE_CURSOR: the device is a graphics tablet "puck" or similar device.
|
||||
* @GDK_SOURCE_KEYBOARD: the device is a keyboard.
|
||||
* @GDK_SOURCE_TOUCH: the device is a (multi)touch capable device.
|
||||
*
|
||||
* An enumeration describing the type of an input device in general terms.
|
||||
*/
|
||||
@@ -75,7 +76,8 @@ typedef enum
|
||||
GDK_SOURCE_PEN,
|
||||
GDK_SOURCE_ERASER,
|
||||
GDK_SOURCE_CURSOR,
|
||||
GDK_SOURCE_KEYBOARD
|
||||
GDK_SOURCE_KEYBOARD,
|
||||
GDK_SOURCE_TOUCH
|
||||
} GdkInputSource;
|
||||
|
||||
/**
|
||||
|
@@ -34,12 +34,13 @@
|
||||
* additional features such as sub-pixel positioning information and additional
|
||||
* device-dependent information.
|
||||
* @Title: GdkDeviceManager
|
||||
* @See_also: #GdkDevice, #GdkEvent, gdk_enable_multidevice()
|
||||
* @See_also: #GdkDevice, #GdkEvent, gdk_disable_multidevice()
|
||||
*
|
||||
* By default, GDK supports the traditional single keyboard/pointer input scheme (Plus additional
|
||||
* special input devices such as tablets. In short, backwards compatible with 2.X). Since version 3.0,
|
||||
* if gdk_enable_multidevice() is called before gdk_display_open() and the platform supports it, GDK
|
||||
* will be aware of multiple keyboard/pointer pairs interacting simultaneously with the user interface.
|
||||
* By default, and if the platform supports it, GDK is aware of multiple keyboard/pointer pairs
|
||||
* and multitouch devices, this behavior can be changed by calling gdk_disable_multidevice()
|
||||
* before gdk_display_open(), although there would be rarely a reason to do that. For a widget
|
||||
* or window to be dealt as multipointer aware, gdk_window_set_support_multidevice() or
|
||||
* gtk_widget_set_support_multidevice() must have been called on it.
|
||||
*
|
||||
* Conceptually, in multidevice mode there are 2 device types, virtual devices (or master devices)
|
||||
* are represented by the pointer cursors and keyboard foci that are seen on the screen. physical
|
||||
|
@@ -1250,6 +1250,7 @@ _gdk_display_add_device_grab (GdkDisplay *display,
|
||||
static void
|
||||
synthesize_crossing_events (GdkDisplay *display,
|
||||
GdkDevice *device,
|
||||
GdkDevice *source_device,
|
||||
GdkWindow *src_window,
|
||||
GdkWindow *dest_window,
|
||||
GdkCrossingMode crossing_mode,
|
||||
@@ -1285,7 +1286,7 @@ synthesize_crossing_events (GdkDisplay *display,
|
||||
_gdk_synthesize_crossing_events (display,
|
||||
src_window,
|
||||
dest_window,
|
||||
device,
|
||||
device, source_device,
|
||||
crossing_mode,
|
||||
x, y, state,
|
||||
time,
|
||||
@@ -1299,7 +1300,7 @@ synthesize_crossing_events (GdkDisplay *display,
|
||||
_gdk_synthesize_crossing_events (display,
|
||||
src_window,
|
||||
NULL,
|
||||
device,
|
||||
device, source_device,
|
||||
crossing_mode,
|
||||
x, y, state,
|
||||
time,
|
||||
@@ -1314,7 +1315,7 @@ synthesize_crossing_events (GdkDisplay *display,
|
||||
_gdk_synthesize_crossing_events (display,
|
||||
src_window,
|
||||
NULL,
|
||||
device,
|
||||
device, source_device,
|
||||
crossing_mode,
|
||||
x, y, state,
|
||||
time,
|
||||
@@ -1325,7 +1326,7 @@ synthesize_crossing_events (GdkDisplay *display,
|
||||
_gdk_synthesize_crossing_events (display,
|
||||
NULL,
|
||||
dest_window,
|
||||
device,
|
||||
device, source_device,
|
||||
crossing_mode,
|
||||
x, y, state,
|
||||
time,
|
||||
@@ -1362,6 +1363,7 @@ get_current_toplevel (GdkDisplay *display,
|
||||
static void
|
||||
switch_to_pointer_grab (GdkDisplay *display,
|
||||
GdkDevice *device,
|
||||
GdkDevice *source_device,
|
||||
GdkDeviceGrabInfo *grab,
|
||||
GdkDeviceGrabInfo *last_grab,
|
||||
guint32 time,
|
||||
@@ -1397,7 +1399,7 @@ switch_to_pointer_grab (GdkDisplay *display,
|
||||
src_window = info->window_under_pointer;
|
||||
|
||||
if (src_window != grab->window)
|
||||
synthesize_crossing_events (display, device,
|
||||
synthesize_crossing_events (display, device, source_device,
|
||||
src_window, grab->window,
|
||||
GDK_CROSSING_GRAB, time, serial);
|
||||
|
||||
@@ -1448,7 +1450,7 @@ switch_to_pointer_grab (GdkDisplay *display,
|
||||
}
|
||||
|
||||
if (pointer_window != last_grab->window)
|
||||
synthesize_crossing_events (display, device,
|
||||
synthesize_crossing_events (display, device, source_device,
|
||||
last_grab->window, pointer_window,
|
||||
GDK_CROSSING_UNGRAB, time, serial);
|
||||
|
||||
@@ -1463,6 +1465,7 @@ switch_to_pointer_grab (GdkDisplay *display,
|
||||
void
|
||||
_gdk_display_device_grab_update (GdkDisplay *display,
|
||||
GdkDevice *device,
|
||||
GdkDevice *source_device,
|
||||
gulong current_serial)
|
||||
{
|
||||
GdkDeviceGrabInfo *current_grab, *next_grab;
|
||||
@@ -1487,7 +1490,7 @@ _gdk_display_device_grab_update (GdkDisplay *display,
|
||||
if (!current_grab->activated)
|
||||
{
|
||||
if (device->source != GDK_SOURCE_KEYBOARD)
|
||||
switch_to_pointer_grab (display, device, current_grab, NULL, time, current_serial);
|
||||
switch_to_pointer_grab (display, device, source_device, current_grab, NULL, time, current_serial);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -1515,7 +1518,7 @@ _gdk_display_device_grab_update (GdkDisplay *display,
|
||||
g_hash_table_insert (display->device_grabs, device, grabs);
|
||||
|
||||
if (device->source != GDK_SOURCE_KEYBOARD)
|
||||
switch_to_pointer_grab (display, device,
|
||||
switch_to_pointer_grab (display, device, source_device,
|
||||
next_grab, current_grab,
|
||||
time, current_serial);
|
||||
|
||||
|
@@ -1053,6 +1053,66 @@ gdk_event_get_device (const GdkEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_event_set_source_device:
|
||||
* @event: a #GdkEvent
|
||||
* @device: a #GdkDevice
|
||||
*
|
||||
* Sets the slave device for @event to @device. The event
|
||||
* must have been allocated by GTK+, for instance, by
|
||||
* gdk_event_copy().
|
||||
*
|
||||
* Since: 3.0
|
||||
**/
|
||||
void
|
||||
gdk_event_set_source_device (GdkEvent *event,
|
||||
GdkDevice *device)
|
||||
{
|
||||
GdkEventPrivate *private;
|
||||
|
||||
g_return_if_fail (gdk_event_is_allocated (event));
|
||||
g_return_if_fail (GDK_IS_DEVICE (device));
|
||||
|
||||
private = (GdkEventPrivate *) event;
|
||||
|
||||
private->source_device = device;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_event_get_source_device:
|
||||
* @event: a #GdkEvent
|
||||
*
|
||||
* This function returns the hardware (slave) #GdkDevice that has triggered the event,
|
||||
* falling back to the virtual (master) device (as in gdk_event_get_device()) if the
|
||||
* event wasn't caused by interaction with a hardware device. This may happen for
|
||||
* example in synthesized crossing events after a #GdkWindow updates its geometry or
|
||||
* a grab is acquired/released.
|
||||
*
|
||||
* If the event does not contain device field, this function will return %NULL.
|
||||
*
|
||||
* Returns: a #GdkDevice, or %NULL.
|
||||
*
|
||||
* Since: 3.0
|
||||
**/
|
||||
GdkDevice *
|
||||
gdk_event_get_source_device (const GdkEvent *event)
|
||||
{
|
||||
GdkEventPrivate *private;
|
||||
|
||||
g_return_val_if_fail (event != NULL, NULL);
|
||||
|
||||
if (!gdk_event_is_allocated (event))
|
||||
return NULL;
|
||||
|
||||
private = (GdkEventPrivate *) event;
|
||||
|
||||
if (private->source_device)
|
||||
return private->source_device;
|
||||
|
||||
/* Fallback to event device */
|
||||
return gdk_event_get_device (event);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_event_request_motions:
|
||||
* @event: a valid #GdkEvent
|
||||
|
@@ -543,6 +543,9 @@ gboolean gdk_event_get_axis (const GdkEvent *event,
|
||||
void gdk_event_set_device (GdkEvent *event,
|
||||
GdkDevice *device);
|
||||
GdkDevice* gdk_event_get_device (const GdkEvent *event);
|
||||
void gdk_event_set_source_device (GdkEvent *event,
|
||||
GdkDevice *device);
|
||||
GdkDevice* gdk_event_get_source_device (const GdkEvent *event);
|
||||
void gdk_event_request_motions (const GdkEventMotion *event);
|
||||
|
||||
gboolean gdk_events_get_distance (GdkEvent *event1,
|
||||
|
@@ -40,7 +40,7 @@ gchar *_gdk_display_name = NULL;
|
||||
gint _gdk_screen_number = -1;
|
||||
gchar *_gdk_display_arg_name = NULL;
|
||||
gboolean _gdk_native_windows = FALSE;
|
||||
gboolean _gdk_enable_multidevice = FALSE;
|
||||
gboolean _gdk_disable_multidevice = FALSE;
|
||||
|
||||
GSList *_gdk_displays = NULL;
|
||||
|
||||
|
@@ -158,6 +158,7 @@ struct _GdkEventPrivate
|
||||
GdkScreen *screen;
|
||||
gpointer windowing_data;
|
||||
GdkDevice *device;
|
||||
GdkDevice *source_device;
|
||||
};
|
||||
|
||||
/* Tracks information about the pointer grab on this display */
|
||||
@@ -273,6 +274,9 @@ struct _GdkWindowObject
|
||||
|
||||
GList *devices_inside;
|
||||
GHashTable *device_events;
|
||||
|
||||
GHashTable *source_event_masks;
|
||||
gulong device_added_handler_id;
|
||||
};
|
||||
|
||||
#define GDK_WINDOW_TYPE(d) (((GdkWindowObject*)(GDK_WINDOW (d)))->window_type)
|
||||
@@ -286,7 +290,7 @@ extern GSList *_gdk_displays;
|
||||
extern gchar *_gdk_display_name;
|
||||
extern gint _gdk_screen_number;
|
||||
extern gchar *_gdk_display_arg_name;
|
||||
extern gboolean _gdk_enable_multidevice;
|
||||
extern gboolean _gdk_disable_multidevice;
|
||||
|
||||
void _gdk_events_queue (GdkDisplay *display);
|
||||
GdkEvent* _gdk_event_unqueue (GdkDisplay *display);
|
||||
@@ -594,6 +598,7 @@ void _gdk_windowing_launch_failed (GAppLaunchContext *context,
|
||||
|
||||
void _gdk_display_device_grab_update (GdkDisplay *display,
|
||||
GdkDevice *device,
|
||||
GdkDevice *source_device,
|
||||
gulong current_serial);
|
||||
GdkDeviceGrabInfo *_gdk_display_get_last_device_grab (GdkDisplay *display,
|
||||
GdkDevice *device);
|
||||
@@ -659,6 +664,7 @@ void _gdk_synthesize_crossing_events (GdkDisplay *display,
|
||||
GdkWindow *src,
|
||||
GdkWindow *dest,
|
||||
GdkDevice *device,
|
||||
GdkDevice *source_device,
|
||||
GdkCrossingMode mode,
|
||||
gint toplevel_x,
|
||||
gint toplevel_y,
|
||||
|
172
gdk/gdkwindow.c
172
gdk/gdkwindow.c
@@ -702,6 +702,9 @@ gdk_window_finalize (GObject *object)
|
||||
if (obj->device_events)
|
||||
g_hash_table_destroy (obj->device_events);
|
||||
|
||||
if (obj->source_event_masks)
|
||||
g_hash_table_destroy (obj->source_event_masks);
|
||||
|
||||
if (obj->devices_inside)
|
||||
g_list_free (obj->devices_inside);
|
||||
|
||||
@@ -10077,6 +10080,7 @@ gdk_window_set_support_multidevice (GdkWindow *window,
|
||||
gboolean support_multidevice)
|
||||
{
|
||||
GdkWindowObject *private = (GdkWindowObject *) window;
|
||||
GdkEventMask event_mask = 0;
|
||||
|
||||
g_return_if_fail (GDK_IS_WINDOW (window));
|
||||
|
||||
@@ -10088,6 +10092,12 @@ gdk_window_set_support_multidevice (GdkWindow *window,
|
||||
|
||||
private->support_multidevice = support_multidevice;
|
||||
|
||||
if (support_multidevice)
|
||||
event_mask = gdk_window_get_events (window);
|
||||
|
||||
/* Enable events for slave touchscreen devices */
|
||||
gdk_window_set_source_events (window, GDK_SOURCE_TOUCH, event_mask);
|
||||
|
||||
/* FIXME: What to do if called when some pointers are inside the window ? */
|
||||
}
|
||||
|
||||
@@ -10345,6 +10355,7 @@ send_crossing_event (GdkDisplay *display,
|
||||
GdkNotifyType notify_type,
|
||||
GdkWindow *subwindow,
|
||||
GdkDevice *device,
|
||||
GdkDevice *source_device,
|
||||
gint toplevel_x,
|
||||
gint toplevel_y,
|
||||
GdkModifierType mask,
|
||||
@@ -10404,6 +10415,10 @@ send_crossing_event (GdkDisplay *display,
|
||||
{
|
||||
event = _gdk_make_event ((GdkWindow *)window, type, event_in_queue, TRUE);
|
||||
gdk_event_set_device (event, device);
|
||||
|
||||
if (source_device)
|
||||
gdk_event_set_source_device (event, source_device);
|
||||
|
||||
event->crossing.time = time_;
|
||||
event->crossing.subwindow = subwindow;
|
||||
if (subwindow)
|
||||
@@ -10431,6 +10446,7 @@ _gdk_synthesize_crossing_events (GdkDisplay *display,
|
||||
GdkWindow *src,
|
||||
GdkWindow *dest,
|
||||
GdkDevice *device,
|
||||
GdkDevice *source_device,
|
||||
GdkCrossingMode mode,
|
||||
gint toplevel_x,
|
||||
gint toplevel_y,
|
||||
@@ -10486,7 +10502,7 @@ _gdk_synthesize_crossing_events (GdkDisplay *display,
|
||||
a, GDK_LEAVE_NOTIFY,
|
||||
mode,
|
||||
notify_type,
|
||||
NULL, device,
|
||||
NULL, device, source_device,
|
||||
toplevel_x, toplevel_y,
|
||||
mask, time_,
|
||||
event_in_queue,
|
||||
@@ -10508,7 +10524,7 @@ _gdk_synthesize_crossing_events (GdkDisplay *display,
|
||||
mode,
|
||||
notify_type,
|
||||
(GdkWindow *)last,
|
||||
device,
|
||||
device, source_device,
|
||||
toplevel_x, toplevel_y,
|
||||
mask, time_,
|
||||
event_in_queue,
|
||||
@@ -10555,7 +10571,7 @@ _gdk_synthesize_crossing_events (GdkDisplay *display,
|
||||
mode,
|
||||
notify_type,
|
||||
(GdkWindow *)next,
|
||||
device,
|
||||
device, source_device,
|
||||
toplevel_x, toplevel_y,
|
||||
mask, time_,
|
||||
event_in_queue,
|
||||
@@ -10577,7 +10593,7 @@ _gdk_synthesize_crossing_events (GdkDisplay *display,
|
||||
mode,
|
||||
notify_type,
|
||||
NULL,
|
||||
device,
|
||||
device, source_device,
|
||||
toplevel_x, toplevel_y,
|
||||
mask, time_,
|
||||
event_in_queue,
|
||||
@@ -10911,6 +10927,123 @@ gdk_window_geometry_changed (GdkWindow *window)
|
||||
_gdk_synthesize_crossing_events_for_geometry_change (window);
|
||||
}
|
||||
|
||||
static void
|
||||
source_events_device_added (GdkDeviceManager *device_manager,
|
||||
GdkDevice *device,
|
||||
gpointer user_data)
|
||||
{
|
||||
GdkWindow *window;
|
||||
GdkWindowObject *private;
|
||||
GdkEventMask event_mask;
|
||||
GdkInputSource source;
|
||||
|
||||
if (gdk_device_get_device_type (device) != GDK_DEVICE_TYPE_FLOATING)
|
||||
return;
|
||||
|
||||
window = user_data;
|
||||
private = (GdkWindowObject *) window;
|
||||
source = gdk_device_get_source (device);
|
||||
|
||||
event_mask = GPOINTER_TO_UINT (g_hash_table_lookup (private->source_event_masks,
|
||||
GUINT_TO_POINTER (source)));
|
||||
if (event_mask)
|
||||
{
|
||||
gdk_window_set_device_events (window, device, event_mask);
|
||||
gdk_device_set_mode (device, GDK_MODE_SCREEN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_window_set_source_events:
|
||||
* @window: a #GdkWindow
|
||||
* @source: a #GdkInputSource to define the source class.
|
||||
* @event_mask: event mask for @window
|
||||
*
|
||||
* Sets the event mask for any floating device (i.e. not attached to any
|
||||
* visible pointer) that has the source defined as @source. This event
|
||||
* mask will be applied both to currently existing and newly added devices
|
||||
* after this call.
|
||||
**/
|
||||
void
|
||||
gdk_window_set_source_events (GdkWindow *window,
|
||||
GdkInputSource source,
|
||||
GdkEventMask event_mask)
|
||||
{
|
||||
GdkDeviceManager *device_manager;
|
||||
GdkWindowObject *private;
|
||||
GdkDisplay *display;
|
||||
GList *devices, *d;
|
||||
guint size;
|
||||
|
||||
g_return_if_fail (GDK_IS_WINDOW (window));
|
||||
|
||||
display = gdk_drawable_get_display (GDK_DRAWABLE (window));
|
||||
device_manager = gdk_display_get_device_manager (display);
|
||||
private = (GdkWindowObject *) window;
|
||||
|
||||
devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_FLOATING);
|
||||
|
||||
/* Set event mask for existing devices */
|
||||
for (d = devices; d; d = d->next)
|
||||
{
|
||||
GdkDevice *device = d->data;
|
||||
|
||||
if (source == gdk_device_get_source (device))
|
||||
{
|
||||
gdk_window_set_device_events (window, device, event_mask);
|
||||
gdk_device_set_mode (device, GDK_MODE_SCREEN);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update accounting */
|
||||
if (G_UNLIKELY (!private->source_event_masks))
|
||||
private->source_event_masks = g_hash_table_new (NULL, NULL);
|
||||
|
||||
if (event_mask)
|
||||
g_hash_table_insert (private->source_event_masks,
|
||||
GUINT_TO_POINTER (source),
|
||||
GUINT_TO_POINTER (event_mask));
|
||||
else
|
||||
g_hash_table_remove (private->source_event_masks,
|
||||
GUINT_TO_POINTER (source));
|
||||
|
||||
size = g_hash_table_size (private->source_event_masks);
|
||||
|
||||
/* Update handler if needed */
|
||||
if (!private->device_added_handler_id && size > 0)
|
||||
{
|
||||
private->device_added_handler_id =
|
||||
g_signal_connect (device_manager, "device-added",
|
||||
G_CALLBACK (source_events_device_added), window);
|
||||
}
|
||||
else if (private->device_added_handler_id && size == 0)
|
||||
g_signal_handler_disconnect (device_manager, private->device_added_handler_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_window_get_source_events:
|
||||
* @window: a #GdkWindow
|
||||
* @source: a #GdkInputSource to define the source class.
|
||||
*
|
||||
* Returns the event mask for @window corresponding to the device class specified
|
||||
* by @source.
|
||||
*
|
||||
* Returns: source event mask for @window
|
||||
**/
|
||||
GdkEventMask
|
||||
gdk_window_get_source_events (GdkWindow *window,
|
||||
GdkInputSource source)
|
||||
{
|
||||
GdkWindowObject *private;
|
||||
|
||||
g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
|
||||
|
||||
private = (GdkWindowObject *) window;
|
||||
|
||||
return GPOINTER_TO_UINT (g_hash_table_lookup (private->source_event_masks,
|
||||
GUINT_TO_POINTER (source)));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_synthesize_crossing_event (gpointer data)
|
||||
{
|
||||
@@ -10952,7 +11085,7 @@ do_synthesize_crossing_event (gpointer data)
|
||||
_gdk_synthesize_crossing_events (display,
|
||||
pointer_info->window_under_pointer,
|
||||
new_window_under_pointer,
|
||||
device,
|
||||
device, NULL,
|
||||
GDK_CROSSING_NORMAL,
|
||||
pointer_info->toplevel_x,
|
||||
pointer_info->toplevel_y,
|
||||
@@ -11072,7 +11205,7 @@ proxy_pointer_event (GdkDisplay *display,
|
||||
GdkWindow *toplevel_window, *event_window;
|
||||
GdkWindow *pointer_window;
|
||||
GdkPointerWindowInfo *pointer_info;
|
||||
GdkDevice *device;
|
||||
GdkDevice *device, *source_device;
|
||||
GdkEvent *event;
|
||||
guint state;
|
||||
gdouble toplevel_x, toplevel_y;
|
||||
@@ -11084,6 +11217,7 @@ proxy_pointer_event (GdkDisplay *display,
|
||||
gdk_event_get_state (source_event, &state);
|
||||
time_ = gdk_event_get_time (source_event);
|
||||
device = gdk_event_get_device (source_event);
|
||||
source_device = gdk_event_get_source_device (source_event);
|
||||
pointer_info = _gdk_display_get_pointer_info (display, device);
|
||||
toplevel_window = convert_native_coords_to_toplevel (event_window,
|
||||
toplevel_x, toplevel_y,
|
||||
@@ -11117,7 +11251,7 @@ proxy_pointer_event (GdkDisplay *display,
|
||||
_gdk_synthesize_crossing_events (display,
|
||||
pointer_info->window_under_pointer,
|
||||
event_window,
|
||||
device,
|
||||
device, source_device,
|
||||
source_event->crossing.mode,
|
||||
toplevel_x, toplevel_y,
|
||||
state, time_,
|
||||
@@ -11133,7 +11267,7 @@ proxy_pointer_event (GdkDisplay *display,
|
||||
source_event->crossing.mode,
|
||||
source_event->crossing.detail,
|
||||
NULL,
|
||||
device,
|
||||
device, source_device,
|
||||
toplevel_x, toplevel_y,
|
||||
state, time_,
|
||||
source_event,
|
||||
@@ -11163,7 +11297,7 @@ proxy_pointer_event (GdkDisplay *display,
|
||||
source_event->crossing.mode,
|
||||
source_event->crossing.detail,
|
||||
NULL,
|
||||
device,
|
||||
device, source_device,
|
||||
toplevel_x, toplevel_y,
|
||||
state, time_,
|
||||
source_event,
|
||||
@@ -11173,7 +11307,7 @@ proxy_pointer_event (GdkDisplay *display,
|
||||
_gdk_synthesize_crossing_events (display,
|
||||
event_window,
|
||||
pointer_window,
|
||||
device,
|
||||
device, source_device,
|
||||
source_event->crossing.mode,
|
||||
toplevel_x, toplevel_y,
|
||||
state, time_,
|
||||
@@ -11192,7 +11326,7 @@ proxy_pointer_event (GdkDisplay *display,
|
||||
_gdk_synthesize_crossing_events (display,
|
||||
pointer_info->window_under_pointer,
|
||||
pointer_window,
|
||||
device,
|
||||
device, source_device,
|
||||
GDK_CROSSING_NORMAL,
|
||||
toplevel_x, toplevel_y,
|
||||
state, time_,
|
||||
@@ -11282,7 +11416,7 @@ proxy_button_event (GdkEvent *source_event,
|
||||
gdouble toplevel_x, toplevel_y;
|
||||
GdkDisplay *display;
|
||||
GdkWindowObject *w;
|
||||
GdkDevice *device;
|
||||
GdkDevice *device, *source_device;
|
||||
|
||||
type = source_event->any.type;
|
||||
event_window = source_event->any.window;
|
||||
@@ -11290,6 +11424,7 @@ proxy_button_event (GdkEvent *source_event,
|
||||
gdk_event_get_state (source_event, &state);
|
||||
time_ = gdk_event_get_time (source_event);
|
||||
device = gdk_event_get_device (source_event);
|
||||
source_device = gdk_event_get_source_device (source_event);
|
||||
display = gdk_drawable_get_display (source_event->any.window);
|
||||
toplevel_window = convert_native_coords_to_toplevel (event_window,
|
||||
toplevel_x, toplevel_y,
|
||||
@@ -11326,7 +11461,7 @@ proxy_button_event (GdkEvent *source_event,
|
||||
serial,
|
||||
time_,
|
||||
TRUE);
|
||||
_gdk_display_device_grab_update (display, device, serial);
|
||||
_gdk_display_device_grab_update (display, device, source_device, serial);
|
||||
}
|
||||
|
||||
pointer_window = get_pointer_window (display, toplevel_window, device,
|
||||
@@ -11473,20 +11608,21 @@ _gdk_windowing_got_event (GdkDisplay *display,
|
||||
guint old_state, old_button;
|
||||
GdkDeviceGrabInfo *button_release_grab;
|
||||
GdkPointerWindowInfo *pointer_info;
|
||||
GdkDevice *device;
|
||||
GdkDevice *device, *source_device;
|
||||
gboolean is_toplevel;
|
||||
|
||||
if (gdk_event_get_time (event) != GDK_CURRENT_TIME)
|
||||
display->last_event_time = gdk_event_get_time (event);
|
||||
|
||||
device = gdk_event_get_device (event);
|
||||
source_device = gdk_event_get_source_device (event);
|
||||
|
||||
if (device)
|
||||
{
|
||||
GdkInputMode mode;
|
||||
|
||||
g_object_get (device, "input-mode", &mode, NULL);
|
||||
_gdk_display_device_grab_update (display, device, serial);
|
||||
_gdk_display_device_grab_update (display, device, source_device, serial);
|
||||
|
||||
if (mode == GDK_MODE_DISABLED ||
|
||||
!_gdk_display_check_grab_ownership (display, device, serial))
|
||||
@@ -11532,7 +11668,7 @@ _gdk_windowing_got_event (GdkDisplay *display,
|
||||
serial,
|
||||
gdk_event_get_time (event),
|
||||
TRUE);
|
||||
_gdk_display_device_grab_update (display, device, serial);
|
||||
_gdk_display_device_grab_update (display, device, source_device, serial);
|
||||
}
|
||||
if (event->type == GDK_BUTTON_RELEASE &&
|
||||
!event->any.send_event)
|
||||
@@ -11545,7 +11681,7 @@ _gdk_windowing_got_event (GdkDisplay *display,
|
||||
{
|
||||
button_release_grab->serial_end = serial;
|
||||
button_release_grab->implicit_ungrab = FALSE;
|
||||
_gdk_display_device_grab_update (display, device, serial);
|
||||
_gdk_display_device_grab_update (display, device, source_device, serial);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11667,7 +11803,7 @@ _gdk_windowing_got_event (GdkDisplay *display,
|
||||
{
|
||||
button_release_grab->serial_end = serial;
|
||||
button_release_grab->implicit_ungrab = FALSE;
|
||||
_gdk_display_device_grab_update (display, device, serial);
|
||||
_gdk_display_device_grab_update (display, device, source_device, serial);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -779,6 +779,12 @@ void gdk_window_set_device_events (GdkWindow *window,
|
||||
GdkEventMask gdk_window_get_device_events (GdkWindow *window,
|
||||
GdkDevice *device);
|
||||
|
||||
void gdk_window_set_source_events (GdkWindow *window,
|
||||
GdkInputSource source,
|
||||
GdkEventMask event_mask);
|
||||
GdkEventMask gdk_window_get_source_events (GdkWindow *window,
|
||||
GdkInputSource source);
|
||||
|
||||
void gdk_window_set_icon_list (GdkWindow *window,
|
||||
GList *pixbufs);
|
||||
void gdk_window_set_icon (GdkWindow *window,
|
||||
|
@@ -48,7 +48,7 @@ _gdk_device_manager_new (GdkDisplay *display)
|
||||
major = 2;
|
||||
minor = 0;
|
||||
|
||||
if (_gdk_enable_multidevice &&
|
||||
if (!_gdk_disable_multidevice &&
|
||||
XIQueryVersion (xdisplay, &major, &minor) != BadRequest)
|
||||
{
|
||||
GdkDeviceManagerXI2 *device_manager_xi2;
|
||||
|
@@ -205,6 +205,9 @@ create_device (GdkDeviceManager *device_manager,
|
||||
else if (strstr (tmp_name, "wacom") ||
|
||||
strstr (tmp_name, "pen"))
|
||||
input_source = GDK_SOURCE_PEN;
|
||||
else if (strstr (tmp_name, "touchscreen") ||
|
||||
strstr (tmp_name, "touchpoint"))
|
||||
input_source = GDK_SOURCE_TOUCH;
|
||||
else
|
||||
input_source = GDK_SOURCE_MOUSE;
|
||||
|
||||
@@ -634,6 +637,7 @@ translate_keyboard_string (GdkEventKey *event)
|
||||
static void
|
||||
generate_focus_event (GdkWindow *window,
|
||||
GdkDevice *device,
|
||||
GdkDevice *source_device,
|
||||
gboolean in)
|
||||
{
|
||||
GdkEvent *event;
|
||||
@@ -643,6 +647,7 @@ generate_focus_event (GdkWindow *window,
|
||||
event->focus_change.send_event = FALSE;
|
||||
event->focus_change.in = in;
|
||||
gdk_event_set_device (event, device);
|
||||
gdk_event_set_source_device (event, source_device);
|
||||
|
||||
gdk_event_put (event);
|
||||
gdk_event_free (event);
|
||||
@@ -651,6 +656,7 @@ generate_focus_event (GdkWindow *window,
|
||||
static void
|
||||
handle_focus_change (GdkWindow *window,
|
||||
GdkDevice *device,
|
||||
GdkDevice *source_device,
|
||||
gint detail,
|
||||
gint mode,
|
||||
gboolean in)
|
||||
@@ -710,7 +716,7 @@ handle_focus_change (GdkWindow *window,
|
||||
}
|
||||
|
||||
if (HAS_FOCUS (toplevel) != had_focus)
|
||||
generate_focus_event (window, device, (in) ? TRUE : FALSE);
|
||||
generate_focus_event (window, device, source_device, (in) ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
static gdouble *
|
||||
@@ -908,7 +914,7 @@ gdk_device_manager_xi2_translate_event (GdkEventTranslator *translator,
|
||||
XIDeviceEvent *xev = (XIDeviceEvent *) ev;
|
||||
GdkKeymap *keymap = gdk_keymap_get_for_display (display);
|
||||
GdkModifierType consumed, state;
|
||||
GdkDevice *device;
|
||||
GdkDevice *device, *source_device;
|
||||
|
||||
event->key.type = xev->evtype == XI_KeyPress ? GDK_KEY_PRESS : GDK_KEY_RELEASE;
|
||||
|
||||
@@ -925,6 +931,10 @@ gdk_device_manager_xi2_translate_event (GdkEventTranslator *translator,
|
||||
GUINT_TO_POINTER (xev->deviceid));
|
||||
gdk_event_set_device (event, device);
|
||||
|
||||
source_device = g_hash_table_lookup (device_manager->id_table,
|
||||
GUINT_TO_POINTER (xev->sourceid));
|
||||
gdk_event_set_source_device (event, source_device);
|
||||
|
||||
event->key.keyval = GDK_VoidSymbol;
|
||||
|
||||
gdk_keymap_translate_keyboard_state (keymap,
|
||||
@@ -953,6 +963,7 @@ gdk_device_manager_xi2_translate_event (GdkEventTranslator *translator,
|
||||
case XI_ButtonRelease:
|
||||
{
|
||||
XIDeviceEvent *xev = (XIDeviceEvent *) ev;
|
||||
GdkDevice *source_device;
|
||||
|
||||
switch (xev->detail)
|
||||
{
|
||||
@@ -981,6 +992,10 @@ gdk_device_manager_xi2_translate_event (GdkEventTranslator *translator,
|
||||
event->scroll.device = g_hash_table_lookup (device_manager->id_table,
|
||||
GUINT_TO_POINTER (xev->deviceid));
|
||||
|
||||
source_device = g_hash_table_lookup (device_manager->id_table,
|
||||
GUINT_TO_POINTER (xev->sourceid));
|
||||
gdk_event_set_source_device (event, source_device);
|
||||
|
||||
event->scroll.state = gdk_device_xi2_translate_state (&xev->mods, &xev->buttons);
|
||||
break;
|
||||
default:
|
||||
@@ -996,6 +1011,10 @@ gdk_device_manager_xi2_translate_event (GdkEventTranslator *translator,
|
||||
event->button.device = g_hash_table_lookup (device_manager->id_table,
|
||||
GUINT_TO_POINTER (xev->deviceid));
|
||||
|
||||
source_device = g_hash_table_lookup (device_manager->id_table,
|
||||
GUINT_TO_POINTER (xev->sourceid));
|
||||
gdk_event_set_source_device (event, source_device);
|
||||
|
||||
event->button.axes = translate_axes (event->button.device,
|
||||
event->button.x,
|
||||
event->button.y,
|
||||
@@ -1028,6 +1047,7 @@ gdk_device_manager_xi2_translate_event (GdkEventTranslator *translator,
|
||||
case XI_Motion:
|
||||
{
|
||||
XIDeviceEvent *xev = (XIDeviceEvent *) ev;
|
||||
GdkDevice *source_device;
|
||||
|
||||
event->motion.type = GDK_MOTION_NOTIFY;
|
||||
|
||||
@@ -1042,6 +1062,10 @@ gdk_device_manager_xi2_translate_event (GdkEventTranslator *translator,
|
||||
event->motion.device = g_hash_table_lookup (device_manager->id_table,
|
||||
GINT_TO_POINTER (xev->deviceid));
|
||||
|
||||
source_device = g_hash_table_lookup (device_manager->id_table,
|
||||
GUINT_TO_POINTER (xev->sourceid));
|
||||
gdk_event_set_source_device (event, source_device);
|
||||
|
||||
event->motion.state = gdk_device_xi2_translate_state (&xev->mods, &xev->buttons);
|
||||
|
||||
/* There doesn't seem to be motion hints in XI */
|
||||
@@ -1067,7 +1091,7 @@ gdk_device_manager_xi2_translate_event (GdkEventTranslator *translator,
|
||||
case XI_Leave:
|
||||
{
|
||||
XIEnterEvent *xev = (XIEnterEvent *) ev;
|
||||
GdkDevice *device;
|
||||
GdkDevice *device, *source_device;
|
||||
|
||||
event->crossing.type = (ev->evtype == XI_Enter) ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
|
||||
|
||||
@@ -1085,6 +1109,10 @@ gdk_device_manager_xi2_translate_event (GdkEventTranslator *translator,
|
||||
GINT_TO_POINTER (xev->deviceid));
|
||||
gdk_event_set_device (event, device);
|
||||
|
||||
source_device = g_hash_table_lookup (device_manager->id_table,
|
||||
GUINT_TO_POINTER (xev->sourceid));
|
||||
gdk_event_set_source_device (event, source_device);
|
||||
|
||||
event->crossing.mode = translate_crossing_mode (xev->mode);
|
||||
event->crossing.detail = translate_notify_type (xev->detail);
|
||||
event->crossing.state = gdk_device_xi2_translate_state (&xev->mods, &xev->buttons);
|
||||
@@ -1094,12 +1122,16 @@ gdk_device_manager_xi2_translate_event (GdkEventTranslator *translator,
|
||||
case XI_FocusOut:
|
||||
{
|
||||
XIEnterEvent *xev = (XIEnterEvent *) ev;
|
||||
GdkDevice *device;
|
||||
GdkDevice *device, *source_device;
|
||||
|
||||
device = g_hash_table_lookup (device_manager->id_table,
|
||||
GINT_TO_POINTER (xev->deviceid));
|
||||
|
||||
handle_focus_change (window, device, xev->detail, xev->mode,
|
||||
source_device = g_hash_table_lookup (device_manager->id_table,
|
||||
GUINT_TO_POINTER (xev->sourceid));
|
||||
|
||||
handle_focus_change (window, device, source_device,
|
||||
xev->detail, xev->mode,
|
||||
(ev->evtype == XI_FocusIn) ? TRUE : FALSE);
|
||||
|
||||
return_val = FALSE;
|
||||
|
@@ -1630,7 +1630,7 @@ device_ungrab_callback (GdkDisplay *display,
|
||||
{
|
||||
GdkDevice *device = data;
|
||||
|
||||
_gdk_display_device_grab_update (display, device, serial);
|
||||
_gdk_display_device_grab_update (display, device, NULL, serial);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -135,7 +135,7 @@ has_pointer_grab_callback (GdkDisplay *display,
|
||||
{
|
||||
GdkDevice *device = data;
|
||||
|
||||
_gdk_display_device_grab_update (display, device, serial);
|
||||
_gdk_display_device_grab_update (display, device, NULL, serial);
|
||||
}
|
||||
|
||||
GdkGrabStatus
|
||||
|
@@ -200,6 +200,7 @@ gtk_public_h_sources = \
|
||||
gtkcomboboxentry.h \
|
||||
gtkcontainer.h \
|
||||
gtkdebug.h \
|
||||
gtkdevicegroup.h \
|
||||
gtkdialog.h \
|
||||
gtkdnd.h \
|
||||
gtkdrawingarea.h \
|
||||
@@ -458,6 +459,7 @@ gtk_base_c_sources = \
|
||||
gtkcombobox.c \
|
||||
gtkcomboboxentry.c \
|
||||
gtkcontainer.c \
|
||||
gtkdevicegroup.c \
|
||||
gtkdialog.c \
|
||||
gtkdrawingarea.c \
|
||||
gtkeditable.c \
|
||||
|
@@ -4487,10 +4487,22 @@ gtk_widget_set_mapped
|
||||
gtk_widget_get_mapped
|
||||
gtk_widget_get_support_multidevice
|
||||
gtk_widget_set_support_multidevice
|
||||
gtk_widget_create_device_group
|
||||
gtk_widget_remove_device_group
|
||||
gtk_widget_get_group_for_device
|
||||
gtk_widget_device_is_shadowed
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if IN_HEADER(__GTK_DEVICE_GROUP_H__)
|
||||
#if IN_FILE(__GTK_DEVICE_GROUP_C__)
|
||||
gtk_device_group_get_type G_GNUC_CONST
|
||||
gtk_device_group_add_device
|
||||
gtk_device_group_remove_device
|
||||
gtk_device_group_get_devices
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if IN_HEADER(__GTK_WINDOW_H__)
|
||||
#if IN_FILE(__GTK_WINDOW_C__)
|
||||
gtk_window_activate_default
|
||||
|
215
gtk/gtkdevicegroup.c
Normal file
215
gtk/gtkdevicegroup.c
Normal file
@@ -0,0 +1,215 @@
|
||||
/* GTK - The GIMP Toolkit
|
||||
* Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkdevicegroup.h"
|
||||
#include "gtkalias.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkdevicegroup
|
||||
* @Short_description: Group of input devices for multidevice events.
|
||||
* @Title: Device groups
|
||||
* @See_also: #GtkMultiDeviceEvent
|
||||
*
|
||||
* #GtkDeviceGroup defines a group of devices, they are created through
|
||||
* gtk_widget_create_device_group() and destroyed through
|
||||
* gtk_widget_remove_device_group(). Device groups are used by its
|
||||
* corresponding #GtkWidget in order to issue #GtkMultiDeviceEvent<!-- -->s
|
||||
* whenever any of the contained devices emits a #GDK_MOTION_NOTIFY
|
||||
* event, or any device enters or leaves the group.
|
||||
*/
|
||||
|
||||
typedef struct GtkDeviceGroupPrivate GtkDeviceGroupPrivate;
|
||||
|
||||
struct GtkDeviceGroupPrivate
|
||||
{
|
||||
GList *devices;
|
||||
};
|
||||
|
||||
#define GTK_DEVICE_GROUP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_DEVICE_GROUP, GtkDeviceGroupPrivate))
|
||||
|
||||
static void gtk_device_group_class_init (GtkDeviceGroupClass *klass);
|
||||
static void gtk_device_group_init (GtkDeviceGroup *group);
|
||||
|
||||
static void gtk_device_group_finalize (GObject *object);
|
||||
|
||||
enum {
|
||||
DEVICE_ADDED,
|
||||
DEVICE_REMOVED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
G_DEFINE_TYPE (GtkDeviceGroup, gtk_device_group, G_TYPE_OBJECT)
|
||||
|
||||
|
||||
static void
|
||||
gtk_device_group_class_init (GtkDeviceGroupClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = gtk_device_group_finalize;
|
||||
|
||||
/**
|
||||
* GtkDeviceGroup::device-added:
|
||||
* @device_group: the object that received the signal
|
||||
* @device: the device that was just added
|
||||
*
|
||||
* This signal is emitted right after a #GdkDevice is added
|
||||
* to @device_group.
|
||||
*/
|
||||
signals[DEVICE_ADDED] =
|
||||
g_signal_new (I_("device-added"),
|
||||
G_TYPE_FROM_CLASS (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GtkDeviceGroupClass, device_added),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__OBJECT,
|
||||
G_TYPE_NONE, 1, GDK_TYPE_DEVICE);
|
||||
/**
|
||||
* GtkDeviceGroup::device-removed:
|
||||
* @device_group: the object that received the signal
|
||||
* @device: the device that was just removed
|
||||
*
|
||||
* This signal is emitted right after a #GdkDevice is removed
|
||||
* from @device_group.
|
||||
*/
|
||||
signals[DEVICE_REMOVED] =
|
||||
g_signal_new (I_("device-removed"),
|
||||
G_TYPE_FROM_CLASS (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GtkDeviceGroupClass, device_removed),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__OBJECT,
|
||||
G_TYPE_NONE, 1, GDK_TYPE_DEVICE);
|
||||
|
||||
g_type_class_add_private (object_class, sizeof (GtkDeviceGroupPrivate));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_device_group_init (GtkDeviceGroup *group)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_device_group_finalize (GObject *object)
|
||||
{
|
||||
GtkDeviceGroupPrivate *priv;
|
||||
|
||||
priv = GTK_DEVICE_GROUP_GET_PRIVATE (object);
|
||||
|
||||
g_list_foreach (priv->devices, (GFunc) g_object_unref, NULL);
|
||||
g_list_free (priv->devices);
|
||||
|
||||
G_OBJECT_CLASS (gtk_device_group_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_device_group_add_device:
|
||||
* @group: a #GtkDeviceGroup
|
||||
* @device: a #GdkDevice
|
||||
*
|
||||
* Adds @device to @group, so events coming from this device will
|
||||
* trigger #GtkWidget::multidevice-event<!-- -->s for @group. Adding
|
||||
* devices with source %GDK_SOURCE_KEYBOARD is not allowed.
|
||||
*
|
||||
* Since: 3.0
|
||||
**/
|
||||
void
|
||||
gtk_device_group_add_device (GtkDeviceGroup *group,
|
||||
GdkDevice *device)
|
||||
{
|
||||
GtkDeviceGroupPrivate *priv;
|
||||
|
||||
g_return_if_fail (GTK_IS_DEVICE_GROUP (group));
|
||||
g_return_if_fail (GDK_IS_DEVICE (device));
|
||||
g_return_if_fail (device->source != GDK_SOURCE_KEYBOARD);
|
||||
|
||||
priv = GTK_DEVICE_GROUP_GET_PRIVATE (group);
|
||||
|
||||
if (g_list_find (priv->devices, device))
|
||||
return;
|
||||
|
||||
priv->devices = g_list_prepend (priv->devices,
|
||||
g_object_ref (device));
|
||||
|
||||
g_signal_emit (group, signals[DEVICE_ADDED], 0, device);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_device_group_remove_device:
|
||||
* @group: a #GtkDeviceGroup
|
||||
* @device: a #GdkDevice
|
||||
*
|
||||
* Removes @device from @group, if it was there.
|
||||
*
|
||||
* Since: 3.0
|
||||
**/
|
||||
void
|
||||
gtk_device_group_remove_device (GtkDeviceGroup *group,
|
||||
GdkDevice *device)
|
||||
{
|
||||
GtkDeviceGroupPrivate *priv;
|
||||
GList *dev;
|
||||
|
||||
g_return_if_fail (GTK_IS_DEVICE_GROUP (group));
|
||||
g_return_if_fail (GDK_IS_DEVICE (device));
|
||||
|
||||
priv = GTK_DEVICE_GROUP_GET_PRIVATE (group);
|
||||
|
||||
dev = g_list_find (priv->devices, device);
|
||||
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
priv->devices = g_list_remove_link (priv->devices, dev);
|
||||
|
||||
g_signal_emit (group, signals[DEVICE_REMOVED], 0, device);
|
||||
|
||||
g_object_unref (dev->data);
|
||||
g_list_free_1 (dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_device_group_get_devices:
|
||||
* @group: a #GtkDeviceGroup
|
||||
*
|
||||
* Returns a #GList of #GdkDevices with the devices contained in @group.
|
||||
*
|
||||
* Returns: a list of #GdkDevices. This list and its elements are owned
|
||||
* by group, and must not be freed or unref'ed.
|
||||
*
|
||||
* Since: 3.0
|
||||
**/
|
||||
GList *
|
||||
gtk_device_group_get_devices (GtkDeviceGroup *group)
|
||||
{
|
||||
GtkDeviceGroupPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_DEVICE_GROUP (group), NULL);
|
||||
|
||||
priv = GTK_DEVICE_GROUP_GET_PRIVATE (group);
|
||||
|
||||
return priv->devices;
|
||||
}
|
||||
|
||||
#define __GTK_DEVICE_GROUP_C__
|
||||
#include "gtkaliasdef.c"
|
71
gtk/gtkdevicegroup.h
Normal file
71
gtk/gtkdevicegroup.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* GTK - The GIMP Toolkit
|
||||
* Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#if defined(GTK_DISABLE_SINGLE_INCLUDES) && !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#ifndef __GTK_DEVICE_GROUP_H__
|
||||
#define __GTK_DEVICE_GROUP_H__
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct GtkDeviceGroup GtkPointerGroup;
|
||||
|
||||
#define GTK_TYPE_DEVICE_GROUP (gtk_device_group_get_type ())
|
||||
#define GTK_DEVICE_GROUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_DEVICE_GROUP, GtkDeviceGroup))
|
||||
#define GTK_DEVICE_GROUP_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GTK_TYPE_DEVICE_GROUP, GtkDeviceGroupClass))
|
||||
#define GTK_IS_DEVICE_GROUP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_DEVICE_GROUP))
|
||||
#define GTK_IS_DEVICE_GROUP_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GTK_TYPE_DEVICE_GROUP))
|
||||
#define GTK_DEVICE_GROUP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_DEVICE_GROUP, GtkDeviceGroupClass))
|
||||
|
||||
typedef struct GtkDeviceGroup GtkDeviceGroup;
|
||||
typedef struct GtkDeviceGroupClass GtkDeviceGroupClass;
|
||||
|
||||
struct GtkDeviceGroup
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
struct GtkDeviceGroupClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (* device_added) (GtkDeviceGroup *group,
|
||||
GdkDevice *device);
|
||||
void (* device_removed) (GtkDeviceGroup *group,
|
||||
GdkDevice *device);
|
||||
};
|
||||
|
||||
GType gtk_device_group_get_type (void) G_GNUC_CONST;
|
||||
|
||||
void gtk_device_group_add_device (GtkDeviceGroup *group,
|
||||
GdkDevice *device);
|
||||
|
||||
void gtk_device_group_remove_device (GtkDeviceGroup *group,
|
||||
GdkDevice *device);
|
||||
|
||||
GList * gtk_device_group_get_devices (GtkDeviceGroup *group);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_DEVICE_GROUP_H__ */
|
380
gtk/gtkwidget.c
380
gtk/gtkwidget.c
@@ -200,6 +200,7 @@ enum {
|
||||
KEYNAV_FAILED,
|
||||
DRAG_FAILED,
|
||||
DAMAGE_EVENT,
|
||||
MULTIDEVICE_EVENT,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
@@ -231,6 +232,7 @@ enum {
|
||||
};
|
||||
|
||||
typedef struct _GtkStateData GtkStateData;
|
||||
typedef struct _GtkMultiDeviceData GtkMultiDeviceData;
|
||||
|
||||
struct _GtkStateData
|
||||
{
|
||||
@@ -240,6 +242,12 @@ struct _GtkStateData
|
||||
guint use_forall : 1;
|
||||
};
|
||||
|
||||
struct _GtkMultiDeviceData
|
||||
{
|
||||
GList *groups;
|
||||
GHashTable *by_dev;
|
||||
};
|
||||
|
||||
/* --- prototypes --- */
|
||||
static void gtk_widget_class_init (GtkWidgetClass *klass);
|
||||
static void gtk_widget_base_class_finalize (GtkWidgetClass *klass);
|
||||
@@ -395,6 +403,8 @@ static GQuark quark_mnemonic_labels = 0;
|
||||
static GQuark quark_tooltip_markup = 0;
|
||||
static GQuark quark_has_tooltip = 0;
|
||||
static GQuark quark_tooltip_window = 0;
|
||||
static GQuark quark_multidevice_events = 0;
|
||||
static GQuark quark_multidevice_data = 0;
|
||||
GParamSpecPool *_gtk_widget_child_property_pool = NULL;
|
||||
GObjectNotifyContext *_gtk_widget_child_property_notify_context = NULL;
|
||||
|
||||
@@ -492,6 +502,8 @@ gtk_widget_class_init (GtkWidgetClass *klass)
|
||||
quark_tooltip_markup = g_quark_from_static_string ("gtk-tooltip-markup");
|
||||
quark_has_tooltip = g_quark_from_static_string ("gtk-has-tooltip");
|
||||
quark_tooltip_window = g_quark_from_static_string ("gtk-tooltip-window");
|
||||
quark_multidevice_events = g_quark_from_static_string ("gtk-multidevice-events");
|
||||
quark_multidevice_data = g_quark_from_static_string ("gtk-multidevice-data");
|
||||
|
||||
style_property_spec_pool = g_param_spec_pool_new (FALSE);
|
||||
_gtk_widget_child_property_pool = g_param_spec_pool_new (TRUE);
|
||||
@@ -2423,6 +2435,29 @@ gtk_widget_class_init (GtkWidgetClass *klass)
|
||||
_gtk_marshal_BOOLEAN__UINT,
|
||||
G_TYPE_BOOLEAN, 1, G_TYPE_UINT);
|
||||
|
||||
/**
|
||||
* GtkWidget::multidevice-event:
|
||||
* @widget: the object which received the signal
|
||||
* @device_group: the #GtkDeviceGroup that was updated by a
|
||||
* device event
|
||||
* @event: a #GtkMultiDeviceEvent containing event information
|
||||
* for all devices in @device_group.
|
||||
*
|
||||
* This signal is emitted right after an input device that is
|
||||
* contained in @device_group emits one event, or whenever a
|
||||
* #GdkDevice is added or removed from @device_group.
|
||||
*
|
||||
* Since: 3.0
|
||||
*/
|
||||
widget_signals[MULTIDEVICE_EVENT] =
|
||||
g_signal_new (I_("multidevice-event"),
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0, NULL, NULL,
|
||||
_gtk_marshal_VOID__OBJECT_POINTER,
|
||||
G_TYPE_NONE, 2,
|
||||
GTK_TYPE_DEVICE_GROUP, G_TYPE_POINTER);
|
||||
|
||||
binding_set = gtk_binding_set_by_class (klass);
|
||||
gtk_binding_entry_add_signal (binding_set, GDK_F10, GDK_SHIFT_MASK,
|
||||
"popup-menu", 0);
|
||||
@@ -4806,6 +4841,85 @@ event_window_is_still_viewable (GdkEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
compose_multidevice_event (GtkWidget *widget,
|
||||
GdkDevice *device,
|
||||
GdkEventMotion *new_event)
|
||||
{
|
||||
GHashTable *multidevice_events;
|
||||
GtkMultiDeviceEventType type;
|
||||
GtkDeviceGroup *group;
|
||||
GtkMultiDeviceData *data;
|
||||
GtkMultiDeviceEvent event;
|
||||
GdkEvent *updated_event;
|
||||
GList *devices;
|
||||
gint i = 0;
|
||||
|
||||
data = g_object_get_qdata ((GObject *) widget,
|
||||
quark_multidevice_data);
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
group = g_hash_table_lookup (data->by_dev, device);
|
||||
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
multidevice_events = g_object_get_qdata ((GObject *) widget,
|
||||
quark_multidevice_events);
|
||||
|
||||
if (G_UNLIKELY (!multidevice_events))
|
||||
{
|
||||
multidevice_events = g_hash_table_new_full (g_direct_hash,
|
||||
g_direct_equal,
|
||||
(GDestroyNotify) g_object_unref,
|
||||
(GDestroyNotify) gdk_event_free);
|
||||
g_object_set_qdata_full ((GObject *) widget,
|
||||
quark_multidevice_events,
|
||||
multidevice_events,
|
||||
(GDestroyNotify) g_hash_table_destroy);
|
||||
}
|
||||
|
||||
if (!new_event)
|
||||
{
|
||||
g_hash_table_remove (multidevice_events, device);
|
||||
type = GTK_EVENT_DEVICE_REMOVED;
|
||||
updated_event = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g_hash_table_lookup (multidevice_events, device) == NULL)
|
||||
type = GTK_EVENT_DEVICE_ADDED;
|
||||
else
|
||||
type = GTK_EVENT_DEVICE_UPDATED;
|
||||
|
||||
updated_event = gdk_event_copy ((GdkEvent *) new_event);
|
||||
|
||||
g_hash_table_insert (multidevice_events,
|
||||
g_object_ref (device),
|
||||
updated_event);
|
||||
}
|
||||
|
||||
devices = gtk_device_group_get_devices (group);
|
||||
|
||||
/* Compose event */
|
||||
event.type = type;
|
||||
event.n_events = g_list_length (devices);
|
||||
event.events = g_new0 (GdkEventMotion *, event.n_events);
|
||||
event.updated_event = (GdkEventMotion *) updated_event;
|
||||
event.updated_device = device;
|
||||
|
||||
while (devices)
|
||||
{
|
||||
event.events[i] = g_hash_table_lookup (multidevice_events, devices->data);
|
||||
devices = devices->next;
|
||||
i++;
|
||||
}
|
||||
|
||||
g_signal_emit (widget, widget_signals[MULTIDEVICE_EVENT], 0, group, &event);
|
||||
g_free (event.events);
|
||||
}
|
||||
|
||||
static gint
|
||||
gtk_widget_event_internal (GtkWidget *widget,
|
||||
GdkEvent *event)
|
||||
@@ -4928,6 +5042,15 @@ gtk_widget_event_internal (GtkWidget *widget,
|
||||
}
|
||||
if (signal_num != -1)
|
||||
g_signal_emit (widget, widget_signals[signal_num], 0, event, &return_val);
|
||||
|
||||
if (event->type == GDK_MOTION_NOTIFY &&
|
||||
(GTK_WIDGET_FLAGS (widget) & GTK_MULTIDEVICE) != 0)
|
||||
{
|
||||
GdkEventMotion *event_motion;
|
||||
|
||||
event_motion = (GdkEventMotion *) event;
|
||||
compose_multidevice_event (widget, event_motion->device, event_motion);
|
||||
}
|
||||
}
|
||||
if (WIDGET_REALIZED_FOR_EVENT (widget, event))
|
||||
g_signal_emit (widget, widget_signals[EVENT_AFTER], 0, event);
|
||||
@@ -11553,6 +11676,263 @@ gtk_widget_set_support_multidevice (GtkWidget *widget,
|
||||
gdk_window_set_support_multidevice (widget->window, support_multidevice);
|
||||
}
|
||||
|
||||
static GdkEventMotion *
|
||||
convert_event_to_motion (GdkEvent *event)
|
||||
{
|
||||
GdkEventMotion *new_event;
|
||||
|
||||
new_event = (GdkEventMotion *) gdk_event_new (GDK_MOTION_NOTIFY);
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
case GDK_BUTTON_PRESS:
|
||||
case GDK_2BUTTON_PRESS:
|
||||
case GDK_3BUTTON_PRESS:
|
||||
case GDK_BUTTON_RELEASE:
|
||||
new_event->window = g_object_ref (event->button.window);
|
||||
new_event->send_event = TRUE;
|
||||
new_event->time = event->button.time;
|
||||
new_event->x = event->button.x;
|
||||
new_event->y = event->button.y;
|
||||
|
||||
if (event->button.axes)
|
||||
new_event->axes = g_memdup (event->button.axes,
|
||||
sizeof (gdouble) * event->button.device->num_axes);
|
||||
|
||||
new_event->state = 0; /* FIXME */
|
||||
new_event->is_hint = FALSE;
|
||||
new_event->device = event->button.device;
|
||||
new_event->x_root = event->button.x_root;
|
||||
new_event->y_root = event->button.y_root;
|
||||
break;
|
||||
case GDK_ENTER_NOTIFY:
|
||||
case GDK_LEAVE_NOTIFY:
|
||||
new_event->window = g_object_ref (event->crossing.window);
|
||||
new_event->send_event = TRUE;
|
||||
new_event->time = event->crossing.time;
|
||||
new_event->x = event->crossing.x;
|
||||
new_event->y = event->crossing.y;
|
||||
new_event->axes = NULL; /* FIXME: not ideal for non-mice */
|
||||
new_event->state = 0; /* FIXME */
|
||||
new_event->is_hint = FALSE;
|
||||
new_event->x_root = event->crossing.x_root;
|
||||
new_event->y_root = event->crossing.y_root;
|
||||
gdk_event_set_device ((GdkEvent *) new_event, gdk_event_get_device ((GdkEvent *) event));
|
||||
break;
|
||||
default:
|
||||
g_warning ("Event with type %d can not be transformed to GdkEventMotion", event->type);
|
||||
gdk_event_free ((GdkEvent *) new_event);
|
||||
new_event = NULL;
|
||||
}
|
||||
|
||||
return new_event;
|
||||
}
|
||||
|
||||
static void
|
||||
device_group_device_added (GtkDeviceGroup *group,
|
||||
GdkDevice *device,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
GtkMultiDeviceData *data;
|
||||
GtkDeviceGroup *old_group;
|
||||
GdkEventMotion *new_event = NULL;
|
||||
GtkWidget *event_widget;
|
||||
GdkEvent *event;
|
||||
|
||||
data = g_object_get_qdata (G_OBJECT (widget), quark_multidevice_data);
|
||||
|
||||
if (G_UNLIKELY (!data))
|
||||
return;
|
||||
|
||||
/* Remove device from old group, if any */
|
||||
old_group = g_hash_table_lookup (data->by_dev, device);
|
||||
|
||||
if (old_group)
|
||||
gtk_device_group_remove_device (old_group, device);
|
||||
|
||||
g_hash_table_insert (data->by_dev,
|
||||
g_object_ref (device),
|
||||
g_object_ref (group));
|
||||
|
||||
event = gtk_get_current_event ();
|
||||
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
if (event->type == GDK_MOTION_NOTIFY)
|
||||
new_event = (GdkEventMotion *) event;
|
||||
else
|
||||
{
|
||||
event_widget = gtk_get_event_widget (event);
|
||||
|
||||
if (widget == event_widget)
|
||||
new_event = convert_event_to_motion (event);
|
||||
|
||||
gdk_event_free (event);
|
||||
}
|
||||
|
||||
if (new_event)
|
||||
{
|
||||
gtk_widget_event_internal (widget, (GdkEvent *) new_event);
|
||||
gdk_event_free ((GdkEvent *) new_event);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
device_group_device_removed (GtkDeviceGroup *group,
|
||||
GdkDevice *device,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
GtkMultiDeviceData *data;
|
||||
|
||||
data = g_object_get_qdata (G_OBJECT (widget), quark_multidevice_data);
|
||||
|
||||
g_assert (data != NULL);
|
||||
|
||||
compose_multidevice_event (widget, device, NULL);
|
||||
g_hash_table_remove (data->by_dev, device);
|
||||
}
|
||||
|
||||
static void
|
||||
free_multidevice_data (GtkMultiDeviceData *data)
|
||||
{
|
||||
g_list_foreach (data->groups, (GFunc) g_object_unref, NULL);
|
||||
g_list_free (data->groups);
|
||||
|
||||
g_hash_table_destroy (data->by_dev);
|
||||
|
||||
g_slice_free (GtkMultiDeviceData, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_widget_create_device_group:
|
||||
* @widget: a #GtkWidget
|
||||
*
|
||||
* Creates a new #GtkDeviceGroup for @widget. devices can be added
|
||||
* later through gtk_device_group_add_device(). Note that
|
||||
* #GdkDevice<!-- -->s can only pertain to one #GtkDeviceGroup for a
|
||||
* given #GtkWidget, so adding it to a new group will remove it from
|
||||
* its previous one.
|
||||
*
|
||||
* Returns: a newly created #GtkDeviceGroup. This object is owned by
|
||||
* @widget and must be destroyed through
|
||||
* gtk_widget_remove_device_group().
|
||||
*
|
||||
* Since: 3.0
|
||||
**/
|
||||
GtkDeviceGroup *
|
||||
gtk_widget_create_device_group (GtkWidget *widget)
|
||||
{
|
||||
GtkMultiDeviceData *data;
|
||||
GtkDeviceGroup *group;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
|
||||
|
||||
group = g_object_new (GTK_TYPE_DEVICE_GROUP, NULL);
|
||||
|
||||
g_signal_connect (group, "device-added",
|
||||
G_CALLBACK (device_group_device_added), widget);
|
||||
g_signal_connect (group, "device-removed",
|
||||
G_CALLBACK (device_group_device_removed), widget);
|
||||
|
||||
data = g_object_get_qdata (G_OBJECT (widget), quark_multidevice_data);
|
||||
|
||||
if (G_UNLIKELY (!data))
|
||||
{
|
||||
data = g_slice_new0 (GtkMultiDeviceData);
|
||||
data->by_dev = g_hash_table_new_full (g_direct_hash,
|
||||
g_direct_equal,
|
||||
(GDestroyNotify) g_object_unref,
|
||||
(GDestroyNotify) g_object_unref);
|
||||
|
||||
g_object_set_qdata_full (G_OBJECT (widget),
|
||||
quark_multidevice_data,
|
||||
data,
|
||||
(GDestroyNotify) free_multidevice_data);
|
||||
}
|
||||
|
||||
data->groups = g_list_prepend (data->groups, group);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_widget_remove_device_group:
|
||||
* @widget: a #GtkWidget
|
||||
* @group: a #GtkDeviceGroup
|
||||
*
|
||||
* If @group pertains to @widget, @group will be destroyed so no further
|
||||
* #GtkWidget::multidevice-event<!-- -->s are emitted for it.
|
||||
*
|
||||
* Since: 3.0
|
||||
**/
|
||||
void
|
||||
gtk_widget_remove_device_group (GtkWidget *widget,
|
||||
GtkDeviceGroup *group)
|
||||
{
|
||||
GtkMultiDeviceData *data;
|
||||
GList *devices, *g;
|
||||
|
||||
g_return_if_fail (GTK_IS_WIDGET (widget));
|
||||
g_return_if_fail (GTK_IS_DEVICE_GROUP (group));
|
||||
|
||||
data = g_object_get_qdata (G_OBJECT (widget), quark_multidevice_data);
|
||||
|
||||
if (G_UNLIKELY (!data))
|
||||
return;
|
||||
|
||||
g = g_list_find (data->groups, group);
|
||||
|
||||
if (G_UNLIKELY (!g))
|
||||
return;
|
||||
|
||||
devices = gtk_device_group_get_devices (group);
|
||||
|
||||
/* Free per-device data */
|
||||
while (devices)
|
||||
{
|
||||
g_hash_table_remove (data->by_dev, devices->data);
|
||||
devices = devices->next;
|
||||
}
|
||||
|
||||
/* Free group */
|
||||
data->groups = g_list_remove_link (data->groups, g);
|
||||
g_object_unref (g->data);
|
||||
g_list_free_1 (g);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_widget_get_group_for_device:
|
||||
* @widget: a #GtkWidget
|
||||
* @device: a #GdkDevice
|
||||
*
|
||||
* Returns the #GtkDeviceGroup containing the #GdkDevice, or %NULL if
|
||||
* there is none.
|
||||
*
|
||||
* Returns: a #GtkDeviceGroup, or %NULL.
|
||||
*
|
||||
* Since: 3.0
|
||||
**/
|
||||
GtkDeviceGroup *
|
||||
gtk_widget_get_group_for_device (GtkWidget *widget,
|
||||
GdkDevice *device)
|
||||
{
|
||||
GtkMultiDeviceData *data;
|
||||
GtkDeviceGroup *group = NULL;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
|
||||
g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
|
||||
|
||||
data = g_object_get_qdata (G_OBJECT (widget), quark_multidevice_data);
|
||||
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
group = g_hash_table_lookup (data->by_dev, device);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
static void
|
||||
_gtk_widget_set_has_focus (GtkWidget *widget,
|
||||
gboolean has_focus)
|
||||
|
@@ -37,6 +37,7 @@
|
||||
#include <gtk/gtkadjustment.h>
|
||||
#include <gtk/gtkstyle.h>
|
||||
#include <gtk/gtksettings.h>
|
||||
#include <gtk/gtkdevicegroup.h>
|
||||
#include <atk/atk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
@@ -202,6 +203,7 @@ typedef struct _GtkWidgetShapeInfo GtkWidgetShapeInfo;
|
||||
typedef struct _GtkClipboard GtkClipboard;
|
||||
typedef struct _GtkTooltip GtkTooltip;
|
||||
typedef struct _GtkWindow GtkWindow;
|
||||
typedef struct _GtkMultiDeviceEvent GtkMultiDeviceEvent;
|
||||
|
||||
|
||||
/**
|
||||
@@ -533,6 +535,43 @@ struct _GtkWidgetClass
|
||||
void (*_gtk_reserved7) (void);
|
||||
};
|
||||
|
||||
/**
|
||||
* GtkMultiDeviceEventType:
|
||||
* @GTK_EVENT_DEVICE_ADDED: A device was added to the device group.
|
||||
* @GTK_EVENT_DEVICE_REMOVED: A device was removed from the device group.
|
||||
* @GTK_EVENT_DEVICE_UPDATED: A device in the device group has updated its
|
||||
* state.
|
||||
*
|
||||
* Provides a hint about the change that initiated the
|
||||
* #GtkWidget::multidevice-event.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GTK_EVENT_DEVICE_ADDED,
|
||||
GTK_EVENT_DEVICE_REMOVED,
|
||||
GTK_EVENT_DEVICE_UPDATED
|
||||
} GtkMultiDeviceEventType;
|
||||
|
||||
/**
|
||||
* GtkMultiDeviceEvent:
|
||||
* @type: the event type.
|
||||
* @n_events: number of device events contained in this #GtkMultiDeviceEvent
|
||||
* @events: an array of #GdkEventMotion structs.
|
||||
* @updated_event: latest updated event, or %NULL if @type is %GDK_EVENT_DEVICE_REMOVED.
|
||||
* @updated_device: device that triggered the event.
|
||||
*
|
||||
* The #GtkMultiDeviceEvent struct contains information about latest state of
|
||||
* multiple device pointers. These devices are contained in a #GtkDeviceGroup.
|
||||
*/
|
||||
struct _GtkMultiDeviceEvent
|
||||
{
|
||||
GtkMultiDeviceEventType type;
|
||||
guint n_events;
|
||||
GdkEventMotion **events;
|
||||
GdkEventMotion *updated_event;
|
||||
GdkDevice *updated_device;
|
||||
};
|
||||
|
||||
struct _GtkWidgetAuxInfo
|
||||
{
|
||||
gint x;
|
||||
@@ -766,6 +805,11 @@ GdkPixmap * gtk_widget_get_snapshot (GtkWidget *widget,
|
||||
gboolean gtk_widget_get_support_multidevice (GtkWidget *widget);
|
||||
void gtk_widget_set_support_multidevice (GtkWidget *widget,
|
||||
gboolean support_multidevice);
|
||||
GtkDeviceGroup * gtk_widget_get_group_for_device (GtkWidget *widget,
|
||||
GdkDevice *device);
|
||||
GtkDeviceGroup * gtk_widget_create_device_group (GtkWidget *widget);
|
||||
void gtk_widget_remove_device_group (GtkWidget *widget,
|
||||
GtkDeviceGroup *group);
|
||||
|
||||
/* Accessibility support */
|
||||
AtkObject* gtk_widget_get_accessible (GtkWidget *widget);
|
||||
|
@@ -1,6 +1,8 @@
|
||||
## Makefile.am for gtk+/tests
|
||||
include $(top_srcdir)/Makefile.decl
|
||||
|
||||
SUBDIRS = multidevice
|
||||
|
||||
INCLUDES = \
|
||||
-I$(top_srcdir) \
|
||||
-I$(top_builddir)/gdk \
|
||||
|
47
tests/multidevice/Makefile.am
Normal file
47
tests/multidevice/Makefile.am
Normal file
@@ -0,0 +1,47 @@
|
||||
## Makefile.am for gtk+/tests/mpx
|
||||
include $(top_srcdir)/Makefile.decl
|
||||
|
||||
INCLUDES = \
|
||||
-I$(top_srcdir) \
|
||||
-I$(top_builddir)/gdk \
|
||||
-I$(top_srcdir)/gdk \
|
||||
-DGDK_DISABLE_DEPRECATED \
|
||||
-DGTK_DISABLE_DEPRECATED \
|
||||
$(GTK_DEBUG_FLAGS) \
|
||||
$(GTK_DEP_CFLAGS)
|
||||
|
||||
DEPS = \
|
||||
$(top_builddir)/gdk-pixbuf/libgdk_pixbuf-$(GTK_API_VERSION).la \
|
||||
$(top_builddir)/gdk/$(gdktargetlib) \
|
||||
$(top_builddir)/gtk/$(gtktargetlib)
|
||||
|
||||
LDADDS = \
|
||||
$(top_builddir)/gdk-pixbuf/libgdk_pixbuf-$(GTK_API_VERSION).la \
|
||||
$(top_builddir)/gdk/$(gdktargetlib) \
|
||||
$(top_builddir)/gtk/$(gtktargetlib)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
testangle \
|
||||
testcoordinates \
|
||||
testphotoalbum
|
||||
|
||||
testangle_DEPENDENCIES = $(TEST_DEPS)
|
||||
testcoordinates_DEPENDENCIES = $(TEST_DEPS)
|
||||
testphotoalbum_DEPENDENCIES = $(TEST_DEPS)
|
||||
|
||||
testangle_LDADD = $(LDADDS)
|
||||
testcoordinates_LDADD = $(LDADDS)
|
||||
testphotoalbum_LDADD = $(LDADDS)
|
||||
|
||||
testangle_SOURCES = \
|
||||
testangle.c
|
||||
|
||||
testcoordinates_SOURCES = \
|
||||
testcoordinatewidget.c \
|
||||
testcoordinatewidget.h \
|
||||
testcoordinates.c
|
||||
|
||||
testphotoalbum_SOURCES = \
|
||||
testphotoalbumwidget.c \
|
||||
testphotoalbumwidget.h \
|
||||
testphotoalbum.c
|
197
tests/multidevice/testangle.c
Normal file
197
tests/multidevice/testangle.c
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
|
||||
*
|
||||
* This work is provided "as is"; redistribution and modification
|
||||
* in whole or in part, in any medium, physical or electronic is
|
||||
* permitted without restriction.
|
||||
*
|
||||
* This work is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* In no event shall the authors or contributors be liable for any
|
||||
* direct, indirect, incidental, special, exemplary, or consequential
|
||||
* damages (including, but not limited to, procurement of substitute
|
||||
* goods or services; loss of use, data, or profits; or business
|
||||
* interruption) however caused and on any theory of liability, whether
|
||||
* in contract, strict liability, or tort (including negligence or
|
||||
* otherwise) arising in any way out of the use of this software, even
|
||||
* if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
typedef struct DeviceData DeviceData;
|
||||
typedef struct Data Data;
|
||||
|
||||
struct Data
|
||||
{
|
||||
GtkDeviceGroup *group;
|
||||
|
||||
gdouble x;
|
||||
gdouble y;
|
||||
gdouble angle;
|
||||
gdouble distance;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
button_press_cb (GtkWidget *widget,
|
||||
GdkEvent *event,
|
||||
gpointer user_data)
|
||||
{
|
||||
Data *data;
|
||||
GList *devices;
|
||||
|
||||
data = (Data *) user_data;
|
||||
devices = gtk_device_group_get_devices (data->group);
|
||||
|
||||
if (g_list_length (devices) < 2)
|
||||
gtk_device_group_add_device (data->group, gdk_event_get_device (event));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
button_release_cb (GtkWidget *widget,
|
||||
GdkEvent *event,
|
||||
gpointer user_data)
|
||||
{
|
||||
Data *data;
|
||||
|
||||
data = (Data *) user_data;
|
||||
|
||||
gtk_device_group_remove_device (data->group, gdk_event_get_device (event));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
multidevice_cb (GtkWidget *widget,
|
||||
GtkDeviceGroup *group,
|
||||
GtkMultiDeviceEvent *event,
|
||||
gpointer user_data)
|
||||
{
|
||||
Data *data;
|
||||
|
||||
data = user_data;
|
||||
|
||||
if (event->n_events > 0)
|
||||
{
|
||||
data->x = event->events[0]->x;
|
||||
data->y = event->events[0]->y;
|
||||
}
|
||||
|
||||
if (event->n_events > 1)
|
||||
{
|
||||
gdk_events_get_distance ((GdkEvent *) event->events[0],
|
||||
(GdkEvent *) event->events[1],
|
||||
&data->distance);
|
||||
gdk_events_get_angle ((GdkEvent *) event->events[0],
|
||||
(GdkEvent *) event->events[1],
|
||||
&data->angle);
|
||||
}
|
||||
else
|
||||
{
|
||||
data->distance = 0;
|
||||
data->angle = 0;
|
||||
}
|
||||
|
||||
gtk_widget_queue_draw (widget);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
expose_cb (GtkWidget *widget,
|
||||
GdkEventExpose *event,
|
||||
gpointer user_data)
|
||||
{
|
||||
Data *data;
|
||||
cairo_t *cr;
|
||||
GList *devices;
|
||||
|
||||
data = (Data *) user_data;
|
||||
|
||||
devices = gtk_device_group_get_devices (data->group);
|
||||
|
||||
if (!devices)
|
||||
return TRUE;
|
||||
|
||||
cr = gdk_cairo_create (widget->window);
|
||||
cairo_translate (cr, data->x, data->y);
|
||||
|
||||
cairo_save (cr);
|
||||
|
||||
cairo_set_source_rgb (cr, 0., 0., 0.);
|
||||
cairo_move_to (cr, 0, 0);
|
||||
cairo_rel_line_to (cr, 1000, 0);
|
||||
cairo_stroke (cr);
|
||||
|
||||
cairo_restore (cr);
|
||||
|
||||
if (data->angle > 0)
|
||||
{
|
||||
cairo_save (cr);
|
||||
|
||||
cairo_set_source_rgb (cr, 0, 0, 1);
|
||||
cairo_move_to (cr, 0, 0);
|
||||
|
||||
cairo_arc (cr,
|
||||
0.,
|
||||
0.,
|
||||
MAX (10., data->distance),
|
||||
0.,
|
||||
data->angle);
|
||||
|
||||
cairo_close_path (cr);
|
||||
|
||||
cairo_fill (cr);
|
||||
|
||||
cairo_restore (cr);
|
||||
|
||||
cairo_set_source_rgb (cr, 1., 0., 0.);
|
||||
cairo_rotate (cr, data->angle);
|
||||
cairo_move_to (cr, 0, 0);
|
||||
cairo_rel_line_to (cr, data->distance, 0);
|
||||
cairo_stroke (cr);
|
||||
}
|
||||
|
||||
cairo_destroy (cr);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, gchar *argv[])
|
||||
{
|
||||
GtkWidget *window;
|
||||
Data data = { 0 };
|
||||
|
||||
gtk_init (&argc, &argv);
|
||||
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_widget_set_app_paintable (window, TRUE);
|
||||
|
||||
data.group = gtk_widget_create_device_group (window);
|
||||
|
||||
gtk_widget_add_events (window,
|
||||
(GDK_POINTER_MOTION_MASK |
|
||||
GDK_BUTTON_PRESS_MASK |
|
||||
GDK_BUTTON_RELEASE_MASK |
|
||||
GDK_BUTTON_MOTION_MASK));
|
||||
|
||||
gtk_widget_set_support_multidevice (window, TRUE);
|
||||
|
||||
g_signal_connect (window, "button-press-event",
|
||||
G_CALLBACK (button_press_cb), &data);
|
||||
g_signal_connect (window, "button-release-event",
|
||||
G_CALLBACK (button_release_cb), &data);
|
||||
g_signal_connect (window, "multidevice-event",
|
||||
G_CALLBACK (multidevice_cb), &data);
|
||||
g_signal_connect (window, "expose-event",
|
||||
G_CALLBACK (expose_cb), &data);
|
||||
|
||||
gtk_widget_show (window);
|
||||
|
||||
gtk_main ();
|
||||
|
||||
return 0;
|
||||
}
|
45
tests/multidevice/testcoordinates.c
Normal file
45
tests/multidevice/testcoordinates.c
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
|
||||
*
|
||||
* This work is provided "as is"; redistribution and modification
|
||||
* in whole or in part, in any medium, physical or electronic is
|
||||
* permitted without restriction.
|
||||
*
|
||||
* This work is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* In no event shall the authors or contributors be liable for any
|
||||
* direct, indirect, incidental, special, exemplary, or consequential
|
||||
* damages (including, but not limited to, procurement of substitute
|
||||
* goods or services; loss of use, data, or profits; or business
|
||||
* interruption) however caused and on any theory of liability, whether
|
||||
* in contract, strict liability, or tort (including negligence or
|
||||
* otherwise) arising in any way out of the use of this software, even
|
||||
* if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include "testcoordinatewidget.h"
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GtkWidget *window, *widget;
|
||||
|
||||
gtk_init (&argc, &argv);
|
||||
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
|
||||
widget = test_coordinate_widget_new ();
|
||||
gtk_container_add (GTK_CONTAINER (window), widget);
|
||||
|
||||
g_signal_connect (window, "delete-event",
|
||||
G_CALLBACK (gtk_main_quit), NULL);
|
||||
|
||||
gtk_widget_show_all (window);
|
||||
|
||||
gtk_main ();
|
||||
|
||||
return 0;
|
||||
}
|
251
tests/multidevice/testcoordinatewidget.c
Normal file
251
tests/multidevice/testcoordinatewidget.c
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
|
||||
*
|
||||
* This work is provided "as is"; redistribution and modification
|
||||
* in whole or in part, in any medium, physical or electronic is
|
||||
* permitted without restriction.
|
||||
*
|
||||
* This work is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* In no event shall the authors or contributors be liable for any
|
||||
* direct, indirect, incidental, special, exemplary, or consequential
|
||||
* damages (including, but not limited to, procurement of substitute
|
||||
* goods or services; loss of use, data, or profits; or business
|
||||
* interruption) however caused and on any theory of liability, whether
|
||||
* in contract, strict liability, or tort (including negligence or
|
||||
* otherwise) arising in any way out of the use of this software, even
|
||||
* if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#include "testcoordinatewidget.h"
|
||||
|
||||
#define TEST_COORDINATE_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TEST_TYPE_COORDINATE_WIDGET, TestCoordinateWidgetPrivate))
|
||||
|
||||
typedef struct TestCoordinateWidgetPrivate TestCoordinateWidgetPrivate;
|
||||
|
||||
struct TestCoordinateWidgetPrivate
|
||||
{
|
||||
GtkDeviceGroup *group;
|
||||
GHashTable *coordinates;
|
||||
};
|
||||
|
||||
static void test_coordinate_widget_class_init (TestCoordinateWidgetClass *klass);
|
||||
static void test_coordinate_widget_init (TestCoordinateWidget *coordinate);
|
||||
|
||||
static void test_coordinate_widget_destroy (GtkObject *object);
|
||||
|
||||
static gboolean test_coordinate_widget_button_press (GtkWidget *widget,
|
||||
GdkEventButton *event);
|
||||
static gboolean test_coordinate_widget_button_release (GtkWidget *widget,
|
||||
GdkEventButton *event);
|
||||
static gboolean test_coordinate_widget_expose (GtkWidget *widget,
|
||||
GdkEventExpose *event);
|
||||
|
||||
static void test_coordinate_widget_multidevice_event (GtkWidget *widget,
|
||||
GtkDeviceGroup *group,
|
||||
GtkMultiDeviceEvent *event);
|
||||
|
||||
G_DEFINE_TYPE (TestCoordinateWidget, test_coordinate_widget, GTK_TYPE_DRAWING_AREA)
|
||||
|
||||
|
||||
static void
|
||||
test_coordinate_widget_class_init (TestCoordinateWidgetClass *klass)
|
||||
{
|
||||
GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
|
||||
object_class->destroy = test_coordinate_widget_destroy;
|
||||
|
||||
widget_class->button_press_event = test_coordinate_widget_button_press;
|
||||
widget_class->button_release_event = test_coordinate_widget_button_release;
|
||||
widget_class->expose_event = test_coordinate_widget_expose;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (TestCoordinateWidgetPrivate));
|
||||
}
|
||||
|
||||
static void
|
||||
test_coordinate_widget_init (TestCoordinateWidget *coord)
|
||||
{
|
||||
TestCoordinateWidgetPrivate *priv;
|
||||
GtkWidget *widget;
|
||||
|
||||
priv = TEST_COORDINATE_WIDGET_GET_PRIVATE (coord);
|
||||
widget = GTK_WIDGET (coord);
|
||||
|
||||
priv->coordinates = g_hash_table_new_full (g_direct_hash,
|
||||
g_direct_equal,
|
||||
(GDestroyNotify) g_object_unref,
|
||||
(GDestroyNotify) g_free);
|
||||
|
||||
gtk_widget_add_events (widget,
|
||||
(GDK_BUTTON_PRESS_MASK |
|
||||
GDK_BUTTON_RELEASE_MASK |
|
||||
GDK_POINTER_MOTION_MASK |
|
||||
GDK_ENTER_NOTIFY_MASK |
|
||||
GDK_LEAVE_NOTIFY_MASK));
|
||||
|
||||
priv->group = gtk_widget_create_device_group (GTK_WIDGET (coord));
|
||||
gtk_widget_set_support_multidevice (widget, TRUE);
|
||||
|
||||
/* Multidevice events are not exposed through GtkWidgetClass */
|
||||
g_signal_connect (coord, "multidevice-event",
|
||||
G_CALLBACK (test_coordinate_widget_multidevice_event), NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
test_coordinate_widget_destroy (GtkObject *object)
|
||||
{
|
||||
TestCoordinateWidgetPrivate *priv;
|
||||
|
||||
priv = TEST_COORDINATE_WIDGET_GET_PRIVATE (object);
|
||||
|
||||
if (priv->coordinates)
|
||||
{
|
||||
g_hash_table_destroy (priv->coordinates);
|
||||
priv->coordinates = NULL;
|
||||
}
|
||||
|
||||
if (priv->group)
|
||||
{
|
||||
gtk_widget_remove_device_group (GTK_WIDGET (object), priv->group);
|
||||
priv->group = NULL;
|
||||
}
|
||||
|
||||
GTK_OBJECT_CLASS (test_coordinate_widget_parent_class)->destroy (object);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
test_coordinate_widget_button_press (GtkWidget *widget,
|
||||
GdkEventButton *event)
|
||||
{
|
||||
TestCoordinateWidgetPrivate *priv;
|
||||
GdkDevice *device;
|
||||
|
||||
priv = TEST_COORDINATE_WIDGET_GET_PRIVATE (widget);
|
||||
device = gdk_event_get_device ((GdkEvent *) event);
|
||||
|
||||
gtk_device_group_add_device (priv->group, device);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
test_coordinate_widget_button_release (GtkWidget *widget,
|
||||
GdkEventButton *event)
|
||||
{
|
||||
TestCoordinateWidgetPrivate *priv;
|
||||
GdkDevice *device;
|
||||
|
||||
priv = TEST_COORDINATE_WIDGET_GET_PRIVATE (widget);
|
||||
device = gdk_event_get_device ((GdkEvent *) event);
|
||||
|
||||
gtk_device_group_remove_device (priv->group, device);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_coordinate_widget_multidevice_event (GtkWidget *widget,
|
||||
GtkDeviceGroup *group,
|
||||
GtkMultiDeviceEvent *event)
|
||||
{
|
||||
TestCoordinateWidgetPrivate *priv;
|
||||
GdkEventMotion *ev;
|
||||
GdkPoint *point;
|
||||
|
||||
priv = TEST_COORDINATE_WIDGET_GET_PRIVATE (widget);
|
||||
|
||||
if (event->type == GTK_EVENT_DEVICE_REMOVED)
|
||||
g_hash_table_remove (priv->coordinates, event->updated_device);
|
||||
else
|
||||
{
|
||||
point = g_hash_table_lookup (priv->coordinates, event->updated_device);
|
||||
ev = event->updated_event;
|
||||
|
||||
if (G_UNLIKELY (!point))
|
||||
{
|
||||
point = g_new (GdkPoint, 1);
|
||||
g_hash_table_insert (priv->coordinates,
|
||||
g_object_ref (event->updated_device),
|
||||
point);
|
||||
}
|
||||
|
||||
point->x = ev->x;
|
||||
point->y = ev->y;
|
||||
}
|
||||
|
||||
gtk_widget_queue_draw (widget);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
test_coordinate_widget_expose (GtkWidget *widget,
|
||||
GdkEventExpose *event)
|
||||
{
|
||||
TestCoordinateWidgetPrivate *priv;
|
||||
GList *coords, *c;
|
||||
cairo_t *cr;
|
||||
|
||||
priv = TEST_COORDINATE_WIDGET_GET_PRIVATE (widget);
|
||||
cr = gdk_cairo_create (widget->window);
|
||||
|
||||
coords = g_hash_table_get_values (priv->coordinates);
|
||||
|
||||
cairo_set_source_rgb (cr, 1., 1., 1.);
|
||||
cairo_rectangle (cr,
|
||||
widget->allocation.x,
|
||||
widget->allocation.y,
|
||||
widget->allocation.width,
|
||||
widget->allocation.height);
|
||||
cairo_fill (cr);
|
||||
|
||||
cairo_set_source_rgb (cr, 0.8, 0.8, 0.8);
|
||||
|
||||
for (c = coords; c; c = c->next)
|
||||
{
|
||||
GdkPoint *point;
|
||||
|
||||
point = c->data;
|
||||
|
||||
cairo_move_to (cr, point->x, 0);
|
||||
cairo_rel_line_to (cr, 0, widget->allocation.height);
|
||||
|
||||
cairo_move_to (cr, 0, point->y);
|
||||
cairo_rel_line_to (cr, widget->allocation.width, 0);
|
||||
}
|
||||
|
||||
cairo_stroke (cr);
|
||||
|
||||
cairo_set_source_rgba (cr, 0.5, 0., 0., 0.5);
|
||||
|
||||
if (g_list_length (coords) > 1)
|
||||
{
|
||||
for (c = coords; c; c = c->next)
|
||||
{
|
||||
GdkPoint *point;
|
||||
|
||||
point = c->data;
|
||||
|
||||
cairo_line_to (cr, point->x, point->y);
|
||||
}
|
||||
|
||||
cairo_close_path (cr);
|
||||
cairo_fill_preserve (cr);
|
||||
|
||||
cairo_set_source_rgba (cr, 0., 0., 0.5, 0.5);
|
||||
cairo_stroke (cr);
|
||||
}
|
||||
|
||||
cairo_destroy (cr);
|
||||
|
||||
g_list_free (coords);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
test_coordinate_widget_new (void)
|
||||
{
|
||||
return g_object_new (TEST_TYPE_COORDINATE_WIDGET, NULL);
|
||||
}
|
57
tests/multidevice/testcoordinatewidget.h
Normal file
57
tests/multidevice/testcoordinatewidget.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
|
||||
*
|
||||
* This work is provided "as is"; redistribution and modification
|
||||
* in whole or in part, in any medium, physical or electronic is
|
||||
* permitted without restriction.
|
||||
*
|
||||
* This work is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* In no event shall the authors or contributors be liable for any
|
||||
* direct, indirect, incidental, special, exemplary, or consequential
|
||||
* damages (including, but not limited to, procurement of substitute
|
||||
* goods or services; loss of use, data, or profits; or business
|
||||
* interruption) however caused and on any theory of liability, whether
|
||||
* in contract, strict liability, or tort (including negligence or
|
||||
* otherwise) arising in any way out of the use of this software, even
|
||||
* if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#ifndef __TEST_COORDINATE_WIDGET_H__
|
||||
#define __TEST_COORDINATE_WIDGET_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define TEST_TYPE_COORDINATE_WIDGET (test_coordinate_widget_get_type ())
|
||||
#define TEST_COORDINATE_WIDGET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TEST_TYPE_COORDINATE_WIDGET, TestCoordinateWidget))
|
||||
#define TEST_COORDINATE_WIDGET_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), TEST_TYPE_COORDINATE_WIDGET, TestCoordinateWidgetClass))
|
||||
#define TEST_IS_COORDINATE_WIDGET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TEST_TYPE_COORDINATE_WIDGET))
|
||||
#define TEST_IS_COORDINATE_WIDGET_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), TEST_TYPE_COORDINATE_WIDGET))
|
||||
#define TEST_COORDINATE_WIDGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TEST_TYPE_COORDINATE_WIDGET, TestCoordinateWidgetClass))
|
||||
|
||||
typedef struct TestCoordinateWidget TestCoordinateWidget;
|
||||
typedef struct TestCoordinateWidgetClass TestCoordinateWidgetClass;
|
||||
|
||||
struct TestCoordinateWidget
|
||||
{
|
||||
GtkDrawingArea parent_instance;
|
||||
};
|
||||
|
||||
struct TestCoordinateWidgetClass
|
||||
{
|
||||
GtkDrawingAreaClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
GType test_coordinate_widget_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GtkWidget * test_coordinate_widget_new (void);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __TEST_COORDINATE_WIDGET_H__ */
|
111
tests/multidevice/testphotoalbum.c
Normal file
111
tests/multidevice/testphotoalbum.c
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
|
||||
*
|
||||
* This work is provided "as is"; redistribution and modification
|
||||
* in whole or in part, in any medium, physical or electronic is
|
||||
* permitted without restriction.
|
||||
*
|
||||
* This work is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* In no event shall the authors or contributors be liable for any
|
||||
* direct, indirect, incidental, special, exemplary, or consequential
|
||||
* damages (including, but not limited to, procurement of substitute
|
||||
* goods or services; loss of use, data, or profits; or business
|
||||
* interruption) however caused and on any theory of liability, whether
|
||||
* in contract, strict liability, or tort (including negligence or
|
||||
* otherwise) arising in any way out of the use of this software, even
|
||||
* if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include "testphotoalbumwidget.h"
|
||||
|
||||
static void
|
||||
add_image (TestPhotoAlbumWidget *album,
|
||||
const gchar *image_path)
|
||||
{
|
||||
GdkPixbuf *pixbuf;
|
||||
|
||||
pixbuf = gdk_pixbuf_new_from_file_at_size (image_path,
|
||||
200, -1,
|
||||
NULL);
|
||||
if (pixbuf)
|
||||
{
|
||||
test_photo_album_widget_add_image (album, pixbuf);
|
||||
g_object_unref (pixbuf);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
read_directory (GFile *directory,
|
||||
TestPhotoAlbumWidget *album)
|
||||
{
|
||||
GFileEnumerator *enumerator;
|
||||
GError *error = NULL;
|
||||
GFileInfo *info;
|
||||
|
||||
enumerator = g_file_enumerate_children (directory,
|
||||
G_FILE_ATTRIBUTE_STANDARD_NAME,
|
||||
G_FILE_QUERY_INFO_NONE,
|
||||
NULL,
|
||||
&error);
|
||||
|
||||
if (error)
|
||||
{
|
||||
g_warning ("%s\n", error->message);
|
||||
g_error_free (error);
|
||||
return;
|
||||
}
|
||||
|
||||
while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)) != NULL)
|
||||
{
|
||||
GFile *file;
|
||||
gchar *path;
|
||||
|
||||
file = g_file_get_child (directory, g_file_info_get_name (info));
|
||||
|
||||
path = g_file_get_path (file);
|
||||
add_image (album, path);
|
||||
g_free (path);
|
||||
|
||||
g_object_unref (file);
|
||||
}
|
||||
|
||||
g_file_enumerator_close (enumerator, NULL, NULL);
|
||||
g_object_unref (enumerator);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GtkWidget *window, *widget;
|
||||
GFile *dir;
|
||||
|
||||
if (argc != 2)
|
||||
{
|
||||
g_print ("USAGE: %s <path-to-directory-with-images>\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gtk_init (&argc, &argv);
|
||||
|
||||
dir = g_file_new_for_commandline_arg (argv[1]);
|
||||
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
|
||||
widget = test_photo_album_widget_new ();
|
||||
gtk_container_add (GTK_CONTAINER (window), widget);
|
||||
|
||||
g_signal_connect (window, "delete-event",
|
||||
G_CALLBACK (gtk_main_quit), NULL);
|
||||
|
||||
read_directory (dir, TEST_PHOTO_ALBUM_WIDGET (widget));
|
||||
|
||||
gtk_widget_show_all (window);
|
||||
|
||||
gtk_main ();
|
||||
|
||||
return 0;
|
||||
}
|
619
tests/multidevice/testphotoalbumwidget.c
Normal file
619
tests/multidevice/testphotoalbumwidget.c
Normal file
@@ -0,0 +1,619 @@
|
||||
/*
|
||||
* Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
|
||||
*
|
||||
* This work is provided "as is"; redistribution and modification
|
||||
* in whole or in part, in any medium, physical or electronic is
|
||||
* permitted without restriction.
|
||||
*
|
||||
* This work is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* In no event shall the authors or contributors be liable for any
|
||||
* direct, indirect, incidental, special, exemplary, or consequential
|
||||
* damages (including, but not limited to, procurement of substitute
|
||||
* goods or services; loss of use, data, or profits; or business
|
||||
* interruption) however caused and on any theory of liability, whether
|
||||
* in contract, strict liability, or tort (including negligence or
|
||||
* otherwise) arising in any way out of the use of this software, even
|
||||
* if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#include "testphotoalbumwidget.h"
|
||||
#include <math.h>
|
||||
|
||||
#define TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TEST_TYPE_PHOTO_ALBUM_WIDGET, TestPhotoAlbumWidgetPrivate))
|
||||
|
||||
typedef struct TestPhotoAlbumWidgetPrivate TestPhotoAlbumWidgetPrivate;
|
||||
typedef struct TestPhoto TestPhoto;
|
||||
|
||||
struct TestPhoto
|
||||
{
|
||||
GtkDeviceGroup *group;
|
||||
gdouble center_x;
|
||||
gdouble center_y;
|
||||
gdouble x;
|
||||
gdouble y;
|
||||
gdouble angle;
|
||||
gdouble zoom;
|
||||
|
||||
cairo_surface_t *surface;
|
||||
|
||||
GdkPoint points[4];
|
||||
|
||||
gdouble base_zoom;
|
||||
gdouble base_angle;
|
||||
gdouble initial_distance;
|
||||
gdouble initial_angle;
|
||||
};
|
||||
|
||||
struct TestPhotoAlbumWidgetPrivate
|
||||
{
|
||||
GPtrArray *photos;
|
||||
};
|
||||
|
||||
static GQuark quark_group_photo = 0;
|
||||
|
||||
|
||||
static void test_photo_album_widget_class_init (TestPhotoAlbumWidgetClass *klass);
|
||||
static void test_photo_album_widget_init (TestPhotoAlbumWidget *album);
|
||||
|
||||
static void test_photo_album_widget_destroy (GtkObject *object);
|
||||
|
||||
static gboolean test_photo_album_widget_button_press (GtkWidget *widget,
|
||||
GdkEventButton *event);
|
||||
static gboolean test_photo_album_widget_button_release (GtkWidget *widget,
|
||||
GdkEventButton *event);
|
||||
static void test_photo_album_widget_multidevice_event (GtkWidget *widget,
|
||||
GtkDeviceGroup *group,
|
||||
GtkMultiDeviceEvent *event);
|
||||
static gboolean test_photo_album_widget_expose (GtkWidget *widget,
|
||||
GdkEventExpose *event);
|
||||
|
||||
G_DEFINE_TYPE (TestPhotoAlbumWidget, test_photo_album_widget, GTK_TYPE_DRAWING_AREA)
|
||||
|
||||
|
||||
static void
|
||||
test_photo_album_widget_class_init (TestPhotoAlbumWidgetClass *klass)
|
||||
{
|
||||
GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
|
||||
object_class->destroy = test_photo_album_widget_destroy;
|
||||
|
||||
widget_class->button_press_event = test_photo_album_widget_button_press;
|
||||
widget_class->button_release_event = test_photo_album_widget_button_release;
|
||||
widget_class->expose_event = test_photo_album_widget_expose;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (TestPhotoAlbumWidgetPrivate));
|
||||
|
||||
quark_group_photo = g_quark_from_static_string ("group-photo");
|
||||
}
|
||||
|
||||
static void
|
||||
test_photo_album_widget_init (TestPhotoAlbumWidget *album)
|
||||
{
|
||||
TestPhotoAlbumWidgetPrivate *priv;
|
||||
GtkWidget *widget;
|
||||
|
||||
priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (album);
|
||||
widget = GTK_WIDGET (album);
|
||||
|
||||
priv->photos = g_ptr_array_new ();
|
||||
|
||||
gtk_widget_add_events (widget,
|
||||
(GDK_POINTER_MOTION_MASK |
|
||||
GDK_BUTTON_MOTION_MASK |
|
||||
GDK_BUTTON_PRESS_MASK |
|
||||
GDK_BUTTON_RELEASE_MASK));
|
||||
|
||||
gtk_widget_set_support_multidevice (widget, TRUE);
|
||||
|
||||
/* Multidevice events are not exposed through GtkWidgetClass */
|
||||
g_signal_connect (album, "multidevice-event",
|
||||
G_CALLBACK (test_photo_album_widget_multidevice_event), NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
calculate_rotated_point (gdouble angle,
|
||||
gdouble zoom,
|
||||
gdouble center_x,
|
||||
gdouble center_y,
|
||||
gdouble point_x,
|
||||
gdouble point_y,
|
||||
gdouble *ret_x,
|
||||
gdouble *ret_y)
|
||||
{
|
||||
gdouble distance, xd, yd, ang;
|
||||
|
||||
if (angle == 0)
|
||||
{
|
||||
*ret_x = point_x;
|
||||
*ret_y = point_y;
|
||||
return;
|
||||
}
|
||||
|
||||
xd = center_x - point_x;
|
||||
yd = center_y - point_y;
|
||||
|
||||
if (xd == 0 && yd == 0)
|
||||
{
|
||||
*ret_x = center_x;
|
||||
*ret_y = center_y;
|
||||
return;
|
||||
}
|
||||
|
||||
distance = sqrt ((xd * xd) + (yd * yd));
|
||||
distance *= zoom;
|
||||
|
||||
ang = atan2 (xd, yd);
|
||||
|
||||
/* Invert angle */
|
||||
ang = (2 * G_PI) - ang;
|
||||
|
||||
/* Shift it 270° */
|
||||
ang += 3 * (G_PI / 2);
|
||||
|
||||
/* And constraint it to 0°-360° */
|
||||
ang = fmod (ang, 2 * G_PI);
|
||||
ang += angle;
|
||||
|
||||
*ret_x = center_x + (distance * cos (ang));
|
||||
*ret_y = center_y + (distance * sin (ang));
|
||||
}
|
||||
|
||||
static void
|
||||
test_photo_bounding_rect (TestPhoto *photo,
|
||||
GdkRectangle *rect)
|
||||
{
|
||||
gint i, left, right, top, bottom;
|
||||
|
||||
left = top = G_MAXINT;
|
||||
right = bottom = 0;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (photo->points[i].x < left)
|
||||
left = photo->points[i].x;
|
||||
|
||||
if (photo->points[i].x > right)
|
||||
right = photo->points[i].x;
|
||||
|
||||
if (photo->points[i].y < top)
|
||||
top = photo->points[i].y;
|
||||
|
||||
if (photo->points[i].y > bottom)
|
||||
bottom = photo->points[i].y;
|
||||
}
|
||||
|
||||
rect->x = left - 20;
|
||||
rect->y = top - 20;
|
||||
rect->width = right - left + 40;
|
||||
rect->height = bottom - top + 40;
|
||||
}
|
||||
|
||||
static void
|
||||
allocate_photo_region (TestPhoto *photo)
|
||||
{
|
||||
gint width, height, i;
|
||||
|
||||
width = cairo_image_surface_get_width (photo->surface);
|
||||
height = cairo_image_surface_get_height (photo->surface);
|
||||
|
||||
/* Top/left */
|
||||
photo->points[0].x = photo->x - photo->center_x;
|
||||
photo->points[0].y = photo->y - photo->center_y;
|
||||
|
||||
/* Top/right */
|
||||
photo->points[1].x = photo->x - photo->center_x + width;
|
||||
photo->points[1].y = photo->y - photo->center_y;
|
||||
|
||||
/* Bottom/right */
|
||||
photo->points[2].x = photo->x - photo->center_x + width;
|
||||
photo->points[2].y = photo->y - photo->center_y + height;
|
||||
|
||||
/* Bottom/left */
|
||||
photo->points[3].x = photo->x - photo->center_x;
|
||||
photo->points[3].y = photo->y - photo->center_y + height;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
gdouble ret_x, ret_y;
|
||||
|
||||
calculate_rotated_point (photo->angle,
|
||||
photo->zoom,
|
||||
photo->x,
|
||||
photo->y,
|
||||
(gdouble) photo->points[i].x,
|
||||
(gdouble) photo->points[i].y,
|
||||
&ret_x,
|
||||
&ret_y);
|
||||
|
||||
photo->points[i].x = (gint) ret_x;
|
||||
photo->points[i].y = (gint) ret_y;
|
||||
}
|
||||
}
|
||||
|
||||
static TestPhoto *
|
||||
test_photo_new (TestPhotoAlbumWidget *album,
|
||||
GdkPixbuf *pixbuf)
|
||||
{
|
||||
TestPhoto *photo;
|
||||
static gdouble angle = 0;
|
||||
gint width, height;
|
||||
cairo_t *cr;
|
||||
|
||||
photo = g_slice_new0 (TestPhoto);
|
||||
photo->group = gtk_widget_create_device_group (GTK_WIDGET (album));
|
||||
g_object_set_qdata (G_OBJECT (photo->group), quark_group_photo, photo);
|
||||
|
||||
photo->center_x = 0;
|
||||
photo->center_y = 0;
|
||||
photo->x = 0;
|
||||
photo->y = 0;
|
||||
photo->angle = angle;
|
||||
photo->zoom = 1.0;
|
||||
|
||||
angle += 0.2;
|
||||
|
||||
width = gdk_pixbuf_get_width (pixbuf);
|
||||
height = gdk_pixbuf_get_height (pixbuf);
|
||||
|
||||
/* Put the pixbuf into an image surface */
|
||||
photo->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
|
||||
|
||||
cr = cairo_create (photo->surface);
|
||||
gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
|
||||
cairo_paint (cr);
|
||||
|
||||
cairo_set_source_rgb (cr, 0., 0., 0.);
|
||||
cairo_rectangle (cr, 0, 0, width, height);
|
||||
cairo_stroke (cr);
|
||||
|
||||
allocate_photo_region (photo);
|
||||
|
||||
return photo;
|
||||
}
|
||||
|
||||
static void
|
||||
test_photo_free (TestPhoto *photo,
|
||||
TestPhotoAlbumWidget *album)
|
||||
{
|
||||
gtk_widget_remove_device_group (GTK_WIDGET (album), photo->group);
|
||||
cairo_surface_destroy (photo->surface);
|
||||
|
||||
g_slice_free (TestPhoto, photo);
|
||||
}
|
||||
|
||||
static void
|
||||
test_photo_raise (TestPhoto *photo,
|
||||
TestPhotoAlbumWidget *album)
|
||||
{
|
||||
TestPhotoAlbumWidgetPrivate *priv;
|
||||
|
||||
priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (album);
|
||||
g_ptr_array_remove (priv->photos, photo);
|
||||
g_ptr_array_add (priv->photos, photo);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
test_photo_point_in (TestPhoto *photo,
|
||||
gint x,
|
||||
gint y)
|
||||
{
|
||||
GdkPoint *left, *right, *top, *bottom;
|
||||
gint i;
|
||||
|
||||
left = right = top = bottom = NULL;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
GdkPoint *p = &photo->points[i];
|
||||
|
||||
if (!left ||
|
||||
p->x < left->x ||
|
||||
(p->x == left->x && p->y > left->y))
|
||||
left = p;
|
||||
|
||||
if (!right ||
|
||||
p->x > right->x ||
|
||||
(p->x == right->x && p->y < right->y))
|
||||
right = p;
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
GdkPoint *p = &photo->points[i];
|
||||
|
||||
if (p == left || p == right)
|
||||
continue;
|
||||
|
||||
if (!top ||
|
||||
p->y < top->y)
|
||||
top = p;
|
||||
|
||||
if (!bottom ||
|
||||
p->y > bottom->y)
|
||||
bottom = p;
|
||||
}
|
||||
|
||||
g_assert (left && right && top && bottom);
|
||||
|
||||
if (x < left->x ||
|
||||
x > right->x ||
|
||||
y < top->y ||
|
||||
y > bottom->y)
|
||||
return FALSE;
|
||||
|
||||
/* Check whether point is above the sides
|
||||
* between leftmost and topmost, and
|
||||
* topmost and rightmost corners.
|
||||
*/
|
||||
if (x <= top->x)
|
||||
{
|
||||
if (left->y - ((left->y - top->y) * (((gdouble) x - left->x) / (top->x - left->x))) > y)
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (top->y + ((right->y - top->y) * (((gdouble) x - top->x) / (right->x - top->x))) > y)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Check whether point is below the sides
|
||||
* between leftmost and bottom, and
|
||||
* bottom and rightmost corners.
|
||||
*/
|
||||
if (x <= bottom->x)
|
||||
{
|
||||
if (left->y + ((bottom->y - left->y) * (((gdouble) x - left->x) / (bottom->x - left->x))) < y)
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bottom->y - ((bottom->y - right->y) * (((gdouble) x - bottom->x) / (right->x - bottom->x))) < y)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_photo_album_widget_destroy (GtkObject *object)
|
||||
{
|
||||
TestPhotoAlbumWidgetPrivate *priv;
|
||||
|
||||
priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (object);
|
||||
|
||||
if (priv->photos)
|
||||
{
|
||||
g_ptr_array_foreach (priv->photos, (GFunc) test_photo_free, object);
|
||||
g_ptr_array_free (priv->photos, TRUE);
|
||||
priv->photos = NULL;
|
||||
}
|
||||
|
||||
GTK_OBJECT_CLASS (test_photo_album_widget_parent_class)->destroy (object);
|
||||
}
|
||||
|
||||
static TestPhoto *
|
||||
find_photo_at_position (TestPhotoAlbumWidget *album,
|
||||
gdouble x,
|
||||
gdouble y)
|
||||
{
|
||||
TestPhotoAlbumWidgetPrivate *priv;
|
||||
TestPhoto *photo;
|
||||
gint i;
|
||||
|
||||
priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (album);
|
||||
|
||||
for (i = priv->photos->len - 1; i >= 0; i--)
|
||||
{
|
||||
photo = g_ptr_array_index (priv->photos, i);
|
||||
|
||||
if (test_photo_point_in (photo, (gint) x, (gint) y))
|
||||
return photo;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
test_photo_album_widget_button_press (GtkWidget *widget,
|
||||
GdkEventButton *event)
|
||||
{
|
||||
TestPhoto *photo;
|
||||
|
||||
photo = find_photo_at_position (TEST_PHOTO_ALBUM_WIDGET (widget), event->x, event->y);
|
||||
|
||||
if (!photo)
|
||||
return FALSE;
|
||||
|
||||
test_photo_raise (photo, TEST_PHOTO_ALBUM_WIDGET (widget));
|
||||
gtk_device_group_add_device (photo->group, event->device);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
test_photo_album_widget_button_release (GtkWidget *widget,
|
||||
GdkEventButton *event)
|
||||
{
|
||||
GtkDeviceGroup *group;
|
||||
|
||||
group = gtk_widget_get_group_for_device (widget, event->device);
|
||||
|
||||
if (group)
|
||||
gtk_device_group_remove_device (group, event->device);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_photo_album_widget_multidevice_event (GtkWidget *widget,
|
||||
GtkDeviceGroup *group,
|
||||
GtkMultiDeviceEvent *event)
|
||||
{
|
||||
TestPhotoAlbumWidgetPrivate *priv;
|
||||
cairo_region_t *region;
|
||||
TestPhoto *photo;
|
||||
gboolean new_center = FALSE;
|
||||
gboolean new_position = FALSE;
|
||||
gdouble event_x, event_y;
|
||||
GdkRectangle rect;
|
||||
|
||||
priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (widget);
|
||||
photo = g_object_get_qdata (G_OBJECT (group), quark_group_photo);
|
||||
|
||||
test_photo_bounding_rect (photo, &rect);
|
||||
region = cairo_region_create_rectangle ((cairo_rectangle_int_t *) &rect);
|
||||
|
||||
if (event->n_events == 1)
|
||||
{
|
||||
if (event->type == GTK_EVENT_DEVICE_REMOVED)
|
||||
{
|
||||
/* Device was just removed, unset zoom/angle info */
|
||||
photo->base_zoom = 0;
|
||||
photo->base_angle = 0;
|
||||
photo->initial_angle = 0;
|
||||
photo->initial_distance = 0;
|
||||
new_center = TRUE;
|
||||
}
|
||||
else if (event->type == GTK_EVENT_DEVICE_ADDED)
|
||||
new_center = TRUE;
|
||||
|
||||
event_x = event->events[0]->x;
|
||||
event_y = event->events[0]->y;
|
||||
new_position = TRUE;
|
||||
}
|
||||
else if (event->n_events == 2)
|
||||
{
|
||||
gdouble distance, angle;
|
||||
|
||||
gdk_events_get_center ((GdkEvent *) event->events[0],
|
||||
(GdkEvent *) event->events[1],
|
||||
&event_x, &event_y);
|
||||
|
||||
gdk_events_get_distance ((GdkEvent *) event->events[0],
|
||||
(GdkEvent *) event->events[1],
|
||||
&distance);
|
||||
|
||||
gdk_events_get_angle ((GdkEvent *) event->events[0],
|
||||
(GdkEvent *) event->events[1],
|
||||
&angle);
|
||||
|
||||
if (event->type == GTK_EVENT_DEVICE_ADDED)
|
||||
{
|
||||
photo->base_zoom = photo->zoom;
|
||||
photo->base_angle = photo->angle;
|
||||
photo->initial_angle = angle;
|
||||
photo->initial_distance = distance;
|
||||
new_center = TRUE;
|
||||
}
|
||||
|
||||
photo->zoom = photo->base_zoom * (distance / photo->initial_distance);
|
||||
photo->angle = photo->base_angle + (angle - photo->initial_angle);
|
||||
new_position = TRUE;
|
||||
}
|
||||
|
||||
if (new_center)
|
||||
{
|
||||
gdouble origin_x, origin_y;
|
||||
|
||||
origin_x = photo->x - photo->center_x;
|
||||
origin_y = photo->y - photo->center_y;
|
||||
|
||||
calculate_rotated_point (- photo->angle,
|
||||
1 / photo->zoom,
|
||||
photo->x - origin_x,
|
||||
photo->y - origin_y,
|
||||
event_x - origin_x,
|
||||
event_y - origin_y,
|
||||
&photo->center_x,
|
||||
&photo->center_y);
|
||||
}
|
||||
|
||||
if (new_position)
|
||||
{
|
||||
photo->x = event_x;
|
||||
photo->y = event_y;
|
||||
}
|
||||
|
||||
allocate_photo_region (photo);
|
||||
|
||||
test_photo_bounding_rect (photo, &rect);
|
||||
cairo_region_union_rectangle (region, (cairo_rectangle_int_t *) &rect);
|
||||
|
||||
gdk_window_invalidate_region (widget->window, region, FALSE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
test_photo_album_widget_expose (GtkWidget *widget,
|
||||
GdkEventExpose *event)
|
||||
{
|
||||
TestPhotoAlbumWidgetPrivate *priv;
|
||||
cairo_t *cr;
|
||||
gint i;
|
||||
|
||||
priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (widget);
|
||||
cr = gdk_cairo_create (widget->window);
|
||||
|
||||
gdk_cairo_region (cr, event->region);
|
||||
cairo_clip (cr);
|
||||
|
||||
for (i = 0; i < priv->photos->len; i++)
|
||||
{
|
||||
TestPhoto *photo = g_ptr_array_index (priv->photos, i);
|
||||
GdkRectangle rect;
|
||||
|
||||
test_photo_bounding_rect (photo, &rect);
|
||||
|
||||
if (!gdk_rectangle_intersect (&rect, &event->area, NULL))
|
||||
continue;
|
||||
|
||||
cairo_save (cr);
|
||||
|
||||
cairo_translate (cr, photo->points[0].x, photo->points[0].y);
|
||||
cairo_scale (cr, photo->zoom, photo->zoom);
|
||||
cairo_rotate (cr, photo->angle);
|
||||
|
||||
cairo_set_source_surface (cr, photo->surface, 0, 0);
|
||||
cairo_paint (cr);
|
||||
|
||||
cairo_restore (cr);
|
||||
}
|
||||
|
||||
cairo_destroy (cr);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
test_photo_album_widget_new (void)
|
||||
{
|
||||
return g_object_new (TEST_TYPE_PHOTO_ALBUM_WIDGET, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
test_photo_album_widget_add_image (TestPhotoAlbumWidget *album,
|
||||
GdkPixbuf *pixbuf)
|
||||
{
|
||||
TestPhotoAlbumWidgetPrivate *priv;
|
||||
TestPhoto *photo;
|
||||
|
||||
g_return_if_fail (TEST_IS_PHOTO_ALBUM_WIDGET (album));
|
||||
g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
|
||||
|
||||
priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (album);
|
||||
|
||||
photo = test_photo_new (album, pixbuf);
|
||||
g_ptr_array_add (priv->photos, photo);
|
||||
|
||||
if (gtk_widget_get_realized (GTK_WIDGET (album)) &&
|
||||
gtk_widget_is_drawable (GTK_WIDGET (album)))
|
||||
{
|
||||
GdkRectangle rect;
|
||||
|
||||
test_photo_bounding_rect (photo, &rect);
|
||||
gdk_window_invalidate_rect (GTK_WIDGET (album)->window, &rect, FALSE);
|
||||
}
|
||||
}
|
60
tests/multidevice/testphotoalbumwidget.h
Normal file
60
tests/multidevice/testphotoalbumwidget.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
|
||||
*
|
||||
* This work is provided "as is"; redistribution and modification
|
||||
* in whole or in part, in any medium, physical or electronic is
|
||||
* permitted without restriction.
|
||||
*
|
||||
* This work is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* In no event shall the authors or contributors be liable for any
|
||||
* direct, indirect, incidental, special, exemplary, or consequential
|
||||
* damages (including, but not limited to, procurement of substitute
|
||||
* goods or services; loss of use, data, or profits; or business
|
||||
* interruption) however caused and on any theory of liability, whether
|
||||
* in contract, strict liability, or tort (including negligence or
|
||||
* otherwise) arising in any way out of the use of this software, even
|
||||
* if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
#ifndef __TEST_PHOTO_ALBUM_WIDGET_H__
|
||||
#define __TEST_PHOTO_ALBUM_WIDGET_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define TEST_TYPE_PHOTO_ALBUM_WIDGET (test_photo_album_widget_get_type ())
|
||||
#define TEST_PHOTO_ALBUM_WIDGET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TEST_TYPE_PHOTO_ALBUM_WIDGET, TestPhotoAlbumWidget))
|
||||
#define TEST_PHOTO_ALBUM_WIDGET_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), TEST_TYPE_PHOTO_ALBUM_WIDGET, TestPhotoAlbumWidgetClass))
|
||||
#define TEST_IS_PHOTO_ALBUM_WIDGET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TEST_TYPE_PHOTO_ALBUM_WIDGET))
|
||||
#define TEST_IS_PHOTO_ALBUM_WIDGET_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), TEST_TYPE_PHOTO_ALBUM_WIDGET))
|
||||
#define TEST_PHOTO_ALBUM_WIDGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TEST_TYPE_PHOTO_ALBUM_WIDGET, TestPhotoAlbumWidgetClass))
|
||||
|
||||
typedef struct TestPhotoAlbumWidget TestPhotoAlbumWidget;
|
||||
typedef struct TestPhotoAlbumWidgetClass TestPhotoAlbumWidgetClass;
|
||||
|
||||
struct TestPhotoAlbumWidget
|
||||
{
|
||||
GtkDrawingArea parent_instance;
|
||||
};
|
||||
|
||||
struct TestPhotoAlbumWidgetClass
|
||||
{
|
||||
GtkDrawingAreaClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
GType test_photo_album_widget_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GtkWidget * test_photo_album_widget_new (void);
|
||||
|
||||
void test_photo_album_widget_add_image (TestPhotoAlbumWidget *album,
|
||||
GdkPixbuf *pixbuf);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __TEST_PHOTO_ALBUM_WIDGET_H__ */
|
Reference in New Issue
Block a user