// Input handler routines, platform-independent. // The Jai Input module ported to use sokol events. #load "sokol.jai"; Event_Type :: enum u32 { // If we set this to u8, our packing will stop matching C's. UNINITIALIZED :: 0; KEYBOARD :: 1; TEXT_INPUT :: 2; WINDOW :: 3; MOUSE_WHEEL :: 4; QUIT :: 5; DRAG_AND_DROP_FILES :: 6; TOUCH :: 7; } Key_Current_State :: enum_flags u32 { NONE :: 0x0; DOWN :: 0x1; START :: 0x4; END :: 0x8; } // We reserve 32 buttons for each gamepad. GAMEPAD_BUTTON_COUNT :: 32; Key_Code :: enum u32 { UNKNOWN :: 0; // Non-textual keys that have placements in the ASCII table // (and thus in Unicode): BACKSPACE :: 8; TAB :: 9; LINEFEED :: 10; ENTER :: 13; ESCAPE :: 27; SPACEBAR :: 32; // The letters A-Z live in here as well and may be returned // by keyboard events. DELETE :: 127; ARROW_UP :: 128; ARROW_DOWN :: 129; ARROW_LEFT :: 130; ARROW_RIGHT :: 131; PAGE_UP :: 132; PAGE_DOWN :: 133; HOME :: 134; END :: 135; INSERT :: 136; PAUSE :: 137; SCROLL_LOCK :: 138; ALT; CTRL; SHIFT; CMD; META :: CMD; F1; F2; F3; F4; F5; F6; F7; F8; F9; F10; F11; F12; F13; F14; F15; F16; F17; F18; F19; F20; F21; F22; F23; F24; PRINT_SCREEN; MOUSE_BUTTON_LEFT; MOUSE_BUTTON_MIDDLE; MOUSE_BUTTON_RIGHT; MOUSE_WHEEL_UP; MOUSE_WHEEL_DOWN; // We reserve button codes for up to 4 gamepads. GAMEPAD_0_BEGIN; GAMEPAD_0_END :: GAMEPAD_0_BEGIN + xx GAMEPAD_BUTTON_COUNT; GAMEPAD_1_BEGIN; GAMEPAD_1_END :: GAMEPAD_1_BEGIN + xx GAMEPAD_BUTTON_COUNT; GAMEPAD_2_BEGIN; GAMEPAD_2_END :: GAMEPAD_2_BEGIN + xx GAMEPAD_BUTTON_COUNT; GAMEPAD_3_BEGIN; GAMEPAD_3_END :: GAMEPAD_3_BEGIN + xx GAMEPAD_BUTTON_COUNT; TOUCH; // WARNING! // // We make an array whose size is controlled // by the last enum value in this array, so if you make // really big values to match Unicode code points, our // memory usage will become quite sorry. // // -jblow, 19 March 2017 // } // Modifier_Flags used to use #place, but I rewrote it to use a union // instead .... it is not necessarily clearer though! So I am switching // it back for now... Event :: struct { Modifier_Flags :: union { // Eventually we'd like the *_pressed modifiers to be 1 bit each, // but still be nameable as booleans. But for now they're 1 byte each. // You can compare them as a u32 using the 'packed' member. struct { // @@ This is confusing. Below key_pressed means the key was just pressed, here _pressed means the key is held down. shift_pressed := false; ctrl_pressed := false; alt_pressed := false; cmd_meta_pressed := false; // Cmd on macOS, Meta on Linux } packed: u32 = 0; } type: Event_Type = Event_Type.UNINITIALIZED; // If keyboard event: key_pressed: u32; // If not pressed, it's a key release. key_code := Key_Code.UNKNOWN; using modifier_flags: Modifier_Flags; // Only set for Event_Type.KEYBOARD. utf32: u32; // If TEXT_INPUT. repeat := false; // If KEYBOARD event. text_input_count: u16; // If KEYBOARD event that also generated TEXT_INPUT events, this will tell you how many TEXT_INPUT events after this KEYBOARD event were generated. typical_wheel_delta: s32; // Used only for mouse events. wheel_delta: s32; // Used only for mouse events. files: [..] string; // Used only for drag and drop events. Both the array and its contents are heap-allocated, lives until events are reset for the next frame. touch_type: enum u8 { // Used only for touch events. MOVED :: 0; PRESSED :: 1; RELEASED :: 2; } touch_index: s32; // Used only for touch events. Index of which touch this is. } // Per-frame mouse deltas: mouse_delta_x: int; mouse_delta_y: int; mouse_delta_z: int; events_this_frame: [..] Event; input_button_states: [NUM_BUTTON_STATES] Key_Current_State; input_application_has_focus := false; NUM_BUTTON_STATES :: #run enum_highest_value(Key_Code) + 1; Window_Resize_Record :: struct { window: Window_Type; width: s32; height: s32; } Window_Move_Record :: struct { window: Window_Type; x: s32; y: s32; } get_window_resizes :: () -> [] Window_Resize_Record { // The return value here will stick around in memory until the next call // to get_window_resizes (from any thread. Actually this whole module does // not deal with threading, so don't do that!) if resizes_to_free array_reset(*resizes_to_free); if !pending_resizes return .[]; array_copy(*resizes_to_free, pending_resizes); this_allocation_is_not_a_leak(resizes_to_free.data); pending_resizes.count = 0; return resizes_to_free; } get_window_moves :: () -> [] Window_Move_Record { // See notes on get_window_resizes. This works the same way. if moves_to_free array_reset(*moves_to_free); if !pending_moves return .[]; array_copy(*moves_to_free, pending_moves); pending_moves.count = 0; return moves_to_free; } input_per_frame_event_and_flag_update :: () { // Called once per frame, probably. for events_this_frame { allocator := it.files.allocator; // Same allocator for filenames and the array itself. for file: it.files { free(file,, allocator); } array_free(it.files,, allocator); } array_reset(*events_this_frame); mask := ~Key_Current_State.START; end_mask := ~(Key_Current_State.END | .DOWN | .START); // @Speed: Could just keep a list of who is not currently set. for * input_button_states { if it.* & .END { it.* &= end_mask; } else { it.* &= mask; } } mouse_delta_x = 0; mouse_delta_y = 0; mouse_delta_z = 0; } #scope_module pending_moves: [..] Window_Move_Record; moves_to_free: [..] Window_Move_Record; pending_resizes: [..] Window_Resize_Record; resizes_to_free: [..] Window_Resize_Record; #import "Basic"; #import "Window_Type";