431 lines
12 KiB
C++
431 lines
12 KiB
C++
/*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include "WindowGLFW.hpp"
|
|
#include "Base/Logger.hpp"
|
|
#include <GLFW/glfw3.h>
|
|
|
|
namespace OpenVulkano::GLFW
|
|
{
|
|
WindowGLFW::WindowGLFW(OpenVulkano::GLFW::InputProviderGLFW& inputProvider)
|
|
: inputProvider(inputProvider)
|
|
{}
|
|
|
|
WindowGLFW::~WindowGLFW() noexcept
|
|
{
|
|
if (window)
|
|
{
|
|
Close();
|
|
}
|
|
}
|
|
|
|
GLFWmonitor* WindowGLFW::GetCurrentMonitor() const
|
|
{
|
|
int monitorCount = 0;
|
|
GLFWmonitor** monitors = glfwGetMonitors(&monitorCount);
|
|
for (int i = 0; i < monitorCount; i++)
|
|
{
|
|
int posX, posY, sizeX, sizeY;
|
|
glfwGetMonitorWorkarea(monitors[i], &posX, &posY, &sizeX, &sizeY);
|
|
if (windowConfig.position.x >= posX && windowConfig.position.x < posX + sizeX &&
|
|
windowConfig.position.y >= posY && windowConfig.position.y < posY + sizeY)
|
|
{
|
|
return monitors[i];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
GLFWmonitor* WindowGLFW::GetTargetMonitor() const
|
|
{
|
|
if (windowConfig.windowMode == FULLSCREEN)
|
|
{
|
|
// TODO add config to control used display
|
|
return GetPrimaryMonitor();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void WindowGLFW::Create()
|
|
{
|
|
glfwWindowHint(GLFW_DECORATED, (~windowConfig.windowMode) & 1);
|
|
//TODO handle full screen resolutions
|
|
window = glfwCreateWindow(windowConfig.size.x, windowConfig.size.y, windowConfig.title.c_str(), GetTargetMonitor(), nullptr);
|
|
if (!window) return;
|
|
glfwSetWindowUserPointer(window, this);
|
|
RegisterCallbacks();
|
|
}
|
|
|
|
void WindowGLFW::RegisterCallbacks() const
|
|
{
|
|
glfwSetDropCallback(window, DropCallback);
|
|
glfwSetFramebufferSizeCallback(window, ResizeCallback);
|
|
glfwSetWindowFocusCallback(window, FocusCallback);
|
|
glfwSetWindowRefreshCallback(window, RefreshCallback);
|
|
glfwSetWindowIconifyCallback(window, MinimizeCallback);
|
|
glfwSetWindowPosCallback(window, WindowMoveCallback);
|
|
glfwSetWindowCloseCallback(window, CloseCallback);
|
|
|
|
// Input Callbacks
|
|
glfwSetKeyCallback(window, KeyboardCallback);
|
|
glfwSetMouseButtonCallback(window, MouseButtonCallback);
|
|
glfwSetCursorPosCallback(window, MouseMoveCallback);
|
|
glfwSetScrollCallback(window, MouseScrollCallback);
|
|
glfwSetCursorEnterCallback(window, MouseEnterExitCallback);
|
|
|
|
// Set mouse visibility
|
|
}
|
|
|
|
void WindowGLFW::SetMouseVisibility(bool hideMouse)
|
|
{
|
|
glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, hideMouse);
|
|
glfwSetInputMode(window, GLFW_CURSOR, hideMouse ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL);
|
|
|
|
}
|
|
|
|
GLFWmonitor* WindowGLFW::GetPrimaryMonitor()
|
|
{
|
|
return glfwGetPrimaryMonitor();
|
|
}
|
|
|
|
std::vector<GLFWmonitor*> WindowGLFW::GetMonitors()
|
|
{
|
|
int count;
|
|
GLFWmonitor** monitorsArray = glfwGetMonitors(&count);
|
|
std::vector<GLFWmonitor*> monitors;
|
|
monitors.reserve(count);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
monitors[i] = monitorsArray[i];
|
|
}
|
|
return monitors;
|
|
}
|
|
|
|
void WindowGLFW::Init(RenderAPI::RenderApi renderApi)
|
|
{
|
|
if (renderApi == RenderAPI::Vulkan) glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
|
Create();
|
|
if (!window)
|
|
{
|
|
throw WindowInitFailedException("Failed to initialize window");
|
|
}
|
|
if (renderApi != RenderAPI::Vulkan) MakeCurrentThread();
|
|
Logger::WINDOW->info("GLFW Window created (id: {0})", GetWindowId());
|
|
}
|
|
|
|
void WindowGLFW::Close()
|
|
{
|
|
glfwDestroyWindow(window);
|
|
window = nullptr;
|
|
Logger::WINDOW->info("GLFW Window destroyed (id: {0})", GetWindowId());
|
|
}
|
|
|
|
void WindowGLFW::Present() const
|
|
{
|
|
glfwSwapBuffers(window);
|
|
}
|
|
|
|
void WindowGLFW::Show()
|
|
{
|
|
glfwShowWindow(window);
|
|
}
|
|
|
|
void WindowGLFW::Hide()
|
|
{
|
|
glfwHideWindow(window);
|
|
}
|
|
|
|
void WindowGLFW::SetTitle(const std::string& title)
|
|
{
|
|
windowConfig.title = title;
|
|
if (window)
|
|
{
|
|
glfwSetWindowTitle(window, title.c_str());
|
|
}
|
|
}
|
|
|
|
void WindowGLFW::SetSize(uint32_t width, uint32_t height)
|
|
{
|
|
windowConfig.size.x = width;
|
|
windowConfig.size.y = height;
|
|
if (window)
|
|
{
|
|
glfwSetWindowSize(window, width, height);
|
|
}
|
|
}
|
|
|
|
void WindowGLFW::SetPosition(int posX, int posY)
|
|
{
|
|
windowConfig.position.x = posX;
|
|
windowConfig.position.y = posY;
|
|
if (window)
|
|
{
|
|
glfwSetWindowPos(window, posX, posY);
|
|
}
|
|
}
|
|
|
|
Math::Vector2ui WindowGLFW::GetSize()
|
|
{
|
|
Math::Vector2i size;
|
|
glfwGetWindowSize(window, &size.x, &size.y);
|
|
return size;
|
|
}
|
|
|
|
Math::Vector2i WindowGLFW::GetPosition()
|
|
{
|
|
Math::Vector2i pos;
|
|
glfwGetWindowPos(window, &pos.x, &pos.y);
|
|
return pos;
|
|
}
|
|
|
|
void WindowGLFW::SetSizeLimits(int minWidth, int minHeight, int maxWidth, int maxHeight)
|
|
{
|
|
minWidth = (minWidth < 0) ? GLFW_DONT_CARE : minWidth;
|
|
minHeight = (minHeight < 0) ? GLFW_DONT_CARE : minHeight;
|
|
maxWidth = (maxWidth < 0) ? GLFW_DONT_CARE : maxWidth;
|
|
maxHeight = (maxHeight < 0) ? GLFW_DONT_CARE : maxHeight;
|
|
glfwSetWindowSizeLimits(window, minWidth, minHeight, maxWidth, maxHeight);
|
|
}
|
|
|
|
void WindowGLFW::MakeCurrentThread()
|
|
{
|
|
glfwMakeContextCurrent(window);
|
|
}
|
|
|
|
void WindowGLFW::SetWindowMode(WindowMode windowMode)
|
|
{
|
|
if (windowMode == this->windowConfig.windowMode) return; // Nothing to change here
|
|
if (!window) return; // Window not yet created
|
|
this->windowConfig.windowMode = windowMode;
|
|
uint32_t sizeX = 0, sizeY = 0;
|
|
int posX = 0, posY = 0;
|
|
GLFWmonitor* monitor = nullptr;
|
|
glfwWindowHint(GLFW_DECORATED, (~windowMode) & 1);
|
|
if (windowMode == WINDOWED || windowMode == BORDERLESS)
|
|
{
|
|
sizeX = windowConfig.size.x;
|
|
sizeY = windowConfig.size.y;
|
|
posX = windowConfig.position.x;
|
|
posY = windowConfig.position.y;
|
|
monitor = nullptr;
|
|
if (windowMode == WINDOWED)
|
|
{
|
|
Logger::WINDOW->info("Switching to Windowed mode");
|
|
}
|
|
else
|
|
{
|
|
Logger::WINDOW->info("Switching to Borderless Windowed mode");
|
|
}
|
|
}
|
|
else
|
|
{ // Fullscreen
|
|
windowConfig.position = GetPosition(); // Backup current window position
|
|
monitor = GetCurrentMonitor();
|
|
if (!monitor) monitor = GetPrimaryMonitor();
|
|
if (!monitor) return; // We don't have a monitor to set the fullscreen window to
|
|
const GLFWvidmode* videoMode = glfwGetVideoMode(monitor ? monitor : GetPrimaryMonitor());
|
|
sizeX = videoMode->width;
|
|
sizeY = videoMode->height;
|
|
if (windowMode == FULLSCREEN)
|
|
{
|
|
// TODO find video mode that best matches user settings
|
|
Logger::WINDOW->info("Switching to Fullscreen mode");
|
|
}
|
|
else if (windowMode == BORDERLESS_FULLSCREEN)
|
|
{
|
|
int sX, sY;
|
|
glfwGetMonitorWorkarea(monitor, &posX, &posY, &sX, &sY);
|
|
monitor = nullptr;
|
|
Logger::WINDOW->info("Switching to Borderless Fullscreen mode");
|
|
}
|
|
}
|
|
glfwSetWindowMonitor(this->window, monitor, posX, posY, sizeX, sizeY, GLFW_DONT_CARE);
|
|
}
|
|
|
|
void WindowGLFW::SetWindowHandler(IWindowHandler* handler)
|
|
{
|
|
this->handler = handler;
|
|
}
|
|
|
|
vk::SurfaceKHR WindowGLFW::CreateSurface(const vk::Instance& instance, const vk::AllocationCallbacks* pAllocator)
|
|
{
|
|
VkSurfaceKHR rawSurface;
|
|
const auto result = static_cast<vk::Result>(glfwCreateWindowSurface(static_cast<VkInstance>(instance), window, reinterpret_cast<const VkAllocationCallbacks*>(pAllocator), &rawSurface));
|
|
return rawSurface;//createResultValue(result, rawSurface, "vk::CommandBuffer::begin");
|
|
}
|
|
|
|
std::vector<std::string> WindowGLFW::GetRequiredInstanceExtensions()
|
|
{
|
|
return GetVulkanRequiredInstanceExtensions();
|
|
}
|
|
|
|
void WindowGLFW::OnResize(const uint32_t newWidth, const uint32_t newHeight)
|
|
{
|
|
Logger::WINDOW->debug("Window (id: {0}) resized (width: {1}, height: {2})", GetWindowId(), newWidth, newHeight);
|
|
handler->OnWindowResize(this, newWidth, newHeight);
|
|
}
|
|
|
|
void WindowGLFW::OnMinimize()
|
|
{
|
|
Logger::WINDOW->debug("Window (id: {0}) minimized", GetWindowId());
|
|
handler->OnWindowMinimize(this);
|
|
}
|
|
|
|
void WindowGLFW::OnRestore()
|
|
{
|
|
Logger::WINDOW->debug("Window (id: {0}) restored", GetWindowId());
|
|
handler->OnWindowRestore(this);
|
|
}
|
|
|
|
void WindowGLFW::OnFocusLost()
|
|
{
|
|
Logger::WINDOW->debug("Window (id: {0}) focus lost", GetWindowId());
|
|
handler->OnWindowFocusLost(this);
|
|
}
|
|
|
|
void WindowGLFW::OnFocusGained()
|
|
{
|
|
Logger::WINDOW->debug("Window (id: {0}) focus gained", GetWindowId());
|
|
handler->OnWindowFocusGained(this);
|
|
}
|
|
|
|
void WindowGLFW::OnMove(const int posX, const int posY)
|
|
{
|
|
Logger::WINDOW->debug("Window (id: {0}) moved (x: {1}, y: {2})", GetWindowId(), posX, posY);
|
|
if (windowConfig.windowMode == WINDOWED || windowConfig.windowMode == BORDERLESS)
|
|
{ // Don't save window position for fullscreen
|
|
windowConfig.position.x = posX;
|
|
windowConfig.position.y = posY;
|
|
}
|
|
handler->OnWindowMove(this, posX, posY);
|
|
}
|
|
|
|
void WindowGLFW::OnClose()
|
|
{
|
|
Logger::WINDOW->debug("Window (id: {0}) closed", GetWindowId());
|
|
handler->OnWindowClose(this);
|
|
}
|
|
|
|
void WindowGLFW::OnKeyEvent(int key, int scanCode, int action, int mods)
|
|
{
|
|
if (key == GLFW_KEY_ENTER && action == GLFW_PRESS && mods == GLFW_MOD_ALT)
|
|
{
|
|
WindowMode newMode = FULLSCREEN;
|
|
switch (windowConfig.windowMode)
|
|
{
|
|
case WINDOWED: newMode = FULLSCREEN; break;
|
|
case BORDERLESS: newMode = BORDERLESS_FULLSCREEN; break;
|
|
case FULLSCREEN: newMode = WINDOWED; break;
|
|
case BORDERLESS_FULLSCREEN: newMode = BORDERLESS; break;
|
|
}
|
|
SetWindowMode(newMode);
|
|
}
|
|
switch (action)
|
|
{
|
|
case GLFW_PRESS:// OnKeyPressed(key, mods);
|
|
Logger::INPUT->debug("Key Pressed: {0} Mod: {1} ScanCode: {2}", key, mods, scanCode);
|
|
break;
|
|
case GLFW_RELEASE:// OnKeyReleased(key, mods);
|
|
Logger::INPUT->debug("Key Released: {0} Mod: {1} ScanCode: {2}", key, mods, scanCode);
|
|
break;
|
|
case GLFW_REPEAT:
|
|
Logger::INPUT->debug("Key Repeat: {0} Mod: {1} ScanCode: {2}", key, mods, scanCode);
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
WindowGLFW* WindowGLFW::GetWindow(GLFWwindow* window)
|
|
{
|
|
return static_cast<WindowGLFW*>(glfwGetWindowUserPointer(window));
|
|
}
|
|
|
|
void WindowGLFW::KeyboardCallback(GLFWwindow* window, int key, int scanCode, int action, int mods)
|
|
{
|
|
GetWindow(window)->OnKeyEvent(key, scanCode, action, mods);
|
|
const auto windowInstance = GetWindow(window);
|
|
windowInstance->inputProvider.SetKeyboardKey(windowInstance, key, scanCode, action, mods);
|
|
}
|
|
|
|
void WindowGLFW::MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
|
|
{
|
|
GetWindow(window)->inputProvider.SetMouseButton(button, action != GLFW_RELEASE);
|
|
}
|
|
|
|
void WindowGLFW::MouseMoveCallback(GLFWwindow* window, double posX, double posY)
|
|
{
|
|
GetWindow(window)->inputProvider.mouse.UpdatePosition(posX, posY);
|
|
}
|
|
|
|
void WindowGLFW::MouseScrollCallback(GLFWwindow* window, double xOffset, double yOffset)
|
|
{
|
|
GetWindow(window)->inputProvider.mouse.UpdateWheel(xOffset, yOffset);
|
|
}
|
|
|
|
void WindowGLFW::MouseEnterExitCallback(GLFWwindow* window, int entered)
|
|
{
|
|
const auto windowInstance = GetWindow(window);
|
|
windowInstance->inputProvider.MouseEnterExitWindow(windowInstance);
|
|
Logger::INPUT->trace("Mouse enter/exit: {}", entered);
|
|
}
|
|
|
|
void WindowGLFW::ResizeCallback(GLFWwindow* window, int width, int height)
|
|
{
|
|
GetWindow(window)->OnResize(width, height);
|
|
}
|
|
|
|
void WindowGLFW::FocusCallback(GLFWwindow* window, const int focused)
|
|
{
|
|
if (focused == GLFW_TRUE)
|
|
GetWindow(window)->OnFocusGained();
|
|
else
|
|
GetWindow(window)->OnFocusLost();
|
|
}
|
|
|
|
void WindowGLFW::MinimizeCallback(GLFWwindow* window, const int minimized)
|
|
{
|
|
if (minimized == GLFW_TRUE)
|
|
GetWindow(window)->OnMinimize();
|
|
else
|
|
GetWindow(window)->OnRestore();
|
|
}
|
|
|
|
void WindowGLFW::RefreshCallback(GLFWwindow* window)
|
|
{
|
|
//TODO is there really anything to do? or is it ok if the window is only redrawn on the next frame?
|
|
}
|
|
|
|
void WindowGLFW::WindowMoveCallback(GLFWwindow* window, const int posX, const int posY)
|
|
{
|
|
GetWindow(window)->OnMove(posX, posY);
|
|
}
|
|
|
|
void WindowGLFW::CloseCallback(GLFWwindow* window)
|
|
{
|
|
GetWindow(window)->OnClose();
|
|
}
|
|
|
|
void WindowGLFW::DropCallback(GLFWwindow* window, const int count, const char** paths)
|
|
{
|
|
//TODO something useful
|
|
}
|
|
|
|
std::vector<std::string> WindowGLFW::GetVulkanRequiredInstanceExtensions()
|
|
{
|
|
std::vector<std::string> result;
|
|
uint32_t count = 0;
|
|
const char** names = glfwGetRequiredInstanceExtensions(&count);
|
|
if (names && count)
|
|
{
|
|
for (uint32_t i = 0; i < count; ++i)
|
|
{
|
|
result.emplace_back(names[i]);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
} |