From 85bd92eba496f6592c4196a3fca4336686ee1b71 Mon Sep 17 00:00:00 2001 From: "S. Gilles" Date: Thu, 3 Nov 2016 04:27:10 -0400 Subject: [PATCH] Rudimentary event handling --- Makefile | 2 +- clav.c | 92 +++++++++++++++++++++++++++++++++++- ui-sdl.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- ui.h | 33 ++++++++++++- 4 files changed, 275 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 4ae97a6..dd6a928 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ CFLAGS += -std=c99 -D_XOPEN_SOURCE LD ?= gcc # Debug -CFLAGS += -g -O0 -pedantic -Wall -Wextra +CFLAGS += -g -O0 -pedantic -Wall -Wextra -Werror SDLPROG = clav-sdl diff --git a/clav.c b/clav.c index 6e6fe41..db25b56 100644 --- a/clav.c +++ b/clav.c @@ -1,23 +1,111 @@ #include +#include #include #include "macros.h" #include "ui.h" +/* The general state of the application */ +static enum { + ST_DEFAULT = 0, + ST_ASKING_QUIT, + ST_SHOULD_QUIT +} state; + +/* Having received some kind of input, change state accordingly */ +static int state_transition(struct ui_event *e) +{ + int ret = 0; + + switch (state) { + case ST_SHOULD_QUIT: + break; + case ST_DEFAULT: + + switch (e->type) { + case ET_INITIAL_QUIT: + ret = ui_confirm_deny("Quit?"); + state = ST_ASKING_QUIT; + break; + case ET_FORCE_QUIT: + state = ST_SHOULD_QUIT; + break; + case ET_NONE: + case ET_CONFIRM: + case ET_DENY: + break; + } + + break; + case ST_ASKING_QUIT: + + switch (e->type) { + case ET_CONFIRM: + case ET_FORCE_QUIT: + state = ST_SHOULD_QUIT; + break; + case ET_DENY: + state = ST_DEFAULT; + break; + case ET_NONE: + case ET_INITIAL_QUIT: + break; + } + + break; + } + + if (ret) { + state = ST_SHOULD_QUIT; + } + + return ret; +} + +/* Main loop */ int main(int argc, char **argv) { int ret; + uint_fast8_t more_evs = 0; + struct ui_event e = { 0 }; UNUSED(argc); UNUSED(argv); setlocale(LC_ALL, ""); - if ((ret = init_ui())) { + if ((ret = ui_init())) { + goto done; + } + + if ((ret = ui_start_frame())) { goto done; } + while (state != ST_SHOULD_QUIT) { + /* includes framelimiting delay */ + if ((ret = ui_finish_frame())) { + goto done; + } + + more_evs = 1; + + while (more_evs) { + if ((ret = ui_get_event(&e, &more_evs))) { + goto done; + } + + if (e.type) { + state_transition(&e); + } + } + + if ((ret = ui_start_frame())) { + goto done; + } + } + done: - teardown_ui(); + ui_teardown(); return ret; } diff --git a/ui-sdl.c b/ui-sdl.c index 6bdea08..9d32777 100644 --- a/ui-sdl.c +++ b/ui-sdl.c @@ -1,20 +1,90 @@ #include #include +#include +#include #include #include "macros.h" #include "ui.h" +#define TICKS_PER_FRAME (1000 / 60) + /* The window we'll be using */ -static SDL_Window *sdl_win = 0; +static SDL_Window *sdl_win; /* The screen surface we'll be using */ -static SDL_Surface *sdl_surf = 0; +static SDL_Surface *sdl_surf; + +/* How to limit the framerate */ +static Uint32 frame_start_ticks; + +/* If conf/deny, the message onscreen */ +static const char *conf_deny_text; + +/* Buffer for event queue */ +static struct ui_event *eq_buf; + +/* Current max length of event queue */ +static size_t eq_len; + +/* Current head of queue */ +static size_t eq_head; + +/* Current tail of queue */ +static size_t eq_tail; + +/* Pop from queue */ +static void eq_pop(struct ui_event *out) +{ + if (eq_head == eq_tail) { + *out = (struct ui_event) { 0 }; + + return; + } + + memcpy(out, eq_buf + eq_head, sizeof *out); + eq_buf[eq_head] = (struct ui_event) { 0 }; + eq_head = (eq_head + 1) % eq_len; +} + +/* Push into queue */ +static int eq_push(struct ui_event *in) +{ + void *newmem; + int sv_err; + + if (((eq_tail + 1) % eq_len) == eq_head) { + if ((eq_len * sizeof *in) >= ((size_t) -1) / 2) { + fprintf(stderr, L( + "eq_push: Impossibly large buffer\n")); + + return ENOMEM; + } + + if (!(newmem = realloc(eq_buf, (eq_len * 2) * + sizeof *eq_buf))) { + sv_err = errno; + perror(L("realloc")); + + return sv_err; + } + + eq_buf = (struct ui_event *) newmem; + eq_len *= 2; + } + + memcpy(eq_buf + eq_tail, in, sizeof *in); + eq_tail = (eq_tail + 1) % eq_len; + + return 0; +} /* Initialize SDL */ -int init_ui(void) +int ui_init(void) { + int sv_err; + if (SDL_Init(SDL_INIT_VIDEO) < 0) { fprintf(stderr, L("SDL_Init(): %s\n"), SDL_GetError()); @@ -41,16 +111,27 @@ int init_ui(void) return ENOMEDIUM; } - SDL_FillRect(sdl_surf, 0, SDL_MapRGB(sdl_surf->format, 0xff, 0xff, - 0xff)); + SDL_FillRect(sdl_surf, 0, SDL_MapRGB(sdl_surf->format, 0xe2, 0xe2, + 0xe2)); SDL_UpdateWindowSurface(sdl_win); - SDL_Delay(2000); + + /* Set up queue for returning data */ + if (!(eq_buf = calloc(2, sizeof *eq_buf))) { + sv_err = errno; + perror(L("malloc")); + + return sv_err; + } + + eq_len = 2; + eq_head = 0; + eq_tail = 0; return 0; } /* Tear down SDL */ -int teardown_ui(void) +int ui_teardown(void) { if (sdl_win) { SDL_DestroyWindow(sdl_win); @@ -60,3 +141,68 @@ int teardown_ui(void) return 0; } + +/* Record that a frame has been started */ +int ui_start_frame(void) +{ + frame_start_ticks = SDL_GetTicks(); + + return 0; +} + +/* Draw a frame, possibly sleeping for framelimit */ +int ui_finish_frame(void) +{ + Uint32 now = 0; + Uint32 elapsed_time = 0; + SDL_Event sdl_e = { 0 }; + struct ui_event ui_e = { 0 }; + + /* Draw the damn thing */ + + /* + * draw(); + */ + + /* Handle user input */ + while (SDL_PollEvent(&sdl_e) != 0) { + switch (sdl_e.type) { + case SDL_QUIT: + ui_e = (struct ui_event) { .type = ET_FORCE_QUIT }; + eq_push(&ui_e); + break; + case SDL_KEYUP: + break; + } + } + + /* framelimit */ + now = SDL_GetTicks(); + + if (frame_start_ticks < now) { + elapsed_time = now - frame_start_ticks; + + if (elapsed_time < TICKS_PER_FRAME) { + SDL_Delay(TICKS_PER_FRAME - elapsed_time); + } + } + + return 0; +} + +/* Return an event to the main loop */ +int ui_get_event(struct ui_event *e, uint_fast8_t *more) +{ + eq_pop(e); + *more = eq_head != eq_tail; + + return 0; +} + +/* Be told to display a confirm dialog */ +int ui_confirm_deny(const char *s) +{ + conf_deny_text = s; + + return 0; +} diff --git a/ui.h b/ui.h index 41c9bcc..34d4557 100644 --- a/ui.h +++ b/ui.h @@ -1,7 +1,36 @@ +enum event_type { + ET_NONE = 0, + ET_INITIAL_QUIT, + ET_FORCE_QUIT, + ET_CONFIRM, + ET_DENY, +}; + +struct ui_event { + /* What event this is */ + enum event_type type; +}; + /* Initialize whatever UI is present */ int -init_ui(void); +ui_init(void); /* Tear down whatever UI was made */ int -teardown_ui(void); +ui_teardown(void); + +/* Record that a frame has been started */ +int +ui_start_frame(void); + +/* Draw a frame, sleep to framelimit */ +int +ui_finish_frame(void); + +/* Get some kind of ui-agnostic event (like `quit' or `mutate' */ +int +ui_get_event(struct ui_event *e, uint_fast8_t *more); + +/* Display a message. Y/n eventually handed back through ui_get_event() */ +int +ui_confirm_deny(const char *s); -- 2.11.4.GIT