up-device.c 22.7 KB
Newer Older
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
David Zeuthen's avatar
David Zeuthen committed
2
 *
David Zeuthen's avatar
David Zeuthen committed
3
 * Copyright (C) 2008 David Zeuthen <davidz@redhat.com>
4
 * Copyright (C) 2008-2009 Richard Hughes <richard@hughsie.com>
David Zeuthen's avatar
David Zeuthen committed
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (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
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

22
#include "config.h"
David Zeuthen's avatar
David Zeuthen committed
23
24
25
26
27
28
29
30

#include <string.h>

#include <glib.h>
#include <glib/gstdio.h>
#include <glib/gi18n-lib.h>
#include <glib-object.h>

31
32
33
#include "up-native.h"
#include "up-device.h"
#include "up-history.h"
34
35
#include "up-history-item.h"
#include "up-stats-item.h"
36

37
typedef struct
38
{
39
	UpDaemon		*daemon;
40
	/* native == NULL implies display device */
41
	GObject			*native;
42
43

	UpHistory		*history;
44
	gboolean		 has_ever_refresh;
45
46
47

	gint64			last_refresh;
	int			poll_timeout;
48
} UpDevicePrivate;
49

50
51
52
53
54
55
static void up_device_initable_iface_init (GInitableIface *iface);

G_DEFINE_TYPE_EXTENDED (UpDevice, up_device, UP_TYPE_EXPORTED_DEVICE_SKELETON, 0,
                        G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
                                               up_device_initable_iface_init)
                        G_ADD_PRIVATE (UpDevice))
56

57
58
59
60
enum {
  PROP_0,
  PROP_DAEMON,
  PROP_NATIVE,
61
62
  PROP_LAST_REFRESH,
  PROP_POLL_TIMEOUT,
63
64
65
66
67
  N_PROPS
};

static GParamSpec *properties[N_PROPS];

68
69
#define UP_DEVICES_DBUS_PATH "/org/freedesktop/UPower/devices"

70
71
72
73
74
/* This needs to be called when one of those properties changes:
 * state
 * power_supply
 * percentage
 * time_to_empty
75
 * battery_level
76
77
 *
 * type should not change for non-display devices
78
79
80
81
 */
static void
update_warning_level (UpDevice *device)
{
82
	UpDevicePrivate *priv = up_device_get_instance_private (device);
83
	UpDeviceLevel warning_level, battery_level;
84
	UpExportedDevice *skeleton = UP_EXPORTED_DEVICE (device);
85

86
	if (priv->native == NULL)
87
88
		return;

89
90
91
92
93
94
95
96
97
98
99
	/* If the battery level is available, and is critical,
	 * we need to fallback to calculations to get the warning
	 * level, as that might be "action" at this point */
	battery_level = up_exported_device_get_battery_level (skeleton);
	if (battery_level != UP_DEVICE_LEVEL_NONE &&
	    battery_level != UP_DEVICE_LEVEL_CRITICAL) {
		if (battery_level == UP_DEVICE_LEVEL_LOW)
			warning_level = battery_level;
		else
			warning_level = UP_DEVICE_LEVEL_NONE;
	} else {
100
		warning_level = up_daemon_compute_warning_level (priv->daemon,
101
102
103
104
105
106
								 up_exported_device_get_state (skeleton),
								 up_exported_device_get_type_ (skeleton),
								 up_exported_device_get_power_supply (skeleton),
								 up_exported_device_get_percentage (skeleton),
								 up_exported_device_get_time_to_empty (skeleton));
	}
107
108

	up_exported_device_set_warning_level (skeleton, warning_level);
109
110
}

Bastien Nocera's avatar
Bastien Nocera committed
111
112
113
114
115
116
117
118
119
/* This needs to be called when one of those properties changes:
 * type
 * state
 * percentage
 * is-present
 */
