ratbagd: add ability to load json for test devices

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Peter Hutterer
2019-07-26 16:01:10 +10:00
parent 3a3b11ade4
commit 818e665853
8 changed files with 665 additions and 10 deletions

View File

@@ -84,7 +84,7 @@ fedora_prep_cache: &fedora_prep_cache
steps:
- run:
name: Initializing Fedora dnf cache
command: dnf install -y --downloadonly libsolv tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml libunistring-devel dbus-daemon
command: dnf install -y --downloadonly libsolv tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml libunistring-devel dbus-daemon json-glib-devel
- persist_to_workspace:
root: /var/cache/
paths:
@@ -100,7 +100,7 @@ fedora_install: &fedora_install
command: |
echo keepcache=1 >> /etc/dnf/dnf.conf
dnf upgrade -y libsolv
dnf install -y tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml python3-devel swig libunistring-devel dbus-daemon
dnf install -y tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml python3-devel swig libunistring-devel dbus-daemon json-glib-devel
sed -i 's/systemd//' /etc/nsswitch.conf
fedora_settings: &fedora_settings
@@ -130,7 +130,7 @@ ubuntu_settings: &ubuntu_settings
apt-get remove -y libnss-systemd
add-apt-repository universe
apt-get update
apt-get install -y tree git gcc g++ pkg-config meson check libudev-dev libevdev-dev libsystemd-dev valgrind python3-gi python3-evdev libglib2.0-dev python3-lxml python3-dev swig systemd libunistring-dev
apt-get install -y tree git gcc g++ pkg-config meson check libudev-dev libevdev-dev libsystemd-dev valgrind python3-gi python3-evdev libglib2.0-dev python3-lxml python3-dev swig systemd libunistring-dev libjson-glib-dev
- checkout
- *start_dbus
- *build_and_test

View File

@@ -65,6 +65,7 @@ pkgconfig = import('pkgconfig')
dep_udev = dependency('libudev')
dep_libevdev = dependency('libevdev')
dep_glib = dependency('glib-2.0')
dep_json_glib = dependency('json-glib-1.0')
dep_lm = cc.find_library('m')
dep_unistring = cc.find_library('unistring')
@@ -183,6 +184,7 @@ deps_libratbag = [
dep_udev,
dep_libevdev,
dep_glib,
dep_json_glib,
dep_libutil,
dep_libhidpp,
]
@@ -394,6 +396,8 @@ src_ratbagd = [
'ratbagd/ratbagd-profile.c',
'ratbagd/ratbagd-resolution.c',
'ratbagd/ratbagd-test.c',
'ratbagd/ratbagd-json.c',
'ratbagd/ratbagd-json.h',
'src/libratbag-util.h',
'src/libratbag-util.c',
]

562
ratbagd/ratbagd-json.c Normal file
View File

