Various Changes
[tpg/acess2.git] / Kernel / drv / vterm.c
1 /*
2  * Acess2 Virtual Terminal Driver
3  */
4 #include <common.h>
5 #include <fs_devfs.h>
6 #include <modules.h>
7 #include <tpl_drv_video.h>
8 #include <tpl_drv_keyboard.h>
9
10 // === CONSTANTS ===
11 #define NUM_VTS 4
12 #define MAX_INPUT_CHARS 64
13 #define VT_SCROLLBACK   1       // 4 Screens of text
14 #define DEFAULT_OUTPUT  "/Devices/VGA"
15 #define DEFAULT_INPUT   "/Devices/PS2Keyboard"
16 #define DEFAULT_WIDTH   80
17 #define DEFAULT_HEIGHT  25
18 #define DEFAULT_COLOUR  (VT_COL_BLACK|(0xAAA<<16))
19
20 #define VT_FLAG_HIDECSR 0x01
21
22 enum eVT_Modes {
23         VT_MODE_TEXT8,  // UTF-8 Text Mode (VT100 Emulation)
24         VT_MODE_TEXT32, // UTF-32 Text Mode (Acess Native)
25         VT_MODE_8BPP,   // 256 Colour Mode
26         VT_MODE_16BPP,  // 16 bit Colour Mode
27         VT_MODE_24BPP,  // 24 bit Colour Mode
28         VT_MODE_32BPP,  // 32 bit Colour Mode
29         NUM_VT_MODES
30 };
31
32 // === TYPES ===
33 typedef struct {
34          int    Mode;
35          int    Flags;
36          int    Width, Height;
37          int    ViewPos, WritePos;
38         Uint32  CurColour;
39         char    Name[2];
40          int    InputRead;
41          int    InputWrite;
42         Uint32  InputBuffer[MAX_INPUT_CHARS];
43         union {
44                 tVT_Char        *Text;
45                 Uint32          *Buffer;
46         };
47         tVFS_Node       Node;
48 } tVTerm;
49
50 // === PROTOTYPES ===
51  int    VT_Install(char **Arguments);
52 char    *VT_ReadDir(tVFS_Node *Node, int Pos);
53 tVFS_Node       *VT_FindDir(tVFS_Node *Node, char *Name);
54 Uint64  VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
55 Uint64  VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
56  int    VT_IOCtl(tVFS_Node *Node, int Id, void *Data);
57 void    VT_KBCallBack(Uint32 Codepoint);
58 void    VT_int_PutString(tVTerm *Term, Uint8 *Buffer, Uint Count);
59  int    VT_int_ParseEscape(tVTerm *Term, char *Buffer);
60 void    VT_int_PutChar(tVTerm *Term, Uint32 Ch);
61 void    VT_int_UpdateScreen( tVTerm *Term, int UpdateAll );
62
63 // === CONSTANTS ===
64 const Uint16    caVT100Colours[] = {
65                 VT_COL_BLACK, 0x700, 0x070, 0x770, 0x007, 0x707, 0x077, 0xAAA,
66                 VT_COL_GREY, 0xF00, 0x0F0, 0xFF0, 0x00F, 0xF0F, 0x0FF, VT_COL_WHITE
67         };
68
69 // === GLOBALS ===
70 MODULE_DEFINE(0, 0x0032, VTerm, VT_Install, NULL, NULL);
71 tDevFS_Driver   gVT_DrvInfo = {
72         NULL, "VTerm",
73         {
74         .Flags = VFS_FFLAG_DIRECTORY,
75         .Size = NUM_VTS,
76         .Inode = -1,
77         .NumACLs = 0,
78         .ReadDir = VT_ReadDir,
79         .FindDir = VT_FindDir
80         }
81 };
82 tVTerm  gVT_Terminals[NUM_VTS];
83 char    *gsVT_OutputDevice = NULL;
84 char    *gsVT_InputDevice = NULL;
85  int    giVT_OutputDevHandle = -2;
86  int    giVT_InputDevHandle = -2;
87  int    giVT_CurrentTerminal = 0;
88
89 // === CODE ===
90 /**
91  * \fn int VT_Install(char **Arguments)
92  * \brief Installs the Virtual Terminal Driver
93  */
94 int VT_Install(char **Arguments)
95 {
96         char    **args = Arguments;
97         char    *arg;
98          int    i;
99         
100         // Scan Arguments
101         if(Arguments)
102         {
103                 for( ; (arg = *args); args++ )
104                 {
105                         if(arg[0] != '-')       continue;
106                         
107                         switch(arg[1])
108                         {
109                         // Set output device
110                         case 'o':
111                                 if(args[1] ==  NULL)    break;
112                                 if(gsVT_OutputDevice)   free(gsVT_OutputDevice);
113                                 gsVT_OutputDevice = malloc(strlen(args[1])+1);
114                                 strcpy(gsVT_OutputDevice, args[1]);
115                                 args ++;
116                                 break;
117                         
118                         // Set input device
119                         case 'i':
120                                 if(args[1] == NULL)     break;
121                                 if(gsVT_InputDevice)    free(gsVT_InputDevice);
122                                 gsVT_InputDevice = malloc(strlen(args[1])+1);
123                                 strcpy(gsVT_InputDevice, args[1]);
124                                 args ++;
125                                 break;
126                         
127                         }
128                 }
129         }
130         
131         // Apply Defaults
132         if(!gsVT_OutputDevice)  gsVT_OutputDevice = DEFAULT_OUTPUT;
133         if(!gsVT_InputDevice)   gsVT_InputDevice = DEFAULT_INPUT;
134         
135         LOG("Using '%s' as output", gsVT_OutputDevice);
136         LOG("Using '%s' as input", gsVT_InputDevice);
137         
138         // Create Nodes
139         for( i = 0; i < NUM_VTS; i++ )
140         {
141                 gVT_Terminals[i].Mode = VT_MODE_TEXT8;
142                 gVT_Terminals[i].Flags = 0;
143                 gVT_Terminals[i].Width = DEFAULT_WIDTH;
144                 gVT_Terminals[i].Height = DEFAULT_HEIGHT;
145                 gVT_Terminals[i].CurColour = DEFAULT_COLOUR;
146                 gVT_Terminals[i].WritePos = 0;
147                 gVT_Terminals[i].ViewPos = 0;
148                 
149                 gVT_Terminals[i].Buffer = malloc( DEFAULT_WIDTH*DEFAULT_HEIGHT*VT_SCROLLBACK*sizeof(tVT_Char) );
150                 memset( gVT_Terminals[i].Buffer, 0, DEFAULT_WIDTH*DEFAULT_HEIGHT*VT_SCROLLBACK*sizeof(tVT_Char) );
151                 
152                 gVT_Terminals[i].Name[0] = '0'+i;
153                 gVT_Terminals[i].Name[1] = '\0';
154                 gVT_Terminals[i].Node.Inode = i;
155                 gVT_Terminals[i].Node.NumACLs = 0;      // Only root can open virtual terminals
156                 
157                 gVT_Terminals[i].Node.Read = VT_Read;
158                 gVT_Terminals[i].Node.Write = VT_Write;
159                 gVT_Terminals[i].Node.IOCtl = VT_IOCtl;
160         }
161         
162         // Add to DevFS
163         DevFS_AddDevice( &gVT_DrvInfo );
164         
165         return 0;
166 }
167
168 /**
169  * \fn void VT_InitOutput()
170  * \brief Initialise Video Output
171  */
172 void VT_InitOutput()
173 {
174         giVT_OutputDevHandle = VFS_Open(gsVT_OutputDevice, VFS_OPENFLAG_WRITE);
175         LOG("giVT_OutputDevHandle = %x\n", giVT_OutputDevHandle);
176 }
177
178 /**
179  * \fn void VT_InitInput()
180  * \brief Initialises the input
181  */
182 void VT_InitInput()
183 {
184         giVT_InputDevHandle = VFS_Open(gsVT_InputDevice, VFS_OPENFLAG_READ);
185         LOG("giVT_InputDevHandle = %x\n", giVT_InputDevHandle);
186         if(giVT_InputDevHandle == -1)   return ;
187         VFS_IOCtl(giVT_InputDevHandle, KB_IOCTL_SETCALLBACK, VT_KBCallBack);
188 }
189
190 /**
191  * \fn char *VT_ReadDir(tVFS_Node *Node, int Pos)
192  * \brief Read from the VTerm Directory
193  */
194 char *VT_ReadDir(tVFS_Node *Node, int Pos)
195 {
196         if(Pos < 0)     return NULL;
197         if(Pos >= NUM_VTS)      return NULL;
198         return strdup( gVT_Terminals[Pos].Name );
199 }
200
201 /**
202  * \fn tVFS_Node *VT_FindDir(tVFS_Node *Node, char *Name)
203  * \brief Find an item in the VTerm directory
204  */
205 tVFS_Node *VT_FindDir(tVFS_Node *Node, char *Name)
206 {
207          int    num;
208         
209         //ENTER("pNode sName", Node, Name);
210         
211         // Open the input and output files if needed
212         if(giVT_OutputDevHandle == -2)  VT_InitOutput();
213         if(giVT_InputDevHandle == -2)   VT_InitInput();
214         
215         // Sanity check name
216         if(Name[0] < '0' || Name[0] > '9' || Name[1] != '\0') {
217                 LEAVE('n');
218                 return NULL;
219         }
220         // Get index
221         num = Name[0] - '0';
222         if(num >= NUM_VTS) {
223                 LEAVE('n');
224                 return NULL;
225         }
226         // Return node
227         //LEAVE('p', &gVT_Terminals[num].Node);
228         return &gVT_Terminals[num].Node;
229 }
230
231 /**
232  * \fn Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
233  * \brief Read from a virtual terminal
234  */
235 Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
236 {
237          int    pos = 0;
238         tVTerm  *term = &gVT_Terminals[ Node->Inode ];
239         
240         // Check current mode
241         switch(term->Mode)
242         {
243         case VT_MODE_TEXT8:
244                 while(pos < Length)
245                 {
246                         while(term->InputRead == term->InputWrite)      Threads_Yield();
247                         while(term->InputRead != term->InputWrite)
248                         {
249                                 pos += WriteUTF8(Buffer+pos, term->InputBuffer[term->InputRead]);
250                                 term->InputRead ++;
251                                 term->InputRead %= MAX_INPUT_CHARS;
252                         }
253                 }
254                 break;
255         
256         case VT_MODE_TEXT32:
257                 while(pos < Length)
258                 {
259                         while(term->InputRead == term->InputWrite)      Threads_Yield();
260                         while(term->InputRead != term->InputWrite)
261                         {
262                                 ((Uint32*)Buffer)[pos] = term->InputBuffer[term->InputRead];
263                                 pos ++;
264                                 term->InputRead ++;
265                                 term->InputRead %= MAX_INPUT_CHARS;
266                         }
267                 }
268                 break;
269         }
270         return 0;
271 }
272
273 /**
274  * \fn Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
275  * \brief Write to a virtual terminal
276  */
277 Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
278 {
279         tVTerm  *term = &gVT_Terminals[ Node->Inode ];
280         
281         //ENTER("pNode XOffset XLength pBuffer",  Node, Offset, Length, Buffer);
282         
283         // Write
284         switch( term->Mode )
285         {
286         case VT_MODE_TEXT8:
287                 VT_int_PutString(term, Buffer, Length);
288                 break;
289         case VT_MODE_TEXT32:
290                 //VT_int_PutString32(term, Buffer, Length);
291                 break;
292         }
293         
294         //LEAVE('i', 0);
295         return 0;
296 }
297
298 /**
299  * \fn int VT_IOCtl(tVFS_Node *Node, int Id, void *Data)
300  * \brief Call an IO Control on a virtual terminal
301  */
302 int VT_IOCtl(tVFS_Node *Node, int Id, void *Data)
303 {
304         return 0;
305 }
306
307 /**
308  * \fn void VT_KBCallBack(Uint32 Codepoint)
309  * \brief Called on keyboard interrupt
310  */
311 void VT_KBCallBack(Uint32 Codepoint)
312 {
313         tVTerm  *term = &gVT_Terminals[giVT_CurrentTerminal];
314         
315         term->InputBuffer[ term->InputWrite ] = Codepoint;
316         term->InputWrite ++;
317         term->InputWrite %= MAX_INPUT_CHARS;
318         if(term->InputRead == term->InputWrite) {
319                 term->InputRead ++;
320                 term->InputRead %= MAX_INPUT_CHARS;
321         }
322 }
323
324 /**
325  * \fn void VT_int_PutString(tVTerm *Term, Uint8 *Buffer, Uint Count)
326  * \brief Print a string to the Virtual Terminal
327  */
328 void VT_int_PutString(tVTerm *Term, Uint8 *Buffer, Uint Count)
329 {
330         Uint32  val;
331          int    i;
332         for( i = 0; i < Count; i++ )
333         {
334                 if( Buffer[i] == 0x1B ) // Escape Sequence
335                 {
336                         i ++;
337                         i += VT_int_ParseEscape(Term, (char*)&Buffer[i]);
338                         continue;
339                 }
340                 
341                 if( Buffer[i] < 128 )   // Plain ASCII
342                         VT_int_PutChar(Term, Buffer[i]);
343                 else {  // UTF-8
344                         i += ReadUTF8(&Buffer[i], &val);
345                         VT_int_PutChar(Term, val);
346                 }
347         }
348         
349         // Update cursor
350         if( !(Term->Flags & VT_FLAG_HIDECSR) )
351         {
352                 tVideo_IOCtl_Pos        pos;
353                 pos.x = Term->WritePos % Term->Width;
354                 pos.y = Term->WritePos / Term->Width;
355                 VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSOR, &pos);
356         }
357 }
358
359 /**
360  * \fn void VT_int_ClearLine(tVTerm *Term, int Num)
361  * \brief Clears a line in a virtual terminal
362  */
363 void VT_int_ClearLine(tVTerm *Term, int Num)
364 {
365          int    i;
366         //ENTER("pTerm iNum", Term, Num);
367         for( i = Term->Width; i--; )
368         {
369                 Term->Text[ Num*Term->Width + i ].Ch = 0;
370                 Term->Text[ Num*Term->Width + i ].Colour = Term->CurColour;
371         }
372         //LEAVE('-');
373 }
374
375 /**
376  * \fn int VT_int_ParseEscape(tVTerm *Term, char *Buffer)
377  * \brief Parses a VT100 Escape code
378  */
379 int VT_int_ParseEscape(tVTerm *Term, char *Buffer)
380 {
381         char    c;
382          int    argc = 0, j = 1;
383          int    args[4] = {0,0,0,0};
384         
385         switch(Buffer[0]) {
386         //Large Code
387         case '[':
388                 // Get Arguments
389                 c = Buffer[1];
390                 do {
391                         while('0' <= c && c <= '9') {
392                                 args[argc] *= 10;
393                                 args[argc] += c-'0';
394                                 c = Buffer[++j];
395                         }
396                         argc ++;
397                 } while(c == ';');
398                 
399                 // Get string (what does this do?)
400                 if(c == '"') {
401                         c = Buffer[++j];
402                         while(c != '"')
403                                 c = Buffer[++j];
404                 }
405                 
406                 // Get Command
407                 if(     ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'))
408                 {
409                         switch(c) {
410                         //Clear By Line
411                         case 'J':
412                                 // Clear Screen
413                                 switch(args[0])
414                                 {
415                                 case 2:
416                                         {
417                                          int    i = Term->Height * VT_SCROLLBACK;
418                                         while( i-- )    VT_int_ClearLine(Term, i);
419                                         Term->WritePos = 0;
420                                         Term->ViewPos = 0;
421                                         VT_int_UpdateScreen(Term, 1);
422                                         }
423                                         break;
424                                 }
425                                 break;
426                         // Set Font flags
427                         case 'm':
428                                 for( ; argc--; )
429                                 {
430                                         // Flags
431                                         if( 0 <= args[argc] && args[argc] <= 8)
432                                         {
433                                                 switch(args[argc])
434                                                 {
435                                                 case 0: Term->CurColour = DEFAULT_COLOUR;       break;  // Reset
436                                                 case 1: Term->CurColour |= 0x80000000;  break;  // Bright
437                                                 case 2: Term->CurColour &= ~0x80000000; break;  // Dim
438                                                 }
439                                         }
440                                         // Foreground Colour
441                                         else if(30 <= args[argc] && args[argc] <= 37) {
442                                                 Term->CurColour &= 0xF000FFFF;
443                                                 Term->CurColour |= (Uint32)caVT100Colours[ args[argc]-30+(Term->CurColour>>28) ] << 16;
444                                         }
445                                         // Background Colour
446                                         else if(40 <= args[argc] && args[argc] <= 47) {
447                                                 Term->CurColour &= 0xFFFF8000;
448                                                 Term->CurColour |= caVT100Colours[ args[argc]-40+((Term->CurColour>>12)&15) ];
449                                         }
450                                 }
451                                 break;
452                         }
453                 }
454                 break;
455                 
456         default:
457                 break;
458         }
459         
460         return j + 1;
461 }
462
463 /**
464  * \fn void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
465  * \brief Write a single character to a VTerm
466  */
467 void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
468 {
469          int    i;
470         //ENTER("pTerm xCh", Term, Ch);
471         //LOG("Term = {WritePos:%i, ViewPos:%i}\n", Term->WritePos, Term->ViewPos);
472         
473         switch(Ch)
474         {
475         case '\0':      return; // Ignore NULL byte
476         case '\n':
477                 Term->WritePos += Term->Width;
478         case '\r':
479                 Term->WritePos -= Term->WritePos % Term->Width;
480                 break;
481         
482         case '\t':
483                 do {
484                         Term->Text[ Term->WritePos ].Ch = '\0';
485                         Term->Text[ Term->WritePos ].Colour = Term->CurColour;
486                         Term->WritePos ++;
487                 } while(Term->WritePos & 7);
488                 break;
489         
490         case '\b':
491                 // Backspace is invalid at Offset 0
492                 if(Term->WritePos == 0) break;
493                 
494                 Term->WritePos --;
495                 // Singe Character
496                 if(Term->Text[ Term->WritePos ].Ch != '\0') {
497                         Term->Text[ Term->WritePos ].Ch = 0;
498                         Term->Text[ Term->WritePos ].Colour = Term->CurColour;
499                         break;
500                 }
501                 // Tab
502                 i = 7;  // Limit it to 8
503                 do {
504                         Term->Text[ Term->WritePos ].Ch = 0;
505                         Term->Text[ Term->WritePos ].Colour = Term->CurColour;
506                         Term->WritePos --;
507                 } while(Term->WritePos && i-- && Term->Text[ Term->WritePos ].Ch == '\0');
508                 if(Term->Text[ Term->WritePos ].Ch != '\0')
509                         Term->WritePos ++;
510                 break;
511         
512         default:
513                 Term->Text[ Term->WritePos ].Ch = Ch;
514                 Term->Text[ Term->WritePos ].Colour = Term->CurColour;
515                 Term->WritePos ++;
516                 break;
517         }
518                 
519         
520         // Move Screen
521         if(Term->WritePos >= Term->Width*Term->Height*VT_SCROLLBACK)
522         {
523                  int    base, i;
524                 Term->WritePos -= Term->Width;
525                 
526                 // Update view position
527                 base = Term->Width*Term->Height*(VT_SCROLLBACK-1);
528                 if(Term->ViewPos < base)        Term->ViewPos += Term->Width;
529                 if(Term->ViewPos > base)        Term->ViewPos = base;
530                 
531                 // Scroll terminal cache
532                 base = Term->Width*(Term->Height*VT_SCROLLBACK-1);
533                 
534                 // Scroll Back
535                 memcpy(
536                         Term->Text,
537                         &Term->Text[Term->Width],
538                         Term->Width*(Term->Height-1)*VT_SCROLLBACK*sizeof(tVT_Char)
539                         );
540                 
541                 // Clear last row
542                 for( i = 0; i < Term->Width; i ++ )
543                 {
544                         Term->Text[ base + i ].Ch = 0;
545                         Term->Text[ base + i ].Colour = Term->CurColour;
546                 }
547                 
548                 VT_int_UpdateScreen( Term, 1 );
549         }
550         else if(Term->WritePos > Term->Width*Term->Height+Term->ViewPos)
551         {
552                 Term->ViewPos += Term->Width;
553                 VT_int_UpdateScreen( Term, 1 );
554         }
555         else
556                 VT_int_UpdateScreen( Term, 0 );
557         
558         //LEAVE('-');
559 }
560
561 /**
562  * \fn void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll )
563  * \brief Updates the video framebuffer
564  */
565 void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll )
566 {
567         if(UpdateAll) {
568                 VFS_WriteAt(
569                         giVT_OutputDevHandle,
570                         0,
571                         Term->Width*Term->Height*sizeof(tVT_Char),
572                         &Term->Text[Term->ViewPos]
573                         );
574         } else {
575                  int    pos = Term->WritePos - Term->WritePos % Term->Width;
576                 VFS_WriteAt(
577                         giVT_OutputDevHandle,
578                         (pos - Term->ViewPos)*sizeof(tVT_Char),
579                         Term->Width*sizeof(tVT_Char),
580                         &Term->Text[pos]
581                         );
582         }
583 }
584
585 // ---
586 // Font Render
587 // ---
588 #define MONOSPACE_FONT  10816
589
590 #if MONOSPACE_FONT == 10808     // 8x8
591 # include "vterm_font_8x8.h"
592 #elif MONOSPACE_FONT == 10816   // 8x16
593 # include "vterm_font_8x16.h"
594 #endif
595
596 // === PROTOTYPES ===
597 Uint8   *VT_Font_GetChar(Uint32 Codepoint);
598
599 // === GLOBALS ===
600 int     giVT_CharWidth = FONT_WIDTH;
601 int     giVT_CharHeight = FONT_HEIGHT;
602
603 // === CODE ===
604 /**
605  * \fn void VT_Font_Render(Uint32 Codepoint, void *Buffer, int Pitch, Uint32 BGC, Uint32 FGC)
606  * \brief Render a font character
607  */
608 void VT_Font_Render(Uint32 Codepoint, void *Buffer, int Pitch, Uint32 BGC, Uint32 FGC)
609 {
610         Uint8   *font;
611         Uint32  *buf = Buffer;
612          int    x, y;
613         font = VT_Font_GetChar(Codepoint);
614         
615         for(y = 0; y < FONT_HEIGHT; y ++)
616         {
617                 for(x = 0; x < FONT_WIDTH; x ++)
618                 {
619                         if(*font & (1 << (FONT_WIDTH-x)))
620                                 buf[x] = FGC;
621                         else
622                                 buf[x] = BGC;
623                 }
624                 buf += Pitch;
625                 font ++;
626         }
627 }
628
629 /**
630  * \fn Uint32 VT_Colour12to24(Uint16 Col12)
631  * \brief Converts a 
632  */
633 Uint32 VT_Colour12to24(Uint16 Col12)
634 {
635         Uint32  ret;
636          int    tmp;
637         tmp = Col12 & 0xF;
638         ret  = (tmp << 0) | (tmp << 4);
639         tmp = (Col12 & 0xF0) >> 4;
640         ret |= (tmp << 8) | (tmp << 12);
641         tmp = (Col12 & 0xF00) >> 8;
642         ret |= (tmp << 16) | (tmp << 20);
643         return ret;
644 }
645
646 /**
647  * \fn Uint8 *VT_Font_GetChar(Uint32 Codepoint)
648  * \brief Gets an index into the font array given a Unicode Codepoint
649  * \note See http://en.wikipedia.org/wiki/CP437
650  */
651 Uint8 *VT_Font_GetChar(Uint32 Codepoint)
652 {
653          int    index = 0;
654         if(Codepoint < 128)
655                 return &VTermFont[Codepoint*FONT_HEIGHT];
656         switch(Codepoint)
657         {
658         case 0xC7:      index = 128;    break;  // Ç
659         case 0xFC:      index = 129;    break;  // ü
660         case 0xE9:      index = 130;    break;  // é
661         case 0xE2:      index = 131;    break;  // â
662         case 0xE4:      index = 132;    break;  // ä
663         case 0xE0:      index = 133;    break;  // à
664         case 0xE5:      index = 134;    break;  // å
665         case 0xE7:      index = 135;    break;  // ç
666         case 0xEA:      index = 136;    break;  // ê
667         case 0xEB:      index = 137;    break;  // ë
668         case 0xE8:      index = 138;    break;  // è
669         case 0xEF:      index = 139;    break;  // ï
670         case 0xEE:      index = 140;    break;  // î
671         case 0xEC:      index = 141;    break;  // ì
672         case 0xC4:      index = 142;    break;  // Ä
673         case 0xC5:      index = 143;    break;  // Å
674         }
675         
676         return &VTermFont[index*FONT_HEIGHT];
677 }
678
679 EXPORTAS(&giVT_CharWidth, giVT_CharWidth);
680 EXPORTAS(&giVT_CharHeight, giVT_CharHeight);
681 EXPORT(VT_Font_Render);
682 EXPORT(VT_Colour12to24);

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