From ab5266b924e2e0b9bc9f2d762a2a4d29de574e22 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 11 Oct 2018 12:33:03 +0200 Subject: [PATCH] Rage hacking on TerminalWidget. There's some really hideous plumbing with globals going on here, but my priority right now is getting a basic VT100 terminal emulator working. --- Widgets/AbstractScreen.cpp | 12 ++++++ Widgets/EventLoopSDL.cpp | 34 +++++++++++++++- Widgets/Painter.cpp | 9 ++++- Widgets/Peanut8x8.h | 1 + Widgets/TerminalWidget.cpp | 82 +++++++++++++++++++++++++++++++++----- Widgets/TerminalWidget.h | 7 +++- Widgets/Widget.cpp | 4 ++ Widgets/Widget.h | 2 + 8 files changed, 136 insertions(+), 15 deletions(-) diff --git a/Widgets/AbstractScreen.cpp b/Widgets/AbstractScreen.cpp index 203b743b89..72e02439d9 100644 --- a/Widgets/AbstractScreen.cpp +++ b/Widgets/AbstractScreen.cpp @@ -3,9 +3,12 @@ #include "Event.h" #include "Widget.h" #include +#include "TerminalWidget.h" static AbstractScreen* s_the; +extern TerminalWidget* g_tw; + AbstractScreen& AbstractScreen::the() { ASSERT(s_the); @@ -36,7 +39,16 @@ void AbstractScreen::event(Event& event) //printf("hit test for %d,%d found: %s{%p} %d,%d\n", me.x(), me.y(), result.widget->className(), result.widget, result.localX, result.localY); auto localEvent = make(event.type(), result.localX, result.localY, me.button()); result.widget->event(*localEvent); + return Object::event(event); } + + if (event.type() == Event::KeyDown || event.type() == Event::KeyUp) { + // FIXME: Implement proper focus. + Widget* focusedWidget = g_tw; + return focusedWidget->event(event); + } + + return Object::event(event); } void AbstractScreen::setRootWidget(Widget* widget) diff --git a/Widgets/EventLoopSDL.cpp b/Widgets/EventLoopSDL.cpp index 383a873158..e0d670ca76 100644 --- a/Widgets/EventLoopSDL.cpp +++ b/Widgets/EventLoopSDL.cpp @@ -3,6 +3,11 @@ #include #include "AbstractScreen.h" #include "Widget.h" +#include "TerminalWidget.h" +#include + +int g_fd; +extern TerminalWidget* g_tw; EventLoopSDL::EventLoopSDL() { @@ -14,7 +19,6 @@ EventLoopSDL::~EventLoopSDL() static inline MouseButton toMouseButton(byte sdlButton) { - printf("sdlbutton = %u\n", sdlButton); if (sdlButton == 1) return MouseButton::Left; if (sdlButton == 2) @@ -25,10 +29,15 @@ static inline MouseButton toMouseButton(byte sdlButton) return MouseButton::None; } +static inline int toKey(const SDL_Keysym& sym) +{ + return sym.sym; +} + void EventLoopSDL::waitForEvent() { SDL_Event sdlEvent; - while (SDL_WaitEvent(&sdlEvent) != 0) { + while (SDL_PollEvent(&sdlEvent) != 0) { switch (sdlEvent.type) { case SDL_QUIT: postEvent(nullptr, make()); @@ -49,7 +58,28 @@ void EventLoopSDL::waitForEvent() case SDL_MOUSEBUTTONUP: postEvent(&AbstractScreen::the(), make(Event::MouseUp, sdlEvent.button.x, sdlEvent.button.y, toMouseButton(sdlEvent.button.button))); return; + case SDL_KEYDOWN: + postEvent(&AbstractScreen::the(), make(Event::KeyDown, toKey(sdlEvent.key.keysym))); + return; + case SDL_KEYUP: + postEvent(&AbstractScreen::the(), make(Event::KeyUp, toKey(sdlEvent.key.keysym))); + return; } } + + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(g_fd, &rfds); + + struct timeval tv = { 0, 5000 }; + int rc = select(g_fd + 1, &rfds, NULL, NULL, &tv); + + //printf("select{%d} = %d\n", g_fd, rc); + + if (rc > 0) { + byte buf[1024]; + int nread = read(g_fd, buf, sizeof(buf)); + g_tw->onReceive(ByteBuffer::wrap(buf, nread)); + } } diff --git a/Widgets/Painter.cpp b/Widgets/Painter.cpp index b9a2e3f1f1..c5faad46dc 100644 --- a/Widgets/Painter.cpp +++ b/Widgets/Painter.cpp @@ -55,9 +55,14 @@ void Painter::drawText(const Rect& rect, const String& text, TextAlignment align int y = point.y() + row; dword* bits = scanline(y); for (unsigned i = 0; i < text.length(); ++i) { - if (text[i] == ' ') + byte ch = text[i]; + if (ch == ' ') continue; - const char* fontCharacter = Peanut8x8::font[text[i] - Peanut8x8::firstCharacter]; + if (ch < Peanut8x8::firstCharacter || ch > Peanut8x8::lastCharacter) { + printf("Font doesn't have 0x%02x ('%c')\n", ch, ch); + ASSERT_NOT_REACHED(); + } + const char* fontCharacter = Peanut8x8::font[ch - Peanut8x8::firstCharacter]; int x = point.x() + i * Peanut8x8::fontWidth; for (unsigned j = 0; j < Peanut8x8::fontWidth; ++j) { char fc = fontCharacter[row * Peanut8x8::fontWidth + j]; diff --git a/Widgets/Peanut8x8.h b/Widgets/Peanut8x8.h index 6d8535bf67..a7c97c2fab 100644 --- a/Widgets/Peanut8x8.h +++ b/Widgets/Peanut8x8.h @@ -3,6 +3,7 @@ namespace Peanut8x8 { static constexpr char firstCharacter = '!'; +static constexpr char lastCharacter = '~'; static constexpr byte fontWidth = 8; static constexpr byte fontHeight = 8; diff --git a/Widgets/TerminalWidget.cpp b/Widgets/TerminalWidget.cpp index 9de00cd9c8..1053c32cc1 100644 --- a/Widgets/TerminalWidget.cpp +++ b/Widgets/TerminalWidget.cpp @@ -1,18 +1,47 @@ #include "TerminalWidget.h" #include "Painter.h" +#include +#include +#include +#include + +extern int g_fd; +TerminalWidget* g_tw; TerminalWidget::TerminalWidget(Widget* parent) : Widget(parent) { + g_tw = this; + setRect({ 100, 300, columns() * 8, rows() * 8 }); - m_screen = new CharacterWithAttributes[rows() * columns() * 2]; + printf("rekt: %d x %d\n", width(), height()); + m_screen = new CharacterWithAttributes[rows() * columns()]; for (unsigned row = 0; row < m_rows; ++row) { for (unsigned column = 0; column < m_columns; ++column) { at(row, column).character = ' '; at(row, column).attribute = 0x07; } } - onReceive(String("Serenity/OS").toByteBuffer()); + g_fd = getpt(); + grantpt(g_fd); + unlockpt(g_fd); + char buf[1024]; + ptsname_r(g_fd, buf, sizeof(buf)); + + if (fork() == 0) { + close(g_fd); + setsid(); + int fd = open(buf, O_RDWR); + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + signal(SIGWINCH, SIG_IGN); + ioctl(fd, TIOCSCTTY); + execl("/bin/bash", "bash", nullptr); + ASSERT_NOT_REACHED(); + } + + signal(SIGCHLD, SIG_IGN); } TerminalWidget::~TerminalWidget() @@ -53,17 +82,52 @@ void TerminalWidget::onReceive(const ByteBuffer& buffer) void TerminalWidget::onReceive(byte ch) { - at(m_cursorRow, m_cursorColumn).character = ch; - printf("%2u,%2u -> ", m_cursorRow, m_cursorColumn); - if (++m_cursorColumn > m_columns) { - m_cursorColumn = 0; + //printf("receive %02x\n", ch); + auto scrollScreen = [&] () { + memmove(m_screen, m_screen + columns(), (m_rows - 1) * columns() * sizeof(CharacterWithAttributes)); + memset(m_screen + (m_rows - 1) * columns(), ' ', columns() * sizeof(CharacterWithAttributes) * 2); + }; + + auto addChar = [&] (byte ch) { + at(m_cursorRow, m_cursorColumn).character = ch; + if (++m_cursorColumn > m_columns) { + m_cursorColumn = 0; + if (m_cursorRow < (m_rows - 1)) { + ++m_cursorRow; + } else { + scrollScreen(); + } + } + }; + + if (ch == '\n') { if (m_cursorRow < (m_rows - 1)) { ++m_cursorRow; } else { - // FIXME: Scroll it! - ASSERT_NOT_REACHED(); + scrollScreen(); } + } else if (ch == '\r') { + m_cursorColumn = 0; + } else if (ch == '\t') { + while ((m_cursorColumn % 8) != 0 && m_cursorColumn < m_columns) { + addChar(' '); + } + } else { + addChar(ch); } - printf("%2u,%2u\n", m_cursorRow, m_cursorColumn); + update(); +} + +void TerminalWidget::onKeyDown(KeyEvent& event) +{ + char buf[] = { 0, 0 }; + buf[0] = event.key(); + write(g_fd, buf, 2); + return Widget::onKeyDown(event); +} + +void TerminalWidget::onKeyUp(KeyEvent& event) +{ + return Widget::onKeyUp(event); } diff --git a/Widgets/TerminalWidget.h b/Widgets/TerminalWidget.h index ba77c5d10a..2cd651b870 100644 --- a/Widgets/TerminalWidget.h +++ b/Widgets/TerminalWidget.h @@ -15,13 +15,16 @@ public: unsigned rows() const { return m_rows; } unsigned columns() const { return m_columns; } + + void onReceive(const ByteBuffer&); + void onReceive(byte); private: CharacterWithAttributes& at(unsigned row, unsigned column); virtual void onPaint(PaintEvent&) override; - void onReceive(const ByteBuffer&); - void onReceive(byte); + virtual void onKeyDown(KeyEvent&) override; + virtual void onKeyUp(KeyEvent&) override; unsigned m_columns { 80 }; unsigned m_rows { 25 }; diff --git a/Widgets/Widget.cpp b/Widgets/Widget.cpp index 9a1b66b16f..932d15de17 100644 --- a/Widgets/Widget.cpp +++ b/Widgets/Widget.cpp @@ -25,6 +25,7 @@ void Widget::event(Event& event) { switch (event.type()) { case Event::Paint: + m_hasPendingPaintEvent = false; return onPaint(static_cast(event)); case Event::Show: return onShow(static_cast(event)); @@ -85,6 +86,9 @@ void Widget::onMouseMove(MouseEvent&) void Widget::update() { + if (m_hasPendingPaintEvent) + return; + m_hasPendingPaintEvent = true; EventLoop::main().postEvent(this, make()); } diff --git a/Widgets/Widget.h b/Widgets/Widget.h index 7b49e92c0f..77c0f82738 100644 --- a/Widgets/Widget.h +++ b/Widgets/Widget.h @@ -49,4 +49,6 @@ private: Rect m_rect; Color m_backgroundColor; Color m_foregroundColor; + + bool m_hasPendingPaintEvent { false }; };