Line |
Branch |
Exec |
Source |
1 |
|
|
/* |
2 |
|
|
* Copyright © 2016 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: Carlos Garnacho <carlosg@gnome.org> |
18 |
|
|
* |
19 |
|
|
*/ |
20 |
|
|
|
21 |
|
|
#include "config.h" |
22 |
|
|
#include "cc-tablet-tool-map.h" |
23 |
|
|
|
24 |
|
|
#define KEY_TOOL_ID "ID" |
25 |
|
|
#define KEY_DEVICE_STYLI "Styli" |
26 |
|
|
#define GENERIC_STYLUS "generic" |
27 |
|
|
|
28 |
|
|
typedef struct _CcTabletToolMap CcTabletToolMap; |
29 |
|
|
|
30 |
|
|
struct _CcTabletToolMap { |
31 |
|
|
GObject parent_instance; |
32 |
|
|
GKeyFile *tablets; |
33 |
|
|
GKeyFile *tools; |
34 |
|
|
GHashTable *tool_map; |
35 |
|
|
GHashTable *tablet_map; |
36 |
|
|
GHashTable *no_serial_tool_map; |
37 |
|
|
|
38 |
|
|
gchar *tablet_path; |
39 |
|
|
gchar *tool_path; |
40 |
|
|
}; |
41 |
|
|
|
42 |
|
✗ |
G_DEFINE_TYPE (CcTabletToolMap, cc_tablet_tool_map, G_TYPE_OBJECT) |
43 |
|
|
|
44 |
|
|
static void |
45 |
|
✗ |
load_keyfiles (CcTabletToolMap *map) |
46 |
|
|
{ |
47 |
|
✗ |
g_autoptr(GError) devices_error = NULL; |
48 |
|
✗ |
g_autoptr(GError) tools_error = NULL; |
49 |
|
✗ |
g_autofree gchar *dir = NULL; |
50 |
|
|
|
51 |
|
✗ |
dir = g_build_filename (g_get_user_cache_dir (), "gnome-control-center", "wacom", NULL); |
52 |
|
|
|
53 |
|
✗ |
if (g_mkdir_with_parents (dir, 0700) < 0) { |
54 |
|
✗ |
g_warning ("Could not create directory '%s', expect stylus mapping oddities: %m", dir); |
55 |
|
✗ |
return; |
56 |
|
|
} |
57 |
|
|
|
58 |
|
✗ |
map->tablet_path = g_build_filename (dir, "devices", NULL); |
59 |
|
✗ |
g_key_file_load_from_file (map->tablets, map->tablet_path, |
60 |
|
|
G_KEY_FILE_NONE, &devices_error); |
61 |
|
|
|
62 |
|
✗ |
if (devices_error && !g_error_matches (devices_error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { |
63 |
|
✗ |
g_warning ("Could not load tablets keyfile '%s': %s", |
64 |
|
|
map->tablet_path, devices_error->message); |
65 |
|
|
} |
66 |
|
|
|
67 |
|
✗ |
map->tool_path = g_build_filename (dir, "tools", NULL); |
68 |
|
✗ |
g_key_file_load_from_file (map->tools, map->tool_path, |
69 |
|
|
G_KEY_FILE_NONE, &tools_error); |
70 |
|
|
|
71 |
|
✗ |
if (tools_error && !g_error_matches (tools_error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { |
72 |
|
✗ |
g_warning ("Could not load tools keyfile '%s': %s", |
73 |
|
|
map->tool_path, tools_error->message); |
74 |
|
|
} |
75 |
|
|
} |
76 |
|
|
|
77 |
|
|
static void |
78 |
|
✗ |
cache_tools (CcTabletToolMap *map) |
79 |
|
|
{ |
80 |
|
✗ |
g_auto(GStrv) serials = NULL; |
81 |
|
|
gsize n_serials, i; |
82 |
|
|
|
83 |
|
✗ |
serials = g_key_file_get_groups (map->tools, &n_serials); |
84 |
|
|
|
85 |
|
✗ |
for (i = 0; i < n_serials; i++) { |
86 |
|
✗ |
g_autofree gchar *str = NULL; |
87 |
|
|
gchar *end; |
88 |
|
|
guint64 serial, id; |
89 |
|
✗ |
g_autoptr(GError) error = NULL; |
90 |
|
|
CcWacomTool *tool; |
91 |
|
|
|
92 |
|
✗ |
serial = g_ascii_strtoull (serials[i], &end, 16); |
93 |
|
|
|
94 |
|
✗ |
if (*end != '\0') { |
95 |
|
✗ |
g_warning ("Invalid tool serial %s", serials[i]); |
96 |
|
✗ |
continue; |
97 |
|
|
} |
98 |
|
|
|
99 |
|
✗ |
str = g_key_file_get_string (map->tools, serials[i], KEY_TOOL_ID, &error); |
100 |
|
✗ |
if (str == NULL) { |
101 |
|
✗ |
g_warning ("Could not get cached ID for tool with serial %s: %s", |
102 |
|
|
serials[i], error->message); |
103 |
|
✗ |
continue; |
104 |
|
|
} |
105 |
|
|
|
106 |
|
✗ |
id = g_ascii_strtoull (str, &end, 16); |
107 |
|
✗ |
if (*end != '\0') { |
108 |
|
✗ |
g_warning ("Invalid tool ID %s", str); |
109 |
|
✗ |
continue; |
110 |
|
|
} |
111 |
|
|
|
112 |
|
✗ |
tool = cc_wacom_tool_new (serial, id, NULL); |
113 |
|
✗ |
g_hash_table_insert (map->tool_map, g_strdup (serials[i]), tool); |
114 |
|
|
} |
115 |
|
✗ |
} |
116 |
|
|
|
117 |
|
|
static void |
118 |
|
✗ |
cache_devices (CcTabletToolMap *map) |
119 |
|
|
{ |
120 |
|
|
gchar **ids; |
121 |
|
|
gsize n_ids, i; |
122 |
|
|
|
123 |
|
✗ |
ids = g_key_file_get_groups (map->tablets, &n_ids); |
124 |
|
|
|
125 |
|
✗ |
for (i = 0; i < n_ids; i++) { |
126 |
|
|
gchar **styli; |
127 |
|
|
gsize n_styli, j; |
128 |
|
✗ |
g_autoptr(GError) error = NULL; |
129 |
|
✗ |
GList *tools = NULL; |
130 |
|
|
|
131 |
|
✗ |
styli = g_key_file_get_string_list (map->tablets, ids[i], KEY_DEVICE_STYLI, &n_styli, &error); |
132 |
|
✗ |
if (styli == NULL) { |
133 |
|
✗ |
g_warning ("Could not get cached styli for with ID %s: %s", |
134 |
|
|
ids[i], error->message); |
135 |
|
✗ |
continue; |
136 |
|
|
} |
137 |
|
|
|
138 |
|
✗ |
for (j = 0; j < n_styli; j++) { |
139 |
|
|
CcWacomTool *tool; |
140 |
|
|
|
141 |
|
✗ |
if (g_str_equal (styli[j], GENERIC_STYLUS)) { |
142 |
|
|
/* We don't have a GsdDevice yet to create the |
143 |
|
|
* serial=0 CcWacomTool, insert a NULL and defer |
144 |
|
|
* to device lookups. |
145 |
|
|
*/ |
146 |
|
✗ |
g_hash_table_insert (map->no_serial_tool_map, |
147 |
|
✗ |
g_strdup (ids[i]), NULL); |
148 |
|
|
} |
149 |
|
|
|
150 |
|
✗ |
tool = g_hash_table_lookup (map->tool_map, styli[j]); |
151 |
|
|
|
152 |
|
✗ |
if (tool) |
153 |
|
✗ |
tools = g_list_prepend (tools, tool); |
154 |
|
|
} |
155 |
|
|
|
156 |
|
✗ |
if (tools) { |
157 |
|
✗ |
g_hash_table_insert (map->tablet_map, g_strdup (ids[i]), tools); |
158 |
|
|
} |
159 |
|
|
|
160 |
|
✗ |
g_strfreev (styli); |
161 |
|
|
} |
162 |
|
|
|
163 |
|
✗ |
g_strfreev (ids); |
164 |
|
✗ |
} |
165 |
|
|
|
166 |
|
|
static void |
167 |
|
✗ |
cc_tablet_tool_map_finalize (GObject *object) |
168 |
|
|
{ |
169 |
|
✗ |
CcTabletToolMap *map = CC_TABLET_TOOL_MAP (object); |
170 |
|
|
|
171 |
|
✗ |
g_key_file_unref (map->tools); |
172 |
|
✗ |
g_key_file_unref (map->tablets); |
173 |
|
✗ |
g_hash_table_destroy (map->tool_map); |
174 |
|
✗ |
g_hash_table_destroy (map->tablet_map); |
175 |
|
✗ |
g_hash_table_destroy (map->no_serial_tool_map); |
176 |
|
✗ |
g_free (map->tablet_path); |
177 |
|
✗ |
g_free (map->tool_path); |
178 |
|
|
|
179 |
|
✗ |
G_OBJECT_CLASS (cc_tablet_tool_map_parent_class)->finalize (object); |
180 |
|
✗ |
} |
181 |
|
|
|
182 |
|
|
static void |
183 |
|
✗ |
null_safe_unref (gpointer data) |
184 |
|
|
{ |
185 |
|
✗ |
if (data != NULL) |
186 |
|
✗ |
g_object_unref (data); |
187 |
|
✗ |
} |
188 |
|
|
|
189 |
|
|
static void |
190 |
|
✗ |
cc_tablet_tool_map_init (CcTabletToolMap *map) |
191 |
|
|
{ |
192 |
|
✗ |
map->tablets = g_key_file_new (); |
193 |
|
✗ |
map->tools = g_key_file_new (); |
194 |
|
✗ |
map->tool_map = g_hash_table_new_full (g_str_hash, g_str_equal, |
195 |
|
|
(GDestroyNotify) g_free, |
196 |
|
|
(GDestroyNotify) g_object_unref); |
197 |
|
✗ |
map->tablet_map = g_hash_table_new_full (g_str_hash, g_str_equal, |
198 |
|
|
(GDestroyNotify) g_free, |
199 |
|
|
(GDestroyNotify) g_list_free); |
200 |
|
✗ |
map->no_serial_tool_map = g_hash_table_new_full (g_str_hash, g_str_equal, |
201 |
|
|
(GDestroyNotify) g_free, |
202 |
|
|
(GDestroyNotify) null_safe_unref); |
203 |
|
✗ |
load_keyfiles (map); |
204 |
|
✗ |
cache_tools (map); |
205 |
|
✗ |
cache_devices (map); |
206 |
|
✗ |
} |
207 |
|
|
|
208 |
|
|
static void |
209 |
|
✗ |
cc_tablet_tool_map_class_init (CcTabletToolMapClass *klass) |
210 |
|
|
{ |
211 |
|
✗ |
GObjectClass *object_class = G_OBJECT_CLASS (klass); |
212 |
|
|
|
213 |
|
✗ |
object_class->finalize = cc_tablet_tool_map_finalize; |
214 |
|
✗ |
} |
215 |
|
|
|
216 |
|
|
CcTabletToolMap * |
217 |
|
✗ |
cc_tablet_tool_map_new (void) |
218 |
|
|
{ |
219 |
|
✗ |
return g_object_new (CC_TYPE_TABLET_TOOL_MAP, NULL); |
220 |
|
|
} |
221 |
|
|
|
222 |
|
|
static gchar * |
223 |
|
✗ |
get_device_key (CcWacomDevice *device) |
224 |
|
|
{ |
225 |
|
|
const gchar *vendor, *product; |
226 |
|
|
GsdDevice *gsd_device; |
227 |
|
|
|
228 |
|
✗ |
gsd_device = cc_wacom_device_get_device (device); |
229 |
|
✗ |
gsd_device_get_device_ids (gsd_device, &vendor, &product); |
230 |
|
|
|
231 |
|
✗ |
return g_strdup_printf ("%s:%s", vendor, product); |
232 |
|
|
} |
233 |
|
|
|
234 |
|
|
static gchar * |
235 |
|
✗ |
get_tool_key (guint64 serial) |
236 |
|
|
{ |
237 |
|
✗ |
return g_strdup_printf ("%lx", serial); |
238 |
|
|
} |
239 |
|
|
|
240 |
|
|
GList * |
241 |
|
✗ |
cc_tablet_tool_map_list_tools (CcTabletToolMap *map, |
242 |
|
|
CcWacomDevice *device) |
243 |
|
|
{ |
244 |
|
|
CcWacomTool *no_serial_tool; |
245 |
|
|
GList *styli; |
246 |
|
✗ |
g_autofree gchar *key = NULL; |
247 |
|
|
|
248 |
|
✗ |
g_return_val_if_fail (CC_IS_TABLET_TOOL_MAP (map), NULL); |
249 |
|
✗ |
g_return_val_if_fail (CC_IS_WACOM_DEVICE (device), NULL); |
250 |
|
|
|
251 |
|
✗ |
key = get_device_key (device); |
252 |
|
✗ |
styli = g_list_copy (g_hash_table_lookup (map->tablet_map, key)); |
253 |
|
|
|
254 |
|
✗ |
if (g_hash_table_lookup_extended (map->no_serial_tool_map, key, |
255 |
|
|
NULL, (gpointer) &no_serial_tool)) { |
256 |
|
✗ |
if (!no_serial_tool) { |
257 |
|
✗ |
no_serial_tool = cc_wacom_tool_new (0, 0, device); |
258 |
|
✗ |
g_hash_table_replace (map->no_serial_tool_map, |
259 |
|
✗ |
g_strdup (key), |
260 |
|
|
no_serial_tool); |
261 |
|
|
} |
262 |
|
|
|
263 |
|
✗ |
styli = g_list_prepend (styli, no_serial_tool); |
264 |
|
|
} |
265 |
|
|
|
266 |
|
✗ |
return styli; |
267 |
|
|
} |
268 |
|
|
|
269 |
|
|
CcWacomTool * |
270 |
|
✗ |
cc_tablet_tool_map_lookup_tool (CcTabletToolMap *map, |
271 |
|
|
CcWacomDevice *device, |
272 |
|
|
guint64 serial) |
273 |
|
|
{ |
274 |
|
✗ |
CcWacomTool *tool = NULL; |
275 |
|
✗ |
g_autofree gchar *key = NULL; |
276 |
|
|
|
277 |
|
✗ |
g_return_val_if_fail (CC_IS_TABLET_TOOL_MAP (map), FALSE); |
278 |
|
✗ |
g_return_val_if_fail (CC_IS_WACOM_DEVICE (device), FALSE); |
279 |
|
|
|
280 |
|
✗ |
if (serial == 0) { |
281 |
|
✗ |
key = get_device_key (device); |
282 |
|
✗ |
tool = g_hash_table_lookup (map->no_serial_tool_map, key); |
283 |
|
|
} else { |
284 |
|
✗ |
key = get_tool_key (serial); |
285 |
|
✗ |
tool = g_hash_table_lookup (map->tool_map, key); |
286 |
|
|
} |
287 |
|
|
|
288 |
|
✗ |
return tool; |
289 |
|
|
} |
290 |
|
|
|
291 |
|
|
static void |
292 |
|
✗ |
keyfile_add_device_stylus (CcTabletToolMap *map, |
293 |
|
|
const gchar *device_key, |
294 |
|
|
const gchar *tool_key) |
295 |
|
|
{ |
296 |
|
✗ |
g_autoptr(GArray) array = NULL; |
297 |
|
✗ |
g_auto(GStrv) styli = NULL; |
298 |
|
|
gsize n_styli; |
299 |
|
|
|
300 |
|
✗ |
array = g_array_new (FALSE, FALSE, sizeof (gchar *)); |
301 |
|
✗ |
styli = g_key_file_get_string_list (map->tablets, device_key, |
302 |
|
|
KEY_DEVICE_STYLI, &n_styli, |
303 |
|
|
NULL); |
304 |
|
|
|
305 |
|
✗ |
if (styli) { |
306 |
|
✗ |
g_array_append_vals (array, styli, n_styli); |
307 |
|
|
} |
308 |
|
|
|
309 |
|
✗ |
g_array_append_val (array, tool_key); |
310 |
|
✗ |
g_key_file_set_string_list (map->tablets, device_key, KEY_DEVICE_STYLI, |
311 |
|
✗ |
(const gchar **) array->data, array->len); |
312 |
|
✗ |
} |
313 |
|
|
|
314 |
|
|
static void |
315 |
|
✗ |
keyfile_add_stylus (CcTabletToolMap *map, |
316 |
|
|
const gchar *tool_key, |
317 |
|
|
guint64 id) |
318 |
|
|
{ |
319 |
|
✗ |
g_autofree gchar *str = NULL; |
320 |
|
|
|
321 |
|
|
/* Also works for IDs */ |
322 |
|
✗ |
str = get_tool_key (id); |
323 |
|
✗ |
g_key_file_set_string (map->tools, tool_key, KEY_TOOL_ID, str); |
324 |
|
✗ |
} |
325 |
|
|
|
326 |
|
|
void |
327 |
|
✗ |
cc_tablet_tool_map_add_relation (CcTabletToolMap *map, |
328 |
|
|
CcWacomDevice *device, |
329 |
|
|
CcWacomTool *tool) |
330 |
|
|
{ |
331 |
|
✗ |
gboolean tablets_changed = FALSE, tools_changed = FALSE; |
332 |
|
✗ |
gboolean new_tool_without_serial = FALSE; |
333 |
|
✗ |
g_autofree gchar *tool_key = NULL; |
334 |
|
✗ |
g_autofree gchar *device_key = NULL; |
335 |
|
|
guint64 serial, id; |
336 |
|
|
GList *styli; |
337 |
|
|
|
338 |
|
✗ |
g_return_if_fail (CC_IS_TABLET_TOOL_MAP (map)); |
339 |
|
✗ |
g_return_if_fail (CC_IS_WACOM_DEVICE (device)); |
340 |
|
✗ |
g_return_if_fail (CC_IS_WACOM_TOOL (tool)); |
341 |
|
|
|
342 |
|
✗ |
serial = cc_wacom_tool_get_serial (tool); |
343 |
|
✗ |
id = cc_wacom_tool_get_id (tool); |
344 |
|
✗ |
device_key = get_device_key (device); |
345 |
|
|
|
346 |
|
✗ |
if (serial == 0) { |
347 |
|
✗ |
tool_key = g_strdup (GENERIC_STYLUS); |
348 |
|
|
|
349 |
|
✗ |
if (!g_hash_table_contains (map->no_serial_tool_map, device_key)) { |
350 |
|
✗ |
g_hash_table_insert (map->no_serial_tool_map, |
351 |
|
✗ |
g_strdup (device_key), |
352 |
|
|
g_object_ref (tool)); |
353 |
|
✗ |
new_tool_without_serial = TRUE; |
354 |
|
|
} |
355 |
|
|
} else { |
356 |
|
✗ |
tool_key = get_tool_key (serial); |
357 |
|
|
|
358 |
|
✗ |
if (!g_hash_table_contains (map->tool_map, tool_key)) { |
359 |
|
✗ |
keyfile_add_stylus (map, tool_key, id); |
360 |
|
✗ |
tools_changed = TRUE; |
361 |
|
✗ |
g_hash_table_insert (map->tool_map, |
362 |
|
✗ |
g_strdup (tool_key), |
363 |
|
|
g_object_ref (tool)); |
364 |
|
|
} |
365 |
|
|
} |
366 |
|
|
|
367 |
|
✗ |
styli = g_hash_table_lookup (map->tablet_map, device_key); |
368 |
|
|
|
369 |
|
✗ |
if (!g_list_find (styli, tool)) { |
370 |
|
✗ |
styli = g_list_prepend (styli, tool); |
371 |
|
✗ |
g_hash_table_replace (map->tablet_map, |
372 |
|
✗ |
g_strdup (device_key), |
373 |
|
✗ |
g_list_copy (styli)); |
374 |
|
|
|
375 |
|
✗ |
if (serial || new_tool_without_serial) { |
376 |
|
✗ |
tablets_changed = TRUE; |
377 |
|
✗ |
keyfile_add_device_stylus (map, device_key, tool_key); |
378 |
|
|
} |
379 |
|
|
} |
380 |
|
|
|
381 |
|
✗ |
if (tools_changed) { |
382 |
|
✗ |
g_autoptr(GError) error = NULL; |
383 |
|
|
|
384 |
|
✗ |
if (!g_key_file_save_to_file (map->tools, map->tool_path, &error)) { |
385 |
|
✗ |
g_warning ("Error saving tools keyfile: %s", |
386 |
|
|
error->message); |
387 |
|
|
} |
388 |
|
|
} |
389 |
|
|
|
390 |
|
✗ |
if (tablets_changed) { |
391 |
|
✗ |
g_autoptr(GError) error = NULL; |
392 |
|
|
|
393 |
|
✗ |
if (!g_key_file_save_to_file (map->tablets, map->tablet_path, &error)) { |
394 |
|
✗ |
g_warning ("Error saving tablets keyfile: %s", |
395 |
|
|
error->message); |
396 |
|
|
} |
397 |
|
|
} |
398 |
|
|
} |
399 |
|
|
|