Inspired by Simon Ford's "Terminal" library, this is a clean-room reimplementation that supports a larger set of the ANSI escape sequences and includes a few handy drawing routines. Useful for making console UIs for your projects. The box-drawing stuff requires your terminal to be set to codepage 850.

Fork of ANSITerm by Dan Summers

Committer:
dansummers
Date:
Tue Sep 18 18:58:19 2012 +0000
Revision:
0:863811463610
Child:
2:a681bda60db7
ANSITerm first release: supports colour and styling, cursor movement, box drawing with Codepage850 box-drawing characters.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
dansummers 0:863811463610 1 /*
dansummers 0:863811463610 2 Copyright (c) 2012 dansummers
dansummers 0:863811463610 3
dansummers 0:863811463610 4 Permission is hereby granted, free of charge, to any person obtaining a copy of
dansummers 0:863811463610 5 this software and associated documentation files (the "Software"), to deal in
dansummers 0:863811463610 6 the Software without restriction, including without limitation the rights to
dansummers 0:863811463610 7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
dansummers 0:863811463610 8 of the Software, and to permit persons to whom the Software is furnished to do
dansummers 0:863811463610 9 so, subject to the following conditions:
dansummers 0:863811463610 10
dansummers 0:863811463610 11 The above copyright notice and this permission notice shall be included in all
dansummers 0:863811463610 12 copies or substantial portions of the Software.
dansummers 0:863811463610 13
dansummers 0:863811463610 14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
dansummers 0:863811463610 15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
dansummers 0:863811463610 16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
dansummers 0:863811463610 17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
dansummers 0:863811463610 18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
dansummers 0:863811463610 19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
dansummers 0:863811463610 20 SOFTWARE.
dansummers 0:863811463610 21 */
dansummers 0:863811463610 22 #include "ANSITerm.h"
dansummers 0:863811463610 23 #include "mbed.h"
dansummers 0:863811463610 24
dansummers 0:863811463610 25 //Box styles
dansummers 0:863811463610 26 /* topleft, topright, bottomleft, bottomright, horiz, vert, uptee, downtee, lefttee, righttee,cross */
dansummers 0:863811463610 27 const char ANSITerm::two_lines[11] = {0xC9,0xBB,0xC8,0xBC,0xCD,0xBA,0xCA,0xCB,0xB9,0xCC,0xCE};
dansummers 0:863811463610 28 const char ANSITerm::one_line[11] = {0xDA,0xBF,0xC0,0xD9,0xC4,0xB3,0xC1,0xC2,0xB4,0xC3,0xC5};
dansummers 0:863811463610 29 const char ANSITerm::simple[11] = {'+','+','+','+','-','|','+','+','+','+','+'};
dansummers 0:863811463610 30
dansummers 0:863811463610 31 ANSITerm::ANSITerm(PinName tx, PinName rx) : Serial(tx, rx) {current_style = 0;}
dansummers 0:863811463610 32
dansummers 0:863811463610 33 /* Device Status Report (Returns CSIn;mR, where n,m is the coordinates of the cursor). */
dansummers 0:863811463610 34 void ANSITerm::ansi_dsr(int* x_coord, int* y_coord)
dansummers 0:863811463610 35 {
dansummers 0:863811463610 36 char _r; bool reading_x = false; bool reading_y = false;
dansummers 0:863811463610 37 this->puts("6n"); wait(0.02);
dansummers 0:863811463610 38
dansummers 0:863811463610 39 /* detect presence of symbol "CMSIS_OS_RTX", and switch on thread safety if we spot it (mutexes, thread.wait instead of wait, etc) */
dansummers 0:863811463610 40 /* TODO: Make thread-safe at some point. */
dansummers 0:863811463610 41 /*Reply format is "\033[<ycoord>;<xcoord>R" */
dansummers 0:863811463610 42 *x_coord = 0; *y_coord = 0;
dansummers 0:863811463610 43 while(this->readable() == 1)
dansummers 0:863811463610 44 {
dansummers 0:863811463610 45 _r = this->getc();
dansummers 0:863811463610 46 switch(_r) {
dansummers 0:863811463610 47 case 033:
dansummers 0:863811463610 48 //ignore, start of DSR
dansummers 0:863811463610 49 break;
dansummers 0:863811463610 50 case '[':
dansummers 0:863811463610 51 //next char starts the y-coordinate
dansummers 0:863811463610 52 reading_y = true;
dansummers 0:863811463610 53 break;
dansummers 0:863811463610 54 default:
dansummers 0:863811463610 55 //this is probably a numeric: check and, if so, subtract 48 to decode the ASCII to int.
dansummers 0:863811463610 56 if((_r>47) && (_r<58)) {
dansummers 0:863811463610 57 if(reading_x == true) {
dansummers 0:863811463610 58 *x_coord = ((*x_coord)*10) + (_r-48);
dansummers 0:863811463610 59 } else if(reading_y == true) {
dansummers 0:863811463610 60 *y_coord = ((*y_coord)*10) + (_r-48);
dansummers 0:863811463610 61 }
dansummers 0:863811463610 62 } else {
dansummers 0:863811463610 63 /* Not a numeric, ignore it.*/
dansummers 0:863811463610 64 }
dansummers 0:863811463610 65 break;
dansummers 0:863811463610 66 case ';':
dansummers 0:863811463610 67 //next char starts the x-coordinate
dansummers 0:863811463610 68 reading_y = false; reading_x = true;
dansummers 0:863811463610 69 break;
dansummers 0:863811463610 70 case 'R':
dansummers 0:863811463610 71 //end of ANSI-DSR reply
dansummers 0:863811463610 72 return;
dansummers 0:863811463610 73 }
dansummers 0:863811463610 74 }
dansummers 0:863811463610 75 }
dansummers 0:863811463610 76
dansummers 0:863811463610 77 /* Select Graphic Rendition - Make the output bold, underlined, coloured... all manner of things.*/
dansummers 0:863811463610 78 void ANSITerm::ansi_sgr(bool reset, char text_style, char text_colour, char background_colour)
dansummers 0:863811463610 79 {
dansummers 0:863811463610 80 bool needs_sep = false;
dansummers 0:863811463610 81 if(reset) { this->putc('0'); }
dansummers 0:863811463610 82 else
dansummers 0:863811463610 83 {
dansummers 0:863811463610 84 /* text style (select many) */
dansummers 0:863811463610 85 // These two are mutually exclusive
dansummers 0:863811463610 86 if((text_style & SGR_BOLD) == SGR_BOLD) { this->putc('1'); }
dansummers 0:863811463610 87 else if ((text_style & SGR_FAINT) == SGR_FAINT){ this->putc('2'); }
dansummers 0:863811463610 88 else{ this->puts("22"); }
dansummers 0:863811463610 89 this->putc(';');
dansummers 0:863811463610 90
dansummers 0:863811463610 91 if((text_style & SGR_ITALIC) == SGR_ITALIC) { this->putc('3'); }
dansummers 0:863811463610 92 else{ this->puts("23"); }
dansummers 0:863811463610 93 this->putc(';');
dansummers 0:863811463610 94
dansummers 0:863811463610 95 if((text_style & SGR_UNDERLINE) == SGR_UNDERLINE) { this->putc('4'); }
dansummers 0:863811463610 96 else{ this->puts("24"); }
dansummers 0:863811463610 97 this->putc(';');
dansummers 0:863811463610 98
dansummers 0:863811463610 99 // These two are mutually exclusive
dansummers 0:863811463610 100 if((text_style & SGR_BLINK_SLOW) == SGR_BLINK_SLOW) { this->putc('5'); }
dansummers 0:863811463610 101 else if ((text_style & SGR_BLINK_RAPID) == SGR_BLINK_RAPID) { this->putc('6'); }
dansummers 0:863811463610 102 else{ this->puts("25"); }
dansummers 0:863811463610 103 this->putc(';');
dansummers 0:863811463610 104
dansummers 0:863811463610 105 if((text_style & SGR_IMAGE_NEGATIVE) == SGR_IMAGE_NEGATIVE) { this->putc('7'); }
dansummers 0:863811463610 106 else{ this->puts("27"); }
dansummers 0:863811463610 107 this->putc(';');
dansummers 0:863811463610 108
dansummers 0:863811463610 109 if((text_style & SGR_CROSSED_OUT) == SGR_CROSSED_OUT) { this->putc('9'); }
dansummers 0:863811463610 110 else{ this->puts("29"); }
dansummers 0:863811463610 111 needs_sep = true;
dansummers 0:863811463610 112
dansummers 0:863811463610 113 /* text colour (select 1) */
dansummers 0:863811463610 114 if(text_colour != 0)
dansummers 0:863811463610 115 {
dansummers 0:863811463610 116 if(needs_sep) { this->putc(';'); }/* Emit a separator if needed. */
dansummers 0:863811463610 117 if ((text_colour & SGR_BLACK) == SGR_BLACK) { this->puts("30"); }
dansummers 0:863811463610 118 else if((text_colour & SGR_RED) == SGR_RED) { this->puts("31"); }
dansummers 0:863811463610 119 else if((text_colour & SGR_GREEN) == SGR_GREEN) { this->puts("32"); }
dansummers 0:863811463610 120 else if((text_colour & SGR_YELLOW) == SGR_YELLOW) { this->puts("33"); }
dansummers 0:863811463610 121 else if((text_colour & SGR_BLUE) == SGR_BLUE) { this->puts("34"); }
dansummers 0:863811463610 122 else if((text_colour & SGR_MAGENTA) == SGR_MAGENTA) { this->puts("35"); }
dansummers 0:863811463610 123 else if((text_colour & SGR_CYAN) == SGR_CYAN) { this->puts("36"); }
dansummers 0:863811463610 124 else if((text_colour & SGR_WHITE) == SGR_WHITE) { this->puts("37"); }
dansummers 0:863811463610 125 }
dansummers 0:863811463610 126 /* background_colour (select 1) */
dansummers 0:863811463610 127 if(background_colour != 0)
dansummers 0:863811463610 128 {
dansummers 0:863811463610 129 if(needs_sep) { this->putc(';'); needs_sep = false; }
dansummers 0:863811463610 130 if ((background_colour & SGR_BLACK) == SGR_BLACK) { this->puts("40"); }
dansummers 0:863811463610 131 else if((background_colour & SGR_RED) == SGR_RED) { this->puts("41"); }
dansummers 0:863811463610 132 else if((background_colour & SGR_GREEN) == SGR_GREEN) { this->puts("42"); }
dansummers 0:863811463610 133 else if((background_colour & SGR_YELLOW) == SGR_YELLOW) { this->puts("43"); }
dansummers 0:863811463610 134 else if((background_colour & SGR_BLUE) == SGR_BLUE) { this->puts("44"); }
dansummers 0:863811463610 135 else if((background_colour & SGR_MAGENTA) == SGR_MAGENTA) { this->puts("45"); }
dansummers 0:863811463610 136 else if((background_colour & SGR_CYAN) == SGR_CYAN) { this->puts("46"); }
dansummers 0:863811463610 137 else if((background_colour & SGR_WHITE) == SGR_WHITE) { this->puts("47"); }
dansummers 0:863811463610 138 }
dansummers 0:863811463610 139 }
dansummers 0:863811463610 140 putc('m');
dansummers 0:863811463610 141 }
dansummers 0:863811463610 142
dansummers 0:863811463610 143 /*
dansummers 0:863811463610 144 Draw a box with the specified top-left and bottom-right points (these are the coords of the box, not its contents!)
dansummers 0:863811463610 145 Style is an array of chars to use to draw the box, [topleft, topright, bottomleft, bottomright, horiz, vert, uptee, downtee, lefttee, righttee,cross].
dansummers 0:863811463610 146 if clear_inner is set, the 'contents' of the box onscreen will be erased. If it's clear, they won't be.
dansummers 0:863811463610 147 */
dansummers 0:863811463610 148 void ANSITerm::draw_box(int x1, int y1, int x2, int y2, const char* style, bool clear_inner)
dansummers 0:863811463610 149 {
dansummers 0:863811463610 150 this->set_cursor_position(x1,y1);
dansummers 0:863811463610 151 //draw the top row
dansummers 0:863811463610 152 this->putc(style[0]); for(int i = x1; i<(x2-1);i++) this->putc(style[4]); this->putc(style[1]);
dansummers 0:863811463610 153
dansummers 0:863811463610 154 for(int i=(y1+1); i<(y2);i++)
dansummers 0:863811463610 155 {
dansummers 0:863811463610 156 this->set_cursor_position(x1,i);
dansummers 0:863811463610 157 this->putc(style[5]);//draw the left-hand side of this row
dansummers 0:863811463610 158 this->set_cursor_position(x2,i);
dansummers 0:863811463610 159 this->putc(style[5]);//draw the right-hand side of this row
dansummers 0:863811463610 160 }
dansummers 0:863811463610 161
dansummers 0:863811463610 162 if(clear_inner)
dansummers 0:863811463610 163 {
dansummers 0:863811463610 164 for(int i = (y1+1);i<(y2);i++)
dansummers 0:863811463610 165 {
dansummers 0:863811463610 166 this->set_cursor_position((x1+1),i); //position at the start of this row
dansummers 0:863811463610 167 for(int j=x1;j<(x2-1);j++) this->putc(' '); //fill it with spaces
dansummers 0:863811463610 168 }
dansummers 0:863811463610 169 }
dansummers 0:863811463610 170
dansummers 0:863811463610 171 this->set_cursor_position(x1,y2);
dansummers 0:863811463610 172 //draw the bottom row
dansummers 0:863811463610 173 this->putc(style[2]); for(int i = x1; i<(x2-1);i++) this->putc(style[4]); this->putc(style[3]);
dansummers 0:863811463610 174 }