| 1 | // This file is part of SmallBASIC |
| 2 | // |
| 3 | // Copyright(C) 2001-2019 Chris Warren-Smith. |
| 4 | // |
| 5 | // This program is distributed under the terms of the GPL v2.0 or later |
| 6 | // Download the GNU Public License (GPL) from www.gnu.org |
| 7 | // |
| 8 | |
| 9 | #include <string.h> |
| 10 | |
| 11 | #include "ui/screen.h" |
| 12 | |
| 13 | #define WHITE 15 |
| 14 | #define SCROLL_IND 4 |
| 15 | #define MAX_HEIGHT 10000 |
| 16 | #define TEXT_ROWS 1000 |
| 17 | |
| 18 | #define DRAW_SHAPE \ |
| 19 | Shape *rect = (*it); \ |
| 20 | if (rect->_y >= _scrollY && \ |
| 21 | rect->_y <= _scrollY + _height) \ |
| 22 | rect->draw(_x + rect->_x, _y + rect->_y - _scrollY, w(), h(), _charWidth) |
| 23 | |
| 24 | int compareZIndex(const void *p1, const void *p2) { |
| 25 | ImageDisplay **i1 = (ImageDisplay **)p1; |
| 26 | ImageDisplay **i2 = (ImageDisplay **)p2; |
| 27 | return (*i1)->_zIndex < (*i2)->_zIndex ? -1 : (*i1)->_zIndex == (*i2)->_zIndex ? 0 : 1; |
| 28 | } |
| 29 | |
| 30 | bool Shape::isFullScreen() const { |
| 31 | MAExtent screenSize = maGetScrSize(); |
| 32 | return _width == EXTENT_X(screenSize) && _height == EXTENT_Y(screenSize); |
| 33 | } |
| 34 | |
| 35 | Screen::Screen(int x, int y, int width, int height, int fontSize) : |
| 36 | Shape(x, y, width, height), |
| 37 | _font(0), |
| 38 | _fontSize(fontSize), |
| 39 | _fontStyle(0), |
| 40 | _charWidth(0), |
| 41 | _charHeight(0), |
| 42 | _scrollX(0), |
| 43 | _scrollY(0), |
| 44 | _bg(0), |
| 45 | _fg(0), |
| 46 | _curX(INITXY), |
| 47 | _curY(INITXY), |
| 48 | _dirty(0), |
| 49 | _linePadding(0) { |
| 50 | } |
| 51 | |
| 52 | Screen::~Screen() { |
| 53 | if (_font) { |
| 54 | maFontDelete(_font); |
| 55 | } |
| 56 | _images.removeAll(); |
| 57 | } |
| 58 | |
| 59 | // converts ANSI colors to MoSync colors |
| 60 | int Screen::ansiToMosync(long c) { |
| 61 | int result; |
| 62 | if (c < 0) { |
| 63 | result = -c; |
| 64 | } else { |
| 65 | result = (c > 15) ? colors[WHITE] : colors[c]; |
| 66 | } |
| 67 | return result; |
| 68 | } |
| 69 | |
| 70 | void Screen::add(Shape *button) { |
| 71 | _shapes.add(button); |
| 72 | if (button->_x + button->_width > _curX) { |
| 73 | _curX = button->_x + button->_width; |
| 74 | } |
| 75 | if (button->_y > _curY) { |
| 76 | _curY = button->_y; |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | void Screen::addImage(ImageDisplay &image) { |
| 81 | bool exists = false; |
| 82 | List_each(ImageDisplay *, it, _images) { |
| 83 | ImageDisplay *next = (*it); |
| 84 | if (next->_id == image._id) { |
| 85 | exists = true; |
| 86 | next->copyImage(image); |
| 87 | break; |
| 88 | } |
| 89 | } |
| 90 | if (!exists) { |
| 91 | _images.add(new ImageDisplay(image)); |
| 92 | } |
| 93 | _images.sort(compareZIndex); |
| 94 | setDirty(); |
| 95 | } |
| 96 | |
| 97 | void Screen::clear() { |
| 98 | _curX = INITXY; |
| 99 | _curY = INITXY; |
| 100 | _scrollX = 0; |
| 101 | _scrollY = 0; |
| 102 | |
| 103 | // cleanup any shapes |
| 104 | _shapes.removeAll(); |
| 105 | _inputs.removeAll(); |
| 106 | _images.removeAll(); |
| 107 | _label.clear(); |
| 108 | } |
| 109 | |
| 110 | void Screen::drawLabel() { |
| 111 | if (!_label.empty()) { |
| 112 | int labelLen = _label.length(); |
| 113 | int w = _charWidth * (labelLen + 2); |
| 114 | int h = _charHeight + 2; |
| 115 | int top = _height - h; |
| 116 | int left = (_width - w) / 2; |
| 117 | int textY = top + ((h - _charHeight) / 2); |
| 118 | |
| 119 | maSetColor(0xbfbfbf); |
| 120 | maFillRect(left, top, w, h); |
| 121 | maSetColor(0xe5e5e5); |
| 122 | maLine(left, top, left + w, top); |
| 123 | maSetColor(0x737373); |
| 124 | maLine(left, top + h - 1, left + w, top + h - 1); |
| 125 | maLine(left + w, top + 1, left + w, top + h - 1); |
| 126 | maSetColor(0x403c44); |
| 127 | maDrawText(left + _charWidth, textY, _label.c_str(), labelLen); |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | void Screen::() { |
| 132 | static const char dot[] = {'\260', '\0'}; |
| 133 | int gap = _charHeight / 3; |
| 134 | int left = _width - _charWidth - (_charWidth / 2); |
| 135 | int top = (_height - _charHeight) + gap; |
| 136 | maSetColor(_fg); |
| 137 | maDrawText(left, top, dot, 1); |
| 138 | maDrawText(left, top - gap, dot, 1); |
| 139 | maDrawText(left, top - gap - gap, dot, 1); |
| 140 | } |
| 141 | |
| 142 | void Screen::drawShape(Shape *rect) { |
| 143 | if (rect != NULL && |
| 144 | rect->_y >= _scrollY && |
| 145 | rect->_y + rect->_height <= _scrollY + _height) { |
| 146 | rect->draw(_x + rect->_x, _y + rect->_y - _scrollY, w(), h(), _charWidth); |
| 147 | |
| 148 | List_each(FormInput *, it, _inputs) { |
| 149 | FormInput *input = (*it); |
| 150 | if (input->isVisible() && |
| 151 | input->isDrawTop() && |
| 152 | input->_y >= _scrollY - _height && |
| 153 | input->_y + input->_height <= _scrollY + _height) { |
| 154 | input->draw(_x + input->_x, _y + input->_y - _scrollY, w(), h(), _charWidth); |
| 155 | } |
| 156 | } |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | void Screen::drawOverlay(bool vscroll) { |
| 161 | // draw any visible shapes |
| 162 | List_each(Shape *, it, _shapes) { |
| 163 | DRAW_SHAPE; |
| 164 | } |
| 165 | |
| 166 | List_each(ImageDisplay *, it, _images) { |
| 167 | DRAW_SHAPE; |
| 168 | } |
| 169 | |
| 170 | FormInput *drawTop = NULL; |
| 171 | List_each(FormInput *, it, _inputs) { |
| 172 | FormInput *input = (*it); |
| 173 | if (input->_y >= _scrollY - _height && |
| 174 | input->isVisible()) { |
| 175 | if (input->isDrawTop()) { |
| 176 | drawTop = input; |
| 177 | } else { |
| 178 | input->draw(_x + input->_x, _y + input->_y - _scrollY, w(), h(), _charWidth); |
| 179 | } |
| 180 | } |
| 181 | } |
| 182 | if (drawTop != NULL) { |
| 183 | drawTop->draw(_x + drawTop->_x, _y + drawTop->_y - _scrollY, w(), h(), _charWidth); |
| 184 | } |
| 185 | |
| 186 | if (vscroll && _curY) { |
| 187 | // display the vertical scrollbar |
| 188 | int pageHeight = _curY + _charHeight + _charHeight; |
| 189 | int barSize = _height * _height / pageHeight; |
| 190 | |
| 191 | if (barSize < _height) { |
| 192 | int barRange = _height - (barSize + SCROLL_IND * 2); |
| 193 | int barTop = SCROLL_IND + (barRange * _scrollY / (pageHeight - _height)); |
| 194 | int barBottom = _y + barTop + barSize; |
| 195 | if (barBottom + SCROLL_IND > _height) { |
| 196 | barBottom = _height - SCROLL_IND; |
| 197 | } |
| 198 | maSetColor(_fg); |
| 199 | maLine(_x + _width - 3, _y + barTop, _x + _width - 3, barBottom); |
| 200 | maLine(_x + _width - 4, _y + barTop, _x + _width - 4, barBottom); |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | drawLabel(); |
| 205 | |
| 206 | #if defined(_FLTK) |
| 207 | drawMenu(); |
| 208 | #else |
| 209 | if ((!_inputs.empty() || !_label.empty()) && isFullScreen()) { |
| 210 | // draw the menu widget when in UI mode |
| 211 | drawMenu(); |
| 212 | } |
| 213 | #endif |
| 214 | } |
| 215 | |
| 216 | void Screen::drawInto(bool background) { |
| 217 | maSetColor(background ? _bg : _fg); |
| 218 | setDirty(); |
| 219 | } |
| 220 | |
| 221 | int Screen::getIndex(FormInput *input) const { |
| 222 | int index; |
| 223 | if (input == NULL) { |
| 224 | index = -1; |
| 225 | } else { |
| 226 | index = 0; |
| 227 | List_each(FormInput *, it, _inputs) { |
| 228 | FormInput *next = (*it); |
| 229 | if (next == input) { |
| 230 | break; |
| 231 | } else { |
| 232 | index++; |
| 233 | } |
| 234 | } |
| 235 | } |
| 236 | return index; |
| 237 | } |
| 238 | |
| 239 | FormInput *Screen::(FormInput *prev, int px, int py) { |
| 240 | FormInput *result = _inputs[0]; |
| 241 | if (result != NULL && overlaps(px, py)) { |
| 242 | int item = (py - _y) / result->_height; |
| 243 | result = _inputs[item]; |
| 244 | } else { |
| 245 | result = NULL; |
| 246 | } |
| 247 | if (result != prev) { |
| 248 | MAHandle currentHandle = maSetDrawTarget(HANDLE_SCREEN); |
| 249 | if (prev != NULL) { |
| 250 | prev->_pressed = false; |
| 251 | drawShape(prev); |
| 252 | } |
| 253 | if (result != NULL) { |
| 254 | result->_pressed = true; |
| 255 | drawShape(result); |
| 256 | } |
| 257 | maUpdateScreen(); |
| 258 | maSetDrawTarget(currentHandle); |
| 259 | } |
| 260 | return result; |
| 261 | } |
| 262 | |
| 263 | FormInput *Screen::(FormInput *prev, bool up) { |
| 264 | int index; |
| 265 | if (prev == NULL) { |
| 266 | index = 0; |
| 267 | } else { |
| 268 | index = getIndex(prev) + (up ? -1 : 1); |
| 269 | } |
| 270 | FormInput *next = prev; |
| 271 | if (index > -1 && index < _inputs.size()) { |
| 272 | MAHandle currentHandle = maSetDrawTarget(HANDLE_SCREEN); |
| 273 | next = _inputs.get(index); |
| 274 | next->_pressed = true; |
| 275 | drawShape(next); |
| 276 | if (prev != NULL) { |
| 277 | prev->_pressed = false; |
| 278 | drawShape(prev); |
| 279 | } |
| 280 | maUpdateScreen(); |
| 281 | maSetDrawTarget(currentHandle); |
| 282 | } |
| 283 | return next; |
| 284 | } |
| 285 | |
| 286 | void Screen::layoutInputs(int newWidth, int newHeight) { |
| 287 | List_each(FormInput *, it, _inputs) { |
| 288 | FormInput *r1 = (*it); |
| 289 | r1->layout(newWidth, newHeight); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | // whether the point overlaps the label text |
| 294 | bool Screen::overLabel(int px, int py) { |
| 295 | bool result; |
| 296 | if (!_label.empty()) { |
| 297 | int w = _charWidth * (_label.length() + 2); |
| 298 | int h = _charHeight + 2; |
| 299 | int top = _height - h; |
| 300 | int left = (_width - w) / 2; |
| 301 | result = (!OUTSIDE_RECT(px, py, left, top, w, h)); |
| 302 | } else { |
| 303 | result = false; |
| 304 | } |
| 305 | return result; |
| 306 | } |
| 307 | |
| 308 | // whether the point overlaps the menu widget |
| 309 | bool Screen::(int px, int py) { |
| 310 | int w = _charWidth * 3; |
| 311 | int h = _charHeight * 2; |
| 312 | return (!OUTSIDE_RECT(px, py, _width - w, _height - h, w, h)); |
| 313 | } |
| 314 | |
| 315 | // whether the given point overlaps with the screen rectangle |
| 316 | bool Screen::overlaps(int px, int py) { |
| 317 | return (!OUTSIDE_RECT(px, py, _x, _y, _width, _height)); |
| 318 | } |
| 319 | |
| 320 | int Screen::print(const char *p, int lineHeight, bool allChars) { |
| 321 | // print minimum of one character |
| 322 | int numChars = 1; |
| 323 | int cx = _charWidth; |
| 324 | int w = _width - 1; |
| 325 | |
| 326 | // print further non-control, non-null characters |
| 327 | // up to the width of the line |
| 328 | while (p[numChars] > 31) { |
| 329 | cx += _charWidth; |
| 330 | if (allChars || _curX + cx < w) { |
| 331 | numChars++; |
| 332 | } else { |
| 333 | break; |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | _curX += cx; |
| 338 | return numChars; |
| 339 | } |
| 340 | |
| 341 | // remove the button from the list |
| 342 | void Screen::remove(Shape *button) { |
| 343 | List_each(Shape *, it, _shapes) { |
| 344 | Shape *next = (*it); |
| 345 | if (next == button) { |
| 346 | _shapes.remove(it); |
| 347 | setDirty(); |
| 348 | break; |
| 349 | } |
| 350 | } |
| 351 | } |
| 352 | |
| 353 | // remove the image from the list |
| 354 | bool Screen::removeInput(FormInput *input) { |
| 355 | bool result = false; |
| 356 | List_each(FormInput *, it, _inputs) { |
| 357 | FormInput *next = (*it); |
| 358 | if (next == input) { |
| 359 | _inputs.remove(it); |
| 360 | result = true; |
| 361 | break; |
| 362 | } |
| 363 | } |
| 364 | return result; |
| 365 | } |
| 366 | |
| 367 | // remove the image from the list |
| 368 | void Screen::removeImage(unsigned imageId) { |
| 369 | List_each(ImageDisplay *, it, _images) { |
| 370 | ImageDisplay *next = (*it); |
| 371 | if (next->_id == imageId) { |
| 372 | _images.remove(it); |
| 373 | delete next; |
| 374 | setDirty(); |
| 375 | break; |
| 376 | } |
| 377 | } |
| 378 | } |
| 379 | |
| 380 | void Screen::replaceFont(int type) { |
| 381 | logEntered(); |
| 382 | if (_font) { |
| 383 | maFontDelete(_font); |
| 384 | } |
| 385 | _font = maFontLoadDefault(type, _fontStyle, _fontSize); |
| 386 | if (_font != -1) { |
| 387 | maFontSetCurrent(_font); |
| 388 | MAExtent extent = maGetTextSize("W" ); |
| 389 | _charWidth = EXTENT_X(extent); |
| 390 | _charHeight = EXTENT_Y(extent) + LINE_SPACING; |
| 391 | } else { |
| 392 | trace("maFontLoadDefault failed: style=%d size=%d" , _fontStyle, _fontSize); |
| 393 | } |
| 394 | } |
| 395 | |
| 396 | void Screen::reset(int fontSize) { |
| 397 | _fg = DEFAULT_FOREGROUND; |
| 398 | _bg = DEFAULT_BACKGROUND; |
| 399 | setFont(false, false, fontSize); |
| 400 | } |
| 401 | |
| 402 | void Screen::setColor(long color) { |
| 403 | _fg = ansiToMosync(color); |
| 404 | } |
| 405 | |
| 406 | void Screen::setTextColor(long foreground, long background) { |
| 407 | _bg = ansiToMosync(background); |
| 408 | _fg = ansiToMosync(foreground); |
| 409 | } |
| 410 | |
| 411 | // updated the current font according to accumulated flags |
| 412 | void Screen::setFont(bool bold, bool italic, int size) { |
| 413 | int style = FONT_STYLE_NORMAL; |
| 414 | int type = FONT_TYPE_MONOSPACE; |
| 415 | |
| 416 | if (italic) { |
| 417 | style |= FONT_STYLE_ITALIC; |
| 418 | type = FONT_TYPE_SERIF; |
| 419 | } |
| 420 | if (bold) { |
| 421 | style |= FONT_STYLE_BOLD; |
| 422 | if (!italic) { |
| 423 | type = FONT_TYPE_SANS_SERIF; |
| 424 | } |
| 425 | } |
| 426 | |
| 427 | if ((style != _fontStyle || size != _fontSize) || !_font) { |
| 428 | _fontStyle = style; |
| 429 | _fontSize = size; |
| 430 | replaceFont(type); |
| 431 | } |
| 432 | } |
| 433 | |
| 434 | FormInput *Screen::getNextField(FormInput *field) { |
| 435 | FormInput *result = NULL; |
| 436 | bool setNext = false; |
| 437 | List_each(FormInput *, it, _inputs) { |
| 438 | FormInput *next = (*it); |
| 439 | if (!next->isNoFocus()) { |
| 440 | if (result == NULL) { |
| 441 | // set result to first item |
| 442 | result = next; |
| 443 | if (field == NULL) { |
| 444 | // no next item |
| 445 | break; |
| 446 | } |
| 447 | } |
| 448 | if (setNext) { |
| 449 | result = next; |
| 450 | break; |
| 451 | } else if (next == field) { |
| 452 | setNext = true; |
| 453 | } |
| 454 | } |
| 455 | } |
| 456 | return result; |
| 457 | } |
| 458 | |
| 459 | void Screen::updateInputs(var_p_t form, bool setVars) { |
| 460 | List_each(FormInput *, it, _inputs) { |
| 461 | FormInput *next = (*it); |
| 462 | if (setVars) { |
| 463 | next->updateField(form); |
| 464 | } else { |
| 465 | var_p_t field = next->getField(form); |
| 466 | if (field == NULL) { |
| 467 | _inputs.remove(it); |
| 468 | delete next; |
| 469 | setDirty(); |
| 470 | it--; |
| 471 | } else if (next->updateUI(form, field)) { |
| 472 | setDirty(); |
| 473 | } |
| 474 | } |
| 475 | } |
| 476 | } |
| 477 | |
| 478 | // |
| 479 | // Graphics and text based screen with limited scrollback support |
| 480 | // |
| 481 | GraphicScreen::GraphicScreen(int width, int height, int fontSize) : |
| 482 | Screen(0, 0, width, height, fontSize), |
| 483 | _image(0), |
| 484 | _underline(0), |
| 485 | _invert(0), |
| 486 | _bold(0), |
| 487 | _italic(0), |
| 488 | _imageWidth(width), |
| 489 | _imageHeight(height), |
| 490 | _curYSaved(0), |
| 491 | _curXSaved(0), |
| 492 | _tabSize(40) { // tab size in pixels (160/32 = 5) |
| 493 | } |
| 494 | |
| 495 | GraphicScreen::~GraphicScreen() { |
| 496 | if (_image) { |
| 497 | maDestroyPlaceholder(_image); |
| 498 | } |
| 499 | } |
| 500 | |
| 501 | // calculate the pixel movement for the given cursor position |
| 502 | void GraphicScreen::calcTab() { |
| 503 | int c = 1; |
| 504 | int x = _curX + 1; |
| 505 | while (x > _tabSize) { |
| 506 | x -= _tabSize; |
| 507 | c++; |
| 508 | } |
| 509 | _curX = c * _tabSize; |
| 510 | } |
| 511 | |
| 512 | bool GraphicScreen::construct() { |
| 513 | bool result = true; |
| 514 | _image = maCreatePlaceholder(); |
| 515 | if (maCreateDrawableImage(_image, _imageWidth, _imageHeight) == RES_OK) { |
| 516 | reset(_fontSize); |
| 517 | } else { |
| 518 | result = false; |
| 519 | } |
| 520 | return result; |
| 521 | } |
| 522 | |
| 523 | void GraphicScreen::clear() { |
| 524 | drawInto(true); |
| 525 | maSetColor(_bg); |
| 526 | maFillRect(0, 0, _imageWidth, _imageHeight); |
| 527 | Screen::clear(); |
| 528 | } |
| 529 | |
| 530 | void GraphicScreen::drawArc(int xc, int yc, double r, double start, double end, double aspect) { |
| 531 | drawInto(); |
| 532 | maArc(xc, yc, r, start, end, aspect); |
| 533 | } |
| 534 | |
| 535 | void GraphicScreen::drawBase(bool vscroll, bool update) { |
| 536 | MARect srcRect; |
| 537 | MAPoint2d dstPoint; |
| 538 | srcRect.left = 0; |
| 539 | srcRect.top = _scrollY; |
| 540 | srcRect.width = _width; |
| 541 | srcRect.height = _height; |
| 542 | dstPoint.x = _x; |
| 543 | dstPoint.y = _y; |
| 544 | MAHandle currentHandle = maSetDrawTarget(HANDLE_SCREEN); |
| 545 | maDrawImageRegion(_image, &srcRect, &dstPoint, TRANS_NONE); |
| 546 | |
| 547 | drawOverlay(vscroll); |
| 548 | _dirty = 0; |
| 549 | if (update) { |
| 550 | maUpdateScreen(); |
| 551 | } |
| 552 | maSetDrawTarget(currentHandle); |
| 553 | } |
| 554 | |
| 555 | void GraphicScreen::drawEllipse(int xc, int yc, int rx, int ry, int fill) { |
| 556 | drawInto(); |
| 557 | maEllipse(xc, yc, rx, ry, fill); |
| 558 | } |
| 559 | |
| 560 | void GraphicScreen::drawImage(ImageDisplay &image) { |
| 561 | drawInto(); |
| 562 | image.draw(image._x, image._y, image._width, image._height, 0); |
| 563 | } |
| 564 | |
| 565 | void GraphicScreen::drawInto(bool background) { |
| 566 | maSetDrawTarget(_image); |
| 567 | Screen::drawInto(background); |
| 568 | } |
| 569 | |
| 570 | void GraphicScreen::drawLine(int x1, int y1, int x2, int y2) { |
| 571 | drawInto(); |
| 572 | maLine(x1, y1, x2, y2); |
| 573 | } |
| 574 | |
| 575 | void GraphicScreen::drawRect(int x1, int y1, int x2, int y2) { |
| 576 | drawInto(); |
| 577 | maLine(x1, y1, x2, y1); // top |
| 578 | maLine(x1, y2, x2, y2); // bottom |
| 579 | maLine(x1, y1, x1, y2); // left |
| 580 | maLine(x2, y1, x2, y2); // right |
| 581 | } |
| 582 | |
| 583 | void GraphicScreen::drawRectFilled(int x1, int y1, int x2, int y2) { |
| 584 | drawInto(); |
| 585 | maFillRect(x1, y1, x2 - x1, y2 - y1); |
| 586 | } |
| 587 | |
| 588 | // returns the color of the pixel at the given xy location |
| 589 | int GraphicScreen::getPixel(int x, int y) { |
| 590 | MARect rc; |
| 591 | rc.left = x; |
| 592 | rc.top = y; |
| 593 | rc.width = 1; |
| 594 | rc.height = 1; |
| 595 | int data[1]; |
| 596 | |
| 597 | if (x < 0 || y < 0) { |
| 598 | rc.left = x < 0 ? -x : x; |
| 599 | rc.top = y < 0 ? -y : y; |
| 600 | drawBase(false); |
| 601 | maGetImageData(HANDLE_SCREEN, &data, &rc, 1); |
| 602 | } else { |
| 603 | maGetImageData(_image, &data, &rc, 1); |
| 604 | } |
| 605 | return -(data[0] & 0x00FFFFFF); |
| 606 | } |
| 607 | |
| 608 | // extend the image to allow for additional content on the newline |
| 609 | void GraphicScreen::imageAppend(MAHandle newImage) { |
| 610 | MARect srcRect; |
| 611 | MAPoint2d dstPoint; |
| 612 | |
| 613 | srcRect.left = 0; |
| 614 | srcRect.top = 0; |
| 615 | srcRect.width = _imageWidth; |
| 616 | srcRect.height = _imageHeight; |
| 617 | dstPoint.x = 0; |
| 618 | dstPoint.y = 0; |
| 619 | |
| 620 | maSetDrawTarget(newImage); |
| 621 | maDrawImageRegion(_image, &srcRect, &dstPoint, TRANS_NONE); |
| 622 | |
| 623 | // clear the new segment |
| 624 | maSetColor(_bg); |
| 625 | maFillRect(0, _imageHeight, _imageWidth, _imageHeight + _height); |
| 626 | _imageHeight += _height; |
| 627 | |
| 628 | // cleanup the old image |
| 629 | maDestroyPlaceholder(_image); |
| 630 | _image = newImage; |
| 631 | } |
| 632 | |
| 633 | // scroll back the image to allow for additioal content on the newline |
| 634 | void GraphicScreen::imageScroll() { |
| 635 | MAHandle newImage = maCreatePlaceholder(); |
| 636 | int newHeight = _imageHeight; |
| 637 | if (maCreateDrawableImage(newImage, _imageWidth, newHeight) == RES_OK) { |
| 638 | MARect srcRect; |
| 639 | MAPoint2d dstPoint; |
| 640 | int scrollBack = _height; |
| 641 | int copiedHeight = _imageHeight - scrollBack; |
| 642 | |
| 643 | srcRect.left = 0; |
| 644 | srcRect.top = scrollBack; |
| 645 | srcRect.width = _imageWidth; |
| 646 | srcRect.height = copiedHeight; |
| 647 | dstPoint.x = 0; |
| 648 | dstPoint.y = 0; |
| 649 | |
| 650 | maSetDrawTarget(newImage); |
| 651 | maDrawImageRegion(_image, &srcRect, &dstPoint, TRANS_NONE); |
| 652 | |
| 653 | // clear the new segment |
| 654 | maSetColor(_bg); |
| 655 | maFillRect(0, copiedHeight, _imageWidth, scrollBack); |
| 656 | |
| 657 | // cleanup the old image |
| 658 | maDestroyPlaceholder(_image); |
| 659 | _image = newImage; |
| 660 | _scrollY -= scrollBack; |
| 661 | _curY -= scrollBack; |
| 662 | } else { |
| 663 | // unable to create duplicate |
| 664 | maDestroyPlaceholder(newImage); |
| 665 | clear(); |
| 666 | } |
| 667 | } |
| 668 | |
| 669 | // handles the \n character |
| 670 | void GraphicScreen::newLine(int lineHeight) { |
| 671 | lineHeight += _linePadding; |
| 672 | _linePadding = 0; |
| 673 | _curX = INITXY; |
| 674 | if (_height < MAX_HEIGHT) { |
| 675 | int offset = _curY + (lineHeight * 2); |
| 676 | if (offset >= _height) { |
| 677 | if (offset >= _imageHeight) { |
| 678 | // extend the base image by another page size |
| 679 | MAHandle newImage = maCreatePlaceholder(); |
| 680 | int newHeight = _imageHeight + _height; |
| 681 | if (maCreateDrawableImage(newImage, _imageWidth, newHeight) != RES_OK) { |
| 682 | // maximum image size reached |
| 683 | maDestroyPlaceholder(newImage); |
| 684 | imageScroll(); |
| 685 | lineHeight = 0; |
| 686 | } else { |
| 687 | imageAppend(newImage); |
| 688 | } |
| 689 | } |
| 690 | _scrollY += lineHeight; |
| 691 | } |
| 692 | _curY += lineHeight; |
| 693 | } else { |
| 694 | // overflow |
| 695 | clear(); |
| 696 | } |
| 697 | } |
| 698 | |
| 699 | int GraphicScreen::print(const char *p, int lineHeight, bool allChars) { |
| 700 | if (_curX + _charWidth >= _width - 1) { |
| 701 | newLine(lineHeight); |
| 702 | } |
| 703 | |
| 704 | int cx = _curX; |
| 705 | int numChars = Screen::print(p, lineHeight); |
| 706 | |
| 707 | // erase the background |
| 708 | maSetColor(_invert ? _fg : _bg); |
| 709 | maFillRect(cx, _curY, _curX-cx, lineHeight); |
| 710 | |
| 711 | // draw the text buffer |
| 712 | maSetColor(_invert ? _bg : _fg); |
| 713 | maDrawText(cx, _curY, p, numChars); |
| 714 | |
| 715 | if (_underline) { |
| 716 | maLine(cx, _curY + lineHeight - 2, _curX, _curY + lineHeight - 2); |
| 717 | } |
| 718 | |
| 719 | return numChars; |
| 720 | } |
| 721 | |
| 722 | // reset the current drawing variables |
| 723 | void GraphicScreen::reset(int fontSize) { |
| 724 | Screen::reset(fontSize); |
| 725 | _curXSaved = 0; |
| 726 | _curYSaved = 0; |
| 727 | _invert = false; |
| 728 | _underline = false; |
| 729 | _bold = false; |
| 730 | _italic = false; |
| 731 | } |
| 732 | |
| 733 | // update the widget to new dimensions |
| 734 | void GraphicScreen::resize(int newWidth, int newHeight, int oldWidth, |
| 735 | int oldHeight, int lineHeight) { |
| 736 | logEntered(); |
| 737 | bool fullscreen = ((_width - _x) == oldWidth && (_height - _y) == oldHeight); |
| 738 | if (fullscreen && (newWidth > _imageWidth || newHeight > _imageHeight)) { |
| 739 | // screen is larger than existing virtual size |
| 740 | MARect srcRect; |
| 741 | MAPoint2d dstPoint; |
| 742 | MAHandle newImage = maCreatePlaceholder(); |
| 743 | int newImageWidth = MAX(newWidth, _imageWidth); |
| 744 | int newImageHeight = MAX(newHeight, _imageHeight); |
| 745 | |
| 746 | srcRect.left = 0; |
| 747 | srcRect.top = 0; |
| 748 | srcRect.width = MIN(_imageWidth, newImageWidth); |
| 749 | srcRect.height = MIN(_imageHeight, newImageHeight); |
| 750 | dstPoint.x = 0; |
| 751 | dstPoint.y = 0; |
| 752 | |
| 753 | if (maCreateDrawableImage(newImage, newImageWidth, newImageHeight) == RES_OK) { |
| 754 | maSetDrawTarget(newImage); |
| 755 | maSetColor(_bg); |
| 756 | maFillRect(0, 0, newImageWidth, newImageHeight); |
| 757 | maDrawImageRegion(_image, &srcRect, &dstPoint, TRANS_NONE); |
| 758 | maDestroyPlaceholder(_image); |
| 759 | } else { |
| 760 | // cannot resize - alert and abort |
| 761 | deviceLog("Failed to resize to %d %d" , newImageWidth, newImageHeight); |
| 762 | abort(); |
| 763 | } |
| 764 | |
| 765 | _image = newImage; |
| 766 | _imageWidth = newImageWidth; |
| 767 | _imageHeight = newImageHeight; |
| 768 | |
| 769 | if (_curY >= _imageHeight) { |
| 770 | _curY = _height - lineHeight; |
| 771 | } |
| 772 | if (_curX >= _imageWidth) { |
| 773 | _curX = 0; |
| 774 | } |
| 775 | } |
| 776 | _scrollY = 0; |
| 777 | _width = newWidth; |
| 778 | _height = newHeight; |
| 779 | if (!fullscreen) { |
| 780 | drawBase(false); |
| 781 | } |
| 782 | layoutInputs(newWidth, newHeight); |
| 783 | } |
| 784 | |
| 785 | void GraphicScreen::updateFont(int size) { |
| 786 | setFont(_bold, _italic, size > 0 ? size : _fontSize); |
| 787 | } |
| 788 | |
| 789 | // handles the given escape character. Returns whether the font has changed |
| 790 | bool GraphicScreen::setGraphicsRendition(const char c, int escValue, int lineHeight) { |
| 791 | switch (c) { |
| 792 | case 'K': |
| 793 | maSetColor(_bg); // \e[K - clear to eol |
| 794 | maFillRect(_curX, _curY, _width - _curX, lineHeight); |
| 795 | break; |
| 796 | case 'G': // move to column |
| 797 | _curX = escValue * _charWidth; |
| 798 | break; |
| 799 | case 's': // save cursor position |
| 800 | _curYSaved = _curX; |
| 801 | _curXSaved = _curY; |
| 802 | break; |
| 803 | case 'u': // restore cursor position |
| 804 | _curX = _curYSaved; |
| 805 | _curY = _curXSaved; |
| 806 | break; |
| 807 | case ';': // fallthru |
| 808 | case 'm': // \e[...m - ANSI terminal |
| 809 | switch (escValue) { |
| 810 | case 0: // reset |
| 811 | reset(_fontSize); |
| 812 | break; |
| 813 | case 1: // set bold on |
| 814 | _bold = true; |
| 815 | return true; |
| 816 | case 2: // set faint on |
| 817 | break; |
| 818 | case 3: // set italic on |
| 819 | _italic = true; |
| 820 | return true; |
| 821 | case 4: // set underline on |
| 822 | _underline = true; |
| 823 | break; |
| 824 | case 5: // set blink on |
| 825 | break; |
| 826 | case 6: // rapid blink on |
| 827 | break; |
| 828 | case 7: // reverse video on |
| 829 | _invert = true; |
| 830 | break; |
| 831 | case 8: // conceal on |
| 832 | break; |
| 833 | case 21: // set bold off |
| 834 | _bold = false; |
| 835 | return true; |
| 836 | case 23: |
| 837 | _italic = false; |
| 838 | return true; |
| 839 | case 24: // set underline off |
| 840 | _underline = false; |
| 841 | break; |
| 842 | case 27: // reverse video off |
| 843 | _invert = false; |
| 844 | break; |
| 845 | // colors - 30..37 foreground, 40..47 background |
| 846 | case 30: // set black fg |
| 847 | _fg = ansiToMosync(0); |
| 848 | break; |
| 849 | case 31: // set red fg |
| 850 | _fg = ansiToMosync(4); |
| 851 | break; |
| 852 | case 32: // set green fg |
| 853 | _fg = ansiToMosync(2); |
| 854 | break; |
| 855 | case 33: // set yellow fg |
| 856 | _fg = ansiToMosync(6); |
| 857 | break; |
| 858 | case 34: // set blue fg |
| 859 | _fg = ansiToMosync(1); |
| 860 | break; |
| 861 | case 35: // set magenta fg |
| 862 | _fg = ansiToMosync(5); |
| 863 | break; |
| 864 | case 36: // set cyan fg |
| 865 | _fg = ansiToMosync(3); |
| 866 | break; |
| 867 | case 37: // set white fg |
| 868 | _fg = ansiToMosync(7); |
| 869 | break; |
| 870 | case 40: // set black bg |
| 871 | _bg = ansiToMosync(0); |
| 872 | break; |
| 873 | case 41: // set red bg |
| 874 | _bg = ansiToMosync(4); |
| 875 | break; |
| 876 | case 42: // set green bg |
| 877 | _bg = ansiToMosync(2); |
| 878 | break; |
| 879 | case 43: // set yellow bg |
| 880 | _bg = ansiToMosync(6); |
| 881 | break; |
| 882 | case 44: // set blue bg |
| 883 | _bg = ansiToMosync(1); |
| 884 | break; |
| 885 | case 45: // set magenta bg |
| 886 | _bg = ansiToMosync(5); |
| 887 | break; |
| 888 | case 46: // set cyan bg |
| 889 | _bg = ansiToMosync(3); |
| 890 | break; |
| 891 | case 47: // set white bg |
| 892 | _bg = ansiToMosync(15); |
| 893 | break; |
| 894 | case 48: // subscript on |
| 895 | break; |
| 896 | case 49: // superscript |
| 897 | break; |
| 898 | }; |
| 899 | } |
| 900 | return false; |
| 901 | } |
| 902 | |
| 903 | void GraphicScreen::setPixel(int x, int y, int c) { |
| 904 | drawInto(); |
| 905 | maSetColor(ansiToMosync(c)); |
| 906 | maPlot(x, y); |
| 907 | } |
| 908 | |
| 909 | struct LineShape : Shape { |
| 910 | LineShape(int x, int y, int w, int h) : Shape(x, y, w, h) {} |
| 911 | void draw(int ax, int ay, int, int, int) { |
| 912 | maLine(_x, _y, _width, _height); |
| 913 | } |
| 914 | }; |
| 915 | |
| 916 | struct RectShape : Shape { |
| 917 | RectShape(int x, int y, int w, int h) : Shape(x, y, w, h) {} |
| 918 | void draw(int ax, int ay, int, int, int) { |
| 919 | int x1 = _x; |
| 920 | int y1 = _y; |
| 921 | int x2 = _x + _width; |
| 922 | int y2 = _y + _width; |
| 923 | maLine(x1, y1, x2, y1); // top |
| 924 | maLine(x1, y2, x2, y2); // bottom |
| 925 | maLine(x1, y1, x1, y2); // left |
| 926 | maLine(x2, y1, x2, y2); // right |
| 927 | } |
| 928 | }; |
| 929 | |
| 930 | struct RectFilledShape : Shape { |
| 931 | RectFilledShape(int x, int y, int w, int h) : Shape(x, y, w, h) {} |
| 932 | void draw(int ax, int ay, int, int, int) { |
| 933 | maFillRect(_x, _y, _width, _height); |
| 934 | } |
| 935 | }; |
| 936 | |
| 937 | // |
| 938 | // Text based screen with a large scrollback buffer |
| 939 | // |
| 940 | TextScreen::TextScreen(int width, int height, int fontSize) : |
| 941 | Screen(0, 0, width, height, fontSize), |
| 942 | _over(NULL), |
| 943 | _inset(0, 0, 0, 0), |
| 944 | _buffer(NULL), |
| 945 | _head(0), |
| 946 | _tail(0), |
| 947 | _rows(TEXT_ROWS), |
| 948 | _cols(0) { |
| 949 | } |
| 950 | |
| 951 | TextScreen::~TextScreen() { |
| 952 | delete[] _buffer; |
| 953 | } |
| 954 | |
| 955 | void TextScreen::calcTab() { |
| 956 | Row *line = getLine(_head); // pointer to current line |
| 957 | line->tab(); |
| 958 | } |
| 959 | |
| 960 | bool TextScreen::construct() { |
| 961 | reset(_fontSize); |
| 962 | _buffer = new Row[_rows]; |
| 963 | return (_buffer != NULL); |
| 964 | } |
| 965 | |
| 966 | // |
| 967 | // clear the screen |
| 968 | // |
| 969 | void TextScreen::clear() { |
| 970 | _head = _tail = _cols = 0; |
| 971 | getLine(0)->clear(); |
| 972 | Screen::clear(); |
| 973 | } |
| 974 | |
| 975 | // |
| 976 | // draw the text |
| 977 | // |
| 978 | void TextScreen::drawBase(bool vscroll, bool update) { |
| 979 | // prepare escape state variables |
| 980 | bool bold = false; |
| 981 | bool italic = false; |
| 982 | bool underline = false; |
| 983 | bool invert = false; |
| 984 | int color = DEFAULT_FOREGROUND; |
| 985 | |
| 986 | // calculate rows to display |
| 987 | int = getPageRows(); |
| 988 | int textRows = getTextRows(); |
| 989 | int numRows = textRows < pageRows ? textRows : pageRows; |
| 990 | int firstRow = _tail + (_scrollY / _charHeight); |
| 991 | int yoffs = _scrollY % _charHeight; // smooth scrolling offset |
| 992 | |
| 993 | // prevent drawing beyond available text |
| 994 | if (numRows > textRows - firstRow) { |
| 995 | numRows = textRows - firstRow; |
| 996 | } |
| 997 | |
| 998 | if (_over != NULL && _over != this) { |
| 999 | _over->drawBase(vscroll, false); |
| 1000 | } |
| 1001 | |
| 1002 | // setup the background colour |
| 1003 | MAHandle currentHandle = maSetDrawTarget(HANDLE_SCREEN_BUFFER); |
| 1004 | maSetColor(_bg); |
| 1005 | maFillRect(_x, _y, _width, _height); |
| 1006 | maSetColor(color); |
| 1007 | |
| 1008 | // draw the visible segments |
| 1009 | int pageWidth = 0; |
| 1010 | for (int row = firstRow, rows = 0, py = _y - yoffs; |
| 1011 | rows < numRows; |
| 1012 | row++, rows++, py += _charHeight) { |
| 1013 | Row *line = getLine(row); // next logical row |
| 1014 | TextSeg *seg = line->_head; |
| 1015 | int px = (_x + INITXY) - _scrollX; |
| 1016 | while (seg != NULL) { |
| 1017 | if (seg->escape(&bold, &italic, &underline, &invert)) { |
| 1018 | setFont(bold, italic, _fontSize); |
| 1019 | } else if (seg->isReset()) { |
| 1020 | reset(_fontSize); |
| 1021 | bold = false; |
| 1022 | italic = false; |
| 1023 | underline = false; |
| 1024 | invert = false; |
| 1025 | color = DEFAULT_FOREGROUND; |
| 1026 | maSetColor(color); |
| 1027 | } |
| 1028 | if (seg->_color != NO_COLOR) { |
| 1029 | color = seg->_color; |
| 1030 | maSetColor(color); |
| 1031 | } |
| 1032 | int width = seg->width(); |
| 1033 | if (seg->_str) { |
| 1034 | if (invert) { |
| 1035 | maSetColor(_fg); |
| 1036 | maFillRect(px, py, width, _charHeight); |
| 1037 | maSetColor(_bg); |
| 1038 | maDrawText(px, py, seg->_str, seg->numChars()); |
| 1039 | maSetColor(color); |
| 1040 | } else { |
| 1041 | maDrawText(px, py, seg->_str, seg->numChars()); |
| 1042 | } |
| 1043 | if (underline) { |
| 1044 | maLine(px, py + _charHeight, width, py + _charHeight); |
| 1045 | } |
| 1046 | } |
| 1047 | px += width; |
| 1048 | seg = seg->_next; |
| 1049 | } |
| 1050 | int rowWidth = line->width(); |
| 1051 | if (rowWidth > pageWidth) { |
| 1052 | pageWidth = rowWidth; |
| 1053 | } |
| 1054 | } |
| 1055 | |
| 1056 | // draw the base components |
| 1057 | drawOverlay(vscroll); |
| 1058 | _dirty = 0; |
| 1059 | maUpdateScreen(); |
| 1060 | maSetDrawTarget(currentHandle); |
| 1061 | } |
| 1062 | |
| 1063 | void TextScreen::drawLine(int x1, int y1, int x2, int y2) { |
| 1064 | add(new LineShape(x1, y1, x2, y2)); |
| 1065 | } |
| 1066 | |
| 1067 | void TextScreen::drawRect(int x1, int y1, int x2, int y2) { |
| 1068 | add(new RectShape(x1, y1, x2, y2)); |
| 1069 | } |
| 1070 | |
| 1071 | void TextScreen::drawRectFilled(int x1, int y1, int x2, int y2) { |
| 1072 | add(new RectFilledShape(x1, y1, x2 - x1, y2 - y1)); |
| 1073 | } |
| 1074 | |
| 1075 | // |
| 1076 | // return a pointer to the specified line of the display. |
| 1077 | // |
| 1078 | Row *TextScreen::getLine(int pos) { |
| 1079 | if (pos < 0) { |
| 1080 | pos += _rows; |
| 1081 | } |
| 1082 | if (pos > _rows - 1) { |
| 1083 | pos -= _rows; |
| 1084 | } |
| 1085 | |
| 1086 | return &_buffer[pos]; |
| 1087 | } |
| 1088 | |
| 1089 | void TextScreen::inset(int x, int y, int w, int h, Screen *over) { |
| 1090 | _x = over->_x; |
| 1091 | _y = over->_y; |
| 1092 | _width = over->_width; |
| 1093 | _height = over->_height; |
| 1094 | _inset._x = x; |
| 1095 | _inset._y = y; |
| 1096 | _inset._width = w; |
| 1097 | _inset._height = h; |
| 1098 | _over = over; |
| 1099 | resize(_width, _height, 0, 0, 0); |
| 1100 | } |
| 1101 | |
| 1102 | void TextScreen::newLine(int lineHeight) { |
| 1103 | // scroll by moving logical last line |
| 1104 | if (getTextRows() == _rows) { |
| 1105 | _tail = (_tail + 1 >= _rows) ? 0 : _tail + 1; |
| 1106 | } |
| 1107 | _head = (_head + 1 >= _rows) ? 0 : _head + 1; |
| 1108 | |
| 1109 | // clear the new line |
| 1110 | Row* line = getLine(_head); |
| 1111 | line->clear(); |
| 1112 | |
| 1113 | lineHeight += _linePadding; |
| 1114 | _linePadding = 0; |
| 1115 | |
| 1116 | _curX = INITXY; |
| 1117 | _curY += lineHeight; |
| 1118 | } |
| 1119 | |
| 1120 | void TextScreen::resize(int newWidth, int newHeight, int, int, int) { |
| 1121 | if (_inset._width != 0 && _inset._height != 0) { |
| 1122 | _x = newWidth * _inset._x / 100; |
| 1123 | _y = newHeight * _inset._y / 100; |
| 1124 | _width = (newWidth * _inset._width / 100) - _x; |
| 1125 | _height = (newHeight * _inset._height / 100) - _y; |
| 1126 | } else { |
| 1127 | _width = newWidth; |
| 1128 | _height = newHeight; |
| 1129 | } |
| 1130 | layoutInputs(newWidth, newHeight); |
| 1131 | } |
| 1132 | |
| 1133 | // |
| 1134 | // Creates a new segment then prints the line |
| 1135 | // |
| 1136 | int TextScreen::print(const char *p, int lineHeight, bool allChars) { |
| 1137 | Row *line = getLine(_head); |
| 1138 | TextSeg *segment = new TextSeg(); |
| 1139 | line->append(segment); |
| 1140 | |
| 1141 | int numChars = Screen::print(p, lineHeight, true); |
| 1142 | |
| 1143 | // Print the next (possible) line of text |
| 1144 | segment->setText(p, numChars); |
| 1145 | |
| 1146 | // remember the maximum line length |
| 1147 | if (numChars > _cols) { |
| 1148 | _cols = numChars; |
| 1149 | } |
| 1150 | |
| 1151 | return numChars; |
| 1152 | } |
| 1153 | |
| 1154 | // |
| 1155 | // performs the ANSI text SGI function. |
| 1156 | // |
| 1157 | bool TextScreen::setGraphicsRendition(const char c, int escValue, int lineHeight) { |
| 1158 | if (c == ';' || c == 'm') { |
| 1159 | Row *line = getLine(_head); |
| 1160 | TextSeg *segment = line->next(); |
| 1161 | |
| 1162 | if (segment->_flags || segment->_color != NO_COLOR) { |
| 1163 | // avoid overwriting existing flags |
| 1164 | segment = new TextSeg(); |
| 1165 | line->append(segment); |
| 1166 | } |
| 1167 | |
| 1168 | switch (escValue) { |
| 1169 | case 0: |
| 1170 | segment->reset(); |
| 1171 | reset(_fontSize); |
| 1172 | break; |
| 1173 | |
| 1174 | case 1: // Bold on |
| 1175 | segment->set(TextSeg::BOLD, true); |
| 1176 | break; |
| 1177 | |
| 1178 | case 2: // Faint on |
| 1179 | segment->set(TextSeg::BOLD, false); |
| 1180 | break; |
| 1181 | |
| 1182 | case 3: // Italic on |
| 1183 | segment->set(TextSeg::ITALIC, true); |
| 1184 | break; |
| 1185 | |
| 1186 | case 4: // Underscrore |
| 1187 | segment->set(TextSeg::UNDERLINE, true); |
| 1188 | break; |
| 1189 | |
| 1190 | case 7: // reverse video on |
| 1191 | segment->set(TextSeg::INVERT, true); |
| 1192 | break; |
| 1193 | |
| 1194 | case 21: // set bold off |
| 1195 | segment->set(TextSeg::BOLD, false); |
| 1196 | break; |
| 1197 | |
| 1198 | case 23: |
| 1199 | segment->set(TextSeg::ITALIC, false); |
| 1200 | break; |
| 1201 | |
| 1202 | case 24: // set underline off |
| 1203 | segment->set(TextSeg::UNDERLINE, false); |
| 1204 | break; |
| 1205 | |
| 1206 | case 27: // reverse video off |
| 1207 | segment->set(TextSeg::INVERT, false); |
| 1208 | break; |
| 1209 | |
| 1210 | case 30: // Black |
| 1211 | _fg = segment->_color = ansiToMosync(0); |
| 1212 | break; |
| 1213 | |
| 1214 | case 31: // Red |
| 1215 | _fg = segment->_color = ansiToMosync(4); |
| 1216 | break; |
| 1217 | |
| 1218 | case 32: // Green |
| 1219 | _fg = segment->_color = ansiToMosync(2); |
| 1220 | break; |
| 1221 | |
| 1222 | case 33: // Yellow |
| 1223 | _fg = segment->_color = ansiToMosync(6); |
| 1224 | break; |
| 1225 | |
| 1226 | case 34: // Blue |
| 1227 | _fg = segment->_color = ansiToMosync(1); |
| 1228 | break; |
| 1229 | |
| 1230 | case 35: // Magenta |
| 1231 | _fg = segment->_color = ansiToMosync(5); |
| 1232 | break; |
| 1233 | |
| 1234 | case 36: // Cyan |
| 1235 | _fg = segment->_color = ansiToMosync(3); |
| 1236 | break; |
| 1237 | |
| 1238 | case 37: // White |
| 1239 | _fg = segment->_color = ansiToMosync(7); |
| 1240 | break; |
| 1241 | } |
| 1242 | } |
| 1243 | return false; |
| 1244 | } |
| 1245 | |
| 1246 | |