tizen 2.4 release accepted/tizen_2.4_mobile tizen_2.4 accepted/tizen/2.4/mobile/20151029.030925 submit/tizen_2.4/20151028.065032 tizen_2.4_mobile_release
authorjk7744.park <jk7744.park@samsung.com>
Mon, 26 Oct 2015 07:01:56 +0000 (16:01 +0900)
committerjk7744.park <jk7744.park@samsung.com>
Mon, 26 Oct 2015 07:01:56 +0000 (16:01 +0900)
51 files changed:
.gitignore [new file with mode: 0644]
COPYING [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
README.md [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.ac [new file with mode: 0644]
doc/.gitignore [new file with mode: 0644]
doc/Makefile.am [new file with mode: 0644]
doc/libevdev.css [new file with mode: 0644]
doc/libevdev.doxygen.in [new file with mode: 0644]
doc/libevdev.man.in [new file with mode: 0644]
include/linux/input.h [new file with mode: 0644]
libevdev.pc.in [new file with mode: 0644]
libevdev/.gitignore [new file with mode: 0644]
libevdev/Makefile.am [new file with mode: 0644]
libevdev/libevdev-int.h [new file with mode: 0644]
libevdev/libevdev-names.c [new file with mode: 0644]
libevdev/libevdev-uinput-int.h [new file with mode: 0644]
libevdev/libevdev-uinput.c [new file with mode: 0644]
libevdev/libevdev-uinput.h [new file with mode: 0644]
libevdev/libevdev-util.h [new file with mode: 0644]
libevdev/libevdev.c [new file with mode: 0644]
libevdev/libevdev.h [new file with mode: 0644]
libevdev/libevdev.sym [new file with mode: 0644]
libevdev/make-event-names.py [new file with mode: 0755]
m4/.gitignore [new file with mode: 0644]
m4/attributes.m4 [new file with mode: 0644]
packaging/libevdev.spec [new file with mode: 0644]
test/.gitignore [new file with mode: 0644]
test/Makefile.am [new file with mode: 0644]
test/test-common-uinput.c [new file with mode: 0644]
test/test-common-uinput.h [new file with mode: 0644]
test/test-common.c [new file with mode: 0644]
test/test-common.h [new file with mode: 0644]
test/test-compile-pedantic.c [new file with mode: 0644]
test/test-event-codes.c [new file with mode: 0644]
test/test-event-names.c [new file with mode: 0644]
test/test-int-queue.c [new file with mode: 0644]
test/test-kernel.c [new file with mode: 0644]
test/test-libevdev-events.c [new file with mode: 0644]
test/test-libevdev-has-event.c [new file with mode: 0644]
test/test-libevdev-init.c [new file with mode: 0644]
test/test-link.c [new file with mode: 0644]
test/test-main.c [new file with mode: 0644]
test/test-uinput.c [new file with mode: 0644]
test/valgrind.suppressions [new file with mode: 0644]
tools/.gitignore [new file with mode: 0644]
tools/Makefile.am [new file with mode: 0644]
tools/libevdev-events.c [new file with mode: 0644]
tools/publish-doc [new file with mode: 0755]
tools/touchpad-edge-detector.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..957e4c0
--- /dev/null
@@ -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 (file)
index 0000000..16f959a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,29 @@
+Copyright © 2013 Red Hat, Inc.
+Copyright © 2013 David Herrmann <dh.herrmann@gmail.com>
+
+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 (file)
index 0000000..95a16ea
--- /dev/null
@@ -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 (file)
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 (executable)
index 0000000..dcf3ccf
--- /dev/null
@@ -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 (file)
index 0000000..dc17109
--- /dev/null
@@ -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 (file)
index 0000000..20b4ef5
--- /dev/null
@@ -0,0 +1,3 @@
+html/
+libevdev.doxygen
+libevdev.man
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644 (file)
index 0000000..c1a06aa
--- /dev/null
@@ -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 (file)
index 0000000..851b154
--- /dev/null
@@ -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 (file)
index 0000000..853c47d
--- /dev/null
@@ -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 <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> 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 <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> 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
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+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 (file)
index 0000000..58a15c7
--- /dev/null
@@ -0,0 +1,32 @@
+.TH LIBEVDEV 3 @PACKAGE_VERSION@
+.SH NAME
+libevdev \- wrapper library for evdev devices
+.SH SYNOPSIS
+.HP
+#include <libevdev/libevdev.h>
+.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 (file)
index 0000000..e37293b
--- /dev/null
@@ -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 <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <linux/types.h>
+
+
+/*
+ * 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 (file)
index 0000000..77127e0
--- /dev/null
@@ -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 (file)
index 0000000..339d72d
--- /dev/null
@@ -0,0 +1 @@
+event-names.h
diff --git a/libevdev/Makefile.am b/libevdev/Makefile.am
new file mode 100644 (file)
index 0000000..c83a225
--- /dev/null
@@ -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 (file)
index 0000000..f587e76
--- /dev/null
@@ -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 <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#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 (file)
index 0000000..10669a3
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright © 2013 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * 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 <config.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+#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 (file)
index 0000000..fbc1c29
--- /dev/null
@@ -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 (file)
index 0000000..f176a00
--- /dev/null
@@ -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 <config.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <linux/uinput.h>
+
+#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 (file)
index 0000000..f5b8df4
--- /dev/null
@@ -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 <libevdev/libevdev.h>
+
+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 <strong>/dev/uinput</strong>. 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 (file)
index 0000000..31c1a14
--- /dev/null
@@ -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 <config.h>
+#include <stdbool.h>
+#include <string.h>
+
+#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 (file)
index 0000000..a316831
--- /dev/null
@@ -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 <config.h>
+#include <errno.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdbool.h>
+
+#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 (file)
index 0000000..6c03cce
--- /dev/null
@@ -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 <linux/input.h>
+#include <stdarg.h>
+
+#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 <b>not</b> 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.
+ *
+ * <dl>
+ * <dt>EVIOCGVERSION:</dt>
+ * <dd>supported, see libevdev_get_driver_version()</dd>
+ * <dt>EVIOCGID:</dt>
+ * <dd>supported, see libevdev_get_id_product(), libevdev_get_id_vendor(),
+ * libevdev_get_id_bustype(), libevdev_get_id_version()</dd>
+ * <dt>EVIOCGREP:</dt>
+ * <dd>supported, see libevdev_get_event_value())</dd>
+ * <dt>EVIOCSREP:</dt>
+ * <dd>supported, see libevdev_enable_event_code()</dd>
+ * <dt>EVIOCGKEYCODE:</dt>
+ * <dd>currently not supported</dd>
+ * <dt>EVIOCGKEYCODE:</dt>
+ * <dd>currently not supported</dd>
+ * <dt>EVIOCSKEYCODE:</dt>
+ * <dd>currently not supported</dd>
+ * <dt>EVIOCSKEYCODE:</dt>
+ * <dd>currently not supported</dd>
+ * <dt>EVIOCGNAME:</dt>
+ * <dd>supported, see libevdev_get_name()</dd>
+ * <dt>EVIOCGPHYS:</dt>
+ * <dd>supported, see libevdev_get_phys()</dd>
+ * <dt>EVIOCGUNIQ:</dt>
+ * <dd>supported, see libevdev_get_uniq()</dd>
+ * <dt>EVIOCGPROP:</dt>
+ * <dd>supported, see libevdev_has_property()</dd>
+ * <dt>EVIOCGMTSLOTS:</dt>
+ * <dd>supported, see libevdev_get_num_slots(), libevdev_get_slot_value()</dd>
+ * <dt>EVIOCGKEY:</dt>
+ * <dd>supported, see libevdev_has_event_code(), libevdev_get_event_value()</dd>
+ * <dt>EVIOCGLED:</dt>
+ * <dd>supported, see libevdev_has_event_code(), libevdev_get_event_value()</dd>
+ * <dt>EVIOCGSND:</dt>
+ * <dd>currently not supported</dd>
+ * <dt>EVIOCGSW:</dt>
+ * <dd>supported, see libevdev_has_event_code(), libevdev_get_event_value()</dd>
+ * <dt>EVIOCGBIT:</dt>
+ * <dd>supported, see libevdev_has_event_code(), libevdev_get_event_value()</dd>
+ * <dt>EVIOCGABS:</dt>
+ * <dd>supported, see libevdev_has_event_code(), libevdev_get_event_value(),
+ * libevdev_get_abs_info()</dd>
+ * <dt>EVIOCSABS:</dt>
+ * <dd>supported, see libevdev_kernel_set_abs_info()</dd>
+ * <dt>EVIOCSFF:</dt>
+ * <dd>currently not supported</dd>
+ * <dt>EVIOCRMFF:</dt>
+ * <dd>currently not supported</dd>
+ * <dt>EVIOCGEFFECTS:</dt>
+ * <dd>currently not supported</dd>
+ * <dt>EVIOCGRAB:</dt>
+ * <dd>supported, see libevdev_grab()</dd>
+ * <dt>EVIOCSCLOCKID:</dt>
+ * <dd>supported, see libevdev_set_clock_id()</dd>
+ * <dt>EVIOCREVOKE:</dt>
+ * <dd>currently not supported, see
+ * http://lists.freedesktop.org/archives/input-tools/2014-January/000688.html</dd>
+ * </dl>
+ *
+ */
+
+/**
+ * @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 <code>struct
+ * libevdev</code> 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 (file)
index 0000000..ef4f06b
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2013 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * 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 (executable)
index 0000000..149f093
--- /dev/null
@@ -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 (file)
index 0000000..38066dd
--- /dev/null
@@ -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 (file)
index 0000000..aa53ef2
--- /dev/null
@@ -0,0 +1,288 @@
+dnl Macros to check the presence of generic (non-typed) symbols.
+dnl Copyright (c) 2006-2008 Diego Pettenò <flameeyes@gmail.com>
+dnl Copyright (c) 2006-2008 xine project
+dnl Copyright (c) 2012 Lucas De Marchi <lucas.de.marchi@gmail.com>
+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 (file)
index 0000000..65389f2
--- /dev/null
@@ -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 (file)
index 0000000..4b0ab3e
--- /dev/null
@@ -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 (file)
index 0000000..7b195fb
--- /dev/null
@@ -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 (file)
index 0000000..617b8e2
--- /dev/null
@@ -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 <config.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <linux/uinput.h>
+#include <dirent.h>
+
+#include <libevdev/libevdev.h>
+#include <libevdev/libevdev-int.h>
+#include <libevdev/libevdev-util.h>
+#include <libevdev/libevdev-uinput.h>
+
+#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 (file)
index 0000000..6273a6d
--- /dev/null
@@ -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 <config.h>
+#include <libevdev/libevdev.h>
+
+#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 (file)
index 0000000..6449d46
--- /dev/null
@@ -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 <config.h>
+#include <check.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#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 (file)
index 0000000..d7a33d0
--- /dev/null
@@ -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 <config.h>
+#include <libevdev/libevdev.h>
+
+#include <check.h>
+
+#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 (file)
index 0000000..6faea86
--- /dev/null
@@ -0,0 +1,6 @@
+#include <libevdev/libevdev.h>
+#include <libevdev/libevdev-uinput.h>
+
+int main(void) {
+       return 0;
+}
diff --git a/test/test-event-codes.c b/test/test-event-codes.c
new file mode 100644 (file)
index 0000000..3a57973
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright © 2013 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * 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 <config.h>
+#include <libevdev/libevdev-int.h>
+#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 (file)
index 0000000..8d7e6d6
--- /dev/null
@@ -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 <config.h>
+#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 (file)
index 0000000..d45fee0
--- /dev/null
@@ -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 <config.h>
+#include <limits.h>
+#include <libevdev/libevdev-int.h>
+#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 (file)
index 0000000..f3bc7e0
--- /dev/null
@@ -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 <config.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <linux/input.h>
+
+#include <libevdev/libevdev.h>
+#include <libevdev/libevdev-uinput.h>
+#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 (file)
index 0000000..6ef0bf9
--- /dev/null
@@ -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 <config.h>
+#include <linux/input.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <libevdev/libevdev-util.h>
+
+#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 (file)
index 0000000..3ac41f1
--- /dev/null
@@ -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 <config.h>
+#include <linux/input.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <fcntl.h>
+
+#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 (file)
index 0000000..61fea4b
--- /dev/null
@@ -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 <config.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <libevdev/libevdev-uinput.h>
+#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 (file)
index 0000000..a66ea86
--- /dev/null
@@ -0,0 +1,6 @@
+#include <stddef.h>
+#include <libevdev/libevdev.h>
+
+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 (file)
index 0000000..83fce14
--- /dev/null
@@ -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 <config.h>
+#include <check.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <libevdev/libevdev.h>
+
+#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 (file)
index 0000000..99738e3
--- /dev/null
@@ -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 <config.h>
+#include <linux/input.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <libevdev/libevdev-uinput.h>
+
+#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 (file)
index 0000000..07fae53
--- /dev/null
@@ -0,0 +1,22 @@
+{
+   <timer_create@@GLIBC_2.3.3>
+   Memcheck:Param
+   timer_create(evp)
+   fun:timer_create@@GLIBC_2.3.3
+   fun:srunner_run
+   fun:main
+}
+{
+   <ioctl_EVIOCGRAB_unaddressable_bytes>
+   Memcheck:Param
+   ioctl(generic)
+   fun:ioctl
+   fun:libevdev_grab
+}
+{
+   <ioctl_EVIOCREVOKE_unaddressable_bytes>
+   Memcheck:Param
+   ioctl(generic)
+   fun:ioctl
+   fun:test_revoke*
+}
diff --git a/tools/.gitignore b/tools/.gitignore
new file mode 100644 (file)
index 0000000..8084fed
--- /dev/null
@@ -0,0 +1,2 @@
+libevdev-events
+touchpad-edge-detector
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644 (file)
index 0000000..ef429c4
--- /dev/null
@@ -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 (file)
index 0000000..0711c78
--- /dev/null
@@ -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 <config.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <linux/input.h>
+
+#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 (executable)
index 0000000..2dcd45e
--- /dev/null
@@ -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 (file)
index 0000000..b857934
--- /dev/null
@@ -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 <libevdev/libevdev.h>
+#include <sys/signalfd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#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;
+}
+