/*
* Copyright (c) 2002-2006 Samit Basu
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <qapplication.h>
#include "Serialize.hpp"
#include "Terminal.hpp"
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef Q_WS_X11
#include <term.h>
#include <curses.h>
#include <string>
#include <sys/ioctl.h>
#include "Exception.hpp"
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <glob.h>
#endif
#include "File.hpp"
#define KM_ESC 0x1b
// Automake on OS X does not support conditionals in sources... so we
// have the braindead solution of ifdef-ing out all the stuff that
// will choke. Sigh.
// Set up the terminal in raw mode, and initialize the control
// strings.
Terminal::Terminal() {
}
void Terminal::Initialize() {
RetrieveTerminalName();
SetRawMode();
SetupControlStrings();
ResizeEvent();
// SetInterface(this);
state = 0;
}
Terminal::~Terminal() {
}
void Terminal::SetRawMode() {
#ifdef Q_WS_X11
tcgetattr(STDIN_FILENO, &oldattr);
newattr = oldattr;
newattr.c_lflag &= ~(ECHO | ICANON | IEXTEN);
newattr.c_iflag &= ~(ICRNL | INPCK | ISTRIP);
newattr.c_cflag &= ~(CSIZE | PARENB);
newattr.c_cflag |= CS8;
newattr.c_oflag &= ~(OPOST);
newattr.c_cc[VMIN] = 1;
newattr.c_cc[VTIME] = 0;
while (tcsetattr(STDIN_FILENO, TCSADRAIN, &newattr)) {
if (errno != EINTR)
throw Exception(string("Unable to set up terminal attributes: tcsetattr error:") + strerror(errno));
}
#endif
}
void Terminal::RestoreOriginalMode() {
#ifdef Q_WS_X11
// Restore the original terminal setttings
while (tcsetattr(STDIN_FILENO, TCSADRAIN, &oldattr)) {
if (errno != EINTR)
throw Exception(string("Unable to set up terminal attributes: tcsetattr error:") + strerror(errno));
}
#endif
}
void Terminal::RetrieveTerminalName() {
#ifdef Q_WS_X11
term = getenv("TERM");
if (!term)
throw Exception("Unable to retrieve terminal name!");
if (setupterm((char*) term, STDIN_FILENO, NULL) == ERR)
throw Exception(string("Unable to retrieve terminal info for ") + term);
#endif
}
const char* Terminal::LookupControlString(const char *name) {
#ifdef Q_WS_X11
const char* value = tigetstr((char*)name);
if (!value || value == (char*) -1)
return NULL;
else
return strdup(value);
#endif
}
void Terminal::SetupControlStrings() {
#ifdef Q_WS_X11
left = LookupControlString("cub1");
right = LookupControlString("cuf1");
up = LookupControlString("cuu1");
down = LookupControlString("cud1");
home = LookupControlString("home");
clear_eol = LookupControlString("el");
clear_eod = LookupControlString("ed");
u_arrow = LookupControlString("kcuu1");
d_arrow = LookupControlString("kcud1");
l_arrow = LookupControlString("kcub1");
r_arrow = LookupControlString("kcuf1");
const char* delstr = LookupControlString("kdch1");
// Store these in the esc_seq_array for later lookup
esc_seq_array = (mapping*) malloc(13*sizeof(mapping));
esc_seq_count = 13;
// These are the ones provided by terminfo
esc_seq_array[0].sequence = l_arrow;
esc_seq_array[0].keycode = 0x101;
esc_seq_array[1].sequence = r_arrow;
esc_seq_array[1].keycode = 0x102;
esc_seq_array[2].sequence = u_arrow;
esc_seq_array[2].keycode = 0x103;
esc_seq_array[3].sequence = d_arrow;
esc_seq_array[3].keycode = 0x104;
// These are defaults...
esc_seq_array[4].sequence = "\033[D";
esc_seq_array[4].keycode = 0x101;
esc_seq_array[5].sequence = "\033[C";
esc_seq_array[5].keycode = 0x102;
esc_seq_array[6].sequence = "\033[A";
esc_seq_array[6].keycode = 0x103;
esc_seq_array[7].sequence = "\033[B";
esc_seq_array[7].keycode = 0x104;
// These are defaults...
esc_seq_array[8].sequence = "\033OD";
esc_seq_array[8].keycode = 0x101;
esc_seq_array[9].sequence = "\033OC";
esc_seq_array[9].keycode = 0x102;
esc_seq_array[10].sequence = "\033OA";
esc_seq_array[10].keycode = 0x103;
esc_seq_array[11].sequence = "\033OB";
esc_seq_array[11].keycode = 0x104;
// The delete character...
esc_seq_array[12].sequence = delstr;
esc_seq_array[12].keycode = 0x108;
#endif
}
// Translate the given character (which is a raw
// character) - this traps escape sequences and
// maps them to integer constants (as expected
// by, e.g. KeyManager).
void Terminal::ProcessChar(char c) {
// Check our current state, if it is zero, we are not
// in the middle of an escape sequence.
if (state == 0) {
if (c != KM_ESC) {
emit OnChar(c);
return;
} else {
escseq[0] = KM_ESC;
state = 1;
return;
}
}
// We are in the middle of an escape sequence.
// Append the current character to the escseq
// buffer, increase the state number.
escseq[state] = c; escseq[state+1] = 0;
state++;
// Now we check against the contents of esc-seq-array for
// a match
int matchnum = 0;
bool isprefix = false;
bool matchfound = false;
while (!matchfound && (matchnum < esc_seq_count)) {
matchfound = (strcmp(escseq,esc_seq_array[matchnum].sequence)==0);
isprefix = isprefix | (strncmp(escseq,esc_seq_array[matchnum].sequence,state)==0);
if (!matchfound) matchnum++;
}
// Did we find a match? If so, convert to an extended key
// code (by adding 0x101), reset state to 0, and call the callback.
if (matchfound) {
state = 0;
emit OnChar(esc_seq_array[matchnum].keycode);
return;
}
// Not a prefix of any known codes... ignore it
if (!isprefix) {
state = 0;
}
}
// The terminal has been resized - calculate the
void Terminal::ResizeEvent() {
#ifdef Q_WS_X11
int lines_used; /* The number of lines currently in use */
struct winsize size; /* The new size information */
int ncolumns;
int i;
/*
* Query the new terminal window size. Ignore invalid responses.
*/
if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == 0 &&
size.ws_row > 0 && size.ws_col > 0) {
nline = size.ws_row;
ncolumns = size.ws_col;
};
emit SetTextWidth(ncolumns);
#endif
}
void Terminal::MoveDown() {
#ifdef Q_WS_X11
tputs(down,1,putchar);
fflush(stdout);
#endif
}
void Terminal::MoveUp() {
#ifdef Q_WS_X11
tputs(up,1,putchar);
fflush(stdout);
#endif
}
void Terminal::MoveRight() {
#ifdef Q_WS_X11
tputs(right,1,putchar);
fflush(stdout);
#endif
}
void Terminal::MoveLeft() {
#ifdef Q_WS_X11
tputs(left,1,putchar);
fflush(stdout);
#endif
}
void Terminal::ClearEOL() {
#ifdef Q_WS_X11
tputs(clear_eol,1,putchar);
fflush(stdout);
#endif
}
void Terminal::ClearEOD() {
#ifdef Q_WS_X11
tputs(clear_eod,nline,putchar);
fflush(stdout);
#endif
}
void Terminal::MoveBOL() {
#ifdef Q_WS_X11
// tputs(home,1,putchar);
putchar('\r');
fflush(stdout);
#endif
}
void Terminal::OutputRawString(string txt) {
#ifdef Q_WS_X11
int ndone = 0; /* The number of characters written so far */
/*
* How long is the string to be written?
*/
int slen = txt.size();
/*
* Attempt to write the string to the terminal, restarting the
* write if a signal is caught.
*/
while(ndone < slen) {
int nnew = write(STDOUT_FILENO, txt.c_str() + ndone,
sizeof(char)*(slen-ndone));
if(nnew > 0)
ndone += nnew;
else if(errno != EINTR && errno != EAGAIN) {
perror("write");
throw Exception("stop");
}
}
#endif
}
void Terminal::DoRead() {
#ifdef Q_WS_X11
char c;
while (read(STDIN_FILENO, &c, 1) == 1)
ProcessChar(c);
#endif
}
void Terminal::ClearDisplay() {
#ifdef Q_WS_X11
tputs(home,1,putchar);
tputs(clear_eod,nline,putchar);
fflush(stdout);
#endif
}
syntax highlighted by Code2HTML, v. 0.9.1