From a645436ebe5b5d709dfe313822d0d5ffc1b1f877 Mon Sep 17 00:00:00 2001 From: Dave Murphy Date: Thu, 11 Dec 2014 23:05:29 +0000 Subject: [PATCH] add console --- libctru/data/default_font.bin | Bin 0 -> 2048 bytes libctru/include/3ds.h | 1 + libctru/include/3ds/console.h | 138 +++++++++ libctru/source/console.c | 552 ++++++++++++++++++++++++++++++++++ 4 files changed, 691 insertions(+) create mode 100644 libctru/data/default_font.bin create mode 100644 libctru/include/3ds/console.h create mode 100644 libctru/source/console.c diff --git a/libctru/data/default_font.bin b/libctru/data/default_font.bin new file mode 100644 index 0000000000000000000000000000000000000000..73436fc909db8bb3a7ca8af9133084e68c4f354e GIT binary patch literal 2048 zcmcIlO=}cE5G|ZxT$W}NmK++g^mrH)JV-<1xWr-FKn@;6@Sxx!5EvMPS>!f>obodI0d=RJk;2=#8c@tGfO03V_ zP4=mAIhW+GpUwzk9W38>a@IGt>CY6cwGx%rr8p?oIyv3LWQt5&L2=+_7;qZK(wHKB z6PK0f$CF<4FXB)Z`zfK9)I)?mu`L5bhrP_;ME?LLS!f~np@1u}R>Q8s9PdZ~F!?hc z`&YdM0Vy5^)dupx{5W`GHQxj})36S17=;m+rU{c*6+VWnYuj+Qix+&Mhwd;cmxrNl#fQy9sdCmUUPhQjI zSN^#v%p8_K=RcDlCymaLyAU@GP~?WreSNZNu?)82R4K!?)(eesa;BV4a2a?Tj#In6 z`)qd`^U7z{Od3|wpZB`h{&?sNEE%t2>~^co#jn~!jdiTY@WDl|w{RqZV) zx3V6vkBawpKFMjd|CKtrupZ}cl;d3E{N|&C zW}0xkk&ri$>nl6?Y^yt~_n{c&5f4Me{)A?lFyN)29q~bzV|+!#QAAut+>s-`FXIh; z81UX9+fWygbrG4Lc%}~p`W=q_>irz$SXTdpW|}bQy>asS+c0n5SKK#5!!uKR)Tj3z zc@&XH5&bKo&fn}Jo4KntLBF08qx|9iD1SiZiT4T5GT)0)jyCZwz1->ZFd9`s{eMNAO4oOR$I2=j1zi988Y|0Vn{*&UA5(0`)E*q<`c{pA1v literal 0 HcmV?d00001 diff --git a/libctru/include/3ds.h b/libctru/include/3ds.h index beec914..d128d25 100644 --- a/libctru/include/3ds.h +++ b/libctru/include/3ds.h @@ -11,6 +11,7 @@ extern "C" { #include <3ds/linear.h> #include <3ds/os.h> #include <3ds/gfx.h> +#include <3ds/console.h> #include <3ds/services/ac.h> #include <3ds/services/apt.h> diff --git a/libctru/include/3ds/console.h b/libctru/include/3ds/console.h new file mode 100644 index 0000000..fe694bf --- /dev/null +++ b/libctru/include/3ds/console.h @@ -0,0 +1,138 @@ + + +/*! \file console.h + \brief 3ds stdio support. + +
+Provides stdio integration for printing to the 3DS screen as well as debug print +functionality provided by stderr. + +General usage is to initialize the console by: +consoleDemoInit() +or to customize the console usage by: +consoleInit() + +*/ + +#ifndef CONSOLE_H +#define CONSOLE_H + +#include <3ds/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef bool(* ConsolePrint)(void* con, char c); + +//! a font struct for the console. +typedef struct ConsoleFont +{ + u8* gfx; //!< A pointer to the font graphics + u16 asciiOffset; //!< Offset to the first valid character in the font table + u16 numChars; //!< Number of characters in the font graphics + +}ConsoleFont; + +/** \brief console structure used to store the state of a console render context. + +Default values from consoleGetDefault(); +
+PrintConsole defaultConsole =
+{
+	//Font:
+	{
+		(u8*)default_font_bin, //font gfx
+		0, //first ascii character in the set
+		128, //number of characters in the font set
+	},
+	0,0, //cursorX cursorY
+	0,0, //prevcursorX prevcursorY
+	40, //console width
+	30, //console height
+	0,  //window x
+	0,  //window y
+	32, //window width
+	24, //window height
+	3, //tab size
+	0, //font character offset
+	0,  //print callback
+	false //console initialized
+};
+
+*/ +typedef struct PrintConsole +{ + ConsoleFont font; //!< font of the console. + + u16 *frameBuffer; //!< framebuffer address. + + int cursorX; /*!< Current X location of the cursor (as a tile offset by default) */ + int cursorY; /*!< Current Y location of the cursor (as a tile offset by default) */ + + int prevCursorX; /*!< Internal state */ + int prevCursorY; /*!< Internal state */ + + int consoleWidth; /*!< Width of the console hardware layer in characters */ + int consoleHeight; /*!< Height of the console hardware layer in characters */ + + int windowX; /*!< Window X location in characters (not implemented) */ + int windowY; /*!< Window Y location in characters (not implemented) */ + int windowWidth; /*!< Window width in characters (not implemented) */ + int windowHeight; /*!< Window height in characters (not implemented) */ + + int tabSize; /*!< Size of a tab*/ + int fg; /*!< foreground color*/ + int bg; /*!< background color*/ + int flags; /*!< reverse/bright flags*/ + + ConsolePrint PrintChar; /*!< callback for printing a character. Should return true if it has handled rendering the graphics + (else the print engine will attempt to render via tiles) */ + + bool consoleInitialised; /*!< True if the console is initialized */ +}PrintConsole; + +#define CONSOLE_COLOR_BRIGHT (1<<0) +#define CONSOLE_COLOR_REVERSE (1<<1) + +/*! \brief Loads the font into the console + \param console pointer to the console to update, if NULL it will update the current console + \param font the font to load +*/ +void consoleSetFont(PrintConsole* console, ConsoleFont* font); + +/*! \brief Sets the print window + \param console console to set, if NULL it will set the current console window + \param x x location of the window + \param y y location of the window + \param width width of the window + \param height height of the window +*/ +void consoleSetWindow(PrintConsole* console, int x, int y, int width, int height); + +/*! \brief Gets a pointer to the console with the default values + this should only be used when using a single console or without changing the console that is returned, other wise use consoleInit() + \return A pointer to the console with the default values +*/ +PrintConsole* consoleGetDefault(void); + +/*! \brief Make the specified console the render target + \param console A pointer to the console struct (must have been initialized with consoleInit(PrintConsole* console) + \return a pointer to the previous console +*/ +PrintConsole *consoleSelect(PrintConsole* console); + +/*! \brief Initialise the console. + \param console A pointer to the console data to initialze (if it's NULL, the default console will be used) + \return A pointer to the current console. +*/ +PrintConsole* consoleInit(PrintConsole* console); + +//! Clears the screan by using iprintf("\x1b[2J"); +void consoleClear(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libctru/source/console.c b/libctru/source/console.c new file mode 100644 index 0000000..451fe0c --- /dev/null +++ b/libctru/source/console.c @@ -0,0 +1,552 @@ +#include +#include +#include +#include <3ds/gfx.h> +#include <3ds/console.h> + +#include "default_font_bin.h" + +//set up the palette for color printing +static u16 colorTable[] = { + RGB565( 0, 0, 0), // normal black + RGB565(17, 0, 0), // normal red + RGB565( 0,15, 0), // normal green + RGB565(17,34, 0), // normal yellow + RGB565( 0, 0,17), // normal blue + RGB565(17, 0,17), // normal magenta + RGB565( 0,34,17), // normal cyan + RGB565(17,34,17), // normal white + RGB565( 0, 0, 0), // bright black + RGB565(25, 0, 0), // bright red + RGB565( 0,52, 0), // bright green + RGB565(25,52, 0), // bright yellow + RGB565( 4,18,31), // bright blue + RGB565(25, 0,25), // bright magenta + RGB565( 0,52,25), // bright cyan + RGB565(28,57,28) // bright white +}; + +PrintConsole defaultConsole = +{ + //Font: + { + (u8*)default_font_bin, //font gfx + 0, //first ascii character in the set + 128 //number of characters in the font set + }, + (u16*)NULL, + 0,0, //cursorX cursorY + 0,0, //prevcursorX prevcursorY + 40, //console width + 30, //console height + 0, //window x + 0, //window y + 40, //window width + 30, //window height + 3, //tab size + 7, // foreground color + 0, // background color + CONSOLE_COLOR_BRIGHT, // flags + 0, //print callback + false //console initialized +}; + +PrintConsole currentCopy; + +PrintConsole* currentConsole = ¤tCopy; + +PrintConsole* consoleGetDefault(void){return &defaultConsole;} + +void consolePrintChar(char c); +void consoleDrawChar(int c); + +//--------------------------------------------------------------------------------- +static void consoleCls(char mode) { +//--------------------------------------------------------------------------------- + + int i = 0; + int colTemp,rowTemp; + + switch (mode) + { + case '[': + case '0': + { + colTemp = currentConsole->cursorX ; + rowTemp = currentConsole->cursorY ; + + while(i++ < ((currentConsole->windowHeight * currentConsole->windowWidth) - (rowTemp * currentConsole->consoleWidth + colTemp))) + consolePrintChar(' '); + + currentConsole->cursorX = colTemp; + currentConsole->cursorY = rowTemp; + break; + } + case '1': + { + colTemp = currentConsole->cursorX ; + rowTemp = currentConsole->cursorY ; + + currentConsole->cursorY = 0; + currentConsole->cursorX = 0; + + while (i++ < (rowTemp * currentConsole->windowWidth + colTemp)) + consolePrintChar(' '); + + currentConsole->cursorX = colTemp; + currentConsole->cursorY = rowTemp; + break; + } + case '2': + { + currentConsole->cursorY = 0; + currentConsole->cursorX = 0; + + while(i++ < currentConsole->windowHeight * currentConsole->windowWidth) + consolePrintChar(' '); + + currentConsole->cursorY = 0; + currentConsole->cursorX = 0; + break; + } + } +} +//--------------------------------------------------------------------------------- +static void consoleClearLine(char mode) { +//--------------------------------------------------------------------------------- + + int i = 0; + int colTemp; + + switch (mode) + { + case '[': + case '0': + { + colTemp = currentConsole->cursorX ; + + while(i++ < (currentConsole->windowWidth - colTemp)) { + consolePrintChar(' '); + } + + currentConsole->cursorX = colTemp; + + break; + } + case '1': + { + colTemp = currentConsole->cursorX ; + + currentConsole->cursorX = 0; + + while(i++ < ((currentConsole->windowWidth - colTemp)-2)) { + consolePrintChar(' '); + } + + currentConsole->cursorX = colTemp; + + break; + } + case '2': + { + colTemp = currentConsole->cursorX ; + + currentConsole->cursorX = 0; + + while(i++ < currentConsole->windowWidth) { + consolePrintChar(' '); + } + + currentConsole->cursorX = colTemp; + + break; + } + default: + { + colTemp = currentConsole->cursorX ; + + while(i++ < (currentConsole->windowWidth - colTemp)) { + consolePrintChar(' '); + } + + currentConsole->cursorX = colTemp; + + break; + } + } +} + + +//--------------------------------------------------------------------------------- +ssize_t con_write(struct _reent *r,int fd,const char *ptr, size_t len) { +//--------------------------------------------------------------------------------- + + char chr; + + int i, count = 0; + char *tmp = (char*)ptr; + + if(!tmp || len<=0) return -1; + + i = 0; + + while(icursorY = (currentConsole->cursorY - parameter) < 0 ? 0 : currentConsole->cursorY - parameter; + escaping = false; + break; + case 'B': + sscanf(escapeseq,"[%dB", ¶meter); + currentConsole->cursorY = (currentConsole->cursorY + parameter) > currentConsole->windowHeight - 1 ? currentConsole->windowHeight - 1 : currentConsole->cursorY + parameter; + escaping = false; + break; + case 'C': + sscanf(escapeseq,"[%dC", ¶meter); + currentConsole->cursorX = (currentConsole->cursorX + parameter) > currentConsole->windowWidth - 1 ? currentConsole->windowWidth - 1 : currentConsole->cursorX + parameter; + escaping = false; + break; + case 'D': + sscanf(escapeseq,"[%dD", ¶meter); + currentConsole->cursorX = (currentConsole->cursorX - parameter) < 0 ? 0 : currentConsole->cursorX - parameter; + escaping = false; + break; + //--------------------------------------- + // Cursor position movement + //--------------------------------------- + case 'H': + case 'f': + sscanf(escapeseq,"[%d;%df", ¤tConsole->cursorY , ¤tConsole->cursorX ); + escaping = false; + break; + //--------------------------------------- + // Screen clear + //--------------------------------------- + case 'J': + consoleCls(escapeseq[escapelen-2]); + escaping = false; + break; + //--------------------------------------- + // Line clear + //--------------------------------------- + case 'K': + consoleClearLine(escapeseq[escapelen-2]); + escaping = false; + break; + //--------------------------------------- + // Save cursor position + //--------------------------------------- + case 's': + currentConsole->prevCursorX = currentConsole->cursorX ; + currentConsole->prevCursorY = currentConsole->cursorY ; + escaping = false; + break; + //--------------------------------------- + // Load cursor position + //--------------------------------------- + case 'u': + currentConsole->cursorX = currentConsole->prevCursorX ; + currentConsole->cursorY = currentConsole->prevCursorY ; + escaping = false; + break; + //--------------------------------------- + // Color scan codes + //--------------------------------------- + case 'm': + escapeseq++; + scanning = true; + +// do while doesn't work at -O2 +// do { + sscanf(escapeseq,"%d;%n", ¶meter, &consumed); + escapeseq += consumed; + + if (parameter == 0 ) { + currentConsole->flags |= CONSOLE_COLOR_BRIGHT; + currentConsole->flags &= ~CONSOLE_COLOR_REVERSE; + currentConsole->bg = 0; + currentConsole->fg = 7; + } else if (parameter == 7) { // reverse video + currentConsole->flags |= CONSOLE_COLOR_REVERSE; + } else if (parameter == 2) { // half bright + currentConsole->flags &= ~CONSOLE_COLOR_BRIGHT; + } else if (parameter >= 30 && parameter <= 37) { // writing color + currentConsole->fg = parameter - 30; + } else if (parameter >= 40 && parameter <= 47) { // screen color + currentConsole->bg = parameter - 40; + } + if(escapeseq >= tmp) scanning = false; +// } while(scanning); + + escaping = false; + break; + } + } while (escaping); + continue; + } + + consolePrintChar(chr); + } + + return count; +} + +static const devoptab_t dotab_stdout = { + "con", + 0, + NULL, + NULL, + con_write, + NULL, + NULL, + NULL +}; + +static const devoptab_t dotab_null = { + "null", + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +//--------------------------------------------------------------------------------- +PrintConsole* consoleInit(PrintConsole* console) { +//--------------------------------------------------------------------------------- + + static bool firstConsoleInit = true; + + if(firstConsoleInit) { + devoptab_list[STD_OUT] = &dotab_stdout; + devoptab_list[STD_ERR] = &dotab_stdout; + + setvbuf(stdout, NULL , _IONBF, 0); + setvbuf(stderr, NULL , _IONBF, 0); + + firstConsoleInit = false; + } + + if(console) { + currentConsole = console; + } else { + console = currentConsole; + } + + *currentConsole = defaultConsole; + + console->consoleInitialised = 1; + + gfxSetScreenFormat(GFX_BOTTOM,GSP_RGB565_OES); + gfxSetDoubleBuffering(GFX_BOTTOM,false); + console->frameBuffer = (u16*)gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL); + + + consoleCls('2'); + + return currentConsole; + +} +//--------------------------------------------------------------------------------- +PrintConsole *consoleSelect(PrintConsole* console){ +//--------------------------------------------------------------------------------- + PrintConsole *tmp = currentConsole; + currentConsole = console; + return tmp; +} + +//--------------------------------------------------------------------------------- +void consoleSetFont(PrintConsole* console, ConsoleFont* font){ +//--------------------------------------------------------------------------------- + + if(!console) console = currentConsole; + + console->font = *font; + +} + +//--------------------------------------------------------------------------------- +static void newRow() { +//--------------------------------------------------------------------------------- + + + currentConsole->cursorY ++; + + if(currentConsole->cursorY >= currentConsole->windowHeight) { + currentConsole->cursorY --; + u16 *dst = ¤tConsole->frameBuffer[(currentConsole->windowX * 8 * 240) + (239 - (currentConsole->windowY * 8))]; + u16 *src = dst - 8; + + int i,j; + + for (i=0; iwindowWidth*8; i++) { + u32 *from=(u32*)src; + u32 *to = (u32*)dst; + for (j=0; j<((currentConsole->windowHeight*8)-8)/2;j++) *(to--) = *(from--); + + dst += 240; + src += 240; + } + + consoleClearLine('2'); + } +} +//--------------------------------------------------------------------------------- +void consoleDrawChar(int c) { +//--------------------------------------------------------------------------------- + c -= currentConsole->font.asciiOffset; + if ( c < 0 || c > currentConsole->font.numChars ) return; + + u8 *fontdata = currentConsole->font.gfx + (8 * c); + + int writingColor = currentConsole->fg; + int screenColor = currentConsole->bg; + + if (currentConsole->flags & CONSOLE_COLOR_BRIGHT) { + writingColor |= 8; + screenColor |=8; + } + + if (currentConsole->flags & CONSOLE_COLOR_REVERSE) { + int tmp = writingColor; + writingColor = screenColor; + screenColor = tmp; + } + + u16 bg = colorTable[screenColor]; + u16 fg = colorTable[writingColor]; + + u8 b1 = *(fontdata++); + u8 b2 = *(fontdata++); + u8 b3 = *(fontdata++); + u8 b4 = *(fontdata++); + u8 b5 = *(fontdata++); + u8 b6 = *(fontdata++); + u8 b7 = *(fontdata++); + u8 b8 = *(fontdata++); + + u8 mask = 0x80; + + + int i; + + int x = (currentConsole->cursorX + currentConsole->windowX) * 8; + int y = ((currentConsole->cursorY + currentConsole->windowY) *8 ); + + u16 *screen = ¤tConsole->frameBuffer[(x * 240) + (239 - (y + 7))]; + + for (i=0;i<8;i++) { + if (b8 & mask) { *(screen++) = fg; }else{ *(screen++) = bg; } + if (b7 & mask) { *(screen++) = fg; }else{ *(screen++) = bg; } + if (b6 & mask) { *(screen++) = fg; }else{ *(screen++) = bg; } + if (b5 & mask) { *(screen++) = fg; }else{ *(screen++) = bg; } + if (b4 & mask) { *(screen++) = fg; }else{ *(screen++) = bg; } + if (b3 & mask) { *(screen++) = fg; }else{ *(screen++) = bg; } + if (b2 & mask) { *(screen++) = fg; }else{ *(screen++) = bg; } + if (b1 & mask) { *(screen++) = fg; }else{ *(screen++) = bg; } + mask >>= 1; + screen += 240 - 8; + } + +} + +//--------------------------------------------------------------------------------- +void consolePrintChar(char c) { +//--------------------------------------------------------------------------------- + if (c==0) return; + + if(currentConsole->PrintChar) + if(currentConsole->PrintChar(currentConsole, c)) + return; + + if(currentConsole->cursorX >= currentConsole->windowWidth) { + currentConsole->cursorX = 0; + + newRow(); + } + + switch(c) { + /* + The only special characters we will handle are tab (\t), carriage return (\r), line feed (\n) + and backspace (\b). + Carriage return & line feed will function the same: go to next line and put cursor at the beginning. + For everything else, use VT sequences. + + Reason: VT sequences are more specific to the task of cursor placement. + The special escape sequences \b \f & \v are archaic and non-portable. + */ + case 8: + currentConsole->cursorX--; + + if(currentConsole->cursorX < 0) { + if(currentConsole->cursorY > 0) { + currentConsole->cursorX = currentConsole->windowX - 1; + currentConsole->cursorY--; + } else { + currentConsole->cursorX = 0; + } + } + + consoleDrawChar(' '); + break; + + case 9: + currentConsole->cursorX += currentConsole->tabSize - ((currentConsole->cursorX)%(currentConsole->tabSize)); + break; + case 10: + newRow(); + case 13: + currentConsole->cursorX = 0; + break; + default: + consoleDrawChar(c); + ++currentConsole->cursorX ; + break; + } +} + +//--------------------------------------------------------------------------------- +void consoleClear(void) { +//--------------------------------------------------------------------------------- + iprintf("\x1b[2J"); +} + +//--------------------------------------------------------------------------------- +void consoleSetWindow(PrintConsole* console, int x, int y, int width, int height){ +//--------------------------------------------------------------------------------- + + if(!console) console = currentConsole; + + console->windowWidth = width; + console->windowHeight = height; + console->windowX = x; + console->windowY = y; + + console->cursorX = 0; + console->cursorY = 0; + +} + +