21 files changed, 5023 insertions, 48 deletions
diff --git a/noncore/apps/opie-console/TECommon.h b/noncore/apps/opie-console/TECommon.h new file mode 100644 index 0000000..5db41ad --- a/dev/null +++ b/noncore/apps/opie-console/TECommon.h @@ -0,0 +1,114 @@ +/* -------------------------------------------------------------------------- */ +/* */ +/* [TECommon.h] Common Definitions */ +/* */ +/* -------------------------------------------------------------------------- */ +/* */ +/* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ +/* */ +/* This file is part of Konsole - an X terminal for KDE */ +/* */ +/* -------------------------------------------------------------------------- */ +/* */ +/* Ported Konsole to Qt/Embedded */ +/* */ +/* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ +/* */ +/* -------------------------------------------------------------------------- */ + +/*! \file TECommon.h + \brief Definitions shared between TEScreen and TEWidget. +*/ + +#ifndef TECOMMON_H +#define TECOMMON_H + +#include <qcolor.h> + +#ifndef BOOL +typedef int BOOL; +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef UINT8 +typedef unsigned char UINT8; +#endif + +#ifndef UINT16 +typedef unsigned short UINT16; +#endif + +// Attributed Character Representations /////////////////////////////// + +// Colors + +#define BASE_COLORS (2+8) +#define INTENSITIES 2 +#define TABLE_COLORS (INTENSITIES*BASE_COLORS) + +#define DEFAULT_FORE_COLOR 0 +#define DEFAULT_BACK_COLOR 1 + +#define DEFAULT_RENDITION 0 +#define RE_BOLD (1 << 0) +#define RE_BLINK (1 << 1) +#define RE_UNDERLINE (1 << 2) +#define RE_REVERSE (1 << 3) // Screen only +#define RE_INTENSIVE (1 << 3) // Widget only + +/*! \class ca + * \brief a character with rendition attributes. +*/ + +class ca +{ +public: + inline ca(UINT16 _c = ' ', + UINT8 _f = DEFAULT_FORE_COLOR, + UINT8 _b = DEFAULT_BACK_COLOR, + UINT8 _r = DEFAULT_RENDITION) + : c(_c), f(_f), b(_b), r(_r) {} +public: + UINT16 c; // character + UINT8 f; // foreground color + UINT8 b; // background color + UINT8 r; // rendition +public: + friend BOOL operator == (ca a, ca b); + friend BOOL operator != (ca a, ca b); +}; + +inline BOOL operator == (ca a, ca b) +{ + return a.c == b.c && a.f == b.f && a.b == b.b && a.r == b.r; +} + +inline BOOL operator != (ca a, ca b) +{ + return a.c != b.c || a.f != b.f || a.b != b.b || a.r != b.r; +} + +/*! +*/ +struct ColorEntry +{ + ColorEntry(QColor c, bool tr, bool b) : color(c), transparent(tr), bold(b) {} + ColorEntry() : transparent(false), bold(false) {} // default constructors + void operator=(const ColorEntry& rhs) { + color = rhs.color; + transparent = rhs.transparent; + bold = rhs.bold; + } + QColor color; + bool transparent; // if used on bg + bool bold; // if used on fg +}; + +#endif // TECOMMON_H diff --git a/noncore/apps/opie-console/TEHistory.cpp b/noncore/apps/opie-console/TEHistory.cpp new file mode 100644 index 0000000..317ce57 --- a/dev/null +++ b/noncore/apps/opie-console/TEHistory.cpp @@ -0,0 +1,212 @@ +/* -------------------------------------------------------------------------- */ +/* */ +/* [TEHistory.C] History Buffer */ +/* */ +/* -------------------------------------------------------------------------- */ +/* */ +/* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ +/* */ +/* This file is part of Konsole - an X terminal for KDE */ +/* */ +/* -------------------------------------------------------------------------- */ +/* */ +/* Ported Konsole to Qt/Embedded */ +/* */ +/* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ +/* */ +/* -------------------------------------------------------------------------- */ + +#include "TEHistory.h" +#include <stdlib.h> +#include <assert.h> +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> +#include <errno.h> + +#define HERE printf("%s(%d): here\n",__FILE__,__LINE__) + +/* + An arbitrary long scroll. + + One can modify the scroll only by adding either cells + or newlines, but access it randomly. + + The model is that of an arbitrary wide typewriter scroll + in that the scroll is a serie of lines and each line is + a serie of cells with no overwriting permitted. + + The implementation provides arbitrary length and numbers + of cells and line/column indexed read access to the scroll + at constant costs. + +FIXME: some complain about the history buffer comsuming the + memory of their machines. This problem is critical + since the history does not behave gracefully in cases + where the memory is used up completely. + + I put in a workaround that should handle it problem + now gracefully. I'm not satisfied with the solution. + +FIXME: Terminating the history is not properly indicated + in the menu. We should throw a signal. + +FIXME: There is noticable decrease in speed, also. Perhaps, + there whole feature needs to be revisited therefore. + Disadvantage of a more elaborated, say block-oriented + scheme with wrap around would be it's complexity. +*/ + +//FIXME: tempory replacement for tmpfile +// this is here one for debugging purpose. + +//#define tmpfile xTmpFile + +FILE* xTmpFile() +{ + static int fid = 0; + char fname[80]; + sprintf(fname,"TmpFile.%d",fid++); + return fopen(fname,"w"); +} + + +// History Buffer /////////////////////////////////////////// + +/* + A Row(X) data type which allows adding elements to the end. +*/ + +HistoryBuffer::HistoryBuffer() +{ + ion = -1; + length = 0; +} + +HistoryBuffer::~HistoryBuffer() +{ + setScroll(FALSE); +} + +void HistoryBuffer::setScroll(bool on) +{ + if (on == hasScroll()) return; + + if (on) + { + assert( ion < 0 ); + assert( length == 0); + FILE* tmp = tmpfile(); if (!tmp) { perror("konsole: cannot open temp file.\n"); return; } + ion = dup(fileno(tmp)); if (ion<0) perror("konsole: cannot dup temp file.\n"); + fclose(tmp); + } + else + { + assert( ion >= 0 ); + close(ion); + ion = -1; + length = 0; + } +} + +bool HistoryBuffer::hasScroll() +{ + return ion >= 0; +} + +void HistoryBuffer::add(const unsigned char* bytes, int len) +{ int rc; + assert(hasScroll()); + rc = lseek(ion,length,SEEK_SET); if (rc < 0) { perror("HistoryBuffer::add.seek"); setScroll(FALSE); return; } + rc = write(ion,bytes,len); if (rc < 0) { perror("HistoryBuffer::add.write"); setScroll(FALSE); return; } + length += rc; +} + +void HistoryBuffer::get(unsigned char* bytes, int len, int loc) +{ int rc; + assert(hasScroll()); + if (loc < 0 || len < 0 || loc + len > length) + fprintf(stderr,"getHist(...,%d,%d): invalid args.\n",len,loc); + rc = lseek(ion,loc,SEEK_SET); if (rc < 0) { perror("HistoryBuffer::get.seek"); setScroll(FALSE); return; } + rc = read(ion,bytes,len); if (rc < 0) { perror("HistoryBuffer::get.read"); setScroll(FALSE); return; } +} + +int HistoryBuffer::len() +{ + return length; +} + +// History Scroll ////////////////////////////////////// + +/* + The history scroll makes a Row(Row(Cell)) from + two history buffers. The index buffer contains + start of line positions which refere to the cells + buffer. + + Note that index[0] addresses the second line + (line #1), while the first line (line #0) starts + at 0 in cells. +*/ + +HistoryScroll::HistoryScroll() +{ +} + +HistoryScroll::~HistoryScroll() +{ +} + +void HistoryScroll::setScroll(bool on) +{ + index.setScroll(on); + cells.setScroll(on); +} + +bool HistoryScroll::hasScroll() +{ + return index.hasScroll() && cells.hasScroll(); +} + +int HistoryScroll::getLines() +{ + if (!hasScroll()) return 0; + return index.len() / sizeof(int); +} + +int HistoryScroll::getLineLen(int lineno) +{ + if (!hasScroll()) return 0; + return (startOfLine(lineno+1) - startOfLine(lineno)) / sizeof(ca); +} + +int HistoryScroll::startOfLine(int lineno) +{ + if (lineno <= 0) return 0; + if (!hasScroll()) return 0; + if (lineno <= getLines()) + { int res; + index.get((unsigned char*)&res,sizeof(int),(lineno-1)*sizeof(int)); + return res; + } + return cells.len(); +} + +void HistoryScroll::getCells(int lineno, int colno, int count, ca res[]) +{ + assert(hasScroll()); + cells.get((unsigned char*)res,count*sizeof(ca),startOfLine(lineno)+colno*sizeof(ca)); +} + +void HistoryScroll::addCells(ca text[], int count) +{ + if (!hasScroll()) return; + cells.add((unsigned char*)text,count*sizeof(ca)); +} + +void HistoryScroll::addLine() +{ + if (!hasScroll()) return; + int locn = cells.len(); + index.add((unsigned char*)&locn,sizeof(int)); +} diff --git a/noncore/apps/opie-console/TEHistory.h b/noncore/apps/opie-console/TEHistory.h new file mode 100644 index 0000000..8339ec6 --- a/dev/null +++ b/noncore/apps/opie-console/TEHistory.h @@ -0,0 +1,75 @@ +/* -------------------------------------------------------------------------- */ +/* */ +/* [TEHistory.H] History Buffer */ +/* */ +/* -------------------------------------------------------------------------- */ +/* */ +/* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ +/* */ +/* This file is part of Konsole - an X terminal for KDE */ +/* */ +/* -------------------------------------------------------------------------- */ +/* */ +/* Ported Konsole to Qt/Embedded */ +/* */ +/* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ +/* */ +/* -------------------------------------------------------------------------- */ + +#ifndef TEHISTORY_H +#define TEHISTORY_H + +#include "TECommon.h" + +/* + An extendable tmpfile(1) based buffer. +*/ +class HistoryBuffer +{ +public: + HistoryBuffer(); + ~HistoryBuffer(); + +public: + void setScroll(bool on); + bool hasScroll(); + +public: + void add(const unsigned char* bytes, int len); + void get(unsigned char* bytes, int len, int loc); + int len(); + +private: + int ion; + int length; +}; + +class HistoryScroll +{ +public: + HistoryScroll(); + ~HistoryScroll(); + +public: + void setScroll(bool on); + bool hasScroll(); + +public: // access to history + int getLines(); + int getLineLen(int lineno); + void getCells(int lineno, int colno, int count, ca res[]); + +public: // backward compatibility (obsolete) + ca getCell(int lineno, int colno) { ca res; getCells(lineno,colno,1,&res); return res; } + +public: // adding lines. + void addCells(ca a[], int count); + void addLine(); + +private: + int startOfLine(int lineno); + HistoryBuffer index; // lines Row(int) + HistoryBuffer cells; // text Row(ca) +}; + +#endif // TEHISTORY_H diff --git a/noncore/apps/opie-console/TEScreen.cpp b/noncore/apps/opie-console/TEScreen.cpp new file mode 100644 index 0000000..a3d115d --- a/dev/null +++ b/noncore/apps/opie-console/TEScreen.cpp @@ -0,0 +1,1197 @@ +/* -------------------------------------------------------------------------- */ +/* */ +/* [TEScreen.C] Screen Data Type */ +/* */ +/* -------------------------------------------------------------------------- */ +/* */ +/* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ +/* */ +/* This file is part of Konsole - an X terminal for KDE */ +/* */ +/* -------------------------------------------------------------------------- */ +/* */ +/* Ported Konsole to Qt/Embedded */ +/* */ +/* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ +/* */ +/* -------------------------------------------------------------------------- */ + +/*! \file +*/ + +/*! \class TEScreen + + \brief The image manipulated by the emulation. + + This class implements the operations of the terminal emulation framework. + It is a complete passive device, driven by the emulation decoder + (TEmuVT102). By this it forms in fact an ADT, that defines operations + on a rectangular image. + + It does neither know how to display its image nor about escape sequences. + It is further independent of the underlying toolkit. By this, one can even + use this module for an ordinary text surface. + + Since the operations are called by a specific emulation decoder, one may + collect their different operations here. + + The state manipulated by the operations is mainly kept in `image', though + it is a little more complex bejond this. See the header file of the class. + + \sa TEWidget \sa VT102Emulation +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +// #include <kdebug.h> + +#include <assert.h> +#include <string.h> +#include <ctype.h> + +#include "TEScreen.h" + +#define HERE printf("%s(%d): here\n",__FILE__,__LINE__) + +//FIXME: this is emulation specific. Use FALSE for xterm, TRUE for ANSI. +//FIXME: see if we can get this from terminfo. +#define BS_CLEARS FALSE + +#define loc(X,Y) ((Y)*columns+(X)) + +/*! creates a `TEScreen' of `lines' lines and `columns' columns. +*/ + +TEScreen::TEScreen(int lines, int columns) +{ + this->lines = lines; + this->columns = columns; + + image = (ca*) malloc(lines*columns*sizeof(ca)); + tabstops = NULL; initTabStops(); + + histCursor = 0; + + clearSelection(); + reset(); +} + +/*! Destructor +*/ + +TEScreen::~TEScreen() +{ + free(image); + if (tabstops) free(tabstops); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Normalized Screen Operations */ +/* */ +/* ------------------------------------------------------------------------- */ + +// Cursor Setting -------------------------------------------------------------- + +/*! \section Cursor + + The `cursor' is a location within the screen that is implicitely used in + many operations. The operations within this section allow to manipulate + the cursor explicitly and to obtain it's value. + + The position of the cursor is guarantied to be between (including) 0 and + `columns-1' and `lines-1'. +*/ + +/*! + Move the cursor up. + + The cursor will not be moved beyond the top margin. +*/ + +void TEScreen::cursorUp(int n) +//=CUU +{ + if (n == 0) n = 1; // Default + int stop = cuY < tmargin ? 0 : tmargin; + cuX = QMIN(columns-1,cuX); // nowrap! + cuY = QMAX(stop,cuY-n); +} + +/*! + Move the cursor down. + + The cursor will not be moved beyond the bottom margin. +*/ + +void TEScreen::cursorDown(int n) +//=CUD +{ + if (n == 0) n = 1; // Default + int stop = cuY > bmargin ? lines-1 : bmargin; + cuX = QMIN(columns-1,cuX); // nowrap! + cuY = QMIN(stop,cuY+n); +} + +/*! + Move the cursor left. + + The cursor will not move beyond the first column. +*/ + +void TEScreen::cursorLeft(int n) +//=CUB +{ + if (n == 0) n = 1; // Default + cuX = QMIN(columns-1,cuX); // nowrap! + cuX = QMAX(0,cuX-n); +} + +/*! + Move the cursor left. + + The cursor will not move beyond the rightmost column. +*/ + +void TEScreen::cursorRight(int n) +//=CUF +{ + if (n == 0) n = 1; // Default + cuX = QMIN(columns-1,cuX+n); +} + +/*! + Set top and bottom margin. +*/ + +void TEScreen::setMargins(int top, int bot) +//=STBM +{ + if (top == 0) top = 1; // Default + if (bot == 0) bot = lines; // Default + top = top - 1; // Adjust to internal lineno + bot = bot - 1; // Adjust to internal lineno + if ( !( 0 <= top && top < bot && bot < lines ) ) + { fprintf(stderr,"%s(%d) : setRegion(%d,%d) : bad range.\n", + __FILE__,__LINE__,top,bot); + return; // Default error action: ignore + } + tmargin = top; + bmargin = bot; + cuX = 0; + cuY = getMode(MODE_Origin) ? top : 0; +} + +/*! + Move the cursor down one line. + + If cursor is on bottom margin, the region between the + actual top and bottom margin is scrolled up instead. +*/ + +void TEScreen::index() +//=IND +{ + if (cuY == bmargin) + { + if (tmargin == 0 && bmargin == lines-1) addHistLine(); // hist.history + scrollUp(tmargin,1); + } + else if (cuY < lines-1) + cuY += 1; +} + +/*! + Move the cursor up one line. + + If cursor is on the top margin, the region between the + actual top and bottom margin is scrolled down instead. +*/ + +void TEScreen::reverseIndex() +//=RI +{ + if (cuY == tmargin) + scrollDown(tmargin,1); + else if (cuY > 0) + cuY -= 1; +} + +/*! + Move the cursor to the begin of the next line. + + If cursor is on bottom margin, the region between the + actual top and bottom margin is scrolled up. +*/ + +void TEScreen::NextLine() +//=NEL +{ + Return(); index(); +} + +// Line Editing ---------------------------------------------------------------- + +/*! \section inserting / deleting characters +*/ + +/*! erase `n' characters starting from (including) the cursor position. + + The line is filled in from the right with spaces. +*/ + +void TEScreen::eraseChars(int n) +{ + if (n == 0) n = 1; // Default + int p = QMAX(0,QMIN(cuX+n-1,columns-1)); + clearImage(loc(cuX,cuY),loc(p,cuY),' '); +} + +/*! delete `n' characters starting from (including) the cursor position. + + The line is filled in from the right with spaces. +*/ + +void TEScreen::deleteChars(int n) +{ + if (n == 0) n = 1; // Default + int p = QMAX(0,QMIN(cuX+n,columns-1)); + moveImage(loc(cuX,cuY),loc(p,cuY),loc(columns-1,cuY)); + clearImage(loc(columns-n,cuY),loc(columns-1,cuY),' '); +} + +/*! insert `n' spaces at the cursor position. + + The cursor is not moved by the operation. +*/ + +void TEScreen::insertChars(int n) +{ + if (n == 0) n = 1; // Default + int p = QMAX(0,QMIN(columns-1-n,columns-1)); + int q = QMAX(0,QMIN(cuX+n,columns-1)); + moveImage(loc(q,cuY),loc(cuX,cuY),loc(p,cuY)); + clearImage(loc(cuX,cuY),loc(q-1,cuY),' '); +} + +/*! delete `n' lines starting from (including) the cursor position. + + The cursor is not moved by the operation. +*/ + +void TEScreen::deleteLines(int n) +{ + if (n == 0) n = 1; // Default + scrollUp(cuY,n); +} + +/*! insert `n' lines at the cursor position. + + The cursor is not moved by the operation. +*/ + +void TEScreen::insertLines(int n) +{ + if (n == 0) n = 1; // Default + scrollDown(cuY,n); +} + +// Mode Operations ----------------------------------------------------------- + +/*! Set a specific mode. */ + +void TEScreen::setMode(int m) +{ + currParm.mode[m] = TRUE; + switch(m) + { + case MODE_Origin : cuX = 0; cuY = tmargin; break; //FIXME: home + } +} + +/*! Reset a specific mode. */ + +void TEScreen::resetMode(int m) +{ + currParm.mode[m] = FALSE; + switch(m) + { + case MODE_Origin : cuX = 0; cuY = 0; break; //FIXME: home + } +} + +/*! Save a specific mode. */ + +void TEScreen::saveMode(int m) +{ + saveParm.mode[m] = currParm.mode[m]; +} + +/*! Restore a specific mode. */ + +void TEScreen::restoreMode(int m) +{ + currParm.mode[m] = saveParm.mode[m]; +} + +//NOTE: this is a helper function +/*! Return the setting a specific mode. */ +BOOL TEScreen::getMode(int m) +{ + return currParm.mode[m]; +} + +/*! Save the cursor position and the rendition attribute settings. */ + +void TEScreen::saveCursor() +{ + sa_cuX = cuX; + sa_cuY = cuY; + sa_cu_re = cu_re; + sa_cu_fg = cu_fg; + sa_cu_bg = cu_bg; +} + +/*! Restore the cursor position and the rendition attribute settings. */ + +void TEScreen::restoreCursor() +{ + cuX = QMIN(sa_cuX,columns-1); + cuY = QMIN(sa_cuY,lines-1); + cu_re = sa_cu_re; + cu_fg = sa_cu_fg; + cu_bg = sa_cu_bg; + effectiveRendition(); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Screen Operations */ +/* */ +/* ------------------------------------------------------------------------- */ + +/*! Assing a new size to the screen. + + The topmost left position is maintained, while lower lines + or right hand side columns might be removed or filled with + spaces to fit the new size. + + The region setting is reset to the whole screen and the + tab positions reinitialized. +*/ + +void TEScreen::resizeImage(int new_lines, int new_columns) +{ + + if (cuY > new_lines-1) + { // attempt to preserve focus and lines + bmargin = lines-1; //FIXME: margin lost + for (int i = 0; i < cuY-(new_lines-1); i++) + { + addHistLine(); scrollUp(0,1); + } + } + + // make new image + ca* newimg = (ca*)malloc(new_lines*new_columns*sizeof(ca)); + + clearSelection(); + + // clear new image + for (int y = 0; y < new_lines; y++) + for (int x = 0; x < new_columns; x++) + { + newimg[y*new_columns+x].c = ' '; + newimg[y*new_columns+x].f = DEFAULT_FORE_COLOR; + newimg[y*new_columns+x].b = DEFAULT_BACK_COLOR; + newimg[y*new_columns+x].r = DEFAULT_RENDITION; + } + int cpy_lines = QMIN(new_lines, lines); + int cpy_columns = QMIN(new_columns,columns); + // copy to new image + for (int y = 0; y < cpy_lines; y++) + for (int x = 0; x < cpy_columns; x++) + { + newimg[y*new_columns+x].c = image[loc(x,y)].c; + newimg[y*new_columns+x].f = image[loc(x,y)].f; + newimg[y*new_columns+x].b = image[loc(x,y)].b; + newimg[y*new_columns+x].r = image[loc(x,y)].r; + } + free(image); + image = newimg; + lines = new_lines; + columns = new_columns; + cuX = QMIN(cuX,columns-1); + cuY = QMIN(cuY,lines-1); + + // FIXME: try to keep values, evtl. + tmargin=0; + bmargin=lines-1; + initTabStops(); + clearSelection(); +} + +/* + Clarifying rendition here and in TEWidget. + + currently, TEWidget's color table is + 0 1 2 .. 9 10 .. 17 + dft_fg, dft_bg, dim 0..7, intensive 0..7 + + cu_fg, cu_bg contain values 0..8; + - 0 = default color + - 1..8 = ansi specified color + + re_fg, re_bg contain values 0..17 + due to the TEWidget's color table + + rendition attributes are + + attr widget screen + -------------- ------ ------ + RE_UNDERLINE XX XX affects foreground only + RE_BLINK XX XX affects foreground only + RE_BOLD XX XX affects foreground only + RE_REVERSE -- XX + RE_TRANSPARENT XX -- affects background only + RE_INTENSIVE XX -- affects foreground only + + Note that RE_BOLD is used in both widget + and screen rendition. Since xterm/vt102 + is to poor to distinguish between bold + (which is a font attribute) and intensive + (which is a color attribute), we translate + this and RE_BOLD in falls eventually appart + into RE_BOLD and RE_INTENSIVE. +*/ + +void TEScreen::reverseRendition(ca* p) +{ UINT8 f = p->f; UINT8 b = p->b; + p->f = b; p->b = f; //p->r &= ~RE_TRANSPARENT; +} + +void TEScreen::effectiveRendition() +// calculate rendition +{ + ef_re = cu_re & (RE_UNDERLINE | RE_BLINK); + if (cu_re & RE_REVERSE) + { + ef_fg = cu_bg; + ef_bg = cu_fg; + } + else + { + ef_fg = cu_fg; + ef_bg = cu_bg; + } + if (cu_re & RE_BOLD) + { + if (ef_fg < BASE_COLORS) + ef_fg += BASE_COLORS; + else + ef_fg -= BASE_COLORS; + } +} + +/*! + returns the image. + + Get the size of the image by \sa getLines and \sa getColumns. + + NOTE that the image returned by this function must later be + freed. + +*/ + +ca* TEScreen::getCookedImage() +{ int x,y; + ca* merged = (ca*)malloc(lines*columns*sizeof(ca)); + ca dft(' ',DEFAULT_FORE_COLOR,DEFAULT_BACK_COLOR,DEFAULT_RENDITION); + + for (y = 0; (y < lines) && (y < (hist.getLines()-histCursor)); y++) + { + int len = QMIN(columns,hist.getLineLen(y+histCursor)); + int yp = y*columns; + int yq = (y+histCursor)*columns; + + hist.getCells(y+histCursor,0,len,merged+yp); + for (x = len; x < columns; x++) merged[yp+x] = dft; + for (x = 0; x < columns; x++) + { int p=x + yp; int q=x + yq; + if ( ( q >= sel_TL ) && ( q <= sel_BR ) ) + reverseRendition(&merged[p]); // for selection + } + } + if (lines >= hist.getLines()-histCursor) + { + for (y = (hist.getLines()-histCursor); y < lines ; y++) + { + int yp = y*columns; + int yq = (y+histCursor)*columns; + int yr = (y-hist.getLines()+histCursor)*columns; + for (x = 0; x < columns; x++) + { int p = x + yp; int q = x + yq; int r = x + yr; + merged[p] = image[r]; + if ( q >= sel_TL && q <= sel_BR ) + reverseRendition(&merged[p]); // for selection + } + + } + } + // evtl. inverse display + if (getMode(MODE_Screen)) + { int i,n = lines*columns; + for (i = 0; i < n; i++) + reverseRendition(&merged[i]); // for reverse display + } + if (getMode(MODE_Cursor) && (cuY+(hist.getLines()-histCursor) < lines)) // cursor visible + reverseRendition(&merged[loc(cuX,cuY+(hist.getLines()-histCursor))]); + return merged; +} + + +/*! +*/ + +void TEScreen::reset() +{ + setMode(MODE_Wrap ); saveMode(MODE_Wrap ); // wrap at end of margin + resetMode(MODE_Origin); saveMode(MODE_Origin); // position refere to [1,1] + resetMode(MODE_Insert); saveMode(MODE_Insert); // overstroke + setMode(MODE_Cursor); // cursor visible + resetMode(MODE_Screen); // screen not inverse + resetMode(MODE_NewLine); + + tmargin=0; + bmargin=lines-1; + + setDefaultRendition(); + saveCursor(); + + clear(); +} + +/*! Clear the entire screen and home the cursor. +*/ + +void TEScreen::clear() +{ + clearEntireScreen(); + home(); +} + +/*! Moves the cursor left one column. +*/ + +void TEScreen::BackSpace() +{ + cuX = QMAX(0,cuX-1); + if (BS_CLEARS) image[loc(cuX,cuY)].c = ' '; +} + +/*! +*/ + +void TEScreen::Tabulate() +{ + // note that TAB is a format effector (does not write ' '); + cursorRight(1); while(cuX < columns-1 && !tabstops[cuX]) cursorRight(1); +} + +void TEScreen::clearTabStops() +{ + for (int i = 0; i < columns; i++) tabstops[i-1] = FALSE; +} + +void TEScreen::changeTabStop(bool set) +{ + if (cuX >= columns) return; + tabstops[cuX] = set; +} + +void TEScreen::initTabStops() +{ + if (tabstops) free(tabstops); + tabstops = (bool*)malloc(columns*sizeof(bool)); + // Arrg! The 1st tabstop has to be one longer than the other. + // i.e. the kids start counting from 0 instead of 1. + // Other programs might behave correctly. Be aware. + for (int i = 0; i < columns; i++) tabstops[i] = (i%8 == 0 && i != 0); +} + +/*! + This behaves either as IND (Screen::Index) or as NEL (Screen::NextLine) + depending on the NewLine Mode (LNM). This mode also + affects the key sequence returned for newline ([CR]LF). +*/ + +void TEScreen::NewLine() +{ + if (getMode(MODE_NewLine)) Return(); + index(); +} + +/*! put `c' literally onto the screen at the current cursor position. + + VT100 uses the convention to produce an automatic newline (am) + with the *first* character that would fall onto the next line (xenl). +*/ + +void TEScreen::checkSelection(int from, int to) +{ + if (sel_begin == -1) return; + int scr_TL = loc(0, hist.getLines()); + //Clear entire selection if it overlaps region [from, to] + if ( (sel_BR > (from+scr_TL) )&&(sel_TL < (to+scr_TL)) ) + { + clearSelection(); + } +} + +void TEScreen::ShowCharacter(unsigned short c) +{ + // Note that VT100 does wrapping BEFORE putting the character. + // This has impact on the assumption of valid cursor positions. + // We indicate the fact that a newline has to be triggered by + // putting the cursor one right to the last column of the screen. + + if (cuX >= columns) + { + if (getMode(MODE_Wrap)) NextLine(); else cuX = columns-1; + } + + if (getMode(MODE_Insert)) insertChars(1); + + int i = loc(cuX,cuY); + + checkSelection(i, i); // check if selection is still valid. + + image[i].c = c; + image[i].f = ef_fg; + image[i].b = ef_bg; + image[i].r = ef_re; + + cuX += 1; +} + +// Region commands ------------------------------------------------------------- + + +/*! scroll up `n' lines within current region. + The `n' new lines are cleared. + \sa setRegion \sa scrollDown +*/ + +void TEScreen::scrollUp(int from, int n) +{ + if (n <= 0 || from + n > bmargin) return; + //FIXME: make sure `tmargin', `bmargin', `from', `n' is in bounds. + moveImage(loc(0,from),loc(0,from+n),loc(columns-1,bmargin)); + clearImage(loc(0,bmargin-n+1),loc(columns-1,bmargin),' '); +} + +/*! scroll down `n' lines within current region. + The `n' new lines are cleared. + \sa setRegion \sa scrollUp +*/ + +void TEScreen::scrollDown(int from, int n) +{ +//FIXME: make sure `tmargin', `bmargin', `from', `n' is in bounds. + if (n <= 0) return; + if (from > bmargin) return; + if (from + n > bmargin) n = bmargin - from; + moveImage(loc(0,from+n),loc(0,from),loc(columns-1,bmargin-n)); + clearImage(loc(0,from),loc(columns-1,from+n-1),' '); +} + +/*! position the cursor to a specific line and column. */ +void TEScreen::setCursorYX(int y, int x) +{ + setCursorY(y); setCursorX(x); +} + +/*! Set the cursor to x-th line. */ + +void TEScreen::setCursorX(int x) +{ + if (x == 0) x = 1; // Default + x -= 1; // Adjust + cuX = QMAX(0,QMIN(columns-1, x)); +} + +/*! Set the cursor to y-th line. */ + +void TEScreen::setCursorY(int y) +{ + if (y == 0) y = 1; // Default + y -= 1; // Adjust + cuY = QMAX(0,QMIN(lines -1, y + (getMode(MODE_Origin) ? tmargin : 0) )); +} + +/*! set cursor to the `left upper' corner of the screen (1,1). +*/ + +void TEScreen::home() +{ + cuX = 0; + cuY = 0; +} + +/*! set cursor to the begin of the current line. +*/ + +void TEScreen::Return() +{ + cuX = 0; +} + +/*! returns the current cursor columns. +*/ + +int TEScreen::getCursorX() +{ + return cuX; +} + +/*! returns the current cursor line. +*/ + +int TEScreen::getCursorY() +{ + return cuY; +} + +// Erasing --------------------------------------------------------------------- + +/*! \section Erasing + + This group of operations erase parts of the screen contents by filling + it with spaces colored due to the current rendition settings. + + Althought the cursor position is involved in most of these operations, + it is never modified by them. +*/ + +/*! fill screen between (including) `loca' and `loce' with spaces. + + This is an internal helper functions. The parameter types are internal + addresses of within the screen image and make use of the way how the + screen matrix is mapped to the image vector. +*/ + +void TEScreen::clearImage(int loca, int loce, char c) +{ int i; + int scr_TL=loc(0,hist.getLines()); + //FIXME: check positions + + //Clear entire selection if it overlaps region to be moved... + if ( (sel_BR > (loca+scr_TL) )&&(sel_TL < (loce+scr_TL)) ) + { + clearSelection(); + } + for (i = loca; i <= loce; i++) + { + image[i].c = c; + image[i].f = ef_fg; //DEFAULT_FORE_COLOR; //FIXME: xterm and linux/ansi + image[i].b = ef_bg; //DEFAULT_BACK_COLOR; // many have different + image[i].r = ef_re; //DEFAULT_RENDITION; // ideas here. + } +} + +/*! move image between (including) `loca' and `loce' to 'dst'. + + This is an internal helper functions. The parameter types are internal + addresses of within the screen image and make use of the way how the + screen matrix is mapped to the image vector. +*/ + +void TEScreen::moveImage(int dst, int loca, int loce) +{ +//FIXME: check positions + if (loce < loca) { + // kdDebug() << "WARNING!!! call to TEScreen:moveImage with loce < loca!" << endl; + return; + } + memmove(&image[dst],&image[loca],(loce-loca+1)*sizeof(ca)); +} + +/*! clear from (including) current cursor position to end of screen. +*/ + +void TEScreen::clearToEndOfScreen() +{ + clearImage(loc(cuX,cuY),loc(columns-1,lines-1),' '); +} + +/*! clear from begin of screen to (including) current cursor position. +*/ + +void TEScreen::clearToBeginOfScreen() +{ + clearImage(loc(0,0),loc(cuX,cuY),' '); +} + +/*! clear the entire screen. +*/ + +void TEScreen::clearEntireScreen() +{ + clearImage(loc(0,0),loc(columns-1,lines-1),' '); +} + +/*! fill screen with 'E' + This is to aid screen alignment +*/ + +void TEScreen::helpAlign() +{ + clearImage(loc(0,0),loc(columns-1,lines-1),'E'); +} + +/*! clear from (including) current cursor position to end of current cursor line. +*/ + +void TEScreen::clearToEndOfLine() +{ + clearImage(loc(cuX,cuY),loc(columns-1,cuY),' '); +} + +/*! clear from begin of current cursor line to (including) current cursor position. +*/ + +void TEScreen::clearToBeginOfLine() +{ + clearImage(loc(0,cuY),loc(cuX,cuY),' '); +} + +/*! clears entire current cursor line +*/ + +void TEScreen::clearEntireLine() +{ + clearImage(loc(0,cuY),loc(columns-1,cuY),' '); +} + +// Rendition ------------------------------------------------------------------ + +/*! + set rendition mode +*/ + +void TEScreen::setRendition(int re) +{ + cu_re |= re; + effectiveRendition(); +} + +/*! + reset rendition mode +*/ + +void TEScreen::resetRendition(int re) +{ + cu_re &= ~re; + effectiveRendition(); +} + +/*! +*/ + +void TEScreen::setDefaultRendition() +{ + setForeColorToDefault(); + setBackColorToDefault(); + cu_re = DEFAULT_RENDITION; + effectiveRendition(); +} + +/*! +*/ + +void TEScreen::setForeColor(int fgcolor) +{ + cu_fg = (fgcolor&7)+((fgcolor&8) ? 4+8 : 2); + effectiveRendition(); +} + +/*! +*/ + +void TEScreen::setBackColor(int bgcolor) +{ + cu_bg = (bgcolor&7)+((bgcolor&8) ? 4+8 : 2); + effectiveRendition(); +} + +/*! +*/ + +void TEScreen::setBackColorToDefault() +{ + cu_bg = DEFAULT_BACK_COLOR; + effectiveRendition(); +} + +/*! +*/ + +void TEScreen::setForeColorToDefault() +{ + cu_fg = DEFAULT_FORE_COLOR; + effectiveRendition(); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Marking & Selection */ +/* */ +/* ------------------------------------------------------------------------- */ + +void TEScreen::clearSelection() +{ + sel_BR = -1; + sel_TL = -1; + sel_begin = -1; +} + +void TEScreen::setSelBeginXY(const int x, const int y) +{ + sel_begin = loc(x,y+histCursor) ; + sel_BR = sel_begin; + sel_TL = sel_begin; +} + +void TEScreen::setSelExtentXY(const int x, const int y) +{ + if (sel_begin == -1) return; + int l = loc(x,y + histCursor); + + if (l < sel_begin) + { + sel_TL = l; + sel_BR = sel_begin; + } + else + { + /* FIXME, HACK to correct for x too far to the right... */ + if (( x == columns )|| (x == 0)) l--; + + sel_TL = sel_begin; + sel_BR = l; + } +} + +QString TEScreen::getSelText(const BOOL preserve_line_breaks) +{ + if (sel_begin == -1) + return QString::null; // Selection got clear while selecting. + + int *m; // buffer to fill. + int s, d; // source index, dest. index. + int hist_BR = loc(0, hist.getLines()); + int hY = sel_TL / columns; + int hX = sel_TL % columns; + int eol; // end of line + + s = sel_TL; // tracks copy in source. + + // allocate buffer for maximum + // possible size... + d = (sel_BR - sel_TL) / columns + 1; + m = new int[d * (columns + 1) + 2]; + d = 0; + + while (s <= sel_BR) + { + if (s < hist_BR) + { // get lines from hist.history + // buffer. + eol = hist.getLineLen(hY); + + if ((hY == (sel_BR / columns)) && + (eol >= (sel_BR % columns))) + { + eol = sel_BR % columns + 1; + } + + while (hX < eol) + { + m[d++] = hist.getCell(hY, hX++).c; + s++; + } + + if (s <= sel_BR) + { + // The line break handling + // It's different from the screen + // image case! + if (eol % columns == 0) + { + // That's either a completely filled + // line or an empty line + if (eol == 0) + { + m[d++] = '\n'; + } + else + { + // We have a full line. + // FIXME: How can we handle newlines + // at this position?! + } + } + else if ((eol + 1) % columns == 0) + { + // FIXME: We don't know if this was a + // space at the last position or a + // short line!! + m[d++] = ' '; + } + else + { + // We have a short line here. Put a + // newline or a space into the + // buffer. + m[d++] = preserve_line_breaks ? '\n' : ' '; + } + } + + hY++; + hX = 0; + s = hY * columns; + } + else + { // or from screen image. + eol = (s / columns + 1) * columns - 1; + + if (eol < sel_BR) + { + while ((eol > s) && + isspace(image[eol - hist_BR].c)) + { + eol--; + } + } + else + { + eol = sel_BR; + } + + while (s <= eol) + { + m[d++] = image[s++ - hist_BR].c; + } + + if (eol < sel_BR) + { + // eol processing see below ... + if ((eol + 1) % columns == 0) + { + if (image[eol - hist_BR].c == ' ') + { + m[d++] = ' '; + } + } + else + { + m[d++] = ((preserve_line_breaks || + ((eol % columns) == 0)) ? + '\n' : ' '); + } + } + + s = (eol / columns + 1) * columns; + } + } + + QChar* qc = new QChar[d]; + + for (int i = 0; i < d; i++) + { + qc[i] = m[i]; + } + + QString res(qc, d); + + delete m; + delete qc; + + return res; +} +/* above ... end of line processing for selection -- psilva +cases: + +1) (eol+1)%columns == 0 --> the whole line is filled. + If the last char is a space, insert (preserve) space. otherwise + leave the text alone, so that words that are broken by linewrap + are preserved. + +FIXME: + * this suppresses \n for command output that is + sized to the exact column width of the screen. + +2) eol%columns == 0 --> blank line. + insert a \n unconditionally. + Do it either you would because you are in preserve_line_break mode, + or because it's an ASCII paragraph delimiter, so even when + not preserving line_breaks, you want to preserve paragraph breaks. + +3) else --> partially filled line + insert a \n in preserve line break mode, else a space + The space prevents concatenation of the last word of one + line with the first of the next. + +*/ + +void TEScreen::addHistLine() +{ + assert(hasScroll() || histCursor == 0); + + // add to hist buffer + // we have to take care about scrolling, too... + + if (hasScroll()) + { ca dft; + + int end = columns-1; + while (end >= 0 && image[end] == dft) + end -= 1; + + hist.addCells(image,end+1); + hist.addLine(); + + // adjust history cursor + histCursor += (hist.getLines()-1 == histCursor); + } + + if (!hasScroll()) histCursor = 0; //FIXME: a poor workaround +} + +void TEScreen::setHistCursor(int cursor) +{ + histCursor = cursor; //FIXME:rangecheck +} + +int TEScreen::getHistCursor() +{ + return histCursor; +} + +int TEScreen::getHistLines() +{ + return hist.getLines(); +} + +void TEScreen::setScroll(bool on) +{ + histCursor = 0; + clearSelection(); + hist.setScroll(on); +} + +bool TEScreen::hasScroll() +{ + return hist.hasScroll(); +} diff --git a/noncore/apps/opie-console/TEScreen.h b/noncore/apps/opie-console/TEScreen.h new file mode 100644 index 0000000..ba47ee5 --- a/dev/null +++ b/noncore/apps/opie-console/TEScreen.h @@ -0,0 +1,259 @@ +/* -------------------------------------------------------------------------- */ +/* */ +/* [te_screen.h] Screen Data Type */ +/* */ +/* -------------------------------------------------------------------------- */ +/* */ +/* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ +/* */ +/* This file is part of Konsole - an X terminal for KDE */ +/* */ +/* -------------------------------------------------------------------------- */ +/* */ +/* Ported Konsole to Qt/Embedded */ +/* */ +/* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ +/* */ +/* -------------------------------------------------------------------------- */ + +#ifndef TESCREEN_H +#define TESCREEN_H + +/*! \file +*/ + +#include "TECommon.h" +#include "TEHistory.h" + +#define MODE_Origin 0 +#define MODE_Wrap 1 +#define MODE_Insert 2 +#define MODE_Screen 3 +#define MODE_Cursor 4 +#define MODE_NewLine 5 +#define MODES_SCREEN 6 + +/*! +*/ +struct ScreenParm +{ + int mode[MODES_SCREEN]; +}; + + +class TEScreen +{ +public: + TEScreen(int lines, int columns); + ~TEScreen(); + +public: // these are all `Screen' operations + // + // VT100/2 Operations ------------------ + // + // Cursor Movement + // + void cursorUp (int n); + void cursorDown (int n); + void cursorLeft (int n); + void cursorRight (int n); + void setCursorY (int y); + void setCursorX (int x); + void setCursorYX (int y, int x); + void setMargins (int t, int b); + // + // Cursor Movement with Scrolling + // + void NewLine (); + void NextLine (); + void index (); + void reverseIndex(); + // + void Return (); + void BackSpace (); + void Tabulate (); + // + // Editing + // + void eraseChars (int n); + void deleteChars (int n); + void insertChars (int n); + void deleteLines (int n); + void insertLines (int n); + // + // ------------------------------------- + // + void clearTabStops(); + void changeTabStop(bool set); + // + void resetMode (int n); + void setMode (int n); + void saveMode (int n); + void restoreMode (int n); + // + void saveCursor (); + void restoreCursor(); + // + // ------------------------------------- + // + void clearEntireScreen(); + void clearToEndOfScreen(); + void clearToBeginOfScreen(); + // + void clearEntireLine(); + void clearToEndOfLine(); + void clearToBeginOfLine(); + // + void helpAlign (); + // + // ------------------------------------- + // + void setRendition (int rendition); + void resetRendition(int rendition); + void setForeColor (int fgcolor); + void setBackColor (int bgcolor); + // + void setDefaultRendition(); + void setForeColorToDefault(); + void setBackColorToDefault(); + // + // ------------------------------------- + // + BOOL getMode (int n); + // + // only for report cursor position + // + int getCursorX(); + int getCursorY(); + // + // ------------------------------------- + // + void clear(); + void home(); + void reset(); + // + void ShowCharacter(unsigned short c); + // + void resizeImage(int new_lines, int new_columns); + // + ca* getCookedImage(); + + /*! return the number of lines. */ + int getLines() { return lines; } + /*! return the number of columns. */ + int getColumns() { return columns; } + + /*! set the position of the history cursor. */ + void setHistCursor(int cursor); + /*! return the position of the history cursor. */ + int getHistCursor(); + + int getHistLines (); + void setScroll(bool on); + bool hasScroll(); + + // + // Selection + // + void setSelBeginXY(const int x, const int y); + void setSelExtentXY(const int x, const int y); + void clearSelection(); + QString getSelText(const BOOL preserve_line_breaks); + + void checkSelection(int from, int to); + +private: // helper + + void clearImage(int loca, int loce, char c); + void moveImage(int dst, int loca, int loce); + + void scrollUp(int from, int i); + void scrollDown(int from, int i); + + void addHistLine(); + + void initTabStops(); + + void effectiveRendition(); + void reverseRendition(ca* p); + +private: + + /* + The state of the screen is more complex as one would + expect first. The screem does really do part of the + emulation providing state informations in form of modes, + margins, tabulators, cursor etc. + + Even more unexpected are variables to save and restore + parts of the state. + */ + + // screen image ---------------- + + int lines; + int columns; + ca *image; // [lines][columns] + + // history buffer --------------- + + int histCursor; // display position relative to start of the history buffer + HistoryScroll hist; + + // cursor location + + int cuX; + int cuY; + + // cursor color and rendition info + + UINT8 cu_fg; // foreground + UINT8 cu_bg; // background + UINT8 cu_re; // rendition + + // margins ---------------- + + int tmargin; // top margin + int bmargin; // bottom margin + + // states ---------------- + + ScreenParm currParm; + + // ---------------------------- + + bool* tabstops; + + // selection ------------------- + + int sel_begin; // The first location selected. + int sel_TL; // TopLeft Location. + int sel_BR; // Bottom Right Location. + + // effective colors and rendition ------------ + + UINT8 ef_fg; // These are derived from + UINT8 ef_bg; // the cu_* variables above + UINT8 ef_re; // to speed up operation + + // + // save cursor, rendition & states ------------ + // + + // cursor location + + int sa_cuX; + int sa_cuY; + + // rendition info + + UINT8 sa_cu_re; + UINT8 sa_cu_fg; + UINT8 sa_cu_bg; + + // modes + + ScreenParm saveParm; +}; + +#endif // TESCREEN_H diff --git a/noncore/apps/opie-console/TEWidget.cpp b/noncore/apps/opie-console/TEWidget.cpp new file mode 100644 index 0000000..75c438c --- a/dev/null +++ b/noncore/apps/opie-console/TEWidget.cpp @@ -0,0 +1,1264 @@ +/* ------------------------------------------------------------------------ */ +/* */ +/* [TEWidget.C] Terminal Emulation Widget */ +/* */ +/* ------------------------------------------------------------------------ */ +/* */ +/* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ +/* */ +/* This file is part of Konsole - an X terminal for KDE */ +/* */ +/* ------------------------------------------------------------------------ */ +/* */ +/* Ported Konsole to Qt/Embedded */ +/* */ +/* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ +/* */ +/* -------------------------------------------------------------------------- */ +/*! \class TEWidget + + \brief Visible screen contents + + This class is responsible to map the `image' of a terminal emulation to the + display. All the dependency of the emulation to a specific GUI or toolkit is + localized here. Further, this widget has no knowledge about being part of an + emulation, it simply work within the terminal emulation framework by exposing + size and key events and by being ordered to show a new image. + + <ul> + <li> The internal image has the size of the widget (evtl. rounded up) + <li> The external image used in setImage can have any size. + <li> (internally) the external image is simply copied to the internal + when a setImage happens. During a resizeEvent no painting is done + a paintEvent is expected to follow anyway. + </ul> + + \sa TEScreen \sa Emulation +*/ + +/* FIXME: + - 'image' may also be used uninitialized (it isn't in fact) in resizeEvent + - 'font_a' not used in mouse events + - add destructor +*/ + +/* TODO + - evtl. be sensitive to `paletteChange' while using default colors. + - set different 'rounding' styles? I.e. have a mode to show clipped chars? +*/ + +// #include "config.h" +#include "TEWidget.h" +#include "session.h" +#include <qpe/config.h> + +#include <qapplication.h> +#include <qcursor.h> +#include <qregexp.h> +#include <qpainter.h> +#include <qclipboard.h> +#include <qstyle.h> +#include <qfile.h> +#include <qdragobject.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <signal.h> + +#include <assert.h> + +// #include "TEWidget.moc" +//#include <kapp.h> +//#include <kcursor.h> +//#include <kurl.h> +//#include <kdebug.h> +//#include <klocale.h> + +#define HERE printf("%s(%d): %s\n",__FILE__,__LINE__,__FUNCTION__) +#define HCNT(Name) // { static int cnt = 1; printf("%s(%d): %s %d\n",__FILE__,__LINE__,Name,cnt++); } + +#define loc(X,Y) ((Y)*columns+(X)) + +//FIXME: the rim should normally be 1, 0 only when running in full screen mode. +#define rimX 0 // left/right rim width +#define rimY 0 // top/bottom rim high + +#define SCRWIDTH 16 // width of the scrollbar + +#define yMouseScroll 1 +// scroll increment used when dragging selection at top/bottom of window. + +/* ------------------------------------------------------------------------- */ +/* */ +/* Colors */ +/* */ +/* ------------------------------------------------------------------------- */ + +//FIXME: the default color table is in session.C now. +// We need a way to get rid of this one, here. +static const ColorEntry base_color_table[TABLE_COLORS] = +// The following are almost IBM standard color codes, with some slight +// gamma correction for the dim colors to compensate for bright X screens. +// It contains the 8 ansiterm/xterm colors in 2 intensities. +{ + // Fixme: could add faint colors here, also. + // normal + ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 1, 0 ), // Dfore, Dback + ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0x18), 0, 0 ), // Black, Red + ColorEntry(QColor(0x18,0xB2,0x18), 0, 0 ), ColorEntry( QColor(0xB2,0x68,0x18), 0, 0 ), // Green, Yellow + ColorEntry(QColor(0x18,0x18,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), // Blue, Magenta + ColorEntry(QColor(0x18,0xB2,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 0, 0 ), // Cyan, White + // intensiv + ColorEntry(QColor(0x00,0x00,0x00), 0, 1 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 1, 0 ), + ColorEntry(QColor(0x68,0x68,0x68), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0x54), 0, 0 ), + ColorEntry(QColor(0x54,0xFF,0x54), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0x54), 0, 0 ), + ColorEntry(QColor(0x54,0x54,0xFF), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), + ColorEntry(QColor(0x54,0xFF,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 0, 0 ) +}; + +/* Note that we use ANSI color order (bgr), while IBMPC color order is (rgb) + + Code 0 1 2 3 4 5 6 7 + ----------- ------- ------- ------- ------- ------- ------- ------- ------- + ANSI (bgr) Black Red Green Yellow Blue Magenta Cyan White + IBMPC (rgb) Black Blue Green Cyan Red Magenta Yellow White +*/ + +QColor TEWidget::getDefaultBackColor() +{ + return color_table[DEFAULT_BACK_COLOR].color; +} + +const ColorEntry* TEWidget::getColorTable() const +{ + return color_table; +} + +const ColorEntry* TEWidget::getdefaultColorTable() const +{ + return base_color_table; +} + + +const QPixmap *TEWidget::backgroundPixmap() +{ + static QPixmap *bg = new QPixmap("~/qpim/main/pics/faded_bg.xpm"); + const QPixmap *pm = bg; + return pm; +} + +void TEWidget::setColorTable(const ColorEntry table[]) +{ + for (int i = 0; i < TABLE_COLORS; i++) color_table[i] = table[i]; + + const QPixmap* pm = backgroundPixmap(); + if (!pm) setBackgroundColor(color_table[DEFAULT_BACK_COLOR].color); + update(); +} + +//FIXME: add backgroundPixmapChanged. + +/* ------------------------------------------------------------------------- */ +/* */ +/* Font */ +/* */ +/* ------------------------------------------------------------------------- */ + +/* + The VT100 has 32 special graphical characters. The usual vt100 extended + xterm fonts have these at 0x00..0x1f. + + QT's iso mapping leaves 0x00..0x7f without any changes. But the graphicals + come in here as proper unicode characters. + + We treat non-iso10646 fonts as VT100 extended and do the requiered mapping + from unicode to 0x00..0x1f. The remaining translation is then left to the + QCodec. +*/ + +// assert for i in [0..31] : vt100extended(vt100_graphics[i]) == i. + +unsigned short vt100_graphics[32] = +{ // 0/8 1/9 2/10 3/11 4/12 5/13 6/14 7/15 + 0x0020, 0x25C6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, + 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, + 0xF800, 0xF801, 0x2500, 0xF803, 0xF804, 0x251c, 0x2524, 0x2534, + 0x252c, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00b7 +}; + +static QChar vt100extended(QChar c) +{ + switch (c.unicode()) + { + case 0x25c6 : return 1; + case 0x2592 : return 2; + case 0x2409 : return 3; + case 0x240c : return 4; + case 0x240d : return 5; + case 0x240a : return 6; + case 0x00b0 : return 7; + case 0x00b1 : return 8; + case 0x2424 : return 9; + case 0x240b : return 10; + case 0x2518 : return 11; + case 0x2510 : return 12; + case 0x250c : return 13; + case 0x2514 : return 14; + case 0x253c : return 15; + case 0xf800 : return 16; + case 0xf801 : return 17; + case 0x2500 : return 18; + case 0xf803 : return 19; + case 0xf804 : return 20; + case 0x251c : return 21; + case 0x2524 : return 22; + case 0x2534 : return 23; + case 0x252c : return 24; + case 0x2502 : return 25; + case 0x2264 : return 26; + case 0x2265 : return 27; + case 0x03c0 : return 28; + case 0x2260 : return 29; + case 0x00a3 : return 30; + case 0x00b7 : return 31; + } + return c; +} + +static QChar identicalMap(QChar c) +{ + return c; +} + +void TEWidget::fontChange(const QFont &) +{ + QFontMetrics fm(font()); + font_h = fm.height(); + font_w = fm.maxWidth(); + font_a = fm.ascent(); +//printf("font_h: %d\n",font_h); +//printf("font_w: %d\n",font_w); +//printf("font_a: %d\n",font_a); +//printf("charset: %s\n",QFont::encodingName(font().charSet()).ascii()); +//printf("rawname: %s\n",font().rawName().ascii()); + fontMap = +#if QT_VERSION < 300 + strcmp(QFont::encodingName(font().charSet()).ascii(),"iso10646") + ? vt100extended + : +#endif + identicalMap; + propagateSize(); + update(); +} + +void TEWidget::setVTFont(const QFont& f) +{ + QFrame::setFont(f); +} + +QFont TEWidget::getVTFont() { + return font(); +} + +void TEWidget::setFont(const QFont &) +{ + // ignore font change request if not coming from konsole itself +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Constructor / Destructor */ +/* */ +/* ------------------------------------------------------------------------- */ + +TEWidget::TEWidget(QWidget *parent, const char *name) : QFrame(parent,name) +{ +#ifndef QT_NO_CLIPBOARD + cb = QApplication::clipboard(); + QObject::connect( (QObject*)cb, SIGNAL(dataChanged()), + this, SLOT(onClearSelection()) ); +#endif + + scrollbar = new QScrollBar(this); + scrollbar->setCursor( arrowCursor ); + connect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); + + Config cfg("Konsole"); + cfg.setGroup("ScrollBar"); + switch( cfg.readNumEntry("Position",2)){ + case 0: + scrollLoc = SCRNONE; + break; + case 1: + scrollLoc = SCRLEFT; + break; + case 2: + scrollLoc = SCRRIGHT; + break; + }; + + blinkT = new QTimer(this); + connect(blinkT, SIGNAL(timeout()), this, SLOT(blinkEvent())); + // blinking = FALSE; + blinking = TRUE; + + resizing = FALSE; + actSel = 0; + image = 0; + lines = 1; + columns = 1; + font_w = 1; + font_h = 1; + font_a = 1; + word_selection_mode = FALSE; + + setMouseMarks(TRUE); + setVTFont( QFont("fixed") ); + setColorTable(base_color_table); // init color table + + qApp->installEventFilter( this ); //FIXME: see below +// KCursor::setAutoHideCursor( this, true ); + + // Init DnD //////////////////////////////////////////////////////////////// + currentSession = NULL; +// setAcceptDrops(true); // attempt +// m_drop = new QPopupMenu(this); +// m_drop->insertItem( QString("Paste"), 0); +// m_drop->insertItem( QString("cd"), 1); +// connect(m_drop, SIGNAL(activated(int)), SLOT(drop_menu_activated(int))); + + // we need focus so that the auto-hide cursor feature works + setFocus(); + setFocusPolicy( WheelFocus ); +} + +//FIXME: make proper destructor +// Here's a start (David) +TEWidget::~TEWidget() +{ + qApp->removeEventFilter( this ); + if (image) free(image); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Display Operations */ +/* */ +/* ------------------------------------------------------------------------- */ + +/*! + attributed string draw primitive +*/ + +void TEWidget::drawAttrStr(QPainter &paint, QRect rect, + QString& str, ca attr, BOOL pm, BOOL clear) +{ + if (pm && color_table[attr.b].transparent) + { + paint.setBackgroundMode( TransparentMode ); + if (clear) erase(rect); + } + else + { + if (blinking) + paint.fillRect(rect, color_table[attr.b].color); + else + { + paint.setBackgroundMode( OpaqueMode ); + paint.setBackgroundColor( color_table[attr.b].color ); + } + } + + if (color_table[attr.f].bold) + paint.setPen(QColor( 0x8F, 0x00, 0x00 )); + else + paint.setPen(color_table[attr.f].color); + + paint.drawText(rect.x(),rect.y()+font_a, str); + + if (attr.r & RE_UNDERLINE) + paint.drawLine(rect.left(), rect.y()+font_a+1, rect.right(),rect.y()+font_a+1 ); +} + +/*! + The image can only be set completely. + + The size of the new image may or may not match the size of the widget. +*/ + +void TEWidget::setImage(const ca* const newimg, int lines, int columns) +{ int y,x,len; + const QPixmap* pm = backgroundPixmap(); + QPainter paint; + setUpdatesEnabled(FALSE); + paint.begin( this ); +HCNT("setImage"); + + QPoint tL = contentsRect().topLeft(); + int tLx = tL.x(); + int tLy = tL.y(); + hasBlinker = FALSE; + + int cf = -1; // undefined + int cb = -1; // undefined + int cr = -1; // undefined + + int lins = QMIN(this->lines, QMAX(0,lines )); + int cols = QMIN(this->columns,QMAX(0,columns)); + QChar *disstrU = new QChar[cols]; + +//{ static int cnt = 0; printf("setImage %d\n",cnt++); } + for (y = 0; y < lins; y++) + { + const ca* lcl = &image[y*this->columns]; + const ca* const ext = &newimg[y*columns]; + if (!resizing) // not while resizing, we're expecting a paintEvent + for (x = 0; x < cols; x++) + { + hasBlinker |= (ext[x].r & RE_BLINK); + if (ext[x] != lcl[x]) + { + cr = ext[x].r; + cb = ext[x].b; + if (ext[x].f != cf) cf = ext[x].f; + int lln = cols - x; + disstrU[0] = fontMap(ext[x+0].c); + for (len = 1; len < lln; len++) + { + if (ext[x+len].f != cf || ext[x+len].b != cb || ext[x+len].r != cr || + ext[x+len] == lcl[x+len] ) + break; + disstrU[len] = fontMap(ext[x+len].c); + } + QString unistr(disstrU,len); + drawAttrStr(paint, + QRect(blX+tLx+font_w*x,bY+tLy+font_h*y,font_w*len,font_h), + unistr, ext[x], pm != NULL, true); + x += len - 1; + } + } + // finally, make `image' become `newimg'. + memcpy((void*)lcl,(const void*)ext,cols*sizeof(ca)); + } + drawFrame( &paint ); + paint.end(); + setUpdatesEnabled(TRUE); + if ( hasBlinker && !blinkT->isActive()) blinkT->start(1000); // 1000 ms + if (!hasBlinker && blinkT->isActive()) { blinkT->stop(); blinking = FALSE; } + delete [] disstrU; +} + +// paint Event //////////////////////////////////////////////////// + +/*! + The difference of this routine vs. the `setImage' is, + that the drawing does not include a difference analysis + between the old and the new image. Instead, the internal + image is used and the painting bound by the PaintEvent box. +*/ + +void TEWidget::paintEvent( QPaintEvent* pe ) +{ + +//{ static int cnt = 0; printf("paint %d\n",cnt++); } + const QPixmap* pm = backgroundPixmap(); + QPainter paint; + setUpdatesEnabled(FALSE); + paint.begin( this ); + paint.setBackgroundMode( TransparentMode ); +HCNT("paintEvent"); + + // Note that the actual widget size can be slightly larger + // that the image (the size is truncated towards the smaller + // number of characters in `resizeEvent'. The paint rectangle + // can thus be larger than the image, but less then the size + // of one character. + + QRect rect = pe->rect().intersect(contentsRect()); + + QPoint tL = contentsRect().topLeft(); + int tLx = tL.x(); + int tLy = tL.y(); + + int lux = QMIN(columns-1, QMAX(0,(rect.left() - tLx - blX ) / font_w)); + int luy = QMIN(lines-1, QMAX(0,(rect.top() - tLy - bY ) / font_h)); + int rlx = QMIN(columns-1, QMAX(0,(rect.right() - tLx - blX ) / font_w)); + int rly = QMIN(lines-1, QMAX(0,(rect.bottom() - tLy - bY ) / font_h)); + + /* + printf("paintEvent: %d..%d, %d..%d (%d..%d, %d..%d)\n",lux,rlx,luy,rly, + rect.left(), rect.right(), rect.top(), rect.bottom()); + */ + + // if (pm != NULL && color_table[image->b].transparent) + // erase(rect); + // BL: I have no idea why we need this, and it breaks the refresh. + + QChar *disstrU = new QChar[columns]; + for (int y = luy; y <= rly; y++) + for (int x = lux; x <= rlx; x++) + { + int len = 1; + disstrU[0] = fontMap(image[loc(x,y)].c); + int cf = image[loc(x,y)].f; + int cb = image[loc(x,y)].b; + int cr = image[loc(x,y)].r; + while (x+len <= rlx && + image[loc(x+len,y)].f == cf && + image[loc(x+len,y)].b == cb && + image[loc(x+len,y)].r == cr ) + { + disstrU[len] = fontMap(image[loc(x+len,y)].c); + len += 1; + } + QString unistr(disstrU,len); + drawAttrStr(paint, + QRect(blX+tLx+font_w*x,bY+tLy+font_h*y,font_w*len,font_h), + unistr, image[loc(x,y)], pm != NULL, false); + x += len - 1; + } + delete [] disstrU; + drawFrame( &paint ); + paint.end(); + setUpdatesEnabled(TRUE); +} + +void TEWidget::blinkEvent() +{ + blinking = !blinking; + repaint(FALSE); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Resizing */ +/* */ +/* ------------------------------------------------------------------------- */ + +void TEWidget::resizeEvent(QResizeEvent* ev) +{ +// printf("resize: %d,%d\n",ev->size().width(),ev->size().height()); + //printf("approx: %d,%d\n",ev->size().width()/font_w,ev->size().height()/font_h); + //printf("leaves: %d,%d\n",ev->size().width()%font_w,ev->size().height()%font_h); + //printf("curren: %d,%d\n",width(),height()); +HCNT("resizeEvent"); + + // see comment in `paintEvent' concerning the rounding. + //FIXME: could make a routine here; check width(),height() + assert(ev->size().width() == width()); + assert(ev->size().height() == height()); + + propagateSize(); +} + +void TEWidget::propagateSize() +{ + ca* oldimg = image; + int oldlin = lines; + int oldcol = columns; + makeImage(); + // we copy the old image to reduce flicker + int lins = QMIN(oldlin,lines); + int cols = QMIN(oldcol,columns); + if (oldimg) + { + for (int lin = 0; lin < lins; lin++) + memcpy((void*)&image[columns*lin], + (void*)&oldimg[oldcol*lin],cols*sizeof(ca)); + free(oldimg); //FIXME: try new,delete + } + else + clearImage(); + + //NOTE: control flows from the back through the chest right into the eye. + // `emu' will call back via `setImage'. + + resizing = TRUE; + emit changedImageSizeSignal(lines, columns); // expose resizeEvent + resizing = FALSE; +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Scrollbar */ +/* */ +/* ------------------------------------------------------------------------- */ + +void TEWidget::scrollChanged(int) +{ + emit changedHistoryCursor(scrollbar->value()); //expose +} + +void TEWidget::setScroll(int cursor, int slines) +{ + disconnect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); + scrollbar->setRange(0,slines); + scrollbar->setSteps(1,lines); + scrollbar->setValue(cursor); + connect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); +} + +void TEWidget::setScrollbarLocation(int loc) +{ + if (scrollLoc == loc) return; // quickly + scrollLoc = loc; + propagateSize(); + update(); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Mouse */ +/* */ +/* ------------------------------------------------------------------------- */ + +/*! + Three different operations can be performed using the mouse, and the + routines in this section serve all of them: + + 1) The press/release events are exposed to the application + 2) Marking (press and move left button) and Pasting (press middle button) + 3) The right mouse button is used from the configuration menu + + NOTE: During the marking process we attempt to keep the cursor within + the bounds of the text as being displayed by setting the mouse position + whenever the mouse has left the text area. + + Two reasons to do so: + 1) QT does not allow the `grabMouse' to confine-to the TEWidget. + Thus a `XGrapPointer' would have to be used instead. + 2) Even if so, this would not help too much, since the text area + of the TEWidget is normally not identical with it's bounds. + + The disadvantage of the current handling is, that the mouse can visibly + leave the bounds of the widget and is then moved back. Because of the + current construction, and the reasons mentioned above, we cannot do better + without changing the overall construction. +*/ + +/*! +*/ + +void TEWidget::mousePressEvent(QMouseEvent* ev) +{ +//printf("press [%d,%d] %d\n",ev->x()/font_w,ev->y()/font_h,ev->button()); + if ( !contentsRect().contains(ev->pos()) ) return; + QPoint tL = contentsRect().topLeft(); + int tLx = tL.x(); + int tLy = tL.y(); + + word_selection_mode = FALSE; + +//printf("press top left [%d,%d] by=%d\n",tLx,tLy, bY); + if ( ev->button() == LeftButton) + { + QPoint pos = QPoint((ev->x()-tLx-blX)/font_w,(ev->y()-tLy-bY)/font_h); + + if ( ev->state() & ControlButton ) preserve_line_breaks = FALSE ; + + if (mouse_marks || (ev->state() & ShiftButton)) + { + emit clearSelectionSignal(); + iPntSel = pntSel = pos; + actSel = 1; // left mouse button pressed but nothing selected yet. + grabMouse( /*crossCursor*/ ); // handle with care! + } + else + { + emit mouseSignal( 0, pos.x() + 1, pos.y() + 1 ); // left button + } + } + if ( ev->button() == MidButton ) + { + emitSelection(); + } + if ( ev->button() == RightButton ) // Configure + { + emit configureRequest( this, ev->state()&(ShiftButton|ControlButton), ev->x(), ev->y() ); + } +} + +void TEWidget::mouseMoveEvent(QMouseEvent* ev) +{ + // for auto-hiding the cursor, we need mouseTracking + if (ev->state() == NoButton ) return; + + if (actSel == 0) return; + + // don't extend selection while pasting + if (ev->state() & MidButton) return; + + //if ( !contentsRect().contains(ev->pos()) ) return; + QPoint tL = contentsRect().topLeft(); + int tLx = tL.x(); + int tLy = tL.y(); + int scroll = scrollbar->value(); + + // we're in the process of moving the mouse with the left button pressed + // the mouse cursor will kept catched within the bounds of the text in + // this widget. + + // Adjust position within text area bounds. See FIXME above. + QPoint pos = ev->pos(); + if ( pos.x() < tLx+blX ) pos.setX( tLx+blX ); + if ( pos.x() > tLx+blX+columns*font_w-1 ) pos.setX( tLx+blX+columns*font_w ); + if ( pos.y() < tLy+bY ) pos.setY( tLy+bY ); + if ( pos.y() > tLy+bY+lines*font_h-1 ) pos.setY( tLy+bY+lines*font_h-1 ); + // check if we produce a mouse move event by this + if ( pos != ev->pos() ) cursor().setPos(mapToGlobal(pos)); + + if ( pos.y() == tLy+bY+lines*font_h-1 ) + { + scrollbar->setValue(scrollbar->value()+yMouseScroll); // scrollforward + } + if ( pos.y() == tLy+bY ) + { + scrollbar->setValue(scrollbar->value()-yMouseScroll); // scrollback + } + + QPoint here = QPoint((pos.x()-tLx-blX)/font_w,(pos.y()-tLy-bY)/font_h); + QPoint ohere; + bool swapping = FALSE; + + if ( word_selection_mode ) + { + // Extend to word boundaries + int i; + int selClass; + + bool left_not_right = ( here.y() < iPntSel.y() || + here.y() == iPntSel.y() && here.x() < iPntSel.x() ); + bool old_left_not_right = ( pntSel.y() < iPntSel.y() || + pntSel.y() == iPntSel.y() && pntSel.x() < iPntSel.x() ); + swapping = left_not_right != old_left_not_right; + + // Find left (left_not_right ? from here : from start) + QPoint left = left_not_right ? here : iPntSel; + i = loc(left.x(),left.y()); + selClass = charClass(image[i].c); + while ( left.x() > 0 && charClass(image[i-1].c) == selClass ) + { i--; left.rx()--; } + + // Find left (left_not_right ? from start : from here) + QPoint right = left_not_right ? iPntSel : here; + i = loc(right.x(),right.y()); + selClass = charClass(image[i].c); + while ( right.x() < columns-1 && charClass(image[i+1].c) == selClass ) + { i++; right.rx()++; } + + // Pick which is start (ohere) and which is extension (here) + if ( left_not_right ) + { + here = left; ohere = right; + } + else + { + here = right; ohere = left; + } + } + + if (here == pntSel && scroll == scrollbar->value()) return; // not moved + + if ( word_selection_mode ) { + if ( actSel < 2 || swapping ) { + emit beginSelectionSignal( ohere.x(), ohere.y() ); + } + } else if ( actSel < 2 ) { + emit beginSelectionSignal( pntSel.x(), pntSel.y() ); + } + + actSel = 2; // within selection + pntSel = here; + emit extendSelectionSignal( here.x(), here.y() ); +} + +void TEWidget::mouseReleaseEvent(QMouseEvent* ev) +{ +//printf("release [%d,%d] %d\n",ev->x()/font_w,ev->y()/font_h,ev->button()); + if ( ev->button() == LeftButton) + { + if ( actSel > 1 ) emit endSelectionSignal(preserve_line_breaks); + preserve_line_breaks = TRUE; + actSel = 0; + + //FIXME: emits a release event even if the mouse is + // outside the range. The procedure used in `mouseMoveEvent' + // applies here, too. + + QPoint tL = contentsRect().topLeft(); + int tLx = tL.x(); + int tLy = tL.y(); + + if (!mouse_marks && !(ev->state() & ShiftButton)) + emit mouseSignal( 3, // release + (ev->x()-tLx-blX)/font_w + 1, + (ev->y()-tLy-bY)/font_h + 1 ); + releaseMouse(); + } +} + +void TEWidget::mouseDoubleClickEvent(QMouseEvent* ev) +{ + if ( ev->button() != LeftButton) return; + + QPoint tL = contentsRect().topLeft(); + int tLx = tL.x(); + int tLy = tL.y(); + QPoint pos = QPoint((ev->x()-tLx-blX)/font_w,(ev->y()-tLy-bY)/font_h); + + // pass on double click as two clicks. + if (!mouse_marks && !(ev->state() & ShiftButton)) + { + emit mouseSignal( 0, pos.x()+1, pos.y()+1 ); // left button + emit mouseSignal( 3, pos.x()+1, pos.y()+1 ); // release + emit mouseSignal( 0, pos.x()+1, pos.y()+1 ); // left button + return; + } + + + emit clearSelectionSignal(); + QPoint bgnSel = pos; + QPoint endSel = QPoint((ev->x()-tLx-blX)/font_w,(ev->y()-tLy-bY)/font_h); + int i = loc(bgnSel.x(),bgnSel.y()); + iPntSel = bgnSel; + + word_selection_mode = TRUE; + + // find word boundaries... + int selClass = charClass(image[i].c); + { + // set the start... + int x = bgnSel.x(); + while ( x > 0 && charClass(image[i-1].c) == selClass ) + { i--; x--; } + bgnSel.setX(x); + emit beginSelectionSignal( bgnSel.x(), bgnSel.y() ); + + // set the end... + i = loc( endSel.x(), endSel.y() ); + x = endSel.x(); + while( x < columns-1 && charClass(image[i+1].c) == selClass ) + { i++; x++ ; } + endSel.setX(x); + actSel = 2; // within selection + emit extendSelectionSignal( endSel.x(), endSel.y() ); + emit endSelectionSignal(preserve_line_breaks); + preserve_line_breaks = TRUE; + } +} + +void TEWidget::focusInEvent( QFocusEvent * ) +{ + + // do nothing, to prevent repainting +} + + +void TEWidget::focusOutEvent( QFocusEvent * ) +{ + // do nothing, to prevent repainting +} + +bool TEWidget::focusNextPrevChild( bool next ) +{ + if (next) + return false; // This disables changing the active part in konqueror + // when pressing Tab + return QFrame::focusNextPrevChild( next ); +} + + +int TEWidget::charClass(char ch) const +{ + // This might seem like overkill, but imagine if ch was a Unicode + // character (Qt 2.0 QChar) - it might then be sensible to separate + // the different language ranges, etc. + + if ( isspace(ch) ) return ' '; + + static const char *word_characters = ":@-./_~"; + if ( isalnum(ch) || strchr(word_characters, ch) ) + return 'a'; + + // Everything else is weird + return 1; +} + +void TEWidget::setMouseMarks(bool on) +{ + mouse_marks = on; + setCursor( mouse_marks ? ibeamCursor : arrowCursor ); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Clipboard */ +/* */ +/* ------------------------------------------------------------------------- */ + +#undef KeyPress + +void TEWidget::emitSelection() +// Paste Clipboard by simulating keypress events +{ +#ifndef QT_NO_CLIPBOARD + QString text = QApplication::clipboard()->text(); + if ( ! text.isNull() ) + { + text.replace(QRegExp("\n"), "\r"); + QKeyEvent e(QEvent::KeyPress, 0, -1, 0, text); + emit keyPressedSignal(&e); // expose as a big fat keypress event + emit clearSelectionSignal(); + } +#endif +} + +void TEWidget::emitText(QString text) +{ + QKeyEvent e(QEvent::KeyPress, 0, -1, 0, text); + emit keyPressedSignal(&e); // expose as a big fat keypress event +} + +void TEWidget::pasteClipboard( ) +{ + emitSelection(); +} + +void TEWidget::setSelection(const QString& t) +{ +#ifndef QT_NO_CLIPBOARD + // Disconnect signal while WE set the clipboard + QObject *cb = QApplication::clipboard(); + QObject::disconnect( cb, SIGNAL(dataChanged()), + this, SLOT(onClearSelection()) ); + + QApplication::clipboard()->setText(t); + + QObject::connect( cb, SIGNAL(dataChanged()), + this, SLOT(onClearSelection()) ); +#endif +} + +void TEWidget::onClearSelection() +{ + emit clearSelectionSignal(); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Keyboard */ +/* */ +/* ------------------------------------------------------------------------- */ + +//FIXME: an `eventFilter' has been installed instead of a `keyPressEvent' +// due to a bug in `QT' or the ignorance of the author to prevent +// repaint events being emitted to the screen whenever one leaves +// or reenters the screen to/from another application. +// +// Troll says one needs to change focusInEvent() and focusOutEvent(), +// which would also let you have an in-focus cursor and an out-focus +// cursor like xterm does. + +// for the auto-hide cursor feature, I added empty focusInEvent() and +// focusOutEvent() so that update() isn't called. +// For auto-hide, we need to get keypress-events, but we only get them when +// we have focus. + +void TEWidget::doScroll(int lines) +{ + scrollbar->setValue(scrollbar->value()+lines); +} + +bool TEWidget::eventFilter( QObject *obj, QEvent *e ) +{ + if ( (e->type() == QEvent::Accel || + e->type() == QEvent::AccelAvailable ) && qApp->focusWidget() == this ) { + static_cast<QKeyEvent *>( e )->ignore(); + return true; + } + if ( obj != this /* when embedded */ && obj != parent() /* when standalone */ ) + return FALSE; // not us + if ( e->type() == QEvent::Wheel) { + QApplication::sendEvent(scrollbar, e); + } + +#ifdef FAKE_CTRL_AND_ALT + static bool control = FALSE; + static bool alt = FALSE; +// qDebug(" Has a keyboard with no CTRL and ALT keys, but we fake it:"); + bool dele=FALSE; + if ( e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease ) { + QKeyEvent* ke = (QKeyEvent*)e; + bool keydown = e->type() == QEvent::KeyPress || ke->isAutoRepeat(); + switch (ke->key()) { + case Key_F9: // let this be "Control" + control = keydown; + e = new QKeyEvent(QEvent::KeyPress, Key_Control, 0, ke->state()); + dele=TRUE; + break; + case Key_F13: // let this be "Alt" + alt = keydown; + e = new QKeyEvent(QEvent::KeyPress, Key_Alt, 0, ke->state()); + dele=TRUE; + break; + default: + if ( control ) { + int a = toupper(ke->ascii())-64; + if ( a >= 0 && a < ' ' ) { + e = new QKeyEvent(e->type(), ke->key(), + a, ke->state()|ControlButton, QChar(a,0)); + dele=TRUE; + } + } + if ( alt ) { + e = new QKeyEvent(e->type(), ke->key(), + ke->ascii(), ke->state()|AltButton, ke->text()); + dele=TRUE; + } + } + } +#endif + + if ( e->type() == QEvent::KeyPress ) { + QKeyEvent* ke = (QKeyEvent*)e; + actSel=0; // Key stroke implies a screen update, so TEWidget won't + // know where the current selection is. + +// qDebug("key pressed is 0x%x",ke->key()); + + if( ke->state() == ShiftButton && ke->key() == Key_Tab) { //lets hardcode this sucker + +// qDebug("key pressed 2 is 0x%x",ke->key()); + emitText("\\"); // expose + } else + emit keyPressedSignal(ke); // expose + ke->accept(); +#ifdef FAKE_CTRL_AND_ALT + if ( dele ) delete e; +#endif + return true; // stop the event + } + if ( e->type() == QEvent::Enter ) { + QObject::disconnect( (QObject*)cb, SIGNAL(dataChanged()), + this, SLOT(onClearSelection()) ); + } + if ( e->type() == QEvent::Leave ) { + QObject::connect( (QObject*)cb, SIGNAL(dataChanged()), + this, SLOT(onClearSelection()) ); + } + return QFrame::eventFilter( obj, e ); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Frame */ +/* */ +/* ------------------------------------------------------------------------- */ + +void TEWidget::frameChanged() +{ + propagateSize(); + update(); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Sound */ +/* */ +/* ------------------------------------------------------------------------- */ + +void TEWidget::Bell() +{ + QApplication::beep(); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Auxiluary */ +/* */ +/* ------------------------------------------------------------------------- */ + +void TEWidget::clearImage() +// initialize the image +// for internal use only +{ + for (int y = 0; y < lines; y++) + for (int x = 0; x < columns; x++) + { + image[loc(x,y)].c = 0xff; //' '; + image[loc(x,y)].f = 0xff; //DEFAULT_FORE_COLOR; + image[loc(x,y)].b = 0xff; //DEFAULT_BACK_COLOR; + image[loc(x,y)].r = 0xff; //DEFAULT_RENDITION; + } +} + +// Create Image /////////////////////////////////////////////////////// + +void TEWidget::calcGeometry() +{ + //FIXME: set rimX == rimY == 0 when running in full screen mode. + + scrollbar->resize(QApplication::style().scrollBarExtent().width(), + contentsRect().height()); + switch(scrollLoc) + { + case SCRNONE : + columns = ( contentsRect().width() - 2 * rimX ) / font_w; + blX = (contentsRect().width() - (columns*font_w) ) / 2; + brX = blX; + scrollbar->hide(); + break; + case SCRLEFT : + columns = ( contentsRect().width() - 2 * rimX - scrollbar->width()) / font_w; + brX = (contentsRect().width() - (columns*font_w) - scrollbar->width() ) / 2; + blX = brX + scrollbar->width(); + scrollbar->move(contentsRect().topLeft()); + scrollbar->show(); + break; + case SCRRIGHT: + columns = ( contentsRect().width() - 2 * rimX - scrollbar->width()) / font_w; + blX = (contentsRect().width() - (columns*font_w) - scrollbar->width() ) / 2; + brX = blX; + scrollbar->move(contentsRect().topRight() - QPoint(scrollbar->width()-1,0)); + scrollbar->show(); + break; + } + //FIXME: support 'rounding' styles + lines = ( contentsRect().height() - 2 * rimY ) / font_h; + bY = (contentsRect().height() - (lines *font_h)) / 2; +} + +void TEWidget::makeImage() +//FIXME: rename 'calcGeometry? +{ + calcGeometry(); + image = (ca*) malloc(lines*columns*sizeof(ca)); + clearImage(); +} + +// calculate the needed size +QSize TEWidget::calcSize(int cols, int lins) const +{ + int frw = width() - contentsRect().width(); + int frh = height() - contentsRect().height(); + int scw = (scrollLoc==SCRNONE?0:scrollbar->width()); + return QSize( font_w*cols + 2*rimX + frw + scw, font_h*lins + 2*rimY + frh ); +} + +QSize TEWidget::sizeHint() const +{ + return size(); +} + +void TEWidget::styleChange(QStyle &) +{ + propagateSize(); +} + +#ifndef QT_NO_DRAGANDDROP + +/* --------------------------------------------------------------------- */ +/* */ +/* Drag & Drop */ +/* */ +/* --------------------------------------------------------------------- */ + + +void TEWidget::dragEnterEvent(QDragEnterEvent* e) +{ + e->accept(QTextDrag::canDecode(e) || + QUriDrag::canDecode(e)); +} + +void TEWidget::dropEvent(QDropEvent* event) +{ + // The current behaviour when url(s) are dropped is + // * if there is only ONE url and if it's a LOCAL one, ask for paste or cd + // * in all other cases, just paste + // (for non-local ones, or for a list of URLs, 'cd' is nonsense) + QStrList strlist; + int file_count = 0; + dropText = ""; + bool bPopup = true; + + if(QUriDrag::decode(event, strlist)) { + if (strlist.count()) { + for(const char* p = strlist.first(); p; p = strlist.next()) { + if(file_count++ > 0) { + dropText += " "; + bPopup = false; // more than one file, don't popup + } + +/* + KURL url(p); + if (url.isLocalFile()) { + dropText += url.path(); // local URL : remove protocol + } + else { + dropText += url.prettyURL(); + bPopup = false; // a non-local file, don't popup + } +*/ + + } + + if (bPopup) + // m_drop->popup(pos() + event->pos()); + m_drop->popup(mapToGlobal(event->pos())); + else + { + if (currentSession) { + currentSession->getEmulation()->sendString(dropText.local8Bit()); + } +// kdDebug() << "Drop:" << dropText.local8Bit() << "\n"; + } + } + } + else if(QTextDrag::decode(event, dropText)) { +// kdDebug() << "Drop:" << dropText.local8Bit() << "\n"; + if (currentSession) { + currentSession->getEmulation()->sendString(dropText.local8Bit()); + } + // Paste it + } +} +#endif + + +void TEWidget::drop_menu_activated(int item) +{ +#ifndef QT_NO_DRAGANDDROP + switch (item) + { + case 0: // paste + currentSession->getEmulation()->sendString(dropText.local8Bit()); +// KWM::activate((Window)this->winId()); + break; + case 1: // cd ... + currentSession->getEmulation()->sendString("cd "); + struct stat statbuf; + if ( ::stat( QFile::encodeName( dropText ), &statbuf ) == 0 ) + { + if ( !S_ISDIR(statbuf.st_mode) ) + { +/* + KURL url; + url.setPath( dropText ); + dropText = url.directory( true, false ); // remove filename +*/ + } + } + dropText.replace(QRegExp(" "), "\\ "); // escape spaces + currentSession->getEmulation()->sendString(dropText.local8Bit()); + currentSession->getEmulation()->sendString("\n"); +// KWM::activate((Window)this->winId()); + break; + } +#endif +} + diff --git a/noncore/apps/opie-console/TEWidget.h b/noncore/apps/opie-console/TEWidget.h new file mode 100644 index 0000000..40e1aea --- a/dev/null +++ b/noncore/apps/opie-console/TEWidget.h @@ -0,0 +1,202 @@ +/* ----------------------------------------------------------------------- */ +/* */ +/* [te_widget.h] Terminal Emulation Widget */ +/* */ +/* ----------------------------------------------------------------------- */ +/* */ +/* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ +/* */ +/* This file is part of Konsole - an X terminal for KDE */ +/* */ +/* ----------------------------------------------------------------------- */ +/* */ +/* Ported Konsole to Qt/Embedded */ +/* */ +/* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ +/* */ +/* -------------------------------------------------------------------------- */ +#ifndef TE_WIDGET_H +#define TE_WIDGET_H + +#include <qwidget.h> +#include <qlabel.h> +#include <qtimer.h> +#include <qcolor.h> +#include <qkeycode.h> +#include <qscrollbar.h> + +#include <qpopupmenu.h> + +#include "TECommon.h" + +extern unsigned short vt100_graphics[32]; + +class TESession; + +// class Konsole; + +class TEWidget : public QFrame +// a widget representing attributed text +{ Q_OBJECT + +// friend class Konsole; + +public: + + TEWidget(QWidget *parent=0, const char *name=0); + virtual ~TEWidget(); + +public: + + QColor getDefaultBackColor(); + + const ColorEntry* getColorTable() const; + const ColorEntry* getdefaultColorTable() const; + void setColorTable(const ColorEntry table[]); + + void setScrollbarLocation(int loc); + enum { SCRNONE=0, SCRLEFT=1, SCRRIGHT=2 }; + + void setScroll(int cursor, int lines); + void doScroll(int lines); + + void emitSelection(); + +public: + + void setImage(const ca* const newimg, int lines, int columns); + + int Lines() { return lines; } + int Columns() { return columns; } + + void calcGeometry(); + void propagateSize(); + QSize calcSize(int cols, int lins) const; + + QSize sizeHint() const; + +public: + + void Bell(); + void emitText(QString text); + void pasteClipboard(); + +signals: + + void keyPressedSignal(QKeyEvent *e); + void mouseSignal(int cb, int cx, int cy); + void changedImageSizeSignal(int lines, int columns); + void changedHistoryCursor(int value); + void configureRequest( TEWidget*, int state, int x, int y ); + + void clearSelectionSignal(); + void beginSelectionSignal( const int x, const int y ); + void extendSelectionSignal( const int x, const int y ); + void endSelectionSignal(const BOOL preserve_line_breaks); + + +protected: + + virtual void styleChange( QStyle& ); + + bool eventFilter( QObject *, QEvent * ); + + void drawAttrStr(QPainter &paint, QRect rect, + QString& str, ca attr, BOOL pm, BOOL clear); + void paintEvent( QPaintEvent * ); + + void resizeEvent(QResizeEvent*); + + void fontChange(const QFont &font); + void frameChanged(); + + void mouseDoubleClickEvent(QMouseEvent* ev); + void mousePressEvent( QMouseEvent* ); + void mouseReleaseEvent( QMouseEvent* ); + void mouseMoveEvent( QMouseEvent* ); + + void focusInEvent( QFocusEvent * ); + void focusOutEvent( QFocusEvent * ); + bool focusNextPrevChild( bool next ); + +#ifndef QT_NO_DRAGANDDROP + // Dnd + void dragEnterEvent(QDragEnterEvent* event); + void dropEvent(QDropEvent* event); +#endif + + virtual int charClass(char) const; + + void clearImage(); + +public: + const QPixmap *backgroundPixmap(); + + void setSelection(const QString &t); + + virtual void setFont(const QFont &); + void setVTFont(const QFont &); + QFont getVTFont(); + + void setMouseMarks(bool on); + +public slots: + + void onClearSelection(); + +protected slots: + + void scrollChanged(int value); + void blinkEvent(); + +private: + + QChar (*fontMap)(QChar); // possible vt100 font extention + + bool fixed_font; // has fixed pitch + int font_h; // height + int font_w; // width + int font_a; // ascend + + int blX; // actual offset (left) + int brX; // actual offset (right) + int bY; // actual offset + + int lines; + int columns; + ca *image; // [lines][columns] + + ColorEntry color_table[TABLE_COLORS]; + + BOOL resizing; + bool mouse_marks; + + void makeImage(); + + QPoint iPntSel; // initial selection point + QPoint pntSel; // current selection point + int actSel; // selection state + BOOL word_selection_mode; + BOOL preserve_line_breaks; + + QClipboard* cb; + QScrollBar* scrollbar; + int scrollLoc; + +//#define SCRNONE 0 +//#define SCRLEFT 1 +//#define SCRRIGHT 2 + + BOOL blinking; // hide text in paintEvent + BOOL hasBlinker; // has characters to blink + QTimer* blinkT; // active when hasBlinker + QPopupMenu* m_drop; + QString dropText; + public: + // current session in this widget + TESession *currentSession; +private slots: + void drop_menu_activated(int item); +}; + +#endif // TE_WIDGET_H diff --git a/noncore/apps/opie-console/TEmuVt102.cpp b/noncore/apps/opie-console/TEmuVt102.cpp new file mode 100644 index 0000000..275c18d --- a/dev/null +++ b/noncore/apps/opie-console/TEmuVt102.cpp @@ -0,0 +1,1020 @@ +/* ------------------------------------------------------------------------- */ +/* */ +/* [TEmuVt102.C] VT102 Terminal Emulation */ +/* */ +/* ------------------------------------------------------------------------- */ +/* */ +/* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ +/* */ +/* This file is part of Konsole - an X terminal for KDE */ +/* */ +/* ------------------------------------------------------------------------- */ +/* */ +/* Ported Konsole to Qt/Embedded */ +/* */ +/* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ +/* */ +/* -------------------------------------------------------------------------- */ + +/*! \class TEmuVt102 + + \brief Actual Emulation for Konsole + + \sa TEWidget \sa TEScreen +*/ + +#include "TEmuVt102.h" +#include "TEWidget.h" +#include "TEScreen.h" +#include "keytrans.h" + +#include <stdio.h> +#include <unistd.h> +#include <qkeycode.h> +#include <qtextcodec.h> + + +/* VT102 Terminal Emulation + + This class puts together the screens, the pty and the widget to a + complete terminal emulation. Beside combining it's componentes, it + handles the emulations's protocol. + + This module consists of the following sections: + + - Constructor/Destructor + - Incoming Bytes Event pipeline + - Outgoing Bytes + - Mouse Events + - Keyboard Events + - Modes and Charset State + - Diagnostics +*/ + + +/* ------------------------------------------------------------------------- */ +/* */ +/* Constructor / Destructor */ +/* */ +/* ------------------------------------------------------------------------- */ + +/* + Nothing really intesting happens here. +*/ + +/*! +*/ + +TEmuVt102::TEmuVt102(TEWidget* gui) : TEmulation(gui) +{ + QObject::connect(gui,SIGNAL(mouseSignal(int,int,int)), + this,SLOT(onMouse(int,int,int))); + initTokenizer(); + reset(); +} + +/*! +*/ + +TEmuVt102::~TEmuVt102() +{ +} + +/*! +*/ + +void TEmuVt102::reset() +{ + resetToken(); + resetModes(); + resetCharset(0); screen[0]->reset(); + resetCharset(1); screen[0]->reset(); + setCodec(0); + setKeytrans("linux.keytab"); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Processing the incoming byte stream */ +/* */ +/* ------------------------------------------------------------------------- */ + +/* Incoming Bytes Event pipeline + + This section deals with decoding the incoming character stream. + Decoding means here, that the stream is first seperated into `tokens' + which are then mapped to a `meaning' provided as operations by the + `TEScreen' class or by the emulation class itself. + + The pipeline proceeds as follows: + + - Tokenizing the ESC codes (onRcvChar) + - VT100 code page translation of plain characters (applyCharset) + - Interpretation of ESC codes (tau) + + The escape codes and their meaning are described in the + technical reference of this program. +*/ + +// Tokens ------------------------------------------------------------------ -- + +/* + Since the tokens are the central notion if this section, we've put them + in front. They provide the syntactical elements used to represent the + terminals operations as byte sequences. + + They are encodes here into a single machine word, so that we can later + switch over them easily. Depending on the token itself, additional + argument variables are filled with parameter values. + + The tokens are defined below: + + - CHR - Printable characters (32..255 but DEL (=127)) + - CTL - Control characters (0..31 but ESC (= 27), DEL) + - ESC - Escape codes of the form <ESC><CHR but `[]()+*#'> + - ESC_DE - Escape codes of the form <ESC><any of `()+*#%'> C + - CSI_PN - Escape codes of the form <ESC>'[' {Pn} ';' {Pn} C + - CSI_PS - Escape codes of the form <ESC>'[' {Pn} ';' ... C + - CSI_PR - Escape codes of the form <ESC>'[' '?' {Pn} ';' ... C + - VT52 - VT52 escape codes + - <ESC><Chr> + - <ESC>'Y'{Pc}{Pc} + - XTE_HA - Xterm hacks <ESC>`]' {Pn} `;' {Text} <BEL> + note that this is handled differently + + The last two forms allow list of arguments. Since the elements of + the lists are treated individually the same way, they are passed + as individual tokens to the interpretation. Further, because the + meaning of the parameters are names (althought represented as numbers), + they are includes within the token ('N'). + +*/ + +#define TY_CONSTR(T,A,N) ( ((((int)N) & 0xffff) << 16) | ((((int)A) & 0xff) << 8) | (((int)T) & 0xff) ) + +#define TY_CHR___( ) TY_CONSTR(0,0,0) +#define TY_CTL___(A ) TY_CONSTR(1,A,0) +#define TY_ESC___(A ) TY_CONSTR(2,A,0) +#define TY_ESC_CS(A,B) TY_CONSTR(3,A,B) +#define TY_ESC_DE(A ) TY_CONSTR(4,A,0) +#define TY_CSI_PS(A,N) TY_CONSTR(5,A,N) +#define TY_CSI_PN(A ) TY_CONSTR(6,A,0) +#define TY_CSI_PR(A,N) TY_CONSTR(7,A,N) + +#define TY_VT52__(A ) TY_CONSTR(8,A,0) + +// Tokenizer --------------------------------------------------------------- -- + +/* The tokenizers state + + The state is represented by the buffer (pbuf, ppos), + and accompanied by decoded arguments kept in (argv,argc). + Note that they are kept internal in the tokenizer. +*/ + +void TEmuVt102::resetToken() +{ + ppos = 0; argc = 0; argv[0] = 0; argv[1] = 0; +} + +void TEmuVt102::addDigit(int dig) +{ + argv[argc] = 10*argv[argc] + dig; +} + +void TEmuVt102::addArgument() +{ + argc = QMIN(argc+1,MAXARGS-1); + argv[argc] = 0; +} + +void TEmuVt102::pushToToken(int cc) +{ + pbuf[ppos] = cc; + ppos = QMIN(ppos+1,MAXPBUF-1); +} + +// Character Classes used while decoding + +#define CTL 1 +#define CHR 2 +#define CPN 4 +#define DIG 8 +#define SCS 16 +#define GRP 32 + +void TEmuVt102::initTokenizer() +{ int i; UINT8* s; + for(i = 0; i < 256; i++) tbl[ i] = 0; + for(i = 0; i < 32; i++) tbl[ i] |= CTL; + for(i = 32; i < 256; i++) tbl[ i] |= CHR; + for(s = (UINT8*)"@ABCDGHLMPXcdfry"; *s; s++) tbl[*s] |= CPN; + for(s = (UINT8*)"0123456789" ; *s; s++) tbl[*s] |= DIG; + for(s = (UINT8*)"()+*%" ; *s; s++) tbl[*s] |= SCS; + for(s = (UINT8*)"()+*#[]%" ; *s; s++) tbl[*s] |= GRP; + resetToken(); +} + +/* Ok, here comes the nasty part of the decoder. + + Instead of keeping an explicit state, we deduce it from the + token scanned so far. It is then immediately combined with + the current character to form a scanning decision. + + This is done by the following defines. + + - P is the length of the token scanned so far. + - L (often P-1) is the position on which contents we base a decision. + - C is a character or a group of characters (taken from 'tbl'). + + Note that they need to applied in proper order. +*/ + +#define lec(P,L,C) (p == (P) && s[(L)] == (C)) +#define lun( ) (p == 1 && cc >= 32 ) +#define les(P,L,C) (p == (P) && s[L] < 256 && (tbl[s[(L)]] & (C)) == (C)) +#define eec(C) (p >= 3 && cc == (C)) +#define ees(C) (p >= 3 && cc < 256 && (tbl[ cc ] & (C)) == (C)) +#define eps(C) (p >= 3 && s[2] != '?' && cc < 256 && (tbl[ cc ] & (C)) == (C)) +#define epp( ) (p >= 3 && s[2] == '?' ) +#define egt( ) (p == 3 && s[2] == '>' ) +#define Xpe (ppos>=2 && pbuf[1] == ']' ) +#define Xte (Xpe && cc == 7 ) +#define ces(C) ( cc < 256 && (tbl[ cc ] & (C)) == (C) && !Xte) + +#define ESC 27 +#define CNTL(c) ((c)-'@') + +// process an incoming unicode character + +void TEmuVt102::onRcvChar(int cc) +{ int i; + + if (cc == 127) return; //VT100: ignore. + + if (ces( CTL)) + { // DEC HACK ALERT! Control Characters are allowed *within* esc sequences in VT100 + // This means, they do neither a resetToken nor a pushToToken. Some of them, do + // of course. Guess this originates from a weakly layered handling of the X-on + // X-off protocol, which comes really below this level. + if (cc == CNTL('X') || cc == CNTL('Z') || cc == ESC) resetToken(); //VT100: CAN or SUB + if (cc != ESC) { tau( TY_CTL___(cc+'@' ), 0, 0); return; } + } + + pushToToken(cc); // advance the state + + int* s = pbuf; + int p = ppos; + + if (getMode(MODE_Ansi)) // decide on proper action + { + if (lec(1,0,ESC)) { return; } + if (les(2,1,GRP)) { return; } + if (Xte ) { XtermHack(); resetToken(); return; } + if (Xpe ) { return; } + if (lec(3,2,'?')) { return; } + if (lec(3,2,'>')) { return; } + if (lun( )) { tau( TY_CHR___(), applyCharset(cc), 0); resetToken(); return; } + if (lec(2,0,ESC)) { tau( TY_ESC___(s[1]), 0, 0); resetToken(); return; } + if (les(3,1,SCS)) { tau( TY_ESC_CS(s[1],s[2]), 0, 0); resetToken(); return; } + if (lec(3,1,'#')) { tau( TY_ESC_DE(s[2]), 0, 0); resetToken(); return; } +// if (egt( )) { tau( TY_CSI_PG(cc ), '>', 0); resetToken(); return; } + if (eps( CPN)) { tau( TY_CSI_PN(cc), argv[0],argv[1]); resetToken(); return; } + if (ees( DIG)) { addDigit(cc-'0'); return; } + if (eec( ';')) { addArgument(); return; } + for (i=0;i<=argc;i++) + if (epp( )) tau( TY_CSI_PR(cc,argv[i]), 0, 0); else + tau( TY_CSI_PS(cc,argv[i]), 0, 0); + resetToken(); + } + else // mode VT52 + { + if (lec(1,0,ESC)) return; + if (les(1,0,CHR)) { tau( TY_CHR___( ), s[0], 0); resetToken(); return; } + if (lec(2,1,'Y')) return; + if (lec(3,1,'Y')) return; + if (p < 4) { tau( TY_VT52__(s[1] ), 0, 0); resetToken(); return; } + tau( TY_VT52__(s[1] ), s[2],s[3]); resetToken(); return; + } +} + +void TEmuVt102::XtermHack() +{ int i,arg = 0; + for (i = 2; i < ppos && '0'<=pbuf[i] && pbuf[i]<'9' ; i++) + arg = 10*arg + (pbuf[i]-'0'); + if (pbuf[i] != ';') { ReportErrorToken(); return; } + QChar *str = new QChar[ppos-i-2]; + for (int j = 0; j < ppos-i-2; j++) str[j] = pbuf[i+1+j]; + QString unistr(str,ppos-i-2); + // arg == 1 doesn't change the title. In XTerm it only changes the icon name + // (btw: arg=0 changes title and icon, arg=1 only icon, arg=2 only title + if (arg == 0 || arg == 2) emit changeTitle(arg,unistr); + delete [] str; +} + +// Interpreting Codes --------------------------------------------------------- + +/* + Now that the incoming character stream is properly tokenized, + meaning is assigned to them. These are either operations of + the current screen, or of the emulation class itself. + + The token to be interpreteted comes in as a machine word + possibly accompanied by two parameters. + + Likewise, the operations assigned to, come with up to two + arguments. One could consider to make up a proper table + from the function below. + + The technical reference manual provides more informations + about this mapping. +*/ + +void TEmuVt102::tau( int token, int p, int q ) +{ +//scan_buffer_report(); +//if (token == TY_CHR___()) printf("%c",p); else +//printf("tau(%d,%d,%d, %d,%d)\n",(token>>0)&0xff,(token>>8)&0xff,(token>>16)&0xffff,p,q); + switch (token) + { + + case TY_CHR___( ) : scr->ShowCharacter (p ); break; //UTF16 + + // 127 DEL : ignored on input + + case TY_CTL___('@' ) : /* NUL: ignored */ break; + case TY_CTL___('A' ) : /* SOH: ignored */ break; + case TY_CTL___('B' ) : /* STX: ignored */ break; + case TY_CTL___('C' ) : /* ETX: ignored */ break; + case TY_CTL___('D' ) : /* EOT: ignored */ break; + case TY_CTL___('E' ) : reportAnswerBack ( ); break; //VT100 + case TY_CTL___('F' ) : /* ACK: ignored */ break; + case TY_CTL___('G' ) : gui->Bell ( ); break; //VT100 + case TY_CTL___('H' ) : scr->BackSpace ( ); break; //VT100 + case TY_CTL___('I' ) : scr->Tabulate ( ); break; //VT100 + case TY_CTL___('J' ) : scr->NewLine ( ); break; //VT100 + case TY_CTL___('K' ) : scr->NewLine ( ); break; //VT100 + case TY_CTL___('L' ) : scr->NewLine ( ); break; //VT100 + case TY_CTL___('M' ) : scr->Return ( ); break; //VT100 + + case TY_CTL___('N' ) : useCharset ( 1); break; //VT100 + case TY_CTL___('O' ) : useCharset ( 0); break; //VT100 + + case TY_CTL___('P' ) : /* DLE: ignored */ break; + case TY_CTL___('Q' ) : /* DC1: XON continue */ break; //VT100 + case TY_CTL___('R' ) : /* DC2: ignored */ break; + case TY_CTL___('S' ) : /* DC3: XOFF halt */ break; //VT100 + case TY_CTL___('T' ) : /* DC4: ignored */ break; + case TY_CTL___('U' ) : /* NAK: ignored */ break; + case TY_CTL___('V' ) : /* SYN: ignored */ break; + case TY_CTL___('W' ) : /* ETB: ignored */ break; + case TY_CTL___('X' ) : scr->ShowCharacter ( 0x2592); break; //VT100 + case TY_CTL___('Y' ) : /* EM : ignored */ break; + case TY_CTL___('Z' ) : scr->ShowCharacter ( 0x2592); break; //VT100 + case TY_CTL___('[' ) : /* ESC: cannot be seen here. */ break; + case TY_CTL___('\\' ) : /* FS : ignored */ break; + case TY_CTL___(']' ) : /* GS : ignored */ break; + case TY_CTL___('^' ) : /* RS : ignored */ break; + case TY_CTL___('_' ) : /* US : ignored */ break; + + case TY_ESC___('D' ) : scr->index ( ); break; //VT100 + case TY_ESC___('E' ) : scr->NextLine ( ); break; //VT100 + case TY_ESC___('H' ) : scr->changeTabStop (TRUE ); break; //VT100 + case TY_ESC___('M' ) : scr->reverseIndex ( ); break; //VT100 + case TY_ESC___('Z' ) : reportTerminalType ( ); break; + case TY_ESC___('c' ) : reset ( ); break; + + case TY_ESC___('n' ) : useCharset ( 2); break; + case TY_ESC___('o' ) : useCharset ( 3); break; + case TY_ESC___('7' ) : saveCursor ( ); break; + case TY_ESC___('8' ) : restoreCursor ( ); break; + + case TY_ESC___('=' ) : setMode (MODE_AppKeyPad); break; + case TY_ESC___('>' ) : resetMode (MODE_AppKeyPad); break; + case TY_ESC___('<' ) : setMode (MODE_Ansi ); break; //VT100 + + case TY_ESC_CS('(', '0') : setCharset (0, '0'); break; //VT100 + case TY_ESC_CS('(', 'A') : setCharset (0, 'A'); break; //VT100 + case TY_ESC_CS('(', 'B') : setCharset (0, 'B'); break; //VT100 + + case TY_ESC_CS(')', '0') : setCharset (1, '0'); break; //VT100 + case TY_ESC_CS(')', 'A') : setCharset (1, 'A'); break; //VT100 + case TY_ESC_CS(')', 'B') : setCharset (1, 'B'); break; //VT100 + + case TY_ESC_CS('*', '0') : setCharset (2, '0'); break; //VT100 + case TY_ESC_CS('*', 'A') : setCharset (2, 'A'); break; //VT100 + case TY_ESC_CS('*', 'B') : setCharset (2, 'B'); break; //VT100 + + case TY_ESC_CS('+', '0') : setCharset (3, '0'); break; //VT100 + case TY_ESC_CS('+', 'A') : setCharset (3, 'A'); break; //VT100 + case TY_ESC_CS('+', 'B') : setCharset (3, 'B'); break; //VT100 + + case TY_ESC_CS('%', 'G') : setCodec (1 ); break; //LINUX + case TY_ESC_CS('%', '@') : setCodec (0 ); break; //LINUX + + case TY_ESC_DE('3' ) : /* IGNORED: double high, top half */ break; + case TY_ESC_DE('4' ) : /* IGNORED: double high, bottom half */ break; + case TY_ESC_DE('5' ) : /* IGNORED: single width, single high*/ break; + case TY_ESC_DE('6' ) : /* IGNORED: double width, single high*/ break; + case TY_ESC_DE('8' ) : scr->helpAlign ( ); break; + + case TY_CSI_PS('K', 0) : scr->clearToEndOfLine ( ); break; + case TY_CSI_PS('K', 1) : scr->clearToBeginOfLine ( ); break; + case TY_CSI_PS('K', 2) : scr->clearEntireLine ( ); break; + case TY_CSI_PS('J', 0) : scr->clearToEndOfScreen ( ); break; + case TY_CSI_PS('J', 1) : scr->clearToBeginOfScreen ( ); break; + case TY_CSI_PS('J', 2) : scr->clearEntireScreen ( ); break; + case TY_CSI_PS('g', 0) : scr->changeTabStop (FALSE ); break; //VT100 + case TY_CSI_PS('g', 3) : scr->clearTabStops ( ); break; //VT100 + case TY_CSI_PS('h', 4) : scr-> setMode (MODE_Insert ); break; + case TY_CSI_PS('h', 20) : setMode (MODE_NewLine ); break; + case TY_CSI_PS('i', 0) : /* IGNORE: attached printer */ break; //VT100 + case TY_CSI_PS('l', 4) : scr-> resetMode (MODE_Insert ); break; + case TY_CSI_PS('l', 20) : resetMode (MODE_NewLine ); break; + + case TY_CSI_PS('m', 0) : scr->setDefaultRendition ( ); break; + case TY_CSI_PS('m', 1) : scr-> setRendition (RE_BOLD ); break; //VT100 + case TY_CSI_PS('m', 4) : scr-> setRendition (RE_UNDERLINE); break; //VT100 + case TY_CSI_PS('m', 5) : scr-> setRendition (RE_BLINK ); break; //VT100 + case TY_CSI_PS('m', 7) : scr-> setRendition (RE_REVERSE ); break; + case TY_CSI_PS('m', 10) : /* IGNORED: mapping related */ break; //LINUX + case TY_CSI_PS('m', 11) : /* IGNORED: mapping related */ break; //LINUX + case TY_CSI_PS('m', 12) : /* IGNORED: mapping related */ break; //LINUX + case TY_CSI_PS('m', 22) : scr->resetRendition (RE_BOLD ); break; + case TY_CSI_PS('m', 24) : scr->resetRendition (RE_UNDERLINE); break; + case TY_CSI_PS('m', 25) : scr->resetRendition (RE_BLINK ); break; + case TY_CSI_PS('m', 27) : scr->resetRendition (RE_REVERSE ); break; + + case TY_CSI_PS('m', 30) : scr->setForeColor ( 0); break; + case TY_CSI_PS('m', 31) : scr->setForeColor ( 1); break; + case TY_CSI_PS('m', 32) : scr->setForeColor ( 2); break; + case TY_CSI_PS('m', 33) : scr->setForeColor ( 3); break; + case TY_CSI_PS('m', 34) : scr->setForeColor ( 4); break; + case TY_CSI_PS('m', 35) : scr->setForeColor ( 5); break; + case TY_CSI_PS('m', 36) : scr->setForeColor ( 6); break; + case TY_CSI_PS('m', 37) : scr->setForeColor ( 7); break; + case TY_CSI_PS('m', 39) : scr->setForeColorToDefault( ); break; + + case TY_CSI_PS('m', 40) : scr->setBackColor ( 0); break; + case TY_CSI_PS('m', 41) : scr->setBackColor ( 1); break; + case TY_CSI_PS('m', 42) : scr->setBackColor ( 2); break; + case TY_CSI_PS('m', 43) : scr->setBackColor ( 3); break; + case TY_CSI_PS('m', 44) : scr->setBackColor ( 4); break; + case TY_CSI_PS('m', 45) : scr->setBackColor ( 5); break; + case TY_CSI_PS('m', 46) : scr->setBackColor ( 6); break; + case TY_CSI_PS('m', 47) : scr->setBackColor ( 7); break; + case TY_CSI_PS('m', 49) : scr->setBackColorToDefault( ); break; + + case TY_CSI_PS('m', 90) : scr->setForeColor ( 8); break; + case TY_CSI_PS('m', 91) : scr->setForeColor ( 9); break; + case TY_CSI_PS('m', 92) : scr->setForeColor ( 10); break; + case TY_CSI_PS('m', 93) : scr->setForeColor ( 11); break; + case TY_CSI_PS('m', 94) : scr->setForeColor ( 12); break; + case TY_CSI_PS('m', 95) : scr->setForeColor ( 13); break; + case TY_CSI_PS('m', 96) : scr->setForeColor ( 14); break; + case TY_CSI_PS('m', 97) : scr->setForeColor ( 15); break; + + case TY_CSI_PS('m', 100) : scr->setBackColor ( 8); break; + case TY_CSI_PS('m', 101) : scr->setBackColor ( 9); break; + case TY_CSI_PS('m', 102) : scr->setBackColor ( 10); break; + case TY_CSI_PS('m', 103) : scr->setBackColor ( 11); break; + case TY_CSI_PS('m', 104) : scr->setBackColor ( 12); break; + case TY_CSI_PS('m', 105) : scr->setBackColor ( 13); break; + case TY_CSI_PS('m', 106) : scr->setBackColor ( 14); break; + case TY_CSI_PS('m', 107) : scr->setBackColor ( 15); break; + + case TY_CSI_PS('n', 5) : reportStatus ( ); break; + case TY_CSI_PS('n', 6) : reportCursorPosition ( ); break; + case TY_CSI_PS('q', 0) : /* IGNORED: LEDs off */ break; //VT100 + case TY_CSI_PS('q', 1) : /* IGNORED: LED1 on */ break; //VT100 + case TY_CSI_PS('q', 2) : /* IGNORED: LED2 on */ break; //VT100 + case TY_CSI_PS('q', 3) : /* IGNORED: LED3 on */ break; //VT100 + case TY_CSI_PS('q', 4) : /* IGNORED: LED4 on */ break; //VT100 + case TY_CSI_PS('x', 0) : reportTerminalParms ( 2); break; //VT100 + case TY_CSI_PS('x', 1) : reportTerminalParms ( 3); break; //VT100 + + case TY_CSI_PN('@' ) : scr->insertChars (p ); break; + case TY_CSI_PN('A' ) : scr->cursorUp (p ); break; //VT100 + case TY_CSI_PN('B' ) : scr->cursorDown (p ); break; //VT100 + case TY_CSI_PN('C' ) : scr->cursorRight (p ); break; //VT100 + case TY_CSI_PN('D' ) : scr->cursorLeft (p ); break; //VT100 + case TY_CSI_PN('G' ) : scr->setCursorX (p ); break; //LINUX + case TY_CSI_PN('H' ) : scr->setCursorYX (p, q); break; //VT100 + case TY_CSI_PN('L' ) : scr->insertLines (p ); break; + case TY_CSI_PN('M' ) : scr->deleteLines (p ); break; + case TY_CSI_PN('P' ) : scr->deleteChars (p ); break; + case TY_CSI_PN('X' ) : scr->eraseChars (p ); break; + case TY_CSI_PN('c' ) : reportTerminalType ( ); break; //VT100 + case TY_CSI_PN('d' ) : scr->setCursorY (p ); break; //LINUX + case TY_CSI_PN('f' ) : scr->setCursorYX (p, q); break; //VT100 + case TY_CSI_PN('r' ) : scr->setMargins (p, q); break; //VT100 + case TY_CSI_PN('y' ) : /* IGNORED: Confidence test */ break; //VT100 + + case TY_CSI_PR('h', 1) : setMode (MODE_AppCuKeys); break; //VT100 + case TY_CSI_PR('l', 1) : resetMode (MODE_AppCuKeys); break; //VT100 + case TY_CSI_PR('s', 1) : saveMode (MODE_AppCuKeys); break; //FIXME + case TY_CSI_PR('r', 1) : restoreMode (MODE_AppCuKeys); break; //FIXME + + case TY_CSI_PR('l', 2) : resetMode (MODE_Ansi ); break; //VT100 + + case TY_CSI_PR('h', 3) : setColumns ( 132); break; //VT100 + case TY_CSI_PR('l', 3) : setColumns ( 80); break; //VT100 + + case TY_CSI_PR('h', 4) : /* IGNORED: soft scrolling */ break; //VT100 + case TY_CSI_PR('l', 4) : /* IGNORED: soft scrolling */ break; //VT100 + + case TY_CSI_PR('h', 5) : scr-> setMode (MODE_Screen ); break; //VT100 + case TY_CSI_PR('l', 5) : scr-> resetMode (MODE_Screen ); break; //VT100 + + case TY_CSI_PR('h', 6) : scr-> setMode (MODE_Origin ); break; //VT100 + case TY_CSI_PR('l', 6) : scr-> resetMode (MODE_Origin ); break; //VT100 + case TY_CSI_PR('s', 6) : scr-> saveMode (MODE_Origin ); break; //FIXME + case TY_CSI_PR('r', 6) : scr->restoreMode (MODE_Origin ); break; //FIXME + + case TY_CSI_PR('h', 7) : scr-> setMode (MODE_Wrap ); break; //VT100 + case TY_CSI_PR('l', 7) : scr-> resetMode (MODE_Wrap ); break; //VT100 + case TY_CSI_PR('s', 7) : scr-> saveMode (MODE_Wrap ); break; //FIXME + case TY_CSI_PR('r', 7) : scr->restoreMode (MODE_Wrap ); break; //FIXME + + case TY_CSI_PR('h', 8) : /* IGNORED: autorepeat on */ break; //VT100 + case TY_CSI_PR('l', 8) : /* IGNORED: autorepeat off */ break; //VT100 + + case TY_CSI_PR('h', 9) : /* IGNORED: interlace */ break; //VT100 + case TY_CSI_PR('l', 9) : /* IGNORED: interlace */ break; //VT100 + + case TY_CSI_PR('h', 25) : setMode (MODE_Cursor ); break; //VT100 + case TY_CSI_PR('l', 25) : resetMode (MODE_Cursor ); break; //VT100 + + case TY_CSI_PR('h', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM + case TY_CSI_PR('l', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM + case TY_CSI_PR('s', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM + case TY_CSI_PR('r', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM + + case TY_CSI_PR('h', 47) : setMode (MODE_AppScreen); break; //VT100 + case TY_CSI_PR('l', 47) : resetMode (MODE_AppScreen); break; //VT100 + + case TY_CSI_PR('h', 1000) : setMode (MODE_Mouse1000); break; //XTERM + case TY_CSI_PR('l', 1000) : resetMode (MODE_Mouse1000); break; //XTERM + case TY_CSI_PR('s', 1000) : saveMode (MODE_Mouse1000); break; //XTERM + case TY_CSI_PR('r', 1000) : restoreMode (MODE_Mouse1000); break; //XTERM + + case TY_CSI_PR('h', 1001) : /* IGNORED: hilite mouse tracking */ break; //XTERM + case TY_CSI_PR('l', 1001) : /* IGNORED: hilite mouse tracking */ break; //XTERM + case TY_CSI_PR('s', 1001) : /* IGNORED: hilite mouse tracking */ break; //XTERM + case TY_CSI_PR('r', 1001) : /* IGNORED: hilite mouse tracking */ break; //XTERM + + case TY_CSI_PR('h', 1047) : setMode (MODE_AppScreen); break; //XTERM + case TY_CSI_PR('l', 1047) : resetMode (MODE_AppScreen); break; //XTERM + + //FIXME: Unitoken: save translations + case TY_CSI_PR('h', 1048) : saveCursor ( ); break; //XTERM + case TY_CSI_PR('l', 1048) : restoreCursor ( ); break; //XTERM + + //FIXME: every once new sequences like this pop up in xterm. + // Here's a guess of what they could mean. + case TY_CSI_PR('h', 1049) : setMode (MODE_AppScreen); break; //XTERM + case TY_CSI_PR('l', 1049) : resetMode (MODE_AppScreen); break; //XTERM + + //FIXME: when changing between vt52 and ansi mode evtl do some resetting. + case TY_VT52__('A' ) : scr->cursorUp ( 1); break; //VT52 + case TY_VT52__('B' ) : scr->cursorDown ( 1); break; //VT52 + case TY_VT52__('C' ) : scr->cursorRight ( 1); break; //VT52 + case TY_VT52__('D' ) : scr->cursorLeft ( 1); break; //VT52 + + case TY_VT52__('F' ) : setAndUseCharset (0, '0'); break; //VT52 + case TY_VT52__('G' ) : setAndUseCharset (0, 'B'); break; //VT52 + + case TY_VT52__('H' ) : scr->setCursorYX (1,1 ); break; //VT52 + case TY_VT52__('I' ) : scr->reverseIndex ( ); break; //VT52 + case TY_VT52__('J' ) : scr->clearToEndOfScreen ( ); break; //VT52 + case TY_VT52__('K' ) : scr->clearToEndOfLine ( ); break; //VT52 + case TY_VT52__('Y' ) : scr->setCursorYX (p-31,q-31 ); break; //VT52 + case TY_VT52__('Z' ) : reportTerminalType ( ); break; //VT52 + case TY_VT52__('<' ) : setMode (MODE_Ansi ); break; //VT52 + case TY_VT52__('=' ) : setMode (MODE_AppKeyPad); break; //VT52 + case TY_VT52__('>' ) : resetMode (MODE_AppKeyPad); break; //VT52 + + default : ReportErrorToken(); break; + }; +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Terminal to Host protocol */ +/* */ +/* ------------------------------------------------------------------------- */ + +/* + Outgoing bytes originate from several sources: + + - Replies to Enquieries. + - Mouse Events + - Keyboard Events +*/ + +/*! +*/ + +void TEmuVt102::sendString(const char* s) +{ + emit sndBlock(s,strlen(s)); +} + +// Replies ----------------------------------------------------------------- -- + +// This section copes with replies send as response to an enquiery control code. + +/*! +*/ + +void TEmuVt102::reportCursorPosition() +{ char tmp[20]; + sprintf(tmp,"\033[%d;%dR",scr->getCursorY()+1,scr->getCursorX()+1); + sendString(tmp); +} + +/* + What follows here is rather obsolete and faked stuff. + The correspondent enquieries are neverthenless issued. +*/ + +/*! +*/ + +void TEmuVt102::reportTerminalType() +{ +//FIXME: should change? + if (getMode(MODE_Ansi)) +// sendString("\033[?1;2c"); // I'm a VT100 with AP0 //FIXME: send only in response to ^[[0c + sendString("\033[>0;115;0c"); // I'm a VT220 //FIXME: send only in response to ^[[>c + else + sendString("\033/Z"); // I'm a VT52 +} + +void TEmuVt102::reportTerminalParms(int p) +// DECREPTPARM +{ char tmp[100]; + sprintf(tmp,"\033[%d;1;1;112;112;1;0x",p); // not really true. + sendString(tmp); +} + +/*! +*/ + +void TEmuVt102::reportStatus() +{ + sendString("\033[0n"); //VT100. Device status report. 0 = Ready. +} + +/*! +*/ + +#define ANSWER_BACK "" // This is really obsolete VT100 stuff. + +void TEmuVt102::reportAnswerBack() +{ + sendString(ANSWER_BACK); +} + +// Mouse Handling ---------------------------------------------------------- -- + +/*! + Mouse clicks are possibly reported to the client + application if it has issued interest in them. + They are normally consumed by the widget for copy + and paste, but may be propagated from the widget + when gui->setMouseMarks is set via setMode(MODE_Mouse1000). + + `x',`y' are 1-based. + `ev' (event) indicates the button pressed (0-2) + or a general mouse release (3). +*/ + +void TEmuVt102::onMouse( int cb, int cx, int cy ) +{ char tmp[20]; + if (!connected) return; + sprintf(tmp,"\033[M%c%c%c",cb+040,cx+040,cy+040); + sendString(tmp); +} + +// Keyboard Handling ------------------------------------------------------- -- + +#define encodeMode(M,B) BITS(B,getMode(M)) +#define encodeStat(M,B) BITS(B,((ev->state() & (M)) == (M))) + +/* + Keyboard event handling has been simplified somewhat by pushing + the complications towards a configuration file [see KeyTrans class]. +*/ + +void TEmuVt102::onKeyPress( QKeyEvent* ev ) +{ + if (!connected) return; // someone else gets the keys + +//printf("State/Key: 0x%04x 0x%04x (%d,%d)\n",ev->state(),ev->key(),ev->text().length(),ev->text().length()?ev->text().ascii()[0]:0); + + // revert to non-history when typing + if (scr->getHistCursor() != scr->getHistLines()); + scr->setHistCursor(scr->getHistLines()); + + // lookup in keyboard translation table ... + int cmd; const char* txt; int len; + if (keytrans->findEntry(ev->key(), encodeMode(MODE_NewLine , BITS_NewLine ) + // OLD, + encodeMode(MODE_Ansi , BITS_Ansi ) + // OBSOLETE, + encodeMode(MODE_AppCuKeys, BITS_AppCuKeys ) + // VT100 stuff + encodeStat(ControlButton , BITS_Control ) + + encodeStat(ShiftButton , BITS_Shift ) + + encodeStat(AltButton , BITS_Alt ), + &cmd, &txt, &len )) +//printf("cmd: %d, %s, %d\n",cmd,txt,len); + switch(cmd) // ... and execute if found. + { + case CMD_emitSelection : gui->emitSelection(); return; + case CMD_scrollPageUp : gui->doScroll(-gui->Lines()/2); return; + case CMD_scrollPageDown : gui->doScroll(+gui->Lines()/2); return; + case CMD_scrollLineUp : gui->doScroll(-1 ); return; + case CMD_scrollLineDown : gui->doScroll(+1 ); return; + case CMD_send : emit sndBlock(txt,len); return; + case CMD_prevSession : emit prevSession(); return; + case CMD_nextSession : emit nextSession(); return; + } + + // fall back handling + if (!ev->text().isEmpty()) + { + if (ev->state() & AltButton) sendString("\033"); // ESC, this is the ALT prefix + /// very hacky + if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='A')) sendString("\01"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='B')) sendString("\02"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='C')) sendString("\03"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='D')) sendString("\04"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='E')) sendString("\05"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='F')) sendString("\06"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='G')) sendString("\07"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='H')) sendString("\010"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='I')) sendString("\011"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='J')) sendString("\012"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='K')) sendString("\013"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='L')) sendString("\014"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='M')) sendString("\015"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='N')) sendString("\016"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='O')) sendString("\017"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='P')) sendString("\020"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='Q')) sendString("\021"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='R')) sendString("\022"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='S')) sendString("\023"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='T')) sendString("\024"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='U')) sendString("\025"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='V')) sendString("\026"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='W')) sendString("\027"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='X')) sendString("\030"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='Y')) sendString("\031"); + else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='Z')) sendString("\032"); + else { + QCString s = codec->fromUnicode(ev->text()); // encode for application + emit sndBlock(s.data(),s.length()); // we may well have s.length() > 1 + } + return; + } +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* VT100 Charsets */ +/* */ +/* ------------------------------------------------------------------------- */ + +// Character Set Conversion ------------------------------------------------ -- + +/* + The processing contains a VT100 specific code translation layer. + It's still in use and mainly responsible for the line drawing graphics. + + These and some other glyphs are assigned to codes (0x5f-0xfe) + normally occupied by the latin letters. Since this codes also + appear within control sequences, the extra code conversion + does not permute with the tokenizer and is placed behind it + in the pipeline. It only applies to tokens, which represent + plain characters. + + This conversion it eventually continued in TEWidget.C, since + it might involve VT100 enhanced fonts, which have these + particular glyphs allocated in (0x00-0x1f) in their code page. +*/ + +#define CHARSET charset[scr==screen[1]] + +// Apply current character map. + +unsigned short TEmuVt102::applyCharset(unsigned short c) +{ + if (CHARSET.graphic && 0x5f <= c && c <= 0x7e) return vt100_graphics[c-0x5f]; + if (CHARSET.pound && c == '#' ) return 0xa3; //This mode is obsolete + return c; +} + +/* + "Charset" related part of the emulation state. + This configures the VT100 charset filter. + + While most operation work on the current screen, + the following two are different. +*/ + +void TEmuVt102::resetCharset(int scrno) +{ + charset[scrno].cu_cs = 0; + strncpy(charset[scrno].charset,"BBBB",4); + charset[scrno].sa_graphic = FALSE; + charset[scrno].sa_pound = FALSE; + charset[scrno].graphic = FALSE; + charset[scrno].pound = FALSE; +} + +/*! +*/ + +void TEmuVt102::setCharset(int n, int cs) // on both screens. +{ + charset[0].charset[n&3] = cs; useCharset(charset[0].cu_cs); + charset[1].charset[n&3] = cs; useCharset(charset[1].cu_cs); +} + +/*! +*/ + +void TEmuVt102::setAndUseCharset(int n, int cs) +{ + CHARSET.charset[n&3] = cs; + useCharset(n&3); +} + +/*! +*/ + +void TEmuVt102::useCharset(int n) +{ + CHARSET.cu_cs = n&3; + CHARSET.graphic = (CHARSET.charset[n&3] == '0'); + CHARSET.pound = (CHARSET.charset[n&3] == 'A'); //This mode is obsolete +} + +/*! Save the cursor position and the rendition attribute settings. */ + +void TEmuVt102::saveCursor() +{ + CHARSET.sa_graphic = CHARSET.graphic; + CHARSET.sa_pound = CHARSET.pound; //This mode is obsolete + // we are not clear about these + //sa_charset = charsets[cScreen->charset]; + //sa_charset_num = cScreen->charset; + scr->saveCursor(); +} + +/*! Restore the cursor position and the rendition attribute settings. */ + +void TEmuVt102::restoreCursor() +{ + CHARSET.graphic = CHARSET.sa_graphic; + CHARSET.pound = CHARSET.sa_pound; //This mode is obsolete + scr->restoreCursor(); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Mode Operations */ +/* */ +/* ------------------------------------------------------------------------- */ + +/* + Some of the emulations state is either added to the state of the screens. + + This causes some scoping problems, since different emulations choose to + located the mode either to the current screen or to both. + + For strange reasons, the extend of the rendition attributes ranges over + all screens and not over the actual screen. + + We decided on the precise precise extend, somehow. +*/ + +// "Mode" related part of the state. These are all booleans. + +void TEmuVt102::resetModes() +{ + resetMode(MODE_Mouse1000); saveMode(MODE_Mouse1000); + resetMode(MODE_AppScreen); saveMode(MODE_AppScreen); + // here come obsolete modes + resetMode(MODE_AppCuKeys); saveMode(MODE_AppCuKeys); + resetMode(MODE_NewLine ); + setMode(MODE_Ansi ); +} + +void TEmuVt102::setMode(int m) +{ + currParm.mode[m] = TRUE; + switch (m) + { + case MODE_Mouse1000 : gui->setMouseMarks(FALSE); + break; + case MODE_AppScreen : screen[1]->clearSelection(); + screen[1]->clearEntireScreen(); + setScreen(1); + break; + } + if (m < MODES_SCREEN || m == MODE_NewLine) + { + screen[0]->setMode(m); + screen[1]->setMode(m); + } +} + +void TEmuVt102::resetMode(int m) +{ + currParm.mode[m] = FALSE; + switch (m) + { + case MODE_Mouse1000 : gui->setMouseMarks(TRUE); + break; + case MODE_AppScreen : screen[0]->clearSelection(); + setScreen(0); + break; + } + if (m < MODES_SCREEN || m == MODE_NewLine) + { + screen[0]->resetMode(m); + screen[1]->resetMode(m); + } +} + +void TEmuVt102::saveMode(int m) +{ + saveParm.mode[m] = currParm.mode[m]; +} + +void TEmuVt102::restoreMode(int m) +{ + if(saveParm.mode[m]) setMode(m); else resetMode(m); +} + +BOOL TEmuVt102::getMode(int m) +{ + return currParm.mode[m]; +} + +void TEmuVt102::setConnect(bool c) +{ + TEmulation::setConnect(c); + if (c) + { // refresh mouse mode + if (getMode(MODE_Mouse1000)) + setMode(MODE_Mouse1000); + else + resetMode(MODE_Mouse1000); + } +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Diagnostic */ +/* */ +/* ------------------------------------------------------------------------- */ + +/*! shows the contents of the scan buffer. + + This functions is used for diagnostics. It is called by \e ReportErrorToken + to inform about strings that cannot be decoded or handled by the emulation. + + \sa ReportErrorToken +*/ + +/*! +*/ + +static void hexdump(int* s, int len) +{ int i; + for (i = 0; i < len; i++) + { + if (s[i] == '\\') + printf("\\\\"); + else + if ((s[i]) > 32 && s[i] < 127) + printf("%c",s[i]); + else + printf("\\%04x(hex)",s[i]); + } +} + +void TEmuVt102::scan_buffer_report() +{ + if (ppos == 0 || ppos == 1 && (pbuf[0] & 0xff) >= 32) return; + printf("token: "); hexdump(pbuf,ppos); printf("\n"); +} + +/*! +*/ + +void TEmuVt102::ReportErrorToken() +{ + printf("undecodable "); scan_buffer_report(); +} diff --git a/noncore/apps/opie-console/TEmuVt102.h b/noncore/apps/opie-console/TEmuVt102.h new file mode 100644 index 0000000..a448a71 --- a/dev/null +++ b/noncore/apps/opie-console/TEmuVt102.h @@ -0,0 +1,135 @@ +/* -------------------------------------------------------------------------- */ +/* */ +/* [TEmuVt102.h] X Terminal Emulation */ +/* */ +/* -------------------------------------------------------------------------- */ +/* */ +/* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ +/* */ +/* This file is part of Konsole - an X terminal for KDE */ +/* */ +/* -------------------------------------------------------------------------- */ +/* */ +/* Ported Konsole to Qt/Embedded */ +/* */ +/* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ +/* */ +/* -------------------------------------------------------------------------- */ + +#ifndef VT102EMU_H +#define VT102EMU_H + +#include "TEWidget.h" +#include "TEScreen.h" +#include "TEmulation.h" +#include <qtimer.h> +#include <stdio.h> + +// + +#define MODE_AppScreen (MODES_SCREEN+0) +#define MODE_AppCuKeys (MODES_SCREEN+1) +#define MODE_AppKeyPad (MODES_SCREEN+2) +#define MODE_Mouse1000 (MODES_SCREEN+3) +#define MODE_Ansi (MODES_SCREEN+4) +#define MODE_total (MODES_SCREEN+5) + +struct DECpar +{ + BOOL mode[MODE_total]; +}; + +struct CharCodes +{ + // coding info + char charset[4]; // + int cu_cs; // actual charset. + bool graphic; // Some VT100 tricks + bool pound ; // Some VT100 tricks + bool sa_graphic; // saved graphic + bool sa_pound; // saved pound +}; + +class TEmuVt102 : public TEmulation +{ Q_OBJECT + +public: + + TEmuVt102(TEWidget* gui); + ~TEmuVt102(); + +public slots: // signals incoming from TEWidget + + void onKeyPress(QKeyEvent*); + void onMouse(int cb, int cx, int cy); + +signals: + + void changeTitle(int,const QString&); + void prevSession(); + void nextSession(); + +public: + + void reset(); + + void onRcvChar(int cc); + void sendString(const char *); + +public: + + BOOL getMode (int m); + + void setMode (int m); + void resetMode (int m); + void saveMode (int m); + void restoreMode(int m); + void resetModes(); + + void setConnect(bool r); + +private: + + void resetToken(); +#define MAXPBUF 80 + void pushToToken(int cc); + int pbuf[MAXPBUF]; //FIXME: overflow? + int ppos; +#define MAXARGS 15 + void addDigit(int dig); + void addArgument(); + int argv[MAXARGS]; + int argc; + void initTokenizer(); + int tbl[256]; + + void scan_buffer_report(); //FIXME: rename + void ReportErrorToken(); //FIXME: rename + + void tau(int code, int p, int q); + void XtermHack(); + + // + + void reportTerminalType(); + void reportStatus(); + void reportAnswerBack(); + void reportCursorPosition(); + void reportTerminalParms(int p); + +protected: + + unsigned short applyCharset(unsigned short c); + void setCharset(int n, int cs); + void useCharset(int n); + void setAndUseCharset(int n, int cs); + void saveCursor(); + void restoreCursor(); + void resetCharset(int scrno); + CharCodes charset[2]; + + DECpar currParm; + DECpar saveParm; +}; + +#endif // ifndef ANSIEMU_H diff --git a/noncore/apps/opie-console/TEmulation.cpp b/noncore/apps/opie-console/TEmulation.cpp new file mode 100644 index 0000000..6f3ad32 --- a/dev/null +++ b/noncore/apps/opie-console/TEmulation.cpp @@ -0,0 +1,363 @@ +/* -------------------------------------------------------------------------- */ +/* */ +/* [TEmulation.cpp] Terminal Emulation Decoder */ +/* */ +/* -------------------------------------------------------------------------- */ +/* */ +/* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ +/* */ +/* This file is part of Konsole - an X terminal for KDE */ +/* */ +/* -------------------------------------------------------------------------- */ +/* */ +/* Ported Konsole to Qt/Embedded */ +/* */ +/* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ +/* */ +/* -------------------------------------------------------------------------- */ + +/*! \class TEmulation + + \brief Mediator between TEWidget and TEScreen. + + This class is responsible to scan the escapes sequences of the terminal + emulation and to map it to their corresponding semantic complements. + Thus this module knows mainly about decoding escapes sequences and + is a stateless device w.r.t. the semantics. + + It is also responsible to refresh the TEWidget by certain rules. + + \sa TEWidget \sa TEScreen + + \par A note on refreshing + + Although the modifications to the current screen image could immediately + be propagated via `TEWidget' to the graphical surface, we have chosen + another way here. + + The reason for doing so is twofold. + + First, experiments show that directly displaying the operation results + in slowing down the overall performance of emulations. Displaying + individual characters using X11 creates a lot of overhead. + + Second, by using the following refreshing method, the screen operations + can be completely separated from the displaying. This greatly simplifies + the programmer's task of coding and maintaining the screen operations, + since one need not worry about differential modifications on the + display affecting the operation of concern. + + We use a refreshing algorithm here that has been adoped from rxvt/kvt. + + By this, refreshing is driven by a timer, which is (re)started whenever + a new bunch of data to be interpreted by the emulation arives at `onRcvBlock'. + As soon as no more data arrive for `BULK_TIMEOUT' milliseconds, we trigger + refresh. This rule suits both bulk display operation as done by curses as + well as individual characters typed. + (BULK_TIMEOUT < 1000 / max characters received from keyboard per second). + + Additionally, we trigger refreshing by newlines comming in to make visual + snapshots of lists as produced by `cat', `ls' and likely programs, thereby + producing the illusion of a permanent and immediate display operation. + + As a sort of catch-all needed for cases where none of the above + conditions catch, the screen refresh is also triggered by a count + of incoming bulks (`bulk_incnt'). +*/ + +/* FIXME + - evtl. the bulk operations could be made more transparent. +*/ + +#include "TEmulation.h" +#include "TEWidget.h" +#include "TEScreen.h" +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <qkeycode.h> + + +/* ------------------------------------------------------------------------- */ +/* */ +/* TEmulation */ +/* */ +/* ------------------------------------------------------------------------- */ + +#define CNTL(c) ((c)-'@') + +/*! +*/ + +TEmulation::TEmulation(TEWidget* gui) +: decoder((QTextDecoder*)NULL) +{ + this->gui = gui; + + screen[0] = new TEScreen(gui->Lines(),gui->Columns()); + screen[1] = new TEScreen(gui->Lines(),gui->Columns()); + scr = screen[0]; + + bulk_nlcnt = 0; // reset bulk newline counter + bulk_incnt = 0; // reset bulk counter + connected = FALSE; + + QObject::connect(&bulk_timer, SIGNAL(timeout()), this, SLOT(showBulk()) ); + QObject::connect(gui,SIGNAL(changedImageSizeSignal(int,int)), + this,SLOT(onImageSizeChange(int,int))); + QObject::connect(gui,SIGNAL(changedHistoryCursor(int)), + this,SLOT(onHistoryCursorChange(int))); + QObject::connect(gui,SIGNAL(keyPressedSignal(QKeyEvent*)), + this,SLOT(onKeyPress(QKeyEvent*))); + QObject::connect(gui,SIGNAL(beginSelectionSignal(const int,const int)), + this,SLOT(onSelectionBegin(const int,const int)) ); + QObject::connect(gui,SIGNAL(extendSelectionSignal(const int,const int)), + this,SLOT(onSelectionExtend(const int,const int)) ); + QObject::connect(gui,SIGNAL(endSelectionSignal(const BOOL)), + this,SLOT(setSelection(const BOOL)) ); + QObject::connect(gui,SIGNAL(clearSelectionSignal()), + this,SLOT(clearSelection()) ); +} + +/*! +*/ + +TEmulation::~TEmulation() +{ + delete screen[0]; + delete screen[1]; + bulk_timer.stop(); +} + +/*! change between primary and alternate screen +*/ + +void TEmulation::setScreen(int n) +{ + scr = screen[n&1]; +} + +void TEmulation::setHistory(bool on) +{ + screen[0]->setScroll(on); + if (!connected) return; + showBulk(); +} + +bool TEmulation::history() +{ + return screen[0]->hasScroll(); +} + +void TEmulation::setCodec(int c) +{ + //FIXME: check whether we have to free codec + codec = c ? QTextCodec::codecForName("utf8") + : QTextCodec::codecForLocale(); + if (decoder) delete decoder; + decoder = codec->makeDecoder(); +} + +void TEmulation::setKeytrans(int no) +{ + keytrans = KeyTrans::find(no); +} + +void TEmulation::setKeytrans(const char * no) +{ + keytrans = KeyTrans::find(no); +} + +// Interpreting Codes --------------------------------------------------------- + +/* + This section deals with decoding the incoming character stream. + Decoding means here, that the stream is first seperated into `tokens' + which are then mapped to a `meaning' provided as operations by the + `Screen' class. +*/ + +/*! +*/ + +void TEmulation::onRcvChar(int c) +// process application unicode input to terminal +// this is a trivial scanner +{ + c &= 0xff; + switch (c) + { + case '\b' : scr->BackSpace(); break; + case '\t' : scr->Tabulate(); break; + case '\n' : scr->NewLine(); break; + case '\r' : scr->Return(); break; + case 0x07 : gui->Bell(); break; + default : scr->ShowCharacter(c); break; + }; +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Keyboard Handling */ +/* */ +/* ------------------------------------------------------------------------- */ + +/*! +*/ + +void TEmulation::onKeyPress( QKeyEvent* ev ) +{ + if (!connected) return; // someone else gets the keys + if (scr->getHistCursor() != scr->getHistLines()); + scr->setHistCursor(scr->getHistLines()); + if (!ev->text().isEmpty()) + { // A block of text + // Note that the text is proper unicode. + // We should do a conversion here, but since this + // routine will never be used, we simply emit plain ascii. + emit sndBlock(ev->text().ascii(),ev->text().length()); + } + else if (ev->ascii()>0) + { unsigned char c[1]; + c[0] = ev->ascii(); + emit sndBlock((char*)c,1); + } +} + +// Unblocking, Byte to Unicode translation --------------------------------- -- + +/* + We are doing code conversion from locale to unicode first. +*/ + +void TEmulation::onRcvBlock(const char *s, int len) +{ + bulkStart(); + bulk_incnt += 1; + for (int i = 0; i < len; i++) + { + QString result = decoder->toUnicode(&s[i],1); + int reslen = result.length(); + for (int j = 0; j < reslen; j++) + onRcvChar(result[j].unicode()); + if (s[i] == '\n') bulkNewline(); + } + bulkEnd(); +} + +// Selection --------------------------------------------------------------- -- + +void TEmulation::onSelectionBegin(const int x, const int y) { + if (!connected) return; + scr->setSelBeginXY(x,y); + showBulk(); +} + +void TEmulation::onSelectionExtend(const int x, const int y) { + if (!connected) return; + scr->setSelExtentXY(x,y); + showBulk(); +} + +void TEmulation::setSelection(const BOOL preserve_line_breaks) { + if (!connected) return; + QString t = scr->getSelText(preserve_line_breaks); + if (!t.isNull()) gui->setSelection(t); +} + +void TEmulation::clearSelection() { + if (!connected) return; + scr->clearSelection(); + showBulk(); +} + +// Refreshing -------------------------------------------------------------- -- + +#define BULK_TIMEOUT 20 + +/*! + called when \n comes in. Evtl. triggers showBulk at endBulk +*/ + +void TEmulation::bulkNewline() +{ + bulk_nlcnt += 1; + bulk_incnt = 0; // reset bulk counter since `nl' rule applies +} + +/*! +*/ + +void TEmulation::showBulk() +{ + bulk_nlcnt = 0; // reset bulk newline counter + bulk_incnt = 0; // reset bulk counter + if (connected) + { + ca* image = scr->getCookedImage(); // get the image + gui->setImage(image, + scr->getLines(), + scr->getColumns()); // actual refresh + free(image); + //FIXME: check that we do not trigger other draw event here. + gui->setScroll(scr->getHistCursor(),scr->getHistLines()); + } +} + +void TEmulation::bulkStart() +{ + if (bulk_timer.isActive()) bulk_timer.stop(); +} + +void TEmulation::bulkEnd() +{ + if ( bulk_nlcnt > gui->Lines() || bulk_incnt > 20 ) + showBulk(); // resets bulk_??cnt to 0, too. + else + bulk_timer.start(BULK_TIMEOUT,TRUE); +} + +void TEmulation::setConnect(bool c) +{ + connected = c; + if ( connected) + { + onImageSizeChange(gui->Lines(), gui->Columns()); + showBulk(); + } + else + { + scr->clearSelection(); + } +} + +// --------------------------------------------------------------------------- + +/*! triggered by image size change of the TEWidget `gui'. + + This event is simply propagated to the attached screens + and to the related serial line. +*/ + +void TEmulation::onImageSizeChange(int lines, int columns) +{ + if (!connected) return; + screen[0]->resizeImage(lines,columns); + screen[1]->resizeImage(lines,columns); + showBulk(); + emit ImageSizeChanged(lines,columns); // propagate event to serial line +} + +void TEmulation::onHistoryCursorChange(int cursor) +{ + if (!connected) return; + scr->setHistCursor(cursor); + showBulk(); +} + +void TEmulation::setColumns(int columns) +{ + //FIXME: this goes strange ways. + // Can we put this straight or explain it at least? + emit changeColumns(columns); +} diff --git a/noncore/apps/opie-console/TEmulation.h b/noncore/apps/opie-console/TEmulation.h new file mode 100644 index 0000000..ec15e7a --- a/dev/null +++ b/noncore/apps/opie-console/TEmulation.h @@ -0,0 +1,117 @@ +/* -------------------------------------------------------------------------- */ +/* */ +/* [emulation.h] Fundamental Terminal Emulation */ +/* */ +/* -------------------------------------------------------------------------- */ +/* */ +/* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ +/* */ +/* This file is part of Konsole - an X terminal for KDE */ +/* */ +/* -------------------------------------------------------------------------- */ +/* */ +/* Ported Konsole to Qt/Embedded */ +/* */ +/* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ +/* */ +/* -------------------------------------------------------------------------- */ + +#ifndef EMULATION_H +#define EMULATION_H + +#include "TEWidget.h" +#include "TEScreen.h" +#include <qtimer.h> +#include <stdio.h> +#include <qtextcodec.h> +#include "keytrans.h" + +class TEmulation : public QObject +{ Q_OBJECT + +public: + + TEmulation(TEWidget* gui); + ~TEmulation(); + +public: + virtual void setHistory(bool on); + virtual bool history(); + +public slots: // signals incoming from TEWidget + + virtual void onImageSizeChange(int lines, int columns); + virtual void onHistoryCursorChange(int cursor); + virtual void onKeyPress(QKeyEvent*); + + virtual void clearSelection(); + virtual void onSelectionBegin(const int x, const int y); + virtual void onSelectionExtend(const int x, const int y); + virtual void setSelection(const BOOL preserve_line_breaks); + +public slots: // signals incoming from data source + + void onRcvBlock(const char* txt,int len); + +signals: + + void sndBlock(const char* txt,int len); + void ImageSizeChanged(int lines, int columns); + void changeColumns(int columns); + void changeTitle(int arg, const char* str); + +public: + + virtual void onRcvChar(int); + + virtual void setMode (int) = 0; + virtual void resetMode(int) = 0; + + virtual void sendString(const char*) = 0; + + virtual void setConnect(bool r); + void setColumns(int columns); + + void setKeytrans(int no); + void setKeytrans(const char * no); + +protected: + + TEWidget* gui; + TEScreen* scr; // referes to one `screen' + TEScreen* screen[2]; // 0 = primary, 1 = alternate + void setScreen(int n); // set `scr' to `screen[n]' + + bool connected; // communicate with widget + + void setCodec(int c); // codec number, 0 = locale, 1=utf8 + + QTextCodec* codec; + QTextCodec* localeCodec; + QTextDecoder* decoder; + + KeyTrans* keytrans; + +// refreshing related material. +// this is localized in the class. +private slots: // triggered by timer + + void showBulk(); + +private: + + void bulkNewline(); + void bulkStart(); + void bulkEnd(); + +private: + + QTimer bulk_timer; + int bulk_nlcnt; // bulk newline counter + char* SelectedText; + int bulk_incnt; // bulk counter + + +}; + +#endif // ifndef EMULATION_H diff --git a/noncore/apps/opie-console/default.cpp b/noncore/apps/opie-console/default.cpp index dd9681d..ce5c870 100644 --- a/noncore/apps/opie-console/default.cpp +++ b/noncore/apps/opie-console/default.cpp @@ -59,21 +59,21 @@ extern "C" { ProfileDialogWidget* newBTWidget( const QString& str, QWidget* wid ) { return new BTConfigWidget(str, wid ); } // Terminal Widget(s) - ProfileDialogWidget* newTerminalWidget(const QString& na, QWidget* wid) { +/* ProfileDialogWidget* newTerminalWidget(const QString& na, QWidget* wid) { return new TerminalWidget(na, wid,0 ); } - - // VT Emulations +*/ +/* // VT Emulations EmulationLayer* newVT102( WidgetLayer* wid ) { return new Vt102Emulation( wid ); } - +*/ }; Default::Default( MetaFactory* fact ) { fact->addFileTransferLayer( "SZ", QObject::tr("Z-Modem"), newSZTransfer ); fact->addFileTransferLayer( "SY", QObject::tr("Y-Modem"), newSYTransfer ); fact->addFileTransferLayer( "SX", QObject::tr("X-Modem"), newSXTransfer ); @@ -88,13 +88,13 @@ Default::Default( MetaFactory* fact ) { fact->addConnectionWidgetFactory( "serial", QObject::tr("Serial"), newSerialWidget ); fact->addConnectionWidgetFactory( "irda", QObject::tr("Infrared"), newIrDaWidget ); fact->addConnectionWidgetFactory( "modem", QObject::tr("Modem"), newModemWidget ); fact->addConnectionWidgetFactory( "bt", QObject::tr("Bluetooth"), newBTWidget ); - fact->addTerminalWidgetFactory( "default", QObject::tr("Default Terminal"), newTerminalWidget ); +// fact->addTerminalWidgetFactory( "default", QObject::tr("Default Terminal"), newTerminalWidget ); - fact->addEmulationLayer( "default", QObject::tr("Default Terminal"), newVT102 ); +// fact->addEmulationLayer( "default", QObject::tr("Default Terminal"), newVT102 ); } Default::~Default() { } diff --git a/noncore/apps/opie-console/default.h b/noncore/apps/opie-console/default.h index 03616f3..288f370 100644 --- a/noncore/apps/opie-console/default.h +++ b/noncore/apps/opie-console/default.h @@ -19,15 +19,15 @@ extern "C" { IOLayer* newIrDaLayer(const Profile& ); ProfileDialogWidget* newSerialWidget(const QString&, QWidget* ); ProfileDialogWidget* newIrDaWidget (const QString&, QWidget* ); ProfileDialogWidget* newBTWidget (const QString&, QWidget* ); - ProfileDialogWidget* newTerminalWidget(const QString&, QWidget* ); +// ProfileDialogWidget* newTerminalWidget(const QString&, QWidget* ); - EmulationLayer* newVT102( WidgetLayer* ); +// EmulationLayer* newVT102( WidgetLayer* ); }; class MetaFactory; struct Default { public: Default(MetaFactory* ); diff --git a/noncore/apps/opie-console/history.cpp b/noncore/apps/opie-console/history.cpp index 3b82d16..226a0a2 100644 --- a/noncore/apps/opie-console/history.cpp +++ b/noncore/apps/opie-console/history.cpp @@ -64,21 +64,21 @@ FIXME: There is noticable decrease in speed, also. Perhaps, */ //FIXME: tempory replacement for tmpfile // this is here one for debugging purpose. //#define tmpfile xTmpFile - +/* FILE* xTmpFile() { static int fid = 0; char fname[80]; sprintf(fname,"TmpFile.%d",fid++); return fopen(fname,"w"); } - +*/ // History Buffer /////////////////////////////////////////// /* A Row(X) data type which allows adding elements to the end. */ diff --git a/noncore/apps/opie-console/io_layer.cpp b/noncore/apps/opie-console/io_layer.cpp index 9ba2f70..975ee60 100644 --- a/noncore/apps/opie-console/io_layer.cpp +++ b/noncore/apps/opie-console/io_layer.cpp @@ -15,6 +15,8 @@ IOLayer::~IOLayer() { int IOLayer::rawIO()const{ return -1; } void IOLayer::closeRawIO(int) { } +void IOLayer::setSize(int, int ) { +} diff --git a/noncore/apps/opie-console/io_layer.h b/noncore/apps/opie-console/io_layer.h index bf5a893..9c99222 100644 --- a/noncore/apps/opie-console/io_layer.h +++ b/noncore/apps/opie-console/io_layer.h @@ -61,12 +61,14 @@ public: /** * will close the rawIO stuff * and will listen to it's data again... */ virtual void closeRawIO(int); + + signals: /** * received input as QCString */ virtual void received( const QByteArray& ) = 0; @@ -94,9 +96,15 @@ public slots: virtual void close() = 0; /** * closes and reloads the settings */ virtual void reload( const Profile& ) = 0; + + /** + * set the size + * needed for pty + */ + virtual void setSize(int rows, int cols ); }; #endif diff --git a/noncore/apps/opie-console/mainwindow.cpp b/noncore/apps/opie-console/mainwindow.cpp index 1adb43d..1d7a4be 100644 --- a/noncore/apps/opie-console/mainwindow.cpp +++ b/noncore/apps/opie-console/mainwindow.cpp @@ -211,43 +211,47 @@ void MainWindow::slotNew() { if ( ret == QDialog::Accepted ) { create( dlg.profile() ); } } void MainWindow::slotRecordScript() { - if (currentSession()) { +/* if (currentSession()) { currentSession()->emulationLayer()->startRecording(); } + */ } void MainWindow::slotSaveScript() { - if (currentSession() && currentSession()->emulationLayer()->isRecording()) { +/* if (currentSession() && currentSession()->emulationLayer()->isRecording()) { MimeTypes types; QStringList script; script << "text/plain"; types.insert("Script", script); QString filename = OFileDialog::getSaveFileName(2, "/", QString::null, types); if (!filename.isEmpty()) { currentSession()->emulationLayer()->script()->saveTo(filename); currentSession()->emulationLayer()->clearScript(); } } + */ } void MainWindow::slotRunScript() { +/* if (currentSession()) { MimeTypes types; QStringList script; script << "text/plain"; types.insert("Script", script); QString filename = OFileDialog::getOpenFileName(2, "/", QString::null, types); if (!filename.isEmpty()) { Script script(DocLnk(filename).file()); currentSession()->emulationLayer()->runScript(&script); } } + */ } void MainWindow::slotConnect() { if ( currentSession() ) currentSession()->layer()->open(); } diff --git a/noncore/apps/opie-console/opie-console.pro b/noncore/apps/opie-console/opie-console.pro index 26bce83..7990a64 100644 --- a/noncore/apps/opie-console/opie-console.pro +++ b/noncore/apps/opie-console/opie-console.pro @@ -10,64 +10,58 @@ HEADERS = io_layer.h io_serial.h io_irda.h io_bt.h io_modem.h \ profile.h \ profileconfig.h \ profilemanager.h \ configwidget.h \ tabwidget.h \ configdialog.h \ - emulation_layer.h \ - vt102emulation.h \ - common.h \ - history.h \ - screen.h \ keytrans.h \ - widget_layer.h \ transferdialog.h \ profiledialogwidget.h \ profileeditordialog.h \ default.h \ - terminalwidget.h \ iolayerbase.h \ serialconfigwidget.h irdaconfigwidget.h \ btconfigwidget.h modemconfigwidget.h \ atconfigdialog.h dialdialog.h \ - emulation_widget.h procctl.h \ + procctl.h \ function_keyboard.h \ receive_layer.h filereceive.h \ script.h \ - dialer.h + dialer.h \ + emulation_handler.h TECommon.h \ + TEHistroy.h TEScreen.h TEWidget.h \ + TEmuVt102.h TEmulation.h SOURCES = io_layer.cpp io_serial.cpp io_irda.cpp io_bt.cpp io_modem.cpp \ file_layer.cpp filetransfer.cpp \ main.cpp \ metafactory.cpp \ session.cpp \ mainwindow.cpp \ profile.cpp \ profileconfig.cpp \ profilemanager.cpp \ tabwidget.cpp \ configdialog.cpp \ - emulation_layer.cpp \ - vt102emulation.cpp \ - history.cpp \ - screen.cpp \ keytrans.cpp \ - widget_layer.cpp \ transferdialog.cpp \ profiledialogwidget.cpp \ profileeditordialog.cpp \ - terminalwidget.cpp \ iolayerbase.cpp \ serialconfigwidget.cpp irdaconfigwidget.cpp \ btconfigwidget.cpp modemconfigwidget.cpp \ atconfigdialog.cpp dialdialog.cpp \ - emulation_widget.cpp default.cpp procctl.cpp \ + default.cpp procctl.cpp \ function_keyboard.cpp \ receive_layer.cpp filereceive.cpp \ script.cpp \ - dialer.cpp + dialer.cpp \ + emulation_handler.cpp TEHistory.cpp \ + TEScreen.cpp TEWidget.cpp \ + TEmuVt102.cpp TEmulation.cpp + INTERFACES = configurebase.ui editbase.ui INCLUDEPATH += $(OPIEDIR)/include DEPENDPATH += $(OPIEDIR)/include LIBS += -lqpe -lopie TARGET = opie-console diff --git a/noncore/apps/opie-console/profilemanager.cpp b/noncore/apps/opie-console/profilemanager.cpp index 4b88dec..113327c 100644 --- a/noncore/apps/opie-console/profilemanager.cpp +++ b/noncore/apps/opie-console/profilemanager.cpp @@ -4,12 +4,13 @@ #include <qfile.h> #include <qlayout.h> #include <qwidgetstack.h> #include <qpe/config.h> +#include "emulation_handler.h" #include "widget_layer.h" #include "emulation_widget.h" #include "metafactory.h" #include "profileconfig.h" #include "profilemanager.h" @@ -80,18 +81,20 @@ Session* ProfileManager::fromProfile( const Profile& prof, QWidget* parent) { QWidgetStack *stack = new QWidgetStack(parent); session->setWidgetStack( stack ); QWidget* dummy = new QWidget( stack ); QHBoxLayout* lay = new QHBoxLayout(dummy ); stack->addWidget( dummy, 0 ); stack->raiseWidget( 0 ); - WidgetLayer* wid = new EmulationWidget( prof, dummy ); - lay->addWidget( wid ); - - session->setEmulationWidget( wid ); - session->setEmulationLayer( m_fact->newEmulationLayer( m_fact->external( prof.terminalName() ), - wid ) ); + EmulationHandler* handler = new EmulationHandler(prof,dummy ); + lay->addWidget( handler->widget() ); +// WidgetLayer* wid = new EmulationWidget( prof, dummy ); +// lay->addWidget( wid ); + +// session->setEmulationWidget( wid ); +// session->setEmulationLayer( m_fact->newEmulationLayer( m_fact->external( prof.terminalName() ), +// wid ) ); session->connect(); return session; } void ProfileManager::save( ) { QFile::remove( (QString(getenv("HOME") )+ "/Settings/opie-console-profiles.conf" ) ); diff --git a/noncore/apps/opie-console/session.cpp b/noncore/apps/opie-console/session.cpp index ff2c3e2..7cae0df 100644 --- a/noncore/apps/opie-console/session.cpp +++ b/noncore/apps/opie-console/session.cpp @@ -7,75 +7,81 @@ #include "session.h" Session::Session() { m_widget = 0l; m_layer = 0l; - m_widLay = 0l; - m_emLay = 0l; +// m_widLay = 0l; +// m_emLay = 0l; } Session::Session( const QString& na, QWidgetStack* widget, IOLayer* lay) : m_name( na ), m_widget( widget ), m_layer( lay ) { - m_widLay = 0l; - m_emLay = 0l; +// m_widLay = 0l; +// m_emLay = 0l; } Session::~Session() { delete m_layer; - delete m_emLay; +// delete m_emLay; delete m_widget; /* the widget layer should be deleted by the m_widget */ } QString Session::name()const { return m_name; } QWidgetStack* Session::widgetStack() { return m_widget; } IOLayer* Session::layer() { return m_layer; } -EmulationLayer* Session::emulationLayer() { +/*EmulationLayer* Session::emulationLayer() { return m_emLay; } WidgetLayer* Session::emulationWidget() { return m_widLay; } +*/ void Session::connect() { - if ( !m_layer || !m_emLay ) +/* if ( !m_layer || !m_emLay ) return; QObject::connect(m_layer, SIGNAL(received(const QByteArray&) ), m_emLay, SLOT(onRcvBlock(const QByteArray&) ) ); QObject::connect(m_emLay, SIGNAL(sndBlock(const QByteArray&) ), m_layer, SLOT(send(const QByteArray&) ) ); + */ } void Session::disconnect() { +/* if ( !m_layer || !m_emLay ) return; QObject::disconnect(m_layer, SIGNAL(received(const QByteArray&) ), m_emLay, SLOT(onRcvBlock(const QByteArray&) ) ); QObject::disconnect(m_emLay, SIGNAL(sndBlock(const QByteArray&) ), m_layer, SLOT(send(const QByteArray&) ) ); + */ } void Session::setName( const QString& na){ m_name = na; } void Session::setWidgetStack( QWidgetStack* wid ) { delete m_widget; /* the EmulationLayer was destroyed... */ - delete m_emLay; + //delete m_emLay; m_widget = wid; } void Session::setIOLayer( IOLayer* lay ) { delete m_layer; m_layer = lay; } +/* void Session::setEmulationLayer( EmulationLayer* lay ) { delete m_emLay; m_emLay = lay; } void Session::setEmulationWidget( WidgetLayer* lay ) { delete m_widLay; m_widLay = lay; } +*/
\ No newline at end of file diff --git a/noncore/apps/opie-console/session.h b/noncore/apps/opie-console/session.h index 64c2cdb..04bf257 100644 --- a/noncore/apps/opie-console/session.h +++ b/noncore/apps/opie-console/session.h @@ -38,13 +38,13 @@ public: /** * return the layer */ IOLayer* layer(); - EmulationLayer* emulationLayer(); +// EmulationLayer* emulationLayer(); WidgetLayer* emulationWidget(); /* * connects the data flow from * the IOLayer to the EmulationLayer */ @@ -54,21 +54,21 @@ public: * disconnect the dataflow * this will be done for ft */ void disconnect(); void setWidgetStack( QWidgetStack* widget ); - void setEmulationLayer( EmulationLayer* lay ); - void setEmulationWidget( WidgetLayer* lay ); +// void setEmulationLayer( EmulationLayer* lay ); +// void setEmulationWidget( WidgetLayer* lay ); void setIOLayer( IOLayer* ); void setName( const QString& ); private: QString m_name; QWidgetStack* m_widget; IOLayer* m_layer; - EmulationLayer* m_emLay; - WidgetLayer* m_widLay; +// EmulationLayer* m_emLay; +// WidgetLayer* m_widLay; }; #endif |