2018-10-11 03:50:08 +03:00
|
|
|
#include "TerminalWidget.h"
|
2018-10-12 00:54:34 +03:00
|
|
|
#include "Font.h"
|
2018-10-11 03:50:08 +03:00
|
|
|
#include "Painter.h"
|
2018-10-11 13:33:03 +03:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
extern int g_fd;
|
|
|
|
TerminalWidget* g_tw;
|
2018-10-11 03:50:08 +03:00
|
|
|
|
|
|
|
TerminalWidget::TerminalWidget(Widget* parent)
|
|
|
|
: Widget(parent)
|
|
|
|
{
|
2018-10-11 13:33:03 +03:00
|
|
|
g_tw = this;
|
2018-10-12 00:54:34 +03:00
|
|
|
|
|
|
|
auto& font = Font::defaultFont();
|
2018-10-11 13:33:03 +03:00
|
|
|
|
2018-10-12 15:15:14 +03:00
|
|
|
setWindowRelativeRect({ 0, 0, (columns() * font.glyphWidth()) + 4, (rows() * font.glyphHeight()) + 4 });
|
2018-10-12 00:14:51 +03:00
|
|
|
|
2018-10-11 13:33:03 +03:00
|
|
|
printf("rekt: %d x %d\n", width(), height());
|
|
|
|
m_screen = new CharacterWithAttributes[rows() * columns()];
|
2018-10-11 03:50:08 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2018-10-11 13:33:03 +03:00
|
|
|
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);
|
2018-10-11 03:50:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
TerminalWidget::~TerminalWidget()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
CharacterWithAttributes& TerminalWidget::at(unsigned row, unsigned column)
|
|
|
|
{
|
|
|
|
ASSERT(m_screen);
|
|
|
|
ASSERT(row < m_rows);
|
|
|
|
ASSERT(column < m_columns);
|
|
|
|
return m_screen[row * columns() + column];
|
|
|
|
}
|
|
|
|
|
|
|
|
void TerminalWidget::onPaint(PaintEvent&)
|
|
|
|
{
|
|
|
|
Painter painter(*this);
|
2018-10-13 00:02:23 +03:00
|
|
|
painter.fillRect(rect(), Color::Black);
|
2018-10-11 03:50:08 +03:00
|
|
|
|
2018-10-12 00:54:34 +03:00
|
|
|
auto& font = Font::defaultFont();
|
|
|
|
|
2018-10-11 03:50:08 +03:00
|
|
|
char buf[2] = { 0, 0 };
|
|
|
|
for (unsigned row = 0; row < m_rows; ++row) {
|
2018-10-12 00:54:34 +03:00
|
|
|
int y = row * font.glyphHeight();
|
2018-10-11 03:50:08 +03:00
|
|
|
for (unsigned column = 0; column < m_columns; ++column) {
|
2018-10-12 00:54:34 +03:00
|
|
|
int x = column * font.glyphWidth();
|
2018-10-11 03:50:08 +03:00
|
|
|
buf[0] = at(row, column).character;
|
2018-10-12 00:54:34 +03:00
|
|
|
painter.drawText({ x + 2, y + 2, width(), font.glyphHeight() }, buf, Painter::TextAlignment::TopLeft, Color(0xa0, 0xa0, 0xa0));
|
2018-10-11 03:50:08 +03:00
|
|
|
}
|
|
|
|
}
|
2018-10-12 21:05:11 +03:00
|
|
|
|
|
|
|
if (m_belling)
|
|
|
|
painter.drawRect(rect(), Color::Red);
|
2018-10-11 03:50:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void TerminalWidget::onReceive(const ByteBuffer& buffer)
|
|
|
|
{
|
|
|
|
for (unsigned i = 0; i < buffer.size(); ++i) {
|
|
|
|
onReceive(buffer[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TerminalWidget::onReceive(byte ch)
|
|
|
|
{
|
2018-10-11 13:33:03 +03:00
|
|
|
//printf("receive %02x\n", ch);
|
|
|
|
auto scrollScreen = [&] () {
|
|
|
|
memmove(m_screen, m_screen + columns(), (m_rows - 1) * columns() * sizeof(CharacterWithAttributes));
|
2018-10-12 21:55:06 +03:00
|
|
|
memset(m_screen + (m_rows - 1) * columns(), ' ', columns() * sizeof(CharacterWithAttributes));
|
2018-10-11 13:33:03 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
auto addChar = [&] (byte ch) {
|
|
|
|
at(m_cursorRow, m_cursorColumn).character = ch;
|
2018-10-11 15:44:27 +03:00
|
|
|
if (++m_cursorColumn >= m_columns) {
|
2018-10-11 13:33:03 +03:00
|
|
|
m_cursorColumn = 0;
|
|
|
|
if (m_cursorRow < (m_rows - 1)) {
|
|
|
|
++m_cursorRow;
|
|
|
|
} else {
|
|
|
|
scrollScreen();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-10-12 21:05:11 +03:00
|
|
|
switch (ch) {
|
|
|
|
case '\n':
|
|
|
|
if (m_cursorRow < (m_rows - 1)) {
|
|
|
|
++m_cursorRow;
|
|
|
|
} else {
|
|
|
|
scrollScreen();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '\r':
|
|
|
|
m_cursorColumn = 0;
|
|
|
|
break;
|
|
|
|
case '\t':
|
|
|
|
// FIXME: Respect terminal tab stops.
|
|
|
|
while ((m_cursorColumn % 8) != 0 && m_cursorColumn < m_columns) {
|
|
|
|
addChar(' ');
|
|
|
|
break;
|
|
|
|
case '\a':
|
|
|
|
bell();
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
if (m_cursorColumn > 0) {
|
|
|
|
--m_cursorColumn;
|
|
|
|
at(m_cursorRow, m_cursorColumn).character = ' ';
|
|
|
|
}
|
|
|
|
break;
|
2018-10-12 23:50:28 +03:00
|
|
|
case 27:
|
|
|
|
printf("TerminalWidget: got escape!\n");
|
|
|
|
break;
|
2018-10-12 21:05:11 +03:00
|
|
|
default:
|
|
|
|
addChar(ch);
|
|
|
|
break;
|
2018-10-11 13:33:03 +03:00
|
|
|
}
|
2018-10-11 03:50:08 +03:00
|
|
|
}
|
2018-10-11 13:33:03 +03:00
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TerminalWidget::onKeyDown(KeyEvent& event)
|
|
|
|
{
|
2018-10-12 21:55:06 +03:00
|
|
|
if (event.text().isEmpty())
|
|
|
|
return;
|
|
|
|
write(g_fd, event.text().characters(), event.text().length());
|
2018-10-11 13:33:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void TerminalWidget::onKeyUp(KeyEvent& event)
|
|
|
|
{
|
|
|
|
return Widget::onKeyUp(event);
|
2018-10-11 03:50:08 +03:00
|
|
|
}
|
|
|
|
|
2018-10-12 21:05:11 +03:00
|
|
|
void TerminalWidget::bell()
|
|
|
|
{
|
|
|
|
if (m_belling)
|
|
|
|
stopTimer();
|
|
|
|
startTimer(250);
|
|
|
|
m_belling = true;
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TerminalWidget::onTimer(TimerEvent&)
|
|
|
|
{
|
|
|
|
m_belling = false;
|
|
|
|
stopTimer();
|
|
|
|
update();
|
|
|
|
}
|