static void
update_icon_name (UpDevice *device)
{
120
	UpDevicePrivate *priv = up_device_get_instance_private (device);
Bastien Nocera's avatar
Bastien Nocera committed
121
	const gchar *icon_name = NULL;
122
	UpExportedDevice *skeleton = UP_EXPORTED_DEVICE (device);
Bastien Nocera's avatar
Bastien Nocera committed
123
124

	/* get the icon from some simple rules */
125
	if (up_exported_device_get_type_ (skeleton) == UP_DEVICE_KIND_LINE_POWER) {
Bastien Nocera's avatar
Bastien Nocera committed
126
127
128
		icon_name = "ac-adapter-symbolic";
	} else {

129
		if (!up_exported_device_get_is_present (skeleton)) {
Bastien Nocera's avatar
Bastien Nocera committed
130
131
132
			icon_name = "battery-missing-symbolic";

		} else {
133
			switch (up_exported_device_get_state (skeleton)) {
Bastien Nocera's avatar
Bastien Nocera committed
134
135
136
137
138
139
140
141
			case UP_DEVICE_STATE_EMPTY:
				icon_name = "battery-empty-symbolic";
				break;
			case UP_DEVICE_STATE_FULLY_CHARGED:
				icon_name = "battery-full-charged-symbolic";
				break;
			case UP_DEVICE_STATE_CHARGING:
			case UP_DEVICE_STATE_PENDING_CHARGE:
142
				icon_name = up_daemon_get_charge_icon (priv->daemon,
143
								       up_exported_device_get_percentage (skeleton),
144
								       up_exported_device_get_battery_level (skeleton),
145
								    TRUE);
Bastien Nocera's avatar
Bastien Nocera committed
146
147
148
				break;
			case UP_DEVICE_STATE_DISCHARGING:
			case UP_DEVICE_STATE_PENDING_DISCHARGE:
149
				icon_name = up_daemon_get_charge_icon (priv->daemon,
150
								       up_exported_device_get_percentage (skeleton),
151
								       up_exported_device_get_battery_level (skeleton),
152
								    FALSE);
Bastien Nocera's avatar
Bastien Nocera committed
153
154
155
156
157
158
159
				break;
			default:
				icon_name = "battery-missing-symbolic";
			}
		}
	}

160
	up_exported_device_set_icon_name (skeleton, icon_name);
161
162
}

David Zeuthen's avatar
David Zeuthen committed
163
static void
164
update_history (UpDevice *device)
David Zeuthen's avatar
David Zeuthen committed
165
{
166
	UpDevicePrivate *priv = up_device_get_instance_private (device);
167
168
169
	UpExportedDevice *skeleton = UP_EXPORTED_DEVICE (device);

	/* save new history */
170
171
172
173
174
	up_history_set_state (priv->history, up_exported_device_get_state (skeleton));
	up_history_set_charge_data (priv->history, up_exported_device_get_percentage (skeleton));
	up_history_set_rate_data (priv->history, up_exported_device_get_energy_rate (skeleton));
	up_history_set_time_full_data (priv->history, up_exported_device_get_time_to_full (skeleton));
	up_history_set_time_empty_data (priv->history, up_exported_device_get_time_to_empty (skeleton));
175
}
176

177
static void
178
up_device_notify (GObject *object, GParamSpec *pspec)
179
{
180
	UpDevice *device = UP_DEVICE (object);
181
	UpDevicePrivate *priv = up_device_get_instance_private (device);
182

183
	/* Not finished setting up the object? */
184
	if (priv->daemon == NULL)
185
186
		return;

187
	G_OBJECT_CLASS (up_device_parent_class)->notify (object, pspec);
Cosimo Cecchi's avatar
Cosimo Cecchi committed
188

189
190
	if (g_strcmp0 (pspec->name, "type") == 0 ||
	    g_strcmp0 (pspec->name, "is-present") == 0) {
Bastien Nocera's avatar
Bastien Nocera committed
191
		update_icon_name (device);
192
193
	} else if (g_strcmp0 (pspec->name, "power-supply") == 0 ||
		   g_strcmp0 (pspec->name, "time-to-empty") == 0) {
194
		update_warning_level (device);
195
	} else if (g_strcmp0 (pspec->name, "state") == 0 ||
196
197
		   g_strcmp0 (pspec->name, "percentage") == 0 ||
		   g_strcmp0 (pspec->name, "battery-level") == 0) {
198
		update_warning_level (device);
Bastien Nocera's avatar
Bastien Nocera committed
199
		update_icon_name (device);
200
201
	} else if (g_strcmp0 (pspec->name, "update-time") == 0) {
		update_history (device);
202
	}
David Zeuthen's avatar
David Zeuthen committed
203
204
}

