finalcut/src/fvterm.cpp

3203 lines
86 KiB
C++
Raw Permalink Normal View History

2017-11-04 07:03:53 +01:00
/***********************************************************************
* fvterm.cpp - Virtual terminal implementation *
* *
* This file is part of the FINAL CUT widget toolkit *
2017-11-04 07:03:53 +01:00
* *
* Copyright 2016-2021 Markus Gans *
2017-11-04 07:03:53 +01:00
* *
* FINAL CUT is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation; either version 3 of *
2017-11-04 07:03:53 +01:00
* the License, or (at your option) any later version. *
* *
* FINAL CUT is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
2017-11-04 07:03:53 +01:00
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this program. If not, see *
* <http://www.gnu.org/licenses/>. *
***********************************************************************/
#if defined(__CYGWIN__)
#include <unistd.h> // need for ttyname_r
#endif
#include <numeric>
2017-09-11 03:06:02 +02:00
#include <queue>
#include <string>
#include <vector>
#include "final/fapplication.h"
#include "final/fc.h"
#include "final/fcharmap.h"
#include "final/fcolorpair.h"
#include "final/fkeyboard.h"
2020-05-13 23:47:14 +02:00
#include "final/flog.h"
#include "final/fmouse.h"
#include "final/foptiattr.h"
#include "final/foptimove.h"
#include "final/fstyle.h"
#include "final/fsystem.h"
2018-10-29 00:45:45 +01:00
#include "final/fterm.h"
2019-02-24 00:25:36 +01:00
#include "final/ftermbuffer.h"
#include "final/ftermdata.h"
#include "final/ftermdetection.h"
#include "final/ftermfreebsd.h"
#include "final/ftermcap.h"
#include "final/ftypes.h"
#include "final/fvterm.h"
namespace finalcut
{
// static class attributes
2020-10-20 16:52:25 +02:00
bool FVTerm::draw_completed{false};
bool FVTerm::combined_char_support{false};
2020-04-02 09:59:34 +02:00
bool FVTerm::no_terminal_updates{false};
2020-06-06 21:10:06 +02:00
bool FVTerm::cursor_hideable{false};
bool FVTerm::force_terminal_update{false};
uInt64 FVTerm::flush_wait{MIN_FLUSH_WAIT};
uInt64 FVTerm::flush_average{MIN_FLUSH_WAIT};
uInt64 FVTerm::flush_median{MIN_FLUSH_WAIT};
2020-08-15 23:42:29 +02:00
uInt64 FVTerm::term_size_check_timeout{500000}; // 500 ms
2019-08-25 22:16:00 +02:00
uInt FVTerm::erase_char_length{};
uInt FVTerm::repeat_char_length{};
uInt FVTerm::clr_bol_length{};
uInt FVTerm::clr_eol_length{};
uInt FVTerm::cursor_address_length{};
struct timeval FVTerm::time_last_flush{};
2020-08-15 23:42:29 +02:00
struct timeval FVTerm::last_term_size_check{};
const FVTerm* FVTerm::init_object{nullptr};
2019-10-08 04:37:19 +02:00
FVTerm::FTermArea* FVTerm::vterm{nullptr};
FVTerm::FTermArea* FVTerm::vdesktop{nullptr};
FVTerm::FTermArea* FVTerm::active_area{nullptr};
FChar FVTerm::term_attribute{};
FChar FVTerm::next_attribute{};
FChar FVTerm::s_ch{};
FChar FVTerm::i_ch{};
//----------------------------------------------------------------------
// class FVTerm
//----------------------------------------------------------------------
// constructors and destructor
//----------------------------------------------------------------------
2020-06-06 21:10:06 +02:00
FVTerm::FVTerm()
{
2020-06-06 21:10:06 +02:00
if ( ! init_object )
init();
else
{
fterm = std::shared_ptr<FTerm>(init_object->fterm);
term_pos = std::shared_ptr<FPoint>(init_object->term_pos);
output_buffer = std::shared_ptr<OutputBuffer>(init_object->output_buffer);
window_list = std::shared_ptr<FVTermList>(init_object->window_list);
}
}
//----------------------------------------------------------------------
FVTerm::~FVTerm() // destructor
{
if ( init_object == this )
finish();
}
2019-02-24 00:25:36 +01:00
// Overloaded operators
//----------------------------------------------------------------------
FVTerm& FVTerm::operator << (const FTermBuffer& term_buffer)
{
print (term_buffer);
return *this;
}
// public methods of FVTerm
//----------------------------------------------------------------------
FPoint FVTerm::getPrintCursor()
{
const auto& win = getPrintArea();
if ( win )
return { win->offset_left + win->cursor_x
, win->offset_top + win->cursor_y };
return {0, 0};
}
//----------------------------------------------------------------------
2020-07-12 15:25:21 +02:00
void FVTerm::setTermXY (int x, int y) const
{
// Sets the hardware cursor to the given (x,y) position
if ( term_pos->getX() == x && term_pos->getY() == y )
return;
const auto term_width = int(FTerm::getColumnNumber());
const auto term_height = int(FTerm::getLineNumber());
2018-10-24 08:51:38 +02:00
if ( x >= term_width && term_width > 0 )
{
y += x / term_width;
x %= term_width;
}
if ( term_pos->getY() >= term_height )
term_pos->setY(term_height - 1);
if ( y >= term_height )
y = term_height - 1;
const int term_x = term_pos->getX();
const int term_y = term_pos->getY();
2021-02-22 20:29:49 +01:00
const auto& move_str = FTerm::moveCursorString (term_x, term_y, x, y);
2021-02-22 20:29:49 +01:00
if ( ! move_str.empty() )
appendOutputBuffer(FTermControl{move_str});
2017-09-11 03:06:02 +02:00
term_pos->setPoint(x, y);
}
2020-04-02 09:59:34 +02:00
//----------------------------------------------------------------------
2020-12-05 21:24:09 +01:00
void FVTerm::setTerminalUpdates (TerminalUpdate refresh_state) const
2020-04-02 09:59:34 +02:00
{
2020-12-05 21:24:09 +01:00
if ( refresh_state == TerminalUpdate::Stop )
2020-04-02 09:59:34 +02:00
{
2020-04-19 20:38:52 +02:00
no_terminal_updates = true;
}
2020-12-05 21:24:09 +01:00
else if ( refresh_state == TerminalUpdate::Continue
|| refresh_state == TerminalUpdate::Start )
2020-04-19 20:38:52 +02:00
{
no_terminal_updates = false;
2020-04-02 09:59:34 +02:00
}
2020-12-05 21:24:09 +01:00
if ( refresh_state == TerminalUpdate::Start )
2020-04-02 09:59:34 +02:00
updateTerminal();
}
//----------------------------------------------------------------------
2020-07-12 15:25:21 +02:00
void FVTerm::hideCursor (bool enable) const
{
// Hides or shows the input cursor on the terminal
2020-06-06 21:10:06 +02:00
if ( ! cursor_hideable )
return;
const char* visibility_str = FTerm::cursorsVisibilityString (enable);
if ( ! visibility_str ) // Exit the function if the string is empty
2020-10-19 23:38:12 +02:00
return;
appendOutputBuffer(FTermControl{visibility_str});
flush();
}
//----------------------------------------------------------------------
void FVTerm::setPrintCursor (const FPoint& pos)
{
if ( auto win = getPrintArea() )
{
win->cursor_x = pos.getX() - win->offset_left;
win->cursor_y = pos.getY() - win->offset_top;
}
}
//----------------------------------------------------------------------
2020-07-12 15:25:21 +02:00
FColor FVTerm::rgb2ColorIndex (uInt8 r, uInt8 g, uInt8 b) const
{
// Converts a 24-bit RGB color to a 256-color compatible approximation
2020-12-31 20:45:10 +01:00
const uInt16 ri = (((r * 5) + 127) / 255) * 36;
const uInt16 gi = (((g * 5) + 127) / 255) * 6;
const uInt16 bi = (((b * 5) + 127) / 255);
return FColor(16 + ri + gi + bi);
}
//----------------------------------------------------------------------
void FVTerm::setNonBlockingRead (bool enable)
{
#if defined(__CYGWIN__)
// Fixes problem with mouse input
char termfilename[256]{};
if ( ttyname_r(1, termfilename, sizeof(termfilename)) )
termfilename[0] = '\0';
if ( std::strncmp(termfilename, "/dev/cons", 9) == 0 )
{
FKeyboard::setNonBlockingInputSupport(false);
return;
}
#endif
uInt64 blocking_time = enable ? 5000 : 100000; // 5 or 100 ms
FKeyboard::setReadBlockingTime (blocking_time);
}
2016-12-28 16:29:49 +01:00
//----------------------------------------------------------------------
2020-12-31 20:45:10 +01:00
void FVTerm::clearArea (wchar_t fillchar)
2016-12-28 16:29:49 +01:00
{
clearArea (vwin, fillchar);
}
//----------------------------------------------------------------------
void FVTerm::createVTerm (const FSize& size)
{
// initialize virtual terminal
const FRect box{0, 0, size.getWidth(), size.getHeight()};
const FSize shadow{0, 0};
createArea (box, shadow, vterm);
}
//----------------------------------------------------------------------
2020-07-12 17:54:13 +02:00
void FVTerm::resizeVTerm (const FSize& size) const
{
// resize virtual terminal
const FRect box{0, 0, size.getWidth(), size.getHeight()};
const FSize shadow{0, 0};
resizeArea (box, shadow, vterm);
}
//----------------------------------------------------------------------
2020-08-23 14:00:53 +02:00
void FVTerm::putVTerm() const
{
2020-10-25 01:21:45 +02:00
for (auto i{0}; i < vterm->height; i++)
{
vterm->changes[i].xmin = 0;
vterm->changes[i].xmax = uInt(vterm->width - 1);
}
updateTerminal();
}
//----------------------------------------------------------------------
bool FVTerm::updateTerminal() const
{
// Updates pending changes to the terminal
2020-10-22 03:14:14 +02:00
// Check if terminal updates were stopped, application is stopping,
2020-11-07 21:51:55 +01:00
// VTerm has no changes, or the drawing is not completed
2020-10-22 03:14:14 +02:00
if ( no_terminal_updates || FApplication::isQuit()
2020-12-31 20:45:10 +01:00
|| ! (isFlushTimeout() || force_terminal_update)
2020-10-22 03:14:14 +02:00
|| ! (hasPendingUpdates(vterm) && draw_completed) )
{
return false;
2020-10-22 03:14:14 +02:00
}
2020-10-24 01:56:15 +02:00
std::size_t changedlines = 0;
2019-08-25 22:16:00 +02:00
for (uInt y{0}; y < uInt(vterm->height); y++)
2020-10-24 01:56:15 +02:00
{
if ( updateTerminalLine(y) )
changedlines++;
}
vterm->has_changes = false;
// sets the new input cursor position
bool cursor_update = updateTerminalCursor();
return cursor_update || changedlines > 0;
}
//----------------------------------------------------------------------
2020-04-13 12:40:11 +02:00
void FVTerm::addPreprocessingHandler ( const FVTerm* instance
, FPreprocessingFunction&& function )
{
if ( ! print_area )
FVTerm::getPrintArea();
if ( print_area )
{
delPreprocessingHandler (instance);
2020-12-05 21:24:09 +01:00
auto obj = make_unique<FVTermPreprocessing> \
2020-12-05 23:54:40 +01:00
(instance, std::move(function));
print_area->preproc_list.emplace_back(std::move(obj));
}
}
//----------------------------------------------------------------------
2020-04-13 12:40:11 +02:00
void FVTerm::delPreprocessingHandler (const FVTerm* instance)
{
if ( ! print_area )
FVTerm::getPrintArea();
2019-10-06 22:35:00 +02:00
if ( ! print_area || print_area->preproc_list.empty() )
return;
2019-10-06 22:35:00 +02:00
auto iter = print_area->preproc_list.begin();
2019-10-06 22:35:00 +02:00
while ( iter != print_area->preproc_list.end() )
{
2020-12-05 21:24:09 +01:00
if ( iter->get()->instance.get() == instance )
iter = print_area->preproc_list.erase(iter);
else
++iter;
}
}
2016-12-28 16:29:49 +01:00
//----------------------------------------------------------------------
int FVTerm::print (const FString& string)
2016-12-28 16:29:49 +01:00
{
if ( string.isNull() )
return -1;
FTermBuffer term_buffer{};
term_buffer.write(string);
return print (term_buffer);
2016-12-28 16:29:49 +01:00
}
2016-12-28 16:29:49 +01:00
//----------------------------------------------------------------------
int FVTerm::print (FTermArea* area, const FString& string)
2016-12-28 16:29:49 +01:00
{
if ( ! area || string.isNull() )
2016-12-28 16:29:49 +01:00
return -1;
FTermBuffer term_buffer{};
term_buffer.write(string);
return print (area, term_buffer);
}
2019-02-24 00:25:36 +01:00
//----------------------------------------------------------------------
int FVTerm::print (const std::vector<FChar>& term_string)
2019-02-24 00:25:36 +01:00
{
if ( term_string.empty() )
2019-02-24 00:25:36 +01:00
return -1;
FTermBuffer term_buffer{term_string.begin(), term_string.end()};
return print (term_buffer);
2019-02-24 00:25:36 +01:00
}
//----------------------------------------------------------------------
int FVTerm::print (FTermArea* area, const std::vector<FChar>& term_string)
2019-02-24 00:25:36 +01:00
{
if ( ! area || term_string.empty() )
return -1;
FTermBuffer term_buffer{term_string.begin(), term_string.end()};
return print (area, term_buffer);
2019-02-24 00:25:36 +01:00
}
//----------------------------------------------------------------------
int FVTerm::print (const FTermBuffer& term_buffer)
{
if ( term_buffer.isEmpty() )
return -1;
auto area = getPrintArea();
if ( ! area )
2019-02-24 00:25:36 +01:00
return -1;
return print (area, term_buffer);
}
//----------------------------------------------------------------------
int FVTerm::print (FTermArea* area, const FTermBuffer& term_buffer)
{
2019-08-25 22:16:00 +02:00
int len{0};
const auto tabstop = uInt(FTerm::getTabstop());
if ( ! area || term_buffer.isEmpty() )
return -1;
for (auto&& fchar : term_buffer)
{
2019-08-25 22:16:00 +02:00
bool printable_character{false};
switch ( fchar.ch[0] )
{
case '\n':
area->cursor_y++;
2017-08-11 10:50:39 +02:00
// fall through
case '\r':
area->cursor_x = 1;
break;
case '\t':
2018-12-28 22:57:43 +01:00
area->cursor_x = int ( uInt(area->cursor_x)
+ tabstop
- uInt(area->cursor_x)
+ 1
% tabstop );
break;
case '\b':
area->cursor_x--;
break;
case '\a':
FTerm::beep();
break;
default:
2020-04-13 12:40:11 +02:00
print (area, fchar); // print next character
printable_character = true;
}
if ( ! printable_character && printWrap(area) )
break; // end of area reached
len++;
}
return len;
}
//----------------------------------------------------------------------
int FVTerm::print (wchar_t c)
{
2020-11-24 21:06:39 +01:00
FChar nc{FVTerm::getAttribute()}; // next character
nc.ch[0] = c;
nc.attr.byte[2] = 0;
nc.attr.byte[3] = 0;
return print (nc);
}
//----------------------------------------------------------------------
2019-10-08 04:37:19 +02:00
int FVTerm::print (FTermArea* area, wchar_t c)
{
if ( ! area )
return -1;
2021-04-05 21:20:02 +02:00
FChar nc = FVTerm::getAttribute(); // next character
nc.ch[0] = c;
nc.attr.byte[2] = 0;
2020-04-13 12:40:11 +02:00
nc.attr.byte[3] = 0;
return print (area, nc);
}
//----------------------------------------------------------------------
2019-10-08 04:37:19 +02:00
int FVTerm::print (FChar& term_char)
{
auto area = getPrintArea();
if ( ! area )
2019-02-24 00:25:36 +01:00
return -1;
return print (area, term_char);
}
2020-04-13 12:40:11 +02:00
//----------------------------------------------------------------------
int FVTerm::print (FTermArea* area, const FChar& term_char)
{
auto fchar = term_char;
return print (area, fchar);
}
//----------------------------------------------------------------------
2019-10-08 04:37:19 +02:00
int FVTerm::print (FTermArea* area, FChar& term_char)
{
2016-12-28 16:29:49 +01:00
if ( ! area )
return -1;
2020-10-14 23:43:34 +02:00
const int ax = area->cursor_x - 1;
const int ay = area->cursor_y - 1;
if ( term_char.attr.bit.char_width == 0 )
addColumnWidth(term_char); // add column width
auto char_width = term_char.attr.bit.char_width;
2020-10-14 23:43:34 +02:00
if ( char_width == 0 && ! term_char.attr.bit.fullwidth_padding )
return 0;
2020-10-14 23:43:34 +02:00
// Print term_char on area at position (ax, ay)
printCharacterOnCoordinate (area, ax, ay, term_char);
2016-12-28 16:29:49 +01:00
area->cursor_x++;
area->has_changes = true;
// Line break at right margin
2020-10-14 23:43:34 +02:00
if ( area->cursor_x > area->width + area->right_shadow )
2016-12-28 16:29:49 +01:00
{
area->cursor_x = 1;
area->cursor_y++;
}
else if ( char_width == 2 )
2020-10-14 23:43:34 +02:00
printPaddingCharacter (area, term_char);
// Prevent up scrolling
2020-10-14 23:43:34 +02:00
if ( area->cursor_y > area->height + area->bottom_shadow )
2016-12-28 16:29:49 +01:00
{
area->cursor_y--;
return -1;
}
2016-12-28 16:29:49 +01:00
return 1;
}
//----------------------------------------------------------------------
void FVTerm::print (const FPoint& p)
{
setPrintCursor (p);
}
//----------------------------------------------------------------------
void FVTerm::print (const FStyle& style)
{
2020-12-31 20:45:10 +01:00
Style attr = style.getStyle();
2020-12-31 20:45:10 +01:00
if ( attr == Style::None )
setNormal();
else
{
if ( (attr & Style::Bold) != Style::None ) setBold();
if ( (attr & Style::Dim) != Style::None ) setDim();
if ( (attr & Style::Italic) != Style::None ) setItalic();
if ( (attr & Style::Underline) != Style::None ) setUnderline();
if ( (attr & Style::Blink) != Style::None ) setBlink();
if ( (attr & Style::Reverse) != Style::None ) setReverse();
if ( (attr & Style::Standout) != Style::None ) setStandout();
if ( (attr & Style::Invisible) != Style::None ) setInvisible();
if ( (attr & Style::Protected) != Style::None ) setProtected();
if ( (attr & Style::CrossedOut) != Style::None ) setCrossedOut();
if ( (attr & Style::DoubleUnderline) != Style::None ) setDoubleUnderline();
if ( (attr & Style::Transparent) != Style::None ) setTransparent();
if ( (attr & Style::ColorOverlay) != Style::None ) setColorOverlay();
if ( (attr & Style::InheritBackground) != Style::None ) setInheritBackground();
}
}
//----------------------------------------------------------------------
void FVTerm::print (const FColorPair& pair)
{
setColor (pair.getForegroundColor(), pair.getBackgroundColor());
}
//----------------------------------------------------------------------
void FVTerm::flush() const
{
// Flush the output buffer
flushTimeAdjustment();
if ( ! output_buffer || output_buffer->empty()
|| ! (isFlushTimeout() || force_terminal_update) )
2020-06-06 21:10:06 +02:00
return;
while ( ! output_buffer->empty() )
{
const auto& first = output_buffer->front();
const auto& type = std::get<0>(first);
const auto& str = std::get<1>(first);
2020-10-19 23:38:12 +02:00
if ( type == OutputType::String )
{
static const FTerm::defaultPutChar& FTermPutchar = FTerm::putchar();
if ( ! FTermPutchar )
return;
for (auto&& ch : str.wstring)
FTermPutchar(int(ch));
}
else if ( type == OutputType::Control )
FTerm::putstring (str.string);
output_buffer->pop();
}
std::fflush(stdout);
auto& mouse = FTerm::getFMouseControl();
mouse.drawPointer();
FObject::getCurrentTime (&time_last_flush);
}
2016-12-28 16:29:49 +01:00
// protected methods of FVTerm
//----------------------------------------------------------------------
2019-10-08 04:37:19 +02:00
FVTerm::FTermArea* FVTerm::getPrintArea()
{
// returns the print area of this object
if ( print_area )
return print_area;
else
{
if ( vwin )
{
print_area = vwin;
return print_area;
}
else if ( child_print_area )
{
print_area = child_print_area;
return print_area;
}
}
return vdesktop;
}
//----------------------------------------------------------------------
void FVTerm::createArea ( const FRect& box
, const FSize& shadow
2019-10-08 04:37:19 +02:00
, FTermArea*& area )
{
2016-12-28 16:29:49 +01:00
// initialize virtual window
2017-08-12 22:55:29 +02:00
try
{
2019-10-08 04:37:19 +02:00
area = new FTermArea;
2017-08-12 22:55:29 +02:00
}
2020-05-13 23:47:14 +02:00
catch (const std::bad_alloc&)
2017-08-12 22:55:29 +02:00
{
2020-05-13 23:47:14 +02:00
badAllocOutput ("FTermArea");
2017-08-12 22:55:29 +02:00
return;
}
area->setOwner<FVTerm*>(this);
resizeArea (box, shadow, area);
}
//----------------------------------------------------------------------
void FVTerm::resizeArea ( const FRect& box
, const FSize& shadow
2020-07-12 17:00:16 +02:00
, FTermArea* area ) const
{
// Resize the virtual window to a new size.
const int offset_left = box.getX();
const int offset_top = box.getY();
const auto width = int(box.getWidth());
const auto height = int(box.getHeight());
const auto rsw = int(shadow.getWidth());
const auto bsh = int(shadow.getHeight());
2017-01-22 23:04:40 +01:00
assert ( offset_top >= 0 );
assert ( width > 0 && width + rsw > 0 );
assert ( height > 0 && height + bsh > 0 );
2017-01-22 23:04:40 +01:00
assert ( rsw >= 0 );
assert ( bsh >= 0 );
if ( ! area )
2016-12-28 16:29:49 +01:00
return;
2017-03-19 17:18:07 +01:00
if ( width == area->width
2017-11-26 22:37:18 +01:00
&& height == area->height
&& rsw == area->right_shadow
&& bsh == area->bottom_shadow )
2017-03-19 17:18:07 +01:00
{
if ( offset_left != area->offset_left )
area->offset_left = offset_left;
if ( offset_top != area->offset_top )
area->offset_top = offset_top;
return;
}
2019-08-25 22:16:00 +02:00
bool realloc_success{false};
const std::size_t full_width = std::size_t(width) + std::size_t(rsw);
const std::size_t full_height = std::size_t(height) + std::size_t(bsh);
const std::size_t area_size = full_width * full_height;
2016-12-28 16:29:49 +01:00
2019-09-09 19:13:38 +02:00
if ( area->height + area->bottom_shadow != int(full_height) )
{
2018-10-08 04:14:20 +02:00
realloc_success = reallocateTextArea ( area
2019-09-09 19:13:38 +02:00
, full_height
2018-10-08 04:14:20 +02:00
, area_size );
}
2019-09-09 19:13:38 +02:00
else if ( area->width + area->right_shadow != int(full_width) )
2016-12-28 16:29:49 +01:00
{
2018-02-04 19:42:30 +01:00
realloc_success = reallocateTextArea (area, area_size);
2016-12-28 16:29:49 +01:00
}
else
return;
2018-02-04 19:42:30 +01:00
if ( ! realloc_success )
return;
area->offset_left = offset_left;
area->offset_top = offset_top;
area->width = width;
area->height = height;
area->right_shadow = rsw;
area->bottom_shadow = bsh;
area->has_changes = false;
2018-02-04 19:42:30 +01:00
const FSize size{full_width, full_height};
2020-07-12 15:25:21 +02:00
resetTextAreaToDefault (area, size);
2018-02-04 19:42:30 +01:00
}
2016-12-28 16:29:49 +01:00
//----------------------------------------------------------------------
2019-10-08 04:37:19 +02:00
void FVTerm::removeArea (FTermArea*& area)
2016-12-28 16:29:49 +01:00
{
// remove the virtual window
if ( area == nullptr )
return;
if ( area->changes != nullptr )
{
delete[] area->changes;
area->changes = nullptr;
}
if ( area->data != nullptr )
{
delete[] area->data;
area->data = nullptr;
2016-12-28 16:29:49 +01:00
}
delete area;
area = nullptr;
2016-12-28 16:29:49 +01:00
}
2016-12-28 16:29:49 +01:00
//----------------------------------------------------------------------
void FVTerm::restoreVTerm (const FRect& box)
{
2020-06-06 21:10:06 +02:00
if ( ! vterm )
return;
int x = box.getX() - 1;
int y = box.getY() - 1;
auto w = int(box.getWidth());
auto h = int(box.getHeight());
2016-12-28 16:29:49 +01:00
if ( x < 0 )
x = 0;
2016-12-28 16:29:49 +01:00
if ( y < 0 )
y = 0;
if ( x + w > vterm->width )
2016-12-28 16:29:49 +01:00
w = vterm->width - x;
2016-12-28 16:29:49 +01:00
if ( w < 0 )
return;
if ( y + h > vterm->height )
2016-12-28 16:29:49 +01:00
h = vterm->height - y;
2016-12-28 16:29:49 +01:00
if ( h < 0 )
return;
2020-10-25 01:21:45 +02:00
for (auto ty{0}; ty < h; ty++)
2016-12-28 16:29:49 +01:00
{
const int ypos = y + ty;
2016-12-28 16:29:49 +01:00
2020-10-25 01:21:45 +02:00
for (auto tx{0}; tx < w; tx++)
2018-01-05 00:49:00 +01:00
{
const int xpos = x + tx;
2020-10-23 01:07:59 +02:00
auto& tc = vterm->data[ypos * vterm->width + xpos]; // terminal character
auto sc = generateCharacter(FPoint{xpos, ypos}); // shown character
2020-10-23 01:07:59 +02:00
std::memcpy (&tc, &sc, sizeof(tc));
2018-01-05 00:49:00 +01:00
}
2016-12-28 16:29:49 +01:00
2018-12-28 22:57:43 +01:00
if ( int(vterm->changes[ypos].xmin) > x )
2018-01-05 00:49:00 +01:00
vterm->changes[ypos].xmin = uInt(x);
2016-12-28 16:29:49 +01:00
2018-12-28 22:57:43 +01:00
if ( int(vterm->changes[ypos].xmax) < x + w - 1 )
2018-01-05 00:49:00 +01:00
vterm->changes[ypos].xmax = uInt(x + w - 1);
}
vterm->has_changes = true;
2016-12-28 16:29:49 +01:00
}
2016-12-28 16:29:49 +01:00
//----------------------------------------------------------------------
2020-07-12 17:00:16 +02:00
bool FVTerm::updateVTermCursor (const FTermArea* area) const
2016-12-28 16:29:49 +01:00
{
2020-10-23 01:07:59 +02:00
if ( ! (area && isActive(area) && area->visible) )
2019-10-06 22:35:00 +02:00
return false;
if ( area->input_cursor_visible )
{
2019-10-06 22:35:00 +02:00
// area cursor position
const int cx = area->input_cursor_x;
const int cy = area->input_cursor_y;
2020-10-23 01:07:59 +02:00
// terminal position = area offset + area cursor position
const int x = area->offset_left + cx;
const int y = area->offset_top + cy;
2019-09-29 22:28:58 +02:00
if ( isInsideArea (FPoint{cx, cy}, area)
&& isInsideTerminal (FPoint{x, y})
2020-12-31 20:45:10 +01:00
&& isCovered (FPoint{x, y}, area) == CoveredState::None )
{
2019-10-06 22:35:00 +02:00
vterm->input_cursor_x = x;
vterm->input_cursor_y = y;
vterm->input_cursor_visible = true;
vterm->has_changes = true;
return true;
}
}
2019-10-06 22:35:00 +02:00
vterm->input_cursor_visible = false;
return false;
2016-12-28 16:29:49 +01:00
}
//----------------------------------------------------------------------
2019-10-06 22:35:00 +02:00
void FVTerm::setAreaCursor ( const FPoint& pos
, bool visible
2019-10-08 04:37:19 +02:00
, FTermArea* area )
{
2019-10-06 22:35:00 +02:00
if ( ! area )
return;
2019-10-06 22:35:00 +02:00
area->input_cursor_x = pos.getX() - 1;
area->input_cursor_y = pos.getY() - 1;
area->input_cursor_visible = visible;
}
//----------------------------------------------------------------------
2020-04-13 12:40:11 +02:00
void FVTerm::getArea (const FPoint& pos, const FTermArea* area)
{
2019-10-06 22:35:00 +02:00
// Copies a block from the virtual terminal position to the given area
2019-10-06 22:35:00 +02:00
if ( ! area )
return;
const int ax = pos.getX() - 1;
const int ay = pos.getY() - 1;
2020-04-13 12:40:11 +02:00
int y_end{};
int length{};
2019-10-06 22:35:00 +02:00
if ( area->height + ay > vterm->height )
y_end = area->height - ay;
else
y_end = area->height;
2019-10-06 22:35:00 +02:00
if ( area->width + ax > vterm->width )
length = vterm->width - ax;
else
length = area->width;
2020-10-25 01:21:45 +02:00
for (auto y{0}; y < y_end; y++) // line loop
2019-10-06 22:35:00 +02:00
{
2020-10-15 00:07:36 +02:00
const auto& tc = vterm->data[(ay + y) * vterm->width + ax]; // terminal character
2020-10-14 23:43:34 +02:00
auto& ac = area->data[y * area->width]; // area character
std::memcpy (&ac, &tc, sizeof(ac) * unsigned(length));
2019-10-06 22:35:00 +02:00
if ( int(area->changes[y].xmin) > 0 )
area->changes[y].xmin = 0;
if ( int(area->changes[y].xmax) < length - 1 )
area->changes[y].xmax = uInt(length - 1);
}
}
//----------------------------------------------------------------------
2020-04-13 12:40:11 +02:00
void FVTerm::getArea (const FRect& box, const FTermArea* area)
{
2019-10-06 22:35:00 +02:00
// Copies a block from the virtual terminal rectangle to the given area
2019-10-06 22:35:00 +02:00
if ( ! area )
return;
const int x = box.getX();
const int y = box.getY();
const auto w = int(box.getWidth());
const auto h = int(box.getHeight());
const int dx = x - area->offset_left + 1;
const int dy = y - area->offset_top + 1;
2020-04-13 12:40:11 +02:00
int y_end{};
int length{};
2019-10-06 22:35:00 +02:00
if ( x < 0 || y < 0 )
return;
2019-10-06 22:35:00 +02:00
if ( y - 1 + h > vterm->height )
y_end = vterm->height - y + 1;
else
2019-10-06 22:35:00 +02:00
y_end = h - 1;
2019-10-06 22:35:00 +02:00
if ( x - 1 + w > vterm->width )
length = vterm->width - x + 1;
else
length = w;
2019-10-06 22:35:00 +02:00
if ( length < 1 )
2016-12-28 16:29:49 +01:00
return;
2020-10-25 01:21:45 +02:00
for (auto _y = 0; _y < y_end; _y++) // line loop
{
const int line_len = area->width + area->right_shadow;
2020-10-15 00:07:36 +02:00
const auto& tc = vterm->data[(y + _y - 1) * vterm->width + x - 1]; // terminal character
2020-10-14 23:43:34 +02:00
auto& ac = area->data[(dy + _y) * line_len + dx]; // area character
std::memcpy (&ac, &tc, sizeof(ac) * unsigned(length));
2019-10-06 22:35:00 +02:00
if ( int(area->changes[dy + _y].xmin) > dx )
area->changes[dy + _y].xmin = uInt(dx);
2019-10-06 22:35:00 +02:00
if ( int(area->changes[dy + _y].xmax) < dx + length - 1 )
area->changes[dy + _y].xmax = uInt(dx + length - 1);
}
}
//----------------------------------------------------------------------
2020-07-12 17:54:13 +02:00
void FVTerm::putArea (const FTermArea* area) const
{
2019-10-06 22:35:00 +02:00
// Add area changes to the virtual terminal
2016-12-28 16:29:49 +01:00
if ( ! area || ! area->visible )
return;
int ax = area->offset_left;
const int ay = area->offset_top;
const int width = area->width + area->right_shadow;
const int height = area->height + area->bottom_shadow;
int ol{0}; // Outside left
int y_end{};
2019-10-06 22:35:00 +02:00
// Call the preprocessing handler methods
callPreprocessingHandler(area);
2016-12-28 16:29:49 +01:00
if ( ax < 0 )
{
ol = std::abs(ax);
ax = 0;
}
if ( height + ay > vterm->height )
2016-12-28 16:29:49 +01:00
y_end = vterm->height - ay;
else
y_end = height;
2020-10-25 01:21:45 +02:00
for (auto y{0}; y < y_end; y++) // Line loop
{
bool modified{false};
auto line_xmin = int(area->changes[y].xmin);
auto line_xmax = int(area->changes[y].xmax);
if ( line_xmin > line_xmax )
continue;
if ( ax == 0 )
line_xmin = ol;
if ( width + ax - ol >= vterm->width )
line_xmax = vterm->width + ol - ax - 1;
if ( ax + line_xmin >= vterm->width )
continue;
2020-10-25 01:21:45 +02:00
for (auto x = line_xmin; x <= line_xmax; x++) // Column loop
{
// Global terminal positions
int tx = ax + x;
const int ty = ay + y;
if ( tx < 0 || ty < 0 )
continue;
tx -= ol;
2020-10-23 01:07:59 +02:00
bool update = updateVTermCharacter(area, FPoint{x, y}, FPoint{tx, ty});
2020-10-23 01:07:59 +02:00
if ( ! modified && ! update )
line_xmin++; // Don't update covered character
2020-10-23 01:07:59 +02:00
if ( update )
modified = true;
}
int _xmin = ax + line_xmin - ol;
int _xmax = ax + line_xmax;
2018-12-28 22:57:43 +01:00
if ( _xmin < int(vterm->changes[ay + y].xmin) )
vterm->changes[ay + y].xmin = uInt(_xmin);
if ( _xmax >= vterm->width )
_xmax = vterm->width - 1;
2018-12-28 22:57:43 +01:00
if ( _xmax > int(vterm->changes[ay + y].xmax) )
vterm->changes[ay + y].xmax = uInt(_xmax);
area->changes[y].xmin = uInt(width);
area->changes[y].xmax = 0;
2016-12-28 16:29:49 +01:00
}
vterm->has_changes = true;
2016-12-28 16:29:49 +01:00
updateVTermCursor(area);
}
//----------------------------------------------------------------------
2020-04-13 12:40:11 +02:00
void FVTerm::putArea (const FPoint& pos, const FTermArea* area)
{
2019-10-06 22:35:00 +02:00
// Copies the given area block to the virtual terminal position
2016-12-28 16:29:49 +01:00
2019-10-06 22:35:00 +02:00
if ( ! area || ! area->visible )
return;
2016-12-28 16:29:49 +01:00
2019-10-06 22:35:00 +02:00
int ax = pos.getX() - 1;
const int ay = pos.getY() - 1;
const int width = area->width + area->right_shadow;
const int height = area->height + area->bottom_shadow;
2019-10-06 22:35:00 +02:00
int ol{0}; // outside left
2020-04-13 12:40:11 +02:00
int y_end{};
int length{};
2019-10-06 22:35:00 +02:00
if ( ax < 0 )
{
2019-10-06 22:35:00 +02:00
ol = std::abs(ax);
ax = 0;
}
2019-10-06 22:35:00 +02:00
if ( ay + height > vterm->height )
y_end = vterm->height - ay;
else
y_end = height;
if ( width - ol + ax > vterm->width )
length = vterm->width - ax;
else
length = width - ol;
if ( length < 1 )
return;
2020-10-25 01:21:45 +02:00
for (auto y{0}; y < y_end; y++) // line loop
2019-10-06 22:35:00 +02:00
{
if ( area->changes[y].trans_count == 0 )
{
2019-10-06 22:35:00 +02:00
// Line has only covered characters
2020-10-14 23:43:34 +02:00
const auto& ac = area->data[y * width + ol]; // area character
auto& tc = vterm->data[(ay + y) * vterm->width + ax]; // terminal character
2020-10-23 01:07:59 +02:00
putAreaLine (ac, tc, std::size_t(length));
2019-10-06 22:35:00 +02:00
}
else
{
// Line has one or more transparent characters
2020-10-25 01:21:45 +02:00
for (auto x{0}; x < length; x++) // column loop
2019-10-06 22:35:00 +02:00
{
const int cx = ax + x;
const int cy = ay + y;
2020-10-14 23:43:34 +02:00
const auto& ac = area->data[y * width + ol + x]; // area character
auto& tc = vterm->data[cy * vterm->width + cx]; // terminal character
2020-10-23 01:07:59 +02:00
putAreaCharacter (FPoint{cx, cy}, area, ac, tc);
2019-10-06 22:35:00 +02:00
}
}
2019-10-06 22:35:00 +02:00
if ( ax < int(vterm->changes[ay + y].xmin) )
vterm->changes[ay + y].xmin = uInt(ax);
if ( ax + length - 1 > int(vterm->changes[ay + y].xmax) )
vterm->changes[ay + y].xmax = uInt(ax + length - 1);
}
2016-12-28 16:29:49 +01:00
2019-10-06 22:35:00 +02:00
vterm->has_changes = true;
}
//----------------------------------------------------------------------
int FVTerm::getLayer (const FVTerm* obj)
{
// returns the layer from the FVTerm object
if ( ! getWindowList() || getWindowList()->empty() )
return -1;
auto iter = getWindowList()->begin();
const auto end = getWindowList()->end();
while ( iter != end )
{
if ( *iter == obj )
break;
++iter;
}
return int(std::distance(getWindowList()->begin(), iter) + 1);
}
//----------------------------------------------------------------------
2020-07-12 17:00:16 +02:00
void FVTerm::scrollAreaForward (FTermArea* area) const
{
2019-10-06 22:35:00 +02:00
// Scrolls the entire area up line down
2019-10-06 22:35:00 +02:00
if ( ! area )
return;
2016-12-28 16:29:49 +01:00
2019-10-06 22:35:00 +02:00
if ( area->height <= 1 )
return;
const int length = area->width;
const int total_width = area->width + area->right_shadow;
const int y_max = area->height - 1;
2020-10-25 01:21:45 +02:00
for (auto y{0}; y < y_max; y++)
2019-10-06 22:35:00 +02:00
{
const int pos1 = y * total_width;
const int pos2 = (y + 1) * total_width;
2020-10-15 00:07:36 +02:00
const auto& sc = area->data[pos2]; // source character
2020-10-14 23:43:34 +02:00
auto& dc = area->data[pos1]; // destination character
std::memcpy (&dc, &sc, sizeof(dc) * unsigned(length));
2019-10-06 22:35:00 +02:00
area->changes[y].xmin = 0;
area->changes[y].xmax = uInt(area->width - 1);
}
// insert a new line below
2020-10-14 23:43:34 +02:00
FChar nc{}; // next character
2020-10-24 01:56:15 +02:00
auto bottom_right = std::size_t((y_max * total_width) - area->right_shadow - 1);
2020-10-14 23:43:34 +02:00
const auto& lc = area->data[bottom_right]; // last character
std::memcpy (&nc, &lc, sizeof(nc));
2021-04-05 21:20:02 +02:00
nc.ch[0] = L' ';
2020-10-14 23:43:34 +02:00
auto& dc = area->data[y_max * total_width]; // destination character
std::fill_n (&dc, area->width, nc);
2019-10-06 22:35:00 +02:00
area->changes[y_max].xmin = 0;
area->changes[y_max].xmax = uInt(area->width - 1);
area->has_changes = true;
2020-12-31 20:45:10 +01:00
if ( area == vdesktop && TCAP(t_scroll_forward) )
2019-10-06 22:35:00 +02:00
{
2020-04-19 20:38:52 +02:00
setTermXY (0, vdesktop->height);
FTerm::scrollTermForward();
putArea (FPoint{1, 1}, vdesktop);
2019-10-06 22:35:00 +02:00
2020-04-19 20:38:52 +02:00
// avoid update lines from 0 to (y_max - 1)
2020-10-25 01:21:45 +02:00
for (auto y{0}; y < y_max; y++)
2020-04-19 20:38:52 +02:00
{
area->changes[y].xmin = uInt(area->width - 1);
area->changes[y].xmax = 0;
2019-10-06 22:35:00 +02:00
}
}
2016-12-28 16:29:49 +01:00
}
2016-12-28 16:29:49 +01:00
//----------------------------------------------------------------------
2020-07-12 17:00:16 +02:00
void FVTerm::scrollAreaReverse (FTermArea* area) const
2016-12-28 16:29:49 +01:00
{
2019-10-06 22:35:00 +02:00
// Scrolls the entire area one line down
2016-12-28 16:29:49 +01:00
if ( ! area )
return;
2019-10-06 22:35:00 +02:00
if ( area->height <= 1 )
return;
const int length = area->width;
const int total_width = area->width + area->right_shadow;
const int y_max = area->height - 1;
2020-10-25 01:21:45 +02:00
for (auto y = y_max; y > 0; y--)
{
const int pos1 = (y - 1) * total_width;
const int pos2 = y * total_width;
2020-10-15 00:07:36 +02:00
const auto& sc = area->data[pos1]; // source character
2020-10-14 23:43:34 +02:00
auto& dc = area->data[pos2]; // destination character
std::memcpy (&dc, &sc, sizeof(dc) * unsigned(length));
2019-10-06 22:35:00 +02:00
area->changes[y].xmin = 0;
area->changes[y].xmax = uInt(area->width - 1);
}
2019-10-06 22:35:00 +02:00
// insert a new line above
2020-10-14 23:43:34 +02:00
FChar nc{}; // next character
const auto& lc = area->data[total_width]; // last character
std::memcpy (&nc, &lc, sizeof(nc));
2021-04-05 21:20:02 +02:00
nc.ch[0] = L' ';
2020-10-14 23:43:34 +02:00
auto& dc = area->data[0]; // destination character
std::fill_n (&dc, area->width, nc);
2019-10-06 22:35:00 +02:00
area->changes[0].xmin = 0;
area->changes[0].xmax = uInt(area->width - 1);
area->has_changes = true;
2020-12-31 20:45:10 +01:00
if ( area == vdesktop && TCAP(t_scroll_reverse) )
2019-10-06 22:35:00 +02:00
{
2020-04-19 20:38:52 +02:00
setTermXY (0, 0);
FTerm::scrollTermReverse();
putArea (FPoint{1, 1}, vdesktop);
2019-10-06 22:35:00 +02:00
2020-04-19 20:38:52 +02:00
// avoid update lines from 1 to y_max
2020-10-25 01:21:45 +02:00
for (auto y{1}; y <= y_max; y++)
2020-04-19 20:38:52 +02:00
{
area->changes[y].xmin = uInt(area->width - 1);
area->changes[y].xmax = 0;
2019-10-06 22:35:00 +02:00
}
2016-12-28 16:29:49 +01:00
}
}
2016-12-28 16:29:49 +01:00
//----------------------------------------------------------------------
2020-12-31 20:45:10 +01:00
void FVTerm::clearArea (FTermArea* area, wchar_t fillchar) const
2016-12-28 16:29:49 +01:00
{
2019-10-06 22:35:00 +02:00
// Clear the area with the current attributes
2019-10-08 04:37:19 +02:00
FChar nc{}; // next character
2019-10-06 22:35:00 +02:00
// Current attributes with a space character
std::memcpy (&nc, &next_attribute, sizeof(nc));
nc.ch[0] = fillchar;
2019-10-08 04:37:19 +02:00
if ( ! (area && area->data) )
2019-10-06 22:35:00 +02:00
{
clearTerm (fillchar);
2016-12-28 16:29:49 +01:00
return;
2019-10-06 22:35:00 +02:00
}
const auto w = uInt(area->width + area->right_shadow);
2019-10-06 22:35:00 +02:00
if ( area->right_shadow == 0 )
{
if ( clearFullArea(area, nc) )
return;
}
2016-12-28 16:29:49 +01:00
else
2019-10-06 22:35:00 +02:00
clearAreaWithShadow(area, nc);
2020-10-25 01:21:45 +02:00
for (auto i{0}; i < area->height; i++)
2016-12-28 16:29:49 +01:00
{
2019-10-06 22:35:00 +02:00
area->changes[i].xmin = 0;
area->changes[i].xmax = w - 1;
2019-10-06 22:35:00 +02:00
if ( nc.attr.bit.transparent
|| nc.attr.bit.color_overlay
|| nc.attr.bit.inherit_background )
2019-10-06 22:35:00 +02:00
area->changes[i].trans_count = w;
else if ( area->right_shadow != 0 )
area->changes[i].trans_count = uInt(area->right_shadow);
else
area->changes[i].trans_count = 0;
}
2016-12-28 16:29:49 +01:00
2020-10-25 01:21:45 +02:00
for (auto i{0}; i < area->bottom_shadow; i++)
2019-10-06 22:35:00 +02:00
{
const int y = area->height + i;
2019-10-06 22:35:00 +02:00
area->changes[y].xmin = 0;
area->changes[y].xmax = w - 1;
area->changes[y].trans_count = w;
}
2019-10-06 22:35:00 +02:00
area->has_changes = true;
}
//----------------------------------------------------------------------
void FVTerm::forceTerminalUpdate() const
{
force_terminal_update = true;
processTerminalUpdate();
flush();
force_terminal_update = false;
}
//----------------------------------------------------------------------
bool FVTerm::processTerminalUpdate() const
{
2020-10-19 23:38:12 +02:00
// Checks if the resizing of the terminal is not finished
if ( FTerm::hasChangedTermSize() )
return false;
2020-10-19 23:38:12 +02:00
// Update data on VTerm
updateVTerm();
2020-10-19 23:38:12 +02:00
2020-10-22 03:14:14 +02:00
// Update the visible terminal
return updateTerminal();
2019-10-06 22:35:00 +02:00
}
2019-10-06 22:35:00 +02:00
//----------------------------------------------------------------------
2020-10-20 16:52:25 +02:00
void FVTerm::startDrawing()
2019-10-06 22:35:00 +02:00
{
// Pauses the terminal updates for the printing phase
2020-10-20 16:52:25 +02:00
draw_completed = false;
2019-10-06 22:35:00 +02:00
}
2019-10-06 22:35:00 +02:00
//----------------------------------------------------------------------
2020-10-20 16:52:25 +02:00
void FVTerm::finishDrawing()
2019-10-06 22:35:00 +02:00
{
// After the printing phase is completed, the terminal will be updated
2020-10-20 16:52:25 +02:00
draw_completed = true;
2019-10-06 22:35:00 +02:00
}
2020-06-06 21:10:06 +02:00
//----------------------------------------------------------------------
void FVTerm::initTerminal()
{
if ( fterm )
fterm->initTerminal();
// Hide the input cursor
cursor_hideable = FTerm::isCursorHideable();
hideCursor();
// Initialize character lengths
init_characterLengths();
// Check for support for combined characters
init_combined_character();
2020-06-06 21:10:06 +02:00
}
2019-10-06 22:35:00 +02:00
// private methods of FVTerm
//----------------------------------------------------------------------
2020-07-12 15:25:21 +02:00
inline void FVTerm::resetTextAreaToDefault ( const FTermArea* area
, const FSize& size ) const
2019-10-06 22:35:00 +02:00
{
2019-10-08 04:37:19 +02:00
FChar default_char;
FLineChanges unchanged;
2019-10-06 22:35:00 +02:00
2021-04-05 21:20:02 +02:00
default_char.ch[0] = L' ';
2020-12-31 20:45:10 +01:00
default_char.fg_color = FColor::Default;
default_char.bg_color = FColor::Default;
2019-10-06 22:35:00 +02:00
default_char.attr.byte[0] = 0;
default_char.attr.byte[1] = 0;
default_char.attr.byte[2] = 0;
2020-04-13 12:40:11 +02:00
default_char.attr.byte[3] = 0;
2019-10-06 22:35:00 +02:00
2019-10-08 04:37:19 +02:00
std::fill_n (area->data, size.getArea(), default_char);
2019-10-06 22:35:00 +02:00
unchanged.xmin = uInt(size.getWidth());
unchanged.xmax = 0;
unchanged.trans_count = 0;
std::fill_n (area->changes, size.getHeight(), unchanged);
}
//----------------------------------------------------------------------
2019-10-08 04:37:19 +02:00
inline bool FVTerm::reallocateTextArea ( FTermArea* area
2019-10-06 22:35:00 +02:00
, std::size_t height
, std::size_t size )
{
// Reallocate "height" lines for changes
// and "size" bytes for the text area
2020-02-19 21:59:13 +01:00
if ( area->changes != nullptr )
2019-10-06 22:35:00 +02:00
delete[] area->changes;
2020-02-19 21:59:13 +01:00
if ( area->data != nullptr )
2019-10-08 04:37:19 +02:00
delete[] area->data;
2019-10-06 22:35:00 +02:00
try
{
2019-10-08 04:37:19 +02:00
area->changes = new FLineChanges[height];
area->data = new FChar[size];
2019-10-06 22:35:00 +02:00
}
2020-05-13 23:47:14 +02:00
catch (const std::bad_alloc&)
2019-10-06 22:35:00 +02:00
{
2020-05-21 14:53:51 +02:00
badAllocOutput ("FLineChanges[height] or FChar[size]");
2019-10-06 22:35:00 +02:00
return false;
}
return true;
}
//----------------------------------------------------------------------
2019-10-08 04:37:19 +02:00
inline bool FVTerm::reallocateTextArea (FTermArea* area, std::size_t size)
2019-10-06 22:35:00 +02:00
{
// Reallocate "size" bytes for the text area
2020-02-19 21:59:13 +01:00
if ( area->data != nullptr )
2019-10-08 04:37:19 +02:00
delete[] area->data;
2019-10-06 22:35:00 +02:00
try
{
2019-10-08 04:37:19 +02:00
area->data = new FChar[size];
2019-10-06 22:35:00 +02:00
}
2020-05-13 23:47:14 +02:00
catch (const std::bad_alloc&)
2019-10-06 22:35:00 +02:00
{
2020-05-21 14:53:51 +02:00
badAllocOutput ("FChar[size]");
2019-10-06 22:35:00 +02:00
return false;
}
return true;
}
//----------------------------------------------------------------------
2020-12-05 21:24:09 +01:00
FVTerm::CoveredState FVTerm::isCovered ( const FPoint& pos
, const FTermArea* area )
2019-10-06 22:35:00 +02:00
{
// Determines the covered state for the given position
if ( ! area )
2020-12-31 20:45:10 +01:00
return CoveredState::None;
2019-10-06 22:35:00 +02:00
2020-12-31 20:45:10 +01:00
auto is_covered = CoveredState::None;
2019-10-06 22:35:00 +02:00
if ( getWindowList() && ! getWindowList()->empty() )
2019-10-06 22:35:00 +02:00
{
bool found{ area == vdesktop };
2019-10-06 22:35:00 +02:00
for (auto& win_obj : *getWindowList())
2019-10-06 22:35:00 +02:00
{
const auto& win = win_obj->getVWin();
2019-10-06 22:35:00 +02:00
if ( ! (win && win->visible) )
2019-10-06 22:35:00 +02:00
continue;
const int& win_x = win->offset_left;
const int& win_y = win->offset_top;
const FRect geometry { win_x , win_y
, std::size_t(win->width) + std::size_t(win->right_shadow)
, std::size_t(win->height) + std::size_t(win->bottom_shadow) };
2019-10-06 22:35:00 +02:00
if ( found && geometry.contains(pos) )
{
const int width = win->width + win->right_shadow;
const int& x = pos.getX();
const int& y = pos.getY();
const auto& tmp = &win->data[(y - win_y) * width + (x - win_x)];
2019-10-06 22:35:00 +02:00
if ( tmp->attr.bit.color_overlay )
2019-10-06 22:35:00 +02:00
{
2020-12-31 20:45:10 +01:00
is_covered = CoveredState::Half;
2019-10-06 22:35:00 +02:00
}
else if ( ! tmp->attr.bit.transparent )
{
2020-12-31 20:45:10 +01:00
is_covered = CoveredState::Full;
2019-10-06 22:35:00 +02:00
break;
}
}
2019-10-06 22:35:00 +02:00
if ( area == win )
found = true;
}
2019-10-06 22:35:00 +02:00
}
2019-10-06 22:35:00 +02:00
return is_covered;
}
2016-12-28 16:29:49 +01:00
2019-10-06 22:35:00 +02:00
//----------------------------------------------------------------------
2020-10-24 01:56:15 +02:00
inline void FVTerm::updateOverlappedColor ( const FChar& area_char
, const FChar& over_char
, FChar& vterm_char )
2019-10-06 22:35:00 +02:00
{
// Add the overlapping color to this character
// New character
2019-10-08 04:37:19 +02:00
FChar nc{};
2020-10-23 01:07:59 +02:00
std::memcpy (&nc, &area_char, sizeof(nc));
nc.fg_color = over_char.fg_color;
nc.bg_color = over_char.bg_color;
2019-10-06 22:35:00 +02:00
nc.attr.bit.reverse = false;
nc.attr.bit.standout = false;
2020-12-31 20:45:10 +01:00
if ( nc.ch[0] == UniChar::LowerHalfBlock
|| nc.ch[0] == UniChar::UpperHalfBlock
|| nc.ch[0] == UniChar::LeftHalfBlock
|| nc.ch[0] == UniChar::RightHalfBlock
|| nc.ch[0] == UniChar::MediumShade
|| nc.ch[0] == UniChar::FullBlock )
2021-04-05 21:20:02 +02:00
nc.ch[0] = L' ';
2019-10-06 22:35:00 +02:00
2020-10-23 01:07:59 +02:00
nc.attr.bit.no_changes = bool(vterm_char.attr.bit.printed && vterm_char == nc);
std::memcpy (&vterm_char, &nc, sizeof(vterm_char));
2019-10-06 22:35:00 +02:00
}
//----------------------------------------------------------------------
2020-10-24 01:56:15 +02:00
inline void FVTerm::updateOverlappedCharacter ( FChar& cover_char
, FChar& vterm_char )
2019-10-06 22:35:00 +02:00
{
// Restore one character on vterm
2020-10-23 01:07:59 +02:00
cover_char.attr.bit.no_changes = \
bool(vterm_char.attr.bit.printed && vterm_char == cover_char);
std::memcpy (&vterm_char, &cover_char, sizeof(vterm_char));
2019-10-06 22:35:00 +02:00
}
//----------------------------------------------------------------------
2020-10-24 01:56:15 +02:00
inline void FVTerm::updateShadedCharacter ( const FChar& area_char
, FChar& cover_char
, FChar& vterm_char )
2019-10-06 22:35:00 +02:00
{
// Get covered character + add the current color
2020-10-23 01:07:59 +02:00
cover_char.fg_color = area_char.fg_color;
cover_char.bg_color = area_char.bg_color;
cover_char.attr.bit.reverse = false;
cover_char.attr.bit.standout = false;
2019-10-06 22:35:00 +02:00
2020-12-31 20:45:10 +01:00
if ( cover_char.ch[0] == UniChar::LowerHalfBlock
|| cover_char.ch[0] == UniChar::UpperHalfBlock
|| cover_char.ch[0] == UniChar::LeftHalfBlock
|| cover_char.ch[0] == UniChar::RightHalfBlock
|| cover_char.ch[0] == UniChar::MediumShade
|| cover_char.ch[0] == UniChar::FullBlock )
2021-04-05 21:20:02 +02:00
cover_char.ch[0] = L' ';
2019-10-06 22:35:00 +02:00
2020-10-23 01:07:59 +02:00
cover_char.attr.bit.no_changes = \
bool(vterm_char.attr.bit.printed && vterm_char == cover_char);
std::memcpy (&vterm_char, &cover_char, sizeof(vterm_char));
2019-10-06 22:35:00 +02:00
}
//----------------------------------------------------------------------
2020-10-24 01:56:15 +02:00
inline void FVTerm::updateInheritBackground ( const FChar& area_char
, const FChar& cover_char
, FChar& vterm_char )
2019-10-06 22:35:00 +02:00
{
// Add the covered background to this character
// New character
2019-10-08 04:37:19 +02:00
FChar nc{};
2020-10-23 01:07:59 +02:00
std::memcpy (&nc, &area_char, sizeof(nc));
nc.bg_color = cover_char.bg_color;
nc.attr.bit.no_changes = \
bool(vterm_char.attr.bit.printed && vterm_char == nc);
std::memcpy (&vterm_char, &nc, sizeof(vterm_char));
2019-10-06 22:35:00 +02:00
}
//----------------------------------------------------------------------
2020-10-24 01:56:15 +02:00
inline void FVTerm::updateCharacter (const FChar& area_char, FChar& vterm_char)
2019-10-06 22:35:00 +02:00
{
// Copy a area character to the virtual terminal
2020-10-23 01:07:59 +02:00
std::memcpy (&vterm_char, &area_char, sizeof(vterm_char));
2020-10-23 01:07:59 +02:00
if ( vterm_char.attr.bit.printed && vterm_char == area_char )
vterm_char.attr.bit.no_changes = true;
2019-10-06 22:35:00 +02:00
else
2020-10-23 01:07:59 +02:00
vterm_char.attr.bit.no_changes = false;
}
//----------------------------------------------------------------------
2020-04-26 20:45:57 +02:00
bool FVTerm::updateVTermCharacter ( const FTermArea* area
2019-10-06 22:35:00 +02:00
, const FPoint& area_pos
, const FPoint& terminal_pos )
{
2019-10-06 22:35:00 +02:00
// Area character
2020-10-23 01:07:59 +02:00
const int width = area->width + area->right_shadow;
const int area_index = area_pos.getY() * width + area_pos.getX();
const auto& ac = area->data[area_index];
// Terminal character
const int terminal_index = terminal_pos.getY() * vterm->width
+ terminal_pos.getX();
auto& tc = vterm->data[terminal_index];
2016-12-28 16:29:49 +01:00
2019-10-06 22:35:00 +02:00
// Get covered state
const auto is_covered = isCovered(terminal_pos, area);
2016-12-28 16:29:49 +01:00
2020-12-31 20:45:10 +01:00
if ( is_covered == CoveredState::Full )
2019-10-06 22:35:00 +02:00
return false;
2016-12-28 16:29:49 +01:00
2020-12-31 20:45:10 +01:00
if ( is_covered == CoveredState::Half )
{
2020-10-23 01:07:59 +02:00
// Overlapped character
auto oc = getOverlappedCharacter (terminal_pos, area);
2020-10-24 01:56:15 +02:00
updateOverlappedColor (ac, oc, tc);
}
2020-10-14 23:43:34 +02:00
else if ( ac.attr.bit.transparent ) // Transparent
2016-12-28 16:29:49 +01:00
{
2020-10-23 01:07:59 +02:00
// Covered character
auto cc = getCoveredCharacter (terminal_pos, area);
2020-10-24 01:56:15 +02:00
updateOverlappedCharacter (cc, tc);
2019-10-06 22:35:00 +02:00
}
else // Not transparent
{
2020-10-14 23:43:34 +02:00
if ( ac.attr.bit.color_overlay ) // Transparent shadow
2016-12-28 16:29:49 +01:00
{
2020-10-23 01:07:59 +02:00
// Covered character
auto cc = getCoveredCharacter (terminal_pos, area);
2020-10-24 01:56:15 +02:00
updateShadedCharacter (ac, cc, tc);
2019-10-06 22:35:00 +02:00
}
2020-10-14 23:43:34 +02:00
else if ( ac.attr.bit.inherit_background )
2019-10-06 22:35:00 +02:00
{
2020-10-23 01:07:59 +02:00
// Covered character
auto cc = getCoveredCharacter (terminal_pos, area);
2020-10-24 01:56:15 +02:00
updateInheritBackground (ac, cc, tc);
2019-10-06 22:35:00 +02:00
}
else // Default
{
2020-10-23 01:07:59 +02:00
updateCharacter (ac, tc);
2016-12-28 16:29:49 +01:00
}
}
2019-10-06 22:35:00 +02:00
return true;
2016-12-28 16:29:49 +01:00
}
//----------------------------------------------------------------------
2020-07-12 19:05:29 +02:00
void FVTerm::updateVTerm() const
2016-12-28 16:29:49 +01:00
{
2019-10-06 22:35:00 +02:00
// Updates the character data from all areas to VTerm
2019-08-25 22:16:00 +02:00
2020-10-19 23:38:12 +02:00
if ( hasPendingUpdates(vdesktop) )
2019-10-06 22:35:00 +02:00
{
putArea(vdesktop);
vdesktop->has_changes = false;
}
2016-12-28 16:29:49 +01:00
if ( ! getWindowList() || getWindowList()->empty() )
2016-12-28 16:29:49 +01:00
return;
for (auto&& window : *getWindowList())
{
2019-10-08 04:37:19 +02:00
auto v_win = window->getVWin();
2019-10-08 04:37:19 +02:00
if ( ! (v_win && v_win->visible) )
2019-10-06 22:35:00 +02:00
continue;
2020-10-19 23:38:12 +02:00
if ( hasPendingUpdates(v_win) )
2016-12-28 16:29:49 +01:00
{
2019-10-08 04:37:19 +02:00
putArea(v_win);
v_win->has_changes = false;
2019-10-06 22:35:00 +02:00
}
2019-10-08 04:37:19 +02:00
else if ( hasChildAreaChanges(v_win) )
2019-10-06 22:35:00 +02:00
{
2019-10-08 04:37:19 +02:00
putArea(v_win); // and call the child area processing handler there
clearChildAreaChanges(v_win);
}
}
}
//----------------------------------------------------------------------
2020-04-13 12:40:11 +02:00
void FVTerm::callPreprocessingHandler (const FTermArea* area)
{
2019-10-06 22:35:00 +02:00
// Call preprocessing handler
2020-10-22 03:14:14 +02:00
if ( ! area || area->preproc_list.empty() )
2019-10-06 22:35:00 +02:00
return;
2019-10-06 22:35:00 +02:00
for (auto&& pcall : area->preproc_list)
{
2019-10-06 22:35:00 +02:00
// call the preprocessing handler
2020-12-05 21:24:09 +01:00
auto preprocessingHandler = pcall->function;
2019-10-06 22:35:00 +02:00
preprocessingHandler();
}
2019-10-06 22:35:00 +02:00
}
2019-10-06 22:35:00 +02:00
//----------------------------------------------------------------------
bool FVTerm::hasChildAreaChanges (FTermArea* area) const
2019-10-06 22:35:00 +02:00
{
2020-10-22 03:14:14 +02:00
if ( ! area || area->preproc_list.empty() )
2019-10-06 22:35:00 +02:00
return false;
2020-02-19 21:59:13 +01:00
return std::any_of ( area->preproc_list.begin()
, area->preproc_list.end()
2020-12-05 21:24:09 +01:00
, [] (const std::unique_ptr<FVTermPreprocessing>& pcall)
2020-02-19 21:59:13 +01:00
{
2020-12-05 21:24:09 +01:00
return pcall->instance
&& pcall->instance->child_print_area
&& pcall->instance->child_print_area->has_changes;
2020-02-19 21:59:13 +01:00
}
);
2019-10-06 22:35:00 +02:00
}
2019-10-06 22:35:00 +02:00
//----------------------------------------------------------------------
2020-07-12 15:25:21 +02:00
void FVTerm::clearChildAreaChanges (const FTermArea* area) const
2019-10-06 22:35:00 +02:00
{
2020-10-22 03:14:14 +02:00
if ( ! area || area->preproc_list.empty() )
2019-10-06 22:35:00 +02:00
return;
2019-10-06 22:35:00 +02:00
for (auto&& pcall : area->preproc_list)
{
2020-12-05 21:24:09 +01:00
if ( pcall->instance && pcall->instance->child_print_area )
pcall->instance->child_print_area->has_changes = false;
}
2019-10-06 22:35:00 +02:00
}
2019-10-06 22:35:00 +02:00
//----------------------------------------------------------------------
2020-04-13 12:40:11 +02:00
bool FVTerm::isInsideArea (const FPoint& pos, const FTermArea* area)
2019-10-06 22:35:00 +02:00
{
// Check whether the coordinates are within the area
const auto aw = std::size_t(area->width);
const auto ah = std::size_t(area->height);
FRect area_geometry{0, 0, aw, ah};
2019-10-06 22:35:00 +02:00
if ( area_geometry.contains(pos) )
return true;
else
return false;
}
2018-01-05 00:49:00 +01:00
//----------------------------------------------------------------------
FChar FVTerm::generateCharacter (const FPoint& pos)
2018-01-05 00:49:00 +01:00
{
// Generates characters for a given position considering all areas
const int x = pos.getX();
const int y = pos.getY();
2019-10-08 04:37:19 +02:00
auto sc = &vdesktop->data[y * vdesktop->width + x]; // shown character
2018-01-05 00:49:00 +01:00
if ( ! getWindowList() || getWindowList()->empty() )
2018-01-05 00:49:00 +01:00
return *sc;
for (auto& win_obj : *getWindowList())
2018-01-05 00:49:00 +01:00
{
const auto& win = win_obj->getVWin();
2018-01-05 00:49:00 +01:00
if ( ! win || ! win->visible )
continue;
const int win_x = win->offset_left;
const int win_y = win->offset_top;
const FRect geometry { win_x, win_y
, std::size_t(win->width) + std::size_t(win->right_shadow)
, std::size_t(win->height) + std::size_t(win->bottom_shadow) };
2018-01-05 00:49:00 +01:00
// Window is visible and contains current character
if ( geometry.contains(x, y) )
{
const int line_len = win->width + win->right_shadow;
2019-10-08 04:37:19 +02:00
auto tmp = &win->data[(y - win_y) * line_len + (x - win_x)];
2018-01-05 00:49:00 +01:00
if ( ! tmp->attr.bit.transparent ) // Current character not transparent
{
if ( tmp->attr.bit.color_overlay ) // Transparent shadow
2018-01-05 00:49:00 +01:00
{
// Keep the current vterm character
2019-07-29 02:34:58 +02:00
if ( sc != &s_ch )
std::memcpy (&s_ch, sc, sizeof(s_ch));
2018-01-05 00:49:00 +01:00
s_ch.fg_color = tmp->fg_color;
s_ch.bg_color = tmp->bg_color;
s_ch.attr.bit.reverse = false;
s_ch.attr.bit.standout = false;
2020-12-31 20:45:10 +01:00
if ( s_ch.ch[0] == UniChar::LowerHalfBlock
|| s_ch.ch[0] == UniChar::UpperHalfBlock
|| s_ch.ch[0] == UniChar::LeftHalfBlock
|| s_ch.ch[0] == UniChar::RightHalfBlock
|| s_ch.ch[0] == UniChar::MediumShade
|| s_ch.ch[0] == UniChar::FullBlock )
2021-04-05 21:20:02 +02:00
s_ch.ch[0] = L' ';
2018-01-05 00:49:00 +01:00
sc = &s_ch;
}
else if ( tmp->attr.bit.inherit_background )
2018-01-05 00:49:00 +01:00
{
// Add the covered background to this character
2018-11-07 22:06:58 +01:00
std::memcpy (&i_ch, tmp, sizeof(i_ch));
2018-01-05 00:49:00 +01:00
i_ch.bg_color = sc->bg_color; // Last background color
sc = &i_ch;
}
else // Default
sc = tmp;
}
}
}
return *sc;
}
//----------------------------------------------------------------------
2020-12-31 20:45:10 +01:00
FChar FVTerm::getCharacter ( CharacterType char_type
, const FPoint& pos
2020-10-23 01:07:59 +02:00
, const FTermArea* area )
{
2016-12-28 16:29:49 +01:00
// Gets the overlapped or the covered character for a given position
2020-10-23 01:07:59 +02:00
const int x = pos.getX();
const int y = pos.getY();
int xx = ( x > 0 ) ? x : 0;
int yy = ( y > 0 ) ? y : 0;
2016-12-28 16:29:49 +01:00
if ( xx >= vterm->width )
xx = vterm->width - 1;
2016-12-28 16:29:49 +01:00
if ( yy >= vterm->height )
yy = vterm->height - 1;
2019-10-08 04:37:19 +02:00
auto cc = &vdesktop->data[yy * vdesktop->width + xx]; // covered character
if ( ! area || ! getWindowList() || getWindowList()->empty() )
return *cc;
2020-10-24 01:56:15 +02:00
// Get the window layer of this widget object
const auto has_an_owner = area->hasOwner();
const auto area_owner = area->getOwner<FVTerm*>();
const int layer = has_an_owner ? getLayer(area_owner) : 0;
for (auto&& win_obj : *getWindowList())
{
bool significant_char{false};
// char_type can be "overlapped_character"
// or "covered_character"
2020-12-31 20:45:10 +01:00
if ( char_type == CharacterType::Covered )
significant_char = bool(layer >= getLayer(win_obj));
else
significant_char = bool(layer < getLayer(win_obj));
if ( has_an_owner && area_owner != win_obj && significant_char )
{
const auto& win = win_obj->getVWin();
2016-12-28 16:29:49 +01:00
if ( ! win || ! win->visible )
continue;
2016-12-28 16:29:49 +01:00
const FRect geometry { win->offset_left, win->offset_top
, std::size_t(win->width) + std::size_t(win->right_shadow)
, std::size_t(win->height) + std::size_t(win->bottom_shadow) };
2016-12-28 16:29:49 +01:00
// Window visible and contains current character
if ( geometry.contains(x, y) )
getAreaCharacter (FPoint{x, y}, win, cc);
2016-12-28 16:29:49 +01:00
}
2020-12-31 20:45:10 +01:00
else if ( char_type == CharacterType::Covered )
break;
}
2016-12-28 16:29:49 +01:00
return *cc;
}
//----------------------------------------------------------------------
2020-10-24 01:56:15 +02:00
inline FChar FVTerm::getCoveredCharacter (const FPoint& pos, const FTermArea* area)
{
2016-12-28 16:29:49 +01:00
// Gets the covered character for a given position
2020-12-31 20:45:10 +01:00
return getCharacter (CharacterType::Covered, pos, area);
2016-12-28 16:29:49 +01:00
}
2016-12-28 16:29:49 +01:00
//----------------------------------------------------------------------
2020-10-24 01:56:15 +02:00
inline FChar FVTerm::getOverlappedCharacter (const FPoint& pos, const FTermArea* area)
2016-12-28 16:29:49 +01:00
{
// Gets the overlapped character for a given position
2020-12-31 20:45:10 +01:00
return getCharacter (CharacterType::Overlapped, pos, area);
2016-12-28 16:29:49 +01:00
}
2020-05-24 02:15:43 +02:00
//----------------------------------------------------------------------
2020-06-06 21:10:06 +02:00
void FVTerm::init()
{
2018-02-04 19:42:30 +01:00
init_object = this;
vterm = nullptr;
vdesktop = nullptr;
2017-08-12 22:55:29 +02:00
try
{
fterm = std::make_shared<FTerm>();
term_pos = std::make_shared<FPoint>(-1, -1);
output_buffer = std::make_shared<OutputBuffer>();
window_list = std::make_shared<FVTermList>();
2017-08-12 22:55:29 +02:00
}
2020-05-13 23:47:14 +02:00
catch (const std::bad_alloc&)
2017-08-12 22:55:29 +02:00
{
2020-05-13 23:47:14 +02:00
badAllocOutput ("FTerm, FPoint, or std::queue<int>");
2020-06-06 21:10:06 +02:00
return;
2017-08-12 22:55:29 +02:00
}
2020-06-06 21:10:06 +02:00
// Presetting of the current locale for full-width character support.
// The final setting is made later in FTerm::init_locale().
std::setlocale (LC_ALL, "");
2016-12-28 16:29:49 +01:00
// term_attribute stores the current state of the terminal
2020-12-05 21:24:09 +01:00
term_attribute.ch = {{ L'\0' }};
2020-12-31 20:45:10 +01:00
term_attribute.fg_color = FColor::Default;
term_attribute.bg_color = FColor::Default;
2018-02-04 19:42:30 +01:00
term_attribute.attr.byte[0] = 0;
2020-04-13 12:40:11 +02:00
term_attribute.attr.byte[1] = 0;
term_attribute.attr.byte[2] = 0;
term_attribute.attr.byte[3] = 0;
2016-12-28 16:29:49 +01:00
// next_attribute contains the state of the next printed character
2018-11-07 22:06:58 +01:00
std::memcpy (&next_attribute, &term_attribute, sizeof(next_attribute));
// Create virtual terminal
FRect term_geometry {0, 0, FTerm::getColumnNumber(), FTerm::getLineNumber()};
createVTerm (term_geometry.getSize());
// Create virtual desktop area
FSize shadow_size{0, 0};
2016-12-28 16:29:49 +01:00
createArea (term_geometry, shadow_size, vdesktop);
vdesktop->visible = true;
active_area = vdesktop;
2020-08-15 23:42:29 +02:00
// Initialize the flush and last terminal size check time
time_last_flush.tv_sec = 0;
time_last_flush.tv_usec = 0;
2020-08-15 23:42:29 +02:00
last_term_size_check.tv_sec = 0;
last_term_size_check.tv_usec = 0;
}
//----------------------------------------------------------------------
void FVTerm::init_characterLengths()
{
2021-05-09 21:03:59 +02:00
const auto& opti_move = FTerm::getFOptiMove();
cursor_address_length = opti_move.getCursorAddressLength();
erase_char_length = opti_move.getEraseCharsLength();
repeat_char_length = opti_move.getRepeatCharLength();
clr_bol_length = opti_move.getClrBolLength();
clr_eol_length = opti_move.getClrEolLength();
if ( cursor_address_length == 0 )
cursor_address_length = INT_MAX;
if ( erase_char_length == 0 )
erase_char_length = INT_MAX;
if ( repeat_char_length == 0 )
repeat_char_length = INT_MAX;
if ( clr_bol_length == 0 )
clr_bol_length = INT_MAX;
if ( clr_eol_length == 0 )
clr_eol_length = INT_MAX;
}
//----------------------------------------------------------------------
void FVTerm::init_combined_character()
{
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(UNIT_TEST)
if ( FTermFreeBSD::isFreeBSDConsole() )
return;
#endif
if ( FTerm::getEncoding() != Encoding::UTF8 )
return;
2021-05-09 21:03:59 +02:00
const auto& term_detection = FTerm::getFTermDetection();
if ( term_detection.isCygwinTerminal() )
return;
if ( term_detection.isXTerminal()
|| term_detection.isUrxvtTerminal()
|| term_detection.isMinttyTerm()
|| term_detection.isPuttyTerminal() )
{
combined_char_support = true;
}
}
//----------------------------------------------------------------------
void FVTerm::finish() const
{
2016-12-28 16:29:49 +01:00
// Show the input cursor
showCursor();
2016-12-28 16:29:49 +01:00
// Clear the terminal
setNormal();
2017-03-26 20:40:04 +02:00
2020-06-06 21:10:06 +02:00
if ( FTerm::hasAlternateScreen()
&& FTerm::getFTermData().isInAlternateScreen() )
2017-03-26 20:40:04 +02:00
clearTerm();
forceTerminalUpdate();
2016-12-28 16:29:49 +01:00
// remove virtual terminal + virtual desktop area
removeArea (vdesktop);
removeArea (vterm);
2020-08-11 23:04:46 +02:00
init_object = nullptr;
2016-12-28 16:29:49 +01:00
}
2018-02-08 00:25:51 +01:00
//----------------------------------------------------------------------
2020-10-23 01:07:59 +02:00
void FVTerm::putAreaLine (const FChar& area_char, FChar& vterm_char, std::size_t length)
2018-02-08 00:25:51 +01:00
{
// copy "length" characters from area to terminal
2020-10-23 01:07:59 +02:00
std::memcpy (&vterm_char, &area_char, sizeof(vterm_char) * length);
2018-02-08 00:25:51 +01:00
}
//----------------------------------------------------------------------
2020-10-23 01:07:59 +02:00
void FVTerm::putAreaCharacter ( const FPoint& pos, const FTermArea* area
, const FChar& area_char, FChar& vterm_char )
2018-02-08 00:25:51 +01:00
{
2020-10-23 01:07:59 +02:00
if ( area_char.attr.bit.transparent ) // Transparent
2018-02-08 00:25:51 +01:00
{
// Restore one character on vterm
2020-10-23 01:07:59 +02:00
FChar ch = getCoveredCharacter (pos, area);
std::memcpy (&vterm_char, &ch, sizeof(vterm_char));
2018-02-08 00:25:51 +01:00
}
else // Mot transparent
{
2020-10-23 01:07:59 +02:00
if ( area_char.attr.bit.color_overlay ) // Transparent shadow
2018-02-08 00:25:51 +01:00
{
// Get covered character + add the current color
2020-10-23 01:07:59 +02:00
FChar ch = getCoveredCharacter (pos, area);
ch.fg_color = area_char.fg_color;
ch.bg_color = area_char.bg_color;
2018-02-08 00:25:51 +01:00
ch.attr.bit.reverse = false;
ch.attr.bit.standout = false;
2020-12-31 20:45:10 +01:00
if ( ch.ch[0] == UniChar::LowerHalfBlock
|| ch.ch[0] == UniChar::UpperHalfBlock
|| ch.ch[0] == UniChar::LeftHalfBlock
|| ch.ch[0] == UniChar::RightHalfBlock
|| ch.ch[0] == UniChar::MediumShade
|| ch.ch[0] == UniChar::FullBlock )
2021-04-05 21:20:02 +02:00
ch.ch[0] = L' ';
2018-02-08 00:25:51 +01:00
2020-10-23 01:07:59 +02:00
std::memcpy (&vterm_char, &ch, sizeof(vterm_char));
2018-02-08 00:25:51 +01:00
}
2020-10-23 01:07:59 +02:00
else if ( area_char.attr.bit.inherit_background )
2018-02-08 00:25:51 +01:00
{
// Add the covered background to this character
2019-10-08 04:37:19 +02:00
FChar ch{};
2020-10-23 01:07:59 +02:00
std::memcpy (&ch, &area_char, sizeof(ch));
FChar cc = getCoveredCharacter (pos, area);
2018-02-08 00:25:51 +01:00
ch.bg_color = cc.bg_color;
2020-10-23 01:07:59 +02:00
std::memcpy (&vterm_char, &ch, sizeof(vterm_char));
2018-02-08 00:25:51 +01:00
}
else // Default
2020-10-23 01:07:59 +02:00
std::memcpy (&vterm_char, &area_char, sizeof(vterm_char));
2018-02-08 00:25:51 +01:00
}
}
//----------------------------------------------------------------------
2020-04-13 12:40:11 +02:00
void FVTerm::getAreaCharacter ( const FPoint& pos, const FTermArea* area
2019-10-08 04:37:19 +02:00
, FChar*& cc )
{
const int area_x = area->offset_left;
const int area_y = area->offset_top;
const int line_len = area->width + area->right_shadow;
const int x = pos.getX();
const int y = pos.getY();
2020-10-14 23:43:34 +02:00
auto& tmp = area->data[(y - area_y) * line_len + (x - area_x)];
// Current character not transparent
2020-10-14 23:43:34 +02:00
if ( ! tmp.attr.bit.transparent )
{
2020-10-14 23:43:34 +02:00
if ( tmp.attr.bit.color_overlay ) // transparent shadow
{
// Keep the current vterm character
2018-11-07 22:06:58 +01:00
std::memcpy (&s_ch, cc, sizeof(s_ch));
2020-10-14 23:43:34 +02:00
s_ch.fg_color = tmp.fg_color;
s_ch.bg_color = tmp.bg_color;
s_ch.attr.bit.reverse = false;
s_ch.attr.bit.standout = false;
cc = &s_ch;
}
2020-10-14 23:43:34 +02:00
else if ( tmp.attr.bit.inherit_background )
{
// Add the covered background to this character
2020-10-14 23:43:34 +02:00
std::memcpy (&i_ch, &tmp, sizeof(i_ch));
i_ch.bg_color = cc->bg_color; // last background color
cc = &i_ch;
}
else // default
2020-10-14 23:43:34 +02:00
cc = &tmp;
}
}
2016-12-28 16:29:49 +01:00
//----------------------------------------------------------------------
bool FVTerm::clearTerm (wchar_t fillchar) const
2016-12-28 16:29:49 +01:00
{
// Clear the real terminal and put cursor at home
2018-02-08 00:25:51 +01:00
2020-12-31 20:45:10 +01:00
const auto& cl = TCAP(t_clear_screen);
const auto& cd = TCAP(t_clr_eos);
const auto& cb = TCAP(t_clr_eol);
const bool ut = FTermcap::background_color_erase;
2020-10-25 01:21:45 +02:00
const bool normal = FTerm::isNormal (next_attribute);
appendAttributes (next_attribute);
2016-12-28 16:29:49 +01:00
if ( ! ( (cl || cd || cb) && (normal || ut) )
|| fillchar != L' '
|| ! draw_completed )
{
2016-12-28 16:29:49 +01:00
return false;
}
2018-02-08 00:25:51 +01:00
if ( cl ) // Clear screen
{
appendOutputBuffer (FTermControl{cl});
2018-11-20 21:11:04 +01:00
term_pos->setPoint(0, 0);
2016-12-28 16:29:49 +01:00
}
2018-02-08 00:25:51 +01:00
else if ( cd ) // Clear to end of screen
2016-12-28 16:29:49 +01:00
{
setTermXY (0, 0);
appendOutputBuffer (FTermControl{cd});
2017-09-11 03:06:02 +02:00
term_pos->setPoint(-1, -1);
2016-12-28 16:29:49 +01:00
}
2018-02-08 00:25:51 +01:00
else if ( cb ) // Clear to end of line
2016-12-28 16:29:49 +01:00
{
2017-09-11 03:06:02 +02:00
term_pos->setPoint(-1, -1);
2020-10-25 01:21:45 +02:00
for (auto i{0}; i < int(FTerm::getLineNumber()); i++)
2016-12-28 16:29:49 +01:00
{
setTermXY (0, i);
appendOutputBuffer (FTermControl{cb});
}
2016-12-28 16:29:49 +01:00
2018-11-20 21:11:04 +01:00
setTermXY (0, 0);
}
2016-12-28 16:29:49 +01:00
flush();
2016-12-28 16:29:49 +01:00
return true;
}
2018-02-08 00:25:51 +01:00
//----------------------------------------------------------------------
2020-07-12 17:54:13 +02:00
bool FVTerm::clearFullArea (const FTermArea* area, FChar& nc) const
2018-02-08 00:25:51 +01:00
{
// Clear area
const int area_size = area->width * area->height;
2019-10-08 04:37:19 +02:00
std::fill_n (area->data, area_size, nc);
2018-02-08 00:25:51 +01:00
if ( area != vdesktop ) // Is the area identical to the desktop?
return false;
// Try to clear the terminal rapidly with a control sequence
if ( clearTerm (nc.ch[0]) )
2018-02-08 00:25:51 +01:00
{
nc.attr.bit.printed = true;
2019-10-08 04:37:19 +02:00
std::fill_n (vterm->data, area_size, nc);
2018-02-08 00:25:51 +01:00
}
else
{
2020-10-25 01:21:45 +02:00
for (auto i{0}; i < vdesktop->height; i++)
2018-02-08 00:25:51 +01:00
{
vdesktop->changes[i].xmin = 0;
vdesktop->changes[i].xmax = uInt(vdesktop->width) - 1;
vdesktop->changes[i].trans_count = 0;
}
vdesktop->has_changes = true;
}
return true;
}
//----------------------------------------------------------------------
2020-04-13 12:40:11 +02:00
void FVTerm::clearAreaWithShadow (const FTermArea* area, const FChar& nc)
2018-02-08 00:25:51 +01:00
{
2019-10-08 04:37:19 +02:00
FChar t_char = nc;
const int total_width = area->width + area->right_shadow;
2018-02-08 00:25:51 +01:00
t_char.attr.bit.transparent = true;
2020-10-25 01:21:45 +02:00
for (auto y{0}; y < area->height; y++)
2018-02-08 00:25:51 +01:00
{
const int pos = y * total_width;
2018-02-08 00:25:51 +01:00
// Clear area
2019-10-08 04:37:19 +02:00
std::fill_n (&area->data[pos], total_width, nc);
2018-02-08 00:25:51 +01:00
// Make right shadow transparent
2019-10-08 04:37:19 +02:00
std::fill_n (&area->data[pos + area->width], area->right_shadow, t_char);
2018-02-08 00:25:51 +01:00
}
// Make bottom shadow transparent
2020-10-25 01:21:45 +02:00
for (auto y{0}; y < area->bottom_shadow; y++)
2018-02-08 00:25:51 +01:00
{
const int pos = total_width * (y + area->height);
2019-10-08 04:37:19 +02:00
std::fill_n (&area->data[pos], total_width, t_char);
2018-02-08 00:25:51 +01:00
}
}
//----------------------------------------------------------------------
2017-11-30 02:38:55 +01:00
bool FVTerm::canClearToEOL (uInt xmin, uInt y)
{
2017-11-30 02:38:55 +01:00
// Is the line from xmin to the end of the line blank?
// => clear to end of line
2020-10-25 01:21:45 +02:00
auto& vt = vterm;
2020-12-31 20:45:10 +01:00
const auto& ce = TCAP(t_clr_eol);
2020-10-23 01:07:59 +02:00
const auto& min_char = vt->data[y * uInt(vt->width) + xmin];
2017-11-30 02:38:55 +01:00
2021-04-05 21:20:02 +02:00
if ( ce && min_char.ch[0] == L' ' )
{
2017-11-30 02:38:55 +01:00
uInt beginning_whitespace = 1;
const bool normal = FTerm::isNormal(min_char);
const bool& ut = FTermcap::background_color_erase;
2016-11-26 15:18:44 +01:00
2017-11-30 02:38:55 +01:00
for (uInt x = xmin + 1; x < uInt(vt->width); x++)
2016-11-26 15:18:44 +01:00
{
2020-10-23 01:07:59 +02:00
const auto& ch = vt->data[y * uInt(vt->width) + x];
2016-12-28 16:29:49 +01:00
2020-10-23 01:07:59 +02:00
if ( min_char == ch )
2017-11-30 02:38:55 +01:00
beginning_whitespace++;
else
break;
}
2017-11-30 02:38:55 +01:00
if ( beginning_whitespace == uInt(vt->width) - xmin
&& (ut || normal)
&& clr_eol_length < beginning_whitespace )
2017-11-30 02:38:55 +01:00
return true;
}
return false;
}
//----------------------------------------------------------------------
bool FVTerm::canClearLeadingWS (uInt& xmin, uInt y)
{
// Line has leading whitespace
// => clear from xmin to beginning of line
2020-10-25 01:21:45 +02:00
auto& vt = vterm;
2020-12-31 20:45:10 +01:00
const auto& cb = TCAP(t_clr_bol);
2020-10-23 01:07:59 +02:00
const auto& first_char = vt->data[y * uInt(vt->width)];
2017-11-30 02:38:55 +01:00
2021-04-05 21:20:02 +02:00
if ( cb && first_char.ch[0] == L' ' )
2017-11-30 02:38:55 +01:00
{
uInt leading_whitespace = 1;
const bool normal = FTerm::isNormal(first_char);
const bool& ut = FTermcap::background_color_erase;
2019-08-25 22:16:00 +02:00
for (uInt x{1}; x < uInt(vt->width); x++)
2017-11-30 02:38:55 +01:00
{
2020-10-23 01:07:59 +02:00
const auto& ch = vt->data[y * uInt(vt->width) + x];
2017-11-30 02:38:55 +01:00
2020-10-23 01:07:59 +02:00
if ( first_char == ch )
2017-11-30 02:38:55 +01:00
leading_whitespace++;
else
break;
2016-11-26 15:18:44 +01:00
}
2017-11-30 02:38:55 +01:00
if ( leading_whitespace > xmin
&& (ut || normal)
&& clr_bol_length < leading_whitespace )
{
2017-11-30 02:38:55 +01:00
xmin = leading_whitespace - 1;
return true;
}
}
2017-11-30 02:38:55 +01:00
return false;
}
2017-11-30 02:38:55 +01:00
//----------------------------------------------------------------------
2017-12-19 02:06:27 +01:00
bool FVTerm::canClearTrailingWS (uInt& xmax, uInt y)
2017-11-30 02:38:55 +01:00
{
2017-12-19 02:06:27 +01:00
// Line has trailing whitespace
2017-11-30 02:38:55 +01:00
// => clear from xmax to end of line
2020-10-25 01:21:45 +02:00
auto& vt = vterm;
2020-12-31 20:45:10 +01:00
const auto& ce = TCAP(t_clr_eol);
2020-10-23 01:07:59 +02:00
const auto& last_char = vt->data[(y + 1) * uInt(vt->width) - 1];
2021-04-05 21:20:02 +02:00
if ( ce && last_char.ch[0] == L' ' )
2017-11-30 02:38:55 +01:00
{
2017-12-19 02:06:27 +01:00
uInt trailing_whitespace = 1;
const bool normal = FTerm::isNormal(last_char);
const bool& ut = FTermcap::background_color_erase;
2017-11-30 02:38:55 +01:00
for (uInt x = uInt(vt->width) - 1; x > 0 ; x--)
{
2020-10-23 01:07:59 +02:00
const auto& ch = vt->data[y * uInt(vt->width) + x];
2020-10-23 01:07:59 +02:00
if ( last_char == ch )
2017-12-19 02:06:27 +01:00
trailing_whitespace++;
2017-11-30 02:38:55 +01:00
else
break;
}
2017-12-19 02:06:27 +01:00
if ( trailing_whitespace > uInt(vt->width) - xmax
2017-11-30 02:38:55 +01:00
&& (ut || normal)
&& clr_bol_length < trailing_whitespace )
2017-11-30 02:38:55 +01:00
{
2017-12-19 02:06:27 +01:00
xmax = uInt(vt->width) - trailing_whitespace;
2017-11-30 02:38:55 +01:00
return true;
2016-12-28 16:29:49 +01:00
}
2017-11-30 02:38:55 +01:00
}
2016-12-28 16:29:49 +01:00
2017-11-30 02:38:55 +01:00
return false;
}
2016-12-28 16:29:49 +01:00
2017-11-30 02:38:55 +01:00
//----------------------------------------------------------------------
2020-07-12 17:00:16 +02:00
bool FVTerm::skipUnchangedCharacters (uInt& x, uInt xmax, uInt y) const
2017-11-30 02:38:55 +01:00
{
// Skip characters without changes if it is faster than redrawing
2020-10-25 01:21:45 +02:00
auto& vt = vterm;
2020-11-08 12:17:05 +01:00
auto& print_char = vt->data[y * uInt(vt->width) + x];
print_char.attr.bit.printed = true;
2017-11-30 02:38:55 +01:00
2020-11-08 12:17:05 +01:00
if ( print_char.attr.bit.no_changes )
2017-11-30 02:38:55 +01:00
{
2019-08-25 22:16:00 +02:00
uInt count{1};
2017-11-30 02:38:55 +01:00
for (uInt i = x + 1; i <= xmax; i++)
2016-12-28 16:29:49 +01:00
{
2020-11-08 18:51:59 +01:00
const auto& ch = vt->data[y * uInt(vt->width) + i];
2017-11-30 02:38:55 +01:00
2020-11-08 12:17:05 +01:00
if ( ch.attr.bit.no_changes )
2017-11-30 02:38:55 +01:00
count++;
else
break;
}
if ( count > cursor_address_length )
2017-11-30 02:38:55 +01:00
{
setTermXY (int(x + count), int(y));
x = x + count - 1;
return true;
}
}
return false;
}
//----------------------------------------------------------------------
void FVTerm::printRange ( uInt xmin, uInt xmax, uInt y
2020-08-11 23:04:46 +02:00
, bool draw_trailing_ws ) const
2017-11-30 02:38:55 +01:00
{
for (uInt x = xmin; x <= xmax; x++)
{
2020-10-25 01:21:45 +02:00
auto& vt = vterm;
2020-12-31 20:45:10 +01:00
const auto& ec = TCAP(t_erase_chars);
const auto& rp = TCAP(t_repeat_char);
2020-10-25 01:21:45 +02:00
auto& print_char = vt->data[y * uInt(vt->width) + x];
print_char.attr.bit.printed = true;
replaceNonPrintableFullwidth (x, print_char);
2017-11-30 02:38:55 +01:00
// skip character with no changes
if ( skipUnchangedCharacters(x, xmax, y) )
continue;
// Erase character
2021-04-05 21:20:02 +02:00
if ( ec && print_char.ch[0] == L' ' )
2017-11-30 02:38:55 +01:00
{
2020-12-31 20:45:10 +01:00
PrintState erase_state = \
2017-12-19 02:06:27 +01:00
eraseCharacters(x, xmax, y, draw_trailing_ws);
2017-11-30 02:38:55 +01:00
2020-12-31 20:45:10 +01:00
if ( erase_state == PrintState::LineCompletelyPrinted )
2017-11-30 02:38:55 +01:00
break;
}
else if ( rp ) // Repeat one character n-fold
{
repeatCharacter(x, xmax, y);
2016-12-28 16:29:49 +01:00
}
else // General character output
{
bool min_and_not_max( x == xmin && xmin != xmax );
printCharacter (x, y, min_and_not_max, print_char);
}
}
}
//----------------------------------------------------------------------
inline void FVTerm::replaceNonPrintableFullwidth ( uInt x
2020-10-25 01:21:45 +02:00
, FChar& print_char ) const
{
// Replace non-printable full-width characters that are truncated
// from the right or left terminal side
2020-10-25 01:21:45 +02:00
if ( x == 0 && isFullWidthPaddingChar(print_char) )
{
2020-12-31 20:45:10 +01:00
print_char.ch[0] = wchar_t(UniChar::SingleLeftAngleQuotationMark); //
print_char.ch[1] = L'\0';
2020-10-25 01:21:45 +02:00
print_char.attr.bit.fullwidth_padding = false;
}
else if ( x == uInt(vterm->width - 1)
2020-10-25 01:21:45 +02:00
&& isFullWidthChar(print_char) )
{
2020-12-31 20:45:10 +01:00
print_char.ch[0] = wchar_t(UniChar::SingleRightAngleQuotationMark); //
print_char.ch[1] = L'\0';
2020-10-25 01:21:45 +02:00
print_char.attr.bit.char_width = 1;
}
}
//----------------------------------------------------------------------
void FVTerm::printCharacter ( uInt& x, uInt y, bool min_and_not_max
2020-10-25 01:21:45 +02:00
, FChar& print_char) const
{
// General character output on terminal
2020-10-25 01:21:45 +02:00
if ( x < uInt(vterm->width - 1) && isFullWidthChar(print_char) )
{
printFullWidthCharacter (x, y, print_char);
}
else if ( x > 0 && x < uInt(vterm->width - 1)
2020-10-25 01:21:45 +02:00
&& isFullWidthPaddingChar(print_char) )
{
printFullWidthPaddingCharacter (x, y, print_char);
}
else if ( x > 0 && min_and_not_max )
{
printHalfCovertFullWidthCharacter (x, y, print_char);
}
else
{
// Print a half-width character
appendCharacter (print_char);
markAsPrinted (x, y);
}
}
//----------------------------------------------------------------------
void FVTerm::printFullWidthCharacter ( uInt& x, uInt y
2020-10-25 01:21:45 +02:00
, FChar& print_char ) const
{
const auto vt = vterm;
2020-10-25 01:21:45 +02:00
auto& next_char = vt->data[y * uInt(vt->width) + x + 1];
2020-10-25 01:21:45 +02:00
if ( print_char.attr.byte[0] == next_char.attr.byte[0]
&& print_char.attr.byte[1] == next_char.attr.byte[1]
&& print_char.fg_color == next_char.fg_color
&& print_char.bg_color == next_char.bg_color
&& isFullWidthChar(print_char)
&& isFullWidthPaddingChar(next_char) )
{
// Print a full-width character
appendCharacter (print_char);
markAsPrinted (x, y);
skipPaddingCharacter (x, y, print_char);
}
else
{
// Print ellipses for the 1st full-width character column
appendAttributes (print_char);
appendOutputBuffer (FTermChar{wchar_t(UniChar::HorizontalEllipsis)});
term_pos->x_ref()++;
markAsPrinted (x, y);
2020-10-25 01:21:45 +02:00
if ( isFullWidthPaddingChar(next_char) )
{
// Print ellipses for the 2nd full-width character column
x++;
appendAttributes (next_char);
appendOutputBuffer (FTermChar{wchar_t(UniChar::HorizontalEllipsis)});
term_pos->x_ref()++;
markAsPrinted (x, y);
}
}
}
//----------------------------------------------------------------------
void FVTerm::printFullWidthPaddingCharacter ( uInt& x, uInt y
2020-10-25 01:21:45 +02:00
, FChar& print_char) const
{
const auto vt = vterm;
2020-10-25 01:21:45 +02:00
auto& prev_char = vt->data[y * uInt(vt->width) + x - 1];
2020-10-25 01:21:45 +02:00
if ( print_char.attr.byte[0] == prev_char.attr.byte[0]
&& print_char.attr.byte[1] == prev_char.attr.byte[1]
&& print_char.fg_color == prev_char.fg_color
&& print_char.bg_color == prev_char.bg_color
&& isFullWidthChar(prev_char)
&& isFullWidthPaddingChar(print_char) )
{
// Move cursor one character to the left
2020-12-31 20:45:10 +01:00
const auto& le = TCAP(t_cursor_left);
const auto& LE = TCAP(t_parm_left_cursor);
if ( le )
appendOutputBuffer (FTermControl{le});
2020-10-14 23:43:34 +02:00
else if ( LE )
appendOutputBuffer (FTermControl{FTermcap::encodeParameter(LE, 1)});
2016-12-28 16:29:49 +01:00
else
{
skipPaddingCharacter (x, y, prev_char);
return;
}
// Print a full-width character
x--;
term_pos->x_ref()--;
appendCharacter (prev_char);
markAsPrinted (x, y);
skipPaddingCharacter (x, y, prev_char);
}
else
{
// Print ellipses for the 1st full-width character column
appendAttributes (print_char);
appendOutputBuffer (FTermChar{wchar_t(UniChar::HorizontalEllipsis)});
term_pos->x_ref()++;
markAsPrinted (x, y);
}
}
//----------------------------------------------------------------------
void FVTerm::printHalfCovertFullWidthCharacter ( uInt& x, uInt y
2020-10-25 01:21:45 +02:00
, FChar& print_char ) const
{
const auto vt = vterm;
2020-10-25 01:21:45 +02:00
auto& prev_char = vt->data[y * uInt(vt->width) + x - 1];
2020-10-25 01:21:45 +02:00
if ( isFullWidthChar(prev_char) && ! isFullWidthPaddingChar(print_char) )
{
// Move cursor one character to the left
2020-12-31 20:45:10 +01:00
const auto& le = TCAP(t_cursor_left);
const auto& LE = TCAP(t_parm_left_cursor);
if ( le )
appendOutputBuffer (FTermControl{le});
2020-10-14 23:43:34 +02:00
else if ( LE )
appendOutputBuffer (FTermControl{FTermcap::encodeParameter(LE, 1)});
2020-10-14 23:43:34 +02:00
if ( le || LE )
{
// Print ellipses for the 1st full-width character column
x--;
term_pos->x_ref()--;
2020-10-14 23:43:34 +02:00
appendAttributes (prev_char);
appendOutputBuffer (FTermChar{wchar_t(UniChar::HorizontalEllipsis)});
term_pos->x_ref()++;
2017-11-30 02:38:55 +01:00
markAsPrinted (x, y);
x++;
2017-11-30 02:38:55 +01:00
}
}
// Print a half-width character
appendCharacter (print_char);
markAsPrinted (x, y);
2017-11-30 02:38:55 +01:00
}
//----------------------------------------------------------------------
inline void FVTerm::skipPaddingCharacter ( uInt& x, uInt y
2020-10-25 01:21:45 +02:00
, const FChar& print_char ) const
{
2020-10-25 01:21:45 +02:00
if ( isFullWidthChar(print_char) ) // full-width character
{
x++; // Skip the following padding character
term_pos->x_ref()++;
markAsPrinted (x, y);
}
}
2017-11-30 02:38:55 +01:00
//----------------------------------------------------------------------
2020-12-31 20:45:10 +01:00
FVTerm::PrintState FVTerm::eraseCharacters ( uInt& x, uInt xmax, uInt y
, bool draw_trailing_ws ) const
2017-11-30 02:38:55 +01:00
{
// Erase a number of characters to draw simple whitespaces
const auto& vt = vterm;
2020-12-31 20:45:10 +01:00
const auto& ec = TCAP(t_erase_chars);
2020-10-23 01:07:59 +02:00
auto& print_char = vt->data[y * uInt(vt->width) + x];
2021-04-05 21:20:02 +02:00
if ( ! ec || print_char.ch[0] != L' ' )
2020-12-31 20:45:10 +01:00
return PrintState::NothingPrinted;
uInt whitespace{1};
2017-11-30 02:38:55 +01:00
for (uInt i = x + 1; i <= xmax; i++)
{
2020-10-23 01:07:59 +02:00
const auto& ch = vt->data[y * uInt(vt->width) + i];
2020-10-23 01:07:59 +02:00
if ( print_char == ch )
2017-11-30 02:38:55 +01:00
whitespace++;
else
break;
}
2017-11-30 02:38:55 +01:00
if ( whitespace == 1 )
{
2020-10-25 01:21:45 +02:00
appendCharacter (print_char);
2017-11-30 02:38:55 +01:00
markAsPrinted (x, y);
}
else
{
const uInt start_pos = x;
const bool& ut = FTermcap::background_color_erase;
2020-12-31 20:45:10 +01:00
const bool normal = FTerm::isNormal(print_char);
if ( whitespace > erase_char_length + cursor_address_length
2017-11-30 02:38:55 +01:00
&& (ut || normal) )
{
2020-10-25 01:21:45 +02:00
appendAttributes (print_char);
appendOutputBuffer (FTermControl{FTermcap::encodeParameter(ec, whitespace)});
2017-12-19 02:06:27 +01:00
if ( x + whitespace - 1 < xmax || draw_trailing_ws )
2017-11-30 02:38:55 +01:00
setTermXY (int(x + whitespace), int(y));
else
2020-12-31 20:45:10 +01:00
return PrintState::LineCompletelyPrinted;
2017-11-30 02:38:55 +01:00
x = x + whitespace - 1;
}
else
{
x--;
2020-04-13 12:40:11 +02:00
for (uInt i{0}; i < whitespace; i++)
{
2020-10-25 01:21:45 +02:00
appendCharacter (print_char);
2020-04-13 12:40:11 +02:00
x++;
}
2017-11-30 02:38:55 +01:00
}
2017-11-30 02:38:55 +01:00
markAsPrinted (start_pos, x, y);
}
2020-12-31 20:45:10 +01:00
return PrintState::WhitespacesPrinted;
2017-11-30 02:38:55 +01:00
}
2017-11-30 02:38:55 +01:00
//----------------------------------------------------------------------
2020-12-31 20:45:10 +01:00
FVTerm::PrintState FVTerm::repeatCharacter (uInt& x, uInt xmax, uInt y) const
2017-11-30 02:38:55 +01:00
{
// Repeat one character n-fold
const auto& vt = vterm;
2020-12-31 20:45:10 +01:00
const auto& rp = TCAP(t_repeat_char);
2020-10-25 01:21:45 +02:00
auto& print_char = vt->data[y * uInt(vt->width) + x];
2016-12-28 16:29:49 +01:00
2017-11-30 02:38:55 +01:00
if ( ! rp )
2020-12-31 20:45:10 +01:00
return PrintState::NothingPrinted;
2016-12-28 16:29:49 +01:00
2019-08-25 22:16:00 +02:00
uInt repetitions{1};
2016-12-28 16:29:49 +01:00
2017-11-30 02:38:55 +01:00
for (uInt i = x + 1; i <= xmax; i++)
{
2020-10-24 01:56:15 +02:00
const auto& ch = vt->data[y * uInt(vt->width) + i];
2016-12-28 16:29:49 +01:00
2020-10-25 01:21:45 +02:00
if ( print_char == ch )
2017-11-30 02:38:55 +01:00
repetitions++;
else
break;
}
2016-12-28 16:29:49 +01:00
2017-11-30 02:38:55 +01:00
if ( repetitions == 1 )
{
appendCharacter (print_char);
markAsPrinted (x, y);
}
else
{
const uInt start_pos = x;
2016-12-28 16:29:49 +01:00
if ( repetitions > repeat_char_length
&& is7bit(print_char.ch[0]) && print_char.ch[1] == L'\0' )
2017-11-30 02:38:55 +01:00
{
newFontChanges (print_char);
charsetChanges (print_char);
appendAttributes (print_char);
appendOutputBuffer (FTermControl{FTermcap::encodeParameter(rp, print_char.ch[0], repetitions)});
2018-12-28 22:57:43 +01:00
term_pos->x_ref() += int(repetitions);
2017-11-30 02:38:55 +01:00
x = x + repetitions - 1;
}
2017-11-30 02:38:55 +01:00
else
{
x--;
2016-12-28 16:29:49 +01:00
2020-04-13 12:40:11 +02:00
for (uInt i{0}; i < repetitions; i++)
{
2017-11-30 02:38:55 +01:00
appendCharacter (print_char);
2020-04-13 12:40:11 +02:00
x++;
}
2017-11-30 02:38:55 +01:00
}
markAsPrinted (start_pos, x, y);
}
2020-12-31 20:45:10 +01:00
return PrintState::RepeatCharacterPrinted;
2017-11-30 02:38:55 +01:00
}
//----------------------------------------------------------------------
2020-10-24 01:56:15 +02:00
inline bool FVTerm::isFullWidthChar (const FChar& ch) const
{
return ch.attr.bit.char_width == 2;
}
//----------------------------------------------------------------------
2020-10-24 01:56:15 +02:00
inline bool FVTerm::isFullWidthPaddingChar (const FChar& ch) const
{
2020-10-24 01:56:15 +02:00
return ch.attr.bit.fullwidth_padding;
}
2017-11-30 02:38:55 +01:00
//----------------------------------------------------------------------
void FVTerm::cursorWrap() const
2017-11-30 02:38:55 +01:00
{
// Wrap the cursor
const auto& vt = vterm;
2017-11-30 02:38:55 +01:00
if ( term_pos->getX() >= vt->width )
2016-12-28 16:29:49 +01:00
{
if ( term_pos->getY() == vt->height - 1 )
2016-12-28 16:29:49 +01:00
term_pos->x_ref()--;
else
{
if ( FTermcap::eat_nl_glitch )
{
2017-09-11 03:06:02 +02:00
term_pos->setPoint(-1, -1);
2016-12-28 16:29:49 +01:00
}
else if ( FTermcap::automatic_right_margin )
{
term_pos->setX(0);
term_pos->y_ref()++;
}
else
term_pos->x_ref()--;
}
}
}
//----------------------------------------------------------------------
2020-07-12 15:25:21 +02:00
bool FVTerm::printWrap (FTermArea* area) const
{
2019-08-25 22:16:00 +02:00
bool end_of_area{false};
const int width = area->width;
const int height = area->height;
const int rsh = area->right_shadow;
const int bsh = area->bottom_shadow;
// Line break at right margin
if ( area->cursor_x > width + rsh )
{
area->cursor_x = 1;
area->cursor_y++;
}
// Prevent up scrolling
if ( area->cursor_y > height + bsh )
{
area->cursor_y--;
end_of_area = true;
}
return end_of_area;
}
2020-10-14 23:43:34 +02:00
//----------------------------------------------------------------------
inline void FVTerm::printCharacterOnCoordinate ( FTermArea* area
, const int& ax
, const int& ay
2020-10-15 00:07:36 +02:00
, const FChar& ch) const
2020-10-14 23:43:34 +02:00
{
if ( area->cursor_x <= 0
|| area->cursor_y <= 0
|| ax >= area->width + area->right_shadow
|| ay >= area->height + area->bottom_shadow )
return;
const int line_len = area->width + area->right_shadow;
auto& ac = area->data[ay * line_len + ax]; // area character
if ( ac != ch ) // compare with an overloaded operator
{
if ( ( ! ac.attr.bit.transparent && ch.attr.bit.transparent )
|| ( ! ac.attr.bit.color_overlay && ch.attr.bit.color_overlay )
|| ( ! ac.attr.bit.inherit_background && ch.attr.bit.inherit_background ) )
{
// add one transparent character form line
area->changes[ay].trans_count++;
}
if ( ( ac.attr.bit.transparent && ! ch.attr.bit.transparent )
|| ( ac.attr.bit.color_overlay && ! ch.attr.bit.color_overlay )
|| ( ac.attr.bit.inherit_background && ! ch.attr.bit.inherit_background ) )
{
// remove one transparent character from line
area->changes[ay].trans_count--;
}
// copy character to area
std::memcpy (&ac, &ch, sizeof(ac));
if ( ax < int(area->changes[ay].xmin) )
area->changes[ay].xmin = uInt(ax);
if ( ax > int(area->changes[ay].xmax) )
area->changes[ay].xmax = uInt(ax);
}
}
//----------------------------------------------------------------------
2020-04-25 02:32:33 +02:00
void FVTerm::printPaddingCharacter (FTermArea* area, const FChar& term_char)
{
// Creates a padding-character from the current character (term_char)
// and prints it. It is a placeholder for the column after
// a full-width character.
2019-10-08 04:37:19 +02:00
FChar pc; // padding character
// Copy character to padding character
std::memcpy (&pc, &term_char, sizeof(pc));
2020-12-31 20:45:10 +01:00
if ( FTerm::getEncoding() == Encoding::UTF8 )
{
2020-12-05 21:24:09 +01:00
pc.ch = {{ L'\0' }};
pc.attr.bit.fullwidth_padding = true;
pc.attr.bit.char_width = 0;
}
else
{
pc.ch[0] = L'.';
pc.ch[1] = L'\0';
pc.attr.bit.char_width = 1;
}
// Print the padding-character
print (area, pc);
}
2017-11-30 02:38:55 +01:00
//----------------------------------------------------------------------
2020-10-24 01:56:15 +02:00
bool FVTerm::updateTerminalLine (uInt y) const
2017-11-30 02:38:55 +01:00
{
// Updates pending changes from line y to the terminal
bool ret{false};
const auto& vt = vterm;
2017-11-30 02:38:55 +01:00
uInt& xmin = vt->changes[y].xmin;
uInt& xmax = vt->changes[y].xmax;
if ( xmin <= xmax ) // Line has changes
2017-11-30 02:38:55 +01:00
{
2020-10-24 01:56:15 +02:00
ret = true;
2017-11-30 02:38:55 +01:00
bool draw_leading_ws = false;
2017-12-19 02:06:27 +01:00
bool draw_trailing_ws = false;
2020-12-31 20:45:10 +01:00
const auto& ce = TCAP(t_clr_eol);
2017-11-30 02:38:55 +01:00
// Clear rest of line
bool is_eol_clean = canClearToEOL (xmin, y);
if ( ! is_eol_clean )
{
// leading whitespace
draw_leading_ws = canClearLeadingWS (xmin, y);
2017-12-19 02:06:27 +01:00
// trailing whitespace
draw_trailing_ws = canClearTrailingWS (xmax, y);
2017-11-30 02:38:55 +01:00
}
setTermXY (int(xmin), int(y));
if ( is_eol_clean )
{
auto& min_char = vt->data[y * uInt(vt->width) + xmin];
2017-11-30 02:38:55 +01:00
appendAttributes (min_char);
appendOutputBuffer (FTermControl{ce});
2017-11-30 02:38:55 +01:00
markAsPrinted (xmin, uInt(vt->width - 1), y);
}
else
{
if ( draw_leading_ws )
{
2020-12-31 20:45:10 +01:00
const auto& cb = TCAP(t_clr_bol);
auto& first_char = vt->data[y * uInt(vt->width)];
2017-11-30 02:38:55 +01:00
appendAttributes (first_char);
appendOutputBuffer (FTermControl{cb});
2017-11-30 02:38:55 +01:00
markAsPrinted (0, xmin, y);
}
2017-12-19 02:06:27 +01:00
printRange (xmin, xmax, y, draw_trailing_ws);
2017-11-30 02:38:55 +01:00
2017-12-19 02:06:27 +01:00
if ( draw_trailing_ws )
2017-11-30 02:38:55 +01:00
{
auto& last_char = vt->data[(y + 1) * uInt(vt->width) - 1];
2017-11-30 02:38:55 +01:00
appendAttributes (last_char);
appendOutputBuffer (FTermControl{ce});
2017-11-30 02:38:55 +01:00
markAsPrinted (xmax + 1, uInt(vt->width - 1), y);
}
}
// Reset line changes
xmin = uInt(vt->width);
xmax = 0;
}
cursorWrap();
2020-10-24 01:56:15 +02:00
return ret;
2017-11-30 02:38:55 +01:00
}
//----------------------------------------------------------------------
2020-07-12 17:00:16 +02:00
bool FVTerm::updateTerminalCursor() const
{
2016-12-28 16:29:49 +01:00
// Updates the input cursor visibility and the position
if ( vterm && vterm->input_cursor_visible )
{
const int x = vterm->input_cursor_x;
const int y = vterm->input_cursor_y;
2016-12-28 16:29:49 +01:00
if ( isInsideTerminal(FPoint{x, y}) )
2016-12-28 16:29:49 +01:00
{
2017-09-11 03:06:02 +02:00
setTermXY (x, y);
2016-12-28 16:29:49 +01:00
showCursor();
return true;
}
}
else
hideCursor();
return false;
}
//----------------------------------------------------------------------
2020-07-12 15:25:21 +02:00
bool FVTerm::isInsideTerminal (const FPoint& pos) const
{
2016-12-28 16:29:49 +01:00
// Check whether the coordinates are within the virtual terminal
const FRect term_geometry {0, 0, FTerm::getColumnNumber(), FTerm::getLineNumber()};
2016-12-28 16:29:49 +01:00
if ( term_geometry.contains(pos) )
2016-12-28 16:29:49 +01:00
return true;
else
return false;
}
//----------------------------------------------------------------------
2020-07-12 15:25:21 +02:00
inline bool FVTerm::isTermSizeChanged() const
{
2020-08-15 23:42:29 +02:00
if ( ! isTermSizeCheckTimeout() )
return false;
FObject::getCurrentTime (&last_term_size_check);
auto& fterm_data = FTerm::getFTermData();
const auto& old_term_geometry = fterm_data.getTermGeometry();
2019-10-06 22:35:00 +02:00
FTerm::detectTermSize();
auto term_geometry = fterm_data.getTermGeometry();
term_geometry.move (-1, -1);
if ( old_term_geometry.getSize() != term_geometry.getSize() )
return true;
return false;
}
//----------------------------------------------------------------------
inline void FVTerm::flushTimeAdjustment() const
{
timeval now;
FObject::getCurrentTime(&now);
timeval diff = now - time_last_flush;
if ( diff.tv_sec > 0 || diff.tv_usec > 400000 )
{
flush_wait = MIN_FLUSH_WAIT; // Reset to minimum values after 400 ms
flush_average = MIN_FLUSH_WAIT;
flush_median = MIN_FLUSH_WAIT;
}
else
{
auto usec = uInt64(diff.tv_usec);
if ( usec < MIN_FLUSH_WAIT )
usec = MIN_FLUSH_WAIT;
else if ( usec > MAX_FLUSH_WAIT )
usec = MAX_FLUSH_WAIT;
if ( usec >= flush_average )
flush_average += (usec - flush_average) / 10;
else
{
uInt64 delta = (flush_average - usec) / 10;
if ( flush_average >= delta ) // Avoid uInt64 underflow
flush_average -= delta;
}
if ( usec >= flush_median )
flush_median += flush_average / 5;
else
{
uInt64 delta = flush_average / 5;
if ( flush_median >= delta ) // Avoid uInt64 underflow
flush_median -= delta;
}
flush_wait = flush_median;
}
}
//----------------------------------------------------------------------
inline bool FVTerm::isFlushTimeout()
{
return FObject::isTimeout (&time_last_flush, flush_wait);
}
2020-08-15 23:42:29 +02:00
//----------------------------------------------------------------------
inline bool FVTerm::isTermSizeCheckTimeout()
{
return FObject::isTimeout (&last_term_size_check, term_size_check_timeout);
}
2020-10-19 23:38:12 +02:00
//----------------------------------------------------------------------
2020-10-20 16:52:25 +02:00
inline bool FVTerm::hasPendingUpdates (const FTermArea* area)
2020-10-19 23:38:12 +02:00
{
return ( area && area->has_changes ) ? true : false;
}
//----------------------------------------------------------------------
2016-12-28 16:29:49 +01:00
inline void FVTerm::markAsPrinted (uInt pos, uInt line)
{
2016-12-28 16:29:49 +01:00
// Marks a character as printed
2019-10-08 04:37:19 +02:00
vterm->data[line * uInt(vterm->width) + pos].attr.bit.printed = true;
}
//----------------------------------------------------------------------
2016-12-28 16:29:49 +01:00
inline void FVTerm::markAsPrinted (uInt from, uInt to, uInt line)
{
2016-12-28 16:29:49 +01:00
// Marks characters in the specified range [from .. to] as printed
for (uInt x = from; x <= to; x++)
2019-10-08 04:37:19 +02:00
vterm->data[line * uInt(vterm->width) + x].attr.bit.printed = true;
}
//----------------------------------------------------------------------
2020-10-25 01:21:45 +02:00
inline void FVTerm::newFontChanges (FChar& next_char)
{
2016-12-28 16:29:49 +01:00
// NewFont special cases
if ( ! FTerm::isNewFont() )
return;
2016-12-28 16:29:49 +01:00
2020-12-31 20:45:10 +01:00
if ( next_char.ch[0] == UniChar::LowerHalfBlock )
{
2020-12-31 20:45:10 +01:00
next_char.ch[0] = wchar_t(UniChar::UpperHalfBlock);
2020-10-25 01:21:45 +02:00
next_char.attr.bit.reverse = true;
2016-12-28 16:29:49 +01:00
}
else if ( isReverseNewFontchar(next_char.ch[0]) )
2020-10-25 01:21:45 +02:00
next_char.attr.bit.reverse = true; // Show in reverse video
}
//----------------------------------------------------------------------
2020-10-25 01:21:45 +02:00
inline void FVTerm::charsetChanges (FChar& next_char)
{
const wchar_t& ch = next_char.ch[0];
std::copy( next_char.ch.begin()
, next_char.ch.end()
, next_char.encoded_char.begin() );
2020-12-31 20:45:10 +01:00
if ( FTerm::getEncoding() == Encoding::UTF8 )
2016-12-28 16:29:49 +01:00
return;
const wchar_t ch_enc = FTerm::charEncode(ch);
2019-10-08 04:37:19 +02:00
if ( ch_enc == ch )
return;
if ( ch_enc == 0 )
{
2020-12-31 20:45:10 +01:00
next_char.encoded_char[0] = wchar_t(FTerm::charEncode(ch, Encoding::ASCII));
return;
}
next_char.encoded_char[0] = ch_enc;
2016-12-28 16:29:49 +01:00
2020-12-31 20:45:10 +01:00
if ( FTerm::getEncoding() == Encoding::VT100 )
2020-10-25 01:21:45 +02:00
next_char.attr.bit.alt_charset = true;
2020-12-31 20:45:10 +01:00
else if ( FTerm::getEncoding() == Encoding::PC )
{
2020-10-25 01:21:45 +02:00
next_char.attr.bit.pc_charset = true;
2017-11-03 05:04:27 +01:00
if ( FTerm::isPuttyTerminal() )
return;
if ( FTerm::isXTerminal() && ch_enc < 0x20 ) // Character 0x00..0x1f
{
if ( FTerm::hasUTF8() )
2020-12-31 20:45:10 +01:00
next_char.encoded_char[0] = int(FTerm::charEncode(ch, Encoding::ASCII));
else
2017-11-03 05:04:27 +01:00
{
next_char.encoded_char[0] += 0x5f;
2020-10-25 01:21:45 +02:00
next_char.attr.bit.alt_charset = true;
2017-11-03 05:04:27 +01:00
}
}
}
}
//----------------------------------------------------------------------
2020-10-25 01:21:45 +02:00
inline void FVTerm::appendCharacter (FChar& next_char) const
{
const int term_width = vterm->width - 1;
const int term_height = vterm->height - 1;
2016-12-28 16:29:49 +01:00
if ( term_pos->getX() == term_width
2017-11-26 22:37:18 +01:00
&& term_pos->getY() == term_height )
2016-12-28 16:29:49 +01:00
appendLowerRight (next_char);
else
appendChar (next_char);
2016-12-28 16:29:49 +01:00
term_pos->x_ref()++;
}
2016-12-28 16:29:49 +01:00
//----------------------------------------------------------------------
2020-10-25 01:21:45 +02:00
inline void FVTerm::appendChar (FChar& next_char) const
2016-12-28 16:29:49 +01:00
{
newFontChanges (next_char);
charsetChanges (next_char);
appendAttributes (next_char);
characterFilter (next_char);
for (auto&& ch : next_char.encoded_char)
{
if ( ch != L'\0')
appendOutputBuffer (FTermChar{ch});
if ( ! combined_char_support )
return;
}
2016-12-28 16:29:49 +01:00
}
2016-12-28 16:29:49 +01:00
//----------------------------------------------------------------------
2020-10-25 01:21:45 +02:00
inline void FVTerm::appendAttributes (FChar& next_attr) const
2016-12-28 16:29:49 +01:00
{
// generate attribute string for the next character
2020-11-08 18:51:59 +01:00
const auto& attr_str = FTerm::changeAttribute (term_attribute, next_attr);
2016-12-28 16:29:49 +01:00
if ( attr_str )
appendOutputBuffer (FTermControl{attr_str});
}
//----------------------------------------------------------------------
2020-10-25 01:21:45 +02:00
void FVTerm::appendLowerRight (FChar& last_char) const
{
2020-12-31 20:45:10 +01:00
const auto& SA = TCAP(t_enter_am_mode);
const auto& RA = TCAP(t_exit_am_mode);
2016-12-28 16:29:49 +01:00
if ( ! FTermcap::automatic_right_margin )
{
2020-10-25 01:21:45 +02:00
appendChar (last_char);
2016-12-28 16:29:49 +01:00
}
else if ( SA && RA )
{
appendOutputBuffer (FTermControl{RA});
2020-10-25 01:21:45 +02:00
appendChar (last_char);
appendOutputBuffer (FTermControl{SA});
2016-12-28 16:29:49 +01:00
}
else
{
2020-12-31 20:45:10 +01:00
const auto& IC = TCAP(t_parm_ich);
const auto& im = TCAP(t_enter_insert_mode);
const auto& ei = TCAP(t_exit_insert_mode);
const auto& ip = TCAP(t_insert_padding);
const auto& ic = TCAP(t_insert_character);
2016-11-26 15:18:44 +01:00
const int x = int(FTerm::getColumnNumber()) - 2;
const int y = int(FTerm::getLineNumber()) - 1;
2016-12-28 16:29:49 +01:00
setTermXY (x, y);
2020-10-25 01:21:45 +02:00
appendChar (last_char);
2016-12-28 16:29:49 +01:00
term_pos->x_ref()++;
2016-12-28 16:29:49 +01:00
setTermXY (x, y);
2020-10-25 01:21:45 +02:00
FChar& second_last = *(&last_char - 1);
2016-12-28 16:29:49 +01:00
if ( IC )
{
appendOutputBuffer (FTermControl{FTermcap::encodeParameter(IC, 1)});
2020-10-25 01:21:45 +02:00
appendChar (second_last);
2016-12-28 16:29:49 +01:00
}
else if ( im && ei )
{
appendOutputBuffer (FTermControl{im});
2020-10-25 01:21:45 +02:00
appendChar (second_last);
2016-12-28 16:29:49 +01:00
if ( ip )
appendOutputBuffer (FTermControl{ip});
2016-12-28 16:29:49 +01:00
appendOutputBuffer (FTermControl{ei});
2016-12-28 16:29:49 +01:00
}
else if ( ic )
{
appendOutputBuffer (FTermControl{ic});
2020-10-25 01:21:45 +02:00
appendChar (second_last);
2016-12-28 16:29:49 +01:00
if ( ip )
appendOutputBuffer (FTermControl{ip});
2016-12-28 16:29:49 +01:00
}
}
}
//----------------------------------------------------------------------
inline void FVTerm::characterFilter (FChar& next_char) const
{
2021-04-05 21:20:02 +02:00
auto& sub_map = fterm->getCharSubstitutionMap();
const auto& entry = sub_map[next_char.encoded_char[0]];
2021-04-05 21:20:02 +02:00
if ( entry )
next_char.encoded_char[0] = entry;
}
//----------------------------------------------------------------------
inline bool FVTerm::isOutputBufferLimitReached() const
2016-12-28 16:29:49 +01:00
{
return output_buffer->size() >= TERMINAL_OUTPUT_BUFFER_LIMIT;
}
2018-01-25 09:31:59 +01:00
//----------------------------------------------------------------------
inline void FVTerm::appendOutputBuffer (const FTermControl& ctrl) const
{
output_buffer->emplace(std::make_tuple(OutputType::Control, TermString(ctrl.string)));
if ( isOutputBufferLimitReached() )
flush();
}
2016-12-28 16:29:49 +01:00
//----------------------------------------------------------------------
inline void FVTerm::appendOutputBuffer (const FTermChar& c) const
{
if ( c.ch != L'\0' )
appendOutputBuffer(FTermString{std::wstring(1, c.ch)});
}
//----------------------------------------------------------------------
void FVTerm::appendOutputBuffer (const FTermString& str) const
{
if ( ! output_buffer->empty()
&& std::get<0>(output_buffer->back()) == OutputType::String )
{
// Append string data to the back element
auto& string_buf = std::get<1>(output_buffer->back());
std::transform ( str.string.begin()
, str.string.end()
, std::back_inserter(string_buf.wstring)
, [] (wchar_t ch)
{
return ch;
}
);
}
else
output_buffer->emplace(std::make_tuple(OutputType::String, TermString(str.string)));
if ( isOutputBufferLimitReached() )
flush();
}
2018-01-25 09:31:59 +01:00
} // namespace finalcut