Compare commits

...

12 Commits

Author SHA1 Message Date
Carlos Garnacho
bb99289487 gtk-demo: Add multitouch demo
Pretty much ignores pointer events currently, so you're out
of luck to see anything happening at all if you don't have
a multitouch device...
2011-03-12 13:21:39 +01:00
Carlos Garnacho
f48204d4af Add multitouch-event signal and vfunc to GtkWidget 2011-03-11 23:33:15 +01:00
Carlos Garnacho
fa3f6fa0ed Add machinery to emit GdkEventMultiTouch events
These events are created from GDK_TOUCH_MOTION/PRESS/RELEASE
events, if the touch ID generating the event is within a
touch cluster, that event is stored and not pushed to the queue,
so a touch ID can only emit GDK_TOUCH_* or GDK_MULTITOUCH_* events
at the same time.
2011-03-11 23:33:14 +01:00
Carlos Garnacho
be5f2c7cd4 Define GdkEventMultiTouch and its related event types.
This event will gather all touches within a GdkTouchCluster,
including an array of the latest GDK_TOUCH_MOTION events for
the touch IDs contained in there.
2011-03-11 23:33:14 +01:00
Carlos Garnacho
776477ee96 Add gdk_window_[create|remove]_touch_cluster()
These are the functions to create/destroy a GdkTouchCluster,
as they are associated to GdkWindows.
2011-03-11 23:33:10 +01:00
Carlos Garnacho
4be23cb80c Introduce GdkTouchCluster
This is a per-window/device object to gather a group of touch IDs
as a single entity.
2011-03-11 23:32:51 +01:00
Carlos Garnacho
93162f080d Add gdk_event_get_touch_id()
Just a helper function to get the touch ID from touch events, it
returns FALSE in any other case.
2011-03-11 23:32:51 +01:00
Carlos Garnacho
8bc4f71f68 Make touch events go through csw/widget event handling.
In GtkWidget, touch events go through the same handler
than motion events, with the difference that touch_id
will have something meaningful there.

Touch events need to be explicitly selected, so if this
is enabled, the possibility of different motion streams
with different touch IDs must be handled in some way.
2011-03-11 23:32:51 +01:00
Carlos Garnacho
a0c29b0114 Handle TouchBegin/End events
These are translated into GDK_TOUCH_PRESS/RELEASE GdkEvents,
which use the GdkEventButton struct, a touch_id parameter
has been added there to cope with touches.
2011-03-11 23:32:50 +01:00
Carlos Garnacho
a898575fdd Add initial handling of TouchMotion events.
GdkDeviceManagerXI2 now handles TouchMotion and TouchMotionUnowned
events, which are translated to GDK_TOUCH_MOTION events.
2011-03-11 23:32:50 +01:00
Carlos Garnacho
02f62468aa Add touch motion event type and mask.
These events' struct is the same than GdkEventMotion, which has been
added a touch_id parameter. The gdk_event_* functions have been modified
to also handle this event type.
2011-03-11 22:03:30 +01:00
Carlos Garnacho
df3af16b3a configure: Detect XInput 2.1 2011-03-11 22:03:29 +01:00
24 changed files with 1806 additions and 87 deletions

View File

@@ -1078,6 +1078,10 @@ if test "x$enable_x11_backend" == xyes; then
AC_DEFINE(XINPUT_2, 1, [Define to 1 if XInput 2.0 is available]),
X_EXTENSIONS="$X_EXTENSIONS XInput")
gtk_save_LIBS="$LIBS"
LIBS="$LIBS -lXi"
AC_CHECK_FUNC(XIAllowTouchEvents, AC_DEFINE(XINPUT_2_1, 1, [Define to 1 if XInput 2.1 is available]))
LIBS="$gtk_save_LIBS"
else
AC_DEFINE(XINPUT_NONE, 1,
[Define to 1 if no XInput should be used])

View File

@@ -28,6 +28,7 @@ demos = \
links.c \
list_store.c \
menus.c \
multitouch.c \
offscreen_window.c \
offscreen_window2.c \
panes.c \

514
demos/gtk-demo/multitouch.c Normal file
View File

