/* ** ClanLib SDK ** Copyright (c) 1997-2005 The ClanLib Team ** ** This software is provided 'as-is', without any express or implied ** warranty. In no event will the authors be held liable for any damages ** arising from the use of this software. ** ** Permission is granted to anyone to use this software for any purpose, ** including commercial applications, and to alter it and redistribute it ** freely, subject to the following restrictions: ** ** 1. The origin of this software must not be misrepresented; you must not ** claim that you wrote the original software. If you use this software ** in a product, an acknowledgment in the product documentation would be ** appreciated but is not required. ** 2. Altered source versions must be plainly marked as such, and must not be ** misrepresented as being the original software. ** 3. This notice may not be removed or altered from any source distribution. ** ** Note: Some of the libraries ClanLib may link to may have additional ** requirements or restrictions. ** ** File Author(s): ** ** Magnus Norddahl ** (if your name is missing here, please add it) */ #include "Display/display_precomp.h" #include "API/Display/input_event.h" #include "input_device_directinput.h" #include "API/Core/System/clanstring.h" #include "API/Core/System/error.h" #include "API/Core/System/log.h" ///////////////////////////////////////////////////////////////////////////// // CL_InputDevice_DirectInput construction: CL_InputDevice_DirectInput::CL_InputDevice_DirectInput( CL_DisplayWindow_Win32 *window, LPCDIDEVICEINSTANCE ptr_device_instance) : window(window), device_instance(*ptr_device_instance), directinput_device(0) { HRESULT result = window->get_directinput()->CreateDevice( device_instance.guidInstance, &directinput_device, 0); if (FAILED(result)) { throw CL_Error( CL_String::format( "Unable to open device %1 (%2)", device_instance.tszInstanceName, device_instance.tszProductName)); } result = directinput_device->SetCooperativeLevel(window->get_hwnd(), DISCL_FOREGROUND|DISCL_NONEXCLUSIVE); if (FAILED(result)) { directinput_device->Release(); throw CL_Error( CL_String::format( "Unable to the set cooperative level for %1 (%2)", device_instance.tszInstanceName, device_instance.tszProductName)); } result = directinput_device->SetDataFormat(&c_dfDIJoystick2); if (FAILED(result)) { directinput_device->Release(); throw CL_Error( CL_String::format( "Unable to set device dataformat for %1 (%2)", device_instance.tszInstanceName, device_instance.tszProductName)); } // Enable buffering of input events: DIPROPDWORD value; memset(&value, 0, sizeof(DIPROPDWORD)); value.diph.dwSize = sizeof(DIPROPDWORD); value.diph.dwHeaderSize = sizeof(DIPROPHEADER); value.diph.dwObj = 0; value.diph.dwHow = DIPH_DEVICE; value.dwData = 128; result = directinput_device->SetProperty(DIPROP_BUFFERSIZE, &value.diph); if (FAILED(result)) { directinput_device->Release(); throw CL_Error( CL_String::format( "Unable to set buffer size attribute on device %1 (%2)", device_instance.tszInstanceName, device_instance.tszProductName)); } result = directinput_device->Acquire(); if (FAILED(result)) { directinput_device->Release(); throw CL_Error( CL_String::format( "Unable to acquire device %1 (%2)", device_instance.tszInstanceName, device_instance.tszProductName)); } } CL_InputDevice_DirectInput::~CL_InputDevice_DirectInput() { if (directinput_device) directinput_device->Release(); } ///////////////////////////////////////////////////////////////////////////// // CL_InputDevice_DirectInput attributes: int CL_InputDevice_DirectInput::get_x() const { return 0; } int CL_InputDevice_DirectInput::get_y() const { return 0; } bool CL_InputDevice_DirectInput::get_keycode(int keycode) const { if (keycode < 0 || keycode >= 128) return false; DIJOYSTATE2 joystate2; HRESULT result = directinput_device->GetDeviceState(sizeof(DIJOYSTATE2), &joystate2); if (FAILED(result)) return false; return LOBYTE(joystate2.rgbButtons[keycode]) != 0; } std::string CL_InputDevice_DirectInput::get_key_name(int id) const { return CL_String::format("Joystick button %1", id); } float CL_InputDevice_DirectInput::get_axis(int index) const { DIJOYSTATE2 joystate2; HRESULT result = directinput_device->GetDeviceState(sizeof(DIJOYSTATE2), &joystate2); if (FAILED(result)) return 0; switch (index) { case 0: return float(joystate2.lX)/(0xffff/2)-1.0f; case 1: return float(joystate2.lY)/(0xffff/2)-1.0f; case 2: return float(joystate2.lZ)/(0xffff/2)-1.0f; case 3: return float(joystate2.lRx)/(0xffff/2)-1.0f; case 4: return float(joystate2.lRy)/(0xffff/2)-1.0f; case 5: return float(joystate2.lRz)/(0xffff/2)-1.0f; case 6: return float(joystate2.rglSlider[0])/(0xffff/2)-1.0f; case 7: return float(joystate2.rglSlider[1])/(0xffff/2)-1.0f; case 8: return float(joystate2.lVX)/(0xffff/2)-1.0f; case 9: return float(joystate2.lVY)/(0xffff/2)-1.0f; case 10: return float(joystate2.lVZ)/(0xffff/2)-1.0f; case 11: return float(joystate2.lVRx)/(0xffff/2)-1.0f; case 12: return float(joystate2.lVRy)/(0xffff/2)-1.0f; case 13: return float(joystate2.lVRz)/(0xffff/2)-1.0f; case 14: return float(joystate2.rglVSlider[0])/(0xffff/2)-1.0f; case 15: return float(joystate2.rglVSlider[1])/(0xffff/2)-1.0f; case 16: return float(joystate2.lAX)/(0xffff/2)-1.0f; case 17: return float(joystate2.lAY)/(0xffff/2)-1.0f; case 18: return float(joystate2.lAZ)/(0xffff/2)-1.0f; case 19: return float(joystate2.lARx)/(0xffff/2)-1.0f; case 20: return float(joystate2.lARy)/(0xffff/2)-1.0f; case 21: return float(joystate2.lARz)/(0xffff/2)-1.0f; case 22: return float(joystate2.rglASlider[0])/(0xffff/2)-1.0f; case 23: return float(joystate2.rglASlider[1])/(0xffff/2)-1.0f; case 24: return float(joystate2.lFX)/(0xffff/2)-1.0f; case 25: return float(joystate2.lFY)/(0xffff/2)-1.0f; case 26: return float(joystate2.lFZ)/(0xffff/2)-1.0f; case 27: return float(joystate2.lFRx)/(0xffff/2)-1.0f; case 28: return float(joystate2.lFRy)/(0xffff/2)-1.0f; case 29: return float(joystate2.lFRz)/(0xffff/2)-1.0f; case 30: return float(joystate2.rglFSlider[0])/(0xffff/2)-1.0f; case 31: return float(joystate2.rglFSlider[1])/(0xffff/2)-1.0f; case 32: return float(joystate2.rgdwPOV[0]); case 33: return float(joystate2.rgdwPOV[1]); case 34: return float(joystate2.rgdwPOV[2]); case 35: return float(joystate2.rgdwPOV[3]); } return 0.0f; } std::string CL_InputDevice_DirectInput::get_name() const { return device_instance.tszInstanceName; } int CL_InputDevice_DirectInput::get_axis_count() const { return 36; } int CL_InputDevice_DirectInput::get_button_count() const { return -1; } ///////////////////////////////////////////////////////////////////////////// // CL_InputDevice_DirectInput operations: void CL_InputDevice_DirectInput::set_position(int x, int y) { } ///////////////////////////////////////////////////////////////////////////// // CL_InputDevice_DirectInput implementation: void CL_InputDevice_DirectInput::keep_alive() { directinput_device->Poll(); // Get events: while (true) { DIDEVICEOBJECTDATA buffer[16]; DWORD num_events = 16; HRESULT result = directinput_device->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), buffer, &num_events, 0); // Try to reacquire joystick if we lost it. if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) directinput_device->Acquire(); if (FAILED(result) && result != DI_BUFFEROVERFLOW) break; if (num_events == 0) break; // Ok here follows the decoding of events. // // Imagine that. Microsoft managed to make something even more ugly than the MAPI API and // the older Direct3D APIs. Didn't think that was possible. // // Microsoft monkey #1: "I got this cool idea. We simply just do a memcpy from the driver // to the directinput application and returns offsets into the struct that changed" // // Microsoft monkey #2: "Aw cool man. That's like.. even better than the MAPI API I did back // in 1994.. Brilliant" // // Monkey #1 and #2 laughs evilly for (unsigned int i=0; imouse_pos; event.repeat_count = 0; if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lX)) { // X Axis position event event.type = CL_InputEvent::axis_moved; event.id = 0; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lY)) { // Y Axis position event event.type = CL_InputEvent::axis_moved; event.id = 1; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lZ)) { // Z Axis position event event.type = CL_InputEvent::axis_moved; event.id = 2; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, rglSlider[0])) { // extra 1 axis position event event.type = CL_InputEvent::axis_moved; event.id = 3; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, rglSlider[1])) { // extra 2 axis position event event.type = CL_InputEvent::axis_moved; event.id = 4; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs >= FIELD_OFFSET(DIJOYSTATE2, rgbButtons) && buffer[i].dwOfs < FIELD_OFFSET(DIJOYSTATE2, rgbButtons)+128) { // Button event int button_index = buffer[i].dwOfs - FIELD_OFFSET(DIJOYSTATE2, rgbButtons); event.id = button_index; if (LOBYTE(buffer[i].dwData)) // If high bit of lower byte is set, key is down { event.type = CL_InputEvent::pressed; sig_key_down.call(event); } else { event.type = CL_InputEvent::released; sig_key_up.call(event); } } else if (buffer[i].dwOfs >= FIELD_OFFSET(DIJOYSTATE2, rgdwPOV) && buffer[i].dwOfs < FIELD_OFFSET(DIJOYSTATE2, rgdwPOV)+4*sizeof(DWORD)) { // Hat event: int hat_index = (buffer[i].dwOfs - FIELD_OFFSET(DIJOYSTATE2, rgdwPOV)) / sizeof(DWORD); bool centered = (LOWORD(buffer[i].dwData) == 0xFFFF); int direction = buffer[i].dwData / DI_DEGREES; if (centered) direction = -1; event.type = CL_InputEvent::axis_moved; event.id = 32 + hat_index; event.axis_pos = float(direction); sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lRx)) { // X Axis rotation event event.type = CL_InputEvent::axis_moved; event.id = 5; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lRy)) { // Y Axis rotation event event.type = CL_InputEvent::axis_moved; event.id = 6; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lRz)) { // Z Axis rotation event event.type = CL_InputEvent::axis_moved; event.id = 7; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lVX)) { // X Axis velocity event event.type = CL_InputEvent::axis_moved; event.id = 8; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lVY)) { // Y Axis velocity event event.type = CL_InputEvent::axis_moved; event.id = 9; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lVZ)) { // Z Axis velocity event event.type = CL_InputEvent::axis_moved; event.id = 10; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lVRx)) { // X Axis angular velocity event event.type = CL_InputEvent::axis_moved; event.id = 11; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lVRy)) { // Y Axis angular velocity event event.type = CL_InputEvent::axis_moved; event.id = 12; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lVRz)) { // Z Axis angular velocity event event.type = CL_InputEvent::axis_moved; event.id = 13; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, rglVSlider[0])) { // extra 1 axis velocity event event.type = CL_InputEvent::axis_moved; event.id = 14; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, rglVSlider[1])) { // extra 2 axis velocity event event.type = CL_InputEvent::axis_moved; event.id = 15; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lAX)) { // X Axis acceleration event event.type = CL_InputEvent::axis_moved; event.id = 16; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lAY)) { // Y Axis acceleration event event.type = CL_InputEvent::axis_moved; event.id = 17; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lAZ)) { // Z Axis acceleration event event.type = CL_InputEvent::axis_moved; event.id = 18; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lARx)) { // X Axis angular acceleration event event.type = CL_InputEvent::axis_moved; event.id = 19; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lARy)) { // Y Axis angular acceleration event event.type = CL_InputEvent::axis_moved; event.id = 20; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lARz)) { // Z Axis angular acceleration event event.type = CL_InputEvent::axis_moved; event.id = 21; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, rglASlider[0])) { // extra 1 axis acceleration event event.type = CL_InputEvent::axis_moved; event.id = 22; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, rglASlider[1])) { // extra 2 axis acceleration event event.type = CL_InputEvent::axis_moved; event.id = 23; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lFX)) { // X Axis force event event.type = CL_InputEvent::axis_moved; event.id = 24; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lFY)) { // Y Axis force event event.type = CL_InputEvent::axis_moved; event.id = 25; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lFZ)) { // Z Axis force event event.type = CL_InputEvent::axis_moved; event.id = 26; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lFRx)) { // X Axis torque event event.type = CL_InputEvent::axis_moved; event.id = 27; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lFRy)) { // Y Axis torque event event.type = CL_InputEvent::axis_moved; event.id = 28; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, lFRz)) { // Z Axis torque event event.type = CL_InputEvent::axis_moved; event.id = 29; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, rglFSlider[0])) { // extra 1 axis force event event.type = CL_InputEvent::axis_moved; event.id = 30; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } else if (buffer[i].dwOfs == FIELD_OFFSET(DIJOYSTATE2, rglFSlider[1])) { // extra 2 axis force event event.type = CL_InputEvent::axis_moved; event.id = 31; event.axis_pos = float(buffer[i].dwData)/(0xffff/2)-1.0f; sig_axis_move.call(event); } } } }