>From 5ad76a56beacc3fdc7cbfcff4ae42fa3dfcac768 Mon Sep 17 00:00:00 2001
From: Olivier Fourdan <ofour...@redhat.com>
Date: Mon, 29 Oct 2012 18:08:27 +0100
Subject: [PATCH 3/4] tools: add tablet image viewer
test: add image validation
for tablet images.
Built is optional and depends on availability of GTK+-2.x
and librsvg-2.x libraries.
---
configure.ac | 6 +
test/Makefile.am | 7 +
test/tablet-svg-validity.c | 133 +++++++++++++++
tools/Makefile.am | 8 +
tools/show-svg-image.c | 388 ++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 542 insertions(+), 0 deletions(-)
create mode 100644 test/tablet-svg-validity.c
create mode 100644 tools/show-svg-image.c
diff --git a/configure.ac b/configure.ac
index 3fd7aa9..91d9542 100644
--- a/configure.ac
+++ b/configure.ac
@@ -32,6 +32,12 @@ AM_CONDITIONAL(HAVE_DOXYGEN, test "x$HAVE_DOXYGEN" = xyes)
PKG_CHECK_MODULES(GLIB, glib-2.0 gudev-1.0)
+PKG_CHECK_MODULES(RSVG, librsvg-2.0 glib-2.0, HAVE_RSVG="yes", HAVE_RSVG="no")
+AM_CONDITIONAL(HAVE_RSVG, test x$HAVE_RSVG = xyes)
+
+PKG_CHECK_MODULES(GTK2, librsvg-2.0 glib-2.0 gtk+-2.0, HAVE_GTK2="yes", HAVE_GTK2="no")
+AM_CONDITIONAL(HAVE_GTK2, test x$HAVE_GTK2 = xyes)
+
AC_CONFIG_FILES([Makefile
data/Makefile
data/images/Makefile
diff --git a/test/Makefile.am b/test/Makefile.am
index 6be4403..ab05e6d 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -8,6 +8,13 @@ load_LDADD=$(top_builddir)/libwacom/libwacom.la
dbverify_LDADD=$(top_builddir)/libwacom/libwacom.la
tablet_validity_LDADD=$(top_builddir)/libwacom/libwacom.la
+if HAVE_RSVG
+noinst_PROGRAMS += tablet-svg-validity
+tablet_svg_validity_SOURCES = tablet-svg-validity.c
+tablet_svg_validity_LDADD = $(top_builddir)/libwacom/libwacom.la $(RSVG_LIBS)
+tablet_svg_validity_CFLAGS = $(RSVG_CFLAGS)
+endif
+
clean-local: clean-local-check
.PHONY: clean-local-check
diff --git a/test/tablet-svg-validity.c b/test/tablet-svg-validity.c
new file mode 100644
index 0000000..011a0f7
--- /dev/null
+++ b/test/tablet-svg-validity.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright © 2012 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of Red Hat
+ * not be used in advertising or publicity pertaining to distribution
+ * of the software without specific, written prior permission. Red
+ * Hat makes no representations about the suitability of this software
+ * for any purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Olivier Fourdan <ofour...@redhat.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <librsvg/rsvg.h>
+#include <libwacom/libwacom.h>
+#include <assert.h>
+#include <unistd.h>
+
+static void
+verify_has_sub (RsvgHandle *handle, const char *name)
+{
+ RsvgDimensionData dimensions;
+ RsvgPositionData position;
+
+ /* control is present */
+ assert (rsvg_handle_has_sub (handle, name) != FALSE);
+
+ /* Can retrieve position */
+ assert (rsvg_handle_get_position_sub (handle, &position, name) != FALSE);
+ assert (position.x >= 0);
+ assert (position.y >= 0);
+
+ /* And size */
+ assert (rsvg_handle_get_dimensions_sub (handle, &dimensions, name) != FALSE);
+ assert (dimensions.width > 0);
+ assert (dimensions.height > 0);
+}
+
+static void
+verify_tablet_image(WacomDeviceDatabase *db, WacomDevice *device)
+{
+ GError *error;
+ RsvgHandle *handle;
+ RsvgDimensionData dimensions;
+ RsvgPositionData position;
+ const char *name;
+ const char *filename;
+ char button;
+ int num_buttons;
+
+ name = libwacom_get_name(device);
+ if (strcmp(name, "Generic") == 0)
+ return;
+
+ filename = libwacom_get_image_filename(device);
+ if (filename == NULL)
+ return;
+
+ handle = rsvg_handle_new_from_file (filename, NULL);
+ assert(handle != NULL);
+
+ rsvg_handle_get_dimensions (handle, &dimensions);
+ assert(dimensions.width != 0);
+ assert(dimensions.height != 0);
+
+ num_buttons = libwacom_get_num_buttons (device);
+ for (button = 'A'; button < 'A' + num_buttons; button++) {
+ char *sub;
+
+ sub = g_strdup_printf ("#Button%c", button);
+ verify_has_sub (handle, sub);
+ g_free (sub);
+
+ sub = g_strdup_printf ("#Label%c", button);
+ verify_has_sub (handle, sub);
+ g_free (sub);
+ }
+
+ /* Touch rings */
+ if (libwacom_has_ring(device))
+ verify_has_sub (handle, "#Ring");
+ if (libwacom_has_ring2(device))
+ verify_has_sub (handle, "#Ring2");
+
+ /* Touch strips */
+ if (libwacom_get_num_strips(device) > 0)
+ verify_has_sub (handle, "#Strip");
+ if (libwacom_get_num_strips(device) > 1)
+ verify_has_sub (handle, "#Strip2");
+ g_message ("Device '%s', file %s OK", name, filename);
+}
+
+int main(int argc, char **argv)
+{
+ WacomDeviceDatabase *db;
+ WacomDevice **device, **devices;
+
+ g_type_init ();
+ db = libwacom_database_new_for_path(TOPSRCDIR"/data");
+ if (!db)
+ printf("Failed to load data from %s", TOPSRCDIR"/data");
+ assert(db);
+
+ devices = libwacom_list_devices_from_database(db, NULL);
+ assert(devices);
+ assert(*devices);
+
+ for (device = devices; *device; device++)
+ verify_tablet_image(db, *device);
+
+ libwacom_database_destroy (db);
+
+ return 0;
+}
+
diff --git a/tools/Makefile.am b/tools/Makefile.am
index c204ab0..94fe401 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -1,6 +1,7 @@
AM_CPPFLAGS=-I$(top_srcdir)/libwacom -DTOPSRCDIR="\"$(top_srcdir)\""
noinst_PROGRAMS = generate-udev-rules list-devices
+
generate_udev_rules_SOURCES = generate-udev-rules.c
generate_udev_rules_LDADD=$(top_builddir)/libwacom/libwacom.la $(GLIB_LIBS)
generate_udev_rules_CFLAGS=$(GLIB_CFLAGS)
@@ -13,3 +14,10 @@ libwacom_list_local_devices_SOURCES = list-local-devices.c
libwacom_list_local_devices_LDADD=$(top_builddir)/libwacom/libwacom.la $(GLIB_LIBS)
libwacom_list_local_devices_CFLAGS=$(GLIB_CFLAGS)
+if HAVE_GTK2
+noinst_PROGRAMS += show-svg-image
+show_svg_image_SOURCES = show-svg-image.c
+show_svg_image_LDADD = $(top_builddir)/libwacom/libwacom.la $(GTK2_LIBS)
+show_svg_image_CFLAGS = $(GTK2_CFLAGS)
+endif
+
diff --git a/tools/show-svg-image.c b/tools/show-svg-image.c
new file mode 100644
index 0000000..87b18df
--- /dev/null
+++ b/tools/show-svg-image.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright © 2012 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of Red Hat
+ * not be used in advertising or publicity pertaining to distribution
+ * of the software without specific, written prior permission. Red
+ * Hat makes no representations about the suitability of this software
+ * for any purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Olivier Fourdan <ofour...@redhat.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+#include <cairo.h>
+#include <librsvg/rsvg.h>
+#include <libwacom/libwacom.h>
+
+#define INACTIVE_COLOR "#2d2d2d"
+#define ACTIVE_COLOR "#ffffff"
+#define STROKE_COLOR "#b4b4b4"
+#define DARK_COLOR "#141414"
+#define BACK_COLOR "#000000"
+
+/* Convenient struct to store our stuff around */
+typedef struct
+{
+ RsvgHandle *handle;
+ GtkWidget *widget;
+ guint timeout;
+ WacomDevice *device;
+ GdkRectangle area;
+ char active_button;
+ int num_buttons;
+} Tablet;
+
+static gboolean
+get_sub_location (cairo_t *cairo_context, RsvgHandle *handle, const char *sub, double *x, double *y, double *width, double *height)
+{
+ if (x || y) {
+ RsvgPositionData position;
+ double tx, ty;
+
+ if (!rsvg_handle_get_position_sub (handle, &position, sub)) {
+ g_warning ("Failed to retrieve '%s' position", sub);
+ return FALSE;
+ }
+
+ tx = (double) position.x;
+ ty = (double) position.y;
+ cairo_user_to_device (cairo_context, &tx, &ty);
+
+ if (x)
+ *x = tx;
+ if (y)
+ *y = ty;
+ }
+
+ if (width || height) {
+ RsvgDimensionData dimensions;
+ double twidth, theight;
+
+ if (!rsvg_handle_get_dimensions_sub (handle, &dimensions, sub)) {
+ g_warning ("Failed to retrieve '%s' dimension", sub);
+ return FALSE;
+ }
+
+ twidth = (double) dimensions.width;
+ theight = (double) dimensions.height;
+ cairo_user_to_device_distance (cairo_context, &twidth, &theight);
+
+ if (width)
+ *width = twidth;
+ if (height)
+ *height = theight;
+ }
+
+ return TRUE;
+}
+
+static void
+print_button_labels (cairo_t *cairo_context, Tablet *tablet)
+{
+ char button;
+ GtkWidget *widget;
+ GtkAllocation allocation;
+
+ widget = GTK_WIDGET(tablet->widget);
+ gtk_widget_get_allocation(widget, &allocation);
+
+ for (button = 'A'; button < 'A' + tablet->num_buttons; button++) {
+ WacomButtonFlags flags;
+ GtkStyle *style;
+ PangoContext *pango_context;
+ PangoLayout *pango_layout;
+ PangoRectangle pango_rect;
+ double label_x, label_y;
+ double btn_x, btn_y, btn_width, btn_height;
+ int x, y;
+ gchar *sub;
+ gchar *markup;
+
+ flags = libwacom_get_button_flag(tablet->device, button);
+ sub = g_strdup_printf ("#Label%c", button);
+ if (!get_sub_location (cairo_context, tablet->handle, sub, &label_x, &label_y, NULL, NULL)) {
+ g_warning ("Failed to retrieve %s position", sub);
+ goto next;
+ }
+
+ /* Write the label */
+ style = gtk_widget_get_style (widget);
+ pango_context = gtk_widget_get_pango_context (widget);
+ pango_layout = pango_layout_new (pango_context);
+ if (button == tablet->active_button)
+ markup = g_strdup_printf ("<span foreground=\"" ACTIVE_COLOR "\" weight=\"bold\">Button %c</span>", button);
+ else
+ markup = g_strdup_printf ("<span foreground=\"" INACTIVE_COLOR "\" weight=\"bold\">Button %c</span>", button);
+ pango_layout_set_markup (pango_layout, markup, -1);
+ g_free (markup);
+
+ pango_layout_get_pixel_extents (pango_layout, NULL, &pango_rect);
+
+ if (flags & WACOM_BUTTON_POSITION_LEFT) {
+ pango_layout_set_alignment (pango_layout, PANGO_ALIGN_LEFT);
+ x = (int) label_x;
+ y = (int) label_y + pango_rect.height / 2;
+ } else if (flags & WACOM_BUTTON_POSITION_RIGHT) {
+ pango_layout_set_alignment (pango_layout, PANGO_ALIGN_RIGHT);
+ x = (int) label_x - pango_rect.width;
+ y = (int) label_y + pango_rect.height / 2;
+
+ } else {
+ pango_layout_set_alignment (pango_layout, PANGO_ALIGN_CENTER);
+ x = (int) label_x - pango_rect.width / 2;
+ y = (int) label_y;
+ }
+
+ gtk_paint_layout (style,
+ gtk_widget_get_window (widget),
+ 0,
+ TRUE,
+ &allocation,
+ widget,
+ NULL,
+ x,
+ y,
+ pango_layout);
+
+ g_object_unref (pango_layout);
+next:
+ g_free (sub);
+ }
+}
+
+static void
+update_tablet (Tablet *tablet)
+{
+ char *width, *height;
+ char button;
+ GError *error;
+ guint8 *data;
+
+ if (tablet->handle)
+ g_object_unref (tablet->handle);
+
+ width = g_strdup_printf ("%d", tablet->area.width);
+ height = g_strdup_printf ("%d", tablet->area.height);
+
+ data = g_strconcat ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
+ "<svg version=\"1.1\"\n"
+ " xmlns=\"http://www.w3.org/2000/svg\"\n"
+ " xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n"
+ " width=\"", width, "\"\n"
+ " height=\"", height, "\">\n"
+ " <style type=\"text/css\">\n"
+ " * {\n"
+ " stroke: ", STROKE_COLOR," !important;\n"
+ " fill: ", INACTIVE_COLOR," !important;\n"
+ " }\n",
+ NULL);
+ g_free (width);
+ g_free (height);
+
+ for (button = 'A'; button < 'A' + tablet->num_buttons; button++) {
+ gchar class[] = {button, '\0'};
+ if (button == tablet->active_button) {
+ data = g_strconcat (data,
+ " .", class, " {\n"
+ " stroke: ", STROKE_COLOR," !important;\n"
+ " fill: ", button == tablet->active_button ? ACTIVE_COLOR : INACTIVE_COLOR, " !important;\n"
+ " }\n",
+ NULL);
+ }
+ }
+
+ /* Hide the existing labels */
+ data = g_strconcat (data,
+ " .Label {\n"
+ " stroke: none !important;\n"
+ " fill: ", BACK_COLOR, " !important;\n"
+ " }\n",
+ " .TouchStrip,.TouchRing {\n"
+ " stroke: ", INACTIVE_COLOR," !important;\n"
+ " fill: ", DARK_COLOR, " !important;\n"
+ " }\n",
+ " </style>\n"
+ " <xi:include href=\"", libwacom_get_image_filename (tablet->device), "\"/>\n"
+ "</svg>",
+ NULL);
+
+ tablet->handle = rsvg_handle_new_from_data (data, strlen(data), &error);
+ g_free (data);
+}
+
+static void
+on_expose_cb (GtkWidget *widget, GdkEvent *event, Tablet *tablet)
+{
+ GdkEventExpose *expose_event;
+ GtkAllocation allocation;
+ cairo_t *cairo_context;
+ float scale;
+ double twidth, theight;
+
+ expose_event = (GdkEventExpose *) event;
+
+ if (tablet->handle == NULL)
+ update_tablet (tablet);
+
+ /* Create a Cairo for the widget */
+ cairo_context = gdk_cairo_create (gtk_widget_get_window (widget));
+ cairo_set_operator (cairo_context, CAIRO_OPERATOR_CLEAR);
+ cairo_paint (cairo_context);
+ cairo_set_operator (cairo_context, CAIRO_OPERATOR_OVER);
+
+ /* Scale to fit in window */
+ gtk_widget_get_allocation(widget, &allocation);
+ scale = MIN ((float) allocation.width / tablet->area.width,
+ (float) allocation.height / tablet->area.height);
+ cairo_scale (cairo_context, scale, scale);
+
+ /* Center the result in window */
+ twidth = (double) tablet->area.width;
+ theight = (double) tablet->area.height;
+ cairo_user_to_device_distance (cairo_context, &twidth, &theight);
+ twidth = ((double) allocation.width - twidth) / 2.0;
+ theight = ((double) allocation.height - theight) / 2.0;
+ cairo_device_to_user_distance (cairo_context, &twidth, &theight);
+ cairo_translate (cairo_context, twidth, theight);
+
+ /* And render the tablet image */
+ rsvg_handle_render_cairo (tablet->handle, cairo_context);
+ print_button_labels (cairo_context, tablet);
+ cairo_destroy (cairo_context);
+}
+
+static gboolean
+on_timer_cb (Tablet *tablet)
+{
+ GtkAllocation allocation;
+ GdkRectangle rect;
+ int num_buttons;
+
+ tablet->active_button++;
+ num_buttons = libwacom_get_num_buttons (tablet->device);
+ if (tablet->active_button >= 'A' + num_buttons)
+ tablet->active_button = 'A';
+ update_tablet (tablet);
+ gtk_widget_get_allocation (GTK_WIDGET(tablet->widget), &allocation);
+ gdk_window_invalidate_rect(gtk_widget_get_window (tablet->widget), &allocation, FALSE);
+
+ return TRUE;
+}
+
+static gboolean
+on_delete_cb (GtkWidget *widget, GdkEvent *event, Tablet *tablet)
+{
+ gtk_main_quit ();
+
+ return TRUE;
+}
+
+int
+main (int argc, char **argv)
+{
+ GOptionContext *context;
+ RsvgHandle *handle;
+ RsvgDimensionData dimensions;
+ GError *error;
+ WacomDeviceDatabase *db;
+ WacomDevice *device;
+ Tablet *tablet;
+ GtkWidget *widget;
+ GdkWindow *gdk_win;
+ GdkColor black;
+ char *tabletname;
+ const char *filename;
+ GOptionEntry
+ options[] = {
+ {"tablet", 't', 0, G_OPTION_ARG_STRING, &tabletname, "Name of the tablet to show", "<string>"},
+ {NULL}
+ };
+
+ handle = NULL;
+ error = NULL;
+ tabletname = NULL;
+
+ context = g_option_context_new ("- libwacom tablet viewer");
+ g_option_context_add_main_entries (context, options, NULL);
+ g_option_context_add_group (context, gtk_get_option_group (TRUE));
+ g_option_context_set_help_enabled (context, TRUE);
+ g_option_context_parse (context, &argc, &argv, NULL);
+ g_option_context_free (context);
+
+ gtk_init (&argc, &argv);
+ if (tabletname == NULL) {
+ g_warning ("No tablet name provided, exiting");
+ return 1;
+ }
+ db = libwacom_database_new_for_path(TOPSRCDIR"/data");
+ if (!db) {
+ g_warning ("Failed to load libwacom database, exiting");
+ return 1;
+ }
+ device = libwacom_new_from_name(db, tabletname, NULL);
+ if (!device) {
+ g_warning ("Device '%s' not found in libwacom database, exiting", tabletname);
+ return 1;
+ }
+
+ filename = libwacom_get_image_filename(device);
+ if (filename == NULL) {
+ g_warning ("Device '%s' has no image available, exiting", tabletname);
+ return 1;
+ }
+ handle = rsvg_handle_new_from_file (filename, &error);
+ if (error || handle == NULL)
+ return 1;
+ rsvg_handle_get_dimensions (handle, &dimensions);
+ g_object_unref (handle);
+
+ tablet = g_new0 (Tablet, 1);
+ tablet->device = device;
+ tablet->area.width = dimensions.width;
+ tablet->area.height = dimensions.height;
+ tablet->handle = NULL;
+ tablet->active_button = 'A';
+ tablet->num_buttons = libwacom_get_num_buttons (device);
+ tablet->widget = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_widget_set_app_paintable (tablet->widget, TRUE);
+ gtk_widget_realize (tablet->widget);
+ gdk_win = gtk_widget_get_window (tablet->widget);
+ gdk_color_parse (BACK_COLOR, &black);
+ gdk_window_set_background (gdk_win, &black);
+ gtk_window_set_default_size (GTK_WINDOW (tablet->widget), 800, 600);
+
+ g_signal_connect (tablet->widget, "expose-event", G_CALLBACK(on_expose_cb), tablet);
+ g_signal_connect (tablet->widget, "delete-event", G_CALLBACK(on_delete_cb), tablet);
+ tablet->timeout = g_timeout_add(500 /* ms */, (GSourceFunc) on_timer_cb, tablet);
+
+ gtk_widget_show (tablet->widget);
+
+ gtk_main();
+
+ libwacom_destroy(device);
+ libwacom_database_destroy(db);
+
+ return 0;
+}
--
1.7.1
------------------------------------------------------------------------------
The Windows 8 Center - In partnership with Sourceforge
Your idea - your app - 30 days.
Get started!
http://windows8center.sourceforge.net/
what-html-developers-need-to-know-about-coding-windows-8-metro-style-apps/
_______________________________________________
Linuxwacom-devel mailing list
Linuxwacom-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linuxwacom-devel