@@ -0,0 +1,562 @@
/*
* Copyright © 2019 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.
*/
/* JSON parser for ratbag test devices. The JSON format is not fixed and may
change at any time, but basically looks like this:
{
"profiles": [
{
"is_active": bool,
"is_default": bool,
"is_disabled": bool,
"rate": int,
"report_rates": [ int, ...],
"capabilities": [int, ...] # profile capabilities
"resolutions": [
{
"xres": int,
"yres": int,
"dpi_min", int,
"dpi_max", int,
"is_active": bool,
"is_default": bool,
"capabilities": [int, ...] # resolution capabilities
}
]
"buttons": [
"action_type": "<enum>" # none, button, key, special, macro, unknown
"button": int, # if button
"key": int, # if key
"special": "<enum>", # if special, for values see see special_lookup()
"macro": ["+B", "-B", "t400"] # if macro. uppercase only
]
"leds": [ ]
},
{
... next profile ...
}
]
}
ratbagd uses a minimum sane device (1 profile, 1 resolution, etc.) and any
JSON instructions get merged into that device. Which means you usually
only need to set those bits you care about being checked.
*/
#include "libratbag-util.h"
#include "ratbagd-json.h"
#include "ratbagd-test.h"
#include <json-glib/json-glib.h>
#include <libevdev/libevdev.h>
/* Hack because we don't do proper data passing */
static int num_resolutions;
static int num_buttons;
static int num_leds;
static int parse_error = 0;
#define parser_error(element_) { \
log_error("json: parser error: %s:%d: element '%s'\n", __func__, __LINE__, (element_)); \
parse_error = -EINVAL; \
goto out; \
}
static void parse_resolution_member(JsonObject *obj, const gchar *name,
JsonNode *node, void *data)
{
struct ratbag_test_resolution *resolution = data;
if (streq(name, "xres")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 20000)
parser_error("xres");
resolution->xres = v;
log_verbose("json: xres: %d\n", v);
} else if (streq(name, "yres")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 20000)
parser_error("yres");
resolution->yres = v;
log_verbose("json: yres: %d\n", v);
} else if (streq(name, "dpi_min")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 20000)
parser_error("dpi_min");
resolution->dpi_min = v;
log_verbose("json: dpi_min: %d\n", v);
} else if (streq(name, "dpi_max")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 20000)
parser_error("dpi_min");
resolution->dpi_max = v;
log_verbose("json: dpi_max: %d\n", v);
} else if (streq(name, "is_active")) {
gboolean v = json_object_get_boolean_member(obj, name);
resolution->active = v;
log_verbose("json: is_active: %d\n", v);
} else if (streq(name, "is_default")) {
gboolean v = json_object_get_boolean_member(obj, name);
resolution->dflt = v;
log_verbose("json: is_default: %d\n", v);
} else if (streq(name, "capabilities")) {
JsonArray *a = json_object_get_array_member(obj, name);
assert(json_array_get_length(a) < ARRAY_LENGTH(resolution->caps));
for (size_t s = 0; s < json_array_get_length(a); s++) {
int v = json_array_get_int_element(a, s);
if (v > RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION)
parser_error("capabilities");
resolution->caps[s] = v;
}
log_verbose("json: caps: %d %d %d %d %d...\n",
resolution->caps[0],
resolution->caps[1],
resolution->caps[2],
resolution->caps[3],
resolution->caps[4]);
} else {
log_error("json: unknown resolution key '%s'\n", name);
parse_error = -EINVAL;
}
out:
return;
}
static void parse_resolution(JsonNode *node,
struct ratbag_test_resolution *resolution)
{
JsonObject *obj = json_node_get_object(node);
json_object_foreach_member(obj, parse_resolution_member, resolution);
}
static void parse_led_member(JsonObject *obj, const gchar *name,
JsonNode *node, void *data)
{
struct ratbag_test_led *led = data;
if (streq(name, "type")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > RATBAG_LED_TYPE_WHEEL)
parser_error("type");
led->type = v;
log_verbose("json: type: %d\n", v);
} else if (streq(name, "mode")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > RATBAG_LED_BREATHING)
parser_error("mode");
led->mode = v;
log_verbose("json: mode: %d\n", v);
} else if (streq(name, "duration")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 10000)
parser_error("duration");
led->ms = v;
log_verbose("json: duration: %d\n", v);
} else if (streq(name, "brightness")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 100)
parser_error("brightness");
led->brightness = v;
log_verbose("json: brightness: %d\n", v);
} else if (streq(name, "color")) {
JsonArray *a = json_object_get_array_member(obj, name);
assert(json_array_get_length(a) == 3);
led->color.red = json_array_get_int_element(a, 0);
led->color.green = json_array_get_int_element(a, 1);
led->color.blue = json_array_get_int_element(a, 2);
log_verbose("json: color: %02x%02x%02x\n",
led->color.red,
led->color.green,
led->color.blue);
} else {
log_error("json: unknown led key '%s'\n", name);
parse_error = -EINVAL;
}
out:
return;
}
static void parse_led(JsonNode *node, struct ratbag_test_led *led)
{
JsonObject *obj = json_node_get_object(node);
json_object_foreach_member(obj, parse_led_member, led);
}
static enum ratbag_button_action_special
special_lookup(const char *string)
{
const struct {
const char *key;
enum ratbag_button_action_special special;
} lut[] = {
{ "invalid", RATBAG_BUTTON_ACTION_SPECIAL_INVALID},
{ "unknown", RATBAG_BUTTON_ACTION_SPECIAL_UNKNOWN},
{ "doubleclick", RATBAG_BUTTON_ACTION_SPECIAL_DOUBLECLICK},
{ "wheel-left", RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_LEFT},
{ "wheel-right", RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_RIGHT},
{ "wheel-up", RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP},
{ "wheel-down", RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN},
{ "ratchet-mode-switch", RATBAG_BUTTON_ACTION_SPECIAL_RATCHET_MODE_SWITCH},
{ "resolution-cycle-up", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP},
{ "resolution-cycledown", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_DOWN},
{ "resolution-up", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP},
{ "resolution-down", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN},
{ "resolution-alternate", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_ALTERNATE},
{ "resolution-default", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DEFAULT},
{ "profile-cycle-up", RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP},
{ "profile-cycle-down", RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_DOWN},
{ "profile-up", RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_UP},
{ "profile-down", RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_DOWN},
{ "second-mode", RATBAG_BUTTON_ACTION_SPECIAL_SECOND_MODE},
{ "battery-level", RATBAG_BUTTON_ACTION_SPECIAL_BATTERY_LEVEL},
};
for (size_t i = 0; i < ARRAY_LENGTH(lut); i++) {
if (streq(string, lut[i].key))
return lut[i].special;
}
parser_error("special");
out:
return RATBAG_BUTTON_ACTION_SPECIAL_INVALID;
}
static enum ratbag_button_action_type
action_type_lookup(const char *string)
{
const struct {
const char *key;
enum ratbag_button_action_type type;
} lut[] = {
{ "none", RATBAG_BUTTON_ACTION_TYPE_NONE},
{ "button", RATBAG_BUTTON_ACTION_TYPE_BUTTON},
{ "special", RATBAG_BUTTON_ACTION_TYPE_SPECIAL},
{ "key", RATBAG_BUTTON_ACTION_TYPE_KEY},
{ "macro", RATBAG_BUTTON_ACTION_TYPE_MACRO},
{ "unknown", RATBAG_BUTTON_ACTION_TYPE_UNKNOWN},
};
for (size_t i = 0; i < ARRAY_LENGTH(lut); i++) {
if (streq(string, lut[i].key))
return lut[i].type;
}
parser_error("action_type");
out:
return RATBAG_BUTTON_ACTION_TYPE_UNKNOWN;
}
static inline struct ratbag_test_macro_event
parse_macro(const char *m)
{
struct ratbag_test_macro_event event = {
.type = RATBAG_MACRO_EVENT_INVALID,
.value = 0,
};
char keyname[32];
if (strlen(m) < 2)
goto out;
switch(m[0]) {
case '+':
event.type = RATBAG_MACRO_EVENT_KEY_PRESSED;
snprintf(keyname, sizeof(keyname), "KEY_%s", m + 1);
event.value = libevdev_event_code_from_name(EV_KEY,
keyname);
log_verbose("json: macro: +%s\n", keyname);
break;
case '-':
event.type = RATBAG_MACRO_EVENT_KEY_RELEASED;
snprintf(keyname, sizeof(keyname), "KEY_%s", m + 1);
event.value = libevdev_event_code_from_name(EV_KEY,
keyname);
log_verbose("json: macro: -%s\n", keyname);
break;
case 't':
event.type = RATBAG_MACRO_EVENT_WAIT;
event.value = atoi(m+1);
log_verbose("json: macro: t%d\n", event.value);
break;
default:
parser_error("macro");
break;
}
out:
return event;
}
static void parse_button_member(JsonObject *obj, const gchar *name,
JsonNode *node, void *data)
{
struct ratbag_test_button *button = data;
if (streq(name, "action_type")) {
const gchar* v = json_object_get_string_member(obj, name);
button->action_type = action_type_lookup(v);
log_verbose("json: action_type: %s\n", v);
} else if (streq(name, "button")) {
gint v = json_object_get_int_member(obj, name);
if (v < 0 || v > 32)
parser_error("button");
button->button = v;
log_verbose("json: button: %d\n", v);
} else if (streq(name, "key")) {
gint v = json_object_get_int_member(obj, name);
if (v < 0 || v > KEY_MAX)
parser_error("key");
button->key = v;
log_verbose("json: key: %d\n", v);
} else if (streq(name, "special")) {
const gchar *v = json_object_get_string_member(obj, name);
button->special = special_lookup(v);
log_verbose("json: special: %s\n", v);
} else if (streq(name, "macro")) {
JsonArray *a = json_object_get_array_member(obj, name);
assert(json_array_get_length(a) < ARRAY_LENGTH(button->macro));
for (size_t s = 0; s < json_array_get_length(a); s++) {
const gchar *v = json_array_get_string_element(a, s);
button->macro[s] = parse_macro(v);
}
} else {
log_error("json: unknown button key '%s'\n", name);
parse_error = -EINVAL;
}
out:
return;
}
static void parse_button(JsonNode *node, struct ratbag_test_button *button)
{
JsonObject *obj = json_node_get_object(node);
json_object_foreach_member(obj, parse_button_member, button);
}
static void parse_profile_member(JsonObject *obj, const gchar *name,
JsonNode *node, void *data)
{
struct ratbag_test_profile *profile = data;
if (streq(name, "name")) {
const char *v = json_object_get_string_member(obj, name);
if (v == NULL)
parser_error("name");
free(profile->name);
profile->name = strdup_safe(v);
log_verbose("name: %s\n", v);
} else if (streq(name, "is_default")) {
gboolean v = json_object_get_boolean_member(obj, name);
profile->dflt = v;
log_verbose("json: is_default: %d\n", v);
} else if (streq(name, "is_active")) {
gboolean v = json_object_get_boolean_member(obj, name);
profile->active = v;
log_verbose("json: is_active: %d\n", v);
} else if (streq(name, "is_disabled")) {
gboolean v = json_object_get_boolean_member(obj, name);
profile->disabled = v;
log_verbose("json: is_disabled: %d\n", v);
} else if (streq(name, "rate")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 20000)
parser_error("rate");
profile->hz = v;
log_verbose("json: rate: %d\n", v);
} else if (streq(name, "report_rates")) {
JsonArray *a = json_object_get_array_member(obj, name);
assert(json_array_get_length(a) < ARRAY_LENGTH(profile->report_rates));
for (size_t s = 0; s < json_array_get_length(a); s++) {
int v = json_array_get_int_element(a, s);
if (v < 0 || v > 20000)
parser_error("report_rate");
profile->report_rates[s] = v;
}
log_verbose("json: report rates: %d %d %d %d %d\n",
profile->report_rates[0],
profile->report_rates[1],
profile->report_rates[2],
profile->report_rates[3],
profile->report_rates[4]);
} else if (streq(name, "capabilities")) {
JsonArray *a = json_object_get_array_member(obj, name);
assert(json_array_get_length(a) < ARRAY_LENGTH(profile->caps));
for (size_t s = 0; s < json_array_get_length(a); s++) {
int v = json_array_get_int_element(a, s);
profile->caps[s] = v;
}
log_verbose("json: caps: %d %d %d %d %d...\n",
profile->caps[0],
profile->caps[1],
profile->caps[2],
profile->caps[3],
profile->caps[4]);
} else if (streq(name, "resolutions")) {
JsonArray *a = json_object_get_array_member(obj, name);
GList *list = json_array_get_elements(a);
GList *l = list;
int idx = 0;
while (l != NULL) {
log_verbose("json: processing resolution %d\n", idx);
parse_resolution(l->data, &profile->resolutions[idx]);
l = g_list_next(l);
idx++;
}
g_list_free(list);
num_resolutions = max(idx, num_resolutions);
} else if (streq(name, "leds")) {
JsonArray *a = json_object_get_array_member(obj, name);
GList *list = json_array_get_elements(a);
GList *l = list;
int idx = 0;
while (l != NULL) {
log_verbose("json: processing LED %d\n", idx);
parse_led(l->data, &profile->leds[idx]);
l = g_list_next(l);
idx++;
}
g_list_free(list);
num_leds = max(idx, num_leds);
} else if (streq(name, "buttons")) {
JsonArray *a = json_object_get_array_member(obj, name);
GList *list = json_array_get_elements(a);
GList *l = list;
int idx = 0;
while (l != NULL) {
log_verbose("json: processing button %d\n", idx);
parse_button(l->data, &profile->buttons[idx]);
l = g_list_next(l);
idx++;
}
g_list_free(list);
num_buttons = max(idx, num_buttons);
} else {
log_error("json: unknown profile key '%s'\n", name);
parse_error = -EINVAL;
}
out:
return;
}
static void parse_profile(JsonNode *node,
struct ratbag_test_profile *profile)
{
JsonObject *obj = json_node_get_object(node);
json_object_foreach_member(obj, parse_profile_member, profile);
}
/* declared here because this isn't really public API, we just need to
* access it from the tests */
int ratbagd_parse_json(const char *data, struct ratbag_test_device *device)
{
g_autoptr(JsonParser) parser = NULL;
JsonNode *root;
JsonObject *obj;
JsonArray *arr;
int r = -EINVAL;
g_autoptr(GError) error = NULL;
error = 0;
parser = json_parser_new();
if (!json_parser_load_from_data(parser, data, strlen(data), &error)) {
log_error("Failed to load JSON: %s\n", error->message);
r = -EINVAL;
goto out;
}
log_verbose("json: data: %s\n", data);
root = json_parser_get_root(parser);
if (JSON_NODE_TYPE(root) != JSON_NODE_OBJECT)
parser_error("root");
obj = json_node_get_object(root);
arr = json_object_get_array_member(obj, "profiles");
if (!arr)
parser_error("profiles");
/* Our test device is preloaded with sane defaults, let's keep those */
num_resolutions = device->num_resolutions;
num_buttons = device->num_buttons;
num_leds = device->num_leds;
g_autoptr(GList) list = json_array_get_elements(arr);
GList *l = list;
int idx = 0;
while (l != NULL) {
log_verbose("json: processing profile %d\n", idx);
parse_profile(l->data, &device->profiles[idx]);
if (parse_error != 0)
goto out;;
l = g_list_next(l);
idx++;
}
device->num_profiles = idx;
device->num_resolutions = num_resolutions;
device->num_buttons = num_buttons;
device->num_leds = num_leds;
r = 0;
out:
return r;
}

