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

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