1 /* -*- Mode: C; indent-tabs-mode: nil; tab-width: 4 -*-
3 * Copyright (C) 2010-2011 Robert Ancell.
4 * Author: Robert Ancell <robert.ancell@canonical.com>
6 * This program is free software: you can redistribute it and/or modify it under
7 * the terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
9 * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
18 #define LOGIN1_SERVICE_NAME "org.freedesktop.login1"
19 #define LOGIN1_OBJECT_NAME "/org/freedesktop/login1"
20 #define LOGIN1_MANAGER_INTERFACE_NAME "org.freedesktop.login1.Manager"
27 static guint service_signals[LAST_SERVICE_SIGNAL] = { 0 };
29 struct Login1ServicePrivate
31 /* Connection to bus service is running on */
32 GDBusConnection *connection;
34 /* TRUE if have connected to service */
37 /* Seats the service is reporting */
40 /* Handle to signal subscription */
45 CAN_GRAPHICAL_CHANGED,
46 ACTIVE_SESSION_CHANGED,
49 static guint seat_signals[LAST_SEAT_SIGNAL] = { 0 };
51 struct Login1SeatPrivate
53 /* Connection to bus seat is running on */
54 GDBusConnection *connection;
59 /* D-Bus path for this seat */
62 /* Handle to signal subscription */
65 /* TRUE if can run a graphical display on this seat */
66 gboolean can_graphical;
68 /* TRUE if can do session switching */
69 gboolean can_multi_session;
72 G_DEFINE_TYPE (Login1Service, login1_service, G_TYPE_OBJECT);
73 G_DEFINE_TYPE (Login1Seat, login1_seat, G_TYPE_OBJECT);
75 static Login1Service *singleton = NULL;
78 login1_service_get_instance (void)
81 singleton = g_object_new (LOGIN1_SERVICE_TYPE, NULL);
86 update_property (Login1Seat *seat, const gchar *name, GVariant *value)
88 if (strcmp (name, "CanGraphical") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
90 seat->priv->can_graphical = g_variant_get_boolean (value);
91 g_signal_emit (seat, seat_signals[CAN_GRAPHICAL_CHANGED], 0);
93 else if (strcmp (name, "ActiveSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE ("(so)")))
95 const gchar *login1_session_id;
96 g_variant_get (value, "(&so)", &login1_session_id, NULL);
97 g_signal_emit (seat, seat_signals[ACTIVE_SESSION_CHANGED], 0, login1_session_id);
102 seat_properties_changed_cb (GDBusConnection *connection,
103 const gchar *sender_name,
104 const gchar *object_path,
105 const gchar *interface_name,
106 const gchar *signal_name,
107 GVariant *parameters,
110 Login1Seat *seat = user_data;
112 GVariantIter *invalidated_properties;
116 g_variant_get (parameters, "(sa{sv}as)", NULL, &iter, &invalidated_properties);
117 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
118 update_property (seat, name, value);
119 g_variant_iter_free (iter);
120 while (g_variant_iter_loop (invalidated_properties, "&s", &name))
123 GError *error = NULL;
125 result = g_dbus_connection_call_sync (connection,
128 "org.freedesktop.DBus.Properties",
130 g_variant_new ("(ss)", "org.freedesktop.login1.Seat", name),
131 G_VARIANT_TYPE ("(v)"),
132 G_DBUS_CALL_FLAGS_NONE,
137 g_warning ("Error updating seat property %s: %s", name, error->message);
138 g_clear_error (&error);
141 g_variant_get (result, "(v)", &value);
142 update_property (seat, name, value);
143 g_variant_unref (result);
146 g_variant_iter_free (invalidated_properties);
150 add_seat (Login1Service *service, const gchar *id, const gchar *path)
154 GError *error = NULL;
156 seat = g_object_new (LOGIN1_SEAT_TYPE, NULL);
157 seat->priv->connection = g_object_ref (service->priv->connection);
158 seat->priv->id = g_strdup (id);
159 seat->priv->path = g_strdup (path);
161 seat->priv->signal_id = g_dbus_connection_signal_subscribe (seat->priv->connection,
163 "org.freedesktop.DBus.Properties",
166 "org.freedesktop.login1.Seat",
167 G_DBUS_SIGNAL_FLAGS_NONE,
168 seat_properties_changed_cb,
172 /* Get properties for this seat */
173 result = g_dbus_connection_call_sync (seat->priv->connection,
176 "org.freedesktop.DBus.Properties",
178 g_variant_new ("(s)", "org.freedesktop.login1.Seat"),
179 G_VARIANT_TYPE ("(a{sv})"),
180 G_DBUS_CALL_FLAGS_NONE,
185 g_warning ("Failed to get seat properties: %s", error->message);
186 g_clear_error (&error);
189 GVariantIter *properties;
193 g_variant_get (result, "(a{sv})", &properties);
194 while (g_variant_iter_loop (properties, "{&sv}", &name, &value))
196 if (strcmp (name, "CanGraphical") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
197 seat->priv->can_graphical = g_variant_get_boolean (value);
198 else if (strcmp (name, "CanMultiSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
199 seat->priv->can_multi_session = g_variant_get_boolean (value);
201 g_variant_iter_free (properties);
202 g_variant_unref (result);
205 service->priv->seats = g_list_append (service->priv->seats, seat);
211 signal_cb (GDBusConnection *connection,
212 const gchar *sender_name,
213 const gchar *object_path,
214 const gchar *interface_name,
215 const gchar *signal_name,
216 GVariant *parameters,
219 Login1Service *service = user_data;
221 if (strcmp (signal_name, "SeatNew") == 0)
223 const gchar *id, *path;
226 g_variant_get (parameters, "(&s&o)", &id, &path);
227 seat = login1_service_get_seat (service, id);
230 seat = add_seat (service, id, path);
231 g_signal_emit (service, service_signals[SEAT_ADDED], 0, seat);
234 else if (strcmp (signal_name, "SeatRemoved") == 0)
236 const gchar *id, *path;
239 g_variant_get (parameters, "(&s&o)", &id, &path);
240 seat = login1_service_get_seat (service, id);
243 service->priv->seats = g_list_remove (service->priv->seats, seat);
244 g_signal_emit (service, service_signals[SEAT_REMOVED], 0, seat);
245 g_object_unref (seat);
251 login1_service_connect (Login1Service *service)
254 GVariantIter *seat_iter;
255 const gchar *id, *path;
256 GError *error = NULL;
258 g_return_val_if_fail (service != NULL, FALSE);
260 if (service->priv->connected)
263 service->priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
265 g_warning ("Failed to get system bus: %s", error->message);
266 g_clear_error (&error);
267 if (!service->priv->connection)
270 service->priv->signal_id = g_dbus_connection_signal_subscribe (service->priv->connection,
272 LOGIN1_MANAGER_INTERFACE_NAME,
276 G_DBUS_SIGNAL_FLAGS_NONE,
278 g_object_ref (service),
281 result = g_dbus_connection_call_sync (service->priv->connection,
284 LOGIN1_MANAGER_INTERFACE_NAME,
286 g_variant_new ("()"),
287 G_VARIANT_TYPE ("(a(so))"),
288 G_DBUS_CALL_FLAGS_NONE,
293 g_warning ("Failed to get list of logind seats: %s", error->message);
294 g_clear_error (&error);
298 g_variant_get (result, "(a(so))", &seat_iter);
299 while (g_variant_iter_loop (seat_iter, "(&s&o)", &id, &path))
300 add_seat (service, id, path);
301 g_variant_iter_free (seat_iter);
302 g_variant_unref (result);
304 service->priv->connected = TRUE;
310 login1_service_get_is_connected (Login1Service *service)
312 g_return_val_if_fail (service != NULL, FALSE);
313 return service->priv->connected;
317 login1_service_get_seats (Login1Service *service)
319 g_return_val_if_fail (service != NULL, NULL);
320 return service->priv->seats;
324 login1_service_get_seat (Login1Service *service, const gchar *id)
328 g_return_val_if_fail (service != NULL, NULL);
330 for (link = service->priv->seats; link; link = link->next)
332 Login1Seat *seat = link->data;
333 if (strcmp (seat->priv->id, id) == 0)
341 login1_service_lock_session (Login1Service *service, const gchar *session_id)
343 GError *error = NULL;
345 g_return_if_fail (service != NULL);
346 g_return_if_fail (session_id != NULL);
348 g_debug ("Locking login1 session %s", session_id);
354 result = g_dbus_connection_call_sync (service->priv->connection,
357 LOGIN1_MANAGER_INTERFACE_NAME,
359 g_variant_new ("(s)", session_id),
360 G_VARIANT_TYPE ("()"),
361 G_DBUS_CALL_FLAGS_NONE,
366 g_warning ("Error locking login1 session: %s", error->message);
367 g_clear_error (&error);
369 g_variant_unref (result);
374 login1_service_unlock_session (Login1Service *service, const gchar *session_id)
376 GError *error = NULL;
378 g_return_if_fail (service != NULL);
379 g_return_if_fail (session_id != NULL);
381 g_debug ("Unlocking login1 session %s", session_id);
387 result = g_dbus_connection_call_sync (service->priv->connection,
390 LOGIN1_MANAGER_INTERFACE_NAME,
392 g_variant_new ("(s)", session_id),
393 G_VARIANT_TYPE ("()"),
394 G_DBUS_CALL_FLAGS_NONE,
399 g_warning ("Error unlocking login1 session: %s", error->message);
400 g_clear_error (&error);
402 g_variant_unref (result);
407 login1_service_activate_session (Login1Service *service, const gchar *session_id)
409 GError *error = NULL;
411 g_return_if_fail (service != NULL);
412 g_return_if_fail (session_id != NULL);
414 g_debug ("Activating login1 session %s", session_id);
420 result = g_dbus_connection_call_sync (service->priv->connection,
423 LOGIN1_MANAGER_INTERFACE_NAME,
425 g_variant_new ("(s)", session_id),
426 G_VARIANT_TYPE ("()"),
427 G_DBUS_CALL_FLAGS_NONE,
432 g_warning ("Error activating login1 session: %s", error->message);
433 g_clear_error (&error);
435 g_variant_unref (result);
440 login1_service_init (Login1Service *service)
442 service->priv = G_TYPE_INSTANCE_GET_PRIVATE (service, LOGIN1_SERVICE_TYPE, Login1ServicePrivate);
446 login1_service_finalize (GObject *object)
448 Login1Service *self = LOGIN1_SERVICE (object);
450 g_list_free_full (self->priv->seats, g_object_unref);
451 g_dbus_connection_signal_unsubscribe (self->priv->connection, self->priv->signal_id);
452 g_object_unref (self->priv->connection);
454 G_OBJECT_CLASS (login1_service_parent_class)->finalize (object);
458 login1_service_class_init (Login1ServiceClass *klass)
460 GObjectClass *object_class = G_OBJECT_CLASS (klass);
462 object_class->finalize = login1_service_finalize;
464 g_type_class_add_private (klass, sizeof (Login1ServicePrivate));
466 service_signals[SEAT_ADDED] =
467 g_signal_new (LOGIN1_SERVICE_SIGNAL_SEAT_ADDED,
468 G_TYPE_FROM_CLASS (klass),
470 G_STRUCT_OFFSET (Login1ServiceClass, seat_added),
473 G_TYPE_NONE, 1, LOGIN1_SEAT_TYPE);
474 service_signals[SEAT_REMOVED] =
475 g_signal_new (LOGIN1_SERVICE_SIGNAL_SEAT_REMOVED,
476 G_TYPE_FROM_CLASS (klass),
478 G_STRUCT_OFFSET (Login1ServiceClass, seat_removed),
481 G_TYPE_NONE, 1, LOGIN1_SEAT_TYPE);
485 login1_seat_get_id (Login1Seat *seat)
487 g_return_val_if_fail (seat != NULL, NULL);
488 return seat->priv->id;
492 login1_seat_get_can_graphical (Login1Seat *seat)
494 g_return_val_if_fail (seat != NULL, FALSE);
495 return seat->priv->can_graphical;
499 login1_seat_get_can_multi_session (Login1Seat *seat)
501 g_return_val_if_fail (seat != NULL, FALSE);
502 return seat->priv->can_multi_session;
506 login1_seat_init (Login1Seat *seat)
508 seat->priv = G_TYPE_INSTANCE_GET_PRIVATE (seat, LOGIN1_SEAT_TYPE, Login1SeatPrivate);
512 login1_seat_finalize (GObject *object)
514 Login1Seat *self = LOGIN1_SEAT (object);
516 g_free (self->priv->id);
517 g_free (self->priv->path);
518 g_dbus_connection_signal_unsubscribe (self->priv->connection, self->priv->signal_id);
519 g_object_unref (self->priv->connection);
521 G_OBJECT_CLASS (login1_seat_parent_class)->finalize (object);
525 login1_seat_class_init (Login1SeatClass *klass)
527 GObjectClass *object_class = G_OBJECT_CLASS (klass);
529 object_class->finalize = login1_seat_finalize;
531 g_type_class_add_private (klass, sizeof (Login1SeatPrivate));
533 seat_signals[CAN_GRAPHICAL_CHANGED] =
534 g_signal_new (LOGIN1_SEAT_SIGNAL_CAN_GRAPHICAL_CHANGED,
535 G_TYPE_FROM_CLASS (klass),
537 G_STRUCT_OFFSET (Login1SeatClass, can_graphical_changed),
542 seat_signals[ACTIVE_SESSION_CHANGED] =
543 g_signal_new (LOGIN1_SIGNAL_ACTIVE_SESION_CHANGED,
544 G_TYPE_FROM_CLASS (klass),
546 G_STRUCT_OFFSET (Login1SeatClass, active_session_changed),
549 G_TYPE_NONE, 1, G_TYPE_STRING);