[gnome-settings-daemon] wacom: implement OSD help window
- From: Bastien Nocera <hadess src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-settings-daemon] wacom: implement OSD help window
- Date: Thu, 20 Dec 2012 10:21:06 +0000 (UTC)
commit 8eb78560cc698a196cdc995a8585ea85d6896cea
Author: Olivier Fourdan <ofourdan redhat com>
Date: Tue Dec 4 12:04:05 2012 +0100
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
configure.ac | 2 +-
data/gsd-enums.h | 3 +-
plugins/wacom/Makefile.am | 55 ++-
plugins/wacom/gsd-wacom-device.c | 89 ++-
plugins/wacom/gsd-wacom-device.h | 18 +
plugins/wacom/gsd-wacom-manager.c | 120 +++
plugins/wacom/gsd-wacom-osd-window.c | 1507 ++++++++++++++++++++++++++++++++++
plugins/wacom/gsd-wacom-osd-window.h | 61 ++
plugins/wacom/tablet-layout.css | 34 +
plugins/wacom/test-osd-window.c | 137 +++
plugins/wacom/wacom.gresource.xml | 7 +
po/POTFILES.in | 1 +
12 files changed, 2023 insertions(+), 11 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 1faec53..8a3c950 100644
--- a/configure.ac
+++ b/configure.ac
@@ -249,7 +249,7 @@ case $host_os in
have_wacom=no
else
if test x$enable_gudev != xno; then
- PKG_CHECK_MODULES(WACOM, [libwacom >= $LIBWACOM_REQUIRED_VERSION x11 xi xtst gudev-1.0 gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION xorg-wacom])
+ PKG_CHECK_MODULES(WACOM, [libwacom >= $LIBWACOM_REQUIRED_VERSION x11 xi xtst gudev-1.0 gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION xorg-wacom librsvg-2.0])
else
AC_MSG_ERROR([GUdev is necessary to compile Wacom support])
fi
diff --git a/data/gsd-enums.h b/data/gsd-enums.h
index 8b8f2f5..b6876a3 100644
--- a/data/gsd-enums.h
+++ b/data/gsd-enums.h
@@ -95,7 +95,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..67701b1 100644
--- a/plugins/wacom/Makefile.am
+++ b/plugins/wacom/Makefile.am
@@ -7,8 +7,11 @@ 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
+ gsd-wacom-device.h \
+ gsd-wacom-resources.c
libgsdwacom_la_CPPFLAGS = \
-I$(top_srcdir)/gnome-settings-daemon \
@@ -35,6 +38,14 @@ libgsdwacom_la_LIBADD = \
org.gnome.settings-daemon.plugins.wacom.policy.in: org.gnome.settings-daemon.plugins.wacom.policy.in.in Makefile
$(AM_V_GEN) sed -e "s|\ libexecdir\@|$(libexecdir)|" $< > $@
+gsd-wacom-resources.c: wacom.gresource.xml tablet-layout.css
+ glib-compile-resources \
+ --target=$@ \
+ --sourcedir=$(srcdir) \
+ --generate-source \
+ --c-name gsd_wacom \
+ $(srcdir)/wacom.gresource.xml
+
@INTLTOOL_POLICY_RULE@
polkit_policydir = $(datadir)/polkit-1/actions
polkit_policy_in_files = org.gnome.settings-daemon.plugins.wacom.policy.in
@@ -43,7 +54,7 @@ polkit_policy_DATA = $(polkit_policy_in_files:.policy.in=.policy)
# so it always gets included in the tarball
gsd_wacom_led_helper_SOURCES = gsd-wacom-led-helper.c
-EXTRA_DIST = $(gsd_wacom_led_helper_SOURCES)
+EXTRA_DIST = $(gsd_wacom_led_helper_SOURCES) wacom.gresource.xml tablet-layout.css
if HAVE_GUDEV
libexec_PROGRAMS = gsd-wacom-led-helper
@@ -60,14 +71,17 @@ 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
+ gsd-wacom-device.h \
+ gsd-wacom-resources.c
gsd_test_wacom_CPPFLAGS = \
-I$(top_srcdir)/data/ \
@@ -123,6 +137,38 @@ 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-wacom-resources.c
+
+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)
@@ -130,6 +176,7 @@ plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin
EXTRA_DIST += $(plugin_in_files) README.config-storage
CLEANFILES = \
$(plugin_DATA) \
+ gsd-wacom-resources.c \
org.gnome.settings-daemon.plugins.wacom.policy \
org.gnome.settings-daemon.plugins.wacom.policy.in
diff --git a/plugins/wacom/gsd-wacom-device.c b/plugins/wacom/gsd-wacom-device.c
index ba1f83e..28d6e4d 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,
int status_led)
@@ -274,6 +275,7 @@ gsd_wacom_tablet_button_new (const char *name,
ret->group_id = group_id;
ret->idx = idx;
ret->type = type;
+ ret->pos = pos;
ret->status_led = status_led;
return ret;
@@ -330,6 +332,7 @@ struct GsdWacomDevicePrivate
char *path;
char *machine_id;
const char *icon_name;
+ char *layout_path;
char *tool_name;
gboolean reversible;
gboolean is_screen_tablet;
@@ -957,6 +960,32 @@ add_stylus_to_device (GsdWacomDevice *device,
}
int
+gsd_wacom_device_get_num_modes (GsdWacomDevice *device,
+ int group_id)
+{
+ int num_modes;
+
+ g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), -1);
+ num_modes = GPOINTER_TO_INT (g_hash_table_lookup (device->priv->num_modes, GINT_TO_POINTER(group_id)));
+
+ return num_modes;
+}
+
+int
+gsd_wacom_device_get_current_mode (GsdWacomDevice *device,
+ int group_id)
+{
+ int current_idx;
+
+ 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,
GsdWacomTabletButton *button)
{
@@ -996,9 +1025,9 @@ gsd_wacom_device_set_next_mode (GsdWacomDevice *device,
/* Only one mode-switch? cycle through the modes */
if (num_switches == 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);
+ 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++;
}
@@ -1048,6 +1077,7 @@ gsd_wacom_device_add_ring_modes (WacomDevice *wacom_device,
"left-ring-mode-1",
settings_path,
WACOM_TABLET_BUTTON_TYPE_RING,
+ WACOM_TABLET_BUTTON_POS_LEFT,
group,
0,
GSD_WACOM_NO_LED));
@@ -1059,6 +1089,7 @@ gsd_wacom_device_add_ring_modes (WacomDevice *wacom_device,
id,
settings_path,
WACOM_TABLET_BUTTON_TYPE_RING,
+ WACOM_TABLET_BUTTON_POS_LEFT,
group,
i - 1,
GSD_WACOM_NO_LED));
@@ -1075,6 +1106,7 @@ gsd_wacom_device_add_ring_modes (WacomDevice *wacom_device,
"right-ring-mode-1",
settings_path,
WACOM_TABLET_BUTTON_TYPE_RING,
+ WACOM_TABLET_BUTTON_POS_RIGHT,
group,
0,
GSD_WACOM_NO_LED));
@@ -1086,6 +1118,7 @@ gsd_wacom_device_add_ring_modes (WacomDevice *wacom_device,
id,
settings_path,
WACOM_TABLET_BUTTON_TYPE_RING,
+ WACOM_TABLET_BUTTON_POS_RIGHT,
group,
i - 1,
GSD_WACOM_NO_LED));
@@ -1124,6 +1157,7 @@ gsd_wacom_device_add_strip_modes (WacomDevice *wacom_device,
"left-strip-mode-1",
settings_path,
WACOM_TABLET_BUTTON_TYPE_STRIP,
+ WACOM_TABLET_BUTTON_POS_LEFT,
group,
0,
GSD_WACOM_NO_LED));
@@ -1135,6 +1169,7 @@ gsd_wacom_device_add_strip_modes (WacomDevice *wacom_device,
id,
settings_path,
WACOM_TABLET_BUTTON_TYPE_STRIP,
+ WACOM_TABLET_BUTTON_POS_LEFT,
group,
i - 1,
GSD_WACOM_NO_LED));
@@ -1151,6 +1186,7 @@ gsd_wacom_device_add_strip_modes (WacomDevice *wacom_device,
"right-strip-mode-1",
settings_path,
WACOM_TABLET_BUTTON_TYPE_STRIP,
+ WACOM_TABLET_BUTTON_POS_RIGHT,
group,
0,
GSD_WACOM_NO_LED));
@@ -1162,6 +1198,7 @@ gsd_wacom_device_add_strip_modes (WacomDevice *wacom_device,
id,
settings_path,
WACOM_TABLET_BUTTON_TYPE_STRIP,
+ WACOM_TABLET_BUTTON_POS_RIGHT,
group,
i - 1,
GSD_WACOM_NO_LED));
@@ -1195,6 +1232,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,
@@ -1221,7 +1275,14 @@ 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, GSD_WACOM_NO_LED));
+ 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,
+ GSD_WACOM_NO_LED));
g_free (name);
g_free (id);
}
@@ -1242,7 +1303,14 @@ 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);
status_led = libwacom_get_button_led_group (wacom_device, 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, status_led));
+ 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,
+ status_led));
g_free (name);
g_free (id);
}
@@ -1343,6 +1411,7 @@ gsd_wacom_device_update_from_db (GsdWacomDevice *device,
settings_path);
device->priv->name = g_strdup (libwacom_get_name (wacom_device));
+ device->priv->layout_path = g_strdup (libwacom_get_layout_filename (wacom_device));
device->priv->reversible = libwacom_is_reversible (wacom_device);
integration_flags = libwacom_get_integration_flags (wacom_device);
device->priv->is_screen_tablet = (integration_flags & WACOM_DEVICE_INTEGRATED_DISPLAY);
@@ -1592,6 +1661,8 @@ gsd_wacom_device_finalize (GObject *object)
p->num_modes = NULL;
}
+ g_clear_pointer (&p->layout_path, g_free);
+
gdk_window_remove_filter (NULL,
(GdkFilterFunc) filter_events,
device);
@@ -1641,6 +1712,14 @@ gsd_wacom_device_get_name (GsdWacomDevice *device)
}
const char *
+gsd_wacom_device_get_layout_path (GsdWacomDevice *device)
+{
+ g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL);
+
+ return device->priv->layout_path;
+}
+
+const char *
gsd_wacom_device_get_path (GsdWacomDevice *device)
{
g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL);
diff --git a/plugins/wacom/gsd-wacom-device.h b/plugins/wacom/gsd-wacom-device.h
index c593e66..335945f 100644
--- a/plugins/wacom/gsd-wacom-device.h
+++ b/plugins/wacom/gsd-wacom-device.h
@@ -98,6 +98,18 @@ typedef enum {
WACOM_TABLET_BUTTON_TYPE_HARDCODED
} GsdWacomTabletButtonType;
+/*
+ * Positions of the buttons on the tablet in default right-handed mode
+ * (ie with no rotation applied).
+ */
+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
#define GSD_WACOM_NO_LED -1
@@ -108,6 +120,7 @@ typedef struct
char *id;
GSettings *settings;
GsdWacomTabletButtonType type;
+ GsdWacomTabletButtonPos pos;
int group_id, idx;
int status_led;
} GsdWacomTabletButton;
@@ -141,6 +154,7 @@ GsdWacomRotation gsd_wacom_device_get_display_rotation (GsdWacomDevice *device);
GsdWacomDevice * gsd_wacom_device_new (GdkDevice *device);
GList * gsd_wacom_device_list_styli (GsdWacomDevice *device);
const char * gsd_wacom_device_get_name (GsdWacomDevice *device);
+const char * gsd_wacom_device_get_layout_path (GsdWacomDevice *device);
const char * gsd_wacom_device_get_path (GsdWacomDevice *device);
const char * gsd_wacom_device_get_icon_name (GsdWacomDevice *device);
const char * gsd_wacom_device_get_tool_name (GsdWacomDevice *device);
@@ -163,6 +177,10 @@ 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_num_modes (GsdWacomDevice *device,
+ int group_id);
+int gsd_wacom_device_get_current_mode (GsdWacomDevice *device,
+ int group_id);
int gsd_wacom_device_set_next_mode (GsdWacomDevice *device,
GsdWacomTabletButton *button);
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 eb5c23e..0e6e9c7 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);
@@ -851,6 +855,97 @@ last_stylus_changed (GsdWacomDevice *device,
}
static void
+osd_window_destroy (GsdWacomManager *manager)
+{
+ g_return_if_fail (manager != NULL);
+
+ g_clear_pointer (&manager->priv->osd_window, gtk_widget_destroy);
+}
+
+static gboolean
+osd_window_on_key_release_event (GtkWidget *widget,
+ GdkEventKey *event,
+ GsdWacomManager *manager)
+{
+ osd_window_destroy (manager);
+
+ return FALSE;
+}
+
+static gboolean
+osd_window_on_focus_out_event (GtkWidget *widget,
+ GdkEvent *event,
+ GsdWacomManager *manager)
+{
+ /* If the OSD window loses focus, hide it */
+ osd_window_destroy (manager);
+
+ return FALSE;
+}
+
+static gboolean
+osd_window_toggle_visibility (GsdWacomManager *manager,
+ GsdWacomDevice *device)
+{
+ GtkWidget *widget;
+ const gchar *layout_path;
+
+ if (manager->priv->osd_window) {
+ osd_window_destroy (manager);
+ return FALSE;
+ }
+
+ layout_path = gsd_wacom_device_get_layout_path (device);
+ if (layout_path == NULL) {
+ g_warning ("Cannot display the on-screen help window as the tablet "
+ "definition for %s is missing the layout\n"
+ "Please consider contributing the layout for your "
+ "tablet to libwacom at linuxwacom-devel lists sourceforge net\n",
+ gsd_wacom_device_get_name (device));
+ return FALSE;
+ }
+
+ if (g_file_test (layout_path, G_FILE_TEST_EXISTS) == FALSE) {
+ g_warning ("Cannot display the on-screen help window as the "
+ "layout file %s cannot be found on disk\n"
+ "Please check your libwacom installation\n",
+ layout_path);
+ return FALSE;
+ }
+
+ widget = gsd_wacom_osd_window_new (device, NULL);
+
+ /* 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,
+ GsdWacomTabletButton *button,
+ GtkDirectionType dir,
+ 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,
+ dir,
+ xiev->evtype == XI_ButtonPress);
+
+ return TRUE;
+}
+
+static void
device_added_cb (GdkDeviceManager *device_manager,
GdkDevice *gdk_device,
GsdWacomManager *manager)
@@ -1121,6 +1216,7 @@ filter_button_events (XEvent *xevent,
int button;
GsdWacomTabletButton *wbutton;
GtkDirectionType dir;
+ gboolean emulate;
/* verify we have a key event */
if (xevent->type != GenericEvent)
@@ -1142,6 +1238,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_destroy (manager);
+
button = xev->detail;
wbutton = gsd_wacom_device_get_button (device, button, &dir);
@@ -1162,6 +1263,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, dir, xiev))
+ return GDK_FILTER_REMOVE;
+
/* We switch modes on key release */
if (xiev->evtype == XI_ButtonRelease)
return GDK_FILTER_REMOVE;
@@ -1171,10 +1276,23 @@ filter_button_events (XEvent *xevent,
return GDK_FILTER_REMOVE;
}
+ /* Update OSD window if shown */
+ emulate = osd_window_update_viewable (manager, wbutton, dir, 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)
@@ -1385,6 +1503,8 @@ gsd_wacom_manager_stop (GsdWacomManager *manager)
for (l = p->rr_screens; l != NULL; l = l->next)
g_signal_handlers_disconnect_by_func (l->data, on_screen_changed_cb, manager);
+
+ g_clear_pointer (&p->osd_window, gtk_widget_destroy);
}
static void
diff --git a/plugins/wacom/gsd-wacom-osd-window.c b/plugins/wacom/gsd-wacom-osd-window.c
new file mode 100644
index 0000000..9055251
--- /dev/null
+++ b/plugins/wacom/gsd-wacom-osd-window.c
@@ -0,0 +1,1507 @@
+/*
+ * 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 redhat com>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <cairo.h>
+#include <librsvg/rsvg.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 RES_PATH "/org/gnome/settings-daemon/plugins/wacom/"
+
+#define BACK_OPACITY 0.8
+#define INACTIVE_COLOR "#ededed"
+#define ACTIVE_COLOR "#729fcf"
+#define STROKE_COLOR "#000000"
+#define DARK_COLOR "#535353"
+#define BACK_COLOR "#000000"
+
+#define ELEVATOR_TIMEOUT 250 /* ms */
+
+static struct {
+ const gchar *color_name;
+ const gchar *color_value;
+} css_color_table[] = {
+ { "inactive_color", INACTIVE_COLOR },
+ { "active_color", ACTIVE_COLOR },
+ { "stroke_color", STROKE_COLOR },
+ { "dark_color", DARK_COLOR },
+ { "back_color", BACK_COLOR }
+};
+
+static gchar *
+replace_string (gchar **string, const gchar *search, const char *replacement)
+{
+ GRegex *regex;
+ gchar *res;
+
+ g_return_val_if_fail (*string != NULL, NULL);
+ g_return_val_if_fail (string != NULL, NULL);
+ g_return_val_if_fail (search != NULL, *string);
+ g_return_val_if_fail (replacement != NULL, *string);
+
+ regex = g_regex_new (search, 0, 0, NULL);
+ res = g_regex_replace_literal (regex, *string, -1, 0, replacement, 0, NULL);
+ g_regex_unref (regex);
+ /* The given string is freed and replaced by the resulting replacement */
+ g_free (*string);
+ *string = res;
+
+ return res;
+}
+
+static gchar
+get_last_char (gchar *string)
+{
+ size_t pos;
+
+ g_return_val_if_fail (string != NULL, '\0');
+ pos = strlen (string);
+ g_return_val_if_fail (pos > 0, '\0');
+
+ return string[pos - 1];
+}
+
+static double
+get_rotation_in_radian (GsdWacomRotation rotation)
+{
+ switch (rotation) {
+ case GSD_WACOM_ROTATION_NONE:
+ return 0.0;
+ break;
+ case GSD_WACOM_ROTATION_HALF:
+ return G_PI;
+ break;
+ /* We only support left-handed/right-handed */
+ case GSD_WACOM_ROTATION_CCW:
+ case GSD_WACOM_ROTATION_CW:
+ default:
+ break;
+ }
+
+ /* Fallback */
+ return 0.0;
+}
+
+static gboolean
+get_sub_location (RsvgHandle *handle,
+ const char *sub,
+ cairo_t *cr,
+ double *x,
+ double *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 (cr, &tx, &ty);
+
+ if (x)
+ *x = tx;
+ if (y)
+ *y = ty;
+
+ return TRUE;
+}
+
+static gboolean
+get_image_size (const char *filename, int *width, int *height)
+{
+ RsvgHandle *handle;
+ RsvgDimensionData dimensions;
+ GError* error = NULL;
+
+ if (filename == NULL)
+ return FALSE;
+
+ handle = rsvg_handle_new_from_file (filename, &error);
+ if (error != NULL) {
+ g_printerr ("%s\n", error->message);
+ g_error_free (error);
+ }
+ if (handle == NULL)
+ return FALSE;
+
+ /* Compute image size */
+ rsvg_handle_get_dimensions (handle, &dimensions);
+ g_object_unref (handle);
+
+ if (dimensions.width == 0 || dimensions.height == 0)
+ return FALSE;
+
+ if (width)
+ *width = dimensions.width;
+
+ if (height)
+ *height = dimensions.height;
+
+ return TRUE;
+}
+
+static int
+get_pango_vertical_offset (PangoLayout *layout)
+{
+ const PangoFontDescription *desc;
+ PangoContext *context;
+ PangoLanguage *language;
+ PangoFontMetrics *metrics;
+ int baseline;
+ int strikethrough;
+ int thickness;
+
+ context = pango_layout_get_context (layout);
+ language = pango_language_get_default ();
+ desc = pango_layout_get_font_description (layout);
+ metrics = pango_context_get_metrics (context, desc, language);
+
+ baseline = pango_layout_get_baseline (layout);
+ strikethrough = pango_font_metrics_get_strikethrough_position (metrics);
+ thickness = pango_font_metrics_get_underline_thickness (metrics);
+
+ return PANGO_PIXELS (baseline - strikethrough - thickness / 2);
+}
+
+static void
+set_grab_keyboard (GdkWindow *window, gboolean grab)
+{
+ GdkDisplay *display;
+ GdkDeviceManager *device_manager;
+ GList *devices, *dev;
+
+ display = gdk_window_get_display (window);
+ device_manager = gdk_display_get_device_manager (display);
+ devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+
+ for (dev = devices; dev; dev = dev->next) {
+ GdkDevice *device = dev->data;
+ if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD)
+ continue;
+ if (grab)
+ gdk_device_grab (device,
+ window,
+ GDK_OWNERSHIP_NONE,
+ TRUE,
+ GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+ NULL,
+ GDK_CURRENT_TIME);
+ else
+ gdk_device_ungrab (device, GDK_CURRENT_TIME);
+ }
+}
+
+#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_CLASS,
+ PROP_OSD_BUTTON_LABEL,
+ PROP_OSD_BUTTON_ACTIVE,
+ PROP_OSD_BUTTON_AUTO_OFF
+};
+
+#define GSD_WACOM_OSD_BUTTON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+ GSD_TYPE_WACOM_OSD_BUTTON, \
+ GsdWacomOSDButtonPrivate))
+
+struct GsdWacomOSDButtonPrivate {
+ GtkWidget *widget;
+ char *id;
+ char *class;
+ char *label;
+ double label_x;
+ double label_y;
+ GsdWacomTabletButtonType type;
+ GsdWacomTabletButtonPos position;
+ gboolean active;
+ guint auto_off;
+ guint timeout;
+};
+
+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 *id)
+{
+ g_return_if_fail (GSD_IS_WACOM_OSD_BUTTON (osd_button));
+
+ osd_button->priv->id = g_strdup (id);
+}
+
+static void
+gsd_wacom_osd_button_set_class (GsdWacomOSDButton *osd_button,
+ const gchar *class)
+{
+ g_return_if_fail (GSD_IS_WACOM_OSD_BUTTON (osd_button));
+
+ osd_button->priv->class = g_strdup (class);
+}
+
+static gchar*
+gsd_wacom_osd_button_get_label_class (GsdWacomOSDButton *osd_button)
+{
+ gchar *label_class;
+
+ label_class = g_strconcat ("#Label", osd_button->priv->class, NULL);
+
+ return (label_class);
+}
+
+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_location (GsdWacomOSDButton *osd_button,
+ double x,
+ double y)
+{
+ g_return_if_fail (GSD_IS_WACOM_OSD_BUTTON (osd_button));
+
+ osd_button->priv->label_x = x;
+ osd_button->priv->label_y = y;
+}
+
+static void
+gsd_wacom_osd_button_set_auto_off (GsdWacomOSDButton *osd_button,
+ guint timeout)
+{
+ g_return_if_fail (GSD_IS_WACOM_OSD_BUTTON (osd_button));
+
+ osd_button->priv->auto_off = timeout;
+}
+
+static void
+gsd_wacom_osd_button_redraw (GsdWacomOSDButton *osd_button)
+{
+ GdkWindow *window;
+
+ g_return_if_fail (GTK_IS_WIDGET (osd_button->priv->widget));
+
+ window = gtk_widget_get_window (GTK_WIDGET (osd_button->priv->widget));
+ gdk_window_invalidate_rect (window, NULL, FALSE);
+}
+
+static gboolean
+gsd_wacom_osd_button_timer (GsdWacomOSDButton *osd_button)
+{
+ /* Auto de-activate the button */
+ osd_button->priv->active = FALSE;
+ gsd_wacom_osd_button_redraw (osd_button);
+
+ return FALSE;
+}
+
+static void
+gsd_wacom_osd_button_set_active (GsdWacomOSDButton *osd_button,
+ gboolean active)
+{
+ gboolean previous_state;
+
+ g_return_if_fail (GSD_IS_WACOM_OSD_BUTTON (osd_button));
+
+ previous_state = osd_button->priv->active;
+ if (osd_button->priv->auto_off > 0) {
+ /* For auto-off buttons, apply only if active, de-activation is done in the timeout */
+ if (active == TRUE)
+ osd_button->priv->active = active;
+
+ if (osd_button->priv->timeout)
+ g_source_remove (osd_button->priv->timeout);
+ osd_button->priv->timeout = g_timeout_add (osd_button->priv->auto_off,
+ (GSourceFunc) gsd_wacom_osd_button_timer,
+ osd_button);
+ } else {
+ /* Whereas for other buttons, apply the change straight away */
+ osd_button->priv->active = active;
+ }
+
+ if (previous_state != osd_button->priv->active)
+ gsd_wacom_osd_button_redraw (osd_button);
+}
+
+static GsdWacomOSDButton *
+gsd_wacom_osd_button_new (GtkWidget *widget,
+ gchar *id)
+{
+ GsdWacomOSDButton *osd_button;
+
+ osd_button = GSD_WACOM_OSD_BUTTON (g_object_new (GSD_TYPE_WACOM_OSD_BUTTON,
+ "id", id,
+ NULL));
+ osd_button->priv->widget = widget;
+
+ 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_CLASS:
+ gsd_wacom_osd_button_set_class (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;
+ case PROP_OSD_BUTTON_AUTO_OFF:
+ gsd_wacom_osd_button_set_auto_off (osd_button, g_value_get_uint (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_CLASS:
+ g_value_set_string (value, osd_button->priv->class);
+ 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;
+ case PROP_OSD_BUTTON_AUTO_OFF:
+ g_value_set_uint (value, osd_button->priv->auto_off);
+ 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_CLASS,
+ g_param_spec_string ("class",
+ "Button Class",
+ "The Wacom Button Class",
+ "",
+ 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_object_class_install_property (object_class,
+ PROP_OSD_BUTTON_AUTO_OFF,
+ g_param_spec_uint ("auto-off",
+ "Auto Off",
+ "Timeout before button disables itself automatically",
+ 0,
+ G_MAXUINT,
+ 0, /* disabled by default */
+ 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;
+
+ if (priv->timeout > 0)
+ g_source_remove (priv->timeout);
+ g_clear_pointer (&priv->id, g_free);
+ g_clear_pointer (&priv->class, g_free);
+ g_clear_pointer (&priv->label, g_free);
+
+ 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;
+ /* We only support left-handed/right-handed */
+ case GSD_WACOM_ROTATION_CCW:
+ case GSD_WACOM_ROTATION_CW:
+ default:
+ break;
+ }
+ /* fallback */
+ return position;
+}
+
+static void
+gsd_wacom_osd_button_draw_label (GsdWacomOSDButton *osd_button,
+ GtkStyleContext *style_context,
+ PangoContext *pango_context,
+ cairo_t *cr,
+ GsdWacomRotation rotation)
+{
+ GsdWacomOSDButtonPrivate *priv;
+ PangoLayout *layout;
+ PangoRectangle logical_rect;
+ GsdWacomTabletButtonPos actual_position;
+ double lx, ly;
+ gchar *markup;
+
+ g_return_if_fail (GSD_IS_WACOM_OSD_BUTTON (osd_button));
+
+ priv = osd_button->priv;
+
+ actual_position = get_actual_position (priv->position, rotation);
+ layout = pango_layout_new (pango_context);
+ if (priv->active)
+ markup = g_strdup_printf ("<span foreground=\"" ACTIVE_COLOR "\" weight=\"normal\">%s</span>", priv->label);
+ else
+ markup = g_strdup_printf ("<span foreground=\"" INACTIVE_COLOR "\" weight=\"normal\">%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_LEFT:
+ pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT);
+ lx = priv->label_x + logical_rect.x;
+ ly = priv->label_y + logical_rect.y - get_pango_vertical_offset (layout);
+ break;
+ case WACOM_TABLET_BUTTON_POS_RIGHT:
+ pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
+ lx = priv->label_x + logical_rect.x - logical_rect.width;
+ ly = priv->label_y + logical_rect.y - get_pango_vertical_offset (layout);
+ break;
+ case WACOM_TABLET_BUTTON_POS_TOP:
+ pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
+ lx = priv->label_x + logical_rect.x - logical_rect.width / 2;
+ ly = priv->label_y + logical_rect.y;
+ break;
+ case WACOM_TABLET_BUTTON_POS_BOTTOM:
+ pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
+ lx = priv->label_x + logical_rect.x - logical_rect.width / 2;
+ ly = priv->label_y + logical_rect.y - logical_rect.height;
+ break;
+ default:
+ g_warning ("Unhandled button position");
+ pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
+ lx = priv->label_x + logical_rect.x - logical_rect.width / 2;
+ ly = priv->label_y + logical_rect.y - logical_rect.height / 2;
+ 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
+{
+ RsvgHandle *handle;
+ GsdWacomDevice *pad;
+ GsdWacomRotation rotation;
+ GdkRectangle screen_area;
+ GdkRectangle monitor_area;
+ GdkRectangle tablet_area;
+ char *message;
+ 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_update (GsdWacomOSDWindow *osd_window)
+{
+ GError *error = NULL;
+ gchar *width, *height;
+ gchar *buttons_section;
+ gchar *css_string;
+ const gchar *layout_file;
+ GBytes *css_data;
+ guint i;
+ GList *l;
+
+ g_return_if_fail (GSD_IS_WACOM_OSD_WINDOW (osd_window));
+ g_return_if_fail (GSD_IS_WACOM_DEVICE (osd_window->priv->pad));
+
+ css_data = g_resources_lookup_data (RES_PATH "tablet-layout.css", 0, &error);
+ if (error != NULL) {
+ g_printerr ("GResource error: %s\n", error->message);
+ g_clear_pointer (&error, g_error_free);
+ }
+ if (css_data == NULL)
+ return;
+ css_string = g_strdup ((gchar *) g_bytes_get_data (css_data, NULL));
+ g_bytes_unref(css_data);
+
+ width = g_strdup_printf ("%d", osd_window->priv->tablet_area.width);
+ replace_string (&css_string, "layout_width", width);
+ g_free (width);
+
+ height = g_strdup_printf ("%d", osd_window->priv->tablet_area.height);
+ replace_string (&css_string, "layout_height", height);
+ g_free (height);
+
+ /* Build the buttons section */
+ buttons_section = g_strdup ("");
+ for (l = osd_window->priv->buttons; l != NULL; l = l->next) {
+ GsdWacomOSDButton *osd_button = l->data;
+
+ if (osd_button->priv->active) {
+ buttons_section = g_strconcat (buttons_section,
+ ".", osd_button->priv->class, " {\n"
+ " stroke: active_color !important;\n"
+ " fill: active_color !important;\n"
+ " }\n",
+ NULL);
+ }
+ }
+ replace_string (&css_string, "buttons_section", buttons_section);
+ g_free (buttons_section);
+
+ for (i = 0; i < G_N_ELEMENTS (css_color_table); i++)
+ replace_string (&css_string,
+ css_color_table[i].color_name,
+ css_color_table[i].color_value);
+
+ layout_file = gsd_wacom_device_get_layout_path (osd_window->priv->pad);
+ replace_string (&css_string, "layout_file", layout_file);
+
+ /* Render the SVG with the CSS applied */
+ if (osd_window->priv->handle)
+ g_object_unref (osd_window->priv->handle);
+ osd_window->priv->handle = rsvg_handle_new_from_data ((guint8 *) css_string,
+ strlen (css_string),
+ &error);
+ if (error != NULL) {
+ g_debug ("CSS applied:\n%s\n", css_string);
+ g_printerr ("RSVG error: %s\n", error->message);
+ g_clear_pointer (&error, g_error_free);
+ }
+ g_free (css_string);
+}
+
+static void
+gsd_wacom_osd_window_draw_message (GsdWacomOSDWindow *osd_window,
+ GtkStyleContext *style_context,
+ PangoContext *pango_context,
+ cairo_t *cr)
+{
+ GdkRectangle *monitor_area = &osd_window->priv->monitor_area;
+ PangoRectangle logical_rect;
+ PangoLayout *layout;
+ char *markup;
+ double x;
+ double y;
+
+ if (osd_window->priv->message == NULL)
+ return;
+
+ 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 = (monitor_area->width - logical_rect.width) / 2 + logical_rect.x;
+ y = (monitor_area->height - logical_rect.height) / 2 + logical_rect.y;
+
+ gtk_render_layout (style_context, cr, x, y, layout);
+ g_object_unref (layout);
+}
+
+static void
+gsd_wacom_osd_window_draw_labels (GsdWacomOSDWindow *osd_window,
+ GtkStyleContext *style_context,
+ PangoContext *pango_context,
+ cairo_t *cr)
+{
+ GList *l;
+
+ for (l = osd_window->priv->buttons; l != NULL; l = l->next) {
+ GsdWacomOSDButton *osd_button = l->data;
+
+ gsd_wacom_osd_button_draw_label (osd_button,
+ style_context,
+ pango_context,
+ cr,
+ osd_window->priv->rotation);
+ }
+}
+
+static void
+gsd_wacom_osd_window_place_buttons (GsdWacomOSDWindow *osd_window,
+ cairo_t *cr)
+{
+ GList *l;
+
+ g_return_if_fail (GSD_IS_WACOM_OSD_WINDOW (osd_window));
+
+ for (l = osd_window->priv->buttons; l != NULL; l = l->next) {
+ GsdWacomOSDButton *osd_button = l->data;
+ double label_x, label_y;
+ gchar *sub;
+
+ sub = gsd_wacom_osd_button_get_label_class (osd_button);
+ if (!get_sub_location (osd_window->priv->handle, sub, cr, &label_x, &label_y)) {
+ g_warning ("Failed to retrieve %s position", sub);
+ g_free (sub);
+ continue;
+ }
+ g_free (sub);
+ gsd_wacom_osd_button_set_location (osd_button, label_x, label_y);
+ }
+}
+
+/* Note: this function does modify the given cairo context */
+static void
+gsd_wacom_osd_window_adjust_cairo (GsdWacomOSDWindow *osd_window,
+ cairo_t *cr)
+{
+ double scale, twidth, theight;
+ GdkRectangle *tablet_area = &osd_window->priv->tablet_area;
+ GdkRectangle *screen_area = &osd_window->priv->screen_area;
+ GdkRectangle *monitor_area = &osd_window->priv->monitor_area;
+
+ /* Rotate */
+ cairo_rotate (cr, get_rotation_in_radian (osd_window->priv->rotation));
+
+ /* Scale to fit in window */
+ scale = MIN ((double) monitor_area->width / tablet_area->width,
+ (double) monitor_area->height / tablet_area->height);
+ cairo_scale (cr, scale, scale);
+
+ /* Center the result in window */
+ twidth = (double) tablet_area->width;
+ theight = (double) tablet_area->height;
+ cairo_user_to_device_distance (cr, &twidth, &theight);
+
+ twidth = ((double) monitor_area->width - twidth) / 2.0;
+ theight = ((double) monitor_area->height - theight) / 2.0;
+ cairo_device_to_user_distance (cr, &twidth, &theight);
+
+ twidth = twidth + (double) (monitor_area->x - screen_area->x);
+ theight = theight + (double) (monitor_area->y - screen_area->y);
+
+ cairo_translate (cr, twidth, theight);
+}
+
+static gboolean
+gsd_wacom_osd_window_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ GsdWacomOSDWindow *osd_window = GSD_WACOM_OSD_WINDOW (widget);
+
+ g_return_val_if_fail (GSD_IS_WACOM_OSD_WINDOW (osd_window), FALSE);
+ g_return_val_if_fail (GSD_IS_WACOM_DEVICE (osd_window->priv->pad), FALSE);
+
+ if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget))) {
+ GtkStyleContext *style_context;
+ PangoContext *pango_context;
+
+ style_context = gtk_widget_get_style_context (widget);
+ pango_context = gtk_widget_get_pango_context (widget);
+
+ cairo_set_source_rgba (cr, 0, 0, 0, BACK_OPACITY);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+ /* Save original matrix */
+ cairo_save (cr);
+
+ /* Apply new cairo transformation matrix */
+ gsd_wacom_osd_window_adjust_cairo (osd_window, cr);
+
+ /* And render the tablet layout */
+ gsd_wacom_osd_window_update(osd_window);
+ rsvg_handle_render_cairo (osd_window->priv->handle, cr);
+
+ gsd_wacom_osd_window_place_buttons (osd_window, cr);
+
+ /* Reset to original matrix */
+ cairo_restore (cr);
+
+ /* Draw button labels and message */
+ gsd_wacom_osd_window_draw_labels (osd_window,
+ style_context,
+ pango_context,
+ cr);
+ gsd_wacom_osd_window_draw_message (osd_window,
+ style_context,
+ pango_context,
+ cr);
+ }
+
+ return FALSE;
+}
+
+static gchar *
+get_escaped_accel_shortcut (const gchar *accel)
+{
+ guint keyval;
+ GdkModifierType mask;
+ gchar *str, *label;
+
+ if (accel == NULL || accel[0] == '\0')
+ return g_strdup (C_("Action type", "None"));
+
+ gtk_accelerator_parse (accel, &keyval, &mask);
+
+ str = gtk_accelerator_get_label (keyval, mask);
+ label = g_markup_printf_escaped (C_("Action type", "Send Keystroke %s"), str);
+ g_free (str);
+
+ return label;
+}
+
+static gchar *
+get_tablet_button_label_normal (GsdWacomDevice *device,
+ GsdWacomTabletButton *button)
+{
+ GsdWacomActionType type;
+ gchar *name, *str;
+
+ type = g_settings_get_enum (button->settings, ACTION_TYPE_KEY);
+ if (type == GSD_WACOM_ACTION_TYPE_NONE)
+ return g_strdup (C_("Action type", "None"));
+
+ if (type == GSD_WACOM_ACTION_TYPE_HELP)
+ return g_strdup (C_("Action type", "Show On-Screen Help"));
+
+ if (type == GSD_WACOM_ACTION_TYPE_SWITCH_MONITOR)
+ return g_strdup (C_("Action type", "Switch Monitor"));
+
+ str = g_settings_get_string (button->settings, CUSTOM_ACTION_KEY);
+ if (str == NULL || *str == '\0') {
+ g_free (str);
+ return g_strdup (C_("Action type", "None"));
+ }
+
+ name = get_escaped_accel_shortcut (str);
+ g_free (str);
+
+ return name;
+}
+
+static gchar *
+get_tablet_button_label_touch (GsdWacomDevice *device,
+ GsdWacomTabletButton *button,
+ GtkDirectionType dir)
+{
+ char **strv, *name, *str;
+ gint mode;
+
+
+ strv = g_settings_get_strv (button->settings, CUSTOM_ELEVATOR_ACTION_KEY);
+ name = NULL;
+
+ if (strv) {
+ if (g_strv_length (strv) >= 1 && dir == GTK_DIR_UP)
+ name = g_strdup (strv[0]);
+ else if (g_strv_length (strv) >= 2 && dir == GTK_DIR_DOWN)
+ name = g_strdup (strv[1]);
+ g_strfreev (strv);
+ }
+
+ str = get_escaped_accel_shortcut (name);
+ g_free (name);
+ name = str;
+
+ /* With multiple modes, also show the current mode for that action */
+ if (gsd_wacom_device_get_num_modes (device, button->group_id) > 1) {
+ mode = gsd_wacom_device_get_current_mode (device, button->group_id);
+ name = g_strdup_printf (_("Mode %d: %s"), mode, str);
+ g_free (str);
+ }
+
+ return name;
+}
+
+static gchar *
+get_tablet_button_label (GsdWacomDevice *device,
+ GsdWacomTabletButton *button,
+ GtkDirectionType dir)
+{
+ g_return_val_if_fail (button, NULL);
+
+ if (!button->settings)
+ goto out;
+
+ switch (button->type) {
+ case WACOM_TABLET_BUTTON_TYPE_NORMAL:
+ return get_tablet_button_label_normal (device, button);
+ break;
+ case WACOM_TABLET_BUTTON_TYPE_RING:
+ case WACOM_TABLET_BUTTON_TYPE_STRIP:
+ return get_tablet_button_label_touch (device, button, dir);
+ break;
+ case WACOM_TABLET_BUTTON_TYPE_HARDCODED:
+ default:
+ break;
+ }
+out:
+ return g_strdup (button->name);
+}
+
+static gchar*
+get_tablet_button_class_name (GsdWacomTabletButton *tablet_button,
+ GtkDirectionType dir)
+{
+ gchar *id;
+ gchar c;
+
+ id = tablet_button->id;
+ switch (tablet_button->type) {
+ case WACOM_TABLET_BUTTON_TYPE_RING:
+ if (id[0] == 'l') /* left-ring */
+ return g_strdup_printf ("Ring%s", (dir == GTK_DIR_UP ? "CCW" : "CW"));
+ if (id[0] == 'r') /* right-ring */
+ return g_strdup_printf ("Ring2%s", (dir == GTK_DIR_UP ? "CCW" : "CW"));
+ g_warning ("Unknown ring type '%s'", id);
+ return NULL;
+ break;
+ case WACOM_TABLET_BUTTON_TYPE_STRIP:
+ if (id[0] == 'l') /* left-strip */
+ return g_strdup_printf ("Strip%s", (dir == GTK_DIR_UP ? "Up" : "Down"));
+ if (id[0] == 'r') /* right-strip */
+ return g_strdup_printf ("Strip2%s", (dir == GTK_DIR_UP ? "Up" : "Down"));
+ g_warning ("Unknown strip type '%s'", id);
+ return NULL;
+ break;
+ case WACOM_TABLET_BUTTON_TYPE_NORMAL:
+ case WACOM_TABLET_BUTTON_TYPE_HARDCODED:
+ c = get_last_char (id);
+ return g_strdup_printf ("%c", g_ascii_toupper (c));
+ break;
+ default:
+ g_warning ("Unknown button type '%s'", id);
+ break;
+ }
+
+ return NULL;
+}
+
+static gchar*
+get_tablet_button_id_name (GsdWacomTabletButton *tablet_button,
+ GtkDirectionType dir)
+{
+ gchar *id;
+ gchar c;
+
+ id = tablet_button->id;
+ switch (tablet_button->type) {
+ case WACOM_TABLET_BUTTON_TYPE_RING:
+ return g_strconcat (id, (dir == GTK_DIR_UP ? "-ccw" : "-cw"), NULL);
+ break;
+ case WACOM_TABLET_BUTTON_TYPE_STRIP:
+ return g_strconcat (id, (dir == GTK_DIR_UP ? "-up" : "-down"), NULL);
+ break;
+ case WACOM_TABLET_BUTTON_TYPE_NORMAL:
+ case WACOM_TABLET_BUTTON_TYPE_HARDCODED:
+ c = get_last_char (id);
+ return g_strdup_printf ("%c", g_ascii_toupper (c));
+ break;
+ default:
+ g_warning ("Unknown button type '%s'", id);
+ break;
+ }
+
+ return NULL;
+}
+
+static gint
+get_elevator_current_mode (GsdWacomOSDWindow *osd_window,
+ GsdWacomTabletButton *elevator_button)
+{
+ GList *list, *l;
+ gint mode;
+
+ mode = 1;
+ /* Search in the list of buttons the corresponding
+ * mode-switch button and get the current mode
+ */
+ list = gsd_wacom_device_get_buttons (osd_window->priv->pad);
+ for (l = list; l != NULL; l = l->next) {
+ GsdWacomTabletButton *tablet_button = l->data;
+
+ if (tablet_button->type != WACOM_TABLET_BUTTON_TYPE_HARDCODED)
+ continue;
+ if (elevator_button->group_id != tablet_button->group_id)
+ continue;
+ mode = gsd_wacom_device_get_current_mode (osd_window->priv->pad,
+ tablet_button->group_id);
+ break;
+ }
+ g_list_free (list);
+
+ return mode;
+}
+
+static void
+gsd_wacom_osd_window_add_button_with_dir (GsdWacomOSDWindow *osd_window,
+ GsdWacomTabletButton *tablet_button,
+ guint timeout,
+ GtkDirectionType dir)
+{
+ GsdWacomOSDButton *osd_button;
+ gchar *str;
+
+ str = get_tablet_button_id_name (tablet_button, dir);
+ osd_button = gsd_wacom_osd_button_new (GTK_WIDGET (osd_window), str);
+ g_free (str);
+
+ str = get_tablet_button_class_name (tablet_button, dir);
+ gsd_wacom_osd_button_set_class (osd_button, str);
+ g_free (str);
+
+ str = get_tablet_button_label (osd_window->priv->pad, tablet_button, dir);
+ gsd_wacom_osd_button_set_label (osd_button, str);
+ g_free (str);
+
+ gsd_wacom_osd_button_set_button_type (osd_button, tablet_button->type);
+ gsd_wacom_osd_button_set_position (osd_button, tablet_button->pos);
+ gsd_wacom_osd_button_set_auto_off (osd_button, timeout);
+ osd_window->priv->buttons = g_list_append (osd_window->priv->buttons, osd_button);
+}
+
+static void
+gsd_wacom_osd_window_add_tablet_button (GsdWacomOSDWindow *osd_window,
+ GsdWacomTabletButton *tablet_button)
+{
+ gint mode;
+
+ switch (tablet_button->type) {
+ case WACOM_TABLET_BUTTON_TYPE_NORMAL:
+ case WACOM_TABLET_BUTTON_TYPE_HARDCODED:
+ gsd_wacom_osd_window_add_button_with_dir (osd_window,
+ tablet_button,
+ 0,
+ 0);
+ break;
+ case WACOM_TABLET_BUTTON_TYPE_RING:
+ case WACOM_TABLET_BUTTON_TYPE_STRIP:
+ mode = get_elevator_current_mode (osd_window, tablet_button);
+ if (tablet_button->idx != mode - 1)
+ break;
+
+ /* Add 2 buttons per elevator, one "Up"... */
+ gsd_wacom_osd_window_add_button_with_dir (osd_window,
+ tablet_button,
+ ELEVATOR_TIMEOUT,
+ GTK_DIR_UP);
+ /* ... and one "Down" */
+ gsd_wacom_osd_window_add_button_with_dir (osd_window,
+ tablet_button,
+ ELEVATOR_TIMEOUT,
+ GTK_DIR_DOWN);
+ break;
+ default:
+ g_warning ("Unknown button type");
+ break;
+ }
+}
+
+/*
+ * Returns the rotation to apply a device to get a representation 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;
+}
+
+static void
+gsd_wacom_osd_window_set_device (GsdWacomOSDWindow *osd_window,
+ GsdWacomDevice *device)
+{
+ GsdWacomRotation device_rotation;
+ GsdWacomRotation output_rotation;
+ GSettings *settings;
+ gint monitor;
+ GdkScreen *screen;
+ GList *list, *l;
+ gboolean status;
+
+ g_return_if_fail (GSD_IS_WACOM_OSD_WINDOW (osd_window));
+ g_return_if_fail (GSD_IS_WACOM_DEVICE (device));
+
+ /* If we had a layout previously handled, get rid of it */
+ if (osd_window->priv->handle)
+ g_object_unref (osd_window->priv->handle);
+ osd_window->priv->handle = NULL;
+
+ /* 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 */
+ screen = gdk_screen_get_default ();
+ monitor = gsd_wacom_device_get_display_monitor (device);
+ if (monitor == GSD_WACOM_SET_ALL_MONITORS) {
+ /* Covers the entire screen */
+ osd_window->priv->screen_area.x = 0;
+ osd_window->priv->screen_area.y = 0;
+ osd_window->priv->screen_area.width = gdk_screen_get_width (screen);
+ osd_window->priv->screen_area.height = gdk_screen_get_height (screen);
+ gdk_screen_get_monitor_geometry (screen, 0, &osd_window->priv->monitor_area);
+ } else {
+ gdk_screen_get_monitor_geometry (screen, monitor, &osd_window->priv->screen_area);
+ osd_window->priv->monitor_area = osd_window->priv->screen_area;
+ }
+ status = get_image_size (gsd_wacom_device_get_layout_path (device),
+ &osd_window->priv->tablet_area.width,
+ &osd_window->priv->tablet_area.height);
+ if (status == FALSE)
+ osd_window->priv->tablet_area = osd_window->priv->monitor_area;
+
+ /* 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 buttons */
+ list = gsd_wacom_device_get_buttons (device);
+ for (l = list; l != NULL; l = l->next) {
+ GsdWacomTabletButton *tablet_button = l->data;
+
+ gsd_wacom_osd_window_add_tablet_button (osd_window, tablet_button);
+ }
+ g_list_free (list);
+}
+
+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_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,
+ GsdWacomTabletButton *button,
+ GtkDirectionType dir,
+ gboolean active)
+{
+ GList *l;
+ gchar *id;
+
+ g_return_if_fail (GSD_IS_WACOM_OSD_WINDOW (osd_window));
+ g_return_if_fail (button != NULL);
+
+ id = get_tablet_button_id_name (button, dir);
+ for (l = osd_window->priv->buttons; l != NULL; l = l->next) {
+ GsdWacomOSDButton *osd_button = l->data;
+ if (g_strcmp0 (osd_button->priv->id, id) == 0)
+ gsd_wacom_osd_button_set_active (osd_button, active);
+ }
+ g_free (id);
+}
+
+GtkWidget *
+gsd_wacom_osd_window_new (GsdWacomDevice *pad,
+ const gchar *message)
+{
+ GsdWacomOSDWindow *osd_window;
+ GdkWindow *window;
+ GdkRGBA transparent;
+ GdkCursor *cursor;
+ GdkVisual *visual;
+ GdkScreen *screen;
+
+ osd_window = GSD_WACOM_OSD_WINDOW (g_object_new (GSD_TYPE_WACOM_OSD_WINDOW,
+ "wacom-device", pad,
+ "message", message,
+ "type", GTK_WINDOW_POPUP,
+ NULL));
+
+ gtk_widget_set_app_paintable (GTK_WIDGET (osd_window), TRUE);
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (osd_window));
+ visual = gdk_screen_get_rgba_visual (screen);
+ if (visual == NULL)
+ visual = gdk_screen_get_system_visual (screen);
+ gtk_widget_set_visual (GTK_WIDGET (osd_window), visual);
+ gtk_widget_realize (GTK_WIDGET (osd_window));
+
+ gtk_window_move (GTK_WINDOW (osd_window),
+ osd_window->priv->screen_area.x,
+ osd_window->priv->screen_area.y);
+ gtk_window_set_default_size (GTK_WINDOW (osd_window),
+ osd_window->priv->screen_area.width,
+ osd_window->priv->screen_area.height);
+
+ transparent.red = transparent.green = transparent.blue = 0.0;
+ transparent.alpha = BACK_OPACITY;
+ window = gtk_widget_get_window (GTK_WIDGET (osd_window));
+ gdk_window_set_background_rgba (window, &transparent);
+
+ cursor = gdk_cursor_new (GDK_BLANK_CURSOR);
+ gdk_window_set_cursor (window, cursor);
+ g_object_unref (cursor);
+
+ return GTK_WIDGET (osd_window);
+}
+
+static gboolean
+gsd_wacom_osd_window_map_event (GtkWidget *widget,
+ GdkEventAny *event)
+{
+ GsdWacomOSDWindow *osd_window = GSD_WACOM_OSD_WINDOW (widget);
+
+ g_return_val_if_fail (GSD_IS_WACOM_OSD_WINDOW (osd_window), FALSE);
+ g_return_val_if_fail (GSD_IS_WACOM_DEVICE (osd_window->priv->pad), FALSE);
+
+ set_grab_keyboard (gtk_widget_get_window (widget), TRUE);
+
+ return TRUE;
+}
+
+static gboolean
+gsd_wacom_osd_window_unmap_event (GtkWidget *widget,
+ GdkEventAny *event)
+{
+ GsdWacomOSDWindow *osd_window = GSD_WACOM_OSD_WINDOW (widget);
+
+ g_return_val_if_fail (GSD_IS_WACOM_OSD_WINDOW (osd_window), FALSE);
+ g_return_val_if_fail (GSD_IS_WACOM_DEVICE (osd_window->priv->pad), FALSE);
+
+ set_grab_keyboard (gtk_widget_get_window (widget), FALSE);
+
+ return TRUE;
+}
+
+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->map_event = gsd_wacom_osd_window_map_event;
+ widget_class->unmap_event = gsd_wacom_osd_window_unmap_event;
+
+ 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_CONSTRUCT_ONLY | 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_clear_object (&priv->handle);
+ g_clear_pointer (&priv->message, g_free);
+ 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..a567b9d
--- /dev/null
+++ b/plugins/wacom/gsd-wacom-osd-window.h
@@ -0,0 +1,61 @@
+/*
+ * 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 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;
+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,
+ GsdWacomTabletButton *button,
+ GtkDirectionType dir,
+ gboolean active);
+GtkWidget * gsd_wacom_osd_window_new (GsdWacomDevice *pad,
+ const gchar *message);
+
+#endif /* __GSD_WACOM_OSD_WINDOW_H */
diff --git a/plugins/wacom/tablet-layout.css b/plugins/wacom/tablet-layout.css
new file mode 100644
index 0000000..dde9d06
--- /dev/null
+++ b/plugins/wacom/tablet-layout.css
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ width="layout_width"
+ height="layout_height" >
+ <style type="text/css">
+ .Leader {
+ stroke-width: .5 !important;
+ stroke: dark_color;
+ fill: none !important;
+ }
+ .Button {
+ stroke-width: .25;
+ stroke: inactive_color;
+ fill: inactive_color;
+ }
+ buttons_section
+ .Leader {
+ fill: none !important;
+ }
+ .Label {
+ stroke: none !important;
+ stroke-width: .1 !important;
+ font-size: .1 !important;
+ fill: back_color !important;
+ }
+ .TouchStrip,.TouchRing {
+ stroke-width: .1 !important;
+ stroke: inactive_color !important;
+ fill: dark_color !important;
+ }
+ </style>
+ <xi:include href="layout_file" />
+</svg>
diff --git a/plugins/wacom/test-osd-window.c b/plugins/wacom/test-osd-window.c
new file mode 100644
index 0000000..8eba75a
--- /dev/null
+++ b/plugins/wacom/test-osd-window.c
@@ -0,0 +1,137 @@
+/*
+ * 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 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 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 (const char *tablet)
+{
+ GsdWacomDevice *device;
+ gchar *tool;
+
+ tool = g_strdup_printf ("%s pad", tablet);
+ device = gsd_wacom_device_create_fake (WACOM_TYPE_PAD, tablet, tool);
+ g_free (tool);
+
+ return device;
+}
+
+static gboolean
+on_key_release_event(GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer data)
+{
+ gtk_main_quit();
+
+ return FALSE;
+}
+
+int main(int argc, char** argv)
+{
+ GtkWidget *widget;
+ GError *error = NULL;
+ GOptionContext *context;
+ GsdWacomDevice *device;
+ gchar *message;
+ gchar *tablet = NULL;
+ const GOptionEntry entries[] = {
+ { "tablet", 't', 0, G_OPTION_ARG_STRING, &tablet, "Name of the tablet to show", "<string>"},
+ { "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);
+ g_option_context_add_group (context, gtk_get_option_group (TRUE));
+ g_option_context_set_help_enabled (context, TRUE);
+ if (g_option_context_parse (context, &argc, &argv, &error) == FALSE) {
+ g_print ("%s\n", error->message);
+ return 1;
+ }
+ g_option_context_free (context);
+
+ if (option_debug)
+ g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
+
+ if (tablet)
+ device = create_fake_device (tablet);
+ else
+ device = search_pad_device ();
+
+ if (device == NULL) {
+ g_print ("No pad device found, consider using --tablet\n");
+ return 1;
+ }
+
+ if (gsd_wacom_device_get_layout_path (device) == NULL) {
+ g_print ("This device has not layout available in libwacom\n");
+ return 1;
+ }
+
+ message = g_strdup_printf ("<big><b>%s</b></big>\n<i>(Press a key to exit)</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;
+}
diff --git a/plugins/wacom/wacom.gresource.xml b/plugins/wacom/wacom.gresource.xml
new file mode 100644
index 0000000..57ac200
--- /dev/null
+++ b/plugins/wacom/wacom.gresource.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/settings-daemon/plugins/wacom">
+ <file>tablet-layout.css</file>
+ </gresource>
+</gresources>
+
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 7eb291b..46dd915 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -44,6 +44,7 @@ plugins/smartcard/gsd-smartcard-manager.c
plugins/updates/gsd-updates-firmware.c
plugins/updates/gsd-updates-manager.c
plugins/wacom/gsd-wacom-device.c
+plugins/wacom/gsd-wacom-osd-window.c
plugins/wacom/org.gnome.settings-daemon.plugins.wacom.policy.in.in
plugins/xrandr/gsd-xrandr-manager.c
[type: gettext/ini]plugins/xrandr/xrandr.gnome-settings-plugin.in
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]