#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 #include #include #include #include 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 &drawbuf, int width, int height)>; /// Return true to stop the event loop. using EventHandler = std::function; /// 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 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); } }