sdl2_impl 0.1.0
Vendor SDL2 2.32 : IGraphic2Module / IAudioModule
Loading...
Searching...
No Matches
SdlWindow.hpp
Go to the documentation of this file.
1
11#ifndef SDLWINDOW_HPP_
12#define SDLWINDOW_HPP_
13
14//Sdl
15#include <SDL.h>
16
17//Interface
18#include "IWindow2.hpp"
19
20//encapsulation
21#include "SdlGamepad.hpp"
22#include "SdlKeyboard.hpp"
23#include "SdlMouse.hpp"
24#include "SdlPolygon.hpp"
25#include "SdlSprite.hpp"
26#include "SdlText.hpp"
27
28#include <array>
29#include <string>
30#include <unordered_map>
31#include <vector>
32
50class SdlWindow : public graphic::IWindow2 {
51
52 public:
53 SdlWindow(int32_t screenWidth, int32_t screenHeight, std::string title) {
54 _window = SDL_CreateWindow(title.c_str(),
55 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
56 screenWidth, screenHeight,
57 SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI);
58 if (!_window)
59 return;
60
61 _renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED);
62 if (_renderer) {
63 //sinon un alpha < 255 est ecrase au lieu d'etre melange
64 SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_BLEND);
65 /* Une unite de dessin = un pixel logique, quel que soit
66 * l'ecran. Sans ca, sur un ecran retina la souris et le
67 * dessin ne parlent plus du meme repere - le meme piege que
68 * la vue chez sfml. */
69 SDL_RenderSetLogicalSize(_renderer, screenWidth, screenHeight);
70 }
71
72 _id = SDL_GetWindowID(_window);
73 registry()[_id] = this;
74 _lastTicks = SDL_GetTicks();
75 }
76
78 /* On part : si c'est nous qui l'avions cache, on le rend. Le
79 * pump suivant le recachera si la souris se trouve sur une
80 * autre fenetre qui le demande. */
81 registry().erase(_id);
82 if (!_cursor && !shown()) {
83 shown() = true;
84 SDL_ShowCursor(SDL_ENABLE);
85 }
86 if (_renderer)
87 SDL_DestroyRenderer(_renderer);
88 if (_window)
89 SDL_DestroyWindow(_window);
90 }
91
92 //lifecycle
93 bool isOpen() override { return _open && _window != nullptr; }
94
95 void close() override { _open = false; }
96
97 Vector2f getPosition() override {
98 int x = 0, y = 0;
99
100 SDL_GetWindowPosition(_window, &x, &y);
101 return {static_cast<double>(x), static_cast<double>(y)};
102 }
103
104 void setPosition(Vector2f position) override {
105 SDL_SetWindowPosition(_window, static_cast<int>(position.x), static_cast<int>(position.y));
106 }
107
108 Vector2f getSize() override {
109 int w = 0, h = 0;
110
111 SDL_GetWindowSize(_window, &w, &h);
112 return {static_cast<double>(w), static_cast<double>(h)};
113 }
114
115 void setSize(Vector2f size) override {
116 SDL_SetWindowSize(_window, static_cast<int>(size.x), static_cast<int>(size.y));
117 if (_renderer)
118 SDL_RenderSetLogicalSize(_renderer, static_cast<int>(size.x), static_cast<int>(size.y));
119 }
120
121 void setFrameLimit(int32_t limit) override { _frameLimit = limit; }
122
137 void setMouseVisibility(bool visible) override { _cursor = visible; }
138
139 int32_t getDelta() override { return _delta; }
140
147 bool pollEvent() override {
148 pump();
149 return !_events.empty();
150 }
151
152 void eventClose() override {
153 for (const SDL_Event &event : _events) {
154 if (event.type == SDL_QUIT ||
155 (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE) ||
156 (event.type == SDL_KEYDOWN && event.key.keysym.scancode == SDL_SCANCODE_ESCAPE)) {
157 _open = false;
158 return;
159 }
160 }
161 }
162
163 //2D
164 void beginDraw() override {
165 if (!_renderer)
166 return;
167 SDL_SetRenderDrawColor(_renderer, 0, 0, 0, 255);
168 SDL_RenderClear(_renderer);
169 }
170
171 void endDraw() override {
172 if (_renderer)
173 SDL_RenderPresent(_renderer);
174
175 const uint32_t now = SDL_GetTicks();
176
177 _delta = static_cast<int32_t>(now - _lastTicks);
178
179 /* SDL n'a pas de limiteur : on attend nous-memes de quoi tenir
180 * la cadence demandee, sinon la boucle tourne a vide et mange
181 * un coeur entier. */
182 if (_frameLimit > 0) {
183 const int32_t budget = 1000 / _frameLimit;
184
185 if (_delta < budget) {
186 SDL_Delay(static_cast<uint32_t>(budget - _delta));
187 _delta = budget;
188 }
189 }
190
191 _lastTicks = SDL_GetTicks();
192 _events.clear(); //la frontiere de frame, cf. IWindow::pollEvent
193 }
194
195 void drawPoly(graphic::IPolygon *polygon) override;
196 void drawSprite(graphic::ISprite *sprite) override;
197 void drawText(graphic::IText *text) override;
198
199 friend class SdlKeyboard;
200 friend class SdlMouse;
201 friend class SdlGamepad;
202
203 private:
205 static std::unordered_map<uint32_t, SdlWindow *> &registry() {
206 static std::unordered_map<uint32_t, SdlWindow *> windows;
207
208 return windows;
209 }
210
219 static void pump() {
220 SDL_Event event;
221
222 /* Le curseur AVANT la file : l'etat suit le survol, et le survol
223 * a pu changer depuis la frame precedente meme sans evenement. */
224 applyCursor(SDL_GetMouseFocus());
225
226 while (SDL_PollEvent(&event)) {
227 const uint32_t target = windowOf(event);
228
229 if (target == 0) {
230 for (auto &[id, window] : registry())
231 window->push(event);
232 continue;
233 }
234
235 const auto found = registry().find(target);
236
237 if (found == registry().end())
238 continue; //une fenetre deja detruite : l'evenement tombe
239 found->second->push(event);
240 }
241 }
242
254 static void applyCursor(SDL_Window *focused) {
255 if (!focused)
256 return;
257
258 const auto found = registry().find(SDL_GetWindowID(focused));
259
260 if (found == registry().end())
261 return;
262
263 const bool wanted = found->second->_cursor;
264
265 if (wanted == shown())
266 return;
267 shown() = wanted;
268 SDL_ShowCursor(wanted ? SDL_ENABLE : SDL_DISABLE);
269 }
270
272 static bool &shown() {
273 static bool visible = true;
274
275 return visible;
276 }
277
279 static uint32_t windowOf(const SDL_Event &event) {
280 switch (event.type) {
281 case SDL_WINDOWEVENT: return event.window.windowID;
282 case SDL_KEYDOWN:
283 case SDL_KEYUP: return event.key.windowID;
284 case SDL_MOUSEBUTTONDOWN:
285 case SDL_MOUSEBUTTONUP: return event.button.windowID;
286 case SDL_MOUSEMOTION: return event.motion.windowID;
287 case SDL_MOUSEWHEEL: return event.wheel.windowID;
288 case SDL_TEXTINPUT: return event.text.windowID;
289 default: return 0;
290 }
291 }
292
302 void push(const SDL_Event &event) {
303 if (_events.size() >= MAX_EVENTS)
304 _events.erase(_events.begin());
305 _events.push_back(event);
306 feedEvent(event);
307 }
308
309 /* L'etat reconstruit DEPUIS LES EVENEMENTS, jamais par une requete
310 * globale comme SDL_GetKeyboardState() : celle-la est commune au
311 * processus, donc deux fenetres y liraient la meme chose meme si
312 * une seule a le focus.
313 *
314 * Seul ce qu'aucun evenement ne peut dire est garde : une touche
315 * reste "enfoncee" entre son KEYDOWN et son KEYUP. Les fronts, eux,
316 * se relisent dans _events, qui porte la frame. */
317 void feedEvent(const SDL_Event &event) {
318 switch (event.type) {
319 case SDL_KEYDOWN:
320 if (!event.key.repeat)
321 _keysDown[event.key.keysym.scancode] = true;
322 break;
323 case SDL_KEYUP:
324 _keysDown[event.key.keysym.scancode] = false;
325 break;
326 case SDL_MOUSEBUTTONDOWN:
327 _mouseDown[event.button.button] = true;
328 break;
329 case SDL_MOUSEBUTTONUP:
330 _mouseDown[event.button.button] = false;
331 break;
332 case SDL_MOUSEMOTION:
333 _mousePosition = {static_cast<double>(event.motion.x), static_cast<double>(event.motion.y)};
334 break;
335 case SDL_WINDOWEVENT:
336 //sans ca, une touche relachee hors focus resterait "enfoncee"
337 if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) {
338 _keysDown.fill(false);
339 _mouseDown.fill(false);
340 }
341 break;
342 default:
343 break;
344 }
345 }
346
348 static constexpr size_t MAX_EVENTS = 1024;
349
350 SDL_Window *_window = nullptr;
351 SDL_Renderer *_renderer = nullptr;
352 uint32_t _id = 0;
353 bool _open = true;
354 bool _cursor = true;
355
356 /* Les evenements de la frame. Rempli par pump(), vide par endDraw() :
357 * entre les deux, tout le monde y lit la meme chose. */
358 std::vector<SDL_Event> _events;
359
360 uint32_t _lastTicks = 0;
361 int32_t _delta = 0;
362 int32_t _frameLimit = 0;
363
364 std::array<bool, SDL_NUM_SCANCODES> _keysDown{};
365 std::array<bool, 8> _mouseDown{};
366 Vector2f _mousePosition{0, 0};
367};
368
369void SdlWindow::drawPoly(graphic::IPolygon *polygon) {
370 SdlPolygon *sdlPolygon = static_cast<SdlPolygon *>(polygon);
371
372 if (!_renderer || sdlPolygon->_vertices.empty())
373 return;
374
375 /* SDL_RenderGeometry n'a pas de transformation : il faut lui donner des
376 * coordonnees ecran. On recopie les sommets locaux en ajoutant la
377 * position, dans un tampon qui appartient au polygone - donc aucune
378 * allocation apres la premiere frame. */
379 const float x = static_cast<float>(sdlPolygon->_position.x);
380 const float y = static_cast<float>(sdlPolygon->_position.y);
381
382 for (size_t i = 0; i < sdlPolygon->_vertices.size(); i++) {
383 sdlPolygon->_screen[i] = sdlPolygon->_vertices[i];
384 sdlPolygon->_screen[i].position.x += x;
385 sdlPolygon->_screen[i].position.y += y;
386 }
387
388 SDL_RenderGeometry(_renderer, nullptr,
389 sdlPolygon->_screen.data(), static_cast<int>(sdlPolygon->_screen.size()),
390 nullptr, 0);
391}
392
393void SdlWindow::drawSprite(graphic::ISprite *sprite) {
394 SdlSprite *sdlSprite = static_cast<SdlSprite *>(sprite);
395 SDL_Texture *texture = sdlSprite->_texture.handle(_renderer);
396
397 if (!texture)
398 return;
399
400 const SDL_Rect source{
401 static_cast<int>(sdlSprite->_crop.x), static_cast<int>(sdlSprite->_crop.y),
402 static_cast<int>(sdlSprite->_crop.w), static_cast<int>(sdlSprite->_crop.h)};
403 const SDL_Rect destination{
404 static_cast<int>(sdlSprite->_position.x), static_cast<int>(sdlSprite->_position.y),
405 static_cast<int>(sdlSprite->_size.x), static_cast<int>(sdlSprite->_size.y)};
406
407 SDL_RenderCopyEx(_renderer, texture, &source, &destination,
408 sdlSprite->_rotation, nullptr, SDL_FLIP_NONE);
409}
410
411void SdlWindow::drawText(graphic::IText *text) {
412 SdlText *sdlText = static_cast<SdlText *>(text);
413 SDL_Texture *texture = sdlText->handle(_renderer);
414
415 if (!texture)
416 return;
417
418 const SDL_Rect destination{
419 static_cast<int>(sdlText->_position.x), static_cast<int>(sdlText->_position.y),
420 sdlText->_width, sdlText->_height};
421
422 SDL_RenderCopyEx(_renderer, texture, nullptr, &destination,
423 sdlText->_rotation, nullptr, SDL_FLIP_NONE);
424}
425
426/* Entrees.
427 *
428 * isKeyPressed / isKeyReleased : les evenements de la frame sont encore
429 * dans _events, donc un front s'y relit. Rien n'est consomme, donc
430 * n'importe quel nombre de lecteurs obtient la meme reponse.
431 * isKeyDown / isKeyUp : l'etat que la fenetre tient, puisqu'aucun
432 * evenement seul ne peut dire qu'une touche est toujours enfoncee.
433 */
434
436template <typename Match>
437static bool anyEvent(const std::vector<SDL_Event> &events, uint32_t type, Match match) {
438 for (const SDL_Event &event : events)
439 if (event.type == type && match(event))
440 return true;
441 return false;
442}
443
444std::vector<graphic::IKeyboard::Keys> SdlKeyboard::whichKeyDown() const {
445 std::vector<Keys> keys;
446
447 for (const auto &[key, code] : _keys)
448 if (_window._keysDown[code])
449 keys.push_back(key);
450 return keys;
451}
452
453bool SdlKeyboard::isKeyPressed(Keys key) const {
454 const SDL_Scancode code = _keys.at(key);
455
456 return anyEvent(_window._events, SDL_KEYDOWN,
457 [code](const SDL_Event &event) { return event.key.keysym.scancode == code && !event.key.repeat; });
458}
459
460bool SdlKeyboard::isKeyReleased(Keys key) const {
461 const SDL_Scancode code = _keys.at(key);
462
463 return anyEvent(_window._events, SDL_KEYUP,
464 [code](const SDL_Event &event) { return event.key.keysym.scancode == code; });
465}
466
467bool SdlKeyboard::isKeyDown(Keys key) const { return _window._keysDown[_keys.at(key)]; }
468bool SdlKeyboard::isKeyUp(Keys key) const { return !isKeyDown(key); }
469
470bool SdlMouse::isButtonPressed(Buttons key) const {
471 const uint8_t button = _buttons.at(key);
472
473 return anyEvent(_window._events, SDL_MOUSEBUTTONDOWN,
474 [button](const SDL_Event &event) { return event.button.button == button; });
475}
476
477bool SdlMouse::isButtonReleased(Buttons key) const {
478 const uint8_t button = _buttons.at(key);
479
480 return anyEvent(_window._events, SDL_MOUSEBUTTONUP,
481 [button](const SDL_Event &event) { return event.button.button == button; });
482}
483
484bool SdlMouse::isButtonDown(Buttons key) const { return _window._mouseDown[_buttons.at(key)]; }
485bool SdlMouse::isButtonUp(Buttons key) const { return !isButtonDown(key); }
486
487Vector2f SdlMouse::getPosition() const { return _window._mousePosition; }
488
489void SdlMouse::setPosition(Vector2f position) {
490 _window._mousePosition = position;
491 SDL_WarpMouseInWindow(_window._window, static_cast<int>(position.x), static_cast<int>(position.y));
492}
493
495 float delta = 0.f;
496
497 //additionne : deux crans dans la meme frame, sinon on en perd un
498 for (const SDL_Event &event : _window._events)
499 if (event.type == SDL_MOUSEWHEEL)
500 delta += event.wheel.preciseY;
501 return delta;
502}
503
504bool SdlGamepad::isButtonPressed(Button button) const {
505 const SDL_GameControllerButton raw = _buttons.at(button);
506
507 return anyEvent(_window._events, SDL_CONTROLLERBUTTONDOWN,
508 [raw](const SDL_Event &event) { return event.cbutton.button == raw; });
509}
510
511bool SdlGamepad::isButtonReleased(Button button) const {
512 const SDL_GameControllerButton raw = _buttons.at(button);
513
514 return anyEvent(_window._events, SDL_CONTROLLERBUTTONUP,
515 [raw](const SDL_Event &event) { return event.cbutton.button == raw; });
516}
517
520#endif /* !SDLWINDOW_HPP_ */
La manette numero _index, ouverte a la construction.
Definition SdlGamepad.hpp:31
Le clavier d'UNE fenetre.
Definition SdlKeyboard.hpp:33
Definition SdlMouse.hpp:24
Un contour quelconque, decoupe en triangles une fois pour toutes.
Definition SdlPolygon.hpp:35
Une vue sur une texture : decoupe, position, taille, rotation.
Definition SdlSprite.hpp:29
Une chaine rendue en texture, refaite quand elle change.
Definition SdlText.hpp:37
SDL_Texture * handle(SDL_Renderer *renderer) const
La texture pour CE renderer, fabriquee au besoin.
Definition SdlTexture.hpp:64
Une fenetre SDL et son renderer, implemente IWindow2.
Definition SdlWindow.hpp:50
~SdlWindow()
Definition SdlWindow.hpp:77
Vector2f getPosition() override
Definition SdlWindow.hpp:97
void eventClose() override
Definition SdlWindow.hpp:152
SdlWindow(int32_t screenWidth, int32_t screenHeight, std::string title)
Definition SdlWindow.hpp:53
bool pollEvent() override
Vide la file SDL une fois, puis rend l'etat de CETTE fenetre.
Definition SdlWindow.hpp:147
void setPosition(Vector2f position) override
Definition SdlWindow.hpp:104
void setFrameLimit(int32_t limit) override
Definition SdlWindow.hpp:121
void setSize(Vector2f size) override
Definition SdlWindow.hpp:115
void beginDraw() override
Definition SdlWindow.hpp:164
void endDraw() override
Definition SdlWindow.hpp:171
int32_t getDelta() override
Definition SdlWindow.hpp:139
Vector2f getSize() override
Definition SdlWindow.hpp:108
void close() override
Definition SdlWindow.hpp:95
void setMouseVisibility(bool visible) override
Cache ou montre le pointeur SUR CETTE FENETRE.
Definition SdlWindow.hpp:137
bool isOpen() override
Definition SdlWindow.hpp:93
bool isKeyPressed(Keys key) const override
Definition SdlWindow.hpp:453
void drawText(graphic::IText *text) override
Definition SdlWindow.hpp:411
bool isKeyDown(Keys key) const override
Definition SdlWindow.hpp:467
bool isButtonPressed(Buttons key) const override
Definition SdlWindow.hpp:470
void drawSprite(graphic::ISprite *sprite) override
Definition SdlWindow.hpp:393
void setPosition(Vector2f position) override
Definition SdlWindow.hpp:489
bool isButtonDown(Buttons key) const override
Definition SdlWindow.hpp:484
bool isKeyUp(Keys key) const override
Definition SdlWindow.hpp:468
void drawPoly(graphic::IPolygon *polygon) override
Definition SdlWindow.hpp:369
float GetMouseWheelMove() const override
Definition SdlWindow.hpp:494
bool isButtonReleased(Buttons key) const override
Definition SdlWindow.hpp:477
bool isButtonUp(Buttons key) const override
Definition SdlWindow.hpp:485
bool isButtonReleased(Button button) const override
Definition SdlWindow.hpp:511
bool isKeyReleased(Keys key) const override
Definition SdlWindow.hpp:460
bool isButtonPressed(Button button) const override
Definition SdlWindow.hpp:504
Vector2f getPosition() const override
Definition SdlWindow.hpp:487
std::vector< Keys > whichKeyDown() const override
Definition SdlWindow.hpp:444