>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

Reply via email to