diff --git a/doc/feedback-themes.rst b/doc/feedback-themes.rst index 4676ae2..dea3682 100644 --- a/doc/feedback-themes.rst +++ b/doc/feedback-themes.rst @@ -48,7 +48,9 @@ LED feedback The `Led` feedback type uses two properties to specify the way a LED blinks. - `color`: This specifies the color a LED should blink with. It supports the fixed color names `red`, - `green`, `blue` and `white. + `green`, `blue` and `white` as well as values in the RGB HEX format (`#RRGGBB`) where + `RR`, `GG` and `BB` are two digit hex value between `00` and `FF` specifying the value of + each component. E.g. `#00FFFF` corresponds to cyan color. - `frequency`: The LEDs blinkinig frequencey in mHz. See also diff --git a/src/fbd-dev-led-multicolor.c b/src/fbd-dev-led-multicolor.c index b211dfb..e330155 100644 --- a/src/fbd-dev-led-multicolor.c +++ b/src/fbd-dev-led-multicolor.c @@ -91,7 +91,8 @@ fbd_dev_led_multicolor_probe (FbdDevLed *led, GError **error) static gboolean fbd_dev_led_multicolor_set_color (FbdDevLed *led, - FbdFeedbackLedColor color) + FbdFeedbackLedColor color, + FbdLedRgbColor *rgb) { FbdDevLedMulticolor *self = FBD_DEV_LED_MULTICOLOR (led); FbdDevLedMulticolorPrivate *priv = fbd_dev_led_multicolor_get_instance_private (self); @@ -124,12 +125,18 @@ fbd_dev_led_multicolor_set_color (FbdDevLed *led, colors[priv->green_index] = 0; colors[priv->blue_index] = max_brightness; break; + case FBD_FEEDBACK_LED_COLOR_RGB: + colors[priv->red_index] = rgb->r; + colors[priv->green_index] = rgb->g; + colors[priv->blue_index] = rgb->b; + break; default: g_warning("Unhandled color: %d\n", color); return FALSE; } intensity = g_strdup_printf ("%u %u %u\n", colors[0], colors[1], colors[2]); + g_debug ("Multicolor intensity: %s", intensity); fbd_dev_led_set_brightness (led, max_brightness); success = fbd_udev_set_sysfs_path_attr_as_string (dev, @@ -158,6 +165,8 @@ fbd_dev_led_multicolor_supports_color (FbdDevLed *led, return TRUE; case FBD_FEEDBACK_LED_COLOR_BLUE: return TRUE; + case FBD_FEEDBACK_LED_COLOR_RGB: + return TRUE; default: g_warning ("Color unsupported: %d", color); return FALSE; diff --git a/src/fbd-dev-led.c b/src/fbd-dev-led.c index f3b942f..1ec0812 100644 --- a/src/fbd-dev-led.c +++ b/src/fbd-dev-led.c @@ -33,10 +33,7 @@ static GParamSpec *props[PROP_LAST_PROP]; typedef struct _FbdDevLedPrivate { GUdevDevice *dev; guint max_brightness; - /* - * We just use the colors from the feedback until we - * do rgb mixing, etc - */ + FbdFeedbackLedColor color; } FbdDevLedPrivate; @@ -55,9 +52,6 @@ fbd_dev_led_probe_default (FbdDevLed *led, GError **error) gboolean success = FALSE; name = g_udev_device_get_name (priv->dev); - /* We don't know anything about diffusors that can combine different - color LEDSs so go with fixed colors until the kernel gives us - enough information */ for (int i = 0; i <= FBD_FEEDBACK_LED_COLOR_LAST; i++) { g_autofree char *color = NULL; g_autofree char *enum_name = NULL; @@ -94,7 +88,8 @@ fbd_dev_led_probe_default (FbdDevLed *led, GError **error) static gboolean fbd_dev_led_set_color_default (FbdDevLed *led, - FbdFeedbackLedColor color) + FbdFeedbackLedColor color, + FbdLedRgbColor *rgb) { /* Ignore setting color as we have a single color only */ return TRUE; @@ -270,9 +265,9 @@ fbd_dev_led_set_brightness (FbdDevLed *led, guint brightness) gboolean -fbd_dev_led_start_periodic (FbdDevLed *led, - guint max_brightness_percentage, - guint freq) +fbd_dev_led_start_periodic (FbdDevLed *led, + guint max_brightness_percentage, + guint freq) { FbdDevLedClass *fbd_dev_led_class = FBD_DEV_LED_GET_CLASS (led); @@ -283,14 +278,15 @@ fbd_dev_led_start_periodic (FbdDevLed *led, gboolean -fbd_dev_led_set_color (FbdDevLed *led, - FbdFeedbackLedColor color) +fbd_dev_led_set_color (FbdDevLed *led, + FbdFeedbackLedColor color, + FbdLedRgbColor *rgb) { FbdDevLedClass *fbd_dev_led_class = FBD_DEV_LED_GET_CLASS (led); g_return_val_if_fail (FBD_IS_DEV_LED (led), FALSE); - return fbd_dev_led_class->set_color (led, color); + return fbd_dev_led_class->set_color (led, color, rgb); } diff --git a/src/fbd-dev-led.h b/src/fbd-dev-led.h index 0aba548..0122ecd 100644 --- a/src/fbd-dev-led.h +++ b/src/fbd-dev-led.h @@ -20,10 +20,12 @@ G_DECLARE_DERIVABLE_TYPE (FbdDevLed, fbd_dev_led, FBD, DEV_LED, GObject) FbdDevLed *fbd_dev_led_new (GUdevDevice *dev, GError **err); gboolean fbd_dev_led_set_brightness (FbdDevLed *led, guint brightness); guint fbd_dev_led_get_max_brightness (FbdDevLed *led); -gboolean fbd_dev_led_set_color (FbdDevLed *led, FbdFeedbackLedColor color); -gboolean fbd_dev_led_start_periodic (FbdDevLed *led, - guint max_brightness_percentage, - guint freq); +gboolean fbd_dev_led_set_color (FbdDevLed *led, + FbdFeedbackLedColor color, + FbdLedRgbColor *rgb); +gboolean fbd_dev_led_start_periodic (FbdDevLed *led, + guint max_brightness_percentage, + guint freq); gboolean fbd_dev_led_supports_color (FbdDevLed *led, FbdFeedbackLedColor color); struct _FbdDevLedClass { @@ -34,7 +36,8 @@ struct _FbdDevLedClass { guint max_brightness_percentage, guint freq); gboolean (*set_color) (FbdDevLed *led, - FbdFeedbackLedColor color); + FbdFeedbackLedColor color, + FbdLedRgbColor *rgb); gboolean (*supports_color) (FbdDevLed *led, FbdFeedbackLedColor color); }; diff --git a/src/fbd-dev-leds.c b/src/fbd-dev-leds.c index 418cf44..f0eb1df 100644 --- a/src/fbd-dev-leds.c +++ b/src/fbd-dev-leds.c @@ -180,15 +180,19 @@ fbd_dev_leds_new (GError **error) /** * fbd_dev_leds_start_periodic: * @self: The #FbdDevLeds - * @color: The color to use for the LED pattern + * @color: The color LED to use for the LED pattern + * @rgb: The rgb value to set (if `color` indicates an RGB led) * @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) +fbd_dev_leds_start_periodic (FbdDevLeds *self, + FbdFeedbackLedColor color, + FbdLedRgbColor *rgb, + guint max_brightness_percentage, + guint freq) { FbdDevLed *led; @@ -197,7 +201,7 @@ fbd_dev_leds_start_periodic (FbdDevLeds *self, FbdFeedbackLedColor color, led = find_led_by_color (self, color); g_return_val_if_fail (led, FALSE); - fbd_dev_led_set_color (led, color); + fbd_dev_led_set_color (led, color, rgb); return fbd_dev_led_start_periodic (led, max_brightness_percentage, freq); } diff --git a/src/fbd-dev-leds.h b/src/fbd-dev-leds.h index 999dbd9..6387882 100644 --- a/src/fbd-dev-leds.h +++ b/src/fbd-dev-leds.h @@ -17,9 +17,11 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (FbdDevLeds, fbd_dev_leds, FBD, DEV_LEDS, GObject); FbdDevLeds *fbd_dev_leds_new (GError **error); -gboolean fbd_dev_leds_start_periodic (FbdDevLeds *self, - FbdFeedbackLedColor color, - guint max_brighness, guint freq); +gboolean fbd_dev_leds_start_periodic (FbdDevLeds *self, + FbdFeedbackLedColor color, + FbdLedRgbColor *rgb, + guint max_brighness, + guint freq); gboolean fbd_dev_leds_stop (FbdDevLeds *self, FbdFeedbackLedColor color); diff --git a/src/fbd-feedback-led.c b/src/fbd-feedback-led.c index 4cae94c..dc297b7 100644 --- a/src/fbd-feedback-led.c +++ b/src/fbd-feedback-led.c @@ -13,6 +13,8 @@ #include "fbd-feedback-led.h" #include "fbd-feedback-manager.h" +#include + /** * FbdFeedbackLed: * @@ -41,21 +43,110 @@ typedef struct _FbdFeedbackLed { G_DEFINE_TYPE (FbdFeedbackLed, fbd_feedback_led, FBD_TYPE_FEEDBACK_BASE) +/** + * ascii_to_int: + * @c: a character + * + * Interprets a character as digit. E.g. '1' will be converted to 1, + * 'C' or 'c' will be converted to 12. + * + * Returns: The integer value or -1 if the character isn't a hex digit. + */ +static int +hex_ascii_to_int (char c) +{ + switch (c) { + case '0'...'9': + return c - '0'; + break; + case 'a'...'f': + return 10 + c - 'a'; + break; + case 'A'...'F': + return 10 + c - 'A'; + break; + default: + return -1; + } +} + + +static gboolean +parse_hex_color (const char *color, FbdLedRgbColor *rgb) +{ + FbdLedRgbColor parsed = { 0 }; + + if (color[0] != '#') + return FALSE; + + if (strlen (color) != strlen ("#11AA00")) + return FALSE; + + if (!rgb) + return TRUE; + + for (int i = 1; color[i]; i++) { + int c = hex_ascii_to_int (color[i]); + + if (c < 0) + return FALSE; + + switch (i) { + case 1: + parsed.r |= hex_ascii_to_int (color[i]) << 4; + break; + case 2: + parsed.r |= hex_ascii_to_int (color[i]); + break; + case 3: + parsed.g |= hex_ascii_to_int (color[i]) << 4; + break; + case 4: + parsed.g |= hex_ascii_to_int (color[i]); + break; + case 5: + parsed.b |= hex_ascii_to_int (color[i]) << 4; + break; + case 6: + parsed.b |= hex_ascii_to_int (color[i]); + break; + } + } + + memcpy (rgb, &parsed, sizeof (FbdLedRgbColor)); + + return TRUE; +} + static FbdFeedbackLedColor -color_string_to_color (const char *color) +color_string_to_color (const char *color, FbdLedRgbColor *rgb) { + g_return_val_if_fail (!gm_str_is_null_or_empty (color), FBD_FEEDBACK_LED_COLOR_WHITE); - if (g_strcmp0 (color, "red") == 0) + if (g_strcmp0 (color, "red") == 0) { + if (rgb) + rgb->r = 255; return FBD_FEEDBACK_LED_COLOR_RED; - else if (g_strcmp0 (color, "green") == 0) + } else if (g_strcmp0 (color, "green") == 0) { + if (rgb) + rgb->g = 255; return FBD_FEEDBACK_LED_COLOR_GREEN; - else if (g_strcmp0 (color, "blue") == 0) + } else if (g_strcmp0 (color, "blue") == 0) { + if (rgb) + rgb->b = 255; return FBD_FEEDBACK_LED_COLOR_BLUE; - else if (g_strcmp0 (color, "white") == 0) + } else if (g_strcmp0 (color, "white") == 0) { + if (rgb) + rgb->r = rgb->g = rgb->b = 255; return FBD_FEEDBACK_LED_COLOR_WHITE; + } else if (parse_hex_color (color, rgb)) { + return FBD_FEEDBACK_LED_COLOR_RGB; + } g_warning_once ("Can't parse color '%s' using white", color); + if (rgb) + rgb->r = rgb->g = rgb->b = 255; return FBD_FEEDBACK_LED_COLOR_WHITE; } @@ -123,14 +214,16 @@ fbd_feedback_led_run (FbdFeedbackBase *base) FbdFeedbackManager *manager = fbd_feedback_manager_get_default (); FbdDevLeds *dev = fbd_feedback_manager_get_dev_leds (manager); FbdFeedbackLedColor color; + FbdLedRgbColor rgb; g_return_if_fail (FBD_IS_DEV_LEDS (dev)); - g_debug ("Periodic led feedback: self->max_brightness, self->frequency"); + g_debug ("Periodic led feedback: max brightness: %d, freq: %d", self->max_brightness, self->frequency); - color = color_string_to_color (self->color); + color = color_string_to_color (self->color, &rgb); /* FIXME: handle priority */ fbd_dev_leds_start_periodic (dev, color, + &rgb, self->max_brightness, self->frequency); } @@ -144,7 +237,7 @@ fbd_feedback_led_end (FbdFeedbackBase *base) FbdDevLeds *dev = fbd_feedback_manager_get_dev_leds (manager); FbdFeedbackLedColor color; - color = color_string_to_color (self->color); + color = color_string_to_color (self->color, NULL); if (dev) fbd_dev_leds_stop (dev, color); fbd_feedback_base_done (FBD_FEEDBACK_BASE (self)); diff --git a/src/fbd-feedback-led.h b/src/fbd-feedback-led.h index 8a6fe91..f67af6c 100644 --- a/src/fbd-feedback-led.h +++ b/src/fbd-feedback-led.h @@ -28,6 +28,10 @@ typedef enum _FbdFeedbackLedColor { FBD_FEEDBACK_LED_COLOR_LAST = FBD_FEEDBACK_LED_COLOR_RGB, } FbdFeedbackLedColor; +typedef struct { + guint r, g, b; +} FbdLedRgbColor; + #define FBD_TYPE_FEEDBACK_LED (fbd_feedback_led_get_type ()) G_DECLARE_FINAL_TYPE (FbdFeedbackLed, fbd_feedback_led, FBD, FEEDBACK_LED, FbdFeedbackBase); diff --git a/tests/meson.build b/tests/meson.build index a8672a3..509a663 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -78,6 +78,7 @@ test_fbd_deps = [ # HW independent tests fbd_tests = [ + 'fbd-feedback-led', 'fbd-feedback-profile', 'fbd-feedback-theme', 'fbd-event', diff --git a/tests/test-fbd-feedback-led.c b/tests/test-fbd-feedback-led.c new file mode 100644 index 0000000..2b7bb0e --- /dev/null +++ b/tests/test-fbd-feedback-led.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 The Phosh Developers + * + * SPDX-License-Identifier: GPL-3.0+ + * + * Author: Guido Günther + */ + +#define GMOBILE_USE_UNSTABLE_API +#include "fbd-feedback-led.c" + + +static void +test_fbd_feedback_led_parse_color (void) +{ + FbdLedRgbColor rgb = { 0 }; + + g_assert_true (parse_hex_color ("#11aaBB", &rgb)); + g_assert_cmpint (rgb.r, ==, 0x11); + g_assert_cmpint (rgb.g, ==, 0xaa); + g_assert_cmpint (rgb.b, ==, 0xbb); + + g_assert_true (parse_hex_color ("#000000", &rgb)); + g_assert_cmpint (rgb.r, ==, 0x00); + g_assert_cmpint (rgb.g, ==, 0x00); + g_assert_cmpint (rgb.b, ==, 0x00); + + g_assert_false (parse_hex_color ("#1bc", &rgb)); + g_assert_false (parse_hex_color ("11aaBB", &rgb)); + g_assert_false (parse_hex_color ("", &rgb)); + g_assert_false (parse_hex_color ("#", &rgb)); + g_assert_false (parse_hex_color ("#QWERTY", &rgb)); +} + + +static void +test_fbd_feedback_led_color_string_to_color (void) +{ + FbdLedRgbColor rgb = { 0 }; + + g_assert_cmpint (color_string_to_color ("red", NULL), ==, FBD_FEEDBACK_LED_COLOR_RED); + + g_assert_cmpint (color_string_to_color ("#11aaBB", &rgb), ==, FBD_FEEDBACK_LED_COLOR_RGB); + g_assert_cmpint (rgb.r, ==, 0x11); + g_assert_cmpint (rgb.g, ==, 0xaa); + g_assert_cmpint (rgb.b, ==, 0xbb); + + g_assert_cmpint (color_string_to_color ("#00FF00", &rgb), ==, FBD_FEEDBACK_LED_COLOR_RGB); + g_assert_cmpint (rgb.r, ==, 0x00); + g_assert_cmpint (rgb.g, ==, 0xFF); + g_assert_cmpint (rgb.b, ==, 0x00); +} + + +gint +main (gint argc, gchar *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func("/feedbackd/fbd/feedback-led/parse-color", test_fbd_feedback_led_parse_color); + g_test_add_func("/feedbackd/fbd/feedback-led/color-string-to-color", + test_fbd_feedback_led_color_string_to_color); + + return g_test_run(); +}