Many changes, Mostly working on improving the BootConf script engine.
[tpg/acess2.git] / Kernel / drv / vterm.c
1 /*
2  * Acess2 Virtual Terminal Driver
3  */
4 #define DEBUG   1
5 #include <acess.h>
6 #include <fs_devfs.h>
7 #include <modules.h>
8 #include <tpl_drv_video.h>
9 #include <tpl_drv_keyboard.h>
10 #include <tpl_drv_terminal.h>
11 #include <errno.h>
12
13 #define USE_CTRL_ALT    0
14
15 // === CONSTANTS ===
16 #define VERSION ((0<<8)|(50))
17
18 #define NUM_VTS 8
19 #define MAX_INPUT_CHARS32       64
20 #define MAX_INPUT_CHARS8        (MAX_INPUT_CHARS32*4)
21 #define VT_SCROLLBACK   1       // 2 Screens of text
22 #define DEFAULT_OUTPUT  "VGA"
23 //#define DEFAULT_OUTPUT        "BochsGA"
24 //#define DEFAULT_OUTPUT        "Vesa"
25 #define DEFAULT_INPUT   "PS2Keyboard"
26 #define DEFAULT_WIDTH   80
27 #define DEFAULT_HEIGHT  25
28 #define DEFAULT_COLOUR  (VT_COL_BLACK|(0xAAA<<16))
29
30 #define VT_FLAG_HIDECSR 0x01
31 #define VT_FLAG_HASFB   0x10    //!< Set if the VTerm has requested the Framebuffer
32
33 enum eVT_InModes {
34         VT_INMODE_TEXT8,        // UTF-8 Text Mode (VT100 Emulation)
35         VT_INMODE_TEXT32,       // UTF-32 Text Mode (Acess Native)
36         NUM_VT_INMODES
37 };
38
39 // === TYPES ===
40 typedef struct {
41          int    Mode;   //!< Current Mode (see ::eTplTerminal_Modes)
42          int    Flags;  //!< Flags (see VT_FLAG_*)
43         short   Width;  //!< Virtual Width
44         short   Height; //!< Virtual Height
45         short   RealWidth;      //!< Real Width
46         short   RealHeight;     //!< Real Height
47         
48          int    ViewPos;        //!< View Buffer Offset (Text Only)
49          int    WritePos;       //!< Write Buffer Offset (Text Only)
50         Uint32  CurColour;      //!< Current Text Colour
51         char    Name[2];        //!< Name of the terminal
52         
53          int    InputRead;      //!< Input buffer read position
54          int    InputWrite;     //!< Input buffer write position
55         char    InputBuffer[MAX_INPUT_CHARS8];
56         union {
57                 tVT_Char        *Text;
58                 Uint32          *Buffer;
59         };
60         tVFS_Node       Node;
61 } tVTerm;
62
63 // === IMPORTS ===
64 extern void     Debug_SetKTerminal(char *File);
65
66 // === PROTOTYPES ===
67  int    VT_Install(char **Arguments);
68 char    *VT_ReadDir(tVFS_Node *Node, int Pos);
69 tVFS_Node       *VT_FindDir(tVFS_Node *Node, char *Name);
70  int    VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data);
71 Uint64  VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
72 Uint64  VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
73  int    VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data);
74 void    VT_SetTerminal(int ID);
75 void    VT_KBCallBack(Uint32 Codepoint);
76 void    VT_int_PutString(tVTerm *Term, Uint8 *Buffer, Uint Count);
77  int    VT_int_ParseEscape(tVTerm *Term, char *Buffer);
78 void    VT_int_PutChar(tVTerm *Term, Uint32 Ch);
79 void    VT_int_UpdateScreen( tVTerm *Term, int UpdateAll );
80 void    VT_int_ChangeMode(tVTerm *Term, int NewMode);
81
82 // === CONSTANTS ===
83 const Uint16    caVT100Colours[] = {
84                 VT_COL_BLACK, 0x700, 0x070, 0x770, 0x007, 0x707, 0x077, 0xAAA,
85                 VT_COL_GREY, 0xF00, 0x0F0, 0xFF0, 0x00F, 0xF0F, 0x0FF, VT_COL_WHITE
86         };
87
88 // === GLOBALS ===
89 MODULE_DEFINE(0, VERSION, VTerm, VT_Install, NULL, DEFAULT_OUTPUT, DEFAULT_INPUT, NULL);
90 tDevFS_Driver   gVT_DrvInfo = {
91         NULL, "VTerm",
92         {
93         .Flags = VFS_FFLAG_DIRECTORY,
94         .Size = NUM_VTS,
95         .Inode = -1,
96         .NumACLs = 0,
97         .ReadDir = VT_ReadDir,
98         .FindDir = VT_FindDir,
99         .IOCtl = VT_Root_IOCtl
100         }
101 };
102 // --- Terminals ---
103 tVTerm  gVT_Terminals[NUM_VTS];
104  int    giVT_CurrentTerminal = 0;
105 // --- Driver Handles ---
106 char    *gsVT_OutputDevice = NULL;
107 char    *gsVT_InputDevice = NULL;
108  int    giVT_OutputDevHandle = -2;
109  int    giVT_InputDevHandle = -2;
110 // --- Key States --- (Used for VT Switching/Magic Combos)
111  int    gbVT_CtrlDown = 0;
112  int    gbVT_AltDown = 0;
113  int    gbVT_SysrqDown = 0;
114
115 // === CODE ===
116 /**
117  * \fn int VT_Install(char **Arguments)
118  * \brief Installs the Virtual Terminal Driver
119  */
120 int VT_Install(char **Arguments)
121 {
122         char    **args = Arguments;
123         char    *arg;
124          int    i;
125         
126         // Scan Arguments
127         if(Arguments)
128         {
129                 for( ; (arg = *args); args++ )
130                 {
131                         if(arg[0] != '-')       continue;
132                         
133                         switch(arg[1])
134                         {
135                         // Set output device
136                         case 'o':
137                                 if(args[1] ==  NULL)    break;
138                                 if(gsVT_OutputDevice)   free(gsVT_OutputDevice);
139                                 gsVT_OutputDevice = malloc(strlen(args[1])+1);
140                                 strcpy(gsVT_OutputDevice, args[1]);
141                                 args ++;
142                                 break;
143                         
144                         // Set input device
145                         case 'i':
146                                 if(args[1] == NULL)     break;
147                                 if(gsVT_InputDevice)    free(gsVT_InputDevice);
148                                 gsVT_InputDevice = malloc(strlen(args[1])+1);
149                                 strcpy(gsVT_InputDevice, args[1]);
150                                 args ++;
151                                 break;
152                         
153                         }
154                 }
155         }
156         
157         // Apply Defaults
158         if(!gsVT_OutputDevice)  gsVT_OutputDevice = "/Devices/"DEFAULT_OUTPUT;
159         if(!gsVT_InputDevice)   gsVT_InputDevice = "/Devices/"DEFAULT_INPUT;
160         
161         Log_Log("VTerm", "Using '%s' as output", gsVT_OutputDevice);
162         Log_Log("VTerm", "Using '%s' as input", gsVT_InputDevice);
163         
164         // Create Nodes
165         for( i = 0; i < NUM_VTS; i++ )
166         {
167                 gVT_Terminals[i].Mode = TERM_MODE_TEXT;
168                 gVT_Terminals[i].Flags = 0;
169                 gVT_Terminals[i].Width = DEFAULT_WIDTH;
170                 gVT_Terminals[i].Height = DEFAULT_HEIGHT;
171                 gVT_Terminals[i].CurColour = DEFAULT_COLOUR;
172                 gVT_Terminals[i].WritePos = 0;
173                 gVT_Terminals[i].ViewPos = 0;
174                 
175                 gVT_Terminals[i].Buffer = calloc( DEFAULT_WIDTH*DEFAULT_HEIGHT*VT_SCROLLBACK, sizeof(tVT_Char) );
176                 
177                 gVT_Terminals[i].Name[0] = '0'+i;
178                 gVT_Terminals[i].Name[1] = '\0';
179                 gVT_Terminals[i].Node.Inode = i;
180                 gVT_Terminals[i].Node.ImplPtr = &gVT_Terminals[i];
181                 gVT_Terminals[i].Node.NumACLs = 0;      // Only root can open virtual terminals
182                 
183                 gVT_Terminals[i].Node.Read = VT_Read;
184                 gVT_Terminals[i].Node.Write = VT_Write;
185                 gVT_Terminals[i].Node.IOCtl = VT_Terminal_IOCtl;
186         }
187         
188         // Add to DevFS
189         DevFS_AddDevice( &gVT_DrvInfo );
190         
191         // Set kernel output to VT0
192         Debug_SetKTerminal("/Devices/VTerm/0");
193         
194         return MODULE_ERR_OK;
195 }
196
197 /**
198  * \fn void VT_InitOutput()
199  * \brief Initialise Video Output
200  */
201 void VT_InitOutput()
202 {
203         giVT_OutputDevHandle = VFS_Open(gsVT_OutputDevice, VFS_OPENFLAG_WRITE);
204         VT_SetTerminal( 0 );
205 }
206
207 /**
208  * \fn void VT_InitInput()
209  * \brief Initialises the input
210  */
211 void VT_InitInput()
212 {
213         giVT_InputDevHandle = VFS_Open(gsVT_InputDevice, VFS_OPENFLAG_READ);
214         if(giVT_InputDevHandle == -1)   return ;
215         VFS_IOCtl(giVT_InputDevHandle, KB_IOCTL_SETCALLBACK, VT_KBCallBack);
216 }
217
218 /**
219  * \fn char *VT_ReadDir(tVFS_Node *Node, int Pos)
220  * \brief Read from the VTerm Directory
221  */
222 char *VT_ReadDir(tVFS_Node *Node, int Pos)
223 {
224         if(Pos < 0)     return NULL;
225         if(Pos >= NUM_VTS)      return NULL;
226         return strdup( gVT_Terminals[Pos].Name );
227 }
228
229 /**
230  * \fn tVFS_Node *VT_FindDir(tVFS_Node *Node, char *Name)
231  * \brief Find an item in the VTerm directory
232  */
233 tVFS_Node *VT_FindDir(tVFS_Node *Node, char *Name)
234 {
235          int    num;
236         
237         //ENTER("pNode sName", Node, Name);
238         
239         // Open the input and output files if needed
240         if(giVT_OutputDevHandle == -2)  VT_InitOutput();
241         if(giVT_InputDevHandle == -2)   VT_InitInput();
242         
243         // Sanity check name
244         if(Name[0] < '0' || Name[0] > '9' || Name[1] != '\0') {
245                 LEAVE('n');
246                 return NULL;
247         }
248         // Get index
249         num = Name[0] - '0';
250         if(num >= NUM_VTS) {
251                 LEAVE('n');
252                 return NULL;
253         }
254         // Return node
255         //LEAVE('p', &gVT_Terminals[num].Node);
256         return &gVT_Terminals[num].Node;
257 }
258
259 /**
260  * \fn int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data)
261  * \brief Control the VTerm Driver
262  */
263 int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data)
264 {
265          int    len;
266         switch(Id)
267         {
268         case DRV_IOCTL_TYPE:    return DRV_TYPE_MISC;
269         case DRV_IOCTL_IDENT:   memcpy(Data, "VT\0\0", 4);      return 0;
270         case DRV_IOCTL_VERSION: return VERSION;
271         case DRV_IOCTL_LOOKUP:  return 0;
272         
273         case 4: // Get Video Driver
274                 if(Data)        strcpy(Data, gsVT_OutputDevice);
275                 return strlen(gsVT_OutputDevice);
276         
277         case 5: // Set Video Driver
278                 if(!Data)       return -EINVAL;
279                 if(Threads_GetUID() != 0)       return -EACCES;
280                 
281                 len = strlen(Data);
282                 
283                 free(gsVT_OutputDevice);
284                 
285                 gsVT_OutputDevice = malloc(len+1);
286                 strcpy(gsVT_OutputDevice, Data);
287                 
288                 VFS_Close(giVT_OutputDevHandle);
289                 giVT_OutputDevHandle = -1;
290                 
291                 VT_InitOutput();
292                 return 1;
293         }
294         return 0;
295 }
296
297 /**
298  * \fn Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
299  * \brief Read from a virtual terminal
300  */
301 Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
302 {
303          int    pos = 0;
304         tVTerm  *term = &gVT_Terminals[ Node->Inode ];
305         
306         // Check current mode
307         switch(term->Mode)
308         {
309         case TERM_MODE_TEXT:
310                 while(pos < Length)
311                 {
312                         while(term->InputRead == term->InputWrite)      Threads_Yield();
313                         
314                         ((char*)Buffer)[pos] = term->InputBuffer[term->InputRead];
315                         pos ++;
316                         term->InputRead ++;
317                         term->InputRead %= MAX_INPUT_CHARS8;
318                 }
319                 break;
320         
321         case TERM_MODE_FB:
322         //case TERM_MODE_:
323                 while(pos < Length)
324                 {
325                         while(term->InputRead == term->InputWrite)      Threads_Yield();
326                         ((Uint32*)Buffer)[pos] = ((Uint32*)term->InputBuffer)[term->InputRead];
327                         pos ++;
328                         term->InputRead ++;
329                         term->InputRead %= MAX_INPUT_CHARS32;
330                 }
331                 break;
332         }
333         return 0;
334 }
335
336 /**
337  * \fn Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
338  * \brief Write to a virtual terminal
339  */
340 Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
341 {
342         tVTerm  *term = &gVT_Terminals[ Node->Inode ];
343         
344         //ENTER("pNode XOffset XLength pBuffer",  Node, Offset, Length, Buffer);
345         
346         // Write
347         switch( term->Mode )
348         {
349         case TERM_MODE_TEXT:
350                 VT_int_PutString(term, Buffer, Length);
351                 break;
352         case TERM_MODE_FB:
353                 if( term->RealWidth > term->Width || term->RealHeight > term->Height )
354                 {
355                         #if 0
356                          int    x, y, h;
357                         x = Offset/4;   y = x / term->Width;    x %= term->Width;
358                         w = Length/4+x; h = w / term->Width;    w %= term->Width;
359                         while(h--)
360                         {
361                                 VFS_WriteAt( giVT_OutputDevHandle,
362                                         (x+y*term->RealWidth)*4,
363                                         term->Width * 4,
364                                         Buffer
365                                         );
366                                 Buffer = (void*)( (Uint)Buffer + term->Width*term->Height*4 );
367                         }
368                         #endif
369                         return 0;
370                 }
371                 else {
372                         return VFS_WriteAt( giVT_OutputDevHandle, Offset, Length, Buffer );
373                 }
374         }
375         
376         //LEAVE('i', 0);
377         return 0;
378 }
379
380 /**
381  * \fn int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data)
382  * \brief Call an IO Control on a virtual terminal
383  */
384 int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data)
385 {
386          int    *iData = Data;
387         tVTerm  *term = Node->ImplPtr;
388         ENTER("pNode iId pData", Node, Id, Data);
389         
390         if(Id >= DRV_IOCTL_LOOKUP) {
391                 if( Threads_GetUID() != 0 )     return -1;
392         }
393         
394         switch(Id)
395         {
396         case DRV_IOCTL_TYPE:
397                 LEAVE('i', DRV_TYPE_TERMINAL);
398                 return DRV_TYPE_TERMINAL;
399         case DRV_IOCTL_IDENT:
400                 memcpy(Data, "VT\0\0", 4);
401                 LEAVE('i', 0);
402                 return 0;
403         case DRV_IOCTL_VERSION:
404                 LEAVE('x', VERSION);
405                 return VERSION;
406         case DRV_IOCTL_LOOKUP:
407                 LEAVE('i', 0);
408                 return 0;
409         
410         // Get/Set the mode (and apply any changes)
411         case TERM_IOCTL_MODETYPE:
412                 if(Data != NULL)
413                 {
414                         if(term->Mode != *iData)
415                                 VT_int_ChangeMode(term, *iData);
416                         
417                         // Update the screen dimensions
418                         if(giVT_CurrentTerminal == Node->Inode)
419                                 VT_SetTerminal( giVT_CurrentTerminal );
420                 }
421                 LEAVE('i', term->Mode);
422                 return term->Mode;
423         
424         // Get/set the terminal width
425         case TERM_IOCTL_WIDTH:
426                 if(Data != NULL)        term->Width = *iData;
427                 Log("VT_Terminal_IOCtl - RETURN term->Width = %i", term->Width);
428                 LEAVE('i', term->Width);
429                 return term->Width;
430         
431         // Get/set the terminal height
432         case TERM_IOCTL_HEIGHT:
433                 if(Data != NULL)        term->Height = *iData;
434                 Log("VT_Terminal_IOCtl - RETURN term->Height = %i", term->Height);
435                 LEAVE('i', term->Height);
436                 return term->Height;
437         
438         case TERM_IOCTL_FORCESHOW:
439                 VT_SetTerminal( Node->Inode );
440                 LEAVE('i', 1);
441                 return 1;
442         }
443         LEAVE('i', -1);
444         return -1;
445 }
446
447 /**
448  * \fn void VT_SetTerminal(int ID)
449  * \brief Set the current terminal
450  */
451 void VT_SetTerminal(int ID)
452 {
453         tVideo_IOCtl_Mode       mode = {0};
454          int    modeNum;
455         
456         // Create the video mode
457         mode.width = gVT_Terminals[ ID ].Width;
458         mode.height = gVT_Terminals[ ID ].Height;
459         // - Text Mode
460         if(gVT_Terminals[ ID ].Mode == TERM_MODE_TEXT) {
461                 mode.bpp = 12;
462                 mode.flags = VIDEO_FLAG_TEXT;
463         }
464         // - Framebuffer or 3D
465         else {
466                 mode.bpp = 32;
467                 mode.flags = 0;
468         }
469         
470         // Set video mode
471         VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_FINDMODE, &mode );
472         modeNum = mode.id;
473         gVT_Terminals[ ID ].RealWidth = mode.width;
474         gVT_Terminals[ ID ].RealHeight = mode.height;
475         VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_GETSETMODE, &modeNum );
476         
477         // Update current terminal ID
478         Log_Log("VTerm", "Changed terminal from %i to %i", giVT_CurrentTerminal, ID);
479         giVT_CurrentTerminal = ID;
480         
481         // Update the screen
482         VT_int_UpdateScreen( &gVT_Terminals[ ID ], 1 );
483 }
484
485 /**
486  * \fn void VT_KBCallBack(Uint32 Codepoint)
487  * \brief Called on keyboard interrupt
488  * \param Codepoint     Pseudo-UTF32 character
489  * 
490  * Handles a key press and sends the key code to the user's buffer.
491  * If the code creates a kernel-magic sequence, it is not passed to the
492  * user and is handled in-kernel.
493  */
494 void VT_KBCallBack(Uint32 Codepoint)
495 {
496         tVTerm  *term = &gVT_Terminals[giVT_CurrentTerminal];
497         
498         // How the hell did we get a Codepoint of zero?
499         if(Codepoint == 0)      return;
500         
501         // Key Up
502         if( Codepoint & 0x80000000 )
503         {
504                 Codepoint &= 0x7FFFFFFF;
505                 switch(Codepoint)
506                 {
507                 #if !USE_CTRL_ALT
508                 case KEY_RSHIFT:        gbVT_CtrlDown = 0;      break;
509                 case KEY_LSHIFT:        gbVT_AltDown = 0;       break;
510                 #else
511                 case KEY_LALT:
512                 case KEY_RALT:
513                         gbVT_AltDown = 0;
514                         break;
515                 case KEY_LCTRL:
516                 case KEY_RCTRL:
517                         gbVT_CtrlDown = 0;
518                         break;
519                 #endif
520                 }
521                 return;
522         }
523         
524         switch(Codepoint)
525         {
526         #if !USE_CTRL_ALT
527         case KEY_RSHIFT:        gbVT_CtrlDown = 1;      break;
528         case KEY_LSHIFT:        gbVT_AltDown = 1;       break;
529         #else
530         case KEY_LALT:
531         case KEY_RALT:
532                 gbVT_AltDown = 1;
533                 break;
534         case KEY_LCTRL:
535         case KEY_RCTRL:
536                 gbVT_CtrlDown = 1;
537                 break;
538         #endif
539         
540         default:
541                 #if USE_CTRL_ALT
542                 if(!gbVT_AltDown || !gbVT_CtrlDown)
543                         break;
544                 #endif
545                 switch(Codepoint)
546                 {
547                 case KEY_F1:    VT_SetTerminal(0);      return;
548                 case KEY_F2:    VT_SetTerminal(1);      return;
549                 case KEY_F3:    VT_SetTerminal(2);      return;
550                 case KEY_F4:    VT_SetTerminal(3);      return;
551                 case KEY_F5:    VT_SetTerminal(4);      return;
552                 case KEY_F6:    VT_SetTerminal(5);      return;
553                 case KEY_F7:    VT_SetTerminal(6);      return;
554                 case KEY_F8:    VT_SetTerminal(7);      return;
555                 case KEY_F9:    VT_SetTerminal(8);      return;
556                 case KEY_F10:   VT_SetTerminal(9);      return;
557                 case KEY_F11:   VT_SetTerminal(10);     return;
558                 case KEY_F12:   VT_SetTerminal(11);     return;
559                 case KEY_PGUP:
560                         return;
561                 case KEY_PGDOWN:
562                         return;
563                 }
564         }
565         
566         // Encode key
567         if(term->Mode == TERM_MODE_TEXT)
568         {
569                 Uint8   buf[6] = {0};
570                  int    len = 0;
571                 
572                 // Ignore Modifer Keys
573                 if(Codepoint > KEY_MODIFIERS)   return;
574                 
575                 // Get UTF-8/ANSI Encoding
576                 switch(Codepoint)
577                 {
578                 case KEY_LEFT:
579                         buf[0] = '\x1B';        buf[1] = '[';   buf[2] = 'D';
580                         len = 3;
581                         break;
582                 case KEY_RIGHT:
583                         buf[0] = '\x1B';        buf[1] = '[';   buf[2] = 'C';
584                         len = 3;
585                         break;
586                 case KEY_UP:
587                         buf[0] = '\x1B';        buf[1] = '[';   buf[2] = 'A';
588                         len = 3;
589                         break;
590                 case KEY_DOWN:
591                         buf[0] = '\x1B';        buf[1] = '[';   buf[2] = 'B';
592                         len = 3;
593                         break;
594                 
595                 case KEY_PGUP:
596                         buf[0] = '\x1B';        buf[1] = '[';   buf[2] = '5';   // Some overline also
597                         //len = 4;      // Commented out until I'm sure
598                         break;
599                 case KEY_PGDOWN:
600                         len = 0;
601                         break;
602                 
603                 // Attempt to encode in UTF-8
604                 default:
605                         len = WriteUTF8( buf, Codepoint );
606                         if(len == 0) {
607                                 Warning("Codepoint (%x) is unrepresentable in UTF-8", Codepoint);
608                         }
609                         break;
610                 }
611                 
612                 if(len == 0) {
613                         // Unprintable / Don't Pass
614                         return;
615                 }
616                 
617                 // Write
618                 if( MAX_INPUT_CHARS8 - term->InputWrite >= len )
619                         memcpy( &term->InputBuffer[term->InputWrite], buf, len );
620                 else {
621                         memcpy( &term->InputBuffer[term->InputWrite], buf, MAX_INPUT_CHARS8 - term->InputWrite );
622                         memcpy( &term->InputBuffer[0], buf, len - (MAX_INPUT_CHARS8 - term->InputWrite) );
623                 }
624                 // Roll the buffer over
625                 term->InputWrite += len;
626                 term->InputWrite %= MAX_INPUT_CHARS8;
627                 if( (term->InputWrite - term->InputRead + MAX_INPUT_CHARS8)%MAX_INPUT_CHARS8 < len ) {
628                         term->InputRead = term->InputWrite + 1;
629                         term->InputRead %= MAX_INPUT_CHARS8;
630                 }
631                 
632         }
633         else
634         {
635                 // Encode the raw UTF-32 Key
636                 ((Uint32*)term->InputBuffer)[ term->InputWrite ] = Codepoint;
637                 term->InputWrite ++;
638                 term->InputWrite %= MAX_INPUT_CHARS32;
639                 if(term->InputRead == term->InputWrite) {
640                         term->InputRead ++;
641                         term->InputRead %= MAX_INPUT_CHARS32;
642                 }
643         }
644 }
645
646 /**
647  * \fn void VT_int_PutString(tVTerm *Term, Uint8 *Buffer, Uint Count)
648  * \brief Print a string to the Virtual Terminal
649  */
650 void VT_int_PutString(tVTerm *Term, Uint8 *Buffer, Uint Count)
651 {
652         Uint32  val;
653          int    i;
654         for( i = 0; i < Count; i++ )
655         {
656                 if( Buffer[i] == 0x1B ) // Escape Sequence
657                 {
658                         i ++;
659                         i += VT_int_ParseEscape(Term, (char*)&Buffer[i]);
660                         continue;
661                 }
662                 
663                 if( Buffer[i] < 128 )   // Plain ASCII
664                         VT_int_PutChar(Term, Buffer[i]);
665                 else {  // UTF-8
666                         i += ReadUTF8(&Buffer[i], &val);
667                         VT_int_PutChar(Term, val);
668                 }
669         }
670         
671         // Update cursor
672         if( !(Term->Flags & VT_FLAG_HIDECSR) )
673         {
674                 tVideo_IOCtl_Pos        pos;
675                 pos.x = Term->WritePos % Term->Width;
676                 pos.y = Term->WritePos / Term->Width;
677                 VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSOR, &pos);
678         }
679 }
680
681 /**
682  * \fn void VT_int_ClearLine(tVTerm *Term, int Num)
683  * \brief Clears a line in a virtual terminal
684  */
685 void VT_int_ClearLine(tVTerm *Term, int Num)
686 {
687          int    i;
688         //ENTER("pTerm iNum", Term, Num);
689         for( i = Term->Width; i--; )
690         {
691                 Term->Text[ Num*Term->Width + i ].Ch = 0;
692                 Term->Text[ Num*Term->Width + i ].Colour = Term->CurColour;
693         }
694         //LEAVE('-');
695 }
696
697 /**
698  * \fn int VT_int_ParseEscape(tVTerm *Term, char *Buffer)
699  * \brief Parses a VT100 Escape code
700  */
701 int VT_int_ParseEscape(tVTerm *Term, char *Buffer)
702 {
703         char    c;
704          int    argc = 0, j = 1;
705          int    tmp;
706          int    args[4] = {0,0,0,0};
707         
708         switch(Buffer[0]) {
709         //Large Code
710         case '[':
711                 // Get Arguments
712                 c = Buffer[1];
713                 do {
714                         while('0' <= c && c <= '9') {
715                                 args[argc] *= 10;
716                                 args[argc] += c-'0';
717                                 c = Buffer[++j];
718                         }
719                         if( j != 1 )    argc ++;
720                 } while(c == ';');
721                 
722                 // Get string (what does this do?)
723                 if(c == '"') {
724                         c = Buffer[++j];
725                         while(c != '"')
726                                 c = Buffer[++j];
727                 }
728                 
729                 // Get Command
730                 if(     ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'))
731                 {
732                         switch(c)
733                         {
734                         // Left
735                         case 'D':
736                                 if(argc == 1)   tmp = args[0];
737                                 else    tmp = 1;
738                                 
739                                 if( Term->WritePos-(tmp-1) % Term->Width == 0 )
740                                         Term->WritePos -= Term->WritePos % Term->Width;
741                                 else
742                                         Term->WritePos -= tmp;
743                                 break;
744                         
745                         // Right
746                         case 'C':
747                                 if(argc == 1)   tmp = args[0];
748                                 else    tmp = 1;
749                                 if( (Term->WritePos + tmp) % Term->Width == 0 ) {
750                                         Term->WritePos -= Term->WritePos % Term->Width;
751                                         Term->WritePos += Term->Width - 1;
752                                 } else
753                                         Term->WritePos += tmp;
754                                 break;
755                         
756                         // Clear By Line
757                         case 'J':
758                                 // Clear Screen
759                                 switch(args[0])
760                                 {
761                                 case 2:
762                                         {
763                                          int    i = Term->Height * VT_SCROLLBACK;
764                                         while( i-- )    VT_int_ClearLine(Term, i);
765                                         Term->WritePos = 0;
766                                         Term->ViewPos = 0;
767                                         VT_int_UpdateScreen(Term, 1);
768                                         }
769                                         break;
770                                 }
771                                 break;
772                         // Set Font flags
773                         case 'm':
774                                 for( ; argc--; )
775                                 {
776                                         // Flags
777                                         if( 0 <= args[argc] && args[argc] <= 8)
778                                         {
779                                                 switch(args[argc])
780                                                 {
781                                                 case 0: Term->CurColour = DEFAULT_COLOUR;       break;  // Reset
782                                                 case 1: Term->CurColour |= 0x80000000;  break;  // Bright
783                                                 case 2: Term->CurColour &= ~0x80000000; break;  // Dim
784                                                 }
785                                         }
786                                         // Foreground Colour
787                                         else if(30 <= args[argc] && args[argc] <= 37) {
788                                                 Term->CurColour &= 0xF000FFFF;
789                                                 Term->CurColour |= (Uint32)caVT100Colours[ args[argc]-30+(Term->CurColour>>28) ] << 16;
790                                         }
791                                         // Background Colour
792                                         else if(40 <= args[argc] && args[argc] <= 47) {
793                                                 Term->CurColour &= 0xFFFF8000;
794                                                 Term->CurColour |= caVT100Colours[ args[argc]-40+((Term->CurColour>>12)&15) ];
795                                         }
796                                 }
797                                 break;
798                         }
799                 }
800                 break;
801                 
802         default:
803                 break;
804         }
805         
806         return j + 1;
807 }
808
809 /**
810  * \fn void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
811  * \brief Write a single character to a VTerm
812  */
813 void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
814 {
815          int    i;
816         //ENTER("pTerm xCh", Term, Ch);
817         //LOG("Term = {WritePos:%i, ViewPos:%i}", Term->WritePos, Term->ViewPos);
818         
819         switch(Ch)
820         {
821         case '\0':      return; // Ignore NULL byte
822         case '\n':
823                 Term->WritePos += Term->Width;
824         case '\r':
825                 Term->WritePos -= Term->WritePos % Term->Width;
826                 break;
827         
828         case '\t':
829                 do {
830                         Term->Text[ Term->WritePos ].Ch = '\0';
831                         Term->Text[ Term->WritePos ].Colour = Term->CurColour;
832                         Term->WritePos ++;
833                 } while(Term->WritePos & 7);
834                 break;
835         
836         case '\b':
837                 // Backspace is invalid at Offset 0
838                 if(Term->WritePos == 0) break;
839                 
840                 Term->WritePos --;
841                 // Singe Character
842                 if(Term->Text[ Term->WritePos ].Ch != '\0') {
843                         Term->Text[ Term->WritePos ].Ch = 0;
844                         Term->Text[ Term->WritePos ].Colour = Term->CurColour;
845                         break;
846                 }
847                 // Tab
848                 i = 7;  // Limit it to 8
849                 do {
850                         Term->Text[ Term->WritePos ].Ch = 0;
851                         Term->Text[ Term->WritePos ].Colour = Term->CurColour;
852                         Term->WritePos --;
853                 } while(Term->WritePos && i-- && Term->Text[ Term->WritePos ].Ch == '\0');
854                 if(Term->Text[ Term->WritePos ].Ch != '\0')
855                         Term->WritePos ++;
856                 break;
857         
858         default:
859                 Term->Text[ Term->WritePos ].Ch = Ch;
860                 Term->Text[ Term->WritePos ].Colour = Term->CurColour;
861                 Term->WritePos ++;
862                 break;
863         }
864                 
865         
866         // Move Screen
867         if(Term->WritePos >= Term->Width*Term->Height*VT_SCROLLBACK)
868         {
869                  int    base, i;
870                 Term->WritePos -= Term->Width;
871                 
872                 // Update view position
873                 base = Term->Width*Term->Height*(VT_SCROLLBACK-1);
874                 if(Term->ViewPos < base)        Term->ViewPos += Term->Width;
875                 if(Term->ViewPos > base)        Term->ViewPos = base;
876                 
877                 // Scroll terminal cache
878                 base = Term->Width*(Term->Height*VT_SCROLLBACK-1);
879                 
880                 // Scroll Back
881                 memcpy(
882                         Term->Text,
883                         &Term->Text[Term->Width],
884                         Term->Width*(Term->Height-1)*VT_SCROLLBACK*sizeof(tVT_Char)
885                         );
886                 
887                 // Clear last row
888                 for( i = 0; i < Term->Width; i ++ )
889                 {
890                         Term->Text[ base + i ].Ch = 0;
891                         Term->Text[ base + i ].Colour = Term->CurColour;
892                 }
893                 
894                 VT_int_UpdateScreen( Term, 1 );
895         }
896         else if(Term->WritePos > Term->Width*Term->Height+Term->ViewPos)
897         {
898                 Term->ViewPos += Term->Width;
899                 VT_int_UpdateScreen( Term, 1 );
900         }
901         else
902                 VT_int_UpdateScreen( Term, 0 );
903         
904         //LEAVE('-');
905 }
906
907 /**
908  * \fn void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll )
909  * \brief Updates the video framebuffer
910  */
911 void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll )
912 {
913         // Only update if this is the current terminal
914         if( Term != &gVT_Terminals[giVT_CurrentTerminal] )      return;
915         
916         if( Term->Mode == TERM_MODE_TEXT )
917         {
918                 if(UpdateAll) {
919                         VFS_WriteAt(
920                                 giVT_OutputDevHandle,
921                                 0,
922                                 Term->Width*Term->Height*sizeof(tVT_Char),
923                                 &Term->Text[Term->ViewPos]
924                                 );
925                 } else {
926                          int    pos = Term->WritePos - Term->WritePos % Term->Width;
927                         VFS_WriteAt(
928                                 giVT_OutputDevHandle,
929                                 (pos - Term->ViewPos)*sizeof(tVT_Char),
930                                 Term->Width*sizeof(tVT_Char),
931                                 &Term->Text[pos]
932                                 );
933                 }
934         }
935         else
936         {
937                 VFS_WriteAt(
938                         giVT_OutputDevHandle,
939                         0,
940                         Term->Width*Term->Height*sizeof(Uint32),
941                         &Term->Buffer
942                         );
943         }
944 }
945
946 /**
947  * \fn void VT_int_ChangeMode(tVTerm *Term, int NewMode)
948  * \brief Change the mode of a VTerm
949  */
950 void VT_int_ChangeMode(tVTerm *Term, int NewMode)
951 {       
952         switch(NewMode)
953         {
954         case TERM_MODE_TEXT:
955                 free(Term->Buffer);
956                 Term->Text = calloc( Term->Width*Term->Height*VT_SCROLLBACK, sizeof(tVT_Char) );
957                 break;
958         case TERM_MODE_FB:
959                 free(Term->Text);
960                 Term->Buffer = calloc( Term->Width*Term->Height, sizeof(Uint32) );
961                 break;
962         //case TERM_MODE_OPENGL:
963         //      return;
964         }
965         
966         Term->Mode = NewMode;
967 }
968
969 // ---
970 // Font Render
971 // ---
972 #define MONOSPACE_FONT  10816
973
974 #if MONOSPACE_FONT == 10808     // 8x8
975 # include "vterm_font_8x8.h"
976 #elif MONOSPACE_FONT == 10816   // 8x16
977 # include "vterm_font_8x16.h"
978 #endif
979
980 // === PROTOTYPES ===
981 Uint8   *VT_Font_GetChar(Uint32 Codepoint);
982
983 // === GLOBALS ===
984 int     giVT_CharWidth = FONT_WIDTH+1;
985 int     giVT_CharHeight = FONT_HEIGHT;
986
987 // === CODE ===
988 /**
989  * \fn void VT_Font_Render(Uint32 Codepoint, void *Buffer, int Pitch, Uint32 BGC, Uint32 FGC)
990  * \brief Render a font character
991  */
992 void VT_Font_Render(Uint32 Codepoint, void *Buffer, int Pitch, Uint32 BGC, Uint32 FGC)
993 {
994         Uint8   *font;
995         Uint32  *buf = Buffer;
996          int    x, y;
997         
998         font = VT_Font_GetChar(Codepoint);
999         
1000         for(y = 0; y < FONT_HEIGHT; y ++)
1001         {
1002                 for(x = 0; x < FONT_WIDTH; x ++)
1003                 {
1004                         if(*font & (1 << (FONT_WIDTH-x-1)))
1005                                 buf[x] = FGC;
1006                         else
1007                                 buf[x] = BGC;
1008                 }
1009                 buf += Pitch;
1010                 font ++;
1011         }
1012 }
1013
1014 /**
1015  * \fn Uint32 VT_Colour12to24(Uint16 Col12)
1016  * \brief Converts a 
1017  */
1018 Uint32 VT_Colour12to24(Uint16 Col12)
1019 {
1020         Uint32  ret;
1021          int    tmp;
1022         tmp = Col12 & 0xF;
1023         ret  = (tmp << 0) | (tmp << 4);
1024         tmp = (Col12 & 0xF0) >> 4;
1025         ret |= (tmp << 8) | (tmp << 12);
1026         tmp = (Col12 & 0xF00) >> 8;
1027         ret |= (tmp << 16) | (tmp << 20);
1028         return ret;
1029 }
1030
1031 /**
1032  * \fn Uint8 *VT_Font_GetChar(Uint32 Codepoint)
1033  * \brief Gets an index into the font array given a Unicode Codepoint
1034  * \note See http://en.wikipedia.org/wiki/CP437
1035  */
1036 Uint8 *VT_Font_GetChar(Uint32 Codepoint)
1037 {
1038          int    index = 0;
1039         if(Codepoint < 128)
1040                 return &VTermFont[Codepoint*FONT_HEIGHT];
1041         switch(Codepoint)
1042         {
1043         case 0xC7:      index = 128;    break;  // Ç
1044         case 0xFC:      index = 129;    break;  // ü
1045         case 0xE9:      index = 130;    break;  // é
1046         case 0xE2:      index = 131;    break;  // â
1047         case 0xE4:      index = 132;    break;  // ä
1048         case 0xE0:      index = 133;    break;  // à
1049         case 0xE5:      index = 134;    break;  // å
1050         case 0xE7:      index = 135;    break;  // ç
1051         case 0xEA:      index = 136;    break;  // ê
1052         case 0xEB:      index = 137;    break;  // ë
1053         case 0xE8:      index = 138;    break;  // è
1054         case 0xEF:      index = 139;    break;  // ï
1055         case 0xEE:      index = 140;    break;  // î
1056         case 0xEC:      index = 141;    break;  // ì
1057         case 0xC4:      index = 142;    break;  // Ä
1058         case 0xC5:      index = 143;    break;  // Å
1059         }
1060         
1061         return &VTermFont[index*FONT_HEIGHT];
1062 }
1063
1064 EXPORTAS(&giVT_CharWidth, giVT_CharWidth);
1065 EXPORTAS(&giVT_CharHeight, giVT_CharHeight);
1066 EXPORT(VT_Font_Render);
1067 EXPORT(VT_Colour12to24);

UCC git Repository :: git.ucc.asn.au