/*************************************************************************** * * vjoy.c * * Virtual (analog) joystick driver for ntvdm * * Copyright (c) 1991-1996 Microsoft Corporation. All Rights Reserved. * ***************************************************************************/ /***************************************************************************** * * #includes * *****************************************************************************/ #include // The VDD is a win32 DLL #include // Definition of VDD calls #include #include #define JOY_DRIVER_PATH L"\\\\.\\JOY1" #define JOYSTICK_POLL_INTERVAL 100 JOY_DD_INPUT_DATA JoyData; HANDLE hJoyDriver = INVALID_HANDLE_VALUE; BOOL bInAnalogRead = FALSE; BOOL bDataValid = FALSE; ULONG ReadsSinceLastPortWrite = 0; BOOL bEnabled = FALSE; BOOL bAttemptedInit = FALSE; UCHAR JoyFlags = 0xf; USHORT TimeNdx; ULONG Times[4]; UCHAR Values[4]; LONG InitialCount; /* * DLL entry point routine. * Returns TRUE on success. */ BOOL WINAPI DllEntryPoint( HINSTANCE hInstance, DWORD reason, LPVOID reserved ) { static VDD_IO_PORTRANGE PortRange; static VDD_IO_HANDLERS handlers = { JoystickPortRead, NULL, NULL, NULL, JoystickPortWrite, NULL, NULL, NULL}; switch (reason) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(hInstance); PortRange.First = JOYSTICK_PORT; PortRange.Last = JOYSTICK_PORT; if (!VDDInstallIOHook((HANDLE)hInstance, 1, &PortRange, &handlers)) { return FALSE; } if (!JoystickInit()) { VDDDeInstallIOHook((HANDLE)hInstance, 1, &PortRange); return FALSE; } return TRUE; case DLL_PROCESS_DETACH: bEnabled = FALSE; // tell thread to exit VDDDeInstallIOHook((HANDLE)hInstance, 1, &PortRange); return TRUE; default: return TRUE; } } BOOL JoystickInit( VOID ) { HANDLE tHandle; ULONG ThreadId; ULONG numread; // BUGBUG: Should be using MM calls instead of this hJoyDriver = CreateFile(JOY_DRIVER_PATH, GENERIC_READ, 0, (LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING, 0, (HANDLE) NULL); if (hJoyDriver != INVALID_HANDLE_VALUE) { if(!(tHandle = CreateThread(NULL, 0, JoystickPollThread, NULL, CREATE_SUSPENDED, &ThreadId))) { CloseHandle(hJoyDriver); return FALSE; } bEnabled = TRUE; // // Do an initial read // if (ReadFile(hJoyDriver, &JoyData, sizeof(JOY_DD_INPUT_DATA), &numread, NULL)) { bDataValid = TRUE; } else { bDataValid = FALSE; } ResumeThread(tHandle); CloseHandle(tHandle); } return TRUE; } VOID JoystickPortRead( WORD port, BYTE *pData ) { BYTE data = 0xff; USHORT CurrentTime; LONG TargetTime; USHORT TargetCount; LONG USecsElapsed; ULONG TargetValue; static USHORT LastWriteTime; static LONG InitialCount; if (!bAttemptedInit) { bAttemptedInit = TRUE; JoystickInit(); } if (!bEnabled) { *pData = data; return; } if (bDataValid && !JoyData.Unplugged) { // // Get the button state // data = (BYTE) ((~JoyData.Buttons << 4) & 0xf0); // // Get the analog resistive inputs // if (bInAnalogRead) { if (!ReadsSinceLastPortWrite) { VdmParametersInfo(VDM_GET_TIMER0_INITIAL_COUNT, &InitialCount, sizeof(LONG)); VdmParametersInfo(VDM_GET_LAST_UPDATED_TIMER0_COUNT, &LastWriteTime, sizeof(USHORT)); VDM_TRACE(0x6a1, (USHORT) 0, LastWriteTime); } if (++ReadsSinceLastPortWrite > 256) { // Too much time elapsed, we are done VDM_TRACE(0x6bf, 0, 0); if (JoyData.XTime) { JoyFlags &= ~1; } if (JoyData.YTime) { JoyFlags &= ~2; } if (JoyData.ZTime) { JoyFlags &= ~4; } if (JoyData.TTime) { JoyFlags &= ~8; } bInAnalogRead = FALSE; } else { TargetTime = (LONG)(LastWriteTime - (USHORT)(Times[TimeNdx]*3)); if (TargetTime < 0) { TargetTime += InitialCount; } TargetCount = (USHORT) TargetTime; VdmParametersInfo(VDM_SET_NEXT_TIMER0_COUNT, &TargetCount, sizeof(USHORT)); VDM_TRACE(0x6b2, Values[TimeNdx], TargetTime); JoyFlags &= ~Values[TimeNdx]; if (++TimeNdx >= 4) { bInAnalogRead = FALSE; } } } data += JoyFlags; } *pData = data; // VDM_TRACE(0x6b0, data, 0); } VOID JoystickPortWrite( WORD port, BYTE data ) { ULONG numread; ULONG TTim; CHAR TVal; int i, j; if (!bAttemptedInit) { bAttemptedInit = TRUE; JoystickInit(); } ReadsSinceLastPortWrite = 0; JoyFlags = 0xf; Values[0] = 1; Values[1] = 2; Values[2] = 4; Values[3] = 8; Times[0] = JoyData.XTime; Times[1] = JoyData.YTime; Times[2] = JoyData.ZTime; Times[3] = JoyData.TTime; // // Sort them // Using just a bubble sort here with 4 items... // for (i=0; i<3; i++) { for (j=0; j<3-i; j++) { if (Times[j] > Times[j+1]) { TTim = Times[j]; TVal = Values[j]; Times[j] = Times[j+1]; Values[j] = Values[j+1]; Times[j+1] = TTim; Values[j+1] = TVal; } } } for (TimeNdx=0; TimeNdx<4; TimeNdx++) { if (Times[TimeNdx]) { break; } } if (TimeNdx < 4) { bInAnalogRead = TRUE; } for (i=0; i<4; i++) { VDM_TRACE(0x6a0, (USHORT) Values[i], Times[i]); } } DWORD WINAPI JoystickPollThread( LPVOID context ) { ULONG numread; while(bEnabled) { Sleep(JOYSTICK_POLL_INTERVAL); if (ReadFile(hJoyDriver, &JoyData, sizeof(JOY_DD_INPUT_DATA), &numread, NULL)) { bDataValid = TRUE; } else { bDataValid = FALSE; } } return 0; }