Split joystick logic to its own file#4524
Conversation
There was a problem hiding this comment.
Pull request overview
This pull request refactors the joystick/controller input handling code by extracting it from bflib_inputctrl.cpp into a new dedicated file bflib_input_joyst.cpp. The separation improves code organization and modularity.
Changes:
- Extracted all joystick and game controller related functions, variables, and logic into a new file
bflib_input_joyst.cpp - Cleaned up includes in
bflib_inputctrl.cpp(removed unusedsounds.h) - Added forward declarations for the joystick functions in
bflib_inputctrl.cpp - Updated build files to include the new source file
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/bflib_input_joyst.cpp | New file containing all joystick/controller input logic, including initialization, event handling, and polling functions |
| src/bflib_inputctrl.cpp | Removed joystick-specific code, added forward declarations for joystick functions, cleaned up includes |
| linux.mk | Added bflib_input_joyst.cpp to the source files list |
| Makefile | Added bflib_input_joyst.o to the object files list |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| void JEvent(const SDL_Event *ev) | ||
| { | ||
| SYNCDBG(10, "Starting"); | ||
|
|
||
| switch (ev->type) | ||
| { | ||
| case SDL_JOYAXISMOTION: | ||
| case SDL_JOYBALLMOTION: | ||
| case SDL_JOYHATMOTION: | ||
| break; | ||
| case SDL_JOYBUTTONDOWN: | ||
| case SDL_JOYBUTTONUP: | ||
| { | ||
| TbKeyCode keycode = joystickbutton_to_keycode(ev->jbutton.button); | ||
| if (keycode != KC_UNASSIGNED) | ||
| { | ||
| if (ev->type == SDL_JOYBUTTONDOWN) | ||
| { | ||
| lbKeyOn[keycode] = 1; | ||
| lbInkey = keycode; | ||
| } | ||
| else | ||
| { | ||
| lbKeyOn[keycode] = 0; | ||
| } | ||
| } | ||
| } | ||
| break; | ||
| case SDL_CONTROLLERAXISMOTION: | ||
| break; | ||
| case SDL_CONTROLLERBUTTONDOWN: | ||
| case SDL_CONTROLLERBUTTONUP: | ||
| { | ||
| // Some controllers with proper mappings send these events instead of joystick events | ||
| Uint8 button_val = ev->cbutton.button; | ||
| TbKeyCode keycode = gamecontrollerbutton_to_keycode(button_val); | ||
| if (keycode != KC_UNASSIGNED) | ||
| { | ||
| if (ev->type == SDL_CONTROLLERBUTTONDOWN) | ||
| { | ||
| lbKeyOn[keycode] = 1; | ||
| lbInkey = keycode; | ||
| } | ||
| else | ||
| { | ||
| lbKeyOn[keycode] = 0; | ||
| } | ||
| } | ||
| } | ||
| break; | ||
|
|
||
| case SDL_JOYDEVICEADDED: | ||
| open_controller(ev->jdevice.which); | ||
| break; | ||
| case SDL_JOYDEVICEREMOVED: | ||
| close_controller(ev->jdevice.which); | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| void controller_rumble(long ms) | ||
| { | ||
| if (controller != NULL) { | ||
| SDL_GameControllerRumble(controller, 0xFFFF, 0xFFFF, ms); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| #define STICK_DEADZONE 0.15f | ||
|
|
||
| static void poll_controller_movement(Sint16 lx, Sint16 ly) | ||
| { | ||
| float nx = lx / 32768.0f; | ||
| float ny = ly / 32768.0f; | ||
|
|
||
| // Handle horizontal movement - just accumulate for local camera | ||
| float move_mag_x = fabsf(nx); | ||
| if (move_mag_x > STICK_DEADZONE) { | ||
| float norm_mag = (move_mag_x - STICK_DEADZONE) / (1.0f - STICK_DEADZONE); | ||
| float curved = norm_mag * norm_mag; | ||
| float presses_this_frame = curved * input_delta_time; | ||
|
|
||
| movement_accum_x += (nx > 0 ? presses_this_frame : -presses_this_frame); | ||
|
|
||
| struct PlayerInfo* player = get_my_player(); | ||
| if(player->work_state == PSt_FreeCtrlDirect || player->work_state == PSt_CtrlDirect) { | ||
| struct Packet* packet = get_packet(my_player_number); | ||
| while (movement_accum_x >= 1.0f) { | ||
| set_packet_control(packet, PCtr_MoveRight); | ||
| movement_accum_x -= 1.0f; | ||
| } | ||
| while (movement_accum_x <= -1.0f) { | ||
| set_packet_control(packet, PCtr_MoveLeft); | ||
| movement_accum_x += 1.0f; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Handle vertical movement - just accumulate for local camera | ||
| float move_mag_y = fabsf(ny); | ||
| if (move_mag_y > STICK_DEADZONE) { | ||
| float norm_mag = (move_mag_y - STICK_DEADZONE) / (1.0f - STICK_DEADZONE); | ||
| float curved = norm_mag * norm_mag; | ||
| float presses_this_frame = curved * input_delta_time; | ||
|
|
||
| movement_accum_y += (ny > 0 ? presses_this_frame : -presses_this_frame); | ||
|
|
||
| struct PlayerInfo* player = get_my_player(); | ||
| if(player->work_state == PSt_FreeCtrlDirect || player->work_state == PSt_CtrlDirect) { | ||
| struct Packet* packet = get_packet(my_player_number); | ||
| while (movement_accum_y >= 1.0f) { | ||
| set_packet_control(packet, PCtr_MoveDown); | ||
| movement_accum_y -= 1.0f; | ||
| } | ||
| while (movement_accum_y <= -1.0f) { | ||
| set_packet_control(packet, PCtr_MoveUp); | ||
| movement_accum_y += 1.0f; | ||
| } | ||
| } | ||
| } | ||
| // Packets will be sent by send_camera_catchup_packets() based on position difference | ||
| } | ||
|
|
||
| #define SECONDS_TO_CROSS 20.0f | ||
| static void poll_controller_mouse(Sint16 rx, Sint16 ry) | ||
| { | ||
| float nx = rx / 32768.0f; | ||
| float ny = ry / 32768.0f; | ||
| float mag = sqrtf(nx * nx + ny * ny); | ||
|
|
||
| if (mag < STICK_DEADZONE) | ||
| return; | ||
|
|
||
| nx /= mag; | ||
| ny /= mag; | ||
|
|
||
| float norm_mag = (mag - STICK_DEADZONE) / (1.0f - STICK_DEADZONE); | ||
| float curved = norm_mag * norm_mag; | ||
| float pixels_per_second = lbDisplay.GraphicsWindowWidth / SECONDS_TO_CROSS; | ||
| float pixels_this_frame = pixels_per_second * input_delta_time; | ||
|
|
||
| mouse_accum_x += nx * curved * pixels_this_frame; | ||
| mouse_accum_y += ny * curved * pixels_this_frame; | ||
|
|
||
| int dx = (int)mouse_accum_x; | ||
| int dy = (int)mouse_accum_y; | ||
|
|
||
| mouse_accum_x -= dx; | ||
| mouse_accum_y -= dy; | ||
|
|
||
| if (dx != 0 || dy != 0) { | ||
| struct TbPoint mouseDelta = { dx, dy }; | ||
| mouseControl(MActn_MOUSEMOVE, &mouseDelta); | ||
| } | ||
| } | ||
|
|
||
| static float get_input_delta_time() | ||
| { | ||
| long double frame_time_in_nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(TimeNow - delta_time_previous_timepoint).count(); | ||
| delta_time_previous_timepoint = TimeNow; | ||
| float calculated_delta_time = (frame_time_in_nanoseconds/1000000000.0) * game_num_fps; | ||
| return min(calculated_delta_time, 1.0f); | ||
| } | ||
|
|
||
| void poll_controller() | ||
| { | ||
| input_delta_time = get_input_delta_time(); | ||
| if (controller != NULL) { | ||
|
|
||
| TbBool has_right_stick = | ||
| SDL_GameControllerHasAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX) && | ||
| SDL_GameControllerHasAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY); | ||
| TbBool has_left_stick = | ||
| SDL_GameControllerHasAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) && | ||
| SDL_GameControllerHasAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); | ||
|
|
||
| //analog sticks and dpad, layout based on what's available, with mouse being most important, then movement, then cam rotation/zoom | ||
|
|
||
| if (has_right_stick && has_left_stick) { | ||
| lbKeyOn[KC_HOME] = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_UP); | ||
| lbKeyOn[KC_END] = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_DOWN); | ||
| lbKeyOn[KC_PGDOWN] = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_LEFT); | ||
| lbKeyOn[KC_DELETE] = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); | ||
|
|
||
|
|
||
| Sint16 leftX = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); | ||
| Sint16 leftY = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); | ||
| poll_controller_movement(leftX, leftY); | ||
|
|
||
| Sint16 rx = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); | ||
| Sint16 ry = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY); | ||
| poll_controller_mouse(rx, ry); | ||
|
|
||
| } else if (has_left_stick) { | ||
|
|
||
| lbKeyOn[KC_UP] = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_UP); | ||
| lbKeyOn[KC_DOWN] = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_DOWN); | ||
| lbKeyOn[KC_LEFT] = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_LEFT); | ||
| lbKeyOn[KC_RIGHT] = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); | ||
|
|
||
| Sint16 rx = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); | ||
| Sint16 ry = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); | ||
| poll_controller_mouse(rx, ry); | ||
| } else { | ||
| TbBool up = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_UP); | ||
| TbBool down = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_DOWN); | ||
| TbBool left = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_LEFT); | ||
| TbBool right = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); | ||
|
|
||
| struct TbPoint mouseDelta; | ||
| mouseDelta.x = (right - left) * lbDisplay.GraphicsWindowHeight * input_delta_time / 0.02f; | ||
| mouseDelta.y = (down - up) * lbDisplay.GraphicsWindowHeight * input_delta_time / 0.02f; | ||
| mouseControl(MActn_MOUSEMOVE, &mouseDelta); | ||
| } | ||
|
|
||
| TbBool has_triggers = | ||
| SDL_GameControllerHasAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT) && | ||
| SDL_GameControllerHasAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT); | ||
|
|
||
| if(has_triggers) | ||
| { | ||
| Uint8 current_leftshoulder = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); | ||
| if (current_leftshoulder && !prev_leftshoulder) { | ||
| go_to_adjacent_menu_tab(-1); | ||
| } | ||
| prev_leftshoulder = current_leftshoulder; | ||
|
|
||
| Uint8 current_rightshoulder = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); | ||
| if (current_rightshoulder && !prev_rightshoulder) { | ||
| go_to_adjacent_menu_tab(1); | ||
| } | ||
| prev_rightshoulder = current_rightshoulder; | ||
|
|
||
|
|
||
| // Handle triggers for mouse buttons | ||
| Sint16 lt = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT); | ||
| Sint16 rt = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT); | ||
| struct TbPoint delta = {0, 0}; | ||
| if (lt > 10000 && !lt_pressed) { | ||
| lt_pressed = true; | ||
| mouseControl(MActn_RBUTTONDOWN, &delta); | ||
| } else if (lt <= 10000 && lt_pressed) { | ||
| lt_pressed = false; | ||
| mouseControl(MActn_RBUTTONUP, &delta); | ||
| } | ||
| if (rt > 10000 && !rt_pressed) { | ||
| rt_pressed = true; | ||
| mouseControl(MActn_LBUTTONDOWN, &delta); | ||
| } else if (rt <= 10000 && rt_pressed) { | ||
| rt_pressed = false; | ||
| mouseControl(MActn_LBUTTONUP, &delta); | ||
| } | ||
| } | ||
| else { | ||
| struct TbPoint delta = {0, 0}; | ||
|
|
||
| Uint8 current_leftshoulder = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); | ||
| if (current_leftshoulder && !prev_leftshoulder) { | ||
| mouseControl(MActn_RBUTTONDOWN, &delta); | ||
| } | ||
| else if (!current_leftshoulder && prev_leftshoulder) { | ||
| mouseControl(MActn_RBUTTONUP, &delta); | ||
| } | ||
| prev_leftshoulder = current_leftshoulder; | ||
|
|
||
| Uint8 current_rightshoulder = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); | ||
| if (current_rightshoulder && !prev_rightshoulder) { | ||
| mouseControl(MActn_LBUTTONDOWN, &delta); | ||
| } | ||
| else if (!current_rightshoulder && prev_rightshoulder) { | ||
| mouseControl(MActn_LBUTTONUP, &delta); | ||
| } | ||
| prev_rightshoulder = current_rightshoulder; | ||
| } | ||
|
|
||
| lbKeyOn[KC_LSHIFT] = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_A); | ||
| lbKeyOn[KC_LCONTROL] = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_B); | ||
| lbKeyOn[settings.kbkeys[Gkey_ZoomToFight].code] = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_X); | ||
| lbKeyOn[settings.kbkeys[Gkey_ZoomRoomHeart].code] = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_Y); | ||
|
|
||
| // Handle Start and Back buttons with edge detection to simulate key presses | ||
| lbKeyOn[KC_SPACE] = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_BACK); | ||
|
|
||
|
|
||
| Uint8 current_back = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_START); | ||
| if (current_back && !prev_back) { | ||
| keyboardControl(KActn_KEYDOWN, KC_ESCAPE, KMod_NONE, 0); | ||
| keyboardControl(KActn_KEYDOWN, KC_P, KMod_NONE, 0); | ||
| } else if (!current_back && prev_back) { | ||
| keyboardControl(KActn_KEYUP, KC_ESCAPE, KMod_NONE, 0); | ||
| keyboardControl(KActn_KEYUP, KC_P, KMod_NONE, 0); | ||
| } | ||
| prev_back = current_back; | ||
|
|
||
| Uint8 current_joy_left = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_LEFTSTICK); | ||
| if (current_joy_left && !prev_joy_left) { | ||
| keyboardControl(KActn_KEYDOWN, settings.kbkeys[Gkey_SwitchToMap].code, KMod_NONE, 0); | ||
| } else if (!current_joy_left && prev_joy_left) { | ||
| keyboardControl(KActn_KEYUP, settings.kbkeys[Gkey_SwitchToMap].code, KMod_NONE, 0); | ||
| } | ||
| prev_joy_left = current_joy_left; | ||
|
|
||
| //Uint8 current_joy_right = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_RIGHTSTICK); | ||
| //if (current_joy_right && !prev_joy_right) { | ||
| // keyboardControl(KActn_KEYDOWN, KC_P, KMod_NONE, 0); | ||
| //} else if (!current_joy_right && prev_joy_right) { | ||
| // keyboardControl(KActn_KEYUP, KC_P, KMod_NONE, 0); | ||
| //} | ||
| //prev_joy_right = current_joy_right; | ||
|
|
||
|
|
||
| } else if (joystick != NULL) { | ||
| // Map joystick buttons to keyboard keys (assuming standard layout) | ||
| lbKeyOn[KC_HOME] = SDL_JoystickGetButton(joystick, 10); // D-pad up | ||
| lbKeyOn[KC_END] = SDL_JoystickGetButton(joystick, 12); // D-pad down | ||
| lbKeyOn[KC_PGDOWN] = SDL_JoystickGetButton(joystick, 13); // D-pad left | ||
| lbKeyOn[KC_DELETE] = SDL_JoystickGetButton(joystick, 11); // D-pad right | ||
|
|
||
| //lbKeyOn[KC_SPACE] = SDL_JoystickGetButton(joystick, 0); // Button 0 (A) | ||
| //lbKeyOn[KC_LCONTROL] = SDL_JoystickGetButton(joystick, 1); // Button 1 (B) | ||
| lbKeyOn[KC_LSHIFT] = SDL_JoystickGetButton(joystick, 2); // Button 2 (X) | ||
| lbKeyOn[KC_LCONTROL] = SDL_JoystickGetButton(joystick, 3); // Button 3 (Y) | ||
| lbKeyOn[settings.kbkeys[Gkey_ZoomToFight].code] = SDL_JoystickGetButton(joystick, 4); | ||
| lbKeyOn[settings.kbkeys[Gkey_ZoomRoomHeart].code] = SDL_JoystickGetButton(joystick, 5); | ||
| lbKeyOn[settings.kbkeys[Gkey_SwitchToMap].code] = SDL_JoystickGetButton(joystick, 6); | ||
| lbKeyOn[settings.kbkeys[Gkey_ToggleMessage].code] = SDL_JoystickGetButton(joystick, 7); | ||
|
|
||
| // Handle analog sticks for movement (axes 0 and 1) | ||
| Sint16 leftX = SDL_JoystickGetAxis(joystick, 0); | ||
| Sint16 leftY = SDL_JoystickGetAxis(joystick, 1); | ||
| poll_controller_movement(leftX, leftY); | ||
|
|
||
| // Handle right stick for mouse movement (axes 2 and 3) | ||
| Sint16 rightX = SDL_JoystickGetAxis(joystick, 3); | ||
| Sint16 rightY = SDL_JoystickGetAxis(joystick, 4); | ||
| poll_controller_mouse(rightX, rightY); | ||
|
|
||
| // Handle triggers for mouse buttons (axes 4 and 5) | ||
| struct TbPoint delta = { 0, 0 }; | ||
| if (SDL_JoystickGetButton(joystick, 0)) | ||
| { | ||
| lt_pressed = true; | ||
| mouseControl(MActn_LBUTTONDOWN, &delta); | ||
| } | ||
| else | ||
| { | ||
| if (lt_pressed) | ||
| { | ||
| mouseControl(MActn_LBUTTONUP, &delta); | ||
| } | ||
| lt_pressed = false; | ||
| } | ||
| if (SDL_JoystickGetButton(joystick, 1)) | ||
| { | ||
| rt_pressed = true; | ||
| mouseControl(MActn_RBUTTONDOWN, &delta); | ||
| } | ||
| else | ||
| { | ||
| if (rt_pressed) | ||
| { | ||
| mouseControl(MActn_RBUTTONUP, &delta); | ||
| } | ||
| rt_pressed = false; | ||
| } | ||
| } | ||
|
|
||
| } | ||
|
|
||
|
|
||
| void init_controller_input() |
There was a problem hiding this comment.
The functions init_controller_input, JEvent, and poll_controller defined in this file are called from bflib_inputctrl.cpp but are not declared in a header file. These functions should be declared in bflib_inputctrl.h or a separate header file to provide proper interface declarations. Currently, they rely on forward declarations in the calling file, which is not a clean separation of concerns and violates proper modularity practices.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
No description provided.