-
Notifications
You must be signed in to change notification settings - Fork 98
Split joystick logic to its own file #4524
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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() |
Copilot
AI
Feb 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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.