Game state implementation

This commit is contained in:
2025-07-21 21:00:00 +02:00
parent 97249a0da1
commit 6623314156
30 changed files with 750 additions and 659 deletions
+7 -2
View File
@@ -21,6 +21,7 @@ uidummy=res/shaders/uidummy.vert;res/shaders/uidummy.frag
[Textures]
font0=res/textures/font_9x20.png
icons=res/textures/icons.png
logo=res/textures/logo.png
terrain0=res/textures/dirt.png
terrain1=res/textures/drygrass.png
@@ -40,12 +41,16 @@ clouds=res/textures/clouds.png
# heightmap bitmap is multiplied by `heightmult`, and that's how many
# metres 1 is.
[Map]
heightmult=16
height_multiplier=16
heightmap=res/map/heightmap.png
texturemap=res/map/texturemap.png
entitymap=res/map/entitymap.png
[UIDefs]
tooltip=res/scripts/ui/tooltip.scm
main_menu=res/scripts/ui/debug_window.scm
# The scripts are loaded and executed after everything above has been
# loaded, so that Guile has some stuff to work with.
[Scripts]
global=res/scripts/ui_styles.scm;res/scripts/ui_composites.scm
global=res/scripts/global/ui_styles.scm;res/scripts/global/ui_composites.scm
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

