diff options
author | Jelle van der Waa <jvanderwaa@redhat.com> | 2024-01-17 16:17:08 +0100 |
---|---|---|
committer | Kate Hsuan <hpa@redhat.com> | 2024-01-26 14:57:27 +0000 |
commit | 17c14cc63f9078089a4115d6120f69d24e169636 (patch) | |
tree | b0e956b48f3d339fda9e7ec6a1b0974f5f6959df | |
parent | 18d550a5552376869b9c9490a7df3c19dd12c519 (diff) |
Revert "all: Remove Lid handling"
This reverts commit 07565ef6a1aa4a115f8ce51e259e408edbaed4cc.
In the current systemd stable release 255 org.freedesktop.login1 does
not emit a LidisClosed event, this has added in systemd `main` and will
be availble in the next release.
As GNOME control panel still uses UPower's `LidIsclosed` property and
many other DE's such as Xfce/LXQt/Deepin as well revert this until the
systemd changes are available in all Distributions.
https://github.com/systemd/systemd/pull/30706
Resolve: https://gitlab.freedesktop.org/upower/upower/-/issues/260
-rw-r--r-- | dbus/org.freedesktop.UPower.xml | 20 | ||||
-rw-r--r-- | libupower-glib/up-client.c | 70 | ||||
-rw-r--r-- | libupower-glib/up-client.h | 4 | ||||
-rw-r--r-- | src/freebsd/up-backend.c | 33 | ||||
-rwxr-xr-x | src/linux/integration-test.py | 2 | ||||
-rw-r--r-- | src/linux/meson.build | 2 | ||||
-rw-r--r-- | src/linux/up-backend.c | 55 | ||||
-rw-r--r-- | src/linux/up-input.c | 422 | ||||
-rw-r--r-- | src/linux/up-input.h | 42 | ||||
-rw-r--r-- | src/openbsd/up-backend.c | 68 | ||||
-rw-r--r-- | src/up-daemon.c | 36 | ||||
-rw-r--r-- | src/up-daemon.h | 4 | ||||
-rw-r--r-- | tools/up-tool.c | 6 |
13 files changed, 764 insertions, 0 deletions
diff --git a/dbus/org.freedesktop.UPower.xml b/dbus/org.freedesktop.UPower.xml index 246a3ef..f3291d8 100644 --- a/dbus/org.freedesktop.UPower.xml +++ b/dbus/org.freedesktop.UPower.xml @@ -179,6 +179,26 @@ method return sender=:1.386 -> dest=:1.451 reply_serial=2 </doc:para></doc:description></doc:doc> </property> + <property name="LidIsClosed" type="b" access="read"> + <doc:doc> + <doc:description> + <doc:para> + Indicates if the laptop lid is closed where the display cannot be seen. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="LidIsPresent" type="b" access="read"> + <doc:doc> + <doc:description> + <doc:para> + If the system has a lid device. + </doc:para> + </doc:description> + </doc:doc> + </property> + </interface> </node> diff --git a/libupower-glib/up-client.c b/libupower-glib/up-client.c index b005a2f..41e61ed 100644 --- a/libupower-glib/up-client.c +++ b/libupower-glib/up-client.c @@ -63,6 +63,8 @@ enum { PROP_0, PROP_DAEMON_VERSION, PROP_ON_BATTERY, + PROP_LID_IS_CLOSED, + PROP_LID_IS_PRESENT, PROP_LAST }; @@ -286,6 +288,40 @@ up_client_get_daemon_version (UpClient *client) } /** + * up_client_get_lid_is_closed: + * @client: a #UpClient instance. + * + * Get whether the laptop lid is closed. + * + * Return value: %TRUE if lid is closed or %FALSE otherwise. + * + * Since: 0.9.0 + */ +gboolean +up_client_get_lid_is_closed (UpClient *client) +{ + g_return_val_if_fail (UP_IS_CLIENT (client), FALSE); + return up_exported_daemon_get_lid_is_closed (client->priv->proxy); +} + +/** + * up_client_get_lid_is_present: + * @client: a #UpClient instance. + * + * Get whether a laptop lid is present on this machine. + * + * Return value: %TRUE if the machine has a laptop lid + * + * Since: 0.9.2 + */ +gboolean +up_client_get_lid_is_present (UpClient *client) +{ + g_return_val_if_fail (UP_IS_CLIENT (client), FALSE); + return up_exported_daemon_get_lid_is_present (client->priv->proxy); +} + +/** * up_client_get_on_battery: * @client: a #UpClient instance. * @@ -378,6 +414,12 @@ up_client_get_property (GObject *object, case PROP_ON_BATTERY: g_value_set_boolean (value, up_exported_daemon_get_on_battery (client->priv->proxy)); break; + case PROP_LID_IS_CLOSED: + g_value_set_boolean (value, up_exported_daemon_get_lid_is_closed (client->priv->proxy)); + break; + case PROP_LID_IS_PRESENT: + g_value_set_boolean (value, up_exported_daemon_get_lid_is_present (client->priv->proxy)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -424,6 +466,34 @@ up_client_class_init (UpClientClass *klass) NULL, FALSE, G_PARAM_READABLE)); + /** + * UpClient:lid-is-closed: + * + * If the laptop lid is closed. + * + * Since: 0.9.0 + */ + g_object_class_install_property (object_class, + PROP_LID_IS_CLOSED, + g_param_spec_boolean ("lid-is-closed", + "If the laptop lid is closed", + NULL, + FALSE, + G_PARAM_READABLE | G_PARAM_DEPRECATED)); + /** + * UpClient:lid-is-present: + * + * If a laptop lid is present. + * + * Since: 0.9.0 + */ + g_object_class_install_property (object_class, + PROP_LID_IS_PRESENT, + g_param_spec_boolean ("lid-is-present", + "If a laptop lid is present", + NULL, + FALSE, + G_PARAM_READABLE | G_PARAM_DEPRECATED)); /** * UpClient::device-added: diff --git a/libupower-glib/up-client.h b/libupower-glib/up-client.h index dfc4853..da6a5d4 100644 --- a/libupower-glib/up-client.h +++ b/libupower-glib/up-client.h @@ -94,6 +94,10 @@ GPtrArray *up_client_get_devices_finish (UpClient *client, GAsyncResult *res, GError **error); const gchar *up_client_get_daemon_version (UpClient *client); +G_DEPRECATED +gboolean up_client_get_lid_is_closed (UpClient *client); +G_DEPRECATED +gboolean up_client_get_lid_is_present (UpClient *client); gboolean up_client_get_on_battery (UpClient *client); G_END_DECLS diff --git a/src/freebsd/up-backend.c b/src/freebsd/up-backend.c index b132a94..00bd7db 100644 --- a/src/freebsd/up-backend.c +++ b/src/freebsd/up-backend.c @@ -50,6 +50,7 @@ static void up_backend_finalize (GObject *object); static gboolean up_backend_acpi_devd_notify (UpBackend *backend, const gchar *system, const gchar *subsystem, const gchar *type, const gchar *data); static void up_backend_create_new_device (UpBackend *backend, UpAcpiNative *native); +static void up_backend_lid_coldplug (UpBackend *backend); struct UpBackendPrivate { @@ -111,6 +112,21 @@ up_backend_acpi_devd_notify (UpBackend *backend, const gchar *system, const gcha } } } + } else if (!strcmp (subsystem, "Lid")) { + gboolean is_present; + gboolean is_closed; + + g_object_get (backend->priv->daemon, + "lid-is-present", &is_present, NULL); + if (!is_present) { + g_warning ("received lid event without a configured lid; cold-plugging one"); + up_backend_lid_coldplug (backend); + /* FALLTHROUGH */ + } + + is_closed = (data != NULL && !strcmp (data, "notify=0x00")) ? TRUE : FALSE; + up_daemon_set_lid_is_closed (backend->priv->daemon, is_closed); + goto out; } if (native == NULL) @@ -171,6 +187,21 @@ up_backend_create_new_device (UpBackend *backend, UpAcpiNative *native) } /** + * up_backend_lid_coldplug: + **/ +static void +up_backend_lid_coldplug (UpBackend *backend) +{ + gchar *lid_state; + + lid_state = up_get_string_sysctl (NULL, "hw.acpi.lid_switch_state"); + if (lid_state) { + up_daemon_set_lid_is_present (backend->priv->daemon, TRUE); + } + g_free (lid_state); +} + +/** * up_backend_coldplug: * @backend: The %UpBackend class instance * @daemon: The %UpDaemon controlling instance @@ -216,6 +247,8 @@ up_backend_coldplug (UpBackend *backend, UpDaemon *daemon) } } + up_backend_lid_coldplug (backend); + acnative = up_acpi_native_new ("hw.acpi.acline"); up_backend_create_new_device (backend, acnative); g_object_unref (acnative); diff --git a/src/linux/integration-test.py b/src/linux/integration-test.py index 4c071e8..1ff58d2 100755 --- a/src/linux/integration-test.py +++ b/src/linux/integration-test.py @@ -2712,6 +2712,8 @@ class Tests(dbusmock.DBusTestCase): self.start_daemon() client = UPowerGlib.Client.new() self.assertRegex(client.get_daemon_version(), '^[0-9.]+$') + self.assertIn(client.get_lid_is_present(), [False, True]) + self.assertIn(client.get_lid_is_closed(), [False, True]) self.assertEqual(client.get_on_battery(), False) self.assertEqual(client.get_critical_action(), 'HybridSleep') self.stop_daemon() diff --git a/src/linux/meson.build b/src/linux/meson.build index 83691e5..1134e7c 100644 --- a/src/linux/meson.build +++ b/src/linux/meson.build @@ -18,6 +18,8 @@ upshared += { 'linux': static_library('upshared', 'up-device-wup.h', 'up-device-bluez.c', 'up-device-bluez.h', + 'up-input.c', + 'up-input.h', 'up-backend.c', 'up-native.c', 'up-enumerator-udev.c', diff --git a/src/linux/up-backend.c b/src/linux/up-backend.c index dfb0297..2aac74d 100644 --- a/src/linux/up-backend.c +++ b/src/linux/up-backend.c @@ -40,6 +40,7 @@ #include "up-device-wup.h" #include "up-device-hid.h" #include "up-device-bluez.h" +#include "up-input.h" #include "up-config.h" #ifdef HAVE_IDEVICE #include "up-device-idevice.h" @@ -58,6 +59,7 @@ struct UpBackendPrivate UpDaemon *daemon; UpDeviceList *device_list; GUdevClient *gudev_client; + UpInput *lid_device; UpConfig *config; GDBusProxy *logind_proxy; guint logind_sleep_id; @@ -80,6 +82,40 @@ static guint signals [SIGNAL_LAST] = { 0 }; G_DEFINE_TYPE_WITH_PRIVATE (UpBackend, up_backend, G_TYPE_OBJECT) +static void +input_switch_changed_cb (UpInput *input, + gboolean switch_value, + UpBackend *backend) +{ + up_daemon_set_lid_is_closed (backend->priv->daemon, switch_value); +} + +static void +up_backend_uevent_signal_handler_cb (GUdevClient *client, const gchar *action, + GUdevDevice *device, gpointer user_data) +{ + UpBackend *backend = UP_BACKEND (user_data); + g_autoptr(UpInput) input = NULL; + + if (backend->priv->lid_device) + return; + + if (g_strcmp0 (action, "add") != 0) + return; + + /* check if the input device is a lid */ + input = up_input_new (); + if (up_input_coldplug (input, device)) { + up_daemon_set_lid_is_present (backend->priv->daemon, TRUE); + g_signal_connect (G_OBJECT (input), "switch-changed", + G_CALLBACK (input_switch_changed_cb), backend); + up_daemon_set_lid_is_closed (backend->priv->daemon, + up_input_get_switch_value (input)); + + backend->priv->lid_device = g_steal_pointer (&input); + } +} + static UpDevice * find_duplicate_device (UpBackend *backend, UpDevice *device) @@ -427,9 +463,25 @@ udev_device_removed_cb (UpBackend *backend, UpDevice *device) gboolean up_backend_coldplug (UpBackend *backend, UpDaemon *daemon) { + g_autolist(GUdevDevice) devices = NULL; + GList *l; + backend->priv->daemon = g_object_ref (daemon); backend->priv->device_list = up_daemon_get_device_list (daemon); + /* Watch udev for input devices to find the lid switch */ + backend->priv->gudev_client = g_udev_client_new ((const char *[]){ "input", NULL }); + g_signal_connect (backend->priv->gudev_client, "uevent", + G_CALLBACK (up_backend_uevent_signal_handler_cb), backend); + + /* add all subsystems */ + devices = g_udev_client_query_by_subsystem (backend->priv->gudev_client, "input"); + for (l = devices; l != NULL; l = l->next) + up_backend_uevent_signal_handler_cb (backend->priv->gudev_client, + "add", + G_UDEV_DEVICE (l->data), + backend); + backend->priv->bluez_watch_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM, "org.bluez", G_BUS_NAME_WATCHER_FLAGS_NONE, @@ -465,6 +517,7 @@ up_backend_unplug (UpBackend *backend) g_clear_object (&backend->priv->gudev_client); g_clear_object (&backend->priv->udev_enum); g_clear_object (&backend->priv->device_list); + g_clear_object (&backend->priv->lid_device); g_clear_object (&backend->priv->daemon); if (backend->priv->bluez_watch_id > 0) { g_bus_unwatch_name (backend->priv->bluez_watch_id); @@ -775,6 +828,8 @@ up_backend_finalize (GObject *object) g_clear_object (&backend->priv->logind_proxy); + g_clear_object (&backend->priv->lid_device); + G_OBJECT_CLASS (up_backend_parent_class)->finalize (object); } diff --git a/src/linux/up-input.c b/src/linux/up-input.c new file mode 100644 index 0000000..db96a9e --- /dev/null +++ b/src/linux/up-input.c @@ -0,0 +1,422 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2009 Richard Hughes <richard@hughsie.com> + * + * 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 + * + */ + +#include "config.h" + +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <linux/input.h> + +#include <glib.h> +#include <glib/gstdio.h> +#include <glib-object.h> + +#include "up-input.h" + +struct _UpInput +{ + GObject parent_instance; + + guint watched_switch; + int last_switch_state; + struct input_event event; + gsize offset; + GIOChannel *channel; +}; + +G_DEFINE_TYPE (UpInput, up_input, G_TYPE_OBJECT) + +enum { + PROP_0, + PROP_WATCHED_SWITCH +}; + +enum { + SWITCH_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +/* we must use this kernel-compatible implementation */ +#define BITS_PER_LONG (sizeof(long) * 8) +#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) +#define OFF(x) ((x)%BITS_PER_LONG) +#define BIT(x) (1UL<<OFF(x)) +#define LONG(x) ((x)/BITS_PER_LONG) +#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1) + + +/** + * up_input_get_device_sysfs_path: + **/ +static char * +up_input_get_device_sysfs_path (GUdevDevice *device) +{ + const char *root; + + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE); + + root = g_getenv ("UMOCKDEV_DIR"); + if (!root || *root == '\0') + return g_strdup (g_udev_device_get_sysfs_path (device)); + + return g_build_filename (root, + g_udev_device_get_sysfs_path (device), + NULL); +} + +/** + * up_input_str_to_bitmask: + **/ +static gint +up_input_str_to_bitmask (const gchar *s, glong *bitmask, size_t max_size) +{ + gint i, j; + gchar **v; + gint num_bits_set = 0; + + memset (bitmask, 0, max_size); + v = g_strsplit (s, " ", max_size); + for (i = g_strv_length (v) - 1, j = 0; i >= 0; i--, j++) { + gulong val; + + val = strtoul (v[i], NULL, 16); + bitmask[j] = val; + + while (val != 0) { + num_bits_set++; + val &= (val - 1); + } + } + g_strfreev(v); + + return num_bits_set; +} + +/** + * up_input_event_io: + **/ +static gboolean +up_input_event_io (GIOChannel *channel, GIOCondition condition, gpointer data) +{ + UpInput *input = (UpInput*) data; + GError *error = NULL; + gsize read_bytes; + glong bitmask[NBITS(SW_MAX)]; + + /* uninteresting */ + if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) + return FALSE; + + /* read event */ + while (g_io_channel_read_chars (channel, + ((gchar*)&input->event) + input->offset, + sizeof(struct input_event) - input->offset, + &read_bytes, &error) == G_IO_STATUS_NORMAL) { + + /* not enough data */ + if (input->offset + read_bytes < sizeof (struct input_event)) { + input->offset = input->offset + read_bytes; + g_debug ("incomplete read"); + goto out; + } + + /* we have all the data */ + input->offset = 0; + + g_debug ("event.value=%d ; event.code=%d (0x%02x)", + input->event.value, + input->event.code, + input->event.code); + + /* switch? */ + if (input->event.type != EV_SW) { + g_debug ("not a switch event"); + continue; + } + + /* is not the watched switch */ + if (input->event.code != input->watched_switch) { + g_debug ("not the watched switch"); + continue; + } + + /* check switch state */ + if (ioctl (g_io_channel_unix_get_fd(channel), EVIOCGSW(sizeof (bitmask)), bitmask) < 0) { + g_debug ("ioctl EVIOCGSW failed"); + continue; + } + + /* are we set */ + input->last_switch_state = test_bit (input->event.code, bitmask); + g_signal_emit_by_name (G_OBJECT (input), + "switch-changed", + input->last_switch_state); + } +out: + return TRUE; +} + +/** + * up_input_coldplug: + **/ +gboolean +up_input_coldplug (UpInput *input, GUdevDevice *d) +{ + gboolean ret = FALSE; + gchar *path; + gchar *contents = NULL; + gchar *native_path = NULL; + const gchar *device_file; + GError *error = NULL; + glong bitmask[NBITS(SW_MAX)]; + gint num_bits; + GIOStatus status; + int eventfd; + + /* get sysfs path */ + native_path = up_input_get_device_sysfs_path (d); + + /* is a switch */ + path = g_build_filename (native_path, "../capabilities/sw", NULL); + if (!g_file_test (path, G_FILE_TEST_EXISTS)) { + char *path2; + path2 = g_build_filename (native_path, "capabilities/sw", NULL); + if (!g_file_test (path2, G_FILE_TEST_EXISTS)) { + g_debug ("not a switch [%s]", path); + g_debug ("not a switch [%s]", path2); + g_free (path2); + goto out; + } + g_free (path); + path = path2; + } + + /* get caps */ + ret = g_file_get_contents (path, &contents, NULL, &error); + if (!ret) { + g_debug ("failed to get contents for [%s]: %s", path, error->message); + g_error_free (error); + goto out; + } + + /* convert to a bitmask */ + num_bits = up_input_str_to_bitmask (contents, bitmask, sizeof (bitmask)); + if ((num_bits == 0) || (num_bits >= SW_CNT)) { + g_debug ("invalid bitmask entry for %s", native_path); + ret = FALSE; + goto out; + } + + /* is this the watched switch? */ + if (!test_bit (input->watched_switch, bitmask)) { + g_debug ("not the watched switch: %s", native_path); + ret = FALSE; + goto out; + } + + /* get device file */ + device_file = g_udev_device_get_device_file (d); + if (device_file == NULL || device_file[0] == '\0') { + g_debug ("no device file: %s", native_path); + ret = FALSE; + goto out; + } + + /* open device file */ + eventfd = open (device_file, O_RDONLY | O_NONBLOCK); + if (eventfd < 0) { + g_warning ("cannot open '%s': %s", device_file, strerror (errno)); + ret = FALSE; + goto out; + } + + /* get initial state */ + if (ioctl (eventfd, EVIOCGSW(sizeof (bitmask)), bitmask) < 0) { + g_warning ("ioctl EVIOCGSW on %s failed", native_path); + close(eventfd); + ret = FALSE; + goto out; + } + + /* create channel */ + g_debug ("watching %s (%i)", device_file, eventfd); + input->channel = g_io_channel_unix_new (eventfd); + g_io_channel_set_close_on_unref (input->channel, TRUE); + + /* set binary encoding */ + status = g_io_channel_set_encoding (input->channel, NULL, &error); + if (status != G_IO_STATUS_NORMAL) { + g_warning ("failed to set encoding: %s", error->message); + g_error_free (error); + ret = FALSE; + goto out; + } + + /* watch this */ + g_io_add_watch (input->channel, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, up_input_event_io, input); + + /* set if we are closed */ + g_debug ("using %s for watched switch event", native_path); + input->last_switch_state = test_bit (input->watched_switch, bitmask); + +out: + g_free (native_path); + g_free (path); + g_free (contents); + return ret; +} + +/** + * up_input_init: + **/ +static void +up_input_init (UpInput *input) +{ + input->last_switch_state = -1; +} + +/** + * up_input_finalize: + **/ +static void +up_input_finalize (GObject *object) +{ + UpInput *input; + + g_return_if_fail (object != NULL); + g_return_if_fail (UP_IS_INPUT (object)); + + input = UP_INPUT (object); + + if (input->channel) { + g_io_channel_shutdown (input->channel, FALSE, NULL); + g_io_channel_unref (input->channel); + } + G_OBJECT_CLASS (up_input_parent_class)->finalize (object); +} + +static void +up_input_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + UpInput *input = UP_INPUT (object); + + switch (property_id) { + case PROP_WATCHED_SWITCH: + input->watched_switch = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +up_input_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + UpInput *input = UP_INPUT (object); + + switch (property_id) { + case PROP_WATCHED_SWITCH: + g_value_set_uint (value, input->watched_switch); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +/** + * up_input_class_init: + **/ +static void +up_input_class_init (UpInputClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = up_input_finalize; + object_class->set_property = up_input_set_property; + object_class->get_property = up_input_get_property; + + g_object_class_install_property (object_class, PROP_WATCHED_SWITCH, + g_param_spec_uint("watched-switch", + "Watched switch", + "The input switch to watch", + SW_LID, SW_MAX, SW_LID, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + signals[SWITCH_CHANGED] = g_signal_new ("switch-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 1, + G_TYPE_BOOLEAN); +} + +/** + * up_input_new: + * + * Returns a #UpInput that watches the computer lid switch. + **/ +UpInput * +up_input_new (void) +{ + return g_object_new (UP_TYPE_INPUT, NULL); +} + +/** + * up_input_new_for_switch: + * @watched_switch: the identifier for the `SW_` switch to watch + * + * Returns a #UpInput that watches the switched passed as argument. + **/ +UpInput * +up_input_new_for_switch (guint watched_switch) +{ + return g_object_new (UP_TYPE_INPUT, + "watched-switch", watched_switch, + NULL); +} + +/** + * up_input_get_switch_value: + * @input: a #UpInput + * + * Returns the last state of the switch. It is an error + * to call this without having successfully run + * up_input_coldplug(). + **/ +gboolean +up_input_get_switch_value (UpInput *input) +{ + g_return_val_if_fail (UP_IS_INPUT(input), FALSE); + g_return_val_if_fail (input->last_switch_state != -1, FALSE); + + return input->last_switch_state; +} diff --git a/src/linux/up-input.h b/src/linux/up-input.h new file mode 100644 index 0000000..3229be1 --- /dev/null +++ b/src/linux/up-input.h @@ -0,0 +1,42 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2009 Richard Hughes <richard@hughsie.com> + * + * 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 + * + */ + +#ifndef __UP_INPUT_H__ +#define __UP_INPUT_H__ + +#include <glib-object.h> +#include <gudev/gudev.h> + +G_BEGIN_DECLS + +#define UP_TYPE_INPUT (up_input_get_type ()) +G_DECLARE_FINAL_TYPE (UpInput, up_input, UP, INPUT, GObject) + +GType up_input_get_type (void); +UpInput *up_input_new (void); +UpInput *up_input_new_for_switch (guint watched_switch); +gboolean up_input_coldplug (UpInput *input, + GUdevDevice *d); +gboolean up_input_get_switch_value (UpInput *input); + +G_END_DECLS + +#endif /* __UP_INPUT_H__ */ + diff --git a/src/openbsd/up-backend.c b/src/openbsd/up-backend.c index 86fb3c4..a4a607e 100644 --- a/src/openbsd/up-backend.c +++ b/src/openbsd/up-backend.c @@ -36,6 +36,7 @@ static gboolean up_backend_apm_get_power_info(struct apm_power_info*); static gpointer up_backend_apm_event_thread(gpointer object); UpDeviceState up_backend_apm_get_battery_state_value(u_char battery_state); static void up_backend_update_acpibat_state(UpDevice*, struct sensordev); +static void up_backend_update_lid_status(UpDaemon*); static gboolean up_apm_device_get_on_battery (UpDevice *device, gboolean *on_battery); static gboolean up_apm_device_get_online (UpDevice *device, gboolean *online); @@ -184,6 +185,7 @@ up_backend_coldplug (UpBackend *backend, UpDaemon *daemon) "update-time", (guint64) current_time, (void*) NULL); + up_backend_update_lid_status(daemon); if (!g_initable_init (G_INITABLE (backend->priv->ac), NULL, NULL)) g_warning ("failed to coldplug ac"); else @@ -286,6 +288,7 @@ up_backend_update_ac_state(UpDevice* device) gboolean ret, new_is_online, cur_is_online; struct apm_power_info a; + up_backend_update_lid_status(up_device_get_daemon(device)); ret = up_backend_apm_get_power_info(&a); if (!ret) return ret; @@ -492,6 +495,71 @@ up_apm_device_refresh(UpDevice* device, UpRefreshReason reason) return ret; } +/* + * Check the lid status, return TRUE if one was found, FALSE otherwise. + */ +static void +up_backend_update_lid_status(UpDaemon *daemon) { +#ifndef UPOWER_CI_DISABLE_PLATFORM_CODE + /* Use hw.sensors.acpibtn0.indicator0=On (lid open) */ + struct sensordev sensordev; + struct sensor sensor; + size_t sdlen, slen; + int dev, numt, mib[5] = {CTL_HW, HW_SENSORS, 0, 0, 0}; + gboolean lid_found = FALSE; + gboolean lid_open = FALSE; + + sdlen = sizeof(struct sensordev); + slen = sizeof(struct sensor); + + /* go through all acpibtn devices, and check if one of the values match "lid" + if so, use that device. + */ + for (dev = 0; ; dev++) { + mib[2] = dev; + if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) { + if (errno == ENXIO) + continue; + if (errno == ENOENT) + break; + } + + if (strstr(sensordev.xname, "acpibtn") != NULL) { + mib[3] = SENSOR_INDICATOR; + for (numt = 0; numt < sensordev.maxnumt[SENSOR_INDICATOR]; numt++) { + mib[4] = numt; + if (sysctl(mib, 5, &sensor, &slen, NULL, 0) == -1) { + if (errno != ENOENT) { + g_warning("failed to get sensor data from %s", + sensordev.xname); + continue; + } + } + + /* + * Found an acpibtn device, now check if the + * description has got anything with a lid in it. + */ + if (strstr(sensor.desc, "lid open") == NULL) { + g_warning ("nothing here for %s with %s\n", + sensordev.xname, sensor.desc); + continue; + } else { + lid_found = TRUE; + if (sensor.value) + lid_open = TRUE; + else + lid_open = FALSE; + } + } + } + } + + up_daemon_set_lid_is_present (daemon, lid_found); + up_daemon_set_lid_is_closed (daemon, !lid_open); +#endif +} + /* thread doing kqueue() on apm device */ static gpointer up_backend_apm_event_thread(gpointer object) diff --git a/src/up-daemon.c b/src/up-daemon.c index b8d614a..82c8b48 100644 --- a/src/up-daemon.c +++ b/src/up-daemon.c @@ -576,6 +576,42 @@ up_daemon_get_device_list (UpDaemon *daemon) } /** + * up_daemon_set_lid_is_closed: + **/ +void +up_daemon_set_lid_is_closed (UpDaemon *daemon, gboolean lid_is_closed) +{ + UpDaemonPrivate *priv = daemon->priv; + + /* check if we are ignoring the lid */ + if (up_config_get_boolean (priv->config, "IgnoreLid")) { + g_debug ("ignoring lid state"); + return; + } + + g_debug ("lid_is_closed = %s", lid_is_closed ? "yes" : "no"); + up_exported_daemon_set_lid_is_closed (UP_EXPORTED_DAEMON (daemon), lid_is_closed); +} + +/** + * up_daemon_set_lid_is_present: + **/ +void +up_daemon_set_lid_is_present (UpDaemon *daemon, gboolean lid_is_present) +{ + UpDaemonPrivate *priv = daemon->priv; + + /* check if we are ignoring the lid */ + if (up_config_get_boolean (priv->config, "IgnoreLid")) { + g_debug ("ignoring lid state"); + return; + } + + g_debug ("lid_is_present = %s", lid_is_present ? "yes" : "no"); + up_exported_daemon_set_lid_is_present (UP_EXPORTED_DAEMON (daemon), lid_is_present); +} + +/** * up_daemon_set_on_battery: **/ void diff --git a/src/up-daemon.h b/src/up-daemon.h index fcae293..e36663b 100644 --- a/src/up-daemon.h +++ b/src/up-daemon.h @@ -68,6 +68,10 @@ UpDeviceList *up_daemon_get_device_list (UpDaemon *daemon); gboolean up_daemon_startup (UpDaemon *daemon, GDBusConnection *connection); void up_daemon_shutdown (UpDaemon *daemon); +void up_daemon_set_lid_is_closed (UpDaemon *daemon, + gboolean lid_is_closed); +void up_daemon_set_lid_is_present (UpDaemon *daemon, + gboolean lid_is_present); void up_daemon_set_on_battery (UpDaemon *daemon, gboolean on_battery); void up_daemon_set_warning_level (UpDaemon *daemon, diff --git a/tools/up-tool.c b/tools/up-tool.c index e599d86..afd3def 100644 --- a/tools/up-tool.c +++ b/tools/up-tool.c @@ -116,15 +116,21 @@ up_client_print (UpClient *client) { gchar *daemon_version; gboolean on_battery; + gboolean lid_is_closed; + gboolean lid_is_present; char *action; g_object_get (client, "daemon-version", &daemon_version, "on-battery", &on_battery, + "lid-is-closed", &lid_is_closed, + "lid-is-present", &lid_is_present, NULL); g_print (" daemon-version: %s\n", daemon_version); g_print (" on-battery: %s\n", on_battery ? "yes" : "no"); + g_print (" lid-is-closed: %s\n", lid_is_closed ? "yes" : "no"); + g_print (" lid-is-present: %s\n", lid_is_present ? "yes" : "no"); action = up_client_get_critical_action (client); g_print (" critical-action: %s\n", action); g_free (action); |