2022-09-02 11:06:59 +00:00
|
|
|
#include "window.hpp"
|
|
|
|
|
2022-09-17 00:22:35 +00:00
|
|
|
#include "log.hpp"
|
|
|
|
|
2022-09-02 11:06:59 +00:00
|
|
|
#include <iostream>
|
|
|
|
#include <stdexcept>
|
|
|
|
|
|
|
|
const uint64_t BILLION = 1000000000;
|
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
namespace engine {
|
|
|
|
|
2022-10-06 10:30:44 +00:00
|
|
|
Window::Window(const std::string& title, bool resizable) : m_title(title), m_resizable(resizable)
|
2022-10-02 15:34:51 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
// init SDL
|
|
|
|
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
|
|
|
|
const std::string errMsg("Unable to initialise SDL: " + std::string(SDL_GetError()));
|
|
|
|
if (SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "SDL error", errMsg.c_str(), NULL) != 0) {
|
|
|
|
std::cerr << errMsg << "\nAre you in a graphical environment?\n";
|
|
|
|
}
|
|
|
|
throw std::runtime_error(errMsg);
|
2022-09-02 11:06:59 +00:00
|
|
|
}
|
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
m_counterFreq = SDL_GetPerformanceFrequency();
|
|
|
|
m_startTime = getNanos();
|
|
|
|
m_lastFrameStamp = m_startTime - 1;
|
|
|
|
m_avgFpsStart = m_startTime;
|
2022-09-02 11:06:59 +00:00
|
|
|
|
2022-10-06 10:30:44 +00:00
|
|
|
Uint32 windowFlags = SDL_WINDOW_SHOWN;
|
|
|
|
|
|
|
|
#ifdef ENGINE_BUILD_VULKAN
|
|
|
|
windowFlags |= SDL_WINDOW_VULKAN;
|
|
|
|
#endif
|
2022-11-07 20:15:26 +00:00
|
|
|
#ifdef ENGINE_BUILD_OPENGL
|
|
|
|
windowFlags |= SDL_WINDOW_OPENGL;
|
|
|
|
#endif
|
2022-10-06 10:30:44 +00:00
|
|
|
|
|
|
|
if (m_resizable) {
|
|
|
|
windowFlags |= SDL_WINDOW_RESIZABLE;
|
|
|
|
}
|
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
// create the window
|
|
|
|
m_handle = SDL_CreateWindow(
|
2022-09-02 11:06:59 +00:00
|
|
|
m_title.c_str(),
|
|
|
|
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
|
|
|
static_cast<int>(m_winSize.x),
|
|
|
|
static_cast<int>(m_winSize.y),
|
2022-10-06 10:30:44 +00:00
|
|
|
windowFlags);
|
2022-10-02 15:34:51 +00:00
|
|
|
if (m_handle == NULL) {
|
|
|
|
SDL_Quit();
|
|
|
|
throw std::runtime_error("Unable to create window: " + std::string(SDL_GetError()));
|
|
|
|
}
|
2022-09-02 11:06:59 +00:00
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
// get window size
|
|
|
|
int winWidth, winHeight;
|
|
|
|
SDL_GetWindowSize(m_handle, &winWidth, &winHeight);
|
|
|
|
m_winSize.x = winWidth;
|
|
|
|
m_winSize.y = winHeight;
|
|
|
|
|
|
|
|
const int WINDOWED_MIN_WIDTH = 640;
|
|
|
|
const int WINDOWED_MIN_HEIGHT = 480;
|
|
|
|
SDL_SetWindowMinimumSize(m_handle, WINDOWED_MIN_WIDTH, WINDOWED_MIN_HEIGHT);
|
|
|
|
|
|
|
|
// onResize(m_winSize.x, m_winSize.y);
|
2022-09-02 11:06:59 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
Window::~Window()
|
|
|
|
{
|
2022-09-02 11:06:59 +00:00
|
|
|
SDL_DestroyWindow(m_handle);
|
|
|
|
SDL_Quit();
|
|
|
|
}
|
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
// private methods
|
2022-09-02 11:06:59 +00:00
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
void Window::onResize(Sint32 width, Sint32 height)
|
|
|
|
{
|
|
|
|
// get window size
|
|
|
|
m_winSize.x = static_cast<int>(width);
|
|
|
|
m_winSize.y = static_cast<int>(height);
|
2022-09-02 11:06:59 +00:00
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
m_justResized = true;
|
|
|
|
}
|
2022-09-02 11:06:59 +00:00
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
void Window::resetInputDeltas()
|
|
|
|
{
|
|
|
|
m_justResized = false;
|
2022-09-02 11:06:59 +00:00
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
m_keyboard.deltas.fill(ButtonDelta::SAME);
|
2022-09-02 11:06:59 +00:00
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
m_mouse.deltas.fill(ButtonDelta::SAME);
|
|
|
|
m_mouse.dx = 0;
|
|
|
|
m_mouse.dy = 0;
|
|
|
|
m_mouse.xscroll = 0.0f;
|
|
|
|
m_mouse.yscroll = 0.0f;
|
|
|
|
}
|
2022-09-02 11:06:59 +00:00
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
// TODO event methods (like callbacks)
|
2022-09-02 11:06:59 +00:00
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
void Window::onWindowEvent(SDL_WindowEvent& e)
|
|
|
|
{
|
2022-09-02 11:06:59 +00:00
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
switch (e.event) {
|
2022-09-02 11:06:59 +00:00
|
|
|
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
|
|
|
onResize(e.data1, e.data2);
|
|
|
|
break;
|
|
|
|
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
|
|
|
m_keyboardFocus = true;
|
|
|
|
break;
|
|
|
|
case SDL_WINDOWEVENT_FOCUS_LOST:
|
|
|
|
m_keyboardFocus = false;
|
|
|
|
break;
|
2022-10-02 15:34:51 +00:00
|
|
|
}
|
2022-09-02 11:06:59 +00:00
|
|
|
}
|
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
void Window::onKeyEvent(SDL_KeyboardEvent& e)
|
|
|
|
{
|
|
|
|
bool keyWasDown = m_keyboard.keys[e.keysym.scancode];
|
|
|
|
bool keyIsDown = (e.state == SDL_PRESSED);
|
|
|
|
m_keyboard.keys[e.keysym.scancode] = keyIsDown;
|
|
|
|
if (keyIsDown != keyWasDown) { // (if key was pressed or released)
|
|
|
|
m_keyboard.deltas[e.keysym.scancode] = keyIsDown ? ButtonDelta::PRESSED : ButtonDelta::RELEASED;
|
|
|
|
}
|
2022-09-02 11:06:59 +00:00
|
|
|
}
|
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
void Window::onMouseButtonEvent(SDL_MouseButtonEvent& e)
|
|
|
|
{
|
|
|
|
enum inputs::MouseButton button = inputs::MouseButton::M_INVALID;
|
|
|
|
switch (e.button) {
|
2022-09-02 11:06:59 +00:00
|
|
|
case SDL_BUTTON_LEFT:
|
|
|
|
button = inputs::MouseButton::M_LEFT;
|
|
|
|
break;
|
|
|
|
case SDL_BUTTON_MIDDLE:
|
|
|
|
button = inputs::MouseButton::M_MIDDLE;
|
|
|
|
break;
|
|
|
|
case SDL_BUTTON_RIGHT:
|
|
|
|
button = inputs::MouseButton::M_RIGHT;
|
|
|
|
break;
|
|
|
|
case SDL_BUTTON_X1:
|
|
|
|
button = inputs::MouseButton::M_X1;
|
|
|
|
break;
|
|
|
|
case SDL_BUTTON_X2:
|
|
|
|
button = inputs::MouseButton::M_X2;
|
|
|
|
break;
|
2022-10-02 15:34:51 +00:00
|
|
|
}
|
|
|
|
int buttonIndex = static_cast<int>(button);
|
|
|
|
bool buttonWasDown = m_mouse.buttons.at(buttonIndex);
|
|
|
|
bool buttonIsDown = (e.state == SDL_PRESSED);
|
|
|
|
m_mouse.buttons.at(buttonIndex) = buttonIsDown;
|
|
|
|
if (buttonIsDown != buttonWasDown) { // (if button was pressed or released)
|
|
|
|
// only sets delta if it hasn't already been set this frame (to detect very fast presses)
|
|
|
|
if (m_mouse.deltas[buttonIndex] == ButtonDelta::SAME) {
|
|
|
|
m_mouse.deltas[buttonIndex] = buttonIsDown ? ButtonDelta::PRESSED : ButtonDelta::RELEASED;
|
|
|
|
}
|
2022-09-02 11:06:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
void Window::onMouseMotionEvent(SDL_MouseMotionEvent& e)
|
|
|
|
{
|
|
|
|
m_mouse.x = e.x;
|
|
|
|
m_mouse.y = e.y;
|
|
|
|
m_mouse.dx = e.xrel;
|
|
|
|
m_mouse.dy = e.yrel;
|
|
|
|
}
|
2022-09-02 11:06:59 +00:00
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
void Window::onMouseWheelEvent(SDL_MouseWheelEvent& e)
|
|
|
|
{
|
|
|
|
if (e.direction == SDL_MOUSEWHEEL_NORMAL) {
|
|
|
|
m_mouse.xscroll = e.preciseX;
|
|
|
|
m_mouse.yscroll = e.preciseY;
|
|
|
|
}
|
|
|
|
else { // flipped
|
|
|
|
m_mouse.xscroll = -e.preciseX;
|
|
|
|
m_mouse.yscroll = -e.preciseY;
|
|
|
|
}
|
2022-09-02 11:06:59 +00:00
|
|
|
}
|
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
// public methods
|
2022-09-02 11:06:59 +00:00
|
|
|
|
2022-10-05 16:01:40 +00:00
|
|
|
SDL_Window* Window::getHandle() const
|
|
|
|
{
|
|
|
|
return m_handle;
|
|
|
|
}
|
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
std::string Window::getTitle() const
|
|
|
|
{
|
|
|
|
return m_title;
|
|
|
|
}
|
2022-09-02 11:06:59 +00:00
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
void Window::getInputAndEvents()
|
|
|
|
{
|
2022-09-02 11:06:59 +00:00
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
m_frames++;
|
|
|
|
uint64_t currentFrameStamp = getNanos();
|
|
|
|
m_lastFrameTime = currentFrameStamp - m_lastFrameStamp;
|
|
|
|
m_lastFrameStamp = currentFrameStamp;
|
2022-09-02 11:06:59 +00:00
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
resetInputDeltas();
|
2022-09-02 11:06:59 +00:00
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
// loop through all available events
|
|
|
|
SDL_Event e;
|
|
|
|
while (SDL_PollEvent(&e)) {
|
|
|
|
switch (e.type) {
|
2022-09-02 11:06:59 +00:00
|
|
|
|
|
|
|
case SDL_QUIT:
|
|
|
|
setCloseFlag();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_WINDOWEVENT:
|
|
|
|
onWindowEvent(e.window);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_KEYDOWN: // FALL THROUGH
|
|
|
|
case SDL_KEYUP:
|
|
|
|
onKeyEvent(e.key);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_MOUSEBUTTONDOWN: // FALL THROUGH
|
|
|
|
case SDL_MOUSEBUTTONUP:
|
|
|
|
onMouseButtonEvent(e.button);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_MOUSEMOTION:
|
|
|
|
onMouseMotionEvent(e.motion);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDL_MOUSEWHEEL:
|
|
|
|
onMouseWheelEvent(e.wheel);
|
|
|
|
break;
|
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
}
|
2022-09-02 11:06:59 +00:00
|
|
|
}
|
2022-10-02 15:34:51 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Window::setTitle(std::string title)
|
|
|
|
{
|
|
|
|
SDL_SetWindowTitle(m_handle, title.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Window::getWindowResized() const
|
|
|
|
{
|
|
|
|
return m_justResized;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Window::show()
|
|
|
|
{
|
|
|
|
SDL_ShowWindow(m_handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Window::hide()
|
|
|
|
{
|
|
|
|
SDL_HideWindow(m_handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Window::focus()
|
|
|
|
{
|
|
|
|
SDL_RaiseWindow(m_handle);
|
|
|
|
m_keyboardFocus = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Window::hasFocus() const
|
|
|
|
{
|
|
|
|
return m_keyboardFocus;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Window::setCloseFlag()
|
|
|
|
{
|
|
|
|
m_shouldClose = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Window::isRunning() const
|
|
|
|
{
|
|
|
|
return !m_shouldClose;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Window::setFullscreen(bool fullscreen, bool exclusive)
|
|
|
|
{
|
2022-11-11 16:18:22 +00:00
|
|
|
|
2022-10-06 10:30:44 +00:00
|
|
|
if (m_resizable) {
|
2022-11-11 16:18:22 +00:00
|
|
|
|
|
|
|
SDL_DisplayMode mode;
|
|
|
|
SDL_GetDesktopDisplayMode(SDL_GetWindowDisplayIndex(m_handle), &mode);
|
|
|
|
SDL_SetWindowDisplayMode(m_handle, &mode);
|
|
|
|
|
2022-10-06 10:30:44 +00:00
|
|
|
if (SDL_SetWindowFullscreen(m_handle, fullscreen ? (exclusive ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP) : 0) != 0) {
|
|
|
|
throw std::runtime_error("Unable to set window to fullscreen/windowed");
|
|
|
|
}
|
|
|
|
m_fullscreen = fullscreen;
|
|
|
|
if (fullscreen) {
|
2022-11-11 16:18:22 +00:00
|
|
|
|
2022-10-06 10:30:44 +00:00
|
|
|
int width, height;
|
|
|
|
SDL_GetWindowSize(m_handle, &width, &height);
|
|
|
|
onResize(width, height);
|
|
|
|
}
|
2022-10-02 15:34:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Window::toggleFullscreen()
|
|
|
|
{
|
2022-11-11 16:18:22 +00:00
|
|
|
setFullscreen(!m_fullscreen, true);
|
2022-10-02 15:34:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Window::isFullscreen() const
|
|
|
|
{
|
|
|
|
return m_fullscreen;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Window::setRelativeMouseMode(bool enabled)
|
|
|
|
{
|
|
|
|
m_mouse.captured = enabled;
|
|
|
|
int code = SDL_SetRelativeMouseMode(static_cast<SDL_bool>(enabled));
|
|
|
|
if (code != 0) {
|
|
|
|
throw std::runtime_error("Unable to set relative mouse mode");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Window::mouseCaptured()
|
|
|
|
{
|
|
|
|
return m_mouse.captured;
|
|
|
|
}
|
|
|
|
|
|
|
|
// getting input
|
|
|
|
|
|
|
|
bool Window::getKey(inputs::Key key) const
|
|
|
|
{
|
|
|
|
return m_keyboard.keys[static_cast<int>(key)];
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Window::getKeyPress(inputs::Key key) const
|
|
|
|
{
|
|
|
|
return m_keyboard.deltas[static_cast<int>(key)] == ButtonDelta::PRESSED;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Window::getKeyRelease(inputs::Key key) const
|
|
|
|
{
|
|
|
|
return m_keyboard.deltas[static_cast<int>(key)] == ButtonDelta::RELEASED;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO mouse input
|
|
|
|
|
|
|
|
bool Window::getButton(inputs::MouseButton button) const
|
|
|
|
{
|
|
|
|
return m_mouse.buttons[static_cast<int>(button)];
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Window::getButtonPress(inputs::MouseButton button) const
|
|
|
|
{
|
|
|
|
return m_mouse.deltas[static_cast<int>(button)] == ButtonDelta::PRESSED;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Window::getButtonRelease(inputs::MouseButton button) const
|
|
|
|
{
|
|
|
|
return m_mouse.deltas[static_cast<int>(button)] == ButtonDelta::RELEASED;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Window::getMouseX() const
|
|
|
|
{
|
|
|
|
return static_cast<int>(m_mouse.x);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Window::getMouseY() const
|
|
|
|
{
|
|
|
|
return static_cast<int>(m_mouse.y);
|
|
|
|
}
|
|
|
|
|
|
|
|
float Window::getMouseNormX() const
|
|
|
|
{
|
|
|
|
return ((float)m_mouse.x * 2.0f / (float)m_winSize.x) - 1.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
float Window::getMouseNormY() const
|
|
|
|
{
|
|
|
|
return ((float)m_mouse.y * -2.0f / (float)m_winSize.y) + 1.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Window::getMouseDX() const
|
|
|
|
{
|
|
|
|
return static_cast<int>(m_mouse.dx);
|
|
|
|
}
|
|
|
|
|
|
|
|
int Window::getMouseDY() const
|
|
|
|
{
|
|
|
|
return static_cast<int>(m_mouse.dy);
|
|
|
|
}
|
|
|
|
|
|
|
|
float Window::getMouseScrollX() const
|
|
|
|
{
|
|
|
|
return m_mouse.xscroll;
|
|
|
|
}
|
|
|
|
|
|
|
|
float Window::getMouseScrollY() const
|
|
|
|
{
|
|
|
|
return m_mouse.yscroll;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO game pad
|
|
|
|
|
|
|
|
// get timer value
|
|
|
|
uint64_t Window::getNanos() const
|
|
|
|
{
|
|
|
|
uint64_t count;
|
|
|
|
|
|
|
|
count = SDL_GetPerformanceCounter();
|
|
|
|
if (m_counterFreq == BILLION) {
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return count * (BILLION / m_counterFreq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t Window::getLastFrameStamp() const
|
|
|
|
{
|
|
|
|
return m_lastFrameStamp;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t Window::getFrameCount() const
|
|
|
|
{
|
|
|
|
return m_frames;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t Window::getStartTime() const
|
|
|
|
{
|
|
|
|
return m_startTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
float Window::dt() const
|
|
|
|
{
|
|
|
|
return (float)m_lastFrameTime / (float)BILLION;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t Window::getFPS() const
|
|
|
|
{
|
|
|
|
if (m_lastFrameTime == 0) return 0;
|
|
|
|
return BILLION / m_lastFrameTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t Window::getAvgFPS() const
|
|
|
|
{
|
|
|
|
uint64_t delta_t = getNanos() - m_avgFpsStart;
|
|
|
|
if (delta_t == 0) return 0;
|
|
|
|
return BILLION * (m_frames - m_avgFpsStartCount) / delta_t;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Window::resetAvgFPS()
|
|
|
|
{
|
|
|
|
m_avgFpsStart = getNanos();
|
|
|
|
m_avgFpsStartCount = getFrameCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Window::infoBox(const std::string& title, const std::string& msg)
|
|
|
|
{
|
|
|
|
if (isFullscreen() == false) {
|
|
|
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, title.c_str(), msg.c_str(), m_handle);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* STATIC METHODS */
|
|
|
|
|
|
|
|
// Display an error message box
|
|
|
|
void Window::errorBox(const std::string& message)
|
|
|
|
{
|
|
|
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Game Error", message.c_str(), NULL);
|
2022-09-02 11:06:59 +00:00
|
|
|
}
|
|
|
|
|
2022-10-02 15:34:51 +00:00
|
|
|
}
|