1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3
* Copyright (C) 2009-2012 Richard Hughes <richard@hughsie.com>
5
* Licensed under the GNU General Public License Version 2
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25
#include <glib/gi18n.h>
27
#include <sane/sane.h>
28
#include <gudev/gudev.h>
30
#include "cd-client.h"
31
#include "cd-device.h"
35
gboolean doing_refresh;
37
GDBusNodeInfo *introspection;
39
GPtrArray *array; /* of CdMainDev's */
40
GUdevClient *gudev_client;
47
gchar *id; /* note: we can get this from CdDevice, but we don't wan't to connect() */
51
#define COLORD_SANE_DBUS_SERVICE "org.freedesktop.colord-sane"
52
#define COLORD_SANE_DBUS_PATH "/org/freedesktop/colord_sane"
53
#define COLORD_SANE_DBUS_INTERFACE "org.freedesktop.colord.sane"
55
#define COLORD_SANE_UEVENT_DELAY 2 /* seconds */
61
cd_main_dev_free (CdMainDev *tmp)
63
g_object_unref (tmp->device);
69
* cd_main_dev_set_invalid:
72
cd_main_dev_set_invalid (CdMainPrivate *priv)
78
if (priv->array->len == 0)
80
for (i = 0; i < priv->array->len; i++) {
81
tmp = g_ptr_array_index (priv->array, i);
87
* cd_main_dev_find_by_id:
90
cd_main_dev_find_by_id (CdMainPrivate *priv,
97
if (priv->array->len == 0)
99
for (i = 0; i < priv->array->len; i++) {
100
tmp = g_ptr_array_index (priv->array, i);
101
if (g_strcmp0 (tmp->id, id) == 0)
109
* cd_client_get_id_for_sane_device:
112
cd_client_get_id_for_sane_device (const SANE_Device *sane_device)
115
id = g_strdup_printf ("sane-%s", sane_device->model);
122
} CdMainCreateDeviceHelper;
125
* cd_main_colord_create_device_cb:
128
cd_main_colord_create_device_cb (GObject *source_object,
132
CdClient *client = CD_CLIENT (source_object);
134
CdMainCreateDeviceHelper *helper = (CdMainCreateDeviceHelper *) user_data;
136
CdMainPrivate *priv = helper->priv;
137
GError *error = NULL;
140
device = cd_client_create_device_finish (client, res, &error);
141
if (device == NULL) {
142
g_warning ("failed to create device: %s",
144
g_error_free (error);
147
g_debug ("Added device: %s", cd_device_get_object_path (device));
148
dev = g_new (CdMainDev, 1);
149
dev->id = g_strdup (helper->id);
150
dev->device = g_object_ref (device);
151
g_ptr_array_add (priv->array, dev);
156
g_object_unref (device);
160
* cd_sane_client_add:
163
cd_sane_client_add (CdMainPrivate *priv, const SANE_Device *sane_device)
165
CdMainCreateDeviceHelper *helper = NULL;
169
gchar *vendor = NULL;
170
GHashTable *properties = NULL;
172
/* ignore noname, no support devices */
173
if (g_strcmp0 (sane_device->vendor, "Noname") == 0) {
174
g_debug ("CdSaneClient: Ignoring sane device %s",
179
/* convert device_id 'plustek:libusb:004:002' to suitable id */
180
id = cd_client_get_id_for_sane_device (sane_device);
182
/* see if this device already exists */
183
dev = cd_main_dev_find_by_id (priv, id);
189
/* Make human readable */
190
model = g_strdup (sane_device->model);
191
g_strdelimit (model, "_", ' ');
192
vendor = g_strdup (sane_device->vendor);
193
g_strdelimit (vendor, "_", ' ');
195
/* create initial device properties */
196
properties = g_hash_table_new_full (g_str_hash, g_str_equal,
198
g_hash_table_insert (properties,
199
(gpointer) CD_DEVICE_PROPERTY_KIND,
200
(gpointer) cd_device_kind_to_string (CD_DEVICE_KIND_SCANNER));
201
g_hash_table_insert (properties,
202
(gpointer) CD_DEVICE_PROPERTY_MODE,
203
(gpointer) cd_device_mode_to_string (CD_DEVICE_MODE_PHYSICAL));
204
g_hash_table_insert (properties,
205
(gpointer) CD_DEVICE_PROPERTY_COLORSPACE,
206
(gpointer) cd_colorspace_to_string (CD_COLORSPACE_RGB));
207
g_hash_table_insert (properties,
208
(gpointer) CD_DEVICE_PROPERTY_VENDOR,
210
g_hash_table_insert (properties,
211
(gpointer) CD_DEVICE_PROPERTY_MODEL,
213
g_hash_table_insert (properties,
214
(gpointer) CD_DEVICE_PROPERTY_SERIAL,
215
(gpointer) sane_device->name);
216
helper = g_new0 (CdMainCreateDeviceHelper, 1);
218
helper->id = g_strdup (id);
219
cd_client_create_device (priv->client,
221
CD_OBJECT_SCOPE_TEMP,
224
cd_main_colord_create_device_cb,
227
if (properties != NULL)
228
g_hash_table_unref (properties);
235
* cd_main_colord_delete_device_cb:
238
cd_main_colord_delete_device_cb (GObject *source_object,
242
CdClient *client = CD_CLIENT (source_object);
244
GError *error = NULL;
247
ret = cd_client_delete_device_finish (client, res, &error);
249
g_warning ("failed to delete device: %s",
251
g_error_free (error);
256
* cd_sane_client_remove:
259
cd_sane_client_remove (CdMainPrivate *priv, CdDevice *device)
261
g_debug ("Deleting device: %s", cd_device_get_object_path (device));
262
cd_client_delete_device (priv->client,
265
cd_main_colord_delete_device_cb,
270
* cd_sane_client_refresh:
273
cd_sane_client_refresh (CdMainPrivate *priv)
276
const SANE_Device **device_list = NULL;
281
/* don't be re-entrant */
282
if (priv->doing_refresh)
284
priv->doing_refresh = TRUE;
286
/* force sane to drop it's cache of devices -- yes, it is that crap */
287
if (priv->init_sane) {
289
priv->init_sane = FALSE;
291
status = sane_init (NULL, NULL);
292
if (status != SANE_STATUS_GOOD) {
293
g_warning ("failed to init SANE: %s",
294
sane_strstatus (status));
297
priv->init_sane = TRUE;
299
/* invalidate all devices */
300
cd_main_dev_set_invalid (priv);
302
/* get scanners on the local server */
303
status = sane_get_devices (&device_list, TRUE);
304
if (status != SANE_STATUS_GOOD) {
305
g_warning ("failed to get devices from SANE: %s",
306
sane_strstatus (status));
311
if (device_list == NULL || device_list[0] == NULL)
315
for (idx = 0; device_list[idx] != NULL; idx++)
316
cd_sane_client_add (priv, device_list[idx]);
318
/* remove any that are invalid */
319
for (i = 0; i < priv->array->len; i++) {
320
tmp = g_ptr_array_index (priv->array, i);
323
cd_sane_client_remove (priv, tmp->device);
326
priv->doing_refresh = FALSE;
330
* cd_sane_client_refresh_cb:
333
cd_sane_client_refresh_cb (gpointer user_data)
335
CdMainPrivate *priv = (CdMainPrivate *) user_data;
336
g_debug ("Refreshing scanner devices...");
337
cd_sane_client_refresh (priv);
343
* cd_sane_client_refresh_schedule:
346
cd_sane_client_refresh_schedule (CdMainPrivate *priv)
348
if (priv->timer_id != 0)
349
g_source_remove (priv->timer_id);
350
priv->timer_id = g_timeout_add_seconds (COLORD_SANE_UEVENT_DELAY,
351
cd_sane_client_refresh_cb,
356
* cd_main_daemon_method_call:
359
cd_main_daemon_method_call (GDBusConnection *connection_, const gchar *sender,
360
const gchar *object_path, const gchar *interface_name,
361
const gchar *method_name, GVariant *parameters,
362
GDBusMethodInvocation *invocation, gpointer user_data)
364
CdMainPrivate *priv = (CdMainPrivate *) user_data;
367
if (g_strcmp0 (method_name, "Refresh") == 0) {
368
g_debug ("CdMain: %s:Refresh()", sender);
369
cd_sane_client_refresh_schedule (priv);
370
g_dbus_method_invocation_return_value (invocation, NULL);
378
* cd_main_daemon_get_property:
381
cd_main_daemon_get_property (GDBusConnection *connection_, const gchar *sender,
382
const gchar *object_path, const gchar *interface_name,
383
const gchar *property_name, GError **error,
386
GVariant *retval = NULL;
388
if (g_strcmp0 (property_name, "DaemonVersion") == 0) {
389
retval = g_variant_new_string (VERSION);
391
g_critical ("failed to get property %s",
399
* cd_main_on_bus_acquired_cb:
402
cd_main_on_bus_acquired_cb (GDBusConnection *connection_,
406
CdMainPrivate *priv = (CdMainPrivate *) user_data;
407
guint registration_id;
408
static const GDBusInterfaceVTable interface_vtable = {
409
cd_main_daemon_method_call,
410
cd_main_daemon_get_property,
414
registration_id = g_dbus_connection_register_object (connection_,
415
COLORD_SANE_DBUS_PATH,
416
priv->introspection->interfaces[0],
418
priv, /* user_data */
419
NULL, /* user_data_free_func */
420
NULL); /* GError** */
421
g_assert (registration_id > 0);
425
* cd_main_colord_connect_cb:
428
cd_main_colord_connect_cb (GObject *source_object,
432
CdMainPrivate *priv = (CdMainPrivate *) user_data;
434
GError *error = NULL;
437
ret = cd_client_connect_finish (priv->client, res, &error);
439
g_warning ("failed to connect to colord: %s",
441
g_error_free (error);
446
cd_sane_client_refresh (priv);
452
* cd_main_udev_uevent_cb:
455
cd_main_udev_uevent_cb (GUdevClient *gudev_client,
457
GUdevDevice *udev_device,
461
if (g_strcmp0 (action, "add") == 0 ||
462
g_strcmp0 (action, "remove") == 0) {
463
cd_sane_client_refresh_schedule (priv);
468
* cd_main_on_name_acquired_cb:
471
cd_main_on_name_acquired_cb (GDBusConnection *connection_,
475
CdMainPrivate *priv = (CdMainPrivate *) user_data;
476
const gchar *subsystems[] = {"usb", NULL};
478
g_debug ("CdMain: acquired name: %s", name);
481
priv->array = g_ptr_array_new_with_free_func ((GDestroyNotify) cd_main_dev_free);
482
priv->client = cd_client_new ();
483
priv->gudev_client = g_udev_client_new (subsystems);
484
g_signal_connect (priv->gudev_client, "uevent",
485
G_CALLBACK (cd_main_udev_uevent_cb), priv);
487
/* connect to daemon */
488
cd_client_connect (priv->client,
490
cd_main_colord_connect_cb,
495
* cd_main_on_name_lost_cb:
498
cd_main_on_name_lost_cb (GDBusConnection *connection_,
502
CdMainPrivate *priv = (CdMainPrivate *) user_data;
503
g_debug ("CdMain: lost name: %s", name);
504
g_main_loop_quit (priv->loop);
508
* cd_main_timed_exit_cb:
511
cd_main_timed_exit_cb (gpointer user_data)
513
CdMainPrivate *priv = (CdMainPrivate *) user_data;
514
g_main_loop_quit (priv->loop);
519
* cd_main_load_introspection:
521
static GDBusNodeInfo *
522
cd_main_load_introspection (const gchar *filename, GError **error)
526
GDBusNodeInfo *info = NULL;
530
file = g_file_new_for_path (filename);
531
ret = g_file_load_contents (file, NULL, &data,
536
/* build introspection from XML */
537
info = g_dbus_node_info_new_for_xml (data, error);
541
g_object_unref (file);
550
main (int argc, char *argv[])
552
CdMainPrivate *priv = NULL;
553
gboolean immediate_exit = FALSE;
554
gboolean timed_exit = FALSE;
555
GError *error = NULL;
556
GOptionContext *context;
558
const GOptionEntry options[] = {
559
{ "timed-exit", '\0', 0, G_OPTION_ARG_NONE, &timed_exit,
560
/* TRANSLATORS: exit after we've started up, used for user profiling */
561
_("Exit after a small delay"), NULL },
562
{ "immediate-exit", '\0', 0, G_OPTION_ARG_NONE, &immediate_exit,
563
/* TRANSLATORS: exit straight away, used for automatic profiling */
564
_("Exit after the engine has loaded"), NULL },
568
setlocale (LC_ALL, "");
570
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
571
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
572
textdomain (GETTEXT_PACKAGE);
576
/* TRANSLATORS: program name */
577
g_set_application_name (_("Color Management (SANE helper)"));
578
context = g_option_context_new (NULL);
579
g_option_context_add_main_entries (context, options, NULL);
580
g_option_context_set_summary (context, _("Color Management D-Bus Service (SANE)"));
581
g_option_context_parse (context, &argc, &argv, NULL);
582
g_option_context_free (context);
584
/* create new objects */
585
priv = g_new0 (CdMainPrivate, 1);
586
priv->loop = g_main_loop_new (NULL, FALSE);
588
/* load introspection from file */
589
priv->introspection = cd_main_load_introspection (DATADIR "/dbus-1/interfaces/"
590
COLORD_SANE_DBUS_INTERFACE ".xml",
592
if (priv->introspection == NULL) {
593
g_warning ("CdMain: failed to load introspection: %s",
595
g_error_free (error);
600
priv->owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM,
601
COLORD_SANE_DBUS_SERVICE,
602
G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
603
G_BUS_NAME_OWNER_FLAGS_REPLACE,
604
cd_main_on_bus_acquired_cb,
605
cd_main_on_name_acquired_cb,
606
cd_main_on_name_lost_cb,
609
/* Only timeout and close the mainloop if we have specified it
610
* on the command line */
612
g_idle_add (cd_main_timed_exit_cb, priv);
614
g_timeout_add_seconds (5, cd_main_timed_exit_cb, priv);
617
g_main_loop_run (priv->loop);
625
if (priv->array != NULL)
626
g_ptr_array_unref (priv->array);
627
if (priv->owner_id > 0)
628
g_bus_unown_name (priv->owner_id);
629
if (priv->client != NULL)
630
g_object_unref (priv->client);
631
if (priv->gudev_client != NULL)
632
g_object_unref (priv->gudev_client);
633
if (priv->introspection != NULL)
634
g_dbus_node_info_unref (priv->introspection);
635
g_main_loop_unref (priv->loop);