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
ANSITerm.cpp@0:863811463610, 2012-09-18 (annotated)
- 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?
User | Revision | Line number | New 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 | } |