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 |
|
|
#include <string.h> |
25 |
|
|
#include <gtk/gtk.h> |
26 |
|
|
#include <glib/gi18n-lib.h> |
27 |
|
|
|
28 |
|
|
#include "shell/cc-application.h" |
29 |
|
|
#include "shell/cc-log.h" |
30 |
|
|
#include "cc-wacom-panel.h" |
31 |
|
|
#include "cc-wacom-page.h" |
32 |
|
|
#include "cc-wacom-ekr-page.h" |
33 |
|
|
#include "cc-wacom-stylus-page.h" |
34 |
|
|
#include "cc-wacom-resources.h" |
35 |
|
|
#include "cc-drawing-area.h" |
36 |
|
|
#include "cc-tablet-tool-map.h" |
37 |
|
|
#include "gsd-device-manager.h" |
38 |
|
|
|
39 |
|
|
#ifdef GDK_WINDOWING_WAYLAND |
40 |
|
|
#include <gdk/wayland/gdkwayland.h> |
41 |
|
|
#endif |
42 |
|
|
|
43 |
|
|
#define EKR_VENDOR "056a" |
44 |
|
|
#define EKR_PRODUCT "0331" |
45 |
|
|
#define POLL_MS 300 |
46 |
|
|
|
47 |
|
|
struct _CcWacomPanel |
48 |
|
|
{ |
49 |
|
|
CcPanel parent_instance; |
50 |
|
|
|
51 |
|
|
GtkWidget *test_popover; |
52 |
|
|
GtkWidget *test_draw_area; |
53 |
|
|
GtkWidget *test_button; |
54 |
|
|
GtkWidget *scrollable; |
55 |
|
|
GtkWidget *tablets; |
56 |
|
|
GtkWidget *styli; |
57 |
|
|
GtkWidget *initial_state_stack; |
58 |
|
|
GtkWidget *panel_view; |
59 |
|
|
GtkWidget *panel_empty_state; |
60 |
|
|
GHashTable *devices; /* key=GsdDevice, value=CcWacomDevice */ |
61 |
|
|
GHashTable *pages; /* key=CcWacomDevice, value=GtkWidget */ |
62 |
|
|
GHashTable *stylus_pages; /* key=CcWacomTool, value=CcWacomStylusPage */ |
63 |
|
|
guint mock_stylus_id; |
64 |
|
|
|
65 |
|
|
CcTabletToolMap *tablet_tool_map; |
66 |
|
|
|
67 |
|
|
GtkAdjustment *vadjustment; |
68 |
|
|
GtkGesture *stylus_gesture; |
69 |
|
|
|
70 |
|
|
GtkWidget *highlighted_widget; |
71 |
|
|
CcWacomStylusPage *highlighted_stylus_page; |
72 |
|
|
guint highlight_timeout_id; |
73 |
|
|
|
74 |
|
|
/* DBus */ |
75 |
|
|
GDBusProxy *proxy; |
76 |
|
|
GDBusProxy *input_mapping_proxy; |
77 |
|
|
}; |
78 |
|
|
|
79 |
|
✗ |
CC_PANEL_REGISTER (CcWacomPanel, cc_wacom_panel) |
80 |
|
|
|
81 |
|
|
typedef struct { |
82 |
|
|
const char *name; |
83 |
|
|
CcWacomDevice *stylus; |
84 |
|
|
CcWacomDevice *pad; |
85 |
|
|
} Tablet; |
86 |
|
|
|
87 |
|
|
enum { |
88 |
|
|
WACOM_PAGE = -1, |
89 |
|
|
PLUG_IN_PAGE = 0, |
90 |
|
|
}; |
91 |
|
|
|
92 |
|
|
enum { |
93 |
|
|
PROP_0, |
94 |
|
|
PROP_PARAMETERS |
95 |
|
|
}; |
96 |
|
|
|
97 |
|
|
/* Static init function */ |
98 |
|
|
static void |
99 |
|
✗ |
update_visibility (GsdDeviceManager *manager, |
100 |
|
|
GsdDevice *device, |
101 |
|
|
gpointer user_data) |
102 |
|
|
{ |
103 |
|
|
CcApplication *application; |
104 |
|
✗ |
g_autoptr(GList) devices = NULL; |
105 |
|
|
guint i; |
106 |
|
|
|
107 |
|
✗ |
devices = gsd_device_manager_list_devices (manager, GSD_DEVICE_TYPE_TABLET); |
108 |
|
✗ |
i = g_list_length (devices); |
109 |
|
|
|
110 |
|
|
/* Set the new visibility */ |
111 |
|
✗ |
application = CC_APPLICATION (g_application_get_default ()); |
112 |
|
✗ |
cc_shell_model_set_panel_visibility (cc_application_get_model (application), |
113 |
|
|
"wacom", |
114 |
|
|
i > 0 ? CC_PANEL_VISIBLE : CC_PANEL_VISIBLE_IN_SEARCH); |
115 |
|
|
|
116 |
|
✗ |
g_debug ("Wacom panel visible: %s", i > 0 ? "yes" : "no"); |
117 |
|
✗ |
} |
118 |
|
|
|
119 |
|
|
void |
120 |
|
✗ |
cc_wacom_panel_static_init_func (void) |
121 |
|
|
{ |
122 |
|
|
GsdDeviceManager *manager; |
123 |
|
|
|
124 |
|
✗ |
manager = gsd_device_manager_get (); |
125 |
|
✗ |
g_signal_connect (G_OBJECT (manager), "device-added", |
126 |
|
|
G_CALLBACK (update_visibility), NULL); |
127 |
|
✗ |
g_signal_connect (G_OBJECT (manager), "device-removed", |
128 |
|
|
G_CALLBACK (update_visibility), NULL); |
129 |
|
✗ |
update_visibility (manager, NULL, NULL); |
130 |
|
✗ |
} |
131 |
|
|
|
132 |
|
|
static CcWacomDevice * |
133 |
|
✗ |
lookup_wacom_device (CcWacomPanel *self, |
134 |
|
|
const gchar *name) |
135 |
|
|
{ |
136 |
|
|
GHashTableIter iter; |
137 |
|
|
CcWacomDevice *wacom_device; |
138 |
|
|
|
139 |
|
✗ |
g_hash_table_iter_init (&iter, self->devices); |
140 |
|
✗ |
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &wacom_device)) { |
141 |
|
✗ |
if (g_strcmp0 (cc_wacom_device_get_name (wacom_device), name) == 0) |
142 |
|
✗ |
return wacom_device; |
143 |
|
|
} |
144 |
|
|
|
145 |
|
✗ |
return NULL; |
146 |
|
|
} |
147 |
|
|
|
148 |
|
|
static void |
149 |
|
✗ |
highlight_widget (CcWacomPanel *self, GtkWidget *widget) |
150 |
|
|
{ |
151 |
|
|
graphene_point_t p; |
152 |
|
|
|
153 |
|
✗ |
if (self->highlighted_widget == widget) |
154 |
|
✗ |
return; |
155 |
|
|
|
156 |
|
✗ |
if (!gtk_widget_compute_point (widget, |
157 |
|
|
self->scrollable, |
158 |
|
✗ |
&GRAPHENE_POINT_INIT (0, 0), |
159 |
|
|
&p)) |
160 |
|
✗ |
return; |
161 |
|
|
|
162 |
|
✗ |
gtk_adjustment_set_value (self->vadjustment, p.y); |
163 |
|
✗ |
self->highlighted_widget = widget; |
164 |
|
|
} |
165 |
|
|
|
166 |
|
|
static CcWacomPage * |
167 |
|
✗ |
update_highlighted_device (CcWacomPanel *self, const gchar *device_name) |
168 |
|
|
{ |
169 |
|
|
CcWacomPage *page; |
170 |
|
|
CcWacomDevice *wacom_device; |
171 |
|
|
|
172 |
|
✗ |
if (device_name == NULL) |
173 |
|
✗ |
return NULL; |
174 |
|
|
|
175 |
|
✗ |
wacom_device = lookup_wacom_device (self, device_name); |
176 |
|
✗ |
if (!wacom_device) { |
177 |
|
✗ |
g_warning ("Failed to find device '%s', supplied in the command line.", device_name); |
178 |
|
✗ |
return NULL; |
179 |
|
|
} |
180 |
|
|
|
181 |
|
✗ |
page = g_hash_table_lookup (self->pages, wacom_device); |
182 |
|
✗ |
highlight_widget (self, GTK_WIDGET (page)); |
183 |
|
|
|
184 |
|
✗ |
return page; |
185 |
|
|
} |
186 |
|
|
|
187 |
|
|
static void |
188 |
|
✗ |
run_operation_from_params (CcWacomPanel *self, GVariant *parameters) |
189 |
|
|
{ |
190 |
|
✗ |
g_autoptr(GVariant) v = NULL; |
191 |
|
✗ |
g_autoptr(GVariant) v2 = NULL; |
192 |
|
|
CcWacomPage *page; |
193 |
|
✗ |
const gchar *operation = NULL; |
194 |
|
✗ |
const gchar *device_name = NULL; |
195 |
|
|
gint n_params; |
196 |
|
|
|
197 |
|
✗ |
n_params = g_variant_n_children (parameters); |
198 |
|
|
|
199 |
|
✗ |
g_variant_get_child (parameters, n_params - 1, "v", &v); |
200 |
|
✗ |
device_name = g_variant_get_string (v, NULL); |
201 |
|
|
|
202 |
|
✗ |
if (!g_variant_is_of_type (v, G_VARIANT_TYPE_STRING)) { |
203 |
|
✗ |
g_warning ("Wrong type for the second argument GVariant, expected 's' but got '%s'", |
204 |
|
|
g_variant_get_type_string (v)); |
205 |
|
✗ |
return; |
206 |
|
|
} |
207 |
|
|
|
208 |
|
✗ |
switch (n_params) { |
209 |
|
✗ |
case 3: |
210 |
|
✗ |
page = update_highlighted_device (self, device_name); |
211 |
|
✗ |
if (page == NULL) |
212 |
|
✗ |
return; |
213 |
|
|
|
214 |
|
✗ |
g_variant_get_child (parameters, 1, "v", &v2); |
215 |
|
|
|
216 |
|
✗ |
if (!g_variant_is_of_type (v2, G_VARIANT_TYPE_STRING)) { |
217 |
|
✗ |
g_warning ("Wrong type for the operation name argument. A string is expected."); |
218 |
|
✗ |
break; |
219 |
|
|
} |
220 |
|
|
|
221 |
|
✗ |
operation = g_variant_get_string (v2, NULL); |
222 |
|
✗ |
if (g_strcmp0 (operation, "run-calibration") == 0) { |
223 |
|
✗ |
if (cc_wacom_page_can_calibrate (page)) |
224 |
|
✗ |
cc_wacom_page_calibrate (page); |
225 |
|
|
else |
226 |
|
✗ |
g_warning ("The device %s cannot be calibrated.", device_name); |
227 |
|
|
} else { |
228 |
|
✗ |
g_warning ("Ignoring unrecognized operation '%s'", operation); |
229 |
|
|
} |
230 |
|
|
case 2: |
231 |
|
✗ |
update_highlighted_device (self, device_name); |
232 |
|
✗ |
break; |
233 |
|
✗ |
case 1: |
234 |
|
✗ |
g_assert_not_reached (); |
235 |
|
✗ |
default: |
236 |
|
✗ |
g_warning ("Unexpected number of parameters found: %d. Request ignored.", n_params); |
237 |
|
|
} |
238 |
|
|
} |
239 |
|
|
|
240 |
|
|
/* Boilerplate code goes below */ |
241 |
|
|
|
242 |
|
|
static void |
243 |
|
✗ |
cc_wacom_panel_get_property (GObject *object, |
244 |
|
|
guint property_id, |
245 |
|
|
GValue *value, |
246 |
|
|
GParamSpec *pspec) |
247 |
|
|
{ |
248 |
|
|
switch (property_id) |
249 |
|
|
{ |
250 |
|
|
default: |
251 |
|
✗ |
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
252 |
|
|
} |
253 |
|
✗ |
} |
254 |
|
|
|
255 |
|
|
static void |
256 |
|
✗ |
cc_wacom_panel_set_property (GObject *object, |
257 |
|
|
guint property_id, |
258 |
|
|
const GValue *value, |
259 |
|
|
GParamSpec *pspec) |
260 |
|
|
{ |
261 |
|
|
CcWacomPanel *self; |
262 |
|
✗ |
self = CC_WACOM_PANEL (object); |
263 |
|
|
|
264 |
|
✗ |
switch (property_id) |
265 |
|
|
{ |
266 |
|
✗ |
case PROP_PARAMETERS: { |
267 |
|
|
GVariant *parameters; |
268 |
|
|
|
269 |
|
✗ |
parameters = g_value_get_variant (value); |
270 |
|
✗ |
if (parameters == NULL || g_variant_n_children (parameters) <= 1) |
271 |
|
✗ |
return; |
272 |
|
|
|
273 |
|
✗ |
run_operation_from_params (self, parameters); |
274 |
|
|
|
275 |
|
✗ |
break; |
276 |
|
|
} |
277 |
|
✗ |
default: |
278 |
|
✗ |
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
279 |
|
|
} |
280 |
|
|
} |
281 |
|
|
|
282 |
|
|
static void |
283 |
|
✗ |
cc_wacom_panel_dispose (GObject *object) |
284 |
|
|
{ |
285 |
|
✗ |
CcWacomPanel *self = CC_WACOM_PANEL (object); |
286 |
|
|
CcShell *shell; |
287 |
|
|
|
288 |
|
✗ |
shell = cc_panel_get_shell (CC_PANEL (self)); |
289 |
|
✗ |
if (shell) { |
290 |
|
✗ |
gtk_widget_remove_controller (GTK_WIDGET (shell), |
291 |
|
✗ |
GTK_EVENT_CONTROLLER (self->stylus_gesture)); |
292 |
|
|
} |
293 |
|
|
|
294 |
|
|
|
295 |
|
✗ |
g_clear_pointer (&self->devices, g_hash_table_unref); |
296 |
|
✗ |
g_clear_object (&self->proxy); |
297 |
|
✗ |
g_clear_object (&self->input_mapping_proxy); |
298 |
|
✗ |
g_clear_pointer (&self->pages, g_hash_table_unref); |
299 |
|
✗ |
g_clear_pointer (&self->stylus_pages, g_hash_table_unref); |
300 |
|
✗ |
g_clear_handle_id (&self->mock_stylus_id, g_source_remove); |
301 |
|
✗ |
g_clear_handle_id (&self->highlight_timeout_id, g_source_remove); |
302 |
|
✗ |
g_clear_object (&self->highlighted_stylus_page); |
303 |
|
|
|
304 |
|
✗ |
G_OBJECT_CLASS (cc_wacom_panel_parent_class)->dispose (object); |
305 |
|
✗ |
} |
306 |
|
|
|
307 |
|
|
static void |
308 |
|
✗ |
check_remove_stylus_pages (CcWacomPanel *self) |
309 |
|
|
{ |
310 |
|
|
GHashTableIter iter; |
311 |
|
|
CcWacomDevice *device; |
312 |
|
|
CcWacomTool *tool; |
313 |
|
|
CcWacomStylusPage *page; |
314 |
|
|
GList *tools; |
315 |
|
✗ |
g_autoptr(GList) total = NULL; |
316 |
|
|
|
317 |
|
|
/* First. Iterate known devices and get the tools */ |
318 |
|
✗ |
g_hash_table_iter_init (&iter, self->devices); |
319 |
|
✗ |
while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &device)) { |
320 |
|
✗ |
tools = cc_tablet_tool_map_list_tools (self->tablet_tool_map, device); |
321 |
|
✗ |
total = g_list_concat (total, tools); |
322 |
|
|
} |
323 |
|
|
|
324 |
|
|
/* Second. Iterate through stylus pages and remove the ones whose |
325 |
|
|
* tool is no longer in the list. |
326 |
|
|
*/ |
327 |
|
✗ |
g_hash_table_iter_init (&iter, self->stylus_pages); |
328 |
|
✗ |
while (g_hash_table_iter_next (&iter, (gpointer*) &tool, (gpointer*) &page)) { |
329 |
|
✗ |
if (g_list_find (total, tool)) |
330 |
|
✗ |
continue; |
331 |
|
|
|
332 |
|
✗ |
if (page == self->highlighted_stylus_page) { |
333 |
|
✗ |
g_clear_object (&self->highlighted_stylus_page); |
334 |
|
✗ |
g_clear_handle_id (&self->highlight_timeout_id, g_source_remove); |
335 |
|
|
} |
336 |
|
|
|
337 |
|
✗ |
gtk_box_remove (GTK_BOX (self->styli), GTK_WIDGET (page)); |
338 |
|
✗ |
g_hash_table_iter_remove (&iter); |
339 |
|
|
} |
340 |
|
|
|
341 |
|
✗ |
} |
342 |
|
|
|
343 |
|
|
static gboolean |
344 |
|
✗ |
add_stylus (CcWacomPanel *self, |
345 |
|
|
CcWacomTool *tool) |
346 |
|
|
{ |
347 |
|
|
GtkWidget *page; |
348 |
|
|
|
349 |
|
✗ |
if (g_hash_table_lookup (self->stylus_pages, tool)) |
350 |
|
✗ |
return FALSE; |
351 |
|
|
|
352 |
|
✗ |
page = cc_wacom_stylus_page_new (self, tool); |
353 |
|
✗ |
gtk_box_append (GTK_BOX (self->styli), page); |
354 |
|
✗ |
g_hash_table_insert (self->stylus_pages, tool, page); |
355 |
|
|
|
356 |
|
✗ |
return TRUE; |
357 |
|
|
} |
358 |
|
|
|
359 |
|
|
static void |
360 |
|
✗ |
update_test_button (CcWacomPanel *self) |
361 |
|
|
{ |
362 |
|
✗ |
if (!self->test_button) |
363 |
|
✗ |
return; |
364 |
|
|
|
365 |
|
✗ |
if (g_hash_table_size (self->devices) == 0) { |
366 |
|
✗ |
gtk_popover_popdown (GTK_POPOVER (self->test_popover)); |
367 |
|
✗ |
gtk_widget_set_sensitive (self->test_button, FALSE); |
368 |
|
|
} else { |
369 |
|
✗ |
gtk_widget_set_sensitive (self->test_button, TRUE); |
370 |
|
|
} |
371 |
|
|
} |
372 |
|
|
|
373 |
|
|
static void |
374 |
|
✗ |
update_initial_state (CcWacomPanel *self) |
375 |
|
|
{ |
376 |
|
✗ |
gtk_stack_set_visible_child (GTK_STACK (self->initial_state_stack), |
377 |
|
✗ |
g_hash_table_size (self->devices) == 0 ? |
378 |
|
|
self->panel_empty_state : |
379 |
|
|
self->panel_view); |
380 |
|
✗ |
} |
381 |
|
|
|
382 |
|
|
static void |
383 |
|
✗ |
on_stylus_timeout (gpointer data) |
384 |
|
|
{ |
385 |
|
✗ |
CcWacomPanel *panel = CC_WACOM_PANEL (data); |
386 |
|
|
|
387 |
|
✗ |
cc_wacom_stylus_page_set_highlight (panel->highlighted_stylus_page, FALSE); |
388 |
|
✗ |
g_clear_object (&panel->highlighted_stylus_page); |
389 |
|
✗ |
panel->highlight_timeout_id = 0; |
390 |
|
✗ |
} |
391 |
|
|
|
392 |
|
|
static void |
393 |
|
✗ |
update_highlighted_stylus (CcWacomPanel *self, |
394 |
|
|
CcWacomTool *stylus_to_highlight) |
395 |
|
|
{ |
396 |
|
|
GHashTableIter iter; |
397 |
|
|
CcWacomTool *stylus; |
398 |
|
|
CcWacomStylusPage *page; |
399 |
|
|
|
400 |
|
✗ |
g_hash_table_iter_init (&iter, self->stylus_pages); |
401 |
|
✗ |
while (g_hash_table_iter_next (&iter, (gpointer *)&stylus, (gpointer *)&page)) { |
402 |
|
✗ |
gboolean highlight = stylus == stylus_to_highlight; |
403 |
|
✗ |
cc_wacom_stylus_page_set_highlight (page, highlight); |
404 |
|
✗ |
if (highlight) { |
405 |
|
✗ |
highlight_widget (self, GTK_WIDGET (page)); |
406 |
|
✗ |
g_clear_object (&self->highlighted_stylus_page); |
407 |
|
✗ |
g_clear_handle_id (&self->highlight_timeout_id, g_source_remove); |
408 |
|
✗ |
self->highlight_timeout_id = g_timeout_add_once (POLL_MS, on_stylus_timeout, self); |
409 |
|
✗ |
self->highlighted_stylus_page = g_object_ref (page); |
410 |
|
|
} |
411 |
|
|
} |
412 |
|
|
|
413 |
|
✗ |
} |
414 |
|
|
|
415 |
|
|
static void |
416 |
|
✗ |
update_current_tool (CcWacomPanel *self, |
417 |
|
|
GdkDevice *device, |
418 |
|
|
GdkDeviceTool *tool) |
419 |
|
|
{ |
420 |
|
|
GsdDeviceManager *device_manager; |
421 |
|
|
CcWacomDevice *wacom_device; |
422 |
|
|
CcWacomTool *stylus; |
423 |
|
|
GsdDevice *gsd_device; |
424 |
|
|
guint64 serial, id; |
425 |
|
|
|
426 |
|
✗ |
if (!tool) |
427 |
|
✗ |
return; |
428 |
|
|
|
429 |
|
|
/* Work our way to the CcWacomDevice */ |
430 |
|
✗ |
device_manager = gsd_device_manager_get (); |
431 |
|
✗ |
gsd_device = gsd_device_manager_lookup_gdk_device (device_manager, |
432 |
|
|
device); |
433 |
|
✗ |
if (!gsd_device) |
434 |
|
✗ |
return; |
435 |
|
|
|
436 |
|
✗ |
wacom_device = g_hash_table_lookup (self->devices, gsd_device); |
437 |
|
✗ |
if (!wacom_device) |
438 |
|
✗ |
return; |
439 |
|
|
|
440 |
|
|
/* Check whether we already know this tool, nothing to do then */ |
441 |
|
✗ |
serial = gdk_device_tool_get_serial (tool); |
442 |
|
|
|
443 |
|
|
/* The wacom driver sends serial-less tools with a serial of |
444 |
|
|
* 1, libinput uses 0. No device exists with serial 1, let's reset |
445 |
|
|
* it here so everything else works as expected. |
446 |
|
|
*/ |
447 |
|
✗ |
if (serial == 1) |
448 |
|
✗ |
serial = 0; |
449 |
|
|
|
450 |
|
✗ |
stylus = cc_tablet_tool_map_lookup_tool (self->tablet_tool_map, |
451 |
|
|
wacom_device, serial); |
452 |
|
|
|
453 |
|
✗ |
if (!stylus) { |
454 |
|
✗ |
id = gdk_device_tool_get_hardware_id (tool); |
455 |
|
|
|
456 |
|
|
/* The wacom driver sends a hw id of 0x2 for stylus and 0xa |
457 |
|
|
* for eraser for devices that don't have a true HW id. |
458 |
|
|
* Reset those to 0 so we can use the same code-paths |
459 |
|
|
* libinput uses. |
460 |
|
|
* The touch ID is 0x3, let's ignore that because we don't |
461 |
|
|
* have a touch tool and it only happens when the wacom |
462 |
|
|
* driver handles the touch device. |
463 |
|
|
*/ |
464 |
|
✗ |
if (id == 0x2 || id == 0xa) |
465 |
|
✗ |
id = 0; |
466 |
|
✗ |
else if (id == 0x3) |
467 |
|
✗ |
return; |
468 |
|
|
|
469 |
|
✗ |
stylus = cc_wacom_tool_new (serial, id, wacom_device); |
470 |
|
✗ |
if (!stylus) |
471 |
|
✗ |
return; |
472 |
|
|
} |
473 |
|
|
|
474 |
|
✗ |
add_stylus (self, stylus); |
475 |
|
|
|
476 |
|
✗ |
update_highlighted_stylus (self, stylus); |
477 |
|
|
|
478 |
|
✗ |
cc_tablet_tool_map_add_relation (self->tablet_tool_map, |
479 |
|
|
wacom_device, stylus); |
480 |
|
|
} |
481 |
|
|
|
482 |
|
|
static void |
483 |
|
✗ |
on_stylus_proximity_cb (CcWacomPanel *self, |
484 |
|
|
double x, |
485 |
|
|
double y, |
486 |
|
|
GtkGestureStylus *gesture) |
487 |
|
|
{ |
488 |
|
|
GdkDevice *device; |
489 |
|
|
GdkDeviceTool *tool; |
490 |
|
|
|
491 |
|
✗ |
device = gtk_event_controller_get_current_event_device (GTK_EVENT_CONTROLLER (gesture)); |
492 |
|
✗ |
tool = gtk_gesture_stylus_get_device_tool (gesture); |
493 |
|
✗ |
update_current_tool (self, device, tool); |
494 |
|
✗ |
} |
495 |
|
|
|
496 |
|
|
static gboolean |
497 |
|
✗ |
show_mock_stylus_cb (gpointer user_data) |
498 |
|
|
{ |
499 |
|
✗ |
CcWacomPanel *self = user_data; |
500 |
|
|
GList *device_list; |
501 |
|
|
CcWacomDevice *wacom_device; |
502 |
|
|
CcWacomTool *stylus; |
503 |
|
|
|
504 |
|
✗ |
self->mock_stylus_id = 0; |
505 |
|
|
|
506 |
|
✗ |
device_list = g_hash_table_get_values (self->devices); |
507 |
|
✗ |
if (device_list == NULL) { |
508 |
|
✗ |
g_warning ("Could not create fake stylus event because could not find tablet device"); |
509 |
|
✗ |
return G_SOURCE_REMOVE; |
510 |
|
|
} |
511 |
|
|
|
512 |
|
✗ |
wacom_device = device_list->data; |
513 |
|
✗ |
g_list_free (device_list); |
514 |
|
|
|
515 |
|
✗ |
stylus = cc_wacom_tool_new (0, 0, wacom_device); |
516 |
|
✗ |
add_stylus (self, stylus); |
517 |
|
✗ |
update_highlighted_stylus (self, stylus); |
518 |
|
✗ |
cc_tablet_tool_map_add_relation (self->tablet_tool_map, |
519 |
|
|
wacom_device, stylus); |
520 |
|
|
|
521 |
|
✗ |
return G_SOURCE_REMOVE; |
522 |
|
|
} |
523 |
|
|
|
524 |
|
|
static void |
525 |
|
✗ |
cc_wacom_panel_constructed (GObject *object) |
526 |
|
|
{ |
527 |
|
✗ |
CcWacomPanel *self = CC_WACOM_PANEL (object); |
528 |
|
|
CcShell *shell; |
529 |
|
|
|
530 |
|
✗ |
G_OBJECT_CLASS (cc_wacom_panel_parent_class)->constructed (object); |
531 |
|
|
|
532 |
|
|
/* Add test area button to shell header. */ |
533 |
|
✗ |
shell = cc_panel_get_shell (CC_PANEL (self)); |
534 |
|
|
|
535 |
|
✗ |
self->stylus_gesture = gtk_gesture_stylus_new (); |
536 |
|
✗ |
g_signal_connect_swapped (self->stylus_gesture, "proximity", |
537 |
|
|
G_CALLBACK (on_stylus_proximity_cb), self); |
538 |
|
✗ |
gtk_widget_add_controller (GTK_WIDGET (shell), |
539 |
|
✗ |
GTK_EVENT_CONTROLLER (self->stylus_gesture)); |
540 |
|
|
|
541 |
|
✗ |
if (g_getenv ("UMOCKDEV_DIR") != NULL) |
542 |
|
✗ |
self->mock_stylus_id = g_idle_add (show_mock_stylus_cb, self); |
543 |
|
✗ |
} |
544 |
|
|
|
545 |
|
|
static const char * |
546 |
|
✗ |
cc_wacom_panel_get_help_uri (CcPanel *panel) |
547 |
|
|
{ |
548 |
|
✗ |
return "help:gnome-help/wacom"; |
549 |
|
|
} |
550 |
|
|
|
551 |
|
|
static void |
552 |
|
✗ |
cc_wacom_panel_class_init (CcWacomPanelClass *klass) |
553 |
|
|
{ |
554 |
|
✗ |
GObjectClass *object_class = G_OBJECT_CLASS (klass); |
555 |
|
✗ |
CcPanelClass *panel_class = CC_PANEL_CLASS (klass); |
556 |
|
✗ |
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); |
557 |
|
|
|
558 |
|
✗ |
object_class->get_property = cc_wacom_panel_get_property; |
559 |
|
✗ |
object_class->set_property = cc_wacom_panel_set_property; |
560 |
|
✗ |
object_class->dispose = cc_wacom_panel_dispose; |
561 |
|
✗ |
object_class->constructed = cc_wacom_panel_constructed; |
562 |
|
|
|
563 |
|
✗ |
panel_class->get_help_uri = cc_wacom_panel_get_help_uri; |
564 |
|
|
|
565 |
|
✗ |
g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters"); |
566 |
|
|
|
567 |
|
✗ |
g_type_ensure (CC_TYPE_DRAWING_AREA); |
568 |
|
|
|
569 |
|
✗ |
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/wacom/cc-wacom-panel.ui"); |
570 |
|
|
|
571 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcWacomPanel, scrollable); |
572 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcWacomPanel, test_button); |
573 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcWacomPanel, test_popover); |
574 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcWacomPanel, test_draw_area); |
575 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcWacomPanel, tablets); |
576 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcWacomPanel, styli); |
577 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcWacomPanel, initial_state_stack); |
578 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcWacomPanel, panel_empty_state); |
579 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcWacomPanel, panel_view); |
580 |
|
✗ |
gtk_widget_class_bind_template_child (widget_class, CcWacomPanel, vadjustment); |
581 |
|
✗ |
} |
582 |
|
|
|
583 |
|
|
static void |
584 |
|
✗ |
add_known_device (CcWacomPanel *self, |
585 |
|
|
GsdDevice *gsd_device) |
586 |
|
|
{ |
587 |
|
|
CcWacomDevice *device; |
588 |
|
|
GsdDeviceType device_type; |
589 |
|
✗ |
g_autoptr(GList) tools = NULL; |
590 |
|
|
GtkWidget *page; |
591 |
|
✗ |
gboolean is_ekr = FALSE; |
592 |
|
|
GList *l; |
593 |
|
|
|
594 |
|
✗ |
device_type = gsd_device_get_device_type (gsd_device); |
595 |
|
|
|
596 |
|
✗ |
if ((device_type & GSD_DEVICE_TYPE_TABLET) == 0) |
597 |
|
✗ |
return; |
598 |
|
|
|
599 |
|
✗ |
if ((device_type & |
600 |
|
|
(GSD_DEVICE_TYPE_TOUCHSCREEN | |
601 |
|
|
GSD_DEVICE_TYPE_TOUCHPAD)) != 0) { |
602 |
|
✗ |
return; |
603 |
|
|
} |
604 |
|
|
|
605 |
|
✗ |
if ((device_type & GSD_DEVICE_TYPE_PAD) != 0) { |
606 |
|
|
const char *vendor, *product; |
607 |
|
|
|
608 |
|
✗ |
gsd_device_get_device_ids (gsd_device, &vendor, &product); |
609 |
|
✗ |
is_ekr = (g_strcmp0 (vendor, EKR_VENDOR) == 0 && |
610 |
|
✗ |
g_strcmp0 (product, EKR_PRODUCT) == 0); |
611 |
|
|
|
612 |
|
|
/* Express key remote is an special case, as it is an |
613 |
|
|
* external pad device, we want to distinctly show it |
614 |
|
|
* in the list. Other pads are mounted on a tablet, which |
615 |
|
|
* get their own entries. |
616 |
|
|
*/ |
617 |
|
✗ |
if (!is_ekr) |
618 |
|
✗ |
return; |
619 |
|
|
} |
620 |
|
|
|
621 |
|
✗ |
device = cc_wacom_device_new (gsd_device); |
622 |
|
✗ |
if (!device) |
623 |
|
✗ |
return; |
624 |
|
|
|
625 |
|
✗ |
g_hash_table_insert (self->devices, gsd_device, device); |
626 |
|
|
|
627 |
|
✗ |
tools = cc_tablet_tool_map_list_tools (self->tablet_tool_map, device); |
628 |
|
|
|
629 |
|
✗ |
for (l = tools; l != NULL; l = l->next) { |
630 |
|
✗ |
add_stylus (self, l->data); |
631 |
|
|
} |
632 |
|
|
|
633 |
|
✗ |
if (is_ekr) |
634 |
|
✗ |
page = cc_wacom_ekr_page_new (self, device); |
635 |
|
|
else |
636 |
|
✗ |
page = cc_wacom_page_new (self, device); |
637 |
|
|
|
638 |
|
✗ |
gtk_box_append (GTK_BOX (self->tablets), page); |
639 |
|
✗ |
g_hash_table_insert (self->pages, device, page); |
640 |
|
|
} |
641 |
|
|
|
642 |
|
|
static void |
643 |
|
✗ |
device_removed_cb (CcWacomPanel *self, |
644 |
|
|
GsdDevice *gsd_device) |
645 |
|
|
{ |
646 |
|
|
CcWacomDevice *device; |
647 |
|
|
GtkWidget *page; |
648 |
|
|
|
649 |
|
✗ |
device = g_hash_table_lookup (self->devices, gsd_device); |
650 |
|
✗ |
if (!device) |
651 |
|
✗ |
return; |
652 |
|
|
|
653 |
|
✗ |
page = g_hash_table_lookup (self->pages, device); |
654 |
|
✗ |
if (page) { |
655 |
|
✗ |
g_hash_table_remove (self->pages, device); |
656 |
|
✗ |
gtk_box_remove (GTK_BOX (self->tablets), page); |
657 |
|
|
} |
658 |
|
|
|
659 |
|
✗ |
g_hash_table_remove (self->devices, gsd_device); |
660 |
|
✗ |
check_remove_stylus_pages (self); |
661 |
|
✗ |
update_test_button (self); |
662 |
|
✗ |
update_initial_state (self); |
663 |
|
|
} |
664 |
|
|
|
665 |
|
|
static void |
666 |
|
✗ |
device_added_cb (CcWacomPanel *self, |
667 |
|
|
GsdDevice *device) |
668 |
|
|
{ |
669 |
|
✗ |
add_known_device (self, device); |
670 |
|
✗ |
update_test_button (self); |
671 |
|
✗ |
update_initial_state (self); |
672 |
|
✗ |
} |
673 |
|
|
|
674 |
|
|
void |
675 |
|
✗ |
cc_wacom_panel_switch_to_panel (CcWacomPanel *self, |
676 |
|
|
const char *panel) |
677 |
|
|
{ |
678 |
|
|
CcShell *shell; |
679 |
|
✗ |
g_autoptr(GError) error = NULL; |
680 |
|
|
|
681 |
|
✗ |
g_return_if_fail (self); |
682 |
|
|
|
683 |
|
✗ |
shell = cc_panel_get_shell (CC_PANEL (self)); |
684 |
|
✗ |
if (!cc_shell_set_active_panel_from_id (shell, panel, NULL, &error)) |
685 |
|
✗ |
g_warning ("Failed to activate '%s' panel: %s", panel, error->message); |
686 |
|
|
} |
687 |
|
|
|
688 |
|
|
static void |
689 |
|
✗ |
got_osd_proxy_cb (GObject *source_object, |
690 |
|
|
GAsyncResult *res, |
691 |
|
|
gpointer data) |
692 |
|
|
{ |
693 |
|
✗ |
g_autoptr(GError) error = NULL; |
694 |
|
|
CcWacomPanel *self; |
695 |
|
|
|
696 |
|
✗ |
self = CC_WACOM_PANEL (data); |
697 |
|
✗ |
self->proxy = g_dbus_proxy_new_for_bus_finish (res, &error); |
698 |
|
|
|
699 |
|
✗ |
if (self->proxy == NULL) { |
700 |
|
✗ |
g_printerr ("Error creating proxy: %s\n", error->message); |
701 |
|
✗ |
return; |
702 |
|
|
} |
703 |
|
|
} |
704 |
|
|
|
705 |
|
|
static void |
706 |
|
✗ |
got_input_mapping_proxy_cb (GObject *source_object, |
707 |
|
|
GAsyncResult *res, |
708 |
|
|
gpointer data) |
709 |
|
|
{ |
710 |
|
✗ |
g_autoptr(GError) error = NULL; |
711 |
|
|
CcWacomPanel *self; |
712 |
|
|
|
713 |
|
✗ |
self = CC_WACOM_PANEL (data); |
714 |
|
✗ |
self->input_mapping_proxy = g_dbus_proxy_new_for_bus_finish (res, &error); |
715 |
|
|
|
716 |
|
✗ |
if (self->input_mapping_proxy == NULL) { |
717 |
|
✗ |
g_printerr ("Error creating input mapping proxy: %s\n", error->message); |
718 |
|
✗ |
return; |
719 |
|
|
} |
720 |
|
|
} |
721 |
|
|
|
722 |
|
|
static void |
723 |
|
✗ |
cc_wacom_panel_init (CcWacomPanel *self) |
724 |
|
|
{ |
725 |
|
|
GsdDeviceManager *device_manager; |
726 |
|
✗ |
g_autoptr(GList) devices = NULL; |
727 |
|
|
GList *l; |
728 |
|
✗ |
g_autoptr(GError) error = NULL; |
729 |
|
|
|
730 |
|
✗ |
g_resources_register (cc_wacom_get_resource ()); |
731 |
|
|
|
732 |
|
✗ |
gtk_widget_init_template (GTK_WIDGET (self)); |
733 |
|
|
|
734 |
|
✗ |
self->tablet_tool_map = cc_tablet_tool_map_new (); |
735 |
|
|
|
736 |
|
✗ |
g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION, |
737 |
|
|
G_DBUS_PROXY_FLAGS_NONE, |
738 |
|
|
NULL, |
739 |
|
|
"org.gnome.Shell", |
740 |
|
|
"/org/gnome/Shell/Wacom", |
741 |
|
|
"org.gnome.Shell.Wacom.PadOsd", |
742 |
|
|
cc_panel_get_cancellable (CC_PANEL (self)), |
743 |
|
|
got_osd_proxy_cb, |
744 |
|
|
self); |
745 |
|
|
|
746 |
|
✗ |
g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION, |
747 |
|
|
G_DBUS_PROXY_FLAGS_NONE, |
748 |
|
|
NULL, |
749 |
|
|
"org.gnome.Shell", |
750 |
|
|
"/org/gnome/Mutter/InputMapping", |
751 |
|
|
"org.gnome.Mutter.InputMapping", |
752 |
|
|
cc_panel_get_cancellable (CC_PANEL (self)), |
753 |
|
|
got_input_mapping_proxy_cb, |
754 |
|
|
self); |
755 |
|
|
|
756 |
|
✗ |
self->devices = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref); |
757 |
|
✗ |
self->pages = g_hash_table_new (NULL, NULL); |
758 |
|
✗ |
self->stylus_pages = g_hash_table_new (NULL, NULL); |
759 |
|
|
|
760 |
|
✗ |
device_manager = gsd_device_manager_get (); |
761 |
|
✗ |
g_signal_connect_object (device_manager, "device-added", |
762 |
|
|
G_CALLBACK (device_added_cb), self, G_CONNECT_SWAPPED); |
763 |
|
✗ |
g_signal_connect_object (device_manager, "device-removed", |
764 |
|
|
G_CALLBACK (device_removed_cb), self, G_CONNECT_SWAPPED); |
765 |
|
|
|
766 |
|
✗ |
devices = gsd_device_manager_list_devices (device_manager, |
767 |
|
|
GSD_DEVICE_TYPE_TABLET); |
768 |
|
✗ |
for (l = devices; l ; l = l->next) |
769 |
|
✗ |
add_known_device (self, l->data); |
770 |
|
|
|
771 |
|
✗ |
update_test_button (self); |
772 |
|
✗ |
update_initial_state (self); |
773 |
|
✗ |
} |
774 |
|
|
|
775 |
|
|
GDBusProxy * |
776 |
|
✗ |
cc_wacom_panel_get_gsd_wacom_bus_proxy (CcWacomPanel *self) |
777 |
|
|
{ |
778 |
|
✗ |
g_return_val_if_fail (CC_IS_WACOM_PANEL (self), NULL); |
779 |
|
|
|
780 |
|
✗ |
return self->proxy; |
781 |
|
|
} |
782 |
|
|
|
783 |
|
|
GDBusProxy * |
784 |
|
✗ |
cc_wacom_panel_get_input_mapping_bus_proxy (CcWacomPanel *self) |
785 |
|
|
{ |
786 |
|
✗ |
g_return_val_if_fail (CC_IS_WACOM_PANEL (self), NULL); |
787 |
|
|
|
788 |
|
✗ |
return self->input_mapping_proxy; |
789 |
|
|
} |
790 |
|
|
|