1a46c0ec8Sopenharmony_ci/* 2a46c0ec8Sopenharmony_ci * Copyright © 2013-2019 Red Hat, Inc. 3a46c0ec8Sopenharmony_ci * 4a46c0ec8Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 5a46c0ec8Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 6a46c0ec8Sopenharmony_ci * to deal in the Software without restriction, including without limitation 7a46c0ec8Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8a46c0ec8Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 9a46c0ec8Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 10a46c0ec8Sopenharmony_ci * 11a46c0ec8Sopenharmony_ci * The above copyright notice and this permission notice (including the next 12a46c0ec8Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 13a46c0ec8Sopenharmony_ci * Software. 14a46c0ec8Sopenharmony_ci * 15a46c0ec8Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16a46c0ec8Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17a46c0ec8Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18a46c0ec8Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19a46c0ec8Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20a46c0ec8Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21a46c0ec8Sopenharmony_ci * DEALINGS IN THE SOFTWARE. 22a46c0ec8Sopenharmony_ci */ 23a46c0ec8Sopenharmony_ci 24a46c0ec8Sopenharmony_ci#include "util-prop-parsers.h" 25a46c0ec8Sopenharmony_ci 26a46c0ec8Sopenharmony_ci#include <libevdev/libevdev.h> 27a46c0ec8Sopenharmony_ci#include <string.h> 28a46c0ec8Sopenharmony_ci 29a46c0ec8Sopenharmony_ci#include "util-macros.h" 30a46c0ec8Sopenharmony_ci#include "util-strings.h" 31a46c0ec8Sopenharmony_ci 32a46c0ec8Sopenharmony_ci/* Helper function to parse the mouse DPI tag from udev. 33a46c0ec8Sopenharmony_ci * The tag is of the form: 34a46c0ec8Sopenharmony_ci * MOUSE_DPI=400 *1000 2000 35a46c0ec8Sopenharmony_ci * or 36a46c0ec8Sopenharmony_ci * MOUSE_DPI=400@125 *1000@125 2000@125 37a46c0ec8Sopenharmony_ci * Where the * indicates the default value and @number indicates device poll 38a46c0ec8Sopenharmony_ci * rate. 39a46c0ec8Sopenharmony_ci * Numbers should be in ascending order, and if rates are present they should 40a46c0ec8Sopenharmony_ci * be present for all entries. 41a46c0ec8Sopenharmony_ci * 42a46c0ec8Sopenharmony_ci * When parsing the mouse DPI property, if we find an error we just return 0 43a46c0ec8Sopenharmony_ci * since it's obviously invalid, the caller will treat that as an error and 44a46c0ec8Sopenharmony_ci * use a reasonable default instead. If the property contains multiple DPI 45a46c0ec8Sopenharmony_ci * settings but none flagged as default, we return the last because we're 46a46c0ec8Sopenharmony_ci * lazy and that's a silly way to set the property anyway. 47a46c0ec8Sopenharmony_ci * 48a46c0ec8Sopenharmony_ci * @param prop The value of the udev property (without the MOUSE_DPI=) 49a46c0ec8Sopenharmony_ci * @return The default dpi value on success, 0 on error 50a46c0ec8Sopenharmony_ci */ 51a46c0ec8Sopenharmony_ciint 52a46c0ec8Sopenharmony_ciparse_mouse_dpi_property(const char *prop) 53a46c0ec8Sopenharmony_ci{ 54a46c0ec8Sopenharmony_ci bool is_default = false; 55a46c0ec8Sopenharmony_ci int nread, dpi = 0, rate; 56a46c0ec8Sopenharmony_ci 57a46c0ec8Sopenharmony_ci if (!prop) 58a46c0ec8Sopenharmony_ci return 0; 59a46c0ec8Sopenharmony_ci 60a46c0ec8Sopenharmony_ci while (*prop != 0) { 61a46c0ec8Sopenharmony_ci if (*prop == ' ') { 62a46c0ec8Sopenharmony_ci prop++; 63a46c0ec8Sopenharmony_ci continue; 64a46c0ec8Sopenharmony_ci } 65a46c0ec8Sopenharmony_ci if (*prop == '*') { 66a46c0ec8Sopenharmony_ci prop++; 67a46c0ec8Sopenharmony_ci is_default = true; 68a46c0ec8Sopenharmony_ci if (!isdigit(prop[0])) 69a46c0ec8Sopenharmony_ci return 0; 70a46c0ec8Sopenharmony_ci } 71a46c0ec8Sopenharmony_ci 72a46c0ec8Sopenharmony_ci /* While we don't do anything with the rate right now we 73a46c0ec8Sopenharmony_ci * will validate that, if it's present, it is non-zero and 74a46c0ec8Sopenharmony_ci * positive 75a46c0ec8Sopenharmony_ci */ 76a46c0ec8Sopenharmony_ci rate = 1; 77a46c0ec8Sopenharmony_ci nread = 0; 78a46c0ec8Sopenharmony_ci sscanf(prop, "%d@%d%n", &dpi, &rate, &nread); 79a46c0ec8Sopenharmony_ci if (!nread) 80a46c0ec8Sopenharmony_ci sscanf(prop, "%d%n", &dpi, &nread); 81a46c0ec8Sopenharmony_ci if (!nread || dpi <= 0 || rate <= 0 || prop[nread] == '@') 82a46c0ec8Sopenharmony_ci return 0; 83a46c0ec8Sopenharmony_ci 84a46c0ec8Sopenharmony_ci if (is_default) 85a46c0ec8Sopenharmony_ci break; 86a46c0ec8Sopenharmony_ci prop += nread; 87a46c0ec8Sopenharmony_ci } 88a46c0ec8Sopenharmony_ci return dpi; 89a46c0ec8Sopenharmony_ci} 90a46c0ec8Sopenharmony_ci 91a46c0ec8Sopenharmony_ci/** 92a46c0ec8Sopenharmony_ci * Helper function to parse the MOUSE_WHEEL_CLICK_COUNT property from udev. 93a46c0ec8Sopenharmony_ci * Property is of the form: 94a46c0ec8Sopenharmony_ci * MOUSE_WHEEL_CLICK_COUNT=<integer> 95a46c0ec8Sopenharmony_ci * Where the number indicates the number of wheel clicks per 360 deg 96a46c0ec8Sopenharmony_ci * rotation. 97a46c0ec8Sopenharmony_ci * 98a46c0ec8Sopenharmony_ci * @param prop The value of the udev property (without the MOUSE_WHEEL_CLICK_COUNT=) 99a46c0ec8Sopenharmony_ci * @return The click count of the wheel (may be negative) or 0 on error. 100a46c0ec8Sopenharmony_ci */ 101a46c0ec8Sopenharmony_ciint 102a46c0ec8Sopenharmony_ciparse_mouse_wheel_click_count_property(const char *prop) 103a46c0ec8Sopenharmony_ci{ 104a46c0ec8Sopenharmony_ci int count = 0; 105a46c0ec8Sopenharmony_ci 106a46c0ec8Sopenharmony_ci if (!prop) 107a46c0ec8Sopenharmony_ci return 0; 108a46c0ec8Sopenharmony_ci 109a46c0ec8Sopenharmony_ci if (!safe_atoi(prop, &count) || abs(count) > 360) 110a46c0ec8Sopenharmony_ci return 0; 111a46c0ec8Sopenharmony_ci 112a46c0ec8Sopenharmony_ci return count; 113a46c0ec8Sopenharmony_ci} 114a46c0ec8Sopenharmony_ci 115a46c0ec8Sopenharmony_ci/** 116a46c0ec8Sopenharmony_ci * 117a46c0ec8Sopenharmony_ci * Helper function to parse the MOUSE_WHEEL_CLICK_ANGLE property from udev. 118a46c0ec8Sopenharmony_ci * Property is of the form: 119a46c0ec8Sopenharmony_ci * MOUSE_WHEEL_CLICK_ANGLE=<integer> 120a46c0ec8Sopenharmony_ci * Where the number indicates the degrees travelled for each click. 121a46c0ec8Sopenharmony_ci * 122a46c0ec8Sopenharmony_ci * @param prop The value of the udev property (without the MOUSE_WHEEL_CLICK_ANGLE=) 123a46c0ec8Sopenharmony_ci * @return The angle of the wheel (may be negative) or 0 on error. 124a46c0ec8Sopenharmony_ci */ 125a46c0ec8Sopenharmony_ciint 126a46c0ec8Sopenharmony_ciparse_mouse_wheel_click_angle_property(const char *prop) 127a46c0ec8Sopenharmony_ci{ 128a46c0ec8Sopenharmony_ci int angle = 0; 129a46c0ec8Sopenharmony_ci 130a46c0ec8Sopenharmony_ci if (!prop) 131a46c0ec8Sopenharmony_ci return 0; 132a46c0ec8Sopenharmony_ci 133a46c0ec8Sopenharmony_ci if (!safe_atoi(prop, &angle) || abs(angle) > 360) 134a46c0ec8Sopenharmony_ci return 0; 135a46c0ec8Sopenharmony_ci 136a46c0ec8Sopenharmony_ci return angle; 137a46c0ec8Sopenharmony_ci} 138a46c0ec8Sopenharmony_ci 139a46c0ec8Sopenharmony_ci/** 140a46c0ec8Sopenharmony_ci * Parses a simple dimension string in the form of "10x40". The two 141a46c0ec8Sopenharmony_ci * numbers must be positive integers in decimal notation. 142a46c0ec8Sopenharmony_ci * On success, the two numbers are stored in w and h. On failure, w and h 143a46c0ec8Sopenharmony_ci * are unmodified. 144a46c0ec8Sopenharmony_ci * 145a46c0ec8Sopenharmony_ci * @param prop The value of the property 146a46c0ec8Sopenharmony_ci * @param w Returns the first component of the dimension 147a46c0ec8Sopenharmony_ci * @param h Returns the second component of the dimension 148a46c0ec8Sopenharmony_ci * @return true on success, false otherwise 149a46c0ec8Sopenharmony_ci */ 150a46c0ec8Sopenharmony_cibool 151a46c0ec8Sopenharmony_ciparse_dimension_property(const char *prop, size_t *w, size_t *h) 152a46c0ec8Sopenharmony_ci{ 153a46c0ec8Sopenharmony_ci int x, y; 154a46c0ec8Sopenharmony_ci 155a46c0ec8Sopenharmony_ci if (!prop) 156a46c0ec8Sopenharmony_ci return false; 157a46c0ec8Sopenharmony_ci 158a46c0ec8Sopenharmony_ci if (sscanf(prop, "%dx%d", &x, &y) != 2) 159a46c0ec8Sopenharmony_ci return false; 160a46c0ec8Sopenharmony_ci 161a46c0ec8Sopenharmony_ci if (x <= 0 || y <= 0) 162a46c0ec8Sopenharmony_ci return false; 163a46c0ec8Sopenharmony_ci 164a46c0ec8Sopenharmony_ci *w = (size_t)x; 165a46c0ec8Sopenharmony_ci *h = (size_t)y; 166a46c0ec8Sopenharmony_ci return true; 167a46c0ec8Sopenharmony_ci} 168a46c0ec8Sopenharmony_ci 169a46c0ec8Sopenharmony_ci/** 170a46c0ec8Sopenharmony_ci * Parses a set of 6 space-separated floats. 171a46c0ec8Sopenharmony_ci * 172a46c0ec8Sopenharmony_ci * @param prop The string value of the property 173a46c0ec8Sopenharmony_ci * @param calibration Returns the six components 174a46c0ec8Sopenharmony_ci * @return true on success, false otherwise 175a46c0ec8Sopenharmony_ci */ 176a46c0ec8Sopenharmony_cibool 177a46c0ec8Sopenharmony_ciparse_calibration_property(const char *prop, float calibration_out[6]) 178a46c0ec8Sopenharmony_ci{ 179a46c0ec8Sopenharmony_ci if (!prop) 180a46c0ec8Sopenharmony_ci return false; 181a46c0ec8Sopenharmony_ci 182a46c0ec8Sopenharmony_ci bool rc = false; 183a46c0ec8Sopenharmony_ci 184a46c0ec8Sopenharmony_ci size_t num_calibration; 185a46c0ec8Sopenharmony_ci char **strv = strv_from_string(prop, " ", &num_calibration); 186a46c0ec8Sopenharmony_ci if (!strv || num_calibration < 6) 187a46c0ec8Sopenharmony_ci goto out; 188a46c0ec8Sopenharmony_ci 189a46c0ec8Sopenharmony_ci float calibration[6]; 190a46c0ec8Sopenharmony_ci for (size_t idx = 0; idx < 6; idx++) { 191a46c0ec8Sopenharmony_ci double v; 192a46c0ec8Sopenharmony_ci if (!safe_atod(strv[idx], &v)) 193a46c0ec8Sopenharmony_ci goto out; 194a46c0ec8Sopenharmony_ci 195a46c0ec8Sopenharmony_ci calibration[idx] = v; 196a46c0ec8Sopenharmony_ci } 197a46c0ec8Sopenharmony_ci 198a46c0ec8Sopenharmony_ci memcpy(calibration_out, calibration, sizeof(calibration)); 199a46c0ec8Sopenharmony_ci rc = true; 200a46c0ec8Sopenharmony_ci 201a46c0ec8Sopenharmony_ciout: 202a46c0ec8Sopenharmony_ci strv_free(strv); 203a46c0ec8Sopenharmony_ci return rc; 204a46c0ec8Sopenharmony_ci} 205a46c0ec8Sopenharmony_ci 206a46c0ec8Sopenharmony_cibool 207a46c0ec8Sopenharmony_ciparse_switch_reliability_property(const char *prop, 208a46c0ec8Sopenharmony_ci enum switch_reliability *reliability) 209a46c0ec8Sopenharmony_ci{ 210a46c0ec8Sopenharmony_ci if (!prop) { 211a46c0ec8Sopenharmony_ci *reliability = RELIABILITY_RELIABLE; 212a46c0ec8Sopenharmony_ci return true; 213a46c0ec8Sopenharmony_ci } 214a46c0ec8Sopenharmony_ci 215a46c0ec8Sopenharmony_ci if (streq(prop, "reliable")) 216a46c0ec8Sopenharmony_ci *reliability = RELIABILITY_RELIABLE; 217a46c0ec8Sopenharmony_ci else if (streq(prop, "unreliable")) 218a46c0ec8Sopenharmony_ci *reliability = RELIABILITY_UNRELIABLE; 219a46c0ec8Sopenharmony_ci else if (streq(prop, "write_open")) 220a46c0ec8Sopenharmony_ci *reliability = RELIABILITY_WRITE_OPEN; 221a46c0ec8Sopenharmony_ci else 222a46c0ec8Sopenharmony_ci return false; 223a46c0ec8Sopenharmony_ci 224a46c0ec8Sopenharmony_ci return true; 225a46c0ec8Sopenharmony_ci} 226a46c0ec8Sopenharmony_ci 227a46c0ec8Sopenharmony_ci/** 228a46c0ec8Sopenharmony_ci * Parses a string with the allowed values: "below" 229a46c0ec8Sopenharmony_ci * The value refers to the position of the touchpad (relative to the 230a46c0ec8Sopenharmony_ci * keyboard, i.e. your average laptop would be 'below') 231a46c0ec8Sopenharmony_ci * 232a46c0ec8Sopenharmony_ci * @param prop The value of the property 233a46c0ec8Sopenharmony_ci * @param layout The layout 234a46c0ec8Sopenharmony_ci * @return true on success, false otherwise 235a46c0ec8Sopenharmony_ci */ 236a46c0ec8Sopenharmony_cibool 237a46c0ec8Sopenharmony_ciparse_tpkbcombo_layout_poperty(const char *prop, 238a46c0ec8Sopenharmony_ci enum tpkbcombo_layout *layout) 239a46c0ec8Sopenharmony_ci{ 240a46c0ec8Sopenharmony_ci if (!prop) 241a46c0ec8Sopenharmony_ci return false; 242a46c0ec8Sopenharmony_ci 243a46c0ec8Sopenharmony_ci if (streq(prop, "below")) { 244a46c0ec8Sopenharmony_ci *layout = TPKBCOMBO_LAYOUT_BELOW; 245a46c0ec8Sopenharmony_ci return true; 246a46c0ec8Sopenharmony_ci } 247a46c0ec8Sopenharmony_ci 248a46c0ec8Sopenharmony_ci return false; 249a46c0ec8Sopenharmony_ci} 250a46c0ec8Sopenharmony_ci 251a46c0ec8Sopenharmony_ci/** 252a46c0ec8Sopenharmony_ci * Parses a string of the format "a:b" where both a and b must be integer 253a46c0ec8Sopenharmony_ci * numbers and a > b. Also allowed is the special string value "none" which 254a46c0ec8Sopenharmony_ci * amounts to unsetting the property. 255a46c0ec8Sopenharmony_ci * 256a46c0ec8Sopenharmony_ci * @param prop The value of the property 257a46c0ec8Sopenharmony_ci * @param hi Set to the first digit or 0 in case of 'none' 258a46c0ec8Sopenharmony_ci * @param lo Set to the second digit or 0 in case of 'none' 259a46c0ec8Sopenharmony_ci * @return true on success, false otherwise 260a46c0ec8Sopenharmony_ci */ 261a46c0ec8Sopenharmony_cibool 262a46c0ec8Sopenharmony_ciparse_range_property(const char *prop, int *hi, int *lo) 263a46c0ec8Sopenharmony_ci{ 264a46c0ec8Sopenharmony_ci int first, second; 265a46c0ec8Sopenharmony_ci 266a46c0ec8Sopenharmony_ci if (!prop) 267a46c0ec8Sopenharmony_ci return false; 268a46c0ec8Sopenharmony_ci 269a46c0ec8Sopenharmony_ci if (streq(prop, "none")) { 270a46c0ec8Sopenharmony_ci *hi = 0; 271a46c0ec8Sopenharmony_ci *lo = 0; 272a46c0ec8Sopenharmony_ci return true; 273a46c0ec8Sopenharmony_ci } 274a46c0ec8Sopenharmony_ci 275a46c0ec8Sopenharmony_ci if (sscanf(prop, "%d:%d", &first, &second) != 2) 276a46c0ec8Sopenharmony_ci return false; 277a46c0ec8Sopenharmony_ci 278a46c0ec8Sopenharmony_ci if (second >= first) 279a46c0ec8Sopenharmony_ci return false; 280a46c0ec8Sopenharmony_ci 281a46c0ec8Sopenharmony_ci *hi = first; 282a46c0ec8Sopenharmony_ci *lo = second; 283a46c0ec8Sopenharmony_ci 284a46c0ec8Sopenharmony_ci return true; 285a46c0ec8Sopenharmony_ci} 286a46c0ec8Sopenharmony_ci 287a46c0ec8Sopenharmony_cibool 288a46c0ec8Sopenharmony_ciparse_boolean_property(const char *prop, bool *b) 289a46c0ec8Sopenharmony_ci{ 290a46c0ec8Sopenharmony_ci if (!prop) 291a46c0ec8Sopenharmony_ci return false; 292a46c0ec8Sopenharmony_ci 293a46c0ec8Sopenharmony_ci if (streq(prop, "1")) 294a46c0ec8Sopenharmony_ci *b = true; 295a46c0ec8Sopenharmony_ci else if (streq(prop, "0")) 296a46c0ec8Sopenharmony_ci *b = false; 297a46c0ec8Sopenharmony_ci else 298a46c0ec8Sopenharmony_ci return false; 299a46c0ec8Sopenharmony_ci 300a46c0ec8Sopenharmony_ci return true; 301a46c0ec8Sopenharmony_ci} 302a46c0ec8Sopenharmony_ci 303a46c0ec8Sopenharmony_cistatic bool 304a46c0ec8Sopenharmony_ciparse_evcode_string(const char *s, int *type_out, int *code_out) 305a46c0ec8Sopenharmony_ci{ 306a46c0ec8Sopenharmony_ci int type, code; 307a46c0ec8Sopenharmony_ci 308a46c0ec8Sopenharmony_ci if (strneq(s, "EV_", 3)) { 309a46c0ec8Sopenharmony_ci type = libevdev_event_type_from_name(s); 310a46c0ec8Sopenharmony_ci if (type == -1) 311a46c0ec8Sopenharmony_ci return false; 312a46c0ec8Sopenharmony_ci 313a46c0ec8Sopenharmony_ci code = EVENT_CODE_UNDEFINED; 314a46c0ec8Sopenharmony_ci } else { 315a46c0ec8Sopenharmony_ci struct map { 316a46c0ec8Sopenharmony_ci const char *str; 317a46c0ec8Sopenharmony_ci int type; 318a46c0ec8Sopenharmony_ci } map[] = { 319a46c0ec8Sopenharmony_ci { "KEY_", EV_KEY }, 320a46c0ec8Sopenharmony_ci { "BTN_", EV_KEY }, 321a46c0ec8Sopenharmony_ci { "ABS_", EV_ABS }, 322a46c0ec8Sopenharmony_ci { "REL_", EV_REL }, 323a46c0ec8Sopenharmony_ci { "SW_", EV_SW }, 324a46c0ec8Sopenharmony_ci }; 325a46c0ec8Sopenharmony_ci bool found = false; 326a46c0ec8Sopenharmony_ci 327a46c0ec8Sopenharmony_ci ARRAY_FOR_EACH(map, m) { 328a46c0ec8Sopenharmony_ci if (!strstartswith(s, m->str)) 329a46c0ec8Sopenharmony_ci continue; 330a46c0ec8Sopenharmony_ci 331a46c0ec8Sopenharmony_ci type = m->type; 332a46c0ec8Sopenharmony_ci code = libevdev_event_code_from_name(type, s); 333a46c0ec8Sopenharmony_ci if (code == -1) 334a46c0ec8Sopenharmony_ci return false; 335a46c0ec8Sopenharmony_ci 336a46c0ec8Sopenharmony_ci found = true; 337a46c0ec8Sopenharmony_ci break; 338a46c0ec8Sopenharmony_ci } 339a46c0ec8Sopenharmony_ci if (!found) 340a46c0ec8Sopenharmony_ci return false; 341a46c0ec8Sopenharmony_ci } 342a46c0ec8Sopenharmony_ci 343a46c0ec8Sopenharmony_ci *type_out = type; 344a46c0ec8Sopenharmony_ci *code_out = code; 345a46c0ec8Sopenharmony_ci 346a46c0ec8Sopenharmony_ci return true; 347a46c0ec8Sopenharmony_ci} 348a46c0ec8Sopenharmony_ci 349a46c0ec8Sopenharmony_ci/** 350a46c0ec8Sopenharmony_ci * Parses a string of the format "+EV_ABS;+KEY_A;-BTN_TOOL_DOUBLETAP;-ABS_X;" 351a46c0ec8Sopenharmony_ci * where each element must be + or - (enable/disable) followed by a named event 352a46c0ec8Sopenharmony_ci * type OR a named event code OR a tuple in the form of EV_KEY:0x123, i.e. a 353a46c0ec8Sopenharmony_ci * named event type followed by a hex event code. 354a46c0ec8Sopenharmony_ci * 355a46c0ec8Sopenharmony_ci * events must point to an existing array of size nevents. 356a46c0ec8Sopenharmony_ci * nevents specifies the size of the array in events and returns the number 357a46c0ec8Sopenharmony_ci * of items, elements exceeding nevents are simply ignored, just make sure 358a46c0ec8Sopenharmony_ci * events is large enough for your use-case. 359a46c0ec8Sopenharmony_ci * 360a46c0ec8Sopenharmony_ci * The results are returned as input events with type and code set, all 361a46c0ec8Sopenharmony_ci * other fields undefined. Where only the event type is specified, the code 362a46c0ec8Sopenharmony_ci * is set to EVENT_CODE_UNDEFINED. 363a46c0ec8Sopenharmony_ci * 364a46c0ec8Sopenharmony_ci * On success, events contains nevents events with each event's value set to 1 365a46c0ec8Sopenharmony_ci * or 0 depending on the + or - prefix. 366a46c0ec8Sopenharmony_ci */ 367a46c0ec8Sopenharmony_cibool 368a46c0ec8Sopenharmony_ciparse_evcode_property(const char *prop, struct input_event *events, size_t *nevents) 369a46c0ec8Sopenharmony_ci{ 370a46c0ec8Sopenharmony_ci bool rc = false; 371a46c0ec8Sopenharmony_ci /* A randomly chosen max so we avoid crazy quirks */ 372a46c0ec8Sopenharmony_ci struct input_event evs[32]; 373a46c0ec8Sopenharmony_ci 374a46c0ec8Sopenharmony_ci memset(evs, 0, sizeof evs); 375a46c0ec8Sopenharmony_ci 376a46c0ec8Sopenharmony_ci size_t ncodes; 377a46c0ec8Sopenharmony_ci char **strv = strv_from_string(prop, ";", &ncodes); 378a46c0ec8Sopenharmony_ci if (!strv || ncodes == 0 || ncodes > ARRAY_LENGTH(evs)) 379a46c0ec8Sopenharmony_ci goto out; 380a46c0ec8Sopenharmony_ci 381a46c0ec8Sopenharmony_ci ncodes = min(*nevents, ncodes); 382a46c0ec8Sopenharmony_ci for (size_t idx = 0; strv[idx]; idx++) { 383a46c0ec8Sopenharmony_ci char *s = strv[idx]; 384a46c0ec8Sopenharmony_ci bool enable; 385a46c0ec8Sopenharmony_ci 386a46c0ec8Sopenharmony_ci switch (*s) { 387a46c0ec8Sopenharmony_ci case '+': enable = true; break; 388a46c0ec8Sopenharmony_ci case '-': enable = false; break; 389a46c0ec8Sopenharmony_ci default: 390a46c0ec8Sopenharmony_ci goto out; 391a46c0ec8Sopenharmony_ci } 392a46c0ec8Sopenharmony_ci 393a46c0ec8Sopenharmony_ci s++; 394a46c0ec8Sopenharmony_ci 395a46c0ec8Sopenharmony_ci int type, code; 396a46c0ec8Sopenharmony_ci 397a46c0ec8Sopenharmony_ci if (strstr(s, ":") == NULL) { 398a46c0ec8Sopenharmony_ci if (!parse_evcode_string(s, &type, &code)) 399a46c0ec8Sopenharmony_ci goto out; 400a46c0ec8Sopenharmony_ci } else { 401a46c0ec8Sopenharmony_ci int consumed; 402a46c0ec8Sopenharmony_ci char stype[13] = {0}; /* EV_FF_STATUS + '\0' */ 403a46c0ec8Sopenharmony_ci 404a46c0ec8Sopenharmony_ci if (sscanf(s, "%12[A-Z_]:%x%n", stype, &code, &consumed) != 2 || 405a46c0ec8Sopenharmony_ci strlen(s) != (size_t)consumed || 406a46c0ec8Sopenharmony_ci (type = libevdev_event_type_from_name(stype)) == -1 || 407a46c0ec8Sopenharmony_ci code < 0 || code > libevdev_event_type_get_max(type)) 408a46c0ec8Sopenharmony_ci goto out; 409a46c0ec8Sopenharmony_ci } 410a46c0ec8Sopenharmony_ci 411a46c0ec8Sopenharmony_ci evs[idx].type = type; 412a46c0ec8Sopenharmony_ci evs[idx].code = code; 413a46c0ec8Sopenharmony_ci evs[idx].value = enable; 414a46c0ec8Sopenharmony_ci } 415a46c0ec8Sopenharmony_ci 416a46c0ec8Sopenharmony_ci memcpy(events, evs, ncodes * sizeof *events); 417a46c0ec8Sopenharmony_ci *nevents = ncodes; 418a46c0ec8Sopenharmony_ci rc = true; 419a46c0ec8Sopenharmony_ci 420a46c0ec8Sopenharmony_ciout: 421a46c0ec8Sopenharmony_ci strv_free(strv); 422a46c0ec8Sopenharmony_ci return rc; 423a46c0ec8Sopenharmony_ci} 424a46c0ec8Sopenharmony_ci 425a46c0ec8Sopenharmony_ci/** 426a46c0ec8Sopenharmony_ci * Parses a string of the format "+INPUT_PROP_BUTTONPAD;-INPUT_PROP_POINTER;+0x123;" 427a46c0ec8Sopenharmony_ci * where each element must be a named input prop OR a hexcode in the form 428a46c0ec8Sopenharmony_ci * 0x1234. The prefix for each element must be either '+' (enable) or '-' (disable). 429a46c0ec8Sopenharmony_ci * 430a46c0ec8Sopenharmony_ci * props must point to an existing array of size nprops. 431a46c0ec8Sopenharmony_ci * nprops specifies the size of the array in props and returns the number 432a46c0ec8Sopenharmony_ci * of elements, elements exceeding nprops are simply ignored, just make sure 433a46c0ec8Sopenharmony_ci * props is large enough for your use-case. 434a46c0ec8Sopenharmony_ci * 435a46c0ec8Sopenharmony_ci * On success, props contains nprops elements. 436a46c0ec8Sopenharmony_ci */ 437a46c0ec8Sopenharmony_cibool 438a46c0ec8Sopenharmony_ciparse_input_prop_property(const char *prop, struct input_prop *props_out, size_t *nprops) 439a46c0ec8Sopenharmony_ci{ 440a46c0ec8Sopenharmony_ci bool rc = false; 441a46c0ec8Sopenharmony_ci struct input_prop props[INPUT_PROP_CNT]; /* doubling up on quirks is a bug */ 442a46c0ec8Sopenharmony_ci 443a46c0ec8Sopenharmony_ci size_t count; 444a46c0ec8Sopenharmony_ci char **strv = strv_from_string(prop, ";", &count); 445a46c0ec8Sopenharmony_ci if (!strv || count == 0 || count > ARRAY_LENGTH(props)) 446a46c0ec8Sopenharmony_ci goto out; 447a46c0ec8Sopenharmony_ci 448a46c0ec8Sopenharmony_ci count = min(*nprops, count); 449a46c0ec8Sopenharmony_ci for (size_t idx = 0; strv[idx]; idx++) { 450a46c0ec8Sopenharmony_ci char *s = strv[idx]; 451a46c0ec8Sopenharmony_ci unsigned int prop; 452a46c0ec8Sopenharmony_ci bool enable; 453a46c0ec8Sopenharmony_ci 454a46c0ec8Sopenharmony_ci switch (*s) { 455a46c0ec8Sopenharmony_ci case '+': enable = true; break; 456a46c0ec8Sopenharmony_ci case '-': enable = false; break; 457a46c0ec8Sopenharmony_ci default: 458a46c0ec8Sopenharmony_ci goto out; 459a46c0ec8Sopenharmony_ci } 460a46c0ec8Sopenharmony_ci 461a46c0ec8Sopenharmony_ci s++; 462a46c0ec8Sopenharmony_ci 463a46c0ec8Sopenharmony_ci if (safe_atou_base(s, &prop, 16)) { 464a46c0ec8Sopenharmony_ci if (prop > INPUT_PROP_MAX) 465a46c0ec8Sopenharmony_ci goto out; 466a46c0ec8Sopenharmony_ci } else { 467a46c0ec8Sopenharmony_ci int val = libevdev_property_from_name(s); 468a46c0ec8Sopenharmony_ci if (val == -1) 469a46c0ec8Sopenharmony_ci goto out; 470a46c0ec8Sopenharmony_ci prop = (unsigned int)val; 471a46c0ec8Sopenharmony_ci } 472a46c0ec8Sopenharmony_ci props[idx].prop = prop; 473a46c0ec8Sopenharmony_ci props[idx].enabled = enable; 474a46c0ec8Sopenharmony_ci } 475a46c0ec8Sopenharmony_ci 476a46c0ec8Sopenharmony_ci memcpy(props_out, props, count * sizeof *props); 477a46c0ec8Sopenharmony_ci *nprops = count; 478a46c0ec8Sopenharmony_ci rc = true; 479a46c0ec8Sopenharmony_ci 480a46c0ec8Sopenharmony_ciout: 481a46c0ec8Sopenharmony_ci strv_free(strv); 482a46c0ec8Sopenharmony_ci return rc; 483a46c0ec8Sopenharmony_ci} 484a46c0ec8Sopenharmony_ci 485a46c0ec8Sopenharmony_ci/** 486a46c0ec8Sopenharmony_ci * Parse the property value for the EVDEV_ABS_00 properties. Spec is 487a46c0ec8Sopenharmony_ci * EVDEV_ABS_00=min:max:res:fuzz:flat 488a46c0ec8Sopenharmony_ci * where any element may be empty and subsequent elements may not be 489a46c0ec8Sopenharmony_ci * present. So we have to parse 490a46c0ec8Sopenharmony_ci * EVDEV_ABS_00=min:max:res 491a46c0ec8Sopenharmony_ci * EVDEV_ABS_00=::res 492a46c0ec8Sopenharmony_ci * EVDEV_ABS_00=::res:fuzz: 493a46c0ec8Sopenharmony_ci * 494a46c0ec8Sopenharmony_ci * Returns a mask of the bits set and the absinfo struct with the values. 495a46c0ec8Sopenharmony_ci * The abs value for an unset bit is undefined. 496a46c0ec8Sopenharmony_ci */ 497a46c0ec8Sopenharmony_ciuint32_t 498a46c0ec8Sopenharmony_ciparse_evdev_abs_prop(const char *prop, struct input_absinfo *abs) 499a46c0ec8Sopenharmony_ci{ 500a46c0ec8Sopenharmony_ci char *str = safe_strdup(prop); 501a46c0ec8Sopenharmony_ci char *current, *next; 502a46c0ec8Sopenharmony_ci uint32_t mask = 0; 503a46c0ec8Sopenharmony_ci int bit = ABS_MASK_MIN; 504a46c0ec8Sopenharmony_ci int *val; 505a46c0ec8Sopenharmony_ci int values[5]; 506a46c0ec8Sopenharmony_ci 507a46c0ec8Sopenharmony_ci /* basic sanity check: 5 digits for min/max, 3 for resolution, fuzz, 508a46c0ec8Sopenharmony_ci * flat and the colons. That's plenty, anything over is garbage */ 509a46c0ec8Sopenharmony_ci if (!prop || strlen(prop) > 24) 510a46c0ec8Sopenharmony_ci goto out; 511a46c0ec8Sopenharmony_ci 512a46c0ec8Sopenharmony_ci current = str; 513a46c0ec8Sopenharmony_ci val = values; 514a46c0ec8Sopenharmony_ci while (current && *current != '\0' && bit <= ABS_MASK_FLAT) { 515a46c0ec8Sopenharmony_ci if (*current != ':') { 516a46c0ec8Sopenharmony_ci int v; 517a46c0ec8Sopenharmony_ci next = index(current, ':'); 518a46c0ec8Sopenharmony_ci if (next) 519a46c0ec8Sopenharmony_ci *next = '\0'; 520a46c0ec8Sopenharmony_ci 521a46c0ec8Sopenharmony_ci if (!safe_atoi(current, &v)) { 522a46c0ec8Sopenharmony_ci mask = 0; 523a46c0ec8Sopenharmony_ci goto out; 524a46c0ec8Sopenharmony_ci } 525a46c0ec8Sopenharmony_ci *val = v; 526a46c0ec8Sopenharmony_ci mask |= bit; 527a46c0ec8Sopenharmony_ci current = next ? ++next : NULL; 528a46c0ec8Sopenharmony_ci } else { 529a46c0ec8Sopenharmony_ci current++; 530a46c0ec8Sopenharmony_ci } 531a46c0ec8Sopenharmony_ci bit <<= 1; 532a46c0ec8Sopenharmony_ci val++; 533a46c0ec8Sopenharmony_ci } 534a46c0ec8Sopenharmony_ci 535a46c0ec8Sopenharmony_ci if (mask & ABS_MASK_MIN) 536a46c0ec8Sopenharmony_ci abs->minimum = values[0]; 537a46c0ec8Sopenharmony_ci if (mask & ABS_MASK_MAX) 538a46c0ec8Sopenharmony_ci abs->maximum = values[1]; 539a46c0ec8Sopenharmony_ci if (mask & ABS_MASK_RES) 540a46c0ec8Sopenharmony_ci abs->resolution = values[2]; 541a46c0ec8Sopenharmony_ci if (mask & ABS_MASK_FUZZ) 542a46c0ec8Sopenharmony_ci abs->fuzz = values[3]; 543a46c0ec8Sopenharmony_ci if (mask & ABS_MASK_FLAT) 544a46c0ec8Sopenharmony_ci abs->flat = values[4]; 545a46c0ec8Sopenharmony_ci 546a46c0ec8Sopenharmony_ciout: 547a46c0ec8Sopenharmony_ci free(str); 548a46c0ec8Sopenharmony_ci 549a46c0ec8Sopenharmony_ci return mask; 550a46c0ec8Sopenharmony_ci} 551