From a662fd570ce5a65a8a139faa42cbe85c8c2d3732 Mon Sep 17 00:00:00 2001 From: "jk7744.park" Date: Mon, 26 Oct 2015 16:01:56 +0900 Subject: [PATCH] tizen 2.4 release --- .gitignore | 70 ++ COPYING | 29 + Makefile.am | 11 + README.md | 22 + autogen.sh | 12 + configure.ac | 143 +++ doc/.gitignore | 3 + doc/Makefile.am | 29 + doc/libevdev.css | 227 +++++ doc/libevdev.doxygen.in | 1854 +++++++++++++++++++++++++++++++++++ doc/libevdev.man.in | 32 + include/linux/input.h | 1173 ++++++++++++++++++++++ libevdev.pc.in | 10 + libevdev/.gitignore | 1 + libevdev/Makefile.am | 32 + libevdev/libevdev-int.h | 335 +++++++ libevdev/libevdev-names.c | 143 +++ libevdev/libevdev-uinput-int.h | 31 + libevdev/libevdev-uinput.c | 381 ++++++++ libevdev/libevdev-uinput.h | 236 +++++ libevdev/libevdev-util.h | 82 ++ libevdev/libevdev.c | 1650 +++++++++++++++++++++++++++++++ libevdev/libevdev.h | 2031 ++++++++++++++++++++++++++++++++++++++ libevdev/libevdev.sym | 104 ++ libevdev/make-event-names.py | 202 ++++ m4/.gitignore | 5 + m4/attributes.m4 | 288 ++++++ packaging/libevdev.spec | 57 ++ test/.gitignore | 5 + test/Makefile.am | 114 +++ test/test-common-uinput.c | 299 ++++++ test/test-common-uinput.h | 49 + test/test-common.c | 122 +++ test/test-common.h | 55 ++ test/test-compile-pedantic.c | 6 + test/test-event-codes.c | 119 +++ test/test-event-names.c | 301 ++++++ test/test-int-queue.c | 360 +++++++ test/test-kernel.c | 185 ++++ test/test-libevdev-events.c | 2115 ++++++++++++++++++++++++++++++++++++++++ test/test-libevdev-has-event.c | 1166 ++++++++++++++++++++++ test/test-libevdev-init.c | 497 ++++++++++ test/test-link.c | 6 + test/test-main.c | 92 ++ test/test-uinput.c | 393 ++++++++ test/valgrind.suppressions | 22 + tools/.gitignore | 2 + tools/Makefile.am | 12 + tools/libevdev-events.c | 195 ++++ tools/publish-doc | 7 + tools/touchpad-edge-detector.c | 196 ++++ 51 files changed, 15511 insertions(+) create mode 100644 .gitignore create mode 100644 COPYING create mode 100644 Makefile.am create mode 100644 README.md create mode 100755 autogen.sh create mode 100644 configure.ac create mode 100644 doc/.gitignore create mode 100644 doc/Makefile.am create mode 100644 doc/libevdev.css create mode 100644 doc/libevdev.doxygen.in create mode 100644 doc/libevdev.man.in create mode 100644 include/linux/input.h create mode 100644 libevdev.pc.in create mode 100644 libevdev/.gitignore create mode 100644 libevdev/Makefile.am create mode 100644 libevdev/libevdev-int.h create mode 100644 libevdev/libevdev-names.c create mode 100644 libevdev/libevdev-uinput-int.h create mode 100644 libevdev/libevdev-uinput.c create mode 100644 libevdev/libevdev-uinput.h create mode 100644 libevdev/libevdev-util.h create mode 100644 libevdev/libevdev.c create mode 100644 libevdev/libevdev.h create mode 100644 libevdev/libevdev.sym create mode 100755 libevdev/make-event-names.py create mode 100644 m4/.gitignore create mode 100644 m4/attributes.m4 create mode 100644 packaging/libevdev.spec create mode 100644 test/.gitignore create mode 100644 test/Makefile.am create mode 100644 test/test-common-uinput.c create mode 100644 test/test-common-uinput.h create mode 100644 test/test-common.c create mode 100644 test/test-common.h create mode 100644 test/test-compile-pedantic.c create mode 100644 test/test-event-codes.c create mode 100644 test/test-event-names.c create mode 100644 test/test-int-queue.c create mode 100644 test/test-kernel.c create mode 100644 test/test-libevdev-events.c create mode 100644 test/test-libevdev-has-event.c create mode 100644 test/test-libevdev-init.c create mode 100644 test/test-link.c create mode 100644 test/test-main.c create mode 100644 test/test-uinput.c create mode 100644 test/valgrind.suppressions create mode 100644 tools/.gitignore create mode 100644 tools/Makefile.am create mode 100644 tools/libevdev-events.c create mode 100755 tools/publish-doc create mode 100644 tools/touchpad-edge-detector.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..957e4c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,70 @@ +# GNU Build System (Autotools) +aclocal.m4 +*.announce +autom4te.cache/ +autoscan.log +build-aux/ +ChangeLog +compile +config.cache +config.guess +config.h +config.h.in +config.log +config-ml.in +config.py +config.status +config.status.lineno +config.sub +configure +configure.scan +depcomp +.dirstamp +install-sh +libtool +ltmain.sh +Makefile +Makefile.in +mdate-sh +missing +mkinstalldirs +stamp-h? +# Edit Compile Debug Document Distribute +*~ +*.[0-9] +*.[0-9]x +*.bak +*.bin +core +.deps/ +*.dll +*.exe +*.gcda +*.gcno +*.gcov +*.kld +*.ko +*.ko.cmd +*.lai +.libs/ +*.l[oa] +*.[oa] +*.objq +*.patch +*.pc +*.pdb +*.pyc +py-compile +*.pyo +*.so +*.swo +*.swp +symlink-tree +tags +*.tar.bz2 +*.tar.gz +*.tar.xz +texinfo.tex +.vimdir +ylwrap +# Application Specific Files diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..16f959a --- /dev/null +++ b/COPYING @@ -0,0 +1,29 @@ +Copyright © 2013 Red Hat, Inc. +Copyright © 2013 David Herrmann + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that copyright +notice and this permission notice appear in supporting documentation, and +that the name of the copyright holders not be used in advertising or +publicity pertaining to distribution of the software without specific, +written prior permission. The copyright holders make no representations +about the suitability of this software for any purpose. It is provided "as +is" without express or implied warranty. + +THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +OF THIS SOFTWARE. + +The following license is from a Linux kernel header file and there is no GPL +code this package links to. + +Copyright (c) 1999-2002 Vojtech Pavlik + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 as published by +the Free Software Foundation. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..95a16ea --- /dev/null +++ b/Makefile.am @@ -0,0 +1,11 @@ +ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} +PRINT_DIRECTORY_FLAGS_1= +PRINT_DIRECTORY_FLAGS_0=--no-print-directory +PRINT_DIRECTORY_FLAGS_=$(PRINT_DIRECTORY_FLAGS_$(AM_DEFAULT_VERBOSITY)) +AM_MAKEFLAGS = $(PRINT_DIRECTORY_FLAGS_$(V)) +SUBDIRS = doc libevdev tools test + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libevdev.pc + +EXTRA_DIST = libevdev.pc.in diff --git a/README.md b/README.md new file mode 100644 index 0000000..499d2d6 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +libevdev - wrapper library for evdev input devices +================================================== + +libevdev is a wrapper library for evdev devices. it moves the common +tasks when dealing with evdev devices into a library and provides a library +interface to the callers, thus avoiding erroneous ioctls, etc. + +git://git.freedesktop.org/git/libevdev +http://cgit.freedesktop.org/libevdev/ + +The eventual goal is that libevdev wraps all ioctls available to evdev +devices, thus making direct access unnecessary. + +Go here for the API documentation: +http://www.freedesktop.org/software/libevdev/doc/latest/ + +File bugs in the freedesktop.org bugzilla: +https://bugs.freedesktop.org/enter_bug.cgi?product=libevdev + +Patches, questions and general comments should be submitted to the input-tools@lists.freedesktop.org +mailing list: +http://lists.freedesktop.org/mailman/listinfo/input-tools diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..dcf3ccf --- /dev/null +++ b/autogen.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +test -n "$srcdir" || srcdir=`dirname "$0"` +test -n "$srcdir" || srcdir=. + +olddir=`pwd` + +cd "$srcdir" +autoreconf -fvi || exit $? + +cd "$olddir" +test -n "$NOCONFIGURE" || $srcdir/configure "$@" diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..dc17109 --- /dev/null +++ b/configure.ac @@ -0,0 +1,143 @@ +# Copyright © 2013 Red Hat, Inc. +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that copyright +# notice and this permission notice appear in supporting documentation, and +# that the name of the copyright holders not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. The copyright holders make no representations +# about the suitability of this software for any purpose. It is provided "as +# is" without express or implied warranty. +# +# THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +# EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR +# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +# DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. + +AC_PREREQ([2.62]) + +AC_INIT([libevdev], + [1.2.2], + [https://bugs.freedesktop.org/enter_bug.cgi?product=libevdev], + [libevdev], + [http://freedesktop.org/wiki/Software/libevdev/]) + +AC_CONFIG_SRCDIR([libevdev/libevdev.c]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_USE_SYSTEM_EXTENSIONS + +AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz]) + +# Before making a release, the LIBEVDEV_LT_VERSION string should be +# modified. +# The string is of the form C:R:A. +# - If interfaces have been changed or added, but binary compatibility has +# been preserved, change to C+1:0:A+1 +# - If binary compatibility has been broken (eg removed or changed interfaces) +# change to C+1:0:0 +# - If the interface is the same as the previous version, change to C:R+1:A +LIBEVDEV_LT_VERSION=2:3:0 +AC_SUBST(LIBEVDEV_LT_VERSION) + + +AM_SILENT_RULES([yes]) + +# Check for programs +AC_PROG_CC_C99 + +# Initialize libtool +LT_PREREQ([2.2]) +LT_INIT +LT_PATH_LD + +with_ldflags="" +if test "x$lt_cv_prog_gnu_ld" = "xyes"; then + CC_CHECK_FLAGS_APPEND([with_ldflags], [LDFLAGS], [\ + -Wl,--as-needed \ + -Wl,--gc-sections \ + -Wl,-z,relro \ + -Wl,-z,now]) +fi +AC_SUBST([GNU_LD_FLAGS], $with_ldflags) + +PKG_PROG_PKG_CONFIG() +PKG_CHECK_MODULES(CHECK, [check >= 0.9.9], [HAVE_CHECK="yes"], [HAVE_CHECK="no"]) +if test "x$HAVE_CHECK" = "xyes"; then + AC_PATH_PROG(VALGRIND, [valgrind]) +else + AC_MSG_WARN([check not found - skipping building unit tests]) +fi +AM_CONDITIONAL(HAVE_VALGRIND, [test "x$VALGRIND" != "x"]) +AM_CONDITIONAL(BUILD_TESTS, [test "x$HAVE_CHECK" = "xyes"]) + +with_cflags="" +if test "x$GCC" = "xyes"; then + CC_CHECK_FLAGS_APPEND([with_cflags], [CFLAGS], [\ + -Wall \ + -Wextra \ + -Wno-unused-parameter \ + -Wstrict-prototypes \ + -Wmissing-prototypes \ + -fvisibility=hidden \ + -pipe \ + -fno-strict-aliasing \ + -ffunction-sections \ + -fdata-sections \ + -fno-strict-aliasing \ + -fdiagnostics-show-option \ + -fno-common]) +fi +AC_SUBST([GCC_CFLAGS], $with_cflags) + +AC_PATH_PROG(DOXYGEN, [doxygen]) +if test "x$DOXYGEN" = "x"; then + AC_MSG_WARN([doxygen not found - required for documentation]) + have_doxygen="no" +else + have_doxygen="yes" +fi +AM_CONDITIONAL([HAVE_DOXYGEN], [test "x$have_doxygen" = "xyes"]) + +AC_MSG_CHECKING([whether to build with gcov]) +AC_ARG_ENABLE([gcov], + [AS_HELP_STRING([--enable-gcov], + [Whether to enable coverage testing (default:disabled)])], + [], + [enable_gcov=no], + ) +AS_IF([test "x$enable_gcov" != "xno"], + [ + GCOV_CFLAGS="-fprofile-arcs -ftest-coverage" + GCOV_LDFLAGS="-fprofile-arcs -ftest-coverage" + enable_gcov=yes + ], +) + +AM_PATH_PYTHON([2.6]) + +AM_CONDITIONAL([GCOV_ENABLED], [test "x$enable_gcov" != "xno"]) +AC_SUBST([GCOV_CFLAGS]) +AC_SUBST([GCOV_LDFLAGS]) +AC_MSG_RESULT([$enable_gcov]) + +AC_CONFIG_FILES([Makefile + libevdev/Makefile + doc/Makefile + doc/libevdev.doxygen + doc/libevdev.man + tools/Makefile + test/Makefile + libevdev.pc]) +AC_OUTPUT + +AC_MSG_RESULT([ + Build documentation ${have_doxygen} + Build unit-tests ${HAVE_CHECK} + Enable profiling ${enable_gcov} + ]) diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 0000000..20b4ef5 --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,3 @@ +html/ +libevdev.doxygen +libevdev.man diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 0000000..c1a06aa --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,29 @@ +man3_MANS = libevdev.man + +if HAVE_DOXYGEN + +noinst_DATA = html/index.html + +header_files = \ + $(top_srcdir)/libevdev/libevdev.h \ + $(top_srcdir)/libevdev/libevdev-uinput.h + +html/index.html: libevdev.doxygen $(header_files) + $(AM_V_GEN)$(DOXYGEN) $< + +clean-local: + $(AM_V_at)rm -rf html + +doc_src= $(shell find html -type f -printf "html/%P\n" 2>/dev/null) +EXTRA_DIST = html/index.html $(doc_src) libevdev.css + +endif + +# make sure doc was built before running dist +dist-hook: + @test -f $(distdir)/html/index.html || (\ + echo "******************************************************" && \ + echo "Couldn't find documentation files, refusing make dist." && \ + echo "Install doxygen to build documentation for tarball." && \ + echo "******************************************************" && \ + test ) diff --git a/doc/libevdev.css b/doc/libevdev.css new file mode 100644 index 0000000..851b154 --- /dev/null +++ b/doc/libevdev.css @@ -0,0 +1,227 @@ +h1 { + font-size: 150%; + color: #354C7B; + border-bottom: 1px solid #879ECB; + font-weight: normal; + padding-bottom: 4px; + padding-bottom: 8px; +} + +#titlearea { + width: 30%; + margin-left: auto; + margin-right: auto; + padding: 0px 10px 15px 10px; + border: none; + border-bottom: 1px solid #879ECB; +} + +#projectname { + text-align: center; + font-weight: bold; + font-size: 300%; + margin-top: 5px; + padding: 2px 0 0 0; + margin-left: auto; + margin-right: auto; + color: #354C7B; +} + +#projectnumber { + font-size: 100%; + color: #354C7B; +} + +#projectbrief { + text-align: center; + margin-left: 20px; + margin-top: 5px; + padding: 2px 0 0 0; + margin-left: auto; + margin-right: auto; + color: #354C7B; +} + +#MSearchBox { + display: none; +} + +#titlearea table { + margin-left: auto; + margin-right: auto; +} + +#navrow1, #navrow2, #navrow3, #navrow4, #navpath { + width: 600px; + width: -moz-max-content; + margin-left: auto; + margin-right: auto; +} + +/* in file list, appears under the nav bars */ +.navpath ul, .navpath li { + width: 600px; + width: -moz-max-content; + margin-left: auto; + margin-right: auto; + background-image: none; + border: none; + border-bottom: 1px solid; +} + +.navpath li.navelem a { + text-shadow: none; + outline: none; +} + +.tabs, .tabs2, .tabs3 { + background-image: none; +} + +/* main page/modules/files tabs */ +.tablist li { + background-image: none; +} + +/* main page/modules/files link text */ +.tablist a { + background-image: none; + background-repeat: none; +} + +/* main page/modules/files link text when hovering */ +.tablist a:hover { + background-image: none; + background-repeat: none; + text-shadow: none; + color: black; +} + +/* main page/modules/files currently selected */ +.tablist li.current a { + background-image: none; + text-shadow: none; + color: black; + border-bottom: 1px solid; +} + +.navpath { + background-image: none; +} + +/* libevdev documentation/modules/file list ... superfluous header */ +div.header { + display: none; + width: -moz-max-content; + margin-left: auto; + margin-right: auto; + background-image: none; + background-color: inherit; + font-size: 300%; +} + +/* general text blocks */ +.textblock { + width: 600px; + margin-left: auto; + margin-right: auto; +} + +/* code fragments should expand to what's needed */ +.fragment { + width: -moz-max-content; +} + +/* list of modules container */ +div .directory{ + margin-left: auto; + margin-right: auto; + width: 600px; + border: none; +} + + +.directory td.entry { + width: 40%; + white-space: normal; + padding-left: 5px; + +} + +.directory td.desc { + width: 60%; +} + +.directory td.entry img { + display: none; +} + +h2.groupheader { + width: -moz-max-content; +} + +/* table for list of functions */ +table.memberdecls { + width: -moz-max-content; +} + +div.memitem { + width: -moz-max-content; + border-bottom: 1px solid; +} + +/* function prototype */ +div.memproto { + background-image: none; + width: 600px; + border: none; + box-shadow: none; +} + +/* function documentation */ +div.memdoc { + background-image: none; + width: -moz-max-content; + box-shadow: none; + border: none; +} + +div.contents { + margin-left: auto; + margin-right: auto; + width: 600px; + width: -moz-max-content; +} + +p { + width: 580px; +} + +dl.return { + width: 480px; +} + +code { + background-color: #F9FAFC; +} + + + +.footer { + width: 600px; + margin-left: auto; + margin-right: auto; +} + +img.footer { + width: auto; +} + +/* note, see also, returns */ +dl.section { + width: 560px; +} + +table.params { + width: 560px; +} diff --git a/doc/libevdev.doxygen.in b/doc/libevdev.doxygen.in new file mode 100644 index 0000000..853c47d --- /dev/null +++ b/doc/libevdev.doxygen.in @@ -0,0 +1,1854 @@ +# Doxyfile 1.8.3.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = @PACKAGE_NAME@ + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = @PACKAGE_VERSION@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "A wrapper library for evdev devices" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. Note that you specify absolute paths here, but also +# relative paths, which will be relative from the directory where doxygen is +# started. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, +# and language is one of the parsers supported by doxygen: IDL, Java, +# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, +# C++. For instance to make doxygen treat .inc files as Fortran files (default +# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note +# that for custom extensions you also need to set FILE_PATTERNS otherwise the +# files are not read by doxygen. + +EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented classes, +# or namespaces to their corresponding documentation. Such a link can be +# prevented in individual cases by by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES (the +# default) will make doxygen replace the get and set methods by a property in +# the documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if section-label ... \endif +# and \cond section-label ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 0 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. Do not use +# file names with spaces, bibtex cannot handle them. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = @top_srcdir@/libevdev/libevdev.h \ + @top_srcdir@/libevdev/libevdev-uinput.h + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = @top_srcdir@/include + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page (index.html). +# This can be useful if you have a project on for instance GitHub and want reuse +# the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C, C++ and Fortran comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If left blank doxygen will +# generate a default style sheet. Note that it is recommended to use +# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this +# tag will in the future become obsolete. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional +# user-defined cascading style sheet that is included after the standard +# style sheets created by doxygen. Using this option one can overrule +# certain style aspects. This is preferred over using HTML_STYLESHEET +# since it does not replace the standard style sheet and is therefor more +# robust against future updates. Doxygen will copy the style sheet file to +# the output directory. + +HTML_EXTRA_STYLESHEET = @srcdir@/libevdev.css + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely +# identify the documentation publisher. This should be a reverse domain-name +# style string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and +# SVG. The default value is HTML-CSS, which is slower, but has the best +# compatibility. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. +# However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. +# There are two flavours of web server based search depending on the +# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for +# searching and an index file used by the script. When EXTERNAL_SEARCH is +# enabled the indexing and searching needs to be provided by external tools. +# See the manual for details. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain +# the search results. Doxygen ships with an example indexer (doxyindexer) and +# search engine (doxysearch.cgi) which are based on the open source search engine +# library Xapian. See the manual for configuration details. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will returned the search results when EXTERNAL_SEARCH is enabled. +# Doxygen ships with an example search engine (doxysearch) which is based on +# the open source search engine library Xapian. See the manual for configuration +# details. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id +# of to a relative location where the documentation can be found. +# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = YES + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = LIBEVDEV_ATTRIBUTE_PRINTF(f,a)= + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# managable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/doc/libevdev.man.in b/doc/libevdev.man.in new file mode 100644 index 0000000..58a15c7 --- /dev/null +++ b/doc/libevdev.man.in @@ -0,0 +1,32 @@ +.TH LIBEVDEV 3 @PACKAGE_VERSION@ +.SH NAME +libevdev \- wrapper library for evdev devices +.SH SYNOPSIS +.HP +#include +.PP +int +.B libevdev_new_from_fd +(int fd, struct libevdev **device) +.PP +void +.B libevdev_free +(struct libevdev *device) + +.SH DESCRIPTION +.PP +.B libevdev +is a wrapper library for evdev devices. it moves the common +tasks when dealing with evdev devices into a library and provides a library +interface to the callers, thus avoiding erroneous ioctls, etc. +.PP +This man page is a placeholder only. The documentation for this version of +.B libevdev +is available at: +.PP +.B http://www.freedesktop.org/software/libevdev/doc/@PACKAGE_VERSION@/ +.SH LICENSE +.B libevdev +is licensed under the MIT license. + + diff --git a/include/linux/input.h b/include/linux/input.h new file mode 100644 index 0000000..e37293b --- /dev/null +++ b/include/linux/input.h @@ -0,0 +1,1173 @@ +/* + * Copyright (c) 1999-2002 Vojtech Pavlik + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ +#ifndef _INPUT_H +#define _INPUT_H + + +#include +#include +#include +#include + + +/* + * The event structure itself + */ + +struct input_event { + struct timeval time; + __u16 type; + __u16 code; + __s32 value; +}; + +/* + * Protocol version. + */ + +#define EV_VERSION 0x010001 + +/* + * IOCTLs (0x00 - 0x7f) + */ + +struct input_id { + __u16 bustype; + __u16 vendor; + __u16 product; + __u16 version; +}; + +/** + * struct input_absinfo - used by EVIOCGABS/EVIOCSABS ioctls + * @value: latest reported value for the axis. + * @minimum: specifies minimum value for the axis. + * @maximum: specifies maximum value for the axis. + * @fuzz: specifies fuzz value that is used to filter noise from + * the event stream. + * @flat: values that are within this value will be discarded by + * joydev interface and reported as 0 instead. + * @resolution: specifies resolution for the values reported for + * the axis. + * + * Note that input core does not clamp reported values to the + * [minimum, maximum] limits, such task is left to userspace. + * + * Resolution for main axes (ABS_X, ABS_Y, ABS_Z) is reported in + * units per millimeter (units/mm), resolution for rotational axes + * (ABS_RX, ABS_RY, ABS_RZ) is reported in units per radian. + */ +struct input_absinfo { + __s32 value; + __s32 minimum; + __s32 maximum; + __s32 fuzz; + __s32 flat; + __s32 resolution; +}; + +/** + * struct input_keymap_entry - used by EVIOCGKEYCODE/EVIOCSKEYCODE ioctls + * @scancode: scancode represented in machine-endian form. + * @len: length of the scancode that resides in @scancode buffer. + * @index: index in the keymap, may be used instead of scancode + * @flags: allows to specify how kernel should handle the request. For + * example, setting INPUT_KEYMAP_BY_INDEX flag indicates that kernel + * should perform lookup in keymap by @index instead of @scancode + * @keycode: key code assigned to this scancode + * + * The structure is used to retrieve and modify keymap data. Users have + * option of performing lookup either by @scancode itself or by @index + * in keymap entry. EVIOCGKEYCODE will also return scancode or index + * (depending on which element was used to perform lookup). + */ +struct input_keymap_entry { +#define INPUT_KEYMAP_BY_INDEX (1 << 0) + __u8 flags; + __u8 len; + __u16 index; + __u32 keycode; + __u8 scancode[32]; +}; + +#define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */ +#define EVIOCGID _IOR('E', 0x02, struct input_id) /* get device ID */ +#define EVIOCGREP _IOR('E', 0x03, unsigned int[2]) /* get repeat settings */ +#define EVIOCSREP _IOW('E', 0x03, unsigned int[2]) /* set repeat settings */ + +#define EVIOCGKEYCODE _IOR('E', 0x04, unsigned int[2]) /* get keycode */ +#define EVIOCGKEYCODE_V2 _IOR('E', 0x04, struct input_keymap_entry) +#define EVIOCSKEYCODE _IOW('E', 0x04, unsigned int[2]) /* set keycode */ +#define EVIOCSKEYCODE_V2 _IOW('E', 0x04, struct input_keymap_entry) + +#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */ +#define EVIOCGPHYS(len) _IOC(_IOC_READ, 'E', 0x07, len) /* get physical location */ +#define EVIOCGUNIQ(len) _IOC(_IOC_READ, 'E', 0x08, len) /* get unique identifier */ +#define EVIOCGPROP(len) _IOC(_IOC_READ, 'E', 0x09, len) /* get device properties */ + +/** + * EVIOCGMTSLOTS(len) - get MT slot values + * @len: size of the data buffer in bytes + * + * The ioctl buffer argument should be binary equivalent to + * + * struct input_mt_request_layout { + * __u32 code; + * __s32 values[num_slots]; + * }; + * + * where num_slots is the (arbitrary) number of MT slots to extract. + * + * The ioctl size argument (len) is the size of the buffer, which + * should satisfy len = (num_slots + 1) * sizeof(__s32). If len is + * too small to fit all available slots, the first num_slots are + * returned. + * + * Before the call, code is set to the wanted ABS_MT event type. On + * return, values[] is filled with the slot values for the specified + * ABS_MT code. + * + * If the request code is not an ABS_MT value, -EINVAL is returned. + */ +#define EVIOCGMTSLOTS(len) _IOC(_IOC_READ, 'E', 0x0a, len) + +#define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len) /* get global key state */ +#define EVIOCGLED(len) _IOC(_IOC_READ, 'E', 0x19, len) /* get all LEDs */ +#define EVIOCGSND(len) _IOC(_IOC_READ, 'E', 0x1a, len) /* get all sounds status */ +#define EVIOCGSW(len) _IOC(_IOC_READ, 'E', 0x1b, len) /* get all switch states */ + +#define EVIOCGBIT(ev,len) _IOC(_IOC_READ, 'E', 0x20 + (ev), len) /* get event bits */ +#define EVIOCGABS(abs) _IOR('E', 0x40 + (abs), struct input_absinfo) /* get abs value/limits */ +#define EVIOCSABS(abs) _IOW('E', 0xc0 + (abs), struct input_absinfo) /* set abs value/limits */ + +#define EVIOCSFF _IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect)) /* send a force effect to a force feedback device */ +#define EVIOCRMFF _IOW('E', 0x81, int) /* Erase a force effect */ +#define EVIOCGEFFECTS _IOR('E', 0x84, int) /* Report number of effects playable at the same time */ + +#define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */ +#define EVIOCREVOKE _IOW('E', 0x91, int) /* Revoke device access */ + +#define EVIOCSCLOCKID _IOW('E', 0xa0, int) /* Set clockid to be used for timestamps */ + +/* + * Device properties and quirks + */ + +#define INPUT_PROP_POINTER 0x00 /* needs a pointer */ +#define INPUT_PROP_DIRECT 0x01 /* direct input devices */ +#define INPUT_PROP_BUTTONPAD 0x02 /* has button(s) under pad */ +#define INPUT_PROP_SEMI_MT 0x03 /* touch rectangle only */ + +#define INPUT_PROP_MAX 0x1f +#define INPUT_PROP_CNT (INPUT_PROP_MAX + 1) + +/* + * Event types + */ + +#define EV_SYN 0x00 +#define EV_KEY 0x01 +#define EV_REL 0x02 +#define EV_ABS 0x03 +#define EV_MSC 0x04 +#define EV_SW 0x05 +#define EV_LED 0x11 +#define EV_SND 0x12 +#define EV_REP 0x14 +#define EV_FF 0x15 +#define EV_PWR 0x16 +#define EV_FF_STATUS 0x17 +#define EV_MAX 0x1f +#define EV_CNT (EV_MAX+1) + +/* + * Synchronization events. + */ + +#define SYN_REPORT 0 +#define SYN_CONFIG 1 +#define SYN_MT_REPORT 2 +#define SYN_DROPPED 3 +#define SYN_MAX 0xf +#define SYN_CNT (SYN_MAX+1) + +/* + * Keys and buttons + * + * Most of the keys/buttons are modeled after USB HUT 1.12 + * (see http://www.usb.org/developers/hidpage). + * Abbreviations in the comments: + * AC - Application Control + * AL - Application Launch Button + * SC - System Control + */ + +#define KEY_RESERVED 0 +#define KEY_ESC 1 +#define KEY_1 2 +#define KEY_2 3 +#define KEY_3 4 +#define KEY_4 5 +#define KEY_5 6 +#define KEY_6 7 +#define KEY_7 8 +#define KEY_8 9 +#define KEY_9 10 +#define KEY_0 11 +#define KEY_MINUS 12 +#define KEY_EQUAL 13 +#define KEY_BACKSPACE 14 +#define KEY_TAB 15 +#define KEY_Q 16 +#define KEY_W 17 +#define KEY_E 18 +#define KEY_R 19 +#define KEY_T 20 +#define KEY_Y 21 +#define KEY_U 22 +#define KEY_I 23 +#define KEY_O 24 +#define KEY_P 25 +#define KEY_LEFTBRACE 26 +#define KEY_RIGHTBRACE 27 +#define KEY_ENTER 28 +#define KEY_LEFTCTRL 29 +#define KEY_A 30 +#define KEY_S 31 +#define KEY_D 32 +#define KEY_F 33 +#define KEY_G 34 +#define KEY_H 35 +#define KEY_J 36 +#define KEY_K 37 +#define KEY_L 38 +#define KEY_SEMICOLON 39 +#define KEY_APOSTROPHE 40 +#define KEY_GRAVE 41 +#define KEY_LEFTSHIFT 42 +#define KEY_BACKSLASH 43 +#define KEY_Z 44 +#define KEY_X 45 +#define KEY_C 46 +#define KEY_V 47 +#define KEY_B 48 +#define KEY_N 49 +#define KEY_M 50 +#define KEY_COMMA 51 +#define KEY_DOT 52 +#define KEY_SLASH 53 +#define KEY_RIGHTSHIFT 54 +#define KEY_KPASTERISK 55 +#define KEY_LEFTALT 56 +#define KEY_SPACE 57 +#define KEY_CAPSLOCK 58 +#define KEY_F1 59 +#define KEY_F2 60 +#define KEY_F3 61 +#define KEY_F4 62 +#define KEY_F5 63 +#define KEY_F6 64 +#define KEY_F7 65 +#define KEY_F8 66 +#define KEY_F9 67 +#define KEY_F10 68 +#define KEY_NUMLOCK 69 +#define KEY_SCROLLLOCK 70 +#define KEY_KP7 71 +#define KEY_KP8 72 +#define KEY_KP9 73 +#define KEY_KPMINUS 74 +#define KEY_KP4 75 +#define KEY_KP5 76 +#define KEY_KP6 77 +#define KEY_KPPLUS 78 +#define KEY_KP1 79 +#define KEY_KP2 80 +#define KEY_KP3 81 +#define KEY_KP0 82 +#define KEY_KPDOT 83 + +#define KEY_ZENKAKUHANKAKU 85 +#define KEY_102ND 86 +#define KEY_F11 87 +#define KEY_F12 88 +#define KEY_RO 89 +#define KEY_KATAKANA 90 +#define KEY_HIRAGANA 91 +#define KEY_HENKAN 92 +#define KEY_KATAKANAHIRAGANA 93 +#define KEY_MUHENKAN 94 +#define KEY_KPJPCOMMA 95 +#define KEY_KPENTER 96 +#define KEY_RIGHTCTRL 97 +#define KEY_KPSLASH 98 +#define KEY_SYSRQ 99 +#define KEY_RIGHTALT 100 +#define KEY_LINEFEED 101 +#define KEY_HOME 102 +#define KEY_UP 103 +#define KEY_PAGEUP 104 +#define KEY_LEFT 105 +#define KEY_RIGHT 106 +#define KEY_END 107 +#define KEY_DOWN 108 +#define KEY_PAGEDOWN 109 +#define KEY_INSERT 110 +#define KEY_DELETE 111 +#define KEY_MACRO 112 +#define KEY_MUTE 113 +#define KEY_VOLUMEDOWN 114 +#define KEY_VOLUMEUP 115 +#define KEY_POWER 116 /* SC System Power Down */ +#define KEY_KPEQUAL 117 +#define KEY_KPPLUSMINUS 118 +#define KEY_PAUSE 119 +#define KEY_SCALE 120 /* AL Compiz Scale (Expose) */ + +#define KEY_KPCOMMA 121 +#define KEY_HANGEUL 122 +#define KEY_HANGUEL KEY_HANGEUL +#define KEY_HANJA 123 +#define KEY_YEN 124 +#define KEY_LEFTMETA 125 +#define KEY_RIGHTMETA 126 +#define KEY_COMPOSE 127 + +#define KEY_STOP 128 /* AC Stop */ +#define KEY_AGAIN 129 +#define KEY_PROPS 130 /* AC Properties */ +#define KEY_UNDO 131 /* AC Undo */ +#define KEY_FRONT 132 +#define KEY_COPY 133 /* AC Copy */ +#define KEY_OPEN 134 /* AC Open */ +#define KEY_PASTE 135 /* AC Paste */ +#define KEY_FIND 136 /* AC Search */ +#define KEY_CUT 137 /* AC Cut */ +#define KEY_HELP 138 /* AL Integrated Help Center */ +#define KEY_MENU 139 /* Menu (show menu) */ +#define KEY_CALC 140 /* AL Calculator */ +#define KEY_SETUP 141 +#define KEY_SLEEP 142 /* SC System Sleep */ +#define KEY_WAKEUP 143 /* System Wake Up */ +#define KEY_FILE 144 /* AL Local Machine Browser */ +#define KEY_SENDFILE 145 +#define KEY_DELETEFILE 146 +#define KEY_XFER 147 +#define KEY_PROG1 148 +#define KEY_PROG2 149 +#define KEY_WWW 150 /* AL Internet Browser */ +#define KEY_MSDOS 151 +#define KEY_COFFEE 152 /* AL Terminal Lock/Screensaver */ +#define KEY_SCREENLOCK KEY_COFFEE +#define KEY_DIRECTION 153 +#define KEY_CYCLEWINDOWS 154 +#define KEY_MAIL 155 +#define KEY_BOOKMARKS 156 /* AC Bookmarks */ +#define KEY_COMPUTER 157 +#define KEY_BACK 158 /* AC Back */ +#define KEY_FORWARD 159 /* AC Forward */ +#define KEY_CLOSECD 160 +#define KEY_EJECTCD 161 +#define KEY_EJECTCLOSECD 162 +#define KEY_NEXTSONG 163 +#define KEY_PLAYPAUSE 164 +#define KEY_PREVIOUSSONG 165 +#define KEY_STOPCD 166 +#define KEY_RECORD 167 +#define KEY_REWIND 168 +#define KEY_PHONE 169 /* Media Select Telephone */ +#define KEY_ISO 170 +#define KEY_CONFIG 171 /* AL Consumer Control Configuration */ +#define KEY_HOMEPAGE 172 /* AC Home */ +#define KEY_REFRESH 173 /* AC Refresh */ +#define KEY_EXIT 174 /* AC Exit */ +#define KEY_MOVE 175 +#define KEY_EDIT 176 +#define KEY_SCROLLUP 177 +#define KEY_SCROLLDOWN 178 +#define KEY_KPLEFTPAREN 179 +#define KEY_KPRIGHTPAREN 180 +#define KEY_NEW 181 /* AC New */ +#define KEY_REDO 182 /* AC Redo/Repeat */ + +#define KEY_F13 183 +#define KEY_F14 184 +#define KEY_F15 185 +#define KEY_F16 186 +#define KEY_F17 187 +#define KEY_F18 188 +#define KEY_F19 189 +#define KEY_F20 190 +#define KEY_F21 191 +#define KEY_F22 192 +#define KEY_F23 193 +#define KEY_F24 194 + +#define KEY_PLAYCD 200 +#define KEY_PAUSECD 201 +#define KEY_PROG3 202 +#define KEY_PROG4 203 +#define KEY_DASHBOARD 204 /* AL Dashboard */ +#define KEY_SUSPEND 205 +#define KEY_CLOSE 206 /* AC Close */ +#define KEY_PLAY 207 +#define KEY_FASTFORWARD 208 +#define KEY_BASSBOOST 209 +#define KEY_PRINT 210 /* AC Print */ +#define KEY_HP 211 +#define KEY_CAMERA 212 +#define KEY_SOUND 213 +#define KEY_QUESTION 214 +#define KEY_EMAIL 215 +#define KEY_CHAT 216 +#define KEY_SEARCH 217 +#define KEY_CONNECT 218 +#define KEY_FINANCE 219 /* AL Checkbook/Finance */ +#define KEY_SPORT 220 +#define KEY_SHOP 221 +#define KEY_ALTERASE 222 +#define KEY_CANCEL 223 /* AC Cancel */ +#define KEY_BRIGHTNESSDOWN 224 +#define KEY_BRIGHTNESSUP 225 +#define KEY_MEDIA 226 + +#define KEY_SWITCHVIDEOMODE 227 /* Cycle between available video + outputs (Monitor/LCD/TV-out/etc) */ +#define KEY_KBDILLUMTOGGLE 228 +#define KEY_KBDILLUMDOWN 229 +#define KEY_KBDILLUMUP 230 + +#define KEY_SEND 231 /* AC Send */ +#define KEY_REPLY 232 /* AC Reply */ +#define KEY_FORWARDMAIL 233 /* AC Forward Msg */ +#define KEY_SAVE 234 /* AC Save */ +#define KEY_DOCUMENTS 235 + +#define KEY_BATTERY 236 + +#define KEY_BLUETOOTH 237 +#define KEY_WLAN 238 +#define KEY_UWB 239 + +#define KEY_UNKNOWN 240 + +#define KEY_VIDEO_NEXT 241 /* drive next video source */ +#define KEY_VIDEO_PREV 242 /* drive previous video source */ +#define KEY_BRIGHTNESS_CYCLE 243 /* brightness up, after max is min */ +#define KEY_BRIGHTNESS_ZERO 244 /* brightness off, use ambient */ +#define KEY_DISPLAY_OFF 245 /* display device to off state */ + +#define KEY_WWAN 246 /* Wireless WAN (LTE, UMTS, GSM, etc.) */ +#define KEY_WIMAX KEY_WWAN +#define KEY_RFKILL 247 /* Key that controls all radios */ + +#define KEY_MICMUTE 248 /* Mute / unmute the microphone */ + +/* Code 255 is reserved for special needs of AT keyboard driver */ + +#define BTN_MISC 0x100 +#define BTN_0 0x100 +#define BTN_1 0x101 +#define BTN_2 0x102 +#define BTN_3 0x103 +#define BTN_4 0x104 +#define BTN_5 0x105 +#define BTN_6 0x106 +#define BTN_7 0x107 +#define BTN_8 0x108 +#define BTN_9 0x109 + +#define BTN_MOUSE 0x110 +#define BTN_LEFT 0x110 +#define BTN_RIGHT 0x111 +#define BTN_MIDDLE 0x112 +#define BTN_SIDE 0x113 +#define BTN_EXTRA 0x114 +#define BTN_FORWARD 0x115 +#define BTN_BACK 0x116 +#define BTN_TASK 0x117 + +#define BTN_JOYSTICK 0x120 +#define BTN_TRIGGER 0x120 +#define BTN_THUMB 0x121 +#define BTN_THUMB2 0x122 +#define BTN_TOP 0x123 +#define BTN_TOP2 0x124 +#define BTN_PINKIE 0x125 +#define BTN_BASE 0x126 +#define BTN_BASE2 0x127 +#define BTN_BASE3 0x128 +#define BTN_BASE4 0x129 +#define BTN_BASE5 0x12a +#define BTN_BASE6 0x12b +#define BTN_DEAD 0x12f + +#define BTN_GAMEPAD 0x130 +#define BTN_SOUTH 0x130 +#define BTN_A BTN_SOUTH +#define BTN_EAST 0x131 +#define BTN_B BTN_EAST +#define BTN_C 0x132 +#define BTN_NORTH 0x133 +#define BTN_X BTN_NORTH +#define BTN_WEST 0x134 +#define BTN_Y BTN_WEST +#define BTN_Z 0x135 +#define BTN_TL 0x136 +#define BTN_TR 0x137 +#define BTN_TL2 0x138 +#define BTN_TR2 0x139 +#define BTN_SELECT 0x13a +#define BTN_START 0x13b +#define BTN_MODE 0x13c +#define BTN_THUMBL 0x13d +#define BTN_THUMBR 0x13e + +#define BTN_DIGI 0x140 +#define BTN_TOOL_PEN 0x140 +#define BTN_TOOL_RUBBER 0x141 +#define BTN_TOOL_BRUSH 0x142 +#define BTN_TOOL_PENCIL 0x143 +#define BTN_TOOL_AIRBRUSH 0x144 +#define BTN_TOOL_FINGER 0x145 +#define BTN_TOOL_MOUSE 0x146 +#define BTN_TOOL_LENS 0x147 +#define BTN_TOOL_QUINTTAP 0x148 /* Five fingers on trackpad */ +#define BTN_TOUCH 0x14a +#define BTN_STYLUS 0x14b +#define BTN_STYLUS2 0x14c +#define BTN_TOOL_DOUBLETAP 0x14d +#define BTN_TOOL_TRIPLETAP 0x14e +#define BTN_TOOL_QUADTAP 0x14f /* Four fingers on trackpad */ + +#define BTN_WHEEL 0x150 +#define BTN_GEAR_DOWN 0x150 +#define BTN_GEAR_UP 0x151 + +#define KEY_OK 0x160 +#define KEY_SELECT 0x161 +#define KEY_GOTO 0x162 +#define KEY_CLEAR 0x163 +#define KEY_POWER2 0x164 +#define KEY_OPTION 0x165 +#define KEY_INFO 0x166 /* AL OEM Features/Tips/Tutorial */ +#define KEY_TIME 0x167 +#define KEY_VENDOR 0x168 +#define KEY_ARCHIVE 0x169 +#define KEY_PROGRAM 0x16a /* Media Select Program Guide */ +#define KEY_CHANNEL 0x16b +#define KEY_FAVORITES 0x16c +#define KEY_EPG 0x16d +#define KEY_PVR 0x16e /* Media Select Home */ +#define KEY_MHP 0x16f +#define KEY_LANGUAGE 0x170 +#define KEY_TITLE 0x171 +#define KEY_SUBTITLE 0x172 +#define KEY_ANGLE 0x173 +#define KEY_ZOOM 0x174 +#define KEY_MODE 0x175 +#define KEY_KEYBOARD 0x176 +#define KEY_SCREEN 0x177 +#define KEY_PC 0x178 /* Media Select Computer */ +#define KEY_TV 0x179 /* Media Select TV */ +#define KEY_TV2 0x17a /* Media Select Cable */ +#define KEY_VCR 0x17b /* Media Select VCR */ +#define KEY_VCR2 0x17c /* VCR Plus */ +#define KEY_SAT 0x17d /* Media Select Satellite */ +#define KEY_SAT2 0x17e +#define KEY_CD 0x17f /* Media Select CD */ +#define KEY_TAPE 0x180 /* Media Select Tape */ +#define KEY_RADIO 0x181 +#define KEY_TUNER 0x182 /* Media Select Tuner */ +#define KEY_PLAYER 0x183 +#define KEY_TEXT 0x184 +#define KEY_DVD 0x185 /* Media Select DVD */ +#define KEY_AUX 0x186 +#define KEY_MP3 0x187 +#define KEY_AUDIO 0x188 /* AL Audio Browser */ +#define KEY_VIDEO 0x189 /* AL Movie Browser */ +#define KEY_DIRECTORY 0x18a +#define KEY_LIST 0x18b +#define KEY_MEMO 0x18c /* Media Select Messages */ +#define KEY_CALENDAR 0x18d +#define KEY_RED 0x18e +#define KEY_GREEN 0x18f +#define KEY_YELLOW 0x190 +#define KEY_BLUE 0x191 +#define KEY_CHANNELUP 0x192 /* Channel Increment */ +#define KEY_CHANNELDOWN 0x193 /* Channel Decrement */ +#define KEY_FIRST 0x194 +#define KEY_LAST 0x195 /* Recall Last */ +#define KEY_AB 0x196 +#define KEY_NEXT 0x197 +#define KEY_RESTART 0x198 +#define KEY_SLOW 0x199 +#define KEY_SHUFFLE 0x19a +#define KEY_BREAK 0x19b +#define KEY_PREVIOUS 0x19c +#define KEY_DIGITS 0x19d +#define KEY_TEEN 0x19e +#define KEY_TWEN 0x19f +#define KEY_VIDEOPHONE 0x1a0 /* Media Select Video Phone */ +#define KEY_GAMES 0x1a1 /* Media Select Games */ +#define KEY_ZOOMIN 0x1a2 /* AC Zoom In */ +#define KEY_ZOOMOUT 0x1a3 /* AC Zoom Out */ +#define KEY_ZOOMRESET 0x1a4 /* AC Zoom */ +#define KEY_WORDPROCESSOR 0x1a5 /* AL Word Processor */ +#define KEY_EDITOR 0x1a6 /* AL Text Editor */ +#define KEY_SPREADSHEET 0x1a7 /* AL Spreadsheet */ +#define KEY_GRAPHICSEDITOR 0x1a8 /* AL Graphics Editor */ +#define KEY_PRESENTATION 0x1a9 /* AL Presentation App */ +#define KEY_DATABASE 0x1aa /* AL Database App */ +#define KEY_NEWS 0x1ab /* AL Newsreader */ +#define KEY_VOICEMAIL 0x1ac /* AL Voicemail */ +#define KEY_ADDRESSBOOK 0x1ad /* AL Contacts/Address Book */ +#define KEY_MESSENGER 0x1ae /* AL Instant Messaging */ +#define KEY_DISPLAYTOGGLE 0x1af /* Turn display (LCD) on and off */ +#define KEY_SPELLCHECK 0x1b0 /* AL Spell Check */ +#define KEY_LOGOFF 0x1b1 /* AL Logoff */ + +#define KEY_DOLLAR 0x1b2 +#define KEY_EURO 0x1b3 + +#define KEY_FRAMEBACK 0x1b4 /* Consumer - transport controls */ +#define KEY_FRAMEFORWARD 0x1b5 +#define KEY_CONTEXT_MENU 0x1b6 /* GenDesc - system context menu */ +#define KEY_MEDIA_REPEAT 0x1b7 /* Consumer - transport control */ +#define KEY_10CHANNELSUP 0x1b8 /* 10 channels up (10+) */ +#define KEY_10CHANNELSDOWN 0x1b9 /* 10 channels down (10-) */ +#define KEY_IMAGES 0x1ba /* AL Image Browser */ + +#define KEY_DEL_EOL 0x1c0 +#define KEY_DEL_EOS 0x1c1 +#define KEY_INS_LINE 0x1c2 +#define KEY_DEL_LINE 0x1c3 + +#define KEY_FN 0x1d0 +#define KEY_FN_ESC 0x1d1 +#define KEY_FN_F1 0x1d2 +#define KEY_FN_F2 0x1d3 +#define KEY_FN_F3 0x1d4 +#define KEY_FN_F4 0x1d5 +#define KEY_FN_F5 0x1d6 +#define KEY_FN_F6 0x1d7 +#define KEY_FN_F7 0x1d8 +#define KEY_FN_F8 0x1d9 +#define KEY_FN_F9 0x1da +#define KEY_FN_F10 0x1db +#define KEY_FN_F11 0x1dc +#define KEY_FN_F12 0x1dd +#define KEY_FN_1 0x1de +#define KEY_FN_2 0x1df +#define KEY_FN_D 0x1e0 +#define KEY_FN_E 0x1e1 +#define KEY_FN_F 0x1e2 +#define KEY_FN_S 0x1e3 +#define KEY_FN_B 0x1e4 + +#define KEY_BRL_DOT1 0x1f1 +#define KEY_BRL_DOT2 0x1f2 +#define KEY_BRL_DOT3 0x1f3 +#define KEY_BRL_DOT4 0x1f4 +#define KEY_BRL_DOT5 0x1f5 +#define KEY_BRL_DOT6 0x1f6 +#define KEY_BRL_DOT7 0x1f7 +#define KEY_BRL_DOT8 0x1f8 +#define KEY_BRL_DOT9 0x1f9 +#define KEY_BRL_DOT10 0x1fa + +#define KEY_NUMERIC_0 0x200 /* used by phones, remote controls, */ +#define KEY_NUMERIC_1 0x201 /* and other keypads */ +#define KEY_NUMERIC_2 0x202 +#define KEY_NUMERIC_3 0x203 +#define KEY_NUMERIC_4 0x204 +#define KEY_NUMERIC_5 0x205 +#define KEY_NUMERIC_6 0x206 +#define KEY_NUMERIC_7 0x207 +#define KEY_NUMERIC_8 0x208 +#define KEY_NUMERIC_9 0x209 +#define KEY_NUMERIC_STAR 0x20a +#define KEY_NUMERIC_POUND 0x20b + +#define KEY_CAMERA_FOCUS 0x210 +#define KEY_WPS_BUTTON 0x211 /* WiFi Protected Setup key */ + +#define KEY_TOUCHPAD_TOGGLE 0x212 /* Request switch touchpad on or off */ +#define KEY_TOUCHPAD_ON 0x213 +#define KEY_TOUCHPAD_OFF 0x214 + +#define KEY_CAMERA_ZOOMIN 0x215 +#define KEY_CAMERA_ZOOMOUT 0x216 +#define KEY_CAMERA_UP 0x217 +#define KEY_CAMERA_DOWN 0x218 +#define KEY_CAMERA_LEFT 0x219 +#define KEY_CAMERA_RIGHT 0x21a + +#define KEY_ATTENDANT_ON 0x21b +#define KEY_ATTENDANT_OFF 0x21c +#define KEY_ATTENDANT_TOGGLE 0x21d /* Attendant call on or off */ +#define KEY_LIGHTS_TOGGLE 0x21e /* Reading light on or off */ + +#define BTN_DPAD_UP 0x220 +#define BTN_DPAD_DOWN 0x221 +#define BTN_DPAD_LEFT 0x222 +#define BTN_DPAD_RIGHT 0x223 + +#define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */ + +#define BTN_TRIGGER_HAPPY 0x2c0 +#define BTN_TRIGGER_HAPPY1 0x2c0 +#define BTN_TRIGGER_HAPPY2 0x2c1 +#define BTN_TRIGGER_HAPPY3 0x2c2 +#define BTN_TRIGGER_HAPPY4 0x2c3 +#define BTN_TRIGGER_HAPPY5 0x2c4 +#define BTN_TRIGGER_HAPPY6 0x2c5 +#define BTN_TRIGGER_HAPPY7 0x2c6 +#define BTN_TRIGGER_HAPPY8 0x2c7 +#define BTN_TRIGGER_HAPPY9 0x2c8 +#define BTN_TRIGGER_HAPPY10 0x2c9 +#define BTN_TRIGGER_HAPPY11 0x2ca +#define BTN_TRIGGER_HAPPY12 0x2cb +#define BTN_TRIGGER_HAPPY13 0x2cc +#define BTN_TRIGGER_HAPPY14 0x2cd +#define BTN_TRIGGER_HAPPY15 0x2ce +#define BTN_TRIGGER_HAPPY16 0x2cf +#define BTN_TRIGGER_HAPPY17 0x2d0 +#define BTN_TRIGGER_HAPPY18 0x2d1 +#define BTN_TRIGGER_HAPPY19 0x2d2 +#define BTN_TRIGGER_HAPPY20 0x2d3 +#define BTN_TRIGGER_HAPPY21 0x2d4 +#define BTN_TRIGGER_HAPPY22 0x2d5 +#define BTN_TRIGGER_HAPPY23 0x2d6 +#define BTN_TRIGGER_HAPPY24 0x2d7 +#define BTN_TRIGGER_HAPPY25 0x2d8 +#define BTN_TRIGGER_HAPPY26 0x2d9 +#define BTN_TRIGGER_HAPPY27 0x2da +#define BTN_TRIGGER_HAPPY28 0x2db +#define BTN_TRIGGER_HAPPY29 0x2dc +#define BTN_TRIGGER_HAPPY30 0x2dd +#define BTN_TRIGGER_HAPPY31 0x2de +#define BTN_TRIGGER_HAPPY32 0x2df +#define BTN_TRIGGER_HAPPY33 0x2e0 +#define BTN_TRIGGER_HAPPY34 0x2e1 +#define BTN_TRIGGER_HAPPY35 0x2e2 +#define BTN_TRIGGER_HAPPY36 0x2e3 +#define BTN_TRIGGER_HAPPY37 0x2e4 +#define BTN_TRIGGER_HAPPY38 0x2e5 +#define BTN_TRIGGER_HAPPY39 0x2e6 +#define BTN_TRIGGER_HAPPY40 0x2e7 + +/* We avoid low common keys in module aliases so they don't get huge. */ +#define KEY_MIN_INTERESTING KEY_MUTE +#define KEY_MAX 0x2ff +#define KEY_CNT (KEY_MAX+1) + +/* + * Relative axes + */ + +#define REL_X 0x00 +#define REL_Y 0x01 +#define REL_Z 0x02 +#define REL_RX 0x03 +#define REL_RY 0x04 +#define REL_RZ 0x05 +#define REL_HWHEEL 0x06 +#define REL_DIAL 0x07 +#define REL_WHEEL 0x08 +#define REL_MISC 0x09 +#define REL_MAX 0x0f +#define REL_CNT (REL_MAX+1) + +/* + * Absolute axes + */ + +#define ABS_X 0x00 +#define ABS_Y 0x01 +#define ABS_Z 0x02 +#define ABS_RX 0x03 +#define ABS_RY 0x04 +#define ABS_RZ 0x05 +#define ABS_THROTTLE 0x06 +#define ABS_RUDDER 0x07 +#define ABS_WHEEL 0x08 +#define ABS_GAS 0x09 +#define ABS_BRAKE 0x0a +#define ABS_HAT0X 0x10 +#define ABS_HAT0Y 0x11 +#define ABS_HAT1X 0x12 +#define ABS_HAT1Y 0x13 +#define ABS_HAT2X 0x14 +#define ABS_HAT2Y 0x15 +#define ABS_HAT3X 0x16 +#define ABS_HAT3Y 0x17 +#define ABS_PRESSURE 0x18 +#define ABS_DISTANCE 0x19 +#define ABS_TILT_X 0x1a +#define ABS_TILT_Y 0x1b +#define ABS_TOOL_WIDTH 0x1c + +#define ABS_VOLUME 0x20 + +#define ABS_MISC 0x28 + +#define ABS_MT_SLOT 0x2f /* MT slot being modified */ +#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ +#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */ +#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */ +#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */ +#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */ +#define ABS_MT_POSITION_X 0x35 /* Center X touch position */ +#define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */ +#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */ +#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */ +#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */ +#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */ +#define ABS_MT_DISTANCE 0x3b /* Contact hover distance */ +#define ABS_MT_TOOL_X 0x3c /* Center X tool position */ +#define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */ + + +#define ABS_MAX 0x3f +#define ABS_CNT (ABS_MAX+1) + +/* + * Switch events + */ + +#define SW_LID 0x00 /* set = lid shut */ +#define SW_TABLET_MODE 0x01 /* set = tablet mode */ +#define SW_HEADPHONE_INSERT 0x02 /* set = inserted */ +#define SW_RFKILL_ALL 0x03 /* rfkill master switch, type "any" + set = radio enabled */ +#define SW_RADIO SW_RFKILL_ALL /* deprecated */ +#define SW_MICROPHONE_INSERT 0x04 /* set = inserted */ +#define SW_DOCK 0x05 /* set = plugged into dock */ +#define SW_LINEOUT_INSERT 0x06 /* set = inserted */ +#define SW_JACK_PHYSICAL_INSERT 0x07 /* set = mechanical switch set */ +#define SW_VIDEOOUT_INSERT 0x08 /* set = inserted */ +#define SW_CAMERA_LENS_COVER 0x09 /* set = lens covered */ +#define SW_KEYPAD_SLIDE 0x0a /* set = keypad slide out */ +#define SW_FRONT_PROXIMITY 0x0b /* set = front proximity sensor active */ +#define SW_ROTATE_LOCK 0x0c /* set = rotate locked/disabled */ +#define SW_LINEIN_INSERT 0x0d /* set = inserted */ +#define SW_MUTE_DEVICE 0x0e /* set = device disabled */ +#define SW_MAX 0x0f +#define SW_CNT (SW_MAX+1) + +/* + * Misc events + */ + +#define MSC_SERIAL 0x00 +#define MSC_PULSELED 0x01 +#define MSC_GESTURE 0x02 +#define MSC_RAW 0x03 +#define MSC_SCAN 0x04 +#define MSC_TIMESTAMP 0x05 +#define MSC_MAX 0x07 +#define MSC_CNT (MSC_MAX+1) + +/* + * LEDs + */ + +#define LED_NUML 0x00 +#define LED_CAPSL 0x01 +#define LED_SCROLLL 0x02 +#define LED_COMPOSE 0x03 +#define LED_KANA 0x04 +#define LED_SLEEP 0x05 +#define LED_SUSPEND 0x06 +#define LED_MUTE 0x07 +#define LED_MISC 0x08 +#define LED_MAIL 0x09 +#define LED_CHARGING 0x0a +#define LED_MAX 0x0f +#define LED_CNT (LED_MAX+1) + +/* + * Autorepeat values + */ + +#define REP_DELAY 0x00 +#define REP_PERIOD 0x01 +#define REP_MAX 0x01 +#define REP_CNT (REP_MAX+1) + +/* + * Sounds + */ + +#define SND_CLICK 0x00 +#define SND_BELL 0x01 +#define SND_TONE 0x02 +#define SND_MAX 0x07 +#define SND_CNT (SND_MAX+1) + +/* + * IDs. + */ + +#define ID_BUS 0 +#define ID_VENDOR 1 +#define ID_PRODUCT 2 +#define ID_VERSION 3 + +#define BUS_PCI 0x01 +#define BUS_ISAPNP 0x02 +#define BUS_USB 0x03 +#define BUS_HIL 0x04 +#define BUS_BLUETOOTH 0x05 +#define BUS_VIRTUAL 0x06 + +#define BUS_ISA 0x10 +#define BUS_I8042 0x11 +#define BUS_XTKBD 0x12 +#define BUS_RS232 0x13 +#define BUS_GAMEPORT 0x14 +#define BUS_PARPORT 0x15 +#define BUS_AMIGA 0x16 +#define BUS_ADB 0x17 +#define BUS_I2C 0x18 +#define BUS_HOST 0x19 +#define BUS_GSC 0x1A +#define BUS_ATARI 0x1B +#define BUS_SPI 0x1C + +/* + * MT_TOOL types + */ +#define MT_TOOL_FINGER 0 +#define MT_TOOL_PEN 1 +#define MT_TOOL_MAX 1 + +/* + * Values describing the status of a force-feedback effect + */ +#define FF_STATUS_STOPPED 0x00 +#define FF_STATUS_PLAYING 0x01 +#define FF_STATUS_MAX 0x01 + +/* + * Structures used in ioctls to upload effects to a device + * They are pieces of a bigger structure (called ff_effect) + */ + +/* + * All duration values are expressed in ms. Values above 32767 ms (0x7fff) + * should not be used and have unspecified results. + */ + +/** + * struct ff_replay - defines scheduling of the force-feedback effect + * @length: duration of the effect + * @delay: delay before effect should start playing + */ +struct ff_replay { + __u16 length; + __u16 delay; +}; + +/** + * struct ff_trigger - defines what triggers the force-feedback effect + * @button: number of the button triggering the effect + * @interval: controls how soon the effect can be re-triggered + */ +struct ff_trigger { + __u16 button; + __u16 interval; +}; + +/** + * struct ff_envelope - generic force-feedback effect envelope + * @attack_length: duration of the attack (ms) + * @attack_level: level at the beginning of the attack + * @fade_length: duration of fade (ms) + * @fade_level: level at the end of fade + * + * The @attack_level and @fade_level are absolute values; when applying + * envelope force-feedback core will convert to positive/negative + * value based on polarity of the default level of the effect. + * Valid range for the attack and fade levels is 0x0000 - 0x7fff + */ +struct ff_envelope { + __u16 attack_length; + __u16 attack_level; + __u16 fade_length; + __u16 fade_level; +}; + +/** + * struct ff_constant_effect - defines parameters of a constant force-feedback effect + * @level: strength of the effect; may be negative + * @envelope: envelope data + */ +struct ff_constant_effect { + __s16 level; + struct ff_envelope envelope; +}; + +/** + * struct ff_ramp_effect - defines parameters of a ramp force-feedback effect + * @start_level: beginning strength of the effect; may be negative + * @end_level: final strength of the effect; may be negative + * @envelope: envelope data + */ +struct ff_ramp_effect { + __s16 start_level; + __s16 end_level; + struct ff_envelope envelope; +}; + +/** + * struct ff_condition_effect - defines a spring or friction force-feedback effect + * @right_saturation: maximum level when joystick moved all way to the right + * @left_saturation: same for the left side + * @right_coeff: controls how fast the force grows when the joystick moves + * to the right + * @left_coeff: same for the left side + * @deadband: size of the dead zone, where no force is produced + * @center: position of the dead zone + */ +struct ff_condition_effect { + __u16 right_saturation; + __u16 left_saturation; + + __s16 right_coeff; + __s16 left_coeff; + + __u16 deadband; + __s16 center; +}; + +/** + * struct ff_periodic_effect - defines parameters of a periodic force-feedback effect + * @waveform: kind of the effect (wave) + * @period: period of the wave (ms) + * @magnitude: peak value + * @offset: mean value of the wave (roughly) + * @phase: 'horizontal' shift + * @envelope: envelope data + * @custom_len: number of samples (FF_CUSTOM only) + * @custom_data: buffer of samples (FF_CUSTOM only) + * + * Known waveforms - FF_SQUARE, FF_TRIANGLE, FF_SINE, FF_SAW_UP, + * FF_SAW_DOWN, FF_CUSTOM. The exact syntax FF_CUSTOM is undefined + * for the time being as no driver supports it yet. + * + * Note: the data pointed by custom_data is copied by the driver. + * You can therefore dispose of the memory after the upload/update. + */ +struct ff_periodic_effect { + __u16 waveform; + __u16 period; + __s16 magnitude; + __s16 offset; + __u16 phase; + + struct ff_envelope envelope; + + __u32 custom_len; + __s16 *custom_data; +}; + +/** + * struct ff_rumble_effect - defines parameters of a periodic force-feedback effect + * @strong_magnitude: magnitude of the heavy motor + * @weak_magnitude: magnitude of the light one + * + * Some rumble pads have two motors of different weight. Strong_magnitude + * represents the magnitude of the vibration generated by the heavy one. + */ +struct ff_rumble_effect { + __u16 strong_magnitude; + __u16 weak_magnitude; +}; + +/** + * struct ff_effect - defines force feedback effect + * @type: type of the effect (FF_CONSTANT, FF_PERIODIC, FF_RAMP, FF_SPRING, + * FF_FRICTION, FF_DAMPER, FF_RUMBLE, FF_INERTIA, or FF_CUSTOM) + * @id: an unique id assigned to an effect + * @direction: direction of the effect + * @trigger: trigger conditions (struct ff_trigger) + * @replay: scheduling of the effect (struct ff_replay) + * @u: effect-specific structure (one of ff_constant_effect, ff_ramp_effect, + * ff_periodic_effect, ff_condition_effect, ff_rumble_effect) further + * defining effect parameters + * + * This structure is sent through ioctl from the application to the driver. + * To create a new effect application should set its @id to -1; the kernel + * will return assigned @id which can later be used to update or delete + * this effect. + * + * Direction of the effect is encoded as follows: + * 0 deg -> 0x0000 (down) + * 90 deg -> 0x4000 (left) + * 180 deg -> 0x8000 (up) + * 270 deg -> 0xC000 (right) + */ +struct ff_effect { + __u16 type; + __s16 id; + __u16 direction; + struct ff_trigger trigger; + struct ff_replay replay; + + union { + struct ff_constant_effect constant; + struct ff_ramp_effect ramp; + struct ff_periodic_effect periodic; + struct ff_condition_effect condition[2]; /* One for each axis */ + struct ff_rumble_effect rumble; + } u; +}; + +/* + * Force feedback effect types + */ + +#define FF_RUMBLE 0x50 +#define FF_PERIODIC 0x51 +#define FF_CONSTANT 0x52 +#define FF_SPRING 0x53 +#define FF_FRICTION 0x54 +#define FF_DAMPER 0x55 +#define FF_INERTIA 0x56 +#define FF_RAMP 0x57 + +#define FF_EFFECT_MIN FF_RUMBLE +#define FF_EFFECT_MAX FF_RAMP + +/* + * Force feedback periodic effect types + */ + +#define FF_SQUARE 0x58 +#define FF_TRIANGLE 0x59 +#define FF_SINE 0x5a +#define FF_SAW_UP 0x5b +#define FF_SAW_DOWN 0x5c +#define FF_CUSTOM 0x5d + +#define FF_WAVEFORM_MIN FF_SQUARE +#define FF_WAVEFORM_MAX FF_CUSTOM + +/* + * Set ff device properties + */ + +#define FF_GAIN 0x60 +#define FF_AUTOCENTER 0x61 + +#define FF_MAX 0x7f +#define FF_CNT (FF_MAX+1) + +#endif /* _INPUT_H */ diff --git a/libevdev.pc.in b/libevdev.pc.in new file mode 100644 index 0000000..77127e0 --- /dev/null +++ b/libevdev.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libevdev +Description: Handler library for evdev events +Version: @VERSION@ +Cflags: -I${includedir}/libevdev-1.0/ +Libs: -L${libdir} -levdev diff --git a/libevdev/.gitignore b/libevdev/.gitignore new file mode 100644 index 0000000..339d72d --- /dev/null +++ b/libevdev/.gitignore @@ -0,0 +1 @@ +event-names.h diff --git a/libevdev/Makefile.am b/libevdev/Makefile.am new file mode 100644 index 0000000..c83a225 --- /dev/null +++ b/libevdev/Makefile.am @@ -0,0 +1,32 @@ +lib_LTLIBRARIES=libevdev.la + +AM_CPPFLAGS = $(GCC_CFLAGS) -I$(top_srcdir)/include -I$(top_srcdir) + +libevdev_la_SOURCES = \ + libevdev.h \ + libevdev-int.h \ + libevdev-util.h \ + libevdev-uinput.c \ + libevdev-uinput.h \ + libevdev-uinput-int.h \ + libevdev.c \ + libevdev-names.c \ + ../include/linux/input.h + +libevdev_la_LDFLAGS = \ + -version-info $(LIBEVDEV_LT_VERSION) \ + -Wl,--version-script="$(srcdir)/libevdev.sym" \ + $(GNU_LD_FLAGS) + +EXTRA_libevdev_la_DEPENDENCIES = $(srcdir)/libevdev.sym + +libevdevincludedir = $(includedir)/libevdev-1.0/libevdev +libevdevinclude_HEADERS = libevdev.h libevdev-uinput.h + +event-names.h: Makefile make-event-names.py + $(PYTHON) $(srcdir)/make-event-names.py $(top_srcdir)/include/linux/input.h > $@ + +EXTRA_DIST = make-event-names.py libevdev.sym +CLEANFILES = event-names.h +BUILT_SOURCES = event-names.h + diff --git a/libevdev/libevdev-int.h b/libevdev/libevdev-int.h new file mode 100644 index 0000000..f587e76 --- /dev/null +++ b/libevdev/libevdev-int.h @@ -0,0 +1,335 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef LIBEVDEV_INT_H +#define LIBEVDEV_INT_H + +#include +#include +#include +#include +#include +#include "libevdev.h" +#include "libevdev-util.h" + +#define MAX_NAME 256 +#define ABS_MT_MIN ABS_MT_SLOT +#define ABS_MT_MAX ABS_MT_TOOL_Y +#define ABS_MT_CNT (ABS_MT_MAX - ABS_MT_MIN + 1) +#define LIBEVDEV_EXPORT __attribute__((visibility("default"))) +#define ALIAS(_to) __attribute__((alias(#_to))) + +/** + * Sync state machine: + * default state: SYNC_NONE + * + * SYNC_NONE → SYN_DROPPED or forced sync → SYNC_NEEDED + * SYNC_NEEDED → libevdev_next_event(LIBEVDEV_READ_FLAG_SYNC) → SYNC_IN_PROGRESS + * SYNC_NEEDED → libevdev_next_event(LIBEVDEV_READ_FLAG_SYNC_NONE) → SYNC_NONE + * SYNC_IN_PROGRESS → libevdev_next_event(LIBEVDEV_READ_FLAG_SYNC_NONE) → SYNC_NONE + * SYNC_IN_PROGRESS → no sync events left → SYNC_NONE + * + */ +enum SyncState { + SYNC_NONE, + SYNC_NEEDED, + SYNC_IN_PROGRESS, +}; + +struct mt_sync_state { + int code; + int val[]; +}; + +struct libevdev { + int fd; + bool initialized; + char *name; + char *phys; + char *uniq; + struct input_id ids; + int driver_version; + unsigned long bits[NLONGS(EV_CNT)]; + unsigned long props[NLONGS(INPUT_PROP_CNT)]; + unsigned long key_bits[NLONGS(KEY_CNT)]; + unsigned long rel_bits[NLONGS(REL_CNT)]; + unsigned long abs_bits[NLONGS(ABS_CNT)]; + unsigned long led_bits[NLONGS(LED_CNT)]; + unsigned long msc_bits[NLONGS(MSC_CNT)]; + unsigned long sw_bits[NLONGS(SW_CNT)]; + unsigned long rep_bits[NLONGS(REP_CNT)]; /* convenience, always 1 */ + unsigned long ff_bits[NLONGS(FF_CNT)]; + unsigned long snd_bits[NLONGS(SND_CNT)]; + unsigned long key_values[NLONGS(KEY_CNT)]; + unsigned long led_values[NLONGS(LED_CNT)]; + unsigned long sw_values[NLONGS(SW_CNT)]; + struct input_absinfo abs_info[ABS_CNT]; + int *mt_slot_vals; /* [num_slots * ABS_MT_CNT] */ + int num_slots; /**< valid slots in mt_slot_vals */ + int current_slot; + int rep_values[REP_CNT]; + + enum SyncState sync_state; + enum libevdev_grab_mode grabbed; + + struct input_event *queue; + size_t queue_size; /**< size of queue in elements */ + size_t queue_next; /**< next event index */ + size_t queue_nsync; /**< number of sync events */ + + struct timeval last_event_time; + + struct { + struct mt_sync_state *mt_state; + size_t mt_state_sz; /* in bytes */ + unsigned long *slot_update; + size_t slot_update_sz; /* in bytes */ + unsigned long *tracking_id_changes; + size_t tracking_id_changes_sz; /* in bytes */ + } mt_sync; +}; + +struct logdata { + enum libevdev_log_priority priority; /** minimum logging priority */ + libevdev_log_func_t handler; /** handler function */ + void *userdata; /** user-defined data pointer */ +}; +extern struct logdata log_data; + +#define log_msg_cond(priority, ...) \ + do { \ + if (libevdev_get_log_priority() >= priority) \ + log_msg(priority, log_data.userdata, __FILE__, __LINE__, __func__, __VA_ARGS__); \ + } while(0) + +#define log_error(...) log_msg_cond(LIBEVDEV_LOG_ERROR, __VA_ARGS__) +#define log_info(...) log_msg_cond(LIBEVDEV_LOG_INFO, __VA_ARGS__) +#define log_dbg(...) log_msg_cond(LIBEVDEV_LOG_DEBUG, __VA_ARGS__) +#define log_bug(...) log_msg_cond(LIBEVDEV_LOG_ERROR, "BUG: "__VA_ARGS__) + +extern void +log_msg(enum libevdev_log_priority priority, + void *data, + const char *file, int line, const char *func, + const char *format, ...) LIBEVDEV_ATTRIBUTE_PRINTF(6, 7); + +/** + * @return a pointer to the next element in the queue, or NULL if the queue + * is full. + */ +static inline struct input_event* +queue_push(struct libevdev *dev) +{ + if (dev->queue_next >= dev->queue_size) + return NULL; + + return &dev->queue[dev->queue_next++]; +} + +/** + * Set ev to the last element in the queue, removing it from the queue. + * + * @return 0 on success, 1 if the queue is empty. + */ +static inline int +queue_pop(struct libevdev *dev, struct input_event *ev) +{ + if (dev->queue_next == 0) + return 1; + + *ev = dev->queue[--dev->queue_next]; + + return 0; +} + +static inline int +queue_peek(struct libevdev *dev, size_t idx, struct input_event *ev) +{ + if (dev->queue_next == 0 || idx > dev->queue_next) + return 1; + *ev = dev->queue[idx]; + return 0; +} + + +/** + * Shift the first n elements into ev and return the number of elements + * shifted. + * ev must be large enough to store n elements. + * + * @param ev The buffer to copy into, or NULL + * @return The number of elements in ev. + */ +static inline int +queue_shift_multiple(struct libevdev *dev, size_t n, struct input_event *ev) +{ + size_t i; + + if (dev->queue_next == 0) + return 0; + + n = min(n, dev->queue_next); + + if (ev) { + for (i = 0; i < n; i++) + ev[i] = dev->queue[i]; + } + + for (i = 0; i < dev->queue_next - n; i++) + dev->queue[i] = dev->queue[n + i]; + + dev->queue_next -= n; + return n; +} + +/** + * Set ev to the first element in the queue, shifting everything else + * forward by one. + * + * @return 0 on success, 1 if the queue is empty. + */ +static inline int +queue_shift(struct libevdev *dev, struct input_event *ev) +{ + return queue_shift_multiple(dev, 1, ev) == 1 ? 0 : 1; +} + +static inline int +queue_alloc(struct libevdev *dev, size_t size) +{ + if (size == 0) + return -ENOMEM; + + dev->queue = calloc(size, sizeof(struct input_event)); + if (!dev->queue) + return -ENOMEM; + + dev->queue_size = size; + dev->queue_next = 0; + return 0; +} + +static inline void +queue_free(struct libevdev *dev) +{ + free(dev->queue); + dev->queue_size = 0; + dev->queue_next = 0; +} + +static inline size_t +queue_num_elements(struct libevdev *dev) +{ + return dev->queue_next; +} + +static inline size_t +queue_size(struct libevdev *dev) +{ + return dev->queue_size; +} + +static inline size_t +queue_num_free_elements(struct libevdev *dev) +{ + if (dev->queue_size == 0) + return 0; + + return dev->queue_size - dev->queue_next; +} + +static inline struct input_event * +queue_next_element(struct libevdev *dev) +{ + if (dev->queue_next == dev->queue_size) + return NULL; + + return &dev->queue[dev->queue_next]; +} + +static inline int +queue_set_num_elements(struct libevdev *dev, size_t nelem) +{ + if (nelem > dev->queue_size) + return 1; + + dev->queue_next = nelem; + + return 0; +} + +#define max_mask(uc, lc) \ + case EV_##uc: \ + *mask = dev->lc##_bits; \ + max = libevdev_event_type_get_max(type); \ + break; + + +static inline int +type_to_mask_const(const struct libevdev *dev, unsigned int type, const unsigned long **mask) +{ + int max; + + switch(type) { + max_mask(ABS, abs); + max_mask(REL, rel); + max_mask(KEY, key); + max_mask(LED, led); + max_mask(MSC, msc); + max_mask(SW, sw); + max_mask(FF, ff); + max_mask(REP, rep); + max_mask(SND, snd); + default: + max = -1; + break; + } + + return max; +} + +static inline int +type_to_mask(struct libevdev *dev, unsigned int type, unsigned long **mask) +{ + int max; + + switch(type) { + max_mask(ABS, abs); + max_mask(REL, rel); + max_mask(KEY, key); + max_mask(LED, led); + max_mask(MSC, msc); + max_mask(SW, sw); + max_mask(FF, ff); + max_mask(REP, rep); + max_mask(SND, snd); + default: + max = -1; + break; + } + + return max; +} + +#undef max_mask +#endif + diff --git a/libevdev/libevdev-names.c b/libevdev/libevdev-names.c new file mode 100644 index 0000000..10669a3 --- /dev/null +++ b/libevdev/libevdev-names.c @@ -0,0 +1,143 @@ +/* + * Copyright © 2013 David Herrmann + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "libevdev.h" +#include "libevdev-int.h" +#include "libevdev-util.h" +#include "event-names.h" + +struct name_lookup { + const char *name; + size_t len; +}; + +static int cmp_entry(const void *vlookup, const void *ventry) +{ + const struct name_lookup *lookup = vlookup; + const struct name_entry *entry = ventry; + int r; + + r = strncmp(lookup->name, entry->name, lookup->len); + if (!r) { + if (entry->name[lookup->len]) + r = -1; + else + r = 0; + } + + return r; +} + +static const struct name_entry* +lookup_name(const struct name_entry *array, size_t asize, + struct name_lookup *lookup) +{ + const struct name_entry *entry; + + entry = bsearch(lookup, array, asize, sizeof(*array), cmp_entry); + if (!entry) + return NULL; + + return entry; +} + +LIBEVDEV_EXPORT int +libevdev_event_type_from_name(const char *name) +{ + return libevdev_event_type_from_name_n(name, strlen(name)); +} + +LIBEVDEV_EXPORT int +libevdev_event_type_from_name_n(const char *name, size_t len) +{ + struct name_lookup lookup; + const struct name_entry *entry; + + lookup.name = name; + lookup.len = len; + + entry = lookup_name(ev_names, ARRAY_LENGTH(ev_names), &lookup); + + return entry ? (int)entry->value : -1; +} + +static int type_from_prefix(const char *name, ssize_t len) +{ + const char *e; + size_t i; + ssize_t l; + + /* MAX_ is not allowed, even though EV_MAX exists */ + if (startswith(name, len, "MAX_", 4)) + return -1; + /* BTN_ is special as there is no EV_BTN type */ + if (startswith(name, len, "BTN_", 4)) + return EV_KEY; + /* FF_STATUS_ is special as FF_ is a prefix of it, so test it first */ + if (startswith(name, len, "FF_STATUS_", 10)) + return EV_FF_STATUS; + + for (i = 0; i < ARRAY_LENGTH(ev_names); ++i) { + /* skip EV_ prefix so @e is suffix of [EV_]XYZ */ + e = &ev_names[i].name[3]; + l = strlen(e); + + /* compare prefix and test for trailing _ */ + if (len > l && startswith(name, len, e, l) && name[l] == '_') + return ev_names[i].value; + } + + return -1; +} + +LIBEVDEV_EXPORT int +libevdev_event_code_from_name(unsigned int type, const char *name) +{ + return libevdev_event_code_from_name_n(type, name, strlen(name)); +} + +LIBEVDEV_EXPORT int +libevdev_event_code_from_name_n(unsigned int type, const char *name, size_t len) +{ + struct name_lookup lookup; + const struct name_entry *entry; + int real_type; + + /* verify that @name is really of type @type */ + real_type = type_from_prefix(name, len); + if (real_type < 0 || (unsigned int)real_type != type) + return -1; + + /* now look up the name @name and return the constant */ + lookup.name = name; + lookup.len = len; + + entry = lookup_name(code_names, ARRAY_LENGTH(code_names), &lookup); + + return entry ? (int)entry->value : -1; +} diff --git a/libevdev/libevdev-uinput-int.h b/libevdev/libevdev-uinput-int.h new file mode 100644 index 0000000..fbc1c29 --- /dev/null +++ b/libevdev/libevdev-uinput-int.h @@ -0,0 +1,31 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + + +struct libevdev_uinput { + int fd; /**< file descriptor to uinput */ + int fd_is_managed; /**< do we need to close it? */ + char *name; /**< device name */ + char *syspath; /**< /sys path */ + char *devnode; /**< device node */ + time_t ctime[2]; /**< before/after UI_DEV_CREATE */ +}; diff --git a/libevdev/libevdev-uinput.c b/libevdev/libevdev-uinput.c new file mode 100644 index 0000000..f176a00 --- /dev/null +++ b/libevdev/libevdev-uinput.c @@ -0,0 +1,381 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libevdev.h" +#include "libevdev-int.h" +#include "libevdev-uinput.h" +#include "libevdev-uinput-int.h" +#include "libevdev-util.h" + +#define SYS_INPUT_DIR "/sys/devices/virtual/input/" + +#ifndef UINPUT_IOCTL_BASE +#define UINPUT_IOCTL_BASE 'U' +#endif + +#ifndef UI_SET_PROPBIT +#define UI_SET_PROPBIT _IOW(UINPUT_IOCTL_BASE, 110, int) +#endif + +static struct libevdev_uinput * +alloc_uinput_device(const char *name) +{ + struct libevdev_uinput *uinput_dev; + + uinput_dev = calloc(1, sizeof(struct libevdev_uinput)); + if (uinput_dev) { + uinput_dev->name = strdup(name); + uinput_dev->fd = -1; + } + + return uinput_dev; +} + +static int +set_evbits(const struct libevdev *dev, int fd, struct uinput_user_dev *uidev) +{ + int rc = 0; + unsigned int type; + + for (type = 0; type < EV_CNT; type++) { + unsigned int code; + int max; + int uinput_bit; + const unsigned long *mask; + + if (!libevdev_has_event_type(dev, type)) + continue; + + rc = ioctl(fd, UI_SET_EVBIT, type); + if (rc == -1) + break; + + /* uinput can't set EV_REP */ + if (type == EV_REP) + continue; + + max = type_to_mask_const(dev, type, &mask); + if (max == -1) + continue; + + switch(type) { + case EV_KEY: uinput_bit = UI_SET_KEYBIT; break; + case EV_REL: uinput_bit = UI_SET_RELBIT; break; + case EV_ABS: uinput_bit = UI_SET_ABSBIT; break; + case EV_MSC: uinput_bit = UI_SET_MSCBIT; break; + case EV_LED: uinput_bit = UI_SET_LEDBIT; break; + case EV_SND: uinput_bit = UI_SET_SNDBIT; break; + case EV_FF: uinput_bit = UI_SET_FFBIT; break; + case EV_SW: uinput_bit = UI_SET_SWBIT; break; + default: + rc = -1; + errno = EINVAL; + goto out; + } + + for (code = 0; code <= (unsigned int)max; code++) { + if (!libevdev_has_event_code(dev, type, code)) + continue; + + rc = ioctl(fd, uinput_bit, code); + if (rc == -1) + goto out; + + if (type == EV_ABS) { + const struct input_absinfo *abs = libevdev_get_abs_info(dev, code); + uidev->absmin[code] = abs->minimum; + uidev->absmax[code] = abs->maximum; + uidev->absfuzz[code] = abs->fuzz; + uidev->absflat[code] = abs->flat; + /* uinput has no resolution in the device struct, this needs + * to be fixed in the kernel */ + } + } + + } + +out: + return rc; +} + +static int +set_props(const struct libevdev *dev, int fd, struct uinput_user_dev *uidev) +{ + unsigned int prop; + int rc = 0; + + for (prop = 0; prop <= INPUT_PROP_MAX; prop++) { + if (!libevdev_has_property(dev, prop)) + continue; + + rc = ioctl(fd, UI_SET_PROPBIT, prop); + if (rc == -1) { + /* If UI_SET_PROPBIT is not supported, treat -EINVAL + * as success. The kernel only sends -EINVAL for an + * invalid ioctl, invalid INPUT_PROP_MAX or if the + * ioctl is called on an already created device. The + * last two can't happen here. + */ + if (errno == -EINVAL) + rc = 0; + break; + } + } + return rc; +} + +LIBEVDEV_EXPORT int +libevdev_uinput_get_fd(const struct libevdev_uinput *uinput_dev) +{ + return uinput_dev->fd; +} + +static int is_event_device(const struct dirent *dent) { + return strncmp("event", dent->d_name, 5) == 0; +} + +static char * +fetch_device_node(const char *path) +{ + char *devnode = NULL; + struct dirent **namelist; + int ndev, i; + + ndev = scandir(path, &namelist, is_event_device, alphasort); + if (ndev <= 0) + return NULL; + + /* ndev should only ever be 1 */ + + for (i = 0; i < ndev; i++) { + if (!devnode && asprintf(&devnode, "/dev/input/%s", namelist[i]->d_name) == -1) + devnode = NULL; + free(namelist[i]); + } + + free(namelist); + + return devnode; +} + +static int is_input_device(const struct dirent *dent) { + return strncmp("input", dent->d_name, 5) == 0; +} + +static int +fetch_syspath_and_devnode(struct libevdev_uinput *uinput_dev) +{ + struct dirent **namelist; + int ndev, i; + + /* FIXME: use new ioctl() here once kernel supports it */ + + ndev = scandir(SYS_INPUT_DIR, &namelist, is_input_device, alphasort); + if (ndev <= 0) + return -1; + + for (i = 0; i < ndev; i++) { + int fd, len; + char buf[sizeof(SYS_INPUT_DIR) + 64]; + struct stat st; + + strcpy(buf, SYS_INPUT_DIR); + strcat(buf, namelist[i]->d_name); + + if (stat(buf, &st) == -1) + continue; + + /* created before UI_DEV_CREATE, or after it finished */ + if (st.st_ctime < uinput_dev->ctime[0] || + st.st_ctime > uinput_dev->ctime[1]) + continue; + + /* created within time frame */ + strcat(buf, "/name"); + fd = open(buf, O_RDONLY); + if (fd < 0) + continue; + + len = read(fd, buf, sizeof(buf)); + close(fd); + if (len <= 0) + continue; + + buf[len - 1] = '\0'; /* file contains \n */ + if (strcmp(buf, uinput_dev->name) == 0) { + if (uinput_dev->syspath) { + /* FIXME: could descend into bit comparison here */ + log_info("multiple identical devices found. syspath is unreliable\n"); + break; + } else { + strcpy(buf, SYS_INPUT_DIR); + strcat(buf, namelist[i]->d_name); + uinput_dev->syspath = strdup(buf); + uinput_dev->devnode = fetch_device_node(buf); + } + } + } + + for (i = 0; i < ndev; i++) + free(namelist[i]); + free(namelist); + + return uinput_dev->devnode ? 0 : -1; +} + + + +LIBEVDEV_EXPORT int +libevdev_uinput_create_from_device(const struct libevdev *dev, int fd, struct libevdev_uinput** uinput_dev) +{ + int rc; + struct uinput_user_dev uidev; + struct libevdev_uinput *new_device; + + new_device = alloc_uinput_device(libevdev_get_name(dev)); + if (!new_device) + return -ENOMEM; + + if (fd == LIBEVDEV_UINPUT_OPEN_MANAGED) { + fd = open("/dev/uinput", O_RDWR|O_CLOEXEC); + if (fd < 0) + goto error; + + new_device->fd_is_managed = 1; + } else if (fd < 0) { + log_bug("Invalid fd %d\n", fd); + errno = EBADF; + goto error; + } + + memset(&uidev, 0, sizeof(uidev)); + + strncpy(uidev.name, libevdev_get_name(dev), UINPUT_MAX_NAME_SIZE - 1); + uidev.id.vendor = libevdev_get_id_vendor(dev); + uidev.id.product = libevdev_get_id_product(dev); + uidev.id.bustype = libevdev_get_id_bustype(dev); + uidev.id.version = libevdev_get_id_version(dev); + + if (set_evbits(dev, fd, &uidev) != 0) + goto error; + if (set_props(dev, fd, &uidev) != 0) + goto error; + + rc = write(fd, &uidev, sizeof(uidev)); + if (rc < 0) + goto error; + else if ((size_t)rc < sizeof(uidev)) { + errno = EINVAL; + goto error; + } + + /* ctime notes time before/after ioctl to help us filter out devices + when traversing /sys/devices/virtual/input to find the device + node. + + this is in seconds, so ctime[0]/[1] will almost always be + identical but /sys doesn't give us sub-second ctime so... + */ + new_device->ctime[0] = time(NULL); + + rc = ioctl(fd, UI_DEV_CREATE, NULL); + if (rc == -1) + goto error; + + new_device->ctime[1] = time(NULL); + new_device->fd = fd; + + if (fetch_syspath_and_devnode(new_device) == -1) { + log_error("unable to fetch syspath or device node.\n"); + errno = ENODEV; + goto error; + } + + *uinput_dev = new_device; + + return 0; + +error: + libevdev_uinput_destroy(new_device); + return -errno; +} + +LIBEVDEV_EXPORT void +libevdev_uinput_destroy(struct libevdev_uinput *uinput_dev) +{ + if (!uinput_dev) + return; + + ioctl(uinput_dev->fd, UI_DEV_DESTROY, NULL); + if (uinput_dev->fd_is_managed) + close(uinput_dev->fd); + free(uinput_dev->syspath); + free(uinput_dev->devnode); + free(uinput_dev->name); + free(uinput_dev); +} + +LIBEVDEV_EXPORT const char* +libevdev_uinput_get_syspath(struct libevdev_uinput *uinput_dev) +{ + return uinput_dev->syspath; +} + +LIBEVDEV_EXPORT const char* +libevdev_uinput_get_devnode(struct libevdev_uinput *uinput_dev) +{ + return uinput_dev->devnode; +} + +LIBEVDEV_EXPORT int +libevdev_uinput_write_event(const struct libevdev_uinput *uinput_dev, + unsigned int type, + unsigned int code, + int value) +{ + struct input_event ev = { {0,0}, type, code, value }; + int fd = libevdev_uinput_get_fd(uinput_dev); + int rc, max; + + if (type > EV_MAX) + return -EINVAL; + + max = libevdev_event_type_get_max(type); + if (max == -1 || code > (unsigned int)max) + return -EINVAL; + + rc = write(fd, &ev, sizeof(ev)); + + return rc < 0 ? -errno : 0; +} diff --git a/libevdev/libevdev-uinput.h b/libevdev/libevdev-uinput.h new file mode 100644 index 0000000..f5b8df4 --- /dev/null +++ b/libevdev/libevdev-uinput.h @@ -0,0 +1,236 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef LIBEVDEV_UINPUT_H +#define LIBEVDEV_UINPUT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct libevdev_uinput; + +/** + * @defgroup uinput uinput device creation + * + * Creation of uinput devices based on existing libevdev devices. These functions + * help to create uinput devices that emulate libevdev devices. In the simplest + * form it serves to duplicate an existing device: + * + @code + int err; + int new_fd; + struct libevdev *dev; + struct libevdev_uinput *uidev; + struct input_event ev[2]; + + err = libevdev_new_from_fd(&dev, fd); + if (err != 0) + return err; + + uifd = open("/dev/uinput", O_RDWR); + if (uidev < 0) + return -errno; + + err = libevdev_uinput_create_from_device(dev, uifd, &uidev); + if (err != 0) + return err; + + // post a REL_X event + err = libevdev_uinput_write_event(uidev, EV_REL, REL_X, -1); + if (err != 0) + return err; + libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0); + if (err != 0) + return err; + + libevdev_uinput_destroy(uidev); + close(uifd); + + @endcode + * + * Alternatively, a device can be constructed from scratch: + * + @code + int err; + struct libevdev *dev; + struct libevdev_uinput *uidev; + + dev = libevdev_new(); + libevdev_set_name(dev, "test device"); + libevdev_enable_event_type(dev, EV_REL); + libevdev_enable_event_code(dev, EV_REL, REL_X); + libevdev_enable_event_code(dev, EV_REL, REL_Y); + libevdev_enable_event_type(dev, EV_KEY); + libevdev_enable_event_code(dev, EV_KEY, BTN_LEFT); + libevdev_enable_event_code(dev, EV_KEY, BTN_MIDDLE); + libevdev_enable_event_code(dev, EV_KEY, BTN_RIGHT); + + err = libevdev_uinput_create_from_device(dev, + LIBEVDEV_UINPUT_OPEN_MANAGED, + &uidev); + if (err != 0) + return err; + + // ... do something ... + + libevdev_uinput_destroy(uidev); + + @endcode + */ + +enum libevdev_uinput_open_mode { + /* intentionally -2 to avoid to avoid code like the below from accidentally working: + fd = open("/dev/uinput", O_RDWR); // fails, fd is -1 + libevdev_uinput_create_from_device(dev, fd, &uidev); // may hide the error */ + LIBEVDEV_UINPUT_OPEN_MANAGED = -2 /**< let libevdev open and close @c /dev/uinput */ +}; + +/** + * @ingroup uinput + * + * Create a uinput device based on the given libevdev device. The uinput device + * will be an exact copy of the libevdev device, minus the bits that uinput doesn't + * allow to be set. + * + * If uinput_fd is @ref LIBEVDEV_UINPUT_OPEN_MANAGED, libevdev_uinput_create_from_device() + * will open @c /dev/uinput in read/write mode and manage the file descriptor. + * Otherwise, uinput_fd must be opened by the caller and opened with the + * appropriate permissions. + * + * The device's lifetime is tied to the uinput file descriptor, closing it will + * destroy the uinput device. You should call libevdev_uinput_destroy() before + * closing the file descriptor to free allocated resources. + * A file descriptor can only create one uinput device at a time; the second device + * will fail with -EINVAL. + * + * You don't need to keep the file descriptor variable around, + * libevdev_uinput_get_fd() will return it when needed. + * + * @note Due to limitations in the uinput kernel module, REP_DELAY and + * REP_PERIOD will default to the kernel defaults, not to the ones set in the + * source device. + * + * @param dev The device to duplicate + * @param uinput_fd @ref LIBEVDEV_UINPUT_OPEN_MANAGED or a file descriptor to @c /dev/uinput, + * @param[out] uinput_dev The newly created libevdev device. + * + * @return 0 on success or a negative errno on failure. On failure, the value of + * uinput_dev is unmodified. + * + * @see libevdev_uinput_destroy + */ +int libevdev_uinput_create_from_device(const struct libevdev *dev, + int uinput_fd, + struct libevdev_uinput **uinput_dev); + +/** + * @ingroup uinput + * + * Destroy a previously created uinput device and free associated memory. + * + * If the device was opened with @ref LIBEVDEV_UINPUT_OPEN_MANAGED, + * libevdev_uinput_destroy() also closes the file descriptor. Otherwise, the + * fd is left as-is and must be closed by the caller. + * + * @param uinput_dev A previously created uinput device. + */ +void libevdev_uinput_destroy(struct libevdev_uinput *uinput_dev); + +/** + * @ingroup uinput + * + * Return the file descriptor used to create this uinput device. This is the + * fd pointing to /dev/uinput. This file descriptor may be used to write + * events that are emitted by the uinput device. + * Closing this file descriptor will destroy the uinput device, you should + * call libevdev_uinput_destroy() first to free allocated resources. + * + * @param uinput_dev A previously created uinput device. + * + * @return The file descriptor used to create this device + */ +int libevdev_uinput_get_fd(const struct libevdev_uinput *uinput_dev); + +/** + * @ingroup uinput + * + * Return the syspath representing this uinput device. + * At the time of writing, the uinput kernel device does not + * provide a way to get the syspath directly through uinput so libevdev must guess. + * In some cases libevdev is unable to derive the syspath. If the running kernel + * supports the UI_GET_SYSNAME ioctl, the syspath is retrieved through that and will + * be reliable and not be NULL. The UI_GET_SYSNAME ioctl is currently + * scheduled for 3.15. + * + * @note This function may return NULL. libevdev currently uses ctime and + * the device name to guess devices. To avoid false positives, wait at least + * wait at least 1.5s between creating devices that have the same name. + * @param uinput_dev A previously created uinput device. + * @return The syspath for this device, including the preceding /sys + * + * @see libevdev_uinput_get_devnode + */ +const char*libevdev_uinput_get_syspath(struct libevdev_uinput *uinput_dev); + +/** + * @ingroup uinput + * + * Return the device node representing this uinput device. + * + * This relies on libevdev_uinput_get_syspath() to provide a valid syspath. + * See libevdev_uinput_get_syspath() for more details. + * + * @note This function may return NULL. libevdev currently has to guess the + * syspath and the device node. See libevdev_uinput_get_syspath() for details. + * @param uinput_dev A previously created uinput device. + * @return The device node for this device, in the form of /dev/input/eventN + * + * @see libevdev_uinput_get_syspath + */ +const char* libevdev_uinput_get_devnode(struct libevdev_uinput *uinput_dev); + +/** + * @ingroup uinput + * + * Post an event through the uinput device. It is the caller's responsibility + * that any event sequence is terminated with an EV_SYN/SYN_REPORT/0 event. + * Otherwise, listeners on the device node will not see the events until the + * next EV_SYN event is posted. + * + * @param uinput_dev A previously created uinput device. + * @param type Event type (EV_ABS, EV_REL, etc.) + * @param code Event code (ABS_X, REL_Y, etc.) + * @param value The event value + * @return 0 on success or a negative errno on error + */ +int libevdev_uinput_write_event(const struct libevdev_uinput *uinput_dev, + unsigned int type, + unsigned int code, + int value); +#ifdef __cplusplus +} +#endif + +#endif /* LIBEVDEV_UINPUT_H */ diff --git a/libevdev/libevdev-util.h b/libevdev/libevdev-util.h new file mode 100644 index 0000000..31c1a14 --- /dev/null +++ b/libevdev/libevdev-util.h @@ -0,0 +1,82 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _UTIL_H_ +#define _UTIL_H_ + +#include +#include +#include + +#define LONG_BITS (sizeof(long) * 8) +#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS) +#define ARRAY_LENGTH(a) (sizeof(a) / (sizeof((a)[0]))) +#define unlikely(x) (__builtin_expect(!!(x),0)) + +#undef min +#undef max +#define min(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a > _b ? _b : _a; \ + }) +#define max(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a > _b ? _a : _b; \ + }) + + +static inline bool +startswith(const char *str, size_t len, const char *prefix, size_t plen) +{ + return len >= plen && !strncmp(str, prefix, plen); +} + +static inline int +bit_is_set(const unsigned long *array, int bit) +{ + return !!(array[bit / LONG_BITS] & (1LL << (bit % LONG_BITS))); +} + +static inline void +set_bit(unsigned long *array, int bit) +{ + array[bit / LONG_BITS] |= (1LL << (bit % LONG_BITS)); +} + +static inline void +clear_bit(unsigned long *array, int bit) +{ + array[bit / LONG_BITS] &= ~(1LL << (bit % LONG_BITS)); +} + +static inline void +set_bit_state(unsigned long *array, int bit, int state) +{ + if (state) + set_bit(array, bit); + else + clear_bit(array, bit); +} + +#endif diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c new file mode 100644 index 0000000..a316831 --- /dev/null +++ b/libevdev/libevdev.c @@ -0,0 +1,1650 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libevdev.h" +#include "libevdev-int.h" +#include "libevdev-util.h" +#include "event-names.h" + +#define MAXEVENTS 64 + +enum event_filter_status { + EVENT_FILTER_NONE, /**< Event untouched by filters */ + EVENT_FILTER_MODIFIED, /**< Event was modified */ + EVENT_FILTER_DISCARD, /**< Discard current event */ +}; + +static int sync_mt_state(struct libevdev *dev, int create_events); + +static inline int* +slot_value(const struct libevdev *dev, int slot, int axis) +{ + if (unlikely(slot > dev->num_slots)) { + log_bug("Slot %d exceeds number of slots (%d)\n", slot, dev->num_slots); + slot = 0; + } + if (unlikely(axis < ABS_MT_MIN || axis > ABS_MT_MAX)) { + log_bug("MT axis %d is outside the valid range [%d,%d]\n", + axis, ABS_MT_MIN, ABS_MT_MAX); + axis = ABS_MT_MIN; + } + return &dev->mt_slot_vals[slot * ABS_MT_CNT + axis - ABS_MT_MIN]; +} + +static int +init_event_queue(struct libevdev *dev) +{ + const int MIN_QUEUE_SIZE = 256; + int nevents = 1; /* terminating SYN_REPORT */ + int nslots; + unsigned int type, code; + + /* count the number of axes, keys, etc. to get a better idea at how + many events per EV_SYN we could possibly get. That's the max we + may get during SYN_DROPPED too. Use double that, just so we have + room for events while syncing an event. + */ + for (type = EV_KEY; type < EV_MAX; type++) { + int max = libevdev_event_type_get_max(type); + for (code = 0; max > 0 && code < (unsigned int) max; code++) { + if (libevdev_has_event_code(dev, type, code)) + nevents++; + } + } + + nslots = libevdev_get_num_slots(dev); + if (nslots > 1) { + int num_mt_axes = 0; + + for (code = ABS_MT_SLOT; code < ABS_MAX; code++) { + if (libevdev_has_event_code(dev, EV_ABS, code)) + num_mt_axes++; + } + + /* We already counted the first slot in the initial count */ + nevents += num_mt_axes * (nslots - 1); + } + + return queue_alloc(dev, max(MIN_QUEUE_SIZE, nevents * 2)); +} + +static void +libevdev_dflt_log_func(enum libevdev_log_priority priority, + void *data, + const char *file, int line, const char *func, + const char *format, va_list args) +{ + const char *prefix; + switch(priority) { + case LIBEVDEV_LOG_ERROR: prefix = "libevdev error"; break; + case LIBEVDEV_LOG_INFO: prefix = "libevdev info"; break; + case LIBEVDEV_LOG_DEBUG: + prefix = "libevdev debug"; + break; + default: + prefix = "libevdev INVALID LOG PRIORITY"; + break; + } + /* default logging format: + libevev error in libevdev_some_func: blah blah + libevev info in libevdev_some_func: blah blah + libevev debug in file.c:123:libevdev_some_func: blah blah + */ + + fprintf(stderr, "%s in ", prefix); + if (priority == LIBEVDEV_LOG_DEBUG) + fprintf(stderr, "%s:%d:", file, line); + fprintf(stderr, "%s: ", func); + vfprintf(stderr, format, args); +} + +/* + * Global logging settings. + */ +struct logdata log_data = { + LIBEVDEV_LOG_INFO, + libevdev_dflt_log_func, + NULL, +}; + +void +log_msg(enum libevdev_log_priority priority, + void *data, + const char *file, int line, const char *func, + const char *format, ...) +{ + va_list args; + + if (!log_data.handler || priority > log_data.priority) + return; + + va_start(args, format); + log_data.handler(priority, data, file, line, func, format, args); + va_end(args); +} + +static void +libevdev_reset(struct libevdev *dev) +{ + free(dev->name); + free(dev->phys); + free(dev->uniq); + free(dev->mt_slot_vals); + free(dev->mt_sync.mt_state); + free(dev->mt_sync.tracking_id_changes); + free(dev->mt_sync.slot_update); + memset(dev, 0, sizeof(*dev)); + dev->fd = -1; + dev->initialized = false; + dev->num_slots = -1; + dev->current_slot = -1; + dev->grabbed = LIBEVDEV_UNGRAB; + dev->sync_state = SYNC_NONE; + libevdev_enable_event_type(dev, EV_SYN); +} + +LIBEVDEV_EXPORT struct libevdev* +libevdev_new(void) +{ + struct libevdev *dev; + + dev = calloc(1, sizeof(*dev)); + if (!dev) + return NULL; + + libevdev_reset(dev); + + return dev; +} + +LIBEVDEV_EXPORT int +libevdev_new_from_fd(int fd, struct libevdev **dev) +{ + struct libevdev *d; + int rc; + + d = libevdev_new(); + if (!d) + return -ENOMEM; + + rc = libevdev_set_fd(d, fd); + if (rc < 0) + libevdev_free(d); + else + *dev = d; + return rc; +} + +LIBEVDEV_EXPORT void +libevdev_free(struct libevdev *dev) +{ + if (!dev) + return; + + queue_free(dev); + libevdev_reset(dev); + free(dev); +} + +LIBEVDEV_EXPORT void +libevdev_set_log_function(libevdev_log_func_t logfunc, void *data) +{ + log_data.handler = logfunc; + log_data.userdata = data; +} + +LIBEVDEV_EXPORT void +libevdev_set_log_priority(enum libevdev_log_priority priority) +{ + if (priority > LIBEVDEV_LOG_DEBUG) + priority = LIBEVDEV_LOG_DEBUG; + log_data.priority = priority; +} + +LIBEVDEV_EXPORT enum libevdev_log_priority +libevdev_get_log_priority(void) +{ + return log_data.priority; +} + +LIBEVDEV_EXPORT int +libevdev_change_fd(struct libevdev *dev, int fd) +{ + if (!dev->initialized) { + log_bug("device not initialized. call libevdev_set_fd() first\n"); + return -1; + } + dev->fd = fd; + return 0; +} + +LIBEVDEV_EXPORT int +libevdev_set_fd(struct libevdev* dev, int fd) +{ + int rc; + int i; + char buf[256]; + + if (dev->initialized) { + log_bug("device already initialized.\n"); + return -EBADF; + } else if (fd < 0) + return -EBADF; + + libevdev_reset(dev); + + rc = ioctl(fd, EVIOCGBIT(0, sizeof(dev->bits)), dev->bits); + if (rc < 0) + goto out; + + memset(buf, 0, sizeof(buf)); + rc = ioctl(fd, EVIOCGNAME(sizeof(buf) - 1), buf); + if (rc < 0) + goto out; + + free(dev->name); + dev->name = strdup(buf); + if (!dev->name) { + errno = ENOMEM; + goto out; + } + + free(dev->phys); + dev->phys = NULL; + memset(buf, 0, sizeof(buf)); + rc = ioctl(fd, EVIOCGPHYS(sizeof(buf) - 1), buf); + if (rc < 0) { + /* uinput has no phys */ + if (errno != ENOENT) + goto out; + } else { + dev->phys = strdup(buf); + if (!dev->phys) { + errno = ENOMEM; + goto out; + } + } + + free(dev->uniq); + dev->uniq = NULL; + memset(buf, 0, sizeof(buf)); + rc = ioctl(fd, EVIOCGUNIQ(sizeof(buf) - 1), buf); + if (rc < 0) { + if (errno != ENOENT) + goto out; + } else { + dev->uniq = strdup(buf); + if (!dev->uniq) { + errno = ENOMEM; + goto out; + } + } + + rc = ioctl(fd, EVIOCGID, &dev->ids); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGVERSION, &dev->driver_version); + if (rc < 0) + goto out; + + /* Built on a kernel with props, running against a kernel without property + support. This should not be a fatal case, we'll be missing properties but other + than that everything is as expected. + */ + rc = ioctl(fd, EVIOCGPROP(sizeof(dev->props)), dev->props); + if (rc < 0 && errno != EINVAL) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_REL, sizeof(dev->rel_bits)), dev->rel_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(dev->abs_bits)), dev->abs_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_LED, sizeof(dev->led_bits)), dev->led_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(dev->key_bits)), dev->key_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_SW, sizeof(dev->sw_bits)), dev->sw_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_MSC, sizeof(dev->msc_bits)), dev->msc_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_FF, sizeof(dev->ff_bits)), dev->ff_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_SND, sizeof(dev->snd_bits)), dev->snd_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGKEY(sizeof(dev->key_values)), dev->key_values); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGLED(sizeof(dev->led_values)), dev->led_values); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGSW(sizeof(dev->sw_values)), dev->sw_values); + if (rc < 0) + goto out; + + /* rep is a special case, always set it to 1 for both values if EV_REP is set */ + if (bit_is_set(dev->bits, EV_REP)) { + for (i = 0; i < REP_CNT; i++) + set_bit(dev->rep_bits, i); + rc = ioctl(fd, EVIOCGREP, dev->rep_values); + if (rc < 0) + goto out; + } + + for (i = ABS_X; i <= ABS_MAX; i++) { + if (bit_is_set(dev->abs_bits, i)) { + struct input_absinfo abs_info; + rc = ioctl(fd, EVIOCGABS(i), &abs_info); + if (rc < 0) + goto out; + + dev->abs_info[i] = abs_info; + } + } + + dev->fd = fd; + + /* devices with ABS_MT_SLOT - 1 aren't MT devices, + see the documentation for multitouch-related + functions for more details */ + if (!libevdev_has_event_code(dev, EV_ABS, ABS_MT_SLOT - 1) && + libevdev_has_event_code(dev, EV_ABS, ABS_MT_SLOT)) { + const struct input_absinfo *abs_info; + + abs_info = libevdev_get_abs_info(dev, ABS_MT_SLOT); + + dev->num_slots = abs_info->maximum + 1; + dev->mt_slot_vals = calloc(dev->num_slots * ABS_MT_CNT, sizeof(int)); + if (!dev->mt_slot_vals) { + rc = -ENOMEM; + goto out; + } + dev->current_slot = abs_info->value; + + dev->mt_sync.mt_state_sz = sizeof(*dev->mt_sync.mt_state) + + (dev->num_slots) * sizeof(int); + dev->mt_sync.mt_state = calloc(1, dev->mt_sync.mt_state_sz); + + dev->mt_sync.tracking_id_changes_sz = NLONGS(dev->num_slots) * sizeof(long); + dev->mt_sync.tracking_id_changes = malloc(dev->mt_sync.tracking_id_changes_sz); + + dev->mt_sync.slot_update_sz = NLONGS(dev->num_slots * ABS_MT_CNT) * sizeof(long); + dev->mt_sync.slot_update = malloc(dev->mt_sync.slot_update_sz); + + if (!dev->mt_sync.tracking_id_changes || + !dev->mt_sync.slot_update || + !dev->mt_sync.mt_state) { + rc = -ENOMEM; + goto out; + } + + sync_mt_state(dev, 0); + } + + rc = init_event_queue(dev); + if (rc < 0) { + dev->fd = -1; + return -rc; + } + + /* not copying key state because we won't know when we'll start to + * use this fd and key's are likely to change state by then. + * Same with the valuators, really, but they may not change. + */ + + dev->initialized = true; +out: + if (rc) + libevdev_reset(dev); + return rc ? -errno : 0; +} + +LIBEVDEV_EXPORT int +libevdev_get_fd(const struct libevdev* dev) +{ + return dev->fd; +} + +static inline void +init_event(struct libevdev *dev, struct input_event *ev, int type, int code, int value) +{ + ev->time = dev->last_event_time; + ev->type = type; + ev->code = code; + ev->value = value; +} + +static int +sync_key_state(struct libevdev *dev) +{ + int rc; + int i; + unsigned long keystate[NLONGS(KEY_CNT)] = {0}; + + rc = ioctl(dev->fd, EVIOCGKEY(sizeof(keystate)), keystate); + if (rc < 0) + goto out; + + for (i = 0; i < KEY_CNT; i++) { + int old, new; + old = bit_is_set(dev->key_values, i); + new = bit_is_set(keystate, i); + if (old ^ new) { + struct input_event *ev = queue_push(dev); + init_event(dev, ev, EV_KEY, i, new ? 1 : 0); + } + } + + memcpy(dev->key_values, keystate, rc); + + rc = 0; +out: + return rc ? -errno : 0; +} + +static int +sync_sw_state(struct libevdev *dev) +{ + int rc; + int i; + unsigned long swstate[NLONGS(SW_CNT)] = {0}; + + rc = ioctl(dev->fd, EVIOCGSW(sizeof(swstate)), swstate); + if (rc < 0) + goto out; + + for (i = 0; i < SW_CNT; i++) { + int old, new; + old = bit_is_set(dev->sw_values, i); + new = bit_is_set(swstate, i); + if (old ^ new) { + struct input_event *ev = queue_push(dev); + init_event(dev, ev, EV_SW, i, new ? 1 : 0); + } + } + + memcpy(dev->sw_values, swstate, rc); + + rc = 0; +out: + return rc ? -errno : 0; +} + +static int +sync_led_state(struct libevdev *dev) +{ + int rc; + int i; + unsigned long ledstate[NLONGS(LED_CNT)] = {0}; + + rc = ioctl(dev->fd, EVIOCGLED(sizeof(ledstate)), ledstate); + if (rc < 0) + goto out; + + for (i = 0; i < LED_CNT; i++) { + int old, new; + old = bit_is_set(dev->led_values, i); + new = bit_is_set(ledstate, i); + if (old ^ new) { + struct input_event *ev = queue_push(dev); + init_event(dev, ev, EV_LED, i, new ? 1 : 0); + } + } + + memcpy(dev->led_values, ledstate, rc); + + rc = 0; +out: + return rc ? -errno : 0; +} +static int +sync_abs_state(struct libevdev *dev) +{ + int rc; + int i; + + for (i = ABS_X; i < ABS_CNT; i++) { + struct input_absinfo abs_info; + + if (i >= ABS_MT_MIN && i <= ABS_MT_MAX) + continue; + + if (!bit_is_set(dev->abs_bits, i)) + continue; + + rc = ioctl(dev->fd, EVIOCGABS(i), &abs_info); + if (rc < 0) + goto out; + + if (dev->abs_info[i].value != abs_info.value) { + struct input_event *ev = queue_push(dev); + + init_event(dev, ev, EV_ABS, i, abs_info.value); + dev->abs_info[i].value = abs_info.value; + } + } + + rc = 0; +out: + return rc ? -errno : 0; +} + +static int +sync_mt_state(struct libevdev *dev, int create_events) +{ + struct input_event *ev; + struct input_absinfo abs_info; + int rc; + int axis, slot; + int ioctl_success = 0; + int last_reported_slot = 0; + struct mt_sync_state *mt_state = dev->mt_sync.mt_state; + unsigned long *slot_update = dev->mt_sync.slot_update; + unsigned long *tracking_id_changes = dev->mt_sync.tracking_id_changes; + int need_tracking_id_changes = 0; + + memset(dev->mt_sync.slot_update, 0, dev->mt_sync.slot_update_sz); + memset(dev->mt_sync.tracking_id_changes, 0, + dev->mt_sync.tracking_id_changes_sz); + +#define AXISBIT(_slot, _axis) (_slot * ABS_MT_CNT + _axis - ABS_MT_MIN) + + for (axis = ABS_MT_MIN; axis <= ABS_MT_MAX; axis++) { + if (axis == ABS_MT_SLOT) + continue; + + if (!libevdev_has_event_code(dev, EV_ABS, axis)) + continue; + + mt_state->code = axis; + rc = ioctl(dev->fd, EVIOCGMTSLOTS(dev->mt_sync.mt_state_sz), mt_state); + if (rc < 0) { + /* if the first ioctl fails with -EINVAL, chances are the kernel + doesn't support the ioctl. Simply continue */ + if (errno == -EINVAL && !ioctl_success) { + rc = 0; + } else /* if the second, ... ioctl fails, really fail */ + goto out; + } else { + if (ioctl_success == 0) + ioctl_success = 1; + + for (slot = 0; slot < dev->num_slots; slot++) { + + if (*slot_value(dev, slot, axis) == mt_state->val[slot]) + continue; + + if (axis == ABS_MT_TRACKING_ID && + *slot_value(dev, slot, axis) != -1 && + mt_state->val[slot] != -1) { + set_bit(tracking_id_changes, slot); + need_tracking_id_changes = 1; + } + + *slot_value(dev, slot, axis) = mt_state->val[slot]; + + set_bit(slot_update, AXISBIT(slot, axis)); + /* note that this slot has updates */ + set_bit(slot_update, AXISBIT(slot, ABS_MT_SLOT)); + } + + + } + } + + if (!create_events) { + rc = 0; + goto out; + } + + if (need_tracking_id_changes) { + for (slot = 0; slot < dev->num_slots; slot++) { + if (!bit_is_set(tracking_id_changes, slot)) + continue; + + ev = queue_push(dev); + init_event(dev, ev, EV_ABS, ABS_MT_SLOT, slot); + ev = queue_push(dev); + init_event(dev, ev, EV_ABS, ABS_MT_TRACKING_ID, -1); + + last_reported_slot = slot; + } + + ev = queue_push(dev); + init_event(dev, ev, EV_SYN, SYN_REPORT, 0); + } + + for (slot = 0; slot < dev->num_slots; slot++) { + if (!bit_is_set(slot_update, AXISBIT(slot, ABS_MT_SLOT))) + continue; + + ev = queue_push(dev); + init_event(dev, ev, EV_ABS, ABS_MT_SLOT, slot); + last_reported_slot = slot; + + for (axis = ABS_MT_MIN; axis <= ABS_MT_MAX; axis++) { + if (axis == ABS_MT_SLOT || + !libevdev_has_event_code(dev, EV_ABS, axis)) + continue; + + if (bit_is_set(slot_update, AXISBIT(slot, axis))) { + ev = queue_push(dev); + init_event(dev, ev, EV_ABS, axis, *slot_value(dev, slot, axis)); + } + } + } + + /* add one last slot event to make sure the client is on the same + slot as the kernel */ + + rc = ioctl(dev->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info); + if (rc < 0) + goto out; + + dev->current_slot = abs_info.value; + + if (dev->current_slot != last_reported_slot) { + ev = queue_push(dev); + init_event(dev, ev, EV_ABS, ABS_MT_SLOT, dev->current_slot); + } + +#undef AXISBIT + + rc = 0; +out: + return rc ? -errno : 0; +} + +static int +read_more_events(struct libevdev *dev) +{ + int free_elem; + int len; + struct input_event *next; + + free_elem = queue_num_free_elements(dev); + if (free_elem <= 0) + return 0; + + next = queue_next_element(dev); + len = read(dev->fd, next, free_elem * sizeof(struct input_event)); + if (len < 0) { + return -errno; + } else if (len > 0 && len % sizeof(struct input_event) != 0) + return -EINVAL; + else if (len > 0) { + int nev = len/sizeof(struct input_event); + queue_set_num_elements(dev, queue_num_elements(dev) + nev); + } + + return 0; +} + +static inline void +drain_events(struct libevdev *dev) +{ + int rc; + size_t nelem; + int iterations = 0; + const int max_iterations = 8; /* EVDEV_BUF_PACKETS in + kernel/drivers/input/evedev.c */ + + queue_shift_multiple(dev, queue_num_elements(dev), NULL); + + do { + rc = read_more_events(dev); + if (rc == -EAGAIN) + return; + + if (rc < 0) { + log_error("Failed to drain events before sync.\n"); + return; + } + + nelem = queue_num_elements(dev); + queue_shift_multiple(dev, nelem, NULL); + } while (iterations++ < max_iterations && nelem >= queue_size(dev)); + + /* Our buffer should be roughly the same or bigger than the kernel + buffer in most cases, so we usually don't expect to recurse. If + we do, make sure we stop after max_iterations and proceed with + what we have. This could happen if events queue up faster than + we can drain them. + */ + if (iterations >= max_iterations) + log_info("Unable to drain events, buffer size mismatch.\n"); +} + +static int +sync_state(struct libevdev *dev) +{ + int rc = 0; + struct input_event *ev; + + /* see section "Discarding events before synchronizing" in + * libevdev/libevdev.h */ + drain_events(dev); + + if (libevdev_has_event_type(dev, EV_KEY)) + rc = sync_key_state(dev); + if (libevdev_has_event_type(dev, EV_LED)) + rc = sync_led_state(dev); + if (libevdev_has_event_type(dev, EV_SW)) + rc = sync_sw_state(dev); + if (rc == 0 && libevdev_has_event_type(dev, EV_ABS)) + rc = sync_abs_state(dev); + if (rc == 0 && dev->num_slots > -1 && + libevdev_has_event_code(dev, EV_ABS, ABS_MT_SLOT)) + rc = sync_mt_state(dev, 1); + + dev->queue_nsync = queue_num_elements(dev); + + if (dev->queue_nsync > 0) { + ev = queue_push(dev); + init_event(dev, ev, EV_SYN, SYN_REPORT, 0); + dev->queue_nsync++; + } + + return rc; +} + +static int +update_key_state(struct libevdev *dev, const struct input_event *e) +{ + if (!libevdev_has_event_type(dev, EV_KEY)) + return 1; + + if (e->code > KEY_MAX) + return 1; + + set_bit_state(dev->key_values, e->code, e->value != 0); + + return 0; +} + +static int +update_mt_state(struct libevdev *dev, const struct input_event *e) +{ + if (e->code == ABS_MT_SLOT && dev->num_slots > -1) { + int i; + dev->current_slot = e->value; + /* sync abs_info with the current slot values */ + for (i = ABS_MT_SLOT + 1; i <= ABS_MT_MAX; i++) { + if (libevdev_has_event_code(dev, EV_ABS, i)) + dev->abs_info[i].value = *slot_value(dev, dev->current_slot, i); + } + + return 0; + } else if (dev->current_slot == -1) + return 1; + + *slot_value(dev, dev->current_slot, e->code) = e->value; + + return 0; +} + +static int +update_abs_state(struct libevdev *dev, const struct input_event *e) +{ + if (!libevdev_has_event_type(dev, EV_ABS)) + return 1; + + if (e->code > ABS_MAX) + return 1; + + if (e->code >= ABS_MT_MIN && e->code <= ABS_MT_MAX) + update_mt_state(dev, e); + + dev->abs_info[e->code].value = e->value; + + return 0; +} + +static int +update_led_state(struct libevdev *dev, const struct input_event *e) +{ + if (!libevdev_has_event_type(dev, EV_LED)) + return 1; + + if (e->code > LED_MAX) + return 1; + + set_bit_state(dev->led_values, e->code, e->value != 0); + + return 0; +} + +static int +update_sw_state(struct libevdev *dev, const struct input_event *e) +{ + if (!libevdev_has_event_type(dev, EV_SW)) + return 1; + + if (e->code > SW_MAX) + return 1; + + set_bit_state(dev->sw_values, e->code, e->value != 0); + + return 0; +} + +static int +update_state(struct libevdev *dev, const struct input_event *e) +{ + int rc = 0; + + switch(e->type) { + case EV_SYN: + case EV_REL: + break; + case EV_KEY: + rc = update_key_state(dev, e); + break; + case EV_ABS: + rc = update_abs_state(dev, e); + break; + case EV_LED: + rc = update_led_state(dev, e); + break; + case EV_SW: + rc = update_sw_state(dev, e); + break; + } + + dev->last_event_time = e->time; + + return rc; +} + +/** + * Sanitize/modify events where needed. + */ +static inline enum event_filter_status +sanitize_event(const struct libevdev *dev, + struct input_event *ev, + enum SyncState sync_state) +{ + if (unlikely(dev->num_slots > -1 && + libevdev_event_is_code(ev, EV_ABS, ABS_MT_SLOT) && + (ev->value < 0 || ev->value >= dev->num_slots))) { + log_bug("Device \"%s\" received an invalid slot index %d." + "Capping to announced max slot number %d.\n", + dev->name, ev->value, dev->num_slots - 1); + ev->value = dev->num_slots - 1; + return EVENT_FILTER_MODIFIED; + + /* Drop any invalid tracking IDs, they are only supposed to go from + N to -1 or from -1 to N. Never from -1 to -1, or N to M. Very + unlikely to ever happen from a real device. + */ + } else if (unlikely(sync_state == SYNC_NONE && + dev->num_slots > -1 && + libevdev_event_is_code(ev, EV_ABS, ABS_MT_TRACKING_ID) && + ((ev->value == -1 && + *slot_value(dev, dev->current_slot, ABS_MT_TRACKING_ID) == -1) || + (ev->value != -1 && + *slot_value(dev, dev->current_slot, ABS_MT_TRACKING_ID) != -1)))) { + log_bug("Device \"%s\" received a double tracking ID %d in slot %d.\n", + dev->name, ev->value, dev->current_slot); + return EVENT_FILTER_DISCARD; + } + + return EVENT_FILTER_NONE; +} + +LIBEVDEV_EXPORT int +libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event *ev) +{ + int rc = LIBEVDEV_READ_STATUS_SUCCESS; + enum event_filter_status filter_status; + + if (!dev->initialized) { + log_bug("device not initialized. call libevdev_set_fd() first\n"); + return -EBADF; + } else if (dev->fd < 0) + return -EBADF; + + if (!(flags & (LIBEVDEV_READ_FLAG_NORMAL|LIBEVDEV_READ_FLAG_SYNC|LIBEVDEV_READ_FLAG_FORCE_SYNC))) { + log_bug("invalid flags %#x\n.\n", flags); + return -EINVAL; + } + + if (flags & LIBEVDEV_READ_FLAG_SYNC) { + if (dev->sync_state == SYNC_NEEDED) { + rc = sync_state(dev); + if (rc != 0) + return rc; + dev->sync_state = SYNC_IN_PROGRESS; + } + + if (dev->queue_nsync == 0) { + dev->sync_state = SYNC_NONE; + return -EAGAIN; + } + + } else if (dev->sync_state != SYNC_NONE) { + struct input_event e; + + /* call update_state for all events here, otherwise the library has the wrong view + of the device too */ + while (queue_shift(dev, &e) == 0) { + dev->queue_nsync--; + if (sanitize_event(dev, &e, dev->sync_state) != EVENT_FILTER_DISCARD) + update_state(dev, &e); + } + + dev->sync_state = SYNC_NONE; + } + + /* Always read in some more events. Best case this smoothes over a potential SYN_DROPPED, + worst case we don't read fast enough and end up with SYN_DROPPED anyway. + + Except if the fd is in blocking mode and we still have events from the last read, don't + read in any more. + */ + do { + if (!(flags & LIBEVDEV_READ_FLAG_BLOCKING) || + queue_num_elements(dev) == 0) { + rc = read_more_events(dev); + if (rc < 0 && rc != -EAGAIN) + goto out; + } + + if (flags & LIBEVDEV_READ_FLAG_FORCE_SYNC) { + dev->sync_state = SYNC_NEEDED; + rc = LIBEVDEV_READ_STATUS_SYNC; + goto out; + } + + + if (queue_shift(dev, ev) != 0) + return -EAGAIN; + + filter_status = sanitize_event(dev, ev, dev->sync_state); + if (filter_status != EVENT_FILTER_DISCARD) + update_state(dev, ev); + + /* if we disabled a code, get the next event instead */ + } while(filter_status == EVENT_FILTER_DISCARD || + !libevdev_has_event_code(dev, ev->type, ev->code)); + + rc = LIBEVDEV_READ_STATUS_SUCCESS; + if (ev->type == EV_SYN && ev->code == SYN_DROPPED) { + dev->sync_state = SYNC_NEEDED; + rc = LIBEVDEV_READ_STATUS_SYNC; + } + + if (flags & LIBEVDEV_READ_FLAG_SYNC && dev->queue_nsync > 0) { + dev->queue_nsync--; + rc = LIBEVDEV_READ_STATUS_SYNC; + if (dev->queue_nsync == 0) { + struct input_event next; + dev->sync_state = SYNC_NONE; + + if (queue_peek(dev, 0, &next) == 0 && + next.type == EV_SYN && next.code == SYN_DROPPED) + log_info("SYN_DROPPED received after finished " + "sync - you're not keeping up\n"); + } + } + +out: + return rc; +} + +LIBEVDEV_EXPORT int +libevdev_has_event_pending(struct libevdev *dev) +{ + struct pollfd fds = { dev->fd, POLLIN, 0 }; + int rc; + + if (!dev->initialized) { + log_bug("device not initialized. call libevdev_set_fd() first\n"); + return -EBADF; + } else if (dev->fd < 0) + return -EBADF; + + if (queue_num_elements(dev) != 0) + return 1; + + rc = poll(&fds, 1, 0); + return (rc >= 0) ? rc : -errno; +} + +LIBEVDEV_EXPORT const char * +libevdev_get_name(const struct libevdev *dev) +{ + return dev->name ? dev->name : ""; +} + +LIBEVDEV_EXPORT const char * +libevdev_get_phys(const struct libevdev *dev) +{ + return dev->phys; +} + +LIBEVDEV_EXPORT const char * +libevdev_get_uniq(const struct libevdev *dev) +{ + return dev->uniq; +} + +#define STRING_SETTER(field) \ +LIBEVDEV_EXPORT void libevdev_set_##field(struct libevdev *dev, const char *field) \ +{ \ + if (field == NULL) \ + return; \ + free(dev->field); \ + dev->field = strdup(field); \ +} + +STRING_SETTER(name) +STRING_SETTER(phys) +STRING_SETTER(uniq) + + +#define PRODUCT_GETTER(name) \ +LIBEVDEV_EXPORT int libevdev_get_id_##name(const struct libevdev *dev) \ +{ \ + return dev->ids.name; \ +} + +PRODUCT_GETTER(product) +PRODUCT_GETTER(vendor) +PRODUCT_GETTER(bustype) +PRODUCT_GETTER(version) + +#define PRODUCT_SETTER(field) \ +LIBEVDEV_EXPORT void libevdev_set_id_##field(struct libevdev *dev, int field) \ +{ \ + dev->ids.field = field;\ +} + +PRODUCT_SETTER(product) +PRODUCT_SETTER(vendor) +PRODUCT_SETTER(bustype) +PRODUCT_SETTER(version) + +LIBEVDEV_EXPORT int +libevdev_get_driver_version(const struct libevdev *dev) +{ + return dev->driver_version; +} + +LIBEVDEV_EXPORT int +libevdev_has_property(const struct libevdev *dev, unsigned int prop) +{ + return (prop <= INPUT_PROP_MAX) && bit_is_set(dev->props, prop); +} + +LIBEVDEV_EXPORT int +libevdev_enable_property(struct libevdev *dev, unsigned int prop) +{ + if (prop > INPUT_PROP_MAX) + return -1; + + set_bit(dev->props, prop); + return 0; +} + +LIBEVDEV_EXPORT int +libevdev_has_event_type(const struct libevdev *dev, unsigned int type) +{ + return type == EV_SYN ||(type <= EV_MAX && bit_is_set(dev->bits, type)); +} + +LIBEVDEV_EXPORT int +libevdev_has_event_code(const struct libevdev *dev, unsigned int type, unsigned int code) +{ + const unsigned long *mask = NULL; + int max; + + if (!libevdev_has_event_type(dev, type)) + return 0; + + if (type == EV_SYN) + return 1; + + max = type_to_mask_const(dev, type, &mask); + + if (max == -1 || code > (unsigned int)max) + return 0; + + return bit_is_set(mask, code); +} + +LIBEVDEV_EXPORT int +libevdev_get_event_value(const struct libevdev *dev, unsigned int type, unsigned int code) +{ + int value = 0; + + if (!libevdev_has_event_type(dev, type) || !libevdev_has_event_code(dev, type, code)) + return 0; + + switch (type) { + case EV_ABS: value = dev->abs_info[code].value; break; + case EV_KEY: value = bit_is_set(dev->key_values, code); break; + case EV_LED: value = bit_is_set(dev->led_values, code); break; + case EV_SW: value = bit_is_set(dev->sw_values, code); break; + case EV_REP: + switch(code) { + case REP_DELAY: + libevdev_get_repeat(dev, &value, NULL); + break; + case REP_PERIOD: + libevdev_get_repeat(dev, NULL, &value); + break; + default: + value = 0; + break; + } + break; + default: + value = 0; + break; + } + + return value; +} + +LIBEVDEV_EXPORT int +libevdev_set_event_value(struct libevdev *dev, unsigned int type, unsigned int code, int value) +{ + int rc = 0; + struct input_event e; + + if (!libevdev_has_event_type(dev, type) || !libevdev_has_event_code(dev, type, code)) + return -1; + + e.type = type; + e.code = code; + e.value = value; + + if (sanitize_event(dev, &e, SYNC_NONE) != EVENT_FILTER_NONE) + return -1; + + switch(type) { + case EV_ABS: rc = update_abs_state(dev, &e); break; + case EV_KEY: rc = update_key_state(dev, &e); break; + case EV_LED: rc = update_led_state(dev, &e); break; + case EV_SW: rc = update_sw_state(dev, &e); break; + default: + rc = -1; + break; + } + + return rc; +} + +LIBEVDEV_EXPORT int +libevdev_fetch_event_value(const struct libevdev *dev, unsigned int type, unsigned int code, int *value) +{ + if (libevdev_has_event_type(dev, type) && + libevdev_has_event_code(dev, type, code)) { + *value = libevdev_get_event_value(dev, type, code); + return 1; + } else + return 0; +} + +LIBEVDEV_EXPORT int +libevdev_get_slot_value(const struct libevdev *dev, unsigned int slot, unsigned int code) +{ + if (!libevdev_has_event_type(dev, EV_ABS) || !libevdev_has_event_code(dev, EV_ABS, code)) + return 0; + + if (dev->num_slots < 0 || slot >= (unsigned int)dev->num_slots) + return 0; + + if (code > ABS_MT_MAX || code < ABS_MT_MIN) + return 0; + + return *slot_value(dev, slot, code); +} + +LIBEVDEV_EXPORT int +libevdev_set_slot_value(struct libevdev *dev, unsigned int slot, unsigned int code, int value) +{ + if (!libevdev_has_event_type(dev, EV_ABS) || !libevdev_has_event_code(dev, EV_ABS, code)) + return -1; + + if (dev->num_slots == -1 || slot >= (unsigned int)dev->num_slots) + return -1; + + if (code > ABS_MT_MAX || code < ABS_MT_MIN) + return -1; + + if (code == ABS_MT_SLOT) { + if (value < 0 || value >= libevdev_get_num_slots(dev)) + return -1; + dev->current_slot = value; + } + + *slot_value(dev, slot, code) = value; + + return 0; +} + +LIBEVDEV_EXPORT int +libevdev_fetch_slot_value(const struct libevdev *dev, unsigned int slot, unsigned int code, int *value) +{ + if (libevdev_has_event_type(dev, EV_ABS) && + libevdev_has_event_code(dev, EV_ABS, code) && + dev->num_slots >= 0 && + slot < (unsigned int)dev->num_slots) { + *value = libevdev_get_slot_value(dev, slot, code); + return 1; + } else + return 0; +} + +LIBEVDEV_EXPORT int +libevdev_get_num_slots(const struct libevdev *dev) +{ + return dev->num_slots; +} + +LIBEVDEV_EXPORT int +libevdev_get_current_slot(const struct libevdev *dev) +{ + return dev->current_slot; +} + +LIBEVDEV_EXPORT const struct input_absinfo* +libevdev_get_abs_info(const struct libevdev *dev, unsigned int code) +{ + if (!libevdev_has_event_type(dev, EV_ABS) || + !libevdev_has_event_code(dev, EV_ABS, code)) + return NULL; + + return &dev->abs_info[code]; +} + +#define ABS_GETTER(name) \ +LIBEVDEV_EXPORT int libevdev_get_abs_##name(const struct libevdev *dev, unsigned int code) \ +{ \ + const struct input_absinfo *absinfo = libevdev_get_abs_info(dev, code); \ + return absinfo ? absinfo->name : 0; \ +} + +ABS_GETTER(maximum) +ABS_GETTER(minimum) +ABS_GETTER(fuzz) +ABS_GETTER(flat) +ABS_GETTER(resolution) + +#define ABS_SETTER(field) \ +LIBEVDEV_EXPORT void libevdev_set_abs_##field(struct libevdev *dev, unsigned int code, int val) \ +{ \ + if (!libevdev_has_event_code(dev, EV_ABS, code)) \ + return; \ + dev->abs_info[code].field = val; \ +} + +ABS_SETTER(maximum) +ABS_SETTER(minimum) +ABS_SETTER(fuzz) +ABS_SETTER(flat) +ABS_SETTER(resolution) + +LIBEVDEV_EXPORT void +libevdev_set_abs_info(struct libevdev *dev, unsigned int code, const struct input_absinfo *abs) +{ + if (!libevdev_has_event_code(dev, EV_ABS, code)) + return; + + dev->abs_info[code] = *abs; +} + +LIBEVDEV_EXPORT int +libevdev_enable_event_type(struct libevdev *dev, unsigned int type) +{ + int max; + + if (type > EV_MAX) + return -1; + + if (libevdev_has_event_type(dev, type)) + return 0; + + max = libevdev_event_type_get_max(type); + if (max == -1) + return -1; + + set_bit(dev->bits, type); + + if (type == EV_REP) { + int delay = 0, period = 0; + libevdev_enable_event_code(dev, EV_REP, REP_DELAY, &delay); + libevdev_enable_event_code(dev, EV_REP, REP_PERIOD, &period); + } + return 0; +} + +LIBEVDEV_EXPORT int +libevdev_disable_event_type(struct libevdev *dev, unsigned int type) +{ + int max; + + if (type > EV_MAX || type == EV_SYN) + return -1; + + max = libevdev_event_type_get_max(type); + if (max == -1) + return -1; + + clear_bit(dev->bits, type); + + return 0; +} + +LIBEVDEV_EXPORT int +libevdev_enable_event_code(struct libevdev *dev, unsigned int type, + unsigned int code, const void *data) +{ + unsigned int max; + unsigned long *mask = NULL; + + if (libevdev_enable_event_type(dev, type)) + return -1; + + switch(type) { + case EV_SYN: + return 0; + case EV_ABS: + case EV_REP: + if (data == NULL) + return -1; + break; + default: + if (data != NULL) + return -1; + break; + } + + max = type_to_mask(dev, type, &mask); + + if (code > max || (int)max == -1) + return -1; + + set_bit(mask, code); + + if (type == EV_ABS) { + const struct input_absinfo *abs = data; + dev->abs_info[code] = *abs; + } else if (type == EV_REP) { + const int *value = data; + dev->rep_values[code] = *value; + } + + return 0; +} + +LIBEVDEV_EXPORT int +libevdev_disable_event_code(struct libevdev *dev, unsigned int type, unsigned int code) +{ + unsigned int max; + unsigned long *mask = NULL; + + if (type > EV_MAX || type == EV_SYN) + return -1; + + max = type_to_mask(dev, type, &mask); + + if (code > max || (int)max == -1) + return -1; + + clear_bit(mask, code); + + return 0; +} + +LIBEVDEV_EXPORT int +libevdev_kernel_set_abs_info(struct libevdev *dev, unsigned int code, const struct input_absinfo *abs) +{ + int rc; + + if (!dev->initialized) { + log_bug("device not initialized. call libevdev_set_fd() first\n"); + return -EBADF; + } else if (dev->fd < 0) + return -EBADF; + + if (code > ABS_MAX) + return -EINVAL; + + rc = ioctl(dev->fd, EVIOCSABS(code), abs); + if (rc < 0) + rc = -errno; + else + rc = libevdev_enable_event_code(dev, EV_ABS, code, abs); + + return rc; +} + +LIBEVDEV_EXPORT int +libevdev_grab(struct libevdev *dev, enum libevdev_grab_mode grab) +{ + int rc = 0; + + if (!dev->initialized) { + log_bug("device not initialized. call libevdev_set_fd() first\n"); + return -EBADF; + } else if (dev->fd < 0) + return -EBADF; + + if (grab != LIBEVDEV_GRAB && grab != LIBEVDEV_UNGRAB) { + log_bug("invalid grab parameter %#x\n", grab); + return -EINVAL; + } + + if (grab == dev->grabbed) + return 0; + + if (grab == LIBEVDEV_GRAB) + rc = ioctl(dev->fd, EVIOCGRAB, (void *)1); + else if (grab == LIBEVDEV_UNGRAB) + rc = ioctl(dev->fd, EVIOCGRAB, (void *)0); + + if (rc == 0) + dev->grabbed = grab; + + return rc < 0 ? -errno : 0; +} + +LIBEVDEV_EXPORT int +libevdev_event_is_type(const struct input_event *ev, unsigned int type) +{ + return type < EV_CNT && ev->type == type; +} + +LIBEVDEV_EXPORT int +libevdev_event_is_code(const struct input_event *ev, unsigned int type, unsigned int code) +{ + int max; + + if (!libevdev_event_is_type(ev, type)) + return 0; + + max = libevdev_event_type_get_max(type); + return (max > -1 && code <= (unsigned int)max && ev->code == code); +} + +LIBEVDEV_EXPORT const char* +libevdev_event_type_get_name(unsigned int type) +{ + if (type > EV_MAX) + return NULL; + + return ev_map[type]; +} + +LIBEVDEV_EXPORT const char* +libevdev_event_code_get_name(unsigned int type, unsigned int code) +{ + int max = libevdev_event_type_get_max(type); + + if (max == -1 || code > (unsigned int)max) + return NULL; + + return event_type_map[type][code]; +} + +LIBEVDEV_EXPORT const char* +libevdev_property_get_name(unsigned int prop) +{ + if (prop > INPUT_PROP_MAX) + return NULL; + + return input_prop_map[prop]; +} + +LIBEVDEV_EXPORT int +libevdev_event_type_get_max(unsigned int type) +{ + if (type > EV_MAX) + return -1; + + return ev_max[type]; +} + +LIBEVDEV_EXPORT int +libevdev_get_repeat(const struct libevdev *dev, int *delay, int *period) +{ + if (!libevdev_has_event_type(dev, EV_REP)) + return -1; + + if (delay != NULL) + *delay = dev->rep_values[REP_DELAY]; + if (period != NULL) + *period = dev->rep_values[REP_PERIOD]; + + return 0; +} + +LIBEVDEV_EXPORT int +libevdev_kernel_set_led_value(struct libevdev *dev, unsigned int code, enum libevdev_led_value value) +{ + return libevdev_kernel_set_led_values(dev, code, value, -1); +} + +LIBEVDEV_EXPORT int +libevdev_kernel_set_led_values(struct libevdev *dev, ...) +{ + struct input_event ev[LED_MAX + 1]; + enum libevdev_led_value val; + va_list args; + int code; + int rc = 0; + size_t nleds = 0; + + if (!dev->initialized) { + log_bug("device not initialized. call libevdev_set_fd() first\n"); + return -EBADF; + } else if (dev->fd < 0) + return -EBADF; + + memset(ev, 0, sizeof(ev)); + + va_start(args, dev); + code = va_arg(args, unsigned int); + while (code != -1) { + if (code > LED_MAX) { + rc = -EINVAL; + break; + } + val = va_arg(args, enum libevdev_led_value); + if (val != LIBEVDEV_LED_ON && val != LIBEVDEV_LED_OFF) { + rc = -EINVAL; + break; + } + + if (libevdev_has_event_code(dev, EV_LED, code)) { + struct input_event *e = ev; + + while (e->type > 0 && e->code != code) + e++; + + if (e->type == 0) + nleds++; + e->type = EV_LED; + e->code = code; + e->value = (val == LIBEVDEV_LED_ON); + } + code = va_arg(args, unsigned int); + } + va_end(args); + + if (rc == 0 && nleds > 0) { + ev[nleds].type = EV_SYN; + ev[nleds++].code = SYN_REPORT; + + rc = write(libevdev_get_fd(dev), ev, nleds * sizeof(ev[0])); + if (rc > 0) { + nleds--; /* last is EV_SYN */ + while (nleds--) + update_led_state(dev, &ev[nleds]); + } + rc = (rc != -1) ? 0 : -errno; + } + + return rc; +} + +LIBEVDEV_EXPORT int +libevdev_set_clock_id(struct libevdev *dev, int clockid) +{ + if (!dev->initialized) { + log_bug("device not initialized. call libevdev_set_fd() first\n"); + return -EBADF; + } else if (dev->fd < 0) + return -EBADF; + + return ioctl(dev->fd, EVIOCSCLOCKID, &clockid) ? -errno : 0; +} diff --git a/libevdev/libevdev.h b/libevdev/libevdev.h new file mode 100644 index 0000000..6c03cce --- /dev/null +++ b/libevdev/libevdev.h @@ -0,0 +1,2031 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef LIBEVDEV_H +#define LIBEVDEV_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define LIBEVDEV_ATTRIBUTE_PRINTF(_format, _args) __attribute__ ((format (printf, _format, _args))) + +/** + * @mainpage + * + * **libevdev** is a library for handling evdev kernel devices. It abstracts + * the \ref ioctls through type-safe interfaces and provides functions to change + * the appearance of the device. + * + * Development of libevdev is discussed on + * [input-tools@lists.freedesktop.org](http://lists.freedesktop.org/mailman/listinfo/input-tools) + * Please submit patches, questions or general comments there. + * + * Handling events and SYN_DROPPED + * =============================== + * + * libevdev provides an interface for handling events, including most notably + * SYN_DROPPED events. SYN_DROPPED events are sent by the kernel when the + * process does not read events fast enough and the kernel is forced to drop + * some events. This causes the device to get out of sync with the process' + * view of it. libevdev handles this by telling the caller that a SYN_DROPPED + * has been received and that the state of the device is different to what is + * to be expected. It then provides the delta between the previous state and + * the actual state of the device as a set of events. See + * libevdev_next_event() and @ref syn_dropped for more information on how + * SYN_DROPPED is handled. + * + * Signal safety + * ============= + * + * libevdev is signal-safe for the majority of its operations, i.e. many of + * its functions are safe to be called from within a signal handler. + * Check the API documentation to make sure, unless explicitly stated a call + * is not signal safe. + * + * Device handling + * =============== + * + * A libevdev context is valid for a given file descriptor and its + * duration. Closing the file descriptor will not destroy the libevdev device + * but libevdev will not be able to read further events. + * + * libevdev does not attempt duplicate detection. Initializing two libevdev + * devices for the same fd is valid and behaves the same as for two different + * devices. + * + * libevdev does not handle the file descriptors directly, it merely uses + * them. The caller is responsible for opening the file descriptors, setting + * them to O_NONBLOCK and handling permissions. + * + * Where does libevdev sit? + * ======================== + * + * libevdev is essentially a `read(2)` on steroids for `/dev/input/eventX` + * devices. It sits below the process that handles input events, in between + * the kernel and that process. In the simplest case, e.g. an evtest-like tool + * the stack would look like this: + * + * kernel → libevdev → evtest + * + * For X.Org input modules, the stack would look like this: + * + * kernel → libevdev → xf86-input-evdev → X server → X client + * + * For Weston/Wayland, the stack would look like this: + * + * kernel → libevdev → Weston → Wayland client + * + * libevdev does **not** have knowledge of X clients or Wayland clients, it is + * too low in the stack. + * + * Example + * ======= + * Below is a simple example that shows how libevdev could be used. This example + * opens a device, checks for relative axes and a left mouse button and if it + * finds them monitors the device to print the event. + * + * @code + struct libevdev *dev = NULL; + int fd; + int rc = 1; + + fd = open("/dev/input/event0", O_RDONLY|O_NONBLOCK); + rc = libevdev_new_from_fd(fd, &dev); + if (rc < 0) { + fprintf(stderr, "Failed to init libevdev (%s)\n", strerror(-rc)); + exit(1); + } + printf("Input device name: \"%s\"\n", libevdev_get_name(dev)); + printf("Input device ID: bus %#x vendor %#x product %#x\n", + libevdev_get_id_bustype(dev), + libevdev_get_id_vendor(dev), + libevdev_get_id_product(dev)); + if (!libevdev_has_event_type(dev, EV_REL) || + !libevdev_has_event_code(dev, EV_KEY, BTN_LEFT)) { + printf("This device does not look like a mouse\n"); + exit(1); + } + + do { + struct input_event ev; + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + if (rc == 0) + printf("Event: %s %s %d\n", + libevdev_get_event_type_name(ev.type), + libevdev_get_event_code_name(ev.type, ev.code), + ev.value); + } while (rc == 1 || rc == 0 || rc == -EAGAIN); + @endcode + * + * A more complete example is available with the libevdev-events tool here: + * http://cgit.freedesktop.org/libevdev/tree/tools/libevdev-events.c + * + * Backwards compatibility with older kernel + * ========================================= + * libevdev attempts to build and run correctly on a number of kernel versions. + * If features required are not available, libevdev attempts to work around them + * in the most reasonable way. For more details see \ref backwardscompatibility. + * + * License information + * =================== + * libevdev is licensed under the + * [X11 license](http://cgit.freedesktop.org/libevdev/tree/COPYING). + * + * Reporting bugs + * ============== + * Please report bugs in the freedesktop.org bugzilla under the libevdev product: + * https://bugs.freedesktop.org/enter_bug.cgi?product=libevdev + */ + +/** + * @page syn_dropped SYN_DROPPED handling + * + * This page describes how libevdev handles SYN_DROPPED events. + * + * Receiving SYN_DROPPED events + * ============================ + * + * The kernel sends evdev events separated by an event of type EV_SYN and + * code SYN_REPORT. Such an event marks the end of a frame of hardware + * events. The number of events between SYN_REPORT events is arbitrary and + * depends on the hardware. An example event sequence may look like this: + * @code + EV_ABS ABS_X 9 + EV_ABS ABS_Y 8 + EV_SYN SYN_REPORT 0 + ------------------------ + EV_ABS ABS_X 10 + EV_ABS ABS_Y 10 + EV_KEY BTN_TOUCH 1 + EV_SYN SYN_REPORT 0 + ------------------------ + EV_ABS ABS_X 11 + EV_SYN SYN_REPORT 0 + * @endcode + * + * Events are handed to the client buffer as they appear, the kernel adjusts + * the buffer size to handle at least one full event. In the normal case, + * the client reads the event and the kernel can place the next event in the + * buffer. If the client is not fast enough, the kernel places an event of + * type EV_SYN and code SYN_DROPPED into the buffer, effectively notifying + * the client that some events were lost. The above example event sequence + * may look like this (note the missing/repeated events): + * @code + EV_ABS ABS_X 9 + EV_ABS ABS_Y 8 + EV_SYN SYN_REPORT 0 + ------------------------ + EV_ABS ABS_X 10 + EV_ABS ABS_Y 10 + EV_SYN SYN_DROPPED 0 + EV_ABS ABS_Y 15 + EV_SYN SYN_REPORT 0 + ------------------------ + EV_ABS ABS_X 11 + EV_KEY BTN_TOUCH 0 + EV_SYN SYN_REPORT 0 + * @endcode + * + * A SYN_DROPPED event may be recieved at any time in the event sequence. + * When a SYN_DROPPED event is received, the client must: + * * discard all events since the last SYN_REPORT + * * discard all events until including the next SYN_REPORT + * These event are part of incomplete event frames. + * + * Synchronizing the state of the device + * ===================================== + * + * The handling of the device after a SYN_DROPPED depends on the available + * event codes. For all event codes of type EV_REL, no handling is + * necessary, there is no state attached. For all event codes of type + * EV_KEY, EV_SW, EV_LED and EV_SND, the matching @ref ioctls retrieve the + * current state. The caller must then compare the last-known state to the + * retrieved state and handle the deltas accordingly. + * libevdev simplifies this approach: if the state of the device has + * changed, libevdev generates an event for each code with the new value and + * passes it to the caller during libevdev_next_event() if + * @ref LIBEVDEV_READ_FLAG_SYNC is set. + * + * For events of type EV_ABS and an event code less than ABS_MT_SLOT, the + * handling of state changes is as described above. For events between + * ABS_MT_SLOT and ABS_MAX, the event handling differs. + * Slots are the vehicles to transport information for multiple simultaneous + * touchpoints on a device. Slots are re-used once a touchpoint has ended. + * The kernel sends an ABS_MT_SLOT event whenever the current slot + * changes; any event in the above axis range applies only to the currently + * active slot. + * Thus, an event sequence from a slot-capable device may look like this: + * @code + EV_ABS ABS_MT_POSITION_Y 10 + EV_ABS ABS_MT_SLOT 1 + EV_ABS ABS_MT_POSITION_X 100 + EV_ABS ABS_MT_POSITION_Y 80 + EV_SYN SYN_REPORT 0 + * @endcode + * Note the lack of ABS_MT_SLOT: the first ABS_MT_POSITION_Y applies to + * a slot opened previously, and is the only axis that changed for that + * slot. The touchpoint in slot 1 now has position 100/80. + * The kernel does not provide events if a value does not change, and does + * not send ABS_MT_SLOT events if the slot does not change, or none of the + * values within a slot changes. A client must thus keep the state for each + * slot. + * + * If a SYN_DROPPED is received, the client must sync all slots + * individually and update its internal state. libevdev simplifies this by + * generating multiple events: + * * for each slot on the device, libevdev generates an + * ABS_MT_SLOT event with the value set to the slot number + * * for each event code between ABS_MT_SLOT + 1 and ABS_MAX that changed + * state for this slot, libevdev generates an event for the new state + * * libevdev sends a final ABS_MT_SLOT event for the current slot as + * seen by the kernel + * * libevdev terminates this sequence with an EV_SYN SYN_REPORT event + * + * An example event sequence for such a sync may look like this: + * @code + EV_ABS ABS_MT_SLOT 0 + EV_ABS ABS_MT_POSITION_Y 10 + EV_ABS ABS_MT_SLOT 1 + EV_ABS ABS_MT_POSITION_X 100 + EV_ABS ABS_MT_POSITION_Y 80 + EV_ABS ABS_MT_SLOT 2 + EV_ABS ABS_MT_POSITION_Y 8 + EV_ABS ABS_MT_PRESSURE 12 + EV_ABS ABS_MT_SLOT 1 + EV_SYN SYN_REPORT 0 + * @endcode + * Note the terminating ABS_MT_SLOT event, this indicates that the kernel + * currently has slot 1 active. + * + * Synchronizing ABS_MT_TRACKING_ID + * ================================ + * + * The event code ABS_MT_TRACKING_ID is used to denote the start and end of + * a touch point within a slot. An ABS_MT_TRACKING_ID of zero or greater + * denotes the start of a touchpoint, an ABS_MT_TRACKING_ID of -1 denotes + * the end of a touchpoint within this slot. During SYN_DROPPED, a touch + * point may have ended and re-started within a slot - a client must check + * the ABS_MT_TRACKING_ID. libevdev simplifies this by emulating extra + * events if the ABS_MT_TRACKING_ID has changed: + * * if the ABS_MT_TRACKING_ID was valid and is -1, libevdev enqueues an + * ABS_MT_TRACKING_ID event with value -1. + * * if the ABS_MT_TRACKING_ID was -1 and is now a valid ID, libevdev + * enqueues an ABS_MT_TRACKING_ID event with the current value. + * * if the ABS_MT_TRACKING_ID was a valid ID and is now a different valid + * ID, libevev enqueues an ABS_MT_TRACKING_ID event with value -1 and + * another ABS_MT_TRACKING_ID event with the new value. + * + * An example event sequence for such a sync may look like this: + * @code + EV_ABS ABS_MT_SLOT 0 + EV_ABS ABS_MT_TRACKING_ID -1 + EV_ABS ABS_MT_SLOT 2 + EV_ABS ABS_MT_TRACKING_ID -1 + EV_SYN SYN_REPORT 0 + ------------------------ + EV_ABS ABS_MT_SLOT 1 + EV_ABS ABS_MT_POSITION_X 100 + EV_ABS ABS_MT_POSITION_Y 80 + EV_ABS ABS_MT_SLOT 2 + EV_ABS ABS_MT_TRACKING_ID 45 + EV_ABS ABS_MT_POSITION_Y 8 + EV_ABS ABS_MT_PRESSURE 12 + EV_ABS ABS_MT_SLOT 1 + EV_SYN SYN_REPORT 0 + * @endcode + * Note how the touchpoint in slot 0 was terminated, the touchpoint in slot + * 2 was terminated and then started with a new ABS_MT_TRACKING_ID. The touchpoint + * in slot 1 maintained the same ABS_MT_TRACKING_ID and only updated the + * coordinates. Slot 1 is the currently active slot. + * + * In the case of a SYN_DROPPED event, a touch point may be invisible to a + * client if it started after SYN_DROPPED and finished before the client + * handles events again. The below example shows an example event sequence + * and what libevdev sees in the case of a SYN_DROPPED event: + * @code + + kernel | userspace + | + EV_ABS ABS_MT_SLOT 0 | EV_ABS ABS_MT_SLOT 0 + EV_ABS ABS_MT_TRACKING_ID -1 | EV_ABS ABS_MT_TRACKING_ID -1 + EV_SYN SYN_REPORT 0 | EV_SYN SYN_REPORT 0 + ------------------------ | ------------------------ + EV_ABS ABS_MT_TRACKING_ID 30 | + EV_ABS ABS_MT_POSITION_X 100 | + EV_ABS ABS_MT_POSITION_Y 80 | + EV_SYN SYN_REPORT 0 | SYN_DROPPED + ------------------------ | + EV_ABS ABS_MT_TRACKING_ID -1 | + EV_SYN SYN_REPORT 0 | + ------------------------ | ------------------------ + EV_ABS ABS_MT_SLOT 1 | EV_ABS ABS_MT_SLOT 1 + EV_ABS ABS_MT_POSITION_X 90 | EV_ABS ABS_MT_POSITION_X 90 + EV_ABS ABS_MT_POSITION_Y 10 | EV_ABS ABS_MT_POSITION_Y 10 + EV_SYN SYN_REPORT 0 | EV_SYN SYN_REPORT 0 + * @endcode + * If such an event sequence occurs, libevdev will send all updated axes + * during the sync process. Axis events may thus be generated for devices + * without a currently valid ABS_MT_TRACKING_ID. Specifically for the above + * example, the client would receive the following event sequence: + * @code + EV_ABS ABS_MT_SLOT 0 ← LIBEVDEV_READ_FLAG_NORMAL + EV_ABS ABS_MT_TRACKING_ID -1 + EV_SYN SYN_REPORT 0 + ------------------------ + EV_SYN SYN_DROPPED 0 → LIBEVDEV_READ_STATUS_SYNC + ------------------------ + EV_ABS ABS_MT_POSITION_X 100 ← LIBEVDEV_READ_FLAG_SYNC + EV_ABS ABS_MT_POSITION_Y 80 + EV_SYN SYN_REPORT 0 + ----------------------------- → -EGAIN + EV_ABS ABS_MT_SLOT 1 ← LIBEVDEV_READ_FLAG_NORMAL + EV_ABS ABS_MT_POSITION_X 90 + EV_ABS ABS_MT_POSITION_Y 10 + EV_SYN SYN_REPORT 0 + ------------------- + * @endcode + * The axis events do not reflect the position of a current touch point, a + * client must take care not to generate a new touch point based on those + * updates. + * + * Discarding events before synchronizing + * ===================================== + * + * The kernel implements the client buffer as a ring buffer. SYN_DROPPED + * events are handled when the buffer is full and a new event is received + * from a device. All existing events are discarded, a SYN_DROPPED is added + * to the buffer followed by the actual device event. Further events will be + * appended to the buffer until it is either read by the client, or filled + * again, at which point the sequence repeats. + * + * When the client reads the buffer, the buffer will thus always consist of + * exactly one SYN_DROPPED event followed by an unspecified number of real + * events. The data the ioctls return is the current state of the device, + * i.e. the state after all these events have been processed. For example, + * assume the buffer contains the following sequence: + * + * @code + EV_SYN SYN_DROPPED + EV_ABS ABS_X 1 + EV_SYN SYN_REPORT 0 + EV_ABS ABS_X 2 + EV_SYN SYN_REPORT 0 + EV_ABS ABS_X 3 + EV_SYN SYN_REPORT 0 + EV_ABS ABS_X 4 + EV_SYN SYN_REPORT 0 + EV_ABS ABS_X 5 + EV_SYN SYN_REPORT 0 + EV_ABS ABS_X 6 + EV_SYN SYN_REPORT 0 + * @endcode + * An ioctl at any time in this sequence will return a value of 6 for ABS_X. + * + * libevdev discards all events after a SYN_DROPPED to ensure the events + * during @ref LIBEVDEV_READ_FLAG_SYNC represent the last known state of the + * device. This loses some granularity of the events especially as the time + * between the SYN_DROPPED and the sync process increases. It does however + * avoid spurious cursor movements. In the above example, the event sequence + * by libevdev is: + * @code + EV_SYN SYN_DROPPED + EV_ABS ABS_X 6 + EV_SYN SYN_REPORT 0 + @endcode + */ + +/** + * @page backwardscompatibility Compatibility and Behavior across kernel versions + * + * This page describes libevdev's behavior when the build-time kernel and the + * run-time kernel differ in their feature set. + * + * With the exception of event names, libevdev defines features that may be + * missing on older kernels and building on such kernels will not disable + * features. Running libevdev on a kernel that is missing some feature will + * change libevdev's behavior. In most cases, the new behavior should be + * obvious, but it is spelled out below in detail. + * + * Minimum requirements + * ==================== + * libevdev requires a 2.6.36 kernel as minimum. Specifically, it requires + * kernel-support for ABS_MT_SLOT. + * + * Event and input property names + * ============================== + * Event names and input property names are defined at build-time by the + * linux/input.h shipped with libevdev. + * The list of event names is compiled at build-time, any events not defined + * at build time will not resolve. Specifically, + * libevdev_event_code_get_name() for an undefined type or code will + * always return NULL. Likewise, libevdev_property_get_name() will return NULL + * for properties undefined at build-time. + * + * Input properties + * ================ + * If the kernel does not support input properties, specifically the + * EVIOCGPROPS ioctl, libevdev does not expose input properties to the caller. + * Specifically, libevdev_has_property() will always return 0 unless the + * property has been manually set with libevdev_enable_property(). + * + * This also applies to the libevdev-uinput code. If uinput does not honor + * UI_SET_PROPBIT, libevdev will continue without setting the properties on + * the device. + * + * MT slot behavior + * ================= + * If the kernel does not support the EVIOCGMTSLOTS ioctl, libevdev + * assumes all values in all slots are 0 and continues without an error. + * + * SYN_DROPPED behavior + * ==================== + * A kernel without SYN_DROPPED won't send such an event. libevdev_next_event() + * will never require the switch to sync mode. + */ + +/** + * @page ioctls evdev ioctls + * + * This page lists the status of the evdev-specific ioctls in libevdev. + * + *
+ *
EVIOCGVERSION:
+ *
supported, see libevdev_get_driver_version()
+ *
EVIOCGID:
+ *
supported, see libevdev_get_id_product(), libevdev_get_id_vendor(), + * libevdev_get_id_bustype(), libevdev_get_id_version()
+ *
EVIOCGREP:
+ *
supported, see libevdev_get_event_value())
+ *
EVIOCSREP:
+ *
supported, see libevdev_enable_event_code()
+ *
EVIOCGKEYCODE:
+ *
currently not supported
+ *
EVIOCGKEYCODE:
+ *
currently not supported
+ *
EVIOCSKEYCODE:
+ *
currently not supported
+ *
EVIOCSKEYCODE:
+ *
currently not supported
+ *
EVIOCGNAME:
+ *
supported, see libevdev_get_name()
+ *
EVIOCGPHYS:
+ *
supported, see libevdev_get_phys()
+ *
EVIOCGUNIQ:
+ *
supported, see libevdev_get_uniq()
+ *
EVIOCGPROP:
+ *
supported, see libevdev_has_property()
+ *
EVIOCGMTSLOTS:
+ *
supported, see libevdev_get_num_slots(), libevdev_get_slot_value()
+ *
EVIOCGKEY:
+ *
supported, see libevdev_has_event_code(), libevdev_get_event_value()
+ *
EVIOCGLED:
+ *
supported, see libevdev_has_event_code(), libevdev_get_event_value()
+ *
EVIOCGSND:
+ *
currently not supported
+ *
EVIOCGSW:
+ *
supported, see libevdev_has_event_code(), libevdev_get_event_value()
+ *
EVIOCGBIT:
+ *
supported, see libevdev_has_event_code(), libevdev_get_event_value()
+ *
EVIOCGABS:
+ *
supported, see libevdev_has_event_code(), libevdev_get_event_value(), + * libevdev_get_abs_info()
+ *
EVIOCSABS:
+ *
supported, see libevdev_kernel_set_abs_info()
+ *
EVIOCSFF:
+ *
currently not supported
+ *
EVIOCRMFF:
+ *
currently not supported
+ *
EVIOCGEFFECTS:
+ *
currently not supported
+ *
EVIOCGRAB:
+ *
supported, see libevdev_grab()
+ *
EVIOCSCLOCKID:
+ *
supported, see libevdev_set_clock_id()
+ *
EVIOCREVOKE:
+ *
currently not supported, see + * http://lists.freedesktop.org/archives/input-tools/2014-January/000688.html
+ *
+ * + */ + +/** + * @page kernel_header Kernel header + * + * libevdev provides its own copy of the Linux kernel header file and + * compiles against the definitions define here. Event type and event code + * names, etc. are taken from the file below: + * @include linux/input.h + */ + +/** + * @page testing libevdev-internal test suite + * + * libevdev's internal test suite uses the + * [Check unit testing framework](http://check.sourceforge.net/). Tests are + * divided into test suites and test cases. Most tests create a uinput device, + * so you'll need to run as root, and your kernel must have + * CONFIG_INPUT_UINPUT enabled. + * + * To run a specific suite only: + * + * export CK_RUN_SUITE="suite name" + * + * To run a specific test case only: + * + * export CK_RUN_TEST="test case name" + * + * To get a list of all suites or tests: + * + * git grep "suite_create" + * git grep "tcase_create" + * + * By default, Check forks, making debugging harder. The test suite tries to detect + * if it is running inside gdb and disable forking. If that doesn't work for + * some reason, run gdb as below to avoid forking. + * + * sudo CK_FORK=no CK_RUN_TEST="test case name" gdb ./test/test-libevdev + * + * A special target `make gcov-report.txt` exists that runs gcov and leaves a + * `libevdev.c.gcov` file. Check that for test coverage. + * + * `make check` is hooked up to run the test and gcov (again, needs root). + * + * The test suite creates a lot of devices, very quickly. Add the following + * xorg.conf.d snippet to avoid the devices being added as X devices (at the + * time of writing, mutter can't handle these devices and exits after getting + * a BadDevice error). + * + * $ cat /etc/X11/xorg.conf.d/99-ignore-libevdev-devices.conf + * Section "InputClass" + * Identifier "Ignore libevdev test devices" + * MatchProduct "libevdev test device" + * Option "Ignore" "on" + * EndSection + * + */ + +/** + * @defgroup init Initialization and setup + * + * Initialization, initial setup and file descriptor handling. + * These functions are the main entry points for users of libevdev, usually a + * caller will use this series of calls: + * + * @code + * struct libevdev *dev; + * int err; + * + * dev = libevdev_new(); + * if (!dev) + * return ENOMEM; + * + * err = libevdev_set_fd(dev, fd); + * if (err < 0) { + * printf("Failed (errno %d): %s\n", -err, strerror(-err)); + * + * libevdev_free(dev); + * @endcode + * + * libevdev_set_fd() is the central call and initializes the internal structs + * for the device at the given fd. libevdev functions will fail if called + * before libevdev_set_fd() unless documented otherwise. + */ + +/** + * @defgroup bits Querying device capabilities + * + * Abstraction functions to handle device capabilities, specificially + * device properties such as the name of the device and the bits + * representing the events suppported by this device. + * + * The logical state returned may lag behind the physical state of the device. + * libevdev queries the device state on libevdev_set_fd() and then relies on + * the caller to parse events through libevdev_next_event(). If a caller does not + * use libevdev_next_event(), libevdev will not update the internal state of the + * device and thus returns outdated values. + */ + +/** + * @defgroup mt Multi-touch related functions + * Functions for querying multi-touch-related capabilities. MT devices + * following the kernel protocol B (using ABS_MT_SLOT) provide multiple touch + * points through so-called slots on the same axis. The slots are enumerated, + * a client reading from the device will first get an ABS_MT_SLOT event, then + * the values of axes changed in this slot. Multiple slots may be provided in + * before an EV_SYN event. + * + * As with @ref bits, the logical state of the device as seen by the library + * depends on the caller using libevdev_next_event(). + * + * The Linux kernel requires all axes on a device to have a semantic + * meaning, matching the axis names in linux/input.h. Some devices merely + * export a number of axes beyond the available axis list. For those + * devices, the multitouch information is invalid. Specfically, if a device + * provides the ABS_MT_SLOT axis AND also the (ABS_MT_SLOT - 1) axis, the + * device is not treated as multitouch device. No slot information is + * available and the ABS_MT axis range for these devices is treated as all + * other EV_ABS axes. + * + * Note that because of limitations in the kernel API, such fake multitouch + * devices can not be reliably synched after a SYN_DROPPED event. libevdev + * ignores all ABS_MT axis values during the sync process and instead + * relies on the device to send the current axis value with the first event + * after SYN_DROPPED. + */ + +/** + * @defgroup kernel Modifying the appearance or capabilities of the device + * + * Modifying the set of events reported by this device. By default, the + * libevdev device mirrors the kernel device, enabling only those bits + * exported by the kernel. This set of functions enable or disable bits as + * seen from the caller. + * + * Enabling an event type or code does not affect event reporting - a + * software-enabled event will not be generated by the physical hardware. + * Disabling an event will prevent libevdev from routing such events to the + * caller. Enabling and disabling event types and codes is at the library + * level and thus only affects the caller. + * + * If an event type or code is enabled at kernel-level, future users of this + * device will see this event enabled. Currently there is no option of + * disabling an event type or code at kernel-level. + */ + +/** + * @defgroup misc Miscellaneous helper functions + * + * Functions for printing or querying event ranges. The list of names is + * compiled into libevdev and is independent of the run-time kernel. + * Likewise, the max for each event type is compiled in and does not check + * the kernel at run-time. + */ + +/** + * @defgroup events Event handling + * + * Functions to handle events and fetch the current state of the event. + * libevdev updates its internal state as the event is processed and forwarded + * to the caller. Thus, the libevdev state of the device should always be identical + * to the caller's state. It may however lag behind the actual state of the device. + */ + +/** + * @ingroup init + * + * Opaque struct representing an evdev device. + */ +struct libevdev; + +/** + * @ingroup events + */ +enum libevdev_read_flag { + LIBEVDEV_READ_FLAG_SYNC = 1, /**< Process data in sync mode */ + LIBEVDEV_READ_FLAG_NORMAL = 2, /**< Process data in normal mode */ + LIBEVDEV_READ_FLAG_FORCE_SYNC = 4, /**< Pretend the next event is a SYN_DROPPED and + require the caller to sync */ + LIBEVDEV_READ_FLAG_BLOCKING = 8 /**< The fd is not in O_NONBLOCK and a read may block */ +}; + +/** + * @ingroup init + * + * Initialize a new libevdev device. This function only allocates the + * required memory and initializes the struct to sane default values. + * To actually hook up the device to a kernel device, use + * libevdev_set_fd(). + * + * Memory allocated through libevdev_new() must be released by the + * caller with libevdev_free(). + * + * @see libevdev_set_fd + * @see libevdev_free + */ +struct libevdev* libevdev_new(void); + +/** + * @ingroup init + * + * Initialize a new libevdev device from the given fd. + * + * This is a shortcut for + * + @code + int err; + struct libevdev *dev = libevdev_new(); + err = libevdev_set_fd(dev, fd); + @endcode + * + * @param fd A file descriptor to the device in O_RDWR or O_RDONLY mode. + * @param[out] dev The newly initialized evdev device. + * + * @return On success, 0 is returned and dev is set to the newly + * allocated struct. On failure, a negative errno is returned and the value + * of dev is undefined. + * + * @see libevdev_free + */ +int libevdev_new_from_fd(int fd, struct libevdev **dev); + +/** + * @ingroup init + * + * Clean up and free the libevdev struct. After completion, the struct + * libevdev is invalid and must not be used. + * + * @param dev The evdev device + * + * @note This function may be called before libevdev_set_fd(). + */ +void libevdev_free(struct libevdev *dev); + +/** + * @ingroup init + */ +enum libevdev_log_priority { + LIBEVDEV_LOG_ERROR = 10, /**< critical errors and application bugs */ + LIBEVDEV_LOG_INFO = 20, /**< informational messages */ + LIBEVDEV_LOG_DEBUG = 30 /**< debug information */ +}; + +/** + * @ingroup init + * + * Logging function called by library-internal logging. + * This function is expected to treat its input like printf would. + * + * @param priority Log priority of this message + * @param data User-supplied data pointer (see libevdev_set_log_function()) + * @param file libevdev source code file generating this message + * @param line libevdev source code line generating this message + * @param func libevdev source code function generating this message + * @param format printf-style format string + * @param args List of arguments + * + * @see libevdev_set_log_function + */ +typedef void (*libevdev_log_func_t)(enum libevdev_log_priority priority, + void *data, + const char *file, int line, + const char *func, + const char *format, va_list args) + LIBEVDEV_ATTRIBUTE_PRINTF(6, 0); + +/** + * @ingroup init + * + * Set a printf-style logging handler for library-internal logging. The default + * logging function is to stdout. + * + * @param logfunc The logging function for this device. If NULL, the current + * logging function is unset and no logging is performed. + * @param data User-specific data passed to the log handler. + * + * @note This function may be called before libevdev_set_fd(). + */ +void libevdev_set_log_function(libevdev_log_func_t logfunc, void *data); + +/** + * @ingroup init + * + * Define the minimum level to be printed to the log handler. + * Messages higher than this level are printed, others are discarded. This + * is a global setting and applies to any future logging messages. + * + * @param priority Minimum priority to be printed to the log. + * + */ +void libevdev_set_log_priority(enum libevdev_log_priority priority); + +/** + * @ingroup init + * + * Return the current log priority level. Messages higher than this level + * are printed, others are discarded. This is a global setting. + * + * @return the current log level + */ +enum libevdev_log_priority libevdev_get_log_priority(void); + +/** + * @ingroup init + */ +enum libevdev_grab_mode { + LIBEVDEV_GRAB = 3, /**< Grab the device if not currently grabbed */ + LIBEVDEV_UNGRAB = 4 /**< Ungrab the device if currently grabbed */ +}; + +/** + * @ingroup init + * + * Grab or ungrab the device through a kernel EVIOCGRAB. This prevents other + * clients (including kernel-internal ones such as rfkill) from receiving + * events from this device. + * + * This is generally a bad idea. Don't do this. + * + * Grabbing an already grabbed device, or ungrabbing an ungrabbed device is + * a noop and always succeeds. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param grab If true, grab the device. Otherwise ungrab the device. + * + * @return 0 if the device was successfull grabbed or ungrabbed, or a + * negative errno in case of failure. + */ +int libevdev_grab(struct libevdev *dev, enum libevdev_grab_mode grab); + +/** + * @ingroup init + * + * Set the fd for this struct and initialize internal data. + * The fd must be in O_RDONLY or O_RDWR mode. + * + * This function may only be called once per device. If the device changed and + * you need to re-read a device, use libevdev_free() and libevdev_new(). If + * you need to change the fd after closing and re-opening the same device, use + * libevdev_change_fd(). + * + * Unless otherwise specified, libevdev function behavior is undefined until + * a successfull call to libevdev_set_fd(). + * + * @param dev The evdev device + * @param fd The file descriptor for the device + * + * @return 0 on success, or a negative errno on failure + * + * @see libevdev_change_fd + * @see libevdev_new + * @see libevdev_free + */ +int libevdev_set_fd(struct libevdev* dev, int fd); + +/** + * @ingroup init + * + * Change the fd for this device, without re-reading the actual device. If the fd + * changes after initializing the device, for example after a VT-switch in the + * X.org X server, this function updates the internal fd to the newly opened. + * No check is made that new fd points to the same device. If the device has + * changed, libevdev's behavior is undefined. + * + * libevdev does not sync itself after changing the fd and keeps the current + * device state. Use libevdev_next_event with the + * @ref LIBEVDEV_READ_FLAG_FORCE_SYNC flag to force a re-sync. + * + * The example code below illustrates how to force a re-sync of the + * library-internal state. Note that this code doesn't handle the events in + * the caller, it merely forces an update of the internal library state. + @code + struct input_event ev; + libevdev_change_fd(dev, new_fd); + libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev); + while (libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev) == LIBEVDEV_READ_STATUS_SYNC) + ; // noop + @endcode + * + * The fd may be open in O_RDONLY or O_RDWR. + * + * It is an error to call this function before calling libevdev_set_fd(). + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param fd The new fd + * + * @return 0 on success, or -1 on failure. + * + * @see libevdev_set_fd + */ +int libevdev_change_fd(struct libevdev* dev, int fd); + +/** + * @ingroup init + * + * @param dev The evdev device + * + * @return The previously set fd, or -1 if none had been set previously. + * @note This function may be called before libevdev_set_fd(). + */ +int libevdev_get_fd(const struct libevdev* dev); + + +/** + * @ingroup events + */ +enum libevdev_read_status { + /** + * libevdev_next_event() has finished without an error + * and an event is available for processing. + * + * @see libevdev_next_event + */ + LIBEVDEV_READ_STATUS_SUCCESS = 0, + /** + * Depending on the libevdev_next_event() read flag: + * * libevdev received a SYN_DROPPED from the device, and the caller should + * now resync the device, or, + * * an event has been read in sync mode. + * + * @see libevdev_next_event + */ + LIBEVDEV_READ_STATUS_SYNC = 1 +}; +/** + * @ingroup events + * + * Get the next event from the device. This function operates in two different + * modes: normal mode or sync mode. + * + * In normal mode (when flags has @ref LIBEVDEV_READ_FLAG_NORMAL set), this + * function returns @ref LIBEVDEV_READ_STATUS_SUCCESS and returns the event + * in the argument @p ev. If no events are available at this + * time, it returns -EAGAIN and ev is undefined. + * + * If the current event is an EV_SYN SYN_DROPPED event, this function returns + * @ref LIBEVDEV_READ_STATUS_SYNC and ev is set to the EV_SYN event. + * The caller should now call this function with the + * @ref LIBEVDEV_READ_FLAG_SYNC flag set, to get the set of events that make up the + * device state delta. This function returns @ref LIBEVDEV_READ_STATUS_SYNC for + * each event part of that delta, until it returns -EAGAIN once all events + * have been synced. For more details on what libevdev does to sync after a + * SYN_DROPPED event, see @ref syn_dropped. + * + * If a device needs to be synced by the caller but the caller does not call + * with the @ref LIBEVDEV_READ_FLAG_SYNC flag set, all events from the diff are + * dropped after libevdev updates its internal state and event processing + * continues as normal. Note that the current slot and the state of touch + * points may have updated during the SYN_DROPPED event, it is strongly + * recommended that a caller ignoring all sync events calls + * libevdev_get_current_slot() and checks the ABS_MT_TRACKING_ID values for + * all slots. + * + * If a device has changed state without events being enqueued in libevdev, + * e.g. after changing the file descriptor, use the @ref + * LIBEVDEV_READ_FLAG_FORCE_SYNC flag. This triggers an internal sync of the + * device and libevdev_next_event() returns @ref LIBEVDEV_READ_STATUS_SYNC. + * Any state changes are available as events as described above. If + * @ref LIBEVDEV_READ_FLAG_FORCE_SYNC is set, the value of ev is undefined. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param flags Set of flags to determine behaviour. If @ref LIBEVDEV_READ_FLAG_NORMAL + * is set, the next event is read in normal mode. If @ref LIBEVDEV_READ_FLAG_SYNC is + * set, the next event is read in sync mode. + * @param ev On success, set to the current event. + * @return On failure, a negative errno is returned. + * @retval LIBEVDEV_READ_STATUS_SUCCESS One or more events were read of the + * device and ev points to the next event in the queue + * @retval -EAGAIN No events are currently available on the device + * @retval LIBEVDEV_READ_STATUS_SYNC A SYN_DROPPED event was received, or a + * synced event was returned and ev points to the SYN_DROPPED event + * + * @note This function is signal-safe. + */ +int libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event *ev); + +/** + * @ingroup events + * + * Check if there are events waiting for us. This function does not read an + * event off the fd and may not access the fd at all. If there are events + * queued internally this function will return non-zero. If the internal + * queue is empty, this function will poll the file descriptor for data. + * + * This is a convenience function for simple processes, most complex programs + * are expected to use select(2) or poll(2) on the file descriptor. The kernel + * guarantees that if data is available, it is a multiple of sizeof(struct + * input_event), and thus calling libevdev_next_event() when select(2) or + * poll(2) return is safe. You do not need libevdev_has_event_pending() if + * you're using select(2) or poll(2). + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @return On failure, a negative errno is returned. + * @retval 0 No event is currently available + * @retval 1 One or more events are available on the fd + * + * @note This function is signal-safe. + */ +int libevdev_has_event_pending(struct libevdev *dev); + +/** + * @ingroup bits + * + * Retrieve the device's name, either as set by the caller or as read from + * the kernel. The string returned is valid until libevdev_free() or until + * libevdev_set_name(), whichever comes earlier. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * + * @return The device name as read off the kernel device. The name is never + * NULL but it may be the empty string. + * + * @note This function is signal-safe. + */ +const char* libevdev_get_name(const struct libevdev *dev); + +/** + * @ingroup kernel + * + * Change the device's name as returned by libevdev_get_name(). This + * function destroys the string previously returned by libevdev_get_name(), + * a caller must take care that no references are kept. + * + * @param dev The evdev device + * @param name The new, non-NULL, name to assign to this device. + * + * @note This function may be called before libevdev_set_fd(). A call to + * libevdev_set_fd() will overwrite any previously set value. + */ +void libevdev_set_name(struct libevdev *dev, const char *name); + +/** + * @ingroup bits + * + * Retrieve the device's physical location, either as set by the caller or + * as read from the kernel. The string returned is valid until + * libevdev_free() or until libevdev_set_phys(), whichever comes earlier. + * + * Virtual devices such as uinput devices have no phys location. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * + * @return The physical location of this device, or NULL if there is none + * + * @note This function is signal safe. + */ +const char * libevdev_get_phys(const struct libevdev *dev); + +/** + * @ingroup kernel + * + * Change the device's physical location as returned by libevdev_get_phys(). + * This function destroys the string previously returned by + * libevdev_get_phys(), a caller must take care that no references are kept. + * + * @param dev The evdev device + * @param phys The new phys to assign to this device. + * + * @note This function may be called before libevdev_set_fd(). A call to + * libevdev_set_fd() will overwrite any previously set value. + */ +void libevdev_set_phys(struct libevdev *dev, const char *phys); + +/** + * @ingroup bits + * + * Retrieve the device's unique identifier, either as set by the caller or + * as read from the kernel. The string returned is valid until + * libevdev_free() or until libevdev_set_uniq(), whichever comes earlier. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * + * @return The unique identifier for this device, or NULL if there is none + * + * @note This function is signal safe. + */ +const char * libevdev_get_uniq(const struct libevdev *dev); + +/** + * @ingroup kernel + * + * Change the device's unique identifier as returned by libevdev_get_uniq(). + * This function destroys the string previously returned by + * libevdev_get_uniq(), a caller must take care that no references are kept. + * + * @param dev The evdev device + * @param uniq The new uniq to assign to this device. + * + * @note This function may be called before libevdev_set_fd(). A call to + * libevdev_set_fd() will overwrite any previously set value. + */ +void libevdev_set_uniq(struct libevdev *dev, const char *uniq); + +/** + * @ingroup bits + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * + * @return The device's product ID + * + * @note This function is signal-safe. + */ +int libevdev_get_id_product(const struct libevdev *dev); + +/** + * @ingroup kernel + * + * @param dev The evdev device + * @param product_id The product ID to assign to this device + * + * @note This function may be called before libevdev_set_fd(). A call to + * libevdev_set_fd() will overwrite any previously set value. + */ +void libevdev_set_id_product(struct libevdev *dev, int product_id); + +/** + * @ingroup bits + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * + * @return The device's vendor ID + * + * @note This function is signal-safe. + */ +int libevdev_get_id_vendor(const struct libevdev *dev); + +/** + * @ingroup kernel + * + * @param dev The evdev device + * @param vendor_id The vendor ID to assign to this device + * + * @note This function may be called before libevdev_set_fd(). A call to + * libevdev_set_fd() will overwrite any previously set value. + */ +void libevdev_set_id_vendor(struct libevdev *dev, int vendor_id); + +/** + * @ingroup bits + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * + * @return The device's bus type + * + * @note This function is signal-safe. + */ +int libevdev_get_id_bustype(const struct libevdev *dev); + +/** + * @ingroup kernel + * + * @param dev The evdev device + * @param bustype The bustype to assign to this device + * + * @note This function may be called before libevdev_set_fd(). A call to + * libevdev_set_fd() will overwrite any previously set value. + */ +void libevdev_set_id_bustype(struct libevdev *dev, int bustype); + +/** + * @ingroup bits + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * + * @return The device's firmware version + * + * @note This function is signal-safe. + */ +int libevdev_get_id_version(const struct libevdev *dev); + +/** + * @ingroup kernel + * + * @param dev The evdev device + * @param version The version to assign to this device + * + * @note This function may be called before libevdev_set_fd(). A call to + * libevdev_set_fd() will overwrite any previously set value. + */ +void libevdev_set_id_version(struct libevdev *dev, int version); + +/** + * @ingroup bits + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * + * @return The driver version for this device + * + * @note This function is signal-safe. + */ +int libevdev_get_driver_version(const struct libevdev *dev); + +/** + * @ingroup bits + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param prop The input property to query for, one of INPUT_PROP_... + * + * @return 1 if the device provides this input property, or 0 otherwise. + * + * @note This function is signal-safe + */ +int libevdev_has_property(const struct libevdev *dev, unsigned int prop); + +/** + * @ingroup kernel + * + * @param dev The evdev device + * @param prop The input property to enable, one of INPUT_PROP_... + * + * @return 0 on success or -1 on failure + * + * @note This function may be called before libevdev_set_fd(). A call to + * libevdev_set_fd() will overwrite any previously set value. + */ +int libevdev_enable_property(struct libevdev *dev, unsigned int prop); + +/** + * @ingroup bits + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param type The event type to query for, one of EV_SYN, EV_REL, etc. + * + * @return 1 if the device supports this event type, or 0 otherwise. + * + * @note This function is signal-safe. + */ +int libevdev_has_event_type(const struct libevdev *dev, unsigned int type); + +/** + * @ingroup bits + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param type The event type for the code to query (EV_SYN, EV_REL, etc.) + * @param code The event code to query for, one of ABS_X, REL_X, etc. + * + * @return 1 if the device supports this event type and code, or 0 otherwise. + * + * @note This function is signal-safe. + */ +int libevdev_has_event_code(const struct libevdev *dev, unsigned int type, unsigned int code); + +/** + * @ingroup bits + * + * Get the minimum axis value for the given axis, as advertised by the kernel. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code The EV_ABS event code to query for, one of ABS_X, ABS_Y, etc. + * + * @return axis minimum for the given axis or 0 if the axis is invalid + * + * @note This function is signal-safe. + */ +int libevdev_get_abs_minimum(const struct libevdev *dev, unsigned int code); +/** + * @ingroup bits + * + * Get the maximum axis value for the given axis, as advertised by the kernel. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code The EV_ABS event code to query for, one of ABS_X, ABS_Y, etc. + * + * @return axis maximum for the given axis or 0 if the axis is invalid + * + * @note This function is signal-safe. + */ +int libevdev_get_abs_maximum(const struct libevdev *dev, unsigned int code); +/** + * @ingroup bits + * + * Get the axis fuzz for the given axis, as advertised by the kernel. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code The EV_ABS event code to query for, one of ABS_X, ABS_Y, etc. + * + * @return axis fuzz for the given axis or 0 if the axis is invalid + * + * @note This function is signal-safe. + */ +int libevdev_get_abs_fuzz(const struct libevdev *dev, unsigned int code); +/** + * @ingroup bits + * + * Get the axis flat for the given axis, as advertised by the kernel. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code The EV_ABS event code to query for, one of ABS_X, ABS_Y, etc. + * + * @return axis flat for the given axis or 0 if the axis is invalid + * + * @note This function is signal-safe. + */ +int libevdev_get_abs_flat(const struct libevdev *dev, unsigned int code); +/** + * @ingroup bits + * + * Get the axis resolution for the given axis, as advertised by the kernel. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code The EV_ABS event code to query for, one of ABS_X, ABS_Y, etc. + * + * @return axis resolution for the given axis or 0 if the axis is invalid + * + * @note This function is signal-safe. + */ +int libevdev_get_abs_resolution(const struct libevdev *dev, unsigned int code); + +/** + * @ingroup bits + * + * Get the axis info for the given axis, as advertised by the kernel. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code The EV_ABS event code to query for, one of ABS_X, ABS_Y, etc. + * + * @return The input_absinfo for the given code, or NULL if the device does + * not support this event code. + * + * @note This function is signal-safe. + */ +const struct input_absinfo* libevdev_get_abs_info(const struct libevdev *dev, unsigned int code); + +/** + * @ingroup bits + * + * Behaviour of this function is undefined if the device does not provide + * the event. + * + * If the device supports ABS_MT_SLOT, the value returned for any ABS_MT_* + * event code is the value of the currently active slot. You should use + * libevdev_get_slot_value() instead. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param type The event type for the code to query (EV_SYN, EV_REL, etc.) + * @param code The event code to query for, one of ABS_X, REL_X, etc. + * + * @return The current value of the event. + * + * @note This function is signal-safe. + * @note The value for ABS_MT_ events is undefined, use + * libevdev_get_slot_value() instead + * + * @see libevdev_get_slot_value + */ +int libevdev_get_event_value(const struct libevdev *dev, unsigned int type, unsigned int code); + +/** + * @ingroup kernel + * + * Set the value for a given event type and code. This only makes sense for + * some event types, e.g. setting the value for EV_REL is pointless. + * + * This is a local modification only affecting only this representation of + * this device. A future call to libevdev_get_event_value() will return this + * value, unless the value was overwritten by an event. + * + * If the device supports ABS_MT_SLOT, the value set for any ABS_MT_* + * event code is the value of the currently active slot. You should use + * libevdev_set_slot_value() instead. + * + * If the device supports ABS_MT_SLOT and the type is EV_ABS and the code is + * ABS_MT_SLOT, the value must be a positive number less then the number of + * slots on the device. Otherwise, libevdev_set_event_value() returns -1. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param type The event type for the code to query (EV_SYN, EV_REL, etc.) + * @param code The event code to set the value for, one of ABS_X, LED_NUML, etc. + * @param value The new value to set + * + * @return 0 on success, or -1 on failure. + * @retval -1 the device does not have the event type or code enabled, or the code is outside the + * allowed limits for the given type, or the type cannot be set, or the + * value is not permitted for the given code. + * + * @see libevdev_set_slot_value + * @see libevdev_get_event_value + */ +int libevdev_set_event_value(struct libevdev *dev, unsigned int type, unsigned int code, int value); + +/** + * @ingroup bits + * + * Fetch the current value of the event type. This is a shortcut for + * + @code + if (libevdev_has_event_type(dev, t) && libevdev_has_event_code(dev, t, c)) + val = libevdev_get_event_value(dev, t, c); + @endcode + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param type The event type for the code to query (EV_SYN, EV_REL, etc.) + * @param code The event code to query for, one of ABS_X, REL_X, etc. + * @param[out] value The current value of this axis returned. + * + * @return If the device supports this event type and code, the return value is + * non-zero and value is set to the current value of this axis. Otherwise, + * 0 is returned and value is unmodified. + * + * @note This function is signal-safe. + * @note The value for ABS_MT_ events is undefined, use + * libevdev_fetch_slot_value() instead + * + * @see libevdev_fetch_slot_value + */ +int libevdev_fetch_event_value(const struct libevdev *dev, unsigned int type, unsigned int code, int *value); + +/** + * @ingroup mt + * + * Return the current value of the code for the given slot. + * + * The return value is undefined for a slot exceeding the available slots on + * the device, for a code that is not in the permitted ABS_MT range or for a + * device that does not have slots. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param slot The numerical slot number, must be smaller than the total number + * of slots on this device + * @param code The event code to query for, one of ABS_MT_POSITION_X, etc. + * + * @note This function is signal-safe. + * @note The value for events other than ABS_MT_ is undefined, use + * libevdev_fetch_value() instead + * + * @see libevdev_get_event_value + */ +int libevdev_get_slot_value(const struct libevdev *dev, unsigned int slot, unsigned int code); + +/** + * @ingroup kernel + * + * Set the value for a given code for the given slot. + * + * This is a local modification only affecting only this representation of + * this device. A future call to libevdev_get_slot_value() will return this + * value, unless the value was overwritten by an event. + * + * This function does not set event values for axes outside the ABS_MT range, + * use libevdev_set_event_value() instead. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param slot The numerical slot number, must be smaller than the total number + * of slots on this device + * @param code The event code to set the value for, one of ABS_MT_POSITION_X, etc. + * @param value The new value to set + * + * @return 0 on success, or -1 on failure. + * @retval -1 the device does not have the event code enabled, or the code is + * outside the allowed limits for multitouch events, or the slot number is outside + * the limits for this device, or the device does not support multitouch events. + * + * @see libevdev_set_event_value + * @see libevdev_get_slot_value + */ +int libevdev_set_slot_value(struct libevdev *dev, unsigned int slot, unsigned int code, int value); + +/** + * @ingroup mt + * + * Fetch the current value of the code for the given slot. This is a shortcut for + * + @code + if (libevdev_has_event_type(dev, EV_ABS) && + libevdev_has_event_code(dev, EV_ABS, c) && + slot < device->number_of_slots) + val = libevdev_get_slot_value(dev, slot, c); + @endcode + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param slot The numerical slot number, must be smaller than the total number + * of slots on this * device + * @param[out] value The current value of this axis returned. + * + * @param code The event code to query for, one of ABS_MT_POSITION_X, etc. + * @return If the device supports this event code, the return value is + * non-zero and value is set to the current value of this axis. Otherwise, or + * if the event code is not an ABS_MT_* event code, 0 is returned and value + * is unmodified. + * + * @note This function is signal-safe. + */ +int libevdev_fetch_slot_value(const struct libevdev *dev, unsigned int slot, unsigned int code, int *value); + +/** + * @ingroup mt + * + * Get the number of slots supported by this device. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * + * @return The number of slots supported, or -1 if the device does not provide + * any slots + * + * @note A device may provide ABS_MT_SLOT but a total number of 0 slots. Hence + * the return value of -1 for "device does not provide slots at all" + */ +int libevdev_get_num_slots(const struct libevdev *dev); + +/** + * @ingroup mt + * + * Get the currently active slot. This may differ from the value + * an ioctl may return at this time as events may have been read off the fd + * since changing the slot value but those events are still in the buffer + * waiting to be processed. The returned value is the value a caller would + * see if it were to process events manually one-by-one. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * + * @return the currently active slot (logically) + * + * @note This function is signal-safe. + */ +int libevdev_get_current_slot(const struct libevdev *dev); + +/** + * @ingroup kernel + * + * Change the minimum for the given EV_ABS event code, if the code exists. + * This function has no effect if libevdev_has_event_code() returns false for + * this code. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code One of ABS_X, ABS_Y, ... + * @param min The new minimum for this axis + */ +void libevdev_set_abs_minimum(struct libevdev *dev, unsigned int code, int min); + +/** + * @ingroup kernel + * + * Change the maximum for the given EV_ABS event code, if the code exists. + * This function has no effect if libevdev_has_event_code() returns false for + * this code. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code One of ABS_X, ABS_Y, ... + * @param max The new maxium for this axis + */ +void libevdev_set_abs_maximum(struct libevdev *dev, unsigned int code, int max); + +/** + * @ingroup kernel + * + * Change the fuzz for the given EV_ABS event code, if the code exists. + * This function has no effect if libevdev_has_event_code() returns false for + * this code. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code One of ABS_X, ABS_Y, ... + * @param fuzz The new fuzz for this axis + */ +void libevdev_set_abs_fuzz(struct libevdev *dev, unsigned int code, int fuzz); + +/** + * @ingroup kernel + * + * Change the flat for the given EV_ABS event code, if the code exists. + * This function has no effect if libevdev_has_event_code() returns false for + * this code. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code One of ABS_X, ABS_Y, ... + * @param flat The new flat for this axis + */ +void libevdev_set_abs_flat(struct libevdev *dev, unsigned int code, int flat); + +/** + * @ingroup kernel + * + * Change the resolution for the given EV_ABS event code, if the code exists. + * This function has no effect if libevdev_has_event_code() returns false for + * this code. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code One of ABS_X, ABS_Y, ... + * @param resolution The new axis resolution + */ +void libevdev_set_abs_resolution(struct libevdev *dev, unsigned int code, int resolution); + +/** + * @ingroup kernel + * + * Change the abs info for the given EV_ABS event code, if the code exists. + * This function has no effect if libevdev_has_event_code() returns false for + * this code. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code One of ABS_X, ABS_Y, ... + * @param abs The new absolute axis data (min, max, fuzz, flat, resolution) + */ +void libevdev_set_abs_info(struct libevdev *dev, unsigned int code, const struct input_absinfo *abs); + +/** + * @ingroup kernel + * + * Forcibly enable an event type on this device, even if the underlying + * device does not support it. While this cannot make the device actually + * report such events, it will now return true for libevdev_has_event_type(). + * + * This is a local modification only affecting only this representation of + * this device. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param type The event type to enable (EV_ABS, EV_KEY, ...) + * + * @return 0 on success or -1 otherwise + * + * @see libevdev_has_event_type + */ +int libevdev_enable_event_type(struct libevdev *dev, unsigned int type); + +/** + * @ingroup kernel + * + * Forcibly disable an event type on this device, even if the underlying + * device provides it. This effectively mutes the respective set of + * events. libevdev will filter any events matching this type and none will + * reach the caller. libevdev_has_event_type() will return false for this + * type. + * + * In most cases, a caller likely only wants to disable a single code, not + * the whole type. Use libevdev_disable_event_code() for that. + * + * Disabling EV_SYN will not work. Don't shoot yourself in the foot. + * It hurts. + * + * This is a local modification only affecting only this representation of + * this device. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param type The event type to disable (EV_ABS, EV_KEY, ...) + * + * @return 0 on success or -1 otherwise + * + * @see libevdev_has_event_type + * @see libevdev_disable_event_type + */ +int libevdev_disable_event_type(struct libevdev *dev, unsigned int type); + +/** + * @ingroup kernel + * + * Forcibly enable an event type on this device, even if the underlying + * device does not support it. While this cannot make the device actually + * report such events, it will now return true for libevdev_has_event_code(). + * + * The last argument depends on the type and code: + * - If type is EV_ABS, data must be a pointer to a struct input_absinfo + * containing the data for this axis. + * - If type is EV_REP, daat must be a pointer to a int containing the data + * for this axis + * - For all other types, the argument must be NULL. + * + * This function calls libevdev_enable_event_type() if necessary. + * + * This is a local modification only affecting only this representation of + * this device. + * + * If this function is called with a type of EV_ABS and EV_REP on a device + * that already has the given event code enabled, the values in data + * overwrite the previous values. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param type The event type to enable (EV_ABS, EV_KEY, ...) + * @param code The event code to enable (ABS_X, REL_X, etc.) + * @param data If type is EV_ABS, data points to a struct input_absinfo. If type is EV_REP, data + * points to an integer. Otherwise, data must be NULL. + * + * @return 0 on success or -1 otherwise + * + * @see libevdev_enable_event_type + */ +int libevdev_enable_event_code(struct libevdev *dev, unsigned int type, unsigned int code, const void *data); + +/** + * @ingroup kernel + * + * Forcibly disable an event code on this device, even if the underlying + * device provides it. This effectively mutes the respective set of + * events. libevdev will filter any events matching this type and code and + * none will reach the caller. libevdev_has_event_code() will return false for + * this code. + * + * Disabling all event codes for a given type will not disable the event + * type. Use libevdev_disable_event_type() for that. + * + * This is a local modification only affecting only this representation of + * this device. + * + * Disabling EV_SYN will not work. Don't shoot yourself in the foot. + * It hurts. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param type The event type to disable (EV_ABS, EV_KEY, ...) + * @param code The event code to disable (ABS_X, REL_X, etc.) + * + * @return 0 on success or -1 otherwise + * + * @see libevdev_has_event_code + * @see libevdev_disable_event_type + */ +int libevdev_disable_event_code(struct libevdev *dev, unsigned int type, unsigned int code); + +/** + * @ingroup kernel + * + * Set the device's EV_ABS axis to the value defined in the abs + * parameter. This will be written to the kernel. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code The EV_ABS event code to modify, one of ABS_X, ABS_Y, etc. + * @param abs Axis info to set the kernel axis to + * + * @return 0 on success, or a negative errno on failure + * + * @see libevdev_enable_event_code + */ +int libevdev_kernel_set_abs_info(struct libevdev *dev, unsigned int code, const struct input_absinfo *abs); + + +/** + * @ingroup kernel + */ +enum libevdev_led_value { + LIBEVDEV_LED_ON = 3, /**< Turn the LED on */ + LIBEVDEV_LED_OFF = 4 /**< Turn the LED off */ +}; + +/** + * @ingroup kernel + * + * Turn an LED on or off. Convenience function, if you need to modify multiple + * LEDs simultaneously, use libevdev_kernel_set_led_values() instead. + * + * @note enabling an LED requires write permissions on the device's file descriptor. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code The EV_LED event code to modify, one of LED_NUML, LED_CAPSL, ... + * @param value Specifies whether to turn the LED on or off + * @return 0 on success, or a negative errno on failure + */ +int libevdev_kernel_set_led_value(struct libevdev *dev, unsigned int code, enum libevdev_led_value value); + +/** + * @ingroup kernel + * + * Turn multiple LEDs on or off simultaneously. This function expects a pair + * of LED codes and values to set them to, terminated by a -1. For example, to + * switch the NumLock LED on but the CapsLock LED off, use: + * + @code + libevdev_kernel_set_led_values(dev, LED_NUML, LIBEVDEV_LED_ON, + LED_CAPSL, LIBEVDEV_LED_OFF, + -1); + @endcode + * + * If any LED code or value is invalid, this function returns -EINVAL and no + * LEDs are modified. + * + * @note enabling an LED requires write permissions on the device's file descriptor. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param ... A pair of LED_* event codes and libevdev_led_value_t, followed by + * -1 to terminate the list. + * @return 0 on success, or a negative errno on failure + */ +int libevdev_kernel_set_led_values(struct libevdev *dev, ...); + +/** + * @ingroup kernel + * + * Set the clock ID to be used for timestamps. Further events from this device + * will report an event time based on the given clock. + * + * This is a modification only affecting this representation of + * this device. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param clockid The clock to use for future events. Permitted values + * are CLOCK_MONOTONIC and CLOCK_REALTIME (the default). + * @return 0 on success, or a negative errno on failure + */ +int libevdev_set_clock_id(struct libevdev *dev, int clockid); + +/** + * @ingroup misc + * + * Helper function to check if an event is of a specific type. This is + * virtually the same as: + * + * ev->type == type + * + * with the exception that some sanity checks are performed to ensure type + * is valid. + * + * @note The ranges for types are compiled into libevdev. If the kernel + * changes the max value, libevdev will not automatically pick these up. + * + * @param ev The input event to check + * @param type Input event type to compare the event against (EV_REL, EV_ABS, + * etc.) + * + * @return 1 if the event type matches the given type, 0 otherwise (or if + * type is invalid) + */ +int libevdev_event_is_type(const struct input_event *ev, unsigned int type); + +/** + * @ingroup misc + * + * Helper function to check if an event is of a specific type and code. This + * is virtually the same as: + * + * ev->type == type && ev->code == code + * + * with the exception that some sanity checks are performed to ensure type and + * code are valid. + * + * @note The ranges for types and codes are compiled into libevdev. If the kernel + * changes the max value, libevdev will not automatically pick these up. + * + * @param ev The input event to check + * @param type Input event type to compare the event against (EV_REL, EV_ABS, + * etc.) + * @param code Input event code to compare the event against (ABS_X, REL_X, + * etc.) + * + * @return 1 if the event type matches the given type and code, 0 otherwise + * (or if type/code are invalid) + */ +int libevdev_event_is_code(const struct input_event *ev, unsigned int type, unsigned int code); + +/** + * @ingroup misc + * + * @param type The event type to return the name for. + * + * @return The name of the given event type (e.g. EV_ABS) or NULL for an + * invalid type + * + * @note The list of names is compiled into libevdev. If the kernel adds new + * defines for new event types, libevdev will not automatically pick these up. + */ +const char * libevdev_event_type_get_name(unsigned int type); +/** + * @ingroup misc + * + * @param type The event type for the code to query (EV_SYN, EV_REL, etc.) + * @param code The event code to return the name for (e.g. ABS_X) + * + * @return The name of the given event code (e.g. ABS_X) or NULL for an + * invalid type or code + * + * @note The list of names is compiled into libevdev. If the kernel adds new + * defines for new event codes, libevdev will not automatically pick these up. + */ +const char * libevdev_event_code_get_name(unsigned int type, unsigned int code); + +/** + * @ingroup misc + * + * @param prop The input prop to return the name for (e.g. INPUT_PROP_BUTTONPAD) + * + * @return The name of the given input prop (e.g. INPUT_PROP_BUTTONPAD) or NULL for an + * invalid property + * + * @note The list of names is compiled into libevdev. If the kernel adds new + * defines for new properties libevdev will not automatically pick these up. + * @note On older kernels input properties may not be defined and + * libevdev_property_get_name() will always return NULL + */ +const char* libevdev_property_get_name(unsigned int prop); + +/** + * @ingroup misc + * + * @param type The event type to return the maximum for (EV_ABS, EV_REL, etc.). No max is defined for + * EV_SYN. + * + * @return The max value defined for the given event type, e.g. ABS_MAX for a type of EV_ABS, or -1 + * for an invalid type. + * + * @note The max value is compiled into libevdev. If the kernel changes the + * max value, libevdev will not automatically pick these up. + */ +int libevdev_event_type_get_max(unsigned int type); + +/** + * @ingroup misc + * + * Look up an event-type by its name. Event-types start with "EV_" followed by + * the name (eg., "EV_ABS"). The "EV_" prefix must be included in the name. It + * returns the constant assigned to the event-type or -1 if not found. + * + * @param name A non-NULL string describing an input-event type ("EV_KEY", + * "EV_ABS", ...), zero-terminated. + * + * @return The given type constant for the passed name or -1 if not found. + * + * @note EV_MAX is also recognized. + */ +int libevdev_event_type_from_name(const char *name); + +/** + * @ingroup misc + * + * Look up an event-type by its name. Event-types start with "EV_" followed by + * the name (eg., "EV_ABS"). The "EV_" prefix must be included in the name. It + * returns the constant assigned to the event-type or -1 if not found. + * + * @param name A non-NULL string describing an input-event type ("EV_KEY", + * "EV_ABS", ...). + * @param len The length of the passed string excluding any terminating 0 + * character. + * + * @return The given type constant for the passed name or -1 if not found. + * + * @note EV_MAX is also recognized. + */ +int libevdev_event_type_from_name_n(const char *name, size_t len); + +/** + * @ingroup misc + * + * Look up an event code by its type and name. Event codes start with a fixed + * prefix followed by their name (eg., "ABS_X"). The prefix must be included in + * the name. It returns the constant assigned to the event code or -1 if not + * found. + * + * You have to pass the event type where to look for the name. For instance, to + * resolve "ABS_X" you need to pass EV_ABS as type and "ABS_X" as string. + * Supported event codes are codes starting with SYN_, KEY_, BTN_, REL_, ABS_, + * MSC_, SND_, SW_, LED_, REP_, FF_. + * + * @param type The event type (EV_* constant) where to look for the name. + * @param name A non-NULL string describing an input-event code ("KEY_A", + * "ABS_X", "BTN_Y", ...), zero-terminated. + * + * @return The given code constant for the passed name or -1 if not found. + */ +int libevdev_event_code_from_name(unsigned int type, const char *name); + +/** + * @ingroup misc + * + * Look up an event code by its type and name. Event codes start with a fixed + * prefix followed by their name (eg., "ABS_X"). The prefix must be included in + * the name. It returns the constant assigned to the event code or -1 if not + * found. + * + * You have to pass the event type where to look for the name. For instance, to + * resolve "ABS_X" you need to pass EV_ABS as type and "ABS_X" as string. + * Supported event codes are codes starting with SYN_, KEY_, BTN_, REL_, ABS_, + * MSC_, SND_, SW_, LED_, REP_, FF_. + * + * @param type The event type (EV_* constant) where to look for the name. + * @param name A non-NULL string describing an input-event code ("KEY_A", + * "ABS_X", "BTN_Y", ...). + * @param len The length of the string in @p name excluding any terminating 0 + * character. + * + * @return The given code constant for the name or -1 if not found. + */ +int libevdev_event_code_from_name_n(unsigned int type, const char *name, + size_t len); + +/** + * @ingroup bits + * + * Get the repeat delay and repeat period values for this device. This + * function is a convenience function only, EV_REP is supported by + * libevdev_get_event_value(). + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param delay If not null, set to the repeat delay value + * @param period If not null, set to the repeat period value + * + * @return 0 on success, -1 if this device does not have repeat settings. + * + * @note This function is signal-safe + * + * @see libevdev_get_event_value + */ +int libevdev_get_repeat(const struct libevdev *dev, int *delay, int *period); + + +/********* DEPRECATED SECTION *********/ +#if defined(__GNUC__) && __GNUC__ >= 4 +#define LIBEVDEV_DEPRECATED __attribute__ ((deprecated)) +#else +#define LIBEVDEV_DEPRECATED +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LIBEVDEV_H */ diff --git a/libevdev/libevdev.sym b/libevdev/libevdev.sym new file mode 100644 index 0000000..ef4f06b --- /dev/null +++ b/libevdev/libevdev.sym @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2013 David Herrmann + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +LIBEVDEV_1 { +global: + libevdev_change_fd; + libevdev_disable_event_code; + libevdev_disable_event_type; + libevdev_enable_event_code; + libevdev_enable_event_type; + libevdev_enable_property; + libevdev_event_code_from_name; + libevdev_event_code_from_name_n; + libevdev_event_code_get_name; + libevdev_event_is_code; + libevdev_event_is_type; + libevdev_event_type_from_name; + libevdev_event_type_from_name_n; + libevdev_event_type_get_max; + libevdev_event_type_get_name; + libevdev_fetch_event_value; + libevdev_fetch_slot_value; + libevdev_free; + libevdev_get_abs_flat; + libevdev_get_abs_fuzz; + libevdev_get_abs_info; + libevdev_get_abs_maximum; + libevdev_get_abs_minimum; + libevdev_get_abs_resolution; + libevdev_get_current_slot; + libevdev_get_driver_version; + libevdev_get_event_value; + libevdev_get_fd; + libevdev_get_id_bustype; + libevdev_get_id_product; + libevdev_get_id_vendor; + libevdev_get_id_version; + libevdev_get_log_priority; + libevdev_get_name; + libevdev_get_num_slots; + libevdev_get_phys; + libevdev_get_repeat; + libevdev_get_slot_value; + libevdev_get_uniq; + libevdev_grab; + libevdev_has_event_code; + libevdev_has_event_pending; + libevdev_has_event_type; + libevdev_has_property; + libevdev_kernel_set_abs_info; + libevdev_kernel_set_led_value; + libevdev_kernel_set_led_values; + libevdev_new; + libevdev_new_from_fd; + libevdev_next_event; + libevdev_property_get_name; + libevdev_set_abs_flat; + libevdev_set_abs_fuzz; + libevdev_set_abs_info; + libevdev_set_abs_maximum; + libevdev_set_abs_minimum; + libevdev_set_abs_resolution; + libevdev_set_clock_id; + libevdev_set_event_value; + libevdev_set_fd; + libevdev_set_id_bustype; + libevdev_set_id_product; + libevdev_set_id_vendor; + libevdev_set_id_version; + libevdev_set_log_function; + libevdev_set_log_priority; + libevdev_set_name; + libevdev_set_phys; + libevdev_set_slot_value; + libevdev_set_uniq; + libevdev_uinput_create_from_device; + libevdev_uinput_destroy; + libevdev_uinput_get_devnode; + libevdev_uinput_get_fd; + libevdev_uinput_get_syspath; + libevdev_uinput_write_event; + +local: + *; +}; diff --git a/libevdev/make-event-names.py b/libevdev/make-event-names.py new file mode 100755 index 0000000..149f093 --- /dev/null +++ b/libevdev/make-event-names.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python +# Parses linux/input.h scanning for #define KEY_FOO 134 +# Prints C header files or Python files that can be used as +# mapping and lookup tables. +# + +from __future__ import print_function +import re +import sys + +class Bits(object): + pass + +prefixes = [ + "EV_", + "REL_", + "ABS_", + "KEY_", + "BTN_", + "LED_", + "SND_", + "MSC_", + "SW_", + "FF_", + "SYN_", + "REP_", + "INPUT_PROP_", +] + +blacklist = [ + "EV_VERSION", + "BTN_MISC", + "BTN_MOUSE", + "BTN_JOYSTICK", + "BTN_GAMEPAD", + "BTN_DIGI", + "BTN_WHEEL", + "BTN_TRIGGER_HAPPY" +] + +btn_additional = [ + [0, "BTN_A"], + [0, "BTN_B"], + [0, "BTN_X"], + [0, "BTN_Y"], +] + +names = [ + "REL_", + "ABS_", + "KEY_", + "BTN_", + "LED_", + "SND_", + "MSC_", + "SW_", + "FF_", + "SYN_", + "REP_", +] + +def print_bits(bits, prefix): + if not hasattr(bits, prefix): + return + print("static const char * const %s_map[%s_MAX + 1] = {" % (prefix, prefix.upper())) + for val, name in list(getattr(bits, prefix).items()): + print(" [%s] = \"%s\"," % (name, name)) + if prefix == "key": + for val, name in list(getattr(bits, "btn").items()): + print(" [%s] = \"%s\"," % (name, name)) + print("};") + print("") + +def print_map(bits): + print("static const char * const * const event_type_map[EV_MAX + 1] = {") + + for prefix in prefixes: + if prefix == "BTN_" or prefix == "EV_" or prefix == "INPUT_PROP_": + continue + print(" [EV_%s] = %s_map," % (prefix[:-1], prefix[:-1].lower())) + + print("};") + print("") + + print("#pragma GCC diagnostic push") + print("#pragma GCC diagnostic ignored \"-Woverride-init\"") + print("static const int ev_max[EV_MAX + 1] = {") + print(" [0 ... EV_MAX] = -1,") + for prefix in prefixes: + if prefix == "BTN_" or prefix == "EV_" or prefix == "INPUT_PROP_": + continue + print(" [EV_%s] = %s_MAX," % (prefix[:-1], prefix[:-1])) + print("};") + print("#pragma GCC diagnostic pop /* \"-Woverride-init\" */") + print("") + +def print_python_map(bits): + print("map = {") + + for val, name in list(getattr(bits, "ev").items()): + name = name[3:] + if name == "REP" or name == "PWR" or name == "FF_STATUS" or name == "MAX": + continue + print(" %d : %s_map," % (val, name.lower())) + + print("}") + print("") + +def print_lookup(bits, prefix): + if not hasattr(bits, prefix): + return + + names = list(getattr(bits, prefix).items()) + if prefix == "btn": + names = names + btn_additional; + + for val, name in sorted(names, key=lambda e: e[1]): + print(" { .name = \"%s\", .value = %s }," % (name, name)) + +def print_lookup_table(bits): + print("struct name_entry {") + print(" const char *name;") + print(" unsigned int value;") + print("};") + print("") + print("static const struct name_entry ev_names[] = {") + print_lookup(bits, "ev") + print("};") + print("") + + print("static const struct name_entry code_names[] = {") + for prefix in sorted(names, key=lambda e: e): + print_lookup(bits, prefix[:-1].lower()) + print("};") + print("") + + +def print_mapping_table(bits): + print("/* THIS FILE IS GENERATED, DO NOT EDIT */") + print("") + print("#ifndef EVENT_NAMES_H") + print("#define EVENT_NAMES_H") + print("") + + for prefix in prefixes: + if prefix == "BTN_": + continue + print_bits(bits, prefix[:-1].lower()) + + print_map(bits) + print_lookup_table(bits) + + print("#endif /* EVENT_NAMES_H */") + +def parse_define(bits, line): + m = re.match(r"^#define\s+(\w+)\s+(\w+)", line) + if m == None: + return + + name = m.group(1) + + if name in blacklist: + return + + try: + value = int(m.group(2), 0) + except ValueError: + return + + for prefix in prefixes: + if not name.startswith(prefix): + continue + + attrname = prefix[:-1].lower() + + if not hasattr(bits, attrname): + setattr(bits, attrname, {}) + b = getattr(bits, attrname) + b[value] = name + +def parse(fp): + bits = Bits() + + lines = fp.readlines() + for line in lines: + if not line.startswith("#define"): + continue + parse_define(bits, line) + + return bits + +def usage(prog): + print("Usage: %s /path/to/linux/input.h" % prog) + +if __name__ == "__main__": + if len(sys.argv) != 2: + usage(sys.argv[0]) + sys.exit(2) + + with open(sys.argv[1]) as f: + bits = parse(f) + print_mapping_table(bits) diff --git a/m4/.gitignore b/m4/.gitignore new file mode 100644 index 0000000..38066dd --- /dev/null +++ b/m4/.gitignore @@ -0,0 +1,5 @@ +libtool.m4 +ltoptions.m4 +ltsugar.m4 +ltversion.m4 +lt~obsolete.m4 diff --git a/m4/attributes.m4 b/m4/attributes.m4 new file mode 100644 index 0000000..aa53ef2 --- /dev/null +++ b/m4/attributes.m4 @@ -0,0 +1,288 @@ +dnl Macros to check the presence of generic (non-typed) symbols. +dnl Copyright (c) 2006-2008 Diego Pettenò +dnl Copyright (c) 2006-2008 xine project +dnl Copyright (c) 2012 Lucas De Marchi +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2, or (at your option) +dnl any later version. +dnl +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +dnl 02110-1301, USA. +dnl +dnl As a special exception, the copyright owners of the +dnl macro gives unlimited permission to copy, distribute and modify the +dnl configure scripts that are the output of Autoconf when processing the +dnl Macro. You need not follow the terms of the GNU General Public +dnl License when using or distributing such scripts, even though portions +dnl of the text of the Macro appear in them. The GNU General Public +dnl License (GPL) does govern all other use of the material that +dnl constitutes the Autoconf Macro. +dnl +dnl This special exception to the GPL applies to versions of the +dnl Autoconf Macro released by this project. When you make and +dnl distribute a modified version of the Autoconf Macro, you may extend +dnl this special exception to the GPL to apply to your modified version as +dnl well. + +dnl Check if FLAG in ENV-VAR is supported by compiler and append it +dnl to WHERE-TO-APPEND variable +dnl CC_CHECK_FLAG_APPEND([WHERE-TO-APPEND], [ENV-VAR], [FLAG]) + +AC_DEFUN([CC_CHECK_FLAG_APPEND], [ + AC_CACHE_CHECK([if $CC supports flag $3 in envvar $2], + AS_TR_SH([cc_cv_$2_$3]), + [eval "AS_TR_SH([cc_save_$2])='${$2}'" + eval "AS_TR_SH([$2])='-Werror $3'" + AC_LINK_IFELSE([AC_LANG_SOURCE([int a = 0; int main(void) { return a; } ])], + [eval "AS_TR_SH([cc_cv_$2_$3])='yes'"], + [eval "AS_TR_SH([cc_cv_$2_$3])='no'"]) + eval "AS_TR_SH([$2])='$cc_save_$2'"]) + + AS_IF([eval test x$]AS_TR_SH([cc_cv_$2_$3])[ = xyes], + [eval "$1='${$1} $3'"]) +]) + +dnl CC_CHECK_FLAGS_APPEND([WHERE-TO-APPEND], [ENV-VAR], [FLAG1 FLAG2]) +AC_DEFUN([CC_CHECK_FLAGS_APPEND], [ + for flag in $3; do + CC_CHECK_FLAG_APPEND($1, $2, $flag) + done +]) + +dnl Check if the flag is supported by linker (cacheable) +dnl CC_CHECK_LDFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) + +AC_DEFUN([CC_CHECK_LDFLAGS], [ + AC_CACHE_CHECK([if $CC supports $1 flag], + AS_TR_SH([cc_cv_ldflags_$1]), + [ac_save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $1" + AC_LINK_IFELSE([int main() { return 1; }], + [eval "AS_TR_SH([cc_cv_ldflags_$1])='yes'"], + [eval "AS_TR_SH([cc_cv_ldflags_$1])="]) + LDFLAGS="$ac_save_LDFLAGS" + ]) + + AS_IF([eval test x$]AS_TR_SH([cc_cv_ldflags_$1])[ = xyes], + [$2], [$3]) +]) + +dnl define the LDFLAGS_NOUNDEFINED variable with the correct value for +dnl the current linker to avoid undefined references in a shared object. +AC_DEFUN([CC_NOUNDEFINED], [ + dnl We check $host for which systems to enable this for. + AC_REQUIRE([AC_CANONICAL_HOST]) + + case $host in + dnl FreeBSD (et al.) does not complete linking for shared objects when pthreads + dnl are requested, as different implementations are present; to avoid problems + dnl use -Wl,-z,defs only for those platform not behaving this way. + *-freebsd* | *-openbsd*) ;; + *) + dnl First of all check for the --no-undefined variant of GNU ld. This allows + dnl for a much more readable commandline, so that people can understand what + dnl it does without going to look for what the heck -z defs does. + for possible_flags in "-Wl,--no-undefined" "-Wl,-z,defs"; do + CC_CHECK_LDFLAGS([$possible_flags], [LDFLAGS_NOUNDEFINED="$possible_flags"]) + break + done + ;; + esac + + AC_SUBST([LDFLAGS_NOUNDEFINED]) +]) + +dnl Check for a -Werror flag or equivalent. -Werror is the GCC +dnl and ICC flag that tells the compiler to treat all the warnings +dnl as fatal. We usually need this option to make sure that some +dnl constructs (like attributes) are not simply ignored. +dnl +dnl Other compilers don't support -Werror per se, but they support +dnl an equivalent flag: +dnl - Sun Studio compiler supports -errwarn=%all +AC_DEFUN([CC_CHECK_WERROR], [ + AC_CACHE_CHECK( + [for $CC way to treat warnings as errors], + [cc_cv_werror], + [CC_CHECK_CFLAGS_SILENT([-Werror], [cc_cv_werror=-Werror], + [CC_CHECK_CFLAGS_SILENT([-errwarn=%all], [cc_cv_werror=-errwarn=%all])]) + ]) +]) + +AC_DEFUN([CC_CHECK_ATTRIBUTE], [ + AC_REQUIRE([CC_CHECK_WERROR]) + AC_CACHE_CHECK([if $CC supports __attribute__(( ifelse([$2], , [$1], [$2]) ))], + AS_TR_SH([cc_cv_attribute_$1]), + [ac_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $cc_cv_werror" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([$3])], + [eval "AS_TR_SH([cc_cv_attribute_$1])='yes'"], + [eval "AS_TR_SH([cc_cv_attribute_$1])='no'"]) + CFLAGS="$ac_save_CFLAGS" + ]) + + AS_IF([eval test x$]AS_TR_SH([cc_cv_attribute_$1])[ = xyes], + [AC_DEFINE( + AS_TR_CPP([SUPPORT_ATTRIBUTE_$1]), 1, + [Define this if the compiler supports __attribute__(( ifelse([$2], , [$1], [$2]) ))] + ) + $4], + [$5]) +]) + +AC_DEFUN([CC_ATTRIBUTE_CONSTRUCTOR], [ + CC_CHECK_ATTRIBUTE( + [constructor],, + [void __attribute__((constructor)) ctor() { int a; }], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_FORMAT], [ + CC_CHECK_ATTRIBUTE( + [format], [format(printf, n, n)], + [void __attribute__((format(printf, 1, 2))) printflike(const char *fmt, ...) { fmt = (void *)0; }], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_FORMAT_ARG], [ + CC_CHECK_ATTRIBUTE( + [format_arg], [format_arg(printf)], + [char *__attribute__((format_arg(1))) gettextlike(const char *fmt) { fmt = (void *)0; }], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_VISIBILITY], [ + CC_CHECK_ATTRIBUTE( + [visibility_$1], [visibility("$1")], + [void __attribute__((visibility("$1"))) $1_function() { }], + [$2], [$3]) +]) + +AC_DEFUN([CC_ATTRIBUTE_NONNULL], [ + CC_CHECK_ATTRIBUTE( + [nonnull], [nonnull()], + [void __attribute__((nonnull())) some_function(void *foo, void *bar) { foo = (void*)0; bar = (void*)0; }], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_UNUSED], [ + CC_CHECK_ATTRIBUTE( + [unused], , + [void some_function(void *foo, __attribute__((unused)) void *bar);], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_SENTINEL], [ + CC_CHECK_ATTRIBUTE( + [sentinel], , + [void some_function(void *foo, ...) __attribute__((sentinel));], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_DEPRECATED], [ + CC_CHECK_ATTRIBUTE( + [deprecated], , + [void some_function(void *foo, ...) __attribute__((deprecated));], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_ALIAS], [ + CC_CHECK_ATTRIBUTE( + [alias], [weak, alias], + [void other_function(void *foo) { } + void some_function(void *foo) __attribute__((weak, alias("other_function")));], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_MALLOC], [ + CC_CHECK_ATTRIBUTE( + [malloc], , + [void * __attribute__((malloc)) my_alloc(int n);], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_PACKED], [ + CC_CHECK_ATTRIBUTE( + [packed], , + [struct astructure { char a; int b; long c; void *d; } __attribute__((packed));], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_CONST], [ + CC_CHECK_ATTRIBUTE( + [const], , + [int __attribute__((const)) twopow(int n) { return 1 << n; } ], + [$1], [$2]) +]) + +AC_DEFUN([CC_FLAG_VISIBILITY], [ + AC_REQUIRE([CC_CHECK_WERROR]) + AC_CACHE_CHECK([if $CC supports -fvisibility=hidden], + [cc_cv_flag_visibility], + [cc_flag_visibility_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $cc_cv_werror" + CC_CHECK_CFLAGS_SILENT([-fvisibility=hidden], + cc_cv_flag_visibility='yes', + cc_cv_flag_visibility='no') + CFLAGS="$cc_flag_visibility_save_CFLAGS"]) + + AS_IF([test "x$cc_cv_flag_visibility" = "xyes"], + [AC_DEFINE([SUPPORT_FLAG_VISIBILITY], 1, + [Define this if the compiler supports the -fvisibility flag]) + $1], + [$2]) +]) + +AC_DEFUN([CC_FUNC_EXPECT], [ + AC_REQUIRE([CC_CHECK_WERROR]) + AC_CACHE_CHECK([if compiler has __builtin_expect function], + [cc_cv_func_expect], + [ac_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $cc_cv_werror" + AC_COMPILE_IFELSE([AC_LANG_SOURCE( + [int some_function() { + int a = 3; + return (int)__builtin_expect(a, 3); + }])], + [cc_cv_func_expect=yes], + [cc_cv_func_expect=no]) + CFLAGS="$ac_save_CFLAGS" + ]) + + AS_IF([test "x$cc_cv_func_expect" = "xyes"], + [AC_DEFINE([SUPPORT__BUILTIN_EXPECT], 1, + [Define this if the compiler supports __builtin_expect() function]) + $1], + [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_ALIGNED], [ + AC_REQUIRE([CC_CHECK_WERROR]) + AC_CACHE_CHECK([highest __attribute__ ((aligned ())) supported], + [cc_cv_attribute_aligned], + [ac_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $cc_cv_werror" + for cc_attribute_align_try in 64 32 16 8 4 2; do + AC_COMPILE_IFELSE([AC_LANG_SOURCE([ + int main() { + static char c __attribute__ ((aligned($cc_attribute_align_try))) = 0; + return c; + }])], [cc_cv_attribute_aligned=$cc_attribute_align_try; break]) + done + CFLAGS="$ac_save_CFLAGS" + ]) + + if test "x$cc_cv_attribute_aligned" != "x"; then + AC_DEFINE_UNQUOTED([ATTRIBUTE_ALIGNED_MAX], [$cc_cv_attribute_aligned], + [Define the highest alignment supported]) + fi +]) diff --git a/packaging/libevdev.spec b/packaging/libevdev.spec new file mode 100644 index 0000000..65389f2 --- /dev/null +++ b/packaging/libevdev.spec @@ -0,0 +1,57 @@ +Name: libevdev +Version: 1.2.2 +Release: 0 +License: MIT +Summary: wrapper library for evdev input devices +Url: git://anongit.freedesktop.org/libevdev +Group: System/Libraries +Source: %{name}-%{version}.tar.gz + +BuildRequires: doxygen +BuildRequires: make +BuildRequires: python + + +%description +libevdev is a wrapper library for evdev devices. it moves the common +tasks when dealing with evdev devices into a library and provides a library +interface to the callers, thus avoiding erroneous ioctls, etc. + + +%package devel +Summary: wrapper library for evdev input devices +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} + +%description devel +libevdev is a wrapper library for evdev devices. it moves the common +tasks when dealing with evdev devices into a library and provides a library +interface to the callers, thus avoiding erroneous ioctls, etc. + +%prep +%setup -q +%autogen + +%build +make %{?jobs:-j%jobs} V=1 + +%install +%make_install +#%fdupes %{buildroot} + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + + +%files +%defattr(-,root,root) +%{_libdir}/*.so.* +%{_datadir}/* + + +%files devel +%defattr(-,root,root,-) +%{_includedir}/* +%{_libdir}/*.so +%{_libdir}/pkgconfig/* diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..4b0ab3e --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,5 @@ +gcov-report.txt +test-libevdev +test-link +test-compile-pedantic +test-kernel diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 0000000..7b195fb --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,114 @@ +if BUILD_TESTS +run_tests = test-libevdev test-kernel +build_tests = test-compile-pedantic test-link + +.NOTPARALLEL: + +noinst_PROGRAMS = $(run_tests) $(build_tests) + +TESTS = $(run_tests) + +libevdev_sources = $(top_srcdir)/libevdev/libevdev.c \ + $(top_srcdir)/libevdev/libevdev.h \ + $(top_srcdir)/libevdev/libevdev-names.c \ + $(top_srcdir)/libevdev/libevdev-uinput.h \ + $(top_srcdir)/libevdev/libevdev-uinput.c \ + $(top_srcdir)/libevdev/libevdev-uinput-int.h \ + $(top_srcdir)/libevdev/libevdev-util.h \ + $(top_srcdir)/libevdev/libevdev-int.h +common_sources = $(libevdev_sources) \ + test-common-uinput.c \ + test-common-uinput.h \ + test-common.c \ + test-common.h + +# include builddir for event-names.h +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/include -I$(top_builddir)/libevdev $(CHECK_CFLAGS) $(GCOV_CFLAGS) + +test_libevdev_SOURCES = \ + test-main.c \ + test-event-names.c \ + test-event-codes.c \ + test-libevdev-init.c \ + test-libevdev-has-event.c \ + test-int-queue.c \ + test-libevdev-events.c \ + test-uinput.c \ + $(common_sources) + +test_libevdev_LDADD = $(CHECK_LIBS) $(GCOV_LDFLAGS) + +test_compile_pedantic_SOURCES = test-compile-pedantic.c +test_compile_pedantic_CFLAGS = $(AM_CPPFLAGS) -pedantic -Werror -std=c90 + +test_link_SOURCES = test-link.c +test_link_CFLAGS = -I$(top_srcdir) +test_link_LDADD = $(top_builddir)/libevdev/libevdev.la + +test_kernel_SOURCES = \ + test-kernel.c \ + $(common_sources) +test_kernel_CFLAGS = -I$(top_srcdir) +test_kernel_LDADD = $(CHECK_LIBS) + +if HAVE_VALGRIND +VALGRIND_FLAGS=--leak-check=full \ + --quiet \ + --error-exitcode=3 \ + --suppressions=$(srcdir)/valgrind.suppressions + +valgrind: + $(MAKE) check-TESTS LOG_COMPILER="$(VALGRIND)" LOG_FLAGS="$(VALGRIND_FLAGS)" + +check: valgrind + +endif + +EXTRA_DIST = valgrind.suppressions + +if GCOV_ENABLED + +CLEANFILES = gcov-report.txt + +gcov-clean: + @rm -f *.gcov + +gcov-report.txt: gcov-clean check-TESTS + $(AM_V_GEN)(rm -rf $@; \ + echo "========== coverage report ========" >> $@; \ + for file in `find $(top_srcdir)/libevdev -name "*.c" -printf "%P\n"`; do \ + gcov $$file > /dev/null; \ + if test -f $$file.gcov; then \ + total=`grep -v " -:" $$file.gcov | wc -l`; \ + missing=`grep "#####" $$file.gcov | wc -l`; \ + hit=$$((total - missing)); \ + echo -e "$$file: total lines: $$total not tested: $$missing ($$((($$hit * 100)/$$total))%)"; \ + fi \ + done >> $@; \ + echo "========== =============== ========" >> $@; \ + ) + +gcov: gcov-report.txt + @cat gcov-report.txt + +check-local: gcov + +else + +gcov-report.txt: + @true + +gcov: + @true + +gcov-clean: + @true + +endif + +.PHONY: gcov gcov-clean gcov-report.txt + +clean-local: gcov-clean + rm -f *.gcno *.gcda + +endif diff --git a/test/test-common-uinput.c b/test/test-common-uinput.c new file mode 100644 index 0000000..617b8e2 --- /dev/null +++ b/test/test-common-uinput.c @@ -0,0 +1,299 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "test-common-uinput.h" + +#define SYS_INPUT_DIR "/sys/class/input" +#define DEV_INPUT_DIR "/dev/input/" + +struct uinput_device +{ + struct libevdev *d; /* lazy, it has all the accessors */ + struct libevdev_uinput *uidev; + int dev_fd; /* open fd to the devnode */ + int uinput_fd; +}; + +struct uinput_device* +uinput_device_new(const char *name) +{ + struct uinput_device *dev; + + dev = calloc(1, sizeof(*dev)); + if (!dev) + return NULL; + + dev->d = libevdev_new(); + dev->dev_fd = -1; + dev->uinput_fd = -1; + + if (name) + libevdev_set_name(dev->d, name); + + return dev; +} + +int +uinput_device_new_with_events_v(struct uinput_device **d, const char *name, const struct input_id *id, va_list args) +{ + int rc; + struct uinput_device *dev; + + dev = uinput_device_new(name); + if (!dev) + return -ENOMEM; + if (id != DEFAULT_IDS) + uinput_device_set_ids(dev, id); + + rc = uinput_device_set_event_bits_v(dev, args); + + if (rc == 0) + rc = uinput_device_create(dev); + + if (rc != 0) { + uinput_device_free(dev); + dev = NULL; + } else + *d = dev; + + return rc; +} + +int +uinput_device_new_with_events(struct uinput_device **d, const char *name, const struct input_id *id, ...) +{ + int rc; + va_list args; + + va_start(args, id); + rc = uinput_device_new_with_events_v(d, name, id, args); + va_end(args); + + return rc; +} + +void +uinput_device_free(struct uinput_device *dev) +{ + if (!dev) + return; + + if (dev->uinput_fd != -1) { + ioctl(dev->uinput_fd, UI_DEV_DESTROY, NULL); + close(dev->uinput_fd); + } + if (dev->dev_fd != -1) + close(dev->dev_fd); + libevdev_free(dev->d); + libevdev_uinput_destroy(dev->uidev); + free(dev); +} + +int +uinput_device_get_fd(const struct uinput_device *dev) +{ + return dev->dev_fd; +} + +const char* +uinput_device_get_devnode(const struct uinput_device *dev) +{ + return libevdev_uinput_get_devnode(dev->uidev); +} + +int +uinput_device_create(struct uinput_device* d) +{ + int rc; + int fd; + const char *devnode; + + fd = open("/dev/uinput", O_RDWR); + if (fd < 0) + goto error; + + d->uinput_fd = fd; + + rc = libevdev_uinput_create_from_device(d->d, fd, &d->uidev); + if (rc != 0) + goto error; + + devnode = libevdev_uinput_get_devnode(d->uidev); + if (devnode == NULL) + goto error; + + d->dev_fd = open(devnode, O_RDWR); + if (d->dev_fd == -1) + goto error; + + /* write abs resolution now */ + if (libevdev_has_event_type(d->d, EV_ABS)) { + int code; + for (code = 0; code < ABS_CNT; code++) { + const struct input_absinfo *abs; + + /* can't change slots */ + if (code == ABS_MT_SLOT) + continue; + + abs = libevdev_get_abs_info(d->d, code); + if (!abs) + continue; + + rc = ioctl(d->dev_fd, EVIOCSABS(code), abs); + if (rc < 0) { + printf("error %s for code %d\n", strerror(-rc), code); + goto error; + } + } + } + + return 0; + +error: + if (d->dev_fd != -1) + close(d->dev_fd); + if (d->uinput_fd != -1) + close(d->uinput_fd); + return -errno; + +} + +int uinput_device_set_name(struct uinput_device *dev, const char *name) +{ + libevdev_set_name(dev->d, name); + return 0; +} + +int uinput_device_set_ids(struct uinput_device *dev, const struct input_id *ids) +{ + libevdev_set_id_product(dev->d, ids->product); + libevdev_set_id_vendor(dev->d, ids->vendor); + libevdev_set_id_bustype(dev->d, ids->bustype); + libevdev_set_id_version(dev->d, ids->version); + return 0; +} + +int +uinput_device_set_bit(struct uinput_device* dev, unsigned int bit) +{ + return libevdev_enable_event_type(dev->d, bit); +} + +int +uinput_device_set_prop(struct uinput_device *dev, unsigned int prop) +{ + return libevdev_enable_property(dev->d, prop); +} + +int +uinput_device_set_event_bit(struct uinput_device* dev, unsigned int type, unsigned int code) +{ + return libevdev_enable_event_code(dev->d, type, code, NULL); +} + +int +uinput_device_set_event_bits_v(struct uinput_device *dev, va_list args) +{ + int type, code; + int rc = 0; + + do { + type = va_arg(args, int); + if (type == -1) + break; + code = va_arg(args, int); + if (code == -1) + break; + rc = libevdev_enable_event_code(dev->d, type, code, NULL); + } while (rc == 0); + + return rc; +} + +int +uinput_device_set_event_bits(struct uinput_device *dev, ...) +{ + int rc; + va_list args; + va_start(args, dev); + rc = uinput_device_set_event_bits_v(dev, args); + va_end(args); + + return rc; +} + +int +uinput_device_set_abs_bit(struct uinput_device* dev, unsigned int code, const struct input_absinfo *absinfo) +{ + return libevdev_enable_event_code(dev->d, EV_ABS, code, absinfo); +} + +int +uinput_device_event(const struct uinput_device *dev, unsigned int type, unsigned int code, int value) +{ + return libevdev_uinput_write_event(dev->uidev, type, code, value); +} + +int uinput_device_event_multiple_v(const struct uinput_device* dev, va_list args) +{ + int type, code, value; + int rc = 0; + + do { + type = va_arg(args, int); + if (type == -1) + break; + code = va_arg(args, int); + if (code == -1) + break; + value = va_arg(args, int); + rc = uinput_device_event(dev, type, code, value); + } while (rc == 0); + + return rc; +} + +int uinput_device_event_multiple(const struct uinput_device* dev, ...) +{ + int rc; + va_list args; + va_start(args, dev); + rc = uinput_device_event_multiple_v(dev, args); + va_end(args); + return rc; +} diff --git a/test/test-common-uinput.h b/test/test-common-uinput.h new file mode 100644 index 0000000..6273a6d --- /dev/null +++ b/test/test-common-uinput.h @@ -0,0 +1,49 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include + +#define DEFAULT_IDS NULL + + +struct uinput_device* uinput_device_new(const char *name); +int uinput_device_new_with_events(struct uinput_device **dev, const char *name, const struct input_id *ids, ...); +int uinput_device_new_with_events_v(struct uinput_device **dev, const char *name, const struct input_id *ids, va_list args); +void uinput_device_free(struct uinput_device *dev); + +int uinput_device_create(struct uinput_device* dev); +int uinput_device_set_name(struct uinput_device* dev, const char *name); +int uinput_device_set_ids(struct uinput_device* dev, const struct input_id *ids); +int uinput_device_set_bit(struct uinput_device* dev, unsigned int bit); +int uinput_device_set_prop(struct uinput_device *dev, unsigned int prop); +int uinput_device_set_event_bit(struct uinput_device* dev, unsigned int type, unsigned int code); +int uinput_device_set_event_bits(struct uinput_device* dev, ...); +int uinput_device_set_event_bits_v(struct uinput_device* dev, va_list args); +int uinput_device_set_abs_bit(struct uinput_device* dev, unsigned int code, const struct input_absinfo *absinfo); +int uinput_device_event(const struct uinput_device* dev, unsigned int type, unsigned int code, int value); +int uinput_device_event_multiple(const struct uinput_device* dev, ...); +int uinput_device_event_multiple_v(const struct uinput_device* dev, va_list args); +int uinput_device_get_fd(const struct uinput_device *dev); +const char* uinput_device_get_devnode(const struct uinput_device *dev); + +char *uinput_devnode_from_syspath(const char *syspath); diff --git a/test/test-common.c b/test/test-common.c new file mode 100644 index 0000000..6449d46 --- /dev/null +++ b/test/test-common.c @@ -0,0 +1,122 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "test-common.h" + +void test_logfunc_abort_on_error(enum libevdev_log_priority priority, + void *data, + const char *file, int line, + const char *func, + const char *format, va_list args) +{ + vprintf(format, args); + ck_abort(); +} + +void test_logfunc_ignore_error(enum libevdev_log_priority priority, + void *data, + const char *file, int line, + const char *func, + const char *format, va_list args) +{ +} + +int test_create_device(struct uinput_device **uidev_return, + struct libevdev **dev_return, + ...) +{ + int rc, fd; + struct uinput_device *uidev; + struct libevdev *dev; + va_list args; + + va_start(args, dev_return); + + rc = uinput_device_new_with_events_v(&uidev, TEST_DEVICE_NAME, DEFAULT_IDS, args); + va_end(args); + + ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc)); + + fd = uinput_device_get_fd(uidev); + + rc = libevdev_new_from_fd(fd, &dev); + ck_assert_msg(rc == 0, "Failed to init device device: %s", strerror(-rc)); + rc = fcntl(fd, F_SETFL, O_NONBLOCK); + ck_assert_msg(rc == 0, "fcntl failed: %s", strerror(errno)); + + *uidev_return = uidev; + *dev_return = dev; + + return rc == 0 ? rc : -errno; +} + +int test_create_abs_device(struct uinput_device **uidev_return, + struct libevdev **dev_return, + int nabs, + const struct input_absinfo *abs, + ...) +{ + int rc, fd; + struct uinput_device *uidev; + struct libevdev *dev; + va_list args; + + uidev = uinput_device_new(TEST_DEVICE_NAME); + ck_assert(uidev != NULL); + + va_start(args, abs); + rc = uinput_device_set_event_bits_v(uidev, args); + va_end(args); + + while (--nabs >= 0) { + int code; + struct input_absinfo a; + + code = abs[nabs].value; + a = abs[nabs]; + a.value = 0; + + rc = uinput_device_set_abs_bit(uidev, code, &a); + ck_assert_msg(rc == 0, "for abs field %d\n", nabs); + } + + rc = uinput_device_create(uidev); + ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc)); + + fd = uinput_device_get_fd(uidev); + + rc = libevdev_new_from_fd(fd, &dev); + ck_assert_msg(rc == 0, "Failed to init device device: %s", strerror(-rc)); + rc = fcntl(fd, F_SETFL, O_NONBLOCK); + ck_assert_msg(rc == 0, "fcntl failed: %s", strerror(errno)); + + *uidev_return = uidev; + *dev_return = dev; + + return rc == 0 ? rc : -errno; +} diff --git a/test/test-common.h b/test/test-common.h new file mode 100644 index 0000000..d7a33d0 --- /dev/null +++ b/test/test-common.h @@ -0,0 +1,55 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include + +#include + +#ifndef _TEST_COMMON_H_ +#define _TEST_COMMON_H_ + +#define TEST_DEVICE_NAME "libevdev test device" + +#include "test-common-uinput.h" + + +int test_create_device(struct uinput_device **uidev, + struct libevdev **dev, + ...); +int test_create_abs_device(struct uinput_device **uidev, + struct libevdev **dev, + int nabs, + const struct input_absinfo *abs, + ...); + +void test_logfunc_abort_on_error(enum libevdev_log_priority priority, + void *data, + const char *file, int line, + const char *func, + const char *format, va_list args); +void test_logfunc_ignore_error(enum libevdev_log_priority priority, + void *data, + const char *file, int line, + const char *func, + const char *format, va_list args); +#endif /* _TEST_COMMON_H_ */ diff --git a/test/test-compile-pedantic.c b/test/test-compile-pedantic.c new file mode 100644 index 0000000..6faea86 --- /dev/null +++ b/test/test-compile-pedantic.c @@ -0,0 +1,6 @@ +#include +#include + +int main(void) { + return 0; +} diff --git a/test/test-event-codes.c b/test/test-event-codes.c new file mode 100644 index 0000000..3a57973 --- /dev/null +++ b/test/test-event-codes.c @@ -0,0 +1,119 @@ +/* + * Copyright © 2013 David Herrmann + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include "test-common.h" + +START_TEST(test_type_codes) +{ + ck_assert(libevdev_event_type_from_name("EV_SYN") == EV_SYN); + ck_assert(libevdev_event_type_from_name("EV_KEY") == EV_KEY); + ck_assert(libevdev_event_type_from_name("EV_REL") == EV_REL); + ck_assert(libevdev_event_type_from_name("EV_ABS") == EV_ABS); + ck_assert(libevdev_event_type_from_name("EV_MSC") == EV_MSC); + ck_assert(libevdev_event_type_from_name("EV_SND") == EV_SND); + ck_assert(libevdev_event_type_from_name("EV_SW") == EV_SW); + ck_assert(libevdev_event_type_from_name("EV_LED") == EV_LED); + ck_assert(libevdev_event_type_from_name("EV_REP") == EV_REP); + ck_assert(libevdev_event_type_from_name("EV_FF") == EV_FF); + ck_assert(libevdev_event_type_from_name("EV_FF_STATUS") == EV_FF_STATUS); + ck_assert(libevdev_event_type_from_name("EV_MAX") == EV_MAX); + + ck_assert(libevdev_event_type_from_name_n("EV_SYNTAX", 6) == EV_SYN); + ck_assert(libevdev_event_type_from_name_n("EV_REPTILE", 6) == EV_REP); +} +END_TEST + +START_TEST(test_type_invalid) +{ + ck_assert(libevdev_event_type_from_name("EV_Syn") == -1); + ck_assert(libevdev_event_type_from_name("ev_SYN") == -1); + ck_assert(libevdev_event_type_from_name("SYN") == -1); + ck_assert(libevdev_event_type_from_name("EV_SYNTAX") == -1); + + ck_assert(libevdev_event_type_from_name_n("EV_SYN", 5) == -1); + ck_assert(libevdev_event_type_from_name_n("EV_REPTILE", 7) == -1); +} +END_TEST + +START_TEST(test_key_codes) +{ + ck_assert(libevdev_event_code_from_name(EV_SYN, "SYN_REPORT") == SYN_REPORT); + ck_assert(libevdev_event_code_from_name(EV_ABS, "ABS_X") == ABS_X); + ck_assert(libevdev_event_code_from_name(EV_KEY, "BTN_A") == BTN_A); + ck_assert(libevdev_event_code_from_name(EV_KEY, "KEY_A") == KEY_A); + ck_assert(libevdev_event_code_from_name(EV_REL, "REL_X") == REL_X); + ck_assert(libevdev_event_code_from_name(EV_MSC, "MSC_RAW") == MSC_RAW); + ck_assert(libevdev_event_code_from_name(EV_LED, "LED_KANA") == LED_KANA); + ck_assert(libevdev_event_code_from_name(EV_SND, "SND_BELL") == SND_BELL); + ck_assert(libevdev_event_code_from_name(EV_REP, "REP_DELAY") == REP_DELAY); + ck_assert(libevdev_event_code_from_name(EV_SYN, "SYN_DROPPED") == SYN_DROPPED); + ck_assert(libevdev_event_code_from_name(EV_KEY, "KEY_RESERVED") == KEY_RESERVED); + ck_assert(libevdev_event_code_from_name(EV_KEY, "BTN_0") == BTN_0); + ck_assert(libevdev_event_code_from_name(EV_KEY, "KEY_0") == KEY_0); + ck_assert(libevdev_event_code_from_name(EV_FF, "FF_GAIN") == FF_GAIN); + ck_assert(libevdev_event_code_from_name(EV_FF_STATUS, "FF_STATUS_MAX") == FF_STATUS_MAX); + ck_assert(libevdev_event_code_from_name(EV_SW, "SW_MAX") == SW_MAX); + + ck_assert(libevdev_event_code_from_name_n(EV_ABS, "ABS_YXZ", 5) == ABS_Y); +} +END_TEST + +START_TEST(test_key_invalid) +{ + ck_assert(libevdev_event_code_from_name(EV_MAX, "MAX_FAKE") == -1); + ck_assert(libevdev_event_code_from_name(EV_CNT, "CNT_FAKE") == -1); + ck_assert(libevdev_event_code_from_name(EV_PWR, "PWR_SOMETHING") == -1); + ck_assert(libevdev_event_code_from_name(EV_ABS, "EV_ABS") == -1); + ck_assert(libevdev_event_code_from_name(EV_ABS, "ABS_XY") == -1); + ck_assert(libevdev_event_code_from_name(EV_KEY, "BTN_GAMEPAD") == -1); + ck_assert(libevdev_event_code_from_name(EV_KEY, "BUS_PCI") == -1); + ck_assert(libevdev_event_code_from_name(EV_FF_STATUS, "FF_STATUS") == -1); + ck_assert(libevdev_event_code_from_name(EV_FF_STATUS, "FF_STATUS_") == -1); + ck_assert(libevdev_event_code_from_name(EV_FF, "FF_STATUS") == -1); + ck_assert(libevdev_event_code_from_name(EV_FF, "FF_STATUS_") == -1); + ck_assert(libevdev_event_code_from_name(EV_KEY, "ID_BUS") == -1); + ck_assert(libevdev_event_code_from_name(EV_SND, "SND_CNT") == -1); + ck_assert(libevdev_event_code_from_name(EV_SW, "SW_CNT") == -1); + + ck_assert(libevdev_event_code_from_name_n(EV_ABS, "ABS_X", 4) == -1); +} +END_TEST + +Suite * +event_code_suite(void) +{ + Suite *s = suite_create("Event codes"); + + TCase *tc = tcase_create("type tests"); + tcase_add_test(tc, test_type_codes); + tcase_add_test(tc, test_type_invalid); + suite_add_tcase(s, tc); + + tc = tcase_create("key tests"); + tcase_add_test(tc, test_key_codes); + tcase_add_test(tc, test_key_invalid); + suite_add_tcase(s, tc); + + return s; +} diff --git a/test/test-event-names.c b/test/test-event-names.c new file mode 100644 index 0000000..8d7e6d6 --- /dev/null +++ b/test/test-event-names.c @@ -0,0 +1,301 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include "test-common.h" + +START_TEST(test_limits) +{ + ck_assert(libevdev_event_type_get_name(EV_MAX + 1) == NULL); + ck_assert(libevdev_event_code_get_name(EV_ABS, ABS_MAX + 1) == NULL); + ck_assert(libevdev_event_code_get_name(EV_REL, REL_MAX + 1) == NULL); + ck_assert(libevdev_event_code_get_name(EV_KEY, KEY_MAX + 1) == NULL); + ck_assert(libevdev_event_code_get_name(EV_LED, LED_MAX + 1) == NULL); + ck_assert(libevdev_event_code_get_name(EV_SW, SW_MAX + 1) == NULL); + ck_assert(libevdev_event_code_get_name(EV_MSC, MSC_MAX + 1) == NULL); + ck_assert(libevdev_event_code_get_name(EV_SND, SND_MAX + 1) == NULL); + ck_assert(libevdev_event_code_get_name(EV_REP, REP_MAX + 1) == NULL); + ck_assert(libevdev_event_code_get_name(EV_FF, FF_MAX + 1) == NULL); + ck_assert(libevdev_event_code_get_name(EV_MAX + 1, 0) == NULL); +} +END_TEST + +START_TEST(test_type_name) +{ + ck_assert_str_eq(libevdev_event_type_get_name(EV_SYN), "EV_SYN"); + ck_assert_str_eq(libevdev_event_type_get_name(EV_REL), "EV_REL"); + ck_assert_str_eq(libevdev_event_type_get_name(EV_ABS), "EV_ABS"); + ck_assert_str_eq(libevdev_event_type_get_name(EV_MSC), "EV_MSC"); + ck_assert_str_eq(libevdev_event_type_get_name(EV_SW), "EV_SW"); + ck_assert_str_eq(libevdev_event_type_get_name(EV_LED), "EV_LED"); + ck_assert_str_eq(libevdev_event_type_get_name(EV_SND), "EV_SND"); + ck_assert_str_eq(libevdev_event_type_get_name(EV_REP), "EV_REP"); + ck_assert_str_eq(libevdev_event_type_get_name(EV_FF), "EV_FF"); + ck_assert_str_eq(libevdev_event_type_get_name(EV_PWR), "EV_PWR"); + ck_assert_str_eq(libevdev_event_type_get_name(EV_FF_STATUS), "EV_FF_STATUS"); + ck_assert_str_eq(libevdev_event_type_get_name(EV_MAX), "EV_MAX"); +} +END_TEST + +START_TEST(test_code_abs_name) +{ + /* pick out a few only */ + ck_assert_str_eq(libevdev_event_code_get_name(EV_ABS, ABS_X), "ABS_X"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_ABS, ABS_Y), "ABS_Y"); + + ck_assert_str_eq(libevdev_event_code_get_name(EV_ABS, ABS_MT_SLOT), "ABS_MT_SLOT"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_ABS, ABS_MISC), "ABS_MISC"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_ABS, ABS_MAX), "ABS_MAX"); + + ck_assert(libevdev_event_code_get_name(EV_ABS, ABS_MAX - 1) == NULL); + +} +END_TEST + +START_TEST(test_code_rel_name) +{ + /* pick out a few only */ + ck_assert_str_eq(libevdev_event_code_get_name(EV_REL, REL_X), "REL_X"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_REL, REL_Y), "REL_Y"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_REL, REL_MISC), "REL_MISC"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_REL, REL_MAX), "REL_MAX"); + + ck_assert(libevdev_event_code_get_name(EV_REL, REL_MAX - 1) == NULL); + +} +END_TEST + +START_TEST(test_code_key_name) +{ + /* pick out a few only */ + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, KEY_RESERVED), "KEY_RESERVED"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, KEY_ESC), "KEY_ESC"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, KEY_1), "KEY_1"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, KEY_2), "KEY_2"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, KEY_UNKNOWN), "KEY_UNKNOWN"); + + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, BTN_0), "BTN_0"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, BTN_LEFT), "BTN_LEFT"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, BTN_TRIGGER), "BTN_TRIGGER"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, BTN_A), "BTN_SOUTH"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, BTN_TOOL_PEN), "BTN_TOOL_PEN"); + + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, KEY_TOUCHPAD_TOGGLE), "KEY_TOUCHPAD_TOGGLE"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, BTN_TRIGGER_HAPPY), "BTN_TRIGGER_HAPPY1"); + + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, KEY_MAX), "KEY_MAX"); + ck_assert(libevdev_event_code_get_name(EV_KEY, KEY_MAX - 1) == NULL); + + /* special cases that resolve to something else */ + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, KEY_HANGUEL), "KEY_HANGEUL"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, KEY_SCREENLOCK), "KEY_COFFEE"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, BTN_MISC), "BTN_0"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, BTN_MOUSE), "BTN_LEFT"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, BTN_JOYSTICK), "BTN_TRIGGER"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, BTN_GAMEPAD), "BTN_SOUTH"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, BTN_DIGI), "BTN_TOOL_PEN"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, BTN_WHEEL), "BTN_GEAR_DOWN"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_KEY, BTN_TRIGGER_HAPPY), "BTN_TRIGGER_HAPPY1"); + +} +END_TEST + +START_TEST(test_code_led_name) +{ + /* pick out a few only */ + ck_assert_str_eq(libevdev_event_code_get_name(EV_LED, LED_NUML), "LED_NUML"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_LED, LED_KANA), "LED_KANA"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_LED, LED_MAX), "LED_MAX"); + + ck_assert(libevdev_event_code_get_name(EV_LED, LED_MAX - 1) == NULL); + +} +END_TEST + +START_TEST(test_code_snd_name) +{ + /* pick out a few only */ + ck_assert_str_eq(libevdev_event_code_get_name(EV_SND, SND_CLICK), "SND_CLICK"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_SND, SND_TONE), "SND_TONE"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_SND, SND_MAX), "SND_MAX"); + + ck_assert(libevdev_event_code_get_name(EV_SND, SND_MAX - 1) == NULL); + +} +END_TEST + +START_TEST(test_code_msc_name) +{ + /* pick out a few only */ + ck_assert_str_eq(libevdev_event_code_get_name(EV_MSC, MSC_SERIAL), "MSC_SERIAL"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_MSC, MSC_RAW), "MSC_RAW"); +#ifdef MSC_TIMESTAMP + ck_assert_str_eq(libevdev_event_code_get_name(EV_MSC, MSC_TIMESTAMP), "MSC_TIMESTAMP"); +#endif + ck_assert_str_eq(libevdev_event_code_get_name(EV_MSC, MSC_MAX), "MSC_MAX"); + + ck_assert(libevdev_event_code_get_name(EV_MSC, MSC_MAX - 1) == NULL); + +} +END_TEST + +START_TEST(test_code_sw_name) +{ + /* pick out a few only */ + ck_assert_str_eq(libevdev_event_code_get_name(EV_SW, SW_LID), "SW_LID"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_SW, SW_RFKILL_ALL), "SW_RFKILL_ALL"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_SW, SW_LINEIN_INSERT), "SW_LINEIN_INSERT"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_SW, SW_MAX), "SW_MAX"); +} +END_TEST + +START_TEST(test_code_ff_name) +{ + /* pick out a few only */ + ck_assert_str_eq(libevdev_event_code_get_name(EV_FF, FF_STATUS_STOPPED), "FF_STATUS_STOPPED"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_FF, FF_FRICTION), "FF_FRICTION"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_FF, FF_CUSTOM), "FF_CUSTOM"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_FF, FF_MAX), "FF_MAX"); + + ck_assert(libevdev_event_code_get_name(EV_FF, FF_MAX - 1) == NULL); + +} +END_TEST + +START_TEST(test_code_syn_name) +{ + ck_assert_str_eq(libevdev_event_code_get_name(EV_SYN, SYN_REPORT), "SYN_REPORT"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_SYN, SYN_CONFIG), "SYN_CONFIG"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_SYN, SYN_MT_REPORT), "SYN_MT_REPORT"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_SYN, SYN_DROPPED), "SYN_DROPPED"); + ck_assert_str_eq(libevdev_event_code_get_name(EV_SYN, SYN_MAX), "SYN_MAX"); +} +END_TEST + +START_TEST(test_prop_name) +{ + ck_assert_str_eq(libevdev_property_get_name(INPUT_PROP_POINTER), "INPUT_PROP_POINTER"); + ck_assert_str_eq(libevdev_property_get_name(INPUT_PROP_DIRECT), "INPUT_PROP_DIRECT"); + ck_assert_str_eq(libevdev_property_get_name(INPUT_PROP_BUTTONPAD), "INPUT_PROP_BUTTONPAD"); + ck_assert_str_eq(libevdev_property_get_name(INPUT_PROP_SEMI_MT), "INPUT_PROP_SEMI_MT"); + ck_assert_str_eq(libevdev_property_get_name(INPUT_PROP_MAX), "INPUT_PROP_MAX"); + + ck_assert(libevdev_property_get_name(INPUT_PROP_MAX - 1) == NULL); + ck_assert(libevdev_property_get_name(INPUT_PROP_MAX + 1) == NULL); +} +END_TEST + +START_TEST(test_event_type_max) +{ + ck_assert_int_eq(libevdev_event_type_get_max(EV_ABS), ABS_MAX); + ck_assert_int_eq(libevdev_event_type_get_max(EV_REL), REL_MAX); + ck_assert_int_eq(libevdev_event_type_get_max(EV_KEY), KEY_MAX); + + ck_assert_int_eq(libevdev_event_type_get_max(EV_MAX - 1), -1); + ck_assert_int_eq(libevdev_event_type_get_max(EV_MAX + 1), -1); + +} +END_TEST + +START_TEST(test_event_type) +{ + struct input_event ev; + int i = 0; + + ev.type = EV_REL; + + ck_assert_int_eq(libevdev_event_is_type(&ev, EV_REL), 1); + for (i = 0; i < EV_CNT; i++) { + if (i == ev.type) + continue; + ck_assert_int_eq(libevdev_event_is_type(&ev, i), 0); + } + ck_assert_int_eq(libevdev_event_is_type(&ev, EV_MAX + 1), 0); +} +END_TEST + +START_TEST(test_event_code) +{ + struct input_event ev; + int i = 0; + + ev.type = EV_REL; + ev.code = REL_Y; + + ck_assert_int_eq(libevdev_event_is_code(&ev, EV_REL, REL_Y), 1); + for (i = 0; i < EV_CNT; i++) { + int j; + if (i == ev.type || i == EV_SYN) + continue; + + for (j = 0; j < libevdev_event_type_get_max(i); i++) { + ck_assert_int_eq(libevdev_event_is_code(&ev, i, j), 0); + } + } + ck_assert_int_eq(libevdev_event_is_code(&ev, EV_MAX + 1, ev.code), 0); + ck_assert_int_eq(libevdev_event_is_code(&ev, EV_REL, REL_MAX + 1), 0); + + ev.type = EV_SYN; + ev.code = SYN_REPORT; + ck_assert_int_eq(libevdev_event_is_code(&ev, EV_SYN, SYN_REPORT), 1); + ck_assert_int_eq(libevdev_event_is_code(&ev, EV_SYN, SYN_DROPPED), 0); +} +END_TEST + +Suite * +event_name_suite(void) +{ + Suite *s = suite_create("Event names"); + + TCase *tc = tcase_create("type limits"); + tcase_add_test(tc, test_limits); + tcase_add_test(tc, test_event_type_max); + suite_add_tcase(s, tc); + + tc = tcase_create("type names"); + tcase_add_test(tc, test_type_name); + suite_add_tcase(s, tc); + + tc = tcase_create("code names"); + tcase_add_test(tc, test_code_abs_name); + tcase_add_test(tc, test_code_rel_name); + tcase_add_test(tc, test_code_key_name); + tcase_add_test(tc, test_code_led_name); + tcase_add_test(tc, test_code_snd_name); + tcase_add_test(tc, test_code_msc_name); + tcase_add_test(tc, test_code_sw_name); + tcase_add_test(tc, test_code_ff_name); + tcase_add_test(tc, test_code_syn_name); + suite_add_tcase(s, tc); + + tc = tcase_create("prop names"); + tcase_add_test(tc, test_prop_name); + suite_add_tcase(s, tc); + + tc = tcase_create("event values"); + tcase_add_test(tc, test_event_type); + tcase_add_test(tc, test_event_code); + suite_add_tcase(s, tc); + + return s; +} + diff --git a/test/test-int-queue.c b/test/test-int-queue.c new file mode 100644 index 0000000..d45fee0 --- /dev/null +++ b/test/test-int-queue.c @@ -0,0 +1,360 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include +#include "test-common.h" + +START_TEST(test_queue_alloc) +{ + struct libevdev dev; + int rc; + + rc = queue_alloc(&dev, 0); + ck_assert_int_eq(rc, -ENOMEM); + + rc = queue_alloc(&dev, ULONG_MAX); + ck_assert_int_eq(rc, -ENOMEM); + + rc = queue_alloc(&dev, 100); + ck_assert_int_eq(rc, 0); + + ck_assert_int_eq(dev.queue_size, 100); + ck_assert_int_eq(dev.queue_next, 0); + + queue_free(&dev); + ck_assert_int_eq(dev.queue_size, 0); + ck_assert_int_eq(dev.queue_next, 0); + +} +END_TEST + +START_TEST(test_queue_sizes) +{ + struct libevdev dev = {0}; + + queue_alloc(&dev, 0); + ck_assert_int_eq(queue_num_elements(&dev), 0); + ck_assert_int_eq(queue_num_free_elements(&dev), 0); + ck_assert_int_eq(queue_size(&dev), 0); + + queue_alloc(&dev, 100); + ck_assert_int_eq(queue_num_elements(&dev), 0); + ck_assert_int_eq(queue_num_free_elements(&dev), 100); + ck_assert_int_eq(queue_size(&dev), 100); + + queue_free(&dev); + + ck_assert_int_eq(queue_num_elements(&dev), 0); + ck_assert_int_eq(queue_num_free_elements(&dev), 0); + ck_assert_int_eq(queue_size(&dev), 0); +} +END_TEST + +START_TEST(test_queue_push) +{ + struct libevdev dev = {0}; + struct input_event *ev; + + queue_alloc(&dev, 0); + ev = queue_push(&dev); + ck_assert(ev == NULL); + + queue_alloc(&dev, 2); + ev = queue_push(&dev); + ck_assert(ev == dev.queue); + ck_assert_int_eq(queue_num_elements(&dev), 1); + ck_assert_int_eq(queue_num_free_elements(&dev), 1); + + ev = queue_push(&dev); + ck_assert(ev == dev.queue + 1); + + ev = queue_push(&dev); + ck_assert(ev == NULL); + + queue_free(&dev); + ev = queue_push(&dev); + ck_assert(ev == NULL); + +} +END_TEST + +START_TEST(test_queue_pop) +{ + struct libevdev dev = {0}; + struct input_event ev, *e, tmp; + int rc; + + queue_alloc(&dev, 0); + rc = queue_pop(&dev, &ev); + ck_assert_int_eq(rc, 1); + + queue_alloc(&dev, 2); + e = queue_push(&dev); + memset(e, 0xab, sizeof(*e)); + ck_assert_int_eq(queue_num_elements(&dev), 1); + ck_assert_int_eq(queue_num_free_elements(&dev), 1); + + rc = queue_pop(&dev, &ev); + ck_assert_int_eq(rc, 0); + memset(&tmp, 0xab, sizeof(tmp)); + rc = memcmp(&tmp, &ev, sizeof(tmp)); + ck_assert_int_eq(rc, 0); + + ck_assert_int_eq(queue_num_elements(&dev), 0); + ck_assert_int_eq(queue_num_free_elements(&dev), 2); + + rc = queue_pop(&dev, &ev); + ck_assert_int_eq(rc, 1); + + queue_free(&dev); +} +END_TEST + +START_TEST(test_queue_peek) +{ + struct libevdev dev = {0}; + struct input_event ev, *e, tmp; + int rc; + + queue_alloc(&dev, 0); + rc = queue_peek(&dev, 0, &ev); + ck_assert_int_eq(rc, 1); + + queue_alloc(&dev, 2); + e = queue_push(&dev); + memset(e, 0xab, sizeof(*e)); + + rc = queue_peek(&dev, 0, &ev); + ck_assert_int_eq(rc, 0); + memset(&tmp, 0xab, sizeof(tmp)); + rc = memcmp(&tmp, &ev, sizeof(tmp)); + ck_assert_int_eq(rc, 0); + + ck_assert_int_eq(queue_num_elements(&dev), 1); + e = queue_push(&dev); + memset(e, 0xbc, sizeof(*e)); + + rc = queue_peek(&dev, 1, &ev); + ck_assert_int_eq(rc, 0); + memset(&tmp, 0xbc, sizeof(tmp)); + rc = memcmp(&tmp, &ev, sizeof(tmp)); + ck_assert_int_eq(rc, 0); + + rc = queue_peek(&dev, 0, &ev); + ck_assert_int_eq(rc, 0); + memset(&tmp, 0xab, sizeof(tmp)); + rc = memcmp(&tmp, &ev, sizeof(tmp)); + ck_assert_int_eq(rc, 0); + + ck_assert_int_eq(queue_num_elements(&dev), 2); + + queue_free(&dev); +} +END_TEST + +START_TEST(test_queue_shift) +{ + struct libevdev dev = {0}; + struct input_event ev, *first, *second, e1, e2; + int rc; + + ck_assert_int_eq(queue_shift(&dev, &ev), 1); + + queue_alloc(&dev, 10); + ck_assert_int_eq(queue_shift(&dev, &ev), 1); + + first = queue_push(&dev); + ck_assert(first != NULL); + memset(first, 0xab, sizeof(*first)); + + e1 = *first; + + second = queue_push(&dev); + ck_assert(second != NULL); + memset(second, 0x12, sizeof(*second)); + + e2 = *second; + + rc = queue_shift(&dev, &ev); + ck_assert_int_eq(rc, 0); + rc = memcmp(&ev, &e1, sizeof(ev)); + ck_assert_int_eq(rc, 0); + + rc = queue_shift(&dev, &ev); + ck_assert_int_eq(rc, 0); + rc = memcmp(&ev, &e2, sizeof(ev)); + ck_assert_int_eq(rc, 0); + + ck_assert_int_eq(queue_shift(&dev, &ev), 1); + + queue_free(&dev); +} +END_TEST + +START_TEST(test_queue_shift_multiple) +{ + struct libevdev dev = {0}; + struct input_event ev, *first, *second, e1, e2; + struct input_event events[5]; + int rc; + + ck_assert_int_eq(queue_shift_multiple(&dev, 1, &ev), 0); + ck_assert_int_eq(queue_shift_multiple(&dev, 0, &ev), 0); + + queue_alloc(&dev, 10); + ck_assert_int_eq(queue_shift_multiple(&dev, 1, &ev), 0); + ck_assert_int_eq(queue_shift_multiple(&dev, 0, &ev), 0); + + first = queue_push(&dev); + ck_assert(first != NULL); + memset(first, 0xab, sizeof(*first)); + e1 = *first; + + second = queue_push(&dev); + ck_assert(second != NULL); + memset(second, 0x12, sizeof(*second)); + e2 = *second; + + rc = queue_shift_multiple(&dev, 5, events); + ck_assert_int_eq(rc, 2); + rc = memcmp(&events[0], &e1, sizeof(ev)); + ck_assert_int_eq(rc, 0); + rc = memcmp(&events[1], &e2, sizeof(ev)); + ck_assert_int_eq(rc, 0); + + first = queue_push(&dev); + ck_assert(first != NULL); + memset(first, 0xab, sizeof(*first)); + e1 = *first; + + second = queue_push(&dev); + ck_assert(second != NULL); + memset(second, 0x12, sizeof(*second)); + e2 = *second; + + rc = queue_shift_multiple(&dev, 1, events); + ck_assert_int_eq(rc, 1); + rc = memcmp(&events[0], &e1, sizeof(ev)); + ck_assert_int_eq(rc, 0); + + rc = queue_shift_multiple(&dev, 1, events); + ck_assert_int_eq(rc, 1); + rc = memcmp(&events[0], &e2, sizeof(ev)); + ck_assert_int_eq(rc, 0); + + ck_assert_int_eq(queue_shift_multiple(&dev, 1, events), 0); + + queue_free(&dev); +} +END_TEST + +START_TEST(test_queue_next_element) +{ + struct libevdev dev = {0}; + struct input_event ev, *first, *second; + int rc; + + queue_alloc(&dev, 0); + first = queue_next_element(&dev); + ck_assert(first == NULL); + + queue_alloc(&dev, 2); + first = queue_next_element(&dev); + ck_assert(first != NULL); + memset(first, 0xab, sizeof(*first)); + + second = queue_next_element(&dev); + ck_assert(second != NULL); + memset(second, 0xbc, sizeof(*second)); + + /* queue_next_element does not advance, so we overwrite */ + memset(&ev, 0xbc, sizeof(ev)); + rc = memcmp(&ev, first, sizeof(ev)); + ck_assert_int_eq(rc, 0); + + ck_assert_int_eq(queue_num_elements(&dev), 0); + + first = queue_next_element(&dev); + ck_assert(first != NULL); + memset(first, 0xab, sizeof(*first)); + + queue_set_num_elements(&dev, 1); + ck_assert_int_eq(queue_num_elements(&dev), 1); + + second = queue_next_element(&dev); + ck_assert(second != NULL); + memset(second, 0xbc, sizeof(*second)); + + memset(&ev, 0xab, sizeof(ev)); + rc = memcmp(&ev, first, sizeof(ev)); + ck_assert_int_eq(rc, 0); + + queue_free(&dev); +} +END_TEST + +START_TEST(test_queue_set_num_elements) +{ + struct libevdev dev = {0}; + + queue_alloc(&dev, 0); + ck_assert_int_eq(queue_set_num_elements(&dev, 1), 1); + + queue_alloc(&dev, 2); + ck_assert_int_eq(queue_set_num_elements(&dev, 3), 1); + ck_assert_int_eq(queue_set_num_elements(&dev, 2), 0); + + queue_free(&dev); +} +END_TEST + +Suite * +queue_suite(void) +{ + Suite *s = suite_create("Event queue"); + + TCase *tc = tcase_create("Queue allocation"); + tcase_add_test(tc, test_queue_alloc); + tcase_add_test(tc, test_queue_sizes); + suite_add_tcase(s, tc); + + tc = tcase_create("Queue push/pop/peek"); + tcase_add_test(tc, test_queue_push); + tcase_add_test(tc, test_queue_pop); + tcase_add_test(tc, test_queue_peek); + suite_add_tcase(s, tc); + + tc = tcase_create("Queue shift"); + tcase_add_test(tc, test_queue_shift); + tcase_add_test(tc, test_queue_shift_multiple); + suite_add_tcase(s, tc); + + tc = tcase_create("Queue next elem"); + tcase_add_test(tc, test_queue_next_element); + tcase_add_test(tc, test_queue_set_num_elements); + suite_add_tcase(s, tc); + + return s; +} + diff --git a/test/test-kernel.c b/test/test-kernel.c new file mode 100644 index 0000000..f3bc7e0 --- /dev/null +++ b/test/test-kernel.c @@ -0,0 +1,185 @@ +/* + * Copyright © 2014 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test-common.h" + +START_TEST(test_revoke) +{ + struct uinput_device* uidev; + struct libevdev *dev, *dev2; + int rc, fd; + struct input_event ev1, ev2; + int dev_fd; + + test_create_device(&uidev, &dev, + EV_SYN, SYN_REPORT, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_REL, REL_WHEEL, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_RIGHT, + -1); + + fd = open(uinput_device_get_devnode(uidev), O_RDONLY|O_NONBLOCK); + ck_assert_int_gt(fd, -1); + rc = libevdev_new_from_fd(fd, &dev2); + ck_assert_msg(rc == 0, "Failed to create second device: %s", strerror(-rc)); + + uinput_device_event(uidev, EV_REL, REL_X, 1); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev1); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); + + rc = libevdev_next_event(dev2, LIBEVDEV_READ_FLAG_NORMAL, &ev2); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); + + ck_assert_int_eq(ev1.type, ev2.type); + ck_assert_int_eq(ev1.code, ev2.code); + ck_assert_int_eq(ev1.value, ev2.value); + + /* revoke first device, expect it closed, second device still open */ + dev_fd = libevdev_get_fd(dev); + ck_assert_int_ge(dev_fd, 0); + rc = ioctl(dev_fd, EVIOCREVOKE, NULL); + if (rc == -1 && errno == -EINVAL) { + fprintf(stderr, "WARNING: skipping EVIOCREVOKE test, not suported by current kernel\n"); + goto out; + } + ck_assert_msg(rc == 0, "Failed to revoke device: %s", strerror(errno)); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev1); + ck_assert_int_eq(rc, -ENODEV); + + rc = libevdev_next_event(dev2, LIBEVDEV_READ_FLAG_NORMAL, &ev2); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); + +out: + uinput_device_free(uidev); + libevdev_free(dev); + libevdev_free(dev2); + close(fd); +} +END_TEST + +START_TEST(test_revoke_invalid) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + int dev_fd; + + test_create_device(&uidev, &dev, + EV_SYN, SYN_REPORT, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_REL, REL_WHEEL, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_RIGHT, + -1); + + dev_fd = libevdev_get_fd(dev); + ck_assert_int_ge(dev_fd, 0); + /* ioctl requires 0 as value */ + rc = ioctl(dev_fd, EVIOCREVOKE, 1); + ck_assert_int_eq(rc, -1); + ck_assert_int_eq(errno, EINVAL); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_revoke_fail_after) +{ + struct uinput_device* uidev; + struct libevdev *dev, *dev2 = NULL; + int rc, fd; + + test_create_device(&uidev, &dev, + EV_SYN, SYN_REPORT, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_REL, REL_WHEEL, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_RIGHT, + -1); + + fd = open(uinput_device_get_devnode(uidev), O_RDONLY|O_NONBLOCK); + ck_assert_int_gt(fd, -1); + + rc = ioctl(fd, EVIOCREVOKE, NULL); + if (rc == -1 && errno == -EINVAL) { + fprintf(stderr, "WARNING: skipping EVIOCREVOKE test, not suported by current kernel\n"); + goto out; + } + ck_assert_msg(rc == 0, "Failed to revoke device: %s", strerror(errno)); + + rc = libevdev_new_from_fd(fd, &dev2); + ck_assert_int_eq(rc, -ENODEV); + +out: + uinput_device_free(uidev); + libevdev_free(dev); + close(fd); +} +END_TEST + +int main(int argc, char **argv) +{ + SRunner *sr; + Suite *s; + TCase *tc; + int failed; + + s = suite_create("kernel tests"); + + tc = tcase_create("EVIOCREVOKE"); + tcase_add_test(tc, test_revoke); + tcase_add_test(tc, test_revoke_invalid); + tcase_add_test(tc, test_revoke_fail_after); + suite_add_tcase(s, tc); + + sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + + failed = srunner_ntests_failed(sr); + srunner_free(sr); + + return failed; +} diff --git a/test/test-libevdev-events.c b/test/test-libevdev-events.c new file mode 100644 index 0000000..6ef0bf9 --- /dev/null +++ b/test/test-libevdev-events.c @@ -0,0 +1,2115 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "test-common.h" + +START_TEST(test_next_event) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + + test_create_device(&uidev, &dev, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_KEY, BTN_LEFT, + -1); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, -EAGAIN); + + uinput_device_event(uidev, EV_KEY, BTN_LEFT, 1); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); + ck_assert_int_eq(ev.type, EV_KEY); + ck_assert_int_eq(ev.code, BTN_LEFT); + ck_assert_int_eq(ev.value, 1); + + libevdev_free(dev); + uinput_device_free(uidev); + +} +END_TEST + +START_TEST(test_syn_dropped_event) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + int pipefd[2]; + + test_create_device(&uidev, &dev, + EV_SYN, SYN_REPORT, + EV_SYN, SYN_DROPPED, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_KEY, BTN_LEFT, + -1); + + /* This is a bit complicated: + we can't get SYN_DROPPED through uinput, so we push two events down + uinput, and fetch one off libevdev (reading in the other one on the + way). Then write a SYN_DROPPED on a pipe, switch the fd and read + one event off the wire (but returning the second event from + before). Switch back, so that when we do read off the SYN_DROPPED + we have the fd back on the device and the ioctls work. + */ + uinput_device_event(uidev, EV_KEY, BTN_LEFT, 1); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); + ck_assert_int_eq(ev.type, EV_KEY); + ck_assert_int_eq(ev.code, BTN_LEFT); + rc = pipe2(pipefd, O_NONBLOCK); + ck_assert_int_eq(rc, 0); + + libevdev_change_fd(dev, pipefd[0]); + ev.type = EV_SYN; + ev.code = SYN_DROPPED; + ev.value = 0; + rc = write(pipefd[1], &ev, sizeof(ev)); + ck_assert_int_eq(rc, sizeof(ev)); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + + libevdev_change_fd(dev, uinput_device_get_fd(uidev)); + + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); + ck_assert_int_eq(ev.type, EV_SYN); + ck_assert_int_eq(ev.code, SYN_REPORT); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_SYN); + ck_assert_int_eq(ev.code, SYN_DROPPED); + + /* only check for the rc, nothing actually changed on the device */ + + libevdev_free(dev); + uinput_device_free(uidev); + + close(pipefd[0]); + close(pipefd[1]); + +} +END_TEST + +void double_syn_dropped_logfunc(enum libevdev_log_priority priority, + void *data, + const char *file, int line, + const char *func, + const char *format, va_list args) +{ + unsigned int *hit = data; + *hit = 1; +} + +START_TEST(test_double_syn_dropped_event) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + int pipefd[2]; + unsigned int logfunc_hit = 0; + + test_create_device(&uidev, &dev, + EV_SYN, SYN_REPORT, + EV_SYN, SYN_DROPPED, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_KEY, BTN_LEFT, + -1); + + libevdev_set_log_function(double_syn_dropped_logfunc, &logfunc_hit); + + /* This is a bit complicated: + we can't get SYN_DROPPED through uinput, so we push two events down + uinput, and fetch one off libevdev (reading in the other one on the + way). Then write a SYN_DROPPED on a pipe, switch the fd and read + one event off the wire (but returning the second event from + before). Switch back, so that when we do read off the SYN_DROPPED + we have the fd back on the device and the ioctls work. + */ + uinput_device_event(uidev, EV_KEY, BTN_LEFT, 1); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); + ck_assert_int_eq(ev.type, EV_KEY); + ck_assert_int_eq(ev.code, BTN_LEFT); + rc = pipe2(pipefd, O_NONBLOCK); + ck_assert_int_eq(rc, 0); + + libevdev_change_fd(dev, pipefd[0]); + ev.type = EV_SYN; + ev.code = SYN_DROPPED; + ev.value = 0; + rc = write(pipefd[1], &ev, sizeof(ev)); + ck_assert_int_eq(rc, sizeof(ev)); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + + /* sneak in a button change event while we're not looking, this way + * the sync queue contains 2 events: BTN_LEFT and SYN_REPORT. */ + uinput_device_event(uidev, EV_KEY, BTN_LEFT, 0); + ck_assert_int_eq(read(pipefd[0], &ev, sizeof(ev)), -1); + + libevdev_change_fd(dev, uinput_device_get_fd(uidev)); + + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); + ck_assert_int_eq(ev.type, EV_SYN); + ck_assert_int_eq(ev.code, SYN_REPORT); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_SYN); + ck_assert_int_eq(ev.code, SYN_DROPPED); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_KEY); + ck_assert_int_eq(ev.code, BTN_LEFT); + ck_assert_int_eq(ev.value, 0); + + /* now write the second SYN_DROPPED on the pipe so we pick it up + * before we finish syncing. */ + libevdev_change_fd(dev, pipefd[0]); + ev.type = EV_SYN; + ev.code = SYN_DROPPED; + ev.value = 0; + rc = write(pipefd[1], &ev, sizeof(ev)); + ck_assert_int_eq(rc, sizeof(ev)); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_SYN); + ck_assert_int_eq(ev.code, SYN_REPORT); + ck_assert_int_eq(ev.value, 0); + + /* back to enable the ioctls again */ + libevdev_change_fd(dev, uinput_device_get_fd(uidev)); + + ck_assert_int_eq(logfunc_hit, 1); + + libevdev_free(dev); + uinput_device_free(uidev); + + close(pipefd[0]); + close(pipefd[1]); + + libevdev_set_log_function(test_logfunc_abort_on_error, NULL); +} +END_TEST + +START_TEST(test_event_type_filtered) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + + test_create_device(&uidev, &dev, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_KEY, BTN_LEFT, + -1); + + libevdev_disable_event_type(dev, EV_REL); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, -EAGAIN); + + uinput_device_event(uidev, EV_REL, REL_X, 1); + uinput_device_event(uidev, EV_KEY, REL_Y, 1); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); + ck_assert_int_eq(ev.type, EV_SYN); + ck_assert_int_eq(ev.code, SYN_REPORT); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, -EAGAIN); + + libevdev_free(dev); + uinput_device_free(uidev); + +} +END_TEST + +START_TEST(test_event_code_filtered) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + + test_create_device(&uidev, &dev, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_KEY, BTN_LEFT, + -1); + + libevdev_disable_event_code(dev, EV_REL, REL_X); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, -EAGAIN); + + uinput_device_event(uidev, EV_REL, REL_X, 1); + uinput_device_event(uidev, EV_REL, REL_Y, 1); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); + ck_assert_int_eq(ev.type, EV_REL); + ck_assert_int_eq(ev.code, REL_Y); + ck_assert_int_eq(ev.value, 1); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); + ck_assert_int_eq(ev.type, EV_SYN); + ck_assert_int_eq(ev.code, SYN_REPORT); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, -EAGAIN); + + libevdev_free(dev); + uinput_device_free(uidev); + +} +END_TEST + +START_TEST(test_has_event_pending) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + + test_create_device(&uidev, &dev, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_KEY, BTN_LEFT, + -1); + + ck_assert_int_eq(libevdev_has_event_pending(dev), 0); + + uinput_device_event(uidev, EV_REL, REL_X, 1); + uinput_device_event(uidev, EV_REL, REL_Y, 1); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + + ck_assert_int_eq(libevdev_has_event_pending(dev), 1); + + libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + + ck_assert_int_eq(libevdev_has_event_pending(dev), 1); + + while ((rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev)) != -EAGAIN) + ; + + ck_assert_int_eq(libevdev_has_event_pending(dev), 0); + + libevdev_change_fd(dev, -1); + ck_assert_int_eq(libevdev_has_event_pending(dev), -EBADF); + + libevdev_free(dev); + uinput_device_free(uidev); + +} +END_TEST + +START_TEST(test_syn_delta_button) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + + test_create_device(&uidev, &dev, + EV_SYN, SYN_REPORT, + EV_SYN, SYN_DROPPED, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_RIGHT, + EV_KEY, KEY_MAX, + -1); + + uinput_device_event(uidev, EV_KEY, BTN_LEFT, 1); + uinput_device_event(uidev, EV_KEY, BTN_RIGHT, 1); + uinput_device_event(uidev, EV_KEY, KEY_MAX, 1); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_KEY); + ck_assert_int_eq(ev.code, BTN_LEFT); + ck_assert_int_eq(ev.value, 1); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_KEY); + ck_assert_int_eq(ev.code, BTN_RIGHT); + ck_assert_int_eq(ev.value, 1); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_KEY); + ck_assert_int_eq(ev.code, KEY_MAX); + ck_assert_int_eq(ev.value, 1); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_SYN); + ck_assert_int_eq(ev.code, SYN_REPORT); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, -EAGAIN); + + ck_assert(libevdev_get_event_value(dev, EV_KEY, BTN_LEFT)); + ck_assert(libevdev_get_event_value(dev, EV_KEY, BTN_RIGHT)); + ck_assert(!libevdev_get_event_value(dev, EV_KEY, BTN_MIDDLE)); + ck_assert(libevdev_get_event_value(dev, EV_KEY, KEY_MAX)); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_syn_delta_abs) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + struct input_absinfo abs[3]; + + memset(abs, 0, sizeof(abs)); + abs[0].value = ABS_X; + abs[0].maximum = 1000; + + abs[1].value = ABS_Y; + abs[1].maximum = 1000; + + abs[2].value = ABS_MAX; + abs[2].maximum = 1000; + + test_create_abs_device(&uidev, &dev, + 3, abs, + EV_SYN, SYN_REPORT, + EV_SYN, SYN_DROPPED, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_RIGHT, + -1); + + uinput_device_event(uidev, EV_ABS, ABS_X, 100); + uinput_device_event(uidev, EV_ABS, ABS_Y, 500); + uinput_device_event(uidev, EV_ABS, ABS_MAX, 700); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_ABS); + ck_assert_int_eq(ev.code, ABS_X); + ck_assert_int_eq(ev.value, 100); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_ABS); + ck_assert_int_eq(ev.code, ABS_Y); + ck_assert_int_eq(ev.value, 500); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_ABS); + ck_assert_int_eq(ev.code, ABS_MAX); + ck_assert_int_eq(ev.value, 700); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_SYN); + ck_assert_int_eq(ev.code, SYN_REPORT); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, -EAGAIN); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_syn_delta_mt) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + struct input_absinfo abs[6]; + + memset(abs, 0, sizeof(abs)); + abs[0].value = ABS_X; + abs[0].maximum = 1000; + abs[1].value = ABS_MT_POSITION_X; + abs[1].maximum = 1000; + + abs[2].value = ABS_Y; + abs[2].maximum = 1000; + abs[3].value = ABS_MT_POSITION_Y; + abs[3].maximum = 1000; + + + abs[4].value = ABS_MT_SLOT; + abs[4].maximum = 1; + abs[5].value = ABS_MT_TRACKING_ID; + abs[5].minimum = -1; + abs[5].maximum = 2; + + test_create_abs_device(&uidev, &dev, + 6, abs, + EV_SYN, SYN_REPORT, + -1); + + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0); + uinput_device_event(uidev, EV_ABS, ABS_X, 100); + uinput_device_event(uidev, EV_ABS, ABS_Y, 500); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 100); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 500); + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 1); + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 1); + uinput_device_event(uidev, EV_ABS, ABS_X, 1); + uinput_device_event(uidev, EV_ABS, ABS_Y, 5); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 1); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 5); + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 2); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + + ck_assert_int_eq(libevdev_get_current_slot(dev), 0); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_ABS); + ck_assert_int_eq(ev.code, ABS_X); + ck_assert_int_eq(ev.value, 1); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_ABS); + ck_assert_int_eq(ev.code, ABS_Y); + ck_assert_int_eq(ev.value, 5); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_ABS); + ck_assert_int_eq(ev.code, ABS_MT_SLOT); + ck_assert_int_eq(ev.value, 0); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_ABS); + ck_assert_int_eq(ev.code, ABS_MT_POSITION_X); + ck_assert_int_eq(ev.value, 100); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_ABS); + ck_assert_int_eq(ev.code, ABS_MT_POSITION_Y); + ck_assert_int_eq(ev.value, 500); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_ABS); + ck_assert_int_eq(ev.code, ABS_MT_TRACKING_ID); + ck_assert_int_eq(ev.value, 1); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_ABS); + ck_assert_int_eq(ev.code, ABS_MT_SLOT); + ck_assert_int_eq(ev.value, 1); + ck_assert_int_eq(libevdev_get_current_slot(dev), 1); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_ABS); + ck_assert_int_eq(ev.code, ABS_MT_POSITION_X); + ck_assert_int_eq(ev.value, 1); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_ABS); + ck_assert_int_eq(ev.code, ABS_MT_POSITION_Y); + ck_assert_int_eq(ev.value, 5); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_ABS); + ck_assert_int_eq(ev.code, ABS_MT_TRACKING_ID); + ck_assert_int_eq(ev.value, 2); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_SYN); + ck_assert_int_eq(ev.code, SYN_REPORT); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, -EAGAIN); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_syn_delta_mt_reset_slot) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev, + last_slot_event = { .type = 0}; + struct input_absinfo abs[6]; + + memset(abs, 0, sizeof(abs)); + abs[0].value = ABS_X; + abs[0].maximum = 1000; + abs[1].value = ABS_MT_POSITION_X; + abs[1].maximum = 1000; + + abs[2].value = ABS_Y; + abs[2].maximum = 1000; + abs[3].value = ABS_MT_POSITION_Y; + abs[3].maximum = 1000; + + + abs[4].value = ABS_MT_SLOT; + abs[4].maximum = 1; + abs[5].value = ABS_MT_TRACKING_ID; + abs[5].minimum = -1; + abs[5].maximum = 2; + + test_create_abs_device(&uidev, &dev, + 6, abs, + EV_SYN, SYN_REPORT, + -1); + + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 1); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 100); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 500); + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 1); + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 1); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 5); + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 2); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + + do { + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + if (libevdev_event_is_code(&ev, EV_ABS, ABS_MT_SLOT)) + last_slot_event = ev; + } while (rc != -EAGAIN); + + ck_assert(libevdev_event_is_code(&last_slot_event, EV_ABS, ABS_MT_SLOT)); + ck_assert_int_eq(last_slot_event.value, 0); + ck_assert_int_eq(libevdev_get_current_slot(dev), 0); + + last_slot_event.type = 0; + + /* same thing again, this time swap the numbers */ + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 100); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 500); + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 1); + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 1); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 1); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 5); + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 2); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + + do { + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + if (libevdev_event_is_code(&ev, EV_ABS, ABS_MT_SLOT)) + last_slot_event = ev; + } while (rc != -EAGAIN); + + ck_assert(libevdev_event_is_code(&last_slot_event, EV_ABS, ABS_MT_SLOT)); + ck_assert_int_eq(last_slot_event.value, 1); + ck_assert_int_eq(libevdev_get_current_slot(dev), 1); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_syn_delta_led) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + + test_create_device(&uidev, &dev, + EV_SYN, SYN_REPORT, + EV_SYN, SYN_DROPPED, + EV_LED, LED_NUML, + EV_LED, LED_CAPSL, + EV_LED, LED_MAX, + -1); + + uinput_device_event(uidev, EV_LED, LED_NUML, 1); + uinput_device_event(uidev, EV_LED, LED_CAPSL, 1); + uinput_device_event(uidev, EV_LED, LED_MAX, 1); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_LED); + ck_assert_int_eq(ev.code, LED_NUML); + ck_assert_int_eq(ev.value, 1); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_LED); + ck_assert_int_eq(ev.code, LED_CAPSL); + ck_assert_int_eq(ev.value, 1); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_LED); + ck_assert_int_eq(ev.code, LED_MAX); + ck_assert_int_eq(ev.value, 1); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_SYN); + ck_assert_int_eq(ev.code, SYN_REPORT); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, -EAGAIN); + + ck_assert_int_eq(libevdev_get_event_value(dev, EV_LED, LED_NUML), 1); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_LED, LED_CAPSL), 1); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_LED, LED_MAX), 1); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_syn_delta_sw) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + + test_create_device(&uidev, &dev, + EV_SYN, SYN_REPORT, + EV_SYN, SYN_DROPPED, + EV_SW, SW_LID, + EV_SW, SW_MICROPHONE_INSERT, + EV_SW, SW_MAX, + -1); + + uinput_device_event(uidev, EV_SW, SW_LID, 1); + uinput_device_event(uidev, EV_SW, SW_MICROPHONE_INSERT, 1); + uinput_device_event(uidev, EV_SW, SW_MAX, 1); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_SW); + ck_assert_int_eq(ev.code, SW_LID); + ck_assert_int_eq(ev.value, 1); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_SW); + ck_assert_int_eq(ev.code, SW_MICROPHONE_INSERT); + ck_assert_int_eq(ev.value, 1); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_SW); + ck_assert_int_eq(ev.code, SW_MAX); + ck_assert_int_eq(ev.value, 1); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_SYN); + ck_assert_int_eq(ev.code, SYN_REPORT); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, -EAGAIN); + + ck_assert_int_eq(libevdev_get_event_value(dev, EV_SW, SW_LID), 1); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_SW, SW_MICROPHONE_INSERT), 1); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_SW, SW_MAX), 1); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_syn_delta_tracking_ids) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + struct input_absinfo abs[6]; + int i; + const int num_slots = 15; + int slot = -1; + unsigned long terminated[NLONGS(num_slots)]; + unsigned long restarted[NLONGS(num_slots)]; + + memset(abs, 0, sizeof(abs)); + abs[0].value = ABS_X; + abs[0].maximum = 1000; + abs[1].value = ABS_MT_POSITION_X; + abs[1].maximum = 1000; + + abs[2].value = ABS_Y; + abs[2].maximum = 1000; + abs[3].value = ABS_MT_POSITION_Y; + abs[3].maximum = 1000; + + abs[4].value = ABS_MT_SLOT; + abs[4].maximum = num_slots; + + abs[5].minimum = -1; + abs[5].maximum = 255; + abs[5].value = ABS_MT_TRACKING_ID; + + test_create_abs_device(&uidev, &dev, + 6, abs, + EV_SYN, SYN_REPORT, + -1); + + /* Test the sync process to make sure we get touches terminated when + * the tracking id changes: + * 1) start a bunch of touch points + * 2) read data into libevdev, make sure state is up-to-date + * 3) change touchpoints + * 3.1) change the tracking ID on some (indicating terminated and + * re-started touchpoint) + * 3.2) change the tracking ID to -1 on some (indicating termianted + * touchpoint) + * 3.3) just update the data on others + * 4) force a sync on the device + * 5) make sure we get the right tracking ID changes in the caller + */ + + /* Start a bunch of touch points */ + for (i = num_slots; i >= 0; i--) { + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, i); + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, i); + uinput_device_event(uidev, EV_ABS, ABS_X, 100 + i); + uinput_device_event(uidev, EV_ABS, ABS_Y, 500 + i); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 100 + i); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 500 + i); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + do { + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_ne(rc, LIBEVDEV_READ_STATUS_SYNC); + } while (rc >= 0); + } + + /* we have a bunch of touches now, and libevdev knows it. Change all + * touches */ + for (i = num_slots; i >= 0; i--) { + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, i); + if (i % 3 == 0) { + /* change some slots with a new tracking id */ + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, num_slots + i); + uinput_device_event(uidev, EV_ABS, ABS_X, 200 + i); + uinput_device_event(uidev, EV_ABS, ABS_Y, 700 + i); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 200 + i); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 700 + i); + } else if (i % 3 == 1) { + /* stop others */ + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, -1); + } else { + /* just update */ + uinput_device_event(uidev, EV_ABS, ABS_X, 200 + i); + uinput_device_event(uidev, EV_ABS, ABS_Y, 700 + i); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 200 + i); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 700 + i); + } + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + } + + /* Force sync */ + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + + /* now check for the right tracking IDs */ + memset(terminated, 0, sizeof(terminated)); + memset(restarted, 0, sizeof(restarted)); + slot = -1; + while ((rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev)) != -EAGAIN) { + if (libevdev_event_is_code(&ev, EV_SYN, SYN_REPORT)) + continue; + + if (libevdev_event_is_code(&ev, EV_ABS, ABS_MT_SLOT)) { + slot = ev.value; + continue; + } + + if (libevdev_event_is_code(&ev, EV_ABS, ABS_X) || + libevdev_event_is_code(&ev, EV_ABS, ABS_Y)) + continue; + + ck_assert_int_ne(slot, -1); + + if (libevdev_event_is_code(&ev, EV_ABS, ABS_MT_TRACKING_ID)) { + if (slot % 3 == 0) { + if (!bit_is_set(terminated, slot)) { + ck_assert_int_eq(ev.value, -1); + set_bit(terminated, slot); + } else { + ck_assert_int_eq(ev.value, num_slots + slot); + set_bit(restarted, slot); + } + } else if (slot % 3 == 1) { + ck_assert(!bit_is_set(terminated, slot)); + ck_assert_int_eq(ev.value, -1); + set_bit(terminated, slot); + } else + ck_abort(); + + continue; + } + + switch(ev.code) { + case ABS_MT_POSITION_X: + ck_assert_int_eq(ev.value, 200 + slot); + break; + case ABS_MT_POSITION_Y: + ck_assert_int_eq(ev.value, 700 + slot); + break; + default: + ck_abort(); + } + } + + for (i = 0; i < num_slots; i++) { + if (i % 3 == 0) { + ck_assert(bit_is_set(terminated, i)); + ck_assert(bit_is_set(restarted, i)); + } else if (i % 3 == 1) { + ck_assert(bit_is_set(terminated, i)); + ck_assert(!bit_is_set(restarted, i)); + } else { + ck_assert(!bit_is_set(terminated, i)); + ck_assert(!bit_is_set(restarted, i)); + } + } + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_syn_delta_late_sync) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + struct input_absinfo abs[6]; + int i, slot; + + memset(abs, 0, sizeof(abs)); + abs[0].value = ABS_X; + abs[0].maximum = 1000; + abs[1].value = ABS_MT_POSITION_X; + abs[1].maximum = 1000; + + abs[2].value = ABS_Y; + abs[2].maximum = 1000; + abs[3].value = ABS_MT_POSITION_Y; + abs[3].maximum = 1000; + + abs[4].value = ABS_MT_SLOT; + abs[4].maximum = 1; + + abs[5].minimum = -1; + abs[5].maximum = 255; + abs[5].value = ABS_MT_TRACKING_ID; + + test_create_abs_device(&uidev, &dev, + 6, abs, + EV_SYN, SYN_REPORT, + -1); + + /* emulate a touch down, make sure libevdev sees it */ + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0); + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 1); + uinput_device_event(uidev, EV_ABS, ABS_X, 100); + uinput_device_event(uidev, EV_ABS, ABS_Y, 500); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 100); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 500); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + do { + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_ne(rc, LIBEVDEV_READ_STATUS_SYNC); + } while (rc >= 0); + + /* force enough events to trigger a SYN_DROPPED */ + for (i = 0; i < 100; i++) { + uinput_device_event(uidev, EV_ABS, ABS_X, 100 + i); + uinput_device_event(uidev, EV_ABS, ABS_Y, 500 + i); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 100 + i); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 500 + i); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + } + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + + /* trigger the tracking ID change after getting the SYN_DROPPED */ + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0); + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, -1); + uinput_device_event(uidev, EV_ABS, ABS_X, 200); + uinput_device_event(uidev, EV_ABS, ABS_Y, 600); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 200); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 600); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + + slot = 0; + + /* Now sync the device, expect the data to be equal to the last event*/ + while ((rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev)) != -EAGAIN) { + if (ev.type == EV_SYN) + continue; + + ck_assert_int_eq(ev.type, EV_ABS); + switch(ev.code) { + case ABS_MT_SLOT: + slot = ev.value; + break; + case ABS_MT_TRACKING_ID: + if (slot == 0) + ck_assert_int_eq(ev.value, -1); + break; + case ABS_X: + case ABS_MT_POSITION_X: + ck_assert_int_eq(ev.value, 200); + break; + case ABS_Y: + case ABS_MT_POSITION_Y: + ck_assert_int_eq(ev.value, 600); + break; + } + } + + /* And a new tracking ID */ + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0); + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 2); + uinput_device_event(uidev, EV_ABS, ABS_X, 201); + uinput_device_event(uidev, EV_ABS, ABS_Y, 601); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 201); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 601); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + + while ((rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev)) != -EAGAIN) { + ck_assert_int_ne(rc, LIBEVDEV_READ_STATUS_SYNC); + + if (ev.type == EV_SYN) + continue; + + ck_assert_int_eq(ev.type, EV_ABS); + + switch(ev.code) { + case ABS_MT_SLOT: + ck_assert_int_eq(ev.value, 0); + break; + case ABS_MT_TRACKING_ID: + ck_assert_int_eq(ev.value, 2); + break; + case ABS_X: + case ABS_MT_POSITION_X: + ck_assert_int_eq(ev.value, 201); + break; + case ABS_Y: + case ABS_MT_POSITION_Y: + ck_assert_int_eq(ev.value, 601); + break; + } + } + + + /* Now we basically re-do the exact same test, just with the + tracking ID order inverted */ + + /* drop the tracking ID, make sure libevdev sees it */ + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0); + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, -1); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + do { + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_ne(rc, LIBEVDEV_READ_STATUS_SYNC); + } while (rc >= 0); + + /* force enough events to trigger a SYN_DROPPED */ + for (i = 0; i < 100; i++) { + uinput_device_event(uidev, EV_ABS, ABS_X, 100 + i); + uinput_device_event(uidev, EV_ABS, ABS_Y, 500 + i); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 100 + i); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 500 + i); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + } + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + + /* trigger the new tracking ID after getting the SYN_DROPPED */ + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0); + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 5); + uinput_device_event(uidev, EV_ABS, ABS_X, 200); + uinput_device_event(uidev, EV_ABS, ABS_Y, 600); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 200); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 600); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + + slot = 0; + + /* Now sync the device, expect the data to be equal to the last event*/ + while ((rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev)) != -EAGAIN) { + if (ev.type == EV_SYN) + continue; + + ck_assert_int_eq(ev.type, EV_ABS); + switch(ev.code) { + case ABS_MT_SLOT: + slot = ev.value; + break; + case ABS_MT_TRACKING_ID: + if (slot == 0) + ck_assert_int_eq(ev.value, 5); + break; + case ABS_X: + case ABS_MT_POSITION_X: + ck_assert_int_eq(ev.value, 200); + break; + case ABS_Y: + case ABS_MT_POSITION_Y: + ck_assert_int_eq(ev.value, 600); + break; + } + } + + /* Drop the tracking ID */ + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0); + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, -1); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + + while ((rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev)) != -EAGAIN) { + ck_assert_int_ne(rc, LIBEVDEV_READ_STATUS_SYNC); + + if (ev.type == EV_SYN) + continue; + + ck_assert_int_eq(ev.type, EV_ABS); + + switch(ev.code) { + case ABS_MT_SLOT: + ck_assert_int_eq(ev.value, 0); + break; + case ABS_MT_TRACKING_ID: + ck_assert_int_eq(ev.value, -1); + break; + } + } + + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_syn_delta_fake_mt) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + struct input_absinfo abs[] = { { ABS_X, 0, 1000 }, + { ABS_Y, 0, 1000 }, + { ABS_MT_POSITION_X, 0, 1000 }, + { ABS_MT_POSITION_Y, 0, 1000 }, + { ABS_MT_SLOT - 1, 0, 2 }}; + /* don't set ABS_MT_SLOT here, otherwise uinput will init + * slots and the behavior is different to real devices with + * such events */ + unsigned long received[NLONGS(ABS_CNT)] = {0}; + + test_create_abs_device(&uidev, &dev, 5, abs, + -1); + /* first set of events */ + uinput_device_event(uidev, EV_ABS, ABS_X, 200); + uinput_device_event(uidev, EV_ABS, ABS_Y, 400); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 100); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 500); + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT - 1, 1); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + + /* second set of events */ + uinput_device_event(uidev, EV_ABS, ABS_X, 201); + uinput_device_event(uidev, EV_ABS, ABS_Y, 401); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 101); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 501); + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT - 1, 2); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + + while ((rc = libevdev_next_event(dev, LIBEVDEV_READ_STATUS_SYNC, &ev)) != -EAGAIN) { + if (ev.type != EV_ABS) + continue; + + ck_assert(!bit_is_set(received, ev.code)); + + switch(ev.code) { + /* see comment below for ABS_MT_POSITION_X + * and ABS_MT_POSITION_Y */ + case ABS_MT_POSITION_X: + case ABS_MT_POSITION_Y: + ck_abort(); + break; + + case ABS_MT_SLOT - 1: ck_assert_int_eq(ev.value, 2); break; + case ABS_X: ck_assert_int_eq(ev.value, 201); break; + case ABS_Y: ck_assert_int_eq(ev.value, 401); break; + default: + ck_abort(); + } + + set_bit(received, ev.code); + } + + /* Dont' expect ABS_MT values, they are ignored during the sync + * process */ + ck_assert(!bit_is_set(received, ABS_MT_POSITION_X)); + ck_assert(!bit_is_set(received, ABS_MT_POSITION_Y)); + ck_assert(bit_is_set(received, ABS_MT_SLOT - 1)); + ck_assert(bit_is_set(received, ABS_X)); + ck_assert(bit_is_set(received, ABS_Y)); + + ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_X), 201); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_Y), 401); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_MT_SLOT - 1), 2); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_skipped_sync) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + struct input_absinfo abs[2]; + + memset(abs, 0, sizeof(abs)); + abs[0].value = ABS_X; + abs[0].maximum = 1000; + + abs[1].value = ABS_Y; + abs[1].maximum = 1000; + + test_create_abs_device(&uidev, &dev, + 2, abs, + EV_SYN, SYN_REPORT, + EV_SYN, SYN_DROPPED, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_RIGHT, + -1); + + uinput_device_event(uidev, EV_KEY, BTN_LEFT, 1); + uinput_device_event(uidev, EV_ABS, ABS_X, 100); + uinput_device_event(uidev, EV_ABS, ABS_Y, 500); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, -EAGAIN); + + ck_assert_int_eq(libevdev_get_event_value(dev, EV_KEY, BTN_LEFT), 1); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_X), 100); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_Y), 500); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_incomplete_sync) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + struct input_absinfo abs[2]; + + memset(abs, 0, sizeof(abs)); + abs[0].value = ABS_X; + abs[0].maximum = 1000; + + abs[1].value = ABS_Y; + abs[1].maximum = 1000; + + test_create_abs_device(&uidev, &dev, + 2, abs, + EV_SYN, SYN_REPORT, + EV_SYN, SYN_DROPPED, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_RIGHT, + -1); + + uinput_device_event(uidev, EV_KEY, BTN_LEFT, 1); + uinput_device_event(uidev, EV_ABS, ABS_X, 100); + uinput_device_event(uidev, EV_ABS, ABS_Y, 500); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + ck_assert_int_eq(ev.type, EV_KEY); + ck_assert_int_eq(ev.code, BTN_LEFT); + ck_assert_int_eq(ev.value, 1); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, -EAGAIN); + + ck_assert_int_eq(libevdev_get_event_value(dev, EV_KEY, BTN_LEFT), 1); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_X), 100); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_Y), 500); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_empty_sync) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + + test_create_device(&uidev, &dev, + EV_SYN, SYN_REPORT, + EV_SYN, SYN_DROPPED, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_RIGHT, + -1); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + ck_assert_int_eq(rc, -EAGAIN); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_event_values) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + struct input_absinfo abs[2]; + int value; + + memset(abs, 0, sizeof(abs)); + abs[0].value = ABS_X; + abs[0].maximum = 1000; + + abs[1].value = ABS_Y; + abs[1].maximum = 1000; + + test_create_abs_device(&uidev, &dev, + 2, abs, + EV_SYN, SYN_REPORT, + EV_SYN, SYN_DROPPED, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_RIGHT, + -1); + + uinput_device_event(uidev, EV_KEY, BTN_LEFT, 1); + uinput_device_event(uidev, EV_ABS, ABS_X, 100); + uinput_device_event(uidev, EV_ABS, ABS_Y, 500); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + + /* must still be on old values */ + ck_assert_int_eq(libevdev_get_event_value(dev, EV_KEY, BTN_LEFT), 0); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_X), 0); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_Y), 0); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_REL, REL_X), 0); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_REL, REL_Y), 0); + + ck_assert_int_eq(libevdev_fetch_event_value(dev, EV_KEY, BTN_LEFT, &value), 1); + ck_assert_int_eq(value, 0); + + do { + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + } while (rc == 0); + ck_assert_int_eq(rc, -EAGAIN); + + ck_assert_int_eq(libevdev_get_event_value(dev, EV_KEY, BTN_LEFT), 1); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_X), 100); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_Y), 500); + + /* always 0 */ + ck_assert_int_eq(libevdev_get_event_value(dev, EV_REL, REL_X), 0); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_REL, REL_Y), 0); + + ck_assert_int_eq(libevdev_fetch_event_value(dev, EV_KEY, BTN_LEFT, &value), 1); + ck_assert_int_eq(value, 1); + ck_assert_int_eq(libevdev_fetch_event_value(dev, EV_ABS, ABS_X, &value), 1); + ck_assert_int_eq(value, 100); + ck_assert_int_eq(libevdev_fetch_event_value(dev, EV_ABS, ABS_Y, &value), 1); + ck_assert_int_eq(value, 500); + + uinput_device_free(uidev); + libevdev_free(dev); + +} +END_TEST + +START_TEST(test_event_values_invalid) +{ + struct uinput_device* uidev; + struct libevdev *dev; + struct input_absinfo abs[2]; + int value; + + memset(abs, 0, sizeof(abs)); + abs[0].value = ABS_X; + abs[0].maximum = 1000; + + abs[1].value = ABS_Y; + abs[1].maximum = 1000; + + test_create_abs_device(&uidev, &dev, + 2, abs, + EV_SYN, SYN_REPORT, + EV_SYN, SYN_DROPPED, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_RIGHT, + -1); + + ck_assert_int_eq(libevdev_get_event_value(dev, EV_KEY, BTN_EXTRA), 0); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_Z), 0); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_REL, REL_Z), 0); + + value = 0xab; + ck_assert_int_eq(libevdev_fetch_event_value(dev, EV_KEY, BTN_EXTRA, &value), 0); + ck_assert_int_eq(value, 0xab); + ck_assert_int_eq(libevdev_fetch_event_value(dev, EV_ABS, ABS_Z, &value), 0); + ck_assert_int_eq(value, 0xab); + ck_assert_int_eq(libevdev_fetch_event_value(dev, EV_REL, REL_Z, &value), 0); + ck_assert_int_eq(value, 0xab); + + + uinput_device_free(uidev); + libevdev_free(dev); + +} +END_TEST + +START_TEST(test_mt_event_values) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + struct input_absinfo abs[5]; + int value; + + memset(abs, 0, sizeof(abs)); + abs[0].value = ABS_X; + abs[0].maximum = 1000; + abs[1].value = ABS_MT_POSITION_X; + abs[1].maximum = 1000; + + abs[2].value = ABS_Y; + abs[2].maximum = 1000; + abs[3].value = ABS_MT_POSITION_Y; + abs[3].maximum = 1000; + + abs[4].value = ABS_MT_SLOT; + abs[4].maximum = 2; + + test_create_abs_device(&uidev, &dev, + 5, abs, + EV_SYN, SYN_REPORT, + -1); + + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0); + uinput_device_event(uidev, EV_ABS, ABS_X, 100); + uinput_device_event(uidev, EV_ABS, ABS_Y, 500); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 100); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 500); + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 1); + uinput_device_event(uidev, EV_ABS, ABS_X, 1); + uinput_device_event(uidev, EV_ABS, ABS_Y, 5); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 1); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 5); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + + /* must still be on old values */ + ck_assert_int_eq(libevdev_get_current_slot(dev), 0); + ck_assert_int_eq(libevdev_get_slot_value(dev, 0, ABS_MT_POSITION_X), 0); + ck_assert_int_eq(libevdev_get_slot_value(dev, 0, ABS_MT_POSITION_Y), 0); + ck_assert_int_eq(libevdev_get_slot_value(dev, 1, ABS_MT_POSITION_X), 0); + ck_assert_int_eq(libevdev_get_slot_value(dev, 1, ABS_MT_POSITION_Y), 0); + + do { + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + } while (rc == LIBEVDEV_READ_STATUS_SUCCESS); + ck_assert_int_eq(rc, -EAGAIN); + + ck_assert_int_eq(libevdev_get_current_slot(dev), 1); + ck_assert_int_eq(libevdev_get_slot_value(dev, 0, ABS_MT_POSITION_X), 100); + ck_assert_int_eq(libevdev_get_slot_value(dev, 0, ABS_MT_POSITION_Y), 500); + ck_assert_int_eq(libevdev_get_slot_value(dev, 1, ABS_MT_POSITION_X), 1); + ck_assert_int_eq(libevdev_get_slot_value(dev, 1, ABS_MT_POSITION_Y), 5); + + ck_assert_int_eq(libevdev_fetch_slot_value(dev, 0, ABS_MT_POSITION_X, &value), 1); + ck_assert_int_eq(value, 100); + ck_assert_int_eq(libevdev_fetch_slot_value(dev, 0, ABS_MT_POSITION_Y, &value), 1); + ck_assert_int_eq(value, 500); + ck_assert_int_eq(libevdev_fetch_slot_value(dev, 1, ABS_MT_POSITION_X, &value), 1); + ck_assert_int_eq(value, 1); + ck_assert_int_eq(libevdev_fetch_slot_value(dev, 1, ABS_MT_POSITION_Y, &value), 1); + ck_assert_int_eq(value, 5); + + uinput_device_free(uidev); + libevdev_free(dev); + +} +END_TEST + +START_TEST(test_mt_event_values_invalid) +{ + struct uinput_device* uidev; + struct libevdev *dev; + struct input_absinfo abs[5]; + int value; + + memset(abs, 0, sizeof(abs)); + abs[0].value = ABS_X; + abs[0].maximum = 1000; + abs[1].value = ABS_MT_POSITION_X; + abs[1].maximum = 1000; + + abs[2].value = ABS_Y; + abs[2].maximum = 1000; + abs[3].value = ABS_MT_POSITION_Y; + abs[3].maximum = 1000; + + abs[4].value = ABS_MT_SLOT; + abs[4].maximum = 2; + + test_create_abs_device(&uidev, &dev, + 5, abs, + EV_SYN, SYN_REPORT, + -1); + + ck_assert_int_eq(libevdev_get_current_slot(dev), 0); + ck_assert_int_eq(libevdev_get_slot_value(dev, 0, ABS_MT_TOUCH_MINOR), 0); + value = 0xab; + ck_assert_int_eq(libevdev_fetch_slot_value(dev, 0, ABS_MT_TOUCH_MINOR, &value), 0); + ck_assert_int_eq(value, 0xab); + + ck_assert_int_eq(libevdev_get_slot_value(dev, 10, ABS_MT_POSITION_X), 0); + ck_assert_int_eq(libevdev_get_slot_value(dev, 0, ABS_X), 0); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_mt_slot_ranges_invalid) +{ + struct uinput_device* uidev; + struct libevdev *dev; + struct input_event ev[2]; + int rc; + struct input_absinfo abs[5]; + int num_slots = 2; + int pipefd[2]; + + memset(abs, 0, sizeof(abs)); + abs[0].value = ABS_X; + abs[0].maximum = 1000; + abs[1].value = ABS_MT_POSITION_X; + abs[1].maximum = 1000; + + abs[2].value = ABS_Y; + abs[2].maximum = 1000; + abs[3].value = ABS_MT_POSITION_Y; + abs[3].maximum = 1000; + + abs[4].value = ABS_MT_SLOT; + abs[4].maximum = num_slots - 1; + + test_create_abs_device(&uidev, &dev, + 5, abs, + EV_SYN, SYN_REPORT, + -1); + + rc = pipe2(pipefd, O_NONBLOCK); + ck_assert_int_eq(rc, 0); + libevdev_change_fd(dev, pipefd[0]); + + memset(ev, 0, sizeof(ev)); + ev[0].type = EV_ABS; + ev[0].code = ABS_MT_SLOT; + ev[0].value = num_slots; + ev[1].type = EV_SYN; + ev[1].code = SYN_REPORT; + ev[1].value = 0; + rc = write(pipefd[1], ev, sizeof(ev)); + ck_assert_int_eq(rc, sizeof(ev)); + + libevdev_set_log_function(test_logfunc_ignore_error, NULL); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, ev); + ck_assert(libevdev_event_is_code(ev, EV_ABS, ABS_MT_SLOT)); + ck_assert_int_eq(ev[0].value, num_slots - 1); + + /* drain the EV_SYN */ + libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, ev); + + ev[0].type = EV_ABS; + ev[0].code = ABS_MT_SLOT; + ev[0].value = -1; + ev[1].type = EV_SYN; + ev[1].code = SYN_REPORT; + ev[1].value = 0; + rc = write(pipefd[1], ev, sizeof(ev)); + ck_assert_int_eq(rc, sizeof(ev)); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, ev); + ck_assert(libevdev_event_is_code(ev, EV_ABS, ABS_MT_SLOT)); + ck_assert_int_eq(ev[0].value, num_slots - 1); + + ck_assert_int_eq(libevdev_get_current_slot(dev), num_slots - 1); + + ck_assert_int_eq(libevdev_set_event_value(dev, EV_ABS, ABS_MT_SLOT, num_slots), -1); + ck_assert_int_eq(libevdev_set_event_value(dev, EV_ABS, ABS_MT_SLOT, -1), -1); + + libevdev_set_log_function(test_logfunc_abort_on_error, NULL); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_mt_tracking_id_discard) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + struct input_absinfo abs[6]; + + memset(abs, 0, sizeof(abs)); + abs[0].value = ABS_X; + abs[0].maximum = 1000; + abs[1].value = ABS_MT_POSITION_X; + abs[1].maximum = 1000; + + abs[2].value = ABS_Y; + abs[2].maximum = 1000; + abs[3].value = ABS_MT_POSITION_Y; + abs[3].maximum = 1000; + + abs[4].value = ABS_MT_SLOT; + abs[4].maximum = 10; + abs[5].value = ABS_MT_TRACKING_ID; + abs[5].maximum = 500; + + rc = test_create_abs_device(&uidev, &dev, + 6, abs, + EV_SYN, SYN_REPORT, + -1); + + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 1); + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 1); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + + /* second tracking ID on same slot */ + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 2); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + + libevdev_set_log_function(test_logfunc_ignore_error, NULL); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); + ck_assert_int_eq(ev.type, EV_ABS); + ck_assert_int_eq(ev.code, ABS_MT_SLOT); + ck_assert_int_eq(ev.value, 1); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); + ck_assert_int_eq(ev.type, EV_ABS); + ck_assert_int_eq(ev.code, ABS_MT_TRACKING_ID); + ck_assert_int_eq(ev.value, 1); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); + ck_assert_int_eq(ev.type, EV_SYN); + ck_assert_int_eq(ev.code, SYN_REPORT); + ck_assert_int_eq(ev.value, 0); + + /* expect tracking ID discarded */ + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); + ck_assert_int_eq(ev.type, EV_SYN); + ck_assert_int_eq(ev.code, SYN_REPORT); + ck_assert_int_eq(ev.value, 0); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, -EAGAIN); + + libevdev_set_log_function(test_logfunc_abort_on_error, NULL); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_mt_tracking_id_discard_neg_1) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev; + struct input_absinfo abs[6]; + int pipefd[2]; + struct input_event events[] = { + { .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = -1 }, + { .type = EV_SYN, .code = SYN_REPORT, .value = 0 }, + }; + + rc = pipe2(pipefd, O_NONBLOCK); + ck_assert_int_eq(rc, 0); + + memset(abs, 0, sizeof(abs)); + abs[0].value = ABS_X; + abs[0].maximum = 1000; + abs[1].value = ABS_MT_POSITION_X; + abs[1].maximum = 1000; + + abs[2].value = ABS_Y; + abs[2].maximum = 1000; + abs[3].value = ABS_MT_POSITION_Y; + abs[3].maximum = 1000; + + abs[4].value = ABS_MT_SLOT; + abs[4].maximum = 10; + abs[5].value = ABS_MT_TRACKING_ID; + abs[5].maximum = 500; + + rc = test_create_abs_device(&uidev, &dev, + 6, abs, + EV_SYN, SYN_REPORT, + -1); + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 1); + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 1); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + + while (libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev) != -EAGAIN) + ; + + libevdev_set_log_function(test_logfunc_ignore_error, NULL); + + /* two -1 tracking ids, need to use the pipe here, the kernel will + filter it otherwise */ + libevdev_change_fd(dev, pipefd[0]); + + rc = write(pipefd[1], events, sizeof(events)); + ck_assert_int_eq(rc, sizeof(events)); + rc = write(pipefd[1], events, sizeof(events)); + ck_assert_int_eq(rc, sizeof(events)); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); + ck_assert_int_eq(ev.type, EV_ABS); + ck_assert_int_eq(ev.code, ABS_MT_TRACKING_ID); + ck_assert_int_eq(ev.value, -1); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); + ck_assert_int_eq(ev.type, EV_SYN); + ck_assert_int_eq(ev.code, SYN_REPORT); + ck_assert_int_eq(ev.value, 0); + + /* expect second tracking ID discarded */ + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); + ck_assert_int_eq(ev.type, EV_SYN); + ck_assert_int_eq(ev.code, SYN_REPORT); + ck_assert_int_eq(ev.value, 0); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + ck_assert_int_eq(rc, -EAGAIN); + + libevdev_set_log_function(test_logfunc_abort_on_error, NULL); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_ev_rep_values) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int delay = 500, period = 200; + test_create_device(&uidev, &dev, + EV_KEY, BTN_LEFT, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_SYN, SYN_REPORT, + -1); + + libevdev_enable_event_code(dev, EV_REP, REP_DELAY, &delay); + libevdev_enable_event_code(dev, EV_REP, REP_PERIOD, &period); + + ck_assert_int_eq(libevdev_has_event_type(dev, EV_REP), 1); + ck_assert_int_eq(libevdev_has_event_code(dev, EV_REP, REP_DELAY), 1); + ck_assert_int_eq(libevdev_has_event_code(dev, EV_REP, REP_PERIOD), 1); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_REP, REP_DELAY), 500); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_REP, REP_PERIOD), 200); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_event_value_setters) +{ + struct uinput_device* uidev; + struct libevdev *dev; + struct input_absinfo abs[2]; + + memset(abs, 0, sizeof(abs)); + abs[0].value = ABS_X; + abs[0].maximum = 1000; + + abs[1].value = ABS_Y; + abs[1].maximum = 1000; + + test_create_abs_device(&uidev, &dev, + 2, abs, + EV_SYN, SYN_REPORT, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_RIGHT, + EV_LED, LED_NUML, + EV_LED, LED_CAPSL, + EV_SW, SW_LID, + EV_SW, SW_TABLET_MODE, + -1); + + ck_assert_int_eq(libevdev_get_event_value(dev, EV_KEY, BTN_LEFT), 0); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_X), 0); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_Y), 0); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_REL, REL_X), 0); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_REL, REL_Y), 0); + + ck_assert_int_eq(libevdev_set_event_value(dev, EV_KEY, BTN_LEFT, 1), 0); + ck_assert_int_eq(libevdev_set_event_value(dev, EV_KEY, BTN_RIGHT, 1), 0); + + ck_assert_int_eq(libevdev_get_event_value(dev, EV_KEY, BTN_LEFT), 1); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_KEY, BTN_RIGHT), 1); + + ck_assert_int_eq(libevdev_set_event_value(dev, EV_ABS, ABS_X, 10), 0); + ck_assert_int_eq(libevdev_set_event_value(dev, EV_ABS, ABS_Y, 20), 0); + + ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_X), 10); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_Y), 20); + + ck_assert_int_eq(libevdev_set_event_value(dev, EV_LED, LED_NUML, 1), 0); + ck_assert_int_eq(libevdev_set_event_value(dev, EV_LED, LED_CAPSL, 1), 0); + + ck_assert_int_eq(libevdev_get_event_value(dev, EV_LED, LED_NUML), 1); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_LED, LED_CAPSL), 1); + + ck_assert_int_eq(libevdev_set_event_value(dev, EV_SW, SW_LID, 1), 0); + ck_assert_int_eq(libevdev_set_event_value(dev, EV_SW, SW_TABLET_MODE, 1), 0); + + ck_assert_int_eq(libevdev_get_event_value(dev, EV_SW, SW_LID), 1); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_SW, SW_TABLET_MODE), 1); + + uinput_device_free(uidev); + libevdev_free(dev); + +} +END_TEST + +START_TEST(test_event_value_setters_invalid) +{ + struct uinput_device* uidev; + struct libevdev *dev; + struct input_absinfo abs[2]; + + memset(abs, 0, sizeof(abs)); + abs[0].value = ABS_X; + abs[0].maximum = 1000; + + abs[1].value = ABS_Y; + abs[1].maximum = 1000; + + test_create_abs_device(&uidev, &dev, + 2, abs, + EV_SYN, SYN_REPORT, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_RIGHT, + -1); + + ck_assert_int_eq(libevdev_set_event_value(dev, EV_REL, REL_X, 1), -1); + ck_assert_int_eq(libevdev_set_event_value(dev, EV_SW, SW_DOCK, 1), -1); + ck_assert_int_eq(libevdev_set_event_value(dev, EV_ABS, ABS_Z, 1), -1); + ck_assert_int_eq(libevdev_set_event_value(dev, EV_MAX + 1, 0, 1), -1); + ck_assert_int_eq(libevdev_set_event_value(dev, EV_SYN, SYN_REPORT, 0), -1); + + uinput_device_free(uidev); + libevdev_free(dev); + +} +END_TEST + +START_TEST(test_event_mt_value_setters) +{ + struct uinput_device* uidev; + struct libevdev *dev; + struct input_absinfo abs[5]; + + memset(abs, 0, sizeof(abs)); + abs[0].value = ABS_X; + abs[0].maximum = 1000; + abs[1].value = ABS_MT_POSITION_X; + abs[1].maximum = 1000; + + abs[2].value = ABS_Y; + abs[2].maximum = 1000; + abs[3].value = ABS_MT_POSITION_Y; + abs[3].maximum = 1000; + + abs[4].value = ABS_MT_SLOT; + abs[4].maximum = 2; + + test_create_abs_device(&uidev, &dev, + 5, abs, + EV_SYN, SYN_REPORT, + -1); + + ck_assert_int_eq(libevdev_set_slot_value(dev, 1, ABS_MT_POSITION_X, 1), 0); + ck_assert_int_eq(libevdev_set_slot_value(dev, 1, ABS_MT_POSITION_Y, 2), 0); + ck_assert_int_eq(libevdev_set_slot_value(dev, 0, ABS_MT_POSITION_X, 3), 0); + ck_assert_int_eq(libevdev_set_slot_value(dev, 0, ABS_MT_POSITION_Y, 4), 0); + + ck_assert_int_eq(libevdev_get_slot_value(dev, 1, ABS_MT_POSITION_X), 1); + ck_assert_int_eq(libevdev_get_slot_value(dev, 1, ABS_MT_POSITION_Y), 2); + ck_assert_int_eq(libevdev_get_slot_value(dev, 0, ABS_MT_POSITION_X), 3); + ck_assert_int_eq(libevdev_get_slot_value(dev, 0, ABS_MT_POSITION_Y), 4); + + ck_assert_int_eq(libevdev_set_slot_value(dev, 1, ABS_MT_SLOT, 1), 0); + ck_assert_int_eq(libevdev_get_slot_value(dev, 1, ABS_MT_SLOT), 1); + ck_assert_int_eq(libevdev_get_current_slot(dev), 1); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_event_mt_value_setters_invalid) +{ + struct uinput_device* uidev; + struct libevdev *dev; + struct input_absinfo abs[5]; + + memset(abs, 0, sizeof(abs)); + abs[0].value = ABS_X; + abs[0].maximum = 1000; + abs[1].value = ABS_MT_POSITION_X; + abs[1].maximum = 1000; + + abs[2].value = ABS_Y; + abs[2].maximum = 1000; + abs[3].value = ABS_MT_POSITION_Y; + abs[3].maximum = 1000; + + abs[4].value = ABS_MT_SLOT; + abs[4].maximum = 2; + + test_create_abs_device(&uidev, &dev, + 5, abs, + EV_SYN, SYN_REPORT, + -1); + + /* invalid axis */ + ck_assert_int_eq(libevdev_set_slot_value(dev, 1, ABS_Z, 1), -1); + /* valid, but non-mt axis */ + ck_assert_int_eq(libevdev_set_slot_value(dev, 1, ABS_X, 1), -1); + /* invalid mt axis */ + ck_assert_int_eq(libevdev_set_slot_value(dev, 1, ABS_MT_PRESSURE, 1), -1); + /* invalid slot no */ + ck_assert_int_eq(libevdev_set_slot_value(dev, 4, ABS_X, 1), -1); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_event_mt_value_setters_current_slot) +{ + struct uinput_device* uidev; + struct libevdev *dev; + struct input_absinfo abs[5]; + + memset(abs, 0, sizeof(abs)); + abs[0].value = ABS_X; + abs[0].maximum = 1000; + abs[1].value = ABS_MT_POSITION_X; + abs[1].maximum = 1000; + + abs[2].value = ABS_Y; + abs[2].maximum = 1000; + abs[3].value = ABS_MT_POSITION_Y; + abs[3].maximum = 1000; + + abs[4].value = ABS_MT_SLOT; + abs[4].maximum = 2; + + test_create_abs_device(&uidev, &dev, + 5, abs, + EV_SYN, SYN_REPORT, + -1); + + /* set_event_value/get_event_value works on the current slot */ + + ck_assert_int_eq(libevdev_get_current_slot(dev), 0); + ck_assert_int_eq(libevdev_set_event_value(dev, EV_ABS, ABS_MT_POSITION_X, 1), 0); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_MT_POSITION_X), 1); + ck_assert_int_eq(libevdev_get_slot_value(dev, 0, ABS_MT_POSITION_X), 1); + + ck_assert_int_eq(libevdev_set_event_value(dev, EV_ABS, ABS_MT_SLOT, 1), 0); + ck_assert_int_eq(libevdev_get_current_slot(dev), 1); + ck_assert_int_eq(libevdev_set_event_value(dev, EV_ABS, ABS_MT_POSITION_X, 2), 0); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_MT_POSITION_X), 2); + ck_assert_int_eq(libevdev_get_slot_value(dev, 1, ABS_MT_POSITION_X), 2); + + /* set slot 0, but current is still slot 1 */ + ck_assert_int_eq(libevdev_set_slot_value(dev, 0, ABS_MT_POSITION_X, 3), 0); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_MT_POSITION_X), 2); + + ck_assert_int_eq(libevdev_set_event_value(dev, EV_ABS, ABS_MT_SLOT, 0), 0); + ck_assert_int_eq(libevdev_get_current_slot(dev), 0); + ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_MT_POSITION_X), 3); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +Suite * +libevdev_events(void) +{ + Suite *s = suite_create("libevdev event tests"); + + TCase *tc = tcase_create("event polling"); + tcase_add_test(tc, test_next_event); + tcase_add_test(tc, test_syn_dropped_event); + tcase_add_test(tc, test_double_syn_dropped_event); + tcase_add_test(tc, test_event_type_filtered); + tcase_add_test(tc, test_event_code_filtered); + tcase_add_test(tc, test_has_event_pending); + suite_add_tcase(s, tc); + + tc = tcase_create("SYN_DROPPED deltas"); + tcase_add_test(tc, test_syn_delta_button); + tcase_add_test(tc, test_syn_delta_abs); + tcase_add_test(tc, test_syn_delta_mt); + tcase_add_test(tc, test_syn_delta_mt_reset_slot); + tcase_add_test(tc, test_syn_delta_led); + tcase_add_test(tc, test_syn_delta_sw); + tcase_add_test(tc, test_syn_delta_fake_mt); + tcase_add_test(tc, test_syn_delta_tracking_ids); + tcase_add_test(tc, test_syn_delta_late_sync); + suite_add_tcase(s, tc); + + tc = tcase_create("skipped syncs"); + tcase_add_test(tc, test_skipped_sync); + tcase_add_test(tc, test_incomplete_sync); + tcase_add_test(tc, test_empty_sync); + suite_add_tcase(s, tc); + + tc = tcase_create("event values"); + tcase_add_test(tc, test_event_values); + tcase_add_test(tc, test_event_values_invalid); + tcase_add_test(tc, test_mt_event_values); + tcase_add_test(tc, test_mt_event_values_invalid); + tcase_add_test(tc, test_mt_slot_ranges_invalid); + tcase_add_test(tc, test_mt_tracking_id_discard); + tcase_add_test(tc, test_mt_tracking_id_discard_neg_1); + tcase_add_test(tc, test_ev_rep_values); + suite_add_tcase(s, tc); + + tc = tcase_create("event value setters"); + tcase_add_test(tc, test_event_value_setters); + tcase_add_test(tc, test_event_value_setters_invalid); + tcase_add_test(tc, test_event_mt_value_setters); + tcase_add_test(tc, test_event_mt_value_setters_invalid); + tcase_add_test(tc, test_event_mt_value_setters_current_slot); + suite_add_tcase(s, tc); + + return s; +} + diff --git a/test/test-libevdev-has-event.c b/test/test-libevdev-has-event.c new file mode 100644 index 0000000..3ac41f1 --- /dev/null +++ b/test/test-libevdev-has-event.c @@ -0,0 +1,1166 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include "test-common.h" + +static int evbits[] = { + EV_SYN, EV_KEY, EV_REL, EV_ABS, EV_MSC, + EV_SW, EV_LED, EV_SND, EV_FF, + /* Intentionally skipping these, they're different + * EV_PWR, EV_FF_STATUS, EV_REP, */ + -1, +}; + +START_TEST(test_has_ev_bit) +{ + int *evbit = evbits; + + while(*evbit != -1) { + struct uinput_device* uidev; + struct libevdev *dev; + int i; + + if (*evbit == EV_ABS) { + struct input_absinfo abs = { ABS_X, 0, 2, 0, 0, 0}; + test_create_abs_device(&uidev, &dev, + 1, &abs, + -1); + } else + test_create_device(&uidev, &dev, + *evbit, 0, + -1); + + ck_assert_msg(libevdev_has_event_type(dev, EV_SYN), "for event type %d\n", *evbit); + ck_assert_msg(libevdev_has_event_type(dev, *evbit), "for event type %d\n", *evbit); + + for (i = 0; i <= EV_MAX; i++) { + if (i == EV_SYN || i == *evbit) + continue; + + ck_assert_msg(!libevdev_has_event_type(dev, i), "for event type %d\n", i); + } + + libevdev_free(dev); + uinput_device_free(uidev); + + evbit++; + } +} +END_TEST + +START_TEST(test_ev_bit_limits) +{ + int *evbit = evbits; + + while(*evbit != -1) { + struct uinput_device* uidev; + struct libevdev *dev; + + if (*evbit == EV_ABS) { + struct input_absinfo abs = { ABS_X, 0, 2, 0, 0, 0}; + test_create_abs_device(&uidev, &dev, + 1, &abs, + -1); + } else + test_create_device(&uidev, &dev, + *evbit, 0, + -1); + + ck_assert_int_eq(libevdev_has_event_type(dev, EV_MAX + 1), 0); + ck_assert_int_eq(libevdev_has_event_type(dev, INT_MAX), 0); + ck_assert_int_eq(libevdev_has_event_type(dev, UINT_MAX), 0); + + libevdev_free(dev); + uinput_device_free(uidev); + + evbit++; + } +} +END_TEST + +START_TEST(test_event_codes) +{ + int *evbit = evbits; + + while(*evbit != -1) { + struct uinput_device* uidev; + struct libevdev *dev; + int code, max; + if (*evbit == EV_SYN) { + evbit++; + continue; + } + + max = libevdev_event_type_get_max(*evbit); + + for (code = 1; code < max; code += 10) { + if (*evbit == EV_ABS) { + struct input_absinfo abs = { code, 0, 2, 0, 0, 0}; + test_create_abs_device(&uidev, &dev, + 1, &abs, + -1); + } else + test_create_device(&uidev, &dev, + *evbit, code, + -1); + + ck_assert_msg(libevdev_has_event_type(dev, *evbit), "for event type %d\n", *evbit); + ck_assert_msg(libevdev_has_event_code(dev, *evbit, code), "for type %d code %d", *evbit, code); + ck_assert_msg(libevdev_has_event_code(dev, EV_SYN, SYN_REPORT), "for EV_SYN"); + /* always false */ + ck_assert_msg(!libevdev_has_event_code(dev, EV_PWR, 0), "for EV_PWR"); + + libevdev_free(dev); + uinput_device_free(uidev); + } + + evbit++; + } +} +END_TEST + +START_TEST(test_event_code_limits) +{ + int *evbit = evbits; + + while(*evbit != -1) { + struct uinput_device* uidev; + struct libevdev *dev; + int max; + + if (*evbit == EV_SYN) { + evbit++; + continue; + } + + max = libevdev_event_type_get_max(*evbit); + ck_assert(max != -1); + + if (*evbit == EV_ABS) { + struct input_absinfo abs = { ABS_X, 0, 2, 0, 0, 0}; + test_create_abs_device(&uidev, &dev, + 1, &abs, + -1); + } else + test_create_device(&uidev, &dev, + *evbit, 1, + -1); + + ck_assert_msg(!libevdev_has_event_code(dev, *evbit, max), "for type %d code %d", *evbit, max); + ck_assert_msg(!libevdev_has_event_code(dev, *evbit, INT_MAX), "for type %d code %d", *evbit, INT_MAX); + ck_assert_msg(!libevdev_has_event_code(dev, *evbit, UINT_MAX), "for type %d code %d", *evbit, UINT_MAX); + + libevdev_free(dev); + uinput_device_free(uidev); + + evbit++; + } +} +END_TEST + +START_TEST(test_ev_rep) +{ + struct libevdev *dev; + struct uinput_device* uidev; + int rc; + int rep, delay; + const int KERNEL_DEFAULT_REP = 250; + const int KERNEL_DEFAULT_DELAY = 33; + + /* EV_REP is special, it's always fully set if set at all, + can't test this through uinput though */ + uidev = uinput_device_new(TEST_DEVICE_NAME); + ck_assert(uidev != NULL); + rc = uinput_device_set_bit(uidev, EV_REP); + ck_assert_int_eq(rc, 0); + + rc = uinput_device_create(uidev); + ck_assert_int_eq(rc, 0); + + rc = libevdev_new_from_fd(uinput_device_get_fd(uidev), &dev); + ck_assert_int_eq(rc, 0); + + ck_assert_int_eq(libevdev_has_event_type(dev, EV_REP), 1); + ck_assert_int_eq(libevdev_has_event_code(dev, EV_REP, REP_DELAY), 1); + ck_assert_int_eq(libevdev_has_event_code(dev, EV_REP, REP_PERIOD), 1); + + ck_assert_int_eq(libevdev_get_repeat(dev, &rep, &delay), 0); + /* default values as set by the kernel, + see drivers/input/input.c:input_register_device() */ + ck_assert_int_eq(rep, KERNEL_DEFAULT_REP); + ck_assert_int_eq(delay, KERNEL_DEFAULT_DELAY); + + libevdev_free(dev); + uinput_device_free(uidev); +} +END_TEST + +START_TEST(test_ev_rep_values) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int delay = 0xab, period = 0xbc; + + /* EV_REP is special, it's always fully set if set at all, can't set + it through uinput though. */ + test_create_device(&uidev, &dev, -1); + + ck_assert_int_eq(libevdev_get_repeat(dev, NULL, NULL), -1); + ck_assert_int_eq(libevdev_get_repeat(dev, &delay, NULL), -1); + ck_assert_int_eq(libevdev_get_repeat(dev, NULL, &period), -1); + ck_assert_int_eq(libevdev_get_repeat(dev, &delay, &period), -1); + + ck_assert_int_eq(delay, 0xab); + ck_assert_int_eq(period, 0xbc); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_input_props) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc, i; + struct input_absinfo abs = {0, 0, 2, 0, 0}; + + uidev = uinput_device_new(TEST_DEVICE_NAME); + rc = uinput_device_set_abs_bit(uidev, ABS_X, &abs); + ck_assert_int_eq(rc, 0); + uinput_device_set_prop(uidev, INPUT_PROP_DIRECT); + uinput_device_set_prop(uidev, INPUT_PROP_BUTTONPAD); + rc = uinput_device_create(uidev); + ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc)); + + rc = libevdev_new_from_fd(uinput_device_get_fd(uidev), &dev); + ck_assert_msg(rc == 0, "Failed to create device: %s", strerror(-rc)); + + + for (i = 0; i < INPUT_PROP_CNT; i++) { + if (i == INPUT_PROP_DIRECT || i == INPUT_PROP_BUTTONPAD) + ck_assert_int_eq(libevdev_has_property(dev, i), 1); + else + ck_assert_int_eq(libevdev_has_property(dev, i), 0); + } + + ck_assert_int_eq(libevdev_has_property(dev, INPUT_PROP_MAX + 1), 0); + ck_assert_int_eq(libevdev_has_property(dev, INPUT_PROP_MAX), 0); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_set_input_props) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc, fd; + struct input_absinfo abs = {0, 0, 2, 0, 0}; + + dev = libevdev_new(); + ck_assert_int_eq(libevdev_enable_property(dev, INPUT_PROP_MAX + 1), -1); + ck_assert_int_eq(libevdev_enable_property(dev, INPUT_PROP_DIRECT), 0); + ck_assert_int_eq(libevdev_enable_property(dev, INPUT_PROP_BUTTONPAD), 0); + ck_assert_int_eq(libevdev_has_property(dev, INPUT_PROP_DIRECT), 1); + ck_assert_int_eq(libevdev_has_property(dev, INPUT_PROP_BUTTONPAD), 1); + + uidev = uinput_device_new(TEST_DEVICE_NAME); + rc = uinput_device_set_abs_bit(uidev, ABS_X, &abs); + ck_assert_int_eq(rc, 0); + uinput_device_set_prop(uidev, INPUT_PROP_BUTTONPAD); + rc = uinput_device_create(uidev); + ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc)); + + fd = uinput_device_get_fd(uidev); + rc = libevdev_set_fd(dev, fd); + ck_assert_msg(rc == 0, "Failed to create device: %s", strerror(-rc)); + + ck_assert_int_eq(libevdev_has_property(dev, INPUT_PROP_DIRECT), 0); + ck_assert_int_eq(libevdev_has_property(dev, INPUT_PROP_BUTTONPAD), 1); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_slot_init_value) +{ + struct uinput_device *uidev; + struct libevdev *dev; + int rc; + const int nabs = 6; + int i; + int fd; + struct input_absinfo abs[] = { { ABS_X, 0, 1000 }, + { ABS_Y, 0, 1000 }, + { ABS_MT_POSITION_X, 0, 1000 }, + { ABS_MT_POSITION_Y, 0, 1000 }, + { ABS_MT_TRACKING_ID, -1, 2 }, + { ABS_MT_SLOT, 0, 1 }}; + + uidev = uinput_device_new(TEST_DEVICE_NAME); + + for (i = 0; i < nabs; i++) { + rc = uinput_device_set_abs_bit(uidev, abs[i].value, &abs[i]); + ck_assert_int_eq(rc, 0); + } + + rc = uinput_device_create(uidev); + ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc)); + + fd = uinput_device_get_fd(uidev); + rc = fcntl(fd, F_SETFL, O_NONBLOCK); + ck_assert_msg(rc == 0, "fcntl failed: %s", strerror(errno)); + + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0); + uinput_device_event(uidev, EV_ABS, ABS_X, 100); + uinput_device_event(uidev, EV_ABS, ABS_Y, 500); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 100); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 500); + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 1); + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 1); + uinput_device_event(uidev, EV_ABS, ABS_X, 1); + uinput_device_event(uidev, EV_ABS, ABS_Y, 5); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 1); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 5); + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 2); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + + rc = libevdev_new_from_fd(fd, &dev); + ck_assert_int_eq(rc, 0); + + ck_assert_int_eq(libevdev_get_current_slot(dev), 1); + ck_assert_int_eq(libevdev_get_slot_value(dev, 0, ABS_MT_POSITION_X), 100); + ck_assert_int_eq(libevdev_get_slot_value(dev, 0, ABS_MT_POSITION_Y), 500); + ck_assert_int_eq(libevdev_get_slot_value(dev, 1, ABS_MT_POSITION_X), 1); + ck_assert_int_eq(libevdev_get_slot_value(dev, 1, ABS_MT_POSITION_Y), 5); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_no_slots) +{ + struct uinput_device* uidev; + struct libevdev *dev; + struct input_absinfo abs[] = { { ABS_X, 0, 2 }, + { ABS_Y, 0, 2 }, + { ABS_MT_POSITION_X, 0, 2 }, + { ABS_MT_POSITION_Y, 0, 2 }}; + + test_create_abs_device(&uidev, &dev, 4, abs, + -1); + + ck_assert_int_eq(libevdev_get_num_slots(dev), -1); + ck_assert_int_eq(libevdev_get_current_slot(dev), -1); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_slot_number) +{ + struct uinput_device* uidev; + struct libevdev *dev; + const int nslots = 4; + struct input_absinfo abs[] = { { ABS_X, 0, 2 }, + { ABS_Y, 0, 2 }, + { ABS_MT_POSITION_X, 0, 2 }, + { ABS_MT_POSITION_Y, 0, 2 }, + { ABS_MT_SLOT, 0, nslots - 1 }}; + + test_create_abs_device(&uidev, &dev, 5, abs, + -1); + + ck_assert_int_eq(libevdev_get_num_slots(dev), nslots); + ck_assert_int_eq(libevdev_get_current_slot(dev), 0); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_invalid_mt_device) +{ + struct uinput_device* uidev; + struct libevdev *dev; + const int nslots = 4; + int value; + struct input_absinfo abs[] = { { ABS_X, 0, 2 }, + { ABS_Y, 0, 2 }, + { ABS_MT_POSITION_X, 0, 2 }, + { ABS_MT_POSITION_Y, 0, 2 }, + { ABS_MT_SLOT - 1, 0, 2 }, + { ABS_MT_SLOT, 0, nslots - 1 }}; + + test_create_abs_device(&uidev, &dev, 6, abs, + -1); + + ck_assert_int_eq(libevdev_get_num_slots(dev), -1); + ck_assert_int_eq(libevdev_get_current_slot(dev), -1); + ck_assert_int_eq(libevdev_set_slot_value(dev, 0, ABS_MT_POSITION_X, 0), -1); + ck_assert_int_eq(libevdev_fetch_slot_value(dev, 0, ABS_MT_POSITION_X, &value), 0); + + ck_assert(libevdev_has_event_code(dev, EV_ABS, ABS_MT_SLOT - 1)); + ck_assert(libevdev_has_event_code(dev, EV_ABS, ABS_MT_SLOT)); + + ck_assert_int_eq(libevdev_set_event_value(dev, EV_ABS, ABS_MT_SLOT, 1), 0); + ck_assert(libevdev_get_event_value(dev, EV_ABS, ABS_MT_SLOT) == 1); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_device_name) +{ + struct uinput_device* uidev; + struct libevdev *dev; + struct input_id ids = {1, 2, 3, 4}; + const char *str; + int rc; + + dev = libevdev_new(); + + str = libevdev_get_name(dev); + ck_assert(str != NULL); + ck_assert_int_eq(strlen(str), 0); + + rc = uinput_device_new_with_events(&uidev, TEST_DEVICE_NAME, &ids, + EV_REL, REL_X, + -1); + ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc)); + rc = libevdev_set_fd(dev, uinput_device_get_fd(uidev)); + ck_assert_msg(rc == 0, "Failed to init device: %s", strerror(-rc));; + + str = libevdev_get_name(dev); + ck_assert_int_eq(strcmp(str, TEST_DEVICE_NAME), 0); + + str = libevdev_get_phys(dev); + ck_assert(str == NULL); + + str = libevdev_get_uniq(dev); + ck_assert(str == NULL); + + ck_assert_int_eq(libevdev_get_id_bustype(dev), ids.bustype); + ck_assert_int_eq(libevdev_get_id_vendor(dev), ids.vendor); + ck_assert_int_eq(libevdev_get_id_product(dev), ids.product); + ck_assert_int_eq(libevdev_get_id_version(dev), ids.version); + ck_assert_int_eq(libevdev_get_driver_version(dev), EV_VERSION); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_device_set_name) +{ + struct uinput_device* uidev; + struct libevdev *dev; + struct input_id ids = {1, 2, 3, 4}; + const char *str; + int rc; + + dev = libevdev_new(); + + libevdev_set_name(dev, "the name"); + libevdev_set_phys(dev, "the phys"); + libevdev_set_uniq(dev, "the uniq"); + + str = libevdev_get_name(dev); + ck_assert(str != NULL); + ck_assert_int_eq(strcmp(str, "the name"), 0); + + str = libevdev_get_phys(dev); + ck_assert(str != NULL); + ck_assert_int_eq(strcmp(str, "the phys"), 0); + + str = libevdev_get_uniq(dev); + ck_assert(str != NULL); + ck_assert_int_eq(strcmp(str, "the uniq"), 0); + + rc = uinput_device_new_with_events(&uidev, TEST_DEVICE_NAME, &ids, + EV_REL, REL_X, + -1); + ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc)); + rc = libevdev_set_fd(dev, uinput_device_get_fd(uidev)); + ck_assert_msg(rc == 0, "Failed to init device: %s", strerror(-rc));; + + str = libevdev_get_name(dev); + ck_assert_int_eq(strcmp(str, TEST_DEVICE_NAME), 0); + + str = libevdev_get_phys(dev); + ck_assert(str == NULL); + + str = libevdev_get_uniq(dev); + ck_assert(str == NULL); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_device_set_ids) +{ + struct uinput_device* uidev; + struct libevdev *dev; + struct input_id ids = {1, 2, 3, 4}; + int rc; + + dev = libevdev_new(); + + libevdev_set_id_product(dev, 10); + libevdev_set_id_vendor(dev, 20); + libevdev_set_id_bustype(dev, 30); + libevdev_set_id_version(dev, 40); + + ck_assert_int_eq(libevdev_get_id_product(dev), 10); + ck_assert_int_eq(libevdev_get_id_vendor(dev), 20); + ck_assert_int_eq(libevdev_get_id_bustype(dev), 30); + ck_assert_int_eq(libevdev_get_id_version(dev), 40); + + rc = uinput_device_new_with_events(&uidev, TEST_DEVICE_NAME, &ids, + EV_REL, REL_X, + -1); + ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc)); + rc = libevdev_set_fd(dev, uinput_device_get_fd(uidev)); + ck_assert_msg(rc == 0, "Failed to init device: %s", strerror(-rc));; + + ck_assert_int_eq(libevdev_get_id_bustype(dev), ids.bustype); + ck_assert_int_eq(libevdev_get_id_vendor(dev), ids.vendor); + ck_assert_int_eq(libevdev_get_id_product(dev), ids.product); + ck_assert_int_eq(libevdev_get_id_version(dev), ids.version); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_device_get_abs_info) +{ + struct uinput_device* uidev; + struct libevdev *dev; + struct input_absinfo abs; + const struct input_absinfo *a; + int rc; + + uidev = uinput_device_new(TEST_DEVICE_NAME); + ck_assert(uidev != NULL); + + + abs.minimum = 0; + abs.maximum = 1000; + abs.fuzz = 1; + abs.flat = 2; + abs.resolution = 3; + abs.value = 0; + + uinput_device_set_abs_bit(uidev, ABS_X, &abs); + uinput_device_set_abs_bit(uidev, ABS_MT_POSITION_X, &abs); + + abs.minimum = -500; + abs.maximum = 500; + abs.fuzz = 10; + abs.flat = 20; + abs.resolution = 30; + abs.value = 0; + + uinput_device_set_abs_bit(uidev, ABS_Y, &abs); + uinput_device_set_abs_bit(uidev, ABS_MT_POSITION_Y, &abs); + + rc = uinput_device_create(uidev); + ck_assert_msg(rc == 0, "Failed to create device: %s", strerror(-rc)); + + rc = libevdev_new_from_fd(uinput_device_get_fd(uidev), &dev); + ck_assert_msg(rc == 0, "Failed to init device: %s", strerror(-rc));; + + ck_assert_int_eq(libevdev_get_abs_minimum(dev, ABS_MAX + 1), 0); + ck_assert_int_eq(libevdev_get_abs_maximum(dev, ABS_MAX + 1), 0); + ck_assert_int_eq(libevdev_get_abs_fuzz(dev, ABS_MAX + 1), 0); + ck_assert_int_eq(libevdev_get_abs_flat(dev, ABS_MAX + 1), 0); + ck_assert_int_eq(libevdev_get_abs_resolution(dev, ABS_MAX + 1), 0); + ck_assert(!libevdev_get_abs_info(dev, ABS_MAX + 1)); + + ck_assert_int_eq(libevdev_get_abs_minimum(dev, ABS_X), 0); + ck_assert_int_eq(libevdev_get_abs_maximum(dev, ABS_X), 1000); + ck_assert_int_eq(libevdev_get_abs_fuzz(dev, ABS_X), 1); + ck_assert_int_eq(libevdev_get_abs_flat(dev, ABS_X), 2); + ck_assert_int_eq(libevdev_get_abs_resolution(dev, ABS_X), 3); + a = libevdev_get_abs_info(dev, ABS_X); + ck_assert(a != NULL); + ck_assert_int_eq(a->minimum, 0); + ck_assert_int_eq(a->maximum, 1000); + ck_assert_int_eq(a->fuzz, 1); + ck_assert_int_eq(a->flat, 2); + ck_assert_int_eq(a->resolution, 3); + + ck_assert_int_eq(libevdev_get_abs_minimum(dev, ABS_MT_POSITION_X), 0); + ck_assert_int_eq(libevdev_get_abs_maximum(dev, ABS_MT_POSITION_X), 1000); + ck_assert_int_eq(libevdev_get_abs_fuzz(dev, ABS_MT_POSITION_X), 1); + ck_assert_int_eq(libevdev_get_abs_flat(dev, ABS_MT_POSITION_X), 2); + ck_assert_int_eq(libevdev_get_abs_resolution(dev, ABS_MT_POSITION_X), 3); + a = libevdev_get_abs_info(dev, ABS_MT_POSITION_X); + ck_assert(a != NULL); + ck_assert_int_eq(a->minimum, 0); + ck_assert_int_eq(a->maximum, 1000); + ck_assert_int_eq(a->fuzz, 1); + ck_assert_int_eq(a->flat, 2); + ck_assert_int_eq(a->resolution, 3); + + ck_assert_int_eq(libevdev_get_abs_minimum(dev, ABS_Y), -500); + ck_assert_int_eq(libevdev_get_abs_maximum(dev, ABS_Y), 500); + ck_assert_int_eq(libevdev_get_abs_fuzz(dev, ABS_Y), 10); + ck_assert_int_eq(libevdev_get_abs_flat(dev, ABS_Y), 20); + ck_assert_int_eq(libevdev_get_abs_resolution(dev, ABS_Y), 30); + a = libevdev_get_abs_info(dev, ABS_Y); + ck_assert(a != NULL); + ck_assert_int_eq(a->minimum, -500); + ck_assert_int_eq(a->maximum, 500); + ck_assert_int_eq(a->fuzz, 10); + ck_assert_int_eq(a->flat, 20); + ck_assert_int_eq(a->resolution, 30); + + ck_assert_int_eq(libevdev_get_abs_minimum(dev, ABS_MT_POSITION_Y), -500); + ck_assert_int_eq(libevdev_get_abs_maximum(dev, ABS_MT_POSITION_Y), 500); + ck_assert_int_eq(libevdev_get_abs_fuzz(dev, ABS_MT_POSITION_Y), 10); + ck_assert_int_eq(libevdev_get_abs_flat(dev, ABS_MT_POSITION_Y), 20); + ck_assert_int_eq(libevdev_get_abs_resolution(dev, ABS_MT_POSITION_Y), 30); + a = libevdev_get_abs_info(dev, ABS_MT_POSITION_Y); + ck_assert(a != NULL); + ck_assert_int_eq(a->minimum, -500); + ck_assert_int_eq(a->maximum, 500); + ck_assert_int_eq(a->fuzz, 10); + ck_assert_int_eq(a->flat, 20); + ck_assert_int_eq(a->resolution, 30); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_device_set_abs) +{ + struct uinput_device* uidev; + struct libevdev *dev; + struct input_absinfo abs[2]; + struct input_absinfo a; + + memset(abs, 0, sizeof(abs)); + abs[0].value = ABS_X; + abs[0].maximum = 1000; + + abs[1].value = ABS_Y; + abs[1].maximum = 1000; + + test_create_abs_device(&uidev, &dev, + 2, abs, + EV_SYN, + -1); + + libevdev_set_abs_minimum(dev, ABS_X, 1); + libevdev_set_abs_minimum(dev, ABS_Y, 5); + ck_assert_int_eq(libevdev_get_abs_minimum(dev, ABS_X), 1); + ck_assert_int_eq(libevdev_get_abs_minimum(dev, ABS_Y), 5); + + libevdev_set_abs_maximum(dev, ABS_X, 3000); + libevdev_set_abs_maximum(dev, ABS_Y, 5000); + ck_assert_int_eq(libevdev_get_abs_maximum(dev, ABS_X), 3000); + ck_assert_int_eq(libevdev_get_abs_maximum(dev, ABS_Y), 5000); + + libevdev_set_abs_fuzz(dev, ABS_X, 3); + libevdev_set_abs_fuzz(dev, ABS_Y, 5); + ck_assert_int_eq(libevdev_get_abs_fuzz(dev, ABS_X), 3); + ck_assert_int_eq(libevdev_get_abs_fuzz(dev, ABS_Y), 5); + + libevdev_set_abs_flat(dev, ABS_X, 8); + libevdev_set_abs_flat(dev, ABS_Y, 15); + ck_assert_int_eq(libevdev_get_abs_flat(dev, ABS_X), 8); + ck_assert_int_eq(libevdev_get_abs_flat(dev, ABS_Y), 15); + + libevdev_set_abs_resolution(dev, ABS_X, 80); + libevdev_set_abs_resolution(dev, ABS_Y, 150); + ck_assert_int_eq(libevdev_get_abs_resolution(dev, ABS_X), 80); + ck_assert_int_eq(libevdev_get_abs_resolution(dev, ABS_Y), 150); + + a.value = 0; + a.minimum = 10; + a.maximum = 100; + a.fuzz = 13; + a.flat = 1; + a.resolution = 16; + + libevdev_set_abs_info(dev, ABS_X, &a); + ck_assert_int_eq(memcmp(&a, libevdev_get_abs_info(dev, ABS_X), sizeof(a)), 0); + + libevdev_set_abs_minimum(dev, ABS_Z, 10); + ck_assert_int_eq(libevdev_has_event_code(dev, EV_ABS, ABS_Z), 0); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_device_enable_bit) +{ + struct uinput_device* uidev; + struct libevdev *dev, *dev2; + struct input_absinfo abs = {ABS_X, 0, 2}; + int rc; + + test_create_abs_device(&uidev, &dev, 1, &abs, + -1); + + ck_assert(!libevdev_has_event_code(dev, EV_ABS, ABS_Y)); + ck_assert(!libevdev_has_event_type(dev, EV_REL)); + ck_assert(!libevdev_has_event_code(dev, EV_REL, REL_X)); + + abs.minimum = 0; + abs.maximum = 100; + abs.fuzz = 1; + abs.flat = 2; + abs.resolution = 3; + + ck_assert_int_eq(libevdev_enable_event_code(dev, EV_ABS, ABS_Y, &abs), 0); + ck_assert(libevdev_has_event_code(dev, EV_ABS, ABS_Y)); + + ck_assert_int_eq(libevdev_enable_event_type(dev, EV_REL), 0); + ck_assert(libevdev_has_event_type(dev, EV_REL)); + ck_assert(!libevdev_has_event_code(dev, EV_REL, REL_X)); + + ck_assert_int_eq(libevdev_enable_event_code(dev, EV_REL, REL_X, NULL), 0); + ck_assert(libevdev_has_event_code(dev, EV_REL, REL_X)); + + /* make sure kernel device is unchanged */ + rc = libevdev_new_from_fd(uinput_device_get_fd(uidev), &dev2); + ck_assert_msg(rc == 0, "Failed to init device: %s", strerror(-rc)); + ck_assert(libevdev_has_event_code(dev2, EV_ABS, ABS_X)); + ck_assert(!libevdev_has_event_code(dev2, EV_ABS, ABS_Y)); + ck_assert(!libevdev_has_event_type(dev2, EV_REL)); + ck_assert(!libevdev_has_event_code(dev2, EV_REL, REL_X)); + libevdev_free(dev2); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_device_enable_bit_invalid) +{ + struct uinput_device* uidev; + struct libevdev *dev; + struct input_absinfo abs = {ABS_X, 0, 1}; + + test_create_abs_device(&uidev, &dev, 1, &abs, + -1); + + ck_assert_int_eq(libevdev_enable_event_code(dev, EV_ABS, ABS_MAX + 1, &abs), -1); + ck_assert_int_eq(libevdev_enable_event_code(dev, EV_MAX + 1, ABS_MAX + 1, &abs), -1); + ck_assert_int_eq(libevdev_enable_event_type(dev, EV_MAX + 1), -1); + /* there's a gap between EV_SW and EV_LED */ + ck_assert_int_eq(libevdev_enable_event_type(dev, EV_LED - 1), -1); + ck_assert_int_eq(libevdev_enable_event_code(dev, EV_LED - 1, 0, NULL), -1); + + ck_assert_int_eq(libevdev_enable_event_code(dev, EV_ABS, ABS_Y, NULL), -1); + ck_assert_int_eq(libevdev_enable_event_code(dev, EV_REP, REP_DELAY, NULL), -1); + ck_assert_int_eq(libevdev_enable_event_code(dev, EV_REL, REL_X, &abs), -1); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_device_disable_bit) +{ + struct uinput_device* uidev; + struct libevdev *dev, *dev2; + int rc; + struct input_absinfo abs[2] = {{ABS_X, 0, 1}, {ABS_Y, 0, 1}}; + + test_create_abs_device(&uidev, &dev, + 2, abs, + EV_REL, REL_X, + EV_REL, REL_Y, + -1); + + ck_assert(libevdev_has_event_code(dev, EV_ABS, ABS_X)); + ck_assert(libevdev_has_event_code(dev, EV_ABS, ABS_Y)); + ck_assert(libevdev_has_event_type(dev, EV_REL)); + ck_assert(libevdev_has_event_code(dev, EV_REL, REL_X)); + ck_assert(libevdev_has_event_code(dev, EV_REL, REL_Y)); + + ck_assert_int_eq(libevdev_disable_event_code(dev, EV_ABS, ABS_Y), 0); + ck_assert(!libevdev_has_event_code(dev, EV_ABS, ABS_Y)); + + ck_assert_int_eq(libevdev_disable_event_code(dev, EV_REL, REL_X), 0); + ck_assert(!libevdev_has_event_code(dev, EV_REL, REL_X)); + ck_assert(libevdev_has_event_code(dev, EV_REL, REL_Y)); + ck_assert(libevdev_has_event_type(dev, EV_REL)); + + ck_assert_int_eq(libevdev_disable_event_type(dev, EV_REL), 0); + ck_assert(!libevdev_has_event_type(dev, EV_REL)); + ck_assert(!libevdev_has_event_code(dev, EV_REL, REL_X)); + ck_assert(!libevdev_has_event_code(dev, EV_REL, REL_Y)); + + /* make sure kernel device is unchanged */ + rc = libevdev_new_from_fd(uinput_device_get_fd(uidev), &dev2); + ck_assert_msg(rc == 0, "Failed to init device: %s", strerror(-rc)); + ck_assert(libevdev_has_event_code(dev2, EV_ABS, ABS_X)); + ck_assert(libevdev_has_event_code(dev2, EV_ABS, ABS_Y)); + ck_assert(libevdev_has_event_type(dev2, EV_REL)); + ck_assert(libevdev_has_event_code(dev2, EV_REL, REL_X)); + ck_assert(libevdev_has_event_code(dev2, EV_REL, REL_Y)); + libevdev_free(dev2); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_device_disable_bit_invalid) +{ + struct uinput_device* uidev; + struct libevdev *dev; + struct input_absinfo abs = {ABS_X, 0, 1}; + + test_create_abs_device(&uidev, &dev, 1, &abs, -1); + + /* there's a gap between EV_SW and EV_LED */ + ck_assert_int_eq(libevdev_disable_event_type(dev, EV_LED - 1), -1); + ck_assert_int_eq(libevdev_disable_event_code(dev, EV_LED - 1, 0), -1); + ck_assert_int_eq(libevdev_disable_event_code(dev, EV_ABS, ABS_MAX + 1), -1); + ck_assert_int_eq(libevdev_disable_event_code(dev, EV_MAX + 1, ABS_MAX + 1), -1); + ck_assert_int_eq(libevdev_disable_event_type(dev, EV_MAX + 1), -1); + ck_assert_int_eq(libevdev_disable_event_type(dev, EV_SYN), -1); + ck_assert_int_eq(libevdev_disable_event_code(dev, EV_SYN, SYN_REPORT), -1); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_device_kernel_change_axis) +{ + struct uinput_device* uidev; + struct libevdev *dev, *dev2; + struct input_absinfo abs; + int rc; + + uidev = uinput_device_new(TEST_DEVICE_NAME); + ck_assert(uidev != NULL); + + abs.minimum = 0; + abs.maximum = 1000; + abs.fuzz = 1; + abs.flat = 2; + abs.resolution = 3; + abs.value = 0; + + uinput_device_set_abs_bit(uidev, ABS_X, &abs); + + rc = uinput_device_create(uidev); + ck_assert_msg(rc == 0, "Failed to create device: %s", strerror(-rc)); + + rc = libevdev_new_from_fd(uinput_device_get_fd(uidev), &dev); + ck_assert_msg(rc == 0, "Failed to init device: %s", strerror(-rc));; + + ck_assert_int_eq(libevdev_get_abs_minimum(dev, ABS_X), 0); + ck_assert_int_eq(libevdev_get_abs_maximum(dev, ABS_X), 1000); + ck_assert_int_eq(libevdev_get_abs_fuzz(dev, ABS_X), 1); + ck_assert_int_eq(libevdev_get_abs_flat(dev, ABS_X), 2); + ck_assert_int_eq(libevdev_get_abs_resolution(dev, ABS_X), 3); + + abs.minimum = 500; + abs.maximum = 5000; + abs.fuzz = 10; + abs.flat = 20; + abs.resolution = 30; + rc = libevdev_kernel_set_abs_info(dev, ABS_X, &abs); + ck_assert_int_eq(rc, 0); + + ck_assert_int_eq(libevdev_get_abs_minimum(dev, ABS_X), 500); + ck_assert_int_eq(libevdev_get_abs_maximum(dev, ABS_X), 5000); + ck_assert_int_eq(libevdev_get_abs_fuzz(dev, ABS_X), 10); + ck_assert_int_eq(libevdev_get_abs_flat(dev, ABS_X), 20); + ck_assert_int_eq(libevdev_get_abs_resolution(dev, ABS_X), 30); + + /* make sure kernel device is changed */ + rc = libevdev_new_from_fd(uinput_device_get_fd(uidev), &dev2); + ck_assert_msg(rc == 0, "Failed to init device: %s", strerror(-rc)); + ck_assert_int_eq(libevdev_get_abs_minimum(dev2, ABS_X), 500); + ck_assert_int_eq(libevdev_get_abs_maximum(dev2, ABS_X), 5000); + ck_assert_int_eq(libevdev_get_abs_fuzz(dev2, ABS_X), 10); + ck_assert_int_eq(libevdev_get_abs_flat(dev2, ABS_X), 20); + ck_assert_int_eq(libevdev_get_abs_resolution(dev2, ABS_X), 30); + libevdev_free(dev2); + + libevdev_free(dev); + uinput_device_free(uidev); +} +END_TEST + +START_TEST(test_device_kernel_change_axis_invalid) +{ + struct uinput_device* uidev; + struct libevdev *dev; + struct input_absinfo abs; + int rc; + + uidev = uinput_device_new(TEST_DEVICE_NAME); + ck_assert(uidev != NULL); + + abs.minimum = 0; + abs.maximum = 1000; + abs.fuzz = 1; + abs.flat = 2; + abs.resolution = 3; /* FIXME: value is unused, we can't test resolution */ + abs.value = 0; + + uinput_device_set_abs_bit(uidev, ABS_X, &abs); + + rc = uinput_device_create(uidev); + ck_assert_msg(rc == 0, "Failed to create device: %s", strerror(-rc)); + + rc = libevdev_new_from_fd(uinput_device_get_fd(uidev), &dev); + ck_assert_msg(rc == 0, "Failed to init device: %s", strerror(-rc));; + + rc = libevdev_kernel_set_abs_info(dev, ABS_MAX + 1, &abs); + ck_assert_int_eq(rc, -EINVAL); + + libevdev_free(dev); + uinput_device_free(uidev); +} +END_TEST + +START_TEST(test_led_valid) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + + test_create_device(&uidev, &dev, + EV_LED, LED_NUML, + EV_LED, LED_CAPSL, + EV_LED, LED_COMPOSE, + -1); + + rc = libevdev_kernel_set_led_value(dev, LED_NUML, LIBEVDEV_LED_ON); + ck_assert_int_eq(rc, 0); + rc = libevdev_kernel_set_led_value(dev, LED_NUML, LIBEVDEV_LED_OFF); + ck_assert_int_eq(rc, 0); + + rc = libevdev_kernel_set_led_values(dev, + LED_NUML, LIBEVDEV_LED_OFF, + LED_CAPSL, LIBEVDEV_LED_ON, + LED_COMPOSE, LIBEVDEV_LED_OFF, + -1); + ck_assert_int_eq(rc, 0); + ck_assert_int_eq(0, libevdev_get_event_value(dev, EV_LED, LED_NUML)); + ck_assert_int_eq(1, libevdev_get_event_value(dev, EV_LED, LED_CAPSL)); + ck_assert_int_eq(0, libevdev_get_event_value(dev, EV_LED, LED_COMPOSE)); + + rc = libevdev_kernel_set_led_values(dev, + LED_NUML, LIBEVDEV_LED_ON, + LED_CAPSL, LIBEVDEV_LED_OFF, + LED_COMPOSE, LIBEVDEV_LED_ON, + -1); + ck_assert_int_eq(rc, 0); + ck_assert_int_eq(1, libevdev_get_event_value(dev, EV_LED, LED_NUML)); + ck_assert_int_eq(0, libevdev_get_event_value(dev, EV_LED, LED_CAPSL)); + ck_assert_int_eq(1, libevdev_get_event_value(dev, EV_LED, LED_COMPOSE)); + + /* make sure we ignore unset leds */ + rc = libevdev_kernel_set_led_values(dev, + LED_NUML, LIBEVDEV_LED_ON, + LED_CAPSL, LIBEVDEV_LED_OFF, + LED_SCROLLL, LIBEVDEV_LED_OFF, + LED_COMPOSE, LIBEVDEV_LED_ON, + -1); + ck_assert_int_eq(rc, 0); + ck_assert_int_eq(1, libevdev_get_event_value(dev, EV_LED, LED_NUML)); + ck_assert_int_eq(0, libevdev_get_event_value(dev, EV_LED, LED_CAPSL)); + ck_assert_int_eq(1, libevdev_get_event_value(dev, EV_LED, LED_COMPOSE)); + + libevdev_free(dev); + uinput_device_free(uidev); +} +END_TEST + +START_TEST(test_led_invalid) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + + test_create_device(&uidev, &dev, + EV_LED, LED_NUML, + EV_LED, LED_CAPSL, + EV_LED, LED_COMPOSE, + -1); + + rc = libevdev_kernel_set_led_value(dev, LED_MAX + 1, LIBEVDEV_LED_ON); + ck_assert_int_eq(rc, -EINVAL); + + rc = libevdev_kernel_set_led_value(dev, LED_NUML, LIBEVDEV_LED_OFF + 1); + ck_assert_int_eq(rc, -EINVAL); + + rc = libevdev_kernel_set_led_value(dev, LED_SCROLLL, LIBEVDEV_LED_ON); + ck_assert_int_eq(rc, 0); + + rc = libevdev_kernel_set_led_values(dev, + LED_NUML, LIBEVDEV_LED_OFF + 1, + -1); + ck_assert_int_eq(rc, -EINVAL); + + rc = libevdev_kernel_set_led_values(dev, + LED_MAX + 1, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF + 1, + -1); + ck_assert_int_eq(rc, -EINVAL); + + rc = libevdev_kernel_set_led_values(dev, + LED_SCROLLL, LIBEVDEV_LED_OFF, + -1); + ck_assert_int_eq(rc, 0); + + libevdev_free(dev); + uinput_device_free(uidev); +} +END_TEST + +START_TEST(test_led_same) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + + test_create_device(&uidev, &dev, + EV_LED, LED_NUML, + EV_LED, LED_CAPSL, + EV_LED, LED_COMPOSE, + -1); + + rc = libevdev_kernel_set_led_values(dev, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + LED_NUML, LIBEVDEV_LED_OFF, + LED_NUML, LIBEVDEV_LED_ON, + /* more than LED_CNT */ + -1); + ck_assert_int_eq(rc, 0); + ck_assert_int_eq(1, libevdev_get_event_value(dev, EV_LED, LED_NUML)); + ck_assert_int_eq(0, libevdev_get_event_value(dev, EV_LED, LED_CAPSL)); + ck_assert_int_eq(0, libevdev_get_event_value(dev, EV_LED, LED_COMPOSE)); + + libevdev_free(dev); + uinput_device_free(uidev); +} +END_TEST + +Suite * +libevdev_has_event_test(void) +{ + Suite *s = suite_create("libevdev_has_event tests"); + + TCase *tc = tcase_create("event type"); + tcase_add_test(tc, test_ev_bit_limits); + tcase_add_test(tc, test_has_ev_bit); + suite_add_tcase(s, tc); + + tc = tcase_create("event codes"); + tcase_add_test(tc, test_event_codes); + tcase_add_test(tc, test_event_code_limits); + suite_add_tcase(s, tc); + + tc = tcase_create("ev_rep"); + tcase_add_test(tc, test_ev_rep); + tcase_add_test(tc, test_ev_rep_values); + suite_add_tcase(s, tc); + + tc = tcase_create("input properties"); + tcase_add_test(tc, test_input_props); + tcase_add_test(tc, test_set_input_props); + suite_add_tcase(s, tc); + + tc = tcase_create("multitouch info"); + tcase_add_test(tc, test_no_slots); + tcase_add_test(tc, test_slot_number); + tcase_add_test(tc, test_slot_init_value); + tcase_add_test(tc, test_invalid_mt_device); + suite_add_tcase(s, tc); + + tc = tcase_create("device info"); + tcase_add_test(tc, test_device_name); + tcase_add_test(tc, test_device_set_name); + tcase_add_test(tc, test_device_set_ids); + tcase_add_test(tc, test_device_get_abs_info); + suite_add_tcase(s, tc); + + tc = tcase_create("device bit manipulation"); + tcase_add_test(tc, test_device_set_abs); + tcase_add_test(tc, test_device_enable_bit); + tcase_add_test(tc, test_device_enable_bit_invalid); + tcase_add_test(tc, test_device_disable_bit); + tcase_add_test(tc, test_device_disable_bit_invalid); + tcase_add_test(tc, test_device_kernel_change_axis); + tcase_add_test(tc, test_device_kernel_change_axis_invalid); + suite_add_tcase(s, tc); + + tc = tcase_create("led manipulation"); + tcase_add_test(tc, test_led_valid); + tcase_add_test(tc, test_led_invalid); + tcase_add_test(tc, test_led_same); + suite_add_tcase(s, tc); + + return s; +} + diff --git a/test/test-libevdev-init.c b/test/test-libevdev-init.c new file mode 100644 index 0000000..61fea4b --- /dev/null +++ b/test/test-libevdev-init.c @@ -0,0 +1,497 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "test-common.h" + +START_TEST(test_new_device) +{ + struct libevdev *dev; + + dev = libevdev_new(); + ck_assert(dev != NULL); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_free_device) +{ + libevdev_free(NULL); +} +END_TEST + +START_TEST(test_init_from_invalid_fd) +{ + int rc; + struct libevdev *dev = NULL; + + rc = libevdev_new_from_fd(-1, &dev); + + ck_assert(dev == NULL); + ck_assert_int_eq(rc, -EBADF); + + rc = libevdev_new_from_fd(STDIN_FILENO, &dev); + ck_assert(dev == NULL); + ck_assert_int_eq(rc, -ENOTTY); +} +END_TEST + +START_TEST(test_init_and_change_fd) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + + dev = libevdev_new(); + ck_assert(dev != NULL); + ck_assert_int_eq(libevdev_set_fd(dev, -1), -EBADF); + + libevdev_set_log_function(test_logfunc_ignore_error, NULL); + ck_assert_int_eq(libevdev_change_fd(dev, -1), -1); + libevdev_set_log_function(test_logfunc_abort_on_error, NULL); + + rc = uinput_device_new_with_events(&uidev, + TEST_DEVICE_NAME, DEFAULT_IDS, + EV_SYN, SYN_REPORT, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_REL, REL_WHEEL, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_RIGHT, + -1); + ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc)); + ck_assert_int_eq(libevdev_set_fd(dev, uinput_device_get_fd(uidev)), 0); + + libevdev_set_log_function(test_logfunc_ignore_error, NULL); + ck_assert_int_eq(libevdev_set_fd(dev, 0), -EBADF); + libevdev_set_log_function(test_logfunc_abort_on_error, NULL); + + ck_assert_int_eq(libevdev_get_fd(dev), uinput_device_get_fd(uidev)); + + ck_assert_int_eq(libevdev_change_fd(dev, 0), 0); + ck_assert_int_eq(libevdev_get_fd(dev), 0); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +static int log_fn_called = 0; +static char *logdata = "test"; +static void logfunc(enum libevdev_log_priority priority, + void *data, + const char *file, int line, const char *func, + const char *f, va_list args) { + ck_assert_int_eq(strcmp(logdata, data), 0); + log_fn_called++; +} + +START_TEST(test_log_init) +{ + struct libevdev *dev = NULL; + enum libevdev_log_priority old; + + old = libevdev_get_log_priority(); + + libevdev_set_log_priority(LIBEVDEV_LOG_DEBUG); + + libevdev_set_log_function(logfunc, NULL); + libevdev_set_log_function(NULL, NULL); + + dev = libevdev_new(); + ck_assert(dev != NULL); + + libevdev_set_log_function(logfunc, logdata); + libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); + + libevdev_set_log_function(NULL, NULL); + libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); + + libevdev_set_log_function(logfunc, logdata); + libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); + + /* libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL) should + trigger a log message. We called it three times, but only twice + with the logfunc set, thus, ensure we only called the logfunc + twice */ + ck_assert_int_eq(log_fn_called, 2); + + libevdev_free(dev); + + libevdev_set_log_function(test_logfunc_abort_on_error, NULL); + + log_fn_called = 0; + + libevdev_set_log_priority(old); +} +END_TEST + +START_TEST(test_log_default_priority) +{ + ck_assert_int_eq(libevdev_get_log_priority(), LIBEVDEV_LOG_INFO); +} +END_TEST + +START_TEST(test_log_set_get_priority) +{ + enum libevdev_log_priority pri; + enum libevdev_log_priority old; + + old = libevdev_get_log_priority(); + + pri = LIBEVDEV_LOG_DEBUG; + libevdev_set_log_priority(pri); + ck_assert_int_eq(libevdev_get_log_priority(), pri); + + pri = LIBEVDEV_LOG_INFO; + libevdev_set_log_priority(pri); + ck_assert_int_eq(libevdev_get_log_priority(), pri); + + pri = LIBEVDEV_LOG_ERROR; + libevdev_set_log_priority(pri); + ck_assert_int_eq(libevdev_get_log_priority(), pri); + + /* debug and above is clamped */ + pri = LIBEVDEV_LOG_DEBUG + 1; + libevdev_set_log_priority(pri); + ck_assert_int_eq(libevdev_get_log_priority(), LIBEVDEV_LOG_DEBUG); + + /* error and below is not clamped, we need this for another test */ + pri = LIBEVDEV_LOG_ERROR - 1; + libevdev_set_log_priority(pri); + ck_assert_int_eq(libevdev_get_log_priority(), pri); + + libevdev_set_log_priority(old); +} +END_TEST + +START_TEST(test_log_priority) +{ + struct libevdev *dev = NULL; + enum libevdev_log_priority old; + + old = libevdev_get_log_priority(); + + libevdev_set_log_function(logfunc, logdata); + + dev = libevdev_new(); + ck_assert(dev != NULL); + + libevdev_set_log_priority(LIBEVDEV_LOG_DEBUG); + libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); + ck_assert_int_eq(log_fn_called, 1); + + libevdev_set_log_priority(LIBEVDEV_LOG_INFO); + libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); + ck_assert_int_eq(log_fn_called, 2); + + libevdev_set_log_priority(LIBEVDEV_LOG_ERROR); + libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); + ck_assert_int_eq(log_fn_called, 3); + + /* we don't have any log msgs > ERROR at the moment, so test it by + setting an invalid priority. */ + libevdev_set_log_priority(LIBEVDEV_LOG_ERROR - 1); + libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); + ck_assert_int_eq(log_fn_called, 3); + + libevdev_free(dev); + + libevdev_set_log_function(test_logfunc_abort_on_error, NULL); + + log_fn_called = 0; + + libevdev_set_log_priority(old); +} +END_TEST + +static char *logdata_1 = "foo"; +static char *logdata_2 = "bar"; +static int log_data_fn_called = 0; +static void logfunc_data(enum libevdev_log_priority priority, + void *data, + const char *file, int line, const char *func, + const char *f, va_list args) { + switch(log_data_fn_called) { + case 0: ck_assert(data == logdata_1); break; + case 1: ck_assert(data == logdata_2); break; + case 2: ck_assert(data == NULL); break; + default: + ck_abort(); + } + log_data_fn_called++; +} + +START_TEST(test_log_data) +{ + struct libevdev *dev = NULL; + + dev = libevdev_new(); + ck_assert(dev != NULL); + + libevdev_set_log_function(logfunc_data, logdata_1); + libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); + + libevdev_set_log_function(logfunc_data, logdata_2); + libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); + + libevdev_set_log_function(logfunc_data, NULL); + libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); + + libevdev_free(dev); +} +END_TEST + +START_TEST(test_device_init) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + + rc = uinput_device_new_with_events(&uidev, + TEST_DEVICE_NAME, DEFAULT_IDS, + EV_SYN, SYN_REPORT, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_REL, REL_WHEEL, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_RIGHT, + -1); + ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc)); + + dev = libevdev_new(); + ck_assert(dev != NULL); + rc = libevdev_set_fd(dev, uinput_device_get_fd(uidev)); + ck_assert_msg(rc == 0, "Failed to init device: %s", strerror(-rc));; + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_device_init_from_fd) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + + rc = uinput_device_new_with_events(&uidev, + TEST_DEVICE_NAME, DEFAULT_IDS, + EV_SYN, SYN_REPORT, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_REL, REL_WHEEL, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_RIGHT, + -1); + ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc)); + + rc = libevdev_new_from_fd(uinput_device_get_fd(uidev), &dev); + ck_assert_msg(rc == 0, "Failed to init device: %s", strerror(-rc));; + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_device_grab) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + + test_create_device(&uidev, &dev, + EV_SYN, SYN_REPORT, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_REL, REL_WHEEL, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_RIGHT, + -1); + + libevdev_set_log_function(test_logfunc_ignore_error, NULL); + rc = libevdev_grab(dev, 0); + ck_assert_int_eq(rc, -EINVAL); + rc = libevdev_grab(dev, 1); + ck_assert_int_eq(rc, -EINVAL); + libevdev_set_log_function(test_logfunc_abort_on_error, NULL); + + rc = libevdev_grab(dev, LIBEVDEV_UNGRAB); + ck_assert_int_eq(rc, 0); + rc = libevdev_grab(dev, LIBEVDEV_GRAB); + ck_assert_int_eq(rc, 0); + rc = libevdev_grab(dev, LIBEVDEV_GRAB); + ck_assert_int_eq(rc, 0); + rc = libevdev_grab(dev, LIBEVDEV_UNGRAB); + ck_assert_int_eq(rc, 0); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_set_clock_id) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + + test_create_device(&uidev, &dev, + EV_SYN, SYN_REPORT, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_REL, REL_WHEEL, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_RIGHT, + -1); + + rc = libevdev_set_clock_id(dev, CLOCK_REALTIME); + ck_assert_int_eq(rc, 0); + + rc = libevdev_set_clock_id(dev, CLOCK_MONOTONIC); + ck_assert_int_eq(rc, 0); + + rc = libevdev_set_clock_id(dev, CLOCK_MONOTONIC_RAW); + ck_assert_int_eq(rc, -EINVAL); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + +START_TEST(test_clock_id_events) +{ + struct uinput_device* uidev; + struct libevdev *dev, *dev2; + int rc, fd; + struct input_event ev1, ev2; + struct timespec t1_real, t2_real; + struct timespec t1_mono, t2_mono; + int64_t t1, t2; + + test_create_device(&uidev, &dev, + EV_SYN, SYN_REPORT, + EV_REL, REL_X, + EV_REL, REL_Y, + EV_REL, REL_WHEEL, + EV_KEY, BTN_LEFT, + EV_KEY, BTN_MIDDLE, + EV_KEY, BTN_RIGHT, + -1); + + fd = open(uinput_device_get_devnode(uidev), O_RDONLY); + ck_assert_int_gt(fd, -1); + + rc = libevdev_new_from_fd(fd, &dev2); + ck_assert_msg(rc == 0, "Failed to create second device: %s", strerror(-rc)); + + rc = libevdev_set_clock_id(dev2, CLOCK_MONOTONIC); + ck_assert_int_eq(rc, 0); + + clock_gettime(CLOCK_REALTIME, &t1_real); + clock_gettime(CLOCK_MONOTONIC, &t1_mono); + uinput_device_event(uidev, EV_REL, REL_X, 1); + uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); + clock_gettime(CLOCK_REALTIME, &t2_real); + clock_gettime(CLOCK_MONOTONIC, &t2_mono); + + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev1); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); + + rc = libevdev_next_event(dev2, LIBEVDEV_READ_FLAG_NORMAL, &ev2); + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); + + ck_assert_int_eq(ev1.type, ev2.type); + ck_assert_int_eq(ev1.code, ev2.code); + ck_assert_int_eq(ev1.value, ev2.value); + + t1 = ev1.time.tv_sec * 1000000LL + ev1.time.tv_usec; + t2 = ev2.time.tv_sec * 1000000LL + ev2.time.tv_usec; + ck_assert_int_ne(t1, t2); + + ck_assert_int_ge(ev1.time.tv_sec, t1_real.tv_sec); + ck_assert_int_ge(ev1.time.tv_usec, t1_real.tv_nsec/1000); + ck_assert_int_le(ev1.time.tv_sec, t2_real.tv_sec); + ck_assert_int_le(ev1.time.tv_usec, t2_real.tv_nsec/1000); + + ck_assert_int_ge(ev2.time.tv_sec, t1_mono.tv_sec); + ck_assert_int_ge(ev2.time.tv_usec, t1_mono.tv_nsec/1000); + ck_assert_int_le(ev2.time.tv_sec, t2_mono.tv_sec); + ck_assert_int_le(ev2.time.tv_usec, t2_mono.tv_nsec/1000); + + uinput_device_free(uidev); + libevdev_free(dev); + libevdev_free(dev2); + close(fd); +} +END_TEST + +Suite * +libevdev_init_test(void) +{ + Suite *s = suite_create("libevdev init tests"); + + TCase *tc = tcase_create("device init"); + tcase_add_test(tc, test_new_device); + tcase_add_test(tc, test_free_device); + tcase_add_test(tc, test_init_from_invalid_fd); + tcase_add_test(tc, test_init_and_change_fd); + suite_add_tcase(s, tc); + + tc = tcase_create("log init"); + tcase_add_test(tc, test_log_init); + tcase_add_test(tc, test_log_priority); + tcase_add_test(tc, test_log_set_get_priority); + tcase_add_test(tc, test_log_default_priority); + tcase_add_test(tc, test_log_data); + suite_add_tcase(s, tc); + + tc = tcase_create("device fd init"); + tcase_add_test(tc, test_device_init); + tcase_add_test(tc, test_device_init_from_fd); + suite_add_tcase(s, tc); + + tc = tcase_create("device grab"); + tcase_add_test(tc, test_device_grab); + suite_add_tcase(s, tc); + + tc = tcase_create("clock id"); + tcase_add_test(tc, test_set_clock_id); + tcase_add_test(tc, test_clock_id_events); + suite_add_tcase(s, tc); + + return s; +} diff --git a/test/test-link.c b/test/test-link.c new file mode 100644 index 0000000..a66ea86 --- /dev/null +++ b/test/test-link.c @@ -0,0 +1,6 @@ +#include +#include + +int main(void) { + return libevdev_new_from_fd(0, NULL); +} diff --git a/test/test-main.c b/test/test-main.c new file mode 100644 index 0000000..83fce14 --- /dev/null +++ b/test/test-main.c @@ -0,0 +1,92 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "test-common.h" + +extern Suite *event_name_suite(void); +extern Suite *event_code_suite(void); +extern Suite *libevdev_init_test(void); +extern Suite *queue_suite(void); +extern Suite *libevdev_has_event_test(void); +extern Suite *libevdev_events(void); +extern Suite *uinput_suite(void); + +static int +is_debugger_attached(void) +{ + int status; + int rc; + int pid = fork(); + + if (pid == -1) + return 0; + + if (pid == 0) { + int ppid = getppid(); + if (ptrace(PTRACE_ATTACH, ppid, NULL, NULL) == 0) { + waitpid(ppid, NULL, 0); + ptrace(PTRACE_CONT, NULL, NULL); + ptrace(PTRACE_DETACH, ppid, NULL, NULL); + rc = 0; + } else + rc = 1; + _exit(rc); + } else { + waitpid(pid, &status, 0); + rc = WEXITSTATUS(status); + } + + return rc; +} + +int main(int argc, char **argv) +{ + int failed; + + if (is_debugger_attached()) + setenv("CK_FORK", "no", 0); + + libevdev_set_log_function(test_logfunc_abort_on_error, NULL); + + Suite *s = libevdev_has_event_test(); + SRunner *sr = srunner_create(s); + srunner_add_suite(sr, libevdev_events()); + srunner_add_suite(sr, libevdev_init_test()); + srunner_add_suite(sr, queue_suite()); + srunner_add_suite(sr, event_name_suite()); + srunner_add_suite(sr, event_code_suite()); + srunner_add_suite(sr, uinput_suite()); + srunner_run_all(sr, CK_NORMAL); + + failed = srunner_ntests_failed(sr); + srunner_free(sr); + + return failed; +} diff --git a/test/test-uinput.c b/test/test-uinput.c new file mode 100644 index 0000000..99738e3 --- /dev/null +++ b/test/test-uinput.c @@ -0,0 +1,393 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "test-common.h" +#define UINPUT_NODE "/dev/uinput" + +START_TEST(test_uinput_create_device) +{ + struct libevdev *dev, *dev2; + struct libevdev_uinput *uidev; + int fd, uinput_fd; + unsigned int type, code; + int rc; + const char *devnode; + + dev = libevdev_new(); + ck_assert(dev != NULL); + libevdev_set_name(dev, TEST_DEVICE_NAME); + libevdev_enable_event_type(dev, EV_SYN); + libevdev_enable_event_type(dev, EV_REL); + libevdev_enable_event_code(dev, EV_REL, REL_X, NULL); + libevdev_enable_event_code(dev, EV_REL, REL_Y, NULL); + libevdev_enable_event_code(dev, EV_REL, REL_MAX, NULL); + + rc = libevdev_uinput_create_from_device(dev, LIBEVDEV_UINPUT_OPEN_MANAGED, &uidev); + ck_assert_int_eq(rc, 0); + ck_assert(uidev != NULL); + + uinput_fd = libevdev_uinput_get_fd(uidev); + ck_assert_int_gt(uinput_fd, -1); + + devnode = libevdev_uinput_get_devnode(uidev); + ck_assert(devnode != NULL); + + fd = open(devnode, O_RDONLY); + ck_assert_int_gt(fd, -1); + rc = libevdev_new_from_fd(fd, &dev2); + ck_assert_int_eq(rc, 0); + + for (type = 0; type < EV_CNT; type++) { + int max = libevdev_event_type_get_max(type); + if (max == -1) + continue; + + for (code = 0; code < max; code++) { + ck_assert_int_eq(libevdev_has_event_code(dev, type, code), + libevdev_has_event_code(dev2, type, code)); + } + } + + libevdev_free(dev); + libevdev_free(dev2); + libevdev_uinput_destroy(uidev); + close(fd); + + /* uinput fd is managed, so make sure it did get closed */ + ck_assert_int_eq(close(uinput_fd), -1); + ck_assert_int_eq(errno, EBADF); + +} +END_TEST + +START_TEST(test_uinput_create_device_invalid) +{ + struct libevdev *dev; + struct libevdev_uinput *uidev = NULL; + int rc; + + dev = libevdev_new(); + ck_assert(dev != NULL); + libevdev_set_name(dev, TEST_DEVICE_NAME); + libevdev_enable_event_type(dev, EV_SYN); + libevdev_enable_event_type(dev, EV_REL); + libevdev_enable_event_code(dev, EV_REL, REL_X, NULL); + libevdev_enable_event_code(dev, EV_REL, REL_Y, NULL); + + libevdev_set_log_function(test_logfunc_ignore_error, NULL); + rc = libevdev_uinput_create_from_device(dev, -1, &uidev); + ck_assert_int_eq(rc, -EBADF); + ck_assert(uidev == NULL); + libevdev_set_log_function(test_logfunc_abort_on_error, NULL); + + libevdev_free(dev); +} +END_TEST + +START_TEST(test_uinput_create_device_from_fd) +{ + struct libevdev *dev, *dev2; + struct libevdev_uinput *uidev; + int fd, fd2; + unsigned int type, code; + int rc; + const char *devnode; + + dev = libevdev_new(); + ck_assert(dev != NULL); + libevdev_set_name(dev, TEST_DEVICE_NAME); + libevdev_enable_event_type(dev, EV_SYN); + libevdev_enable_event_type(dev, EV_REL); + libevdev_enable_event_code(dev, EV_REL, REL_X, NULL); + libevdev_enable_event_code(dev, EV_REL, REL_Y, NULL); + + fd = open(UINPUT_NODE, O_RDWR); + ck_assert_int_gt(fd, -1); + + rc = libevdev_uinput_create_from_device(dev, fd, &uidev); + ck_assert_int_eq(rc, 0); + ck_assert(uidev != NULL); + + ck_assert_int_eq(libevdev_uinput_get_fd(uidev), fd); + + devnode = libevdev_uinput_get_devnode(uidev); + ck_assert(devnode != NULL); + + fd2 = open(devnode, O_RDONLY); + ck_assert_int_gt(fd2, -1); + rc = libevdev_new_from_fd(fd2, &dev2); + ck_assert_int_eq(rc, 0); + + for (type = 0; type < EV_CNT; type++) { + int max = libevdev_event_type_get_max(type); + if (max == -1) + continue; + + for (code = 0; code < max; code++) { + ck_assert_int_eq(libevdev_has_event_code(dev, type, code), + libevdev_has_event_code(dev2, type, code)); + } + } + + libevdev_free(dev); + libevdev_free(dev2); + libevdev_uinput_destroy(uidev); + close(fd); + close(fd2); +} +END_TEST + +START_TEST(test_uinput_check_syspath_time) +{ + struct libevdev *dev; + struct libevdev_uinput *uidev, *uidev2; + const char *syspath, *syspath2; + int fd, fd2; + int rc; + + dev = libevdev_new(); + ck_assert(dev != NULL); + libevdev_set_name(dev, TEST_DEVICE_NAME); + libevdev_enable_event_type(dev, EV_SYN); + libevdev_enable_event_type(dev, EV_REL); + libevdev_enable_event_code(dev, EV_REL, REL_X, NULL); + libevdev_enable_event_code(dev, EV_REL, REL_Y, NULL); + + fd = open(UINPUT_NODE, O_RDWR); + ck_assert_int_gt(fd, -1); + fd2 = open(UINPUT_NODE, O_RDWR); + ck_assert_int_gt(fd2, -1); + + rc = libevdev_uinput_create_from_device(dev, fd, &uidev); + ck_assert_int_eq(rc, 0); + + /* sleep for 1.5 seconds. sysfs resolution is 1 second, so + creating both devices without delay means + libevdev_uinput_get_syspath can't actually differ between + them. By waiting, we get different ctime for uidev and uidev2, + and exercise that part of the code. + */ + usleep(1500000); + + /* create a second one to test the syspath time filtering code */ + rc = libevdev_uinput_create_from_device(dev, fd2, &uidev2); + ck_assert_int_eq(rc, 0); + + syspath = libevdev_uinput_get_syspath(uidev); + ck_assert(syspath != NULL); + + /* get syspath twice returns same pointer */ + syspath2 = libevdev_uinput_get_syspath(uidev); + ck_assert(syspath == syspath2); + + /* second dev has different syspath */ + syspath2 = libevdev_uinput_get_syspath(uidev2); + ck_assert(strcmp(syspath, syspath2) != 0); + + libevdev_free(dev); + libevdev_uinput_destroy(uidev); + libevdev_uinput_destroy(uidev2); + + close(fd); + close(fd2); +} +END_TEST + +START_TEST(test_uinput_check_syspath_name) +{ + struct libevdev *dev; + struct libevdev_uinput *uidev, *uidev2; + const char *syspath, *syspath2; + int fd, fd2; + int rc; + + dev = libevdev_new(); + ck_assert(dev != NULL); + libevdev_set_name(dev, TEST_DEVICE_NAME); + libevdev_enable_event_type(dev, EV_SYN); + libevdev_enable_event_type(dev, EV_REL); + libevdev_enable_event_code(dev, EV_REL, REL_X, NULL); + libevdev_enable_event_code(dev, EV_REL, REL_Y, NULL); + + fd = open(UINPUT_NODE, O_RDWR); + ck_assert_int_gt(fd, -1); + fd2 = open(UINPUT_NODE, O_RDWR); + ck_assert_int_gt(fd2, -1); + + rc = libevdev_uinput_create_from_device(dev, fd, &uidev); + ck_assert_int_eq(rc, 0); + + /* create a second one to stress the syspath filtering code */ + libevdev_set_name(dev, TEST_DEVICE_NAME " 2"); + rc = libevdev_uinput_create_from_device(dev, fd2, &uidev2); + ck_assert_int_eq(rc, 0); + + syspath = libevdev_uinput_get_syspath(uidev); + ck_assert(syspath != NULL); + + /* get syspath twice returns same pointer */ + syspath2 = libevdev_uinput_get_syspath(uidev); + ck_assert(syspath == syspath2); + + /* second dev has different syspath */ + syspath2 = libevdev_uinput_get_syspath(uidev2); + ck_assert(strcmp(syspath, syspath2) != 0); + + libevdev_free(dev); + libevdev_uinput_destroy(uidev); + libevdev_uinput_destroy(uidev2); + + close(fd); + close(fd2); +} +END_TEST + +START_TEST(test_uinput_events) +{ + struct libevdev *dev; + struct libevdev_uinput *uidev; + int fd, fd2; + int rc; + const char *devnode; + int i; + const int nevents = 5; + struct input_event events[] = { {{0, 0}, EV_REL, REL_X, 1}, + {{0, 0}, EV_REL, REL_Y, -1}, + {{0, 0}, EV_SYN, SYN_REPORT, 0}, + {{0, 0}, EV_KEY, BTN_LEFT, 1}, + {{0, 0}, EV_SYN, SYN_REPORT, 0}}; + struct input_event events_read[nevents]; + + dev = libevdev_new(); + ck_assert(dev != NULL); + libevdev_set_name(dev, TEST_DEVICE_NAME); + libevdev_enable_event_type(dev, EV_SYN); + libevdev_enable_event_type(dev, EV_REL); + libevdev_enable_event_type(dev, EV_KEY); + libevdev_enable_event_code(dev, EV_REL, REL_X, NULL); + libevdev_enable_event_code(dev, EV_REL, REL_Y, NULL); + libevdev_enable_event_code(dev, EV_KEY, BTN_LEFT, NULL); + + fd = open(UINPUT_NODE, O_RDWR); + ck_assert_int_gt(fd, -1); + + rc = libevdev_uinput_create_from_device(dev, fd, &uidev); + ck_assert_int_eq(rc, 0); + ck_assert(uidev != NULL); + + devnode = libevdev_uinput_get_devnode(uidev); + ck_assert(devnode != NULL); + + fd2 = open(devnode, O_RDONLY); + + for (i = 0; i < nevents; i++) + libevdev_uinput_write_event(uidev, events[i].type, events[i].code, events[i].value); + + rc = read(fd2, events_read, sizeof(events_read)); + ck_assert_int_eq(rc, sizeof(events_read)); + + for (i = 0; i < nevents; i++) { + ck_assert_int_eq(events[i].type, events_read[i].type); + ck_assert_int_eq(events[i].code, events_read[i].code); + ck_assert_int_eq(events[i].value, events_read[i].value); + } + + libevdev_free(dev); + libevdev_uinput_destroy(uidev); + close(fd); + close(fd2); +} +END_TEST + +START_TEST(test_uinput_properties) +{ + struct libevdev *dev, *dev2; + struct libevdev_uinput *uidev; + int fd; + int rc; + const char *devnode; + + dev = libevdev_new(); + ck_assert(dev != NULL); + libevdev_set_name(dev, TEST_DEVICE_NAME); + libevdev_enable_event_type(dev, EV_SYN); + libevdev_enable_event_type(dev, EV_REL); + libevdev_enable_event_type(dev, EV_KEY); + libevdev_enable_event_code(dev, EV_REL, REL_X, NULL); + libevdev_enable_event_code(dev, EV_REL, REL_Y, NULL); + libevdev_enable_event_code(dev, EV_KEY, BTN_LEFT, NULL); + libevdev_enable_property(dev, INPUT_PROP_BUTTONPAD); + libevdev_enable_property(dev, INPUT_PROP_MAX); + + rc = libevdev_uinput_create_from_device(dev, LIBEVDEV_UINPUT_OPEN_MANAGED, &uidev); + ck_assert_int_eq(rc, 0); + ck_assert(uidev != NULL); + + devnode = libevdev_uinput_get_devnode(uidev); + ck_assert(devnode != NULL); + + fd = open(devnode, O_RDONLY); + ck_assert_int_gt(fd, -1); + rc = libevdev_new_from_fd(fd, &dev2); + ck_assert_int_eq(rc, 0); + + ck_assert(libevdev_has_property(dev2, INPUT_PROP_BUTTONPAD)); + ck_assert(libevdev_has_property(dev2, INPUT_PROP_MAX)); + + libevdev_free(dev); + libevdev_free(dev2); + libevdev_uinput_destroy(uidev); + close(fd); +} +END_TEST + +Suite * +uinput_suite(void) +{ + Suite *s = suite_create("libevdev uinput device tests"); + + TCase *tc = tcase_create("device creation"); + tcase_add_test(tc, test_uinput_create_device); + tcase_add_test(tc, test_uinput_create_device_invalid); + tcase_add_test(tc, test_uinput_create_device_from_fd); + tcase_add_test(tc, test_uinput_check_syspath_time); + tcase_add_test(tc, test_uinput_check_syspath_name); + suite_add_tcase(s, tc); + + tc = tcase_create("device events"); + tcase_add_test(tc, test_uinput_events); + suite_add_tcase(s, tc); + + tc = tcase_create("device properties"); + tcase_add_test(tc, test_uinput_properties); + suite_add_tcase(s, tc); + + return s; +} diff --git a/test/valgrind.suppressions b/test/valgrind.suppressions new file mode 100644 index 0000000..07fae53 --- /dev/null +++ b/test/valgrind.suppressions @@ -0,0 +1,22 @@ +{ + + Memcheck:Param + timer_create(evp) + fun:timer_create@@GLIBC_2.3.3 + fun:srunner_run + fun:main +} +{ + + Memcheck:Param + ioctl(generic) + fun:ioctl + fun:libevdev_grab +} +{ + + Memcheck:Param + ioctl(generic) + fun:ioctl + fun:test_revoke* +} diff --git a/tools/.gitignore b/tools/.gitignore new file mode 100644 index 0000000..8084fed --- /dev/null +++ b/tools/.gitignore @@ -0,0 +1,2 @@ +libevdev-events +touchpad-edge-detector diff --git a/tools/Makefile.am b/tools/Makefile.am new file mode 100644 index 0000000..ef429c4 --- /dev/null +++ b/tools/Makefile.am @@ -0,0 +1,12 @@ +noinst_PROGRAMS = libevdev-events touchpad-edge-detector + +AM_CPPFLAGS = $(GCC_CFLAGS) -I$(top_srcdir) -I$(top_srcdir)/include -I$(top_srcdir)/libevdev -fPIE +AM_LDFLAGS = -pie +libevdev_ldadd = $(top_builddir)/libevdev/libevdev.la + +libevdev_events_SOURCES = libevdev-events.c +libevdev_events_LDADD = $(libevdev_ldadd) + +touchpad_edge_detector_SOURCES = touchpad-edge-detector.c +touchpad_edge_detector_LDADD = $(libevdev_ldadd) + diff --git a/tools/libevdev-events.c b/tools/libevdev-events.c new file mode 100644 index 0000000..0711c78 --- /dev/null +++ b/tools/libevdev-events.c @@ -0,0 +1,195 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libevdev.h" + +static void +print_abs_bits(struct libevdev *dev, int axis) +{ + const struct input_absinfo *abs; + + if (!libevdev_has_event_code(dev, EV_ABS, axis)) + return; + + abs = libevdev_get_abs_info(dev, axis); + + printf(" Value %6d\n", abs->value); + printf(" Min %6d\n", abs->minimum); + printf(" Max %6d\n", abs->maximum); + if (abs->fuzz) + printf(" Fuzz %6d\n", abs->fuzz); + if (abs->flat) + printf(" Flat %6d\n", abs->flat); + if (abs->resolution) + printf(" Resolution %6d\n", abs->resolution); +} + +static void +print_code_bits(struct libevdev *dev, unsigned int type, unsigned int max) +{ + unsigned int i; + for (i = 0; i <= max; i++) { + if (!libevdev_has_event_code(dev, type, i)) + continue; + + printf(" Event code %i (%s)\n", i, libevdev_event_code_get_name(type, i)); + if (type == EV_ABS) + print_abs_bits(dev, i); + } +} + +static void +print_bits(struct libevdev *dev) +{ + unsigned int i; + printf("Supported events:\n"); + + for (i = 0; i <= EV_MAX; i++) { + if (libevdev_has_event_type(dev, i)) + printf(" Event type %d (%s)\n", i, libevdev_event_type_get_name(i)); + switch(i) { + case EV_KEY: + print_code_bits(dev, EV_KEY, KEY_MAX); + break; + case EV_REL: + print_code_bits(dev, EV_REL, REL_MAX); + break; + case EV_ABS: + print_code_bits(dev, EV_ABS, ABS_MAX); + break; + case EV_LED: + print_code_bits(dev, EV_LED, LED_MAX); + break; + } + } +} + +static void +print_props(struct libevdev *dev) +{ + unsigned int i; + printf("Properties:\n"); + + for (i = 0; i <= INPUT_PROP_MAX; i++) { + if (libevdev_has_property(dev, i)) + printf(" Property type %d (%s)\n", i, + libevdev_property_get_name(i)); + } +} + +static int +print_event(struct input_event *ev) +{ + if (ev->type == EV_SYN) + printf("Event: time %ld.%06ld, ++++++++++++++++++++ %s +++++++++++++++\n", + ev->time.tv_sec, + ev->time.tv_usec, + libevdev_event_type_get_name(ev->type)); + else + printf("Event: time %ld.%06ld, type %d (%s), code %d (%s), value %d\n", + ev->time.tv_sec, + ev->time.tv_usec, + ev->type, + libevdev_event_type_get_name(ev->type), + ev->code, + libevdev_event_code_get_name(ev->type, ev->code), + ev->value); + return 0; +} + +static int +print_sync_event(struct input_event *ev) +{ + printf("SYNC: "); + print_event(ev); + return 0; +} + +int +main(int argc, char **argv) +{ + struct libevdev *dev = NULL; + const char *file; + int fd; + int rc = 1; + + if (argc < 2) + goto out; + + file = argv[1]; + fd = open(file, O_RDONLY); + if (fd < 0) { + perror("Failed to open device"); + goto out; + } + + rc = libevdev_new_from_fd(fd, &dev); + if (rc < 0) { + fprintf(stderr, "Failed to init libevdev (%s)\n", strerror(-rc)); + goto out; + } + + printf("Input device ID: bus %#x vendor %#x product %#x\n", + libevdev_get_id_bustype(dev), + libevdev_get_id_vendor(dev), + libevdev_get_id_product(dev)); + printf("Evdev version: %x\n", libevdev_get_driver_version(dev)); + printf("Input device name: \"%s\"\n", libevdev_get_name(dev)); + printf("Phys location: %s\n", libevdev_get_phys(dev)); + printf("Uniq identifier: %s\n", libevdev_get_uniq(dev)); + print_bits(dev); + print_props(dev); + + do { + struct input_event ev; + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL|LIBEVDEV_READ_FLAG_BLOCKING, &ev); + if (rc == LIBEVDEV_READ_STATUS_SYNC) { + printf("::::::::::::::::::::: dropped ::::::::::::::::::::::\n"); + while (rc == LIBEVDEV_READ_STATUS_SYNC) { + print_sync_event(&ev); + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + } + printf("::::::::::::::::::::: re-synced ::::::::::::::::::::::\n"); + } else if (rc == LIBEVDEV_READ_STATUS_SUCCESS) + print_event(&ev); + } while (rc == LIBEVDEV_READ_STATUS_SYNC || rc == LIBEVDEV_READ_STATUS_SUCCESS || rc == -EAGAIN); + + if (rc != LIBEVDEV_READ_STATUS_SUCCESS && rc != -EAGAIN) + fprintf(stderr, "Failed to handle events: %s\n", strerror(rc)); + + rc = 0; +out: + libevdev_free(dev); + + return rc; +} diff --git a/tools/publish-doc b/tools/publish-doc new file mode 100755 index 0000000..2dcd45e --- /dev/null +++ b/tools/publish-doc @@ -0,0 +1,7 @@ +#!/bin/bash + +set -e + +make +rsync --delete -avz doc/html/ freedesktop.org:/srv/www.freedesktop.org/www/software/libevdev/doc/latest + diff --git a/tools/touchpad-edge-detector.c b/tools/touchpad-edge-detector.c new file mode 100644 index 0000000..b857934 --- /dev/null +++ b/tools/touchpad-edge-detector.c @@ -0,0 +1,196 @@ +/* + * Copyright © 2014 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of Red Hat + * not be used in advertising or publicity pertaining to distribution + * of the software without specific, written prior permission. Red + * Hat makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied + * warranty. + * + * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +static int +usage(void) { + printf("Usage: %s /dev/input/event0\n", program_invocation_short_name); + printf("\n"); + printf("This tool reads the touchpad events from the kernel and calculates\n " + "the minimum and maximum for the x and y coordinates, respectively.\n"); + return 1; +} + +struct dimensions { + int top, bottom, left, right; +}; + +static int +print_current_values(const struct dimensions *d) +{ + static int progress; + char status = 0; + + switch (progress) { + case 0: status = '|'; break; + case 1: status = '/'; break; + case 2: status = '-'; break; + case 3: status = '\\'; break; + } + + progress = (progress + 1) % 4; + + printf("\rTouchpad sends: x [%d..%d], y [%d..%d] %c", + d->left, d->right, d->top, d->bottom, status); + return 0; +} + +static int +handle_event(struct dimensions *d, const struct input_event *ev) { + if (ev->type == EV_SYN) { + return print_current_values(d); + } else if (ev->type != EV_ABS) + return 0; + + switch(ev->code) { + case ABS_X: + case ABS_MT_POSITION_X: + d->left = min(d->left, ev->value); + d->right = max(d->right, ev->value); + break; + case ABS_Y: + case ABS_MT_POSITION_Y: + d->top = min(d->top, ev->value); + d->bottom = max(d->bottom, ev->value); + break; + } + + return 0; +} + +static int +mainloop(struct libevdev *dev, struct dimensions *dim) { + struct pollfd fds[2]; + sigset_t mask; + + fds[0].fd = libevdev_get_fd(dev); + fds[0].events = POLLIN; + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + fds[1].fd = signalfd(-1, &mask, SFD_NONBLOCK); + fds[1].events = POLLIN; + + sigprocmask(SIG_BLOCK, &mask, NULL); + + while (poll(fds, 2, -1)) { + struct input_event ev; + int rc; + + if (fds[1].revents) + break; + + do { + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + if (rc == LIBEVDEV_READ_STATUS_SYNC) { + fprintf(stderr, "Error: cannot keep up\n"); + return 1; + } else if (rc != -EAGAIN && rc < 0) { + fprintf(stderr, "Error: %s\n", strerror(-rc)); + return 1; + } else if (rc == LIBEVDEV_READ_STATUS_SUCCESS) { + handle_event(dim, &ev); + } + } while (rc != -EAGAIN); + } + + return 0; +} + +int main (int argc, char **argv) { + int rc; + int fd; + const char *path; + struct libevdev *dev; + struct dimensions dim; + + if (argc < 2) + return usage(); + + path = argv[1]; + if (path[0] == '-') + return usage(); + + fd = open(path, O_RDONLY|O_NONBLOCK); + if (fd < 0) { + fprintf(stderr, "Error opening the device: %s\n", strerror(errno)); + return 1; + } + + rc = libevdev_new_from_fd(fd, &dev); + if (rc != 0) { + fprintf(stderr, "Error fetching the device info: %s\n", strerror(-rc)); + return 1; + } + + if (libevdev_grab(dev, LIBEVDEV_GRAB) != 0) { + fprintf(stderr, "Error: cannot grab the device, something else is grabbing it.\n"); + fprintf(stderr, "Use 'fuser -v %s' to find processes with an open fd\n", path); + return 1; + } + libevdev_grab(dev, LIBEVDEV_UNGRAB); + + dim.left = INT_MAX; + dim.right = INT_MIN; + dim.top = INT_MAX; + dim.bottom = INT_MIN; + + printf("Touchpad %s on %s\n", libevdev_get_name(dev), path); + printf("Move one finger around the touchpad to detect the actual edges\n"); + printf("Kernel says: x [%d..%d], y [%d..%d]\n", + libevdev_get_abs_minimum(dev, ABS_X), + libevdev_get_abs_maximum(dev, ABS_X), + libevdev_get_abs_minimum(dev, ABS_Y), + libevdev_get_abs_maximum(dev, ABS_Y)); + + setbuf(stdout, NULL); + + rc = mainloop(dev, &dim); + + printf("\n"); + + libevdev_free(dev); + close(fd); + + return rc; +} + -- 2.7.4