+3
View File
@@ -42,4 +42,7 @@
(define style-tooltip
(style-new
#:text-color COLOR_WHITE
#:text-color-hovered COLOR_WHITE
#:text-color-clicked COLOR_WHITE
#:background-color COLOR_BLACK))
+2 -3
View File
@@ -1,4 +1,3 @@
(use-modules (ice-9 format))
;; The function is evaulated and returns a pointer, the game eats it
;; and adds it to the UI root. Boom.
(widget-new
@@ -17,8 +16,8 @@
#:layout-spacing 8
#:horizontal #t
(widget-label-gvar 'float "Delta time: ~,2f" "delta_time" #:style style-default #:layout-weight 1 #:h 20 #:text-alignment 'center)
(widget-label-gvar 'int "Chunks: ~d" "frame_chunks" #:style style-default #:layout-weight 0.5 #:h 20)
(widget-label-gvar 'int "Entities: ~d" "frame_entities" #:style style-default #:layout-weight 0.5 #:h 20)
(widget-label-gvar 'int "~d chunks" "frame_chunks" #:style style-default #:layout-weight 0.5 #:h 20)
(widget-label-gvar 'int "~d entities" "frame_entities" #:style style-default #:layout-weight 0.5 #:h 20)
(widget-checkbox "Flight" "flight_enabled" #:layout-weight 0.5 #:h 20)
(widget-checkbox "Wireframe" "wireframe_enabled" #:layout-weight 0.5 #:h 20)
(widget-spinner 'int "Render distance: ~d" "render_distance" #:layout-weight 1 #:h 20)))
+6 -9
View File
@@ -1,9 +1,6 @@
(widget-new
'flowbox
#:style style-tooltip
#:w 200
#:visible #f
#:horizontal #t
#:layout-padding 4
#:layout-spacing 8
(widget-new 'multiline #:line-height 20 #:layout-weight 1 #:style style-default))
(widget-new 'multiline
#:w 200
#:visible #f
#:layout-padding 4
#:line-height 20
#:style style-tooltip)
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

-1
View File
@@ -10,7 +10,6 @@
#include "templates.h"
#include "../gl/mesh.h"
#include "../util.h"
#include "../state.h"
#include "../chunk/chunkman.h"
#include "../graphics.h"
#include "../gvar.h"
-146
View File
@@ -1,146 +0,0 @@
#include "evtman.h"
extern vec3 camera_rotation;
static GList *handlers = NULL;
gboolean
evt_pred_key (SDL_Event *event, void *data)
{
unsigned int expected_key = GPOINTER_TO_INT (data);
return event->key.key == expected_key;
}
gboolean
evt_pred_mouse (SDL_Event *event, void *data)
{
unsigned int expected_button = GPOINTER_TO_INT (data);
return event->button.button == expected_button;
}
gboolean
evt_pred_flag (SDL_Event *event, void *data)
{
UNUSED (event);
char *flag = data;
return FLAG_GET (flag);
}
gboolean
evt_cb_rotate_camera (SDL_Event *event, void *userdata)
{
UNUSED (userdata);
camera_rotation[0] -= event->motion.xrel / 100.f;
camera_rotation[1] -= event->motion.yrel / 100.f;
camera_rotation[1] =
glm_clamp (camera_rotation[1], -M_PI / 2 + 0.025, M_PI / 2 - 0.025);
return true;
}
gboolean
evt_cb_flag_off (SDL_Event *event, void *data)
{
UNUSED (event);
char *flag = data;
FLAG_SET (flag, FALSE);
return true;
}
gboolean
evt_cb_flag_on (SDL_Event *event, void *data)
{
UNUSED (event);
char *flag = data;
FLAG_SET (flag, TRUE);
return true;
}
gboolean
evt_cb_flag_switch (SDL_Event *event, void *data)
{
UNUSED (event);
char *flag = data;
FLAG_SWITCH (flag);
return true;
}
void
evtman_init ()
{
#define KEY(k) GINT_TO_POINTER(k)
evtman_add_handler (2, SDL_EVENT_WINDOW_RESIZED, NULL, NULL, graphics_cb_window_resize, NULL);
evtman_add_handler (2, SDL_EVENT_WINDOW_RESIZED, NULL, NULL, uiman_cb_window_resize, NULL);
/* The UI effectively eats most SDL events, as it needs it for it's
own calculations. */
evtman_add_handler (2, SDL_EVENT_KEY_DOWN, evt_pred_key, KEY (SDLK_TAB), uiman_cb_switch_focus, NULL);
evtman_add_handler (1, SDL_EVENT_KEY_DOWN, evt_pred_flag, "ui_has_focus", uiman_cb_key_down, NULL);
evtman_add_handler (1, SDL_EVENT_KEY_UP, evt_pred_flag, "ui_has_focus", uiman_cb_key_up, NULL);
evtman_add_handler (1, SDL_EVENT_MOUSE_MOTION, evt_pred_flag, "ui_has_focus", uiman_cb_mouse_motion, NULL);
evtman_add_handler (1, SDL_EVENT_MOUSE_BUTTON_DOWN, evt_pred_flag, "ui_has_focus", uiman_cb_button_down, NULL);
evtman_add_handler (1, SDL_EVENT_MOUSE_BUTTON_UP, evt_pred_flag, "ui_has_focus", uiman_cb_button_up, NULL);
evtman_add_handler (1, SDL_EVENT_MOUSE_WHEEL, evt_pred_flag, "ui_has_focus", uiman_cb_mouse_wheel, NULL);
evtman_add_handler (1, SDL_EVENT_TEXT_INPUT, evt_pred_flag, "ui_has_focus", uiman_cb_text_input, NULL);
evtman_add_handler (0, SDL_EVENT_KEY_DOWN, evt_pred_key, KEY (SDLK_W), evt_cb_flag_on, "moving_forward");
evtman_add_handler (0, SDL_EVENT_KEY_UP, evt_pred_key, KEY (SDLK_W), evt_cb_flag_off, "moving_forward");
evtman_add_handler (0, SDL_EVENT_KEY_DOWN, evt_pred_key, KEY (SDLK_S), evt_cb_flag_on, "moving_backward");
evtman_add_handler (0, SDL_EVENT_KEY_UP, evt_pred_key, KEY (SDLK_S), evt_cb_flag_off, "moving_backward");
evtman_add_handler (0, SDL_EVENT_KEY_DOWN, evt_pred_key, KEY (SDLK_A), evt_cb_flag_on, "moving_left");
evtman_add_handler (0, SDL_EVENT_KEY_UP, evt_pred_key, KEY (SDLK_A), evt_cb_flag_off, "moving_left");
evtman_add_handler (0, SDL_EVENT_KEY_DOWN, evt_pred_key, KEY (SDLK_D), evt_cb_flag_on, "moving_right");
evtman_add_handler (0, SDL_EVENT_KEY_UP, evt_pred_key, KEY (SDLK_D), evt_cb_flag_off, "moving_right");
evtman_add_handler (0, SDL_EVENT_MOUSE_MOTION, NULL, NULL, evt_cb_rotate_camera, NULL);
evtman_add_handler (0, SDL_EVENT_KEY_DOWN, evt_pred_key, KEY (SDLK_SPACE), evt_cb_flag_on, "flying_up");
evtman_add_handler (0, SDL_EVENT_KEY_UP, evt_pred_key, KEY (SDLK_SPACE), evt_cb_flag_off, "flying_up");
evtman_add_handler (0, SDL_EVENT_KEY_DOWN, evt_pred_key, KEY (SDLK_LCTRL), evt_cb_flag_on, "flying_down");
evtman_add_handler (0, SDL_EVENT_KEY_UP, evt_pred_key, KEY (SDLK_LCTRL), evt_cb_flag_off, "flying_down");
evtman_add_handler (0, SDL_EVENT_KEY_DOWN, evt_pred_key, KEY (SDLK_LSHIFT), evt_cb_flag_on, "flying_fast");
evtman_add_handler (0, SDL_EVENT_KEY_UP, evt_pred_key, KEY (SDLK_LSHIFT), evt_cb_flag_off, "flying_fast");
#undef KEY
}
void
evtman_add_handler (int priority, unsigned int filter,
gboolean (*predicate) (SDL_Event *sdl_event, void *userdata),
void *predicate_data,
gboolean (*callback) (SDL_Event *sdl_event, void *userdata),
void *callback_data)
{
EventHandler *h = g_new (EventHandler, 1);
h->filter = filter;
h->priority = priority;
h->predicate_data = predicate_data;
h->callback_data = callback_data;
h->predicate = predicate;
h->callback = callback;
GList *iter = handlers;
while (iter && ((EventHandler *) iter->data)->priority >= priority)
{
iter = iter->next;
}
handlers = g_list_insert_before (handlers, iter, h);
}
void
evtman_handle_SDL_Event (SDL_Event *event)
{
for (GList *l = handlers; l != NULL; l = l->next)
{
EventHandler *h = (EventHandler *) l->data;
if ((h->filter == 0 || h->filter == event->type)
&& (!h->predicate || h->predicate (event, h->predicate_data)))
{
if (h->callback && h->callback (event, h->callback_data))
{
break;
}
}
}
}
-19
View File
@@ -1,19 +0,0 @@
#pragma once
#include <SDL3/SDL.h>
#include <glib-2.0/glib.h>
#include <cglm/cglm.h>
#include "handler.h"
#include "../util.h"
#include "../gvar.h"
#include "../graphics.h"
#include "../ui/uiman.h"
void evtman_init ();
void evtman_add_handler (int priority, unsigned int filter,
gboolean (*predicate) (SDL_Event *sdl_event, void *userdata),
void *predicate_data,
gboolean (*callback) (SDL_Event *sdl_event, void *userdata),
void *callback_data);
void evtman_handle_SDL_Event (SDL_Event *event);
-12
View File
@@ -1,12 +0,0 @@
#pragma once
#include <SDL3/SDL.h>
#include <glib-2.0/glib.h>
typedef struct {
unsigned int filter;
int priority;
void *predicate_data, *callback_data;
gboolean (*predicate) (SDL_Event *sdl_event, void *userdata);
gboolean (*callback) (SDL_Event *sdl_event, void *userdata);
} EventHandler;
+6 -63
View File
@@ -5,9 +5,6 @@ extern vec3 camera_position;
SDL_Window *sdl_window;
static SDL_GLContext gl_context;
static uint64_t last_check_perf = 0;
static int avg_counter = 0;
static double avg_elapsed = 0;
void
graphics_init ()
@@ -40,7 +37,6 @@ graphics_init ()
}
SDL_GL_MakeCurrent (sdl_window, gl_context);
SDL_SetWindowRelativeMouseMode (sdl_window, true);
g_info ("Renderer: %s", glGetString (GL_RENDERER));
g_info ("OpenGL version: %s", glGetString (GL_VERSION));
@@ -57,68 +53,15 @@ graphics_init ()
}
void
graphics_render ()
graphics_prerender ()
{
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
uint64_t start = SDL_GetPerformanceCounter ();
uint64_t freq = SDL_GetPerformanceFrequency ();
mat4 projection, view;
camera_get_projection (gvar_get ("window_width") / gvar_get ("window_height"), projection);
camera_get_view (view);
pakman_all_paks_set_mat4 ("projection", projection);
pakman_all_paks_set_mat4 ("view", view);
pakman_all_paks_set_float ("time", gvar_get ("last_time"));
vec4 planes[6];
#if FRUSTUM_CULL
mat4 view_proj;
glm_mat4_mul (projection, view, view_proj);
glm_frustum_planes (view_proj, planes);
#endif
chunkman_render (camera_position, planes);
entman_render (view, planes);
/* We don't care about blending elsewhere for now. */
glEnable (GL_SCISSOR_TEST);
glEnable (GL_BLEND);
glDisable (GL_DEPTH_TEST);
uiman_render ();
glEnable (GL_DEPTH_TEST);
glDisable (GL_BLEND);
glDisable (GL_SCISSOR_TEST);
SDL_GL_SwapWindow (sdl_window);
uint64_t now = SDL_GetPerformanceCounter();
double elapsed_us = (double) (now - start) * 1000000.0 / (double) freq;
avg_elapsed += elapsed_us;
avg_counter++;
int render_distance = gvar_get ("render_distance");
if ((now - last_check_perf) >= freq * RENDER_AVG_FREQ_S)
{
double avg = avg_elapsed / avg_counter;
if (avg < RENDER_FRAME_BUDGET_US && render_distance < RENDER_DISTANCE_MAX)
{
gvar_set ("render_distance", ++render_distance);
}
else if (avg > RENDER_FRAME_BUDGET_US && render_distance > RENDER_DISTANCE_MIN)
{
gvar_set ("render_distance", --render_distance);
}
avg_elapsed = 0;
avg_counter = 0;
last_check_perf = now;
g_debug ("Dynamic render distance check: render distance %d, average ms: %.2f", render_distance, avg / 10000.0);
}
void
graphics_postrender ()
{
SDL_GL_SwapWindow (sdl_window);
}
void
+2 -3
View File
@@ -4,10 +4,8 @@
#include <SDL3/SDL_opengles2.h>
#include <SDL3_image/SDL_image.h>
#include "ui/uiman.h"
#include "chunk/chunkman.h"
#include "entity/entman.h"
#include "state.h"
#include "util.h"
#include "gvar.h"
#include "pakman.h"
@@ -23,6 +21,7 @@
#define RENDER_DISTANCE_MAX 12
void graphics_init ();
void graphics_render ();
void graphics_prerender ();
void graphics_postrender ();
void graphics_free ();
gboolean graphics_cb_window_resize ();
+16
View File
@@ -8,6 +8,22 @@
gvars store arbitrary variables as floats. This can be used for
flags as well as configuration options and whatnot.
Here's a list of game flags that are used regardless of whether a
.pak defines some or not:
in_main_menu
ui_has_focus
moving_forward
moving_left
moving_right
moving_back
flight_enabled
flying_up
flying_down
flying_fast
*/
#define FLAG_GET(key) ((gboolean) gvar_get (key))
+30 -19
View File
@@ -2,18 +2,14 @@
#include <SDL3/SDL_main.h>
#include <libguile.h>
#include "event/evtman.h"
#include "chunk/chunkman.h"
#include "entity/templates.h"
#include "entity/entity_guile.h"
#include "ui/uiman.h"
#include "ui/widget_guile.h"
#include "state.h"
#include "graphics.h"
#include "gvar.h"
#include "pakman.h"
extern vec3 camera_position;
#include "states/stateman.h"
SDL_AppResult
SDL_AppInit (void **appstate, int argc, char **argv)
@@ -21,24 +17,38 @@ SDL_AppInit (void **appstate, int argc, char **argv)
UNUSED (appstate, argc, argv);
SDL_SetAppMetadata (GAME_NAME, GAME_VERSION, "eur.chopininteractive.wschod-game");
/* Initialize GNU Guile and register all kinds of functions and cool
things. */
scm_init_guile ();
gvar_guile_register ();
widget_guile_register ();
entity_guile_register ();
pakman_guile_register ();
/* TODO: move this somewhere. */
scm_c_primitive_load ("res/scripts/global/ui_styles.scm");
scm_c_primitive_load ("res/scripts/global/ui_composites.scm");
/* The basics; game variables, graphics, the state manager, and the
pak manager. */
gvar_init ();
graphics_init ();
pakman_init ();
/* The base pack is always required, as it contains all the data
needed for the functioning of the UI and graphics. */
pakman_load_pak ("res/");
evtman_init ();
uiman_init ();
chunkman_init ();
entman_init ();
ent_templates_init ();
game_state_init (SDL_GetTicks ());
stateman_init ();
/* I don't know. This probably needs to be moved somewhere. */
ui_widget_mesh_init ();
/* The UI, entity and chunk subsystems. */
entman_init ();
chunkman_init ();
stateman_switch_state ("main_menu");
return SDL_APP_CONTINUE;
}
@@ -52,7 +62,8 @@ SDL_AppEvent (void *appstate, SDL_Event *event)
return SDL_APP_SUCCESS;
}
evtman_handle_SDL_Event (event);
stateman_on_SDL_Event (event);
return SDL_APP_CONTINUE;
}
@@ -61,11 +72,11 @@ SDL_AppIterate (void *appstate)
{
UNUSED (appstate);
game_state_tick (SDL_GetTicks ());
chunkman_tick (camera_position);
entman_tick ();
uiman_tick ();
graphics_render ();
stateman_update ();
graphics_prerender ();
stateman_render ();
graphics_postrender ();
return SDL_APP_CONTINUE;
}
@@ -75,5 +86,5 @@ SDL_AppQuit (void *appstate, SDL_AppResult result)
{
UNUSED (appstate, result);
graphics_free ();
uiman_free ();
stateman_free ();
}
+43 -1
View File
@@ -24,11 +24,20 @@ _scm_get_texture (SCM pak_scm, SCM key_scm)
return scm_from_int (pakman_pak_texture (pak, key));
}
static SCM
_scm_get_uidef (SCM pak_scm, SCM key_scm)
{
const char *pak = scm_to_utf8_string (pak_scm);
const char *key = scm_to_utf8_string (key_scm);
return scm_from_pointer (pakman_pak_uidef (pak, key), NULL);
}
void
pakman_guile_register ()
{
scm_c_define_gsubr ("pak-program", 2, 0, 0, _scm_get_program);
scm_c_define_gsubr ("pak-texture", 2, 0, 0, _scm_get_texture);
scm_c_define_gsubr ("pak-uidef", 2, 0, 0, _scm_get_uidef);
}
unsigned int
@@ -87,7 +96,8 @@ pakman_load_pak (const char *path)
PakFile *pak = g_new0 (PakFile, 1);
pak->programs = g_hash_table_new (g_str_hash, g_str_equal);
pak->textures = g_hash_table_new (g_str_hash, g_str_equal);
pak->textures = g_hash_table_new (g_str_hash, g_str_equal);
pak->uidefs = g_hash_table_new (g_str_hash, g_str_equal);
char **programs = g_key_file_get_keys (key_file, "Programs", NULL, &err);
@@ -114,6 +124,16 @@ pakman_load_pak (const char *path)
g_hash_table_insert (pak->textures, g_strdup (*t), GUINT_TO_POINTER (_pm_load_texture (texture)));
}
char **uidefs = g_key_file_get_keys (key_file, "UIDefs", NULL, &err);
for (char **u = uidefs; *u != NULL; u++)
{
char *uidef_path = g_key_file_get_string (key_file, "UIDefs", *u, &err);
SCM uidef_scm = scm_c_primitive_load (uidef_path);
Widget *uidef = (Widget *) scm_to_pointer (uidef_scm);
g_hash_table_insert (pak->uidefs, g_strdup (*u), uidef);
}
char *id = g_key_file_get_string (key_file, "Metadata", "id", &err);
g_hash_table_insert (paks, g_strdup (id), pak);
@@ -171,6 +191,28 @@ pakman_pak_texture (const char *pak, const char *texture)
return GPOINTER_TO_UINT (value);
}
Widget *
pakman_pak_uidef (const char *pak, const char *uidef)
{
PakFile *p = g_hash_table_lookup (paks, pak);
if (!p)
{
g_warning ("Pak `%s' not found.", pak);
return NULL;
}
Widget *value = g_hash_table_lookup (p->uidefs, uidef);
if (!value)
{
g_warning ("Uidef `%s' not found in pak `%s'.", uidef, pak);
return NULL;
}
return value;
}
void
pakman_all_paks_set_mat4 (const char *uniform, mat4 value)
{
+4
View File
@@ -8,11 +8,13 @@
#include "gl/program.h"
#include "gl/texture.h"
#include "util.h"
#include "ui/widget_core.h"
typedef struct PakFile
{
GHashTable *programs;
GHashTable *textures;
GHashTable *uidefs;
} PakFile;
void pakman_init ();
@@ -21,9 +23,11 @@ void pakman_load_pak (const char *path);
unsigned int pakman_pak_program (const char *pak, const char *program);
unsigned int pakman_pak_texture (const char *pak, const char *texture);
Widget *pakman_pak_uidef (const char *pak, const char *texture);
void pakman_all_paks_set_mat4 (const char *uniform, mat4 value);
void pakman_all_paks_set_float (const char *uniform, float value);
#define PMPRG(program) pakman_pak_program ("base", program)
#define PMTEX(texture) pakman_pak_texture ("base", texture)
#define PMUID(uidef) pakman_pak_uidef ("base", uidef)
-91
View File
@@ -1,91 +0,0 @@
#include "state.h"
extern vec3 camera_position;
extern vec3 camera_rotation;
extern Entity *player_entity, *sky_entity;
void
game_state_init (int ticks)
{
gvar_set ("last_time", ticks);
}
void
game_state_tick (int ticks)
{
/* Caluclate stuff per frame. */
gvar_set ("delta_time", (ticks - gvar_get ("last_time")) / 1000.0);
gvar_set ("last_time", ticks);
float delta_time = gvar_get ("delta_time");
/* We pin the camera to the player position, with eye level 20
centimeters below the height. */
glm_vec3_copy (player_entity->position, camera_position);
camera_position[1] += player_entity->scale[1] - 0.2;
player_entity->rotation[1] = camera_rotation[1];
/* The sky needs to be in the center of the world all the time,
otherwise, as we explore the world, we might reach the end of the
sky. */
sky_entity->position[0] = camera_position[0];
sky_entity->position[2] = camera_position[2];
int speed = 5;
if (FLAG_GET ("flight_enabled") && FLAG_GET ("flying_fast"))
{
speed = 15;
}
if (FLAG_GET ("moving_forward"))
{
vec3 dir, change, new_pos;
camera_get_direction (dir);
glm_vec3_scale (dir, delta_time * speed, change);
glm_vec3_add (player_entity->position, change, new_pos);
glm_vec3_copy (new_pos, player_entity->position);
}
if (FLAG_GET ("moving_backward"))
{
vec3 dir, change, new_pos;
camera_get_direction (dir);
glm_vec3_scale (dir, delta_time * speed, change);
glm_vec3_sub (player_entity->position, change, new_pos);
glm_vec3_copy (new_pos, player_entity->position);
}
if (FLAG_GET ("moving_right"))
{
vec3 right, change, new_pos;
camera_get_right (right);
glm_vec3_scale (right, delta_time * speed, change);
glm_vec3_add (player_entity->position, change, new_pos);
glm_vec3_copy (new_pos, player_entity->position);
}
if (FLAG_GET ("moving_left"))
{
vec3 right, change, new_pos;
camera_get_right (right);
glm_vec3_scale (right, delta_time * speed, change);
glm_vec3_sub (player_entity->position, change, new_pos);
glm_vec3_copy (new_pos, player_entity->position);
}
if (FLAG_GET ("flying_up") && FLAG_GET ("flight_enabled"))
{
vec3 change, new_pos;
glm_vec3_scale (GLM_YUP, delta_time * speed, change);
glm_vec3_add (player_entity->position, change, new_pos);
glm_vec3_copy (new_pos, player_entity->position);
}
if (FLAG_GET ("flying_down") && FLAG_GET ("flight_enabled"))
{
vec3 change, new_pos;
glm_vec3_scale (GLM_YUP, delta_time * speed, change);
glm_vec3_sub (player_entity->position, change, new_pos);
glm_vec3_copy (new_pos, player_entity->position);
}
}
-9
View File
@@ -1,9 +0,0 @@
#pragma once
#include "camera.h"
#include "gvar.h"
#include "chunk/chunkman.h"
#include "entity/entman.h"
void game_state_init (int ticks);
void game_state_tick (int ticks);
+125
View File
@@ -0,0 +1,125 @@
#include "state_common.h"
GameState *
GameState_new ()
{
GameState *g = g_new0 (GameState, 1);
g->ui_context = UIContext_new ();
g->ui_context->tooltip = PMUID ("tooltip");
return g;
}
void
GameState_register_UIContext_handlers (GameState *self)
{
/* UGH... I fucking hate this whole structure of the game I did
here. It should be really extensible and whatnot, but jesus
christ, it's so complex... */
#define GAEH GameState_add_event_handler
GAEH (self, 1, SDL_EVENT_WINDOW_RESIZED, NULL, NULL, UIContext_window_resize,self->ui_context);
GAEH (self, 1, SDL_EVENT_KEY_DOWN, evt_pred_flag, "ui_has_focus", UIContext_key_down, self->ui_context);
GAEH (self, 1, SDL_EVENT_KEY_UP, evt_pred_flag, "ui_has_focus", UIContext_key_up, self->ui_context);
GAEH (self, 1, SDL_EVENT_MOUSE_MOTION, evt_pred_flag, "ui_has_focus", UIContext_mouse_motion, self->ui_context);
GAEH (self, 1, SDL_EVENT_MOUSE_BUTTON_DOWN, evt_pred_flag, "ui_has_focus", UIContext_button_down, self->ui_context);
GAEH (self, 1, SDL_EVENT_MOUSE_BUTTON_UP, evt_pred_flag, "ui_has_focus", UIContext_button_up, self->ui_context);
GAEH (self, 1, SDL_EVENT_MOUSE_WHEEL, evt_pred_flag, "ui_has_focus", UIContext_mouse_wheel, self->ui_context);
GAEH (self, 1, SDL_EVENT_TEXT_INPUT, evt_pred_flag, "ui_has_focus", UIContext_text_input, self->ui_context);
#undef GAEH
}
void
GameState_add_event_handler (GameState *self, int priority, unsigned int on_event,
gboolean (*predicate) (SDL_Event *sdl_event, void *userdata),
void *predicate_data,
gboolean (*callback) (SDL_Event *sdl_event, void *userdata),
void *callback_data)
{
EventHandler *h = g_new (EventHandler, 1);
h->on_event = on_event;
h->priority = priority;
h->predicate_data = predicate_data;
h->callback_data = callback_data;
h->predicate = predicate;
h->callback = callback;
GList *iter = self->event_handlers;
while (iter && ((EventHandler *) iter->data)->priority >= priority)
{
iter = iter->next;
}
self->event_handlers = g_list_insert_before (self->event_handlers, iter, h);
}
void
GameState_handle_SDL_Event (GameState *self, SDL_Event *event)
{
for (GList *l = self->event_handlers; l != NULL; l = l->next)
{
EventHandler *h = (EventHandler *) l->data;
if ((h->on_event == 0 || h->on_event == event->type)
&& (!h->predicate || h->predicate (event, h->predicate_data)))
{
if (h->callback && h->callback (event, h->callback_data))
{
break;
}
}
}
}
/* Event predicates, for convenience's sake. */
gboolean
evt_pred_key (SDL_Event *event, void *data)
{
unsigned int expected_key = GPOINTER_TO_INT (data);
return event->key.key == expected_key;
}
gboolean
evt_pred_flag (SDL_Event *event, void *data)
{
UNUSED (event);
char *flag = data;
return FLAG_GET (flag);
}
/* Event callbacks. */
gboolean
evt_cb_flag_off (SDL_Event *event, void *data)
{
UNUSED (event);
char *flag = data;
FLAG_SET (flag, FALSE);
return TRUE;
}
gboolean
evt_cb_flag_on (SDL_Event *event, void *data)
{
UNUSED (event);
char *flag = data;
FLAG_SET (flag, TRUE);
return TRUE;
}
gboolean
evt_cb_flag_switch (SDL_Event *event, void *data)
{
UNUSED (event);
char *flag = data;
FLAG_SWITCH (flag);
return TRUE;
}
gboolean
evt_cb_eat (SDL_Event *event, void *data)
{
UNUSED (event, data);
return TRUE;
}
+48
View File
@@ -0,0 +1,48 @@
#pragma once
#include <SDL3/SDL.h>
#include <glib-2.0/glib.h>
#include "../ui/ui_context.h"
#include "../util.h"
#include "../gvar.h"
typedef struct {
unsigned int on_event;
int priority;
void *predicate_data, *callback_data;
gboolean (*predicate) (SDL_Event *sdl_event, void *userdata);
gboolean (*callback) (SDL_Event *sdl_event, void *userdata);
} EventHandler;
typedef struct GameState
{
UIContext *ui_context;
GList *event_handlers;
void (*on_enter) (struct GameState *);
void (*on_exit) (struct GameState *);
void (*on_update) (struct GameState *);
void (*on_render) (struct GameState *);
} GameState;
GameState *GameState_new ();
void GameState_register_UIContext_handlers (GameState *self);
void GameState_add_event_handler (GameState *self, int priority, unsigned int on_event,
gboolean (*predicate) (SDL_Event *sdl_event, void *userdata),
void *predicate_data,
gboolean (*callback) (SDL_Event *sdl_event, void *userdata),
void *callback_data);
void GameState_handle_SDL_Event (GameState *self, SDL_Event *event);
gboolean evt_pred_key (SDL_Event *event, void *data);
gboolean evt_pred_flag (SDL_Event *event, void *data);
gboolean evt_cb_flag_off (SDL_Event *event, void *data);
gboolean evt_cb_flag_on (SDL_Event *event, void *data);
gboolean evt_cb_flag_switch (SDL_Event *event, void *data);
gboolean evt_cb_eat (SDL_Event *event, void *data);
+41
View File
@@ -0,0 +1,41 @@
#include "state_global.h"
/* The required callbacks. */
static void
_on_enter ()
{
gvar_set ("last_time", SDL_GetTicks ());
g_print ("Successfuly initialized global state\n");
}
static void _on_exit () { return; }
static void
_on_update ()
{
unsigned int now = SDL_GetTicks ();
gvar_set ("delta_time", (now - gvar_get ("last_time")) / 1000.0);
gvar_set ("last_time", now);
}
static void _on_render () { return; }
GameState *
state_global_new ()
{
GameState *self = GameState_new ();
#define GAEH GameState_add_event_handler
GAEH (self, 0, SDL_EVENT_WINDOW_RESIZED, NULL, NULL, graphics_cb_window_resize, NULL);
#undef GAEH
self->on_enter = _on_enter;
self->on_exit = _on_exit;
self->on_update = _on_update;
self->on_render = _on_render;
return self;
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#include "state_common.h"
#include "../graphics.h"
#include "../gvar.h"
GameState *state_global_new ();
+43
View File
@@ -0,0 +1,43 @@
#include "state_main_menu.h"
static void
_on_enter ()
{
gvar_set ("ui_has_focus", TRUE);
}
static void
_on_exit ()
{
}
static void
_on_update ()
{
}
static void
_on_render ()
{
}
GameState *
state_main_menu_new ()
{
GameState *g = GameState_new ();
g->on_enter = _on_enter;
g->on_exit = _on_exit;
g->on_update = _on_update;
g->on_render = _on_render;
/* If your GameState uses the UI, always call this. */
GameState_register_UIContext_handlers (g);
g->ui_context->root = PMUID ("main_menu");
return g;
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#include "state_common.h"
#include "../graphics.h"
#include "../gvar.h"
GameState *state_main_menu_new ();
+87
View File
@@ -0,0 +1,87 @@
#include "stateman.h"
static GHashTable *game_states;
static GameState *current_state;
static GameState *global_state;
void
stateman_init ()
{
game_states = g_hash_table_new (g_str_hash, g_str_equal);
stateman_add_state ("main_menu", state_main_menu_new ());
global_state = state_global_new ();
global_state->on_enter (global_state);
}
void
stateman_add_state (const char *name, GameState *state)
{
g_hash_table_replace (game_states, g_strdup (name), state);
}
void
stateman_switch_state (const char *name)
{
GameState *next_state = g_hash_table_lookup (game_states, name);
if (current_state && current_state->on_exit)
{
current_state->on_exit (current_state);
}
if (next_state && next_state->on_enter)
{
/* When we enter, we also set the UI root to be the current
window size. After that, if callbacks for a given state are
set up correctly (using
GameState_register_UIContext_handlers), then it will update
the root size automatically.*/
next_state->ui_context->root->size[0] = gvar_get ("window_width");
next_state->ui_context->root->size[1] = gvar_get ("window_height");
next_state->on_enter (next_state);
current_state = next_state;
}
}
void stateman_update ()
{
global_state->on_update (global_state);
if (current_state && current_state->on_update)
{
current_state->on_update (current_state);
}
if (current_state && current_state->ui_context)
{
UIContext_update (current_state->ui_context);
}
}
void
stateman_on_SDL_Event (SDL_Event *event)
{
GameState_handle_SDL_Event (global_state, event);
if (current_state && current_state->event_handlers)
{
GameState_handle_SDL_Event (current_state, event);
}
}
void
stateman_render ()
{
if (current_state && current_state->on_render)
{
current_state->on_render (current_state);
}
if (current_state && current_state->ui_context)
{
UIContext_render (current_state->ui_context);
}
}
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include <glib-2.0/glib.h>
#include "state_common.h"
#include "state_global.h"
#include "state_main_menu.h"
void stateman_init ();
static inline void stateman_free () { } /* TODO: implement */
void stateman_add_state (const char *name, GameState *state);
void stateman_switch_state (const char *name);
void stateman_update ();
void stateman_on_SDL_Event (SDL_Event *event);
void stateman_render ();
+230
View File
@@ -0,0 +1,230 @@
#include "ui_context.h"
extern SDL_Window *sdl_window;
UIContext *
UIContext_new ()
{
UIContext *u = g_new0 (UIContext, 1);
u->root = Widget_new ();
u->tooltip = Widget_new ();
return u;
}
void
UIContext_update (UIContext *self)
{
Widget_tick (self->root);
}
void
UIContext_render (UIContext *self)
{
glEnable (GL_SCISSOR_TEST);
glEnable (GL_BLEND);
glDisable (GL_DEPTH_TEST);
/* Probably good enough, LOL. No need to store that shiet
anywhere. */
mat4 projection;
glm_ortho (0.0, gvar_get ("window_width"), gvar_get ("window_height"), 0.0, -1.0, 1.0, projection);
Widget_render (self->root, projection, gvar_get ("window_height"));
Widget_render (self->tooltip, projection, gvar_get ("window_height"));
glEnable (GL_DEPTH_TEST);
glDisable (GL_BLEND);
glDisable (GL_SCISSOR_TEST);
}
/* UIContext callback helpers */
static inline gboolean
_call_on_event (Widget *widget, SDL_Event *sdl_event, WidgetEvent event)
{
if (widget && widget->on_event[event])
{
widget->on_event[event] (widget, sdl_event, widget->event_data[event]);
return TRUE;
}
return FALSE;
}
static inline void
_set_clicked (Widget *widget, gboolean state)
{
if (widget)
{
widget->clicked = state;
}
}
static inline void
_set_hovered (Widget *widget, gboolean state)
{
if (widget)
{
widget->hovered = state;
}
}
static inline void
_set_focused (Widget *widget, gboolean state)
{
if (widget)
{
widget->focused = state;
}
}
/* UIContext callbacks */
gboolean
UIContext_window_resize (SDL_Event *event, void *data)
{
UNUSED (event);
UIContext *self = data;
self->root->size[0] = gvar_get ("window_width");
self->root->size[1] = gvar_get ("window_height");
return TRUE;
}
gboolean
UIContext_key_down (SDL_Event *event, void *data)
{
UIContext *self = data;
Widget *focused = self->focused_widget;
SDL_Scancode sc = event->key.scancode;
if (focused && focused->on_key[sc])
{
focused->on_key[sc] (focused, event, focused->key_data[sc]);
return TRUE;
}
if (focused && SDL_TextInputActive (sdl_window))
{
return TRUE;
}
return FALSE;
}
gboolean
UIContext_key_up (SDL_Event *event, void *data)
{
UNUSED (event, data);
return FALSE;
}
gboolean
UIContext_mouse_motion (SDL_Event *event, void *data)
{
UIContext *self = data;
ivec2 pos = { event->motion.x, event->motion.y };
Widget *new_hover = Widget_find_xy_cond (self->root, pos, wcond_any, NULL);
if (new_hover != self->hovered_widget)
{
_call_on_event (self->hovered_widget, event, EVENT_MOUSE_LEAVE);
_set_hovered (self->hovered_widget, FALSE);
_call_on_event (new_hover, event, EVENT_MOUSE_ENTER);
_set_hovered (new_hover, TRUE);
self->hovered_widget = new_hover;
}
_call_on_event (self->hovered_widget, event, EVENT_MOUSE_MOVE);
/* Even if it isn't the hovered widget, this is useful for stuff
like scrollbars. */
_call_on_event (self->clicked_widget, event, EVENT_MOUSE_MOVE);
/* TODO: Later... */
Widget *tooltip_source = Widget_find_xy_cond (self->root, pos, wcond_tooltip, NULL);
if (tooltip_source && tooltip_source->tooltip && tooltip_source->tooltip->len > 0)
{
g_string_overwrite (self->tooltip->multiline_text.text, 0, tooltip_source->tooltip->str);
self->tooltip->visible = TRUE;
self->tooltip->position[0] = pos[0] + 12;
self->tooltip->position[1] = pos[1] + 12;
}
else
{
self->tooltip->visible = FALSE;
}
/* This callback always eats the event, because why would the game
need mouse motion if the UI has stolen the focus? */
return TRUE;
}
gboolean
UIContext_button_down (SDL_Event *event, void *data)
{
UIContext *self = data;
Widget *focused_widget = self->focused_widget;
ivec2 pos = { event->button.x, event->button.y };
WidgetEvent e = event->button.button == SDL_BUTTON_LEFT ? EVENT_LEFT_DOWN : EVENT_RIGHT_DOWN;
Widget *widget = Widget_find_xy_cond (self->root, pos, wcond_any, NULL);
_set_clicked (widget, TRUE);
self->clicked_widget = widget;
if (widget != focused_widget)
{
_set_focused (focused_widget, FALSE);
_call_on_event (focused_widget, event, EVENT_FOCUS_LOST);
_set_focused (widget, TRUE);
_call_on_event (widget, event, EVENT_FOCUS_GAINED);
focused_widget = widget;
}
return _call_on_event (widget, event, e);
}
gboolean
UIContext_button_up (SDL_Event *event, void *data)
{
UIContext *self = data;
WidgetEvent e = event->button.button == SDL_BUTTON_LEFT ? EVENT_LEFT_UP : EVENT_RIGHT_UP;
gboolean val = _call_on_event (self->clicked_widget, event, e);
_set_clicked (self->clicked_widget, FALSE);
self->clicked_widget = NULL;
return val;
}
gboolean
UIContext_mouse_wheel (SDL_Event *event, void *data)
{
UNUSED (event);
UIContext *self = data;
ivec2 pos = { event->button.x, event->button.y };
WidgetEvent e = event->wheel.y > 0 ? EVENT_SCROLL_UP : EVENT_SCROLL_DOWN;
Widget *widget = Widget_find_xy_cond (self->root, pos, wcond_event, GINT_TO_POINTER (e));
return _call_on_event (widget, event, e);
}
gboolean
UIContext_text_input (SDL_Event *event, void *data)
{
UNUSED (event);
UIContext *self = data;
if (self->focused_widget)
{
_call_on_event (self->focused_widget, event, EVENT_TEXT_INPUT);
return TRUE;
}
return FALSE;
}
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include "widget_core.h"
#include "../gvar.h"
#include "../graphics.h"
typedef struct UIContext
{
Widget *root, *hovered_widget, *clicked_widget, *focused_widget;
Widget *tooltip;
} UIContext;
UIContext *UIContext_new ();
void UIContext_update (UIContext *self);
void UIContext_render (UIContext *self);
gboolean UIContext_window_resize (SDL_Event *event, void *data);
gboolean UIContext_key_down (SDL_Event *event, void *data);
gboolean UIContext_key_up (SDL_Event *event, void *data);
gboolean UIContext_mouse_motion (SDL_Event *event, void *data);
gboolean UIContext_button_down (SDL_Event *event, void *data);
gboolean UIContext_button_up (SDL_Event *event, void *data);
gboolean UIContext_mouse_wheel (SDL_Event *event, void *data);
gboolean UIContext_text_input (SDL_Event *event, void *data);
-256
View File
@@ -1,256 +0,0 @@
#include "uiman.h"
extern SDL_Window *sdl_window;
/* UI global state variables. */
static Widget *ui_root;
static mat4 projection;
static Widget *hovered_widget = NULL;
static Widget *clicked_widget = NULL;
static Widget *focused_widget = NULL;
static Widget *tooltip_widget = NULL;
static Widget *tooltip_text = NULL;
void
uiman_init ()
{
ui_widget_mesh_init ();
glm_ortho (0.0, gvar_get ("window_width"), gvar_get ("window_height"), 0.0, -1.0, 1.0, projection);
ui_root = Widget_new ();
ui_root->size[0] = gvar_get ("window_width");
ui_root->size[1] = gvar_get ("window_height");
scm_c_primitive_load ("res/scripts/global/ui_styles.scm");
scm_c_primitive_load ("res/scripts/global/ui_composites.scm");
SCM scm_debug = scm_c_primitive_load ("res/scripts/ui/debug_window.scm");
Widget *debug_window = (Widget *) scm_to_pointer (scm_debug);
Widget_add_child (ui_root, debug_window);
SCM scm_tooltip = scm_c_primitive_load ("res/scripts/ui/tooltip.scm");
tooltip_widget = (Widget *) scm_to_pointer (scm_tooltip);
tooltip_text = g_array_index (tooltip_widget->children, Widget *, 0);
}
void
uiman_tick ()
{
Widget_tick (ui_root);
}
void
uiman_render ()
{
Widget_render (ui_root, projection, gvar_get ("window_height"));
Widget_render (tooltip_widget, projection, gvar_get ("window_height"));
}
void
uiman_free ()
{
Widget_free (ui_root);
}
static inline gboolean
_uiman_call_on_event (Widget *widget, SDL_Event *sdl_event, WidgetEvent event)
{
if (widget && widget->on_event[event])
{
widget->on_event[event] (widget, sdl_event, widget->event_data[event]);
return TRUE;
}
return FALSE;
}
static inline void
_uiman_set_clicked (Widget *widget, gboolean state)
{
if (widget)
{
widget->clicked = state;
}
}
static inline void
_uiman_set_hovered (Widget *widget, gboolean state)
{
if (widget)
{
widget->hovered = state;
}
}
static inline void
_uiman_set_focused (Widget *widget, gboolean state)
{
if (widget)
{
widget->focused = state;
}
}
gboolean
uiman_cb_switch_focus (SDL_Event *event, void *data)
{
UNUSED (event, data);
FLAG_SWITCH ("ui_has_focus");
SDL_SetWindowRelativeMouseMode (sdl_window, !FLAG_GET ("ui_has_focus"));
/* We reset the global states of the UI. TODO: also launch the
callbacks on mouse leave? */
SDL_StopTextInput (sdl_window);
_uiman_set_hovered (hovered_widget, FALSE);
hovered_widget = NULL;
_uiman_set_focused (focused_widget, FALSE);
focused_widget = NULL;
_uiman_set_clicked (clicked_widget, FALSE);
clicked_widget = NULL;
tooltip_widget->visible = FALSE;
return TRUE;
}
gboolean
uiman_cb_window_resize (SDL_Event *event, void *data)
{
UNUSED (event, data);
glm_ortho (0.0, gvar_get ("window_width"), gvar_get ("window_height"), 0.0, -1.0, 1.0, projection);
ui_root->size[0] = gvar_get ("window_width");
ui_root->size[1] = gvar_get ("window_height");
return FALSE;
}
gboolean
uiman_cb_key_down (SDL_Event *event, void *data)
{
UNUSED (data);
SDL_Scancode sc = event->key.scancode;
if (focused_widget && focused_widget->on_key[sc])
{
focused_widget->on_key[sc] (focused_widget, event, focused_widget->key_data[sc]);
return TRUE;
}
if (focused_widget && SDL_TextInputActive (sdl_window))
{
return TRUE;
}
return FALSE;
}
gboolean
uiman_cb_key_up (SDL_Event *event, void *data)
{
UNUSED (event, data);
return FALSE;
}
gboolean
uiman_cb_mouse_motion (SDL_Event *event, void *data)
{
UNUSED (data);
ivec2 pos = { event->motion.x, event->motion.y };
Widget *new_hover = Widget_find_xy_cond (ui_root, pos, wcond_any, NULL);
if (new_hover != hovered_widget)
{
_uiman_call_on_event (hovered_widget, event, EVENT_MOUSE_LEAVE);
_uiman_set_hovered (hovered_widget, FALSE);
_uiman_call_on_event (new_hover, event, EVENT_MOUSE_ENTER);
_uiman_set_hovered (new_hover, TRUE);
hovered_widget = new_hover;
}
_uiman_call_on_event (hovered_widget, event, EVENT_MOUSE_MOVE);
/* Even if it isn't the hovered widget, this is useful for stuff
like scrollbars. */
_uiman_call_on_event (clicked_widget, event, EVENT_MOUSE_MOVE);
Widget *tooltip_source = Widget_find_xy_cond (ui_root, pos, wcond_tooltip, NULL);
if (tooltip_source && tooltip_source->tooltip && tooltip_source->tooltip->len > 0)
{
g_string_overwrite (tooltip_text->multiline_text.text, 0, tooltip_source->tooltip->str);
tooltip_widget->visible = TRUE;
tooltip_widget->position[0] = pos[0] + 12;
tooltip_widget->position[1] = pos[1] + 12;
}
else
{
tooltip_widget->visible = FALSE;
}
/* This callback always eats the event, because why would the game
need mouse motion if the UI has stolen the focus? */
return TRUE;
}
gboolean
uiman_cb_button_down (SDL_Event *event, void *data)
{
UNUSED (data);
ivec2 pos = { event->button.x, event->button.y };
WidgetEvent e = event->button.button == SDL_BUTTON_LEFT ? EVENT_LEFT_DOWN : EVENT_RIGHT_DOWN;
Widget *widget = Widget_find_xy_cond (ui_root, pos, wcond_any, NULL);
_uiman_set_clicked (widget, TRUE);
clicked_widget = widget;
if (widget != focused_widget)
{
_uiman_set_focused (focused_widget, FALSE);
_uiman_call_on_event (focused_widget, event, EVENT_FOCUS_LOST);
_uiman_set_focused (widget, TRUE);
_uiman_call_on_event (widget, event, EVENT_FOCUS_GAINED);
focused_widget = widget;
}
return _uiman_call_on_event (widget, event, e);
}
gboolean
uiman_cb_button_up (SDL_Event *event, void *data)
{
UNUSED (data);
WidgetEvent e = event->button.button == SDL_BUTTON_LEFT ? EVENT_LEFT_UP : EVENT_RIGHT_UP;
gboolean val = _uiman_call_on_event (clicked_widget, event, e);
_uiman_set_clicked (clicked_widget, FALSE);
clicked_widget = NULL;
return val;
}
gboolean
uiman_cb_mouse_wheel (SDL_Event *event, void *data)
{
UNUSED (event, data);
ivec2 pos = { event->button.x, event->button.y };
WidgetEvent e = event->wheel.y > 0 ? EVENT_SCROLL_UP : EVENT_SCROLL_DOWN;
Widget *widget = Widget_find_xy_cond (ui_root, pos, wcond_event, GINT_TO_POINTER (e));
return _uiman_call_on_event (widget, event, e);
}
gboolean
uiman_cb_text_input (SDL_Event *event, void *data)
{
UNUSED (event, data);
if (focused_widget)
{
_uiman_call_on_event (focused_widget, event, EVENT_TEXT_INPUT);
return TRUE;
}
return FALSE;
}
-25
View File
@@ -1,25 +0,0 @@
#pragma once
#include <libguile.h>
#include "widget_core.h"
#include "../gvar.h"
#include "../state.h"
#include "../camera.h"
#include "../graphics.h"
void uiman_init ();
void uiman_tick ();
void uiman_render ();
void uiman_free ();
gboolean uiman_cb_switch_focus (SDL_Event *event, void *data);
gboolean uiman_cb_window_resize (SDL_Event *event, void *data);
gboolean uiman_cb_key_down (SDL_Event *event, void *data);
gboolean uiman_cb_key_up (SDL_Event *event, void *data);
gboolean uiman_cb_mouse_motion (SDL_Event *event, void *data);
gboolean uiman_cb_button_down (SDL_Event *event, void *data);
gboolean uiman_cb_button_up (SDL_Event *event, void *data);
gboolean uiman_cb_mouse_wheel (SDL_Event *event, void *data);
gboolean uiman_cb_text_input (SDL_Event *event, void *data);