devicemanager,xi2: Implement smooth scrolling

XInput >= 2.1 allows for implementing smooth scrolling,
reporting the different scrolling axes as valuators.
Any change in those will be reported as GdkEventScroll
events with delta_x/y information.

the older kind of scroll events is still handled, and
emulated in devices able to provide smooth scrolling,
setting _gdk_event_set_pointer_emulated() in that case.
This commit is contained in:
Carlos Garnacho 2012-01-24 00:49:52 +01:00 committed by Matthias Clasen
parent f941c78969
commit 147cdd8465
3 changed files with 206 additions and 3 deletions

View File

@ -33,11 +33,22 @@
#endif #endif
typedef struct _ScrollValuator ScrollValuator;
struct _ScrollValuator
{
guint n_valuator : 4;
guint direction : 4;
guint last_value_valid : 1;
gdouble last_value;
};
struct _GdkX11DeviceXI2 struct _GdkX11DeviceXI2
{ {
GdkDevice parent_instance; GdkDevice parent_instance;
gint device_id; gint device_id;
GArray *scroll_valuators;
}; };
struct _GdkX11DeviceXI2Class struct _GdkX11DeviceXI2Class
@ -49,6 +60,7 @@ G_DEFINE_TYPE (GdkX11DeviceXI2, gdk_x11_device_xi2, GDK_TYPE_DEVICE)
#ifdef XINPUT_2 #ifdef XINPUT_2
static void gdk_x11_device_xi2_finalize (GObject *object);
static void gdk_x11_device_xi2_get_property (GObject *object, static void gdk_x11_device_xi2_get_property (GObject *object,
guint prop_id, guint prop_id,
GValue *value, GValue *value,
@ -110,6 +122,7 @@ gdk_x11_device_xi2_class_init (GdkX11DeviceXI2Class *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass);
GdkDeviceClass *device_class = GDK_DEVICE_CLASS (klass); GdkDeviceClass *device_class = GDK_DEVICE_CLASS (klass);
object_class->finalize = gdk_x11_device_xi2_finalize;
object_class->get_property = gdk_x11_device_xi2_get_property; object_class->get_property = gdk_x11_device_xi2_get_property;
object_class->set_property = gdk_x11_device_xi2_set_property; object_class->set_property = gdk_x11_device_xi2_set_property;
@ -134,6 +147,17 @@ gdk_x11_device_xi2_class_init (GdkX11DeviceXI2Class *klass)
static void static void
gdk_x11_device_xi2_init (GdkX11DeviceXI2 *device) gdk_x11_device_xi2_init (GdkX11DeviceXI2 *device)
{ {
device->scroll_valuators = g_array_new (FALSE, FALSE, sizeof (ScrollValuator));
}
static void
gdk_x11_device_xi2_finalize (GObject *object)
{
GdkX11DeviceXI2 *device = GDK_X11_DEVICE_XI2 (object);
g_array_free (device->scroll_valuators, TRUE);
G_OBJECT_CLASS (gdk_x11_device_xi2_parent_class)->finalize (object);
} }
static void static void
@ -777,6 +801,83 @@ _gdk_x11_device_xi2_translate_state (XIModifierState *mods_state,
return state; return state;
} }
void
_gdk_x11_device_xi2_add_scroll_valuator (GdkX11DeviceXI2 *device,
guint n_valuator,
GdkScrollDirection direction)
{
ScrollValuator scroll;
g_return_if_fail (GDK_IS_X11_DEVICE_XI2 (device));
g_return_if_fail (n_valuator < gdk_device_get_n_axes (GDK_DEVICE (device)));
scroll.n_valuator = n_valuator;
scroll.direction = direction;
scroll.last_value_valid = FALSE;
g_array_append_val (device->scroll_valuators, scroll);
}
gboolean
_gdk_x11_device_xi2_get_scroll_delta (GdkX11DeviceXI2 *device,
guint n_valuator,
gdouble valuator_value,
GdkScrollDirection *direction_ret,
gdouble *delta_ret)
{
guint i;
g_return_val_if_fail (GDK_IS_X11_DEVICE_XI2 (device), FALSE);
g_return_val_if_fail (n_valuator < gdk_device_get_n_axes (GDK_DEVICE (device)), FALSE);
for (i = 0; i < device->scroll_valuators->len; i++)
{
ScrollValuator *scroll;
scroll = &g_array_index (device->scroll_valuators, ScrollValuator, i);
if (scroll->n_valuator == n_valuator)
{
if (direction_ret)
*direction_ret = scroll->direction;
if (delta_ret)
*delta_ret = 0;
if (scroll->last_value_valid)
{
if (delta_ret)
*delta_ret = valuator_value - scroll->last_value;
scroll->last_value = valuator_value;
}
else
{
scroll->last_value = valuator_value;
scroll->last_value_valid = TRUE;
}
return TRUE;
}
}
return FALSE;
}
void
_gdk_device_xi2_reset_scroll_valuators (GdkX11DeviceXI2 *device)
{
guint i;
for (i = 0; i < device->scroll_valuators->len; i++)
{
ScrollValuator *scroll;
scroll = &g_array_index (device->scroll_valuators, ScrollValuator, i);
scroll->last_value_valid = FALSE;
}
}
gint gint
_gdk_x11_device_xi2_get_id (GdkX11DeviceXI2 *device) _gdk_x11_device_xi2_get_id (GdkX11DeviceXI2 *device)
{ {

View File

@ -241,6 +241,22 @@ translate_device_classes (GdkDisplay *display,
valuator_info->resolution); valuator_info->resolution);
} }
break; break;
#ifdef XINPUT_2_2
case XIScrollClass:
{
XIScrollClassInfo *scroll_info = (XIScrollClassInfo *) class_info;
GdkScrollDirection direction;
if (scroll_info->scroll_type == XIScrollTypeVertical)
direction = GDK_SCROLL_DOWN;
else
direction = GDK_SCROLL_RIGHT;
_gdk_x11_device_xi2_add_scroll_valuator (GDK_X11_DEVICE_XI2 (device),
scroll_info->number,
direction);
}
#endif /* XINPUT_2_2 */
default: default:
/* Ignore */ /* Ignore */
break; break;
@ -1036,6 +1052,47 @@ gdk_x11_device_manager_xi2_translate_core_event (GdkEventTranslator *translator,
return TRUE; return TRUE;
} }
static gboolean
scroll_valuators_changed (GdkX11DeviceXI2 *device,
XIValuatorState *valuators,
gdouble *dx,
gdouble *dy)
{
gdouble has_scroll_valuators = FALSE;
GdkScrollDirection direction;
guint n_axes, i, n_val;
gdouble *vals;
n_axes = gdk_device_get_n_axes (GDK_DEVICE (device));
vals = valuators->values;
*dx = *dy = 0;
n_val = 0;
for (i = 0; i < MIN (valuators->mask_len * 8, n_axes); i++)
{
gdouble delta;
if (!XIMaskIsSet (valuators->mask, i))
continue;
if (_gdk_x11_device_xi2_get_scroll_delta (device, i, vals[n_val],
&direction, &delta))
{
has_scroll_valuators = TRUE;
if (direction == GDK_SCROLL_UP ||
direction == GDK_SCROLL_DOWN)
*dy = delta;
else
*dx = delta;
}
n_val++;
}
return has_scroll_valuators;
}
static gboolean static gboolean
gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator, gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
GdkDisplay *display, GdkDisplay *display,
@ -1142,8 +1199,11 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
XIDeviceEvent *xev = (XIDeviceEvent *) ev; XIDeviceEvent *xev = (XIDeviceEvent *) ev;
GdkDevice *source_device; GdkDevice *source_device;
if (ev->evtype == XI_ButtonPress && if (ev->evtype == XI_ButtonRelease &&
(xev->detail >= 4 && xev->detail <= 7)) (xev->detail >= 4 && xev->detail <= 7))
return FALSE;
else if (ev->evtype == XI_ButtonPress &&
(xev->detail >= 4 && xev->detail <= 7))
{ {
/* Button presses of button 4-7 are scroll events */ /* Button presses of button 4-7 are scroll events */
event->scroll.type = GDK_SCROLL; event->scroll.type = GDK_SCROLL;
@ -1163,6 +1223,8 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
event->scroll.y = (gdouble) xev->event_y; event->scroll.y = (gdouble) xev->event_y;
event->scroll.x_root = (gdouble) xev->root_x; event->scroll.x_root = (gdouble) xev->root_x;
event->scroll.y_root = (gdouble) xev->root_y; event->scroll.y_root = (gdouble) xev->root_y;
event->scroll.delta_x = 0;
event->scroll.delta_y = 0;
event->scroll.device = g_hash_table_lookup (device_manager->id_table, event->scroll.device = g_hash_table_lookup (device_manager->id_table,
GUINT_TO_POINTER (xev->deviceid)); GUINT_TO_POINTER (xev->deviceid));
@ -1172,6 +1234,9 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
gdk_event_set_source_device (event, source_device); gdk_event_set_source_device (event, source_device);
event->scroll.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group); event->scroll.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
if (xev->flags & XIPointerEmulated)
_gdk_event_set_pointer_emulated (event, TRUE);
} }
else else
{ {
@ -1233,6 +1298,36 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
{ {
XIDeviceEvent *xev = (XIDeviceEvent *) ev; XIDeviceEvent *xev = (XIDeviceEvent *) ev;
GdkDevice *source_device; GdkDevice *source_device;
gdouble delta_x, delta_y;
source_device = g_hash_table_lookup (device_manager->id_table,
GUINT_TO_POINTER (xev->sourceid));
if (scroll_valuators_changed (GDK_X11_DEVICE_XI2 (source_device),
&xev->valuators, &delta_x, &delta_y))
{
event->scroll.type = GDK_SCROLL;
event->scroll.direction = GDK_SCROLL_SMOOTH;
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.delta_x = delta_x;
event->scroll.delta_y = delta_y;
event->scroll.device = g_hash_table_lookup (device_manager->id_table,
GUINT_TO_POINTER (xev->deviceid));
gdk_event_set_source_device (event, source_device);
event->scroll.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
break;
}
else
_gdk_device_xi2_reset_scroll_valuators (GDK_X11_DEVICE_XI2 (source_device));
event->motion.type = GDK_MOTION_NOTIFY; event->motion.type = GDK_MOTION_NOTIFY;
event->motion.window = window; event->motion.window = window;
@ -1245,8 +1340,6 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
event->motion.device = g_hash_table_lookup (device_manager->id_table, event->motion.device = g_hash_table_lookup (device_manager->id_table,
GINT_TO_POINTER (xev->deviceid)); GINT_TO_POINTER (xev->deviceid));
source_device = g_hash_table_lookup (device_manager->id_table,
GUINT_TO_POINTER (xev->sourceid));
gdk_event_set_source_device (event, source_device); gdk_event_set_source_device (event, source_device);
event->motion.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group); event->motion.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);

View File

@ -255,6 +255,15 @@ gint _gdk_x11_device_xi2_get_id (GdkX11DeviceXI2 *device);
GdkDevice * _gdk_x11_device_manager_xi2_lookup (GdkX11DeviceManagerXI2 *device_manager_xi2, GdkDevice * _gdk_x11_device_manager_xi2_lookup (GdkX11DeviceManagerXI2 *device_manager_xi2,
gint device_id); gint device_id);
void _gdk_x11_device_xi2_add_scroll_valuator (GdkX11DeviceXI2 *device,
guint n_valuator,
GdkScrollDirection direction);
gboolean _gdk_x11_device_xi2_get_scroll_delta (GdkX11DeviceXI2 *device,
guint n_valuator,
gdouble valuator_value,
GdkScrollDirection *direction_ret,
gdouble *delta_ret);
void _gdk_device_xi2_reset_scroll_valuators (GdkX11DeviceXI2 *device);
#endif #endif