/*********************************************************************** * 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 * * . * ***********************************************************************/ #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() { // 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() { *FApplication::getLog() << 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() { 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() { 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() { // 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() { // Reset the XTerm text foreground color if ( canResetColor() ) { oscPrefix(); FTerm::putstring (OSC "110" BEL); oscPostfix(); std::fflush(stdout); } } //---------------------------------------------------------------------- void FTermXTerminal::resetXTermBackground() { // Reset the XTerm text background color if ( canResetColor() ) { oscPrefix(); FTerm::putstring (OSC "111" BEL); oscPostfix(); std::fflush(stdout); } } //---------------------------------------------------------------------- void FTermXTerminal::resetXTermCursorColor() { // Reset the text cursor color if ( canResetColor() ) { oscPrefix(); FTerm::putstring (OSC "112" BEL); oscPostfix(); std::fflush(stdout); } } //---------------------------------------------------------------------- void FTermXTerminal::resetXTermMouseForeground() { // Reset the mouse foreground color if ( canResetColor() ) { oscPrefix(); FTerm::putstring (OSC "113" BEL); oscPostfix(); std::fflush(stdout); } } //---------------------------------------------------------------------- void FTermXTerminal::resetXTermMouseBackground() { // Reset the mouse background color if ( canResetColor() ) { oscPrefix(); FTerm::putstring (OSC "114" BEL); oscPostfix(); std::fflush(stdout); } } //---------------------------------------------------------------------- void FTermXTerminal::resetXTermHighlightBackground() { // Reset the highlight background color if ( canResetColor() ) { oscPrefix(); FTerm::putstring (OSC "117" BEL); oscPostfix(); std::fflush(stdout); } } //---------------------------------------------------------------------- bool FTermXTerminal::canResetColor() { 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() { 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() { initCheck(); if ( term_detection->isScreenTerm() || term_detection->isTmuxTerm() ) { // GNU Screen/tmux string terminator FTerm::putstring (ESC "\\"); } } //---------------------------------------------------------------------- const FString FTermXTerminal::captureXTermFont() { 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{}; } //---------------------------------------------------------------------- const FString FTermXTerminal::captureXTermTitle() { 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