ASW Lib
A.D.S. Games SDL Wrapper Library. A library targeted at Allegro4 users who want to switch to SDL3 and use modern c++.
Loading...
Searching...
No Matches
core.cpp
Go to the documentation of this file.
2
3#include <SDL3/SDL.h>
4#include <SDL3_mixer/SDL_mixer.h>
5#include <SDL3_ttf/SDL_ttf.h>
6#include <algorithm>
7#include <format>
8
9#ifdef __EMSCRIPTEN__
10#include <emscripten.h>
11#endif
12
17#include "./asw/modules/input.h"
18#include "./asw/modules/log.h"
19#include "./asw/modules/sound.h"
20#include "./asw/modules/util.h"
21
22namespace {
23bool exiting = false;
24
25// Longest frame get_delta_time() reports
26constexpr float MAX_DELTA_TIME = 0.25F;
27
28uint64_t last_update_ns = 0;
29float delta_time = 0.0F;
30} // namespace
31
33{
34 const uint64_t now = SDL_GetTicksNS();
35 if (last_update_ns != 0) {
37 = std::min(static_cast<float>(now - last_update_ns) / 1'000'000'000.0F, MAX_DELTA_TIME);
38 }
39 last_update_ns = now;
40
42
43 SDL_Event e;
44
45 while (SDL_PollEvent(&e)) {
46 switch (e.type) {
47 case SDL_EVENT_WINDOW_RESIZED: {
49 if (r == nullptr) {
50 break;
51 }
52
53 // Maintain aspect ratio. Window coordinates, not pixels, to match
54 // SDL_SetWindowSize on high density displays
55 SDL_Point window_size;
56 SDL_GetWindowSize(asw::display::get_window(), &window_size.x, &window_size.y);
57
58 // SDL reports the logical size of the current render target,
59 // which is 0 while a texture is the target
60 const auto render_size = asw::display::get_logical_size();
61 if (render_size.x <= 0 || render_size.y <= 0 || window_size.x <= 0
62 || window_size.y <= 0) {
63 break;
64 }
65
66 const auto x_scale
67 = static_cast<float>(window_size.x) / static_cast<float>(render_size.x);
68
69 const auto y_scale
70 = static_cast<float>(window_size.y) / static_cast<float>(render_size.y);
71
72 const auto scale = std::min(x_scale, y_scale);
73
74 SDL_SetWindowSize(asw::display::get_window(),
75 static_cast<int>(static_cast<float>(render_size.x) * scale),
76 static_cast<int>(static_cast<float>(render_size.y) * scale));
77 break;
78 }
79
80 case SDL_EVENT_KEY_DOWN: {
81 asw::input::_key_down(e.key.scancode, e.key.repeat);
82 break;
83 }
84
85 case SDL_EVENT_KEY_UP: {
86 if (!e.key.repeat) {
87 asw::input::_key_up(e.key.scancode);
88 }
89 break;
90 }
91
92 case SDL_EVENT_MOUSE_BUTTON_DOWN: {
93 asw::input::_mouse_button_down(e.button.button);
94 break;
95 }
96
97 case SDL_EVENT_MOUSE_BUTTON_UP: {
98 asw::input::_mouse_button_up(e.button.button);
99 break;
100 }
101
102 case SDL_EVENT_MOUSE_MOTION: {
103 // Ensure scale is applied to mouse coordinates
104 auto* r = asw::display::get_renderer();
105 if (r != nullptr) {
106 SDL_ConvertEventToRenderCoordinates(r, &e);
107 }
108
109 asw::input::_mouse_motion(e.motion.x, e.motion.y, e.motion.xrel, e.motion.yrel);
110 break;
111 }
112
113 case SDL_EVENT_MOUSE_WHEEL: {
114 asw::input::_mouse_wheel(e.wheel.y);
115 break;
116 }
117
118 case SDL_EVENT_GAMEPAD_AXIS_MOTION: {
119 asw::input::_controller_axis_motion(e.gaxis.which, e.gaxis.axis, e.gaxis.value);
120 break;
121 }
122
123 case SDL_EVENT_GAMEPAD_BUTTON_DOWN: {
124 asw::input::_controller_button_down(e.gbutton.which, e.gbutton.button);
125 break;
126 }
127
128 case SDL_EVENT_GAMEPAD_BUTTON_UP: {
129 asw::input::_controller_button_up(e.gbutton.which, e.gbutton.button);
130 break;
131 }
132
133 case SDL_EVENT_GAMEPAD_ADDED: {
134 asw::input::_controller_added(e.gdevice.which);
135 break;
136 }
137
138 case SDL_EVENT_GAMEPAD_REMOVED: {
139 asw::input::_controller_removed(e.gdevice.which);
140 break;
141 }
142
143 case SDL_EVENT_TEXT_INPUT: {
144 asw::input::_append_text(e.text.text);
145 break;
146 }
147
148 case SDL_EVENT_QUIT: {
149 exit();
150 }
151
152 default:
153 break;
154 }
155 }
156
157 // After the events, so actions match raw input on the same frame
159
161}
162
164{
165 return delta_time;
166}
167
168void asw::core::init(int width, int height, int scale)
169{
170 if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) {
171 asw::util::abort_on_error("SDL_Init");
172 }
173
175
176 if (!TTF_Init()) {
177 asw::util::abort_on_error("TTF_Init");
178 }
179
180 if (!asw::sound::_init()) {
181 asw::util::abort_on_error("Sound initialization failed");
182 }
183
184 asw::display::_init(width, height, scale);
185}
186
187void asw::core::init_opengl(int width, int height, int scale)
188{
189 if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) {
190 asw::util::abort_on_error("SDL_Init");
191 }
192
194
195 if (!TTF_Init()) {
196 asw::util::abort_on_error("TTF_Init");
197 }
198
199 if (!asw::sound::_init()) {
200 asw::util::abort_on_error("Sound initialization failed");
201 }
202
203 // --- Set GL attributes before creating the window ---
204 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); // Request OpenGL 3.x
205 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
206 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
207 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
208
209 asw::display::_init_opengl(width, height, scale);
210}
211
213{
214 asw::log::info("ASW Info");
215 asw::log::info("========");
216
217 const char* renderer_name = "none";
218 if (auto* r = asw::display::get_renderer(); r != nullptr) {
219 renderer_name = SDL_GetRendererName(r);
220 }
221
223 "SDL Version: {}.{}.{}", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_MICRO_VERSION);
224 asw::log::info("Renderer: {}", renderer_name);
225}
226
228{
229 exiting = true;
230}
231
233{
236
237 // Clear asset caches while SDL resources are still valid — SDL_Destroy*
238 // calls in the shared_ptr deleters are safe at this point.
240
241 // _shutdown() nulls the renderer/window pointers BEFORE calling
242 // SDL_DestroyRenderer/SDL_DestroyWindow. Any outstanding shared_ptr<SDL_Texture>
243 // held by user code will see a null renderer in their deleter and skip the
244 // SDL_DestroyTexture call, preventing use-after-free.
246
247 // Same pattern for sound: null mixer pointer before teardown so any
248 // surviving Sample/Music shared_ptrs become no-ops.
250
252
253 TTF_Quit();
254 SDL_Quit();
255}
256
257void asw::core::run(const std::function<void()>& frame)
258{
259#ifdef __EMSCRIPTEN__
260 // The browser calls the loop, so keep the frame alive after run() returns
261 // control to it
262 static std::function<void()> run_frame;
263 run_frame = frame;
264
265 emscripten_set_main_loop(
266 []() {
267 if (exiting) {
268 emscripten_cancel_main_loop();
269
270 // Let the page know, for example to offer a restart
271 EM_ASM({
272 if (Module.onStop) {
273 Module.onStop();
274 }
275 });
276 return;
277 }
278
279 run_frame();
280 },
281 0, true);
282#else
283 while (!exiting) {
284 frame();
285 }
286#endif
287}
288
290{
291 return exiting;
292}
Action binding system for the ASW library.
Asset routines for the ASW library.
Settings from a config file, set by whoever runs the game.
Core routines including main loop and initialization.
Display and window routines for the ASW library.
Input module for the ASW library.
Structured logging system.
constexpr float MAX_DELTA_TIME
Definition core.cpp:26
void clear_all()
Clear all cached assets. This will remove all cached textures, fonts, samples, and music.
Definition assets.cpp:333
void _shutdown()
Remove all settings. Called by asw::core::shutdown().
Definition config.cpp:75
void _init()
Load the file named by ASW_CONFIG, if it is set. Called by asw::core::init() before the window opens.
Definition config.cpp:65
void exit()
Exit the application. Sets exiting flag to true, which will cause the main loop to exit on the next u...
Definition core.cpp:227
void print_info()
Prints information about the core module.
Definition core.cpp:212
void init_opengl(int width, int height, int scale=1)
Initializes the core module for opengl.
Definition core.cpp:187
void run(const std::function< void()> &frame)
Run a main loop until exit() is called.
Definition core.cpp:257
void shutdown()
Cleanup resources used by the core module. Should be called on application exit.
Definition core.cpp:232
void init(int width, int height, int scale=1)
Initializes the core module.
Definition core.cpp:168
void update()
Updates core module functionality.
Definition core.cpp:32
float get_delta_time()
Get the time between the last two calls to update(). Capped at a quarter of a second,...
Definition core.cpp:163
bool is_exiting()
Return exiting status.
Definition core.cpp:289
asw::Vec2< int > get_logical_size()
Get the logical size of the window. This may differ from the actual size if scaling is enabled.
Definition display.cpp:198
void _init_opengl(int width, int height, int scale)
Initialize the display module with OpenGL support. Called by asw::core::init().
Definition display.cpp:91
void _shutdown()
Shut down the display module. Called by asw::core::shutdown(). Nulls the renderer and window pointers...
Definition display.cpp:116
asw::Renderer * get_renderer()
Get the SDL renderer.
Definition display.cpp:139
asw::Window * get_window()
Get the SDL window.
Definition display.cpp:144
void _init(int width, int height, int scale)
Initialize the display module. Called by asw::core::init().
Definition display.cpp:59
void _mouse_button_up(uint8_t button)
Mouse button up hook.
Definition input.cpp:622
void _shutdown()
Free cached cursors. Called by asw::core::shutdown().
Definition input.cpp:216
void _mouse_button_down(uint8_t button)
Mouse button down hook.
Definition input.cpp:607
void _key_up(SDL_Scancode scancode)
Key up hook.
Definition input.cpp:601
void _controller_removed(SDL_JoystickID id)
Controller removed hook.
Definition input.cpp:683
void reset()
Reset all input states. Called by the core.
Definition input.cpp:226
void _mouse_wheel(float delta_z)
Mouse wheel hook.
Definition input.cpp:643
void _key_down(SDL_Scancode scancode, bool repeat=false)
Key down hook.
Definition input.cpp:584
void clear_actions()
Remove all registered actions and their bindings.
Definition action.cpp:163
void _controller_axis_motion(SDL_JoystickID id, uint32_t axis, float value)
Axis moved hook.
Definition input.cpp:705
void update_actions()
Update cached action states from current raw input.
Definition action.cpp:192
void _controller_added(SDL_JoystickID id)
Controller added hook.
Definition input.cpp:648
void _append_text(const char *text)
Event Hooks.
Definition input.cpp:211
void _controller_button_down(SDL_JoystickID id, uint32_t button)
Button down hook.
Definition input.cpp:724
void _controller_button_up(SDL_JoystickID id, uint32_t button)
Button up hook.
Definition input.cpp:743
void _mouse_motion(float x, float y, float delta_x, float delta_y)
Mouse motion hook.
Definition input.cpp:633
void info(const std::string &message)
Log an info message.
Definition log.cpp:186
void _shutdown()
Shut down the sound module. Called automatically by asw::core::shutdown().
Definition sound.cpp:444
bool _init()
Initialize the sound module. Called automatically by asw::core::init().
Definition sound.cpp:465
void _update()
Update fades, ducking and positional sounds. Called automatically by asw::core::update().
Definition sound.cpp:511
void abort_on_error(const std::string &message)
Abort program and display error message.
Definition util.cpp:67
Sound module for the ASW library.
General utility functions.