2013-05-03 14:36:17 +00:00
|
|
|
/*
|
2013-06-15 11:09:02 +00:00
|
|
|
* This file is subject to the terms of the GFX License. If a copy of
|
2013-05-03 14:36:17 +00:00
|
|
|
* the license was not distributed with this file, you can obtain one at:
|
|
|
|
*
|
2013-07-21 20:20:37 +00:00
|
|
|
* http://ugfx.org/license.html
|
2013-05-03 14:36:17 +00:00
|
|
|
*/
|
2013-05-06 04:44:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @file src/gwin/console.c
|
|
|
|
* @brief GWIN sub-system console code.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "gfx.h"
|
|
|
|
|
2013-06-06 04:33:32 +00:00
|
|
|
#if GFX_USE_GWIN && GWIN_NEED_CONSOLE
|
2013-05-06 04:44:47 +00:00
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
2013-06-06 04:33:32 +00:00
|
|
|
#include "gwin/class_gwin.h"
|
2013-05-06 04:44:47 +00:00
|
|
|
|
|
|
|
#define GWIN_CONSOLE_USE_CLEAR_LINES TRUE
|
|
|
|
#define GWIN_CONSOLE_USE_FILLED_CHARS FALSE
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Stream interface implementation. The interface is write only
|
|
|
|
*/
|
|
|
|
|
2013-05-25 16:06:55 +00:00
|
|
|
#if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM
|
2013-05-24 15:26:52 +00:00
|
|
|
#define Stream2GWindow(ip) ((GHandle)(((char *)(ip)) - (size_t)(&(((GConsoleObject *)0)->stream))))
|
|
|
|
|
|
|
|
static size_t GWinStreamWrite(void *ip, const uint8_t *bp, size_t n) { gwinPutCharArray(Stream2GWindow(ip), (const char *)bp, n); return RDY_OK; }
|
|
|
|
static size_t GWinStreamRead(void *ip, uint8_t *bp, size_t n) { (void)ip; (void)bp; (void)n; return 0; }
|
|
|
|
static msg_t GWinStreamPut(void *ip, uint8_t b) { gwinPutChar(Stream2GWindow(ip), (char)b); return RDY_OK; }
|
|
|
|
static msg_t GWinStreamGet(void *ip) {(void)ip; return RDY_OK; }
|
|
|
|
static msg_t GWinStreamPutTimed(void *ip, uint8_t b, systime_t time) { (void)time; gwinPutChar(Stream2GWindow(ip), (char)b); return RDY_OK; }
|
|
|
|
static msg_t GWinStreamGetTimed(void *ip, systime_t timeout) { (void)ip; (void)timeout; return RDY_OK; }
|
|
|
|
static size_t GWinStreamWriteTimed(void *ip, const uint8_t *bp, size_t n, systime_t time) { (void)time; gwinPutCharArray(Stream2GWindow(ip), (const char *)bp, n); return RDY_OK; }
|
|
|
|
static size_t GWinStreamReadTimed(void *ip, uint8_t *bp, size_t n, systime_t time) { (void)ip; (void)bp; (void)n; (void)time; return 0; }
|
|
|
|
|
|
|
|
struct GConsoleWindowVMT_t {
|
|
|
|
_base_asynchronous_channel_methods
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct GConsoleWindowVMT_t GWindowConsoleVMT = {
|
|
|
|
GWinStreamWrite,
|
|
|
|
GWinStreamRead,
|
|
|
|
GWinStreamPut,
|
|
|
|
GWinStreamGet,
|
|
|
|
GWinStreamPutTimed,
|
|
|
|
GWinStreamGetTimed,
|
|
|
|
GWinStreamWriteTimed,
|
|
|
|
GWinStreamReadTimed
|
|
|
|
};
|
|
|
|
#endif
|
2013-05-06 04:44:47 +00:00
|
|
|
|
2013-06-06 04:33:32 +00:00
|
|
|
static void AfterClear(GWindowObject *gh) {
|
|
|
|
((GConsoleObject *)gh)->cx = 0;
|
|
|
|
((GConsoleObject *)gh)->cy = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const gwinVMT consoleVMT = {
|
|
|
|
"Console", // The classname
|
2013-06-24 12:58:37 +00:00
|
|
|
sizeof(GConsoleObject), // The object size
|
2013-06-06 04:33:32 +00:00
|
|
|
0, // The destroy routine
|
2013-06-07 16:27:59 +00:00
|
|
|
0, // The redraw routine
|
2013-06-06 04:33:32 +00:00
|
|
|
AfterClear, // The after-clear routine
|
|
|
|
};
|
|
|
|
|
2013-07-07 09:40:37 +00:00
|
|
|
GHandle gwinConsoleCreate(GConsoleObject *gc, const GWindowInit *pInit) {
|
2013-06-24 12:58:37 +00:00
|
|
|
if (!(gc = (GConsoleObject *)_gwindowCreate(&gc->g, pInit, &consoleVMT, 0)))
|
2013-05-06 04:44:47 +00:00
|
|
|
return 0;
|
2013-06-02 08:57:22 +00:00
|
|
|
#if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM
|
2013-05-24 15:26:52 +00:00
|
|
|
gc->stream.vmt = &GWindowConsoleVMT;
|
|
|
|
#endif
|
2013-05-06 04:44:47 +00:00
|
|
|
gc->cx = 0;
|
|
|
|
gc->cy = 0;
|
2013-06-24 12:58:37 +00:00
|
|
|
gwinSetVisible((GHandle)gc, pInit->show);
|
2013-05-06 04:44:47 +00:00
|
|
|
return (GHandle)gc;
|
|
|
|
}
|
|
|
|
|
2013-05-25 16:06:55 +00:00
|
|
|
#if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM
|
2013-07-07 09:40:37 +00:00
|
|
|
BaseSequentialStream *gwinConsoleGetStream(GHandle gh) {
|
2013-06-06 04:33:32 +00:00
|
|
|
if (gh->vmt != &consoleVMT)
|
2013-05-24 15:26:52 +00:00
|
|
|
return 0;
|
|
|
|
return (BaseSequentialStream *)&(((GConsoleObject *)(gh))->stream);
|
|
|
|
}
|
|
|
|
#endif
|
2013-05-06 04:44:47 +00:00
|
|
|
|
|
|
|
void gwinPutChar(GHandle gh, char c) {
|
|
|
|
#define gcw ((GConsoleObject *)gh)
|
2013-06-06 04:33:32 +00:00
|
|
|
uint8_t width, fy, fp;
|
|
|
|
|
|
|
|
if (gh->vmt != &consoleVMT || !gh->font)
|
|
|
|
return;
|
2013-05-06 04:44:47 +00:00
|
|
|
|
2013-06-06 04:33:32 +00:00
|
|
|
fy = gdispGetFontMetric(gh->font, fontHeight);
|
|
|
|
fp = gdispGetFontMetric(gh->font, fontCharPadding);
|
2013-05-06 04:44:47 +00:00
|
|
|
|
|
|
|
#if GDISP_NEED_CLIP
|
|
|
|
gdispSetClip(gh->x, gh->y, gh->width, gh->height);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (c == '\n') {
|
|
|
|
gcw->cx = 0;
|
2013-06-06 04:33:32 +00:00
|
|
|
gcw->cy += fy;
|
2013-05-06 04:44:47 +00:00
|
|
|
// We use lazy scrolling here and only scroll when the next char arrives
|
|
|
|
} else if (c == '\r') {
|
|
|
|
// gcw->cx = 0;
|
|
|
|
} else {
|
2013-06-06 04:33:32 +00:00
|
|
|
width = gdispGetCharWidth(c, gh->font) + fp;
|
2013-05-06 04:44:47 +00:00
|
|
|
if (gcw->cx + width >= gh->width) {
|
|
|
|
gcw->cx = 0;
|
2013-06-06 04:33:32 +00:00
|
|
|
gcw->cy += fy;
|
2013-05-06 04:44:47 +00:00
|
|
|
}
|
|
|
|
|
2013-06-06 04:33:32 +00:00
|
|
|
if (gcw->cy + fy > gh->height) {
|
2013-05-06 04:44:47 +00:00
|
|
|
#if GDISP_NEED_SCROLL
|
|
|
|
/* scroll the console */
|
2013-06-06 04:33:32 +00:00
|
|
|
gdispVerticalScroll(gh->x, gh->y, gh->width, gh->height, fy, gh->bgcolor);
|
2013-05-06 04:44:47 +00:00
|
|
|
/* reset the cursor to the start of the last line */
|
|
|
|
gcw->cx = 0;
|
2013-06-06 04:33:32 +00:00
|
|
|
gcw->cy = (((coord_t)(gh->height/fy))-1)*fy;
|
2013-05-06 04:44:47 +00:00
|
|
|
#else
|
|
|
|
/* clear the console */
|
|
|
|
gdispFillArea(gh->x, gh->y, gh->width, gh->height, gh->bgcolor);
|
|
|
|
/* reset the cursor to the top of the window */
|
|
|
|
gcw->cx = 0;
|
|
|
|
gcw->cy = 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#if GWIN_CONSOLE_USE_CLEAR_LINES
|
|
|
|
/* clear to the end of the line */
|
|
|
|
if (gcw->cx == 0)
|
2013-06-06 04:33:32 +00:00
|
|
|
gdispFillArea(gh->x, gh->y + gcw->cy, gh->width, fy, gh->bgcolor);
|
2013-05-06 04:44:47 +00:00
|
|
|
#endif
|
|
|
|
#if GWIN_CONSOLE_USE_FILLED_CHARS
|
|
|
|
gdispFillChar(gh->x + gcw->cx, gh->y + gcw->cy, c, gh->font, gh->color, gh->bgcolor);
|
|
|
|
#else
|
|
|
|
gdispDrawChar(gh->x + gcw->cx, gh->y + gcw->cy, c, gh->font, gh->color);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* update cursor */
|
|
|
|
gcw->cx += width;
|
|
|
|
}
|
|
|
|
#undef gcw
|
|
|
|
}
|
|
|
|
|
|
|
|
void gwinPutString(GHandle gh, const char *str) {
|
|
|
|
while(*str)
|
|
|
|
gwinPutChar(gh, *str++);
|
|
|
|
}
|
|
|
|
|
|
|
|
void gwinPutCharArray(GHandle gh, const char *str, size_t n) {
|
|
|
|
while(n--)
|
|
|
|
gwinPutChar(gh, *str++);
|
|
|
|
}
|
|
|
|
|
2013-05-25 16:06:55 +00:00
|
|
|
#include <stdarg.h>
|
|
|
|
|
|
|
|
#define MAX_FILLER 11
|
|
|
|
#define FLOAT_PRECISION 100000
|
|
|
|
|
|
|
|
static char *ltoa_wd(char *p, long num, unsigned radix, long divisor) {
|
|
|
|
int i;
|
|
|
|
char *q;
|
|
|
|
|
|
|
|
if (!divisor) divisor = num;
|
|
|
|
|
|
|
|
q = p + MAX_FILLER;
|
|
|
|
do {
|
|
|
|
i = (int)(num % radix);
|
|
|
|
i += '0';
|
|
|
|
if (i > '9')
|
|
|
|
i += 'A' - '0' - 10;
|
|
|
|
*--q = i;
|
|
|
|
num /= radix;
|
|
|
|
} while ((divisor /= radix) != 0);
|
|
|
|
|
|
|
|
i = (int)(p + MAX_FILLER - q);
|
|
|
|
do {
|
|
|
|
*p++ = *q++;
|
|
|
|
} while (--i);
|
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if GWIN_CONSOLE_USE_FLOAT
|
|
|
|
static char *ftoa(char *p, double num) {
|
|
|
|
long l;
|
|
|
|
unsigned long precision = FLOAT_PRECISION;
|
|
|
|
|
|
|
|
l = num;
|
|
|
|
p = ltoa_wd(p, l, 10, 0);
|
|
|
|
*p++ = '.';
|
|
|
|
l = (num - l) * precision;
|
|
|
|
return ltoa_wd(p, l, 10, precision / 10);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void gwinPrintf(GHandle gh, const char *fmt, ...) {
|
|
|
|
va_list ap;
|
|
|
|
char *p, *s, c, filler;
|
|
|
|
int i, precision, width;
|
|
|
|
bool_t is_long, left_align;
|
|
|
|
long l;
|
2013-06-02 08:57:22 +00:00
|
|
|
#if GWIN_CONSOLE_USE_FLOAT
|
2013-05-25 16:06:55 +00:00
|
|
|
float f;
|
|
|
|
char tmpbuf[2*MAX_FILLER + 1];
|
|
|
|
#else
|
|
|
|
char tmpbuf[MAX_FILLER + 1];
|
|
|
|
#endif
|
|
|
|
|
2013-06-06 04:33:32 +00:00
|
|
|
if (gh->vmt != &consoleVMT || !gh->font)
|
|
|
|
return;
|
2013-05-25 16:06:55 +00:00
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
while (TRUE) {
|
|
|
|
c = *fmt++;
|
|
|
|
if (c == 0) {
|
|
|
|
va_end(ap);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (c != '%') {
|
|
|
|
gwinPutChar(gh, c);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = tmpbuf;
|
|
|
|
s = tmpbuf;
|
|
|
|
left_align = FALSE;
|
|
|
|
if (*fmt == '-') {
|
|
|
|
fmt++;
|
|
|
|
left_align = TRUE;
|
|
|
|
}
|
|
|
|
filler = ' ';
|
|
|
|
if (*fmt == '.') {
|
|
|
|
fmt++;
|
|
|
|
filler = '0';
|
|
|
|
}
|
|
|
|
width = 0;
|
|
|
|
|
|
|
|
while (TRUE) {
|
|
|
|
c = *fmt++;
|
|
|
|
if (c >= '0' && c <= '9')
|
|
|
|
c -= '0';
|
|
|
|
else if (c == '*')
|
|
|
|
c = va_arg(ap, int);
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
width = width * 10 + c;
|
|
|
|
}
|
|
|
|
precision = 0;
|
|
|
|
if (c == '.') {
|
|
|
|
while (TRUE) {
|
|
|
|
c = *fmt++;
|
|
|
|
if (c >= '0' && c <= '9')
|
|
|
|
c -= '0';
|
|
|
|
else if (c == '*')
|
|
|
|
c = va_arg(ap, int);
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
precision = precision * 10 + c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Long modifier.*/
|
|
|
|
if (c == 'l' || c == 'L') {
|
|
|
|
is_long = TRUE;
|
|
|
|
if (*fmt)
|
|
|
|
c = *fmt++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
is_long = (c >= 'A') && (c <= 'Z');
|
|
|
|
|
|
|
|
/* Command decoding.*/
|
|
|
|
switch (c) {
|
|
|
|
case 'c':
|
|
|
|
filler = ' ';
|
|
|
|
*p++ = va_arg(ap, int);
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
filler = ' ';
|
|
|
|
if ((s = va_arg(ap, char *)) == 0)
|
|
|
|
s = "(null)";
|
|
|
|
if (precision == 0)
|
|
|
|
precision = 32767;
|
|
|
|
for (p = s; *p && (--precision >= 0); p++);
|
|
|
|
break;
|
|
|
|
case 'D':
|
|
|
|
case 'd':
|
|
|
|
if (is_long)
|
|
|
|
l = va_arg(ap, long);
|
|
|
|
else
|
|
|
|
l = va_arg(ap, int);
|
|
|
|
if (l < 0) {
|
|
|
|
*p++ = '-';
|
|
|
|
l = -l;
|
|
|
|
}
|
|
|
|
p = ltoa_wd(p, l, 10, 0);
|
|
|
|
break;
|
2013-06-02 08:57:22 +00:00
|
|
|
#if GWIN_CONSOLE_USE_FLOAT
|
2013-05-25 16:06:55 +00:00
|
|
|
case 'f':
|
|
|
|
f = (float) va_arg(ap, double);
|
|
|
|
if (f < 0) {
|
|
|
|
*p++ = '-';
|
|
|
|
f = -f;
|
|
|
|
}
|
|
|
|
p = ftoa(p, f);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case 'X':
|
|
|
|
case 'x':
|
|
|
|
c = 16;
|
|
|
|
goto unsigned_common;
|
|
|
|
case 'U':
|
|
|
|
case 'u':
|
|
|
|
c = 10;
|
|
|
|
goto unsigned_common;
|
|
|
|
case 'O':
|
|
|
|
case 'o':
|
|
|
|
c = 8;
|
|
|
|
unsigned_common:
|
|
|
|
if (is_long)
|
|
|
|
l = va_arg(ap, long);
|
|
|
|
else
|
|
|
|
l = va_arg(ap, int);
|
|
|
|
p = ltoa_wd(p, l, c, 0);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
*p++ = c;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
i = (int)(p - s);
|
|
|
|
if ((width -= i) < 0)
|
|
|
|
width = 0;
|
|
|
|
if (left_align == FALSE)
|
|
|
|
width = -width;
|
|
|
|
if (width < 0) {
|
|
|
|
if (*s == '-' && filler == '0') {
|
|
|
|
gwinPutChar(gh, *s++);
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
do {
|
|
|
|
gwinPutChar(gh, filler);
|
|
|
|
} while (++width != 0);
|
|
|
|
}
|
|
|
|
while (--i >= 0)
|
|
|
|
gwinPutChar(gh, *s++);
|
|
|
|
while (width) {
|
|
|
|
gwinPutChar(gh, filler);
|
|
|
|
width--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-06 04:44:47 +00:00
|
|
|
#endif /* GFX_USE_GWIN && GWIN_NEED_CONSOLE */
|
2013-06-06 04:33:32 +00:00
|
|
|
|
2013-05-06 04:44:47 +00:00
|
|
|
|