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