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

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