Hey, working through setting up a mini-engine thing with SDL3 and I want to avoid having a giant switch statement for handling events, but since C unions (at least to my knowledge) have no method of checking which value is stored in it I was wondering if SDL itself has someway of getting that information.
Ideally I would like to be able to do something akin to this:
static SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
if (event is SDL_WindowEvent) {
handle_window_events(event);
}
}
So I don’t need to handle every event in the same function.
Try setting an event watch or an event filter. Both are very similar, but the filter gives you the option to remove the event from the queue when the callback returns.
Please Note: The event queue has a max size, so you should still have a while loop in the main thread that polls events in order to consume them from the queue even if the default action is to ignore these events.
I misunderstood the prompt, please ignore this post.
However, I don’t think a giant switch sounds like such a bad idea…
static SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
switch (event->type) {
case SDL_EVENT_WINDOW_SHOWN:
case SDL_EVENT_WINDOW_HIDDEN:
case SDL_EVENT_WINDOW_EXPOSED:
case SDL_EVENT_WINDOW_MOVED:
case SDL_EVENT_WINDOW_RESIZED:
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
case SDL_EVENT_WINDOW_METAL_VIEW_RESIZED:
case SDL_EVENT_WINDOW_MINIMIZED:
case SDL_EVENT_WINDOW_MAXIMIZED:
case SDL_EVENT_WINDOW_RESTORED:
case SDL_EVENT_WINDOW_MOUSE_ENTER:
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
case SDL_EVENT_WINDOW_FOCUS_GAINED:
case SDL_EVENT_WINDOW_FOCUS_LOST:
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
case SDL_EVENT_WINDOW_HIT_TEST:
case SDL_EVENT_WINDOW_ICCPROF_CHANGED:
case SDL_EVENT_WINDOW_DISPLAY_CHANGED:
case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
case SDL_EVENT_WINDOW_SAFE_AREA_CHANGED:
case SDL_EVENT_WINDOW_OCCLUDED:
case SDL_EVENT_WINDOW_ENTER_FULLSCREEN:
case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN:
case SDL_EVENT_WINDOW_DESTROYED:
case SDL_EVENT_WINDOW_HDR_STATE_CHANGED:
handle_window_event(event->window);
break;
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP:
handle_key_event(event->key);
break;
case SDL_EVENT_KEYBOARD_ADDED:
case SDL_EVENT_KEYBOARD_REMOVED:
handle_keyboard_device_event(event->kdevice);
break;
case SDL_EVENT_KEYMAP_CHANGED:
handle_keymap_changed_event();
break;
... and so on ...
It might make sense to break up (some of) the window events into multiple functions even though they all use the same type. That way you could give more meaningful names for the parameters.
void handle_window_shown_event(SDL_WindowID windowId);
void handle_window_hidden_event(SDL_WindowID windowId);
void handle_window_exposed_event(SDL_WindowID windowId);
void handle_window_moved_event(SDL_WindowID windowId, int x, int y);
void handle_window_resized_event(SDL_WindowID windowId, int width, int height);
...
static SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
switch (event->type) {
case SDL_EVENT_WINDOW_SHOWN:
handle_window_shown_event(event->window->windowID);
break;
case SDL_EVENT_WINDOW_HIDDEN:
handle_window_hidden_event(event->window->windowID);
break;
case SDL_EVENT_WINDOW_EXPOSED:
handle_window_exposed_event(event->window->windowID);
break;
case SDL_EVENT_WINDOW_MOVED:
handle_window_moved_event(event->window->windowID, event->window->data1, event->window->data2);
break;
case SDL_EVENT_WINDOW_RESIZED:
handle_window_resized_event(event->window->windowID, event->window->data1, event->window->data2);
break;
...
You could of course break up the other events too if you want. It all depends on how specialized you want the handlers to be.
There’s two ways that I know of, either setting an event watch or an event filter. Both are very similar, but the filter gives you the option to remove the event from the queue when the callback returns.
Thanks, I will look into those. Do you know if you can set multiple filters? Ideally I want to be able to set a filter per category of input to handle it.
Well, at least for the window events you can do:
If I can’t get the filters working this might be what I end up doing.
I may have misunderstood the goal in my first post.
So, looking back at your initial request, you are asking for a way to chop up the switch statement into smaller chunks based on event category. Yes, we can do that.
Keep in mind that there are some events that are considered “Spammy” like the mouse motion event. It would be best to handle those earlier in this dispatcher so that your code doesn’t run through as many checks for those spammy events.
The switch-case statement is going to be the more optimized option as fewer function calls and if-statements will generally always be faster, and the compiler can optimize switches for cases that get called more often.
It still might be worth it if it makes your code more maintainable for you.
Conveniently, this is actually what I ended up doing. Good tip about the spammy events though. i hadn’t considered that. I can put a special handling condition for things like mouse motion
Note that the event filters (and event watchers) can run in different threads so if you use that you need to be careful with what you do.
Not all gamepad events use the same data type. Same is true for the mouse events.
SDL_EVENT_GAMEPAD_AXIS_MOTION uses SDL_GamepadAxisEvent (gaxis), SDL_EVENT_GAMEPAD_BUTTON_* uses SDL_GamepadButtonEvent (gbutton), SDL_EVENT_GAMEPAD_TOUCHPAD_* uses SDL_GamepadTouchpadEvent (gtouchpad), SDL_EVENT_GAMEPAD_SENSOR_UPDATE uses SDL_GamepadSensorEvent (gsensor),
and the rest of the gamepad events (which are not one contiguous range) uses SDL_GamepadDeviceEvent (gdevice).
If you only care about the most recent mouse position it might be best to just set some variable(s) and handle the updated mouse position once after the event loop (or just call SDL_GetMouseState to get the position each time).
Using a switch statement doesn’t rule out the use of functions. I wouldn’t worry about the function call overhead in this situation because (1) compilers are pretty good at optimizing and (2) even if you got 1000 events each second that ended up calling a few functions each time it’s not going to matter because they are still pretty few. What you do inside those functions is much more important.