From be3721119fea12d0a16ec49be142ed2fd6c0ee4c Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 15 Aug 2009 17:13:44 +0200 Subject: [PATCH] Add/Improve API for handling devices' axes. --- gdk/gdkdevice.c | 325 +++++++++++++++++++++++++++++++++++++++-- gdk/gdkdeviceprivate.h | 23 ++- 2 files changed, 327 insertions(+), 21 deletions(-) diff --git a/gdk/gdkdevice.c b/gdk/gdkdevice.c index 1051914fad..ca02952a23 100644 --- a/gdk/gdkdevice.c +++ b/gdk/gdkdevice.c @@ -26,10 +26,25 @@ #define GDK_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDK_TYPE_DEVICE, GdkDevicePrivate)) typedef struct _GdkDevicePrivate GdkDevicePrivate; +typedef struct _GdkAxisInfo GdkAxisInfo; + +struct _GdkAxisInfo +{ + GdkAtom label; + GdkAxisUse use; + + gdouble min_axis; + gdouble max_axis; + + gdouble min_value; + gdouble max_value; + gdouble resolution; +}; struct _GdkDevicePrivate { GdkDisplay *display; + GArray *axes; }; static void gdk_device_set_property (GObject *object, @@ -51,6 +66,7 @@ enum { PROP_INPUT_SOURCE, PROP_INPUT_MODE, PROP_HAS_CURSOR, + PROP_N_AXES }; @@ -99,6 +115,13 @@ gdk_device_class_init (GdkDeviceClass *klass) P_("Whether there is a visible cursor following device motion"), FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_N_AXES, + g_param_spec_uint ("n-axes", + P_("Number of axes in the device"), + P_("Number of axes in the device"), + 0, G_MAXUINT, 0, + G_PARAM_READABLE)); g_type_class_add_private (object_class, sizeof (GdkDevicePrivate)); } @@ -106,6 +129,10 @@ gdk_device_class_init (GdkDeviceClass *klass) static void gdk_device_init (GdkDevice *device) { + GdkDevicePrivate *priv; + + priv = GDK_DEVICE_GET_PRIVATE (device); + priv->axes = g_array_new (FALSE, TRUE, sizeof (GdkAxisInfo)); } static void @@ -171,6 +198,9 @@ gdk_device_get_property (GObject *object, g_value_set_boolean (value, device->has_cursor); break; + case PROP_N_AXES: + g_value_set_uint (value, priv->axes->len); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -221,22 +251,6 @@ gdk_device_free_history (GdkTimeCoord **events, g_free (events); } -gboolean -gdk_device_get_axis (GdkDevice *device, - gdouble *axes, - GdkAxisUse use, - gdouble *value) -{ - g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE); - g_return_val_if_fail (axes != NULL, FALSE); - g_return_val_if_fail (value != NULL, FALSE); - - if (!GDK_DEVICE_GET_CLASS (device)->get_axis) - return FALSE; - - return GDK_DEVICE_GET_CLASS (device)->get_axis (device, axes, use, value); -} - void gdk_device_set_source (GdkDevice *device, GdkInputSource source) @@ -312,5 +326,284 @@ gdk_device_get_display (GdkDevice *device) return priv->display; } +GList * +gdk_device_list_axes (GdkDevice *device) +{ + GdkDevicePrivate *priv; + GList *axes = NULL; + gint i; + + priv = GDK_DEVICE_GET_PRIVATE (device); + + for (i = 0; i < priv->axes->len; i++) + { + GdkAxisInfo axis_info; + + axis_info = g_array_index (priv->axes, GdkAxisInfo, i); + axes = g_list_prepend (axes, GDK_ATOM_TO_POINTER (axis_info.label)); + } + + return g_list_reverse (axes); +} + +gboolean +gdk_device_get_axis_value (GdkDevice *device, + gdouble *axes, + GdkAtom axis_label, + gdouble *value) +{ + GdkDevicePrivate *priv; + gint i; + + g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE); + + if (axes == NULL) + return FALSE; + + priv = GDK_DEVICE_GET_PRIVATE (device); + + for (i = 0; i < priv->axes->len; i++) + { + GdkAxisInfo axis_info; + + axis_info = g_array_index (priv->axes, GdkAxisInfo, i); + + if (axis_info.label != axis_label) + continue; + + if (value) + *value = axes[i]; + + return TRUE; + } + + return FALSE; +} + +gboolean +gdk_device_get_axis (GdkDevice *device, + gdouble *axes, + GdkAxisUse use, + gdouble *value) +{ + GdkDevicePrivate *priv; + gint i; + + g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE); + + if (axes == NULL) + return FALSE; + + priv = GDK_DEVICE_GET_PRIVATE (device); + + g_return_val_if_fail (priv->axes != NULL, FALSE); + + for (i = 0; i < priv->axes->len; i++) + { + GdkAxisInfo axis_info; + + axis_info = g_array_index (priv->axes, GdkAxisInfo, i); + + if (axis_info.use != use) + continue; + + if (value) + *value = axes[i]; + + return TRUE; + } + + return FALSE; +} + +/* Private API */ +void +_gdk_device_reset_axes (GdkDevice *device) +{ + GdkDevicePrivate *priv; + guint i; + + priv = GDK_DEVICE_GET_PRIVATE (device); + + for (i = priv->axes->len - 1; i >= 0; i--) + g_array_remove_index (priv->axes, i); +} + +guint +_gdk_device_add_axis (GdkDevice *device, + GdkAtom label_atom, + GdkAxisUse use, + gdouble min_value, + gdouble max_value, + gdouble resolution) +{ + GdkDevicePrivate *priv; + GdkAxisInfo axis_info; + + priv = GDK_DEVICE_GET_PRIVATE (device); + + axis_info.use = use; + axis_info.label = label_atom; + axis_info.min_value = min_value; + axis_info.max_value = max_value; + axis_info.resolution = resolution; + + switch (use) + { + case GDK_AXIS_X: + case GDK_AXIS_Y: + axis_info.min_axis = 0.; + axis_info.max_axis = 0.; + break; + case GDK_AXIS_XTILT: + case GDK_AXIS_YTILT: + axis_info.min_axis = -1.; + axis_info.max_axis = 1.; + break; + default: + axis_info.min_axis = 0.; + axis_info.max_axis = 1.; + break; + } + + priv->axes = g_array_append_val (priv->axes, axis_info); + + return priv->axes->len - 1; +} + +GdkAxisInfo * +find_axis_info (GArray *array, + GdkAxisUse use) +{ + GdkAxisInfo *info; + gint i; + + for (i = 0; i < GDK_AXIS_LAST; i++) + { + info = &g_array_index (array, GdkAxisInfo, i); + + if (info->use == use) + return info; + } + + return NULL; +} + +gboolean +_gdk_device_translate_axis (GdkDevice *device, + gdouble window_width, + gdouble window_height, + gdouble window_x, + gdouble window_y, + guint index, + gdouble value, + gdouble *axis_value) +{ + GdkDevicePrivate *priv; + GdkAxisInfo axis_info; + gdouble out = 0; + + priv = GDK_DEVICE_GET_PRIVATE (device); + + if (index >= priv->axes->len) + return FALSE; + + axis_info = g_array_index (priv->axes, GdkAxisInfo, index); + + if (axis_info.use == GDK_AXIS_X || + axis_info.use == GDK_AXIS_Y) + { + GdkAxisInfo *axis_info_x, *axis_info_y; + gdouble device_width, device_height; + gdouble x_offset, y_offset; + gdouble x_scale, y_scale; + + if (axis_info.use == GDK_AXIS_X) + { + axis_info_x = &axis_info; + axis_info_y = find_axis_info (priv->axes, GDK_AXIS_Y); + } + else + { + axis_info_x = find_axis_info (priv->axes, GDK_AXIS_X); + axis_info_y = &axis_info; + } + + device_width = axis_info_x->max_value - axis_info_x->min_value; + device_height = axis_info_y->max_value - axis_info_y->min_value; + + if (device->mode == GDK_MODE_SCREEN) + { + if (axis_info.use == GDK_AXIS_X) + out = window_x; + else + out = window_y; + } + else /* GDK_MODE_WINDOW */ + { + gdouble x_resolution, y_resolution, device_aspect; + + x_resolution = axis_info_x->resolution; + y_resolution = axis_info_y->resolution; + + /* + * Some drivers incorrectly report the resolution of the device + * as zero (in partiular linuxwacom < 0.5.3 with usb tablets). + * This causes the device_aspect to become NaN and totally + * breaks windowed mode. If this is the case, the best we can + * do is to assume the resolution is non-zero is equal in both + * directions (which is true for many devices). The absolute + * value of the resolution doesn't matter since we only use the + * ratio. + */ + if (x_resolution == 0 || y_resolution == 0) + { + x_resolution = 1; + y_resolution = 1; + } + + device_aspect = (device_height * y_resolution) / + (device_width * x_resolution); + + if (device_aspect * window_width >= window_height) + { + /* device taller than window */ + x_scale = window_width / device_width; + y_scale = (x_scale * x_resolution) / y_resolution; + + x_offset = 0; + y_offset = - (device_height * y_scale - window_height) / 2; + } + else + { + /* window taller than device */ + y_scale = window_height / device_height; + x_scale = (y_scale * y_resolution) / x_resolution; + + y_offset = 0; + x_offset = - (device_width * x_scale - window_width) / 2; + } + + if (axis_info.use == GDK_AXIS_X) + out = x_offset + x_scale * (value - axis_info.min_value); + else + out = y_offset + y_scale * (value - axis_info.min_value); + } + } + else + { + gdouble axis_width; + + axis_width = axis_info.max_value - axis_info.min_value; + out = (axis_info.max_axis * (value - axis_info.min_value) + + axis_info.min_axis * (axis_info.max_value - value)) / axis_width; + } + + if (axis_value) + *axis_value = out; + + return TRUE; +} + #define __GDK_DEVICE_C__ #include "gdkaliasdef.c" diff --git a/gdk/gdkdeviceprivate.h b/gdk/gdkdeviceprivate.h index d1ce69bb0d..ca6bd21ec7 100644 --- a/gdk/gdkdeviceprivate.h +++ b/gdk/gdkdeviceprivate.h @@ -50,11 +50,6 @@ struct _GdkDeviceClass gdouble *axes, GdkModifierType *mask); - gboolean (* get_axis) (GdkDevice *device, - gdouble *axes, - GdkAxisUse use, - gdouble *value); - void (* set_window_cursor) (GdkDevice *device, GdkWindow *window, GdkCursor *cursor); @@ -65,6 +60,24 @@ struct _GdkDeviceClass gint y); }; + +void _gdk_device_reset_axes (GdkDevice *device); +guint _gdk_device_add_axis (GdkDevice *device, + GdkAtom label_atom, + GdkAxisUse use, + gdouble min_value, + gdouble max_value, + gdouble resolution); + +gboolean _gdk_device_translate_axis (GdkDevice *device, + gdouble window_width, + gdouble window_height, + gdouble window_x, + gdouble window_y, + guint index, + gdouble value, + gdouble *axis_value); + G_END_DECLS #endif /* __GDK_DEVICE_PRIVATE_H__ */