21#include "pedigree/kernel/ActivityDiagnostics.h"
22#include "pedigree/kernel/LockGuard.h"
23#include "pedigree/kernel/Log.h"
24#include "pedigree/kernel/machine/InputManager.h"
25#include "pedigree/kernel/machine/Machine.h"
26#include "pedigree/kernel/machine/Vga.h"
27#include "pedigree/kernel/process/Mutex.h"
28#include "pedigree/kernel/process/Scheduler.h"
29#include "pedigree/kernel/process/Thread.h"
30#include "pedigree/kernel/processor/MemoryRegion.h"
31#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
32#include "pedigree/kernel/processor/Processor.h"
33#include "pedigree/kernel/processor/ProcessorInformation.h"
34#include "pedigree/kernel/processor/VirtualAddressSpace.h"
35#include "pedigree/kernel/processor/types.h"
36#include "pedigree/kernel/time/Time.h"
37#include "pedigree/kernel/utilities/Buffer.h"
38#include "pedigree/kernel/utilities/StaticString.h"
39#include "pedigree/kernel/utilities/String.h"
40#include "pedigree/kernel/utilities/utility.h"
42#include "modules/system/vfs/File.h"
48#define ALT_KEY (1ULL << 60)
49#define SHIFT_KEY (1ULL << 61)
50#define CTRL_KEY (1ULL << 62)
51#define SPECIAL_KEY (1ULL << 63)
53static int startFlipThread(
void* param);
56 :
File(
str, 0, 0, 0, inode, pParentFS, 0, pParent),
57 m_bInitialised(false),
60 m_bParenthesis(false),
62 m_bQuestionMark(false),
75 m_Backbuffer(
"TextIO Backbuffer"),
84 m_VtermScrollbackLines(0),
86 m_OutBuffer(TEXTIO_BUFFER_SIZE),
96 m_bOwnsConsole(false),
97 m_InputMode(
TextIO::Standard) {
98 size_t backbufferSize = BACKBUFFER_STRIDE * BACKBUFFER_ROWS *
sizeof(VgaCell);
103 m_Backbuffer, backbufferPages, 0,
105 ERROR(
"TextIO: failed to allocate backbuffer!");
107 m_pBackbuffer =
reinterpret_cast<VgaCell*
>(m_Backbuffer.virtualAddress());
113 setPermissionsOnly(FILE_GR | FILE_GW | FILE_UR | FILE_UW);
127 m_bInitialised =
false;
140 if (m_bInitialised) {
143 m_bInitialised =
false;
152 m_bInitialised =
false;
154 m_bControlSeq =
false;
157 m_bQuestionMark =
false;
160 m_CursorX = m_CursorY = 0;
161 m_ScrollStart = m_ScrollEnd = 0;
162 m_LeftMargin = m_RightMargin = 0;
163 m_SavedCursorX = m_SavedCursorY = 0;
165 ByteSet(m_Params, 0,
sizeof(
size_t) * MAX_TEXTIO_PARAMS);
166 ByteSet(m_TabStops, 0, BACKBUFFER_STRIDE);
167 m_InputMode = Standard;
169 m_pVga = Machine::instance().
getVga(0);
172 m_pFramebuffer = *m_pVga;
173 if (m_pFramebuffer != 0) {
176 ByteSet(m_pFramebuffer, 0,
183 m_bInitialised =
true;
191 m_CurrentModes = AnsiVt52 | CharacterSetG0;
196 for (
size_t i = 0; i < BACKBUFFER_STRIDE; i += 8)
205 if (m_bInitialised) {
210 m_FlipThread.adopt(flipThread);
211 m_FlipThread->setName(
"TextIO flip thread");
214 return m_bInitialised;
217void TextIO::initialiseVterm() {
222 if (rows > BACKBUFFER_ROWS)
223 rows = BACKBUFFER_ROWS;
224 if (cols > BACKBUFFER_STRIDE)
225 cols = BACKBUFFER_STRIDE;
229 m_pVterm = vterm_new(
static_cast<int>(rows),
static_cast<int>(cols));
231 ERROR(
"TextIO: failed to allocate libvterm state");
235 vterm_set_utf8(m_pVterm, 1);
236 m_pVtermScreen = vterm_obtain_screen(m_pVterm);
237 m_pVtermState = vterm_obtain_state(m_pVterm);
239 static const VTermScreenCallbacks callbacks = {
240 vtermDamage, vtermMoveRect, vtermMoveCursor, vtermSetTermProp, vtermBell,
241 vtermResize, vtermScrollbackPush, vtermScrollbackPop, vtermScrollbackClear};
242 vterm_screen_set_callbacks(m_pVtermScreen, &callbacks,
this);
243 vterm_screen_enable_reflow(m_pVtermScreen,
true);
244 vterm_screen_enable_altscreen(m_pVtermScreen,
true);
245 vterm_screen_set_damage_merge(m_pVtermScreen, VTERM_DAMAGE_SCROLL);
246 vterm_output_set_callback(m_pVterm, vtermOutput,
this);
247 vterm_screen_reset(m_pVtermScreen, 1);
249 m_VtermScrollbackLines = 0;
250 ByteSet(m_VtermDirty, 1,
sizeof(m_VtermDirty));
251 syncVtermRect({0,
static_cast<int>(rows), 0,
static_cast<int>(cols)});
254void TextIO::destroyVterm() {
256 vterm_free(m_pVterm);
260 m_VtermScrollbackLines = 0;
263void TextIO::syncVtermRect(VTermRect rect) {
264 if (!m_pVtermScreen || !m_pBackbuffer)
267 if (rect.start_row < 0)
269 if (rect.start_col < 0)
271 if (rect.end_row > BACKBUFFER_ROWS)
272 rect.end_row = BACKBUFFER_ROWS;
273 if (rect.end_col > BACKBUFFER_STRIDE)
274 rect.end_col = BACKBUFFER_STRIDE;
276 for (
int row = rect.start_row; row < rect.end_row; ++row) {
277 for (
int col = rect.start_col; col < rect.end_col; ++col) {
278 VTermPos pos = {row, col};
279 VTermScreenCell cell;
280 if (vterm_screen_get_cell(m_pVtermScreen, pos, &cell)) {
281 copyVtermCell(cell, m_pBackbuffer[row * BACKBUFFER_STRIDE + col]);
288void TextIO::syncVtermCell(VTermPos pos) {
289 syncVtermRect({pos.row, pos.row + 1, pos.col, pos.col + 1});
292void TextIO::markVtermRect(VTermRect rect) {
293 if (rect.start_row < 0)
295 if (rect.start_col < 0)
297 if (rect.end_row > BACKBUFFER_ROWS)
298 rect.end_row = BACKBUFFER_ROWS;
299 if (rect.end_col > BACKBUFFER_STRIDE)
300 rect.end_col = BACKBUFFER_STRIDE;
302 for (
int row = rect.start_row; row < rect.end_row; ++row)
303 for (
int col = rect.start_col; col < rect.end_col; ++col)
304 m_VtermDirty[row * BACKBUFFER_STRIDE + col] = 1;
307void TextIO::markVtermCell(VTermPos pos) {
308 if (pos.row >= 0 && pos.row < BACKBUFFER_ROWS && pos.col >= 0 && pos.col < BACKBUFFER_STRIDE)
309 m_VtermDirty[pos.row * BACKBUFFER_STRIDE + pos.col] = 1;
312TextIO::VgaColour TextIO::vtermColour(VTermColor colour,
bool foreground)
const {
313 if ((foreground && VTERM_COLOR_IS_DEFAULT_FG(&colour)) ||
314 (!foreground && VTERM_COLOR_IS_DEFAULT_BG(&colour)))
315 return foreground ? LightGrey : Black;
318 vterm_screen_convert_color_to_rgb(m_pVtermScreen, &colour);
320 static const uint8_t palette[16][3] = {
321 {0, 0, 0}, {0, 0, 170}, {0, 170, 0}, {0, 170, 170}, {170, 0, 0}, {170, 0, 170},
322 {170, 85, 0}, {170, 170, 170}, {85, 85, 85}, {85, 85, 255}, {85, 255, 85}, {85, 255, 255},
323 {255, 85, 85}, {255, 85, 255}, {255, 255, 85}, {255, 255, 255}};
325 uint8_t red = colour.rgb.red, green = colour.rgb.green, blue = colour.rgb.blue;
326 unsigned bestDistance = ~0U;
328 for (
size_t i = 0; i < 16; ++i) {
329 int dr =
static_cast<int>(red) - palette[i][0];
330 int dg =
static_cast<int>(green) - palette[i][1];
331 int db =
static_cast<int>(blue) - palette[i][2];
332 unsigned distance =
static_cast<unsigned>(dr * dr + dg * dg + db * db);
333 if (distance < bestDistance) {
334 bestDistance = distance;
338 return static_cast<VgaColour
>(best);
341void TextIO::copyVtermCell(
const VTermScreenCell& source, VgaCell& destination) {
342 destination.character = source.chars[0] ?
translate(source.chars[0]) :
' ';
343 destination.fore = vtermColour(source.fg,
true);
344 destination.back = vtermColour(source.bg,
false);
345 destination.flags = 0;
346 if (source.attrs.bold)
347 destination.flags |=
Bright;
348 if (source.attrs.blink)
349 destination.flags |= Blink;
350 if (source.attrs.reverse)
352 destination.hidden = source.attrs.conceal;
355void TextIO::storeVtermScrollback(
const VTermScreenCell* cells,
int cols) {
356 if (!cells || cols <= 0)
359 if (m_VtermScrollbackLines < VTERM_SCROLLBACK_LINES)
360 ++m_VtermScrollbackLines;
362 MemoryCopy(m_VtermScrollback[0], m_VtermScrollback[1],
363 (VTERM_SCROLLBACK_LINES - 1) *
sizeof(m_VtermScrollback[0]));
365 VgaCell* line = m_VtermScrollback[m_VtermScrollbackLines - 1];
366 ByteSet(line, 0,
sizeof(m_VtermScrollback[0]));
367 size_t count =
static_cast<size_t>(cols);
368 if (count > BACKBUFFER_STRIDE)
369 count = BACKBUFFER_STRIDE;
370 for (
size_t col = 0; col < count; ++col)
371 copyVtermCell(cells[col], line[col]);
374int TextIO::popVtermScrollback(VTermScreenCell* cells,
int cols) {
375 if (!cells || cols <= 0 || !m_VtermScrollbackLines)
378 size_t count =
static_cast<size_t>(cols);
379 if (count > BACKBUFFER_STRIDE)
380 count = BACKBUFFER_STRIDE;
381 for (
size_t col = 0; col < count; ++col) {
382 VgaCell& source = m_VtermScrollback[m_VtermScrollbackLines - 1][col];
383 VTermScreenCell& destination = cells[col];
384 ByteSet(&destination, 0,
sizeof(destination));
385 destination.chars[0] = source.character;
386 destination.width = 1;
387 destination.fg.type = VTERM_COLOR_INDEXED;
388 static const uint8_t vtermPalette[16] = {0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15};
389 destination.fg.indexed.idx = vtermPalette[source.fore & 0x0F];
390 destination.bg.type = VTERM_COLOR_INDEXED;
391 destination.bg.indexed.idx = vtermPalette[source.back & 0x0F];
392 destination.attrs.bold = (source.flags &
Bright) != 0;
393 destination.attrs.blink = (source.flags & Blink) != 0;
394 destination.attrs.reverse = (source.flags &
Inverse) != 0;
395 destination.attrs.conceal = source.hidden;
397 --m_VtermScrollbackLines;
401void TextIO::vtermOutput(
const char* bytes,
size_t length,
void* user) {
404 WARNING(
"TextIO: output buffer is full or closed, dropping terminal response");
407int TextIO::vtermDamage(VTermRect rect,
void* user) {
409 console->syncVtermRect(rect);
413int TextIO::vtermMoveRect(VTermRect dest, VTermRect src,
void* user) {
415 if (!console->m_pBackbuffer)
417 int rows = dest.end_row - dest.start_row;
418 int cols = dest.end_col - dest.start_col;
419 if (rows <= 0 || cols <= 0 || src.end_row - src.start_row != rows ||
420 src.end_col - src.start_col != cols)
423 int rowStep = dest.start_row > src.start_row ? -1 : 1;
424 for (
int i = 0; i < rows; ++i) {
425 int row = rowStep > 0 ? i : rows - 1 - i;
426 for (
int j = 0; j < cols; ++j) {
427 int sourceCol = src.start_col + j;
428 int destCol = dest.start_col + j;
429 if (dest.start_col > src.start_col) {
430 sourceCol = src.end_col - 1 - j;
431 destCol = dest.end_col - 1 - j;
433 VTermPos source = {src.start_row + row, sourceCol};
434 VTermPos target = {dest.start_row + row, destCol};
435 if (source.row >= 0 && source.row < BACKBUFFER_ROWS && source.col >= 0 &&
436 source.col < BACKBUFFER_STRIDE && target.row >= 0 && target.row < BACKBUFFER_ROWS &&
437 target.col >= 0 && target.col < BACKBUFFER_STRIDE)
438 console->m_pBackbuffer[target.row * BACKBUFFER_STRIDE + target.col] =
439 console->m_pBackbuffer[source.row * BACKBUFFER_STRIDE + source.col];
442 console->markVtermRect(dest);
446int TextIO::vtermMoveCursor(VTermPos pos, VTermPos oldPos,
int visible,
void* user) {
448 console->m_CursorX = pos.col;
449 console->m_CursorY = pos.row;
450 console->markVtermCell(oldPos);
451 console->markVtermCell(pos);
452 if (console->m_pVga && console->
isPrimary())
453 console->m_pVga->
moveCursor(pos.col, pos.row);
457int TextIO::vtermSetTermProp(VTermProp prop, VTermValue* value,
void* user) {
459 if (prop == VTERM_PROP_REVERSE && value && value->boolean)
460 console->m_CurrentModes |= Screen;
461 else if (prop == VTERM_PROP_REVERSE)
462 console->m_CurrentModes &= ~Screen;
466int TextIO::vtermBell(
void*) {
470int TextIO::vtermResize(
int rows,
int cols,
void* user) {
472 if (rows < 1 || cols < 1)
474 console->markVtermRect({0, rows, 0, cols});
478int TextIO::vtermScrollbackPush(
int cols,
const VTermScreenCell* cells,
void* user) {
479 static_cast<TextIO*
>(user)->storeVtermScrollback(cells, cols);
483int TextIO::vtermScrollbackPop(
int cols, VTermScreenCell* cells,
void* user) {
484 return static_cast<TextIO*
>(user)->popVtermScrollback(cells, cols);
487int TextIO::vtermScrollbackClear(
void* user) {
488 static_cast<TextIO*
>(user)->m_VtermScrollbackLines = 0;
493 if (!m_bInitialised) {
494 FATAL(
"TextIO misused: successfully call initialise() first.");
498 ERROR(
"TextIO: null string passed in.");
504 bool usedVterm =
false;
511 vterm_input_write(m_pVterm, s, len);
512 vterm_screen_flush_damage(m_pVtermScreen);
526 const char* orig = s;
527 while ((*s) && (len--)) {
529 uint8_t
byte = *
reinterpret_cast<const uint8_t*
>(s);
544 "TextIO: expected a continuation byte, but didn't "
554 if (((
byte & 0xC0) != 0x80) && (s != orig)) {
561 ERROR(
"TextIO: invalid UTF8 sequence encountered.");
565 ERROR(
"TextIO: too many continuation bytes for a UTF8 sequence!");
570 }
else if ((
byte & 0xC0) == 0xC0) {
573 uint8_t thisByte = *
reinterpret_cast<const uint8_t*
>(s);
574 if ((thisByte & 0xF8) == 0xF0) {
578 }
else if ((thisByte & 0xF0) == 0xE0) {
582 }
else if ((thisByte & 0xE0) == 0xC0) {
588 "TextIO: invalid UTF8 leading byte (possible 5- or "
589 "6-byte sequence?)");
595 }
else if ((
byte & 0x80) == 0x80) {
596 ERROR(
"TextIO: invalid ASCII character " <<
byte <<
" (not a UTF8 leading byte)");
602 if (m_bControlSeq && m_bBracket) {
617 if (m_CurrentModes & LineFeedNewLine)
627 m_bQuestionMark =
true;
640 m_Params[m_CurrentParam] = (m_Params[m_CurrentParam] * 10) + (
m_nCharacter -
'0');
646 if (m_CurrentParam >= MAX_TEXTIO_PARAMS)
647 FATAL(
"TextIO: too many parameters!");
653 if (m_bParams && m_Params[0])
654 m_CursorY -= m_Params[0];
659 if (m_CursorY < m_ScrollStart)
660 m_CursorY = m_ScrollStart;
662 m_bControlSeq =
false;
667 if (m_bParams && m_Params[0])
668 m_CursorY += m_Params[0];
672 if (m_CursorY > m_ScrollEnd)
673 m_CursorY = m_ScrollEnd;
675 m_bControlSeq =
false;
680 if (m_bParams && m_Params[0])
681 m_CursorX += m_Params[0];
685 if (m_CursorX >= m_RightMargin)
686 m_CursorX = m_RightMargin - 1;
688 m_bControlSeq =
false;
694 if (m_bParams && m_Params[0])
695 m_CursorX -= m_Params[0];
700 if (m_CursorX < m_LeftMargin)
701 m_CursorX = m_LeftMargin;
703 m_bControlSeq =
false;
710 size_t xmove = m_Params[1] ? m_Params[1] - 1 : 0;
711 size_t ymove = m_Params[0] ? m_Params[0] - 1 : 0;
714 goHome(xmove, ymove);
720 m_bControlSeq =
false;
724 if ((!m_bParams) || (!m_Params[0])) {
726 }
else if (m_Params[0] == 1) {
728 }
else if (m_Params[0] == 2) {
733 m_bControlSeq =
false;
737 if ((!m_bParams) || (!m_Params[0])) {
739 }
else if (m_Params[0] == 1) {
742 }
else if (m_Params[0] == 2) {
746 m_bControlSeq =
false;
752 "TextIO: Device Attributes command with non-zero "
757 const char* attribs =
"\033[?1;2c";
760 m_bControlSeq =
false;
765 if (m_Params[0] == 3) {
766 ByteSet(m_TabStops, 0, BACKBUFFER_STRIDE);
769 m_TabStops[m_CursorX] = 0;
771 m_bControlSeq =
false;
776 int modesToChange = 0;
778 if (m_bQuestionMark & m_bParams) {
779 for (
size_t i = 0; i <= m_CurrentParam; ++i) {
780 switch (m_Params[i]) {
782 modesToChange |= CursorKey;
785 modesToChange |= AnsiVt52;
788 modesToChange |= Column;
791 modesToChange |= Scrolling;
794 modesToChange |= Screen;
797 modesToChange |= Origin;
800 modesToChange |= AutoWrap;
803 modesToChange |= AutoRepeat;
806 modesToChange |= Interlace;
810 "TextIO: unknown 'DEC Private Mode "
812 << m_Params[i] <<
"'");
816 }
else if (m_bParams) {
817 for (
size_t i = 0; i <= m_CurrentParam; ++i) {
818 switch (m_Params[i]) {
820 modesToChange |= LineFeedNewLine;
823 WARNING(
"TextIO: unknown 'Set Mode' mode '" << m_Params[i] <<
"'");
831 m_CurrentModes |= modesToChange;
834 if (modesToChange & Origin) {
836 m_CursorX = m_LeftMargin;
837 m_CursorY = m_ScrollStart;
838 }
else if (modesToChange & Column) {
839 m_RightMargin = BACKBUFFER_COLS_WIDE;
847 m_ScrollEnd = BACKBUFFER_ROWS - 1;
855 m_CurrentModes &= ~(modesToChange);
858 if (modesToChange & Origin) {
862 }
else if (modesToChange & Column) {
863 m_RightMargin = BACKBUFFER_COLS_NORMAL;
871 m_ScrollEnd = BACKBUFFER_ROWS - 1;
879 m_bControlSeq =
false;
883 for (
size_t i = 0; i <= m_CurrentParam; ++i) {
884 switch (m_Params[i]) {
893 if (!(m_CurrentModes &
Bright)) {
899 if (m_CurrentModes &
Bright) {
900 m_CurrentModes &= ~Bright;
906 if (!(m_CurrentModes & Blink)) {
907 m_CurrentModes |= Blink;
912 if (!(m_CurrentModes &
Inverse)) {
925 setColour(&m_Fore, m_Params[i] - 30, m_CurrentModes &
Bright);
928 if (m_Params[i + 1] == 5) {
929 setColour(&m_Fore, m_Params[i + 2], m_CurrentModes &
Bright);
934 setColour(&m_Back, 7, m_CurrentModes &
Bright);
945 setColour(&m_Back, m_Params[i] - 40);
948 if (m_Params[i + 1] == 5) {
949 setColour(&m_Back, m_Params[i + 2]);
954 setColour(&m_Back, 0);
965 setColour(&m_Fore, m_Params[i] - 90,
true);
976 setColour(&m_Back, m_Params[i] - 100,
true);
981 "TextIO: unhandled 'Set Attribute Mode' "
983 <<
Dec << m_Params[i] <<
Hex <<
".");
987 m_bControlSeq =
false;
991 switch (m_Params[0]) {
994 const char* status =
"\033[0n";
1002 ssize_t reportX = m_CursorX + 1;
1003 ssize_t reportY = m_CursorY + 1;
1005 if (m_CurrentModes & Origin) {
1009 if ((reportX > m_LeftMargin) && (reportX <= m_RightMargin))
1010 reportX -= m_LeftMargin;
1011 if ((reportY > m_ScrollStart) && (reportY <= m_ScrollEnd))
1012 reportY -= m_ScrollStart;
1015 response.append(reportY);
1016 response.append(
";");
1017 response.append(reportX);
1018 response.append(
"R");
1023 NOTICE(
"TextIO: unknown device status request " <<
Dec << m_Params[0] <<
Hex <<
".");
1026 m_bControlSeq =
false;
1036 "TextIO: dropping command after seeing 'p' command "
1037 "sequence terminator.");
1038 m_bControlSeq =
false;
1044 m_bControlSeq =
false;
1049 m_ScrollStart = m_Params[0] - 1;
1050 m_ScrollEnd = m_Params[1] - 1;
1052 if (m_ScrollStart >= BACKBUFFER_ROWS)
1053 m_ScrollStart = BACKBUFFER_ROWS - 1;
1054 if (m_ScrollEnd >= BACKBUFFER_ROWS)
1055 m_ScrollEnd = BACKBUFFER_ROWS - 1;
1058 m_ScrollEnd = BACKBUFFER_ROWS - 1;
1061 if (m_ScrollStart > m_ScrollEnd) {
1062 size_t tmp = m_ScrollStart;
1063 m_ScrollStart = m_ScrollEnd;
1069 m_bControlSeq =
false;
1073 m_SavedCursorX = m_CursorX;
1074 m_SavedCursorY = m_CursorY;
1075 m_bControlSeq =
false;
1079 m_CursorX = m_SavedCursorX;
1080 m_CursorY = m_SavedCursorY;
1081 m_bControlSeq =
false;
1086 if (m_Params[0] > 1) {
1088 "TextIO: invalid 'sol' parameter for 'Request "
1089 "Terminal Parameters'");
1100 const char* termparms = 0;
1102 termparms =
"\033[3;1;1;120;120;1;0x";
1104 termparms =
"\033[2;1;1;120;120;1;0x";
1107 m_bControlSeq =
false;
1112 m_bControlSeq =
false;
1116 ERROR(
"TextIO: unknown control sequence character '" <<
m_nCharacter <<
"'!");
1117 m_bControlSeq =
false;
1120 }
else if (m_bControlSeq && (!m_bBracket) && (!m_bParenthesis)) {
1127 if (m_CursorY > m_ScrollStart)
1129 m_bControlSeq =
false;
1133 if (m_CursorY < m_ScrollEnd)
1135 m_bControlSeq =
false;
1140 if (m_CursorX >= m_RightMargin)
1141 m_CursorX = m_RightMargin - 1;
1142 m_bControlSeq =
false;
1146 if (m_CurrentModes & AnsiVt52) {
1151 if (m_CursorX > m_LeftMargin)
1154 m_bControlSeq =
false;
1161 m_bControlSeq =
false;
1166 ERROR(
"TextIO: graphics mode is not implemented.");
1167 m_bControlSeq =
false;
1171 if (m_CurrentModes & AnsiVt52) {
1173 m_TabStops[m_CursorX] =
'|';
1179 m_bControlSeq =
false;
1188 m_bControlSeq =
false;
1193 m_bControlSeq =
false;
1198 m_bControlSeq =
false;
1202 uint8_t row = (*(++s)) - 0x20;
1203 uint8_t col = (*(++s)) - 0x20;
1209 m_bControlSeq =
false;
1213 const char* identifier = 0;
1214 if (m_CurrentModes & AnsiVt52)
1215 identifier =
"\033[?1;2c";
1217 identifier =
"\033/Z";
1218 m_OutBuffer.
write(
const_cast<char*
>(identifier), StringLength(identifier));
1220 m_bControlSeq =
false;
1235 ERROR(
"TextIO: unknown DEC command '" <<
m_nCharacter <<
"'");
1238 m_bControlSeq =
false;
1243 ERROR(
"TextIO: alternate keypad mode is not implemented.");
1244 m_bControlSeq =
false;
1248 m_CurrentModes |= AnsiVt52;
1249 m_bControlSeq =
false;
1254 ERROR(
"TextIO: alternate keypad mode is not implemented.");
1255 m_bControlSeq =
false;
1277 if ((next >=
'0' && next <=
'2') || (next >=
'A' && next <=
'B')) {
1282 else if (curr ==
')')
1286 "TextIO: only 'ESC(C' and 'ESC)C' are "
1287 "supported on a VT100.");
1289 m_bControlSeq =
false;
1293 m_SavedCursorX = m_CursorX;
1294 m_SavedCursorY = m_CursorY;
1295 m_bControlSeq =
false;
1299 m_CursorX = m_SavedCursorX;
1300 m_CursorY = m_SavedCursorY;
1301 m_bControlSeq =
false;
1307 m_bControlSeq =
false;
1311 ERROR(
"TextIO: unknown escape sequence character '" <<
m_nCharacter <<
"'!");
1312 m_bControlSeq =
false;
1317 m_bControlSeq =
true;
1320 m_bParenthesis =
false;
1321 m_bQuestionMark =
true;
1323 ByteSet(m_Params, 0,
sizeof(
size_t) * MAX_TEXTIO_PARAMS);
1328 const char* answerback =
"\033[1;2c";
1329 m_OutBuffer.
write(
const_cast<char*
>(answerback), StringLength(answerback));
1343 if (m_CurrentModes & LineFeedNewLine)
1349 m_CurrentModes &= ~CharacterSetG0;
1350 m_CurrentModes |= CharacterSetG1;
1354 m_CurrentModes &= ~CharacterSetG1;
1355 m_CurrentModes |= CharacterSetG0;
1361 uint8_t characterSet =
m_G0;
1362 if (m_CurrentModes & CharacterSetG1)
1363 characterSet = m_G1;
1365 if (characterSet >=
'0' && characterSet <=
'2') {
1442 if (m_CursorX < BACKBUFFER_STRIDE) {
1444 VgaCell* pCell = &m_pBackbuffer[(m_CursorY * BACKBUFFER_STRIDE) + m_CursorX];
1445 pCell->character = c;
1446 pCell->fore = m_Fore;
1447 pCell->back = m_Back;
1448 pCell->flags = m_CurrentModes;
1452 "TextIO: X co-ordinate is beyond the end "
1453 "of a backbuffer line: "
1454 << m_CursorX <<
" vs " << BACKBUFFER_STRIDE <<
"?");
1462 if (m_CursorX < m_LeftMargin) {
1463 WARNING(
"TextIO: X co-ordinate ended up befor the left margin.");
1464 m_CursorX = m_LeftMargin;
1484void TextIO::setColour(TextIO::VgaColour* which,
size_t param,
bool bBright) {
1487 *which = bBright ? DarkGrey : Black;
1490 *which = bBright ? LightRed : Red;
1493 *which = bBright ? LightGreen : Green;
1496 *which = bBright ? Yellow : Orange;
1499 *which = bBright ? LightBlue : Blue;
1502 *which = bBright ? LightMagenta : Magenta;
1505 *which = bBright ? LightCyan : Cyan;
1508 *which = bBright ? White : LightGrey;
1515void TextIO::doBackspace() {
1518 if (m_CursorX == m_RightMargin)
1522 if (m_CursorX > m_LeftMargin)
1526void TextIO::doLinefeed() {
1531void TextIO::doCarriageReturn() {
1532 m_CursorX = m_LeftMargin;
1535void TextIO::doHorizontalTab() {
1536 bool tabStopFound =
false;
1539 for (ssize_t x = (m_CursorX + 1); x < m_RightMargin; ++x) {
1540 if (m_TabStops[x] != 0) {
1542 tabStopFound =
true;
1547 if (!tabStopFound) {
1549 m_CursorX = m_RightMargin - 1;
1550 }
else if (m_CursorX >= m_RightMargin)
1551 m_CursorX = m_RightMargin - 1;
1554void TextIO::checkScroll() {
1559 if (m_CursorY < m_ScrollStart) {
1561 size_t numRows = (m_ScrollStart - m_CursorY);
1564 size_t sourceRow = m_ScrollStart;
1565 size_t destRow = m_ScrollStart + numRows;
1568 size_t sourceEnd = m_ScrollEnd + 1 - numRows;
1571 MemoryCopy(&m_pBackbuffer[destRow * BACKBUFFER_STRIDE],
1572 &m_pBackbuffer[sourceRow * BACKBUFFER_STRIDE],
1573 (sourceEnd - sourceRow) * BACKBUFFER_STRIDE *
sizeof(VgaCell));
1576 for (
size_t i = 0; i < ((destRow - sourceRow) * BACKBUFFER_STRIDE); ++i) {
1577 VgaCell* pCell = &m_pBackbuffer[(sourceRow * BACKBUFFER_STRIDE) + i];
1578 pCell->character =
' ';
1579 pCell->back = m_Back;
1580 pCell->fore = m_Fore;
1584 m_CursorY = m_ScrollStart;
1585 }
else if (m_CursorY > m_ScrollEnd) {
1587 size_t numRows = (m_CursorY - m_ScrollEnd);
1591 size_t startOffset = m_ScrollStart * BACKBUFFER_STRIDE;
1594 size_t fromOffset = (m_ScrollStart + numRows) * BACKBUFFER_STRIDE;
1598 size_t movedRows = ((m_ScrollEnd + 1) * BACKBUFFER_STRIDE) - fromOffset;
1601 size_t blankFrom = (((m_ScrollEnd + 1) - numRows) * BACKBUFFER_STRIDE);
1604 size_t blankLength = ((m_ScrollEnd + 1) * BACKBUFFER_STRIDE) - blankFrom;
1606 MemoryCopy(&m_pBackbuffer[startOffset], &m_pBackbuffer[fromOffset],
1607 movedRows *
sizeof(VgaCell));
1608 for (
size_t i = 0; i < blankLength; ++i) {
1609 VgaCell* pCell = &m_pBackbuffer[blankFrom + i];
1610 pCell->character =
' ';
1611 pCell->back = m_Back;
1612 pCell->fore = m_Fore;
1616 m_CursorY = m_ScrollEnd;
1620void TextIO::checkWrap() {
1621 if (m_CursorX >= m_RightMargin) {
1624 if (m_CurrentModes & AutoWrap) {
1625 m_CursorX = m_LeftMargin;
1630 m_CursorX = m_RightMargin - 1;
1635void TextIO::eraseSOS() {
1642 for (ssize_t y = 0; y < m_CursorY; ++y) {
1643 for (
size_t x = 0; x < BACKBUFFER_STRIDE; ++x) {
1644 VgaCell* pCell = &m_pBackbuffer[(y * BACKBUFFER_STRIDE) + x];
1645 pCell->character =
' ';
1646 pCell->fore = m_Fore;
1647 pCell->back = m_Back;
1653void TextIO::eraseEOS() {
1660 for (
size_t y = m_CursorY + 1; y < BACKBUFFER_ROWS; ++y) {
1661 for (
size_t x = 0; x < BACKBUFFER_STRIDE; ++x) {
1662 VgaCell* pCell = &m_pBackbuffer[(y * BACKBUFFER_STRIDE) + x];
1663 pCell->character =
' ';
1664 pCell->back = m_Back;
1665 pCell->fore = m_Fore;
1671void TextIO::eraseEOL() {
1675 for (
size_t x = m_CursorX; x < BACKBUFFER_STRIDE; ++x) {
1676 VgaCell* pCell = &m_pBackbuffer[(m_CursorY * BACKBUFFER_STRIDE) + x];
1677 pCell->character =
' ';
1678 pCell->back = m_Back;
1679 pCell->fore = m_Fore;
1684void TextIO::eraseSOL() {
1687 for (ssize_t x = 0; x <= m_CursorX; ++x) {
1688 VgaCell* pCell = &m_pBackbuffer[(m_CursorY * BACKBUFFER_STRIDE) + x];
1689 pCell->character =
' ';
1690 pCell->fore = m_Fore;
1691 pCell->back = m_Back;
1696void TextIO::eraseLine() {
1699 for (
size_t x = 0; x < BACKBUFFER_STRIDE; ++x) {
1700 VgaCell* pCell = &m_pBackbuffer[(m_CursorY * BACKBUFFER_STRIDE) + x];
1701 pCell->character =
' ';
1702 pCell->fore = m_Fore;
1703 pCell->back = m_Back;
1708void TextIO::eraseScreen(uint8_t character) {
1711 for (
size_t y = 0; y < BACKBUFFER_ROWS; ++y) {
1712 for (
size_t x = 0; x < BACKBUFFER_STRIDE; ++x) {
1713 VgaCell* pCell = &m_pBackbuffer[(y * BACKBUFFER_STRIDE) + x];
1714 pCell->character = character;
1715 pCell->fore = m_Fore;
1716 pCell->back = m_Back;
1722void TextIO::goHome(ssize_t xmove, ssize_t ymove) {
1724 if (m_CurrentModes & Origin) {
1725 m_CursorX = m_LeftMargin + xmove;
1726 m_CursorY = m_ScrollStart + ymove;
1734 ByteSet(m_pBackbuffer, 0, BACKBUFFER_STRIDE * BACKBUFFER_ROWS *
sizeof(
VgaCell));
1740#if PEDIGREE_ACTIVITY_DIAGNOSTICS
1741 const uint64_t activityStart = ActivityDiagnostics::timestamp();
1742 size_t activityCells = 0;
1745 const VgaColour defaultBack = Black, defaultFore = LightGrey;
1761 if (numRows > BACKBUFFER_ROWS)
1762 numRows = BACKBUFFER_ROWS;
1763 if (numCols > BACKBUFFER_STRIDE)
1764 numCols = BACKBUFFER_STRIDE;
1766 for (
size_t y = 0; y < numRows; ++y) {
1767 for (
size_t x = 0; x < numCols; ++x) {
1768 size_t dirtyIndex = (y * BACKBUFFER_STRIDE) + x;
1769 VgaCell* pCell = &m_pBackbuffer[(y * BACKBUFFER_STRIDE) + x];
1770 if (m_pVterm && !timer && !m_VtermDirty[dirtyIndex])
1772 if (timer && !(pCell->flags & Blink))
1775 if (pCell->flags & Blink)
1776 pCell->
hidden = hideState;
1781 VgaColour fore = pCell->fore;
1782 VgaColour back = pCell->back;
1785 if ((pCell->flags &
Bright) && (fore < DarkGrey))
1786 fore = adjustColour(fore,
true);
1790 VgaColour tmp = fore;
1795 uint8_t attrib = (back << 4) | (fore & 0x0F);
1796 if (m_CurrentModes & Screen) {
1798 if (pCell->fore == defaultFore && pCell->back == defaultBack) {
1799 attrib = (fore << 4) | (back & 0x0F);
1803 uint16_t front = (pCell->
hidden ?
' ' : pCell->character) | (attrib << 8);
1804 m_pFramebuffer[(y * numCols) + x] = front;
1805#if PEDIGREE_ACTIVITY_DIAGNOSTICS
1809 m_VtermDirty[dirtyIndex] = 0;
1813#if PEDIGREE_ACTIVITY_DIAGNOSTICS
1814 ActivityDiagnostics::recordFramebufferFlip(activityCells,
1815 ActivityDiagnostics::timestamp() - activityStart);
1820 return m_OutBuffer.
read(
reinterpret_cast<char*
>(buffer), size, bCanBlock);
1824 writeStr(
reinterpret_cast<const char*
>(buffer), size);
1836void TextIO::flipThread() {
1837 while (m_bInitialised) {
1845 flip(
true, !bBlinkOn);
1857 switch (codepoint) {
2116 if (codepoint <= 0xFF)
2117 return codepoint & 0xFF;
2122static int startFlipThread(
void* param) {
2142 if (m_InputMode == Raw) {
2143 if (in.type != InputManager::MachineKey) {
2147 uint8_t buf = in.data.rawkey.
scancode | (in.data.rawkey.
keyUp ? 0x80 : 0);
2150 WARNING(
"TextIO: input buffer is full or closed, dropping keypress");
2163 uint64_t c = in.data.key.key;
2166 if (c & SPECIAL_KEY) {
2167 uint32_t k = c & 0xFFFFFFFFULL;
2168 char* str =
reinterpret_cast<char*
>(&k);
2170 if (!StringCompareN(str,
"left", 4)) {
2172 }
else if (!StringCompareN(str,
"righ", 4)) {
2174 }
else if (!StringCompareN(str,
"up", 2)) {
2176 }
else if (!StringCompareN(str,
"down", 4)) {
2182 }
else if (c & CTRL_KEY) {
2190 const char* input =
nullptr;
2191 size_t inputLength = 0;
2193 if (direction >= 0) {
2194 switch (direction) {
2214 }
else if (c & ALT_KEY) {
2218 buf[1] =
static_cast<char>(c & 0xFF);
2233 WARNING(
"TextIO: input buffer is full or closed, dropping keypress");
2246 ByteSet(m_VtermDirty, 1,
sizeof(m_VtermDirty));
size_t write(const T *buffer, size_t count, bool block=true)
bool canWrite(bool block)
size_t writeAvailable(const T *buffer, size_t count, bool atomic=false)
size_t read(T *buffer, size_t count, bool block=true)
virtual Vga * getVga(size_t n)=0
static PhysicalMemoryManager & instance()
static constexpr size_t getPageSize() PURE
virtual bool allocateRegion(MemoryRegion &Region, size_t cPages, size_t pageConstraints, size_t Flags, physical_uintptr_t start=-1)=0
static ProcessorInformation & information()
static Scheduler & instance()
bool tryAcquire(size_t n=1)
bool acquire(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
static size_t Utf32ToUtf8(uint32_t utf32, char *utf8)
bool initialise(bool bClear=true)
InputMode getMode() const
void flip(bool timer=false, bool hideState=false)
Buffer< char > m_OutBuffer
void writeStr(const char *s, size_t len)
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
@ Inverse
AKA: 'Negative'.
void setMode(InputMode mode)
uint8_t translate(uint32_t codepoint)
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
virtual int select(bool bWriting=false, int timeout=0)
@ Continue
No unwind necessary, carry on as normal.
virtual void moveCursor(size_t nX, size_t nY)=0
virtual bool setLargestTextMode()=0
virtual size_t getNumRows()=0
virtual size_t getNumCols()=0
static const size_t KernelMode
static const size_t Write