2010-07-08 22:26:58 +00:00
|
|
|
/*
|
|
|
|
* Screen.cpp
|
|
|
|
* 2Term
|
|
|
|
*
|
|
|
|
* Created by Kelvin Sherlock on 7/7/2010.
|
|
|
|
* Copyright 2010 __MyCompanyName__. All rights reserved.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "Screen.h"
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
2011-01-11 00:29:59 +00:00
|
|
|
|
|
|
|
|
2010-07-08 22:26:58 +00:00
|
|
|
Screen::Screen(unsigned height, unsigned width)
|
|
|
|
{
|
2010-12-23 20:41:58 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
_frame = iRect(0, 0, width, height);
|
2011-01-11 00:29:59 +00:00
|
|
|
|
2010-12-23 20:41:58 +00:00
|
|
|
_screen.resize(height);
|
2010-07-08 22:26:58 +00:00
|
|
|
|
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
for (auto &line : _screen) line.resize(width);
|
2010-07-08 22:26:58 +00:00
|
|
|
}
|
|
|
|
|
2010-07-12 02:18:37 +00:00
|
|
|
Screen::~Screen()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2010-07-08 22:26:58 +00:00
|
|
|
|
|
|
|
void Screen::beginUpdate()
|
|
|
|
{
|
2010-07-09 01:18:30 +00:00
|
|
|
_lock.lock();
|
2010-07-08 22:26:58 +00:00
|
|
|
_updates.clear();
|
2011-01-11 00:29:59 +00:00
|
|
|
_updateCursor = cursor();
|
2010-07-08 22:26:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
iRect Screen::endUpdate()
|
|
|
|
{
|
|
|
|
int maxX = -1;
|
|
|
|
int maxY = -1;
|
2010-12-23 20:41:58 +00:00
|
|
|
int minX = width();
|
|
|
|
int minY = height();
|
2010-07-09 01:18:30 +00:00
|
|
|
|
2011-01-11 00:29:59 +00:00
|
|
|
iPoint c = cursor();
|
2010-07-09 01:18:30 +00:00
|
|
|
|
2011-01-11 00:29:59 +00:00
|
|
|
if (c != _updateCursor)
|
2010-07-12 02:18:37 +00:00
|
|
|
{
|
2011-01-11 00:29:59 +00:00
|
|
|
_updates.push_back(c);
|
2010-07-12 02:18:37 +00:00
|
|
|
_updates.push_back(_updateCursor);
|
|
|
|
}
|
2010-07-08 22:26:58 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
for (auto &point : _updates) {
|
|
|
|
maxX = std::max(maxX, point.x);
|
|
|
|
maxY = std::max(maxY, point.y);
|
2010-07-08 22:26:58 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
minX = std::min(minX, point.x);
|
|
|
|
minY = std::min(minY, point.y);
|
2010-07-08 22:26:58 +00:00
|
|
|
}
|
|
|
|
|
2010-07-09 01:18:30 +00:00
|
|
|
_lock.unlock();
|
|
|
|
|
2010-07-08 22:26:58 +00:00
|
|
|
return iRect(iPoint(minX, minY), iSize(maxX + 1 - minX, maxY + 1 - minY));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-01-11 00:29:59 +00:00
|
|
|
|
2016-07-11 02:25:39 +00:00
|
|
|
|
2017-02-16 18:06:20 +00:00
|
|
|
void Screen::putc(uint8_t c, iPoint cursor, uint8_t flags)
|
2010-07-08 22:26:58 +00:00
|
|
|
{
|
2017-02-16 00:26:45 +00:00
|
|
|
if (!_frame.contains(cursor)) return;
|
2017-02-16 18:06:20 +00:00
|
|
|
if (cursor.x >= _frame.maxX() || cursor.y >= _frame.maxY()) return;
|
2010-07-08 22:26:58 +00:00
|
|
|
|
2011-01-11 00:29:59 +00:00
|
|
|
_updates.push_back(cursor);
|
2017-02-16 18:06:20 +00:00
|
|
|
_screen[cursor.y][cursor.x] = char_info(c, flags);
|
2010-07-08 22:26:58 +00:00
|
|
|
}
|
|
|
|
|
2011-01-11 00:29:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2010-12-20 23:37:02 +00:00
|
|
|
|
2011-01-11 00:29:59 +00:00
|
|
|
#pragma mark -
|
|
|
|
#pragma mark Cursor manipulation.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
#pragma mark -
|
|
|
|
#pragma mark Erase
|
2011-01-11 00:29:59 +00:00
|
|
|
|
2017-02-17 03:54:52 +00:00
|
|
|
void Screen::eraseScreen() { fillScreen(char_info()); }
|
|
|
|
void Screen::eraseRect(iRect rect) { fillRect(rect, char_info()); }
|
|
|
|
|
2011-01-11 00:29:59 +00:00
|
|
|
|
2017-02-17 03:54:52 +00:00
|
|
|
void Screen::fillScreen(char_info ci) {
|
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
for (auto &line : _screen) {
|
2017-02-17 03:54:52 +00:00
|
|
|
std::fill(line.begin(), line.end(), ci);
|
2010-12-20 23:37:02 +00:00
|
|
|
}
|
2017-02-16 00:26:45 +00:00
|
|
|
_updates.push_back(iPoint(0,0));
|
|
|
|
_updates.push_back(iPoint(width() - 1, height() - 1));
|
2010-12-20 23:37:02 +00:00
|
|
|
}
|
2010-07-17 18:21:26 +00:00
|
|
|
|
2017-02-17 03:54:52 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Screen::fillRect(iRect rect, char_info ci) {
|
2011-01-11 00:29:59 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
rect = rect.intersection(_frame);
|
2010-07-08 22:26:58 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
if (!rect.valid()) return;
|
2010-07-08 22:26:58 +00:00
|
|
|
|
2017-02-17 03:54:52 +00:00
|
|
|
if (rect == _frame) return fillScreen(ci);
|
2011-01-11 00:29:59 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
auto yIter = _screen.begin() + rect.origin.y;
|
|
|
|
auto yEnd = yIter + rect.size.height;
|
2011-01-11 00:29:59 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
while (yIter != yEnd) {
|
|
|
|
auto &line = *yIter++;
|
|
|
|
auto xIter = line.begin() + rect.origin.x;
|
|
|
|
auto xEnd = xIter + rect.size.width;
|
|
|
|
|
2017-02-17 03:54:52 +00:00
|
|
|
std::fill(xIter, xEnd, ci);
|
2017-02-16 00:26:45 +00:00
|
|
|
}
|
|
|
|
_updates.push_back(rect.origin);
|
|
|
|
_updates.push_back(iPoint(rect.maxX()-1, rect.maxY()-1));
|
2010-07-08 22:26:58 +00:00
|
|
|
}
|
|
|
|
|
2011-01-11 00:29:59 +00:00
|
|
|
|
2010-07-08 22:26:58 +00:00
|
|
|
|
2011-01-11 00:29:59 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
template< class BidirIt1, class BidirIt2, class FX >
|
|
|
|
BidirIt2 copy_backward(BidirIt1 first, BidirIt1 last, BidirIt2 d_last, FX fx)
|
2010-07-08 22:26:58 +00:00
|
|
|
{
|
2017-02-16 00:26:45 +00:00
|
|
|
while (first != last) {
|
|
|
|
|
|
|
|
fx(*(--last), *(--d_last));
|
|
|
|
}
|
|
|
|
return d_last;
|
2010-07-08 22:26:58 +00:00
|
|
|
}
|
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
template<class InputIt, class OutputIt, class FX>
|
|
|
|
OutputIt copy_forward(InputIt first, InputIt last, OutputIt d_first, FX fx)
|
2010-07-08 22:26:58 +00:00
|
|
|
{
|
2017-02-16 00:26:45 +00:00
|
|
|
while (first != last) {
|
|
|
|
fx( *first, *d_first);
|
|
|
|
++first;
|
|
|
|
++d_first;
|
|
|
|
}
|
|
|
|
return d_first;
|
2010-07-08 22:26:58 +00:00
|
|
|
}
|
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
void Screen::scrollUp()
|
2010-07-21 01:30:31 +00:00
|
|
|
{
|
2017-02-16 00:26:45 +00:00
|
|
|
// 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());
|
2010-07-21 01:30:31 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
auto iter = std::move(_screen.begin() + 1, _screen.end(), _screen.begin());
|
2010-07-21 01:30:31 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
*iter = std::move(tmp);
|
2010-07-21 01:30:31 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
_updates.push_back(iPoint(0,0));
|
|
|
|
_updates.push_back(iPoint(width() - 1, height() - 1));
|
2011-01-12 03:43:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
void Screen::scrollUp(iRect rect)
|
2011-01-12 03:43:41 +00:00
|
|
|
{
|
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
rect = rect.intersection(_frame);
|
2011-01-12 03:43:41 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
if (!rect.valid()) return;
|
2011-01-12 03:43:41 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
if (rect == _frame) return scrollUp();
|
2011-01-12 03:43:41 +00:00
|
|
|
|
2010-07-21 01:30:31 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
auto src = _screen.begin() + rect.minY()+1;
|
|
|
|
auto dest = _screen.begin() + rect.minY();
|
|
|
|
auto end = _screen.begin() + rect.maxY();
|
2010-07-21 01:30:31 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
auto iter = copy_forward(src, end, dest, [=](const auto &src, auto &dest){
|
|
|
|
std::copy(src.begin() + rect.minX(), src.begin() + rect.maxX(), dest.begin());
|
|
|
|
});
|
2010-07-08 22:26:58 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
std::fill(iter->begin() + rect.minX(), iter->begin() + rect.maxX(), char_info());
|
2010-07-17 18:21:26 +00:00
|
|
|
|
|
|
|
|
|
|
|
_updates.push_back(rect.origin);
|
2017-02-16 00:26:45 +00:00
|
|
|
_updates.push_back(iPoint(rect.maxX()-1, rect.maxY()-1));
|
2010-07-17 18:21:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
void Screen::scrollDown()
|
2010-07-08 22:26:58 +00:00
|
|
|
{
|
|
|
|
|
2017-02-18 18:00:18 +00:00
|
|
|
// save the last line (to avoid allocation/deallocation)
|
2017-02-16 00:26:45 +00:00
|
|
|
std::vector<char_info> tmp;
|
|
|
|
std::swap(tmp, _screen.back());
|
|
|
|
std::fill(tmp.begin(), tmp.end(), char_info());
|
2010-12-23 20:41:58 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
auto iter = std::move_backward(_screen.begin(), _screen.end()-1, _screen.end());
|
2010-12-23 20:41:58 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
--iter;
|
|
|
|
*iter = std::move(tmp);
|
|
|
|
|
|
|
|
_updates.push_back(iPoint(0,0));
|
|
|
|
_updates.push_back(iPoint(width() - 1, height() - 1));
|
2011-01-11 00:29:59 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
void Screen::scrollDown(iRect rect)
|
2011-01-11 00:29:59 +00:00
|
|
|
{
|
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
rect = rect.intersection(_frame);
|
2010-12-23 20:41:58 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
if (!rect.valid()) return;
|
2010-12-23 20:41:58 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
if (rect == _frame) return scrollDown();
|
2010-12-23 20:41:58 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
auto src = _screen.begin() + rect.minY();
|
|
|
|
auto end = _screen.begin() + rect.maxY()-1;
|
|
|
|
auto dest = _screen.begin() + rect.maxY();
|
2010-07-08 22:26:58 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
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());
|
2010-07-17 18:21:26 +00:00
|
|
|
|
2011-01-11 00:29:59 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
_updates.push_back(rect.origin);
|
|
|
|
_updates.push_back(iPoint(rect.maxX()-1, rect.maxY()-1));
|
2010-07-17 18:21:26 +00:00
|
|
|
}
|
|
|
|
|
2017-02-18 18:00:18 +00:00
|
|
|
void Screen::scrollLeft(int n) {
|
2010-07-08 22:26:58 +00:00
|
|
|
|
2017-02-18 18:00:18 +00:00
|
|
|
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());
|
2010-07-17 18:21:26 +00:00
|
|
|
|
2017-02-18 18:00:18 +00:00
|
|
|
while (n--) *iter++ = char_info();
|
|
|
|
}
|
2010-07-17 18:21:26 +00:00
|
|
|
|
2017-02-18 18:00:18 +00:00
|
|
|
_updates.push_back(iPoint(0,0));
|
|
|
|
_updates.push_back(iPoint(width() - 1, height() - 1));
|
2010-12-24 20:00:44 +00:00
|
|
|
|
2017-02-18 18:00:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Screen::scrollLeft(iRect rect, int n) {
|
2010-12-24 20:00:44 +00:00
|
|
|
|
2017-02-18 18:00:18 +00:00
|
|
|
if (n <= 0) return;
|
2017-02-16 00:26:45 +00:00
|
|
|
|
2017-02-18 18:00:18 +00:00
|
|
|
rect = rect.intersection(_frame);
|
2017-02-16 00:26:45 +00:00
|
|
|
if (!rect.valid()) return;
|
2017-02-18 18:00:18 +00:00
|
|
|
if (rect == _frame) return scrollLeft(n);
|
|
|
|
|
|
|
|
if (n >= rect.width()) return eraseRect(rect);
|
2010-12-24 20:00:44 +00:00
|
|
|
|
2017-02-18 18:00:18 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2010-12-24 20:00:44 +00:00
|
|
|
|
2017-02-18 18:00:18 +00:00
|
|
|
_updates.push_back(rect.origin);
|
|
|
|
_updates.push_back(iPoint(rect.maxX()-1, rect.maxY()-1));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Screen::scrollRight(int n) {
|
2010-12-24 20:00:44 +00:00
|
|
|
|
2017-02-18 18:00:18 +00:00
|
|
|
if (n <= 0) return;
|
|
|
|
if (n >= _frame.width()) return eraseScreen();
|
2010-12-24 20:00:44 +00:00
|
|
|
|
2017-02-18 18:00:18 +00:00
|
|
|
for (auto &line : _screen) {
|
|
|
|
|
|
|
|
auto iter = std::copy_backward(line.begin(), line.end()-n, line.end());
|
|
|
|
|
|
|
|
while (n--) { --iter; *iter = char_info(); }
|
|
|
|
}
|
2010-12-24 20:00:44 +00:00
|
|
|
|
2017-02-18 18:00:18 +00:00
|
|
|
_updates.push_back(iPoint(0,0));
|
|
|
|
_updates.push_back(iPoint(width() - 1, height() - 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-24 20:00:44 +00:00
|
|
|
|
2010-07-17 18:21:26 +00:00
|
|
|
|
2017-02-18 18:00:18 +00:00
|
|
|
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(xIter, xEnd-n, xEnd);
|
|
|
|
while (n--) { --iter; *iter = char_info(); }
|
2010-07-17 18:21:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
_updates.push_back(rect.origin);
|
|
|
|
_updates.push_back(iPoint(rect.maxX()-1, rect.maxY()-1));
|
2010-07-12 02:18:37 +00:00
|
|
|
}
|
|
|
|
|
2010-07-17 18:21:26 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2010-12-23 20:41:58 +00:00
|
|
|
void Screen::setSize(unsigned w, unsigned h)
|
2010-07-12 02:18:37 +00:00
|
|
|
{
|
2011-01-11 00:29:59 +00:00
|
|
|
// TODO -- have separate minimum size for textport?
|
2010-07-12 02:18:37 +00:00
|
|
|
|
2010-12-23 20:41:58 +00:00
|
|
|
if ((height() == h) && (width() == w)) return;
|
2010-07-12 02:18:37 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
_screen.resize(h);
|
|
|
|
for (auto &line : _screen) {
|
|
|
|
line.resize(w);
|
2010-07-12 02:18:37 +00:00
|
|
|
}
|
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
_frame.size = iSize(w, h);
|
2010-07-12 02:18:37 +00:00
|
|
|
|
2017-02-16 00:26:45 +00:00
|
|
|
if (_cursor.y >= h) _cursor.y = h - 1;
|
|
|
|
if (_cursor.x >= w) _cursor.x = w - 1;
|
2010-07-12 02:18:37 +00:00
|
|
|
|
|
|
|
//fprintf(stderr, "setSize(%u, %u)\n", width, height);
|
|
|
|
|
2011-02-05 01:46:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Screen::setCursorType(CursorType cursorType)
|
|
|
|
{
|
|
|
|
_cursorType = cursorType;
|
|
|
|
}
|