1 /*
2 * Copyright (C) 2012 Red Hat, Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA.
17 *
18 * Author: Olivier Fourdan <ofourdan@redhat.com>
19 *
20 */
21
22 #include "config.h"
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <glib/gi18n.h>
27 #include <gtk/gtk.h>
28 #include <cairo.h>
29 #include <librsvg/rsvg.h>
30
31 #include "csd-wacom-osd-window.h"
32 #include "csd-wacom-device.h"
33 #include "csd-enums.h"
34
35 #define ROTATION_KEY "rotation"
36 #define ACTION_TYPE_KEY "action-type"
37 #define CUSTOM_ACTION_KEY "custom-action"
38 #define CUSTOM_ELEVATOR_ACTION_KEY "custom-elevator-action"
39 #define RES_PATH "/org/cinnamon/settings-daemon/plugins/wacom/"
40
41 #define BACK_OPACITY 0.8
42 #define INACTIVE_COLOR "#ededed"
43 #define ACTIVE_COLOR "#729fcf"
44 #define STROKE_COLOR "#000000"
45 #define DARK_COLOR "#535353"
46 #define BACK_COLOR "#000000"
47
48 #define ELEVATOR_TIMEOUT 250 /* ms */
49
50 static struct {
51 const gchar *color_name;
52 const gchar *color_value;
53 } css_color_table[] = {
54 { "inactive_color", INACTIVE_COLOR },
55 { "active_color", ACTIVE_COLOR },
56 { "stroke_color", STROKE_COLOR },
57 { "dark_color", DARK_COLOR },
58 { "back_color", BACK_COLOR }
59 };
60
61 static gchar *
replace_string(gchar ** string,const gchar * search,const char * replacement)62 replace_string (gchar **string, const gchar *search, const char *replacement)
63 {
64 GRegex *regex;
65 gchar *res;
66
67 g_return_val_if_fail (*string != NULL, NULL);
68 g_return_val_if_fail (string != NULL, NULL);
69 g_return_val_if_fail (search != NULL, *string);
70 g_return_val_if_fail (replacement != NULL, *string);
71
72 regex = g_regex_new (search, 0, 0, NULL);
73 res = g_regex_replace_literal (regex, *string, -1, 0, replacement, 0, NULL);
74 g_regex_unref (regex);
75 /* The given string is freed and replaced by the resulting replacement */
76 g_free (*string);
77 *string = res;
78
79 return res;
80 }
81
82 static gchar
get_last_char(gchar * string)83 get_last_char (gchar *string)
84 {
85 size_t pos;
86
87 g_return_val_if_fail (string != NULL, '\0');
88 pos = strlen (string);
89 g_return_val_if_fail (pos > 0, '\0');
90
91 return string[pos - 1];
92 }
93
94 static double
get_rotation_in_radian(CsdWacomRotation rotation)95 get_rotation_in_radian (CsdWacomRotation rotation)
96 {
97 switch (rotation) {
98 case CSD_WACOM_ROTATION_NONE:
99 return 0.0;
100 break;
101 case CSD_WACOM_ROTATION_HALF:
102 return G_PI;
103 break;
104 /* We only support left-handed/right-handed */
105 case CSD_WACOM_ROTATION_CCW:
106 case CSD_WACOM_ROTATION_CW:
107 default:
108 break;
109 }
110
111 /* Fallback */
112 return 0.0;
113 }
114
115 static gboolean
get_sub_location(RsvgHandle * handle,const char * sub,cairo_t * cr,double * x,double * y)116 get_sub_location (RsvgHandle *handle,
117 const char *sub,
118 cairo_t *cr,
119 double *x,
120 double *y)
121 {
122 RsvgPositionData position;
123 double tx, ty;
124
125 if (!rsvg_handle_get_position_sub (handle, &position, sub)) {
126 g_warning ("Failed to retrieve '%s' position", sub);
127 return FALSE;
128 }
129
130 tx = (double) position.x;
131 ty = (double) position.y;
132 cairo_user_to_device (cr, &tx, &ty);
133
134 if (x)
135 *x = tx;
136 if (y)
137 *y = ty;
138
139 return TRUE;
140 }
141
142 static gboolean
get_image_size(const char * filename,int * width,int * height)143 get_image_size (const char *filename, int *width, int *height)
144 {
145 RsvgHandle *handle;
146 RsvgDimensionData dimensions;
147 GError* error = NULL;
148
149 if (filename == NULL)
150 return FALSE;
151
152 handle = rsvg_handle_new_from_file (filename, &error);
153 if (error != NULL) {
154 g_printerr ("%s\n", error->message);
155 g_error_free (error);
156 }
157 if (handle == NULL)
158 return FALSE;
159
160 /* Compute image size */
161 rsvg_handle_get_dimensions (handle, &dimensions);
162 g_object_unref (handle);
163
164 if (dimensions.width == 0 || dimensions.height == 0)
165 return FALSE;
166
167 if (width)
168 *width = dimensions.width;
169
170 if (height)
171 *height = dimensions.height;
172
173 return TRUE;
174 }
175
176 static int
get_pango_vertical_offset(PangoLayout * layout)177 get_pango_vertical_offset (PangoLayout *layout)
178 {
179 const PangoFontDescription *desc;
180 PangoContext *context;
181 PangoLanguage *language;
182 PangoFontMetrics *metrics;
183 int baseline;
184 int strikethrough;
185 int thickness;
186
187 context = pango_layout_get_context (layout);
188 language = pango_language_get_default ();
189 desc = pango_layout_get_font_description (layout);
190 metrics = pango_context_get_metrics (context, desc, language);
191
192 baseline = pango_layout_get_baseline (layout);
193 strikethrough = pango_font_metrics_get_strikethrough_position (metrics);
194 thickness = pango_font_metrics_get_underline_thickness (metrics);
195
196 return PANGO_PIXELS (baseline - strikethrough - thickness / 2);
197 }
198
199 #define CSD_TYPE_WACOM_OSD_BUTTON (csd_wacom_osd_button_get_type ())
200 #define CSD_WACOM_OSD_BUTTON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CSD_TYPE_WACOM_OSD_BUTTON, CsdWacomOSDButton))
201 #define CSD_WACOM_OSD_BUTTON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CSD_TYPE_WACOM_OSD_BUTTON, CsdWacomOSDButtonClass))
202 #define CSD_IS_WACOM_OSD_BUTTON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CSD_TYPE_WACOM_OSD_BUTTON))
203 #define CSD_IS_WACOM_OSD_BUTTON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CSD_TYPE_WACOM_OSD_BUTTON))
204 #define CSD_WACOM_OSD_BUTTON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CSD_TYPE_WACOM_OSD_BUTTON, CsdWacomOSDButtonClass))
205
206 typedef struct CsdWacomOSDButtonPrivate CsdWacomOSDButtonPrivate;
207
208 typedef struct {
209 GObject parent;
210 CsdWacomOSDButtonPrivate *priv;
211 } CsdWacomOSDButton;
212
213 typedef struct {
214 GObjectClass parent_class;
215 } CsdWacomOSDButtonClass;
216
217 GType csd_wacom_osd_button_get_type (void) G_GNUC_CONST;
218
219 enum {
220 PROP_OSD_BUTTON_0,
221 PROP_OSD_BUTTON_ID,
222 PROP_OSD_BUTTON_CLASS,
223 PROP_OSD_BUTTON_LABEL,
224 PROP_OSD_BUTTON_ACTIVE,
225 PROP_OSD_BUTTON_VISIBLE,
226 PROP_OSD_BUTTON_AUTO_OFF
227 };
228
229 #define CSD_WACOM_OSD_BUTTON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
230 CSD_TYPE_WACOM_OSD_BUTTON, \
231 CsdWacomOSDButtonPrivate))
232 #define MATCH_ID(b,s) (g_strcmp0 (b->priv->id, s) == 0)
233
234 struct CsdWacomOSDButtonPrivate {
235 GtkWidget *widget;
236 char *id;
237 char *class;
238 char *label;
239 double label_x;
240 double label_y;
241 CsdWacomTabletButtonType type;
242 CsdWacomTabletButtonPos position;
243 gboolean active;
244 gboolean visible;
245 guint auto_off;
246 guint timeout;
247 };
248
249 static void csd_wacom_osd_button_class_init (CsdWacomOSDButtonClass *klass);
250 static void csd_wacom_osd_button_init (CsdWacomOSDButton *osd_button);
251 static void csd_wacom_osd_button_finalize (GObject *object);
252
G_DEFINE_TYPE(CsdWacomOSDButton,csd_wacom_osd_button,G_TYPE_OBJECT)253 G_DEFINE_TYPE (CsdWacomOSDButton, csd_wacom_osd_button, G_TYPE_OBJECT)
254
255 static void
256 csd_wacom_osd_button_set_id (CsdWacomOSDButton *osd_button,
257 const gchar *id)
258 {
259 g_return_if_fail (CSD_IS_WACOM_OSD_BUTTON (osd_button));
260
261 osd_button->priv->id = g_strdup (id);
262 }
263
264 static void
csd_wacom_osd_button_set_class(CsdWacomOSDButton * osd_button,const gchar * class)265 csd_wacom_osd_button_set_class (CsdWacomOSDButton *osd_button,
266 const gchar *class)
267 {
268 g_return_if_fail (CSD_IS_WACOM_OSD_BUTTON (osd_button));
269
270 osd_button->priv->class = g_strdup (class);
271 }
272
273 static gchar*
csd_wacom_osd_button_get_label_class(CsdWacomOSDButton * osd_button)274 csd_wacom_osd_button_get_label_class (CsdWacomOSDButton *osd_button)
275 {
276 gchar *label_class;
277
278 label_class = g_strconcat ("#Label", osd_button->priv->class, NULL);
279
280 return (label_class);
281 }
282
283 static void
csd_wacom_osd_button_set_label(CsdWacomOSDButton * osd_button,const gchar * str)284 csd_wacom_osd_button_set_label (CsdWacomOSDButton *osd_button,
285 const gchar *str)
286 {
287 g_return_if_fail (CSD_IS_WACOM_OSD_BUTTON (osd_button));
288
289 g_free (osd_button->priv->label);
290 osd_button->priv->label = g_strdup (str ? str : "");
291 }
292
293 static void
csd_wacom_osd_button_set_button_type(CsdWacomOSDButton * osd_button,CsdWacomTabletButtonType type)294 csd_wacom_osd_button_set_button_type (CsdWacomOSDButton *osd_button,
295 CsdWacomTabletButtonType type)
296 {
297 g_return_if_fail (CSD_IS_WACOM_OSD_BUTTON (osd_button));
298
299 osd_button->priv->type = type;
300 }
301
302 static void
csd_wacom_osd_button_set_position(CsdWacomOSDButton * osd_button,CsdWacomTabletButtonPos position)303 csd_wacom_osd_button_set_position (CsdWacomOSDButton *osd_button,
304 CsdWacomTabletButtonPos position)
305 {
306 g_return_if_fail (CSD_IS_WACOM_OSD_BUTTON (osd_button));
307
308 osd_button->priv->position = position;
309 }
310
311 static void
csd_wacom_osd_button_set_location(CsdWacomOSDButton * osd_button,double x,double y)312 csd_wacom_osd_button_set_location (CsdWacomOSDButton *osd_button,
313 double x,
314 double y)
315 {
316 g_return_if_fail (CSD_IS_WACOM_OSD_BUTTON (osd_button));
317
318 osd_button->priv->label_x = x;
319 osd_button->priv->label_y = y;
320 }
321
322 static void
csd_wacom_osd_button_set_auto_off(CsdWacomOSDButton * osd_button,guint timeout)323 csd_wacom_osd_button_set_auto_off (CsdWacomOSDButton *osd_button,
324 guint timeout)
325 {
326 g_return_if_fail (CSD_IS_WACOM_OSD_BUTTON (osd_button));
327
328 osd_button->priv->auto_off = timeout;
329 }
330
331 static void
csd_wacom_osd_button_redraw(CsdWacomOSDButton * osd_button)332 csd_wacom_osd_button_redraw (CsdWacomOSDButton *osd_button)
333 {
334 GdkWindow *window;
335
336 g_return_if_fail (GTK_IS_WIDGET (osd_button->priv->widget));
337
338 window = gtk_widget_get_window (GTK_WIDGET (osd_button->priv->widget));
339 gdk_window_invalidate_rect (window, NULL, FALSE);
340 }
341
342 static gboolean
csd_wacom_osd_button_timer(CsdWacomOSDButton * osd_button)343 csd_wacom_osd_button_timer (CsdWacomOSDButton *osd_button)
344 {
345 /* Auto de-activate the button */
346 osd_button->priv->active = FALSE;
347 csd_wacom_osd_button_redraw (osd_button);
348
349 return FALSE;
350 }
351
352 static void
csd_wacom_osd_button_set_active(CsdWacomOSDButton * osd_button,gboolean active)353 csd_wacom_osd_button_set_active (CsdWacomOSDButton *osd_button,
354 gboolean active)
355 {
356 gboolean previous_state;
357
358 g_return_if_fail (CSD_IS_WACOM_OSD_BUTTON (osd_button));
359
360 previous_state = osd_button->priv->active;
361 if (osd_button->priv->auto_off > 0) {
362 /* For auto-off buttons, apply only if active, de-activation is done in the timeout */
363 if (active == TRUE)
364 osd_button->priv->active = active;
365
366 if (osd_button->priv->timeout) {
367 g_source_remove (osd_button->priv->timeout);
368 osd_button->priv->timeout = 0;
369 }
370 osd_button->priv->timeout = g_timeout_add (osd_button->priv->auto_off,
371 (GSourceFunc) csd_wacom_osd_button_timer,
372 osd_button);
373 } else {
374 /* Whereas for other buttons, apply the change straight away */
375 osd_button->priv->active = active;
376 }
377
378 if (previous_state != osd_button->priv->active)
379 csd_wacom_osd_button_redraw (osd_button);
380 }
381
382 static void
csd_wacom_osd_button_set_visible(CsdWacomOSDButton * osd_button,gboolean visible)383 csd_wacom_osd_button_set_visible (CsdWacomOSDButton *osd_button,
384 gboolean visible)
385 {
386 g_return_if_fail (CSD_IS_WACOM_OSD_BUTTON (osd_button));
387
388 osd_button->priv->visible = visible;
389 }
390
391 static CsdWacomOSDButton *
csd_wacom_osd_button_new(GtkWidget * widget,gchar * id)392 csd_wacom_osd_button_new (GtkWidget *widget,
393 gchar *id)
394 {
395 CsdWacomOSDButton *osd_button;
396
397 osd_button = CSD_WACOM_OSD_BUTTON (g_object_new (CSD_TYPE_WACOM_OSD_BUTTON,
398 "id", id,
399 NULL));
400 osd_button->priv->widget = widget;
401
402 return osd_button;
403 }
404
405 static void
csd_wacom_osd_button_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)406 csd_wacom_osd_button_set_property (GObject *object,
407 guint prop_id,
408 const GValue *value,
409 GParamSpec *pspec)
410 {
411 CsdWacomOSDButton *osd_button;
412
413 osd_button = CSD_WACOM_OSD_BUTTON (object);
414
415 switch (prop_id) {
416 case PROP_OSD_BUTTON_ID:
417 csd_wacom_osd_button_set_id (osd_button, g_value_get_string (value));
418 break;
419 case PROP_OSD_BUTTON_CLASS:
420 csd_wacom_osd_button_set_class (osd_button, g_value_get_string (value));
421 break;
422 case PROP_OSD_BUTTON_LABEL:
423 csd_wacom_osd_button_set_label (osd_button, g_value_get_string (value));
424 break;
425 case PROP_OSD_BUTTON_ACTIVE:
426 csd_wacom_osd_button_set_active (osd_button, g_value_get_boolean (value));
427 break;
428 case PROP_OSD_BUTTON_VISIBLE:
429 csd_wacom_osd_button_set_visible (osd_button, g_value_get_boolean (value));
430 break;
431 case PROP_OSD_BUTTON_AUTO_OFF:
432 csd_wacom_osd_button_set_auto_off (osd_button, g_value_get_uint (value));
433 break;
434 default:
435 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
436 break;
437 }
438 }
439
440 static void
csd_wacom_osd_button_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)441 csd_wacom_osd_button_get_property (GObject *object,
442 guint prop_id,
443 GValue *value,
444 GParamSpec *pspec)
445 {
446 CsdWacomOSDButton *osd_button;
447
448 osd_button = CSD_WACOM_OSD_BUTTON (object);
449
450 switch (prop_id) {
451 case PROP_OSD_BUTTON_ID:
452 g_value_set_string (value, osd_button->priv->id);
453 break;
454 case PROP_OSD_BUTTON_CLASS:
455 g_value_set_string (value, osd_button->priv->class);
456 break;
457 case PROP_OSD_BUTTON_LABEL:
458 g_value_set_string (value, osd_button->priv->label);
459 break;
460 case PROP_OSD_BUTTON_ACTIVE:
461 g_value_set_boolean (value, osd_button->priv->active);
462 break;
463 case PROP_OSD_BUTTON_VISIBLE:
464 g_value_set_boolean (value, osd_button->priv->visible);
465 break;
466 case PROP_OSD_BUTTON_AUTO_OFF:
467 g_value_set_uint (value, osd_button->priv->auto_off);
468 break;
469 default:
470 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
471 break;
472 }
473 }
474
475 static void
csd_wacom_osd_button_class_init(CsdWacomOSDButtonClass * klass)476 csd_wacom_osd_button_class_init (CsdWacomOSDButtonClass *klass)
477 {
478 GObjectClass *object_class = G_OBJECT_CLASS (klass);
479
480 object_class->set_property = csd_wacom_osd_button_set_property;
481 object_class->get_property = csd_wacom_osd_button_get_property;
482 object_class->finalize = csd_wacom_osd_button_finalize;
483
484 g_object_class_install_property (object_class,
485 PROP_OSD_BUTTON_ID,
486 g_param_spec_string ("id",
487 "Button Id",
488 "The Wacom Button ID",
489 "",
490 G_PARAM_READWRITE));
491 g_object_class_install_property (object_class,
492 PROP_OSD_BUTTON_CLASS,
493 g_param_spec_string ("class",
494 "Button Class",
495 "The Wacom Button Class",
496 "",
497 G_PARAM_READWRITE));
498 g_object_class_install_property (object_class,
499 PROP_OSD_BUTTON_LABEL,
500 g_param_spec_string ("label",
501 "Label",
502 "The button label",
503 "",
504 G_PARAM_READWRITE));
505 g_object_class_install_property (object_class,
506 PROP_OSD_BUTTON_ACTIVE,
507 g_param_spec_boolean ("active",
508 "Active",
509 "Whether the button is active",
510 FALSE,
511 G_PARAM_READWRITE));
512 g_object_class_install_property (object_class,
513 PROP_OSD_BUTTON_VISIBLE,
514 g_param_spec_boolean ("visible",
515 "Visible",
516 "Whether the button is visible",
517 TRUE,
518 G_PARAM_READWRITE));
519 g_object_class_install_property (object_class,
520 PROP_OSD_BUTTON_AUTO_OFF,
521 g_param_spec_uint ("auto-off",
522 "Auto Off",
523 "Timeout before button disables itself automatically",
524 0,
525 G_MAXUINT,
526 0, /* disabled by default */
527 G_PARAM_READWRITE));
528
529 g_type_class_add_private (klass, sizeof (CsdWacomOSDButtonPrivate));
530 }
531
532 static void
csd_wacom_osd_button_init(CsdWacomOSDButton * osd_button)533 csd_wacom_osd_button_init (CsdWacomOSDButton *osd_button)
534 {
535 osd_button->priv = CSD_WACOM_OSD_BUTTON_GET_PRIVATE (osd_button);
536 }
537
538 static void
csd_wacom_osd_button_finalize(GObject * object)539 csd_wacom_osd_button_finalize (GObject *object)
540 {
541 CsdWacomOSDButton *osd_button;
542 CsdWacomOSDButtonPrivate *priv;
543
544 g_return_if_fail (object != NULL);
545 g_return_if_fail (CSD_IS_WACOM_OSD_BUTTON (object));
546
547 osd_button = CSD_WACOM_OSD_BUTTON (object);
548
549 g_return_if_fail (osd_button->priv != NULL);
550
551 priv = osd_button->priv;
552
553 if (priv->timeout > 0) {
554 g_source_remove (priv->timeout);
555 priv->timeout = 0;
556 }
557 g_clear_pointer (&priv->id, g_free);
558 g_clear_pointer (&priv->class, g_free);
559 g_clear_pointer (&priv->label, g_free);
560
561 G_OBJECT_CLASS (csd_wacom_osd_button_parent_class)->finalize (object);
562 }
563
564 /* Compute the new actual position once rotation is applied */
565 static CsdWacomTabletButtonPos
get_actual_position(CsdWacomTabletButtonPos position,CsdWacomRotation rotation)566 get_actual_position (CsdWacomTabletButtonPos position,
567 CsdWacomRotation rotation)
568 {
569 switch (rotation) {
570 case CSD_WACOM_ROTATION_NONE:
571 return position;
572 break;
573 case CSD_WACOM_ROTATION_HALF:
574 if (position == WACOM_TABLET_BUTTON_POS_LEFT)
575 return WACOM_TABLET_BUTTON_POS_RIGHT;
576 if (position == WACOM_TABLET_BUTTON_POS_RIGHT)
577 return WACOM_TABLET_BUTTON_POS_LEFT;
578 if (position == WACOM_TABLET_BUTTON_POS_TOP)
579 return WACOM_TABLET_BUTTON_POS_BOTTOM;
580 if (position == WACOM_TABLET_BUTTON_POS_BOTTOM)
581 return WACOM_TABLET_BUTTON_POS_TOP;
582 break;
583 /* We only support left-handed/right-handed */
584 case CSD_WACOM_ROTATION_CCW:
585 case CSD_WACOM_ROTATION_CW:
586 default:
587 break;
588 }
589 /* fallback */
590 return position;
591 }
592
593 static void
csd_wacom_osd_button_draw_label(CsdWacomOSDButton * osd_button,GtkStyleContext * style_context,PangoContext * pango_context,cairo_t * cr,CsdWacomRotation rotation)594 csd_wacom_osd_button_draw_label (CsdWacomOSDButton *osd_button,
595 GtkStyleContext *style_context,
596 PangoContext *pango_context,
597 cairo_t *cr,
598 CsdWacomRotation rotation)
599 {
600 CsdWacomOSDButtonPrivate *priv;
601 PangoLayout *layout;
602 PangoRectangle logical_rect;
603 CsdWacomTabletButtonPos actual_position;
604 double lx, ly;
605 gchar *markup;
606
607 g_return_if_fail (CSD_IS_WACOM_OSD_BUTTON (osd_button));
608
609 priv = osd_button->priv;
610 if (priv->visible == FALSE)
611 return;
612
613 actual_position = get_actual_position (priv->position, rotation);
614 layout = pango_layout_new (pango_context);
615 if (priv->active)
616 markup = g_strdup_printf ("<span foreground=\"" ACTIVE_COLOR "\" weight=\"normal\">%s</span>", priv->label);
617 else
618 markup = g_strdup_printf ("<span foreground=\"" INACTIVE_COLOR "\" weight=\"normal\">%s</span>", priv->label);
619 pango_layout_set_markup (layout, markup, -1);
620 g_free (markup);
621
622 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
623 switch (actual_position) {
624 case WACOM_TABLET_BUTTON_POS_LEFT:
625 pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT);
626 lx = priv->label_x + logical_rect.x;
627 ly = priv->label_y + logical_rect.y - get_pango_vertical_offset (layout);
628 break;
629 case WACOM_TABLET_BUTTON_POS_RIGHT:
630 pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
631 lx = priv->label_x + logical_rect.x - logical_rect.width;
632 ly = priv->label_y + logical_rect.y - get_pango_vertical_offset (layout);
633 break;
634 case WACOM_TABLET_BUTTON_POS_TOP:
635 pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
636 lx = priv->label_x + logical_rect.x - logical_rect.width / 2;
637 ly = priv->label_y + logical_rect.y;
638 break;
639 case WACOM_TABLET_BUTTON_POS_BOTTOM:
640 pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
641 lx = priv->label_x + logical_rect.x - logical_rect.width / 2;
642 ly = priv->label_y + logical_rect.y - logical_rect.height;
643 break;
644 default:
645 g_warning ("Unhandled button position");
646 pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
647 lx = priv->label_x + logical_rect.x - logical_rect.width / 2;
648 ly = priv->label_y + logical_rect.y - logical_rect.height / 2;
649 break;
650 }
651 gtk_render_layout (style_context, cr, lx, ly, layout);
652 g_object_unref (layout);
653 }
654
655 enum {
656 PROP_OSD_WINDOW_0,
657 PROP_OSD_WINDOW_MESSAGE,
658 PROP_OSD_WINDOW_CSD_WACOM_DEVICE
659 };
660
661 #define CSD_WACOM_OSD_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
662 CSD_TYPE_WACOM_OSD_WINDOW, \
663 CsdWacomOSDWindowPrivate))
664
665 struct CsdWacomOSDWindowPrivate
666 {
667 RsvgHandle *handle;
668 CsdWacomDevice *pad;
669 CsdWacomRotation rotation;
670 GdkRectangle screen_area;
671 GdkRectangle monitor_area;
672 GdkRectangle tablet_area;
673 char *message;
674 GList *buttons;
675 };
676
677 static void csd_wacom_osd_window_class_init (CsdWacomOSDWindowClass *klass);
678 static void csd_wacom_osd_window_init (CsdWacomOSDWindow *osd_window);
679 static void csd_wacom_osd_window_finalize (GObject *object);
680
G_DEFINE_TYPE(CsdWacomOSDWindow,csd_wacom_osd_window,GTK_TYPE_WINDOW)681 G_DEFINE_TYPE (CsdWacomOSDWindow, csd_wacom_osd_window, GTK_TYPE_WINDOW)
682
683 static RsvgHandle *
684 load_rsvg_with_base (const char *css_string,
685 const char *original_layout_path,
686 GError **error)
687 {
688 RsvgHandle *handle;
689 char *dirname;
690
691 handle = rsvg_handle_new ();
692
693 dirname = g_path_get_dirname (original_layout_path);
694 rsvg_handle_set_base_uri (handle, dirname);
695 g_free (dirname);
696
697 if (!rsvg_handle_write (handle,
698 (guint8 *) css_string,
699 strlen (css_string),
700 error)) {
701 g_object_unref (handle);
702 return NULL;
703 }
704 if (!rsvg_handle_close (handle, error)) {
705 g_object_unref (handle);
706 return NULL;
707 }
708
709 return handle;
710 }
711
712 static void
csd_wacom_osd_window_update(CsdWacomOSDWindow * osd_window)713 csd_wacom_osd_window_update (CsdWacomOSDWindow *osd_window)
714 {
715 GError *error = NULL;
716 gchar *width, *height;
717 gchar *buttons_section;
718 gchar *css_string;
719 const gchar *layout_file;
720 GBytes *css_data;
721 guint i;
722 GList *l;
723
724 g_return_if_fail (CSD_IS_WACOM_OSD_WINDOW (osd_window));
725 g_return_if_fail (CSD_IS_WACOM_DEVICE (osd_window->priv->pad));
726
727 css_data = g_resources_lookup_data (RES_PATH "tablet-layout.css", 0, &error);
728 if (error != NULL) {
729 g_printerr ("GResource error: %s\n", error->message);
730 g_clear_pointer (&error, g_error_free);
731 }
732 if (css_data == NULL)
733 return;
734 css_string = g_strdup ((gchar *) g_bytes_get_data (css_data, NULL));
735 g_bytes_unref(css_data);
736
737 width = g_strdup_printf ("%d", osd_window->priv->tablet_area.width);
738 replace_string (&css_string, "layout_width", width);
739 g_free (width);
740
741 height = g_strdup_printf ("%d", osd_window->priv->tablet_area.height);
742 replace_string (&css_string, "layout_height", height);
743 g_free (height);
744
745 /* Build the buttons section */
746 buttons_section = g_strdup ("");
747 for (l = osd_window->priv->buttons; l != NULL; l = l->next) {
748 CsdWacomOSDButton *osd_button = l->data;
749
750 if (osd_button->priv->visible == FALSE)
751 continue;
752
753 if (osd_button->priv->active) {
754 buttons_section = g_strconcat (buttons_section,
755 ".", osd_button->priv->class, " {\n"
756 " stroke: active_color !important;\n"
757 " fill: active_color !important;\n"
758 " }\n",
759 NULL);
760 }
761 }
762 replace_string (&css_string, "buttons_section", buttons_section);
763 g_free (buttons_section);
764
765 for (i = 0; i < G_N_ELEMENTS (css_color_table); i++)
766 replace_string (&css_string,
767 css_color_table[i].color_name,
768 css_color_table[i].color_value);
769
770 layout_file = csd_wacom_device_get_layout_path (osd_window->priv->pad);
771 replace_string (&css_string, "layout_file", layout_file);
772
773 /* Render the SVG with the CSS applied */
774 g_clear_object (&osd_window->priv->handle);
775 osd_window->priv->handle = load_rsvg_with_base (css_string, layout_file, &error);
776 if (osd_window->priv->handle == NULL) {
777 g_debug ("CSS applied:\n%s\n", css_string);
778 g_printerr ("RSVG error: %s\n", error->message);
779 g_clear_error (&error);
780 }
781 g_free (css_string);
782 }
783
784 static void
csd_wacom_osd_window_draw_message(CsdWacomOSDWindow * osd_window,GtkStyleContext * style_context,PangoContext * pango_context,cairo_t * cr)785 csd_wacom_osd_window_draw_message (CsdWacomOSDWindow *osd_window,
786 GtkStyleContext *style_context,
787 PangoContext *pango_context,
788 cairo_t *cr)
789 {
790 GdkRectangle *monitor_area = &osd_window->priv->monitor_area;
791 PangoRectangle logical_rect;
792 PangoLayout *layout;
793 char *markup;
794 double x;
795 double y;
796
797 if (osd_window->priv->message == NULL)
798 return;
799
800 layout = pango_layout_new (pango_context);
801 pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
802
803 markup = g_strdup_printf ("<span foreground=\"white\">%s</span>", osd_window->priv->message);
804 pango_layout_set_markup (layout, markup, -1);
805 g_free (markup);
806
807 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
808 x = (monitor_area->width - logical_rect.width) / 2 + logical_rect.x;
809 y = (monitor_area->height - logical_rect.height) / 2 + logical_rect.y;
810
811 gtk_render_layout (style_context, cr, x, y, layout);
812 g_object_unref (layout);
813 }
814
815 static void
csd_wacom_osd_window_draw_labels(CsdWacomOSDWindow * osd_window,GtkStyleContext * style_context,PangoContext * pango_context,cairo_t * cr)816 csd_wacom_osd_window_draw_labels (CsdWacomOSDWindow *osd_window,
817 GtkStyleContext *style_context,
818 PangoContext *pango_context,
819 cairo_t *cr)
820 {
821 GList *l;
822
823 for (l = osd_window->priv->buttons; l != NULL; l = l->next) {
824 CsdWacomOSDButton *osd_button = l->data;
825
826 if (osd_button->priv->visible == FALSE)
827 continue;
828
829 csd_wacom_osd_button_draw_label (osd_button,
830 style_context,
831 pango_context,
832 cr,
833 osd_window->priv->rotation);
834 }
835 }
836
837 static void
csd_wacom_osd_window_place_buttons(CsdWacomOSDWindow * osd_window,cairo_t * cr)838 csd_wacom_osd_window_place_buttons (CsdWacomOSDWindow *osd_window,
839 cairo_t *cr)
840 {
841 GList *l;
842
843 g_return_if_fail (CSD_IS_WACOM_OSD_WINDOW (osd_window));
844
845 for (l = osd_window->priv->buttons; l != NULL; l = l->next) {
846 CsdWacomOSDButton *osd_button = l->data;
847 double label_x, label_y;
848 gchar *sub;
849
850 sub = csd_wacom_osd_button_get_label_class (osd_button);
851 if (!get_sub_location (osd_window->priv->handle, sub, cr, &label_x, &label_y)) {
852 g_warning ("Failed to retrieve %s position", sub);
853 g_free (sub);
854 continue;
855 }
856 g_free (sub);
857 csd_wacom_osd_button_set_location (osd_button, label_x, label_y);
858 }
859 }
860
861 /* Note: this function does modify the given cairo context */
862 static void
csd_wacom_osd_window_adjust_cairo(CsdWacomOSDWindow * osd_window,cairo_t * cr)863 csd_wacom_osd_window_adjust_cairo (CsdWacomOSDWindow *osd_window,
864 cairo_t *cr)
865 {
866 double scale, twidth, theight;
867 GdkRectangle *tablet_area = &osd_window->priv->tablet_area;
868 GdkRectangle *screen_area = &osd_window->priv->screen_area;
869 GdkRectangle *monitor_area = &osd_window->priv->monitor_area;
870
871 /* Rotate */
872 cairo_rotate (cr, get_rotation_in_radian (osd_window->priv->rotation));
873
874 /* Scale to fit in window */
875 scale = MIN ((double) monitor_area->width / tablet_area->width,
876 (double) monitor_area->height / tablet_area->height);
877 cairo_scale (cr, scale, scale);
878
879 /* Center the result in window */
880 twidth = (double) tablet_area->width;
881 theight = (double) tablet_area->height;
882 cairo_user_to_device_distance (cr, &twidth, &theight);
883
884 twidth = ((double) monitor_area->width - twidth) / 2.0;
885 theight = ((double) monitor_area->height - theight) / 2.0;
886 cairo_device_to_user_distance (cr, &twidth, &theight);
887
888 twidth = twidth + (double) (monitor_area->x - screen_area->x);
889 theight = theight + (double) (monitor_area->y - screen_area->y);
890
891 cairo_translate (cr, twidth, theight);
892 }
893
894 static gboolean
csd_wacom_osd_window_draw(GtkWidget * widget,cairo_t * cr)895 csd_wacom_osd_window_draw (GtkWidget *widget,
896 cairo_t *cr)
897 {
898 CsdWacomOSDWindow *osd_window = CSD_WACOM_OSD_WINDOW (widget);
899
900 g_return_val_if_fail (CSD_IS_WACOM_OSD_WINDOW (osd_window), FALSE);
901 g_return_val_if_fail (CSD_IS_WACOM_DEVICE (osd_window->priv->pad), FALSE);
902
903 if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget))) {
904 GtkStyleContext *style_context;
905 PangoContext *pango_context;
906
907 style_context = gtk_widget_get_style_context (widget);
908 pango_context = gtk_widget_get_pango_context (widget);
909
910 cairo_set_source_rgba (cr, 0, 0, 0, BACK_OPACITY);
911 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
912 cairo_paint (cr);
913 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
914
915 /* Save original matrix */
916 cairo_save (cr);
917
918 /* Apply new cairo transformation matrix */
919 csd_wacom_osd_window_adjust_cairo (osd_window, cr);
920
921 /* And render the tablet layout */
922 csd_wacom_osd_window_update (osd_window);
923 rsvg_handle_render_cairo (osd_window->priv->handle, cr);
924
925 csd_wacom_osd_window_place_buttons (osd_window, cr);
926
927 /* Reset to original matrix */
928 cairo_restore (cr);
929
930 /* Draw button labels and message */
931 csd_wacom_osd_window_draw_labels (osd_window,
932 style_context,
933 pango_context,
934 cr);
935 csd_wacom_osd_window_draw_message (osd_window,
936 style_context,
937 pango_context,
938 cr);
939 }
940
941 return FALSE;
942 }
943
944 static gchar *
get_escaped_accel_shortcut(const gchar * accel)945 get_escaped_accel_shortcut (const gchar *accel)
946 {
947 guint keyval;
948 GdkModifierType mask;
949 gchar *str, *label;
950
951 if (accel == NULL || accel[0] == '\0')
952 return g_strdup (_("Do Nothing"));
953
954 gtk_accelerator_parse (accel, &keyval, &mask);
955
956 str = gtk_accelerator_get_label (keyval, mask);
957 label = g_markup_printf_escaped (_("Send Keystroke %s"), str);
958 g_free (str);
959
960 return label;
961 }
962
963 static gchar *
get_tablet_button_label_normal(CsdWacomDevice * device,CsdWacomTabletButton * button)964 get_tablet_button_label_normal (CsdWacomDevice *device,
965 CsdWacomTabletButton *button)
966 {
967 CsdWacomActionType type;
968 gchar *name, *str;
969
970 type = g_settings_get_enum (button->settings, ACTION_TYPE_KEY);
971 if (type == CSD_WACOM_ACTION_TYPE_NONE)
972 return g_strdup (_("Do Nothing"));
973
974 if (type == CSD_WACOM_ACTION_TYPE_HELP)
975 return g_strdup (_("Show On-Screen Help"));
976
977 if (type == CSD_WACOM_ACTION_TYPE_SWITCH_MONITOR)
978 return g_strdup (_("Switch Monitor"));
979
980 str = g_settings_get_string (button->settings, CUSTOM_ACTION_KEY);
981 if (str == NULL || *str == '\0') {
982 g_free (str);
983 return g_strdup (_("Do Nothing"));
984 }
985
986 name = get_escaped_accel_shortcut (str);
987 g_free (str);
988
989 return name;
990 }
991
992 static gchar *
get_tablet_button_label_touch(CsdWacomDevice * device,CsdWacomTabletButton * button,GtkDirectionType dir)993 get_tablet_button_label_touch (CsdWacomDevice *device,
994 CsdWacomTabletButton *button,
995 GtkDirectionType dir)
996 {
997 char **strv, *name, *str;
998
999 strv = g_settings_get_strv (button->settings, CUSTOM_ELEVATOR_ACTION_KEY);
1000 name = NULL;
1001
1002 if (strv) {
1003 if (g_strv_length (strv) >= 1 && dir == GTK_DIR_UP)
1004 name = g_strdup (strv[0]);
1005 else if (g_strv_length (strv) >= 2 && dir == GTK_DIR_DOWN)
1006 name = g_strdup (strv[1]);
1007 g_strfreev (strv);
1008 }
1009
1010 str = get_escaped_accel_shortcut (name);
1011 g_free (name);
1012 name = str;
1013
1014 /* With multiple modes, also show the current mode for that action */
1015 if (csd_wacom_device_get_num_modes (device, button->group_id) > 1) {
1016 name = g_strdup_printf (_("Mode %d: %s"), button->idx + 1, str);
1017 g_free (str);
1018 }
1019
1020 return name;
1021 }
1022
1023 static gchar *
get_tablet_button_label(CsdWacomDevice * device,CsdWacomTabletButton * button,GtkDirectionType dir)1024 get_tablet_button_label (CsdWacomDevice *device,
1025 CsdWacomTabletButton *button,
1026 GtkDirectionType dir)
1027 {
1028 g_return_val_if_fail (button, NULL);
1029
1030 if (!button->settings)
1031 goto out;
1032
1033 switch (button->type) {
1034 case WACOM_TABLET_BUTTON_TYPE_NORMAL:
1035 return get_tablet_button_label_normal (device, button);
1036 break;
1037 case WACOM_TABLET_BUTTON_TYPE_RING:
1038 case WACOM_TABLET_BUTTON_TYPE_STRIP:
1039 return get_tablet_button_label_touch (device, button, dir);
1040 break;
1041 case WACOM_TABLET_BUTTON_TYPE_HARDCODED:
1042 default:
1043 break;
1044 }
1045 out:
1046 return g_strdup (button->name);
1047 }
1048
1049 static gchar*
get_tablet_button_class_name(CsdWacomTabletButton * tablet_button,GtkDirectionType dir)1050 get_tablet_button_class_name (CsdWacomTabletButton *tablet_button,
1051 GtkDirectionType dir)
1052 {
1053 gchar *id;
1054 gchar c;
1055
1056 id = tablet_button->id;
1057 switch (tablet_button->type) {
1058 case WACOM_TABLET_BUTTON_TYPE_RING:
1059 if (id[0] == 'l') /* left-ring */
1060 return g_strdup_printf ("Ring%s", (dir == GTK_DIR_UP ? "CCW" : "CW"));
1061 if (id[0] == 'r') /* right-ring */
1062 return g_strdup_printf ("Ring2%s", (dir == GTK_DIR_UP ? "CCW" : "CW"));
1063 g_warning ("Unknown ring type '%s'", id);
1064 return NULL;
1065 break;
1066 case WACOM_TABLET_BUTTON_TYPE_STRIP:
1067 if (id[0] == 'l') /* left-strip */
1068 return g_strdup_printf ("Strip%s", (dir == GTK_DIR_UP ? "Up" : "Down"));
1069 if (id[0] == 'r') /* right-strip */
1070 return g_strdup_printf ("Strip2%s", (dir == GTK_DIR_UP ? "Up" : "Down"));
1071 g_warning ("Unknown strip type '%s'", id);
1072 return NULL;
1073 break;
1074 case WACOM_TABLET_BUTTON_TYPE_NORMAL:
1075 case WACOM_TABLET_BUTTON_TYPE_HARDCODED:
1076 c = get_last_char (id);
1077 return g_strdup_printf ("%c", g_ascii_toupper (c));
1078 break;
1079 default:
1080 g_warning ("Unknown button type '%s'", id);
1081 break;
1082 }
1083
1084 return NULL;
1085 }
1086
1087 static gchar*
get_tablet_button_id_name(CsdWacomTabletButton * tablet_button,GtkDirectionType dir)1088 get_tablet_button_id_name (CsdWacomTabletButton *tablet_button,
1089 GtkDirectionType dir)
1090 {
1091 gchar *id;
1092 gchar c;
1093
1094 id = tablet_button->id;
1095 switch (tablet_button->type) {
1096 case WACOM_TABLET_BUTTON_TYPE_RING:
1097 return g_strconcat (id, (dir == GTK_DIR_UP ? "-ccw" : "-cw"), NULL);
1098 break;
1099 case WACOM_TABLET_BUTTON_TYPE_STRIP:
1100 return g_strconcat (id, (dir == GTK_DIR_UP ? "-up" : "-down"), NULL);
1101 break;
1102 case WACOM_TABLET_BUTTON_TYPE_NORMAL:
1103 case WACOM_TABLET_BUTTON_TYPE_HARDCODED:
1104 c = get_last_char (id);
1105 return g_strdup_printf ("%c", g_ascii_toupper (c));
1106 break;
1107 default:
1108 g_warning ("Unknown button type '%s'", id);
1109 break;
1110 }
1111
1112 return NULL;
1113 }
1114
1115 static gint
get_elevator_current_mode(CsdWacomOSDWindow * osd_window,CsdWacomTabletButton * elevator_button)1116 get_elevator_current_mode (CsdWacomOSDWindow *osd_window,
1117 CsdWacomTabletButton *elevator_button)
1118 {
1119 GList *list, *l;
1120 gint mode;
1121
1122 mode = 1;
1123 /* Search in the list of buttons the corresponding
1124 * mode-switch button and get the current mode
1125 */
1126 list = csd_wacom_device_get_buttons (osd_window->priv->pad);
1127 for (l = list; l != NULL; l = l->next) {
1128 CsdWacomTabletButton *tablet_button = l->data;
1129
1130 if (tablet_button->type != WACOM_TABLET_BUTTON_TYPE_HARDCODED)
1131 continue;
1132 if (elevator_button->group_id != tablet_button->group_id)
1133 continue;
1134 mode = csd_wacom_device_get_current_mode (osd_window->priv->pad,
1135 tablet_button->group_id);
1136 break;
1137 }
1138 g_list_free (list);
1139
1140 return mode;
1141 }
1142
1143 static CsdWacomOSDButton *
csd_wacom_osd_window_add_button_with_dir(CsdWacomOSDWindow * osd_window,CsdWacomTabletButton * tablet_button,guint timeout,GtkDirectionType dir)1144 csd_wacom_osd_window_add_button_with_dir (CsdWacomOSDWindow *osd_window,
1145 CsdWacomTabletButton *tablet_button,
1146 guint timeout,
1147 GtkDirectionType dir)
1148 {
1149 CsdWacomOSDButton *osd_button;
1150 gchar *str;
1151
1152 str = get_tablet_button_id_name (tablet_button, dir);
1153 osd_button = csd_wacom_osd_button_new (GTK_WIDGET (osd_window), str);
1154 g_free (str);
1155
1156 str = get_tablet_button_class_name (tablet_button, dir);
1157 csd_wacom_osd_button_set_class (osd_button, str);
1158 g_free (str);
1159
1160 str = get_tablet_button_label (osd_window->priv->pad, tablet_button, dir);
1161 csd_wacom_osd_button_set_label (osd_button, str);
1162 g_free (str);
1163
1164 csd_wacom_osd_button_set_button_type (osd_button, tablet_button->type);
1165 csd_wacom_osd_button_set_position (osd_button, tablet_button->pos);
1166 csd_wacom_osd_button_set_auto_off (osd_button, timeout);
1167 osd_window->priv->buttons = g_list_append (osd_window->priv->buttons, osd_button);
1168
1169 return osd_button;
1170 }
1171
1172 static void
csd_wacom_osd_window_add_tablet_button(CsdWacomOSDWindow * osd_window,CsdWacomTabletButton * tablet_button)1173 csd_wacom_osd_window_add_tablet_button (CsdWacomOSDWindow *osd_window,
1174 CsdWacomTabletButton *tablet_button)
1175 {
1176 CsdWacomOSDButton *osd_button;
1177 gint mode;
1178
1179 switch (tablet_button->type) {
1180 case WACOM_TABLET_BUTTON_TYPE_NORMAL:
1181 case WACOM_TABLET_BUTTON_TYPE_HARDCODED:
1182 osd_button = csd_wacom_osd_window_add_button_with_dir (osd_window,
1183 tablet_button,
1184 0,
1185 0);
1186 csd_wacom_osd_button_set_visible (osd_button, TRUE);
1187 break;
1188 case WACOM_TABLET_BUTTON_TYPE_RING:
1189 case WACOM_TABLET_BUTTON_TYPE_STRIP:
1190 mode = get_elevator_current_mode (osd_window, tablet_button) - 1;
1191
1192 /* Add 2 buttons per elevator, one "Up"... */
1193 osd_button = csd_wacom_osd_window_add_button_with_dir (osd_window,
1194 tablet_button,
1195 ELEVATOR_TIMEOUT,
1196 GTK_DIR_UP);
1197 csd_wacom_osd_button_set_visible (osd_button, tablet_button->idx == mode);
1198
1199 /* ... and one "Down" */
1200 osd_button = csd_wacom_osd_window_add_button_with_dir (osd_window,
1201 tablet_button,
1202 ELEVATOR_TIMEOUT,
1203 GTK_DIR_DOWN);
1204 csd_wacom_osd_button_set_visible (osd_button, tablet_button->idx == mode);
1205
1206 break;
1207 default:
1208 g_warning ("Unknown button type");
1209 break;
1210 }
1211 }
1212
1213 /*
1214 * Returns the rotation to apply a device to get a representation relative to
1215 * the current rotation of the output.
1216 * (This function is _not_ the same as in csd-wacom-manager.c)
1217 */
1218 static CsdWacomRotation
display_relative_rotation(CsdWacomRotation device_rotation,CsdWacomRotation output_rotation)1219 display_relative_rotation (CsdWacomRotation device_rotation,
1220 CsdWacomRotation output_rotation)
1221 {
1222 CsdWacomRotation rotations[] = { CSD_WACOM_ROTATION_HALF,
1223 CSD_WACOM_ROTATION_CW,
1224 CSD_WACOM_ROTATION_NONE,
1225 CSD_WACOM_ROTATION_CCW };
1226 guint i;
1227
1228 if (device_rotation == output_rotation)
1229 return CSD_WACOM_ROTATION_NONE;
1230
1231 if (output_rotation == CSD_WACOM_ROTATION_NONE)
1232 return device_rotation;
1233
1234 for (i = 0; i < G_N_ELEMENTS (rotations); i++) {
1235 if (device_rotation == rotations[i])
1236 break;
1237 }
1238
1239 if (output_rotation == CSD_WACOM_ROTATION_HALF)
1240 return rotations[(i + G_N_ELEMENTS (rotations) - 2) % G_N_ELEMENTS (rotations)];
1241
1242 if (output_rotation == CSD_WACOM_ROTATION_CW)
1243 return rotations[(i + 1) % G_N_ELEMENTS (rotations)];
1244
1245 if (output_rotation == CSD_WACOM_ROTATION_CCW)
1246 return rotations[(i + G_N_ELEMENTS (rotations) - 1) % G_N_ELEMENTS (rotations)];
1247
1248 /* fallback */
1249 return CSD_WACOM_ROTATION_NONE;
1250 }
1251
1252 static void
csd_wacom_osd_window_mapped(GtkWidget * widget,gpointer data)1253 csd_wacom_osd_window_mapped (GtkWidget *widget,
1254 gpointer data)
1255 {
1256 CsdWacomOSDWindow *osd_window = CSD_WACOM_OSD_WINDOW (widget);
1257
1258 g_return_if_fail (CSD_IS_WACOM_OSD_WINDOW (osd_window));
1259
1260 /* Position the window at its expected position before moving
1261 * to fullscreen, so the window will be on the right monitor.
1262 */
1263 gtk_window_move (GTK_WINDOW (osd_window),
1264 osd_window->priv->screen_area.x,
1265 osd_window->priv->screen_area.y);
1266
1267 gtk_window_fullscreen (GTK_WINDOW (osd_window));
1268 gtk_window_set_keep_above (GTK_WINDOW (osd_window), TRUE);
1269 }
1270
1271 static void
csd_wacom_osd_window_realized(GtkWidget * widget,gpointer data)1272 csd_wacom_osd_window_realized (GtkWidget *widget,
1273 gpointer data)
1274 {
1275 CsdWacomOSDWindow *osd_window = CSD_WACOM_OSD_WINDOW (widget);
1276 GdkWindow *gdk_window;
1277 GdkRGBA transparent;
1278 GdkScreen *screen;
1279 GdkCursor *cursor;
1280 gint monitor;
1281 gboolean status;
1282
1283 g_return_if_fail (CSD_IS_WACOM_OSD_WINDOW (osd_window));
1284 g_return_if_fail (CSD_IS_WACOM_DEVICE (osd_window->priv->pad));
1285
1286 if (!gtk_widget_get_realized (widget))
1287 return;
1288
1289 screen = gtk_widget_get_screen (widget);
1290 gdk_window = gtk_widget_get_window (widget);
1291
1292 transparent.red = transparent.green = transparent.blue = 0.0;
1293 transparent.alpha = BACK_OPACITY;
1294 gdk_window_set_background_rgba (gdk_window, &transparent);
1295
1296 cursor = gdk_cursor_new (GDK_BLANK_CURSOR);
1297 gdk_window_set_cursor (gdk_window, cursor);
1298 g_object_unref (cursor);
1299
1300 /* Determine the monitor for that device and set appropriate fullscreen mode*/
1301 monitor = csd_wacom_device_get_display_monitor (osd_window->priv->pad);
1302 if (monitor == CSD_WACOM_SET_ALL_MONITORS) {
1303 /* Covers the entire screen */
1304 osd_window->priv->screen_area.x = 0;
1305 osd_window->priv->screen_area.y = 0;
1306 osd_window->priv->screen_area.width = gdk_screen_get_width (screen);
1307 osd_window->priv->screen_area.height = gdk_screen_get_height (screen);
1308 gdk_screen_get_monitor_geometry (screen, 0, &osd_window->priv->monitor_area);
1309 gdk_window_set_fullscreen_mode (gdk_window, GDK_FULLSCREEN_ON_ALL_MONITORS);
1310 } else {
1311 gdk_screen_get_monitor_geometry (screen, monitor, &osd_window->priv->screen_area);
1312 osd_window->priv->monitor_area = osd_window->priv->screen_area;
1313 gdk_window_set_fullscreen_mode (gdk_window, GDK_FULLSCREEN_ON_CURRENT_MONITOR);
1314 }
1315
1316 gtk_window_set_default_size (GTK_WINDOW (osd_window),
1317 osd_window->priv->screen_area.width,
1318 osd_window->priv->screen_area.height);
1319
1320 status = get_image_size (csd_wacom_device_get_layout_path (osd_window->priv->pad),
1321 &osd_window->priv->tablet_area.width,
1322 &osd_window->priv->tablet_area.height);
1323 if (status == FALSE)
1324 osd_window->priv->tablet_area = osd_window->priv->monitor_area;
1325 }
1326
1327 static void
csd_wacom_osd_window_set_device(CsdWacomOSDWindow * osd_window,CsdWacomDevice * device)1328 csd_wacom_osd_window_set_device (CsdWacomOSDWindow *osd_window,
1329 CsdWacomDevice *device)
1330 {
1331 CsdWacomRotation device_rotation;
1332 CsdWacomRotation output_rotation;
1333 GSettings *settings;
1334 GList *list, *l;
1335
1336 g_return_if_fail (CSD_IS_WACOM_OSD_WINDOW (osd_window));
1337 g_return_if_fail (CSD_IS_WACOM_DEVICE (device));
1338
1339 /* If we had a layout previously handled, get rid of it */
1340 if (osd_window->priv->handle)
1341 g_object_unref (osd_window->priv->handle);
1342 osd_window->priv->handle = NULL;
1343
1344 /* Bind the device with the OSD window */
1345 if (osd_window->priv->pad)
1346 g_object_weak_unref (G_OBJECT(osd_window->priv->pad),
1347 (GWeakNotify) gtk_widget_destroy,
1348 osd_window);
1349 osd_window->priv->pad = device;
1350 g_object_weak_ref (G_OBJECT(osd_window->priv->pad),
1351 (GWeakNotify) gtk_widget_destroy,
1352 osd_window);
1353
1354 /* Capture current rotation, we do not update that later, OSD window is meant to be short lived */
1355 settings = csd_wacom_device_get_settings (osd_window->priv->pad);
1356 device_rotation = g_settings_get_enum (settings, ROTATION_KEY);
1357 output_rotation = csd_wacom_device_get_display_rotation (osd_window->priv->pad);
1358 osd_window->priv->rotation = display_relative_rotation (device_rotation, output_rotation);
1359
1360 /* Create the buttons */
1361 list = csd_wacom_device_get_buttons (device);
1362 for (l = list; l != NULL; l = l->next) {
1363 CsdWacomTabletButton *tablet_button = l->data;
1364
1365 csd_wacom_osd_window_add_tablet_button (osd_window, tablet_button);
1366 }
1367 g_list_free (list);
1368 }
1369
1370 CsdWacomDevice *
csd_wacom_osd_window_get_device(CsdWacomOSDWindow * osd_window)1371 csd_wacom_osd_window_get_device (CsdWacomOSDWindow *osd_window)
1372 {
1373 g_return_val_if_fail (CSD_IS_WACOM_OSD_WINDOW (osd_window), NULL);
1374
1375 return osd_window->priv->pad;
1376 }
1377
1378 void
csd_wacom_osd_window_set_message(CsdWacomOSDWindow * osd_window,const gchar * str)1379 csd_wacom_osd_window_set_message (CsdWacomOSDWindow *osd_window,
1380 const gchar *str)
1381 {
1382 g_return_if_fail (CSD_IS_WACOM_OSD_WINDOW (osd_window));
1383
1384 g_free (osd_window->priv->message);
1385 osd_window->priv->message = g_strdup (str);
1386 }
1387
1388 const char *
csd_wacom_osd_window_get_message(CsdWacomOSDWindow * osd_window)1389 csd_wacom_osd_window_get_message (CsdWacomOSDWindow *osd_window)
1390 {
1391 g_return_val_if_fail (CSD_IS_WACOM_OSD_WINDOW (osd_window), NULL);
1392
1393 return osd_window->priv->message;
1394 }
1395
1396 static void
csd_wacom_osd_window_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1397 csd_wacom_osd_window_set_property (GObject *object,
1398 guint prop_id,
1399 const GValue *value,
1400 GParamSpec *pspec)
1401 {
1402 CsdWacomOSDWindow *osd_window;
1403
1404 osd_window = CSD_WACOM_OSD_WINDOW (object);
1405
1406 switch (prop_id) {
1407 case PROP_OSD_WINDOW_MESSAGE:
1408 csd_wacom_osd_window_set_message (osd_window, g_value_get_string (value));
1409 break;
1410 case PROP_OSD_WINDOW_CSD_WACOM_DEVICE:
1411 csd_wacom_osd_window_set_device (osd_window, g_value_get_object (value));
1412 break;
1413 default:
1414 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1415 break;
1416 }
1417 }
1418
1419 static void
csd_wacom_osd_window_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1420 csd_wacom_osd_window_get_property (GObject *object,
1421 guint prop_id,
1422 GValue *value,
1423 GParamSpec *pspec)
1424 {
1425 CsdWacomOSDWindow *osd_window;
1426
1427 osd_window = CSD_WACOM_OSD_WINDOW (object);
1428
1429 switch (prop_id) {
1430 case PROP_OSD_WINDOW_MESSAGE:
1431 g_value_set_string (value, osd_window->priv->message);
1432 break;
1433 case PROP_OSD_WINDOW_CSD_WACOM_DEVICE:
1434 g_value_set_object (value, (GObject*) osd_window->priv->pad);
1435 break;
1436 default:
1437 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1438 break;
1439 }
1440 }
1441
1442 void
csd_wacom_osd_window_set_active(CsdWacomOSDWindow * osd_window,CsdWacomTabletButton * button,GtkDirectionType dir,gboolean active)1443 csd_wacom_osd_window_set_active (CsdWacomOSDWindow *osd_window,
1444 CsdWacomTabletButton *button,
1445 GtkDirectionType dir,
1446 gboolean active)
1447 {
1448 GList *l;
1449 gchar *id;
1450
1451 g_return_if_fail (CSD_IS_WACOM_OSD_WINDOW (osd_window));
1452 g_return_if_fail (button != NULL);
1453
1454 id = get_tablet_button_id_name (button, dir);
1455 for (l = osd_window->priv->buttons; l != NULL; l = l->next) {
1456 CsdWacomOSDButton *osd_button = l->data;
1457 if (MATCH_ID (osd_button, id))
1458 csd_wacom_osd_button_set_active (osd_button, active);
1459 }
1460 g_free (id);
1461 }
1462
1463 void
csd_wacom_osd_window_set_mode(CsdWacomOSDWindow * osd_window,gint group_id,gint mode)1464 csd_wacom_osd_window_set_mode (CsdWacomOSDWindow *osd_window,
1465 gint group_id,
1466 gint mode)
1467 {
1468 GList *list, *l;
1469
1470 list = csd_wacom_device_get_buttons (osd_window->priv->pad);
1471 for (l = list; l != NULL; l = l->next) {
1472 CsdWacomTabletButton *tablet_button = l->data;
1473 GList *l2;
1474 gchar *id_up, *id_down;
1475
1476 if (tablet_button->type != WACOM_TABLET_BUTTON_TYPE_STRIP &&
1477 tablet_button->type != WACOM_TABLET_BUTTON_TYPE_RING)
1478 continue;
1479 if (tablet_button->group_id != group_id)
1480 continue;
1481
1482 id_up = get_tablet_button_id_name (tablet_button, GTK_DIR_UP);
1483 id_down = get_tablet_button_id_name (tablet_button, GTK_DIR_DOWN);
1484
1485 for (l2 = osd_window->priv->buttons; l2 != NULL; l2 = l2->next) {
1486 CsdWacomOSDButton *osd_button = l2->data;
1487 gboolean visible = (tablet_button->idx == mode - 1);
1488
1489 if (MATCH_ID (osd_button, id_up) || MATCH_ID (osd_button, id_down))
1490 csd_wacom_osd_button_set_visible (osd_button, visible);
1491 }
1492
1493 g_free (id_up);
1494 g_free (id_down);
1495
1496 }
1497 g_list_free (list);
1498 }
1499
1500 GtkWidget *
csd_wacom_osd_window_new(CsdWacomDevice * pad,const gchar * message)1501 csd_wacom_osd_window_new (CsdWacomDevice *pad,
1502 const gchar *message)
1503 {
1504 CsdWacomOSDWindow *osd_window;
1505 GdkScreen *screen;
1506 GdkVisual *visual;
1507
1508 osd_window = CSD_WACOM_OSD_WINDOW (g_object_new (CSD_TYPE_WACOM_OSD_WINDOW,
1509 "type", GTK_WINDOW_TOPLEVEL,
1510 "skip-pager-hint", TRUE,
1511 "skip-taskbar-hint", TRUE,
1512 "focus-on-map", TRUE,
1513 "decorated", FALSE,
1514 "deletable", FALSE,
1515 "accept-focus", TRUE,
1516 "wacom-device", pad,
1517 "message", message,
1518 NULL));
1519
1520 /* Must set the visual before realizing the window */
1521 gtk_widget_set_app_paintable (GTK_WIDGET (osd_window), TRUE);
1522 screen = gdk_screen_get_default ();
1523 visual = gdk_screen_get_rgba_visual (screen);
1524 if (visual == NULL)
1525 visual = gdk_screen_get_system_visual (screen);
1526 gtk_widget_set_visual (GTK_WIDGET (osd_window), visual);
1527
1528 g_signal_connect (GTK_WIDGET (osd_window), "realize",
1529 G_CALLBACK (csd_wacom_osd_window_realized),
1530 NULL);
1531 g_signal_connect (GTK_WIDGET (osd_window), "map",
1532 G_CALLBACK (csd_wacom_osd_window_mapped),
1533 NULL);
1534
1535 return GTK_WIDGET (osd_window);
1536 }
1537
1538 static void
csd_wacom_osd_window_class_init(CsdWacomOSDWindowClass * klass)1539 csd_wacom_osd_window_class_init (CsdWacomOSDWindowClass *klass)
1540 {
1541 GObjectClass *gobject_class;
1542 GtkWidgetClass *widget_class;
1543
1544 gobject_class = G_OBJECT_CLASS (klass);
1545 widget_class = GTK_WIDGET_CLASS (klass);
1546
1547 gobject_class->set_property = csd_wacom_osd_window_set_property;
1548 gobject_class->get_property = csd_wacom_osd_window_get_property;
1549 gobject_class->finalize = csd_wacom_osd_window_finalize;
1550 widget_class->draw = csd_wacom_osd_window_draw;
1551
1552 g_object_class_install_property (gobject_class,
1553 PROP_OSD_WINDOW_MESSAGE,
1554 g_param_spec_string ("message",
1555 "Window message",
1556 "The message shown in the OSD window",
1557 "",
1558 G_PARAM_READWRITE));
1559 g_object_class_install_property (gobject_class,
1560 PROP_OSD_WINDOW_CSD_WACOM_DEVICE,
1561 g_param_spec_object ("wacom-device",
1562 "Wacom device",
1563 "The Wacom device represented by the OSD window",
1564 CSD_TYPE_WACOM_DEVICE,
1565 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
1566
1567 g_type_class_add_private (klass, sizeof (CsdWacomOSDWindowPrivate));
1568 }
1569
1570 static void
csd_wacom_osd_window_init(CsdWacomOSDWindow * osd_window)1571 csd_wacom_osd_window_init (CsdWacomOSDWindow *osd_window)
1572 {
1573 osd_window->priv = CSD_WACOM_OSD_WINDOW_GET_PRIVATE (osd_window);
1574 }
1575
1576 static void
csd_wacom_osd_window_finalize(GObject * object)1577 csd_wacom_osd_window_finalize (GObject *object)
1578 {
1579 CsdWacomOSDWindow *osd_window;
1580 CsdWacomOSDWindowPrivate *priv;
1581
1582 g_return_if_fail (object != NULL);
1583 g_return_if_fail (CSD_IS_WACOM_OSD_WINDOW (object));
1584
1585 osd_window = CSD_WACOM_OSD_WINDOW (object);
1586 g_return_if_fail (osd_window->priv != NULL);
1587
1588 priv = osd_window->priv;
1589 g_clear_object (&priv->handle);
1590 g_clear_pointer (&priv->message, g_free);
1591 if (priv->buttons) {
1592 g_list_free_full (priv->buttons, g_object_unref);
1593 priv->buttons = NULL;
1594 }
1595
1596 G_OBJECT_CLASS (csd_wacom_osd_window_parent_class)->finalize (object);
1597 }
1598