From f9efd40c9de45bf74a870d9a029c1ffac9ef187c Mon Sep 17 00:00:00 2001 From: project-repo Date: Wed, 15 Sep 2021 08:37:50 +0200 Subject: [PATCH] Implement libinput device configuration --- cagebreak.c | 5 +- input.h | 8 +- input_manager.c | 87 ++++++++-------- input_manager.h | 10 +- keybinding.c | 20 ++++ keybinding.h | 4 + libinput.c | 147 +++++++++++++++++++-------- meson.build | 3 + parse.c | 264 ++++++++++++++++++++++++++++++++++++++++++++---- 9 files changed, 430 insertions(+), 118 deletions(-) diff --git a/cagebreak.c b/cagebreak.c index 7794b40..90e767b 100644 --- a/cagebreak.c +++ b/cagebreak.c @@ -1,4 +1,4 @@ - * + /* * Cagebreak: A Wayland tiling compositor. * * Copyright (C) 2018-2020 Jente Hidskes @@ -362,8 +362,6 @@ main(int argc, char *argv[]) { } server.input = input_manager_create(&server); - fprintf(stderr, "input_manager_create:%d\n",wl_list_length(&server.input->devices)); - data_control_manager = wlr_data_control_manager_v1_create(server.wl_display); @@ -599,6 +597,7 @@ end: wl_display_destroy(server.wl_display); wlr_output_layout_destroy(server.output_layout); + free(server.input); pango_cairo_font_map_set_default(NULL); cairo_debug_reset_static_data(); FcFini(); diff --git a/input.h b/input.h index c2ba243..139fadf 100644 --- a/input.h +++ b/input.h @@ -1,12 +1,16 @@ -#ifndef _LIBINPUT_H -#define _LIBINPUT_H +#ifndef _CG_INPUT_H +#define _CG_INPUT_H #include struct cg_input_device; +struct cg_input_config; +struct cg_server; void cg_input_configure_libinput_device(struct cg_input_device *device); +void cg_input_apply_config(struct cg_input_config *config, struct cg_server *server); + void cg_input_reset_libinput_device(struct cg_input_device *device); bool cg_libinput_device_is_builtin(struct cg_input_device *device); diff --git a/input_manager.c b/input_manager.c index 8232891..1a2646d 100644 --- a/input_manager.c +++ b/input_manager.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -210,58 +211,50 @@ struct cg_input_manager *input_manager_create(struct cg_server *server) { return input; } -static bool device_is_touchpad(struct cg_input_device *device) { - if (device->wlr_device->type != WLR_INPUT_DEVICE_POINTER || - !wlr_input_device_is_libinput(device->wlr_device)) { - return false; +uint32_t get_mouse_bindsym(const char *name, char **error) { + // Get event code from name + int code = libevdev_event_code_from_name(EV_KEY, name); + if (code == -1) { + size_t len = snprintf(NULL, 0, "Unknown event %s", name) + 1; + *error = malloc(len); + if (*error) { + snprintf(*error, len, "Unknown event %s", name); + } + return 0; } - - struct libinput_device *libinput_device = - wlr_libinput_get_device_handle(device->wlr_device); - - return libinput_device_config_tap_get_finger_count(libinput_device) > 0; + return code; } -const char *input_device_get_type(struct cg_input_device *device) { - switch (device->wlr_device->type) { - case WLR_INPUT_DEVICE_POINTER: - if (device_is_touchpad(device)) { - return "touchpad"; - } else { - return "pointer"; - } - case WLR_INPUT_DEVICE_KEYBOARD: - return "keyboard"; - case WLR_INPUT_DEVICE_TOUCH: - return "touch"; - case WLR_INPUT_DEVICE_TABLET_TOOL: - return "tablet_tool"; - case WLR_INPUT_DEVICE_TABLET_PAD: - return "tablet_pad"; - case WLR_INPUT_DEVICE_SWITCH: - return "switch"; +uint32_t get_mouse_bindcode(const char *name, char **error) { + // Validate event code + errno = 0; + char *endptr; + int code = strtol(name, &endptr, 10); + if (endptr == name && code <= 0) { + *error = strdup("Button event code must be a positive integer."); + return 0; + } else if (errno == ERANGE) { + *error = strdup("Button event code out of range."); + return 0; } - return "unknown"; + const char *event = libevdev_event_code_get_name(EV_KEY, code); + if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { + size_t len = snprintf(NULL, 0, "Event code %d (%s) is not a button", + code, event ? event : "(null)") + 1; + *error = malloc(len); + if (*error) { + snprintf(*error, len, "Event code %d (%s) is not a button", + code, event ? event : "(null)"); + } + return 0; + } + return code; } -struct cg_input_config *input_device_get_config(struct cg_input_device *device) { - struct cg_server *server=device->server; - struct cg_input_config *wildcard_config = NULL; - struct cg_input_config *config = NULL; - wl_list_for_each(config, &server->input_config, link) { - if (strcmp(config->identifier, device->identifier) == 0) { - return config; - } else if (strcmp(config->identifier, "*") == 0) { - wildcard_config = config; - } +uint32_t input_manager_get_mouse_button(const char *name, char **error) { + uint32_t button = get_mouse_bindsym(name, error); + if (!button && !*error) { + button = get_mouse_bindcode(name, error); } - - const char *device_type = input_device_get_type(device); - wl_list_for_each(config, &server->input_config, link) { - if (strncmp(config->identifier,"type:",5)&&strcmp(config->identifier + 5, device_type) == 0) { - return config; - } - } - - return wildcard_config; + return button; } diff --git a/input_manager.h b/input_manager.h index d0bd3ff..de8e69b 100644 --- a/input_manager.h +++ b/input_manager.h @@ -10,6 +10,7 @@ struct cg_input_manager *input_manager_create(struct cg_server *server); struct cg_input_config *input_device_get_config(struct cg_input_device *device); void input_manager_handle_device_destroy(struct wl_listener *listener, void *data); +uint32_t input_manager_get_mouse_button(const char *name, char **error); struct cg_input_manager { struct wl_list devices; @@ -46,7 +47,6 @@ enum cg_input_config_mapped_to { */ struct cg_input_config { char *identifier; - const char *input_type; int accel_profile; struct calibration_matrix calibration_matrix; @@ -67,7 +67,7 @@ struct cg_input_config { int tap; int tap_button_map; - char *xkb_layout; +/* char *xkb_layout; char *xkb_model; char *xkb_options; char *xkb_rules; @@ -77,16 +77,16 @@ struct cg_input_config { bool xkb_file_is_set; int xkb_numlock; - int xkb_capslock; + int xkb_capslock;*/ struct wl_list link; struct cg_input_config_mapped_from_region *mapped_from_region; enum cg_input_config_mapped_to mapped_to; char *mapped_to_output; - struct wlr_box *mapped_to_region; + /*struct wlr_box *mapped_to_region;*/ - struct wl_list tools; + /*struct wl_list tools;*/ bool capturable; struct wlr_box region; diff --git a/keybinding.c b/keybinding.c index 4eec370..5ffee81 100644 --- a/keybinding.c +++ b/keybinding.c @@ -12,6 +12,7 @@ #include "keybinding.h" #include "message.h" #include "output.h" +#include "input.h" #include "input_manager.h" #include "seat.h" #include "server.h" @@ -64,6 +65,16 @@ keybinding_free(struct keybinding *keybinding, bool recursive) { case KEYBINDING_CONFIGURE_OUTPUT: free(keybinding->data.o_cfg->output_name); free(keybinding->data.o_cfg); + break; + case KEYBINDING_CONFIGURE_INPUT: + free(keybinding->data.i_cfg->identifier); + if(keybinding->data.i_cfg->mapped_from_region) { + free(keybinding->data.i_cfg->mapped_from_region); + } + if(keybinding->data.i_cfg->mapped_to_output) { + free(keybinding->data.i_cfg->mapped_to_output); + } + free(keybinding->data.o_cfg); default: break; } @@ -871,6 +882,12 @@ keybinding_configure_output(struct cg_server *server, } } +void +keybinding_configure_input(struct cg_server *server, + struct cg_input_config *cfg) { + cg_input_apply_config(cfg,server); +} + void keybinding_configure_input_dev(struct cg_server *server, struct cg_input_config *cfg) { @@ -1001,6 +1018,9 @@ run_action(enum keybinding_action action, struct cg_server *server, case KEYBINDING_CONFIGURE_OUTPUT: keybinding_configure_output(server, data.o_cfg); break; + case KEYBINDING_CONFIGURE_INPUT: + keybinding_configure_input(server, data.i_cfg); + break; case KEYBINDING_CLOSE_VIEW: keybinding_close_view( server->curr_output->workspaces[server->curr_output->curr_workspace] diff --git a/keybinding.h b/keybinding.h index 705a4ab..8db9de4 100644 --- a/keybinding.h +++ b/keybinding.h @@ -5,6 +5,7 @@ #include "config.h" #include +#include #include struct cg_server; @@ -24,6 +25,8 @@ enum keybinding_action { KEYBINDING_CYCLE_OUTPUT, // data.b is 0 if forward, 1 if reverse KEYBINDING_CONFIGURE_OUTPUT, // data.o_cfg is the desired output // configuration + KEYBINDING_CONFIGURE_INPUT, // data.i_cfg is the desired input + // configuration KEYBINDING_QUIT, KEYBINDING_NOOP, KEYBINDING_SWITCH_WORKSPACE, // data.u is the desired workspace @@ -61,6 +64,7 @@ union keybinding_params { float color[3]; struct keybinding *kb; struct cg_output_config *o_cfg; + struct cg_input_config *i_cfg; }; struct keybinding { diff --git a/libinput.c b/libinput.c index d5a3bba..fd9ce4c 100644 --- a/libinput.c +++ b/libinput.c @@ -207,76 +207,137 @@ struct cg_output *output_by_name_or_id(const char *name_or_id, struct cg_server return NULL; } -void cg_input_configure_libinput_device(struct cg_input_device *input_device) { - struct cg_input_config *ic = input_device_get_config(input_device); - if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) { +static bool device_is_touchpad(struct cg_input_device *device) { + if (device->wlr_device->type != WLR_INPUT_DEVICE_POINTER || + !wlr_input_device_is_libinput(device->wlr_device)) { + return false; + } + + struct libinput_device *libinput_device = + wlr_libinput_get_device_handle(device->wlr_device); + + return libinput_device_config_tap_get_finger_count(libinput_device) > 0; +} + +const char *input_device_get_type(struct cg_input_device *device) { + switch (device->wlr_device->type) { + case WLR_INPUT_DEVICE_POINTER: + if (device_is_touchpad(device)) { + return "touchpad"; + } else { + return "pointer"; + } + case WLR_INPUT_DEVICE_KEYBOARD: + return "keyboard"; + case WLR_INPUT_DEVICE_TOUCH: + return "touch"; + case WLR_INPUT_DEVICE_TABLET_TOOL: + return "tablet_tool"; + case WLR_INPUT_DEVICE_TABLET_PAD: + return "tablet_pad"; + case WLR_INPUT_DEVICE_SWITCH: + return "switch"; + } + return "unknown"; +} + +void apply_config_to_device(struct cg_input_config *config, struct cg_input_device *input_device) { + if (!wlr_input_device_is_libinput(input_device->wlr_device)) { return; } struct libinput_device *device = wlr_libinput_get_device_handle(input_device->wlr_device); - wlr_log(WLR_DEBUG, "cg_input_configure_libinput_device('%s' on '%s')", - ic->identifier, input_device->identifier); - - bool changed = false; - if (ic->mapped_to_output && - !output_by_name_or_id(ic->mapped_to_output,input_device->server)) { + if (config->mapped_to_output && + !output_by_name_or_id(config->mapped_to_output,input_device->server)) { wlr_log(WLR_DEBUG, - "%s '%s' is mapped to offline output '%s'; disabling input", - ic->input_type, ic->identifier, ic->mapped_to_output); - changed |= set_send_events(device, + "'%s' is mapped to offline output '%s'; disabling input", + config->identifier, config->mapped_to_output); + set_send_events(device, LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); - } else if (ic->send_events != INT_MIN) { - changed |= set_send_events(device, ic->send_events); + } else if (config->send_events != INT_MIN) { + set_send_events(device, config->send_events); } else { // Have to reset to the default mode here, otherwise if ic->send_events // is unset and a mapped output just came online after being disabled, // we'd remain stuck sending no events. - changed |= set_send_events(device, + set_send_events(device, libinput_device_config_send_events_get_default_mode(device)); } - if (ic->tap != INT_MIN) { - changed |= set_tap(device, ic->tap); + if (config->tap != INT_MIN) { + set_tap(device, config->tap); } - if (ic->tap_button_map != INT_MIN) { - changed |= set_tap_button_map(device, ic->tap_button_map); + if (config->tap_button_map != INT_MIN) { + set_tap_button_map(device, config->tap_button_map); } - if (ic->drag != INT_MIN) { - changed |= set_tap_drag(device, ic->drag); + if (config->drag != INT_MIN) { + set_tap_drag(device, config->drag); } - if (ic->drag_lock != INT_MIN) { - changed |= set_tap_drag_lock(device, ic->drag_lock); + if (config->drag_lock != INT_MIN) { + set_tap_drag_lock(device, config->drag_lock); } - if (ic->pointer_accel != FLT_MIN) { - changed |= set_accel_speed(device, ic->pointer_accel); + if (config->pointer_accel != FLT_MIN) { + set_accel_speed(device, config->pointer_accel); } - if (ic->accel_profile != INT_MIN) { - changed |= set_accel_profile(device, ic->accel_profile); + if (config->accel_profile != INT_MIN) { + set_accel_profile(device, config->accel_profile); } - if (ic->natural_scroll != INT_MIN) { - changed |= set_natural_scroll(device, ic->natural_scroll); + if (config->natural_scroll != INT_MIN) { + set_natural_scroll(device, config->natural_scroll); } - if (ic->left_handed != INT_MIN) { - changed |= set_left_handed(device, ic->left_handed); + if (config->left_handed != INT_MIN) { + set_left_handed(device, config->left_handed); } - if (ic->click_method != INT_MIN) { - changed |= set_click_method(device, ic->click_method); + if (config->click_method != INT_MIN) { + set_click_method(device, config->click_method); } - if (ic->middle_emulation != INT_MIN) { - changed |= set_middle_emulation(device, ic->middle_emulation); + if (config->middle_emulation != INT_MIN) { + set_middle_emulation(device, config->middle_emulation); } - if (ic->scroll_method != INT_MIN) { - changed |= set_scroll_method(device, ic->scroll_method); + if (config->scroll_method != INT_MIN) { + set_scroll_method(device, config->scroll_method); } - if (ic->scroll_button != INT_MIN) { - changed |= set_scroll_button(device, ic->scroll_button); + if (config->scroll_button != INT_MIN) { + set_scroll_button(device, config->scroll_button); } - if (ic->dwt != INT_MIN) { - changed |= set_dwt(device, ic->dwt); + if (config->dwt != INT_MIN) { + set_dwt(device, config->dwt); } - if (ic->calibration_matrix.configured) { - changed |= set_calibration_matrix(device, ic->calibration_matrix.matrix); + if (config->calibration_matrix.configured) { + set_calibration_matrix(device, config->calibration_matrix.matrix); + } +} + +void cg_input_apply_config(struct cg_input_config *config, struct cg_server *server) { + struct cg_input_device *device=NULL; + wl_list_for_each(device,&server->input->devices,link) { + if (strcmp(config->identifier, device->identifier) != 0&&strcmp(config->identifier, "*") != 0) { + continue; + } + + const char *device_type = input_device_get_type(device); + if (strncmp(config->identifier,"type:",5)==0&&strcmp(config->identifier + 5, device_type) != 0) { + continue; + } + apply_config_to_device(config,device); + } +} + +void cg_input_configure_libinput_device(struct cg_input_device *input_device) { + struct cg_server *server=input_device->server; + struct cg_input_config *config = NULL; + + wl_list_for_each(config, &server->input_config, link) { + if (strcmp(config->identifier, input_device->identifier) != 0&&strcmp(config->identifier, "*") != 0) { + continue; + } + + const char *device_type = input_device_get_type(input_device); + if (!(strncmp(config->identifier,"type:",5)&&strcmp(config->identifier + 5, device_type) == 0)) { + continue; + } + apply_config_to_device(config,input_device); } } diff --git a/meson.build b/meson.build index 872ae1b..7ab216b 100644 --- a/meson.build +++ b/meson.build @@ -65,6 +65,7 @@ pango = dependency('pango') pangocairo = dependency('pangocairo') fontconfig = dependency('fontconfig') libinput = dependency('libinput') +libevdev = dependency('libevdev') libudev = dependency('libudev') math = cc.find_library('m') @@ -191,6 +192,7 @@ cagebreak_dependencies_dict = { 'xkbcommon': [xkbcommon,true], 'fontconfig': [fontconfig,true], 'libinput': [libinput,true], + 'libevdev': [libevdev,true], 'libudev': [libudev,true], 'pixman': [pixman,true], 'pango': [pango,true], @@ -208,6 +210,7 @@ reproducible_build_versions = { 'xkbcommon': '1.3.0', 'fontconfig': '2.13.94', 'libinput': '2.13.94', + 'libevdev': '2.13.94', 'libudev': '2.13.94', 'pixman': '0.40.0', 'pango': '1.48.7', diff --git a/parse.c b/parse.c index 46b275a..3faccbd 100644 --- a/parse.c +++ b/parse.c @@ -1,11 +1,14 @@ #define _POSIX_C_SOURCE 200812L #include +#include #include #include +#include #include "keybinding.h" #include "output.h" +#include "input_manager.h" #include "parse.h" #include "server.h" @@ -106,6 +109,242 @@ parse_keybinding(struct cg_server *server, char **saveptr, char **errstr) { return keybinding; } +float +parse_float(char **saveptr, const char *delim) { + char *uint_str = strtok_r(NULL, delim, saveptr); + if(uint_str == NULL) { + wlr_log(WLR_ERROR, "Expected a float, got nothing"); + return -1; + } + float ufloat = strtof(uint_str, NULL); + if(ufloat != NAN && ufloat != INFINITY && errno!=ERANGE) { + return ufloat; + } else { + wlr_log(WLR_ERROR, "Error parsing float"); + return FLT_MIN; + } +} + +struct cg_input_config * +parse_input_config(char **saveptr, char **errstr) { + struct cg_input_config *cfg = calloc(1,sizeof(struct cg_input_config)); + char *value=NULL; + char *ident = NULL; + if(cfg == NULL) { + *errstr = + log_error("Failed to allocate memory for output configuration"); + goto error; + } + + cfg->tap = INT_MIN; + cfg->tap_button_map = INT_MIN; + cfg->drag = INT_MIN; + cfg->drag_lock = INT_MIN; + cfg->dwt = INT_MIN; + cfg->send_events = INT_MIN; + cfg->click_method = INT_MIN; + cfg->middle_emulation = INT_MIN; + cfg->natural_scroll = INT_MIN; + cfg->accel_profile = INT_MIN; + cfg->pointer_accel = FLT_MIN; + cfg->scroll_factor = FLT_MIN; + cfg->scroll_button = INT_MIN; + cfg->scroll_method = INT_MIN; + cfg->left_handed = INT_MIN; + cfg->repeat_delay = INT_MIN; + cfg->repeat_rate = INT_MIN; + /*cfg->xkb_numlock = INT_MIN; + cfg->xkb_capslock = INT_MIN; + cfg->xkb_file_is_set = false; + wl_list_init(&cfg->tools);*/ + + ident = strtok_r(NULL, " ", saveptr); + + if(ident == NULL) { + *errstr = log_error("Expected identifier of input device to configure, got none"); + goto error; + } + cfg->identifier=strdup(ident); + + char *setting = strtok_r(NULL, " ", saveptr); + if(setting == NULL) { + *errstr = + log_error("Expected setting to be set on input device, got none"); + goto error; + } + + value = strdup(*saveptr); + + if(value == NULL) { + *errstr = + log_error("Failed to obtain value for input device configuration of device \"%s\"",ident); + goto error; + } + + if(strcmp(setting,"accel_profile") == 0) { + if(strcmp(value,"adaptive") == 0) { + cfg->accel_profile=LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; + } else if(strcmp(value,"flat") == 0) { + cfg->accel_profile=LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT; + } else { + *errstr = log_error("Invalid profile \"%s\" for accel_profile configuration", value); + goto error; + } + } else if(strcmp(setting,"calibration_matrix") == 0) { + cfg->calibration_matrix.configured=true; + for(int i=0;i<6;++i) { + cfg->calibration_matrix.matrix[i]=parse_float(saveptr," "); + if(cfg->calibration_matrix.matrix[i]==FLT_MIN) { + *errstr = log_error("Failed to read calibration matrix, expected 6 floating point values separated by spaces"); + goto error; + } + } + } else if(strcmp(setting,"click_method") == 0) { + if(strcmp(value,"none")) { + cfg->click_method=LIBINPUT_CONFIG_CLICK_METHOD_NONE; + } else if(strcmp(value,"button_areas")) { + cfg->click_method=LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; + } else if(strcmp(value,"clickfinger")) { + cfg->click_method=LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; + } else { + *errstr = log_error("Invalid method \"%s\" for click_method configuration", value); + goto error; + } + } else if(strcmp(setting,"drag") == 0) { + if(strcmp(value,"enabled") == 0) { + cfg->drag=LIBINPUT_CONFIG_DRAG_ENABLED; + } else if(strcmp(value,"disabled") == 0) { + cfg->drag=LIBINPUT_CONFIG_DRAG_DISABLED; + } else { + *errstr = log_error("Invalid option \"%s\" to setting \"drag\"", value); + goto error; + } + } else if(strcmp(setting,"drag_lock") == 0) { + if(strcmp(value,"enabled") == 0) { + cfg->drag_lock=LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; + } else if(strcmp(value,"disabled") == 0) { + cfg->drag_lock=LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; + } else { + *errstr = log_error("Invalid option \"%s\" to setting \"drag_lock\"", value); + goto error; + } + } else if(strcmp(setting,"dwt") == 0) { + if(strcmp(value,"enabled") == 0) { + cfg->dwt=LIBINPUT_CONFIG_DWT_ENABLED; + } else if(strcmp(value,"disabled") == 0) { + cfg->dwt=LIBINPUT_CONFIG_DWT_DISABLED; + } else { + *errstr = log_error("Invalid option \"%s\" to setting \"dwt\"", value); + goto error; + } + } else if(strcmp(setting,"events") == 0) { + if(strcmp(value,"enabled") == 0) { + cfg->send_events=LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; + } else if(strcmp(value,"disabled") == 0) { + cfg->send_events=LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; + } else if(strcmp(value,"disabled_on_external_mouse") == 0) { + cfg->send_events=LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; + } else { + *errstr = log_error("Invalid option \"%s\" to setting \"events\"", value); + goto error; + } + } else if(strcmp(setting,"left_handed") == 0) { + if(strcmp(value,"enabled") == 0) { + cfg->left_handed=true; + } else if(strcmp(value,"disabled") == 0) { + cfg->left_handed=false; + } else { + *errstr = log_error("Invalid option \"%s\" to setting \"left_handed\"", value); + goto error; + } + } else if(strcmp(setting,"middle_emulation") == 0) { + if(strcmp(value,"enabled") == 0) { + cfg->middle_emulation=LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED; + } else if(strcmp(value,"disabled") == 0) { + cfg->middle_emulation=LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; + } else { + *errstr = log_error("Invalid option \"%s\" to setting \"middle_emulation\"", value); + goto error; + } + } else if(strcmp(setting,"natural_scroll") == 0) { + if(strcmp(value,"enabled") == 0) { + cfg->natural_scroll=true; + } else if(strcmp(value,"disabled") == 0) { + cfg->natural_scroll=false; + } else { + *errstr = log_error("Invalid option \"%s\" to setting \"natural_scroll\"", value); + goto error; + } + } else if(strcmp(setting,"pointer_accel") == 0) { + cfg->pointer_accel=parse_float(saveptr, " "); + if(cfg->pointer_accel==FLT_MIN) { + *errstr = log_error("Invalid option \"%s\" to setting \"pointer_accel\", expected float value", value); + goto error; + } + } else if(strcmp(setting,"scroll_button") == 0) { + char *err=NULL; + cfg->scroll_button=input_manager_get_mouse_button(value,&err); + if(err) { + *errstr = log_error("Error parsing button for \"scroll_button\" setting. Returned error \"%s\"", err); + goto error; + } + } else if(strcmp(setting,"scroll_factor") == 0) { + cfg->scroll_factor=parse_float(saveptr, " "); + if(cfg->scroll_factor==FLT_MIN) { + *errstr = log_error("Invalid option \"%s\" to setting \"scroll_factor\", expected float value", value); + goto error; + } + } else if(strcmp(setting,"scroll_method") == 0) { + if(strcmp(value,"none") == 0) { + cfg->scroll_method=LIBINPUT_CONFIG_SCROLL_NO_SCROLL; + } else if(strcmp(value,"two_finger") == 0) { + cfg->scroll_method=LIBINPUT_CONFIG_SCROLL_2FG; + } else if(strcmp(value,"edge") == 0) { + cfg->scroll_method=LIBINPUT_CONFIG_SCROLL_EDGE; + } else if(strcmp(value,"on_button_down") == 0) { + cfg->scroll_method=LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; + } else { + *errstr = log_error("Invalid option \"%s\" to setting \"scroll_method\"", value); + goto error; + } + } else if(strcmp(setting,"tap") == 0) { + if(strcmp(value,"enabled") == 0) { + cfg->tap=LIBINPUT_CONFIG_TAP_ENABLED; + } else if(strcmp(value,"disabled") == 0) { + cfg->tap=LIBINPUT_CONFIG_TAP_DISABLED; + } else { + *errstr = log_error("Invalid option \"%s\" to setting \"tap\"", value); + goto error; + } + } else if(strcmp(setting,"tap_button_map") == 0) { + if(strcmp(value,"lrm") == 0) { + cfg->tap=LIBINPUT_CONFIG_TAP_MAP_LRM; + } else if(strcmp(value,"lmr") == 0) { + cfg->tap=LIBINPUT_CONFIG_TAP_MAP_LMR; + } else { + *errstr = log_error("Invalid option \"%s\" to setting \"tap_button_map\"", value); + goto error; + } + } + + free(value); + return cfg; + +error: + if(cfg) { + if(cfg->identifier) { + free(cfg->identifier); + } + free(cfg); + } + if(value) { + free(value); + } + wlr_log(WLR_ERROR, + "Input configuration must be of the form 'input \"\" '"); + return NULL; +} + struct keybinding * parse_bind(struct cg_server *server, char **saveptr, char **errstr) { struct keybinding *keybinding = parse_keybinding(server, saveptr, errstr); @@ -242,23 +481,6 @@ parse_uint(char **saveptr, const char *delim) { } } -float -parse_float(char **saveptr, const char *delim) { - char *uint_str = strtok_r(NULL, delim, saveptr); - if(uint_str == NULL) { - wlr_log(WLR_ERROR, "Expected a non-negative float, got nothing"); - return -1; - } - float ufloat = strtof(uint_str, NULL); - if(ufloat >= 0) { - return ufloat; - } else { - wlr_log(WLR_ERROR, "Error parsing non-negative float. Must be a number " - "larger or equal to 0"); - return -1; - } -} - int parse_output_config_keyword(char *key_str, enum output_status *status) { if(key_str == NULL) { @@ -353,7 +575,7 @@ parse_output_config(char **saveptr, char **errstr) { cfg->refresh_rate = parse_float(saveptr, " "); if(cfg->refresh_rate <= 0.0) { *errstr = log_error( - "Error parsing refresh rate of output configuration for output %s", + "Error parsing refresh rate of output configuration for output %s, expected positive float", name); goto error; } @@ -562,6 +784,12 @@ parse_command(struct cg_server *server, struct keybinding *keybinding, if(keybinding->data.o_cfg == NULL) { return -1; } + } else if(strcmp(action, "input") == 0) { + keybinding->action = KEYBINDING_CONFIGURE_INPUT; + keybinding->data.i_cfg = parse_input_config(&saveptr, errstr); + if(keybinding->data.i_cfg == NULL) { + return -1; + } } else { *errstr = log_error("Error, unsupported action \"%s\".", action); return -1;