.gitignore | 8 Makefile.am | 2 configure.ac | 3 include/libinput-properties.h | 10 man/libinput.man | 58 src/Makefile.am | 6 src/draglock.c | 282 ++++ src/draglock.h | 159 ++ src/libinput.c | 2616 --------------------------------------- src/xf86libinput.c | 2765 ++++++++++++++++++++++++++++++++++++++++++ test/.gitignore | 1 test/Makefile.am | 13 test/test-draglock.c | 540 ++++++++ 13 files changed, 3841 insertions(+), 2622 deletions(-)
New commits: commit f48b64c8cd6f280ba8c589842ec2522a4bfe9b5c Author: Peter Hutterer <[email protected]> Date: Mon Aug 31 13:27:09 2015 +1000 xf86-input-libinput 0.14.0 Signed-off-by: Peter Hutterer <[email protected]> diff --git a/configure.ac b/configure.ac index 26e0e70..bfb1d75 100644 --- a/configure.ac +++ b/configure.ac @@ -23,7 +23,7 @@ # Initialize Autoconf AC_PREREQ([2.60]) AC_INIT([xf86-input-libinput], - [0.13.0], + [0.14.0], [https://bugs.freedesktop.org/enter_bug.cgi?product=xorg], [xf86-input-libinput]) AC_CONFIG_SRCDIR([Makefile.am]) commit b55239ef2552c43efd4c4fb7d39e22c255dd4e6d Author: Yomi0 <[email protected]> Date: Sun Aug 30 23:14:25 2015 -0400 Fix typo in libinput.man Correct typo. Draging to dragging. diff --git a/man/libinput.man b/man/libinput.man index 3c35776..196686d 100644 --- a/man/libinput.man +++ b/man/libinput.man @@ -123,7 +123,7 @@ Enables or disables tap-to-click behavior. .BI "Option \*qTappingDragLock\*q \*q" bool \*q Enables or disables drag lock during tapping behavior. When enabled, a finger up during tap-and-drag will not immediately release the button. If -the finger is set down again within the timeout, the draging process +the finger is set down again within the timeout, the dragging process continues. .TP 7 .BI "Option \*qDisableWhileTyping\*q \*q" bool \*q commit 9563334dda3c5563550fb2534b228c47216ec008 Author: Peter Hutterer <[email protected]> Date: Thu Aug 13 11:03:44 2015 +1000 Use xf86OpenSerial instead of a direct open() call This will transparently handle server-side fds for us. Signed-off-by: Peter Hutterer <[email protected]> Reviewed-by: Hans de Goede <[email protected]> Reviewed-by: Martin Pieuchot <[email protected]> diff --git a/src/xf86libinput.c b/src/xf86libinput.c index 204f288..7ecc0e6 100644 --- a/src/xf86libinput.c +++ b/src/xf86libinput.c @@ -34,6 +34,7 @@ #include <exevents.h> #include <xkbsrv.h> #include <xf86Xinput.h> +#include <xf86_OSproc.h> #include <xserver-properties.h> #include <libinput.h> #include <linux/input.h> @@ -923,7 +924,6 @@ open_restricted(const char *path, int flags, void *data) char *device = xf86CheckStrOption(pInfo->options, "Device", NULL); if (device != NULL && strcmp(path, device) == 0) { - fd = xf86CheckIntOption(pInfo->options, "fd", -1); free(device); break; } @@ -935,8 +935,7 @@ open_restricted(const char *path, int flags, void *data) return -ENODEV; } - if (fd == -1) - fd = open(path, flags); + fd = xf86OpenSerial(pInfo->options); return fd < 0 ? -errno : fd; } @@ -957,7 +956,7 @@ close_restricted(int fd, void *data) } if (!found) - close(fd); + xf86CloseSerial(fd); } const struct libinput_interface interface = { commit 353c52f2bec035f04c136c8f3b28571e2a4515df Author: Peter Hutterer <[email protected]> Date: Thu Aug 13 10:59:39 2015 +1000 Revamp server fd opening The server already stores the server-fd in the options, so we only need to run through the list of current devices, find a match and extract that fd from the options. Less magic in our driver and it gives us a pInfo handle in open_restricted which we'll can use for xf86OpenSerial(). Signed-off-by: Peter Hutterer <[email protected]> Reviewed-by: Hans de Goede <[email protected]> Reviewed-by: Martin Pieuchot <[email protected]> diff --git a/src/xf86libinput.c b/src/xf86libinput.c index 8987518..204f288 100644 --- a/src/xf86libinput.c +++ b/src/xf86libinput.c @@ -118,80 +118,11 @@ struct xf86libinput { struct draglock draglock; }; -/* - libinput provides a userdata for the context, but not per path device. so - the open_restricted call has the libinput context, but no reference to - the pInfo->fd that we actually need to return. - To avoid this, we store each path/fd combination during pre_init in the - context, then return that during open_restricted. If a device is added - twice with two different fds this may give us the wrong fd but why are - you doing that anyway. - */ -struct serverfd { - struct xorg_list node; - int fd; - char *path; -}; - static inline int use_server_fd(const InputInfoPtr pInfo) { return pInfo->fd > -1 && (pInfo->flags & XI86_SERVER_FD); } -static inline void -fd_push(struct xf86libinput_driver *context, - int fd, - const char *path) -{ - struct serverfd *sfd = xnfcalloc(1, sizeof(*sfd)); - - sfd->fd = fd; - sfd->path = xnfstrdup(path); - xorg_list_add(&sfd->node, &context->server_fds); -} - -static inline int -fd_get(struct xf86libinput_driver *context, - const char *path) -{ - struct serverfd *sfd; - - xorg_list_for_each_entry(sfd, &context->server_fds, node) { - if (strcmp(path, sfd->path) == 0) - return sfd->fd; - } - - return -1; -} - -static inline void -fd_pop(struct xf86libinput_driver *context, int fd) -{ - struct serverfd *sfd; - - xorg_list_for_each_entry(sfd, &context->server_fds, node) { - if (fd != sfd->fd) - continue; - - xorg_list_del(&sfd->node); - free(sfd->path); - free(sfd); - break; - } -} - -static inline int -fd_find(struct xf86libinput_driver *context, int fd) -{ - struct serverfd *sfd; - - xorg_list_for_each_entry(sfd, &context->server_fds, node) { - if (fd == sfd->fd) - return fd; - } - return -1; -} - static inline unsigned int btn_linux2xorg(unsigned int b) { @@ -360,12 +291,6 @@ xf86libinput_on(DeviceIntPtr dev) struct libinput *libinput = driver_context.libinput; struct libinput_device *device = driver_data->device; - if (use_server_fd(pInfo)) { - char *path = xf86SetStrOption(pInfo->options, "Device", NULL); - fd_push(&driver_context, pInfo->fd, path); - free(path); - } - device = libinput_path_add_device(libinput, driver_data->path); if (!device) return !Success; @@ -404,7 +329,6 @@ xf86libinput_off(DeviceIntPtr dev) } if (use_server_fd(pInfo)) { - fd_pop(&driver_context, pInfo->fd); pInfo->fd = xf86SetIntOption(pInfo->options, "fd", -1); } else { pInfo->fd = -1; @@ -981,13 +905,36 @@ xf86libinput_read_input(InputInfoPtr pInfo) } } +/* + libinput provides a userdata for the context, but not per path device. so + the open_restricted call has the libinput context, but no reference to + the pInfo->fd that we actually need to return. + The server stores the fd in the options though, so we just get it from + there. If a device is added twice with two different fds this may give us + the wrong fd but why are you doing that anyway. + */ static int open_restricted(const char *path, int flags, void *data) { - struct xf86libinput_driver *context = data; - int fd; + InputInfoPtr pInfo; + int fd = -1; + + nt_list_for_each_entry(pInfo, xf86FirstLocalDevice(), next) { + char *device = xf86CheckStrOption(pInfo->options, "Device", NULL); + + if (device != NULL && strcmp(path, device) == 0) { + fd = xf86CheckIntOption(pInfo->options, "fd", -1); + free(device); + break; + } + free(device); + } + + if (pInfo == NULL) { + xf86Msg(X_ERROR, "Failed to look up path '%s'\n", path); + return -ENODEV; + } - fd = fd_get(context, path); if (fd == -1) fd = open(path, flags); return fd < 0 ? -errno : fd; @@ -996,9 +943,20 @@ open_restricted(const char *path, int flags, void *data) static void close_restricted(int fd, void *data) { - struct xf86libinput_driver *context = data; + InputInfoPtr pInfo; + int server_fd = -1; + BOOL found = FALSE; + + nt_list_for_each_entry(pInfo, xf86FirstLocalDevice(), next) { + server_fd = xf86CheckIntOption(pInfo->options, "fd", -1); - if (fd_find(context, fd) == -1) + if (server_fd == fd) { + found = TRUE; + break; + } + } + + if (!found) close(fd); } @@ -1523,9 +1481,6 @@ xf86libinput_pre_init(InputDriverPtr drv, goto fail; } - if (use_server_fd(pInfo)) - fd_push(&driver_context, pInfo->fd, path); - device = libinput_path_add_device(libinput, path); if (!device) { xf86IDrvMsg(pInfo, X_ERROR, "Failed to create a device for %s\n", path); @@ -1537,8 +1492,6 @@ xf86libinput_pre_init(InputDriverPtr drv, */ libinput_device_ref(device); libinput_path_remove_device(device); - if (use_server_fd(pInfo)) - fd_pop(&driver_context, pInfo->fd); pInfo->private = driver_data; driver_data->path = path; @@ -1564,8 +1517,6 @@ xf86libinput_pre_init(InputDriverPtr drv, return Success; fail: - if (use_server_fd(pInfo) && driver_context.libinput != NULL) - fd_pop(&driver_context, pInfo->fd); if (driver_data->valuators) valuator_mask_free(&driver_data->valuators); if (driver_data->valuators_unaccelerated) commit f139f1424936abdc43b2c8611d569b496ffa4a68 Author: Peter Hutterer <[email protected]> Date: Wed Aug 12 10:15:31 2015 +1000 Add an option to disable horizontal scrolling libinput always has horizontal scrolling enabled and punts the decision when to scroll horizontally to the toolkit/widget. This is the better approach, but while we have a stack that's not ready for that, and in the X case likely never will be fully ready provide an option to disable horizontal scrolling. This option doesn't really disable horizontal scrolling, it merely discards any horizontal scroll delta. libinput will still think it's scrolling. https://bugs.freedesktop.org/show_bug.cgi?id=91589 Signed-off-by: Peter Hutterer <[email protected]> Reviewed-by: Hans de Goede <[email protected]> diff --git a/include/libinput-properties.h b/include/libinput-properties.h index ed009d5..06fad7f 100644 --- a/include/libinput-properties.h +++ b/include/libinput-properties.h @@ -120,4 +120,8 @@ the target button number */ #define LIBINPUT_PROP_DRAG_LOCK_BUTTONS "libinput Drag Lock Buttons" +/* Horizontal scroll events enabled: BOOL, 1 value (0 or 1). + * If disabled, horizontal scroll events are discarded */ +#define LIBINPUT_PROP_HORIZ_SCROLL_ENABLED "libinput Horizonal Scroll Enabled" + #endif /* _LIBINPUT_PROPERTIES_H_ */ diff --git a/man/libinput.man b/man/libinput.man index ff7a411..3c35776 100644 --- a/man/libinput.man +++ b/man/libinput.man @@ -107,6 +107,12 @@ Enables a scroll method. Permitted values are Not all devices support all options, if an option is unsupported, the default scroll option for this device is used. .TP 7 +.BI "Option \*qHorizontalScrolling\*q" bool \*q +Disables horizontal scrolling. When disabled, this driver will discard any +horizontal scroll events from libinput. Note that this does not disable +horizontal scrolling, it merely discards the horizontal axis from any scroll +events. +.TP 7 .BI "Option \*qSendEventsMode\*q \*q" (disabled|enabled|disabled-on-external-mouse) \*q Sets the send events mode to disabled, enabled, or "disable when an external mouse is connected". @@ -226,6 +232,10 @@ Either one 8-bit value specifying the meta drag lock button, or a list of button pairs. See section .B BUTTON DRAG LOCK for details. +.TP 7 +.BI "libinput Horizontal Scrolling Enabled" +1 boolean value (8 bit, 0 or 1). Indicates whether horizontal scrolling +events are enabled or not. .SH BUTTON MAPPING X clients receive events with logical button numbers, where 1, 2, 3 diff --git a/src/xf86libinput.c b/src/xf86libinput.c index 8500792..8987518 100644 --- a/src/xf86libinput.c +++ b/src/xf86libinput.c @@ -111,6 +111,8 @@ struct xf86libinput { enum libinput_config_click_method click_method; unsigned char btnmap[MAX_BUTTONS + 1]; + + BOOL horiz_scrolling_enabled; } options; struct draglock draglock; @@ -831,6 +833,10 @@ xf86libinput_handle_axis(InputInfoPtr pInfo, struct libinput_event_pointer *even } valuator_mask_set_double(mask, 3, value); } + + if (!driver_data->options.horiz_scrolling_enabled) + goto out; + axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL; if (libinput_event_pointer_has_axis(event, axis)) { if (source == LIBINPUT_POINTER_AXIS_SOURCE_WHEEL) { @@ -842,6 +848,7 @@ xf86libinput_handle_axis(InputInfoPtr pInfo, struct libinput_event_pointer *even valuator_mask_set_double(mask, 2, value); } +out: xf86PostMotionEventM(dev, Relative, mask); } @@ -1425,6 +1432,12 @@ xf86libinput_parse_draglock_option(InputInfoPtr pInfo, free(str); } +static inline BOOL +xf86libinput_parse_horiz_scroll_option(InputInfoPtr pInfo) +{ + return xf86SetBoolOption(pInfo->options, "HorizontalScrolling", TRUE); +} + static void xf86libinput_parse_options(InputInfoPtr pInfo, struct xf86libinput *driver_data, @@ -1450,8 +1463,10 @@ xf86libinput_parse_options(InputInfoPtr pInfo, xf86libinput_parse_buttonmap_option(pInfo, options->btnmap, sizeof(options->btnmap)); - if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER)) + if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER)) { xf86libinput_parse_draglock_option(pInfo, driver_data); + options->horiz_scrolling_enabled = xf86libinput_parse_horiz_scroll_option(pInfo); + } } static int @@ -1647,6 +1662,7 @@ static Atom prop_disable_while_typing_default; /* driver properties */ static Atom prop_draglock; +static Atom prop_horiz_scroll; /* general properties */ static Atom prop_float; @@ -2166,6 +2182,33 @@ LibinputSetPropertyDragLockButtons(DeviceIntPtr dev, val->size, checkonly); } +static inline int +LibinputSetPropertyHorizScroll(DeviceIntPtr dev, + Atom atom, + XIPropertyValuePtr val, + BOOL checkonly) +{ + InputInfoPtr pInfo = dev->public.devicePrivate; + struct xf86libinput *driver_data = pInfo->private; + BOOL enabled; + + if (val->format != 8 || val->type != XA_INTEGER || val->size != 1) + return BadMatch; + + enabled = *(BOOL*)val->data; + if (checkonly) { + if (enabled != 0 && enabled != 1) + return BadValue; + + if (!xf86libinput_check_device (dev, atom)) + return BadMatch; + } else { + driver_data->options.horiz_scrolling_enabled = enabled; + } + + return Success; + } + static int LibinputSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val, BOOL checkonly) @@ -2205,6 +2248,8 @@ LibinputSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val, rc = LibinputSetPropertyDisableWhileTyping(dev, atom, val, checkonly); else if (atom == prop_draglock) rc = LibinputSetPropertyDragLockButtons(dev, atom, val, checkonly); + else if (atom == prop_horiz_scroll) + rc = LibinputSetPropertyHorizScroll(dev, atom, val, checkonly); else if (atom == prop_device || atom == prop_product_id || atom == prop_tap_default || atom == prop_tap_drag_lock_default || @@ -2704,6 +2749,18 @@ LibinputInitDragLockProperty(DeviceIntPtr dev, } static void +LibinputInitHorizScrollProperty(DeviceIntPtr dev, + struct xf86libinput *driver_data) +{ + BOOL enabled = driver_data->options.horiz_scrolling_enabled; + + prop_horiz_scroll = LibinputMakeProperty(dev, + LIBINPUT_PROP_HORIZ_SCROLL_ENABLED, + XA_INTEGER, 8, + 1, &enabled); +} + +static void LibinputInitProperty(DeviceIntPtr dev) { InputInfoPtr pInfo = dev->public.devicePrivate; @@ -2754,4 +2811,5 @@ LibinputInitProperty(DeviceIntPtr dev) XISetDevicePropertyDeletable(dev, prop_product_id, FALSE); LibinputInitDragLockProperty(dev, driver_data); + LibinputInitHorizScrollProperty(dev, driver_data); } commit e3a888c3ab0f4cc42943b0216852cba110c3dad2 Author: Peter Hutterer <[email protected]> Date: Fri Aug 7 15:19:12 2015 +1000 Add drag lock support First, why is this here and not in libinput: drag lock should be implemented in the compositor (not in libinput) so it can provide feedback when it activates and grouped in with other accessibility features. That will work for Wayland but in X the compositor cannot filter button events - only the server and the drivers can. This patch adds mostly the same functionality that evdev provides with two options on how it works: * a single button number configures the given button to lock the next button pressed in a logically down state until a press+ release of that same button again * a set of button number pairs configures each button with the to-be-locked logical button, i.e. a pair of "1 3" will hold 3 logically down after a button 1 press The property and the xorg.conf options take the same configuration as the evdev driver (though the property has a different prefix, libinput instead of Evdev). The behavior difference to evdev is in how releases are handled, evdev sends the release on the second button press event, this implementation sends the release on the second release event. https://bugs.freedesktop.org/show_bug.cgi?id=85577 Signed-off-by: Peter Hutterer <[email protected]> Reviewed-by: Hans de Goede <[email protected]> diff --git a/Makefile.am b/Makefile.am index 99e6808..ef17c35 100644 --- a/Makefile.am +++ b/Makefile.am @@ -21,7 +21,7 @@ DISTCHECK_CONFIGURE_FLAGS = --with-sdkdir='$${includedir}/xorg' -SUBDIRS = src include man +SUBDIRS = src include man test MAINTAINERCLEANFILES = ChangeLog INSTALL pkgconfigdir = $(libdir)/pkgconfig diff --git a/configure.ac b/configure.ac index c149a1b..26e0e70 100644 --- a/configure.ac +++ b/configure.ac @@ -71,5 +71,6 @@ AC_CONFIG_FILES([Makefile include/Makefile src/Makefile man/Makefile + test/Makefile xorg-libinput.pc]) AC_OUTPUT diff --git a/include/libinput-properties.h b/include/libinput-properties.h index f54cee7..ed009d5 100644 --- a/include/libinput-properties.h +++ b/include/libinput-properties.h @@ -114,4 +114,10 @@ /* Disable while typing: BOOL, 1 value, read-only */ #define LIBINPUT_PROP_DISABLE_WHILE_TYPING_DEFAULT "libinput Disable While Typing Enabled Default" +/* Drag lock buttons, either: + CARD8, one value, the meta lock button, or + CARD8, n * 2 values, the drag lock pairs with n being the button and n+1 + the target button number */ +#define LIBINPUT_PROP_DRAG_LOCK_BUTTONS "libinput Drag Lock Buttons" + #endif /* _LIBINPUT_PROPERTIES_H_ */ diff --git a/man/libinput.man b/man/libinput.man index ac546e6..ff7a411 100644 --- a/man/libinput.man +++ b/man/libinput.man @@ -123,6 +123,27 @@ continues. .BI "Option \*qDisableWhileTyping\*q \*q" bool \*q Indicates if the touchpad should be disabled while typing on the keyboard (this does not apply to modifier keys such as Ctrl or Alt). +.TP 7 +.BI "Option \*qDragLockButtons\*q \*q" "L1 B1 L2 B2 ..." \*q +Sets "drag lock buttons" that simulate a button logically down even when it has +been physically released. To logically release a locked button, a second click +of the same button is required. +.IP +If the option is a single button number, that button acts as the +"meta" locking button for the next button number. See section +.B BUTTON DRAG LOCK +for details. +.IP +If the option is a list of button number pairs, the first number of each +number pair is the lock button, the second number the logical button number +to be locked. See section +.B BUTTON DRAG LOCK +for details. +.IP +For both meta and button pair configuration, the button numbers are +device button numbers, i.e. the +.B ButtonMapping +applies after drag lock. .PP For all options, the options are only parsed if the device supports that configuration option. For all options, the default value is the one used by @@ -195,11 +216,16 @@ disabled. .BI "libinput Disable While Typing Enabled" 1 boolean value (8 bit, 0 or 1). Indicates if disable while typing is enabled or disabled. -.TP7 .PP The above properties have a .BI "libinput <property name> Default" equivalent that indicates the default value for this setting on this device. +.TP 7 +.BI "libinput Drag Lock Buttons" +Either one 8-bit value specifying the meta drag lock button, or a list of +button pairs. See section +.B BUTTON DRAG LOCK +for details. .SH BUTTON MAPPING X clients receive events with logical button numbers, where 1, 2, 3 @@ -226,6 +252,24 @@ __xservername__ input driver does not use the button mapping after setup. Use XSetPointerMapping(__libmansuffix__) to modify the button mapping at runtime. +.SH BUTTON DRAG LOCK +Button drag lock holds a button logically down even when the button itself +has been physically released since. Button drag lock comes in two modes. +.PP +If in "meta" mode, a meta button click activates drag lock for the next +button press of any other button. A button click in the future will keep +that button held logically down until a subsequent click of that same +button. The meta button events themselves are discarded. A separate meta +button click is required each time a drag lock should be activated for a +button in the future. +.PP +If in "pairs" mode, each button can be assigned a target locking button. +On button click, the target lock button is held logically down until the +next click of the same button. The button events themselves are discarded +and only the target button events are sent. +.TP +This feature is provided by this driver, not by libinput. + .SH AUTHORS Peter Hutterer .SH "SEE ALSO" diff --git a/src/Makefile.am b/src/Makefile.am index 6085a9a..60703e6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -30,8 +30,10 @@ AM_CPPFLAGS =-I$(top_srcdir)/include $(LIBINPUT_CFLAGS) @DRIVER_NAME@_drv_la_LTLIBRARIES = @DRIVER_NAME@_drv.la @DRIVER_NAME@_drv_la_LDFLAGS = -module -avoid-version -@DRIVER_NAME@_drv_la_LIBADD = $(LIBINPUT_LIBS) +@DRIVER_NAME@_drv_la_LIBADD = $(LIBINPUT_LIBS) libdraglock.la @DRIVER_NAME@_drv_ladir = @inputdir@ @DRIVER_NAME@_drv_la_SOURCES = xf86libinput.c +noinst_LTLIBRARIES = libdraglock.la +libdraglock_la_SOURCES = draglock.c draglock.h diff --git a/src/draglock.c b/src/draglock.c new file mode 100644 index 0000000..b0bcac3 --- /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 sz) +{ + 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, sz * sizeof(array[0])); + for (i = 0; i < sz && 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 sz) +{ + unsigned int i; + + if (sz == 0 || array[0] != 0) + return 1; + + for (i = 0; i < sz; i++) { + if (array[i] < 0 || array[i] >= DRAGLOCK_MAX_BUTTONS) + return 1; + } + + dl->mode = DRAGLOCK_DISABLED; + for (i = 0; i < sz; 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; +} diff --git a/src/draglock.h b/src/draglock.h new file mode 100644 index 0000000..acc1314 --- /dev/null +++ b/src/draglock.h @@ -0,0 +1,159 @@ +/* + * 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. + */ +

