summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Smeding <tom.smeding@gmail.com>2020-04-12 17:19:11 +0200
committerTom Smeding <tom.smeding@gmail.com>2020-04-12 17:19:11 +0200
commita089056a95104576ab9832b01c67009f4e040243 (patch)
treea39d411574ce931632622f27d09bdc936cc804fc
Initial commit
-rw-r--r--.gitignore1
-rw-r--r--window.h126
2 files changed, 127 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9bf3746
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+compile_commands.json
diff --git a/window.h b/window.h
new file mode 100644
index 0000000..3ecfe57
--- /dev/null
+++ b/window.h
@@ -0,0 +1,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);
+ }
+}