53d01bbb50810360e3103eb6efadaa1e3510aaef
[lunaix-os.git] / lunaix-os / hal / char / lxconsole.c
1 /**
2  * @file lxconsole.c
3  * @author Lunaixsky (lunaxisky@qq.com)
4  * @brief Provides simple terminal support
5  * @version 0.1
6  * @date 2023-06-18
7  *
8  * @copyright Copyright (c) 2023
9  *
10  */
11
12 #include <klibc/string.h>
13 #include <lunaix/device.h>
14 #include <lunaix/input.h>
15 #include <lunaix/ioctl.h>
16 #include <lunaix/keyboard.h>
17 #include <lunaix/mm/pmm.h>
18 #include <lunaix/mm/valloc.h>
19 #include <lunaix/mm/vmm.h>
20 #include <lunaix/sched.h>
21 #include <lunaix/signal.h>
22 #include <lunaix/tty/tty.h>
23 #include <lunaix/ds/fifo.h>
24 #include <lunaix/owloysius.h>
25
26 #include <hal/term.h>
27
28 struct console
29 {
30     struct lx_timer* flush_timer;
31     struct fifo_buf output;
32     struct fifo_buf input;
33     size_t wnd_start;
34     size_t lines;
35 };
36
37 static struct console lx_console;
38
39 int
40 __tty_write(struct device* dev, void* buf, size_t offset, size_t len);
41
42 int
43 __tty_read(struct device* dev, void* buf, size_t offset, size_t len);
44
45 void
46 console_write(struct console* console, u8_t* data, size_t size);
47
48 void
49 console_flush();
50
51 void
52 console_view_up();
53
54 void
55 console_view_down();
56
57 static waitq_t lx_reader;
58
59 static volatile pid_t fg_pgid = 0;
60
61 int
62 __lxconsole_listener(struct input_device* dev)
63 {
64     u32_t key = dev->current_pkt.sys_code;
65     u32_t type = dev->current_pkt.pkt_type;
66     kbd_kstate_t state = key >> 16;
67     u8_t ttychr = key & 0xff;
68     key = key & 0xffff;
69
70     if (type == PKT_RELEASE) {
71         goto done;
72     }
73
74     if ((state & KBD_KEY_FLCTRL_HELD)) {
75         char cntrl = (char)(ttychr | 0x20);
76         if ('a' > cntrl || cntrl > 'z') {
77             goto done;
78         }
79         ttychr = cntrl - 'a' + 1;
80     } else if (key == KEY_PG_UP) {
81         console_view_up();
82         goto done;
83     } else if (key == KEY_PG_DOWN) {
84         console_view_down();
85         goto done;
86     } else if ((key & 0xff00) <= KEYPAD) {
87         ttychr = key;
88     } else {
89         goto done;
90     }
91
92     fifo_putone(&lx_console.input, ttychr);
93     pwake_all(&lx_reader);
94
95 done:
96     return INPUT_EVT_NEXT;
97 }
98
99 int
100 __tty_write_pg(struct device* dev, void* buf, size_t offset)
101 {
102     return __tty_write(dev, buf, offset, PG_SIZE);
103 }
104
105 int
106 __tty_read_pg(struct device* dev, void* buf, size_t offset)
107 {
108     return __tty_read(dev, buf, offset, PG_SIZE);
109 }
110
111 int
112 __tty_write(struct device* dev, void* buf, size_t offset, size_t len)
113 {
114     struct console* console = (struct console*)dev->underlay;
115     console_write(console, buf, len);
116
117     return len;
118 }
119
120 int
121 __tty_read(struct device* dev, void* buf, size_t offset, size_t len)
122 {
123     struct console* console = (struct console*)dev->underlay;
124
125     size_t count = fifo_read(&console->input, buf, len);
126     if (count > 0) {
127         return count;
128     }
129
130     pwait(&lx_reader);
131
132     return count + fifo_read(&console->input, buf + count, len - count);
133 }
134
135 size_t
136 __find_next_line(size_t start)
137 {
138     size_t p = start - 1;
139     struct fifo_buf* buffer = &lx_console.output;
140     do {
141         p = (p + 1) % buffer->size;
142     } while (p != buffer->wr_pos && ((char*)buffer->data)[p] != '\n');
143     return p + (((char*)buffer->data)[p] == '\n');
144 }
145
146 size_t
147 __find_prev_line(size_t start)
148 {
149     size_t p = start - 1;
150     struct fifo_buf* buffer = &lx_console.output;
151     do {
152         p--;
153     } while (p < lx_console.wnd_start && p != buffer->wr_pos &&
154              ((char*)buffer->data)[p] != '\n');
155
156     if (p > lx_console.wnd_start) {
157         return 0;
158     }
159     return p + 1;
160 }
161
162 void
163 console_view_up()
164 {
165     struct fifo_buf* buffer = &lx_console.output;
166     mutex_lock(&buffer->lock);
167     fifo_set_rdptr(buffer, __find_prev_line(buffer->rd_pos));
168     buffer->flags |= FIFO_DIRTY;
169     mutex_unlock(&buffer->lock);
170
171     console_flush();
172 }
173
174 void
175 console_view_down()
176 {
177     struct fifo_buf* buffer = &lx_console.output;
178     mutex_lock(&buffer->lock);
179
180     size_t wnd = lx_console.wnd_start;
181     size_t p = __find_next_line(buffer->rd_pos);
182     fifo_set_rdptr(buffer, p > wnd ? wnd : p);
183     buffer->flags |= FIFO_DIRTY;
184     mutex_unlock(&buffer->lock);
185
186     console_flush();
187 }
188
189 void
190 console_flush()
191 {
192     if (mutex_on_hold(&lx_console.output.lock)) {
193         return;
194     }
195     if (!(lx_console.output.flags & FIFO_DIRTY)) {
196         return;
197     }
198
199     size_t rdpos_save = lx_console.output.rd_pos;
200     tty_flush_buffer(&lx_console.output);
201     fifo_set_rdptr(&lx_console.output, rdpos_save);
202
203     lx_console.output.flags &= ~FIFO_DIRTY;
204 }
205
206 void
207 console_write(struct console* console, u8_t* data, size_t size)
208 {
209     struct fifo_buf* fbuf = &console->output;
210     mutex_lock(&console->output.lock);
211     fifo_set_rdptr(fbuf, console->wnd_start);
212
213     u8_t* buffer = fbuf->data;
214     ptr_t ptr = fbuf->wr_pos;
215     ptr_t rd_ptr = fbuf->rd_pos;
216
217     char c;
218     for (size_t i = 0; i < size; i++) {
219         c = data[i];
220         if (!c) {
221             continue;
222         }
223         if (c == '\n') {
224             console->lines++;
225         } else if (c == '\x08') {
226             ptr = ptr ? ptr - 1 : fbuf->size - 1;
227             continue;
228         }
229         buffer[ptr] = c;
230         ptr = (ptr + 1) % fbuf->size;
231     }
232
233     fifo_set_wrptr(fbuf, ptr);
234
235     while (console->lines >= TTY_HEIGHT) {
236         rd_ptr = __find_next_line(rd_ptr);
237         console->lines--;
238     }
239
240     fifo_set_rdptr(&lx_console.output, rd_ptr);
241     console->wnd_start = rd_ptr;
242     fbuf->flags |= FIFO_DIRTY;
243     mutex_unlock(&fbuf->lock);
244
245     if (!lx_console.flush_timer) {
246         console_flush();
247     }
248 }
249
250 void
251 console_write_str(char* str)
252 {
253     console_write(&lx_console, (u8_t*)str, strlen(str));
254 }
255
256 void
257 console_write_char(char str)
258 {
259     console_write(&lx_console, (u8_t*)&str, 1);
260 }
261
262
263 static void
264 lxconsole_init()
265 {
266     memset(&lx_console, 0, sizeof(lx_console));
267     fifo_init(&lx_console.output, valloc(8192), 8192, 0);
268     fifo_init(&lx_console.input, valloc(4096), 4096, 0);
269
270     lx_console.flush_timer = NULL;
271 }
272 owloysius_fetch_init(lxconsole_init, on_earlyboot)
273
274 static int
275 lxconsole_spawn_ttydev(struct device_def* devdef)
276 {
277     struct device* tty_dev = device_allocseq(NULL, &lx_console);
278     tty_dev->ops.write = __tty_write;
279     tty_dev->ops.write_page = __tty_write_pg;
280     tty_dev->ops.read = __tty_read;
281     tty_dev->ops.read_page = __tty_read_pg;
282
283     waitq_init(&lx_reader);
284     input_add_listener(__lxconsole_listener);
285
286     register_device(tty_dev, &devdef->class, "vcon");
287
288     term_create(tty_dev, "FB");
289
290     return 0;
291 }
292
293 static struct device_def lxconsole_def = {
294     .name = "Lunaix Virtual Console",
295     .class = DEVCLASS(DEVIF_NON, DEVFN_TTY, DEV_BUILTIN),
296     .init = lxconsole_spawn_ttydev
297 };
298 // FIXME
299 EXPORT_DEVICE(lxconsole, &lxconsole_def, load_onboot);