X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=Kernel%2Fdrv%2Fvterm.c;h=2ae38ee894c602430190a667d7f050eaca3f6578;hb=7536e8afcc3018c4ca2a4aa8f8422cf86a6c188c;hp=5f013ac8b76b9d28f22e26a2573bd25fe8856668;hpb=6098f7e3a0a3247c9517f9b04be814d16a98a564;p=tpg%2Facess2.git diff --git a/Kernel/drv/vterm.c b/Kernel/drv/vterm.c index 5f013ac8..2ae38ee8 100644 --- a/Kernel/drv/vterm.c +++ b/Kernel/drv/vterm.c @@ -1,89 +1,81 @@ /* * Acess2 Virtual Terminal Driver */ -#include +#define DEBUG 0 +#include "vterm.h" #include #include -#include -#include +#include +#include +#include +#include // === CONSTANTS === -#define NUM_VTS 4 -#define MAX_INPUT_CHARS 64 -#define VT_SCROLLBACK 1 // 4 Screens of text -#define DEFAULT_OUTPUT "/Devices/VGA" -#define DEFAULT_INPUT "/Devices/PS2Keyboard" -#define DEFAULT_WIDTH 80 -#define DEFAULT_HEIGHT 25 -#define DEFAULT_COLOUR (VT_COL_BLACK|(VT_COL_WHITE<<16)) +#define VERSION ((0<<8)|(50)) -#define VT_FLAG_HIDECSR 0x01 - -enum eVT_Modes { - VT_MODE_TEXT8, // UTF-8 Text Mode (VT100 Emulation) - VT_MODE_TEXT32, // UTF-32 Text Mode (Acess Native) - VT_MODE_8BPP, // 256 Colour Mode - VT_MODE_16BPP, // 16 bit Colour Mode - VT_MODE_24BPP, // 24 bit Colour Mode - VT_MODE_32BPP, // 32 bit Colour Mode - NUM_VT_MODES -}; +#define NUM_VTS 8 +//#define DEFAULT_OUTPUT "BochsGA" +#define DEFAULT_OUTPUT "Vesa" +#define FALLBACK_OUTPUT "x86_VGAText" +#define DEFAULT_INPUT "PS2Keyboard" +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 +#define DEFAULT_SCROLLBACK 2 // 2 Screens of text + current screen +//#define DEFAULT_SCROLLBACK 0 // === TYPES === -typedef struct { - int Mode; - int Flags; - int Width, Height; - int ViewPos, WritePos; - Uint32 CurColour; - char Name[2]; - int InputRead; - int InputWrite; - Uint32 InputBuffer[MAX_INPUT_CHARS]; - union { - tVT_Char *Text; - Uint32 *Buffer; - }; - tVFS_Node Node; -} tVTerm; + +// === IMPORTS === +extern void Debug_SetKTerminal(const char *File); // === PROTOTYPES === int VT_Install(char **Arguments); char *VT_ReadDir(tVFS_Node *Node, int Pos); -tVFS_Node *VT_FindDir(tVFS_Node *Node, char *Name); +tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name); + int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data); Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); -Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); - int VT_IOCtl(tVFS_Node *Node, int Id, void *Data); -void VT_KBCallBack(Uint32 Codepoint); -void VT_int_PutString(tVTerm *Term, Uint8 *Buffer, Uint Count); - int VT_int_ParseEscape(tVTerm *Term, char *Buffer); -void VT_int_PutChar(tVTerm *Term, Uint32 Ch); -void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ); +Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); + int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data); // === CONSTANTS === -const Uint16 caVT100Colours[] = { - VT_COL_BLACK, 0x700, 0x070, 0x770, 0x007, 0x707, 0x077, 0x777, - VT_COL_GREY, 0xF00, 0x0F0, 0xFF0, 0x00F, 0xF0F, 0x0FF, VT_COL_WHITE - }; // === GLOBALS === -MODULE_DEFINE(0, 0x0032, VTerm, VT_Install, NULL, NULL); +MODULE_DEFINE(0, VERSION, VTerm, VT_Install, NULL, DEFAULT_INPUT, NULL); +tVFS_NodeType gVT_RootNodeType = { + .TypeName = "VTerm Root", + .ReadDir = VT_ReadDir, + .FindDir = VT_FindDir, + .IOCtl = VT_Root_IOCtl + }; +tVFS_NodeType gVT_TermNodeType = { + .TypeName = "VTerm", + .Read = VT_Read, + .Write = VT_Write, + .IOCtl = VT_Terminal_IOCtl + }; tDevFS_Driver gVT_DrvInfo = { NULL, "VTerm", { .Flags = VFS_FFLAG_DIRECTORY, + .Size = NUM_VTS, .Inode = -1, .NumACLs = 0, - .ReadDir = VT_ReadDir, - .FindDir = VT_FindDir + .Type = &gVT_RootNodeType } }; +// --- Terminals --- tVTerm gVT_Terminals[NUM_VTS]; + int giVT_CurrentTerminal = 0; +tVTerm *gpVT_CurTerm = &gVT_Terminals[0]; +// --- Video State --- +short giVT_RealWidth = DEFAULT_WIDTH; //!< Screen Width +short giVT_RealHeight = DEFAULT_HEIGHT; //!< Screen Height + int giVT_Scrollback = DEFAULT_SCROLLBACK; +// --- Driver Handles --- char *gsVT_OutputDevice = NULL; char *gsVT_InputDevice = NULL; int giVT_OutputDevHandle = -2; int giVT_InputDevHandle = -2; - int giVT_CurrentTerminal = 0; // === CODE === /** @@ -92,98 +84,171 @@ char *gsVT_InputDevice = NULL; */ int VT_Install(char **Arguments) { - char **args = Arguments; - char *arg; int i; // Scan Arguments if(Arguments) { - for( ; (arg = *args); args++ ) + char **args; + const char *arg; + for(args = Arguments; (arg = *args); args++ ) { - if(arg[0] != '-') continue; + char data[strlen(arg)+1]; + char *opt = data; + char *val; - switch(arg[1]) - { - // Set output device - case 'o': - if(args[1] == NULL) break; - if(gsVT_OutputDevice) free(gsVT_OutputDevice); - gsVT_OutputDevice = malloc(strlen(args[1])+1); - strcpy(gsVT_OutputDevice, args[1]); - args ++; - break; - - // Set input device - case 'i': - if(args[1] == NULL) break; - if(gsVT_InputDevice) free(gsVT_InputDevice); - gsVT_InputDevice = malloc(strlen(args[1])+1); - strcpy(gsVT_InputDevice, args[1]); - args ++; - break; + val = strchr(arg, '='); + strcpy(data, arg); + if( val ) { + data[ val - arg ] = '\0'; + val ++; + } + Log_Debug("VTerm", "Argument '%s'", arg); + if( strcmp(opt, "Video") == 0 ) { + if( !gsVT_OutputDevice ) + gsVT_OutputDevice = strdup(val); + } + else if( strcmp(opt, "Input") == 0 ) { + if( !gsVT_InputDevice ) + gsVT_InputDevice = strdup(val); + } + else if( strcmp(opt, "Width") == 0 ) { + giVT_RealWidth = atoi( val ); + } + else if( strcmp(opt, "Height") == 0 ) { + giVT_RealHeight = atoi( val ); + } + else if( strcmp(opt, "Scrollback") == 0 ) { + giVT_Scrollback = atoi( val ); } } } // Apply Defaults - if(!gsVT_OutputDevice) gsVT_OutputDevice = DEFAULT_OUTPUT; - if(!gsVT_InputDevice) gsVT_InputDevice = DEFAULT_INPUT; + if(!gsVT_OutputDevice) gsVT_OutputDevice = (char*)DEFAULT_OUTPUT; + else if( Module_EnsureLoaded( gsVT_OutputDevice ) ) gsVT_OutputDevice = (char*)DEFAULT_OUTPUT; + if( Module_EnsureLoaded( gsVT_OutputDevice ) ) gsVT_OutputDevice = (char*)FALLBACK_OUTPUT; + if( Module_EnsureLoaded( gsVT_OutputDevice ) ) { + Log_Error("VTerm", "Fallback video '%s' is not avaliable, giving up", FALLBACK_OUTPUT); + return MODULE_ERR_MISC; + } + + if(!gsVT_InputDevice) gsVT_InputDevice = (char*)DEFAULT_INPUT; + else if( Module_EnsureLoaded( gsVT_InputDevice ) ) gsVT_InputDevice = (char*)DEFAULT_INPUT; + + // Create device paths + { + char *tmp; + tmp = malloc( 9 + strlen(gsVT_OutputDevice) + 1 ); + strcpy(tmp, "/Devices/"); + strcpy(&tmp[9], gsVT_OutputDevice); + gsVT_OutputDevice = tmp; + + tmp = malloc( 9 + strlen(gsVT_InputDevice) + 1 ); + strcpy(tmp, "/Devices/"); + strcpy(&tmp[9], gsVT_InputDevice); + gsVT_InputDevice = tmp; + } - LOG("Using '%s' as output", gsVT_OutputDevice); - LOG("Using '%s' as input", gsVT_InputDevice); + Log_Log("VTerm", "Using '%s' as output", gsVT_OutputDevice); + Log_Log("VTerm", "Using '%s' as input", gsVT_InputDevice); + + VT_InitOutput(); + VT_InitInput(); // Create Nodes for( i = 0; i < NUM_VTS; i++ ) { - gVT_Terminals[i].Mode = VT_MODE_TEXT8; + gVT_Terminals[i].Mode = TERM_MODE_TEXT; gVT_Terminals[i].Flags = 0; - gVT_Terminals[i].Width = DEFAULT_WIDTH; - gVT_Terminals[i].Height = DEFAULT_HEIGHT; +// gVT_Terminals[i].Flags = VT_FLAG_HIDECSR; //HACK - Stop all those memcpy calls gVT_Terminals[i].CurColour = DEFAULT_COLOUR; gVT_Terminals[i].WritePos = 0; + gVT_Terminals[i].AltWritePos = 0; gVT_Terminals[i].ViewPos = 0; + gVT_Terminals[i].ReadingThread = -1; + gVT_Terminals[i].ScrollHeight = 0; - gVT_Terminals[i].Buffer = malloc( DEFAULT_WIDTH*DEFAULT_HEIGHT*VT_SCROLLBACK*sizeof(tVT_Char) ); - memset( gVT_Terminals[i].Buffer, 0, DEFAULT_WIDTH*DEFAULT_HEIGHT*VT_SCROLLBACK*sizeof(tVT_Char) ); + // Initialise + VT_int_ChangeMode( &gVT_Terminals[i], + TERM_MODE_TEXT, giVT_RealWidth, giVT_RealHeight ); gVT_Terminals[i].Name[0] = '0'+i; gVT_Terminals[i].Name[1] = '\0'; gVT_Terminals[i].Node.Inode = i; + gVT_Terminals[i].Node.ImplPtr = &gVT_Terminals[i]; gVT_Terminals[i].Node.NumACLs = 0; // Only root can open virtual terminals - - gVT_Terminals[i].Node.Read = VT_Read; - gVT_Terminals[i].Node.Write = VT_Write; - gVT_Terminals[i].Node.IOCtl = VT_IOCtl; + + gVT_Terminals[i].Node.Type = &gVT_TermNodeType; +// Semaphore_Init(&gVT_Terminals[i].InputSemaphore, 0, MAX_INPUT_CHARS8, "VTerm", gVT_Terminals[i].Name); } // Add to DevFS DevFS_AddDevice( &gVT_DrvInfo ); - return 0; -} - -/** - * \fn void VT_InitOutput() - * \brief Initialise Video Output - */ -void VT_InitOutput() -{ - giVT_OutputDevHandle = VFS_Open(gsVT_OutputDevice, VFS_OPENFLAG_WRITE); - LOG("giVT_OutputDevHandle = %x\n", giVT_OutputDevHandle); + // Set kernel output to VT0 + Debug_SetKTerminal("/Devices/VTerm/0"); + + return MODULE_ERR_OK; } /** - * \fn void VT_InitInput() - * \brief Initialises the input + * \brief Set the video resolution + * \param Width New screen width + * \param Height New screen height */ -void VT_InitInput() +void VT_SetResolution(int Width, int Height) { - giVT_InputDevHandle = VFS_Open(gsVT_InputDevice, VFS_OPENFLAG_READ); - LOG("giVT_InputDevHandle = %x\n", giVT_InputDevHandle); - if(giVT_InputDevHandle == -1) return ; - VFS_IOCtl(giVT_InputDevHandle, KB_IOCTL_SETCALLBACK, VT_KBCallBack); + tVideo_IOCtl_Mode mode = {0}; + int tmp; + int i; + + // Create the video mode + mode.width = Width; + mode.height = Height; + mode.bpp = 32; + mode.flags = 0; + + // Set video mode + VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_FINDMODE, &mode ); + tmp = mode.id; + if( Width != mode.width || Height != mode.height ) + { + Log_Warning("VTerm", + "Selected resolution (%ix%i is not supported) by the device, using (%ix%i)", + giVT_RealWidth, giVT_RealHeight, + mode.width, mode.height + ); + giVT_RealWidth = mode.width; + giVT_RealHeight = mode.height; + } + VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_GETSETMODE, &tmp ); + + // Resize text terminals if needed + if( gVT_Terminals[0].Text && (giVT_RealWidth != mode.width || giVT_RealHeight != mode.height) ) + { + int newBufSize = (giVT_RealWidth/giVT_CharWidth) + *(giVT_RealHeight/giVT_CharHeight) + *(giVT_Scrollback+1); + //tVT_Char *tmp; + // Resize the text terminals + Log_Debug("VTerm", "Resizing terminals to %ix%i", + giVT_RealWidth/giVT_CharWidth, giVT_RealHeight/giVT_CharHeight); + for( i = 0; i < NUM_VTS; i ++ ) + { + if( gVT_Terminals[i].Mode != TERM_MODE_TEXT ) continue; + + gVT_Terminals[i].TextWidth = giVT_RealWidth/giVT_CharWidth; + gVT_Terminals[i].TextHeight = giVT_RealHeight/giVT_CharHeight; + gVT_Terminals[i].ScrollHeight = gVT_Terminals[i].TextHeight; + + gVT_Terminals[i].Text = realloc( + gVT_Terminals[i].Text, + newBufSize*sizeof(tVT_Char) + ); + } + } } /** @@ -194,18 +259,20 @@ char *VT_ReadDir(tVFS_Node *Node, int Pos) { if(Pos < 0) return NULL; if(Pos >= NUM_VTS) return NULL; - return gVT_Terminals[Pos].Name; + return strdup( gVT_Terminals[Pos].Name ); } /** - * \fn tVFS_Node *VT_FindDir(tVFS_Node *Node, char *Name) + * \fn tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name) * \brief Find an item in the VTerm directory + * \param Node Root node + * \param Name Name (number) of the terminal */ -tVFS_Node *VT_FindDir(tVFS_Node *Node, char *Name) +tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name) { int num; - //ENTER("pNode sName", Node, Name); + ENTER("pNode sName", Node, Name); // Open the input and output files if needed if(giVT_OutputDevHandle == -2) VT_InitOutput(); @@ -223,418 +290,475 @@ tVFS_Node *VT_FindDir(tVFS_Node *Node, char *Name) return NULL; } // Return node - //LEAVE('p', &gVT_Terminals[num].Node); + LEAVE('p', &gVT_Terminals[num].Node); return &gVT_Terminals[num].Node; } /** - * \fn Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \fn int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data) + * \brief Control the VTerm Driver + */ +int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data) +{ + int len; + switch(Id) + { + case DRV_IOCTL_TYPE: return DRV_TYPE_MISC; + case DRV_IOCTL_IDENT: memcpy(Data, "VT\0\0", 4); return 0; + case DRV_IOCTL_VERSION: return VERSION; + case DRV_IOCTL_LOOKUP: return 0; + + case 4: // Get Video Driver + if(Data) strcpy(Data, gsVT_OutputDevice); + return strlen(gsVT_OutputDevice); + + case 5: // Set Video Driver + if(!Data) return -EINVAL; + if(Threads_GetUID() != 0) return -EACCES; + + len = strlen(Data); + + // TODO: Check if the string used is a heap string + + free(gsVT_OutputDevice); + + gsVT_OutputDevice = malloc(len+1); + strcpy(gsVT_OutputDevice, Data); + + VFS_Close(giVT_OutputDevHandle); + giVT_OutputDevHandle = -1; + + VT_InitOutput(); + return 1; + } + return 0; +} + +/** * \brief Read from a virtual terminal */ Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) { int pos = 0; + int avail; tVTerm *term = &gVT_Terminals[ Node->Inode ]; + Uint32 *codepoint_buf = Buffer; + Uint32 *codepoint_in; + + Mutex_Acquire( &term->ReadingLock ); // Check current mode switch(term->Mode) { - case VT_MODE_TEXT8: - while(pos < Length) + // Text Mode (UTF-8) + case TERM_MODE_TEXT: + VT_int_UpdateCursor(term, 1); + + VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "VT_Read (UTF-8)"); + + avail = term->InputWrite - term->InputRead; + if(avail < 0) + avail += MAX_INPUT_CHARS8; + if(avail > Length - pos) + avail = Length - pos; + + while( avail -- ) { - while(term->InputRead == term->InputWrite) Threads_Yield(); - while(term->InputRead != term->InputWrite) - { - pos += WriteUTF8(Buffer+pos, term->InputBuffer[term->InputRead]); - term->InputRead ++; - term->InputRead %= MAX_INPUT_CHARS; - } + ((char*)Buffer)[pos] = term->InputBuffer[term->InputRead]; + pos ++; + term->InputRead ++; + while(term->InputRead >= MAX_INPUT_CHARS8) + term->InputRead -= MAX_INPUT_CHARS8; } break; - case VT_MODE_TEXT32: - while(pos < Length) + //case TERM_MODE_FB: + // Other - UCS-4 + default: + VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "VT_Read (UCS-4)"); + + avail = term->InputWrite - term->InputRead; + if(avail < 0) + avail += MAX_INPUT_CHARS32; + Length /= 4; + if(avail > Length - pos) + avail = Length - pos; + + codepoint_in = (void*)term->InputBuffer; + codepoint_buf = Buffer; + + while( avail -- ) { - while(term->InputRead == term->InputWrite) Threads_Yield(); - while(term->InputRead != term->InputWrite) - { - ((Uint32*)Buffer)[pos] = term->InputBuffer[term->InputRead]; - pos ++; - term->InputRead ++; - term->InputRead %= MAX_INPUT_CHARS; - } + codepoint_buf[pos] = codepoint_in[term->InputRead]; + pos ++; + term->InputRead ++; + while(term->InputRead >= MAX_INPUT_CHARS32) + term->InputRead -= MAX_INPUT_CHARS32; } + pos *= 4; break; } - return 0; + + // Mark none avaliable if buffer empty + if( term->InputRead == term->InputWrite ) + VFS_MarkAvaliable(&term->Node, 0); + + term->ReadingThread = -1; + +// VT_int_UpdateCursor(term, term->Mode == TERM_MODE_TEXT); + + Mutex_Release( &term->ReadingLock ); + + return pos; } /** - * \fn Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \fn Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) * \brief Write to a virtual terminal */ -Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) { tVTerm *term = &gVT_Terminals[ Node->Inode ]; - - //ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); + int size; // Write switch( term->Mode ) { - case VT_MODE_TEXT8: + // Print Text + case TERM_MODE_TEXT: VT_int_PutString(term, Buffer, Length); break; - case VT_MODE_TEXT32: - //VT_int_PutString32(term, Buffer, Length); + + // Framebuffer :) + case TERM_MODE_FB: + // - Sanity Checking + size = term->Width*term->Height*4; + if( Offset > size ) { + Log_Notice("VTerm", "VT_Write: Offset (0x%llx) > FBSize (0x%x)", + Offset, size); + return 0; + } + if( Offset + Length > size ) { + Log_Notice("VTerm", "VT_Write: Offset+Length (0x%llx) > FBSize (0x%x)", + Offset+Length, size); + Length = size - Offset; + } + + // Update screen if needed + if( Node->Inode == giVT_CurrentTerminal ) + { + if( giVT_RealHeight > term->Height ) + Offset += (giVT_RealHeight - term->Height) / 2 * term->Width * 4; + // Handle undersized virtual terminals + if( giVT_RealWidth > term->Width ) + { + // No? :( Well, just center it + int x, y, w, h; + Uint dst_ofs; + // TODO: Fix to handle the final line correctly? + x = Offset/4; y = x / term->Width; x %= term->Width; + w = Length/4+x; h = w / term->Width; w %= term->Width; + + // Center + x += (giVT_RealWidth - term->Width) / 2; + dst_ofs = (x + y * giVT_RealWidth) * 4; + while(h--) + { + VFS_WriteAt( giVT_OutputDevHandle, + dst_ofs, + term->Width * 4, + Buffer + ); + Buffer = (void*)( (Uint)Buffer + term->Width*4 ); + dst_ofs += giVT_RealWidth * 4; + } + return 0; + } + else + { + return VFS_WriteAt( giVT_OutputDevHandle, Offset, Length, Buffer ); + } + } + else + { + if( !term->Buffer ) + term->Buffer = malloc( term->Width * term->Height * 4 ); + // Copy to the local cache + memcpy( (char*)term->Buffer + (Uint)Offset, Buffer, Length ); + } + break; + // Just pass on (for now) + // TODO: Handle locally too to ensure no information is lost on + // VT Switch (and to isolate terminals from each other) + case TERM_MODE_2DACCEL: + //case TERM_MODE_3DACCEL: + if( Node->Inode == giVT_CurrentTerminal ) + { + VFS_Write( giVT_OutputDevHandle, Length, Buffer ); + } break; } - //LEAVE('i', 0); return 0; } /** - * \fn int VT_IOCtl(tVFS_Node *Node, int Id, void *Data) + * \fn int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data) * \brief Call an IO Control on a virtual terminal */ -int VT_IOCtl(tVFS_Node *Node, int Id, void *Data) +int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data) { - return 0; -} - -/** - * \fn void VT_KBCallBack(Uint32 Codepoint) - * \brief Called on keyboard interrupt - */ -void VT_KBCallBack(Uint32 Codepoint) -{ - tVTerm *term = &gVT_Terminals[giVT_CurrentTerminal]; - - term->InputBuffer[ term->InputWrite ] = Codepoint; - term->InputWrite ++; - term->InputWrite %= MAX_INPUT_CHARS; - if(term->InputRead == term->InputWrite) { - term->InputRead ++; - term->InputRead %= MAX_INPUT_CHARS; + int *iData = Data; + int ret; + tVTerm *term = Node->ImplPtr; + ENTER("pNode iId pData", Node, Id, Data); + + if(Id >= DRV_IOCTL_LOOKUP) { + // Only root can fiddle with graphics modes + // TODO: Remove this and replace with user ownership + if( Threads_GetUID() != 0 ) return -1; } -} - -/** - * \fn void VT_int_PutString(tVTerm *Term, Uint8 *Buffer, Uint Count) - * \brief Print a string to the Virtual Terminal - */ -void VT_int_PutString(tVTerm *Term, Uint8 *Buffer, Uint Count) -{ - Uint32 val; - int i; - for( i = 0; i < Count; i++ ) + + switch(Id) { - if( Buffer[i] == 0x1B ) // Escape Sequence + // --- Core Defined + case DRV_IOCTL_TYPE: + LEAVE('i', DRV_TYPE_TERMINAL); + return DRV_TYPE_TERMINAL; + case DRV_IOCTL_IDENT: + memcpy(Data, "VT\0\0", 4); + LEAVE('i', 0); + return 0; + case DRV_IOCTL_VERSION: + LEAVE('x', VERSION); + return VERSION; + case DRV_IOCTL_LOOKUP: + LEAVE('i', 0); + return 0; + + // Get/Set the mode (and apply any changes) + case TERM_IOCTL_MODETYPE: + if(Data != NULL) { - i ++; - i += VT_int_ParseEscape(Term, (char*)&Buffer[i]); - continue; + if( CheckMem(Data, sizeof(int)) == 0 ) { + LEAVE('i', -1); + return -1; + } + Log_Log("VTerm", "VTerm %i mode set to %i", (int)Node->Inode, *iData); + + // Update mode if needed + if( term->Mode != *iData || term->NewWidth || term->NewHeight) + { + // Adjust for text mode + if( *iData == TERM_MODE_TEXT ) { + term->NewHeight *= giVT_CharHeight; + term->NewWidth *= giVT_CharWidth; + } + // Fill unchanged dimensions + if(term->NewHeight == 0) term->NewHeight = term->Height; + if(term->NewWidth == 0) term->NewWidth = term->Width; + // Set new mode + VT_int_ChangeMode(term, *iData, term->NewWidth, term->NewHeight); + // Clear unapplied dimensions + term->NewWidth = 0; + term->NewHeight = 0; + } + + // Update the screen dimensions + if(Node->Inode == giVT_CurrentTerminal) + VT_SetTerminal( giVT_CurrentTerminal ); } - - if( Buffer[i] < 128 ) // Plain ASCII - VT_int_PutChar(Term, Buffer[i]); - else { // UTF-8 - i += ReadUTF8(&Buffer[i], &val); - VT_int_PutChar(Term, val); + LEAVE('i', term->Mode); + return term->Mode; + + // Get/set the terminal width + case TERM_IOCTL_WIDTH: + if(Data != NULL) { + if( CheckMem(Data, sizeof(int)) == 0 ) { + LEAVE('i', -1); + return -1; + } + term->NewWidth = *iData; } - } + if( term->NewWidth ) + ret = term->NewWidth; + else if( term->Mode == TERM_MODE_TEXT ) + ret = term->TextWidth; + else + ret = term->Width; + LEAVE('i', ret); + return ret; - // Update cursor - if( !(Term->Flags & VT_FLAG_HIDECSR) ) - { - tVideo_IOCtl_Pos pos; - pos.x = Term->WritePos % Term->Width; - pos.y = Term->WritePos / Term->Width; - VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSOR, &pos); - } -} - -/** - * \fn void VT_int_ClearLine(tVTerm *Term, int Num) - * \brief Clears a line in a virtual terminal - */ -void VT_int_ClearLine(tVTerm *Term, int Num) -{ - int i; - //ENTER("pTerm iNum", Term, Num); - for( i = Term->Width; i--; ) - { - Term->Text[ Num*Term->Width + i ].Ch = 0; - Term->Text[ Num*Term->Width + i ].Colour = Term->CurColour; - } - //LEAVE('-'); -} - -/** - * \fn int VT_int_ParseEscape(tVTerm *Term, char *Buffer) - * \brief Parses a VT100 Escape code - */ -int VT_int_ParseEscape(tVTerm *Term, char *Buffer) -{ - char c; - int argc = 0, j = 1; - int args[4] = {0,0,0,0}; - - switch(Buffer[0]) { - //Large Code - case '[': - // Get Arguments - c = Buffer[1]; - do { - while('0' <= c && c <= '9') { - args[argc] *= 10; - args[argc] += c-'0'; - c = Buffer[++j]; + // Get/set the terminal height + case TERM_IOCTL_HEIGHT: + if(Data != NULL) { + if( CheckMem(Data, sizeof(int)) == 0 ) { + LEAVE('i', -1); + return -1; } - argc ++; - } while(c == ';'); - - // Get string (what does this do?) - if(c == '"') { - c = Buffer[++j]; - while(c != '"') - c = Buffer[++j]; + term->NewHeight = *iData; } + if( term->NewHeight ) + ret = term->NewHeight; + else if( term->Mode == TERM_MODE_TEXT ) + ret = term->TextHeight; + else + ret = term->Height; + LEAVE('i', ret); + return ret; + + case TERM_IOCTL_FORCESHOW: + Log_Log("VTerm", "Thread %i forced VTerm %i to be shown", + Threads_GetTID(), (int)Node->Inode); + VT_SetTerminal( Node->Inode ); + LEAVE('i', 1); + return 1; + + case TERM_IOCTL_GETSETCURSOR: + if(Data != NULL) + { + tVideo_IOCtl_Pos *pos = Data; + if( !CheckMem(Data, sizeof(*pos)) ) { + errno = -EINVAL; + LEAVE('i', -1); + return -1; + } - // Get Command - if( ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) + if( term->Mode == TERM_MODE_TEXT ) + { + if(term->Flags & VT_FLAG_ALTBUF) + term->AltWritePos = pos->x + pos->y * term->TextWidth; + else + term->WritePos = pos->x + pos->y * term->TextWidth + term->ViewPos; + VT_int_UpdateCursor(term, 0); + } + else + { + term->VideoCursorX = pos->x; + term->VideoCursorY = pos->y; + VT_int_UpdateCursor(term, 1); + } + } + ret = (term->Flags & VT_FLAG_ALTBUF) ? term->AltWritePos : term->WritePos-term->ViewPos; + LEAVE('i', ret); + return ret; + + case TERM_IOCTL_SETCURSORBITMAP: { + tVideo_IOCtl_Bitmap *bmp = Data; + if( Data == NULL ) { - switch(c) { - //Clear By Line - case 'J': - // Clear Screen - switch(args[0]) - { - case 2: - { - int i = Term->Height * VT_SCROLLBACK; - while( i-- ) VT_int_ClearLine(Term, i); - Term->WritePos = 0; - Term->ViewPos = 0; - VT_int_UpdateScreen(Term, 1); - } - break; - } - break; - // Set Font flags - case 'm': - for( ; argc--; ) - { - // Flags - if( 0 <= args[argc] && args[argc] <= 8) - { - switch(args[argc]) - { - case 0: Term->CurColour = DEFAULT_COLOUR; break; // Reset - case 1: Term->CurColour |= 0x80000000; break; // Bright - case 2: Term->CurColour &= ~0x80000000; break; // Dim - } - } - // Foreground Colour - else if(30 <= args[argc] && args[argc] <= 37) { - Term->CurColour &= 0xF000FFFF; - Term->CurColour |= (Uint32)caVT100Colours[ args[argc]-30+(Term->CurColour>>28) ] << 16; - } - // Background Colour - else if(40 <= args[argc] && args[argc] <= 47) { - Term->CurColour &= 0xFFFF8000; - Term->CurColour |= caVT100Colours[ args[argc]-40+((Term->CurColour>>12)&15) ]; - } - } - break; + free( term->VideoCursor ); + term->VideoCursor = NULL; + LEAVE('i', 0); + return 0; + } + + // Sanity check bitmap + if( !CheckMem(bmp, sizeof(tVideo_IOCtl_Bitmap)) ) { + Log_Notice("VTerm", "%p in TERM_IOCTL_SETCURSORBITMAP invalid", bmp); + errno = -EINVAL; + LEAVE_RET('i', -1); + } + if( !CheckMem(bmp->Data, bmp->W*bmp->H*sizeof(Uint32)) ) { + Log_Notice("VTerm", "%p in TERM_IOCTL_SETCURSORBITMAP invalid", bmp); + errno = -EINVAL; + LEAVE_RET('i', -1); + } + + // Reallocate if needed + if(term->VideoCursor) + { + if(bmp->W * bmp->H != term->VideoCursor->W * term->VideoCursor->H) { + free(term->VideoCursor); + term->VideoCursor = NULL; + } + } + if(!term->VideoCursor) { + term->VideoCursor = malloc(sizeof(tVideo_IOCtl_Pos) + bmp->W*bmp->H*sizeof(Uint32)); + if(!term->VideoCursor) { + Log_Error("VTerm", "Unable to allocate memory for cursor"); + errno = -ENOMEM; + LEAVE_RET('i', -1); } } - break; - default: - break; - } + memcpy(term->VideoCursor, bmp, sizeof(tVideo_IOCtl_Pos) + bmp->W*bmp->H*sizeof(Uint32)); - return j + 1; + Log_Debug("VTerm", "Set VT%i's cursor to %p %ix%i", + (int)term->Node.Inode, bmp, bmp->W, bmp->H); + + if(gpVT_CurTerm == term) + VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSORBITMAP, term->VideoCursor); + + LEAVE('i', 0); + return 0; } + } + LEAVE('i', -1); + return -1; } /** - * \fn void VT_int_PutChar(tVTerm *Term, Uint32 Ch) - * \brief Write a single character to a VTerm + * \fn void VT_SetTerminal(int ID) + * \brief Set the current terminal */ -void VT_int_PutChar(tVTerm *Term, Uint32 Ch) +void VT_SetTerminal(int ID) { - int i; - //ENTER("pTerm xCh", Term, Ch); - //LOG("Term = {WritePos:%i, ViewPos:%i}\n", Term->WritePos, Term->ViewPos); - - switch(Ch) + // Copy the screen state + if( ID != giVT_CurrentTerminal && gpVT_CurTerm->Mode != TERM_MODE_TEXT ) { - case 0: return; // Ignore NULL byte - case '\n': - Term->WritePos += Term->Width; - case '\r': - Term->WritePos -= Term->WritePos % Term->Width; - break; - - case '\t': - do { - Term->Text[ Term->WritePos ].Ch = '\t'; - Term->Text[ Term->WritePos ].Colour = Term->CurColour; - Term->WritePos ++; - } while(Term->WritePos & 7); - break; - - case '\b': - // Backspace is invalid at Offset 0 - if(Term->WritePos == 0) break; - - Term->WritePos --; - // Singe Character - if(Term->Text[ Term->WritePos ].Ch != '\t') { - Term->Text[ Term->WritePos ].Ch = 0; - Term->Text[ Term->WritePos ].Colour = Term->CurColour; - break; + if( !gpVT_CurTerm->Buffer ) + gpVT_CurTerm->Buffer = malloc( gpVT_CurTerm->Width*gpVT_CurTerm->Height*4 ); + if( gpVT_CurTerm->Width < giVT_RealWidth ) + { + int line; + Uint ofs = 0; + Uint32 *dest = gpVT_CurTerm->Buffer; + // Slower scanline copy + for( line = 0; line < gpVT_CurTerm->Height; line ++ ) + { + VFS_ReadAt(giVT_OutputDevHandle, ofs, gpVT_CurTerm->Width*4, dest); + ofs += giVT_RealWidth * 4; + dest += gpVT_CurTerm->Width; + } } - // Tab - i = 7; // Limit it to 8 - do { - Term->Text[ Term->WritePos ].Ch = 0; - Term->Text[ Term->WritePos ].Colour = Term->CurColour; - Term->WritePos --; - } while(Term->WritePos && i-- && Term->Text[ Term->WritePos ].Ch == '\t'); - break; - - default: - Term->Text[ Term->WritePos ].Ch = Ch; - Term->Text[ Term->WritePos ].Colour = Term->CurColour; - Term->WritePos ++; - break; - } - - - // Move Screen - if(Term->WritePos >= Term->Width*Term->Height*VT_SCROLLBACK) - { - int base, i; - Term->WritePos -= Term->Width; - - // Update view position - base = Term->Width*Term->Height*(VT_SCROLLBACK-1); - if(Term->ViewPos < base) Term->ViewPos += Term->Width; - if(Term->ViewPos > base) Term->ViewPos = base; - - // Scroll terminal cache - base = Term->Width*(Term->Height*VT_SCROLLBACK-1); - - // Scroll Back - memcpy( - Term->Text, - &Term->Text[Term->Width], - Term->Width*(Term->Height-1)*VT_SCROLLBACK*sizeof(tVT_Char) - ); - - // Clear last row - for( i = 0; i < Term->Width; i ++ ) + else { - Term->Text[ base + i ].Ch = 0; - Term->Text[ base + i ].Colour = Term->CurColour; + VFS_ReadAt(giVT_OutputDevHandle, + 0, gpVT_CurTerm->Height*giVT_RealWidth*4, + gpVT_CurTerm->Buffer + ); } - - VT_int_UpdateScreen( Term, 1 ); } - else if(Term->WritePos > Term->Width*Term->Height+Term->ViewPos) + + // Update current terminal ID + Log_Log("VTerm", "Changed terminal from %i to %i", giVT_CurrentTerminal, ID); + giVT_CurrentTerminal = ID; + gpVT_CurTerm = &gVT_Terminals[ID]; + + if( gpVT_CurTerm->Mode == TERM_MODE_TEXT ) { - Term->ViewPos += Term->Width; - VT_int_UpdateScreen( Term, 1 ); + VT_SetMode( VIDEO_BUFFMT_TEXT ); } else - VT_int_UpdateScreen( Term, 0 ); + { + // Update the cursor image + if(gpVT_CurTerm->VideoCursor) + VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSORBITMAP, gpVT_CurTerm->VideoCursor); + VT_SetMode( VIDEO_BUFFMT_FRAMEBUFFER ); + } - //LEAVE('-'); -} - -/** - * \fn void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ) - * \brief Updates the video framebuffer - */ -void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ) -{ - if(UpdateAll) { + if(gpVT_CurTerm->Buffer) + { + // TODO: Handle non equal sized VFS_WriteAt( giVT_OutputDevHandle, 0, - Term->Width*Term->Height*sizeof(tVT_Char), - &Term->Text[Term->ViewPos] - ); - } else { - int pos = Term->WritePos - Term->WritePos % Term->Width; - VFS_WriteAt( - giVT_OutputDevHandle, - (pos - Term->ViewPos)*sizeof(tVT_Char), - Term->Width*sizeof(tVT_Char), - &Term->Text[pos] + gpVT_CurTerm->Width*gpVT_CurTerm->Height*sizeof(Uint32), + gpVT_CurTerm->Buffer ); } -} - -// --- -// Font Render -// --- -#define MONOSPACE_FONT 10816 - -#if MONOSPACE_FONT == 10808 // 8x8 -# include "vterm_font_8x8.h" -#elif MONOSPACE_FONT == 10816 // 8x16 -# include "vterm_font_8x16.h" -#endif - - -int VT_Font_GetWidth(Uint32 Codepoint) -{ - return FONT_WIDTH; -} -int VT_Font_GetHeight(Uint32 Codepoint) -{ - return FONT_HEIGHT; -} - -void VT_Font_Render(Uint32 Codepoint, void *Buffer, Uint32 BGC, Uint32 FGC) -{ -// Uint8 *font; - -} - -/** - * \fn Uint8 *VT_Font_GetChar(Uint32 Codepoint) - * \brief Gets an index into the font array given a Unicode Codepoint - * \note See http://en.wikipedia.org/wiki/CP437 - */ -Uint8 *VT_Font_GetChar(Uint32 Codepoint) -{ - int index = 0; - if(Codepoint < 128) - return &VTermFont[Codepoint*FONT_HEIGHT]; - switch(Codepoint) - { - case 0xC7: index = 128; break; // Ç - case 0xFC: index = 129; break; // ü - case 0xE9: index = 130; break; // é - case 0xE2: index = 131; break; // â - case 0xE4: index = 132; break; // ä - case 0xE0: index = 133; break; // à - case 0xE5: index = 134; break; // å - case 0xE7: index = 135; break; // ç - case 0xEA: index = 136; break; // ê - case 0xEB: index = 137; break; // ë - case 0xE8: index = 138; break; // è - case 0xEF: index = 139; break; // ï - case 0xEE: index = 140; break; // î - case 0xEC: index = 141; break; // ì - case 0xC4: index = 142; break; // Ä - case 0xC5: index = 143; break; // Å - } - return &VTermFont[index*FONT_HEIGHT]; + VT_int_UpdateCursor(gpVT_CurTerm, 1); + // Update the screen + VT_int_UpdateScreen(gpVT_CurTerm, 1); }