205
/**
206
 * up_device_get_on_battery:
207
208
 *
 * Note: Only implement for system devices, i.e. ones supplying the system
209
210
 **/
gboolean
211
up_device_get_on_battery (UpDevice *device, gboolean *on_battery)
David Zeuthen's avatar
David Zeuthen committed
212
{
213
	UpDeviceClass *klass = UP_DEVICE_GET_CLASS (device);
214

215
	g_return_val_if_fail (UP_IS_DEVICE (device), FALSE);
216

217
	if (klass->get_on_battery == NULL)
218
219
		return FALSE;

220
	return klass->get_on_battery (device, on_battery);
David Zeuthen's avatar
David Zeuthen committed
221
222
}

223
/**
224
 * up_device_get_online:
225
226
227
228
 *
 * Note: Only implement for system devices, i.e. devices supplying the system
 **/
gboolean
229
up_device_get_online (UpDevice *device, gboolean *online)
230
{
231
	UpDeviceClass *klass = UP_DEVICE_GET_CLASS (device);
232

233
	g_return_val_if_fail (UP_IS_DEVICE (device), FALSE);
234
235
236
237
238
239
240

	if (klass->get_online == NULL)
		return FALSE;

	return klass->get_online (device, online);
}

241
static gchar *
242
up_device_get_id (UpDevice *device)
243
{
244
	UpDevicePrivate *priv = up_device_get_instance_private (device);
245
246
	GString *string;
	gchar *id = NULL;
Cosimo Cecchi's avatar
Cosimo Cecchi committed
247
248
	const char *model;
	const char *serial;
249
	UpExportedDevice *skeleton;
Cosimo Cecchi's avatar
Cosimo Cecchi committed
250

251
	if (priv->native == NULL)
252
253
		return NULL;

254
255
256
	skeleton = UP_EXPORTED_DEVICE (device);
	model = up_exported_device_get_model (skeleton);
	serial = up_exported_device_get_serial (skeleton);
257

258
	if (up_exported_device_get_type_ (skeleton) == UP_DEVICE_KIND_LINE_POWER) {
259
260
		goto out;

261
	} else if (up_exported_device_get_type_ (skeleton) == UP_DEVICE_KIND_BATTERY) {
262
		/* we don't have an ID if we are not present */
263
		if (!up_exported_device_get_is_present (skeleton))
264
265
266
267
268
			goto out;

		string = g_string_new ("");

		/* in an ideal world, model-capacity-serial */
Cosimo Cecchi's avatar
Cosimo Cecchi committed
269
270
		if (model != NULL && strlen (model) > 2) {
			g_string_append (string, model);
271
272
			g_string_append_c (string, '-');
		}
273
		if (up_exported_device_get_energy_full_design (skeleton) > 0) {
274
			/* FIXME: this may not be stable if we are using voltage_now */
275
			g_string_append_printf (string, "%i", (guint) up_exported_device_get_energy_full_design (skeleton));
276
277
			g_string_append_c (string, '-');
		}
Cosimo Cecchi's avatar
Cosimo Cecchi committed
278
279
		if (serial != NULL && strlen (serial) > 2) {
			g_string_append (string, serial);
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
			g_string_append_c (string, '-');
		}

		/* make sure we are sane */
		if (string->len == 0) {
			/* just use something generic */
			g_string_append (string, "generic_id");
		} else {
			/* remove trailing '-' */
			g_string_set_size (string, string->len - 1);
		}

		id = g_string_free (string, FALSE);

	} else {
295
296
		/* generic fallback, get what data we can */
		string = g_string_new ("");
297
298
		if (up_exported_device_get_vendor (skeleton) != NULL) {
			g_string_append (string, up_exported_device_get_vendor (skeleton));
299
300
			g_string_append_c (string, '-');
		}
Cosimo Cecchi's avatar
Cosimo Cecchi committed
301
302
		if (model != NULL) {
			g_string_append (string, model);
303
304
			g_string_append_c (string, '-');
		}
Cosimo Cecchi's avatar
Cosimo Cecchi committed
305
306
		if (serial != NULL) {
			g_string_append (string, serial);
307
308
309
310
311
312
313
314
315
316
317
318
319
			g_string_append_c (string, '-');
		}

		/* make sure we are sane */
		if (string->len == 0) {
			/* just use something generic */
			g_string_append (string, "generic_id");
		} else {
			/* remove trailing '-' */
			g_string_set_size (string, string->len - 1);
		}

		id = g_string_free (string, FALSE);
320
321
	}

322
	/* the id may have invalid chars that need to be replaced */
323
324
325
326
327
328
	g_strdelimit (id, "\\\t\"?' /,.", '_');

out:
	return id;
}

