/* * 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 #include #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 "" */ 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 * " ..." */ 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; }