Browse code

WIP: Add GLBackendWxWidgets

Robert Cranston authored on 20/10/2021 21:11:33
Showing 9 changed files

... ...
@@ -1,2 +1,5 @@
1 1
 # SDL
2 2
 # leak:libX11
3
+
4
+# wxWidgets
5
+leak:libfontconfig
... ...
@@ -31,6 +31,25 @@ if(SDL2_FOUND)
31 31
     list(APPEND GLBACKEND_DEFINITIONS  GLBACKEND_SDL)
32 32
 endif()
33 33
 
34
+## wxwidgets
35
+# https://cmake.org/cmake/help/v3.14/module/FindwxWidgets.html
36
+# https://cmake.org/cmake/help/v3.14/module/UsewxWidgets.html
37
+find_package(wxWidgets COMPONENTS gl core base)
38
+if(wxWidgets_FOUND)
39
+    add_library(wxWidgets INTERFACE)
40
+    set_target_properties(wxWidgets PROPERTIES
41
+        INTERFACE_INCLUDE_DIRECTORIES        "${wxWidgets_INCLUDE_DIRS}"
42
+        INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${wxWidgets_INCLUDE_DIRS}"
43
+        INTERFACE_LINK_DIRECTORIES           "${wxWidgets_LIBRARY_DIRS}"
44
+        INTERFACE_COMPILE_DEFINITIONS        "${wxWidgets_DEFINITIONS}"
45
+        INTERFACE_COMPILE_DEFINITIONS_DEBUG  "${wxWidgets_DEFINITIONS_DEBUG}"
46
+        INTERFACE_COMPILE_OPTIONS            "${wxWidgets_CXX_FLAGS}"
47
+        INTERFACE_LINK_LIBRARIES             "${wxWidgets_LIBRARIES}"
48
+    )
49
+    list(APPEND GLBACKEND_DEPENDENCIES wxWidgets)
50
+    list(APPEND GLBACKEND_DEFINITIONS  GLBACKEND_WXWIDGETS)
51
+endif()
52
+
34 53
 ## Common
35 54
 include(common.cmake)
