summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 18:38:11 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 18:38:11 +0000
commit265809de0590083e8edfee5aefae64c402b3f540 (patch)
treebbc5a8b83a18fa32afd23c408ff3eabc8ad52edb /src
parentInitial commit. (diff)
downloadxserver-xorg-input-libinput-upstream.tar.xz
xserver-xorg-input-libinput-upstream.zip
Adding upstream version 1.2.1.upstream/1.2.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/Makefile.am40
-rw-r--r--src/Makefile.in753
-rw-r--r--src/bezier.c179
-rw-r--r--src/bezier.h71
-rw-r--r--src/draglock.c284
-rw-r--r--src/draglock.h161
-rw-r--r--src/xf86libinput.c5947
7 files changed, 7435 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..5dcb55e
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,40 @@
+# Copyright 2005 Adam Jackson.
+#
+# 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
+# on the rights to use, copy, modify, merge, publish, distribute, sub
+# license, 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+# ADAM JACKSON 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.
+
+
+# this is obnoxious:
+# -module lets us name the module exactly how we want
+# -avoid-version prevents gratuitous .0.0.0 version numbers on the end
+# _ladir passes a dummy rpath to libtool so the thing will actually link
+# TODO: -nostdlib/-Bstatic/-lgcc platform magic, not installing the .a, etc.
+
+AM_CFLAGS = $(XORG_CFLAGS) $(CWARNFLAGS)
+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) libdraglock.la libbezier.la -lm
+@DRIVER_NAME@_drv_ladir = @inputdir@
+
+@DRIVER_NAME@_drv_la_SOURCES = xf86libinput.c
+
+noinst_LTLIBRARIES = libdraglock.la libbezier.la
+libdraglock_la_SOURCES = draglock.c draglock.h
+libbezier_la_SOURCES = bezier.c bezier.h
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..8515441
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,753 @@
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Copyright 2005 Adam Jackson.
+#
+# 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
+# on the rights to use, copy, modify, merge, publish, distribute, sub
+# license, 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+# ADAM JACKSON 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.
+
+# this is obnoxious:
+# -module lets us name the module exactly how we want
+# -avoid-version prevents gratuitous .0.0.0 version numbers on the end
+# _ladir passes a dummy rpath to libtool so the thing will actually link
+# TODO: -nostdlib/-Bstatic/-lgcc platform magic, not installing the .a, etc.
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(@DRIVER_NAME@_drv_ladir)"
+LTLIBRARIES = $(@DRIVER_NAME@_drv_la_LTLIBRARIES) \
+ $(noinst_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+@DRIVER_NAME@_drv_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
+ libdraglock.la libbezier.la
+am_@DRIVER_NAME@_drv_la_OBJECTS = xf86libinput.lo
+@DRIVER_NAME@_drv_la_OBJECTS = $(am_@DRIVER_NAME@_drv_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+@DRIVER_NAME@_drv_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(@DRIVER_NAME@_drv_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libbezier_la_LIBADD =
+am_libbezier_la_OBJECTS = bezier.lo
+libbezier_la_OBJECTS = $(am_libbezier_la_OBJECTS)
+libdraglock_la_LIBADD =
+am_libdraglock_la_OBJECTS = draglock.lo
+libdraglock_la_OBJECTS = $(am_libdraglock_la_OBJECTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/bezier.Plo ./$(DEPDIR)/draglock.Plo \
+ ./$(DEPDIR)/xf86libinput.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(@DRIVER_NAME@_drv_la_SOURCES) $(libbezier_la_SOURCES) \
+ $(libdraglock_la_SOURCES)
+DIST_SOURCES = $(@DRIVER_NAME@_drv_la_SOURCES) $(libbezier_la_SOURCES) \
+ $(libdraglock_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ADMIN_MAN_DIR = @ADMIN_MAN_DIR@
+ADMIN_MAN_SUFFIX = @ADMIN_MAN_SUFFIX@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APP_MAN_DIR = @APP_MAN_DIR@
+APP_MAN_SUFFIX = @APP_MAN_SUFFIX@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BASE_CFLAGS = @BASE_CFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CHANGELOG_CMD = @CHANGELOG_CMD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CWARNFLAGS = @CWARNFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DRIVER_MAN_DIR = @DRIVER_MAN_DIR@
+DRIVER_MAN_SUFFIX = @DRIVER_MAN_SUFFIX@
+DRIVER_NAME = @DRIVER_NAME@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_MAN_DIR = @FILE_MAN_DIR@
+FILE_MAN_SUFFIX = @FILE_MAN_SUFFIX@
+GREP = @GREP@
+INPUTPROTO24_CFLAGS = @INPUTPROTO24_CFLAGS@
+INPUTPROTO24_LIBS = @INPUTPROTO24_LIBS@
+INSTALL = @INSTALL@
+INSTALL_CMD = @INSTALL_CMD@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBINPUT_CFLAGS = @LIBINPUT_CFLAGS@
+LIBINPUT_LIBS = @LIBINPUT_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIB_MAN_DIR = @LIB_MAN_DIR@
+LIB_MAN_SUFFIX = @LIB_MAN_SUFFIX@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MAN_SUBSTS = @MAN_SUBSTS@
+MISC_MAN_DIR = @MISC_MAN_DIR@
+MISC_MAN_SUFFIX = @MISC_MAN_SUFFIX@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRICT_CFLAGS = @STRICT_CFLAGS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+XORG_CFLAGS = @XORG_CFLAGS@
+XORG_LIBS = @XORG_LIBS@
+XORG_MAN_PAGE = @XORG_MAN_PAGE@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+inputdir = @inputdir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sdkdir = @sdkdir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+xorgconfdir = @xorgconfdir@
+AM_CFLAGS = $(XORG_CFLAGS) $(CWARNFLAGS)
+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) libdraglock.la libbezier.la -lm
+@DRIVER_NAME@_drv_ladir = @inputdir@
+@DRIVER_NAME@_drv_la_SOURCES = xf86libinput.c
+noinst_LTLIBRARIES = libdraglock.la libbezier.la
+libdraglock_la_SOURCES = draglock.c draglock.h
+libbezier_la_SOURCES = bezier.c bezier.h
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-@DRIVER_NAME@_drv_laLTLIBRARIES: $(@DRIVER_NAME@_drv_la_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(@DRIVER_NAME@_drv_la_LTLIBRARIES)'; test -n "$(@DRIVER_NAME@_drv_ladir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(@DRIVER_NAME@_drv_ladir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(@DRIVER_NAME@_drv_ladir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(@DRIVER_NAME@_drv_ladir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(@DRIVER_NAME@_drv_ladir)"; \
+ }
+
+uninstall-@DRIVER_NAME@_drv_laLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(@DRIVER_NAME@_drv_la_LTLIBRARIES)'; test -n "$(@DRIVER_NAME@_drv_ladir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(@DRIVER_NAME@_drv_ladir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(@DRIVER_NAME@_drv_ladir)/$$f"; \
+ done
+
+clean-@DRIVER_NAME@_drv_laLTLIBRARIES:
+ -test -z "$(@DRIVER_NAME@_drv_la_LTLIBRARIES)" || rm -f $(@DRIVER_NAME@_drv_la_LTLIBRARIES)
+ @list='$(@DRIVER_NAME@_drv_la_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+@DRIVER_NAME@_drv.la: $(@DRIVER_NAME@_drv_la_OBJECTS) $(@DRIVER_NAME@_drv_la_DEPENDENCIES) $(EXTRA_@DRIVER_NAME@_drv_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(@DRIVER_NAME@_drv_la_LINK) -rpath $(@DRIVER_NAME@_drv_ladir) $(@DRIVER_NAME@_drv_la_OBJECTS) $(@DRIVER_NAME@_drv_la_LIBADD) $(LIBS)
+
+libbezier.la: $(libbezier_la_OBJECTS) $(libbezier_la_DEPENDENCIES) $(EXTRA_libbezier_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libbezier_la_OBJECTS) $(libbezier_la_LIBADD) $(LIBS)
+
+libdraglock.la: $(libdraglock_la_OBJECTS) $(libdraglock_la_DEPENDENCIES) $(EXTRA_libdraglock_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libdraglock_la_OBJECTS) $(libdraglock_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bezier.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/draglock.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xf86libinput.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+ for dir in "$(DESTDIR)$(@DRIVER_NAME@_drv_ladir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-@DRIVER_NAME@_drv_laLTLIBRARIES clean-generic \
+ clean-libtool clean-noinstLTLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/bezier.Plo
+ -rm -f ./$(DEPDIR)/draglock.Plo
+ -rm -f ./$(DEPDIR)/xf86libinput.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-@DRIVER_NAME@_drv_laLTLIBRARIES
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/bezier.Plo
+ -rm -f ./$(DEPDIR)/draglock.Plo
+ -rm -f ./$(DEPDIR)/xf86libinput.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-@DRIVER_NAME@_drv_laLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-@DRIVER_NAME@_drv_laLTLIBRARIES clean-generic \
+ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install \
+ install-@DRIVER_NAME@_drv_laLTLIBRARIES install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall \
+ uninstall-@DRIVER_NAME@_drv_laLTLIBRARIES uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/bezier.c b/src/bezier.c
new file mode 100644
index 0000000..9bb123e
--- /dev/null
+++ b/src/bezier.c
@@ -0,0 +1,179 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright © 2016 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+
+#include "bezier.h"
+
+const struct bezier_control_point bezier_defaults[4] = {
+ { 0.0, 0.0 },
+ { 0.0, 0.0 },
+ { 1.0, 1.0 },
+ { 1.0, 1.0 },
+};
+
+struct point {
+ int x, y;
+};
+
+/**
+ * de Casteljau's algorithm. See this page here
+ * https://pomax.github.io/bezierinfo/#extended
+ *
+ * To play with bezier curve shapes, I used
+ * http://cubic-bezier.com/
+ */
+static struct point
+decasteljau(const struct point *controls,
+ size_t ncontrols,
+ double t)
+{
+ struct point new_controls[ncontrols];
+
+ if (ncontrols == 1)
+ return controls[0];
+
+ for (int i = 0; i < ncontrols - 1; i++) {
+ new_controls[i].x = (1.0 - t) * controls[i].x + t * controls[i + 1].x;
+ new_controls[i].y = (1.0 - t) * controls[i].y + t * controls[i + 1].y;
+ }
+
+ return decasteljau(new_controls, ncontrols - 1, t);
+}
+
+/**
+ * Given a Bézier curve defined by the control points, reduce the curve to
+ * one with ncurve_points.
+ */
+static void
+flatten_curve(const struct point *controls,
+ size_t ncontrols,
+ struct point *curve,
+ size_t ncurve_points)
+{
+ ncurve_points--; /* make sure we end up with 100/100 as last point */
+
+ for (int i = 0; i <= ncurve_points; i++) {
+ double t = 1.0 * i/ncurve_points;
+ struct point p;
+
+ p = decasteljau(controls, ncontrols, t);
+ curve[i] = p;
+ }
+}
+
+/**
+ * Calculate line through a and b, set curve[x] for each x between
+ * [a.x, b.x].
+ *
+ * Note: pcurve must be at least b.x size.
+ */
+static void
+line_between(struct point a, struct point b,
+ struct point *curve, size_t curve_sz)
+{
+ double slope;
+ double offset;
+
+ assert(b.x < curve_sz);
+
+ if (a.x == b.x) {
+ curve[a.x].x = a.x;
+ curve[a.x].y = a.y;
+ return;
+ }
+
+ slope = (double)(b.y - a.y)/(b.x - a.x);
+ offset = a.y - slope * a.x;
+
+ for (int x = a.x; x <= b.x; x++) {
+ struct point p;
+ p.x = x;
+ p.y = slope * x + offset;
+ curve[x] = p;
+ }
+}
+
+bool
+cubic_bezier(const struct bezier_control_point controls[4],
+ int *bezier_out,
+ size_t bezier_sz)
+{
+ const int nsegments = 50;
+ const int range = bezier_sz - 1;
+ struct point curve[nsegments];
+ struct point bezier[bezier_sz];
+ struct point zero = { 0, 0 },
+ max = { range, range};
+
+ /* Scale control points into the [0, bezier_sz) range */
+ struct point ctrls[4];
+
+ for (int i = 0; i < 4; i++) {
+ if (controls[i].x < 0.0 || controls[i].x > 1.0 ||
+ controls[i].y < 0.0 || controls[i].y > 1.0)
+ return false;
+
+ ctrls[i].x = controls[i].x * range;
+ ctrls[i].y = controls[i].y * range;
+ }
+
+ for (int i = 0; i < 3; i++) {
+ if (ctrls[i].x > ctrls[i+1].x)
+ return false;
+ }
+
+ /* Reduce curve to nsegments, because this isn't a drawing program */
+ flatten_curve(ctrls, 4, curve, nsegments);
+
+ /* we now have nsegments points in curve that represent the bezier
+ curve (already in the [0, bezier_sz) range). Run through the
+ points and draw a straight line between each point and voila, we
+ have our curve.
+
+ If the first control points (x0/y0) is not at x == 0 or the last
+ control point (x3/y3) is not at the max value, draw a line
+ between from 0/0 to x0/y0 and from x3/y3 to xmax/y3.
+ */
+
+ line_between(zero, curve[0], bezier, bezier_sz);
+
+ for (int i = 0; i < nsegments - 1; i++)
+ line_between(curve[i], curve[i+1], bezier, bezier_sz);
+
+ if (curve[nsegments - 1].x < max.x)
+ line_between(curve[nsegments - 1], max, bezier, bezier_sz);
+
+ for (int i = 0; i < bezier_sz; i++)
+ bezier_out[i] = bezier[i].y;
+
+ return true;
+}
diff --git a/src/bezier.h b/src/bezier.h
new file mode 100644
index 0000000..f86588b
--- /dev/null
+++ b/src/bezier.h
@@ -0,0 +1,71 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright © 2016 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef BEZIER_H
+#define BEZIER_H
+
+#include <stdlib.h>
+#include <stdbool.h>
+
+struct bezier_control_point {
+ double x, y;
+};
+
+extern const struct bezier_control_point bezier_defaults[4];
+
+/**
+ * Given four control points in the range [(0.0/0.0), (1.0/1.0)]
+ * construct a Bézier curve.
+ *
+ * ^
+ *1.0 | c2 ______ c3
+ * | _/
+ * | /
+ * |c1 /
+ * | /
+ * | /
+ * |/_________________>
+ * c0 1.0
+ *
+ * This function requires that c[i].x <= c[i+1].x
+ *
+ * The curve is mapped into a canvas size [0, bezier_sz)². For each x
+ * coordinate in [0, bezier_sz), the matching y coordinate is thus
+ * bezier[x].
+ *
+ * In other words, if you have a range [0,2048) input possible values,
+ * the output is a list of 2048 points in a [0, 2048) range.
+ *
+ * @return true on success, false otherwise
+ */
+bool
+cubic_bezier(const struct bezier_control_point controls[4],
+ int *bezier,
+ size_t bezier_sz);
+#endif
diff --git a/src/draglock.c b/src/draglock.c
new file mode 100644
index 0000000..1ab4678
--- /dev/null
+++ b/src/draglock.c
@@ -0,0 +1,284 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright © 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.
+ */
+
+#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;
+}
diff --git a/src/draglock.h b/src/draglock.h
new file mode 100644
index 0000000..90a71b9
--- /dev/null
+++ b/src/draglock.h
@@ -0,0 +1,161 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright © 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef DRAGLOCK_H
+#define DRAGLOCK_H 1
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+/* 32 buttons are enough for everybody™
+ * Note that this is the limit of physical buttons as well as the highest
+ * allowed target button.
+ */
+#define DRAGLOCK_MAX_BUTTONS 32
+
+enum draglock_mode
+{
+ DRAGLOCK_DISABLED,
+ DRAGLOCK_META,
+ DRAGLOCK_PAIRS
+};
+
+enum draglock_button_state
+{
+ DRAGLOCK_BUTTON_STATE_NONE,
+ DRAGLOCK_BUTTON_STATE_DOWN_1,
+ DRAGLOCK_BUTTON_STATE_UP_1,
+ DRAGLOCK_BUTTON_STATE_DOWN_2,
+};
+
+struct draglock
+{
+ enum draglock_mode mode;
+ int meta_button; /* meta key to lock any button */
+ bool meta_state; /* meta_button state */
+ unsigned int lock_pair[DRAGLOCK_MAX_BUTTONS + 1];/* specify a meta/lock pair */
+ enum draglock_button_state lock_state[DRAGLOCK_MAX_BUTTONS + 1]; /* state of any locked buttons */
+};
+
+/**
+ * Initialize the draglock struct based on the config string. The string is
+ * either a single number to configure DRAGLOCK_META mode or a list of
+ * number pairs, with pair[0] as button and pair[1] as target lock number to
+ * configure DRAGLOCK_PAIRS mode.
+ *
+ * If config is NULL, the empty string, "0" or an even-numbered list of 0,
+ * the drag lock mode is DRAGLOCK_DISABLED.
+ *
+ * @return 0 on success or nonzero on error
+ */
+int
+draglock_init_from_string(struct draglock *dl, const char *config);
+
+/**
+ * Get the current drag lock mode.
+ *
+ * If the mode is DRAGLOCK_META, a meta button click will cause the next
+ * subsequent button click to be held logically down until the release of
+ * the second button click of that same button. Events from the meta button
+ * are always discarded.
+ *
+ * If the mode is DRAGLOCK_PAIRS, any button may be configured with a
+ * 'target' button number. A click of that button causes the target button
+ * to be held logically down until the release of the second button click.
+ */
+enum draglock_mode
+draglock_get_mode(const struct draglock *dl);
+
+/**
+ * @return the meta button number or 0 if the current mode is not
+ * DRAGLOCK_META.
+ */
+int
+draglock_get_meta(const struct draglock *dl);
+
+/**
+ * Get the drag lock button mapping pairs. The array is filled with the
+ * button number as index and the mapped target button number as value, i.e.
+ * array[3] == 8 means button 3 will draglock button 8.
+ *
+ * A value of 0 indicates draglock is disabled for that button.
+ *
+ * @note Button numbers start at 1, array[0] is always 0.
+ *
+ * @param[in|out] array Caller-allocated array to hold the button mappings.
+ * @param[in] nelem Maximum number of elements in array
+ *
+ * @return The number of valid elements in array or 0 if the current mode is
+ * not DRAGLOCK_PAIRS
+ */
+size_t
+draglock_get_pairs(const struct draglock *dl, int *array, size_t nelem);
+
+/**
+ * Set the drag lock config to the DRAGLOCK_META mode, with the given
+ * button as meta button.
+ *
+ * If the button is 0 the mode becomes DRAGLOCK_DISABLED.
+ *
+ * @return 0 on success, nonzero otherwise
+ */
+int
+draglock_set_meta(struct draglock *dl, int meta_button);
+
+/**
+ * Set the drag lock config to the DRAGLOCK_PAIRS mode. The array
+ * must be filled with the button number as index and the mapped target
+ * button number as value, i.e.
+ * array[3] == 8 means button 3 will draglock button 8.
+ *
+ * A value of 0 indicates draglock is disabled for that button. If all
+ * buttons are 0, the mode becomes DRAGLOCK_DISABLED.
+ *
+ * @note Button numbers start at 1, array[0] is always 0.
+ *
+ * @return 0 on successor nonzero otherwise
+ */
+int
+draglock_set_pairs(struct draglock *dl, const int *array, size_t nelem);
+
+/**
+ * Process the given button event through the drag lock state machine.
+ * If the event is to be discarded by the caller, button is set to 0.
+ * Otherwise, button is set to the button event to process and is_press is
+ * set to the button state to process.
+ *
+ * @param[in|out] button The button number to process
+ * @param[in|out] is_press nonzero for press, zero for release
+ *
+ * @return 0 on success or 1 on error
+ */
+int
+draglock_filter_button(struct draglock *dl, int *button, int *is_press);
+
+#endif /* DRAGLOCK_H */
diff --git a/src/xf86libinput.c b/src/xf86libinput.c
new file mode 100644
index 0000000..b9cd086
--- /dev/null
+++ b/src/xf86libinput.c
@@ -0,0 +1,5947 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright © 2013-2017 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
+#include <unistd.h>
+#include <xorg-server.h>
+#include <list.h>
+#include <exevents.h>
+#include <xkbsrv.h>
+#include <xf86.h>
+#include <xf86Xinput.h>
+#include <xf86_OSproc.h>
+#include <xserver-properties.h>
+#include <libinput.h>
+#include <linux/input.h>
+
+#include <X11/Xatom.h>
+
+#include "bezier.h"
+#include "draglock.h"
+#include "libinput-properties.h"
+
+#define TOUCHPAD_NUM_AXES 4 /* x, y, hscroll, vscroll */
+#define TABLET_NUM_BUTTONS 7 /* we need scroll buttons */
+#define TOUCH_MAX_SLOTS 15
+#define XORG_KEYCODE_OFFSET 8
+#define SCROLL_INCREMENT 15
+#define TOUCHPAD_SCROLL_DIST_MIN 10 /* in libinput pixels */
+#define TOUCHPAD_SCROLL_DIST_MAX 50 /* in libinput pixels */
+
+#define streq(a, b) (strcmp(a, b) == 0)
+#define strneq(a, b, n) (strncmp(a, b, n) == 0)
+
+/*
+ libinput does not provide axis information for absolute devices, instead
+ it scales into the screen dimensions provided. So we set up the axes with
+ a fixed range, let libinput scale into that range and then the server
+ do the scaling it usually does.
+ */
+#define TOUCH_AXIS_MAX 0xffff
+#define TABLET_AXIS_MAX 0xffffff
+#define TABLET_PRESSURE_AXIS_MAX 2047
+#define TABLET_TILT_AXIS_MAX 64
+#define TABLET_STRIP_AXIS_MAX 4096
+#define TABLET_RING_AXIS_MAX 71
+
+#define CAP_KEYBOARD 0x1
+#define CAP_POINTER 0x2
+#define CAP_TOUCH 0x4
+#define CAP_TABLET 0x8
+#define CAP_TABLET_TOOL 0x10
+#define CAP_TABLET_PAD 0x20
+#define CAP_GESTURE 0x40
+
+#if HAVE_INPUTPROTO24
+#if ABI_XINPUT_VERSION >= SET_ABI_VERSION(24, 4)
+#define HAVE_GESTURES
+#endif
+#endif
+
+struct xf86libinput_driver {
+ struct libinput *libinput;
+ int device_enabled_count;
+ void *registered_InputInfoPtr;
+};
+
+static struct xf86libinput_driver driver_context;
+
+struct xf86libinput_device {
+ int refcount;
+ int enabled_count;
+ uint32_t id;
+ struct libinput_device *device;
+ struct xorg_list device_list;
+ int server_fd;
+
+ struct xorg_list unclaimed_tablet_tool_list;
+};
+
+struct xf86libinput_tablet_tool_queued_event {
+ struct xorg_list node;
+ struct libinput_event_tablet_tool *event;
+};
+
+struct xf86libinput_tablet_tool_event_queue {
+ bool need_to_queue;
+ struct xorg_list event_list;
+};
+
+struct xf86libinput_tablet_tool {
+ struct xorg_list node;
+ struct libinput_tablet_tool *tool;
+};
+
+struct xf86libinput {
+ InputInfoPtr pInfo;
+ char *path;
+ uint32_t capabilities;
+
+ struct {
+ struct scroll_axis {
+ int dist;
+ double fraction;
+ } v, h;
+ } scroll;
+
+ struct {
+ double x;
+ double y;
+ double x_remainder;
+ double y_remainder;
+ } scale;
+
+ BOOL has_abs;
+
+ ValuatorMask *valuators;
+ ValuatorMask *valuators_unaccelerated;
+
+ struct options {
+ BOOL tapping;
+ BOOL tap_drag;
+ BOOL tap_drag_lock;
+ enum libinput_config_tap_button_map tap_button_map;
+ BOOL natural_scrolling;
+ BOOL left_handed;
+ BOOL middle_emulation;
+ BOOL disable_while_typing;
+ CARD32 sendevents;
+ CARD32 scroll_button; /* xorg button number */
+ BOOL scroll_buttonlock;
+ uint32_t scroll_pixel_distance;
+ float speed;
+ float matrix[9];
+ enum libinput_config_scroll_method scroll_method;
+ enum libinput_config_click_method click_method;
+ enum libinput_config_accel_profile accel_profile;
+
+ unsigned char btnmap[MAX_BUTTONS + 1];
+
+ BOOL horiz_scrolling_enabled;
+ BOOL hires_scrolling_enabled;
+
+ float rotation_angle;
+ struct bezier_control_point pressurecurve[4];
+ struct ratio {
+ int x, y;
+ } area;
+ } options;
+
+ struct draglock draglock;
+
+ struct xf86libinput_device *shared_device;
+ struct xorg_list shared_device_link;
+
+ struct libinput_tablet_tool *tablet_tool;
+
+ bool allow_mode_group_updates;
+
+ /* Pre-calculated pressure curve.
+ In the 0...TABLET_AXIS_MAX range */
+ struct {
+ int *values;
+ size_t sz;
+ } pressurecurve;
+
+ struct scale_factor {
+ double x, y;
+ } area_scale_factor;
+};
+
+enum event_handling {
+ EVENT_QUEUED,
+ EVENT_HANDLED,
+};
+
+static void
+xf86libinput_create_subdevice(InputInfoPtr pInfo,
+ uint32_t capabilities,
+ XF86OptionPtr extra_opts);
+static inline void
+update_mode_prop(InputInfoPtr pInfo,
+ struct libinput_event_tablet_pad *event);
+
+static enum event_handling
+xf86libinput_handle_event(struct libinput_event *event);
+
+static void
+xf86libinput_post_tablet_motion(InputInfoPtr pInfo,
+ struct libinput_event_tablet_tool *event);
+
+static inline int
+use_server_fd(const InputInfoPtr pInfo) {
+ return pInfo->fd > -1 && (pInfo->flags & XI86_SERVER_FD);
+}
+
+static inline unsigned int
+btn_linux2xorg(unsigned int b)
+{
+ unsigned int button;
+
+ switch(b) {
+ case 0: button = 0; break;
+ case BTN_LEFT: button = 1; break;
+ case BTN_MIDDLE: button = 2; break;
+ case BTN_RIGHT: button = 3; break;
+ /* tablet button range */
+ case BTN_STYLUS: button = 2; break;
+ case BTN_STYLUS2: button = 3; break;
+ default:
+ button = 8 + b - BTN_SIDE;
+ break;
+ }
+
+ return button;
+}
+static inline unsigned int
+btn_xorg2linux(unsigned int b)
+{
+ unsigned int button;
+
+ switch(b) {
+ case 0: button = 0; break;
+ case 1: button = BTN_LEFT; break;
+ case 2: button = BTN_MIDDLE; break;
+ case 3: button = BTN_RIGHT; break;
+ default:
+ button = b - 8 + BTN_SIDE;
+ break;
+ }
+
+ return button;
+}
+
+static BOOL
+xf86libinput_is_subdevice(InputInfoPtr pInfo)
+{
+ char *source;
+ BOOL is_subdevice;
+
+ source = xf86CheckStrOption(pInfo->options, "_source", "");
+ is_subdevice = streq(source, "_driver/libinput");
+ free(source);
+
+ return is_subdevice;
+}
+
+static inline InputInfoPtr
+xf86libinput_get_parent(InputInfoPtr pInfo)
+{
+ InputInfoPtr parent;
+ int parent_id;
+
+ parent_id = xf86CheckIntOption(pInfo->options, "_libinput/shared-device", -1);
+ if (parent_id == -1)
+ return NULL;
+
+ nt_list_for_each_entry(parent, xf86FirstLocalDevice(), next) {
+ int id = xf86CheckIntOption(parent->options,
+ "_libinput/shared-device",
+ -1);
+ if (id == parent_id && !xf86libinput_is_subdevice(parent))
+ return parent;
+ }
+
+ return NULL;
+}
+
+static inline struct xf86libinput_device*
+xf86libinput_shared_create(struct libinput_device *device)
+{
+ static uint32_t next_shared_device_id;
+ struct xf86libinput_device *shared_device;
+
+ shared_device = calloc(1, sizeof(*shared_device));
+ if (!shared_device)
+ return NULL;
+
+ shared_device->device = device;
+ shared_device->refcount = 1;
+ shared_device->id = ++next_shared_device_id;
+ xorg_list_init(&shared_device->device_list);
+ xorg_list_init(&shared_device->unclaimed_tablet_tool_list);
+
+ return shared_device;
+}
+
+static inline struct xf86libinput_device*
+xf86libinput_shared_ref(struct xf86libinput_device *shared_device)
+{
+ shared_device->refcount++;
+
+ return shared_device;
+}
+
+static inline struct xf86libinput_device*
+xf86libinput_shared_unref(struct xf86libinput_device *shared_device)
+{
+ shared_device->refcount--;
+
+ if (shared_device->refcount > 0)
+ return shared_device;
+
+ free(shared_device);
+
+ return NULL;
+}
+
+static inline struct libinput_device *
+xf86libinput_shared_enable(InputInfoPtr pInfo,
+ struct xf86libinput_device *shared_device,
+ const char *path)
+{
+ struct libinput_device *device;
+ struct libinput *libinput = driver_context.libinput;
+
+ /* With systemd-logind the server requests the fd from logind, sets
+ * pInfo->fd and sets the "fd" option to the fd number.
+ *
+ * If we have a second device that uses the same path, the server
+ * checks all pInfo->major/minor for a match and returns the matched
+ * device's pInfo->fd. In this driver, this fd is the epollfd, not
+ * the actual device. This causes troubles when removing the
+ * device.
+ *
+ * What we need to do here is: after enabling the device the first
+ * time extract the real fd and store it in the shared device
+ * struct. The second device replaces the pInfo->options "fd" with
+ * the real fd we're using.
+ *
+ * When the device is unplugged, the server now correctly finds two
+ * devices on the real fd and releases them in order.
+ */
+ shared_device->enabled_count++;
+ if (shared_device->enabled_count > 1) {
+ if (pInfo->flags & XI86_SERVER_FD) {
+ pInfo->options = xf86ReplaceIntOption(pInfo->options,
+ "fd",
+ shared_device->server_fd);
+ }
+
+ return shared_device->device;
+ }
+
+ device = libinput_path_add_device(libinput, path);
+ if (!device)
+ return NULL;
+
+ libinput_device_set_user_data(device, shared_device);
+ shared_device->device = libinput_device_ref(device);
+
+ if (pInfo->flags & XI86_SERVER_FD)
+ shared_device->server_fd = xf86CheckIntOption(pInfo->options,
+ "fd",
+ -1);
+ return device;
+}
+
+static inline void
+xf86libinput_shared_disable(struct xf86libinput_device *shared_device)
+{
+ struct libinput_device *device = shared_device->device;
+
+ shared_device->enabled_count--;
+
+ if (shared_device->enabled_count > 0)
+ return;
+
+ if (!device)
+ return;
+
+ libinput_device_set_user_data(device, NULL);
+ libinput_path_remove_device(device);
+ libinput_device_unref(device);
+ shared_device->device = NULL;
+}
+
+static inline bool
+xf86libinput_shared_is_enabled(struct xf86libinput_device *shared_device)
+{
+ return shared_device->enabled_count > 0;
+}
+
+static inline bool
+xf86libinput_set_pressurecurve(struct xf86libinput *driver_data,
+ const struct bezier_control_point controls[4])
+{
+ if (memcmp(controls, bezier_defaults, sizeof(bezier_defaults)) == 0) {
+ free(driver_data->pressurecurve.values);
+ driver_data->pressurecurve.values = NULL;
+ return true;
+ }
+
+ if (!driver_data->pressurecurve.values) {
+ int *vals = calloc(TABLET_PRESSURE_AXIS_MAX + 1, sizeof(int));
+ if (!vals)
+ return false;
+
+ driver_data->pressurecurve.values = vals;
+ driver_data->pressurecurve.sz = TABLET_PRESSURE_AXIS_MAX + 1;
+ }
+
+ return cubic_bezier(controls,
+ driver_data->pressurecurve.values,
+ driver_data->pressurecurve.sz);
+}
+
+static inline void
+xf86libinput_set_area_ratio(struct xf86libinput *driver_data,
+ const struct ratio *ratio)
+{
+ double f;
+ double w, h;
+
+ if (libinput_device_get_size(driver_data->shared_device->device, &w, &h) != 0)
+ return;
+
+ driver_data->options.area = *ratio;
+
+ if (ratio->y == 0) {
+ driver_data->area_scale_factor.x = 1.0;
+ driver_data->area_scale_factor.y = 1.0;
+ return;
+ }
+
+ f = 1.0 * (ratio->x * h)/(ratio->y * w);
+
+ if (f <= 1.0) {
+ driver_data->area_scale_factor.x = 1.0/f;
+ driver_data->area_scale_factor.y = 1.0;
+ } else {
+ driver_data->area_scale_factor.x = 1.0;
+ driver_data->area_scale_factor.y = f;
+ }
+}
+
+/**
+ * returns true if the device has one or more of the given capabilities or
+ * if the device isn't a subdevice
+ */
+static inline bool
+subdevice_has_capabilities(DeviceIntPtr dev, uint32_t capabilities)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+
+ if (!xf86libinput_is_subdevice(pInfo))
+ return true;
+
+ return !!(driver_data->capabilities & capabilities);
+}
+
+static int
+LibinputSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
+ BOOL checkonly);
+static void
+LibinputInitProperty(DeviceIntPtr dev);
+
+static void
+LibinputApplyConfigSendEvents(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+
+ if (libinput_device_config_send_events_get_modes(device) != LIBINPUT_CONFIG_SEND_EVENTS_ENABLED &&
+ libinput_device_config_send_events_set_mode(device,
+ driver_data->options.sendevents) != LIBINPUT_CONFIG_STATUS_SUCCESS)
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set SendEventsMode %u\n",
+ driver_data->options.sendevents);
+}
+
+static void
+LibinputApplyConfigNaturalScroll(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER))
+ return;
+
+ if (libinput_device_config_scroll_has_natural_scroll(device) &&
+ libinput_device_config_scroll_set_natural_scroll_enabled(device,
+ driver_data->options.natural_scrolling) != LIBINPUT_CONFIG_STATUS_SUCCESS)
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set NaturalScrolling to %d\n",
+ driver_data->options.natural_scrolling);
+}
+
+static void
+LibinputApplyConfigAccel(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER))
+ return;
+
+ if (libinput_device_config_accel_is_available(device) &&
+ libinput_device_config_accel_set_speed(device,
+ driver_data->options.speed) != LIBINPUT_CONFIG_STATUS_SUCCESS)
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set speed %.2f\n",
+ driver_data->options.speed);
+
+ if (libinput_device_config_accel_get_profiles(device) &&
+ driver_data->options.accel_profile != LIBINPUT_CONFIG_ACCEL_PROFILE_NONE &&
+ libinput_device_config_accel_set_profile(device,
+ driver_data->options.accel_profile) !=
+ LIBINPUT_CONFIG_STATUS_SUCCESS) {
+ const char *profile;
+
+ switch (driver_data->options.accel_profile) {
+ case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE:
+ profile = "adaptive";
+ break;
+ case LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT:
+ profile = "flat";
+ break;
+ default:
+ profile = "unknown";
+ break;
+ }
+ xf86IDrvMsg(pInfo, X_ERROR, "Failed to set profile %s\n", profile);
+ }
+}
+
+static inline void
+LibinputApplyConfigTap(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER))
+ return;
+
+ if (libinput_device_config_tap_get_finger_count(device) > 0 &&
+ libinput_device_config_tap_set_enabled(device,
+ driver_data->options.tapping) != LIBINPUT_CONFIG_STATUS_SUCCESS)
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set Tapping to %d\n",
+ driver_data->options.tapping);
+
+ if (libinput_device_config_tap_get_finger_count(device) > 0 &&
+ libinput_device_config_tap_set_button_map(device,
+ driver_data->options.tap_button_map) != LIBINPUT_CONFIG_STATUS_SUCCESS) {
+ const char *map;
+
+ switch(driver_data->options.tap_button_map) {
+ case LIBINPUT_CONFIG_TAP_MAP_LRM: map = "lrm"; break;
+ case LIBINPUT_CONFIG_TAP_MAP_LMR: map = "lmr"; break;
+ default: map = "unknown"; break;
+ }
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set Tapping ButtonMap to %s\n",
+ map);
+ }
+
+ if (libinput_device_config_tap_get_finger_count(device) > 0 &&
+ libinput_device_config_tap_set_drag_lock_enabled(device,
+ driver_data->options.tap_drag_lock) != LIBINPUT_CONFIG_STATUS_SUCCESS)
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set Tapping DragLock to %d\n",
+ driver_data->options.tap_drag_lock);
+
+ if (libinput_device_config_tap_get_finger_count(device) > 0 &&
+ libinput_device_config_tap_set_drag_enabled(device,
+ driver_data->options.tap_drag) != LIBINPUT_CONFIG_STATUS_SUCCESS)
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set Tapping Drag to %d\n",
+ driver_data->options.tap_drag);
+}
+
+static void
+LibinputApplyConfigCalibration(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+
+ if (!subdevice_has_capabilities(dev, CAP_TOUCH|CAP_TABLET))
+ return;
+
+ if (libinput_device_config_calibration_has_matrix(device) &&
+ libinput_device_config_calibration_set_matrix(device,
+ driver_data->options.matrix) != LIBINPUT_CONFIG_STATUS_SUCCESS)
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to apply matrix: "
+ "%.2f %.2f %.2f %2.f %.2f %.2f %.2f %.2f %.2f\n",
+ driver_data->options.matrix[0], driver_data->options.matrix[1],
+ driver_data->options.matrix[2], driver_data->options.matrix[3],
+ driver_data->options.matrix[4], driver_data->options.matrix[5],
+ driver_data->options.matrix[6], driver_data->options.matrix[7],
+ driver_data->options.matrix[8]);
+}
+
+static void
+LibinputApplyConfigLeftHanded(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER|CAP_TABLET))
+ return;
+
+ if (libinput_device_config_left_handed_is_available(device) &&
+ libinput_device_config_left_handed_set(device,
+ driver_data->options.left_handed) != LIBINPUT_CONFIG_STATUS_SUCCESS)
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set LeftHanded to %d\n",
+ driver_data->options.left_handed);
+}
+
+static void
+LibinputApplyConfigScrollMethod(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER))
+ return;
+
+ if (libinput_device_config_scroll_set_method(device,
+ driver_data->options.scroll_method) != LIBINPUT_CONFIG_STATUS_SUCCESS) {
+ const char *method;
+
+ switch(driver_data->options.scroll_method) {
+ case LIBINPUT_CONFIG_SCROLL_NO_SCROLL: method = "none"; break;
+ case LIBINPUT_CONFIG_SCROLL_2FG: method = "twofinger"; break;
+ case LIBINPUT_CONFIG_SCROLL_EDGE: method = "edge"; break;
+ case LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN: method = "button"; break;
+ default:
+ method = "unknown"; break;
+ }
+
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set scroll to %s\n",
+ method);
+ }
+
+ if (libinput_device_config_scroll_get_methods(device) & LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) {
+ unsigned int scroll_button;
+#if HAVE_LIBINPUT_SCROLL_BUTTON_LOCK
+ enum libinput_config_scroll_button_lock_state buttonlock;
+
+ buttonlock = driver_data->options.scroll_buttonlock ?
+ LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED :
+ LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED;
+
+ if (libinput_device_config_scroll_set_button_lock(device, buttonlock)
+ != LIBINPUT_CONFIG_STATUS_SUCCESS) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to %s ScrollButtonLock\n",
+ buttonlock ? "enable" : "disable");
+ }
+#endif
+
+ scroll_button = btn_xorg2linux(driver_data->options.scroll_button);
+ if (libinput_device_config_scroll_set_button(device, scroll_button) != LIBINPUT_CONFIG_STATUS_SUCCESS)
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set ScrollButton to %u\n",
+ driver_data->options.scroll_button);
+
+ }
+}
+
+static void
+LibinputApplyConfigClickMethod(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER))
+ return;
+
+ if (libinput_device_config_click_set_method(device,
+ driver_data->options.click_method) != LIBINPUT_CONFIG_STATUS_SUCCESS) {
+ const char *method;
+
+ switch (driver_data->options.click_method) {
+ case LIBINPUT_CONFIG_CLICK_METHOD_NONE: method = "none"; break;
+ case LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS: method = "buttonareas"; break;
+ case LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER: method = "clickfinger"; break;
+ default:
+ method = "unknown"; break;
+ }
+
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set click method to %s\n",
+ method);
+ }
+}
+
+static void
+LibinputApplyConfigMiddleEmulation(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER))
+ return;
+
+ if (libinput_device_config_middle_emulation_is_available(device) &&
+ libinput_device_config_middle_emulation_set_enabled(device,
+ driver_data->options.middle_emulation) != LIBINPUT_CONFIG_STATUS_SUCCESS)
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set MiddleEmulation to %d\n",
+ driver_data->options.middle_emulation);
+}
+
+static void
+LibinputApplyConfigDisableWhileTyping(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER))
+ return;
+
+ if (libinput_device_config_dwt_is_available(device) &&
+ libinput_device_config_dwt_set_enabled(device,
+ driver_data->options.disable_while_typing) != LIBINPUT_CONFIG_STATUS_SUCCESS)
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set DisableWhileTyping to %d\n",
+ driver_data->options.disable_while_typing);
+}
+
+static void
+LibinputApplyConfigRotation(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER))
+ return;
+
+ if (libinput_device_config_rotation_is_available(device) &&
+ libinput_device_config_rotation_set_angle(device, driver_data->options.rotation_angle) != LIBINPUT_CONFIG_STATUS_SUCCESS)
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set RotationAngle to %.2f\n",
+ driver_data->options.rotation_angle);
+}
+
+static inline void
+LibinputApplyConfig(DeviceIntPtr dev)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+
+ LibinputApplyConfigSendEvents(dev, driver_data, device);
+ LibinputApplyConfigNaturalScroll(dev, driver_data, device);
+ LibinputApplyConfigAccel(dev, driver_data, device);
+ LibinputApplyConfigTap(dev, driver_data, device);
+ LibinputApplyConfigCalibration(dev, driver_data, device);
+ LibinputApplyConfigLeftHanded(dev, driver_data, device);
+ LibinputApplyConfigScrollMethod(dev, driver_data, device);
+ LibinputApplyConfigClickMethod(dev, driver_data, device);
+ LibinputApplyConfigMiddleEmulation(dev, driver_data, device);
+ LibinputApplyConfigDisableWhileTyping(dev, driver_data, device);
+ LibinputApplyConfigRotation(dev, driver_data, device);
+}
+
+static int
+xf86libinput_on(DeviceIntPtr dev)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct xf86libinput_device *shared_device = driver_data->shared_device;
+ struct libinput *libinput = driver_context.libinput;
+ struct libinput_device *device;
+
+ device = xf86libinput_shared_enable(pInfo,
+ shared_device,
+ driver_data->path);
+ if (!device)
+ return !Success;
+
+ /* if we use server fds, overwrite the fd with the one from
+ libinput nonetheless, otherwise the server won't call ReadInput
+ for our device. This must be swapped back to the real fd in
+ DEVICE_OFF so systemd-logind closes the right fd */
+ pInfo->fd = libinput_get_fd(libinput);
+
+ if (driver_context.device_enabled_count == 0) {
+ xf86AddEnabledDevice(pInfo);
+ driver_context.registered_InputInfoPtr = pInfo;
+ }
+
+ driver_context.device_enabled_count++;
+ dev->public.on = TRUE;
+
+ LibinputApplyConfig(dev);
+
+ return Success;
+}
+
+static int
+xf86libinput_off(DeviceIntPtr dev)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct xf86libinput_device *shared_device = driver_data->shared_device;
+
+ if (--driver_context.device_enabled_count == 0) {
+ xf86RemoveEnabledDevice(pInfo);
+ }
+
+ if (use_server_fd(pInfo)) {
+ pInfo->fd = xf86SetIntOption(pInfo->options, "fd", -1);
+ } else {
+ pInfo->fd = -1;
+ }
+
+ dev->public.on = FALSE;
+
+ xf86libinput_shared_disable(shared_device);
+
+ return Success;
+}
+
+static void
+xf86libinput_ptr_ctl(DeviceIntPtr dev, PtrCtrl *ctl)
+{
+}
+
+static void
+init_button_map(unsigned char *btnmap, size_t size)
+{
+ int i;
+
+ memset(btnmap, 0, size);
+ for (i = 0; i < size; i++)
+ btnmap[i] = i;
+}
+
+static void
+init_button_labels(Atom *labels, size_t size)
+{
+ assert(size > 10);
+
+ memset(labels, 0, size * sizeof(Atom));
+ labels[0] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_LEFT);
+ labels[1] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_MIDDLE);
+ labels[2] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_RIGHT);
+ labels[3] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_UP);
+ labels[4] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_DOWN);
+ labels[5] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_LEFT);
+ labels[6] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_RIGHT);
+ labels[7] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_SIDE);
+ labels[8] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_EXTRA);
+ labels[9] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_FORWARD);
+ labels[10] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_BACK);
+}
+
+static void
+init_axis_labels(Atom *labels, size_t size)
+{
+ memset(labels, 0, size * sizeof(Atom));
+ labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_X);
+ labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y);
+ labels[2] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_HSCROLL);
+ labels[3] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_VSCROLL);
+}
+
+static int
+xf86libinput_init_pointer(InputInfoPtr pInfo)
+{
+ DeviceIntPtr dev= pInfo->dev;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+ int min, max, res;
+ int nbuttons = 7;
+ int i;
+
+ Atom btnlabels[MAX_BUTTONS];
+ Atom axislabels[TOUCHPAD_NUM_AXES];
+
+ for (i = BTN_JOYSTICK - 1; i >= BTN_SIDE; i--) {
+ if (libinput_device_pointer_has_button(device, i)) {
+ nbuttons += i - BTN_SIDE + 1;
+ break;
+ }
+ }
+
+ init_button_labels(btnlabels, ARRAY_SIZE(btnlabels));
+ init_axis_labels(axislabels, ARRAY_SIZE(axislabels));
+
+ InitPointerDeviceStruct((DevicePtr)dev,
+ driver_data->options.btnmap,
+ nbuttons,
+ btnlabels,
+ xf86libinput_ptr_ctl,
+ GetMotionHistorySize(),
+ TOUCHPAD_NUM_AXES,
+ axislabels);
+ min = -1;
+ max = -1;
+ res = 0;
+
+ xf86InitValuatorAxisStruct(dev, 0,
+ XIGetKnownProperty(AXIS_LABEL_PROP_REL_X),
+ min, max, res * 1000, 0, res * 1000, Relative);
+ xf86InitValuatorAxisStruct(dev, 1,
+ XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y),
+ min, max, res * 1000, 0, res * 1000, Relative);
+
+ SetScrollValuator(dev, 2, SCROLL_TYPE_HORIZONTAL, driver_data->scroll.h.dist, 0);
+ SetScrollValuator(dev, 3, SCROLL_TYPE_VERTICAL, driver_data->scroll.v.dist, 0);
+
+ return Success;
+}
+
+static int
+xf86libinput_init_pointer_absolute(InputInfoPtr pInfo)
+{
+ DeviceIntPtr dev= pInfo->dev;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+ int min, max, res;
+ int nbuttons = 7;
+ int i;
+
+ Atom btnlabels[MAX_BUTTONS];
+ Atom axislabels[TOUCHPAD_NUM_AXES];
+
+ for (i = BTN_BACK; i >= BTN_SIDE; i--) {
+ if (libinput_device_pointer_has_button(device, i)) {
+ nbuttons += i - BTN_SIDE + 1;
+ break;
+ }
+ }
+
+ init_button_labels(btnlabels, ARRAY_SIZE(btnlabels));
+ init_axis_labels(axislabels, ARRAY_SIZE(axislabels));
+
+ InitPointerDeviceStruct((DevicePtr)dev,
+ driver_data->options.btnmap,
+ nbuttons,
+ btnlabels,
+ xf86libinput_ptr_ctl,
+ GetMotionHistorySize(),
+ TOUCHPAD_NUM_AXES,
+ axislabels);
+ min = 0;
+ max = TOUCH_AXIS_MAX;
+ res = 0;
+
+ xf86InitValuatorAxisStruct(dev, 0,
+ XIGetKnownProperty(AXIS_LABEL_PROP_ABS_X),
+ min, max, res * 1000, 0, res * 1000, Absolute);
+ xf86InitValuatorAxisStruct(dev, 1,
+ XIGetKnownProperty(AXIS_LABEL_PROP_ABS_Y),
+ min, max, res * 1000, 0, res * 1000, Absolute);
+
+ SetScrollValuator(dev, 2, SCROLL_TYPE_HORIZONTAL, driver_data->scroll.h.dist, 0);
+ SetScrollValuator(dev, 3, SCROLL_TYPE_VERTICAL, driver_data->scroll.v.dist, 0);
+
+ driver_data->has_abs = TRUE;
+
+ return Success;
+}
+static void
+xf86libinput_kbd_ctrl(DeviceIntPtr device, KeybdCtrl *ctrl)
+{
+#define CAPSFLAG 1
+#define NUMFLAG 2
+#define SCROLLFLAG 4
+
+ static struct { int xbit, code; } bits[] = {
+ { CAPSFLAG, LIBINPUT_LED_CAPS_LOCK },
+ { NUMFLAG, LIBINPUT_LED_NUM_LOCK },
+ { SCROLLFLAG, LIBINPUT_LED_SCROLL_LOCK },
+ { 0, 0 },
+ };
+ int i = 0;
+ enum libinput_led leds = 0;
+ InputInfoPtr pInfo = device->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *ldevice = driver_data->shared_device->device;
+
+ if (!device->enabled)
+ return;
+
+ while (bits[i].xbit) {
+ if (ctrl->leds & bits[i].xbit)
+ leds |= bits[i].code;
+ i++;
+ }
+
+ libinput_device_led_update(ldevice, leds);
+}
+
+static void
+xf86libinput_init_keyboard(InputInfoPtr pInfo)
+{
+ DeviceIntPtr dev= pInfo->dev;
+ XkbRMLVOSet rmlvo = {0};
+ XkbRMLVOSet defaults = {0};
+
+ XkbGetRulesDflts(&defaults);
+
+ rmlvo.rules = xf86SetStrOption(pInfo->options,
+ "xkb_rules",
+ defaults.rules);
+ rmlvo.model = xf86SetStrOption(pInfo->options,
+ "xkb_model",
+ defaults.model);
+ rmlvo.layout = xf86SetStrOption(pInfo->options,
+ "xkb_layout",
+ defaults.layout);
+ rmlvo.variant = xf86SetStrOption(pInfo->options,
+ "xkb_variant",
+ defaults.variant);
+ rmlvo.options = xf86SetStrOption(pInfo->options,
+ "xkb_options",
+ defaults.options);
+
+ InitKeyboardDeviceStruct(dev, &rmlvo, NULL,
+ xf86libinput_kbd_ctrl);
+ XkbFreeRMLVOSet(&rmlvo, FALSE);
+ XkbFreeRMLVOSet(&defaults, FALSE);
+}
+
+static void
+xf86libinput_init_touch(InputInfoPtr pInfo)
+{
+ DeviceIntPtr dev = pInfo->dev;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+ int min, max, res;
+ Atom btnlabels[MAX_BUTTONS];
+ Atom axislabels[TOUCHPAD_NUM_AXES];
+ int nbuttons = 7;
+ int ntouches = TOUCH_MAX_SLOTS;
+
+ init_button_labels(btnlabels, ARRAY_SIZE(btnlabels));
+ init_axis_labels(axislabels, ARRAY_SIZE(axislabels));
+
+ InitPointerDeviceStruct((DevicePtr)dev,
+ driver_data->options.btnmap,
+ nbuttons,
+ btnlabels,
+ xf86libinput_ptr_ctl,
+ GetMotionHistorySize(),
+ TOUCHPAD_NUM_AXES,
+ axislabels);
+ min = 0;
+ max = TOUCH_AXIS_MAX;
+ res = 0;
+
+ xf86InitValuatorAxisStruct(dev, 0,
+ XIGetKnownProperty(AXIS_LABEL_PROP_ABS_MT_POSITION_X),
+ min, max, res * 1000, 0, res * 1000, Absolute);
+ xf86InitValuatorAxisStruct(dev, 1,
+ XIGetKnownProperty(AXIS_LABEL_PROP_ABS_MT_POSITION_Y),
+ min, max, res * 1000, 0, res * 1000, Absolute);
+
+ ntouches = libinput_device_touch_get_touch_count(device);
+ if (ntouches == 0) /* unknown - mtdev */
+ ntouches = TOUCH_MAX_SLOTS;
+ InitTouchClassDeviceStruct(dev, ntouches, XIDirectTouch, 2);
+}
+
+#ifdef HAVE_GESTURES
+static void
+xf86libinput_init_gesture(InputInfoPtr pInfo)
+{
+ DeviceIntPtr dev = pInfo->dev;
+ int ntouches = TOUCH_MAX_SLOTS;
+ InitGestureClassDeviceStruct(dev, ntouches);
+}
+#endif
+
+static int
+xf86libinput_init_tablet_pen_or_eraser(InputInfoPtr pInfo,
+ struct libinput_tablet_tool *tool)
+{
+ DeviceIntPtr dev = pInfo->dev;
+ int min, max, res;
+ int axis;
+
+ min = 0;
+ max = TABLET_PRESSURE_AXIS_MAX;
+ res = 0;
+ axis = 2;
+ if (libinput_tablet_tool_has_pressure(tool))
+ xf86InitValuatorAxisStruct(dev, axis++,
+ XIGetKnownProperty(AXIS_LABEL_PROP_ABS_PRESSURE),
+ min, max, res * 1000, 0, res * 1000, Absolute);
+ max = TABLET_TILT_AXIS_MAX;
+ min = -TABLET_TILT_AXIS_MAX;
+ if (libinput_tablet_tool_has_tilt(tool)) {
+ xf86InitValuatorAxisStruct(dev, axis++,
+ XIGetKnownProperty(AXIS_LABEL_PROP_ABS_TILT_X),
+ min, max, res * 1000, 0, res * 1000, Absolute);
+ xf86InitValuatorAxisStruct(dev, axis++,
+ XIGetKnownProperty(AXIS_LABEL_PROP_ABS_TILT_Y),
+ min, max, res * 1000, 0, res * 1000, Absolute);
+ }
+
+ min = -TABLET_AXIS_MAX;
+ max = TABLET_AXIS_MAX;
+ if (libinput_tablet_tool_has_rotation(tool))
+ xf86InitValuatorAxisStruct(dev, axis++,
+ XIGetKnownProperty(AXIS_LABEL_PROP_ABS_RZ),
+ min, max, res * 1000, 0, res * 1000, Absolute);
+ return axis;
+}
+
+static void
+xf86libinput_init_tablet_airbrush(InputInfoPtr pInfo,
+ struct libinput_tablet_tool *tool)
+{
+ DeviceIntPtr dev = pInfo->dev;
+ int min, max, res;
+ int axis;
+
+ /* first axes are shared */
+ axis = xf86libinput_init_tablet_pen_or_eraser(pInfo, tool);
+ if (axis < 5) {
+ xf86IDrvMsg(pInfo, X_ERROR, "Airbrush tool has missing pressure or tilt axes\n");
+ return;
+ }
+
+ if (!libinput_tablet_tool_has_slider(tool)) {
+ xf86IDrvMsg(pInfo, X_ERROR, "Airbrush tool is missing the slider axis\n");
+ return;
+ }
+
+ min = -TABLET_AXIS_MAX;
+ max = TABLET_AXIS_MAX;
+ res = 0;
+
+ xf86InitValuatorAxisStruct(dev, axis,
+ XIGetKnownProperty(AXIS_LABEL_PROP_ABS_THROTTLE),
+ min, max, res * 1000, 0, res * 1000, Absolute);
+}
+
+static void
+xf86libinput_init_tablet_mouse(InputInfoPtr pInfo,
+ struct libinput_tablet_tool *tool)
+{
+ DeviceIntPtr dev = pInfo->dev;
+ int min, max, res;
+ int axis;
+
+ if (!libinput_tablet_tool_has_rotation(tool)) {
+ xf86IDrvMsg(pInfo, X_ERROR, "Mouse tool is missing the rotation axis\n");
+ return;
+ }
+
+ min = 0;
+ max = TABLET_AXIS_MAX;
+ res = 0;
+
+ /* The mouse/lens tool don't have pressure, but for backwards-compat
+ with the xorg wacom driver we initialize the the axis anyway */
+ axis = 2;
+ xf86InitValuatorAxisStruct(dev, axis,
+ XIGetKnownProperty(AXIS_LABEL_PROP_ABS_PRESSURE),
+ min, max, res * 1000, 0, res * 1000, Absolute);
+
+ axis = 3;
+ min = -TABLET_AXIS_MAX;
+ max = TABLET_AXIS_MAX;
+ xf86InitValuatorAxisStruct(dev, axis,
+ XIGetKnownProperty(AXIS_LABEL_PROP_ABS_RZ),
+ min, max, res * 1000, 0, res * 1000, Absolute);
+ return;
+}
+
+static void
+xf86libinput_init_tablet(InputInfoPtr pInfo)
+{
+ DeviceIntPtr dev = pInfo->dev;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_tablet_tool *tool;
+ int min, max, res;
+ Atom btnlabels[TABLET_NUM_BUTTONS] = {0};
+ Atom axislabels[TOUCHPAD_NUM_AXES] = {0};
+ int nbuttons = TABLET_NUM_BUTTONS;
+ int naxes = 2;
+
+ BUG_RETURN(driver_data->tablet_tool == NULL);
+
+ tool = driver_data->tablet_tool;
+
+ if (libinput_tablet_tool_has_pressure(tool))
+ naxes++;
+ if (libinput_tablet_tool_has_tilt(tool))
+ naxes += 2;
+ if (libinput_tablet_tool_has_slider(tool))
+ naxes++;
+ if (libinput_tablet_tool_has_rotation(tool))
+ naxes++;
+
+ InitPointerDeviceStruct((DevicePtr)dev,
+ driver_data->options.btnmap,
+ nbuttons,
+ btnlabels,
+ xf86libinput_ptr_ctl,
+ GetMotionHistorySize(),
+ naxes,
+ axislabels);
+
+ min = 0;
+ max = TABLET_AXIS_MAX;
+ res = 0;
+ xf86InitValuatorAxisStruct(dev, 0,
+ XIGetKnownProperty(AXIS_LABEL_PROP_ABS_X),
+ min, max, res * 1000, 0, res * 1000, Absolute);
+ xf86InitValuatorAxisStruct(dev, 1,
+ XIGetKnownProperty(AXIS_LABEL_PROP_ABS_Y),
+ min, max, res * 1000, 0, res * 1000, Absolute);
+
+ switch (libinput_tablet_tool_get_type(tool)) {
+ case LIBINPUT_TABLET_TOOL_TYPE_PEN:
+ case LIBINPUT_TABLET_TOOL_TYPE_ERASER:
+ xf86libinput_init_tablet_pen_or_eraser(pInfo, tool);
+ break;
+ case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH:
+ xf86libinput_init_tablet_airbrush(pInfo, tool);
+ break;
+ case LIBINPUT_TABLET_TOOL_TYPE_MOUSE:
+ case LIBINPUT_TABLET_TOOL_TYPE_LENS:
+ xf86libinput_init_tablet_mouse(pInfo, tool);
+ break;
+ default:
+ xf86IDrvMsg(pInfo, X_ERROR, "Tool type not supported yet\n");
+ break;
+ }
+
+ InitProximityClassDeviceStruct(dev);
+}
+
+static void
+xf86libinput_init_tablet_pad(InputInfoPtr pInfo)
+{
+ DeviceIntPtr dev = pInfo->dev;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+ int min, max, res;
+ Atom btnlabels[MAX_BUTTONS] = {0};
+ Atom axislabels[TOUCHPAD_NUM_AXES] = {0};
+ int nbuttons;
+ int naxes = 7;
+
+ nbuttons = libinput_device_tablet_pad_get_num_buttons(device) + 4;
+
+ InitPointerDeviceStruct((DevicePtr)dev,
+ driver_data->options.btnmap,
+ nbuttons,
+ btnlabels,
+ xf86libinput_ptr_ctl,
+ GetMotionHistorySize(),
+ naxes,
+ axislabels);
+
+ /* For compat with xf86-input-wacom we init x, y, pressure, followed
+ * by strip x, strip y, ring, ring2*/
+ min = 0;
+ max = TABLET_AXIS_MAX;
+ res = 0;
+ xf86InitValuatorAxisStruct(dev, 0,
+ XIGetKnownProperty(AXIS_LABEL_PROP_ABS_X),
+ min, max, res * 1000, 0, res * 1000, Absolute);
+ xf86InitValuatorAxisStruct(dev, 1,
+ XIGetKnownProperty(AXIS_LABEL_PROP_ABS_Y),
+ min, max, res * 1000, 0, res * 1000, Absolute);
+ xf86InitValuatorAxisStruct(dev, 2,
+ XIGetKnownProperty(AXIS_LABEL_PROP_ABS_PRESSURE),
+ min, max, res * 1000, 0, res * 1000, Absolute);
+
+ /* strip x */
+ max = TABLET_STRIP_AXIS_MAX;
+ xf86InitValuatorAxisStruct(dev, 3,
+ None,
+ min, max, res * 1000, 0, res * 1000, Absolute);
+ /* strip y */
+ xf86InitValuatorAxisStruct(dev, 4,
+ None,
+ min, max, res * 1000, 0, res * 1000, Absolute);
+ /* first ring */
+ max = TABLET_RING_AXIS_MAX;
+ xf86InitValuatorAxisStruct(dev, 5,
+ XIGetKnownProperty(AXIS_LABEL_PROP_ABS_WHEEL),
+ min, max, res * 1000, 0, res * 1000, Absolute);
+ /* second ring */
+ xf86InitValuatorAxisStruct(dev, 6,
+ None,
+ min, max, res * 1000, 0, res * 1000, Absolute);
+}
+
+static int
+xf86libinput_init(DeviceIntPtr dev)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct xf86libinput_device *shared_device = driver_data->shared_device;
+ struct libinput_device *device = shared_device->device;
+
+ BUG_RETURN_VAL(device == NULL, !Success);
+
+ dev->public.on = FALSE;
+
+ if (driver_data->capabilities & CAP_KEYBOARD)
+ xf86libinput_init_keyboard(pInfo);
+ if (driver_data->capabilities & CAP_POINTER) {
+ if (libinput_device_config_calibration_has_matrix(device) &&
+ !libinput_device_config_accel_is_available(device))
+ xf86libinput_init_pointer_absolute(pInfo);
+ else
+ xf86libinput_init_pointer(pInfo);
+ }
+ if (driver_data->capabilities & CAP_TOUCH)
+ xf86libinput_init_touch(pInfo);
+#ifdef HAVE_GESTURES
+ if (driver_data->capabilities & CAP_GESTURE)
+ xf86libinput_init_gesture(pInfo);
+#endif
+ if (driver_data->capabilities & CAP_TABLET_TOOL)
+ xf86libinput_init_tablet(pInfo);
+ if (driver_data->capabilities & CAP_TABLET_PAD)
+ xf86libinput_init_tablet_pad(pInfo);
+
+ LibinputApplyConfig(dev);
+ LibinputInitProperty(dev);
+ XIRegisterPropertyHandler(dev, LibinputSetProperty, NULL, NULL);
+
+ /* If we have a device but it's not yet enabled it's the
+ * already-removed device from PreInit. Drop the ref to clean up,
+ * we'll get a new libinput_device during DEVICE_ON when we re-add
+ * it. */
+ if (!xf86libinput_shared_is_enabled(shared_device)) {
+ libinput_device_unref(device);
+ shared_device->device = NULL;
+ }
+
+ return 0;
+}
+
+static bool
+is_libinput_device(InputInfoPtr pInfo)
+{
+ char *driver;
+ BOOL rc;
+
+ driver = xf86CheckStrOption(pInfo->options, "driver", "");
+ rc = streq(driver, "libinput");
+ free(driver);
+
+ return rc;
+}
+
+static void
+swap_registered_device(InputInfoPtr pInfo)
+{
+ InputInfoPtr next;
+
+ if (pInfo != driver_context.registered_InputInfoPtr)
+ return;
+
+ next = xf86FirstLocalDevice();
+ while (next == pInfo || !is_libinput_device(next))
+ next = next->next;
+
+ input_lock();
+ xf86RemoveEnabledDevice(pInfo);
+ xf86AddEnabledDevice(next);
+ driver_context.registered_InputInfoPtr = next;
+ input_unlock();
+}
+
+static void
+xf86libinput_destroy(DeviceIntPtr dev)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct xf86libinput_device *shared_device = driver_data->shared_device;
+
+ /* If the device being destroyed is the one we used for
+ * xf86AddEnabledDevice(), we need to swap it out for one that is
+ * still live. xf86AddEnabledDevice() buffers some data and once the
+ * deletes pInfo (when DEVICE_OFF completes) the thread will keep
+ * calling that struct's read_input because we never removed it.
+ * Avoid this by removing ours and substituting one that's still
+ * valid, the fd is the same anyway (libinput's epollfd).
+ */
+ if (driver_context.device_enabled_count > 0)
+ swap_registered_device(pInfo);
+
+ xorg_list_del(&driver_data->shared_device_link);
+
+ if (driver_data->tablet_tool)
+ libinput_tablet_tool_unref(driver_data->tablet_tool);
+
+ xf86libinput_shared_unref(shared_device);
+}
+
+static int
+xf86libinput_device_control(DeviceIntPtr dev, int mode)
+{
+ int rc = BadValue;
+
+ switch(mode) {
+ case DEVICE_INIT:
+ rc = xf86libinput_init(dev);
+ break;
+ case DEVICE_ON:
+ rc = xf86libinput_on(dev);
+ break;
+ case DEVICE_OFF:
+ rc = xf86libinput_off(dev);
+ break;
+ case DEVICE_CLOSE:
+ xf86libinput_destroy(dev);
+ rc = Success;
+ break;
+ }
+
+ return rc;
+}
+
+static void
+xf86libinput_handle_motion(InputInfoPtr pInfo, struct libinput_event_pointer *event)
+{
+ DeviceIntPtr dev = pInfo->dev;
+ struct xf86libinput *driver_data = pInfo->private;
+ ValuatorMask *mask = driver_data->valuators;
+ double x, y;
+
+ if ((driver_data->capabilities & CAP_POINTER) == 0)
+ return;
+
+ x = libinput_event_pointer_get_dx(event);
+ y = libinput_event_pointer_get_dy(event);
+
+ valuator_mask_zero(mask);
+
+ {
+ double ux, uy;
+
+ ux = libinput_event_pointer_get_dx_unaccelerated(event);
+ uy = libinput_event_pointer_get_dy_unaccelerated(event);
+
+ valuator_mask_set_unaccelerated(mask, 0, x, ux);
+ valuator_mask_set_unaccelerated(mask, 1, y, uy);
+ }
+ xf86PostMotionEventM(dev, Relative, mask);
+}
+
+static void
+xf86libinput_handle_absmotion(InputInfoPtr pInfo, struct libinput_event_pointer *event)
+{
+ DeviceIntPtr dev = pInfo->dev;
+ struct xf86libinput *driver_data = pInfo->private;
+ ValuatorMask *mask = driver_data->valuators;
+ double x, y;
+
+ if (!driver_data->has_abs) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Discarding absolute event from relative device. "
+ "Please file a bug\n");
+ return;
+ }
+
+ if ((driver_data->capabilities & CAP_POINTER) == 0)
+ return;
+
+ x = libinput_event_pointer_get_absolute_x_transformed(event, TOUCH_AXIS_MAX);
+ y = libinput_event_pointer_get_absolute_y_transformed(event, TOUCH_AXIS_MAX);
+
+ valuator_mask_zero(mask);
+ valuator_mask_set_double(mask, 0, x);
+ valuator_mask_set_double(mask, 1, y);
+
+ xf86PostMotionEventM(dev, Absolute, mask);
+}
+
+static void
+xf86libinput_handle_button(InputInfoPtr pInfo, struct libinput_event_pointer *event)
+{
+ DeviceIntPtr dev = pInfo->dev;
+ struct xf86libinput *driver_data = pInfo->private;
+ int button;
+ int is_press;
+
+ if ((driver_data->capabilities & CAP_POINTER) == 0)
+ return;
+
+ button = btn_linux2xorg(libinput_event_pointer_get_button(event));
+ is_press = (libinput_event_pointer_get_button_state(event) == LIBINPUT_BUTTON_STATE_PRESSED);
+
+ if (draglock_get_mode(&driver_data->draglock) != DRAGLOCK_DISABLED)
+ draglock_filter_button(&driver_data->draglock, &button, &is_press);
+
+ if (button && button < 256)
+ xf86PostButtonEvent(dev, Relative, button, is_press, 0, 0);
+}
+
+static void
+xf86libinput_handle_key(InputInfoPtr pInfo, struct libinput_event_keyboard *event)
+{
+ DeviceIntPtr dev = pInfo->dev;
+ struct xf86libinput *driver_data = pInfo->private;
+ int is_press;
+ int key = libinput_event_keyboard_get_key(event);
+
+ if ((driver_data->capabilities & CAP_KEYBOARD) == 0)
+ return;
+
+ key += XORG_KEYCODE_OFFSET;
+
+ is_press = (libinput_event_keyboard_get_key_state(event) == LIBINPUT_KEY_STATE_PRESSED);
+ xf86PostKeyboardEvent(dev, key, is_press);
+}
+
+/*
+ * The scroll fraction is the value we divide the scroll dist with to
+ * accommodate for wheels with a small click angle. On these devices,
+ * multiple clicks of small angle accumulate to the XI 2.1 scroll distance.
+ * This gives us smooth scrolling on those wheels for small movements, the
+ * legacy button events are generated whenever the full distance is reached.
+ * e.g. a 2 degree click angle requires 8 clicks before a legacy event is
+ * sent, but each of those clicks will send XI2.1 smooth scroll data for
+ * compatible clients.
+ *
+ * Starting with kernel v5.0 we should get REL_WHEEL_HI_RES from those
+ * devices for the fine-grained scrolling and REL_WHEEL for the normal one,
+ * so the use-case above shouldn't matter anymore.
+ */
+static inline double
+guess_wheel_scroll_value(struct xf86libinput *driver_data,
+ struct libinput_event_pointer *event,
+ enum libinput_pointer_axis axis)
+{
+ struct scroll_axis *s;
+ double f;
+ double angle;
+ int discrete;
+
+ switch (axis) {
+ case LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL:
+ s = &driver_data->scroll.h;
+ break;
+ case LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL:
+ s = &driver_data->scroll.v;
+ break;
+ default:
+ return 0.0;
+ }
+
+ angle = libinput_event_pointer_get_axis_value(event, axis);
+ discrete = libinput_event_pointer_get_axis_value_discrete(event, axis);
+
+ /* We only need to guess the fraction on the first set of
+ * scroll events until a discrete value arrives. Once known, we
+ * re-use the fraction until the device goes away.
+ */
+ if (s->fraction != 0.0)
+ goto out;
+
+ /* if we get a discrete of 0, assume REL_WHEEL_HI_RES exists and
+ * normal scroll events are sent correctly, so skip all the
+ * guesswork.
+ */
+ if (discrete == 0) {
+ s->fraction = 1.0;
+ goto out;
+ }
+
+ /* Calculate the angle per single scroll event */
+ angle /= discrete;
+
+ /* We only do magic for click angles smaller than 10 degrees */
+ if (angle >= 10) {
+ s->fraction = 1.0;
+ goto out;
+ }
+
+ /* Figure out something that gets close to 15 degrees (the general
+ * wheel default) with a number of clicks. This formula gives us
+ * between 12 and and 20 degrees for the range of 1-10. See
+ * https://bugs.freedesktop.org/attachment.cgi?id=128256 for a
+ * graph.
+ */
+ f = round(15.0/angle);
+
+ s->fraction = f;
+
+out:
+ return s->dist/s->fraction * discrete;
+}
+
+#if HAVE_LIBINPUT_AXIS_VALUE_V120
+static inline double
+get_wheel_120_value(struct xf86libinput *driver_data,
+ struct libinput_event_pointer *event,
+ enum libinput_pointer_axis axis)
+{
+ struct scroll_axis *s;
+ double angle;
+
+ switch (axis) {
+ case LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL:
+ s = &driver_data->scroll.h;
+ break;
+ case LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL:
+ s = &driver_data->scroll.v;
+ break;
+ default:
+ return 0.0;
+ }
+
+ angle = libinput_event_pointer_get_scroll_value_v120(event, axis);
+ return s->dist * angle/120;
+}
+#endif
+
+static inline double
+get_wheel_scroll_value(struct xf86libinput *driver_data,
+ struct libinput_event_pointer *event,
+ enum libinput_pointer_axis axis)
+{
+#if HAVE_LIBINPUT_AXIS_VALUE_V120
+ if (driver_data->options.hires_scrolling_enabled)
+ return get_wheel_120_value(driver_data, event, axis);
+#endif
+ return guess_wheel_scroll_value(driver_data, event, axis);
+}
+
+static inline double
+get_finger_or_continuous_scroll_value(struct xf86libinput *driver_data,
+ struct libinput_event_pointer *event,
+ enum libinput_pointer_axis axis)
+{
+#if HAVE_LIBINPUT_AXIS_VALUE_V120
+ if (driver_data->options.hires_scrolling_enabled)
+ return libinput_event_pointer_get_scroll_value(event, axis);
+#endif
+ return libinput_event_pointer_get_axis_value(event, axis);
+}
+
+static inline bool
+calculate_axis_value(struct xf86libinput *driver_data,
+ enum libinput_pointer_axis axis,
+ struct libinput_event_pointer *event,
+ enum libinput_pointer_axis_source source,
+ double *value_out)
+{
+ double value;
+
+ if (!libinput_event_pointer_has_axis(event, axis))
+ return false;
+
+ /* Event may be LIBINPUT_POINTER_AXIS or
+ * LIBINPUT_EVENT_POINTER_SCROLL_{WHEEL|FINGER|CONTINUOUS}, depending
+ * on the libinput version.
+ *
+ * libinput guarantees the axis source is set for the second set of
+ * events too but we can switch to the event type once we ditch
+ * libinput < 1.19 support.
+ */
+ if (source == LIBINPUT_POINTER_AXIS_SOURCE_WHEEL) {
+ value = get_wheel_scroll_value(driver_data, event, axis);
+ } else {
+ double dist = driver_data->options.scroll_pixel_distance;
+ assert(dist != 0.0);
+
+ value = get_finger_or_continuous_scroll_value(driver_data,
+ event,
+ axis);
+
+ /* We need to scale this value into our scroll increment range
+ * because that one is constant for the lifetime of the
+ * device. The user may change the ScrollPixelDistance
+ * though, so where we have a dist of 10 but an increment of
+ * 15, we need to scale from 0..10 into 0..15.
+ *
+ * We now switched to vdist of 120, so make this
+ * proportionate - 120/15 is 8.
+ */
+ value = value/dist * SCROLL_INCREMENT * 8;
+ }
+
+ *value_out = value;
+
+ return true;
+}
+
+static void
+xf86libinput_handle_axis(InputInfoPtr pInfo,
+ struct libinput_event *e,
+ enum libinput_pointer_axis_source source)
+{
+ struct libinput_event_pointer *event;
+ DeviceIntPtr dev = pInfo->dev;
+ struct xf86libinput *driver_data = pInfo->private;
+ ValuatorMask *mask = driver_data->valuators;
+ double value;
+
+ if ((driver_data->capabilities & CAP_POINTER) == 0)
+ return;
+
+ valuator_mask_zero(mask);
+
+ event = libinput_event_get_pointer_event(e);
+ if (calculate_axis_value(driver_data,
+ LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
+ event,
+ source,
+ &value))
+ valuator_mask_set_double(mask, 3, value);
+
+ if (!driver_data->options.horiz_scrolling_enabled)
+ goto out;
+
+ if (calculate_axis_value(driver_data,
+ LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
+ event,
+ source,
+ &value))
+ valuator_mask_set_double(mask, 2, value);
+
+ if (source == LIBINPUT_POINTER_AXIS_SOURCE_WHEEL &&
+ !valuator_mask_isset(mask, 2) &&
+ !valuator_mask_isset(mask, 3))
+ return;
+
+out:
+ xf86PostMotionEventM(dev, Relative, mask);
+}
+
+static void
+xf86libinput_handle_touch(InputInfoPtr pInfo,
+ struct libinput_event_touch *event,
+ enum libinput_event_type event_type)
+{
+ DeviceIntPtr dev = pInfo->dev;
+ struct xf86libinput *driver_data = pInfo->private;
+ int type;
+ int slot;
+ ValuatorMask *m = driver_data->valuators;
+ double val;
+
+ /* libinput doesn't give us hw touch ids which X expects, so
+ emulate them here */
+ static unsigned int next_touchid;
+ static unsigned int touchids[TOUCH_MAX_SLOTS] = {0};
+
+ if ((driver_data->capabilities & CAP_TOUCH) == 0)
+ return;
+
+ slot = libinput_event_touch_get_seat_slot(event);
+
+ switch (event_type) {
+ case LIBINPUT_EVENT_TOUCH_DOWN:
+ type = XI_TouchBegin;
+ touchids[slot] = next_touchid++;
+ break;
+ case LIBINPUT_EVENT_TOUCH_UP:
+ case LIBINPUT_EVENT_TOUCH_CANCEL:
+ type = XI_TouchEnd;
+ break;
+ case LIBINPUT_EVENT_TOUCH_MOTION:
+ type = XI_TouchUpdate;
+ break;
+ default:
+ return;
+ }
+
+ valuator_mask_zero(m);
+
+ if (type != XI_TouchEnd) {
+ val = libinput_event_touch_get_x_transformed(event, TOUCH_AXIS_MAX);
+ valuator_mask_set_double(m, 0, val);
+
+ val = libinput_event_touch_get_y_transformed(event, TOUCH_AXIS_MAX);
+ valuator_mask_set_double(m, 1, val);
+ }
+
+ xf86PostTouchEvent(dev, touchids[slot], type, 0, m);
+}
+
+#ifdef HAVE_GESTURES
+static void
+xf86libinput_handle_gesture_swipe(InputInfoPtr pInfo,
+ struct libinput_event_gesture *event,
+ enum libinput_event_type event_type)
+{
+ DeviceIntPtr dev = pInfo->dev;
+ struct xf86libinput *driver_data = pInfo->private;
+ int type;
+ uint32_t flags = 0;
+
+ if ((driver_data->capabilities & CAP_GESTURE) == 0)
+ return;
+
+ switch (event_type) {
+ case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
+ type = XI_GestureSwipeBegin;
+ break;
+ case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
+ type = XI_GestureSwipeUpdate;
+ break;
+ case LIBINPUT_EVENT_GESTURE_SWIPE_END:
+ type = XI_GestureSwipeEnd;
+ if (libinput_event_gesture_get_cancelled(event))
+ flags |= XIGestureSwipeEventCancelled;
+ break;
+ default:
+ return;
+ }
+
+ xf86PostGestureSwipeEvent(dev, type,
+ libinput_event_gesture_get_finger_count(event),
+ flags,
+ libinput_event_gesture_get_dx(event),
+ libinput_event_gesture_get_dy(event),
+ libinput_event_gesture_get_dx_unaccelerated(event),
+ libinput_event_gesture_get_dy_unaccelerated(event));
+}
+
+static void
+xf86libinput_handle_gesture_pinch(InputInfoPtr pInfo,
+ struct libinput_event_gesture *event,
+ enum libinput_event_type event_type)
+{
+ DeviceIntPtr dev = pInfo->dev;
+ struct xf86libinput *driver_data = pInfo->private;
+ int type;
+ uint32_t flags = 0;
+
+ if ((driver_data->capabilities & CAP_GESTURE) == 0)
+ return;
+
+ switch (event_type) {
+ case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
+ type = XI_GesturePinchBegin;
+ break;
+ case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
+ type = XI_GesturePinchUpdate;
+ break;
+ case LIBINPUT_EVENT_GESTURE_PINCH_END:
+ type = XI_GesturePinchEnd;
+ if (libinput_event_gesture_get_cancelled(event))
+ flags |= XIGesturePinchEventCancelled;
+ break;
+ default:
+ return;
+ }
+
+ xf86PostGesturePinchEvent(dev, type,
+ libinput_event_gesture_get_finger_count(event),
+ flags,
+ libinput_event_gesture_get_dx(event),
+ libinput_event_gesture_get_dy(event),
+ libinput_event_gesture_get_dx_unaccelerated(event),
+ libinput_event_gesture_get_dy_unaccelerated(event),
+ libinput_event_gesture_get_scale(event),
+ libinput_event_gesture_get_angle_delta(event));
+}
+#endif
+
+static InputInfoPtr
+xf86libinput_pick_device(struct xf86libinput_device *shared_device,
+ struct libinput_event *event)
+{
+ struct xf86libinput *driver_data;
+ uint32_t needed_cap;
+ enum libinput_event_type type = libinput_event_get_type(event);
+
+ if (shared_device == NULL)
+ return NULL;
+
+ switch(type) {
+ case LIBINPUT_EVENT_KEYBOARD_KEY:
+ needed_cap = CAP_KEYBOARD;
+ break;
+ case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
+ needed_cap = CAP_TABLET;
+ break;
+ case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
+ case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
+ case LIBINPUT_EVENT_TABLET_TOOL_TIP:
+ needed_cap = CAP_TABLET_TOOL;
+ break;
+ default:
+ needed_cap = ~CAP_KEYBOARD;
+ break;
+ }
+
+ xorg_list_for_each_entry(driver_data,
+ &shared_device->device_list,
+ shared_device_link) {
+ if (driver_data->capabilities & needed_cap) {
+ struct libinput_tablet_tool *tool;
+
+ if (needed_cap != CAP_TABLET_TOOL)
+ return driver_data->pInfo;
+
+ tool = libinput_event_tablet_tool_get_tool(
+ libinput_event_get_tablet_tool_event(event));
+ if (libinput_tablet_tool_get_serial(driver_data->tablet_tool) ==
+ libinput_tablet_tool_get_serial(tool) &&
+ libinput_tablet_tool_get_tool_id(driver_data->tablet_tool) ==
+ libinput_tablet_tool_get_tool_id(tool) &&
+ libinput_tablet_tool_get_type(driver_data->tablet_tool) ==
+ libinput_tablet_tool_get_type(tool))
+ return driver_data->pInfo;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+xf86libinput_tool_destroy_queued_event(struct xf86libinput_tablet_tool_queued_event *qe)
+{
+ struct libinput_event *e;
+
+ e = libinput_event_tablet_tool_get_base_event(qe->event);
+ libinput_event_destroy(e);
+ xorg_list_del(&qe->node);
+ free(qe);
+}
+
+static void
+xf86libinput_tool_replay_events(struct xf86libinput_tablet_tool_event_queue *queue)
+{
+ struct xf86libinput_tablet_tool_queued_event *qe, *tmp;
+
+ xorg_list_for_each_entry_safe(qe, tmp, &queue->event_list, node) {
+ struct libinput_event *e;
+
+ e = libinput_event_tablet_tool_get_base_event(qe->event);
+ xf86libinput_handle_event(e);
+ xf86libinput_tool_destroy_queued_event(qe);
+ }
+}
+
+static bool
+xf86libinput_tool_queue_event(struct libinput_event_tablet_tool *event)
+{
+ struct libinput_event *e;
+ struct libinput_tablet_tool *tool;
+ struct xf86libinput_tablet_tool_event_queue *queue;
+ struct xf86libinput_tablet_tool_queued_event *qe;
+
+ tool = libinput_event_tablet_tool_get_tool(event);
+ if (!tool)
+ return true;
+
+ queue = libinput_tablet_tool_get_user_data(tool);
+ if (!queue)
+ return false;
+
+ if (!queue->need_to_queue) {
+ if (!xorg_list_is_empty(&queue->event_list)) {
+ libinput_tablet_tool_set_user_data(tool, NULL);
+ xf86libinput_tool_replay_events(queue);
+ free(queue);
+ }
+
+ return false;
+ }
+
+ /* We got the prox out while still queuing, just ditch the whole
+ * series of events and the event queue with it. */
+ if (libinput_event_tablet_tool_get_proximity_state(event) ==
+ LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT) {
+ struct xf86libinput_tablet_tool_queued_event *tmp;
+
+ xorg_list_for_each_entry_safe(qe, tmp, &queue->event_list, node)
+ xf86libinput_tool_destroy_queued_event(qe);
+
+ libinput_tablet_tool_set_user_data(tool, NULL);
+ free(queue);
+
+ /* we destroy the event here but return true
+ * to make sure the event looks like it got queued and the
+ * caller doesn't destroy it for us
+ */
+ e = libinput_event_tablet_tool_get_base_event(event);
+ libinput_event_destroy(e);
+ return true;
+ }
+
+ qe = calloc(1, sizeof(*qe));
+ if (!qe) {
+ e = libinput_event_tablet_tool_get_base_event(event);
+ libinput_event_destroy(e);
+ return true;
+ }
+
+ qe->event = event;
+ xorg_list_append(&qe->node, &queue->event_list);
+
+ return true;
+}
+
+static enum event_handling
+xf86libinput_handle_tablet_tip(InputInfoPtr pInfo,
+ struct libinput_event_tablet_tool *event)
+{
+ DeviceIntPtr pDev = pInfo->dev;
+ enum libinput_tablet_tool_tip_state state;
+ const BOOL is_absolute = TRUE;
+
+ if (xf86libinput_tool_queue_event(event))
+ return EVENT_QUEUED;
+
+ xf86libinput_post_tablet_motion(pDev->public.devicePrivate, event);
+
+ state = libinput_event_tablet_tool_get_tip_state(event);
+
+ xf86PostButtonEventP(pInfo->dev,
+ is_absolute, 1,
+ state == LIBINPUT_TABLET_TOOL_TIP_DOWN ? 1 : 0,
+ 0, 0, NULL);
+
+ return EVENT_HANDLED;
+}
+
+static enum event_handling
+xf86libinput_handle_tablet_button(InputInfoPtr pInfo,
+ struct libinput_event_tablet_tool *event)
+{
+ enum libinput_button_state state;
+ uint32_t button, b;
+
+ if (xf86libinput_tool_queue_event(event))
+ return EVENT_QUEUED;
+
+ button = libinput_event_tablet_tool_get_button(event);
+ state = libinput_event_tablet_tool_get_button_state(event);
+
+ b = btn_linux2xorg(button);
+
+ xf86PostButtonEventP(pInfo->dev,
+ TRUE,
+ b,
+ state == LIBINPUT_BUTTON_STATE_PRESSED ? 1 : 0,
+ 0, 0, NULL);
+
+ return EVENT_HANDLED;
+}
+
+static void
+xf86libinput_apply_area(InputInfoPtr pInfo, double *x, double *y)
+{
+ struct xf86libinput *driver_data = pInfo->private;
+ const struct scale_factor *f = &driver_data->area_scale_factor;
+ double sx, sy;
+
+ if (driver_data->options.area.x == 0)
+ return;
+
+ /* In left-handed mode, libinput already gives us transformed
+ * coordinates, so we can clip the same way. */
+
+ sx = min(*x * f->x, TABLET_AXIS_MAX);
+ sy = min(*y * f->y, TABLET_AXIS_MAX);
+
+ *x = sx;
+ *y = sy;
+}
+
+static void
+xf86libinput_post_tablet_motion(InputInfoPtr pInfo,
+ struct libinput_event_tablet_tool *event)
+{
+ DeviceIntPtr dev = pInfo->dev;
+ struct xf86libinput *driver_data = pInfo->private;
+ ValuatorMask *mask = driver_data->valuators;
+ struct libinput_tablet_tool *tool;
+ double value;
+ double x, y;
+
+ x = libinput_event_tablet_tool_get_x_transformed(event,
+ TABLET_AXIS_MAX);
+ y = libinput_event_tablet_tool_get_y_transformed(event,
+ TABLET_AXIS_MAX);
+ xf86libinput_apply_area(pInfo, &x, &y);
+ valuator_mask_set_double(mask, 0, x);
+ valuator_mask_set_double(mask, 1, y);
+
+ tool = libinput_event_tablet_tool_get_tool(event);
+
+ if (libinput_tablet_tool_has_pressure(tool)) {
+ value = TABLET_PRESSURE_AXIS_MAX * libinput_event_tablet_tool_get_pressure(event);
+ if (driver_data->pressurecurve.values)
+ value = driver_data->pressurecurve.values[(int)value];
+ valuator_mask_set_double(mask, 2, value);
+ }
+
+ if (libinput_tablet_tool_has_tilt(tool)) {
+ value = libinput_event_tablet_tool_get_tilt_x(event);
+ valuator_mask_set_double(mask, 3, value);
+
+ value = libinput_event_tablet_tool_get_tilt_y(event);
+ valuator_mask_set_double(mask, 4, value);
+ }
+
+ if (libinput_tablet_tool_has_slider(tool)) {
+ value = libinput_event_tablet_tool_get_slider_position(event);
+ value *= TABLET_AXIS_MAX;
+ valuator_mask_set_double(mask, 5, value);
+ }
+
+ if (libinput_tablet_tool_has_rotation(tool)) {
+ int valuator;
+
+ value = libinput_event_tablet_tool_get_rotation(event);
+ value *= TABLET_AXIS_MAX;
+
+ switch (libinput_tablet_tool_get_type(tool)) {
+ case LIBINPUT_TABLET_TOOL_TYPE_PEN:
+ case LIBINPUT_TABLET_TOOL_TYPE_ERASER:
+ valuator = 5;
+ break;
+ case LIBINPUT_TABLET_TOOL_TYPE_MOUSE:
+ case LIBINPUT_TABLET_TOOL_TYPE_LENS:
+ valuator = 3;
+ break;
+ default:
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Invalid rotation axis on tool\n");
+ return;
+ }
+
+ valuator_mask_set_double(mask, valuator, value);
+ }
+
+ xf86PostMotionEventM(dev, Absolute, mask);
+}
+
+static enum event_handling
+xf86libinput_handle_tablet_axis(InputInfoPtr pInfo,
+ struct libinput_event_tablet_tool *event)
+{
+ if (xf86libinput_tool_queue_event(event))
+ return EVENT_QUEUED;
+
+ xf86libinput_post_tablet_motion(pInfo, event);
+
+ return EVENT_HANDLED;
+}
+
+static inline const char *
+tool_type_to_str(enum libinput_tablet_tool_type type)
+{
+ const char *str;
+
+ switch (type) {
+ case LIBINPUT_TABLET_TOOL_TYPE_PEN: str = "Pen"; break;
+ case LIBINPUT_TABLET_TOOL_TYPE_BRUSH: str = "Brush"; break;
+ case LIBINPUT_TABLET_TOOL_TYPE_PENCIL: str = "Pencil"; break;
+ case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH: str = "Airbrush"; break;
+ case LIBINPUT_TABLET_TOOL_TYPE_ERASER: str = "Eraser"; break;
+ case LIBINPUT_TABLET_TOOL_TYPE_MOUSE: str = "Mouse"; break;
+ case LIBINPUT_TABLET_TOOL_TYPE_LENS: str = "Lens"; break;
+ default:
+ str = "unknown tool";
+ break;
+ }
+
+ return str;
+}
+
+static inline void
+xf86libinput_create_tool_subdevice(InputInfoPtr pInfo,
+ struct libinput_event_tablet_tool *event)
+{
+ struct xf86libinput *driver_data = pInfo->private;
+ struct xf86libinput_device *shared_device = driver_data->shared_device;
+ struct xf86libinput_tablet_tool *t;
+ struct xf86libinput_tablet_tool_event_queue *queue;
+ struct libinput_tablet_tool *tool;
+ enum libinput_tablet_tool_type tool_type;
+ uint64_t serial, tool_id;
+ XF86OptionPtr options = NULL;
+ char name[64];
+
+ t = calloc(1, sizeof *t);
+ if (!t)
+ return;
+
+ queue = calloc(1, sizeof(*queue));
+ if (!queue) {
+ free(t);
+ return;
+ }
+ queue->need_to_queue = true;
+ xorg_list_init(&queue->event_list);
+
+ tool = libinput_event_tablet_tool_get_tool(event);
+ serial = libinput_tablet_tool_get_serial(tool);
+ tool_id = libinput_tablet_tool_get_tool_id(tool);
+ tool_type = libinput_tablet_tool_get_type(tool);
+
+ t->tool = libinput_tablet_tool_ref(tool);
+ xorg_list_append(&t->node, &shared_device->unclaimed_tablet_tool_list);
+
+ options = xf86ReplaceIntOption(options, "_libinput/tablet-tool-serial", serial);
+ options = xf86ReplaceIntOption(options, "_libinput/tablet-tool-id", tool_id);
+ options = xf86ReplaceIntOption(options, "_libinput/tablet-tool-type", tool_type);
+ /* Convert the name to "<base name> <tool type> (serial number)" */
+ if (snprintf(name,
+ sizeof(name),
+ "%s %s (%#x)",
+ pInfo->name,
+ tool_type_to_str(libinput_tablet_tool_get_type(tool)),
+ (uint32_t)serial) > strlen(pInfo->name))
+ options = xf86ReplaceStrOption(options, "Name", name);
+
+ libinput_tablet_tool_set_user_data(tool, queue);
+ xf86libinput_tool_queue_event(event);
+
+ xf86libinput_create_subdevice(pInfo, CAP_TABLET_TOOL, options);
+}
+
+static inline DeviceIntPtr
+xf86libinput_find_device_for_tool(InputInfoPtr pInfo,
+ struct libinput_tablet_tool *tool)
+{
+ struct xf86libinput *dev;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct xf86libinput_device *shared_device = driver_data->shared_device;
+ uint64_t serial = libinput_tablet_tool_get_serial(tool);
+ uint64_t tool_id = libinput_tablet_tool_get_tool_id(tool);
+ enum libinput_tablet_tool_type tool_type = libinput_tablet_tool_get_type(tool);
+
+ xorg_list_for_each_entry(dev,
+ &shared_device->device_list,
+ shared_device_link) {
+ if (dev->tablet_tool &&
+ libinput_tablet_tool_get_serial(dev->tablet_tool) == serial &&
+ libinput_tablet_tool_get_tool_id(dev->tablet_tool) == tool_id &&
+ libinput_tablet_tool_get_type(dev->tablet_tool) == tool_type) {
+ return dev->pInfo->dev;
+ }
+ }
+
+ return NULL;
+}
+
+static enum event_handling
+xf86libinput_handle_tablet_proximity(InputInfoPtr pInfo,
+ struct libinput_event_tablet_tool *event)
+{
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_tablet_tool *tool;
+ DeviceIntPtr pDev;
+ ValuatorMask *mask = driver_data->valuators;
+ double x, y;
+ BOOL in_prox;
+
+ tool = libinput_event_tablet_tool_get_tool(event);
+ pDev = xf86libinput_find_device_for_tool(pInfo, tool);
+
+ in_prox = libinput_event_tablet_tool_get_proximity_state(event) ==
+ LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN;
+
+ if (pDev == NULL && in_prox) {
+ xf86libinput_create_tool_subdevice(pInfo, event);
+ return EVENT_QUEUED;
+ }
+
+ if (xf86libinput_tool_queue_event(event))
+ return EVENT_QUEUED;
+
+ BUG_RETURN_VAL(pDev == NULL, EVENT_HANDLED);
+
+ x = libinput_event_tablet_tool_get_x_transformed(event, TABLET_AXIS_MAX);
+ y = libinput_event_tablet_tool_get_y_transformed(event, TABLET_AXIS_MAX);
+ valuator_mask_set_double(mask, 0, x);
+ valuator_mask_set_double(mask, 1, y);
+
+ xf86PostProximityEventM(pDev, in_prox, mask);
+
+ /* We have to send an extra motion event after proximity to make
+ * sure the client got the updated x/y coordinates, especially if
+ * they don't handle proximity events (XI2).
+ */
+ if (in_prox)
+ xf86libinput_post_tablet_motion(pDev->public.devicePrivate, event);
+
+ return EVENT_HANDLED;
+}
+
+static void
+xf86libinput_handle_tablet_pad_button(InputInfoPtr pInfo,
+ struct libinput_event_tablet_pad *event)
+{
+ DeviceIntPtr dev = pInfo->dev;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_tablet_pad_mode_group *group;
+ int button, b;
+ int is_press;
+
+ if ((driver_data->capabilities & CAP_TABLET_PAD) == 0)
+ return;
+
+ b = libinput_event_tablet_pad_get_button_number(event);
+ button = 1 + b;
+ if (button > 3)
+ button += 4; /* offset by scroll buttons */
+ is_press = (libinput_event_tablet_pad_get_button_state(event) == LIBINPUT_BUTTON_STATE_PRESSED);
+
+ xf86PostButtonEvent(dev, Relative, button, is_press, 0, 0);
+
+ group = libinput_event_tablet_pad_get_mode_group(event);
+ if (libinput_tablet_pad_mode_group_button_is_toggle(group, b))
+ update_mode_prop(pInfo, event);
+}
+
+static void
+xf86libinput_handle_tablet_pad_strip(InputInfoPtr pInfo,
+ struct libinput_event_tablet_pad *event)
+{
+ DeviceIntPtr dev = pInfo->dev;
+ struct xf86libinput *driver_data = pInfo->private;
+ ValuatorMask *mask = driver_data->valuators;
+ double value;
+ int axis = 3;
+ int v;
+
+ if ((driver_data->capabilities & CAP_TABLET_PAD) == 0)
+ return;
+
+ /* this isn't compatible with the wacom driver which just forwards
+ * the values and lets the clients handle them with log2. */
+ axis += libinput_event_tablet_pad_get_strip_number(event);
+ value = libinput_event_tablet_pad_get_strip_position(event);
+ v = TABLET_STRIP_AXIS_MAX * value;
+
+ valuator_mask_zero(mask);
+ valuator_mask_set(mask, axis, v);
+
+ xf86PostMotionEventM(dev, Absolute, mask);
+}
+
+static void
+xf86libinput_handle_tablet_pad_ring(InputInfoPtr pInfo,
+ struct libinput_event_tablet_pad *event)
+{
+ DeviceIntPtr dev = pInfo->dev;
+ struct xf86libinput *driver_data = pInfo->private;
+ ValuatorMask *mask = driver_data->valuators;
+ double value;
+ int axis = 5;
+ int v;
+
+ if ((driver_data->capabilities & CAP_TABLET_PAD) == 0)
+ return;
+
+ axis += libinput_event_tablet_pad_get_ring_number(event);
+ value = libinput_event_tablet_pad_get_ring_position(event)/360.0;
+ v = TABLET_RING_AXIS_MAX * value;
+
+ valuator_mask_zero(mask);
+ valuator_mask_set(mask, axis, v);
+
+ xf86PostMotionEventM(dev, Absolute, mask);
+}
+
+static enum event_handling
+xf86libinput_handle_event(struct libinput_event *event)
+{
+ struct libinput_device *device;
+ enum libinput_event_type type;
+ InputInfoPtr pInfo;
+ struct xf86libinput *driver_data;
+ enum event_handling event_handling = EVENT_HANDLED;
+
+ type = libinput_event_get_type(event);
+ device = libinput_event_get_device(event);
+ pInfo = xf86libinput_pick_device(libinput_device_get_user_data(device),
+ event);
+
+ if (!pInfo || !pInfo->dev->public.on)
+ goto out;
+
+ driver_data = pInfo->private;
+
+ switch (type) {
+ case LIBINPUT_EVENT_NONE:
+ case LIBINPUT_EVENT_DEVICE_ADDED:
+ case LIBINPUT_EVENT_DEVICE_REMOVED:
+ break;
+ case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
+ xf86libinput_handle_absmotion(pInfo,
+ libinput_event_get_pointer_event(event));
+ break;
+
+ case LIBINPUT_EVENT_POINTER_MOTION:
+ xf86libinput_handle_motion(pInfo,
+ libinput_event_get_pointer_event(event));
+ break;
+ case LIBINPUT_EVENT_POINTER_BUTTON:
+ xf86libinput_handle_button(pInfo,
+ libinput_event_get_pointer_event(event));
+ break;
+ case LIBINPUT_EVENT_KEYBOARD_KEY:
+ xf86libinput_handle_key(pInfo,
+ libinput_event_get_keyboard_event(event));
+ break;
+ case LIBINPUT_EVENT_POINTER_AXIS:
+#if HAVE_LIBINPUT_AXIS_VALUE_V120
+ /* ignore POINTER_AXIS where we have libinput 1.19 and
+ higher and high-resolution scroll is enabled */
+ if (driver_data->options.hires_scrolling_enabled)
+ break;
+#endif
+
+ xf86libinput_handle_axis(pInfo,
+ event,
+ libinput_event_pointer_get_axis_source(
+ libinput_event_get_pointer_event(event)
+ ));
+ break;
+#if HAVE_LIBINPUT_AXIS_VALUE_V120
+ case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL:
+ if (driver_data->options.hires_scrolling_enabled) {
+ xf86libinput_handle_axis(pInfo,
+ event,
+ LIBINPUT_POINTER_AXIS_SOURCE_WHEEL);
+ }
+ break;
+ case LIBINPUT_EVENT_POINTER_SCROLL_FINGER:
+ if (driver_data->options.hires_scrolling_enabled) {
+ xf86libinput_handle_axis(pInfo,
+ event,
+ LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
+ }
+ break;
+ case LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS:
+ if (driver_data->options.hires_scrolling_enabled) {
+ xf86libinput_handle_axis(pInfo,
+ event,
+ LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS);
+ }
+ break;
+#endif
+ case LIBINPUT_EVENT_TOUCH_FRAME:
+ break;
+ case LIBINPUT_EVENT_TOUCH_UP:
+ case LIBINPUT_EVENT_TOUCH_DOWN:
+ case LIBINPUT_EVENT_TOUCH_MOTION:
+ case LIBINPUT_EVENT_TOUCH_CANCEL:
+ xf86libinput_handle_touch(pInfo,
+ libinput_event_get_touch_event(event),
+ libinput_event_get_type(event));
+ break;
+ case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
+ case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
+ case LIBINPUT_EVENT_GESTURE_SWIPE_END:
+#ifdef HAVE_GESTURES
+ xf86libinput_handle_gesture_swipe(pInfo,
+ libinput_event_get_gesture_event(event),
+ type);
+#endif
+ break;
+ case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
+ case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
+ case LIBINPUT_EVENT_GESTURE_PINCH_END:
+#ifdef HAVE_GESTURES
+ xf86libinput_handle_gesture_pinch(pInfo,
+ libinput_event_get_gesture_event(event),
+ type);
+#endif
+ break;
+ case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
+ event_handling = xf86libinput_handle_tablet_axis(pInfo,
+ libinput_event_get_tablet_tool_event(event));
+ break;
+ case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
+ event_handling = xf86libinput_handle_tablet_button(pInfo,
+ libinput_event_get_tablet_tool_event(event));
+ break;
+ case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
+ event_handling = xf86libinput_handle_tablet_proximity(pInfo,
+ libinput_event_get_tablet_tool_event(event));
+ break;
+ case LIBINPUT_EVENT_TABLET_TOOL_TIP:
+ event_handling = xf86libinput_handle_tablet_tip(pInfo,
+ libinput_event_get_tablet_tool_event(event));
+ break;
+ case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
+ xf86libinput_handle_tablet_pad_button(pInfo,
+ libinput_event_get_tablet_pad_event(event));
+ break;
+ case LIBINPUT_EVENT_TABLET_PAD_RING:
+ xf86libinput_handle_tablet_pad_ring(pInfo,
+ libinput_event_get_tablet_pad_event(event));
+ break;
+ case LIBINPUT_EVENT_TABLET_PAD_STRIP:
+ xf86libinput_handle_tablet_pad_strip(pInfo,
+ libinput_event_get_tablet_pad_event(event));
+ break;
+ case LIBINPUT_EVENT_SWITCH_TOGGLE:
+ break;
+ }
+
+out:
+ return event_handling;
+}
+
+static void
+xf86libinput_read_input(InputInfoPtr pInfo)
+{
+ struct libinput *libinput = driver_context.libinput;
+ int rc;
+ struct libinput_event *event;
+
+ rc = libinput_dispatch(libinput);
+ if (rc == -EAGAIN)
+ return;
+
+ if (rc < 0) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Error reading events: %s\n",
+ strerror(-rc));
+ return;
+ }
+
+ while ((event = libinput_get_event(libinput))) {
+ if (xf86libinput_handle_event(event) == EVENT_HANDLED)
+ libinput_event_destroy(event);
+ }
+}
+
+/*
+ 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)
+{
+ InputInfoPtr pInfo;
+ int fd = -1;
+
+ /* Special handling for sysfs files (used for pad LEDs) */
+ if (strneq(path, "/sys/", 5)) {
+ fd = open(path, flags);
+ return fd < 0 ? -errno : fd;
+ }
+
+ nt_list_for_each_entry(pInfo, xf86FirstLocalDevice(), next) {
+ char *device = xf86CheckStrOption(pInfo->options, "Device", NULL);
+
+ if (device != NULL && streq(path, device)) {
+ free(device);
+ break;
+ }
+ free(device);
+ }
+
+ if (pInfo == NULL) {
+ xf86Msg(X_ERROR, "Failed to look up path '%s'\n", path);
+ return -ENODEV;
+ }
+
+ fd = xf86OpenSerial(pInfo->options);
+ if (fd < 0)
+ return -errno;
+
+ xf86FlushInput(fd);
+
+ return fd;
+}
+
+static void
+close_restricted(int fd, void *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 (server_fd == fd) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found)
+ xf86CloseSerial(fd);
+}
+
+const struct libinput_interface interface = {
+ .open_restricted = open_restricted,
+ .close_restricted = close_restricted,
+};
+
+_X_ATTRIBUTE_PRINTF(3, 0)
+static void
+xf86libinput_log_handler(struct libinput *libinput,
+ enum libinput_log_priority priority,
+ const char *format,
+ va_list args)
+{
+ MessageType type;
+ int verbosity;
+
+ switch(priority) {
+ case LIBINPUT_LOG_PRIORITY_DEBUG:
+ type = X_DEBUG;
+ verbosity = 10;
+ break;
+ case LIBINPUT_LOG_PRIORITY_ERROR:
+ type = X_ERROR;
+ verbosity = -1;
+ break;
+ case LIBINPUT_LOG_PRIORITY_INFO:
+ type = X_INFO;
+ verbosity = 3;
+ break;
+ default:
+ return;
+ }
+
+ /* log messages in libinput are per-context, not per device, so we
+ can't use xf86IDrvMsg here, and the server has no xf86VMsg or
+ similar */
+ LogVMessageVerb(type, verbosity, format, args);
+}
+
+static inline BOOL
+xf86libinput_parse_tap_option(InputInfoPtr pInfo,
+ struct libinput_device *device)
+{
+ BOOL tap;
+
+ if (libinput_device_config_tap_get_finger_count(device) == 0)
+ return FALSE;
+
+ tap = xf86SetBoolOption(pInfo->options,
+ "Tapping",
+ libinput_device_config_tap_get_enabled(device));
+
+ if (libinput_device_config_tap_set_enabled(device, tap) !=
+ LIBINPUT_CONFIG_STATUS_SUCCESS) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set Tapping to %d\n",
+ tap);
+ tap = libinput_device_config_tap_get_enabled(device);
+ }
+
+ return tap;
+}
+
+static inline BOOL
+xf86libinput_parse_tap_drag_option(InputInfoPtr pInfo,
+ struct libinput_device *device)
+{
+ BOOL drag;
+
+ if (libinput_device_config_tap_get_finger_count(device) == 0)
+ return FALSE;
+
+ drag = xf86SetBoolOption(pInfo->options,
+ "TappingDrag",
+ libinput_device_config_tap_get_drag_enabled(device));
+
+ if (libinput_device_config_tap_set_drag_enabled(device, drag) !=
+ LIBINPUT_CONFIG_STATUS_SUCCESS) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set Tapping Drag Lock to %d\n",
+ drag);
+ drag = libinput_device_config_tap_get_drag_enabled(device);
+ }
+
+ return drag;
+}
+
+static inline BOOL
+xf86libinput_parse_tap_drag_lock_option(InputInfoPtr pInfo,
+ struct libinput_device *device)
+{
+ BOOL drag_lock;
+
+ if (libinput_device_config_tap_get_finger_count(device) == 0)
+ return FALSE;
+
+ drag_lock = xf86SetBoolOption(pInfo->options,
+ "TappingDragLock",
+ libinput_device_config_tap_get_drag_lock_enabled(device));
+
+ if (libinput_device_config_tap_set_drag_lock_enabled(device, drag_lock) !=
+ LIBINPUT_CONFIG_STATUS_SUCCESS) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set Tapping Drag Lock to %d\n",
+ drag_lock);
+ drag_lock = libinput_device_config_tap_get_drag_lock_enabled(device);
+ }
+
+ return drag_lock;
+}
+
+static inline enum libinput_config_tap_button_map
+xf86libinput_parse_tap_buttonmap_option(InputInfoPtr pInfo,
+ struct libinput_device *device)
+{
+ enum libinput_config_tap_button_map map;
+ char *str;
+
+ if (libinput_device_config_tap_get_finger_count(device) == 0)
+ return FALSE;
+
+ map = libinput_device_config_tap_get_button_map(device);
+ str = xf86SetStrOption(pInfo->options,
+ "TappingButtonMap",
+ NULL);
+ if (str) {
+ if (streq(str, "lmr"))
+ map = LIBINPUT_CONFIG_TAP_MAP_LMR;
+ else if (streq(str, "lrm"))
+ map = LIBINPUT_CONFIG_TAP_MAP_LRM;
+ else
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Invalid TapButtonMap: %s\n",
+ str);
+ free(str);
+ }
+
+ if (libinput_device_config_tap_set_button_map(device, map) !=
+ LIBINPUT_CONFIG_STATUS_SUCCESS) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set Tapping Button Map to %d\n",
+ map);
+ map = libinput_device_config_tap_get_button_map(device);
+ }
+
+ return map;
+}
+
+static inline double
+xf86libinput_parse_accel_option(InputInfoPtr pInfo,
+ struct libinput_device *device)
+{
+ double speed;
+
+ if (!libinput_device_config_accel_is_available(device))
+ return 0.0;
+
+ speed = xf86SetRealOption(pInfo->options,
+ "AccelSpeed",
+ libinput_device_config_accel_get_speed(device));
+ if (libinput_device_config_accel_set_speed(device, speed) !=
+ LIBINPUT_CONFIG_STATUS_SUCCESS) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Invalid speed %.2f, using 0 instead\n",
+ speed);
+ speed = libinput_device_config_accel_get_speed(device);
+ }
+
+ return speed;
+}
+
+static inline enum libinput_config_accel_profile
+xf86libinput_parse_accel_profile_option(InputInfoPtr pInfo,
+ struct libinput_device *device)
+{
+ enum libinput_config_accel_profile profile;
+ char *str;
+
+ if (libinput_device_config_accel_get_profiles(device) ==
+ LIBINPUT_CONFIG_ACCEL_PROFILE_NONE)
+ return LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
+
+ str = xf86SetStrOption(pInfo->options, "AccelProfile", NULL);
+ if (!str)
+ profile = libinput_device_config_accel_get_profile(device);
+ else if (strncasecmp(str, "adaptive", 9) == 0)
+ profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
+ else if (strncasecmp(str, "flat", 4) == 0)
+ profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT;
+ else {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Unknown accel profile '%s'. Using default.\n",
+ str);
+ profile = libinput_device_config_accel_get_profile(device);
+ }
+
+ free(str);
+
+ return profile;
+}
+
+static inline BOOL
+xf86libinput_parse_natscroll_option(InputInfoPtr pInfo,
+ struct libinput_device *device)
+{
+ BOOL natural_scroll;
+
+ if (!libinput_device_config_scroll_has_natural_scroll(device))
+ return FALSE;
+
+ natural_scroll = xf86SetBoolOption(pInfo->options,
+ "NaturalScrolling",
+ libinput_device_config_scroll_get_natural_scroll_enabled(device));
+ if (libinput_device_config_scroll_set_natural_scroll_enabled(device,
+ natural_scroll) !=
+ LIBINPUT_CONFIG_STATUS_SUCCESS) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set NaturalScrolling to %d\n",
+ natural_scroll);
+
+ natural_scroll = libinput_device_config_scroll_get_natural_scroll_enabled(device);
+ }
+
+ return natural_scroll;
+}
+
+static inline enum libinput_config_send_events_mode
+xf86libinput_parse_sendevents_option(InputInfoPtr pInfo,
+ struct libinput_device *device)
+{
+ char *modestr;
+ enum libinput_config_send_events_mode mode;
+
+ if (libinput_device_config_send_events_get_modes(device) == LIBINPUT_CONFIG_SEND_EVENTS_ENABLED)
+ return LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
+
+ mode = libinput_device_config_send_events_get_mode(device);
+ modestr = xf86SetStrOption(pInfo->options,
+ "SendEventsMode",
+ NULL);
+ if (modestr) {
+ if (streq(modestr, "enabled"))
+ mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
+ else if (streq(modestr, "disabled"))
+ mode = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
+ else if (streq(modestr, "disabled-on-external-mouse"))
+ mode = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
+ else
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Invalid SendeventsMode: %s\n",
+ modestr);
+ free(modestr);
+ }
+
+ if (libinput_device_config_send_events_set_mode(device, mode) !=
+ LIBINPUT_CONFIG_STATUS_SUCCESS) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set SendEventsMode %u\n", mode);
+ mode = libinput_device_config_send_events_get_mode(device);
+ }
+
+ return mode;
+}
+
+static inline void
+xf86libinput_parse_calibration_option(InputInfoPtr pInfo,
+ struct libinput_device *device,
+ float matrix_out[9])
+{
+ char *str;
+ float matrix[9] = { 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0};
+ int num_calibration;
+
+ memcpy(matrix_out, matrix, sizeof(matrix));
+
+ if (!libinput_device_config_calibration_has_matrix(device))
+ return;
+
+ libinput_device_config_calibration_get_matrix(device, matrix);
+ memcpy(matrix_out, matrix, sizeof(matrix));
+
+ str = xf86SetStrOption(pInfo->options,
+ "CalibrationMatrix",
+ NULL);
+ if (!str)
+ return;
+
+ num_calibration = sscanf(str, "%f %f %f %f %f %f %f %f %f ",
+ &matrix[0], &matrix[1],
+ &matrix[2], &matrix[3],
+ &matrix[4], &matrix[5],
+ &matrix[6], &matrix[7],
+ &matrix[8]);
+ if (num_calibration != 9) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Invalid matrix: %s, using default\n", str);
+ } else if (libinput_device_config_calibration_set_matrix(device,
+ matrix) ==
+ LIBINPUT_CONFIG_STATUS_SUCCESS) {
+ memcpy(matrix_out, matrix, sizeof(matrix));
+ } else
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to apply matrix: %s, using default\n", str);
+ free(str);
+}
+
+static inline BOOL
+xf86libinput_parse_lefthanded_option(InputInfoPtr pInfo,
+ struct libinput_device *device)
+{
+ BOOL left_handed;
+
+ if (!libinput_device_config_left_handed_is_available(device))
+ return FALSE;
+
+ left_handed = xf86SetBoolOption(pInfo->options,
+ "LeftHanded",
+ libinput_device_config_left_handed_get(device));
+ if (libinput_device_config_left_handed_set(device,
+ left_handed) !=
+ LIBINPUT_CONFIG_STATUS_SUCCESS) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set LeftHanded to %d\n",
+ left_handed);
+ left_handed = libinput_device_config_left_handed_get(device);
+ }
+
+ return left_handed;
+}
+
+static inline enum libinput_config_scroll_method
+xf86libinput_parse_scroll_option(InputInfoPtr pInfo,
+ struct libinput_device *device)
+{
+ uint32_t scroll_methods;
+ enum libinput_config_scroll_method m;
+ char *method;
+
+ scroll_methods = libinput_device_config_scroll_get_methods(device);
+ if (scroll_methods == LIBINPUT_CONFIG_SCROLL_NO_SCROLL)
+ return LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
+
+ method = xf86SetStrOption(pInfo->options, "ScrollMethod", NULL);
+ if (!method)
+ m = libinput_device_config_scroll_get_method(device);
+ else if (strncasecmp(method, "twofinger", 9) == 0)
+ m = LIBINPUT_CONFIG_SCROLL_2FG;
+ else if (strncasecmp(method, "edge", 4) == 0)
+ m = LIBINPUT_CONFIG_SCROLL_EDGE;
+ else if (strncasecmp(method, "button", 6) == 0)
+ m = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
+ else if (strncasecmp(method, "none", 4) == 0)
+ m = LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
+ else {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Unknown scroll method '%s'. Using default.\n",
+ method);
+ m = libinput_device_config_scroll_get_method(device);
+ }
+
+ free(method);
+ return m;
+}
+
+static inline unsigned int
+xf86libinput_parse_scrollbutton_option(InputInfoPtr pInfo,
+ struct libinput_device *device)
+{
+ unsigned int b;
+ CARD32 scroll_button;
+
+ if ((libinput_device_config_scroll_get_methods(device) &
+ LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) == 0)
+ return 0;
+
+ b = btn_linux2xorg(libinput_device_config_scroll_get_button(device));
+ scroll_button = xf86SetIntOption(pInfo->options,
+ "ScrollButton",
+ b);
+
+ b = btn_xorg2linux(scroll_button);
+
+ if (libinput_device_config_scroll_set_button(device,
+ b) != LIBINPUT_CONFIG_STATUS_SUCCESS) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set ScrollButton to %u\n",
+ scroll_button);
+ scroll_button = btn_linux2xorg(libinput_device_config_scroll_get_button(device));
+ }
+ return scroll_button;
+}
+
+static inline BOOL
+xf86libinput_parse_scrollbuttonlock_option(InputInfoPtr pInfo,
+ struct libinput_device *device)
+{
+ bool dflt;
+ BOOL buttonlock = FALSE;
+
+ if ((libinput_device_config_scroll_get_methods(device) &
+ LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) == 0)
+ return 0;
+
+#if HAVE_LIBINPUT_SCROLL_BUTTON_LOCK
+ dflt = libinput_device_config_scroll_get_default_button_lock(device);
+ buttonlock = xf86SetBoolOption(pInfo->options, "ScrollButtonLock", dflt);
+
+ if (libinput_device_config_scroll_set_button_lock(device, buttonlock)
+ != LIBINPUT_CONFIG_STATUS_SUCCESS) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to %s ScrollButtonLock\n",
+ buttonlock ? "enable" : "disable");
+ buttonlock = libinput_device_config_scroll_get_button_lock(device);
+ }
+#endif
+ return buttonlock;
+}
+
+static inline bool
+xf86libinput_want_scroll_distance_option(struct libinput_device *device)
+{
+ uint32_t methods =
+ LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN |
+ LIBINPUT_CONFIG_SCROLL_2FG |
+ LIBINPUT_CONFIG_SCROLL_EDGE;
+
+ if ((libinput_device_config_scroll_get_methods(device) & methods) == 0)
+ return false;
+
+ return true;
+}
+
+static inline uint32_t
+xf86libinput_parse_scroll_pixel_distance_option(InputInfoPtr pInfo,
+ struct libinput_device *device)
+{
+ uint32_t dflt = SCROLL_INCREMENT;
+ uint32_t dist;
+
+ if (!xf86libinput_want_scroll_distance_option(device))
+ return dflt;
+
+ dist = xf86SetIntOption(pInfo->options, "ScrollPixelDistance", dflt);
+ if (dist < TOUCHPAD_SCROLL_DIST_MIN || dist > TOUCHPAD_SCROLL_DIST_MAX) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Invalid ScrollPixelDistance %d\n", dist);
+ dist = dflt;
+ }
+ return dist;
+}
+
+static inline unsigned int
+xf86libinput_parse_clickmethod_option(InputInfoPtr pInfo,
+ struct libinput_device *device)
+{
+ uint32_t click_methods = libinput_device_config_click_get_methods(device);
+ enum libinput_config_click_method m;
+ char *method;
+
+ if (click_methods == LIBINPUT_CONFIG_CLICK_METHOD_NONE)
+ return LIBINPUT_CONFIG_CLICK_METHOD_NONE;
+
+ method = xf86SetStrOption(pInfo->options, "ClickMethod", NULL);
+
+ if (!method)
+ m = libinput_device_config_click_get_method(device);
+ else if (strncasecmp(method, "buttonareas", 11) == 0)
+ m = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
+ else if (strncasecmp(method, "clickfinger", 11) == 0)
+ m = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
+ else if (strncasecmp(method, "none", 4) == 0)
+ m = LIBINPUT_CONFIG_CLICK_METHOD_NONE;
+ else {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Unknown click method '%s'. Using default.\n",
+ method);
+ m = libinput_device_config_click_get_method(device);
+ }
+ free(method);
+
+ return m;
+}
+
+static inline BOOL
+xf86libinput_parse_middleemulation_option(InputInfoPtr pInfo,
+ struct libinput_device *device)
+{
+ BOOL enabled;
+
+ if (!libinput_device_config_middle_emulation_is_available(device))
+ return FALSE;
+
+ enabled = xf86SetBoolOption(pInfo->options,
+ "MiddleEmulation",
+ libinput_device_config_middle_emulation_get_default_enabled(device));
+ if (libinput_device_config_middle_emulation_set_enabled(device, enabled) !=
+ LIBINPUT_CONFIG_STATUS_SUCCESS) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set MiddleEmulation to %d\n",
+ enabled);
+ enabled = libinput_device_config_middle_emulation_get_enabled(device);
+ }
+
+ return enabled;
+}
+
+static inline BOOL
+xf86libinput_parse_disablewhiletyping_option(InputInfoPtr pInfo,
+ struct libinput_device *device)
+{
+ BOOL enabled;
+
+ if (!libinput_device_config_dwt_is_available(device))
+ return FALSE;
+
+ enabled = xf86SetBoolOption(pInfo->options,
+ "DisableWhileTyping",
+ libinput_device_config_dwt_get_default_enabled(device));
+ if (libinput_device_config_dwt_set_enabled(device, enabled) !=
+ LIBINPUT_CONFIG_STATUS_SUCCESS) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Failed to set DisableWhileTyping to %d\n",
+ enabled);
+ enabled = libinput_device_config_dwt_get_enabled(device);
+ }
+
+ return enabled;
+}
+
+static void
+xf86libinput_parse_buttonmap_option(InputInfoPtr pInfo,
+ unsigned char *btnmap,
+ size_t size)
+{
+ const int MAXBUTTONS = 32;
+ char *mapping, *map, *s = NULL;
+ int idx = 1;
+
+ init_button_map(btnmap, size);
+
+ mapping = xf86SetStrOption(pInfo->options, "ButtonMapping", NULL);
+ if (!mapping)
+ return;
+
+ map = mapping;
+ do
+ {
+ unsigned long int btn = strtoul(map, &s, 10);
+
+ if (s == map || btn > MAXBUTTONS)
+ {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "... Invalid button mapping. Using defaults\n");
+ init_button_map(btnmap, size);
+ break;
+ }
+
+ btnmap[idx++] = btn;
+ map = s;
+ } while (s && *s != '\0' && idx < MAXBUTTONS);
+
+ free(mapping);
+}
+
+static inline void
+xf86libinput_parse_draglock_option(InputInfoPtr pInfo,
+ struct xf86libinput *driver_data)
+{
+ char *str;
+
+ str = xf86SetStrOption(pInfo->options, "DragLockButtons", NULL);
+ if (draglock_init_from_string(&driver_data->draglock, str) != 0)
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Invalid DragLockButtons option: \"%s\"\n",
+ str);
+ free(str);
+}
+
+static inline BOOL
+xf86libinput_parse_horiz_scroll_option(InputInfoPtr pInfo)
+{
+ return xf86SetBoolOption(pInfo->options, "HorizontalScrolling", TRUE);
+}
+
+static inline BOOL
+xf86libinput_parse_hirescroll_option(InputInfoPtr pInfo,
+ struct libinput_device *device)
+{
+ return xf86SetBoolOption(pInfo->options,
+ "HighResolutionWheelScrolling",
+ TRUE);
+}
+
+static inline double
+xf86libinput_parse_rotation_angle_option(InputInfoPtr pInfo,
+ struct libinput_device *device)
+{
+ double angle;
+
+ if (!libinput_device_config_rotation_is_available(device))
+ return 0.0;
+
+ angle = xf86SetRealOption(pInfo->options,
+ "RotationAngle",
+ libinput_device_config_rotation_get_default_angle(device));
+ if (libinput_device_config_rotation_set_angle(device, angle) !=
+ LIBINPUT_CONFIG_STATUS_SUCCESS) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Invalid angle %.2f, using 0.0 instead\n",
+ angle);
+ angle = libinput_device_config_rotation_get_angle(device);
+ }
+
+ return angle;
+}
+
+static void
+xf86libinput_parse_pressurecurve_option(InputInfoPtr pInfo,
+ struct xf86libinput *driver_data,
+ struct bezier_control_point pcurve[4])
+{
+ struct bezier_control_point controls[4] = {
+ { 0.0, 0.0 },
+ { 0.0, 0.0 },
+ { 1.0, 1.0 },
+ { 1.0, 1.0 },
+ };
+ float points[8];
+ char *str;
+ int rc = 0;
+ int test_bezier[64];
+ struct libinput_tablet_tool *tool = driver_data->tablet_tool;
+
+ if ((driver_data->capabilities & CAP_TABLET_TOOL) == 0)
+ return;
+
+ if (!tool || !libinput_tablet_tool_has_pressure(tool))
+ return;
+
+ str = xf86SetStrOption(pInfo->options,
+ "TabletToolPressureCurve",
+ NULL);
+ if (!str)
+ goto out;
+
+ rc = sscanf(str, "%f/%f %f/%f %f/%f %f/%f",
+ &points[0], &points[1], &points[2], &points[3],
+ &points[4], &points[5], &points[6], &points[7]);
+ if (rc != 8)
+ goto out;
+
+ for (int i = 0; i < 4; i++) {
+ if (points[i] < 0.0 || points[i] > 1.0)
+ goto out;
+ }
+
+ controls[0].x = points[0];
+ controls[0].y = points[1];
+ controls[1].x = points[2];
+ controls[1].y = points[3];
+ controls[2].x = points[4];
+ controls[2].y = points[5];
+ controls[3].x = points[6];
+ controls[3].y = points[7];
+
+ if (!cubic_bezier(controls, test_bezier, ARRAY_SIZE(test_bezier))) {
+ memcpy(controls, bezier_defaults, sizeof(controls));
+ goto out;
+ }
+
+ rc = 0;
+out:
+ if (rc != 0)
+ xf86IDrvMsg(pInfo, X_ERROR, "Invalid pressure curve: %s\n", str);
+ free(str);
+ memcpy(pcurve, controls, sizeof(controls));
+ xf86libinput_set_pressurecurve(driver_data, controls);
+}
+
+static inline bool
+want_area_handling(struct xf86libinput *driver_data)
+{
+ struct libinput_device *device = driver_data->shared_device->device;
+
+ if ((driver_data->capabilities & CAP_TABLET_TOOL) == 0)
+ return false;
+
+ /* If we have a calibration matrix, it's a built-in tablet and we
+ * don't need to set the area ratio on those */
+ return !libinput_device_config_calibration_has_matrix(device);
+}
+
+static void
+xf86libinput_parse_tablet_area_option(InputInfoPtr pInfo,
+ struct xf86libinput *driver_data,
+ struct ratio *area_out)
+{
+ char *str;
+ int rc;
+ struct ratio area;
+
+ if (!want_area_handling(driver_data))
+ return;
+
+ str = xf86SetStrOption(pInfo->options,
+ "TabletToolAreaRatio",
+ NULL);
+ if (!str || streq(str, "default"))
+ goto out;
+
+ rc = sscanf(str, "%d:%d", &area.x, &area.y);
+ if (rc != 2 || area.x <= 0 || area.y <= 0) {
+ xf86IDrvMsg(pInfo, X_ERROR, "Invalid tablet tool area ratio: %s\n", str);
+ } else {
+ *area_out = area;
+ }
+
+out:
+ free(str);
+}
+
+static void
+xf86libinput_parse_options(InputInfoPtr pInfo,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ struct options *options = &driver_data->options;
+
+ /* libinput options */
+ options->tapping = xf86libinput_parse_tap_option(pInfo, device);
+ options->tap_drag = xf86libinput_parse_tap_drag_option(pInfo, device);
+ options->tap_drag_lock = xf86libinput_parse_tap_drag_lock_option(pInfo, device);
+ options->tap_button_map = xf86libinput_parse_tap_buttonmap_option(pInfo, device);
+ options->speed = xf86libinput_parse_accel_option(pInfo, device);
+ options->accel_profile = xf86libinput_parse_accel_profile_option(pInfo, device);
+ options->natural_scrolling = xf86libinput_parse_natscroll_option(pInfo, device);
+ options->sendevents = xf86libinput_parse_sendevents_option(pInfo, device);
+ options->left_handed = xf86libinput_parse_lefthanded_option(pInfo, device);
+ options->scroll_method = xf86libinput_parse_scroll_option(pInfo, device);
+ options->scroll_button = xf86libinput_parse_scrollbutton_option(pInfo, device);
+ options->scroll_buttonlock = xf86libinput_parse_scrollbuttonlock_option(pInfo, device);
+ options->scroll_pixel_distance = xf86libinput_parse_scroll_pixel_distance_option(pInfo, device);
+ options->click_method = xf86libinput_parse_clickmethod_option(pInfo, device);
+ options->middle_emulation = xf86libinput_parse_middleemulation_option(pInfo, device);
+ options->disable_while_typing = xf86libinput_parse_disablewhiletyping_option(pInfo, device);
+ options->rotation_angle = xf86libinput_parse_rotation_angle_option(pInfo, device);
+ xf86libinput_parse_calibration_option(pInfo, device, driver_data->options.matrix);
+
+ /* non-libinput options */
+ xf86libinput_parse_buttonmap_option(pInfo,
+ options->btnmap,
+ sizeof(options->btnmap));
+ if (driver_data->capabilities & CAP_POINTER) {
+ xf86libinput_parse_draglock_option(pInfo, driver_data);
+ options->horiz_scrolling_enabled = xf86libinput_parse_horiz_scroll_option(pInfo);
+ options->hires_scrolling_enabled = xf86libinput_parse_hirescroll_option(pInfo, device);
+ }
+
+ xf86libinput_parse_pressurecurve_option(pInfo,
+ driver_data,
+ options->pressurecurve);
+ xf86libinput_parse_tablet_area_option(pInfo,
+ driver_data,
+ &options->area);
+}
+
+static const char*
+xf86libinput_get_type_name(struct libinput_device *device,
+ struct xf86libinput *driver_data)
+{
+ const char *type_name;
+
+ /* now pick an actual type */
+ if (libinput_device_config_tap_get_finger_count(device) > 0)
+ type_name = XI_TOUCHPAD;
+ else if (driver_data->capabilities & CAP_TOUCH)
+ type_name = XI_TOUCHSCREEN;
+ else if (driver_data->capabilities & CAP_POINTER)
+ type_name = XI_MOUSE;
+ else if (driver_data->capabilities & CAP_TABLET)
+ type_name = XI_TABLET;
+ else if (driver_data->capabilities & CAP_TABLET_PAD)
+ type_name = "PAD";
+ else if (driver_data->capabilities & CAP_TABLET_TOOL){
+ switch (libinput_tablet_tool_get_type(driver_data->tablet_tool)) {
+ case LIBINPUT_TABLET_TOOL_TYPE_PEN:
+ case LIBINPUT_TABLET_TOOL_TYPE_BRUSH:
+ case LIBINPUT_TABLET_TOOL_TYPE_PENCIL:
+ case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH:
+ type_name = "STYLUS";
+ break;
+ case LIBINPUT_TABLET_TOOL_TYPE_ERASER:
+ type_name = "ERASER";
+ break;
+ case LIBINPUT_TABLET_TOOL_TYPE_MOUSE:
+ case LIBINPUT_TABLET_TOOL_TYPE_LENS:
+ type_name = "CURSOR";
+ break;
+ default:
+ type_name = XI_TABLET;
+ break;
+ }
+ } else
+ type_name = XI_KEYBOARD;
+
+ return type_name;
+}
+
+static void
+xf86libinput_init_driver_context(void)
+{
+ if (!driver_context.libinput) {
+ driver_context.libinput = libinput_path_create_context(&interface, &driver_context);
+ libinput_log_set_handler(driver_context.libinput,
+ xf86libinput_log_handler);
+ /* we want all msgs, let the server filter */
+ libinput_log_set_priority(driver_context.libinput,
+ LIBINPUT_LOG_PRIORITY_DEBUG);
+ } else {
+ libinput_ref(driver_context.libinput);
+ }
+}
+
+struct xf86libinput_hotplug_info {
+ InputAttributes *attrs;
+ InputOption *input_options;
+};
+
+static DeviceIntPtr
+xf86libinput_hotplug_device(struct xf86libinput_hotplug_info *hotplug)
+{
+ DeviceIntPtr dev;
+
+ input_lock();
+ if (NewInputDeviceRequest(hotplug->input_options,
+ hotplug->attrs,
+ &dev) != Success)
+ dev = NULL;
+ input_unlock();
+
+ input_option_free_list(&hotplug->input_options);
+ FreeInputAttributes(hotplug->attrs);
+ free(hotplug);
+
+ return dev;
+}
+
+static Bool
+xf86libinput_hotplug_device_cb(ClientPtr client, pointer closure)
+{
+ struct xf86libinput_hotplug_info *hotplug = closure;
+
+ xf86libinput_hotplug_device(hotplug);
+
+ return TRUE;
+}
+
+static void
+xf86libinput_create_subdevice(InputInfoPtr pInfo,
+ uint32_t capabilities,
+ XF86OptionPtr extra_options)
+{
+ struct xf86libinput *driver_data = pInfo->private;
+ struct xf86libinput_device *shared_device;
+ struct xf86libinput_hotplug_info *hotplug;
+ InputOption *iopts = NULL;
+ XF86OptionPtr options, o;
+
+ shared_device = driver_data->shared_device;
+ pInfo->options = xf86ReplaceIntOption(pInfo->options,
+ "_libinput/shared-device",
+ shared_device->id);
+
+ options = xf86OptionListDuplicate(pInfo->options);
+ options = xf86ReplaceStrOption(options, "_source", "_driver/libinput");
+ options = xf86OptionListMerge(options, extra_options);
+
+ if (capabilities & CAP_KEYBOARD)
+ options = xf86ReplaceBoolOption(options, "_libinput/cap-keyboard", 1);
+ if (capabilities & CAP_POINTER)
+ options = xf86ReplaceBoolOption(options, "_libinput/cap-pointer", 1);
+ if (capabilities & CAP_TOUCH)
+ options = xf86ReplaceBoolOption(options, "_libinput/cap-touch", 1);
+#ifdef HAVE_GESTURES
+ if (capabilities & CAP_GESTURE)
+ options = xf86ReplaceBoolOption(options, "_libinput/cap-gesture", 1);
+#endif
+ if (capabilities & CAP_TABLET_TOOL)
+ options = xf86ReplaceBoolOption(options, "_libinput/cap-tablet-tool", 1);
+ if (capabilities & CAP_TABLET_PAD)
+ options = xf86ReplaceBoolOption(options, "_libinput/cap-tablet-pad", 1);
+
+ /* need convert from one option list to the other. woohoo. */
+ o = options;
+ while (o) {
+ iopts = input_option_new(iopts,
+ xf86OptionName(o),
+ xf86OptionValue(o));
+ o = xf86NextOption(o);
+ }
+ xf86OptionListFree(options);
+
+ hotplug = calloc(1, sizeof(*hotplug));
+ if (!hotplug)
+ return;
+
+ hotplug->input_options = iopts;
+ hotplug->attrs = DuplicateInputAttributes(pInfo->attrs);
+
+ xf86IDrvMsg(pInfo, X_INFO, "needs a virtual subdevice\n");
+
+ QueueWorkProc(xf86libinput_hotplug_device_cb, serverClient, hotplug);
+}
+
+static inline uint32_t
+caps_from_options(InputInfoPtr pInfo)
+{
+ uint32_t capabilities = 0;
+
+ if (xf86CheckBoolOption(pInfo->options, "_libinput/cap-keyboard", 0))
+ capabilities |= CAP_KEYBOARD;
+ if (xf86CheckBoolOption(pInfo->options, "_libinput/cap-pointer", 0))
+ capabilities |= CAP_POINTER;
+ if (xf86CheckBoolOption(pInfo->options, "_libinput/cap-touch", 0))
+ capabilities |= CAP_TOUCH;
+#ifdef HAVE_GESTURES
+ if (xf86CheckBoolOption(pInfo->options, "_libinput/cap-gesture", 0))
+ capabilities |= CAP_GESTURE;
+#endif
+ if (xf86CheckBoolOption(pInfo->options, "_libinput/cap-tablet-tool", 0))
+ capabilities |= CAP_TABLET_TOOL;
+
+ return capabilities;
+}
+
+static inline Bool
+claim_tablet_tool(InputInfoPtr pInfo)
+{
+ struct xf86libinput *driver_data = pInfo->private;
+ struct xf86libinput_device *shared_device = driver_data->shared_device;
+ struct xf86libinput_tablet_tool_event_queue *queue;
+ struct xf86libinput_tablet_tool *t;
+ enum libinput_tablet_tool_type tool_type;
+ uint64_t serial, tool_id;
+
+ serial = (uint32_t)xf86CheckIntOption(pInfo->options, "_libinput/tablet-tool-serial", 0);
+ tool_id = (uint32_t)xf86CheckIntOption(pInfo->options, "_libinput/tablet-tool-id", 0);
+ tool_type = (uint32_t)xf86CheckIntOption(pInfo->options, "_libinput/tablet-tool-type", 0);
+
+ xorg_list_for_each_entry(t,
+ &shared_device->unclaimed_tablet_tool_list,
+ node) {
+ if (libinput_tablet_tool_get_serial(t->tool) == serial &&
+ libinput_tablet_tool_get_tool_id(t->tool) == tool_id &&
+ libinput_tablet_tool_get_type(t->tool) == tool_type) {
+ driver_data->tablet_tool = t->tool;
+ queue = libinput_tablet_tool_get_user_data(t->tool);
+ if (queue)
+ queue->need_to_queue = false;
+ xorg_list_del(&t->node);
+ free(t);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static int
+xf86libinput_pre_init(InputDriverPtr drv,
+ InputInfoPtr pInfo,
+ int flags)
+{
+ struct xf86libinput *driver_data = NULL;
+ struct xf86libinput_device *shared_device = NULL;
+ struct libinput *libinput = NULL;
+ struct libinput_device *device = NULL;
+ char *path = NULL;
+ bool is_subdevice;
+
+ pInfo->type_name = 0;
+ pInfo->device_control = xf86libinput_device_control;
+ pInfo->read_input = xf86libinput_read_input;
+ pInfo->control_proc = NULL;
+ pInfo->switch_mode = NULL;
+
+ driver_data = calloc(1, sizeof(*driver_data));
+ if (!driver_data)
+ goto fail;
+
+ driver_data->valuators = valuator_mask_new(6);
+ if (!driver_data->valuators)
+ goto fail;
+
+ driver_data->valuators_unaccelerated = valuator_mask_new(2);
+ if (!driver_data->valuators_unaccelerated)
+ goto fail;
+
+ path = xf86SetStrOption(pInfo->options, "Device", NULL);
+ if (!path)
+ goto fail;
+
+ xf86libinput_init_driver_context();
+ libinput = driver_context.libinput;
+
+ if (libinput == NULL) {
+ xf86IDrvMsg(pInfo, X_ERROR, "Creating a device for %s failed\n", path);
+ goto fail;
+ }
+
+ is_subdevice = xf86libinput_is_subdevice(pInfo);
+ if (is_subdevice) {
+ InputInfoPtr parent;
+ struct xf86libinput *parent_driver_data;
+
+ parent = xf86libinput_get_parent(pInfo);
+ if (!parent) {
+ xf86IDrvMsg(pInfo, X_ERROR, "Failed to find parent device\n");
+ goto fail;
+ }
+
+ parent_driver_data = parent->private;
+ if (!parent_driver_data) /* parent already removed again */
+ goto fail;
+
+ xf86IDrvMsg(pInfo, X_INFO, "is a virtual subdevice\n");
+ shared_device = xf86libinput_shared_ref(parent_driver_data->shared_device);
+ device = shared_device->device;
+ if (!device)
+ xf86IDrvMsg(pInfo, X_ERROR, "Parent device not available\n");
+ }
+
+ if (!device) {
+ device = libinput_path_add_device(libinput, path);
+ if (!device) {
+ xf86IDrvMsg(pInfo, X_ERROR, "Failed to create a device for %s\n", path);
+ goto fail;
+ }
+
+ /* We ref the device above, then remove it. It gets
+ re-added with the same path in DEVICE_ON, we hope
+ it doesn't change until then */
+ libinput_device_ref(device);
+ libinput_path_remove_device(device);
+
+ shared_device = xf86libinput_shared_create(device);
+ if (!shared_device) {
+ libinput_device_unref(device);
+ goto fail;
+ }
+ }
+
+ pInfo->private = driver_data;
+ driver_data->pInfo = pInfo;
+ driver_data->path = path;
+ driver_data->shared_device = shared_device;
+ xorg_list_append(&driver_data->shared_device_link,
+ &shared_device->device_list);
+
+ /* Scroll dist value matters for source finger/continuous. For those
+ * devices libinput provides pixel-like data, changing this will
+ * affect touchpad scroll speed. For wheels it doesn't matter as
+ * we're using the discrete value only.
+ */
+ driver_data->scroll.v.dist = 120;
+ driver_data->scroll.h.dist = 120;
+
+ if (!is_subdevice) {
+ if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER))
+ driver_data->capabilities |= CAP_POINTER;
+ if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_KEYBOARD))
+ driver_data->capabilities |= CAP_KEYBOARD;
+ if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TOUCH))
+ driver_data->capabilities |= CAP_TOUCH;
+#ifdef HAVE_GESTURES
+ if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_GESTURE))
+ driver_data->capabilities |= CAP_GESTURE;
+#endif
+ if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TABLET_TOOL))
+ driver_data->capabilities |= CAP_TABLET;
+ if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TABLET_PAD))
+ driver_data->capabilities |= CAP_TABLET_PAD;
+ } else {
+
+ driver_data->capabilities = caps_from_options(pInfo);
+
+ if (driver_data->capabilities & CAP_TABLET_TOOL)
+ claim_tablet_tool(pInfo);
+ }
+
+ /* Disable acceleration in the server, libinput does it for us */
+ pInfo->options = xf86ReplaceIntOption(pInfo->options, "AccelerationProfile", -1);
+ pInfo->options = xf86ReplaceStrOption(pInfo->options, "AccelerationScheme", "none");
+
+ xf86libinput_parse_options(pInfo, driver_data, device);
+
+ /* Device is both keyboard and pointer. Drop the keyboard cap from
+ * this device, create a separate device instead */
+ if (!is_subdevice &&
+ driver_data->capabilities & CAP_KEYBOARD &&
+ driver_data->capabilities & (CAP_POINTER|CAP_TOUCH|CAP_GESTURE)) {
+ driver_data->capabilities &= ~CAP_KEYBOARD;
+ xf86libinput_create_subdevice(pInfo,
+ CAP_KEYBOARD,
+ NULL);
+ }
+
+ pInfo->type_name = xf86libinput_get_type_name(device, driver_data);
+
+ return Success;
+fail:
+ if (driver_data) {
+ if (driver_data->valuators)
+ valuator_mask_free(&driver_data->valuators);
+ if (driver_data->valuators_unaccelerated)
+ valuator_mask_free(&driver_data->valuators_unaccelerated);
+ }
+ free(path);
+ if (shared_device)
+ xf86libinput_shared_unref(shared_device);
+ free(driver_data);
+ if (libinput)
+ driver_context.libinput = libinput_unref(libinput);
+ return BadValue;
+}
+
+static void
+xf86libinput_uninit(InputDriverPtr drv,
+ InputInfoPtr pInfo,
+ int flags)
+{
+ struct xf86libinput *driver_data = pInfo->private;
+ if (driver_data) {
+ driver_context.libinput = libinput_unref(driver_context.libinput);
+ valuator_mask_free(&driver_data->valuators);
+ valuator_mask_free(&driver_data->valuators_unaccelerated);
+ free(driver_data->path);
+ free(driver_data);
+ pInfo->private = NULL;
+ }
+ xf86DeleteInput(pInfo, flags);
+}
+
+InputDriverRec xf86libinput_driver = {
+ .driverVersion = 1,
+ .driverName = "libinput",
+ .PreInit = xf86libinput_pre_init,
+ .UnInit = xf86libinput_uninit,
+ .module = NULL,
+ .default_options= NULL,
+ .capabilities = XI86_DRV_CAP_SERVER_FD
+};
+
+static XF86ModuleVersionInfo xf86libinput_version_info = {
+ "libinput",
+ MODULEVENDORSTRING,
+ MODINFOSTRING1,
+ MODINFOSTRING2,
+ XORG_VERSION_CURRENT,
+ PACKAGE_VERSION_MAJOR, PACKAGE_VERSION_MINOR, PACKAGE_VERSION_PATCHLEVEL,
+ ABI_CLASS_XINPUT,
+ ABI_XINPUT_VERSION,
+ MOD_CLASS_XINPUT,
+ {0, 0, 0, 0}
+};
+
+static pointer
+xf86libinput_setup_proc(pointer module, pointer options, int *errmaj, int *errmin)
+{
+ xf86AddInputDriver(&xf86libinput_driver, module, 0);
+ return module;
+}
+
+_X_EXPORT XF86ModuleData libinputModuleData = {
+ .vers = &xf86libinput_version_info,
+ .setup = &xf86libinput_setup_proc,
+ .teardown = NULL
+};
+
+/* Property support */
+
+/* libinput-specific properties */
+static Atom prop_tap;
+static Atom prop_tap_default;
+static Atom prop_tap_drag;
+static Atom prop_tap_drag_default;
+static Atom prop_tap_drag_lock;
+static Atom prop_tap_drag_lock_default;
+static Atom prop_tap_buttonmap;
+static Atom prop_tap_buttonmap_default;
+static Atom prop_calibration;
+static Atom prop_calibration_default;
+static Atom prop_accel;
+static Atom prop_accel_default;
+static Atom prop_accel_profile_enabled;
+static Atom prop_accel_profile_default;
+static Atom prop_accel_profiles_available;
+static Atom prop_natural_scroll;
+static Atom prop_natural_scroll_default;
+static Atom prop_sendevents_available;
+static Atom prop_sendevents_enabled;
+static Atom prop_sendevents_default;
+static Atom prop_left_handed;
+static Atom prop_left_handed_default;
+static Atom prop_scroll_methods_available;
+static Atom prop_scroll_method_enabled;
+static Atom prop_scroll_method_default;
+static Atom prop_scroll_button;
+static Atom prop_scroll_button_default;
+static Atom prop_scroll_buttonlock;
+static Atom prop_scroll_buttonlock_default;
+static Atom prop_scroll_pixel_distance;
+static Atom prop_scroll_pixel_distance_default;
+static Atom prop_click_methods_available;
+static Atom prop_click_method_enabled;
+static Atom prop_click_method_default;
+static Atom prop_middle_emulation;
+static Atom prop_middle_emulation_default;
+static Atom prop_disable_while_typing;
+static Atom prop_disable_while_typing_default;
+static Atom prop_mode_groups_available;
+static Atom prop_mode_groups;
+static Atom prop_mode_groups_buttons;
+static Atom prop_mode_groups_rings;
+static Atom prop_mode_groups_strips;
+static Atom prop_rotation_angle;
+static Atom prop_rotation_angle_default;
+
+/* driver properties */
+static Atom prop_draglock;
+static Atom prop_horiz_scroll;
+static Atom prop_pressurecurve;
+static Atom prop_area_ratio;
+static Atom prop_hires_scroll;
+
+/* general properties */
+static Atom prop_float;
+static Atom prop_device;
+static Atom prop_product_id;
+
+struct mode_prop_state {
+ int deviceid;
+ InputInfoPtr pInfo;
+
+ struct libinput_tablet_pad_mode_group *group;
+ unsigned int mode;
+ unsigned int idx;
+};
+
+static Bool
+update_mode_prop_cb(ClientPtr client, pointer closure)
+{
+ struct mode_prop_state *state = closure;
+ InputInfoPtr pInfo = state->pInfo, tmp;
+ struct xf86libinput *driver_data = pInfo->private;
+ BOOL found = FALSE;
+ XIPropertyValuePtr val;
+ int rc;
+ unsigned char groups[4] = {0};
+ struct libinput_tablet_pad_mode_group *group = state->group;
+ unsigned int mode = state->mode;
+ unsigned int idx = state->idx;
+
+ if (idx >= ARRAY_SIZE(groups))
+ goto out;
+
+ /* The device may have gotten removed before the WorkProc was
+ * scheduled. X reuses deviceids, but if the pointer value and
+ * device ID are what we had before, we're good */
+ nt_list_for_each_entry(tmp, xf86FirstLocalDevice(), next) {
+ if (tmp->dev->id == state->deviceid && tmp == pInfo) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (!found)
+ goto out;
+
+ rc = XIGetDeviceProperty(pInfo->dev,
+ prop_mode_groups,
+ &val);
+ if (rc != Success ||
+ val->format != 8 ||
+ val->size <= 0)
+ goto out;
+
+ memcpy(groups, (unsigned char*)val->data, val->size);
+
+ if (groups[idx] == mode)
+ goto out;
+
+ groups[idx] = mode;
+
+ driver_data->allow_mode_group_updates = true;
+ XIChangeDeviceProperty(pInfo->dev,
+ prop_mode_groups,
+ XA_INTEGER, 8,
+ PropModeReplace,
+ val->size,
+ groups,
+ TRUE);
+ driver_data->allow_mode_group_updates = false;
+
+out:
+ libinput_tablet_pad_mode_group_unref(group);
+ free(state);
+ return TRUE;
+}
+
+static inline void
+update_mode_prop(InputInfoPtr pInfo,
+ struct libinput_event_tablet_pad *event)
+{
+ struct libinput_tablet_pad_mode_group *group;
+ struct mode_prop_state *state;
+
+ state = calloc(1, sizeof(*state));
+ if (!state)
+ return;
+
+ state->deviceid = pInfo->dev->id;
+ state->pInfo = pInfo;
+
+ group = libinput_event_tablet_pad_get_mode_group(event);
+
+ state->group = libinput_tablet_pad_mode_group_ref(group);
+ state->mode = libinput_event_tablet_pad_get_mode(event);
+ state->idx = libinput_tablet_pad_mode_group_get_index(group);
+
+ /* Schedule a WorkProc so we don't update from within the input
+ thread */
+ QueueWorkProc(update_mode_prop_cb, serverClient, state);
+}
+
+static inline BOOL
+xf86libinput_check_device(DeviceIntPtr dev,
+ Atom atom)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+
+ if (device == NULL) {
+ BUG_WARN(dev->public.on);
+ xf86IDrvMsg(pInfo, X_INFO,
+ "SetProperty on %u called but device is disabled.\n"
+ "This driver cannot change properties on a disabled device\n",
+ atom);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static inline int
+LibinputSetPropertyTap(DeviceIntPtr dev,
+ Atom atom,
+ XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+ BOOL* data;
+
+ if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
+ return BadMatch;
+
+ data = (BOOL*)val->data;
+ if (checkonly) {
+ if (*data != 0 && *data != 1)
+ return BadValue;
+
+ if (!xf86libinput_check_device(dev, atom))
+ return BadMatch;
+
+ if (libinput_device_config_tap_get_finger_count(device) == 0)
+ return BadMatch;
+ } else {
+ driver_data->options.tapping = *data;
+ }
+
+ return Success;
+}
+
+static inline int
+LibinputSetPropertyTapDrag(DeviceIntPtr dev,
+ Atom atom,
+ XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+ BOOL* data;
+
+ if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
+ return BadMatch;
+
+ data = (BOOL*)val->data;
+ if (checkonly) {
+ if (*data != 0 && *data != 1)
+ return BadValue;
+
+ if (!xf86libinput_check_device(dev, atom))
+ return BadMatch;
+
+ if (libinput_device_config_tap_get_finger_count(device) == 0)
+ return BadMatch;
+ } else {
+ driver_data->options.tap_drag = *data;
+ }
+
+ return Success;
+}
+
+static inline int
+LibinputSetPropertyTapDragLock(DeviceIntPtr dev,
+ Atom atom,
+ XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+ BOOL* data;
+
+ if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
+ return BadMatch;
+
+ data = (BOOL*)val->data;
+ if (checkonly) {
+ if (*data != 0 && *data != 1)
+ return BadValue;
+
+ if (!xf86libinput_check_device(dev, atom))
+ return BadMatch;
+
+ if (libinput_device_config_tap_get_finger_count(device) == 0)
+ return BadMatch;
+ } else {
+ driver_data->options.tap_drag_lock = *data;
+ }
+
+ return Success;
+}
+
+static inline int
+LibinputSetPropertyTapButtonmap(DeviceIntPtr dev,
+ Atom atom,
+ XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ BOOL* data;
+ enum libinput_config_tap_button_map map;
+
+ if (val->format != 8 || val->size != 2 || val->type != XA_INTEGER)
+ return BadMatch;
+
+ data = (BOOL*)val->data;
+
+ if (checkonly) {
+ if ((data[0] && data[1]) || (!data[0] && !data[1]))
+ return BadValue;
+
+ if (!xf86libinput_check_device(dev, atom))
+ return BadMatch;
+ }
+
+ if (data[0])
+ map = LIBINPUT_CONFIG_TAP_MAP_LRM;
+ else if (data[1])
+ map = LIBINPUT_CONFIG_TAP_MAP_LMR;
+ else
+ return BadValue;
+
+ if (!checkonly)
+ driver_data->options.tap_button_map = map;
+
+ return Success;
+}
+
+static inline int
+LibinputSetPropertyCalibration(DeviceIntPtr dev,
+ Atom atom,
+ XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+ float* data;
+
+ if (val->format != 32 || val->size != 9 || val->type != prop_float)
+ return BadMatch;
+
+ data = (float*)val->data;
+
+ if (checkonly) {
+ if (data[6] != 0.0 ||
+ data[7] != 0.0 ||
+ data[8] != 1.0)
+ return BadValue;
+
+ if (!xf86libinput_check_device(dev, atom))
+ return BadMatch;
+
+ if (!libinput_device_config_calibration_has_matrix(device))
+ return BadMatch;
+ } else {
+ memcpy(driver_data->options.matrix,
+ data,
+ sizeof(driver_data->options.matrix));
+ }
+
+ return Success;
+}
+
+static inline int
+LibinputSetPropertyAccel(DeviceIntPtr dev,
+ Atom atom,
+ XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+ float* data;
+
+ if (val->format != 32 || val->size != 1 || val->type != prop_float)
+ return BadMatch;
+
+ data = (float*)val->data;
+
+ if (checkonly) {
+ if (*data < -1.0 || *data > 1.0)
+ return BadValue;
+
+ if (!xf86libinput_check_device(dev, atom))
+ return BadMatch;
+
+ if (libinput_device_config_accel_is_available(device) == 0)
+ return BadMatch;
+ } else {
+ driver_data->options.speed = *data;
+ }
+
+ return Success;
+}
+
+static inline int
+LibinputSetPropertyAccelProfile(DeviceIntPtr dev,
+ Atom atom,
+ XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+ BOOL* data;
+ uint32_t profiles = 0;
+
+ if (val->format != 8 || val->size != 2 || val->type != XA_INTEGER)
+ return BadMatch;
+
+ data = (BOOL*)val->data;
+
+ if (data[0])
+ profiles |= LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
+ if (data[1])
+ profiles |= LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT;
+
+ if (checkonly) {
+ uint32_t supported;
+
+ if (__builtin_popcount(profiles) > 1)
+ return BadValue;
+
+ if (!xf86libinput_check_device(dev, atom))
+ return BadMatch;
+
+ supported = libinput_device_config_accel_get_profiles(device);
+ if (profiles && (profiles & supported) == 0)
+ return BadValue;
+ } else {
+ driver_data->options.accel_profile = profiles;
+ }
+
+ return Success;
+}
+
+static inline int
+LibinputSetPropertyNaturalScroll(DeviceIntPtr dev,
+ Atom atom,
+ XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+ BOOL* data;
+
+ if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
+ return BadMatch;
+
+ data = (BOOL*)val->data;
+
+ if (checkonly) {
+ if (*data != 0 && *data != 1)
+ return BadValue;
+
+ if (!xf86libinput_check_device(dev, atom))
+ return BadMatch;
+
+ if (libinput_device_config_scroll_has_natural_scroll(device) == 0)
+ return BadMatch;
+ } else {
+ driver_data->options.natural_scrolling = *data;
+ }
+
+ return Success;
+}
+
+static inline int
+LibinputSetPropertySendEvents(DeviceIntPtr dev,
+ Atom atom,
+ XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+ BOOL* data;
+ uint32_t modes = 0;
+
+ if (val->format != 8 || val->size != 2 || val->type != XA_INTEGER)
+ return BadMatch;
+
+ data = (BOOL*)val->data;
+
+ if (data[0])
+ modes |= LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
+ if (data[1])
+ modes |= LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
+
+ if (checkonly) {
+ uint32_t supported;
+
+ if (!xf86libinput_check_device(dev, atom))
+ return BadMatch;
+
+ supported = libinput_device_config_send_events_get_modes(device);
+ if ((modes | supported) != supported)
+ return BadValue;
+
+ } else {
+ driver_data->options.sendevents = modes;
+ }
+
+ return Success;
+}
+
+static inline int
+LibinputSetPropertyLeftHanded(DeviceIntPtr dev,
+ Atom atom,
+ XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+ BOOL* data;
+
+ if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
+ return BadMatch;
+
+ data = (BOOL*)val->data;
+
+ if (checkonly) {
+ int supported;
+ int left_handed = *data;
+
+ if (!xf86libinput_check_device(dev, atom))
+ return BadMatch;
+
+ supported = libinput_device_config_left_handed_is_available(device);
+ if (!supported && left_handed)
+ return BadValue;
+ } else {
+ struct xf86libinput *other;
+
+ driver_data->options.left_handed = *data;
+
+ xorg_list_for_each_entry(other,
+ &driver_data->shared_device->device_list,
+ shared_device_link) {
+ DeviceIntPtr other_device = other->pInfo->dev;
+
+ if (other->options.left_handed == *data)
+ continue;
+
+ XIChangeDeviceProperty(other_device,
+ atom,
+ val->type,
+ val->format,
+ PropModeReplace,
+ val->size,
+ val->data,
+ TRUE);
+ }
+
+ }
+
+ return Success;
+}
+
+static inline int
+LibinputSetPropertyScrollMethods(DeviceIntPtr dev,
+ Atom atom,
+ XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+ BOOL* data;
+ uint32_t modes = 0;
+
+ if (val->format != 8 || val->size != 3 || val->type != XA_INTEGER)
+ return BadMatch;
+
+ data = (BOOL*)val->data;
+
+ if (data[0])
+ modes |= LIBINPUT_CONFIG_SCROLL_2FG;
+ if (data[1])
+ modes |= LIBINPUT_CONFIG_SCROLL_EDGE;
+ if (data[2])
+ modes |= LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
+
+ if (checkonly) {
+ uint32_t supported;
+
+ if (__builtin_popcount(modes) > 1)
+ return BadValue;
+
+ if (!xf86libinput_check_device(dev, atom))
+ return BadMatch;
+
+ supported = libinput_device_config_scroll_get_methods(device);
+ if (modes && (modes & supported) == 0)
+ return BadValue;
+ } else {
+ driver_data->options.scroll_method = modes;
+ }
+
+ return Success;
+}
+
+static inline int
+LibinputSetPropertyScrollButton(DeviceIntPtr dev,
+ Atom atom,
+ XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+ CARD32* data;
+
+ if (val->format != 32 || val->size != 1 || val->type != XA_CARDINAL)
+ return BadMatch;
+
+ data = (CARD32*)val->data;
+
+ if (checkonly) {
+ uint32_t button = *data;
+ uint32_t supported;
+
+ if (!xf86libinput_check_device(dev, atom))
+ return BadMatch;
+
+ supported = libinput_device_pointer_has_button(device,
+ btn_xorg2linux(button));
+ if (button && !supported)
+ return BadValue;
+ } else {
+ driver_data->options.scroll_button = *data;
+ }
+
+ return Success;
+}
+
+static inline int
+LibinputSetPropertyScrollButtonLock(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->size != 1 || val->type != XA_INTEGER)
+ 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.scroll_buttonlock = enabled;
+ }
+
+ return Success;
+}
+
+static inline int
+LibinputSetPropertyClickMethod(DeviceIntPtr dev,
+ Atom atom,
+ XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+ BOOL* data;
+ uint32_t modes = 0;
+
+ if (val->format != 8 || val->size != 2 || val->type != XA_INTEGER)
+ return BadMatch;
+
+ data = (BOOL*)val->data;
+
+ if (data[0])
+ modes |= LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
+ if (data[1])
+ modes |= LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
+
+ if (checkonly) {
+ uint32_t supported;
+
+ if (__builtin_popcount(modes) > 1)
+ return BadValue;
+
+ if (!xf86libinput_check_device(dev, atom))
+ return BadMatch;
+
+ supported = libinput_device_config_click_get_methods(device);
+ if (modes && (modes & supported) == 0)
+ return BadValue;
+ } else {
+ driver_data->options.click_method = modes;
+ }
+
+ return Success;
+}
+
+static inline int
+LibinputSetPropertyMiddleEmulation(DeviceIntPtr dev,
+ Atom atom,
+ XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+ BOOL* data;
+
+ if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
+ return BadMatch;
+
+ data = (BOOL*)val->data;
+ if (checkonly) {
+ if (*data != 0 && *data != 1)
+ return BadValue;
+
+ if (!xf86libinput_check_device(dev, atom))
+ return BadMatch;
+
+ if (!libinput_device_config_middle_emulation_is_available(device))
+ return BadMatch;
+ } else {
+ driver_data->options.middle_emulation = *data;
+ }
+
+ return Success;
+}
+
+static inline int
+LibinputSetPropertyDisableWhileTyping(DeviceIntPtr dev,
+ Atom atom,
+ XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+ BOOL* data;
+
+ if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
+ return BadMatch;
+
+ data = (BOOL*)val->data;
+ if (checkonly) {
+ if (*data != 0 && *data != 1)
+ return BadValue;
+
+ if (!xf86libinput_check_device(dev, atom))
+ return BadMatch;
+
+ if (!libinput_device_config_dwt_is_available(device))
+ return BadMatch;
+ } else {
+ driver_data->options.disable_while_typing = *data;
+ }
+
+ return Success;
+}
+
+static inline int
+prop_draglock_set_meta(struct xf86libinput *driver_data,
+ const BYTE *values,
+ int len,
+ BOOL checkonly)
+{
+ struct draglock *dl,
+ dummy; /* for checkonly */
+ int meta;
+
+ if (len > 1)
+ return BadImplementation; /* should not happen */
+
+ dl = (checkonly) ? &dummy : &driver_data->draglock;
+ meta = len > 0 ? values[0] : 0;
+
+ return draglock_set_meta(dl, meta) == 0 ? Success: BadValue;
+}
+
+static inline int
+prop_draglock_set_pairs(struct xf86libinput *driver_data,
+ const BYTE* pairs,
+ int len,
+ BOOL checkonly)
+{
+ struct draglock *dl,
+ dummy; /* for checkonly */
+ int data[MAX_BUTTONS + 1] = {0};
+ int i;
+ int highest = 0;
+
+ if (len >= ARRAY_SIZE(data))
+ return BadMatch;
+
+ if (len < 2 || len % 2)
+ return BadImplementation; /* should not happen */
+
+ dl = (checkonly) ? &dummy : &driver_data->draglock;
+
+ for (i = 0; i < len; i += 2) {
+ if (pairs[i] > MAX_BUTTONS)
+ return BadValue;
+
+ data[pairs[i]] = pairs[i+1];
+ highest = max(highest, pairs[i]);
+ }
+
+ return draglock_set_pairs(dl, data, highest + 1) == 0 ? Success : BadValue;
+}
+
+static inline int
+LibinputSetPropertyDragLockButtons(DeviceIntPtr dev,
+ Atom atom,
+ XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+
+ if (val->format != 8 || val->type != XA_INTEGER)
+ return BadMatch;
+
+ /* either a single value, or pairs of values */
+ if (val->size > 1 && val->size % 2)
+ return BadMatch;
+
+ if (!xf86libinput_check_device(dev, atom))
+ return BadMatch;
+
+ if (val->size <= 1)
+ return prop_draglock_set_meta(driver_data,
+ (BYTE*)val->data,
+ val->size, checkonly);
+ else
+ return prop_draglock_set_pairs(driver_data,
+ (BYTE*)val->data,
+ 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->size != 1 || val->type != XA_INTEGER)
+ 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 inline int
+LibinputSetPropertyScrollPixelDistance(DeviceIntPtr dev,
+ Atom atom,
+ XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ uint32_t dist;
+
+ if (val->format != 32 || val->size != 1 || val->type != XA_CARDINAL)
+ return BadMatch;
+
+ dist = *(BOOL*)val->data;
+ if (checkonly) {
+ if (dist < TOUCHPAD_SCROLL_DIST_MIN ||
+ dist > TOUCHPAD_SCROLL_DIST_MAX)
+ return BadValue;
+
+ if (!xf86libinput_check_device(dev, atom))
+ return BadMatch;
+ } else {
+ driver_data->options.scroll_pixel_distance = dist;
+ }
+
+ return Success;
+}
+
+static inline int
+LibinputSetPropertyRotationAngle(DeviceIntPtr dev,
+ Atom atom,
+ XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+ float *angle;
+
+ if (val->format != 32 || val->size != 1 || val->type != prop_float)
+ return BadMatch;
+
+ angle = (float*)val->data;
+
+ if (checkonly) {
+ if (*angle < 0.0 || *angle >= 360.0)
+ return BadValue;
+
+ if (!xf86libinput_check_device(dev, atom))
+ return BadMatch;
+
+ if (libinput_device_config_rotation_is_available(device) == 0)
+ return BadMatch;
+ } else {
+ driver_data->options.rotation_angle = *angle;
+ }
+
+ return Success;
+}
+
+static inline int
+LibinputSetPropertyPressureCurve(DeviceIntPtr dev,
+ Atom atom,
+ XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ float *vals;
+ struct bezier_control_point controls[4];
+
+ if (val->format != 32 || val->size != 8 || val->type != prop_float)
+ return BadMatch;
+
+ vals = val->data;
+ controls[0].x = vals[0];
+ controls[0].y = vals[1];
+ controls[1].x = vals[2];
+ controls[1].y = vals[3];
+ controls[2].x = vals[4];
+ controls[2].y = vals[5];
+ controls[3].x = vals[6];
+ controls[3].y = vals[7];
+
+ if (checkonly) {
+ int test_bezier[64];
+
+ for (int i = 0; i < val->size; i++) {
+ if (vals[i] < 0.0 || vals[i] > 1.0)
+ return BadValue;
+ }
+
+ if (!xf86libinput_check_device(dev, atom))
+ return BadMatch;
+
+ if (!cubic_bezier(controls, test_bezier, ARRAY_SIZE(test_bezier)))
+ return BadValue;
+ } else {
+ xf86libinput_set_pressurecurve(driver_data, controls);
+ memcpy(driver_data->options.pressurecurve, controls,
+ sizeof(controls));
+ }
+
+ return Success;
+}
+
+static inline int
+LibinputSetPropertyAreaRatio(DeviceIntPtr dev,
+ Atom atom,
+ XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ uint32_t *vals;
+ struct ratio area = { 0, 0 };
+
+ if (val->format != 32 || val->size != 2 || val->type != XA_CARDINAL)
+ return BadMatch;
+
+ vals = val->data;
+ area.x = vals[0];
+ area.y = vals[1];
+
+ if (checkonly) {
+ if (area.x < 0 || area.y < 0)
+ return BadValue;
+
+ if ((area.x != 0 && area.y == 0) ||
+ (area.x == 0 && area.y != 0))
+ return BadValue;
+
+ if (!xf86libinput_check_device(dev, atom))
+ return BadMatch;
+ } else {
+ struct xf86libinput *other;
+
+ xf86libinput_set_area_ratio(driver_data, &area);
+
+ xorg_list_for_each_entry(other,
+ &driver_data->shared_device->device_list,
+ shared_device_link) {
+ DeviceIntPtr other_device = other->pInfo->dev;
+
+ if (other->options.area.x == area.x &&
+ other->options.area.y == area.y)
+ continue;
+
+ XIChangeDeviceProperty(other_device,
+ atom,
+ val->type,
+ val->format,
+ PropModeReplace,
+ val->size,
+ val->data,
+ TRUE);
+ }
+ }
+
+ return Success;
+}
+
+static inline int
+LibinputSetPropertyHighResolutionScroll(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->size != 1 || val->type != XA_INTEGER)
+ 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.hires_scrolling_enabled = enabled;
+ }
+
+ return Success;
+}
+
+static int
+LibinputSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
+ BOOL checkonly)
+{
+ int rc;
+
+ if (atom == prop_tap)
+ rc = LibinputSetPropertyTap(dev, atom, val, checkonly);
+ else if (atom == prop_tap_drag)
+ rc = LibinputSetPropertyTapDrag(dev, atom, val, checkonly);
+ else if (atom == prop_tap_drag_lock)
+ rc = LibinputSetPropertyTapDragLock(dev, atom, val, checkonly);
+ else if (atom == prop_tap_buttonmap)
+ rc = LibinputSetPropertyTapButtonmap(dev, atom, val, checkonly);
+ else if (atom == prop_calibration)
+ rc = LibinputSetPropertyCalibration(dev, atom, val,
+ checkonly);
+ else if (atom == prop_accel)
+ rc = LibinputSetPropertyAccel(dev, atom, val, checkonly);
+ else if (atom == prop_accel_profile_enabled)
+ rc = LibinputSetPropertyAccelProfile(dev, atom, val, checkonly);
+ else if (atom == prop_natural_scroll)
+ rc = LibinputSetPropertyNaturalScroll(dev, atom, val, checkonly);
+ else if (atom == prop_sendevents_enabled)
+ rc = LibinputSetPropertySendEvents(dev, atom, val, checkonly);
+ else if (atom == prop_left_handed)
+ rc = LibinputSetPropertyLeftHanded(dev, atom, val, checkonly);
+ else if (atom == prop_scroll_method_enabled)
+ rc = LibinputSetPropertyScrollMethods(dev, atom, val, checkonly);
+ else if (atom == prop_scroll_button)
+ rc = LibinputSetPropertyScrollButton(dev, atom, val, checkonly);
+ else if (atom == prop_scroll_buttonlock)
+ rc = LibinputSetPropertyScrollButtonLock(dev, atom, val, checkonly);
+ else if (atom == prop_click_method_enabled)
+ rc = LibinputSetPropertyClickMethod(dev, atom, val, checkonly);
+ else if (atom == prop_middle_emulation)
+ rc = LibinputSetPropertyMiddleEmulation(dev, atom, val, checkonly);
+ else if (atom == prop_disable_while_typing)
+ 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_scroll_pixel_distance)
+ rc = LibinputSetPropertyScrollPixelDistance(dev, atom, val, checkonly);
+ else if (atom == prop_mode_groups) {
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+
+ if (driver_data->allow_mode_group_updates)
+ return Success;
+ else
+ return BadAccess;
+ }
+ else if (atom == prop_rotation_angle)
+ rc = LibinputSetPropertyRotationAngle(dev, atom, val, checkonly);
+ else if (atom == prop_pressurecurve)
+ rc = LibinputSetPropertyPressureCurve(dev, atom, val, checkonly);
+ else if (atom == prop_area_ratio)
+ rc = LibinputSetPropertyAreaRatio(dev, atom, val, checkonly);
+ else if (atom == prop_hires_scroll)
+ rc = LibinputSetPropertyHighResolutionScroll(dev, atom, val, checkonly);
+ else if (atom == prop_device || atom == prop_product_id ||
+ atom == prop_tap_default ||
+ atom == prop_tap_drag_default ||
+ atom == prop_tap_drag_lock_default ||
+ atom == prop_tap_buttonmap_default ||
+ atom == prop_calibration_default ||
+ atom == prop_accel_default ||
+ atom == prop_accel_profile_default ||
+ atom == prop_natural_scroll_default ||
+ atom == prop_sendevents_default ||
+ atom == prop_sendevents_available ||
+ atom == prop_left_handed_default ||
+ atom == prop_scroll_method_default ||
+ atom == prop_scroll_methods_available ||
+ atom == prop_scroll_button_default ||
+ atom == prop_scroll_buttonlock_default ||
+ atom == prop_scroll_pixel_distance_default ||
+ atom == prop_click_method_default ||
+ atom == prop_click_methods_available ||
+ atom == prop_middle_emulation_default ||
+ atom == prop_disable_while_typing_default ||
+ atom == prop_mode_groups_available ||
+ atom == prop_mode_groups_buttons ||
+ atom == prop_mode_groups_rings ||
+ atom == prop_mode_groups_strips ||
+ atom == prop_rotation_angle_default)
+ return BadAccess; /* read-only */
+ else
+ return Success;
+
+ if (!checkonly && rc == Success)
+ LibinputApplyConfig(dev);
+
+ return rc;
+}
+
+static Atom
+LibinputMakeProperty(DeviceIntPtr dev,
+ const char *prop_name,
+ Atom type,
+ int format,
+ int len,
+ void *data)
+{
+ int rc;
+ Atom prop = MakeAtom(prop_name, strlen(prop_name), TRUE);
+
+ rc = XIChangeDeviceProperty(dev, prop, type, format,
+ PropModeReplace,
+ len, data, FALSE);
+ if (rc != Success)
+ return None;
+
+ XISetDevicePropertyDeletable(dev, prop, FALSE);
+
+ return prop;
+}
+
+static void
+LibinputInitTapProperty(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ BOOL tap = driver_data->options.tapping;
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER))
+ return;
+
+ if (libinput_device_config_tap_get_finger_count(device) == 0)
+ return;
+
+ prop_tap = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_TAP,
+ XA_INTEGER,
+ 8,
+ 1,
+ &tap);
+ if (!prop_tap)
+ return;
+
+ tap = libinput_device_config_tap_get_default_enabled(device);
+ prop_tap_default = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_TAP_DEFAULT,
+ XA_INTEGER, 8,
+ 1, &tap);
+}
+
+static void
+LibinputInitTapDragProperty(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ BOOL drag = driver_data->options.tap_drag;
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER))
+ return;
+
+ if (libinput_device_config_tap_get_finger_count(device) == 0)
+ return;
+
+ prop_tap_drag = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_TAP_DRAG,
+ XA_INTEGER, 8,
+ 1, &drag);
+ if (!prop_tap_drag)
+ return;
+
+ drag = libinput_device_config_tap_get_default_drag_enabled(device);
+ prop_tap_drag_default = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_TAP_DRAG_DEFAULT,
+ XA_INTEGER, 8,
+ 1, &drag);
+}
+
+static void
+LibinputInitTapDragLockProperty(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ BOOL drag_lock = driver_data->options.tap_drag_lock;
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER))
+ return;
+
+ if (libinput_device_config_tap_get_finger_count(device) == 0)
+ return;
+
+ prop_tap_drag_lock = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_TAP_DRAG_LOCK,
+ XA_INTEGER, 8,
+ 1, &drag_lock);
+ if (!prop_tap_drag_lock)
+ return;
+
+ drag_lock = libinput_device_config_tap_get_default_drag_lock_enabled(device);
+ prop_tap_drag_lock_default = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_TAP_DRAG_LOCK_DEFAULT,
+ XA_INTEGER, 8,
+ 1, &drag_lock);
+}
+
+static void
+LibinputInitTapButtonmapProperty(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ enum libinput_config_tap_button_map map;
+ BOOL data[2] = {0};
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER))
+ return;
+
+ map = driver_data->options.tap_button_map;
+
+ if (libinput_device_config_tap_get_finger_count(device) == 0)
+ return;
+
+ switch (map) {
+ case LIBINPUT_CONFIG_TAP_MAP_LRM:
+ data[0] = 1;
+ break;
+ case LIBINPUT_CONFIG_TAP_MAP_LMR:
+ data[1] = 1;
+ break;
+ default:
+ break;
+ }
+
+ prop_tap_buttonmap = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_TAP_BUTTONMAP,
+ XA_INTEGER, 8,
+ 2, data);
+ if (!prop_tap_buttonmap)
+ return;
+
+ map = libinput_device_config_tap_get_default_button_map(device);
+ memset(data, 0, sizeof(data));
+
+ switch (map) {
+ case LIBINPUT_CONFIG_TAP_MAP_LRM:
+ data[0] = 1;
+ break;
+ case LIBINPUT_CONFIG_TAP_MAP_LMR:
+ data[1] = 1;
+ break;
+ default:
+ break;
+ }
+
+ prop_tap_buttonmap_default = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_TAP_BUTTONMAP_DEFAULT,
+ XA_INTEGER, 8,
+ 2, data);
+}
+
+static void
+LibinputInitCalibrationProperty(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ float calibration[9];
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER|CAP_TOUCH|CAP_TABLET))
+ return;
+
+ if (!libinput_device_config_calibration_has_matrix(device))
+ return;
+
+ /* We use a 9-element matrix just to be closer to the X server's
+ transformation matrix which also has the full matrix */
+
+ libinput_device_config_calibration_get_matrix(device, calibration);
+ calibration[6] = 0.0;
+ calibration[7] = 0.0;
+ calibration[8] = 1.0;
+
+ prop_calibration = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_CALIBRATION,
+ prop_float, 32,
+ 9, calibration);
+ if (!prop_calibration)
+ return;
+
+ libinput_device_config_calibration_get_default_matrix(device,
+ calibration);
+
+ prop_calibration_default = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_CALIBRATION_DEFAULT,
+ prop_float, 32,
+ 9, calibration);
+}
+
+static void
+LibinputInitAccelProperty(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ float speed = driver_data->options.speed;
+ uint32_t profile_mask;
+ enum libinput_config_accel_profile profile;
+ BOOL profiles[2] = {FALSE};
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER))
+ return;
+
+ if (!libinput_device_config_accel_is_available(device) ||
+ driver_data->capabilities & CAP_TABLET)
+ return;
+
+ prop_accel = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_ACCEL,
+ prop_float, 32,
+ 1, &speed);
+ if (!prop_accel)
+ return;
+
+ speed = libinput_device_config_accel_get_default_speed(device);
+ prop_accel_default = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_ACCEL_DEFAULT,
+ prop_float, 32,
+ 1, &speed);
+
+ profile_mask = libinput_device_config_accel_get_profiles(device);
+ if (profile_mask == LIBINPUT_CONFIG_ACCEL_PROFILE_NONE)
+ return;
+
+ if (profile_mask & LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE)
+ profiles[0] = TRUE;
+ if (profile_mask & LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT)
+ profiles[1] = TRUE;
+
+ prop_accel_profiles_available = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_ACCEL_PROFILES_AVAILABLE,
+ XA_INTEGER, 8,
+ ARRAY_SIZE(profiles),
+ profiles);
+ if (!prop_accel_profiles_available)
+ return;
+
+ memset(profiles, 0, sizeof(profiles));
+
+ profile = libinput_device_config_accel_get_profile(device);
+ switch(profile) {
+ case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE:
+ profiles[0] = TRUE;
+ break;
+ case LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT:
+ profiles[1] = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ prop_accel_profile_enabled = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_ACCEL_PROFILE_ENABLED,
+ XA_INTEGER, 8,
+ ARRAY_SIZE(profiles),
+ profiles);
+ if (!prop_accel_profile_enabled)
+ return;
+
+ memset(profiles, 0, sizeof(profiles));
+
+ profile = libinput_device_config_accel_get_default_profile(device);
+ switch(profile) {
+ case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE:
+ profiles[0] = TRUE;
+ break;
+ case LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT:
+ profiles[1] = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ prop_accel_profile_default = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_ACCEL_PROFILE_ENABLED_DEFAULT,
+ XA_INTEGER, 8,
+ ARRAY_SIZE(profiles),
+ profiles);
+ if (!prop_accel_profile_default)
+ return;
+
+}
+
+static void
+LibinputInitNaturalScrollProperty(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ BOOL natural_scroll = driver_data->options.natural_scrolling;
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER))
+ return;
+
+ if (!libinput_device_config_scroll_has_natural_scroll(device))
+ return;
+
+ prop_natural_scroll = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_NATURAL_SCROLL,
+ XA_INTEGER, 8,
+ 1, &natural_scroll);
+ if (!prop_natural_scroll)
+ return;
+
+ natural_scroll = libinput_device_config_scroll_get_default_natural_scroll_enabled(device);
+ prop_natural_scroll_default = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_NATURAL_SCROLL_DEFAULT,
+ XA_INTEGER, 8,
+ 1, &natural_scroll);
+}
+
+static void
+LibinputInitSendEventsProperty(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ uint32_t sendevent_modes;
+ uint32_t sendevents;
+ BOOL modes[2] = {FALSE};
+
+ sendevent_modes = libinput_device_config_send_events_get_modes(device);
+ if (sendevent_modes == LIBINPUT_CONFIG_SEND_EVENTS_ENABLED)
+ return;
+
+ if (sendevent_modes & LIBINPUT_CONFIG_SEND_EVENTS_DISABLED)
+ modes[0] = TRUE;
+ if (sendevent_modes & LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE)
+ modes[1] = TRUE;
+
+ prop_sendevents_available = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_SENDEVENTS_AVAILABLE,
+ XA_INTEGER, 8,
+ 2, modes);
+ if (!prop_sendevents_available)
+ return;
+
+ memset(modes, 0, sizeof(modes));
+ sendevents = driver_data->options.sendevents;
+
+ switch(sendevents) {
+ case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED:
+ modes[0] = TRUE;
+ break;
+ case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE:
+ modes[1] = TRUE;
+ break;
+ }
+
+ prop_sendevents_enabled = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_SENDEVENTS_ENABLED,
+ XA_INTEGER, 8,
+ 2, modes);
+
+ if (!prop_sendevents_enabled)
+ return;
+
+ memset(modes, 0, sizeof(modes));
+ sendevent_modes = libinput_device_config_send_events_get_default_mode(device);
+ if (sendevent_modes & LIBINPUT_CONFIG_SEND_EVENTS_DISABLED)
+ modes[0] = TRUE;
+ if (sendevent_modes & LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE)
+ modes[1] = TRUE;
+
+ prop_sendevents_default = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_SENDEVENTS_ENABLED_DEFAULT,
+ XA_INTEGER, 8,
+ 2, modes);
+}
+
+static void
+LibinputInitLeftHandedProperty(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ BOOL left_handed = driver_data->options.left_handed;
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER|CAP_TABLET))
+ return;
+
+ if (!libinput_device_config_left_handed_is_available(device) ||
+ driver_data->capabilities & CAP_TABLET)
+ return;
+
+ prop_left_handed = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_LEFT_HANDED,
+ XA_INTEGER, 8,
+ 1, &left_handed);
+ if (!prop_left_handed)
+ return;
+
+ left_handed = libinput_device_config_left_handed_get_default(device);
+ prop_left_handed_default = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_LEFT_HANDED_DEFAULT,
+ XA_INTEGER, 8,
+ 1, &left_handed);
+}
+
+static void
+LibinputInitScrollMethodsProperty(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ uint32_t scroll_methods;
+ enum libinput_config_scroll_method method;
+ BOOL methods[3] = {FALSE};
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER))
+ return;
+
+ scroll_methods = libinput_device_config_scroll_get_methods(device);
+ if (scroll_methods == LIBINPUT_CONFIG_SCROLL_NO_SCROLL)
+ return;
+
+ if (scroll_methods & LIBINPUT_CONFIG_SCROLL_2FG)
+ methods[0] = TRUE;
+ if (scroll_methods & LIBINPUT_CONFIG_SCROLL_EDGE)
+ methods[1] = TRUE;
+ if (scroll_methods & LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN)
+ methods[2] = TRUE;
+
+ prop_scroll_methods_available = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_SCROLL_METHODS_AVAILABLE,
+ XA_INTEGER, 8,
+ ARRAY_SIZE(methods),
+ methods);
+ if (!prop_scroll_methods_available)
+ return;
+
+ memset(methods, 0, sizeof(methods));
+
+ method = libinput_device_config_scroll_get_method(device);
+ switch(method) {
+ case LIBINPUT_CONFIG_SCROLL_2FG:
+ methods[0] = TRUE;
+ break;
+ case LIBINPUT_CONFIG_SCROLL_EDGE:
+ methods[1] = TRUE;
+ break;
+ case LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN:
+ methods[2] = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ prop_scroll_method_enabled = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_SCROLL_METHOD_ENABLED,
+ XA_INTEGER, 8,
+ ARRAY_SIZE(methods),
+ methods);
+ if (!prop_scroll_method_enabled)
+ return;
+
+ scroll_methods = libinput_device_config_scroll_get_default_method(device);
+ if (scroll_methods & LIBINPUT_CONFIG_SCROLL_2FG)
+ methods[0] = TRUE;
+ if (scroll_methods & LIBINPUT_CONFIG_SCROLL_EDGE)
+ methods[1] = TRUE;
+ if (scroll_methods & LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN)
+ methods[2] = TRUE;
+
+ prop_scroll_method_default = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_SCROLL_METHOD_ENABLED_DEFAULT,
+ XA_INTEGER, 8,
+ ARRAY_SIZE(methods),
+ methods);
+ /* Scroll button and scroll button lock */
+ if (libinput_device_config_scroll_get_methods(device) &
+ LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) {
+ CARD32 scroll_button = driver_data->options.scroll_button;
+ BOOL lock_enabled = driver_data->options.scroll_buttonlock;
+
+ prop_scroll_button = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_SCROLL_BUTTON,
+ XA_CARDINAL, 32,
+ 1, &scroll_button);
+ if (!prop_scroll_button)
+ return;
+
+#if HAVE_LIBINPUT_SCROLL_BUTTON_LOCK
+ scroll_button = libinput_device_config_scroll_get_default_button(device);
+ scroll_button = btn_linux2xorg(scroll_button);
+ prop_scroll_button_default = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_SCROLL_BUTTON_DEFAULT,
+ XA_CARDINAL, 32,
+ 1, &scroll_button);
+ prop_scroll_buttonlock = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_SCROLL_BUTTON_LOCK,
+ XA_INTEGER, 8,
+ 1, &lock_enabled);
+ if (!prop_scroll_buttonlock)
+ return;
+
+ lock_enabled = libinput_device_config_scroll_get_default_button_lock(device);
+ prop_scroll_buttonlock_default = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_SCROLL_BUTTON_LOCK_DEFAULT,
+ XA_INTEGER, 8,
+ 1, &lock_enabled);
+#endif
+ }
+}
+
+static void
+LibinputInitScrollPixelDistanceProperty(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ CARD32 dist = driver_data->options.scroll_pixel_distance;
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER))
+ return;
+
+ if (!xf86libinput_want_scroll_distance_option(device))
+ return;
+
+ prop_scroll_pixel_distance = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_SCROLL_PIXEL_DISTANCE,
+ XA_CARDINAL, 32,
+ 1, &dist);
+ if (!prop_scroll_pixel_distance)
+ return;
+
+ prop_scroll_pixel_distance_default = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_SCROLL_PIXEL_DISTANCE_DEFAULT,
+ XA_CARDINAL, 32,
+ 1, &dist);
+}
+
+static void
+LibinputInitClickMethodsProperty(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ uint32_t click_methods;
+ enum libinput_config_click_method method;
+ BOOL methods[2] = {FALSE};
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER))
+ return;
+
+ click_methods = libinput_device_config_click_get_methods(device);
+ if (click_methods == LIBINPUT_CONFIG_CLICK_METHOD_NONE)
+ return;
+
+ if (click_methods & LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS)
+ methods[0] = TRUE;
+ if (click_methods & LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER)
+ methods[1] = TRUE;
+
+ prop_click_methods_available = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_CLICK_METHODS_AVAILABLE,
+ XA_INTEGER, 8,
+ ARRAY_SIZE(methods),
+ methods);
+ if (!prop_click_methods_available)
+ return;
+
+ memset(methods, 0, sizeof(methods));
+
+ method = libinput_device_config_click_get_method(device);
+ switch(method) {
+ case LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS:
+ methods[0] = TRUE;
+ break;
+ case LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER:
+ methods[1] = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ prop_click_method_enabled = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_CLICK_METHOD_ENABLED,
+ XA_INTEGER, 8,
+ ARRAY_SIZE(methods),
+ methods);
+
+ if (!prop_click_method_enabled)
+ return;
+
+ memset(methods, 0, sizeof(methods));
+
+ method = libinput_device_config_click_get_default_method(device);
+ switch(method) {
+ case LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS:
+ methods[0] = TRUE;
+ break;
+ case LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER:
+ methods[1] = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ prop_click_method_default = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_CLICK_METHOD_ENABLED_DEFAULT,
+ XA_INTEGER, 8,
+ ARRAY_SIZE(methods),
+ methods);
+}
+
+static void
+LibinputInitMiddleEmulationProperty(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ BOOL middle = driver_data->options.middle_emulation;
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER))
+ return;
+
+ if (!libinput_device_config_middle_emulation_is_available(device))
+ return;
+
+ prop_middle_emulation = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_MIDDLE_EMULATION_ENABLED,
+ XA_INTEGER,
+ 8,
+ 1,
+ &middle);
+ if (!prop_middle_emulation)
+ return;
+
+ middle = libinput_device_config_middle_emulation_get_default_enabled(device);
+ prop_middle_emulation_default = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_MIDDLE_EMULATION_ENABLED_DEFAULT,
+ XA_INTEGER, 8,
+ 1, &middle);
+}
+
+static void
+LibinputInitDisableWhileTypingProperty(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ BOOL dwt = driver_data->options.disable_while_typing;
+
+ if (!subdevice_has_capabilities(dev, CAP_POINTER))
+ return;
+
+ if (!libinput_device_config_dwt_is_available(device))
+ return;
+
+ prop_disable_while_typing = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_DISABLE_WHILE_TYPING,
+ XA_INTEGER,
+ 8,
+ 1,
+ &dwt);
+ if (!prop_disable_while_typing)
+ return;
+
+ dwt = libinput_device_config_dwt_get_default_enabled(device);
+ prop_disable_while_typing_default = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_DISABLE_WHILE_TYPING_DEFAULT,
+ XA_INTEGER, 8,
+ 1, &dwt);
+}
+
+static void
+LibinputInitModeGroupProperties(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ struct libinput_tablet_pad_mode_group *group;
+ int ngroups, nmodes, mode;
+ int nbuttons, nstrips, nrings;
+ unsigned char groups[4] = {0},
+ current[4] = {0},
+ associations[MAX_BUTTONS] = {0};
+ int g, b, r, s;
+
+ if (!subdevice_has_capabilities(dev, CAP_TABLET_PAD))
+ return;
+
+ if (!libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TABLET_PAD))
+ return;
+
+ ngroups = libinput_device_tablet_pad_get_num_mode_groups(device);
+ if (ngroups <= 0)
+ return;
+
+ group = libinput_device_tablet_pad_get_mode_group(device, 0);
+ nmodes = libinput_tablet_pad_mode_group_get_num_modes(group);
+ if (ngroups == 1 && nmodes == 1)
+ return;
+
+ ngroups = min(ngroups, ARRAY_SIZE(groups));
+ for (g = 0; g < ngroups; g++) {
+ group = libinput_device_tablet_pad_get_mode_group(device, g);
+ nmodes = libinput_tablet_pad_mode_group_get_num_modes(group);
+ mode = libinput_tablet_pad_mode_group_get_mode(group);
+
+ groups[g] = nmodes;
+ current[g] = mode;
+ }
+
+ prop_mode_groups_available = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_TABLET_PAD_MODE_GROUPS_AVAILABLE,
+ XA_INTEGER,
+ 8,
+ ngroups,
+ groups);
+ if (!prop_mode_groups_available)
+ return;
+
+ prop_mode_groups = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_TABLET_PAD_MODE_GROUPS,
+ XA_INTEGER,
+ 8,
+ ngroups,
+ current);
+ if (!prop_mode_groups)
+ return;
+
+ for (b = 0; b < ARRAY_SIZE(associations); b++)
+ associations[b] = -1;
+
+ nbuttons = libinput_device_tablet_pad_get_num_buttons(device);
+ for (b = 0; b < nbuttons; b++) {
+ /* logical buttons exclude scroll wheel buttons */
+ int lb = b <= 3 ? b : b + 4;
+ associations[lb] = -1;
+ for (g = 0; g < ngroups; g++) {
+ group = libinput_device_tablet_pad_get_mode_group(device, g);
+ if (libinput_tablet_pad_mode_group_has_button(group, b)) {
+ associations[lb] = g;
+ break;
+ }
+ }
+ }
+
+ prop_mode_groups_buttons = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_TABLET_PAD_MODE_GROUP_BUTTONS,
+ XA_INTEGER,
+ 8,
+ nbuttons,
+ associations);
+ if (!prop_mode_groups_buttons)
+ return;
+
+ nrings = libinput_device_tablet_pad_get_num_rings(device);
+ if (nrings) {
+ for (r = 0; r < nrings; r++) {
+ associations[r] = -1;
+ for (g = 0; g < ngroups; g++) {
+ group = libinput_device_tablet_pad_get_mode_group(device, g);
+ if (libinput_tablet_pad_mode_group_has_ring(group, r)) {
+ associations[r] = g;
+ break;
+ }
+ }
+ }
+
+ prop_mode_groups_rings = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_TABLET_PAD_MODE_GROUP_RINGS,
+ XA_INTEGER,
+ 8,
+ nrings,
+ associations);
+ if (!prop_mode_groups_rings)
+ return;
+ }
+
+ nstrips = libinput_device_tablet_pad_get_num_strips(device);
+ if (nstrips) {
+ for (s = 0; s < nstrips; s++) {
+ associations[s] = -1;
+ for (g = 0; g < ngroups; g++) {
+ group = libinput_device_tablet_pad_get_mode_group(device, g);
+ if (libinput_tablet_pad_mode_group_has_strip(group, s)) {
+ associations[s] = g;
+ break;
+ }
+ }
+ }
+
+ prop_mode_groups_strips = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_TABLET_PAD_MODE_GROUP_STRIPS,
+ XA_INTEGER,
+ 8,
+ nstrips,
+ associations);
+ if (!prop_mode_groups_strips)
+ return;
+ }
+}
+
+static void
+LibinputInitDragLockProperty(DeviceIntPtr dev,
+ struct xf86libinput *driver_data)
+{
+ size_t sz;
+ int dl_values[MAX_BUTTONS + 1];
+
+ if ((driver_data->capabilities & CAP_POINTER) == 0)
+ return;
+
+ switch (draglock_get_mode(&driver_data->draglock)) {
+ case DRAGLOCK_DISABLED:
+ sz = 0; /* will be an empty property */
+ break;
+ case DRAGLOCK_META:
+ dl_values[0] = draglock_get_meta(&driver_data->draglock);
+ sz = 1;
+ break;
+ case DRAGLOCK_PAIRS:
+ sz = draglock_get_pairs(&driver_data->draglock,
+ dl_values, ARRAY_SIZE(dl_values));
+ break;
+ default:
+ xf86IDrvMsg(dev->public.devicePrivate,
+ X_ERROR,
+ "Invalid drag lock mode\n");
+ return;
+ }
+
+ prop_draglock = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_DRAG_LOCK_BUTTONS,
+ XA_INTEGER, 8,
+ sz, dl_values);
+}
+
+static void
+LibinputInitHorizScrollProperty(DeviceIntPtr dev,
+ struct xf86libinput *driver_data)
+{
+ BOOL enabled = driver_data->options.horiz_scrolling_enabled;
+
+ if ((driver_data->capabilities & CAP_POINTER) == 0)
+ return;
+
+ prop_horiz_scroll = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_HORIZ_SCROLL_ENABLED,
+ XA_INTEGER, 8,
+ 1, &enabled);
+}
+
+static void
+LibinputInitRotationAngleProperty(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ float angle = driver_data->options.rotation_angle;
+
+ if (!libinput_device_config_rotation_is_available(device))
+ return;
+
+ prop_rotation_angle = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_ROTATION_ANGLE,
+ prop_float, 32,
+ 1, &angle);
+ if (!prop_rotation_angle)
+ return;
+
+ angle = libinput_device_config_rotation_get_default_angle(device);
+ prop_rotation_angle_default = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_ROTATION_ANGLE_DEFAULT,
+ prop_float, 32,
+ 1, &angle);
+
+ if (!prop_rotation_angle_default)
+ return;
+}
+
+static void
+LibinputInitPressureCurveProperty(DeviceIntPtr dev,
+ struct xf86libinput *driver_data)
+{
+ const struct bezier_control_point *curve = driver_data->options.pressurecurve;
+ struct libinput_tablet_tool *tool = driver_data->tablet_tool;
+ float data[8];
+
+ if ((driver_data->capabilities & CAP_TABLET_TOOL) == 0)
+ return;
+
+ if (!tool || !libinput_tablet_tool_has_pressure(tool))
+ return;
+
+ data[0] = curve[0].x;
+ data[1] = curve[0].y;
+ data[2] = curve[1].x;
+ data[3] = curve[1].y;
+ data[4] = curve[2].x;
+ data[5] = curve[2].y;
+ data[6] = curve[3].x;
+ data[7] = curve[3].y;
+
+ prop_pressurecurve = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_TABLET_TOOL_PRESSURECURVE,
+ prop_float, 32,
+ 8, data);
+}
+
+static void
+LibinputInitTabletAreaRatioProperty(DeviceIntPtr dev,
+ struct xf86libinput *driver_data)
+{
+ const struct ratio *ratio = &driver_data->options.area;
+ uint32_t data[2];
+
+ if (!want_area_handling(driver_data))
+ return;
+
+ data[0] = ratio->x;
+ data[1] = ratio->y;
+
+ prop_area_ratio = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_TABLET_TOOL_AREA_RATIO,
+ XA_CARDINAL, 32,
+ 2, data);
+}
+
+static void
+LibinputInitHighResolutionScrollProperty(DeviceIntPtr dev,
+ struct xf86libinput *driver_data,
+ struct libinput_device *device)
+{
+ BOOL enabled = driver_data->options.hires_scrolling_enabled;
+
+ if ((driver_data->capabilities & CAP_POINTER) == 0)
+ return;
+
+ prop_hires_scroll = LibinputMakeProperty(dev,
+ LIBINPUT_PROP_HIRES_WHEEL_SCROLL_ENABLED,
+ XA_INTEGER, 8,
+ 1, &enabled);
+}
+
+static void
+LibinputInitProperty(DeviceIntPtr dev)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ struct xf86libinput *driver_data = pInfo->private;
+ struct libinput_device *device = driver_data->shared_device->device;
+ const char *device_node;
+ CARD32 product[2];
+ int rc;
+
+ prop_float = XIGetKnownProperty("FLOAT");
+
+ LibinputInitTapProperty(dev, driver_data, device);
+ LibinputInitTapDragProperty(dev, driver_data, device);
+ LibinputInitTapDragLockProperty(dev, driver_data, device);
+ LibinputInitTapButtonmapProperty(dev, driver_data, device);
+ LibinputInitNaturalScrollProperty(dev, driver_data, device);
+ LibinputInitDisableWhileTypingProperty(dev, driver_data, device);
+ LibinputInitScrollMethodsProperty(dev, driver_data, device);
+ LibinputInitClickMethodsProperty(dev, driver_data, device);
+ LibinputInitMiddleEmulationProperty(dev, driver_data, device);
+ LibinputInitRotationAngleProperty(dev, driver_data, device);
+ LibinputInitAccelProperty(dev, driver_data, device);
+ LibinputInitCalibrationProperty(dev, driver_data, device);
+ LibinputInitLeftHandedProperty(dev, driver_data, device);
+ LibinputInitModeGroupProperties(dev, driver_data, device);
+ LibinputInitSendEventsProperty(dev, driver_data, device);
+
+ /* Device node property, read-only */
+ device_node = driver_data->path;
+ prop_device = MakeAtom(XI_PROP_DEVICE_NODE,
+ strlen(XI_PROP_DEVICE_NODE),
+ TRUE);
+ rc = XIChangeDeviceProperty(dev, prop_device, XA_STRING, 8,
+ PropModeReplace,
+ strlen(device_node), device_node,
+ FALSE);
+ if (rc != Success)
+ return;
+
+ XISetDevicePropertyDeletable(dev, prop_device, FALSE);
+
+ prop_product_id = MakeAtom(XI_PROP_PRODUCT_ID,
+ strlen(XI_PROP_PRODUCT_ID),
+ TRUE);
+ product[0] = libinput_device_get_id_vendor(device);
+ product[1] = libinput_device_get_id_product(device);
+ rc = XIChangeDeviceProperty(dev, prop_product_id, XA_INTEGER, 32,
+ PropModeReplace, 2, product, FALSE);
+ if (rc != Success)
+ return;
+
+ XISetDevicePropertyDeletable(dev, prop_product_id, FALSE);
+
+ LibinputInitDragLockProperty(dev, driver_data);
+ LibinputInitHorizScrollProperty(dev, driver_data);
+ LibinputInitScrollPixelDistanceProperty(dev, driver_data, device);
+ LibinputInitPressureCurveProperty(dev, driver_data);
+ LibinputInitTabletAreaRatioProperty(dev, driver_data);
+ LibinputInitHighResolutionScrollProperty(dev, driver_data, device);
+}