#include #include #include #include "neovision/ansi.h" #include "neovision/application.h" namespace neovision { std::weak_ptr TheApp; /* There's a bit of evil voodoo going on where with the TheApp pointer. Technically we don't "own" the TheApp pointer, the caller who instantiates us does - yet we set the TheApp pointer (a weak (observing) pointer) - in order to not force the user to do it. In order to do this, we create a shared 'this' ptr with a custom no-op Deleter (to avoid a double-free, since we get deleted already by the caller). */ Application::Application(): View(), m_selfPtr(this, [](Application*){}) { TheApp = m_selfPtr; } Application::~Application() { if (m_running == true) Stop(); } void Application::Initialize() { View::Initialize(); m_terminal.Initialize(); IO::Get().SetInputFunction([this](size_t b){ return m_terminal.Read(b); }); IO::Get().SetOutputFunction([this](const std::wstring& data){ m_terminal.Write(data); }); m_terminal.Unbuffer(); Size(m_terminal.Size()- Vector2D(1, 1), m_bgFillCharacter); m_terminal.SetOnResize([&](){ const Vector2D newSize = m_terminal.Size() - Vector2D(1, 1); Size(newSize, m_bgFillCharacter); OnResize(newSize); /** * @todo A complete redraw on resize seems to be needed for VTE based * terminals for some reason. Debug this some day probably never. */ ForceRedrawAll(); }); m_terminal.SetOnTerminate([&](){ Stop(); }); SetMouseReporting(MouseEventMode::ButtonOnly); m_inputParser.SetOnKeyEvent([this](const std::wstring& k){ OnKey(k); }); m_inputParser.SetOnMouseEvent([this](const MouseEventData& m){ OnMouse(m); }); InitializeViews(); } void Application::InitializeViews() { for (auto& view: m_views) { if (!view) return; if (view->Initialized() != true) view->Initialize(); } } void Application::Run() { if (!Initialized()) { throw std::runtime_error( "Application::Run() called before " "Application::Initialized() was called." ); } ClearScreen(); HideCursor(); m_running = true; InitializeViews(); while (m_running) { m_terminal.ProcessEvents(); OnDraw(); m_inputParser.Read(); } } void Application::Stop() { ClearScreen(); SetMouseReporting(MouseEventMode::None); ShowCursor(); IO::Get().Write(L"\r\n"); // Just in case we need a buffer flush. IO::Get().Write(ansi::MakeEscapeSequence(ansi::EscapeSequence::RIS)); m_terminal.Cleanup(); m_running = false; } Terminal& Application::Term() { return m_terminal; } void Application::OnMouse(const MouseEventData& mouseData) { /* Figure out what view is clicked. Reverse iterate so the top-most window always receives the click (the array should be sorted in z-order) */ for (auto viewIter = m_views.rbegin(); viewIter != m_views.rend(); ++viewIter) { auto& view = (*viewIter); const std::uint32_t x1 = view->Position().X(); const std::uint32_t x2 = x1 + view->Size().X(); const std::uint32_t y1 = view->Position().Y(); const std::uint32_t y2 = y1 + view->Size().Y(); if ( (mouseData.position.X() >= x1) && (mouseData.position.X() <= x2) && (mouseData.position.Y() >= y1) && (mouseData.position.Y() <= y2) ) { view->OnMouse(mouseData); break; } } } void Application::OnKey(const std::wstring& keyData) { if (keyData.empty()) return; } } // namespace neovision