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

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