Files
face_sdk/vulkan/framework/platform/glfw_window.cpp
T

416 lines
12 KiB
C++

/* Copyright (c) 2019-2025, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "glfw_window.h"
#include <unordered_map>
#include "common/error.h"
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h>
#include <fmt/format.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include "core/util/logging.hpp"
#include "platform/platform.h"
namespace vkb
{
namespace
{
void error_callback(int error, const char *description)
{
LOGE("GLFW Error (code {}): {}", error, description);
}
void window_close_callback(GLFWwindow *window)
{
glfwSetWindowShouldClose(window, GLFW_TRUE);
}
void window_size_callback(GLFWwindow *window, int width, int height)
{
if (auto platform = reinterpret_cast<Platform *>(glfwGetWindowUserPointer(window)))
{
platform->resize(width, height);
}
}
void window_focus_callback(GLFWwindow *window, int focused)
{
if (auto platform = reinterpret_cast<Platform *>(glfwGetWindowUserPointer(window)))
{
platform->set_focus(focused);
}
}
inline KeyCode translate_key_code(int key)
{
static const std::unordered_map<int, KeyCode> key_lookup =
{
{GLFW_KEY_SPACE, KeyCode::Space},
{GLFW_KEY_APOSTROPHE, KeyCode::Apostrophe},
{GLFW_KEY_COMMA, KeyCode::Comma},
{GLFW_KEY_MINUS, KeyCode::Minus},
{GLFW_KEY_PERIOD, KeyCode::Period},
{GLFW_KEY_SLASH, KeyCode::Slash},
{GLFW_KEY_0, KeyCode::_0},
{GLFW_KEY_1, KeyCode::_1},
{GLFW_KEY_2, KeyCode::_2},
{GLFW_KEY_3, KeyCode::_3},
{GLFW_KEY_4, KeyCode::_4},
{GLFW_KEY_5, KeyCode::_5},
{GLFW_KEY_6, KeyCode::_6},
{GLFW_KEY_7, KeyCode::_7},
{GLFW_KEY_8, KeyCode::_8},
{GLFW_KEY_9, KeyCode::_9},
{GLFW_KEY_SEMICOLON, KeyCode::Semicolon},
{GLFW_KEY_EQUAL, KeyCode::Equal},
{GLFW_KEY_A, KeyCode::A},
{GLFW_KEY_B, KeyCode::B},
{GLFW_KEY_C, KeyCode::C},
{GLFW_KEY_D, KeyCode::D},
{GLFW_KEY_E, KeyCode::E},
{GLFW_KEY_F, KeyCode::F},
{GLFW_KEY_G, KeyCode::G},
{GLFW_KEY_H, KeyCode::H},
{GLFW_KEY_I, KeyCode::I},
{GLFW_KEY_J, KeyCode::J},
{GLFW_KEY_K, KeyCode::K},
{GLFW_KEY_L, KeyCode::L},
{GLFW_KEY_M, KeyCode::M},
{GLFW_KEY_N, KeyCode::N},
{GLFW_KEY_O, KeyCode::O},
{GLFW_KEY_P, KeyCode::P},
{GLFW_KEY_Q, KeyCode::Q},
{GLFW_KEY_R, KeyCode::R},
{GLFW_KEY_S, KeyCode::S},
{GLFW_KEY_T, KeyCode::T},
{GLFW_KEY_U, KeyCode::U},
{GLFW_KEY_V, KeyCode::V},
{GLFW_KEY_W, KeyCode::W},
{GLFW_KEY_X, KeyCode::X},
{GLFW_KEY_Y, KeyCode::Y},
{GLFW_KEY_Z, KeyCode::Z},
{GLFW_KEY_LEFT_BRACKET, KeyCode::LeftBracket},
{GLFW_KEY_BACKSLASH, KeyCode::Backslash},
{GLFW_KEY_RIGHT_BRACKET, KeyCode::RightBracket},
{GLFW_KEY_GRAVE_ACCENT, KeyCode::GraveAccent},
{GLFW_KEY_ESCAPE, KeyCode::Escape},
{GLFW_KEY_ENTER, KeyCode::Enter},
{GLFW_KEY_TAB, KeyCode::Tab},
{GLFW_KEY_BACKSPACE, KeyCode::Backspace},
{GLFW_KEY_INSERT, KeyCode::Insert},
{GLFW_KEY_DELETE, KeyCode::DelKey},
{GLFW_KEY_RIGHT, KeyCode::Right},
{GLFW_KEY_LEFT, KeyCode::Left},
{GLFW_KEY_DOWN, KeyCode::Down},
{GLFW_KEY_UP, KeyCode::Up},
{GLFW_KEY_PAGE_UP, KeyCode::PageUp},
{GLFW_KEY_PAGE_DOWN, KeyCode::PageDown},
{GLFW_KEY_HOME, KeyCode::Home},
{GLFW_KEY_END, KeyCode::End},
{GLFW_KEY_CAPS_LOCK, KeyCode::CapsLock},
{GLFW_KEY_SCROLL_LOCK, KeyCode::ScrollLock},
{GLFW_KEY_NUM_LOCK, KeyCode::NumLock},
{GLFW_KEY_PRINT_SCREEN, KeyCode::PrintScreen},
{GLFW_KEY_PAUSE, KeyCode::Pause},
{GLFW_KEY_F1, KeyCode::F1},
{GLFW_KEY_F2, KeyCode::F2},
{GLFW_KEY_F3, KeyCode::F3},
{GLFW_KEY_F4, KeyCode::F4},
{GLFW_KEY_F5, KeyCode::F5},
{GLFW_KEY_F6, KeyCode::F6},
{GLFW_KEY_F7, KeyCode::F7},
{GLFW_KEY_F8, KeyCode::F8},
{GLFW_KEY_F9, KeyCode::F9},
{GLFW_KEY_F10, KeyCode::F10},
{GLFW_KEY_F11, KeyCode::F11},
{GLFW_KEY_F12, KeyCode::F12},
{GLFW_KEY_KP_0, KeyCode::KP_0},
{GLFW_KEY_KP_1, KeyCode::KP_1},
{GLFW_KEY_KP_2, KeyCode::KP_2},
{GLFW_KEY_KP_3, KeyCode::KP_3},
{GLFW_KEY_KP_4, KeyCode::KP_4},
{GLFW_KEY_KP_5, KeyCode::KP_5},
{GLFW_KEY_KP_6, KeyCode::KP_6},
{GLFW_KEY_KP_7, KeyCode::KP_7},
{GLFW_KEY_KP_8, KeyCode::KP_8},
{GLFW_KEY_KP_9, KeyCode::KP_9},
{GLFW_KEY_KP_DECIMAL, KeyCode::KP_Decimal},
{GLFW_KEY_KP_DIVIDE, KeyCode::KP_Divide},
{GLFW_KEY_KP_MULTIPLY, KeyCode::KP_Multiply},
{GLFW_KEY_KP_SUBTRACT, KeyCode::KP_Subtract},
{GLFW_KEY_KP_ADD, KeyCode::KP_Add},
{GLFW_KEY_KP_ENTER, KeyCode::KP_Enter},
{GLFW_KEY_KP_EQUAL, KeyCode::KP_Equal},
{GLFW_KEY_LEFT_SHIFT, KeyCode::LeftShift},
{GLFW_KEY_LEFT_CONTROL, KeyCode::LeftControl},
{GLFW_KEY_LEFT_ALT, KeyCode::LeftAlt},
{GLFW_KEY_RIGHT_SHIFT, KeyCode::RightShift},
{GLFW_KEY_RIGHT_CONTROL, KeyCode::RightControl},
{GLFW_KEY_RIGHT_ALT, KeyCode::RightAlt},
};
auto key_it = key_lookup.find(key);
if (key_it == key_lookup.end())
{
return KeyCode::Unknown;
}
return key_it->second;
}
inline KeyAction translate_key_action(int action)
{
if (action == GLFW_PRESS)
{
return KeyAction::Down;
}
else if (action == GLFW_RELEASE)
{
return KeyAction::Up;
}
else if (action == GLFW_REPEAT)
{
return KeyAction::Repeat;
}
return KeyAction::Unknown;
}
void key_callback(GLFWwindow *window, int key, int /*scancode*/, int action, int /*mods*/)
{
KeyCode key_code = translate_key_code(key);
KeyAction key_action = translate_key_action(action);
if (auto platform = reinterpret_cast<Platform *>(glfwGetWindowUserPointer(window)))
{
platform->input_event(KeyInputEvent{key_code, key_action});
}
}
inline MouseButton translate_mouse_button(int button)
{
if (button < GLFW_MOUSE_BUTTON_6)
{
return static_cast<MouseButton>(button);
}
return MouseButton::Unknown;
}
inline MouseAction translate_mouse_action(int action)
{
if (action == GLFW_PRESS)
{
return MouseAction::Down;
}
else if (action == GLFW_RELEASE)
{
return MouseAction::Up;
}
return MouseAction::Unknown;
}
void cursor_position_callback(GLFWwindow *window, double xpos, double ypos)
{
if (auto *platform = reinterpret_cast<Platform *>(glfwGetWindowUserPointer(window)))
{
platform->input_event(MouseButtonInputEvent{
MouseButton::Unknown,
MouseAction::Move,
static_cast<float>(xpos),
static_cast<float>(ypos)});
}
}
void mouse_button_callback(GLFWwindow *window, int button, int action, int /*mods*/)
{
MouseAction mouse_action = translate_mouse_action(action);
if (auto *platform = reinterpret_cast<Platform *>(glfwGetWindowUserPointer(window)))
{
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
platform->input_event(MouseButtonInputEvent{
translate_mouse_button(button),
mouse_action,
static_cast<float>(xpos),
static_cast<float>(ypos)});
}
}
} // namespace
GlfwWindow::GlfwWindow(Platform *platform, const Window::Properties &properties) :
Window(properties)
{
#if defined(VK_USE_PLATFORM_XLIB_KHR)
glfwInitHint(GLFW_X11_XCB_VULKAN_SURFACE, false);
#endif
if (!glfwInit())
{
throw std::runtime_error("GLFW couldn't be initialized.");
}
glfwSetErrorCallback(error_callback);
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
switch (properties.mode)
{
case Window::Mode::Fullscreen:
{
auto *monitor = glfwGetPrimaryMonitor();
const auto *mode = glfwGetVideoMode(monitor);
handle = glfwCreateWindow(mode->width, mode->height, properties.title.c_str(), monitor, NULL);
break;
}
case Window::Mode::FullscreenBorderless:
{
auto *monitor = glfwGetPrimaryMonitor();
const auto *mode = glfwGetVideoMode(monitor);
glfwWindowHint(GLFW_RED_BITS, mode->redBits);
glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits);
glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits);
glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate);
handle = glfwCreateWindow(mode->width, mode->height, properties.title.c_str(), monitor, NULL);
break;
}
case Window::Mode::FullscreenStretch:
{
throw std::runtime_error("Cannot support stretch mode on this platform.");
break;
}
default:
handle = glfwCreateWindow(properties.extent.width, properties.extent.height, properties.title.c_str(), NULL, NULL);
break;
}
resize(Extent{properties.extent.width, properties.extent.height});
if (!handle)
{
throw std::runtime_error("Couldn't create glfw window.");
}
glfwSetWindowUserPointer(handle, platform);
glfwSetWindowCloseCallback(handle, window_close_callback);
glfwSetWindowSizeCallback(handle, window_size_callback);
glfwSetWindowFocusCallback(handle, window_focus_callback);
glfwSetKeyCallback(handle, key_callback);
glfwSetCursorPosCallback(handle, cursor_position_callback);
glfwSetMouseButtonCallback(handle, mouse_button_callback);
glfwSetInputMode(handle, GLFW_STICKY_KEYS, 1);
glfwSetInputMode(handle, GLFW_STICKY_MOUSE_BUTTONS, 1);
}
GlfwWindow::~GlfwWindow()
{
glfwTerminate();
}
VkSurfaceKHR GlfwWindow::create_surface(vkb::core::InstanceC &instance)
{
return create_surface(instance.get_handle(), VK_NULL_HANDLE);
}
VkSurfaceKHR GlfwWindow::create_surface(VkInstance instance, VkPhysicalDevice)
{
if (instance == VK_NULL_HANDLE || !handle)
{
return VK_NULL_HANDLE;
}
VkSurfaceKHR surface;
VkResult errCode = glfwCreateWindowSurface(instance, handle, NULL, &surface);
if (errCode != VK_SUCCESS)
{
return nullptr;
}
return surface;
}
bool GlfwWindow::should_close()
{
return glfwWindowShouldClose(handle);
}
void GlfwWindow::process_events()
{
glfwPollEvents();
}
void GlfwWindow::close()
{
glfwSetWindowShouldClose(handle, GLFW_TRUE);
}
/// @brief It calculates the dpi factor using the density from GLFW physical size
/// <a href="https://www.glfw.org/docs/latest/monitor_guide.html#monitor_size">GLFW docs for dpi</a>
float GlfwWindow::get_dpi_factor() const
{
auto primary_monitor = glfwGetPrimaryMonitor();
auto vidmode = glfwGetVideoMode(primary_monitor);
int width_mm, height_mm;
glfwGetMonitorPhysicalSize(primary_monitor, &width_mm, &height_mm);
// As suggested by the GLFW monitor guide
static const float inch_to_mm = 25.0f;
static const float win_base_density = 96.0f;
auto dpi = static_cast<uint32_t>(vidmode->width / (width_mm / inch_to_mm));
auto dpi_factor = dpi / win_base_density;
return dpi_factor;
}
float GlfwWindow::get_content_scale_factor() const
{
int fb_width, fb_height;
glfwGetFramebufferSize(handle, &fb_width, &fb_height);
int win_width, win_height;
glfwGetWindowSize(handle, &win_width, &win_height);
// We could return a 2D result here instead of a scalar,
// but non-uniform scaling is very unlikely, and would
// require significantly more changes in the IMGUI integration
return static_cast<float>(fb_width) / win_width;
}
std::vector<const char *> GlfwWindow::get_required_surface_extensions() const
{
uint32_t glfw_extension_count{0};
const char **names = glfwGetRequiredInstanceExtensions(&glfw_extension_count);
return {names, names + glfw_extension_count};
}
} // namespace vkb