From fb692b80b4856884daaf3bea1fc45c3dabaac921 Mon Sep 17 00:00:00 2001 From: rexy712 Date: Tue, 22 Sep 2020 15:24:31 -0700 Subject: [PATCH] Add RAII window class --- include/graphics/window.hpp | 127 ++++++++++++++++ include/render.hpp | 10 +- src/graphics/window.cpp | 279 ++++++++++++++++++++++++++++++++++++ src/main.cpp | 11 +- src/render.cpp | 43 ++---- 5 files changed, 431 insertions(+), 39 deletions(-) create mode 100644 include/graphics/window.hpp create mode 100644 src/graphics/window.cpp diff --git a/include/graphics/window.hpp b/include/graphics/window.hpp new file mode 100644 index 0000000..2523c22 --- /dev/null +++ b/include/graphics/window.hpp @@ -0,0 +1,127 @@ +/** + This file is a part of our_dick + Copyright (C) 2020 rexy712 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +#ifndef OUR_DICK_GRAPHICS_WINDOW_HPP +#define OUR_DICK_GRAPHICS_WINDOW_HPP + +#include "gl_include.hpp" +#include "math/math.hpp" + +namespace graphics{ + + using namespace math; + + class window + { + public: + enum class mousemode : int{ + DISABLED, + HIDDEN, + NORMAL, + }; + private: + static constexpr int s_default_cver_maj = 3; + static constexpr int s_default_cver_min = 3; + + private: + GLFWwindow* m_window = nullptr; + char* m_title = nullptr; + int m_antialias_level = 0; + int m_refresh_rate = GLFW_DONT_CARE; + int m_swap_interval = 1; + + public: + window(int width, int height, const char* title); + window(int context_vmaj, int context_vmin, int width, int height, const char* title, int antialias = 0, int refresh = GLFW_DONT_CARE); + window(const window&); + window(window&&); + ~window(); + + window& operator=(const window&); + window& operator=(window&&); + + GLFWwindow* raw(); + const GLFWwindow* raw()const; + + operator GLFWwindow*(); + operator const GLFWwindow*()const; + + void make_current(); + void swap_buffers(); + + void destroy(); + + void set_size(const vec2&); + void set_size(int w, int h); + void set_width(int w); + void set_height(int h); + void set_title(const char* t); + void set_pos(const vec2&); + void set_pos(int x, int y); + void set_x(int x); + void set_y(int y); + + void set_visible(bool b = true); + void toggle_visible(); + void set_resizable(bool b = true); + void set_decorated(bool b = true); + void set_always_on_top(bool b = true); + void set_antialias(int level); + void set_refresh_rate(int rate); + void set_swap_interval(int i); + void set_should_close(bool b = true); + void set_mousemode(mousemode m); + + void set_key_callback(); + //TODO callbacks + void set_active(); + + + vec2 get_size()const; + int get_width()const; + int get_height()const; + + vec2 get_pos()const; + int get_posx()const; + int get_posy()const; + + vec2 get_context_version()const; + int get_context_vmaj()const; + int get_context_vmin()const; + int get_swap_interval()const; + + vec2 get_cursor_pos()const; + double get_cursor_posx()const; + double get_cursor_posy()const; + bool get_key(int key, int action)const; + + bool is_visible()const; + bool is_resizable()const; + bool is_decorated()const; + bool is_always_on_top()const; + bool is_fullscreen()const; + bool should_close()const; + mousemode get_mousemode()const; + + private: + void copy_title(const char* title, size_t titlelen); + }; + +} + +#endif diff --git a/include/render.hpp b/include/render.hpp index 7e5a71e..f7399bf 100644 --- a/include/render.hpp +++ b/include/render.hpp @@ -3,6 +3,7 @@ #include #include "graphics/gl_include.hpp" +#include "graphics/window.hpp" #define OPENGL_VERSION 400 #define GLSL_VERSION 400 @@ -11,12 +12,14 @@ class GLFWwindow; class render_manager { +private: + static inline bool s_initialized = false; private: using close_callback = void(*)(void); using input_callback = void(*)(GLFWwindow*, int, int, int, int); close_callback m_window_close_callback; input_callback m_input_handler; - GLFWwindow* m_main_window; + graphics::window m_main_window; public: enum renderer_error{ @@ -29,8 +32,9 @@ public: render_manager(const render_manager&) = delete; render_manager(render_manager&&) = delete; - render_manager(); - renderer_error init(int width, int height, const char* title); // Sets up the OpenGL environment + bool should_close()const; + + render_manager(int width, int height, const char* title); void update(); // Update the GL context and draw new frame void request_exit(); diff --git a/src/graphics/window.cpp b/src/graphics/window.cpp new file mode 100644 index 0000000..067a20c --- /dev/null +++ b/src/graphics/window.cpp @@ -0,0 +1,279 @@ +/** + This file is a part of our_dick + Copyright (C) 2020 rexy712 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +#include "graphics/window.hpp" + +#include //exchange, swap, pair +#include //strlen, strncpy + +namespace graphics{ + + window::window(int width, int height, const char* title): + window(s_default_cver_maj, s_default_cver_min, width, height, title){} + window::window(int cver_maj, int cver_min, int width, int height, const char* title, int antialias, int refresh): + m_antialias_level(antialias), + m_refresh_rate(refresh) + { + copy_title(title, strlen(title)); + + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); + glfwWindowHint(GLFW_FLOATING, GLFW_FALSE); + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + glfwWindowHint(GLFW_REFRESH_RATE, m_refresh_rate); + glfwWindowHint(GLFW_SAMPLES, m_antialias_level); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, cver_maj); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, cver_min); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + m_window = glfwCreateWindow(width, height, title, nullptr, nullptr); + } + window::window(const window& w): + m_antialias_level(w.m_antialias_level), + m_refresh_rate(w.m_refresh_rate), + m_swap_interval(w.m_swap_interval) + { + GLFWwindow* current_context = glfwGetCurrentContext(); + + copy_title(w.m_title, strlen(w.m_title)); + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, w.get_context_vmaj()); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, w.get_context_vmin()); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_RESIZABLE, w.is_resizable()); + glfwWindowHint(GLFW_DECORATED, w.is_decorated()); + glfwWindowHint(GLFW_FLOATING, w.is_always_on_top()); + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + glfwWindowHint(GLFW_REFRESH_RATE, w.m_refresh_rate); + glfwWindowHint(GLFW_SAMPLES, w.m_antialias_level); + + auto size = w.get_size(); + m_window = glfwCreateWindow(size.x(), size.y(), m_title, nullptr, nullptr); + make_current(); + set_swap_interval(w.m_swap_interval); + set_pos(w.get_pos()); + set_visible(w.is_visible()); + + glfwMakeContextCurrent(current_context); + } + window::window(window&& w): + m_window(std::exchange(w.m_window, nullptr)), + m_title(std::exchange(w.m_title, nullptr)), + m_antialias_level(w.m_antialias_level), + m_refresh_rate(w.m_refresh_rate), + m_swap_interval(w.m_swap_interval){} + window::~window(){ + destroy(); + } + + window& window::operator=(const window& w){ + return (*this = window(w)); + } + window& window::operator=(window&& w){ + std::swap(m_window, w.m_window); + m_antialias_level = w.m_antialias_level; + m_refresh_rate = w.m_refresh_rate; + m_swap_interval = w.m_swap_interval; + return *this; + } + GLFWwindow* window::raw(){ + return m_window; + } + const GLFWwindow* window::raw()const{ + return m_window; + } + + window::operator GLFWwindow*(){ + return m_window; + } + window::operator const GLFWwindow*()const{ + return m_window; + } + + + void window::make_current(){ + glfwMakeContextCurrent(m_window); + } + void window::swap_buffers(){ + glfwSwapBuffers(m_window); + } + + void window::destroy(){ + if(m_window) + glfwDestroyWindow(m_window); + m_window = nullptr; + + delete[] m_title; + m_title = nullptr; + } + + void window::set_size(const vec2& v){ + set_size(v.x(), v.y()); + } + void window::set_size(int w, int h){ + glfwSetWindowSize(m_window, w, h); + } + void window::set_width(int w){ + set_size(w, get_height()); + } + void window::set_height(int h){ + set_size(get_width(), h); + } + void window::set_title(const char* t){ + glfwSetWindowTitle(m_window, t); + } + void window::set_pos(const vec2& v){ + set_pos(v.x(), v.y()); + } + void window::set_pos(int x, int y){ + glfwSetWindowPos(m_window, x, y); + } + void window::set_x(int x){ + set_pos(x, get_posy()); + } + void window::set_y(int y){ + set_pos(get_posx(), y); + } + + void window::set_visible(bool b){ + if(b) + glfwShowWindow(m_window); + else + glfwHideWindow(m_window); + } + void window::toggle_visible(){ + if(is_visible()) + glfwHideWindow(m_window); + else + glfwShowWindow(m_window); + } + void window::set_resizable(bool b){ + glfwSetWindowAttrib(m_window, GLFW_RESIZABLE, b); + } + void window::set_decorated(bool b){ + glfwSetWindowAttrib(m_window, GLFW_DECORATED, b); + } + void window::set_always_on_top(bool b){ + glfwSetWindowAttrib(m_window, GLFW_FLOATING, b); + } + void window::set_antialias(int level){ + m_antialias_level = level; + } + void window::set_refresh_rate(int rate){ + m_refresh_rate = rate; + } + void window::set_swap_interval(int i){ + GLFWwindow* current_context = glfwGetCurrentContext(); + glfwMakeContextCurrent(m_window); + glfwSwapInterval(i); + glfwMakeContextCurrent(current_context); + } + + void window::set_should_close(bool b){ + glfwSetWindowShouldClose(m_window, b); + } +/* + void window::set_mousemode(mousemode m){ + //TODO + } +*/ + void window::set_active(){ + glfwMakeContextCurrent(m_window); + } + + + vec2 window::get_size()const{ + vec2 retval; + glfwGetWindowSize(m_window, &retval.x(), &retval.y()); + return retval; + } + int window::get_width()const{ + return get_size().x(); + } + int window::get_height()const{ + return get_size().y(); + } + + vec2 window::get_pos()const{ + vec2 retval; + glfwGetWindowPos(m_window, &retval.x(), &retval.y()); + return retval; + } + int window::get_posx()const{ + return get_pos().x(); + } + int window::get_posy()const{ + return get_pos().x(); + } + vec2 window::get_context_version()const{ + return vec2{get_context_vmaj(), get_context_vmin()}; + } + int window::get_context_vmaj()const{ + return glfwGetWindowAttrib(m_window, GLFW_CONTEXT_VERSION_MAJOR); + } + int window::get_context_vmin()const{ + return glfwGetWindowAttrib(m_window, GLFW_CONTEXT_VERSION_MINOR); + } + int window::get_swap_interval()const{ + return m_swap_interval; + } + + vec2 window::get_cursor_pos()const{ + vec2 retval; + glfwGetCursorPos(m_window, &retval.x(), &retval.y()); + return retval; + } + double window::get_cursor_posx()const{ + return get_cursor_pos().x(); + } + double window::get_cursor_posy()const{ + return get_cursor_pos().y(); + } + bool window::get_key(int key, int action)const{ + return glfwGetKey(m_window, key) == action; + } + + bool window::is_visible()const{ + return glfwGetWindowAttrib(m_window, GLFW_VISIBLE); + } + bool window::is_resizable()const{ + return glfwGetWindowAttrib(m_window, GLFW_RESIZABLE); + } + bool window::is_decorated()const{ + return glfwGetWindowAttrib(m_window, GLFW_DECORATED); + } + bool window::is_always_on_top()const{ + return glfwGetWindowAttrib(m_window, GLFW_FLOATING); + } + bool window::should_close()const{ + return glfwWindowShouldClose(m_window); + } +/* bool window::is_fullscreen()const{ + //TODO + } + auto window::get_mousemode()const -> mousemode{ + //TODO + } +*/ + void window::copy_title(const char* title, size_t titlelen){ + m_title = new char[titlelen + 1]; + strncpy(m_title, title, titlelen); + m_title[titlelen] = 0; + } +} diff --git a/src/main.cpp b/src/main.cpp index 10b219a..8b3e022 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,7 +30,6 @@ namespace{ bool running = true; - render_manager manager; } int get_player_input(){ @@ -103,10 +102,10 @@ void handle_window_close(){ running = false; } -void handle_input_events(GLFWwindow*, int key, int, int, int){ +void handle_input_events(GLFWwindow* window, int key, int, int, int){ debug_print("[II] Recieved keycode %d\n", key); if(key == 256) - manager.request_exit(); + glfwSetWindowShouldClose(window, GLFW_TRUE); } audio::mixchunk read_audio_file(const char* filename){ @@ -188,10 +187,11 @@ int main(){ }; srand(time(NULL)); + glfwInit(); //TODO: handle in a similar way as PortAudio library initialization game_state gs = {}; audio::mixer mix(audio::mixer::mode::STEREO, 1, 44100); - manager.init(640, 480, "Tic-Tac-Gugh"); + render_manager manager(640, 480, "Tic-Tac-Gugh"); manager.handle_window_close_event(handle_window_close); manager.handle_keypress_event(handle_input_events); @@ -213,7 +213,7 @@ int main(){ attrib.set_float_pointer(3, 3, 0); attrib.enable_array_mode(); - while(running){ + while(!manager.should_close()){ //render using program 'prog' with data from 'vao' prog.use(); vao.bind(); @@ -226,4 +226,5 @@ int main(){ game_turn(gs, get_player_input()); } t.join(); + //TODO: cannot call glfwTerminate before the window is closed } diff --git a/src/render.cpp b/src/render.cpp index 587a63b..f6d9d19 100644 --- a/src/render.cpp +++ b/src/render.cpp @@ -1,6 +1,7 @@ #include "graphics/gl_include.hpp" #include "render.hpp" #include "config.hpp" +#include "graphics/window.hpp" #include namespace{ @@ -11,45 +12,25 @@ namespace{ } -render_manager::render_manager() : +render_manager::render_manager(int width, int height, const char* title): m_window_close_callback(nullptr), m_input_handler(nullptr), - m_main_window(nullptr){} - -render_manager::renderer_error render_manager::init (int width, int height, const char* title){ - if(!glfwInit()){ - fprintf(stderr, "[EE] failed to initialize GLFW.\n"); - return RENDERER_INIT_ERROR; - } - - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - - m_main_window = glfwCreateWindow(width, height, title, nullptr, nullptr); - if(!m_main_window){ - fprintf(stderr, "[EE] Could not create window\n"); - return RENDERER_WINDOW_ERROR; - } - - glfwMakeContextCurrent(m_main_window); - if(gl3wInit()){ - fprintf(stderr, "[EE] failed to initialize OpenGL\n"); - return RENDERER_CONTEXT_ERROR; - } - + m_main_window(width, height, title) +{ + m_main_window.make_current(); + gl3wInit(); glViewport(0, 0, width, height); glClearColor(0, 0, 0, 1); - - debug_print("[DD] OpenGL %s, GLSL %s\n", glGetString(GL_VERSION), glGetString(GL_SHADING_LANGUAGE_VERSION)); - glfwSetFramebufferSizeCallback(m_main_window, handle_resize_event); + m_main_window.set_visible(true); +} - return RENDERER_OK; +bool render_manager::should_close()const{ + return m_main_window.should_close(); } void render_manager::update(){ - glfwSwapBuffers(m_main_window); + m_main_window.swap_buffers(); glClear(GL_COLOR_BUFFER_BIT); glfwPollEvents(); } @@ -57,5 +38,5 @@ void render_manager::update(){ void render_manager::request_exit(){ if(m_window_close_callback) m_window_close_callback(); - glfwSetWindowShouldClose(m_main_window, GLFW_TRUE); + m_main_window.set_should_close(true); }