xinput: Implements support for Xbox360 & XboxOne gamepads (linux-input)

Diaz Fernandez Eric eric.diaz.fernandez at gmail.com
Thu Sep 22 12:45:47 CDT 2016


Supports XInputGetState, XInputSetState, XInputGetCapabilities,
XInputEnable and XInputGetStateEx.

Also supports XInputGetKeystroke, by working from state changes.

XInputGetBatteryInformation returns success and an unknown full battery,
  or "not connected".

XInputGetDSoundAudioDeviceGuids returns "not connected" or "no headphones".

The DLL handles multiple users of different architectures (32/64)
simultaneously.

ie: steam (32 bits) and No Man Sky (64 bits) can both access the pads through
    the DLL

Some tuning can be done using xinput_settings.h

Tested on Manjaro OpenRC.

Signed-off-by: Eric Diaz Fernandez <eric.diaz.fernandez at gmail.com>
---
 configure                                      | 154 ++++-
 configure.ac                                   |  14 +
 dlls/xinput1_3/Makefile.in                     |  12 +-
 dlls/xinput1_3/xinput1_3.spec                  |   1 +
 dlls/xinput1_3/xinput1_3_main.c                | 523 +++++++++++++--
 dlls/xinput1_3/xinput_debug.c                  |  87 +++
 dlls/xinput1_3/xinput_debug.h                  |  35 +
 dlls/xinput1_3/xinput_device_id.h              |  39 ++
 dlls/xinput1_3/xinput_gamepad.c                | 580 +++++++++++++++++
 dlls/xinput1_3/xinput_gamepad.h                | 108 +++
 dlls/xinput1_3/xinput_linux_input.c            | 437 +++++++++++++
 dlls/xinput1_3/xinput_linux_input.h            | 103 +++
 dlls/xinput1_3/xinput_linux_input_debug.c      | 262 ++++++++
 dlls/xinput1_3/xinput_linux_input_debug.h      |  35 +
 dlls/xinput1_3/xinput_linux_input_translator.c | 100 +++
 dlls/xinput1_3/xinput_linux_input_translator.h | 125 ++++
 dlls/xinput1_3/xinput_linux_input_xboxpad.c    | 371 +++++++++++
 dlls/xinput1_3/xinput_linux_input_xboxpad.h    |  54 ++
 dlls/xinput1_3/xinput_linux_input_xboxpad_2.c  | 295 +++++++++
 dlls/xinput1_3/xinput_linux_input_xboxpad_2.h  |  61 ++
 dlls/xinput1_3/xinput_service.c                | 869 +++++++++++++++++++++++++
 dlls/xinput1_3/xinput_service.h                | 101 +++
 dlls/xinput1_3/xinput_settings.h               | 103 +++
 dlls/xinput1_3/xinput_tools.c                  | 126 ++++
 dlls/xinput1_3/xinput_tools.h                  |  90 +++
 include/config.h.in                            |   6 +
 26 files changed, 4617 insertions(+), 74 deletions(-)
 create mode 100644 dlls/xinput1_3/xinput_debug.c
 create mode 100644 dlls/xinput1_3/xinput_debug.h
 create mode 100644 dlls/xinput1_3/xinput_device_id.h
 create mode 100644 dlls/xinput1_3/xinput_gamepad.c
 create mode 100644 dlls/xinput1_3/xinput_gamepad.h
 create mode 100644 dlls/xinput1_3/xinput_linux_input.c
 create mode 100644 dlls/xinput1_3/xinput_linux_input.h
 create mode 100644 dlls/xinput1_3/xinput_linux_input_debug.c
 create mode 100644 dlls/xinput1_3/xinput_linux_input_debug.h
 create mode 100644 dlls/xinput1_3/xinput_linux_input_translator.c
 create mode 100644 dlls/xinput1_3/xinput_linux_input_translator.h
 create mode 100644 dlls/xinput1_3/xinput_linux_input_xboxpad.c
 create mode 100644 dlls/xinput1_3/xinput_linux_input_xboxpad.h
 create mode 100644 dlls/xinput1_3/xinput_linux_input_xboxpad_2.c
 create mode 100644 dlls/xinput1_3/xinput_linux_input_xboxpad_2.h
 create mode 100644 dlls/xinput1_3/xinput_service.c
 create mode 100644 dlls/xinput1_3/xinput_service.h
 create mode 100644 dlls/xinput1_3/xinput_settings.h
 create mode 100644 dlls/xinput1_3/xinput_tools.c
 create mode 100644 dlls/xinput1_3/xinput_tools.h

diff --git a/configure b/configure
index 6bda01c..a6394f4 100755
--- a/configure
+++ b/configure
@@ -627,6 +627,8 @@ LIBOBJS
 WINELOADER_INSTALL
 ALL_VARS_RULES
 LDAP_LIBS
+MQ_LIBS
+SHM_LIBS
 RT_LIBS
 POLL_LIBS
 DL_LIBS
@@ -789,7 +791,6 @@ infodir
 docdir
 oldincludedir
 includedir
-runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -1668,7 +1669,6 @@ datadir='${datarootdir}'
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
-runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1921,15 +1921,6 @@ do
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;
 
-  -runstatedir | --runstatedir | --runstatedi | --runstated \
-  | --runstate | --runstat | --runsta | --runst | --runs \
-  | --run | --ru | --r)
-    ac_prev=runstatedir ;;
-  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
-  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
-  | --run=* | --ru=* | --r=*)
-    runstatedir=$ac_optarg ;;
-
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -2067,7 +2058,7 @@ fi
 for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
 		datadir sysconfdir sharedstatedir localstatedir includedir \
 		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-		libdir localedir mandir runstatedir
+		libdir localedir mandir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -2220,7 +2211,6 @@ Fine tuning of the installation directories:
   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
-  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
@@ -6302,7 +6292,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -6348,7 +6338,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -6372,7 +6362,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -6417,7 +6407,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -6441,7 +6431,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15660,6 +15650,132 @@ fi
 
 LIBS=$ac_save_LIBS
 
+ac_save_LIBS=$LIBS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing shm_open" >&5
+$as_echo_n "checking for library containing shm_open... " >&6; }
+if ${ac_cv_search_shm_open+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char shm_open ();
+int
+main ()
+{
+return shm_open ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' rt; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_search_shm_open=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext
+  if ${ac_cv_search_shm_open+:} false; then :
+  break
+fi
+done
+if ${ac_cv_search_shm_open+:} false; then :
+
+else
+  ac_cv_search_shm_open=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_shm_open" >&5
+$as_echo "$ac_cv_search_shm_open" >&6; }
+ac_res=$ac_cv_search_shm_open
+if test "$ac_res" != no; then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+$as_echo "#define HAVE_SHM_OPEN 1" >>confdefs.h
+
+                               test "$ac_res" = "none required" || SHM_LIBS="$ac_res"
+
+fi
+
+LIBS=$ac_save_LIBS
+
+ac_save_LIBS=$LIBS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing mq_open" >&5
+$as_echo_n "checking for library containing mq_open... " >&6; }
+if ${ac_cv_search_mq_open+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char mq_open ();
+int
+main ()
+{
+return mq_open ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' rt; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_search_mq_open=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext
+  if ${ac_cv_search_mq_open+:} false; then :
+  break
+fi
+done
+if ${ac_cv_search_mq_open+:} false; then :
+
+else
+  ac_cv_search_mq_open=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_mq_open" >&5
+$as_echo "$ac_cv_search_mq_open" >&6; }
+ac_res=$ac_cv_search_mq_open
+if test "$ac_res" != no; then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+$as_echo "#define HAVE_MQ_OPEN 1" >>confdefs.h
+
+                               test "$ac_res" = "none required" || MQ_LIBS="$ac_res"
+
+fi
+
+LIBS=$ac_save_LIBS
+
 LDAP_LIBS=""
 
 if test "$ac_cv_header_ldap_h" = "yes" -a "$ac_cv_header_lber_h" = "yes"
@@ -17499,6 +17615,8 @@ EXCESS_PRECISION_CFLAGS = $EXCESS_PRECISION_CFLAGS
 DL_LIBS = $DL_LIBS
 POLL_LIBS = $POLL_LIBS
 RT_LIBS = $RT_LIBS
+SHM_LIBS = $SHM_LIBS
+MQ_LIBS = $MQ_LIBS
 LDAP_LIBS = $LDAP_LIBS
 "
 
diff --git a/configure.ac b/configure.ac
index 6a0a1c9..fcbfa5e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2107,6 +2107,20 @@ AC_SEARCH_LIBS(clock_gettime, rt,
                 test "$ac_res" = "none required" || AC_SUBST(RT_LIBS,"$ac_res")])
 LIBS=$ac_save_LIBS
 
+dnl Check for shm_open which may be in -lrt
+ac_save_LIBS=$LIBS
+AC_SEARCH_LIBS(shm_open, rt,
+               [AC_DEFINE(HAVE_SHM_OPEN, 1, [Define to 1 if you have the `shm_open' function.])
+                               test "$ac_res" = "none required" || AC_SUBST(SHM_LIBS,"$ac_res")])
+LIBS=$ac_save_LIBS
+
+dnl Check for mq_open which may be in -lrt
+ac_save_LIBS=$LIBS
+AC_SEARCH_LIBS(mq_open, rt,
+               [AC_DEFINE(HAVE_MQ_OPEN, 1, [Define to 1 if you have the `mq_open' function.])
+                               test "$ac_res" = "none required" || AC_SUBST(MQ_LIBS,"$ac_res")])
+LIBS=$ac_save_LIBS
+
 dnl **** Check for OpenLDAP ***
 AC_SUBST(LDAP_LIBS,"")
 if test "$ac_cv_header_ldap_h" = "yes" -a "$ac_cv_header_lber_h" = "yes"
diff --git a/dlls/xinput1_3/Makefile.in b/dlls/xinput1_3/Makefile.in
index cf8f730..bfe3014 100644
--- a/dlls/xinput1_3/Makefile.in
+++ b/dlls/xinput1_3/Makefile.in
@@ -1,7 +1,17 @@
 MODULE    = xinput1_3.dll
 IMPORTLIB = xinput
+EXTRALIBS = $(PTHREAD_LIBS) $(SHM_LIBS) $(MQ_LIBS)
 
 C_SRCS = \
-	xinput1_3_main.c
+	xinput1_3_main.c \
+	xinput_debug.c \
+	xinput_tools.c \
+	xinput_linux_input.c \
+	xinput_linux_input_debug.c \
+	xinput_linux_input_translator.c \
+	xinput_linux_input_xboxpad.c \
+	xinput_linux_input_xboxpad_2.c \
+	xinput_service.c \
+	xinput_gamepad.c
 
 RC_SRCS = version.rc
diff --git a/dlls/xinput1_3/xinput1_3.spec b/dlls/xinput1_3/xinput1_3.spec
index 0023016..f5828b7 100644
--- a/dlls/xinput1_3/xinput1_3.spec
+++ b/dlls/xinput1_3/xinput1_3.spec
@@ -7,3 +7,4 @@
 7 stdcall XInputGetBatteryInformation(long long ptr)
 8 stdcall XInputGetKeystroke(long long ptr)
 100 stdcall XInputGetStateEx(long ptr)
+101 stdcall XInputServer(long long ptr long)
diff --git a/dlls/xinput1_3/xinput1_3_main.c b/dlls/xinput1_3/xinput1_3_main.c
index 04732eb..54873e2 100644
--- a/dlls/xinput1_3/xinput1_3_main.c
+++ b/dlls/xinput1_3/xinput1_3_main.c
@@ -1,6 +1,7 @@
 /*
  * The Wine project - Xinput Joystick Library
  * Copyright 2008 Andrew Fenn
+ * Copyright 2016 Eric Diaz Fernandez
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -10,17 +11,19 @@
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
+ * Lesser General Public License for more details. *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 #include "config.h"
+#include "xinput_settings.h"
+
+#include <pthread.h>
 #include <assert.h>
-#include <stdarg.h>
-#include <string.h>
+#include <errno.h>
+#include <unistd.h>
 
 #include "wine/debug.h"
 #include "windef.h"
@@ -28,19 +31,34 @@
 #include "winerror.h"
 
 #include "xinput.h"
+#include "xinput_service.h"
+#include "xinput_gamepad.h"
+#include "xinput_tools.h"
 
-/* Not defined in the headers, used only by XInputGetStateEx */
-#define XINPUT_GAMEPAD_GUIDE 0x0400
+#if HAVE_LINUX_INPUT_H
+#define XINPUT_SUPPORTED 1
+#else
+#define XINPUT_SUPPORTED 0
+#endif
 
 WINE_DEFAULT_DEBUG_CHANNEL(xinput);
 
-struct
-{
-    BOOL connected;
-} controllers[XUSER_MAX_COUNT];
+#ifndef DLL_WINE_PREATTACH
+#define DLL_WINE_PREATTACH 8
+#endif
+
+/**
+ * When a DLL is spawned,
+ * Try to find the shared memory.
+ * If it exists
+ */
 
 BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved)
 {
+    (void)reserved;
+
+    TRACE("DllMain(%p, %i, %p)\n", inst, reason, reserved);
+
     switch(reason)
     {
     case DLL_WINE_PREATTACH:
@@ -48,117 +66,512 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved)
     case DLL_PROCESS_ATTACH:
         DisableThreadLibraryCalls(inst);
         break;
+    case DLL_PROCESS_DETACH:
+#if XINPUT_SUPPORTED
+        xinput_gamepad_finalize();
+#endif
+        break;
     }
+
     return TRUE;
 }
 
