| ... | ... | @@ -17,6 +17,16 @@ add_library(${PROJECT_NAME}) | 
| 17 | 17 | set(GLBACKEND_DEPENDENCIES) | 
| 18 | 18 | set(GLBACKEND_DEFINITIONS) | 
| 19 | 19 |  | 
| 20 | +## glfw | |
| 21 | +find_package(glfw3) | |
| 22 | +if(glfw3_FOUND) | |
| 23 | + list(APPEND GLBACKEND_DEPENDENCIES glfw) | |
| 24 | + list(APPEND GLBACKEND_DEFINITIONS GLBACKEND_GLFW) | |
| 25 | +endif() | |
| 26 | + | |
| 27 | +## Cache | |
| 28 | +set(GLBACKEND_DEFINITIONS "${GLBACKEND_DEFINITIONS}" CACHE INTERNAL "") | |
| 29 | + | |
| 20 | 30 | ## Common | 
| 21 | 31 | include(common.cmake) | 
| 22 | 32 | common( | 
| ... | ... | @@ -4,14 +4,16 @@ A [C++11][]/[OpenGL][] \>=[1.0][] [backend][] library. | 
| 4 | 4 |  | 
| 5 | 5 | Currently supported backends: | 
| 6 | 6 |  | 
| 7 | -| Name | Category | Define | Include | Class | | |
| 8 | -| -- | -- | -- | -- | -- | | |
| 7 | +| Name | Category | Define | Include | Class | | |
| 8 | +| -- | -- | -- | -- | -- | | |
| 9 | +| [GLFW][] | Windowing/input | `GLBACKEND_GLFW` | `glbackend_glfw.hpp` | `BackendGLFW` | | |
| 9 | 10 |  | 
| 10 | 11 | [`glbackend`]: https://git.rcrnstn.net/rcrnstn/glbackend | 
| 11 | 12 | [C++11]: https://en.wikipedia.org/wiki/C++11 | 
| 12 | 13 | [OpenGL]: https://en.wikipedia.org/wiki/OpenGL | 
| 13 | 14 | [1.0]: https://en.wikipedia.org/wiki/OpenGL#Version_history | 
| 14 | 15 | [backend]: https://www.khronos.org/opengl/wiki/Related_toolkits_and_APIs#OpenGL_initialization | 
| 16 | +[GLFW]: https://www.glfw.org | |
| 15 | 17 |  | 
| 16 | 18 | ## Usage | 
| 17 | 19 |  | 
| ... | ... | @@ -330,10 +332,13 @@ System dependencies that need to be installed: | 
| 330 | 332 | - Public (interface): | 
| 331 | 333 | - [OpenGL][] (e.g. [`libgl1-mesa-dev`][]). | 
| 332 | 334 | - [OpenGL Extension Wrangler (GLEW)][] (e.g. [`libglew-dev`][]). | 
| 335 | +- Private (tests): | |
| 336 | + - [GLFW][] (e.g. [`libglfw3-dev`][]). | |
| 333 | 337 |  | 
| 334 | 338 | [OpenGL Extension Wrangler (GLEW)]: http://glew.sourceforge.net | 
| 335 | 339 | [`libgl1-mesa-dev`]: https://packages.debian.org/search?keywords=libgl1-mesa-dev | 
| 336 | 340 | [`libglew-dev`]: https://packages.debian.org/search?keywords=libglew-dev | 
| 341 | +[`libglfw3-dev`]: https://packages.debian.org/search?keywords=libglfw3-dev | |
| 337 | 342 |  | 
| 338 | 343 | ## Build system | 
| 339 | 344 |  | 
| 9 | 12 | new file mode 100644 | 
| ... | ... | @@ -0,0 +1,182 @@ | 
| 1 | +/// Guards | |
| 2 | + | |
| 3 | +#ifdef GLBACKEND_GLFW | |
| 4 | +#ifndef GLBACKEND_GLFW_HPP_ | |
| 5 | +#define GLBACKEND_GLFW_HPP_ | |
| 6 | + | |
| 7 | + | |
| 8 | +/// Includes | |
| 9 | + | |
| 10 | +#include <array> | |
| 11 | +#include <string> | |
| 12 | + | |
| 13 | +#include <glbackend.hpp> | |
| 14 | + | |
| 15 | +#include <GLFW/glfw3.h> | |
| 16 | + | |
| 17 | + | |
| 18 | +/// Class definition | |
| 19 | + | |
| 20 | +class GLBackendGLFW final : public GLBackend | |
| 21 | +{ | |
| 22 | + | |
| 23 | +public: | |
| 24 | + | |
| 25 | + //// Special member functions | |
| 26 | + | |
| 27 | + explicit GLBackendGLFW( | |
| 28 | + std::string const & title, | |
| 29 | +        std::array<int, 2>  size        = {0, 0}, | |
| 30 | +        std::array<int, 2>  version     = {0, 0}, | |
| 31 | + int samples = 0, | |
| 32 | + bool fullscreen = false, | |
| 33 | + bool transparent = false | |
| 34 | + ); | |
| 35 | + ~GLBackendGLFW(); | |
| 36 | + GLBackendGLFW(GLBackendGLFW &&) noexcept; | |
| 37 | + GLBackendGLFW(GLBackendGLFW const &) = delete; | |
| 38 | + GLBackendGLFW & operator=(GLBackendGLFW &&) = delete; | |
| 39 | + GLBackendGLFW & operator=(GLBackendGLFW const &) = delete; | |
| 40 | + | |
| 41 | + //// Context | |
| 42 | + | |
| 43 | + void current() override; | |
| 44 | + | |
| 45 | + //// Render loop | |
| 46 | + | |
| 47 | + void swap() override; | |
| 48 | + | |
| 49 | + void events() override; | |
| 50 | + | |
| 51 | + bool running() const override; | |
| 52 | + bool running(bool running) override; | |
| 53 | + | |
| 54 | + float time() const override; | |
| 55 | + float time(float time) override; | |
| 56 | + | |
| 57 | + //// Input and output | |
| 58 | + | |
| 59 | + void lock(bool lock) override; | |
| 60 | + | |
| 61 | + bool key (std::string const & key) const override; | |
| 62 | + bool button(int button) const override; | |
| 63 | + | |
| 64 | + //// Debug | |
| 65 | + | |
| 66 | + std::string debug_info() const override; | |
| 67 | + | |
| 68 | +protected: | |
| 69 | + | |
| 70 | + //// Special member functions | |
| 71 | + | |
| 72 | + void destroy_(); | |
| 73 | + | |
| 74 | + //// Debug | |
| 75 | + | |
| 76 | + void static debug_glfw_error_callback_( | |
| 77 | + int error, | |
| 78 | + char const * message | |
| 79 | + ); | |
| 80 | + | |
| 81 | +protected: | |
| 82 | + | |
| 83 | + //// Context | |
| 84 | + | |
| 85 | + int static thread_local init_count_; | |
| 86 | + | |
| 87 | + GLFWwindow * window_; | |
| 88 | + | |
| 89 | +}; | |
| 90 | + | |
| 91 | + | |
| 92 | +/// Inline definitions | |
| 93 | + | |
| 94 | +inline void GLBackendGLFW::current() | |
| 95 | +{ | |
| 96 | + glfwMakeContextCurrent(window_); | |
| 97 | +} | |
| 98 | + | |
| 99 | +inline void GLBackendGLFW::events() | |
| 100 | +{ | |
| 101 | + glfwPollEvents(); | |
| 102 | +} | |
| 103 | + | |
| 104 | +inline void GLBackendGLFW::swap() | |
| 105 | +{ | |
| 106 | + glfwSwapBuffers(window_); | |
| 107 | +} | |
| 108 | + | |
| 109 | +inline bool GLBackendGLFW::running(bool running) | |
| 110 | +{ | |
| 111 | + glfwSetWindowShouldClose(window_, !running); | |
| 112 | + return running; | |
| 113 | +} | |
| 114 | + | |
| 115 | +inline bool GLBackendGLFW::running() const | |
| 116 | +{ | |
| 117 | + return !glfwWindowShouldClose(window_); | |
| 118 | +} | |
| 119 | + | |
| 120 | +inline float GLBackendGLFW::time() const | |
| 121 | +{ | |
| 122 | + return (float)glfwGetTime(); | |
| 123 | +} | |
| 124 | + | |
| 125 | +inline float GLBackendGLFW::time(float time) | |
| 126 | +{ | |
| 127 | + glfwSetTime((double)time); | |
| 128 | + return time; | |
| 129 | +} | |
| 130 | + | |
| 131 | +inline void GLBackendGLFW::lock(bool lock) | |
| 132 | +{ | |
| 133 | + glfwSetInputMode( | |
| 134 | + window_, GLFW_CURSOR, lock ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL | |
| 135 | + ); | |
| 136 | +} | |
| 137 | + | |
| 138 | +inline bool GLBackendGLFW::key(std::string const & key) const | |
| 139 | +{ | |
| 140 | + if (key == "Left") | |
| 141 | + return glfwGetKey(window_, GLFW_KEY_LEFT) == GLFW_PRESS; | |
| 142 | + else if (key == "Right") | |
| 143 | + return glfwGetKey(window_, GLFW_KEY_RIGHT) == GLFW_PRESS; | |
| 144 | + else if (key == "Up") | |
| 145 | + return glfwGetKey(window_, GLFW_KEY_UP) == GLFW_PRESS; | |
| 146 | + else if (key == "Down") | |
| 147 | + return glfwGetKey(window_, GLFW_KEY_DOWN) == GLFW_PRESS; | |
| 148 | + else if (key == "Enter") | |
| 149 | + return glfwGetKey(window_, GLFW_KEY_ENTER) == GLFW_PRESS; | |
| 150 | + else if (key == "Escape") | |
| 151 | + return glfwGetKey(window_, GLFW_KEY_ESCAPE) == GLFW_PRESS; | |
| 152 | + else if (key == "Tab") | |
| 153 | + return glfwGetKey(window_, GLFW_KEY_TAB) == GLFW_PRESS; | |
| 154 | + else if (key == "Backspace") | |
| 155 | + return glfwGetKey(window_, GLFW_KEY_BACKSPACE) == GLFW_PRESS; | |
| 156 | + else if (key == "Control") | |
| 157 | + return | |
| 158 | + glfwGetKey(window_, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || | |
| 159 | + glfwGetKey(window_, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS; | |
| 160 | + else if (key == "Shift") | |
| 161 | + return | |
| 162 | + glfwGetKey(window_, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || | |
| 163 | + glfwGetKey(window_, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS; | |
| 164 | + else if (key == "Alt") | |
| 165 | + return | |
| 166 | + glfwGetKey(window_, GLFW_KEY_LEFT_ALT) == GLFW_PRESS || | |
| 167 | + glfwGetKey(window_, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS; | |
| 168 | + else if (key.length() == 1) | |
| 169 | + return glfwGetKey(window_, key[0]) == GLFW_PRESS; | |
| 170 | + return false; | |
| 171 | +} | |
| 172 | + | |
| 173 | +inline bool GLBackendGLFW::button(int button) const | |
| 174 | +{ | |
| 175 | + return glfwGetMouseButton(window_, button - 1) == GLFW_PRESS; | |
| 176 | +} | |
| 177 | + | |
| 178 | + | |
| 179 | +/// Guards | |
| 180 | + | |
| 181 | +#endif | |
| 182 | +#endif | 
| 0 | 183 | new file mode 100644 | 
| ... | ... | @@ -0,0 +1,380 @@ | 
| 1 | +/// Guards | |
| 2 | + | |
| 3 | + | |
| 4 | +#ifdef GLBACKEND_GLFW | |
| 5 | + | |
| 6 | + | |
| 7 | +/// Includes | |
| 8 | + | |
| 9 | + | |
| 10 | +#include <glbackend_glfw.hpp> | |
| 11 | + | |
| 12 | +#include <array> | |
| 13 | +#include <functional> | |
| 14 | +#include <limits> | |
| 15 | +#include <sstream> | |
| 16 | +#include <string> | |
| 17 | +#include <type_traits> | |
| 18 | +#include <utility> | |
| 19 | + | |
| 20 | +#include <glbase.hpp> | |
| 21 | +#include <glbackend.hpp> | |
| 22 | + | |
| 23 | +#include <GLFW/glfw3.h> | |
| 24 | + | |
| 25 | +// NOLINTNEXTLINE | |
| 26 | +#define STR_EXCEPTION GLBase::Exception | |
| 27 | +#include <str.hpp> | |
| 28 | + | |
| 29 | + | |
| 30 | +/// Special member functions | |
| 31 | + | |
| 32 | + | |
| 33 | +GLBackendGLFW::GLBackendGLFW( | |
| 34 | + std::string const & title, | |
| 35 | + std::array<int, 2> size, | |
| 36 | + std::array<int, 2> version, | |
| 37 | + int samples, | |
| 38 | + bool fullscreen, | |
| 39 | + bool transparent | |
| 40 | +) | |
| 41 | +: | |
| 42 | + GLBackend(), | |
| 43 | +    window_{nullptr} | |
| 44 | +{ | |
| 45 | + //// Backend errors | |
| 46 | + glfwSetErrorCallback(debug_glfw_error_callback_); | |
| 47 | + auto const glfw_get_error_description = []() | |
| 48 | +    { | |
| 49 | +        char const * description{}; | |
| 50 | + glfwGetError(&description); | |
| 51 | + return description; | |
| 52 | + }; | |
| 53 | + | |
| 54 | + //// Backend init | |
| 55 | + if (!glfwInit()) | |
| 56 | + STR_THROW( | |
| 57 | + "Failed to initialize GLFW" << ":\n" << | |
| 58 | + glfw_get_error_description() | |
| 59 | + ); | |
| 60 | + ++init_count_; | |
| 61 | + | |
| 62 | + //// Window options | |
| 63 | + glfwDefaultWindowHints(); | |
| 64 | + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); | |
| 65 | + GLFWmonitor * monitor = nullptr; | |
| 66 | + if (fullscreen) | |
| 67 | + monitor = glfwGetPrimaryMonitor(); | |
| 68 | + if (transparent) | |
| 69 | +    { | |
| 70 | + glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); | |
| 71 | + glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); | |
| 72 | + glfwWindowHint(GLFW_FLOATING, GLFW_TRUE); | |
| 73 | + } | |
| 74 | + if (size[0] == 0 || size[1] == 0) | |
| 75 | +    { | |
| 76 | + auto const * mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); | |
| 77 | + size[0] = mode->width; | |
| 78 | + size[1] = mode->height; | |
| 79 | + } | |
| 80 | + size_ = size; | |
| 81 | + | |
| 82 | + //// Context options | |
| 83 | + glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE); | |
| 84 | + glfwWindowHint(GLFW_RED_BITS, 8); // NOLINT | |
| 85 | + glfwWindowHint(GLFW_GREEN_BITS, 8); // NOLINT | |
| 86 | + glfwWindowHint(GLFW_BLUE_BITS, 8); // NOLINT | |
| 87 | + glfwWindowHint(GLFW_ALPHA_BITS, 8); // NOLINT | |
| 88 | + glfwWindowHint(GLFW_DEPTH_BITS, 24); // NOLINT | |
| 89 | + glfwWindowHint(GLFW_STENCIL_BITS, 8); // NOLINT | |
| 90 | + if (samples) | |
| 91 | + glfwWindowHint(GLFW_SAMPLES, samples); | |
| 92 | + if (version[0]) | |
| 93 | + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, version[0]); | |
| 94 | + if (version[1]) | |
| 95 | + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, version[1]); | |
| 96 | + if ((version[0] == 3 && version[1] >= 2) || version[0] >= 4) // NOLINT | |
| 97 | +    { | |
| 98 | + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); | |
| 99 | + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); | |
| 100 | + } | |
| 101 | + if (debug() >= 1) | |
| 102 | + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); | |
| 103 | + else if (version[0] >= 2) // NOLINT | |
| 104 | + glfwWindowHint(GLFW_CONTEXT_NO_ERROR, GLFW_TRUE); | |
| 105 | + | |
| 106 | + try | |
| 107 | +    { | |
| 108 | + //// Window | |
| 109 | + window_ = glfwCreateWindow( | |
| 110 | + size[0], | |
| 111 | + size[1], | |
| 112 | + title.c_str(), | |
| 113 | + monitor, | |
| 114 | + nullptr | |
| 115 | + ); | |
| 116 | + if (!window_) | |
| 117 | + STR_THROW( | |
| 118 | + "Failed to create GLFW window" << ":\n" << | |
| 119 | + glfw_get_error_description() | |
| 120 | + ); | |
| 121 | + | |
| 122 | + //// Context | |
| 123 | + glfwMakeContextCurrent(window_); | |
| 124 | + if (debug() == 0) | |
| 125 | + glfwSwapInterval(1); | |
| 126 | + init_(); | |
| 127 | + | |
| 128 | + //// Lock | |
| 129 | + if (fullscreen) | |
| 130 | + lock(true); | |
| 131 | + } | |
| 132 | + catch (...) | |
| 133 | +    { | |
| 134 | + destroy_(); | |
| 135 | + throw; | |
| 136 | + } | |
| 137 | + | |
| 138 | + //// Callbacks | |
| 139 | + // NOLINTNEXTLINE | |
| 140 | + #define GLBACKEND_GLFW_SELF_(WINDOW) \ | |
| 141 | + (*static_cast<GLBackendGLFW *>(glfwGetWindowUserPointer(WINDOW))) | |
| 142 | + glfwSetWindowUserPointer(window_, this); | |
| 143 | + glfwSetKeyCallback( | |
| 144 | + window_, | |
| 145 | + []( | |
| 146 | + GLFWwindow * window, | |
| 147 | + int key, | |
| 148 | + int scancode, | |
| 149 | + int action, | |
| 150 | + int mods | |
| 151 | + ) | |
| 152 | +        { | |
| 153 | + (void)scancode; | |
| 154 | + (void)mods; | |
| 155 | + if (action != GLFW_PRESS) | |
| 156 | + return; | |
| 157 | + auto & self = GLBACKEND_GLFW_SELF_(window); | |
| 158 | + if (self.callback_key_) | |
| 159 | +            { | |
| 160 | + if(key == GLFW_KEY_LEFT) | |
| 161 | +                    self.callback_key_("Left"); | |
| 162 | + else if (key == GLFW_KEY_RIGHT) | |
| 163 | +                    self.callback_key_("Right"); | |
| 164 | + else if (key == GLFW_KEY_UP) | |
| 165 | +                    self.callback_key_("Up"); | |
| 166 | + else if (key == GLFW_KEY_DOWN) | |
| 167 | +                    self.callback_key_("Down"); | |
| 168 | + else if (key == GLFW_KEY_ENTER) | |
| 169 | +                    self.callback_key_("Enter"); | |
| 170 | + else if (key == GLFW_KEY_ESCAPE) | |
| 171 | +                    self.callback_key_("Escape"); | |
| 172 | + else if (key == GLFW_KEY_TAB) | |
| 173 | +                    self.callback_key_("Tab"); | |
| 174 | + else if (key == GLFW_KEY_BACKSPACE) | |
| 175 | +                    self.callback_key_("Backspace"); | |
| 176 | + else if ( | |
| 177 | + key == GLFW_KEY_RIGHT_CONTROL || | |
| 178 | + key == GLFW_KEY_LEFT_CONTROL | |
| 179 | + ) | |
| 180 | +                    self.callback_key_("Control"); | |
| 181 | + else if ( | |
| 182 | + key == GLFW_KEY_RIGHT_SHIFT || | |
| 183 | + key == GLFW_KEY_LEFT_SHIFT | |
| 184 | + ) | |
| 185 | +                    self.callback_key_("Shift"); | |
| 186 | + else if ( | |
| 187 | + key == GLFW_KEY_RIGHT_ALT || | |
| 188 | + key == GLFW_KEY_LEFT_ALT | |
| 189 | + ) | |
| 190 | +                    self.callback_key_("Alt"); | |
| 191 | + else if ( | |
| 192 | + 0 <= key && key <= std::numeric_limits<char>::max() | |
| 193 | + ) | |
| 194 | + self.callback_key_(std::string(1, (char)key)); | |
| 195 | + } | |
| 196 | + } | |
| 197 | + ); | |
| 198 | + glfwSetMouseButtonCallback( | |
| 199 | + window_, | |
| 200 | + []( | |
| 201 | + GLFWwindow * window, | |
| 202 | + int button, | |
| 203 | + int action, | |
| 204 | + int mods | |
| 205 | + ) | |
| 206 | +        { | |
| 207 | + (void)mods; | |
| 208 | + if (action != GLFW_PRESS) | |
| 209 | + return; | |
| 210 | + auto & self = GLBACKEND_GLFW_SELF_(window); | |
| 211 | + if (self.callback_button_) | |
| 212 | + self.callback_button_(button); | |
| 213 | + } | |
| 214 | + ); | |
| 215 | + glfwSetScrollCallback( | |
| 216 | + window_, | |
| 217 | + []( | |
| 218 | + GLFWwindow * window, | |
| 219 | + double scroll_x, | |
| 220 | + double scroll_y | |
| 221 | + ) | |
| 222 | +        { | |
| 223 | + auto & self = GLBACKEND_GLFW_SELF_(window); | |
| 224 | +            self.scroll_ = { | |
| 225 | + (float)scroll_x, | |
| 226 | + (float)scroll_y, | |
| 227 | + }; | |
| 228 | + if (self.callback_scroll_) | |
| 229 | + self.callback_scroll_(self.scroll_); | |
| 230 | + } | |
| 231 | + ); | |
| 232 | + glfwSetCursorPosCallback( | |
| 233 | + window_, | |
| 234 | + []( | |
| 235 | + GLFWwindow * window, | |
| 236 | + double x, | |
| 237 | + double y | |
| 238 | + ) | |
| 239 | +        { | |
| 240 | + auto & self = GLBACKEND_GLFW_SELF_(window); | |
| 241 | +            self.move_ = { | |
| 242 | + (float)x - self.position_[0], | |
| 243 | + (float)y - self.position_[1], | |
| 244 | + }; | |
| 245 | +            self.position_ = { | |
| 246 | + (float)x, | |
| 247 | + (float)y, | |
| 248 | + }; | |
| 249 | + if (self.callback_position_) | |
| 250 | + self.callback_position_(self.position_); | |
| 251 | + if (self.callback_move_) | |
| 252 | + self.callback_move_(self.move_); | |
| 253 | + } | |
| 254 | + ); | |
| 255 | + glfwSetFramebufferSizeCallback( | |
| 256 | + window_, | |
| 257 | + []( | |
| 258 | + GLFWwindow * window, | |
| 259 | + int width, | |
| 260 | + int height | |
| 261 | + ) | |
| 262 | +        { | |
| 263 | + auto & self = GLBACKEND_GLFW_SELF_(window); | |
| 264 | +            self.size_ = { | |
| 265 | + width, | |
| 266 | + height, | |
| 267 | + }; | |
| 268 | + if (self.callback_size_) | |
| 269 | + self.callback_size_(self.size_); | |
| 270 | + } | |
| 271 | + ); | |
| 272 | +} | |
| 273 | + | |
| 274 | + | |
| 275 | +GLBackendGLFW::GLBackendGLFW(GLBackendGLFW && other) noexcept | |
| 276 | +: | |
| 277 | + GLBackend(std::move(other)), | |
| 278 | +    window_{other.window_} | |
| 279 | +{ | |
| 280 | + other.window_ = nullptr; | |
| 281 | +} | |
| 282 | + | |
| 283 | + | |
| 284 | +GLBackendGLFW::~GLBackendGLFW() | |
| 285 | +{ | |
| 286 | + destroy_(); | |
| 287 | +} | |
| 288 | + | |
| 289 | + | |
| 290 | +void GLBackendGLFW::destroy_() | |
| 291 | +{ | |
| 292 | + if (window_) | |
| 293 | +    { | |
| 294 | + lock(false); | |
| 295 | + glfwDestroyWindow(window_); | |
| 296 | + } | |
| 297 | + if (--init_count_ == 0) | |
| 298 | + glfwTerminate(); | |
| 299 | +} | |
| 300 | + | |
| 301 | + | |
| 302 | +GLBASE_GLOBAL(GLBackendGLFW::init_count_, {0}) | |
| 303 | + | |
| 304 | + | |
| 305 | +/// Debug | |
| 306 | + | |
| 307 | + | |
| 308 | +std::string GLBackendGLFW::debug_info() const | |
| 309 | +{ | |
| 310 | +    auto ostream = std::ostringstream{}; | |
| 311 | + | |
| 312 | +    auto version_major = int{}; | |
| 313 | +    auto version_minor = int{}; | |
| 314 | +    auto version_rev   = int{}; | |
| 315 | + glfwGetVersion( | |
| 316 | + &version_major, | |
| 317 | + &version_minor, | |
| 318 | + &version_rev | |
| 319 | + ); | |
| 320 | +    debug_info_(ostream, "GLFW", { | |
| 321 | +        { | |
| 322 | + "VERSION_COMPILED", | |
| 323 | + glfwGetVersionString() | |
| 324 | + }, | |
| 325 | +        { | |
| 326 | + "VERSION_LINKED", | |
| 327 | + std::to_string(version_major) + "." + | |
| 328 | + std::to_string(version_minor) + "." + | |
| 329 | + std::to_string(version_rev), | |
| 330 | + }, | |
| 331 | + }); | |
| 332 | + | |
| 333 | + ostream << GLBackend::debug_info(); | |
| 334 | + | |
| 335 | + return ostream.str(); | |
| 336 | +} | |
| 337 | + | |
| 338 | + | |
| 339 | +void GLBackendGLFW::debug_glfw_error_callback_( | |
| 340 | + int error, | |
| 341 | + char const * message | |
| 342 | +) | |
| 343 | +{ | |
| 344 | +    auto ostream = std::ostringstream{}; | |
| 345 | + ostream << std::hex << std::showbase; | |
| 346 | + | |
| 347 | + // https://www.glfw.org/docs/3.3/group__errors.html | |
| 348 | + ostream << "GLFW error "; | |
| 349 | + // NOLINTNEXTLINE | |
| 350 | + #define GLBACKEND_GLFW_CALLBACK_CASE_(VALUE) \ | |
| 351 | + case GLFW_ ## VALUE: \ | |
| 352 | + ostream << #VALUE; \ | |
| 353 | + break; | |
| 354 | + switch(error) | |
| 355 | +    { | |
| 356 | + GLBACKEND_GLFW_CALLBACK_CASE_(NOT_INITIALIZED) | |
| 357 | + GLBACKEND_GLFW_CALLBACK_CASE_(NO_CURRENT_CONTEXT) | |
| 358 | + GLBACKEND_GLFW_CALLBACK_CASE_(INVALID_ENUM) | |
| 359 | + GLBACKEND_GLFW_CALLBACK_CASE_(INVALID_VALUE) | |
| 360 | + GLBACKEND_GLFW_CALLBACK_CASE_(OUT_OF_MEMORY) | |
| 361 | + GLBACKEND_GLFW_CALLBACK_CASE_(API_UNAVAILABLE) | |
| 362 | + GLBACKEND_GLFW_CALLBACK_CASE_(VERSION_UNAVAILABLE) | |
| 363 | + GLBACKEND_GLFW_CALLBACK_CASE_(PLATFORM_ERROR) | |
| 364 | + GLBACKEND_GLFW_CALLBACK_CASE_(FORMAT_UNAVAILABLE) | |
| 365 | + GLBACKEND_GLFW_CALLBACK_CASE_(NO_WINDOW_CONTEXT) | |
| 366 | + default: | |
| 367 | + ostream << error; | |
| 368 | + } | |
| 369 | + ostream << ":\n"; | |
| 370 | + | |
| 371 | + ostream << message; | |
| 372 | + | |
| 373 | + debug_callback()(ostream.str()); | |
| 374 | +} | |
| 375 | + | |
| 376 | + | |
| 377 | +/// Guards | |
| 378 | + | |
| 379 | + | |
| 380 | +#endif | 
| 0 | 381 | new file mode 100644 | 
| ... | ... | @@ -0,0 +1,29 @@ | 
| 1 | +#ifdef GLBACKEND_GLFW | |
| 2 | + | |
| 3 | + | |
| 4 | +#include <GLFW/glfw3.h> | |
| 5 | + | |
| 6 | + | |
| 7 | +constexpr auto width = 640; | |
| 8 | +constexpr auto height = 480; | |
| 9 | + | |
| 10 | + | |
| 11 | +int main() | |
| 12 | +{ | |
| 13 | + if (!glfwInit()) | |
| 14 | + return 1; | |
| 15 | + auto * window = glfwCreateWindow( | |
| 16 | + width, height, | |
| 17 | + "baseline_glfw", | |
| 18 | + nullptr, nullptr | |
| 19 | + ); | |
| 20 | + if (!window) | |
| 21 | + return 1; | |
| 22 | + glfwDestroyWindow(window); | |
| 23 | + glfwTerminate(); | |
| 24 | +} | |
| 25 | + | |
| 26 | + | |
| 27 | +#else | |
| 28 | +int main() {} | |
| 29 | +#endif |