/*
GameEngine and Editor
by John Ryland
Copyright (c) 2023
*/
////////////////////////////////////////////////////////////////////////////////////
// Terminal View
#include "TerminalView.h"
#include "IUiSystem.h"
#include "Utilities.h"
#include "imgui.h"
#include "imgui_internal.h"
#include <chrono>
#include <cstdio>
#include <string>
namespace GameEngine {
TerminalView::TerminalView(const ApplicationFramework::IApplication& app)
: m_application(app)
, m_emulator(app)
{
}
void TerminalView::AddShowMenuItem()
{
ImGui::MenuItem("Show Terminal", NULL, &m_open);
}
void TerminalView::Update()
{
if (m_open)
{
ImGui::SetNextWindowBgAlpha(0.75f); // Transparent background
if (ImGui::Begin((std::string(m_emulator.m_title) + "###Terminal").c_str(), &m_open))
{
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
ImGui::PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 2);
ImGui::SetNextFrameWantCaptureKeyboard(true);
ImVec2 termSiz = ImGui::GetContentRegionAvail();
if (!m_emulator.m_altScreenBuffer)
termSiz.y = Terminal::TerminalEmulator::MAX_SCROLL_BACK_LINES * m_emulator.m_fontHeight;
ImGuiWindow* termWin = ImGui::GetCurrentWindow();
ImGuiID termId = termWin->GetID("termwin");
ImGui::BeginChild(termId, termSiz, ImGuiWindowFlags_AlwaysVerticalScrollbar);
if (m_emulator.m_needScrollToY)
{
ImGui::SetScrollFromPosY(termWin, ((m_emulator.m_cursorY + 5) * m_emulator.m_fontHeight) - termWin->Scroll.y, 1.0);
m_emulator.m_needScrollToY = false;
}
auto siz = ImGui::GetContentRegionAvail();
m_emulator.UpdateSize((int)siz.x, (int)siz.y);
m_emulator.Update();
ImGui::EndChild();
ImGui::PopStyleVar();
ImGui::PopStyleColor();
}
ImGui::End();
}
}
void TerminalView::Initialize()
{
m_emulator.Initialize();
}
void TerminalView::Shutdown()
{
m_emulator.Shutdown();
}
TerminalView::Emulator::Emulator(const ApplicationFramework::IApplication& app)
: m_application(app)
{
}
// virtual
void TerminalView::Emulator::HandleInput()
{
ImGuiIO& io = ImGui::GetIO();
auto shift = io.KeyShift;
auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows))
{
if (!ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Enter)))
SendChar('\n');
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Tab)))
SendChar('\t');
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace)))
SendChar('\b');
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
SendChar('\033');
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert)))
m_process.SendBytes("\033[2~", 4);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete)))
m_process.SendBytes("\033[3~", 4);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp)))
m_process.SendBytes("\033[5~", 4);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown)))
m_process.SendBytes("\033[6~", 4);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_F1)))
m_process.SendBytes("\033[11~", 5);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_F2)))
m_process.SendBytes("\033[12~", 5);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_F3)))
m_process.SendBytes("\033[13~", 5);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_F4)))
m_process.SendBytes("\033[14~", 5);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_F5)))
m_process.SendBytes("\033[15~", 5);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_F6)))
m_process.SendBytes("\033[17~", 5);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_F7)))
m_process.SendBytes("\033[18~", 5);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_F8)))
m_process.SendBytes("\033[19~", 5);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_F9)))
m_process.SendBytes("\033[20~", 5);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_F10)))
m_process.SendBytes("\033[21~", 5);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_F11)))
m_process.SendBytes("\033[23~", 5);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_F12)))
m_process.SendBytes("\033[24~", 5);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)))
m_process.SendBytes("\033[A", 3);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)))
m_process.SendBytes("\033[B", 3);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)))
m_process.SendBytes("\033[C", 3);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)))
m_process.SendBytes("\033[D", 3);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End)))
m_process.SendBytes("\033[E", 3);
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home)))
m_process.SendBytes("\033[F", 3);
// Send CTRL-C
if (ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C)))
SendChar('\003');
for (int i = 0; i < ImGui::GetIO().InputQueueCharacters.Size; i++)
{
ImWchar c = ImGui::GetIO().InputQueueCharacters[i];
if (c != 0 && (c == '\n' || c >= 32))
SendChar(c); // TODO: support internationalization
}
}
}
// virtual
void TerminalView::Emulator::Display()
{
ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos();
// Stop it moving when resizing
cursorScreenPos.y += -m_fontHeight + (int)ImGui::GetContentRegionAvail().y % m_fontHeight;
int x = cursorScreenPos.x;
int y = cursorScreenPos.y;
auto* drawList = ImGui::GetWindowDrawList();
auto textCol = ImGui::GetColorU32(ImGuiCol_Text) | 0xFF000000;
auto& font = ImGui::GetIO().Fonts->Fonts[m_application.UiSystem()->FixedFontIndex()];
//ImGui::Text("%5i", j); // Line numbers
for (int j = 0; j < m_currentLines; ++j)
{
for (int i = 0; i < m_currentColumns; ++i)
{
char ch = Line(j)[i].ch;
uint32_t fgCol = Line(j)[i].foregroundColor;
uint32_t bgCol = Line(j)[i].backgroundColor;
ImVec2 pos(x + ((m_leftMargin + i) * m_fontWidth), y + j * m_fontHeight);
ImVec2 pos2(pos.x + m_fontWidth, pos.y + m_fontHeight);
if (bgCol)
drawList->AddRectFilled(pos, pos2, bgCol);
if (::isprint(ch))
font->RenderChar(drawList, -1.0, pos, fgCol ? fgCol : textCol, ch);
}
}
}
// virtual
void TerminalView::Emulator::DrawCursor()
{
// Draw the flashing text cursor
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows))
{
static uint64_t mStartTime = 0;
auto timeEnd = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
auto elapsed = timeEnd - mStartTime;
if (elapsed > 400)
{
auto cx = (m_leftMargin + Utilities::clamp(m_cursorX, 0, m_currentColumns - 1)) * m_fontWidth;
auto cy = (0 - Utilities::clamp(m_cursorY, 0, m_currentLines - 1)) * m_fontHeight;
ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos();
// Stop it moving when resizing
cursorScreenPos.y += -m_fontHeight + (int)ImGui::GetContentRegionAvail().y % m_fontHeight;
ImVec2 pos(cursorScreenPos.x + cx, cursorScreenPos.y - cy);
ImVec2 pos2(pos.x + m_fontWidth, pos.y + m_fontHeight);
ImGui::GetWindowDrawList()->AddRectFilled(pos, pos2, 0xFFFFFFFF);
if (elapsed > 800)
mStartTime = timeEnd;
}
}
}
} // GameEngine namespace