@@ -0,0 +1,514 @@
/* Multitouch
*
* Demonstrates some general multitouch event handling,
* using GdkTouchCluster in order to get grouped motion
* events for the touches within a cluster.
*/
#include <math.h>
#include <gtk/gtk.h>
#include "demo-common.h"
static GtkWidget *window = NULL;
static GList *shapes = NULL;
typedef struct {
GdkTouchCluster *cluster;
GdkRGBA color;
gdouble angle;
gdouble zoom;
gdouble center_x;
gdouble center_y;
gdouble x;
gdouble y;
gdouble width;
gdouble height;
gdouble base_zoom;
gdouble base_angle;
gdouble initial_distance;
gdouble initial_angle;
GdkPoint points[4];
} ShapeInfo;
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
shape_info_allocate_input_rect (ShapeInfo *info)
{
gint width, height, i;
width = info->width;
height = info->height;
/* Top/left */
info->points[0].x = info->x - info->center_x;
info->points[0].y = info->y - info->center_y;
/* Top/right */
info->points[1].x = info->x - info->center_x + width;
info->points[1].y = info->y - info->center_y;
/* Bottom/right */
info->points[2].x = info->x - info->center_x + width;
info->points[2].y = info->y - info->center_y + height;
/* Bottom/left */
info->points[3].x = info->x - info->center_x;
info->points[3].y = info->y - info->center_y + height;
for (i = 0; i < 4; i++)
{
gdouble ret_x, ret_y;
calculate_rotated_point (info->angle,
info->zoom,
info->x,
info->y,
(gdouble) info->points[i].x,
(gdouble) info->points[i].y,
&ret_x,
&ret_y);
info->points[i].x = (gint) ret_x;
info->points[i].y = (gint) ret_y;
}
}
static void
shape_info_bounding_rect (ShapeInfo *info,
GdkRectangle *rect)
{
gint i, left, right, top, bottom;
left = top = G_MAXINT;
right = bottom = 0;
for (i = 0; i < 4; i++)
{
if (info->points[i].x < left)
left = info->points[i].x;
if (info->points[i].x > right)
right = info->points[i].x;
if (info->points[i].y < top)
top = info->points[i].y;
if (info->points[i].y > bottom)
bottom = info->points[i].y;
}
rect->x = left - 20;
rect->y = top - 20;
rect->width = right - left + 40;
rect->height = bottom - top + 40;
}
static gboolean
shape_info_point_in (ShapeInfo *info,
gint x,
gint y)
{
GdkPoint *left, *right, *top, *bottom;
gint i;
left = right = top = bottom = NULL;
for (i = 0; i < 4; i++)
{
GdkPoint *p = &info->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 = &info->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 ShapeInfo *
shape_info_new (gdouble x,
gdouble y,
gdouble width,
gdouble height,
GdkRGBA *color)
{
ShapeInfo *info;
info = g_slice_new0 (ShapeInfo);
info->cluster = NULL;
info->color = *color;
info->x = x;
info->y = y;
info->width = width;
info->height = height;
info->angle = 0;
info->zoom = 1;
info->base_zoom = 1;
info->base_angle = 0;
info->initial_distance = 0;
info->initial_angle = 0;
shape_info_allocate_input_rect (info);
shapes = g_list_prepend (shapes, info);
return info;
}
static void
shape_info_free (ShapeInfo *info)
{
g_slice_free (ShapeInfo, info);
}
static void
shape_info_draw (cairo_t *cr,
ShapeInfo *info)
{
cairo_save (cr);
cairo_translate (cr, info->points[0].x, info->points[0].y);
cairo_scale (cr, info->zoom, info->zoom);
cairo_rotate (cr, info->angle);
cairo_rectangle (cr, 0, 0, info->width, info->height);
gdk_cairo_set_source_rgba (cr, &info->color);
cairo_fill_preserve (cr);
cairo_set_line_width (cr, 6);
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_stroke (cr);
cairo_restore (cr);
}
static gboolean
draw_cb (GtkWidget *widget,
cairo_t *cr,
gpointer user_data)
{
GList *l;
for (l = shapes; l; l = l->next)
shape_info_draw (cr, l->data);
return FALSE;
}
static gboolean
button_press_cb (GtkWidget *widget,
GdkEvent *event,
gpointer user_data)
{
ShapeInfo *shape = NULL;
guint touch_id;
if (gdk_event_get_touch_id (event, &touch_id))
{
GList *l;
for (l = shapes; l; l = l->next)
{
ShapeInfo *info = l->data;
if (shape_info_point_in (info,
(gint) event->button.x,
(gint) event->button.y))
shape = info;
}
if (!shape)
return FALSE;
if (!shape->cluster)
shape->cluster = gdk_window_create_touch_cluster (gtk_widget_get_window (widget));
/* Only change cluster device if there were no touches or no device */
if (!gdk_touch_cluster_get_device (shape->cluster) ||
!gdk_touch_cluster_get_touches (shape->cluster))
gdk_touch_cluster_set_device (shape->cluster,
gdk_event_get_source_device (event));
gdk_touch_cluster_add_touch (shape->cluster, touch_id);
return TRUE;
}
return FALSE;
}
static gboolean
multitouch_cb (GtkWidget *widget,
GdkEvent *event,
gpointer user_data)
{
ShapeInfo *info = NULL;
gboolean new_center = FALSE;
gboolean new_position = FALSE;
gdouble event_x, event_y;
cairo_region_t *region;
GdkRectangle rect;
GList *l;
for (l = shapes; l; l = l->next)
{
ShapeInfo *shape = l->data;
if (event->multitouch.group == shape->cluster)
{
info = shape;
break;
}
}
if (!info)
return FALSE;
shape_info_bounding_rect (info, &rect);
region = cairo_region_create_rectangle ((cairo_rectangle_int_t *) &rect);
if (event->multitouch.n_events == 1)
{
/* Update center if we just got to
* this situation from either way */
if (event->type == GDK_MULTITOUCH_ADDED ||
event->type == GDK_MULTITOUCH_REMOVED)
new_center = TRUE;
event_x = event->multitouch.events[0]->x;
event_y = event->multitouch.events[0]->y;
new_position = TRUE;
}
else if (event->multitouch.n_events == 2)
{
gdouble distance, angle;
gdk_events_get_center ((GdkEvent *) event->multitouch.events[0],
(GdkEvent *) event->multitouch.events[1],
&event_x, &event_y);
gdk_events_get_distance ((GdkEvent *) event->multitouch.events[0],
(GdkEvent *) event->multitouch.events[1],
&distance);
gdk_events_get_angle ((GdkEvent *) event->multitouch.events[0],
(GdkEvent *) event->multitouch.events[1],
&angle);
if (event->type == GDK_MULTITOUCH_ADDED)
{
/* Second touch was just added, update base zoom/angle */
info->base_zoom = info->zoom;
info->base_angle = info->angle;
info->initial_angle = angle;
info->initial_distance = distance;
new_center = TRUE;
}
info->zoom = MAX (info->base_zoom * (distance / info->initial_distance), 1.0);
info->angle = info->base_angle + (angle - info->initial_angle);
new_position = TRUE;
}
if (new_center)
{
gdouble origin_x, origin_y;
origin_x = info->x - info->center_x;
origin_y = info->y - info->center_y;
calculate_rotated_point (- info->angle,
1 / info->zoom,
info->x - origin_x,
info->y - origin_y,
event_x - origin_x,
event_y - origin_y,
&info->center_x,
&info->center_y);
}
if (new_position)
{
info->x = event_x;
info->y = event_y;
}
shape_info_allocate_input_rect (info);
shape_info_bounding_rect (info, &rect);
cairo_region_union_rectangle (region, (cairo_rectangle_int_t *) &rect);
gdk_window_invalidate_region (gtk_widget_get_window (widget), region, FALSE);
return TRUE;
}
GtkWidget *
do_multitouch (GtkWidget *do_widget)
{
if (!window)
{
GdkRGBA color;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "Multitouch demo");
gtk_window_set_default_size (GTK_WINDOW (window), 600, 600);
gtk_window_set_screen (GTK_WINDOW (window),
gtk_widget_get_screen (do_widget));
gtk_widget_add_events (window,
GDK_TOUCH_MASK |
GDK_POINTER_MOTION_MASK |
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK);
gtk_widget_set_app_paintable (window, TRUE);
g_signal_connect (window, "draw",
G_CALLBACK (draw_cb), NULL);
g_signal_connect (window, "button-press-event",
G_CALLBACK (button_press_cb), NULL);
g_signal_connect (window, "multitouch-event",
G_CALLBACK (multitouch_cb), NULL);
gdk_rgba_parse (&color, "red");
color.alpha = 0.5;
shape_info_new (100, 50, 100, 140, &color);
gdk_rgba_parse (&color, "green");
color.alpha = 0.5;
shape_info_new (200, 100, 120, 90, &color);
gdk_rgba_parse (&color, "blue");
color.alpha = 0.5;
shape_info_new (150, 190, 140, 90, &color);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
{
gtk_widget_destroy (window);
window = NULL;
g_list_foreach (shapes, (GFunc) shape_info_free, NULL);
g_list_free (shapes);
shapes = NULL;
}
return window;
}

View File

@@ -33,6 +33,7 @@
<xi:include href="xml/windows.xml" />
<xi:include href="xml/events.xml" />
<xi:include href="xml/event_structs.xml" />
<xi:include href="xml/touchcluster.xml" />
<xi:include href="xml/keys.xml" />
<xi:include href="xml/selections.xml" />
<xi:include href="xml/dnd.xml" />

View File

@@ -770,6 +770,7 @@ gdk_event_request_motions
gdk_events_get_angle
gdk_events_get_center
gdk_events_get_distance
gdk_event_get_touch_id
<SUBSECTION>
gdk_event_handler_set
@@ -817,6 +818,7 @@ GdkEventWindowState
GdkEventSetting
GdkEventOwnerChange
GdkEventGrabBroken
GdkEventMultiTouch
<SUBSECTION>
GdkScrollDirection
@@ -844,6 +846,29 @@ gdk_event_get_type
gdk_owner_change_get_type
</SECTION>
<SECTION>
<TITLE>Multitouch</TITLE>
<FILE>touchcluster</FILE>
GdkTouchCluster
gdk_touch_cluster_add_touch
gdk_touch_cluster_remove_touch
gdk_touch_cluster_remove_all
gdk_touch_cluster_set_device
gdk_touch_cluster_get_device
gdk_touch_cluster_get_touches
<SUBSECTION>
gdk_window_create_touch_cluster
gdk_window_remove_touch_cluster
<SUBSECTION Standard>
GDK_TYPE_TOUCH_CLUSTER
<SUBSECTION Private>
gdk_touch_cluster_get_type
</SECTION>
<SECTION>
<TITLE>Cursors</TITLE>
<FILE>cursors</FILE>

View File

@@ -9,5 +9,6 @@ gdk_display_manager_get_type
gdk_drag_context_get_type
gdk_keymap_get_type
gdk_screen_get_type
gdk_touch_cluster_get_type
gdk_visual_get_type
gdk_window_get_type

View File

@@ -88,6 +88,7 @@ gdk_public_h_sources = \
gdkselection.h \
gdktestutils.h \
gdkthreads.h \
gdktouchcluster.h \
gdktypes.h \
gdkvisual.h \
gdkwindow.h
@@ -129,6 +130,7 @@ gdk_c_sources = \
gdkrgba.c \
gdkscreen.c \
gdkselection.c \
gdktouchcluster.c \
gdkvisual.c \
gdkwindow.c \
gdkwindowimpl.c

View File

@@ -53,6 +53,7 @@
#include <gdk/gdkselection.h>
#include <gdk/gdktestutils.h>
#include <gdk/gdkthreads.h>
#include <gdk/gdktouchcluster.h>
#include <gdk/gdktypes.h>
#include <gdk/gdkvisual.h>
#include <gdk/gdkwindow.h>

View File

@@ -163,6 +163,7 @@ gdk_event_get_screen
gdk_event_get_source_device
gdk_event_get_state
gdk_event_get_time
gdk_event_get_touch_id
gdk_event_get_type G_GNUC_CONST
gdk_event_handler_set
gdk_event_mask_get_type G_GNUC_CONST
@@ -318,6 +319,11 @@ gdk_threads_enter
gdk_threads_init
gdk_threads_leave
gdk_threads_set_lock_functions
gdk_touch_cluster_add_touch
gdk_touch_cluster_get_device
gdk_touch_cluster_get_type G_GNUC_CONST
gdk_touch_cluster_list_touches
gdk_touch_cluster_remove_touch
gdk_unicode_to_keyval G_GNUC_CONST
gdk_utf8_to_string_target
gdk_visibility_state_get_type G_GNUC_CONST

View File

@@ -61,6 +61,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 touch capable device.
*
* An enumeration describing the type of an input device in general terms.
*/
@@ -70,7 +71,8 @@ typedef enum
GDK_SOURCE_PEN,
GDK_SOURCE_ERASER,
GDK_SOURCE_CURSOR,
GDK_SOURCE_KEYBOARD
GDK_SOURCE_KEYBOARD,
GDK_SOURCE_TOUCH
} GdkInputSource;
/**

View File

@@ -445,6 +445,7 @@ gdk_event_new (GdkEventType type)
switch (type)
{
case GDK_MOTION_NOTIFY:
case GDK_TOUCH_MOTION:
new_event->motion.x = 0.;
new_event->motion.y = 0.;
new_event->motion.x_root = 0.;
@@ -454,6 +455,8 @@ gdk_event_new (GdkEventType type)
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
case GDK_TOUCH_PRESS:
case GDK_TOUCH_RELEASE:
new_event->button.x = 0.;
new_event->button.y = 0.;
new_event->button.x_root = 0.;
@@ -558,12 +561,15 @@ gdk_event_copy (const GdkEvent *event)
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
case GDK_TOUCH_PRESS:
case GDK_TOUCH_RELEASE:
if (event->button.axes)
new_event->button.axes = g_memdup (event->button.axes,
sizeof (gdouble) * gdk_device_get_n_axes (event->button.device));
break;
case GDK_MOTION_NOTIFY:
case GDK_TOUCH_MOTION:
if (event->motion.axes)
new_event->motion.axes = g_memdup (event->motion.axes,
sizeof (gdouble) * gdk_device_get_n_axes (event->motion.device));
@@ -583,6 +589,22 @@ gdk_event_copy (const GdkEvent *event)
g_object_unref (new_event->selection.requestor);
break;
case GDK_MULTITOUCH_ADDED:
case GDK_MULTITOUCH_REMOVED:
case GDK_MULTITOUCH_UPDATED:
{
GdkEventMotion **motion_events;
guint i;
motion_events = g_new0 (GdkEventMotion*, event->multitouch.n_events);
for (i = 0; i < event->multitouch.n_events; i++)
motion_events[i] = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event->multitouch.events[i]);
new_event->multitouch.events = motion_events;
}
break;
default:
break;
}
@@ -637,6 +659,8 @@ gdk_event_free (GdkEvent *event)
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
case GDK_TOUCH_PRESS:
case GDK_TOUCH_RELEASE:
g_free (event->button.axes);
break;
@@ -647,6 +671,7 @@ gdk_event_free (GdkEvent *event)
break;
case GDK_MOTION_NOTIFY:
case GDK_TOUCH_MOTION:
g_free (event->motion.axes);
break;
@@ -666,6 +691,20 @@ gdk_event_free (GdkEvent *event)
g_object_unref (event->selection.requestor);
break;
case GDK_MULTITOUCH_ADDED:
case GDK_MULTITOUCH_REMOVED:
case GDK_MULTITOUCH_UPDATED:
if (event->multitouch.events)
{
guint i;
for (i = 0; i < event->multitouch.n_events; i++)
gdk_event_free ((GdkEvent *) event->multitouch.events[i]);
g_free (event->multitouch.events);
}
break;
default:
break;
}
@@ -692,11 +731,14 @@ gdk_event_get_time (const GdkEvent *event)
switch (event->type)
{
case GDK_MOTION_NOTIFY:
case GDK_TOUCH_MOTION:
return event->motion.time;
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
case GDK_TOUCH_PRESS:
case GDK_TOUCH_RELEASE:
return event->button.time;
case GDK_SCROLL:
return event->scroll.time;
@@ -722,6 +764,10 @@ gdk_event_get_time (const GdkEvent *event)
case GDK_DROP_START:
case GDK_DROP_FINISHED:
return event->dnd.time;
case GDK_MULTITOUCH_ADDED:
case GDK_MULTITOUCH_REMOVED:
case GDK_MULTITOUCH_UPDATED:
return event->multitouch.time;
case GDK_CLIENT_EVENT:
case GDK_VISIBILITY_NOTIFY:
case GDK_CONFIGURE:
@@ -767,12 +813,15 @@ gdk_event_get_state (const GdkEvent *event,
switch (event->type)
{
case GDK_MOTION_NOTIFY:
case GDK_TOUCH_MOTION:
*state = event->motion.state;
return TRUE;
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
case GDK_TOUCH_PRESS:
case GDK_TOUCH_RELEASE:
*state = event->button.state;
return TRUE;
case GDK_SCROLL:
@@ -786,6 +835,11 @@ gdk_event_get_state (const GdkEvent *event,
case GDK_LEAVE_NOTIFY:
*state = event->crossing.state;
return TRUE;
case GDK_MULTITOUCH_ADDED:
case GDK_MULTITOUCH_REMOVED:
case GDK_MULTITOUCH_UPDATED:
*state = event->multitouch.state;
return TRUE;
case GDK_PROPERTY_NOTIFY:
case GDK_VISIBILITY_NOTIFY:
case GDK_CLIENT_EVENT:
@@ -861,10 +915,13 @@ gdk_event_get_coords (const GdkEvent *event,
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
case GDK_TOUCH_PRESS:
case GDK_TOUCH_RELEASE:
x = event->button.x;
y = event->button.y;
break;
case GDK_MOTION_NOTIFY:
case GDK_TOUCH_MOTION:
x = event->motion.x;
y = event->motion.y;
break;
@@ -904,6 +961,7 @@ gdk_event_get_root_coords (const GdkEvent *event,
switch (event->type)
{
case GDK_MOTION_NOTIFY:
case GDK_TOUCH_MOTION:
x = event->motion.x_root;
y = event->motion.y_root;
break;
@@ -915,6 +973,8 @@ gdk_event_get_root_coords (const GdkEvent *event,
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
case GDK_TOUCH_PRESS:
case GDK_TOUCH_RELEASE:
x = event->button.x_root;
y = event->button.y_root;
break;
@@ -972,7 +1032,8 @@ gdk_event_get_axis (const GdkEvent *event,
switch (event->type)
{
case GDK_MOTION_NOTIFY:
case GDK_MOTION_NOTIFY:
case GDK_TOUCH_MOTION:
x = event->motion.x;
y = event->motion.y;
break;
@@ -982,6 +1043,8 @@ gdk_event_get_axis (const GdkEvent *event,
break;
case GDK_BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
case GDK_TOUCH_PRESS:
case GDK_TOUCH_RELEASE:
x = event->button.x;
y = event->button.y;
break;
@@ -1003,12 +1066,15 @@ gdk_event_get_axis (const GdkEvent *event,
return TRUE;
}
else if (event->type == GDK_BUTTON_PRESS ||
event->type == GDK_BUTTON_RELEASE)
event->type == GDK_BUTTON_RELEASE ||
event->type == GDK_TOUCH_PRESS ||
event->type == GDK_TOUCH_RELEASE)
{
device = event->button.device;
axes = event->button.axes;
}
else if (event->type == GDK_MOTION_NOTIFY)
else if (event->type == GDK_MOTION_NOTIFY ||
event->type == GDK_TOUCH_MOTION)
{
device = event->motion.device;
axes = event->motion.axes;
@@ -1045,12 +1111,15 @@ gdk_event_set_device (GdkEvent *event,
switch (event->type)
{
case GDK_MOTION_NOTIFY:
case GDK_TOUCH_MOTION:
event->motion.device = device;
break;
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
case GDK_TOUCH_PRESS:
case GDK_TOUCH_RELEASE:
event->button.device = device;
break;
case GDK_SCROLL:
@@ -1060,6 +1129,10 @@ gdk_event_set_device (GdkEvent *event,
case GDK_PROXIMITY_OUT:
event->proximity.device = device;
break;
case GDK_MULTITOUCH_ADDED:
case GDK_MULTITOUCH_REMOVED:
case GDK_MULTITOUCH_UPDATED:
event->multitouch.device = device;
default:
break;
}
@@ -1092,17 +1165,24 @@ gdk_event_get_device (const GdkEvent *event)
switch (event->type)
{
case GDK_MOTION_NOTIFY:
case GDK_TOUCH_MOTION:
return event->motion.device;
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
case GDK_TOUCH_PRESS:
case GDK_TOUCH_RELEASE:
return event->button.device;
case GDK_SCROLL:
return event->scroll.device;
case GDK_PROXIMITY_IN:
case GDK_PROXIMITY_OUT:
return event->proximity.device;
case GDK_MULTITOUCH_ADDED:
case GDK_MULTITOUCH_REMOVED:
case GDK_MULTITOUCH_UPDATED:
return event->multitouch.device;
default:
break;
}
@@ -1111,10 +1191,13 @@ gdk_event_get_device (const GdkEvent *event)
switch (event->type)
{
case GDK_MOTION_NOTIFY:
case GDK_TOUCH_MOTION:
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
case GDK_TOUCH_PRESS:
case GDK_TOUCH_RELEASE:
case GDK_ENTER_NOTIFY:
case GDK_LEAVE_NOTIFY:
case GDK_FOCUS_CHANGE:
@@ -1130,6 +1213,9 @@ gdk_event_get_device (const GdkEvent *event)
case GDK_GRAB_BROKEN:
case GDK_KEY_PRESS:
case GDK_KEY_RELEASE:
case GDK_MULTITOUCH_ADDED:
case GDK_MULTITOUCH_REMOVED:
case GDK_MULTITOUCH_UPDATED:
{
GdkDisplay *display;
GdkDeviceManager *device_manager;
@@ -1446,6 +1532,45 @@ gdk_event_get_screen (const GdkEvent *event)
return NULL;
}
/**
* gdk_event_get_touch_id:
* @event: a #GdkEvent
* @touch_id: return location of the touch ID of a touch event
*
* If @event if of type %GDK_TOUCH_MOTION, %GDK_TOUCH_PRESS or
* %GDK_TOUCH_RELEASE, fills in @touch_id and returns %TRUE,
* else it returns %FALSE.
*
* Returns: %TRUE if the touch ID can be extracted from @event.
**/
gboolean
gdk_event_get_touch_id (const GdkEvent *event,
guint *touch_id)
{
if (!event)
return FALSE;
if (event->type == GDK_TOUCH_MOTION)
{
if (touch_id)
*touch_id = event->motion.touch_id;
return TRUE;
}
else if (event->type == GDK_TOUCH_PRESS ||
event->type == GDK_TOUCH_RELEASE)
{
if (touch_id)
*touch_id = event->button.touch_id;
return TRUE;
}
else
{
if (touch_id)
*touch_id = 0;
return FALSE;
}
}
/**
* gdk_set_show_events:
* @show_events: %TRUE to output event debugging information.

View File

@@ -35,6 +35,7 @@
#include <gdk/gdktypes.h>
#include <gdk/gdkdnd.h>
#include <gdk/gdkdevice.h>
#include <gdk/gdktouchcluster.h>
G_BEGIN_DECLS
@@ -93,6 +94,7 @@ typedef struct _GdkEventDND GdkEventDND;
typedef struct _GdkEventWindowState GdkEventWindowState;
typedef struct _GdkEventSetting GdkEventSetting;
typedef struct _GdkEventGrabBroken GdkEventGrabBroken;
typedef struct _GdkEventMultiTouch GdkEventMultiTouch;
typedef union _GdkEvent GdkEvent;
@@ -213,6 +215,12 @@ typedef GdkFilterReturn (*GdkFilterFunc) (GdkXEvent *xevent,
* was added in 2.8.
* @GDK_DAMAGE: the content of the window has been changed. This event type
* was added in 2.14.
* @GDK_TOUCH_MOTION: A touch device has been updated.
* @GDK_TOUCH_PRESS: A new touch stream has just started.
* @GDK_TOUCH_RELEASE: A touch stream has finished.
* @GDK_MULTITOUCH_ADDED: A touch ID was added to a #GdkTouchCluster
* @GDK_MULTITOUCH_UPDATED: A touch within a #GdkTouchCluster has been updated.
* @GDK_MULTITOUCH_REMOVED: A touch ID was removed from a #GdkTouchCluster.
* @GDK_EVENT_LAST: marks the end of the GdkEventType enumeration. Added in 2.18
*
* Specifies the type of the event.
@@ -260,6 +268,12 @@ typedef enum
GDK_OWNER_CHANGE = 34,
GDK_GRAB_BROKEN = 35,
GDK_DAMAGE = 36,
GDK_TOUCH_MOTION = 37,
GDK_TOUCH_PRESS = 38,
GDK_TOUCH_RELEASE = 39,
GDK_MULTITOUCH_ADDED = 40,
GDK_MULTITOUCH_UPDATED = 41,
GDK_MULTITOUCH_REMOVED = 42,
GDK_EVENT_LAST /* helper variable for decls */
} GdkEventType;
@@ -500,8 +514,9 @@ struct _GdkEventVisibility
* screen.
* @y_root: the y coordinate of the pointer relative to the root of the
* screen.
* @touch_id: touch ID, only meaningful if event is of type %GDK_TOUCH_MOTION.
*
* Generated when the pointer moves.
* Generated when the pointer/touch moves.
*/
struct _GdkEventMotion
{
@@ -516,6 +531,56 @@ struct _GdkEventMotion
gint16 is_hint;
GdkDevice *device;
gdouble x_root, y_root;
guint touch_id;
};
/**
* GdkEventMultiTouch:
* @type: the type of the event (%GDK_MULTITOUCH_ADDED, %GDK_MULTITOUCH_UPDATED
* or %GDK_MULTITOUCH_REMOVED).
* @window: the window which received the event.
* @send_event: %TRUE if the event was sent explicitly (e.g. using
* <function>XSendEvent</function>).
* @time: the time of the event in milliseconds.
* @state: (type GdkModifierType): a bit-mask representing the state of
* the modifier keys (e.g. Control, Shift and Alt) and the pointer
* buttons. See #GdkModifierType.
* @device: the device where the event originated.
* @group: the #GdkTouchCluster containing the touches that generated this event
* @events: an array of events of type %GDK_TOUCH_MOTION for the touches in @group
* @updated_touch_id: the touch ID that caused this event to be generated
* @n_events: the number of events in @events
* @n_updated_event: the index in @events of the event corresponding to
* @updated_touch_id, or -1 for %GDK_MULTITOUCH_REMOVED events.
*
* Used for multitouch events. The @type field will be one of
* %GDK_MULTITOUCH_ADDED, %GDK_MULTITOUCH_UPDATED or
* %GDK_MULTITOUCH_REMOVED.
*
* Multitouch events group the events from the touches in a
* #GdkTouchCluster, so one of these events is generated
* whenever a touch ID generates a new event, or a touch ID
* is added or removed.
*
* For any given touch ID, %GDK_MULTITOUCH_ADDED and
* %GDK_MULTITOUCH_REMOVED events are always paired,
* with any number of %GDK_MULTITOUCH_UPDATED
* events in between. The minimum event stream is an
* added/removed pair.
*/
struct _GdkEventMultiTouch
{
GdkEventType type;
GdkWindow *window;
gint8 send_event;
guint32 time;
guint state;
GdkDevice *device;
GdkTouchCluster *group;
GdkEventMotion **events;
guint updated_touch_id;
gint8 n_events;
gint8 n_updated_event;
};
/**
@@ -542,6 +607,8 @@ struct _GdkEventMotion
* screen.
* @y_root: the y coordinate of the pointer relative to the root of the
* screen.
* @touch_id: touch ID, only meaningful if event is of type %GDK_TOUCH_PRESS
* or %GDK_TOUCH_RELEASE.
*
* Used for button press and button release events. The
* @type field will be one of %GDK_BUTTON_PRESS,
@@ -591,6 +658,7 @@ struct _GdkEventButton
guint button;
GdkDevice *device;
gdouble x_root, y_root;
guint touch_id;
};
/**
@@ -1034,6 +1102,7 @@ union _GdkEvent
GdkEventWindowState window_state;
GdkEventSetting setting;
GdkEventGrabBroken grab_broken;
GdkEventMultiTouch multitouch;
};
GType gdk_event_get_type (void) G_GNUC_CONST;
@@ -1087,6 +1156,9 @@ void gdk_event_set_screen (GdkEvent *event,
GdkScreen *screen);
GdkScreen *gdk_event_get_screen (const GdkEvent *event);
gboolean gdk_event_get_touch_id (const GdkEvent *event,
guint *touch_id);
void gdk_set_show_events (gboolean show_events);
gboolean gdk_get_show_events (void);

View File

@@ -248,6 +248,12 @@ struct _GdkWindow
GHashTable *source_event_masks;
gulong device_added_handler_id;
gulong device_changed_handler_id;
/* Store of latest per-touch events, keys are
* GdkDevices, values are hashtables of touchID/info
*/
GHashTable *touch_event_tracker;
GList *touch_clusters;
};
#define GDK_WINDOW_TYPE(d) (((GDK_WINDOW (d)))->window_type)

