TwoTerm/cpp/Screen.cpp

361 lines
7.8 KiB
C++

/*
* Screen.cpp
* 2Term
*
* Created by Kelvin Sherlock on 7/7/2010.
* Copyright 2010 __MyCompanyName__. All rights reserved.
*
*/
#include "Screen.h"
#include <algorithm>
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<class InputIt, class OutputIt, class FX>
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<char_info> 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<char_info> 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;
}