diff --git a/developer/flags.sh b/developer/flags.sh new file mode 100644 index 00000000..486f7756 --- /dev/null +++ b/developer/flags.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -e + +function compiler() { + make clean + make RENDER_API=$1 WINDOW_API=$2 AUDIO_API=$3 CONTROLLER_API=$4 + mv ./build/us_pc/sm64.us.f3dex2e.exe ./build/us_pc/$5.exe +} + +compiler GL_LEGACY SDL1 SDL1 SDL1 legacy_1 +compiler GL SDL1 SDL1 SDL1 gl_1 +compiler D3D11 DXGI SDL1 SDL1 d3d11_1 +compiler D3D12 DXGI SDL1 SDL1 d3d12_1 + +compiler GL_LEGACY SDL2 SDL2 SDL2 legacy_2 +compiler GL SDL1 SDL2 SDL2 gl_2 +compiler D3D11 DXGI SDL2 SDL2 d3d11_2 +compiler D3D12 DXGI SDL2 SDL2 d3d12_2 diff --git a/src/game/chat.c b/src/game/chat.c index 1f346237..c799bd0b 100644 --- a/src/game/chat.c +++ b/src/game/chat.c @@ -127,33 +127,17 @@ void chat_add_message(char* ascii, enum ChatMessageType chatMessageType) { static void chat_stop_input(void) { sInChatInput = FALSE; - keyboard_stop_text_input(); } static void chat_send_input(void) { - sInChatInput = FALSE; - keyboard_stop_text_input(); - if (strlen(gTextInput) == 0) { return; } - chat_add_message(gTextInput, CMT_LOCAL); - // our message has the same color as our shirt - network_send_chat(gTextInput, get_player_color(gNetworkPlayerLocal->globalIndex, 0)); } void chat_start_input(void) { - sInChatInput = TRUE; - keyboard_start_text_input(TIM_SINGLE_LINE, CHAT_DIALOG_MAX - 3, chat_stop_input, chat_send_input); } void render_chat(void) { u8 count = 0; if (sInChatInput) { - struct ChatMessage inputMessage = { 0 }; - inputMessage.type = CMT_INPUT; - inputMessage.dialog[0] = 0xFD; - inputMessage.dialog[1] = 0x9E; - str_ascii_to_dialog(gTextInput, &inputMessage.dialog[2], MIN(strlen(gTextInput), CHAT_DIALOG_MAX - 3)); - inputMessage.life = CHAT_LIFE_MAX; - render_chat_message(&inputMessage, count++); } u8 index = onMessageIndex; diff --git a/src/goddard/renderer.c b/src/goddard/renderer.c index 3d2a8356..4732abb3 100644 --- a/src/goddard/renderer.c +++ b/src/goddard/renderer.c @@ -227,7 +227,7 @@ static Gfx gd_texture1_dummy_aligner1[] = { // @ 801A8728 gsSPEndDisplayList(), }; -ALIGNED8 static u8 gd_texture_hand_open[] = { +ALIGNED8 u8 gd_texture_hand_open[] = { #include "textures/intro_raw/hand_open.rgba16.inc.c" }; @@ -235,7 +235,7 @@ static Gfx gd_texture2_dummy_aligner1[] = { gsSPEndDisplayList() }; -ALIGNED8 static u8 gd_texture_hand_closed[] = { +ALIGNED8 u8 gd_texture_hand_closed[] = { #include "textures/intro_raw/hand_closed.rgba16.inc.c" }; diff --git a/src/menu/custom_menu.c b/src/menu/custom_menu.c index 31774403..830fa8d6 100644 --- a/src/menu/custom_menu.c +++ b/src/menu/custom_menu.c @@ -178,75 +178,15 @@ static void connect_menu_draw_strings(void) { print_generic_ascii_string(30, 175, "Type in or paste the host's IP."); print_generic_ascii_string(30, 160, "Note - the host must forward a port on their router."); - - if (keyboard_in_text_input()) { - if (strlen(gTextInput) >= 7) { - print_generic_ascii_string(30, 100, "Press (ENTER) to connect."); - } else { - print_generic_ascii_string(30, 100, "Press (ESC) to cancel."); - } - } - - gDPSetEnvColor(gDisplayListHead++, 130, 222, 140, gMenuStringAlpha); - print_generic_ascii_string(30, 130, gTextInput); } static void connect_menu_on_connection_attempt(void) { - play_sound(SOUND_GENERAL_COIN, gDefaultSoundArgs); - - keyboard_stop_text_input(); - if (gNetworkType != NT_NONE) { return; } - - char delims[2] = " "; - - // copy input - char buffer[MAX_TEXT_INPUT] = { 0 }; - strncpy(buffer, gTextInput, MAX_TEXT_INPUT); - char* text = buffer; - - // trim whitespace - while (*text == ' ') { text++; } - - // grab IP - char* ip = strtok(text, delims); - if (ip == NULL) { custom_menu_close(); return; } - strncpy(configJoinIp, ip, MAX_CONFIG_STRING); - - // grab port - char* port = strtok(NULL, delims); - if (port != NULL) { - unsigned int intPort = atoi(port); - if (intPort == 0 || intPort > 65535) { configJoinPort = DEFAULT_PORT; custom_menu_close(); return; } - configJoinPort = intPort; - } - else { - configJoinPort = DEFAULT_PORT; - } - - network_set_system(NS_SOCKET); - network_init(NT_CLIENT); - } static void connect_menu_on_click(void) { - sConnectionJoinError[0] = '\0'; - - keyboard_start_text_input(TIM_IP, MAX_TEXT_INPUT, custom_menu_close, connect_menu_on_connection_attempt); - - // fill in our last attempt - if (configJoinPort == 0 || configJoinPort > 65535) { configJoinPort = DEFAULT_PORT; } - - // only print custom port - if (configJoinPort == DEFAULT_PORT) { - sprintf(gTextInput, "%s", configJoinIp); - } - else { - sprintf(gTextInput, "%s %d", configJoinIp, configJoinPort); - } } static void connect_menu_on_close(void) { - keyboard_stop_text_input(); network_shutdown(); } diff --git a/src/pc/controller/controller_keyboard.c b/src/pc/controller/controller_keyboard.c index d964d7af..a9074ff0 100644 --- a/src/pc/controller/controller_keyboard.c +++ b/src/pc/controller/controller_keyboard.c @@ -20,19 +20,6 @@ #include "game/chat.h" #include "src/pc/djui/djui.h" -// TODO: use some common lookup header -#define SCANCODE_BACKSPACE 0x0E -#define SCANCODE_ESCAPE 0x01 -#define SCANCODE_ENTER 0x1C -#define SCANCODE_V 0x2F -#define SCANCODE_INSERT 0x152 -#define SCANCODE_CTRL1 0x1D -#define SCANCODE_CTRL2 0x11D -#define SCANCODE_SHIFT1 0x2A -#define SCANCODE_SHIFT2 0x36 -#define SCANCODE_ALT1 0x38 -#define SCANCODE_ALT2 0x138 - static int keyboard_buttons_down; #define MAX_KEYBINDS 64 @@ -41,16 +28,6 @@ static int num_keybinds = 0; static u32 keyboard_lastkey = VK_INVALID; -char gTextInput[MAX_TEXT_INPUT]; -static u8 sInTextInput = false; -static u8 sMaxTextInput = 0; -static clock_t sIgnoreTextInput = 0; - -u8 held_ctrl, held_shift, held_alt; -static enum TextInputMode sTextInputMode; -void (*textInputOnEscape)(void) = NULL; -void (*textInputOnEnter)(void) = NULL; - static int keyboard_map_scancode(int scancode) { int ret = 0; for (int i = 0; i < num_keybinds; i++) { @@ -61,70 +38,16 @@ static int keyboard_map_scancode(int scancode) { return ret; } -static void keyboard_alter_modifier(int scancode, bool down) { - if (down) { - switch (scancode) { - case SCANCODE_CTRL1: held_ctrl |= (1 << 0); break; - case SCANCODE_CTRL2: held_ctrl |= (1 << 1); break; - case SCANCODE_SHIFT1: held_shift |= (1 << 0); break; - case SCANCODE_SHIFT2: held_shift |= (1 << 1); break; - case SCANCODE_ALT1: held_alt |= (1 << 0); break; - case SCANCODE_ALT2: held_alt |= (1 << 1); break; - } - } else { - switch (scancode) { - case SCANCODE_CTRL1: held_ctrl &= ~(1 << 0); break; - case SCANCODE_CTRL2: held_ctrl &= ~(1 << 1); break; - case SCANCODE_SHIFT1: held_shift &= ~(1 << 0); break; - case SCANCODE_SHIFT2: held_shift &= ~(1 << 1); break; - case SCANCODE_ALT1: held_alt &= ~(1 << 0); break; - case SCANCODE_ALT2: held_alt &= ~(1 << 1); break; - } - } -} - bool keyboard_on_key_down(int scancode) { - // alter the held value of modifier keys - keyboard_alter_modifier(scancode, true); - - djui_interactable_on_key_down(scancode); - #ifdef DEBUG - if (!sInTextInput) { - debug_keyboard_on_key_down(scancode); - } + debug_keyboard_on_key_down(scancode); #endif - if (sInTextInput) { - // perform text-input-specific actions - switch (scancode & 0xFF) { - case SCANCODE_BACKSPACE: - gTextInput[max(strlen(gTextInput) - 1, 0)] = '\0'; - break; - case SCANCODE_ESCAPE: - if (textInputOnEscape != NULL) { textInputOnEscape(); } - break; - case SCANCODE_ENTER: - if (textInputOnEnter != NULL) { textInputOnEnter(); } - break; - case SCANCODE_V: - if (held_ctrl) { keyboard_on_text_input(wm_api->get_clipboard_text()); } - break; - case SCANCODE_INSERT: - if (held_shift) { keyboard_on_text_input(wm_api->get_clipboard_text()); } - break; - } - // ignore any normal key down event if we're in text-input mode + // see if interactable captures this scancode + if (djui_interactable_on_key_down(scancode)) { return FALSE; } - if (!held_alt && (scancode == (int)configKeyChat[0])) { - if (sSelectedFileNum != 0) { - sIgnoreTextInput = clock() + CLOCKS_PER_SEC * 0.01f; - chat_start_input(); - } - } - int mapped = keyboard_map_scancode(scancode); keyboard_buttons_down |= mapped; keyboard_lastkey = scancode; @@ -132,16 +55,8 @@ bool keyboard_on_key_down(int scancode) { } bool keyboard_on_key_up(int scancode) { - // alter the held value of modifier keys - keyboard_alter_modifier(scancode, false); - djui_interactable_on_key_up(scancode); - if (sInTextInput) { - // ignore any key up event if we're in text-input mode - return FALSE; - } - int mapped = keyboard_map_scancode(scancode); keyboard_buttons_down &= ~mapped; if (keyboard_lastkey == (u32) scancode) @@ -153,87 +68,8 @@ void keyboard_on_all_keys_up(void) { keyboard_buttons_down = 0; } -char* keyboard_start_text_input(enum TextInputMode inInputMode, u8 inMaxTextInput, void (*onEscape)(void), void (*onEnter)(void)) { - // set text-input events - textInputOnEscape = onEscape; - textInputOnEnter = onEnter; - sMaxTextInput = inMaxTextInput; - - // clear buffer - for (int i = 0; i < MAX_TEXT_INPUT; i++) { gTextInput[i] = '\0'; } - - // clear held-value for modifiers - held_ctrl = 0; - held_shift = 0; - held_alt = 0; - - // start allowing text input - wm_api->start_text_input(); - sTextInputMode = inInputMode; - sInTextInput = true; -} - -void keyboard_stop_text_input(void) { - // stop allowing text input - sInTextInput = false; - wm_api->stop_text_input(); -} - -bool keyboard_in_text_input(void) { return sInTextInput; } - -static bool keyboard_allow_character_input(char c) { - switch (sTextInputMode) { - case TIM_IP: - // IP only allows numbers, periods, and spaces - return (c >= '0' && c <= '9') - || (c == '.') - || (c == ' '); - - case TIM_MULTI_LINE: - // multi-line allows new-line character - if (c == '\n') { return true; } - // intentional fall-through - - case TIM_SINGLE_LINE: - // allow all characters that we can display in-game - return (c >= '0' && c <= '9') - || (c >= 'a' && c <= 'z') - || (c >= 'A' && c <= 'Z') - || (c == '\'') || (c == '.') - || (c == ',') || (c == '-') - || (c == '(') || (c == ')') - || (c == '&') || (c == '!') - || (c == '%') || (c == '?') - || (c == '"') || (c == '~') - || (c == '*') || (c == ' '); - } - - return false; -} - void keyboard_on_text_input(char* text) { - if (sIgnoreTextInput != 0 && clock() <= sIgnoreTextInput) { - sIgnoreTextInput = 0; - return; - } - - if (!sInTextInput) { return; } - // sanity check input - if (text == NULL) { return; } - - int i = strlen(gTextInput); - while (*text != '\0') { - // make sure we don't overrun the buffer - if (i >= MAX_TEXT_INPUT) { break; } - if (i >= sMaxTextInput) { break; } - - // copy over character if we're allowed to input it - if (keyboard_allow_character_input(*text)) { - gTextInput[i++] = *text; - } - - text++; - } + djui_interactable_on_text_input(text); } static void keyboard_add_binds(int mask, unsigned int *scancode) { diff --git a/src/pc/controller/controller_keyboard.h b/src/pc/controller/controller_keyboard.h index 6d5123ea..b7edea01 100644 --- a/src/pc/controller/controller_keyboard.h +++ b/src/pc/controller/controller_keyboard.h @@ -6,26 +6,32 @@ # define VK_BASE_KEYBOARD 0x0000 +#define SCANCODE_ESCAPE 1 +#define SCANCODE_BACKSPACE 14 +#define SCANCODE_ENTER 28 +#define SCANCODE_CONTROL_LEFT 29 +#define SCANCODE_SHIFT_LEFT 42 +#define SCANCODE_A 30 +#define SCANCODE_X 45 +#define SCANCODE_C 46 +#define SCANCODE_V 47 +#define SCANCODE_SHIFT_RIGHT 54 +#define SCANCODE_CONTROL_RIGHT 285 +#define SCANCODE_HOME 327 +#define SCANCODE_LEFT 331 +#define SCANCODE_RIGHT 333 +#define SCANCODE_END 335 +#define SCANCODE_INSERT 338 +#define SCANCODE_DELETE 339 + #ifdef __cplusplus extern "C" { #endif -#define MAX_TEXT_INPUT 255 -extern char gTextInput[]; - -enum TextInputMode { - TIM_IP, - TIM_MULTI_LINE, - TIM_SINGLE_LINE, -}; - bool keyboard_on_key_down(int scancode); bool keyboard_on_key_up(int scancode); void keyboard_on_all_keys_up(void); void keyboard_on_text_input(char* text); -char* keyboard_start_text_input(enum TextInputMode, u8 inMaxTextInput, void (*onEscape)(void), void (*onEnter)(void)); -void keyboard_stop_text_input(void); -bool keyboard_in_text_input(void); #ifdef __cplusplus } diff --git a/src/pc/controller/controller_sdl1.c b/src/pc/controller/controller_sdl1.c index d944b177..d913f147 100644 --- a/src/pc/controller/controller_sdl1.c +++ b/src/pc/controller/controller_sdl1.c @@ -159,9 +159,11 @@ static void controller_sdl_read(OSContPad *pad) { u32 mouse = SDL_GetRelativeMouseState(&mouse_x, &mouse_y); - for (u32 i = 0; i < num_mouse_binds; ++i) - if (mouse & SDL_BUTTON(mouse_binds[i][0])) - pad->button |= mouse_binds[i][1]; + if (!gInteractableOverridePad) { + for (u32 i = 0; i < num_mouse_binds; ++i) + if (mouse & SDL_BUTTON(mouse_binds[i][0])) + pad->button |= mouse_binds[i][1]; + } // remember buttons that changed from 0 to 1 last_mouse = (mouse_buttons ^ mouse) & mouse; diff --git a/src/pc/controller/controller_sdl2.c b/src/pc/controller/controller_sdl2.c index 8bdf9648..67a8f966 100644 --- a/src/pc/controller/controller_sdl2.c +++ b/src/pc/controller/controller_sdl2.c @@ -20,6 +20,8 @@ #include "game/level_update.h" +#include "pc/djui/djui.h" + // mouse buttons are also in the controller namespace (why), just offset 0x100 #define VK_OFS_SDL_MOUSE 0x0100 #define VK_BASE_SDL_MOUSE (VK_BASE_SDL_GAMEPAD + VK_OFS_SDL_MOUSE) @@ -163,10 +165,11 @@ static void controller_sdl_read(OSContPad *pad) { u32 mouse = SDL_GetRelativeMouseState(&mouse_x, &mouse_y); - for (u32 i = 0; i < num_mouse_binds; ++i) - if (mouse & SDL_BUTTON(mouse_binds[i][0])) - pad->button |= mouse_binds[i][1]; - + if (!gInteractableOverridePad) { + for (u32 i = 0; i < num_mouse_binds; ++i) + if (mouse & SDL_BUTTON(mouse_binds[i][0])) + pad->button |= mouse_binds[i][1]; + } // remember buttons that changed from 0 to 1 last_mouse = (mouse_buttons ^ mouse) & mouse; mouse_buttons = mouse; diff --git a/src/pc/djui/djui_checkbox.c b/src/pc/djui/djui_checkbox.c index 84bc2d2d..9ec8a94c 100644 --- a/src/pc/djui/djui_checkbox.c +++ b/src/pc/djui/djui_checkbox.c @@ -37,14 +37,6 @@ static void djui_checkbox_on_cursor_down_end(struct DjuiBase* base) { djui_checkbox_set_default_style(base); } -static void djui_checkbox_on_focus_begin(struct DjuiBase* base) { - struct DjuiCheckbox* checkbox = (struct DjuiCheckbox*)base; - djui_base_set_border_color(&checkbox->rect->base, 20, 170, 255, 255); - djui_base_set_color(&checkbox->rect->base, 255, 255, 255, 32); - djui_base_set_color(&checkbox->text->base, 229, 241, 251, 255); - djui_base_set_color(&checkbox->rectValue->base, 255, 255, 255, 255); -} - static void djui_checkbox_destroy(struct DjuiBase* base) { struct DjuiCheckbox* checkbox = (struct DjuiCheckbox*)base; free(checkbox); diff --git a/src/pc/djui/djui_cursor.c b/src/pc/djui/djui_cursor.c index 28fa2f31..6acf0704 100644 --- a/src/pc/djui/djui_cursor.c +++ b/src/pc/djui/djui_cursor.c @@ -1,15 +1,9 @@ #include "djui.h" +#include "pc/controller/controller_mouse.h" +#include "pc/controller/controller_sdl.h" -#include "src/pc/controller/controller_sdl.h" -#include "src/pc/controller/controller_mouse.h" - -ALIGNED8 static u8 texture_hand_open[] = { -#include "textures/intro_raw/hand_open.rgba16.inc.c" -}; - -ALIGNED8 static u8 texture_hand_closed[] = { -#include "textures/intro_raw/hand_closed.rgba16.inc.c" -}; +extern ALIGNED8 u8 gd_texture_hand_open[]; +extern ALIGNED8 u8 gd_texture_hand_closed[]; static struct DjuiImage* sMouseCursor = NULL; @@ -22,7 +16,11 @@ f32 gCursorX = 0; f32 gCursorY = 0; void djui_cursor_set_visible(bool visible) { - djui_base_set_visible(&sMouseCursor->base, visible); + if (sMouseCursor) { + djui_base_set_visible(&sMouseCursor->base, visible); + } + sSavedMouseX = mouse_window_x; + sSavedMouseY = mouse_window_y; } bool djui_cursor_inside_base(struct DjuiBase* base) { @@ -42,11 +40,7 @@ static void djui_cursor_base_hover_location(struct DjuiBase* base, f32* x, f32* void djui_cursor_input_controlled_center(struct DjuiBase* base) { if (!sCursorMouseControlled) { sInputControlledBase = base; - if (sMouseCursor != NULL) { - djui_base_set_visible(&sMouseCursor->base, (base != NULL)); - } - sSavedMouseX = mouse_window_x; - sSavedMouseY = mouse_window_y; + djui_cursor_set_visible(base != NULL); } } @@ -109,12 +103,11 @@ void djui_cursor_update(void) { controller_sdl_read_mouse_window(); // check if mouse is in control again - if (!sCursorMouseControlled) { + if (!sCursorMouseControlled || (sMouseCursor && !sMouseCursor->base.visible)) { f32 dist = sqrtf(powf(mouse_window_x - sSavedMouseX, 2) + powf(mouse_window_y - sSavedMouseY, 2)); if (dist > 5) { sCursorMouseControlled = true; - djui_interactable_set_input_focus(NULL); - djui_base_set_visible(&sMouseCursor->base, true); + djui_cursor_set_visible(true); } } @@ -131,16 +124,16 @@ void djui_cursor_update(void) { // set cursor sprite if ((gInteractablePad.button & PAD_BUTTON_A) || (mouse_window_buttons & MOUSE_BUTTON_1)) { - djui_image_set_image(sMouseCursor, texture_hand_closed, 32, 32, 16); + djui_image_set_image(sMouseCursor, gd_texture_hand_closed, 32, 32, 16); } else { - djui_image_set_image(sMouseCursor, texture_hand_open, 32, 32, 16); + djui_image_set_image(sMouseCursor, gd_texture_hand_open, 32, 32, 16); } #endif djui_base_render(&sMouseCursor->base); } void djui_cursor_create(void) { - sMouseCursor = djui_image_create(NULL, texture_hand_open, 32, 32, 16); + sMouseCursor = djui_image_create(NULL, gd_texture_hand_open, 32, 32, 16); djui_base_set_location(&sMouseCursor->base, 0, 0); djui_base_set_size(&sMouseCursor->base, 64, 64); } \ No newline at end of file diff --git a/src/pc/djui/djui_inputbox.c b/src/pc/djui/djui_inputbox.c index c4008f60..96603794 100644 --- a/src/pc/djui/djui_inputbox.c +++ b/src/pc/djui/djui_inputbox.c @@ -1,15 +1,24 @@ #include #include #include "djui.h" +#include "pc/gfx/gfx_window_manager_api.h" +#include "pc/pc_main.h" #include "game/segment2.h" +#include "pc/controller/controller_keyboard.h" #define DJUI_INPUTBOX_YOFF (-2) #define DJUI_INPUTBOX_MAX_BLINK 50 #define DJUI_INPUTBOX_MID_BLINK (DJUI_INPUTBOX_MAX_BLINK / 2) #define DJUI_INPUTBOX_CURSOR_WIDTH (2.0f / 32.0f) +static u8 sHeldShift = 0; +static u8 sHeldControl = 0; static u8 sCursorBlink = 0; +void djui_inputbox_hook_enter_press(struct DjuiInputbox* inputbox, void (*on_enter_press)(void)) { + inputbox->on_enter_press = on_enter_press; +} + static void djui_inputbox_set_default_style(struct DjuiBase* base) { struct DjuiInputbox* inputbox = (struct DjuiInputbox*)base; djui_base_set_border_color(base, 150, 150, 150, 255); @@ -51,6 +60,7 @@ static void djui_inputbox_on_cursor_down_begin(struct DjuiBase* base, bool input inputbox->selection[0] = index; inputbox->selection[1] = index; sCursorBlink = 0; + djui_interactable_set_input_focus(base); } static void djui_inputbox_on_cursor_down(struct DjuiBase* base) { @@ -63,6 +73,236 @@ static void djui_inputbox_on_cursor_down_end(struct DjuiBase* base) { djui_inputbox_set_default_style(base); } +static u16 djui_inputbox_jump_word_left(char* msg, u16 len, u16 i) { + if (i == 0) { return i; } + + s32 lastI = i; + bool seenNonSpace = false; + while (true) { + if (msg[i] == ' ' && seenNonSpace) { i = lastI; break; } + lastI = i; + i--; + if (i <= 0) { i = 0; break; } + if (msg[i] != ' ') { seenNonSpace = true; } + } + + return i; +} + +static u16 djui_inputbox_jump_word_right(char *msg, u16 len, u16 i) { + if (i >= len) { return len; } + + bool seenSpace = false; + while (true) { + i++; + if (i >= len) { i = len; break; } + if (msg[i] != ' ' && seenSpace) { break; } + if (msg[i] == ' ') { seenSpace = true; } + }; + + return i; +} + +static void djui_inputbox_delete_selection(struct DjuiInputbox *inputbox) { + u16 *sel = inputbox->selection; + char *msg = inputbox->buffer; + u16 len = strlen(msg); + + if (sel[0] != sel[1]) { + u16 s1 = fmin(sel[0], sel[1]); + u16 s2 = fmax(sel[0], sel[1]); + memmove(&msg[s1], &msg[s2], (len + 1) - s2); + sel[0] = s1; + sel[1] = s1; + } +} + +static bool djui_inputbox_on_key_down(struct DjuiBase *base, int scancode) { + struct DjuiInputbox *inputbox = (struct DjuiInputbox *) base; + u16 *sel = inputbox->selection; + char *msg = inputbox->buffer; + u16 len = strlen(msg); + u16 s1 = fmin(sel[0], sel[1]); + u16 s2 = fmax(sel[0], sel[1]); + + switch (scancode) { + case SCANCODE_CONTROL_LEFT: sHeldControl |= (1 << 0); return true; + case SCANCODE_CONTROL_RIGHT: sHeldControl |= (1 << 1); return true; + case SCANCODE_SHIFT_LEFT: sHeldShift |= (1 << 0); return true; + case SCANCODE_SHIFT_RIGHT: sHeldShift |= (1 << 1); return true; + } + + if (scancode == SCANCODE_LEFT) { + if (sHeldControl) { + sel[0] = djui_inputbox_jump_word_left(msg, len, sel[0]); + } else if (sel[0] > 0) { + sel[0]--; + } + if (!sHeldShift) { sel[1] = sel[0]; } + sCursorBlink = 0; + return true; + } + + if (scancode == SCANCODE_RIGHT) { + if (sHeldControl) { + sel[0] = djui_inputbox_jump_word_right(msg, len, sel[0]); + } else if (sel[0] < len) { + sel[0]++; + } + if (!sHeldShift) { sel[1] = sel[0]; } + sCursorBlink = 0; + return true; + } + + if (scancode == SCANCODE_HOME) { + sel[0] = 0; + if (!sHeldShift) { sel[1] = sel[0]; } + sCursorBlink = 0; + return true; + } + + if (scancode == SCANCODE_END) { + sel[0] = len; + if (!sHeldShift) { sel[1] = sel[0]; } + sCursorBlink = 0; + return true; + } + + if (scancode == SCANCODE_BACKSPACE) { + if (sel[0] == sel[1]) { + if (sHeldControl) { + sel[0] = djui_inputbox_jump_word_left(msg, len, sel[0]); + } else if (sel[0] > 0) { + sel[0]--; + } + } + if (sel[0] != sel[1]) { + djui_inputbox_delete_selection(inputbox); + } + sCursorBlink = 0; + return true; + } + + if (scancode == SCANCODE_DELETE) { + if (sel[0] == sel[1]) { + if (sHeldControl) { + sel[1] = djui_inputbox_jump_word_right(msg, len, sel[1]); + } else if (sel[1] < len) { + sel[1]++; + } + } + if (sel[0] != sel[1]) { + djui_inputbox_delete_selection(inputbox); + } + sCursorBlink = 0; + return true; + } + + if ((sHeldControl && scancode == SCANCODE_V) || (sHeldShift && scancode == SCANCODE_INSERT)) { + djui_interactable_on_text_input(wm_api->get_clipboard_text()); + sCursorBlink = 0; + return true; + } + + if (sHeldControl && (scancode == SCANCODE_C || scancode == SCANCODE_X)) { + if (sel[0] != sel[1]) { + char clipboardText[256] = { 0 }; + snprintf(clipboardText, fmin(256, 1 + s2 - s1), "%s", &msg[s1]); + wm_api->set_clipboard_text(clipboardText); + if (scancode == SCANCODE_X) { + djui_inputbox_delete_selection(inputbox); + sCursorBlink = 0; + } + } + return true; + } + + if (sHeldControl && scancode == SCANCODE_A) { + inputbox->selection[0] = len; + inputbox->selection[1] = 0; + sCursorBlink = 0; + return true; + } + + if (scancode == SCANCODE_ESCAPE) { + djui_interactable_set_input_focus(NULL); + return true; + } + + if (scancode == SCANCODE_ENTER) { + djui_interactable_set_input_focus(NULL); + if (inputbox->on_enter_press) { + inputbox->on_enter_press(); + } + return true; + } + + return true; +} + +static void djui_inputbox_on_key_up(struct DjuiBase *base, int scancode) { + switch (scancode) { + case SCANCODE_CONTROL_LEFT: sHeldControl &= ~(1 << 0); break; + case SCANCODE_CONTROL_RIGHT: sHeldControl &= ~(1 << 1); break; + case SCANCODE_SHIFT_LEFT: sHeldShift &= ~(1 << 0); break; + case SCANCODE_SHIFT_RIGHT: sHeldShift &= ~(1 << 1); break; + } +} + +static void djui_inputbox_on_focus_begin(struct DjuiBase* base) { + sHeldControl = 0; + sHeldShift = 0; + wm_api->start_text_input(); +} + +static void djui_inputbox_on_focus_end(struct DjuiBase* base) { + wm_api->stop_text_input(); +} + +static void djui_inputbox_on_text_input(struct DjuiBase *base, char* text) { + struct DjuiInputbox *inputbox = (struct DjuiInputbox *) base; + char* msg = inputbox->buffer; + int msgLen = strlen(msg); + int textLen = strlen(text); + + // truncate + if (textLen + msgLen >= inputbox->bufferSize) { + int space = (inputbox->bufferSize - msgLen); + if (space <= 1) { return; } + text[space - 1] = '\0'; + textLen = space - 1; + } + + // erase selection + if (inputbox->selection[0] != inputbox->selection[1]) { + djui_inputbox_delete_selection(inputbox); + } + + // sanitize + char *t = text; + while (*t != '\0') { + if (*t == '\n') { *t = ' '; } + else if (*t == '\r') { *t = ' '; } + else if (*t == ' ') { ; } + else if (*t < '!' || *t > '~') { *t = '?'; } + t++; + } + + // back up current message + char* sMsg = malloc(sizeof(char) * (inputbox->bufferSize)); + memcpy(sMsg, msg, inputbox->bufferSize); + + // insert text + u16 sel = inputbox->selection[0]; + snprintf(&msg[sel], (inputbox->bufferSize - sel), "%s%s", text, &sMsg[sel]); + free(sMsg); + + // adjust cursor + inputbox->selection[0] += strlen(text); + inputbox->selection[1] = inputbox->selection[0]; + sCursorBlink = 0; +} + static void djui_inputbox_render_char(struct DjuiInputbox* inputbox, char c, f32* drawX, f32* additionalShift) { struct DjuiBaseRect* comp = &inputbox->base.comp; struct DjuiFont* font = &gDjuiFonts[0]; @@ -74,16 +314,14 @@ static void djui_inputbox_render_char(struct DjuiInputbox* inputbox, char c, f32 f32 charWidth = font->char_width(c); *drawX += charWidth * font->defaultFontScale; - if (c != ' ') { - if (djui_gfx_add_clipping_specific(&inputbox->base, font->rotatedUV, dX, dY, dW, dH)) { - *additionalShift += charWidth; - return; + if (c != ' ' && !djui_gfx_add_clipping_specific(&inputbox->base, font->rotatedUV, dX, dY, dW, dH)) { + if (*additionalShift > 0) { + create_dl_translation_matrix(DJUI_MTX_NOPUSH, *additionalShift, 0, 0); + *additionalShift = 0; } font->render_char(c); } - - create_dl_translation_matrix(DJUI_MTX_NOPUSH, charWidth + *additionalShift, 0, 0); - *additionalShift = 0; + *additionalShift += charWidth; } static void djui_inputbox_render_selection(struct DjuiInputbox* inputbox) { @@ -109,7 +347,7 @@ static void djui_inputbox_render_selection(struct DjuiInputbox* inputbox) { // render only cursor when there is no selection width if (selection[0] == selection[1]) { - if (sCursorBlink < DJUI_INPUTBOX_MID_BLINK) { + if (sCursorBlink < DJUI_INPUTBOX_MID_BLINK && djui_interactable_is_input_focus(&inputbox->base)) { create_dl_translation_matrix(DJUI_MTX_PUSH, x - DJUI_INPUTBOX_CURSOR_WIDTH / 2.0f, -0.1f, 0); create_dl_scale_matrix(DJUI_MTX_NOPUSH, DJUI_INPUTBOX_CURSOR_WIDTH, 0.8f, 1.0f); gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255); @@ -139,7 +377,7 @@ static void djui_inputbox_render_selection(struct DjuiInputbox* inputbox) { gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); // render selection cursor - if (sCursorBlink < DJUI_INPUTBOX_MID_BLINK) { + if (sCursorBlink < DJUI_INPUTBOX_MID_BLINK && djui_interactable_is_input_focus(&inputbox->base)) { f32 cX = (inputbox->selection[0] < inputbox->selection[1]) ? x : (x + width); create_dl_translation_matrix(DJUI_MTX_PUSH, cX - DJUI_INPUTBOX_CURSOR_WIDTH / 2.0f, -0.1f, 0); create_dl_scale_matrix(DJUI_MTX_NOPUSH, DJUI_INPUTBOX_CURSOR_WIDTH, 0.8f, 1.0f); @@ -237,13 +475,10 @@ static void djui_inputbox_destroy(struct DjuiBase* base) { struct DjuiInputbox* djui_inputbox_create(struct DjuiBase* parent, u16 bufferSize) { struct DjuiInputbox* inputbox = malloc(sizeof(struct DjuiInputbox)); struct DjuiBase* base = &inputbox->base; - inputbox->viewX = 0; - inputbox->selection[0] = 0; - inputbox->selection[1] = 0; + memset(inputbox, 0, sizeof(struct DjuiInputbox)); inputbox->bufferSize = bufferSize; inputbox->buffer = malloc(sizeof(char) * bufferSize); memset(inputbox->buffer, 0, sizeof(char) * bufferSize); - sprintf(inputbox->buffer, "testing string hello world there it is"); djui_base_init(parent, base, djui_inputbox_render, djui_inputbox_destroy); djui_base_set_size(base, 200, 32); @@ -251,6 +486,9 @@ struct DjuiInputbox* djui_inputbox_create(struct DjuiBase* parent, u16 bufferSiz djui_interactable_create(base); djui_interactable_hook_hover(base, djui_inputbox_on_hover, djui_inputbox_on_hover_end); djui_interactable_hook_cursor_down(base, djui_inputbox_on_cursor_down_begin, djui_inputbox_on_cursor_down, djui_inputbox_on_cursor_down_end); + djui_interactable_hook_key(base, djui_inputbox_on_key_down, djui_inputbox_on_key_up); + djui_interactable_hook_focus(base, djui_inputbox_on_focus_begin, NULL, djui_inputbox_on_focus_end); + djui_interactable_hook_text_input(base, djui_inputbox_on_text_input); djui_inputbox_set_default_style(base); diff --git a/src/pc/djui/djui_inputbox.h b/src/pc/djui/djui_inputbox.h index d9557f99..9d7ca72c 100644 --- a/src/pc/djui/djui_inputbox.h +++ b/src/pc/djui/djui_inputbox.h @@ -8,6 +8,9 @@ struct DjuiInputbox { u16 bufferSize; u16 selection[2]; f32 viewX; + void (*on_enter_press)(void); }; +void djui_inputbox_hook_enter_press(struct DjuiInputbox* inputbox, void (*on_enter_press)(void)); + struct DjuiInputbox* djui_inputbox_create(struct DjuiBase* parent, u16 bufferSize); diff --git a/src/pc/djui/djui_interactable.c b/src/pc/djui/djui_interactable.c index 2269d591..525a620a 100644 --- a/src/pc/djui/djui_interactable.c +++ b/src/pc/djui/djui_interactable.c @@ -26,11 +26,12 @@ static bool sIgnoreInteractableUntilCursorReleased = false; static struct DjuiBase* sInteractableFocus = NULL; static struct DjuiBase* sInteractableBinding = NULL; -static struct DjuiBase* sHovered = NULL; -static struct DjuiBase* sMouseDown = NULL; -bool gInteractableOverridePad = false; -OSContPad gInteractablePad = { 0 }; -OSContPad sLastInteractablePad = { 0 }; +static struct DjuiBase* sHovered = NULL; +static struct DjuiBase* sMouseDown = NULL; +bool gInteractableOverridePad = false; +OSContPad gInteractablePad = { 0 }; +static OSContPad sLastInteractablePad = { 0 }; +static int sLastMouseButtons = 0; static void djui_interactable_on_click(struct DjuiBase* base) { if (base == NULL) { return; } @@ -166,17 +167,46 @@ void djui_interactable_set_input_focus(struct DjuiBase* base) { djui_cursor_set_visible(base == NULL); } -void djui_interactable_on_key_down(int scancode) { - switch (scancode) { - case SCANCODE_UP: sKeyboardHoldDirection = PAD_HOLD_DIR_UP; break; - case SCANCODE_DOWN: sKeyboardHoldDirection = PAD_HOLD_DIR_DOWN; break; - case SCANCODE_LEFT: sKeyboardHoldDirection = PAD_HOLD_DIR_LEFT; break; - case SCANCODE_RIGHT: sKeyboardHoldDirection = PAD_HOLD_DIR_RIGHT; break; - case SCANCODE_ENTER: sKeyboardButtons |= PAD_BUTTON_A; break; +bool djui_interactable_is_input_focus(struct DjuiBase* base) { + return sInteractableFocus == base; +} + +bool djui_interactable_on_key_down(int scancode) { + + bool keyFocused = (sInteractableFocus != NULL) + && (sInteractableFocus->interactable != NULL) + && (sInteractableFocus->interactable->on_key_down != NULL); + + if (keyFocused) { + bool consume = sInteractableFocus->interactable->on_key_down(sInteractableFocus, scancode); + sKeyboardHoldDirection = PAD_HOLD_DIR_NONE; + sKeyboardButtons = 0; + return consume; } + + switch (scancode) { + case SCANCODE_UP: sKeyboardHoldDirection = PAD_HOLD_DIR_UP; return true; + case SCANCODE_DOWN: sKeyboardHoldDirection = PAD_HOLD_DIR_DOWN; return true; + case SCANCODE_LEFT: sKeyboardHoldDirection = PAD_HOLD_DIR_LEFT; return true; + case SCANCODE_RIGHT: sKeyboardHoldDirection = PAD_HOLD_DIR_RIGHT; return true; + case SCANCODE_ENTER: sKeyboardButtons |= PAD_BUTTON_A; return true; + } + return false; } void djui_interactable_on_key_up(int scancode) { + + bool keyFocused = (sInteractableFocus != NULL) + && (sInteractableFocus->interactable != NULL) + && (sInteractableFocus->interactable->on_key_up != NULL); + + if (keyFocused) { + sInteractableFocus->interactable->on_key_up(sInteractableFocus, scancode); + sKeyboardHoldDirection = PAD_HOLD_DIR_NONE; + sKeyboardButtons = 0; + return; + } + OSContPad* pad = &gInteractablePad; switch (scancode) { case SCANCODE_UP: if (sKeyboardHoldDirection == PAD_HOLD_DIR_UP) { sKeyboardHoldDirection = PAD_HOLD_DIR_NONE; pad->stick_y = 0; } break; @@ -187,6 +217,13 @@ void djui_interactable_on_key_up(int scancode) { } } +void djui_interactable_on_text_input(char* text) { + if (sInteractableFocus == NULL) { return; } + if (sInteractableFocus->interactable == NULL) { return; } + if (sInteractableFocus->interactable->on_text_input == NULL) { return; } + sInteractableFocus->interactable->on_text_input(sInteractableFocus, text); +} + void djui_interactable_update_pad(void) { OSContPad* pad = &gInteractablePad; @@ -252,16 +289,22 @@ void djui_interactable_update(void) { } } - if (sInteractableBinding != NULL) { - djui_interactable_on_bind(sInteractableBinding); - } else if (sInteractableFocus != NULL) { - // escape focus - u16 buttons = PAD_BUTTON_A | PAD_BUTTON_B; - if ((padButtons & buttons) && !(sLastInteractablePad.button & buttons)) { + // update focused + if (sInteractableFocus) { + u16 mainButtons = PAD_BUTTON_A | PAD_BUTTON_B; + if ((mouseButtons & MOUSE_BUTTON_1) && !(sLastMouseButtons && MOUSE_BUTTON_1) && !djui_cursor_inside_base(sInteractableFocus)) { + // clicked outside of focused + djui_interactable_set_input_focus(NULL); + } else if ((padButtons & mainButtons) && !(sLastInteractablePad.button & mainButtons)) { + // pressed main face button djui_interactable_set_input_focus(NULL); } else { djui_interactable_on_focus(sInteractableFocus); } + } + + if (sInteractableBinding != NULL) { + djui_interactable_on_bind(sInteractableBinding); } else if ((padButtons & PAD_BUTTON_A) || (mouseButtons & MOUSE_BUTTON_1)) { // cursor down events if (sHovered != NULL) { @@ -288,6 +331,7 @@ void djui_interactable_update(void) { } sLastInteractablePad = gInteractablePad; + sLastMouseButtons = mouseButtons; } void djui_interactable_hook_hover(struct DjuiBase* base, @@ -336,6 +380,21 @@ void djui_interactable_hook_bind(struct DjuiBase* base, interactable->on_bind = on_bind; } +void djui_interactable_hook_key(struct DjuiBase* base, + bool (*on_key_down)(struct DjuiBase*, int), + void (*on_key_up)(struct DjuiBase*, int)) { + struct DjuiInteractable *interactable = base->interactable; + interactable->on_key_down = on_key_down; + interactable->on_key_up = on_key_up; + +} + +void djui_interactable_hook_text_input(struct DjuiBase *base, + void (*on_text_input)(struct DjuiBase*, char*)) { + struct DjuiInteractable *interactable = base->interactable; + interactable->on_text_input = on_text_input; +} + void djui_interactable_create(struct DjuiBase* base) { if (base->interactable != NULL) { diff --git a/src/pc/djui/djui_interactable.h b/src/pc/djui/djui_interactable.h index 3b4a89c3..32c59d7d 100644 --- a/src/pc/djui/djui_interactable.h +++ b/src/pc/djui/djui_interactable.h @@ -21,6 +21,9 @@ struct DjuiInteractable { void (*on_click)(struct DjuiBase*); void (*on_value_change)(struct DjuiBase*); void (*on_bind)(struct DjuiBase*); + bool (*on_key_down)(struct DjuiBase*, int scancode); + void (*on_key_up)(struct DjuiBase*, int scancode); + void (*on_text_input)(struct DjuiBase*, char* text); }; extern bool gInteractableOverridePad; @@ -29,8 +32,11 @@ extern OSContPad gInteractablePad; bool djui_interactable_is_binding(void); void djui_interactable_set_binding(struct DjuiBase* base); void djui_interactable_set_input_focus(struct DjuiBase* base); -void djui_interactable_on_key_down(int scancode); +bool djui_interactable_is_input_focus(struct DjuiBase* base); +bool djui_interactable_on_key_down(int scancode); void djui_interactable_on_key_up(int scancode); +void djui_interactable_on_text_input(char *text); + void djui_interactable_update(void); void djui_interactable_hook_hover(struct DjuiBase* base, @@ -55,4 +61,12 @@ void djui_interactable_hook_value_change(struct DjuiBase* base, void djui_interactable_hook_bind(struct DjuiBase* base, void (*on_bind)(struct DjuiBase*)); + +void djui_interactable_hook_key(struct DjuiBase* base, + bool (*on_key_down)(struct DjuiBase*, int), + void (*on_key_up)(struct DjuiBase*, int)); + +void djui_interactable_hook_text_input(struct DjuiBase* base, + void (*on_text_input)(struct DjuiBase*, char*)); + void djui_interactable_create(struct DjuiBase* base); diff --git a/src/pc/djui/djui_panel_controls.c b/src/pc/djui/djui_panel_controls.c index 421a3ec1..a9acdbee 100644 --- a/src/pc/djui/djui_panel_controls.c +++ b/src/pc/djui/djui_panel_controls.c @@ -16,17 +16,17 @@ void djui_panel_controls_create(struct DjuiBase* caller) { djui_base_set_color(&bindBody->base, 0, 0, 0, 0); djui_flow_layout_set_margin(bindBody, 1); { - struct DjuiBind* bind1 = djui_bind_create(&bindBody->base, "A", configKeyA); - struct DjuiBind* bind2 = djui_bind_create(&bindBody->base, "B", configKeyB); - struct DjuiBind* bind3 = djui_bind_create(&bindBody->base, "Start", configKeyStart); - struct DjuiBind* bind4 = djui_bind_create(&bindBody->base, "L", configKeyL); - struct DjuiBind* bind5 = djui_bind_create(&bindBody->base, "R", configKeyR); - struct DjuiBind* bind6 = djui_bind_create(&bindBody->base, "Z", configKeyZ); - struct DjuiBind* bind7 = djui_bind_create(&bindBody->base, "C Up", configKeyCUp); - struct DjuiBind* bind8 = djui_bind_create(&bindBody->base, "C Down", configKeyCDown); - struct DjuiBind* bind9 = djui_bind_create(&bindBody->base, "C Left", configKeyCLeft); - struct DjuiBind* bind10 = djui_bind_create(&bindBody->base, "C Right", configKeyCRight); - struct DjuiBind* bind11 = djui_bind_create(&bindBody->base, "Chat", configKeyChat); + struct DjuiBind* bind1 = djui_bind_create(&bindBody->base, "A", configKeyA); + djui_bind_create(&bindBody->base, "B", configKeyB); + djui_bind_create(&bindBody->base, "Start", configKeyStart); + djui_bind_create(&bindBody->base, "L", configKeyL); + djui_bind_create(&bindBody->base, "R", configKeyR); + djui_bind_create(&bindBody->base, "Z", configKeyZ); + djui_bind_create(&bindBody->base, "C Up", configKeyCUp); + djui_bind_create(&bindBody->base, "C Down", configKeyCDown); + djui_bind_create(&bindBody->base, "C Left", configKeyCLeft); + djui_bind_create(&bindBody->base, "C Right", configKeyCRight); + djui_bind_create(&bindBody->base, "Chat", configKeyChat); defaultBase = &bind1->buttons[0]->base; } diff --git a/src/pc/djui/djui_panel_main.c b/src/pc/djui/djui_panel_main.c index 41b4c22c..decb731f 100644 --- a/src/pc/djui/djui_panel_main.c +++ b/src/pc/djui/djui_panel_main.c @@ -47,7 +47,7 @@ void djui_panel_main_create(struct DjuiBase* caller) { } struct DjuiInputbox* inputbox = djui_inputbox_create(&gDjuiRoot->base, 256); - djui_base_set_location(&inputbox->base, 400, 400); + djui_base_set_location(&inputbox->base, 400, 100); djui_panel_add(caller, &panel->base, defaultBase); gInteractableOverridePad = true; diff --git a/src/pc/gfx/gfx_dxgi.cpp b/src/pc/gfx/gfx_dxgi.cpp index 3e764a0b..0ad1c7f3 100644 --- a/src/pc/gfx/gfx_dxgi.cpp +++ b/src/pc/gfx/gfx_dxgi.cpp @@ -634,6 +634,20 @@ static char* gfx_dxgi_get_clipboard_text(void) { return NULL; } +void gfx_dxgi_set_clipboard_text(char* text) { + if (OpenClipboard(NULL)) { + HGLOBAL clipbuffer; + char *buffer; + EmptyClipboard(); + clipbuffer = GlobalAlloc(GMEM_DDESHARE, strlen(text) + 1); + buffer = (char *) GlobalLock(clipbuffer); + strcpy(buffer, LPCSTR(source)); + GlobalUnlock(clipbuffer); + SetClipboardData(CF_TEXT, clipbuffer); + CloseClipboard(); + } +} + void ThrowIfFailed(HRESULT res) { if (FAILED(res)) { fprintf(stderr, "Error: 0x%08X\n", res); @@ -665,6 +679,7 @@ struct GfxWindowManagerAPI gfx_dxgi = { gfx_dxgi_start_text_input, gfx_dxgi_stop_text_input, gfx_dxgi_get_clipboard_text, + gfx_dxgi_set_clipboard_text, }; #endif diff --git a/src/pc/gfx/gfx_sdl2.c b/src/pc/gfx/gfx_sdl2.c index 8e1325f5..37552616 100644 --- a/src/pc/gfx/gfx_sdl2.c +++ b/src/pc/gfx/gfx_sdl2.c @@ -369,6 +369,7 @@ static void gfx_sdl_shutdown(void) { static void gfx_sdl_start_text_input(void) { SDL_StartTextInput(); } static void gfx_sdl_stop_text_input(void) { SDL_StopTextInput(); } static char* gfx_sdl_get_clipboard_text(void) { return SDL_GetClipboardText(); } +static void gfx_sdl_set_clipboard_text(char* text) { SDL_SetClipboardText(text); } struct GfxWindowManagerAPI gfx_sdl = { gfx_sdl_init, @@ -384,6 +385,7 @@ struct GfxWindowManagerAPI gfx_sdl = { gfx_sdl_start_text_input, gfx_sdl_stop_text_input, gfx_sdl_get_clipboard_text, + gfx_sdl_set_clipboard_text, }; #endif // BACKEND_WM diff --git a/src/pc/gfx/gfx_window_manager_api.h b/src/pc/gfx/gfx_window_manager_api.h index bf4639c2..5439373b 100644 --- a/src/pc/gfx/gfx_window_manager_api.h +++ b/src/pc/gfx/gfx_window_manager_api.h @@ -23,6 +23,7 @@ struct GfxWindowManagerAPI { void (*start_text_input)(void); void (*stop_text_input)(void); char* (*get_clipboard_text)(void); + void (*set_clipboard_text)(char*); }; #endif