/***********************************************************************
* fterm.cpp - Base class for terminal control *
* *
* This file is part of the FINAL CUT widget toolkit *
* *
* Copyright 2012-2021 Markus Gans *
* *
* 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 *
* 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 *
* 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 *
* . *
***********************************************************************/
#include
#include
#include
#include
#include
#include "final/fapplication.h"
#include "final/fc.h"
#include "final/fcharmap.h"
#include "final/fkey_map.h"
#include "final/fkeyboard.h"
#include "final/flog.h"
#include "final/fmouse.h"
#include "final/foptiattr.h"
#include "final/foptimove.h"
#include "final/fstartoptions.h"
#include "final/fstring.h"
#include "final/fsystemimpl.h"
#include "final/fterm.h"
#include "final/ftermbuffer.h"
#include "final/ftermcap.h"
#include "final/ftermcapquirks.h"
#include "final/ftermdata.h"
#include "final/ftermdebugdata.h"
#include "final/ftermdetection.h"
#include "final/ftermios.h"
#include "final/ftermxterminal.h"
#if defined(UNIT_TEST)
#include "final/ftermlinux.h"
#include "final/ftermfreebsd.h"
#include "final/ftermopenbsd.h"
#elif defined(__linux__)
#include "final/ftermlinux.h"
#elif defined(__FreeBSD__) || defined(__DragonFly__)
#include "final/ftermfreebsd.h"
#elif defined(__NetBSD__) || defined(__OpenBSD__)
#include "final/ftermopenbsd.h"
#endif
namespace finalcut
{
namespace internal
{
struct var
{
static FTerm* init_term_object; // Global FTerm object
static bool term_initialized; // Global init state
static uInt object_counter; // Counts the number of object instances
};
FTerm* var::init_term_object{nullptr};
bool var::term_initialized{false};
uInt var::object_counter{0};
} // namespace internal
//----------------------------------------------------------------------
// class FTerm
//----------------------------------------------------------------------
// constructors and destructor
//----------------------------------------------------------------------
FTerm::FTerm()
{
internal::var::object_counter++;
}
//----------------------------------------------------------------------
FTerm::~FTerm() // destructor
{
if ( internal::var::init_term_object == this )
finish(); // Resetting console settings
internal::var::object_counter--;
if ( internal::var::object_counter == 0 )
printExitMessage();
}
// public methods of FTerm
//----------------------------------------------------------------------
std::size_t FTerm::getLineNumber()
{
const auto& term_geometry = FTerm::getFTermData().getTermGeometry();
if ( term_geometry.getHeight() == 0 )
detectTermSize();
return term_geometry.getHeight();
}
//----------------------------------------------------------------------
std::size_t FTerm::getColumnNumber()
{
const auto& term_geometry = FTerm::getFTermData().getTermGeometry();
if ( term_geometry.getWidth() == 0 )
detectTermSize();
return term_geometry.getWidth();
}
//----------------------------------------------------------------------
FString FTerm::getKeyName (FKey keynum)
{
const auto& keyboard = FTerm::getFKeyboard();
return keyboard.getKeyName (keynum);
}
//----------------------------------------------------------------------
charSubstitution& FTerm::getCharSubstitutionMap()
{
return FTerm::getFTermData().getCharSubstitutionMap();
}
//----------------------------------------------------------------------
int FTerm::getTTYFileDescriptor()
{
return FTerm::getFTermData().getTTYFileDescriptor();
}
//----------------------------------------------------------------------
std::string FTerm::getTermType()
{
return FTerm::getFTermData().getTermType();
}
//----------------------------------------------------------------------
std::string FTerm::getTermFileName()
{
return FTerm::getFTermData().getTermFileName();
}
//----------------------------------------------------------------------
int FTerm::getTabstop()
{
return FTermcap::tabstop;
}
//----------------------------------------------------------------------
int FTerm::getMaxColor()
{
return FTermcap::max_color;
}
//----------------------------------------------------------------------
auto FTerm::getColorPaletteTheme() -> std::shared_ptr&
{
static const auto& color_theme = make_unique>();
return *color_theme.get();
}
//----------------------------------------------------------------------
auto FTerm::getFSystem() -> std::unique_ptr&
{
static const auto& fsys = make_unique>(make_unique());
return *fsys.get();
}
//----------------------------------------------------------------------
auto FTerm::getFTermData() -> FTermData&
{
static const auto& data = make_unique();
return *data;
}
//----------------------------------------------------------------------
auto FTerm::getFOptiMove() -> FOptiMove&
{
static const auto& opti_move = make_unique();
return *opti_move;
}
//----------------------------------------------------------------------
auto FTerm::getFOptiAttr() -> FOptiAttr&
{
static const auto& opti_attr = make_unique();
return *opti_attr;
}
//----------------------------------------------------------------------
auto FTerm::getFTermDetection() -> FTermDetection&
{
static const auto& term_detection = make_unique();
return *term_detection;
}
//----------------------------------------------------------------------
auto FTerm::getFTermXTerminal() -> FTermXTerminal&
{
static const auto& xterm = make_unique();
return *xterm;
}
//----------------------------------------------------------------------
auto FTerm::getFKeyboard() -> FKeyboard&
{
static const auto& keyboard = make_unique();
return *keyboard;
}
//----------------------------------------------------------------------
auto FTerm::getFMouseControl() -> FMouseControl&
{
static const auto& mouse = make_unique();
return *mouse;
}
#if defined(__linux__) || defined(UNIT_TEST)
//----------------------------------------------------------------------
auto FTerm::getFTermLinux() -> FTermLinux&
{
static const auto& linux_console = make_unique();
return *linux_console;
}
#endif
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(UNIT_TEST)
//----------------------------------------------------------------------
auto FTerm::getFTermFreeBSD() -> FTermFreeBSD&
{
static const auto& freebsd_console = make_unique();
return *freebsd_console;
}
#endif
#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(UNIT_TEST)
//----------------------------------------------------------------------
auto FTerm::getFTermOpenBSD() -> FTermOpenBSD&
{
static const auto& openbsd_console = make_unique();
return *openbsd_console;
}
#endif
#if DEBUG
//----------------------------------------------------------------------
auto FTerm::getFTermDebugData() -> FTermDebugData&
{
static const auto& debug_data = make_unique();
return *debug_data;
}
#endif // DEBUG
//----------------------------------------------------------------------
bool FTerm::isNormal (const FChar& ch)
{
return FOptiAttr::isNormal(ch);
}
//----------------------------------------------------------------------
bool FTerm::hasUTF8()
{
return FTerm::getFTermData().hasUTF8Console();
}
//----------------------------------------------------------------------
bool FTerm::isMonochron()
{
return FTerm::getFTermData().isMonochron();
}
//----------------------------------------------------------------------
bool FTerm::isAnsiTerminal()
{
return FTerm::getFTermDetection().isAnsiTerminal();
}
//----------------------------------------------------------------------
bool FTerm::isXTerminal()
{
return FTerm::getFTermDetection().isXTerminal();
}
//----------------------------------------------------------------------
bool FTerm::isRxvtTerminal()
{
return FTerm::getFTermDetection().isRxvtTerminal();
}
//----------------------------------------------------------------------
bool FTerm::isUrxvtTerminal()
{
return FTerm::getFTermDetection().isUrxvtTerminal();
}
//----------------------------------------------------------------------
bool FTerm::isKdeTerminal()
{
return FTerm::getFTermDetection().isKdeTerminal();
}
//----------------------------------------------------------------------
bool FTerm::isGnomeTerminal()
{
return FTerm::getFTermDetection().isGnomeTerminal();
}
//----------------------------------------------------------------------
bool FTerm::isPuttyTerminal()
{
return FTerm::getFTermDetection().isPuttyTerminal();
}
//----------------------------------------------------------------------
bool FTerm::isWindowsTerminal()
{
return FTerm::getFTermDetection().isWindowsTerminal();
}
//----------------------------------------------------------------------
bool FTerm::isTeraTerm()
{
return FTerm::getFTermDetection().isTeraTerm();
}
//----------------------------------------------------------------------
bool FTerm::isCygwinTerminal()
{
return FTerm::getFTermDetection().isCygwinTerminal();
}
//----------------------------------------------------------------------
bool FTerm::isMinttyTerm()
{
return FTerm::getFTermDetection().isMinttyTerm();
}
//----------------------------------------------------------------------
bool FTerm::isLinuxTerm()
{
return FTerm::getFTermDetection().isLinuxTerm();
}
//----------------------------------------------------------------------
bool FTerm::isFreeBSDTerm()
{
return FTerm::getFTermDetection().isFreeBSDTerm();
}
//----------------------------------------------------------------------
bool FTerm::isNetBSDTerm()
{
return FTerm::getFTermDetection().isNetBSDTerm();
}
//----------------------------------------------------------------------
bool FTerm::isOpenBSDTerm()
{
return FTerm::getFTermDetection().isOpenBSDTerm();
}
//----------------------------------------------------------------------
bool FTerm::isSunTerminal()
{
return FTerm::getFTermDetection().isSunTerminal();
}
//----------------------------------------------------------------------
bool FTerm::isScreenTerm()
{
return FTerm::getFTermDetection().isScreenTerm();
}
//----------------------------------------------------------------------
bool FTerm::isTmuxTerm()
{
return FTerm::getFTermDetection().isTmuxTerm();
}
//----------------------------------------------------------------------
bool FTerm::isKtermTerminal()
{
return FTerm::getFTermDetection().isKtermTerminal();
}
//----------------------------------------------------------------------
bool FTerm::isMltermTerminal()
{
return FTerm::getFTermDetection().isMltermTerminal();
}
//----------------------------------------------------------------------
bool FTerm::isKittyTerminal()
{
return FTerm::getFTermDetection().isKittyTerminal();
}
//----------------------------------------------------------------------
bool FTerm::isNewFont()
{
return FTerm::getFTermData().isNewFont();
}
//----------------------------------------------------------------------
bool FTerm::isInitialized()
{
return internal::var::term_initialized;
}
//----------------------------------------------------------------------
bool FTerm::isCursorHideable()
{
const auto& cursor_off_str = disableCursorString();
if ( ! cursor_off_str.empty() )
return true;
return false;
}
//----------------------------------------------------------------------
bool FTerm::hasChangedTermSize()
{
return FTerm::getFTermData().hasTermResized();
}
//----------------------------------------------------------------------
bool FTerm::hasShadowCharacter()
{
return FTerm::getFTermData().hasShadowCharacter();
}
//----------------------------------------------------------------------
bool FTerm::hasHalfBlockCharacter()
{
return FTerm::getFTermData().hasHalfBlockCharacter();
}
//----------------------------------------------------------------------
bool FTerm::hasAlternateScreen()
{
return FTerm::getFTermData().hasAlternateScreen();
}
//----------------------------------------------------------------------
bool FTerm::canChangeColorPalette()
{
if ( isCygwinTerminal()
|| isKdeTerminal()
|| isTeraTerm()
|| isMltermTerminal()
|| isNetBSDTerm()
|| isOpenBSDTerm()
|| isSunTerminal()
|| isAnsiTerminal() )
return false;
return FTermcap::can_change_color_palette;
}
//----------------------------------------------------------------------
void FTerm::setTermType (const std::string& term_name)
{
FTerm::getFTermData().setTermType(term_name);
}
//----------------------------------------------------------------------
void FTerm::setInsertCursor (bool enable)
{
if ( enable )
setInsertCursorStyle();
else
setOverwriteCursorStyle();
}
//----------------------------------------------------------------------
void FTerm::redefineDefaultColors (bool enable)
{
if ( isNewFont() ) // NewFont need the reverse-video attribute
return;
getFTermXTerminal().redefineDefaultColors (enable);
}
//----------------------------------------------------------------------
void FTerm::setDblclickInterval (const uInt64 timeout)
{
const auto& mouse = FTerm::getFMouseControl();
mouse.setDblclickInterval(timeout);
}
//----------------------------------------------------------------------
void FTerm::useAlternateScreen (bool enable)
{
// Sets alternate screen usage
FTerm::getFTermData().useAlternateScreen(enable);
}
//----------------------------------------------------------------------
bool FTerm::setUTF8 (bool enable) // UTF-8 (Unicode)
{
auto& data = FTerm::getFTermData();
if ( data.isUTF8() == enable )
return enable;
if ( enable )
data.setUTF8(true);
else
data.setUTF8(false);
#if defined(__linux__)
FTerm::getFTermLinux().setUTF8 (enable);
#endif
return data.isUTF8();
}
//----------------------------------------------------------------------
bool FTerm::setVGAFont()
{
auto& data = FTerm::getFTermData();
if ( data.isVGAFont() )
return data.isVGAFont();
if ( hasNoFontSettingOption() )
return false;
if ( isXTerminal() || isScreenTerm()
|| isUrxvtTerminal() || FTermcap::osc_support )
{
data.setVGAFont(true);
// Set font in xterm to vga
getFTermXTerminal().setFont("vga");
data.setTermEncoding (Encoding::PC);
data.setNewFont(false);
}
#if defined(__linux__)
else if ( isLinuxTerm() )
{
auto& linux_console = FTerm::getFTermLinux();
data.setVGAFont(linux_console.loadVGAFont());
}
#endif // defined(__linux__)
else
data.setVGAFont(false);
if ( data.isVGAFont() )
{
data.supportShadowCharacter (true);
data.supportHalfBlockCharacter (true);
}
return data.isVGAFont();
}
//----------------------------------------------------------------------
bool FTerm::setNewFont()
{
auto& data = FTerm::getFTermData();
if ( isNewFont() )
return true;
if ( hasNoFontSettingOption() )
return false;
if ( isXTerminal() || isScreenTerm()
|| isUrxvtTerminal() || FTermcap::osc_support )
{
data.setNewFont(true);
// Set font in xterm to 8x16graph
getFTermXTerminal().setFont("8x16graph");
}
#if defined(__linux__)
else if ( isLinuxTerm() )
{
auto& linux_console = FTerm::getFTermLinux();
data.setNewFont(linux_console.loadNewFont());
}
#endif // defined(__linux__)
else
data.setNewFont(false);
if ( isNewFont() )
{
data.supportShadowCharacter (true);
data.supportHalfBlockCharacter (true);
}
return isNewFont();
}
//----------------------------------------------------------------------
bool FTerm::resetFont()
{
bool retval{false};
auto& data = FTerm::getFTermData();
if ( ! (data.isNewFont() || data.isVGAFont()) )
return false;
data.setNewFont(false);
data.setVGAFont(false);
if ( isXTerminal() || isScreenTerm()
|| isUrxvtTerminal() || FTermcap::osc_support )
{
const auto& font = data.getXtermFont();
if ( font.getLength() > 2 )
{
// restore saved xterm font
getFTermXTerminal().setFont(font);
}
else
{
// Set font in xterm to vga
getFTermXTerminal().setFont("vga");
}
retval = true;
}
#if defined(__linux__)
else if ( isLinuxTerm() )
{
auto& linux_console = FTerm::getFTermLinux();
retval = linux_console.loadOldFont();
}
#endif // defined(__linux__)
if ( retval )
{
data.setVGAFont(false);
data.setNewFont(false);
}
return retval;
}
//----------------------------------------------------------------------
int FTerm::openConsole()
{
auto& data = FTerm::getFTermData();
int fd = data.getTTYFileDescriptor();
const auto& termfilename = data.getTermFileName();
if ( termfilename.empty() )
return 0;
if ( fd >= 0 ) // console is already opened
return 0;
constexpr std::array terminal_devices =
{{
"/proc/self/fd/0",
"/dev/tty",
"/dev/tty0",
"/dev/vc/0",
"/dev/systty",
"/dev/console"
}};
for (auto&& entry : terminal_devices)
{
const auto& fsys = FTerm::getFSystem();
fd = fsys->open(entry, O_RDWR, 0);
data.setTTYFileDescriptor(fd);
if ( fd >= 0 )
return 0;
}
return -1; // No file descriptor referring to the console
}
//----------------------------------------------------------------------
int FTerm::closeConsole()
{
auto& data = FTerm::getFTermData();
const int fd = data.getTTYFileDescriptor();
int ret{-1};
if ( fd < 0 ) // console is already closed
return 0;
const auto& fsys = FTerm::getFSystem();
ret = fsys->close(fd); // close console
data.setTTYFileDescriptor(-1);
if ( ret == 0 )
return 0;
else
return -1;
}
//----------------------------------------------------------------------
std::string FTerm::moveCursorString (int xold, int yold, int xnew, int ynew)
{
// Returns the cursor move string
if ( FTerm::getFTermData().hasCursorOptimisation() )
{
auto& opti_move = FTerm::getFOptiMove();
return opti_move.moveCursor (xold, yold, xnew, ynew);
}
else
{
const auto& cursor_addr = FTermcap::encodeMotionParameter(TCAP(t_cursor_address), xnew, ynew);
return cursor_addr;
}
}
//----------------------------------------------------------------------
std::string FTerm::cursorsVisibilityString (bool enable)
{
// Hides or shows the input cursor on the terminal
std::string visibility_str{};
auto& data = FTerm::getFTermData();
if ( data.isCursorHidden() == enable )
return {};
if ( enable )
{
visibility_str = disableCursorString();
if ( ! visibility_str.empty() )
data.setCursorHidden (true); // Global state
}
else
{
visibility_str = enableCursorString();
if ( ! visibility_str.empty() )
data.setCursorHidden (false); // Global state
}
return visibility_str;
}
//----------------------------------------------------------------------
void FTerm::detectTermSize()
{
// Detect the terminal width and height
struct winsize win_size{};
auto& term_geometry = FTerm::getFTermData().getTermGeometry();
int ret{};
errno = 0;
do
{
const auto& fsys = FTerm::getFSystem();
ret = fsys->ioctl (FTermios::getStdOut(), TIOCGWINSZ, &win_size);
}
while (errno == EINTR);
if ( ret != 0 || win_size.ws_col == 0 || win_size.ws_row == 0 )
{
term_geometry.setPos (1, 1);
// Use COLUMNS or fallback to the xterm default width of 80 characters
uInt Columns = env2uint ("COLUMNS");
term_geometry.setWidth( ( Columns == 0) ? 80 : Columns);
// Use LINES or fallback to the xterm default height of 24 characters
uInt Lines = env2uint ("LINES");
term_geometry.setHeight( ( Lines == 0 ) ? 24 : Lines);
}
else
{
term_geometry.setRect(1, 1, win_size.ws_col, win_size.ws_row);
}
auto& opti_move = FTerm::getFOptiMove();
opti_move.setTermSize ( term_geometry.getWidth()
, term_geometry.getHeight() );
}
//----------------------------------------------------------------------
void FTerm::setTermSize (const FSize& size)
{
// Set xterm size
getFTermXTerminal().setTermSize (size);
}
//----------------------------------------------------------------------
void FTerm::setTermTitle (const FString& title)
{
// Set the xterm window title
getFTermXTerminal().setTitle (title);
}
//----------------------------------------------------------------------
void FTerm::setKDECursor (KdeKonsoleCursorShape style)
{
// Set cursor style in KDE konsole
if ( isKdeTerminal() )
{
oscPrefix();
putstringf (OSC "50;CursorShape=%d" BEL, style);
oscPostfix();
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
void FTerm::saveColorMap()
{
#if defined(__linux__)
FTerm::getFTermLinux().saveColorMap();
#endif
}
//----------------------------------------------------------------------
void FTerm::resetColorMap()
{
const auto& oc = TCAP(t_orig_colors);
const auto& op = TCAP(t_orig_pair);
if ( oc )
putstring (oc);
#if defined(__linux__)
else
FTerm::getFTermLinux().resetColorMap();
#endif
if ( op )
putstring (op);
std::fflush(stdout);
}
//----------------------------------------------------------------------
void FTerm::setPalette (FColor index, int r, int g, int b)
{
// Redefine RGB color value for a palette entry
const auto& Ic = TCAP(t_initialize_color);
const auto& Ip = TCAP(t_initialize_pair);
bool state{false};
index = FOptiAttr::vga2ansi(index);
if ( Ic || Ip )
{
std::string color_str{};
const int rr = (r * 1001) / 256;
const int gg = (g * 1001) / 256;
const int bb = (b * 1001) / 256;
if ( Ic )
color_str = FTermcap::encodeParameter(Ic, uInt16(index), rr, gg, bb);
else if ( Ip )
color_str = FTermcap::encodeParameter(Ip, uInt16(index), 0, 0, 0, rr, gg, bb);
if ( ! color_str.empty() )
{
putstring (color_str);
state = true;
}
}
#if defined(__linux__)
else
{
state = FTerm::getFTermLinux().setPalette(index, r, g, b);
}
#endif
if ( state )
std::fflush(stdout);
}
//----------------------------------------------------------------------
#if defined(__linux__) || defined(UNIT_TEST)
void FTerm::setBeep (int Hz, int ms)
{
FTerm::getFTermLinux().setBeep (Hz, ms);
}
#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(UNIT_TEST)
void FTerm::setBeep (int Hz, int ms)
{
FTerm::getFTermFreeBSD().setBeep (Hz, ms);
}
#elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(UNIT_TEST)
void FTerm::setBeep (int Hz, int ms)
{
FTerm::getFTermOpenBSD().setBeep (Hz, ms);
}
#else
void FTerm::setBeep (int, int)
{ }
#endif // defined(__linux__)
//----------------------------------------------------------------------
void FTerm::resetBeep()
{
#if defined(__linux__) || defined(UNIT_TEST)
FTerm::getFTermLinux().resetBeep();
#endif
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(UNIT_TEST)
FTerm::getFTermFreeBSD().resetBeep();
#endif
#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(UNIT_TEST)
FTerm::getFTermOpenBSD().resetBeep();
#endif
}
//----------------------------------------------------------------------
void FTerm::beep()
{
if ( TCAP(t_bell) )
putstring (TCAP(t_bell));
}
//----------------------------------------------------------------------
void FTerm::setEncoding (Encoding enc)
{
FTerm::getFTermData().setTermEncoding (enc);
assert ( enc == Encoding::UTF8
|| enc == Encoding::VT100 // VT100 line drawing
|| enc == Encoding::PC // CP-437
|| enc == Encoding::ASCII
|| enc == Encoding::Unknown
|| enc == Encoding::NUM_OF_ENCODINGS );
// Set the new putchar() function pointer
switch ( enc )
{
case Encoding::UTF8:
putchar() = &FTerm::putchar_UTF8;
break;
case Encoding::VT100:
case Encoding::PC:
if ( isXTerminal() && FTerm::getFTermData().hasUTF8Console() )
putchar() = &FTerm::putchar_UTF8;
else
putchar() = &FTerm::putchar_ASCII;
break;
case Encoding::ASCII:
case Encoding::Unknown:
case Encoding::NUM_OF_ENCODINGS:
putchar() = &FTerm::putchar_ASCII;
}
if ( isLinuxTerm() )
{
auto& opti_move = FTerm::getFOptiMove();
if ( enc == Encoding::VT100 || enc == Encoding::PC )
{
const char* empty{nullptr};
opti_move.set_tabular (empty);
}
else
opti_move.set_tabular (TCAP(t_tab));
}
}
//----------------------------------------------------------------------
Encoding FTerm::getEncoding()
{
return FTerm::getFTermData().getTermEncoding();
}
//----------------------------------------------------------------------
std::string FTerm::getEncodingString()
{
auto& data = FTerm::getFTermData();
const auto& term_encoding = data.getTermEncoding();
const auto& encoding_list = data.getEncodingList();
const auto& end = encoding_list.end();
for (auto it = encoding_list.begin(); it != end; ++it )
if ( it->second == term_encoding )
return it->first;
return "";
}
//----------------------------------------------------------------------
bool FTerm::charEncodable (wchar_t c)
{
const wchar_t ch = charEncode(c);
return ch > 0 && ch != c;
}
//----------------------------------------------------------------------
wchar_t FTerm::charEncode (wchar_t c)
{
const auto& data = FTerm::getFTermData();
return charEncode (c, data.getTermEncoding());
}
//----------------------------------------------------------------------
wchar_t FTerm::charEncode (wchar_t c, Encoding enc)
{
wchar_t ch_enc = c;
auto found = std::find_if ( fc::character.begin()
, fc::character.end()
, [&c] (const fc::CharEncodeMap& entry)
{
return entry.unicode == c;
} );
if ( found != fc::character.end() )
ch_enc = getCharacter(*found, enc);
if ( enc == Encoding::PC && ch_enc == c )
ch_enc = finalcut::unicode_to_cp437(c);
return ch_enc;
}
//----------------------------------------------------------------------
bool FTerm::scrollTermForward()
{
if ( TCAP(t_scroll_forward) )
{
putstring (TCAP(t_scroll_forward));
std::fflush(stdout);
return true;
}
return false;
}
//----------------------------------------------------------------------
bool FTerm::scrollTermReverse()
{
if ( TCAP(t_scroll_reverse) )
{
putstring (TCAP(t_scroll_reverse));
std::fflush(stdout);
return true;
}
return false;
}
//----------------------------------------------------------------------
FTerm::defaultPutChar& FTerm::putchar()
{
static const auto& fputchar = make_unique(&FTerm::putchar_ASCII);
return *fputchar.get();
}
//----------------------------------------------------------------------
void FTerm::putstring (const std::string& str, int affcnt)
{
auto status = FTermcap::paddingPrint (str, affcnt, FTerm::putchar_ASCII);
if ( status == FTermcap::Status::Error )
{
// Possible error handling
}
}
//----------------------------------------------------------------------
int FTerm::putchar_ASCII (int c)
{
const auto& fsys = FTerm::getFSystem();
if ( fsys->putchar(char(c)) == EOF )
return 0;
else
return 1;
}
//----------------------------------------------------------------------
int FTerm::putchar_UTF8 (int c)
{
const auto& fsys = FTerm::getFSystem();
if ( c < 0x80 )
{
// 1 Byte (7-bit): 0xxxxxxx
fsys->putchar (c);
return 1;
}
else if ( c < 0x800 )
{
// 2 byte (11-bit): 110xxxxx 10xxxxxx
fsys->putchar (0xc0 | (c >> 6) );
fsys->putchar (0x80 | (c & 0x3f) );
return 2;
}
else if ( c < 0x10000 )
{
// 3 byte (16-bit): 1110xxxx 10xxxxxx 10xxxxxx
fsys->putchar (0xe0 | (c >> 12) );
fsys->putchar (0x80 | ((c >> 6) & 0x3f) );
fsys->putchar (0x80 | (c & 0x3f) );
return 3;
}
else if ( c < 0x200000 )
{
// 4 byte (21-bit): 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
fsys->putchar (0xf0 | (c >> 18) );
fsys->putchar (0x80 | ((c >> 12) & 0x3f) );
fsys->putchar (0x80 | ((c >> 6) & 0x3f) );
fsys->putchar (0x80 | (c & 0x3f));
return 4;
}
else
return EOF;
}
// protected methods of FTerm
//----------------------------------------------------------------------
void FTerm::initScreenSettings()
{
#if defined(__linux__)
// Important: Do not use setNewFont() or setVGAFont() after
// the console character mapping has been initialized
FTerm::getFTermLinux().initCharMap();
#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(UNIT_TEST)
FTerm::getFTermFreeBSD().initCharMap();
#endif
// set xterm underline cursor
getFTermXTerminal().setCursorStyle (XTermCursorStyle::BlinkingUnderline);
// set xterm color settings to defaults
getFTermXTerminal().setDefaults();
}
//----------------------------------------------------------------------
std::string FTerm::changeAttribute (FChar& term_attr, FChar& next_attr)
{
auto& opti_attr = FTerm::getFOptiAttr();
return opti_attr.changeAttribute (term_attr, next_attr);
}
//----------------------------------------------------------------------
void FTerm::changeTermSizeFinished()
{
FTerm::getFTermData().setTermResized(false);
}
// private methods of FTerm
//----------------------------------------------------------------------
inline FStartOptions& FTerm::getStartOptions()
{
return FStartOptions::getFStartOptions();
}
//----------------------------------------------------------------------
void FTerm::init_global_values()
{
// Initialize global values
FTerm::getFTermData().setNewFont(false); // Preset to false
if ( ! getStartOptions().terminal_detection )
{
FTerm::getFTermDetection().setTerminalDetection (false);
}
}
//----------------------------------------------------------------------
void FTerm::init_terminal_device_path()
{
std::array termfilename{};
const int stdout_no = FTermios::getStdOut();
if ( ttyname_r(stdout_no, termfilename.data(), termfilename.size()) )
termfilename[0] = '\0';
FTerm::getFTermData().setTermFileName(termfilename.data());
}
//----------------------------------------------------------------------
void FTerm::oscPrefix()
{
if ( isTmuxTerm() )
{
// tmux device control string
putstring (ESC "Ptmux;" ESC);
}
else if ( isScreenTerm() )
{
// GNU Screen device control string
putstring (ESC "P");
}
}
//----------------------------------------------------------------------
void FTerm::oscPostfix()
{
if ( isScreenTerm() || isTmuxTerm() )
{
// GNU Screen/tmux string terminator
putstring (ESC "\\");
}
}
//----------------------------------------------------------------------
void FTerm::init_alt_charset()
{
// Read the used VT100 pairs
std::unordered_map vt100_alt_char;
if ( TCAP(t_acs_chars) )
{
for (std::size_t n{0}; TCAP(t_acs_chars)[n]; n += 2)
{
// insert the VT100 key/value pairs into a map
const auto p1 = uChar(TCAP(t_acs_chars)[n]);
const auto p2 = uChar(TCAP(t_acs_chars)[n + 1]);
vt100_alt_char[p1] = p2;
}
}
// Update array 'character' with discovered VT100 pairs
for (auto&& pair : fc::dec_special_graphics)
{
const auto keyChar = uChar(pair.key);
const auto altChar = wchar_t(vt100_alt_char[keyChar]);
const auto utf8char = wchar_t(pair.unicode);
const auto p = std::find_if ( fc::character.begin()
, fc::character.end()
, [&utf8char] (fc::CharEncodeMap entry)
{ return entry.unicode == utf8char; } );
if ( p != fc::character.end() ) // found in character
{
const auto item = std::size_t(std::distance(fc::character.begin(), p));
if ( altChar ) // update alternate character set
getCharacter(fc::character[item], Encoding::VT100) = altChar;
else // delete VT100 char in character
getCharacter(fc::character[item], Encoding::VT100) = L'\0';
}
}
}
//----------------------------------------------------------------------
void FTerm::init_pc_charset()
{
bool reinit{false};
auto& opti_attr = FTerm::getFOptiAttr();
// rxvt does not support pc charset
if ( isRxvtTerminal() || isUrxvtTerminal() )
return;
if ( isGnomeTerminal() || isLinuxTerm() )
{
// Fallback if tcap "S2" is not found
if ( ! TCAP(t_enter_pc_charset_mode) )
{
if ( FTerm::getFTermData().hasUTF8Console() )
{
// Select iso8859-1 + null mapping
TCAP(t_enter_pc_charset_mode) = ESC "%@" ESC "(U";
}
else
{
// Select null mapping
TCAP(t_enter_pc_charset_mode) = ESC "(U";
}
opti_attr.set_enter_pc_charset_mode \
(TCAP(t_enter_pc_charset_mode));
reinit = true;
}
// Fallback if tcap "S3" is not found
if ( ! TCAP(t_exit_pc_charset_mode) )
{
if ( FTerm::getFTermData().hasUTF8Console() )
{
// Select ascii mapping + utf8
TCAP(t_exit_pc_charset_mode) = ESC "(B" ESC "%G";
}
else
{
// Select ascii mapping
TCAP(t_enter_pc_charset_mode) = ESC "(B";
}
opti_attr.set_exit_pc_charset_mode \
(TCAP(t_exit_pc_charset_mode));
reinit = true;
}
}
if ( reinit )
opti_attr.initialize();
}
//----------------------------------------------------------------------
void FTerm::init_cygwin_charmap()
{
// Replace don't printable PC charset characters in a Cygwin terminal
if ( ! isCygwinTerminal() )
return;
// PC encoding changes
for (auto&& entry : fc::character)
{
if ( entry.unicode == UniChar::BlackUpPointingTriangle ) // ▲
entry.pc = 0x18;
if ( entry.unicode == UniChar::BlackDownPointingTriangle ) // ▼
entry.pc = 0x19;
if ( entry.unicode == UniChar::InverseBullet // ◘
|| entry.unicode == UniChar::InverseWhiteCircle // ◙
|| entry.unicode == UniChar::UpDownArrow // ↕
|| entry.unicode == UniChar::LeftRightArrow // ↔
|| entry.unicode == UniChar::DoubleExclamationMark // ‼
|| entry.unicode == UniChar::BlackRectangle // ▬
|| entry.unicode == UniChar::RightwardsArrow // →
|| entry.unicode == UniChar::Section // §
|| entry.unicode == UniChar::SquareRoot ) // SquareRoot √
entry.pc = entry.ascii;
}
// General encoding changes
auto& data = FTerm::getFTermData();
charSubstitution& sub_map = data.getCharSubstitutionMap();
sub_map[L'•'] = L'*';
sub_map[L'●'] = L'*';
sub_map[L'◘'] = L'*';
sub_map[L'○'] = L'*';
sub_map[L'◙'] = L'*';
sub_map[L'♪'] = L'♫';
sub_map[L'√'] = L'x';
sub_map[L'ˣ'] = L'`';
}
//----------------------------------------------------------------------
void FTerm::init_fixed_max_color()
{
// Initialize maximum number of colors for known terminals
if ( isCygwinTerminal()
|| isPuttyTerminal()
|| isTeraTerm()
|| isRxvtTerminal() )
{
FTermcap::max_color = 16;
}
}
//----------------------------------------------------------------------
void FTerm::init_teraterm_charmap()
{
// Tera Term can't print ascii characters < 0x20
if ( ! isTeraTerm() )
return;
for (auto&& entry : fc::character)
if ( entry.pc < 0x20 )
entry.pc = entry.ascii;
}
//----------------------------------------------------------------------
void FTerm::init_termcap()
{
// Initialize the terminal capabilities
FTermcap::init();
}
//----------------------------------------------------------------------
void FTerm::init_quirks()
{
// Initialize terminal quirks
FTermcapQuirks::terminalFixup(); // Fix terminal quirks
}
//----------------------------------------------------------------------
void FTerm::init_optiMove()
{
// Duration precalculation of the cursor movement strings
FOptiMove::TermEnv optimove_env =
{
TCAP(t_cursor_home),
TCAP(t_carriage_return),
TCAP(t_cursor_to_ll),
TCAP(t_tab),
TCAP(t_back_tab),
TCAP(t_cursor_up),
TCAP(t_cursor_down),
TCAP(t_cursor_left),
TCAP(t_cursor_right),
TCAP(t_cursor_address),
TCAP(t_column_address),
TCAP(t_row_address),
TCAP(t_parm_up_cursor),
TCAP(t_parm_down_cursor),
TCAP(t_parm_left_cursor),
TCAP(t_parm_right_cursor),
TCAP(t_erase_chars),
TCAP(t_repeat_char),
TCAP(t_clr_bol),
TCAP(t_clr_eol),
FTermcap::tabstop,
FTermcap::automatic_left_margin,
FTermcap::eat_nl_glitch
};
auto& opti_move = FTerm::getFOptiMove();
opti_move.setTermEnvironment(optimove_env);
}
//----------------------------------------------------------------------
void FTerm::init_optiAttr()
{
// Setting video attribute optimization
FOptiAttr::TermEnv optiattr_env =
{
TCAP(t_enter_bold_mode),
TCAP(t_exit_bold_mode),
TCAP(t_enter_dim_mode),
TCAP(t_exit_dim_mode),
TCAP(t_enter_italics_mode),
TCAP(t_exit_italics_mode),
TCAP(t_enter_underline_mode),
TCAP(t_exit_underline_mode),
TCAP(t_enter_blink_mode),
TCAP(t_exit_blink_mode),
TCAP(t_enter_reverse_mode),
TCAP(t_exit_reverse_mode),
TCAP(t_enter_standout_mode),
TCAP(t_exit_standout_mode),
TCAP(t_enter_secure_mode),
TCAP(t_exit_secure_mode),
TCAP(t_enter_protected_mode),
TCAP(t_exit_protected_mode),
TCAP(t_enter_crossed_out_mode),
TCAP(t_exit_crossed_out_mode),
TCAP(t_enter_dbl_underline_mode),
TCAP(t_exit_dbl_underline_mode),
TCAP(t_set_attributes),
TCAP(t_exit_attribute_mode),
TCAP(t_enter_alt_charset_mode),
TCAP(t_exit_alt_charset_mode),
TCAP(t_enter_pc_charset_mode),
TCAP(t_exit_pc_charset_mode),
TCAP(t_set_a_foreground),
TCAP(t_set_a_background),
TCAP(t_set_foreground),
TCAP(t_set_background),
TCAP(t_orig_pair),
TCAP(t_orig_pair),
TCAP(t_orig_colors),
FTermcap::max_color,
FTermcap::attr_without_color,
FTermcap::ansi_default_color
};
auto& opti_attr = FTerm::getFOptiAttr();
opti_attr.setTermEnvironment(optiattr_env);
}
//----------------------------------------------------------------------
bool FTerm::init_font()
{
auto& data = FTerm::getFTermData();
if ( getStartOptions().vgafont && ! setVGAFont() )
{
data.setExitMessage("VGAfont is not supported by this terminal");
FApplication::exit(EXIT_FAILURE);
}
if ( getStartOptions().newfont && ! setNewFont() )
{
data.setExitMessage("Newfont is not supported by this terminal");
FApplication::exit(EXIT_FAILURE);
}
return ! FApplication::isQuit();
}
//----------------------------------------------------------------------
void FTerm::init_locale()
{
// Init current locale
const auto& termtype = FTerm::getFTermData().getTermType();
const char* locale_name = std::setlocale (LC_ALL, "");
std::setlocale (LC_NUMERIC, "");
// Get XTERM_LOCALE
const char* locale_xterm = std::getenv("XTERM_LOCALE");
// set LC_ALL to XTERM_LOCALE
if ( locale_xterm )
locale_name = std::setlocale (LC_ALL, locale_xterm);
// TeraTerm can not show UTF-8 character
if ( isTeraTerm() && ! std::strcmp(nl_langinfo(CODESET), "UTF-8") )
locale_name = std::setlocale (LC_ALL, "C");
// Kterm
if ( isKtermTerminal() && ! std::strcmp(nl_langinfo(CODESET), "UTF-8") )
locale_name = std::setlocale (LC_ALL, "C");
// Sun (color) workstation console can't show UTF-8 character
if ( termtype.substr(0,3) == "sun"
&& ! std::strcmp(nl_langinfo(CODESET), "UTF-8") )
locale_name = std::setlocale (LC_ALL, "C");
// Try to found a meaningful content for locale_name
if ( locale_name )
locale_name = std::setlocale (LC_CTYPE, nullptr);
else
{
locale_name = std::getenv("LC_ALL");
if ( ! locale_name )
{
locale_name = std::getenv("LC_CTYPE");
if ( ! locale_name )
locale_name = std::getenv("LANG");
}
}
// Fallback to C
if ( ! locale_name )
std::setlocale (LC_ALL, "C");
}
//----------------------------------------------------------------------
void FTerm::init_encoding()
{
// detect encoding and set the putchar() function pointer
bool force_vt100{false}; // VT100 line drawing (G1 character set)
init_encoding_set();
if ( isRxvtTerminal() && ! isUrxvtTerminal() )
force_vt100 = true; // This rxvt terminal does not support utf-8
init_term_encoding();
init_pc_charset();
init_individual_term_encoding();
if ( force_vt100 )
init_force_vt100_encoding();
else
init_utf8_without_alt_charset();
init_tab_quirks();
if ( getStartOptions().encoding != Encoding::Unknown )
{
setEncoding(getStartOptions().encoding);
}
}
//----------------------------------------------------------------------
inline void FTerm::init_encoding_set()
{
// Define the encoding set
auto& data = FTerm::getFTermData();
auto& encoding_list = data.getEncodingList();
encoding_list["UTF8"] = Encoding::UTF8;
encoding_list["UTF-8"] = Encoding::UTF8;
encoding_list["VT100"] = Encoding::VT100; // VT100 line drawing
encoding_list["PC"] = Encoding::PC; // CP-437
encoding_list["ASCII"] = Encoding::ASCII;
}
//----------------------------------------------------------------------
void FTerm::init_term_encoding()
{
const int stdout_no = FTermios::getStdOut();
auto& data = FTerm::getFTermData();
const auto& termtype = data.getTermType();
const auto& fsys = FTerm::getFSystem();
if ( fsys->isTTY(stdout_no)
&& ! std::strcmp(nl_langinfo(CODESET), "UTF-8") )
{
data.setUTF8Console(true);
data.setTermEncoding (Encoding::UTF8);
putchar() = &FTerm::putchar_UTF8; // function pointer
data.setUTF8(true);
setUTF8(true);
auto& keyboard = FTerm::getFKeyboard();
keyboard.enableUTF8();
}
else if ( fsys->isTTY(stdout_no)
&& (termtype.length() > 0)
&& (TCAP(t_exit_alt_charset_mode) != nullptr) )
{
data.setVT100Console (true);
data.setTermEncoding (Encoding::VT100);
putchar() = &FTerm::putchar_ASCII; // function pointer
}
else
{
data.setASCIIConsole (true);
data.setTermEncoding (Encoding::ASCII);
putchar() = &FTerm::putchar_ASCII; // function pointer
}
}
//----------------------------------------------------------------------
void FTerm::init_individual_term_encoding()
{
auto& data = FTerm::getFTermData();
if ( isNewFont()
|| (isPuttyTerminal() && ! data.isUTF8())
|| (isTeraTerm() && ! data.isUTF8()) )
{
data.setTermEncoding (Encoding::PC);
putchar() = &FTerm::putchar_ASCII; // function pointer
if ( hasUTF8()
&& getStartOptions().encoding == Encoding::Unknown
&& isXTerminal() )
putchar() = &FTerm::putchar_UTF8; // function pointer
}
}
//----------------------------------------------------------------------
void FTerm::init_force_vt100_encoding()
{
auto& data = FTerm::getFTermData();
data.setVT100Console(true);
data.setTermEncoding (Encoding::VT100);
putchar() = &FTerm::putchar_ASCII; // function pointer
}
//----------------------------------------------------------------------
void FTerm::init_utf8_without_alt_charset()
{
// Fall back to ascii for utf-8 terminals that
// do not support VT100 line drawings
auto& data = FTerm::getFTermData();
if ( FTermcap::no_utf8_acs_chars && data.isUTF8()
&& data.getTermEncoding() == Encoding::VT100 )
{
data.setASCIIConsole(true);
data.setTermEncoding (Encoding::ASCII);
putchar() = &FTerm::putchar_ASCII; // function pointer
}
}
//----------------------------------------------------------------------
void FTerm::init_tab_quirks()
{
// In some alternative character sets, a tab character prints a '○'
// on the terminal and does not move the cursor to the next tab stop
// position
const auto& enc = FTerm::getFTermData().getTermEncoding();
if ( enc == Encoding::VT100 || enc == Encoding::PC )
{
const char* empty{nullptr};
auto& opti_move = FTerm::getFOptiMove();
opti_move.set_tabular (empty);
}
}
//----------------------------------------------------------------------
void FTerm::init_captureFontAndTitle()
{
// Save the used xterm font and window title
if ( ! FStartOptions::getFStartOptions().terminal_data_request )
return;
getFTermXTerminal().captureFontAndTitle();
const auto& font = getFTermXTerminal().getFont();
const auto& title = getFTermXTerminal().getTitle();
auto& data = FTerm::getFTermData();
if ( ! font.isEmpty() )
data.setXtermFont(font);
if ( ! title.isEmpty() )
data.setXtermTitle(title);
}
//----------------------------------------------------------------------
inline bool FTerm::hasNoFontSettingOption()
{
if ( isGnomeTerminal()
|| isKdeTerminal()
|| isPuttyTerminal()
|| isTeraTerm()
|| isCygwinTerminal()
|| isMinttyTerm() )
return true;
return false;
}
//----------------------------------------------------------------------
inline bool FTerm::isDefaultPaletteTheme()
{
FStringList default_themes
{
"default8ColorPalette",
"default16ColorPalette",
"default16DarkColorPalette"
};
auto iter = std::find ( default_themes.begin()
, default_themes.end()
, getColorPaletteTheme()->getClassName() );
if ( iter == default_themes.end() ) // No default theme
return false;
return true;
}
//----------------------------------------------------------------------
void FTerm::redefineColorPalette()
{
// Redefine the color palette
if ( ! (canChangeColorPalette() && getStartOptions().color_change) )
return;
resetColorMap();
saveColorMap();
if ( getColorPaletteTheme().use_count() > 0 && ! isDefaultPaletteTheme() )
{
// A user color palette theme is in use
getColorPaletteTheme()->setColorPalette();
return;
}
if ( getStartOptions().dark_theme )
{
setColorPaletteTheme();
}
else
{
if ( getMaxColor() >= 16 )
setColorPaletteTheme();
else // 8 colors
setColorPaletteTheme();
}
}
//----------------------------------------------------------------------
void FTerm::restoreColorPalette()
{
if ( ! (canChangeColorPalette() && getStartOptions().color_change) )
return;
// Reset screen settings
getColorPaletteTheme()->resetColorPalette();
getFTermXTerminal().resetColorMap();
resetColorMap();
}
//----------------------------------------------------------------------
void FTerm::setInsertCursorStyle()
{
getFTermXTerminal().setCursorStyle (XTermCursorStyle::BlinkingUnderline);
setKDECursor(KdeKonsoleCursorShape::Underline);
#if defined(__linux__)
auto& linux_console = FTerm::getFTermLinux();
linux_console.setCursorStyle (LinuxConsoleCursorStyle::Underscore);
#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(UNIT_TEST)
auto& freebsd_console = FTerm::getFTermFreeBSD();
freebsd_console.setCursorStyle (FreeBSDConsoleCursorStyle::Destructive);
#endif
if ( isUrxvtTerminal() )
getFTermXTerminal().setCursorColor ("rgb:ffff/ffff/ffff");
}
//----------------------------------------------------------------------
void FTerm::setOverwriteCursorStyle()
{
getFTermXTerminal().setCursorStyle (XTermCursorStyle::SteadyBlock);
setKDECursor(KdeKonsoleCursorShape::Block);
#if defined(__linux__)
auto& linux_console = FTerm::getFTermLinux();
linux_console.setCursorStyle (LinuxConsoleCursorStyle::FullBlock);
#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(UNIT_TEST)
auto& freebsd_console = FTerm::getFTermFreeBSD();
freebsd_console.setCursorStyle (FreeBSDConsoleCursorStyle::Normal);
#endif
if ( isUrxvtTerminal() )
getFTermXTerminal().setCursorColor ("rgb:eeee/0000/0000");
}
//----------------------------------------------------------------------
std::string FTerm::enableCursorString()
{
// Returns the cursor enable string
static constexpr std::string::size_type SIZE{32u};
std::string enable_str{};
enable_str.reserve(SIZE);
const auto& vs = TCAP(t_cursor_visible);
const auto& ve = TCAP(t_cursor_normal);
if ( ve )
enable_str = ve;
else if ( vs )
enable_str = vs;
#if defined(__linux__)
if ( isLinuxTerm() )
{
// Restore the last used Linux console cursor style
auto& linux_console = FTerm::getFTermLinux();
const char* cstyle = linux_console.getCursorStyleString();
enable_str.append(cstyle);
}
#endif // defined(__linux__)
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(UNIT_TEST)
if ( isFreeBSDTerm() )
{
// Restore the last used FreeBSD console cursor style
auto& freebsd_console = FTerm::getFTermFreeBSD();
freebsd_console.setCursorStyle (freebsd_console.getCursorStyle());
}
#endif // defined(__FreeBSD__) || defined(__DragonFly__) || defined(UNIT_TEST)
return enable_str;
}
//----------------------------------------------------------------------
std::string FTerm::disableCursorString()
{
// Returns the cursor disable string
const auto& vi = TCAP(t_cursor_invisible);
if ( vi )
return vi;
return {};
}
//----------------------------------------------------------------------
void FTerm::enableMouse()
{
// Enable the terminal mouse support
if ( ! getStartOptions().mouse_support )
return;
bool gpm_mouse{false};
bool xterm_mouse{false};
#if defined(__linux__)
if ( isLinuxTerm() && openConsole() == 0 )
{
if ( FTermLinux::isLinuxConsole() )
gpm_mouse = true;
closeConsole();
}
#endif // defined(__linux__)
if ( TCAP(t_key_mouse) && ! isLinuxTerm() )
xterm_mouse = true;
auto& keyboard = FTerm::getFKeyboard();
keyboard.enableMouseSequences();
auto& mouse = FTerm::getFMouseControl();
mouse.setMaxWidth (uInt16(getColumnNumber()));
mouse.setMaxHeight (uInt16(getLineNumber()));
// Enable the linux general purpose mouse (gpm) server
mouse.useGpmMouse (gpm_mouse);
// Enable xterm mouse support
mouse.useXtermMouse (xterm_mouse);
mouse.enable();
}
//----------------------------------------------------------------------
inline void FTerm::disableMouse()
{
// Disable the terminal mouse support
FTerm::getFKeyboard().disableMouseSequences();
FTerm::getFMouseControl().disable();
}
//----------------------------------------------------------------------
inline void FTerm::enableKeypad()
{
// Enter 'keyboard_transmit' mode
if ( TCAP(t_keypad_xmit) )
{
putstring (TCAP(t_keypad_xmit));
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
inline void FTerm::disableKeypad()
{
// Leave 'keyboard_transmit' mode
if ( TCAP(t_keypad_local) )
{
putstring (TCAP(t_keypad_local));
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
inline void FTerm::enableAlternateCharset()
{
// Enable alternate charset
if ( TCAP(t_enable_acs) )
{
putstring (TCAP(t_enable_acs));
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
inline void FTerm::enableApplicationEscKey()
{
// switch to application escape key mode
if ( isMinttyTerm() )
FTerm::putstring (CSI "?7727h");
}
//----------------------------------------------------------------------
inline void FTerm::disableApplicationEscKey()
{
// Switch to normal escape key mode
if ( isMinttyTerm() )
putstring (CSI "?7727l");
}
//----------------------------------------------------------------------
void FTerm::useAlternateScreenBuffer()
{
// Switch to the alternate screen
if ( ! hasAlternateScreen() )
return;
// Save current cursor position
if ( TCAP(t_save_cursor) )
{
putstring (TCAP(t_save_cursor));
std::fflush(stdout);
}
// Saves the screen and the cursor position
if ( TCAP(t_enter_ca_mode) )
{
putstring (TCAP(t_enter_ca_mode));
std::fflush(stdout);
FTerm::getFTermData().setAlternateScreenInUse(true);
}
}
//----------------------------------------------------------------------
void FTerm::useNormalScreenBuffer()
{
// Switch to the normal screen
if ( ! hasAlternateScreen() )
return;
// restores the screen and the cursor position
if ( TCAP(t_exit_ca_mode) )
{
putstring (TCAP(t_exit_ca_mode));
std::fflush(stdout);
FTerm::getFTermData().setAlternateScreenInUse(false);
}
// restore cursor to position of last save_cursor
if ( TCAP(t_restore_cursor) )
{
putstring (TCAP(t_restore_cursor));
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
void FTerm::init()
{
internal::var::init_term_object = this;
// Initialize global values for all objects
init_global_values();
// Initialize the terminal
if ( ! init_terminal() )
return;
// Set maximum number of colors for detected terminals
init_fixed_max_color();
// Initializes variables for the current terminal
init_termcap();
// Initialize terminal quirks
init_quirks();
// Initialize cursor movement optimization
init_optiMove();
// Initialize video attributes optimization
init_optiAttr();
// Initialize vt100 alternate character set
init_alt_charset();
// Pass the terminal capabilities to the keyboard object
FTerm::getFKeyboard().setTermcapMap();
// Initializes locale information
init_locale();
// Detect environment and set encoding
init_encoding();
// Enable the terminal mouse support
enableMouse();
// Activate meta key sends escape
if ( isXTerminal() )
getFTermXTerminal().metaSendsESC(true);
// switch to application escape key mode
enableApplicationEscKey();
// Enter 'keyboard_transmit' mode
enableKeypad();
// Switch to the alternate screen
useAlternateScreenBuffer();
// Enable alternate charset
enableAlternateCharset();
// Save the used xterm font and window title
init_captureFontAndTitle();
// KDE terminal cursor and cygwin + teraterm charmap correction
initTermspecifics();
// Redefine the color palette
redefineColorPalette();
// Set 220 Hz beep (100 ms)
setBeep(220, 100);
// Set FTerm signal handler
setSignalHandler();
if ( ! getStartOptions().cursor_optimisation )
{
FTerm::getFTermData().supportCursorOptimisation(false);
}
// Activate the VGA or the new graphic font
// (depending on the initialization values)
if ( ! init_font() )
return;
// Turn off hardware echo
FTermios::unsetHardwareEcho();
// Switch to the raw mode
FTermios::setRawMode();
// The terminal is now initialized
internal::var::term_initialized = true;
}
//----------------------------------------------------------------------
bool FTerm::init_terminal() const
{
// Initialize termios
FTermios::init();
auto& data = FTerm::getFTermData();
const auto& fsys = FTerm::getFSystem();
// Check if stdin is a tty
if ( ! fsys->isTTY(FTermios::getStdIn()) )
{
data.setExitMessage("FTerm: Standard input is not a TTY.");
FApplication::exit(EXIT_FAILURE);
return false;
}
// Get pathname of the terminal device
init_terminal_device_path();
// Initialize Linux or *BSD console
initOSspecifics();
// Save termios settings
try
{
FTermios::storeTTYsettings();
}
catch (const std::system_error& ex)
{
FString msg = "FTerm: " + FString{ex.what()};
data.setExitMessage(msg);
FApplication::exit(EXIT_FAILURE);
return false;
}
// Get output baud rate
initBaudRate();
// Terminal detection
FTermDetection::detect();
const auto& term_detection = FTerm::getFTermDetection();
setTermType (term_detection.getTermType());
return true;
}
//----------------------------------------------------------------------
void FTerm::initOSspecifics() const
{
#if defined(__linux__)
auto& linux_console = FTerm::getFTermLinux();
linux_console.init(); // Initialize Linux console
#if DEBUG
auto& data = FTerm::getFTermData();
data.setFramebufferBpp (linux_console.getFramebufferBpp());
#endif
#endif // defined(__linux__)
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(UNIT_TEST)
auto& freebsd_console = FTerm::getFTermFreeBSD();
if ( getStartOptions().meta_sends_escape )
freebsd_console.enableMetaSendsEscape();
else
freebsd_console.disableMetaSendsEscape();
if ( getStartOptions().change_cursorstyle )
freebsd_console.enableChangeCursorStyle();
else
freebsd_console.disableChangeCursorStyle();
freebsd_console.init(); // Initialize BSD console
#elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(UNIT_TEST)
auto& openbsd_console = FTerm::getFTermOpenBSD();
if ( getStartOptions().meta_sends_escape )
openbsd_console.enableMetaSendsEscape();
else
openbsd_console.disableMetaSendsEscape();
openbsd_console.init(); // Initialize wscons console
#endif
}
//----------------------------------------------------------------------
void FTerm::initTermspecifics() const
{
if ( isKdeTerminal() )
setKDECursor(KdeKonsoleCursorShape::Underline);
if ( isCygwinTerminal() )
init_cygwin_charmap();
if ( isTeraTerm() )
init_teraterm_charmap();
}
//----------------------------------------------------------------------
void FTerm::initBaudRate() const
{
const int stdout_no = FTermios::getStdOut();
const uInt baud = FTermios::getBaudRate();
FTerm::getFTermData().setBaudrate(baud);
const auto& fsys = FTerm::getFSystem();
if ( fsys->isTTY(stdout_no) )
{
auto& opti_move = FTerm::getFOptiMove();
opti_move.setBaudRate(int(baud));
}
}
//----------------------------------------------------------------------
void FTerm::finish() const
{
// Set default signal handler
resetSignalHandler();
if ( isXTerminal() && ! isRxvtTerminal() )
getFTermXTerminal().resetTitle();
// Restore the saved termios settings
FTermios::restoreTTYsettings();
// Turn off all attributes
if ( TCAP(t_exit_attribute_mode) )
{
putstring (TCAP(t_exit_attribute_mode));
std::fflush(stdout);
}
// Turn off pc charset mode
if ( TCAP(t_exit_pc_charset_mode) )
{
putstring (TCAP(t_exit_pc_charset_mode));
std::fflush(stdout);
}
// Reset xterm color settings to default values
getFTermXTerminal().resetDefaults();
// Set xterm full block cursor
getFTermXTerminal().setCursorStyle (XTermCursorStyle::SteadyBlock);
// Restore the color palette
restoreColorPalette();
// Switch to normal escape key mode
disableApplicationEscKey();
finishOSspecifics();
if ( isKdeTerminal() )
setKDECursor(KdeKonsoleCursorShape::Block);
resetBeep();
// Disable the terminal mouse support
if ( getStartOptions().mouse_support )
disableMouse();
// Deactivate meta key sends escape
if ( isXTerminal() )
getFTermXTerminal().metaSendsESC(false);
// Switch to the normal screen
useNormalScreenBuffer();
// leave 'keyboard_transmit' mode
disableKeypad();
finish_encoding();
const auto& data = FTerm::getFTermData();
if ( data.isNewFont() || data.isVGAFont() )
resetFont();
}
//----------------------------------------------------------------------
void FTerm::finishOSspecifics() const
{
#if defined(__linux__)
FTerm::getFTermLinux().finish();
#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(UNIT_TEST)
FTerm::getFTermFreeBSD().finish();
#elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(UNIT_TEST)
FTerm::getFTermOpenBSD().finish();
#endif
}
//----------------------------------------------------------------------
void FTerm::finish_encoding() const
{
#if defined(__linux__)
if ( isLinuxTerm() && FTerm::getFTermData().hasUTF8Console() )
setUTF8(true);
#endif
}
//----------------------------------------------------------------------
void FTerm::printExitMessage()
{
// Print exit message
const auto& exit_message = FTerm::getFTermData().getExitMessage();
if ( ! exit_message.isEmpty() )
std::cerr << "Exit: " << exit_message << std::endl;
}
//----------------------------------------------------------------------
void FTerm::terminalSizeChange()
{
// Initialize a resize event to the root element
FTerm::getFTermData().setTermResized(true);
}
//----------------------------------------------------------------------
void FTerm::processTermination (int signum)
{
if ( internal::var::init_term_object )
internal::var::init_term_object->finish();
std::fflush (stderr);
std::fflush (stdout);
FStringStream msg{};
msg << "Program stopped: signal " << signum
<< " (" << strsignal(signum) << ")";
FTerm::getFTermData().setExitMessage(msg.str());
printExitMessage();
std::terminate();
}
//----------------------------------------------------------------------
void FTerm::setSignalHandler()
{
signal(SIGTERM, FTerm::signal_handler); // Termination signal
signal(SIGQUIT, FTerm::signal_handler); // Quit from keyboard (Ctrl-\)
signal(SIGINT, FTerm::signal_handler); // Keyboard interrupt (Ctrl-C)
signal(SIGABRT, FTerm::signal_handler); // Abort signal from abort(3)
signal(SIGILL, FTerm::signal_handler); // Illegal Instruction
signal(SIGSEGV, FTerm::signal_handler); // Invalid memory reference
signal(SIGWINCH, FTerm::signal_handler); // Window resize signal
}
//----------------------------------------------------------------------
void FTerm::resetSignalHandler()
{
signal(SIGWINCH, SIG_DFL); // Window resize signal
signal(SIGSEGV, SIG_DFL); // Invalid memory reference
signal(SIGILL, SIG_DFL); // Illegal Instruction
signal(SIGABRT, SIG_DFL); // Abort signal from abort(3)
signal(SIGINT, SIG_DFL); // Keyboard interrupt (Ctrl-C)
signal(SIGQUIT, SIG_DFL); // Quit from keyboard (Ctrl-\)
signal(SIGTERM, SIG_DFL); // Termination signal
}
//----------------------------------------------------------------------
void FTerm::signal_handler (int signum)
{
switch (signum)
{
case SIGWINCH:
terminalSizeChange();
break;
case SIGTERM:
case SIGQUIT:
case SIGINT:
case SIGABRT:
case SIGILL:
case SIGSEGV:
processTermination(signum);
default:
break;
}
}
} // namespace finalcut