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

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