|
|
|
/*
|
|
|
|
* Copyright (C) 2022 Riyyi
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: MIT
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <csignal> // signal
|
|
|
|
|
|
|
|
#include "GLFW/glfw3.h"
|
|
|
|
#include "ruc/format/log.h"
|
|
|
|
#include "ruc/meta/assert.h"
|
|
|
|
|
|
|
|
#include "inferno/application.h"
|
|
|
|
#include "inferno/core.h"
|
|
|
|
#include "inferno/event/applicationevent.h"
|
|
|
|
#include "inferno/event/keyevent.h"
|
|
|
|
#include "inferno/event/mouseevent.h"
|
|
|
|
#include "inferno/io/input.h"
|
|
|
|
#include "inferno/keycodes.h"
|
|
|
|
#include "inferno/render/context.h"
|
|
|
|
#include "inferno/render/renderer.h"
|
|
|
|
#include "inferno/settings.h"
|
|
|
|
#include "inferno/window.h"
|
|
|
|
|
|
|
|
namespace Inferno {
|
|
|
|
|
|
|
|
unsigned char Window::s_windowCount = 0;
|
|
|
|
|
|
|
|
Window::Window()
|
|
|
|
{
|
|
|
|
m_properties = {
|
|
|
|
Settings::get().window.title,
|
|
|
|
Settings::get().window.width,
|
|
|
|
Settings::get().window.height,
|
|
|
|
Settings::get().window.fullscreen,
|
|
|
|
Settings::get().window.vsync,
|
|
|
|
};
|
|
|
|
|
|
|
|
this->initialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
Window::~Window()
|
|
|
|
{
|
|
|
|
this->destroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------
|
|
|
|
|
|
|
|
void Window::initialize()
|
|
|
|
{
|
|
|
|
std::string title = m_properties.title;
|
|
|
|
uint32_t width = m_properties.width;
|
|
|
|
uint32_t height = m_properties.height;
|
|
|
|
bool vsync = m_properties.vsync;
|
|
|
|
|
|
|
|
// Only init once
|
|
|
|
if (s_windowCount == 0) {
|
|
|
|
glfwInit();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set window properties
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
|
|
|
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
|
|
|
// glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
|
|
|
|
|
|
|
|
// Create GLFW window
|
|
|
|
m_window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
|
|
|
|
s_windowCount++;
|
|
|
|
VERIFY(m_window, "Failed to create GLFW window!");
|
|
|
|
|
|
|
|
// Set windowed/fullscreen/borderless
|
|
|
|
this->setWindowMonitor();
|
|
|
|
|
|
|
|
// Associate the wrapper to the window
|
|
|
|
glfwSetWindowUserPointer(m_window, this);
|
|
|
|
|
|
|
|
// Create graphics context
|
|
|
|
m_context = std::make_shared<Context>(m_window);
|
|
|
|
m_context->initialize();
|
|
|
|
|
|
|
|
// Set vsync, viewport
|
|
|
|
setVSync(vsync);
|
|
|
|
RenderCommand::setViewport(0, 0, width, height);
|
|
|
|
|
|
|
|
// Signal callbacks
|
|
|
|
signal(SIGINT, Window::signalCallback);
|
|
|
|
signal(SIGTERM, Window::signalCallback);
|
|
|
|
|
|
|
|
// Error callback
|
|
|
|
glfwSetErrorCallback([](int error, const char* description) {
|
|
|
|
ruc::error("GLFW Error: {}: {}", error, description);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Window close callback
|
|
|
|
glfwSetWindowCloseCallback(m_window, [](GLFWwindow* window) {
|
|
|
|
Window& w = *(Window*)glfwGetWindowUserPointer(window);
|
|
|
|
|
|
|
|
WindowCloseEvent event;
|
|
|
|
w.m_eventCallback(event);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Window resize callback
|
|
|
|
glfwSetWindowSizeCallback(m_window, [](GLFWwindow* window, int width, int height) {
|
|
|
|
Window& w = *(Window*)glfwGetWindowUserPointer(window);
|
|
|
|
|
|
|
|
w.m_properties.width = width;
|
|
|
|
w.m_properties.height = height;
|
|
|
|
|
|
|
|
WindowResizeEvent event(width, height);
|
|
|
|
w.m_eventCallback(event);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Keyboard callback
|
|
|
|
glfwSetKeyCallback(m_window, [](GLFWwindow* window, int key, int scanCode, int action, int mods) {
|
|
|
|
// Suppress unused warning
|
|
|
|
(void)scanCode;
|
|
|
|
(void)mods;
|
|
|
|
|
|
|
|
Window& w = *(Window*)glfwGetWindowUserPointer(window);
|
|
|
|
|
|
|
|
switch (action) {
|
|
|
|
case GLFW_PRESS: {
|
|
|
|
KeyPressEvent event(key);
|
|
|
|
w.m_eventCallback(event);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GLFW_RELEASE: {
|
|
|
|
KeyReleaseEvent event(key);
|
|
|
|
w.m_eventCallback(event);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GLFW_REPEAT: {
|
|
|
|
KeyRepeatEvent event(key);
|
|
|
|
w.m_eventCallback(event);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Mouse button callback
|
|
|
|
glfwSetMouseButtonCallback(m_window, [](GLFWwindow* window, int button, int action, int mods) {
|
|
|
|
// Suppress unused warning
|
|
|
|
(void)mods;
|
|
|
|
|
|
|
|
Window& w = *(Window*)glfwGetWindowUserPointer(window);
|
|
|
|
|
|
|
|
switch (action) {
|
|
|
|
case GLFW_PRESS: {
|
|
|
|
MouseButtonPressEvent event(button);
|
|
|
|
w.m_eventCallback(event);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GLFW_RELEASE: {
|
|
|
|
MouseButtonReleaseEvent event(button);
|
|
|
|
w.m_eventCallback(event);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Mouse position callback
|
|
|
|
glfwSetCursorPosCallback(m_window, [](GLFWwindow* window, double xPos, double yPos) {
|
|
|
|
Window& w = *(Window*)glfwGetWindowUserPointer(window);
|
|
|
|
|
|
|
|
MousePositionEvent event(xPos, yPos);
|
|
|
|
w.m_eventCallback(event);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Mouse scroll callback
|
|
|
|
glfwSetScrollCallback(m_window, [](GLFWwindow* window, double xOffset, double yOffset) {
|
|
|
|
Window& w = *(Window*)glfwGetWindowUserPointer(window);
|
|
|
|
|
|
|
|
MouseScrollEvent event(xOffset, yOffset);
|
|
|
|
w.m_eventCallback(event);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void Window::destroy()
|
|
|
|
{
|
|
|
|
m_context->destroy();
|
|
|
|
|
|
|
|
glfwDestroyWindow(m_window);
|
|
|
|
s_windowCount--;
|
|
|
|
|
|
|
|
if (s_windowCount == 0) {
|
|
|
|
glfwTerminate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Window::update()
|
|
|
|
{
|
|
|
|
glfwPollEvents();
|
|
|
|
|
|
|
|
// Capture cursor in window and hide it
|
|
|
|
if (!Input::isKeyPressed(keyCode("GLFW_KEY_LEFT_SUPER"))) {
|
|
|
|
glfwSetInputMode(m_window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
glfwSetInputMode(m_window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Window::render()
|
|
|
|
{
|
|
|
|
m_context->render();
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------
|
|
|
|
|
|
|
|
void Window::signalCallback(int signal)
|
|
|
|
{
|
|
|
|
Application::the().setStatus(signal);
|
|
|
|
|
|
|
|
if (signal == SIGINT || signal == SIGTERM) {
|
|
|
|
WindowCloseEvent e;
|
|
|
|
Application::the().getWindow().m_eventCallback(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Window::setWindowMonitor() const
|
|
|
|
{
|
|
|
|
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
|
|
|
int xPos = 0;
|
|
|
|
int yPos = 0;
|
|
|
|
unsigned int width = m_properties.width;
|
|
|
|
unsigned int height = m_properties.height;
|
|
|
|
int refresh = GLFW_DONT_CARE;
|
|
|
|
|
|
|
|
std::string fullscreen = m_properties.fullscreen;
|
|
|
|
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
|
|
|
|
|
|
|
|
if (fullscreen.compare("fullscreen") == 0) {
|
|
|
|
refresh = mode->refreshRate;
|
|
|
|
}
|
|
|
|
else if (fullscreen.compare("borderless") == 0) {
|
|
|
|
width = mode->width;
|
|
|
|
height = mode->height;
|
|
|
|
refresh = mode->refreshRate;
|
|
|
|
}
|
|
|
|
// Default window state is windowed
|
|
|
|
else {
|
|
|
|
monitor = nullptr;
|
|
|
|
// Put window in the center of the monitor
|
|
|
|
xPos = (mode->width - width) / 2;
|
|
|
|
yPos = (mode->height - height) / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
glfwSetWindowMonitor(m_window, monitor, xPos, yPos, width, height, refresh);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Window::setVSync(bool enabled)
|
|
|
|
{
|
|
|
|
enabled ? glfwSwapInterval(GL_TRUE) : glfwSwapInterval(GL_FALSE);
|
|
|
|
m_properties.vsync = enabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Window::setShouldClose(bool close) const
|
|
|
|
{
|
|
|
|
glfwSetWindowShouldClose(m_window, close ? GL_TRUE : GL_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Window::shouldClose() const
|
|
|
|
{
|
|
|
|
return glfwWindowShouldClose(m_window);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Inferno
|