- Implemented execp*.
[planlOS.git] / system / kernel / sys / terminal.c
blob706b7de1a113f3a5e0e58e69c0acb57f90ee4367
1 /*
2 Copyright (C) 2008 Mathias Gottschlag
4 Permission is hereby granted, free of charge, to any person obtaining a copy of
5 this software and associated documentation files (the "Software"), to deal in the
6 Software without restriction, including without limitation the rights to use,
7 copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
8 Software, and to permit persons to whom the Software is furnished to do so,
9 subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
15 INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
16 PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
17 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
18 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
19 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 #include "sys/terminal.h"
23 #include "ke/debug.h"
24 #include "ke/level.h"
25 #include "fs/request.h"
26 #include <string.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <ctype.h>
31 static char *termnames[12] =
33 "tty0",
34 "tty1",
35 "tty2",
36 "tty3",
37 "tty4",
38 "tty5",
39 "tty6",
40 "tty7",
41 "gfx0",
42 "gfx1",
43 "gfx2",
44 "gfx3",
46 static SysTextTerminal textterms[8];
47 static SysGfxTerminal gfxterms[4];
49 static int currentterm = 0;
51 static int screenwidth = 80;
52 static int screenheight = 25;
54 static char *vidmem = (char*)0xC00B8000;
56 static KeSpinlock terminallock;
58 void keTerminalInitialized(void);
60 static int control_pressed = 0;
62 static void sysTerminalScroll(SysTextTerminal *terminal, int lines, int screen)
64 if (screen)
66 memmove(vidmem, vidmem + lines * 160, (25 - lines) * 160);
67 memset(vidmem + (25 - lines) * 160, 0, lines * 160);
69 memmove(terminal->screendata, terminal->screendata + lines * 160, (25 - lines) * 160);
70 memset(terminal->screendata + (25 - lines) * 160, 0, lines * 160);
71 terminal->y -= lines;
74 static void sysTerminalWriteChar(SysTextTerminal *terminal, char c, int screen)
76 if (terminal->x >= 80)
78 terminal->x = 0;
79 terminal->y++;
80 if (terminal->y == 25)
81 sysTerminalScroll(terminal, 1, screen);
83 int position = (screenwidth * terminal->y + terminal->x) * 2;
84 switch (c)
86 case '\n':
87 terminal->y++;
88 terminal->x = 0;
89 if (terminal->y == 25)
90 sysTerminalScroll(terminal, 1, screen);
91 break;
92 case '\r':
93 terminal->x = 0;
94 break;
95 case '\t':
96 sysTerminalWriteChar(terminal, ' ', screen);
97 while (terminal->x % 7)
98 sysTerminalWriteChar(terminal, ' ', screen);
99 break;
100 default:
101 terminal->screendata[position] = c;
102 terminal->screendata[position + 1] = (terminal->brightfg << 3) + (terminal->bgcolor << 4) + terminal->fgcolor;
103 if (screen)
105 vidmem[position] = c;
106 vidmem[position + 1] = (terminal->brightfg << 3) + (terminal->bgcolor << 4) + terminal->fgcolor;
108 terminal->x++;
109 break;
113 static int sysTerminalWriteEscape(SysTextTerminal *terminal, char *data, int size, int screen)
115 // Escape character
116 int written = 1;
117 data++;
118 size--;
119 if (!size) return written;
120 // [
121 switch (*data)
123 case '[':
124 size--;
125 data++;
126 written++;
127 if (!size) return written;
128 break;
129 default:
130 return written;
132 // Parameters
133 int paramcount = 0;
134 int parameters[10] = {0};
135 while (isdigit(*data))
137 parameters[paramcount] = *data - '0';
138 data++;
139 size--;
140 written++;
141 if (!size) return written;
142 if (!size) return written;
143 // Read in number
144 while (isdigit(*data))
146 parameters[paramcount] = parameters[paramcount] * 10 + *data - '0';
147 data++;
148 size--;
149 written++;
150 if (!size) return written;
152 paramcount++;
153 if (paramcount == 10) return written;
154 if (*data == ';')
156 data++;
157 size--;
158 written++;
159 if (!size) return written;
160 continue;
163 // Command
164 int i;
165 switch (*data)
167 case 'J':
168 if ((paramcount >= 1) && (parameters[0] == 2))
170 // Clear screen
171 memset(terminal->screendata, 0, screenwidth * screenheight * 2);
172 if (screen)
174 memset(vidmem, 0, screenwidth * screenheight * 2);
177 break;
178 case 'm':
179 // Set parameters
180 for (i = 0; i < 10; i++)
182 if (i == paramcount) break;
183 switch (parameters[i])
185 case 1: // Bright
186 terminal->brightfg = 1;
187 break;
188 case 2: // Dim
189 terminal->brightfg = 0;
190 break;
191 case 30: // Black
192 terminal->fgcolor = 0;
193 break;
194 case 31: // Red
195 terminal->fgcolor = 4;
196 break;
197 case 32: // Green
198 terminal->fgcolor = 2;
199 break;
200 case 33: // Yellow
201 terminal->fgcolor = 6;
202 break;
203 case 34: // Blue
204 terminal->fgcolor = 1;
205 break;
206 case 35: // Magenta
207 terminal->fgcolor = 5;
208 break;
209 case 36: // Cyan
210 terminal->fgcolor = 3;
211 break;
212 case 37: // White
213 terminal->fgcolor = 7;
214 break;
215 case 40: // Black
216 terminal->bgcolor = 0;
217 break;
218 case 41: // Red
219 terminal->bgcolor = 4;
220 break;
221 case 42: // Green
222 terminal->bgcolor = 2;
223 break;
224 case 43: // Yellow
225 terminal->bgcolor = 6;
226 break;
227 case 44: // Blue
228 terminal->bgcolor = 1;
229 break;
230 case 45: // Magenta
231 terminal->bgcolor = 5;
232 break;
233 case 46: // Cyan
234 terminal->bgcolor = 3;
235 break;
236 case 47: // White
237 terminal->bgcolor = 7;
238 break;
241 break;
242 case 'H':
243 // Set cursor position
244 if (paramcount == 2)
246 terminal->x = parameters[1] - 1;
247 if (terminal->x < 0) terminal->x = 0;
248 if (terminal->x >= screenwidth) terminal->x = screenwidth - 1;
249 terminal->y = parameters[0] - 1;
250 if (terminal->y < 0) terminal->y = 0;
251 if (terminal->y >= screenheight) terminal->y = screenheight - 1;
253 break;
255 return written + 1;
258 static void sysTerminalWrite(SysTextTerminal *terminal, char *data, int size, int screen)
260 int i;
261 for (i = 0; i < size; i++)
263 if (data[i] == 033)
265 i += sysTerminalWriteEscape(terminal, data + i, size - i, screen);
266 i--;
268 else
270 sysTerminalWriteChar(terminal, data[i], screen);
275 static int sysTerminalRequest(struct FsDeviceFile *file, FsRequest *request)
277 SysTextTerminal *terminal = (SysTextTerminal*)file;
278 int index = ((uintptr_t)terminal - (uintptr_t)textterms) / sizeof(SysTextTerminal);
280 switch (request->type)
282 case FS_REQUEST_OPEN:
284 KeExecLevel oldlevel = keSetExecutionLevel(KE_LEVEL_HIGH);
285 keLockSpinlock(&terminallock);
286 if (!terminal->owner && !terminal->opencount)
288 terminal->owner = request->process;
290 if (terminal->owner == request->process)
292 terminal->opencount++;
294 keUnlockSpinlock(&terminallock);
295 keSetExecutionLevel(oldlevel);
296 fsFinishRequest(request);
298 break;
299 case FS_REQUEST_CLOSE:
301 KeExecLevel oldlevel = keSetExecutionLevel(KE_LEVEL_HIGH);
302 keLockSpinlock(&terminallock);
303 if (terminal->owner == request->process)
305 terminal->opencount--;
306 if (!terminal->opencount) terminal->owner = 0;
308 keUnlockSpinlock(&terminallock);
309 keSetExecutionLevel(oldlevel);
310 fsFinishRequest(request);
312 break;
313 case FS_REQUEST_READ:
315 KeExecLevel oldlevel = keSetExecutionLevel(KE_LEVEL_HIGH);
316 keLockSpinlock(&terminallock);
317 if (!terminal->requestcount && (terminal->inputbuffer_pos >= (int)request->bufferlength))
319 // Directly copy from the buffer
320 memcpy(request->buffer, terminal->inputbuffer, request->bufferlength);
321 memmove(terminal->inputbuffer, terminal->inputbuffer + request->bufferlength,
322 terminal->inputbuffer_pos - request->bufferlength);
323 terminal->inputbuffer_pos -= request->bufferlength;
324 request->return_value = request->bufferlength;
325 fsFinishRequest(request);
327 else
329 // Put the request into the queue
330 terminal->requests = realloc(terminal->requests, sizeof(FsRequest*) * (terminal->requestcount + 1));
331 terminal->requests[terminal->requestcount] = request;
332 terminal->requestcount++;
334 keUnlockSpinlock(&terminallock);
335 keSetExecutionLevel(oldlevel);
337 break;
338 case FS_REQUEST_WRITE:
340 KeExecLevel oldlevel = keSetExecutionLevel(KE_LEVEL_HIGH);
341 keLockSpinlock(&terminallock);
342 sysTerminalWrite(terminal, request->buffer, request->bufferlength,
343 index == currentterm);
344 keUnlockSpinlock(&terminallock);
345 keSetExecutionLevel(oldlevel);
346 request->return_value = request->bufferlength;
347 fsFinishRequest(request);
349 break;
350 case FS_REQUEST_IOCTLSIZE:
351 if (request->offset == 0x10)
353 request->return_value = 8;
354 fsFinishRequest(request);
355 return 0;
357 return -1;
358 case FS_REQUEST_IOCTL:
359 if (request->offset == 0x10)
361 uint16_t *data = request->buffer;
362 if (currentterm < 8)
364 data[0] = 25;
365 data[1] = 80;
366 data[2] = 0;
367 data[3] = 0;
369 else
371 memset(data, 0, 8);
373 request->return_value = 0;
374 fsFinishRequest(request);
375 return 0;
377 request->return_value = -1;
378 fsFinishRequest(request);
379 break;
380 default:
381 fsFinishRequest(request);
383 return 0;
385 static int sysGfxRequest(struct FsDeviceFile *file, FsRequest *request)
387 switch (request->type)
389 case FS_REQUEST_READ:
390 fsFinishRequest(request);
391 break;
392 case FS_REQUEST_WRITE:
393 fsFinishRequest(request);
394 break;
395 case FS_REQUEST_IOCTL:
396 fsFinishRequest(request);
397 break;
398 default:
399 fsFinishRequest(request);
401 return 0;
404 int sysInitTerminals(void)
406 // Create text terminals
407 memset(textterms, 0, sizeof(SysTextTerminal) * 8);
408 int i;
409 for (i = 0; i < 8; i++)
411 textterms[i].file.path = termnames[i];
412 textterms[i].file.query_request = sysTerminalRequest;
413 textterms[i].inputbuffer_size = 256;
414 textterms[i].inputbuffer = malloc(256);
415 textterms[i].screendata = malloc(screenwidth * screenheight * 2);
416 memset(textterms[i].screendata, 0, screenwidth * screenheight * 2);
417 textterms[i].fgcolor = 0x7;
418 textterms[i].bgcolor = 0x0;
419 textterms[i].echo = 1;
420 fsCreateDeviceFile(&textterms[i].file);
422 // Create graphical terminals
423 memset(gfxterms, 0, sizeof(SysGfxTerminal) * 4);
424 for (i = 0; i < 4; i++)
426 gfxterms[i].file.path = termnames[i + 8];
427 gfxterms[i].file.query_request = sysGfxRequest;
428 fsCreateDeviceFile(&gfxterms[i].file);
430 // Clear screen
431 keTerminalInitialized();
432 memset(vidmem, 0, 80 * 25 * 2);
433 return 0;
436 void sysSetCurrentTerminal(int terminal)
438 keLockSpinlock(&terminallock);
439 if (terminal != currentterm)
441 if (terminal < 8)
443 memcpy(vidmem, textterms[terminal].screendata, screenwidth * screenheight * 2);
445 else
447 memset(vidmem, 0, screenwidth * screenheight * 2);
449 currentterm = terminal;
451 keUnlockSpinlock(&terminallock);
453 int sysGetCurrentTerminal(void)
455 return currentterm;
458 int sysTerminalInjectKey(int key, int modifiers, int down)
460 KeExecLevel oldlevel = keSetExecutionLevel(KE_LEVEL_HIGH);
461 keLockSpinlock(&terminallock);
462 if (currentterm < 8)
464 SysTextTerminal *terminal = &textterms[currentterm];
465 // SIGINT
466 if (control_pressed && (key == 'c') && down)
468 if (terminal->owner)
470 keSendSignal(terminal->owner, 0, SIGINT);
471 keUnlockSpinlock(&terminallock);
472 keSetExecutionLevel(oldlevel);
473 return 0;
476 if ((key < 256) && down)
478 if (terminal->echo)
480 // Draw char
481 sysTerminalWriteChar(terminal, key, 1);
483 // Add key to buffer
484 if (terminal->inputbuffer_pos < terminal->inputbuffer_size)
486 terminal->inputbuffer[terminal->inputbuffer_pos] = key;
487 terminal->inputbuffer_pos++;
489 while (terminal->requestcount && (terminal->inputbuffer_pos >= (int)terminal->requests[0]->bufferlength))
491 // Answer request
492 FsRequest *request = terminal->requests[0];
493 memcpy(request->buffer, terminal->inputbuffer, request->bufferlength);
494 memmove(terminal->inputbuffer, terminal->inputbuffer + request->bufferlength,
495 terminal->inputbuffer_pos - request->bufferlength);
496 terminal->inputbuffer_pos -= request->bufferlength;
497 request->return_value = request->bufferlength;
498 fsFinishRequest(request);
499 // Delete request from queue
500 memmove(terminal->requests, terminal->requests + 1, (terminal->requestcount - 1) * sizeof(FsRequest*));
501 terminal->requestcount--;
502 terminal->requests = realloc(terminal->requests, sizeof(FsRequest*) * terminal->requestcount);
505 else
507 if (key == SYS_KEY_LCONTROL)
509 control_pressed = down;
511 // Changing terminals
512 if (control_pressed)
514 if ((key >= SYS_KEY_F1) && (key <= SYS_KEY_F12))
516 keUnlockSpinlock(&terminallock);
517 sysSetCurrentTerminal(key - SYS_KEY_F1);
518 keLockSpinlock(&terminallock);
523 else
525 // TODO
527 keUnlockSpinlock(&terminallock);
528 keSetExecutionLevel(oldlevel);
529 return 0;
532 char *sysTerminalPreparePanic(int *width, int *height)
534 *width = 80;
535 *height = 25;
536 return (char*)0xC00B8000;