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

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