28
ratbagd/ratbagd-json.h Normal file
View File

@@ -0,0 +1,28 @@
/*
* Copyright © 2019 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.
*/
#pragma once
#include "libratbag-test.h"
int ratbagd_parse_json(const char *data, struct ratbag_test_device *device);

View File

@@ -30,6 +30,7 @@
#include <systemd/sd-event.h>
#include "libratbag-test.h"
#include "ratbagd-json.h"
/* A pre-setup sane device. Use for sanity testing by toggling the various
* error conditions.
@@ -173,14 +174,12 @@ static const struct ratbag_test_device ratbagd_test_device_descr = {
.destroyed_data = NULL,
};
int ratbagd_reset_test_device(sd_bus_message *m,
void *userdata,
sd_bus_error *error)
static int load_test_device(sd_bus_message *m,
struct ratbagd *ctx,
const struct ratbag_test_device *source)
{
static int count;
static struct ratbagd_device *ratbagd_test_device = NULL;
struct ratbagd *ctx = userdata;
struct ratbag_device *device;
int r;
char devicename[64];
@@ -196,7 +195,7 @@ int ratbagd_reset_test_device(sd_bus_message *m,
NULL);
}
device = ratbag_device_new_test_device(ctx->lib_ctx, &ratbagd_test_device_descr);
device = ratbag_device_new_test_device(ctx->lib_ctx, source);
snprintf(devicename, sizeof(devicename), "testdevice%d", count++);
r = ratbagd_device_new(&ratbagd_test_device, ctx, devicename, device);
@@ -222,6 +221,64 @@ int ratbagd_reset_test_device(sd_bus_message *m,
return 0;
}
int ratbagd_reset_test_device(sd_bus_message *m,
void *userdata,
sd_bus_error *error)
{
struct ratbagd *ctx = userdata;
return load_test_device(m, ctx, &ratbagd_test_device_descr);
}
static const struct ratbag_test_device default_device_descr = {
.num_profiles = 1,
.num_resolutions = 1,
.num_buttons = 1,
.num_leds = 0,
.profiles = {
{
.name = NULL,
.buttons = {
{ .button_type = RATBAG_BUTTON_TYPE_LEFT,
.action_type = RATBAG_BUTTON_ACTION_TYPE_BUTTON,
.button = 0 },
},
.resolutions = {
{ .xres = 1000, .yres = 1000,
.dpi_min = 1000, .dpi_max = 1000},
},
.disabled = false,
.active = true,
.dflt = true,
.hz = 1000,
.report_rates = {1000},
},
},
.destroyed = NULL,
.destroyed_data = NULL,
};
int ratbagd_load_test_device(sd_bus_message *m,
void *userdata,
sd_bus_error *error)
{
struct ratbagd *ctx = userdata;
struct ratbag_test_device td = default_device_descr;
char *data;
int r = 0;
CHECK_CALL(sd_bus_message_read(m, "s", &data));
r = ratbagd_parse_json(data, &td);
if (r != 0) {
log_error("Failed to parse JSON data\n");
} else {
r = load_test_device(m, ctx, &td);
}
return sd_bus_reply_method_return(m, "i", r);
}
#endif
void ratbagd_init_test_device(struct ratbagd *ctx)

View File

@@ -32,4 +32,7 @@ void ratbagd_init_test_device(struct ratbagd *ctx);
int ratbagd_reset_test_device(sd_bus_message *m,
void *userdata,
sd_bus_error *error);
int ratbagd_load_test_device(sd_bus_message *m,
void *userdata,
sd_bus_error *error);
#endif /* RATBAG_DEVELOPER_EDITION */

View File

@@ -169,6 +169,7 @@ static const sd_bus_vtable ratbagd_vtable[] = {
SD_BUS_PROPERTY("Devices", "ao", ratbagd_get_devices, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
#ifdef RATBAG_DEVELOPER_EDITION
SD_BUS_METHOD("ResetTestDevice", "", "u", ratbagd_reset_test_device, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("LoadTestDevice", "s", "i", ratbagd_load_test_device, SD_BUS_VTABLE_UNPRIVILEGED),
#endif /* RATBAG_DEVELOPER_EDITION */
SD_BUS_VTABLE_END,
};

View File

@@ -73,7 +73,7 @@ struct ratbag_test_led {
};
struct ratbag_test_profile {
const char *name;
char *name;
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];