2025-07-06 20:43:53 +03:00
# include "InputManager.hpp"
2026-04-04 17:52:43 +03:00
# include "CustomEvents.hpp"
2025-07-06 20:43:53 +03:00
# include <SDL3/SDL_clipboard.h>
2026-03-01 12:09:27 +03:00
# include <SDL3/SDL_keyboard.h>
2025-07-06 20:43:53 +03:00
# include <SDL3/SDL_keycode.h>
2026-03-03 16:01:21 +03:00
# include <SDL3/SDL_pen.h>
2026-03-05 23:34:30 +03:00
# include <SDL3/SDL_properties.h>
2026-03-05 12:03:10 +03:00
# include <SDL3/SDL_timer.h>
2025-07-06 20:43:53 +03:00
# include <optional>
2026-01-12 11:27:41 +03:00
# include <Helpers/Logger.hpp>
2026-04-04 17:52:43 +03:00
2026-01-12 15:26:58 +03:00
# ifdef _WIN32
2026-01-12 11:27:41 +03:00
# include <include/core/SkStream.h>
# include <include/encode/SkPngEncoder.h>
# include "../deps/clip/clip.h"
# endif
2025-07-06 20:43:53 +03:00
# ifdef __EMSCRIPTEN__
# include <EmscriptenHelpers/emscripten_browser_clipboard.h>
extern " C " {
int isAcceptingInputEmscripten = 0 ;
EMSCRIPTEN_KEEPALIVE inline int is_text_input_happening ( ) {
return isAcceptingInputEmscripten ;
}
}
# endif
2026-03-01 17:28:43 +03:00
# include "MainProgram.hpp"
InputManager : : InputManager ( MainProgram & initMain ) :
main ( initMain ) {
2025-07-06 20:43:53 +03:00
frame_reset ( { 0 , 0 } ) ;
defaultKeyAssignments [ { 0 , SDLK_W } ] = KEY_CAMERA_ROTATE_COUNTERCLOCKWISE ;
defaultKeyAssignments [ { 0 , SDLK_Q } ] = KEY_CAMERA_ROTATE_CLOCKWISE ;
defaultKeyAssignments [ { 0 , SDLK_DELETE } ] = KEY_DRAW_DELETE ;
defaultKeyAssignments [ { CTRL_MOD , SDLK_Z } ] = KEY_UNDO ;
defaultKeyAssignments [ { CTRL_MOD , SDLK_R } ] = KEY_REDO ;
defaultKeyAssignments [ { 0 , SDLK_TAB } ] = KEY_NOGUI ;
defaultKeyAssignments [ { 0 , SDLK_F11 } ] = KEY_FULLSCREEN ;
defaultKeyAssignments [ { CTRL_MOD , SDLK_S } ] = KEY_SAVE ;
defaultKeyAssignments [ { CTRL_MOD | SDL_KMOD_SHIFT , SDLK_S } ] = KEY_SAVE_AS ;
defaultKeyAssignments [ { CTRL_MOD , SDLK_C } ] = KEY_COPY ;
defaultKeyAssignments [ { CTRL_MOD , SDLK_X } ] = KEY_CUT ;
defaultKeyAssignments [ { CTRL_MOD , SDLK_V } ] = KEY_PASTE ;
2026-01-06 19:06:44 +03:00
defaultKeyAssignments [ { CTRL_MOD | SDL_KMOD_SHIFT , SDLK_V } ] = KEY_PASTE_IMAGE ;
2025-07-06 20:43:53 +03:00
defaultKeyAssignments [ { 0 , SDLK_B } ] = KEY_DRAW_TOOL_BRUSH ;
defaultKeyAssignments [ { 0 , SDLK_E } ] = KEY_DRAW_TOOL_ERASER ;
2026-01-07 10:34:51 +03:00
defaultKeyAssignments [ { 0 , SDLK_U } ] = KEY_DRAW_TOOL_ZOOM ;
2025-12-10 21:46:08 +05:30
defaultKeyAssignments [ { 0 , SDLK_L } ] = KEY_DRAW_TOOL_LASSOSELECT ;
defaultKeyAssignments [ { 0 , SDLK_H } ] = KEY_DRAW_TOOL_PAN ;
2025-07-06 20:43:53 +03:00
defaultKeyAssignments [ { 0 , SDLK_T } ] = KEY_DRAW_TOOL_TEXTBOX ;
defaultKeyAssignments [ { 0 , SDLK_R } ] = KEY_DRAW_TOOL_RECTANGLE ;
defaultKeyAssignments [ { 0 , SDLK_C } ] = KEY_DRAW_TOOL_ELLIPSE ;
defaultKeyAssignments [ { 0 , SDLK_X } ] = KEY_DRAW_TOOL_RECTSELECT ;
2025-08-05 12:11:50 +03:00
defaultKeyAssignments [ { 0 , SDLK_I } ] = KEY_DRAW_TOOL_EYEDROPPER ;
2025-07-06 20:43:53 +03:00
defaultKeyAssignments [ { 0 , SDLK_P } ] = KEY_DRAW_TOOL_SCREENSHOT ;
2026-01-08 11:19:48 +03:00
defaultKeyAssignments [ { 0 , SDLK_N } ] = KEY_DRAW_TOOL_LINE ;
2025-07-06 20:43:53 +03:00
defaultKeyAssignments [ { 0 , SDLK_F1 } ] = KEY_OPEN_CHAT ;
defaultKeyAssignments [ { 0 , SDLK_F3 } ] = KEY_SHOW_METRICS ;
defaultKeyAssignments [ { 0 , SDLK_F2 } ] = KEY_SHOW_PLAYER_LIST ;
2025-09-28 18:25:46 +03:00
defaultKeyAssignments [ { 0 , SDLK_SPACE } ] = KEY_HOLD_TO_PAN ;
2026-01-07 10:34:51 +03:00
defaultKeyAssignments [ { 0 , SDLK_Z } ] = KEY_HOLD_TO_ZOOM ;
2026-02-28 22:47:35 +03:00
defaultKeyAssignments [ { 0 , SDLK_ESCAPE } ] = KEY_DESELECT_AND_EDIT_TOOL ;
2025-07-06 20:43:53 +03:00
2025-12-02 10:14:45 +03:00
# ifdef __EMSCRIPTEN__
// Without this, SDL eats the CTRL-V event that initiates the paste event
// https://github.com/pthom/hello_imgui/issues/3#issuecomment-1564536870
EM_ASM ( {
window . addEventListener ( ' keydown ' , function ( event ) {
if ( ( event . ctrlKey | | event . metaKey ) & & ( event . key = = ' v ' | | event . code = = ' KeyV ' ) ) {
if ( Module [ " ccall " ] ( ' is_text_input_happening ' , ' number ' , [ ] , [ ] ) = = = 1 )
event . stopImmediatePropagation ( ) ;
}
} , true ) ;
} ) ;
emscripten_browser_clipboard : : paste_event ( [ ] ( std : : string & & pasteData , void * callbackData ) {
InputManager * inMan = ( InputManager * ) callbackData ;
2026-04-12 17:31:32 +03:00
inMan - > process_text_paste ( pasteData , true ) ;
2025-12-02 10:14:45 +03:00
} , this ) ;
# endif
2025-11-11 22:21:25 +03:00
2025-07-06 20:43:53 +03:00
keyAssignments = defaultKeyAssignments ;
}
2026-04-03 23:02:33 +03:00
unsigned InputManager : : Text : : get_id ( ) {
static unsigned nextID = 0 ;
while ( std : : find_if ( textBoxes . begin ( ) , textBoxes . end ( ) , [ & ] ( const TextBoxInfo & info ) { return info . id = = nextID ; } ) ! = textBoxes . end ( ) )
+ + nextID ;
return nextID ;
}
unsigned InputManager : : set_text_box_front ( const std : : optional < SCollision : : AABB < float > > & textboxRect , const TextInputProperties & inputProps ) {
2026-03-01 12:09:27 +03:00
Text : : TextBoxInfo textBoxInfo ;
2026-03-05 23:34:30 +03:00
textBoxInfo . rect = textboxRect ;
textBoxInfo . textInputProperties = inputProps ;
2026-04-03 23:02:33 +03:00
textBoxInfo . id = text . get_id ( ) ;
2026-03-01 12:09:27 +03:00
text . textBoxes . emplace_front ( textBoxInfo ) ;
2026-03-05 23:34:30 +03:00
text . update_accepting_input ( main . window . sdlWindow ) ;
2026-04-03 23:02:33 +03:00
return textBoxInfo . id ;
2025-10-16 21:22:08 +03:00
}
2026-04-03 23:02:33 +03:00
unsigned InputManager : : set_text_box_back ( const std : : optional < SCollision : : AABB < float > > & textboxRect , const TextInputProperties & inputProps ) {
2026-03-01 12:09:27 +03:00
Text : : TextBoxInfo textBoxInfo ;
2026-03-05 23:34:30 +03:00
textBoxInfo . rect = textboxRect ;
textBoxInfo . textInputProperties = inputProps ;
2026-04-03 23:02:33 +03:00
textBoxInfo . id = text . get_id ( ) ;
2026-03-01 12:09:27 +03:00
text . textBoxes . emplace_back ( textBoxInfo ) ;
2026-03-05 23:34:30 +03:00
text . update_accepting_input ( main . window . sdlWindow ) ;
2026-04-03 23:02:33 +03:00
return textBoxInfo . id ;
2025-10-16 21:22:08 +03:00
}
2026-04-03 23:02:33 +03:00
void InputManager : : remove_text_box ( unsigned id ) {
std : : erase_if ( text . textBoxes , [ id ] ( Text : : TextBoxInfo & info ) {
return ( info . id = = id ) ;
2026-03-01 12:09:27 +03:00
} ) ;
2026-03-05 23:34:30 +03:00
text . update_accepting_input ( main . window . sdlWindow ) ;
2025-07-06 20:43:53 +03:00
}
2026-03-01 12:09:27 +03:00
bool InputManager : : Text : : is_accepting_input ( ) {
2026-03-05 23:34:30 +03:00
return ! textBoxes . empty ( ) ;
2026-03-01 12:09:27 +03:00
}
2026-04-04 17:52:43 +03:00
std : : optional < unsigned > InputManager : : Text : : current_textbox_editing_id ( ) {
if ( textBoxes . empty ( ) )
return std : : nullopt ;
return textBoxes . front ( ) . id ;
}
2026-03-05 23:34:30 +03:00
void InputManager : : Text : : update_accepting_input ( SDL_Window * window ) {
if ( ! textBoxes . empty ( ) ) {
auto & textbox = textBoxes . front ( ) ;
if ( ! propID . has_value ( ) )
propID = SDL_CreateProperties ( ) ;
SDL_PropertiesID propIDVal = propID . value ( ) ;
2026-03-05 23:53:05 +03:00
// There is a bug where decimal places disappear when textinput type is set to number on android. We'll just set those textinputs back to text type
SDL_SetNumberProperty ( propIDVal , SDL_PROP_TEXTINPUT_TYPE_NUMBER , ( textbox . textInputProperties . inputType = = SDL_TEXTINPUT_TYPE_NUMBER ) ? SDL_TEXTINPUT_TYPE_TEXT : textbox . textInputProperties . inputType ) ;
2026-03-05 23:34:30 +03:00
SDL_SetNumberProperty ( propIDVal , SDL_PROP_TEXTINPUT_CAPITALIZATION_NUMBER , textbox . textInputProperties . capitalization ) ;
SDL_SetNumberProperty ( propIDVal , SDL_PROP_TEXTINPUT_AUTOCORRECT_BOOLEAN , textbox . textInputProperties . autocorrect ) ;
SDL_SetNumberProperty ( propIDVal , SDL_PROP_TEXTINPUT_MULTILINE_BOOLEAN , textbox . textInputProperties . multiline ) ;
2026-03-05 23:53:05 +03:00
SDL_SetNumberProperty ( propIDVal , SDL_PROP_TEXTINPUT_ANDROID_INPUTTYPE_NUMBER , ( textbox . textInputProperties . androidInputType & ANDROIDTEXT_TYPE_CLASS_NUMBER ) ? ANDROIDTEXT_TYPE_CLASS_TEXT : textbox . textInputProperties . androidInputType ) ;
2026-03-07 13:00:10 +03:00
if ( ! textBoxes . front ( ) . rect )
SDL_SetTextInputArea ( window , nullptr , 0 ) ;
else {
SDL_Rect r = textBoxes . front ( ) . rect - > get_sdl_rect ( ) ;
SDL_SetTextInputArea ( window , & r , 0 ) ;
}
2026-03-05 23:34:30 +03:00
SDL_StartTextInputWithProperties ( window , propIDVal ) ;
2026-03-07 20:15:20 +03:00
# ifdef __EMSCRIPTEN__
isAcceptingInputEmscripten = 1 ;
# endif
2026-03-01 12:09:27 +03:00
}
2026-03-06 14:12:13 +03:00
else {
2026-03-01 12:09:27 +03:00
SDL_StopTextInput ( window ) ;
2026-03-06 14:12:13 +03:00
SDL_SetTextInputArea ( window , nullptr , 0 ) ;
2026-03-07 20:15:20 +03:00
# ifdef __EMSCRIPTEN__
isAcceptingInputEmscripten = 0 ;
# endif
2026-03-06 14:12:13 +03:00
}
2026-03-01 12:09:27 +03:00
}
2025-12-02 09:53:03 +03:00
std : : string InputManager : : get_clipboard_str_SDL ( ) {
2025-07-06 20:43:53 +03:00
char * data = SDL_GetClipboardText ( ) ;
std : : string toRet ( data ) ;
SDL_free ( data ) ;
return toRet ;
2025-11-11 17:42:41 +03:00
}
2026-01-12 15:02:55 +03:00
# ifdef _WIN32
// From clip library example, useful for debugging
void print_clip_image_spec ( const clip : : image_spec & spec ) {
std : : cout < < " Image in clipboard "
< < spec . width < < " x " < < spec . height
< < " ( " < < spec . bits_per_pixel < < " bpp) \n "
< < " Format: " < < " \n "
< < std : : hex
< < " Red mask: " < < spec . red_mask < < " \n "
< < " Green mask: " < < spec . green_mask < < " \n "
< < " Blue mask: " < < spec . blue_mask < < " \n "
< < " Alpha mask: " < < spec . alpha_mask < < " \n "
< < std : : dec
< < " Red shift: " < < spec . red_shift < < " \n "
< < " Green shift: " < < spec . green_shift < < " \n "
< < " Blue shift: " < < spec . blue_shift < < " \n "
< < " Alpha shift: " < < spec . alpha_shift < < " \n " ;
}
# endif
2026-01-05 19:36:44 +03:00
void InputManager : : get_clipboard_image_data_SDL ( const std : : function < void ( std : : string_view data ) > & callback ) {
2026-01-05 19:15:42 +03:00
# ifndef __EMSCRIPTEN__
static std : : unordered_set < std : : string > validMimetypes ;
if ( validMimetypes . empty ( ) ) {
validMimetypes . emplace ( " image/png " ) ;
validMimetypes . emplace ( " image/gif " ) ;
validMimetypes . emplace ( " image/jpeg " ) ;
validMimetypes . emplace ( " image/svg+xml " ) ;
validMimetypes . emplace ( " image/webp " ) ;
2026-01-12 11:27:41 +03:00
# ifdef _WIN32
2026-01-11 16:16:48 +03:00
validMimetypes . emplace ( " image/bmp " ) ;
2026-01-12 11:27:41 +03:00
# endif
2026-01-05 19:15:42 +03:00
}
size_t mimeTypeSize ;
char * * mimeTypes = SDL_GetClipboardMimeTypes ( & mimeTypeSize ) ;
if ( mimeTypes ) {
for ( size_t i = 0 ; i < mimeTypeSize ; i + + ) {
if ( validMimetypes . contains ( mimeTypes [ i ] ) ) {
2026-01-12 11:27:41 +03:00
if ( std : : string ( mimeTypes [ i ] ) = = " image/bmp " ) {
2026-01-12 15:26:58 +03:00
# ifdef _WIN32
2026-01-12 15:02:55 +03:00
std : : vector < uint8_t > result ;
try {
if ( ! clip : : has ( clip : : image_format ( ) ) )
throw std : : runtime_error ( " Clipboard doesn't contain an image " ) ;
clip : : image img ;
if ( ! clip : : get_image ( img ) )
throw std : : runtime_error ( " Error getting image from clipboard " ) ;
clip : : image_spec spec = img . spec ( ) ;
SDL_PixelFormat pFormat = SDL_GetPixelFormatForMasks ( spec . bits_per_pixel , spec . red_mask , spec . green_mask , spec . blue_mask , spec . alpha_mask ) ;
if ( pFormat ! = SDL_PIXELFORMAT_UNKNOWN ) {
SDL_Surface * surfOriginal = SDL_CreateSurfaceFrom ( spec . width , spec . height , pFormat , img . data ( ) , spec . bytes_per_row ) ;
if ( surfOriginal ) {
SDL_LockSurface ( surfOriginal ) ;
SDL_Surface * cSurf = SDL_ConvertSurface ( surfOriginal , SDL_PIXELFORMAT_RGBA32 ) ;
SDL_UnlockSurface ( surfOriginal ) ;
SDL_DestroySurface ( surfOriginal ) ;
if ( cSurf ) {
SDL_LockSurface ( cSurf ) ;
SkDynamicMemoryWStream out ;
2026-01-12 15:06:21 +03:00
if ( SkPngEncoder : : Encode ( & out , SkPixmap ( SkImageInfo : : Make ( cSurf - > w , cSurf - > h , kRGBA_8888_SkColorType , kUnpremul_SkAlphaType ) , cSurf - > pixels , cSurf - > pitch ) , { } ) ) {
out . flush ( ) ;
SDL_UnlockSurface ( cSurf ) ;
SDL_DestroySurface ( cSurf ) ;
result = out . detachAsVector ( ) ;
}
2026-01-12 15:08:22 +03:00
else {
SDL_UnlockSurface ( cSurf ) ;
SDL_DestroySurface ( cSurf ) ;
2026-01-12 15:06:21 +03:00
throw std : : runtime_error ( " Could not encode image data to PNG " ) ;
2026-01-12 15:08:22 +03:00
}
2026-01-12 15:02:55 +03:00
}
else
throw std : : runtime_error ( " Bitmap could not be converted to new format " ) ;
}
2026-01-12 11:27:41 +03:00
else
2026-01-12 15:02:55 +03:00
throw std : : runtime_error ( " Bitmap could not be read into surface " ) ;
2026-01-12 11:27:41 +03:00
}
2026-01-12 15:02:55 +03:00
else
throw std : : runtime_error ( " Bitmap format is unsupported " ) ;
2026-01-12 11:27:41 +03:00
}
2026-01-12 15:02:55 +03:00
catch ( const std : : runtime_error & e ) {
Logger : : get ( ) . log ( " INFO " , std : : string ( " [InputManager::get_clipboard_image_data_SDL] Error thrown: " ) + e . what ( ) ) ;
}
catch ( . . . ) {
Logger : : get ( ) . log ( " INFO " , " [InputManager::get_clipboard_image_data_SDL] Unknown error thrown " ) ;
}
if ( ! result . empty ( ) )
callback ( std : : string_view ( ( char * ) ( result . data ( ) ) , result . size ( ) ) ) ; // This part of the code should throw errors
2026-01-12 11:27:41 +03:00
# endif
}
else {
size_t clipboardDataSize ;
void * clipboardData = SDL_GetClipboardData ( mimeTypes [ i ] , & clipboardDataSize ) ;
callback ( std : : string_view ( static_cast < char * > ( clipboardData ) , clipboardDataSize ) ) ;
SDL_free ( clipboardData ) ;
}
2026-01-06 19:06:44 +03:00
return ;
2026-01-05 19:15:42 +03:00
}
}
}
# endif
}
2025-07-06 20:43:53 +03:00
void InputManager : : set_clipboard_str ( std : : string_view s ) {
# ifdef __EMSCRIPTEN__
emscripten_browser_clipboard : : copy ( std : : string ( s ) ) ;
2025-11-12 09:00:03 +03:00
lastCopiedRichText = std : : nullopt ;
2025-07-06 20:43:53 +03:00
# else
SDL_SetClipboardText ( s . data ( ) ) ;
# endif
}
2025-11-12 09:00:03 +03:00
void InputManager : : set_clipboard_plain_and_richtext_pair ( const std : : pair < std : : string , RichText : : TextData > & plainAndRichtextPair ) {
2025-11-11 22:21:25 +03:00
set_clipboard_str ( plainAndRichtextPair . first ) ;
2025-11-12 09:00:03 +03:00
lastCopiedRichText = plainAndRichtextPair . second ;
2025-11-11 17:42:41 +03:00
}
2026-03-31 13:17:58 +03:00
std : : string InputManager : : key_assignment_to_str ( const Vector2ui32 & k ) const {
2025-07-06 20:43:53 +03:00
std : : string toRet ;
if ( k . x ( ) & SDL_KMOD_CTRL )
toRet + = " CTRL+ " ;
if ( k . x ( ) & SDL_KMOD_ALT )
toRet + = " ALT+ " ;
if ( k . x ( ) & SDL_KMOD_SHIFT )
toRet + = " SHIFT+ " ;
if ( k . x ( ) & SDL_KMOD_GUI )
toRet + = " META+ " ;
toRet + = SDL_GetKeyName ( k . y ( ) ) ;
return toRet ;
}
2026-03-31 13:17:58 +03:00
Vector2ui32 InputManager : : key_assignment_from_str ( const std : : string & s ) const {
2025-07-06 20:43:53 +03:00
Vector2ui32 toRet = { 0 , 0 } ;
if ( s . find ( " CTRL+ " ) ! = std : : string : : npos )
toRet . x ( ) | = SDL_KMOD_CTRL ;
if ( s . find ( " ALT+ " ) ! = std : : string : : npos )
toRet . x ( ) | = SDL_KMOD_ALT ;
if ( s . find ( " SHIFT+ " ) ! = std : : string : : npos )
toRet . x ( ) | = SDL_KMOD_SHIFT ;
if ( s . find ( " META+ " ) ! = std : : string : : npos )
toRet . x ( ) | = SDL_KMOD_GUI ;
size_t startInd = s . find_last_of ( " + " ) ;
if ( startInd = = std : : string : : npos )
startInd = 0 ;
else
startInd + + ;
toRet . y ( ) = SDL_GetKeyFromName ( s . substr ( startInd ) . c_str ( ) ) ;
if ( toRet . y ( ) = = 0 )
toRet . x ( ) = 0 ;
return toRet ;
}
const InputManager : : KeyData & InputManager : : key ( KeyCode kCode ) {
return keys [ kCode ] ;
}
2026-04-09 16:30:28 +03:00
void InputManager : : set_key_down ( const SDL_KeyboardEvent & e , KeyCode kCode , bool acceptingTextInput ) {
2025-07-06 20:43:53 +03:00
auto & k = keys [ kCode ] ;
k . held = true ;
2026-04-09 16:30:28 +03:00
if ( acceptingTextInput )
2026-04-03 23:02:33 +03:00
main . input_text_key_callback ( KeyCallbackArgs { . key = kCode , . down = e . down , . repeat = e . repeat } ) ;
else
main . input_key_callback ( KeyCallbackArgs { . key = kCode , . down = e . down , . repeat = e . repeat } ) ;
2025-07-06 20:43:53 +03:00
}
2026-04-09 16:30:28 +03:00
void InputManager : : set_key_up ( const SDL_KeyboardEvent & e , KeyCode kCode , bool acceptingTextInput ) {
2025-07-06 20:43:53 +03:00
keys [ kCode ] . held = false ;
2026-04-09 16:30:28 +03:00
if ( acceptingTextInput )
2026-04-03 23:02:33 +03:00
main . input_text_key_callback ( KeyCallbackArgs { . key = kCode , . down = e . down , . repeat = e . repeat } ) ;
// Send key up even if text input is happening
main . input_key_callback ( KeyCallbackArgs { . key = kCode , . down = e . down , . repeat = e . repeat } ) ;
2025-07-06 20:43:53 +03:00
}
uint32_t InputManager : : make_generic_key_mod ( SDL_Keymod m ) {
uint32_t toRet = 0 ;
if ( m & SDL_KMOD_CTRL )
toRet | = SDL_KMOD_CTRL ;
if ( m & SDL_KMOD_ALT )
toRet | = SDL_KMOD_ALT ;
if ( m & SDL_KMOD_GUI )
toRet | = SDL_KMOD_GUI ;
if ( m & SDL_KMOD_SHIFT )
toRet | = SDL_KMOD_SHIFT ;
return toRet ;
}
2026-04-03 23:02:33 +03:00
void InputManager : : backend_input_text_event ( const std : : string & str ) {
2026-04-04 17:52:43 +03:00
main . input_text_callback ( TextCallbackArgs { . str = str } ) ;
2026-04-03 23:02:33 +03:00
}
2026-03-04 11:03:05 +03:00
void InputManager : : backend_drop_file_event ( const SDL_DropEvent & e ) {
Vector2f mousePos = { e . x * main . window . density , e . y * main . window . density } ;
mouse . set_pos ( mousePos ) ;
main . input_drop_file_callback ( {
. pos = mousePos ,
. source = e . source ,
. data = e . data
} ) ;
}
void InputManager : : backend_drop_text_event ( const SDL_DropEvent & e ) {
Vector2f mousePos = { e . x * main . window . density , e . y * main . window . density } ;
mouse . set_pos ( mousePos ) ;
main . input_drop_text_callback ( {
. pos = mousePos ,
. source = e . source ,
. data = e . data
} ) ;
}
2026-03-01 17:28:43 +03:00
void InputManager : : backend_mouse_button_up_update ( const SDL_MouseButtonEvent & e ) {
Vector2f mousePos = { e . x * main . window . density , e . y * main . window . density } ;
2026-03-03 14:59:18 +03:00
mouse . set_pos ( mousePos ) ;
2026-03-01 17:28:43 +03:00
if ( e . button = = 1 )
mouse . leftDown = false ;
else if ( e . button = = 2 )
mouse . middleDown = false ;
else if ( e . button = = 3 )
mouse . rightDown = false ;
2026-03-03 14:59:18 +03:00
MouseButtonCallbackArgs args {
2026-03-05 21:09:30 +03:00
. deviceType = MouseDeviceType : : MOUSE ,
2026-03-02 23:09:48 +03:00
. button = static_cast < MouseButton > ( e . button ) ,
2026-03-01 17:28:43 +03:00
. down = e . down ,
. clicks = e . clicks ,
. pos = mousePos
2026-03-03 14:59:18 +03:00
} ;
main . input_mouse_button_callback ( args ) ;
2026-03-05 12:36:34 +03:00
isTouchDevice = false ;
2026-03-01 17:28:43 +03:00
}
void InputManager : : backend_mouse_button_down_update ( const SDL_MouseButtonEvent & e ) {
Vector2f mousePos = { e . x * main . window . density , e . y * main . window . density } ;
2026-03-03 14:59:18 +03:00
mouse . set_pos ( mousePos ) ;
2026-03-07 14:12:11 +03:00
if ( e . button = = 1 )
2026-03-01 17:28:43 +03:00
mouse . leftDown = true ;
2026-03-07 14:12:11 +03:00
else if ( e . button = = 2 )
2026-03-01 17:28:43 +03:00
mouse . middleDown = true ;
2026-03-07 14:12:11 +03:00
else if ( e . button = = 3 )
2026-03-01 17:28:43 +03:00
mouse . rightDown = true ;
2026-03-07 14:12:11 +03:00
2026-03-03 14:59:18 +03:00
MouseButtonCallbackArgs args {
2026-03-05 21:09:30 +03:00
. deviceType = MouseDeviceType : : MOUSE ,
2026-03-02 23:09:48 +03:00
. button = static_cast < MouseButton > ( e . button ) ,
2026-03-01 17:28:43 +03:00
. down = e . down ,
. clicks = e . clicks ,
. pos = mousePos
2026-03-03 14:59:18 +03:00
} ;
main . input_mouse_button_callback ( args ) ;
2026-03-05 12:36:34 +03:00
isTouchDevice = false ;
2026-03-01 17:28:43 +03:00
}
void InputManager : : backend_mouse_motion_update ( const SDL_MouseMotionEvent & e ) {
Vector2f mouseNewPos = { e . x * main . window . density , e . y * main . window . density } ;
mouse . set_pos ( mouseNewPos ) ;
2026-03-03 14:59:18 +03:00
Vector2f mouseRel = { e . xrel * main . window . density , e . yrel * main . window . density } ;
2026-03-02 16:36:34 +03:00
main . input_mouse_motion_callback ( {
2026-03-05 21:09:30 +03:00
. deviceType = MouseDeviceType : : MOUSE ,
2026-03-01 17:28:43 +03:00
. pos = mouseNewPos ,
. move = mouseRel
} ) ;
2026-03-05 12:36:34 +03:00
isTouchDevice = false ;
2026-03-01 17:28:43 +03:00
}
void InputManager : : backend_mouse_wheel_update ( const SDL_MouseWheelEvent & e ) {
2026-03-02 23:09:48 +03:00
Vector2f mouseNewPos = { e . mouse_x * main . window . density , e . mouse_y * main . window . density } ;
mouse . set_pos ( mouseNewPos ) ;
2026-03-03 14:59:18 +03:00
2026-03-02 23:09:48 +03:00
main . input_mouse_wheel_callback ( {
. mousePos = mouseNewPos ,
. amount = { e . x , e . y } ,
. tickAmount = { e . integer_x , e . integer_y }
} ) ;
2026-03-05 12:36:34 +03:00
isTouchDevice = false ;
2026-03-01 17:28:43 +03:00
}
2026-03-03 14:59:18 +03:00
void InputManager : : backend_pen_button_down_update ( const SDL_PenButtonEvent & e ) {
Vector2f mouseNewPos = { e . x * main . window . density , e . y * main . window . density } ;
mouse . set_pos ( mouseNewPos ) ;
pen . previousPos = mouseNewPos ;
pen . buttons [ e . button ] . held = true ;
2026-03-03 16:01:21 +03:00
pen . isEraser = e . pen_state & SDL_PEN_INPUT_ERASER_TIP ;
2026-03-03 14:59:18 +03:00
2026-03-31 13:17:58 +03:00
if ( e . button = = main . conf . tabletOptions . middleClickButton ) {
2026-03-03 14:59:18 +03:00
main . input_mouse_button_callback ( {
2026-03-05 21:09:30 +03:00
. deviceType = MouseDeviceType : : PEN ,
2026-03-03 14:59:18 +03:00
. button = MouseButton : : MIDDLE ,
. down = e . down ,
. clicks = 1 ,
. pos = mouseNewPos
} ) ;
}
2026-03-31 13:17:58 +03:00
else if ( e . button = = main . conf . tabletOptions . rightClickButton ) {
2026-03-03 14:59:18 +03:00
main . input_mouse_button_callback ( {
2026-03-05 21:09:30 +03:00
. deviceType = MouseDeviceType : : PEN ,
2026-03-03 14:59:18 +03:00
. button = MouseButton : : RIGHT ,
. down = e . down ,
. clicks = 1 ,
. pos = mouseNewPos
} ) ;
}
main . input_pen_button_callback ( {
. button = e . button ,
. down = e . down ,
. pos = mouseNewPos
} ) ;
2026-03-05 12:36:34 +03:00
isTouchDevice = false ;
2026-03-03 14:59:18 +03:00
}
void InputManager : : backend_pen_button_up_update ( const SDL_PenButtonEvent & e ) {
Vector2f mouseNewPos = { e . x * main . window . density , e . y * main . window . density } ;
mouse . set_pos ( mouseNewPos ) ;
pen . previousPos = mouseNewPos ;
pen . buttons [ e . button ] . held = false ;
2026-03-03 16:01:21 +03:00
pen . isEraser = e . pen_state & SDL_PEN_INPUT_ERASER_TIP ;
2026-03-03 14:59:18 +03:00
2026-03-31 13:17:58 +03:00
if ( e . button = = main . conf . tabletOptions . middleClickButton ) {
2026-03-03 14:59:18 +03:00
main . input_mouse_button_callback ( {
2026-03-05 21:09:30 +03:00
. deviceType = MouseDeviceType : : PEN ,
2026-03-03 14:59:18 +03:00
. button = MouseButton : : MIDDLE ,
. down = e . down ,
. clicks = 1 ,
. pos = mouseNewPos
} ) ;
}
2026-03-31 13:17:58 +03:00
else if ( e . button = = main . conf . tabletOptions . rightClickButton ) {
2026-03-03 14:59:18 +03:00
main . input_mouse_button_callback ( {
2026-03-05 21:09:30 +03:00
. deviceType = MouseDeviceType : : PEN ,
2026-03-03 14:59:18 +03:00
. button = MouseButton : : RIGHT ,
. down = e . down ,
. clicks = 1 ,
. pos = mouseNewPos
} ) ;
}
main . input_pen_button_callback ( {
. button = e . button ,
. down = e . down ,
. pos = mouseNewPos
} ) ;
2026-03-05 12:36:34 +03:00
isTouchDevice = false ;
2026-03-03 14:59:18 +03:00
}
void InputManager : : backend_pen_touch_down_update ( const SDL_PenTouchEvent & e ) {
Vector2f mouseNewPos = { e . x * main . window . density , e . y * main . window . density } ;
mouse . set_pos ( mouseNewPos ) ;
pen . previousPos = mouseNewPos ;
pen . isDown = true ;
mouse . leftDown = true ;
if ( ( std : : chrono : : steady_clock : : now ( ) - pen . lastPenLeftClickTime ) > std : : chrono : : milliseconds ( 300 ) )
pen . leftClicksSaved = 0 ;
pen . leftClicksSaved + + ;
pen . lastPenLeftClickTime = std : : chrono : : steady_clock : : now ( ) ;
pen . isEraser = e . eraser ;
main . input_mouse_button_callback ( {
2026-03-05 21:09:30 +03:00
. deviceType = MouseDeviceType : : PEN ,
2026-03-03 14:59:18 +03:00
. button = MouseButton : : LEFT ,
. down = e . down ,
2026-03-07 14:12:11 +03:00
. clicks = pen . leftClicksSaved ,
2026-03-03 14:59:18 +03:00
. pos = mouseNewPos
} ) ;
main . input_pen_touch_callback ( {
. down = e . down ,
. eraser = e . eraser ,
. pos = mouseNewPos
} ) ;
2026-03-05 12:36:34 +03:00
isTouchDevice = false ;
2026-03-03 14:59:18 +03:00
}
void InputManager : : backend_pen_touch_up_update ( const SDL_PenTouchEvent & e ) {
Vector2f mouseNewPos = { e . x * main . window . density , e . y * main . window . density } ;
mouse . set_pos ( mouseNewPos ) ;
pen . previousPos = mouseNewPos ;
pen . isDown = false ;
mouse . leftDown = false ;
pen . isEraser = e . eraser ;
pen . pressure = 0.0 ;
main . input_mouse_button_callback ( {
2026-03-05 21:09:30 +03:00
. deviceType = MouseDeviceType : : PEN ,
2026-03-03 14:59:18 +03:00
. button = MouseButton : : LEFT ,
. down = e . down ,
. clicks = 0 ,
. pos = mouseNewPos
} ) ;
main . input_pen_touch_callback ( {
. down = e . down ,
. eraser = e . eraser ,
. pos = mouseNewPos
} ) ;
2026-03-05 12:36:34 +03:00
isTouchDevice = false ;
2026-03-03 14:59:18 +03:00
}
void InputManager : : backend_pen_motion_update ( const SDL_PenMotionEvent & e ) {
Vector2f mouseNewPos = { e . x * main . window . density , e . y * main . window . density } ;
mouse . set_pos ( mouseNewPos ) ;
Vector2f mouseRel = mouseNewPos - pen . previousPos ;
pen . previousPos = mouseNewPos ;
2026-03-03 16:01:21 +03:00
pen . isEraser = e . pen_state & SDL_PEN_INPUT_ERASER_TIP ;
2026-03-03 14:59:18 +03:00
main . input_mouse_motion_callback ( {
2026-03-05 21:09:30 +03:00
. deviceType = MouseDeviceType : : PEN ,
2026-03-03 14:59:18 +03:00
. pos = mouseNewPos ,
. move = mouseRel
} ) ;
main . input_pen_motion_callback ( {
. pos = mouseNewPos ,
. move = mouseRel
} ) ;
2026-03-05 12:36:34 +03:00
isTouchDevice = false ;
2026-03-03 14:59:18 +03:00
}
2026-03-03 16:01:21 +03:00
void InputManager : : backend_pen_axis_update ( const SDL_PenAxisEvent & e ) {
Vector2f mouseNewPos = { e . x * main . window . density , e . y * main . window . density } ;
mouse . set_pos ( mouseNewPos ) ;
pen . previousPos = mouseNewPos ;
pen . isEraser = e . pen_state & SDL_PEN_INPUT_ERASER_TIP ;
// For now, only pass pressure axis
if ( e . axis = = SDL_PEN_AXIS_PRESSURE ) {
2026-04-03 11:10:21 +03:00
pen . pressure = std : : clamp ( e . value , 0.0f , 1.0f ) ;
2026-03-03 16:01:21 +03:00
main . input_pen_axis_callback ( {
. pos = mouseNewPos ,
. axis = e . axis ,
2026-04-03 11:10:21 +03:00
. value = pen . pressure
2026-03-03 16:01:21 +03:00
} ) ;
}
}
2026-03-05 12:03:10 +03:00
std : : vector < Vector2f > InputManager : : get_multiple_finger_positions ( ) {
std : : vector < Vector2f > pos ;
for ( const SDL_TouchFingerEvent & fingerEvent : touch . fingers )
pos . emplace_back ( fingerEvent . x * main . window . size . x ( ) * main . window . density , fingerEvent . y * main . window . size . y ( ) * main . window . density ) ;
return pos ;
}
std : : vector < Vector2f > InputManager : : get_multiple_finger_motions ( ) {
std : : vector < Vector2f > pos ;
for ( const SDL_TouchFingerEvent & fingerEvent : touch . fingers )
pos . emplace_back ( fingerEvent . dx * main . window . size . x ( ) * main . window . density , fingerEvent . dy * main . window . size . y ( ) * main . window . density ) ;
return pos ;
}
2026-03-05 21:09:30 +03:00
void InputManager : : touch_finger_do_mouse_down ( ) {
auto & prevTouch = touch . fingers [ 0 ] ;
Vector2f mouseNewPos = { prevTouch . x * main . window . size . x ( ) * main . window . density , prevTouch . y * main . window . size . y ( ) * main . window . density } ;
mouse . set_pos ( mouseNewPos ) ;
mouse . leftDown = true ;
if ( ( std : : chrono : : steady_clock : : now ( ) - touch . lastLeftClickTime ) > std : : chrono : : milliseconds ( 500 ) )
touch . leftClicksSaved = 0 ;
touch . leftClicksSaved + + ;
touch . lastLeftClickTime = std : : chrono : : steady_clock : : now ( ) ;
main . input_mouse_button_callback ( {
. deviceType = MouseDeviceType : : TOUCH ,
. button = MouseButton : : LEFT ,
. down = true ,
2026-03-07 14:12:11 +03:00
. clicks = touch . leftClicksSaved ,
2026-03-05 21:09:30 +03:00
. pos = mouseNewPos
} ) ;
}
void InputManager : : touch_finger_do_mouse_up ( const SDL_TouchFingerEvent & e ) {
Vector2f mouseNewPos = { e . x * main . window . size . x ( ) * main . window . density , e . y * main . window . size . y ( ) * main . window . density } ;
mouse . set_pos ( mouseNewPos ) ;
mouse . leftDown = false ;
main . input_mouse_button_callback ( {
. deviceType = MouseDeviceType : : TOUCH ,
. button = MouseButton : : LEFT ,
. down = false ,
. clicks = 0 ,
. pos = mouseNewPos
} ) ;
}
void InputManager : : touch_finger_do_mouse_motion ( const SDL_TouchFingerEvent & e ) {
Vector2f mouseNewPos = { e . x * main . window . size . x ( ) * main . window . density , e . y * main . window . size . y ( ) * main . window . density } ;
mouse . set_pos ( mouseNewPos ) ;
Vector2f mouseRel = { e . dx * main . window . size . x ( ) * main . window . density , e . dy * main . window . size . y ( ) * main . window . density } ;
main . input_mouse_motion_callback ( {
. deviceType = MouseDeviceType : : TOUCH ,
. pos = mouseNewPos ,
. move = mouseRel
} ) ;
}
2026-03-05 12:03:10 +03:00
void InputManager : : backend_touch_finger_down_update ( const SDL_TouchFingerEvent & e ) {
touch . fingers . emplace_back ( e ) ;
2026-03-05 21:09:30 +03:00
auto fingerPosVec = get_multiple_finger_positions ( ) ;
2026-03-05 12:03:10 +03:00
switch ( touch . touchEventType ) {
case Touch : : NO_TOUCH_EVENT : {
if ( touch . fingers . size ( ) = = 2 ) {
touch . touchEventType = Touch : : TWO_FINGER_EVENT ;
main . input_multi_finger_touch_callback ( {
. down = true ,
2026-03-05 21:09:30 +03:00
. pos = fingerPosVec
2026-03-05 12:03:10 +03:00
} ) ;
}
break ;
}
case Touch : : ONE_FINGER_EVENT : {
2026-03-05 21:09:30 +03:00
if ( touch . fingers . size ( ) = = 2 ) {
touch_finger_do_mouse_up ( e ) ;
touch . touchEventType = Touch : : TWO_FINGER_EVENT ;
main . input_multi_finger_touch_callback ( {
. down = true ,
. pos = fingerPosVec
} ) ;
}
2026-03-05 12:03:10 +03:00
break ;
}
case Touch : : TWO_FINGER_EVENT : {
break ;
}
case Touch : : EVENT_DONE : {
if ( touch . fingers . size ( ) = = 1 )
touch . touchEventType = Touch : : NO_TOUCH_EVENT ;
break ;
}
}
2026-03-05 12:36:34 +03:00
2026-03-05 21:09:30 +03:00
if ( ( std : : chrono : : steady_clock : : now ( ) - touch . lastFingerTapTime ) > std : : chrono : : milliseconds ( 500 ) )
touch . fingerTapsSaved = 0 ;
touch . fingerTapsSaved + + ;
touch . lastFingerTapTime = std : : chrono : : steady_clock : : now ( ) ;
main . input_finger_touch_callback ( {
. fingerID = e . fingerID ,
. down = true ,
. pos = fingerPosVec . back ( ) ,
. fingerDownCount = touch . fingers . size ( ) ,
. fingerTapCount = touch . fingerTapsSaved
} ) ;
2026-03-05 12:36:34 +03:00
isTouchDevice = true ;
2026-03-05 12:03:10 +03:00
}
void InputManager : : backend_touch_finger_up_update ( const SDL_TouchFingerEvent & e ) {
switch ( touch . touchEventType ) {
case Touch : : NO_TOUCH_EVENT : {
2026-03-05 21:09:30 +03:00
touch_finger_do_mouse_down ( ) ;
touch_finger_do_mouse_up ( e ) ;
2026-03-05 12:03:10 +03:00
touch . touchEventType = Touch : : EVENT_DONE ;
break ;
}
case Touch : : ONE_FINGER_EVENT : {
2026-03-05 21:09:30 +03:00
touch_finger_do_mouse_up ( e ) ;
2026-03-05 12:03:10 +03:00
touch . touchEventType = Touch : : EVENT_DONE ;
break ;
}
case Touch : : TWO_FINGER_EVENT : {
main . input_multi_finger_touch_callback ( {
. down = false ,
. pos = get_multiple_finger_positions ( )
} ) ;
touch . touchEventType = Touch : : EVENT_DONE ;
break ;
}
case Touch : : EVENT_DONE : {
break ;
}
}
2026-03-05 21:09:30 +03:00
main . input_finger_touch_callback ( {
. fingerID = e . fingerID ,
. down = false ,
. pos = { e . x * main . window . size . x ( ) * main . window . density , e . y * main . window . size . y ( ) * main . window . density } ,
. fingerDownCount = touch . fingers . size ( ) ,
. fingerTapCount = 0
} ) ;
2026-03-05 12:03:10 +03:00
std : : erase_if ( touch . fingers , [ & e ] ( auto & tE ) {
return e . fingerID = = tE . fingerID ;
} ) ;
2026-03-05 12:36:34 +03:00
isTouchDevice = true ;
2026-03-05 12:03:10 +03:00
}
void InputManager : : backend_touch_finger_motion_update ( const SDL_TouchFingerEvent & e ) {
switch ( touch . touchEventType ) {
case Touch : : NO_TOUCH_EVENT : {
2026-03-05 21:09:30 +03:00
touch_finger_do_mouse_down ( ) ;
touch_finger_do_mouse_motion ( e ) ;
2026-03-05 12:03:10 +03:00
touch . touchEventType = Touch : : ONE_FINGER_EVENT ;
break ;
}
case Touch : : ONE_FINGER_EVENT : {
2026-03-05 21:09:30 +03:00
touch_finger_do_mouse_motion ( e ) ;
2026-03-05 12:03:10 +03:00
break ;
}
case Touch : : TWO_FINGER_EVENT : {
main . input_multi_finger_motion_callback ( {
. pos = get_multiple_finger_positions ( ) ,
. move = get_multiple_finger_motions ( )
} ) ;
break ;
}
case Touch : : EVENT_DONE : {
break ;
}
}
for ( auto & tE : touch . fingers ) {
if ( tE . fingerID = = e . fingerID ) {
tE = e ;
break ;
}
}
2026-03-05 12:36:34 +03:00
2026-03-05 21:09:30 +03:00
main . input_finger_motion_callback ( {
. fingerID = e . fingerID ,
. pos = { e . x * main . window . size . x ( ) * main . window . density , e . y * main . window . size . y ( ) * main . window . density } ,
. move = { e . dx * main . window . size . x ( ) * main . window . density , e . dy * main . window . size . y ( ) * main . window . density } ,
. fingerDownCount = touch . fingers . size ( )
} ) ;
2026-03-05 12:36:34 +03:00
isTouchDevice = true ;
2026-03-05 12:03:10 +03:00
}
2026-04-25 20:29:51 +03:00
void InputManager : : update_safe_area ( ) {
SDL_Rect safeArea ;
if ( SDL_GetWindowSafeArea ( main . window . sdlWindow , & safeArea ) )
main . window . safeArea = SCollision : : AABB < float > ( { safeArea . x , safeArea . y } , { safeArea . x + safeArea . w , safeArea . y + safeArea . h } ) ;
else
main . window . safeArea = SCollision : : AABB < float > ( { 0 , 0 } , { main . window . size . x ( ) , main . window . size . y ( ) } ) ;
}
void InputManager : : backend_window_resize_update ( ) {
update_safe_area ( ) ;
2026-04-07 20:21:41 +03:00
WindowResizeCallbackArgs w ;
w . density = main . window . density ;
2026-04-25 20:29:51 +03:00
w . size = main . window . size ;
w . safeArea = main . window . safeArea ;
2026-04-07 20:21:41 +03:00
main . input_window_resize_callback ( w ) ;
}
void InputManager : : backend_window_scale_update ( const SDL_WindowEvent & e ) {
WindowScaleCallbackArgs w ;
w . scale = main . window . scale ;
main . input_window_scale_callback ( w ) ;
}
2025-07-06 20:43:53 +03:00
void InputManager : : backend_key_down_update ( const SDL_KeyboardEvent & e ) {
auto kPress = e . key ;
auto kMod = e . mod ;
2026-04-27 17:20:16 +03:00
# ifdef __ANDROID__
if ( kPress = = SDLK_AC_BACK ) {
main . input_global_back_button_callback ( ) ;
return ;
}
# endif
2025-07-06 20:43:53 +03:00
if ( kPress ! = SDLK_LSHIFT & & kPress ! = SDLK_RSHIFT & & kPress ! = SDLK_LCTRL & & kPress ! = SDLK_RCTRL & &
kPress ! = SDLK_LGUI & & kPress ! = SDLK_RGUI & &
kPress ! = SDLK_LALT & & kPress ! = SDLK_RALT ) {
2026-04-02 19:18:39 +03:00
if ( main . input_keybind_callback ( { make_generic_key_mod ( kMod ) , kPress } ) )
return ;
2025-07-06 20:43:53 +03:00
}
2026-04-09 16:30:28 +03:00
bool acceptingTextInput = text . is_accepting_input ( ) ;
2025-07-06 20:43:53 +03:00
switch ( kPress ) {
case SDLK_UP :
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_GENERIC_UP , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_DOWN :
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_GENERIC_DOWN , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_LEFT :
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_GENERIC_LEFT , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_RIGHT :
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_GENERIC_RIGHT , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_BACKSPACE :
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_TEXT_BACKSPACE , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_DELETE :
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_TEXT_DELETE , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_HOME :
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_TEXT_HOME , acceptingTextInput ) ;
2025-10-16 21:22:08 +03:00
break ;
case SDLK_END :
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_TEXT_END , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_LSHIFT :
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_GENERIC_LSHIFT , acceptingTextInput ) ;
2025-08-05 19:04:47 +03:00
break ;
case SDLK_LALT :
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_GENERIC_LALT , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_LCTRL :
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_GENERIC_LCTRL , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
2025-10-16 21:22:08 +03:00
case SDLK_LMETA :
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_GENERIC_LMETA , acceptingTextInput ) ;
2025-10-16 21:22:08 +03:00
break ;
2025-07-06 20:43:53 +03:00
case SDLK_C :
// Use either Ctrl or Meta (command for Mac) keys. We do either instead of checking, since checking can get complicated on Emscripten
2026-04-03 23:02:33 +03:00
if ( ( kMod & SDL_KMOD_GUI ) | | ( kMod & SDL_KMOD_CTRL ) )
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_TEXT_COPY , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_X :
2026-04-03 23:02:33 +03:00
if ( ( kMod & SDL_KMOD_GUI ) | | ( kMod & SDL_KMOD_CTRL ) )
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_TEXT_CUT , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_V :
2026-04-03 23:02:33 +03:00
if ( ( kMod & SDL_KMOD_GUI ) | | ( kMod & SDL_KMOD_CTRL ) )
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_TEXT_PASTE , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_A :
2026-04-03 23:02:33 +03:00
if ( ( kMod & SDL_KMOD_GUI ) | | ( kMod & SDL_KMOD_CTRL ) )
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_TEXT_SELECTALL , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
2025-11-12 11:36:14 +03:00
case SDLK_Z :
2026-04-03 23:02:33 +03:00
if ( ( kMod & SDL_KMOD_GUI ) | | ( kMod & SDL_KMOD_CTRL ) )
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_TEXT_UNDO , acceptingTextInput ) ;
2025-11-12 11:36:14 +03:00
break ;
case SDLK_R :
2026-04-03 23:02:33 +03:00
if ( ( kMod & SDL_KMOD_GUI ) | | ( kMod & SDL_KMOD_CTRL ) )
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_TEXT_REDO , acceptingTextInput ) ;
2025-11-12 11:36:14 +03:00
break ;
2026-04-03 23:02:33 +03:00
case SDLK_RETURN :
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_GENERIC_ENTER , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
2026-04-03 23:02:33 +03:00
case SDLK_TAB :
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_TEXT_TAB , acceptingTextInput ) ;
2025-10-20 17:30:36 +03:00
break ;
2025-07-06 20:43:53 +03:00
case SDLK_ESCAPE :
2026-04-09 16:30:28 +03:00
set_key_down ( e , KEY_GENERIC_ESCAPE , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
default :
break ;
}
auto f = keyAssignments . find ( { make_generic_key_mod ( kMod ) , kPress } ) ;
2026-04-03 23:02:33 +03:00
if ( f ! = keyAssignments . end ( ) )
2026-04-09 16:30:28 +03:00
set_key_down ( e , f - > second , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
}
2026-04-11 16:36:24 +03:00
void InputManager : : call_paste ( CustomEvents : : PasteEvent : : DataType type , const InputManagerCallPasteInfo & info ) {
2026-04-03 23:02:33 +03:00
// Workaround for not being able to copy richtext to system clipboard, this should at least work within the application itself
2025-12-02 09:53:03 +03:00
# ifdef __EMSCRIPTEN__
2026-04-12 17:31:32 +03:00
switch ( type ) {
case CustomEvents : : PasteEvent : : DataType : : TEXT : {
struct PasteData {
bool allowRichText ;
InputManager * t ;
} ;
static PasteData pasteData ;
pasteData . allowRichText = info . allowRichText ;
pasteData . t = this ;
emscripten_browser_clipboard : : paste_async ( [ ] ( std : : string & & pasteData , void * callbackData ) {
PasteData * p = ( PasteData * ) callbackData ;
p - > t - > process_text_paste ( pasteData , p - > allowRichText ) ;
} , & pasteData ) ;
break ;
}
case CustomEvents : : PasteEvent : : DataType : : IMAGE : {
struct PasteData {
std : : optional < Vector2f > pos ;
InputManager * t ;
} ;
static PasteData pasteData ;
pasteData . pos = info . pastePosition ;
pasteData . t = this ;
emscripten_browser_clipboard : : paste_async_image ( [ ] ( std : : string_view pasteData , void * callbackData ) {
PasteData * p = ( PasteData * ) callbackData ;
p - > t - > process_image_paste ( pasteData , p - > pos ) ;
} , & pasteData ) ;
break ;
}
2026-04-11 16:36:24 +03:00
}
2025-12-02 09:53:03 +03:00
# else
2026-04-04 17:52:43 +03:00
switch ( type ) {
2026-04-11 16:36:24 +03:00
case CustomEvents : : PasteEvent : : DataType : : TEXT :
2026-04-04 21:31:18 +03:00
process_text_paste ( get_clipboard_str_SDL ( ) , info . allowRichText ) ;
2026-04-04 17:52:43 +03:00
break ;
2026-04-11 16:36:24 +03:00
case CustomEvents : : PasteEvent : : DataType : : IMAGE :
get_clipboard_image_data_SDL ( [ & ] ( std : : string_view pasteData ) {
process_image_paste ( pasteData , info . pastePosition ) ;
} ) ;
2026-04-04 17:52:43 +03:00
break ;
}
2025-12-02 09:53:03 +03:00
# endif
}
2026-04-04 21:31:18 +03:00
void InputManager : : process_text_paste ( const std : : string & pasteStr , bool allowRichText ) {
2026-04-11 16:36:24 +03:00
CustomEvents : : emit_event < CustomEvents : : PasteEvent > ( {
. type = CustomEvents : : PasteEvent : : DataType : : TEXT ,
2026-04-04 17:52:43 +03:00
. data = pasteStr ,
2026-04-04 21:31:18 +03:00
. richText = allowRichText & & lastCopiedRichText . has_value ( ) & & lastCopiedRichText . value ( ) . get_plain_text ( ) = = remove_carriage_returns_from_str ( pasteStr ) ? lastCopiedRichText : std : : nullopt
2026-04-04 17:52:43 +03:00
} ) ;
2025-11-11 22:21:25 +03:00
}
2026-04-11 16:36:24 +03:00
void InputManager : : process_image_paste ( std : : string_view pasteData , const std : : optional < Vector2f > & pastePos ) {
CustomEvents : : emit_event < CustomEvents : : PasteEvent > ( {
. type = CustomEvents : : PasteEvent : : DataType : : IMAGE ,
. data = std : : string ( pasteData ) ,
. mousePos = pastePos
} ) ;
}
2025-10-16 21:22:08 +03:00
bool InputManager : : ctrl_or_meta_held ( ) {
return key ( KEY_GENERIC_LCTRL ) . held | | key ( KEY_GENERIC_LMETA ) . held ;
}
2025-07-06 20:43:53 +03:00
void InputManager : : backend_key_up_update ( const SDL_KeyboardEvent & e ) {
auto kPress = e . key ;
2026-04-09 16:30:28 +03:00
bool acceptingTextInput = text . is_accepting_input ( ) ;
2025-07-06 20:43:53 +03:00
switch ( kPress ) {
case SDLK_UP :
2026-04-09 16:30:28 +03:00
set_key_up ( e , KEY_GENERIC_UP , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_DOWN :
2026-04-09 16:30:28 +03:00
set_key_up ( e , KEY_GENERIC_DOWN , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_LEFT :
2026-04-09 16:30:28 +03:00
set_key_up ( e , KEY_GENERIC_LEFT , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_RIGHT :
2026-04-09 16:30:28 +03:00
set_key_up ( e , KEY_GENERIC_RIGHT , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_BACKSPACE :
2026-04-09 16:30:28 +03:00
set_key_up ( e , KEY_TEXT_BACKSPACE , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_DELETE :
2026-04-09 16:30:28 +03:00
set_key_up ( e , KEY_TEXT_DELETE , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_HOME :
2026-04-09 16:30:28 +03:00
set_key_up ( e , KEY_TEXT_HOME , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
2025-10-16 21:22:08 +03:00
case SDLK_END :
2026-04-09 16:30:28 +03:00
set_key_up ( e , KEY_TEXT_END , acceptingTextInput ) ;
2025-10-16 21:22:08 +03:00
break ;
2025-07-06 20:43:53 +03:00
case SDLK_LSHIFT :
2026-04-09 16:30:28 +03:00
set_key_up ( e , KEY_GENERIC_LSHIFT , acceptingTextInput ) ;
2025-08-05 19:04:47 +03:00
break ;
case SDLK_LALT :
2026-04-09 16:30:28 +03:00
set_key_up ( e , KEY_GENERIC_LALT , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_LCTRL :
2026-04-09 16:30:28 +03:00
set_key_up ( e , KEY_GENERIC_LCTRL , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_C :
2026-04-09 16:30:28 +03:00
set_key_up ( e , KEY_TEXT_COPY , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_X :
2026-04-09 16:30:28 +03:00
set_key_up ( e , KEY_TEXT_CUT , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_V :
2026-04-09 16:30:28 +03:00
set_key_up ( e , KEY_TEXT_PASTE , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDLK_A :
2026-04-09 16:30:28 +03:00
set_key_up ( e , KEY_TEXT_SELECTALL , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
2025-11-12 11:36:14 +03:00
case SDLK_Z :
2026-04-09 16:30:28 +03:00
set_key_up ( e , KEY_TEXT_UNDO , acceptingTextInput ) ;
2025-11-12 11:36:14 +03:00
break ;
case SDLK_R :
2026-04-09 16:30:28 +03:00
set_key_up ( e , KEY_TEXT_REDO , acceptingTextInput ) ;
2025-11-12 11:36:14 +03:00
break ;
2025-07-06 20:43:53 +03:00
case SDLK_RETURN :
2026-04-09 16:30:28 +03:00
set_key_up ( e , KEY_GENERIC_ENTER , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
2025-10-20 17:30:36 +03:00
case SDLK_TAB :
2026-04-09 16:30:28 +03:00
set_key_up ( e , KEY_TEXT_TAB , acceptingTextInput ) ;
2025-10-20 17:30:36 +03:00
break ;
2025-07-06 20:43:53 +03:00
case SDLK_ESCAPE :
2026-04-09 16:30:28 +03:00
set_key_up ( e , KEY_GENERIC_ESCAPE , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
break ;
default :
break ;
}
2026-04-03 23:02:33 +03:00
for ( auto & p : keyAssignments )
if ( p . first . y ( ) = = kPress ) // Dont try matching modifiers when setting key to go up
2026-04-09 16:30:28 +03:00
set_key_up ( e , p . second , acceptingTextInput ) ;
2025-07-06 20:43:53 +03:00
}
void InputManager : : Mouse : : set_pos ( const Vector2f & newPos ) {
pos = newPos ;
}
2026-03-05 12:03:10 +03:00
void InputManager : : update ( ) {
2026-03-05 21:09:30 +03:00
if ( touch . touchEventType = = Touch : : NO_TOUCH_EVENT & & ( SDL_GetTicksNS ( ) - touch . fingers [ 0 ] . timestamp ) > ( 200 * 1000000 ) ) { // 200ms to determine whether touch is with a single finger
touch_finger_do_mouse_down ( ) ;
2026-03-05 12:03:10 +03:00
touch . touchEventType = Touch : : ONE_FINGER_EVENT ;
}
2026-03-05 23:34:30 +03:00
if ( ! text . textBoxes . empty ( ) ) {
2026-03-06 14:12:13 +03:00
//auto& textbox = text.textBoxes.front();
//if(textbox.rect) {
// SDL_Rect r = textbox.rect->get_sdl_rect();
// //SDL_SetTextInputArea(main.window.sdlWindow, &r, textbox.cursor->pos.fTextByteIndex);
//}
//else
// //SDL_SetTextInputArea(main.window.sdlWindow, nullptr, textbox.cursor->pos.fTextByteIndex);
2026-03-05 23:34:30 +03:00
}
2026-03-05 12:03:10 +03:00
}
2025-07-06 20:43:53 +03:00
void InputManager : : frame_reset ( const Vector2i & windowSize ) {
cursorIcon = SystemCursorType : : DEFAULT ;
hideCursor = false ;
}