329
/**
330
 * up_device_get_daemon:
331
 *
332
 * Returns a refcounted #UpDaemon instance, or %NULL
333
 **/
334
UpDaemon *
335
up_device_get_daemon (UpDevice *device)
336
{
337
338
339
	UpDevicePrivate *priv = up_device_get_instance_private (device);

	if (priv->daemon == NULL)
340
		return NULL;
341
	return g_object_ref (priv->daemon);
342
343
}

344
345
346
347
static void
up_device_export_skeleton (UpDevice *device,
			   const gchar *object_path)
{
348
	UpDevicePrivate *priv = up_device_get_instance_private (device);
349
350
351
	GError *error = NULL;

	g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (device),
352
					  g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (priv->daemon)),
353
354
355
356
357
358
359
360
361
362
363
364
					  object_path,
					  &error);

	if (error != NULL) {
		g_critical ("error registering device on system bus: %s", error->message);
		g_error_free (error);
	}
}

static gchar *
up_device_compute_object_path (UpDevice *device)
{
365
	UpDevicePrivate *priv = up_device_get_instance_private (device);
366
367
368
369
370
371
372
	gchar *basename;
	gchar *id;
	gchar *object_path;
	const gchar *native_path;
	const gchar *type;
	guint i;

373
	if (priv->native == NULL) {
374
375
376
		return g_build_filename (UP_DEVICES_DBUS_PATH, "DisplayDevice", NULL);
	}

377
378
379
380
381
382
383
384
385
386
387
388
389
	type = up_device_kind_to_string (up_exported_device_get_type_ (UP_EXPORTED_DEVICE (device)));
	native_path = up_exported_device_get_native_path (UP_EXPORTED_DEVICE (device));
	basename = g_path_get_basename (native_path);
	id = g_strjoin ("_", type, basename, NULL);

	/* make DBUS valid path */
	for (i=0; id[i] != '\0'; i++) {
		if (id[i] == '-')
			id[i] = '_';
		if (id[i] == '.')
			id[i] = 'x';
		if (id[i] == ':')
			id[i] = 'o';
390
391
		if (id[i] == '@')
			id[i] = '_';
392
393
394
395
396
397
398
399
400
	}
	object_path = g_build_filename (UP_DEVICES_DBUS_PATH, id, NULL);

	g_free (basename);
	g_free (id);

	return object_path;
}

401
static void
402
403
404
405
406
407
408
409
up_device_register_device (UpDevice *device)
{
	char *object_path = up_device_compute_object_path (device);
	g_debug ("object path = %s", object_path);
	up_device_export_skeleton (device, object_path);
	g_free (object_path);
}

410
411
412
413
414
415
416
417
418
419
/**
 * up_device_refresh:
 *
 * Return %TRUE on success, %FALSE if we failed to refresh or no data
 **/
