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 |
|
|
|