Detect force-feedback-capable linux event device joysticks and return
DIDC_FORCEFEEDBACK when queried for capabilities.

diff --git a/configure b/configure
index bfd10f7..f16079c 100755
--- a/configure
+++ b/configure
@@ -17574,6 +17574,121 @@
 
     fi
 
+echo "$as_me:$LINENO: checking for struct ff_effect.direction" >&5
+echo $ECHO_N "checking for struct ff_effect.direction... $ECHO_C" >&6
+if test "${ac_cv_member_struct_ff_effect_direction+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef HAVE_LINUX_INPUT_H
+#include <linux/input.h>
+#endif
+
+int
+main ()
+{
+static struct ff_effect ac_aggr;
+if (ac_aggr.direction)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_member_struct_ff_effect_direction=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef HAVE_LINUX_INPUT_H
+#include <linux/input.h>
+#endif
+
+int
+main ()
+{
+static struct ff_effect ac_aggr;
+if (sizeof ac_aggr.direction)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_member_struct_ff_effect_direction=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_member_struct_ff_effect_direction=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_member_struct_ff_effect_direction" >&5
+echo "${ECHO_T}$ac_cv_member_struct_ff_effect_direction" >&6
+if test $ac_cv_member_struct_ff_effect_direction = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_FF_EFFECT_DIRECTION 1
+_ACEOF
+
+
+fi
+
+
 echo "$as_me:$LINENO: checking for sigaddset" >&5
 echo $ECHO_N "checking for sigaddset... $ECHO_C" >&6
 if test "${wine_cv_have_sigaddset+set}" = set; then
diff --git a/configure.ac b/configure.ac
index 7e32505..a5dffa3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1289,6 +1289,11 @@
                   [Define if we have linux/input.h AND it contains the INPUT event API])
     fi
 
+AC_CHECK_MEMBERS([struct ff_effect.direction],,,
+[#ifdef HAVE_LINUX_INPUT_H
+#include <linux/input.h>
+#endif])
+
 AC_CACHE_CHECK([for sigaddset],wine_cv_have_sigaddset,
                AC_TRY_LINK([#include <signal.h>],[sigset_t set; sigaddset(&set,SIGTERM);],
                            wine_cv_have_sigaddset=yes,wine_cv_have_sigaddset=no))
diff --git a/dlls/dinput/joystick_linuxinput.c b/dlls/dinput/joystick_linuxinput.c
index 516066d..b46d27d 100644
--- a/dlls/dinput/joystick_linuxinput.c
+++ b/dlls/dinput/joystick_linuxinput.c
@@ -3,6 +3,7 @@
  * Copyright 1998,2000 Marcus Meissner
  * Copyright 1998,1999 Lionel Ulmer
  * Copyright 2000-2001 TransGaming Technologies Inc.
+ * Copyright 2005 Daniel Remenak
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -97,6 +98,10 @@
 	BOOL				overflow;
 	DIJOYSTATE2			js;
 
+	/* Force feedback variables */
+	BOOL				has_ff;
+	int				num_effects;
+
 	/* data returned by the EVIOCGABS() ioctl */
 	int				axes[ABS_MAX+1][5];
 
@@ -107,9 +112,11 @@
 #define AXE_ABSFLAT	4
 
 
-	/* data returned by EVIOCGBIT for EV_ABS and EV_KEY */
+	/* data returned by EVIOCGBIT for caps, EV_ABS, EV_KEY, and EV_FF */
+	BYTE				evbits[(EV_MAX+7)/8];
 	BYTE				absbits[(ABS_MAX+7)/8];
 	BYTE				keybits[(KEY_MAX+7)/8];
+	BYTE				ffbits[(FF_MAX+7)/8];	
 };
 
 /* This GUID is slightly different from the linux joystick one. Take note. */
@@ -379,19 +386,32 @@
     int		i;
     JoystickImpl *This = (JoystickImpl *)iface;
     char	buf[200];
+    BOOL	readonly = TRUE;
 
     TRACE("(this=%p)\n",This);
     if (This->joyfd!=-1)
     	return 0;
     for (i=0;i<64;i++) {
       sprintf(buf,EVDEVPREFIX"%d",i);
-      if (-1==(This->joyfd=open(buf,O_RDONLY))) {
-	if (errno==ENODEV)
-	  return DIERR_NOTFOUND;
-	perror(buf);
-	continue;
+      if (-1==(This->joyfd=open(buf,O_RDWR))) { 
+        if (-1==(This->joyfd=open(buf,O_RDONLY))) {
+	  /* Couldn't open the device at all */ 
+	  if (errno==ENODEV)
+	    return DIERR_NOTFOUND;
+	  perror(buf);
+	  continue;
+	}
+	else {
+	  /* Couldn't open in r/w but opened in read-only. */
+          WARN("Could not open %s in read-write mode.  Force feedback will be disabled.\n",buf);
+	}
       }
-      if ((-1!=ioctl(This->joyfd,EVIOCGBIT(EV_ABS,sizeof(This->absbits)),This->absbits)) &&
+      else {
+	/* Opened device in read-write */
+	readonly = FALSE;
+      }
+      if ((-1!=ioctl(This->joyfd,EVIOCGBIT(0,sizeof(This->evbits)),This->evbits)) &&
+	  (-1!=ioctl(This->joyfd,EVIOCGBIT(EV_ABS,sizeof(This->absbits)),This->absbits)) &&
           (-1!=ioctl(This->joyfd,EVIOCGBIT(EV_KEY,sizeof(This->keybits)),This->keybits)) &&
           (test_bit(This->absbits,ABS_X) && test_bit(This->absbits,ABS_Y) &&
 	   (test_bit(This->keybits,BTN_TRIGGER)||
@@ -407,6 +427,30 @@
     if (This->joyfd==-1)
     	return DIERR_NOTFOUND;
 
+    This->has_ff = FALSE;
+    This->num_effects = 0;
+
+#ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
+    if (!readonly && test_bit(This->evbits, EV_FF)) {
+        if (-1!=ioctl(This->joyfd,EVIOCGBIT(EV_FF,sizeof(This->ffbits)),This->ffbits)) {
+	    if (-1!=ioctl(This->joyfd,EVIOCGEFFECTS,&This->num_effects) 
+		&& This->num_effects > 0) {
+		This->has_ff = TRUE;
+		TRACE("Joystick seems to be capable of force feedback.\n");
+	    }
+	    else {
+		TRACE("Joystick does not support any effects, disabling force feedback.\n");
+	    }
+        }
+        else {
+            TRACE("Could not get EV_FF bits; disabling force feedback.\n");
+        }
+    }
+    else {
+        TRACE("Force feedback disabled (device is readonly or joystick incapable).\n");
+    }
+#endif
+
     for (i=0;i<ABS_MAX;i++) {
 	if (test_bit(This->absbits,i)) {
 	  if (-1==ioctl(This->joyfd,EVIOCGABS(i),&(This->axes[i])))
@@ -779,6 +823,9 @@
     buttons=0;
     for (i=0;i<KEY_MAX;i++) if (test_bit(This->keybits,i)) buttons++;
 
+    if (This->has_ff) 
+	 lpDIDevCaps->dwFlags |= DIDC_FORCEFEEDBACK;
+
     lpDIDevCaps->dwAxes = axes;
     lpDIDevCaps->dwButtons = buttons;
 
diff --git a/include/config.h.in b/include/config.h.in
index 824a53f..03e61d8 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -596,6 +596,9 @@
 /* Define to 1 if you have the `strncasecmp' function. */
 #undef HAVE_STRNCASECMP
 
+/* Define to 1 if `direction' is member of `struct ff_effect'. */
+#undef HAVE_STRUCT_FF_EFFECT_DIRECTION
+
 /* Define to 1 if `msg_accrights' is member of `struct msghdr'. */
 #undef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS