sfml_impl 0.1.0
Vendor SFML 3.0.2 : IGraphic2Module / IAudioModule
Loading...
Searching...
No Matches
SfmlWindow.hpp
Go to the documentation of this file.
1
11#ifndef SFMLWINDOW_HPP_
12#define SFMLWINDOW_HPP_
13
14//Sfml
15#include <SFML/Graphics.hpp>
16
17//Interface
18#include "IWindow2.hpp"
19
20//encapsulation
21#include "SfmlGamepad.hpp"
22#include "SfmlKeyboard.hpp"
23#include "SfmlMouse.hpp"
24#include "SfmlPolygon.hpp"
25#include "SfmlSprite.hpp"
26#include "SfmlText.hpp"
27
28#include <array>
29#include <optional>
30#include <vector>
31
46class SfmlWindow : public graphic::IWindow2 {
47
48 public:
49 SfmlWindow(int32_t screenWidth, int32_t screenHeight, std::string title)
50 : _window(sf::VideoMode({static_cast<uint32_t>(screenWidth), static_cast<uint32_t>(screenHeight)}), title) {
51 // without this, a held key re-sends KeyPressed in bursts and
52 // isKeyPressed() would fire on every OS repeat.
53 _window.setKeyRepeatEnabled(false);
54 syncView();
55 }
56
57 ~SfmlWindow() = default;
58
59 //lifecycle
60 bool isOpen() override {
61 return _window.isOpen();
62 }
63
64 void close() override {
65 _window.close();
66 }
67
68 Vector2f getPosition() override {
69 const sf::Vector2i position = _window.getPosition();
70 return {static_cast<double>(position.x), static_cast<double>(position.y)};
71 }
72
73 void setPosition(Vector2f position) override {
74 _window.setPosition({static_cast<int>(position.x), static_cast<int>(position.y)});
75 }
76
77 Vector2f getSize() override {
78 const sf::Vector2u size = _window.getSize();
79 return {static_cast<double>(size.x), static_cast<double>(size.y)};
80 }
81
82 void setSize(Vector2f size) override {
83 _window.setSize({static_cast<unsigned>(size.x), static_cast<unsigned>(size.y)});
84 /* setSize() n'emet pas forcement de Resized selon la plateforme,
85 * donc on recale la vue tout de suite plutot que d'attendre un
86 * evenement qui ne viendra peut-etre pas. */
87 syncView();
88 }
89
90 void setFrameLimit(int32_t limit) override {
91 _window.setFramerateLimit(limit);
92 }
93
94 /* Propriete de LA fenetre chez sfml : elle meurt avec elle, et le
95 * pointeur revient tout seul des qu'on en sort. */
96 void setMouseVisibility(bool visible) override {
97 _window.setMouseCursorVisible(visible);
98 }
99
100 int32_t getDelta() override {
101 return static_cast<int32_t>(_deltaTime.asMilliseconds());
102 }
103
109 bool pollEvent() override {
110 while (const std::optional<sf::Event> event = _window.pollEvent()) {
111 _events.push_back(*event);
112 feedEvent(*event);
113 }
114 return !_events.empty();
115 }
116
117 void eventClose() override {
118 for (const auto &event : _events) {
119 const auto *pressed = event.getIf<sf::Event::KeyPressed>();
120
121 if (event.is<sf::Event::Closed>() ||
122 (pressed && pressed->scancode == sf::Keyboard::Scancode::Escape)) {
123 _window.close();
124 return;
125 }
126 }
127 }
128
129
130 //2D
131 void beginDraw() override {
132 _window.clear();
133 }
134
135 void endDraw() override {
136 _window.display();
137 _deltaTime = _deltaClock.restart();
138 _events.clear(); // la frontiere de frame, cf. IWindow::pollEvent
139 }
140
141 void drawPoly(graphic::IPolygon *polygon) override;
142 void drawSprite(graphic::ISprite *sprite) override;
143 void drawText(graphic::IText *text) override;
144
145 friend class SfmlKeyboard;
146 friend class SfmlMouse;
147 friend class SfmlGamepad;
148
149 private:
150 /* Input state rebuilt FROM THE EVENTS, never through a global
151 * query like sf::Keyboard::isKeyPressed() : on macOS that one
152 * requires the "Input Monitoring" permission, while events are
153 * delivered normally to the focused window.
154 *
155 * Private : only SfmlKeyboard/Mouse/Gamepad reach it, as friends.
156 * Nothing from sf:: leaks into the window's public API, which
157 * exposes the IWindow2 contract and nothing else. */
171 void syncView() {
172 const sf::Vector2f size(_window.getSize());
173
174 _window.setView(sf::View(sf::FloatRect({0.f, 0.f}, size)));
175 }
176
177 static size_t index(sf::Keyboard::Scancode code) {
178 const auto raw = static_cast<int>(code);
179 return (raw < 0) ? 0 : static_cast<size_t>(raw);
180 }
181
182 /* Only what isKeyDown/isKeyUp need is tracked here : a key stays
183 * "down" between its KeyPressed and its KeyReleased, which no
184 * single event can tell. The fronts need no state - they are read
185 * back off _events, which holds the frame.
186 *
187 * sf::Keyboard::isKeyPressed() would do the same in one line, but
188 * it is a GLOBAL query : on macOS it requires the "Input
189 * Monitoring" permission, while events reach the focused window
190 * normally. */
191 void feedEvent(const sf::Event &event) {
192 if (const auto *pressed = event.getIf<sf::Event::KeyPressed>())
193 _keysDown[index(pressed->scancode)] = true;
194 else if (const auto *released = event.getIf<sf::Event::KeyReleased>())
195 _keysDown[index(released->scancode)] = false;
196 else if (const auto *down = event.getIf<sf::Event::MouseButtonPressed>())
197 _mouseDown[size_t(down->button)] = true;
198 else if (const auto *up = event.getIf<sf::Event::MouseButtonReleased>())
199 _mouseDown[size_t(up->button)] = false;
200 else if (const auto *moved = event.getIf<sf::Event::MouseMoved>())
201 _mousePosition = moved->position; // deja relative a la fenetre
202 else if (event.is<sf::Event::Resized>())
203 syncView(); // sinon le dessin se decale de la souris
204 else if (event.is<sf::Event::FocusLost>()) {
205 // without this, a key released out of focus stays "down"
206 _keysDown.fill(false);
207 _mouseDown.fill(false);
208 }
209 }
210
211 sf::RenderWindow _window;
212
213 /* Les evenements de la frame. Rempli par pollEvent(), vide par
214 * endDraw() : entre les deux, tout le monde y lit la meme chose. */
215 std::vector<sf::Event> _events;
216
217 sf::Clock _deltaClock;
218 sf::Time _deltaTime;
219
220 std::array<bool, sf::Keyboard::ScancodeCount> _keysDown{};
221
222 std::array<bool, sf::Mouse::ButtonCount> _mouseDown{};
223
224 sf::Vector2i _mousePosition{};
225};
226
227void SfmlWindow::drawPoly(graphic::IPolygon *polygon) {
228 SfmlPolygon *sfmlPolygon = static_cast<SfmlPolygon *>(polygon);
229
230 // vertices are already built (at triangulation time), in local
231 // coordinates : position goes through a transform, so nothing is
232 // recomputed or reallocated per frame.
233 sf::Transform transform;
234 transform.translate(sfmlPolygon->_position);
235
236 _window.draw(sfmlPolygon->_vertices, transform);
237}
238
239void SfmlWindow::drawSprite(graphic::ISprite *sprite) {
240 SfmlSprite *sfmlSprite = static_cast<SfmlSprite *>(sprite);
241 _window.draw(sfmlSprite->_sprite);
242}
243
244void SfmlWindow::drawText(graphic::IText *text) {
245 SfmlText *sfmlText = static_cast<SfmlText *>(text);
246 _window.draw(sfmlText->_text);
247}
248
249/* Input.
250 *
251 * isKeyPressed / isKeyReleased : the frame's events are still in _events,
252 * so a front is a lookup in there. Nothing is consumed, so any
253 * number of readers get the same answer.
254 * isKeyDown / isKeyUp : the state the window keeps, since no
255 * single event can say a key is still held.
256 *
257 * Both are valid anywhere in the frame. A handful of events per frame, so
258 * the scan costs nothing next to the 87-entry walk whichKeyDown() does.
259 */
260
262template <typename E, typename Match>
263static bool anyEvent(const std::vector<sf::Event> &events, Match match) {
264 for (const auto &event : events)
265 if (const auto *typed = event.getIf<E>())
266 if (match(*typed))
267 return true;
268 return false;
269}
270
271std::vector<graphic::IKeyboard::Keys> SfmlKeyboard::whichKeyDown() const {
272 std::vector<Keys> keys;
273
274 for (const auto &[key, code] : _keys)
275 if (_window._keysDown[SfmlWindow::index(code)])
276 keys.push_back(key);
277 return keys;
278}
279
280bool SfmlKeyboard::isKeyPressed(Keys key) const {
281 const auto code = _keys.at(key);
282
283 return anyEvent<sf::Event::KeyPressed>(_window._events,
284 [code](const auto &pressed) { return pressed.scancode == code; });
285}
286
287bool SfmlKeyboard::isKeyReleased(Keys key) const {
288 const auto code = _keys.at(key);
289
290 return anyEvent<sf::Event::KeyReleased>(_window._events,
291 [code](const auto &released) { return released.scancode == code; });
292}
293
294bool SfmlKeyboard::isKeyDown(Keys key) const { return _window._keysDown[SfmlWindow::index(_keys.at(key))]; }
295bool SfmlKeyboard::isKeyUp(Keys key) const { return !isKeyDown(key); }
296
297bool SfmlMouse::isButtonPressed(Buttons key) const {
298 const auto button = _buttons.at(key);
299
300 return anyEvent<sf::Event::MouseButtonPressed>(_window._events,
301 [button](const auto &pressed) { return pressed.button == button; });
302}
303
304bool SfmlMouse::isButtonReleased(Buttons key) const {
305 const auto button = _buttons.at(key);
306
307 return anyEvent<sf::Event::MouseButtonReleased>(_window._events,
308 [button](const auto &released) { return released.button == button; });
309}
310
311bool SfmlMouse::isButtonDown(Buttons key) const { return _window._mouseDown[size_t(_buttons.at(key))]; }
312bool SfmlMouse::isButtonUp(Buttons key) const { return !isButtonDown(key); }
313
314Vector2f SfmlMouse::getPosition() const {
315 const sf::Vector2i position = _window._mousePosition;
316 return Vector2f{static_cast<double>(position.x), static_cast<double>(position.y)};
317}
318
319void SfmlMouse::setPosition(Vector2f position) {
320 const sf::Vector2i target{static_cast<int>(position.x), static_cast<int>(position.y)};
321
322 _window._mousePosition = target;
323 sf::Mouse::setPosition(target, _window._window);
324}
325
327 float delta = 0.f;
328
329 // additionne : deux crans dans la meme frame, sinon on en perd un
330 for (const auto &event : _window._events)
331 if (const auto *scroll = event.getIf<sf::Event::MouseWheelScrolled>())
332 if (scroll->wheel == sf::Mouse::Wheel::Vertical)
333 delta += scroll->delta;
334 return delta;
335}
336
337// the pad has no carrying event here : we probe sf::Joystick, which is not
338// subject to Input Monitoring
339bool SfmlGamepad::isButtonDown(Button button) const {
340 return isAvailable() && sf::Joystick::isButtonPressed(_index, _buttons.at(button));
341}
342bool SfmlGamepad::isButtonUp(Button button) const { return !isButtonDown(button); }
343
344bool SfmlGamepad::isButtonPressed(Button button) const {
345 const auto index = _index;
346 const auto raw = _buttons.at(button);
347
348 return anyEvent<sf::Event::JoystickButtonPressed>(_window._events,
349 [index, raw](const auto &pressed) { return pressed.joystickId == index && pressed.button == raw; });
350}
351
352bool SfmlGamepad::isButtonReleased(Button button) const {
353 const auto index = _index;
354 const auto raw = _buttons.at(button);
355
356 return anyEvent<sf::Event::JoystickButtonReleased>(_window._events,
357 [index, raw](const auto &released) { return released.joystickId == index && released.button == raw; });
358}
359
362#endif /* !SFMLWINDOW_HPP_ */
Unlike raylib's GamepadButton (normalized Xbox-style layout), sf::Joystick exposes raw numbered butto...
Definition SfmlGamepad.hpp:35
bool isAvailable() const override
Definition SfmlGamepad.hpp:42
Maps the contract's keys to sfml scancodes, and reads the state the window accumulated while popping ...
Definition SfmlKeyboard.hpp:41
Maps the contract's buttons to sfml buttons, and reads the state the window accumulated while popping...
Definition SfmlMouse.hpp:29
Definition SfmlPolygon.hpp:25
Sfml Sprite class - references a SfmlTexture, does not own it. Deleting the sprite never touches the ...
Definition SfmlSprite.hpp:27
Sfml Text class - references a SfmlFont, does not own it. Deleting the text never touches the font.
Definition SfmlText.hpp:28
Sfml Window class - implements IWindow2 only, sfml has no 3D.
Definition SfmlWindow.hpp:46
Vector2f getPosition() override
Definition SfmlWindow.hpp:68
void setMouseVisibility(bool visible) override
Definition SfmlWindow.hpp:96
void beginDraw() override
Definition SfmlWindow.hpp:131
bool isOpen() override
Definition SfmlWindow.hpp:60
void setFrameLimit(int32_t limit) override
Definition SfmlWindow.hpp:90
void close() override
Definition SfmlWindow.hpp:64
Vector2f getSize() override
Definition SfmlWindow.hpp:77
void endDraw() override
Definition SfmlWindow.hpp:135
int32_t getDelta() override
Definition SfmlWindow.hpp:100
void setPosition(Vector2f position) override
Definition SfmlWindow.hpp:73
void eventClose() override
Definition SfmlWindow.hpp:117
void setSize(Vector2f size) override
Definition SfmlWindow.hpp:82
~SfmlWindow()=default
SfmlWindow(int32_t screenWidth, int32_t screenHeight, std::string title)
Definition SfmlWindow.hpp:49
bool pollEvent() override
Drains the whole queue into _events and folds it into state. Called again in the same frame it finds ...
Definition SfmlWindow.hpp:109
bool isKeyReleased(Keys key) const override
Definition SfmlWindow.hpp:287
void drawText(graphic::IText *text) override
Definition SfmlWindow.hpp:244
bool isButtonDown(Button button) const override
Definition SfmlWindow.hpp:339
bool isButtonPressed(Buttons key) const override
Definition SfmlWindow.hpp:297
float GetMouseWheelMove() const override
Definition SfmlWindow.hpp:326
void drawSprite(graphic::ISprite *sprite) override
Definition SfmlWindow.hpp:239
bool isButtonDown(Buttons key) const override
Definition SfmlWindow.hpp:311
bool isKeyDown(Keys key) const override
Definition SfmlWindow.hpp:294
bool isKeyPressed(Keys key) const override
Definition SfmlWindow.hpp:280
bool isButtonPressed(Button button) const override
Definition SfmlWindow.hpp:344
bool isButtonUp(Buttons key) const override
Definition SfmlWindow.hpp:312
bool isButtonUp(Button button) const override
Definition SfmlWindow.hpp:342
bool isButtonReleased(Button button) const override
Definition SfmlWindow.hpp:352
bool isButtonReleased(Buttons key) const override
Definition SfmlWindow.hpp:304
std::vector< Keys > whichKeyDown() const override
Definition SfmlWindow.hpp:271
void setPosition(Vector2f position) override
Definition SfmlWindow.hpp:319
void drawPoly(graphic::IPolygon *polygon) override
Definition SfmlWindow.hpp:227
Vector2f getPosition() const override
Definition SfmlWindow.hpp:314
bool isKeyUp(Keys key) const override
Definition SfmlWindow.hpp:295