Gamp v0.0.7-36-g24b1eb6
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 printf("Window::resized:: %d x %d: %s\n", wwidth, wheight, win->toString().c_str());
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 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 // 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 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.to_string().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
195WindowRef Window::create(const char* title, int wwidth, int wheight, 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 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
210 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
211 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
212 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
213 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
214 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 1);
215
216 sdl_win = SDL_CreateWindow(title,
217 SDL_WINDOWPOS_UNDEFINED,
218 SDL_WINDOWPOS_UNDEFINED,
219 wwidth, wheight,
220 win_flags);
221 if (nullptr == sdl_win) {
222 printf("SDL: Error initializing window: %s\n", SDL_GetError());
223 return nullptr;
224 }
225
226 sdl_win_id = SDL_GetWindowID(sdl_win);
227 if (0 == sdl_win_id) {
228 printf("SDL: Error retrieving window ID: %s\n", SDL_GetError());
229 SDL_DestroyWindow(sdl_win);
230 return nullptr;
231 }
232
233 gpu_frame_count = 0;
234 jau::math::Recti window_bounds(64, 64, wwidth, wheight);
235 jau::math::Vec2i surface_size(wwidth, wheight);
236 WindowRef res = Window::wrapNative((handle_t)sdl_win, window_bounds, (handle_t)sdl_win, surface_size);
237
238 on_window_resized(res.get(), wwidth, wheight, getElapsedMonotonicTime(), verbose);
239
241 window_list.push_back(res);
242 if( verbose ) {
243 printf("Window::create: %zu windows: %s\n", (size_t)window_list.size(), res->toString().c_str());
244 }
245 return res;
246}
247void Window::disposeImpl(handle_t handle) noexcept {
248 SDL_Window* sdl_win = reinterpret_cast<SDL_Window*>(handle); // NOLINT
249 if( nullptr == sdl_win ) {
250 return;
251 }
252 SDL_DestroyWindow(sdl_win);
253 try {
254 window_list.erase_if(false,
255 [this](const WindowRef& a) noexcept -> bool { return a.get() == this; } );
256 } catch (std::exception &err) {
257 ERR_PRINT("gamp::handle_events: Caught exception %s", err.what());
258 }
259 printf("Window Closed: Remaining windows: %zu\n", (size_t)window_list.size());
260}
261
262extern "C" {
264
266
267 EMSCRIPTEN_KEEPALIVE void set_window_size(int ww, int wh, float devPixelRatioX, float devPixelRatioY) noexcept {
268 jau::math::Vec2i win_size;
269 WindowRef win = window_list.size() > 0 ? (*window_list.snapshot())[0] : nullptr;
270 const bool initial_call = !is_gfx_subsystem_initialized() || !win || !win->isValid();
271 if( win && win->isValid() ) {
272 win_size = win->windowSize();
273 }
274 static bool warn_once = true;
275 if (win_size.x != ww || win_size.y != wh ||
276 devicePixelRatio.x != devPixelRatioX || devicePixelRatio.y != devPixelRatioY) {
277 if (devPixelRatioX >= 0.5f && devPixelRatioY >= 0.5f) {
278 devicePixelRatio.x = devPixelRatioX;
279 devicePixelRatio.y = devPixelRatioY;
280 }
281 if (std::abs(win_size.x - ww) > 1 || std::abs(win_size.y - wh) > 1) {
282 if( initial_call || 0 == win_size.x || 0 == win_size.y) {
283 printf("JS Window Initial Size: Win %d x %d -> %d x %d, devPixelRatio %s\n",
284 win_size.x, win_size.y, ww, wh, devicePixelRatio.toString().c_str());
285 win_size.x = ww;
286 win_size.y = wh;
287 } else {
288 printf("JS Window Resized: Win %d x %d -> %d x %d, devPixelRatio %s\n",
289 win_size.x, win_size.y, ww, wh, devicePixelRatio.toString().c_str());
290 SDL_SetWindowSize(reinterpret_cast<SDL_Window*>(win->windowHandle()), ww, wh); // NOLINT
291 warn_once = true;
292 on_window_resized(win.get(), ww, wh, getElapsedMonotonicTime(), true);
293 }
294 } else if (warn_once) {
295 warn_once = false;
296 printf("JS Window Resize Ignored: Win %d x %d -> %d x %d, devPixelRatio %s\n",
297 win_size.x, win_size.y, ww, wh, devicePixelRatio.toString().c_str());
298 }
299 } else {
300 printf("JS Window Resize Same-Size: Win %d x %d -> %d x %d, devPixelRatio %s\n",
301 win_size.x, win_size.y, ww, wh, devicePixelRatio.toString().c_str());
302 }
303 }
304}
305
306static bool gamp_show_fps = true;
307
308static void gamp_swap_gpu_buffer(bool swapAllWindows, int fps) noexcept {
309 if( swapAllWindows ) {
310 for(const WindowRef& win : *window_list.snapshot() ) {
311 if( win ) {
312 win->surfaceSwap();
313 }
314 }
315 } // else just finish
318 constexpr jau::fraction_timespec fps_resync(3, 0); // 3s
319 constexpr jau::fraction_timespec fps_avg_period(5, 0); // 5s
321 if( gpu_fps_resync && td >= fps_resync ) {
323 gpu_frame_count = 0;
324 gpu_fps_resync = false;
325 if( gamp_show_fps ) {
326 jau::PLAIN_PRINT(true, "fps resync");
327 }
328 } else if( td >= fps_avg_period ) {
330 gpu_fps = float(double(gpu_frame_count) / ( double(td.to_us()) / 1000000.0 ));
332 if( gamp_show_fps ) {
333 jau::PLAIN_PRINT(true, "fps(5s): fps %3.3f, dur %" PRIu64 "ms over %" PRIi64 " frames", gpu_fps, gpu_fdur.to_ms(), gpu_frame_count);
334 }
335 gpu_frame_count = 0;
336 }
337 if( 0 < fps ) {
338 constexpr uint64_t ns_per_ms = 1'000'000UL;
339 const jau::fraction_timespec fudge(0, ns_per_ms / 4); // ns granularity
340 const jau::fraction_timespec td_per_frame(1.0 / fps);
341 const jau::fraction_timespec td_this_frame = gpu_swap_t0 - gpu_swap_t1;
342 const jau::fraction_timespec td_diff = td_per_frame - td_this_frame;
343 if( td_diff > fudge ) {
344 struct timespec ts;
345 ts.tv_sec = static_cast<decltype(ts.tv_sec)>(td_diff.tv_sec); // signed 32- or 64-bit integer
346 ts.tv_nsec = td_diff.tv_nsec - fudge.tv_nsec;
347 nanosleep( &ts, nullptr );
348 // pixel::log_printf("soft-sync [exp %zd > has %zd]ms, delay %" PRIi64 "ms (%lds, %ldns)\n",
349 // ms_per_frame, ms_this_frame, td_ns/pixel::NanoPerMilli, ts.tv_sec, ts.tv_nsec);
350 }
352 } else {
354 }
355}
356
357void gamp::swap_gpu_buffer(int fps) noexcept {
358 gamp_swap_gpu_buffer(true, fps);
359}
360
361float gamp::gpu_avg_fps() noexcept {
362 return gpu_fps;
363}
364
366 return gpu_fdur;
367}
368
369static std::pair<VKeyCode, InputModifier> to_VKeyCode(SDL_Scancode scancode) {
370 if (SDL_SCANCODE_A <= scancode && scancode <= SDL_SCANCODE_Z) {
371 return { VKeyCode(*VKeyCode::VK_A + (scancode - SDL_SCANCODE_A)), InputModifier::none};
372 }
373 if (SDL_SCANCODE_1 <= scancode && scancode <= SDL_SCANCODE_9) {
374 return { VKeyCode(*VKeyCode::VK_1 + (scancode - SDL_SCANCODE_1)), InputModifier::none};
375 }
376 if (SDL_SCANCODE_0 == scancode) {
378 }
379 switch (scancode) {
380 case SDL_SCANCODE_ESCAPE:
382 case SDL_SCANCODE_LSHIFT:
384 case SDL_SCANCODE_RSHIFT:
386 case SDL_SCANCODE_LALT:
388 case SDL_SCANCODE_RALT:
390 case SDL_SCANCODE_LCTRL:
392 case SDL_SCANCODE_RCTRL:
394 case SDL_SCANCODE_PAUSE:
396 case SDL_SCANCODE_UP:
398 case SDL_SCANCODE_LEFT:
400 case SDL_SCANCODE_DOWN:
402 case SDL_SCANCODE_RIGHT:
404 case SDL_SCANCODE_KP_ENTER:
405 [[fallthrough]];
406 case SDL_SCANCODE_RETURN:
408
409 case SDL_SCANCODE_SEMICOLON:
411
412 case SDL_SCANCODE_MINUS:
413 [[fallthrough]];
414 case SDL_SCANCODE_KP_MINUS:
416
417 case SDL_SCANCODE_KP_PLUS:
419
420 case SDL_SCANCODE_KP_MULTIPLY:
422
423 case SDL_SCANCODE_SLASH:
424 [[fallthrough]];
425 case SDL_SCANCODE_KP_DIVIDE:
427
428 case SDL_SCANCODE_KP_PERCENT:
430
431 case SDL_SCANCODE_KP_LEFTPAREN:
432 [[fallthrough]];
433 case SDL_SCANCODE_KP_LEFTBRACE:
434 [[fallthrough]];
435 case SDL_SCANCODE_LEFTBRACKET:
437
438 case SDL_SCANCODE_KP_RIGHTPAREN:
439 [[fallthrough]];
440 case SDL_SCANCODE_KP_RIGHTBRACE:
441 [[fallthrough]];
442 case SDL_SCANCODE_RIGHTBRACKET:
444
445 case SDL_SCANCODE_COMMA:
447
448 case SDL_SCANCODE_PERIOD:
450
451 case SDL_SCANCODE_SPACE:
452 [[fallthrough]];
453 case SDL_SCANCODE_TAB:
455
456 case SDL_SCANCODE_BACKSPACE:
458
459
460 default:
462 }
463}
464
465static uint16_t to_ascii(SDL_Scancode scancode, const InputModifier& mods) {
466 if (SDL_SCANCODE_A <= scancode && scancode <= SDL_SCANCODE_Z) {
467 if( has_any(mods, InputModifier::shift) ) {
468 return 'A' + (scancode - SDL_SCANCODE_A);
469 } else {
470 return 'a' + (scancode - SDL_SCANCODE_A);
471 }
472 }
473 if (SDL_SCANCODE_1 <= scancode && scancode <= SDL_SCANCODE_9) {
474 return '1' + (scancode - SDL_SCANCODE_1);
475 }
476 if (SDL_SCANCODE_0 == scancode) {
477 return '0' + (scancode - SDL_SCANCODE_0);
478 }
479 switch (scancode) {
480 case SDL_SCANCODE_SEMICOLON:
481 return ';';
482
483 case SDL_SCANCODE_MINUS:
484 [[fallthrough]];
485 case SDL_SCANCODE_KP_MINUS:
486 return '-';
487
488 case SDL_SCANCODE_KP_PLUS:
489 return '+';
490
491 case SDL_SCANCODE_KP_MULTIPLY:
492 return '*';
493
494 case SDL_SCANCODE_SLASH:
495 [[fallthrough]];
496 case SDL_SCANCODE_KP_DIVIDE:
497 return '/';
498
499 case SDL_SCANCODE_KP_PERCENT:
500 return '%';
501
502 case SDL_SCANCODE_KP_LEFTPAREN:
503 [[fallthrough]];
504 case SDL_SCANCODE_KP_LEFTBRACE:
505 [[fallthrough]];
506 case SDL_SCANCODE_LEFTBRACKET:
507 return '(';
508
509 case SDL_SCANCODE_KP_RIGHTPAREN:
510 [[fallthrough]];
511 case SDL_SCANCODE_KP_RIGHTBRACE:
512 [[fallthrough]];
513 case SDL_SCANCODE_RIGHTBRACKET:
514 return ')';
515
516 case SDL_SCANCODE_COMMA:
517 return ',';
518
519 case SDL_SCANCODE_PERIOD:
520 return '.';
521
522 case SDL_SCANCODE_SPACE:
523 [[fallthrough]];
524 case SDL_SCANCODE_TAB:
525 return ' ';
526
527 case SDL_SCANCODE_RETURN:
528 [[fallthrough]];
529 case SDL_SCANCODE_KP_ENTER:
530 return '\n';
531
532 case SDL_SCANCODE_BACKSPACE:
533 return 0x08;
534
535 default:
536 return 0;
537 }
538 return 0;
539}
540
541static Window* getWin(Uint32 id) {
542 SDL_Window *p = SDL_GetWindowFromID(id);
543 if( !p ) {
544 return nullptr;
545 }
546 handle_t h = reinterpret_cast<handle_t>(p);
547 for(const WindowRef& win : *window_list.snapshot() ) {
548 if( win && win->windowHandle() == h ) {
549 return win.get();
550 }
551 }
552 return nullptr;
553}
554
555size_t gamp::handle_events() noexcept {
557 return 0;
558 }
559 size_t event_count = 0;
560 SDL_Event sdl_event;
561
562 while (SDL_PollEvent(&sdl_event)) {
563 ++event_count;
564 const int64_t s = sdl_event.common.timestamp/1000;
565 const int64_t ns = ( sdl_event.common.timestamp - s*1000 ) *1'000'000;
566 const jau::fraction_timespec when(s, ns);
567 switch (sdl_event.type) {
568 case SDL_QUIT: {
569 printf("App Close Requested\n");
570 for(const WindowRef& win : *window_list.snapshot() ) {
571 win->dispose(when);
572 }
573 window_list.clear(true);
574 } break;
575
576 case SDL_WINDOWEVENT: {
577 Window* win = getWin(sdl_event.window.windowID);
578 if( win ) {
579 switch (sdl_event.window.event) {
580 case SDL_WINDOWEVENT_CLOSE: {
581 win->dispose(when);
582 printf("Window Close Requested: %zu windows\n", (size_t)window_list.size());
583 } break;
584 case SDL_WINDOWEVENT_SHOWN:
585 printf("Window Shown\n");
587 break;
588 case SDL_WINDOWEVENT_HIDDEN:
589 printf("Window Hidden\n");
591 break;
592 case SDL_WINDOWEVENT_MOVED:
593 printf("Window Moved: %d x %d\n", sdl_event.window.data1, sdl_event.window.data2);
594 win->notifyWindowMoved(when, jau::math::Vec2i(sdl_event.window.data1, sdl_event.window.data2));
595 break;
596 case SDL_WINDOWEVENT_RESIZED:
597 printf("Window Resized: %d x %d\n", sdl_event.window.data1, sdl_event.window.data2);
598 on_window_resized(win, sdl_event.window.data1, sdl_event.window.data2, when);
599 break;
600 case SDL_WINDOWEVENT_SIZE_CHANGED:
601 printf("Window SizeChanged: %d x %d\n", sdl_event.window.data1, sdl_event.window.data2);
602 break;
603
604 default: break;
605 }
606 } }
607 break;
608
609 case SDL_MOUSEMOTION: {
610 Window* win = getWin(sdl_event.motion.windowID);
611 if( win ) {
612 int ypos = int((float)sdl_event.motion.y*devicePixelRatio.y);
613 if( win->isPointerBLOriented() ) {
614 ypos = win->surfaceSize().y - ypos - 1;
615 }
616 win->notifyPointer(EVENT_POINTER_MOVED, when, PointerType::mouse, (uint16_t)sdl_event.motion.which,
617 jau::math::Vec2i(int((float)sdl_event.motion.x*devicePixelRatio.x), ypos),
618 /*clickCount=*/0, InputButton::none,
619 jau::math::Vec3f(), /*rotationScale=*/0.0f);
620 } }
621 break;
622 case SDL_MOUSEBUTTONDOWN: {
623 Window* win = getWin(sdl_event.button.windowID);
624 if( win ) {
625 int ypos = int((float)sdl_event.button.y*devicePixelRatio.y);
626 if( win->isPointerBLOriented() ) {
627 ypos = win->surfaceSize().y - ypos - 1;
628 }
630 jau::math::Vec2i(int((float)sdl_event.button.x*devicePixelRatio.x), ypos),
631 /*clickCount=*/sdl_event.button.clicks,
632 static_cast<InputButton>(sdl_event.button.button),
633 jau::math::Vec3f(), /*rotationScale=*/0.0f);
634 } }
635 break;
636 case SDL_MOUSEBUTTONUP: {
637 Window* win = getWin(sdl_event.button.windowID);
638 if( win ) {
639 int ypos = int((float)sdl_event.button.y*devicePixelRatio.y);
640 if( win->isPointerBLOriented() ) {
641 ypos = win->surfaceSize().y - ypos - 1;
642 }
644 jau::math::Vec2i(int((float)sdl_event.button.x*devicePixelRatio.x), ypos),
645 /*clickCount=*/sdl_event.button.clicks,
646 static_cast<InputButton>(sdl_event.button.button),
647 jau::math::Vec3f(), /*rotationScale=*/0.0f);
648 } }
649 break;
650 case SDL_MOUSEWHEEL: {
651 Window* win = getWin(sdl_event.wheel.windowID);
652 if( win ) {
653 float rotX = sdl_event.wheel.preciseX;
654 float rotY = sdl_event.wheel.preciseY;
656 std::swap(rotX, rotY);
657 }
658 win->notifyPointer(EVENT_POINTER_WHEEL, when, PointerType::mouse, (uint16_t)sdl_event.wheel.which,
659 jau::math::Vec2i((int)sdl_event.wheel.mouseX, (int)sdl_event.wheel.mouseY),
660 /*clickCount=*/0,
662 jau::math::Vec3f(rotX, rotY, 0), /*rotationScale=*/0.0f);
663 } }
664 break;
665 case SDL_KEYUP: {
666 Window* win = getWin(sdl_event.key.windowID);
667 if( win ) {
668 const SDL_Scancode scancode = sdl_event.key.keysym.scancode;
669 const std::pair<VKeyCode, InputModifier> r = to_VKeyCode(scancode);
670 win->notifyKeyReleased(when, r.first, r.second, to_ascii(scancode, win->keyTracker().modifier()));
671 } }
672 break;
673 case SDL_KEYDOWN: {
674 Window* win = getWin(sdl_event.key.windowID);
675 if( win ) {
676 const SDL_Scancode scancode = sdl_event.key.keysym.scancode;
677 const std::pair<VKeyCode, InputModifier> r = to_VKeyCode(scancode);
678 if( sdl_event.key.repeat ) {
679 win->notifyKeyReleased(when, r.first, r.second | InputModifier::repeat, to_ascii(scancode, win->keyTracker().modifier()));
680 win->notifyKeyPressed (when, r.first, r.second | InputModifier::repeat, to_ascii(scancode, win->keyTracker().modifier()));
681 } else {
682 win->notifyKeyPressed(when, r.first, r.second, to_ascii(scancode, win->keyTracker().modifier()));
683 }
684 } }
685 break;
686 default: break;
687 }
688 }
689 return event_count;
690}
691
692bool gamp::mainloop_default() noexcept { // NOLINT
694
696 jau::cow_darray<WindowRef>::storage_t windows = *window_refs;
697 if( windows.size() == 0 ) {
698 return false;
699 }
700 for(const WindowRef& win : windows ) {
701 if( win && win->isValid() ) {
702 win->display(getElapsedMonotonicTime());
703 }
704 }
705 for(const WindowRef& win : windows ) {
706 if( win ) {
707 win->surfaceSwap();
708 }
709 }
711 return true;
712}
713void gamp::mainloop_void() noexcept {
714 if( !mainloop_default() ) {
715 printf("Exit Application\n");
716 #if defined(__EMSCRIPTEN__)
717 emscripten_cancel_main_loop();
718 #else
719 exit(0);
720 #endif
721 }
722}
723
724void gamp::shutdown() noexcept {
726 for(const WindowRef& win : *window_list.snapshot() ) {
727 win->dispose(when);
728 // win->notifyWindowEvent(EVENT_WINDOW_DESTROY_NOTIFY, when);
729 }
730 window_list.clear(true);
731}
#define EMSCRIPTEN_KEEPALIVE
Definition GampTypes.hpp:37
constexpr const Vec2i & surfaceSize() const noexcept
Returns the surface size of the client area excluding insets (window decorations) in pixel units.
Definition Surface.hpp:171
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
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
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
static WindowRef create(const char *title, int wwidth, int wheight, bool verbose=false)
Create an new instance using a native windowing toolkit.
static WindowRef wrapNative(handle_t window_handle, const Recti &window_bounds, handle_t surface_handle, const Vec2i &surface_size)
Create an new instance, wrapping the native windowing toolkit's handle/resources.
Definition Window.hpp:199
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:1069
constexpr Vector2I & set(const value_type vx, const value_type vy) noexcept
TODO constexpr bool operator<=>(const vec2i_t& rhs ) const noexcept { return ... }...
Definition vec2i.hpp:120
value_type x
Definition vec2i.hpp:65
std::string toString() const noexcept
Definition vec2i.hpp:211
value_type y
Definition vec2i.hpp:66
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition debug.hpp:112
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::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 jau::cow_darray< WindowRef > window_list
Definition gamp_sdl2.cpp:63
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 > WindowRef
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:47
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:328
Vector2F< float > Vec2f
Definition vec2f.hpp:416
RectI< int > Recti
Definition recti.hpp:139
Vector3F< float > Vec3f
Definition vec3f.hpp:436
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 Gamp.hpp:29
Author: Sven Gothel sgothel@jausoft.com Copyright Gothel Software e.K.
Definition enum_util.hpp:65
void PLAIN_PRINT(const bool printPrefix, const char *format,...) noexcept
Use for unconditional plain messages, prefix '[elapsed_time] ' if printPrefix == true.
Definition debug.cpp:258
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).
std::string to_string() const noexcept
Return simple string representation in seconds and nanoseconds.
constexpr uint64_t to_us() const noexcept
Returns time in microseconds.
int printf(const char *format,...)
Operating Systems predefined macros.