+#if XINPUT_SUPPORTED
+pthread_mutex_t xinput_enabled_mtx = PTHREAD_MUTEX_INITIALIZER;
+static volatile BOOL xinput_enabled = TRUE;
+
+static BOOL XInputIsEnabled(void)
+{
+    BOOL ret;
+    pthread_mutex_lock(&xinput_enabled_mtx);
+    ret  = xinput_enabled;
+    pthread_mutex_unlock(&xinput_enabled_mtx);
+    return ret;
+}
+
+#endif
+
 void WINAPI XInputEnable(BOOL enable)
 {
-    /* Setting to false will stop messages from XInputSetState being sent
-    to the controllers. Setting to true will send the last vibration
-    value (sent to XInputSetState) to the controller and allow messages to
-    be sent */
-    FIXME("(enable %d) Stub!\n", enable);
+#if XINPUT_SUPPORTED
+#if XINPUT_TRACE_INTERFACE_USE
+    TRACE("XInputEnable(%d), pid=%i\n", enable, getpid());
+#endif
+
+    pthread_mutex_lock(&xinput_enabled_mtx);
+    xinput_enabled = enable;
+    pthread_mutex_unlock(&xinput_enabled_mtx);
+
+    if(!enable)
+    {
+        static const XINPUT_VIBRATION still = {0,0};
+
+        for(int i = 0; i < XUSER_MAX_COUNT; ++i)
+        {
+            if(xinput_gamepad_connected(i))
+            {
+                xinput_gamepad_rumble(i, &still);
+            }
+        }
+    }
+
+#else
+    FIXME("XInputEnable(%d) stub\n", enable);
+#endif
 }
 
 DWORD WINAPI XInputSetState(DWORD index, XINPUT_VIBRATION* vibration)
 {
-    FIXME("(index %u, vibration %p) Stub!\n", index, vibration);
+#if XINPUT_SUPPORTED
+#if XINPUT_TRACE_INTERFACE_USE
+    TRACE("XInputSetState(%d, %p), pid=%i\n", index, vibration, getpid());
+#endif
 
-    if (index >= XUSER_MAX_COUNT)
+    if(index >= XUSER_MAX_COUNT)
+    {
         return ERROR_BAD_ARGUMENTS;
-    if (!controllers[index].connected)
+    }
+
+    if(!xinput_gamepad_connected(index))
+    {
         return ERROR_DEVICE_NOT_CONNECTED;
+    }
 
+    xinput_gamepad_rumble(index, vibration);
+
+    return ERROR_SUCCESS;
+#else
+    FIXME("XInputSetState(%d, %p) stub\n", index, vibration);
     return ERROR_NOT_SUPPORTED;
+#endif
 }
 
 DWORD WINAPI DECLSPEC_HOTPATCH XInputGetState(DWORD index, XINPUT_STATE* state)
 {
-    union
-    {
-        XINPUT_STATE state;
-        XINPUT_STATE_EX state_ex;
-    } xinput;
-    DWORD ret;
-    static int warn_once;
+#if XINPUT_SUPPORTED
+#if XINPUT_TRACE_INTERFACE_USE
+    TRACE("XInputGetState(%d, %p), pid=%i\n", index, state, getpid());
+#endif
 
-    if (!warn_once++)
-        FIXME("(index %u, state %p) Stub!\n", index, state);
+    if(index >= XUSER_MAX_COUNT)
+    {
+        return ERROR_BAD_ARGUMENTS;
+    }
 
-    ret = XInputGetStateEx(index, &xinput.state_ex);
-    if (ret != ERROR_SUCCESS)
-        return ret;
+    if(!xinput_gamepad_connected(index))
+    {
+        return ERROR_DEVICE_NOT_CONNECTED;
+    }
 
-    /* The main difference between this and the Ex version is the media guide button */
-    xinput.state.Gamepad.wButtons &= ~XINPUT_GAMEPAD_GUIDE;
-    *state = xinput.state;
+    if(XInputIsEnabled())
+    {
+        xinput_gamepad_copy_state(index, state);
+    }
+    else
+    {
+        memset(&state->Gamepad, 0, sizeof(state->Gamepad));
+    }
 
     return ERROR_SUCCESS;
+#else
+    FIXME("XInputGetState(%d, %p)\n", index, state);
+    return ERROR_NOT_SUPPORTED;
+#endif
 }
 
 DWORD WINAPI DECLSPEC_HOTPATCH XInputGetStateEx(DWORD index, XINPUT_STATE_EX* state_ex)
 {
-    static int warn_once;
-
-    if (!warn_once++)
-        FIXME("(index %u, state %p) Stub!\n", index, state_ex);
+#if XINPUT_SUPPORTED
+#if XINPUT_TRACE_INTERFACE_USE
+    TRACE("XInputGetStateEx(%d, %p), pid=%i\n", index, state_ex, getpid());
+#endif
 
-    if (index >= XUSER_MAX_COUNT)
+    if(index >= XUSER_MAX_COUNT)
+    {
         return ERROR_BAD_ARGUMENTS;
-    if (!controllers[index].connected)
+    }
+
+    if(!xinput_gamepad_connected(index))
+    {
         return ERROR_DEVICE_NOT_CONNECTED;
+    }
+
+    if(XInputIsEnabled())
+    {
+        xinput_gamepad_copy_state_ex(index, state_ex);
+    }
+    else
+    {
+        memset(&state_ex->Gamepad, 0, sizeof(state_ex->Gamepad));
+    }
+
+#if XINPUT_TRACE_INTERFACE_USE
+    TRACE("XInputGetState(%d, %p) = ERROR_SUCCESS\n", index, state_ex);
+#endif
 
+    return ERROR_SUCCESS;
+#else
+    FIXME("XInputGetStateEx(%d, %p)\n", index, state_ex);
     return ERROR_NOT_SUPPORTED;
+#endif
+}
+
+#if XINPUT_SUPPORTED
+static DWORD xinputkeystroke_state[XUSER_MAX_COUNT] = {0, 0, 0, 0};
+static int xinputkeystroke_any_first = 0;
+
+#define XINPUTKEYSTROKE_VK_BUTTON_SIZE 33
+
+struct xinputkeystroke_mask_to_vk
+{
+    DWORD bits;
+    DWORD mask;
+    WORD value;
+};
+
+static struct xinputkeystroke_mask_to_vk xinputkeystroke_vk_button[XINPUTKEYSTROKE_VK_BUTTON_SIZE] =
+{
+    {XINPUT_GAMEPAD_DPAD_UP, XINPUT_GAMEPAD_DPAD_UP, VK_PAD_DPAD_UP}, /* 0 */
+    {XINPUT_GAMEPAD_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_DOWN, VK_PAD_DPAD_DOWN},
+    {XINPUT_GAMEPAD_DPAD_LEFT, XINPUT_GAMEPAD_DPAD_LEFT, VK_PAD_DPAD_LEFT},
+    {XINPUT_GAMEPAD_DPAD_RIGHT, XINPUT_GAMEPAD_DPAD_RIGHT, VK_PAD_DPAD_RIGHT},
+
+    {XINPUT_GAMEPAD_START, XINPUT_GAMEPAD_START, VK_PAD_START}, /* 4 */
+    {XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_BACK,VK_PAD_BACK},
+    {XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_LEFT_THUMB, VK_PAD_LTHUMB_PRESS},
+    {XINPUT_GAMEPAD_RIGHT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB, VK_PAD_RTHUMB_PRESS},
+
+    {XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_LEFT_SHOULDER, VK_PAD_LSHOULDER}, /* 8 */
+    {XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, VK_PAD_RSHOULDER},
+
+    {XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_A, VK_PAD_A},
+    {XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_B, VK_PAD_B},
+    {XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_X, VK_PAD_X}, /* 12 */
+    {XINPUT_GAMEPAD_Y, XINPUT_GAMEPAD_Y, VK_PAD_Y},
+
+    {XINPUT_GAMEPAD_LTRIGGER, XINPUT_GAMEPAD_LTRIGGER, VK_PAD_LTRIGGER},
+    {XINPUT_GAMEPAD_RTRIGGER, XINPUT_GAMEPAD_RTRIGGER, VK_PAD_RTRIGGER},
+
+    /* Do not change the order in a THUMB group */
+
+    {XINPUT_GAMEPAD_LTHUMB_UP|XINPUT_GAMEPAD_LTHUMB_LEFT, XINPUT_GAMEPAD_LTHUMB_MASK, VK_PAD_LTHUMB_UPLEFT}, /* 16 */
+    {XINPUT_GAMEPAD_LTHUMB_UP|XINPUT_GAMEPAD_LTHUMB_RIGHT, XINPUT_GAMEPAD_LTHUMB_MASK, VK_PAD_LTHUMB_UPRIGHT},
+    {XINPUT_GAMEPAD_LTHUMB_DOWN|XINPUT_GAMEPAD_LTHUMB_RIGHT, XINPUT_GAMEPAD_LTHUMB_MASK, VK_PAD_LTHUMB_DOWNRIGHT},
+    {XINPUT_GAMEPAD_LTHUMB_DOWN|XINPUT_GAMEPAD_LTHUMB_LEFT, XINPUT_GAMEPAD_LTHUMB_MASK, VK_PAD_LTHUMB_DOWNLEFT},
+    {XINPUT_GAMEPAD_LTHUMB_UP, XINPUT_GAMEPAD_LTHUMB_MASK, VK_PAD_LTHUMB_UP},
+    {XINPUT_GAMEPAD_LTHUMB_DOWN, XINPUT_GAMEPAD_LTHUMB_MASK, VK_PAD_LTHUMB_DOWN},
+    {XINPUT_GAMEPAD_LTHUMB_LEFT, XINPUT_GAMEPAD_LTHUMB_MASK, VK_PAD_LTHUMB_LEFT},
+    {XINPUT_GAMEPAD_LTHUMB_RIGHT, XINPUT_GAMEPAD_LTHUMB_MASK, VK_PAD_LTHUMB_RIGHT},
+
+    /* Do not change the order in a THUMB group */
+
+    {XINPUT_GAMEPAD_RTHUMB_UP|XINPUT_GAMEPAD_RTHUMB_LEFT, XINPUT_GAMEPAD_RTHUMB_MASK, VK_PAD_RTHUMB_UPLEFT}, /* 24 */
+    {XINPUT_GAMEPAD_RTHUMB_UP|XINPUT_GAMEPAD_RTHUMB_RIGHT, XINPUT_GAMEPAD_RTHUMB_MASK, VK_PAD_RTHUMB_UPRIGHT},
+    {XINPUT_GAMEPAD_RTHUMB_DOWN|XINPUT_GAMEPAD_RTHUMB_RIGHT, XINPUT_GAMEPAD_RTHUMB_MASK, VK_PAD_RTHUMB_DOWNRIGHT},
+    {XINPUT_GAMEPAD_RTHUMB_DOWN|XINPUT_GAMEPAD_RTHUMB_LEFT, XINPUT_GAMEPAD_RTHUMB_MASK, VK_PAD_RTHUMB_DOWNLEFT},
+    {XINPUT_GAMEPAD_RTHUMB_UP, XINPUT_GAMEPAD_RTHUMB_MASK, VK_PAD_RTHUMB_UP},
+    {XINPUT_GAMEPAD_RTHUMB_DOWN, XINPUT_GAMEPAD_RTHUMB_MASK, VK_PAD_RTHUMB_DOWN},
+    {XINPUT_GAMEPAD_RTHUMB_LEFT, XINPUT_GAMEPAD_RTHUMB_MASK, VK_PAD_RTHUMB_LEFT},
+    {XINPUT_GAMEPAD_RTHUMB_RIGHT, XINPUT_GAMEPAD_RTHUMB_MASK, VK_PAD_RTHUMB_RIGHT},
+
+    {XINPUT_GAMEPAD_GUIDE, XINPUT_GAMEPAD_GUIDE, VK_PAD_GUIDE} /* 32 */
+};
+
+/**
+ * Computes the diff from a set of pressed buttons to another
+ * Only returns the first match
+ * Always find the released buttons first
+ * Sets the keystroke of the found diff
+ * Returns -1 if nothing change was found
+ *
+ * @param index
+ * @param from
+ * @param to
+ * @param next the new (intermediary) buttons state
+ * @param keystroke
+ * @return
+ */
+
+static int XInputGetNextKeyStroke(int index, DWORD from, DWORD to, DWORD* next, PXINPUT_KEYSTROKE keystroke)
+{
+    /* first, look for the released ones */
+    int i;
+
+    if(from == to)
+    {
+        return -1;
+    }
+
+    for(i = 0; i < XINPUTKEYSTROKE_VK_BUTTON_SIZE; ++i)
+    {
+        DWORD bits = xinputkeystroke_vk_button[i].bits;
+        DWORD mask = xinputkeystroke_vk_button[i].mask;
+
+        BOOL is = (to & mask) == bits;
+
+        if(!is)
+        {
+            BOOL was = (from & mask) == bits;
+
+            if(was != is)
+            {
+                /* change */
+
+                *next = from & ~mask;
+                keystroke->VirtualKey = xinputkeystroke_vk_button[i].value;
+                keystroke->Unicode = 0;
+                keystroke->Flags = XINPUT_KEYSTROKE_KEYUP;
+                keystroke->UserIndex = index;
+                keystroke->HidCode = 0;
+                return i;
+            }
+        }
+    }
+
+    /* then, look for the pressed ones */
+
+    for(i = 0; i < XINPUTKEYSTROKE_VK_BUTTON_SIZE; ++i)
+    {
+        DWORD bits = xinputkeystroke_vk_button[i].bits;
+        DWORD mask = xinputkeystroke_vk_button[i].mask;
+
+        BOOL is = (to & mask) == bits;
+
+        if(is)
+        {
+            BOOL was = (from & mask) == bits;
+
+            if(was != is)
+            {
+                /* change */
+
+                *next = (from & ~mask)|bits;
+                keystroke->VirtualKey = xinputkeystroke_vk_button[i].value;
+                keystroke->Unicode = 0;
+                keystroke->Flags = XINPUT_KEYSTROKE_KEYDOWN;
+                keystroke->UserIndex = index;
+                keystroke->HidCode = 0;
+                return i;
+            }
+        }
+    }
+
+    /* no change */
+
+    return -1;
 }
 
+#endif
+
+/**
+ * As no time/serial is required, this function determines the stroke with
+ * diffs between states.
+ *
+ * @param index
+ * @param reserved
+ * @param keystroke
+ * @return
+ */
+
 DWORD WINAPI XInputGetKeystroke(DWORD index, DWORD reserved, PXINPUT_KEYSTROKE keystroke)
 {
-    FIXME("(index %u, reserved %u, keystroke %p) Stub!\n", index, reserved, keystroke);
+#if XINPUT_SUPPORTED
+    DWORD buttons;
+#if XINPUT_TRACE_INTERFACE_USE
+    TRACE("XInputGetKeystroke(%d, %d, %p), pid=%i\n", index, reserved, keystroke, getpid());
+#endif
+    (void)reserved;
+
+    if(index < XUSER_MAX_COUNT)
+    {
+        if(!xinput_gamepad_connected(index))
+        {
+            return ERROR_DEVICE_NOT_CONNECTED;
+        }
+
+        if(!xinput_gamepad_copy_buttons_state(index, &buttons))
+        {
+            return ERROR_EMPTY;
+        }
+
+        /* find the first bit of difference */
 
-    if (index >= XUSER_MAX_COUNT)
+        if(XInputGetNextKeyStroke(index, xinputkeystroke_state[index], buttons, &xinputkeystroke_state[index], keystroke) >= 0)
+        {
+            return ERROR_SUCCESS;
+        }
+
+        return ERROR_EMPTY;
+    }
+    else if(index == XUSER_INDEX_ANY)
+    {
+        /*
+         * for each device, look at the one that got the oldest unknown update
+         * loosely (a.k.a : without mutex) the first device checked in order
+         * to be a bit fair
+         */
+
+        int index = xinputkeystroke_any_first++;
+
+        for(int i = index; i < XUSER_MAX_COUNT; ++i)
+        {
+            if(XInputGetKeystroke(index, reserved, keystroke) == ERROR_SUCCESS)
+            {
+                return ERROR_SUCCESS;
+            }
+        }
+
+        for(int i = 0; i < index; ++i)
+        {
+            if(XInputGetKeystroke(index, reserved, keystroke) == ERROR_SUCCESS)
+            {
+                return ERROR_SUCCESS;
+            }
+        }
+
+        return ERROR_EMPTY;
+    }
+    else
+    {
         return ERROR_BAD_ARGUMENTS;
-    if (!controllers[index].connected)
-        return ERROR_DEVICE_NOT_CONNECTED;
+    }
 
     return ERROR_NOT_SUPPORTED;
+#else
+    TRACE("XInputGetKeystroke(%d, %d, %p)\n", index, reserved, keystroke);
+    return ERROR_NOT_SUPPORTED;
+#endif
 }
 
 DWORD WINAPI XInputGetCapabilities(DWORD index, DWORD flags, XINPUT_CAPABILITIES* capabilities)
 {
-    static int warn_once;
-
-    if (!warn_once++)
-        FIXME("(index %u, flags 0x%x, capabilities %p) Stub!\n", index, flags, capabilities);
+#if XINPUT_SUPPORTED
+    static XINPUT_GAMEPAD XINPUT_GAMEPAD_CAPS =
+    {
+        0xf7ff,
+        0xff,
+        0xff,
+        0x7fff,
+        0x7fff,
+        0x7fff,
+        0x7fff
+    };
+
+    static XINPUT_VIBRATION XINPUT_VIBRATION_CAPS =
+    {
+        0xffff,
+        0xffff,
+    };
 
-    if (index >= XUSER_MAX_COUNT)
+    if(index >= XUSER_MAX_COUNT)
+    {
+#if XINPUT_TRACE_INTERFACE_USE
+        TRACE("XInputGetCapabilities(%i, %x, %p) = ERROR_BAD_ARGUMENTS\n", index, flags, capabilities);
+#endif
         return ERROR_BAD_ARGUMENTS;
-    if (!controllers[index].connected)
+    }
+
+#if XINPUT_TRACE_INTERFACE_USE
+    TRACE("XInputGetCapabilities(%i, %x, %p), id=%i\n", index, flags, capabilities, getpid());
+#endif
+
+    if(!xinput_gamepad_connected(index))
+    {
         return ERROR_DEVICE_NOT_CONNECTED;
+    }
+
+    capabilities->Type = XINPUT_DEVTYPE_GAMEPAD;
+    capabilities->SubType = XINPUT_DEVSUBTYPE_GAMEPAD;
+    capabilities->Flags = 0;
+    capabilities->Gamepad = XINPUT_GAMEPAD_CAPS;
+    capabilities->Vibration = XINPUT_VIBRATION_CAPS;
+
+#if XINPUT_TRACE_INTERFACE_USE
+    TRACE("XInputGetCapabilities(%i, %x, %p) = ERROR_SUCCESS\n", index, flags, capabilities);
+#endif
 
+    return ERROR_DEVICE_NOT_CONNECTED;
+#else
+    TRACE("XInputGetCapabilities(%i, %x, %p)\n", index, flags, capabilities);
     return ERROR_NOT_SUPPORTED;
+#endif
 }
 
+#if XINPUT_SUPPORTED
+static GUID guid_null = {0,0,0,{0,0,0,0,0,0,0,0}};
+#endif
+
 DWORD WINAPI XInputGetDSoundAudioDeviceGuids(DWORD index, GUID* render_guid, GUID* capture_guid)
 {
-    FIXME("(index %u, render guid %p, capture guid %p) Stub!\n", index, render_guid, capture_guid);
+#if XINPUT_SUPPORTED
+#if XINPUT_TRACE_INTERFACE_USE
+    TRACE("XInputGetDSoundAudioDeviceGuids(%d, %p, %p), pid=%i\n", index, render_guid, capture_guid, getpid());
+#endif
+
+    (void)render_guid;
+    (void)capture_guid;
 
-    if (index >= XUSER_MAX_COUNT)
+    if(index >= XUSER_MAX_COUNT)
+    {
         return ERROR_BAD_ARGUMENTS;
-    if (!controllers[index].connected)
+    }
+
+    if(!xinput_gamepad_connected(index))
+    {
         return ERROR_DEVICE_NOT_CONNECTED;
+    }
 
+    *render_guid = guid_null;
+    *capture_guid = guid_null;
+
+    return ERROR_SUCCESS;
+
+    /* return ERROR_NOT_SUPPORTED; */
+#else
+    TRACE("XInputGetDSoundAudioDeviceGuids(%d, %p, %p)\n", index, render_guid, capture_guid);
     return ERROR_NOT_SUPPORTED;
+#endif
 }
 
 DWORD WINAPI XInputGetBatteryInformation(DWORD index, BYTE type, XINPUT_BATTERY_INFORMATION* battery)
 {
-    FIXME("(index %u, type %u, battery %p) Stub!\n", index, type, battery);
+#if XINPUT_SUPPORTED
+#if XINPUT_TRACE_INTERFACE_USE
+    TRACE("XInputGetBatteryInformation(%d, %hhi, %p), pid=%i\n", index, type, battery, getpid());
+#endif
+    (void)type;
 
-    if (index >= XUSER_MAX_COUNT)
+    if(index >= XUSER_MAX_COUNT)
+    {
         return ERROR_BAD_ARGUMENTS;
-    if (!controllers[index].connected)
+    }
+
+    if(xinput_gamepad_connected(index))
+    {
+        battery->BatteryType = BATTERY_TYPE_UNKNOWN;
+        battery->BatteryLevel = BATTERY_LEVEL_FULL;
+
+        return ERROR_SUCCESS;
+    }
+    else
+    {
+        battery->BatteryType = BATTERY_TYPE_DISCONNECTED;
+        battery->BatteryLevel = BATTERY_LEVEL_EMPTY;
         return ERROR_DEVICE_NOT_CONNECTED;
+    }
 
+#else
+    TRACE("XInputGetBatteryInformation(%d, %hhi, %p)\n", index, type, battery);
     return ERROR_NOT_SUPPORTED;
+#endif
+}
+
+void CALLBACK XInputServer(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow)
+{
+    (void)hwnd;
+    (void)hinst;
+    (void)lpszCmdLine;
+    (void)nCmdShow;
+
+    xinput_service_server();
 }
