basic utf8 text input, with clipboard support
This commit is contained in:
parent
ef67a16867
commit
4e2e050baa
6 changed files with 170 additions and 24 deletions
31
src/events.c
31
src/events.c
|
@ -262,17 +262,19 @@ void events_pause_keyrepeat(void) {
|
|||
|
||||
static bool events_handler_quit(SDL_Event *event, void *arg);
|
||||
static bool events_handler_keyrepeat_workaround(SDL_Event *event, void *arg);
|
||||
static bool events_handler_clipboard(SDL_Event *event, void *arg);
|
||||
static bool events_handler_hotkeys(SDL_Event *event, void *arg);
|
||||
static bool events_handler_key_down(SDL_Event *event, void *arg);
|
||||
static bool events_handler_key_up(SDL_Event *event, void *arg);
|
||||
// static bool events_handler_text_input(SDL_Event *event, void *arg);
|
||||
|
||||
|
||||
static EventHandler default_handlers[] = {
|
||||
{ .proc = events_handler_quit, .priority = EPRIO_SYSTEM, .event_type = SDL_QUIT},
|
||||
{ .proc = events_handler_keyrepeat_workaround, .priority = EPRIO_CAPTURE, .event_type = SDL_KEYDOWN},
|
||||
{ .proc = events_handler_hotkeys, .priority = EPRIO_HOTKEYS, .event_type = SDL_KEYDOWN},
|
||||
{ .proc = events_handler_key_down, .priority = EPRIO_TRANSLATION, .event_type = SDL_KEYDOWN},
|
||||
{ .proc = events_handler_key_up, .priority = EPRIO_TRANSLATION, .event_type = SDL_KEYUP},
|
||||
{ .proc = events_handler_quit, .priority = EPRIO_SYSTEM, .event_type = SDL_QUIT },
|
||||
{ .proc = events_handler_keyrepeat_workaround, .priority = EPRIO_CAPTURE, .event_type = SDL_KEYDOWN },
|
||||
{ .proc = events_handler_clipboard, .priority = EPRIO_CAPTURE, .event_type = SDL_KEYDOWN },
|
||||
{ .proc = events_handler_hotkeys, .priority = EPRIO_HOTKEYS, .event_type = SDL_KEYDOWN },
|
||||
{ .proc = events_handler_key_down, .priority = EPRIO_TRANSLATION, .event_type = SDL_KEYDOWN },
|
||||
{ .proc = events_handler_key_up, .priority = EPRIO_TRANSLATION, .event_type = SDL_KEYUP },
|
||||
|
||||
{NULL}
|
||||
};
|
||||
|
@ -305,6 +307,23 @@ static bool events_handler_keyrepeat_workaround(SDL_Event *event, void *arg) {
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool events_handler_clipboard(SDL_Event *event, void *arg) {
|
||||
if(!SDL_IsTextInputActive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(event->key.keysym.mod & KMOD_CTRL && event->key.keysym.scancode == SDL_SCANCODE_V) {
|
||||
if(SDL_HasClipboardText()) {
|
||||
memset(event, 0, sizeof(SDL_Event));
|
||||
event->type = MAKE_TAISEI_EVENT(TE_CLIPBOARD_PASTE);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool events_handler_key_down(SDL_Event *event, void *arg) {
|
||||
SDL_Scancode scan = event->key.keysym.scancode;
|
||||
bool repeat = event->key.repeat;
|
||||
|
|
|
@ -36,6 +36,8 @@ typedef enum {
|
|||
|
||||
TE_VIDEO_MODE_CHANGED,
|
||||
|
||||
TE_CLIPBOARD_PASTE,
|
||||
|
||||
NUM_TAISEI_EVENTS
|
||||
} TaiseiEvent;
|
||||
|
||||
|
|
|
@ -124,11 +124,7 @@ OptionBinding* bind_stroption(ConfigIndex cfgentry) {
|
|||
OptionBinding *bind = bind_new();
|
||||
bind->type = BT_StrValue;
|
||||
bind->configentry = cfgentry;
|
||||
|
||||
bind->valcount = 1;
|
||||
bind->values = malloc(sizeof(char*));
|
||||
*bind->values = malloc(OPTIONS_TEXT_INPUT_BUFSIZE);
|
||||
strlcpy(*bind->values, config_get_str(cfgentry), OPTIONS_TEXT_INPUT_BUFSIZE);
|
||||
stralloc(&bind->strvalue, config_get_str(cfgentry));
|
||||
|
||||
return bind;
|
||||
}
|
||||
|
@ -813,10 +809,12 @@ void draw_options_menu(MenuData *menu) {
|
|||
case BT_StrValue: {
|
||||
if(bind->blockinput) {
|
||||
glColor4f(0.5, 1, 0.5, 1.0);
|
||||
if(strlen(*bind->values))
|
||||
draw_text(AL_Right, origin, 20*i, *bind->values, _fonts.standard);
|
||||
} else
|
||||
if(*bind->strvalue) {
|
||||
draw_text(AL_Right, origin, 20*i, bind->strvalue, _fonts.standard);
|
||||
}
|
||||
} else {
|
||||
draw_text(AL_Right, origin, 20*i, config_get_str(bind->configentry), _fonts.standard);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -908,7 +906,7 @@ static bool options_vidmode_change_handler(SDL_Event *event, void *arg) {
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool options_input_handler_for_binding(SDL_Event *event, void *arg) {
|
||||
static bool options_rebind_input_handler(SDL_Event *event, void *arg) {
|
||||
if(!arg) {
|
||||
return false;
|
||||
}
|
||||
|
@ -916,6 +914,10 @@ static bool options_input_handler_for_binding(SDL_Event *event, void *arg) {
|
|||
OptionBinding *b = arg;
|
||||
uint32_t t = event->type;
|
||||
|
||||
if(b->type == BT_StrValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(t == SDL_KEYDOWN) {
|
||||
SDL_Scancode scan = event->key.keysym.scancode;
|
||||
bool esc = scan == SDL_SCANCODE_ESCAPE;
|
||||
|
@ -990,13 +992,96 @@ static bool options_input_handler_for_binding(SDL_Event *event, void *arg) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// FIXME: Implement text input
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// raw access to arg is safe there after the bind_get check
|
||||
#define SHOULD_SKIP
|
||||
static bool options_text_input_handler(SDL_Event *event, void *arg) {
|
||||
OptionBinding *b = arg;
|
||||
|
||||
if(!b || b->type != BT_StrValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t t = event->type;
|
||||
|
||||
if(t == SDL_TEXTINPUT || t == MAKE_TAISEI_EVENT(TE_CLIPBOARD_PASTE)) {
|
||||
const size_t max_len = 32;
|
||||
const char *snd = "generic_shot";
|
||||
char *text, *text_allocated = NULL;
|
||||
|
||||
if(t == SDL_TEXTINPUT) {
|
||||
text = event->text.text;
|
||||
} else {
|
||||
text = text_allocated = SDL_GetClipboardText();
|
||||
}
|
||||
|
||||
assert(text != NULL);
|
||||
strappend(&b->strvalue, text);
|
||||
|
||||
if(strlen(b->strvalue) > max_len) {
|
||||
/*
|
||||
* EFFICIENT AS FUCK
|
||||
*/
|
||||
|
||||
uint32_t *u = utf8_to_ucs4(b->strvalue);
|
||||
size_t ulen = ucs4len(u);
|
||||
|
||||
if(ulen > max_len) {
|
||||
*(u + max_len) = 0;
|
||||
free(b->strvalue);
|
||||
b->strvalue = ucs4_to_utf8(u);
|
||||
snd = "hit";
|
||||
}
|
||||
|
||||
free(u);
|
||||
}
|
||||
|
||||
free(text_allocated);
|
||||
play_ui_sound(snd);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(t == SDL_KEYDOWN) {
|
||||
SDL_Scancode scan = event->key.keysym.scancode;
|
||||
|
||||
if(scan == SDL_SCANCODE_ESCAPE) {
|
||||
play_ui_sound("hit");
|
||||
stralloc(&b->strvalue, config_get_str(b->configentry));
|
||||
b->blockinput = false;
|
||||
} else if(scan == SDL_SCANCODE_RETURN) {
|
||||
if(*b->strvalue) {
|
||||
play_ui_sound("shot_special1");
|
||||
} else {
|
||||
play_ui_sound("hit");
|
||||
stralloc(&b->strvalue, "Player");
|
||||
}
|
||||
|
||||
config_set_str(b->configentry, b->strvalue);
|
||||
b->blockinput = false;
|
||||
} else if(scan == SDL_SCANCODE_BACKSPACE) {
|
||||
/*
|
||||
* MORE EFFICIENT THAN FUCK
|
||||
*/
|
||||
|
||||
uint32_t *u = utf8_to_ucs4(b->strvalue);
|
||||
|
||||
if(*u) {
|
||||
play_ui_sound("generic_shot");
|
||||
*(ucs4chr(u, 0) - 1) = 0;
|
||||
} else {
|
||||
play_ui_sound("hit");
|
||||
}
|
||||
|
||||
free(b->strvalue);
|
||||
b->strvalue = ucs4_to_utf8(u);
|
||||
free(u);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool options_input_handler(SDL_Event *event, void *arg) {
|
||||
MenuData *menu = arg;
|
||||
|
@ -1063,7 +1148,6 @@ static bool options_input_handler(SDL_Event *event, void *arg) {
|
|||
break;
|
||||
|
||||
case BT_StrValue:
|
||||
bind->selected = strlen(config_get_str(bind->configentry));
|
||||
bind->blockinput = true;
|
||||
break;
|
||||
|
||||
|
@ -1104,7 +1188,12 @@ void options_menu_input(MenuData *menu) {
|
|||
.event_type = MAKE_TAISEI_EVENT(TE_VIDEO_MODE_CHANGED),
|
||||
},
|
||||
{
|
||||
.proc = options_input_handler_for_binding,
|
||||
.proc = options_text_input_handler,
|
||||
.arg = b,
|
||||
.priority = EPRIO_CAPTURE,
|
||||
},
|
||||
{
|
||||
.proc = options_rebind_input_handler,
|
||||
.arg = b,
|
||||
.priority = EPRIO_CAPTURE,
|
||||
},
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
void create_options_menu(MenuData *m);
|
||||
void draw_options_menu(MenuData *m);
|
||||
|
||||
#define OPTIONS_TEXT_INPUT_BUFSIZE 50
|
||||
|
||||
typedef struct OptionBinding OptionBinding;
|
||||
|
||||
typedef int (*BindingGetter)(OptionBinding*);
|
||||
|
@ -33,7 +31,10 @@ typedef enum BindingType {
|
|||
} BindingType;
|
||||
|
||||
typedef struct OptionBinding {
|
||||
char **values;
|
||||
union {
|
||||
char **values;
|
||||
char *strvalue;
|
||||
};
|
||||
bool displaysingle;
|
||||
int valcount;
|
||||
int valrange_min;
|
||||
|
|
30
src/util.c
30
src/util.c
|
@ -156,6 +156,36 @@ char* strappend(char **dst, char *src) {
|
|||
return *dst;
|
||||
}
|
||||
|
||||
uint32_t* ucs4chr(const uint32_t *ucs4, uint32_t chr) {
|
||||
for(; *ucs4 != chr; ++ucs4) {
|
||||
if(!*ucs4) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return (uint32_t*)ucs4;
|
||||
}
|
||||
|
||||
size_t ucs4len(const uint32_t *ucs4) {
|
||||
size_t len;
|
||||
for(len = 0; *ucs4; ++len, ++ucs4);
|
||||
return len;
|
||||
}
|
||||
|
||||
uint32_t* utf8_to_ucs4(const char *utf8) {
|
||||
assert(utf8 != NULL);
|
||||
uint32_t *ucs4 = (uint32_t*)SDL_iconv_string("UCS-4", "UTF-8", utf8, strlen(utf8) + 1);
|
||||
assert(ucs4 != NULL);
|
||||
return ucs4;
|
||||
}
|
||||
|
||||
char* ucs4_to_utf8(const uint32_t *ucs4) {
|
||||
assert(ucs4 != NULL);
|
||||
char *utf8 = SDL_iconv_string("UTF-8", "UCS-4", (const char*)ucs4, sizeof(uint32_t) * (ucs4len(ucs4) + 1));
|
||||
assert(utf8 != NULL);
|
||||
return utf8;
|
||||
}
|
||||
|
||||
/*
|
||||
* public domain strtok_r() by Charlie Gordon
|
||||
*
|
||||
|
|
|
@ -55,6 +55,11 @@ char* strappend(char **dst, char *src);
|
|||
#undef strdup
|
||||
#define strdup SDL_strdup
|
||||
|
||||
uint32_t* ucs4chr(const uint32_t *ucs4, uint32_t chr);
|
||||
size_t ucs4len(const uint32_t *ucs4);
|
||||
uint32_t* utf8_to_ucs4(const char *utf8);
|
||||
char* ucs4_to_utf8(const uint32_t *ucs4);
|
||||
|
||||
//
|
||||
// math utils
|
||||
//
|
||||
|
|
Loading…
Reference in a new issue