From 1cfb72f33ade965ca769721b9e248ca916482cef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Sat, 26 Sep 2020 12:19:50 +0200 Subject: [PATCH] Add torch support This is modeled like KbdBacklight. Taken from: https://gitlab.freedesktop.org/upower/upower/-/merge_requests/50 --- dbus/Makefile.am | 11 + dbus/org.freedesktop.UPower.Torch.xml | 148 ++++++++++ doc/Makefile.am | 9 +- src/Makefile.am | 4 +- src/org.freedesktop.UPower.conf.in | 4 + src/up-main.c | 5 + src/up-torch.c | 388 ++++++++++++++++++++++++++ src/up-torch.h | 40 +++ 8 files changed, 607 insertions(+), 2 deletions(-) create mode 100644 dbus/org.freedesktop.UPower.Torch.xml create mode 100644 src/up-torch.c create mode 100644 src/up-torch.h diff --git a/dbus/Makefile.am b/dbus/Makefile.am index 017c7eb..264761c 100644 --- a/dbus/Makefile.am +++ b/dbus/Makefile.am @@ -17,6 +17,8 @@ BUILT_SOURCES = \ up-device-generated.c \ up-kbd-backlight-generated.h \ up-kbd-backlight-generated.c \ + up-torch-generated.h \ + up-torch-generated.c \ up-wakeups-generated.h \ up-wakeups-generated.c @@ -46,6 +48,14 @@ up-kbd-backlight-generated.h: org.freedesktop.UPower.KbdBacklight.xml Makefile.a $(srcdir)/org.freedesktop.UPower.KbdBacklight.xml up-kbd-backlight-generated.c: up-kbd-backlight-generated.h +up-torch-generated.h: org.freedesktop.UPower.Torch.xml Makefile.am + $(AM_V_GEN) gdbus-codegen --interface-prefix org.freedesktop.UPower.Torch. \ + --generate-c-code up-torch-generated \ + --c-namespace Up \ + --annotate "org.freedesktop.UPower.Torch" "org.gtk.GDBus.C.Name" ExportedTorch \ + $(srcdir)/org.freedesktop.UPower.Torch.xml +up-torch-generated.c: up-torch-generated.h + up-wakeups-generated.h: org.freedesktop.UPower.Wakeups.xml Makefile.am $(AM_V_GEN) gdbus-codegen --interface-prefix org.freedesktop.UPower.Wakeups. \ --generate-c-code up-wakeups-generated \ @@ -59,6 +69,7 @@ dist_dbusif_DATA = \ org.freedesktop.UPower.xml \ org.freedesktop.UPower.Device.xml \ org.freedesktop.UPower.KbdBacklight.xml \ + org.freedesktop.UPower.Torch.xml \ org.freedesktop.UPower.Wakeups.xml -include $(top_srcdir)/git.mk diff --git a/dbus/org.freedesktop.UPower.Torch.xml b/dbus/org.freedesktop.UPower.Torch.xml new file mode 100644 index 0000000..4f7bfd9 --- /dev/null +++ b/dbus/org.freedesktop.UPower.Torch.xml @@ -0,0 +1,148 @@ + +]> + + + + + + org.freedesktop.UPower.Torch is a DBus interface implemented + by UPower. + It allows a torch (if present) to be controlled. + + + + + + + + + + The maximum value of the torch brightness. + + + + + + + Get the maximum brightness level for the torch. + + + + if an error occured while getting the maximum brightness + + + + + + + + + + The current value of the torch brightness. + + + + + + + Get the brightness level of the torch. + + + + if an error occured while getting the brightness + + + + + + + + + + The value to set the torch brightness. + + + + + + + Set the brightness level of the torch. + + + + if an error occured while setting the brightness + + + + + + + + + + The new brightness value of the torch. + + + + + + + The torch brightness level has changed. + + + + + + + + + + The new brightness value of the torch. + + + + + + + Source of the torch brightness change, either + "external" if SetBrightness was called, or "internal" if the + hardware changed the keyboard brightness itself. + + + + + + + The torch brightness level has changed including + information about the source of the change. + + + + + + + + + + The maximum value of the torch brightness. + + + + + + + + + + The current value of the torch brightness. + + + + + + + + diff --git a/doc/Makefile.am b/doc/Makefile.am index 2c20415..718c057 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -65,6 +65,11 @@ org.freedesktop.UPower.KbdBacklight.ref.xml : $(top_srcdir)/dbus/org.freedesktop echo """" > $@ $(XSLTPROC) $(top_srcdir)/doc/spec-to-docbook.xsl $< | tail -n +2 >> $@ +org.freedesktop.UPower.Torch.ref.xml : $(top_srcdir)/dbus/org.freedesktop.UPower.Torch.xml $(top_srcdir)/doc/spec-to-docbook.xsl + if $(AM_V_P); then set -x; else echo " GEN $@"; fi + echo """" > $@ + $(XSLTPROC) $(top_srcdir)/doc/spec-to-docbook.xsl $< | tail -n +2 >> $@ + org.freedesktop.UPower.Wakeups.ref.xml : $(top_srcdir)/dbus/org.freedesktop.UPower.Wakeups.xml $(top_srcdir)/doc/spec-to-docbook.xsl if $(AM_V_P); then set -x; else echo " GEN $@"; fi echo """" > $@ @@ -75,7 +80,8 @@ EXTRA_DIST = spec-to-docbook.xsl dbus-introspect-docs.dtd BUILT_SOURCES = \ org.freedesktop.UPower.ref.xml \ org.freedesktop.UPower.Device.ref.xml \ - org.freedesktop.UPower.KbdBacklight.ref.xml + org.freedesktop.UPower.KbdBacklight.ref.xml \ + org.freedesktop.UPower.Torch.ref.xml # Non-autogenerated SGML files to be included in $(DOC_MAIN_SGML_FILE) content_files = \ @@ -102,6 +108,7 @@ MAINTAINERCLEANFILES = \ DISTCLEANFILES = \ org.freedesktop.UPower.Device.ref.xml \ org.freedesktop.UPower.KbdBacklight.ref.xml \ + org.freedesktop.UPower.Torch.ref.xml \ org.freedesktop.UPower.ref.xml \ upowerd.xml \ upower.xml \ diff --git a/src/Makefile.am b/src/Makefile.am index 3400139..f022efb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -60,7 +60,9 @@ upowerd_SOURCES = \ up-backend.h \ up-native.h \ up-main.c \ - up-backend-bsd-private.h \ + up-torch.h \ + up-torch.c \ + up-backend-bsd-private.h \ $(BUILT_SOURCES) upowerd_CPPFLAGS = \ diff --git a/src/org.freedesktop.UPower.conf.in b/src/org.freedesktop.UPower.conf.in index 5a6c660..ef60549 100644 --- a/src/org.freedesktop.UPower.conf.in +++ b/src/org.freedesktop.UPower.conf.in @@ -21,6 +21,8 @@ send_interface="org.freedesktop.DBus.Properties"/> + @@ -28,6 +30,8 @@ send_interface="org.freedesktop.UPower"/> + daemon); g_clear_object (&state->kbd_backlight); + g_clear_object (&state->torch); g_clear_object (&state->wakeups); g_clear_object (&state->daemon); g_clear_pointer (&state->loop, g_main_loop_unref); @@ -68,6 +71,7 @@ up_state_new (void) UpState *state = g_new0 (UpState, 1); state->kbd_backlight = up_kbd_backlight_new (); + state->torch = up_torch_new (); state->wakeups = up_wakeups_new (); state->daemon = up_daemon_new (); state->loop = g_main_loop_new (NULL, FALSE); @@ -86,6 +90,7 @@ up_main_bus_acquired (GDBusConnection *connection, UpState *state = user_data; up_kbd_backlight_register (state->kbd_backlight, connection); + up_torch_register (state->torch, connection); up_wakeups_register (state->wakeups, connection); if (!up_daemon_startup (state->daemon, connection)) { g_warning ("Could not startup; bailing out"); diff --git a/src/up-torch.c b/src/up-torch.c new file mode 100644 index 0000000..8705041 --- /dev/null +++ b/src/up-torch.c @@ -0,0 +1,388 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2020 Guido Günther + * 2008 Richard Hughes + * 2010 Alex Murray + * + * 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-torch.h" +#include "up-daemon.h" +#include "up-types.h" + +static void up_torch_finalize (GObject *object); + +struct _UpTorch +{ + UpExportedTorchSkeleton parent; + + gint fd; + gint fd_hw_changed; + GIOChannel *channel_hw_changed; + gint max_brightness; +}; + +G_DEFINE_TYPE (UpTorch, up_torch, UP_TYPE_EXPORTED_TORCH_SKELETON) + +/** + * up_torch_emit_change: + **/ +static void +up_torch_emit_change(UpTorch *torch, int value, const char *source) +{ + g_object_set (torch, "brightness", value, NULL); + up_exported_torch_emit_brightness_changed (UP_EXPORTED_TORCH (torch), value); + up_exported_torch_emit_brightness_changed_with_source (UP_EXPORTED_TORCH (torch), value, source); +} + +/** + * up_torch_brightness_read: + **/ +static gint +up_torch_brightness_read (UpTorch *torch, int fd) +{ + gchar buf[16]; + gchar *end = NULL; + ssize_t len; + gint64 brightness = -1; + + g_return_val_if_fail (fd >= 0, brightness); + + lseek (fd, 0, SEEK_SET); + len = read (fd, buf, G_N_ELEMENTS (buf) - 1); + + if (len > 0) { + buf[len] = '\0'; + brightness = g_ascii_strtoll (buf, &end, 10); + + if (brightness < 0 || + brightness > torch->max_brightness || + end == buf) { + brightness = -1; + g_warning ("failed to convert brightness: %s", buf); + } + } + + g_object_set (torch, "brightness", brightness, NULL); + return brightness; +} + +/** + * up_torch_brightness_write: + **/ +static gboolean +up_torch_brightness_write (UpTorch *torch, gint value) +{ + g_autofree gchar *text = NULL; + gint retval; + gint length; + gboolean ret = TRUE; + + /* write new values to backlight */ + if (torch->fd < 0) { + g_warning ("cannot write to torch as file not open"); + return FALSE; + } + + /* limit to between 0 and max */ + value = CLAMP (value, 0, torch->max_brightness); + + /* convert to text */ + text = g_strdup_printf ("%i", value); + length = strlen (text); + + /* write to file */ + lseek (torch->fd, 0, SEEK_SET); + retval = write (torch->fd, text, length); + if (retval != length) { + g_warning ("writing '%s' to device failed", text); + return FALSE; + } + + /* emit signal */ + up_torch_emit_change (torch, value, "external"); + + return ret; +} + +/** + * up_torch_get_brightness: + * + * Gets the current brightness + **/ +static gboolean +up_torch_get_brightness (UpExportedTorch *skeleton, + GDBusMethodInvocation *invocation, + UpTorch *torch) +{ + gint brightness; + + brightness = up_torch_brightness_read (torch, torch->fd); + + if (brightness >= 0) { + up_exported_torch_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_torch_get_max_brightness: + * + * Gets the max brightness + **/ +static gboolean +up_torch_get_max_brightness (UpExportedTorch *skeleton, + GDBusMethodInvocation *invocation, + UpTorch *torch) +{ + up_exported_torch_complete_get_max_brightness (skeleton, invocation, + torch->max_brightness); + return TRUE; +} + +/** + * up_torch_set_brightness: + **/ +static gboolean +up_torch_set_brightness (UpExportedTorch *skeleton, + GDBusMethodInvocation *invocation, + gint value, + UpTorch *torch) +{ + gboolean ret = FALSE; + + g_debug ("setting brightness to %i", value); + ret = up_torch_brightness_write (torch, value); + + if (ret) { + up_exported_torch_complete_set_brightness (skeleton, invocation); + } else { + g_dbus_method_invocation_return_error (invocation, + UP_DAEMON_ERROR, UP_DAEMON_ERROR_GENERAL, + "error writing brightness %d", value); + } + + return TRUE; +} + +/** + * up_torch_class_init: + **/ +static void +up_torch_class_init (UpTorchClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = up_torch_finalize; +} + +/** + * up_torch_event_io: + **/ +static gboolean +up_torch_event_io (GIOChannel *channel, GIOCondition condition, gpointer data) +{ + UpTorch *torch = (UpTorch*) data; + gint brightness; + + if (!(condition & G_IO_PRI)) + return FALSE; + + brightness = up_torch_brightness_read (torch, torch->fd_hw_changed); + if (brightness < 0 && errno == ENODEV) + return FALSE; + + if (brightness >= 0) + up_torch_emit_change (torch, brightness, "internal"); + + return TRUE; +} + +/** + * up_torch_find: + **/ +static gboolean +up_torch_find (UpTorch *torch) +{ + gboolean ret; + gboolean found = FALSE; + GDir *dir; + const gchar *filename; + gchar *end = NULL; + g_autofree gchar *dir_path = NULL; + g_autofree gchar *path_max = NULL; + g_autofree gchar *path_now = NULL; + g_autofree gchar *path_hw_changed = NULL; + g_autofree gchar *buf_max = NULL; + g_autofree gchar *buf_now = NULL; + GError *error = NULL; + + torch->fd = -1; + + /* open directory */ + dir = g_dir_open ("/sys/class/leds", 0, &error); + if (dir == NULL) { + if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) + g_warning ("failed to open directory: %s", error->message); + g_error_free (error); + goto out; + } + + /* find a led device that is a torch */ + while ((filename = g_dir_read_name (dir)) != NULL) { + if (g_strstr_len (filename, -1, "torch") != NULL || + g_strstr_len (filename, -1, "flash") != NULL) { + dir_path = g_build_filename ("/sys/class/leds", + filename, NULL); + break; + } + } + + /* nothing found */ + if (dir_path == NULL) + goto out; + + /* read max brightness */ + path_max = g_build_filename (dir_path, "max_brightness", NULL); + ret = g_file_get_contents (path_max, &buf_max, NULL, &error); + if (!ret) { + g_warning ("failed to get max brightness: %s", error->message); + g_error_free (error); + goto out; + } + torch->max_brightness = g_ascii_strtoull (buf_max, &end, 10); + if (torch->max_brightness == 0 && end == buf_max) { + g_warning ("failed to convert max brightness: %s", buf_max); + goto out; + } + g_object_set (torch, "max-brightness", torch->max_brightness, NULL); + + /* open the brightness file for read and write operations */ + path_now = g_build_filename (dir_path, "brightness", NULL); + torch->fd = open (path_now, O_RDWR); + + /* read brightness and check if it has an acceptable value */ + if (up_torch_brightness_read (torch, torch->fd) < 0) + goto out; + + path_hw_changed = g_build_filename (dir_path, "brightness_hw_changed", NULL); + torch->fd_hw_changed = open (path_hw_changed, O_RDONLY); + if (torch->fd_hw_changed >= 0) { + torch->channel_hw_changed = g_io_channel_unix_new (torch->fd_hw_changed); + g_io_add_watch (torch->channel_hw_changed, + G_IO_PRI, up_torch_event_io, torch); + } + + /* success */ + found = TRUE; +out: + if (dir != NULL) + g_dir_close (dir); + return found; +} + +/** + * up_torch_init: + **/ +static void +up_torch_init (UpTorch *torch) +{ + g_signal_connect (torch, "handle-get-brightness", + G_CALLBACK (up_torch_get_brightness), torch); + g_signal_connect (torch, "handle-get-max-brightness", + G_CALLBACK (up_torch_get_max_brightness), torch); + g_signal_connect (torch, "handle-set-brightness", + G_CALLBACK (up_torch_set_brightness), torch); +} + +/** + * up_torch_finalize: + **/ +static void +up_torch_finalize (GObject *object) +{ + UpTorch *torch; + + g_return_if_fail (object != NULL); + g_return_if_fail (UP_IS_TORCH (object)); + + torch = UP_TORCH (object); + + if (torch->channel_hw_changed) { + g_io_channel_shutdown (torch->channel_hw_changed, FALSE, NULL); + g_io_channel_unref (torch->channel_hw_changed); + } + + if (torch->fd_hw_changed >= 0) + close (torch->fd_hw_changed); + + /* close file */ + if (torch->fd >= 0) + close (torch->fd); + + G_OBJECT_CLASS (up_torch_parent_class)->finalize (object); +} + +/** + * up_torch_new: + **/ +UpTorch * +up_torch_new (void) +{ + return g_object_new (UP_TYPE_TORCH, NULL); +} + +void +up_torch_register (UpTorch *torch, + GDBusConnection *connection) +{ + g_autoptr(GError) error = NULL; + + /* find a kbd backlight in sysfs */ + if (!up_torch_find (torch)) { + g_debug ("cannot find a torch"); + return; + } + + g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (torch), + connection, + "/org/freedesktop/UPower/Torch", + &error); + + if (error != NULL) { + g_warning ("Cannot export Torch object to bus: %s", error->message); + } +} diff --git a/src/up-torch.h b/src/up-torch.h new file mode 100644 index 0000000..e84c9f5 --- /dev/null +++ b/src/up-torch.h @@ -0,0 +1,40 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Richard Hughes + * 2010 Alex Murray + * 2010 Alex Murray + * + * 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_TORCH_H +#define __UP_TORCH_H + +#include + +G_BEGIN_DECLS + +#define UP_TYPE_TORCH (up_torch_get_type ()) +G_DECLARE_FINAL_TYPE (UpTorch, up_torch, UP, TORCH, UpExportedTorchSkeleton); + +UpTorch *up_torch_new (void); +void up_torch_register (UpTorch *torch, + GDBusConnection *connection); + +G_END_DECLS + +#endif /* __UP_TORCH_H */ -- 2.20.1