From: John Hodge Date: Sat, 21 Jan 2012 02:41:48 +0000 (+0800) Subject: Kernel - Split VTerm up into multiple files X-Git-Tag: rel0.15~792^2~29 X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=ceae45d80582dcb433cd26dcd6aad1c17229779d;p=tpg%2Facess2.git Kernel - Split VTerm up into multiple files --- diff --git a/Kernel/Makefile b/Kernel/Makefile index 3b300493..c0360389 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -54,7 +54,8 @@ OBJ := $(addprefix arch/$(ARCHDIR)/,$(A_OBJ)) OBJ += heap.o drvutil.o logging.o debug.o lib.o adt.o time.o OBJ += messages.o modules.o syscalls.o system.o OBJ += threads.o mutex.o semaphore.o workqueue.o -OBJ += drv/vterm.o drv/proc.o drv/fifo.o drv/iocache.o drv/pci.o +OBJ += drv/proc.o drv/fifo.o drv/iocache.o drv/pci.o +OBJ += drv/vterm.o drv/vterm_font.o drv/vterm_vt100.o drv/vterm_output.o drv/vterm_input.o drv/vterm_termbuf.o OBJ += binary.o bin/elf.o bin/pe.o OBJ += vfs/main.o vfs/open.o vfs/acls.o vfs/dir.o vfs/io.o vfs/mount.o OBJ += vfs/memfile.o vfs/nodecache.o vfs/handle.o vfs/select.o vfs/mmap.o @@ -78,14 +79,14 @@ OBJ += $(BUILDINFO_OBJ) all: $(BIN) clean: -# $(RM) $(BIN) ../Acess2.$(ARCH).gz $(BIN).dsm ../Map.$(ARCH).txt LineCounts.$(ARCH).txt $(OBJ) $(DEPFILES) - @$(RM) $(BIN) ../Acess2.$(ARCH).gz $(BIN).dsm ../Map.$(ARCH).txt LineCounts.$(ARCH).txt -r $(OBJDIR) $(OBJ) $(DEPFILES) $(BUILDINFO_SRC) + @$(RM) $(BIN) ../Acess2.$(ARCH).gz $(BIN).dsm ../Map.$(ARCH).txt LineCounts.$(ARCH).txt + @$(RM) -r $(OBJDIR) $(OBJ) $(DEPFILES) $(BUILDINFO_SRC) install: $(BIN) - cp $(BIN) $(BIN)_ - $(STRIP) $(BIN)_ - gzip -c $(BIN)_ > $(GZBIN) - $(RM) $(BIN)_ + @cp $(BIN) $(BIN)_ + @$(STRIP) $(BIN)_ + @gzip -c $(BIN)_ > $(GZBIN) + @$(RM) $(BIN)_ $(xCP) $(GZBIN) $(DISTROOT) apidoc: diff --git a/Kernel/drv/vterm.c b/Kernel/drv/vterm.c index 03e7c6c7..2ae38ee8 100644 --- a/Kernel/drv/vterm.c +++ b/Kernel/drv/vterm.c @@ -2,12 +2,11 @@ * Acess2 Virtual Terminal Driver */ #define DEBUG 0 -#include +#include "vterm.h" #include #include -#include #include -#include +#include #include #include @@ -15,8 +14,6 @@ #define VERSION ((0<<8)|(50)) #define NUM_VTS 8 -#define MAX_INPUT_CHARS32 64 -#define MAX_INPUT_CHARS8 (MAX_INPUT_CHARS32*4) //#define DEFAULT_OUTPUT "BochsGA" #define DEFAULT_OUTPUT "Vesa" #define FALLBACK_OUTPUT "x86_VGAText" @@ -25,100 +22,22 @@ #define DEFAULT_HEIGHT 480 #define DEFAULT_SCROLLBACK 2 // 2 Screens of text + current screen //#define DEFAULT_SCROLLBACK 0 -#define DEFAULT_COLOUR (VT_COL_BLACK|(0xAAA<<16)) - -#define VT_FLAG_HIDECSR 0x01 -#define VT_FLAG_ALTBUF 0x02 //!< Alternate screen buffer -#define VT_FLAG_RAWIN 0x04 //!< Don't handle ^Z/^C/^V -#define VT_FLAG_HASFB 0x10 //!< Set if the VTerm has requested the Framebuffer -#define VT_FLAG_SHOWCSR 0x20 //!< Always show the text cursor - -enum eVT_InModes { - VT_INMODE_TEXT8, // UTF-8 Text Mode (VT100/xterm Emulation) - VT_INMODE_TEXT32, // UTF-32 Text Mode (Acess Native) - NUM_VT_INMODES -}; // === TYPES === -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 TextWidth; //!< Text Virtual Width - short TextHeight; //!< Text Virtual Height - - Uint32 CurColour; //!< Current Text Colour - - int ViewPos; //!< View Buffer Offset (Text Only) - int WritePos; //!< Write Buffer Offset (Text Only) - tVT_Char *Text; - - tVT_Char *AltBuf; //!< Alternate Screen Buffer - int AltWritePos; //!< Alternate write position - short ScrollTop; //!< Top of scrolling region (smallest) - short ScrollHeight; //!< Length of scrolling region - - int VideoCursorX; - int VideoCursorY; - - 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]; -// tSemaphore InputSemaphore; - - Uint32 *Buffer; - - // TODO: Do I need to keep this about? - // When should it be deallocated? on move to text mode, or some other time - // Call set again, it's freed, and if NULL it doesn't get reallocated. - tVideo_IOCtl_Bitmap *VideoCursor; - - char Name[2]; //!< Name of the terminal - tVFS_Node Node; -} tVTerm; // === IMPORTS === extern void Debug_SetKTerminal(const 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, 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, const 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, const Uint8 *Buffer, Uint Count); -void VT_int_ClearLine(tVTerm *Term, int Num); -void VT_int_ParseEscape_StandardLarge(tVTerm *Term, char CmdChar, int argc, int *args); - int VT_int_ParseEscape(tVTerm *Term, const char *Buffer); -void VT_int_PutChar(tVTerm *Term, Uint32 Ch); -void VT_int_ScrollText(tVTerm *Term, int Count); -void VT_int_ScrollFramebuffer( tVTerm *Term, int Count ); -void VT_int_UpdateCursor( tVTerm *Term, int bShow ); -void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ); -void VT_int_ChangeMode(tVTerm *Term, int NewMode, int NewWidth, int NewHeight); -void VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled); // === 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 - }; // === GLOBALS === MODULE_DEFINE(0, VERSION, VTerm, VT_Install, NULL, DEFAULT_INPUT, NULL); @@ -157,10 +76,6 @@ char *gsVT_OutputDevice = NULL; char *gsVT_InputDevice = NULL; int giVT_OutputDevHandle = -2; int giVT_InputDevHandle = -2; -// --- Key States --- (Used for VT Switching/Magic Combos) - int gbVT_CtrlDown = 0; - int gbVT_AltDown = 0; - int gbVT_SysrqDown = 0; // === CODE === /** @@ -278,36 +193,6 @@ int VT_Install(char **Arguments) return MODULE_ERR_OK; } -/** - * \fn void VT_InitOutput() - * \brief Initialise Video Output - */ -void VT_InitOutput() -{ - giVT_OutputDevHandle = VFS_Open(gsVT_OutputDevice, VFS_OPENFLAG_WRITE); - if(giVT_OutputDevHandle == -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 ); -} - -/** - * \fn void VT_InitInput() - * \brief Initialises the input - */ -void VT_InitInput() -{ - giVT_InputDevHandle = VFS_Open(gsVT_InputDevice, VFS_OPENFLAG_READ); - if(giVT_InputDevHandle == -1) { - Log_Warning("VTerm", "Can't open the input device '%s'", gsVT_InputDevice); - return ; - } - VFS_IOCtl(giVT_InputDevHandle, KB_IOCTL_SETCALLBACK, VT_KBCallBack); -} - /** * \brief Set the video resolution * \param Width New screen width @@ -366,14 +251,6 @@ void VT_SetResolution(int Width, int Height) } } -/** - * \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 @@ -885,1246 +762,3 @@ void VT_SetTerminal(int ID) // Update the screen VT_int_UpdateScreen(gpVT_CurTerm, 1); } - -/** - * \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 = gpVT_CurTerm; - - // Catch VT binds - switch( Codepoint & KEY_ACTION_MASK ) - { - case KEY_ACTION_RELEASE: - switch(Codepoint & KEY_CODEPOINT_MASK) - { - 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; - } - break; - - case KEY_ACTION_PRESS: - switch(Codepoint & KEY_CODEPOINT_MASK) - { - 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; - } - - if(!gbVT_AltDown || !gbVT_CtrlDown) - break; - switch(Codepoint & KEY_CODEPOINT_MASK) - { - case KEY_F1: VT_SetTerminal(0); return; - case KEY_F2: VT_SetTerminal(1); return; - case KEY_F3: VT_SetTerminal(2); return; - case KEY_F4: VT_SetTerminal(3); return; - case KEY_F5: VT_SetTerminal(4); return; - case KEY_F6: VT_SetTerminal(5); return; - case KEY_F7: VT_SetTerminal(6); return; - case KEY_F8: VT_SetTerminal(7); return; - case KEY_F9: VT_SetTerminal(8); return; - case KEY_F10: VT_SetTerminal(9); return; - case KEY_F11: VT_SetTerminal(10); return; - case KEY_F12: VT_SetTerminal(11); return; - } - - // Scrolling is only valid in text mode - if(gpVT_CurTerm->Mode != TERM_MODE_TEXT) - break; - - switch(Codepoint & KEY_CODEPOINT_MASK) - { - // Scrolling - case KEY_PGUP: - if( gpVT_CurTerm->Flags & VT_FLAG_ALTBUF ) - return ; - gpVT_CurTerm->ViewPos = MAX( - 0, - gpVT_CurTerm->ViewPos - gpVT_CurTerm->Width - ); - return; - case KEY_PGDOWN: - if( gpVT_CurTerm->Flags & VT_FLAG_ALTBUF ) - return ; - gpVT_CurTerm->ViewPos = MIN( - gpVT_CurTerm->ViewPos + gpVT_CurTerm->Width, - gpVT_CurTerm->Width * gpVT_CurTerm->Height*giVT_Scrollback - ); - return; - } - break; - } - - // Encode key - if(term->Mode == TERM_MODE_TEXT) - { - Uint8 buf[6] = {0}; - int len = 0; - - // Ignore anything that isn't a press or refire - if( (Codepoint & KEY_ACTION_MASK) != KEY_ACTION_PRESS - && (Codepoint & KEY_ACTION_MASK) != KEY_ACTION_REFIRE - ) - { - return ; - } - - Codepoint &= KEY_CODEPOINT_MASK; - - // Ignore Modifer Keys - if(Codepoint > KEY_MODIFIERS) return; - - // Get UTF-8/ANSI Encoding - switch(Codepoint) - { - // 0: No translation, don't send to user - case 0: break; - case KEY_LEFT: - buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'D'; - len = 3; - break; - case KEY_RIGHT: - 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'; buf[3] = '~'; - len = 4; - break; - case KEY_PGDOWN: - buf[0] = '\x1B'; buf[1] = '['; buf[2] = '6'; buf[3] = '~'; - len = 4; - break; - - // Attempt to encode in UTF-8 - default: - len = WriteUTF8( buf, Codepoint ); - if(len == 0) { - Warning("Codepoint (%x) is unrepresentable in UTF-8", Codepoint); - } - break; - } - - if(len == 0) { - // Unprintable / Don't Pass - return; - } - -#if 0 - // Handle meta characters - if( !(term->Flags & VT_FLAG_RAWIN) ) - { - switch(buf[0]) - { - case '\3': // ^C - - break; - } - } -#endif - - // Write - if( MAX_INPUT_CHARS8 - term->InputWrite >= len ) - memcpy( &term->InputBuffer[term->InputWrite], buf, len ); - else { - memcpy( &term->InputBuffer[term->InputWrite], buf, MAX_INPUT_CHARS8 - term->InputWrite ); - memcpy( &term->InputBuffer[0], buf, len - (MAX_INPUT_CHARS8 - term->InputWrite) ); - } - // Roll the buffer over - term->InputWrite += len; - term->InputWrite %= MAX_INPUT_CHARS8; - if( (term->InputWrite - term->InputRead + MAX_INPUT_CHARS8)%MAX_INPUT_CHARS8 < len ) { - term->InputRead = term->InputWrite + 1; - term->InputRead %= MAX_INPUT_CHARS8; - } - } - else - { - // Encode the raw key event - Uint32 *raw_in = (void*)term->InputBuffer; - - #if 0 - // Drop new keys - if( term->InputWrite == term->InputRead ) - return ; - #endif - - raw_in[ term->InputWrite ] = Codepoint; - term->InputWrite ++; - if(term->InputWrite >= MAX_INPUT_CHARS32) - term->InputWrite -= MAX_INPUT_CHARS32; - - #if 1 - // TODO: Should old or new be dropped? - if(term->InputRead == term->InputWrite) { - term->InputRead ++; - if( term->InputRead >= MAX_INPUT_CHARS32 ) - term->InputRead -= MAX_INPUT_CHARS32; - } - #endif - } - - VFS_MarkAvaliable(&term->Node, 1); -} - -/** - * \brief Clears a line in a virtual terminal - * \param Term Terminal to modify - * \param Num Line number to clear - */ -void VT_int_ClearLine(tVTerm *Term, int Num) -{ - int i; - tVT_Char *cell; - - if( Num < 0 || Num >= Term->TextHeight * (giVT_Scrollback + 1) ) return ; - - cell = (Term->Flags & VT_FLAG_ALTBUF) ? Term->AltBuf : Term->Text; - cell = &cell[ Num*Term->TextWidth ]; - - for( i = Term->TextWidth; i--; ) - { - cell[ i ].Ch = 0; - cell[ i ].Colour = Term->CurColour; - } -} - -/** - * \brief Handle a standard large escape code - * - * Handles any escape code of the form \x1B[n,...A where n is an integer - * and A is any letter. - */ -void VT_int_ParseEscape_StandardLarge(tVTerm *Term, char CmdChar, int argc, int *args) -{ - int tmp = 1; - switch(CmdChar) - { - // Left - case 'D': - tmp = -1; - // Right - case 'C': - if(argc == 1) tmp *= args[0]; - if( Term->Flags & VT_FLAG_ALTBUF ) - { - if( (Term->AltWritePos + tmp) % Term->TextWidth == 0 ) { - Term->AltWritePos -= Term->AltWritePos % Term->TextWidth; - Term->AltWritePos += Term->TextWidth - 1; - } - else - Term->AltWritePos += tmp; - } - else - { - if( (Term->WritePos + tmp) % Term->TextWidth == 0 ) { - Term->WritePos -= Term->WritePos % Term->TextWidth; - Term->WritePos += Term->TextWidth - 1; - } - else - Term->WritePos += tmp; - } - break; - - // Erase - case 'J': - switch(args[0]) - { - case 0: // Erase below - break; - case 1: // Erase above - break; - case 2: // Erase all - if( Term->Flags & VT_FLAG_ALTBUF ) - { - int i = Term->TextHeight; - while( i-- ) VT_int_ClearLine(Term, i); - Term->AltWritePos = 0; - VT_int_UpdateScreen(Term, 1); - } - else - { - int i = Term->TextHeight * (giVT_Scrollback + 1); - while( i-- ) VT_int_ClearLine(Term, i); - Term->WritePos = 0; - Term->ViewPos = 0; - VT_int_UpdateScreen(Term, 1); - } - break; - } - break; - - // Erase in line - case 'K': - switch(args[0]) - { - case 0: // Erase to right - if( Term->Flags & VT_FLAG_ALTBUF ) - { - int i, max; - max = Term->Width - Term->AltWritePos % Term->Width; - for( i = 0; i < max; i ++ ) - Term->AltBuf[Term->AltWritePos+i].Ch = 0; - } - else - { - int i, max; - max = Term->Width - Term->WritePos % Term->Width; - for( i = 0; i < max; i ++ ) - Term->Text[Term->WritePos+i].Ch = 0; - } - VT_int_UpdateScreen(Term, 0); - break; - case 1: // Erase to left - if( Term->Flags & VT_FLAG_ALTBUF ) - { - int i = Term->AltWritePos % Term->Width; - while( i -- ) - Term->AltBuf[Term->AltWritePos++].Ch = 0; - } - else - { - int i = Term->WritePos % Term->Width; - while( i -- ) - Term->Text[Term->WritePos++].Ch = 0; - } - VT_int_UpdateScreen(Term, 0); - break; - case 2: // Erase all - if( Term->Flags & VT_FLAG_ALTBUF ) - { - VT_int_ClearLine(Term, Term->AltWritePos / Term->Width); - } - else - { - VT_int_ClearLine(Term, Term->WritePos / Term->Width); - } - VT_int_UpdateScreen(Term, 0); - break; - } - break; - - // Set cursor position - case 'H': - if( Term->Flags & VT_FLAG_ALTBUF ) - Term->AltWritePos = args[0] + args[1]*Term->TextWidth; - else - Term->WritePos = args[0] + args[1]*Term->TextWidth; - //Log_Debug("VTerm", "args = {%i, %i}", args[0], args[1]); - break; - - // Scroll up `n` lines - case 'S': - tmp = -1; - // Scroll down `n` lines - case 'T': - if(argc == 1) tmp *= args[0]; - if( Term->Flags & VT_FLAG_ALTBUF ) - VT_int_ScrollText(Term, tmp); - else - { - if(Term->ViewPos/Term->TextWidth + tmp < 0) - break; - if(Term->ViewPos/Term->TextWidth + tmp > Term->TextHeight * (giVT_Scrollback + 1)) - break; - - Term->ViewPos += Term->TextWidth*tmp; - } - break; - - // Set Font flags - case 'm': - for( ; argc--; ) - { - int colour_idx; - // 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) { - // Get colour index, accounting for bright bit - colour_idx = args[argc]-30 + ((Term->CurColour>>28) & 8); - Term->CurColour &= 0x8000FFFF; - Term->CurColour |= (Uint32)caVT100Colours[ colour_idx ] << 16; - } - // Background Colour - else if(40 <= args[argc] && args[argc] <= 47) { - // Get colour index, accounting for bright bit - colour_idx = args[argc]-40 + ((Term->CurColour>>12) & 8); - Term->CurColour &= 0xFFFF8000; - Term->CurColour |= caVT100Colours[ colour_idx ]; - } - else { - Log_Warning("VTerm", "Unknown font flag %i", args[argc]); - } - } - break; - - // Set scrolling region - case 'r': - if( argc != 2 ) break; - Term->ScrollTop = args[0]; - Term->ScrollHeight = args[1] - args[0]; - break; - - default: - Log_Warning("VTerm", "Unknown control sequence '\\x1B[%c'", CmdChar); - break; - } -} - -/** - * \fn int VT_int_ParseEscape(tVTerm *Term, const char *Buffer) - * \brief Parses a VT100 Escape code - */ -int VT_int_ParseEscape(tVTerm *Term, const char *Buffer) -{ - char c; - int argc = 0, j = 1; - int args[6] = {0,0,0,0}; - int bQuestionMark = 0; - - switch(Buffer[0]) - { - //Large Code - case '[': - // Get Arguments - c = Buffer[j++]; - if(c == '?') { - bQuestionMark = 1; - 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 - if( ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) - { - if( bQuestionMark ) - { - switch(c) - { - // DEC Private Mode Set - case 'h': - if(argc != 1) break; - switch(args[0]) - { - case 25: - Term->Flags &= ~VT_FLAG_HIDECSR; - break; - case 1047: - VT_int_ToggleAltBuffer(Term, 1); - break; - } - break; - case 'l': - if(argc != 1) break; - switch(args[0]) - { - case 25: - Term->Flags |= VT_FLAG_HIDECSR; - break; - case 1047: - VT_int_ToggleAltBuffer(Term, 0); - break; - } - break; - default: - Log_Warning("VTerm", "Unknown control sequence '\\x1B[?%c'", c); - break; - } - } - else - { - VT_int_ParseEscape_StandardLarge(Term, c, argc, args); - } - } - break; - - default: - Log_Notice("VTerm", "TODO: Handle short escape codes"); - break; - } - - //Log_Debug("VTerm", "j = %i, Buffer = '%s'", j, Buffer); - return j; -} - -/** - * \fn void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count) - * \brief Print a string to the Virtual Terminal - */ -void VT_int_PutString(tVTerm *Term, const 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, (const 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 ); -} - -/** - * \fn void VT_int_PutChar(tVTerm *Term, Uint32 Ch) - * \brief Write a single character to a VTerm - */ -void VT_int_PutChar(tVTerm *Term, Uint32 Ch) -{ - int i; - tVT_Char *buffer; - int write_pos; - - if(Term->Flags & VT_FLAG_ALTBUF) { - buffer = Term->AltBuf; - write_pos = Term->AltWritePos; - } - else { - buffer = Term->Text; - write_pos = Term->WritePos; - } - - switch(Ch) - { - case '\0': return; // Ignore NULL byte - case '\n': - VT_int_UpdateScreen( Term, 0 ); // Update the line before newlining - write_pos += Term->TextWidth; - case '\r': - write_pos -= write_pos % Term->TextWidth; - break; - - case '\t': { int tmp = write_pos / Term->TextWidth; - write_pos %= Term->TextWidth; - do { - buffer[ write_pos ].Ch = '\0'; - buffer[ write_pos ].Colour = Term->CurColour; - write_pos ++; - } while(write_pos & 7); - write_pos += tmp * Term->TextWidth; - break; } - - case '\b': - // Backspace is invalid at Offset 0 - if(write_pos == 0) break; - - write_pos --; - // Singe Character - if(buffer[ write_pos ].Ch != '\0') { - buffer[ write_pos ].Ch = 0; - buffer[ write_pos ].Colour = Term->CurColour; - break; - } - // Tab - i = 7; // Limit it to 8 - do { - buffer[ write_pos ].Ch = 0; - buffer[ write_pos ].Colour = Term->CurColour; - write_pos --; - } while(write_pos && i-- && buffer[ write_pos ].Ch == '\0'); - if(buffer[ write_pos ].Ch != '\0') - write_pos ++; - break; - - default: - buffer[ write_pos ].Ch = Ch; - buffer[ write_pos ].Colour = Term->CurColour; - // Update the line before wrapping - if( (write_pos + 1) % Term->TextWidth == 0 ) - VT_int_UpdateScreen( Term, 0 ); - write_pos ++; - break; - } - - if(Term->Flags & VT_FLAG_ALTBUF) - { - Term->AltBuf = buffer; - Term->AltWritePos = write_pos; - - if(Term->AltWritePos >= Term->TextWidth*Term->TextHeight) - { - Term->AltWritePos -= Term->TextWidth; - VT_int_ScrollText(Term, 1); - } - - } - else - { - Term->Text = buffer; - Term->WritePos = write_pos; - // Move Screen - // - Check if we need to scroll the entire scrollback buffer - if(Term->WritePos >= Term->TextWidth*Term->TextHeight*(giVT_Scrollback+1)) - { - int base; - - // Update previous line - Term->WritePos -= Term->TextWidth; - VT_int_UpdateScreen( Term, 0 ); - - // Update view position - base = Term->TextWidth*Term->TextHeight*(giVT_Scrollback); - if(Term->ViewPos < base) - Term->ViewPos += Term->Width; - if(Term->ViewPos > base) - Term->ViewPos = base; - - VT_int_ScrollText(Term, 1); - } - // Ok, so we only need to scroll the screen - else if(Term->WritePos >= Term->ViewPos + Term->TextWidth*Term->TextHeight) - { - // Update the last line - Term->WritePos -= Term->TextWidth; - VT_int_UpdateScreen( Term, 0 ); - Term->WritePos += Term->TextWidth; - - VT_int_ScrollText(Term, 1); - - Term->ViewPos += Term->TextWidth; - } - } - - //LEAVE('-'); -} - -void VT_int_ScrollText(tVTerm *Term, int Count) -{ - tVT_Char *buf; - int height, init_write_pos; - int len, i; - int scroll_top, scroll_height; - - // Get buffer pointer and attributes - if( Term->Flags & VT_FLAG_ALTBUF ) - { - buf = Term->AltBuf; - height = Term->TextHeight; - init_write_pos = Term->AltWritePos; - scroll_top = Term->ScrollTop; - scroll_height = Term->ScrollHeight; - } - else - { - buf = Term->Text; - height = Term->TextHeight*(giVT_Scrollback+1); - init_write_pos = Term->WritePos; - scroll_top = 0; - scroll_height = height; - } - - // Scroll text downwards - if( Count > 0 ) - { - int base; - - // Set up - if(Count > scroll_height) Count = scroll_height; - base = Term->TextWidth*(scroll_top + scroll_height - Count); - len = Term->TextWidth*(scroll_height - Count); - - // Scroll terminal cache - memmove( - &buf[Term->TextWidth*scroll_top], - &buf[Term->TextWidth*(scroll_top+Count)], - len*sizeof(tVT_Char) - ); - // Clear last rows - for( i = 0; i < Term->TextWidth*Count; i ++ ) - { - buf[ base + i ].Ch = 0; - buf[ base + i ].Colour = Term->CurColour; - } - - // Update Screen - VT_int_ScrollFramebuffer( Term, Count ); - if( Term->Flags & VT_FLAG_ALTBUF ) - Term->AltWritePos = base; - else - Term->WritePos = Term->ViewPos + Term->TextWidth*(Term->TextHeight - Count); - for( i = 0; i < Count; i ++ ) - { - VT_int_UpdateScreen( Term, 0 ); - if( Term->Flags & VT_FLAG_ALTBUF ) - Term->AltWritePos += Term->TextWidth; - else - Term->WritePos += Term->TextWidth; - } - } - else - { - Count = -Count; - if(Count > scroll_height) Count = scroll_height; - - len = Term->TextWidth*(scroll_height - Count); - - // Scroll terminal cache - memmove( - &buf[Term->TextWidth*(scroll_top+Count)], - &buf[Term->TextWidth*scroll_top], - len*sizeof(tVT_Char) - ); - // Clear preceding rows - for( i = 0; i < Term->TextWidth*Count; i ++ ) - { - buf[ i ].Ch = 0; - buf[ i ].Colour = Term->CurColour; - } - - VT_int_ScrollFramebuffer( Term, -Count ); - if( Term->Flags & VT_FLAG_ALTBUF ) - Term->AltWritePos = Term->TextWidth*scroll_top; - else - Term->WritePos = Term->ViewPos; - for( i = 0; i < Count; i ++ ) - { - VT_int_UpdateScreen( Term, 0 ); - if( Term->Flags & VT_FLAG_ALTBUF ) - Term->AltWritePos += Term->TextWidth; - else - Term->WritePos += Term->TextWidth; - } - } - - if( Term->Flags & VT_FLAG_ALTBUF ) - Term->AltWritePos = init_write_pos; - else - Term->WritePos = init_write_pos; -} - -/** - * \fn void VT_int_ScrollFramebuffer( tVTerm *Term, int Count ) - * \note Scrolls the framebuffer down by \a Count text lines - */ -void VT_int_ScrollFramebuffer( tVTerm *Term, int Count ) -{ - 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; - - if( Count > Term->ScrollHeight ) Count = Term->ScrollHeight; - if( Count < -Term->ScrollHeight ) Count = -Term->ScrollHeight; - - // Switch to 2D Command Stream - tmp = VIDEO_BUFFMT_2DSTREAM; - VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &tmp); - - // BLIT to 0,0 from 0,giVT_CharHeight - buf.Op = VIDEO_2DOP_BLIT; - buf.SrcX = 0; buf.DstX = 0; - // TODO: Don't assume character dimensions - buf.W = Term->TextWidth * giVT_CharWidth; - if( Count > 0 ) - { - buf.SrcY = (Term->ScrollTop+Count) * giVT_CharHeight; - buf.DstY = Term->ScrollTop * giVT_CharHeight; - } - else // Scroll up, move text down - { - Count = -Count; - buf.SrcY = Term->ScrollTop * giVT_CharHeight; - buf.DstY = (Term->ScrollTop+Count) * giVT_CharHeight; - } - buf.H = (Term->ScrollHeight-Count) * 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); -} - -void VT_int_UpdateCursor( tVTerm *Term, int bShow ) -{ - tVideo_IOCtl_Pos csr_pos; - - if( Term != gpVT_CurTerm ) return ; - - if( !bShow ) - { - csr_pos.x = -1; - csr_pos.y = -1; - } - else if( Term->Mode == TERM_MODE_TEXT ) - { - int offset; - -// if( !(Term->Flags & VT_FLAG_SHOWCSR) -// && ( (Term->Flags & VT_FLAG_HIDECSR) || !Term->Node.ReadThreads) -// ) - if( !Term->Text || Term->Flags & VT_FLAG_HIDECSR ) - { - csr_pos.x = -1; - csr_pos.y = -1; - } - else - { - if(Term->Flags & VT_FLAG_ALTBUF) - offset = Term->AltWritePos; - else - offset = Term->WritePos - Term->ViewPos; - - csr_pos.x = offset % Term->TextWidth; - csr_pos.y = offset / Term->TextWidth; - if( 0 > csr_pos.y || csr_pos.y >= Term->TextHeight ) - csr_pos.y = -1, csr_pos.x = -1; - } - } - else - { - csr_pos.x = Term->VideoCursorX; - csr_pos.y = Term->VideoCursorY; - } - VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSOR, &csr_pos); -} - -/** - * \fn void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ) - * \brief Updates the video framebuffer - */ -void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ) -{ - tVT_Char *buffer; - int view_pos, write_pos; - // Only update if this is the current terminal - if( Term != gpVT_CurTerm ) return; - - switch( Term->Mode ) - { - case TERM_MODE_TEXT: - view_pos = (Term->Flags & VT_FLAG_ALTBUF) ? 0 : Term->ViewPos; - write_pos = (Term->Flags & VT_FLAG_ALTBUF) ? Term->AltWritePos : Term->WritePos; - buffer = (Term->Flags & VT_FLAG_ALTBUF) ? Term->AltBuf : Term->Text; - // Re copy the entire screen? - if(UpdateAll) { - VFS_WriteAt( - giVT_OutputDevHandle, - 0, - Term->TextWidth*Term->TextHeight*sizeof(tVT_Char), - &buffer[view_pos] - ); - } - // Only copy the current line - else { - int ofs = write_pos - write_pos % Term->TextWidth; - VFS_WriteAt( - giVT_OutputDevHandle, - (ofs - view_pos)*sizeof(tVT_Char), - Term->TextWidth*sizeof(tVT_Char), - &buffer[ofs] - ); - } - break; - case TERM_MODE_FB: - break; - } - - VT_int_UpdateCursor(Term, 1); -} - -/** - * \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, int NewWidth, int NewHeight) -{ - int oldW = Term->Width; - int oldTW = Term->TextWidth; - int oldH = Term->Height; - int oldTH = Term->TextHeight; - 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; - - Term->Mode = NewMode; - - // Fast exit if no resolution change - if(NewWidth == Term->Width && NewHeight == Term->Height) - return ; - - // Calculate new dimensions - Term->Width = NewWidth; - Term->Height = NewHeight; - Term->TextWidth = NewWidth / giVT_CharWidth; - Term->TextHeight = NewHeight / giVT_CharHeight; - Term->ScrollHeight = Term->TextHeight - (oldTH - Term->ScrollHeight) - Term->ScrollTop; - - // Allocate new buffers - // - Text - Term->Text = calloc( - Term->TextWidth * Term->TextHeight * (giVT_Scrollback+1), - sizeof(tVT_Char) - ); - if(oldTBuf) { - // Copy old buffer - w = (oldTW > Term->TextWidth) ? Term->TextWidth : oldTW; - h = (oldTH > Term->TextHeight) ? Term->TextHeight : oldTH; - h *= giVT_Scrollback + 1; - for( i = 0; i < h; i ++ ) - { - memcpy( - &Term->Text[i*Term->TextWidth], - &oldTBuf[i*oldTW], - w*sizeof(tVT_Char) - ); - } - free(oldTBuf); - } - - // - Alternate Text - Term->AltBuf = realloc( - Term->AltBuf, - Term->TextWidth * Term->TextHeight * sizeof(tVT_Char) - ); - - // - Framebuffer - if(oldFB) { - Term->Buffer = calloc( Term->Width * Term->Height, sizeof(Uint32) ); - // Copy old buffer - w = (oldW > Term->Width) ? Term->Width : oldW; - h = (oldH > Term->Height) ? Term->Height : oldH; - for( i = 0; i < h; i ++ ) - { - memcpy( - &Term->Buffer[i*Term->Width], - &oldFB[i*oldW], - w*sizeof(Uint32) - ); - } - free(oldFB); - } - - // Debug - switch(NewMode) - { - case TERM_MODE_TEXT: - Log_Log("VTerm", "Set VT %p to text mode (%ix%i)", - Term, Term->TextWidth, Term->TextHeight); - break; - case TERM_MODE_FB: - Log_Log("VTerm", "Set VT %p to framebuffer mode (%ix%i)", - Term, Term->Width, Term->Height); - break; - //case TERM_MODE_2DACCEL: - //case TERM_MODE_3DACCEL: - // return; - } -} - - -void VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled) -{ - if(Enabled) - Term->Flags |= VT_FLAG_ALTBUF; - else - Term->Flags &= ~VT_FLAG_ALTBUF; - VT_int_UpdateScreen(Term, 1); -} - -// --- -// 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 - -// === PROTOTYPES === -Uint8 *VT_Font_GetChar(Uint32 Codepoint); - -// === GLOBALS === -int giVT_CharWidth = FONT_WIDTH; -int giVT_CharHeight = FONT_HEIGHT; - -// === CODE === -/** - * \brief Render a font character - */ -void VT_Font_Render(Uint32 Codepoint, void *Buffer, int Depth, int Pitch, Uint32 BGC, Uint32 FGC) -{ - Uint8 *font; - int x, y; - - // 8-bpp and below - if( Depth <= 8 ) - { - Uint8 *buf = Buffer; - - 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-1))) - buf[x] = FGC; - else - buf[x] = BGC; - } - buf = (void*)( (tVAddr)buf + Pitch ); - font ++; - } - } - // 16-bpp and below - else if( Depth <= 16 ) - { - Uint16 *buf = Buffer; - - 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-1))) - buf[x] = FGC; - else - buf[x] = BGC; - } - buf = (void*)( (tVAddr)buf + Pitch ); - font ++; - } - } - // 24-bpp colour - // - Special handling to not overwrite the next pixel - //TODO: Endian issues here - else if( Depth == 24 ) - { - Uint8 *buf = Buffer; - Uint8 bg_r = (BGC >> 16) & 0xFF; - Uint8 bg_g = (BGC >> 8) & 0xFF; - Uint8 bg_b = (BGC >> 0) & 0xFF; - Uint8 fg_r = (FGC >> 16) & 0xFF; - Uint8 fg_g = (FGC >> 8) & 0xFF; - Uint8 fg_b = (FGC >> 0) & 0xFF; - - font = VT_Font_GetChar(Codepoint); - - for(y = 0; y < FONT_HEIGHT; y ++) - { - for(x = 0; x < FONT_WIDTH; x ++) - { - Uint8 r, g, b; - - if(*font & (1 << (FONT_WIDTH-x-1))) { - r = fg_r; g = fg_g; b = fg_b; - } - else { - r = bg_r; g = bg_g; b = bg_b; - } - buf[x*3+0] = b; - buf[x*3+1] = g; - buf[x*3+2] = r; - } - buf = (void*)( (tVAddr)buf + Pitch ); - font ++; - } - } - // 32-bpp colour (nice and easy) - else if( Depth == 32 ) - { - Uint32 *buf = Buffer; - - 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-1))) - buf[x] = FGC; - else - buf[x] = BGC; - } - buf = (Uint32*)( (tVAddr)buf + Pitch ); - font ++; - } - } -} - -/** - * \fn Uint32 VT_Colour12to24(Uint16 Col12) - * \brief Converts a 12-bit colour into 24 bits - */ -Uint32 VT_Colour12to24(Uint16 Col12) -{ - Uint32 ret; - int tmp; - tmp = Col12 & 0xF; - ret = (tmp << 0) | (tmp << 4); - tmp = (Col12 & 0xF0) >> 4; - ret |= (tmp << 8) | (tmp << 12); - tmp = (Col12 & 0xF00) >> 8; - ret |= (tmp << 16) | (tmp << 20); - return ret; -} -/** - * \brief Converts a 12-bit colour into 15 bits - */ -Uint16 VT_Colour12to15(Uint16 Col12) -{ - Uint32 ret; - int tmp; - tmp = Col12 & 0xF; - ret = (tmp << 1) | (tmp & 1); - tmp = (Col12 & 0xF0) >> 4; - ret |= ( (tmp << 1) | (tmp & 1) ) << 5; - tmp = (Col12 & 0xF00) >> 8; - ret |= ( (tmp << 1) | (tmp & 1) ) << 10; - return ret; -} - -/** - * \brief Converts a 12-bit colour into any other depth - * \param Col12 12-bit source colour - * \param Depth Desired bit deptj - * \note Green then blue get the extra avaliable bits (16:5-6-5, 14:4-5-5) - */ -Uint32 VT_Colour12toN(Uint16 Col12, int Depth) -{ - Uint32 ret; - Uint32 r, g, b; - int rSize, gSize, bSize; - - // Fast returns - if( Depth == 24 ) return VT_Colour12to24(Col12); - if( Depth == 15 ) return VT_Colour12to15(Col12); - // - 32 is a special case, it's usually 24-bit colour with an unused byte - if( Depth == 32 ) return VT_Colour12to24(Col12); - - // Bounds checks - if( Depth < 8 ) return 0; - if( Depth > 32 ) return 0; - - r = Col12 & 0xF; - g = (Col12 & 0xF0) >> 4; - b = (Col12 & 0xF00) >> 8; - - rSize = gSize = bSize = Depth / 3; - if( rSize + gSize + bSize < Depth ) // Depth % 3 == 1 - gSize ++; - if( rSize + gSize + bSize < Depth ) // Depth % 3 == 2 - bSize ++; - - // Expand - r <<= rSize - 4; g <<= gSize - 4; b <<= bSize - 4; - // Fill with the lowest bit - if( Col12 & 0x001 ) r |= (1 << (rSize - 4)) - 1; - if( Col12 & 0x010 ) r |= (1 << (gSize - 4)) - 1; - if( Col12 & 0x100 ) r |= (1 << (bSize - 4)) - 1; - - // Create output - ret = r; - ret |= g << rSize; - ret |= b << (rSize + gSize); - - return ret; -} - -/** - * \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]; -} - -EXPORTAS(&giVT_CharWidth, giVT_CharWidth); -EXPORTAS(&giVT_CharHeight, giVT_CharHeight); -EXPORT(VT_Font_Render); -EXPORT(VT_Colour12to24); diff --git a/Kernel/drv/vterm.h b/Kernel/drv/vterm.h new file mode 100644 index 00000000..cd37ebd9 --- /dev/null +++ b/Kernel/drv/vterm.h @@ -0,0 +1,122 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * drv/vterm.h + * - Virtual Terminal - Common + */ +#ifndef _VTERM_H_ +#define _VTERM_H_ + +#include +#include // tVT_Char +#include +#include + +// === CONSTANTS === +#define MAX_INPUT_CHARS32 64 +#define MAX_INPUT_CHARS8 (MAX_INPUT_CHARS32*4) +#define DEFAULT_COLOUR (VT_COL_BLACK|(0xAAA<<16)) + +/** + * \{ + */ +#define VT_FLAG_HIDECSR 0x01 //!< Hide the cursor +#define VT_FLAG_ALTBUF 0x02 //!< Alternate screen buffer +#define VT_FLAG_RAWIN 0x04 //!< Don't handle ^Z/^C/^V +#define VT_FLAG_HASFB 0x10 //!< Set if the VTerm has requested the Framebuffer +#define VT_FLAG_SHOWCSR 0x20 //!< Always show the text cursor +/** + * \} + */ + +enum eVT_InModes { + VT_INMODE_TEXT8, // UTF-8 Text Mode (VT100/xterm Emulation) + VT_INMODE_TEXT32, // UTF-32 Text Mode (Acess Native) + NUM_VT_INMODES +}; + + +// === TYPES == +typedef struct sVTerm tVTerm; + +// === STRUCTURES === +struct sVTerm +{ + 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 TextWidth; //!< Text Virtual Width + short TextHeight; //!< Text Virtual Height + + Uint32 CurColour; //!< Current Text Colour + + int ViewPos; //!< View Buffer Offset (Text Only) + int WritePos; //!< Write Buffer Offset (Text Only) + tVT_Char *Text; + + tVT_Char *AltBuf; //!< Alternate Screen Buffer + int AltWritePos; //!< Alternate write position + short ScrollTop; //!< Top of scrolling region (smallest) + short ScrollHeight; //!< Length of scrolling region + + int VideoCursorX; + int VideoCursorY; + + 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]; +// tSemaphore InputSemaphore; + + Uint32 *Buffer; + + // TODO: Do I need to keep this about? + // When should it be deallocated? on move to text mode, or some other time + // Call set again, it's freed, and if NULL it doesn't get reallocated. + tVideo_IOCtl_Bitmap *VideoCursor; + + char Name[2]; //!< Name of the terminal + tVFS_Node Node; +}; + +// === GOBALS === +extern tVTerm *gpVT_CurTerm; +extern int giVT_Scrollback; +extern short giVT_RealWidth; //!< Screen Width +extern short giVT_RealHeight; //!< Screen Width +extern char *gsVT_OutputDevice; +extern char *gsVT_InputDevice; +extern int giVT_OutputDevHandle; +extern int giVT_InputDevHandle; + +// === FUNCTIONS === +extern void VT_SetResolution(int Width, int Height); +extern void VT_SetTerminal(int ID); +// --- Output --- +extern void VT_InitOutput(void); +extern void VT_SetMode(int Mode); +extern void VT_int_ScrollFramebuffer( tVTerm *Term, int Count ); +extern void VT_int_UpdateCursor( tVTerm *Term, int bShow ); +extern void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ); +// --- Input --- +extern void VT_InitInput(void); +extern void VT_KBCallBack(Uint32 Codepoint); +// --- VT100 Emulation --- +extern void VT_int_ParseEscape_StandardLarge(tVTerm *Term, char CmdChar, int argc, int *args); +extern int VT_int_ParseEscape(tVTerm *Term, const char *Buffer); +// --- Terminal Buffer --- +extern void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count); +extern void VT_int_PutChar(tVTerm *Term, Uint32 Ch); +extern void VT_int_ScrollText(tVTerm *Term, int Count); +extern void VT_int_ClearLine(tVTerm *Term, int Num); +extern void VT_int_ChangeMode(tVTerm *Term, int NewMode, int NewWidth, int NewHeight); +extern void VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled); + +#endif + diff --git a/Kernel/drv/vterm_font.c b/Kernel/drv/vterm_font.c new file mode 100644 index 00000000..841ea504 --- /dev/null +++ b/Kernel/drv/vterm_font.c @@ -0,0 +1,250 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * drv/vterm_font.c + * - Virtual Terminal - Font rendering code + */ +#include "vterm.h" +#include + +// --- +// 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 + +// === PROTOTYPES === +Uint8 *VT_Font_GetChar(Uint32 Codepoint); + +// === GLOBALS === +int giVT_CharWidth = FONT_WIDTH; +int giVT_CharHeight = FONT_HEIGHT; + +// === CODE === +/** + * \brief Render a font character + */ +void VT_Font_Render(Uint32 Codepoint, void *Buffer, int Depth, int Pitch, Uint32 BGC, Uint32 FGC) +{ + Uint8 *font; + int x, y; + + // 8-bpp and below + if( Depth <= 8 ) + { + Uint8 *buf = Buffer; + + 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-1))) + buf[x] = FGC; + else + buf[x] = BGC; + } + buf = (void*)( (tVAddr)buf + Pitch ); + font ++; + } + } + // 16-bpp and below + else if( Depth <= 16 ) + { + Uint16 *buf = Buffer; + + 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-1))) + buf[x] = FGC; + else + buf[x] = BGC; + } + buf = (void*)( (tVAddr)buf + Pitch ); + font ++; + } + } + // 24-bpp colour + // - Special handling to not overwrite the next pixel + //TODO: Endian issues here + else if( Depth == 24 ) + { + Uint8 *buf = Buffer; + Uint8 bg_r = (BGC >> 16) & 0xFF; + Uint8 bg_g = (BGC >> 8) & 0xFF; + Uint8 bg_b = (BGC >> 0) & 0xFF; + Uint8 fg_r = (FGC >> 16) & 0xFF; + Uint8 fg_g = (FGC >> 8) & 0xFF; + Uint8 fg_b = (FGC >> 0) & 0xFF; + + font = VT_Font_GetChar(Codepoint); + + for(y = 0; y < FONT_HEIGHT; y ++) + { + for(x = 0; x < FONT_WIDTH; x ++) + { + Uint8 r, g, b; + + if(*font & (1 << (FONT_WIDTH-x-1))) { + r = fg_r; g = fg_g; b = fg_b; + } + else { + r = bg_r; g = bg_g; b = bg_b; + } + buf[x*3+0] = b; + buf[x*3+1] = g; + buf[x*3+2] = r; + } + buf = (void*)( (tVAddr)buf + Pitch ); + font ++; + } + } + // 32-bpp colour (nice and easy) + else if( Depth == 32 ) + { + Uint32 *buf = Buffer; + + 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-1))) + buf[x] = FGC; + else + buf[x] = BGC; + } + buf = (Uint32*)( (tVAddr)buf + Pitch ); + font ++; + } + } +} + +/** + * \fn Uint32 VT_Colour12to24(Uint16 Col12) + * \brief Converts a 12-bit colour into 24 bits + */ +Uint32 VT_Colour12to24(Uint16 Col12) +{ + Uint32 ret; + int tmp; + tmp = Col12 & 0xF; + ret = (tmp << 0) | (tmp << 4); + tmp = (Col12 & 0xF0) >> 4; + ret |= (tmp << 8) | (tmp << 12); + tmp = (Col12 & 0xF00) >> 8; + ret |= (tmp << 16) | (tmp << 20); + return ret; +} +/** + * \brief Converts a 12-bit colour into 15 bits + */ +Uint16 VT_Colour12to15(Uint16 Col12) +{ + Uint32 ret; + int tmp; + tmp = Col12 & 0xF; + ret = (tmp << 1) | (tmp & 1); + tmp = (Col12 & 0xF0) >> 4; + ret |= ( (tmp << 1) | (tmp & 1) ) << 5; + tmp = (Col12 & 0xF00) >> 8; + ret |= ( (tmp << 1) | (tmp & 1) ) << 10; + return ret; +} + +/** + * \brief Converts a 12-bit colour into any other depth + * \param Col12 12-bit source colour + * \param Depth Desired bit deptj + * \note Green then blue get the extra avaliable bits (16:5-6-5, 14:4-5-5) + */ +Uint32 VT_Colour12toN(Uint16 Col12, int Depth) +{ + Uint32 ret; + Uint32 r, g, b; + int rSize, gSize, bSize; + + // Fast returns + if( Depth == 24 ) return VT_Colour12to24(Col12); + if( Depth == 15 ) return VT_Colour12to15(Col12); + // - 32 is a special case, it's usually 24-bit colour with an unused byte + if( Depth == 32 ) return VT_Colour12to24(Col12); + + // Bounds checks + if( Depth < 8 ) return 0; + if( Depth > 32 ) return 0; + + r = Col12 & 0xF; + g = (Col12 & 0xF0) >> 4; + b = (Col12 & 0xF00) >> 8; + + rSize = gSize = bSize = Depth / 3; + if( rSize + gSize + bSize < Depth ) // Depth % 3 == 1 + gSize ++; + if( rSize + gSize + bSize < Depth ) // Depth % 3 == 2 + bSize ++; + + // Expand + r <<= rSize - 4; g <<= gSize - 4; b <<= bSize - 4; + // Fill with the lowest bit + if( Col12 & 0x001 ) r |= (1 << (rSize - 4)) - 1; + if( Col12 & 0x010 ) r |= (1 << (gSize - 4)) - 1; + if( Col12 & 0x100 ) r |= (1 << (bSize - 4)) - 1; + + // Create output + ret = r; + ret |= g << rSize; + ret |= b << (rSize + gSize); + + return ret; +} + +/** + * \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]; +} + +EXPORTAS(&giVT_CharWidth, giVT_CharWidth); +EXPORTAS(&giVT_CharHeight, giVT_CharHeight); +EXPORT(VT_Font_Render); +EXPORT(VT_Colour12to24); diff --git a/Kernel/drv/vterm_input.c b/Kernel/drv/vterm_input.c new file mode 100644 index 00000000..d83cedee --- /dev/null +++ b/Kernel/drv/vterm_input.c @@ -0,0 +1,232 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * drv/vterm_input.c + * - Virtual Terminal - Input code + */ +#include "vterm.h" +#include + +// === GLOBALS === +// --- Key States --- (Used for VT Switching/Magic Combos) + int gbVT_CtrlDown = 0; + int gbVT_AltDown = 0; + int gbVT_SysrqDown = 0; + +// === CODE === +/** + * \fn void VT_InitInput() + * \brief Initialises the input + */ +void VT_InitInput() +{ + giVT_InputDevHandle = VFS_Open(gsVT_InputDevice, VFS_OPENFLAG_READ); + if(giVT_InputDevHandle == -1) { + Log_Warning("VTerm", "Can't open the input device '%s'", gsVT_InputDevice); + return ; + } + VFS_IOCtl(giVT_InputDevHandle, KB_IOCTL_SETCALLBACK, VT_KBCallBack); +} + +/** + * \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 = gpVT_CurTerm; + + // Catch VT binds + switch( Codepoint & KEY_ACTION_MASK ) + { + case KEY_ACTION_RELEASE: + switch(Codepoint & KEY_CODEPOINT_MASK) + { + 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; + } + break; + + case KEY_ACTION_PRESS: + switch(Codepoint & KEY_CODEPOINT_MASK) + { + 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; + } + + if(!gbVT_AltDown || !gbVT_CtrlDown) + break; + switch(Codepoint & KEY_CODEPOINT_MASK) + { + case KEY_F1: VT_SetTerminal(0); return; + case KEY_F2: VT_SetTerminal(1); return; + case KEY_F3: VT_SetTerminal(2); return; + case KEY_F4: VT_SetTerminal(3); return; + case KEY_F5: VT_SetTerminal(4); return; + case KEY_F6: VT_SetTerminal(5); return; + case KEY_F7: VT_SetTerminal(6); return; + case KEY_F8: VT_SetTerminal(7); return; + case KEY_F9: VT_SetTerminal(8); return; + case KEY_F10: VT_SetTerminal(9); return; + case KEY_F11: VT_SetTerminal(10); return; + case KEY_F12: VT_SetTerminal(11); return; + } + + // Scrolling is only valid in text mode + if(gpVT_CurTerm->Mode != TERM_MODE_TEXT) + break; + + switch(Codepoint & KEY_CODEPOINT_MASK) + { + // Scrolling + case KEY_PGUP: + if( gpVT_CurTerm->Flags & VT_FLAG_ALTBUF ) + return ; + gpVT_CurTerm->ViewPos = MAX( + 0, + gpVT_CurTerm->ViewPos - gpVT_CurTerm->Width + ); + return; + case KEY_PGDOWN: + if( gpVT_CurTerm->Flags & VT_FLAG_ALTBUF ) + return ; + gpVT_CurTerm->ViewPos = MIN( + gpVT_CurTerm->ViewPos + gpVT_CurTerm->Width, + gpVT_CurTerm->Width * gpVT_CurTerm->Height*giVT_Scrollback + ); + return; + } + break; + } + + // Encode key + if(term->Mode == TERM_MODE_TEXT) + { + Uint8 buf[6] = {0}; + int len = 0; + + // Ignore anything that isn't a press or refire + if( (Codepoint & KEY_ACTION_MASK) != KEY_ACTION_PRESS + && (Codepoint & KEY_ACTION_MASK) != KEY_ACTION_REFIRE + ) + { + return ; + } + + Codepoint &= KEY_CODEPOINT_MASK; + + // Ignore Modifer Keys + if(Codepoint > KEY_MODIFIERS) return; + + // Get UTF-8/ANSI Encoding + switch(Codepoint) + { + // 0: No translation, don't send to user + case 0: break; + case KEY_LEFT: + buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'D'; + len = 3; + break; + case KEY_RIGHT: + 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'; buf[3] = '~'; + len = 4; + break; + case KEY_PGDOWN: + buf[0] = '\x1B'; buf[1] = '['; buf[2] = '6'; buf[3] = '~'; + len = 4; + break; + + // Attempt to encode in UTF-8 + default: + len = WriteUTF8( buf, Codepoint ); + if(len == 0) { + Warning("Codepoint (%x) is unrepresentable in UTF-8", Codepoint); + } + break; + } + + if(len == 0) { + // Unprintable / Don't Pass + return; + } + +#if 0 + // Handle meta characters + if( !(term->Flags & VT_FLAG_RAWIN) ) + { + switch(buf[0]) + { + case '\3': // ^C + + break; + } + } +#endif + + // Write + if( MAX_INPUT_CHARS8 - term->InputWrite >= len ) + memcpy( &term->InputBuffer[term->InputWrite], buf, len ); + else { + memcpy( &term->InputBuffer[term->InputWrite], buf, MAX_INPUT_CHARS8 - term->InputWrite ); + memcpy( &term->InputBuffer[0], buf, len - (MAX_INPUT_CHARS8 - term->InputWrite) ); + } + // Roll the buffer over + term->InputWrite += len; + term->InputWrite %= MAX_INPUT_CHARS8; + if( (term->InputWrite - term->InputRead + MAX_INPUT_CHARS8)%MAX_INPUT_CHARS8 < len ) { + term->InputRead = term->InputWrite + 1; + term->InputRead %= MAX_INPUT_CHARS8; + } + } + else + { + // Encode the raw key event + Uint32 *raw_in = (void*)term->InputBuffer; + + #if 0 + // Drop new keys + if( term->InputWrite == term->InputRead ) + return ; + #endif + + raw_in[ term->InputWrite ] = Codepoint; + term->InputWrite ++; + if(term->InputWrite >= MAX_INPUT_CHARS32) + term->InputWrite -= MAX_INPUT_CHARS32; + + #if 1 + // TODO: Should old or new be dropped? + if(term->InputRead == term->InputWrite) { + term->InputRead ++; + if( term->InputRead >= MAX_INPUT_CHARS32 ) + term->InputRead -= MAX_INPUT_CHARS32; + } + #endif + } + + VFS_MarkAvaliable(&term->Node, 1); +} + diff --git a/Kernel/drv/vterm_output.c b/Kernel/drv/vterm_output.c new file mode 100644 index 00000000..95a2bce8 --- /dev/null +++ b/Kernel/drv/vterm_output.c @@ -0,0 +1,171 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * drv/vterm_input.c + * - Virtual Terminal - Input code + */ +#include "vterm.h" +#include + +// === CODE === +/** + * \fn void VT_InitOutput() + * \brief Initialise Video Output + */ +void VT_InitOutput() +{ + giVT_OutputDevHandle = VFS_Open(gsVT_OutputDevice, VFS_OPENFLAG_WRITE); + if(giVT_OutputDevHandle == -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 ); +} + +/** + * \brief Set video output buffer mode + */ +void VT_SetMode(int Mode) +{ + VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &Mode ); +} + +/** + * \fn void VT_int_ScrollFramebuffer( tVTerm *Term, int Count ) + * \note Scrolls the framebuffer down by \a Count text lines + */ +void VT_int_ScrollFramebuffer( tVTerm *Term, int Count ) +{ + 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; + + if( Count > Term->ScrollHeight ) Count = Term->ScrollHeight; + if( Count < -Term->ScrollHeight ) Count = -Term->ScrollHeight; + + // Switch to 2D Command Stream + tmp = VIDEO_BUFFMT_2DSTREAM; + VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &tmp); + + // BLIT to 0,0 from 0,giVT_CharHeight + buf.Op = VIDEO_2DOP_BLIT; + buf.SrcX = 0; buf.DstX = 0; + // TODO: Don't assume character dimensions + buf.W = Term->TextWidth * giVT_CharWidth; + if( Count > 0 ) + { + buf.SrcY = (Term->ScrollTop+Count) * giVT_CharHeight; + buf.DstY = Term->ScrollTop * giVT_CharHeight; + } + else // Scroll up, move text down + { + Count = -Count; + buf.SrcY = Term->ScrollTop * giVT_CharHeight; + buf.DstY = (Term->ScrollTop+Count) * giVT_CharHeight; + } + buf.H = (Term->ScrollHeight-Count) * 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); +} + +void VT_int_UpdateCursor( tVTerm *Term, int bShow ) +{ + tVideo_IOCtl_Pos csr_pos; + + if( Term != gpVT_CurTerm ) return ; + + if( !bShow ) + { + csr_pos.x = -1; + csr_pos.y = -1; + } + else if( Term->Mode == TERM_MODE_TEXT ) + { + int offset; + +// if( !(Term->Flags & VT_FLAG_SHOWCSR) +// && ( (Term->Flags & VT_FLAG_HIDECSR) || !Term->Node.ReadThreads) +// ) + if( !Term->Text || Term->Flags & VT_FLAG_HIDECSR ) + { + csr_pos.x = -1; + csr_pos.y = -1; + } + else + { + if(Term->Flags & VT_FLAG_ALTBUF) + offset = Term->AltWritePos; + else + offset = Term->WritePos - Term->ViewPos; + + csr_pos.x = offset % Term->TextWidth; + csr_pos.y = offset / Term->TextWidth; + if( 0 > csr_pos.y || csr_pos.y >= Term->TextHeight ) + csr_pos.y = -1, csr_pos.x = -1; + } + } + else + { + csr_pos.x = Term->VideoCursorX; + csr_pos.y = Term->VideoCursorY; + } + VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSOR, &csr_pos); +} + +/** + * \fn void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ) + * \brief Updates the video framebuffer + */ +void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ) +{ + tVT_Char *buffer; + int view_pos, write_pos; + // Only update if this is the current terminal + if( Term != gpVT_CurTerm ) return; + + switch( Term->Mode ) + { + case TERM_MODE_TEXT: + view_pos = (Term->Flags & VT_FLAG_ALTBUF) ? 0 : Term->ViewPos; + write_pos = (Term->Flags & VT_FLAG_ALTBUF) ? Term->AltWritePos : Term->WritePos; + buffer = (Term->Flags & VT_FLAG_ALTBUF) ? Term->AltBuf : Term->Text; + // Re copy the entire screen? + if(UpdateAll) { + VFS_WriteAt( + giVT_OutputDevHandle, + 0, + Term->TextWidth*Term->TextHeight*sizeof(tVT_Char), + &buffer[view_pos] + ); + } + // Only copy the current line + else { + int ofs = write_pos - write_pos % Term->TextWidth; + VFS_WriteAt( + giVT_OutputDevHandle, + (ofs - view_pos)*sizeof(tVT_Char), + Term->TextWidth*sizeof(tVT_Char), + &buffer[ofs] + ); + } + break; + case TERM_MODE_FB: + break; + } + + VT_int_UpdateCursor(Term, 1); +} + diff --git a/Kernel/drv/vterm_termbuf.c b/Kernel/drv/vterm_termbuf.c new file mode 100644 index 00000000..2511048c --- /dev/null +++ b/Kernel/drv/vterm_termbuf.c @@ -0,0 +1,398 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * drv/vterm_termbuf.c + * - Virtual Terminal - Terminal buffer manipulation + */ +#include "vterm.h" + +// === CODE === + +/** + * \fn void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count) + * \brief Print a string to the Virtual Terminal + */ +void VT_int_PutString(tVTerm *Term, const 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, (const 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 ); +} + +/** + * \fn void VT_int_PutChar(tVTerm *Term, Uint32 Ch) + * \brief Write a single character to a VTerm + */ +void VT_int_PutChar(tVTerm *Term, Uint32 Ch) +{ + int i; + tVT_Char *buffer; + int write_pos; + + if(Term->Flags & VT_FLAG_ALTBUF) { + buffer = Term->AltBuf; + write_pos = Term->AltWritePos; + } + else { + buffer = Term->Text; + write_pos = Term->WritePos; + } + + switch(Ch) + { + case '\0': return; // Ignore NULL byte + case '\n': + VT_int_UpdateScreen( Term, 0 ); // Update the line before newlining + write_pos += Term->TextWidth; + case '\r': + write_pos -= write_pos % Term->TextWidth; + break; + + case '\t': { int tmp = write_pos / Term->TextWidth; + write_pos %= Term->TextWidth; + do { + buffer[ write_pos ].Ch = '\0'; + buffer[ write_pos ].Colour = Term->CurColour; + write_pos ++; + } while(write_pos & 7); + write_pos += tmp * Term->TextWidth; + break; } + + case '\b': + // Backspace is invalid at Offset 0 + if(write_pos == 0) break; + + write_pos --; + // Singe Character + if(buffer[ write_pos ].Ch != '\0') { + buffer[ write_pos ].Ch = 0; + buffer[ write_pos ].Colour = Term->CurColour; + break; + } + // Tab + i = 7; // Limit it to 8 + do { + buffer[ write_pos ].Ch = 0; + buffer[ write_pos ].Colour = Term->CurColour; + write_pos --; + } while(write_pos && i-- && buffer[ write_pos ].Ch == '\0'); + if(buffer[ write_pos ].Ch != '\0') + write_pos ++; + break; + + default: + buffer[ write_pos ].Ch = Ch; + buffer[ write_pos ].Colour = Term->CurColour; + // Update the line before wrapping + if( (write_pos + 1) % Term->TextWidth == 0 ) + VT_int_UpdateScreen( Term, 0 ); + write_pos ++; + break; + } + + if(Term->Flags & VT_FLAG_ALTBUF) + { + Term->AltBuf = buffer; + Term->AltWritePos = write_pos; + + if(Term->AltWritePos >= Term->TextWidth*Term->TextHeight) + { + Term->AltWritePos -= Term->TextWidth; + VT_int_ScrollText(Term, 1); + } + + } + else + { + Term->Text = buffer; + Term->WritePos = write_pos; + // Move Screen + // - Check if we need to scroll the entire scrollback buffer + if(Term->WritePos >= Term->TextWidth*Term->TextHeight*(giVT_Scrollback+1)) + { + int base; + + // Update previous line + Term->WritePos -= Term->TextWidth; + VT_int_UpdateScreen( Term, 0 ); + + // Update view position + base = Term->TextWidth*Term->TextHeight*(giVT_Scrollback); + if(Term->ViewPos < base) + Term->ViewPos += Term->Width; + if(Term->ViewPos > base) + Term->ViewPos = base; + + VT_int_ScrollText(Term, 1); + } + // Ok, so we only need to scroll the screen + else if(Term->WritePos >= Term->ViewPos + Term->TextWidth*Term->TextHeight) + { + // Update the last line + Term->WritePos -= Term->TextWidth; + VT_int_UpdateScreen( Term, 0 ); + Term->WritePos += Term->TextWidth; + + VT_int_ScrollText(Term, 1); + + Term->ViewPos += Term->TextWidth; + } + } + + //LEAVE('-'); +} + +void VT_int_ScrollText(tVTerm *Term, int Count) +{ + tVT_Char *buf; + int height, init_write_pos; + int len, i; + int scroll_top, scroll_height; + + // Get buffer pointer and attributes + if( Term->Flags & VT_FLAG_ALTBUF ) + { + buf = Term->AltBuf; + height = Term->TextHeight; + init_write_pos = Term->AltWritePos; + scroll_top = Term->ScrollTop; + scroll_height = Term->ScrollHeight; + } + else + { + buf = Term->Text; + height = Term->TextHeight*(giVT_Scrollback+1); + init_write_pos = Term->WritePos; + scroll_top = 0; + scroll_height = height; + } + + // Scroll text downwards + if( Count > 0 ) + { + int base; + + // Set up + if(Count > scroll_height) Count = scroll_height; + base = Term->TextWidth*(scroll_top + scroll_height - Count); + len = Term->TextWidth*(scroll_height - Count); + + // Scroll terminal cache + memmove( + &buf[Term->TextWidth*scroll_top], + &buf[Term->TextWidth*(scroll_top+Count)], + len*sizeof(tVT_Char) + ); + // Clear last rows + for( i = 0; i < Term->TextWidth*Count; i ++ ) + { + buf[ base + i ].Ch = 0; + buf[ base + i ].Colour = Term->CurColour; + } + + // Update Screen + VT_int_ScrollFramebuffer( Term, Count ); + if( Term->Flags & VT_FLAG_ALTBUF ) + Term->AltWritePos = base; + else + Term->WritePos = Term->ViewPos + Term->TextWidth*(Term->TextHeight - Count); + for( i = 0; i < Count; i ++ ) + { + VT_int_UpdateScreen( Term, 0 ); + if( Term->Flags & VT_FLAG_ALTBUF ) + Term->AltWritePos += Term->TextWidth; + else + Term->WritePos += Term->TextWidth; + } + } + else + { + Count = -Count; + if(Count > scroll_height) Count = scroll_height; + + len = Term->TextWidth*(scroll_height - Count); + + // Scroll terminal cache + memmove( + &buf[Term->TextWidth*(scroll_top+Count)], + &buf[Term->TextWidth*scroll_top], + len*sizeof(tVT_Char) + ); + // Clear preceding rows + for( i = 0; i < Term->TextWidth*Count; i ++ ) + { + buf[ i ].Ch = 0; + buf[ i ].Colour = Term->CurColour; + } + + VT_int_ScrollFramebuffer( Term, -Count ); + if( Term->Flags & VT_FLAG_ALTBUF ) + Term->AltWritePos = Term->TextWidth*scroll_top; + else + Term->WritePos = Term->ViewPos; + for( i = 0; i < Count; i ++ ) + { + VT_int_UpdateScreen( Term, 0 ); + if( Term->Flags & VT_FLAG_ALTBUF ) + Term->AltWritePos += Term->TextWidth; + else + Term->WritePos += Term->TextWidth; + } + } + + if( Term->Flags & VT_FLAG_ALTBUF ) + Term->AltWritePos = init_write_pos; + else + Term->WritePos = init_write_pos; +} + +/** + * \brief Clears a line in a virtual terminal + * \param Term Terminal to modify + * \param Num Line number to clear + */ +void VT_int_ClearLine(tVTerm *Term, int Num) +{ + int i; + tVT_Char *cell; + + if( Num < 0 || Num >= Term->TextHeight * (giVT_Scrollback + 1) ) return ; + + cell = (Term->Flags & VT_FLAG_ALTBUF) ? Term->AltBuf : Term->Text; + cell = &cell[ Num*Term->TextWidth ]; + + for( i = Term->TextWidth; i--; ) + { + cell[ i ].Ch = 0; + cell[ i ].Colour = Term->CurColour; + } +} + +/** + * \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, int NewWidth, int NewHeight) +{ + int oldW = Term->Width; + int oldTW = Term->TextWidth; + int oldH = Term->Height; + int oldTH = Term->TextHeight; + 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; + + Term->Mode = NewMode; + + // Fast exit if no resolution change + if(NewWidth == Term->Width && NewHeight == Term->Height) + return ; + + // Calculate new dimensions + Term->Width = NewWidth; + Term->Height = NewHeight; + Term->TextWidth = NewWidth / giVT_CharWidth; + Term->TextHeight = NewHeight / giVT_CharHeight; + Term->ScrollHeight = Term->TextHeight - (oldTH - Term->ScrollHeight) - Term->ScrollTop; + + // Allocate new buffers + // - Text + Term->Text = calloc( + Term->TextWidth * Term->TextHeight * (giVT_Scrollback+1), + sizeof(tVT_Char) + ); + if(oldTBuf) { + // Copy old buffer + w = (oldTW > Term->TextWidth) ? Term->TextWidth : oldTW; + h = (oldTH > Term->TextHeight) ? Term->TextHeight : oldTH; + h *= giVT_Scrollback + 1; + for( i = 0; i < h; i ++ ) + { + memcpy( + &Term->Text[i*Term->TextWidth], + &oldTBuf[i*oldTW], + w*sizeof(tVT_Char) + ); + } + free(oldTBuf); + } + + // - Alternate Text + Term->AltBuf = realloc( + Term->AltBuf, + Term->TextWidth * Term->TextHeight * sizeof(tVT_Char) + ); + + // - Framebuffer + if(oldFB) { + Term->Buffer = calloc( Term->Width * Term->Height, sizeof(Uint32) ); + // Copy old buffer + w = (oldW > Term->Width) ? Term->Width : oldW; + h = (oldH > Term->Height) ? Term->Height : oldH; + for( i = 0; i < h; i ++ ) + { + memcpy( + &Term->Buffer[i*Term->Width], + &oldFB[i*oldW], + w*sizeof(Uint32) + ); + } + free(oldFB); + } + + // Debug + switch(NewMode) + { + case TERM_MODE_TEXT: + Log_Log("VTerm", "Set VT %p to text mode (%ix%i)", + Term, Term->TextWidth, Term->TextHeight); + break; + case TERM_MODE_FB: + Log_Log("VTerm", "Set VT %p to framebuffer mode (%ix%i)", + Term, Term->Width, Term->Height); + break; + //case TERM_MODE_2DACCEL: + //case TERM_MODE_3DACCEL: + // return; + } +} + + +void VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled) +{ + if(Enabled) + Term->Flags |= VT_FLAG_ALTBUF; + else + Term->Flags &= ~VT_FLAG_ALTBUF; + VT_int_UpdateScreen(Term, 1); +} + diff --git a/Kernel/drv/vterm_vt100.c b/Kernel/drv/vterm_vt100.c new file mode 100644 index 00000000..fb152a30 --- /dev/null +++ b/Kernel/drv/vterm_vt100.c @@ -0,0 +1,295 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * drv/vterm_vt100.c + * - Virtual Terminal - VT100 (Kinda) Emulation + */ +#include "vterm.h" + +// === 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 + }; + +// === CODE === +/** + * \brief Handle a standard large escape code + * + * Handles any escape code of the form \x1B[n,...A where n is an integer + * and A is any letter. + */ +void VT_int_ParseEscape_StandardLarge(tVTerm *Term, char CmdChar, int argc, int *args) +{ + int tmp = 1; + switch(CmdChar) + { + // Left + case 'D': + tmp = -1; + // Right + case 'C': + if(argc == 1) tmp *= args[0]; + if( Term->Flags & VT_FLAG_ALTBUF ) + { + if( (Term->AltWritePos + tmp) % Term->TextWidth == 0 ) { + Term->AltWritePos -= Term->AltWritePos % Term->TextWidth; + Term->AltWritePos += Term->TextWidth - 1; + } + else + Term->AltWritePos += tmp; + } + else + { + if( (Term->WritePos + tmp) % Term->TextWidth == 0 ) { + Term->WritePos -= Term->WritePos % Term->TextWidth; + Term->WritePos += Term->TextWidth - 1; + } + else + Term->WritePos += tmp; + } + break; + + // Erase + case 'J': + switch(args[0]) + { + case 0: // Erase below + break; + case 1: // Erase above + break; + case 2: // Erase all + if( Term->Flags & VT_FLAG_ALTBUF ) + { + int i = Term->TextHeight; + while( i-- ) VT_int_ClearLine(Term, i); + Term->AltWritePos = 0; + VT_int_UpdateScreen(Term, 1); + } + else + { + int i = Term->TextHeight * (giVT_Scrollback + 1); + while( i-- ) VT_int_ClearLine(Term, i); + Term->WritePos = 0; + Term->ViewPos = 0; + VT_int_UpdateScreen(Term, 1); + } + break; + } + break; + + // Erase in line + case 'K': + switch(args[0]) + { + case 0: // Erase to right + if( Term->Flags & VT_FLAG_ALTBUF ) + { + int i, max; + max = Term->Width - Term->AltWritePos % Term->Width; + for( i = 0; i < max; i ++ ) + Term->AltBuf[Term->AltWritePos+i].Ch = 0; + } + else + { + int i, max; + max = Term->Width - Term->WritePos % Term->Width; + for( i = 0; i < max; i ++ ) + Term->Text[Term->WritePos+i].Ch = 0; + } + VT_int_UpdateScreen(Term, 0); + break; + case 1: // Erase to left + if( Term->Flags & VT_FLAG_ALTBUF ) + { + int i = Term->AltWritePos % Term->Width; + while( i -- ) + Term->AltBuf[Term->AltWritePos++].Ch = 0; + } + else + { + int i = Term->WritePos % Term->Width; + while( i -- ) + Term->Text[Term->WritePos++].Ch = 0; + } + VT_int_UpdateScreen(Term, 0); + break; + case 2: // Erase all + if( Term->Flags & VT_FLAG_ALTBUF ) + { + VT_int_ClearLine(Term, Term->AltWritePos / Term->Width); + } + else + { + VT_int_ClearLine(Term, Term->WritePos / Term->Width); + } + VT_int_UpdateScreen(Term, 0); + break; + } + break; + + // Set cursor position + case 'H': + if( Term->Flags & VT_FLAG_ALTBUF ) + Term->AltWritePos = args[0] + args[1]*Term->TextWidth; + else + Term->WritePos = args[0] + args[1]*Term->TextWidth; + //Log_Debug("VTerm", "args = {%i, %i}", args[0], args[1]); + break; + + // Scroll up `n` lines + case 'S': + tmp = -1; + // Scroll down `n` lines + case 'T': + if(argc == 1) tmp *= args[0]; + if( Term->Flags & VT_FLAG_ALTBUF ) + VT_int_ScrollText(Term, tmp); + else + { + if(Term->ViewPos/Term->TextWidth + tmp < 0) + break; + if(Term->ViewPos/Term->TextWidth + tmp > Term->TextHeight * (giVT_Scrollback + 1)) + break; + + Term->ViewPos += Term->TextWidth*tmp; + } + break; + + // Set Font flags + case 'm': + for( ; argc--; ) + { + int colour_idx; + // 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) { + // Get colour index, accounting for bright bit + colour_idx = args[argc]-30 + ((Term->CurColour>>28) & 8); + Term->CurColour &= 0x8000FFFF; + Term->CurColour |= (Uint32)caVT100Colours[ colour_idx ] << 16; + } + // Background Colour + else if(40 <= args[argc] && args[argc] <= 47) { + // Get colour index, accounting for bright bit + colour_idx = args[argc]-40 + ((Term->CurColour>>12) & 8); + Term->CurColour &= 0xFFFF8000; + Term->CurColour |= caVT100Colours[ colour_idx ]; + } + else { + Log_Warning("VTerm", "Unknown font flag %i", args[argc]); + } + } + break; + + // Set scrolling region + case 'r': + if( argc != 2 ) break; + Term->ScrollTop = args[0]; + Term->ScrollHeight = args[1] - args[0]; + break; + + default: + Log_Warning("VTerm", "Unknown control sequence '\\x1B[%c'", CmdChar); + break; + } +} + +/** + * \fn int VT_int_ParseEscape(tVTerm *Term, const char *Buffer) + * \brief Parses a VT100 Escape code + */ +int VT_int_ParseEscape(tVTerm *Term, const char *Buffer) +{ + char c; + int argc = 0, j = 1; + int args[6] = {0,0,0,0}; + int bQuestionMark = 0; + + switch(Buffer[0]) + { + //Large Code + case '[': + // Get Arguments + c = Buffer[j++]; + if(c == '?') { + bQuestionMark = 1; + 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 + if( ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) + { + if( bQuestionMark ) + { + switch(c) + { + // DEC Private Mode Set + case 'h': + if(argc != 1) break; + switch(args[0]) + { + case 25: + Term->Flags &= ~VT_FLAG_HIDECSR; + break; + case 1047: + VT_int_ToggleAltBuffer(Term, 1); + break; + } + break; + case 'l': + if(argc != 1) break; + switch(args[0]) + { + case 25: + Term->Flags |= VT_FLAG_HIDECSR; + break; + case 1047: + VT_int_ToggleAltBuffer(Term, 0); + break; + } + break; + default: + Log_Warning("VTerm", "Unknown control sequence '\\x1B[?%c'", c); + break; + } + } + else + { + VT_int_ParseEscape_StandardLarge(Term, c, argc, args); + } + } + break; + + default: + Log_Notice("VTerm", "TODO: Handle short escape codes"); + break; + } + + //Log_Debug("VTerm", "j = %i, Buffer = '%s'", j, Buffer); + return j; +}