[gnome-settings-daemon] Adds Wacom OSD window from upstream bug #679062

ofourdan ofourdan at fedoraproject.org
Fri Oct 5 16:53:13 UTC 2012


commit 4f01fdffa3037fbebde3667f4b04eff7e520ff77
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Fri Oct 5 18:30:35 2012 +0200

    Adds Wacom OSD window from upstream bug #679062

 0001-wacom-implement-OSD-help-window.patch | 1697 ++++++++++++++++++++++++++++
 gnome-settings-daemon.spec                 |   11 +-
 2 files changed, 1706 insertions(+), 2 deletions(-)
---
diff --git a/0001-wacom-implement-OSD-help-window.patch b/0001-wacom-implement-OSD-help-window.patch
new file mode 100644
index 0000000..4b8feea
--- /dev/null
+++ b/0001-wacom-implement-OSD-help-window.patch
@@ -0,0 +1,1697 @@
+From d86992c2ea731194dbf9bb288fe1d5d78114f478 Mon Sep 17 00:00:00 2001
+From: Olivier Fourdan <ofourdan at redhat.com>
+Date: Tue, 3 Jul 2012 09:39:42 +0200
+Subject: [PATCH] wacom: implement OSD help window
+
+The purpose of that OSD window is to give users a simple way
+to display the current pad button functions.
+
+The on-screen buttons also show when a pad button is pressed
+so that users can visually check the matching button/function.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=679062
+---
+ data/gsd-enums.h                     |    3 +-
+ plugins/wacom/Makefile.am            |   37 +-
+ plugins/wacom/gsd-wacom-device.c     |   51 +-
+ plugins/wacom/gsd-wacom-device.h     |   12 +
+ plugins/wacom/gsd-wacom-manager.c    |  116 ++++
+ plugins/wacom/gsd-wacom-osd-window.c | 1026 ++++++++++++++++++++++++++++++++++
+ plugins/wacom/gsd-wacom-osd-window.h |   62 ++
+ plugins/wacom/test-osd-window.c      |  131 +++++
+ 8 files changed, 1427 insertions(+), 11 deletions(-)
+ create mode 100644 plugins/wacom/gsd-wacom-osd-window.c
+ create mode 100644 plugins/wacom/gsd-wacom-osd-window.h
+ create mode 100644 plugins/wacom/test-osd-window.c
+
+diff --git a/data/gsd-enums.h b/data/gsd-enums.h
+index 6c4edd4..6b153fd 100644
+--- a/data/gsd-enums.h
++++ b/data/gsd-enums.h
+@@ -94,7 +94,8 @@ typedef enum
+ {
+   GSD_WACOM_ACTION_TYPE_NONE,
+   GSD_WACOM_ACTION_TYPE_CUSTOM,
+-  GSD_WACOM_ACTION_TYPE_SWITCH_MONITOR
++  GSD_WACOM_ACTION_TYPE_SWITCH_MONITOR,
++  GSD_WACOM_ACTION_TYPE_HELP
+ } GsdWacomActionType;
+ 
+ typedef enum
+diff --git a/plugins/wacom/Makefile.am b/plugins/wacom/Makefile.am
+index 662388b..7a5dab7 100644
+--- a/plugins/wacom/Makefile.am
++++ b/plugins/wacom/Makefile.am
+@@ -7,6 +7,8 @@ libgsdwacom_la_SOURCES =	\
+ 	gsd-wacom-plugin.c	\
+ 	gsd-wacom-manager.h	\
+ 	gsd-wacom-manager.c	\
++	gsd-wacom-osd-window.h	\
++	gsd-wacom-osd-window.c	\
+ 	gsd-wacom-device.c	\
+ 	gsd-wacom-device.h
+ 
+@@ -60,12 +62,14 @@ endif
+ 
+ EXTRA_DIST += org.gnome.settings-daemon.plugins.wacom.policy.in.in
+ 
+-libexec_PROGRAMS += gsd-test-wacom gsd-list-wacom
++libexec_PROGRAMS += gsd-test-wacom gsd-list-wacom gsd-test-wacom-osd
+ 
+ gsd_test_wacom_SOURCES =	\
+ 	test-wacom.c		\
+ 	gsd-wacom-manager.c	\
+ 	gsd-wacom-manager.h	\
++	gsd-wacom-osd-window.h	\
++	gsd-wacom-osd-window.c	\
+ 	gsd-wacom-device.c	\
+ 	gsd-wacom-device.h
+ 
+@@ -123,6 +127,37 @@ gsd_list_wacom_LDADD =						\
+ 	$(WACOM_LIBS)						\
+ 	-lm
+ 
++gsd_test_wacom_osd_SOURCES =					\
++	test-osd-window.c					\
++	gsd-wacom-osd-window.h					\
++	gsd-wacom-osd-window.c					\
++	gsd-wacom-device.c					\
++	gsd-wacom-device.h
++
++gsd_test_wacom_osd_CPPFLAGS = \
++	-I$(top_srcdir)/data/					\
++	-I$(top_srcdir)/gnome-settings-daemon			\
++	-I$(top_srcdir)/plugins/common				\
++	-DBINDIR=\"$(bindir)\"					\
++	-DPIXMAPDIR=\""$(pkgdatadir)"\"				\
++	-DGTKBUILDERDIR=\""$(pkgdatadir)"\"			\
++	-DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\"	\
++	-DLIBEXECDIR=\""$(libexecdir)"\"			\
++	$(AM_CPPFLAGS)
++
++gsd_test_wacom_osd_CFLAGS =					\
++	$(SETTINGS_PLUGIN_CFLAGS)				\
++	$(WACOM_CFLAGS)						\
++	$(AM_CFLAGS)
++
++gsd_test_wacom_osd_LDADD = \
++	$(top_builddir)/gnome-settings-daemon/libgsd.la		\
++	$(top_builddir)/plugins/common/libcommon.la		\
++	$(SETTINGS_DAEMON_LIBS)					\
++	$(SETTINGS_PLUGIN_LIBS)					\
++	$(WACOM_LIBS)						\
++	-lm
++
+ plugin_in_files = wacom.gnome-settings-plugin.in
+ 
+ plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin)
+diff --git a/plugins/wacom/gsd-wacom-device.c b/plugins/wacom/gsd-wacom-device.c
+index 2f52d2a..22ff967 100644
+--- a/plugins/wacom/gsd-wacom-device.c
++++ b/plugins/wacom/gsd-wacom-device.c
+@@ -255,6 +255,7 @@ gsd_wacom_tablet_button_new (const char               *name,
+ 			     const char               *id,
+ 			     const char               *settings_path,
+ 			     GsdWacomTabletButtonType  type,
++			     GsdWacomTabletButtonPos   pos,
+ 			     int                       group_id,
+ 			     int                       idx)
+ {
+@@ -273,6 +274,7 @@ gsd_wacom_tablet_button_new (const char               *name,
+ 	ret->group_id = group_id;
+ 	ret->idx = idx;
+ 	ret->type = type;
++	ret->pos = pos;
+ 
+ 	return ret;
+ }
+@@ -956,17 +958,31 @@ add_stylus_to_device (GsdWacomDevice *device,
+ }
+ 
+ int
+-gsd_wacom_device_set_next_mode (GsdWacomDevice *device,
+-				int             group_id)
++gsd_wacom_device_get_current_mode (GsdWacomDevice *device,
++				   int             group_id)
+ {
+ 	int current_idx;
+-	int num_modes;
+ 
+ 	g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), -1);
+ 	current_idx = GPOINTER_TO_INT (g_hash_table_lookup (device->priv->modes, GINT_TO_POINTER(group_id)));
+ 	/* That means that the mode doesn't exist, see gsd_wacom_device_add_modes() */
+ 	g_return_val_if_fail (current_idx != 0, -1);
+ 
++	return current_idx;
++}
++
++
++int
++gsd_wacom_device_set_next_mode (GsdWacomDevice *device,
++				int             group_id)
++{
++	int current_idx;
++	int num_modes;
++
++	current_idx = gsd_wacom_device_get_current_mode (device, group_id);
++	/* gsd_wacom_device_get_current_mode() returns -1 when the mode doesn't exist */
++	g_return_val_if_fail (current_idx > 0, -1);
++
+ 	current_idx++;
+ 
+ 	num_modes = GPOINTER_TO_INT (g_hash_table_lookup (device->priv->num_modes, GINT_TO_POINTER(group_id)));
+@@ -1011,7 +1027,7 @@ gsd_wacom_device_add_ring_modes (WacomDevice      *wacom_device,
+ 		for (i = 1; i <= num_modes; i++) {
+ 			name = g_strdup_printf (_("Left Ring Mode #%d"), i);
+ 			id = g_strdup_printf ("left-ring-mode-%d", i);
+-			l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_ELEVATOR, flags_to_group (WACOM_BUTTON_RING_MODESWITCH), i - 1));
++			l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_ELEVATOR, WACOM_TABLET_BUTTON_POS_LEFT, flags_to_group (WACOM_BUTTON_RING_MODESWITCH), i - 1));
+ 			g_free (name);
+ 			g_free (id);
+ 		}
+@@ -1020,7 +1036,7 @@ gsd_wacom_device_add_ring_modes (WacomDevice      *wacom_device,
+ 		for (i = 1; i <= num_modes; i++) {
+ 			name = g_strdup_printf (_("Right Ring Mode #%d"), i);
+ 			id = g_strdup_printf ("right-ring-mode-%d", i);
+-			l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_ELEVATOR, flags_to_group (WACOM_BUTTON_RING2_MODESWITCH), i - 1));
++			l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_ELEVATOR, WACOM_TABLET_BUTTON_POS_RIGHT, flags_to_group (WACOM_BUTTON_RING2_MODESWITCH), i - 1));
+ 			g_free (name);
+ 			g_free (id);
+ 		}
+@@ -1050,7 +1066,7 @@ gsd_wacom_device_add_strip_modes (WacomDevice      *wacom_device,
+ 		for (i = 1; i <= num_modes; i++) {
+ 			name = g_strdup_printf (_("Left Touchstrip Mode #%d"), i);
+ 			id = g_strdup_printf ("left-strip-mode-%d", i);
+-			l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_ELEVATOR, flags_to_group (WACOM_BUTTON_TOUCHSTRIP_MODESWITCH), i - 1));
++			l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_ELEVATOR, WACOM_TABLET_BUTTON_POS_LEFT, flags_to_group (WACOM_BUTTON_TOUCHSTRIP_MODESWITCH), i - 1));
+ 			g_free (name);
+ 			g_free (id);
+ 		}
+@@ -1059,7 +1075,7 @@ gsd_wacom_device_add_strip_modes (WacomDevice      *wacom_device,
+ 		for (i = 1; i <= num_modes; i++) {
+ 			name = g_strdup_printf (_("Right Touchstrip Mode #%d"), i);
+ 			id = g_strdup_printf ("right-strip-mode-%d", i);
+-			l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_ELEVATOR, flags_to_group (WACOM_BUTTON_TOUCHSTRIP2_MODESWITCH), i - 1));
++			l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_ELEVATOR, WACOM_TABLET_BUTTON_POS_RIGHT, flags_to_group (WACOM_BUTTON_TOUCHSTRIP2_MODESWITCH), i - 1));
+ 			g_free (name);
+ 			g_free (id);
+ 		}
+@@ -1089,6 +1105,23 @@ gsd_wacom_device_modeswitch_name (WacomButtonFlags flags,
+ 	return g_strdup_printf (_("Mode Switch #%d"), button_num);
+ }
+ 
++static GsdWacomTabletButtonType
++gsd_wacom_device_button_pos (WacomButtonFlags flags)
++{
++	if (flags & WACOM_BUTTON_POSITION_LEFT)
++		return WACOM_TABLET_BUTTON_POS_LEFT;
++	else if (flags & WACOM_BUTTON_POSITION_RIGHT)
++		return WACOM_TABLET_BUTTON_POS_RIGHT;
++	else if (flags & WACOM_BUTTON_POSITION_TOP)
++		return WACOM_TABLET_BUTTON_POS_TOP;
++	else if (flags & WACOM_BUTTON_POSITION_BOTTOM)
++		return WACOM_TABLET_BUTTON_POS_BOTTOM;
++
++	g_warning ("Unhandled button position");
++
++	return WACOM_TABLET_BUTTON_POS_UNDEF;
++}
++
+ static GList *
+ gsd_wacom_device_add_buttons_dir (WacomDevice      *wacom_device,
+ 				  const char       *settings_path,
+@@ -1115,7 +1148,7 @@ gsd_wacom_device_add_buttons_dir (WacomDevice      *wacom_device,
+ 
+ 		name = g_strdup_printf (button_str, button_num++);
+ 		id = g_strdup_printf ("%s%c", button_str_id, i);
+-		l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_NORMAL, flags_to_group (flags), -1));
++		l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_NORMAL, gsd_wacom_device_button_pos (flags), flags_to_group (flags), -1));
+ 		g_free (name);
+ 		g_free (id);
+ 	}
+@@ -1134,7 +1167,7 @@ gsd_wacom_device_add_buttons_dir (WacomDevice      *wacom_device,
+ 
+ 		name = gsd_wacom_device_modeswitch_name (flags, button_num++);
+ 		id = g_strdup_printf ("%s%c", button_str_id, i);
+-		l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_HARDCODED, flags_to_group (flags), -1));
++		l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_HARDCODED, gsd_wacom_device_button_pos (flags), flags_to_group (flags), -1));
+ 		g_free (name);
+ 		g_free (id);
+ 
+diff --git a/plugins/wacom/gsd-wacom-device.h b/plugins/wacom/gsd-wacom-device.h
+index 24e6e06..b4ad07f 100644
+--- a/plugins/wacom/gsd-wacom-device.h
++++ b/plugins/wacom/gsd-wacom-device.h
+@@ -97,6 +97,15 @@ typedef enum {
+ 	WACOM_TABLET_BUTTON_TYPE_HARDCODED
+ } GsdWacomTabletButtonType;
+ 
++/* Tablet Buttons Position*/
++typedef enum {
++	WACOM_TABLET_BUTTON_POS_UNDEF = 0,
++	WACOM_TABLET_BUTTON_POS_LEFT,
++	WACOM_TABLET_BUTTON_POS_RIGHT,
++	WACOM_TABLET_BUTTON_POS_TOP,
++	WACOM_TABLET_BUTTON_POS_BOTTOM
++} GsdWacomTabletButtonPos;
++
+ #define MAX_GROUP_ID 4
+ 
+ typedef struct
+@@ -105,6 +114,7 @@ typedef struct
+ 	char                     *id;
+ 	GSettings                *settings;
+ 	GsdWacomTabletButtonType  type;
++	GsdWacomTabletButtonPos   pos;
+ 	int                       group_id, idx;
+ } GsdWacomTabletButton;
+ 
+@@ -158,6 +168,8 @@ GList          * gsd_wacom_device_get_buttons       (GsdWacomDevice *device);
+ GsdWacomTabletButton *gsd_wacom_device_get_button   (GsdWacomDevice   *device,
+ 						     int               button,
+ 						     GtkDirectionType *dir);
++int gsd_wacom_device_get_current_mode               (GsdWacomDevice *device,
++						     int             group_id);
+ int gsd_wacom_device_set_next_mode                  (GsdWacomDevice *device,
+ 						     int             group_id);
+ GsdWacomRotation gsd_wacom_device_rotation_name_to_type (const char *rotation);
+diff --git a/plugins/wacom/gsd-wacom-manager.c b/plugins/wacom/gsd-wacom-manager.c
+index 8890988..1fd0167 100644
+--- a/plugins/wacom/gsd-wacom-manager.c
++++ b/plugins/wacom/gsd-wacom-manager.c
+@@ -47,6 +47,7 @@
+ #include "gnome-settings-profile.h"
+ #include "gsd-wacom-manager.h"
+ #include "gsd-wacom-device.h"
++#include "gsd-wacom-osd-window.h"
+ 
+ #define GSD_WACOM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_WACOM_MANAGER, GsdWacomManagerPrivate))
+ 
+@@ -83,6 +84,9 @@ struct GsdWacomManagerPrivate
+         /* button capture */
+         GSList *screens;
+         int      opcode;
++
++        /* Help OSD window */
++        GtkWidget *osd_window;
+ };
+ 
+ static void     gsd_wacom_manager_class_init  (GsdWacomManagerClass *klass);
+@@ -890,6 +894,89 @@ last_stylus_changed (GsdWacomDevice  *device,
+ }
+ 
+ static void
++osd_window_hide (GsdWacomManager *manager)
++{
++	g_return_if_fail (manager != NULL);
++
++	if (manager->priv->osd_window) {
++		gtk_widget_destroy (manager->priv->osd_window);
++		manager->priv->osd_window = NULL;
++	}
++}
++
++static gboolean
++osd_window_on_key_release_event (GtkWidget   *widget,
++                                 GdkEventKey *event,
++                                 GsdWacomManager *manager)
++{
++	if (event->type != GDK_KEY_RELEASE)
++		return FALSE;
++	if (event->keyval != GDK_KEY_Escape)
++		return FALSE;
++
++	osd_window_hide (manager);
++
++	return FALSE;
++}
++
++static gboolean
++osd_window_on_focus_out_event (GtkWidget *widget,
++                               GdkEvent  *event,
++                               GsdWacomManager *manager)
++{
++	/* If the OSD window looses focus, hide it */
++	osd_window_hide (manager);
++
++	return FALSE;
++}
++
++static gboolean
++osd_window_toggle_visibility (GsdWacomManager *manager,
++                              GsdWacomDevice  *device)
++{
++	GtkWidget *widget;
++	gchar *message, *name;
++
++	if (manager->priv->osd_window) {
++		osd_window_hide (manager);
++		return FALSE;
++	}
++
++	name = g_markup_printf_escaped ("%s", gsd_wacom_device_get_name (device));
++	message = g_strdup_printf ("<big><b>%s</b></big>", name);
++	widget = gsd_wacom_osd_window_new (device, message);
++	g_free (message);
++	g_free (name);
++
++	/* Connect some signals to the OSD window */
++	g_signal_connect (widget, "key-release-event",
++			  G_CALLBACK(osd_window_on_key_release_event), manager);
++	g_signal_connect (widget, "focus-out-event",
++			  G_CALLBACK(osd_window_on_focus_out_event), manager);
++	g_object_add_weak_pointer (G_OBJECT (widget), (gpointer *) &manager->priv->osd_window);
++
++	gtk_window_present (GTK_WINDOW(widget));
++	manager->priv->osd_window = widget;
++
++	return TRUE;
++}
++
++static gboolean
++osd_window_update_viewable (GsdWacomManager *manager,
++                            gchar           *button_id,
++                            XIEvent         *xiev)
++{
++	if (manager->priv->osd_window == NULL)
++		return FALSE;
++
++	gsd_wacom_osd_window_set_active (GSD_WACOM_OSD_WINDOW (manager->priv->osd_window),
++	                                 button_id,
++	                                 xiev->evtype == XI_ButtonPress);
++	return TRUE;
++
++}
++
++static void
+ device_added_cb (GdkDeviceManager *device_manager,
+                  GdkDevice        *gdk_device,
+                  GsdWacomManager  *manager)
+@@ -1146,6 +1233,7 @@ filter_button_events (XEvent          *xevent,
+ 	int                  button;
+ 	GsdWacomTabletButton *wbutton;
+ 	GtkDirectionType      dir;
++	gboolean emulate;
+ 
+         /* verify we have a key event */
+ 	if (xevent->type != GenericEvent)
+@@ -1167,6 +1255,11 @@ filter_button_events (XEvent          *xevent,
+ 	if (gsd_wacom_device_get_device_type (device) != WACOM_TYPE_PAD)
+ 		return GDK_FILTER_CONTINUE;
+ 
++	if ((manager->priv->osd_window != NULL) &&
++	    (device != gsd_wacom_osd_window_get_device(GSD_WACOM_OSD_WINDOW(manager->priv->osd_window))))
++		/* This is a button event from another device while showing OSD window */
++		osd_window_hide (manager);
++
+ 	button = xev->detail;
+ 
+ 	wbutton = gsd_wacom_device_get_button (device, button, &dir);
+@@ -1188,6 +1281,10 @@ filter_button_events (XEvent          *xevent,
+ 	if (wbutton->type == WACOM_TABLET_BUTTON_TYPE_HARDCODED) {
+ 		int new_mode;
+ 
++		/* Update OSD window if shown */
++		if (osd_window_update_viewable (manager, wbutton->id, xiev))
++			return GDK_FILTER_REMOVE;
++
+ 		/* We switch modes on key release */
+ 		if (xiev->evtype == XI_ButtonRelease)
+ 			return GDK_FILTER_REMOVE;
+@@ -1197,10 +1294,23 @@ filter_button_events (XEvent          *xevent,
+ 		return GDK_FILTER_REMOVE;
+ 	}
+ 
++	/* Update OSD window if shown */
++	emulate = osd_window_update_viewable (manager, wbutton->id, xiev);
++
+ 	/* Nothing to do */
+ 	if (g_settings_get_enum (wbutton->settings, KEY_ACTION_TYPE) == GSD_WACOM_ACTION_TYPE_NONE)
+ 		return GDK_FILTER_REMOVE;
+ 
++	/* Show OSD window when requested */
++	if (g_settings_get_enum (wbutton->settings, KEY_ACTION_TYPE) == GSD_WACOM_ACTION_TYPE_HELP) {
++		if (xiev->evtype == XI_ButtonRelease)
++			osd_window_toggle_visibility (manager, device);
++		return GDK_FILTER_REMOVE;
++	}
++
++	if (emulate)
++		return GDK_FILTER_REMOVE;
++
+ 	/* Switch monitor */
+ 	if (g_settings_get_enum (wbutton->settings, KEY_ACTION_TYPE) == GSD_WACOM_ACTION_TYPE_SWITCH_MONITOR) {
+ 		if (xiev->evtype == XI_ButtonRelease)
+@@ -1386,6 +1496,12 @@ gsd_wacom_manager_stop (GsdWacomManager *manager)
+                 p->device_manager = NULL;
+         }
+ 
++
++        if (p->osd_window) {
++                gtk_widget_destroy (p->osd_window);
++                p->osd_window = NULL;
++        }
++
+         for (ls = p->screens; ls != NULL; ls = ls->next) {
+                 gdk_window_remove_filter (gdk_screen_get_root_window (ls->data),
+                                           (GdkFilterFunc) filter_button_events,
+diff --git a/plugins/wacom/gsd-wacom-osd-window.c b/plugins/wacom/gsd-wacom-osd-window.c
+new file mode 100644
+index 0000000..3f0577f
+--- /dev/null
++++ b/plugins/wacom/gsd-wacom-osd-window.c
+@@ -0,0 +1,1026 @@
++/*
++ * Copyright (C) 2012 Red Hat, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program 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 General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * Author: Olivier Fourdan <ofourdan at redhat.com>
++ *
++ */
++
++#include "config.h"
++
++#include <stdlib.h>
++#include <stdio.h>
++#include <glib/gi18n.h>
++#include <gtk/gtk.h>
++#include <cairo.h>
++
++#include "gsd-wacom-osd-window.h"
++#include "gsd-wacom-device.h"
++#include "gsd-enums.h"
++
++#define ROTATION_KEY                "rotation"
++#define ACTION_TYPE_KEY             "action-type"
++#define CUSTOM_ACTION_KEY           "custom-action"
++#define CUSTOM_ELEVATOR_ACTION_KEY  "custom-elevator-action"
++
++#define BUTTON_ASPECT		0.8
++#define BUTTON_MAX_WIDTH	120
++#define BUTTON_SPACING		30
++#define LABEL_SPACING		20
++#define LINE_WIDTH		5
++#define WINDOW_OPACITY		0.8
++
++static void
++draw_rounded_rectangle (cairo_t         *cr,
++			double           x,
++			double           y,
++			double           width,
++			double           height)
++{
++	double radius = height / 10.0;
++	double degrees = G_PI / 180.0;
++
++	cairo_new_sub_path (cr);
++	cairo_arc (cr, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees);
++	cairo_arc (cr, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees);
++	cairo_arc (cr, x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees);
++	cairo_arc (cr, x + radius, y + radius, radius, 180 * degrees, 270 * degrees);
++	cairo_close_path (cr);
++}
++
++static void
++draw_circle (cairo_t         *cr,
++	     double           x,
++	     double           y,
++	     double           radius)
++{
++	cairo_new_sub_path (cr);
++	cairo_arc (cr, x, y, radius, 0, 2 * G_PI);
++	cairo_close_path (cr);
++}
++
++#define GSD_TYPE_WACOM_OSD_BUTTON         (gsd_wacom_osd_button_get_type ())
++#define GSD_WACOM_OSD_BUTTON(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_WACOM_OSD_BUTTON, GsdWacomOSDButton))
++#define GSD_WACOM_OSD_BUTTON_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_WACOM_OSD_BUTTON, GsdWacomOSDButtonClass))
++#define GSD_IS_WACOM_OSD_BUTTON(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_WACOM_OSD_BUTTON))
++#define GSD_IS_WACOM_OSD_BUTTON_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_WACOM_OSD_BUTTON))
++#define GSD_WACOM_OSD_BUTTON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_WACOM_OSD_BUTTON, GsdWacomOSDButtonClass))
++
++typedef struct GsdWacomOSDButtonPrivate GsdWacomOSDButtonPrivate;
++
++typedef struct {
++        GObject                   parent;
++        GsdWacomOSDButtonPrivate *priv;
++} GsdWacomOSDButton;
++
++typedef struct {
++        GObjectClass              parent_class;
++} GsdWacomOSDButtonClass;
++
++GType                     gsd_wacom_osd_button_get_type        (void) G_GNUC_CONST;
++
++enum {
++	PROP_OSD_BUTTON_0,
++	PROP_OSD_BUTTON_ID,
++	PROP_OSD_BUTTON_LABEL,
++	PROP_OSD_BUTTON_ACTIVE
++};
++
++#define GSD_WACOM_OSD_BUTTON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
++					     GSD_TYPE_WACOM_OSD_BUTTON, \
++					     GsdWacomOSDButtonPrivate))
++
++struct GsdWacomOSDButtonPrivate {
++	char                     *id;
++	char                     *label;
++	GdkRectangle              area;
++	GsdWacomTabletButtonType  type;
++	GsdWacomTabletButtonPos   position;
++	gboolean                  active;
++};
++
++static void     gsd_wacom_osd_button_class_init  (GsdWacomOSDButtonClass *klass);
++static void     gsd_wacom_osd_button_init        (GsdWacomOSDButton      *osd_button);
++static void     gsd_wacom_osd_button_finalize    (GObject                *object);
++
++G_DEFINE_TYPE (GsdWacomOSDButton, gsd_wacom_osd_button, G_TYPE_OBJECT)
++
++static void
++gsd_wacom_osd_button_set_id (GsdWacomOSDButton *osd_button,
++			     const gchar       *str)
++{
++	g_return_if_fail (GSD_IS_WACOM_OSD_BUTTON (osd_button));
++	g_return_if_fail (str != NULL);
++
++	g_free (osd_button->priv->id);
++	osd_button->priv->id = g_strdup (str);
++}
++
++static void
++gsd_wacom_osd_button_set_label (GsdWacomOSDButton *osd_button,
++				const gchar       *str)
++{
++	g_return_if_fail (GSD_IS_WACOM_OSD_BUTTON (osd_button));
++
++	g_free (osd_button->priv->label);
++	osd_button->priv->label = g_strdup (str ? str : "");
++}
++
++static void
++gsd_wacom_osd_button_set_button_type (GsdWacomOSDButton        *osd_button,
++				      GsdWacomTabletButtonType  type)
++{
++	g_return_if_fail (GSD_IS_WACOM_OSD_BUTTON (osd_button));
++
++	osd_button->priv->type = type;
++}
++
++static void
++gsd_wacom_osd_button_set_position (GsdWacomOSDButton        *osd_button,
++				   GsdWacomTabletButtonPos   position)
++{
++	g_return_if_fail (GSD_IS_WACOM_OSD_BUTTON (osd_button));
++
++	osd_button->priv->position = position;
++}
++
++static void
++gsd_wacom_osd_button_set_active (GsdWacomOSDButton *osd_button,
++				 gboolean           active)
++{
++	g_return_if_fail (GSD_IS_WACOM_OSD_BUTTON (osd_button));
++
++	osd_button->priv->active = active;
++}
++
++static void
++gsd_wacom_osd_button_move (GsdWacomOSDButton *osd_button,
++			   gint               x,
++			   gint               y)
++{
++	g_return_if_fail (GSD_IS_WACOM_OSD_BUTTON (osd_button));
++
++	osd_button->priv->area.x = x;
++	osd_button->priv->area.y = y;
++}
++
++static void
++gsd_wacom_osd_button_resize (GsdWacomOSDButton *osd_button,
++			     gint               width,
++			     gint               height)
++{
++	g_return_if_fail (GSD_IS_WACOM_OSD_BUTTON (osd_button));
++
++	osd_button->priv->area.width = width;
++	osd_button->priv->area.height = height;
++}
++
++static GsdWacomOSDButton *
++gsd_wacom_osd_button_new (gint          x,
++			  gint          y,
++			  gint          width,
++			  gint          height)
++{
++	GsdWacomOSDButton *osd_button;
++
++	osd_button = GSD_WACOM_OSD_BUTTON (g_object_new (GSD_TYPE_WACOM_OSD_BUTTON, NULL));
++	osd_button->priv->area.x = x;
++	osd_button->priv->area.y = y;
++	osd_button->priv->area.width = width;
++	osd_button->priv->area.height = height;
++
++	return osd_button;
++}
++
++static void
++gsd_wacom_osd_button_set_property (GObject        *object,
++				   guint           prop_id,
++				   const GValue   *value,
++				   GParamSpec     *pspec)
++{
++	GsdWacomOSDButton *osd_button;
++
++	osd_button = GSD_WACOM_OSD_BUTTON (object);
++
++	switch (prop_id) {
++	case PROP_OSD_BUTTON_ID:
++		gsd_wacom_osd_button_set_id (osd_button, g_value_get_string (value));
++		break;
++	case PROP_OSD_BUTTON_LABEL:
++		gsd_wacom_osd_button_set_label (osd_button, g_value_get_string (value));
++		break;
++	case PROP_OSD_BUTTON_ACTIVE:
++		gsd_wacom_osd_button_set_active (osd_button, g_value_get_boolean (value));
++		break;
++	default:
++		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++		break;
++	}
++}
++
++static void
++gsd_wacom_osd_button_get_property (GObject        *object,
++				   guint           prop_id,
++				   GValue         *value,
++				   GParamSpec     *pspec)
++{
++	GsdWacomOSDButton *osd_button;
++
++	osd_button = GSD_WACOM_OSD_BUTTON (object);
++
++	switch (prop_id) {
++	case PROP_OSD_BUTTON_ID:
++		g_value_set_string (value, osd_button->priv->id);
++		break;
++	case PROP_OSD_BUTTON_LABEL:
++		g_value_set_string (value, osd_button->priv->label);
++		break;
++	case PROP_OSD_BUTTON_ACTIVE:
++		g_value_set_boolean (value, osd_button->priv->active);
++		break;
++	default:
++		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++		break;
++	}
++}
++
++static void
++gsd_wacom_osd_button_class_init (GsdWacomOSDButtonClass *klass)
++{
++	GObjectClass *object_class = G_OBJECT_CLASS (klass);
++
++	object_class->set_property = gsd_wacom_osd_button_set_property;
++	object_class->get_property = gsd_wacom_osd_button_get_property;
++	object_class->finalize = gsd_wacom_osd_button_finalize;
++
++	g_object_class_install_property (object_class,
++	                                 PROP_OSD_BUTTON_ID,
++	                                 g_param_spec_string ("id",
++	                                                      "Button Id",
++	                                                      "The Wacom Button ID",
++	                                                      "",
++	                                                      G_PARAM_READWRITE));
++	g_object_class_install_property (object_class,
++	                                 PROP_OSD_BUTTON_LABEL,
++	                                 g_param_spec_string ("label",
++	                                                      "Label",
++	                                                      "The button label",
++	                                                      "",
++	                                                      G_PARAM_READWRITE));
++	g_object_class_install_property (object_class,
++	                                 PROP_OSD_BUTTON_ACTIVE,
++	                                 g_param_spec_boolean ("active",
++	                                                       "Active",
++	                                                       "Whether the button is active",
++	                                                       FALSE,
++	                                                       G_PARAM_READWRITE));
++
++	g_type_class_add_private (klass, sizeof (GsdWacomOSDButtonPrivate));
++}
++
++static void
++gsd_wacom_osd_button_init (GsdWacomOSDButton *osd_button)
++{
++	osd_button->priv = GSD_WACOM_OSD_BUTTON_GET_PRIVATE (osd_button);
++}
++
++static void
++gsd_wacom_osd_button_finalize (GObject *object)
++{
++	GsdWacomOSDButton *osd_button;
++	GsdWacomOSDButtonPrivate *priv;
++
++	g_return_if_fail (object != NULL);
++	g_return_if_fail (GSD_IS_WACOM_OSD_BUTTON (object));
++
++	osd_button = GSD_WACOM_OSD_BUTTON (object);
++
++	g_return_if_fail (osd_button->priv != NULL);
++
++	priv = osd_button->priv;
++
++	g_free (priv->id);
++	priv->id = NULL;
++
++	g_free (priv->label);
++	priv->label = NULL;
++
++	G_OBJECT_CLASS (gsd_wacom_osd_button_parent_class)->finalize (object);
++}
++
++/* Compute the new actual position once rotation is applied */
++static GsdWacomTabletButtonPos
++get_actual_position (GsdWacomTabletButtonPos position,
++		     GsdWacomRotation        rotation)
++{
++	switch (rotation) {
++	case GSD_WACOM_ROTATION_NONE:
++		return position;
++		break;
++	case GSD_WACOM_ROTATION_HALF:
++		if (position == WACOM_TABLET_BUTTON_POS_LEFT)
++			return WACOM_TABLET_BUTTON_POS_RIGHT;
++		if (position == WACOM_TABLET_BUTTON_POS_RIGHT)
++			return WACOM_TABLET_BUTTON_POS_LEFT;
++		if (position == WACOM_TABLET_BUTTON_POS_TOP)
++			return WACOM_TABLET_BUTTON_POS_BOTTOM;
++		if (position == WACOM_TABLET_BUTTON_POS_BOTTOM)
++			return WACOM_TABLET_BUTTON_POS_TOP;
++		break;
++	case GSD_WACOM_ROTATION_CCW:
++		if (position == WACOM_TABLET_BUTTON_POS_LEFT)
++			return WACOM_TABLET_BUTTON_POS_BOTTOM;
++		if (position == WACOM_TABLET_BUTTON_POS_RIGHT)
++			return WACOM_TABLET_BUTTON_POS_TOP;
++		if (position == WACOM_TABLET_BUTTON_POS_TOP)
++			return WACOM_TABLET_BUTTON_POS_LEFT;
++		if (position == WACOM_TABLET_BUTTON_POS_BOTTOM)
++			return WACOM_TABLET_BUTTON_POS_RIGHT;
++		break;
++	case GSD_WACOM_ROTATION_CW:
++		if (position == WACOM_TABLET_BUTTON_POS_LEFT)
++			return WACOM_TABLET_BUTTON_POS_TOP;
++		if (position == WACOM_TABLET_BUTTON_POS_RIGHT)
++			return WACOM_TABLET_BUTTON_POS_BOTTOM;
++		if (position == WACOM_TABLET_BUTTON_POS_TOP)
++			return WACOM_TABLET_BUTTON_POS_RIGHT;
++		if (position == WACOM_TABLET_BUTTON_POS_BOTTOM)
++			return WACOM_TABLET_BUTTON_POS_LEFT;
++		break;
++	default:
++		break;
++	}
++	/* fallback, should not happen */
++	return position;
++}
++
++/* Compute the new actual index once rotation is applied */
++static gint
++get_actual_index (GsdWacomTabletButtonPos position,
++		  GsdWacomRotation        rotation,
++		  gint                    n_items,
++		  gint                    current)
++{
++	g_return_val_if_fail (current < n_items, current);
++
++	switch (rotation) {
++	case GSD_WACOM_ROTATION_NONE:
++			return current;
++			break;
++		case GSD_WACOM_ROTATION_HALF:
++			return n_items - current - 1;
++			break;
++		case GSD_WACOM_ROTATION_CCW:
++			if (position == WACOM_TABLET_BUTTON_POS_LEFT ||
++			    position == WACOM_TABLET_BUTTON_POS_RIGHT)
++				return current;
++			if (position == WACOM_TABLET_BUTTON_POS_TOP ||
++			    position == WACOM_TABLET_BUTTON_POS_BOTTOM)
++				return n_items - current - 1;
++			break;
++		case GSD_WACOM_ROTATION_CW:
++			if (position == WACOM_TABLET_BUTTON_POS_LEFT ||
++			    position == WACOM_TABLET_BUTTON_POS_RIGHT)
++				return n_items - current - 1;
++			if (position == WACOM_TABLET_BUTTON_POS_TOP ||
++			    position == WACOM_TABLET_BUTTON_POS_BOTTOM)
++				return current;
++			break;
++		default:
++			break;
++	}
++	/* fallback, should not happen */
++	return current;
++}
++
++static void
++gsd_wacom_osd_button_draw (GsdWacomOSDButton *osd_button,
++			   GtkStyleContext   *style_context,
++			   PangoContext      *pango_context,
++			   cairo_t           *cr,
++			   GtkAllocation     *allocation,
++			   GsdWacomRotation   rotation)
++{
++	GsdWacomOSDButtonPrivate *priv;
++	PangoLayout              *layout;
++	PangoRectangle            logical_rect;
++	GsdWacomTabletButtonPos   actual_position;
++	double                    lx, ly;
++	char                     *markup;
++
++	g_return_if_fail (GSD_IS_WACOM_OSD_BUTTON(osd_button));
++
++	priv = osd_button->priv;
++
++	cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
++	cairo_set_line_width (cr, LINE_WIDTH);
++
++	actual_position = get_actual_position (priv->position, rotation);
++
++	if (osd_button->priv->type == WACOM_TABLET_BUTTON_TYPE_HARDCODED)
++		draw_circle (cr,
++		             priv->area.x + priv->area.width / 2,
++		             priv->area.y + priv->area.height / 2,
++		             MIN (priv->area.width, priv->area.height) / 2);
++	else
++		draw_rounded_rectangle(cr,
++		                       priv->area.x, priv->area.y,
++		                       priv->area.width, priv->area.height);
++
++	if (osd_button->priv->active)
++		cairo_fill_preserve(cr);
++	cairo_stroke (cr);
++
++	/* Write the label */
++	layout = pango_layout_new (pango_context);
++
++	switch (actual_position) {
++	case WACOM_TABLET_BUTTON_POS_TOP:
++	case WACOM_TABLET_BUTTON_POS_BOTTOM:
++		pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
++		break;
++	case WACOM_TABLET_BUTTON_POS_RIGHT:
++		pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
++		break;
++	default:
++		pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT);
++		break;
++	}
++	markup = g_strdup_printf ("<span foreground=\"white\">%s</span>", priv->label);
++	pango_layout_set_markup (layout, markup, -1);
++	g_free (markup);
++
++	pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
++	switch (actual_position) {
++	case WACOM_TABLET_BUTTON_POS_TOP:
++		lx = priv->area.x + (priv->area.width - logical_rect.width) / 2 + logical_rect.x;
++		ly = priv->area.y + (priv->area.height + LABEL_SPACING + logical_rect.y);
++		break;
++	case WACOM_TABLET_BUTTON_POS_BOTTOM:
++		lx = priv->area.x + (priv->area.width - logical_rect.width) / 2 + logical_rect.x;
++		ly = priv->area.y - (LABEL_SPACING + logical_rect.y + logical_rect.height);
++		break;
++	case WACOM_TABLET_BUTTON_POS_RIGHT:
++		lx = priv->area.x - (LABEL_SPACING + logical_rect.x + logical_rect.width);
++		ly = priv->area.y + (priv->area.height - logical_rect.height) / 2 + logical_rect.y;
++		break;
++	default:
++		lx = priv->area.x + (priv->area.width + LABEL_SPACING + logical_rect.x);
++		ly = priv->area.y + (priv->area.height - logical_rect.height) / 2 + logical_rect.y;
++		break;
++	}
++	gtk_render_layout (style_context, cr, lx, ly, layout);
++
++	g_object_unref (layout);
++}
++
++enum {
++  PROP_OSD_WINDOW_0,
++  PROP_OSD_WINDOW_MESSAGE,
++  PROP_OSD_WINDOW_GSD_WACOM_DEVICE
++};
++
++#define GSD_WACOM_OSD_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
++					     GSD_TYPE_WACOM_OSD_WINDOW, \
++					     GsdWacomOSDWindowPrivate))
++
++struct GsdWacomOSDWindowPrivate
++{
++	char                     *message;
++	GsdWacomDevice           *pad;
++	GsdWacomRotation          rotation;
++	gint                      num_buttons[4]; /* How many button per side */
++	GList                    *buttons;
++};
++
++static void     gsd_wacom_osd_window_class_init  (GsdWacomOSDWindowClass *klass);
++static void     gsd_wacom_osd_window_init        (GsdWacomOSDWindow      *osd_window);
++static void     gsd_wacom_osd_window_finalize    (GObject                *object);
++
++G_DEFINE_TYPE (GsdWacomOSDWindow, gsd_wacom_osd_window, GTK_TYPE_WINDOW)
++
++static void
++gsd_wacom_osd_window_draw_message (GsdWacomOSDWindow   *osd_window,
++				   GtkStyleContext     *style_context,
++				   PangoContext        *pango_context,
++				   cairo_t             *cr,
++				   GtkAllocation       *allocation)
++{
++	PangoRectangle logical_rect;
++	PangoLayout *layout;
++	char *markup;
++	double x;
++	double y;
++
++	layout = pango_layout_new (pango_context);
++	pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
++
++	markup = g_strdup_printf ("<span foreground=\"white\">%s</span>", osd_window->priv->message);
++	pango_layout_set_markup (layout, markup, -1);
++	g_free (markup);
++
++	pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
++	x = (allocation->width - logical_rect.width) / 2 + logical_rect.x;
++	y = (allocation->height - logical_rect.height) / 2 + logical_rect.y;
++
++	gtk_render_layout (style_context, cr, x, y, layout);
++	g_object_unref (layout);
++}
++
++
++static gboolean
++gsd_wacom_osd_window_draw (GtkWidget *widget,
++			   cairo_t   *cr)
++{
++	GsdWacomOSDWindow *osd_window = GSD_WACOM_OSD_WINDOW (widget);
++
++	if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget))) {
++		GtkAllocation        allocation;
++		GtkStyleContext     *style_context;
++		PangoContext        *pango_context;
++		GList               *l;
++
++		style_context = gtk_widget_get_style_context (widget);
++		pango_context = gtk_widget_get_pango_context (widget);
++		gtk_widget_get_allocation(widget, &allocation);
++
++		cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
++		cairo_paint (cr);
++		cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
++
++		/* Draw all buttons */
++		for (l = osd_window->priv->buttons; l != NULL; l = l->next) {
++			GsdWacomOSDButton *osd_button = l->data;
++			gsd_wacom_osd_button_draw (osd_button,
++			                           style_context,
++			                           pango_context,
++			                           cr,
++			                           &allocation,
++			                           osd_window->priv->rotation);
++		}
++
++		/* Draw message */
++		gsd_wacom_osd_window_draw_message (osd_window,
++		                                   style_context,
++		                                   pango_context,
++		                                   cr,
++		                                   &allocation);
++	}
++
++	return FALSE;
++}
++
++static void
++gsd_wacom_osd_window_place_buttons (GsdWacomOSDWindow *osd_window,
++				    GtkAllocation *allocation)
++{
++	gint              buttons[4] = { 0, 0, 0, 0 };
++	GsdWacomRotation  rotation;
++	gint              nw, nh, pw, ph;
++	GList            *l;
++
++	g_return_if_fail (GSD_IS_WACOM_OSD_WINDOW (osd_window));
++
++	rotation = osd_window->priv->rotation;
++	nw = MAX (osd_window->priv->num_buttons[get_actual_position (WACOM_TABLET_BUTTON_POS_TOP,    rotation)],
++	          osd_window->priv->num_buttons[get_actual_position (WACOM_TABLET_BUTTON_POS_BOTTOM, rotation)]) + 3;
++	nh = MAX (osd_window->priv->num_buttons[get_actual_position (WACOM_TABLET_BUTTON_POS_LEFT,   rotation)],
++	          osd_window->priv->num_buttons[get_actual_position (WACOM_TABLET_BUTTON_POS_RIGHT,  rotation)]) + 3;
++
++	pw = MIN (BUTTON_MAX_WIDTH, (allocation->width - (nw + 1) * BUTTON_SPACING) / nw);
++	ph = pw * BUTTON_ASPECT;
++
++	if ((ph + BUTTON_SPACING) * nh > allocation->height) {
++		ph = (allocation->height - (nh + 1) * BUTTON_SPACING) / nh;
++		pw = ph / BUTTON_ASPECT;
++	}
++
++	for (l = osd_window->priv->buttons; l != NULL; l = l->next) {
++		GsdWacomTabletButtonPos  actual_position;
++		GsdWacomOSDButton *osd_button = l->data;
++		gint x, y, index, n_items, current;
++
++		n_items = osd_window->priv->num_buttons[osd_button->priv->position];
++		current = buttons[osd_button->priv->position];
++		index = get_actual_index (osd_button->priv->position, rotation, n_items, current);
++		actual_position = get_actual_position (osd_button->priv->position, rotation);
++
++		gsd_wacom_osd_button_resize (osd_button, pw, ph);
++		switch (actual_position) {
++		case WACOM_TABLET_BUTTON_POS_LEFT:
++			x = BUTTON_SPACING;
++			y = (allocation->height - (n_items * (osd_button->priv->area.height + BUTTON_SPACING) - BUTTON_SPACING)) / 2
++			    + index * (osd_button->priv->area.height + BUTTON_SPACING);
++			break;
++		case WACOM_TABLET_BUTTON_POS_RIGHT:
++			x = allocation->width - (osd_button->priv->area.width + BUTTON_SPACING);
++			y = (allocation->height - (n_items * (osd_button->priv->area.height + BUTTON_SPACING) - BUTTON_SPACING)) / 2
++			    + index * (osd_button->priv->area.height + BUTTON_SPACING);
++			break;
++		case WACOM_TABLET_BUTTON_POS_TOP:
++			x = (allocation->width - (n_items * (osd_button->priv->area.width + BUTTON_SPACING) - BUTTON_SPACING)) / 2
++			    + index * (osd_button->priv->area.width + BUTTON_SPACING);
++			y = BUTTON_SPACING;
++			break;
++		case WACOM_TABLET_BUTTON_POS_BOTTOM:
++			x = (allocation->width - (n_items * (osd_button->priv->area.width + BUTTON_SPACING) - BUTTON_SPACING)) / 2
++			    + index * (osd_button->priv->area.width + BUTTON_SPACING);
++			y = allocation->height - (osd_button->priv->area.height + BUTTON_SPACING);
++			break;
++		case WACOM_TABLET_BUTTON_POS_UNDEF:
++		default:
++			g_assert_not_reached();
++			break;
++		}
++		gsd_wacom_osd_button_move (osd_button, x, y);
++		buttons[osd_button->priv->position]++;
++	}
++}
++
++static gchar *
++get_tablet_button_label (GsdWacomDevice       *device,
++	                 GsdWacomTabletButton *button)
++{
++	gchar *str;
++	guint keyval;
++	GdkModifierType mask;
++
++	g_return_val_if_fail (button, NULL);
++
++	if (button->type == WACOM_TABLET_BUTTON_TYPE_HARDCODED) {
++		gint mode;
++
++		mode = gsd_wacom_device_get_current_mode (device, button->group_id);
++		return g_strdup_printf (_("%s\nCurrent mode %d"), button->name, mode);
++	}
++
++	if (!button->settings)
++		goto out;
++
++	if (button->type == WACOM_TABLET_BUTTON_TYPE_NORMAL) {
++		GsdWacomActionType type;
++		gchar *name;
++
++		type = g_settings_get_enum (button->settings, ACTION_TYPE_KEY);
++		if (type == GSD_WACOM_ACTION_TYPE_NONE)
++			return g_strdup (_("None"));
++
++		if (type == GSD_WACOM_ACTION_TYPE_HELP)
++			return g_strdup (_("Show On-Screen Help"));
++
++		str = g_settings_get_string (button->settings, CUSTOM_ACTION_KEY);
++		if (str == NULL || *str == '\0') {
++			g_free (str);
++			return g_strdup (_("None"));
++		}
++
++		gtk_accelerator_parse (str, &keyval, &mask);
++		g_free (str);
++
++		str = gtk_accelerator_get_label (keyval, mask);
++		name = g_markup_printf_escaped ("Send Keystroke %s", str);
++		g_free (str);
++
++		return name;
++	}
++
++out:
++	return g_strdup (button->name);
++}
++
++/*
++ * Returns the rotation to apply a device to get a represntation relative to
++ * the current rotation of the output.
++ * (This function is _not_ the same as in gsd-wacom-manager.c)
++ */
++static GsdWacomRotation
++display_relative_rotation (GsdWacomRotation device_rotation,
++			   GsdWacomRotation output_rotation)
++{
++	GsdWacomRotation rotations[] = { GSD_WACOM_ROTATION_HALF,
++	                                 GSD_WACOM_ROTATION_CW,
++	                                 GSD_WACOM_ROTATION_NONE,
++	                                 GSD_WACOM_ROTATION_CCW };
++	guint i;
++
++	if (device_rotation == output_rotation)
++		return GSD_WACOM_ROTATION_NONE;
++
++	if (output_rotation == GSD_WACOM_ROTATION_NONE)
++		return device_rotation;
++
++	for (i = 0; i < G_N_ELEMENTS (rotations); i++) {
++		if (device_rotation == rotations[i])
++			break;
++	}
++
++	if (output_rotation == GSD_WACOM_ROTATION_HALF)
++		return rotations[(i + G_N_ELEMENTS (rotations) - 2) % G_N_ELEMENTS (rotations)];
++
++	if (output_rotation == GSD_WACOM_ROTATION_CW)
++		return rotations[(i + 1) % G_N_ELEMENTS (rotations)];
++
++	if (output_rotation == GSD_WACOM_ROTATION_CCW)
++		return rotations[(i + G_N_ELEMENTS (rotations) - 1) % G_N_ELEMENTS (rotations)];
++
++	/* fallback */
++	return GSD_WACOM_ROTATION_NONE;
++}
++
++void
++gsd_wacom_osd_window_set_device (GsdWacomOSDWindow *osd_window,
++				 GsdWacomDevice    *device)
++{
++	GtkAllocation     allocation;
++	GsdWacomRotation  device_rotation;
++	GsdWacomRotation  output_rotation;
++	GSettings        *settings;
++	gint              monitor;
++	GdkRectangle      desktop;
++	GdkScreen        *screen;
++	GList            *list, *l;
++
++	g_return_if_fail (GSD_IS_WACOM_OSD_WINDOW (osd_window));
++	g_return_if_fail (GSD_IS_WACOM_DEVICE (device));
++
++	/* Bind the device with the OSD window */
++	if (osd_window->priv->pad)
++		g_object_weak_unref (G_OBJECT(osd_window->priv->pad),
++		                     (GWeakNotify) gtk_widget_destroy,
++		                     osd_window);
++	osd_window->priv->pad = device;
++	g_object_weak_ref (G_OBJECT(osd_window->priv->pad),
++	                   (GWeakNotify) gtk_widget_destroy,
++	                   osd_window);
++
++	/* Determine the monitor for that device */
++	monitor = gsd_wacom_device_get_display_monitor (device);
++	if (monitor < 0)
++		monitor = 0;
++	screen = gtk_window_get_screen (GTK_WINDOW (osd_window));
++	if (screen == NULL)
++		screen = gdk_screen_get_default ();
++	gdk_screen_get_monitor_geometry (screen, monitor, &desktop);
++
++	/* and place the OSD window accordingly */
++	gtk_window_move (GTK_WINDOW (osd_window), desktop.x, desktop.y);
++	gtk_window_set_default_size (GTK_WINDOW (osd_window), desktop.width, desktop.height);
++
++	/* Capture current rotation, we do not update that later, OSD window is meant to be short lived */
++	settings = gsd_wacom_device_get_settings (osd_window->priv->pad);
++	device_rotation = g_settings_get_enum (settings, ROTATION_KEY);
++	output_rotation = gsd_wacom_device_get_display_rotation (osd_window->priv->pad);
++	osd_window->priv->rotation = display_relative_rotation (device_rotation, output_rotation);
++
++	/* Create the new button hash table */
++	if (osd_window->priv->buttons)
++		g_list_free_full (osd_window->priv->buttons, g_object_unref);
++	osd_window->priv->buttons = NULL;
++
++	/* Create the buttons and compute the number of buttons on each side */
++	list = gsd_wacom_device_get_buttons (device);
++	for (l = list; l != NULL; l = l->next) {
++		GsdWacomTabletButton *tablet_button = l->data;
++
++		if (tablet_button->type == WACOM_TABLET_BUTTON_TYPE_NORMAL ||
++		    tablet_button->type == WACOM_TABLET_BUTTON_TYPE_HARDCODED) {
++			GsdWacomOSDButton *osd_button;
++			gchar *str;
++
++			/* For now, place all buttons at (0,0) we'll move them later */
++			osd_button = gsd_wacom_osd_button_new (0, 0, 0, 0);
++			str = get_tablet_button_label (device, tablet_button);
++			gsd_wacom_osd_button_set_label (osd_button, str);
++			g_free (str);
++			gsd_wacom_osd_button_set_id (osd_button, tablet_button->id);
++			gsd_wacom_osd_button_set_button_type (osd_button, tablet_button->type);
++			gsd_wacom_osd_button_set_position (osd_button, tablet_button->pos);
++			osd_window->priv->buttons = g_list_append (osd_window->priv->buttons, osd_button);
++
++			osd_window->priv->num_buttons[tablet_button->pos]++;
++		}
++	}
++	g_list_free (list);
++
++	gtk_widget_get_allocation(GTK_WIDGET(osd_window), &allocation);
++	gsd_wacom_osd_window_place_buttons (osd_window, &allocation);
++}
++
++GsdWacomDevice *
++gsd_wacom_osd_window_get_device (GsdWacomOSDWindow *osd_window)
++{
++	g_return_val_if_fail (GSD_IS_WACOM_OSD_WINDOW (osd_window), NULL);
++
++	return osd_window->priv->pad;
++}
++
++void
++gsd_wacom_osd_window_set_message (GsdWacomOSDWindow *osd_window,
++				  const gchar       *str)
++{
++	g_return_if_fail (GSD_IS_WACOM_OSD_WINDOW (osd_window));
++	g_return_if_fail (str != NULL);
++
++	g_free (osd_window->priv->message);
++	osd_window->priv->message = g_strdup (str);
++}
++
++const char *
++gsd_wacom_osd_window_get_message (GsdWacomOSDWindow *osd_window)
++{
++	g_return_val_if_fail (GSD_IS_WACOM_OSD_WINDOW (osd_window), NULL);
++
++	return osd_window->priv->message;
++}
++
++static void
++gsd_wacom_osd_window_set_property (GObject        *object,
++				   guint           prop_id,
++				   const GValue   *value,
++				   GParamSpec     *pspec)
++{
++	GsdWacomOSDWindow *osd_window;
++
++	osd_window = GSD_WACOM_OSD_WINDOW (object);
++
++	switch (prop_id) {
++	case PROP_OSD_WINDOW_MESSAGE:
++		gsd_wacom_osd_window_set_message (osd_window, g_value_get_string (value));
++		break;
++	case PROP_OSD_WINDOW_GSD_WACOM_DEVICE:
++		gsd_wacom_osd_window_set_device (osd_window, g_value_get_object (value));
++		break;
++	default:
++		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++		break;
++	}
++}
++
++static void
++gsd_wacom_osd_window_get_property (GObject        *object,
++				   guint           prop_id,
++				   GValue         *value,
++				   GParamSpec     *pspec)
++{
++	GsdWacomOSDWindow *osd_window;
++
++	osd_window = GSD_WACOM_OSD_WINDOW (object);
++
++	switch (prop_id) {
++	case PROP_OSD_WINDOW_MESSAGE:
++		g_value_set_string (value, osd_window->priv->message);
++		break;
++	case PROP_OSD_WINDOW_GSD_WACOM_DEVICE:
++		g_value_set_object (value, (GObject*) osd_window->priv->pad);
++		break;
++	default:
++		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++		break;
++	}
++}
++
++void
++gsd_wacom_osd_window_set_active (GsdWacomOSDWindow *osd_window,
++				 gchar             *button_id,
++				 gboolean           active)
++{
++	GdkWindow *win;
++	GList *l;
++
++	g_return_if_fail (GSD_IS_WACOM_OSD_WINDOW (osd_window));
++	g_return_if_fail (button_id != NULL);
++
++	for (l = osd_window->priv->buttons; l != NULL; l = l->next) {
++		GsdWacomOSDButton *osd_button = l->data;
++		if (g_strcmp0 (osd_button->priv->id, button_id) == 0) {
++			gsd_wacom_osd_button_set_active (osd_button, active);
++			win = gtk_widget_get_window(GTK_WIDGET(osd_window));
++			gdk_window_invalidate_rect(win, &osd_button->priv->area, FALSE);
++		}
++	}
++}
++
++GtkWidget *
++gsd_wacom_osd_window_new (GsdWacomDevice       *pad,
++			  const gchar          *message)
++{
++	GsdWacomOSDWindow *osd_window;
++	GdkWindow         *window;
++	GdkRGBA            black;
++	GdkCursor         *cursor;
++
++	osd_window = GSD_WACOM_OSD_WINDOW (g_object_new (GSD_TYPE_WACOM_OSD_WINDOW,
++	                                                 "wacom-device", pad,
++	                                                 "message", message,
++	                                                 NULL));
++
++	gtk_widget_set_app_paintable (GTK_WIDGET (osd_window), TRUE);
++
++	gdk_rgba_parse (&black, "rgb(0,0,0)");
++	gtk_window_set_opacity (GTK_WINDOW (osd_window), WINDOW_OPACITY);
++
++	gtk_widget_realize (GTK_WIDGET (osd_window));
++	window = gtk_widget_get_window (GTK_WIDGET (osd_window));
++	gdk_window_set_background_rgba (window, &black);
++
++	cursor = gdk_cursor_new (GDK_BLANK_CURSOR);
++	gdk_window_set_cursor (window, cursor);
++	g_object_unref (cursor);
++
++	gtk_widget_set_can_focus (GTK_WIDGET (osd_window), TRUE);
++	gtk_window_set_focus_on_map (GTK_WINDOW (osd_window), TRUE);
++	gtk_window_set_decorated (GTK_WINDOW (osd_window), FALSE);
++	gtk_window_set_deletable (GTK_WINDOW (osd_window), FALSE);
++	gtk_window_set_skip_taskbar_hint (GTK_WINDOW (osd_window), TRUE);
++	gtk_window_set_skip_pager_hint (GTK_WINDOW (osd_window), TRUE);
++	gtk_window_set_keep_above (GTK_WINDOW (osd_window), TRUE);
++	gtk_window_fullscreen (GTK_WINDOW (osd_window));
++
++	return GTK_WIDGET (osd_window);
++}
++
++static void
++gsd_wacom_osd_window_size_allocate (GtkWidget     *widget,
++				    GtkAllocation *allocation)
++{
++	/* Recompute buttons position */
++	gsd_wacom_osd_window_place_buttons (GSD_WACOM_OSD_WINDOW (widget), allocation);
++}
++
++static void
++gsd_wacom_osd_window_class_init (GsdWacomOSDWindowClass *klass)
++{
++	GObjectClass *gobject_class;
++	GtkWidgetClass *widget_class;
++
++	gobject_class = G_OBJECT_CLASS (klass);
++	widget_class  = GTK_WIDGET_CLASS (klass);
++
++	gobject_class->set_property = gsd_wacom_osd_window_set_property;
++	gobject_class->get_property = gsd_wacom_osd_window_get_property;
++	gobject_class->finalize     = gsd_wacom_osd_window_finalize;
++	widget_class->draw          = gsd_wacom_osd_window_draw;
++	widget_class->size_allocate = gsd_wacom_osd_window_size_allocate;
++
++	g_object_class_install_property (gobject_class,
++	                                 PROP_OSD_WINDOW_MESSAGE,
++	                                 g_param_spec_string ("message",
++	                                                      "Window message",
++	                                                      "The message shown in the OSD window",
++	                                                      "",
++	                                                      G_PARAM_READWRITE));
++	g_object_class_install_property (gobject_class,
++	                                 PROP_OSD_WINDOW_GSD_WACOM_DEVICE,
++	                                 g_param_spec_object ("wacom-device",
++	                                                      "Wacom device",
++	                                                      "The Wacom device represented by the OSD window",
++	                                                      GSD_TYPE_WACOM_DEVICE,
++	                                                      G_PARAM_READWRITE));
++
++	g_type_class_add_private (klass, sizeof (GsdWacomOSDWindowPrivate));
++}
++
++static void
++gsd_wacom_osd_window_init (GsdWacomOSDWindow *osd_window)
++{
++	osd_window->priv = GSD_WACOM_OSD_WINDOW_GET_PRIVATE (osd_window);
++}
++
++static void
++gsd_wacom_osd_window_finalize (GObject *object)
++{
++	GsdWacomOSDWindow *osd_window;
++	GsdWacomOSDWindowPrivate *priv;
++
++	g_return_if_fail (object != NULL);
++	g_return_if_fail (GSD_IS_WACOM_OSD_WINDOW (object));
++
++	osd_window = GSD_WACOM_OSD_WINDOW (object);
++
++	g_return_if_fail (osd_window->priv != NULL);
++
++	priv = osd_window->priv;
++
++	g_free (priv->message);
++	priv->message = NULL;
++
++	if (priv->buttons) {
++		g_list_free_full (priv->buttons, g_object_unref);
++		priv->buttons = NULL;
++	}
++
++	G_OBJECT_CLASS (gsd_wacom_osd_window_parent_class)->finalize (object);
++}
+diff --git a/plugins/wacom/gsd-wacom-osd-window.h b/plugins/wacom/gsd-wacom-osd-window.h
+new file mode 100644
+index 0000000..a1f669e
+--- /dev/null
++++ b/plugins/wacom/gsd-wacom-osd-window.h
+@@ -0,0 +1,62 @@
++/*
++ * Copyright (C) 2012 Red Hat, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program 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 General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * Author: Olivier Fourdan <ofourdan at redhat.com>
++ *
++ */
++
++#ifndef __GSD_WACOM_OSD_WINDOW_H
++#define __GSD_WACOM_OSD_WINDOW_H
++
++#include <gtk/gtk.h>
++#include <glib-object.h>
++#include "gsd-wacom-device.h"
++
++#define GSD_TYPE_WACOM_OSD_WINDOW         (gsd_wacom_osd_window_get_type ())
++#define GSD_WACOM_OSD_WINDOW(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_WACOM_OSD_WINDOW, GsdWacomOSDWindow))
++#define GSD_WACOM_OSD_WINDOW_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_WACOM_OSD_WINDOW, GsdWacomOSDWindowClass))
++#define GSD_IS_WACOM_OSD_WINDOW(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_WACOM_OSD_WINDOW))
++#define GSD_IS_WACOM_OSD_WINDOW_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_WACOM_OSD_WINDOW))
++#define GSD_WACOM_OSD_WINDOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_WACOM_OSD_WINDOW, GsdWacomOSDWindowClass))
++
++typedef struct GsdWacomOSDWindowPrivate GsdWacomOSDWindowPrivate;
++
++typedef struct
++{
++        GtkWindow                 window;
++        GsdWacomOSDWindowPrivate *priv;
++} GsdWacomOSDWindow;
++
++typedef struct
++{
++        GtkWindowClass            parent_class;
++} GsdWacomOSDWindowClass;
++
++GType                     gsd_wacom_osd_window_get_type        (void) G_GNUC_CONST;
++void                      gsd_wacom_osd_window_set_device      (GsdWacomOSDWindow        *osd_window,
++			                                        GsdWacomDevice           *device);
++GsdWacomDevice *          gsd_wacom_osd_window_get_device      (GsdWacomOSDWindow        *osd_window);
++void                      gsd_wacom_osd_window_set_message     (GsdWacomOSDWindow        *osd_window,
++                                                                const gchar              *str);
++const char *              gsd_wacom_osd_window_get_message     (GsdWacomOSDWindow        *osd_window);
++void                      gsd_wacom_osd_window_set_active      (GsdWacomOSDWindow        *osd_window,
++                                                                gchar                    *button_id,
++                                                                gboolean                  active);
++GtkWidget *               gsd_wacom_osd_window_new             (GsdWacomDevice           *pad,
++                                                                const gchar              *message);
++
++#endif /* __GSD_WACOM_OSD_WINDOW_H */
+diff --git a/plugins/wacom/test-osd-window.c b/plugins/wacom/test-osd-window.c
+new file mode 100644
+index 0000000..484515e
+--- /dev/null
++++ b/plugins/wacom/test-osd-window.c
+@@ -0,0 +1,131 @@
++/*
++ * Copyright (C) 2012 Red Hat, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program 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 General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * Author: Olivier Fourdan <ofourdan at redhat.com>
++ *
++ */
++
++#include "config.h"
++
++#include <stdlib.h>
++#include <ctype.h>
++#include <string.h>
++#include <dirent.h>
++#include <glib/gi18n.h>
++#include "gsd-wacom-osd-window.h"
++
++static gboolean fake_device = FALSE;
++static gboolean option_debug = FALSE;
++
++static GsdWacomDevice *
++search_pad_device (void)
++{
++	GdkDeviceManager *mgr;
++	GList *list, *l;
++
++	mgr = gdk_display_get_device_manager (gdk_display_get_default ());
++	list = gdk_device_manager_list_devices (mgr, GDK_DEVICE_TYPE_SLAVE);
++	for (l = list; l ; l = l->next) {
++		GsdWacomDevice *device;
++
++		device = gsd_wacom_device_new (l->data);
++		if (gsd_wacom_device_get_device_type (device) == WACOM_TYPE_PAD)
++			return (device);
++		g_object_unref (device);
++	}
++	g_list_free (list);
++
++	return NULL;
++}
++
++static GsdWacomDevice *
++create_fake_device (void)
++{
++	return gsd_wacom_device_create_fake (WACOM_TYPE_PAD,
++					     "Wacom Cintiq 21UX2",
++					     "Wacom Cintiq 21UX2 pad");
++}
++
++static gboolean
++on_key_release_event(GtkWidget   *widget,
++                     GdkEventKey *event,
++                     gpointer     data)
++{
++	if (event->type != GDK_KEY_RELEASE)
++		return FALSE;
++	if (event->keyval != GDK_KEY_Escape)
++		return FALSE;
++
++	gtk_main_quit();
++
++	return FALSE;
++}
++
++int main(int argc, char** argv)
++{
++	GtkWidget *widget;
++	GError *error = NULL;
++	GOptionContext *context;
++	GsdWacomDevice *device;
++	gchar *message;
++	const GOptionEntry entries[] = {
++		{ "fake", 'f', 0, G_OPTION_ARG_NONE, &fake_device, "Fake Wacom Cintiq 21UX2", NULL },
++		{ "debug", 'd', 0, G_OPTION_ARG_NONE, &option_debug, "Debug output", NULL },
++		{ NULL }
++	};
++
++	gtk_init (&argc, &argv);
++
++	context = g_option_context_new ("- test functions");
++	g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
++
++	if (g_option_context_parse (context, &argc, &argv, &error) == FALSE) {
++		g_print ("Option parsing failed: %s\n", error->message);
++		return 1;
++	}
++
++	if (option_debug)
++		g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
++
++	if (fake_device == FALSE)
++		device = search_pad_device ();
++	else
++		device = create_fake_device ();
++
++	if (device == NULL) {
++		g_print ("No pad device found, consider using --fake\n");
++		return 1;
++	}
++
++	gtk_init(&argc, &argv);
++
++	message = g_strdup_printf ("<big><b>%s</b></big>\n<i>Test OSD application</i>", gsd_wacom_device_get_name (device));
++	widget = gsd_wacom_osd_window_new (device, message);
++	g_free (message);
++
++	g_signal_connect (widget, "key-release-event",
++			  G_CALLBACK(on_key_release_event), NULL);
++	g_signal_connect (widget, "delete-event",
++			  G_CALLBACK (gtk_main_quit), NULL);
++	g_signal_connect (widget, "unmap",
++			  G_CALLBACK (gtk_main_quit), NULL);
++
++	gtk_widget_show (widget);
++	gtk_main ();
++
++	return 0;
++}
+-- 
+1.7.12.1
+
diff --git a/gnome-settings-daemon.spec b/gnome-settings-daemon.spec
index b75e106..89518f2 100644
--- a/gnome-settings-daemon.spec
+++ b/gnome-settings-daemon.spec
@@ -1,6 +1,6 @@
 Name:           gnome-settings-daemon
 Version:        3.6.0
