summaryrefslogtreecommitdiff
path: root/window.h
blob: 3ecfe573b6df3a886967881dcb3f949eb6cd91a6 (plain)
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);
	}
}