Sync to Wine-20050930:
Jakob Eriksson <jakov@vmlinux.org>
- Get rid of HeapAlloc casts.
Robert Reif <reif@earthlink.net>
- Added keyboard data format.
- Fix joystick crash when a button guid is specified in the data
  format.
  Add some parameter checking.
- Pass around real version and do correct thing based on it.
  Change cooperative level to pass on windows.
- Fix SetProperty error returned.
  Add EnumDevice joystick version check.
  Test multiple versions.
- Added dinput tests.
- Effect objects are not supported yet so don't crash when trying to
  enumerate them.
Raphael Junqueira <fenix@club-internet.fr>
- use WINE_DINPUT_KEYBOARD_MAX_KEYS instead hard-coded 256 value
- better use of critical section
- some cleanup to better understand code paths
- logic correction on SetWindowsHookExA/UnhookWindowsHookEx on
  keyboard.
Alexandre Julliard <julliard@winehq.org>
- Build a static list of devices instead of relying on ELF
  constructors.
Gerald Pfeifer <gerald@pfeifer.com>
- Fix compilation of the case without proper Linux joystick support.
James Dean Anderson <petr@pantek.org>
- Report an error if trying to get data from an unacquired mouse.
Lionel Ulmer <lionel.ulmer@free.fr>
- more pretty print for the types flag
- more Windows-aligned check for object instances
- Windows reports 0x80 on pressed buttons not 0xFF
- trace the events reported to the application
- do not flush the buffer when the application only wants to peek the
  number of elements in the queue
- trace the GetDeviceState values before they are reset
Alexandre Julliard <julliard@winehq.org>
- Added rules for building import libraries in the individual dll
  makefiles, and added support for building a .def.a static import
  library too.
- Added rules to build import libraries in the individual dll makefiles.
  Generate import libraries with the right name right away instead of
  using an intermediate .spec.def file.
- Moved config parameters to HKCU\Software\Wine\DirectInput.
- Added magic comments to all Wine-specific registry accesses to make
  them easier to grep.
- Sort entry points alphabetically.
- Removed some unused or redundant configure checks.
  A few cleanups in configure.ac.
- Don't prefix the functions DllCanUnloadNow, DllGetClassObject and
  Dll(Un)RegisterServer with the dll name so that the compiler can check
  the prototypes.
- Fixed some traces to use the right printf format and avoid typecasts.
- Use a more portable scheme for storing the name of a critical
  section.
- Use the COM macros instead of accessing lpVtbl directly.
- We are no longer generating .dbg.c files.
Marcus Meissner <marcus@jet.franken.de>
- The last argument to MultiByteToWideChar is wide character count and
  not the buffer size in bytes. Fixed all places where it was wrong.
- Fixed 3 memset()s which used the wrong size (too large).
Dmitry Timoshkov <dmitry@codeweavers.com>
- Make more of the OLE interface vtables const.
Daniel Remenak <dtremenak@gmail.com>
- Added a linux input system force feedback effect implementation.
- Allow the creation of an FF effect while the joystick is not
  acquired.
- Failing to download an effect after setting parameters is not a
  fatal error.
- Allow enumeration of objects when the device is not yet acquired.
- Flag FF-capable axes during enumeration.
- Protect FF_STATUS usage to avoid compile errors on machines with old
  linux/input.h.
- Implement EnumEffects, CreateEffect, EnumCreatedEffects,
  SendForceFeedbackCommand, and GetForceFeedbackStatus for linux input
  joysticks.
- Correct dinput handling of sliders and non-zero-based axes through the
  linux input system.
- Correctly enumerate evdev joysticks when enumeration is restricted
  with DIEDFL_FORCEFEEDBACK.
- Detect force-feedback-capable linux event device joysticks and return
  DIDC_FORCEFEEDBACK when queried for capabilities.
Mike McCormack <mike@codeweavers.com>
- Interlocked LONG* gcc warning fixes.
- gcc 4.0 warning fixes.
- gcc 4.0 warning fixes for Interlocked* functions.
- Fix warnings for no force feedback case.
Stefan Huehner <stefan@huehner.org>
- Fix some more -Wmissing-declarations warnings.
Vincent Béron <vberon@mecano.gme.usherb.ca>
- Use proper ifdefs around unistd.h.
Modified: trunk/reactos/lib/dinput/device.c
Modified: trunk/reactos/lib/dinput/device_private.h
Modified: trunk/reactos/lib/dinput/dinput.spec
Modified: trunk/reactos/lib/dinput/dinput.xml
Modified: trunk/reactos/lib/dinput/dinput_main.c
Modified: trunk/reactos/lib/dinput/dinput_private.h
Added: trunk/reactos/lib/dinput/effect_linuxinput.c
Modified: trunk/reactos/lib/dinput/joystick_linux.c
Modified: trunk/reactos/lib/dinput/joystick_linuxinput.c
Modified: trunk/reactos/lib/dinput/keyboard.c
Modified: trunk/reactos/lib/dinput/mouse.c
Modified: trunk/reactos/lib/dinput/regsvr.c
Modified: trunk/reactos/lib/dinput/version.rc

