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

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