mirror of
https://gitlab.freedesktop.org/libevdev/libevdev.git
synced 2025-12-24 19:20:06 +01:00
Add functions to toggle LEDs on the device
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Reviewed-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
This commit is contained in:
parent
bfb6c1c6c5
commit
4d4293a65b
3 changed files with 258 additions and 0 deletions
|
|
@ -1179,3 +1179,62 @@ libevdev_get_repeat(struct libevdev *dev, int *delay, int *period)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
libevdev_kernel_set_led_value(struct libevdev *dev, unsigned int code, enum EvdevLEDValues value)
|
||||
{
|
||||
return libevdev_kernel_set_led_values(dev, code, value, -1);
|
||||
}
|
||||
|
||||
int
|
||||
libevdev_kernel_set_led_values(struct libevdev *dev, ...)
|
||||
{
|
||||
struct input_event ev[LED_MAX];
|
||||
enum EvdevLEDValues val;
|
||||
va_list args;
|
||||
int code;
|
||||
int rc = 0;
|
||||
size_t nleds = 0;
|
||||
|
||||
memset(ev, 0, sizeof(ev));
|
||||
|
||||
va_start(args, dev);
|
||||
code = va_arg(args, unsigned int);
|
||||
while (code != -1) {
|
||||
if (code > LED_MAX) {
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
val = va_arg(args, enum EvdevLEDValues);
|
||||
if (val != LIBEVDEV_LED_ON && val != LIBEVDEV_LED_OFF) {
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (libevdev_has_event_code(dev, EV_LED, code)) {
|
||||
struct input_event *e = ev;
|
||||
|
||||
while (e->type > 0 && e->code != code)
|
||||
e++;
|
||||
|
||||
if (e->type == 0)
|
||||
nleds++;
|
||||
e->type = EV_LED;
|
||||
e->code = code;
|
||||
e->value = (val == LIBEVDEV_LED_ON);
|
||||
}
|
||||
code = va_arg(args, unsigned int);
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
if (rc == 0 && nleds > 0) {
|
||||
rc = write(libevdev_get_fd(dev), ev, nleds * sizeof(ev[0]));
|
||||
if (rc > 0) {
|
||||
while (nleds--)
|
||||
update_led_state(dev, &ev[nleds]);
|
||||
}
|
||||
rc = (rc != -1) ? 0 : -errno;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1146,6 +1146,52 @@ int libevdev_disable_event_code(struct libevdev *dev, unsigned int type, unsigne
|
|||
*/
|
||||
int libevdev_kernel_set_abs_info(struct libevdev *dev, unsigned int code, const struct input_absinfo *abs);
|
||||
|
||||
|
||||
enum EvdevLEDValues {
|
||||
LIBEVDEV_LED_ON = 3,
|
||||
LIBEVDEV_LED_OFF = 4,
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup kernel
|
||||
*
|
||||
* Turn an LED on or off. Convenience function, if you need to modify multiple
|
||||
* LEDs simultaneously, use libevdev_kernel_set_led_values() instead.
|
||||
*
|
||||
* @note enabling an LED requires write permissions on the device's file descriptor.
|
||||
*
|
||||
* @param dev The evdev device, already initialized with libevdev_set_fd()
|
||||
* @param code The EV_LED event code to modify, one of LED_NUML, LED_CAPSL, ...
|
||||
* @param value Specifies whether to turn the LED on or off
|
||||
* @return zero on success, or a negative errno on failure
|
||||
*/
|
||||
int libevdev_kernel_set_led_value(struct libevdev *dev, unsigned int code, enum EvdevLEDValues value);
|
||||
|
||||
/**
|
||||
* @ingroup kernel
|
||||
*
|
||||
* Turn multiple LEDs on or off simultaneously. This function expects a pair
|
||||
* of LED codes and values to set them to, terminated by a -1. For example, to
|
||||
* switch the NumLock LED on but the CapsLock LED off, use:
|
||||
*
|
||||
* @code
|
||||
* libevdev_kernel_set_led_values(dev, LED_NUML, LIBEVDEV_LED_ON,
|
||||
* LED_CAPSL, LIBEVDEV_LED_OFF,
|
||||
* -1);
|
||||
* @endcode
|
||||
*
|
||||
* If any LED code or value is invalid, this function returns -EINVAL and no
|
||||
* LEDs are modified.
|
||||
*
|
||||
* @note enabling an LED requires write permissions on the device's file descriptor.
|
||||
*
|
||||
* @param dev The evdev device, already initialized with libevdev_set_fd()
|
||||
* @param ... A pair of LED_* event codes and enum EvdevLEDValues, followed by
|
||||
* -1 to terminate the list.
|
||||
* @return zero on success, or a negative errno on failure
|
||||
*/
|
||||
int libevdev_kernel_set_led_values(struct libevdev *dev, ...);
|
||||
|
||||
/**
|
||||
* @ingroup misc
|
||||
*
|
||||
|
|
|
|||
|
|
@ -923,6 +923,153 @@ START_TEST(test_device_kernel_change_axis_invalid)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_led_valid)
|
||||
{
|
||||
struct uinput_device* uidev;
|
||||
struct libevdev *dev;
|
||||
int rc;
|
||||
|
||||
rc = test_create_device(&uidev, &dev,
|
||||
EV_LED, LED_NUML,
|
||||
EV_LED, LED_CAPSL,
|
||||
EV_LED, LED_COMPOSE,
|
||||
-1);
|
||||
ck_assert_msg(rc == 0, "Failed to create device: %s", strerror(-rc));
|
||||
|
||||
rc = libevdev_kernel_set_led_value(dev, LED_NUML, LIBEVDEV_LED_ON);
|
||||
ck_assert_int_eq(rc, 0);
|
||||
rc = libevdev_kernel_set_led_value(dev, LED_NUML, LIBEVDEV_LED_OFF);
|
||||
ck_assert_int_eq(rc, 0);
|
||||
|
||||
rc = libevdev_kernel_set_led_values(dev,
|
||||
LED_NUML, LIBEVDEV_LED_OFF,
|
||||
LED_CAPSL, LIBEVDEV_LED_ON,
|
||||
LED_COMPOSE, LIBEVDEV_LED_OFF,
|
||||
-1);
|
||||
ck_assert_int_eq(rc, 0);
|
||||
ck_assert_int_eq(0, libevdev_get_event_value(dev, EV_LED, LED_NUML));
|
||||
ck_assert_int_eq(1, libevdev_get_event_value(dev, EV_LED, LED_CAPSL));
|
||||
ck_assert_int_eq(0, libevdev_get_event_value(dev, EV_LED, LED_COMPOSE));
|
||||
|
||||
rc = libevdev_kernel_set_led_values(dev,
|
||||
LED_NUML, LIBEVDEV_LED_ON,
|
||||
LED_CAPSL, LIBEVDEV_LED_OFF,
|
||||
LED_COMPOSE, LIBEVDEV_LED_ON,
|
||||
-1);
|
||||
ck_assert_int_eq(rc, 0);
|
||||
ck_assert_int_eq(1, libevdev_get_event_value(dev, EV_LED, LED_NUML));
|
||||
ck_assert_int_eq(0, libevdev_get_event_value(dev, EV_LED, LED_CAPSL));
|
||||
ck_assert_int_eq(1, libevdev_get_event_value(dev, EV_LED, LED_COMPOSE));
|
||||
|
||||
/* make sure we ignore unset leds */
|
||||
rc = libevdev_kernel_set_led_values(dev,
|
||||
LED_NUML, LIBEVDEV_LED_ON,
|
||||
LED_CAPSL, LIBEVDEV_LED_OFF,
|
||||
LED_SCROLLL, LIBEVDEV_LED_OFF,
|
||||
LED_COMPOSE, LIBEVDEV_LED_ON,
|
||||
-1);
|
||||
ck_assert_int_eq(rc, 0);
|
||||
ck_assert_int_eq(1, libevdev_get_event_value(dev, EV_LED, LED_NUML));
|
||||
ck_assert_int_eq(0, libevdev_get_event_value(dev, EV_LED, LED_CAPSL));
|
||||
ck_assert_int_eq(1, libevdev_get_event_value(dev, EV_LED, LED_COMPOSE));
|
||||
|
||||
libevdev_free(dev);
|
||||
uinput_device_free(uidev);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_led_invalid)
|
||||
{
|
||||
struct uinput_device* uidev;
|
||||
struct libevdev *dev;
|
||||
int rc;
|
||||
|
||||
rc = test_create_device(&uidev, &dev,
|
||||
EV_LED, LED_NUML,
|
||||
EV_LED, LED_CAPSL,
|
||||
EV_LED, LED_COMPOSE,
|
||||
-1);
|
||||
ck_assert_msg(rc == 0, "Failed to create device: %s", strerror(-rc));
|
||||
|
||||
rc = libevdev_kernel_set_led_value(dev, LED_MAX + 1, LIBEVDEV_LED_ON);
|
||||
ck_assert_int_eq(rc, -EINVAL);
|
||||
|
||||
rc = libevdev_kernel_set_led_value(dev, LED_NUML, LIBEVDEV_LED_OFF + 1);
|
||||
ck_assert_int_eq(rc, -EINVAL);
|
||||
|
||||
rc = libevdev_kernel_set_led_value(dev, LED_SCROLLL, LIBEVDEV_LED_ON);
|
||||
ck_assert_int_eq(rc, 0);
|
||||
|
||||
rc = libevdev_kernel_set_led_values(dev,
|
||||
LED_NUML, LIBEVDEV_LED_OFF + 1,
|
||||
-1);
|
||||
ck_assert_int_eq(rc, -EINVAL);
|
||||
|
||||
rc = libevdev_kernel_set_led_values(dev,
|
||||
LED_MAX + 1, LIBEVDEV_LED_ON,
|
||||
LED_NUML, LIBEVDEV_LED_OFF + 1,
|
||||
-1);
|
||||
ck_assert_int_eq(rc, -EINVAL);
|
||||
|
||||
rc = libevdev_kernel_set_led_values(dev,
|
||||
LED_SCROLLL, LIBEVDEV_LED_OFF,
|
||||
-1);
|
||||
ck_assert_int_eq(rc, 0);
|
||||
|
||||
libevdev_free(dev);
|
||||
uinput_device_free(uidev);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_led_same)
|
||||
{
|
||||
struct uinput_device* uidev;
|
||||
struct libevdev *dev;
|
||||
int rc;
|
||||
|
||||
rc = test_create_device(&uidev, &dev,
|
||||
EV_LED, LED_NUML,
|
||||
EV_LED, LED_CAPSL,
|
||||
EV_LED, LED_COMPOSE,
|
||||
-1);
|
||||
ck_assert_msg(rc == 0, "Failed to create device: %s", strerror(-rc));
|
||||
|
||||
rc = libevdev_kernel_set_led_values(dev,
|
||||
LED_NUML, LIBEVDEV_LED_OFF,
|
||||
LED_NUML, LIBEVDEV_LED_ON,
|
||||
LED_NUML, LIBEVDEV_LED_OFF,
|
||||
LED_NUML, LIBEVDEV_LED_ON,
|
||||
LED_NUML, LIBEVDEV_LED_OFF,
|
||||
LED_NUML, LIBEVDEV_LED_ON,
|
||||
LED_NUML, LIBEVDEV_LED_OFF,
|
||||
LED_NUML, LIBEVDEV_LED_ON,
|
||||
LED_NUML, LIBEVDEV_LED_OFF,
|
||||
LED_NUML, LIBEVDEV_LED_ON,
|
||||
LED_NUML, LIBEVDEV_LED_OFF,
|
||||
LED_NUML, LIBEVDEV_LED_ON,
|
||||
LED_NUML, LIBEVDEV_LED_OFF,
|
||||
LED_NUML, LIBEVDEV_LED_ON,
|
||||
LED_NUML, LIBEVDEV_LED_OFF,
|
||||
LED_NUML, LIBEVDEV_LED_ON,
|
||||
LED_NUML, LIBEVDEV_LED_OFF,
|
||||
LED_NUML, LIBEVDEV_LED_ON,
|
||||
LED_NUML, LIBEVDEV_LED_OFF,
|
||||
LED_NUML, LIBEVDEV_LED_ON,
|
||||
LED_NUML, LIBEVDEV_LED_OFF,
|
||||
LED_NUML, LIBEVDEV_LED_ON,
|
||||
LED_NUML, LIBEVDEV_LED_OFF,
|
||||
LED_NUML, LIBEVDEV_LED_ON,
|
||||
/* more than LED_CNT */
|
||||
-1);
|
||||
ck_assert_int_eq(rc, 0);
|
||||
ck_assert_int_eq(1, libevdev_get_event_value(dev, EV_LED, LED_NUML));
|
||||
ck_assert_int_eq(0, libevdev_get_event_value(dev, EV_LED, LED_CAPSL));
|
||||
ck_assert_int_eq(0, libevdev_get_event_value(dev, EV_LED, LED_COMPOSE));
|
||||
|
||||
libevdev_free(dev);
|
||||
uinput_device_free(uidev);
|
||||
}
|
||||
END_TEST
|
||||
Suite *
|
||||
libevdev_has_event_test(void)
|
||||
{
|
||||
|
|
@ -971,6 +1118,12 @@ libevdev_has_event_test(void)
|
|||
tcase_add_test(tc, test_device_kernel_change_axis_invalid);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
tc = tcase_create("led manipulation");
|
||||
tcase_add_test(tc, test_led_valid);
|
||||
tcase_add_test(tc, test_led_invalid);
|
||||
tcase_add_test(tc, test_led_same);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue