#if INTELLISENSE_DIRECTIVES #include "platform.hpp" #include "engine/engine.hpp" #include "engine/input.hpp" #include "jsl.hpp" #include "win32.hpp" #endif NS_PLATFORM_BEGIN using namespace win32; // Max controllers for the platform layer and thus for all other layers is 4. (Sanity and xinput limit) constexpr u32 Max_Controllers = 4; using JSL_DeviceHandle = int; using EngineXInputPadStates = engine::XInputPadState[ Max_Controllers ]; using EngineDSPadStates = engine::DualsensePadState[Max_Controllers]; internal void input_process_digital_btn( engine::DigitalBtn* old_state, engine::DigitalBtn* new_state, u32 raw_btns, u32 btn_flag ) { #define had_transition() ( old_state->ended_down != new_state->ended_down ) new_state->ended_down = (raw_btns & btn_flag) > 0; if ( had_transition() ) new_state->half_transitions += 1; else new_state->half_transitions = 0; #undef had_transition } internal f32 jsl_input_process_axis_value( f32 value, f32 deadzone_threshold ) { f32 result = 0; if ( value < -deadzone_threshold ) { result = (value + deadzone_threshold ) / (1.0f - deadzone_threshold ); if (result < -1.0f) result = -1.0f; // Clamp to ensure it doesn't go below -1 } else if ( value > deadzone_threshold ) { result = (value - deadzone_threshold ) / (1.0f - deadzone_threshold ); if (result > 1.0f) result = 1.0f; // Clamp to ensure it doesn't exceed 1 } return result; } internal f32 xinput_process_axis_value( s16 value, s16 deadzone_threshold ) { f32 result = 0; if ( value < -deadzone_threshold ) { result = scast(f32, value + deadzone_threshold) / (32768.0f - scast(f32, deadzone_threshold)); } else if ( value > deadzone_threshold ) { result = scast(f32, value + deadzone_threshold) / (32767.0f - scast(f32, deadzone_threshold)); } return result; } internal void poll_input( HWND window_handle, engine::InputState* input, u32 jsl_num_devices, JSL_DeviceHandle* jsl_device_handles , engine::KeyboardState* old_keyboard, engine::KeyboardState* new_keyboard , engine::MousesState* old_mouse, engine::MousesState* new_mouse , EngineXInputPadStates* old_xpads, EngineXInputPadStates* new_xpads , EngineDSPadStates* old_ds_pads, EngineDSPadStates* new_ds_pads ) { // Keyboard Polling // Keyboards are unified for now. { constexpr u32 is_down = 0x80000000; input_process_digital_btn( & old_keyboard->_1, & new_keyboard->_1, GetAsyncKeyState( '1' ), is_down ); input_process_digital_btn( & old_keyboard->_2, & new_keyboard->_2, GetAsyncKeyState( '2' ), is_down ); input_process_digital_btn( & old_keyboard->_3, & new_keyboard->_3, GetAsyncKeyState( '3' ), is_down ); input_process_digital_btn( & old_keyboard->_4, & new_keyboard->_4, GetAsyncKeyState( '4' ), is_down ); input_process_digital_btn( & old_keyboard->Q, & new_keyboard->Q, GetAsyncKeyState( 'Q' ), is_down ); input_process_digital_btn( & old_keyboard->E, & new_keyboard->E, GetAsyncKeyState( 'E' ), is_down ); input_process_digital_btn( & old_keyboard->W, & new_keyboard->W, GetAsyncKeyState( 'W' ), is_down ); input_process_digital_btn( & old_keyboard->A, & new_keyboard->A, GetAsyncKeyState( 'A' ), is_down ); input_process_digital_btn( & old_keyboard->S, & new_keyboard->S, GetAsyncKeyState( 'S' ), is_down ); input_process_digital_btn( & old_keyboard->D, & new_keyboard->D, GetAsyncKeyState( 'D' ), is_down ); input_process_digital_btn( & old_keyboard->K, & new_keyboard->K, GetAsyncKeyState( 'K' ), is_down ); input_process_digital_btn( & old_keyboard->L, & new_keyboard->L, GetAsyncKeyState( 'L' ), is_down ); input_process_digital_btn( & old_keyboard->escape, & new_keyboard->escape, GetAsyncKeyState( VK_ESCAPE ), is_down ); input_process_digital_btn( & old_keyboard->backspace, & new_keyboard->backspace, GetAsyncKeyState( VK_BACK ), is_down ); input_process_digital_btn( & old_keyboard->up, & new_keyboard->up, GetAsyncKeyState( VK_UP ), is_down ); input_process_digital_btn( & old_keyboard->down, & new_keyboard->down, GetAsyncKeyState( VK_DOWN ), is_down ); input_process_digital_btn( & old_keyboard->left, & new_keyboard->left, GetAsyncKeyState( VK_LEFT ), is_down ); input_process_digital_btn( & old_keyboard->right, & new_keyboard->right, GetAsyncKeyState( VK_RIGHT ), is_down ); input_process_digital_btn( & old_keyboard->space, & new_keyboard->space, GetAsyncKeyState( VK_SPACE ), is_down ); input_process_digital_btn( & old_keyboard->pause, & new_keyboard->pause, GetAsyncKeyState( VK_PAUSE ), is_down ); input_process_digital_btn( & old_keyboard->left_alt, & new_keyboard->left_alt, GetAsyncKeyState( VK_LMENU ), is_down ); input_process_digital_btn( & old_keyboard->right_alt, & new_keyboard->right_alt, GetAsyncKeyState( VK_RMENU ), is_down ); input_process_digital_btn( & old_keyboard->left_shift, & new_keyboard->left_shift, GetAsyncKeyState( VK_LSHIFT ), is_down ); input_process_digital_btn( & old_keyboard->right_shift, & new_keyboard->right_shift, GetAsyncKeyState( VK_RSHIFT ), is_down ); input->controllers[0].keyboard = new_keyboard; } // Mouse polling { // input->Controllers[0].Mouse = {}; constexpr u32 is_down = 0x80000000; input_process_digital_btn( & old_mouse->left, & new_mouse->left, GetAsyncKeyState( VK_LBUTTON ), is_down ); input_process_digital_btn( & old_mouse->middle, & new_mouse->middle, GetAsyncKeyState( VK_MBUTTON ), is_down ); input_process_digital_btn( & old_mouse->right, & new_mouse->right, GetAsyncKeyState( VK_RBUTTON ), is_down ); POINT mouse_pos; GetCursorPos( & mouse_pos ); ScreenToClient( window_handle, & mouse_pos ); new_mouse->vertical_wheel = {}; new_mouse->horizontal_wheel = {}; new_mouse->X.end = (f32)mouse_pos.x; new_mouse->Y.end = (f32)mouse_pos.y; input->controllers[0].mouse = new_mouse; } // XInput Polling // TODO(Ed) : Should we poll this more frequently? for ( DWORD controller_index = 0; controller_index < Max_Controllers; ++ controller_index ) { XINPUT_STATE controller_state; b32 xinput_detected = xinput_get_state( controller_index, & controller_state ) == XI_PluggedIn; if ( xinput_detected ) { XINPUT_GAMEPAD* xpad = & controller_state.Gamepad; engine::XInputPadState* old_xpad = old_xpads[ controller_index ]; engine::XInputPadState* new_xpad = new_xpads[ controller_index ]; input_process_digital_btn( & old_xpad->dpad.up, & new_xpad->dpad.up, xpad->wButtons, XINPUT_GAMEPAD_DPAD_UP ); input_process_digital_btn( & old_xpad->dpad.down, & new_xpad->dpad.down, xpad->wButtons, XINPUT_GAMEPAD_DPAD_DOWN ); input_process_digital_btn( & old_xpad->dpad.left, & new_xpad->dpad.left, xpad->wButtons, XINPUT_GAMEPAD_DPAD_LEFT ); input_process_digital_btn( & old_xpad->dpad.right, & new_xpad->dpad.right, xpad->wButtons, XINPUT_GAMEPAD_DPAD_RIGHT ); input_process_digital_btn( & old_xpad->Y, & new_xpad->Y, xpad->wButtons, XINPUT_GAMEPAD_Y ); input_process_digital_btn( & old_xpad->A, & new_xpad->A, xpad->wButtons, XINPUT_GAMEPAD_A ); input_process_digital_btn( & old_xpad->B, & new_xpad->B, xpad->wButtons, XINPUT_GAMEPAD_B ); input_process_digital_btn( & old_xpad->X, & new_xpad->X, xpad->wButtons, XINPUT_GAMEPAD_X ); input_process_digital_btn( & old_xpad->back, & new_xpad->back, xpad->wButtons, XINPUT_GAMEPAD_BACK ); input_process_digital_btn( & old_xpad->start, & new_xpad->start, xpad->wButtons, XINPUT_GAMEPAD_START ); input_process_digital_btn( & old_xpad->left_shoulder, & new_xpad->left_shoulder, xpad->wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER ); input_process_digital_btn( & old_xpad->right_shoulder, & new_xpad->right_shoulder, xpad->wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER ); new_xpad->stick.left.X.start = old_xpad->stick.left.X.end; new_xpad->stick.left.Y.start = old_xpad->stick.left.Y.end; f32 left_x = xinput_process_axis_value( xpad->sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE ); f32 left_y = xinput_process_axis_value( xpad->sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE ); // TODO(Ed) : Min/Max macros!!! new_xpad->stick.left.X.min = new_xpad->stick.left.X.max = new_xpad->stick.left.X.end = left_x; new_xpad->stick.left.Y.min = new_xpad->stick.left.Y.max = new_xpad->stick.left.Y.end = left_y; // TODO(Ed): Make this actually an average for later new_xpad->stick.left.X.average = left_x; new_xpad->stick.left.Y.average = left_y; input->controllers[ controller_index ].xpad = new_xpad; } else { input->controllers[ controller_index ].xpad = nullptr; } } // JSL Input Polling for ( u32 jsl_device_index = 0; jsl_device_index < jsl_num_devices; ++ jsl_device_index ) { if ( ! JslStillConnected( jsl_device_handles[ jsl_device_index ] ) ) { OutputDebugStringA( "Error: JSLStillConnected returned false\n" ); continue; } // TODO : Won't support more than 4 for now... (or prob ever) if ( jsl_device_index > 4 ) break; JOY_SHOCK_STATE state = JslGetSimpleState( jsl_device_handles[ jsl_device_index ] ); // For now we're assuming anything that is detected via JSL is a dualsense pad. // We'll eventually add support possibly for the nintendo pro controller. engine::DualsensePadState* old_ds_pad = old_ds_pads[ jsl_device_index ]; engine::DualsensePadState* new_ds_pad = new_ds_pads[ jsl_device_index ]; input_process_digital_btn( & old_ds_pad->dpad.up, & new_ds_pad->dpad.up, state.buttons, JSMASK_UP ); input_process_digital_btn( & old_ds_pad->dpad.down, & new_ds_pad->dpad.down, state.buttons, JSMASK_DOWN ); input_process_digital_btn( & old_ds_pad->dpad.left, & new_ds_pad->dpad.left, state.buttons, JSMASK_LEFT ); input_process_digital_btn( & old_ds_pad->dpad.right, & new_ds_pad->dpad.right, state.buttons, JSMASK_RIGHT ); input_process_digital_btn( & old_ds_pad->triangle, & new_ds_pad->triangle, state.buttons, JSMASK_N ); input_process_digital_btn( & old_ds_pad->cross, & new_ds_pad->cross, state.buttons, JSMASK_S ); input_process_digital_btn( & old_ds_pad->square, & new_ds_pad->square, state.buttons, JSMASK_W ); input_process_digital_btn( & old_ds_pad->circle, & new_ds_pad->circle, state.buttons, JSMASK_E ); input_process_digital_btn( & old_ds_pad->share, & new_ds_pad->share, state.buttons, JSMASK_SHARE ); input_process_digital_btn( & old_ds_pad->options, & new_ds_pad->options, state.buttons, JSMASK_OPTIONS ); input_process_digital_btn( & old_ds_pad->L1, & new_ds_pad->L1, state.buttons, JSMASK_L ); input_process_digital_btn( & old_ds_pad->R1, & new_ds_pad->R1, state.buttons, JSMASK_R ); new_ds_pad->stick.left.X.start = old_ds_pad->stick.left.X.end; new_ds_pad->stick.left.Y.start = old_ds_pad->stick.left.Y.end; // Joyshock abstracts the sticks to a float value already for us of -1.f to 1.f. // We'll assume a deadzone of 10% for now. f32 left_x = jsl_input_process_axis_value( state.stickLX, 0.1f ); f32 left_y = jsl_input_process_axis_value( state.stickLY, 0.1f ); new_ds_pad->stick.left.X.min = new_ds_pad->stick.left.X.max = new_ds_pad->stick.left.X.end = left_x; new_ds_pad->stick.left.Y.min = new_ds_pad->stick.left.Y.max = new_ds_pad->stick.left.Y.end = left_y; // TODO(Ed): Make this actually an average for later new_ds_pad->stick.left.X.average = left_x; new_ds_pad->stick.left.Y.average = left_y; input->controllers[ jsl_device_index ].ds_pad = new_ds_pad; } } NS_PLATFORM_END