Compare commits
12 Commits
css-curren
...
xi21
Author | SHA1 | Date | |
---|---|---|---|
|
bb99289487 | ||
|
f48204d4af | ||
|
fa3f6fa0ed | ||
|
be5f2c7cd4 | ||
|
776477ee96 | ||
|
4be23cb80c | ||
|
93162f080d | ||
|
8bc4f71f68 | ||
|
a0c29b0114 | ||
|
a898575fdd | ||
|
02f62468aa | ||
|
df3af16b3a |
@@ -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])
|
||||
|
@@ -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
514
demos/gtk-demo/multitouch.c
Normal 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;
|
||||
}
|
@@ -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" />
|
||||
|
@@ -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>
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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>
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
||||
/**
|
||||
|
131
gdk/gdkevents.c
131
gdk/gdkevents.c
@@ -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.
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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
350
gdk/gdktouchcluster.c
Normal 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
69
gdk/gdktouchcluster.h
Normal 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__ */
|
@@ -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;
|
||||
|
||||
/**
|
||||
|
478
gdk/gdkwindow.c
478
gdk/gdkwindow.c
@@ -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);
|
||||
}
|
||||
|
@@ -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__ */
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user