From 5dcdc3c652a93712f5b7ecde093c87bfa47343e0 Mon Sep 17 00:00:00 2001 From: Jente Hidskes Date: Mon, 26 Jun 2017 00:29:10 +0200 Subject: [PATCH] Add an API for LEDs --- README.md | 16 +++ meson.build | 1 + src/ratbagd-device.c | 18 +++ src/ratbagd-led.c | 271 ++++++++++++++++++++++++++++++++++++++++++ src/ratbagd-profile.c | 174 ++++++++++++++++++++++++++- src/ratbagd.h | 20 ++++ 6 files changed, 498 insertions(+), 2 deletions(-) create mode 100644 src/ratbagd-led.c diff --git a/README.md b/README.md index 3ddb81ec..d9f54dae 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Interfaces: - org.freedesktop.ratbag1.Profile - org.freedesktop.ratbag1.Resolution - org.freedesktop.ratbag1.Button +- org.freedesktop.ratbag1.Led The **org.freedesktop.ratbag1.Manager** interface provides: - Properties: @@ -48,6 +49,7 @@ The **org.freedesktop.ratbag1.Profile** interface provides: - Index -> index of the profile - Resolutions -> array of object paths with interface Resolution - Buttons -> array of object paths with interface Button + - Leds -> array of object paths with interface Led - ActiveResolution -> index of the currently active resolution in Resolutions - DefaultResolution -> index of the default resolution in Resolutions - Methods: @@ -86,6 +88,20 @@ The **org.freedesktop.ratbag1.Button** interface provides: - SetKeyMapping(uint[]) -> set the key mapping, first entry is the keycode, other entries, if any, are modifier keycodes - Disable(void) -> disable this button +The **org.freedesktop.ratbag1.Led** interface provides: +- Properties: + - Index -> index of the LED + - Mode -> uint mapping to the mode enum from libratbag + - Type -> string describing the LED type + - Color -> uint triplet (RGB) of the LED's color + - EffectRate -> the effect rate in Hz, possible values are in the range 100 - 20000 + - Brightness -> the brightness of the LED, possible values are in the range 0 - 255 +- Methods: + - SetMode(uint) -> set the mode to the given mode enum value from libratbag + - SetColor((uuu)) -> set the color to the given uint triplet (RGB) + - SetEffectRate(uint) -> set the effect rate in Hz, possible values are in the range 100 - 20000 + - SetBrightness(int) -> set the brightness, possible values are in the range 0 - 255 + For easier debugging, objects paths are constructed from the device. e.g. `/org/freedesktop/ratbag/button/event5/p0/b10` is the button interface for button 10 on profile 0 on event5. The naming is subject to change. Do not diff --git a/meson.build b/meson.build index 63176cb3..44f9d30c 100644 --- a/meson.build +++ b/meson.build @@ -78,6 +78,7 @@ dep_libshared = declare_dependency(link_with: lib_libshared) src_ratbagd = [ 'src/ratbagd.h', 'src/ratbagd.c', + 'src/ratbagd-led.c', 'src/ratbagd-button.c', 'src/ratbagd-device.c', 'src/ratbagd-profile.c', diff --git a/src/ratbagd-device.c b/src/ratbagd-device.c index 096aead1..3191604c 100644 --- a/src/ratbagd-device.c +++ b/src/ratbagd-device.c @@ -427,6 +427,12 @@ unsigned int ratbagd_device_get_num_buttons(struct ratbagd_device *device) return ratbag_device_get_num_buttons(device->lib_device); } +unsigned int ratbagd_device_get_num_leds(struct ratbagd_device *device) +{ + assert(device); + return ratbag_device_get_num_leds(device->lib_device); +} + bool ratbagd_device_linked(struct ratbagd_device *device) { return device && rbnode_linked(&device->node); @@ -502,6 +508,18 @@ void ratbagd_device_link(struct ratbagd_device *device) r = ratbagd_profile_register_buttons(device->ctx->bus, device, device->profiles[i]); + if (r < 0) { + log_error("Cannot register buttons for '%s': %m\n", + device->name); + } + + r = ratbagd_profile_register_leds(device->ctx->bus, + device, + device->profiles[i]); + if (r < 0) { + log_error("Cannot register leds for '%s': %m\n", + device->name); + } } } diff --git a/src/ratbagd-led.c b/src/ratbagd-led.c new file mode 100644 index 00000000..c0c958e6 --- /dev/null +++ b/src/ratbagd-led.c @@ -0,0 +1,271 @@ +/*** + This file is part of ratbagd. + + Copyright 2017 Jente Hidskes . + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +***/ + +#include "config.h" + +#include +#include +#include +#include +#include "ratbagd.h" +#include "shared-macro.h" + +struct ratbagd_led { + struct ratbag_led *lib_led; + unsigned int index; + char *path; +}; + +static int ratbagd_led_get_mode(sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_led *led = userdata; + enum ratbag_led_mode mode; + + mode = ratbag_led_get_mode(led->lib_led); + return sd_bus_message_append(reply, "u", mode); +} + +static int ratbagd_led_set_mode(sd_bus_message *m, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_led *led = userdata; + enum ratbag_led_mode mode; + int r; + + r = sd_bus_message_read(m, "u", &mode); + if (r < 0) + return r; + + r = ratbag_led_set_mode(led->lib_led, mode); + return sd_bus_reply_method_return(m, "u", r); +} + +static int ratbagd_led_get_type(sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_led *led = userdata; + const char *type = NULL; + enum ratbag_led_type t; + + t = ratbag_led_get_type(led->lib_led); + + switch(t) { + default: + log_error("Unknown led type %d\n", t); + /* fallthrough */ + case RATBAG_LED_TYPE_UNKNOWN: + type = "unknown"; + break; + case RATBAG_LED_TYPE_LOGO: + type = "logo"; + break; + case RATBAG_LED_TYPE_SIDE: + type = "side"; + break; + } + + return sd_bus_message_append(reply, "s", type); +} + +static int ratbagd_led_get_color(sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_led *led = userdata; + struct ratbag_color c; + + c = ratbag_led_get_color(led->lib_led); + return sd_bus_message_append(reply, "(uuu)", c.red, c.green, c.blue); +} + +static int ratbagd_led_set_color(sd_bus_message *m, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_led *led = userdata; + struct ratbag_color c; + int r; + + r = sd_bus_message_read(m, "(uuu)", &c.red, &c.green, &c.blue); + if (r < 0) + return r; + + r = ratbag_led_set_color(led->lib_led, c); + return sd_bus_reply_method_return(m, "u", r); +} + +static int ratbagd_led_get_effect_rate(sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_led *led = userdata; + int rate; + + rate = ratbag_led_get_effect_rate(led->lib_led); + return sd_bus_message_append(reply, "i", rate); +} + +static int ratbagd_led_set_effect_rate(sd_bus_message *m, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_led *led = userdata; + unsigned int rate; + int r; + + r = sd_bus_message_read(m, "u", &rate); + if (r < 0) + return r; + + if (rate < 100 || rate > 20000) + return 0; + + r = ratbag_led_set_effect_rate(led->lib_led, rate); + return sd_bus_reply_method_return(m, "u", r); +} + +static int ratbagd_led_get_brightness(sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_led *led = userdata; + unsigned int brightness; + + brightness = ratbag_led_get_brightness(led->lib_led); + return sd_bus_message_append(reply, "u", brightness); +} + +static int ratbagd_led_set_brightness(sd_bus_message *m, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_led *led = userdata; + unsigned int brightness; + int r; + + r = sd_bus_message_read(m, "u", &brightness); + if (r < 0) + return r; + + if (brightness > 255) + return 0; + + r = ratbag_led_set_brightness(led->lib_led, brightness); + return sd_bus_reply_method_return(m, "u", r); +} + +const sd_bus_vtable ratbagd_led_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Index", "u", NULL, offsetof(struct ratbagd_led, index), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Mode", "u", ratbagd_led_get_mode, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Type", "s", ratbagd_led_get_type, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Color", "(uuu)", ratbagd_led_get_color, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("EffectRate", "i", ratbagd_led_get_effect_rate, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Brightness", "u", ratbagd_led_get_brightness, 0, SD_BUS_VTABLE_PROPERTY_CONST), + + SD_BUS_METHOD("SetMode", "u", "u", ratbagd_led_set_mode, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetColor", "(uuu)", "u", ratbagd_led_set_color, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetEffectRate", "u", "u", ratbagd_led_set_effect_rate, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetBrightness", "u", "u", ratbagd_led_set_brightness, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END, +}; + +int ratbagd_led_new(struct ratbagd_led **out, + struct ratbagd_device *device, + struct ratbagd_profile *profile, + struct ratbag_led *lib_led, + unsigned int index) +{ + _cleanup_(ratbagd_led_freep) struct ratbagd_led *led = NULL; + char profile_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1], + led_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1]; + int r; + + assert(out); + assert(lib_led); + + led = calloc(1, sizeof(*led)); + if (!led) + return -ENOMEM; + + led->lib_led = lib_led; + led->index = index; + + sprintf(profile_buffer, "p%u", ratbagd_profile_get_index(profile)); + sprintf(led_buffer, "b%u", index); + r = sd_bus_path_encode_many(&led->path, + "/org/freedesktop/ratbag1/led/%/%/%", + ratbagd_device_get_name(device), + profile_buffer, + led_buffer); + if (r < 0) + return r; + + *out = led; + led = NULL; + return 0; +} + +const char *ratbagd_led_get_path(struct ratbagd_led *led) +{ + assert(led); + return led->path; +} + +struct ratbagd_led *ratbagd_led_free(struct ratbagd_led *led) +{ + if (!led) + return NULL; + + led->path = mfree(led->path); + led->lib_led = ratbag_led_unref(led->lib_led); + + return mfree(led); +} + diff --git a/src/ratbagd-profile.c b/src/ratbagd-profile.c index e36e94b9..45c6fd35 100644 --- a/src/ratbagd-profile.c +++ b/src/ratbagd-profile.c @@ -51,6 +51,11 @@ struct ratbagd_profile { sd_bus_slot *button_enum_slot; unsigned int n_buttons; struct ratbagd_button **buttons; + + sd_bus_slot *led_vtable_slot; + sd_bus_slot *led_enum_slot; + unsigned int n_leds; + struct ratbagd_led **leds; }; static int ratbagd_profile_find_resolution(sd_bus *bus, @@ -148,6 +153,37 @@ static int ratbagd_profile_get_buttons(sd_bus *bus, return sd_bus_message_close_container(reply); } +static int ratbagd_profile_get_leds(sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) +{ + struct ratbagd_profile *profile = userdata; + struct ratbagd_led *led; + unsigned int i; + int r; + + r = sd_bus_message_open_container(reply, 'a', "o"); + if (r < 0) + return r; + + for (i = 0; i < profile->n_leds; ++i) { + led = profile->leds[i]; + if (!led) + continue; + + r = sd_bus_message_append(reply, + "o", + ratbagd_led_get_path(led)); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} static int ratbagd_profile_get_active_resolution(sd_bus *bus, const char *path, @@ -234,6 +270,37 @@ static int ratbagd_profile_find_button(sd_bus *bus, return 1; } +static int ratbagd_profile_find_led(sd_bus *bus, + const char *path, + const char *interface, + void *userdata, + void **found, + sd_bus_error *error) +{ + _cleanup_(freep) char *name = NULL; + struct ratbagd_profile *profile = userdata; + unsigned int index; + int r; + + r = sd_bus_path_decode_many(path, + "/org/freedesktop/ratbag1/led/%/p%/b%", + NULL, + NULL, + &name); + if (r <= 0) + return r; + + r = safe_atou(name, &index); + if (r < 0) + return 0; + + if (index >= profile->n_leds || !profile->leds[index]) + return 0; + + *found = profile->leds[index]; + return 1; +} + static int ratbagd_profile_set_active(sd_bus_message *m, void *userdata, sd_bus_error *error) @@ -282,6 +349,7 @@ const sd_bus_vtable ratbagd_profile_vtable[] = { SD_BUS_PROPERTY("Index", "u", NULL, offsetof(struct ratbagd_profile, index), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Resolutions", "ao", ratbagd_profile_get_resolutions, 0, 0), SD_BUS_PROPERTY("Buttons", "ao", ratbagd_profile_get_buttons, 0, 0), + SD_BUS_PROPERTY("Leds", "ao", ratbagd_profile_get_leds, 0, 0), SD_BUS_PROPERTY("ActiveResolution", "u", ratbagd_profile_get_active_resolution, 0, 0), SD_BUS_PROPERTY("DefaultResolution", "u", ratbagd_profile_get_default_resolution, 0, 0), SD_BUS_METHOD("SetActive", "", "u", ratbagd_profile_set_active, SD_BUS_VTABLE_UNPRIVILEGED), @@ -298,6 +366,7 @@ int ratbagd_profile_new(struct ratbagd_profile **out, _cleanup_(ratbagd_profile_freep) struct ratbagd_profile *profile = NULL; struct ratbag_resolution *resolution; struct ratbag_button *button; + struct ratbag_led *led; char index_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1]; unsigned int i; int r; @@ -331,6 +400,11 @@ int ratbagd_profile_new(struct ratbagd_profile **out, if (!profile->buttons) return -ENOMEM; + profile->n_leds = ratbagd_device_get_num_leds(device); + profile->leds = calloc(profile->n_leds, sizeof(*profile->leds)); + if (!profile->leds) + return -ENOMEM; + for (i = 0; i < profile->n_resolutions; ++i) { resolution = ratbag_profile_get_resolution(profile->lib_profile, i); if (!resolution) @@ -365,6 +439,23 @@ int ratbagd_profile_new(struct ratbagd_profile **out, } } + for (i = 0; i < profile->n_leds; ++i) { + led = ratbag_profile_get_led(profile->lib_profile, i); + if (!led) + continue; + + r = ratbagd_led_new(&profile->leds[i], + device, + profile, + led, + i); + if (r < 0) { + errno = -r; + log_error("Cannot allocate led for '%s': %m\n", + ratbagd_device_get_name(device)); + } + } + *out = profile; profile = NULL; return 0; @@ -381,12 +472,17 @@ struct ratbagd_profile *ratbagd_profile_free(struct ratbagd_profile *profile) profile->resolution_enum_slot = sd_bus_slot_unref(profile->resolution_enum_slot); profile->button_vtable_slot = sd_bus_slot_unref(profile->button_vtable_slot); profile->button_enum_slot = sd_bus_slot_unref(profile->button_enum_slot); + profile->led_vtable_slot = sd_bus_slot_unref(profile->led_vtable_slot); + profile->led_enum_slot = sd_bus_slot_unref(profile->led_enum_slot); + for (i = 0; i< profile->n_leds; ++i) + ratbagd_led_free(profile->leds[i]); for (i = 0; i< profile->n_buttons; ++i) ratbagd_button_free(profile->buttons[i]); for (i = 0; i< profile->n_resolutions; ++i) ratbagd_resolution_free(profile->resolutions[i]); + mfree(profile->leds); mfree(profile->buttons); mfree(profile->resolutions); @@ -467,7 +563,6 @@ int ratbagd_profile_register_resolutions(struct sd_bus *bus, ratbagd_device_get_name(device), index_buffer); - if (r >= 0) { r = sd_bus_add_fallback_vtable(bus, &profile->resolution_vtable_slot, @@ -544,7 +639,6 @@ int ratbagd_profile_register_buttons(struct sd_bus *bus, ratbagd_device_get_name(device), index_buffer); - if (r >= 0) { r = sd_bus_add_fallback_vtable(bus, &profile->button_vtable_slot, @@ -568,3 +662,79 @@ int ratbagd_profile_register_buttons(struct sd_bus *bus, return 0; } + +static int ratbagd_profile_list_leds(sd_bus *bus, + const char *path, + void *userdata, + char ***paths, + sd_bus_error *error) +{ + struct ratbagd_profile *profile = userdata; + struct ratbagd_led *led; + char **leds; + unsigned int i; + + leds = calloc(profile->n_leds + 1, sizeof(char *)); + if (!leds) + return -ENOMEM; + + for (i = 0; i < profile->n_leds; ++i) { + led = profile->leds[i]; + if (!led) + continue; + + leds[i] = strdup(ratbagd_led_get_path(led)); + if (!leds[i]) + goto error; + } + + leds[i] = NULL; + *paths = leds; + return 1; + +error: + for (i = 0; leds[i]; ++i) + free(leds[i]); + free(leds); + return -ENOMEM; +} + +int ratbagd_profile_register_leds(struct sd_bus *bus, + struct ratbagd_device *device, + struct ratbagd_profile *profile) +{ + _cleanup_(freep) char *prefix = NULL; + char index_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1]; + int r; + + sprintf(index_buffer, "p%u", profile->index); + + /* register led interfaces */ + r = sd_bus_path_encode_many(&prefix, + "/org/freedesktop/ratbag1/led/%/%", + ratbagd_device_get_name(device), + index_buffer); + + if (r >= 0) { + r = sd_bus_add_fallback_vtable(bus, + &profile->led_vtable_slot, + prefix, + "org.freedesktop.ratbag1.Led", + ratbagd_led_vtable, + ratbagd_profile_find_led, + profile); + if (r >= 0) + r = sd_bus_add_node_enumerator(bus, + &profile->led_enum_slot, + prefix, + ratbagd_profile_list_leds, + profile); + } + if (r < 0) { + errno = -r; + log_error("Cannot register leds for '%s': %m\n", + ratbagd_device_get_name(device)); + } + + return 0; +} diff --git a/src/ratbagd.h b/src/ratbagd.h index 32e27b8c..7a70f2af 100644 --- a/src/ratbagd.h +++ b/src/ratbagd.h @@ -43,6 +43,7 @@ struct ratbagd_device; struct ratbagd_profile; struct ratbagd_resolution; struct ratbagd_button; +struct ratbagd_led; void log_verbose(const char *fmt, ...) _printf_(1, 2); void log_error(const char *fmt, ...) _printf_(1, 2); @@ -68,6 +69,9 @@ int ratbagd_profile_register_resolutions(struct sd_bus *bus, int ratbagd_profile_register_buttons(struct sd_bus *bus, struct ratbagd_device *device, struct ratbagd_profile *profile); +int ratbagd_profile_register_leds(struct sd_bus *bus, + struct ratbagd_device *device, + struct ratbagd_profile *profile); DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_profile *, ratbagd_profile_free); @@ -103,6 +107,21 @@ const char *ratbagd_button_get_path(struct ratbagd_button *button); DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_button *, ratbagd_button_free); +/* + * Leds + */ +extern const sd_bus_vtable ratbagd_led_vtable[]; + +int ratbagd_led_new(struct ratbagd_led **out, + struct ratbagd_device *device, + struct ratbagd_profile *profile, + struct ratbag_led *lib_led, + unsigned int index); +struct ratbagd_led *ratbagd_led_free(struct ratbagd_led *led); +const char *ratbagd_led_get_path(struct ratbagd_led *led); + +DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_led *, ratbagd_led_free); + /* * Devices */ @@ -117,6 +136,7 @@ struct ratbagd_device *ratbagd_device_free(struct ratbagd_device *device); const char *ratbagd_device_get_name(struct ratbagd_device *device); const char *ratbagd_device_get_path(struct ratbagd_device *device); unsigned int ratbagd_device_get_num_buttons(struct ratbagd_device *device); +unsigned int ratbagd_device_get_num_leds(struct ratbagd_device *device); bool ratbagd_device_linked(struct ratbagd_device *device); void ratbagd_device_link(struct ratbagd_device *device);