1/* @title: Console terminal state */
2#include <asm.h>
3#include <colors.h>
4#include <console/printf.h>
5#include <console/term.h>
6#include <stdbool.h>
7#include <stdint.h>
8#include <string.h>
9#include <time/time.h>
10
11#define TERM_PROBE_SPIN_CAP (1u << 26)
12#define TERM_ESC(s) serial_write((s), sizeof(s) - 1)
13
14/* Properties of the console */
15static struct {
16 bool available;
17 bool plain;
18 bool unicode;
19 bool panic;
20 uint16_t rows;
21 uint16_t cols;
22} term = {.unicode = true};
23
24bool term_available(void) {
25 return term.available;
26}
27
28struct term_geometry term_size(void) {
29 if (!term.available || !term.rows || !term.cols)
30 return (struct term_geometry){.rows = TERM_FALLBACK_ROWS,
31 .cols = TERM_FALLBACK_COLS};
32
33 return (struct term_geometry){.rows = term.rows, .cols = term.cols};
34}
35
36void term_geometry_set(uint16_t rows, uint16_t cols) {
37 if (rows < 2 || cols < 8)
38 return;
39
40 term.rows = rows;
41 term.cols = cols;
42 term.available = true;
43}
44
45void term_set_plain(bool plain) {
46 term.plain = plain;
47}
48
49bool term_plain(void) {
50 return term.plain;
51}
52
53void term_set_unicode(bool unicode) {
54 term.unicode = unicode;
55}
56
57bool term_unicode(void) {
58 return term.unicode;
59}
60
61bool term_in_panic(void) {
62 return term.panic;
63}
64
65const char *term_style(enum term_sev sev) {
66 if (term.plain)
67 return "";
68
69 switch (sev) {
70 case TERM_SEV_DIM: return ANSI_GRAY;
71 case TERM_SEV_LABEL: return ANSI_BRIGHT_BLUE;
72 case TERM_SEV_OK: return ANSI_GREEN;
73 case TERM_SEV_WARN: return ANSI_YELLOW;
74 case TERM_SEV_CRIT: return ANSI_BOLD ANSI_RED;
75 case TERM_SEV_HEAD: return ANSI_BOLD;
76 case TERM_SEV_NORMAL:
77 default: return "";
78 }
79}
80
81const char *term_style_reset(void) {
82 return term.plain ? "" : ANSI_RESET;
83}
84
85static void term_drain_rx(void) {
86 char c;
87
88 while (serial_try_getc(out: &c))
89 ;
90}
91
92bool term_probe(void) {
93 enum { WANT_ESC, WANT_CSI, WANT_ROWS, WANT_COLS } state = WANT_ESC;
94 uint32_t rows = 0, cols = 0;
95 uint64_t spins = 0;
96 bool done = false;
97 char c;
98
99 if (term.panic)
100 return term.available;
101
102 term.available = false;
103 term_drain_rx();
104
105 /* Put the cursor past any reasonable bottom right corner, then ask where
106 * it ended up, mildly hacky */
107 TERM_ESC("\0337\033[999;999H\033[6n");
108
109 time_ms_t deadline = time_get_ms() + TERM_PROBE_TIMEOUT_MS;
110
111 while (!done && time_get_ms() < deadline) {
112 if (++spins > TERM_PROBE_SPIN_CAP)
113 break;
114
115 if (!serial_try_getc(out: &c)) {
116 cpu_relax();
117 continue;
118 }
119
120 switch (state) {
121 case WANT_ESC:
122 /* Whatever typed while waited is noise */
123 if (c == '\033')
124 state = WANT_CSI;
125 break;
126 case WANT_CSI:
127 rows = cols = 0;
128 state = (c == '[') ? WANT_ROWS : WANT_ESC;
129 break;
130 case WANT_ROWS:
131 if (c >= '0' && c <= '9')
132 rows = rows * 10 + (uint32_t) (c - '0');
133 else if (c == ';')
134 state = WANT_COLS;
135 else
136 state = WANT_ESC;
137 break;
138 case WANT_COLS:
139 if (c >= '0' && c <= '9')
140 cols = cols * 10 + (uint32_t) (c - '0');
141 else if (c == 'R')
142 done = true;
143 else
144 state = WANT_ESC;
145 break;
146 }
147 }
148
149 TERM_ESC("\0338");
150
151 /* We need at least two rows to scroll and hold the bar */
152 if (!done || rows < 2 || rows > UINT16_MAX || cols < 8 || cols > UINT16_MAX)
153 return false;
154
155 term.rows = (uint16_t) rows;
156 term.cols = (uint16_t) cols;
157 term.available = true;
158 return true;
159}
160
161void term_enter_panic(void) {
162 term.panic = true;
163
164 if (term.available)
165 TERM_ESC("\033[r\033[?25h");
166}
167