WIP event system rewrite. text input missing
This commit is contained in:
parent
8add469c20
commit
ef67a16867
20 changed files with 818 additions and 509 deletions
|
@ -265,7 +265,7 @@ void credits_loop(void) {
|
|||
credits_preload();
|
||||
credits_init();
|
||||
while(credits.end) {
|
||||
handle_events(NULL, 0, NULL);
|
||||
events_poll(NULL, 0);
|
||||
credits_process();
|
||||
credits_draw();
|
||||
global.frames++;
|
||||
|
|
|
@ -145,7 +145,7 @@ void ending_loop(void) {
|
|||
set_ortho();
|
||||
|
||||
while(e.pos < e.count-1) {
|
||||
handle_events(NULL, 0, NULL);
|
||||
events_poll(NULL, 0);
|
||||
|
||||
ending_draw(&e);
|
||||
global.frames++;
|
||||
|
|
516
src/events.c
516
src/events.c
|
@ -12,37 +12,197 @@
|
|||
#include "video.h"
|
||||
#include "gamepad.h"
|
||||
|
||||
struct sdl_custom_events_s sdl_custom_events;
|
||||
static uint32_t keyrepeat_paused_until;
|
||||
static ListContainer *global_handlers;
|
||||
|
||||
uint32_t sdl_first_user_event;
|
||||
|
||||
static void events_register_default_handlers(void);
|
||||
static void events_unregister_default_handlers(void);
|
||||
|
||||
/*
|
||||
* Public API
|
||||
*/
|
||||
|
||||
void events_init(void) {
|
||||
uint32_t *events = (uint32_t*)&sdl_custom_events;
|
||||
uint32_t id = SDL_RegisterEvents(NUM_CUSTOM_EVENTS);
|
||||
sdl_first_user_event = SDL_RegisterEvents(NUM_TAISEI_EVENTS);
|
||||
|
||||
for(int i = 0; i < NUM_CUSTOM_EVENTS; ++i, ++id) {
|
||||
events[i] = id;
|
||||
log_debug("User event registered: %u", events[i]);
|
||||
if(sdl_first_user_event == (uint32_t)-1) {
|
||||
char *s =
|
||||
"You have exhausted the SDL userevent pool. "
|
||||
"How you managed that is beyond me, but congratulations. "
|
||||
#if (LOG_DEFAULT_LEVELS_BACKTRACE & LOG_FATAL)
|
||||
"Here's your prize stack trace."
|
||||
#endif
|
||||
;
|
||||
log_fatal("%s", s);
|
||||
}
|
||||
|
||||
SDL_EventState(SDL_MOUSEMOTION, SDL_DISABLE);
|
||||
|
||||
events_register_default_handlers();
|
||||
}
|
||||
|
||||
void events_shutdown(void) {
|
||||
events_unregister_default_handlers();
|
||||
|
||||
#ifdef DEBUG
|
||||
if(global_handlers) {
|
||||
log_warn(
|
||||
"Someone didn't unregister their handler. "
|
||||
"Clean up after yourself, I'm not your personal maid. "
|
||||
"Hint: ASan or valgrind can probably determine the culprit."
|
||||
);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool events_invoke_handler(SDL_Event *event, EventHandler *handler) {
|
||||
assert(handler->proc != NULL);
|
||||
|
||||
if(!handler->event_type || handler->event_type == event->type) {
|
||||
bool result = handler->proc(event, handler->arg);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int handler_container_prio_func(void *h) {
|
||||
return ((EventHandler*)((ListContainer*)h)->data)->priority;
|
||||
}
|
||||
|
||||
static EventPriority real_priority(EventPriority prio) {
|
||||
if(prio == EPRIO_DEFAULT) {
|
||||
return EPRIO_DEFAULT_REMAP;
|
||||
}
|
||||
|
||||
assert(prio < NUM_EPRIOS);
|
||||
return prio;
|
||||
}
|
||||
|
||||
static void events_print_handlers(ListContainer *list) {
|
||||
for(ListContainer *c = list; c; c = c->next) {
|
||||
EventHandler *h = c->data;
|
||||
log_debug(" * %p %u", *(void**)&h->proc, h->priority);
|
||||
}
|
||||
}
|
||||
|
||||
void events_pause_keyrepeat(void) {
|
||||
// for whatever stupid bizarre reason, keyrepeat skips the delay after the window toggles fullscreen on some systems
|
||||
// this ugly hack is a workaround for that
|
||||
keyrepeat_paused_until = SDL_GetTicks() + 250;
|
||||
static bool events_invoke_handlers(SDL_Event *event, ListContainer *h_list, EventHandler *h_array) {
|
||||
// invoke handlers from two sources (a list and an array) in the correct order according to priority
|
||||
// list items take precedence
|
||||
//
|
||||
// assumptions:
|
||||
// h_list is sorted by priority
|
||||
// h_array is in arbitrary order and terminated with a NULL-proc entry
|
||||
|
||||
bool result = false;
|
||||
|
||||
if(h_list && !h_array) {
|
||||
// case 1 (simplest): we have a list and no custom handlers
|
||||
|
||||
for(ListContainer *c = h_list; c; c = c->next) {
|
||||
if(result = events_invoke_handler(event, (EventHandler*)c->data)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if(h_list && h_array) {
|
||||
// case 2 (suboptimal): we have both a list and a disordered array; need to do some actual work
|
||||
// if you want to optimize this be my guest
|
||||
|
||||
ListContainer *merged_list = NULL;
|
||||
ListContainer *prevc = NULL;
|
||||
|
||||
// copy the list
|
||||
for(ListContainer *c = h_list; c; c = c->next) {
|
||||
ListContainer *newc = calloc(1, sizeof(ListContainer));
|
||||
newc->data = c->data;
|
||||
newc->prev = prevc;
|
||||
|
||||
if(prevc) {
|
||||
prevc->next = newc;
|
||||
}
|
||||
|
||||
if(!merged_list) {
|
||||
merged_list = newc;
|
||||
}
|
||||
|
||||
prevc = newc;
|
||||
}
|
||||
|
||||
// merge the array into the list copy, respecting priority
|
||||
for(EventHandler *h = h_array; h->proc; ++h) {
|
||||
create_container_at_priority(
|
||||
&merged_list,
|
||||
real_priority(h->priority),
|
||||
handler_container_prio_func
|
||||
)->data = h;
|
||||
}
|
||||
|
||||
// iterate over the merged list
|
||||
for(ListContainer *c = merged_list; c; c = c->next) {
|
||||
if(result = events_invoke_handler(event, (EventHandler*)c->data)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
delete_all_elements((void**)&merged_list, delete_element);
|
||||
return result;
|
||||
}
|
||||
|
||||
if(!h_list && h_array) {
|
||||
// case 3 (unlikely): we don't have a list for some reason (no global handlers?), but there are custom handlers
|
||||
|
||||
for(EventHandler *h = h_array; h->proc; ++h) {
|
||||
if(result = events_invoke_handler(event, h)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// case 4 (unlikely): huh? okay then
|
||||
return result;
|
||||
}
|
||||
|
||||
void handle_events(EventHandler handler, EventFlags flags, void *arg) {
|
||||
SDL_Event event;
|
||||
void events_register_handler(EventHandler *handler) {
|
||||
assert(handler->proc != NULL);
|
||||
EventHandler *handler_alloc = malloc(sizeof(EventHandler));
|
||||
memcpy(handler_alloc, handler, sizeof(EventHandler));
|
||||
|
||||
bool kbd = flags & EF_Keyboard;
|
||||
bool text = flags & EF_Text;
|
||||
bool menu = flags & EF_Menu;
|
||||
bool game = flags & EF_Game;
|
||||
bool video = flags & EF_Video;
|
||||
if(handler_alloc->priority == EPRIO_DEFAULT) {
|
||||
handler_alloc->priority = EPRIO_DEFAULT_REMAP;
|
||||
}
|
||||
|
||||
// TODO: rewrite text input handling to properly support multibyte characters and IMEs
|
||||
assert(handler_alloc->priority > EPRIO_DEFAULT);
|
||||
create_container_at_priority(&global_handlers, handler_alloc->priority, handler_container_prio_func)->data = handler_alloc;
|
||||
|
||||
if(text) {
|
||||
log_debug("Registered handler: %p %u", *(void**)&handler_alloc->proc, handler_alloc->priority);
|
||||
events_print_handlers(global_handlers);
|
||||
}
|
||||
|
||||
void events_unregister_handler(EventHandlerProc proc) {
|
||||
ListContainer *next;
|
||||
for(ListContainer *c = global_handlers; c; c = next) {
|
||||
EventHandler *h = c->data;
|
||||
next = c->next;
|
||||
|
||||
if(h->proc == proc) {
|
||||
free(c->data);
|
||||
delete_element((void**)&global_handlers, c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void events_apply_flags(EventFlags flags) {
|
||||
if(flags & EFLAG_TEXT) {
|
||||
if(!SDL_IsTextInputActive()) {
|
||||
SDL_StartTextInput();
|
||||
}
|
||||
|
@ -52,147 +212,189 @@ void handle_events(EventHandler handler, EventFlags flags, void *arg) {
|
|||
}
|
||||
}
|
||||
|
||||
while(SDL_PollEvent(&event)) {
|
||||
if(IS_CUSTOM_EVENT(event.type)) {
|
||||
log_debug("Custom event %u: %"PRIxMAX" %"PRIxMAX, event.type, (uintmax_t)event.user.data1, (uintmax_t)event.user.data2);
|
||||
TaiseiEvent type;
|
||||
|
||||
if(event.type == sdl_custom_events.video_mode_changed) {
|
||||
if(video) {
|
||||
handler(E_VideoModeChanged, 0, arg);
|
||||
}
|
||||
}
|
||||
for(type = TE_MENU_FIRST; type <= TE_MENU_LAST; ++type) {
|
||||
SDL_EventState(MAKE_TAISEI_EVENT(type), (bool)(flags & EFLAG_MENU));
|
||||
}
|
||||
|
||||
resource_sdl_event(&event);
|
||||
continue;
|
||||
}
|
||||
|
||||
SDL_Scancode scan = event.key.keysym.scancode;
|
||||
SDL_Keymod mod = event.key.keysym.mod;
|
||||
bool repeat = event.key.repeat;
|
||||
uint32_t timenow;
|
||||
|
||||
if((event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) && repeat && (timenow = SDL_GetTicks()) < keyrepeat_paused_until) {
|
||||
log_debug("Prevented a potentially bogus key repeat: %i %i %i %i %i", event.type - SDL_KEYDOWN, scan, mod, repeat, keyrepeat_paused_until - timenow);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(event.type) {
|
||||
case SDL_KEYDOWN:
|
||||
if(text) {
|
||||
if(scan == SDL_SCANCODE_ESCAPE)
|
||||
handler(E_CancelText, 0, arg);
|
||||
else if(scan == SDL_SCANCODE_RETURN)
|
||||
handler(E_SubmitText, 0, arg);
|
||||
else if(scan == SDL_SCANCODE_BACKSPACE)
|
||||
handler(E_CharErased, 0, arg);
|
||||
} else if(!repeat) {
|
||||
if(scan == config_get_int(CONFIG_KEY_SCREENSHOT)) {
|
||||
video_take_screenshot();
|
||||
break;
|
||||
}
|
||||
|
||||
if((scan == SDL_SCANCODE_RETURN && (mod & KMOD_ALT)) || scan == config_get_int(CONFIG_KEY_FULLSCREEN)) {
|
||||
config_set_int(CONFIG_FULLSCREEN, !config_get_int(CONFIG_FULLSCREEN));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(kbd) {
|
||||
handler(E_KeyDown, scan, arg);
|
||||
}
|
||||
|
||||
if(menu && (!repeat || transition.state == TRANS_IDLE)) {
|
||||
struct eventmap_t { int scancode; int event; } map[] = {
|
||||
// order matters
|
||||
// handle all the hardcoded controls first to prevent accidentally overriding them with unusable ones
|
||||
|
||||
{ SDL_SCANCODE_DOWN, E_CursorDown },
|
||||
{ SDL_SCANCODE_UP, E_CursorUp },
|
||||
{ SDL_SCANCODE_RIGHT, E_CursorRight },
|
||||
{ SDL_SCANCODE_LEFT, E_CursorLeft },
|
||||
{ SDL_SCANCODE_RETURN, E_MenuAccept },
|
||||
{ SDL_SCANCODE_ESCAPE, E_MenuAbort },
|
||||
|
||||
{ config_get_int(CONFIG_KEY_DOWN), E_CursorDown },
|
||||
{ config_get_int(CONFIG_KEY_UP), E_CursorUp },
|
||||
{ config_get_int(CONFIG_KEY_RIGHT), E_CursorRight },
|
||||
{ config_get_int(CONFIG_KEY_LEFT), E_CursorLeft },
|
||||
{ config_get_int(CONFIG_KEY_SHOT), E_MenuAccept },
|
||||
{ config_get_int(CONFIG_KEY_BOMB), E_MenuAbort },
|
||||
|
||||
{SDL_SCANCODE_UNKNOWN, -1}
|
||||
};
|
||||
|
||||
for(struct eventmap_t *m = map; m->scancode != SDL_SCANCODE_UNKNOWN; ++m) {
|
||||
if(scan == m->scancode) {
|
||||
handler(m->event, 0, arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(game && !repeat) {
|
||||
if(scan == config_get_int(CONFIG_KEY_PAUSE) || scan == SDL_SCANCODE_ESCAPE) {
|
||||
handler(E_Pause, 0, arg);
|
||||
} else {
|
||||
int key = config_key_from_scancode(scan);
|
||||
if(key >= 0)
|
||||
handler(E_PlrKeyDown, key, arg);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SDL_KEYUP:
|
||||
if(kbd) {
|
||||
handler(E_KeyUp, scan, arg);
|
||||
}
|
||||
|
||||
if(game && !repeat) {
|
||||
int key = config_key_from_scancode(scan);
|
||||
if(key >= 0)
|
||||
handler(E_PlrKeyUp, key, arg);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SDL_TEXTINPUT: {
|
||||
char *c;
|
||||
|
||||
for(c = event.text.text; *c; ++c) {
|
||||
handler(E_CharTyped, *c, arg);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
switch(event.window.event) {
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
video_resize(event.window.data1, event.window.data2);
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
if(game) {
|
||||
handler(E_Pause, 0, arg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_QUIT:
|
||||
exit(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
gamepad_event(&event, handler, flags, arg);
|
||||
break;
|
||||
}
|
||||
for(type = TE_GAME_FIRST; type <= TE_GAME_LAST; ++type) {
|
||||
SDL_EventState(MAKE_TAISEI_EVENT(type), (bool)(flags & EFLAG_GAME));
|
||||
}
|
||||
}
|
||||
|
||||
// Inputdevice-agnostic method of checking whether a game control is pressed.
|
||||
// ALWAYS use this instead of SDL_GetKeyState if you need it.
|
||||
bool gamekeypressed(KeyIndex key) {
|
||||
return SDL_GetKeyboardState(NULL)[config_get_int(KEYIDX_TO_CFGIDX(key))] || gamepad_gamekeypressed(key);
|
||||
void events_poll(EventHandler *handlers, EventFlags flags) {
|
||||
SDL_Event event;
|
||||
events_apply_flags(flags);
|
||||
|
||||
while(SDL_PollEvent(&event)) {
|
||||
events_invoke_handlers(&event, global_handlers, handlers);
|
||||
}
|
||||
}
|
||||
|
||||
void events_emit(TaiseiEvent type, int32_t code, void *data1, void *data2) {
|
||||
assert(TAISEI_EVENT_VALID(type));
|
||||
uint32_t sdltype = MAKE_TAISEI_EVENT(type);
|
||||
assert(IS_TAISEI_EVENT(sdltype));
|
||||
|
||||
if(!SDL_EventState(sdltype, SDL_QUERY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_Event event = { 0 };
|
||||
event.type = sdltype;
|
||||
event.user.code = code;
|
||||
event.user.data1 = data1;
|
||||
event.user.data2 = data2;
|
||||
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
void events_pause_keyrepeat(void) {
|
||||
// for whatever stupid bizarre reason, keyrepeat skips the delay after the window toggles fullscreen on some systems
|
||||
// this ugly hack is a workaround for that
|
||||
keyrepeat_paused_until = SDL_GetTicks() + 250;
|
||||
}
|
||||
|
||||
/*
|
||||
* Default handlers
|
||||
*/
|
||||
|
||||
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_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},
|
||||
|
||||
{NULL}
|
||||
};
|
||||
|
||||
static void events_register_default_handlers(void) {
|
||||
for(EventHandler *h = default_handlers; h->proc; ++h) {
|
||||
events_register_handler(h);
|
||||
}
|
||||
}
|
||||
|
||||
static void events_unregister_default_handlers(void) {
|
||||
for(EventHandler *h = default_handlers; h->proc; ++h) {
|
||||
events_unregister_handler(h->proc);
|
||||
}
|
||||
}
|
||||
|
||||
static bool events_handler_quit(SDL_Event *event, void *arg) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static bool events_handler_keyrepeat_workaround(SDL_Event *event, void *arg) {
|
||||
uint32_t timenow;
|
||||
|
||||
if(event->key.repeat && (timenow = SDL_GetTicks()) < keyrepeat_paused_until) {
|
||||
log_debug("Prevented a potentially bogus key repeat: %i %i %u",
|
||||
event->key.keysym.scancode, event->key.keysym.mod, keyrepeat_paused_until - timenow);
|
||||
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;
|
||||
|
||||
/*
|
||||
* Emit menu events
|
||||
*/
|
||||
|
||||
struct eventmap_s { int scancode; TaiseiEvent event; } menu_event_map[] = {
|
||||
// order matters
|
||||
// handle all the hardcoded controls first to prevent accidentally overriding them with unusable ones
|
||||
|
||||
{ SDL_SCANCODE_DOWN, TE_MENU_CURSOR_DOWN },
|
||||
{ SDL_SCANCODE_UP, TE_MENU_CURSOR_UP },
|
||||
{ SDL_SCANCODE_RIGHT, TE_MENU_CURSOR_RIGHT },
|
||||
{ SDL_SCANCODE_LEFT, TE_MENU_CURSOR_LEFT },
|
||||
{ SDL_SCANCODE_RETURN, TE_MENU_ACCEPT },
|
||||
{ SDL_SCANCODE_ESCAPE, TE_MENU_ABORT },
|
||||
|
||||
{ config_get_int(CONFIG_KEY_DOWN), TE_MENU_CURSOR_DOWN },
|
||||
{ config_get_int(CONFIG_KEY_UP), TE_MENU_CURSOR_UP },
|
||||
{ config_get_int(CONFIG_KEY_RIGHT), TE_MENU_CURSOR_RIGHT },
|
||||
{ config_get_int(CONFIG_KEY_LEFT), TE_MENU_CURSOR_LEFT },
|
||||
{ config_get_int(CONFIG_KEY_SHOT), TE_MENU_ACCEPT },
|
||||
{ config_get_int(CONFIG_KEY_BOMB), TE_MENU_ABORT },
|
||||
|
||||
{SDL_SCANCODE_UNKNOWN, -1}
|
||||
};
|
||||
|
||||
if(!repeat || transition.state == TRANS_IDLE) {
|
||||
for(struct eventmap_s *m = menu_event_map; m->scancode != SDL_SCANCODE_UNKNOWN; ++m) {
|
||||
if(scan == m->scancode) {
|
||||
events_emit(m->event, 0, NULL, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit game events
|
||||
*/
|
||||
|
||||
if(!repeat) {
|
||||
if(scan == config_get_int(CONFIG_KEY_PAUSE) || scan == SDL_SCANCODE_ESCAPE) {
|
||||
events_emit(TE_GAME_PAUSE, 0, NULL, NULL);
|
||||
} else {
|
||||
int key = config_key_from_scancode(scan);
|
||||
|
||||
if(key >= 0) {
|
||||
events_emit(TE_GAME_KEY_DOWN, key, NULL, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool events_handler_key_up(SDL_Event *event, void *arg) {
|
||||
SDL_Scancode scan = event->key.keysym.scancode;
|
||||
|
||||
/*
|
||||
* Emit game events
|
||||
*/
|
||||
|
||||
int key = config_key_from_scancode(scan);
|
||||
|
||||
if(key >= 0) {
|
||||
events_emit(TE_GAME_KEY_UP, key, NULL, NULL);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool events_handler_hotkeys(SDL_Event *event, void *arg) {
|
||||
if(event->key.repeat) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_Scancode scan = event->key.keysym.scancode;
|
||||
SDL_Keymod mod = event->key.keysym.mod;
|
||||
|
||||
if(scan == config_get_int(CONFIG_KEY_SCREENSHOT)) {
|
||||
video_take_screenshot();
|
||||
return true;
|
||||
}
|
||||
|
||||
if((scan == SDL_SCANCODE_RETURN && (mod & KMOD_ALT)) || scan == config_get_int(CONFIG_KEY_FULLSCREEN)) {
|
||||
config_set_int(CONFIG_FULLSCREEN, !config_get_int(CONFIG_FULLSCREEN));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
122
src/events.h
122
src/events.h
|
@ -8,68 +8,88 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
#include "util.h"
|
||||
|
||||
typedef enum {
|
||||
EF_Keyboard = 1,
|
||||
EF_Text = 2,
|
||||
EF_Menu = 4,
|
||||
EF_Game = 8,
|
||||
EF_Gamepad = 16,
|
||||
EF_Video = 32,
|
||||
TE_RESOURCE_ASYNC_LOADED,
|
||||
|
||||
#define TE_MENU_FIRST TE_MENU_CURSOR_UP
|
||||
TE_MENU_CURSOR_UP,
|
||||
TE_MENU_CURSOR_DOWN,
|
||||
TE_MENU_CURSOR_LEFT,
|
||||
TE_MENU_CURSOR_RIGHT,
|
||||
TE_MENU_ACCEPT,
|
||||
TE_MENU_ABORT,
|
||||
#define TE_MENU_LAST TE_MENU_ABORT
|
||||
|
||||
#define TE_GAME_FIRST TE_GAME_KEY_DOWN
|
||||
TE_GAME_KEY_DOWN,
|
||||
TE_GAME_KEY_UP,
|
||||
TE_GAME_AXIS_UD,
|
||||
TE_GAME_AXIS_LR,
|
||||
TE_GAME_PAUSE,
|
||||
#define TE_GAME_LAST TE_GAME_PAUSE
|
||||
|
||||
TE_GAMEPAD_BUTTON_DOWN,
|
||||
TE_GAMEPAD_BUTTON_UP,
|
||||
TE_GAMEPAD_AXIS,
|
||||
|
||||
TE_VIDEO_MODE_CHANGED,
|
||||
|
||||
NUM_TAISEI_EVENTS
|
||||
} TaiseiEvent;
|
||||
|
||||
typedef enum {
|
||||
EPRIO_DEFAULT = 0,
|
||||
|
||||
// from highest to lowest
|
||||
// feel free to add new prios as needed, just don't randomly reorder stuff
|
||||
|
||||
EPRIO_SYSTEM, // for events not associated with user input
|
||||
EPRIO_CAPTURE, // for capturing raw user input before it's further processed
|
||||
EPRIO_HOTKEYS, // for global keybindings
|
||||
EPRIO_TRANSLATION, // for translating raw input events into higher level Taisei events
|
||||
EPRIO_NORMAL, // for everything else
|
||||
|
||||
NUM_EPRIOS
|
||||
} EventPriority;
|
||||
|
||||
typedef enum {
|
||||
EFLAG_MENU = (1 << 0),
|
||||
EFLAG_GAME = (1 << 1),
|
||||
EFLAG_TEXT = (1 << 2),
|
||||
} EventFlags;
|
||||
|
||||
typedef enum {
|
||||
// EF_Keyboard
|
||||
E_KeyDown,
|
||||
E_KeyUp,
|
||||
#define EPRIO_DEFAULT_REMAP EPRIO_NORMAL
|
||||
|
||||
// EF_Text
|
||||
E_CharTyped,
|
||||
E_CharErased,
|
||||
E_SubmitText,
|
||||
E_CancelText,
|
||||
// if the this returns true, the event won't be passed down to lower priority handlers
|
||||
typedef bool (*EventHandlerProc)(SDL_Event *event, void *arg);
|
||||
|
||||
// EF_Menu
|
||||
E_CursorUp,
|
||||
E_CursorDown,
|
||||
E_CursorLeft,
|
||||
E_CursorRight,
|
||||
E_MenuAccept,
|
||||
E_MenuAbort,
|
||||
typedef struct EventHandler {
|
||||
EventHandlerProc proc;
|
||||
void *arg;
|
||||
EventPriority priority;
|
||||
uint32_t event_type; // if 0, this handler gets all events
|
||||
} EventHandler;
|
||||
|
||||
// EF_Game
|
||||
E_PlrKeyDown,
|
||||
E_PlrKeyUp,
|
||||
E_PlrAxisUD,
|
||||
E_PlrAxisLR,
|
||||
E_Pause,
|
||||
extern uint32_t sdl_first_user_event;
|
||||
|
||||
// EF_Gamepad
|
||||
E_GamepadKeyDown,
|
||||
E_GamepadKeyUp,
|
||||
E_GamepadAxis,
|
||||
E_GamepadAxisValue,
|
||||
// Convert a TaiseiEvent code to an SDL event type
|
||||
#define MAKE_TAISEI_EVENT(te_code) (sdl_first_user_event + (te_code))
|
||||
|
||||
// EF_Video
|
||||
E_VideoModeChanged,
|
||||
} EventType;
|
||||
// Convert an SDL event type to a TaiseiEvent code
|
||||
#define TAISEI_EVENT(sdl_etype) ((sdl_etype) - sdl_first_user_event)
|
||||
|
||||
extern struct sdl_custom_events_s {
|
||||
uint32_t resource_load_finished;
|
||||
uint32_t video_mode_changed;
|
||||
} sdl_custom_events;
|
||||
// Check if argument is a valid TaiseiEvent code
|
||||
#define TAISEI_EVENT_VALID(te_code) ((unsigned)(te_code) < NUM_TAISEI_EVENTS)
|
||||
|
||||
#define NUM_CUSTOM_EVENTS (sizeof(struct sdl_custom_events_s)/sizeof(uint32_t))
|
||||
#define FIRST_CUSTOM_EVENT (((uint32_t*)&sdl_custom_events)[0])
|
||||
#define LAST_CUSTOM_EVENT (((uint32_t*)&sdl_custom_events)[NUM_CUSTOM_EVENTS - 1])
|
||||
#define IS_CUSTOM_EVENT(e) ((e) >= FIRST_CUSTOM_EVENT && (e) <= LAST_CUSTOM_EVENT)
|
||||
|
||||
typedef void(*EventHandler)(EventType, int, void*);
|
||||
// Check if an SDL event type is a Taisei event
|
||||
#define IS_TAISEI_EVENT(sdl_etype) (TAISEI_EVENT_VALID(TAISEI_EVENT(sdl_etype)))
|
||||
|
||||
void events_init(void);
|
||||
void events_shutdown(void);
|
||||
void events_pause_keyrepeat(void);
|
||||
void handle_events(EventHandler handler, EventFlags flags, void *arg);
|
||||
bool gamekeypressed(KeyIndex key);
|
||||
|
||||
|
||||
void events_register_handler(EventHandler *handler);
|
||||
void events_unregister_handler(EventHandlerProc proc);
|
||||
void events_poll(EventHandler *handlers, EventFlags flags);
|
||||
void events_emit(TaiseiEvent type, int32_t code, void *data1, void *data2);
|
||||
|
|
133
src/gamepad.c
133
src/gamepad.c
|
@ -24,6 +24,8 @@ static struct {
|
|||
} devices;
|
||||
} gamepad;
|
||||
|
||||
static bool gamepad_event_handler(SDL_Event *event, void *arg);
|
||||
|
||||
static int gamepad_load_mappings(const char *vpath, int warn_noexist) {
|
||||
char *repr = vfs_repr(vpath, true);
|
||||
char *errstr = NULL;
|
||||
|
@ -240,6 +242,12 @@ void gamepad_init(void) {
|
|||
SDL_GameControllerEventState(SDL_ENABLE);
|
||||
|
||||
config_set_str(CONFIG_GAMEPAD_DEVICE, guid);
|
||||
|
||||
events_register_handler(&(EventHandler){
|
||||
.proc = gamepad_event_handler,
|
||||
.priority = EPRIO_TRANSLATION,
|
||||
});
|
||||
|
||||
gamepad.initialized = true;
|
||||
}
|
||||
|
||||
|
@ -261,6 +269,7 @@ void gamepad_shutdown(void) {
|
|||
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
|
||||
|
||||
memset(&gamepad, 0, sizeof(gamepad));
|
||||
events_unregister_handler(gamepad_event_handler);
|
||||
}
|
||||
|
||||
void gamepad_restart(void) {
|
||||
|
@ -304,22 +313,22 @@ int gamepad_gamekey2axisval(KeyIndex key) {
|
|||
}
|
||||
}
|
||||
|
||||
int gamepad_axis2menuevt(SDL_GameControllerAxis id, int val) {
|
||||
static int gamepad_axis2menuevt(SDL_GameControllerAxis id, int val) {
|
||||
if(id == SDL_CONTROLLER_AXIS_LEFTX || id == SDL_CONTROLLER_AXIS_RIGHTX)
|
||||
return val == AXISVAL_LEFT ? E_CursorLeft : E_CursorRight;
|
||||
return val == AXISVAL_LEFT ? TE_MENU_CURSOR_LEFT : TE_MENU_CURSOR_RIGHT;
|
||||
|
||||
if(id == SDL_CONTROLLER_AXIS_LEFTY || id == SDL_CONTROLLER_AXIS_RIGHTY)
|
||||
return val == AXISVAL_UP ? E_CursorUp : E_CursorDown;
|
||||
return val == AXISVAL_UP ? TE_MENU_CURSOR_UP : TE_MENU_CURSOR_DOWN;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int gamepad_axis2gameevt(SDL_GameControllerAxis id) {
|
||||
static int gamepad_axis2gameevt(SDL_GameControllerAxis id) {
|
||||
if(id == config_get_int(CONFIG_GAMEPAD_AXIS_LR))
|
||||
return E_PlrAxisLR;
|
||||
return TE_GAME_AXIS_LR;
|
||||
|
||||
if(id == config_get_int(CONFIG_GAMEPAD_AXIS_UD))
|
||||
return E_PlrAxisUD;
|
||||
return TE_GAME_AXIS_UD;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
@ -386,125 +395,113 @@ int gamepad_get_player_axis_value(GamepadPlrAxis paxis) {
|
|||
return gamepad_axis_process_value(id, SDL_GameControllerGetAxis(gamepad.device, id));
|
||||
}
|
||||
|
||||
void gamepad_axis(SDL_GameControllerAxis id, int raw, EventHandler handler, EventFlags flags, void *arg) {
|
||||
void gamepad_axis(SDL_GameControllerAxis id, int raw) {
|
||||
signed char *a = gamepad.axis;
|
||||
signed char val = AXISVAL(gamepad_axis_process_value_deadzone(raw));
|
||||
bool free = config_get_int(CONFIG_GAMEPAD_AXIS_FREE);
|
||||
bool restricted = !config_get_int(CONFIG_GAMEPAD_AXIS_FREE);
|
||||
|
||||
bool menu = flags & EF_Menu;
|
||||
bool game = flags & EF_Game;
|
||||
bool gp = flags & EF_Gamepad;
|
||||
events_emit(TE_GAMEPAD_AXIS, id, (void*)(intptr_t)raw, NULL);
|
||||
|
||||
if(game && free) {
|
||||
if(!restricted) {
|
||||
int evt = gamepad_axis2gameevt(id);
|
||||
if(evt >= 0) {
|
||||
handler(evt, gamepad_axis_process_value(id, raw), arg);
|
||||
events_emit(evt, gamepad_axis_process_value(id, raw), NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if(val) { // simulate press
|
||||
if(!a[id]) {
|
||||
a[id] = val;
|
||||
int key = gamepad_axis2gamekey(id, val);
|
||||
|
||||
if(game && !free) {
|
||||
int key = gamepad_axis2gamekey(id, val);
|
||||
if(key >= 0) {
|
||||
handler(E_PlrKeyDown, key, arg);
|
||||
if(key >= 0) {
|
||||
if(restricted) {
|
||||
events_emit(TE_GAME_KEY_DOWN, key, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if(menu) {
|
||||
int evt = gamepad_axis2menuevt(id, val);
|
||||
if(evt >= 0) {
|
||||
handler(evt, 0, arg);
|
||||
events_emit(evt, 0, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} else if(a[id]) { // simulate release
|
||||
if(game) {
|
||||
if(restricted) {
|
||||
int key = gamepad_axis2gamekey(id, a[id]);
|
||||
handler(E_PlrKeyUp, key, arg);
|
||||
|
||||
if(key >= 0) {
|
||||
events_emit(TE_GAME_KEY_UP, key, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
a[id] = AXISVAL_NULL;
|
||||
}
|
||||
|
||||
if(gp) {
|
||||
// we probably need a better way to pass more than an int to the handler...
|
||||
handler(E_GamepadAxis, id, arg);
|
||||
handler(E_GamepadAxisValue, raw, arg);
|
||||
}
|
||||
}
|
||||
|
||||
void gamepad_button(SDL_GameControllerButton button, int state, EventHandler handler, EventFlags flags, void *arg) {
|
||||
int menu = flags & EF_Menu;
|
||||
int game = flags & EF_Game;
|
||||
int gpad = flags & EF_Gamepad;
|
||||
|
||||
void gamepad_button(SDL_GameControllerButton button, int state) {
|
||||
int gpkey = config_gamepad_key_from_gamepad_button(button);
|
||||
int key = config_key_from_gamepad_key(gpkey);
|
||||
|
||||
if(state == SDL_PRESSED) {
|
||||
if(game) switch(button) {
|
||||
events_emit(TE_GAMEPAD_BUTTON_DOWN, button, NULL, NULL);
|
||||
|
||||
switch(button) {
|
||||
case SDL_CONTROLLER_BUTTON_START:
|
||||
events_emit(TE_MENU_ACCEPT, 0, NULL, NULL);
|
||||
events_emit(TE_GAME_PAUSE, 0, NULL, NULL);
|
||||
break;
|
||||
|
||||
case SDL_CONTROLLER_BUTTON_BACK:
|
||||
handler(E_Pause, 0, arg); break;
|
||||
events_emit(TE_MENU_ABORT, 0, NULL, NULL);
|
||||
events_emit(TE_GAME_PAUSE, 0, NULL, NULL);
|
||||
break;
|
||||
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_UP: events_emit(TE_MENU_CURSOR_UP, 0, NULL, NULL); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: events_emit(TE_MENU_CURSOR_DOWN, 0, NULL, NULL); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: events_emit(TE_MENU_CURSOR_LEFT, 0, NULL, NULL); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: events_emit(TE_MENU_CURSOR_RIGHT, 0, NULL, NULL); break;
|
||||
|
||||
case SDL_CONTROLLER_BUTTON_A:
|
||||
events_emit(TE_MENU_ACCEPT, 0, NULL, NULL);
|
||||
|
||||
case SDL_CONTROLLER_BUTTON_B:
|
||||
events_emit(TE_MENU_ABORT, 0, NULL, NULL);
|
||||
|
||||
default:
|
||||
if(key >= 0) {
|
||||
handler(E_PlrKeyDown, key, arg);
|
||||
events_emit(TE_GAME_KEY_DOWN, key, NULL, NULL);
|
||||
} break;
|
||||
}
|
||||
|
||||
if(menu) switch(button) {
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_UP: handler(E_CursorUp, 0, arg); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: handler(E_CursorDown, 0, arg); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: handler(E_CursorLeft, 0, arg); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: handler(E_CursorRight, 0, arg); break;
|
||||
|
||||
case SDL_CONTROLLER_BUTTON_A:
|
||||
case SDL_CONTROLLER_BUTTON_START:
|
||||
handler(E_MenuAccept, 0, arg); break;
|
||||
|
||||
case SDL_CONTROLLER_BUTTON_B:
|
||||
case SDL_CONTROLLER_BUTTON_BACK:
|
||||
handler(E_MenuAbort, 0, arg); break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
if(gpad) {
|
||||
handler(E_GamepadKeyDown, button, arg);
|
||||
}
|
||||
} else {
|
||||
if(game && key >= 0) {
|
||||
handler(E_PlrKeyUp, key, arg);
|
||||
}
|
||||
events_emit(TE_GAMEPAD_BUTTON_UP, button, NULL, NULL);
|
||||
|
||||
if(gpad) {
|
||||
handler(E_GamepadKeyUp, button, arg);
|
||||
if(key >= 0) {
|
||||
events_emit(TE_GAME_KEY_UP, key, NULL, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gamepad_event(SDL_Event *event, EventHandler handler, EventFlags flags, void *arg) {
|
||||
if(!gamepad.initialized)
|
||||
return;
|
||||
static bool gamepad_event_handler(SDL_Event *event, void *arg) {
|
||||
assert(gamepad.initialized);
|
||||
|
||||
switch(event->type) {
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
if(event->caxis.which == gamepad.instance) {
|
||||
gamepad_axis(event->caxis.axis, event->caxis.value, handler, flags, arg);
|
||||
gamepad_axis(event->caxis.axis, event->caxis.value);
|
||||
}
|
||||
break;
|
||||
return true;
|
||||
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
if(event->cbutton.which == gamepad.instance) {
|
||||
gamepad_button(event->cbutton.button, event->cbutton.state, handler, flags, arg);
|
||||
gamepad_button(event->cbutton.button, event->cbutton.state);
|
||||
}
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int gamepad_devicecount(void) {
|
||||
|
|
|
@ -27,3 +27,10 @@ void init_global(CLIAction *cli) {
|
|||
log_warn("FPS limiter disabled. Gotta go fast! (frameskip = %i)", global.frameskip);
|
||||
}
|
||||
}
|
||||
|
||||
// Inputdevice-agnostic method of checking whether a game control is pressed.
|
||||
// ALWAYS use this instead of SDL_GetKeyState if you need it.
|
||||
// XXX: Move this somewhere?
|
||||
bool gamekeypressed(KeyIndex key) {
|
||||
return SDL_GetKeyboardState(NULL)[config_get_int(KEYIDX_TO_CFGIDX(key))] || gamepad_gamekeypressed(key);
|
||||
}
|
||||
|
|
|
@ -124,4 +124,5 @@ extern Global global;
|
|||
|
||||
void init_global(CLIAction *cli);
|
||||
|
||||
|
||||
// XXX: Move this somewhere?
|
||||
bool gamekeypressed(KeyIndex key);
|
||||
|
|
62
src/list.c
62
src/list.c
|
@ -12,24 +12,21 @@
|
|||
#include <stdio.h>
|
||||
#include "global.h"
|
||||
|
||||
typedef struct {
|
||||
void *next;
|
||||
void *prev;
|
||||
} List;
|
||||
|
||||
void *_FREEREF;
|
||||
|
||||
void *create_element(void **dest, int size) {
|
||||
List *e = malloc(size);
|
||||
void *create_element(void **dest, size_t size) {
|
||||
assert(dest != NULL);
|
||||
assert(size > 0);
|
||||
|
||||
List *e = calloc(1, size);
|
||||
List **d = (List **)dest;
|
||||
|
||||
e->next = NULL;
|
||||
e->prev = *d;
|
||||
|
||||
if(*d != NULL) {
|
||||
e->next = (*d)->next;
|
||||
if((*d)->next)
|
||||
((List *)(*d)->next)->prev = e;
|
||||
(*d)->next->prev = e;
|
||||
|
||||
(*d)->next = e;
|
||||
} else {
|
||||
|
@ -39,10 +36,57 @@ void *create_element(void **dest, int size) {
|
|||
return e;
|
||||
}
|
||||
|
||||
void* create_element_at_priority(void **list_head, size_t size, int prio, int (*prio_func)(void*)) {
|
||||
assert(list_head != NULL);
|
||||
assert(size > 0);
|
||||
assert(prio_func != NULL);
|
||||
|
||||
List *elem = calloc(1, size);
|
||||
|
||||
if(!*list_head) {
|
||||
*list_head = elem;
|
||||
log_debug("PRIO: %i", prio);
|
||||
return elem;
|
||||
}
|
||||
|
||||
List *dest = *list_head;
|
||||
|
||||
for(List *e = dest; e && prio_func(e) <= prio; e = e->next) {
|
||||
dest = e;
|
||||
}
|
||||
|
||||
if(dest == *list_head && prio_func(dest) > prio) {
|
||||
elem->next = dest;
|
||||
elem->prev = dest->prev;
|
||||
|
||||
if(elem->prev) {
|
||||
elem->prev->next = elem;
|
||||
}
|
||||
|
||||
dest->prev = elem;
|
||||
*list_head = elem;
|
||||
} else {
|
||||
elem->prev = dest;
|
||||
elem->next = dest->next;
|
||||
|
||||
if(dest->next) {
|
||||
dest->next->prev = elem;
|
||||
}
|
||||
|
||||
dest->next = elem;
|
||||
}
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
ListContainer* create_container(ListContainer **dest) {
|
||||
return create_element((void**)dest, sizeof(ListContainer));
|
||||
}
|
||||
|
||||
ListContainer* create_container_at_priority(ListContainer **list_head, int prio, int (*prio_func)(void*)) {
|
||||
return create_element_at_priority((void**)list_head, sizeof(ListContainer), prio, prio_func);
|
||||
}
|
||||
|
||||
void delete_element(void **dest, void *e) {
|
||||
if(((List *)e)->prev != NULL)
|
||||
((List *)((List *)e)->prev)->next = ((List *)e)->next;
|
||||
|
|
15
src/list.h
15
src/list.h
|
@ -12,18 +12,27 @@
|
|||
* so i do some void-magic here to save the lines.
|
||||
*/
|
||||
|
||||
void *create_element(void **dest, int size);
|
||||
#include <stdlib.h>
|
||||
|
||||
void* create_element(void **dest, size_t size);
|
||||
void* create_element_at_priority(void **list_head, size_t size, int prio, int (*prio_func)(void*));
|
||||
void delete_element(void **dest, void *e);
|
||||
void delete_all_elements(void **dest, void (callback)(void **, void *));
|
||||
void delete_all_elements_witharg(void **dest, void (callback)(void **, void *, void *), void *arg);
|
||||
|
||||
typedef struct List {
|
||||
struct List *next;
|
||||
struct List *prev;
|
||||
} List;
|
||||
|
||||
typedef struct ListContainer {
|
||||
void *next;
|
||||
void *prev;
|
||||
struct ListContainer *next;
|
||||
struct ListContainer *prev;
|
||||
void *data;
|
||||
} ListContainer;
|
||||
|
||||
ListContainer* create_container(ListContainer **dest);
|
||||
ListContainer* create_container_at_priority(ListContainer **list_head, int prio, int (*prio_func)(void*));
|
||||
|
||||
typedef struct {
|
||||
void *ptr;
|
||||
|
|
|
@ -38,6 +38,7 @@ static void taisei_shutdown(void) {
|
|||
stage_free_array();
|
||||
config_uninit();
|
||||
vfs_uninit();
|
||||
events_shutdown();
|
||||
|
||||
log_info("Good bye");
|
||||
SDL_Quit();
|
||||
|
@ -182,8 +183,8 @@ int main(int argc, char **argv) {
|
|||
|
||||
init_sdl();
|
||||
init_global(&a);
|
||||
init_fonts();
|
||||
events_init();
|
||||
init_fonts();
|
||||
video_init();
|
||||
init_resources();
|
||||
draw_loading_screen();
|
||||
|
|
|
@ -134,23 +134,24 @@ void draw_char_menu(MenuData *menu) {
|
|||
glColor3f(1,1,1);
|
||||
}
|
||||
|
||||
void char_menu_input_event(EventType type, int state, void *arg) {
|
||||
bool char_menu_input_handler(SDL_Event *event, void *arg) {
|
||||
MenuData *menu = arg;
|
||||
MenuData *mod = menu->context;
|
||||
TaiseiEvent type = TAISEI_EVENT(event->type);
|
||||
|
||||
if(type == E_CursorRight) {
|
||||
if(type == TE_MENU_CURSOR_RIGHT) {
|
||||
play_ui_sound("generic_shot");
|
||||
menu->cursor++;
|
||||
} else if(type == E_CursorLeft) {
|
||||
} else if(type == TE_MENU_CURSOR_LEFT) {
|
||||
play_ui_sound("generic_shot");
|
||||
menu->cursor--;
|
||||
} else if(type == E_CursorDown) {
|
||||
} else if(type == TE_MENU_CURSOR_DOWN) {
|
||||
play_ui_sound("generic_shot");
|
||||
mod->cursor++;
|
||||
} else if(type == E_CursorUp) {
|
||||
} else if(type == TE_MENU_CURSOR_UP) {
|
||||
play_ui_sound("generic_shot");
|
||||
mod->cursor--;
|
||||
} else if(type == E_MenuAccept) {
|
||||
} else if(type == TE_MENU_ACCEPT) {
|
||||
play_ui_sound("shot_special1");
|
||||
mod->selected = mod->cursor;
|
||||
close_menu(mod);
|
||||
|
@ -159,7 +160,7 @@ void char_menu_input_event(EventType type, int state, void *arg) {
|
|||
|
||||
// XXX: This needs a better fix
|
||||
set_shotmode(mod, mod->entries[mod->selected].arg);
|
||||
} else if(type == E_MenuAbort) {
|
||||
} else if(type == TE_MENU_ABORT) {
|
||||
play_ui_sound("hit");
|
||||
close_menu(menu);
|
||||
close_menu(mod);
|
||||
|
@ -167,10 +168,15 @@ void char_menu_input_event(EventType type, int state, void *arg) {
|
|||
|
||||
menu->cursor = (menu->cursor % menu->ecount) + menu->ecount*(menu->cursor < 0);
|
||||
mod->cursor = (mod->cursor % mod->ecount) + mod->ecount*(mod->cursor < 0);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void char_menu_input(MenuData *menu) {
|
||||
handle_events(char_menu_input_event, EF_Menu, menu);
|
||||
events_poll((EventHandler[]){
|
||||
{ .proc = char_menu_input_handler, .arg = menu },
|
||||
{NULL}
|
||||
}, EFLAG_MENU);
|
||||
}
|
||||
|
||||
void free_char_menu(MenuData *menu) {
|
||||
|
|
|
@ -86,11 +86,12 @@ float menu_fade(MenuData *menu) {
|
|||
return transition.fade;
|
||||
}
|
||||
|
||||
void menu_event(EventType type, int state, void *arg) {
|
||||
bool menu_input_handler(SDL_Event *event, void *arg) {
|
||||
MenuData *menu = arg;
|
||||
TaiseiEvent te = TAISEI_EVENT(event->type);
|
||||
|
||||
switch(type) {
|
||||
case E_CursorDown:
|
||||
switch(te) {
|
||||
case TE_MENU_CURSOR_DOWN:
|
||||
play_ui_sound("generic_shot");
|
||||
do {
|
||||
if(++menu->cursor >= menu->ecount)
|
||||
|
@ -98,7 +99,7 @@ void menu_event(EventType type, int state, void *arg) {
|
|||
} while(menu->entries[menu->cursor].action == NULL);
|
||||
break;
|
||||
|
||||
case E_CursorUp:
|
||||
case TE_MENU_CURSOR_UP:
|
||||
play_ui_sound("generic_shot");
|
||||
do {
|
||||
if(--menu->cursor < 0)
|
||||
|
@ -106,7 +107,7 @@ void menu_event(EventType type, int state, void *arg) {
|
|||
} while(menu->entries[menu->cursor].action == NULL);
|
||||
break;
|
||||
|
||||
case E_MenuAccept:
|
||||
case TE_MENU_ACCEPT:
|
||||
play_ui_sound("shot_special1");
|
||||
if(menu->entries[menu->cursor].action) {
|
||||
menu->selected = menu->cursor;
|
||||
|
@ -114,7 +115,7 @@ void menu_event(EventType type, int state, void *arg) {
|
|||
}
|
||||
break;
|
||||
|
||||
case E_MenuAbort:
|
||||
case TE_MENU_ABORT:
|
||||
play_ui_sound("hit");
|
||||
if(menu->flags & MF_Abortable) {
|
||||
menu->selected = -1;
|
||||
|
@ -124,14 +125,19 @@ void menu_event(EventType type, int state, void *arg) {
|
|||
|
||||
default: break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void menu_input(MenuData *menu) {
|
||||
handle_events(menu_event, EF_Menu, (void*)menu);
|
||||
events_poll((EventHandler[]){
|
||||
{ .proc = menu_input_handler, .arg = menu },
|
||||
{NULL}
|
||||
}, EFLAG_MENU);
|
||||
}
|
||||
|
||||
void menu_no_input(MenuData *menu) {
|
||||
handle_events(NULL, 0, NULL);
|
||||
events_poll(NULL, 0);
|
||||
}
|
||||
|
||||
void menu_logic(MenuData *menu) {
|
||||
|
|
|
@ -94,6 +94,4 @@ int menu_loop(MenuData *menu);
|
|||
|
||||
float menu_fade(MenuData *menu);
|
||||
|
||||
void menu_event(EventType type, int state, void *arg);
|
||||
|
||||
|
||||
bool menu_input_handler(SDL_Event *event, void *arg);
|
||||
|
|
|
@ -879,12 +879,8 @@ void draw_options_menu(MenuData *menu) {
|
|||
|
||||
// --- Input/event processing --- //
|
||||
|
||||
static void notify_bindings(EventType type, int state, MenuData *menu) {
|
||||
// FIXME: this won't be called when input is blocked (we need a better event system)
|
||||
|
||||
if(type != E_VideoModeChanged) {
|
||||
return;
|
||||
}
|
||||
static bool options_vidmode_change_handler(SDL_Event *event, void *arg) {
|
||||
MenuData *menu = arg;
|
||||
|
||||
for(int i = 0; i < menu->ecount; ++i) {
|
||||
OptionBinding *bind = bind_get(menu, i);
|
||||
|
@ -908,153 +904,145 @@ static void notify_bindings(EventType type, int state, MenuData *menu) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void bind_input_event(EventType type, int state, void *arg) {
|
||||
static bool options_input_handler_for_binding(SDL_Event *event, void *arg) {
|
||||
if(!arg) {
|
||||
return false;
|
||||
}
|
||||
|
||||
OptionBinding *b = arg;
|
||||
uint32_t t = event->type;
|
||||
|
||||
int scan = state;
|
||||
char c = (char)(((Uint16)state) & 0x7F);
|
||||
char *dest = b->type == BT_StrValue? *b->values : NULL;
|
||||
if(t == SDL_KEYDOWN) {
|
||||
SDL_Scancode scan = event->key.keysym.scancode;
|
||||
bool esc = scan == SDL_SCANCODE_ESCAPE;
|
||||
|
||||
switch(type) {
|
||||
case E_KeyDown: {
|
||||
int esc = scan == SDL_SCANCODE_ESCAPE;
|
||||
if(b->type == BT_GamepadKeyBinding || b->type == BT_GamepadAxisBinding) {
|
||||
if(esc)
|
||||
b->blockinput = false;
|
||||
break;
|
||||
if(b->type != BT_KeyBinding) {
|
||||
if(esc) {
|
||||
b->blockinput = false;
|
||||
}
|
||||
|
||||
if(!esc) {
|
||||
for(int i = CONFIG_KEY_FIRST; i <= CONFIG_KEY_LAST; ++i) {
|
||||
if(config_get_int(i) == scan) {
|
||||
config_set_int(i, config_get_int(b->configentry));
|
||||
}
|
||||
}
|
||||
|
||||
config_set_int(b->configentry, scan);
|
||||
}
|
||||
|
||||
b->blockinput = false;
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
|
||||
case E_GamepadKeyDown: {
|
||||
if(b->type != BT_GamepadKeyBinding) {
|
||||
if(b->type == BT_GamepadAxisBinding)
|
||||
b->blockinput = false;
|
||||
break;
|
||||
} else if(scan == SDL_CONTROLLER_BUTTON_BACK || scan == SDL_CONTROLLER_BUTTON_START) {
|
||||
b->blockinput = false;
|
||||
break;
|
||||
}
|
||||
|
||||
for(int i = CONFIG_GAMEPAD_KEY_FIRST; i <= CONFIG_GAMEPAD_KEY_LAST; ++i) {
|
||||
if(!esc) {
|
||||
for(int i = CONFIG_KEY_FIRST; i <= CONFIG_KEY_LAST; ++i) {
|
||||
if(config_get_int(i) == scan) {
|
||||
config_set_int(i, config_get_int(b->configentry));
|
||||
}
|
||||
}
|
||||
|
||||
config_set_int(b->configentry, scan);
|
||||
b->blockinput = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case E_GamepadAxis: {
|
||||
if(b->type == BT_GamepadAxisBinding) {
|
||||
if(b->configentry == CONFIG_GAMEPAD_AXIS_UD) {
|
||||
if(config_get_int(CONFIG_GAMEPAD_AXIS_LR) == state) {
|
||||
config_set_int(CONFIG_GAMEPAD_AXIS_LR, config_get_int(CONFIG_GAMEPAD_AXIS_UD));
|
||||
}
|
||||
} else if(b->configentry == CONFIG_GAMEPAD_AXIS_LR) {
|
||||
if(config_get_int(CONFIG_GAMEPAD_AXIS_UD) == state) {
|
||||
config_set_int(CONFIG_GAMEPAD_AXIS_UD, config_get_int(CONFIG_GAMEPAD_AXIS_LR));
|
||||
}
|
||||
}
|
||||
b->blockinput = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
config_set_int(b->configentry, state);
|
||||
if(t == MAKE_TAISEI_EVENT(TE_GAMEPAD_BUTTON_DOWN)) {
|
||||
SDL_GameControllerButton button = event->user.code;
|
||||
|
||||
if(b->type != BT_GamepadKeyBinding) {
|
||||
if(b->type == BT_GamepadAxisBinding) {
|
||||
b->blockinput = false;
|
||||
}
|
||||
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
|
||||
case E_CharTyped: {
|
||||
if(c != ':') {
|
||||
char s[] = {c, 0};
|
||||
strlcat(dest, s, OPTIONS_TEXT_INPUT_BUFSIZE);
|
||||
if(button == SDL_CONTROLLER_BUTTON_BACK || button == SDL_CONTROLLER_BUTTON_START) {
|
||||
b->blockinput = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
for(int i = CONFIG_GAMEPAD_KEY_FIRST; i <= CONFIG_GAMEPAD_KEY_LAST; ++i) {
|
||||
if(config_get_int(i) == button) {
|
||||
config_set_int(i, config_get_int(b->configentry));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case E_CharErased: {
|
||||
if(dest != NULL && strlen(dest))
|
||||
dest[strlen(dest)-1] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case E_SubmitText: {
|
||||
if(dest != NULL && strlen(dest))
|
||||
config_set_str(b->configentry, dest);
|
||||
else
|
||||
strlcpy(dest, config_get_str(b->configentry), OPTIONS_TEXT_INPUT_BUFSIZE);
|
||||
b->blockinput = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case E_CancelText: {
|
||||
strlcpy(dest, config_get_str(b->configentry), OPTIONS_TEXT_INPUT_BUFSIZE);
|
||||
b->blockinput = false;
|
||||
break;
|
||||
}
|
||||
|
||||
default: break;
|
||||
config_set_int(b->configentry, button);
|
||||
b->blockinput = false;
|
||||
return true;
|
||||