-Release:        5%{?dist}
+Release:        6%{?dist}
 Summary:        The daemon sharing settings from GNOME to GTK+/KDE applications
 
 Group:          System Environment/Daemons
@@ -15,6 +15,9 @@ Patch0:         %{name}-3.5.4-ppc-no-wacom.patch
 Patch1: 0001-Clean-up-gsd_power_stop.patch
 # https://bugzilla.gnome.org/show_bug.cgi?id=680689
 Patch2: 0001-power-and-media-keys-Use-logind-for-suspending-and-r.patch
+# Wacom OSD window
+# https://bugzilla.gnome.org/show_bug.cgi?id=679062
+Patch3: 0001-wacom-implement-OSD-help-window.patch
 
 Requires: control-center-filesystem
 
@@ -81,6 +84,7 @@ The %{name}-updates package contains the updates plugin for %{name}
 
 %patch1 -p1
 %patch2 -p1
+%patch3 -p1 -b .wacom-osd-window
 
 autoreconf -i -f
 
@@ -248,7 +252,7 @@ glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || :
 %{_libexecdir}/gsd-test-smartcard
 %{_libexecdir}/gsd-test-sound
 %{_libexecdir}/gsd-test-xsettings
-
+%{_libexecdir}/gsd-test-wacom-osd
 
 %files updates
 %{_libdir}/gnome-settings-daemon-3.0/updates.gnome-settings-plugin
@@ -257,6 +261,9 @@ glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || :
 %{_datadir}/dbus-1/interfaces/org.gnome.SettingsDaemonUpdates.xml
 
 %changelog
+* Fri Oct  5 2012 Olivier Fourdan <mclasen at redhat.com> - 3.6.0-6
+- Adds Wacom OSD window from upstream bug #679062
+
 * Wed Oct  3 2012 Matthias Clasen <mclasen at redhat.com> - 3.6.0-5
 - Fix an inhibitor leak in the previous patch
 


More information about the scm-commits mailing list