/* * Screen.cpp * 2Term * * Created by Kelvin Sherlock on 7/7/2010. * Copyright 2010 __MyCompanyName__. All rights reserved. * */ #include "Screen.h" #include Screen::Screen(unsigned height, unsigned width) { _frame = iRect(0, 0, width, height); _screen.resize(height); for (auto &line : _screen) line.resize(width); } Screen::~Screen() { } void Screen::beginUpdate() { _lock.lock(); _updates.clear(); _updateCursor = cursor(); } iRect Screen::endUpdate() { int maxX = -1; int maxY = -1; int minX = width(); int minY = height(); iPoint c = cursor(); if (c != _updateCursor) { _updates.push_back(c); _updates.push_back(_updateCursor); } for (auto &point : _updates) { maxX = std::max(maxX, point.x); maxY = std::max(maxY, point.y); minX = std::min(minX, point.x); minY = std::min(minY, point.y); } _lock.unlock(); return iRect(iPoint(minX, minY), iSize(maxX + 1 - minX, maxY + 1 - minY)); } void Screen::putc(uint8_t c, iPoint cursor, uint8_t flags) { if (!_frame.contains(cursor)) return; if (cursor.x >= _frame.maxX() || cursor.y >= _frame.maxY()) return; _updates.push_back(cursor); _screen[cursor.y][cursor.x] = char_info(c, flags); } #pragma mark - #pragma mark Cursor manipulation. #pragma mark - #pragma mark Erase void Screen::eraseScreen() { fillScreen(char_info()); } void Screen::eraseRect(iRect rect) { fillRect(rect, char_info()); } void Screen::fillScreen(char_info ci) { for (auto &line : _screen) { std::fill(line.begin(), line.end(), ci); } _updates.push_back(iPoint(0,0)); _updates.push_back(iPoint(width() - 1, height() - 1)); } void Screen::fillRect(iRect rect, char_info ci) { rect = rect.intersection(_frame); if (!rect.valid()) return; if (rect == _frame) return fillScreen(ci); auto yIter = _screen.begin() + rect.origin.y; auto yEnd = yIter + rect.size.height; while (yIter != yEnd) { auto &line = *yIter++; auto xIter = line.begin() + rect.origin.x; auto xEnd = xIter + rect.size.width; std::fill(xIter, xEnd, ci); } _updates.push_back(rect.origin); _updates.push_back(iPoint(rect.maxX()-1, rect.maxY()-1)); } template< class BidirIt1, class BidirIt2, class FX > BidirIt2 copy_backward(BidirIt1 first, BidirIt1 last, BidirIt2 d_last, FX fx) { while (first != last) { fx(*(--last), *(--d_last)); } return d_last; } template OutputIt copy_forward(InputIt first, InputIt last, OutputIt d_first, FX fx) { while (first != last) { fx( *first, *d_first); ++first; ++d_first; } return d_first; } void Screen::scrollUp() { // save the first line (to avoid allocation/deallocation) std::vector tmp; std::swap(tmp, _screen.front()); std::fill(tmp.begin(), tmp.end(), char_info()); auto iter = std::move(_screen.begin() + 1, _screen.end(), _screen.begin()); *iter = std::move(tmp); _updates.push_back(iPoint(0,0)); _updates.push_back(iPoint(width() - 1, height() - 1)); } void Screen::scrollUp(iRect rect) { rect = rect.intersection(_frame); if (!rect.valid()) return; if (rect == _frame) return scrollUp(); auto src = _screen.begin() + rect.minY()+1; auto dest = _screen.begin() + rect.minY(); auto end = _screen.begin() + rect.maxY(); auto iter = copy_forward(src, end, dest, [=](const auto &src, auto &dest){ std::copy(src.begin() + rect.minX(), src.begin() + rect.maxX(), dest.begin()); }); std::fill(iter->begin() + rect.minX(), iter->begin() + rect.maxX(), char_info()); _updates.push_back(rect.origin); _updates.push_back(iPoint(rect.maxX()-1, rect.maxY()-1)); } void Screen::scrollDown() { // save the last line (to avoid allocation/deallocation) std::vector tmp; std::swap(tmp, _screen.back()); std::fill(tmp.begin(), tmp.end(), char_info()); auto iter = std::move_backward(_screen.begin(), _screen.end()-1, _screen.end()); --iter; *iter = std::move(tmp); _updates.push_back(iPoint(0,0)); _updates.push_back(iPoint(width() - 1, height() - 1)); } void Screen::scrollDown(iRect rect) { rect = rect.intersection(_frame); if (!rect.valid()) return; if (rect == _frame) return scrollDown(); auto src = _screen.begin() + rect.minY(); auto end = _screen.begin() + rect.maxY()-1; auto dest = _screen.begin() + rect.maxY(); auto iter = copy_backward(src, end, dest, [=](const auto &src, auto &dest) { std::copy(src.begin() + rect.minX(), src.begin() + rect.maxX(), dest.begin()); }); --iter; std::fill(iter->begin() + rect.minX(), iter->begin() + rect.maxX(), char_info()); _updates.push_back(rect.origin); _updates.push_back(iPoint(rect.maxX()-1, rect.maxY()-1)); } void Screen::scrollLeft(int n) { if (n <= 0) return; if (n >= _frame.width()) return eraseScreen(); for (auto &line : _screen) { auto iter = std::copy(line.begin() + n, line.end(), line.begin()); while (n--) *iter++ = char_info(); } _updates.push_back(iPoint(0,0)); _updates.push_back(iPoint(width() - 1, height() - 1)); } void Screen::scrollLeft(iRect rect, int n) { if (n <= 0) return; rect = rect.intersection(_frame); if (!rect.valid()) return; if (rect == _frame) return scrollLeft(n); if (n >= rect.width()) return eraseRect(rect); auto yIter = _screen.begin() + rect.minY(); auto yEnd = _screen.begin() + rect.maxY(); for( ; yIter != yEnd; ++yIter) { auto &line = *yIter; auto xIter = line.begin() + rect.minX(); auto xEnd = line.begin() + rect.maxX(); auto iter = std::copy(xIter + n, xEnd, xIter); while (n--) *iter++ = char_info(); } _updates.push_back(rect.origin); _updates.push_back(iPoint(rect.maxX()-1, rect.maxY()-1)); } void Screen::scrollRight(int n) { if (n <= 0) return; if (n >= _frame.width()) return eraseScreen(); for (auto &line : _screen) { auto iter = std::copy_backward(line.begin(), line.end()-n, line.end()); while (n--) { --iter; *iter = char_info(); } } _updates.push_back(iPoint(0,0)); _updates.push_back(iPoint(width() - 1, height() - 1)); } void Screen::scrollRight(iRect rect, int n) { if (n <= 0) return; rect = rect.intersection(_frame); if (!rect.valid()) return; if (rect == _frame) return scrollRight(n); if (n >= rect.width()) return eraseRect(rect); auto yIter = _screen.begin() + rect.minY(); auto yEnd = _screen.begin() + rect.maxY(); for( ; yIter != yEnd; ++yIter) { auto &line = *yIter; auto xIter = line.begin() + rect.minX(); auto xEnd = line.begin() + rect.maxX(); auto iter = std::copy_backward(xIter, xEnd-n, xEnd); while (n--) { --iter; *iter = char_info(); } } _updates.push_back(rect.origin); _updates.push_back(iPoint(rect.maxX()-1, rect.maxY()-1)); } void Screen::setSize(unsigned w, unsigned h) { // TODO -- have separate minimum size for textport? if ((height() == h) && (width() == w)) return; _screen.resize(h); for (auto &line : _screen) { line.resize(w); } _frame.size = iSize(w, h); if (_cursor.y >= h) _cursor.y = h - 1; if (_cursor.x >= w) _cursor.x = w - 1; //fprintf(stderr, "setSize(%u, %u)\n", width, height); } void Screen::setCursorType(CursorType cursorType) { _cursorType = cursorType; }