2022 lines · 50.3 KB · Text
RawDownload
/* -*- C++ -*- * This file is part of Zutty. * Copyright (C) 2020 Tom Szilagyi * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * See the file LICENSE for the full license. */ #include "log.h"#include "pty.h" #include <algorithm>#include <sstream> // for the debug/step facility:#include <chrono>#include <thread>#include <sys/types.h>#include <signal.h> namespace zutty{#ifdef DEBUG    inline void   Vterm::debugKey ()   {      switch (debugStep)      {      case 0: debugStep = 1; break;      case 1: debugStep = 10; break;      case 10: debugStep = 100; break;      case 100: debugStep = 0; break;      }      debugCnt = debugStep;      logT << "*** DEBUG step=" << debugStep << std::endl;   }    inline void   Vterm::debugBreak ()   {      if (!debugStep || --debugCnt > 0)         return;       debugCnt = debugStep;      logT << "*** DEBUG STOP (step=" << debugStep << "), "           << readPos + 1 - lastStopPos << " bytes since last:\n        "           << dumpBuffer (inputBuf + lastStopPos, inputBuf + readPos + 1);      lastStopPos = readPos + 1;       logT << "Issue 'kill -CONT " << getpid () << "' or 'fg' to continue."           << std::endl;       redraw ();      using namespace std::chrono_literals;      std::this_thread::sleep_for (100ms);       kill (getpid (), SIGSTOP);   }    #define DEBUG_BREAK debugBreak()   #define TRACE_FUN                                                      \      do {                                                                \         logT << __FUNCTION__ << " [";                                    \      for (size_t k = 0; k < nInputOps; ++k) {                            \         if (k) {                                                         \            vlog << ";";                                                  \         }                                                                \         vlog << inputOps [k];                                            \      }                                                                   \      vlog << "] \t"                                                      \           << "p(" << posY << "," << posX << ")  "                        \           << "d(" << nRows << "," << nCols << ")  "                      \           << "mgn[" << marginTop << "," << marginBottom << ")  "         \           << "hmgn:" << horizMarginMode                                  \           << " [" << hMargin << "," << nColsEff << ")"                   \           << std::endl;                                                  \   } while (0)#else   #define DEBUG_BREAK   #define TRACE_FUN#endif // DEBUG    inline void   Vterm::unhandledInput (unsigned char ch)   {      logE << "Unhandled input char '" << ch << "' (" << (int)ch           << ") in state " << strInputState (inputState)           << ". Escape sequence so far: "           << dumpBuffer (inputBuf + lastEscBegin, inputBuf + readPos + 1);      setState (InputState::Normal);   }    inline void   Vterm::traceNormalInput ()   {   #ifdef DEBUG      if (lastNormalBegin < readPos)      {         auto dumpbufs = dumpBuffer (inputBuf + lastNormalBegin,                                     inputBuf + readPos);         if (dumpbufs.length ())         {            logT << "Inserted: " << dumpbufs;         }      }      lastNormalBegin = readPos + 1;   #endif // DEBUG   }    inline void   Vterm::redraw ()   {      onRefresh (* cf);      cf->resetDamage ();   }    inline const MouseTrackingState&   Vterm::getMouseTrackingState () const   {      return mouseTrk;   }    inline void   Vterm::setHasFocus (bool hasFocus_)   {      hasFocus = hasFocus_;      showCursor ();      redraw ();   }    inline void   Vterm::pageUp ()   {      if (altScrollMode && altScreenBufferMode)      {         inputOps [0] = 1;         nInputOps = 1;         for (int k = 0; k < (marginBottom - marginTop) / 2; ++k)            writePty (VtKey::Up);      }      else      {         cf->pageUp (nRows / 2);         redraw ();      }   }    inline void   Vterm::pageDown ()   {      if (altScrollMode && altScreenBufferMode)      {         inputOps [0] = 1;         nInputOps = 1;         for (int k = 0; k < (marginBottom - marginTop) / 2; ++k)            writePty (VtKey::Down);      }      else      {         cf->pageDown (nRows / 2);         redraw ();      }   }    inline void   Vterm::mouseWheelUp ()   {      if (altScrollMode && altScreenBufferMode)      {         inputOps [0] = 1;         nInputOps = 1;         writePty (VtKey::Up);      }      else      {         cf->pageUp (5);         redraw ();      }   }    inline void   Vterm::mouseWheelDown ()   {      if (altScrollMode && altScreenBufferMode)      {         inputOps [0] = 1;         nInputOps = 1;         writePty (VtKey::Down);      }      else      {         cf->pageDown (5);         redraw ();      }   }    inline void   Vterm::resetTerminal ()   {      resetScreen ();      resetAttrs ();       switchColMode (ColMode::C80);       cf->dropScrollbackHistory ();      cf->resetMargins (marginTop, marginBottom);      clearScreen ();       switchScreenBufferMode (false);      altScrollMode = opts.altScrollMode;      altSendsEscape = opts.altSendsEscape;      modifyOtherKeys = opts.modifyOtherKeys;       horizMarginMode = false;      hMargin = 0;      nColsEff = nCols;       setState (InputState::Normal);       if (haveOscHandler)      {         // reset title         argBuf.clear ();         argBuf.push_back ('0');         argBuf.push_back (';');         for (const char* p = opts.title; *p != '\0'; ++p)            argBuf.push_back (*p);         handle_OSC ();      }   }    inline void   Vterm::resetScreen ()   {      showCursorMode = true;      cursorStyle = CharVdev::Cursor::Style::filled_block;      autoWrapMode = true;      autoNewlineMode = false;      keyboardLocked = false;      insertMode = false;      bkspSendsDel = true;      localEcho = false;      bracketedPasteMode = false;       compatLevel = CompatibilityLevel::VT400;      cursorKeyMode = CursorKeyMode::ANSI;      keypadMode = KeypadMode::Normal;      originMode = OriginMode::Absolute;      charsetState = CharsetState {};       savedCursor_SCO.isSet = false;      savedCursor_DEC->isSet = false;       mouseTrk = MouseTrackingState {};       tabStops.clear ();      cf->getSelection ().clear ();   }    inline void   Vterm::resetAttrs ()   {      reverseVideo = false;      fg = &attrs.fg;      bg = &attrs.bg;       inputOps [0] = 0;      nInputOps = 1;      csi_SGR ();   }    inline void   Vterm::clearScreen ()   {      posX = 0;      posY = 0;      lastCol = false;      fillScreen (' ');   }    inline void   Vterm::fillScreen (uint16_t ch)   {      cf->fillCells (ch, attrs);   }    inline void   Vterm::switchColMode (ColMode colMode_)   {      if (colMode == colMode_)         return;       resetScreen ();      clearScreen ();       if (colMode_ == ColMode::C80)      {         logT << "DECCOLM: Selected 80 columns per line" << std::endl;      }      else      {         logT << "DECCOLM: Selected 132 columns per line" << std::endl;      }       colMode = colMode_;   }    inline void   Vterm::switchScreenBufferMode (bool altScreenBufferMode_)   {      if (altScreenBufferMode == altScreenBufferMode_)         return;       if (altScreenBufferMode_)      {         frame_alt = Frame (winPx, winPy, nCols, nRows,                            marginTop, marginBottom);         cf = &frame_alt;         cf->expose ();          savedCursor_DEC = &savedCursor_DEC_alt;         altScreenBufferMode = true;      }      else      {         frame_pri.resize (winPx, winPy, nCols, nRows,                           marginTop, marginBottom);         cf = &frame_pri;         cf->expose ();         frame_alt.freeCells ();          savedCursor_DEC_alt.isSet = false;         savedCursor_DEC = &savedCursor_DEC_pri;         altScreenBufferMode = false;         cf->getSelection ().clear ();      }   }    inline void   Vterm::setState (InputState newState)   {      if (newState == inputState)         return;       if (newState == InputState::Normal)      {         DEBUG_BREAK;         nInputOps = 0;         inputOps [0] = 0;         lastNormalBegin = readPos + 1;      }      else if (inputState == InputState::Normal)      {         traceNormalInput ();      }       inputState = newState;   }    inline bool   Vterm::readPty ()   {      static bool first = true;      ssize_t n = read (ptyFd, inputBuf, sizeof (inputBuf));      if (n < 0)         return true;      else if (n == 0)         return !first;       if (first)      {         // Mitigate the race condition between shell process startup         // and first window size configuration happening in parallel:         // the signal could get delivered before the shell is ready         // for it, and thus get lost.         pty_resize (ptyFd, nCols, nRows);         first = false;      }       logT << "pty read: " << dumpBuffer (inputBuf, inputBuf + n);      processInput (inputBuf, n);       return false;   }    inline void   Vterm::normalizeCursorPos ()   {      if (nColsEff < posX + 1)         posX = nColsEff - 1;       if (nRows < posY + 1)         posY = nRows - 1;       lastCol = false;   }    inline bool   Vterm::isCursorInsideMargins ()   {      return posX >= hMargin && posX < nColsEff &&             posY >= marginTop && posY < marginBottom;   }    inline void   Vterm::eraseRow (uint16_t pY)   {      cf->eraseInRow (pY, hMargin, nColsEff - hMargin, attrs);   }    inline void   Vterm::eraseRows (uint16_t startY, uint16_t count)   {      for (uint16_t pY = startY; pY < startY + count; ++pY)         eraseRow (pY);   }    inline void   Vterm::copyRow (uint16_t dstY, uint16_t srcY)   {      cf->copyRow (dstY, srcY, hMargin, nColsEff - hMargin);   }    // insert blank rows at and below startY, within the scrolling area   inline void   Vterm::insertRows (uint16_t startY, uint16_t count)   {      for (uint16_t pY = marginBottom - count - 1; pY >= startY; --pY)      {         copyRow (pY + count, pY);         if (!pY) break;      }       for (uint16_t pY = startY; pY < startY + count; ++pY)         eraseRow (pY);   }    // delete rows at and below startY, within the scrolling area   inline void   Vterm::deleteRows (uint16_t startY, uint16_t count)   {      for (uint16_t pY = startY; pY < marginBottom - count; ++pY)         copyRow (pY, pY + count);       for (uint16_t pY = marginBottom - count; pY < marginBottom; ++pY)         eraseRow (pY);   }    // insert blank cols at and to the right of startX, within the scrolling area   inline void   Vterm::insertCols (uint16_t startX, uint16_t count)   {      for (uint16_t r = marginTop; r < marginBottom; ++r)      {         cf->moveInRow (r, startX + count, startX, nColsEff - startX - count);         cf->eraseInRow (r, startX, count, attrs);      }   }    // delete cols at and to the right of startX, within the scrolling area   inline void   Vterm::deleteCols (uint16_t startX, uint16_t count)   {      for (uint16_t r = marginTop; r < marginBottom; ++r)      {         cf->moveInRow (r, startX, startX + count, nColsEff - startX - count);         cf->eraseInRow (r, nColsEff - count, count, attrs);      }   }    inline void   Vterm::inputGraphicChar (unsigned char ch)   {      if ((ch & 0x80) == 0)      {         utf8dec.checkPrematureEOS ();          // GL range         Charset cs;         if (charsetState.ss)         {            cs = charsetState.g [charsetState.ss];            charsetState.ss = 0;         }         else            cs = charsetState.g [charsetState.gl];          if (cs == Charset::UTF8)            utf8dec.onUnicode (ch < 127 ? ch : 0);         else if (ch >= 32 && (cs == Charset::IsoLatin1 || ch < 127))            utf8dec.onUnicode (charCodes [(uint8_t)cs] [ch - 32]);      }      else      {         // GR range

Lines 1–500 of 2022.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t