TerminalWidget.cpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. #include "TerminalWidget.h"
  2. #include "Font.h"
  3. #include "Painter.h"
  4. #include <unistd.h>
  5. #include <signal.h>
  6. #include <sys/ioctl.h>
  7. #include <fcntl.h>
  8. extern int g_fd;
  9. TerminalWidget* g_tw;
  10. TerminalWidget::TerminalWidget(Widget* parent)
  11. : Widget(parent)
  12. {
  13. g_tw = this;
  14. setWindowRelativeRect({ 0, 0, int(columns() * font().glyphWidth()) + 4, int(rows() * font().glyphHeight()) + 4 });
  15. printf("rekt: %d x %d\n", width(), height());
  16. m_screen = new CharacterWithAttributes[rows() * columns()];
  17. for (unsigned row = 0; row < m_rows; ++row) {
  18. for (unsigned column = 0; column < m_columns; ++column) {
  19. at(row, column).character = ' ';
  20. at(row, column).attribute = 0x07;
  21. }
  22. }
  23. #if __APPLE__
  24. g_fd = posix_openpt(O_RDWR);
  25. #else
  26. g_fd = getpt();
  27. #endif
  28. grantpt(g_fd);
  29. unlockpt(g_fd);
  30. char buf[1024];
  31. ptsname_r(g_fd, buf, sizeof(buf));
  32. if (fork() == 0) {
  33. close(g_fd);
  34. setsid();
  35. int fd = open(buf, O_RDWR);
  36. dup2(fd, 0);
  37. dup2(fd, 1);
  38. dup2(fd, 2);
  39. signal(SIGWINCH, SIG_IGN);
  40. ioctl(fd, TIOCSCTTY);
  41. execl("/bin/bash", "bash", nullptr);
  42. ASSERT_NOT_REACHED();
  43. }
  44. signal(SIGCHLD, SIG_IGN);
  45. }
  46. TerminalWidget::~TerminalWidget()
  47. {
  48. }
  49. CharacterWithAttributes& TerminalWidget::at(unsigned row, unsigned column)
  50. {
  51. ASSERT(m_screen);
  52. ASSERT(row < m_rows);
  53. ASSERT(column < m_columns);
  54. return m_screen[row * columns() + column];
  55. }
  56. void TerminalWidget::paintEvent(PaintEvent&)
  57. {
  58. Painter painter(*this);
  59. painter.fillRect(rect(), Color::Black);
  60. char buf[2] = { 0, 0 };
  61. for (unsigned row = 0; row < m_rows; ++row) {
  62. int y = row * font().glyphHeight();
  63. for (unsigned column = 0; column < m_columns; ++column) {
  64. int x = column * font().glyphWidth();
  65. buf[0] = at(row, column).character;
  66. painter.drawText({ x + 2, y + 2, width(), font().glyphHeight() }, buf, Painter::TextAlignment::TopLeft, Color(0xa0, 0xa0, 0xa0));
  67. }
  68. }
  69. if (m_belling)
  70. painter.drawRect(rect(), Color::Red);
  71. }
  72. void TerminalWidget::onReceive(const ByteBuffer& buffer)
  73. {
  74. for (unsigned i = 0; i < buffer.size(); ++i) {
  75. onReceive(buffer[i]);
  76. }
  77. }
  78. void TerminalWidget::onReceive(byte ch)
  79. {
  80. //printf("receive %02x\n", ch);
  81. auto scrollScreen = [&] () {
  82. memmove(m_screen, m_screen + columns(), (m_rows - 1) * columns() * sizeof(CharacterWithAttributes));
  83. memset(m_screen + (m_rows - 1) * columns(), ' ', columns() * sizeof(CharacterWithAttributes));
  84. };
  85. auto addChar = [&] (byte ch) {
  86. at(m_cursorRow, m_cursorColumn).character = ch;
  87. if (++m_cursorColumn >= m_columns) {
  88. m_cursorColumn = 0;
  89. if (m_cursorRow < (m_rows - 1)) {
  90. ++m_cursorRow;
  91. } else {
  92. scrollScreen();
  93. }
  94. }
  95. };
  96. switch (ch) {
  97. case '\n':
  98. if (m_cursorRow < (m_rows - 1)) {
  99. ++m_cursorRow;
  100. } else {
  101. scrollScreen();
  102. }
  103. break;
  104. case '\r':
  105. m_cursorColumn = 0;
  106. break;
  107. case '\t':
  108. // FIXME: Respect terminal tab stops.
  109. while ((m_cursorColumn % 8) != 0 && m_cursorColumn < m_columns) {
  110. addChar(' ');
  111. break;
  112. case '\a':
  113. bell();
  114. break;
  115. case 8:
  116. if (m_cursorColumn > 0) {
  117. --m_cursorColumn;
  118. at(m_cursorRow, m_cursorColumn).character = ' ';
  119. }
  120. break;
  121. case 27:
  122. printf("TerminalWidget: got escape!\n");
  123. break;
  124. default:
  125. addChar(ch);
  126. break;
  127. }
  128. }
  129. update();
  130. }
  131. void TerminalWidget::keyDownEvent(KeyEvent& event)
  132. {
  133. if (event.text().is_empty())
  134. return;
  135. write(g_fd, event.text().characters(), event.text().length());
  136. }
  137. void TerminalWidget::keyUpEvent(KeyEvent& event)
  138. {
  139. return Widget::keyUpEvent(event);
  140. }
  141. void TerminalWidget::bell()
  142. {
  143. if (m_belling)
  144. stopTimer();
  145. startTimer(250);
  146. m_belling = true;
  147. update();
  148. }
  149. void TerminalWidget::timerEvent(TimerEvent&)
  150. {
  151. m_belling = false;
  152. stopTimer();
  153. update();
  154. }