GCC Code Coverage Report


Directory: ./
File: panels/wacom/cc-wacom-page.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 336 0.0%
Functions: 0 35 0.0%
Branches: 0 145 0.0%

Line Branch Exec Source
1 /*
2 * Copyright © 2011 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, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors: Peter Hutterer <peter.hutterer@redhat.com>
18 * Bastien Nocera <hadess@hadess.net>
19 *
20 */
21
22 #include <config.h>
23
24 #ifdef FAKE_AREA
25 #include <gdk/gdk.h>
26 #endif /* FAKE_AREA */
27
28 #include <glib/gi18n-lib.h>
29 #include <gtk/gtk.h>
30 #include <gdesktop-enums.h>
31 #ifdef GDK_WINDOWING_X11
32 #include <gdk/x11/gdkx.h>
33 #endif
34 #ifdef GDK_WINDOWING_WAYLAND
35 #include <gdk/wayland/gdkwayland.h>
36 #endif
37
38 #include "cc-list-row.h"
39 #include "cc-wacom-device.h"
40 #include "cc-wacom-button-row.h"
41 #include "cc-wacom-page.h"
42 #include "cc-wacom-stylus-page.h"
43 #include "gsd-enums.h"
44 #include "calibrator-gui.h"
45 #include "gsd-input-helper.h"
46
47 #include <string.h>
48
49 #define MWID(x) (GtkWidget *) gtk_builder_get_object (page->mapping_builder, x)
50
51 #define THRESHOLD_MISCLICK 15
52 #define THRESHOLD_DOUBLECLICK 7
53
54 struct _CcWacomPage
55 {
56 GtkBox parent_instance;
57
58 CcWacomPanel *panel;
59 CcWacomDevice *stylus;
60 CcWacomDevice *pad;
61 CcCalibArea *area;
62 GSettings *wacom_settings;
63
64 GtkWidget *tablet_section;
65 GtkWidget *tablet_icon;
66 GtkWidget *tablet_display;
67 GtkWidget *tablet_calibrate;
68 GtkWidget *tablet_map_buttons;
69 AdwSwitchRow *tablet_mode_row;
70 AdwSwitchRow *tablet_left_handed_row;
71 AdwSwitchRow *tablet_aspect_ratio_row;
72 GtkWidget *display_section;
73
74 GnomeRRScreen *rr_screen;
75
76 /* Button mapping */
77 GtkBuilder *mapping_builder;
78 GtkWindow *button_map;
79 GtkListStore *action_store;
80
81 GCancellable *cancellable;
82
83 /* To reach other grouped devices */
84 GsdDeviceManager *manager;
85 };
86
87 G_DEFINE_TYPE (CcWacomPage, cc_wacom_page, GTK_TYPE_BOX)
88
89 /* Different types of layout for the tablet config */
90 enum {
91 LAYOUT_NORMAL, /* tracking mode, button mapping */
92 LAYOUT_REVERSIBLE, /* tracking mode, button mapping, left-hand orientation */
93 LAYOUT_SCREEN /* button mapping, calibration, display resolution */
94 };
95
96 static int
97 get_layout_type (CcWacomDevice *device)
98 {
99 int layout;
100
101 if (cc_wacom_device_get_integration_flags (device) &
102 (WACOM_DEVICE_INTEGRATED_DISPLAY | WACOM_DEVICE_INTEGRATED_SYSTEM))
103 layout = LAYOUT_SCREEN;
104 else if (cc_wacom_device_is_reversible (device))
105 layout = LAYOUT_REVERSIBLE;
106 else
107 layout = LAYOUT_NORMAL;
108
109 return layout;
110 }
111
112 static void
113 set_calibration (CcWacomDevice *device,
114 gdouble *cal,
115 gsize ncal,
116 GSettings *settings)
117 {
118 GVariant *current; /* current calibration */
119 GVariant *array; /* new calibration */
120 g_autofree GVariant **tmp = NULL;
121 gsize nvalues;
122 gint i;
123
124 current = g_settings_get_value (settings, "area");
125 g_variant_get_fixed_array (current, &nvalues, sizeof (gdouble));
126 if ((ncal != 4) || (nvalues != 4)) {
127 g_warning("Unable to set device calibration property. Got %"G_GSIZE_FORMAT" items to put in %"G_GSIZE_FORMAT" slots; expected %d items.\n", ncal, nvalues, 4);
128 return;
129 }
130
131 tmp = g_malloc (nvalues * sizeof (GVariant*));
132 for (i = 0; i < ncal; i++)
133 tmp[i] = g_variant_new_double (cal[i]);
134
135 array = g_variant_new_array (G_VARIANT_TYPE_DOUBLE, tmp, nvalues);
136 g_settings_set_value (settings, "area", array);
137
138 g_debug ("Setting area to %f, %f, %f, %f (left/right/top/bottom)",
139 cal[0], cal[1], cal[2], cal[3]);
140 }
141
142 static void
143 finish_calibration (CcCalibArea *area,
144 gpointer user_data)
145 {
146 CcWacomPage *page = (CcWacomPage *) user_data;
147 XYinfo axis;
148 gdouble cal[4];
149
150 if (cc_calib_area_finish (area)) {
151 cc_calib_area_get_padding (area, &axis);
152 cal[0] = axis.x_min;
153 cal[1] = axis.x_max;
154 cal[2] = axis.y_min;
155 cal[3] = axis.y_max;
156
157 set_calibration (page->stylus,
158 cal, 4, page->wacom_settings);
159 } else {
160 /* Reset the old values */
161 GVariant *old_calibration;
162
163 old_calibration = g_object_get_data (G_OBJECT (page), "old-calibration");
164 g_settings_set_value (page->wacom_settings, "area", old_calibration);
165 g_object_set_data (G_OBJECT (page), "old-calibration", NULL);
166 }
167
168 cc_calib_area_free (area);
169 page->area = NULL;
170 gtk_widget_set_sensitive (page->tablet_calibrate, TRUE);
171 }
172
173 static gboolean
174 run_calibration (CcWacomPage *page,
175 GVariant *old_calibration,
176 gdouble *cal,
177 GdkMonitor *monitor)
178 {
179 g_assert (page->area == NULL);
180
181 page->area = cc_calib_area_new (NULL,
182 monitor,
183 cc_wacom_device_get_device (page->stylus),
184 finish_calibration,
185 page,
186 THRESHOLD_MISCLICK,
187 THRESHOLD_DOUBLECLICK);
188
189 g_object_set_data_full (G_OBJECT (page),
190 "old-calibration",
191 old_calibration,
192 (GDestroyNotify) g_variant_unref);
193
194 return FALSE;
195 }
196
197 static GdkMonitor *
198 find_monitor_at_point (GdkDisplay *display,
199 gint x,
200 gint y)
201 {
202 GListModel *monitors;
203 int i;
204
205 monitors = gdk_display_get_monitors (display);
206
207 for (i = 0; i < g_list_model_get_n_items (monitors); i++) {
208 g_autoptr(GdkMonitor) m = g_list_model_get_item (monitors, i);
209 GdkRectangle geometry;
210
211 gdk_monitor_get_geometry (m, &geometry);
212 if (gdk_rectangle_contains_point (&geometry, x, y))
213 return g_steal_pointer (&m);
214 }
215
216 return NULL;
217 }
218
219 static void
220 calibrate (CcWacomPage *page)
221 {
222 int i;
223 GVariant *old_calibration, *array;
224 g_autofree GVariant **tmp = NULL;
225 g_autofree gdouble *calibration = NULL;
226 gsize ncal;
227 GdkDisplay *display;
228 g_autoptr(GdkMonitor) monitor = NULL;
229 g_autoptr(GnomeRRScreen) rr_screen = NULL;
230 GnomeRROutput *output;
231 g_autoptr(GError) error = NULL;
232 GDBusProxy *input_mapping_proxy;
233 gint x, y;
234
235 display = gdk_display_get_default ();
236 rr_screen = gnome_rr_screen_new (display, &error);
237 if (error) {
238 g_warning ("Could not connect to display manager: %s", error->message);
239 return;
240 }
241
242 output = cc_wacom_device_get_output (page->stylus, rr_screen);
243 input_mapping_proxy = cc_wacom_panel_get_input_mapping_bus_proxy (page->panel);
244
245 if (output) {
246 gnome_rr_output_get_position (output, &x, &y);
247 monitor = find_monitor_at_point (display, x, y);
248 } else if (input_mapping_proxy) {
249 GsdDevice *gsd_device;
250 GVariant *mapping;
251
252 gsd_device = cc_wacom_device_get_device (page->stylus);
253
254 if (gsd_device) {
255 mapping = g_dbus_proxy_call_sync (input_mapping_proxy,
256 "GetDeviceMapping",
257 g_variant_new ("(o)", gsd_device_get_device_file (gsd_device)),
258 G_DBUS_CALL_FLAGS_NONE,
259 -1,
260 NULL,
261 NULL);
262 if (mapping) {
263 gint x, y, width, height;
264
265 g_variant_get (mapping, "((iiii))", &x, &y, &width, &height);
266 monitor = find_monitor_at_point (display, x, y);
267 }
268 }
269 }
270
271 if (!monitor) {
272 /* The display the tablet should be mapped to could not be located.
273 * This shouldn't happen if the EDID data is good...
274 */
275 g_critical("Output associated with the tablet is not connected. Calibration may appear in wrong monitor.");
276 }
277
278 old_calibration = g_settings_get_value (page->wacom_settings, "area");
279 g_variant_get_fixed_array (old_calibration, &ncal, sizeof (gdouble));
280
281 if (ncal != 4) {
282 g_warning("Device calibration property has wrong length. Got %"G_GSIZE_FORMAT" items; expected %d.\n", ncal, 4);
283 return;
284 }
285
286 calibration = g_new0 (gdouble, ncal);
287
288 /* Reset the current values, to avoid old calibrations
289 * from interfering with the calibration */
290 tmp = g_malloc (ncal * sizeof (GVariant*));
291 for (i = 0; i < ncal; i++) {
292 calibration[i] = 0.0;
293 tmp[i] = g_variant_new_double (calibration[i]);
294 }
295
296 array = g_variant_new_array (G_VARIANT_TYPE_DOUBLE, tmp, ncal);
297 g_settings_set_value (page->wacom_settings, "area", array);
298
299 run_calibration (page, old_calibration, calibration, monitor);
300 gtk_widget_set_sensitive (page->tablet_calibrate, FALSE);
301 }
302
303 static void
304 on_calibrate_activated (CcWacomPage *self)
305 {
306 calibrate (self);
307 }
308
309 /* This avoids us crashing when a newer version of
310 * gnome-control-center has been used, and we load up an
311 * old one, as the action type if unknown to the old g-c-c */
312 static gboolean
313 action_type_is_valid (GDesktopPadButtonAction action)
314 {
315 if (action >= G_N_ELEMENTS (action_table))
316 return FALSE;
317 return TRUE;
318 }
319
320 static void
321 create_row_from_button (GtkWidget *list_box,
322 guint button,
323 GSettings *settings)
324 {
325 gtk_list_box_append (GTK_LIST_BOX (list_box),
326 cc_wacom_button_row_new (button, settings));
327 }
328
329 static void
330 setup_button_mapping (CcWacomPage *page)
331 {
332 GDesktopPadButtonAction action;
333 CcWacomDevice *pad;
334 GtkWidget *list_box;
335 guint i, n_buttons;
336 GSettings *settings;
337
338 list_box = MWID ("shortcuts_list");
339 pad = page->pad;
340 n_buttons = cc_wacom_device_get_num_buttons (pad);
341
342 for (i = 0; i < n_buttons; i++) {
343 settings = cc_wacom_device_get_button_settings (pad, i);
344 if (!settings)
345 continue;
346
347 action = g_settings_get_enum (settings, "action");
348 if (!action_type_is_valid (action))
349 continue;
350
351 create_row_from_button (list_box, i, settings);
352 }
353 }
354
355 static void
356 button_mapping_dialog_closed (CcWacomPage *page)
357 {
358 gtk_window_destroy (GTK_WINDOW (MWID ("button-mapping-dialog")));
359 g_clear_object (&page->mapping_builder);
360 }
361
362 static void
363 show_button_mapping_dialog (CcWacomPage *page)
364 {
365 GtkWidget *toplevel;
366 g_autoptr(GError) error = NULL;
367 GtkWidget *dialog;
368
369 g_assert (page->mapping_builder == NULL);
370 page->mapping_builder = gtk_builder_new ();
371 gtk_builder_add_from_resource (page->mapping_builder,
372 "/org/gnome/control-center/wacom/button-mapping.ui",
373 &error);
374
375 if (error != NULL) {
376 g_warning ("Error loading UI file: %s", error->message);
377 g_clear_object (&page->mapping_builder);
378 return;
379 }
380
381 setup_button_mapping (page);
382
383 dialog = MWID ("button-mapping-dialog");
384 toplevel = GTK_WIDGET (gtk_widget_get_native (GTK_WIDGET (page)));
385 gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (toplevel));
386 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
387 g_signal_connect_object (dialog, "response",
388 G_CALLBACK (button_mapping_dialog_closed), page, G_CONNECT_SWAPPED);
389
390 gtk_window_present (GTK_WINDOW (dialog));
391
392 page->button_map = GTK_WINDOW (dialog);
393 g_object_add_weak_pointer (G_OBJECT (dialog), (gpointer *) &page->button_map);
394 }
395
396 static void
397 set_osd_visibility_cb (GObject *source_object,
398 GAsyncResult *res,
399 gpointer data)
400 {
401 g_autoptr(GError) error = NULL;
402 GVariant *result;
403 CcWacomPage *page;
404
405 page = CC_WACOM_PAGE (data);
406
407 result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error);
408
409 if (result == NULL) {
410 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
411 g_printerr ("Error setting OSD's visibility: %s\n", error->message);
412 show_button_mapping_dialog (page);
413 } else {
414 return;
415 }
416 }
417 }
418
419 static void
420 set_osd_visibility (CcWacomPage *page)
421 {
422 GDBusProxy *proxy;
423 GsdDevice *gsd_device;
424 const gchar *device_path;
425
426 proxy = cc_wacom_panel_get_gsd_wacom_bus_proxy (page->panel);
427
428 /* Pick the first device, the OSD may change later between them */
429 gsd_device = cc_wacom_device_get_device (page->pad);
430
431 device_path = gsd_device_get_device_file (gsd_device);
432
433 if (proxy == NULL) {
434 show_button_mapping_dialog (page);
435 return;
436 }
437
438 g_dbus_proxy_call (proxy,
439 "Show",
440 g_variant_new ("(ob)", device_path, TRUE),
441 G_DBUS_CALL_FLAGS_NONE,
442 -1,
443 page->cancellable,
444 set_osd_visibility_cb,
445 page);
446 }
447
448 static void
449 on_map_buttons_activated (CcWacomPage *self)
450 {
451 set_osd_visibility (self);
452 }
453
454 static void
455 on_display_selected (CcWacomPage *page)
456 {
457 GListModel *list;
458 g_autoptr (GObject) obj = NULL;
459 GVariant *variant;
460 gint idx;
461
462 list = adw_combo_row_get_model (ADW_COMBO_ROW (page->tablet_display));
463 idx = adw_combo_row_get_selected (ADW_COMBO_ROW (page->tablet_display));
464 obj = g_list_model_get_item (list, idx);
465
466 variant = g_object_get_data (obj, "value-output");
467
468 if (variant)
469 g_settings_set_value (page->wacom_settings, "output", g_variant_ref (variant));
470 else
471 g_settings_reset (page->wacom_settings, "output");
472
473 gtk_widget_set_sensitive (page->tablet_calibrate, variant == NULL);
474 }
475
476 /* Boilerplate code goes below */
477
478 static void
479 cc_wacom_page_get_property (GObject *object,
480 guint property_id,
481 GValue *value,
482 GParamSpec *pspec)
483 {
484 switch (property_id)
485 {
486 default:
487 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
488 }
489 }
490
491 static void
492 cc_wacom_page_set_property (GObject *object,
493 guint property_id,
494 const GValue *value,
495 GParamSpec *pspec)
496 {
497 switch (property_id)
498 {
499 default:
500 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
501 }
502 }
503
504 static void
505 cc_wacom_page_dispose (GObject *object)
506 {
507 CcWacomPage *self = CC_WACOM_PAGE (object);
508
509 g_cancellable_cancel (self->cancellable);
510 g_clear_object (&self->cancellable);
511 g_clear_pointer (&self->area, cc_calib_area_free);
512 g_clear_pointer (&self->button_map, gtk_window_destroy);
513 g_clear_object (&self->pad);
514 g_clear_object (&self->rr_screen);
515
516 self->panel = NULL;
517
518 G_OBJECT_CLASS (cc_wacom_page_parent_class)->dispose (object);
519 }
520
521 static void
522 cc_wacom_page_class_init (CcWacomPageClass *klass)
523 {
524 GObjectClass *object_class = G_OBJECT_CLASS (klass);
525 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
526
527 object_class->get_property = cc_wacom_page_get_property;
528 object_class->set_property = cc_wacom_page_set_property;
529 object_class->dispose = cc_wacom_page_dispose;
530
531 g_type_ensure (CC_TYPE_LIST_ROW);
532
533 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/wacom/cc-wacom-page.ui");
534
535 gtk_widget_class_bind_template_child (widget_class, CcWacomPage, tablet_section);
536 gtk_widget_class_bind_template_child (widget_class, CcWacomPage, tablet_icon);
537 gtk_widget_class_bind_template_child (widget_class, CcWacomPage, tablet_display);
538 gtk_widget_class_bind_template_child (widget_class, CcWacomPage, tablet_calibrate);
539 gtk_widget_class_bind_template_child (widget_class, CcWacomPage, tablet_map_buttons);
540 gtk_widget_class_bind_template_child (widget_class, CcWacomPage, tablet_mode_row);
541 gtk_widget_class_bind_template_child (widget_class, CcWacomPage, tablet_left_handed_row);
542 gtk_widget_class_bind_template_child (widget_class, CcWacomPage, tablet_aspect_ratio_row);
543 gtk_widget_class_bind_template_child (widget_class, CcWacomPage, display_section);
544
545 gtk_widget_class_bind_template_callback (widget_class, on_map_buttons_activated);
546 gtk_widget_class_bind_template_callback (widget_class, on_calibrate_activated);
547 gtk_widget_class_bind_template_callback (widget_class, on_display_selected);
548 }
549
550 static void
551 update_displays_model (CcWacomPage *page)
552 {
553 g_autoptr (GtkStringList) list = NULL;
554 GnomeRROutput **outputs, *cur_output;
555 int i, idx = 0, cur = -1, automatic_item = -1;
556 g_autoptr (GObject) obj = NULL;
557 GVariant *variant;
558
559 outputs = gnome_rr_screen_list_outputs (page->rr_screen);
560 list = gtk_string_list_new (NULL);
561 cur_output = cc_wacom_device_get_output (page->stylus,
562 page->rr_screen);
563
564 for (i = 0; outputs[i] != NULL; i++) {
565 GnomeRROutput *output = outputs[i];
566 GnomeRRCrtc *crtc = gnome_rr_output_get_crtc (output);
567 g_autofree gchar *text = NULL;
568 g_autofree gchar *vendor = NULL;
569 g_autofree gchar *product = NULL;
570 g_autofree gchar *serial = NULL;
571 const gchar *name, *disp_name;
572
573 /* Output is turned on? */
574 if (!crtc || gnome_rr_crtc_get_current_mode (crtc) == NULL)
575 continue;
576
577 if (output == cur_output)
578 cur = idx;
579
580 name = gnome_rr_output_get_name (output);
581 disp_name = gnome_rr_output_get_display_name (output);
582 text = g_strdup_printf ("%s (%s)", name, disp_name);
583
584 gnome_rr_output_get_ids_from_edid (output,
585 &vendor,
586 &product,
587 &serial);
588 variant = g_variant_new_strv ((const gchar *[]) { vendor, product, serial, name }, 4);
589
590 gtk_string_list_append (list, text);
591 obj = g_list_model_get_item (G_LIST_MODEL (list), idx);
592 g_object_set_data_full (G_OBJECT (obj), "value-output",
593 variant, (GDestroyNotify) g_variant_unref);
594 idx++;
595 }
596
597 /* All displays item */
598 gtk_string_list_append (list, _("All Displays"));
599 variant = g_variant_new_strv ((const gchar *[]) { "", "", "" }, 3);
600 obj = g_list_model_get_item (G_LIST_MODEL (list), idx);
601 g_object_set_data_full (G_OBJECT (obj), "value-output",
602 variant, (GDestroyNotify) g_variant_unref);
603 if (cur_output == NULL)
604 cur = idx;
605
606 /* "Automatic" item */
607 if (get_layout_type (page->stylus) == LAYOUT_SCREEN) {
608 g_autoptr (GVariant) user_value = NULL;
609
610 idx++;
611 gtk_string_list_append (list, _("Automatic"));
612 automatic_item = idx;
613
614 user_value = g_settings_get_user_value (page->wacom_settings, "output");
615 if (!user_value)
616 cur = idx;
617 }
618
619 g_signal_handlers_block_by_func (page->tablet_display, on_display_selected, page);
620 adw_combo_row_set_model (ADW_COMBO_ROW (page->tablet_display), G_LIST_MODEL (list));
621 adw_combo_row_set_selected (ADW_COMBO_ROW (page->tablet_display), cur);
622 g_signal_handlers_unblock_by_func (page->tablet_display, on_display_selected, page);
623
624 gtk_widget_set_sensitive (page->tablet_calibrate, cur == automatic_item);
625 }
626
627 static void
628 cc_wacom_page_init (CcWacomPage *page)
629 {
630 g_autoptr (GError) error = NULL;
631
632 gtk_widget_init_template (GTK_WIDGET (page));
633 page->rr_screen = gnome_rr_screen_new (gdk_display_get_default (), &error);
634
635 if (error)
636 g_warning ("Could not get RR screen: %s", error->message);
637
638 g_signal_connect_object (page->rr_screen, "changed",
639 G_CALLBACK (update_displays_model),
640 page, G_CONNECT_SWAPPED);
641 }
642
643 static void
644 set_icon_name (CcWacomPage *page,
645 GtkWidget *widget,
646 const char *icon_name)
647 {
648 g_autofree gchar *resource = NULL;
649
650 resource = g_strdup_printf ("/org/gnome/control-center/wacom/%s.svg", icon_name);
651 gtk_picture_set_resource (GTK_PICTURE (widget), resource);
652 }
653
654 static gboolean
655 has_monitor (CcWacomPage *page)
656 {
657 WacomIntegrationFlags integration_flags;
658
659 integration_flags = cc_wacom_device_get_integration_flags (page->stylus);
660
661 return ((integration_flags &
662 (WACOM_DEVICE_INTEGRATED_DISPLAY | WACOM_DEVICE_INTEGRATED_SYSTEM)) != 0);
663 }
664
665 static void
666 update_pad_availability (CcWacomPage *page)
667 {
668 gtk_widget_set_visible (page->tablet_map_buttons, page->pad != NULL);
669 }
670
671 static void
672 check_add_pad (CcWacomPage *page,
673 GsdDevice *gsd_device)
674 {
675 g_autoptr(CcWacomDevice) wacom_device = NULL;
676 const gchar *stylus_vendor, *stylus_product;
677 const gchar *pad_vendor, *pad_product;
678 GsdDevice *stylus_device;
679
680 if ((gsd_device_get_device_type (gsd_device) & GSD_DEVICE_TYPE_PAD) == 0)
681 return;
682
683 stylus_device = cc_wacom_device_get_device (page->stylus);
684 gsd_device_get_device_ids (cc_wacom_device_get_device (page->stylus),
685 &stylus_vendor, &stylus_product);
686 gsd_device_get_device_ids (gsd_device, &pad_vendor, &pad_product);
687
688 if (!gsd_device_shares_group (stylus_device, gsd_device) ||
689 g_strcmp0 (stylus_vendor, pad_vendor) != 0 ||
690 g_strcmp0 (stylus_product, pad_product) != 0)
691 return;
692
693 page->pad = cc_wacom_device_new (gsd_device);
694 if (page->pad)
695 update_pad_availability (page);
696 }
697
698 static void
699 check_remove_pad (CcWacomPage *page,
700 GsdDevice *gsd_device)
701 {
702 if ((gsd_device_get_device_type (gsd_device) & GSD_DEVICE_TYPE_PAD) == 0)
703 return;
704
705 if (cc_wacom_device_get_device (page->pad) == gsd_device) {
706 g_clear_object (&page->pad);
707 update_pad_availability (page);
708 }
709 }
710
711 static GVariant *
712 tablet_mode_bind_set (const GValue *value,
713 const GVariantType *expected_type,
714 gpointer user_data)
715 {
716 gboolean setting;
717
718 setting = g_value_get_boolean (value);
719
720 return g_variant_new_string (setting ? "absolute" : "relative");
721 }
722
723 static gboolean
724 tablet_mode_bind_get (GValue *value,
725 GVariant *variant,
726 gpointer user_data)
727 {
728 g_value_set_boolean (value,
729 g_strcmp0 (g_variant_get_string (variant, NULL),
730 "absolute") == 0);
731 return TRUE;
732 }
733
734 GtkWidget *
735 cc_wacom_page_new (CcWacomPanel *panel,
736 CcWacomDevice *stylus)
737 {
738 g_autoptr (GList) pads = NULL;
739 CcWacomPage *page;
740 GList *l;
741
742 g_return_val_if_fail (CC_IS_WACOM_DEVICE (stylus), NULL);
743
744 page = g_object_new (CC_TYPE_WACOM_PAGE, NULL);
745
746 page->panel = panel;
747 page->stylus = stylus;
748
749 gtk_widget_set_visible (GTK_WIDGET (page->tablet_left_handed_row),
750 get_layout_type (stylus) == LAYOUT_REVERSIBLE);
751 gtk_widget_set_visible (page->tablet_calibrate,
752 get_layout_type (stylus) == LAYOUT_SCREEN);
753
754 /* FIXME move this to construct */
755 page->wacom_settings = cc_wacom_device_get_settings (stylus);
756
757 /* Tablet name */
758 adw_preferences_group_set_title (ADW_PREFERENCES_GROUP (page->tablet_section),
759 cc_wacom_device_get_name (stylus));
760 adw_preferences_group_set_description (ADW_PREFERENCES_GROUP (page->tablet_section),
761 cc_wacom_device_get_description (stylus));
762
763 g_settings_bind_with_mapping (page->wacom_settings, "mapping",
764 page->tablet_mode_row, "active",
765 G_SETTINGS_BIND_DEFAULT,
766 tablet_mode_bind_get,
767 tablet_mode_bind_set,
768 NULL, NULL);
769 g_settings_bind_with_mapping (page->wacom_settings, "mapping",
770 page->display_section, "sensitive",
771 G_SETTINGS_BIND_DEFAULT,
772 tablet_mode_bind_get,
773 tablet_mode_bind_set,
774 NULL, NULL);
775 g_settings_bind (page->wacom_settings, "left-handed",
776 page->tablet_left_handed_row, "active",
777 G_SETTINGS_BIND_DEFAULT);
778 g_settings_bind (page->wacom_settings, "keep-aspect",
779 page->tablet_aspect_ratio_row, "active",
780 G_SETTINGS_BIND_DEFAULT);
781
782 /* Tablet icon */
783 set_icon_name (page, page->tablet_icon, cc_wacom_device_get_icon_name (stylus));
784
785 /* Listen to changes in related/paired pads */
786 page->manager = gsd_device_manager_get ();
787 g_signal_connect_object (G_OBJECT (page->manager), "device-added",
788 G_CALLBACK (check_add_pad), page,
789 G_CONNECT_SWAPPED);
790 g_signal_connect_object (G_OBJECT (page->manager), "device-removed",
791 G_CALLBACK (check_remove_pad), page,
792 G_CONNECT_SWAPPED);
793
794 pads = gsd_device_manager_list_devices (page->manager, GSD_DEVICE_TYPE_PAD);
795 for (l = pads; l ; l = l->next)
796 check_add_pad (page, l->data);
797
798 update_pad_availability (page);
799 update_displays_model (page);
800
801 return GTK_WIDGET (page);
802 }
803
804 void
805 cc_wacom_page_calibrate (CcWacomPage *page)
806 {
807 g_return_if_fail (CC_IS_WACOM_PAGE (page));
808
809 calibrate (page);
810 }
811
812 gboolean
813 cc_wacom_page_can_calibrate (CcWacomPage *page)
814 {
815 g_return_val_if_fail (CC_IS_WACOM_PAGE (page),
816 FALSE);
817
818 return has_monitor (page);
819 }
820