Fixed `make install` issues, bugs with VESA
[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 DEFAULT_OUTPUT        "BochsGA"
22 #define DEFAULT_OUTPUT  "Vesa"
23 #define DEFAULT_INPUT   "PS2Keyboard"
24 #define DEFAULT_WIDTH   640
25 #define DEFAULT_HEIGHT  480
26 #define DEFAULT_SCROLLBACK      2       // 2 Screens of text + current screen
27 #define DEFAULT_COLOUR  (VT_COL_BLACK|(0xAAA<<16))
28
29 #define VT_FLAG_HIDECSR 0x01
30 #define VT_FLAG_HASFB   0x10    //!< Set if the VTerm has requested the Framebuffer
31
32 enum eVT_InModes {
33         VT_INMODE_TEXT8,        // UTF-8 Text Mode (VT100/xterm Emulation)
34         VT_INMODE_TEXT32,       // UTF-32 Text Mode (Acess Native)
35         NUM_VT_INMODES
36 };
37
38 // === TYPES ===
39 typedef struct {
40          int    Mode;   //!< Current Mode (see ::eTplTerminal_Modes)
41          int    Flags;  //!< Flags (see VT_FLAG_*)
42         
43         short   NewWidth;       //!< Un-applied dimensions (Width)
44         short   NewHeight;      //!< Un-applied dimensions (Height)
45         short   Width;  //!< Virtual Width
46         short   Height; //!< Virtual Height
47         short   TextWidth;      //!< Text Virtual Width
48         short   TextHeight;     //!< Text Virtual Height
49         
50          int    ViewPos;        //!< View Buffer Offset (Text Only)
51          int    WritePos;       //!< Write Buffer Offset (Text Only)
52         Uint32  CurColour;      //!< Current Text Colour
53         
54         tMutex  ReadingLock;    //!< Lock the VTerm when a process is reading from it
55         tTID    ReadingThread;  //!< Owner of the lock
56          int    InputRead;      //!< Input buffer read position
57          int    InputWrite;     //!< Input buffer write position
58         char    InputBuffer[MAX_INPUT_CHARS8];
59         
60         tVT_Char        *Text;
61         Uint32          *Buffer;
62         
63         char    Name[2];        //!< Name of the terminal
64         tVFS_Node       Node;
65 } tVTerm;
66
67 // === IMPORTS ===
68 extern void     Debug_SetKTerminal(const char *File);
69
70 // === PROTOTYPES ===
71  int    VT_Install(char **Arguments);
72 void    VT_InitOutput(void);
73 void    VT_InitInput(void);
74 char    *VT_ReadDir(tVFS_Node *Node, int Pos);
75 tVFS_Node       *VT_FindDir(tVFS_Node *Node, const char *Name);
76  int    VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data);
77 Uint64  VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
78 Uint64  VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
79  int    VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data);
80 void    VT_SetResolution(int Width, int Height);
81 void    VT_SetMode(int Mode);
82 void    VT_SetTerminal(int ID);
83 void    VT_KBCallBack(Uint32 Codepoint);
84 void    VT_int_PutString(tVTerm *Term, Uint8 *Buffer, Uint Count);
85 void    VT_int_ClearLine(tVTerm *Term, int Num);
86  int    VT_int_ParseEscape(tVTerm *Term, char *Buffer);
87 void    VT_int_PutChar(tVTerm *Term, Uint32 Ch);
88 void    VT_int_ScrollFramebuffer( tVTerm *Term );
89 void    VT_int_UpdateScreen( tVTerm *Term, int UpdateAll );
90 void    VT_int_ChangeMode(tVTerm *Term, int NewMode, int NewWidth, int NewHeight);
91
92 // === CONSTANTS ===
93 const Uint16    caVT100Colours[] = {
94                 // Black, Red, Green, Yellow, Blue, Purple, Cyan, Gray
95                 // Same again, but bright
96                 VT_COL_BLACK, 0x700, 0x070, 0x770, 0x007, 0x707, 0x077, 0xAAA,
97                 VT_COL_GREY, 0xF00, 0x0F0, 0xFF0, 0x00F, 0xF0F, 0x0FF, VT_COL_WHITE
98         };
99
100 // === GLOBALS ===
101 MODULE_DEFINE(0, VERSION, VTerm, VT_Install, NULL, DEFAULT_OUTPUT, DEFAULT_INPUT, NULL);
102 tDevFS_Driver   gVT_DrvInfo = {
103         NULL, "VTerm",
104         {
105         .Flags = VFS_FFLAG_DIRECTORY,
106         .Size = NUM_VTS,
107         .Inode = -1,
108         .NumACLs = 0,
109         .ReadDir = VT_ReadDir,
110         .FindDir = VT_FindDir,
111         .IOCtl = VT_Root_IOCtl
112         }
113 };
114 // --- Terminals ---
115 tVTerm  gVT_Terminals[NUM_VTS];
116  int    giVT_CurrentTerminal = 0;
117 tVTerm  *gpVT_CurTerm = &gVT_Terminals[0];
118 // --- Video State ---
119 short   giVT_RealWidth  = DEFAULT_WIDTH;        //!< Screen Width
120 short   giVT_RealHeight = DEFAULT_HEIGHT;       //!< Screen Height
121  int    giVT_Scrollback = DEFAULT_SCROLLBACK;
122 // --- Driver Handles ---
123 char    *gsVT_OutputDevice = NULL;
124 char    *gsVT_InputDevice = NULL;
125  int    giVT_OutputDevHandle = -2;
126  int    giVT_InputDevHandle = -2;
127 // --- Key States --- (Used for VT Switching/Magic Combos)
128  int    gbVT_CtrlDown = 0;
129  int    gbVT_AltDown = 0;
130  int    gbVT_SysrqDown = 0;
131
132 // === CODE ===
133 /**
134  * \fn int VT_Install(char **Arguments)
135  * \brief Installs the Virtual Terminal Driver
136  */
137 int VT_Install(char **Arguments)
138 {
139          int    i;
140         
141         // Scan Arguments
142         if(Arguments)
143         {
144                 char    **args;
145                 const char      *arg;
146                 for(args = Arguments; (arg = *args); args++ )
147                 {
148                         char    data[strlen(arg)+1];
149                         char    *opt = data;
150                         char    *val;
151                         
152                         val = strchr(arg, '=');
153                         strcpy(data, arg);
154                         if( val ) {
155                                 data[ val - arg ] = '\0';
156                                 val ++;
157                         }
158                         Log_Debug("VTerm", "Argument '%s'", arg);
159                         
160                         if( strcmp(opt, "Video") == 0 ) {
161                                 gsVT_OutputDevice = strdup(val);
162                         }
163                         else if( strcmp(opt, "Input") == 0 ) {
164                                 gsVT_InputDevice = strdup(val);
165                         }
166                         else if( strcmp(opt, "Width") == 0 ) {
167                                 giVT_RealWidth = atoi( val );
168                         }
169                         else if( strcmp(opt, "Height") == 0 ) {
170                                 giVT_RealHeight = atoi( val );
171                         }
172                         else if( strcmp(opt, "Scrollback") == 0 ) {
173                                 giVT_Scrollback = atoi( val );
174                         }
175                 }
176         }
177         
178         if(gsVT_OutputDevice)   Modules_InitialiseBuiltin( gsVT_OutputDevice );
179         if(gsVT_InputDevice)    Modules_InitialiseBuiltin( gsVT_InputDevice );
180         
181         // Apply Defaults
182         if(!gsVT_OutputDevice)  gsVT_OutputDevice = strdup(DEFAULT_OUTPUT);
183         if(!gsVT_InputDevice)   gsVT_InputDevice = strdup(DEFAULT_INPUT);
184         
185         // Create paths
186         {
187                 char    *tmp;
188                 tmp = malloc( 9 + strlen(gsVT_OutputDevice) + 1 );
189                 strcpy(tmp, "/Devices/");
190                 strcpy(&tmp[9], gsVT_OutputDevice);
191                 gsVT_OutputDevice = tmp;
192                 tmp = malloc( 9 + strlen(gsVT_InputDevice) + 1 );
193                 strcpy(tmp, "/Devices/");
194                 strcpy(&tmp[9], gsVT_InputDevice);
195                 gsVT_InputDevice = tmp;
196         }
197         
198         Log_Log("VTerm", "Using '%s' as output", gsVT_OutputDevice);
199         Log_Log("VTerm", "Using '%s' as input", gsVT_InputDevice);
200         
201         // Create Nodes
202         for( i = 0; i < NUM_VTS; i++ )
203         {
204                 gVT_Terminals[i].Mode = TERM_MODE_TEXT;
205                 gVT_Terminals[i].Flags = 0;
206                 gVT_Terminals[i].CurColour = DEFAULT_COLOUR;
207                 gVT_Terminals[i].WritePos = 0;
208                 gVT_Terminals[i].ViewPos = 0;
209                 gVT_Terminals[i].ReadingThread = -1;
210                 
211                 // Initialise
212                 VT_int_ChangeMode( &gVT_Terminals[i],
213                         TERM_MODE_TEXT, giVT_RealWidth, giVT_RealHeight );
214                 
215                 gVT_Terminals[i].Name[0] = '0'+i;
216                 gVT_Terminals[i].Name[1] = '\0';
217                 gVT_Terminals[i].Node.Inode = i;
218                 gVT_Terminals[i].Node.ImplPtr = &gVT_Terminals[i];
219                 gVT_Terminals[i].Node.NumACLs = 0;      // Only root can open virtual terminals
220                 
221                 gVT_Terminals[i].Node.Read = VT_Read;
222                 gVT_Terminals[i].Node.Write = VT_Write;
223                 gVT_Terminals[i].Node.IOCtl = VT_Terminal_IOCtl;
224         }
225         
226         // Add to DevFS
227         DevFS_AddDevice( &gVT_DrvInfo );
228         
229         VT_InitOutput();
230         VT_InitInput();
231         
232         // Set kernel output to VT0
233         Debug_SetKTerminal("/Devices/VTerm/0");
234         
235         Log_Log("VTerm", "Returning %i", MODULE_ERR_OK);
236         return MODULE_ERR_OK;
237 }
238
239 /**
240  * \fn void VT_InitOutput()
241  * \brief Initialise Video Output
242  */
243 void VT_InitOutput()
244 {
245         giVT_OutputDevHandle = VFS_Open(gsVT_OutputDevice, VFS_OPENFLAG_WRITE);
246         if(giVT_OutputDevHandle == -1) {
247                 Log_Warning("VTerm", "Oh F**k, I can't open the video device '%s'", gsVT_OutputDevice);
248                 return ;
249         }
250         VT_SetResolution( giVT_RealWidth, giVT_RealHeight );
251         VT_SetTerminal( 0 );
252         VT_SetMode( VIDEO_BUFFMT_TEXT );
253 }
254
255 /**
256  * \fn void VT_InitInput()
257  * \brief Initialises the input
258  */
259 void VT_InitInput()
260 {
261         giVT_InputDevHandle = VFS_Open(gsVT_InputDevice, VFS_OPENFLAG_READ);
262         if(giVT_InputDevHandle == -1) {
263                 Log_Warning("VTerm", "Can't open the input device '%s'", gsVT_InputDevice);
264                 return ;
265         }
266         VFS_IOCtl(giVT_InputDevHandle, KB_IOCTL_SETCALLBACK, VT_KBCallBack);
267 }
268
269 /**
270  * \brief Set the video resolution
271  * \param Width New screen width
272  * \param Height        New screen height
273  */
274 void VT_SetResolution(int Width, int Height)
275 {
276         tVideo_IOCtl_Mode       mode = {0};
277          int    tmp;
278          int    i;
279         
280         // Create the video mode
281         mode.width = Width;
282         mode.height = Height;
283         mode.bpp = 32;
284         mode.flags = 0;
285         
286         // Set video mode
287         VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_FINDMODE, &mode );
288         tmp = mode.id;
289         if( Width != mode.width || Height != mode.height )
290         {
291                 Log_Warning("VTerm",
292                         "Selected resolution (%ix%i is not supported) by the device, using (%ix%i)",
293                         giVT_RealWidth, giVT_RealHeight,
294                         mode.width, mode.height
295                         );
296         }
297         VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_GETSETMODE, &tmp );
298         
299         // Resize text terminals if needed
300         if( giVT_RealWidth != mode.width || giVT_RealHeight != mode.height )
301         {
302                  int    newBufSize = (giVT_RealWidth/giVT_CharWidth)
303                                         *(giVT_RealHeight/giVT_CharHeight)
304                                         *(giVT_Scrollback+1);
305                 //tVT_Char      *tmp;
306                 // Resize the text terminals
307                 giVT_RealWidth = mode.width;
308                 giVT_RealHeight = mode.height;
309                 for( i = 0; i < NUM_VTS; i ++ )
310                 {
311                         if( gVT_Terminals[i].Mode != TERM_MODE_TEXT )   continue;
312                         
313                         gVT_Terminals[i].Text = realloc(
314                                 gVT_Terminals[i].Text,
315                                 newBufSize*sizeof(tVT_Char)
316                                 );
317                 }
318         }
319 }
320
321 /**
322  * \brief Set video output buffer mode
323  */
324 void VT_SetMode(int Mode)
325 {
326         VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &Mode );
327 }
328
329 /**
330  * \fn char *VT_ReadDir(tVFS_Node *Node, int Pos)
331  * \brief Read from the VTerm Directory
332  */
333 char *VT_ReadDir(tVFS_Node *Node, int Pos)
334 {
335         if(Pos < 0)     return NULL;
336         if(Pos >= NUM_VTS)      return NULL;
337         return strdup( gVT_Terminals[Pos].Name );
338 }
339
340 /**
341  * \fn tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name)
342  * \brief Find an item in the VTerm directory
343  * \param Node  Root node
344  * \param Name  Name (number) of the terminal
345  */
346 tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name)
347 {
348          int    num;
349         
350         ENTER("pNode sName", Node, Name);
351         
352         // Open the input and output files if needed
353         if(giVT_OutputDevHandle == -2)  VT_InitOutput();
354         if(giVT_InputDevHandle == -2)   VT_InitInput();
355         
356         // Sanity check name
357         if(Name[0] < '0' || Name[0] > '9' || Name[1] != '\0') {
358                 LEAVE('n');
359                 return NULL;
360         }
361         // Get index
362         num = Name[0] - '0';
363         if(num >= NUM_VTS) {
364                 LEAVE('n');
365                 return NULL;
366         }
367         // Return node
368         LEAVE('p', &gVT_Terminals[num].Node);
369         return &gVT_Terminals[num].Node;
370 }
371
372 /**
373  * \fn int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data)
374  * \brief Control the VTerm Driver
375  */
376 int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data)
377 {
378          int    len;
379         switch(Id)
380         {
381         case DRV_IOCTL_TYPE:    return DRV_TYPE_MISC;
382         case DRV_IOCTL_IDENT:   memcpy(Data, "VT\0\0", 4);      return 0;
383         case DRV_IOCTL_VERSION: return VERSION;
384         case DRV_IOCTL_LOOKUP:  return 0;
385         
386         case 4: // Get Video Driver
387                 if(Data)        strcpy(Data, gsVT_OutputDevice);
388                 return strlen(gsVT_OutputDevice);
389         
390         case 5: // Set Video Driver
391                 if(!Data)       return -EINVAL;
392                 if(Threads_GetUID() != 0)       return -EACCES;
393                 
394                 len = strlen(Data);
395                 
396                 // TODO: Check if the string used is a heap string
397                 
398                 free(gsVT_OutputDevice);
399                 
400                 gsVT_OutputDevice = malloc(len+1);
401                 strcpy(gsVT_OutputDevice, Data);
402                 
403                 VFS_Close(giVT_OutputDevHandle);
404                 giVT_OutputDevHandle = -1;
405                 
406                 VT_InitOutput();
407                 return 1;
408         }
409         return 0;
410 }
411
412 /**
413  * \fn Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
414  * \brief Read from a virtual terminal
415  */
416 Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
417 {
418          int    pos = 0;
419         tVTerm  *term = &gVT_Terminals[ Node->Inode ];
420         
421         Mutex_Acquire( &term->ReadingLock );
422         term->ReadingThread = Threads_GetTID();
423         
424         // Check current mode
425         switch(term->Mode)
426         {
427         // Text Mode (UTF-8)
428         case TERM_MODE_TEXT:
429                 while(pos < Length)
430                 {
431                         //TODO: Sleep instead
432                         while(term->InputRead == term->InputWrite)      Threads_Sleep();
433                         
434                         ((char*)Buffer)[pos] = term->InputBuffer[term->InputRead];
435                         pos ++;
436                         term->InputRead ++;
437                         term->InputRead %= MAX_INPUT_CHARS8;
438                 }
439                 break;
440         
441         //case TERM_MODE_FB:
442         // Other - UCS-4
443         default:
444                 while(pos < Length)
445                 {
446                         while(term->InputRead == term->InputWrite)      Threads_Sleep();
447                         ((Uint32*)Buffer)[pos] = ((Uint32*)term->InputBuffer)[term->InputRead];
448                         pos ++;
449                         term->InputRead ++;
450                         term->InputRead %= MAX_INPUT_CHARS32;
451                 }
452                 break;
453         }
454         
455         term->ReadingThread = -1;
456         Mutex_Release( &term->ReadingLock );
457         
458         return 0;
459 }
460
461 /**
462  * \fn Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
463  * \brief Write to a virtual terminal
464  */
465 Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
466 {
467         tVTerm  *term = &gVT_Terminals[ Node->Inode ];
468          int    size;
469         
470         // Write
471         switch( term->Mode )
472         {
473         // Print Text
474         case TERM_MODE_TEXT:
475                 VT_int_PutString(term, Buffer, Length);
476                 break;
477         // Framebuffer :)
478         case TERM_MODE_FB:
479         
480                 // - Sanity Checking
481                 size = term->Width*term->Height*4;
482                 if( Offset > size ) {
483                         Log_Notice("VTerm", "VT_Write: Offset (0x%llx) > FBSize (0x%x)",
484                                 Offset, size);
485                         return 0;
486                 }
487                 if( Offset + Length > size ) {
488                         Log_Notice("VTerm", "VT_Write: Offset+Length (0x%llx) > FBSize (0x%x)",
489                                 Offset+Length, size);
490                         Length = size - Offset;
491                 }
492                 
493                 // Copy to the local cache
494                 memcpy( (void*)((Uint)term->Buffer + (Uint)Offset), Buffer, Length );
495                 
496                 // Update screen if needed
497                 if( Node->Inode == giVT_CurrentTerminal )
498                 {
499                         // Fill entire screen?
500                         if( giVT_RealWidth > term->Width || giVT_RealHeight > term->Height )
501                         {
502                                 // No? :( Well, just center it
503                                  int    x, y, w, h;
504                                 x = Offset/4;   y = x / term->Width;    x %= term->Width;
505                                 w = Length/4+x; h = w / term->Width;    w %= term->Width;
506                                 // Center
507                                 x += (giVT_RealWidth - term->Width) / 2;
508                                 y += (giVT_RealHeight - term->Height) / 2;
509                                 while(h--)
510                                 {
511                                         VFS_WriteAt( giVT_OutputDevHandle,
512                                                 (x + y * giVT_RealWidth)*4,
513                                                 term->Width * 4,
514                                                 Buffer
515                                                 );
516                                         Buffer = (void*)( (Uint)Buffer + term->Width*4 );
517                                         y ++;
518                                 }
519                                 return 0;
520                         }
521                         else {
522                                 return VFS_WriteAt( giVT_OutputDevHandle, Offset, Length, Buffer );
523                         }
524                 }
525         // Just pass on (for now)
526         // TODO: Handle locally too to ensure no information is lost on
527         //       VT Switch (and to isolate terminals from each other)
528         case TERM_MODE_2DACCEL:
529         //case TERM_MODE_3DACCEL:
530                 if( Node->Inode == giVT_CurrentTerminal )
531                 {
532                         VFS_Write( giVT_OutputDevHandle, Length, Buffer );
533                 }
534                 break;
535         }
536         
537         return 0;
538 }
539
540 /**
541  * \fn int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data)
542  * \brief Call an IO Control on a virtual terminal
543  */
544 int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data)
545 {
546          int    *iData = Data;
547          int    ret;
548         tVTerm  *term = Node->ImplPtr;
549         ENTER("pNode iId pData", Node, Id, Data);
550         
551         if(Id >= DRV_IOCTL_LOOKUP) {
552                 // Only root can fiddle with graphics modes
553                 // TODO: Remove this and replace with user ownership
554                 if( Threads_GetUID() != 0 )     return -1;
555         }
556         
557         switch(Id)
558         {
559         // --- Core Defined
560         case DRV_IOCTL_TYPE:
561                 LEAVE('i', DRV_TYPE_TERMINAL);
562                 return DRV_TYPE_TERMINAL;
563         case DRV_IOCTL_IDENT:
564                 memcpy(Data, "VT\0\0", 4);
565                 LEAVE('i', 0);
566                 return 0;
567         case DRV_IOCTL_VERSION:
568                 LEAVE('x', VERSION);
569                 return VERSION;
570         case DRV_IOCTL_LOOKUP:
571                 LEAVE('i', 0);
572                 return 0;
573         
574         // Get/Set the mode (and apply any changes)
575         case TERM_IOCTL_MODETYPE:
576                 if(Data != NULL)
577                 {
578                         if( CheckMem(Data, sizeof(int)) == 0 ) {
579                                 LEAVE('i', -1);
580                                 return -1;
581                         }
582                         Log_Log("VTerm", "VTerm %i mode set to %i", (int)Node->Inode, *iData);
583                         
584                         // Update mode if needed
585                         if( term->Mode != *iData
586                          || term->NewWidth
587                          || term->NewHeight)
588                         {
589                                 // Adjust for text mode
590                                 if( *iData == TERM_MODE_TEXT ) {
591                                         term->NewHeight *= giVT_CharHeight;
592                                         term->NewWidth *= giVT_CharWidth;
593                                 }
594                                 // Fill unchanged dimensions
595                                 if(term->NewHeight == 0)        term->NewHeight = term->Height;
596                                 if(term->NewWidth == 0) term->NewWidth = term->Width;
597                                 // Set new mode
598                                 VT_int_ChangeMode(term, *iData, term->NewWidth, term->NewHeight);
599                                 // Clear unapplied dimensions
600                                 term->NewWidth = 0;
601                                 term->NewHeight = 0;
602                         }
603                         
604                         // Update the screen dimensions
605                         if(Node->Inode == giVT_CurrentTerminal)
606                                 VT_SetTerminal( giVT_CurrentTerminal );
607                 }
608                 LEAVE('i', term->Mode);
609                 return term->Mode;
610         
611         // Get/set the terminal width
612         case TERM_IOCTL_WIDTH:
613                 if(Data != NULL) {
614                         if( CheckMem(Data, sizeof(int)) == 0 ) {
615                                 LEAVE('i', -1);
616                                 return -1;
617                         }
618                         term->NewWidth = *iData;
619                 }
620                 if( term->NewWidth )
621                         ret = term->NewWidth;
622                 else if( term->Mode == TERM_MODE_TEXT )
623                         ret = term->TextWidth;
624                 else
625                         ret = term->Width;
626                 LEAVE('i', ret);
627                 return ret;
628         
629         // Get/set the terminal height
630         case TERM_IOCTL_HEIGHT:
631                 if(Data != NULL) {
632                         if( CheckMem(Data, sizeof(int)) == 0 ) {
633                                 LEAVE('i', -1);
634                                 return -1;
635                         }
636                         term->NewHeight = *iData;
637                 }
638                 if( term->NewHeight )
639                         ret = term->NewHeight;
640                 else if( term->Mode == TERM_MODE_TEXT )
641                         ret = term->TextHeight;
642                 else
643                         ret = term->Height;
644                 LEAVE('i', ret);
645                 return ret;
646         
647         case TERM_IOCTL_FORCESHOW:
648                 Log_Log("VTerm", "Thread %i forced VTerm %i to be shown",
649                         Threads_GetTID(), (int)Node->Inode);
650                 VT_SetTerminal( Node->Inode );
651                 LEAVE('i', 1);
652                 return 1;
653         }
654         LEAVE('i', -1);
655         return -1;
656 }
657
658 /**
659  * \fn void VT_SetTerminal(int ID)
660  * \brief Set the current terminal
661  */
662 void VT_SetTerminal(int ID)
663 {       
664         // Update current terminal ID
665         Log_Log("VTerm", "Changed terminal from %i to %i", giVT_CurrentTerminal, ID);
666         giVT_CurrentTerminal = ID;
667         gpVT_CurTerm = &gVT_Terminals[ID];
668         
669         // Update cursor
670         if( gpVT_CurTerm->Mode == TERM_MODE_TEXT && !(gpVT_CurTerm->Flags & VT_FLAG_HIDECSR) )
671         {
672                 tVideo_IOCtl_Pos        pos;
673                 pos.x = (gpVT_CurTerm->WritePos - gpVT_CurTerm->ViewPos) % gpVT_CurTerm->TextWidth;
674                 pos.y = (gpVT_CurTerm->WritePos - gpVT_CurTerm->ViewPos) / gpVT_CurTerm->TextWidth;
675                 if( pos.x < gpVT_CurTerm->TextHeight )
676                         VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSOR, &pos);
677         }
678         
679         if( gpVT_CurTerm->Mode == TERM_MODE_TEXT )
680                 VT_SetMode( VIDEO_BUFFMT_TEXT );
681         else
682                 VT_SetMode( VIDEO_BUFFMT_FRAMEBUFFER );
683         
684         // Update the screen
685         VT_int_UpdateScreen( &gVT_Terminals[ ID ], 1 );
686 }
687
688 /**
689  * \fn void VT_KBCallBack(Uint32 Codepoint)
690  * \brief Called on keyboard interrupt
691  * \param Codepoint     Pseudo-UTF32 character
692  * 
693  * Handles a key press and sends the key code to the user's buffer.
694  * If the code creates a kernel-magic sequence, it is not passed to the
695  * user and is handled in-kernel.
696  */
697 void VT_KBCallBack(Uint32 Codepoint)
698 {
699         tVTerm  *term = gpVT_CurTerm;
700         
701         // How the hell did we get a codepoint of zero?
702         if(Codepoint == 0)      return;
703         
704         // Key Up
705         if( Codepoint & 0x80000000 )
706         {
707                 Codepoint &= 0x7FFFFFFF;
708                 switch(Codepoint)
709                 {
710                 #if !USE_CTRL_ALT
711                 case KEY_RSHIFT:        gbVT_CtrlDown = 0;      break;
712                 case KEY_LSHIFT:        gbVT_AltDown = 0;       break;
713                 #else
714                 case KEY_LALT:  gbVT_AltDown &= ~1;     break;
715                 case KEY_RALT:  gbVT_AltDown &= ~2;     break;
716                 case KEY_LCTRL: gbVT_CtrlDown &= ~1     break;
717                 case KEY_RCTRL: gbVT_CtrlDown &= ~2;    break;
718                 #endif
719                 }
720                 return;
721         }
722         
723         switch(Codepoint)
724         {
725         #if !USE_CTRL_ALT       // HACK: Use both shifts instead of Ctrl-Alt
726         case KEY_RSHIFT:        gbVT_CtrlDown = 1;      break;
727         case KEY_LSHIFT:        gbVT_AltDown = 1;       break;
728         #else
729         case KEY_LALT:  gbVT_AltDown |= 1;      break;
730         case KEY_RALT:  gbVT_AltDown |= 2;      break;
731         case KEY_LCTRL: gbVT_CtrlDown |= 1;     break;
732         case KEY_RCTRL: gbVT_CtrlDown |= 2;     break;
733         #endif
734         
735         default:
736                 if(!gbVT_AltDown || !gbVT_CtrlDown)
737                         break;
738                 switch(Codepoint)
739                 {
740                 case KEY_F1:    VT_SetTerminal(0);      return;
741                 case KEY_F2:    VT_SetTerminal(1);      return;
742                 case KEY_F3:    VT_SetTerminal(2);      return;
743                 case KEY_F4:    VT_SetTerminal(3);      return;
744                 case KEY_F5:    VT_SetTerminal(4);      return;
745                 case KEY_F6:    VT_SetTerminal(5);      return;
746                 case KEY_F7:    VT_SetTerminal(6);      return;
747                 case KEY_F8:    VT_SetTerminal(7);      return;
748                 case KEY_F9:    VT_SetTerminal(8);      return;
749                 case KEY_F10:   VT_SetTerminal(9);      return;
750                 case KEY_F11:   VT_SetTerminal(10);     return;
751                 case KEY_F12:   VT_SetTerminal(11);     return;
752                 // Scrolling
753                 case KEY_PGUP:
754                         if( gpVT_CurTerm->ViewPos > gpVT_CurTerm->Width )
755                                 gpVT_CurTerm->ViewPos -= gpVT_CurTerm->Width;
756                         else
757                                 gpVT_CurTerm->ViewPos = 0;
758                         return;
759                 case KEY_PGDOWN:
760                         if( gpVT_CurTerm->ViewPos < gpVT_CurTerm->Width*gpVT_CurTerm->Height*(giVT_Scrollback-1) )
761                                 gpVT_CurTerm->ViewPos += gpVT_CurTerm->Width;
762                         else
763                                 gpVT_CurTerm->ViewPos = gpVT_CurTerm->Width*gpVT_CurTerm->Height*(giVT_Scrollback-1);
764                         return;
765                 }
766         }
767         
768         // Encode key
769         if(term->Mode == TERM_MODE_TEXT)
770         {
771                 Uint8   buf[6] = {0};
772                  int    len = 0;
773                 
774                 // Ignore Modifer Keys
775                 if(Codepoint > KEY_MODIFIERS)   return;
776                 
777                 // Get UTF-8/ANSI Encoding
778                 switch(Codepoint)
779                 {
780                 case KEY_LEFT:
781                         buf[0] = '\x1B';        buf[1] = '[';   buf[2] = 'D';
782                         len = 3;
783                         break;
784                 case KEY_RIGHT:
785                         buf[0] = '\x1B';        buf[1] = '[';   buf[2] = 'C';
786                         len = 3;
787                         break;
788                 case KEY_UP:
789                         buf[0] = '\x1B';        buf[1] = '[';   buf[2] = 'A';
790                         len = 3;
791                         break;
792                 case KEY_DOWN:
793                         buf[0] = '\x1B';        buf[1] = '[';   buf[2] = 'B';
794                         len = 3;
795                         break;
796                 
797                 case KEY_PGUP:
798                         buf[0] = '\x1B';        buf[1] = '[';   buf[2] = '5';   // Some overline also
799                         //len = 4;      // Commented out until I'm sure
800                         break;
801                 case KEY_PGDOWN:
802                         len = 0;
803                         break;
804                 
805                 // Attempt to encode in UTF-8
806                 default:
807                         len = WriteUTF8( buf, Codepoint );
808                         if(len == 0) {
809                                 Warning("Codepoint (%x) is unrepresentable in UTF-8", Codepoint);
810                         }
811                         break;
812                 }
813                 
814                 if(len == 0) {
815                         // Unprintable / Don't Pass
816                         return;
817                 }
818                 
819                 // Write
820                 if( MAX_INPUT_CHARS8 - term->InputWrite >= len )
821                         memcpy( &term->InputBuffer[term->InputWrite], buf, len );
822                 else {
823                         memcpy( &term->InputBuffer[term->InputWrite], buf, MAX_INPUT_CHARS8 - term->InputWrite );
824                         memcpy( &term->InputBuffer[0], buf, len - (MAX_INPUT_CHARS8 - term->InputWrite) );
825                 }
826                 // Roll the buffer over
827                 term->InputWrite += len;
828                 term->InputWrite %= MAX_INPUT_CHARS8;
829                 if( (term->InputWrite - term->InputRead + MAX_INPUT_CHARS8)%MAX_INPUT_CHARS8 < len ) {
830                         term->InputRead = term->InputWrite + 1;
831                         term->InputRead %= MAX_INPUT_CHARS8;
832                 }
833                 
834         }
835         else
836         {
837                 // Encode the raw UTF-32 Key
838                 ((Uint32*)term->InputBuffer)[ term->InputWrite ] = Codepoint;
839                 term->InputWrite ++;
840                 term->InputWrite %= MAX_INPUT_CHARS32;
841                 if(term->InputRead == term->InputWrite) {
842                         term->InputRead ++;
843                         term->InputRead %= MAX_INPUT_CHARS32;
844                 }
845         }
846         
847         // Wake up the thread waiting on us
848         if( term->ReadingThread >= 0 ) {
849                 Threads_WakeTID(term->ReadingThread);
850         }
851 }
852
853 /**
854  * \fn void VT_int_ClearLine(tVTerm *Term, int Num)
855  * \brief Clears a line in a virtual terminal
856  */
857 void VT_int_ClearLine(tVTerm *Term, int Num)
858 {
859          int    i;
860         tVT_Char        *cell = &Term->Text[ Num*Term->TextWidth ];
861         if( Num < 0 || Num >= Term->TextHeight * (giVT_Scrollback + 1) )        return ;
862         //ENTER("pTerm iNum", Term, Num);
863         for( i = Term->TextWidth; i--; )
864         {
865                 cell[ i ].Ch = 0;
866                 cell[ i ].Colour = Term->CurColour;
867         }
868         //LEAVE('-');
869 }
870
871 /**
872  * \fn int VT_int_ParseEscape(tVTerm *Term, char *Buffer)
873  * \brief Parses a VT100 Escape code
874  */
875 int VT_int_ParseEscape(tVTerm *Term, char *Buffer)
876 {
877         char    c;
878          int    argc = 0, j = 1;
879          int    tmp;
880          int    args[6] = {0,0,0,0};
881         
882         switch(Buffer[0])
883         {
884         //Large Code
885         case '[':
886                 // Get Arguments
887                 c = Buffer[j++];
888                 if( '0' <= c && c <= '9' )
889                 {
890                         do {
891                                 if(c == ';')    c = Buffer[j++];
892                                 while('0' <= c && c <= '9') {
893                                         args[argc] *= 10;
894                                         args[argc] += c-'0';
895                                         c = Buffer[j++];
896                                 }
897                                 argc ++;
898                         } while(c == ';');
899                 }
900                 
901                 // Get Command
902                 if(     ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'))
903                 {
904                         switch(c)
905                         {
906                         // Left
907                         case 'D':
908                                 if(argc == 1)   tmp = args[0];
909                                 else    tmp = 1;
910                                 
911                                 if( Term->WritePos-(tmp-1) % Term->TextWidth == 0 )
912                                         Term->WritePos -= Term->WritePos % Term->TextWidth;
913                                 else
914                                         Term->WritePos -= tmp;
915                                 break;
916                         
917                         // Right
918                         case 'C':
919                                 if(argc == 1)   tmp = args[0];
920                                 else    tmp = 1;
921                                 if( (Term->WritePos + tmp) % Term->TextWidth == 0 ) {
922                                         Term->WritePos -= Term->WritePos % Term->TextWidth;
923                                         Term->WritePos += Term->TextWidth - 1;
924                                 } else
925                                         Term->WritePos += tmp;
926                                 break;
927                         
928                         // Clear By Line
929                         case 'J':
930                                 // Clear Screen
931                                 switch(args[0])
932                                 {
933                                 case 2:
934                                         {
935                                          int    i = Term->TextHeight * (giVT_Scrollback + 1);
936                                         while( i-- )    VT_int_ClearLine(Term, i);
937                                         Term->WritePos = 0;
938                                         Term->ViewPos = 0;
939                                         VT_int_UpdateScreen(Term, 1);
940                                         }
941                                         break;
942                                 }
943                                 break;
944                         // Set cursor position
945                         case 'h':
946                                 Term->WritePos = args[0] + args[1]*Term->TextWidth;
947                                 Log_Debug("VTerm", "args = {%i, %i}", args[0], args[1]);
948                                 break;
949                         // Set Font flags
950                         case 'm':
951                                 for( ; argc--; )
952                                 {
953                                         // Flags
954                                         if( 0 <= args[argc] && args[argc] <= 8)
955                                         {
956                                                 switch(args[argc])
957                                                 {
958                                                 case 0: Term->CurColour = DEFAULT_COLOUR;       break;  // Reset
959                                                 case 1: Term->CurColour |= 0x80000000;  break;  // Bright
960                                                 case 2: Term->CurColour &= ~0x80000000; break;  // Dim
961                                                 }
962                                         }
963                                         // Foreground Colour
964                                         else if(30 <= args[argc] && args[argc] <= 37) {
965                                                 Term->CurColour &= 0xF000FFFF;
966                                                 Term->CurColour |= (Uint32)caVT100Colours[ args[argc]-30+(Term->CurColour>>28) ] << 16;
967                                         }
968                                         // Background Colour
969                                         else if(40 <= args[argc] && args[argc] <= 47) {
970                                                 Term->CurColour &= 0xFFFF8000;
971                                                 Term->CurColour |= caVT100Colours[ args[argc]-40+((Term->CurColour>>12)&15) ];
972                                         }
973                                 }
974                                 break;
975                         default:
976                                 Log_Warning("VTerm", "Unknown control sequence");
977                                 break;
978                         }
979                 }
980                 break;
981                 
982         default:        break;
983         }
984         
985         //Log_Debug("VTerm", "j = %i, Buffer = '%s'", j, Buffer);
986         return j;
987 }
988
989 /**
990  * \fn void VT_int_PutString(tVTerm *Term, Uint8 *Buffer, Uint Count)
991  * \brief Print a string to the Virtual Terminal
992  */
993 void VT_int_PutString(tVTerm *Term, Uint8 *Buffer, Uint Count)
994 {
995         Uint32  val;
996          int    i;
997         
998         // Iterate
999         for( i = 0; i < Count; i++ )
1000         {
1001                 // Handle escape sequences
1002                 if( Buffer[i] == 0x1B )
1003                 {
1004                         i ++;
1005                         i += VT_int_ParseEscape(Term, (char*)&Buffer[i]) - 1;
1006                         continue;
1007                 }
1008                 
1009                 // Fast check for non UTF-8
1010                 if( Buffer[i] < 128 )   // Plain ASCII
1011                         VT_int_PutChar(Term, Buffer[i]);
1012                 else {  // UTF-8
1013                         i += ReadUTF8(&Buffer[i], &val) - 1;
1014                         VT_int_PutChar(Term, val);
1015                 }
1016         }
1017         // Update Screen
1018         VT_int_UpdateScreen( Term, 0 );
1019         
1020         // Update cursor
1021         if( Term == gpVT_CurTerm && !(Term->Flags & VT_FLAG_HIDECSR) )
1022         {
1023                 tVideo_IOCtl_Pos        pos;
1024                 pos.x = (Term->WritePos - Term->ViewPos) % Term->TextWidth;
1025                 pos.y = (Term->WritePos - Term->ViewPos) / Term->TextWidth;
1026                 VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSOR, &pos);
1027         }
1028 }
1029
1030 /**
1031  * \fn void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
1032  * \brief Write a single character to a VTerm
1033  */
1034 void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
1035 {
1036          int    i;
1037         
1038         switch(Ch)
1039         {
1040         case '\0':      return; // Ignore NULL byte
1041         case '\n':
1042                 VT_int_UpdateScreen( Term, 0 ); // Update the line before newlining
1043                 Term->WritePos += Term->TextWidth;
1044         case '\r':
1045                 Term->WritePos -= Term->WritePos % Term->TextWidth;
1046                 break;
1047         
1048         case '\t':
1049                 do {
1050                         Term->Text[ Term->WritePos ].Ch = '\0';
1051                         Term->Text[ Term->WritePos ].Colour = Term->CurColour;
1052                         Term->WritePos ++;
1053                 } while(Term->WritePos & 7);
1054                 break;
1055         
1056         case '\b':
1057                 // Backspace is invalid at Offset 0
1058                 if(Term->WritePos == 0) break;
1059                 
1060                 Term->WritePos --;
1061                 // Singe Character
1062                 if(Term->Text[ Term->WritePos ].Ch != '\0') {
1063                         Term->Text[ Term->WritePos ].Ch = 0;
1064                         Term->Text[ Term->WritePos ].Colour = Term->CurColour;
1065                         break;
1066                 }
1067                 // Tab
1068                 i = 7;  // Limit it to 8
1069                 do {
1070                         Term->Text[ Term->WritePos ].Ch = 0;
1071                         Term->Text[ Term->WritePos ].Colour = Term->CurColour;
1072                         Term->WritePos --;
1073                 } while(Term->WritePos && i-- && Term->Text[ Term->WritePos ].Ch == '\0');
1074                 if(Term->Text[ Term->WritePos ].Ch != '\0')
1075                         Term->WritePos ++;
1076                 break;
1077         
1078         default:
1079                 Term->Text[ Term->WritePos ].Ch = Ch;
1080                 Term->Text[ Term->WritePos ].Colour = Term->CurColour;
1081                 Term->WritePos ++;
1082                 break;
1083         }
1084         
1085         // Move Screen
1086         // - Check if we need to scroll the entire scrollback buffer
1087         if(Term->WritePos >= Term->TextWidth*Term->TextHeight*(giVT_Scrollback+1))
1088         {
1089                  int    base;
1090                 
1091                 // Move back by one
1092                 Term->WritePos -= Term->TextWidth;
1093                 // Update the scren
1094                 VT_int_UpdateScreen( Term, 0 );
1095                 
1096                 // Update view position
1097                 base = Term->TextWidth*Term->TextHeight*(giVT_Scrollback);
1098                 if(Term->ViewPos < base)
1099                         Term->ViewPos += Term->Width;
1100                 if(Term->ViewPos > base)
1101                         Term->ViewPos = base;
1102                 
1103                 // Scroll terminal cache
1104                 base = Term->TextWidth*(Term->TextHeight*(giVT_Scrollback+1)-1);
1105                 memcpy(
1106                         Term->Text,
1107                         &Term->Text[Term->TextWidth],
1108                         base*sizeof(tVT_Char)
1109                         );
1110                 
1111                 // Clear last row
1112                 for( i = 0; i < Term->TextWidth; i ++ )
1113                 {
1114                         Term->Text[ base + i ].Ch = 0;
1115                         Term->Text[ base + i ].Colour = Term->CurColour;
1116                 }
1117                 
1118                 VT_int_ScrollFramebuffer( Term );
1119                 VT_int_UpdateScreen( Term, 0 );
1120         }
1121         // Ok, so we only need to scroll the screen
1122         else if(Term->WritePos >= Term->ViewPos + Term->TextWidth*Term->TextHeight)
1123         {
1124                 //Debug("Term->WritePos (%i) >= %i",
1125                 //      Term->WritePos,
1126                 //      Term->ViewPos + Term->Width*Term->Height
1127                 //      );
1128                 //Debug("Scrolling screen only");
1129                 
1130                 // Update the last line
1131                 Term->WritePos -= Term->TextWidth;
1132                 VT_int_UpdateScreen( Term, 0 );
1133                 Term->WritePos += Term->TextWidth;
1134                 VT_int_ClearLine(Term, Term->WritePos / Term->TextWidth);
1135                 
1136                 // Scroll
1137                 Term->ViewPos += Term->TextWidth;
1138                 //Debug("Term->ViewPos = %i", Term->ViewPos);
1139                 VT_int_ScrollFramebuffer( Term );
1140                 VT_int_UpdateScreen( Term, 0 );
1141         }
1142         
1143         //LEAVE('-');
1144 }
1145
1146 /**
1147  * \fn void VT_int_ScrollFramebuffer( tVTerm *Term )
1148  * \note Scrolls the framebuffer by 1 text line
1149  */
1150 void VT_int_ScrollFramebuffer( tVTerm *Term )
1151 {
1152          int    tmp;
1153         struct {
1154                 Uint8   Op;
1155                 Uint16  DstX, DstY;
1156                 Uint16  SrcX, SrcY;
1157                 Uint16  W, H;
1158         } PACKED        buf;
1159         
1160         // Only update if this is the current terminal
1161         if( Term != gpVT_CurTerm )      return;
1162         
1163         // Switch to 2D Command Stream
1164         tmp = VIDEO_BUFFMT_2DSTREAM;
1165         VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &tmp);
1166         
1167         // BLIT from 0,0 to 0,giVT_CharHeight
1168         buf.Op = VIDEO_2DOP_BLIT;
1169         buf.DstX = 0;   buf.DstY = 0;
1170         buf.SrcX = 0;   buf.SrcY = giVT_CharHeight;
1171         buf.W = Term->TextWidth * giVT_CharWidth;
1172         buf.H = (Term->TextHeight-1) * giVT_CharHeight;
1173         VFS_WriteAt(giVT_OutputDevHandle, 0, sizeof(buf), &buf);
1174         
1175         // Restore old mode (this function is only called during text mode)
1176         tmp = VIDEO_BUFFMT_TEXT;
1177         VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &tmp);
1178 }
1179
1180 /**
1181  * \fn void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll )
1182  * \brief Updates the video framebuffer
1183  */
1184 void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll )
1185 {
1186         // Only update if this is the current terminal
1187         if( Term != gpVT_CurTerm )      return;
1188         
1189         switch( Term->Mode )
1190         {
1191         case TERM_MODE_TEXT:
1192                 // Re copy the entire screen?
1193                 if(UpdateAll) {
1194                         VFS_WriteAt(
1195                                 giVT_OutputDevHandle,
1196                                 0,
1197                                 Term->TextWidth*Term->TextHeight*sizeof(tVT_Char),
1198                                 &Term->Text[Term->ViewPos]
1199                                 );
1200                 }
1201                 // Only copy the current line
1202                 else {
1203                          int    pos = Term->WritePos - Term->WritePos % Term->TextWidth;
1204                         VFS_WriteAt(
1205                                 giVT_OutputDevHandle,
1206                                 (pos - Term->ViewPos)*sizeof(tVT_Char),
1207                                 Term->TextWidth*sizeof(tVT_Char),
1208                                 &Term->Text[pos]
1209                                 );
1210                 }
1211                 break;
1212         case TERM_MODE_FB:
1213                 VFS_WriteAt(
1214                         giVT_OutputDevHandle,
1215                         0,
1216                         Term->Width*Term->Height*sizeof(Uint32),
1217                         Term->Buffer
1218                         );
1219                 break;
1220         }
1221 }
1222
1223 /**
1224  * \brief Update the screen mode
1225  * \param Term  Terminal to update
1226  * \param NewMode       New mode to set
1227  * \param NewWidth      New framebuffer width
1228  * \param NewHeight     New framebuffer height
1229  */
1230 void VT_int_ChangeMode(tVTerm *Term, int NewMode, int NewWidth, int NewHeight)
1231 {
1232          int    oldW = Term->Width;
1233          int    oldTW = oldW / giVT_CharWidth;
1234          int    oldH = Term->Height;
1235          int    oldTH = oldH / giVT_CharWidth;
1236         tVT_Char        *oldTBuf = Term->Text;
1237         Uint32  *oldFB = Term->Buffer;
1238          int    w, h, i;
1239         
1240         // TODO: Increase RealWidth/RealHeight when this happens
1241         if(NewWidth > giVT_RealWidth)   NewWidth = giVT_RealWidth;
1242         if(NewHeight > giVT_RealHeight) NewHeight = giVT_RealHeight;
1243         
1244         // Calculate new dimensions
1245         Term->TextWidth = NewWidth / giVT_CharWidth;
1246         Term->TextHeight = NewHeight / giVT_CharHeight;
1247         Term->Width = NewWidth;
1248         Term->Height = NewHeight;
1249         Term->Mode = NewMode;
1250         
1251         // Allocate new buffers
1252         // - Text
1253         Term->Text = calloc(
1254                 Term->TextWidth * Term->TextHeight * (giVT_Scrollback+1),
1255                 sizeof(tVT_Char)
1256                 );
1257         if(oldTBuf) {
1258                 // Copy old buffer
1259                 w = oldTW;
1260                 if( w > Term->TextWidth )       w = Term->TextWidth;
1261                 h = oldTH;
1262                 if( h > Term->TextHeight )      h = Term->TextHeight;
1263                 h *= giVT_Scrollback + 1;
1264                 for( i = 0; i < h; i ++ )
1265                 {
1266                         memcpy(
1267                                 &Term->Text[i*Term->TextWidth],
1268                                 &oldTBuf[i*oldTW],
1269                                 w*sizeof(tVT_Char)
1270                                 );
1271                                 
1272                 }
1273         }
1274         
1275         // - Framebuffer
1276         Term->Buffer = calloc( Term->Width * Term->Height, sizeof(Uint32) );
1277         if(oldFB) {
1278                 // Copy old buffer
1279                 w = oldW;
1280                 if( w > Term->Width )   w = Term->Width;
1281                 h = oldH;
1282                 if( h > Term->Height )  h = Term->Height;
1283                 for( i = 0; i < h; i ++ )
1284                 {
1285                         memcpy(
1286                                 &Term->Buffer[i*Term->Width],
1287                                 &oldFB[i*oldW],
1288                                 w*sizeof(Uint32)
1289                                 );
1290                 }
1291         }
1292         
1293         
1294         // Debug
1295         switch(NewMode)
1296         {
1297         case TERM_MODE_TEXT:
1298                 Log_Log("VTerm", "Set VT %p to text mode (%ix%i)",
1299                         Term, Term->TextWidth, Term->TextHeight);
1300                 break;
1301         case TERM_MODE_FB:
1302                 Log_Log("VTerm", "Set VT %p to framebuffer mode (%ix%i)",
1303                         Term, Term->Width, Term->Height);
1304                 break;
1305         //case TERM_MODE_2DACCEL:
1306         //case TERM_MODE_3DACCEL:
1307         //      return;
1308         }
1309 }
1310
1311 // ---
1312 // Font Render
1313 // ---
1314 #define MONOSPACE_FONT  10816
1315
1316 #if MONOSPACE_FONT == 10808     // 8x8
1317 # include "vterm_font_8x8.h"
1318 #elif MONOSPACE_FONT == 10816   // 8x16
1319 # include "vterm_font_8x16.h"
1320 #endif
1321
1322 // === PROTOTYPES ===
1323 Uint8   *VT_Font_GetChar(Uint32 Codepoint);
1324
1325 // === GLOBALS ===
1326 int     giVT_CharWidth = FONT_WIDTH;
1327 int     giVT_CharHeight = FONT_HEIGHT;
1328
1329 // === CODE ===
1330 /**
1331  * \brief Render a font character
1332  */
1333 void VT_Font_Render(Uint32 Codepoint, void *Buffer, int Depth, int Pitch, Uint32 BGC, Uint32 FGC)
1334 {
1335         Uint8   *font;
1336          int    x, y;
1337         
1338         // 8-bpp and below
1339         if( Depth <= 8 )
1340         {
1341                 Uint8   *buf = Buffer;
1342                 
1343                 font = VT_Font_GetChar(Codepoint);
1344                 
1345                 for(y = 0; y < FONT_HEIGHT; y ++)
1346                 {
1347                         for(x = 0; x < FONT_WIDTH; x ++)
1348                         {
1349                                 if(*font & (1 << (FONT_WIDTH-x-1)))
1350                                         buf[x] = FGC;
1351                                 else
1352                                         buf[x] = BGC;
1353                         }
1354                         buf = (void*)( (tVAddr)buf + Pitch );
1355                         font ++;
1356                 }
1357         }
1358         // 16-bpp and below
1359         else if( Depth <= 16 )
1360         {
1361                 Uint16  *buf = Buffer;
1362                 
1363                 font = VT_Font_GetChar(Codepoint);
1364                 
1365                 for(y = 0; y < FONT_HEIGHT; y ++)
1366                 {
1367                         for(x = 0; x < FONT_WIDTH; x ++)
1368                         {
1369                                 if(*font & (1 << (FONT_WIDTH-x-1)))
1370                                         buf[x] = FGC;
1371                                 else
1372                                         buf[x] = BGC;
1373                         }
1374                         buf = (void*)( (tVAddr)buf + Pitch );
1375                         font ++;
1376                 }
1377         }
1378         // 24-bpp colour
1379         // - Special handling to not overwrite the next pixel
1380         //TODO: Endian issues here
1381         else if( Depth == 24 )
1382         {
1383                 Uint8   *buf = Buffer;
1384                 Uint8   bg_r = (BGC >> 16) & 0xFF;
1385                 Uint8   bg_g = (BGC >>  8) & 0xFF;
1386                 Uint8   bg_b = (BGC >>  0) & 0xFF;
1387                 Uint8   fg_r = (FGC >> 16) & 0xFF;
1388                 Uint8   fg_g = (FGC >>  8) & 0xFF;
1389                 Uint8   fg_b = (FGC >>  0) & 0xFF;
1390                 
1391                 font = VT_Font_GetChar(Codepoint);
1392                 
1393                 for(y = 0; y < FONT_HEIGHT; y ++)
1394                 {
1395                         for(x = 0; x < FONT_WIDTH; x ++)
1396                         {
1397                                 Uint8   r, g, b;
1398                                 
1399                                 if(*font & (1 << (FONT_WIDTH-x-1))) {
1400                                         r = fg_r;       g = fg_g;       b = fg_b;
1401                                 }
1402                                 else {
1403                                         r = bg_r;       g = bg_g;       b = bg_b;
1404                                 }
1405                                 buf[x*3+0] = b;
1406                                 buf[x*3+1] = g;
1407                                 buf[x*3+2] = r;
1408                         }
1409                         buf = (void*)( (tVAddr)buf + Pitch );
1410                         font ++;
1411                 }
1412         }
1413         // 32-bpp colour (nice and easy)
1414         else if( Depth == 32 )
1415         {
1416                 Uint32  *buf = Buffer;
1417                 
1418                 font = VT_Font_GetChar(Codepoint);
1419                 
1420                 for(y = 0; y < FONT_HEIGHT; y ++)
1421                 {
1422                         for(x = 0; x < FONT_WIDTH; x ++)
1423                         {
1424                                 if(*font & (1 << (FONT_WIDTH-x-1)))
1425                                         buf[x] = FGC;
1426                                 else
1427                                         buf[x] = BGC;
1428                         }
1429                         buf = (Uint32*)( (tVAddr)buf + Pitch );
1430                         font ++;
1431                 }
1432         }
1433 }
1434
1435 /**
1436  * \fn Uint32 VT_Colour12to24(Uint16 Col12)
1437  * \brief Converts a 12-bit colour into 24 bits
1438  */
1439 Uint32 VT_Colour12to24(Uint16 Col12)
1440 {
1441         Uint32  ret;
1442          int    tmp;
1443         tmp = Col12 & 0xF;
1444         ret  = (tmp << 0) | (tmp << 4);
1445         tmp = (Col12 & 0xF0) >> 4;
1446         ret |= (tmp << 8) | (tmp << 12);
1447         tmp = (Col12 & 0xF00) >> 8;
1448         ret |= (tmp << 16) | (tmp << 20);
1449         return ret;
1450 }
1451 /**
1452  * \brief Converts a 12-bit colour into 15 bits
1453  */
1454 Uint16 VT_Colour12to15(Uint16 Col12)
1455 {
1456         Uint32  ret;
1457          int    tmp;
1458         tmp = Col12 & 0xF;
1459         ret  = (tmp << 1) | (tmp & 1);
1460         tmp = (Col12 & 0xF0) >> 4;
1461         ret |= ( (tmp << 1) | (tmp & 1) ) << 5;
1462         tmp = (Col12 & 0xF00) >> 8;
1463         ret |= ( (tmp << 1) | (tmp & 1) ) << 10;
1464         return ret;
1465 }
1466
1467 /**
1468  * \brief Converts a 12-bit colour into any other depth
1469  * \param Col12 12-bit source colour
1470  * \param Depth Desired bit deptj
1471  * \note Green then blue get the extra avaliable bits (16:5-6-5, 14:4-5-5)
1472  */
1473 Uint32 VT_Colour12toN(Uint16 Col12, int Depth)
1474 {
1475         Uint32  ret;
1476         Uint32  r, g, b;
1477          int    rSize, gSize, bSize;
1478         
1479         // Fast returns
1480         if( Depth == 24 )       return VT_Colour12to24(Col12);
1481         if( Depth == 15 )       return VT_Colour12to15(Col12);
1482         
1483         // Bounds checks
1484         if( Depth < 8 ) return 0;
1485         if( Depth > 32 )        return 0;
1486         
1487         r = Col12 & 0xF;
1488         g = (Col12 & 0xF0) >> 4;
1489         b = (Col12 & 0xF00) >> 8;
1490         
1491         rSize = gSize = bSize = Depth / 3;
1492         if( rSize + gSize + bSize < Depth )     // Depth % 3 == 1
1493                 gSize ++;
1494         if( rSize + gSize + bSize < Depth )     // Depth % 3 == 2
1495                 bSize ++;
1496         
1497         // Expand
1498         r <<= rSize - 4;        g <<= gSize - 4;        b <<= bSize - 4;
1499         // Fill with the lowest bit
1500         if( Col12 & 0x001 )     r |= (1 << (rSize - 4)) - 1;
1501         if( Col12 & 0x010 )     r |= (1 << (gSize - 4)) - 1;
1502         if( Col12 & 0x100 )     r |= (1 << (bSize - 4)) - 1;
1503         
1504         // Create output
1505         ret  = r;
1506         ret |= g << rSize;
1507         ret |= b << (rSize + gSize);
1508         
1509         return ret;
1510 }
1511
1512 /**
1513  * \fn Uint8 *VT_Font_GetChar(Uint32 Codepoint)
1514  * \brief Gets an index into the font array given a Unicode Codepoint
1515  * \note See http://en.wikipedia.org/wiki/CP437
1516  */
1517 Uint8 *VT_Font_GetChar(Uint32 Codepoint)
1518 {
1519          int    index = 0;
1520         if(Codepoint < 128)
1521                 return &VTermFont[Codepoint*FONT_HEIGHT];
1522         switch(Codepoint)
1523         {
1524         case 0xC7:      index = 128;    break;  // Ç
1525         case 0xFC:      index = 129;    break;  // ü
1526         case 0xE9:      index = 130;    break;  // é
1527         case 0xE2:      index = 131;    break;  // â
1528         case 0xE4:      index = 132;    break;  // ä
1529         case 0xE0:      index = 133;    break;  // à
1530         case 0xE5:      index = 134;    break;  // å
1531         case 0xE7:      index = 135;    break;  // ç
1532         case 0xEA:      index = 136;    break;  // ê
1533         case 0xEB:      index = 137;    break;  // ë
1534         case 0xE8:      index = 138;    break;  // è
1535         case 0xEF:      index = 139;    break;  // ï
1536         case 0xEE:      index = 140;    break;  // î
1537         case 0xEC:      index = 141;    break;  // ì
1538         case 0xC4:      index = 142;    break;  // Ä
1539         case 0xC5:      index = 143;    break;  // Å
1540         }
1541         
1542         return &VTermFont[index*FONT_HEIGHT];
1543 }
1544
1545 EXPORTAS(&giVT_CharWidth, giVT_CharWidth);
1546 EXPORTAS(&giVT_CharHeight, giVT_CharHeight);
1547 EXPORT(VT_Font_Render);
1548 EXPORT(VT_Colour12to24);

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