diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 90169137..16aa60de 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -34,7 +34,6 @@ #include "util/listutil.hpp" #include "util/platform.hpp" #include "window/Camera.hpp" -#include "window/Events.hpp" #include "window/input.hpp" #include "window/Window.hpp" #include "world/Level.hpp" @@ -96,20 +95,34 @@ void Engine::initialize(CoreParameters coreParameters) { controller = std::make_unique(*this); if (!params.headless) { - if (!(input = Window::initialize(&settings.display))){ + auto [window, input] = display::initialize(&settings.display); + if (!window || !input){ throw initialize_error("could not initialize window"); } - time.set(Window::time()); + window->setFramerate(settings.display.framerate.get()); + + time.set(window->time()); if (auto icon = load_icon()) { icon->flipY(); - Window::setIcon(icon.get()); + window->setIcon(icon.get()); } + this->window = std::move(window); + this->input = std::move(input); + loadControls(); gui = std::make_unique(*this); if (ENGINE_DEBUG_BUILD) { menus::create_version_label(*gui); } + keepAlive(settings.display.fullscreen.observe( + [this](bool value) { + if (value != this->window->isFullscreen()) { + this->window->toggleFullscreen(); + } + }, + true + )); } audio::initialize(!params.headless, settings.audio); @@ -173,7 +186,7 @@ void Engine::updateHotkeys() { } void Engine::saveScreenshot() { - auto image = Window::takeScreenshot(); + auto image = window->takeScreenshot(); image->flipY(); io::path filename = paths.getNewScreenshotFile("png"); imageio::write(filename.string(), image.get()); @@ -198,26 +211,26 @@ void Engine::updateFrontend() { double delta = time.getDelta(); updateHotkeys(); audio::update(delta); - gui->act(delta, Viewport(Window::width, Window::height)); + gui->act(delta, Viewport(window->getSize())); screen->update(delta); gui->postAct(); } void Engine::nextFrame() { - Window::setFramerate( - Window::isIconified() && settings.display.limitFpsIconified.get() + window->setFramerate( + window->isIconified() && settings.display.limitFpsIconified.get() ? 20 : settings.display.framerate.get() ); - Window::swapBuffers(); + window->swapBuffers(); input->pollEvents(); } void Engine::renderFrame() { screen->draw(time.getDelta()); - Viewport viewport(Window::width, Window::height); - DrawContext ctx(nullptr, viewport, nullptr); + Viewport viewport(window->getSize()); + DrawContext ctx(nullptr, *window, nullptr); gui->draw(ctx, *assets); } @@ -250,7 +263,7 @@ void Engine::close() { scripting::close(); logger.info() << "scripting finished"; if (!params.headless) { - Window::terminate(); + window.reset(); logger.info() << "window closed"; } logger.info() << "engine finished"; @@ -469,7 +482,7 @@ void Engine::onWorldClosed() { void Engine::quit() { quitSignal = true; if (!isHeadless()) { - Window::setShouldClose(true); + window->setShouldClose(true); } } diff --git a/src/engine/Engine.hpp b/src/engine/Engine.hpp index 6d3487dd..8c046b66 100644 --- a/src/engine/Engine.hpp +++ b/src/engine/Engine.hpp @@ -17,6 +17,7 @@ #include #include +class Window; class Assets; class Level; class Screen; @@ -68,6 +69,7 @@ class Engine : public util::ObjectsKeeper { std::unique_ptr controller; std::unique_ptr cmd; std::unique_ptr network; + std::unique_ptr window; std::unique_ptr input; std::vector basePacks; std::unique_ptr gui; @@ -191,6 +193,10 @@ public: return *input; } + Window& getWindow() { + return *window; + } + network::Network& getNetwork() { return *network; } diff --git a/src/engine/Mainloop.cpp b/src/engine/Mainloop.cpp index 2de6291d..ecfd17bc 100644 --- a/src/engine/Mainloop.cpp +++ b/src/engine/Mainloop.cpp @@ -14,6 +14,7 @@ Mainloop::Mainloop(Engine& engine) : engine(engine) { void Mainloop::run() { auto& time = engine.getTime(); + auto& window = engine.getWindow(); engine.setLevelConsumer([this](auto level, int64_t localPlayer) { if (level == nullptr) { @@ -32,10 +33,10 @@ void Mainloop::run() { engine.setScreen(std::make_shared(engine)); logger.info() << "main loop started"; - while (!Window::isShouldClose()){ - time.update(Window::time()); + while (!window.isShouldClose()){ + time.update(window.time()); engine.updateFrontend(); - if (!Window::isIconified()) { + if (!window.isIconified()) { engine.renderFrame(); } engine.postUpdate(); diff --git a/src/frontend/LevelFrontend.cpp b/src/frontend/LevelFrontend.cpp index 1bc6e9e2..b796625c 100644 --- a/src/frontend/LevelFrontend.cpp +++ b/src/frontend/LevelFrontend.cpp @@ -12,27 +12,33 @@ #include "objects/Player.hpp" #include "voxels/Block.hpp" #include "world/Level.hpp" +#include "engine/Engine.hpp" LevelFrontend::LevelFrontend( + Engine& engine, Player* currentPlayer, LevelController* controller, - Assets& assets, const EngineSettings& settings ) : level(*controller->getLevel()), controller(controller), - assets(assets), + assets(*engine.getAssets()), contentCache(std::make_unique( level.content, assets, settings.graphics )) { assets.store( BlocksPreview::build( - *contentCache, assets, *level.content.getIndices() + engine.getWindow(), + *contentCache, + *engine.getAssets(), + *level.content.getIndices() ), "block-previews" ); + + auto& rassets = assets; controller->getBlocksController()->listenBlockInteraction( - [currentPlayer, controller, &assets](auto player, const auto& pos, const auto& def, BlockInteraction type) { + [currentPlayer, controller, &rassets](auto player, const auto& pos, const auto& def, BlockInteraction type) { const auto& level = *controller->getLevel(); auto material = level.content.findBlockMaterial(def.material); if (material == nullptr) { @@ -40,7 +46,7 @@ LevelFrontend::LevelFrontend( } if (type == BlockInteraction::step) { - auto sound = assets.get(material->stepsSound); + auto sound = rassets.get(material->stepsSound); glm::vec3 pos {}; auto soundsCamera = currentPlayer->currentCamera.get(); if (soundsCamera == currentPlayer->spCamera.get() || @@ -66,10 +72,10 @@ LevelFrontend::LevelFrontend( audio::Sound* sound = nullptr; switch (type) { case BlockInteraction::placing: - sound = assets.get(material->placeSound); + sound = rassets.get(material->placeSound); break; case BlockInteraction::destruction: - sound = assets.get(material->breakSound); + sound = rassets.get(material->breakSound); break; default: break; diff --git a/src/frontend/LevelFrontend.hpp b/src/frontend/LevelFrontend.hpp index b53d267a..da1f198a 100644 --- a/src/frontend/LevelFrontend.hpp +++ b/src/frontend/LevelFrontend.hpp @@ -5,6 +5,7 @@ class Level; class Assets; class Player; +class Engine; class ContentGfxCache; class LevelController; struct EngineSettings; @@ -12,13 +13,13 @@ struct EngineSettings; class LevelFrontend { Level& level; LevelController* controller; - const Assets& assets; + Assets& assets; std::unique_ptr contentCache; public: LevelFrontend( + Engine& engine, Player* currentPlayer, LevelController* controller, - Assets& assets, const EngineSettings& settings ); ~LevelFrontend(); diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index 36e140fd..f330b508 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -40,7 +40,6 @@ #include "voxels/Chunks.hpp" #include "voxels/GlobalChunks.hpp" #include "window/Camera.hpp" -#include "window/Events.hpp" #include "window/input.hpp" #include "window/Window.hpp" #include "world/Level.hpp" @@ -225,7 +224,8 @@ void Hud::cleanup() { } void Hud::processInput(bool visible) { - if (!Window::isFocused() && !menu.hasOpenPage() && !isInventoryOpen()) { + const auto& window = engine.getWindow(); + if (!window.isFocused() && !menu.hasOpenPage() && !isInventoryOpen()) { setPause(true); } const auto& bindings = input.getBindings(); @@ -343,10 +343,11 @@ void Hud::update(bool visible) { element.getNode()->setVisible(visible); } + const auto& windowSize = engine.getWindow().getSize(); glm::vec2 caSize = contentAccessPanel->getSize(); contentAccessPanel->setVisible(inventoryView != nullptr && showContentPanel); - contentAccessPanel->setSize(glm::vec2(caSize.x, Window::height)); - contentAccess->setMinSize(glm::vec2(1, Window::height)); + contentAccessPanel->setSize(glm::vec2(caSize.x, windowSize.y)); + contentAccess->setMinSize(glm::vec2(1, windowSize.y)); hotbarView->setVisible(visible && !(secondUI && !inventoryView)); if (visible) { diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index a61a1e81..c10f40c5 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -30,7 +30,6 @@ #include "util/stringutil.hpp" #include "voxels/Chunks.hpp" #include "window/Camera.hpp" -#include "window/Events.hpp" #include "window/Window.hpp" #include "world/Level.hpp" #include "world/World.hpp" @@ -64,7 +63,7 @@ LevelScreen::LevelScreen( ); frontend = std::make_unique( - player, controller.get(), assets, settings + engine, player, controller.get(), settings ); renderer = std::make_unique( engine, *frontend, *player @@ -162,11 +161,14 @@ void LevelScreen::saveWorldPreview() { Camera camera = *player->fpCamera; camera.setFov(glm::radians(70.0f)); - DrawContext pctx(nullptr, {Window::width, Window::height}, batch.get()); + DrawContext pctx(nullptr, engine.getWindow(), batch.get()); + + DrawContext ctx(&pctx, engine.getWindow(), batch.get()); + ctx.setViewport( + {static_cast(previewSize * 1.5), + static_cast(previewSize)} + ); - Viewport viewport(previewSize * 1.5, previewSize); - DrawContext ctx(&pctx, viewport, batch.get()); - renderer->draw(ctx, camera, false, true, 0.0f, *postProcessing); auto image = postProcessing->toImage(); image->flipY(); @@ -226,7 +228,12 @@ void LevelScreen::update(float delta) { playerController->update(delta, inputLocked ? nullptr : &engine.getInput()); } controller->update(glm::min(delta, 0.2f), paused); - playerController->postUpdate(delta, inputLocked ? nullptr : &engine.getInput(), paused); + playerController->postUpdate( + delta, + engine.getWindow().getSize().y, + inputLocked ? nullptr : &engine.getInput(), + paused + ); hud->update(hudVisible); @@ -239,8 +246,7 @@ void LevelScreen::update(float delta) { void LevelScreen::draw(float delta) { auto camera = playerController->getPlayer()->currentCamera; - Viewport viewport(Window::width, Window::height); - DrawContext ctx(nullptr, viewport, batch.get()); + DrawContext ctx(nullptr, engine.getWindow(), batch.get()); if (!hud->isPause()) { scripting::on_entities_render(engine.getTime().getDelta()); diff --git a/src/frontend/screens/MenuScreen.cpp b/src/frontend/screens/MenuScreen.cpp index d31af6ae..62699488 100644 --- a/src/frontend/screens/MenuScreen.cpp +++ b/src/frontend/screens/MenuScreen.cpp @@ -18,7 +18,8 @@ MenuScreen::MenuScreen(Engine& engine) : Screen(engine) { menu->reset(); menu->setPage("main"); - uicamera = std::make_unique(glm::vec3(), Window::height); + uicamera = + std::make_unique(glm::vec3(), engine.getWindow().getSize().y); uicamera->perspective = false; uicamera->flipped = true; } @@ -31,13 +32,14 @@ void MenuScreen::update(float delta) { void MenuScreen::draw(float delta) { auto assets = engine.getAssets(); - Window::clear(); - Window::setBgColor(glm::vec3(0.2f)); + display::clear(); + display::setBgColor(glm::vec3(0.2f)); - uint width = Window::width; - uint height = Window::height; + const auto& size = engine.getWindow().getSize(); + uint width = size.x; + uint height = size.y; - uicamera->setFov(Window::height); + uicamera->setFov(height); uicamera->setAspectRatio(width / static_cast(height)); auto uishader = assets->get("ui"); uishader->use(); @@ -49,7 +51,7 @@ void MenuScreen::draw(float delta) { batch->rect( 0, 0, width, height, 0, 0, 0, - UVRegion(0, 0, width/bg->getWidth(), height/bg->getHeight()), + UVRegion(0, 0, width / bg->getWidth(), height / bg->getHeight()), false, false, glm::vec4(1.0f) ); batch->flush(); diff --git a/src/graphics/core/DrawContext.cpp b/src/graphics/core/DrawContext.cpp index 936de95b..ef98b24d 100644 --- a/src/graphics/core/DrawContext.cpp +++ b/src/graphics/core/DrawContext.cpp @@ -25,10 +25,11 @@ static void set_blend_mode(BlendMode mode) { DrawContext::DrawContext( const DrawContext* parent, - Viewport viewport, + Window& window, Batch2D* g2d -) : parent(parent), - viewport(std::move(viewport)), +) : window(window), + parent(parent), + viewport({window.getSize()}), g2d(g2d), flushable(g2d) {} @@ -39,7 +40,7 @@ DrawContext::~DrawContext() { } while (scissorsCount--) { - Window::popScissor(); + window.popScissor(); } if (parent == nullptr) @@ -54,7 +55,7 @@ DrawContext::~DrawContext() { } } - Window::viewport( + glViewport( 0, 0, parent->viewport.getWidth(), parent->viewport.getHeight() @@ -100,7 +101,7 @@ DrawContext DrawContext::sub(Flushable* flushable) const { void DrawContext::setViewport(const Viewport& viewport) { this->viewport = viewport; - Window::viewport( + glViewport( 0, 0, viewport.getWidth(), viewport.getHeight() @@ -153,7 +154,7 @@ void DrawContext::setBlendMode(BlendMode mode) { } void DrawContext::setScissors(const glm::vec4& area) { - Window::pushScissor(area); + window.pushScissor(area); scissorsCount++; } diff --git a/src/graphics/core/DrawContext.hpp b/src/graphics/core/DrawContext.hpp index 6c20f37f..785fdc93 100644 --- a/src/graphics/core/DrawContext.hpp +++ b/src/graphics/core/DrawContext.hpp @@ -4,10 +4,12 @@ #include "Viewport.hpp" #include "typedefs.hpp" +class Window; class Batch2D; class Framebuffer; class DrawContext { + Window& window; const DrawContext* parent; Viewport viewport; Batch2D* g2d; @@ -20,7 +22,11 @@ class DrawContext { int scissorsCount = 0; float lineWidth = 1.0f; public: - DrawContext(const DrawContext* parent, Viewport viewport, Batch2D* g2d); + DrawContext( + const DrawContext* parent, + Window& window, + Batch2D* g2d + ); ~DrawContext(); Batch2D* getBatch2D() const; diff --git a/src/graphics/core/Viewport.cpp b/src/graphics/core/Viewport.cpp index 7419936d..9bb28d2d 100644 --- a/src/graphics/core/Viewport.cpp +++ b/src/graphics/core/Viewport.cpp @@ -4,6 +4,9 @@ Viewport::Viewport(uint width, uint height) : width(width), height(height) { } +Viewport::Viewport(const glm::ivec2& size) : width(size.x), height(size.y) { +} + uint Viewport::getWidth() const { return width; } diff --git a/src/graphics/core/Viewport.hpp b/src/graphics/core/Viewport.hpp index d7579227..6cd25050 100644 --- a/src/graphics/core/Viewport.hpp +++ b/src/graphics/core/Viewport.hpp @@ -9,6 +9,7 @@ class Viewport { uint height; public: Viewport(uint width, uint height); + Viewport(const glm::ivec2& size); virtual uint getWidth() const; virtual uint getHeight() const; diff --git a/src/graphics/render/BlocksPreview.cpp b/src/graphics/render/BlocksPreview.cpp index 17c087ed..2be2e368 100644 --- a/src/graphics/render/BlocksPreview.cpp +++ b/src/graphics/render/BlocksPreview.cpp @@ -26,7 +26,7 @@ std::unique_ptr BlocksPreview::draw( const Block& def, int size ){ - Window::clear(); + display::clear(); blockid_t id = def.rt.id; const UVRegion texfaces[6]{cache.getRegion(id, 0), cache.getRegion(id, 1), cache.getRegion(id, 2), cache.getRegion(id, 3), @@ -98,6 +98,7 @@ std::unique_ptr BlocksPreview::draw( } std::unique_ptr BlocksPreview::build( + Window& window, const ContentGfxCache& cache, const Assets& assets, const ContentIndices& indices @@ -108,8 +109,7 @@ std::unique_ptr BlocksPreview::build( auto& shader = assets.require("ui3d"); const auto& atlas = assets.require("blocks"); - Viewport viewport(iconSize, iconSize); - DrawContext pctx(nullptr, viewport, nullptr); + DrawContext pctx(nullptr, window, nullptr); DrawContext ctx = pctx.sub(); ctx.setCullFace(true); ctx.setDepthTest(true); @@ -127,8 +127,8 @@ std::unique_ptr BlocksPreview::build( glm::vec3(0, 1, 0))); AtlasBuilder builder; - Window::viewport(0, 0, iconSize, iconSize); - Window::setBgColor(glm::vec4(0.0f)); + ctx.setViewport(Viewport(iconSize, iconSize)); + display::setBgColor(glm::vec4(0.0f)); fbo.bind(); for (size_t i = 0; i < count; i++) { @@ -137,7 +137,5 @@ std::unique_ptr BlocksPreview::build( builder.add(def.name, draw(cache, shader, fbo, batch, def, iconSize)); } fbo.unbind(); - - Window::viewport(0, 0, Window::width, Window::height); return builder.build(2); } diff --git a/src/graphics/render/BlocksPreview.hpp b/src/graphics/render/BlocksPreview.hpp index fecd1c10..bac8e2cf 100644 --- a/src/graphics/render/BlocksPreview.hpp +++ b/src/graphics/render/BlocksPreview.hpp @@ -13,6 +13,7 @@ class Batch3D; class Block; class ContentIndices; class Shader; +class Window; class ContentGfxCache; class BlocksPreview { @@ -26,6 +27,7 @@ class BlocksPreview { ); public: static std::unique_ptr build( + Window& window, const ContentGfxCache& cache, const Assets& assets, const ContentIndices& indices diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 70464a26..61c3a472 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -316,7 +316,7 @@ void WorldRenderer::renderHands( assets.get(def.modelName), nullptr ); - Window::clearDepth(); + display::clearDepth(); setupWorldShader(entityShader, hudcam, engine.getSettings(), 0.0f); skybox->bind(); modelBatch->render(); @@ -359,7 +359,7 @@ void WorldRenderer::draw( DrawContext wctx = pctx.sub(); postProcessing.use(wctx); - Window::clearDepth(); + display::clearDepth(); // Drawing background sky plane skybox->draw(pctx, camera, assets, worldInfo.daytime, clouds); diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp index 9d210d7d..3fdba305 100644 --- a/src/graphics/ui/GUI.cpp +++ b/src/graphics/ui/GUI.cpp @@ -20,7 +20,6 @@ #include "graphics/core/Shader.hpp" #include "gui_util.hpp" #include "window/Camera.hpp" -#include "window/Events.hpp" #include "window/Window.hpp" #include "window/input.hpp" @@ -35,7 +34,8 @@ GUI::GUI(Engine& engine) batch2D(std::make_unique(1024)), container(std::make_shared(*this, glm::vec2(1000))) { container->setId("root"); - uicamera = std::make_unique(glm::vec3(), Window::height); + uicamera = + std::make_unique(glm::vec3(), engine.getWindow().getSize().y); uicamera->perspective = false; uicamera->flipped = true; @@ -121,7 +121,7 @@ void GUI::actMouse(float delta, const CursorState& cursor) { doubleClicked = false; doubleClickTimer += delta + mouseDelta * 0.1f; - auto hover = container->getAt(Events::cursor); + auto hover = container->getAt(cursor.pos); if (this->hover && this->hover != hover) { this->hover->setHover(false); } @@ -255,7 +255,7 @@ void GUI::draw(const DrawContext& pctx, const Assets& assets) { container->draw(ctx, assets); if (hover) { - Window::setCursor(hover->getCursor()); + engine.getWindow().setCursor(hover->getCursor()); } if (hover && debug) { auto pos = hover->calcPos(); @@ -361,3 +361,7 @@ const Input& GUI::getInput() const { Input& GUI::getInput() { return engine.getInput(); } + +Window& GUI::getWindow() { + return engine.getWindow(); +} diff --git a/src/graphics/ui/GUI.hpp b/src/graphics/ui/GUI.hpp index 623bd17e..518d0d10 100644 --- a/src/graphics/ui/GUI.hpp +++ b/src/graphics/ui/GUI.hpp @@ -17,6 +17,7 @@ class Batch2D; struct CursorState; class Engine; class Input; +class Window; /* Some info about padding and margin. @@ -158,5 +159,6 @@ namespace gui { void toggleDebug(); const Input& getInput() const; Input& getInput(); + Window& getWindow(); }; } diff --git a/src/graphics/ui/elements/InventoryView.cpp b/src/graphics/ui/elements/InventoryView.cpp index a44dd8f2..507a1629 100644 --- a/src/graphics/ui/elements/InventoryView.cpp +++ b/src/graphics/ui/elements/InventoryView.cpp @@ -13,7 +13,6 @@ #include "objects/Player.hpp" #include "util/stringutil.hpp" #include "voxels/Block.hpp" -#include "window/Events.hpp" #include "window/input.hpp" #include "world/Level.hpp" #include "graphics/core/Atlas.hpp" diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index cc326976..99d2937e 100644 --- a/src/graphics/ui/elements/TextBox.cpp +++ b/src/graphics/ui/elements/TextBox.cpp @@ -15,7 +15,6 @@ #include "graphics/core/Font.hpp" #include "graphics/ui/markdown.hpp" #include "util/stringutil.hpp" -#include "window/Events.hpp" #include "window/Window.hpp" #include "devtools/actions.hpp" #include "../markdown.hpp" @@ -251,7 +250,9 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) { batch->texture(nullptr); batch->setColor(glm::vec4(1.0f)); - if (editable && int((Window::time() - caretLastMove) * 2) % 2 == 0) { + float time = gui.getWindow().time(); + + if (editable && static_cast((time - caretLastMove) * 2) % 2 == 0) { uint line = rawTextCache.getLineByTextIndex(caret); uint lcaret = caret - rawTextCache.getTextLineOffset(line); int width = font->calcWidth(input, lcaret); @@ -750,7 +751,7 @@ void TextBox::stepRight(bool shiftPressed, bool breakSelection) { size_t caret = breakSelection ? selectionEnd : this->caret; if (caret < input.length()) { setCaret(caret + 1); - caretLastMove = Window::time(); + caretLastMove = gui.getWindow().time(); if (shiftPressed) { if (selectionStart == selectionEnd) { selectionOrigin = previousCaret; @@ -883,7 +884,7 @@ void TextBox::keyPressed(keycode key) { if (key == keycode::C || key == keycode::X) { std::string text = util::wstr2str_utf8(getSelection()); if (!text.empty()) { - Window::setClipboardText(text.c_str()); + gui.getInput().setClipboardText(text.c_str()); } if (editable && key == keycode::X) { eraseSelected(); @@ -1070,7 +1071,7 @@ void TextBox::setCaret(size_t position) { rawTextCache.prepare(font, width); rawTextCache.update(input, multiline, label->isTextWrapping()); - caretLastMove = Window::time(); + caretLastMove = gui.getWindow().time(); uint line = rawTextCache.getLineByTextIndex(caret); int offset = label->getLineYOffset(line) + getContentOffset().y; diff --git a/src/graphics/ui/gui_xml.cpp b/src/graphics/ui/gui_xml.cpp index cbbab546..61719f29 100644 --- a/src/graphics/ui/gui_xml.cpp +++ b/src/graphics/ui/gui_xml.cpp @@ -24,7 +24,6 @@ #include "logic/scripting/scripting.hpp" #include "maths/voxmaths.hpp" #include "util/stringutil.hpp" -#include "window/Events.hpp" using namespace gui; diff --git a/src/io/settings_io.cpp b/src/io/settings_io.cpp index f5f1341b..39df07f0 100644 --- a/src/io/settings_io.cpp +++ b/src/io/settings_io.cpp @@ -7,7 +7,6 @@ #include "coders/toml.hpp" #include "debug/Logger.hpp" #include "settings.hpp" -#include "window/Events.hpp" #include "window/input.hpp" static debug::Logger logger("settings_io"); diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp index b4e20a60..b157e5e5 100644 --- a/src/logic/PlayerController.cpp +++ b/src/logic/PlayerController.cpp @@ -60,7 +60,7 @@ void CameraControl::refreshRotation() { ); } -void CameraControl::updateMouse(PlayerInput& input) { +void CameraControl::updateMouse(PlayerInput& input, int windowHeight) { glm::vec3 rotation = player.getRotation(); float sensitivity = @@ -68,7 +68,7 @@ void CameraControl::updateMouse(PlayerInput& input) { : settings.sensitivity.get()); auto d = glm::degrees( - input.delta / static_cast(Window::height) * sensitivity + input.delta / static_cast(windowHeight) * sensitivity ); rotation.x -= d.x; rotation.y -= d.y; @@ -272,13 +272,15 @@ void PlayerController::update(float delta, const Input* inputEvents) { updatePlayer(delta); } -void PlayerController::postUpdate(float delta, const Input* input, bool pause) { +void PlayerController::postUpdate( + float delta, int windowHeight, const Input* input, bool pause +) { if (!pause) { updateFootsteps(delta); } if (!pause && input) { - camControl.updateMouse(this->input); + camControl.updateMouse(this->input, windowHeight); } camControl.refreshRotation(); player.postUpdate(); diff --git a/src/logic/PlayerController.hpp b/src/logic/PlayerController.hpp index b0c4bf26..53d14311 100644 --- a/src/logic/PlayerController.hpp +++ b/src/logic/PlayerController.hpp @@ -41,7 +41,7 @@ class CameraControl { void switchCamera(); public: CameraControl(Player& player, const CameraSettings& settings); - void updateMouse(PlayerInput& input); + void updateMouse(PlayerInput& input, int windowHeight); void update(PlayerInput input, float delta, const Chunks& chunks); void refreshPosition(); void refreshRotation(); @@ -84,6 +84,8 @@ public: /// @param delta delta time /// @param inputEvents nullable window inputs /// @param pause is game paused - void postUpdate(float delta, const Input* inputEvents, bool pause); + void postUpdate( + float delta, int windowHeight, const Input* inputEvents, bool pause + ); Player* getPlayer(); }; diff --git a/src/logic/scripting/lua/libs/libinput.cpp b/src/logic/scripting/lua/libs/libinput.cpp index cd9325d0..15202f34 100644 --- a/src/logic/scripting/lua/libs/libinput.cpp +++ b/src/logic/scripting/lua/libs/libinput.cpp @@ -7,7 +7,6 @@ #include "libgui.hpp" #include "util/stringutil.hpp" #include "util/observer_handler.hpp" -#include "window/Events.hpp" #include "window/input.hpp" #include "coders/toml.hpp" diff --git a/src/objects/Player.cpp b/src/objects/Player.cpp index cbedd676..b4a3bd66 100644 --- a/src/objects/Player.cpp +++ b/src/objects/Player.cpp @@ -14,7 +14,6 @@ #include "physics/PhysicsSolver.hpp" #include "voxels/Chunks.hpp" #include "window/Camera.hpp" -#include "window/Events.hpp" #include "world/Level.hpp" #include "data/dv_util.hpp" #include "debug/Logger.hpp" diff --git a/src/window/Events.cpp b/src/window/Events.cpp deleted file mode 100644 index 3beabd2d..00000000 --- a/src/window/Events.cpp +++ /dev/null @@ -1,189 +0,0 @@ -#include "Events.hpp" - -#include -#include -#include - -#include "debug/Logger.hpp" -#include "util/stringutil.hpp" -#include "Window.hpp" - -static debug::Logger logger("events"); - -inline constexpr short _MOUSE_KEYS_OFFSET = 1024; - -namespace { - bool keys[KEYS_BUFFER_SIZE] = {}; - uint frames[KEYS_BUFFER_SIZE] = {}; - uint current_frame = 0; - bool cursor_drag = false; - bool cursor_locked = false; - std::unordered_map> key_callbacks; -} - -int Events::scroll = 0; - -glm::vec2 Events::delta = {}; -glm::vec2 Events::cursor = {}; - -std::vector Events::codepoints; -std::vector Events::pressedKeys; -Bindings Events::bindings {}; - -int Events::getScroll() { - return scroll; -} - -bool Events::pressed(keycode keycode) { - return pressed(static_cast(keycode)); -} - -bool Events::pressed(int keycode) { - if (keycode < 0 || keycode >= KEYS_BUFFER_SIZE) { - return false; - } - return keys[keycode]; -} - -bool Events::jpressed(keycode keycode) { - return jpressed(static_cast(keycode)); -} - -bool Events::jpressed(int keycode) { - return Events::pressed(keycode) && frames[keycode] == current_frame; -} - -bool Events::clicked(mousecode button) { - return clicked(static_cast(button)); -} - -bool Events::clicked(int button) { - return Events::pressed(_MOUSE_KEYS_OFFSET + button); -} - -bool Events::jclicked(mousecode button) { - return jclicked(static_cast(button)); -} - -bool Events::jclicked(int button) { - return Events::jpressed(_MOUSE_KEYS_OFFSET + button); -} - -void Events::toggleCursor() { - cursor_drag = false; - cursor_locked = !cursor_locked; - Window::setCursorMode( - cursor_locked ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL - ); -} - -void Events::pollEvents() { - current_frame++; - delta.x = 0.f; - delta.y = 0.f; - scroll = 0; - codepoints.clear(); - pressedKeys.clear(); - glfwPollEvents(); - - for (auto& entry : bindings.getAll()) { - auto& binding = entry.second; - if (!binding.enabled) { - binding.state = false; - continue; - } - binding.justChange = false; - - bool newstate = false; - switch (binding.type) { - case inputtype::keyboard: - newstate = pressed(binding.code); - break; - case inputtype::mouse: - newstate = clicked(binding.code); - break; - } - - if (newstate) { - if (!binding.state) { - binding.state = true; - binding.justChange = true; - binding.onactived.notify(); - } - } else { - if (binding.state) { - binding.state = false; - binding.justChange = true; - } - } - } -} - -Binding* Events::getBinding(const std::string& name) { - return bindings.get(name); -} - -Binding& Events::requireBinding(const std::string& name) { - if (const auto found = getBinding(name)) { - return *found; - } - throw std::runtime_error("binding '" + name + "' does not exist"); -} - -void Events::bind(const std::string& name, inputtype type, keycode code) { - bind(name, type, static_cast(code)); -} - -void Events::bind(const std::string& name, inputtype type, mousecode code) { - bind(name, type, static_cast(code)); -} - -void Events::bind(const std::string& name, inputtype type, int code) { - bindings.bind(name, type, code); -} - -void Events::rebind(const std::string& name, inputtype type, int code) { - requireBinding(name) = Binding(type, code); -} - -bool Events::active(const std::string& name) { - return bindings.active(name); -} - -bool Events::jactive(const std::string& name) { - return bindings.jactive(name); -} - -void Events::setKey(int key, bool b) { - ::keys[key] = b; - ::frames[key] = current_frame; - if (b) { - const auto& callbacks = ::key_callbacks.find(static_cast(key)); - if (callbacks != ::key_callbacks.end()) { - callbacks->second.notify(); - } - } -} - -void Events::setButton(int button, bool b) { - setKey(_MOUSE_KEYS_OFFSET + button, b); -} - -void Events::setPosition(float xpos, float ypos) { - if (::cursor_drag) { - Events::delta.x += xpos - Events::cursor.x; - Events::delta.y += ypos - Events::cursor.y; - } else { - ::cursor_drag = true; - } - Events::cursor.x = xpos; - Events::cursor.y = ypos; -} - -observer_handler Events::addKeyCallback(keycode key, KeyCallback callback) { - return ::key_callbacks[key].add(std::move(callback)); -} - -bool Events::isCursorLocked() { - return cursor_locked; -} diff --git a/src/window/Events.hpp b/src/window/Events.hpp deleted file mode 100644 index 9212e361..00000000 --- a/src/window/Events.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "delegates.hpp" -#include "typedefs.hpp" -#include "input.hpp" - -inline constexpr short KEYS_BUFFER_SIZE = 1036; - -namespace Events { - extern int scroll; - extern glm::vec2 delta; - extern glm::vec2 cursor; - extern std::vector codepoints; - extern std::vector pressedKeys; - extern Bindings bindings; - - void pollEvents(); - - int getScroll(); - - bool pressed(keycode keycode); - bool pressed(int keycode); - bool jpressed(keycode keycode); - bool jpressed(int keycode); - - bool clicked(mousecode button); - bool clicked(int button); - bool jclicked(mousecode button); - bool jclicked(int button); - - void toggleCursor(); - - Binding* getBinding(const std::string& name); - Binding& requireBinding(const std::string& name); - void bind(const std::string& name, inputtype type, keycode code); - void bind(const std::string& name, inputtype type, mousecode code); - void bind(const std::string& name, inputtype type, int code); - void rebind(const std::string& name, inputtype type, int code); - bool active(const std::string& name); - bool jactive(const std::string& name); - - observer_handler addKeyCallback(keycode key, KeyCallback callback); - - void setKey(int key, bool b); - void setButton(int button, bool b); - - void setPosition(float xpos, float ypos); - - bool isCursorLocked(); -}; diff --git a/src/window/Window.cpp b/src/window/Window.cpp deleted file mode 100644 index 8533f522..00000000 --- a/src/window/Window.cpp +++ /dev/null @@ -1,568 +0,0 @@ -#include "Window.hpp" - -#include -#include - -#include -#include -#include -#include - -#include "debug/Logger.hpp" -#include "graphics/core/ImageData.hpp" -#include "graphics/core/Texture.hpp" -#include "settings.hpp" -#include "util/ObjectsKeeper.hpp" -#include "Events.hpp" - -#include "util/platform.hpp" - -static debug::Logger logger("window"); - -namespace { - GLFWwindow* window = nullptr; - DisplaySettings* settings = nullptr; - std::stack scissorStack; - glm::vec4 scissorArea; - int framerate = -1; - double prevSwap = 0.0; - bool fullscreen = false; - CursorShape cursor = CursorShape::ARROW; - int posX = 0; - int posY = 0; -} - -uint Window::width = 0; -uint Window::height = 0; - -static util::ObjectsKeeper observers_keeper; -static std::unordered_set extensionsCache; - -static const char* gl_error_name(int error) { - switch (error) { - case GL_DEBUG_TYPE_ERROR: return "ERROR"; - case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: return "DEPRECATED_BEHAVIOR"; - case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: return "UNDEFINED_BEHAVIOR"; - case GL_DEBUG_TYPE_PORTABILITY: return "PORTABILITY"; - case GL_DEBUG_TYPE_PERFORMANCE: return "PERFORMANCE"; - case GL_DEBUG_TYPE_OTHER: return "OTHER"; - } - return "UNKNOWN"; -} - -static const char* gl_severity_name(int severity) { - switch (severity) { - case GL_DEBUG_SEVERITY_LOW: return "LOW"; - case GL_DEBUG_SEVERITY_MEDIUM: return "MEDIUM"; - case GL_DEBUG_SEVERITY_HIGH: return "HIGH"; - case GL_DEBUG_SEVERITY_NOTIFICATION: return "NOTIFICATION"; - } - return "UNKNOWN"; -} - -static void GLAPIENTRY gl_message_callback( - GLenum source, - GLenum type, - GLuint id, - GLenum severity, - GLsizei length, - const GLchar* message, - const void* userParam -) { - if (severity == GL_DEBUG_SEVERITY_NOTIFICATION) { - return; - } - if (!ENGINE_DEBUG_BUILD && severity != GL_DEBUG_SEVERITY_HIGH) { - return; - } - std::cerr << "GL:" << gl_error_name(type) << ":" - << gl_severity_name(severity) << ": " << message << std::endl; -} - -static void cursor_position_callback(GLFWwindow*, double xpos, double ypos) { - Events::setPosition(xpos, ypos); -} - -static void mouse_button_callback(GLFWwindow*, int button, int action, int) { - Events::setButton(button, action == GLFW_PRESS); -} - -static void key_callback( - GLFWwindow*, int key, int /*scancode*/, int action, int /*mode*/ -) { - if (key == GLFW_KEY_UNKNOWN) return; - if (action == GLFW_PRESS) { - Events::setKey(key, true); - Events::pressedKeys.push_back(static_cast(key)); - } else if (action == GLFW_RELEASE) { - Events::setKey(key, false); - } else if (action == GLFW_REPEAT) { - Events::pressedKeys.push_back(static_cast(key)); - } -} - -static void scroll_callback(GLFWwindow*, double xoffset, double yoffset) { - Events::scroll += yoffset; -} - -static void character_callback(GLFWwindow*, unsigned int codepoint) { - Events::codepoints.push_back(codepoint); -} - -static void window_size_callback(GLFWwindow*, int width, int height) { - if (width && height) { - glViewport(0, 0, width, height); - Window::width = width; - Window::height = height; - - if (!Window::isFullscreen() && !Window::isMaximized()) { - Window::getSettings()->width.set(width); - Window::getSettings()->height.set(height); - } - } - Window::resetScissor(); -} - -bool Window::isMaximized() { - return glfwGetWindowAttrib(window, GLFW_MAXIMIZED); -} - -bool Window::isIconified() { - return glfwGetWindowAttrib(window, GLFW_ICONIFIED); -} - -bool Window::isFocused() { - return glfwGetWindowAttrib(window, GLFW_FOCUSED); -} - -static const char* glfw_error_name(int error) { - switch (error) { - case GLFW_NO_ERROR: - return "no error"; - case GLFW_NOT_INITIALIZED: - return "not initialized"; - case GLFW_NO_CURRENT_CONTEXT: - return "no current context"; - case GLFW_INVALID_ENUM: - return "invalid enum"; - case GLFW_INVALID_VALUE: - return "invalid value"; - case GLFW_OUT_OF_MEMORY: - return "out of memory"; - case GLFW_API_UNAVAILABLE: - return "api unavailable"; - case GLFW_VERSION_UNAVAILABLE: - return "version unavailable"; - case GLFW_PLATFORM_ERROR: - return "platform error"; - case GLFW_FORMAT_UNAVAILABLE: - return "format unavailable"; - case GLFW_NO_WINDOW_CONTEXT: - return "no window context"; - default: - return "unknown error"; - } -} - -static void glfw_error_callback(int error, const char* description) { - auto logline = logger.error(); - logline << "GLFW error [0x" << std::hex << error << " - " - << glfw_error_name(error) << "]"; - if (description) { - logline << ": " << description; - } -} - -static GLFWcursor* standard_cursors[static_cast(CursorShape::LAST) + 1] = {}; - -class GLFWInput : public Input { -public: - void pollEvents() override { - Events::pollEvents(); - } - - const char* getClipboardText() const override { - return glfwGetClipboardString(::window); - } - - int getScroll() override { - return Events::getScroll(); - } - - bool pressed(keycode keycode) const override { - return Events::pressed(keycode); - } - bool jpressed(keycode keycode) const override { - return Events::jpressed(keycode); - } - - bool clicked(mousecode mousecode) const override { - return Events::clicked(mousecode); - } - bool jclicked(mousecode mousecode) const override { - return Events::jclicked(mousecode); - } - - CursorState getCursor() const override { - return { - Events::isCursorLocked(), - Events::cursor, - Events::delta - }; - } - - void toggleCursor() override { - Events::toggleCursor(); - } - - Bindings& getBindings() override { - return Events::bindings; - } - - const Bindings& getBindings() const override { - return Events::bindings; - } - - observer_handler addKeyCallback(keycode key, KeyCallback callback) override { - return Events::addKeyCallback(key, std::move(callback)); - } - - const std::vector& getPressedKeys() const override { - return Events::pressedKeys; - } - - const std::vector& getCodepoints() const override { - return Events::codepoints; - } -}; -static_assert(!std::is_abstract()); - -std::unique_ptr Window::initialize(DisplaySettings* settings) { - ::settings = settings; - Window::width = settings->width.get(); - Window::height = settings->height.get(); - - std::string title = "VoxelCore v" + - std::to_string(ENGINE_VERSION_MAJOR) + "." + - std::to_string(ENGINE_VERSION_MINOR); - if (ENGINE_DEBUG_BUILD) { - title += " [debug]"; - } - - glfwSetErrorCallback(glfw_error_callback); - if (glfwInit() == GLFW_FALSE) { - logger.error() << "failed to initialize GLFW"; - return nullptr; - } - - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); -#ifdef __APPLE__ - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); - glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE); -#else - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE); -#endif - glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); - glfwWindowHint(GLFW_SAMPLES, settings->samples.get()); - - window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr); - if (window == nullptr) { - logger.error() << "failed to create GLFW window"; - glfwTerminate(); - return nullptr; - } - glfwMakeContextCurrent(window); - - glewExperimental = GL_TRUE; - - GLenum glewErr = glewInit(); - if (glewErr != GLEW_OK) { - if (glewErr == GLEW_ERROR_NO_GLX_DISPLAY) { - // see issue #240 - logger.warning() - << "glewInit() returned GLEW_ERROR_NO_GLX_DISPLAY; ignored"; - } else { - logger.error() << "failed to initialize GLEW:\n" - << glewGetErrorString(glewErr); - return nullptr; - } - } - - if (isGlExtensionSupported("GL_KHR_debug")) { - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback(gl_message_callback, nullptr); - } - - glViewport(0, 0, width, height); - glClearColor(0.0f, 0.0f, 0.0f, 1); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - GLint maxTextureSize[1] {static_cast(Texture::MAX_RESOLUTION)}; - glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxTextureSize); - if (maxTextureSize[0] > 0) { - Texture::MAX_RESOLUTION = maxTextureSize[0]; - logger.info() << "max texture size is " << Texture::MAX_RESOLUTION; - } - - glfwSetKeyCallback(window, key_callback); - glfwSetMouseButtonCallback(window, mouse_button_callback); - glfwSetCursorPosCallback(window, cursor_position_callback); - glfwSetWindowSizeCallback(window, window_size_callback); - glfwSetCharCallback(window, character_callback); - glfwSetScrollCallback(window, scroll_callback); - - observers_keeper = util::ObjectsKeeper(); - observers_keeper.keepAlive(settings->fullscreen.observe( - [](bool value) { - if (value != isFullscreen()) { - toggleFullscreen(); - } - }, - true - )); - - glfwSwapInterval(1); - setFramerate(settings->framerate.get()); - const GLubyte* vendor = glGetString(GL_VENDOR); - const GLubyte* renderer = glGetString(GL_RENDERER); - logger.info() << "GL Vendor: " << reinterpret_cast(vendor); - logger.info() << "GL Renderer: " << reinterpret_cast(renderer); - logger.info() << "GLFW: " << glfwGetVersionString(); - glm::vec2 scale; - glfwGetMonitorContentScale(glfwGetPrimaryMonitor(), &scale.x, &scale.y); - logger.info() << "monitor content scale: " << scale.x << "x" << scale.y; - - input_util::initialize(); - - for (int i = 0; i <= static_cast(CursorShape::LAST); i++) { - int cursor = GLFW_ARROW_CURSOR + i; - // GLFW 3.3 does not support some cursors - if (GLFW_VERSION_MAJOR <= 3 && GLFW_VERSION_MINOR <= 3 && cursor > GLFW_VRESIZE_CURSOR) { - break; - } - standard_cursors[i] = glfwCreateStandardCursor(cursor); - } - return std::make_unique(); -} - -void Window::clear() { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); -} - -void Window::clearDepth() { - glClear(GL_DEPTH_BUFFER_BIT); -} - -void Window::setBgColor(glm::vec3 color) { - glClearColor(color.r, color.g, color.b, 1.0f); -} - -void Window::setBgColor(glm::vec4 color) { - glClearColor(color.r, color.g, color.b, color.a); -} - -void Window::viewport(int x, int y, int width, int height) { - glViewport(x, y, width, height); -} - -void Window::setCursorMode(int mode) { - glfwSetInputMode(window, GLFW_CURSOR, mode); -} - -void Window::resetScissor() { - scissorArea = glm::vec4(0.0f, 0.0f, width, height); - scissorStack = std::stack(); - glDisable(GL_SCISSOR_TEST); -} - -void Window::pushScissor(glm::vec4 area) { - if (scissorStack.empty()) { - glEnable(GL_SCISSOR_TEST); - } - scissorStack.push(scissorArea); - - area.z += glm::ceil(area.x); - area.w += glm::ceil(area.y); - - area.x = glm::max(area.x, scissorArea.x); - area.y = glm::max(area.y, scissorArea.y); - - area.z = glm::min(area.z, scissorArea.z); - area.w = glm::min(area.w, scissorArea.w); - - if (area.z < 0.0f || area.w < 0.0f) { - glScissor(0, 0, 0, 0); - } else { - glScissor( - area.x, - Window::height - area.w, - std::max(0, static_cast(glm::ceil(area.z - area.x))), - std::max(0, static_cast(glm::ceil(area.w - area.y))) - ); - } - scissorArea = area; -} - -void Window::popScissor() { - if (scissorStack.empty()) { - logger.warning() << "extra Window::popScissor call"; - return; - } - glm::vec4 area = scissorStack.top(); - scissorStack.pop(); - if (area.z < 0.0f || area.w < 0.0f) { - glScissor(0, 0, 0, 0); - } else { - glScissor( - area.x, - Window::height - area.w, - std::max(0, int(area.z - area.x)), - std::max(0, int(area.w - area.y)) - ); - } - if (scissorStack.empty()) { - glDisable(GL_SCISSOR_TEST); - } - scissorArea = area; -} - -void Window::terminate() { - observers_keeper = util::ObjectsKeeper(); - for (int i = 0; i <= static_cast(CursorShape::LAST); i++) { - glfwDestroyCursor(standard_cursors[i]); - } - glfwTerminate(); -} - -bool Window::isShouldClose() { - return glfwWindowShouldClose(window); -} - -void Window::setShouldClose(bool flag) { - glfwSetWindowShouldClose(window, flag); -} - -void Window::setFramerate(int framerate) { - if ((framerate != -1) != (::framerate != -1)) { - glfwSwapInterval(framerate == -1); - } - ::framerate = framerate; -} - -void Window::toggleFullscreen() { - fullscreen = !fullscreen; - - GLFWmonitor* monitor = glfwGetPrimaryMonitor(); - const GLFWvidmode* mode = glfwGetVideoMode(monitor); - - if (Events::isCursorLocked()){ - Events::toggleCursor(); - } - - if (fullscreen) { - glfwGetWindowPos(window, &posX, &posY); - glfwSetWindowMonitor( - window, monitor, 0, 0, mode->width, mode->height, GLFW_DONT_CARE - ); - } else { - glfwSetWindowMonitor( - window, - nullptr, - posX, - posY, - settings->width.get(), - settings->height.get(), - GLFW_DONT_CARE - ); - } - - double xPos, yPos; - glfwGetCursorPos(window, &xPos, &yPos); - Events::setPosition(xPos, yPos); -} - -bool Window::isFullscreen() { - return fullscreen; -} - -void Window::swapBuffers() { - glfwSwapBuffers(window); - Window::resetScissor(); - if (framerate > 0) { - auto elapsedTime = time() - prevSwap; - auto frameTime = 1.0 / framerate; - if (elapsedTime < frameTime) { - platform::sleep( - static_cast((frameTime - elapsedTime) * 1000) - ); - } - } - prevSwap = time(); -} - -double Window::time() { - return glfwGetTime(); -} - -DisplaySettings* Window::getSettings() { - return settings; -} - -void Window::setCursor(CursorShape shape) { - if (cursor == shape) { - return; - } - cursor = shape; - // NULL cursor is valid for GLFW - glfwSetCursor(window, standard_cursors[static_cast(shape)]); -} - -std::unique_ptr Window::takeScreenshot() { - auto data = std::make_unique(width * height * 3); - glPixelStorei(GL_PACK_ALIGNMENT, 1); - glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, data.get()); - return std::make_unique( - ImageFormat::rgb888, width, height, data.release() - ); -} - -void Window::setClipboardText(const char* text) { - glfwSetClipboardString(window, text); -} - -void Window::setIcon(const ImageData* image) { - GLFWimage icon { - static_cast(image->getWidth()), - static_cast(image->getHeight()), - image->getData()}; - glfwSetWindowIcon(window, 1, &icon); -} - -static void initGlExtensionsCache() { - if (!extensionsCache.empty()) { - return; - } - - GLint numExtensions = 0; - glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions); - - for (GLint i = 0; i < numExtensions; ++i) { - const char *ext = reinterpret_cast(glGetStringi(GL_EXTENSIONS, i)); - if (ext) { - extensionsCache.insert(ext); - } - } -} - -bool Window::isGlExtensionSupported(const char *extension) { - if (!extension || !*extension) { - return false; - } - - initGlExtensionsCache(); - - return extensionsCache.find(extension) != extensionsCache.end(); -} diff --git a/src/window/Window.hpp b/src/window/Window.hpp index 0fe60af8..7b874a9e 100644 --- a/src/window/Window.hpp +++ b/src/window/Window.hpp @@ -12,43 +12,52 @@ class ImageData; class Input; struct DisplaySettings; -namespace Window { - extern uint width; - extern uint height; +class Window { +public: + Window(glm::ivec2 size) : size(std::move(size)) {} - std::unique_ptr initialize(DisplaySettings* settings); - void terminate(); + virtual ~Window() = default; + virtual void swapBuffers() = 0; - void viewport(int x, int y, int width, int height); - void setCursorMode(int mode); - bool isShouldClose(); - void setShouldClose(bool flag); - void swapBuffers(); - void setFramerate(int interval); - void toggleFullscreen(); - bool isFullscreen(); - bool isMaximized(); - bool isFocused(); - bool isIconified(); + virtual bool isMaximized() const = 0; + virtual bool isFocused() const = 0; + virtual bool isIconified() const = 0; - void pushScissor(glm::vec4 area); - void popScissor(); - void resetScissor(); + virtual bool isShouldClose() const = 0; + virtual void setShouldClose(bool flag) = 0; - void setCursor(CursorShape shape); + virtual void setCursor(CursorShape shape) = 0; + virtual void toggleFullscreen() = 0; + virtual bool isFullscreen() const = 0; + + virtual void setIcon(const ImageData* image) = 0; + + virtual void pushScissor(glm::vec4 area) = 0; + virtual void popScissor() = 0; + virtual void resetScissor() = 0; + + virtual double time() = 0; + + virtual void setFramerate(int framerate) = 0; + + // TODO: move somewhere + virtual std::unique_ptr takeScreenshot() = 0; + + const glm::ivec2& getSize() const { + return size; + } +protected: + glm::ivec2 size; +}; + +namespace display { + std::tuple< + std::unique_ptr, + std::unique_ptr + > initialize(DisplaySettings* settings); void clear(); void clearDepth(); void setBgColor(glm::vec3 color); void setBgColor(glm::vec4 color); - double time(); - void setClipboardText(const char* text); - DisplaySettings* getSettings(); - void setIcon(const ImageData* image); - - inline glm::vec2 size() { - return glm::vec2(width, height); - } - - std::unique_ptr takeScreenshot(); }; diff --git a/src/window/detail/GLFWWindow.cpp b/src/window/detail/GLFWWindow.cpp new file mode 100644 index 00000000..a291798d --- /dev/null +++ b/src/window/detail/GLFWWindow.cpp @@ -0,0 +1,683 @@ +#include "window/Window.hpp" + +#include +#include +#include + +#include + +#include "debug/Logger.hpp" +#include "graphics/core/ImageData.hpp" +#include "graphics/core/Texture.hpp" +#include "settings.hpp" +#include "util/ObjectsKeeper.hpp" +#include "util/platform.hpp" +#include "window/input.hpp" + +static debug::Logger logger("window"); + +static std::unordered_set extensions_cache; + +static void init_gl_extensions_cache() { + if (!extensions_cache.empty()) { + return; + } + + GLint numExtensions = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions); + + for (GLint i = 0; i < numExtensions; ++i) { + const char *ext = reinterpret_cast(glGetStringi(GL_EXTENSIONS, i)); + if (ext) { + extensions_cache.insert(ext); + } + } +} + +static bool is_gl_extension_supported(const char *extension) { + if (!extension || !*extension) { + return false; + } + init_gl_extensions_cache(); + return extensions_cache.find(extension) != extensions_cache.end(); +} + +static const char* gl_error_name(int error) { + switch (error) { + case GL_DEBUG_TYPE_ERROR: return "ERROR"; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: return "DEPRECATED_BEHAVIOR"; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: return "UNDEFINED_BEHAVIOR"; + case GL_DEBUG_TYPE_PORTABILITY: return "PORTABILITY"; + case GL_DEBUG_TYPE_PERFORMANCE: return "PERFORMANCE"; + case GL_DEBUG_TYPE_OTHER: return "OTHER"; + } + return "UNKNOWN"; +} + +static const char* gl_severity_name(int severity) { + switch (severity) { + case GL_DEBUG_SEVERITY_LOW: return "LOW"; + case GL_DEBUG_SEVERITY_MEDIUM: return "MEDIUM"; + case GL_DEBUG_SEVERITY_HIGH: return "HIGH"; + case GL_DEBUG_SEVERITY_NOTIFICATION: return "NOTIFICATION"; + } + return "UNKNOWN"; +} + +static void GLAPIENTRY gl_message_callback( + GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam +) { + if (severity == GL_DEBUG_SEVERITY_NOTIFICATION) { + return; + } + if (!ENGINE_DEBUG_BUILD && severity != GL_DEBUG_SEVERITY_HIGH) { + return; + } + logger.warning() << "GL:" << gl_error_name(type) << ":" + << gl_severity_name(severity) << ": " << message; +} + +static const char* glfw_error_name(int error) { + switch (error) { + case GLFW_NO_ERROR: + return "no error"; + case GLFW_NOT_INITIALIZED: + return "not initialized"; + case GLFW_NO_CURRENT_CONTEXT: + return "no current context"; + case GLFW_INVALID_ENUM: + return "invalid enum"; + case GLFW_INVALID_VALUE: + return "invalid value"; + case GLFW_OUT_OF_MEMORY: + return "out of memory"; + case GLFW_API_UNAVAILABLE: + return "api unavailable"; + case GLFW_VERSION_UNAVAILABLE: + return "version unavailable"; + case GLFW_PLATFORM_ERROR: + return "platform error"; + case GLFW_FORMAT_UNAVAILABLE: + return "format unavailable"; + case GLFW_NO_WINDOW_CONTEXT: + return "no window context"; + default: + return "unknown error"; + } +} + +static void glfw_error_callback(int error, const char* description) { + auto logline = logger.error(); + logline << "GLFW error [0x" << std::hex << error << " - " + << glfw_error_name(error) << "]"; + if (description) { + logline << ": " << description; + } +} + +inline constexpr short KEYS_BUFFER_SIZE = 1036; +inline constexpr short _MOUSE_KEYS_OFFSET = 1024; + +static GLFWcursor* standard_cursors[static_cast(CursorShape::LAST) + 1] = {}; + +class GLFWInput : public Input { +public: + int scroll = 0; + uint currentFrame = 0; + uint frames[KEYS_BUFFER_SIZE] {}; + std::vector codepoints; + std::vector pressedKeys; + Bindings bindings; + bool keys[KEYS_BUFFER_SIZE] {}; + std::unordered_map> keyCallbacks; + + GLFWInput(GLFWwindow* window) + : window(window) { + } + + void pollEvents() override { + delta.x = 0.0f; + delta.y = 0.0f; + scroll = 0; + currentFrame++; + codepoints.clear(); + pressedKeys.clear(); + glfwPollEvents(); + + for (auto& entry : bindings.getAll()) { + auto& binding = entry.second; + if (!binding.enabled) { + binding.state = false; + continue; + } + binding.justChange = false; + + bool newstate = false; + switch (binding.type) { + case inputtype::keyboard: + newstate = pressed(static_cast(binding.code)); + break; + case inputtype::mouse: + newstate = clicked(static_cast(binding.code)); + break; + } + + if (newstate) { + if (!binding.state) { + binding.state = true; + binding.justChange = true; + binding.onactived.notify(); + } + } else { + if (binding.state) { + binding.state = false; + binding.justChange = true; + } + } + } + } + + void onKeyCallback(int key, bool pressed) { + bool prevPressed = keys[key]; + keys[key] = pressed; + frames[key] = currentFrame; + if (pressed && !prevPressed) { + const auto& callbacks = keyCallbacks.find(static_cast(key)); + if (callbacks != keyCallbacks.end()) { + callbacks->second.notify(); + } + } + if (pressed) { + pressedKeys.push_back(static_cast(key)); + } + } + + void onMouseCallback(int button, bool pressed) { + int key = button + _MOUSE_KEYS_OFFSET; + onKeyCallback(key, pressed); + } + + const char* getClipboardText() const override { + return glfwGetClipboardString(window); + } + + void setClipboardText(const char* text) override { + glfwSetClipboardString(window, text); + } + + int getScroll() override { + return scroll; + } + + bool pressed(keycode key) const override { + int keycode = static_cast(key); + if (keycode < 0 || keycode >= KEYS_BUFFER_SIZE) { + return false; + } + return keys[keycode]; + } + bool jpressed(keycode keycode) const override { + return pressed(keycode) && + frames[static_cast(keycode)] == currentFrame; + } + + bool clicked(mousecode code) const override { + return pressed( + static_cast(_MOUSE_KEYS_OFFSET + static_cast(code)) + ); + } + bool jclicked(mousecode code) const override { + return clicked(code) && + frames[static_cast(code) + _MOUSE_KEYS_OFFSET] == + currentFrame; + } + + CursorState getCursor() const override { + return {isCursorLocked(), cursor, delta}; + } + + bool isCursorLocked() const override { + return cursorLocked; + } + + void toggleCursor() override { + cursorDrag = false; + if (cursorLocked) { + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + } else { + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + } + cursorLocked = !cursorLocked; + } + + void setCursorPosition(double xpos, double ypos) { + if (cursorDrag) { + delta.x += xpos - cursor.x; + delta.y += ypos - cursor.y; + } else { + cursorDrag = true; + } + cursor.x = xpos; + cursor.y = ypos; + } + + Bindings& getBindings() override { + return bindings; + } + + const Bindings& getBindings() const override { + return bindings; + } + + observer_handler addKeyCallback(keycode key, KeyCallback callback) override { + return keyCallbacks[key].add(std::move(callback)); + } + + const std::vector& getPressedKeys() const override { + return pressedKeys; + } + + const std::vector& getCodepoints() const override { + return codepoints; + } +private: + GLFWwindow* window; + bool cursorLocked = false; + bool cursorDrag = false; + glm::vec2 delta; + glm::vec2 cursor; +}; +static_assert(!std::is_abstract()); + +class GLFWWindow : public Window { +public: + GLFWInput& input; + DisplaySettings* settings; + + GLFWWindow( + GLFWInput& glfwInput, + GLFWwindow* window, + DisplaySettings* settings, + int width, + int height + ) + : Window({width, height}), + input(glfwInput), + settings(settings), + window(window) { + } + + ~GLFWWindow() { + for (int i = 0; i <= static_cast(CursorShape::LAST); i++) { + glfwDestroyCursor(standard_cursors[i]); + } + glfwTerminate(); + } + + double time() override { + return glfwGetTime(); + } + + void swapBuffers() override { + glfwSwapBuffers(window); + resetScissor(); + if (framerate > 0) { + auto elapsedTime = time() - prevSwap; + auto frameTime = 1.0 / framerate; + if (elapsedTime < frameTime) { + platform::sleep( + static_cast((frameTime - elapsedTime) * 1000) + ); + } + } + prevSwap = time(); + } + + bool isMaximized() const override { + return glfwGetWindowAttrib(window, GLFW_MAXIMIZED); + } + + bool isFocused() const override { + return glfwGetWindowAttrib(window, GLFW_FOCUSED); + } + + bool isIconified() const override { + return glfwGetWindowAttrib(window, GLFW_ICONIFIED); + } + + bool isShouldClose() const override { + return glfwWindowShouldClose(window); + } + + void setShouldClose(bool flag) override { + glfwSetWindowShouldClose(window, flag); + } + + void setCursor(CursorShape shape) override { + if (cursor == shape) { + return; + } + cursor = shape; + // NULL cursor is valid for GLFW + glfwSetCursor(window, standard_cursors[static_cast(shape)]); + } + + void toggleFullscreen() override { + fullscreen = !fullscreen; + + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + + if (input.isCursorLocked()){ + input.toggleCursor(); + } + + if (fullscreen) { + glfwGetWindowPos(window, &posX, &posY); + glfwSetWindowMonitor( + window, monitor, 0, 0, mode->width, mode->height, GLFW_DONT_CARE + ); + } else { + glfwSetWindowMonitor( + window, + nullptr, + posX, + posY, + settings->width.get(), + settings->height.get(), + GLFW_DONT_CARE + ); + } + + double xPos, yPos; + glfwGetCursorPos(window, &xPos, &yPos); + input.setCursorPosition(xPos, yPos); + } + + bool isFullscreen() const override { + return fullscreen; + } + + void setIcon(const ImageData* image) override { + if (image == nullptr) { + glfwSetWindowIcon(window, 0, nullptr); + return; + } + GLFWimage icon { + static_cast(image->getWidth()), + static_cast(image->getHeight()), + image->getData()}; + glfwSetWindowIcon(window, 1, &icon); + } + + void setSize(int width, int height) { + glViewport(0, 0, width, height); + size = {width, height}; + + if (!isFullscreen() && !isMaximized()) { + settings->width.set(width); + settings->height.set(height); + } + } + + void pushScissor(glm::vec4 area) override { + if (scissorStack.empty()) { + glEnable(GL_SCISSOR_TEST); + } + scissorStack.push(scissorArea); + + area.z += glm::ceil(area.x); + area.w += glm::ceil(area.y); + + area.x = glm::max(area.x, scissorArea.x); + area.y = glm::max(area.y, scissorArea.y); + + area.z = glm::min(area.z, scissorArea.z); + area.w = glm::min(area.w, scissorArea.w); + + if (area.z < 0.0f || area.w < 0.0f) { + glScissor(0, 0, 0, 0); + } else { + glScissor( + area.x, + size.y - area.w, + std::max(0, static_cast(glm::ceil(area.z - area.x))), + std::max(0, static_cast(glm::ceil(area.w - area.y))) + ); + } + scissorArea = area; + } + + void resetScissor() override { + scissorArea = glm::vec4(0.0f, 0.0f, size.x, size.y); + scissorStack = std::stack(); + glDisable(GL_SCISSOR_TEST); + } + + void popScissor() override { + if (scissorStack.empty()) { + logger.warning() << "extra Window::popScissor call"; + return; + } + glm::vec4 area = scissorStack.top(); + scissorStack.pop(); + if (area.z < 0.0f || area.w < 0.0f) { + glScissor(0, 0, 0, 0); + } else { + glScissor( + area.x, + size.y - area.w, + std::max(0, static_cast(area.z - area.x)), + std::max(0, static_cast(area.w - area.y)) + ); + } + if (scissorStack.empty()) { + glDisable(GL_SCISSOR_TEST); + } + scissorArea = area; + } + + std::unique_ptr takeScreenshot() override { + auto data = std::make_unique(size.x * size.y * 3); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glReadPixels(0, 0, size.x, size.y, GL_RGB, GL_UNSIGNED_BYTE, data.get()); + return std::make_unique( + ImageFormat::rgb888, size.x, size.y, data.release() + ); + } + + void setFramerate(int framerate) override { + if ((framerate != -1) != (this->framerate != -1)) { + glfwSwapInterval(framerate == -1); + } + this->framerate = framerate; + } + +private: + GLFWwindow* window; + CursorShape cursor = CursorShape::ARROW; + bool fullscreen = false; + int framerate = -1; + std::stack scissorStack; + glm::vec4 scissorArea; + double prevSwap = 0.0; + int posX = 0; + int posY = 0; +}; +static_assert(!std::is_abstract()); + +static void mouse_button_callback(GLFWwindow* window, int button, int action, int) { + auto handler = static_cast(glfwGetWindowUserPointer(window)); + handler->input.onMouseCallback(button, action == GLFW_PRESS); +} + +static void character_callback(GLFWwindow* window, unsigned int codepoint) { + auto handler = static_cast(glfwGetWindowUserPointer(window)); + handler->input.codepoints.push_back(codepoint); +} + +static void key_callback( + GLFWwindow* window, int key, int /*scancode*/, int action, int /*mode*/ +) { + auto handler = static_cast(glfwGetWindowUserPointer(window)); + auto& input = handler->input; + if (key == GLFW_KEY_UNKNOWN) { + return; + } + if (action == GLFW_PRESS) { + input.onKeyCallback(key, true); + + } else if (action == GLFW_RELEASE) { + input.onKeyCallback(key, false); + } else if (action == GLFW_REPEAT) { + input.onKeyCallback(key, true); + } +} + +static void window_size_callback(GLFWwindow* window, int width, int height) { + auto handler = static_cast(glfwGetWindowUserPointer(window)); + if (width && height) { + handler->setSize(width, height); + } + handler->resetScissor(); +} + +static void scroll_callback(GLFWwindow* window, double, double yoffset) { + auto handler = static_cast(glfwGetWindowUserPointer(window)); + handler->input.scroll += yoffset; +} + +static void cursor_pos_callback(GLFWwindow* window, double xpos, double ypos) { + auto handler = static_cast(glfwGetWindowUserPointer(window)); + handler->input.setCursorPosition(xpos, ypos); +} + +std::tuple< + std::unique_ptr, + std::unique_ptr +> display::initialize(DisplaySettings* settings) { + int width = settings->width.get(); + int height = settings->height.get(); + + std::string title = "VoxelCore v" + + std::to_string(ENGINE_VERSION_MAJOR) + "." + + std::to_string(ENGINE_VERSION_MINOR); + if (ENGINE_DEBUG_BUILD) { + title += " [debug]"; + } + + glfwSetErrorCallback(glfw_error_callback); + if (glfwInit() == GLFW_FALSE) { + logger.error() << "failed to initialize GLFW"; + return {nullptr, nullptr}; + } + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); +#ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE); +#else + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE); +#endif + glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); + glfwWindowHint(GLFW_SAMPLES, settings->samples.get()); + + auto window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr); + if (window == nullptr) { + logger.error() << "failed to create GLFW window"; + glfwTerminate(); + return {nullptr, nullptr}; + } + glfwMakeContextCurrent(window); + + glewExperimental = GL_TRUE; + + GLenum glewErr = glewInit(); + if (glewErr != GLEW_OK) { + if (glewErr == GLEW_ERROR_NO_GLX_DISPLAY) { + // see issue #240 + logger.warning() + << "glewInit() returned GLEW_ERROR_NO_GLX_DISPLAY; ignored"; + } else { + logger.error() << "failed to initialize GLEW:\n" + << glewGetErrorString(glewErr); + glfwTerminate(); + return {nullptr, nullptr}; + } + } + + if (is_gl_extension_supported("GL_KHR_debug")) { + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(gl_message_callback, nullptr); + } + + glViewport(0, 0, width, height); + glClearColor(0.0f, 0.0f, 0.0f, 1); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + GLint maxTextureSize[1] {static_cast(Texture::MAX_RESOLUTION)}; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxTextureSize); + if (maxTextureSize[0] > 0) { + Texture::MAX_RESOLUTION = maxTextureSize[0]; + logger.info() << "max texture size is " << Texture::MAX_RESOLUTION; + } + + glfwSetKeyCallback(window, key_callback); + glfwSetMouseButtonCallback(window, mouse_button_callback); + glfwSetCursorPosCallback(window, cursor_pos_callback); + glfwSetWindowSizeCallback(window, window_size_callback); + glfwSetCharCallback(window, character_callback); + glfwSetScrollCallback(window, scroll_callback); + + glfwSwapInterval(1); + const GLubyte* vendor = glGetString(GL_VENDOR); + const GLubyte* renderer = glGetString(GL_RENDERER); + logger.info() << "GL Vendor: " << reinterpret_cast(vendor); + logger.info() << "GL Renderer: " << reinterpret_cast(renderer); + logger.info() << "GLFW: " << glfwGetVersionString(); + glm::vec2 scale; + glfwGetMonitorContentScale(glfwGetPrimaryMonitor(), &scale.x, &scale.y); + logger.info() << "monitor content scale: " << scale.x << "x" << scale.y; + + input_util::initialize(); + + for (int i = 0; i <= static_cast(CursorShape::LAST); i++) { + int cursor = GLFW_ARROW_CURSOR + i; + // GLFW 3.3 does not support some cursors + if (GLFW_VERSION_MAJOR <= 3 && GLFW_VERSION_MINOR <= 3 && cursor > GLFW_VRESIZE_CURSOR) { + break; + } + standard_cursors[i] = glfwCreateStandardCursor(cursor); + } + auto inputPtr = std::make_unique(window); + auto windowPtr = std::make_unique( + *inputPtr, window, settings, width, height + ); + glfwSetWindowUserPointer(window, windowPtr.get()); + return {std::move(windowPtr), std::move(inputPtr)}; +} + +void display::clear() { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void display::clearDepth() { + glClear(GL_DEPTH_BUFFER_BIT); +} + +void display::setBgColor(glm::vec3 color) { + glClearColor(color.r, color.g, color.b, 1.0f); +} + +void display::setBgColor(glm::vec4 color) { + glClearColor(color.r, color.g, color.b, color.a); +} diff --git a/src/window/input.hpp b/src/window/input.hpp index 0c1ea008..9868c253 100644 --- a/src/window/input.hpp +++ b/src/window/input.hpp @@ -258,6 +258,7 @@ public: virtual void pollEvents() = 0; virtual const char* getClipboardText() const = 0; + virtual void setClipboardText(const char* str) = 0; virtual int getScroll() = 0; @@ -269,6 +270,7 @@ public: virtual CursorState getCursor() const = 0; + virtual bool isCursorLocked() const = 0; virtual void toggleCursor() = 0; virtual Bindings& getBindings() = 0;