36 55
 common(
... ...
@@ -2,20 +2,25 @@
2 2
 
3 3
 A [C++11][]/[OpenGL][] \>=[1.0][] [backend][] library.
4 4
 
5
-Currently supported backends:
5
+Currently supported backends (one from each category listed under
6
+[Context/Window Toolkits][] on the OpenGL wiki):
6 7
 
7
-| Name     | Category        | Define           | Include              | Class         |
8
-| --       | --              | --               | --                   | --            |
9
-| [GLFW][] | Windowing/input | `GLBACKEND_GLFW` | `glbackend_glfw.hpp` | `BackendGLFW` |
10
-| [SDL2][] | Multimedia      | `GLBACKEND_SDL`  | `glbackend_sdl.hpp`  | `BackendSDL`  |
8
+| Name          | Category           | Define                | Include                   | Class              |
9
+| --            | --                 | --                    | --                        | --                 |
10
+| [GLFW][]      | Windowing/input    | `GLBACKEND_GLFW`      | `glbackend_glfw.hpp`      | `BackendGLFW`      |
11
+| [SDL2][]      | Multimedia         | `GLBACKEND_SDL`       | `glbackend_sdl.hpp`       | `BackendSDL`       |
12
+| [wxWidgets][] | [Widget toolkit][] | `GLBACKEND_WXWIDGETS` | `glbackend_wxwidgets.hpp` | `BackendWxWidgets` |
11 13
 
12 14
 [`glbackend`]: https://git.rcrnstn.net/rcrnstn/glbackend
13 15
 [C++11]: https://en.wikipedia.org/wiki/C++11
14 16
 [OpenGL]: https://en.wikipedia.org/wiki/OpenGL
15 17
 [1.0]: https://en.wikipedia.org/wiki/OpenGL#Version_history
16 18
 [backend]: https://www.khronos.org/opengl/wiki/Related_toolkits_and_APIs#OpenGL_initialization
19
+[Context/Window Toolkits]: https://www.khronos.org/opengl/wiki/Related_toolkits_and_APIs#Context/Window_Toolkits
17 20
 [GLFW]: https://www.glfw.org
18 21
 [SDL2]: https://www.libsdl.org
22
+[wxWidgets]: https://www.wxwidgets.org
23
+[Widget toolkit]: https://en.wikipedia.org/wiki/Widget_toolkit
19 24
 
20 25
 ## Usage
21 26
 
... ...
@@ -337,12 +342,14 @@ System dependencies that need to be installed:
337 342
 -   Private (tests):
338 343
     -   [GLFW][] (e.g. [`libglfw3-dev`][]).
339 344
     -   [SDL2][] (e.g. [`libsdl2-dev`][]).
345
+    -   [wxWidgets][] (e.g. [`libwxgtk3.0-gtk3-dev`]).
340 346
 
341 347
 [OpenGL Extension Wrangler (GLEW)]: http://glew.sourceforge.net
342 348
 [`libgl1-mesa-dev`]: https://packages.debian.org/search?keywords=libgl1-mesa-dev
343 349
 [`libglew-dev`]: https://packages.debian.org/search?keywords=libglew-dev
344 350
 [`libglfw3-dev`]: https://packages.debian.org/search?keywords=libglfw3-dev
345 351
 [`libsdl2-dev`]: https://packages.debian.org/search?keywords=libsdl2-dev
352
+[`libwxgtk3.0-gtk3-dev`]: https://packages.debian.org/search?keywords=libwxgtk3.0-gtk3-dev
346 353
 
347 354
 ## Build system
348 355
 
349 356
new file mode 100644
350 357
Binary files /dev/null and b/assets/tests/GLBackendWxWidgets.tga differ
... ...
@@ -8,6 +8,9 @@
8 8
 #elif defined(GLBACKEND_SDL)
9 9
     #include "glbackend_sdl.hpp" // IWYU pragma: export
10 10
     using GLBackendDefault = GLBackendSDL;
11
+#elif defined(GLBACKEND_WXWIDGETS)
12
+    #include "glbackend_wxwidgets.hpp" // IWYU pragma: export
13
+    using GLBackendDefault = GLBackendWxWidgets;
11 14
 #else
12 15
     #error "No GLBackendDefault configured"
13 16
 #endif
14 17
new file mode 100644
... ...
@@ -0,0 +1,183 @@
1
+/// Guards
2
+
3
+#ifdef  GLBACKEND_WXWIDGETS
4
+#ifndef GLBACKEND_WXWIDGETS_HPP_
5
+#define GLBACKEND_WXWIDGETS_HPP_
6
+
7
+
8
+/// Includes
9
+
10
+#include <array>
11
+#include <string>
12
+
13
+#include <glbackend.hpp>
14
+
15
+// #include <wx/wx.h>
16
+#include <wx/evtloop.h>
17
+#include <wx/glcanvas.h>
18
+#include <wx/app.h>
19
+#include <wx/gdicmn.h>
20
+#include <wx/gtk/app.h>
21
+#include <wx/gtk/evtloop.h>
22
+#include <wx/longlong.h>
23
+#include <wx/time.h>
24
+#include <wx/unix/glx11.h>
25
+#include "wx/cursor.h"
26
+class wxAppConsole;
27
+class wxFrame;
28
+
29
+
30
+/// Class definition
31
+
32
+class GLBackendWxWidgets final : public GLBackend
33
+{
34
+
35
+public:
36
+
37
+    //// Special member functions
38
+
39
+    explicit GLBackendWxWidgets(
40
+        std::string const & title,
41
+        std::array<int, 2>  size        = {0, 0},
42
+        std::array<int, 2>  version     = {0, 0},
43
+        int                 samples     = 0,
44
+        bool                fullscreen  = false,
45
+        bool                transparent = false
46
+    );
47
+    ~GLBackendWxWidgets();
48
+    GLBackendWxWidgets(GLBackendWxWidgets &&) noexcept;
49
+    GLBackendWxWidgets(GLBackendWxWidgets const &) = delete;
50
+    GLBackendWxWidgets & operator=(GLBackendWxWidgets &&) = delete;
51
+    GLBackendWxWidgets & operator=(GLBackendWxWidgets const &) = delete;
52
+
53
+    //// Context
54
+
55
+    void current() override;
56
+
57
+    //// Render loop
58
+
59
+    void swap() override;
60
+
61
+    void events() override;
62
+
63
+    bool running() const override;
64
+    bool running(bool running) override;
65
+
66
+    float time() const override;
67
+    float time(float time) override;
68
+
69
+    //// Input and output
70
+
71
+    void lock(bool lock) override;
72
+
73
+    bool key   (std::string const & key)    const override;
74
+    bool button(int                 button) const override;
75
+
76
+    //// Debug
77
+
78
+    std::string debug_info() const override;
79
+
80
+protected:
81
+
82
+    //// Special member functions
83
+
84
+    void destroy_();
85
+
86
+protected:
87
+
88
+    //// Context
89
+
90
+    wxAppConsole * app_;
91
+    wxFrame      * frame_;
92
+    wxGLCanvas   * canvas_;
93
+    wxGLContext  * context_;
94
+
95
+    //// Render loop
96
+
97
+    wxGUIEventLoop event_loop_;
98
+    float          time_;
99
+
100
+    //// Input and output
101
+
102
+    bool locked_;
103
+
104
+};
105
+
106
+
107
+/// Inline definitions
108
+
109
+inline void GLBackendWxWidgets::current()
110
+{
111
+    context_->SetCurrent(*canvas_);
112
+}
113
+
114
+inline void GLBackendWxWidgets::events()
115
+{
116
+    // wxWidgets/include/wx/evtloop.h
117
+    // wxWidgets/src/common/evtloopcmn.cpp
118
+    wxEventLoopActivator event_loop_activator(&event_loop_);
119
+    while (event_loop_.Pending())
120
+        event_loop_.Dispatch();
121
+    event_loop_.ProcessIdle();
122
+    if (wxTheApp)
123
+        wxTheApp->ProcessPendingEvents();
124
+}
125
+
126
+inline void GLBackendWxWidgets::swap()
127
+{
128
+    canvas_->SwapBuffers();
129
+}
130
+
131
+inline bool GLBackendWxWidgets::running() const
132
+{
133
+    return canvas_->IsShown();
134
+}
135
+
136
+inline bool GLBackendWxWidgets::running(bool running)
137
+{
138
+    canvas_->Show(running);
139
+    return running;
140
+}
141
+
142
+inline float GLBackendWxWidgets::time() const
143
+{
144
+    return (float)(wxGetUTCTimeUSec().ToDouble() / 1000000.0) - time_;
145
+}
146
+
147
+inline float GLBackendWxWidgets::time(float time)
148
+{
149
+    time_ = (float)(wxGetUTCTimeUSec().ToDouble() / 1000000.0) - time;
150
+    return time;
151
+}
152
+
153
+inline void GLBackendWxWidgets::lock(bool lock)
154
+{
155
+    canvas_->SetCursor(lock ? wxCursor(wxCURSOR_BLANK) : wxNullCursor);
156
+    if (lock && !locked_)
157
+        canvas_->CaptureMouse();
158
+    else if (!lock && locked_)
159
+        canvas_->ReleaseMouse();
160
+    locked_ = lock;
161
+    // TODO(rcrnstn): wxWidgets `WarpPointer` needed?
162
+    // canvas_->WarpPointer(
163
+    //     size_[0] / 2,
164
+    //     size_[1] / 2
165
+    // );
166
+}
167
+
168
+inline bool GLBackendWxWidgets::key(std::string const & key) const
169
+{
170
+    (void)key;
171
+    return false;
172
+}
173
+inline bool GLBackendWxWidgets::button(int button) const
174
+{
175
+    (void)button;
176
+    return false;
177
+}
178
+
179
+
180
+/// Guards
181
+
182
+#endif
183
+#endif
0 184
new file mode 100644
... ...
@@ -0,0 +1,284 @@
1
+/// Guards
2
+
3
+
4
+#ifdef GLBACKEND_WXWIDGETS
5
+
6
+
7
+/// Includes
8
+
9
+
10
+#include <glbackend_wxwidgets.hpp>
11
+
12
+#include <array>
13
+#include <sstream>
14
+#include <string>
15
+#include <utility>
16
+#include <vector>
17
+
18
+#include <glbase.hpp>
19
+#include <glbackend.hpp>
20
+
21
+// #include <wx/wx.h>
22
+#include <wx/app.h>
23
+#include <wx/defs.h>
24
+#include <wx/display.h>
25
+#include <wx/event.h>
26
+#include <wx/frame.h>
27
+#include <wx/glcanvas.h>
28
+#include <wx/init.h>
29
+#include <wx/platinfo.h>
30
+#include <wx/string.h>
31
+#include <wx/tbarbase.h>
32
+#include <wx/toplevel.h>
33
+#include <wx/utils.h>
34
+#include <wx/version.h>
35
+#include <wx/versioninfo.h>
36
+#include <wx/vidmode.h>
37
+#include <wx/window.h>
38
+#include <wx/windowid.h>
39
+
40
+// NOLINTNEXTLINE
41
+#define STR_EXCEPTION GLBase::Exception
42
+#include <str.hpp>
43
+
44
+
45
+
46
+namespace
47
+{
48
+class Canvas : public wxGLCanvas
49
+{
50
+
51
+public:
52
+
53
+    Canvas(
54
+        wxWindow      * parent,
55
+        wxWindowID      id,
56
+        int     const * attribs,
57
+        wxPoint const & pos,
58
+        wxSize  const & size
59
+    )
60
+    :
61
+        wxGLCanvas(parent, id, attribs, pos, size)
62
+    {}
63
+
64
+protected:
65
+
66
+    // TODO(rcrnstn): Implement wxWidgets event handling.
67
+    // void paint_(wxPaintEvent& event);
68
+    // void size_(wxSizeEvent& event);
69
+    // void char_(wxKeyEvent& event);
70
+    // void mouse_event_(wxMouseEvent& event);
71
+    // void exit_(wxCommandEvent& event);
72
+    wxDECLARE_EVENT_TABLE(); // NOLINT
73
+
74
+};
75
+}
76
+
77
+
78
+// TODO(rcrnstn): Implement wxWidgets event handling.
79
+#pragma GCC diagnostic push
80
+#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
81
+wxBEGIN_EVENT_TABLE(Canvas, wxGLCanvas) // NOLINT
82
+//     EVT_PAINT(Canvas::paint_)
83
+wxEND_EVENT_TABLE() // NOLINT
84
+#pragma GCC diagnostic pop
85
+
86
+
87
+GLBackendWxWidgets::GLBackendWxWidgets(
88
+    std::string const & title,
89
+    std::array<int, 2>  size,
90
+    std::array<int, 2>  version,
91
+    int                 samples,
92
+    bool                fullscreen,
93
+    bool                transparent
94
+)
95
+:
96
+    GLBackend(),
97
+    app_       {nullptr},
98
+    frame_     {nullptr},
99
+    canvas_    {nullptr},
100
+    context_   {nullptr},
101
+    event_loop_{},
102
+    time_      {0.0F},
103
+    locked_    {false}
104
+{
105
+    // Backend init
106
+    if (!wxApp::GetInstance())
107
+    {
108
+        wxDISABLE_DEBUG_SUPPORT();
109
+
110
+        wxApp::SetInitializerFunction([]()
111
+        {
112
+            // cppcheck-suppress cstyleCast
113
+            return (wxAppConsole *)new wxApp;
114
+        });
115
+
116
+        auto argc = int{0};
117
+        auto argv = std::array<char *, 1>{{nullptr}};
118
+        if (!wxEntryStart(argc, &argv[0]))
119
+            STR_THROW("Failed to initialize wxWidgets.");
120
+
121
+        app_ = wxApp::GetInstance();
122
+        app_->OnInit();
123
+    }
124
+
125
+    // Window options
126
+    auto frame_style = long{0}; // NOLINT
127
+    frame_style |= wxDEFAULT_FRAME_STYLE; // NOLINT
128
+    frame_style &= ~(wxRESIZE_BORDER | wxMAXIMIZE_BOX); // NOLINT
129
+    frame_style |= wxWANTS_CHARS; // NOLINT
130
+    if (transparent)
131
+    {
132
+        frame_style &= ~wxDEFAULT_FRAME_STYLE; // NOLINT
133
+        frame_style |= wxSTAY_ON_TOP; // NOLINT
134
+    }
135
+    if (size[0] == 0 || size[1] == 0)
136
+    {
137
+        auto const mode = wxDisplay(0).GetCurrentMode();
138
+        size[0] = mode.GetWidth();
139
+        size[1] = mode.GetHeight();
140
+    }
141
+    size_ = size;
142
+
143
+    // Context options
144
+    auto canvas_attribs = std::vector<int>{};
145
+    //NOLINTNEXTLINE
146
+    #define GLBACKEND_WXWIDGETS_CANVAS_ATTRIBS_INSERT_(...) \
147
+        canvas_attribs.insert(canvas_attribs.end(), __VA_ARGS__);
148
+    GLBACKEND_WXWIDGETS_CANVAS_ATTRIBS_INSERT_({
149
+        WX_GL_RGBA,
150
+        WX_GL_DOUBLEBUFFER,
151
+        WX_GL_MIN_RED, 8,
152
+        WX_GL_MIN_GREEN, 8,
153
+        WX_GL_MIN_BLUE, 8,
154
+        WX_GL_MIN_ALPHA, 8,
155
+        WX_GL_DEPTH_SIZE, 24,
156
+        WX_GL_STENCIL_SIZE, 8,
157
+    })
158
+    if (samples)
159
+        GLBACKEND_WXWIDGETS_CANVAS_ATTRIBS_INSERT_({
160
+            WX_GL_SAMPLE_BUFFERS, 1,
161
+            WX_GL_SAMPLES, samples,
162
+        })
163
+    if (version[0])
164
+        GLBACKEND_WXWIDGETS_CANVAS_ATTRIBS_INSERT_({
165
+            WX_GL_MAJOR_VERSION, version[0],
166
+        })
167
+    if (version[1])
168
+        GLBACKEND_WXWIDGETS_CANVAS_ATTRIBS_INSERT_({
169
+            WX_GL_MINOR_VERSION, version[1],
170
+        })
171
+    if ((version[0] == 3 && version[1] >= 2) || version[0] >= 4) // NOLINT
172
+        GLBACKEND_WXWIDGETS_CANVAS_ATTRIBS_INSERT_({
173
+            WX_GL_CORE_PROFILE,
174
+        })
175
+    GLBACKEND_WXWIDGETS_CANVAS_ATTRIBS_INSERT_({
176
+        0
177
+    });
178
+
179
+    try
180
+    {
181
+        // Frame
182
+        frame_ = new wxFrame(); // NOLINT
183
+        if (transparent)
184
+            frame_->SetBackgroundStyle(wxBG_STYLE_TRANSPARENT);
185
+        frame_->Create(
186
+            nullptr,
187
+            wxID_ANY,
188
+            title,
189
+            wxDefaultPosition,
190
+            wxDefaultSize,
191
+            frame_style
192
+        );
193
+        frame_->SetClientSize(size[0], size[1]);
194
+        if (fullscreen)
195
+            frame_->ShowFullScreen(true);
196
+        else
197
+            frame_->Show(true);
198
+
199
+        // Canvas
200
+        canvas_ = new Canvas{ // NOLINT
201
+            frame_,
202
+            wxID_ANY,
203
+            &canvas_attribs[0],
204
+            wxDefaultPosition,
205
+            {size[0], size[1]},
206
+        };
207
+
208
+        // Context
209
+        context_ = new wxGLContext(canvas_); // NOLINT
210
+        events(); // NOLINT
211
+        context_->SetCurrent(*canvas_);
212
+        init_();
213
+
214
+        // Lock
215
+        if (fullscreen)
216
+            lock(true);
217
+    }
218
+    catch (...)
219
+    {
220
+        destroy_();
221
+        throw;
222
+    }
223
+}
224
+
225
+
226
+GLBackendWxWidgets::~GLBackendWxWidgets()
227
+{
228
+    destroy_();
229
+}
230
+
231
+
232
+std::string GLBackendWxWidgets::debug_info() const
233
+{
234
+    auto ostream = std::ostringstream{};
235
+
236
+    auto version_compiled = wxGetLibraryVersionInfo().GetVersionString();
237
+    auto version_linked   = wxString(wxVERSION_STRING);
238
+    auto os               = wxGetOsDescription();
239
+    auto port             = wxPlatformInfo::Get().GetPortIdName();
240
+    debug_info_(ostream, "wxWidgets", {
241
+        {"VERSION_COMPILED", version_compiled.ToStdString()},
242
+        {"VERSION_LINKED",   version_linked  .ToStdString()},
243
+        {"OS",               os              .ToStdString()},
244
+        {"PORT",             port            .ToStdString()},
245
+    });
246
+
247
+    ostream << GLBackend::debug_info();
248
+
249
+    return ostream.str();
250
+}
251
+
252
+
253
+// TODO(rcrnstn): Implement wxWidgets event handling.
254
+// void GLBackendWxWidgets::paint_(wxPaintEvent& WXUNUSED(event))
255
+// {
256
+//     wxPaintDC paint_dc(this);
257
+//     if (render_)
258
+//         render_();
259
+// }
260
+
261
+
262
+void GLBackendWxWidgets::destroy_()
263
+{
264
+    delete context_;
265
+    if (canvas_)
266
+    {
267
+        lock(false);
268
+        canvas_->Destroy();
269
+    }
270
+    if (frame_)
271
+        frame_->Destroy();
272
+    events(); // NOLINT
273
+    if (app_)
274
+    {
275
+        app_->OnExit();
276
+        wxEntryCleanup();
277
+    }
278
+}
279
+
280
+
281
+/// Guards
282
+
283
+
284
+#endif
0 285
new file mode 100644
... ...
@@ -0,0 +1,52 @@
1
+#ifdef GLBACKEND_WXWIDGETS
2
+
3
+
4
+// #include <wx/wx.h>
5
+#include <wx/app.h>
6
+#include <wx/defs.h>
7
+#include <wx/event.h>
8
+#include <wx/frame.h>
9
+#include <wx/window.h>
10
+
11
+
12
+class App : public wxApp
13
+{
14
+public:
15
+    bool OnInit() override
16
+    {
17
+        // The default close event handler for wxFrame destroys the frame using
18
+        // Destroy().
19
+        // https://docs.wxwidgets.org/3.0/overview_windowdeletion.html#overview_windowdeletion_default
20
+        auto * frame = new wxFrame(nullptr, wxID_ANY, "baseline_wdwidgets");
21
+        if (!frame)
22
+            return false;
23
+        frame->Show(true);
24
+        return true;
25
+    }
26
+    void OnIdle(wxIdleEvent & event)
27
+    {
28
+        (void)event;
29
+        // Apparently, we only trigger LeakSanitizer if we handle a few
30
+        // messages.
31
+        auto static count = 0;
32
+        if (count++ == 3)
33
+            GetTopWindow()->Close();
34
+    }
35
+    wxDECLARE_EVENT_TABLE(); //NOLINT
36
+};
37
+
38
+
39
+#pragma GCC diagnostic push
40
+#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
41
+wxBEGIN_EVENT_TABLE(App, wxApp) // NOLINT
42
+    EVT_IDLE(App::OnIdle) // NOLINT
43
+wxEND_EVENT_TABLE() // NOLINT
44
+#pragma GCC diagnostic pop
45
+
46
+
47
+wxIMPLEMENT_APP(App);
48
+
49
+
50
+#else
51
+int main() {}
52
+#endif
0 53
new file mode 100644
... ...
@@ -0,0 +1,17 @@
1
+#ifdef GLBACKEND_WXWIDGETS
2
+
3
+
4
+#include <glbackend_wxwidgets.hpp>
5
+
6
+#include "common/run.hpp"
7
+
8
+
9
+int main()
10
+{
11
+    GLBACKEND_TESTS_COMMON_RUN(GLBackendWxWidgets)
12
+}
13
+
14
+
15
+#else
16
+int main() {}
17
+#endif