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

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