static gboolean
up_device_refresh (UpExportedDevice *skeleton,
		   GDBusMethodInvocation *invocation,
		   UpDevice *device)
{
Benjamin Berg's avatar
Benjamin Berg committed
420
	up_device_refresh_internal (device, UP_REFRESH_POLL);
421
422
423
424
	up_exported_device_complete_refresh (skeleton, invocation);
	return TRUE;
}

425
426
427
428
static gboolean
up_device_initable_init (GInitable     *initable,
                         GCancellable  *cancellable,
                         GError       **error)
429
{
430
	UpDevice *device = UP_DEVICE (initable);
431
	UpDevicePrivate *priv = up_device_get_instance_private (device);
432
	const gchar *native_path = "DisplayDevice";
433
	UpDeviceClass *klass = UP_DEVICE_GET_CLASS (device);
434
	gchar *id = NULL;
435
	int ret;
436

437
	g_return_val_if_fail (UP_IS_DEVICE (device), FALSE);
438

439
	if (up_daemon_get_debug (priv->daemon))
440
441
		g_signal_connect (device, "handle-refresh",
				  G_CALLBACK (up_device_refresh), device);
442
443
	if (priv->native) {
		native_path = up_native_get_native_path (priv->native);
444
445
		up_exported_device_set_native_path (UP_EXPORTED_DEVICE (device), native_path);
	}
446
447

	/* coldplug source */
448
449
450
	if (klass->coldplug != NULL) {
		ret = klass->coldplug (device);
		if (!ret) {
Cosimo Cecchi's avatar
Cosimo Cecchi committed
451
			g_debug ("failed to coldplug %s", native_path);
452
453
454
455
			g_propagate_error (error, g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED,
			                                       "Failed to coldplug %s", native_path));

			return FALSE;
456
		}
457
	}
458

459
	/* force a refresh, although failure isn't fatal */
Benjamin Berg's avatar
Benjamin Berg committed
460
	ret = up_device_refresh_internal (device, UP_REFRESH_INIT);
461
	if (!ret) {
Cosimo Cecchi's avatar
Cosimo Cecchi committed
462
		g_debug ("failed to refresh %s", native_path);
463

464
465
466
		/* XXX: We do not store a history if the initial refresh failed.
		 * This doesn't seem sensible, but it was the case historically. */
		goto register_device;
467
	}
468

469
	/* get the id so we can load the old history */
470
	id = up_device_get_id (device);
471
	if (id != NULL) {
472
		up_history_set_id (priv->history, id);
473
474
		g_free (id);
	}
475
476
477
478
479
480
481
482
483
484
485
486

register_device:
	/* put on the bus */
	up_device_register_device (device);

	return TRUE;
}

static void
up_device_initable_iface_init (GInitableIface *iface)
{
  iface->init = up_device_initable_init;
487
488
}

Cosimo Cecchi's avatar
Cosimo Cecchi committed
489
490
491
492
493
static gboolean
up_device_get_statistics (UpExportedDevice *skeleton,
			  GDBusMethodInvocation *invocation,
			  const gchar *type,
			  UpDevice *device)
