1
0
forked from drath/libratbag
Files
libratbag-plus/src/libratbag.c
2020-07-23 00:45:29 +01:00

2015 lines
46 KiB
C

/*
* Copyright © 2015 Red Hat, Inc.
*
* 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 <assert.h>
#include <errno.h>
#include <libudev.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <limits.h>
#include "usb-ids.h"
#include "libratbag-private.h"
#include "libratbag-util.h"
#include "libratbag-data.h"
static enum ratbag_error_code
error_code(enum ratbag_error_code code)
{
switch(code) {
case RATBAG_SUCCESS:
case RATBAG_ERROR_DEVICE:
case RATBAG_ERROR_CAPABILITY:
case RATBAG_ERROR_VALUE:
case RATBAG_ERROR_SYSTEM:
case RATBAG_ERROR_IMPLEMENTATION:
break;
default:
assert(!"Invalid error code. This is a library bug.");
}
return code;
}
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_resolution_destroy(struct ratbag_resolution *resolution);
static void
ratbag_default_log_func(struct ratbag *ratbag,
enum ratbag_log_priority priority,
const char *format, va_list args)
{
const char *prefix;
FILE *out = stdout;
switch(priority) {
case RATBAG_LOG_PRIORITY_RAW:
prefix = "raw";
break;
case RATBAG_LOG_PRIORITY_DEBUG:
prefix = "debug";
break;
case RATBAG_LOG_PRIORITY_INFO:
prefix = "info";
break;
case RATBAG_LOG_PRIORITY_ERROR:
prefix = "error";
out = stderr;
break;
default:
prefix="<invalid priority>";
break;
}
fprintf(out, "ratbag %s: ", prefix);
vfprintf(out, format, args);
}
void
log_msg_va(struct ratbag *ratbag,
enum ratbag_log_priority priority,
const char *format,
va_list args)
{
if (ratbag->log_handler &&
ratbag->log_priority <= priority)
ratbag->log_handler(ratbag, priority, format, args);
}
void
log_msg(struct ratbag *ratbag,
enum ratbag_log_priority priority,
const char *format, ...)
{
va_list args;
va_start(args, format);
log_msg_va(ratbag, priority, format, args);
va_end(args);
}
void
log_buffer(struct ratbag *ratbag,
enum ratbag_log_priority priority,
const char *header,
uint8_t *buf, size_t len)
{
_cleanup_free_ char *output_buf = NULL;
char *sep = "";
unsigned int i, n;
unsigned int buf_len;
if (ratbag->log_handler &&
ratbag->log_priority > priority)
return;
buf_len = header ? strlen(header) : 0;
buf_len += len * 3;
buf_len += 1; /* terminating '\0' */
output_buf = zalloc(buf_len);
n = 0;
if (header)
n += snprintf_safe(output_buf, buf_len - n, "%s", header);
for (i = 0; i < len; ++i) {
n += snprintf_safe(&output_buf[n], buf_len - n, "%s%02x", sep, buf[i] & 0xFF);
sep = " ";
}
log_msg(ratbag, priority, "%s\n", output_buf);
}
LIBRATBAG_EXPORT void
ratbag_log_set_priority(struct ratbag *ratbag,
enum ratbag_log_priority priority)
{
switch (priority) {
case RATBAG_LOG_PRIORITY_RAW:
case RATBAG_LOG_PRIORITY_DEBUG:
case RATBAG_LOG_PRIORITY_INFO:
case RATBAG_LOG_PRIORITY_ERROR:
break;
default:
log_bug_client(ratbag,
"Invalid log priority %d. Using INFO instead\n",
priority);
priority = RATBAG_LOG_PRIORITY_INFO;
}
ratbag->log_priority = priority;
}
LIBRATBAG_EXPORT enum ratbag_log_priority
ratbag_log_get_priority(const struct ratbag *ratbag)
{
return ratbag->log_priority;
}
LIBRATBAG_EXPORT void
ratbag_log_set_handler(struct ratbag *ratbag,
ratbag_log_handler log_handler)
{
ratbag->log_handler = log_handler;
}
struct ratbag_device*
ratbag_device_new(struct ratbag *ratbag, struct udev_device *udev_device,
const char *name, const struct input_id *id)
{
struct ratbag_device *device = NULL;
device = zalloc(sizeof(*device));
device->name = strdup_safe(name);
device->ratbag = ratbag_ref(ratbag);
device->refcount = 1;
device->udev_device = udev_device_ref(udev_device);
device->ids = *id;
device->data = ratbag_device_data_new_for_id(ratbag, id);
list_init(&device->profiles);
list_insert(&ratbag->devices, &device->link);
return device;
}
void
ratbag_device_destroy(struct ratbag_device *device)
{
struct ratbag_profile *profile, *next;
if (!device)
return;
/* if we get to the point where the device is destroyed, profiles,
* buttons, etc. are at a refcount of 0, so we can destroy
* everything */
if (device->driver && device->driver->remove)
device->driver->remove(device);
list_for_each_safe(profile, next, &device->profiles, link)
ratbag_profile_destroy(profile);
if (device->udev_device)
udev_device_unref(device->udev_device);
list_remove(&device->link);
ratbag_unref(device->ratbag);
ratbag_device_data_unref(device->data);
free(device->name);
free(device);
}
static inline bool
ratbag_sanity_check_device(struct ratbag_device *device)
{
struct ratbag *ratbag = device->ratbag;
struct ratbag_profile *profile = NULL;
bool has_active = false;
unsigned int nres;
bool rc = false;
/* arbitrary number: max 16 profiles, does any mouse have more? but
* since we have num_profiles unsigned, it also checks for
* accidental negative */
if (device->num_profiles == 0 || device->num_profiles > 16) {
log_bug_libratbag(ratbag,
"%s: invalid number of profiles (%d)\n",
device->name,
device->num_profiles);
goto out;
}
ratbag_device_for_each_profile(device, profile) {
struct ratbag_resolution *resolution;
unsigned int vals[300];
unsigned int nvals = ARRAY_LENGTH(vals);
/* Allow max 1 active profile */
if (profile->is_active) {
if (has_active) {
log_bug_libratbag(ratbag,
"%s: multiple active profiles\n",
device->name);
goto out;
}
has_active = true;
}
nres = ratbag_profile_get_num_resolutions(profile);
if (nres > 16) {
log_bug_libratbag(ratbag,
"%s: invalid number of resolutions (%d)\n",
device->name,
nres);
goto out;
}
ratbag_profile_for_each_resolution(profile, resolution) {
nvals = ratbag_resolution_get_dpi_list(resolution, vals, nvals);
if (nvals == 0) {
log_bug_libratbag(ratbag,
"%s: invalid dpi list\n",
device->name);
goto out;
}
}
nvals = ratbag_profile_get_report_rate_list(profile, vals, nvals);
if (nvals == 0) {
log_bug_libratbag(ratbag,
"%s: invalid report rate list\n",
device->name);
goto out;
}
}
/* Require 1 active profile */
if (!has_active) {
log_bug_libratbag(ratbag,
"%s: no profile set as active profile\n",
device->name);
goto out;
}
/* Require LED to be the same type in each profile */
ratbag_device_for_each_profile(device, profile) {
_cleanup_profile_ struct ratbag_profile *p0 = ratbag_device_get_profile(device, 0);
struct ratbag_led *led;
ratbag_profile_for_each_led(profile, led) {
_cleanup_led_ struct ratbag_led *p0_led = ratbag_profile_get_led(p0, led->index);
assert(led->type != RATBAG_LED_TYPE_UNKNOWN);
if (p0_led->type != led->type) {
log_bug_libratbag(ratbag,
"%s: mismatching LED types (profile %d led %d)\n",
device->name,
profile->index,
led->type);
goto out;
}
}
}
rc = true;
out:
return rc;
}
static inline bool
ratbag_try_driver(struct ratbag_device *device,
const struct input_id *dev_id,
const char *driver_name,
const struct ratbag_test_device *test_device)
{
struct ratbag *ratbag = device->ratbag;
struct ratbag_driver *driver;
int rc;
list_for_each(driver, &ratbag->drivers, link) {
if (streq(driver->id, driver_name)) {
device->driver = driver;
break;
}
}
if (!device->driver) {
log_error(ratbag, "%s: driver '%s' does not exist\n",
device->name, driver_name);
goto error;
}
if (test_device)
rc = device->driver->test_probe(device, test_device);
else
rc = device->driver->probe(device);
if (rc == 0) {
if (!ratbag_sanity_check_device(device)) {
goto error;
} else {
log_debug(ratbag,
"driver match found: %s\n",
device->driver->name);
return true;
}
}
if (rc != -ENODEV)
log_error(ratbag, "%s: error opening hidraw node (%s)\n",
device->name, strerror(-rc));
device->driver = NULL;
error:
return false;
}
static inline bool
ratbag_driver_fallback_logitech(struct ratbag_device *device,
const struct input_id *dev_id)
{
int rc;
if (dev_id->vendor != USB_VENDOR_ID_LOGITECH)
return false;
rc = ratbag_try_driver(device, dev_id, "hidpp20", NULL);
if (!rc)
rc = ratbag_try_driver(device, dev_id, "hidpp10", NULL);
return rc;
}
bool
ratbag_assign_driver(struct ratbag_device *device,
const struct input_id *dev_id,
const struct ratbag_test_device *test_device)
{
bool rc;
const char *driver_name;
if (!test_device) {
driver_name = ratbag_device_data_get_driver(device->data);
} else {
log_debug(device->ratbag, "This is a test device\n");
driver_name = "test_driver";
}
if (driver_name) {
log_debug(device->ratbag, "device assigned driver %s\n", driver_name);
rc = ratbag_try_driver(device, dev_id, driver_name, test_device);
} else {
rc = ratbag_driver_fallback_logitech(device, dev_id);
}
return rc;
}
static char *
get_device_name(struct udev_device *device)
{
const char *prop;
prop = udev_prop_value(device, "HID_NAME");
return strdup_safe(prop);
}
static inline int
get_product_id(struct udev_device *device, struct input_id *id)
{
const char *product;
struct input_id ids = {0};
int rc;
product = udev_prop_value(device, "HID_ID");
if (!product)
return -1;
rc = sscanf(product, "%hx:%hx:%hx", &ids.bustype, &ids.vendor, &ids.product);
if (rc != 3)
return -1;
*id = ids;
return 0;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_device_new_from_udev_device(struct ratbag *ratbag,
struct udev_device *udev_device,
struct ratbag_device **device_out)
{
struct ratbag_device *device = NULL;
enum ratbag_error_code error = RATBAG_ERROR_DEVICE;
_cleanup_free_ char *name = NULL;
struct input_id id;
assert(ratbag != NULL);
assert(udev_device != NULL);
assert(device_out != NULL);
if (get_product_id(udev_device, &id) != 0)
goto out_err;
if ((name = get_device_name(udev_device)) == 0)
goto out_err;
log_debug(ratbag, "New device: %s\n", name);
device = ratbag_device_new(ratbag, udev_device, name, &id);
if (!device || !device->data)
goto out_err;
if (!ratbag_assign_driver(device, &device->ids, NULL))
goto out_err;
error = RATBAG_SUCCESS;
out_err:
if (error != RATBAG_SUCCESS)
ratbag_device_destroy(device);
else
*device_out = device;
return error_code(error);
}
LIBRATBAG_EXPORT struct ratbag_device *
ratbag_device_ref(struct ratbag_device *device)
{
assert(device->refcount < INT_MAX);
device->refcount++;
return device;
}
LIBRATBAG_EXPORT struct ratbag_device *
ratbag_device_unref(struct ratbag_device *device)
{
if (device == NULL)
return NULL;
assert(device->refcount > 0);
device->refcount--;
if (device->refcount == 0)
ratbag_device_destroy(device);
return NULL;
}
LIBRATBAG_EXPORT const char *
ratbag_device_get_name(const struct ratbag_device* device)
{
return device->name;
}
LIBRATBAG_EXPORT const char *
ratbag_device_get_bustype(const struct ratbag_device *device)
{
switch (device->ids.bustype) {
case BUS_USB:
return "usb";
case BUS_BLUETOOTH:
return "bluetooth";
default:
return NULL;
}
}
LIBRATBAG_EXPORT uint32_t
ratbag_device_get_vendor_id(const struct ratbag_device *device)
{
return device->ids.vendor;
}
LIBRATBAG_EXPORT uint32_t
ratbag_device_get_product_id(const struct ratbag_device *device)
{
return device->ids.product;
}
LIBRATBAG_EXPORT uint32_t
ratbag_device_get_product_version(const struct ratbag_device *device)
{
/* change this when we have a need for it, i.e. when we start supporting devices
* where the USB ID gets reused */
return 0;
}
void
ratbag_register_driver(struct ratbag *ratbag, struct ratbag_driver *driver)
{
if (!driver->name) {
log_bug_libratbag(ratbag, "Driver is missing name\n");
return;
}
if (!driver->probe || !driver->remove) {
log_bug_libratbag(ratbag, "Driver %s is incomplete.\n", driver->name);
return;
}
list_insert(&ratbag->drivers, &driver->link);
}
LIBRATBAG_EXPORT struct ratbag *
ratbag_create_context(const struct ratbag_interface *interface,
void *userdata)
{
struct ratbag *ratbag;
assert(interface != NULL);
assert(interface->open_restricted != NULL);
assert(interface->close_restricted != NULL);
ratbag = zalloc(sizeof(*ratbag));
ratbag->refcount = 1;
ratbag->interface = interface;
ratbag->userdata = userdata;
list_init(&ratbag->drivers);
list_init(&ratbag->devices);
ratbag->udev = udev_new();
if (!ratbag->udev) {
free(ratbag);
return NULL;
}
ratbag->log_handler = ratbag_default_log_func;
ratbag->log_priority = RATBAG_LOG_PRIORITY_INFO;
ratbag_register_driver(ratbag, &etekcity_driver);
ratbag_register_driver(ratbag, &hidpp20_driver);
ratbag_register_driver(ratbag, &hidpp10_driver);
ratbag_register_driver(ratbag, &logitech_g300_driver);
ratbag_register_driver(ratbag, &logitech_g600_driver);
ratbag_register_driver(ratbag, &roccat_driver);
ratbag_register_driver(ratbag, &gskill_driver);
ratbag_register_driver(ratbag, &steelseries_driver);
ratbag_register_driver(ratbag, &sinowealth_driver);
return ratbag;
}
LIBRATBAG_EXPORT struct ratbag *
ratbag_ref(struct ratbag *ratbag)
{
ratbag->refcount++;
return ratbag;
}
LIBRATBAG_EXPORT struct ratbag *
ratbag_unref(struct ratbag *ratbag)
{
if (ratbag == NULL)
return NULL;
assert(ratbag->refcount > 0);
ratbag->refcount--;
if (ratbag->refcount == 0) {
ratbag->udev = udev_unref(ratbag->udev);
free(ratbag);
}
return NULL;
}
static struct ratbag_button *
ratbag_create_button(struct ratbag_profile *profile, unsigned int index)
{
struct ratbag_button *button;
button = zalloc(sizeof(*button));
button->refcount = 0;
button->profile = profile;
button->index = index;
button->type = RATBAG_BUTTON_TYPE_UNKNOWN;
list_append(&profile->buttons, &button->link);
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;
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
led->type = RATBAG_LED_TYPE_UNKNOWN;
if (device->data)
led->type = ratbag_device_data_get_led_type(device->data, led->index);
list_append(&profile->leds, &led->link);
return led;
}
LIBRATBAG_EXPORT bool
ratbag_profile_has_capability(const struct ratbag_profile *profile,
enum ratbag_profile_capability cap)
{
if (cap == RATBAG_PROFILE_CAP_NONE || cap >= MAX_CAP)
abort();
return long_bit_is_set(profile->capabilities, cap);
}
static inline void
ratbag_create_resolution(struct ratbag_profile *profile, int index)
{
struct ratbag_resolution *res;
res = zalloc(sizeof(*res));
res->refcount = 0;
res->profile = profile;
res->index = index;
list_append(&profile->resolutions, &res->link);
profile->num_resolutions++;
}
static struct ratbag_profile *
ratbag_create_profile(struct ratbag_device *device,
unsigned int index,
unsigned int num_resolutions,
unsigned int num_buttons,
unsigned int num_leds)
{
struct ratbag_profile *profile;
unsigned i;
profile = zalloc(sizeof(*profile));
profile->refcount = 0;
profile->device = device;
profile->index = index;
profile->num_resolutions = 0;
profile->is_enabled = true;
profile->name = NULL;
list_append(&device->profiles, &profile->link);
list_init(&profile->buttons);
list_init(&profile->leds);
list_init(&profile->resolutions);
profile->device->num_buttons = num_buttons;
profile->device->num_leds = num_leds;
for (i = 0; i < num_resolutions; i++)
ratbag_create_resolution(profile, i);
for (i = 0; i < num_buttons; i++)
ratbag_create_button(profile, i);
for (i = 0; i < num_leds; i++)
ratbag_create_led(profile, i);
return profile;
}
int
ratbag_device_init_profiles(struct ratbag_device *device,
unsigned int num_profiles,
unsigned int num_resolutions,
unsigned int num_buttons,
unsigned int num_leds)
{
unsigned int i;
for (i = 0; i < num_profiles; i++) {
ratbag_create_profile(device, i, num_resolutions, num_buttons, num_leds);
}
device->num_profiles = num_profiles;
return 0;
}
LIBRATBAG_EXPORT struct ratbag_profile *
ratbag_profile_ref(struct ratbag_profile *profile)
{
assert(profile->refcount < INT_MAX);
ratbag_device_ref(profile->device);
profile->refcount++;
return profile;
}
static void
ratbag_profile_destroy(struct ratbag_profile *profile)
{
struct ratbag_button *button, *b_next;
struct ratbag_led *led, *l_next;
struct ratbag_resolution *res, *r_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, b_next, &profile->buttons, link)
ratbag_button_destroy(button);
list_for_each_safe(led, l_next, &profile->leds, link)
ratbag_led_destroy(led);
list_for_each_safe(res, r_next, &profile->resolutions, link)
ratbag_resolution_destroy(res);
free(profile->name);
list_remove(&profile->link);
free(profile);
}
LIBRATBAG_EXPORT struct ratbag_profile *
ratbag_profile_unref(struct ratbag_profile *profile)
{
if (profile == NULL)
return NULL;
assert(profile->refcount > 0);
profile->refcount--;
ratbag_device_unref(profile->device);
return NULL;
}
LIBRATBAG_EXPORT struct ratbag_profile *
ratbag_device_get_profile(struct ratbag_device *device, unsigned int index)
{
struct ratbag_profile *profile;
if (index >= ratbag_device_get_num_profiles(device)) {
log_bug_client(device->ratbag, "Requested invalid profile %d\n", index);
return NULL;
}
list_for_each(profile, &device->profiles, link) {
if (profile->index == index)
return ratbag_profile_ref(profile);
}
log_bug_libratbag(device->ratbag, "Profile %d not found\n", index);
return NULL;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_profile_set_enabled(struct ratbag_profile *profile, bool enabled)
{
if (!ratbag_profile_has_capability(profile, RATBAG_PROFILE_CAP_DISABLE))
return RATBAG_ERROR_CAPABILITY;
profile->is_enabled = enabled;
profile->dirty = true;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT bool
ratbag_profile_is_active(struct ratbag_profile *profile)
{
return !!profile->is_active;
}
LIBRATBAG_EXPORT bool
ratbag_profile_is_enabled(const struct ratbag_profile *profile)
{
return !!profile->is_enabled;
}
LIBRATBAG_EXPORT unsigned int
ratbag_device_get_num_profiles(struct ratbag_device *device)
{
return device->num_profiles;
}
LIBRATBAG_EXPORT unsigned int
ratbag_device_get_num_buttons(struct ratbag_device *device)
{
return device->num_buttons;
}
LIBRATBAG_EXPORT unsigned int
ratbag_device_get_num_leds(struct ratbag_device *device)
{
return device->num_leds;
}
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->ms, 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.
*/
static enum ratbag_error_code
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;
assert(device->driver->write_profile);
list_for_each(profile, &device->profiles, link) {
if (!profile->dirty)
continue;
rc = device->driver->write_profile(profile);
if (rc)
return RATBAG_ERROR_DEVICE;
if (device->driver->write_resolution_dpi) {
ratbag_profile_for_each_resolution(profile, resolution) {
rc = device->driver->write_resolution_dpi(
resolution, resolution->dpi_x,
resolution->dpi_y);
if (rc)
return RATBAG_ERROR_DEVICE;
}
}
if (device->driver->write_button) {
list_for_each(button, &profile->buttons, link) {
struct ratbag_button_action action =
button->action;
if (!button->dirty)
continue;
rc = device->driver->write_button(button,
&action);
if (rc)
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;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_device_commit(struct ratbag_device *device)
{
struct ratbag_profile *profile;
struct ratbag_button *button;
struct ratbag_led *led;
struct ratbag_resolution *resolution;
int rc;
if (!device->driver->commit) {
rc = ratbag_old_write_profile(device);
if (rc != RATBAG_SUCCESS)
return rc;
} else {
rc = device->driver->commit(device);
if (rc)
return RATBAG_ERROR_DEVICE;
}
list_for_each(profile, &device->profiles, link) {
profile->dirty = false;
profile->rate_dirty = false;
list_for_each(button, &profile->buttons, link)
button->dirty = false;
list_for_each(led, &profile->leds, link)
led->dirty = false;
list_for_each(resolution, &profile->resolutions, link)
resolution->dirty = false;
}
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_profile_set_active(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
struct ratbag_profile *p;
int rc;
if (device->num_profiles == 1)
return RATBAG_SUCCESS;
assert(device->driver->set_active_profile);
rc = device->driver->set_active_profile(device, profile->index);
if (rc)
return RATBAG_ERROR_DEVICE;
list_for_each(p, &device->profiles, link)
p->is_active = false;
profile->is_active = true;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT unsigned int
ratbag_profile_get_num_resolutions(struct ratbag_profile *profile)
{
return profile->num_resolutions;
}
LIBRATBAG_EXPORT struct ratbag_resolution *
ratbag_profile_get_resolution(struct ratbag_profile *profile, unsigned int idx)
{
struct ratbag_device *device = profile->device;
struct ratbag_resolution *res;
unsigned max = ratbag_profile_get_num_resolutions(profile);
if (idx >= max) {
log_bug_client(profile->device->ratbag,
"Requested invalid resolution %d\n", idx);
return NULL;
}
ratbag_profile_for_each_resolution(profile, res) {
if (res->index == idx)
return ratbag_resolution_ref(res);
}
log_bug_libratbag(device->ratbag, "Resolution %d, profile %d not found\n",
idx, profile->index);
return NULL;
}
LIBRATBAG_EXPORT struct ratbag_resolution *
ratbag_resolution_ref(struct ratbag_resolution *resolution)
{
assert(resolution->refcount < INT_MAX);
ratbag_profile_ref(resolution->profile);
resolution->refcount++;
return resolution;
}
LIBRATBAG_EXPORT struct ratbag_resolution *
ratbag_resolution_unref(struct ratbag_resolution *resolution)
{
if (resolution == NULL)
return NULL;
assert(resolution->refcount > 0);
resolution->refcount--;
ratbag_profile_unref(resolution->profile);
return NULL;
}
LIBRATBAG_EXPORT bool
ratbag_resolution_has_capability(struct ratbag_resolution *resolution,
enum ratbag_resolution_capability cap)
{
switch (cap) {
case RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION:
break;
default:
return 0;
}
return !!(resolution->capabilities & (1 << cap));
}
static inline bool
resolution_has_dpi(struct ratbag_resolution *resolution,
unsigned int dpi)
{
for (size_t i = 0; i < resolution->ndpis; i++) {
if (dpi == resolution->dpis[i])
return true;
}
return false;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_resolution_set_dpi(struct ratbag_resolution *resolution,
unsigned int dpi)
{
struct ratbag_profile *profile = resolution->profile;
if (!resolution_has_dpi(resolution, dpi))
return RATBAG_ERROR_VALUE;
if (resolution->dpi_x != dpi || resolution->dpi_y != dpi) {
resolution->dpi_x = dpi;
resolution->dpi_y = dpi;
resolution->dirty = true;
profile->dirty = true;
}
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_resolution_set_dpi_xy(struct ratbag_resolution *resolution,
unsigned int x, unsigned int y)
{
struct ratbag_profile *profile = resolution->profile;
if (!ratbag_resolution_has_capability(resolution,
RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION))
return RATBAG_ERROR_CAPABILITY;
if ((x == 0 && y != 0) || (x != 0 && y == 0))
return RATBAG_ERROR_VALUE;
if (!resolution_has_dpi(resolution, x) || !resolution_has_dpi(resolution, y))
return RATBAG_ERROR_VALUE;
if (resolution->dpi_x != x || resolution->dpi_y != y) {
resolution->dpi_x = x;
resolution->dpi_y = y;
resolution->dirty = true;
profile->dirty = true;
}
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_profile_set_report_rate(struct ratbag_profile *profile,
unsigned int hz)
{
if (profile->hz != hz) {
profile->hz = hz;
profile->dirty = true;
profile->rate_dirty = true;
}
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT int
ratbag_resolution_get_dpi(struct ratbag_resolution *resolution)
{
return resolution->dpi_x;
}
LIBRATBAG_EXPORT size_t
ratbag_resolution_get_dpi_list(struct ratbag_resolution *resolution,
unsigned int *resolutions,
size_t nres)
{
_Static_assert(sizeof(*resolutions) == sizeof(*resolution->dpis), "type mismatch");
assert(nres > 0);
if (resolution->ndpis == 0)
return 0;
memcpy(resolutions, resolution->dpis,
sizeof(unsigned int) * min(nres, resolution->ndpis));
return resolution->ndpis;
}
LIBRATBAG_EXPORT int
ratbag_resolution_get_dpi_x(struct ratbag_resolution *resolution)
{
return resolution->dpi_x;
}
LIBRATBAG_EXPORT int
ratbag_resolution_get_dpi_y(struct ratbag_resolution *resolution)
{
return resolution->dpi_y;
}
LIBRATBAG_EXPORT int
ratbag_profile_get_report_rate(struct ratbag_profile *profile)
{
return profile->hz;
}
LIBRATBAG_EXPORT size_t
ratbag_profile_get_report_rate_list(struct ratbag_profile *profile,
unsigned int *rates,
size_t nrates)
{
_Static_assert(sizeof(*rates) == sizeof(*profile->rates), "type mismatch");
assert(nrates > 0);
if (profile->nrates == 0)
return 0;
memcpy(rates, profile->rates,
sizeof(unsigned int) * min(nrates, profile->nrates));
return profile->nrates;
}
LIBRATBAG_EXPORT bool
ratbag_resolution_is_active(const struct ratbag_resolution *resolution)
{
return !!resolution->is_active;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_resolution_set_active(struct ratbag_resolution *resolution)
{
struct ratbag_profile *profile = resolution->profile;
struct ratbag_resolution *res;
ratbag_profile_for_each_resolution(profile, res)
res->is_active = false;
resolution->is_active = true;
resolution->dirty = true;
profile->dirty = true;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT bool
ratbag_resolution_is_default(const struct ratbag_resolution *resolution)
{
return !!resolution->is_default;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_resolution_set_default(struct ratbag_resolution *resolution)
{
struct ratbag_profile *profile = resolution->profile;
struct ratbag_resolution *other;
/* Unset the default on the other resolutions */
ratbag_profile_for_each_resolution(profile, other) {
if (other == resolution || !other->is_default)
continue;
other->is_default = false;
resolution->dirty = true;
profile->dirty = true;
}
if (!resolution->is_default) {
resolution->is_default = true;
resolution->dirty = true;
profile->dirty = true;
}
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT struct ratbag_button*
ratbag_profile_get_button(struct ratbag_profile *profile,
unsigned int index)
{
struct ratbag_device *device = profile->device;
struct ratbag_button *button;
if (index >= ratbag_device_get_num_buttons(device)) {
log_bug_client(device->ratbag, "Requested invalid button %d\n", index);
return NULL;
}
list_for_each(button, &profile->buttons, link) {
if (button->index == index)
return ratbag_button_ref(button);
}
log_bug_libratbag(device->ratbag, "Button %d, profile %d not found\n",
index, profile->index);
return NULL;
}
LIBRATBAG_EXPORT enum ratbag_button_action_type
ratbag_button_get_action_type(struct ratbag_button *button)
{
return button->action.type;
}
LIBRATBAG_EXPORT bool
ratbag_button_has_action_type(struct ratbag_button *button,
enum ratbag_button_action_type action_type)
{
switch (action_type) {
case RATBAG_BUTTON_ACTION_TYPE_BUTTON:
case RATBAG_BUTTON_ACTION_TYPE_SPECIAL:
case RATBAG_BUTTON_ACTION_TYPE_KEY:
case RATBAG_BUTTON_ACTION_TYPE_MACRO:
break;
default:
return 0;
}
return !!(button->action_caps & (1 << action_type));
}
LIBRATBAG_EXPORT unsigned int
ratbag_button_get_button(struct ratbag_button *button)
{
if (button->action.type != RATBAG_BUTTON_ACTION_TYPE_BUTTON)
return 0;
return button->action.action.button;
}
void
ratbag_button_set_action(struct ratbag_button *button,
const struct ratbag_button_action *action)
{
struct ratbag_macro *macro = button->action.macro;
button->action = *action;
if (action->type != RATBAG_BUTTON_ACTION_TYPE_MACRO)
button->action.macro = macro;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_button_set_button(struct ratbag_button *button, unsigned int btn)
{
struct ratbag_button_action action = {0};
if (!ratbag_button_has_action_type(button,
RATBAG_BUTTON_ACTION_TYPE_BUTTON))
return RATBAG_ERROR_CAPABILITY;
action.type = RATBAG_BUTTON_ACTION_TYPE_BUTTON;
action.action.button = btn;
ratbag_button_set_action(button, &action);
button->dirty = true;
button->profile->dirty = true;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_button_action_special
ratbag_button_get_special(struct ratbag_button *button)
{
if (button->action.type != RATBAG_BUTTON_ACTION_TYPE_SPECIAL)
return RATBAG_BUTTON_ACTION_SPECIAL_INVALID;
return button->action.action.special;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_button_set_special(struct ratbag_button *button,
enum ratbag_button_action_special act)
{
struct ratbag_button_action action = {0};
/* FIXME: range checks */
if (!ratbag_button_has_action_type(button,
RATBAG_BUTTON_ACTION_TYPE_SPECIAL))
return RATBAG_ERROR_CAPABILITY;
action.type = RATBAG_BUTTON_ACTION_TYPE_SPECIAL;
action.action.special = act;
ratbag_button_set_action(button, &action);
button->dirty = true;
button->profile->dirty = true;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT unsigned int
ratbag_button_get_key(struct ratbag_button *button,
unsigned int *modifiers,
size_t *sz)
{
if (button->action.type != RATBAG_BUTTON_ACTION_TYPE_KEY)
return 0;
/* FIXME: modifiers */
*sz = 0;
return button->action.action.key.key;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_button_set_key(struct ratbag_button *button,
unsigned int key,
unsigned int *modifiers,
size_t sz)
{
struct ratbag_button_action action = {0};
/* FIXME: range checks */
if (!ratbag_button_has_action_type(button,
RATBAG_BUTTON_ACTION_TYPE_KEY))
return RATBAG_ERROR_CAPABILITY;
/* FIXME: modifiers */
action.type = RATBAG_BUTTON_ACTION_TYPE_KEY;
action.action.key.key = key;
ratbag_button_set_action(button, &action);
button->dirty = true;
button->profile->dirty = true;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_button_disable(struct ratbag_button *button)
{
struct ratbag_button_action action = {0};
if (!ratbag_button_has_action_type(button,
RATBAG_BUTTON_ACTION_TYPE_KEY))
return RATBAG_ERROR_CAPABILITY;
action.type = RATBAG_BUTTON_ACTION_TYPE_NONE;
ratbag_button_set_action(button, &action);
button->dirty = true;
button->profile->dirty = true;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT struct ratbag_button *
ratbag_button_ref(struct ratbag_button *button)
{
assert(button->refcount < INT_MAX);
ratbag_profile_ref(button->profile);
button->refcount++;
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)
{
list_remove(&button->link);
if (button->action.macro) {
free(button->action.macro->name);
free(button->action.macro->group);
free(button->action.macro);
}
free(button);
}
static void
ratbag_led_destroy(struct ratbag_led *led)
{
list_remove(&led->link);
free(led);
}
static void
ratbag_resolution_destroy(struct ratbag_resolution *res)
{
list_remove(&res->link);
free(res);
}
LIBRATBAG_EXPORT struct ratbag_button *
ratbag_button_unref(struct ratbag_button *button)
{
if (button == NULL)
return NULL;
assert(button->refcount > 0);
button->refcount--;
ratbag_profile_unref(button->profile);
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 bool
ratbag_led_has_mode(struct ratbag_led *led,
enum ratbag_led_mode mode)
{
assert(mode <= RATBAG_LED_BREATHING);
if (mode == RATBAG_LED_OFF)
return 1;
return !!(led->modes & (1 << mode));
}
LIBRATBAG_EXPORT struct ratbag_color
ratbag_led_get_color(struct ratbag_led *led)
{
return led->color;
}
LIBRATBAG_EXPORT int
ratbag_led_get_effect_duration(struct ratbag_led *led)
{
return led->ms;
}
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)
{
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)
{
led->color = color;
led->dirty = true;
led->profile->dirty = true;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_led_colordepth
ratbag_led_get_colordepth(struct ratbag_led *led)
{
return led->colordepth;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_led_set_effect_duration(struct ratbag_led *led, unsigned int ms)
{
led->ms = ms;
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)
{
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 const char *
ratbag_profile_get_name(struct ratbag_profile *profile)
{
return profile->name;
}
LIBRATBAG_EXPORT int
ratbag_profile_set_name(struct ratbag_profile *profile,
const char *name)
{
char *name_copy;
if (!profile->name)
return RATBAG_ERROR_CAPABILITY;
name_copy = strdup_safe(name);
if (profile->name)
free(profile->name);
profile->name = name_copy;
profile->dirty = true;
return 0;
}
LIBRATBAG_EXPORT void
ratbag_set_user_data(struct ratbag *ratbag, void *userdata)
{
ratbag->userdata = userdata;
}
LIBRATBAG_EXPORT void
ratbag_device_set_user_data(struct ratbag_device *ratbag_device, void *userdata)
{
ratbag_device->userdata = userdata;
}
LIBRATBAG_EXPORT void
ratbag_profile_set_user_data(struct ratbag_profile *ratbag_profile, void *userdata)
{
ratbag_profile->userdata = userdata;
}
LIBRATBAG_EXPORT void
ratbag_button_set_user_data(struct ratbag_button *ratbag_button, void *userdata)
{
ratbag_button->userdata = userdata;
}
LIBRATBAG_EXPORT void
ratbag_resolution_set_user_data(struct ratbag_resolution *ratbag_resolution, void *userdata)
{
ratbag_resolution->userdata = userdata;
}
LIBRATBAG_EXPORT void*
ratbag_get_user_data(const struct ratbag *ratbag)
{
return ratbag->userdata;
}
LIBRATBAG_EXPORT void*
ratbag_device_get_user_data(const struct ratbag_device *ratbag_device)
{
return ratbag_device->userdata;
}
LIBRATBAG_EXPORT void*
ratbag_profile_get_user_data(const struct ratbag_profile *ratbag_profile)
{
return ratbag_profile->userdata;
}
LIBRATBAG_EXPORT void*
ratbag_button_get_user_data(const struct ratbag_button *ratbag_button)
{
return ratbag_button->userdata;
}
LIBRATBAG_EXPORT void*
ratbag_resolution_get_user_data(const struct ratbag_resolution *ratbag_resolution)
{
return ratbag_resolution->userdata;
}
LIBRATBAG_EXPORT struct ratbag_button_macro *
ratbag_button_get_macro(struct ratbag_button *button)
{
struct ratbag_button_macro *macro;
if (button->action.type != RATBAG_BUTTON_ACTION_TYPE_MACRO)
return NULL;
macro = ratbag_button_macro_new(button->action.macro->name);
memcpy(macro->macro.events,
button->action.macro->events,
sizeof(macro->macro.events));
return macro;
}
void
ratbag_button_copy_macro(struct ratbag_button *button,
const struct ratbag_button_macro *macro)
{
if (!button->action.macro)
button->action.macro = zalloc(sizeof(struct ratbag_macro));
else {
free(button->action.macro->name);
free(button->action.macro->group);
memset(button->action.macro, 0, sizeof(struct ratbag_macro));
}
button->action.type = RATBAG_BUTTON_ACTION_TYPE_MACRO;
memcpy(button->action.macro->events,
macro->macro.events,
sizeof(macro->macro.events));
button->action.macro->name = strdup_safe(macro->macro.name);
button->action.macro->group = strdup_safe(macro->macro.group);
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_button_set_macro(struct ratbag_button *button,
const struct ratbag_button_macro *macro)
{
if (!ratbag_button_has_action_type(button,
RATBAG_BUTTON_ACTION_TYPE_MACRO))
return RATBAG_ERROR_CAPABILITY;
ratbag_button_copy_macro(button, macro);
button->dirty = true;
button->profile->dirty = true;
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_button_macro_set_event(struct ratbag_button_macro *m,
unsigned int index,
enum ratbag_macro_event_type type,
unsigned int data)
{
struct ratbag_macro *macro = &m->macro;
if (index >= MAX_MACRO_EVENTS)
return RATBAG_ERROR_VALUE;
switch (type) {
case RATBAG_MACRO_EVENT_KEY_PRESSED:
case RATBAG_MACRO_EVENT_KEY_RELEASED:
macro->events[index].type = type;
macro->events[index].event.key = data;
break;
case RATBAG_MACRO_EVENT_WAIT:
macro->events[index].type = type;
macro->events[index].event.timeout = data;
break;
case RATBAG_MACRO_EVENT_NONE:
macro->events[index].type = type;
break;
default:
return RATBAG_ERROR_VALUE;
}
return 0;
}
LIBRATBAG_EXPORT enum ratbag_macro_event_type
ratbag_button_macro_get_event_type(struct ratbag_button_macro *macro, unsigned int index)
{
if (index >= MAX_MACRO_EVENTS)
return RATBAG_MACRO_EVENT_INVALID;
return macro->macro.events[index].type;
}
LIBRATBAG_EXPORT int
ratbag_button_macro_get_event_key(struct ratbag_button_macro *m, unsigned int index)
{
struct ratbag_macro *macro = &m->macro;
if (index >= MAX_MACRO_EVENTS)
return 0;
if (macro->events[index].type != RATBAG_MACRO_EVENT_KEY_PRESSED &&
macro->events[index].type != RATBAG_MACRO_EVENT_KEY_RELEASED)
return -EINVAL;
return macro->events[index].event.key;
}
LIBRATBAG_EXPORT int
ratbag_button_macro_get_event_timeout(struct ratbag_button_macro *m,
unsigned int index)
{
struct ratbag_macro *macro = &m->macro;
if (index >= MAX_MACRO_EVENTS)
return 0;
if (macro->events[index].type != RATBAG_MACRO_EVENT_WAIT)
return 0;
return macro->events[index].event.timeout;
}
LIBRATBAG_EXPORT unsigned int
ratbag_button_macro_get_num_events(struct ratbag_button_macro *macro)
{
return MAX_MACRO_EVENTS;
}
LIBRATBAG_EXPORT const char *
ratbag_button_macro_get_name(struct ratbag_button_macro *macro)
{
return macro->macro.name;
}
static void
ratbag_button_macro_destroy(struct ratbag_button_macro *macro)
{
assert(macro->refcount == 0);
free(macro->macro.name);
free(macro->macro.group);
free(macro);
}
LIBRATBAG_EXPORT struct ratbag_button_macro *
ratbag_button_macro_ref(struct ratbag_button_macro *macro)
{
assert(macro->refcount < INT_MAX);
macro->refcount++;
return macro;
}
LIBRATBAG_EXPORT struct ratbag_button_macro *
ratbag_button_macro_unref(struct ratbag_button_macro *macro)
{
if (macro == NULL)
return NULL;
assert(macro->refcount > 0);
macro->refcount--;
if (macro->refcount == 0)
ratbag_button_macro_destroy(macro);
return NULL;
}
LIBRATBAG_EXPORT struct ratbag_button_macro *
ratbag_button_macro_new(const char *name)
{
struct ratbag_button_macro *macro;
macro = zalloc(sizeof *macro);
macro->refcount = 1;
macro->macro.name = strdup_safe(name);
return macro;
}
struct ratbag_modifier_mapping {
unsigned int modifier_mask;
unsigned int key;
};
struct ratbag_modifier_mapping ratbag_modifier_mapping[] = {
{ MODIFIER_LEFTCTRL, KEY_LEFTCTRL },
{ MODIFIER_LEFTSHIFT, KEY_LEFTSHIFT },
{ MODIFIER_LEFTALT, KEY_LEFTALT },
{ MODIFIER_LEFTMETA, KEY_LEFTMETA },
{ MODIFIER_RIGHTCTRL, KEY_RIGHTCTRL },
{ MODIFIER_RIGHTSHIFT, KEY_RIGHTSHIFT },
{ MODIFIER_RIGHTALT, KEY_RIGHTALT },
{ MODIFIER_RIGHTMETA, KEY_RIGHTMETA },
};
int
ratbag_button_macro_new_from_keycode(struct ratbag_button *button,
unsigned int key,
unsigned int modifiers)
{
struct ratbag_button_macro *macro;
struct ratbag_modifier_mapping *mapping;
int i;
macro = ratbag_button_macro_new("key");
i = 0;
ARRAY_FOR_EACH(ratbag_modifier_mapping, mapping) {
if (modifiers & mapping->modifier_mask)
ratbag_button_macro_set_event(macro,
i++,
RATBAG_MACRO_EVENT_KEY_PRESSED,
mapping->key);
}
ratbag_button_macro_set_event(macro,
i++,
RATBAG_MACRO_EVENT_KEY_PRESSED,
key);
ratbag_button_macro_set_event(macro,
i++,
RATBAG_MACRO_EVENT_KEY_RELEASED,
key);
ARRAY_FOR_EACH(ratbag_modifier_mapping, mapping) {
if (modifiers & mapping->modifier_mask)
ratbag_button_macro_set_event(macro,
i++,
RATBAG_MACRO_EVENT_KEY_RELEASED,
mapping->key);
}
ratbag_button_copy_macro(button, macro);
ratbag_button_macro_unref(macro);
return 0;
}
int
ratbag_action_macro_num_keys(struct ratbag_button_action *action)
{
struct ratbag_macro *macro = action->macro;
int i, count;
count = 0;
for (i = 0; i < MAX_MACRO_EVENTS; i++) {
struct ratbag_macro_event event;
event = macro->events[i];
if (event.type == RATBAG_MACRO_EVENT_KEY_PRESSED)
{
switch(event.event.key) {
case KEY_LEFTCTRL:
case KEY_LEFTSHIFT:
case KEY_LEFTALT:
case KEY_LEFTMETA:
case KEY_RIGHTCTRL:
case KEY_RIGHTSHIFT:
case KEY_RIGHTALT:
case KEY_RIGHTMETA:
break;
default:
count++;
}
}
}
return count;
}
int
ratbag_action_keycode_from_macro(struct ratbag_button_action *action,
unsigned int *key_out,
unsigned int *modifiers_out)
{
struct ratbag_macro *macro = action->macro;
unsigned int key = KEY_RESERVED;
unsigned int modifiers = 0;
unsigned int i;
if (!macro || action->type != RATBAG_BUTTON_ACTION_TYPE_MACRO)
return -EINVAL;
if (macro->events[0].type == RATBAG_MACRO_EVENT_NONE)
return -EINVAL;
if (ratbag_action_macro_num_keys(action) != 1)
return -EINVAL;
for (i = 0; i < MAX_MACRO_EVENTS; i++) {
struct ratbag_macro_event event;
event = macro->events[i];
switch (event.type) {
case RATBAG_MACRO_EVENT_INVALID:
return -EINVAL;
case RATBAG_MACRO_EVENT_NONE:
return 0;
case RATBAG_MACRO_EVENT_KEY_PRESSED:
switch(event.event.key) {
case KEY_LEFTCTRL: modifiers |= MODIFIER_LEFTCTRL; break;
case KEY_LEFTSHIFT: modifiers |= MODIFIER_LEFTSHIFT; break;
case KEY_LEFTALT: modifiers |= MODIFIER_LEFTALT; break;
case KEY_LEFTMETA: modifiers |= MODIFIER_LEFTMETA; break;
case KEY_RIGHTCTRL: modifiers |= MODIFIER_RIGHTCTRL; break;
case KEY_RIGHTSHIFT: modifiers |= MODIFIER_RIGHTSHIFT; break;
case KEY_RIGHTALT: modifiers |= MODIFIER_RIGHTALT; break;
case KEY_RIGHTMETA: modifiers |= MODIFIER_RIGHTMETA; break;
default:
if (key != KEY_RESERVED)
return -EINVAL;
key = event.event.key;
}
break;
case RATBAG_MACRO_EVENT_KEY_RELEASED:
switch(event.event.key) {
case KEY_LEFTCTRL: modifiers &= ~MODIFIER_LEFTCTRL; break;
case KEY_LEFTSHIFT: modifiers &= ~MODIFIER_LEFTSHIFT; break;
case KEY_LEFTALT: modifiers &= ~MODIFIER_LEFTALT; break;
case KEY_LEFTMETA: modifiers &= ~MODIFIER_LEFTMETA; break;
case KEY_RIGHTCTRL: modifiers &= ~MODIFIER_RIGHTCTRL; break;
case KEY_RIGHTSHIFT: modifiers &= ~MODIFIER_RIGHTSHIFT; break;
case KEY_RIGHTALT: modifiers &= ~MODIFIER_RIGHTALT; break;
case KEY_RIGHTMETA: modifiers &= ~MODIFIER_RIGHTMETA; break;
default:
if (event.event.key != key)
return -EINVAL;
*key_out = key;
*modifiers_out = modifiers;
return 1;
}
case RATBAG_MACRO_EVENT_WAIT:
break;
default:
return -EINVAL;
}
}
return -EINVAL;
}