/* * Created by Brett on 28/11/23. * Licensed under GNU General Public License V3.0 * See LICENSE file for license detail */ #include #include #include #include #include void error_callback(int error, const char* description) { BLT_ERROR("GLFW Error (%d): %s", error, description); std::abort(); } namespace blt::gfx { // because we aren't meant to have multiple GLFW windows (especially with GLAD) we will keep the window as global state.1 struct { /* GLFW Window Object */ GLFWwindow* window = nullptr; /* BLT internal input state manager, handles keyboard/mouse allocations + states */ input_manager inputManager; /* stores any drag and dropped paths for processing */ std::queue pendingPaths; /* current width and height of the window */ std::int32_t width = 0; std::int32_t height = 0; } window_state; void create_callbacks() { /* Setup keyboard callback */ glfwSetKeyCallback(window_state.window, [](GLFWwindow* window, int key, int scancode, int action, int mods) { if (key < 0 || key == GLFW_KEY_UNKNOWN) return; KEY_STATE state; switch (action) { case GLFW_PRESS: state = KEY_STATE::PRESS; break; case GLFW_REPEAT: state = KEY_STATE::REPEAT; break; default: state = KEY_STATE::RELEASE; } window_state.inputManager.key(key) = state; }); /* Setup mouse button callback */ glfwSetMouseButtonCallback(window_state.window, [](GLFWwindow* window, int button, int action, int mods) { if (button < 0) return; MOUSE_STATE state; switch (action) { case GLFW_PRESS: state = MOUSE_STATE::PRESS; break; default: state = MOUSE_STATE::RELEASE; break; } window_state.inputManager.mouse(button) = state; }); /* Setup mouse cursor callback */ glfwSetCursorPosCallback(window_state.window, [](GLFWwindow* window, double x, double y) { window_state.inputManager.updateMousePos(x, y); }); /* Setup mouse scroll callback */ glfwSetScrollCallback(window_state.window, [](GLFWwindow* window, double x, double s) { window_state.inputManager.updateScroll(s); }); /* Setup drop input callback */ glfwSetDropCallback(window_state.window, [](GLFWwindow* window, int count, const char** paths) { for (int i = 0; i < count; i++) window_state.pendingPaths.emplace(paths[i]); }); } void init(const window_data& data) { /* -- Setup Error Callback -- */ glfwSetErrorCallback(error_callback); BLT_ASSERT(glfwInit() && "Unable to init GLFW. Aborting."); /* -- Setup Window Context -- */ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, data.context.GL_MAJOR); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, data.context.GL_MINOR); glfwWindowHint(GLFW_DOUBLEBUFFER, data.context.DOUBLE_BUFFER); glfwWindowHint(GLFW_OPENGL_PROFILE, data.context.GL_PROFILE); /* -- Create the Window -- */ window_state.window = glfwCreateWindow(data.width, data.height, data.title.c_str(), nullptr, nullptr); BLT_ASSERT(window_state.window && "Unable to create GLFW window."); /* -- Set Window Specifics + OpenGL -- */ glfwMakeContextCurrent(window_state.window); glfwSwapInterval(data.sync_interval); gladLoadGL(glfwGetProcAddress); create_callbacks(); /* -- Call User Provided post-window-init function -- */ data.init(); /* -- General Loop -- */ while (!glfwWindowShouldClose(window_state.window)) { /* -- Get the current framebuffer size, update the global width/height state, along with OpenGL viewport -- */ glfwGetFramebufferSize(window_state.window, &window_state.width, &window_state.height); glViewport(0, 0, window_state.width, window_state.height); /* -- Call user update function -- */ data.update(window_state.width, window_state.height); /* -- Update GLFW state -- */ glfwSwapBuffers(window_state.window); glfwPollEvents(); } } void cleanup() { glfwDestroyWindow(window_state.window); glfwTerminate(); } double getMouseX() { return window_state.inputManager.mouseX; } double getMouseY() { return window_state.inputManager.mouseY; } double getMouseDX() { return window_state.inputManager.deltaX; } double getMouseDY() { return window_state.inputManager.deltaY; } void lockCursor() { glfwSetInputMode(window_state.window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); } void unlockCursor() { glfwSetInputMode(window_state.window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); } bool isCursorLocked() { return glfwGetInputMode(window_state.window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED; } bool isCursorInWindow() { return glfwGetWindowAttrib(window_state.window, GLFW_HOVERED); } void setRawInput(bool state) { if (glfwRawMouseMotionSupported()) glfwSetInputMode(window_state.window, GLFW_RAW_MOUSE_MOTION, state ? GLFW_TRUE : GLFW_FALSE); } bool isRawInput() { return glfwGetInputMode(window_state.window, GLFW_RAW_MOUSE_MOTION); } void setClipboard(const std::string& str) { glfwSetClipboardString(window_state.window, str.c_str()); } std::string getClipboard() { return glfwGetClipboardString(window_state.window); } bool isMousePressed(int button) { return window_state.inputManager.isMousePressed(button); } bool isKeyPressed(int key) { return window_state.inputManager.isKeyPressed(key); } }