494
{
495
	UpDevicePrivate *priv = up_device_get_instance_private (device);
496
	GPtrArray *array = NULL;
497
	UpStatsItem *item;
498
	guint i;
Cosimo Cecchi's avatar
Cosimo Cecchi committed
499
	GVariantBuilder builder;
500

Cosimo Cecchi's avatar
Cosimo Cecchi committed
501
502
503
504
	if (!up_exported_device_get_has_statistics (skeleton)) {
		g_dbus_method_invocation_return_error_literal (invocation,
							       UP_DAEMON_ERROR, UP_DAEMON_ERROR_GENERAL,
							       "device does not support getting stats");
505
506
507
		goto out;
	}

508
	/* get the correct data */
Richard Hughes's avatar
Richard Hughes committed
509
	if (g_strcmp0 (type, "charging") == 0)
510
		array = up_history_get_profile_data (priv->history, TRUE);
Richard Hughes's avatar
Richard Hughes committed
511
	else if (g_strcmp0 (type, "discharging") == 0)
512
		array = up_history_get_profile_data (priv->history, FALSE);
513

514
515
	/* maybe the device doesn't support histories */
	if (array == NULL) {
Cosimo Cecchi's avatar
Cosimo Cecchi committed
516
517
518
		g_dbus_method_invocation_return_error_literal (invocation,
							       UP_DAEMON_ERROR, UP_DAEMON_ERROR_GENERAL,
							       "device has no statistics");
519
520
521
		goto out;
	}

522
523
	/* always 101 items of data */
	if (array->len != 101) {
Cosimo Cecchi's avatar
Cosimo Cecchi committed
524
525
526
		g_dbus_method_invocation_return_error (invocation,
						       UP_DAEMON_ERROR, UP_DAEMON_ERROR_GENERAL,
						       "statistics invalid as have %i items", array->len);
527
528
529
530
		goto out;
	}

	/* copy data to dbus struct */
Cosimo Cecchi's avatar
Cosimo Cecchi committed
531
532
	g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(dd)"));
	for (i = 0; i < array->len; i++) {
533
		item = (UpStatsItem *) g_ptr_array_index (array, i);
Cosimo Cecchi's avatar
Cosimo Cecchi committed
534
535
536
		g_variant_builder_add (&builder, "(dd)",
				       up_stats_item_get_value (item),
				       up_stats_item_get_accuracy (item));
537
538
	}

Cosimo Cecchi's avatar
Cosimo Cecchi committed
539
540
	up_exported_device_complete_get_statistics (skeleton, invocation,
						    g_variant_builder_end (&builder));
541
out:
542
543
	if (array != NULL)
		g_ptr_array_unref (array);
544
545
546
	return TRUE;
}

Cosimo Cecchi's avatar
Cosimo Cecchi committed
547
548
549
550
551
552
553
static gboolean
up_device_get_history (UpExportedDevice *skeleton,
		       GDBusMethodInvocation *invocation,
		       const gchar *type_string,
		       guint timespan,
		       guint resolution,
		       UpDevice *device)
554
{
555
	UpDevicePrivate *priv = up_device_get_instance_private (device);
556
	GPtrArray *array = NULL;
557
	UpHistoryItem *item;
558
	guint i;
559
	UpHistoryType type = UP_HISTORY_TYPE_UNKNOWN;
Cosimo Cecchi's avatar
Cosimo Cecchi committed
560
	GVariantBuilder builder;
561

562
	/* doesn't even try to support this */
Cosimo Cecchi's avatar
Cosimo Cecchi committed
563
564
565
566
	if (!up_exported_device_get_has_history (skeleton)) {
		g_dbus_method_invocation_return_error_literal (invocation,
							       UP_DAEMON_ERROR, UP_DAEMON_ERROR_GENERAL,
							       "device does not support getting history");
567
568
569
		goto out;
	}

570
	/* get the correct data */
Richard Hughes's avatar
Richard Hughes committed
571
	if (g_strcmp0 (type_string, "rate") == 0)
572
		type = UP_HISTORY_TYPE_RATE;
Richard Hughes's avatar
Richard Hughes committed
573
	else if (g_strcmp0 (type_string, "charge") == 0)
574
		type = UP_HISTORY_TYPE_CHARGE;
Richard Hughes's avatar
Richard Hughes committed
575
	else if (g_strcmp0 (type_string, "time-full") == 0)
576
		type = UP_HISTORY_TYPE_TIME_FULL;
Richard Hughes's avatar
Richard Hughes committed
577
	else if (g_strcmp0 (type_string, "time-empty") == 0)
578
		type = UP_HISTORY_TYPE_TIME_EMPTY;
579
580

	/* something recognised */
581
	if (type != UP_HISTORY_TYPE_UNKNOWN)
582
		array = up_history_get_data (priv->history, type, timespan, resolution);
583
584

	/* maybe the device doesn't have any history */
585
	if (array == NULL) {
Cosimo Cecchi's avatar
Cosimo Cecchi committed
586
587
588
		g_dbus_method_invocation_return_error_literal (invocation,
							       UP_DAEMON_ERROR, UP_DAEMON_ERROR_GENERAL,
							       "device has no history");
589
590
		goto out;
	}
David Zeuthen's avatar
David Zeuthen committed
591

592
	/* copy data to dbus struct */
Cosimo Cecchi's avatar
Cosimo Cecchi committed
593
594
	g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(udu)"));
	for (i = 0; i < array->len; i++) {
595
		item = (UpHistoryItem *) g_ptr_array_index (array, i);
Cosimo Cecchi's avatar
Cosimo Cecchi committed
596
597
598
599
		g_variant_builder_add (&builder, "(udu)",
				       up_history_item_get_time (item),
				       up_history_item_get_value (item),
				       up_history_item_get_state (item));
600
	}
601

Cosimo Cecchi's avatar
Cosimo Cecchi committed
602
603
604
	up_exported_device_complete_get_history (skeleton, invocation,
						 g_variant_builder_end (&builder));

605
out:
606
	if (array != NULL)
607
		g_ptr_array_unref (array);
Cosimo Cecchi's avatar
Cosimo Cecchi committed
608
609
610
	return TRUE;
}

