Created
September 23, 2025 22:11
-
-
Save RandyGaul/d19ae7d049d9756809af0b2d724a8c65 to your computer and use it in GitHub Desktop.
Experimental highish level CF input binding layer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //-------------------------------------------------------------------------------------------------- | |
| // Basic input polling. | |
| // Original implementation by Noel Berry, taken from his Blah code on GitHub. | |
| #define joy_press cf_joypad_button_just_pressed | |
| #define joy_release cf_joypad_button_just_released | |
| #define joy_axis cf_joypad_axis | |
| #define joy_axis_prev cf_joypad_axis_prev | |
| #define joy_down cf_joypad_button_down | |
| #define joy_up !cf_joypad_button_down | |
| #define key_ctrl cf_key_ctrl | |
| #define key_alt cf_key_alt | |
| #define key_shift cf_key_shift | |
| #define key_press cf_key_just_pressed | |
| #define key_release cf_key_just_released | |
| #define key_down cf_key_down | |
| #define key_up cf_key_up | |
| #define mouse_press cf_mouse_just_pressed | |
| #define mouse_release cf_mouse_just_released | |
| #define mouse_down cf_mouse_down | |
| #define mouse_up(x) (!cf_mouse_down(x)) | |
| #define mouse_wheel cf_mouse_wheel_motion | |
| v2 mouse() { return cf_screen_to_world(V2(cf_mouse_x(), cf_mouse_y())); } | |
| #define rumble(lo, hi, duration_seconds) cf_joypad_rumble(0, (uint16_t)((lo) * 32767), (uint16_t)((hi) * 32767), (int)((duration_seconds) * 1000)) | |
| #define mouse_to_world() cf_screen_to_world(V2(cf_mouse_x(),cf_mouse_y())) | |
| //-------------------------------------------------------------------------------------------------- | |
| // Input binding. | |
| // | |
| // Key features | |
| // * Binding abstraction, able to bind multiple physical buttons to a | |
| // single abstract binding object. | |
| // * Input buffering, able to persist inputs over a small time buffer | |
| // to implement QoL features for players, making certain inputs more | |
| // forgiving. | |
| // | |
| // EXAPLE TODO | |
| // | |
| // TODO | |
| #define add_dpad stick_binding_add_dpad | |
| #define add_wasd stick_binding_add_wasd | |
| #define add_arrow_keys stick_binding_add_arrow_keys | |
| #define add_keys stick_binding_add_keys | |
| #define add_left_stick stick_binding_add_left_stick | |
| #define add_right_stick stick_binding_add_right_stick | |
| #define add_key button_binding_add_key | |
| #define add_mouse button_binding_add_mouse | |
| #define add_joy button_binding_add_joy | |
| #define add_trigger button_binding_add_trigger | |
| #define binding_down button_binding_down | |
| #define binding_up !button_binding_down | |
| #define binding_sign(x) _Generic((x), \ | |
| ButtonBinding*: button_binding_sign, \ | |
| AxisBinding*: axis_binding_sign, \ | |
| StickBinding*: stick_binding_sign \ | |
| )(x) | |
| #define binding_value(x) _Generic((x), \ | |
| ButtonBinding*: button_binding_value, \ | |
| AxisBinding*: axis_binding_value, \ | |
| StickBinding*: stick_binding_value \ | |
| )(x) | |
| #define binding_press(x) _Generic((x), \ | |
| ButtonBinding*: button_binding_press, \ | |
| AxisBinding*: axis_binding_press, \ | |
| StickBinding*: stick_binding_press \ | |
| )(x) | |
| #define binding_release(x) _Generic((x), \ | |
| ButtonBinding*: button_binding_release, \ | |
| AxisBinding*: axis_binding_release, \ | |
| StickBinding*: stick_binding_release \ | |
| )(x) | |
| #define consume_press(x) _Generic((x), \ | |
| ButtonBinding*: button_binding_consume_press, \ | |
| AxisBinding*: axis_binding_consume_press, \ | |
| StickBinding*: stick_binding_consume_press \ | |
| )(x) | |
| #define consume_release(x) _Generic((x), \ | |
| ButtonBinding*: button_binding_consume_release, \ | |
| AxisBinding*: axis_binding_consume_release, \ | |
| StickBinding*: stick_binding_consume_release \ | |
| )(x) | |
| #define destroy_binding(x) _Generic((x), \ | |
| ButtonBinding*: destroy_button_binding, \ | |
| AxisBinding*: destroy_axis_binding, \ | |
| StickBinding*: destroy_stick_binding \ | |
| )(x) | |
| //-------------------------------------------------------------------------------------------------- | |
| // Input binding implementation. | |
| // | |
| // Original implementation by Noel Berry (blah framework). | |
| // https://github.com/NoelFB/blah | |
| // | |
| // The deadzone zeros out stick inputs from controllers when they are too close to | |
| // zero. Inputs range from -1 to 1 from the device, but get stuck at near-zero, even | |
| // for a fresh and healthy controller. Controller manufacturers tend to recommend to zero | |
| // out any inputs from the device below roughly 0.15. | |
| // | |
| // Should be a number roughly from 0.1 to 0.2, whereas 0.1 is really on the conservative | |
| // end where you'll start seeing stick drift. | |
| // | |
| // This deadzone implementation is dead simple (haha) using a square clear-to-zero. Adjust | |
| // this value as-needed to get good results. | |
| #define DEADZONE 0.15f | |
| #define MAX_JOYPADS 8 | |
| typedef struct Joypad | |
| { | |
| int index; | |
| bool connected; | |
| CF_JoypadPowerLevel power_level; | |
| const char* name; | |
| CF_JoypadType type; | |
| uint16_t vendor; | |
| uint16_t product_id; | |
| const char* serial_number; | |
| uint16_t firmware_version; | |
| uint16_t product_version; | |
| } Joypad; | |
| typedef struct JoypadOnConnect | |
| { | |
| void* udata; | |
| void (*on_connect)(int, bool, void*); | |
| } JoypadOnConnect; | |
| dyna Joypad* g_joypads; | |
| dyna JoypadOnConnect* g_on_joypad_connects; | |
| Joypad get_joypad(int index) | |
| { | |
| Joypad joy = { | |
| .index = index, | |
| .connected = cf_joypad_is_connected(index), | |
| .power_level = cf_joypad_power_level(index), | |
| .name = cf_joypad_name(index), | |
| .type = cf_joypad_type(index), | |
| .vendor = cf_joypad_vendor(index), | |
| .product_id = cf_joypad_product_id(index), | |
| .serial_number = cf_joypad_serial_number(index), | |
| .firmware_version = cf_joypad_firmware_version(index), | |
| .product_version = cf_joypad_product_version(index), | |
| }; | |
| return joy; | |
| } | |
| // Returns a value from -1.0f to 1.0f. | |
| float int16_to_float(int16_t v) | |
| { | |
| // int16_t ranges from -32768 to 32767. | |
| const int16_t max_int16 = 32767; | |
| // Normalize the value to a float between -1 and 1. | |
| float normalized_v = (float)v / max_int16; | |
| // Simplest possible "square" clear-to-zero strategy. | |
| float abs_v = abs(normalized_v); | |
| if (abs_v < DEADZONE) { | |
| return 0; | |
| } | |
| // Remap outputs back to 0...1 after applying the deadzone. | |
| return sign(normalized_v) * remap_to_01(abs_v, DEADZONE, 1.0f); | |
| } | |
| typedef enum InputType | |
| { | |
| INPUT_TYPE_KEY, | |
| INPUT_TYPE_MOUSE, | |
| INPUT_TYPE_BUTTON, | |
| INPUT_TYPE_TRIGGER, | |
| } InputType; | |
| typedef struct InputBinding | |
| { | |
| InputType type; | |
| bool positive; | |
| float threshold; | |
| union { | |
| int key; | |
| int button; | |
| int axis; | |
| }; | |
| } InputBinding; | |
| bool axis_is_down_raw(InputBinding input, float v) | |
| { | |
| if ((v > 0 && input.positive) || (v < 0 && !input.positive)) { | |
| if (abs(v) >= input.threshold) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| bool axis_is_down(int index, InputBinding input) | |
| { | |
| float v = int16_to_float(joy_axis(index, input.axis)); | |
| return axis_is_down_raw(input, v); | |
| } | |
| bool axis_prev_is_down(int index, InputBinding input) | |
| { | |
| float v = int16_to_float(joy_axis_prev(index, input.axis)); | |
| return axis_is_down_raw(input, v); | |
| } | |
| bool input_is_down(int index, const dyna InputBinding* inputs) | |
| { | |
| for (int i = 0; i < asize(inputs); ++i) { | |
| InputBinding input = inputs[i]; | |
| if (input.type == INPUT_TYPE_KEY) { | |
| if (key_down(input.key)) return true; | |
| } else if (input.type == INPUT_TYPE_MOUSE) { | |
| if (mouse_down(input.button)) return true; | |
| } else if (input.type == INPUT_TYPE_BUTTON) { | |
| if (joy_down(index, input.button)) return true; | |
| } else if (input.type == INPUT_TYPE_TRIGGER) { | |
| if (axis_is_down(index, input)) return true; | |
| } else { | |
| assert(false); | |
| } | |
| } | |
| return false; | |
| } | |
| bool input_get_pressed(int index, const dyna InputBinding* inputs) | |
| { | |
| for (int i = 0; i < asize(inputs); ++i) { | |
| InputBinding input = inputs[i]; | |
| if (input.type == INPUT_TYPE_KEY) { | |
| if (key_press(input.key)) return true; | |
| } else if (input.type == INPUT_TYPE_MOUSE) { | |
| if (mouse_press(input.button)) return true; | |
| } else if (input.type == INPUT_TYPE_BUTTON) { | |
| if (joy_press(index, input.button)) return true; | |
| } else if (input.type == INPUT_TYPE_TRIGGER) { | |
| if (axis_is_down(index, input) && !axis_prev_is_down(index, input)) return true; | |
| } else { | |
| assert(false); | |
| } | |
| } | |
| return false; | |
| } | |
| bool input_get_released(int index, const dyna InputBinding* inputs) | |
| { | |
| for (int i = 0; i < asize(inputs); ++i) { | |
| InputBinding input = inputs[i]; | |
| if (input.type == INPUT_TYPE_KEY) { | |
| if (key_release(input.key)) return true; | |
| } else if (input.type == INPUT_TYPE_MOUSE) { | |
| if (mouse_release(input.button)) return true; | |
| } else if (input.type == INPUT_TYPE_BUTTON) { | |
| if (joy_release(index, input.button)) return true; | |
| } else if (input.type == INPUT_TYPE_TRIGGER) { | |
| if (!axis_is_down(index, input) && axis_prev_is_down(index, input)) return true; | |
| } else { | |
| assert(false); | |
| } | |
| } | |
| return false; | |
| } | |
| float input_get_value(int index, const dyna InputBinding* inputs) | |
| { | |
| float highest_value = 0; | |
| for (int i = 0; i < asize(inputs); ++i) { | |
| InputBinding input = inputs[i]; | |
| if (input.type == INPUT_TYPE_KEY) { | |
| if (key_down(input.key)) return 1.0f; | |
| } else if (input.type == INPUT_TYPE_MOUSE) { | |
| if (mouse_down(input.button)) return 1.0f; | |
| } else if (input.type == INPUT_TYPE_BUTTON) { | |
| if (joy_down(index, input.button)) return 1.0f; | |
| } else if (input.type == INPUT_TYPE_TRIGGER) { | |
| float v = int16_to_float(joy_axis(index, input.axis)); | |
| if (axis_is_down_raw(input, v)) { | |
| highest_value = fmaxf(highest_value, fabsf(v)); | |
| } | |
| } else { | |
| assert(false); | |
| } | |
| } | |
| return highest_value; | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| typedef struct ButtonBinding | |
| { | |
| int index; | |
| float press_buffer; | |
| dyna InputBinding* inputs; | |
| float last_timestep; | |
| float last_press_time; | |
| float last_release_time; | |
| bool is_down; | |
| float v; | |
| bool pressed; | |
| bool released; | |
| bool press_consumed; | |
| bool release_consumed; | |
| } ButtonBinding; | |
| dyna ButtonBinding* g_button_bindings; | |
| ButtonBinding* make_button_binding(int player_index, float press_buffer) | |
| { | |
| ButtonBinding binding = { | |
| .index = player_index, | |
| .press_buffer = press_buffer, | |
| .inputs = NULL, | |
| .last_timestep = 0, | |
| .last_press_time = -1, | |
| .last_release_time = -1, | |
| .is_down = false, | |
| .v = 0, | |
| .pressed = false, | |
| .released = false, | |
| .press_consumed = false, | |
| .release_consumed = false, | |
| }; | |
| apush(g_button_bindings, binding); | |
| return &alast(g_button_bindings); | |
| } | |
| void destroy_button_binding(ButtonBinding* binding) | |
| { | |
| int i = index_of(g_button_bindings, binding); | |
| if (i != -1) { | |
| adel(g_button_bindings, i); | |
| } | |
| } | |
| void button_binding_add_key(ButtonBinding* binding, int key) | |
| { | |
| InputBinding input = { | |
| .type = INPUT_TYPE_KEY, | |
| .key = key, | |
| }; | |
| apush(binding->inputs, input); | |
| } | |
| void button_binding_add_mouse(ButtonBinding* binding, int mouse_button) | |
| { | |
| InputBinding input = { | |
| .type = INPUT_TYPE_MOUSE, | |
| .button = mouse_button, | |
| }; | |
| apush(binding->inputs, input); | |
| } | |
| void button_binding_add_joy(ButtonBinding* binding, int button) | |
| { | |
| InputBinding input = { | |
| .type = INPUT_TYPE_BUTTON, | |
| .button = button, | |
| }; | |
| apush(binding->inputs, input); | |
| } | |
| void button_binding_add_trigger(ButtonBinding* binding, int axis, float threshold, bool positive) | |
| { | |
| InputBinding input = { | |
| .type = INPUT_TYPE_TRIGGER, | |
| .axis = axis, | |
| .threshold = threshold, | |
| .positive = positive, | |
| }; | |
| apush(binding->inputs, input); | |
| } | |
| bool button_binding_press(ButtonBinding* binding) | |
| { | |
| if (binding->press_consumed) return false; | |
| if (binding->last_press_time >= 0 && (SECONDS-binding->last_press_time) <= binding->press_buffer) { | |
| return true; | |
| } | |
| return binding->pressed; | |
| } | |
| bool button_binding_release(ButtonBinding* binding) | |
| { | |
| if (binding->release_consumed) return false; | |
| if (binding->last_release_time >= 0 && (SECONDS-binding->last_release_time) <= binding->press_buffer) { | |
| return true; | |
| } | |
| return binding->released; | |
| } | |
| void button_binding_consume_press(ButtonBinding* binding) | |
| { | |
| binding->press_consumed = true; | |
| binding->last_press_time = -1; | |
| } | |
| void button_binding_consume_release(ButtonBinding* binding) | |
| { | |
| binding->release_consumed = true; | |
| binding->last_release_time = -1; | |
| } | |
| bool button_binding_down(ButtonBinding* binding) | |
| { | |
| return binding->is_down; | |
| } | |
| float button_binding_value(ButtonBinding* binding) | |
| { | |
| return binding->v; | |
| } | |
| float button_binding_sign(ButtonBinding* binding) | |
| { | |
| if (binding->v > 0) return 1.0f; | |
| else if (binding->v == 0) return 0; | |
| else return -1.0f; | |
| } | |
| void button_binding_update(ButtonBinding* binding) | |
| { | |
| binding->press_consumed = false; | |
| binding->release_consumed = false; | |
| if (input_get_pressed(binding->index, binding->inputs)) { | |
| binding->last_timestep = SECONDS; | |
| binding->last_press_time = SECONDS; | |
| binding->pressed = true; | |
| } else { | |
| binding->pressed = false; | |
| } | |
| if (input_get_released(binding->index, binding->inputs)) { | |
| binding->last_release_time = SECONDS; | |
| binding->released = true; | |
| } else { | |
| binding->released = false; | |
| } | |
| binding->is_down = input_is_down(binding->index, binding->inputs); | |
| binding->v = input_get_value(binding->index, binding->inputs); | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| typedef struct AxisBinding | |
| { | |
| int index; | |
| int conflict; | |
| ButtonBinding* negative; | |
| ButtonBinding* positive; | |
| } AxisBinding; | |
| #define HANDLE_CONFLICT_NEWEST 0 | |
| #define HANDLE_CONFLICT_OLDEST 1 | |
| #define HANDLE_CONFLICT_CANCEL 2 | |
| AxisBinding* make_axis_binding(int player_index) | |
| { | |
| AxisBinding* axis_binding = CALLOC(AxisBinding); | |
| *axis_binding = (AxisBinding){ | |
| .index = player_index, | |
| .conflict = HANDLE_CONFLICT_NEWEST, | |
| .negative = make_button_binding(player_index, 0), | |
| .positive = make_button_binding(player_index, 0), | |
| }; | |
| return axis_binding; | |
| } | |
| void destroy_axis_binding(AxisBinding* binding) | |
| { | |
| destroy_button_binding(binding->negative); | |
| destroy_button_binding(binding->positive); | |
| FREE(binding); | |
| } | |
| void axis_binding_add_key_pair(AxisBinding* binding, int negative, int positive) | |
| { | |
| button_binding_add_key(binding->negative, negative); | |
| button_binding_add_key(binding->positive, positive); | |
| } | |
| void axis_binding_add_mouse_button_pair(AxisBinding* binding, int negative, int positive) | |
| { | |
| button_binding_add_mouse(binding->negative, negative); | |
| button_binding_add_mouse(binding->positive, positive); | |
| } | |
| void axis_binding_add_joypad_button_pair(AxisBinding* binding, int negative, int positive) | |
| { | |
| button_binding_add_joy(binding->negative, negative); | |
| button_binding_add_joy(binding->positive, positive); | |
| } | |
| void axis_binding_add_trigger_pair(AxisBinding* binding, int negative, int positive, float threshold) | |
| { | |
| button_binding_add_trigger(binding->negative, negative, threshold, false); | |
| button_binding_add_trigger(binding->positive, positive, threshold, true); | |
| } | |
| void axis_binding_handle_conflict_by_newest(AxisBinding* binding) | |
| { | |
| binding->conflict = HANDLE_CONFLICT_NEWEST; | |
| } | |
| void axis_binding_handle_conflict_by_oldest(AxisBinding* binding) | |
| { | |
| binding->conflict = HANDLE_CONFLICT_OLDEST; | |
| } | |
| void axis_binding_handle_conflict_by_cancel(AxisBinding* binding) | |
| { | |
| binding->conflict = HANDLE_CONFLICT_CANCEL; | |
| } | |
| void axis_binding_add_left_stick_x(AxisBinding* binding, float threshold) | |
| { | |
| button_binding_add_trigger(binding->negative, CF_JOYPAD_AXIS_LEFTX, threshold, false); | |
| button_binding_add_trigger(binding->positive, CF_JOYPAD_AXIS_LEFTX, threshold, true); | |
| } | |
| void axis_binding_add_left_stick_y(AxisBinding* binding, float threshold) | |
| { | |
| button_binding_add_trigger(binding->negative, CF_JOYPAD_AXIS_LEFTY, threshold, true); | |
| button_binding_add_trigger(binding->positive, CF_JOYPAD_AXIS_LEFTY, threshold, false); | |
| } | |
| void axis_binding_add_right_stick_x(AxisBinding* binding, float threshold) | |
| { | |
| button_binding_add_trigger(binding->negative, CF_JOYPAD_AXIS_RIGHTX, threshold, false); | |
| button_binding_add_trigger(binding->positive, CF_JOYPAD_AXIS_RIGHTX, threshold, true); | |
| } | |
| void axis_binding_add_right_stick_y(AxisBinding* binding, float threshold) | |
| { | |
| button_binding_add_trigger(binding->negative, CF_JOYPAD_AXIS_RIGHTY, threshold, true); | |
| button_binding_add_trigger(binding->positive, CF_JOYPAD_AXIS_RIGHTY, threshold, false); | |
| } | |
| bool axis_binding_press(AxisBinding* binding) | |
| { | |
| return button_binding_press(binding->negative) || button_binding_press(binding->positive); | |
| } | |
| bool axis_binding_release(AxisBinding* binding) | |
| { | |
| return button_binding_release(binding->negative) || button_binding_release(binding->positive); | |
| } | |
| void axis_binding_consume_press(AxisBinding* binding) | |
| { | |
| button_binding_consume_press(binding->negative); | |
| button_binding_consume_press(binding->positive); | |
| } | |
| void axis_binding_consume_release(AxisBinding* binding) | |
| { | |
| button_binding_consume_release(binding->negative); | |
| button_binding_consume_release(binding->positive); | |
| } | |
| float axis_binding_value(AxisBinding* binding) | |
| { | |
| float negative = button_binding_value(binding->negative); | |
| float positive = button_binding_value(binding->positive); | |
| if (negative <= 0 && positive <= 0) return 0; | |
| if (negative > 0 && positive <= 0) return -negative; | |
| if (positive > 0 && negative <= 0) return positive; | |
| if (binding->conflict == HANDLE_CONFLICT_CANCEL) return 0; | |
| else if (binding->conflict == HANDLE_CONFLICT_OLDEST) { | |
| if (binding->negative->last_timestep < binding->positive->last_timestep) { | |
| return -negative; | |
| } else { | |
| return positive; | |
| } | |
| } else if (binding->conflict == HANDLE_CONFLICT_NEWEST) { | |
| if (binding->negative->last_timestep < binding->positive->last_timestep) { | |
| return positive; | |
| } else { | |
| return -negative; | |
| } | |
| } | |
| return 0; | |
| } | |
| float axis_binding_sign(AxisBinding* binding) | |
| { | |
| float v = axis_binding_value(binding); | |
| if (v > 0) return 1; | |
| if (v < 0) return -1; | |
| return 0; | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| typedef struct StickBinding | |
| { | |
| AxisBinding* x_axis; | |
| AxisBinding* y_axis; | |
| } StickBinding; | |
| StickBinding* make_stick_binding(int player_index) | |
| { | |
| StickBinding* stick_binding = CALLOC(StickBinding); | |
| *stick_binding = (StickBinding){ | |
| .x_axis = make_axis_binding(player_index), | |
| .y_axis = make_axis_binding(player_index), | |
| }; | |
| return stick_binding; | |
| } | |
| void destroy_stick_binding(StickBinding* binding) | |
| { | |
| destroy_axis_binding(binding->x_axis); | |
| destroy_axis_binding(binding->y_axis); | |
| FREE(binding); | |
| } | |
| void stick_binding_add_keys(StickBinding* binding, int up, int down, int left, int right) | |
| { | |
| axis_binding_add_key_pair(binding->x_axis, left, right); | |
| axis_binding_add_key_pair(binding->y_axis, up, down); | |
| } | |
| void stick_binding_add_wasd(StickBinding* binding) | |
| { | |
| stick_binding_add_keys(binding, CF_KEY_W, CF_KEY_S, CF_KEY_A, CF_KEY_D); | |
| } | |
| void stick_binding_add_arrow_keys(StickBinding* binding) | |
| { | |
| stick_binding_add_keys(binding, CF_KEY_UP, CF_KEY_DOWN, CF_KEY_LEFT, CF_KEY_RIGHT); | |
| } | |
| void stick_binding_add_dpad(StickBinding* binding) | |
| { | |
| axis_binding_add_joypad_button_pair(binding->x_axis, CF_JOYPAD_BUTTON_DPAD_LEFT, CF_JOYPAD_BUTTON_DPAD_RIGHT); | |
| axis_binding_add_joypad_button_pair(binding->y_axis, CF_JOYPAD_BUTTON_DPAD_DOWN, CF_JOYPAD_BUTTON_DPAD_UP); | |
| } | |
| void stick_binding_add_left_stick(StickBinding* binding, float threshold) | |
| { | |
| axis_binding_add_left_stick_x(binding->x_axis, threshold); | |
| axis_binding_add_left_stick_y(binding->y_axis, threshold); | |
| } | |
| void stick_binding_add_right_stick(StickBinding* binding, float threshold) | |
| { | |
| axis_binding_add_right_stick_x(binding->x_axis, threshold); | |
| axis_binding_add_right_stick_y(binding->y_axis, threshold); | |
| } | |
| bool stick_binding_press(StickBinding* binding) | |
| { | |
| return axis_binding_press(binding->x_axis) || axis_binding_press(binding->y_axis); | |
| } | |
| bool stick_binding_release(StickBinding* binding) | |
| { | |
| return axis_binding_release(binding->x_axis) || axis_binding_release(binding->y_axis); | |
| } | |
| void stick_binding_consume_press(StickBinding* binding) | |
| { | |
| axis_binding_consume_press(binding->x_axis); | |
| axis_binding_consume_press(binding->y_axis); | |
| } | |
| void stick_binding_consume_release(StickBinding* binding) | |
| { | |
| axis_binding_consume_release(binding->x_axis); | |
| axis_binding_consume_release(binding->y_axis); | |
| } | |
| v2 stick_binding_value(StickBinding* binding) | |
| { | |
| v2 result; | |
| result.x = axis_binding_value(binding->x_axis); | |
| result.y = axis_binding_value(binding->y_axis); | |
| return result; | |
| } | |
| // Returns -1, 0, or 1 on x/y axes. 0 means no input. | |
| v2 stick_binding_sign(StickBinding* binding) | |
| { | |
| v2 value = stick_binding_value(binding); | |
| v2 result; | |
| result.x = (value.x == 0) ? 0 : sign(value.x); | |
| result.y = (value.y == 0) ? 0 : sign(value.y); | |
| return result; | |
| } | |
| //-------------------------------------------------------------------------------------------------- | |
| void register_joy_on_connect(void (*on_connect)(int, bool, void*), void* udata) | |
| { | |
| JoypadOnConnect callback = { | |
| .on_connect = on_connect, | |
| .udata = udata, | |
| }; | |
| apush(g_on_joypad_connects, callback); | |
| } | |
| void input_init() | |
| { | |
| for (int i = 0; i < MAX_JOYPADS; ++i) { | |
| apush(g_joypads, get_joypad(i)); | |
| } | |
| } | |
| void input_update() | |
| { | |
| // Handle joypad connect/disconnect. | |
| for (int i = 0; i < asize(g_joypads); ++i) { | |
| Joypad* joypad = g_joypads + i; | |
| Joypad refresh = get_joypad(i); | |
| if (refresh.connected) { | |
| if (!joypad->connected) { | |
| *joypad = refresh; | |
| for (int j = 0; j < asize(g_on_joypad_connects); ++j) { | |
| g_on_joypad_connects[j].on_connect(i, true, g_on_joypad_connects[j].udata); | |
| } | |
| } | |
| } else { | |
| if (joypad->connected) { | |
| for (int j = 0; j < asize(g_on_joypad_connects); ++j) { | |
| g_on_joypad_connects[j].on_connect(i, false, g_on_joypad_connects[j].udata); | |
| } | |
| joypad->connected = false; | |
| } | |
| } | |
| } | |
| // Update all binding objects. | |
| for (int i = 0; i < asize(g_button_bindings); ++i) { | |
| button_binding_update(g_button_bindings + i); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment