1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimpinputdevicestore-gudev.c
5  * Input device store based on GUdev, the hardware abstraction layer.
6  * Copyright (C) 2007  Sven Neumann <sven@gimp.org>
7  *               2011  Michael Natterer <mitch@gimp.org>
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
21  */
22 
23 #include "config.h"
24 
25 #include <string.h>
26 
27 #include <gtk/gtk.h>
28 
29 #include "gimpinputdevicestore.h"
30 
31 #include "libgimpmodule/gimpmodule.h"
32 
33 
34 #ifdef HAVE_LIBGUDEV
35 
36 #include <gudev/gudev.h>
37 
38 enum
39 {
40   COLUMN_IDENTIFIER,
41   COLUMN_LABEL,
42   COLUMN_DEVICE_FILE,
43   NUM_COLUMNS
44 };
45 
46 enum
47 {
48   PROP_0,
49   PROP_CONSTRUCT_ERROR
50 };
51 
52 enum
53 {
54   DEVICE_ADDED,
55   DEVICE_REMOVED,
56   LAST_SIGNAL
57 };
58 
59 typedef struct _GimpInputDeviceStoreClass GimpInputDeviceStoreClass;
60 
61 struct _GimpInputDeviceStore
62 {
63   GtkListStore  parent_instance;
64 
65   GUdevClient  *client;
66   GError       *error;
67 };
68 
69 
70 struct _GimpInputDeviceStoreClass
71 {
72   GtkListStoreClass   parent_class;
73 
74   void  (* device_added)   (GimpInputDeviceStore *store,
75                             const gchar          *identifier);
76   void  (* device_removed) (GimpInputDeviceStore *store,
77                             const gchar          *identifier);
78 };
79 
80 
81 static void      gimp_input_device_store_finalize   (GObject              *object);
82 
83 static gboolean  gimp_input_device_store_add        (GimpInputDeviceStore *store,
84                                                      GUdevDevice          *device);
85 static gboolean  gimp_input_device_store_remove     (GimpInputDeviceStore *store,
86                                                      GUdevDevice          *device);
87 
88 static void      gimp_input_device_store_uevent     (GUdevClient          *client,
89                                                      const gchar          *action,
90                                                      GUdevDevice          *device,
91                                                      GimpInputDeviceStore *store);
92 
93 
94 G_DEFINE_DYNAMIC_TYPE (GimpInputDeviceStore, gimp_input_device_store,
95                        GTK_TYPE_LIST_STORE)
96 
97 static guint store_signals[LAST_SIGNAL] = { 0 };
98 
99 
100 void
gimp_input_device_store_register_types(GTypeModule * module)101 gimp_input_device_store_register_types (GTypeModule *module)
102 {
103   gimp_input_device_store_register_type (module);
104 }
105 
106 static void
gimp_input_device_store_class_init(GimpInputDeviceStoreClass * klass)107 gimp_input_device_store_class_init (GimpInputDeviceStoreClass *klass)
108 {
109   GObjectClass *object_class = G_OBJECT_CLASS (klass);
110 
111   store_signals[DEVICE_ADDED] =
112     g_signal_new ("device-added",
113                   G_TYPE_FROM_CLASS (klass),
114                   G_SIGNAL_RUN_FIRST,
115                   G_STRUCT_OFFSET (GimpInputDeviceStoreClass, device_added),
116                   NULL, NULL,
117                   g_cclosure_marshal_VOID__STRING,
118                   G_TYPE_NONE, 1, G_TYPE_STRING);
119 
120   store_signals[DEVICE_REMOVED] =
121     g_signal_new ("device-removed",
122                   G_TYPE_FROM_CLASS (klass),
123                   G_SIGNAL_RUN_FIRST,
124                   G_STRUCT_OFFSET (GimpInputDeviceStoreClass, device_removed),
125                   NULL, NULL,
126                   g_cclosure_marshal_VOID__STRING,
127                   G_TYPE_NONE, 1, G_TYPE_STRING);
128 
129   object_class->finalize = gimp_input_device_store_finalize;
130 
131   klass->device_added    = NULL;
132   klass->device_removed  = NULL;
133 }
134 
135 static void
gimp_input_device_store_class_finalize(GimpInputDeviceStoreClass * klass)136 gimp_input_device_store_class_finalize (GimpInputDeviceStoreClass *klass)
137 {
138 }
139 
140 static void
gimp_input_device_store_init(GimpInputDeviceStore * store)141 gimp_input_device_store_init (GimpInputDeviceStore *store)
142 {
143   GType        types[]      = { G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING };
144   const gchar *subsystems[] = { "input", NULL };
145   GList       *devices;
146   GList       *list;
147 
148   gtk_list_store_set_column_types (GTK_LIST_STORE (store),
149                                    G_N_ELEMENTS (types), types);
150 
151   store->client = g_udev_client_new (subsystems);
152 
153   devices = g_udev_client_query_by_subsystem (store->client, "input");
154 
155   for (list = devices; list; list = g_list_next (list))
156     {
157       GUdevDevice *device = list->data;
158 
159       gimp_input_device_store_add (store, device);
160       g_object_unref (device);
161     }
162 
163   g_list_free (devices);
164 
165   g_signal_connect (store->client, "uevent",
166                     G_CALLBACK (gimp_input_device_store_uevent),
167                     store);
168 }
169 
170 static void
gimp_input_device_store_finalize(GObject * object)171 gimp_input_device_store_finalize (GObject *object)
172 {
173   GimpInputDeviceStore *store = GIMP_INPUT_DEVICE_STORE (object);
174 
175   if (store->client)
176     {
177       g_object_unref (store->client);
178       store->client = NULL;
179     }
180 
181   if (store->error)
182     {
183       g_error_free (store->error);
184       store->error = NULL;
185     }
186 
187   G_OBJECT_CLASS (gimp_input_device_store_parent_class)->finalize (object);
188 }
189 
190 static gboolean
gimp_input_device_store_lookup(GimpInputDeviceStore * store,const gchar * identifier,GtkTreeIter * iter)191 gimp_input_device_store_lookup (GimpInputDeviceStore *store,
192                                 const gchar          *identifier,
193                                 GtkTreeIter          *iter)
194 {
195   GtkTreeModel *model = GTK_TREE_MODEL (store);
196   GValue        value = G_VALUE_INIT;
197   gboolean      iter_valid;
198 
199   for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
200        iter_valid;
201        iter_valid = gtk_tree_model_iter_next (model, iter))
202     {
203       const gchar *str;
204 
205       gtk_tree_model_get_value (model, iter, COLUMN_IDENTIFIER, &value);
206 
207       str = g_value_get_string (&value);
208 
209       if (strcmp (str, identifier) == 0)
210         {
211           g_value_unset (&value);
212           break;
213         }
214 
215       g_value_unset (&value);
216     }
217 
218   return iter_valid;
219 }
220 
221 /*  insert in alphabetic order  */
222 static void
gimp_input_device_store_insert(GimpInputDeviceStore * store,const gchar * identifier,const gchar * label,const gchar * device_file)223 gimp_input_device_store_insert (GimpInputDeviceStore *store,
224                                 const gchar          *identifier,
225                                 const gchar          *label,
226                                 const gchar          *device_file)
227 {
228   GtkTreeModel *model = GTK_TREE_MODEL (store);
229   GtkTreeIter   iter;
230   GValue        value = G_VALUE_INIT;
231   gint          pos   = 0;
232   gboolean      iter_valid;
233 
234   for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
235        iter_valid;
236        iter_valid = gtk_tree_model_iter_next (model, &iter), pos++)
237     {
238       const gchar *str;
239 
240       gtk_tree_model_get_value (model, &iter, COLUMN_LABEL, &value);
241 
242       str = g_value_get_string (&value);
243 
244       if (g_utf8_collate (label, str) < 0)
245         {
246           g_value_unset (&value);
247           break;
248         }
249 
250       g_value_unset (&value);
251     }
252 
253   gtk_list_store_insert_with_values (GTK_LIST_STORE (store), &iter, pos,
254                                      COLUMN_IDENTIFIER,  identifier,
255                                      COLUMN_LABEL,       label,
256                                      COLUMN_DEVICE_FILE, device_file,
257                                      -1);
258 }
259 
260 static gboolean
gimp_input_device_store_add(GimpInputDeviceStore * store,GUdevDevice * device)261 gimp_input_device_store_add (GimpInputDeviceStore *store,
262                              GUdevDevice          *device)
263 {
264   const gchar *device_file = g_udev_device_get_device_file (device);
265 #if 0
266   const gchar *path        = g_udev_device_get_sysfs_path (device);
267 #endif
268   const gchar *name        = g_udev_device_get_sysfs_attr (device, "name");
269 
270 #if 0
271   g_printerr ("\ndevice added: %s, %s, %s\n",
272               name ? name : "NULL",
273               device_file ? device_file : "NULL",
274               path);
275 #endif
276 
277   if (device_file)
278     {
279       if (name)
280         {
281           GtkTreeIter unused;
282 
283           if (! gimp_input_device_store_lookup (store, name, &unused))
284             {
285               gimp_input_device_store_insert (store, name, name, device_file);
286 
287               g_signal_emit (store, store_signals[DEVICE_ADDED], 0,
288                              name);
289 
290               return TRUE;
291             }
292         }
293       else
294         {
295           GUdevDevice *parent = g_udev_device_get_parent (device);
296 
297           if (parent)
298             {
299               const gchar *parent_name;
300 
301               parent_name = g_udev_device_get_sysfs_attr (parent, "name");
302 
303               if (parent_name)
304                 {
305                   GtkTreeIter unused;
306 
307                   if (! gimp_input_device_store_lookup (store, parent_name,
308                                                         &unused))
309                     {
310                       gimp_input_device_store_insert (store,
311                                                       parent_name, parent_name,
312                                                       device_file);
313 
314                       g_signal_emit (store, store_signals[DEVICE_ADDED], 0,
315                                      parent_name);
316 
317                       g_object_unref (parent);
318                       return TRUE;
319                     }
320                 }
321 
322               g_object_unref (parent);
323             }
324         }
325     }
326 
327   return FALSE;
328 }
329 
330 static gboolean
gimp_input_device_store_remove(GimpInputDeviceStore * store,GUdevDevice * device)331 gimp_input_device_store_remove (GimpInputDeviceStore *store,
332                                 GUdevDevice          *device)
333 {
334   const gchar *name = g_udev_device_get_sysfs_attr (device, "name");
335   GtkTreeIter  iter;
336 
337   if (name)
338     {
339       if (gimp_input_device_store_lookup (store, name, &iter))
340         {
341           gtk_list_store_remove (GTK_LIST_STORE (store), &iter);
342 
343           g_signal_emit (store, store_signals[DEVICE_REMOVED], 0, name);
344 
345           return TRUE;
346         }
347     }
348 
349   return FALSE;
350 }
351 
352 static void
gimp_input_device_store_uevent(GUdevClient * client,const gchar * action,GUdevDevice * device,GimpInputDeviceStore * store)353 gimp_input_device_store_uevent (GUdevClient          *client,
354                                 const gchar          *action,
355                                 GUdevDevice          *device,
356                                 GimpInputDeviceStore *store)
357 {
358   if (! strcmp (action, "add"))
359     {
360       gimp_input_device_store_add (store, device);
361     }
362   else if (! strcmp (action, "remove"))
363     {
364       gimp_input_device_store_remove (store, device);
365     }
366 }
367 
368 GimpInputDeviceStore *
gimp_input_device_store_new(void)369 gimp_input_device_store_new (void)
370 {
371   return g_object_new (GIMP_TYPE_INPUT_DEVICE_STORE, NULL);
372 }
373 
374 gchar *
gimp_input_device_store_get_device_file(GimpInputDeviceStore * store,const gchar * identifier)375 gimp_input_device_store_get_device_file (GimpInputDeviceStore *store,
376                                          const gchar          *identifier)
377 {
378   GtkTreeIter iter;
379 
380   g_return_val_if_fail (GIMP_IS_INPUT_DEVICE_STORE (store), NULL);
381   g_return_val_if_fail (identifier != NULL, NULL);
382 
383   if (! store->client)
384     return NULL;
385 
386   if (gimp_input_device_store_lookup (store, identifier, &iter))
387     {
388       GtkTreeModel *model = GTK_TREE_MODEL (store);
389       gchar        *device_file;
390 
391       gtk_tree_model_get (model, &iter,
392                           COLUMN_DEVICE_FILE, &device_file,
393                           -1);
394 
395       return device_file;
396     }
397 
398   return NULL;
399 }
400 
401 GError *
gimp_input_device_store_get_error(GimpInputDeviceStore * store)402 gimp_input_device_store_get_error (GimpInputDeviceStore  *store)
403 {
404   g_return_val_if_fail (GIMP_IS_INPUT_DEVICE_STORE (store), NULL);
405 
406   return store->error ? g_error_copy (store->error) : NULL;
407 }
408 
409 #else /* HAVE_LIBGUDEV */
410 
411 void
gimp_input_device_store_register_types(GTypeModule * module)412 gimp_input_device_store_register_types (GTypeModule *module)
413 {
414 }
415 
416 GType
gimp_input_device_store_get_type(void)417 gimp_input_device_store_get_type (void)
418 {
419   return G_TYPE_NONE;
420 }
421 
422 GimpInputDeviceStore *
gimp_input_device_store_new(void)423 gimp_input_device_store_new (void)
424 {
425   return NULL;
426 }
427 
428 gchar *
gimp_input_device_store_get_device_file(GimpInputDeviceStore * store,const gchar * identifier)429 gimp_input_device_store_get_device_file (GimpInputDeviceStore *store,
430                                          const gchar          *identifier)
431 {
432   return NULL;
433 }
434 
435 GError *
gimp_input_device_store_get_error(GimpInputDeviceStore * store)436 gimp_input_device_store_get_error (GimpInputDeviceStore  *store)
437 {
438   return NULL;
439 }
440 
441 #endif /* HAVE_LIBGUDEV */
442