gimpinputdevicestore-gudev.c 11.5 KB
Newer Older
1 2 3
/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
4 5
 * gimpinputdevicestore-gudev.c
 * 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 13 14 15 16 17 18 19
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * 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 24 25 26 27 28
 */

#include "config.h"

#include <string.h>

#include <gtk/gtk.h>

29 30
#include "gimpinputdevicestore.h"

31 32
#include "libgimpmodule/gimpmodule.h"

33

34
#ifdef HAVE_LIBGUDEV
35

36
#include <gudev/gudev.h>
37 38 39

enum
{
40
  COLUMN_IDENTIFIER,
41
  COLUMN_LABEL,
42
  COLUMN_DEVICE_FILE,
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
  NUM_COLUMNS
};

enum
{
  PROP_0,
  PROP_CONSTRUCT_ERROR
};

enum
{
  DEVICE_ADDED,
  DEVICE_REMOVED,
  LAST_SIGNAL
};

typedef struct _GimpInputDeviceStoreClass GimpInputDeviceStoreClass;

struct _GimpInputDeviceStore
{
63
  GtkListStore  parent_instance;
64

65 66
  GUdevClient  *client;
  GError       *error;
67 68 69 70 71 72 73
};


struct _GimpInputDeviceStoreClass
{
  GtkListStoreClass   parent_class;

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 89 90 91
static void      gimp_input_device_store_uevent     (GUdevClient          *client,
                                                     const gchar          *action,
                                                     GUdevDevice          *device,
                                                     GimpInputDeviceStore *store);
92 93


94 95
G_DEFINE_DYNAMIC_TYPE (GimpInputDeviceStore, gimp_input_device_store,
                       GTK_TYPE_LIST_STORE)
96

97
static guint store_signals[LAST_SIGNAL] = { 0 };
98 99


100 101 102 103
void
gimp_input_device_store_register_types (GTypeModule *module)
{
  gimp_input_device_store_register_type (module);
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
}

static void
gimp_input_device_store_class_init (GimpInputDeviceStoreClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  store_signals[DEVICE_ADDED] =
    g_signal_new ("device-added",
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GimpInputDeviceStoreClass, device_added),
                  NULL, NULL,
                  g_cclosure_marshal_VOID__STRING,
                  G_TYPE_NONE, 1, G_TYPE_STRING);

  store_signals[DEVICE_REMOVED] =
    g_signal_new ("device-removed",
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GimpInputDeviceStoreClass, device_removed),
                  NULL, NULL,
                  g_cclosure_marshal_VOID__STRING,
                  G_TYPE_NONE, 1, G_TYPE_STRING);

  object_class->finalize = gimp_input_device_store_finalize;

  klass->device_added    = NULL;
  klass->device_removed  = NULL;
}

135 136 137 138 139
static void
gimp_input_device_store_class_finalize (GimpInputDeviceStoreClass *klass)
{
}

140 141 142
static void
gimp_input_device_store_init (GimpInputDeviceStore *store)
{
143 144 145 146
  GType        types[]      = { G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING };
  const gchar *subsystems[] = { "input", NULL };
  GList       *devices;
  GList       *list;
147 148 149 150

  gtk_list_store_set_column_types (GTK_LIST_STORE (store),
                                   G_N_ELEMENTS (types), types);

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 160
      gimp_input_device_store_add (store, device);
      g_object_unref (device);
161 162
    }

163 164 165 166 167
  g_list_free (devices);

  g_signal_connect (store->client, "uevent",
                    G_CALLBACK (gimp_input_device_store_uevent),
                    store);
168 169 170 171 172 173 174
}

static void
gimp_input_device_store_finalize (GObject *object)
{
  GimpInputDeviceStore *store = GIMP_INPUT_DEVICE_STORE (object);

175
  if (store->client)
176
    {
177 178
      g_object_unref (store->client);
      store->client = NULL;
179 180 181 182 183 184 185 186
    }

  if (store->error)
    {
      g_error_free (store->error);
      store->error = NULL;
    }

187
  G_OBJECT_CLASS (gimp_input_device_store_parent_class)->finalize (object);
188 189 190 191
}

static gboolean
gimp_input_device_store_lookup (GimpInputDeviceStore *store,
192
                                const gchar          *identifier,
193 194 195
                                GtkTreeIter          *iter)
{
  GtkTreeModel *model = GTK_TREE_MODEL (store);
196
  GValue        value = G_VALUE_INIT;
197 198 199 200 201 202 203 204
  gboolean      iter_valid;

  for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
       iter_valid;
       iter_valid = gtk_tree_model_iter_next (model, iter))
    {
      const gchar *str;

205
      gtk_tree_model_get_value (model, iter, COLUMN_IDENTIFIER, &value);
206 207 208

      str = g_value_get_string (&value);

209
      if (strcmp (str, identifier) == 0)
210 211 212 213 214 215 216 217 218 219 220 221 222 223
        {
          g_value_unset (&value);
          break;
        }

      g_value_unset (&value);
    }

  return iter_valid;
}

/*  insert in alphabetic order  */
static void
gimp_input_device_store_insert (GimpInputDeviceStore *store,
224 225 226
                                const gchar          *identifier,
                                const gchar          *label,
                                const gchar          *device_file)
