diff --git a/README.md b/README.md index da44dcd..63416ea 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Requirements: ```text glib-2.0 >= 2.66.0 gio-2.0 >= 2.16.1 - gudev-1.0 >= 235 (Linux) + gudev-1.0 >= 238 (Linux) libimobiledevice-1.0 >= 0.9.7 (optional) polkit-gobject-1 >= 124 ``` diff --git a/dbus/org.freedesktop.UPower.Device.xml b/dbus/org.freedesktop.UPower.Device.xml index 9b57ac4..8615a11 100644 --- a/dbus/org.freedesktop.UPower.Device.xml +++ b/dbus/org.freedesktop.UPower.Device.xml @@ -943,6 +943,19 @@ method return sender=:1.386 -> dest=:1.477 reply_serial=2 + + + + + + Seat name. + + Non-empty value indicates that device has been assigned to a seat. + + + + + diff --git a/libupower-glib/up-client.c b/libupower-glib/up-client.c index 528e8e7..faf86f7 100644 --- a/libupower-glib/up-client.c +++ b/libupower-glib/up-client.c @@ -51,6 +51,7 @@ static void up_client_finalize (GObject *object); struct _UpClientPrivate { UpExportedDaemon *proxy; + char *seat; }; enum { @@ -122,7 +123,7 @@ up_client_get_devices_full (UpClient *client, const char *object_path = devices[i]; gboolean ret; - device = up_device_new (); + device = g_object_new (UP_TYPE_DEVICE, "client", client, NULL); ret = up_device_set_object_path_sync (device, object_path, cancellable, NULL); if (!ret) continue; @@ -236,7 +237,7 @@ up_client_get_display_device (UpClient *client) gboolean ret; UpDevice *device; - device = up_device_new (); + device = g_object_new (UP_TYPE_DEVICE, "client", client, NULL); ret = up_device_set_object_path_sync (device, "/org/freedesktop/UPower/devices/DisplayDevice", NULL, NULL); if (!ret) { g_object_unref (G_OBJECT (device)); @@ -348,7 +349,7 @@ up_client_add (UpClient *client, const gchar *object_path) gboolean ret; /* create new device */ - device = up_device_new (); + device = g_object_new (UP_TYPE_DEVICE, "client", client, NULL); ret = up_device_set_object_path_sync (device, object_path, NULL, NULL); if (!ret) goto out; @@ -528,6 +529,50 @@ up_client_class_init (UpClientClass *klass) G_TYPE_NONE, 1, G_TYPE_STRING); } +static char * +get_seat (GCancellable *cancellable) +{ + g_autoptr(GError) error = NULL; + g_autoptr(GDBusConnection) connection = NULL; + g_autoptr(GVariant) variant = NULL; + g_autoptr(GVariant) inner = NULL; + + connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, + cancellable, + &error); + + if (connection == NULL) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("system bus not available: %s", error->message); + + return NULL; + } + + variant = g_dbus_connection_call_sync (connection, + "org.freedesktop.login1", + "/org/freedesktop/login1/seat/auto", + "org.freedesktop.DBus.Properties", + "Get", + g_variant_new ("(ss)", + "org.freedesktop.login1.Seat", + "Id"), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + &error); + + if (variant == NULL) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_debug ("Failed to get seat name: %s", error->message); + + return NULL; + } + + g_variant_get (variant, "(v)", &inner); + return g_variant_dup_string (inner, NULL); +} + /* * up_client_init: * @client: This class instance @@ -548,6 +593,8 @@ up_client_initable_init (GInitable *initable, GCancellable *cancellable, GError if (client->priv->proxy == NULL) return FALSE; + client->priv->seat = get_seat (cancellable); + /* all callbacks */ g_signal_connect (client->priv->proxy, "device-added", G_CALLBACK (up_device_added_cb), client); @@ -587,6 +634,7 @@ up_client_finalize (GObject *object) client = UP_CLIENT (object); g_clear_object (&client->priv->proxy); + g_clear_pointer (&client->priv->seat, g_free); G_OBJECT_CLASS (up_client_parent_class)->finalize (object); } @@ -716,3 +764,11 @@ up_client_new_finish (GAsyncResult *res, return g_task_propagate_pointer (G_TASK (res), error); } + +const char * +up_client_get_seat (UpClient *client) +{ + g_return_val_if_fail (UP_IS_CLIENT (client), NULL); + + return client->priv->seat; +} diff --git a/libupower-glib/up-client.h b/libupower-glib/up-client.h index 9435dbd..a7c9306 100644 --- a/libupower-glib/up-client.h +++ b/libupower-glib/up-client.h @@ -100,6 +100,8 @@ G_DEPRECATED gboolean up_client_get_lid_is_present (UpClient *client); gboolean up_client_get_on_battery (UpClient *client); +const char *up_client_get_seat (UpClient *client); + G_END_DECLS #endif /* __UP_CLIENT_H */ diff --git a/libupower-glib/up-device.c b/libupower-glib/up-device.c index bbf1122..58d2424 100644 --- a/libupower-glib/up-device.c +++ b/libupower-glib/up-device.c @@ -35,6 +35,7 @@ #include #include +#include "up-client.h" #include "up-device.h" #include "up-device-generated.h" #include "up-stats-item.h" @@ -51,6 +52,8 @@ static void up_device_finalize (GObject *object); **/ struct _UpDevicePrivate { + UpClient *client; + UpExportedDevice *proxy_device; /* For use when a UpDevice isn't backed by a D-Bus object @@ -60,6 +63,7 @@ struct _UpDevicePrivate enum { PROP_0, + PROP_CLIENT, PROP_UPDATE_TIME, PROP_VENDOR, PROP_MODEL, @@ -97,6 +101,7 @@ enum { PROP_VOLTAGE_MIN_DESIGN, PROP_VOLTAGE_MAX_DESIGN, PROP_CAPACITY_LEVEL, + PROP_SEAT, PROP_LAST }; @@ -613,6 +618,14 @@ up_device_set_property (GObject *object, guint prop_id, const GValue *value, GPa { UpDevice *device = UP_DEVICE (object); + switch (prop_id) { + case PROP_CLIENT: + device->priv->client = g_value_dup_object (value); + break; + default: + break; + } + if (device->priv->proxy_device == NULL) { GValue *v; @@ -735,6 +748,8 @@ up_device_set_property (GObject *object, guint prop_id, const GValue *value, GPa break; case PROP_CAPACITY_LEVEL: up_exported_device_set_capacity_level (device->priv->proxy_device, g_value_get_string (value)); + case PROP_SEAT: + up_exported_device_set_seat (device->priv->proxy_device, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -873,6 +888,8 @@ up_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpe break; case PROP_CAPACITY_LEVEL: g_value_set_string (value, up_exported_device_get_capacity_level (device->priv->proxy_device)); + case PROP_SEAT: + g_value_set_string (value, up_exported_device_get_seat (device->priv->proxy_device)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -891,6 +908,16 @@ up_device_class_init (UpDeviceClass *klass) object_class->set_property = up_device_set_property; object_class->get_property = up_device_get_property; + g_object_class_install_property (object_class, + PROP_CLIENT, + g_param_spec_object ("client", + "UpClient", + "UpClient reference", + UP_TYPE_CLIENT, + G_PARAM_STATIC_STRINGS | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY)); + /** * UpDevice:update-time: * @@ -1387,6 +1414,20 @@ up_device_class_init (UpDeviceClass *klass) g_param_spec_string ("capacity-level", NULL, NULL, NULL, G_PARAM_READWRITE)); + + /** + * UpDevice:seat: + * + * The seat of the device. + * + * Since: 1.90.10 + **/ + g_object_class_install_property (object_class, + PROP_SEAT, + g_param_spec_string ("seat", + NULL, NULL, + NULL, + G_PARAM_READWRITE)); } static void @@ -1427,6 +1468,7 @@ up_device_finalize (GObject *object) device); } + g_clear_object (&device->priv->client); g_clear_object (&device->priv->proxy_device); g_clear_pointer (&device->priv->offline_props, g_hash_table_unref); @@ -1447,3 +1489,37 @@ up_device_new (void) { return UP_DEVICE (g_object_new (UP_TYPE_DEVICE, NULL)); } + +/** + * up_device_is_available_to_user: + * @device: a #UpDevice instance. + * + * If supported by OS backend returns device availability to user otherwise + * it is assumed that device is available. + * + * Returns: #TRUE if device is available or #FALSE otherwise. + * + * Since: 1.90.10 + **/ +gboolean +up_device_is_available_to_user (UpDevice *device) +{ + const char *client_seat; + const char *device_seat; + + g_return_val_if_fail (UP_IS_DEVICE (device), TRUE); + + if (device->priv->client == NULL) + return TRUE; + + client_seat = up_client_get_seat (device->priv->client); + if (client_seat == NULL) + return TRUE; + + /* NULL/empty seat means that device is available to all seats */ + device_seat = up_exported_device_get_seat (device->priv->proxy_device); + if (device_seat == NULL || *device_seat == '\0') + return TRUE; + + return g_str_equal (client_seat, device_seat); +} diff --git a/libupower-glib/up-device.h b/libupower-glib/up-device.h index 9c7a965..d20a5ce 100644 --- a/libupower-glib/up-device.h +++ b/libupower-glib/up-device.h @@ -68,6 +68,8 @@ GType up_device_get_type (void); UpDevice *up_device_new (void); gchar *up_device_to_text (UpDevice *device); +gboolean up_device_is_available_to_user (UpDevice *device); + /* sync versions */ G_DEPRECATED gboolean up_device_refresh_sync (UpDevice *device, diff --git a/src/linux/up-enumerator-udev.c b/src/linux/up-enumerator-udev.c index b46149b..9b9cd2f 100644 --- a/src/linux/up-enumerator-udev.c +++ b/src/linux/up-enumerator-udev.c @@ -97,6 +97,34 @@ is_macbook (gpointer data) return GINT_TO_POINTER(g_str_has_prefix (product, "MacBook")); } +static char * +get_seat_from_device (GUdevDevice *native) +{ + const char *const *tags; + g_autoptr(GUdevDevice) parent = NULL; + + tags = g_udev_device_get_current_tags (native); + + if (g_strv_contains (tags, "seat")) { + int i; + + for (i = 0; tags[i] != NULL; i++) { + if (g_str_has_prefix (tags[i], "seat") && + strlen (tags[i]) > strlen ("seat")) + return g_strdup (tags[i]); + } + + return g_strdup ("seat0"); + } + + parent = g_udev_device_get_parent (native); + + if (parent != NULL) + return get_seat_from_device (parent); + + return NULL; +} + static UpDevice * device_new (UpEnumeratorUdev *self, GUdevDevice *native) { @@ -108,12 +136,15 @@ device_new (UpEnumeratorUdev *self, GUdevDevice *native) subsys = g_udev_device_get_subsystem (native); if (g_strcmp0 (subsys, "power_supply") == 0) { + g_autofree char *seat = NULL; UpDevice *device; + seat = get_seat_from_device (native); device = g_initable_new (UP_TYPE_DEVICE_SUPPLY_BATTERY, NULL, NULL, "daemon", daemon, "native", native, "ignore-system-percentage", GPOINTER_TO_INT (is_macbook (NULL)), + "seat", seat, NULL); if (device) return device; @@ -121,6 +152,7 @@ device_new (UpEnumeratorUdev *self, GUdevDevice *native) return g_initable_new (UP_TYPE_DEVICE_SUPPLY, NULL, NULL, "daemon", daemon, "native", native, + "seat", seat, NULL); } else if (g_strcmp0 (subsys, "tty") == 0) { @@ -302,6 +334,8 @@ uevent_signal_handler_cb (UpEnumeratorUdev *self, g_signal_emit_by_name (self, "device-added", up_dev); } else { + const gchar *subsys; + if (!UP_IS_DEVICE (obj)) { g_autoptr(GUdevDevice) d = get_latest_udev_device (self, obj); if (d) @@ -309,6 +343,14 @@ uevent_signal_handler_cb (UpEnumeratorUdev *self, return; } + subsys = g_udev_device_get_subsystem (device); + if (g_strcmp0 (subsys, "power_supply") == 0) { + g_autofree char *seat = NULL; + + seat = get_seat_from_device (device); + g_object_set (obj, "seat", seat, NULL); + } + g_debug ("refreshing device for path %s", g_udev_device_get_sysfs_path (device)); if (!up_device_refresh_internal (UP_DEVICE (obj), UP_REFRESH_EVENT)) g_debug ("no changes on %s", up_device_get_object_path (UP_DEVICE (obj)));