611
612
613
614
615
616
617
618
619
void
up_device_sibling_discovered (UpDevice *device, GObject *sibling)
{
	UpDeviceClass *klass = UP_DEVICE_GET_CLASS (device);

	if (klass->sibling_discovered)
		klass->sibling_discovered (device, sibling);
}

620
gboolean
Benjamin Berg's avatar
Benjamin Berg committed
621
up_device_refresh_internal (UpDevice *device, UpRefreshReason reason)
622
{
623
	UpDevicePrivate *priv = up_device_get_instance_private (device);
624
	gboolean ret = FALSE;
625
	UpDeviceClass *klass = UP_DEVICE_GET_CLASS (device);
626

627
	if (priv->native == NULL)
628
629
		return TRUE;

630
631
632
633
	/* not implemented */
	if (klass->refresh == NULL)
		goto out;

634
	/* do the refresh, and change the property */
Benjamin Berg's avatar
Benjamin Berg committed
635
	ret = klass->refresh (device, reason);
636
	priv->last_refresh = g_get_monotonic_time ();
637
638
	g_object_notify_by_pspec (G_OBJECT (device), properties[PROP_LAST_REFRESH]);

639
	if (!ret) {
640
		g_debug ("no changes");
641
		goto out;
642
	}
643
644

	/* the first time, print all properties */
645
	if (!priv->has_ever_refresh) {
646
		g_debug ("added native-path: %s", up_exported_device_get_native_path (UP_EXPORTED_DEVICE (device)));
647
		priv->has_ever_refresh = TRUE;
648
649
650
		goto out;
	}
out:
651
	return ret;
652
653
}

654
const gchar *
655
up_device_get_object_path (UpDevice *device)
David Zeuthen's avatar
David Zeuthen committed
656
{
657
	g_return_val_if_fail (UP_IS_DEVICE (device), NULL);
658
	return g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (device));
659
660
}

661
GObject *
662
up_device_get_native (UpDevice *device)
663
{
664
	UpDevicePrivate *priv = up_device_get_instance_private (device);
665
	g_return_val_if_fail (UP_IS_DEVICE (device), NULL);
666
	return priv->native;
David Zeuthen's avatar
David Zeuthen committed
667
}
668

669
static void
670
up_device_init (UpDevice *device)
671
{
672
	UpDevicePrivate *priv = up_device_get_instance_private (device);
673
674
	UpExportedDevice *skeleton;

675
676
	priv = up_device_get_instance_private (device);
	priv->history = up_history_new ();
677

678
679
680
	skeleton = UP_EXPORTED_DEVICE (device);
	up_exported_device_set_battery_level (skeleton, UP_DEVICE_LEVEL_NONE);

681
	g_signal_connect (device, "handle-get-history",
Cosimo Cecchi's avatar
Cosimo Cecchi committed
682
			  G_CALLBACK (up_device_get_history), device);
683
	g_signal_connect (device, "handle-get-statistics",
Cosimo Cecchi's avatar
Cosimo Cecchi committed
684
			  G_CALLBACK (up_device_get_statistics), device);
685
686
687
}

