GCC Code Coverage Report


Directory: ./
File: panels/wacom/cc-tablet-tool-map.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 176 0.0%
Functions: 0 18 0.0%
Branches: 0 97 0.0%

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