diff options
Diffstat (limited to 'src/draglock.c')
-rw-r--r-- | src/draglock.c | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/src/draglock.c b/src/draglock.c new file mode 100644 index 0000000..e0a91d0 --- /dev/null +++ b/src/draglock.c @@ -0,0 +1,282 @@ +/* + * Copyright © 2015 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of Red Hat + * not be used in advertising or publicity pertaining to distribution + * of the software without specific, written prior permission. Red + * Hat makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied + * warranty. + * + * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "draglock.h" + +#include <string.h> +#include <stdlib.h> + +#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) + +static int +draglock_parse_config(struct draglock *dl, const char *config) +{ + int button = 0, target = 0; + const char *str = NULL; + char *end_str = NULL; + int pairs[DRAGLOCK_MAX_BUTTONS] = {0}; + + if (!config) + return 0; + + /* empty string disables drag lock */ + if (*config == '\0') { + dl->mode = DRAGLOCK_DISABLED; + return 0; + } + + /* check for a single-number string first, config is "<int>" */ + button = strtol(config, &end_str, 10); + if (*end_str == '\0') { + if (button < 0 || button >= DRAGLOCK_MAX_BUTTONS) + return 1; + /* we allow for button 0 so stacked xorg.conf.d snippets can + * disable the config again */ + if (button == 0) { + dl->mode = DRAGLOCK_DISABLED; + return 0; + } + + return draglock_set_meta(dl, button); + } + + dl->mode = DRAGLOCK_DISABLED; + + /* check for a set of button pairs, config is + * "<int> <int> <int> <int>..." */ + str = config; + while (*str != '\0') { + button = strtol(str, &end_str, 10); + if (*end_str == '\0') + return 1; + + str = end_str; + target = strtol(str, &end_str, 10); + if (end_str == str) + return 1; + if (button <= 0 || button >= DRAGLOCK_MAX_BUTTONS || target >= DRAGLOCK_MAX_BUTTONS) + return 1; + + pairs[button] = target; + str = end_str; + } + + return draglock_set_pairs(dl, pairs, ARRAY_SIZE(pairs)); +} + +int +draglock_init_from_string(struct draglock *dl, const char *config) +{ + dl->mode = DRAGLOCK_DISABLED; + + dl->meta_button = 0; + dl->meta_state = false; + memset(dl->lock_pair, 0, sizeof(dl->lock_pair)); + memset(dl->lock_state, 0, sizeof(dl->lock_state)); + + return draglock_parse_config(dl, config); +} + +enum draglock_mode +draglock_get_mode(const struct draglock *dl) +{ + return dl->mode; +} + +int +draglock_get_meta(const struct draglock *dl) +{ + if (dl->mode == DRAGLOCK_META) + return dl->meta_button; + return 0; +} + +size_t +draglock_get_pairs(const struct draglock *dl, int *array, size_t nelem) +{ + unsigned int i; + size_t last = 0; + + if (dl->mode != DRAGLOCK_PAIRS) + return 0; + + /* size 1 array with the meta button */ + if (dl->meta_button) { + *array = dl->meta_button; + return 1; + } + + /* size N array with a[0] == 0, the rest ordered by button number */ + memset(array, 0, nelem * sizeof(array[0])); + for (i = 0; i < nelem && i < ARRAY_SIZE(dl->lock_pair); i++) { + array[i] = dl->lock_pair[i]; + if (array[i] != 0 && i > last) + last = i; + } + return last; +} + +int +draglock_set_meta(struct draglock *dl, int meta_button) +{ + if (meta_button < 0 || meta_button >= DRAGLOCK_MAX_BUTTONS) + return 1; + + dl->meta_button = meta_button; + dl->mode = meta_button ? DRAGLOCK_META : DRAGLOCK_DISABLED; + + return 0; +} + +int +draglock_set_pairs(struct draglock *dl, const int *array, size_t nelem) +{ + unsigned int i; + + if (nelem == 0 || array[0] != 0) + return 1; + + for (i = 0; i < nelem; i++) { + if (array[i] < 0 || array[i] >= DRAGLOCK_MAX_BUTTONS) + return 1; + } + + dl->mode = DRAGLOCK_DISABLED; + for (i = 0; i < nelem; i++) { + dl->lock_pair[i] = array[i]; + if (dl->lock_pair[i]) + dl->mode = DRAGLOCK_PAIRS; + } + + return 0; +} + +static int +draglock_filter_meta(struct draglock *dl, int *button, int *press) +{ + int b = *button, + is_press = *press; + + if (b == dl->meta_button) { + if (is_press) + dl->meta_state = true; + *button = 0; + return 0; + } + + switch (dl->lock_state[b]) { + case DRAGLOCK_BUTTON_STATE_NONE: + if (dl->meta_state && is_press) { + dl->lock_state[b] = DRAGLOCK_BUTTON_STATE_DOWN_1; + dl->meta_state = false; + } + break; + case DRAGLOCK_BUTTON_STATE_DOWN_1: + if (!is_press) { + dl->lock_state[b] = DRAGLOCK_BUTTON_STATE_UP_1; + b = 0; + } + break; + case DRAGLOCK_BUTTON_STATE_UP_1: + if (is_press) { + dl->lock_state[b] = DRAGLOCK_BUTTON_STATE_DOWN_2; + b = 0; + } + break; + case DRAGLOCK_BUTTON_STATE_DOWN_2: + if (!is_press) { + dl->lock_state[b] = DRAGLOCK_BUTTON_STATE_NONE; + } + break; + } + + *button = b; + + return 0; +} + +static int +draglock_filter_pair(struct draglock *dl, int *button, int *press) +{ + int b = *button, + is_press = *press; + + if (dl->lock_pair[b] == 0) + return 0; + + switch (dl->lock_state[b]) { + case DRAGLOCK_BUTTON_STATE_NONE: + if (is_press) { + dl->lock_state[b] = DRAGLOCK_BUTTON_STATE_DOWN_1; + b = dl->lock_pair[b]; + } + break; + case DRAGLOCK_BUTTON_STATE_DOWN_1: + if (!is_press) { + dl->lock_state[b] = DRAGLOCK_BUTTON_STATE_UP_1; + b = 0; + } + break; + case DRAGLOCK_BUTTON_STATE_UP_1: + if (is_press) { + dl->lock_state[b] = DRAGLOCK_BUTTON_STATE_DOWN_2; + b = 0; + } + break; + case DRAGLOCK_BUTTON_STATE_DOWN_2: + if (!is_press) { + dl->lock_state[b] = DRAGLOCK_BUTTON_STATE_NONE; + b = dl->lock_pair[b]; + } + break; + } + + *button = b; + + return 0; +} + +int +draglock_filter_button(struct draglock *dl, int *button, int *is_press) +{ + if (*button == 0) + return 0; + + switch(dl->mode) { + case DRAGLOCK_DISABLED: + return 0; + case DRAGLOCK_META: + return draglock_filter_meta(dl, button, is_press); + case DRAGLOCK_PAIRS: + return draglock_filter_pair(dl, button, is_press); + default: + abort(); + break; + } + + return 0; +} |