71c4d4a8fc8e93d34cb622689317111adc3e22bd
[tpg/acess2.git] / KernelLand / Kernel / drv / vterm.c
1 /*
2  * Acess2 Virtual Terminal Driver
3  */
4 #define DEBUG   0
5 #include "vterm.h"
6 #include <fs_devfs.h>
7 #include <modules.h>
8 #include <api_drv_keyboard.h>
9 #include <api_drv_video.h>
10 #include <errno.h>
11 #include <semaphore.h>
12
13 // === CONSTANTS ===
14 #define VERSION ((0<<8)|(50))
15
16 #define NUM_VTS 8
17 //#define DEFAULT_OUTPUT        "BochsGA"
18 #define DEFAULT_OUTPUT  "Vesa"
19 #define FALLBACK_OUTPUT "x86_VGAText"
20 #define DEFAULT_INPUT   "PS2Keyboard"
21 #define DEFAULT_WIDTH   640
22 #define DEFAULT_HEIGHT  480
23 #define DEFAULT_SCROLLBACK      2       // 2 Screens of text + current screen
24 //#define DEFAULT_SCROLLBACK    0
25
26 // === TYPES ===
27
28 // === IMPORTS ===
29 extern void     Debug_SetKTerminal(const char *File);
30
31 // === PROTOTYPES ===
32  int    VT_Install(char **Arguments);
33 char    *VT_ReadDir(tVFS_Node *Node, int Pos);
34 tVFS_Node       *VT_FindDir(tVFS_Node *Node, const char *Name);
35  int    VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data);
36 Uint64  VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
37 Uint64  VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
38  int    VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data);
39
40 // === CONSTANTS ===
41
42 // === GLOBALS ===
43 MODULE_DEFINE(0, VERSION, VTerm, VT_Install, NULL, NULL);
44 tVFS_NodeType   gVT_RootNodeType = {
45         .TypeName = "VTerm Root",
46         .ReadDir = VT_ReadDir,
47         .FindDir = VT_FindDir,
48         .IOCtl = VT_Root_IOCtl
49         };
50 tVFS_NodeType   gVT_TermNodeType = {
51         .TypeName = "VTerm",
52         .Read = VT_Read,
53         .Write = VT_Write,
54         .IOCtl = VT_Terminal_IOCtl
55         };
56 tDevFS_Driver   gVT_DrvInfo = {
57         NULL, "VTerm",
58         {
59         .Flags = VFS_FFLAG_DIRECTORY,
60         .Size = NUM_VTS,
61         .Inode = -1,
62         .NumACLs = 0,
63         .Type = &gVT_RootNodeType
64         }
65 };
66 // --- Terminals ---
67 tVTerm  gVT_Terminals[NUM_VTS];
68  int    giVT_CurrentTerminal = 0;
69 tVTerm  *gpVT_CurTerm = &gVT_Terminals[0];
70 // --- Video State ---
71 short   giVT_RealWidth  = DEFAULT_WIDTH;        //!< Screen Width
72 short   giVT_RealHeight = DEFAULT_HEIGHT;       //!< Screen Height
73  int    giVT_Scrollback = DEFAULT_SCROLLBACK;
74 // --- Driver Handles ---
75 char    *gsVT_OutputDevice = NULL;
76 char    *gsVT_InputDevice = NULL;
77  int    giVT_OutputDevHandle = -2;
78  int    giVT_InputDevHandle = -2;
79
80 // === CODE ===
81 /**
82  * \fn int VT_Install(char **Arguments)
83  * \brief Installs the Virtual Terminal Driver
84  */
85 int VT_Install(char **Arguments)
86 {
87          int    i;
88         
89         // Scan Arguments
90         if(Arguments)
91         {
92                 char    **args;
93                 const char      *arg;
94                 for(args = Arguments; (arg = *args); args++ )
95                 {
96                         char    data[strlen(arg)+1];
97                         char    *opt = data;
98                         char    *val;
99                         
100                         val = strchr(arg, '=');
101                         strcpy(data, arg);
102                         if( val ) {
103                                 data[ val - arg ] = '\0';
104                                 val ++;
105                         }
106                         Log_Debug("VTerm", "Argument '%s'", arg);
107                         
108                         if( strcmp(opt, "Video") == 0 ) {
109                                 if( !gsVT_OutputDevice )
110                                         gsVT_OutputDevice = strdup(val);
111                         }
112                         else if( strcmp(opt, "Input") == 0 ) {
113                                 if( !gsVT_InputDevice )
114                                         gsVT_InputDevice = strdup(val);
115                         }
116                         else if( strcmp(opt, "Width") == 0 ) {
117                                 giVT_RealWidth = atoi( val );
118                         }
119                         else if( strcmp(opt, "Height") == 0 ) {
120                                 giVT_RealHeight = atoi( val );
121                         }
122                         else if( strcmp(opt, "Scrollback") == 0 ) {
123                                 giVT_Scrollback = atoi( val );
124                         }
125                 }
126         }
127         
128         // Apply Defaults
129         if(!gsVT_OutputDevice)  gsVT_OutputDevice = (char*)DEFAULT_OUTPUT;
130         else if( Module_EnsureLoaded( gsVT_OutputDevice ) )     gsVT_OutputDevice = (char*)DEFAULT_OUTPUT;
131         if( Module_EnsureLoaded( gsVT_OutputDevice ) )  gsVT_OutputDevice = (char*)FALLBACK_OUTPUT;
132         if( Module_EnsureLoaded( gsVT_OutputDevice ) ) {
133                 Log_Error("VTerm", "Fallback video '%s' is not avaliable, giving up", FALLBACK_OUTPUT);
134                 return MODULE_ERR_MISC;
135         }
136         
137         if(!gsVT_InputDevice)   gsVT_InputDevice = (char*)DEFAULT_INPUT;
138         else if( Module_EnsureLoaded( gsVT_InputDevice ) )      gsVT_InputDevice = (char*)DEFAULT_INPUT;
139         if( Module_EnsureLoaded( gsVT_OutputDevice ) ) {
140                 Log_Error("VTerm", "Fallback input '%s' is not avaliable, input will not be avaliable", DEFAULT_INPUT);
141         }
142         
143         // Create device paths
144         {
145                 char    *tmp;
146                 tmp = malloc( 9 + strlen(gsVT_OutputDevice) + 1 );
147                 strcpy(tmp, "/Devices/");
148                 strcpy(&tmp[9], gsVT_OutputDevice);
149                 gsVT_OutputDevice = tmp;
150
151                 tmp = malloc( 9 + strlen(gsVT_InputDevice) + 1 );
152                 strcpy(tmp, "/Devices/");
153                 strcpy(&tmp[9], gsVT_InputDevice);
154                 gsVT_InputDevice = tmp;
155         }
156         
157         Log_Log("VTerm", "Using '%s' as output", gsVT_OutputDevice);
158         Log_Log("VTerm", "Using '%s' as input", gsVT_InputDevice);
159         
160         VT_InitOutput();
161         VT_InitInput();
162         
163         // Create Nodes
164         for( i = 0; i < NUM_VTS; i++ )
165         {
166                 gVT_Terminals[i].Mode = TERM_MODE_TEXT;
167                 gVT_Terminals[i].Flags = 0;
168 //              gVT_Terminals[i].Flags = VT_FLAG_HIDECSR;       //HACK - Stop all those memcpy calls
169                 gVT_Terminals[i].CurColour = DEFAULT_COLOUR;
170                 gVT_Terminals[i].WritePos = 0;
171                 gVT_Terminals[i].AltWritePos = 0;
172                 gVT_Terminals[i].ViewPos = 0;
173                 gVT_Terminals[i].ReadingThread = -1;
174                 gVT_Terminals[i].ScrollHeight = 0;
175                 
176                 // Initialise
177                 VT_int_ChangeMode( &gVT_Terminals[i],
178                         TERM_MODE_TEXT, giVT_RealWidth, giVT_RealHeight );
179                 
180                 gVT_Terminals[i].Name[0] = '0'+i;
181                 gVT_Terminals[i].Name[1] = '\0';
182                 gVT_Terminals[i].Node.Inode = i;
183                 gVT_Terminals[i].Node.ImplPtr = &gVT_Terminals[i];
184                 gVT_Terminals[i].Node.NumACLs = 0;      // Only root can open virtual terminals
185         
186                 gVT_Terminals[i].Node.Type = &gVT_TermNodeType; 
187 //              Semaphore_Init(&gVT_Terminals[i].InputSemaphore, 0, MAX_INPUT_CHARS8, "VTerm", gVT_Terminals[i].Name);
188         }
189         
190         // Add to DevFS
191         DevFS_AddDevice( &gVT_DrvInfo );
192         
193         // Set kernel output to VT0
194         Debug_SetKTerminal("/Devices/VTerm/0");
195         
196         return MODULE_ERR_OK;
197 }
198
199 /**
200  * \brief Set the video resolution
201  * \param Width New screen width
202  * \param Height        New screen height
203  */
204 void VT_SetResolution(int Width, int Height)
205 {
206         tVideo_IOCtl_Mode       mode = {0};
207          int    tmp;
208          int    i;
209         
210         // Create the video mode
211         mode.width = Width;
212         mode.height = Height;
213         mode.bpp = 32;
214         mode.flags = 0;
215         
216         // Set video mode
217         VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_FINDMODE, &mode );
218         tmp = mode.id;
219         if( Width != mode.width || Height != mode.height )
220         {
221                 Log_Warning("VTerm",
222                         "Selected resolution (%ix%i is not supported) by the device, using (%ix%i)",
223                         giVT_RealWidth, giVT_RealHeight,
224                         mode.width, mode.height
225                         );
226                 giVT_RealWidth = mode.width;
227                 giVT_RealHeight = mode.height;
228         }
229         VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_GETSETMODE, &tmp );
230         
231         // Resize text terminals if needed
232         if( gVT_Terminals[0].Text && (giVT_RealWidth != mode.width || giVT_RealHeight != mode.height) )
233         {
234                  int    newBufSize = (giVT_RealWidth/giVT_CharWidth)
235                                         *(giVT_RealHeight/giVT_CharHeight)
236                                         *(giVT_Scrollback+1);
237                 //tVT_Char      *tmp;
238                 // Resize the text terminals
239                 Log_Debug("VTerm", "Resizing terminals to %ix%i",
240                         giVT_RealWidth/giVT_CharWidth, giVT_RealHeight/giVT_CharHeight);
241                 for( i = 0; i < NUM_VTS; i ++ )
242                 {
243                         if( gVT_Terminals[i].Mode != TERM_MODE_TEXT )   continue;
244                         
245                         gVT_Terminals[i].TextWidth = giVT_RealWidth/giVT_CharWidth;
246                         gVT_Terminals[i].TextHeight = giVT_RealHeight/giVT_CharHeight;
247                         gVT_Terminals[i].ScrollHeight = gVT_Terminals[i].TextHeight;
248                         
249                         gVT_Terminals[i].Text = realloc(
250                                 gVT_Terminals[i].Text,
251                                 newBufSize*sizeof(tVT_Char)
252                                 );
253                 }
254         }
255 }
256
257 /**
258  * \fn char *VT_ReadDir(tVFS_Node *Node, int Pos)
259  * \brief Read from the VTerm Directory
260  */
261 char *VT_ReadDir(tVFS_Node *Node, int Pos)
262 {
263         if(Pos < 0)     return NULL;
264         if(Pos >= NUM_VTS)      return NULL;
265         return strdup( gVT_Terminals[Pos].Name );
266 }
267
268 /**
269  * \fn tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name)
270  * \brief Find an item in the VTerm directory
271  * \param Node  Root node
272  * \param Name  Name (number) of the terminal
273  */
274 tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name)
275 {
276          int    num;
277         
278         ENTER("pNode sName", Node, Name);
279         
280         // Open the input and output files if needed
281         if(giVT_OutputDevHandle == -2)  VT_InitOutput();
282         if(giVT_InputDevHandle == -2)   VT_InitInput();
283         
284         // Sanity check name
285         if(Name[0] < '0' || Name[0] > '9' || Name[1] != '\0') {
286                 LEAVE('n');
287                 return NULL;
288         }
289         // Get index
290         num = Name[0] - '0';
291         if(num >= NUM_VTS) {
292                 LEAVE('n');
293                 return NULL;
294         }
295         // Return node
296         LEAVE('p', &gVT_Terminals[num].Node);
297         return &gVT_Terminals[num].Node;
298 }
299
300 /**
301  * \fn int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data)
302  * \brief Control the VTerm Driver
303  */
304 int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data)
305 {
306          int    len;
307         switch(Id)
308         {
309         case DRV_IOCTL_TYPE:    return DRV_TYPE_MISC;
310         case DRV_IOCTL_IDENT:   memcpy(Data, "VT\0\0", 4);      return 0;
311         case DRV_IOCTL_VERSION: return VERSION;
312         case DRV_IOCTL_LOOKUP:  return 0;
313         
314         case 4: // Get Video Driver
315                 if(Data)        strcpy(Data, gsVT_OutputDevice);
316                 return strlen(gsVT_OutputDevice);
317         
318         case 5: // Set Video Driver
319                 if(!Data)       return -EINVAL;
320                 if(Threads_GetUID() != 0)       return -EACCES;
321                 
322                 len = strlen(Data);
323                 
324                 // TODO: Check if the string used is a heap string
325                 
326                 free(gsVT_OutputDevice);
327                 
328                 gsVT_OutputDevice = malloc(len+1);
329                 strcpy(gsVT_OutputDevice, Data);
330                 
331                 VFS_Close(giVT_OutputDevHandle);
332                 giVT_OutputDevHandle = -1;
333                 
334                 VT_InitOutput();
335                 return 1;
336         }
337         return 0;
338 }
339
340 /**
341  * \brief Read from a virtual terminal
342  */
343 Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
344 {
345          int    pos = 0;
346          int    avail;
347         tVTerm  *term = &gVT_Terminals[ Node->Inode ];
348         Uint32  *codepoint_buf = Buffer;
349         Uint32  *codepoint_in;
350         
351         Mutex_Acquire( &term->ReadingLock );
352         
353         // Check current mode
354         switch(term->Mode)
355         {
356         // Text Mode (UTF-8)
357         case TERM_MODE_TEXT:
358                 VT_int_UpdateCursor(term, 1);
359         
360                 VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "VT_Read (UTF-8)");
361                 
362                 avail = term->InputWrite - term->InputRead;
363                 if(avail < 0)
364                         avail += MAX_INPUT_CHARS8;
365                 if(avail > Length - pos)
366                         avail = Length - pos;
367                 
368                 while( avail -- )
369                 {
370                         ((char*)Buffer)[pos] = term->InputBuffer[term->InputRead];
371                         pos ++;
372                         term->InputRead ++;
373                         while(term->InputRead >= MAX_INPUT_CHARS8)
374                                 term->InputRead -= MAX_INPUT_CHARS8;
375                 }
376                 break;
377         
378         //case TERM_MODE_FB:
379         // Other - UCS-4
380         default:
381                 VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "VT_Read (UCS-4)");
382                 
383                 avail = term->InputWrite - term->InputRead;
384                 if(avail < 0)
385                         avail += MAX_INPUT_CHARS32;
386                 Length /= 4;
387                 if(avail > Length - pos)
388                         avail = Length - pos;
389                 
390                 codepoint_in = (void*)term->InputBuffer;
391                 codepoint_buf = Buffer;
392                 
393                 while( avail -- )
394                 {
395                         codepoint_buf[pos] = codepoint_in[term->InputRead];
396                         pos ++;
397                         term->InputRead ++;
398                         while(term->InputRead >= MAX_INPUT_CHARS32)
399                                 term->InputRead -= MAX_INPUT_CHARS32;
400                 }
401                 pos *= 4;
402                 break;
403         }
404         
405         // Mark none avaliable if buffer empty
406         if( term->InputRead == term->InputWrite )
407                 VFS_MarkAvaliable(&term->Node, 0);
408         
409         term->ReadingThread = -1;
410
411 //      VT_int_UpdateCursor(term, term->Mode == TERM_MODE_TEXT);
412
413         Mutex_Release( &term->ReadingLock );
414         
415         return pos;
416 }
417
418 /**
419  * \fn Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
420  * \brief Write to a virtual terminal
421  */
422 Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
423 {
424         tVTerm  *term = &gVT_Terminals[ Node->Inode ];
425          int    size;
426         
427         // Write
428         switch( term->Mode )
429         {
430         // Print Text
431         case TERM_MODE_TEXT:
432                 VT_int_PutString(term, Buffer, Length);
433                 break;
434         
435         // Framebuffer :)
436         case TERM_MODE_FB:
437                 // - Sanity Checking
438                 size = term->Width*term->Height*4;
439                 if( Offset > size ) {
440                         Log_Notice("VTerm", "VT_Write: Offset (0x%llx) > FBSize (0x%x)",
441                                 Offset, size);
442                         return 0;
443                 }
444                 if( Offset + Length > size ) {
445                         Log_Notice("VTerm", "VT_Write: Offset+Length (0x%llx) > FBSize (0x%x)",
446                                 Offset+Length, size);
447                         Length = size - Offset;
448                 }
449                 
450                 // Update screen if needed
451                 if( Node->Inode == giVT_CurrentTerminal )
452                 {
453                         if( giVT_RealHeight > term->Height )
454                                 Offset += (giVT_RealHeight - term->Height) / 2 * term->Width * 4;
455                         // Handle undersized virtual terminals
456                         if( giVT_RealWidth > term->Width )
457                         {
458                                 // No? :( Well, just center it
459                                  int    x, y, w, h;
460                                 Uint    dst_ofs;
461                                 // TODO: Fix to handle the final line correctly?
462                                 x = Offset/4;   y = x / term->Width;    x %= term->Width;
463                                 w = Length/4+x; h = w / term->Width;    w %= term->Width;
464                                 
465                                 // Center
466                                 x += (giVT_RealWidth - term->Width) / 2;
467                                 dst_ofs = (x + y * giVT_RealWidth) * 4;
468                                 while(h--)
469                                 {
470                                         VFS_WriteAt( giVT_OutputDevHandle,
471                                                 dst_ofs,
472                                                 term->Width * 4,
473                                                 Buffer
474                                                 );
475                                         Buffer = (void*)( (Uint)Buffer + term->Width*4 );
476                                         dst_ofs += giVT_RealWidth * 4;
477                                 }
478                                 return 0;
479                         }
480                         else
481                         {
482                                 return VFS_WriteAt( giVT_OutputDevHandle, Offset, Length, Buffer );
483                         }
484                 }
485                 else
486                 {
487                         if( !term->Buffer )
488                                 term->Buffer = malloc( term->Width * term->Height * 4 );
489                         // Copy to the local cache
490                         memcpy( (char*)term->Buffer + (Uint)Offset, Buffer, Length );
491                 }
492                 break;
493         // Just pass on (for now)
494         // TODO: Handle locally too to ensure no information is lost on
495         //       VT Switch (and to isolate terminals from each other)
496         case TERM_MODE_2DACCEL:
497         //case TERM_MODE_3DACCEL:
498                 if( Node->Inode == giVT_CurrentTerminal )
499                 {
500                         VFS_Write( giVT_OutputDevHandle, Length, Buffer );
501                 }
502                 break;
503         }
504         
505         return 0;
506 }
507
508 /**
509  * \fn int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data)
510  * \brief Call an IO Control on a virtual terminal
511  */
512 int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data)
513 {
514          int    *iData = Data;
515          int    ret;
516         tVTerm  *term = Node->ImplPtr;
517         ENTER("pNode iId pData", Node, Id, Data);
518         
519         if(Id >= DRV_IOCTL_LOOKUP) {
520                 // Only root can fiddle with graphics modes
521                 // TODO: Remove this and replace with user ownership
522                 if( Threads_GetUID() != 0 )     return -1;
523         }
524         
525         switch(Id)
526         {
527         // --- Core Defined
528         case DRV_IOCTL_TYPE:
529                 LEAVE('i', DRV_TYPE_TERMINAL);
530                 return DRV_TYPE_TERMINAL;
531         case DRV_IOCTL_IDENT:
532                 memcpy(Data, "VT\0\0", 4);
533                 LEAVE('i', 0);
534                 return 0;
535         case DRV_IOCTL_VERSION:
536                 LEAVE('x', VERSION);
537                 return VERSION;
538         case DRV_IOCTL_LOOKUP:
539                 LEAVE('i', 0);
540                 return 0;
541         
542         // Get/Set the mode (and apply any changes)
543         case TERM_IOCTL_MODETYPE:
544                 if(Data != NULL)
545                 {
546                         if( CheckMem(Data, sizeof(int)) == 0 ) {
547                                 LEAVE('i', -1);
548                                 return -1;
549                         }
550                         Log_Log("VTerm", "VTerm %i mode set to %i", (int)Node->Inode, *iData);
551                         
552                         // Update mode if needed
553                         if( term->Mode != *iData || term->NewWidth || term->NewHeight)
554                         {
555                                 // Adjust for text mode
556                                 if( *iData == TERM_MODE_TEXT ) {
557                                         term->NewHeight *= giVT_CharHeight;
558                                         term->NewWidth *= giVT_CharWidth;
559                                 }
560                                 // Fill unchanged dimensions
561                                 if(term->NewHeight == 0)        term->NewHeight = term->Height;
562                                 if(term->NewWidth == 0) term->NewWidth = term->Width;
563                                 // Set new mode
564                                 VT_int_ChangeMode(term, *iData, term->NewWidth, term->NewHeight);
565                                 // Clear unapplied dimensions
566                                 term->NewWidth = 0;
567                                 term->NewHeight = 0;
568                         }
569                         
570                         // Update the screen dimensions
571                         if(Node->Inode == giVT_CurrentTerminal)
572                                 VT_SetTerminal( giVT_CurrentTerminal );
573                 }
574                 LEAVE('i', term->Mode);
575                 return term->Mode;
576         
577         // Get/set the terminal width
578         case TERM_IOCTL_WIDTH:
579                 if(Data != NULL) {
580                         if( CheckMem(Data, sizeof(int)) == 0 ) {
581                                 LEAVE('i', -1);
582                                 return -1;
583                         }
584                         term->NewWidth = *iData;
585                 }
586                 if( term->NewWidth )
587                         ret = term->NewWidth;
588                 else if( term->Mode == TERM_MODE_TEXT )
589                         ret = term->TextWidth;
590                 else
591                         ret = term->Width;
592                 LEAVE('i', ret);
593                 return ret;
594         
595         // Get/set the terminal height
596         case TERM_IOCTL_HEIGHT:
597                 if(Data != NULL) {
598                         if( CheckMem(Data, sizeof(int)) == 0 ) {
599                                 LEAVE('i', -1);
600                                 return -1;
601                         }
602                         term->NewHeight = *iData;
603                 }
604                 if( term->NewHeight )
605                         ret = term->NewHeight;
606                 else if( term->Mode == TERM_MODE_TEXT )
607                         ret = term->TextHeight;
608                 else
609                         ret = term->Height;
610                 LEAVE('i', ret);
611                 return ret;
612         
613         case TERM_IOCTL_FORCESHOW:
614                 Log_Log("VTerm", "Thread %i forced VTerm %i to be shown",
615                         Threads_GetTID(), (int)Node->Inode);
616                 VT_SetTerminal( Node->Inode );
617                 LEAVE('i', 1);
618                 return 1;
619         
620         case TERM_IOCTL_GETSETCURSOR:
621                 if(Data != NULL)
622                 {
623                         tVideo_IOCtl_Pos        *pos = Data;
624                         if( !CheckMem(Data, sizeof(*pos)) ) {
625                                 errno = -EINVAL;
626                                 LEAVE('i', -1);
627                                 return -1;
628                         }
629                 
630                         if( term->Mode == TERM_MODE_TEXT )
631                         {
632                                 if(term->Flags & VT_FLAG_ALTBUF)
633                                         term->AltWritePos = pos->x + pos->y * term->TextWidth;
634                                 else
635                                         term->WritePos = pos->x + pos->y * term->TextWidth + term->ViewPos;
636                                 VT_int_UpdateCursor(term, 0);
637                         }
638                         else
639                         {
640                                 term->VideoCursorX = pos->x;
641                                 term->VideoCursorY = pos->y;
642                                 VT_int_UpdateCursor(term, 1);
643                         }
644                 }
645                 ret = (term->Flags & VT_FLAG_ALTBUF) ? term->AltWritePos : term->WritePos-term->ViewPos;
646                 LEAVE('i', ret);
647                 return ret;
648
649         case TERM_IOCTL_SETCURSORBITMAP: {
650                 tVideo_IOCtl_Bitmap     *bmp = Data;
651                 if( Data == NULL )
652                 {
653                         free( term->VideoCursor );
654                         term->VideoCursor = NULL;
655                         LEAVE('i', 0);
656                         return 0;
657                 }
658
659                 // Sanity check bitmap
660                 if( !CheckMem(bmp, sizeof(tVideo_IOCtl_Bitmap)) ) {
661                         Log_Notice("VTerm", "%p in TERM_IOCTL_SETCURSORBITMAP invalid", bmp);
662                         errno = -EINVAL;
663                         LEAVE_RET('i', -1);
664                 }
665                 if( !CheckMem(bmp->Data, bmp->W*bmp->H*sizeof(Uint32)) ) {
666                         Log_Notice("VTerm", "%p in TERM_IOCTL_SETCURSORBITMAP invalid", bmp);
667                         errno = -EINVAL;
668                         LEAVE_RET('i', -1);
669                 }
670
671                 // Reallocate if needed
672                 if(term->VideoCursor)
673                 {
674                         if(bmp->W * bmp->H != term->VideoCursor->W * term->VideoCursor->H) {
675                                 free(term->VideoCursor);
676                                 term->VideoCursor = NULL;
677                         }
678                 }
679                 if(!term->VideoCursor) {
680                         term->VideoCursor = malloc(sizeof(tVideo_IOCtl_Pos) + bmp->W*bmp->H*sizeof(Uint32));
681                         if(!term->VideoCursor) {
682                                 Log_Error("VTerm", "Unable to allocate memory for cursor");
683                                 errno = -ENOMEM;
684                                 LEAVE_RET('i', -1);
685                         }
686                 }
687                 
688                 memcpy(term->VideoCursor, bmp, sizeof(tVideo_IOCtl_Pos) + bmp->W*bmp->H*sizeof(Uint32));
689         
690                 Log_Debug("VTerm", "Set VT%i's cursor to %p %ix%i",
691                         (int)term->Node.Inode, bmp, bmp->W, bmp->H);
692
693                 if(gpVT_CurTerm == term)
694                         VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSORBITMAP, term->VideoCursor);
695         
696                 LEAVE('i', 0);
697                 return 0; }
698         }
699         LEAVE('i', -1);
700         return -1;
701 }
702
703 /**
704  * \fn void VT_SetTerminal(int ID)
705  * \brief Set the current terminal
706  */
707 void VT_SetTerminal(int ID)
708 {
709         // Copy the screen state
710         if( ID != giVT_CurrentTerminal && gpVT_CurTerm->Mode != TERM_MODE_TEXT )
711         {
712                 if( !gpVT_CurTerm->Buffer )
713                         gpVT_CurTerm->Buffer = malloc( gpVT_CurTerm->Width*gpVT_CurTerm->Height*4 );
714                 if( gpVT_CurTerm->Width < giVT_RealWidth )
715                 {
716                          int    line;
717                         Uint    ofs = 0;
718                         Uint32  *dest = gpVT_CurTerm->Buffer;
719                         // Slower scanline copy
720                         for( line = 0; line < gpVT_CurTerm->Height; line ++ )
721                         {
722                                 VFS_ReadAt(giVT_OutputDevHandle, ofs, gpVT_CurTerm->Width*4, dest);
723                                 ofs += giVT_RealWidth * 4;
724                                 dest += gpVT_CurTerm->Width;
725                         }
726                 }
727                 else
728                 {
729                         VFS_ReadAt(giVT_OutputDevHandle,
730                                 0, gpVT_CurTerm->Height*giVT_RealWidth*4,
731                                 gpVT_CurTerm->Buffer
732                                 );
733                 }
734         }
735
736         // Update current terminal ID
737         Log_Log("VTerm", "Changed terminal from %i to %i", giVT_CurrentTerminal, ID);
738         giVT_CurrentTerminal = ID;
739         gpVT_CurTerm = &gVT_Terminals[ID];
740         
741         if( gpVT_CurTerm->Mode == TERM_MODE_TEXT )
742         {
743                 VT_SetMode( VIDEO_BUFFMT_TEXT );
744         }
745         else
746         {
747                 // Update the cursor image
748                 if(gpVT_CurTerm->VideoCursor)
749                         VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSORBITMAP, gpVT_CurTerm->VideoCursor);
750                 VT_SetMode( VIDEO_BUFFMT_FRAMEBUFFER );
751         }
752         
753         if(gpVT_CurTerm->Buffer)
754         {
755                 // TODO: Handle non equal sized
756                 VFS_WriteAt(
757                         giVT_OutputDevHandle,
758                         0,
759                         gpVT_CurTerm->Width*gpVT_CurTerm->Height*sizeof(Uint32),
760                         gpVT_CurTerm->Buffer
761                         );
762         }
763         
764         VT_int_UpdateCursor(gpVT_CurTerm, 1);
765         // Update the screen
766         VT_int_UpdateScreen(gpVT_CurTerm, 1);
767 }

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