1
0
forked from drath/libratbag

Add the ability to set LED effects

This commit is contained in:
Ilya Guterman
2017-02-17 08:39:12 +02:00
committed by Peter Hutterer
parent 3439058a54
commit 0c0a6d39cb
7 changed files with 623 additions and 2 deletions

View File

@@ -122,6 +122,34 @@ test_read_button(struct ratbag_button *button)
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO);
}
static void
test_read_led(struct ratbag_led *led)
{
struct ratbag_device *device = led->profile->device;
struct ratbag_test_device *d = ratbag_get_drv_data(device);
struct ratbag_test_profile *p = &d->profiles[led->profile->index];
struct ratbag_test_led t_led = p->leds[led->index];
switch (t_led.mode) {
case RATBAG_LED_ON:
led->mode = RATBAG_LED_ON;
break;
case RATBAG_LED_CYCLE:
led->mode = RATBAG_LED_CYCLE;
break;
case RATBAG_LED_BREATHING:
led->mode = RATBAG_LED_BREATHING;
break;
default:
led->mode = RATBAG_LED_OFF;
}
led->color.red = t_led.color.red;
led->color.green = t_led.color.green;
led->color.blue = t_led.color.blue;
led->hz = t_led.hz;
led->brightness = t_led.brightness;
}
static int
test_write_button(struct ratbag_button *button,
const struct ratbag_button_action *action)
@@ -131,6 +159,17 @@ test_write_button(struct ratbag_button *button,
return 0;
}
static int
test_write_led(struct ratbag_led *led,
enum ratbag_led_mode mode,
struct ratbag_color color, unsigned int hz,
unsigned int brightness)
{
/* check if the device is still valid */
assert(ratbag_get_drv_data(led->profile->device) != NULL);
return 0;
}
static int
test_fake_probe(struct ratbag_device *device)
{
@@ -176,6 +215,8 @@ struct ratbag_driver test_driver = {
.write_profile = test_write_profile,
.set_active_profile = test_set_active_profile,
.read_button = test_read_button,
.read_led = test_read_led,
.write_button = test_write_button,
.write_resolution_dpi = NULL,
.write_led = test_write_led,
};

View File

@@ -198,6 +198,21 @@ struct ratbag_driver {
int (*write_resolution_dpi)(struct ratbag_resolution *resolution,
int dpi_x, int dpi_y);
/**
* For the given led, fill in the struct ratbag_led
* with the available information.
*/
void (*read_led)(struct ratbag_led *led);
/*
* FIXME: This function is deprecated and should be removed. Once
* we've updated all the device drivers to stop using it we'll remove
* it. Look at commit() instead.
*/
int (*write_led)(struct ratbag_led *led, enum ratbag_led_mode mode,
struct ratbag_color color, unsigned int hz,
unsigned int brightness);
/* private */
int (*test_probe)(struct ratbag_device *device, void *data);
@@ -216,6 +231,20 @@ struct ratbag_resolution {
uint32_t capabilities;
};
struct ratbag_led {
int refcount;
void *userdata;
struct list link;
struct ratbag_profile *profile;
unsigned index;
enum ratbag_led_type type;
enum ratbag_led_mode mode;
struct ratbag_color color;
unsigned int hz; /**< rate of action in hz */
unsigned int brightness; /**< brightness of the LED */
bool dirty;
};
struct ratbag_profile {
int refcount;
void *userdata;
@@ -230,6 +259,7 @@ struct ratbag_profile {
struct ratbag_resolution *modes;
unsigned int num_modes;
} resolution;
struct list leds;
bool is_active; /**< profile is the currently active one */
bool is_enabled;

View File

@@ -49,9 +49,24 @@ struct ratbag_test_resolution {
uint32_t caps;
};
struct ratbag_test_color {
unsigned short red;
unsigned short green;
unsigned short blue;
};
struct ratbag_test_led {
enum ratbag_led_type type;
enum ratbag_led_mode mode;
struct ratbag_test_color color;
unsigned int hz;
unsigned int brightness;
};
struct ratbag_test_profile {
struct ratbag_test_button buttons[RATBAG_TEST_MAX_BUTTONS];
struct ratbag_test_resolution resolutions[RATBAG_TEST_MAX_RESOLUTIONS];
struct ratbag_test_led leds[RATBAG_TEST_MAX_LEDS];
bool active;
bool dflt;
};

View File

@@ -59,6 +59,8 @@ static void
ratbag_profile_destroy(struct ratbag_profile *profile);
static void
ratbag_button_destroy(struct ratbag_button *button);
static void
ratbag_led_destroy(struct ratbag_led *led);
static void
ratbag_default_log_func(struct ratbag *ratbag,
@@ -582,6 +584,25 @@ ratbag_create_button(struct ratbag_profile *profile, unsigned int index)
return button;
}
static struct ratbag_led *
ratbag_create_led(struct ratbag_profile *profile, unsigned int index)
{
struct ratbag_device *device = profile->device;
struct ratbag_led *led;
led = zalloc(sizeof(*led));
led->refcount = 0;
led->profile = profile;
led->index = index;
list_insert(&profile->leds, &led->link);
if (device->driver->read_led)
device->driver->read_led(led);
return led;
}
static int
ratbag_profile_init_buttons(struct ratbag_profile *profile, unsigned int count)
{
@@ -598,6 +619,11 @@ ratbag_profile_init_buttons(struct ratbag_profile *profile, unsigned int count)
static int
ratbag_profile_init_leds(struct ratbag_profile *profile, unsigned int count)
{
unsigned int i;
for (i = 0; i < count; i++)
ratbag_create_led(profile, i);
profile->device->num_leds = count;
return 0;
@@ -624,6 +650,7 @@ ratbag_create_profile(struct ratbag_device *device,
list_insert(&device->profiles, &profile->link);
list_init(&profile->buttons);
list_init(&profile->leds);
for (i = 0; i < num_resolutions; i++)
ratbag_resolution_init(profile, i, 0, 0, 0);
@@ -682,14 +709,18 @@ ratbag_profile_ref(struct ratbag_profile *profile)
static void
ratbag_profile_destroy(struct ratbag_profile *profile)
{
struct ratbag_button *button, *next;
struct ratbag_button *button, *b_next;
struct ratbag_led *led, *l_next;
/* if we get to the point where the profile is destroyed, buttons,
* resolutions , etc. are at a refcount of 0, so we can destroy
* everything */
list_for_each_safe(button, next, &profile->buttons, link)
list_for_each_safe(button, b_next, &profile->buttons, link)
ratbag_button_destroy(button);
list_for_each_safe(led, l_next, &profile->leds, link)
ratbag_led_destroy(led);
free(profile->resolution.modes);
list_remove(&profile->link);
@@ -790,6 +821,12 @@ ratbag_device_has_capability(const struct ratbag_device *device,
return !!(device->capabilities & (1UL << cap));
}
static inline enum ratbag_error_code
write_led_helper(struct ratbag_device *device, struct ratbag_led *led)
{
return device->driver->write_led(led, led->mode, led->color, led->hz, led->brightness);
}
/* FIXME: This is a temporary fix for all of the drivers that have yet to be
* converted to the new profile-oriented API. Once all of the drivers have been
* converted, this code should be removed.
@@ -799,6 +836,7 @@ ratbag_old_write_profile(struct ratbag_device *device)
{
struct ratbag_profile *profile;
struct ratbag_button *button;
struct ratbag_led *led;
struct ratbag_resolution *resolution;
int rc;
unsigned int i;
@@ -839,6 +877,17 @@ ratbag_old_write_profile(struct ratbag_device *device)
return RATBAG_ERROR_DEVICE;
}
}
if (device->driver->write_led) {
list_for_each(led, &profile->leds, link) {
if (!led->dirty)
continue;
rc = write_led_helper(device, led);
if (rc)
return RATBAG_ERROR_DEVICE;
}
}
}
return RATBAG_SUCCESS;
@@ -1268,6 +1317,16 @@ ratbag_button_ref(struct ratbag_button *button)
return button;
}
LIBRATBAG_EXPORT struct ratbag_led *
ratbag_led_ref(struct ratbag_led *led)
{
assert(led->refcount < INT_MAX);
ratbag_profile_ref(led->profile);
led->refcount++;
return led;
}
static void
ratbag_button_destroy(struct ratbag_button *button)
{
@@ -1280,6 +1339,13 @@ ratbag_button_destroy(struct ratbag_button *button)
free(button);
}
static void
ratbag_led_destroy(struct ratbag_led *led)
{
list_remove(&led->link);
free(led);
}
LIBRATBAG_EXPORT struct ratbag_button *
ratbag_button_unref(struct ratbag_button *button)
{
@@ -1294,6 +1360,126 @@ ratbag_button_unref(struct ratbag_button *button)
return NULL;
}
LIBRATBAG_EXPORT struct ratbag_led *
ratbag_led_unref(struct ratbag_led *led)
{
if (led == NULL)
return NULL;
assert(led->refcount > 0);
led->refcount--;
ratbag_profile_unref(led->profile);
return NULL;
}
LIBRATBAG_EXPORT enum ratbag_led_mode
ratbag_led_get_mode(struct ratbag_led *led)
{
return led->mode;
}
LIBRATBAG_EXPORT enum ratbag_led_type
ratbag_led_get_type(struct ratbag_led *led)
{
return led->type;
}
LIBRATBAG_EXPORT struct ratbag_color
ratbag_led_get_color(struct ratbag_led *led)
{
return led->color;
}
LIBRATBAG_EXPORT int
ratbag_led_get_effect_rate(struct ratbag_led *led)
{
return led->hz;
}
LIBRATBAG_EXPORT unsigned int
ratbag_led_get_brightness(struct ratbag_led *led)
{
return led->brightness;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_led_set_mode(struct ratbag_led *led, enum ratbag_led_mode mode)
{
if (!ratbag_device_has_capability(led->profile->device,
RATBAG_DEVICE_CAP_LED))
return RATBAG_ERROR_CAPABILITY;
led->mode = mode;
led->dirty = true;
led->profile->dirty = true;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_led_set_color(struct ratbag_led *led, struct ratbag_color color)
{
if (!ratbag_device_has_capability(led->profile->device,
RATBAG_DEVICE_CAP_LED))
return RATBAG_ERROR_CAPABILITY;
led->color = color;
led->dirty = true;
led->profile->dirty = true;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_led_set_effect_rate(struct ratbag_led *led, unsigned int hz)
{
if (!ratbag_device_has_capability(led->profile->device,
RATBAG_DEVICE_CAP_LED))
return RATBAG_ERROR_CAPABILITY;
led->hz = hz;
led->dirty = true;
led->profile->dirty = true;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_led_set_brightness(struct ratbag_led *led, unsigned int brightness)
{
if (!ratbag_device_has_capability(led->profile->device,
RATBAG_DEVICE_CAP_LED))
return RATBAG_ERROR_CAPABILITY;
led->brightness = brightness;
led->dirty = true;
led->profile->dirty = true;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT struct ratbag_led *
ratbag_profile_get_led(struct ratbag_profile *profile,
unsigned int index)
{
struct ratbag_device *device = profile->device;
struct ratbag_led *led;
if (index >= ratbag_device_get_num_leds(device)) {
log_bug_client(device->ratbag, "Requested invalid led %d\n", index);
return NULL;
}
list_for_each(led, &profile->leds, link) {
if (led->index == index)
return ratbag_led_ref(led);
}
log_bug_libratbag(device->ratbag, "Led %d, profile %d not found\n",
index, profile->index);
return NULL;
}
LIBRATBAG_EXPORT void
ratbag_set_user_data(struct ratbag *ratbag, void *userdata)
{

View File

@@ -55,6 +55,8 @@ extern "C" {
*
* @defgroup button Button configuration
*
* @defgroup led LED configuration
*
* @defgroup resolution Resolution and frequency mappings
*
* A device's sensor resolution and report rate can be configured per
@@ -126,6 +128,28 @@ struct ratbag_button;
*/
struct ratbag_resolution;
/**
* @ingroup led
* @struct ratbag_color
*
* Represents LED color in RGB format.
* each color component is integer 0 - 255
*/
struct ratbag_color {
unsigned int red;
unsigned int green;
unsigned int blue;
};
/**
* @ingroup led
* @struct ratbag_led
*
* Represents a led on the device.
*/
struct ratbag_led;
/**
* @ingroup button
* @struct ratbag_macro
@@ -1317,6 +1341,23 @@ ratbag_button_set_button(struct ratbag_button *button,
enum ratbag_button_action_special
ratbag_button_get_special(struct ratbag_button *button);
/**
* @ingroup led
*
* RATBAG_LED_OFF - led is now off,
* RATBAG_LED_ON - led is on with static color,
* RATBAG_LED_CYCLE - led is cycling between all colors.
* RATBAG_LED_BREATHING - led is pulsating with static color
*
* Each LED mode has different properties, e.g. the brightness and rate are only
* available in modes @ref RATBAG_LED_CYCLE and @ref RATBAG_LED_BREATHING modes
*/
enum ratbag_led_mode {
RATBAG_LED_OFF = 0,
RATBAG_LED_ON,
RATBAG_LED_CYCLE,
RATBAG_LED_BREATHING,
};
/**
* @ingroup led
*
@@ -1327,6 +1368,145 @@ enum ratbag_led_type {
RATBAG_LED_TYPE_LOGO = 0,
RATBAG_LED_TYPE_SIDE
};
/**
* @ingroup led
*
* Return a reference to the LED given by the index. The order of the
* LEDs is device-specific though.
*
* The LED is refcounted with an initial value of at least 1.
* Use ratbag_led_unref() to release the LED.
*
* @param profile A previously initialized ratbag profile
* @param index The index of the LED
*
* @return A LED context, or NULL if the LED does not exist.
*
* @see ratbag_device_get_profile
*/
struct ratbag_led *
ratbag_profile_get_led(struct ratbag_profile *profile, unsigned int index);
/**
* @ingroup led
*
* This function returns the type for ratbag_led.
*
* @param led A previously initialized ratbag LED
* @return The LED type @ref enum ratbag_led_type
*
* @see ratbag_led_set_mode
*/
enum ratbag_led_type
ratbag_led_get_type(struct ratbag_led *led);
/**
* @ingroup led
*
* This function returns the mode for ratbag_led.
*
* @param led A previously initialized ratbag LED
* @return The LED mod @ref enum ratbag_led_mode
*
* @see ratbag_led_set_mode
*/
enum ratbag_led_mode
ratbag_led_get_mode(struct ratbag_led *led);
/**
* @ingroup led
*
* This function returns the led color.
*
* @param led A previously initialized ratbag LED
* @return The LED color in @ref enum ratbag_led_mode
*
* @see ratbag_led_set_color
*/
struct ratbag_color
ratbag_led_get_color(struct ratbag_led *led);
/**
* @ingroup led
*
* This function returns the LED effect rate.
*
* @param led A previously initialized ratbag LED
* @return The LED rate in Hz, can be 100 - 20000
*
* @see ratbag_led_set_effect_rate
*/
int
ratbag_led_get_effect_rate(struct ratbag_led *led);
/**
* @ingroup led
*
* This function returns the LED brightness.
*
* @param led A previously initialized ratbag LED
* @return The LED brightness 0 - 255
*
* @see ratbag_led_get_brightness
*/
unsigned int
ratbag_led_get_brightness(struct ratbag_led *led);
/**
* @ingroup led
*
* this function sets the LED mode.
*
* @param led A previously initialized ratbag LED
* @param mode LED mode @ref enum ratbag_led_mode.
* @return 0 on success or an error code otherwise.
*
* @see ratbag_led_get_mode
*/
enum ratbag_error_code
ratbag_led_set_mode(struct ratbag_led *led, enum ratbag_led_mode mode);
/**
* @ingroup led
*
* If the LED's mode is @ref RATBAG_LED_ON or @ref RATBAG_LED_BREATHING
* then this function sets the LED color, otherwise it has no effect.
*
* @param led A previously initialized ratbag LED
* @param color A LED color.
* @return 0 on success or an error code otherwise.
*
* @see ratbag_led_get_color
*/
enum ratbag_error_code
ratbag_led_set_color(struct ratbag_led *led, struct ratbag_color color);
/**
* @ingroup led
*
* If the LED's mode is @ref RATBAG_LED_CYCLE or @ref RATBAG_LED_BREATHING
* then this function sets the LED rate in Hz
*
* @param led A previously initialized ratbag LED
* @param rate Effect rate in hz, 100 - 20000
* @return 0 on success or an error code otherwise.
*
* @see ratbag_led_get_effect_rate
*/
enum ratbag_error_code
ratbag_led_set_effect_rate(struct ratbag_led *led, unsigned int rate);
/**
* @ingroup led
*
* If the LED's mode is @ref RATBAG_LED_CYCLE or @ref RATBAG_LED_BREATHING
* then this function sets the LED brightness, otherwise it has no effect.
*
* @param led A previously initialized ratbag LED
* @param brightness Effect brightness 0 - 255
* @return 0 on success or an error code otherwise.
*
* @see ratbag_led_get_brightness
*/
enum ratbag_error_code
ratbag_led_set_brightness(struct ratbag_led *led, unsigned int brightness);
/**
* @ingroup button
*
@@ -1610,6 +1790,31 @@ ratbag_button_ref(struct ratbag_button *button);
struct ratbag_button *
ratbag_button_unref(struct ratbag_button *button);
/**
* @ingroup led
*
* Add a reference to the led. A led is destroyed whenever the
* reference count reaches 0. See @ref ratbag_led_unref.
*
* @param led A previously initialized valid ratbag led
* @return The passed ratbag led
*/
struct ratbag_led *
ratbag_led_ref(struct ratbag_led *led);
/**
* @ingroup led
*
* Dereference the ratbag led. When the internal refcount reaches
* zero, all resources associated with this object are released. The object
* must be considered invalid once unref is called.
*
* @param led A previously initialized ratbag led
* @return Always NULL
*/
struct ratbag_led *
ratbag_led_unref(struct ratbag_led *led);
#ifdef __cplusplus
}
#endif

View File

@@ -45,6 +45,18 @@ global:
ratbag_log_get_priority;
ratbag_log_set_handler;
ratbag_log_set_priority;
ratbag_profile_get_led;
ratbag_led_get_mode;
ratbag_led_get_type;
ratbag_led_get_color;
ratbag_led_get_effect_rate;
ratbag_led_get_brightness;
ratbag_led_ref;
ratbag_led_set_mode;
ratbag_led_set_color;
ratbag_led_set_effect_rate;
ratbag_led_set_brightness;
ratbag_led_unref;
ratbag_device_get_num_leds;
ratbag_profile_get_button;
ratbag_profile_get_num_resolutions;

View File

@@ -79,6 +79,20 @@ const struct ratbag_test_device sane_device = {
{ .xres = 2200, .yres = 2300, .hz = 3000 },
{ .xres = 2300, .yres = 2400, .hz = 3000 },
},
.leds = {
{
.mode = RATBAG_LED_ON,
.color = { .red = 255, .green = 0, .blue = 0 },
.hz = 1,
.brightness = 20
},
{
.mode = RATBAG_LED_CYCLE,
.color = { .red = 255, .green = 255, .blue = 0 },
.hz = 3,
.brightness = 40
}
},
.active = false,
.dflt = false,
},
@@ -754,6 +768,119 @@ START_TEST(device_buttons_set)
}
END_TEST
static void
assert_led_equals(struct ratbag_led *l, struct ratbag_test_led e_l)
{
enum ratbag_led_mode mode;
struct ratbag_color color;
int brightness, hz;
ck_assert(l != NULL);
mode = ratbag_led_get_mode(l);
color = ratbag_led_get_color(l);
hz = ratbag_led_get_effect_rate(l);
brightness = ratbag_led_get_brightness(l);
ck_assert_int_eq(mode, e_l.mode);
ck_assert_int_eq(color.red, e_l.color.red);
ck_assert_int_eq(color.green, e_l.color.green);
ck_assert_int_eq(color.blue, e_l.color.blue);
ck_assert_int_eq(hz, e_l.hz);
ck_assert_int_eq(brightness, e_l.brightness);
}
START_TEST(device_leds)
{
struct ratbag *r;
struct ratbag_device *d;
struct ratbag_profile *p;
struct ratbag_led *led_logo, *led_side;
int nprofiles;
int device_freed_count = 0;
struct ratbag_test_device td = sane_device;
td.destroyed_data = &device_freed_count;
r = ratbag_create_context(&abort_iface, NULL);
d = ratbag_device_new_test_device(r, &td);
nprofiles = ratbag_device_get_num_profiles(d);
ck_assert_int_eq(nprofiles, 3);
p = ratbag_device_get_profile(d, 2);
ck_assert(p != NULL);
led_logo = ratbag_profile_get_led(p, RATBAG_LED_TYPE_LOGO);
assert_led_equals(led_logo, td.profiles[2].leds[RATBAG_LED_TYPE_LOGO]);
led_side = ratbag_profile_get_led(p, RATBAG_LED_TYPE_SIDE);
assert_led_equals(led_side, td.profiles[2].leds[RATBAG_LED_TYPE_SIDE]);
ratbag_led_unref(led_logo);
ratbag_led_unref(led_side);
ratbag_profile_unref(p);
ratbag_device_unref(d);
ratbag_unref(r);
ck_assert_int_eq(device_freed_count, 1);
}
END_TEST
START_TEST(device_leds_set)
{
struct ratbag *r;
struct ratbag_device *d;
struct ratbag_profile *p;
struct ratbag_led *l;
int nprofiles;
int device_freed_count = 0;
struct ratbag_test_device td = sane_device;
struct ratbag_color c = {
.red = 0,
.green = 111,
.blue = 222
};
td.destroyed_data = &device_freed_count;
r = ratbag_create_context(&abort_iface, NULL);
d = ratbag_device_new_test_device(r, &td);
nprofiles = ratbag_device_get_num_profiles(d);
ck_assert_int_eq(nprofiles, 3);
p = ratbag_device_get_profile(d, 0);
ck_assert(p != NULL);
l = ratbag_profile_get_led(p, RATBAG_LED_TYPE_LOGO);
ratbag_led_set_mode(l, RATBAG_LED_BREATHING);
ratbag_led_set_color(l, c);
ratbag_led_set_effect_rate(l, 11);
ratbag_led_set_brightness(l, 22);
l = ratbag_profile_get_led(p, RATBAG_LED_TYPE_LOGO);
struct ratbag_test_led e_l = {
.mode = RATBAG_LED_BREATHING,
.color = {
.red = c.red,
.green = c.green,
.blue = c.blue
},
.hz = 11,
.brightness = 22
};
assert_led_equals(l, e_l);
ratbag_led_unref(l);
ratbag_led_unref(l);
ratbag_profile_unref(p);
ratbag_device_unref(d);
ratbag_unref(r);
ck_assert_int_eq(device_freed_count, 1);
}
END_TEST
static Suite *
test_context_suite(void)
{
@@ -792,6 +919,11 @@ test_context_suite(void)
tcase_add_test(tc, device_buttons_set);
suite_add_tcase(s, tc);
tc = tcase_create("led");
tcase_add_test(tc, device_leds);
tcase_add_test(tc, device_leds_set);
suite_add_tcase(s, tc);
return s;
}