iwa/source/window.cpp

321 lines
8.4 KiB
C++

#include "iwa/window.hpp"
#include <SDL_vulkan.h>
#include "iwa/log.hpp"
#include "iwa/instance.hpp"
namespace iwa
{
inline constexpr const char* WINDOW_DATA_NAME = "iwa_window";
IWA_REGISTER_CLASS(Window)
namespace
{
Window* gLastEventWindow = nullptr;
Window* getWindowFromEvent(Uint32 windowID) noexcept
{
SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID);
Window* iwaWindow = static_cast<Window*>(SDL_GetWindowData(sdlWindow, WINDOW_DATA_NAME));
if (iwaWindow != nullptr)
{
gLastEventWindow = iwaWindow;
}
return gLastEventWindow;
}
void handleWindowEvent(const SDL_Event& event)
{
Window* iwaWindow = getWindowFromEvent(event.window.windowID);
if (iwaWindow == nullptr) {
return;
}
switch (event.window.event)
{
case SDL_WINDOWEVENT_FOCUS_GAINED:
iwaWindow->focusGained.emit();
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
iwaWindow->focusLost.emit();
break;
case SDL_WINDOWEVENT_ENTER:
iwaWindow->mouseEntered.emit();
break;
case SDL_WINDOWEVENT_LEAVE:
iwaWindow->mouseLeft.emit();
break;
case SDL_WINDOWEVENT_CLOSE:
iwaWindow->closeRequested.emit();
break;
}
}
void handleKeyEvent(const SDL_Event& sdlEvent)
{
Window* iwaWindow = getWindowFromEvent(sdlEvent.key.windowID);
if (iwaWindow == nullptr) {
return;
}
const KeyEvent event = {
.keyCode = static_cast<KeyCode>(sdlEvent.key.keysym.sym),
.scanCode = static_cast<ScanCode>(sdlEvent.key.keysym.scancode),
.modifiers = {
.leftShift = (sdlEvent.key.keysym.mod & KMOD_LSHIFT) != 0,
.rightShift = (sdlEvent.key.keysym.mod & KMOD_RSHIFT) != 0,
.leftCtrl = (sdlEvent.key.keysym.mod & KMOD_LCTRL) != 0,
.rightCtrl = (sdlEvent.key.keysym.mod & KMOD_RCTRL) != 0,
.leftAlt = (sdlEvent.key.keysym.mod & KMOD_LALT) != 0,
.rightAlt = (sdlEvent.key.keysym.mod & KMOD_RALT) != 0,
.leftMeta = (sdlEvent.key.keysym.mod & KMOD_LGUI) != 0,
.rightMeta = (sdlEvent.key.keysym.mod & KMOD_RGUI) != 0,
},
.down = sdlEvent.type == SDL_KEYDOWN,
.repeat = sdlEvent.key.repeat > 0
};
iwaWindow->keyChanged.emit(event);
}
void handleMouseMotion(const SDL_Event& sdlEvent)
{
Window* iwaWindow = getWindowFromEvent(sdlEvent.motion.windowID);
if (iwaWindow == nullptr) {
return;
}
const MouseMoveEvent event = {
.relativeX = sdlEvent.motion.xrel,
.relativeY = sdlEvent.motion.yrel,
.absoluteX = sdlEvent.motion.x,
.absoluteY = sdlEvent.motion.y,
.warped = false // TODO?
};
iwaWindow->mouseMoved.emit(event);
}
void handleMouseButtonEvent(const SDL_Event& sdlEvent)
{
Window* iwaWindow = getWindowFromEvent(sdlEvent.button.windowID);
if (iwaWindow == nullptr) {
return;
}
const MouseButtonEvent event = {
.button = static_cast<MouseButton>(sdlEvent.button.button),
.clicks = sdlEvent.button.clicks,
.down = sdlEvent.type == SDL_MOUSEBUTTONDOWN
};
iwaWindow->mouseButtonChanged.emit(event);
}
void handleMouseWheelEvent(const SDL_Event& sdlEvent)
{
Window* iwaWindow = getWindowFromEvent(sdlEvent.wheel.windowID);
if (iwaWindow == nullptr) {
return;
}
const MouseWheelEvent event = {
.relativeX = sdlEvent.wheel.x,
.relativeY = sdlEvent.wheel.y
};
iwaWindow->mouseScrolled.emit(event);
}
void handleTextInputEvent(const SDL_Event& sdlEvent)
{
Window* iwaWindow = getWindowFromEvent(sdlEvent.text.windowID);
if (iwaWindow == nullptr) {
return;
}
const TextInputEvent event = {
.text = sdlEvent.text.text
};
iwaWindow->textEntered.emit(event);
}
void handleEvent(const SDL_Event& event)
{
switch (event.type)
{
case SDL_WINDOWEVENT:
handleWindowEvent(event);
break;
case SDL_KEYDOWN:
case SDL_KEYUP:
handleKeyEvent(event);
break;
case SDL_MOUSEMOTION:
handleMouseMotion(event);
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
handleMouseButtonEvent(event);
break;
case SDL_MOUSEWHEEL:
handleMouseWheelEvent(event);
break;
case SDL_TEXTINPUT:
handleTextInputEvent(event);
break;
}
}
mijin::Task<> c_sdlLoop(ObjectPtr<Instance> instance)
{
while (!instance->isQuitRequested())
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
handleEvent(event);
}
co_await mijin::c_suspend();
}
// SDL_Quit();
co_return;
}
void initSDL(Instance& instance)
{
static bool sdlInited = false;
if (sdlInited) {
return;
}
sdlInited = true;
if (SDL_Init(0) != 0)
{
logAndDie("Error initializing SDL: {}.", SDL_GetError());
}
SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0");
instance.getMainTaskLoop().addTask(c_sdlLoop(instance.getPointer()));
}
}
Window::Window(ObjectPtr<Instance> owner, const WindowCreationArgs& args) : super_t(std::move(owner))
{
initSDL(*getOwner());
const Uint32 flags = SDL_WINDOW_VULKAN
| (args.flags.hidden ? SDL_WINDOW_HIDDEN : 0)
| (args.flags.resizable ? SDL_WINDOW_RESIZABLE : 0)
| (args.flags.borderless ? SDL_WINDOW_BORDERLESS : 0)
| (args.flags.alwayOnTop ? SDL_WINDOW_ALWAYS_ON_TOP : 0)
| (args.flags.skipTaskbar ? SDL_WINDOW_UTILITY : 0);
mHandle = SDL_CreateWindow(
/* title = */ args.title.c_str(),
/* x = */ SDL_WINDOWPOS_CENTERED,
/* y = */ SDL_WINDOWPOS_CENTERED,
/* w = */ args.width,
/* h = */ args.height,
/* flags = */ flags
);
if (mHandle == nullptr)
{
logAndDie("Error creating SDL window: {}.", SDL_GetError());
}
SDL_SetWindowData(mHandle, WINDOW_DATA_NAME, this);
VkSurfaceKHR surface = VK_NULL_HANDLE;
if (!SDL_Vulkan_CreateSurface(mHandle, getOwner()->getVkHandle(), &surface))
{
logAndDie("Error creating Vulkan surface for SDL window: {}", SDL_GetError());
}
mSurface = surface;
getOwner()->windowCreated.emit(*this);
}
Window::~Window() noexcept
{
getOwner()->queueDelete([surface=mSurface, handle=mHandle, instance= getOwner()]
{
if (surface)
{
instance->getVkHandle().destroySurfaceKHR(surface);
}
if (handle)
{
SDL_DestroyWindow(handle);
}
});
}
bool Window::isVisible() const noexcept
{
return (SDL_GetWindowFlags(mHandle) & SDL_WINDOW_HIDDEN) == 0;
}
void Window::setVisible(bool visible) noexcept
{
if (visible) {
SDL_ShowWindow(mHandle);
}
else {
SDL_HideWindow(mHandle);
}
}
std::pair<int, int> Window::getSize() const noexcept
{
std::pair<int, int> size;
SDL_GetWindowSize(mHandle, &size.first, &size.second);
return size;
}
void Window::setSize(int width, int height) noexcept
{
SDL_SetWindowSize(mHandle, std::max(width, 1), std::max(height, 1));
}
std::pair<int, int> Window::getPosition() const noexcept
{
std::pair<int, int> position;
SDL_GetWindowPosition(mHandle, &position.first, &position.second);
return position;
}
void Window::setPosition(int xPos, int yPos) noexcept
{
SDL_SetWindowPosition(mHandle, xPos, yPos);
}
WindowBorder Window::getWindowBorder() const noexcept
{
WindowBorder windowBorder;
SDL_GetWindowBordersSize(mHandle, &windowBorder.top, &windowBorder.left, &windowBorder.bottom, &windowBorder.right);
return windowBorder;
}
bool Window::isFocused() const noexcept
{
return (SDL_GetWindowFlags(mHandle) & SDL_WINDOW_INPUT_FOCUS) != 0;
}
void Window::focus() noexcept
{
SDL_RaiseWindow(mHandle);
}
void Window::setMouseMode(MouseMode mouseMode) noexcept
{
switch (mouseMode)
{
case MouseMode::NORMAL:
SDL_SetRelativeMouseMode(SDL_FALSE);
SDL_SetWindowMouseGrab(mHandle, SDL_FALSE);
break;
case MouseMode::CAPTURED:
SDL_SetRelativeMouseMode(SDL_TRUE);
SDL_SetWindowMouseGrab(mHandle, SDL_TRUE);
break;
}
}
void Window::setModalFor(mijin::Optional<const Window&> parent) noexcept
{
SDL_SetWindowModalFor(mHandle, parent.empty() ? nullptr : parent->getSDLWindow());
}
}