diff --git a/dlls/xinput1_3/xinput_debug.c b/dlls/xinput1_3/xinput_debug.c
new file mode 100644
index 0000000..23c0500
--- /dev/null
+++ b/dlls/xinput1_3/xinput_debug.c
@@ -0,0 +1,87 @@
+/*
+ * The Wine project - Xinput Joystick Library
+ * Copyright 2016 Eric Diaz Fernandez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+#include "xinput_settings.h"
+#include "wine/debug.h"
+
+#include <stdint.h>
+
+#include "xinput_debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(xinput);
+
+void hexdump(const void* buffer, size_t len)
+{
+    const uint8_t* p = (const uint8_t*)buffer;
+    const uint8_t* l = &p[len];
+    while(p < l)
+    {
+        TRACE("%02x ", *p);
+        ++p;
+    }
+}
+
+static const uint8_t* memdump_line(const uint8_t* buffer, size_t len, size_t width)
+{
+    const uint8_t* p = buffer;
+    const uint8_t* l = &p[len];
+    const uint8_t* w = &p[width];
+    TRACE("%p | ", buffer);
+    while(p < l)
+    {
+        TRACE("%02x ", *p);
+        ++p;
+    }
+    while(p < w)
+    {
+        TRACE("-- ");
+        ++p;
+    }
+    TRACE("| ");
+    p = buffer;
+    while(p < l)
+    {
+        char c = *p;
+        if((c < ' ') || (c >= 127))
+        {
+            c = '.';
+        }
+        TRACE("%c", c);
+        ++p;
+    }
+    TRACE("\n");
+
+    return p;
+}
+
+void memdump(const void* buffer, size_t len)
+{
+    const uint8_t* p = (const uint8_t*)buffer;
+    const size_t w = XINPUT_MEMDUMP_COLUMNS;
+
+    while(len > w)
+    {
+        p = memdump_line(p, w, w);
+        len -= w;
+    }
+    if(len > 0)
+    {
+        memdump_line(p, len, w);
+    }
+}
diff --git a/dlls/xinput1_3/xinput_debug.h b/dlls/xinput1_3/xinput_debug.h
new file mode 100644
index 0000000..6ae855c
--- /dev/null
+++ b/dlls/xinput1_3/xinput_debug.h
@@ -0,0 +1,35 @@
+/*
+ * The Wine project - Xinput Joystick Library
+ * Copyright 2016 Eric Diaz Fernandez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef XINPUT_DEBUG_H
+#define XINPUT_DEBUG_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void hexdump(const void* buffer, size_t len);
+void memdump(const void* buffer, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* XINPUT_DEBUG_H */
diff --git a/dlls/xinput1_3/xinput_device_id.h b/dlls/xinput1_3/xinput_device_id.h
new file mode 100644
index 0000000..5afb895
--- /dev/null
+++ b/dlls/xinput1_3/xinput_device_id.h
@@ -0,0 +1,39 @@
+/*
+ * The Wine project - Xinput Joystick Library
+ * Copyright 2016 Eric Diaz Fernandez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef XINPUT_DEVICE_ID_H
+#define XINPUT_DEVICE_ID_H
+
+#define MANUFACTURER_MICROSOFT          0x45e
+
+#define XBOX360_CONTROLLER              0x28e
+#define XBOX360_WIRELESS_CONTROLLER     0x28f
+#define XBOXONE_CONTROLLER              0x2d1
+#define XBOXONE_WIRELESS_CONTROLLER     0x2dd
+
+#define XBOX360_WIRELESS_ADAPTER        0x719
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* XINPUT_DEVICE_ID_H */
diff --git a/dlls/xinput1_3/xinput_gamepad.c b/dlls/xinput1_3/xinput_gamepad.c
new file mode 100644
index 0000000..98cb1a8
--- /dev/null
+++ b/dlls/xinput1_3/xinput_gamepad.c
@@ -0,0 +1,580 @@
+/*
+ * The Wine project - Xinput Joystick Library
+ * Copyright 2016 Eric Diaz Fernandez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+#include "xinput_settings.h"
+
+#include <pthread.h>
+#include <errno.h>
+
+#if XINPUT_USES_SEMAPHORE_MUTEX
+#include <fcntl.h>           /* For O_* constants */
+#include <sys/stat.h>        /* For mode constants */
+#include <semaphore.h>
+#endif
+
+#ifndef HAVE_MQ_OPEN
+#undef XINPUT_USES_MQUEUE
+#endif
+
+#if XINPUT_USES_MQUEUE
+#include <mqueue.h>
+#endif
+
+#include "wine/debug.h"
+#include "winerror.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>        /* For mode constants */
+#include <fcntl.h>           /* For O_* constants */
+#include <stdint.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "xinput.h"
+#include "xinput_gamepad.h"
+#include "xinput_service.h"
+#include "xinput_tools.h"
+#include "xinput_debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(xinput);
+
+static pthread_mutex_t xinput_gamepad_init_mtx = PTHREAD_MUTEX_INITIALIZER;
+static volatile int xinput_gamepad_init_done = 0;
+
+static xinput_shared_gamepad_state* client_shared = NULL;
+static int client_fd = -1;
+static int64_t client_last_probe = 0;
+
+#if XINPUT_USES_SEMAPHORE_MUTEX
+static sem_t*  client_sem = SEM_FAILED;
+#endif
+
+#if XINPUT_USES_MQUEUE
+static mqd_t client_mq = MQD_INVALID;
+#endif
+
+static BOOL xinput_gamepad_lock_open(void)
+{
+#if XINPUT_USES_SEMAPHORE_MUTEX
+    client_sem = sem_open(SERVICE_SEM_NAME, O_RDWR);
+    if(client_sem == SEM_FAILED)
+    {
+        int ret = errno;
+        TRACE("could not open semaphore: %s\n", strerror(ret));
+        return FALSE;
+    }
+#endif
+    return TRUE;
+}
+
+static void xinput_gamepad_lock_close(void)
+{
+#if XINPUT_USES_SEMAPHORE_MUTEX
+    if(client_sem != SEM_FAILED)
+    {
+        sem_close(client_sem);
+        client_sem = SEM_FAILED;
+    }
+#endif
+}
+
+static BOOL xinput_gamepad_lock(void)
+{
+#if XINPUT_USES_SEMAPHORE_MUTEX
+    return sem_trywait(client_sem) == 0;
+#else
+    return TRUE;
+#endif
+}
+
+static void xinput_gamepad_unlock(void)
+{
+#if XINPUT_USES_SEMAPHORE_MUTEX
+    sem_post(client_sem);
+#endif
+}
+
+#if XINPUT_USES_MQUEUE
+
+static BOOL xinput_gamepad_service_queue_open(void)
+{
+    mqd_t mq = mq_open(SERVICE_MSG_NAME, O_WRONLY);
+    if(mq == MQD_INVALID)
+    {
+        int ret = errno;
+        TRACE("could not open message queue: %s\n", strerror(ret));
+        return FALSE;
+    }
+    client_mq = mq;
+
+    return TRUE;
+}
+
+static void xinput_gamepad_service_queue_close(void)
+{
+    if(client_mq != MQD_INVALID)
+    {
+        mq_close(client_mq);
+        client_mq = MQD_INVALID;
+    }
+}
+
+#endif
+
+static void xinput_gamepad_service_disconnect(void)
+{
+#if XINPUT_USES_MQUEUE
+    xinput_gamepad_service_queue_close();
+#endif
+
+    xinput_gamepad_lock_close();
+
+    if(client_shared != NULL)
+    {
+        TRACE("disconnecting\n");
+
+        munmap(client_shared, sizeof(xinput_shared_gamepad_state));
+        client_shared = NULL;
+
+        close_ex(client_fd);
+        client_fd = -1;
+    }
+}
+
+/**
+ * Tells if the service is alive
+ *
+ * @return 0 if the service is alive, <0 if not.
+ */
+
+static int xinput_gamepad_service_is_alive(void)
+{
+    pid_t pid;
+    BOOL dead;
+
+#if !XINPUT_RUNDLL
+
+    if(xinput_service_self())
+    {
+        client_shared->poke_us = timeus();
+
+        TRACE("alive (self)\n");
+        return 0;
+    }
+#endif
+
+    /*  wait until the pid is set, or assumed to be broken */
+
+    TRACE("fetching master pid, shared@%p\n", client_shared);
+
+    /* give 5 tries to get the master */
+
+    for(int i = 5; i >= 0; --i)
+    {
+        /*memdump(client_shared, sizeof(xinput_shared_gamepad_state));*/
+
+        pid = client_shared->master_pid;
+
+        /* if the pid is 0, the shared memory has not been set yet */
+
+        if(pid != 0)
+        {
+            TRACE("owned by pid %i\n", pid);
+            break;
+        }
+
+        /*  this will only happen if the DLL has been loaded twice "at the same time" */
+
+        usleep(XINPUT_OWNER_PROBE_PERIOD_US);
+    }
+
+    TRACE("still owned by pid %i\n", pid);
+
+    /* owner 0 or "broken" means surely dead */
+
+    dead = (pid == 0) || (pid == XINPUT_OWNER_BROKEN);
+
+    if(!dead)
+    {
+        /* else, the pid has to be checked to see if it's still alive */
+
+        dead = kill(pid, 0) < 0;
+
+        if(dead)
+        {
+            int ret = errno;
+            TRACE("could not probe owner on pid: %s\n", strerror(ret));
+        }
+    }
+
+    if(dead)
+    {
+        TRACE("dead\n");
+
+        /*  dead */
+
+        /* tells the server needs to be spawned */
+
+        return -1;
+    }
+    else
+    {
+        client_shared->poke_us = timeus();
+
+        TRACE("alive\n");
+        /*  alive */
+        return 0;
+    }
+}
+
+static int xinput_gamepad_service_connect(void)
+{
+    xinput_shared_gamepad_state* state;
+    int ret;
+    int fd;
+
+    TRACE("connecting\n");
+
+    ret = shm_open(SERVICE_SHM_NAME, O_RDWR, 0666);
+
+    if(ret < 0)
+    {
+        ret = errno;
+        TRACE("could not connect: %i = %s\n", ret, strerror(ret));
+        return ret; /* ENOENT : will create */
+    }
+
+    fd = ret;
+
+    TRACE("mapping\n");
+
+    state = (xinput_shared_gamepad_state*)mmap(
+                NULL,
+                sizeof(xinput_shared_gamepad_state),
+                PROT_READ|PROT_WRITE, MAP_SHARED,
+                fd,
+                0);
+
+    if(state == NULL)
+    {
+        ret = errno;
+        close_ex(fd);
+        TRACE("could not map: %s\n", strerror(ret));
+        return ret;
+    }
+
+    memdump(state, sizeof(xinput_shared_gamepad_state));
+
+    client_shared = state;
+    client_fd = fd;
+
+    if(!xinput_gamepad_lock_open())
+    {
+        TRACE("error opening lock\n");
+
+        xinput_gamepad_service_disconnect();
+
+        return -1;  /* do not return ENOENT */
+    }
+
+#if XINPUT_USES_MQUEUE
+    if(!xinput_gamepad_service_queue_open())
+    {
+        TRACE("error opening queue\n");
+
+        xinput_gamepad_service_disconnect();
+
+        return -2;  /* do not return ENOENT */
+    }
+#endif
+
+    client_shared->poke_us = timeus();
+
+    TRACE("connected\n");
+
+    return ERROR_SUCCESS;
+}
+
+static void xinput_gamepad_service_probe(void)
+{
+    int64_t now;
+
+    xinput_gamepad_init();
+
+    now = timeus();
+
+    if(now - client_last_probe > XINPUT_OWNER_REPROBE_PERIOD_US)
+    {
+        if(xinput_gamepad_service_is_alive() < 0)
+        {
+            /* spawn the server and wait for a bit */
+
+            xinput_gamepad_service_disconnect();
+
+            xinput_service_rundll();
+            sleep(1);
+
+            xinput_gamepad_service_connect();
+        }
+        client_last_probe = now;
+    }
+}
+
+void xinput_gamepad_init(void)
+{
+    pthread_mutex_lock(&xinput_gamepad_init_mtx);
+    if(xinput_gamepad_init_done != 0)
+    {
+        pthread_mutex_unlock(&xinput_gamepad_init_mtx);
+        return;
+    }
+
+    xinput_gamepad_init_done = 1;
+    pthread_mutex_unlock(&xinput_gamepad_init_mtx);
+
+    TRACE("initializing\n");
+
+    xinput_service_rundll();
+
+    for(;;)
+    {
+        int ret = xinput_gamepad_service_connect();
+
+        if(ret == 0)
+        {
+            /*
+             * The IPCs are set, but there may be nobody on the
+             * other side
+             */
+
+            if(xinput_gamepad_service_is_alive() >= 0)
+            {
+                break;
+            }
+            else
+            {
+                /*  the server is dead : retry */
+
+                xinput_gamepad_service_disconnect();
+
+                xinput_service_rundll();
+                sleep(1);
+
+                continue;
+            }
+        }
+
+        if(ret == ENOENT)
+        {
+            TRACE("starting server\n");
+            xinput_service_rundll();
+            sleep(1);
+        }
+        else if(ret < 0)
+        {
+            TRACE("could not connect to IPCs: %i", ret);
+        }
+        else
+        {
+            TRACE("failed to connect: %s\n", strerror(ret));
+        }
+
+        usleep(100000);
+    }
+
+    TRACE("initialized\n");
+}
+
+void xinput_gamepad_finalize(void)
+{
+    pthread_mutex_lock(&xinput_gamepad_init_mtx);
+    if(xinput_gamepad_init_done == 0)
+    {
+        pthread_mutex_unlock(&xinput_gamepad_init_mtx);
+
+        TRACE("not initialized\n");
+        return;
+    }
+
+    xinput_gamepad_init_done = 0;
+    pthread_mutex_unlock(&xinput_gamepad_init_mtx);
+
+    TRACE("finalizing\n");
+
+    xinput_gamepad_service_disconnect();
+
+    TRACE("finalized\n");
+}
+
+BOOL xinput_gamepad_connected(int index)
+{
+    xinput_gamepad_state* xgs;
+    BOOL ret = FALSE;
+
+    xinput_gamepad_service_probe();
+
+    if(client_shared == NULL)
+    {
+        TRACE("shared memory not mapped, initialized=%i\n", xinput_gamepad_init_done);
+        return ret;
+    }
+
+    xgs = &client_shared->state[index];
+
+    if(xinput_gamepad_lock())
+    {
+        ret = xgs->connected;
+        xinput_gamepad_unlock();
+    }
+
+    return ret;
+}
+
+static DWORD xinput_gamepad_axis_to_buttons(SHORT value, DWORD negative, DWORD positive)
+{
+    if(value < -THUMB_TO_BUTTONS_THRESHOLD)
+    {
+        return negative;
+    }
+    else if(value > THUMB_TO_BUTTONS_THRESHOLD)
+    {
+        return positive;
+    }
+    else
+    {
+        return 0;
+    }
+}
+
+BOOL xinput_gamepad_copy_buttons_state(int index, DWORD* out_buttons)
+{
+    xinput_gamepad_state* xgs;
+    XINPUT_GAMEPAD gamepad;
+    DWORD buttons;
+
+    if(out_buttons == NULL)
+    {
+        return FALSE;
+    }
+
+    xinput_gamepad_service_probe();
+    xgs = &client_shared->state[index];
+
+    if(xinput_gamepad_lock())
+    {
+        memcpy(&gamepad, &xgs->gamepad, sizeof(XINPUT_GAMEPAD));
+
+        xinput_gamepad_unlock();
+
+        buttons = gamepad.wButtons;
+        buttons |= xinput_gamepad_axis_to_buttons(gamepad.sThumbLX, XINPUT_GAMEPAD_LTHUMB_LEFT, XINPUT_GAMEPAD_LTHUMB_RIGHT);
+        buttons |= xinput_gamepad_axis_to_buttons(gamepad.sThumbLY, XINPUT_GAMEPAD_LTHUMB_DOWN, XINPUT_GAMEPAD_LTHUMB_UP);
+        buttons |= xinput_gamepad_axis_to_buttons(gamepad.sThumbRX, XINPUT_GAMEPAD_RTHUMB_LEFT, XINPUT_GAMEPAD_RTHUMB_RIGHT);
+        buttons |= xinput_gamepad_axis_to_buttons(gamepad.sThumbRY, XINPUT_GAMEPAD_RTHUMB_DOWN, XINPUT_GAMEPAD_RTHUMB_UP);
+
+        if(gamepad.bLeftTrigger > TRIGGER_TO_BUTTONS_THRESHOLD)
+        {
+            buttons |= XINPUT_GAMEPAD_LTRIGGER;
+        }
+        if(gamepad.bRightTrigger > TRIGGER_TO_BUTTONS_THRESHOLD)
+        {
+            buttons |= XINPUT_GAMEPAD_RTRIGGER;
+        }
+
+        *out_buttons = buttons;
+
+        return TRUE;
+    }
+    else
+    {
+        return FALSE;
+    }
+}
+
+void xinput_gamepad_copy_state(int index, XINPUT_STATE* out_state)
+{
+    xinput_gamepad_state* xgs;
+
+    xinput_gamepad_service_probe();
+    xgs = &client_shared->state[index];
+
+    if(xinput_gamepad_lock())
+    {
+        memcpy(&out_state->Gamepad, &xgs->gamepad, sizeof(XINPUT_GAMEPAD));
+        out_state->dwPacketNumber = xgs->dwPacketNumber;
+        xinput_gamepad_unlock();
+    }
+    /*  If you say so ... : */
+    /* The main difference between this and the Ex version is the media guide button */
+
+    out_state->Gamepad.wButtons &= ~XINPUT_GAMEPAD_GUIDE;
+}
+
+void xinput_gamepad_copy_state_ex(int index, XINPUT_STATE_EX* out_state)
+{
+    xinput_gamepad_state* xgs;
+
+    xinput_gamepad_service_probe();
+    xgs = &client_shared->state[index];
+
+    if(xinput_gamepad_lock())
+    {
+        memcpy(&out_state->Gamepad, &xgs->gamepad, sizeof(XINPUT_GAMEPAD));
+        out_state->dwPacketNumber = xgs->dwPacketNumber;
+        xinput_gamepad_unlock();
+    }
+}
+
+void xinput_gamepad_rumble(int index, const XINPUT_VIBRATION *vibration)
+{
+#if XINPUT_USES_MQUEUE
+    xinput_gamepad_vibration vibration_message;
+
+    if(vibration == NULL)
+    {
+        return;
+    }
+
+    xinput_gamepad_service_probe();
+
+    vibration_message.index = index;
+    vibration_message.vibration = *vibration;
+
+    for(;;)
+    {
+        if(mq_send(client_mq, (const char*)&vibration_message, sizeof(xinput_gamepad_vibration), 0) == 0)
+        {
+            break;
+        }
+        else
+        {
+            int err = errno;
+
+            if((err == EINTR) && (err != EAGAIN))
+            {
+                break;
+            }
+        }
+    }
+#endif
+}
diff --git a/dlls/xinput1_3/xinput_gamepad.h b/dlls/xinput1_3/xinput_gamepad.h
new file mode 100644
index 0000000..eb9339e
--- /dev/null
+++ b/dlls/xinput1_3/xinput_gamepad.h
@@ -0,0 +1,108 @@
+/*
+ * The Wine project - Xinput Joystick Library
+ * Copyright 2016 Eric Diaz Fernandez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef XINPUT_GAMEPAD_H
+#define XINPUT_GAMEPAD_H
+
+#include "xinput.h"
+
+/*
+ * note: moved from the original xinput1_3_main.c:
+ *
+ *  Not defined in the headers, used only by XInputGetStateEx
+ */
+#define XINPUT_GAMEPAD_GUIDE 0x0400
+
+/* So, this would mean ... */
+#define VK_PAD_GUIDE         0x5808
+
+#define XINPUT_GAMEPAD_LTRIGGER             0x00010000
+#define XINPUT_GAMEPAD_RTRIGGER             0x00020000
+#define XINPUT_GAMEPAD_RTRIGGER             0x00020000
+#define XINPUT_GAMEPAD_LTHUMB_UP            0x00100000
+#define XINPUT_GAMEPAD_LTHUMB_RIGHT         0x00200000
+#define XINPUT_GAMEPAD_LTHUMB_DOWN          0x00400000
+#define XINPUT_GAMEPAD_LTHUMB_LEFT          0x00800000
+#define XINPUT_GAMEPAD_RTHUMB_UP            0x01000000
+#define XINPUT_GAMEPAD_RTHUMB_RIGHT         0x02000000
+#define XINPUT_GAMEPAD_RTHUMB_DOWN          0x04000000
+#define XINPUT_GAMEPAD_RTHUMB_LEFT          0x08000000
+
+#define XINPUT_GAMEPAD_LTHUMB_MASK          (XINPUT_GAMEPAD_LTHUMB_UP|    \
+                                             XINPUT_GAMEPAD_LTHUMB_RIGHT| \
+                                             XINPUT_GAMEPAD_LTHUMB_DOWN|  \
+                                             XINPUT_GAMEPAD_LTHUMB_LEFT)
+
+#define XINPUT_GAMEPAD_RTHUMB_MASK          (XINPUT_GAMEPAD_RTHUMB_UP|    \
+                                             XINPUT_GAMEPAD_RTHUMB_RIGHT| \
+                                             XINPUT_GAMEPAD_RTHUMB_DOWN|  \
+                                             XINPUT_GAMEPAD_RTHUMB_LEFT)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct xinput_gamepad_device;
+
+struct xinput_gamepad_device_vtbl
+{
+    int (*read)(struct xinput_gamepad_device* device);
+    void (*update)(struct xinput_gamepad_device* device, XINPUT_GAMEPAD_EX* gamepad, XINPUT_VIBRATION* vibration);
+    int (*rumble)(struct xinput_gamepad_device* device, const XINPUT_VIBRATION* vibration);
+    void (*release)(struct xinput_gamepad_device* device);
+};
+
+typedef struct xinput_gamepad_device_vtbl xinput_gamepad_device_vtbl;
+
+struct xinput_gamepad_device
+{
+    void* data;
+    const xinput_gamepad_device_vtbl* vtbl;
+};
+
+typedef struct xinput_gamepad_device xinput_gamepad_device;
+
+/**
+ * A tool structure to easily store the supported devices
+ */
+
+struct xinput_driver_supported_device
+{
+    const char* name;
+    void (*initialize)(struct xinput_gamepad_device* instance, int fd);
+
+    const WORD vendor;
+    const WORD product;
+};
+
+typedef struct xinput_driver_supported_device xinput_driver_supported_device;
+
+void xinput_gamepad_init(void);
+void xinput_gamepad_finalize(void);
+
+BOOL xinput_gamepad_connected(int index);
+BOOL xinput_gamepad_copy_buttons_state(int index, DWORD* out_buttons);
+void xinput_gamepad_copy_state(int index, XINPUT_STATE* out_state);
+void xinput_gamepad_copy_state_ex(int index, XINPUT_STATE_EX* out_state);
+void xinput_gamepad_rumble(int index, const XINPUT_VIBRATION *vibration);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* XINPUT_GAMEPAD_H */
diff --git a/dlls/xinput1_3/xinput_linux_input.c b/dlls/xinput1_3/xinput_linux_input.c
new file mode 100644
index 0000000..e89df24
--- /dev/null
+++ b/dlls/xinput1_3/xinput_linux_input.c
@@ -0,0 +1,437 @@
+/*
+ * The Wine project - Xinput Joystick Library
+ * Copyright 2016 Eric Diaz Fernandez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+#include "xinput_settings.h"
+
+#if HAVE_LINUX_INPUT_H
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "wine/debug.h"
+
+#include "xinput.h"
+#include "xinput_debug.h"
+#include "xinput_tools.h"
+
+#include "xinput_linux_input.h"
+#include "xinput_linux_input_xboxpad.h"
+
+#include "xinput_linux_input_xboxpad_2.h" /* an example with table implementation */
+
+#include "xinput_linux_input_debug.h"
+
+#define JOY_MAX 32
+
+WINE_DEFAULT_DEBUG_CHANNEL(xinput);
+
+struct XINPUT_GAMEPAD_PRIVATE_STATE
+{
+   xinput_gamepad_device device;
+   int input_index;
+};
+
+typedef struct XINPUT_GAMEPAD_PRIVATE_STATE XINPUT_GAMEPAD_PRIVATE_STATE;
+
+static XINPUT_GAMEPAD_PRIVATE_STATE xinput_linux_input_slot[XUSER_MAX_COUNT];
+
+static uint64_t xinput_linux_input_probe_last_epoch = 0;
+
+static int xinput_linux_input_next_free_slot(void)
+{
+    for(int i = 0; i < XUSER_MAX_COUNT; ++i)
+    {
+        if(xinput_linux_input_slot[i].device.vtbl == NULL)
+        {
+            return i;
+        }
+    }
+    return -1;
+}
+
+int xinput_linux_input_read_next(int fd, struct input_event* ie)
+{
+    return read_fully(fd, ie, sizeof(*ie));
+}
+
+/**
+ * The left motor is supposed to be low frequency, high magnitude
+ * The right motor is supposed to be high frequency, weak magnitude
+ *
+ * @param fd
+ * @param id the current effect id, or -1
+ * @param low
+ * @param high
+ *
+ * @return the id of the effect or -1 if it failed to register the effect
+ */
+
+int xinput_linux_input_rumble(int fd, int id, SHORT low_left, SHORT high_right)
+{
+    struct ff_effect effect;
+
+    effect.type = FF_RUMBLE;
+    effect.id = id;
+    effect.u.rumble.strong_magnitude = low_left;
+    effect.u.rumble.weak_magnitude = high_right;
+    effect.replay.length = 5000;
+    effect.replay.delay = 0;
+
+    if(ioctl(fd, EVIOCSFF, &effect) != -1)
+    {
+        struct input_event ie;
+        ie.type = EV_FF;
+        ie.code = effect.id;
+        ie.value = 1;
+
+        if(write_fully(fd, &ie, sizeof(ie)) < 0)
+        {
+            int err = errno;
+            TRACE("could not send rumble: %i [%i, %i]: %s\n", fd, low_left, high_right, strerror(err));
+        }
+    }
+    else
+    {
+        int err = errno;
+        TRACE("could not setup rumble: %i [%i, %i]: %s\n", fd, low_left, high_right, strerror(err));
+    }
+
+    return effect.id;
+}
+
+void xinput_linux_input_feedback_clear(int fd, int id)
+{
+    if(ioctl(fd, EVIOCRMFF, id) == -1)
+    {
+        int err = errno;
+        TRACE("could not clear effect %i: %s\n", id, strerror(err));
+    }
+}
+
+static BOOL xinput_linux_input_device_in_use(int input_index)
+{
+    for(int i = 0; i < XUSER_MAX_COUNT; ++i)
+    {
+        if(xinput_linux_input_slot[i].input_index == input_index)
+        {
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+/**
+ * Linux input
+ *
+ * @return
+ */
+
+uint32_t xinput_linux_input_probe(void)
+{
+    uint64_t now = timeus();
+    int n;
+    int fd;
+    int version;
+    int abs_count;
+    int key_count;
+    int ff_count;
+
+    uint32_t mask = 0;
+    struct input_id id;
+
+    uint8_t prop[INPUT_PROP_MAX];
+    uint8_t ev_all[EV_CNT>>3];
+    uint8_t ev_key[KEY_CNT>>3];
+    uint8_t ev_abs[ABS_CNT>>3];
+    uint8_t ev_ff[FF_CNT>>3];
+    char device_name[128];
+    char location[128];
+    char filename[128];
+
+    if(now - xinput_linux_input_probe_last_epoch < 5000000)
+    {
+        return 0;
+    }
+
+    xinput_linux_input_probe_last_epoch = now;
+
+#if XINPUT_TRACE_DEVICE_DETECTION
+    TRACE("probing devices\n");
+#endif
+
+    for(int input_index = 0; input_index < JOY_MAX; ++input_index)
+    {
+        int slot = xinput_linux_input_next_free_slot();
+
+        if(slot < 0)
+        {
+            TRACE("no more slot available (whoopsie)\n");
+            break;
+        }
+
+        /* already in use */
+
+        if(xinput_linux_input_device_in_use(input_index))
+        {
+            continue;
+        }
+
+        snprintf(filename, sizeof(filename), "/dev/input/event%i", input_index);
+
+        fd = open(filename, O_RDWR);
+
+        if(fd < 0)
+        {
+            /*  TRACE("no joystick at %s\n", filename); */
+            continue;
+        }
+
+#if XINPUT_TRACE_DEVICE_DETECTION
+        TRACE("opened joystick at %s\n", filename);
+#endif
+
+        if(ioctl(fd, EVIOCGVERSION, &version) != -1)
+        {
+#if XINPUT_TRACE_DEVICE_DETECTION
+            TRACE("version: %x\n", version);
+#endif
+        }
+
+        if(ioctl(fd, EVIOCGID, &id) != -1)
+        {
+#if XINPUT_TRACE_DEVICE_DETECTION
+            TRACE("id: %x %x %x %x\n", id.bustype, id.product, id.vendor, id.version);
+#endif
+        }
+
+        if(ioctl(fd, EVIOCGNAME(sizeof(device_name)), device_name) != -1)
+        {
+#if XINPUT_TRACE_DEVICE_DETECTION
+            TRACE("name: '%s'\n", device_name);
+#endif
+        }
+
+        if(ioctl(fd, EVIOCGPHYS(sizeof(location)), location) != -1)
+        {
+#if XINPUT_TRACE_DEVICE_DETECTION
+            TRACE("loc: '%s'\n", location);
+#endif
+        }
+
+        if((n = ioctl(fd, EVIOCGPROP(sizeof(prop)), prop)) != -1)
+        {
+#if XINPUT_TRACE_DEVICE_DETECTION
+            TRACE("prop: %i\n", n);
+            hexdump(prop, n);
+            TRACE("\n");
+#endif
+        }
+
+        if((n = ioctl(fd, EVIOCGBIT(0, EV_CNT), ev_all)) != -1)
+        {
+#if XINPUT_TRACE_DEVICE_DETECTION
+            memdump(ev_all, sizeof(ev_all));
+
+            for(int j = 0; j < EV_CNT; ++j)
+            {
+                BOOL on = bit_get(ev_all, j);
+                if(on)
+                {
+                    TRACE("%s ", xinput_linux_input_event_type_get_name(j)); /* no LF */
+                }
+            }
+#endif
+        }
+        else
+        {
+#if XINPUT_TRACE_DEVICE_DETECTION
+            TRACE("ev all: %s\n", strerror(errno));
+#endif
+        }
+
+        if(!(bit_get(ev_all, EV_KEY) && bit_get(ev_all, EV_ABS)))
+        {
+            TRACE("%s @%s: no buttons nor absolute axe(s)\n",
+                    device_name,
+                    filename);
+            close_ex(fd);
+            continue;
+        }
+
+        key_count = 0;
+        abs_count = 0;
+        ff_count = 0;
+
+        if((n = ioctl(fd, EVIOCGBIT(EV_KEY, KEY_CNT), ev_key)) != -1)
+        {
+#if XINPUT_TRACE_DEVICE_DETECTION
+            memdump(ev_key, sizeof(ev_key));
+#endif
+            for(int j = 0; j < KEY_CNT; ++j)
+            {
+                BOOL on = bit_get(ev_key, j);
+                if(on)
+                {
+#if XINPUT_TRACE_DEVICE_DETECTION
+                    TRACE("%s ", xinput_linux_input_key_get_name(j)); /* no LF */
+#endif
+                    ++key_count;
+                }
+            }
+
+#if XINPUT_TRACE_DEVICE_DETECTION
+            TRACE(": %i keys\n", key_count);
+#endif
+        }
+        else
+        {
+#if XINPUT_TRACE_DEVICE_DETECTION
+            TRACE("ev key: %s\n", strerror(errno));
+#endif
+        }
+
+        if((n = ioctl(fd, EVIOCGBIT(EV_ABS, ABS_CNT), ev_abs)) != -1)
+        {
+#if XINPUT_TRACE_DEVICE_DETECTION
+            memdump(ev_abs, sizeof(ev_abs));
+#endif
+            for(int j = 0; j < ABS_CNT; ++j)
+            {
+                BOOL on = bit_get(ev_abs, j);
+                if(on)
+                {
+#if XINPUT_TRACE_DEVICE_DETECTION
+                    TRACE("%s ", xinput_linux_input_abs_get_name(j)); /* no LF */
+#endif
+                    ++abs_count;
+                }
+            }
+#if XINPUT_TRACE_DEVICE_DETECTION
+            TRACE(": %i abs\n", abs_count);
+#endif
+        }
+        else
+        {
+#if XINPUT_TRACE_DEVICE_DETECTION
+            TRACE("ev abs: %s\n", strerror(errno));
+#endif
+        }
+
+        if((n = ioctl(fd, EVIOCGBIT(EV_FF, FF_CNT), ev_ff)) != -1)
+        {
+#if XINPUT_TRACE_DEVICE_DETECTION
+            memdump(ev_ff, sizeof(ev_ff));
+#endif
+            for(int j = 0; j < FF_CNT; ++j)
+            {
+                BOOL on = bit_get(ev_ff, j);
+                if(on)
+                {
+#if XINPUT_TRACE_DEVICE_DETECTION
+                    TRACE("%s ", xinput_linux_input_ff_get_name(j)); /* no LF */
+#endif
+                    ++ff_count;
+                }
+            }
+#if XINPUT_TRACE_DEVICE_DETECTION
+            TRACE(": %i ff\n", ff_count);
+#endif
+        }
+
+        if(ioctl(fd, EVIOCGRAB, 1) < 0)
+        {
+            /* cannot grab it for myself */
+            TRACE("cannot grab device: %s\n", strerror(errno));
+
+            continue;
+        }
+
+        if(xinput_linux_input_xboxpad_can_translate(&id))
+        {
+            xinput_gamepad_device* device = &xinput_linux_input_slot[slot].device;
+            xinput_linux_input_xboxpad_new_instance(&id, fd, device);
+            xinput_linux_input_slot[slot].input_index = input_index;
+        }
+        /*
+        else if(xinput_linux_input_xboxpad2_can_translate(&id))
+        {
+            xinput_gamepad_device* device = &xinput_linux_input_slot[slot].device;
+            xinput_linux_input_xboxpad2_new_instance(&id, fd, device);
+            xinput_linux_input_slot[slot].input_index = input_index;
+        }
+        */
+        else
+        {
+            /* no more translators */
+
+            continue;
+        }
+
+        mask |= 1 << slot;
+    }
+
+#if XINPUT_TRACE_DEVICE_DETECTION
+    TRACE("devices probed\n");
+#endif
+
+    return mask;
+}
+
+xinput_gamepad_device* xinput_linux_input_get_device(int slot)
+{
+    if(slot >= 0 && slot < XUSER_MAX_COUNT)
+    {
+        if(xinput_linux_input_slot[slot].device.vtbl != NULL)
+        {
+            return &xinput_linux_input_slot[slot].device;
+        }
+    }
+
+    return NULL;
+}
+
+void xinput_linux_input_device_close(int slot)
+{
+    xinput_gamepad_device* device = xinput_linux_input_get_device(slot);
+
+    if(device != NULL)
+    {
+        device->vtbl->release(device);
+
+        xinput_linux_input_slot[slot].device.data = NULL;
+        xinput_linux_input_slot[slot].device.vtbl = NULL;
+        xinput_linux_input_slot[slot].input_index = -1;
+    }
+}
+
+void xinput_linux_input_initialize(void)
+{
+    for(int slot = 0; slot < XUSER_MAX_COUNT; ++slot)
+    {
+        xinput_linux_input_device_close(slot);
+    }
+}
+
+void xinput_linux_input_finalize(void)
+{
+}
+
+#endif /* HAVE_LINUX_INPUT_H */
diff --git a/dlls/xinput1_3/xinput_linux_input.h b/dlls/xinput1_3/xinput_linux_input.h
new file mode 100644
index 0000000..134d466
--- /dev/null
+++ b/dlls/xinput1_3/xinput_linux_input.h
@@ -0,0 +1,103 @@
+/*
+ * The Wine project - Xinput Joystick Library
+ * Copyright 2016 Eric Diaz Fernandez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef XINPUT_LINUX_INPUT_H
+#define XINPUT_LINUX_INPUT_H
+
+#include <stdint.h>
+#include "xinput_gamepad.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Initialises the linux input
+ *
+ */
+
+void xinput_linux_input_initialize(void);
+
+/**
+ * Probes for new devices.
+ *
+ * @return a bitmask of the newly found devices
+ */
+
+uint32_t xinput_linux_input_probe(void);
+
+/**
+ * Returns the device in the specified slot
+ *
+ * @param slot
+ * @return
+ */
+
+xinput_gamepad_device* xinput_linux_input_get_device(int slot);
+
+/**
+ * Closes the device in the specified slot
+ *
+ * @param slot
+ */
+
+void xinput_linux_input_device_close(int slot);
+
+/**
+ * Cleans-up the linux input
+ *
+ */
+
+void xinput_linux_input_finalize(void);
+
+/**
+ * If multiple driver support is implemented, this will be replaced
+ * by a function setting a virtual table.
+ * Until then, this basic mapping will do.
+ */
+
+#define xinput_driver_initialize xinput_linux_input_initialize
+#define xinput_driver_probe xinput_linux_input_probe
+#define xinput_driver_get_device xinput_linux_input_get_device
+#define xinput_driver_device_close xinput_linux_input_device_close
+#define xinput_driver_finalize xinput_linux_input_finalize
+
+struct input_event;
+int xinput_linux_input_read_next(int fd, struct input_event* ie);
+
+/**
+ * The left motor is supposed to be low frequency, high magnitude
+ * The right motor is supposed to be high frequency, weak magnitude
+ *
+ * @param fd
+ * @param id the current effect id, or -1
+ * @param low
+ * @param high
+ *
+ * @return the id of the effect or -1 if it failed to register the effect
+ */
+
+int xinput_linux_input_rumble(int fd, int id, SHORT low_left, SHORT high_right);
+
+void xinput_linux_input_feedback_clear(int fd, int id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* XINPUT_LINUX_INPUT_H */
diff --git a/dlls/xinput1_3/xinput_linux_input_debug.c b/dlls/xinput1_3/xinput_linux_input_debug.c
new file mode 100644
index 0000000..a33a80b
--- /dev/null
+++ b/dlls/xinput1_3/xinput_linux_input_debug.c
@@ -0,0 +1,262 @@
+/*
+ * The Wine project - Xinput Joystick Library
+ * Copyright 2016 Eric Diaz Fernandez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+
+#if HAVE_LINUX_INPUT_H
+
+#include "xinput_settings.h"
+
+#include <linux/input.h>
+
+static const char* EVENT_TYPES_STR[] =
+{
+    "SYN",          /*  0 */
+    "KEY",
+    "REL",
+    "ABS",
+    "MSC",
+    "SW",           /*  5 */
+    "0x06",
+    "0x07",
+    "0x08",
+    "0x09",
+    "0x0a",
+    "0x0b",
+    "0x0c",
+    "0x0d",
+    "0x0e",
+    "0x0f",
+    "0x10",
+    "LED",          /*  0x11 */
+    "SND",
+    "0x13",
+    "REP",          /*  0x14 */
+    "FF",
+    "PWR",
+    "FF_STATUS",    /*  0x17 */
+};
+
+const char* xinput_linux_input_event_type_get_name(int idx)
+{
+    if(idx >= EV_SYN && idx <= EV_FF_STATUS)
+    {
+        return EVENT_TYPES_STR[idx];
+    }
+    else
+    {
+        return "?";
+    }
+}
+
+static const char* KEY_NAMES_STR[] =
+{
+    "BTN_0",        /*  0x100 */
+    "BTN_1",        /*  0x101 */
+    "BTN_2",        /*  0x102 */
+    "BTN_3",        /*  0x103 */
+    "BTN_4",        /*  0x104 */
+    "BTN_5",        /*  0x105 */
+    "BTN_6",        /*  0x106 */
+    "BTN_7",        /*  0x107 */
+    "BTN_8",        /*  0x108 */
+    "BTN_9",        /*  0x109 */
+    "0x10a",
+    "0x10b",
+    "0x10c",
+    "0x10d",
+    "0x10e",
+    "0x10f",
+    "BTN_LEFT",     /*  0x110 */
+    "BTN_RIGHT",    /*  0x111 */
+    "BTN_MIDDLE",   /*  0x112 */
+    "BTN_SIDE",     /*  0x113 */
+    "BTN_EXTRA",    /*  0x114 */
+    "BTN_FORWARD",  /*  0x115 */
+    "BTN_BACK",     /*  0x116 */
+    "BTN_TASK",     /*  0x117 */
+    "0x118",
+    "0x119",
+    "0x11a",
+    "0x11b",
+    "0x11c",
+    "0x11d",
+    "0x11e",
+    "0x11f",
+    "BTN_TRIGGER",  /*  0x120 */
+    "BTN_THUMB",    /*  0x121 */
+    "BTN_THUMB2",   /*  0x122 */
+    "BTN_TOP",      /*  0x123 */
+    "BTN_TOP2",     /*  0x124 */
+    "BTN_PINKIE",   /*  0x125 */
+    "BTN_BASE",     /*  0x126 */
+    "BTN_BASE2",    /*  0x127 */
+    "BTN_BASE3",    /*  0x128 */
+    "BTN_BASE4",    /*  0x129 */
+    "BTN_BASE5",    /*  0x12a */
+    "BTN_BASE6",    /*  0x12b */
+    "0x12c",
+    "0x12d",
+    "0x12e",
+    "BTN_DEAD",     /*  0x12f */
+
+    "BTN_SOUTH/A",  /*  0x130 */
+    "BTN_EAST/B",   /*  0x131 */
+    "BTN_C",        /*  0x132 */
+    "BTN_NORTH/X",  /*  0x133 */
+    "BTN_WEST/Y",   /*  0x134 */
+    "BTN_Z",        /*  0x135 */
+    "BTN_TL",       /*  0x136 */
+    "BTN_TR",       /*  0x137 */
+    "BTN_TL2",      /*  0x138 */
+    "BTN_TR2",      /*  0x139 */
+    "BTN_SELECT",   /*  0x13a */
+    "BTN_START",    /*  0x13b */
+    "BTN_MODE",     /*  0x13c */
+    "BTN_THUMBL",   /*  0x13d */
+    "BTN_THUMBR",   /*  0x13e */
+    "0x13f"         /*  0x13f */
+};
+
+const char* xinput_linux_input_key_get_name(int idx)
+{
+    if(idx >= BTN_MISC && idx <= BTN_DIGI)
+    {
+        return KEY_NAMES_STR[idx - BTN_MISC];
+    }
+    else
+    {
+        return "?";
+    }
+}
+
+static const char* ABS_NAMES_STR[] =
+{
+    "ABS_X",                /* 0x00 */
+    "ABS_Y",                /* 0x01 */
+    "ABS_Z",                /* 0x02 */
+    "ABS_RX",               /* 0x03 */
+    "ABS_RY",               /* 0x04 */
+    "ABS_RZ",               /* 0x05 */
+    "ABS_THROTTLE",         /* 0x06 */
+    "ABS_RUDDER",           /* 0x07 */
+    "ABS_WHEEL",            /* 0x08 */
+    "ABS_GAS",              /* 0x09 */
+    "ABS_BRAKE",            /* 0x0a */
+    "0x0b",
+    "0x0c",
+    "0x0d",
+    "0x0e",
+    "0x0f",
+    "ABS_HAT0X",            /* 0x10 */
+    "ABS_HAT0Y",            /* 0x11 */
+    "ABS_HAT1X",            /* 0x12 */
+    "ABS_HAT1Y",            /* 0x13 */
+    "ABS_HAT2X",            /* 0x14 */
+    "ABS_HAT2Y",            /* 0x15 */
+    "ABS_HAT3X",            /* 0x16 */
+    "ABS_HAT3Y",            /* 0x17 */
+    "ABS_PRESSURE",         /* 0x18 */
+    "ABS_DISTANCE",         /* 0x19 */
+    "ABS_TILT_X",           /* 0x1a */
+    "ABS_TILT_Y",           /* 0x1b */
+    "ABS_TOOL_WIDTH",       /* 0x1c */
+    "0x1d",
+    "0x1e",
+    "0x1f",
+    "ABS_VOLUME",           /* 0x20 */
+    "0x21",
+    "0x22",
+    "0x23",
+    "0x24",
+    "0x25",
+    "0x26",
+    "0x27",
+    "ABS_MISC",             /* 0x28 */
+    "0x29",
+    "0x2a",
+    "0x2b",
+    "0x2c",
+    "0x2d",
+    "0x2e",
+    "ABS_MT_SLOT",           /* 0x2f     MT slot being modified */
+    "ABS_MT_TOUCH_MAJOR",    /* 0x30     Major axis of touching ellipse */
+    "ABS_MT_TOUCH_MINOR",    /* 0x31     Minor axis (omit if circular) */
+    "ABS_MT_WIDTH_MAJOR",    /* 0x32     Major axis of approaching ellipse */
+    "ABS_MT_WIDTH_MINOR",    /* 0x33     Minor axis (omit if circular) */
+    "ABS_MT_ORIENTATION",    /* 0x34     Ellipse orientation */
+    "ABS_MT_POSITION_X",     /* 0x35     Center X touch position */
+    "ABS_MT_POSITION_Y",     /* 0x36     Center Y touch position */
+    "ABS_MT_TOOL_TYPE",      /* 0x37     Type of touching device */
+    "ABS_MT_BLOB_ID",        /* 0x38     Group a set of packets as a blob */
+    "ABS_MT_TRACKING_ID",    /* 0x39     Unique ID of initiated contact */
+    "ABS_MT_PRESSURE",       /* 0x3a     Pressure on contact area */
+    "ABS_MT_DISTANCE",       /* 0x3b     Contact hover distance */
+    "ABS_MT_TOOL_X",         /* 0x3c     Center X tool position */
+    "ABS_MT_TOOL_Y"          /* 0x3d     Center Y tool position */
+};
+
+const char* xinput_linux_input_abs_get_name(int idx)
+{
+    if(idx >= ABS_X && idx <= ABS_MT_TOOL_Y)
+    {
+        return ABS_NAMES_STR[idx];
+    }
+    else
+    {
+        return "?";
+    }
+}
+
+static const char* FF_NAMES_STR[] = /* [ 0x50: 0x61 ] */
+{
+    "FF_RUMBLE",    /* 0x50 */
+    "FF_PERIODIC",
+    "FF_CONSTANT",
+    "FF_SPRING",
+    "FF_FRICTION",
+    "FF_DAMPER",
+    "FF_INERTIA",
+    "FF_RAMP",      /* 0x57 */
+
+    "FF_SQUARE",
+    "FF_TRIANGLE",
+    "FF_SINE",
+    "FF_SAW_UP",
+    "FF_SAW_DOWN",
+    "FF_CUSTOM",
+    "0x5e",
+    "0x5f",
+    "FF_GAIN",      /* 0x60 */
+    "FF_AUTOCENTER"
+};
+
+const char* xinput_linux_input_ff_get_name(int idx)
+{
+    if(idx >= FF_RUMBLE && idx <= FF_AUTOCENTER)
+    {
+        return FF_NAMES_STR[idx - FF_RUMBLE];
+    }
+    else
+    {
+        return "?";
+    }
+}
+
+
+#endif /* HAVE_LINUX_INPUT_H */
diff --git a/dlls/xinput1_3/xinput_linux_input_debug.h b/dlls/xinput1_3/xinput_linux_input_debug.h
new file mode 100644
index 0000000..19f4cc6
--- /dev/null
+++ b/dlls/xinput1_3/xinput_linux_input_debug.h
@@ -0,0 +1,35 @@
+/*
+ * The Wine project - Xinput Joystick Library
+ * Copyright 2016 Eric Diaz Fernandez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef XINPUT_LINUX_INPUT_DEBUG_H
+#define XINPUT_LINUX_INPUT_DEBUG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const char* xinput_linux_input_event_type_get_name(int idx);
+const char* xinput_linux_input_key_get_name(int idx);
+const char* xinput_linux_input_abs_get_name(int idx);
+const char* xinput_linux_input_ff_get_name(int idx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* XINPUT_LINUX_INPUT_DEBUG_H */
diff --git a/dlls/xinput1_3/xinput_linux_input_translator.c b/dlls/xinput1_3/xinput_linux_input_translator.c
new file mode 100644
index 0000000..6caa474
--- /dev/null
+++ b/dlls/xinput1_3/xinput_linux_input_translator.c
@@ -0,0 +1,100 @@
+/*
+ * The Wine project - Xinput Joystick Library
+ * Copyright 2016 Eric Diaz Fernandez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+
+#if HAVE_LINUX_INPUT_H
+
+#include "xinput_settings.h"
+
+#include "wine/debug.h"
+
+#include "xinput.h"
+#include "xinput_tools.h"
+
+#include "xinput_linux_input_translator.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(xinput);
+
+void xinput_linux_input_translator_abs_translate_nothing(const struct xinput_linux_input_translator_abs_translator_item* item, XINPUT_GAMEPAD_EX* gamepad, int16_t value)
+{
+    (void)item;
+    (void)gamepad;
+    (void)value;
+    FIXME("ABS input not supported\n");
+}
+
+void xinput_linux_input_translator_abs_translate_to_axis(const struct xinput_linux_input_translator_abs_translator_item* item, XINPUT_GAMEPAD_EX* gamepad, int16_t value)
+{
+    SHORT* p;
+    char* base = (char*)gamepad;
+    base += item->to;
+    p = (SHORT*)base;
+    *p = value;
+}
+
+void xinput_linux_input_translator_abs_translate_to_axis_reverse(const struct xinput_linux_input_translator_abs_translator_item* item, XINPUT_GAMEPAD_EX* gamepad, int16_t value)
+{
+    SHORT* p;
+    char* base = (char*)gamepad;
+    base += item->to;
+    p = (SHORT*)base;
+    *p = ~value;
+}
+
+void xinput_linux_input_translator_abs_translate_to_buttons(const struct xinput_linux_input_translator_abs_translator_item* item, XINPUT_GAMEPAD_EX* gamepad, int16_t value)
+{
+    if(value > 0)
+    {
+        gamepad->wButtons = (gamepad->wButtons | item->positive) & ~item->negative;
+    }
+    else if(value < 0)
+    {
+        gamepad->wButtons = (gamepad->wButtons | item->negative) & ~item->positive;
+    }
+    else
+    {
+        gamepad->wButtons &= ~(item->positive | item->negative);
+    }
+}
+
+void
+xinput_linux_input_translator_abs_input_event_to_gamepad(const struct xinput_linux_input_translator_abs_translator* translator, const struct input_event* ie, XINPUT_GAMEPAD_EX* gamepad)
+{
+    const struct xinput_linux_input_translator_abs_translator_item* line = &translator->_item[ie->code];
+    line->translate(line, gamepad, ie->value);
+}
+
+void
+xinput_linux_input_translator_key_input_event_to_gamepad(const struct xinput_linux_input_translator_key_translator* translator, const struct input_event* ie, XINPUT_GAMEPAD_EX* gamepad)
+{
+    if(ie->code >= translator->_first && ie->code <= translator->_last)
+    {
+        SHORT bit = translator->_buttons[ie->code - translator->_first];
+        if(ie->value != 0)
+        {
+            gamepad->wButtons |= bit;
+        }
+        else
+        {
+            gamepad->wButtons &= ~bit;
+        }
+    }
+}
+
+#endif /* HAVE_LINUX_INPUT_H */
diff --git a/dlls/xinput1_3/xinput_linux_input_translator.h b/dlls/xinput1_3/xinput_linux_input_translator.h
new file mode 100644
index 0000000..d42c4bc
--- /dev/null
+++ b/dlls/xinput1_3/xinput_linux_input_translator.h
@@ -0,0 +1,125 @@
+/*
+ * The Wine project - Xinput Joystick Library
+ * Copyright 2016 Eric Diaz Fernandez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef XINPUT_LINUX_INPUT_TRANSLATOR_H
+#define XINPUT_LINUX_INPUT_TRANSLATOR_H
+
+#include <stdint.h>
+#include <linux/input.h>
+
+#include "xinput.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct xinput_linux_input_translator_abs_translator_item;
+
+typedef void (*xinput_linux_input_translator_abs_translator_callback)(const struct xinput_linux_input_translator_abs_translator_item* item, XINPUT_GAMEPAD_EX*, int16_t value);
+
+struct xinput_linux_input_translator_abs_translator_item
+{
+    xinput_linux_input_translator_abs_translator_callback translate;
+    ssize_t to;
+    int16_t positive;
+    int16_t negative;
+};
+
+struct xinput_linux_input_translator_abs_translator
+{
+    struct xinput_linux_input_translator_abs_translator_item _item[ABS_CNT];
+};
+
+void xinput_linux_input_translator_abs_translate_nothing(const struct xinput_linux_input_translator_abs_translator_item* item, XINPUT_GAMEPAD_EX*, int16_t value);
+void xinput_linux_input_translator_abs_translate_to_axis(const struct xinput_linux_input_translator_abs_translator_item* item, XINPUT_GAMEPAD_EX*, int16_t value);
+void xinput_linux_input_translator_abs_translate_to_axis_reverse(const struct xinput_linux_input_translator_abs_translator_item* item, XINPUT_GAMEPAD_EX*, int16_t value);
+void xinput_linux_input_translator_abs_translate_to_buttons(const struct xinput_linux_input_translator_abs_translator_item* item, XINPUT_GAMEPAD_EX*, int16_t value);
+
+/*
+ * Starts the table
+ */
+
+#define XINPUT_GAMEPAD_ABS_BEGIN(__mytable) struct xinput_linux_input_translator_abs_translator __mytable = { ._item = {
+
+/*
+ * Maps an absolute axis to a gamepad axis
+ */
+
+#define XINPUT_GAMEPAD_ABS_AXIS(_debugname,_field) {&xinput_linux_input_translator_abs_translate_to_axis,offsetof(XINPUT_GAMEPAD_EX, _field),0,0},
+
+/*
+ * Maps an absolute axis to a gamepad axis, in the opposite direction [-1; 1] => [1; -1]
+ */
+
+#define XINPUT_GAMEPAD_ABS_SIXA(_debugname,_field) {&xinput_linux_input_translator_abs_translate_to_axis_reverse,offsetof(XINPUT_GAMEPAD_EX, _field),0,0},
+
+/*
+ * Maps an absolute axis to a gamepad button
+ */
+
+#define XINPUT_GAMEPAD_ABS_BTTN(_debugname,_positive,_negative) {&xinput_linux_input_translator_abs_translate_to_buttons, 0,(_positive),(_negative)},
+
+/*
+ * Ignores an absolute axis
+ */
+
+#define XINPUT_GAMEPAD_ABS_NOPE(_debugname) {&xinput_linux_input_translator_abs_translate_nothing,0,(_debugname),0},
+
+/*
+ * Ends the table
+ */
+
+#define XINPUT_GAMEPAD_ABS_END(__mytable) } }; /* __mytable */
+
+typedef void (*xinput_input_event_to_gamepad)(struct input_event*, XINPUT_GAMEPAD_EX*);
+
+struct xinput_linux_input_translator_key_translator
+{
+    int _first;
+    int _last;
+    const SHORT* _buttons;
+};
+
+/*
+ * Maps a range of keys(buttons) to buttons of the gamepad
+ *
+ * Give the first and last keys(buttons) of the range, then each mapped
+ * gamepad button, or 0 for unmapped keys(buttons).
+ */
+
+#define XINPUT_GAMEPAD_KEY_TRANSLATOR(__mytable,_first,_last, args...) \
+    static SHORT __mytable##_buttons[(_last) - (_first) + 1] = { args }; \
+    static struct xinput_linux_input_translator_key_translator __mytable = { (_first), (_last), __mytable##_buttons };
+
+/*
+ * Using the given translator, updates the XINPUT_GAMEPAD_EX with to the input_event
+ */
+
+void xinput_linux_input_translator_abs_input_event_to_gamepad(const struct xinput_linux_input_translator_abs_translator* translator, const struct input_event* ie, XINPUT_GAMEPAD_EX* gamepad);
+
+/*
+ * Using the given translator, updates the XINPUT_GAMEPAD_EX with to the input_event
+ */
+
+void xinput_linux_input_translator_key_input_event_to_gamepad(const struct xinput_linux_input_translator_key_translator* translator, const struct input_event* ie, XINPUT_GAMEPAD_EX* gamepad);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* XINPUT_LINUX_INPUT_TRANSLATOR_H */
diff --git a/dlls/xinput1_3/xinput_linux_input_xboxpad.c b/dlls/xinput1_3/xinput_linux_input_xboxpad.c
new file mode 100644
index 0000000..12ad632
--- /dev/null
+++ b/dlls/xinput1_3/xinput_linux_input_xboxpad.c
@@ -0,0 +1,371 @@
+/*
+ * The Wine project - Xinput Joystick Library
+ * Copyright 2016 Eric Diaz Fernandez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+
+#define DEBUG_EVENTS 0
+
+#if HAVE_LINUX_INPUT_H
+
+#include "xinput_settings.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "wine/debug.h"
+#include "winerror.h"
+
+#include "xinput.h"
+#include "xinput_tools.h"
+#include "xinput_linux_input_debug.h"
+
+#include "xinput_linux_input_xboxpad.h"
+
+#include "xinput_linux_input.h"
+#include "xinput_device_id.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(xinput);
+
+static int xinput_gamepad_translation[16][2] = /*  minus 0x130 */
+{
+    { BTN_A,        XINPUT_GAMEPAD_A},  /* 0x130 */
+    { BTN_B,        XINPUT_GAMEPAD_B},
+    { BTN_C,        0               },
+    { BTN_X,        XINPUT_GAMEPAD_X},
+    { BTN_Y,        XINPUT_GAMEPAD_Y},
+    { BTN_Z,        0},
+    { BTN_TL,       XINPUT_GAMEPAD_LEFT_SHOULDER},
+    { BTN_TR,       XINPUT_GAMEPAD_RIGHT_SHOULDER},
+    { BTN_TL2,      0},                 /* 0x138 */
+    { BTN_TR2,      0},
+    { BTN_SELECT,   XINPUT_GAMEPAD_BACK},
+    { BTN_START,    XINPUT_GAMEPAD_START},
+    { BTN_MODE,     XINPUT_GAMEPAD_GUIDE}, /* is that it ? */
+    { BTN_THUMBL,   XINPUT_GAMEPAD_LEFT_THUMB},
+    { BTN_THUMBR,   XINPUT_GAMEPAD_RIGHT_THUMB},
+    { 0x13f,        0}
+};
+
+/* this is how my xbox360 wireless gamepad is mapped with linux input */
+
+static int xinput_gamepad_translation2[4][2] =     /*  minus 0x2c0 */
+{
+    { BTN_TRIGGER_HAPPY1, XINPUT_GAMEPAD_DPAD_LEFT}, /* 0x2c0 */
+    { BTN_TRIGGER_HAPPY2, XINPUT_GAMEPAD_DPAD_RIGHT},
+    { BTN_TRIGGER_HAPPY3, XINPUT_GAMEPAD_DPAD_UP},
+    { BTN_TRIGGER_HAPPY4, XINPUT_GAMEPAD_DPAD_DOWN}
+};
+
+/*
+ * Another way to translate is to do everything manually :
+ *
+ */
+
+static void xinput_linux_input_xboxpad_input_event_to_gamepad(const struct input_event* ie, XINPUT_GAMEPAD_EX* gamepad)
+{
+    switch(ie->type)
+    {
+        case EV_KEY:
+        {
+#if DEBUG_EVENTS
+            TRACE("EVENT %04hx=%s %04hx=%s %08x\n",
+                    ie->type, xinput_linux_input_event_type_get_name(ie->type),
+                    ie->code, xinput_linux_input_key_get_name(ie->code),
+                    ie->value
+                    );
+#endif
+
+            if(ie->code >= BTN_GAMEPAD && ie->code < BTN_DIGI)
+            {
+                uint16_t mask = xinput_gamepad_translation[ie->code - BTN_GAMEPAD][1];
+                if(ie->value == 1)
+                {
+                    gamepad->wButtons |= mask;
+                }
+                else
+                {
+                    gamepad->wButtons &= ~mask;
+                }
+            } /* one of my wireless 360 gamepads is mapped this way ... */
+            else if(ie->code >= BTN_TRIGGER_HAPPY1 && ie->code <= BTN_TRIGGER_HAPPY4)
+            {
+                uint16_t mask = xinput_gamepad_translation2[ie->code - BTN_TRIGGER_HAPPY1][1];
+                if(ie->value == 1)
+                {
+                    gamepad->wButtons |= mask;
+                }
+                else
+                {
+                    gamepad->wButtons &= ~mask;
+                }
+            }
+
+            break;
+        }
+        case EV_ABS:
+        {
+#if DEBUG_EVENTS
+            TRACE("EVENT %04hx=%s %04hx=%s %08x\n",
+                    ie->type, xinput_linux_input_event_type_get_name(ie->type),
+                    ie->code, xinput_linux_input_abs_get_name(ie->code),
+                    ie->value
+                    );
+#endif
+            switch(ie->code)
+            {
+                case ABS_X:
+                {
+                    gamepad->sThumbLX = (int16_t)ie->value;
+                    break;
+                }
+                case ABS_Y:
+                {   /*  -32768 =  32767 8000 = 7fff */
+                    /*   32767 = -32768 7fff = 8000 */
+                    gamepad->sThumbLY = ~(int16_t)ie->value;
+                    break;
+                }
+                case ABS_RX:
+                {
+                    gamepad->sThumbRX = (int16_t)ie->value;
+                    break;
+                }
+                case ABS_RY:
+                {
+                    gamepad->sThumbRY = ~(int16_t)ie->value;
+                    break;
+                }
+                case ABS_Z:
+                {
+                    gamepad->bLeftTrigger = (int8_t)ie->value;
+                    break;
+                }
+                case ABS_RZ:
+                {
+                    gamepad->bRightTrigger = (int16_t)ie->value;
+                    break;
+                }
+                case ABS_HAT0X:
+                {
+                    if(ie->value > 0)
+                    {
+                        gamepad->wButtons |=  XINPUT_GAMEPAD_DPAD_RIGHT;
+                        gamepad->wButtons &= ~XINPUT_GAMEPAD_DPAD_LEFT;
+                    }
+                    else if(ie->value < 0)
+                    {
+                        gamepad->wButtons &= ~XINPUT_GAMEPAD_DPAD_RIGHT;
+                        gamepad->wButtons |=  XINPUT_GAMEPAD_DPAD_LEFT;
+                    }
+                    else
+                    {
+                        gamepad->wButtons &= ~(XINPUT_GAMEPAD_DPAD_RIGHT | XINPUT_GAMEPAD_DPAD_LEFT);
+                    }
+                    break;
+                }
+                case ABS_HAT0Y:
+                {
+                    if(ie->value > 0)
+                    {
+                        gamepad->wButtons |=  XINPUT_GAMEPAD_DPAD_DOWN;
+                        gamepad->wButtons &= ~XINPUT_GAMEPAD_DPAD_UP;
+                    }
+                    else if(ie->value < 0)
+                    {
+                        gamepad->wButtons &= ~XINPUT_GAMEPAD_DPAD_DOWN;
+                        gamepad->wButtons |=  XINPUT_GAMEPAD_DPAD_UP;
+                    }
+                    else
+                    {
+                        gamepad->wButtons &= ~(XINPUT_GAMEPAD_DPAD_DOWN | XINPUT_GAMEPAD_DPAD_UP);
+                    }
+                    break;
+                }
+            }
+
+            break;
+        }
+        case EV_FF:
+        {
+            /* break; */
+        }
+        case EV_SYN:
+        {
+            /* break; */
+        }
+        default:
+        {
+#if DEBUG_EVENTS
+            TRACE("EVENT %04hx=%s %04hx %08x\n",
+                    ie->type, xinput_linux_input_event_type_get_name(ie->type),
+                    ie->code,
+                    ie->value
+                    );
+#endif
+            break;
+        }
+    }
+}
+
+struct xinput_linux_input_xboxpad_data
+{
+    XINPUT_GAMEPAD_EX gamepad;
+    XINPUT_VIBRATION vibration;
+    int fd;
+    int effect_id;
+};
+
+typedef struct xinput_linux_input_xboxpad_data xinput_linux_input_xboxpad_data;
+
+static int xinput_linux_input_xboxpad_read(struct xinput_gamepad_device* device)
+{
+    xinput_linux_input_xboxpad_data* data = (xinput_linux_input_xboxpad_data*)device->data;
+    struct input_event ie;
+    int ret = xinput_linux_input_read_next(data->fd, &ie);
+    if(ret == 0)
+    {
+        xinput_linux_input_xboxpad_input_event_to_gamepad(&ie, &data->gamepad);
+    }
+
+    return ret;
+}
+
+static void xinput_linux_input_xboxpad_update(struct xinput_gamepad_device* device, XINPUT_GAMEPAD_EX* gamepad, XINPUT_VIBRATION* vibration)
+{
+    xinput_linux_input_xboxpad_data* data = (xinput_linux_input_xboxpad_data*)device->data;
+
+    if(gamepad != NULL)
+    {
+        memcpy(gamepad, &data->gamepad, sizeof(*gamepad));
+    }
+    if(vibration != NULL)
+    {
+        memcpy(vibration, &data->vibration, sizeof(*vibration));
+    }
+}
+
+static int xinput_linux_input_xboxpad_rumble(struct xinput_gamepad_device* device, const XINPUT_VIBRATION* vibration)
+{
+    xinput_linux_input_xboxpad_data* data = (xinput_linux_input_xboxpad_data*)device->data;
+    int id;
+    id = xinput_linux_input_rumble(data->fd, data->effect_id, vibration->wLeftMotorSpeed, vibration->wRightMotorSpeed);
+    if(id >= 0)
+    {
+        data->effect_id = id;
+    }
+
+    return 0;
+}
+
+static void xinput_linux_input_xboxpad_release(struct xinput_gamepad_device* device)
+{
+    xinput_linux_input_xboxpad_data* data = (xinput_linux_input_xboxpad_data*)device->data;
+
+    TRACE("release %p", device);
+
+    if(data->effect_id >= 0)
+    {
+        xinput_linux_input_feedback_clear(data->fd, data->effect_id);
+        data->effect_id = -1;
+    }
+
+    close_ex(data->fd);
+    data->fd = -1;
+    free(data);
+    device->data = NULL;
+    device->vtbl = NULL;
+}
+
+static const xinput_gamepad_device_vtbl xinput_xboxpad_vtbl =
+{
+    &xinput_linux_input_xboxpad_read,
+    &xinput_linux_input_xboxpad_update,
+    &xinput_linux_input_xboxpad_rumble,
+    &xinput_linux_input_xboxpad_release
+};
+
+static void xinput_linux_input_xboxpad_init(struct xinput_gamepad_device* device, int fd)
+{
+    xinput_linux_input_xboxpad_data* data = (xinput_linux_input_xboxpad_data*)malloc(sizeof(xinput_linux_input_xboxpad_data));
+
+    TRACE("init %p with fd %i", device, fd);
+
+    memset(data, 0, sizeof(xinput_linux_input_xboxpad_data));
+    data->fd = fd;
+    data->effect_id = -1;
+    device->data = data;
+    device->vtbl = &xinput_xboxpad_vtbl;
+}
+
+static struct xinput_driver_supported_device xboxpad_factories[] =
+{
+    {
+        "XBox360 Controller", xinput_linux_input_xboxpad_init,
+        MANUFACTURER_MICROSOFT, XBOX360_CONTROLLER
+    },
+    {
+        "XBox360 Wireless Controller", xinput_linux_input_xboxpad_init,
+        MANUFACTURER_MICROSOFT, XBOX360_WIRELESS_CONTROLLER
+    },
+    {
+        "XBox360 Wireless Controller", xinput_linux_input_xboxpad_init,
+        MANUFACTURER_MICROSOFT, XBOX360_WIRELESS_ADAPTER
+    },
+    {
+        "XBoxOne Controller", xinput_linux_input_xboxpad_init,
+        MANUFACTURER_MICROSOFT, XBOXONE_CONTROLLER
+    },
+    {
+        "XBoxOne Wireless Controller", xinput_linux_input_xboxpad_init,
+        MANUFACTURER_MICROSOFT, XBOXONE_WIRELESS_CONTROLLER
+    },
+    {
+        NULL, NULL, 0, 0
+    }
+};
+
+BOOL xinput_linux_input_xboxpad_can_translate(const struct input_id* id)
+{
+    for(int i = 0; xboxpad_factories[i].vendor != 0; ++i)
+    {
+        if( (xboxpad_factories[i].vendor == id->vendor) &&
+            (xboxpad_factories[i].product == id->product) )
+        {
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+BOOL xinput_linux_input_xboxpad_new_instance(const struct input_id* id, int fd, xinput_gamepad_device* instance)
+{
+    for(int i = 0; i < 4; ++i)
+    {
+        if( (xboxpad_factories[i].vendor == id->vendor) &&
+            (xboxpad_factories[i].product == id->product) )
+        {
+            xboxpad_factories[i].initialize(instance, fd);
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+#endif /* HAVE_LINUX_INPUT_H */
diff --git a/dlls/xinput1_3/xinput_linux_input_xboxpad.h b/dlls/xinput1_3/xinput_linux_input_xboxpad.h
new file mode 100644
index 0000000..9cfeed8
--- /dev/null
+++ b/dlls/xinput1_3/xinput_linux_input_xboxpad.h
@@ -0,0 +1,54 @@
+/*
+ * The Wine project - Xinput Joystick Library
+ * Copyright 2016 Eric Diaz Fernandez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef XINPUT_LINUX_INPUT_XBOXPAD_H
+#define XINPUT_LINUX_INPUT_XBOXPAD_H
+
+#include "xinput_gamepad.h"
+
+#include <linux/input.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Tells if this driver can handle that input_id.
+ *
+ * @param id
+ * @return
+ */
+
+BOOL xinput_linux_input_xboxpad_can_translate(const struct input_id* id);
+
+/**
+ * Initialises an instance of driver
+ *
+ * @param id
+ * @param fd
+ * @param instance
+ * @return
+ */
+
+BOOL xinput_linux_input_xboxpad_new_instance(const struct input_id* id, int fd, xinput_gamepad_device* instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* XINPUT_LINUX_INPUT_XBOXPAD_H */
diff --git a/dlls/xinput1_3/xinput_linux_input_xboxpad_2.c b/dlls/xinput1_3/xinput_linux_input_xboxpad_2.c
new file mode 100644
index 0000000..a3d4204
--- /dev/null
+++ b/dlls/xinput1_3/xinput_linux_input_xboxpad_2.c
@@ -0,0 +1,295 @@
+/*
+ * The Wine project - Xinput Joystick Library
+ * Copyright 2016 Eric Diaz Fernandez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+
+#if HAVE_LINUX_INPUT_H
+
+#include "xinput_settings.h"
+
+/*
+ * As a driver, this is redundant with xinput_linux_input_xboxpad
+ * But this is an example of easy implementation that requires only
+ * editing tables to implement the features of the joystick.
+ * (Mind I only have 360/One pads for testing)
+ *
+ * The next driver I'll write is one that generates the same tables
+ * to make do with whatever buttons/axis are found on an unknown joystick.
+ *
+ */
+
+#include <stdlib.h>
+#include "xinput_gamepad.h"
+#include "xinput_linux_input.h"
+#include "xinput_linux_input_translator.h"
+#include "xinput_tools.h"
+#include "xinput_device_id.h"
+
+XINPUT_GAMEPAD_ABS_BEGIN(xbox360_abs)
+XINPUT_GAMEPAD_ABS_AXIS(ABS_X, sThumbLX)           /* 0x00 */
+XINPUT_GAMEPAD_ABS_SIXA(ABS_Y, sThumbLY)           /* 0x01 */
+XINPUT_GAMEPAD_ABS_AXIS(ABS_Z, bLeftTrigger)       /* 0x02 */
+XINPUT_GAMEPAD_ABS_AXIS(ABS_RX, sThumbRX)          /* 0x03 */
+XINPUT_GAMEPAD_ABS_SIXA(ABS_RY, sThumbRY)          /* 0x04 */
+XINPUT_GAMEPAD_ABS_AXIS(ABS_RZ, bRightTrigger)     /* 0x05 */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_THROTTLE)              /* 0x06 */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_RUDDER)                /* 0x07 */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_WHEEL)                 /* 0x08 */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_GAS)                   /* 0x09 */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_BRAKE)                 /* 0x0a */
+XINPUT_GAMEPAD_ABS_NOPE(0x0b)
+XINPUT_GAMEPAD_ABS_NOPE(0x0c)
+XINPUT_GAMEPAD_ABS_NOPE(0x0d)
+XINPUT_GAMEPAD_ABS_NOPE(0x0e)
+XINPUT_GAMEPAD_ABS_NOPE(0x0f)
+XINPUT_GAMEPAD_ABS_BTTN(ABS_HAT0X, XINPUT_GAMEPAD_DPAD_RIGHT, XINPUT_GAMEPAD_DPAD_LEFT) /* 0x10 */
+XINPUT_GAMEPAD_ABS_BTTN(ABS_HAT0Y, XINPUT_GAMEPAD_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_UP)        /* 0x11 */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_HAT1X)                 /*  0x12 */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_HAT1Y)                 /*  0x13 */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_HAT2X)                 /*  0x14 */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_HAT2Y)                 /*  0x15 */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_HAT3X)                 /*  0x16 */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_HAT3Y)                 /*  0x17 */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_PRESSURE)              /*  0x18 */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_DISTANCE)              /*  0x19 */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_TILT_X)                /*  0x1a */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_TILT_Y)                /*  0x1b */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_TOOL_WIDTH)            /*  0x1c */
+XINPUT_GAMEPAD_ABS_NOPE(0x1d)
+XINPUT_GAMEPAD_ABS_NOPE(0x1e)
+XINPUT_GAMEPAD_ABS_NOPE(0x1f)
+XINPUT_GAMEPAD_ABS_NOPE(ABS_VOLUME)                /*  0x20 */
+XINPUT_GAMEPAD_ABS_NOPE(0x21)
+XINPUT_GAMEPAD_ABS_NOPE(0x22)
+XINPUT_GAMEPAD_ABS_NOPE(0x23)
+XINPUT_GAMEPAD_ABS_NOPE(0x24)
+XINPUT_GAMEPAD_ABS_NOPE(0x25)
+XINPUT_GAMEPAD_ABS_NOPE(0x26)
+XINPUT_GAMEPAD_ABS_NOPE(0x27)
+XINPUT_GAMEPAD_ABS_NOPE(ABS_MISC)                  /*  0x28 */
+XINPUT_GAMEPAD_ABS_NOPE(0x29)
+XINPUT_GAMEPAD_ABS_NOPE(0x2a)
+XINPUT_GAMEPAD_ABS_NOPE(0x2b)
+XINPUT_GAMEPAD_ABS_NOPE(0x2c)
+XINPUT_GAMEPAD_ABS_NOPE(0x2d)
+XINPUT_GAMEPAD_ABS_NOPE(0x2e)
+XINPUT_GAMEPAD_ABS_NOPE(ABS_MT_SLOT)               /*  0x2f MT slot being modified */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_MT_TOUCH_MAJOR)        /*  0x30 Major axis of touching ellipse */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_MT_TOUCH_MINOR)        /*  0x31 Minor axis (omit if circular) */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_MT_WIDTH_MAJOR)        /*  0x32 Major axis of approaching ellipse */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_MT_WIDTH_MINOR)        /*  0x33 Minor axis (omit if circular) */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_MT_ORIENTATION)        /*  0x34 Ellipse orientation */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_MT_POSITION_X)         /*  0x35 Center X touch position */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_MT_POSITION_Y)         /*  0x36 Center Y touch position */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_MT_TOOL_TYPE)          /*  0x37 Type of touching device */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_MT_BLOB_ID)            /*  0x38 Group a set of packets as a blob */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_MT_TRACKING_ID)        /*  0x39 Unique ID of initiated contact */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_MT_PRESSURE)           /*  0x3a Pressure on contact area */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_MT_DISTANCE)           /*  0x3b Contact hover distance */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_MT_TOOL_X)             /*  0x3c Center X tool position */
+XINPUT_GAMEPAD_ABS_NOPE(ABS_MT_TOOL_Y)             /*  0x3d Center Y tool position */
+XINPUT_GAMEPAD_ABS_NOPE(0x3e)
+XINPUT_GAMEPAD_ABS_NOPE(0x3f)
+XINPUT_GAMEPAD_ABS_END(xbox360_abs)
+
+XINPUT_GAMEPAD_KEY_TRANSLATOR(xbox360_key,
+        BTN_A, BTN_THUMBR,                 /* first and last button mapped */
+        XINPUT_GAMEPAD_A,                  /* map of the first button ... */
+        XINPUT_GAMEPAD_B,
+        0,
+        XINPUT_GAMEPAD_X,
+        XINPUT_GAMEPAD_Y,
+        0,
+        XINPUT_GAMEPAD_LEFT_SHOULDER,
+        XINPUT_GAMEPAD_RIGHT_SHOULDER,
+        0,
+        0,
+        XINPUT_GAMEPAD_BACK,
+        XINPUT_GAMEPAD_START,
+        XINPUT_GAMEPAD_GUIDE,
+        XINPUT_GAMEPAD_LEFT_THUMB,
+        XINPUT_GAMEPAD_RIGHT_THUMB);       /* map of the last button ... */
+
+/* this is how my xbox360 wireless gamepad is mapped with linux-input */
+
+XINPUT_GAMEPAD_KEY_TRANSLATOR(xbox360_key_bis,
+        BTN_TRIGGER_HAPPY1, BTN_TRIGGER_HAPPY4,
+        XINPUT_GAMEPAD_DPAD_LEFT, /* 0x2c0 */
+        XINPUT_GAMEPAD_DPAD_RIGHT,
+        XINPUT_GAMEPAD_DPAD_UP,
+        XINPUT_GAMEPAD_DPAD_DOWN);
+
+static void xinput_linux_input_xboxpad2_input_event_to_gamepad(const struct input_event* ie, XINPUT_GAMEPAD_EX* gamepad)
+{
+    switch(ie->type)
+    {
+        case EV_KEY:
+        {
+            xinput_linux_input_translator_key_input_event_to_gamepad(&xbox360_key, ie, gamepad);
+            xinput_linux_input_translator_key_input_event_to_gamepad(&xbox360_key_bis, ie, gamepad);
+            break;
+        }
+        case EV_ABS:
+        {
+            xinput_linux_input_translator_abs_input_event_to_gamepad(&xbox360_abs, ie, gamepad);
+            break;
+        }
+        default:
+        {
+            /* does not handle anything else ... yet */
+            break;
+        }
+    }
+}
+struct xinput_linux_input_xboxpad2_data
+{
+    XINPUT_GAMEPAD_EX gamepad;
+    XINPUT_VIBRATION vibration;
+    int fd;
+    int effect_id;
+};
+
+typedef struct xinput_linux_input_xboxpad2_data xinput_linux_input_xboxpad2_data;
+
+static int xinput_linux_input_xboxpad2_read(struct xinput_gamepad_device* device)
+{
+    xinput_linux_input_xboxpad2_data* data = (xinput_linux_input_xboxpad2_data*)device->data;
+    struct input_event ie;
+    int ret = xinput_linux_input_read_next(data->fd, &ie);
+    if(ret == 0)
+    {
+        xinput_linux_input_xboxpad2_input_event_to_gamepad(&ie, &data->gamepad);
+    }
+
+    return ret;
+}
+
+static void xinput_linux_input_xboxpad2_update(struct xinput_gamepad_device* device, XINPUT_GAMEPAD_EX* gamepad, XINPUT_VIBRATION* vibration)
+{
+    xinput_linux_input_xboxpad2_data* data = (xinput_linux_input_xboxpad2_data*)device->data;
+
+    if(gamepad != NULL)
+    {
+        memcpy(gamepad, &data->gamepad, sizeof(*gamepad));
+    }
+    if(vibration != NULL)
+    {
+        memcpy(vibration, &data->vibration, sizeof(*vibration));
+    }
+}
+
+static int xinput_linux_input_xboxpad2_rumble(struct xinput_gamepad_device* device, const XINPUT_VIBRATION* vibration)
+{
+    xinput_linux_input_xboxpad2_data* data = (xinput_linux_input_xboxpad2_data*)device->data;
+    int id;
+    id = xinput_linux_input_rumble(data->fd, data->effect_id, vibration->wLeftMotorSpeed, vibration->wRightMotorSpeed);
+    if(id >= 0)
+    {
+        data->effect_id = id;
+    }
+
+    return 0;
+}
+
+static void xinput_linux_input_xboxpad2_release(struct xinput_gamepad_device* device)
+{
+    xinput_linux_input_xboxpad2_data* data = (xinput_linux_input_xboxpad2_data*)device->data;
+
+    if(data->effect_id >= 0)
+    {
+        xinput_linux_input_feedback_clear(data->fd, data->effect_id);
+        data->effect_id = -1;
+    }
+    close_ex(data->fd);
+    data->fd = -1;
+    free(data);
+    device->data = NULL;
+    device->vtbl = NULL;
+}
+
+static const xinput_gamepad_device_vtbl xinput_xboxpad2_vtbl =
+{
+    &xinput_linux_input_xboxpad2_read,
+    &xinput_linux_input_xboxpad2_update,
+    &xinput_linux_input_xboxpad2_rumble,
+    &xinput_linux_input_xboxpad2_release
+};
+
+static void xinput_linux_input_xboxpad2_init(struct xinput_gamepad_device* instance, int fd)
+{
+    xinput_linux_input_xboxpad2_data* data = (xinput_linux_input_xboxpad2_data*)malloc(sizeof(xinput_linux_input_xboxpad2_data));
+    memset(data, 0, sizeof(xinput_linux_input_xboxpad2_data));
+    data->fd = fd;
+    data->effect_id = -1;
+    instance->data = data;
+    instance->vtbl = &xinput_xboxpad2_vtbl;
+}
+
+static struct xinput_driver_supported_device xboxpad_factories[] =
+{
+    {
+        "XBox360 Controller", xinput_linux_input_xboxpad2_init,
+        MANUFACTURER_MICROSOFT, XBOX360_CONTROLLER
+    },
+    {
+        "XBox360 Wireless Controller", xinput_linux_input_xboxpad2_init,
+        MANUFACTURER_MICROSOFT, XBOX360_WIRELESS_CONTROLLER
+    },
+    {
+        "XBoxOne Controller", xinput_linux_input_xboxpad2_init,
+        MANUFACTURER_MICROSOFT, XBOXONE_CONTROLLER
+    },
+    {
+        "XBoxOne Wireless Controller", xinput_linux_input_xboxpad2_init,
+        MANUFACTURER_MICROSOFT, XBOXONE_WIRELESS_CONTROLLER
+    },
+    {
+        NULL, NULL, 0, 0
+    }
+};
+
+BOOL xinput_linux_input_xboxpad2_can_translate(const struct input_id* id)
+{
+    for(int i = 0; xboxpad_factories[i].vendor != 0; ++i)
+    {
+        if( (xboxpad_factories[i].vendor == id->vendor) &&
+            (xboxpad_factories[i].product == id->product) )
+        {
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+BOOL xinput_linux_input_xboxpad2_new_instance(const struct input_id* id, int fd, xinput_gamepad_device* instance)
+{
+    for(int i = 0; i < 4; ++i)
+    {
+        if( (xboxpad_factories[i].vendor == id->vendor) &&
+            (xboxpad_factories[i].product == id->product) )
+        {
+            xboxpad_factories[i].initialize(instance, fd);
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+#endif /* HAVE_LINUX_INPUT_H */
diff --git a/dlls/xinput1_3/xinput_linux_input_xboxpad_2.h b/dlls/xinput1_3/xinput_linux_input_xboxpad_2.h
new file mode 100644
index 0000000..568a2cd
--- /dev/null
+++ b/dlls/xinput1_3/xinput_linux_input_xboxpad_2.h
@@ -0,0 +1,61 @@
+/*
+ * The Wine project - Xinput Joystick Library
+ * Copyright 2016 Eric Diaz Fernandez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef XINPUT_LINUX_INPUT_XBOXPAD_2_H
+#define XINPUT_LINUX_INPUT_XBOXPAD_2_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * As a driver, this is redundant with xinput_linux_input_xboxpad
+ * But this is an example of easy implementation that requires only
+ * editing tables to implement the features of the joystick.
+ * (Mind I only have 360/One pads for testing)
+ *
+ * The next driver I'll write is one that generates the same tables
+ * to make do with whatever buttons/axis are found on an unknown joystick.
+ *
+ */
+
+/**
+ * Tells if this driver can handle that input_id.
+ *
+ * @param id
+ * @return
+ */
+
+BOOL xinput_linux_input_xboxpad2_can_translate(const struct input_id* id);
+
+/**
+ * Initialises an instance of driver
+ *
+ * @param id
+ * @param fd
+ * @param instance
+ * @return
+ */
+
+BOOL xinput_linux_input_xboxpad2_new_instance(const struct input_id* id, int fd, xinput_gamepad_device* instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* XINPUT_LINUX_INPUT_XBOXPAD_2_H */
diff --git a/dlls/xinput1_3/xinput_service.c b/dlls/xinput1_3/xinput_service.c
new file mode 100644
index 0000000..3768ab7
--- /dev/null
+++ b/dlls/xinput1_3/xinput_service.c
@@ -0,0 +1,869 @@
+/*
+ * The Wine project - Xinput Joystick Library
+ * Copyright 2016 Eric Diaz Fernandez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+#include "xinput_settings.h"
+
+#include <pthread.h>
+#include <stdlib.h>
+
+#include "xinput.h"
+#include "xinput_debug.h"
+#include "xinput_tools.h"
+#include "xinput_service.h"
+#include "xinput_gamepad.h"
+
+#if XINPUT_USES_SEMAPHORE_MUTEX
+#include <fcntl.h>           /* For O_* constants */
+#include <sys/stat.h>        /* For mode constants */
+#include <semaphore.h>
+#endif
+
+#ifndef HAVE_MQ_OPEN
+#undef XINPUT_USES_MQUEUE
+#endif
+
+#if XINPUT_USES_MQUEUE
+#include <mqueue.h>
+#endif
+
+#include "wine/debug.h"
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <signal.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>        /* For mode constants */
+#include <sys/shm.h>
+#include <fcntl.h>           /* For O_* constants */
+#include <stdint.h>
+#include <errno.h>
+#include <unistd.h>
+
+
+#if HAVE_LINUX_INPUT_H
+#include "xinput_linux_input.h"
+#else
+/* no supported driver */
+#define xinput_driver_initialize()
+#define xinput_driver_probe() 0
+#define xinput_driver_get_device(a) NULL
+#define xinput_driver_device_close(a)
+#define xinput_driver_finalize()
+#endif
+
+WINE_DEFAULT_DEBUG_CHANNEL(xinput);
+
+struct xinput_service_thread_args
+{
+    xinput_gamepad_state* xgs;
+    xinput_gamepad_device* device;
+    pthread_t tid;
+    int slot;
+};
+
+typedef struct xinput_service_thread_args xinput_service_thread_args;
+
+static xinput_service_thread_args xinput_service_thread_parameter[XUSER_MAX_COUNT];
+
+static xinput_shared_gamepad_state* service_shared = NULL;
+#if !XINPUT_RUNDLL
+static pthread_t service_thread_id = 0;
+#endif
+static int service_fd = -1;
+#if XINPUT_USES_SEMAPHORE_MUTEX
+static sem_t*  service_sem = SEM_FAILED;
+#endif
+
+
+#if XINPUT_USES_MQUEUE
+
+static mqd_t service_mq = MQD_INVALID;
+static pthread_t xinput_service_rumble_thread_id = 0;
+
+/*
+ * Rumble is handled almost entierely separately
+ */
+
+static BOOL xinput_service_queue_create(void)
+{
+    mqd_t mq;
+    struct mq_attr attr;
+    memset(&attr, 0, sizeof(attr));
+    attr.mq_flags = 0;
+    attr.mq_maxmsg = 10;
+    attr.mq_msgsize = sizeof(xinput_gamepad_vibration);
+
+    mq = mq_open(SERVICE_MSG_NAME, O_RDWR|O_CREAT|O_EXCL, 0666, &attr);
+    if(mq == MQD_INVALID)
+    {
+        int err = errno;
+        if(err != EEXIST)
+        {
+            return FALSE;
+        }
+
+        mq_unlink(SERVICE_MSG_NAME);
+        mq = mq_open(SERVICE_MSG_NAME, O_RDONLY|O_CREAT|O_EXCL, 0666, &attr);
+        if(mq == MQD_INVALID)
+        {
+            err = errno;
+            TRACE("could not create message queue: %s\n", strerror(err));
+
+            return FALSE;
+        }
+    }
+
+    mq_getattr(mq, &attr);
+
+    service_mq = mq;
+
+    return TRUE;
+}
+
+static void xinput_service_queue_destroy(void)
+{
+    if(service_mq != MQD_INVALID)
+    {
+        mq_close(service_mq);
+        service_mq = MQD_INVALID;
+        mq_unlink(SERVICE_MSG_NAME);
+    }
+}
+
+static void* xinput_service_rumble_thread(void* args_)
+{
+    xinput_gamepad_device* device;
+    xinput_gamepad_vibration vibration_message;
+    unsigned int priority = 0;
+    (void)args_;
+
+    for(;;)
+    {
+        ssize_t len = mq_receive(service_mq, (char*)&vibration_message, sizeof(vibration_message), &priority);
+        if(len == -1)
+        {
+            int err = errno;
+            switch(err)
+            {
+                case EINTR:
+                case EAGAIN:
+                /*case ETIMEDOUT:*/
+                    continue;
+                default:
+                    break;
+            }
+
+            TRACE("receive returned an error: %s\n", strerror(err));
+
+            /**
+             * TODO: restart the service
+             */
+
+            break;
+        }
+
+        TRACE("rumble %i: [%4x, %4x]\n", vibration_message.index,
+                vibration_message.vibration.wLeftMotorSpeed,
+                vibration_message.vibration.wRightMotorSpeed);
+
+        /*
+         * send rumble
+         */
+
+        if((device = xinput_linux_input_get_device(vibration_message.index)) != NULL)
+        {
+            device->vtbl->rumble(device, &vibration_message.vibration);
+        }
+    }
+    return NULL;
+}
+
+#endif
+
+static BOOL xinput_service_lock_create(void)
+{
+#if XINPUT_USES_SEMAPHORE_MUTEX
+    service_sem = sem_open(SERVICE_SEM_NAME, O_RDWR|O_CREAT|O_EXCL, 0644, 1);
+    if(service_sem == SEM_FAILED)
+    {
+        int err = errno;
+        if(err != EEXIST)
+        {
+            return FALSE;
+        }
+
+        sem_unlink(SERVICE_SEM_NAME);
+
+        service_sem = sem_open(SERVICE_SEM_NAME, O_CREAT|O_EXCL, 0644, 1);
+        if(service_sem == SEM_FAILED)
+        {
+            err = errno;
+            TRACE("could not create semaphore: %s\n", strerror(err));
+
+            return FALSE;
+        }
+    }
+#endif
+
+    return TRUE;
+}
+
+static void xinput_service_lock_destroy(void)
+{
+#if XINPUT_USES_SEMAPHORE_MUTEX
+    if(service_sem != SEM_FAILED)
+    {
+        sem_close(service_sem);
+        service_sem = SEM_FAILED;
+        sem_unlink(SERVICE_SEM_NAME);
+    }
+#endif
+}
+
+static BOOL xinput_service_lock(void)
+{
+#if XINPUT_USES_SEMAPHORE_MUTEX
+#ifdef __USE_XOPEN2K
+    struct timespec to;
+#else
+    int64_t until = timeus() + 1000000LL;
+#endif
+
+    for(;;)
+    {
+#ifdef __USE_XOPEN2K
+        int64_t now = timeus();
+        now += 1000000LL;
+        to.tv_sec = now / 1000000LL;
+        to.tv_nsec = (now % 1000000LL) *1000LL;
+
+        if(sem_timedwait(service_sem, &to) == 0)
+        {
+            return TRUE;
+        }
+#else
+        if(sem_trywait(service_sem) == 0)
+        {
+            return TRUE;
+        }
+        else if(timeus() < until)
+        {
+            usleep(500);   /* 0.5 ms sleep */
+        }
+#endif
+        else
+        {
+            int err = errno;
+
+            if(err != EINTR)
+            {
+                /*
+                 * ETIMEOUT
+                 * starvation or a client died keeping the lock
+                 *
+                 * TODO: destroy the service, including the semaphore,
+                 * and let it restart
+                 */
+
+                TRACE("semaphore seems stuck\n");
+
+                return FALSE;
+            }
+        }
+    }
+#else
+    return TRUE;
+#endif
+}
+
+static void xinput_service_unlock(void)
+{
+#if XINPUT_USES_SEMAPHORE_MUTEX
+    sem_post(service_sem);
+#endif
+}
+
+static void* xinput_service_gamepad_reader_thread(void* args_)
+{
+    xinput_service_thread_args* args = (xinput_service_thread_args*)args_;
+
+    xinput_gamepad_state* xgs = args->xgs;
+
+    int err;
+    pid_t pid = getpid();
+
+    TRACE("BEGIN %i ==========================================\n", args->slot);
+
+    if(xinput_service_lock())
+    {
+        xgs->connected = TRUE;
+        xinput_service_unlock();
+    }
+
+    for(;;)
+    {
+        if((err = args->device->vtbl->read(args->device)) == 0)
+        {
+            if(xinput_service_lock())
+            {
+                /* copy the data */
+                args->device->vtbl->update(args->device, &xgs->gamepad, &xgs->vibration);
+                ++xgs->dwPacketNumber;
+                xinput_service_unlock();
+            }
+            else
+            {
+                /* semaphore stuck ... ? */
+            }
+        }
+        else
+        {
+            /* ENODEV ... or something */
+            TRACE("%6i: failed to read %i: %i: %s\n", pid, args->slot, err, strerror(err));
+
+            break;
+        }
+#if XINPUT_TRACE_DEVICE_READER_THREAD
+        TRACE("%6i | %9i | %04hx,%04hx %04hx,%04hx %02hhx %02hhx %04hx\n",
+                pid,
+                xgs->dwPacketNumber,
+                xgs->gamepad.sThumbLX,
+                xgs->gamepad.sThumbLY,
+                xgs->gamepad.sThumbRX,
+                xgs->gamepad.sThumbRY,
+                xgs->gamepad.bLeftTrigger,
+                xgs->gamepad.bRightTrigger,
+                xgs->gamepad.wButtons
+                );
+#endif
+    } /*  for */
+
+    xinput_driver_device_close(args->slot);
+    args->device = NULL;
+
+    if(xinput_service_lock())
+    {
+        xgs->connected = FALSE;
+        xinput_service_unlock();
+    }
+
+    TRACE("END %i ============================================\n", args->slot);
+
+    return NULL;
+}
+
+static void xinput_service_gamepad_probe(void)
+{
+    uint32_t mask = xinput_driver_probe();
+
+    if(mask == 0)
+    {
+        return;
+    }
+
+    for(int slot = 0; slot < XUSER_MAX_COUNT; ++slot)
+    {
+        if(mask & (1 << slot))
+        {
+            int ret;
+
+            /* new gamepad */
+
+            xinput_gamepad_device* device = xinput_driver_get_device(slot);
+            xinput_gamepad_state* xgs = &service_shared->state[slot];
+
+            TRACE("starting thread for gamepad %i\n", slot);
+
+            xinput_service_thread_parameter[slot].device = device;
+            xinput_service_thread_parameter[slot].slot = slot;
+            xinput_service_thread_parameter[slot].xgs = xgs;
+
+            TRACE("create\n");
+
+            ret = pthread_create(&xinput_service_thread_parameter[slot].tid, NULL, xinput_service_gamepad_reader_thread, &xinput_service_thread_parameter[slot]);
+
+            if(ret != 0)
+            {
+                xinput_driver_device_close(slot);
+
+                TRACE("pthread_create returned %i: %s\n", ret, strerror(ret));
+            }
+        }
+    }
+
+#if XINPUT_TRACE_DEVICE_DETECTION
+    TRACE("devices probed\n");
+#endif
+}
+
+static void* xinput_service_thread(void* args_)
+{
+    int allalone = 0;
+    (void)args_;
+    for(;;)
+    {
+        int64_t now = timeus();
+
+        if(now >= service_shared->poke_us)
+        {
+            if((now - service_shared->poke_us) <= (XINPUT_DEVICE_PROBE_PERIOD_S * 1000000LL))
+            {
+                int dt = (int)(now - service_shared->poke_us);
+                TRACE("client was active %ius ago\n", dt);
+                allalone = 0;
+            }
+            else
+            {
+                int dt = (int)(now - service_shared->poke_us);
+
+                ++allalone;
+
+                TRACE("client has not been active for %ius (strike %i)\n", dt, allalone);
+
+                if(allalone >= XINPUT_IDLE_CLIENT_STRIKES)
+                {
+                    TRACE("no active client\n");
+                    /* no sign of life for a while : give up */
+                    break;
+                }
+            }
+        }
+        else
+        {
+            TRACE("weird clock value\n");
+            /* weird, let's reset it */
+            service_shared->poke_us = now;
+        }
+
+        xinput_service_gamepad_probe();
+        sleep(XINPUT_DEVICE_PROBE_PERIOD_S);
+    }
+    return NULL;
+}
+
+void xinput_service_destroy(void)
+{
+    TRACE("destroying\n");
+
+#if XINPUT_USES_MQUEUE
+    xinput_service_queue_destroy();
+#endif
+
+    xinput_service_lock_destroy();
+
+    if(service_shared != NULL)
+    {
+        for(int slot = 0; slot < XUSER_MAX_COUNT; ++slot)
+        {
+            xinput_gamepad_device* device;
+
+            if(xinput_service_thread_parameter[slot].tid != 0)
+            {
+                TRACE("canceling device thread %i\n", slot);
+
+                pthread_cancel(xinput_service_thread_parameter[slot].tid);
+                xinput_service_thread_parameter[slot].tid = 0;
+            }
+
+            if((device = xinput_service_thread_parameter[slot].device) != NULL)
+            {
+                xinput_service_thread_parameter[slot].device = NULL;
+                xinput_driver_device_close(slot);
+            }
+        }
+
+#if XINPUT_USES_MQUEUE
+        if(xinput_service_rumble_thread_id != 0)
+        {
+            pthread_cancel(xinput_service_rumble_thread_id);
+            xinput_service_rumble_thread_id = 0;
+        }
+#endif
+
+        service_shared->master_pid = XINPUT_OWNER_BROKEN;
+        TRACE("destroying '%s'\n", SERVICE_SHM_NAME);
+        shm_unlink(SERVICE_SHM_NAME);
+
+        munmap(service_shared, sizeof(xinput_shared_gamepad_state));
+        service_shared = NULL;
+    }
+
+    if(service_fd != -1)
+    {
+        close_ex(service_fd);
+        service_fd = -1;
+    }
+
+    xinput_driver_finalize();
+}
+
+int xinput_service_poke(void)
+{
+    xinput_shared_gamepad_state* state;
+    pid_t pid;
+    int ret;
+    int fd;
+    BOOL dead;
+
+    TRACE("poke\n");
+
+    ret = shm_open(SERVICE_SHM_NAME, O_RDWR, 0666);
+
+    if(ret < 0)
+    {
+        ret = errno; /* ENOENT : should be created */
+        return ret;
+    }
+
+    fd = ret;
+
+    state = (xinput_shared_gamepad_state*)mmap(
+                NULL,
+                sizeof(xinput_shared_gamepad_state),
+                PROT_READ|PROT_WRITE, MAP_SHARED,
+                fd,
+                0);
+
+    if(state == NULL)
+    {
+        ret = errno;
+        close_ex(fd);
+        TRACE("could not map: %s\n", strerror(ret));
+        return ret;
+    }
+
+    for(int i = 5; i >= 0; --i)
+    {
+        memdump(state, sizeof(xinput_shared_gamepad_state));
+
+        pid = state->master_pid;
+        if(pid != 0)
+        {
+            TRACE("owned by pid %i\n", pid);
+            break;
+        }
+
+        /*  this will only happen if the DLL has been loaded twice "at the same time" */
+
+        usleep(XINPUT_OWNER_PROBE_PERIOD_US);
+    }
+
+    dead = (pid == 0) || (pid == XINPUT_OWNER_BROKEN);
+
+    if(!dead)
+    {
+        dead = kill(pid, 0) < 0;
+        if(dead)
+        {
+            int ret = errno;
+            TRACE("could not probe owner on pid: %s\n", strerror(ret));
+        }
+    }
+
+    if(dead)
+    {
+        TRACE("dead\n");
+
+        /*  dead */
+        /*  mark it as dead, delete it, close it restart it */
+        state->master_pid = XINPUT_OWNER_BROKEN; /*  mark broken */
+        TRACE("destroying '%s'\n", SERVICE_SHM_NAME);
+#if XINPUT_USES_MQUEUE
+        mq_unlink(SERVICE_MSG_NAME);
+#endif
+#if XINPUT_USES_SEMAPHORE_MUTEX
+        sem_unlink(SERVICE_SEM_NAME);
+#endif
+        shm_unlink(SERVICE_SHM_NAME);
+
+        munmap(state, sizeof(xinput_shared_gamepad_state));
+        state = NULL;
+
+        close_ex(fd);
+        fd = -1;
+
+        /*  create a new server */
+
+        return ENOENT;
+    }
+    else
+    {
+        /* only the clients are writing, and synchronization does not matter */
+        state->poke_us = timeus();
+        TRACE("alive\n");
+        /*  alive */
+        return EEXIST;
+    }
+}
+
+int xinput_service_create(void)
+{
+    xinput_shared_gamepad_state* state;
+    int ret;
+    int fd;
+
+    TRACE("creating\n");
+
+    ret = shm_open(SERVICE_SHM_NAME, O_RDWR|O_CREAT|O_EXCL, 0666);
+
+    if(ret < 0)
+    {
+        ret = errno;
+
+        TRACE("could not create: %s\n", strerror(ret));
+
+        return ret;
+    }
+
+    TRACE("exclusively created shared memory named '%s'\n", SERVICE_SHM_NAME);
+
+    fd = ret;
+
+    while(ftruncate(fd, sizeof(xinput_shared_gamepad_state)) < 0)
+    {
+        ret = errno;
+
+        TRACE("could not set size: %s\n", strerror(ret));
+
+        if(ret != EINTR)
+        {
+            TRACE("destroying '%s'\n", SERVICE_SHM_NAME);
+            shm_unlink(SERVICE_SHM_NAME);
+            close_ex(fd);
+            return ret;
+        }
+    }
+
+    state = (xinput_shared_gamepad_state*)mmap(
+                    NULL,
+                    sizeof(xinput_shared_gamepad_state),
+                    PROT_READ|PROT_WRITE,
+                    MAP_SHARED,
+                    fd,
+                    0);
+
+    if(state == NULL)
+    {
+        ret = errno;
+
+        TRACE("could not map: %s\n", strerror(ret));
+        TRACE("destroying '%s'\n", SERVICE_SHM_NAME);
+        shm_unlink(SERVICE_SHM_NAME);
+        close_ex(fd);
+        return ret;
+    }
+
+    memdump(state, sizeof(xinput_shared_gamepad_state));
+
+    memset(state, 0, sizeof(xinput_shared_gamepad_state));
+
+    memset(xinput_service_thread_parameter, 0, sizeof(xinput_service_thread_parameter));;
+
+    //state->poke_us = timeus();
+
+    /* from this point, there should be no race/conflict creating the resources */
+
+    if(!xinput_service_lock_create())
+    {
+        xinput_service_destroy();
+        return -1;
+    }
+
+    memset(&xinput_service_thread_parameter, 0, sizeof(xinput_service_thread_parameter));
+    xinput_driver_initialize();
+
+#if XINPUT_USES_MQUEUE
+    if(!xinput_service_queue_create())
+    {
+        xinput_service_destroy();
+        return -1;
+    }
+#endif
+
+    /* everything is ready, set the pid */
+
+    service_shared = state;
+    service_fd = fd;
+
+    memdump(state, sizeof(xinput_shared_gamepad_state));
+
+    /* the probe will live until the program dies */
+
+    return ERROR_SUCCESS;
+}
+
+int xinput_service_run(void)
+{
+#if XINPUT_USES_MQUEUE
+    pthread_t tid;
+    int ret;
+#endif
+
+    service_shared->master_pid = getpid();
+    TRACE("owner set to %i\n", service_shared->master_pid);
+
+#if XINPUT_USES_MQUEUE
+    ret = pthread_create(&tid, NULL, xinput_service_rumble_thread, NULL);
+
+    if(ret == 0)
+    {
+        xinput_service_rumble_thread_id = tid;
+    }
+    else
+    {
+        TRACE("could not spawn: %s\n", strerror(ret));
+
+        xinput_service_destroy();
+        return -1;
+    }
+#endif
+
+    xinput_service_thread(NULL);
+    return 0;
+}
+
+BOOL xinput_service_self(void)
+{
+    return service_shared != NULL;
+}
+
+static BOOL xinput_service_acquire_lock(void)
+{
+    int ret;
+
+    TRACE("system-wide exclusive lock\n");
+
+    ret = open(XINPUT_SYSTEM_WIDE_LOCK_FILE, O_CREAT|O_RDWR|O_TRUNC, 0666);
+    if(ret >= 0)
+    {
+        TRACE("system-wide lock file opened\n");
+
+        if(flock(ret, LOCK_EX|LOCK_NB) >= 0)
+        {
+            TRACE("system-wide lock acquired\n");
+            return TRUE;
+        }
+        else
+        {
+            close_ex(ret);
+
+            ret = errno;
+            TRACE("system-wide lock failed: %s\n", strerror(ret));
+        }
+    }
+    else
+    {
+        ret = errno;
+        TRACE("system-wide lock file cannot be opened: %s\n", strerror(ret));
+    }
+    return FALSE;
+}
+
+void xinput_service_server(void)
+{
+    int ret;
+
+    if(!xinput_service_acquire_lock())
+    {
+        return;
+    }
+
+    TRACE("starting\n");
+
+    for(;;)
+    {
+        TRACE("creating\n");
+        if((ret = xinput_service_create()) == ERROR_SUCCESS)
+        {
+            TRACE("running\n");
+            xinput_service_run();
+
+            break;
+        }
+        else if(ret == EEXIST)
+        {
+            TRACE("poking\n");
+            if((ret = xinput_service_poke()) != ENOENT)
+            {
+                break;
+            }
+        }
+        else
+        {
+            TRACE("cannot handle error %i\n", ret);
+            break;
+        }
+    }
+
+    xinput_service_destroy();
+    TRACE("stopping\n");
+}
+
+#if !XINPUT_RUNDLL
+static void* xinput_service_server_thread(void* args)
+{
+    (void)args;
+    xinput_service_server();
+    return NULL;
+}
+#endif
+
+void xinput_service_rundll(void)
+{
+#if XINPUT_RUNDLL
+    STARTUPINFOA si;
+    PROCESS_INFORMATION pi;
+    LPSTR cmd;
+
+    ZeroMemory( &si, sizeof(si) );
+    si.cb = sizeof(si);
+    ZeroMemory( &pi, sizeof(pi) );
+
+    cmd = strdup("rundll32.exe xinput1_3.dll,XInputServer");
+
+    // Start the child process.
+    if( !CreateProcessA( NULL,                  // No module name (use command line)
+        cmd,    // Command line
+        NULL,           // Process handle not inheritable
+        NULL,           // Thread handle not inheritable
+        FALSE,          // Set handle inheritance to FALSE
+        CREATE_NO_WINDOW|DETACHED_PROCESS,      //
+        NULL,           // Use parent's environment block
+        NULL,           // Use parent's starting directory
+        &si,            // Pointer to STARTUPINFO structure
+        &pi )           // Pointer to PROCESS_INFORMATION structure
+    )
+    {
+        TRACE( "CreateProcess failed (%d).\n", GetLastError() );
+    }
+    free(cmd);
+#else
+    pthread_t tid;
+    if(pthread_create(&tid, NULL, xinput_service_server_thread, NULL) == 0)
+    {
+        service_thread_id = tid;
+    }
+#endif
+}
diff --git a/dlls/xinput1_3/xinput_service.h b/dlls/xinput1_3/xinput_service.h
new file mode 100644
index 0000000..445f93e
--- /dev/null
+++ b/dlls/xinput1_3/xinput_service.h
@@ -0,0 +1,101 @@
+/*
+ * The Wine project - Xinput Joystick Library
+ * Copyright 2016 Eric Diaz Fernandez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef XINPUT_SERVICE_H
+#define XINPUT_SERVICE_H
+
+#include "xinput.h"
+#include <stdint.h>
+
+#ifndef XUSER_MAX_COUNT
+#define XUSER_MAX_COUNT 4
+#endif
+
+#define THUMB_TO_BUTTONS_THRESHOLD 16384
+#define TRIGGER_TO_BUTTONS_THRESHOLD 32
+
+#define SERVICE_NAME "/xinput"
+#define SERVICE_SHM_NAME SERVICE_NAME "shm"
+#define SERVICE_SEM_NAME SERVICE_NAME "mtx"
+#define SERVICE_MSG_NAME SERVICE_NAME "msg"
+#define SERVICE_LCK_NAME SERVICE_NAME "lck"
+
+#define XINPUT_OWNER_BROKEN ((pid_t)~0)
+
+#if XINPUT_USES_MQUEUE
+#define MQD_INVALID ((mqd_t)-1)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct xinput_gamepad_state
+{
+    XINPUT_GAMEPAD_EX gamepad;          /* 16 bytes */
+    XINPUT_VIBRATION vibration;        /* 4 bytes  */
+    volatile DWORD dwPacketNumber;      /* 4 bytes  */
+    volatile BOOL connected;           /* 4 bytes  */
+    DWORD _padding_reserved_0;          /* 4 bytes  */
+    /* 32 bytes mark, a reasonable size for a L1 line (half, or equal) */
+};
+
+typedef struct xinput_gamepad_state xinput_gamepad_state;
+
+struct xinput_shared_gamepad_state
+{
+    xinput_gamepad_state state[XUSER_MAX_COUNT]; // 128 bytes
+    volatile DWORD master_pid;
+    char _padding_reserved_0[60];
+    volatile int64_t poke_us;
+    char _padding_reserved_1[56];
+};
+
+typedef struct xinput_shared_gamepad_state xinput_shared_gamepad_state;
+
+struct xinput_gamepad_vibration
+{
+    XINPUT_VIBRATION vibration;
+    int index;
+};
+
+typedef struct xinput_gamepad_vibration xinput_gamepad_vibration;
+
+BOOL xinput_service_self(void);
+void xinput_service_rundll(void);
+
+void xinput_service_server(void);
+
+/**
+ * Returns EEXIST if the server is up (and running)
+ * Returns ENOENT if the server is not running.
+ * Returns some other errors : give up.
+ *
+ * This is a costly call and should only be used by the service itself at
+ * initialization.
+ *
+ * @return
+ */
+
+int xinput_service_poke(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* XINPUT_SERVICE_H */
diff --git a/dlls/xinput1_3/xinput_settings.h b/dlls/xinput1_3/xinput_settings.h
new file mode 100644
index 0000000..c6a5995
--- /dev/null
+++ b/dlls/xinput1_3/xinput_settings.h
@@ -0,0 +1,103 @@
+/*
+ * The Wine project - Xinput Joystick Library
+ * Copyright 2016 Eric Diaz Fernandez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef XINPUT_SETTINGS_H
+#define XINPUT_SETTINGS_H
+
+/**
+ * TRACE the device input state from the reading threads.
+ */
+
+#define XINPUT_TRACE_DEVICE_READER_THREAD 1
+
+/**
+ * TRACE gamepad probe detection superficial information.
+ */
+
+#define XINPUT_TRACE_DEVICE_DETECTION 1
+
+/**
+ * The approximal time in seconds between two probes.
+ */
+
+#define XINPUT_DEVICE_PROBE_PERIOD_S 5
+
+#define XINPUT_OWNER_PROBE_PERIOD_US 200000LL
+
+#define XINPUT_OWNER_REPROBE_PERIOD_US 1000000LL
+
+/**
+ * For the debug functions
+ */
+
+#define XINPUT_MEMDUMP_COLUMNS  16
+
+/**
+ * Trace calls to the xinput1_3 DLL API.
+ */
+
+#define XINPUT_TRACE_INTERFACE_USE 0
+
+/**
+ * Do not enable this on 64/32 bits systems as it is not supported by POSIX.
+ * Cannot properly be shared in a 32/64 environment)
+ *
+ * Given the nature of the shared data, not using a lock should be
+ * of little consequence.
+ */
+
+#define XINPUT_USES_SEMAPHORE_MUTEX 0 /* KEEP TO 0 */
+
+/**
+ * Rumble queries are sent this way.
+ * This feature can be disabled but then of course no rumble will be available.
+ *
+ * Looks like OSX is not fully POSIX so it will need a slightly different way.
+ */
+
+#define XINPUT_USES_MQUEUE 1
+
+/**
+ * Set to 0, the first instance of the DLL will double as a server
+ * If the program containing the server stops, another instance will take
+ * the job.
+ *
+ * Set to 1, the first instance of the DLL will use Rundll32 to run a server
+ * that will live until wine stops (or it is killed)
+ *
+ */
+
+#define XINPUT_RUNDLL 1
+
+/**
+ * The service can be shared among any process but should only run once.
+ * This define sets the global name of the lock.
+ */
+
+#define XINPUT_SYSTEM_WIDE_LOCK_FILE "/tmp/wine.xinput.lock"
+
+/**
+ * The server will look since when any client wrote a timestamp in the shared
+ * memory.  It does it at the same time it probes for gamepads. (ie: 5 seconds)
+ *
+ * After seeing no changes this many times, the server shuts-down.
+ */
+
+#define XINPUT_IDLE_CLIENT_STRIKES  3
+
+#endif /* XINPUT_SETTINGS_H */
diff --git a/dlls/xinput1_3/xinput_tools.c b/dlls/xinput1_3/xinput_tools.c
new file mode 100644
index 0000000..912ed5b
--- /dev/null
+++ b/dlls/xinput1_3/xinput_tools.c
@@ -0,0 +1,126 @@
+/*
+ * The Wine project - Xinput Joystick Library
+ * Copyright 2016 Eric Diaz Fernandez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+#include "xinput_settings.h"
+
+#include <stdint.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <errno.h>
+
+/**
+ * Reads a file descriptor until the amount of bytes has been read.
+ * Retries on EINTR
+ * Stops trying on errors
+ *
+ * @param fd
+ * @param buffer_
+ * @param len
+ * @return
+ */
+
+int read_fully(int fd, void* buffer_, size_t len)
+{
+    uint8_t* buffer = (uint8_t*)buffer_;
+    while(len > 0)
+    {
+        ssize_t n = read(fd, buffer, len);
+        if(n < 0)
+        {
+            int err = errno;
+            if(err == EINTR)
+            {
+                continue;
+            }
+            return err;
+        }
+        buffer += n;
+        len -= n;
+    }
+    return 0;
+}
+
+/**
+ * Writes a file descriptor until the amount of bytes has been written.
+ * Retries on EINTR
+ * Stops trying on errors
+ *
+ * @param fd
+ * @param buffer_
+ * @param len
+ * @return
+ */
+
+int write_fully(int fd, const void* buffer_, size_t len)
+{
+    const uint8_t* buffer = (const uint8_t*)buffer_;
+    while(len > 0)
+    {
+        ssize_t n = write(fd, buffer, len);
+        if(n < 0)
+        {
+            int err = errno;
+            if(err == EINTR)
+            {
+                continue;
+            }
+            return err;
+        }
+        buffer += n;
+        len -= n;
+    }
+    return 0;
+}
+
+/**
+ * Closes a file descriptor.
+ * Retries on EINTR
+ * Stops trying on errors
+ *
+ * @param fd
+ */
+
+void close_ex(int fd)
+{
+    for(;;)
+    {
+        int ret = close(fd);
+        if((ret >= 0) || (errno != EINTR))
+        {
+            break;
+        }
+    }
+}
+
+/**
+ * Returns epoch with a microsecond accuracy.
+ *
+ * @return
+ */
+
+int64_t timeus(void)
+{
+    struct timeval tp;
+    int64_t now;
+    gettimeofday(&tp, NULL);
+    now = tp.tv_sec;
+    now *= 1000000LL;
+    now += tp.tv_usec;
+    return now;
+}
diff --git a/dlls/xinput1_3/xinput_tools.h b/dlls/xinput1_3/xinput_tools.h
new file mode 100644
index 0000000..95e368c
--- /dev/null
+++ b/dlls/xinput1_3/xinput_tools.h
@@ -0,0 +1,90 @@
+/*
+ * The Wine project - Xinput Joystick Library
+ * Copyright 2016 Eric Diaz Fernandez
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details. *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef XINPUT_TOOLS_H
+#define XINPUT_TOOLS_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Reads a file descriptor until the amount of bytes has been read.
+ * Retries on EINTR
+ * Stops trying on errors
+ *
+ * @param fd
+ * @param buffer_
+ * @param len
+ * @return
+ */
+
+int read_fully(int fd, void* buffer_, size_t len);
+
+/**
+ * Writes a file descriptor until the amount of bytes has been written.
+ * Retries on EINTR
+ * Stops trying on errors
+ *
+ * @param fd
+ * @param buffer_
+ * @param len
+ * @return
+ */
+
+int write_fully(int fd, const void* buffer_, size_t len);
+
+/**
+ * Closes a file descriptor.
+ * Retries on EINTR
+ * Stops trying on errors
+ *
+ * @param fd
+ */
+
+void close_ex(int fd);
+
+/**
+ * Returns the current epoch with a microseconds precision
+ *
+ * @return
+ */
+
+int64_t timeus(void);
+
+/**
+ * Returns the nth bit of a byte array.
+ * Bits are given from lsb to msb
+ *
+ * @param array
+ * @param bit
+ * @return
+ */
+
+static inline BOOL bit_get(const uint8_t* array, int bit)
+{
+    return (array[bit >> 3] & (1 << (bit & 7))) != 0;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* XINPUT_TOOLS_H */
diff --git a/include/config.h.in b/include/config.h.in
index bcee452..8706a15 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -558,6 +558,9 @@
 /* Define to 1 if you have the <mpg123.h> header file. */
 #undef HAVE_MPG123_H
 
+/* Define to 1 if you have the `mq_open' function. */
+#undef HAVE_MQ_OPEN
+
 /* Define to 1 if you have the <ncurses.h> header file. */
 #undef HAVE_NCURSES_H
 
@@ -810,6 +813,9 @@
 /* Define to 1 if `interface_id' is a member of `sg_io_hdr_t'. */
 #undef HAVE_SG_IO_HDR_T_INTERFACE_ID
 
+/* Define to 1 if you have the `shm_open' function. */
+#undef HAVE_SHM_OPEN
+
 /* Define if sigaddset is supported */
 #undef HAVE_SIGADDSET
 
-- 
2.10.0




More information about the wine-patches mailing list