diff --git a/src/linux/meson.build b/src/linux/meson.build index 1134e7c..c3c28a2 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-kbd-backlight.c', + 'up-kbd-backlight.h', 'up-input.c', 'up-input.h', 'up-backend.c', diff --git a/src/linux/up-backend.c b/src/linux/up-backend.c index 3a8fd11..5294647 100644 --- a/src/linux/up-backend.c +++ b/src/linux/up-backend.c @@ -433,13 +433,17 @@ up_device_disconnected_cb (GObject *gobject, } static void -udev_device_added_cb (UpBackend *backend, UpDevice *device) +udev_device_added_cb (UpBackend *backend, GObject *device) { g_debug ("Got new device from udev enumerator: %p", device); g_signal_connect (device, "notify::disconnected", G_CALLBACK (up_device_disconnected_cb), backend); - if (update_added_duplicate_device (backend, device)) + if (UP_IS_DEVICE (device)) { + if (update_added_duplicate_device (backend, UP_DEVICE (device))) + g_signal_emit (backend, signals[SIGNAL_DEVICE_ADDED], 0, device); + } else { g_signal_emit (backend, signals[SIGNAL_DEVICE_ADDED], 0, device); + } } static void @@ -775,7 +779,7 @@ up_backend_class_init (UpBackendClass *klass) G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (UpBackendClass, device_added), NULL, NULL, NULL, - G_TYPE_NONE, 1, UP_TYPE_DEVICE); + G_TYPE_NONE, 1, G_TYPE_OBJECT); signals [SIGNAL_DEVICE_REMOVED] = g_signal_new ("device-removed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, diff --git a/src/linux/up-enumerator-udev.c b/src/linux/up-enumerator-udev.c index d3d7121..f9b0783 100644 --- a/src/linux/up-enumerator-udev.c +++ b/src/linux/up-enumerator-udev.c @@ -31,6 +31,7 @@ #include "up-device-supply-battery.h" #include "up-device-hid.h" #include "up-device-wup.h" +#include "up-kbd-backlight.h" #ifdef HAVE_IDEVICE #include "up-device-idevice.h" #endif /* HAVE_IDEVICE */ @@ -168,6 +169,8 @@ device_new (UpEnumeratorUdev *self, GUdevDevice *native) native_path = g_udev_device_get_sysfs_path (native); if (g_strrstr (native_path, "kbd_backlight") != NULL) g_warning ("native path %s (%s) hit leds device", native_path, subsys); + + return NULL; } else { native_path = g_udev_device_get_sysfs_path (native); @@ -216,19 +219,120 @@ emit_changes_for_siblings (UpEnumeratorUdev *self, } } +static void +power_supply_add_helper (UpEnumeratorUdev *self, + const gchar *action, + GUdevDevice *device, + GUdevClient *client, + GObject *obj, + char *device_key) +{ + g_autoptr(UpDevice) up_dev = NULL; + g_autofree char *parent_id = NULL; + + up_dev = device_new (self, device); + + /* We work with `obj` further down, which is the UpDevice + * if we have it, or the GUdevDevice if not. */ + if (up_dev) + obj = G_OBJECT (up_dev); + else + obj = G_OBJECT (device); + g_hash_table_insert (self->known, (char*) device_key, g_object_ref (obj)); + + /* Fire relevant sibling events and insert into lookup table */ + parent_id = device_parent_id (device); + g_debug ("device %s has parent id: %s", device_key, parent_id); + if (parent_id) { + GPtrArray *devices = NULL; + char *parent_id_key = NULL; + int i; + + g_hash_table_lookup_extended (self->siblings, parent_id, + (gpointer*)&parent_id_key, (gpointer*)&devices); + if (!devices) + devices = g_ptr_array_new_with_free_func (g_object_unref); + + for (i = 0; i < devices->len; i++) { + GObject *sibling = g_ptr_array_index (devices, i); + + if (up_dev) { + g_autoptr(GUdevDevice) d = get_latest_udev_device (self, sibling); + if (d) + up_device_sibling_discovered (up_dev, G_OBJECT (d)); + } + if (UP_IS_DEVICE (sibling)) + up_device_sibling_discovered (UP_DEVICE (sibling), obj); + } + + g_ptr_array_add (devices, g_object_ref (obj)); + if (!parent_id_key) { + parent_id_key = g_strdup (parent_id); + g_hash_table_insert (self->siblings, parent_id_key, devices); + } + + /* Just a reference to the hash table key */ + g_object_set_data (obj, "udev-parent-id", parent_id_key); + } + + if (up_dev) + g_signal_emit_by_name (self, "device-added", up_dev); +} + +static void +kbd_backlight_add_helper (UpEnumeratorUdev *self, + const gchar *action, + GUdevDevice *device, + GUdevClient *client, + GObject *obj, + char *device_key) +{ + UpDaemon *daemon; + g_autoptr(UpDeviceKbdBacklight) up_dev = NULL; + g_autofree char *parent_id = NULL; + + daemon = up_enumerator_get_daemon (UP_ENUMERATOR (self)); + + g_debug ("KBD new"); + up_dev = g_initable_new (UP_TYPE_KBD_BACKLIGHT, NULL, NULL, + "daemon", daemon, + "native", device, + NULL); + + + /* We work with `obj` further down, which is the UpDevice + * if we have it, or the GUdevDevice if not. */ + if (up_dev) + obj = G_OBJECT (up_dev); + else + obj = G_OBJECT (device); + g_hash_table_insert (self->known, (char*) device_key, g_object_ref (obj)); + g_debug ("KBD add emit"); + if (up_dev) + g_signal_emit_by_name (self, "device-added", G_OBJECT (up_dev)); +} + static void uevent_signal_handler_cb (UpEnumeratorUdev *self, - const gchar *action, - GUdevDevice *device, - GUdevClient *client) + const gchar *action, + GUdevDevice *device, + GUdevClient *client) { const char *device_key = g_udev_device_get_sysfs_path (device); + gboolean is_kbd_backlight = FALSE; g_debug ("Received uevent %s on device %s", action, device_key); /* Work around the fact that we don't get a REMOVE event in some cases. */ if (g_strcmp0 (g_udev_device_get_subsystem (device), "power_supply") == 0) device_key = g_udev_device_get_name (device); + else if (g_strcmp0 (g_udev_device_get_subsystem (device), "leds") == 0) { + if (g_strrstr (device_key, "kbd_backlight") == NULL) + return; + is_kbd_backlight = TRUE; + } + + g_debug ("uevent subsystem %s", g_udev_device_get_subsystem (device)); /* It appears that we may not always receive an "add" event. As such, * treat "add"/"change" in the same way, by first checking if we have @@ -250,57 +354,13 @@ uevent_signal_handler_cb (UpEnumeratorUdev *self, } if (!obj) { - g_autoptr(UpDevice) up_dev = NULL; - g_autofree char *parent_id = NULL; - - up_dev = device_new (self, device); - - /* We work with `obj` further down, which is the UpDevice - * if we have it, or the GUdevDevice if not. */ - if (up_dev) - obj = G_OBJECT (up_dev); - else - obj = G_OBJECT (device); - g_hash_table_insert (self->known, (char*) device_key, g_object_ref (obj)); - - /* Fire relevant sibling events and insert into lookup table */ - parent_id = device_parent_id (device); - g_debug ("device %s has parent id: %s", device_key, parent_id); - if (parent_id) { - GPtrArray *devices = NULL; - char *parent_id_key = NULL; - int i; - - g_hash_table_lookup_extended (self->siblings, parent_id, - (gpointer*)&parent_id_key, (gpointer*)&devices); - if (!devices) - devices = g_ptr_array_new_with_free_func (g_object_unref); - - for (i = 0; i < devices->len; i++) { - GObject *sibling = g_ptr_array_index (devices, i); - - if (up_dev) { - g_autoptr(GUdevDevice) d = get_latest_udev_device (self, sibling); - if (d) - up_device_sibling_discovered (up_dev, G_OBJECT (d)); - } - if (UP_IS_DEVICE (sibling)) - up_device_sibling_discovered (UP_DEVICE (sibling), obj); - } - - g_ptr_array_add (devices, g_object_ref (obj)); - if (!parent_id_key) { - parent_id_key = g_strdup (parent_id); - g_hash_table_insert (self->siblings, parent_id_key, devices); - } - - /* Just a reference to the hash table key */ - g_object_set_data (obj, "udev-parent-id", parent_id_key); + if (is_kbd_backlight) { + g_debug ("uevent KDB backlight add."); + kbd_backlight_add_helper (self, action, device, client, obj, device_key); + } else { + power_supply_add_helper (self, action, device, client, obj, device_key); } - if (up_dev) - g_signal_emit_by_name (self, "device-added", up_dev); - } else { if (!UP_IS_DEVICE (obj)) { g_autoptr(GUdevDevice) d = get_latest_udev_device (self, obj); diff --git a/src/linux/up-kbd-backlight.c b/src/linux/up-kbd-backlight.c new file mode 100644 index 0000000..1faa078 --- /dev/null +++ b/src/linux/up-kbd-backlight.c @@ -0,0 +1,277 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2025 Kate Hsuan + * + * Licensed under the GNU General Public License Version 2 + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "up-kbd-backlight.h" +#include "up-daemon.h" +#include "up-native.h" +#include "up-types.h" + +static void up_kbd_backlight_finalize (GObject *object); + +struct UpKbdBacklightPrivate +{ + gint max_brightness; + guint brightness; +}; + +G_DEFINE_TYPE_WITH_PRIVATE (UpKbdBacklight, up_kbd_backlight, UP_TYPE_DEVICE_KBD_BACKLIGHT) + +/** + * up_kbd_backlight_emit_change: + **/ +static void +up_kbd_backlight_emit_change(UpKbdBacklight *kbd_backlight, int value, const char *source) +{ + up_exported_kbd_backlight_emit_brightness_changed (UP_EXPORTED_KBD_BACKLIGHT (kbd_backlight), value); + up_exported_kbd_backlight_emit_brightness_changed_with_source (UP_EXPORTED_KBD_BACKLIGHT (kbd_backlight), value, source); +} + + +/** + * up_kbd_backlight_brightness_write: + **/ +static gboolean +up_kbd_backlight_brightness_write (UpDeviceKbdBacklight *kbd_backlight, const gchar *native_path, gint value) +{ + UpKbdBacklight *kbd; + UpKbdBacklightPrivate *priv; + g_autoptr (GString) value_str = g_string_new (NULL); + + g_return_val_if_fail (UP_IS_DEVICE_KBD_BACKLIGHT (kbd_backlight), FALSE); + + kbd = UP_KBD_BACKLIGHT (kbd_backlight); + priv = up_kbd_backlight_get_instance_private (kbd); + + g_string_printf (value_str, "%d", CLAMP (value, 0, priv->max_brightness)); + if (!g_file_set_contents_full (native_path, value_str->str, value_str->len, + G_FILE_SET_CONTENTS_ONLY_EXISTING, 0644, NULL)) + return FALSE; + + return TRUE; +} + +/** + * up_kbd_backlight_brightness_read: + **/ +static gint +up_kbd_backlight_brightness_read (UpDeviceKbdBacklight *kbd_backlight, const gchar *native_path) +{ + GObject *native = NULL; + g_autofree gchar *buf = NULL; + gint64 brightness = -1; + + g_return_val_if_fail (UP_IS_DEVICE_KBD_BACKLIGHT (kbd_backlight), brightness); + + if (!g_file_get_contents (native_path, &buf, NULL, NULL)) + return -1; + + brightness = g_ascii_strtoll (buf, NULL, 10); + if (brightness < 0) { + g_warning ("failed to convert brightness: %s", buf); + return -1; + } + + return brightness; +} + + +/** + * up_kbd_backlight_set_brightness: + **/ +static gboolean +up_kbd_backlight_set_brightness (UpDeviceKbdBacklight *kbd_backlight, gint value) +{ + GObject *native; + g_autofree gchar *filename = NULL; + const gchar *native_path; + gboolean ret = FALSE; + + native = up_device_kbd_backlight_get_native (UP_DEVICE_KBD_BACKLIGHT (kbd_backlight)); + g_return_val_if_fail (native != NULL, FALSE); + + native_path = up_native_get_native_path (native); + g_return_val_if_fail (native_path != NULL, FALSE); + + filename = g_build_filename (native_path, "brightness", NULL); + + g_debug ("setting brightness to %i", value); + ret = up_kbd_backlight_brightness_write (kbd_backlight, filename, value); + + return ret; +} + + +/** + * up_kbd_backlight_get_brightness: + * + * Gets the current brightness + **/ +static gint +up_kbd_backlight_get_brightness (UpDeviceKbdBacklight *kbd_backlight) +{ + GObject *native; + const gchar *native_path; + g_autofree gchar *filename = NULL; + gint brightness = -1; + + native = up_device_kbd_backlight_get_native (UP_DEVICE_KBD_BACKLIGHT (kbd_backlight)); + g_return_val_if_fail (native != NULL, brightness); + + native_path = up_native_get_native_path (native); + g_return_val_if_fail (native_path != NULL, brightness); + + filename = g_build_filename (native_path, "brightness", NULL); + brightness = up_kbd_backlight_brightness_read (kbd_backlight, filename); + + return brightness; +} + +/** + * up_kbd_backlight_get_max_brightness: + * + * Gets the max brightness + **/ +static gint +up_kbd_backlight_get_max_brightness (UpDeviceKbdBacklight *kbd_backlight) +{ + UpKbdBacklight *kbd = UP_KBD_BACKLIGHT (kbd_backlight); + UpKbdBacklightPrivate *priv = up_kbd_backlight_get_instance_private (kbd); + return priv->max_brightness; +} + +/** + * up_kbd_backlight_coldplug: + * + * Initial max brightness + **/ +static gboolean +up_kbd_backlight_coldplug (UpDeviceKbdBacklight *kbd_backlight) +{ + UpKbdBacklight *kbd; + UpKbdBacklightPrivate *priv; + GObject *native; + g_autofree gchar *filename = NULL; + const gchar *native_path = NULL; + + g_return_val_if_fail (UP_IS_DEVICE_KBD_BACKLIGHT (kbd_backlight), FALSE); + + kbd = UP_KBD_BACKLIGHT (kbd_backlight); + priv = up_kbd_backlight_get_instance_private (kbd); + + native = up_device_kbd_backlight_get_native (kbd_backlight); + if (native == NULL) { + priv->max_brightness = 0; + return FALSE; + } + + g_debug ("coldplug kbd backlight"); + + native_path = up_native_get_native_path (native); + filename = g_build_filename (native_path, "max_brightness", NULL); + g_debug ("LED Max brightness path %s", filename); + + priv->max_brightness = up_kbd_backlight_brightness_read (kbd_backlight, filename); + + return TRUE; +} + + +/** + * up_kbd_backlight_finalize: + **/ +static void +up_kbd_backlight_finalize (GObject *object) +{ + UpKbdBacklight *kbd_backlight; + +#if 0 + g_return_if_fail (object != NULL); + g_return_if_fail (UP_IS_KBD_BACKLIGHT (object)); + + kbd_backlight = UP_KBD_BACKLIGHT (object); + kbd_backlight->priv = up_kbd_backlight_get_instance_private (kbd_backlight); + + if (kbd_backlight->priv->channel_hw_changed) { + g_io_channel_shutdown (kbd_backlight->priv->channel_hw_changed, FALSE, NULL); + g_io_channel_unref (kbd_backlight->priv->channel_hw_changed); + } + + if (kbd_backlight->priv->fd_hw_changed >= 0) + close (kbd_backlight->priv->fd_hw_changed); + + /* close file */ + if (kbd_backlight->priv->fd >= 0) + close (kbd_backlight->priv->fd); + + G_OBJECT_CLASS (up_kbd_backlight_parent_class)->finalize (object); +#endif +} + +/** + * up_kbd_backlight_class_init: + **/ +static void +up_kbd_backlight_class_init (UpKbdBacklightClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + UpDeviceKbdBacklightClass *dev_kbd_klass = UP_DEVICE_KBD_BACKLIGHT_CLASS (klass); + + object_class->finalize = up_kbd_backlight_finalize; + + dev_kbd_klass->coldplug = up_kbd_backlight_coldplug; + dev_kbd_klass->get_max_brightness = up_kbd_backlight_get_max_brightness; + dev_kbd_klass->get_brightness = up_kbd_backlight_get_brightness; + dev_kbd_klass->set_brightness = up_kbd_backlight_set_brightness; +} + + +/** + * up_kbd_backlight_init: + **/ +static void +up_kbd_backlight_init (UpKbdBacklight *kbd_backlight) +{ + UpKbdBacklightPrivate *priv = up_kbd_backlight_get_instance_private (kbd_backlight); + + kbd_backlight->priv = up_kbd_backlight_get_instance_private (kbd_backlight); +} + +/** + * up_kbd_backlight_new: + **/ +UpKbdBacklight * +up_kbd_backlight_new (void) +{ + return g_object_new (UP_TYPE_KBD_BACKLIGHT, NULL); +} diff --git a/src/linux/up-kbd-backlight.h b/src/linux/up-kbd-backlight.h new file mode 100644 index 0000000..7f464d0 --- /dev/null +++ b/src/linux/up-kbd-backlight.h @@ -0,0 +1,55 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2025 Kate Hsuan + * + * Licensed under the GNU General Public License Version 2 + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __UP_KBD_BACKLIGHT_H +#define __UP_KBD_BACKLIGHT_H + +#include +#include "up-device-kbd-backlight.h" + +G_BEGIN_DECLS + +#define UP_TYPE_KBD_BACKLIGHT (up_kbd_backlight_get_type ()) +#define UP_KBD_BACKLIGHT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), UP_TYPE_KBD_BACKLIGHT, UpKbdBacklight)) +#define UP_KBD_BACKLIGHT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), UP_TYPE_KBD_BACKLIGHT, UpKbdBacklightClass)) +#define UP_IS_KBD_BACKLIGHT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), UP_TYPE_KBD_BACKLIGHT)) +#define UP_IS_KBD_BACKLIGHT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), UP_TYPE_KBD_BACKLIGHT)) +#define UP_KBD_BACKLIGHT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), UP_TYPE_KBD_BACKLIGHT, UpKbdBacklightClass)) + +typedef struct UpKbdBacklightPrivate UpKbdBacklightPrivate; + +typedef struct +{ + UpDeviceKbdBacklight parent; + UpKbdBacklightPrivate *priv; +} UpKbdBacklight; + +typedef struct +{ + UpDeviceKbdBacklightClass parent_class; +} UpKbdBacklightClass; + +UpKbdBacklight *up_kbd_backlight_new (void); +GType up_kbd_backlight_get_type (void); + +G_END_DECLS + +#endif /* __UP_KBD_BACKLIGHT_H */ diff --git a/src/meson.build b/src/meson.build index e4ed974..0b79415 100644 --- a/src/meson.build +++ b/src/meson.build @@ -38,8 +38,8 @@ upowerd_private = static_library('upowerd-private', 'up-device-list.c', 'up-enumerator.c', 'up-enumerator.h', - 'up-kbd-backlight.h', - 'up-kbd-backlight.c', + 'up-device-kbd-backlight.h', + 'up-device-kbd-backlight.c', 'up-history.h', 'up-history.c', 'up-backend.h', diff --git a/src/up-daemon.c b/src/up-daemon.c index 2881179..29090f0 100644 --- a/src/up-daemon.c +++ b/src/up-daemon.c @@ -33,6 +33,7 @@ #include "up-polkit.h" #include "up-device-list.h" #include "up-device.h" +#include "up-device-kbd-backlight.h" #include "up-backend.h" #include "up-daemon.h" @@ -43,6 +44,7 @@ struct UpDaemonPrivate UpPolkit *polkit; UpBackend *backend; UpDeviceList *power_devices; + UpDeviceList *kbd_backlight_devices; guint action_timeout_id; guint refresh_batteries_id; guint warning_level_id; @@ -582,6 +584,7 @@ up_daemon_shutdown (UpDaemon *daemon) /* forget about discovered devices */ up_device_list_clear (daemon->priv->power_devices); + up_device_list_clear (daemon->priv->kbd_backlight_devices); /* release UpDaemon reference */ g_object_run_dispose (G_OBJECT (daemon->priv->display_device)); @@ -1015,35 +1018,52 @@ up_daemon_get_env_override (UpDaemon *self) * up_daemon_device_added_cb: **/ static void -up_daemon_device_added_cb (UpBackend *backend, UpDevice *device, UpDaemon *daemon) +up_daemon_device_added_cb (UpBackend *backend, GObject *device, UpDaemon *daemon) { const gchar *object_path; UpDaemonPrivate *priv = daemon->priv; g_return_if_fail (UP_IS_DAEMON (daemon)); - g_return_if_fail (UP_IS_DEVICE (device)); + g_return_if_fail (UP_IS_DEVICE (device) || UP_IS_DEVICE_KBD_BACKLIGHT (device)); - /* add to device list */ - up_device_list_insert (priv->power_devices, device); + g_warning ("daemon device added"); - /* connect, so we get changes */ - g_signal_connect (device, "notify", - G_CALLBACK (up_daemon_device_changed_cb), daemon); + if (UP_IS_DEVICE (device)) { + /* power_supply */ + /* add to device list */ + up_device_list_insert (priv->power_devices, device); - /* emit */ - object_path = up_device_get_object_path (device); - if (object_path == NULL) { - g_debug ("Device %s was unregistered before it was on the bus", - up_exported_device_get_native_path (UP_EXPORTED_DEVICE (device))); - return; + /* connect, so we get changes */ + g_signal_connect (device, "notify", + G_CALLBACK (up_daemon_device_changed_cb), daemon); + + /* emit */ + object_path = up_device_get_object_path (UP_DEVICE (device)); + if (object_path == NULL) { + g_debug ("Device %s was unregistered before it was on the bus", + up_exported_device_get_native_path (UP_EXPORTED_DEVICE (device))); + return; + } + + /* Ensure we poll the new device if needed */ + g_source_set_ready_time (daemon->priv->poll_source, 0); + + g_debug ("emitting added: %s", object_path); + up_daemon_update_warning_level (daemon); + up_exported_daemon_emit_device_added (UP_EXPORTED_DAEMON (daemon), object_path); + } else { + /*leds*/ + g_debug ("Add a leds to list"); + /* emit */ + object_path = up_device_kbd_backlight_get_object_path (UP_DEVICE_KBD_BACKLIGHT (device)); + if (object_path == NULL) { + g_debug ("Device %s was unregistered before it was on the bus", + up_exported_kbd_backlight_get_native_path (UP_EXPORTED_KBD_BACKLIGHT (device))); + return; + } + up_device_list_insert (priv->kbd_backlight_devices, G_OBJECT (device)); + up_exported_daemon_emit_device_added (UP_EXPORTED_DAEMON (daemon), object_path); } - - /* Ensure we poll the new device if needed */ - g_source_set_ready_time (daemon->priv->poll_source, 0); - - g_debug ("emitting added: %s", object_path); - up_daemon_update_warning_level (daemon); - up_exported_daemon_emit_device_added (UP_EXPORTED_DAEMON (daemon), object_path); } /** @@ -1135,6 +1155,7 @@ up_daemon_init (UpDaemon *daemon) daemon->priv->polkit = up_polkit_new (); daemon->priv->config = up_config_new (); daemon->priv->power_devices = up_device_list_new (); + daemon->priv->kbd_backlight_devices = up_device_list_new (); daemon->priv->display_device = up_device_new (daemon, NULL); daemon->priv->poll_source = g_source_new (&poll_source_funcs, sizeof (GSource)); @@ -1219,6 +1240,7 @@ up_daemon_finalize (GObject *object) g_clear_pointer (&daemon->priv->poll_source, g_source_destroy); g_object_unref (priv->power_devices); + g_object_unref (priv->kbd_backlight_devices); g_object_unref (priv->display_device); g_object_unref (priv->polkit); g_object_unref (priv->config); diff --git a/src/up-device-kbd-backlight.c b/src/up-device-kbd-backlight.c new file mode 100644 index 0000000..fd71eb0 --- /dev/null +++ b/src/up-device-kbd-backlight.c @@ -0,0 +1,439 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2025 Kate Hsuan + * + * 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 + +#include +#include +#include +#include + +#include "up-native.h" +#include "up-device.h" +#include "up-device-kbd-backlight.h" +#include "up-history.h" +#include "up-history-item.h" +#include "up-stats-item.h" + +typedef struct +{ + UpDaemon *daemon; + /* native == NULL implies display device */ + GObject *native; + + gint fd_hw_changed; + GIOChannel *channel_hw_changed; +} UpDeviceKbdBacklightPrivate; + +static void up_device_kbd_backlight_initable_iface_init (GInitableIface *iface); + +G_DEFINE_TYPE_EXTENDED (UpDeviceKbdBacklight, up_device_kbd_backlight, UP_TYPE_EXPORTED_KBD_BACKLIGHT_SKELETON, 0, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + up_device_kbd_backlight_initable_iface_init) + G_ADD_PRIVATE (UpDeviceKbdBacklight)) + +enum { + PROP_0, + PROP_DAEMON, + PROP_NATIVE, + N_PROPS +}; + +#define UP_DEVICES_KBD_BACKLIGHT_DBUS_PATH "/org/freedesktop/UPower/KbdBacklight" +static GParamSpec *properties[N_PROPS]; + + +/** + * up_kbd_backlight_emit_change: + **/ +static void +up_kbd_backlight_emit_change(UpDeviceKbdBacklight *kbd_backlight, int value, const char *source) +{ + up_exported_kbd_backlight_emit_brightness_changed (UP_EXPORTED_KBD_BACKLIGHT (kbd_backlight), value); + up_exported_kbd_backlight_emit_brightness_changed_with_source (UP_EXPORTED_KBD_BACKLIGHT (kbd_backlight), value, source); +} + + +/** + * up_kbd_backlight_get_brightness: + * + * Gets the current brightness + **/ +static gboolean +up_kbd_backlight_get_brightness (UpExportedKbdBacklight *skeleton, + GDBusMethodInvocation *invocation, + UpDeviceKbdBacklight *kbd_backlight) +{ + UpDeviceKbdBacklightClass *klass; + gint brightness = 0; + + g_return_val_if_fail (UP_IS_DEVICE_KBD_BACKLIGHT (kbd_backlight), FALSE); + + klass = UP_DEVICE_KBD_BACKLIGHT_GET_CLASS (kbd_backlight); + + brightness = klass->get_brightness (kbd_backlight); + + if (brightness >= 0) { + up_exported_kbd_backlight_complete_get_brightness (skeleton, invocation, + brightness); + } else { + g_dbus_method_invocation_return_error (invocation, + UP_DAEMON_ERROR, UP_DAEMON_ERROR_GENERAL, + "error reading brightness"); + } + + return TRUE; +} + + +/** + * up_kbd_backlight_get_max_brightness: + * + * Gets the max brightness + **/ +static gboolean +up_kbd_backlight_get_max_brightness (UpExportedKbdBacklight *skeleton, + GDBusMethodInvocation *invocation, + UpDeviceKbdBacklight *kbd_backlight) +{ + UpDeviceKbdBacklightClass *klass; + gint brightness = -1; + + g_return_val_if_fail (UP_IS_DEVICE_KBD_BACKLIGHT (kbd_backlight), FALSE); + + klass = UP_DEVICE_KBD_BACKLIGHT_GET_CLASS (kbd_backlight); + + if (klass->get_max_brightness != NULL) + brightness = klass->get_max_brightness (kbd_backlight); + + if (brightness >= 0) { + up_exported_kbd_backlight_complete_get_max_brightness (skeleton, invocation, + brightness); + } else { + g_dbus_method_invocation_return_error (invocation, + UP_DAEMON_ERROR, UP_DAEMON_ERROR_GENERAL, + "error reading brightness"); + } + + return TRUE; +} + + +/** + * up_kbd_backlight_set_brightness: + **/ +static gboolean +up_kbd_backlight_set_brightness (UpExportedKbdBacklight *skeleton, + GDBusMethodInvocation *invocation, + gint value, + UpDeviceKbdBacklight *kbd_backlight) +{ + UpDeviceKbdBacklightClass *klass; + gboolean ret = FALSE; + + g_return_val_if_fail (UP_IS_DEVICE_KBD_BACKLIGHT (kbd_backlight), FALSE); + + klass = UP_DEVICE_KBD_BACKLIGHT_GET_CLASS (kbd_backlight); + + if (klass->set_brightness == NULL) { + g_dbus_method_invocation_return_error (invocation, + UP_DAEMON_ERROR, UP_DAEMON_ERROR_GENERAL, + "setting brightness is unsupported"); + return TRUE; + } + ret = klass->set_brightness (kbd_backlight, value); + + if (ret) { + up_exported_kbd_backlight_complete_set_brightness (skeleton, invocation); + up_kbd_backlight_emit_change (kbd_backlight, value, "external"); + } else { + g_dbus_method_invocation_return_error (invocation, + UP_DAEMON_ERROR, UP_DAEMON_ERROR_GENERAL, + "error writing brightness %d", value); + } + + return TRUE; +} + +GObject * +up_device_kbd_backlight_get_native (UpDeviceKbdBacklight *device) +{ + UpDeviceKbdBacklightPrivate *priv = up_device_kbd_backlight_get_instance_private (device); + g_return_val_if_fail (UP_IS_DEVICE_KBD_BACKLIGHT (device), NULL); + return priv->native; +} + +static gchar * +up_device_kbd_backlight_compute_object_path (UpDeviceKbdBacklight *device) +{ + UpDeviceKbdBacklightPrivate *priv = up_device_kbd_backlight_get_instance_private (device); + gchar *basename; + gchar *id; + gchar *object_path; + const gchar *native_path; + guint i; + + if (priv->native == NULL) { + return g_build_filename (UP_DEVICES_KBD_BACKLIGHT_DBUS_PATH, "KbdBacklight", NULL); + } + + native_path = up_exported_kbd_backlight_get_native_path (UP_EXPORTED_KBD_BACKLIGHT (device)); + basename = g_path_get_basename (native_path); + id = g_strjoin ("_", 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'; + if (id[i] == '@') + id[i] = '_'; + } + object_path = g_build_filename (UP_DEVICES_KBD_BACKLIGHT_DBUS_PATH, id, NULL); + + g_free (basename); + g_free (id); + + return object_path; +} + + +static void +up_device_kbd_backlight_export_skeleton (UpDeviceKbdBacklight *device, + const gchar *object_path) +{ + UpDeviceKbdBacklightPrivate *priv = up_device_kbd_backlight_get_instance_private (device); + GError *error = NULL; + + g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (device), + g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (priv->daemon)), + object_path, + &error); + + if (error != NULL) { + g_critical ("error registering device on system bus: %s", error->message); + g_error_free (error); + } +} + + +gboolean +up_device_kbd_backlight_register (UpDeviceKbdBacklight *device) +{ + g_autofree char *computed_object_path = NULL; + + if (g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (device)) != NULL) + return FALSE; + computed_object_path = up_device_kbd_backlight_compute_object_path (device); + g_debug ("Exported UpDevice with path %s", computed_object_path); + up_device_kbd_backlight_export_skeleton (device, computed_object_path); + return TRUE; +} + +const gchar * +up_device_kbd_backlight_get_object_path (UpDeviceKbdBacklight *device) +{ + g_return_val_if_fail (UP_IS_DEVICE_KBD_BACKLIGHT (device), NULL); + return g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (device)); +} + +static void +up_device_kbd_backlight_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + UpDeviceKbdBacklight *device = UP_DEVICE_KBD_BACKLIGHT (object); + UpDeviceKbdBacklightPrivate *priv = up_device_kbd_backlight_get_instance_private (device); + + g_debug ("kbd property set"); + + switch (prop_id) + { + case PROP_DAEMON: + priv->daemon = g_value_dup_object (value); + break; + + case PROP_NATIVE: + g_debug ("Set kbd native"); + priv->native = g_value_dup_object (value); + if (priv->native == NULL) + g_warning ("KBD native is NULL"); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +up_device_kbd_backlight_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + UpDeviceKbdBacklight *device = UP_DEVICE_KBD_BACKLIGHT (object); + UpDeviceKbdBacklightPrivate *priv = up_device_kbd_backlight_get_instance_private (device); + + return; + + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static gboolean +up_device_kbd_backlight_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + + + UpDeviceKbdBacklight *device = UP_DEVICE_KBD_BACKLIGHT (initable); + UpDeviceKbdBacklightPrivate *priv = up_device_kbd_backlight_get_instance_private (device); + const gchar *native_path = "KbdDevice"; + UpDeviceKbdBacklightClass *klass = UP_DEVICE_KBD_BACKLIGHT_GET_CLASS (device); + int ret; + + g_return_val_if_fail (UP_IS_DEVICE_KBD_BACKLIGHT (device), FALSE); + + if (priv->native) { + native_path = up_native_get_native_path (priv->native); + up_exported_kbd_backlight_set_native_path (UP_EXPORTED_KBD_BACKLIGHT (device), native_path); + } + + /* coldplug source */ + if (klass->coldplug != NULL) { + ret = klass->coldplug (device); + if (!ret) { + g_debug ("failed to coldplug %s", native_path); + g_propagate_error (error, g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, + "Failed to coldplug %s", native_path)); + + return FALSE; + } + } + +register_device: + /* put on the bus */ + up_device_kbd_backlight_register (device); + + return TRUE; +} + +static void +up_device_kbd_backlight_initable_iface_init (GInitableIface *iface) +{ + iface->init = up_device_kbd_backlight_initable_init; +} + +/** + * up_kbd_backlight_init: + **/ +static void +up_device_kbd_backlight_init (UpDeviceKbdBacklight *kbd_backlight) +{ + //kbd_backlight->priv = up_device_kbd_backlight_get_instance_private (kbd_backlight); + + g_signal_connect (kbd_backlight, "handle-get-brightness", + G_CALLBACK (up_kbd_backlight_get_brightness), kbd_backlight); + g_signal_connect (kbd_backlight, "handle-get-max-brightness", + G_CALLBACK (up_kbd_backlight_get_max_brightness), kbd_backlight); + g_signal_connect (kbd_backlight, "handle-set-brightness", + G_CALLBACK (up_kbd_backlight_set_brightness), kbd_backlight); +} + +/** + * up_kbd_backlight_finalize: + **/ +static void +up_device_kbd_backlight_finalize (GObject *object) +{ + UpDeviceKbdBacklight *kbd_backlight = NULL; + UpDeviceKbdBacklightPrivate *priv = up_device_kbd_backlight_get_instance_private (kbd_backlight); + + g_return_if_fail (object != NULL); + g_return_if_fail (UP_IS_DEVICE_KBD_BACKLIGHT (object)); + + kbd_backlight = UP_DEVICE_KBD_BACKLIGHT (object); + //kbd_backlight->priv = up_device_kbd_backlight_get_instance_private (kbd_backlight); + + //if (kbd_backlight->priv->channel_hw_changed) { + // g_io_channel_shutdown (kbd_backlight->priv->channel_hw_changed, FALSE, NULL); + // g_io_channel_unref (kbd_backlight->priv->channel_hw_changed); + //} + + //if (kbd_backlight->priv->fd_hw_changed >= 0) + // close (kbd_backlight->priv->fd_hw_changed); + + /* close file */ + //if (kbd_backlight->priv->fd >= 0) + // close (kbd_backlight->priv->fd); + + G_OBJECT_CLASS (up_device_kbd_backlight_parent_class)->finalize (object); +} + +static void +up_device_kbd_backlight_class_init (UpDeviceKbdBacklightClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + //object_class->notify = up_device_notify; + object_class->finalize = up_device_kbd_backlight_finalize; + //object_class->dispose = up_device_dispose; + + object_class->set_property = up_device_kbd_backlight_set_property; + object_class->get_property = up_device_kbd_backlight_get_property; + + 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); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +/** + * up_kbd_backlight_new: + **/ +UpDeviceKbdBacklight * +up_device_kbd_backlight_new (UpDaemon *daemon, GObject *native) +{ + return UP_DEVICE_KBD_BACKLIGHT (g_object_new (UP_TYPE_DEVICE_KBD_BACKLIGHT, + "daemon", daemon, + "native", native, + NULL)); +} diff --git a/src/up-device-kbd-backlight.h b/src/up-device-kbd-backlight.h new file mode 100644 index 0000000..8119a53 --- /dev/null +++ b/src/up-device-kbd-backlight.h @@ -0,0 +1,77 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2025 Kate Hsuan + * + * 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_DEVICE_KBD_BACKLIGHT_H__ +#define __UP_DEVICE_KBD_BACKLIGHT_H__ + +#include +#include "up-daemon.h" + +G_BEGIN_DECLS + +#define UP_TYPE_DEVICE_KBD_BACKLIGHT (up_device_kbd_backlight_get_type ()) + +G_DECLARE_DERIVABLE_TYPE (UpDeviceKbdBacklight, up_device_kbd_backlight, UP, DEVICE_KBD_BACKLIGHT, UpExportedKbdBacklightSkeleton) + +struct _UpDeviceKbdBacklightClass +{ + UpExportedKbdBacklightSkeletonClass parent_class; + + /* vtable */ + gboolean (*coldplug) (UpDeviceKbdBacklight *device); + + gint (*get_max_brightness) (UpDeviceKbdBacklight *device); + gint (*get_brightness) (UpDeviceKbdBacklight *device); + gboolean (*set_brightness) (UpDeviceKbdBacklight *device, gint brightness); + +}; + + +GType up_device_kbd_backlight_get_type (void); + +const gchar * up_device_kbd_backlight_get_object_path (UpDeviceKbdBacklight *device); +GObject * up_device_kbd_backlight_get_native (UpDeviceKbdBacklight *device); +UpDeviceKbdBacklight *up_device_kbd_backlight_new (UpDaemon *daemon, + GObject *native); +gboolean up_device_kbd_backlight_register (UpDeviceKbdBacklight *device); + +#if 0 + +UpDaemon *up_device_get_daemon (UpDevice *device); +GObject *up_device_get_native (UpDevice *device); +const gchar *up_device_get_object_path (UpDevice *device); + +gboolean up_device_get_online (UpDevice *device, + gboolean *online); +const gchar *up_device_get_state_dir_override (UpDevice *device); +gboolean up_device_polkit_is_allowed (UpDevice *device, + GDBusMethodInvocation *invocation); +void up_device_sibling_discovered (UpDevice *device, + GObject *sibling); +gboolean up_device_refresh_internal (UpDevice *device, + UpRefreshReason reason); +void up_device_unregister (UpDevice *device); + +gboolean up_device_is_registered (UpDevice *device); +#endif + +G_END_DECLS + +#endif /* __UP_DEVICE_KBD_BACKLIGHT_H__ */ diff --git a/src/up-device-list.c b/src/up-device-list.c index af00894..216719d 100644 --- a/src/up-device-list.c +++ b/src/up-device-list.c @@ -28,6 +28,7 @@ #include "up-native.h" #include "up-device-list.h" #include "up-device.h" +#include "up-device-kbd-backlight.h" static void up_device_list_finalize (GObject *object); @@ -80,7 +81,10 @@ up_device_list_insert (UpDeviceList *list, gpointer device) g_return_val_if_fail (UP_IS_DEVICE_LIST (list), FALSE); g_return_val_if_fail (device != NULL, FALSE); - native = up_device_get_native (UP_DEVICE (device)); + if (UP_IS_DEVICE_KBD_BACKLIGHT (device)) + native = up_device_kbd_backlight_get_native (UP_DEVICE_KBD_BACKLIGHT (device)); + else + native = up_device_get_native (UP_DEVICE (device)); g_return_val_if_fail (native != NULL, FALSE); native_path = up_native_get_native_path (native); diff --git a/src/up-main.c b/src/up-main.c index f677cb5..af27e38 100644 --- a/src/up-main.c +++ b/src/up-main.c @@ -62,7 +62,6 @@ up_state_new (void) { UpState *state = g_new0 (UpState, 1); - state->kbd_backlight = up_kbd_backlight_new (); state->daemon = up_daemon_new (); state->loop = g_main_loop_new (NULL, FALSE); @@ -79,7 +78,7 @@ up_main_bus_acquired (GDBusConnection *connection, { UpState *state = user_data; - up_kbd_backlight_register (state->kbd_backlight, connection); + //up_kbd_backlight_register (state->kbd_backlight, connection); if (!up_daemon_startup (state->daemon, connection)) { g_warning ("Could not startup; bailing out"); g_main_loop_quit (state->loop);