Modified: trunk/reactos/lib/dinput/device.c
--- trunk/reactos/lib/dinput/device.c	2005-10-08 19:59:09 UTC (rev 18360)
+++ trunk/reactos/lib/dinput/device.c	2005-10-08 20:11:27 UTC (rev 18361)
@@ -464,7 +464,7 @@
 	LPDIRECTINPUTDEVICE8A iface,HWND hwnd,DWORD dwflags
 ) {
     IDirectInputDevice2AImpl *This = (IDirectInputDevice2AImpl *)iface;
-    TRACE("(this=%p,0x%08lx,0x%08lx)\n",This,(DWORD)hwnd,dwflags);
+    TRACE("(this=%p,%p,0x%08lx)\n",This,hwnd,dwflags);
     if (TRACE_ON(dinput)) {
 	TRACE(" cooperative level : ");
 	_dump_cooperativelevel_DI(dwflags);
@@ -476,7 +476,7 @@
 	LPDIRECTINPUTDEVICE8A iface,HANDLE hnd
 ) {
     IDirectInputDevice2AImpl *This = (IDirectInputDevice2AImpl *)iface;
-    FIXME("(this=%p,0x%08lx): stub\n",This,(DWORD)hnd);
+    FIXME("(this=%p,%p): stub\n",This,hnd);
     return DI_OK;
 }
 

Modified: trunk/reactos/lib/dinput/device_private.h
--- trunk/reactos/lib/dinput/device_private.h	2005-10-08 19:59:09 UTC (rev 18360)
+++ trunk/reactos/lib/dinput/device_private.h	2005-10-08 20:11:27 UTC (rev 18361)
@@ -30,8 +30,8 @@
 typedef struct IDirectInputDevice2AImpl IDirectInputDevice2AImpl;
 struct IDirectInputDevice2AImpl
 {
-        IDirectInputDevice2AVtbl       *lpVtbl;
-        DWORD                           ref;
+        const IDirectInputDevice2AVtbl *lpVtbl;
+        LONG                            ref;
         GUID                            guid;
 };
 

Modified: trunk/reactos/lib/dinput/dinput.spec
--- trunk/reactos/lib/dinput/dinput.spec	2005-10-08 19:59:09 UTC (rev 18360)
+++ trunk/reactos/lib/dinput/dinput.spec	2005-10-08 20:11:27 UTC (rev 18361)
@@ -1,7 +1,7 @@
-@ stdcall DirectInputCreateA(long long ptr ptr)
-@ stdcall DirectInputCreateW(long long ptr ptr)
-@ stdcall DirectInputCreateEx(long long ptr ptr ptr)
-@ stdcall -private DllCanUnloadNow() DINPUT_DllCanUnloadNow
-@ stdcall -private DllGetClassObject(ptr ptr ptr) DINPUT_DllGetClassObject
-@ stdcall -private DllRegisterServer() DINPUT_DllRegisterServer
-@ stdcall -private DllUnregisterServer() DINPUT_DllUnregisterServer
+@ stdcall DirectInputCreateA(long long ptr ptr)
+@ stdcall DirectInputCreateEx(long long ptr ptr ptr)
+@ stdcall DirectInputCreateW(long long ptr ptr)
+@ stdcall -private DllCanUnloadNow()
+@ stdcall -private DllGetClassObject(ptr ptr ptr)
+@ stdcall -private DllRegisterServer()
+@ stdcall -private DllUnregisterServer()
Property changes on: trunk/reactos/lib/dinput/dinput.spec
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: trunk/reactos/lib/dinput/dinput.xml
--- trunk/reactos/lib/dinput/dinput.xml	2005-10-08 19:59:09 UTC (rev 18360)
+++ trunk/reactos/lib/dinput/dinput.xml	2005-10-08 20:11:27 UTC (rev 18361)
@@ -21,6 +21,7 @@
 	<file>data_formats.c</file>
 	<file>device.c</file>
 	<file>dinput_main.c</file>
+	<file>effect_linuxinput.c</file>
 	<file>joystick_linux.c</file>
 	<file>joystick_linuxinput.c</file>
 	<file>keyboard.c</file>

Modified: trunk/reactos/lib/dinput/dinput_main.c
--- trunk/reactos/lib/dinput/dinput_main.c	2005-10-08 19:59:09 UTC (rev 18360)
+++ trunk/reactos/lib/dinput/dinput_main.c	2005-10-08 20:11:27 UTC (rev 18361)
@@ -46,10 +46,10 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
 
-static IDirectInput7AVtbl ddi7avt;
-static IDirectInput7WVtbl ddi7wvt;
-static IDirectInput8AVtbl ddi8avt;
-static IDirectInput8WVtbl ddi8wvt;
+static const IDirectInput7AVtbl ddi7avt;
+static const IDirectInput7WVtbl ddi7wvt;
+static const IDirectInput8AVtbl ddi8avt;
+static const IDirectInput8WVtbl ddi8wvt;
 
 static const struct dinput_device *dinput_devices[] =
 {
@@ -86,7 +86,7 @@
 {
 	IDirectInputImpl* This;
 
-	TRACE("(0x%08lx,%04lx,%s,%p,%p)\n", (DWORD)hinst,dwVersion,debugstr_guid(riid),ppDI,punkOuter);
+	TRACE("(%p,%04lx,%s,%p,%p)\n", hinst,dwVersion,debugstr_guid(riid),ppDI,punkOuter);
 
 	if (IsEqualGUID(&IID_IDirectInputA,riid) ||
 	    IsEqualGUID(&IID_IDirectInput2A,riid) ||
@@ -384,7 +384,7 @@
 							HWND hwndOwner,
 							DWORD dwFlags) {
   IDirectInputImpl *This = (IDirectInputImpl *)iface;
-  FIXME("(%p)->(%08lx,%08lx): stub\n",This, (DWORD) hwndOwner, dwFlags);
+  FIXME("(%p)->(%p,%08lx): stub\n",This, hwndOwner, dwFlags);
 
   return DI_OK;
 }
@@ -535,7 +535,7 @@
 # define XCAST(fun)	(void*)
 #endif
 
-static IDirectInput7AVtbl ddi7avt = {
+static const IDirectInput7AVtbl ddi7avt = {
 	XCAST(QueryInterface)IDirectInputAImpl_QueryInterface,
 	XCAST(AddRef)IDirectInputAImpl_AddRef,
 	XCAST(Release)IDirectInputAImpl_Release,
@@ -555,7 +555,7 @@
 # define XCAST(fun)	(void*)
 #endif
 
-static IDirectInput7WVtbl ddi7wvt = {
+static const IDirectInput7WVtbl ddi7wvt = {
 	XCAST(QueryInterface)IDirectInputWImpl_QueryInterface,
 	XCAST(AddRef)IDirectInputAImpl_AddRef,
 	XCAST(Release)IDirectInputAImpl_Release,
@@ -575,7 +575,7 @@
 # define XCAST(fun)	(void*)
 #endif
 
-static IDirectInput8AVtbl ddi8avt = {
+static const IDirectInput8AVtbl ddi8avt = {
 	XCAST(QueryInterface)IDirectInput8AImpl_QueryInterface,
 	XCAST(AddRef)IDirectInputAImpl_AddRef,
 	XCAST(Release)IDirectInputAImpl_Release,
@@ -595,7 +595,7 @@
 #else
 # define XCAST(fun)	(void*)
 #endif
-static IDirectInput8WVtbl ddi8wvt = {
+static const IDirectInput8WVtbl ddi8wvt = {
 	XCAST(QueryInterface)IDirectInput8WImpl_QueryInterface,
 	XCAST(AddRef)IDirectInputAImpl_AddRef,
 	XCAST(Release)IDirectInputAImpl_Release,
@@ -616,8 +616,8 @@
 typedef struct
 {
     /* IUnknown fields */
-    IClassFactoryVtbl          *lpVtbl;
-    DWORD                       ref;
+    const IClassFactoryVtbl    *lpVtbl;
+    LONG                        ref;
 } IClassFactoryImpl;
 
 static HRESULT WINAPI DICF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
@@ -666,7 +666,7 @@
 	return S_OK;
 }
 
-static IClassFactoryVtbl DICF_Vtbl = {
+static const IClassFactoryVtbl DICF_Vtbl = {
 	DICF_QueryInterface,
 	DICF_AddRef,
 	DICF_Release,
@@ -678,7 +678,7 @@
 /***********************************************************************
  *		DllCanUnloadNow (DINPUT.@)
  */
-HRESULT WINAPI DINPUT_DllCanUnloadNow(void)
+HRESULT WINAPI DllCanUnloadNow(void)
 {
     FIXME("(void): stub\n");
 
@@ -688,8 +688,7 @@
 /***********************************************************************
  *		DllGetClassObject (DINPUT.@)
  */
-HRESULT WINAPI DINPUT_DllGetClassObject(REFCLSID rclsid, REFIID riid,
-					LPVOID *ppv)
+HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
 {
     TRACE("(%s,%s,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
     if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {

Modified: trunk/reactos/lib/dinput/dinput_private.h
--- trunk/reactos/lib/dinput/dinput_private.h	2005-10-08 19:59:09 UTC (rev 18360)
+++ trunk/reactos/lib/dinput/dinput_private.h	2005-10-08 20:11:27 UTC (rev 18361)
@@ -29,8 +29,8 @@
 typedef struct IDirectInputImpl IDirectInputImpl;
 struct IDirectInputImpl
 {
-   LPVOID lpVtbl;
-   DWORD  ref;
+   const void *lpVtbl;
+   LONG  ref;
 
    /* Used to have an unique sequence number for all the events */
    DWORD evsequence;

Copied: trunk/reactos/lib/dinput/effect_linuxinput.c (from rev 18360, vendor/wine/dlls/dinput/current/effect_linuxinput.c)
Property changes on: trunk/reactos/lib/dinput/effect_linuxinput.c ___________________________________________________________________ Name: svn:keywords   + Author Date Id Revision Name: svn:eol-style   + native
Modified: trunk/reactos/lib/dinput/joystick_linux.c
--- trunk/reactos/lib/dinput/joystick_linux.c	2005-10-08 19:59:09 UTC (rev 18360)
+++ trunk/reactos/lib/dinput/joystick_linux.c	2005-10-08 20:11:27 UTC (rev 18361)
@@ -84,12 +84,12 @@
 } POV;
 
 typedef struct JoystickImpl JoystickImpl;
-static IDirectInputDevice8AVtbl JoystickAvt;
-static IDirectInputDevice8WVtbl JoystickWvt;
+static const IDirectInputDevice8AVtbl JoystickAvt;
+static const IDirectInputDevice8WVtbl JoystickWvt;
 struct JoystickImpl
 {
-        LPVOID                          lpVtbl;
-        DWORD                           ref;
+        const void                     *lpVtbl;
+        LONG                            ref;
         GUID                            guid;
 	char				dev[32];
 
@@ -254,10 +254,10 @@
 inline static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
                                     char *buffer, DWORD size )
 {
-    if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, buffer, &size ))
+    if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, (LPBYTE)buffer, &size ))
         return 0;
 
-    if (defkey && !RegQueryValueExA( defkey, name, 0, NULL, buffer, &size ))
+    if (defkey && !RegQueryValueExA( defkey, name, 0, NULL, (LPBYTE)buffer, &size ))
         return 0;
 
     return ERROR_FILE_NOT_FOUND;
@@ -269,28 +269,27 @@
 
 static HRESULT setup_dinput_options(JoystickImpl * device)
 {
-    char buffer[MAX_PATH+1];
+    char buffer[MAX_PATH+16];
     HKEY hkey, appkey = 0;
     DWORD len;
 
     buffer[MAX_PATH]='\0';
 
-    if (RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\dinput", &hkey)) hkey = 0;
+    /* @@ Wine registry key: HKCU\Software\Wine\DirectInput */
+    if (RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\DirectInput", &hkey)) hkey = 0;
 
     len = GetModuleFileNameA( 0, buffer, MAX_PATH );
     if (len && len < MAX_PATH) {
         HKEY tmpkey;
-
-        if (!RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\AppDefaults", &tmpkey )) {
-           char appname[MAX_PATH+16];
-           char *p = strrchr( buffer, '\\' );
-           if (p!=NULL) {
-                   strcpy(appname,p+1);
-                   strcat(appname,"\\dinput");
-                   TRACE("appname = [%s] \n",appname);
-                   if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
-           }
-           RegCloseKey( tmpkey );
+        /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\DirectInput */
+        if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\AppDefaults", &tmpkey ))
+        {
+            char *p, *appname = buffer;
+            if ((p = strrchr( appname, '/' ))) appname = p + 1;
+            if ((p = strrchr( appname, '\\' ))) appname = p + 1;
+            strcat( appname, "\\DirectInput" );
+            if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
+            RegCloseKey( tmpkey );
         }
     }
 
@@ -386,7 +385,7 @@
     return DI_OK;
 }
 
-void calculate_ids(JoystickImpl* device)
+static void calculate_ids(JoystickImpl* device)
 {
     int i;
     int axis = 0;
@@ -437,7 +436,7 @@
     }
 }
 
-static HRESULT alloc_device(REFGUID rguid, LPVOID jvt, IDirectInputImpl *dinput, LPDIRECTINPUTDEVICEA* pdev)
+static HRESULT alloc_device(REFGUID rguid, const void *jvt, IDirectInputImpl *dinput, LPDIRECTINPUTDEVICEA* pdev)
 {
     DWORD i;
     JoystickImpl* newDevice;
@@ -553,7 +552,7 @@
 
     IDirectInputDevice_AddRef((LPDIRECTINPUTDEVICE8A)newDevice->dinput);
     InitializeCriticalSection(&(newDevice->crit));
-    newDevice->crit.DebugInfo->Spare[1] = (DWORD)"DINPUT_Mouse";
+    newDevice->crit.DebugInfo->Spare[0] = (DWORD_PTR)"DINPUT_Mouse";
 
     newDevice->devcaps.dwSize = sizeof(newDevice->devcaps);
     newDevice->devcaps.dwFlags = DIDC_ATTACHED;
@@ -689,7 +688,7 @@
     /* release the data transform filter */
     release_DataFormat(This->transform);
 
-    This->crit.DebugInfo->Spare[1] = 0;
+    This->crit.DebugInfo->Spare[0] = 0;
     DeleteCriticalSection(&(This->crit));
     IDirectInputDevice_Release((LPDIRECTINPUTDEVICE8A)This->dinput);
 
@@ -831,7 +830,7 @@
     return DI_NOEFFECT;
 }
 
-LONG map_axis(JoystickImpl * This, short val, short index)
+static LONG map_axis(JoystickImpl * This, short val, short index)
 {
     double    fval = val;
     double    fmin = This->props[index].lMin;
@@ -849,7 +848,7 @@
 }
 
 /* convert wine format offset to user format object index */
-int offset_to_object(JoystickImpl *This, int offset)
+static int offset_to_object(JoystickImpl *This, int offset)
 {
     int i;
 
@@ -1094,7 +1093,7 @@
     return hr;
 }
 
-int find_property(JoystickImpl * This, LPCDIPROPHEADER ph)
+static int find_property(JoystickImpl * This, LPCDIPROPHEADER ph)
 {
     int i;
     if (ph->dwHow == DIPH_BYOFFSET) {
@@ -1132,7 +1131,7 @@
         _dump_DIPROPHEADER(ph);
 
     if (!HIWORD(rguid)) {
-        switch ((DWORD)rguid) {
+        switch (LOWORD(rguid)) {
         case (DWORD) DIPROP_BUFFERSIZE: {
             LPCDIPROPDWORD	pd = (LPCDIPROPDWORD)ph;
             TRACE("buffersize = %ld\n",pd->dwData);
@@ -1197,7 +1196,7 @@
             break;
         }
         default:
-            FIXME("Unknown type %ld (%s)\n",(DWORD)rguid,debugstr_guid(rguid));
+            FIXME("Unknown type %p (%s)\n",rguid,debugstr_guid(rguid));
             break;
         }
     }
@@ -1213,7 +1212,7 @@
 ) {
     JoystickImpl *This = (JoystickImpl *)iface;
 
-    TRACE("(this=%p,0x%08lx)\n",This,(DWORD)hnd);
+    TRACE("(this=%p,%p)\n",This,hnd);
     This->hEvent = hnd;
     return DI_OK;
 }
@@ -1428,7 +1427,7 @@
         _dump_DIPROPHEADER(pdiph);
 
     if (!HIWORD(rguid)) {
-        switch ((DWORD)rguid) {
+        switch (LOWORD(rguid)) {
         case (DWORD) DIPROP_BUFFERSIZE: {
             LPDIPROPDWORD	pd = (LPDIPROPDWORD)pdiph;
             TRACE(" return buffersize = %d\n",This->queue_len);
@@ -1469,7 +1468,7 @@
             break;
         }
         default:
-            FIXME("Unknown type %ld (%s)\n",(DWORD)rguid,debugstr_guid(rguid));
+            FIXME("Unknown type %p (%s)\n",rguid,debugstr_guid(rguid));
             break;
         }
     }
@@ -1640,7 +1639,7 @@
     return DI_OK;
 }
 
-static IDirectInputDevice8AVtbl JoystickAvt =
+static const IDirectInputDevice8AVtbl JoystickAvt =
 {
 	IDirectInputDevice2AImpl_QueryInterface,
 	IDirectInputDevice2AImpl_AddRef,
@@ -1682,7 +1681,7 @@
 # define XCAST(fun)	(void*)
 #endif
 
-static IDirectInputDevice8WVtbl SysJoystickWvt =
+static const IDirectInputDevice8WVtbl SysJoystickWvt =
 {
 	IDirectInputDevice2WImpl_QueryInterface,
 	XCAST(AddRef)IDirectInputDevice2AImpl_AddRef,

Modified: trunk/reactos/lib/dinput/joystick_linuxinput.c
--- trunk/reactos/lib/dinput/joystick_linuxinput.c	2005-10-08 19:59:09 UTC (rev 18360)
+++ trunk/reactos/lib/dinput/joystick_linuxinput.c	2005-10-08 20:11:27 UTC (rev 18361)
@@ -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
@@ -43,6 +44,9 @@
 #endif
 #ifdef HAVE_LINUX_INPUT_H
 # include <linux/input.h>
+# if defined(EVIOCGBIT) && defined(EV_ABS) && defined(BTN_PINKIE)
+#  define HAVE_CORRECT_LINUXINPUT_H
+# endif
 #endif
 
 #include "wine/debug.h"
@@ -65,13 +69,25 @@
 #define WINE_JOYSTICK_AXIS_BASE   0
 #define WINE_JOYSTICK_BUTTON_BASE 8
 
+typedef struct EffectListItem EffectListItem;
+struct EffectListItem
+{
+        LPDIRECTINPUTEFFECT ref;
+	struct EffectListItem* next;
+};
+
+/* implemented in effect_linuxinput.c */
+HRESULT linuxinput_create_effect(int* fd, REFGUID rguid, LPDIRECTINPUTEFFECT* peff);
+HRESULT linuxinput_get_info_A(int fd, REFGUID rguid, LPDIEFFECTINFOA info);
+HRESULT linuxinput_get_info_W(int fd, REFGUID rguid, LPDIEFFECTINFOW info);
+
 typedef struct JoystickImpl JoystickImpl;
-static IDirectInputDevice8AVtbl JoystickAvt;
-static IDirectInputDevice8WVtbl JoystickWvt;
+static const IDirectInputDevice8AVtbl JoystickAvt;
+static const IDirectInputDevice8WVtbl JoystickWvt;
 struct JoystickImpl
 {
-        LPVOID                          lpVtbl;
-        DWORD                           ref;
+        const void                     *lpVtbl;
+        LONG                            ref;
         GUID                            guid;
 
 
@@ -97,6 +113,12 @@
 	BOOL				overflow;
 	DIJOYSTATE2			js;
 
+	/* Force feedback variables */
+	BOOL				has_ff;
+	int				num_effects;
+	EffectListItem*			top_effect;
+	int				ff_state;
+
 	/* data returned by the EVIOCGABS() ioctl */
 	int				axes[ABS_MAX+1][5];
 
@@ -107,9 +129,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. */
@@ -124,17 +148,24 @@
 
 #define test_bit(arr,bit) (((BYTE*)arr)[bit>>3]&(1<<(bit&7)))
 
-static int joydev_have(void)
+static int joydev_have(BOOL require_ff)
 {
-  int i, fd;
+  int i, fd, flags, num_effects;
   int havejoy = 0;
 
   for (i=0;i<64;i++) {
       char	buf[200];
       BYTE	absbits[(ABS_MAX+7)/8],keybits[(KEY_MAX+7)/8];
+      BYTE	evbits[(EV_MAX+7)/8],ffbits[(FF_MAX+7)/8];
 
       sprintf(buf,EVDEVPREFIX"%d",i);
-      if (-1!=(fd=open(buf,O_RDONLY))) {
+
+      if (require_ff) 
+	  flags = O_RDWR;
+      else
+	  flags = O_RDONLY;
+
+      if (-1!=(fd=open(buf,flags))) {
 	  if (-1==ioctl(fd,EVIOCGBIT(EV_ABS,sizeof(absbits)),absbits)) {
 	      perror("EVIOCGBIT EV_ABS");
 	      close(fd);
@@ -145,6 +176,23 @@
 	      close(fd);
 	      continue;
 	  }
+
+	  /* test for force feedback if it's required */
+	  if (require_ff) {
+	      if ((-1==ioctl(fd,EVIOCGBIT(0,sizeof(evbits)),evbits))) {
+	          perror("EVIOCGBIT 0");
+	          close(fd);
+	          continue; 
+	      }
+	      if (   (!test_bit(evbits,EV_FF))
+	          || (-1==ioctl(fd,EVIOCGBIT(EV_FF,sizeof(ffbits)),ffbits)) 
+                  || (-1==ioctl(fd,EVIOCGEFFECTS,&num_effects))
+                  || (num_effects <= 0)) {
+		  close(fd);
+	          continue;
+	      }
+	  }
+
 	  /* A true joystick has at least axis X and Y, and at least 1
 	   * button. copied from linux/drivers/input/joydev.c */
 	  if (test_bit(absbits,ABS_X) && test_bit(absbits,ABS_Y) &&
@@ -176,10 +224,12 @@
         (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
     return FALSE;
 
+#ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
   if (dwFlags & DIEDFL_FORCEFEEDBACK)
     return FALSE;
+#endif
 
-  havejoy = joydev_have();
+  havejoy = joydev_have(dwFlags & DIEDFL_FORCEFEEDBACK);
 
   if (!havejoy)
       return FALSE;
@@ -214,10 +264,12 @@
         (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
     return FALSE;
 
+#ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
   if (dwFlags & DIEDFL_FORCEFEEDBACK)
     return FALSE;
+#endif
 
-  havejoy = joydev_have();
+  havejoy = joydev_have(dwFlags & DIEDFL_FORCEFEEDBACK);
 
   if (!havejoy)
       return FALSE;
@@ -240,7 +292,7 @@
   return TRUE;
 }
 
-static JoystickImpl *alloc_device(REFGUID rguid, LPVOID jvt, IDirectInputImpl *dinput)
+static JoystickImpl *alloc_device(REFGUID rguid, const void *jvt, IDirectInputImpl *dinput)
 {
   JoystickImpl* newDevice;
   int i;
@@ -250,6 +302,9 @@
   newDevice->ref = 1;
   newDevice->joyfd = -1;
   newDevice->dinput = dinput;
+#ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
+  newDevice->ff_state = FF_STATUS_STOPPED;
+#endif
   memcpy(&(newDevice->guid),rguid,sizeof(*rguid));
   for (i=0;i<ABS_MAX;i++) {
     newDevice->wantmin[i] = -32768;
@@ -269,7 +324,7 @@
 {
   int havejoy = 0;
 
-  havejoy = joydev_have();
+  havejoy = joydev_have(FALSE);
 
   if (!havejoy)
       return DIERR_DEVICENOTREG;
@@ -296,7 +351,7 @@
 {
   int havejoy = 0;
 
-  havejoy = joydev_have();
+  havejoy = joydev_have(FALSE);
 
   if (!havejoy)
       return DIERR_DEVICENOTREG;
@@ -338,6 +393,9 @@
 	if (ref)
 		return ref;
 
+	/* Reset the FF state, free all effects, etc */
+	IDirectInputDevice8_SendForceFeedbackCommand(iface, DISFFC_RESET);
+
 	/* Free the data queue */
 	HeapFree(GetProcessHeap(),0,This->data_queue);
 
@@ -379,19 +437,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 +478,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])))
@@ -468,8 +563,10 @@
     if (xmin == xmax) return val;
 
     /* map the value from the hmin-hmax range into the wmin-wmax range */
-    ret = (val * (wmax-wmin)) / (hmax-hmin) + wmin;
+    ret = ((val-hmin) * (wmax-wmin)) / (hmax-hmin) + wmin;
 
+    TRACE("xmin=%d xmax=%d hmin=%d hmax=%d wmin=%d wmax=%d val=%d ret=%d\n", xmin, xmax, hmin, hmax, wmin, wmax, val, ret);
+
 #if 0
     /* deadzone doesn't work comfortably enough right now. needs more testing*/
     if ((ret > -deadz/2 ) && (ret < deadz/2)) {
@@ -492,6 +589,8 @@
 	ji->js.lRx = map_axis(ji, ABS_RX, ji->axes[ABS_RX][AXE_ABS]);
 	ji->js.lRy = map_axis(ji, ABS_RY, ji->axes[ABS_RY][AXE_ABS]);
 	ji->js.lRz = map_axis(ji, ABS_RZ, ji->axes[ABS_RZ][AXE_ABS]);
+	ji->js.rglSlider[0] = map_axis(ji, ABS_THROTTLE, ji->axes[ABS_THROTTLE][AXE_ABS]);
+	ji->js.rglSlider[1] = map_axis(ji, ABS_RUDDER,   ji->axes[ABS_RUDDER  ][AXE_ABS]);
 }
 
 static void joy_polldev(JoystickImpl *This) {
@@ -613,11 +712,24 @@
 		This->js.lRz = map_axis(This,ABS_RZ,ie.value);
 		GEN_EVENT(DIJOFS_RZ,This->js.lRz,ie.time.tv_usec,(This->dinput->evsequence)++);
 		break;
+	    case ABS_THROTTLE:
+                This->js.rglSlider[0] = map_axis(This,ABS_THROTTLE,ie.value);
+                GEN_EVENT(DIJOFS_SLIDER(0),This->js.rglSlider[0],ie.time.tv_usec,(This->dinput->evsequence)++);
+                break;
+	    case ABS_RUDDER:
+                This->js.rglSlider[1] = map_axis(This,ABS_RUDDER,ie.value);
+                GEN_EVENT(DIJOFS_SLIDER(1),This->js.rglSlider[1],ie.time.tv_usec,(This->dinput->evsequence)++);
+                break;
 	    default:
 		FIXME("unhandled joystick axe event (code %d, value %d)\n",ie.code,ie.value);
 		break;
 	    }
 	    break;
+#ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
+	case EV_FF_STATUS:
+	    This->ff_state = ie.value;
+	    break;
+#endif
 	default:
 	    FIXME("joystick cannot handle type %d event (code %d)\n",ie.type,ie.code);
 	    break;
@@ -683,7 +795,7 @@
   FIXME("ph.dwSize = %ld, ph.dwHeaderSize =%ld, ph.dwObj = %ld, ph.dwHow= %ld\n",ph->dwSize, ph->dwHeaderSize,ph->dwObj,ph->dwHow);
 
   if (!HIWORD(rguid)) {
-    switch ((DWORD)rguid) {
+    switch (LOWORD(rguid)) {
     case (DWORD) DIPROP_BUFFERSIZE: {
       LPCDIPROPDWORD	pd = (LPCDIPROPDWORD)ph;
 
@@ -701,6 +813,8 @@
       case 12:  /* Rx */
       case 16:  /* Ry */
       case 20:  /* Rz */
+      case 24:  /* Slider 0 -> Throttle */
+      case 28:  /* Slider 1 -> Rudder */
 	  This->wantmin[ph->dwObj/4] = pr->lMin;
 	  This->wantmax[ph->dwObj/4] = pr->lMax;
 	  break;
@@ -717,7 +831,7 @@
       break;
     }
     default:
-      FIXME("Unknown type %ld (%s)\n",(DWORD)rguid,debugstr_guid(rguid));
+      FIXME("Unknown type %p (%s)\n",rguid,debugstr_guid(rguid));
       break;
     }
   }
@@ -733,7 +847,7 @@
 ) {
     JoystickImpl *This = (JoystickImpl *)iface;
 
-    TRACE("(this=%p,0x%08lx)\n",This,(DWORD)hnd);
+    TRACE("(this=%p,%p)\n",This,hnd);
     This->hEvent = hnd;
     return DI_OK;
 }
@@ -765,6 +879,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;
 
@@ -795,7 +912,6 @@
   DIDEVICEOBJECTINSTANCEA ddoi;
   int xfd = This->joyfd;
 
-
   TRACE("(this=%p,%p,%p,%08lx)\n", This, lpCallback, lpvRef, dwFlags);
   if (TRACE_ON(dinput)) {
     TRACE("  - flags = ");
@@ -803,7 +919,9 @@
     TRACE("\n");
   }
 
-  if (xfd == -1) return DIERR_NOTACQUIRED;
+  /* We need to work even if we're not yet acquired */
+  if (xfd == -1)
+    IDirectInputDevice8_Acquire(iface);
 
   /* Only the fields till dwFFMaxForce are relevant */
   ddoi.dwSize = FIELD_OFFSET(DIDEVICEOBJECTINSTANCEA, dwFFMaxForce);
@@ -846,14 +964,27 @@
 	ddoi.guidType = GUID_Slider;
 	ddoi.dwOfs = DIJOFS_SLIDER(0);
 	break;
+      case ABS_RUDDER:
+	ddoi.guidType = GUID_Slider;
+	ddoi.dwOfs = DIJOFS_SLIDER(1);
+	break;
       default:
 	FIXME("unhandled abs axis %d, ignoring!\n",i);
       }
       ddoi.dwType = DIDFT_MAKEINSTANCE((1<<i) << WINE_JOYSTICK_AXIS_BASE) | DIDFT_ABSAXIS;
+      /* Linux event force feedback supports only (and always) x and y axes */
+      if (i == ABS_X || i == ABS_Y) {
+	if (This->has_ff)
+	  ddoi.dwFlags |= DIDOI_FFACTUATOR;
+      }
       sprintf(ddoi.tszName, "%d-Axis", i);
       _dump_OBJECTINSTANCEA(&ddoi);
-      if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE)
-	  return DI_OK;
+      if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) {
+	/* return to unaquired state if that's where we were */
+	if (xfd == -1)
+	  IDirectInputDevice8_Unacquire(iface);
+	return DI_OK;
+      }
     }
   }
 
@@ -936,13 +1067,18 @@
       }
       sprintf(ddoi.tszName, "%d-Button", i);
       _dump_OBJECTINSTANCEA(&ddoi);
-      if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE)
-	  return DI_OK;
+      if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) {
+	/* return to unaquired state if that's where we were */
+	if (xfd == -1)
+	  IDirectInputDevice8_Unacquire(iface);
+	return DI_OK;
+      }
     }
   }
 
-  if (xfd!=This->joyfd)
-    close(xfd);
+  /* return to unaquired state if that's where we were */
+  if (xfd == -1)
+    IDirectInputDevice8_Unacquire(iface);
 
   return DI_OK;
 }
@@ -978,7 +1114,7 @@
     _dump_DIPROPHEADER(pdiph);
 
   if (!HIWORD(rguid)) {
-    switch ((DWORD)rguid) {
+    switch (LOWORD(rguid)) {
     case (DWORD) DIPROP_BUFFERSIZE: {
       LPDIPROPDWORD	pd = (LPDIPROPDWORD)pdiph;
 
@@ -999,7 +1135,7 @@
     }
 
     default:
-      FIXME("Unknown type %ld (%s)\n",(DWORD)rguid,debugstr_guid(rguid));
+      FIXME("Unknown type %p (%s)\n",rguid,debugstr_guid(rguid));
       break;
     }
   }
@@ -1008,8 +1144,352 @@
   return DI_OK;
 }
 
-static IDirectInputDevice8AVtbl JoystickAvt =
+/****************************************************************************** 
+  *	CreateEffect - Create a new FF effect with the specified params
+  */
+static HRESULT WINAPI JoystickAImpl_CreateEffect(LPDIRECTINPUTDEVICE8A iface,
+						 REFGUID rguid,
+						 LPCDIEFFECT lpeff,
+						 LPDIRECTINPUTEFFECT *ppdef,
+						 LPUNKNOWN pUnkOuter)
 {
+#ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
+    EffectListItem* new = NULL;
+    HRESULT retval = DI_OK;
+#endif
+
+    JoystickImpl* This = (JoystickImpl*)iface;
+    TRACE("(this=%p,%p,%p,%p,%p)\n", This, rguid, lpeff, ppdef, pUnkOuter);
+
+#ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
+    TRACE("not available (compiled w/o ff support)\n");
+    *ppdef = NULL;
+    return DI_OK; 
+#else
+
+    new = malloc(sizeof(EffectListItem));
+    new->next = This->top_effect;
+    This->top_effect = new;
+
+    retval = linuxinput_create_effect(&(This->joyfd), rguid, &(new->ref));
+    if (retval != DI_OK)
+	return retval;
+ 
+    if (lpeff != NULL)
+	retval = IDirectInputEffect_SetParameters(new->ref, lpeff, 0);
+    if (retval != DI_OK && retval != DI_DOWNLOADSKIPPED)
+	return retval;
+
+    *ppdef = new->ref;
+
+    if (pUnkOuter != NULL)
+	FIXME("Interface aggregation not implemented.\n");
+
+    return DI_OK;
+
+#endif /* HAVE_STRUCT_FF_EFFECT_DIRECTION */
+} 
+
+/*******************************************************************************
+ *	EnumEffects - Enumerate available FF effects
+ */
+static HRESULT WINAPI JoystickAImpl_EnumEffects(LPDIRECTINPUTDEVICE8A iface,
+						LPDIENUMEFFECTSCALLBACKA lpCallback,
+						LPVOID pvRef,
+						DWORD dwEffType)
+{
+#ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
+    DIEFFECTINFOA dei; /* feif */
+    DWORD type = DIEFT_GETTYPE(dwEffType);
+    JoystickImpl* This = (JoystickImpl*)iface;
+    int xfd = This->joyfd;
+
+    TRACE("(this=%p,%p,%ld) type=%ld fd=%d\n", This, pvRef, dwEffType, type, xfd);
+
+    dei.dwSize = sizeof(DIEFFECTINFOA);          
+
+    /* We need to return something even if we're not yet acquired */
+    if (xfd == -1)
+	IDirectInputDevice8_Acquire(iface);
+
+    if ((type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE)
+	&& test_bit(This->ffbits, FF_CONSTANT)) {
+	IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_ConstantForce);
+	(*lpCallback)(&dei, pvRef);
+    }
+
+    if ((type == DIEFT_ALL || type == DIEFT_PERIODIC)
+	&& test_bit(This->ffbits, FF_PERIODIC)) {
+	if (test_bit(This->ffbits, FF_SQUARE)) {
+	    IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Square);
+	    (*lpCallback)(&dei, pvRef);
+	}
+	if (test_bit(This->ffbits, FF_SINE)) {
+            IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Sine);
+	    (*lpCallback)(&dei, pvRef);
+	}
+	if (test_bit(This->ffbits, FF_TRIANGLE)) {
+	    IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Triangle);
+	    (*lpCallback)(&dei, pvRef);
+	}
+	if (test_bit(This->ffbits, FF_SAW_UP)) {
+	    IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothUp);
+	    (*lpCallback)(&dei, pvRef);
+	}
+	if (test_bit(This->ffbits, FF_SAW_DOWN)) {
+	    IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothDown);
+	    (*lpCallback)(&dei, pvRef);
+	}
+    } 
+
+    if ((type == DIEFT_ALL || type == DIEFT_RAMPFORCE)
+	&& test_bit(This->ffbits, FF_RAMP)) {
+        IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_RampForce);
+        (*lpCallback)(&dei, pvRef);
+    }
+
+    if (type == DIEFT_ALL || type == DIEFT_CONDITION) {
+	if (test_bit(This->ffbits, FF_SPRING)) {
+	    IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Spring);
+	    (*lpCallback)(&dei, pvRef);
+	}
+	if (test_bit(This->ffbits, FF_DAMPER)) {
+	    IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Damper);
+	    (*lpCallback)(&dei, pvRef);
+	}
+	if (test_bit(This->ffbits, FF_INERTIA)) {
+	    IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Inertia);
+	    (*lpCallback)(&dei, pvRef);
+	}
+	if (test_bit(This->ffbits, FF_FRICTION)) {
+	    IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Friction);
+	    (*lpCallback)(&dei, pvRef);
+	}
+    }
+
+    /* return to unaquired state if that's where it was */
+    if (xfd == -1)
+	IDirectInputDevice8_Unacquire(iface);
+#endif
+
+    return DI_OK;
+}
+
[truncated at 1000 lines; 564 more skipped]