227 228 229
{
  GtkTreeModel *model = GTK_TREE_MODEL (store);
  GtkTreeIter   iter;
230
  GValue        value = G_VALUE_INIT;
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
  gint          pos   = 0;
  gboolean      iter_valid;

  for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
       iter_valid;
       iter_valid = gtk_tree_model_iter_next (model, &iter), pos++)
    {
      const gchar *str;

      gtk_tree_model_get_value (model, &iter, COLUMN_LABEL, &value);

      str = g_value_get_string (&value);

      if (g_utf8_collate (label, str) < 0)
        {
          g_value_unset (&value);
          break;
        }

      g_value_unset (&value);
    }

  gtk_list_store_insert_with_values (GTK_LIST_STORE (store), &iter, pos,
254 255 256
                                     COLUMN_IDENTIFIER,  identifier,
                                     COLUMN_LABEL,       label,
                                     COLUMN_DEVICE_FILE, device_file,
257 258 259 260 261
                                     -1);
}

static gboolean
gimp_input_device_store_add (GimpInputDeviceStore *store,
262
                             GUdevDevice          *device)
263
{
264 265 266 267 268 269 270 271 272 273 274 275 276 277
  const gchar *device_file = g_udev_device_get_device_file (device);
#if 0
  const gchar *path        = g_udev_device_get_sysfs_path (device);
#endif
  const gchar *name        = g_udev_device_get_sysfs_attr (device, "name");

#if 0
  g_printerr ("\ndevice added: %s, %s, %s\n",
              name ? name : "NULL",
              device_file ? device_file : "NULL",
              path);
#endif

  if (device_file)
278
    {
279 280 281 282 283 284 285
      if (name)
        {
          GtkTreeIter unused;

          if (! gimp_input_device_store_lookup (store, name, &unused))
            {
              gimp_input_device_store_insert (store, name, name, device_file);
286

287 288
              g_signal_emit (store, store_signals[DEVICE_ADDED], 0,
                             name);
289

290 291 292 293
              return TRUE;
            }
        }
      else
294
        {
295
          GUdevDevice *parent = g_udev_device_get_parent (device);
296

297
          if (parent)
298
            {
299 300 301
              const gchar *parent_name;

              parent_name = g_udev_device_get_sysfs_attr (parent, "name");
302

303
              if (parent_name)
304
                {
305 306 307 308 309 310 311 312
                  GtkTreeIter unused;

                  if (! gimp_input_device_store_lookup (store, parent_name,
                                                        &unused))
                    {
                      gimp_input_device_store_insert (store,
                                                      parent_name, parent_name,
                                                      device_file);
313

314 315
                      g_signal_emit (store, store_signals[DEVICE_ADDED], 0,
                                     parent_name);
316

317 318 319
                      g_object_unref (parent);
                      return TRUE;
                    }
320
                }
321

322 323
              g_object_unref (parent);
            }
324 325 326
        }
    }

327
  return FALSE;
328 329 330 331
}

static gboolean
gimp_input_device_store_remove (GimpInputDeviceStore *store,
332
                                GUdevDevice          *device)
333
{
334
  const gchar *name = g_udev_device_get_sysfs_attr (device, "name");
335 336
  GtkTreeIter  iter;

337
  if (name)
338
    {
339 340 341
      if (gimp_input_device_store_lookup (store, name, &iter))
        {
          gtk_list_store_remove (GTK_LIST_STORE (store), &iter);
342

343
          g_signal_emit (store, store_signals[DEVICE_REMOVED], 0, name);
344

345 346
          return TRUE;
        }
347 348 349 350 351 352
    }

  return FALSE;
}

static void
353 354 355 356
gimp_input_device_store_uevent (GUdevClient          *client,
                                const gchar          *action,
                                GUdevDevice          *device,
                                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 369 370 371 372 373 374 375
    }
}

GimpInputDeviceStore *
gimp_input_device_store_new (void)
{
  return g_object_new (GIMP_TYPE_INPUT_DEVICE_STORE, NULL);
}

gchar *
gimp_input_device_store_get_device_file (GimpInputDeviceStore *store,
376
                                         const gchar          *identifier)
377 378 379 380
{
  GtkTreeIter iter;

  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 385
    return NULL;

386
  if (gimp_input_device_store_lookup (store, identifier, &iter))
387
    {
388 389
      GtkTreeModel *model = GTK_TREE_MODEL (store);
      gchar        *device_file;
390

391 392 393
      gtk_tree_model_get (model, &iter,
                          COLUMN_DEVICE_FILE, &device_file,
                          -1);
394

395
      return device_file;
396 397 398 399 400 401 402 403 404 405 406 407 408
    }

  return NULL;
}

GError *
gimp_input_device_store_get_error (GimpInputDeviceStore  *store)
{
  g_return_val_if_fail (GIMP_IS_INPUT_DEVICE_STORE (store), NULL);

  return store->error ? g_error_copy (store->error) : NULL;
}

409
#else /* HAVE_LIBGUDEV */
410

411 412 413 414
void
gimp_input_device_store_register_types (GTypeModule *module)
{
}
415 416

GType
417
gimp_input_device_store_get_type (void)
418 419 420 421 422 423 424 425 426 427 428 429
{
  return G_TYPE_NONE;
}

GimpInputDeviceStore *
gimp_input_device_store_new (void)
{
  return NULL;
}

gchar *
gimp_input_device_store_get_device_file (GimpInputDeviceStore *store,
430
                                         const gchar          *identifier)
431 432 433 434 435 436 437 438 439 440
{
  return NULL;
}

GError *
gimp_input_device_store_get_error (GimpInputDeviceStore  *store)
{
  return NULL;
}

441
#endif /* HAVE_LIBGUDEV */