X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=Kernel%2Fdrv%2Fvterm.c;h=252f2924574ee4b93bf98fd0b2b888046c215a41;hb=34dbc845a86903c0b531216b166a4d596255bc9a;hp=09f85ff6c658dd0b3f32129923b3efaf41f05994;hpb=77ed20ce9d7e25654215980d0f89e63b8dd366f0;p=tpg%2Facess2.git diff --git a/Kernel/drv/vterm.c b/Kernel/drv/vterm.c index 09f85ff6..252f2924 100644 --- a/Kernel/drv/vterm.c +++ b/Kernel/drv/vterm.c @@ -1,7 +1,8 @@ /* * Acess2 Virtual Terminal Driver */ -#include +#define DEBUG 0 +#include #include #include #include @@ -9,18 +10,20 @@ #include #include +#define USE_CTRL_ALT 0 + // === CONSTANTS === #define VERSION ((0<<8)|(50)) -#define NUM_VTS 7 +#define NUM_VTS 8 #define MAX_INPUT_CHARS32 64 #define MAX_INPUT_CHARS8 (MAX_INPUT_CHARS32*4) -#define VT_SCROLLBACK 1 // 2 Screens of text -#define DEFAULT_OUTPUT "VGA" //#define DEFAULT_OUTPUT "BochsGA" +#define DEFAULT_OUTPUT "Vesa" #define DEFAULT_INPUT "PS2Keyboard" -#define DEFAULT_WIDTH 80 -#define DEFAULT_HEIGHT 25 +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 +#define DEFAULT_SCROLLBACK 2 // 2 Screens of text + current screen #define DEFAULT_COLOUR (VT_COL_BLACK|(0xAAA<<16)) #define VT_FLAG_HIDECSR 0x01 @@ -36,23 +39,28 @@ enum eVT_InModes { typedef struct { int Mode; //!< Current Mode (see ::eTplTerminal_Modes) int Flags; //!< Flags (see VT_FLAG_*) + + short NewWidth; //!< Un-applied dimensions (Width) + short NewHeight; //!< Un-applied dimensions (Height) short Width; //!< Virtual Width short Height; //!< Virtual Height - short RealWidth; //!< Real Width - short RealHeight; //!< Real Height + short TextWidth; //!< Text Virtual Width + short TextHeight; //!< Text Virtual Height int ViewPos; //!< View Buffer Offset (Text Only) int WritePos; //!< Write Buffer Offset (Text Only) Uint32 CurColour; //!< Current Text Colour - char Name[2]; //!< Name of the terminal + tMutex ReadingLock; //!< Lock the VTerm when a process is reading from it + tTID ReadingThread; //!< Owner of the lock int InputRead; //!< Input buffer read position int InputWrite; //!< Input buffer write position char InputBuffer[MAX_INPUT_CHARS8]; - union { - tVT_Char *Text; - Uint32 *Buffer; - }; + + tVT_Char *Text; + Uint32 *Buffer; + + char Name[2]; //!< Name of the terminal tVFS_Node Node; } tVTerm; @@ -61,22 +69,29 @@ extern void Debug_SetKTerminal(char *File); // === PROTOTYPES === int VT_Install(char **Arguments); +void VT_InitOutput(void); +void VT_InitInput(void); 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_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data); +void VT_SetResolution(int Width, int Height); +void VT_SetMode(int Mode); void VT_SetTerminal(int ID); 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_ScrollFramebuffer( tVTerm *Term ); void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ); -void VT_int_ChangeMode(tVTerm *Term, int NewMode); +void VT_int_ChangeMode(tVTerm *Term, int NewMode, int NewWidth, int NewHeight); // === CONSTANTS === const Uint16 caVT100Colours[] = { + // Black, Red, Green, Yellow, Blue, Purple, Cyan, Gray + // Same again, but bright VT_COL_BLACK, 0x700, 0x070, 0x770, 0x007, 0x707, 0x077, 0xAAA, VT_COL_GREY, 0xF00, 0x0F0, 0xFF0, 0x00F, 0xF0F, 0x0FF, VT_COL_WHITE }; @@ -98,6 +113,11 @@ tDevFS_Driver gVT_DrvInfo = { // --- 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; @@ -115,60 +135,72 @@ char *gsVT_InputDevice = NULL; */ int VT_Install(char **Arguments) { - char **args = Arguments; - char *arg; int i; // Scan Arguments if(Arguments) { + char **args = Arguments; + char *arg, *opt, *val; for( ; (arg = *args); args++ ) { - if(arg[0] != '-') continue; - - 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; + Log_Debug("VTerm", "Argument '%s'", arg); + opt = arg; + val = arg + strpos(arg, '='); *val++ = '\0'; + if( strcmp(opt, "Video") == 0 ) { + gsVT_OutputDevice = val; + } + else if( strcmp(opt, "Input") == 0 ) { + gsVT_InputDevice = 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 ); } } } + if(gsVT_OutputDevice) Modules_InitialiseBuiltin( gsVT_OutputDevice ); + if(gsVT_InputDevice) Modules_InitialiseBuiltin( gsVT_InputDevice ); + // Apply Defaults - if(!gsVT_OutputDevice) gsVT_OutputDevice = "/Devices/"DEFAULT_OUTPUT; - if(!gsVT_InputDevice) gsVT_InputDevice = "/Devices/"DEFAULT_INPUT; + if(!gsVT_OutputDevice) gsVT_OutputDevice = DEFAULT_OUTPUT; + if(!gsVT_InputDevice) gsVT_InputDevice = DEFAULT_INPUT; + + // Create 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); // Create Nodes for( i = 0; i < NUM_VTS; i++ ) { 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].CurColour = DEFAULT_COLOUR; gVT_Terminals[i].WritePos = 0; gVT_Terminals[i].ViewPos = 0; - gVT_Terminals[i].Buffer = calloc( 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'; @@ -184,10 +216,14 @@ int VT_Install(char **Arguments) // Add to DevFS DevFS_AddDevice( &gVT_DrvInfo ); + VT_InitOutput(); + VT_InitInput(); + // Set kernel output to VT0 Debug_SetKTerminal("/Devices/VTerm/0"); - return 0; + Log_Log("VTerm", "Returning %i", MODULE_ERR_OK); + return MODULE_ERR_OK; } /** @@ -197,8 +233,13 @@ int VT_Install(char **Arguments) void VT_InitOutput() { giVT_OutputDevHandle = VFS_Open(gsVT_OutputDevice, VFS_OPENFLAG_WRITE); - LOG("giVT_OutputDevHandle = %x\n", giVT_OutputDevHandle); + if(giVT_InputDevHandle == -1) { + Log_Warning("VTerm", "Oh F**k, I can't open the video device '%s'", gsVT_OutputDevice); + return ; + } + VT_SetResolution( giVT_RealWidth, giVT_RealHeight ); VT_SetTerminal( 0 ); + VT_SetMode( VIDEO_BUFFMT_TEXT ); } /** @@ -208,11 +249,70 @@ void VT_InitOutput() void VT_InitInput() { 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); } +/** + * \brief Set the video resolution + * \param Width New screen width + * \param Height New screen height + */ +void VT_SetResolution(int Width, int Height) +{ + 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 + ); + } + VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_GETSETMODE, &tmp ); + + // Resize text terminals if needed + if( 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 + giVT_RealWidth = mode.width; + giVT_RealHeight = mode.height; + for( i = 0; i < NUM_VTS; i ++ ) + { + if( gVT_Terminals[i].Mode != TERM_MODE_TEXT ) continue; + + gVT_Terminals[i].Text = realloc( + gVT_Terminals[i].Text, + newBufSize*sizeof(tVT_Char) + ); + } + } +} + +/** + * \brief Set video output buffer mode + */ +void VT_SetMode(int Mode) +{ + VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &Mode ); +} + /** * \fn char *VT_ReadDir(tVFS_Node *Node, int Pos) * \brief Read from the VTerm Directory @@ -225,14 +325,16 @@ char *VT_ReadDir(tVFS_Node *Node, int Pos) } /** - * \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(); @@ -250,7 +352,7 @@ 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; } @@ -301,13 +403,18 @@ Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) int pos = 0; tVTerm *term = &gVT_Terminals[ Node->Inode ]; + Mutex_Acquire( &term->ReadingLock ); + term->ReadingThread = Threads_GetTID(); + // Check current mode switch(term->Mode) { + // Text Mode (UTF-8) case TERM_MODE_TEXT: while(pos < Length) { - while(term->InputRead == term->InputWrite) Threads_Yield(); + //TODO: Sleep instead + while(term->InputRead == term->InputWrite) Threads_Sleep(); ((char*)Buffer)[pos] = term->InputBuffer[term->InputRead]; pos ++; @@ -316,11 +423,12 @@ Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) } break; - case TERM_MODE_FB: - case TERM_MODE_OPENGL: + //case TERM_MODE_FB: + // Other - UCS-4 + default: while(pos < Length) { - while(term->InputRead == term->InputWrite) Threads_Yield(); + while(term->InputRead == term->InputWrite) Threads_Sleep(); ((Uint32*)Buffer)[pos] = ((Uint32*)term->InputBuffer)[term->InputRead]; pos ++; term->InputRead ++; @@ -328,6 +436,10 @@ Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) } break; } + + term->ReadingThread = -1; + Mutex_Release( &term->ReadingLock ); + return 0; } @@ -338,18 +450,75 @@ Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) { tVTerm *term = &gVT_Terminals[ Node->Inode ]; - - //ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); + int size; // Write switch( term->Mode ) { + // Print Text case TERM_MODE_TEXT: VT_int_PutString(term, Buffer, Length); break; + // 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; + } + + // Copy to the local cache + memcpy( (void*)((Uint)term->Buffer + (Uint)Offset), Buffer, Length ); + + // Update screen if needed + if( Node->Inode == giVT_CurrentTerminal ) + { + // Fill entire screen? + if( giVT_RealWidth > term->Width || giVT_RealHeight > term->Height ) + { + // No? :( Well, just center it + int x, y, w, h; + 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; + y += (giVT_RealHeight - term->Height) / 2; + while(h--) + { + VFS_WriteAt( giVT_OutputDevHandle, + (x + y * giVT_RealWidth)*4, + term->Width * 4, + Buffer + ); + Buffer = (void*)( (Uint)Buffer + term->Width*4 ); + y ++; + } + return 0; + } + else { + return VFS_WriteAt( giVT_OutputDevHandle, Offset, Length, Buffer ); + } + } + // 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; } @@ -360,43 +529,115 @@ Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data) { 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; + } + switch(Id) { - case DRV_IOCTL_TYPE: return DRV_TYPE_TERMINAL; - case DRV_IOCTL_IDENT: memcpy(Data, "VT\0\0", 4); return 0; - case DRV_IOCTL_VERSION: return VERSION; - case DRV_IOCTL_LOOKUP: return 0; + // --- 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) return term->Mode; - - if(term->Mode != *iData) { - VT_int_ChangeMode(term, *iData); + if(Data != NULL) + { + 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 ); } - - // Update the screen dimensions - if(giVT_CurrentTerminal == Node->Inode) - VT_SetTerminal( giVT_CurrentTerminal ); - break; + LEAVE('i', term->Mode); + return term->Mode; // Get/set the terminal width case TERM_IOCTL_WIDTH: - if(Data == NULL) return term->Width; - term->Width = *iData; - break; + 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; // Get/set the terminal height case TERM_IOCTL_HEIGHT: - if(Data == NULL) return term->Height; - term->Height = *iData; - break; + if(Data != NULL) { + if( CheckMem(Data, sizeof(int)) == 0 ) { + LEAVE('i', -1); + return -1; + } + 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; - default: - return -1; + 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; } - return 0; + LEAVE('i', -1); + return -1; } /** @@ -404,34 +645,26 @@ int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data) * \brief Set the current terminal */ void VT_SetTerminal(int ID) -{ - tVideo_IOCtl_Mode mode = {0}; - int modeNum; +{ + // Update current terminal ID + Log_Log("VTerm", "Changed terminal from %i to %i", giVT_CurrentTerminal, ID); + giVT_CurrentTerminal = ID; + gpVT_CurTerm = &gVT_Terminals[ID]; - // Create the video mode - mode.width = gVT_Terminals[ ID ].Width; - mode.height = gVT_Terminals[ ID ].Height; - // - Text Mode - if(gVT_Terminals[ ID ].Mode == TERM_MODE_TEXT) { - mode.bpp = 12; - mode.flags = VIDEO_FLAG_TEXT; - } - // - Framebuffer or 3D - else { - mode.bpp = 32; - mode.flags = 0; + // Update cursor + if( gpVT_CurTerm->Mode == TERM_MODE_TEXT && !(gpVT_CurTerm->Flags & VT_FLAG_HIDECSR) ) + { + tVideo_IOCtl_Pos pos; + pos.x = (gpVT_CurTerm->WritePos - gpVT_CurTerm->ViewPos) % gpVT_CurTerm->TextWidth; + pos.y = (gpVT_CurTerm->WritePos - gpVT_CurTerm->ViewPos) / gpVT_CurTerm->TextWidth; + if( pos.x < gpVT_CurTerm->TextHeight ) + VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSOR, &pos); } - // Set video mode - VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_FINDMODE, &mode ); - modeNum = mode.id; - gVT_Terminals[ ID ].RealWidth = mode.width; - gVT_Terminals[ ID ].RealHeight = mode.height; - VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_SETMODE, &modeNum ); - - // Update current terminal ID - Log("Changed terminal from %i to %i", giVT_CurrentTerminal, ID); - giVT_CurrentTerminal = ID; + if( gpVT_CurTerm->Mode == TERM_MODE_TEXT ) + VT_SetMode( VIDEO_BUFFMT_TEXT ); + else + VT_SetMode( VIDEO_BUFFMT_FRAMEBUFFER ); // Update the screen VT_int_UpdateScreen( &gVT_Terminals[ ID ], 1 ); @@ -440,12 +673,17 @@ void VT_SetTerminal(int ID) /** * \fn void VT_KBCallBack(Uint32 Codepoint) * \brief Called on keyboard interrupt + * \param Codepoint Pseudo-UTF32 character + * + * Handles a key press and sends the key code to the user's buffer. + * If the code creates a kernel-magic sequence, it is not passed to the + * user and is handled in-kernel. */ void VT_KBCallBack(Uint32 Codepoint) { - tVTerm *term = &gVT_Terminals[giVT_CurrentTerminal]; + tVTerm *term = gpVT_CurTerm; - // How the hell did we get a Codepoint of zero? + // How the hell did we get a codepoint of zero? if(Codepoint == 0) return; // Key Up @@ -454,28 +692,30 @@ void VT_KBCallBack(Uint32 Codepoint) Codepoint &= 0x7FFFFFFF; switch(Codepoint) { - case KEY_LALT: - case KEY_RALT: - gbVT_AltDown = 0; - break; - case KEY_LCTRL: - case KEY_RCTRL: - gbVT_CtrlDown = 0; - break; + #if !USE_CTRL_ALT + case KEY_RSHIFT: gbVT_CtrlDown = 0; break; + case KEY_LSHIFT: gbVT_AltDown = 0; break; + #else + case KEY_LALT: gbVT_AltDown &= ~1; break; + case KEY_RALT: gbVT_AltDown &= ~2; break; + case KEY_LCTRL: gbVT_CtrlDown &= ~1 break; + case KEY_RCTRL: gbVT_CtrlDown &= ~2; break; + #endif } return; } switch(Codepoint) { - case KEY_LALT: - case KEY_RALT: - gbVT_AltDown = 1; - break; - case KEY_LCTRL: - case KEY_RCTRL: - gbVT_CtrlDown = 1; - break; + #if !USE_CTRL_ALT // HACK: Use both shifts instead of Ctrl-Alt + case KEY_RSHIFT: gbVT_CtrlDown = 1; break; + case KEY_LSHIFT: gbVT_AltDown = 1; break; + #else + case KEY_LALT: gbVT_AltDown |= 1; break; + case KEY_RALT: gbVT_AltDown |= 2; break; + case KEY_LCTRL: gbVT_CtrlDown |= 1; break; + case KEY_RCTRL: gbVT_CtrlDown |= 2; break; + #endif default: if(!gbVT_AltDown || !gbVT_CtrlDown) @@ -494,10 +734,19 @@ void VT_KBCallBack(Uint32 Codepoint) case KEY_F10: VT_SetTerminal(9); return; case KEY_F11: VT_SetTerminal(10); return; case KEY_F12: VT_SetTerminal(11); return; + // Scrolling case KEY_PGUP: - break; + if( gpVT_CurTerm->ViewPos > gpVT_CurTerm->Width ) + gpVT_CurTerm->ViewPos -= gpVT_CurTerm->Width; + else + gpVT_CurTerm->ViewPos = 0; + return; case KEY_PGDOWN: - break; + if( gpVT_CurTerm->ViewPos < gpVT_CurTerm->Width*gpVT_CurTerm->Height*(giVT_Scrollback-1) ) + gpVT_CurTerm->ViewPos += gpVT_CurTerm->Width; + else + gpVT_CurTerm->ViewPos = gpVT_CurTerm->Width*gpVT_CurTerm->Height*(giVT_Scrollback-1); + return; } } @@ -505,7 +754,7 @@ void VT_KBCallBack(Uint32 Codepoint) if(term->Mode == TERM_MODE_TEXT) { Uint8 buf[6] = {0}; - int len; + int len = 0; // Ignore Modifer Keys if(Codepoint > KEY_MODIFIERS) return; @@ -514,20 +763,43 @@ void VT_KBCallBack(Uint32 Codepoint) switch(Codepoint) { case KEY_LEFT: - buf[0] = '\x1B'; buf[1] = '['; - buf[2] = 'D'; len = 3; + buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'D'; + len = 3; break; case KEY_RIGHT: - buf[0] = '\x1B'; buf[1] = '['; - buf[2] = 'C'; len = 3; + buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'C'; + len = 3; + break; + case KEY_UP: + buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'A'; + len = 3; + break; + case KEY_DOWN: + buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'B'; + len = 3; + break; + + case KEY_PGUP: + buf[0] = '\x1B'; buf[1] = '['; buf[2] = '5'; // Some overline also + //len = 4; // Commented out until I'm sure break; + case KEY_PGDOWN: + len = 0; + break; + + // Attempt to encode in UTF-8 default: len = WriteUTF8( buf, Codepoint ); - //Log("Codepoint = 0x%x", Codepoint); + if(len == 0) { + Warning("Codepoint (%x) is unrepresentable in UTF-8", Codepoint); + } break; } - //Log("len = %i, buf = %s", len, buf); + if(len == 0) { + // Unprintable / Don't Pass + return; + } // Write if( MAX_INPUT_CHARS8 - term->InputWrite >= len ) @@ -556,40 +828,10 @@ void VT_KBCallBack(Uint32 Codepoint) term->InputRead %= MAX_INPUT_CHARS32; } } -} - -/** - * \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++ ) - { - if( Buffer[i] == 0x1B ) // Escape Sequence - { - i ++; - i += VT_int_ParseEscape(Term, (char*)&Buffer[i]); - continue; - } - - 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); - } - } - // 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); + // Wake up the thread waiting on us + if( term->ReadingThread >= 0 ) { + Threads_WakeTID(term->ReadingThread); } } @@ -600,11 +842,13 @@ void VT_int_PutString(tVTerm *Term, Uint8 *Buffer, Uint Count) void VT_int_ClearLine(tVTerm *Term, int Num) { int i; + tVT_Char *cell = &Term->Text[ Num*Term->TextWidth ]; + if( Num < 0 || Num >= Term->TextHeight * (giVT_Scrollback + 1) ) return ; //ENTER("pTerm iNum", Term, Num); - for( i = Term->Width; i--; ) + for( i = Term->TextWidth; i--; ) { - Term->Text[ Num*Term->Width + i ].Ch = 0; - Term->Text[ Num*Term->Width + i ].Colour = Term->CurColour; + cell[ i ].Ch = 0; + cell[ i ].Colour = Term->CurColour; } //LEAVE('-'); } @@ -618,27 +862,25 @@ int VT_int_ParseEscape(tVTerm *Term, char *Buffer) char c; int argc = 0, j = 1; int tmp; - int args[4] = {0,0,0,0}; + int args[6] = {0,0,0,0}; - switch(Buffer[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]; - } - if( j != 1 ) argc ++; - } while(c == ';'); - - // Get string (what does this do?) - if(c == '"') { - c = Buffer[++j]; - while(c != '"') - c = Buffer[++j]; + c = Buffer[j++]; + if( '0' <= c && c <= '9' ) + { + do { + if(c == ';') c = Buffer[j++]; + while('0' <= c && c <= '9') { + args[argc] *= 10; + args[argc] += c-'0'; + c = Buffer[j++]; + } + argc ++; + } while(c == ';'); } // Get Command @@ -651,8 +893,8 @@ int VT_int_ParseEscape(tVTerm *Term, char *Buffer) if(argc == 1) tmp = args[0]; else tmp = 1; - if( Term->WritePos-(tmp-1) % Term->Width == 0 ) - Term->WritePos -= Term->WritePos % Term->Width; + if( Term->WritePos-(tmp-1) % Term->TextWidth == 0 ) + Term->WritePos -= Term->WritePos % Term->TextWidth; else Term->WritePos -= tmp; break; @@ -661,9 +903,9 @@ int VT_int_ParseEscape(tVTerm *Term, char *Buffer) case 'C': if(argc == 1) tmp = args[0]; else tmp = 1; - if( (Term->WritePos + tmp) % Term->Width == 0 ) { - Term->WritePos -= Term->WritePos % Term->Width; - Term->WritePos += Term->Width - 1; + if( (Term->WritePos + tmp) % Term->TextWidth == 0 ) { + Term->WritePos -= Term->WritePos % Term->TextWidth; + Term->WritePos += Term->TextWidth - 1; } else Term->WritePos += tmp; break; @@ -675,7 +917,7 @@ int VT_int_ParseEscape(tVTerm *Term, char *Buffer) { case 2: { - int i = Term->Height * VT_SCROLLBACK; + int i = Term->TextHeight * (giVT_Scrollback + 1); while( i-- ) VT_int_ClearLine(Term, i); Term->WritePos = 0; Term->ViewPos = 0; @@ -684,6 +926,11 @@ int VT_int_ParseEscape(tVTerm *Term, char *Buffer) break; } break; + // Set cursor position + case 'h': + Term->WritePos = args[0] + args[1]*Term->TextWidth; + Log_Debug("VTerm", "args = {%i, %i}", args[0], args[1]); + break; // Set Font flags case 'm': for( ; argc--; ) @@ -710,15 +957,59 @@ int VT_int_ParseEscape(tVTerm *Term, char *Buffer) } } break; + default: + Log_Warning("VTerm", "Unknown control sequence"); + break; } } break; - default: - break; + default: break; } - return j + 1; + //Log_Debug("VTerm", "j = %i, Buffer = '%s'", j, Buffer); + return j; +} + +/** + * \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; + + // Iterate + for( i = 0; i < Count; i++ ) + { + // Handle escape sequences + if( Buffer[i] == 0x1B ) + { + i ++; + i += VT_int_ParseEscape(Term, (char*)&Buffer[i]) - 1; + continue; + } + + // Fast check for non UTF-8 + if( Buffer[i] < 128 ) // Plain ASCII + VT_int_PutChar(Term, Buffer[i]); + else { // UTF-8 + i += ReadUTF8(&Buffer[i], &val) - 1; + VT_int_PutChar(Term, val); + } + } + // Update Screen + VT_int_UpdateScreen( Term, 0 ); + + // Update cursor + if( Term == gpVT_CurTerm && !(Term->Flags & VT_FLAG_HIDECSR) ) + { + tVideo_IOCtl_Pos pos; + pos.x = (Term->WritePos - Term->ViewPos) % Term->TextWidth; + pos.y = (Term->WritePos - Term->ViewPos) / Term->TextWidth; + VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSOR, &pos); + } } /** @@ -728,16 +1019,15 @@ int VT_int_ParseEscape(tVTerm *Term, char *Buffer) void VT_int_PutChar(tVTerm *Term, Uint32 Ch) { int i; - //ENTER("pTerm xCh", Term, Ch); - //LOG("Term = {WritePos:%i, ViewPos:%i}\n", Term->WritePos, Term->ViewPos); - + switch(Ch) { case '\0': return; // Ignore NULL byte case '\n': - Term->WritePos += Term->Width; + VT_int_UpdateScreen( Term, 0 ); // Update the line before newlining + Term->WritePos += Term->TextWidth; case '\r': - Term->WritePos -= Term->WritePos % Term->Width; + Term->WritePos -= Term->WritePos % Term->TextWidth; break; case '\t': @@ -776,49 +1066,102 @@ void VT_int_PutChar(tVTerm *Term, Uint32 Ch) Term->WritePos ++; break; } - // Move Screen - if(Term->WritePos >= Term->Width*Term->Height*VT_SCROLLBACK) + // - Check if we need to scroll the entire scrollback buffer + if(Term->WritePos >= Term->TextWidth*Term->TextHeight*(giVT_Scrollback+1)) { int base, i; - Term->WritePos -= Term->Width; + + // Move back by one + Term->WritePos -= Term->TextWidth; + // Update the scren + VT_int_UpdateScreen( Term, 0 ); // 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; + base = Term->TextWidth*Term->TextHeight*(giVT_Scrollback); + 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 + base = Term->TextWidth*(Term->TextHeight*(giVT_Scrollback+1)-1); memcpy( Term->Text, - &Term->Text[Term->Width], - Term->Width*(Term->Height-1)*VT_SCROLLBACK*sizeof(tVT_Char) + &Term->Text[Term->TextWidth], + base*sizeof(tVT_Char) ); // Clear last row - for( i = 0; i < Term->Width; i ++ ) + for( i = 0; i < Term->TextWidth; i ++ ) { Term->Text[ base + i ].Ch = 0; Term->Text[ base + i ].Colour = Term->CurColour; } - VT_int_UpdateScreen( Term, 1 ); + VT_int_ScrollFramebuffer( Term ); + VT_int_UpdateScreen( Term, 0 ); } - else if(Term->WritePos > Term->Width*Term->Height+Term->ViewPos) + // Ok, so we only need to scroll the screen + else if(Term->WritePos >= Term->ViewPos + Term->TextWidth*Term->TextHeight) { - Term->ViewPos += Term->Width; - VT_int_UpdateScreen( Term, 1 ); - } - else + //Debug("Term->WritePos (%i) >= %i", + // Term->WritePos, + // Term->ViewPos + Term->Width*Term->Height + // ); + //Debug("Scrolling screen only"); + + // Update the last line + Term->WritePos -= Term->TextWidth; + VT_int_UpdateScreen( Term, 0 ); + Term->WritePos += Term->TextWidth; + VT_int_ClearLine(Term, Term->WritePos / Term->TextWidth); + + // Scroll + Term->ViewPos += Term->TextWidth; + //Debug("Term->ViewPos = %i", Term->ViewPos); + VT_int_ScrollFramebuffer( Term ); VT_int_UpdateScreen( Term, 0 ); + } //LEAVE('-'); } +/** + * \fn void VT_int_ScrollFramebuffer( tVTerm *Term ) + * \note Scrolls the framebuffer by 1 text line + */ +void VT_int_ScrollFramebuffer( tVTerm *Term ) +{ + int tmp; + struct { + Uint8 Op; + Uint16 DstX, DstY; + Uint16 SrcX, SrcY; + Uint16 W, H; + } PACKED buf; + + // Only update if this is the current terminal + if( Term != gpVT_CurTerm ) return; + + // Switch to 2D Command Stream + tmp = VIDEO_BUFFMT_2DSTREAM; + VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &tmp); + + // BLIT from 0,0 to 0,giVT_CharHeight + buf.Op = VIDEO_2DOP_BLIT; + buf.DstX = 0; buf.DstY = 0; + buf.SrcX = 0; buf.SrcY = giVT_CharHeight; + buf.W = Term->TextWidth * giVT_CharWidth; + buf.H = (Term->TextHeight-1) * giVT_CharHeight; + VFS_WriteAt(giVT_OutputDevHandle, 0, sizeof(buf), &buf); + + // Restore old mode (this function is only called during text mode) + tmp = VIDEO_BUFFMT_TEXT; + VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &tmp); +} + /** * \fn void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ) * \brief Updates the video framebuffer @@ -826,59 +1169,128 @@ void VT_int_PutChar(tVTerm *Term, Uint32 Ch) void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ) { // Only update if this is the current terminal - if( Term != &gVT_Terminals[giVT_CurrentTerminal] ) return; + if( Term != gpVT_CurTerm ) return; - if( Term->Mode == TERM_MODE_TEXT ) + switch( Term->Mode ) { + case TERM_MODE_TEXT: + // Re copy the entire screen? if(UpdateAll) { VFS_WriteAt( giVT_OutputDevHandle, 0, - Term->Width*Term->Height*sizeof(tVT_Char), + Term->TextWidth*Term->TextHeight*sizeof(tVT_Char), &Term->Text[Term->ViewPos] ); - } else { - int pos = Term->WritePos - Term->WritePos % Term->Width; + } + // Only copy the current line + else { + int pos = Term->WritePos - Term->WritePos % Term->TextWidth; VFS_WriteAt( giVT_OutputDevHandle, (pos - Term->ViewPos)*sizeof(tVT_Char), - Term->Width*sizeof(tVT_Char), + Term->TextWidth*sizeof(tVT_Char), &Term->Text[pos] ); } - } - else - { + break; + case TERM_MODE_FB: VFS_WriteAt( giVT_OutputDevHandle, 0, Term->Width*Term->Height*sizeof(Uint32), - &Term->Buffer + Term->Buffer ); + break; } } /** - * \fn void VT_int_ChangeMode(tVTerm *Term, int NewMode) - * \brief Change the mode of a VTerm + * \brief Update the screen mode + * \param Term Terminal to update + * \param NewMode New mode to set + * \param NewWidth New framebuffer width + * \param NewHeight New framebuffer height */ -void VT_int_ChangeMode(tVTerm *Term, int NewMode) -{ +void VT_int_ChangeMode(tVTerm *Term, int NewMode, int NewWidth, int NewHeight) +{ + int oldW = Term->Width; + int oldTW = oldW / giVT_CharWidth; + int oldH = Term->Height; + int oldTH = oldH / giVT_CharWidth; + tVT_Char *oldTBuf = Term->Text; + Uint32 *oldFB = Term->Buffer; + int w, h, i; + + // TODO: Increase RealWidth/RealHeight when this happens + if(NewWidth > giVT_RealWidth) NewWidth = giVT_RealWidth; + if(NewHeight > giVT_RealHeight) NewHeight = giVT_RealHeight; + + // Calculate new dimensions + Term->TextWidth = NewWidth / giVT_CharWidth; + Term->TextHeight = NewHeight / giVT_CharHeight; + Term->Width = NewWidth; + Term->Height = NewHeight; + Term->Mode = NewMode; + + // Allocate new buffers + // - Text + Term->Text = calloc( + Term->TextWidth * Term->TextHeight * (giVT_Scrollback+1), + sizeof(tVT_Char) + ); + if(oldTBuf) { + // Copy old buffer + w = oldTW; + if( w > Term->TextWidth ) w = Term->TextWidth; + h = oldTH; + if( h > Term->TextHeight ) h = Term->TextHeight; + h *= giVT_Scrollback + 1; + for( i = 0; i < h; i ++ ) + { + memcpy( + &Term->Text[i*Term->TextWidth], + &oldTBuf[i*oldTW], + w*sizeof(tVT_Char) + ); + + } + } + + // - Framebuffer + Term->Buffer = calloc( Term->Width * Term->Height, sizeof(Uint32) ); + if(oldFB) { + // Copy old buffer + w = oldW; + if( w > Term->Width ) w = Term->Width; + h = oldH; + if( h > Term->Height ) h = Term->Height; + for( i = 0; i < h; i ++ ) + { + memcpy( + &Term->Buffer[i*Term->Width], + &oldFB[i*oldW], + w*sizeof(Uint32) + ); + } + } + + + // Debug switch(NewMode) { case TERM_MODE_TEXT: - free(Term->Buffer); - Term->Text = calloc( Term->Width*Term->Height*VT_SCROLLBACK, sizeof(tVT_Char) ); + Log_Log("VTerm", "Set VT %p to text mode (%ix%i)", + Term, Term->TextWidth, Term->TextHeight); break; case TERM_MODE_FB: - free(Term->Text); - Term->Buffer = calloc( Term->Width*Term->Height, sizeof(Uint32) ); + Log_Log("VTerm", "Set VT %p to framebuffer mode (%ix%i)", + Term, Term->Width, Term->Height); break; - case TERM_MODE_OPENGL: - return; + //case TERM_MODE_2DACCEL: + //case TERM_MODE_3DACCEL: + // return; } - - Term->Mode = NewMode; } // --- @@ -909,13 +1321,14 @@ void VT_Font_Render(Uint32 Codepoint, void *Buffer, int Pitch, Uint32 BGC, Uint3 Uint8 *font; Uint32 *buf = Buffer; int x, y; + font = VT_Font_GetChar(Codepoint); for(y = 0; y < FONT_HEIGHT; y ++) { for(x = 0; x < FONT_WIDTH; x ++) { - if(*font & (1 << (FONT_WIDTH-x))) + if(*font & (1 << (FONT_WIDTH-x-1))) buf[x] = FGC; else buf[x] = BGC;