basic utf8 text input, with clipboard support

This commit is contained in:
Andrei Alexeyev 2017-09-30 05:38:16 +03:00
parent ef67a16867
commit 4e2e050baa
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
6 changed files with 170 additions and 24 deletions

View file

@ -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;

View file

@ -36,6 +36,8 @@ typedef enum {
TE_VIDEO_MODE_CHANGED,
TE_CLIPBOARD_PASTE,
NUM_TAISEI_EVENTS
} TaiseiEvent;

View file

@ -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,
},

View file

@ -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;

View file

@ -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
*

View file

@ -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
//