Split utility functions into separate source files

libinput-util.h is getting a bit of a catchall bucket and it includes things
like libinput-private.h which in turn includes libwacom. This makes
libinput-util.h less useful for bits that only need e.g. the string processing
utilities.

So let's split them all up in to separate files, to be used as-needed.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Peter Hutterer 2019-09-04 15:11:45 +10:00
parent 00f3345b80
commit c84366e85e
16 changed files with 1270 additions and 898 deletions

View file

@ -218,9 +218,45 @@ else
endif
############ libinput-util.a ############
# Basic compilation test to make sure the headers include and define all the
# necessary bits.
util_headers = [
'util-bits.h',
'util-list.h',
'util-macros.h',
'util-matrix.h',
'util-prop-parsers.h',
'util-ratelimit.h',
'util-strings.h',
'util-time.h',
]
foreach h: util_headers
c = configuration_data()
c.set_quoted('FILE', h)
testfile = configure_file(input : 'test/test-util-includes.c',
output : 'test-util-includes-@0@.c'.format(h),
configuration : c)
executable('test-build-@0@'.format(h),
testfile, join_paths(dir_src, h),
include_directories : [includes_src, includes_include],
install : false)
endforeach
src_libinput_util = [
'src/libinput-util.c',
'src/libinput-util.h'
'src/util-bits.h',
'src/util-list.c',
'src/util-list.h',
'src/util-macros.h',
'src/util-matrix.h',
'src/util-ratelimit.c',
'src/util-ratelimit.h',
'src/util-strings.h',
'src/util-strings.c',
'src/util-time.h',
'src/util-prop-parsers.h',
'src/util-prop-parsers.c',
'src/libinput-util.h',
]
libinput_util = static_library('libinput-util',
src_libinput_util,
@ -836,7 +872,6 @@ if get_option('tests')
test_utils_sources = [
'src/libinput-util.h',
'src/libinput-util.c',
'test/test-utils.c',
]
test_utils = executable('test-utils',
@ -850,7 +885,6 @@ if get_option('tests')
libinput_test_runner_sources = litest_sources + [
'src/libinput-util.h',
'src/libinput-util.c',
'test/test-udev.c',
'test/test-path.c',
'test/test-pointer.c',

View file

@ -31,29 +31,18 @@
#warning "libinput relies on assert(). #defining NDEBUG is not recommended"
#endif
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
#ifdef HAVE_XLOCALE_H
#include <xlocale.h>
#endif
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <linux/input.h>
#include "libinput.h"
#include "util-bits.h"
#include "util-macros.h"
#include "util-list.h"
#include "util-matrix.h"
#include "util-strings.h"
#include "util-ratelimit.h"
#include "util-prop-parsers.h"
#include "util-time.h"
#define VENDOR_ID_APPLE 0x5ac
#define VENDOR_ID_CHICONY 0x4f2
#define VENDOR_ID_LOGITECH 0x46d
@ -68,74 +57,6 @@
#define DEFAULT_MOUSE_DPI 1000
#define DEFAULT_TRACKPOINT_SENSITIVITY 128
#define ANSI_HIGHLIGHT "\x1B[0;1;39m"
#define ANSI_RED "\x1B[0;31m"
#define ANSI_GREEN "\x1B[0;32m"
#define ANSI_YELLOW "\x1B[0;33m"
#define ANSI_BLUE "\x1B[0;34m"
#define ANSI_MAGENTA "\x1B[0;35m"
#define ANSI_CYAN "\x1B[0;36m"
#define ANSI_BRIGHT_RED "\x1B[0;31;1m"
#define ANSI_BRIGHT_GREEN "\x1B[0;32;1m"
#define ANSI_BRIGHT_YELLOW "\x1B[0;33;1m"
#define ANSI_BRIGHT_BLUE "\x1B[0;34;1m"
#define ANSI_BRIGHT_MAGENTA "\x1B[0;35;1m"
#define ANSI_BRIGHT_CYAN "\x1B[0;36;1m"
#define ANSI_NORMAL "\x1B[0m"
#define CASE_RETURN_STRING(a) case a: return #a
#define bit(x_) (1UL << (x_))
/*
* This list data structure is a verbatim copy from wayland-util.h from the
* Wayland project; except that wl_ prefix has been removed.
*/
struct list {
struct list *prev;
struct list *next;
};
void list_init(struct list *list);
void list_insert(struct list *list, struct list *elm);
void list_append(struct list *list, struct list *elm);
void list_remove(struct list *elm);
bool list_empty(const struct list *list);
#define container_of(ptr, type, member) \
(__typeof__(type) *)((char *)(ptr) - \
offsetof(__typeof__(type), member))
#define list_first_entry(head, pos, member) \
container_of((head)->next, __typeof__(*pos), member)
#define list_for_each(pos, head, member) \
for (pos = 0, pos = list_first_entry(head, pos, member); \
&pos->member != (head); \
pos = list_first_entry(&pos->member, pos, member))
#define list_for_each_safe(pos, tmp, head, member) \
for (pos = 0, tmp = 0, \
pos = list_first_entry(head, pos, member), \
tmp = list_first_entry(&pos->member, tmp, member); \
&pos->member != (head); \
pos = tmp, \
tmp = list_first_entry(&pos->member, tmp, member))
#define NBITS(b) (b * 8)
#define LONG_BITS (sizeof(long) * 8)
#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS)
#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
#define ARRAY_FOR_EACH(_arr, _elem) \
for (size_t _i = 0; _i < ARRAY_LENGTH(_arr) && (_elem = &_arr[_i]); _i++)
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define streq(s1, s2) (strcmp((s1), (s2)) == 0)
#define strneq(s1, s2, n) (strncmp((s1), (s2), (n)) == 0)
#define NCHARS(x) ((size_t)(((x) + 7) / 8))
#ifdef DEBUG_TRACE
#define debug_trace(...) \
do { \
@ -148,557 +69,4 @@ bool list_empty(const struct list *list);
#define LIBINPUT_EXPORT __attribute__ ((visibility("default")))
static inline void *
zalloc(size_t size)
{
void *p;
/* We never need to alloc anything more than 1,5 MB so we can assume
* if we ever get above that something's going wrong */
if (size > 1536 * 1024)
assert(!"bug: internal malloc size limit exceeded");
p = calloc(1, size);
if (!p)
abort();
return p;
}
/**
* strdup guaranteed to succeed. If the input string is NULL, the output
* string is NULL. If the input string is a string pointer, we strdup or
* abort on failure.
*/
static inline char*
safe_strdup(const char *str)
{
char *s;
if (!str)
return NULL;
s = strdup(str);
if (!s)
abort();
return s;
}
/* This bitfield helper implementation is taken from from libevdev-util.h,
* except that it has been modified to work with arrays of unsigned chars
*/
static inline bool
bit_is_set(const unsigned char *array, int bit)
{
return !!(array[bit / 8] & (1 << (bit % 8)));
}
static inline void
set_bit(unsigned char *array, int bit)
{
array[bit / 8] |= (1 << (bit % 8));
}
static inline void
clear_bit(unsigned char *array, int bit)
{
array[bit / 8] &= ~(1 << (bit % 8));
}
static inline void
msleep(unsigned int ms)
{
usleep(ms * 1000);
}
static inline bool
long_bit_is_set(const unsigned long *array, int bit)
{
return !!(array[bit / LONG_BITS] & (1ULL << (bit % LONG_BITS)));
}
static inline void
long_set_bit(unsigned long *array, int bit)
{
array[bit / LONG_BITS] |= (1ULL << (bit % LONG_BITS));
}
static inline void
long_clear_bit(unsigned long *array, int bit)
{
array[bit / LONG_BITS] &= ~(1ULL << (bit % LONG_BITS));
}
static inline void
long_set_bit_state(unsigned long *array, int bit, int state)
{
if (state)
long_set_bit(array, bit);
else
long_clear_bit(array, bit);
}
static inline bool
long_any_bit_set(unsigned long *array, size_t size)
{
unsigned long i;
assert(size > 0);
for (i = 0; i < size; i++)
if (array[i] != 0)
return true;
return false;
}
static inline double
deg2rad(int degree)
{
return M_PI * degree / 180.0;
}
struct matrix {
float val[3][3]; /* [row][col] */
};
static inline void
matrix_init_identity(struct matrix *m)
{
memset(m, 0, sizeof(*m));
m->val[0][0] = 1;
m->val[1][1] = 1;
m->val[2][2] = 1;
}
static inline void
matrix_from_farray6(struct matrix *m, const float values[6])
{
matrix_init_identity(m);
m->val[0][0] = values[0];
m->val[0][1] = values[1];
m->val[0][2] = values[2];
m->val[1][0] = values[3];
m->val[1][1] = values[4];
m->val[1][2] = values[5];
}
static inline void
matrix_init_scale(struct matrix *m, float sx, float sy)
{
matrix_init_identity(m);
m->val[0][0] = sx;
m->val[1][1] = sy;
}
static inline void
matrix_init_translate(struct matrix *m, float x, float y)
{
matrix_init_identity(m);
m->val[0][2] = x;
m->val[1][2] = y;
}
static inline void
matrix_init_rotate(struct matrix *m, int degrees)
{
double s, c;
s = sin(deg2rad(degrees));
c = cos(deg2rad(degrees));
matrix_init_identity(m);
m->val[0][0] = c;
m->val[0][1] = -s;
m->val[1][0] = s;
m->val[1][1] = c;
}
static inline bool
matrix_is_identity(const struct matrix *m)
{
return (m->val[0][0] == 1 &&
m->val[0][1] == 0 &&
m->val[0][2] == 0 &&
m->val[1][0] == 0 &&
m->val[1][1] == 1 &&
m->val[1][2] == 0 &&
m->val[2][0] == 0 &&
m->val[2][1] == 0 &&
m->val[2][2] == 1);
}
static inline void
matrix_mult(struct matrix *dest,
const struct matrix *m1,
const struct matrix *m2)
{
struct matrix m; /* allow for dest == m1 or dest == m2 */
int row, col, i;
for (row = 0; row < 3; row++) {
for (col = 0; col < 3; col++) {
double v = 0;
for (i = 0; i < 3; i++) {
v += m1->val[row][i] * m2->val[i][col];
}
m.val[row][col] = v;
}
}
memcpy(dest, &m, sizeof(m));
}
static inline void
matrix_mult_vec(const struct matrix *m, int *x, int *y)
{
int tx, ty;
tx = *x * m->val[0][0] + *y * m->val[0][1] + m->val[0][2];
ty = *x * m->val[1][0] + *y * m->val[1][1] + m->val[1][2];
*x = tx;
*y = ty;
}
static inline void
matrix_to_farray6(const struct matrix *m, float out[6])
{
out[0] = m->val[0][0];
out[1] = m->val[0][1];
out[2] = m->val[0][2];
out[3] = m->val[1][0];
out[4] = m->val[1][1];
out[5] = m->val[1][2];
}
static inline void
matrix_to_relative(struct matrix *dest, const struct matrix *src)
{
matrix_init_identity(dest);
dest->val[0][0] = src->val[0][0];
dest->val[0][1] = src->val[0][1];
dest->val[1][0] = src->val[1][0];
dest->val[1][1] = src->val[1][1];
}
/**
* Simple wrapper for asprintf that ensures the passed in-pointer is set
* to NULL upon error.
* The standard asprintf() call does not guarantee the passed in pointer
* will be NULL'ed upon failure, whereas this wrapper does.
*
* @param strp pointer to set to newly allocated string.
* This pointer should be passed to free() to release when done.
* @param fmt the format string to use for printing.
* @return The number of bytes printed (excluding the null byte terminator)
* upon success or -1 upon failure. In the case of failure the pointer is set
* to NULL.
*/
LIBINPUT_ATTRIBUTE_PRINTF(2, 3)
static inline int
xasprintf(char **strp, const char *fmt, ...)
{
int rc = 0;
va_list args;
va_start(args, fmt);
rc = vasprintf(strp, fmt, args);
va_end(args);
if ((rc == -1) && strp)
*strp = NULL;
return rc;
}
enum ratelimit_state {
RATELIMIT_EXCEEDED,
RATELIMIT_THRESHOLD,
RATELIMIT_PASS,
};
struct ratelimit {
uint64_t interval;
uint64_t begin;
unsigned int burst;
unsigned int num;
};
void ratelimit_init(struct ratelimit *r, uint64_t ival_ms, unsigned int burst);
enum ratelimit_state ratelimit_test(struct ratelimit *r);
int parse_mouse_dpi_property(const char *prop);
int parse_mouse_wheel_click_angle_property(const char *prop);
int parse_mouse_wheel_click_count_property(const char *prop);
bool parse_dimension_property(const char *prop, size_t *width, size_t *height);
bool parse_calibration_property(const char *prop, float calibration[6]);
bool parse_range_property(const char *prop, int *hi, int *lo);
#define EVENT_CODE_UNDEFINED 0xffff
bool parse_evcode_property(const char *prop, struct input_event *events, size_t *nevents);
enum tpkbcombo_layout {
TPKBCOMBO_LAYOUT_UNKNOWN,
TPKBCOMBO_LAYOUT_BELOW,
};
bool parse_tpkbcombo_layout_poperty(const char *prop,
enum tpkbcombo_layout *layout);
enum switch_reliability {
RELIABILITY_UNKNOWN,
RELIABILITY_RELIABLE,
RELIABILITY_WRITE_OPEN,
};
bool
parse_switch_reliability_property(const char *prop,
enum switch_reliability *reliability);
static inline uint64_t
us(uint64_t us)
{
return us;
}
static inline uint64_t
ns2us(uint64_t ns)
{
return us(ns / 1000);
}
static inline uint64_t
ms2us(uint64_t ms)
{
return us(ms * 1000);
}
static inline uint64_t
s2us(uint64_t s)
{
return ms2us(s * 1000);
}
static inline uint32_t
us2ms(uint64_t us)
{
return (uint32_t)(us / 1000);
}
static inline uint64_t
tv2us(const struct timeval *tv)
{
return s2us(tv->tv_sec) + tv->tv_usec;
}
static inline struct timeval
us2tv(uint64_t time)
{
struct timeval tv;
tv.tv_sec = time / ms2us(1000);
tv.tv_usec = time % ms2us(1000);
return tv;
}
static inline bool
safe_atoi_base(const char *str, int *val, int base)
{
char *endptr;
long v;
assert(base == 10 || base == 16 || base == 8);
errno = 0;
v = strtol(str, &endptr, base);
if (errno > 0)
return false;
if (str == endptr)
return false;
if (*str != '\0' && *endptr != '\0')
return false;
if (v > INT_MAX || v < INT_MIN)
return false;
*val = v;
return true;
}
static inline bool
safe_atoi(const char *str, int *val)
{
return safe_atoi_base(str, val, 10);
}
static inline bool
safe_atou_base(const char *str, unsigned int *val, int base)
{
char *endptr;
unsigned long v;
assert(base == 10 || base == 16 || base == 8);
errno = 0;
v = strtoul(str, &endptr, base);
if (errno > 0)
return false;
if (str == endptr)
return false;
if (*str != '\0' && *endptr != '\0')
return false;
if ((long)v < 0)
return false;
*val = v;
return true;
}
static inline bool
safe_atou(const char *str, unsigned int *val)
{
return safe_atou_base(str, val, 10);
}
static inline bool
safe_atod(const char *str, double *val)
{
char *endptr;
double v;
#ifdef HAVE_LOCALE_H
locale_t c_locale;
#endif
size_t slen = strlen(str);
/* We don't have a use-case where we want to accept hex for a double
* or any of the other values strtod can parse */
for (size_t i = 0; i < slen; i++) {
char c = str[i];
if (isdigit(c))
continue;
switch(c) {
case '+':
case '-':
case '.':
break;
default:
return false;
}
}
#ifdef HAVE_LOCALE_H
/* Create a "C" locale to force strtod to use '.' as separator */
c_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0);
if (c_locale == (locale_t)0)
return false;
errno = 0;
v = strtod_l(str, &endptr, c_locale);
freelocale(c_locale);
#else
/* No locale support in provided libc, assume it already uses '.' */
errno = 0;
v = strtod(str, &endptr);
#endif
if (errno > 0)
return false;
if (str == endptr)
return false;
if (*str != '\0' && *endptr != '\0')
return false;
if (v != 0.0 && !isnormal(v))
return false;
*val = v;
return true;
}
char **strv_from_string(const char *string, const char *separator);
char *strv_join(char **strv, const char *separator);
static inline void
strv_free(char **strv) {
char **s = strv;
if (!strv)
return;
while (*s != NULL) {
free(*s);
*s = (char*)0x1; /* detect use-after-free */
s++;
}
free (strv);
}
struct key_value_str{
char *key;
char *value;
};
struct key_value_double {
double key;
double value;
};
static inline ssize_t
kv_double_from_string(const char *string,
const char *pair_separator,
const char *kv_separator,
struct key_value_double **result_out)
{
char **pairs;
char **pair;
struct key_value_double *result = NULL;
ssize_t npairs = 0;
unsigned int idx = 0;
if (!pair_separator || pair_separator[0] == '\0' ||
!kv_separator || kv_separator[0] == '\0')
return -1;
pairs = strv_from_string(string, pair_separator);
if (!pairs)
return -1;
for (pair = pairs; *pair; pair++)
npairs++;
if (npairs == 0)
goto error;
result = zalloc(npairs * sizeof *result);
for (pair = pairs; *pair; pair++) {
char **kv = strv_from_string(*pair, kv_separator);
double k, v;
if (!kv || !kv[0] || !kv[1] || kv[2] ||
!safe_atod(kv[0], &k) ||
!safe_atod(kv[1], &v)) {
strv_free(kv);
goto error;
}
result[idx].key = k;
result[idx].value = v;
idx++;
strv_free(kv);
}
strv_free(pairs);
*result_out = result;
return npairs;
error:
strv_free(pairs);
free(result);
return -1;
}
#endif /* LIBINPUT_UTIL_H */

101
src/util-bits.h Normal file
View file

@ -0,0 +1,101 @@
/*
* Copyright © 2008-2011 Kristian Høgsberg
* Copyright © 2011 Intel Corporation
* Copyright © 2013-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.
*/
#pragma once
#include "config.h"
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#define bit(x_) (1UL << (x_))
#define NBITS(b) (b * 8)
#define LONG_BITS (sizeof(long) * 8)
#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS)
#define NCHARS(x) ((size_t)(((x) + 7) / 8))
/* This bitfield helper implementation is taken from from libevdev-util.h,
* except that it has been modified to work with arrays of unsigned chars
*/
static inline bool
bit_is_set(const unsigned char *array, int bit)
{
return !!(array[bit / 8] & (1 << (bit % 8)));
}
static inline void
set_bit(unsigned char *array, int bit)
{
array[bit / 8] |= (1 << (bit % 8));
}
static inline void
clear_bit(unsigned char *array, int bit)
{
array[bit / 8] &= ~(1 << (bit % 8));
}
static inline bool
long_bit_is_set(const unsigned long *array, int bit)
{
return !!(array[bit / LONG_BITS] & (1ULL << (bit % LONG_BITS)));
}
static inline void
long_set_bit(unsigned long *array, int bit)
{
array[bit / LONG_BITS] |= (1ULL << (bit % LONG_BITS));
}
static inline void
long_clear_bit(unsigned long *array, int bit)
{
array[bit / LONG_BITS] &= ~(1ULL << (bit % LONG_BITS));
}
static inline void
long_set_bit_state(unsigned long *array, int bit, int state)
{
if (state)
long_set_bit(array, bit);
else
long_clear_bit(array, bit);
}
static inline bool
long_any_bit_set(unsigned long *array, size_t size)
{
unsigned long i;
assert(size > 0);
for (i = 0; i < size; i++)
if (array[i] != 0)
return true;
return false;
}

88
src/util-list.c Normal file
View file

@ -0,0 +1,88 @@
/*
* Copyright © 2008-2011 Kristian Høgsberg
* Copyright © 2011 Intel Corporation
* Copyright © 2013-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 <stddef.h>
#include <stdbool.h>
#include "util-list.h"
void
list_init(struct list *list)
{
list->prev = list;
list->next = list;
}
void
list_insert(struct list *list, struct list *elm)
{
assert((list->next != NULL && list->prev != NULL) ||
!"list->next|prev is NULL, possibly missing list_init()");
assert(((elm->next == NULL && elm->prev == NULL) || list_empty(elm)) ||
!"elm->next|prev is not NULL, list node used twice?");
elm->prev = list;
elm->next = list->next;
list->next = elm;
elm->next->prev = elm;
}
void
list_append(struct list *list, struct list *elm)
{
assert((list->next != NULL && list->prev != NULL) ||
!"list->next|prev is NULL, possibly missing list_init()");
assert(((elm->next == NULL && elm->prev == NULL) || list_empty(elm)) ||
!"elm->next|prev is not NULL, list node used twice?");
elm->next = list;
elm->prev = list->prev;
list->prev = elm;
elm->prev->next = elm;
}
void
list_remove(struct list *elm)
{
assert((elm->next != NULL && elm->prev != NULL) ||
!"list->next|prev is NULL, possibly missing list_init()");
elm->prev->next = elm->next;
elm->next->prev = elm->prev;
elm->next = NULL;
elm->prev = NULL;
}
bool
list_empty(const struct list *list)
{
assert((list->next != NULL && list->prev != NULL) ||
!"list->next|prev is NULL, possibly missing list_init()");
return list->next == list;
}

66
src/util-list.h Normal file
View file

@ -0,0 +1,66 @@
/*
* Copyright © 2008-2011 Kristian Høgsberg
* Copyright © 2011 Intel Corporation
* Copyright © 2013-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.
*/
#pragma once
#include "config.h"
#include <stdbool.h>
/*
* This list data structure is a verbatim copy from wayland-util.h from the
* Wayland project; except that wl_ prefix has been removed.
*/
struct list {
struct list *prev;
struct list *next;
};
void list_init(struct list *list);
void list_insert(struct list *list, struct list *elm);
void list_append(struct list *list, struct list *elm);
void list_remove(struct list *elm);
bool list_empty(const struct list *list);
#define container_of(ptr, type, member) \
(__typeof__(type) *)((char *)(ptr) - \
offsetof(__typeof__(type), member))
#define list_first_entry(head, pos, member) \
container_of((head)->next, __typeof__(*pos), member)
#define list_for_each(pos, head, member) \
for (pos = 0, pos = list_first_entry(head, pos, member); \
&pos->member != (head); \
pos = list_first_entry(&pos->member, pos, member))
#define list_for_each_safe(pos, tmp, head, member) \
for (pos = 0, tmp = 0, \
pos = list_first_entry(head, pos, member), \
tmp = list_first_entry(&pos->member, tmp, member); \
&pos->member != (head); \
pos = tmp, \
tmp = list_first_entry(&pos->member, tmp, member))

52
src/util-macros.h Normal file
View file

@ -0,0 +1,52 @@
/*
* Copyright © 2008-2011 Kristian Høgsberg
* Copyright © 2011 Intel Corporation
* Copyright © 2013-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.
*/
#pragma once
#include "config.h"
#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
#define ARRAY_FOR_EACH(_arr, _elem) \
for (size_t _i = 0; _i < ARRAY_LENGTH(_arr) && (_elem = &_arr[_i]); _i++)
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define ANSI_HIGHLIGHT "\x1B[0;1;39m"
#define ANSI_RED "\x1B[0;31m"
#define ANSI_GREEN "\x1B[0;32m"
#define ANSI_YELLOW "\x1B[0;33m"
#define ANSI_BLUE "\x1B[0;34m"
#define ANSI_MAGENTA "\x1B[0;35m"
#define ANSI_CYAN "\x1B[0;36m"
#define ANSI_BRIGHT_RED "\x1B[0;31;1m"
#define ANSI_BRIGHT_GREEN "\x1B[0;32;1m"
#define ANSI_BRIGHT_YELLOW "\x1B[0;33;1m"
#define ANSI_BRIGHT_BLUE "\x1B[0;34;1m"
#define ANSI_BRIGHT_MAGENTA "\x1B[0;35;1m"
#define ANSI_BRIGHT_CYAN "\x1B[0;36;1m"
#define ANSI_NORMAL "\x1B[0m"
#define CASE_RETURN_STRING(a) case a: return #a

162
src/util-matrix.h Normal file
View file

@ -0,0 +1,162 @@
/*
* Copyright © 2008 Kristian Høgsberg
* Copyright © 2013-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.
*/
#pragma once
#include "config.h"
#include <string.h>
#include <stdbool.h>
#include <math.h>
struct matrix {
float val[3][3]; /* [row][col] */
};
static inline double
deg2rad(int degree)
{
return M_PI * degree / 180.0;
}
static inline void
matrix_init_identity(struct matrix *m)
{
memset(m, 0, sizeof(*m));
m->val[0][0] = 1;
m->val[1][1] = 1;
m->val[2][2] = 1;
}
static inline void
matrix_from_farray6(struct matrix *m, const float values[6])
{
matrix_init_identity(m);
m->val[0][0] = values[0];
m->val[0][1] = values[1];
m->val[0][2] = values[2];
m->val[1][0] = values[3];
m->val[1][1] = values[4];
m->val[1][2] = values[5];
}
static inline void
matrix_init_scale(struct matrix *m, float sx, float sy)
{
matrix_init_identity(m);
m->val[0][0] = sx;
m->val[1][1] = sy;
}
static inline void
matrix_init_translate(struct matrix *m, float x, float y)
{
matrix_init_identity(m);
m->val[0][2] = x;
m->val[1][2] = y;
}
static inline void
matrix_init_rotate(struct matrix *m, int degrees)
{
double s, c;
s = sin(deg2rad(degrees));
c = cos(deg2rad(degrees));
matrix_init_identity(m);
m->val[0][0] = c;
m->val[0][1] = -s;
m->val[1][0] = s;
m->val[1][1] = c;
}
static inline bool
matrix_is_identity(const struct matrix *m)
{
return (m->val[0][0] == 1 &&
m->val[0][1] == 0 &&
m->val[0][2] == 0 &&
m->val[1][0] == 0 &&
m->val[1][1] == 1 &&
m->val[1][2] == 0 &&
m->val[2][0] == 0 &&
m->val[2][1] == 0 &&
m->val[2][2] == 1);
}
static inline void
matrix_mult(struct matrix *dest,
const struct matrix *m1,
const struct matrix *m2)
{
struct matrix m; /* allow for dest == m1 or dest == m2 */
int row, col, i;
for (row = 0; row < 3; row++) {
for (col = 0; col < 3; col++) {
double v = 0;
for (i = 0; i < 3; i++) {
v += m1->val[row][i] * m2->val[i][col];
}
m.val[row][col] = v;
}
}
memcpy(dest, &m, sizeof(m));
}
static inline void
matrix_mult_vec(const struct matrix *m, int *x, int *y)
{
int tx, ty;
tx = *x * m->val[0][0] + *y * m->val[0][1] + m->val[0][2];
ty = *x * m->val[1][0] + *y * m->val[1][1] + m->val[1][2];
*x = tx;
*y = ty;
}
static inline void
matrix_to_farray6(const struct matrix *m, float out[6])
{
out[0] = m->val[0][0];
out[1] = m->val[0][1];
out[2] = m->val[0][2];
out[3] = m->val[1][0];
out[4] = m->val[1][1];
out[5] = m->val[1][2];
}
static inline void
matrix_to_relative(struct matrix *dest, const struct matrix *src)
{
matrix_init_identity(dest);
dest->val[0][0] = src->val[0][0];
dest->val[0][1] = src->val[0][1];
dest->val[1][0] = src->val[1][0];
dest->val[1][1] = src->val[1][1];
}

View file

@ -1,7 +1,5 @@
/*
* Copyright © 2008-2011 Kristian Høgsberg
* Copyright © 2011 Intel Corporation
* Copyright © 2013-2015 Red Hat, Inc.
* Copyright © 2013-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"),
@ -23,127 +21,13 @@
* DEALINGS IN THE SOFTWARE.
*/
/*
* This list data structure is verbatim copy from wayland-util.h from the
* Wayland project; except that wl_ prefix has been removed.
*/
#include "util-prop-parsers.h"
#include "config.h"
#include <ctype.h>
#include <locale.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <libevdev/libevdev.h>
#include <string.h>
#include "libinput-util.h"
#include "libinput-private.h"
void
list_init(struct list *list)
{
list->prev = list;
list->next = list;
}
void
list_insert(struct list *list, struct list *elm)
{
assert((list->next != NULL && list->prev != NULL) ||
!"list->next|prev is NULL, possibly missing list_init()");
assert(((elm->next == NULL && elm->prev == NULL) || list_empty(elm)) ||
!"elm->next|prev is not NULL, list node used twice?");
elm->prev = list;
elm->next = list->next;
list->next = elm;
elm->next->prev = elm;
}
void
list_append(struct list *list, struct list *elm)
{
assert((list->next != NULL && list->prev != NULL) ||
!"list->next|prev is NULL, possibly missing list_init()");
assert(((elm->next == NULL && elm->prev == NULL) || list_empty(elm)) ||
!"elm->next|prev is not NULL, list node used twice?");
elm->next = list;
elm->prev = list->prev;
list->prev = elm;
elm->prev->next = elm;
}
void
list_remove(struct list *elm)
{
assert((elm->next != NULL && elm->prev != NULL) ||
!"list->next|prev is NULL, possibly missing list_init()");
elm->prev->next = elm->next;
elm->next->prev = elm->prev;
elm->next = NULL;
elm->prev = NULL;
}
bool
list_empty(const struct list *list)
{
assert((list->next != NULL && list->prev != NULL) ||
!"list->next|prev is NULL, possibly missing list_init()");
return list->next == list;
}
void
ratelimit_init(struct ratelimit *r, uint64_t ival_us, unsigned int burst)
{
r->interval = ival_us;
r->begin = 0;
r->burst = burst;
r->num = 0;
}
/*
* Perform rate-limit test. Returns RATELIMIT_PASS if the rate-limited action
* is still allowed, RATELIMIT_THRESHOLD if the limit has been reached with
* this call, and RATELIMIT_EXCEEDED if you're beyond the threshold.
* It's safe to treat the return-value as boolean, if you're not interested in
* the exact state. It evaluates to "true" if the threshold hasn't been
* exceeded, yet.
*
* The ratelimit object must be initialized via ratelimit_init().
*
* Modelled after Linux' lib/ratelimit.c by Dave Young
* <hidave.darkstar@gmail.com>, which is licensed GPLv2.
*/
enum ratelimit_state
ratelimit_test(struct ratelimit *r)
{
struct timespec ts;
uint64_t utime;
if (r->interval <= 0 || r->burst <= 0)
return RATELIMIT_PASS;
clock_gettime(CLOCK_MONOTONIC, &ts);
utime = s2us(ts.tv_sec) + ns2us(ts.tv_nsec);
if (r->begin <= 0 || r->begin + r->interval < utime) {
/* reset counter */
r->begin = utime;
r->num = 1;
return RATELIMIT_PASS;
} else if (r->num < r->burst) {
/* continue burst */
return (++r->num == r->burst) ? RATELIMIT_THRESHOLD
: RATELIMIT_PASS;
}
return RATELIMIT_EXCEEDED;
}
#include "util-macros.h"
#include "util-strings.h"
/* Helper function to parse the mouse DPI tag from udev.
* The tag is of the form:
@ -517,133 +401,3 @@ out:
strv_free(strv);
return rc;
}
/**
* Return the next word in a string pointed to by state before the first
* separator character. Call repeatedly to tokenize a whole string.
*
* @param state Current state
* @param len String length of the word returned
* @param separators List of separator characters
*
* @return The first word in *state, NOT null-terminated
*/
static const char *
next_word(const char **state, size_t *len, const char *separators)
{
const char *next = *state;
size_t l;
if (!*next)
return NULL;
next += strspn(next, separators);
if (!*next) {
*state = next;
return NULL;
}
l = strcspn(next, separators);
*state = next + l;
*len = l;
return next;
}
/**
* Return a null-terminated string array with the tokens in the input
* string, e.g. "one two\tthree" with a separator list of " \t" will return
* an array [ "one", "two", "three", NULL ].
*
* Use strv_free() to free the array.
*
* @param in Input string
* @param separators List of separator characters
*
* @return A null-terminated string array or NULL on errors
*/
char **
strv_from_string(const char *in, const char *separators)
{
const char *s, *word;
char **strv = NULL;
int nelems = 0, idx;
size_t l;
assert(in != NULL);
s = in;
while ((word = next_word(&s, &l, separators)) != NULL)
nelems++;
if (nelems == 0)
return NULL;
nelems++; /* NULL-terminated */
strv = zalloc(nelems * sizeof *strv);
idx = 0;
s = in;
while ((word = next_word(&s, &l, separators)) != NULL) {
char *copy = strndup(word, l);
if (!copy) {
strv_free(strv);
return NULL;
}
strv[idx++] = copy;
}
return strv;
}
/**
* Return a newly allocated string with all elements joined by the
* joiner, same as Python's string.join() basically.
* A strv of ["one", "two", "three", NULL] with a joiner of ", " results
* in "one, two, three".
*
* An empty strv ([NULL]) returns NULL, same for passing NULL as either
* argument.
*
* @param strv Input string arrray
* @param joiner Joiner between the elements in the final string
*
* @return A null-terminated string joining all elements
*/
char *
strv_join(char **strv, const char *joiner)
{
char **s;
char *str;
size_t slen = 0;
size_t count = 0;
if (!strv || !joiner)
return NULL;
if (strv[0] == NULL)
return NULL;
for (s = strv, count = 0; *s; s++, count++) {
slen += strlen(*s);
}
assert(slen < 1000);
assert(strlen(joiner) < 1000);
assert(count > 0);
assert(count < 100);
slen += (count - 1) * strlen(joiner);
str = zalloc(slen + 1); /* trailing \0 */
for (s = strv; *s; s++) {
strcat(str, *s);
--count;
if (count > 0)
strcat(str, joiner);
}
return str;
}

56
src/util-prop-parsers.h Normal file
View file

@ -0,0 +1,56 @@
/*
* Copyright © 2013-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 "config.h"
#include <linux/input.h>
#include <stdbool.h>
#include <stddef.h>
int parse_mouse_dpi_property(const char *prop);
int parse_mouse_wheel_click_angle_property(const char *prop);
int parse_mouse_wheel_click_count_property(const char *prop);
bool parse_dimension_property(const char *prop, size_t *width, size_t *height);
bool parse_calibration_property(const char *prop, float calibration[6]);
bool parse_range_property(const char *prop, int *hi, int *lo);
#define EVENT_CODE_UNDEFINED 0xffff
bool parse_evcode_property(const char *prop, struct input_event *events, size_t *nevents);
enum tpkbcombo_layout {
TPKBCOMBO_LAYOUT_UNKNOWN,
TPKBCOMBO_LAYOUT_BELOW,
};
bool parse_tpkbcombo_layout_poperty(const char *prop,
enum tpkbcombo_layout *layout);
enum switch_reliability {
RELIABILITY_UNKNOWN,
RELIABILITY_RELIABLE,
RELIABILITY_WRITE_OPEN,
};
bool
parse_switch_reliability_property(const char *prop,
enum switch_reliability *reliability);

79
src/util-ratelimit.c Normal file
View file

@ -0,0 +1,79 @@
/*
* Copyright © 2008-2011 Kristian Høgsberg
* Copyright © 2011 Intel Corporation
* Copyright © 2013-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 <time.h>
#include "util-ratelimit.h"
#include "util-time.h"
void
ratelimit_init(struct ratelimit *r, uint64_t ival_us, unsigned int burst)
{
r->interval = ival_us;
r->begin = 0;
r->burst = burst;
r->num = 0;
}
/*
* Perform rate-limit test. Returns RATELIMIT_PASS if the rate-limited action
* is still allowed, RATELIMIT_THRESHOLD if the limit has been reached with
* this call, and RATELIMIT_EXCEEDED if you're beyond the threshold.
* It's safe to treat the return-value as boolean, if you're not interested in
* the exact state. It evaluates to "true" if the threshold hasn't been
* exceeded, yet.
*
* The ratelimit object must be initialized via ratelimit_init().
*
* Modelled after Linux' lib/ratelimit.c by Dave Young
* <hidave.darkstar@gmail.com>, which is licensed GPLv2.
*/
enum ratelimit_state
ratelimit_test(struct ratelimit *r)
{
struct timespec ts;
uint64_t utime;
if (r->interval <= 0 || r->burst <= 0)
return RATELIMIT_PASS;
clock_gettime(CLOCK_MONOTONIC, &ts);
utime = s2us(ts.tv_sec) + ns2us(ts.tv_nsec);
if (r->begin <= 0 || r->begin + r->interval < utime) {
/* reset counter */
r->begin = utime;
r->num = 1;
return RATELIMIT_PASS;
} else if (r->num < r->burst) {
/* continue burst */
return (++r->num == r->burst) ? RATELIMIT_THRESHOLD
: RATELIMIT_PASS;
}
return RATELIMIT_EXCEEDED;
}

45
src/util-ratelimit.h Normal file
View file

@ -0,0 +1,45 @@
/*
* Copyright © 2008 Kristian Høgsberg
* Copyright © 2013-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.
*/
#pragma once
#include "config.h"
#include <stdint.h>
enum ratelimit_state {
RATELIMIT_EXCEEDED,
RATELIMIT_THRESHOLD,
RATELIMIT_PASS,
};
struct ratelimit {
uint64_t interval;
uint64_t begin;
unsigned int burst;
unsigned int num;
};
void ratelimit_init(struct ratelimit *r, uint64_t ival_ms, unsigned int burst);
enum ratelimit_state ratelimit_test(struct ratelimit *r);

157
src/util-strings.c Normal file
View file

@ -0,0 +1,157 @@
/*
* Copyright © 2008 Kristian Høgsberg
* Copyright © 2013-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 "util-strings.h"
/**
* Return the next word in a string pointed to by state before the first
* separator character. Call repeatedly to tokenize a whole string.
*
* @param state Current state
* @param len String length of the word returned
* @param separators List of separator characters
*
* @return The first word in *state, NOT null-terminated
*/
static const char *
next_word(const char **state, size_t *len, const char *separators)
{
const char *next = *state;
size_t l;
if (!*next)
return NULL;
next += strspn(next, separators);
if (!*next) {
*state = next;
return NULL;
}
l = strcspn(next, separators);
*state = next + l;
*len = l;
return next;
}
/**
* Return a null-terminated string array with the tokens in the input
* string, e.g. "one two\tthree" with a separator list of " \t" will return
* an array [ "one", "two", "three", NULL ].
*
* Use strv_free() to free the array.
*
* @param in Input string
* @param separators List of separator characters
*
* @return A null-terminated string array or NULL on errors
*/
char **
strv_from_string(const char *in, const char *separators)
{
const char *s, *word;
char **strv = NULL;
int nelems = 0, idx;
size_t l;
assert(in != NULL);
s = in;
while ((word = next_word(&s, &l, separators)) != NULL)
nelems++;
if (nelems == 0)
return NULL;
nelems++; /* NULL-terminated */
strv = zalloc(nelems * sizeof *strv);
idx = 0;
s = in;
while ((word = next_word(&s, &l, separators)) != NULL) {
char *copy = strndup(word, l);
if (!copy) {
strv_free(strv);
return NULL;
}
strv[idx++] = copy;
}
return strv;
}
/**
* Return a newly allocated string with all elements joined by the
* joiner, same as Python's string.join() basically.
* A strv of ["one", "two", "three", NULL] with a joiner of ", " results
* in "one, two, three".
*
* An empty strv ([NULL]) returns NULL, same for passing NULL as either
* argument.
*
* @param strv Input string arrray
* @param joiner Joiner between the elements in the final string
*
* @return A null-terminated string joining all elements
*/
char *
strv_join(char **strv, const char *joiner)
{
char **s;
char *str;
size_t slen = 0;
size_t count = 0;
if (!strv || !joiner)
return NULL;
if (strv[0] == NULL)
return NULL;
for (s = strv, count = 0; *s; s++, count++) {
slen += strlen(*s);
}
assert(slen < 1000);
assert(strlen(joiner) < 1000);
assert(count > 0);
assert(count < 100);
slen += (count - 1) * strlen(joiner);
str = zalloc(slen + 1); /* trailing \0 */
for (s = strv; *s; s++) {
strcat(str, *s);
--count;
if (count > 0)
strcat(str, joiner);
}
return str;
}

314
src/util-strings.h Normal file
View file

@ -0,0 +1,314 @@
/*
* Copyright © 2008 Kristian Høgsberg
* Copyright © 2013-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.
*/
#pragma once
#include "config.h"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdarg.h>
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
#ifdef HAVE_XLOCALE_H
#include <xlocale.h>
#endif
#define streq(s1, s2) (strcmp((s1), (s2)) == 0)
#define strneq(s1, s2, n) (strncmp((s1), (s2), (n)) == 0)
static inline void *
zalloc(size_t size)
{
void *p;
/* We never need to alloc anything more than 1,5 MB so we can assume
* if we ever get above that something's going wrong */
if (size > 1536 * 1024)
assert(!"bug: internal malloc size limit exceeded");
p = calloc(1, size);
if (!p)
abort();
return p;
}
/**
* strdup guaranteed to succeed. If the input string is NULL, the output
* string is NULL. If the input string is a string pointer, we strdup or
* abort on failure.
*/
static inline char*
safe_strdup(const char *str)
{
char *s;
if (!str)
return NULL;
s = strdup(str);
if (!s)
abort();
return s;
}
/**
* Simple wrapper for asprintf that ensures the passed in-pointer is set
* to NULL upon error.
* The standard asprintf() call does not guarantee the passed in pointer
* will be NULL'ed upon failure, whereas this wrapper does.
*
* @param strp pointer to set to newly allocated string.
* This pointer should be passed to free() to release when done.
* @param fmt the format string to use for printing.
* @return The number of bytes printed (excluding the null byte terminator)
* upon success or -1 upon failure. In the case of failure the pointer is set
* to NULL.
*/
__attribute__ ((format (printf, 2, 3)))
static inline int
xasprintf(char **strp, const char *fmt, ...)
{
int rc = 0;
va_list args;
va_start(args, fmt);
rc = vasprintf(strp, fmt, args);
va_end(args);
if ((rc == -1) && strp)
*strp = NULL;
return rc;
}
static inline bool
safe_atoi_base(const char *str, int *val, int base)
{
char *endptr;
long v;
assert(base == 10 || base == 16 || base == 8);
errno = 0;
v = strtol(str, &endptr, base);
if (errno > 0)
return false;
if (str == endptr)
return false;
if (*str != '\0' && *endptr != '\0')
return false;
if (v > INT_MAX || v < INT_MIN)
return false;
*val = v;
return true;
}
static inline bool
safe_atoi(const char *str, int *val)
{
return safe_atoi_base(str, val, 10);
}
static inline bool
safe_atou_base(const char *str, unsigned int *val, int base)
{
char *endptr;
unsigned long v;
assert(base == 10 || base == 16 || base == 8);
errno = 0;
v = strtoul(str, &endptr, base);
if (errno > 0)
return false;
if (str == endptr)
return false;
if (*str != '\0' && *endptr != '\0')
return false;
if ((long)v < 0)
return false;
*val = v;
return true;
}
static inline bool
safe_atou(const char *str, unsigned int *val)
{
return safe_atou_base(str, val, 10);
}
static inline bool
safe_atod(const char *str, double *val)
{
char *endptr;
double v;
#ifdef HAVE_LOCALE_H
locale_t c_locale;
#endif
size_t slen = strlen(str);
/* We don't have a use-case where we want to accept hex for a double
* or any of the other values strtod can parse */
for (size_t i = 0; i < slen; i++) {
char c = str[i];
if (isdigit(c))
continue;
switch(c) {
case '+':
case '-':
case '.':
break;
default:
return false;
}
}
#ifdef HAVE_LOCALE_H
/* Create a "C" locale to force strtod to use '.' as separator */
c_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0);
if (c_locale == (locale_t)0)
return false;
errno = 0;
v = strtod_l(str, &endptr, c_locale);
freelocale(c_locale);
#else
/* No locale support in provided libc, assume it already uses '.' */
errno = 0;
v = strtod(str, &endptr);
#endif
if (errno > 0)
return false;
if (str == endptr)
return false;
if (*str != '\0' && *endptr != '\0')
return false;
if (v != 0.0 && !isnormal(v))
return false;
*val = v;
return true;
}
char **strv_from_string(const char *string, const char *separator);
char *strv_join(char **strv, const char *separator);
static inline void
strv_free(char **strv) {
char **s = strv;
if (!strv)
return;
while (*s != NULL) {
free(*s);
*s = (char*)0x1; /* detect use-after-free */
s++;
}
free (strv);
}
struct key_value_str{
char *key;
char *value;
};
struct key_value_double {
double key;
double value;
};
static inline ssize_t
kv_double_from_string(const char *string,
const char *pair_separator,
const char *kv_separator,
struct key_value_double **result_out)
{
char **pairs;
char **pair;
struct key_value_double *result = NULL;
ssize_t npairs = 0;
unsigned int idx = 0;
if (!pair_separator || pair_separator[0] == '\0' ||
!kv_separator || kv_separator[0] == '\0')
return -1;
pairs = strv_from_string(string, pair_separator);
if (!pairs)
return -1;
for (pair = pairs; *pair; pair++)
npairs++;
if (npairs == 0)
goto error;
result = zalloc(npairs * sizeof *result);
for (pair = pairs; *pair; pair++) {
char **kv = strv_from_string(*pair, kv_separator);
double k, v;
if (!kv || !kv[0] || !kv[1] || kv[2] ||
!safe_atod(kv[0], &k) ||
!safe_atod(kv[1], &v)) {
strv_free(kv);
goto error;
}
result[idx].key = k;
result[idx].value = v;
idx++;
strv_free(kv);
}
strv_free(pairs);
*result_out = result;
return npairs;
error:
strv_free(pairs);
free(result);
return -1;
}

83
src/util-time.h Normal file
View file

@ -0,0 +1,83 @@
/*
* Copyright © 2013-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 "config.h"
#include <time.h>
#include <stdint.h>
#include <unistd.h>
static inline void
msleep(unsigned int ms)
{
usleep(ms * 1000);
}
static inline uint64_t
us(uint64_t us)
{
return us;
}
static inline uint64_t
ns2us(uint64_t ns)
{
return us(ns / 1000);
}
static inline uint64_t
ms2us(uint64_t ms)
{
return us(ms * 1000);
}
static inline uint64_t
s2us(uint64_t s)
{
return ms2us(s * 1000);
}
static inline uint32_t
us2ms(uint64_t us)
{
return (uint32_t)(us / 1000);
}
static inline uint64_t
tv2us(const struct timeval *tv)
{
return s2us(tv->tv_sec) + tv->tv_usec;
}
static inline struct timeval
us2tv(uint64_t time)
{
struct timeval tv;
tv.tv_sec = time / ms2us(1000);
tv.tv_usec = time % ms2us(1000);
return tv;
}

View file

@ -0,0 +1,6 @@
/* compile test for the util files */
#include @FILE@
int main(void) {
return 0;
}

View file

@ -24,11 +24,18 @@
#include <config.h>
#include <check.h>
#include <libinput-util.h>
#include <valgrind/valgrind.h>
#include "libinput-util.h"
#include "util-list.h"
#include "util-strings.h"
#include "util-time.h"
#include "util-prop-parsers.h"
#include "util-macros.h"
#include "util-bits.h"
#include "util-ratelimit.h"
#include "util-matrix.h"
#define TEST_VERSIONSORT
#include "libinput-versionsort.h"