319 lines
8.4 KiB
C++
319 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";
|
|
|
|
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());
|
|
}
|
|
} |