static void
688
up_device_finalize (GObject *object)
689
{
690
	UpDevicePrivate *priv = up_device_get_instance_private (UP_DEVICE (object));
691

692
693
694
	g_clear_object (&priv->native);
	g_clear_object (&priv->daemon);
	g_object_unref (priv->history);
695

696
	G_OBJECT_CLASS (up_device_parent_class)->finalize (object);
697
698
}

699
700
701
static void
up_device_dispose (GObject *object)
{
702
703
704
	UpDevicePrivate *priv = up_device_get_instance_private (UP_DEVICE (object));

	g_clear_object (&priv->daemon);
705
706
707
708

	G_OBJECT_CLASS (up_device_parent_class)->dispose (object);
}

709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
static void
up_device_set_property (GObject      *object,
                        guint         prop_id,
                        const GValue *value,
                        GParamSpec   *pspec)
{
	UpDevice *device = UP_DEVICE (object);
	UpDevicePrivate *priv = up_device_get_instance_private (device);

	switch (prop_id)
	{
	case PROP_DAEMON:
		priv->daemon = g_value_dup_object (value);
		break;

	case PROP_NATIVE:
		priv->native = g_value_dup_object (value);
		break;

728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
	case PROP_POLL_TIMEOUT:
		priv->poll_timeout = g_value_get_int (value);
		break;

	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
	}
}

static void
up_device_get_property (GObject      *object,
                        guint         prop_id,
                        GValue       *value,
                        GParamSpec   *pspec)
{
	UpDevice *device = UP_DEVICE (object);
	UpDevicePrivate *priv = up_device_get_instance_private (device);

	switch (prop_id)
	{
	case PROP_POLL_TIMEOUT:
		g_value_set_int (value, priv->poll_timeout);
		break;

	case PROP_LAST_REFRESH:
		g_value_set_int64 (value, priv->last_refresh);
		break;

756
757
758
759
760
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
	}
}

761
static void
762
up_device_class_init (UpDeviceClass *klass)
763
764
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
765

766
	object_class->notify = up_device_notify;
767
	object_class->finalize = up_device_finalize;
768
	object_class->dispose = up_device_dispose;
769
770

	object_class->set_property = up_device_set_property;
771
	object_class->get_property = up_device_get_property;
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786

	properties[PROP_DAEMON] =
		g_param_spec_object ("daemon",
		                     "UpDaemon",
		                     "UpDaemon reference",
		                     UP_TYPE_DAEMON,
		                     G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);

	properties[PROP_NATIVE] =
		g_param_spec_object ("native",
		                     "Native",
		                     "Native Object",
		                     G_TYPE_OBJECT,
		                     G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);

787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
	properties[PROP_POLL_TIMEOUT] =
		g_param_spec_int ("poll-timeout",
		                  "Poll timeout",
		                  "Time in seconds between polls",
		                  0,
		                  3600,
		                  0,
		                  G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE | G_PARAM_READABLE);

	properties[PROP_LAST_REFRESH] =
		g_param_spec_int64 ("last-refresh",
		                    "Last Refresh",
		                    "Time of last refresh (in monotonic clock)",
		                    -1,
		                    G_MAXINT64,
		                    0,
		                    G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);

805
	g_object_class_install_properties (object_class, N_PROPS, properties);
806
807
}

808
UpDevice *
809
810
up_device_new (UpDaemon	*daemon,
               GObject	*native)
811
{
812
813
814
815
	return UP_DEVICE (g_object_new (UP_TYPE_DEVICE,
	                                "daemon", daemon,
	                                "native", native,
	                                NULL));
816
}