1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
#pragma once
// Easy window creation in C++.
//
// This header-only library uses SDL; therefore, on a Linux system, you need to
// add something to the effect of the following to your build system:
// CFLAGS += $(shell pkg-config --cflags sdl2)
// LDFLAGS += $(shell pkg-config --libs sdl2)
#include <string>
#include <vector>
#include <functional>
#include <stdexcept>
#include <SDL.h>
class Window {
public:
class Opts {
public:
inline Opts() = default;
inline Opts& fullscreen(bool f) { _fullscreen = f; return *this; }
inline Opts& resizeable(bool r) { _resizable = r; return *this; }
inline bool fullscreen() const { return _fullscreen; }
inline bool resizable() const { return _resizable; }
private:
bool _fullscreen = false;
bool _resizable = false;
};
/// The width and height passed to this function are _suggestions_.
/// The window manager may decide to use some other size instead, and this
/// size may also change during execution (especially if resizable, but
/// perhaps also otherwise).
inline Window(const std::string &title, int wid_sugg, int hei_sugg);
inline Window(const std::string &title, int wid_sugg, int hei_sugg, Opts opts);
inline ~Window();
/// The draw buffer will be of size 3 * width * height; row-major order, RGB pixels.
using DrawHandler = std::function<void(std::vector<uint8_t> &drawbuf, int width, int height)>;
/// Return true to stop the event loop.
using EventHandler = std::function<bool(const SDL_Event&)>;
/// The draw function will be called after each invocation of the event
/// function, and might also be invoked more often as the user re-focuses the
/// window.
inline void event_loop(EventHandler eventfunc, DrawHandler drawfunc);
private:
SDL_Window *win = nullptr;
std::vector<uint8_t> drawbuf;
SDL_Surface *surface = nullptr;
int surf_wid = -1, surf_hei = -1;
};
inline Window::Window(const std::string &title, int wid_sugg, int hei_sugg)
: Window(title, wid_sugg, hei_sugg, Opts{})
{}
inline Window::Window(const std::string &title, int wid_sugg, int hei_sugg, Opts opts) {
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
throw std::runtime_error(std::string{"SDL init error: "} + SDL_GetError());
}
Uint32 flags = 0;
if (opts.fullscreen()) flags |= SDL_WINDOW_FULLSCREEN;
if (opts.resizable()) flags |= SDL_WINDOW_RESIZABLE;
win = SDL_CreateWindow(
title.data(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
wid_sugg, hei_sugg, flags
);
if (!win) {
throw std::runtime_error(std::string{"SDL window creation error: "} + SDL_GetError());
}
SDL_GetWindowSize(win, &surf_wid, &surf_hei);
drawbuf.resize(3 * surf_wid * surf_hei);
surface = SDL_CreateRGBSurfaceFrom(
drawbuf.data(), surf_wid, surf_hei, 24, 3 * surf_wid,
255, 255 << 8, 255 << 16, 0
);
SDL_BlitSurface(surface, nullptr, SDL_GetWindowSurface(win), nullptr);
SDL_UpdateWindowSurface(win);
}
inline Window::~Window() {
if (surface) SDL_FreeSurface(surface);
SDL_DestroyWindow(win);
SDL_Quit();
}
inline void Window::event_loop(EventHandler eventfunc, DrawHandler drawfunc) {
SDL_Event e;
while (true) {
if (SDL_WaitEvent(&e) == 0) {
throw std::runtime_error(std::string{"SDL event wait error: "} + SDL_GetError());
}
if (e.type == SDL_QUIT) break;
if (eventfunc(e)) break;
int width, height;
SDL_GetWindowSize(win, &width, &height);
if (width != surf_wid || height != surf_hei) {
SDL_FreeSurface(surface);
surf_wid = width;
surf_hei = height;
drawbuf.resize(3 * surf_wid * surf_hei);
surface = SDL_CreateRGBSurfaceFrom(
drawbuf.data(), surf_wid, surf_hei, 24, 3 * surf_wid,
255, 255 << 8, 255 << 16, 0
);
}
drawfunc(drawbuf, width, height);
}
}
|