Browse code

Add GLBackendGLFW

Robert Cranston authored on 12/10/2021 04:08:43
Showing 1 changed files
1 1
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