From d86992c2ea731194dbf9bb288fe1d5d78114f478 Mon Sep 17 00:00:00 2001 From: Olivier Fourdan 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 ("%s", 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 + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#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 ("%s", 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 ("%s", 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 + * + */ + +#ifndef __GSD_WACOM_OSD_WINDOW_H +#define __GSD_WACOM_OSD_WINDOW_H + +#include +#include +#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 + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#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 ("%s\nTest OSD application", 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