Gamp v0.0.8
Gamp: Graphics, Audio, Multimedia and Processing
Loading...
Searching...
No Matches
gamp_sdl2.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright Gothel Software e.K.
4 *
5 * SPDX-License-Identifier: MIT
6 *
7 * This Source Code Form is subject to the terms of the MIT License
8 * If a copy of the MIT was not distributed with this file,
9 * you can obtain one at https://opensource.org/license/mit/.
10 */
11
12#include <cstdint>
13
14#include <jau/basic_types.hpp>
15#include <jau/cow_darray.hpp>
16#include <jau/debug.hpp>
17#include <jau/enum_util.hpp>
18#include <jau/environment.hpp>
19#include <jau/fraction_type.hpp>
20#include <jau/math/vec2i.hpp>
21#include <jau/secmem.hpp>
23
24#include <gamp/Gamp.hpp>
25#include <gamp/GampTypes.hpp>
26#include "gamp/Version.hpp"
31#include <gamp/wt/Window.hpp>
32
33#include <SDL2/SDL.h>
34#include <SDL2/SDL_events.h>
35#include <SDL2/SDL_timer.h>
36#include <SDL2/SDL_video.h>
37
38#include <SDL2/SDL_scancode.h>
39// #include <SDL2/SDL_surface.h>
40// #include <SDL2/SDL_ttf.h>
41// #include <SDL2/SDL_image.h>
42
43using namespace jau::int_literals;
44using namespace jau::fractions_i64_literals;
45using namespace jau::enums;
46
47using namespace gamp;
48using namespace gamp::wt;
49using namespace gamp::wt::event;
50
52static int gpu_forced_fps_ = -1;
53static bool gpu_fps_resync = true;
54static float gpu_fps = 0.0f;
56static int64_t gpu_frame_count = 0;
60
61/** Ratio pixel-size / window-size, a DPI derivative per axis*/
64
65//
66/**
67 * Width of the framebuffer coordinate in pixels.
68 *
69 * Framebuffer origin 0/0 is top-left corner.
70 */
71// static jau::math::Recti viewport;
72
73static void reset_gpu_fps(int fps) {
74 gpu_fps = float(fps);
75 gpu_fdur = jau::fraction_timespec(1.0/double(fps));
79 gpu_fps_resync = true;
80}
81
82int gamp::monitor_fps() noexcept { return monitor_frames_per_sec; }
83
84int gamp::gpu_forced_fps() noexcept { return gpu_forced_fps_; }
85
86void gamp::set_gpu_forced_fps(int fps) noexcept { gpu_forced_fps_=fps; reset_gpu_fps(fps); }
87
88static void on_window_resized(Window* win, int wwidth, int wheight, const jau::fraction_timespec& when, bool verbose=false) noexcept {
89 if( !win || !win->isValid() ) { return; }
90 SDL_Window* sdl_win = reinterpret_cast<SDL_Window*>(win->windowHandle()); // NOLINT
91 if( verbose ) {
92 jau_printf("Window::resized:: %d x %d: %s\n", wwidth, wheight, win->toString());
93 }
94 jau::math::Vec2i window_size = win->windowSize();
95 {
96 int wwidth2 = 0, wheight2 = 0;
97 SDL_GetWindowSize(sdl_win, &wwidth2, &wheight2);
98 if( verbose ) {
99 jau_printf("Window::resized: Size %d x %d -> %d x %d (given), %d x %d (query)\n", window_size.x, window_size.y, wwidth, wheight, wwidth2, wheight2);
100 }
101 if (0 == wwidth || 0 == wheight) {
102 wwidth = wwidth2;
103 wheight = wheight2;
104 }
105 }
106 window_size.set(wwidth, wheight);
107
108 // jau_printf("SDL: Couldn't fetch renderer (window resize): %s\n", SDL_GetError());
109 int fb_width = static_cast<int>(static_cast<float>(wwidth) * devicePixelRatio.x);
110 int fb_height = static_cast<int>(static_cast<float>(wheight) * devicePixelRatio.y);
111 if( verbose ) {
112 jau_printf("Window::resized: DevicePixelRatio Size %f x %f -> %d x %d\n",
113 devicePixelRatio.x, devicePixelRatio.y, fb_width, fb_height);
114 }
115
116 jau::math::Vec2i surface_size(fb_width, fb_height);
117 if( verbose ) {
118 printf("Window::resized: VP Size %s\n", surface_size.toString().c_str());
119 }
120 {
121 SDL_DisplayMode mode;
122 const int win_display_idx = SDL_GetWindowDisplayIndex(sdl_win);
123 jau::zero_bytes_sec(&mode, sizeof(mode));
124 SDL_GetCurrentDisplayMode(win_display_idx, &mode); // SDL_GetWindowDisplayMode(..) fails on some systems (wrong refresh_rate and logical size
125 if( verbose ) {
126 printf("Window::resized: WindowDisplayMode: %d x %d @ %d Hz @ display %d\n", mode.w, mode.h, mode.refresh_rate, win_display_idx);
127 }
128 if( mode.refresh_rate > 0 ) {
129 monitor_frames_per_sec = mode.refresh_rate;
130 }
131 }
132 if( verbose ) {
133 printf("Window::resized:: %s\n", win->toString().c_str());
134 }
135 win->notifyWindowResize(when, window_size, surface_size);
136}
137
138static std::atomic_bool gfx_subsystem_init_called = false;
139static std::atomic_bool gfx_subsystem_init = false;
141
145
149
150bool gamp::init_gfx_subsystem(const char* exe_path) {
151 bool exp_init_called = false;
152 if( !gfx_subsystem_init_called.compare_exchange_strong(exp_init_called, true) ) {
153 return gfx_subsystem_init;
154 }
156 printf("Gamp API %s, lib %s\n", gamp::VERSION_API, gamp::VERSION.toString().c_str());
157 printf("%s\n", jau::os::get_platform_info().c_str());
158
159 if (SDL_Init(SDL_INIT_TIMER) != 0) { // SDL_INIT_EVERYTHING
160 printf("SDL: Error initializing TIMER: %s\n", SDL_GetError());
161 return false;
162 }
163 {
166 const uint32_t sdl_ms = SDL_GetTicks();
167 const int64_t s = sdl_ms/1000;
168 const int64_t ns = ( sdl_ms - s*1000 ) *1'000'000;
169 const jau::fraction_timespec sdl_t1(s, ns);
170 // printf("SDL Timer-Sync: jau_t0: %s\n", init_gfx_t0.to_string().c_str());
171 // printf("SDL Timer-Sync: jau_t1: %s\n", jau_t1.to_string().c_str());
172 // printf("SDL Timer-Sync: sdl_t1: %s\n", sdl_t1.to_string().c_str());
173 const jau::fraction_timespec td = jau_t1 - sdl_t1;
174 printf("SDL Timer-Sync: td: %s\n", td.toString().c_str());
175 }
176 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) { // SDL_INIT_EVERYTHING
177 printf("SDL: Error initializing VIDEO/EVENTS: %s\n", SDL_GetError());
178 return false;
179 }
180#if 0
181 if ( ( IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG ) != IMG_INIT_PNG ) {
182 printf("SDL_image: Error initializing: %s\n", SDL_GetError());
183 return false;
184 }
185 if( 0 != TTF_Init() ) {
186 printf("SDL_TTF: Error initializing: %s\n", SDL_GetError());
187 return false;
188 }
189#endif
190 gfx_subsystem_init = true;
191
192 return true;
193}
194
195WindowSRef Window::create(const char* title, int wwidth, int wheight, const Capabilities &requested, bool verbose) {
197 return nullptr;
198 }
199 if( verbose ) {
200 printf("Window::create: '%s', %d x %d\n", title, wwidth, wheight);
201 }
202 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, std::to_string(gamp_filter_quality).c_str());
203
204 const Uint32 win_flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL; // | SDL_WINDOW_SHOWN;
205
206 SDL_Window* sdl_win = nullptr;
207 Uint32 sdl_win_id = 0;
208
209 Surface::setCaps((handle_t)sdl_win, requested);
210
211 sdl_win = SDL_CreateWindow(title,
212 SDL_WINDOWPOS_UNDEFINED,
213 SDL_WINDOWPOS_UNDEFINED,
214 wwidth, wheight,
215 win_flags);
216 if (nullptr == sdl_win) {
217 printf("SDL: Error initializing window: %s\n", SDL_GetError());
218 return nullptr;
219 }
220
221 sdl_win_id = SDL_GetWindowID(sdl_win);
222 if (0 == sdl_win_id) {
223 printf("SDL: Error retrieving window ID: %s\n", SDL_GetError());
224 SDL_DestroyWindow(sdl_win);
225 return nullptr;
226 }
227
228 gpu_frame_count = 0;
229 jau::math::Recti window_bounds(64, 64, wwidth, wheight);
230 jau::math::Vec2i surface_size(wwidth, wheight);
231 WindowSRef res = Window::wrapNative((handle_t)sdl_win, window_bounds, (handle_t)sdl_win, surface_size, requested);
232
233 on_window_resized(res.get(), wwidth, wheight, getElapsedMonotonicTime(), verbose);
234
236 window_list.push_back(res);
237 if( verbose ) {
238 printf("Window::create: %zu windows: %s\n", (size_t)window_list.size(), res->toString().c_str());
239 }
240 return res;
241}
242void Window::disposeImpl(handle_t handle) noexcept {
243 SDL_Window* sdl_win = reinterpret_cast<SDL_Window*>(handle); // NOLINT
244 if( nullptr == sdl_win ) {
245 return;
246 }
247 SDL_DestroyWindow(sdl_win);
248 try {
249 window_list.erase_if(false,
250 [this](const WindowSRef& a) noexcept -> bool { return a.get() == this; } );
251 } catch (std::exception &err) {
252 jau_ERR_PRINT("gamp::handle_events: Caught exception %s", err.what());
253 }
254 printf("Window Closed: Remaining windows: %zu\n", (size_t)window_list.size());
255}
256
257extern "C" {
259
261
262 EMSCRIPTEN_KEEPALIVE void set_window_size(int ww, int wh, float devPixelRatioX, float devPixelRatioY) noexcept {
263 jau::math::Vec2i win_size;
264 WindowSRef win = window_list.size() > 0 ? (*window_list.snapshot())[0] : nullptr;
265 const bool initial_call = !is_gfx_subsystem_initialized() || !win || !win->isValid();
266 if( win && win->isValid() ) {
267 win_size = win->windowSize();
268 }
269 static bool warn_once = true;
270 if (win_size.x != ww || win_size.y != wh ||
271 devicePixelRatio.x != devPixelRatioX || devicePixelRatio.y != devPixelRatioY) {
272 if (devPixelRatioX >= 0.5f && devPixelRatioY >= 0.5f) {
273 devicePixelRatio.x = devPixelRatioX;
274 devicePixelRatio.y = devPixelRatioY;
275 }
276 if (std::abs(win_size.x - ww) > 1 || std::abs(win_size.y - wh) > 1) {
277 if( initial_call || 0 == win_size.x || 0 == win_size.y) {
278 printf("JS Window Initial Size: Win %d x %d -> %d x %d, devPixelRatio %s\n",
279 win_size.x, win_size.y, ww, wh, devicePixelRatio.toString().c_str());
280 win_size.x = ww;
281 win_size.y = wh;
282 } else {
283 printf("JS Window Resized: Win %d x %d -> %d x %d, devPixelRatio %s\n",
284 win_size.x, win_size.y, ww, wh, devicePixelRatio.toString().c_str());
285 SDL_SetWindowSize(reinterpret_cast<SDL_Window*>(win->windowHandle()), ww, wh); // NOLINT
286 warn_once = true;
287 on_window_resized(win.get(), ww, wh, getElapsedMonotonicTime(), true);
288 }
289 } else if (warn_once) {
290 warn_once = false;
291 printf("JS Window Resize Ignored: Win %d x %d -> %d x %d, devPixelRatio %s\n",
292 win_size.x, win_size.y, ww, wh, devicePixelRatio.toString().c_str());
293 }
294 } else {
295 printf("JS Window Resize Same-Size: Win %d x %d -> %d x %d, devPixelRatio %s\n",
296 win_size.x, win_size.y, ww, wh, devicePixelRatio.toString().c_str());
297 }
298 }
299}
300
301static bool gamp_show_fps = true;
302
303static void gamp_swap_gpu_buffer(bool swapAllWindows, int fps) noexcept {
304 if( swapAllWindows ) {
305 for(const WindowSRef& win : *window_list.snapshot() ) {
306 if( win ) {
307 win->surfaceSwap();
308 }
309 }
310 } // else just finish
313 constexpr jau::fraction_timespec fps_resync(3, 0); // 3s
314 constexpr jau::fraction_timespec fps_avg_period(5, 0); // 5s
316 if( gpu_fps_resync && td >= fps_resync ) {
318 gpu_frame_count = 0;
319 gpu_fps_resync = false;
320 if( gamp_show_fps ) {
321 jau_PLAIN_PRINT(true, "fps resync");
322 }
323 } else if( td >= fps_avg_period ) {
325 gpu_fps = float(double(gpu_frame_count) / ( double(td.to_us()) / 1000000.0 ));
327 if( gamp_show_fps ) {
328 jau_PLAIN_PRINT(true, "fps(5s): fps %3.3f, dur %" PRIu64 "ms over %" PRIi64 " frames", gpu_fps, gpu_fdur.to_ms(), gpu_frame_count);
329 }
330 gpu_frame_count = 0;
331 }
332 if( 0 < fps ) {
333 constexpr uint64_t ns_per_ms = 1'000'000UL;
334 const jau::fraction_timespec fudge(0, ns_per_ms / 4); // ns granularity
335 const jau::fraction_timespec td_per_frame(1.0 / fps);
336 const jau::fraction_timespec td_this_frame = gpu_swap_t0 - gpu_swap_t1;
337 const jau::fraction_timespec td_diff = td_per_frame - td_this_frame;
338 if( td_diff > fudge ) {
339 struct timespec ts;
340 ts.tv_sec = static_cast<decltype(ts.tv_sec)>(td_diff.tv_sec); // signed 32- or 64-bit integer
341 ts.tv_nsec = td_diff.tv_nsec - fudge.tv_nsec;
342 nanosleep( &ts, nullptr );
343 // pixel::log_printf("soft-sync [exp %zd > has %zd]ms, delay %" PRIi64 "ms (%lds, %ldns)\n",
344 // ms_per_frame, ms_this_frame, td_ns/pixel::NanoPerMilli, ts.tv_sec, ts.tv_nsec);
345 }
347 } else {
349 }
350}
351
352void gamp::swap_gpu_buffer(int fps) noexcept {
353 gamp_swap_gpu_buffer(true, fps);
354}
355
356float gamp::gpu_avg_fps() noexcept {
357 return gpu_fps;
358}
359
361 return gpu_fdur;
362}
363
364static std::pair<VKeyCode, InputModifier> to_VKeyCode(SDL_Scancode scancode) {
365 if (SDL_SCANCODE_A <= scancode && scancode <= SDL_SCANCODE_Z) {
366 return { VKeyCode(*VKeyCode::VK_A + (scancode - SDL_SCANCODE_A)), InputModifier::none};
367 }
368 if (SDL_SCANCODE_1 <= scancode && scancode <= SDL_SCANCODE_9) {
369 return { VKeyCode(*VKeyCode::VK_1 + (scancode - SDL_SCANCODE_1)), InputModifier::none};
370 }
371 if (SDL_SCANCODE_0 == scancode) {
373 }
374 switch (scancode) {
375 case SDL_SCANCODE_ESCAPE:
377 case SDL_SCANCODE_LSHIFT:
379 case SDL_SCANCODE_RSHIFT:
381 case SDL_SCANCODE_LALT:
383 case SDL_SCANCODE_RALT:
385 case SDL_SCANCODE_LCTRL:
387 case SDL_SCANCODE_RCTRL:
389 case SDL_SCANCODE_PAUSE:
391 case SDL_SCANCODE_UP:
393 case SDL_SCANCODE_LEFT:
395 case SDL_SCANCODE_DOWN:
397 case SDL_SCANCODE_RIGHT:
399 case SDL_SCANCODE_KP_ENTER:
400 [[fallthrough]];
401 case SDL_SCANCODE_RETURN:
403
404 case SDL_SCANCODE_SEMICOLON:
406
407 case SDL_SCANCODE_MINUS:
408 [[fallthrough]];
409 case SDL_SCANCODE_KP_MINUS:
411
412 case SDL_SCANCODE_KP_PLUS:
414
415 case SDL_SCANCODE_KP_MULTIPLY:
417
418 case SDL_SCANCODE_SLASH:
419 [[fallthrough]];
420 case SDL_SCANCODE_KP_DIVIDE:
422
423 case SDL_SCANCODE_KP_PERCENT:
425
426 case SDL_SCANCODE_KP_LEFTPAREN:
427 [[fallthrough]];
428 case SDL_SCANCODE_KP_LEFTBRACE:
429 [[fallthrough]];
430 case SDL_SCANCODE_LEFTBRACKET:
432
433 case SDL_SCANCODE_KP_RIGHTPAREN:
434 [[fallthrough]];
435 case SDL_SCANCODE_KP_RIGHTBRACE:
436 [[fallthrough]];
437 case SDL_SCANCODE_RIGHTBRACKET:
439
440 case SDL_SCANCODE_COMMA:
442
443 case SDL_SCANCODE_PERIOD:
445
446 case SDL_SCANCODE_SPACE:
447 [[fallthrough]];
448 case SDL_SCANCODE_TAB:
450
451 case SDL_SCANCODE_BACKSPACE:
453
454
455 default:
457 }
458}
459
460static uint16_t to_ascii(SDL_Scancode scancode, const InputModifier& mods) {
461 if (SDL_SCANCODE_A <= scancode && scancode <= SDL_SCANCODE_Z) {
462 if( has_any(mods, InputModifier::shift) ) {
463 return 'A' + (scancode - SDL_SCANCODE_A);
464 } else {
465 return 'a' + (scancode - SDL_SCANCODE_A);
466 }
467 }
468 if (SDL_SCANCODE_1 <= scancode && scancode <= SDL_SCANCODE_9) {
469 return '1' + (scancode - SDL_SCANCODE_1);
470 }
471 if (SDL_SCANCODE_0 == scancode) {
472 return '0' + (scancode - SDL_SCANCODE_0);
473 }
474 switch (scancode) {
475 case SDL_SCANCODE_SEMICOLON:
476 return ';';
477
478 case SDL_SCANCODE_MINUS:
479 [[fallthrough]];
480 case SDL_SCANCODE_KP_MINUS:
481 return '-';
482
483 case SDL_SCANCODE_KP_PLUS:
484 return '+';
485
486 case SDL_SCANCODE_KP_MULTIPLY:
487 return '*';
488
489 case SDL_SCANCODE_SLASH:
490 [[fallthrough]];
491 case SDL_SCANCODE_KP_DIVIDE:
492 return '/';
493
494 case SDL_SCANCODE_KP_PERCENT:
495 return '%';
496
497 case SDL_SCANCODE_KP_LEFTPAREN:
498 [[fallthrough]];
499 case SDL_SCANCODE_KP_LEFTBRACE:
500 [[fallthrough]];
501 case SDL_SCANCODE_LEFTBRACKET:
502 return '(';
503
504 case SDL_SCANCODE_KP_RIGHTPAREN:
505 [[fallthrough]];
506 case SDL_SCANCODE_KP_RIGHTBRACE:
507 [[fallthrough]];
508 case SDL_SCANCODE_RIGHTBRACKET:
509 return ')';
510
511 case SDL_SCANCODE_COMMA:
512 return ',';
513
514 case SDL_SCANCODE_PERIOD:
515 return '.';
516
517 case SDL_SCANCODE_SPACE:
518 [[fallthrough]];
519 case SDL_SCANCODE_TAB:
520 return ' ';
521
522 case SDL_SCANCODE_RETURN:
523 [[fallthrough]];
524 case SDL_SCANCODE_KP_ENTER:
525 return '\n';
526
527 case SDL_SCANCODE_BACKSPACE:
528 return 0x08;
529
530 default:
531 return 0;
532 }
533 return 0;
534}
535
536static Window* getWin(Uint32 id) {
537 SDL_Window *p = SDL_GetWindowFromID(id);
538 if( !p ) {
539 return nullptr;
540 }
541 handle_t h = reinterpret_cast<handle_t>(p);
542 for(const WindowSRef& win : *window_list.snapshot() ) {
543 if( win && win->windowHandle() == h ) {
544 return win.get();
545 }
546 }
547 return nullptr;
548}
549
550size_t gamp::handle_events() noexcept {
552 return 0;
553 }
554 size_t event_count = 0;
555 SDL_Event sdl_event;
556
557 while (SDL_PollEvent(&sdl_event)) {
558 ++event_count;
559 const int64_t s = sdl_event.common.timestamp/1000;
560 const int64_t ns = ( sdl_event.common.timestamp - s*1000 ) *1'000'000;
561 const jau::fraction_timespec when(s, ns);
562 switch (sdl_event.type) {
563 case SDL_QUIT: {
564 printf("App Close Requested\n");
565 for(const WindowSRef& win : *window_list.snapshot() ) {
566 win->dispose(when);
567 }
568 window_list.clear(true);
569 } break;
570
571 case SDL_WINDOWEVENT: {
572 Window* win = getWin(sdl_event.window.windowID);
573 if( win ) {
574 switch (sdl_event.window.event) {
575 case SDL_WINDOWEVENT_CLOSE: {
576 win->dispose(when);
577 printf("Window Close Requested: %zu windows\n", (size_t)window_list.size());
578 } break;
579 case SDL_WINDOWEVENT_SHOWN:
580 printf("Window Shown\n");
582 break;
583 case SDL_WINDOWEVENT_HIDDEN:
584 printf("Window Hidden\n");
586 break;
587 case SDL_WINDOWEVENT_MOVED:
588 printf("Window Moved: %d x %d\n", sdl_event.window.data1, sdl_event.window.data2);
589 win->notifyWindowMoved(when, jau::math::Vec2i(sdl_event.window.data1, sdl_event.window.data2));
590 break;
591 case SDL_WINDOWEVENT_RESIZED:
592 printf("Window Resized: %d x %d\n", sdl_event.window.data1, sdl_event.window.data2);
593 on_window_resized(win, sdl_event.window.data1, sdl_event.window.data2, when);
594 break;
595 case SDL_WINDOWEVENT_SIZE_CHANGED:
596 printf("Window SizeChanged: %d x %d\n", sdl_event.window.data1, sdl_event.window.data2);
597 break;
598
599 default: break;
600 }
601 } }
602 break;
603
604 case SDL_MOUSEMOTION: {
605 Window* win = getWin(sdl_event.motion.windowID);
606 if( win ) {
607 int ypos = int((float)sdl_event.motion.y*devicePixelRatio.y);
608 if( win->isPointerBLOriented() ) {
609 ypos = win->surfaceSize().y - ypos - 1;
610 }
611 win->notifyPointer(EVENT_POINTER_MOVED, when, PointerType::mouse, (uint16_t)sdl_event.motion.which,
612 jau::math::Vec2i(int((float)sdl_event.motion.x*devicePixelRatio.x), ypos),
613 /*clickCount=*/0, InputButton::none,
614 jau::math::Vec3f(), /*rotationScale=*/0.0f);
615 } }
616 break;
617 case SDL_MOUSEBUTTONDOWN: {
618 Window* win = getWin(sdl_event.button.windowID);
619 if( win ) {
620 int ypos = int((float)sdl_event.button.y*devicePixelRatio.y);
621 if( win->isPointerBLOriented() ) {
622 ypos = win->surfaceSize().y - ypos - 1;
623 }
625 jau::math::Vec2i(int((float)sdl_event.button.x*devicePixelRatio.x), ypos),
626 /*clickCount=*/sdl_event.button.clicks,
627 static_cast<InputButton>(sdl_event.button.button),
628 jau::math::Vec3f(), /*rotationScale=*/0.0f);
629 } }
630 break;
631 case SDL_MOUSEBUTTONUP: {
632 Window* win = getWin(sdl_event.button.windowID);
633 if( win ) {
634 int ypos = int((float)sdl_event.button.y*devicePixelRatio.y);
635 if( win->isPointerBLOriented() ) {
636 ypos = win->surfaceSize().y - ypos - 1;
637 }
639 jau::math::Vec2i(int((float)sdl_event.button.x*devicePixelRatio.x), ypos),
640 /*clickCount=*/sdl_event.button.clicks,
641 static_cast<InputButton>(sdl_event.button.button),
642 jau::math::Vec3f(), /*rotationScale=*/0.0f);
643 } }
644 break;
645 case SDL_MOUSEWHEEL: {
646 Window* win = getWin(sdl_event.wheel.windowID);
647 if( win ) {
648 float rotX = sdl_event.wheel.preciseX;
649 float rotY = sdl_event.wheel.preciseY;
651 std::swap(rotX, rotY);
652 }
653 win->notifyPointer(EVENT_POINTER_WHEEL, when, PointerType::mouse, (uint16_t)sdl_event.wheel.which,
654 jau::math::Vec2i((int)sdl_event.wheel.mouseX, (int)sdl_event.wheel.mouseY),
655 /*clickCount=*/0,
657 jau::math::Vec3f(rotX, rotY, 0), /*rotationScale=*/0.0f);
658 } }
659 break;
660 case SDL_KEYUP: {
661 Window* win = getWin(sdl_event.key.windowID);
662 if( win ) {
663 const SDL_Scancode scancode = sdl_event.key.keysym.scancode;
664 const std::pair<VKeyCode, InputModifier> r = to_VKeyCode(scancode);
665 win->notifyKeyReleased(when, r.first, r.second, to_ascii(scancode, win->keyTracker().modifier()));
666 } }
667 break;
668 case SDL_KEYDOWN: {
669 Window* win = getWin(sdl_event.key.windowID);
670 if( win ) {
671 const SDL_Scancode scancode = sdl_event.key.keysym.scancode;
672 const std::pair<VKeyCode, InputModifier> r = to_VKeyCode(scancode);
673 if( sdl_event.key.repeat ) {
674 win->notifyKeyReleased(when, r.first, r.second | InputModifier::repeat, to_ascii(scancode, win->keyTracker().modifier()));
675 win->notifyKeyPressed (when, r.first, r.second | InputModifier::repeat, to_ascii(scancode, win->keyTracker().modifier()));
676 } else {
677 win->notifyKeyPressed(when, r.first, r.second, to_ascii(scancode, win->keyTracker().modifier()));
678 }
679 } }
680 break;
681 default: break;
682 }
683 }
684 return event_count;
685}
686
687bool gamp::mainloop_default() noexcept { // NOLINT
689
691 jau::cow_darray<WindowSRef>::storage_t windows = *window_refs;
692 if( windows.size() == 0 ) {
693 return false;
694 }
695 for(const WindowSRef& win : windows ) {
696 if( win && win->isValid() ) {
697 win->display(getElapsedMonotonicTime());
698 }
699 }
700 for(const WindowSRef& win : windows ) {
701 if( win ) {
702 win->surfaceSwap();
703 }
704 }
706 return true;
707}
708void gamp::mainloop_void() noexcept {
709 if( !mainloop_default() ) {
710 printf("Exit Application\n");
711 #if defined(__EMSCRIPTEN__)
712 emscripten_cancel_main_loop();
713 #else
714 exit(0);
715 #endif
716 }
717}
718
719void gamp::shutdown() noexcept {
721 for(const WindowSRef& win : *window_list.snapshot() ) {
722 win->dispose(when);
723 // win->notifyWindowEvent(EVENT_WINDOW_DESTROY_NOTIFY, when);
724 }
725 window_list.clear(true);
726}
#define EMSCRIPTEN_KEEPALIVE
Definition GampTypes.hpp:42
String toString()
Specifies a set of capabilities that a window's rendering context must support, such as color depth p...
static void setCaps(handle_t surface_handle, const Capabilities &requested) noexcept
constexpr const Vec2i & surfaceSize() const noexcept
Returns the surface size of the client area excluding insets (window decorations) in pixel units.
Definition Surface.hpp:177
constexpr handle_t windowHandle() const noexcept
Returns the handle to the surface for this NativeSurface.
Definition Window.hpp:264
void notifyKeyReleased(const jau::fraction_timespec &when, VKeyCode keySym, InputModifier keySymMods, uint16_t keyChar) noexcept
Definition Window.hpp:307
bool isValid() const noexcept override
Definition Window.hpp:265
static WindowSRef wrapNative(handle_t window_handle, const Recti &window_bounds, handle_t surface_handle, const Vec2i &surface_size, const Capabilities &requested)
Create an new instance, wrapping the native windowing toolkit's handle/resources.
Definition Window.hpp:199
void notifyWindowEvent(uint16_t type, const jau::fraction_timespec &when, bool value=true) noexcept
Definition Window.hpp:278
constexpr Vec2i windowSize() const noexcept
Returns the window size of the client area excluding insets (window decorations) in window units.
Definition Window.hpp:233
void notifyWindowResize(const jau::fraction_timespec &when, const jau::math::Vec2i &winSize, const jau::math::Vec2i &surfSize) noexcept
Definition Window.hpp:281
void notifyWindowMoved(const jau::fraction_timespec &when, const jau::math::Vec2i &winPos) noexcept
Definition Window.hpp:287
static WindowSRef create(const char *title, int wwidth, int wheight, const Capabilities &requested, bool verbose=false)
Create an new instance using a native windowing toolkit.
void notifyKeyPressed(const jau::fraction_timespec &when, VKeyCode keySym, InputModifier keySymMods, uint16_t keyChar) noexcept
Definition Window.hpp:304
void notifyPointer(uint16_t type, const jau::fraction_timespec &when, PointerType ptype, uint16_t id, jau::math::Vec2i pos, uint16_t clickCount, InputButton button, jau::math::Vec3f rotation, float rotationScale) noexcept
Definition Window.hpp:318
const KeyboardTracker & keyTracker() const noexcept
Definition Window.hpp:302
void dispose(const jau::fraction_timespec &when) noexcept override
Definition Window.hpp:355
std::string toString() const noexcept
Definition gamp_wt.cpp:145
constexpr bool isPointerBLOriented() const noexcept
Returns true if this window delivers PointerEvent in OpenGL's coordinate system, origin at bottom lef...
Definition Window.hpp:247
virtual InputModifier modifier() const noexcept=0
Implementation of a Copy-On-Write (CoW) using jau::darray as the underlying storage,...
std::shared_ptr< storage_t > storage_ref_t
darray< value_type, size_type, allocator_type, use_memmove, use_secmem > storage_t
constexpr size_type size() const noexcept
Like std::vector::size().
Definition darray.hpp:1116
value_type y
Definition vec2i.hpp:64
std::string toString() const noexcept
Definition vec2i.hpp:215
constexpr Vector2I & set(const value_type vx, const value_type vy) noexcept
Definition vec2i.hpp:124
value_type x
Definition vec2i.hpp:63
#define jau_PLAIN_PRINT(printPrefix, fmt,...)
Use for unconditional plain messages, prefix '[elapsed_time] ' if printPrefix == true.
Definition debug.hpp:171
#define jau_printf(fmt,...)
Definition debug.hpp:253
#define jau_ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition debug.hpp:152
static std::pair< VKeyCode, InputModifier > to_VKeyCode(SDL_Scancode scancode)
static jau::fraction_timespec init_gfx_t0
static uint16_t to_ascii(SDL_Scancode scancode, const InputModifier &mods)
static void gamp_swap_gpu_buffer(bool swapAllWindows, int fps) noexcept
static jau::fraction_timespec gpu_fps_t0
Definition gamp_sdl2.cpp:57
static jau::fraction_timespec gpu_swap_t1
Definition gamp_sdl2.cpp:59
static void on_window_resized(Window *win, int wwidth, int wheight, const jau::fraction_timespec &when, bool verbose=false) noexcept
Definition gamp_sdl2.cpp:88
static void reset_gpu_fps(int fps)
Width of the framebuffer coordinate in pixels.
Definition gamp_sdl2.cpp:73
static int gpu_forced_fps_
Definition gamp_sdl2.cpp:52
static Window * getWin(Uint32 id)
static jau::fraction_timespec gpu_fdur
Definition gamp_sdl2.cpp:55
static int monitor_frames_per_sec
Definition gamp_sdl2.cpp:51
static jau::cow_darray< WindowSRef > window_list
Definition gamp_sdl2.cpp:63
static jau::math::Vec2f devicePixelRatio(1, 1)
Ratio pixel-size / window-size, a DPI derivative per axis.
EMSCRIPTEN_KEEPALIVE void set_window_size(int ww, int wh, float devPixelRatioX, float devPixelRatioY) noexcept
static std::atomic_bool gfx_subsystem_init_called
static bool gpu_fps_resync
Definition gamp_sdl2.cpp:53
static jau::fraction_timespec gpu_swap_t0
Definition gamp_sdl2.cpp:58
static int64_t gpu_frame_count
Definition gamp_sdl2.cpp:56
EMSCRIPTEN_KEEPALIVE int get_forced_fps() noexcept
static bool gamp_show_fps
static float gpu_fps
Definition gamp_sdl2.cpp:54
static std::atomic_bool gfx_subsystem_init
EMSCRIPTEN_KEEPALIVE void set_forced_fps(int v) noexcept
constexpr bool has_any(const E mask, const E bits) noexcept
fraction_timespec getMonotonicTime() noexcept
Returns current monotonic time since Unix Epoch 00:00:00 UTC on 1970-01-01.
static constexpr short EVENT_POINTER_WHEEL
Definition Event.hpp:70
static constexpr short EVENT_POINTER_PRESSED
Definition Event.hpp:66
static constexpr short EVENT_POINTER_RELEASED
Definition Event.hpp:67
VKeyCode
Virtual key code following UTF16 specification.
Definition KeyEvent.hpp:45
std::shared_ptr< Window > WindowSRef
Definition Event.hpp:36
static constexpr short EVENT_POINTER_MOVED
Definition Event.hpp:68
static constexpr uint16_t EVENT_WINDOW_VISIBILITY_CHANGED
Definition Event.hpp:54
@ repeat
Event is caused by auto-repeat.
Definition Event.hpp:175
@ VK_SEMICOLON
Constant for the semicolon keyu, "u,".
Definition KeyEvent.hpp:221
@ VK_PLUS
Constant for the "+" key.
Definition KeyEvent.hpp:182
@ VK_ESCAPE
Constant for the ESCAPE function key.
Definition KeyEvent.hpp:129
@ VK_PERCENT
Constant for the "%" key.
Definition KeyEvent.hpp:164
@ VK_BACK_SPACE
Constant for the BACK SPACE key "\b"u, matching ASCII.
Definition KeyEvent.hpp:76
@ VK_RIGHT
Constant for the cursor- or numerical-pad right arrow key.
Definition KeyEvent.hpp:461
@ VK_DOWN
Constant for the cursor- or numerical pad down arrow key.
Definition KeyEvent.hpp:464
@ VK_CONTROL
Constant for the CTRL function key.
Definition KeyEvent.hpp:102
@ VK_COMMA
Constant for the comma keyu, "u,".
Definition KeyEvent.hpp:185
@ VK_A
VK_A thru VK_Z are the same as Capital UTF16/ASCII 'A' thru 'Z' (0x41 - 0x5A)
Definition KeyEvent.hpp:239
@ VK_ENTER
Constant for the ENTER keyu, i.e.
Definition KeyEvent.hpp:91
@ VK_UP
Constant for the cursor- or numerical-pad up arrow key.
Definition KeyEvent.hpp:458
@ VK_PERIOD
Constant for the period keyu, ".".
Definition KeyEvent.hpp:191
@ VK_TAB
Constant for the HORIZ TAB key "\t"u, matching ASCII.
Definition KeyEvent.hpp:79
@ VK_UNDEFINED
This value, {@value}, is used to indicate that the keyCode is unknown.
Definition KeyEvent.hpp:53
@ VK_LEFT
Constant for the cursor- or numerical-pad left arrow key.
Definition KeyEvent.hpp:455
@ VK_SHIFT
Constant for the SHIFT function key.
Definition KeyEvent.hpp:96
@ VK_LEFT_PARENTHESIS
Constant for the "(" key.
Definition KeyEvent.hpp:173
@ VK_RIGHT_PARENTHESIS
Constant for the ")" key.
Definition KeyEvent.hpp:176
@ VK_0
VK_0 thru VK_9 are the same as UTF16/ASCII '0' thru '9' [0x30 - 0x39].
Definition KeyEvent.hpp:197
@ VK_ALT
Constant for the ALT function key.
Definition KeyEvent.hpp:105
@ VK_MINUS
Constant for the minus keyu, "-".
Definition KeyEvent.hpp:188
@ VK_SLASH
Constant for the forward slash keyu, "/".
Definition KeyEvent.hpp:194
@ VK_PAUSE
Constant for the PAUSE function key.
Definition KeyEvent.hpp:115
@ VK_MULTIPLY
Numeric keypad multiply key.
Definition KeyEvent.hpp:443
std::string lookup_and_register_asset_dir(const char *exe_path, const char *asset_file="fonts/freefont/FreeSansBold.ttf", const char *asset_install_subdir="gamp") noexcept
Definition gamp.cpp:31
const jau::fraction_timespec & gpu_avg_framedur() noexcept
Returns the measured gpu frame duration in [s] each 5s, starting with 1/gpu_avg_fps()
uintptr_t handle_t
A native handle type, big enough to store a pointer.
Definition GampTypes.hpp:52
void mainloop_void() noexcept
Calls mainloop_default(), but exits application if returning false.
jau::fraction_timespec getElapsedMonotonicTime() noexcept
Returns the elapsed monotonic time since init_gfx_subsystem, synchronized with the gfx subsystem time...
bool init_gfx_subsystem(const char *exe_path)
GFX Toolkit: Initialize the subsystem once.
float gpu_avg_fps() noexcept
Returns the measured gpu fps each 5s, starting with monitor_fps()
bool is_gfx_subsystem_initialized() noexcept
const char * VERSION_API
bool mainloop_default() noexcept
Performs the whole tasks for all created gamp::wt::Window instances.
int gpu_forced_fps() noexcept
Returns optional forced frames per seconds or -1 if unset, set via set_gpu_forced_fps().
Definition gamp_sdl2.cpp:84
size_t handle_events() noexcept
GFX Toolkit: Handle windowing and keyboard events.
void swap_gpu_buffer(int fps) noexcept
GFX Toolkit: Swap GPU back to front framebuffer of all windows using given fps, maintaining vertical ...
int monitor_fps() noexcept
Monitor frames per seconds.
Definition gamp_sdl2.cpp:82
const jau::util::VersionNumberString VERSION
void set_gpu_forced_fps(int fps) noexcept
Optional forced frames per seconds, pass to swap_gpu_buffer() by default.
Definition gamp_sdl2.cpp:86
void shutdown() noexcept
Vector2I< int > Vec2i
Definition vec2i.hpp:321
Vector2F< float > Vec2f
Definition vec2f.hpp:404
RectI< int > Recti
Definition recti.hpp:146
Vector3F< float > Vec3f
Definition vec3f.hpp:422
std::string get_platform_info(std::string &sb) noexcept
void zero_bytes_sec(void *s, size_t n) noexcept __attrdecl_no_optimize__
Wrapper to ::explicit_bzero(), ::bzero() or ::memset(), whichever is available in that order.
Gamp: Graphics, Audio, Multimedia and Processing Framework (Native C++, WebAssembly,...
Definition PTS.hpp:24
Author: Sven Gothel sgothel@jausoft.com Copyright Gothel Software e.K.
Definition enum_util.hpp:65
Timespec structure using int64_t for its components in analogy to struct timespec_t on 64-bit platfor...
int64_t tv_nsec
Nanoseconds component with its absolute value in range [0..1'000'000'000[ or [0..1'000'000'000).
int64_t tv_sec
Seconds component, with its absolute value in range [0..inf[ or [0..inf).
constexpr uint64_t to_us() const noexcept
Returns time in microseconds.
std::string toString() const noexcept
Return simple string representation in seconds and nanoseconds.
int printf(const char *format,...)
Operating Systems predefined macros.