350
gdk/gdktouchcluster.c Normal file
View File

@@ -0,0 +1,350 @@
/* GDK - The GIMP Drawing Kit
* Copyright (C) 2011 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 "gdktouchcluster.h"
#include "gdkintl.h"
/**
* SECTION:touchcluster
* @Short_description: Multitouch handling
* @Title: Multitouch
* @See_also: #GdkEventMultiTouch
*
* #GdkTouchCluster is an object that gathers touch IDs from a
* touch-enabled slave #GdkDevice, in order to send
* #GdkEventMultiTouch<!-- --> events whenever a contained touch ID
* is updated.
*
* #GdkTouchCluster<!-- -->s are always associated to a window,
* you need to create them through gdk_window_create_touch_cluster(),
* and free them through gdk_window_remove_touch_cluster().
*
* Touch IDs from devices can be obtained from %GDK_TOUCH_PRESS,
* %GDK_TOUCH_MOTION or %GDK_TOUCH_RELEASE events through
* gdk_event_get_touch_id(), and then be added via
* gdk_touch_cluster_add_touch(). Note that touch IDs are
* highly transitive, even be an incrementable number only
* identifying the current touch event stream, so touch IDs
* should always be gotten from these events.
*
* Anytime a touch ID is within a cluster, no %GDK_TOUCH_PRESS,
* %GDK_TOUCH_MOTION or %GDK_TOUCH_RELEASE events will happen
* for the individual touch. The event will be available instead
* as part of the #GdkMultitouchEvent that will be emitted. This
* will hold true until gdk_touch_cluster_remove_touch() is
* called for it. Note that GTK+ will automatically take a
* touch ID out of any cluster if %GDK_TOUCH_RELEASE is gotten
* internally.
*/
typedef struct GdkTouchClusterPrivate GdkTouchClusterPrivate;
struct GdkTouchClusterPrivate
{
GdkDevice *device;
GList *touches;
};
enum {
PROP_0,
PROP_DEVICE
};
enum {
TOUCH_ADDED,
TOUCH_REMOVED,
LAST_SIGNAL
};
static guint signals [LAST_SIGNAL] = { 0 };
static void gdk_touch_cluster_finalize (GObject *object);
static void gdk_touch_cluster_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void gdk_touch_cluster_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
G_DEFINE_TYPE (GdkTouchCluster, gdk_touch_cluster, G_TYPE_OBJECT)
static void
gdk_touch_cluster_class_init (GdkTouchClusterClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gdk_touch_cluster_finalize;
object_class->get_property = gdk_touch_cluster_get_property;
object_class->set_property = gdk_touch_cluster_set_property;
g_object_class_install_property (object_class,
PROP_DEVICE,
g_param_spec_object ("device",
P_("Device"),
P_("Device attached to the cluster"),
GDK_TYPE_DEVICE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
signals[TOUCH_ADDED] =
g_signal_new (g_intern_static_string ("touch-added"),
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GdkTouchClusterClass, touch_added),
NULL, NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1, G_TYPE_UINT);
signals[TOUCH_REMOVED] =
g_signal_new (g_intern_static_string ("touch-removed"),
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GdkTouchClusterClass, touch_removed),
NULL, NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1, G_TYPE_UINT);
g_type_class_add_private (object_class, sizeof (GdkTouchClusterPrivate));
}
static void
gdk_touch_cluster_init (GdkTouchCluster *cluster)
{
cluster->priv = G_TYPE_INSTANCE_GET_PRIVATE (cluster,
GDK_TYPE_TOUCH_CLUSTER,
GdkTouchClusterPrivate);
}
static void
gdk_touch_cluster_finalize (GObject *object)
{
GdkTouchClusterPrivate *priv;
priv = GDK_TOUCH_CLUSTER (object)->priv;
g_list_free (priv->touches);
G_OBJECT_CLASS (gdk_touch_cluster_parent_class)->finalize (object);
}
static void
gdk_touch_cluster_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GdkTouchClusterPrivate *priv;
priv = GDK_TOUCH_CLUSTER (object)->priv;
switch (prop_id)
{
case PROP_DEVICE:
gdk_touch_cluster_set_device (GDK_TOUCH_CLUSTER (object),
g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gdk_touch_cluster_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GdkTouchClusterPrivate *priv;
priv = GDK_TOUCH_CLUSTER (object)->priv;
switch (prop_id)
{
case PROP_DEVICE:
g_value_set_object (value, priv->device);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/**
* gdk_touch_cluster_add_touch:
* @cluster: a #GdkTouchCluster
* @touch_id: a touch ID from a touch event
*
* Adds a touch ID to @cluster, so it will generate a
* %GDK_MULTITOUCH_ADDED event, followed by %GDK_MULTITOUCH_UPDATED
* events whenever this touch ID is updated.
*
* If @touch_id already pertained to another #GdkTouchCluster, it
* will be removed from it, generating a %GDK_MULTITOUCH_REMOVED
* for that another cluster.
**/
void
gdk_touch_cluster_add_touch (GdkTouchCluster *cluster,
guint touch_id)
{
GdkTouchClusterPrivate *priv;
g_return_if_fail (GDK_IS_TOUCH_CLUSTER (cluster));
priv = cluster->priv;
if (!g_list_find (priv->touches, GUINT_TO_POINTER (touch_id)))
{
priv->touches = g_list_prepend (priv->touches, GUINT_TO_POINTER (touch_id));
g_signal_emit (cluster, signals [TOUCH_ADDED], 0, touch_id);
}
}
/**
* gdk_touch_cluster_remove_touch:
* @cluster: a #GdkTouchCluster
* @touch_id: a touch ID from a touch event
*
* Removes a touch ID from @cluster, generating a %GDK_MULTITOUCH_REMOVED
* event for @cluster, and causing any further input from @touch_id
* to be reported trough %GDK_TOUCH_MOTION events.
*
* <note><para>
* Note that GTK+ automatically removes a touch ID from any cluster
* if a %GDK_TOUCH_RELEASE event is gotten internally.
* </para></note>
**/
void
gdk_touch_cluster_remove_touch (GdkTouchCluster *cluster,
guint touch_id)
{
GdkTouchClusterPrivate *priv;
GList *link;
g_return_if_fail (GDK_IS_TOUCH_CLUSTER (cluster));
priv = cluster->priv;
link = g_list_find (priv->touches, GUINT_TO_POINTER (touch_id));
if (link)
{
priv->touches = g_list_remove_link (priv->touches, link);
g_signal_emit (cluster, signals [TOUCH_REMOVED], 0, touch_id);
g_list_free1 (link);
}
}
/**
* gdk_touch_cluster_remove_all:
* @cluster: a #GdkTouchCluster
*
* Removes all touch IDs from @cluster.
**/
void
gdk_touch_cluster_remove_all (GdkTouchCluster *cluster)
{
GdkTouchClusterPrivate *priv;
GList *link;
g_return_if_fail (GDK_IS_TOUCH_CLUSTER (cluster));
priv = cluster->priv;
link = priv->touches;
while (link)
{
priv->touches = g_list_remove_link (priv->touches, link);
g_signal_emit (cluster, signals [TOUCH_REMOVED], 0, link->data);
}
g_list_free (priv->touches);
priv->touches = NULL;
}
/**
* gdk_touch_cluster_get_touches:
* @cluster: a #GdkTouchCluster
*
* Returns a const list of touch IDs as #guint.
*
* Returns: (transfer none): A list of touch IDs.
**/
GList *
gdk_touch_cluster_get_touches (GdkTouchCluster *cluster)
{
GdkTouchClusterPrivate *priv;
g_return_val_if_fail (GDK_IS_TOUCH_CLUSTER (cluster), NULL);
priv = cluster->priv;
return priv->touches;
}
/**
* gdk_touch_cluster_set_device:
* @cluster: a #GdkTouchCluster
* @device: a #GdkDevice
*
* Sets the current device associated to @cluster, all contained
* touch IDs must pertain to this device. As a consequence,
* gdk_touch_cluster_remove_all() will be called on @cluster
* if the current device changes.
**/
void
gdk_touch_cluster_set_device (GdkTouchCluster *cluster,
GdkDevice *device)
{
GdkTouchClusterPrivate *priv;
g_return_if_fail (GDK_IS_TOUCH_CLUSTER (cluster));
g_return_if_fail (!device || GDK_IS_DEVICE (device));
priv = cluster->priv;
if (priv->device != device)
gdk_touch_cluster_remove_all (cluster);
priv->device = device;
}
/**
* gdk_touch_cluster_get_device:
* @cluster: a #GdkTouchCluster
*
* Returns the slave/floating device this touch cluster pertains to,
* only touch IDs from this device can be included in @cluster.
* the #GdkDevice will typically have the %GDK_SOURCE_TOUCH input source.
*
* Returns: (transfer none): The #GdkDevice generating the contained touch IDs
**/
GdkDevice *
gdk_touch_cluster_get_device (GdkTouchCluster *cluster)
{
GdkTouchClusterPrivate *priv;
g_return_val_if_fail (GDK_IS_TOUCH_CLUSTER (cluster), NULL);
priv = cluster->priv;
return priv->device;
}

69
gdk/gdktouchcluster.h Normal file
View File

@@ -0,0 +1,69 @@
/* GDK - The GIMP Drawing Kit
* Copyright (C) 2011 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.
*/
#ifndef __GDK_TOUCH_CLUSTER_H__
#define __GDK_TOUCH_CLUSTER_H__
#include <glib-object.h>
#include <gdk/gdkdevice.h>
G_BEGIN_DECLS
#define GDK_TYPE_TOUCH_CLUSTER (gdk_touch_cluster_get_type ())
#define GDK_TOUCH_CLUSTER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_TOUCH_CLUSTER, GdkTouchCluster))
#define GDK_IS_TOUCH_CLUSTER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_TOUCH_CLUSTER))
typedef struct _GdkTouchCluster GdkTouchCluster;
typedef struct _GdkTouchClusterClass GdkTouchClusterClass;
struct _GdkTouchCluster
{
GObject parent_instance;
gpointer priv;
};
struct _GdkTouchClusterClass
{
GObjectClass parent_class;
void (* touch_added) (GdkTouchCluster *cluster,
guint touch_id);
void (* touch_removed) (GdkTouchCluster *cluster,
guint touch_id);
gpointer padding[16];
};
GType gdk_touch_cluster_get_type (void) G_GNUC_CONST;
void gdk_touch_cluster_add_touch (GdkTouchCluster *cluster,
guint touch_id);
void gdk_touch_cluster_remove_touch (GdkTouchCluster *cluster,
guint touch_id);
void gdk_touch_cluster_remove_all (GdkTouchCluster *cluster);
GList * gdk_touch_cluster_get_touches (GdkTouchCluster *cluster);
void gdk_touch_cluster_set_device (GdkTouchCluster *cluster,
GdkDevice *device);
GdkDevice * gdk_touch_cluster_get_device (GdkTouchCluster *cluster);
G_END_DECLS
#endif /* __GDK_TOUCH_CLUSTER_H__ */

View File

@@ -288,6 +288,7 @@ typedef enum
* @GDK_SUBSTRUCTURE_MASK: receive events about window configuration changes of
* child windows
* @GDK_SCROLL_MASK: receive scroll events
* @GDK_TOUCH_MASK: receive (multi)touch events
* @GDK_ALL_EVENTS_MASK: the combination of all the above event masks.
*
* A set of bit-flags to indicate which events a window is to receive.
@@ -327,7 +328,8 @@ typedef enum
GDK_PROXIMITY_OUT_MASK = 1 << 19,
GDK_SUBSTRUCTURE_MASK = 1 << 20,
GDK_SCROLL_MASK = 1 << 21,
GDK_ALL_EVENTS_MASK = 0x3FFFFE
GDK_TOUCH_MASK = 1 << 22,
GDK_ALL_EVENTS_MASK = 0x3FFFFF
} GdkEventMask;
/**

View File

@@ -213,6 +213,11 @@ typedef struct {
int dx, dy; /* The amount that the source was moved to reach dest_region */
} GdkWindowRegionMove;
typedef struct {
GdkEvent *event; /* latest event for touch */
GdkTouchCluster *cluster; /* touch cluster the ID currently pertains to */
} TouchEventInfo;
/* Global info */
static void gdk_window_drop_cairo_surface (GdkWindow *private);
@@ -567,6 +572,12 @@ gdk_window_finalize (GObject *object)
if (window->devices_inside)
g_list_free (window->devices_inside);
if (window->touch_event_tracker)
g_hash_table_destroy (window->touch_event_tracker);
g_list_foreach (window->touch_clusters, (GFunc) g_object_unref, NULL);
g_list_free (window->touch_clusters);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -8066,6 +8077,12 @@ static const guint type_masks[] = {
0, /* GDK_OWNER_CHANGE = 34 */
0, /* GDK_GRAB_BROKEN = 35 */
0, /* GDK_DAMAGE = 36 */
GDK_TOUCH_MASK, /* GDK_TOUCH_MOTION = 37 */
GDK_TOUCH_MASK, /* GDK_TOUCH_PRESS = 38 */
GDK_TOUCH_MASK, /* GDK_TOUCH_RELEASE = 39 */
GDK_TOUCH_MASK, /* GDK_MULTITOUCH_ADDED = 40 */
GDK_TOUCH_MASK, /* GDK_MULTITOUCH_REMOVED = 41 */
GDK_TOUCH_MASK /* GDK_MULTITOUCH_UPDATED = 42 */
};
G_STATIC_ASSERT (G_N_ELEMENTS (type_masks) == GDK_EVENT_LAST);
@@ -8097,6 +8114,8 @@ is_button_type (GdkEventType type)
type == GDK_2BUTTON_PRESS ||
type == GDK_3BUTTON_PRESS ||
type == GDK_BUTTON_RELEASE ||
type == GDK_TOUCH_PRESS ||
type == GDK_TOUCH_RELEASE ||
type == GDK_SCROLL;
}
@@ -8104,6 +8123,7 @@ static gboolean
is_motion_type (GdkEventType type)
{
return type == GDK_MOTION_NOTIFY ||
type == GDK_TOUCH_MOTION ||
type == GDK_ENTER_NOTIFY ||
type == GDK_LEAVE_NOTIFY;
}
@@ -8166,6 +8186,7 @@ _gdk_make_event (GdkWindow *window,
switch (type)
{
case GDK_MOTION_NOTIFY:
case GDK_TOUCH_MOTION:
event->motion.time = the_time;
event->motion.axes = NULL;
event->motion.state = the_state;
@@ -8248,6 +8269,79 @@ _gdk_make_event (GdkWindow *window,
return event;
}
GdkEvent *
gdk_make_multitouch_event (GdkWindow *window,
GdkEventType type,
GdkTouchCluster *cluster,
GdkDevice *device,
guint touch_id,
GdkEvent *event_in_queue)
{
GdkEvent *mt_event, *event = NULL;
gint i, n_touches, n_updated = -1;
GdkEventMotion **subevents;
TouchEventInfo *info;
GHashTable *by_touch;
GList *touches;
if (!window->touch_event_tracker)
return NULL;
by_touch = g_hash_table_lookup (window->touch_event_tracker, device);
if (by_touch)
{
info = g_hash_table_lookup (by_touch, GUINT_TO_POINTER (touch_id));
if (info)
event = info->event;
}
if (!event)
{
g_warning ("Creating a multitouch event but no input was pre-recorded");
return NULL;
}
/* Generate multitouch event */
mt_event = _gdk_make_event (window, type, event_in_queue, FALSE);
mt_event->multitouch.time = event->motion.time;
mt_event->multitouch.state = event->motion.state;
gdk_event_set_device (mt_event, gdk_event_get_device (event));
gdk_event_set_source_device (mt_event, gdk_event_get_source_device (event));
mt_event->multitouch.group = cluster;
/* Fill in individual motion sub-events */
touches = gdk_touch_cluster_get_touches (cluster);
n_touches = g_list_length (touches);
i = 0;
subevents = g_new0 (GdkEventMotion *, n_touches);
while (touches)
{
TouchEventInfo *subevent_info;
GdkEvent *subevent;
subevent_info = g_hash_table_lookup (by_touch, touches->data);
subevent = gdk_event_copy (subevent_info->event);
subevents[i] = (GdkEventMotion *) subevent;
if (subevent->motion.touch_id == touch_id)
n_updated = i;
touches = touches->next;
i++;
}
mt_event->multitouch.events = subevents;
mt_event->multitouch.n_updated_event = n_updated;
mt_event->multitouch.n_events = n_touches;
mt_event->multitouch.updated_touch_id = touch_id;
return mt_event;
}
static void
send_crossing_event (GdkDisplay *display,
GdkWindow *toplevel,
@@ -9092,6 +9186,127 @@ get_event_window (GdkDisplay *display,
return NULL;
}
static TouchEventInfo *
touch_event_info_new (void)
{
return g_slice_new0 (TouchEventInfo);
}
static void
touch_event_info_free (TouchEventInfo *info)
{
if (info->event)
gdk_event_free (info->event);
g_slice_free (TouchEventInfo, info);
}
static TouchEventInfo *
touch_event_info_lookup (GdkWindow *window,
GdkDevice *device,
guint touch_id,
gboolean create)
{
TouchEventInfo *info;
GHashTable *by_touch;
if (G_UNLIKELY (!window->touch_event_tracker))
window->touch_event_tracker = g_hash_table_new_full (NULL, NULL, NULL,
(GDestroyNotify) g_hash_table_destroy);
by_touch = g_hash_table_lookup (window->touch_event_tracker, device);
if (!by_touch)
{
by_touch = g_hash_table_new_full (NULL, NULL, NULL,
(GDestroyNotify) touch_event_info_free);
g_hash_table_insert (window->touch_event_tracker, device, by_touch);
}
info = g_hash_table_lookup (by_touch, GUINT_TO_POINTER (touch_id));
if (create && !info)
{
info = touch_event_info_new ();
g_hash_table_insert (by_touch, GUINT_TO_POINTER (touch_id), info);
}
return info;
}
/* Stores touch event for posterior multitouch
* events generation, takes ownership of event
*/
static void
store_touch_event (GdkWindow *window,
GdkEvent *event,
guint touch_id)
{
GdkDevice *device, *source_device;
TouchEventInfo *info;
if (event->type != GDK_TOUCH_PRESS &&
event->type != GDK_TOUCH_RELEASE &&
event->type != GDK_TOUCH_MOTION)
return;
device = gdk_event_get_device (event);
source_device = gdk_event_get_source_device (event);
if (event->type == GDK_TOUCH_PRESS ||
event->type == GDK_TOUCH_RELEASE)
{
GdkEvent *new_event;
/* Create GDK_TOUCH_MOTION event from the available data */
new_event = gdk_event_new (GDK_TOUCH_MOTION);
if (event->button.window)
new_event->motion.window = g_object_ref (event->button.window);
new_event->motion.send_event = event->button.send_event;
new_event->motion.time = event->button.time;
new_event->motion.x = event->button.x;
new_event->motion.y = event->button.y;
new_event->motion.x_root = event->button.x_root;
new_event->motion.y_root = event->button.y_root;
new_event->motion.state = event->button.state;
new_event->motion.touch_id = event->button.touch_id;
new_event->motion.is_hint = FALSE;
gdk_event_set_device (new_event, device);
gdk_event_set_source_device (new_event, source_device);
new_event->motion.axes = g_memdup (event->button.axes,
sizeof (gdouble) * gdk_device_get_n_axes (device));
gdk_event_free (event);
event = new_event;
}
info = touch_event_info_lookup (window, source_device, touch_id, TRUE);
info->event = event;
}
static GdkTouchCluster *
_gdk_window_lookup_touch_cluster (GdkWindow *window,
GdkEvent *event)
{
TouchEventInfo *info;
GdkDevice *device;
guint touch_id;
if (!gdk_event_get_touch_id (event, &touch_id))
return NULL;
device = gdk_event_get_source_device (event);
info = touch_event_info_lookup (window, device, touch_id, FALSE);
if (!info)
return NULL;
return info->cluster;
}
static gboolean
proxy_pointer_event (GdkDisplay *display,
GdkEvent *source_event,
@@ -9229,7 +9444,8 @@ proxy_pointer_event (GdkDisplay *display,
serial, non_linear);
_gdk_display_set_window_under_pointer (display, device, pointer_window);
}
else if (source_event->type == GDK_MOTION_NOTIFY)
else if (source_event->type == GDK_MOTION_NOTIFY ||
source_event->type == GDK_TOUCH_MOTION)
{
GdkWindow *event_win;
guint evmask;
@@ -9248,6 +9464,15 @@ proxy_pointer_event (GdkDisplay *display,
gdk_window_get_device_events (event_win, device) == 0)
return TRUE;
/* Block motion events coming from touch devices, only if
* the event mask allows both motion and touch events, since
* the latter will come right after.
*/
if ((evmask & GDK_TOUCH_MASK) &&
gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH &&
source_event->type == GDK_MOTION_NOTIFY)
return TRUE;
is_hint = FALSE;
if (event_win &&
@@ -9268,21 +9493,60 @@ proxy_pointer_event (GdkDisplay *display,
}
}
if (event_win && !display->ignore_core_events)
{
event = _gdk_make_event (event_win, GDK_MOTION_NOTIFY, source_event, FALSE);
event->motion.time = time_;
convert_toplevel_coords_to_window (event_win,
toplevel_x, toplevel_y,
&event->motion.x, &event->motion.y);
event->motion.x_root = source_event->motion.x_root;
event->motion.y_root = source_event->motion.y_root;
event->motion.state = state;
event->motion.is_hint = is_hint;
event->motion.device = source_event->motion.device;
if (!event_win)
return TRUE;
if (!display->ignore_core_events)
{
GdkTouchCluster *cluster = NULL;
GdkEventType event_type;
guint touch_id;
if (gdk_event_get_touch_id (source_event, &touch_id))
cluster = _gdk_window_lookup_touch_cluster (event_win, source_event);
if (cluster)
event_type = GDK_TOUCH_MOTION;
else
event_type = source_event->type;
event = gdk_event_new (event_type);
event->any.window = g_object_ref (event_win);
event->any.send_event = source_event->any.send_event;
event->motion.time = time_;
convert_toplevel_coords_to_window (event_win,
toplevel_x, toplevel_y,
&event->motion.x, &event->motion.y);
event->motion.x_root = source_event->motion.x_root;
event->motion.y_root = source_event->motion.y_root;
event->motion.state = state;
event->motion.is_hint = is_hint;
event->motion.device = source_event->motion.device;
event->motion.axes = g_memdup (source_event->motion.axes,
sizeof (gdouble) * gdk_device_get_n_axes (source_event->motion.device));
event->motion.touch_id = touch_id;
gdk_event_set_source_device (event, source_device);
if (cluster)
{
store_touch_event (event_win, event, touch_id);
/* Event is not added to the queue, instead it's stored
* in order to generate a multitouch event for the touch
* ID's cluster.
*/
gdk_make_multitouch_event (event_win, GDK_MULTITOUCH_UPDATED,
cluster, source_device, touch_id,
source_event);
}
else
{
store_touch_event (event_win, gdk_event_copy (event), touch_id);
/* Just insert the event */
_gdk_event_queue_insert_after (gdk_window_get_display (event_win),
source_event, event);
}
}
}
@@ -9299,7 +9563,8 @@ proxy_pointer_event (GdkDisplay *display,
static gboolean
proxy_button_event (GdkEvent *source_event,
gulong serial)
gulong serial,
gboolean *handle_ungrab)
{
GdkWindow *toplevel_window, *event_window;
GdkWindow *event_win;
@@ -9313,6 +9578,7 @@ proxy_button_event (GdkEvent *source_event,
GdkDisplay *display;
GdkWindow *w;
GdkDevice *device, *source_device;
GdkEventMask evmask;
type = source_event->any.type;
event_window = source_event->any.window;
@@ -9325,6 +9591,7 @@ proxy_button_event (GdkEvent *source_event,
toplevel_window = convert_native_coords_to_toplevel (event_window,
toplevel_x, toplevel_y,
&toplevel_x, &toplevel_y);
*handle_ungrab = TRUE;
if (type == GDK_BUTTON_PRESS &&
!source_event->any.send_event &&
@@ -9368,7 +9635,20 @@ proxy_button_event (GdkEvent *source_event,
device,
pointer_window,
type, state,
NULL, serial);
&evmask, serial);
/* Block button press/release events coming from touch devices, only if
* the event mask allows both normal and touch events, since
* the latter will come right after.
*/
if ((evmask & GDK_TOUCH_MASK) &&
gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH &&
source_event->type != GDK_TOUCH_PRESS &&
source_event->type != GDK_TOUCH_RELEASE)
{
*handle_ungrab = FALSE;
return TRUE;
}
if (event_win == NULL || display->ignore_core_events)
return TRUE;
@@ -9383,6 +9663,8 @@ proxy_button_event (GdkEvent *source_event,
{
case GDK_BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
case GDK_TOUCH_PRESS:
case GDK_TOUCH_RELEASE:
event->button.button = source_event->button.button;
convert_toplevel_coords_to_window (event_win,
toplevel_x, toplevel_y,
@@ -9393,12 +9675,13 @@ proxy_button_event (GdkEvent *source_event,
event->button.device = source_event->button.device;
event->button.axes = g_memdup (source_event->button.axes,
sizeof (gdouble) * gdk_device_get_n_axes (source_event->button.device));
event->button.touch_id = source_event->button.touch_id;
gdk_event_set_source_device (event, source_device);
if (type == GDK_BUTTON_PRESS)
_gdk_event_button_generate (display, event);
return TRUE;
break;
case GDK_SCROLL:
event->scroll.direction = source_event->scroll.direction;
@@ -9410,12 +9693,39 @@ proxy_button_event (GdkEvent *source_event,
event->scroll.state = state;
event->scroll.device = source_event->scroll.device;
gdk_event_set_source_device (event, source_device);
return TRUE;
break;
default:
return FALSE;
}
if (type == GDK_TOUCH_RELEASE)
{
GdkTouchCluster *cluster;
GHashTable *by_touch;
guint touch_id;
touch_id = source_event->button.touch_id;
/* Remove the touch ID from any touch cluster it could pertain to */
cluster = _gdk_window_lookup_touch_cluster (event_win, source_event);
if (cluster)
gdk_touch_cluster_remove_touch (cluster, touch_id);
/* Remove in any case the touch ID from the event tracker */
by_touch = g_hash_table_lookup (event_win->touch_event_tracker, source_device);
if (by_touch)
g_hash_table_remove (by_touch, GUINT_TO_POINTER (touch_id));
/* Only remove the grab if it was the last pending touch on the window */
*handle_ungrab = (g_hash_table_size (by_touch) == 0);
}
else if (type == GDK_TOUCH_PRESS)
store_touch_event (event_win, gdk_event_copy (event),
event->button.touch_id);
return TRUE; /* Always unlink original, we want to obey the emulated event mask */
}
@@ -9504,7 +9814,7 @@ _gdk_windowing_got_event (GdkDisplay *display,
GdkDeviceGrabInfo *button_release_grab;
GdkPointerWindowInfo *pointer_info;
GdkDevice *device, *source_device;
gboolean is_toplevel;
gboolean is_toplevel, handle_ungrab = TRUE;
if (gdk_event_get_time (event) != GDK_CURRENT_TIME)
display->last_event_time = gdk_event_get_time (event);
@@ -9630,7 +9940,9 @@ _gdk_windowing_got_event (GdkDisplay *display,
pointer_info->toplevel_y = y;
gdk_event_get_state (event, &pointer_info->state);
if (event->type == GDK_BUTTON_PRESS ||
event->type == GDK_BUTTON_RELEASE)
event->type == GDK_BUTTON_RELEASE ||
event->type == GDK_TOUCH_PRESS ||
event->type == GDK_TOUCH_RELEASE)
pointer_info->button = event->button.button;
if (device &&
@@ -9645,9 +9957,12 @@ _gdk_windowing_got_event (GdkDisplay *display,
serial);
else if (is_button_type (event->type))
unlink_event = proxy_button_event (event,
serial);
serial,
&handle_ungrab);
if (event->type == GDK_BUTTON_RELEASE &&
if (handle_ungrab &&
(event->type == GDK_BUTTON_RELEASE ||
event->type == GDK_TOUCH_RELEASE) &&
!event->any.send_event)
{
button_release_grab =
@@ -10886,3 +11201,124 @@ gdk_property_delete (GdkWindow *window,
{
GDK_WINDOW_IMPL_GET_CLASS (window->impl)->delete_property (window, property);
}
static void
touch_cluster_touch_added (GdkTouchCluster *cluster,
guint touch_id,
gpointer user_data)
{
GdkWindow *window;
TouchEventInfo *info;
GHashTable *by_touch;
GdkDevice *device;
device = gdk_touch_cluster_get_device (cluster);
if (!device)
return;
window = user_data;
by_touch = g_hash_table_lookup (window->touch_event_tracker, device);
g_assert (by_touch != NULL);
info = g_hash_table_lookup (by_touch, GUINT_TO_POINTER (touch_id));
if (info->cluster == cluster)
return;
if (info->cluster)
{
/* Remove touch from old cluster, but keep the stored data around */
g_hash_table_steal (by_touch, GUINT_TO_POINTER (touch_id));
gdk_touch_cluster_remove_touch (info->cluster, touch_id);
g_hash_table_insert (by_touch,
GUINT_TO_POINTER (touch_id),
info);
}
info->cluster = cluster;
gdk_make_multitouch_event (window, GDK_MULTITOUCH_ADDED,
cluster, device, touch_id,
NULL);
}
static void
touch_cluster_touch_removed (GdkTouchCluster *cluster,
guint touch_id,
gpointer user_data)
{
GdkWindow *window;
GdkDevice *device;
GHashTable *by_touch;
window = user_data;
device = gdk_touch_cluster_get_device (cluster);
by_touch = g_hash_table_lookup (window->touch_event_tracker, device);
g_assert (by_touch != NULL);
gdk_make_multitouch_event (window, GDK_MULTITOUCH_REMOVED,
cluster, device, touch_id,
NULL);
g_hash_table_remove (by_touch, GUINT_TO_POINTER (touch_id));
}
/**
* gdk_window_create_touch_cluster:
* @window: a #GdkWindow
*
* Creates a #GdkTouchCluster associated to @window.
*
* Returns: (transfer none): a newly created @GdkTouchCluster. This
* object is owned by @window and must be freed through
* gdk_window_remove_touch_cluster().
**/
GdkTouchCluster *
gdk_window_create_touch_cluster (GdkWindow *window)
{
GdkTouchCluster *cluster;
g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
cluster = g_object_new (GDK_TYPE_TOUCH_CLUSTER, NULL);
g_signal_connect (cluster, "touch-added",
G_CALLBACK (touch_cluster_touch_added), window);
g_signal_connect (cluster, "touch-removed",
G_CALLBACK (touch_cluster_touch_removed), window);
window->touch_clusters = g_list_prepend (window->touch_clusters, cluster);
return cluster;
}
/**
* gdk_window_remove_touch_cluster:
* @window: a #GdkWindow
* @cluster: a #GdkTouchCluster from @window
*
* Removes @cluster from @window. All contained touches will be
* removed one by one, causing %GDK_MULTITOUCH_REMOVED events
* for these before destroying @cluster.
**/
void
gdk_window_remove_touch_cluster (GdkWindow *window,
GdkTouchCluster *cluster)
{
g_return_if_fail (GDK_IS_WINDOW (window));
g_return_if_fail (GDK_IS_TOUCH_CLUSTER (cluster));
if (!window->touch_clusters ||
!g_list_find (window->touch_clusters, cluster))
return;
gdk_touch_cluster_remove_all (cluster);
g_signal_handlers_disconnect_by_func (cluster, "touch-added", window);
g_signal_handlers_disconnect_by_func (cluster, "touch-removed", window);
window->touch_clusters = g_list_remove (window->touch_clusters, cluster);
g_object_unref (cluster);
}

View File

@@ -33,6 +33,7 @@
#include <gdk/gdktypes.h>
#include <gdk/gdkevents.h>
#include <gdk/gdktouchcluster.h>
G_BEGIN_DECLS
@@ -862,6 +863,11 @@ void gdk_window_set_support_multidevice (GdkWindow *window,
gboolean support_multidevice);
gboolean gdk_window_get_support_multidevice (GdkWindow *window);
/* Multitouch support */
GdkTouchCluster * gdk_window_create_touch_cluster (GdkWindow *window);
void gdk_window_remove_touch_cluster (GdkWindow *window,
GdkTouchCluster *cluster);
G_END_DECLS
#endif /* __GDK_WINDOW_H__ */

View File

@@ -687,6 +687,17 @@ _gdk_x11_device_xi2_translate_event_mask (GdkEventMask event_mask,
XISetMask (mask, XI_FocusOut);
}
#ifdef XINPUT_2_1
if (event_mask & GDK_TOUCH_MASK)
{
XISetMask (mask, XI_TouchBegin);
XISetMask (mask, XI_TouchMotion);
XISetMask (mask, XI_TouchMotionUnowned);
XISetMask (mask, XI_TouchEnd);
XISetMask (mask, XI_TouchOwnership);
}
#endif /* XINPUT_2_1 */
return mask;
}

View File

@@ -47,7 +47,11 @@ _gdk_x11_device_manager_new (GdkDisplay *display)
int major, minor;
major = 2;
#ifdef XINPUT_2_1
minor = 1;
#else
minor = 0;
#endif /* XINPUT_2_1 */
if (!_gdk_disable_multidevice &&
XIQueryVersion (xdisplay, &major, &minor) != BadRequest)

View File

@@ -147,8 +147,10 @@ _gdk_x11_device_manager_xi2_select_events (GdkDeviceManager *device_manager,
static void
translate_valuator_class (GdkDisplay *display,
GdkDevice *device,
XIValuatorClassInfo *info,
gint n_valuator)
Atom valuator_label,
gdouble min,
gdouble max,
gdouble resolution)
{
static gboolean initialized = FALSE;
static Atom label_atoms [GDK_AXIS_LAST] = { 0 };
@@ -169,24 +171,19 @@ translate_valuator_class (GdkDisplay *display,
for (i = GDK_AXIS_IGNORE; i <= GDK_AXIS_LAST; i++)
{
if (label_atoms[i] == info->label)
if (label_atoms[i] == valuator_label)
{
use = i;
break;
}
}
if (info->label != None)
label = gdk_x11_xatom_to_atom_for_display (display, info->label);
if (valuator_label != None)
label = gdk_x11_xatom_to_atom_for_display (display, valuator_label);
else
label = GDK_NONE;
_gdk_device_add_axis (device,
label,
use,
info->min,
info->max,
info->resolution);
_gdk_device_add_axis (device, label, use, min, max, resolution);
}
static void
@@ -195,7 +192,7 @@ translate_device_classes (GdkDisplay *display,
XIAnyClassInfo **classes,
guint n_classes)
{
gint i, n_valuator = 0;
gint i;
g_object_freeze_notify (G_OBJECT (device));
@@ -217,11 +214,28 @@ translate_device_classes (GdkDisplay *display,
}
break;
case XIValuatorClass:
translate_valuator_class (display, device,
(XIValuatorClassInfo *) class_info,
n_valuator);
n_valuator++;
{
XIValuatorClassInfo *valuator_info = (XIValuatorClassInfo *) class_info;
translate_valuator_class (display, device,
valuator_info->label,
valuator_info->min,
valuator_info->max,
valuator_info->resolution);
}
break;
#ifdef XINPUT_2_1
case XITouchValuatorClass:
{
XITouchValuatorClassInfo *valuator_info = (XITouchValuatorClassInfo *) class_info;
translate_valuator_class (display, device,
valuator_info->label,
valuator_info->min,
valuator_info->max,
valuator_info->resolution);
}
break;
#endif /* XINPUT_2_1 */
default:
/* Ignore */
break;
@@ -256,6 +270,8 @@ 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, "multitouch"))
input_source = GDK_SOURCE_TOUCH;
else
input_source = GDK_SOURCE_MOUSE;
@@ -883,6 +899,12 @@ get_event_window (GdkEventTranslator *translator,
case XI_ButtonPress:
case XI_ButtonRelease:
case XI_Motion:
#ifdef XINPUT_2_1
case XI_TouchMotion:
case XI_TouchMotionUnowned:
case XI_TouchBegin:
case XI_TouchEnd:
#endif /* XINPUT_2_1 */
{
XIDeviceEvent *xev = (XIDeviceEvent *) ev;
@@ -1063,56 +1085,54 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
break;
case XI_ButtonPress:
case XI_ButtonRelease:
#ifdef XINPUT_2_1
case XI_TouchBegin:
case XI_TouchEnd:
#endif /* XINPUT_2_1 */
{
XIDeviceEvent *xev = (XIDeviceEvent *) ev;
GdkDevice *source_device;
switch (xev->detail)
if (ev->evtype == XI_ButtonPress &&
(xev->detail >= 4 && xev->detail <= 7))
{
case 4:
case 5:
case 6:
case 7:
/* Button presses of button 4-7 are scroll events */
if (ev->evtype == XI_ButtonPress)
{
event->scroll.type = GDK_SCROLL;
/* Button presses of button 4-7 are scroll events */
event->scroll.type = GDK_SCROLL;
if (xev->detail == 4)
event->scroll.direction = GDK_SCROLL_UP;
else if (xev->detail == 5)
event->scroll.direction = GDK_SCROLL_DOWN;
else if (xev->detail == 6)
event->scroll.direction = GDK_SCROLL_LEFT;
else
event->scroll.direction = GDK_SCROLL_RIGHT;
if (xev->detail == 4)
event->scroll.direction = GDK_SCROLL_UP;
else if (xev->detail == 5)
event->scroll.direction = GDK_SCROLL_DOWN;
else if (xev->detail == 6)
event->scroll.direction = GDK_SCROLL_LEFT;
else
event->scroll.direction = GDK_SCROLL_RIGHT;
event->scroll.window = window;
event->scroll.time = xev->time;
event->scroll.x = (gdouble) xev->event_x;
event->scroll.y = (gdouble) xev->event_y;
event->scroll.x_root = (gdouble) xev->root_x;
event->scroll.y_root = (gdouble) xev->root_y;
event->scroll.window = window;
event->scroll.time = xev->time;
event->scroll.x = (gdouble) xev->event_x;
event->scroll.y = (gdouble) xev->event_y;
event->scroll.x_root = (gdouble) xev->root_x;
event->scroll.y_root = (gdouble) xev->root_y;
event->scroll.device = g_hash_table_lookup (device_manager->id_table,
GUINT_TO_POINTER (xev->deviceid));
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);
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_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
break;
}
/* Button presses of button 4-7 are scroll events, so ignore the release */
else if (ev->evtype == XI_ButtonRelease)
{
return_val = FALSE;
break;
}
/* else (XI_ButtonRelease) fall thru */
default:
event->button.type = (ev->evtype == XI_ButtonPress) ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE;
event->scroll.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
}
else
{
#ifdef XINPUT_2_1
if (ev->evtype == XI_TouchBegin ||
ev->evtype == XI_TouchEnd)
event->button.type = (ev->evtype == XI_TouchBegin) ? GDK_TOUCH_PRESS : GDK_TOUCH_RELEASE;
else
#endif /* XINPUT_2_1 */
event->button.type = (ev->evtype == XI_ButtonPress) ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE;
event->button.window = window;
event->button.time = xev->time;
@@ -1144,7 +1164,17 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
}
event->button.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
event->button.button = xev->detail;
#ifdef XINPUT_2_1
if (ev->evtype == XI_TouchBegin ||
ev->evtype == XI_TouchEnd)
{
event->button.button = 1;
event->button.touch_id = xev->detail;
}
else
#endif /* XINPUT_2_1 */
event->button.button = xev->detail;
}
if (return_val == FALSE)
@@ -1161,11 +1191,32 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
break;
}
case XI_Motion:
#ifdef XINPUT_2_1
case XI_TouchMotion:
case XI_TouchMotionUnowned:
/* FIXME: Unowned events should be rollback-able,
* the easiest way to go could be just storing the
* events so they can be replayed in arrival order
* when an ownership event arrives, needs further
* investigation though.
*/
#endif /* XINPUT_2_1 */
{
XIDeviceEvent *xev = (XIDeviceEvent *) ev;
GdkDevice *source_device;
event->motion.type = GDK_MOTION_NOTIFY;
if (ev->evtype == XI_Motion)
{
event->motion.touch_id = 0;
event->motion.type = GDK_MOTION_NOTIFY;
}
#ifdef XINPUT_2_1
else
{
event->motion.touch_id = xev->detail;
event->motion.type = GDK_TOUCH_MOTION;
}
#endif
event->motion.window = window;
@@ -1252,6 +1303,7 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
return_val = FALSE;
}
break;
default:
return_val = FALSE;
break;
@@ -1297,7 +1349,8 @@ gdk_x11_device_manager_xi2_get_handled_events (GdkEventTranslator *translator)
GDK_BUTTON2_MOTION_MASK |
GDK_BUTTON3_MOTION_MASK |
GDK_BUTTON_MOTION_MASK |
GDK_FOCUS_CHANGE_MASK);
GDK_FOCUS_CHANGE_MASK |
GDK_TOUCH_MASK);
}
static void

View File

@@ -1869,6 +1869,12 @@ gtk_main_do_event (GdkEvent *event)
case GDK_BUTTON_RELEASE:
case GDK_PROXIMITY_IN:
case GDK_PROXIMITY_OUT:
case GDK_TOUCH_MOTION:
case GDK_TOUCH_PRESS:
case GDK_TOUCH_RELEASE:
case GDK_MULTITOUCH_ADDED:
case GDK_MULTITOUCH_REMOVED:
case GDK_MULTITOUCH_UPDATED:
gtk_propagate_event (grab_widget, event);
break;
@@ -1912,6 +1918,7 @@ gtk_main_do_event (GdkEvent *event)
|| event->type == GDK_DRAG_ENTER
|| event->type == GDK_GRAB_BROKEN
|| event->type == GDK_MOTION_NOTIFY
|| event->type == GDK_TOUCH_MOTION
|| event->type == GDK_SCROLL)
{
_gtk_tooltip_handle_event (event);

View File

@@ -471,6 +471,7 @@ enum {
QUERY_TOOLTIP,
DRAG_FAILED,
STYLE_UPDATED,
MULTITOUCH_EVENT,
LAST_SIGNAL
};
@@ -2844,6 +2845,15 @@ gtk_widget_class_init (GtkWidgetClass *klass)
G_TYPE_BOOLEAN, 1,
GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
widget_signals[MULTITOUCH_EVENT] =
g_signal_new (I_("multitouch-event"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkWidgetClass, multitouch_event),
_gtk_boolean_handled_accumulator, NULL,
_gtk_marshal_BOOLEAN__BOXED,
G_TYPE_BOOLEAN, 1,
GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
* GtkWidget::query-tooltip:
* @widget: the object which received the signal
@@ -5993,15 +6003,18 @@ gtk_widget_event_internal (GtkWidget *widget,
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
case GDK_TOUCH_PRESS:
signal_num = BUTTON_PRESS_EVENT;
break;
case GDK_SCROLL:
signal_num = SCROLL_EVENT;
break;
case GDK_BUTTON_RELEASE:
case GDK_TOUCH_RELEASE:
signal_num = BUTTON_RELEASE_EVENT;
break;
case GDK_MOTION_NOTIFY:
case GDK_TOUCH_MOTION:
signal_num = MOTION_NOTIFY_EVENT;
break;
case GDK_DELETE:
@@ -6069,6 +6082,11 @@ gtk_widget_event_internal (GtkWidget *widget,
case GDK_DAMAGE:
signal_num = DAMAGE_EVENT;
break;
case GDK_MULTITOUCH_ADDED:
case GDK_MULTITOUCH_REMOVED:
case GDK_MULTITOUCH_UPDATED:
signal_num = MULTITOUCH_EVENT;
break;
default:
g_warning ("gtk_widget_event(): unhandled event type: %d", event->type);
signal_num = -1;

View File

@@ -423,6 +423,10 @@ struct _GtkWidgetClass
void (* style_updated) (GtkWidget *widget);
/* Multitouch */
gboolean (* multitouch_event) (GtkWidget *widget,
GdkEventMultiTouch *event);
/*< private >*/
/* Padding for future expansion */
@@ -433,7 +437,6 @@ struct _GtkWidgetClass
void (*_gtk_reserved5) (void);
void (*_gtk_reserved6) (void);
void (*_gtk_reserved7) (void);
void (*_gtk_reserved8) (void);
};
struct _GtkWidgetAuxInfo