finalcut/src/ftermxterminal.cpp

898 lines
23 KiB
C++

/***********************************************************************
* ftermxterminal.cpp - Contains all xterm-specific terminal functions *
* *
* This file is part of the FINAL CUT widget toolkit *
* *
* Copyright 2018-2020 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 *
* <http://www.gnu.org/licenses/>. *
***********************************************************************/
#if defined(__CYGWIN__)
#include "final/fconfig.h" // includes _GNU_SOURCE for fd_set
#endif
#include "final/fapplication.h"
#include "final/fc.h"
#include "final/flog.h"
#include "final/fstring.h"
#include "final/fterm.h"
#include "final/ftermcap.h"
#include "final/ftermdetection.h"
#include "final/ftermfreebsd.h"
#include "final/ftermios.h"
#include "final/ftermxterminal.h"
#include "final/fsize.h"
#define initCheck(ret_value) \
if ( ! isInitialized() ) \
{ \
if ( ! FApplication::isQuit() ) \
warnNotInitialized(); \
\
return ret_value; \
}
namespace finalcut
{
// static class attributes
bool FTermXTerminal::mouse_support{false};
FSystem* FTermXTerminal::fsystem{nullptr};
//----------------------------------------------------------------------
// class FTermXTerminal
//----------------------------------------------------------------------
// constructors and destructor
//----------------------------------------------------------------------
FTermXTerminal::FTermXTerminal()
{
// Get FSystem object
fsystem = FTerm::getFSystem();
}
//----------------------------------------------------------------------
FTermXTerminal::~FTermXTerminal() // destructor
{ }
// public methods of FTermXTerminal
//----------------------------------------------------------------------
void FTermXTerminal::setCursorStyle (fc::xtermCursorStyle style)
{
// Set the xterm cursor style
cursor_style = style;
setXTermCursorStyle();
}
//----------------------------------------------------------------------
void FTermXTerminal::setFont (const FString& fontname)
{
// Change the XTerm font (needs the allowFontOps resource)
xterm_font = fontname;
setXTermFont();
}
//----------------------------------------------------------------------
void FTermXTerminal::setTitle (const FString& title)
{
// Set the xterm title
xterm_title = title;
setXTermTitle();
}
//----------------------------------------------------------------------
void FTermXTerminal::setTermSize (const FSize& size)
{
// Set xterm size to {term_width} x {term_height}
term_width = size.getWidth();
term_height = size.getHeight();
setXTermSize();
}
//----------------------------------------------------------------------
void FTermXTerminal::setForeground (const FString& fg)
{
// Set the XTerm text foreground color
foreground_color = fg;
setXTermForeground();
}
//----------------------------------------------------------------------
void FTermXTerminal::setBackground (const FString& bg)
{
// Set the XTerm text background color
background_color = bg;
setXTermBackground();
}
//----------------------------------------------------------------------
void FTermXTerminal::setCursorColor (const FString& cc)
{
// Set the text cursor color
cursor_color = cc;
setXTermCursorColor();
}
//----------------------------------------------------------------------
void FTermXTerminal::setMouseForeground (const FString& mfg)
{
// Set the mouse foreground color
mouse_foreground_color = mfg;
setXTermMouseForeground();
}
//----------------------------------------------------------------------
void FTermXTerminal::setMouseBackground (const FString& mbg)
{
// Set the mouse background color
mouse_background_color = mbg;
setXTermMouseBackground();
}
//----------------------------------------------------------------------
void FTermXTerminal::setHighlightBackground (const FString& hbg)
{
// Set the highlight background color
highlight_background_color = hbg;
setXTermHighlightBackground();
}
//----------------------------------------------------------------------
void FTermXTerminal::setMouseSupport (bool enable)
{
// activate/deactivate the xterm mouse support
if ( enable )
enableXTermMouse();
else
disableXTermMouse();
}
//----------------------------------------------------------------------
void FTermXTerminal::metaSendsESC (bool enable)
{
// activate/deactivate the xterm meta key sends escape prefix
if ( enable )
enableXTermMetaSendsESC();
else
disableXTermMetaSendsESC();
}
//----------------------------------------------------------------------
void FTermXTerminal::init()
{
term_detection = FTerm::getFTermDetection();
}
//----------------------------------------------------------------------
void FTermXTerminal::setDefaults()
{
// Redefinition of the XTerm default colors
// for the final cut color theme
if ( FTerm::getMaxColor() >= 16 )
setXTerm16ColorDefaults();
else
setXTerm8ColorDefaults();
}
//----------------------------------------------------------------------
void FTermXTerminal::resetColorMap() const
{
// Reset the entire color table
resetXTermColorMap();
}
//----------------------------------------------------------------------
void FTermXTerminal::resetForeground()
{
// Reset the XTerm text foreground color
foreground_color.clear();
resetXTermForeground();
}
//----------------------------------------------------------------------
void FTermXTerminal::resetBackground()
{
// Reset the XTerm text background color
background_color.clear();
resetXTermBackground();
}
//----------------------------------------------------------------------
void FTermXTerminal::resetCursorColor()
{
// Reset the text cursor color
cursor_color.clear();
resetXTermCursorColor();
}
//----------------------------------------------------------------------
void FTermXTerminal::resetMouseForeground()
{
// Reset the mouse foreground color
mouse_foreground_color.clear();
resetXTermMouseForeground();
}
//----------------------------------------------------------------------
void FTermXTerminal::resetMouseBackground()
{
// Reset the mouse background color
mouse_background_color.clear();
resetXTermMouseBackground();
}
//----------------------------------------------------------------------
void FTermXTerminal::resetHighlightBackground()
{
// Reset the highlight background color
highlight_background_color.clear();
resetXTermHighlightBackground();
}
//----------------------------------------------------------------------
void FTermXTerminal::resetDefaults()
{
initCheck();
if ( term_detection->isPuttyTerminal() )
return;
// Redefines the cursor color if resetCursorColor() doesn't work
setCursorColor("rgb:b1b1/b1b1/b1b1");
// Reset mouse color to default
resetMouseForeground();
resetMouseBackground();
// Reset text cursor color to default
resetCursorColor();
if ( xterm_default_colors )
{
// Reset text foreground, text background, and mouse text highlight
// colors to default values
resetForeground();
resetBackground();
resetHighlightBackground();
}
}
//----------------------------------------------------------------------
void FTermXTerminal::captureFontAndTitle()
{
initCheck();
if ( ( term_detection->isXTerminal()
|| term_detection->isUrxvtTerminal() )
&& ! term_detection->isRxvtTerminal() )
{
FTermios::setCaptureSendCharacters();
xterm_font = captureXTermFont();
xterm_title = captureXTermTitle();
FTermios::unsetCaptureSendCharacters();
}
}
// private methods of FTermXTerminal
//----------------------------------------------------------------------
void FTermXTerminal::warnNotInitialized() const
{
std::clog << FLog::Warn
<< "The FTermXTerminal object has "
<< "not yet been initialized! "
<< "Please call the init() method first."
<< std::endl;
}
//----------------------------------------------------------------------
void FTermXTerminal::setXTermCursorStyle()
{
// Set the xterm cursor style
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(UNIT_TEST)
if ( FTermFreeBSD::isFreeBSDConsole() )
return;
#endif
initCheck();
if ( term_detection->isGnomeTerminal()
&& ! term_detection->hasSetCursorStyleSupport() )
return;
if ( term_detection->isKdeTerminal() )
return;
if ( TCAP(fc::t_cursor_style)
|| term_detection->isXTerminal()
|| term_detection->isCygwinTerminal()
|| term_detection->isMinttyTerm()
|| term_detection->hasSetCursorStyleSupport() )
{
FTerm::putstringf (CSI "%d q", cursor_style);
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
void FTermXTerminal::setXTermTitle()
{
// Set the xterm title
initCheck();
if ( term_detection->isXTerminal()
|| term_detection->isScreenTerm()
|| term_detection->isCygwinTerminal()
|| term_detection->isMinttyTerm()
|| term_detection->isPuttyTerminal()
|| FTermcap::osc_support )
{
oscPrefix();
FTerm::putstringf (OSC "0;%s" BEL, xterm_title.c_str());
oscPostfix();
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
void FTermXTerminal::setXTermSize() const
{
initCheck();
if ( term_detection->isXTerminal() )
{
FTerm::putstringf ( CSI "8;%lu;%lut"
, uLong(term_height)
, uLong(term_width) );
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
void FTermXTerminal::setXTermFont()
{
// Change the XTerm font (needs the allowFontOps resource)
initCheck();
if ( term_detection->isXTerminal()
|| term_detection->isScreenTerm()
|| term_detection->isUrxvtTerminal()
|| FTermcap::osc_support )
{
oscPrefix();
FTerm::putstringf (OSC "50;%s" BEL, xterm_font.c_str() );
oscPostfix();
}
}
//----------------------------------------------------------------------
void FTermXTerminal::setXTermForeground()
{
// Set the XTerm text foreground color
initCheck();
if ( term_detection->isXTerminal()
|| term_detection->isScreenTerm()
|| term_detection->isMinttyTerm()
|| term_detection->isMltermTerminal()
|| FTermcap::osc_support )
{
oscPrefix();
FTerm::putstringf (OSC "10;%s" BEL, foreground_color.c_str());
oscPostfix();
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
void FTermXTerminal::setXTermBackground()
{
// Set the XTerm text background color
initCheck();
if ( term_detection->isXTerminal()
|| term_detection->isScreenTerm()
|| term_detection->isMinttyTerm()
|| term_detection->isMltermTerminal()
|| FTermcap::osc_support )
{
oscPrefix();
FTerm::putstringf (OSC "11;%s" BEL, background_color.c_str());
oscPostfix();
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
void FTermXTerminal::setXTermCursorColor()
{
// Set the text cursor color
initCheck();
if ( term_detection->isXTerminal()
|| term_detection->isScreenTerm()
|| term_detection->isMinttyTerm()
|| term_detection->isUrxvtTerminal()
|| FTermcap::osc_support )
{
oscPrefix();
FTerm::putstringf (OSC "12;%s" BEL, cursor_color.c_str());
oscPostfix();
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
void FTermXTerminal::setXTermMouseForeground()
{
// Set the mouse foreground color
initCheck();
if ( term_detection->isXTerminal()
|| term_detection->isScreenTerm()
|| term_detection->isUrxvtTerminal()
|| FTermcap::osc_support )
{
oscPrefix();
FTerm::putstringf (OSC "13;%s" BEL, mouse_foreground_color.c_str());
oscPostfix();
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
void FTermXTerminal::setXTermMouseBackground()
{
// Set the mouse background color
initCheck();
if ( term_detection->isXTerminal()
|| term_detection->isScreenTerm()
|| FTermcap::osc_support )
{
oscPrefix();
FTerm::putstringf (OSC "14;%s" BEL, mouse_background_color.c_str());
oscPostfix();
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
void FTermXTerminal::setXTermHighlightBackground()
{
// Set the highlight background color
initCheck();
if ( term_detection->isXTerminal()
|| term_detection->isScreenTerm()
|| term_detection->isUrxvtTerminal()
|| FTermcap::osc_support )
{
oscPrefix();
FTerm::putstringf (OSC "17;%s" BEL, highlight_background_color.c_str());
oscPostfix();
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
void FTermXTerminal::setXTerm8ColorDefaults()
{
// Redefinition of the XTerm default colors
// for the final cut 8 color theme
initCheck();
if ( term_detection->isPuttyTerminal() )
return;
setXTermDefaultsMouseCursor();
if ( canSetXTermBackground() )
{
// mintty and rxvt can't reset these settings
setBackground("rgb:2222/2222/b2b2"); // blue
setForeground("rgb:0000/0000/0000"); // black
setHighlightBackground("rgb:8787/8787/8787"); // gray53
}
}
//----------------------------------------------------------------------
void FTermXTerminal::setXTerm16ColorDefaults()
{
// Redefinition of the XTerm default colors
// for the final cut 16 color theme
initCheck();
if ( term_detection->isPuttyTerminal() )
return;
setXTermDefaultsMouseCursor();
if ( canSetXTermBackground() )
{
// mintty and rxvt can't reset these settings
setBackground("rgb:8080/a4a4/ecec"); // very light blue
setForeground("rgb:0000/0000/0000"); // black
setHighlightBackground("rgb:8787/8787/8787"); // gray53
}
}
//----------------------------------------------------------------------
inline void FTermXTerminal::setXTermDefaultsMouseCursor()
{
setMouseBackground("rgb:ffff/ffff/ffff"); // white
setMouseForeground ("rgb:0000/0000/0000"); // black
initCheck();
if ( ! term_detection->isGnomeTerminal() )
setCursorColor("rgb:ffff/ffff/ffff"); // white
}
//----------------------------------------------------------------------
inline bool FTermXTerminal::canSetXTermBackground() const
{
initCheck(false);
if ( xterm_default_colors
&& ! (term_detection->isMinttyTerm()
|| term_detection->isMltermTerminal()
|| term_detection->isRxvtTerminal()
|| term_detection->isScreenTerm()) )
return true;
else
return false;
}
//----------------------------------------------------------------------
void FTermXTerminal::resetXTermColorMap() const
{
// Reset the entire color table
initCheck();
if ( term_detection->isMinttyTerm() )
{
FTerm::putstring (ESC "c"); // Full Reset (RIS)
}
else if ( canResetColor() )
{
oscPrefix();
FTerm::putstring (OSC "104" BEL);
oscPostfix();
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
void FTermXTerminal::resetXTermForeground() const
{
// Reset the XTerm text foreground color
if ( canResetColor() )
{
oscPrefix();
FTerm::putstring (OSC "110" BEL);
oscPostfix();
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
void FTermXTerminal::resetXTermBackground() const
{
// Reset the XTerm text background color
if ( canResetColor() )
{
oscPrefix();
FTerm::putstring (OSC "111" BEL);
oscPostfix();
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
void FTermXTerminal::resetXTermCursorColor() const
{
// Reset the text cursor color
if ( canResetColor() )
{
oscPrefix();
FTerm::putstring (OSC "112" BEL);
oscPostfix();
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
void FTermXTerminal::resetXTermMouseForeground() const
{
// Reset the mouse foreground color
if ( canResetColor() )
{
oscPrefix();
FTerm::putstring (OSC "113" BEL);
oscPostfix();
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
void FTermXTerminal::resetXTermMouseBackground() const
{
// Reset the mouse background color
if ( canResetColor() )
{
oscPrefix();
FTerm::putstring (OSC "114" BEL);
oscPostfix();
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
void FTermXTerminal::resetXTermHighlightBackground() const
{
// Reset the highlight background color
if ( canResetColor() )
{
oscPrefix();
FTerm::putstring (OSC "117" BEL);
oscPostfix();
std::fflush(stdout);
}
}
//----------------------------------------------------------------------
bool FTermXTerminal::canResetColor() const
{
initCheck(false);
if ( term_detection->isGnomeTerminal()
&& term_detection->getGnomeTerminalID() < 3502 )
return false;
if ( term_detection->isPuttyTerminal()
|| term_detection->isMltermTerminal() )
return false;
if ( term_detection->isXTerminal()
|| term_detection->isScreenTerm()
|| FTermcap::osc_support )
return true;
return false;
}
//----------------------------------------------------------------------
void FTermXTerminal::oscPrefix() const
{
initCheck();
if ( term_detection->isTmuxTerm() )
{
// tmux device control string
FTerm::putstring (ESC "Ptmux;" ESC);
}
else if ( term_detection->isScreenTerm() )
{
// GNU Screen device control string
FTerm::putstring (ESC "P");
}
}
//----------------------------------------------------------------------
void FTermXTerminal::oscPostfix() const
{
initCheck();
if ( term_detection->isScreenTerm()
|| term_detection->isTmuxTerm() )
{
// GNU Screen/tmux string terminator
FTerm::putstring (ESC "\\");
}
}
//----------------------------------------------------------------------
FString FTermXTerminal::captureXTermFont() const
{
initCheck(FString{});
if ( term_detection->isXTerminal()
|| term_detection->isScreenTerm()
|| FTermcap::osc_support )
{
fd_set ifds{};
struct timeval tv{};
const int stdin_no = FTermios::getStdIn();
oscPrefix();
FTerm::putstring (OSC "50;?" BEL); // get font
oscPostfix();
std::fflush(stdout);
FD_ZERO(&ifds);
FD_SET(stdin_no, &ifds);
tv.tv_sec = 0;
tv.tv_usec = 150000; // 150 ms
// Read the terminal answer
if ( select(stdin_no + 1, &ifds, nullptr, nullptr, &tv) > 0 )
{
char temp[150]{};
if ( std::scanf("\033]50;%148[^\n]s", temp) == 1 )
{
const std::size_t n = std::strlen(temp);
// BEL + '\0' = string terminator
if ( n >= 5 && temp[n - 1] == BEL[0] && temp[n] == '\0' )
temp[n - 1] = '\0';
return FString{temp};
}
}
}
return FString{};
}
//----------------------------------------------------------------------
FString FTermXTerminal::captureXTermTitle() const
{
initCheck(FString{});
if ( term_detection->isKdeTerminal() )
return FString{};
fd_set ifds{};
struct timeval tv{};
const int stdin_no = FTermios::getStdIn();
FTerm::putstring (CSI "21t"); // get title
std::fflush(stdout);
FD_ZERO(&ifds);
FD_SET(stdin_no, &ifds);
tv.tv_sec = 0;
tv.tv_usec = 150000; // 150 ms
// read the terminal answer
if ( select (stdin_no + 1, &ifds, nullptr, nullptr, &tv) > 0 )
{
char temp[512]{};
if ( std::scanf("\033]l%509[^\n]s", temp) == 1 )
{
const std::size_t n = std::strlen(temp);
// Esc + \ = OSC string terminator
if ( n >= 2 && temp[n - 2] == ESC[0] && temp[n - 1] == '\\' )
{
if ( n < 4 )
return FString{};
temp[n - 2] = '\0';
return FString{temp};
}
}
}
return FString{};
}
//----------------------------------------------------------------------
void FTermXTerminal::enableXTermMouse()
{
// Activate the xterm mouse support
if ( mouse_support )
return; // The mouse is already activated
if ( ! fsystem )
fsystem = FTerm::getFSystem();
FTerm::putstring (CSI "?1001s" // save old highlight mouse tracking
CSI "?1000;" // enable x11 mouse tracking
"1002;" // enable cell motion mouse tracking
"1015;" // enable urxvt mouse mode
"1006h"); // enable SGR mouse mode
std::fflush(stdout);
mouse_support = true;
}
//----------------------------------------------------------------------
void FTermXTerminal::disableXTermMouse()
{
// Deactivate the xterm mouse support
if ( ! mouse_support )
return; // The mouse was already deactivated
FTerm::putstring (CSI "?1006;" // disable SGR mouse mode
"1015;" // disable urxvt mouse mode
"1002;" // disable cell motion mouse tracking
"1000l" // disable x11 mouse tracking
CSI "?1001r"); // restore old highlight mouse tracking
std::fflush(stdout);
mouse_support = false;
}
//----------------------------------------------------------------------
void FTermXTerminal::enableXTermMetaSendsESC()
{
// Activate the xterm meta key sends escape prefix
if ( meta_sends_esc )
return;
FTerm::putstring (CSI "?1036s" // save meta key sends escape
CSI "?1036h"); // enable meta key sends escape
std::fflush(stdout);
meta_sends_esc = true;
}
//----------------------------------------------------------------------
void FTermXTerminal::disableXTermMetaSendsESC()
{
// Deactivate the xterm meta key sends escape prefix
if ( ! meta_sends_esc )
return;
FTerm::putstring (CSI "?1036r"); // restore meta key sends escape
std::fflush(stdout);
meta_sends_esc = false;
}
} // namespace finalcut