Files
feedbackd/src/fbd-dev-leds.c
T
Guido Günther 995d21b587 fbd-dev-led: Move multicolor handling to separate class
Everything related to multicolor moves into the new class and we add the
necessary getters and setters so derived classes can access the parent
classes attributes where needed.
2023-02-10 15:15:12 +00:00

183 lines
4.5 KiB
C

/*
* Copyright (C) 2020 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
* Author: Guido Günther <agx@sigxcpu.org>
*
* See Documentation/ABI/testing/sysfs-class-led-trigger-pattern
*/
#define G_LOG_DOMAIN "fbd-dev-leds"
#include "fbd.h"
#include "fbd-enums.h"
#include "fbd-dev-led.h"
#include "fbd-dev-led-multicolor.h"
#include "fbd-dev-leds.h"
#include "fbd-feedback-led.h"
#include "fbd-udev.h"
#include <gio/gio.h>
#define LED_SUBSYSTEM "leds"
/**
* FbdDevLeds:
*
* LED device interface
*
* #FbdDevLeds is used to interface with LEDS via sysfs
* It currently only supports one pattern per led at a time.
*/
typedef struct _FbdDevLeds {
GObject parent;
GUdevClient *client;
GSList *leds;
} FbdDevLeds;
static void initable_iface_init (GInitableIface *iface);
G_DEFINE_TYPE_WITH_CODE (FbdDevLeds, fbd_dev_leds, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init));
static FbdDevLed *
find_led_by_color (FbdDevLeds *self, FbdFeedbackLedColor color)
{
g_return_val_if_fail (self->leds, NULL);
for (GSList *l = self->leds; l != NULL; l = l->next) {
FbdDevLed *led = l->data;
if (fbd_dev_led_has_color (led, color))
return led;
}
/* If we did not match a color pick the first */
return self->leds->data;
}
static gboolean
initable_init (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
const gchar * const subsystems[] = { LED_SUBSYSTEM, NULL };
FbdDevLeds *self = FBD_DEV_LEDS (initable);
g_autolist (GUdevDevice) leds = NULL;
gboolean found = FALSE;
self->client = g_udev_client_new (subsystems);
leds = g_udev_client_query_by_subsystem (self->client, LED_SUBSYSTEM);
for (GList *l = leds; l != NULL; l = l->next) {
g_autoptr (GError) err = NULL;
GUdevDevice *dev = G_UDEV_DEVICE (l->data);
FbdDevLed *led;
if (g_strcmp0 (g_udev_device_get_property (dev, FEEDBACKD_UDEV_ATTR),
FEEDBACKD_UDEV_VAL_LED)) {
continue;
}
/* Try multicolor first, fall back to single color */
led = fbd_dev_led_multicolor_new (dev, &err);
if (led == NULL) {
g_debug ("Led not usable as multicolor: '%s'", err->message);
led = fbd_dev_led_new (dev, &err);
if (led == NULL)
g_debug ("Led not usable as single color: '%s'", err->message);
}
if (led) {
self->leds = g_slist_append (self->leds, led);
found = TRUE;
}
}
/* TODO: listen for new leds via udev events */
if (!found) {
g_set_error (error,
G_FILE_ERROR, G_FILE_ERROR_FAILED,
"No usable LEDs found");
}
return found;
}
static void
initable_iface_init (GInitableIface *iface)
{
iface->init = initable_init;
}
static void
fbd_dev_leds_dispose (GObject *object)
{
FbdDevLeds *self = FBD_DEV_LEDS (object);
g_clear_object (&self->client);
g_slist_free_full (self->leds, (GDestroyNotify)g_object_unref);
self->leds = NULL;
G_OBJECT_CLASS (fbd_dev_leds_parent_class)->dispose (object);
}
static void
fbd_dev_leds_class_init (FbdDevLedsClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = fbd_dev_leds_dispose;
}
static void
fbd_dev_leds_init (FbdDevLeds *self)
{
}
FbdDevLeds *
fbd_dev_leds_new (GError **error)
{
return FBD_DEV_LEDS (g_initable_new (FBD_TYPE_DEV_LEDS,
NULL,
error,
NULL));
}
/**
* fbd_dev_leds_start_periodic:
* @self: The #FbdDevLeds
* @color: The color to use for the LED pattern
* @max_brightness_percentage: The max brightness (in percent) to use for the pattern
* @freq: The pattern's frequency in mHz
*
* Start periodic feedback.
*/
gboolean
fbd_dev_leds_start_periodic (FbdDevLeds *self, FbdFeedbackLedColor color,
guint max_brightness_percentage, guint freq)
{
FbdDevLed *led;
g_return_val_if_fail (FBD_IS_DEV_LEDS (self), FALSE);
g_return_val_if_fail (max_brightness_percentage <= 100.0, FALSE);
led = find_led_by_color (self, color);
g_return_val_if_fail (led, FALSE);
return fbd_dev_led_start_periodic (led, color, max_brightness_percentage, freq);
}
gboolean
fbd_dev_leds_stop (FbdDevLeds *self, FbdFeedbackLedColor color)
{
FbdDevLed *led;
g_return_val_if_fail (FBD_IS_DEV_LEDS (self), FALSE);
led = find_led_by_color (self, color);
g_return_val_if_fail (led, FALSE);
return fbd_dev_led_set_brightness (led, 0);
}