The official µGFX library repository.

gwin_console.c 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833
  1. /*
  2. * This file is subject to the terms of the GFX License. If a copy of
  3. * the license was not distributed with this file, you can obtain one at:
  4. *
  5. * http://ugfx.org/license.html
  6. */
  7. /**
  8. * @file src/gwin/gwin_console.c
  9. * @brief GWIN sub-system console code.
  10. */
  11. #include "../../gfx.h"
  12. #if GFX_USE_GWIN && GWIN_NEED_CONSOLE
  13. #include <string.h>
  14. #include "gwin_class.h"
  15. #define GWIN_CONSOLE_USE_CLEAR_LINES TRUE // Clear each line before using it
  16. #define GWIN_CONSOLE_USE_FILLED_CHARS FALSE // Use filled characters instead of drawn characters
  17. #define GWIN_CONSOLE_BUFFER_SCROLLING TRUE // Use the history buffer to scroll when it is available
  18. // Our control flags
  19. #define GCONSOLE_FLG_NOSTORE (GWIN_FIRST_CONTROL_FLAG<<0)
  20. #define GCONSOLE_FLG_OVERRUN (GWIN_FIRST_CONTROL_FLAG<<1)
  21. // Meaning of our attribute bits.
  22. #define ESC_REDBIT 0x01
  23. #define ESC_GREENBIT 0x02
  24. #define ESC_BLUEBIT 0x04
  25. #define ESC_USECOLOR 0x08
  26. #define ESC_UNDERLINE 0x10
  27. #define ESC_BOLD 0x20
  28. /*
  29. * Stream interface implementation. The interface is write only
  30. */
  31. #if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM
  32. #define Stream2GWindow(ip) ((GHandle)(((char *)(ip)) - (size_t)(&(((GConsoleObject *)0)->stream))))
  33. #if CH_KERNEL_MAJOR == 2
  34. static size_t GWinStreamWrite(void *ip, const uint8_t *bp, size_t n) { gwinPutCharArray(Stream2GWindow(ip), (const char *)bp, n); return RDY_OK; }
  35. static size_t GWinStreamRead(void *ip, uint8_t *bp, size_t n) { (void)ip; (void)bp; (void)n; return 0; }
  36. static msg_t GWinStreamPut(void *ip, uint8_t b) { gwinPutChar(Stream2GWindow(ip), (char)b); return RDY_OK; }
  37. static msg_t GWinStreamGet(void *ip) {(void)ip; return RDY_OK; }
  38. static msg_t GWinStreamPutTimed(void *ip, uint8_t b, systime_t time) { (void)time; gwinPutChar(Stream2GWindow(ip), (char)b); return RDY_OK; }
  39. static msg_t GWinStreamGetTimed(void *ip, systime_t timeout) { (void)ip; (void)timeout; return RDY_OK; }
  40. 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; }
  41. 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; }
  42. #elif CH_KERNEL_MAJOR == 3
  43. static size_t GWinStreamWrite(void *ip, const uint8_t *bp, size_t n) { gwinPutCharArray(Stream2GWindow(ip), (const char *)bp, n); return MSG_OK; }
  44. static size_t GWinStreamRead(void *ip, uint8_t *bp, size_t n) { (void)ip; (void)bp; (void)n; return 0; }
  45. static msg_t GWinStreamPut(void *ip, uint8_t b) { gwinPutChar(Stream2GWindow(ip), (char)b); return MSG_OK; }
  46. static msg_t GWinStreamGet(void *ip) {(void)ip; return MSG_OK; }
  47. static msg_t GWinStreamPutTimed(void *ip, uint8_t b, systime_t time) { (void)time; gwinPutChar(Stream2GWindow(ip), (char)b); return MSG_OK; }
  48. static msg_t GWinStreamGetTimed(void *ip, systime_t timeout) { (void)ip; (void)timeout; return MSG_OK; }
  49. 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 MSG_OK; }
  50. 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; }
  51. #endif
  52. struct GConsoleWindowVMT_t {
  53. _base_asynchronous_channel_methods
  54. };
  55. static const struct GConsoleWindowVMT_t GWindowConsoleVMT = {
  56. GWinStreamWrite,
  57. GWinStreamRead,
  58. GWinStreamPut,
  59. GWinStreamGet,
  60. GWinStreamPutTimed,
  61. GWinStreamGetTimed,
  62. GWinStreamWriteTimed,
  63. GWinStreamReadTimed
  64. };
  65. #endif
  66. #if GWIN_CONSOLE_ESCSEQ
  67. // Convert escape sequences to attributes
  68. static bool_t ESCtoAttr(char c, uint8_t *pattr) {
  69. uint8_t attr;
  70. attr = pattr[0];
  71. switch(c) {
  72. case '0': case '1': case '2': case '3':
  73. case '4': case '5': case '6': case '7':
  74. attr &= ~(ESC_REDBIT|ESC_GREENBIT|ESC_BLUEBIT);
  75. attr |= (c - '0') | ESC_USECOLOR;
  76. break;
  77. case 'C':
  78. attr &= ~(ESC_REDBIT|ESC_GREENBIT|ESC_BLUEBIT|ESC_USECOLOR);
  79. break;
  80. case 'u':
  81. attr |= ESC_UNDERLINE;
  82. break;
  83. case 'U':
  84. attr &= ~ESC_UNDERLINE;
  85. break;
  86. case 'b':
  87. attr |= ESC_BOLD;
  88. break;
  89. case 'B':
  90. attr &= ~ESC_BOLD;
  91. break;
  92. default:
  93. return FALSE;
  94. }
  95. if (attr == pattr[0])
  96. return FALSE;
  97. pattr[0] = attr;
  98. return TRUE;
  99. }
  100. static color_t ESCPrintColor(GConsoleObject *gcw) {
  101. switch(gcw->currattr & (ESC_REDBIT|ESC_GREENBIT|ESC_BLUEBIT|ESC_USECOLOR)) {
  102. case (ESC_USECOLOR):
  103. return Black;
  104. case (ESC_USECOLOR|ESC_REDBIT):
  105. return Red;
  106. case (ESC_USECOLOR|ESC_GREENBIT):
  107. return Green;
  108. case (ESC_USECOLOR|ESC_REDBIT|ESC_GREENBIT):
  109. return Yellow;
  110. case (ESC_USECOLOR|ESC_BLUEBIT):
  111. return Blue;
  112. case (ESC_USECOLOR|ESC_REDBIT|ESC_BLUEBIT):
  113. return Magenta;
  114. case (ESC_USECOLOR|ESC_GREENBIT|ESC_BLUEBIT):
  115. return Cyan;
  116. case (ESC_USECOLOR|ESC_REDBIT|ESC_GREENBIT|ESC_BLUEBIT):
  117. return White;
  118. default:
  119. return gcw->g.color;
  120. }
  121. }
  122. #else
  123. #define ESCPrintColor(gcw) ((gcw)->g.color)
  124. #endif
  125. #if GWIN_CONSOLE_USE_HISTORY
  126. static void HistoryDestroy(GWindowObject *gh) {
  127. #define gcw ((GConsoleObject *)gh)
  128. // Deallocate the history buffer if required.
  129. if (gcw->buffer) {
  130. gfxFree(gcw->buffer);
  131. gcw->buffer = 0;
  132. }
  133. #undef gcw
  134. }
  135. static void scrollBuffer(GConsoleObject *gcw);
  136. static void HistoryRedraw(GWindowObject *gh) {
  137. #define gcw ((GConsoleObject *)gh)
  138. // No redrawing if there is no history
  139. if (!gcw->buffer)
  140. return;
  141. // Handle vertical size decrease:
  142. // We have to scroll out first lines of log
  143. coord_t fy = gdispGetFontMetric(gh->font, fontHeight);
  144. while (gcw->cy > gh->height) {
  145. scrollBuffer (gcw);
  146. gcw->cy -= fy;
  147. }
  148. // We are printing the buffer - don't store it again
  149. gh->flags |= GCONSOLE_FLG_NOSTORE;
  150. #if !GWIN_CONSOLE_USE_CLEAR_LINES
  151. // Clear the screen
  152. gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor);
  153. #endif
  154. // Reset the cursor
  155. gcw->cx = 0;
  156. gcw->cy = 0;
  157. // Reset the current attributes
  158. #if GWIN_CONSOLE_ESCSEQ
  159. gcw->currattr = gcw->startattr;
  160. #endif
  161. // Print the buffer
  162. gwinPutCharArray(gh, gcw->buffer, gcw->bufpos);
  163. #if GWIN_CONSOLE_USE_CLEAR_LINES
  164. // Clear the remaining space
  165. {
  166. coord_t y;
  167. y = gcw->cy;
  168. if (gcw->cx)
  169. y += gdispGetFontMetric(gh->font, fontHeight);
  170. if (y < gh->height)
  171. gdispGFillArea(gh->display, gh->x, gh->y+y, gh->width, gh->height-y, gh->bgcolor);
  172. }
  173. #endif
  174. // Turn back on storing of buffer contents
  175. gh->flags &= ~GCONSOLE_FLG_NOSTORE;
  176. #undef gcw
  177. }
  178. /**
  179. * Put a character into our history buffer
  180. */
  181. static void putCharInBuffer(GConsoleObject *gcw, char c) {
  182. // Only store if we need to
  183. if (!gcw->buffer || (gcw->g.flags & GCONSOLE_FLG_NOSTORE))
  184. return;
  185. // Do we have enough space in the buffer
  186. if (gcw->bufpos >= gcw->bufsize) {
  187. char *p, *ep;
  188. size_t dp;
  189. /**
  190. * This should never really happen except if the user has changed the window
  191. * size without turning off and then on the buffer. Even then it is unlikely
  192. * because of our conservative allocation strategy.
  193. * If it really is needed we scroll one line to make some space. We also mark
  194. * it is an overrun so that if asked to really scroll later we know we already have.
  195. * Note we only use one bit to indicate an overrun, so an overrun of more
  196. * than one line will lead to some interesting scrolling and refreshing
  197. * effects.
  198. */
  199. // Remove one line from the start
  200. ep = gcw->buffer+gcw->bufpos;
  201. for(p = gcw->buffer; p < ep && *p != '\n'; p++) {
  202. #if GWIN_CONSOLE_ESCSEQ
  203. if (*p == 27)
  204. ESCtoAttr(p[1], &gcw->startattr);
  205. #endif
  206. }
  207. // Was there a newline?
  208. if (*p != '\n')
  209. p = gcw->buffer; // Oops - no newline, just delete one char
  210. else
  211. gcw->g.flags |= GCONSOLE_FLG_OVERRUN; // Mark the overrun
  212. // Delete the data
  213. dp = ++p - gcw->buffer; // Calculate the amount to to be removed
  214. gcw->bufpos -= dp; // Calculate the new size
  215. if (gcw->bufpos)
  216. memcpy(gcw->buffer, p, gcw->bufpos); // Move the rest of the data
  217. }
  218. // Save the character
  219. gcw->buffer[gcw->bufpos++] = c;
  220. }
  221. /**
  222. * Scroll the history buffer by one line
  223. */
  224. static void scrollBuffer(GConsoleObject *gcw) {
  225. char *p, *ep;
  226. size_t dp;
  227. // Only scroll if we need to
  228. if (!gcw->buffer || (gcw->g.flags & GCONSOLE_FLG_NOSTORE))
  229. return;
  230. // If a buffer overrun has been marked don't scroll as we have already
  231. if ((gcw->g.flags & GCONSOLE_FLG_OVERRUN)) {
  232. gcw->g.flags &= ~GCONSOLE_FLG_OVERRUN;
  233. return;
  234. }
  235. // Remove one line from the start
  236. ep = gcw->buffer+gcw->bufpos;
  237. for(p = gcw->buffer; p < ep && *p != '\n'; p++) {
  238. #if GWIN_CONSOLE_ESCSEQ
  239. if (*p == 27)
  240. ESCtoAttr(p[1], &gcw->startattr);
  241. #endif
  242. }
  243. // Was there a newline, if not delete everything.
  244. if (*p != '\n') {
  245. gcw->bufpos = 0;
  246. return;
  247. }
  248. // Delete the data
  249. dp = ++p - gcw->buffer; // Calculate the amount to to be removed
  250. gcw->bufpos -= dp; // Calculate the new size
  251. if (gcw->bufpos)
  252. memcpy(gcw->buffer, p, gcw->bufpos); // Move the rest of the data
  253. }
  254. /**
  255. * Clear the history buffer
  256. */
  257. static void clearBuffer(GConsoleObject *gcw) {
  258. // Only clear if we need to
  259. if (!gcw->buffer || (gcw->g.flags & GCONSOLE_FLG_NOSTORE))
  260. return;
  261. gcw->bufpos = 0;
  262. }
  263. #else
  264. #define putCharInBuffer(gcw, c)
  265. #define scrollBuffer(gcw)
  266. #define clearBuffer(gcw)
  267. #endif
  268. static void AfterClear(GWindowObject *gh) {
  269. #define gcw ((GConsoleObject *)gh)
  270. gcw->cx = 0;
  271. gcw->cy = 0;
  272. clearBuffer(gcw);
  273. #if GWIN_CONSOLE_ESCSEQ
  274. gcw->startattr = gcw->currattr;
  275. #endif
  276. #undef gcw
  277. }
  278. static const gwinVMT consoleVMT = {
  279. "Console", // The classname
  280. sizeof(GConsoleObject), // The object size
  281. #if GWIN_CONSOLE_USE_HISTORY
  282. HistoryDestroy, // The destroy routine (custom)
  283. HistoryRedraw, // The redraw routine (custom)
  284. #else
  285. 0, // The destroy routine
  286. 0, // The redraw routine (default)
  287. #endif
  288. AfterClear, // The after-clear routine
  289. };
  290. GHandle gwinGConsoleCreate(GDisplay *g, GConsoleObject *gc, const GWindowInit *pInit) {
  291. if (!(gc = (GConsoleObject *)_gwindowCreate(g, &gc->g, pInit, &consoleVMT, 0)))
  292. return 0;
  293. #if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM
  294. gc->stream.vmt = &GWindowConsoleVMT;
  295. #endif
  296. #if GWIN_CONSOLE_USE_HISTORY
  297. gc->buffer = 0;
  298. #if GWIN_CONSOLE_HISTORY_ATCREATE
  299. gwinConsoleSetBuffer(&gc->g, TRUE);
  300. #endif
  301. #endif
  302. gc->cx = 0;
  303. gc->cy = 0;
  304. #if GWIN_CONSOLE_ESCSEQ
  305. gc->startattr = gc->currattr = 0;
  306. gc->escstate = 0;
  307. #endif
  308. gwinSetVisible((GHandle)gc, pInit->show);
  309. _gwinFlushRedraws(REDRAW_WAIT);
  310. return (GHandle)gc;
  311. }
  312. #if GFX_USE_OS_CHIBIOS && GWIN_CONSOLE_USE_BASESTREAM
  313. BaseSequentialStream *gwinConsoleGetStream(GHandle gh) {
  314. if (gh->vmt != &consoleVMT)
  315. return 0;
  316. return (BaseSequentialStream *)&(((GConsoleObject *)(gh))->stream);
  317. }
  318. #endif
  319. #if GWIN_CONSOLE_USE_HISTORY
  320. bool_t gwinConsoleSetBuffer(GHandle gh, bool_t onoff) {
  321. #define gcw ((GConsoleObject *)gh)
  322. if (gh->vmt != &consoleVMT)
  323. return FALSE;
  324. // Do we want the buffer turned off?
  325. if (!onoff) {
  326. if (gcw->buffer) {
  327. gfxFree(gcw->buffer);
  328. gcw->buffer = 0;
  329. }
  330. return FALSE;
  331. }
  332. // Is the buffer already on?
  333. if (gcw->buffer)
  334. return TRUE;
  335. // Get the number of characters that fit in the x direction
  336. #if GWIN_CONSOLE_HISTORY_AVERAGING
  337. gcw->bufsize = gh->width / ((2*gdispGetFontMetric(gh->font, fontMinWidth)+gdispGetFontMetric(gh->font, fontMaxWidth))/3);
  338. #else
  339. gcw->bufsize = gh->width / gdispGetFontMetric(gh->font, fontMinWidth);
  340. #endif
  341. gcw->bufsize++; // Allow space for a newline on each line.
  342. // Multiply by the number of lines
  343. gcw->bufsize *= gh->height / gdispGetFontMetric(gh->font, fontHeight);
  344. // Allocate the buffer
  345. if (!(gcw->buffer = gfxAlloc(gcw->bufsize)))
  346. return FALSE;
  347. // All good!
  348. gh->flags &= ~GCONSOLE_FLG_OVERRUN;
  349. gcw->bufpos = 0;
  350. return TRUE;
  351. #undef gcw
  352. }
  353. #endif
  354. /*
  355. * We can get into gwinPutChar() 2 ways -
  356. * 1. when the user calls us, and
  357. * 2. when the redraw uses us to redraw the display.
  358. * When called by option 2 we MUST not try to obtain a draw session
  359. * as we already have one.
  360. *
  361. * We use these macro's below to make sure we do that safely
  362. */
  363. #define DrawStart(gh) ((gh->flags & GCONSOLE_FLG_NOSTORE) || _gwinDrawStart(gh))
  364. #define DrawEnd(gh) { if (!(gh->flags & GCONSOLE_FLG_NOSTORE)) _gwinDrawEnd(gh); }
  365. void gwinPutChar(GHandle gh, char c) {
  366. #define gcw ((GConsoleObject *)gh)
  367. uint8_t width, fy;
  368. if (gh->vmt != &consoleVMT || !gh->font)
  369. return;
  370. fy = gdispGetFontMetric(gh->font, fontHeight);
  371. #if GWIN_CONSOLE_ESCSEQ
  372. /**
  373. * Handle escape sequences
  374. * ESC color Change subsequent text color
  375. * color: "0" = black, "1" = red, "2" = green, "3" = yellow, "4" = blue,
  376. * "5" = magenta, "6" = cyan, "7" = white
  377. * ESC C Revert subsequent text color to the window default
  378. * ESC u Turn on underline
  379. * ESC U Turn off underline
  380. * ESC b Turn on bold
  381. * ESC B Turn off bold
  382. * ESC J Clear the window
  383. */
  384. switch (gcw->escstate) {
  385. case 1:
  386. gcw->escstate = 0;
  387. if (ESCtoAttr(c, &gcw->currattr)) {
  388. if (gcw->cx == 0 && gcw->cy == 0)
  389. gcw->startattr = gcw->currattr;
  390. else {
  391. putCharInBuffer(gcw, 27);
  392. putCharInBuffer(gcw, c);
  393. }
  394. } else {
  395. switch(c) {
  396. case 'J':
  397. // Clear the console and reset the cursor
  398. clearBuffer(gcw);
  399. if (DrawStart(gh)) {
  400. gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor);
  401. DrawEnd(gh);
  402. }
  403. gcw->cx = 0;
  404. gcw->cy = 0;
  405. gcw->startattr = gcw->currattr;
  406. break;
  407. }
  408. }
  409. return;
  410. }
  411. #endif
  412. /**
  413. * Special Characters:
  414. *
  415. * Carriage returns and line feeds (\r & \n) are handled in unix terminal cooked mode; that is,
  416. * line feeds perform both actions and carriage-returns are ignored.
  417. *
  418. * if GWIN_CONSOLE_ESCSEQ is turned on then ESC is trapped ready for the escape command.
  419. *
  420. * All other characters are treated as printable.
  421. */
  422. switch (c) {
  423. case '\n':
  424. // clear to the end of the line
  425. #if GWIN_CONSOLE_USE_CLEAR_LINES
  426. if (gcw->cx == 0 && gcw->cy+fy < gh->height && DrawStart(gh)) {
  427. gdispGFillArea(gh->display, gh->x, gh->y + gcw->cy, gh->width, fy, gh->bgcolor);
  428. DrawEnd(gh);
  429. }
  430. #endif
  431. // update the cursor
  432. gcw->cx = 0;
  433. gcw->cy += fy;
  434. putCharInBuffer(gcw, '\n');
  435. // We use lazy scrolling here and only scroll when the next char arrives
  436. return;
  437. case '\r':
  438. // gcw->cx = 0;
  439. return;
  440. #if GWIN_CONSOLE_ESCSEQ
  441. case 27: // ESC
  442. gcw->escstate = 1;
  443. return;
  444. #endif
  445. }
  446. // Characters with no width are ignored
  447. if (!(width = gdispGetCharWidth(c, gh->font)))
  448. return;
  449. // Allow space for (very crude) bold
  450. #if GWIN_CONSOLE_ESCSEQ
  451. if ((gcw->currattr & ESC_BOLD))
  452. width++;
  453. #endif
  454. // Do we need to go to the next line to fit this character?
  455. if (gcw->cx + width >= gh->width) {
  456. gcw->cx = 0;
  457. gcw->cy += fy;
  458. putCharInBuffer(gcw, '\n');
  459. }
  460. // Do we need to scroll to fit this character?
  461. if (gcw->cy + fy > gh->height) {
  462. #if GWIN_CONSOLE_USE_HISTORY && GWIN_CONSOLE_BUFFER_SCROLLING
  463. if (gcw->buffer) {
  464. // If flag GCONSOLE_FLG_NOSTORE is set, then do not recursive call of HistoryRedraw - just drop buffer.
  465. if (gh->flags & GCONSOLE_FLG_NOSTORE)
  466. gcw->bufpos = 0;
  467. // Scroll the buffer and then redraw using the buffer
  468. scrollBuffer(gcw);
  469. if (DrawStart(gh)) {
  470. HistoryRedraw(gh);
  471. DrawEnd(gh);
  472. }
  473. } else
  474. #endif
  475. #if GDISP_NEED_SCROLL
  476. {
  477. // Scroll the console using hardware
  478. scrollBuffer(gcw);
  479. if (DrawStart(gh)) {
  480. gdispGVerticalScroll(gh->display, gh->x, gh->y, gh->width, gh->height, fy, gh->bgcolor);
  481. DrawEnd(gh);
  482. }
  483. // Set the cursor to the start of the last line
  484. gcw->cx = 0;
  485. gcw->cy = (((coord_t)(gh->height/fy))-1)*fy;
  486. }
  487. #else
  488. {
  489. // Clear the console and reset the cursor
  490. clearBuffer(gcw);
  491. if (DrawStart(gh)) {
  492. gdispGFillArea(gh->display, gh->x, gh->y, gh->width, gh->height, gh->bgcolor);
  493. DrawEnd(gh);
  494. }
  495. gcw->cx = 0;
  496. gcw->cy = 0;
  497. #if GWIN_CONSOLE_ESCSEQ
  498. gcw->startattr = gcw->currattr;
  499. #endif
  500. }
  501. #endif
  502. }
  503. // Save the char
  504. putCharInBuffer(gcw, c);
  505. // Draw the character
  506. if (DrawStart(gh)) {
  507. // If we are at the beginning of a new line clear the line
  508. #if GWIN_CONSOLE_USE_CLEAR_LINES
  509. if (gcw->cx == 0)
  510. gdispGFillArea(gh->display, gh->x, gh->y + gcw->cy, gh->width, fy, gh->bgcolor);
  511. #endif
  512. #if GWIN_CONSOLE_USE_FILLED_CHARS
  513. gdispGFillChar(gh->display, gh->x + gcw->cx, gh->y + gcw->cy, c, gh->font, ESCPrintColor(gcw), gh->bgcolor);
  514. #else
  515. gdispGDrawChar(gh->display, gh->x + gcw->cx, gh->y + gcw->cy, c, gh->font, ESCPrintColor(gcw));
  516. #endif
  517. #if GWIN_CONSOLE_ESCSEQ
  518. // Draw the underline
  519. if ((gcw->currattr & ESC_UNDERLINE))
  520. gdispGDrawLine(gh->display, gh->x + gcw->cx, gh->y + gcw->cy + fy - gdispGetFontMetric(gh->font, fontDescendersHeight),
  521. gh->x + gcw->cx + width + gdispGetFontMetric(gh->font, fontCharPadding), gh->y + gcw->cy + fy - gdispGetFontMetric(gh->font, fontDescendersHeight),
  522. ESCPrintColor(gcw));
  523. // Bold (very crude)
  524. if ((gcw->currattr & ESC_BOLD))
  525. gdispGDrawChar(gh->display, gh->x + gcw->cx + 1, gh->y + gcw->cy, c, gh->font, ESCPrintColor(gcw));
  526. #endif
  527. DrawEnd(gh);
  528. }
  529. // Update the cursor
  530. gcw->cx += width + gdispGetFontMetric(gh->font, fontCharPadding);
  531. #undef gcw
  532. }
  533. void gwinPutString(GHandle gh, const char *str) {
  534. while(*str)
  535. gwinPutChar(gh, *str++);
  536. }
  537. void gwinPutCharArray(GHandle gh, const char *str, size_t n) {
  538. while(n--)
  539. gwinPutChar(gh, *str++);
  540. }
  541. #include <stdarg.h>
  542. #define MAX_FILLER 11
  543. #define FLOAT_PRECISION 100000
  544. static char *consltoa_wd(char *p, long num, unsigned radix, long divisor) {
  545. int i;
  546. char *q;
  547. if (!divisor) divisor = num;
  548. q = p + MAX_FILLER;
  549. do {
  550. i = (int)(num % radix);
  551. i += '0';
  552. if (i > '9')
  553. i += 'A' - '0' - 10;
  554. *--q = i;
  555. num /= radix;
  556. } while ((divisor /= radix) != 0);
  557. i = (int)(p + MAX_FILLER - q);
  558. do {
  559. *p++ = *q++;
  560. } while (--i);
  561. return p;
  562. }
  563. #if GWIN_CONSOLE_USE_FLOAT
  564. static char *ftoa(char *p, double num) {
  565. long l;
  566. unsigned long precision = FLOAT_PRECISION;
  567. l = num;
  568. p = consltoa_wd(p, l, 10, 0);
  569. *p++ = '.';
  570. l = (num - l) * precision;
  571. return consltoa_wd(p, l, 10, precision / 10);
  572. }
  573. #endif
  574. void gwinPrintf(GHandle gh, const char *fmt, ...) {
  575. va_list ap;
  576. char *p, *s, c, filler;
  577. int i, precision, width;
  578. bool_t is_long, left_align;
  579. long l;
  580. #if GWIN_CONSOLE_USE_FLOAT
  581. float f;
  582. char tmpbuf[2*MAX_FILLER + 1];
  583. #else
  584. char tmpbuf[MAX_FILLER + 1];
  585. #endif
  586. if (gh->vmt != &consoleVMT || !gh->font)
  587. return;
  588. va_start(ap, fmt);
  589. while (TRUE) {
  590. c = *fmt++;
  591. if (c == 0) {
  592. va_end(ap);
  593. return;
  594. }
  595. if (c != '%') {
  596. gwinPutChar(gh, c);
  597. continue;
  598. }
  599. p = tmpbuf;
  600. s = tmpbuf;
  601. left_align = FALSE;
  602. if (*fmt == '-') {
  603. fmt++;
  604. left_align = TRUE;
  605. }
  606. filler = ' ';
  607. if (*fmt == '0') {
  608. fmt++;
  609. filler = '0';
  610. }
  611. width = 0;
  612. while (TRUE) {
  613. c = *fmt++;
  614. if (c >= '0' && c <= '9')
  615. c -= '0';
  616. else if (c == '*')
  617. c = va_arg(ap, int);
  618. else
  619. break;
  620. width = width * 10 + c;
  621. }
  622. precision = 0;
  623. if (c == '.') {
  624. while (TRUE) {
  625. c = *fmt++;
  626. if (c >= '0' && c <= '9')
  627. c -= '0';
  628. else if (c == '*')
  629. c = va_arg(ap, int);
  630. else
  631. break;
  632. precision = precision * 10 + c;
  633. }
  634. }
  635. /* Long modifier.*/
  636. if (c == 'l' || c == 'L') {
  637. is_long = TRUE;
  638. if (*fmt)
  639. c = *fmt++;
  640. }
  641. else
  642. is_long = (c >= 'A') && (c <= 'Z');
  643. /* Command decoding.*/
  644. switch (c) {
  645. case 'c':
  646. filler = ' ';
  647. *p++ = va_arg(ap, int);
  648. break;
  649. case 's':
  650. filler = ' ';
  651. if ((s = va_arg(ap, char *)) == 0)
  652. s = "(null)";
  653. if (precision == 0)
  654. precision = 32767;
  655. for (p = s; *p && (--precision >= 0); p++);
  656. break;
  657. case 'D':
  658. case 'd':
  659. if (is_long)
  660. l = va_arg(ap, long);
  661. else
  662. l = va_arg(ap, int);
  663. if (l < 0) {
  664. *p++ = '-';
  665. l = -l;
  666. }
  667. p = consltoa_wd(p, l, 10, 0);
  668. break;
  669. #if GWIN_CONSOLE_USE_FLOAT
  670. case 'f':
  671. f = (float) va_arg(ap, double);
  672. if (f < 0) {
  673. *p++ = '-';
  674. f = -f;
  675. }
  676. p = ftoa(p, f);
  677. break;
  678. #endif
  679. case 'X':
  680. case 'x':
  681. c = 16;
  682. goto unsigned_common;
  683. case 'U':
  684. case 'u':
  685. c = 10;
  686. goto unsigned_common;
  687. case 'O':
  688. case 'o':
  689. c = 8;
  690. unsigned_common:
  691. if (is_long)
  692. l = va_arg(ap, long);
  693. else
  694. l = va_arg(ap, int);
  695. p = consltoa_wd(p, l, c, 0);
  696. break;
  697. default:
  698. *p++ = c;
  699. break;
  700. }
  701. i = (int)(p - s);
  702. if ((width -= i) < 0)
  703. width = 0;
  704. if (left_align == FALSE)
  705. width = -width;
  706. if (width < 0) {
  707. if (*s == '-' && filler == '0') {
  708. gwinPutChar(gh, *s++);
  709. i--;
  710. }
  711. do {
  712. gwinPutChar(gh, filler);
  713. } while (++width != 0);
  714. }
  715. while (--i >= 0)
  716. gwinPutChar(gh, *s++);
  717. while (width) {
  718. gwinPutChar(gh, filler);
  719. width--;
  720. }
  721. }
  722. }
  723. #endif /* GFX_USE_GWIN && GWIN_NEED_CONSOLE */