You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdebase/konsole/konsole/session.cpp

846 lines
20 KiB

/*
This file is part of Konsole, an X terminal.
Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.
*/
#include "session.h"
#include "zmodem_dialog.h"
#include <kdebug.h>
#include <dcopclient.h>
#include <tdemessagebox.h>
#include <knotifyclient.h>
#include <tdelocale.h>
#include <kprocio.h>
#include <krun.h>
#include <kshell.h>
#include <kstandarddirs.h>
#include <stdlib.h>
#include <tqfile.h>
#include <tqdir.h>
#include <tqregexp.h>
#include <tqtextedit.h>
/*! \class TESession
Sessions are combinations of TEPTy and Emulations.
The stuff in here does not belong to the terminal emulation framework,
but to main.cpp. It serves it's duty by providing a single reference
to TEPTy/Emulation pairs. In fact, it is only there to demonstrate one
of the abilities of the framework - multible sessions.
*/
TESession::TESession(TEWidget* _te, const TQString &_term, ulong _winId, const TQString &_sessionId, const TQString &_initial_cwd)
: DCOPObject( _sessionId.latin1() )
, sh(0)
, connected(true)
, monitorActivity(false)
, monitorSilence(false)
, notifiedActivity(false)
, masterMode(false)
, autoClose(true)
, wantedClose(false)
, schema_no(0)
, font_no(3)
, silence_seconds(10)
, add_to_utmp(true)
, xon_xoff(false)
, pgm(TQString())
, args(TQStrList())
, sessionId(_sessionId)
, cwd("")
, initial_cwd(_initial_cwd)
, zmodemBusy(false)
, zmodemProc(0)
, zmodemProgress(0)
, encoding_no(0)
{
//kdDebug(1211)<<"TESession ctor() new TEPty"<<endl;
te = _te;
//kdDebug(1211)<<"TESession ctor() new TEmuVt102"<<endl;
em = new TEmuVt102(te);
font_h = te-> fontHeight();
font_w = te-> fontWidth();
TQObject::connect(te,TQT_SIGNAL(changedContentSizeSignal(int,int)),
this,TQT_SLOT(onContentSizeChange(int,int)));
TQObject::connect(te,TQT_SIGNAL(changedFontMetricSignal(int,int)),
this,TQT_SLOT(onFontMetricChange(int,int)));
term = _term;
winId = _winId;
iconName = "konsole";
setPty( new TEPty() );
connect( em, TQT_SIGNAL( changeTitle( int, const TQString & ) ),
this, TQT_SLOT( setUserTitle( int, const TQString & ) ) );
connect( em, TQT_SIGNAL( notifySessionState(int) ),
this, TQT_SLOT( notifySessionState(int) ) );
monitorTimer = new TQTimer(this);
connect(monitorTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(monitorTimerDone()));
connect( em, TQT_SIGNAL( zmodemDetected() ), this, TQT_SLOT(slotZModemDetected()));
connect( em, TQT_SIGNAL( changeTabTextColor( int ) ),
this, TQT_SLOT( changeTabTextColor( int ) ) );
//kdDebug(1211)<<"TESession ctor() done"<<endl;
}
void TESession::setPty(TEPty *_sh)
{
if ( sh ) {
delete sh;
}
sh = _sh;
connect( sh, TQT_SIGNAL( forkedChild() ),
this, TQT_SIGNAL( forkedChild() ));
//kdDebug(1211)<<"TESession ctor() sh->setSize()"<<endl;
sh->setSize(te->Lines(),te->Columns()); // not absolutely nessesary
sh->useUtf8(em->utf8());
//kdDebug(1211)<<"TESession ctor() connecting"<<endl;
connect( sh,TQT_SIGNAL(block_in(const char*,int)),this,TQT_SLOT(onRcvBlock(const char*,int)) );
connect( em,TQT_SIGNAL(sndBlock(const char*,int)),sh,TQT_SLOT(send_bytes(const char*,int)) );
connect( em,TQT_SIGNAL(lockPty(bool)),sh,TQT_SLOT(lockPty(bool)) );
connect( em,TQT_SIGNAL(useUtf8(bool)),sh,TQT_SLOT(useUtf8(bool)) );
connect( sh,TQT_SIGNAL(done(int)), this,TQT_SLOT(done(int)) );
if (!sh->error().isEmpty())
TQTimer::singleShot(0, this, TQT_SLOT(ptyError()));
}
void TESession::ptyError()
{
// FIXME: sh->error() is always empty
if ( sh->error().isEmpty() )
KMessageBox::error( te->topLevelWidget(),
i18n("Konsole is unable to open a PTY (pseudo teletype). It is likely that this is due to an incorrect configuration of the PTY devices. Konsole needs to have read/write access to the PTY devices."),
i18n("A Fatal Error Has Occurred") );
else
KMessageBox::error(te->topLevelWidget(), sh->error());
emit done(this);
}
void TESession::changeWidget(TEWidget* w)
{
TQObject::disconnect(te,TQT_SIGNAL(changedContentSizeSignal(int,int)),
this,TQT_SLOT(onContentSizeChange(int,int)));
TQObject::disconnect(te,TQT_SIGNAL(changedFontMetricSignal(int,int)),
this,TQT_SLOT(onFontMetricChange(int,int)));
te=w;
em->changeGUI(w);
font_h = te->fontHeight();
font_w = te->fontWidth();
sh->setSize(te->Lines(),te->Columns()); // not absolutely nessesary
te->setDefaultBackColor(modifiedBackground);
TQObject::connect(te,TQT_SIGNAL(changedContentSizeSignal(int,int)),
this,TQT_SLOT(onContentSizeChange(int,int)));
TQObject::connect(te,TQT_SIGNAL(changedFontMetricSignal(int,int)),
this,TQT_SLOT(onFontMetricChange(int,int)));
}
void TESession::setProgram( const TQString &_pgm, const TQStrList &_args )
{
pgm = _pgm;
args = _args;
}
void TESession::run()
{
// Upon a KPty error, there is no description on what that error was...
// Check to see if the given program is executable.
TQString exec = TQFile::encodeName(pgm);
exec = KRun::binaryName(exec, false);
exec = KShell::tildeExpand(exec);
TQString pexec = TDEGlobal::dirs()->findExe(exec);
if ( pexec.isEmpty() ) {
kdError()<<"can not execute "<<exec<<endl;
TQTimer::singleShot(1, this, TQT_SLOT(done()));
return;
}
TQString appId=kapp->dcopClient()->appId();
TQString cwd_save = TQDir::currentDirPath();
if (!initial_cwd.isEmpty())
TQDir::setCurrent(initial_cwd);
sh->setXonXoff(xon_xoff);
int result = sh->run(TQFile::encodeName(pgm), args, term.latin1(),
winId, add_to_utmp,
("DCOPRef("+appId+",konsole)").latin1(),
("DCOPRef("+appId+","+sessionId+")").latin1());
if (result < 0) { // Error in opening pseudo teletype
kdWarning()<<"Unable to open a pseudo teletype!"<<endl;
TQTimer::singleShot(0, this, TQT_SLOT(ptyError()));
}
sh->setErase(em->getErase());
if (!initial_cwd.isEmpty())
TQDir::setCurrent(cwd_save);
else
initial_cwd=cwd_save;
sh->setWriteable(false); // We are reachable via kwrited.
}
void TESession::changeTabTextColor( int color )
{
emit changeTabTextColor( this, color );
}
void TESession::setUserTitle( int what, const TQString &caption )
{
// (btw: what=0 changes title and icon, what=1 only icon, what=2 only title
if ((what == 0) || (what == 2))
userTitle = caption;
if ((what == 0) || (what == 1))
iconText = caption;
if (what == 11) {
TQString colorString = caption.section(';',0,0);
TQColor backColor = TQColor(colorString);
if (backColor.isValid()){// change color via \033]11;Color\007
if (backColor != modifiedBackground) {
modifiedBackground = backColor;
te->setDefaultBackColor(backColor);
}
}
}
if (what == 30)
renameSession(caption);
if (what == 31) {
cwd=caption;
cwd=cwd.replace( TQRegExp("^~"), TQDir::homeDirPath() );
emit openURLRequest(cwd);
}
if (what == 32) { // change icon via \033]32;Icon\007
iconName = caption;
te->update();
}
emit updateTitle(this);
}
TQString TESession::fullTitle() const
{
TQString res = title;
if ( !userTitle.isEmpty() )
res = userTitle + " - " + res;
return res;
}
void TESession::monitorTimerDone()
{
if (monitorSilence) {
KNotifyClient::event(winId, "Silence", i18n("Silence in session '%1'").arg(title));
emit notifySessionState(this,NOTIFYSILENCE);
}
notifiedActivity=false;
}
void TESession::notifySessionState(int state)
{
if (state==NOTIFYBELL) {
te->Bell(em->isConnected(),i18n("Bell in session '%1'").arg(title));
} else if (state==NOTIFYACTIVITY) {
if (monitorSilence) {
monitorTimer->start(silence_seconds*1000,true);
}
if (!monitorActivity)
return;
if (!notifiedActivity) {
KNotifyClient::event(winId, "Activity", i18n("Activity in session '%1'").arg(title));
notifiedActivity=true;
monitorTimer->start(silence_seconds*1000,true);
}
}
emit notifySessionState(this, state);
}
void TESession::onContentSizeChange(int height, int width)
{
// ensure that image is at least one line high by one column wide
const int columns = TQMAX( width/font_w , 1 );
const int lines = TQMAX( height/font_h , 1 );
em->onImageSizeChange( lines , columns );
sh->setSize( lines , columns );
}
void TESession::onFontMetricChange(int height, int width)
{
//kdDebug(1211)<<"TESession::onFontMetricChange " << height << " " << width << endl;
if (connected) {
font_h = height;
font_w = width;
}
}
bool TESession::sendSignal(int signal)
{
return sh->kill(signal);
}
bool TESession::closeSession()
{
autoClose = true;
wantedClose = true;
if (!sh->isRunning() || !sendSignal(SIGHUP))
{
// Forced close.
TQTimer::singleShot(1, this, TQT_SLOT(done()));
}
return true;
}
void TESession::feedSession(const TQString &text)
{
emit disableMasterModeConnections();
setListenToKeyPress(true);
te->emitText(text);
setListenToKeyPress(false);
emit enableMasterModeConnections();
}
void TESession::sendSession(const TQString &text)
{
TQString newtext=text;
newtext.append("\r");
feedSession(newtext);
}
void TESession::renameSession(const TQString &name)
{
title=name;
emit renameSession(this,name);
}
TESession::~TESession()
{
//kdDebug(1211) << "disconnnecting..." << endl;
TQObject::disconnect( sh, TQT_SIGNAL( done(int) ),
this, TQT_SLOT( done(int) ) );
delete em;
delete sh;
delete zmodemProc;
}
void TESession::setConnect(bool c)
{
connected=c;
em->setConnect(c);
setListenToKeyPress(c);
}
void TESession::setListenToKeyPress(bool l)
{
em->setListenToKeyPress(l);
}
void TESession::done() {
emit processExited(sh);
emit done(this);
}
void TESession::done(int exitStatus)
{
if (!autoClose)
{
userTitle = i18n("<Finished>");
emit updateTitle(this);
return;
}
if (!wantedClose && (exitStatus || sh->signalled()))
{
if (sh->normalExit())
KNotifyClient::event(winId, "Finished", i18n("Session '%1' exited with status %2.").arg(title).arg(exitStatus));
else if (sh->signalled())
{
if (sh->coreDumped())
KNotifyClient::event(winId, "Finished", i18n("Session '%1' exited with signal %2 and dumped core.").arg(title).arg(sh->exitSignal()));
else
KNotifyClient::event(winId, "Finished", i18n("Session '%1' exited with signal %2.").arg(title).arg(sh->exitSignal()));
}
else
KNotifyClient::event(winId, "Finished", i18n("Session '%1' exited unexpectedly.").arg(title));
}
emit processExited(sh);
emit done(this);
}
void TESession::terminate()
{
delete this;
}
TEmulation* TESession::getEmulation()
{
return em;
}
// following interfaces might be misplaced ///
int TESession::schemaNo()
{
return schema_no;
}
int TESession::encodingNo()
{
return encoding_no;
}
int TESession::keymapNo()
{
return em->keymapNo();
}
TQString TESession::keymap()
{
return em->keymap();
}
int TESession::fontNo()
{
return font_no;
}
const TQString & TESession::Term()
{
return term;
}
const TQString & TESession::SessionId()
{
return sessionId;
}
void TESession::setSchemaNo(int sn)
{
schema_no = sn;
}
void TESession::setEncodingNo(int index)
{
encoding_no = index;
}
void TESession::setKeymapNo(int kn)
{
em->setKeymap(kn);
}
void TESession::setKeymap(const TQString &id)
{
em->setKeymap(id);
}
void TESession::setFontNo(int fn)
{
font_no = fn;
}
void TESession::setMetaAsAltMode(bool mode)
{
if (em)
em->setMetaKeyMode(mode);
}
void TESession::setTitle(const TQString& _title)
{
title = _title;
//kdDebug(1211)<<"Session setTitle " << title <<endl;
}
const TQString& TESession::Title()
{
return title;
}
void TESession::setIconName(const TQString& _iconName)
{
iconName = _iconName;
}
void TESession::setIconText(const TQString& _iconText)
{
iconText = _iconText;
//kdDebug(1211)<<"Session setIconText " << iconText <<endl;
}
const TQString& TESession::IconName()
{
return iconName;
}
const TQString& TESession::IconText()
{
return iconText;
}
bool TESession::testAndSetStateIconName (const TQString& newname)
{
if (newname != stateIconName)
{
stateIconName = newname;
return true;
}
return false;
}
void TESession::setHistory(const HistoryType &hType)
{
em->setHistory(hType);
}
const HistoryType& TESession::history()
{
return em->history();
}
void TESession::clearHistory()
{
if (history().isOn()) {
int histSize = history().getSize();
setHistory(HistoryTypeNone());
if (histSize)
setHistory(HistoryTypeBuffer(histSize));
else
setHistory(HistoryTypeFile());
}
}
TQStrList TESession::getArgs()
{
return args;
}
TQString TESession::getPgm()
{
return pgm;
}
TQString TESession::getCwd()
{
#ifdef HAVE_PROC_CWD
if (cwd.isEmpty()) {
TQFileInfo Cwd(TQString("/proc/%1/cwd").arg(sh->pid()));
if(Cwd.isSymLink())
return Cwd.readLink();
}
#endif /* HAVE_PROC_CWD */
return cwd;
}
bool TESession::isMonitorActivity() { return monitorActivity; }
bool TESession::isMonitorSilence() { return monitorSilence; }
bool TESession::isMasterMode() { return masterMode; }
void TESession::setMonitorActivity(bool _monitor)
{
monitorActivity=_monitor;
notifiedActivity=false;
}
void TESession::setMonitorSilence(bool _monitor)
{
if (monitorSilence==_monitor)
return;
monitorSilence=_monitor;
if (monitorSilence)
monitorTimer->start(silence_seconds*1000,true);
else
monitorTimer->stop();
}
void TESession::setMonitorSilenceSeconds(int seconds)
{
silence_seconds=seconds;
if (monitorSilence) {
monitorTimer->start(silence_seconds*1000,true);
}
}
void TESession::setMasterMode(bool _master)
{
masterMode=_master;
}
void TESession::setAddToUtmp(bool set)
{
add_to_utmp = set;
}
void TESession::setXonXoff(bool set)
{
xon_xoff = set;
}
void TESession::slotZModemDetected()
{
if (!zmodemBusy)
{
TQTimer::singleShot(10, this, TQT_SLOT(emitZModemDetected()));
zmodemBusy = true;
}
}
void TESession::emitZModemDetected()
{
emit zmodemDetected(this);
}
void TESession::cancelZModem()
{
sh->send_bytes("\030\030\030\030", 4); // Abort
zmodemBusy = false;
}
void TESession::startZModem(const TQString &zmodem, const TQString &dir, const TQStringList &list)
{
zmodemBusy = true;
zmodemProc = new KProcIO;
(*zmodemProc) << zmodem << "-v";
for(TQStringList::ConstIterator it = list.begin();
it != list.end();
++it)
{
(*zmodemProc) << (*it);
}
if (!dir.isEmpty())
zmodemProc->setWorkingDirectory(dir);
zmodemProc->start(KProcIO::NotifyOnExit, false);
// Override the read-processing of KProcIO
disconnect(zmodemProc,TQT_SIGNAL (receivedStdout (TDEProcess *, char *, int)), 0, 0);
connect(zmodemProc,TQT_SIGNAL (receivedStdout (TDEProcess *, char *, int)),
this, TQT_SLOT(zmodemSendBlock(TDEProcess *, char *, int)));
connect(zmodemProc,TQT_SIGNAL (receivedStderr (TDEProcess *, char *, int)),
this, TQT_SLOT(zmodemStatus(TDEProcess *, char *, int)));
connect(zmodemProc,TQT_SIGNAL (processExited(TDEProcess *)),
this, TQT_SLOT(zmodemDone()));
disconnect( sh,TQT_SIGNAL(block_in(const char*,int)), this, TQT_SLOT(onRcvBlock(const char*,int)) );
connect( sh,TQT_SIGNAL(block_in(const char*,int)), this, TQT_SLOT(zmodemRcvBlock(const char*,int)) );
connect( sh,TQT_SIGNAL(buffer_empty()), this, TQT_SLOT(zmodemContinue()));
zmodemProgress = new ZModemDialog(te->topLevelWidget(), false,
i18n("ZModem Progress"));
connect(zmodemProgress, TQT_SIGNAL(user1Clicked()),
this, TQT_SLOT(zmodemDone()));
zmodemProgress->show();
}
void TESession::zmodemSendBlock(TDEProcess *, char *data, int len)
{
sh->send_bytes(data, len);
// tqWarning("<-- %d bytes", len);
if (sh->buffer_full())
{
zmodemProc->suspend();
// tqWarning("ZModem suspend");
}
}
void TESession::zmodemContinue()
{
zmodemProc->resume();
// tqWarning("ZModem resume");
}
void TESession::zmodemStatus(TDEProcess *, char *data, int len)
{
TQCString msg(data, len+1);
while(!msg.isEmpty())
{
int i = msg.find('\015');
int j = msg.find('\012');
TQCString txt;
if ((i != -1) && ((j == -1) || (i < j)))
{
msg = msg.mid(i+1);
}
else if (j != -1)
{
txt = msg.left(j);
msg = msg.mid(j+1);
}
else
{
txt = msg;
msg.truncate(0);
}
if (!txt.isEmpty())
zmodemProgress->addProgressText(TQString::fromLocal8Bit(txt));
}
}
void TESession::zmodemRcvBlock(const char *data, int len)
{
TQByteArray ba;
ba.duplicate(data, len);
zmodemProc->writeStdin(ba);
// tqWarning("--> %d bytes", len);
}
void TESession::zmodemDone()
{
if (zmodemProc)
{
delete zmodemProc;
zmodemProc = 0;
zmodemBusy = false;
disconnect( sh,TQT_SIGNAL(block_in(const char*,int)), this ,TQT_SLOT(zmodemRcvBlock(const char*,int)) );
disconnect( sh,TQT_SIGNAL(buffer_empty()), this, TQT_SLOT(zmodemContinue()));
connect( sh,TQT_SIGNAL(block_in(const char*,int)), this, TQT_SLOT(onRcvBlock(const char*,int)) );
sh->send_bytes("\030\030\030\030", 4); // Abort
sh->send_bytes("\001\013\n", 3); // Try to get prompt back
zmodemProgress->done();
}
}
bool TESession::processDynamic(const TQCString &fun, const TQByteArray &data, TQCString& replyType, TQByteArray &replyData)
{
if (fullScripting)
{
if (fun == "feedSession(TQString)")
{
TQString arg0;
TQDataStream arg( data, IO_ReadOnly );
arg >> arg0;
feedSession(arg0);
replyType = "void";
return true;
}
else if (fun == "sendSession(TQString)")
{
TQString arg0;
TQDataStream arg( data, IO_ReadOnly );
arg >> arg0;
sendSession(arg0);
replyType = "void";
return true;
}
}
return SessionIface::processDynamic(fun, data, replyType, replyData);
}
QCStringList TESession::functionsDynamic()
{
QCStringList funcs = SessionIface::functionsDynamic();
if (fullScripting)
{
funcs << "void feedSession(TQString text)";
funcs << "void sendSession(TQString text)";
}
return funcs;
}
void TESession::onRcvBlock( const char* buf, int len )
{
em->onRcvBlock( buf, len );
emit receivedData( TQString::fromLatin1( buf, len ) );
}
void TESession::print( TQPainter &paint, bool friendly, bool exact )
{
te->print(paint, friendly, exact);
}
TQString TESession::schema()
{
TQString currentSchema;
emit getSessionSchema(this, currentSchema);
return currentSchema;
}
void TESession::setSchema(const TQString &schema)
{
emit setSessionSchema(this, schema);
}
TQString TESession::font()
{
return te->getVTFont().toString();
}
void TESession::setFont(const TQString &font)
{
TQFont tmp;
if (tmp.fromString(font))
te->setVTFont(tmp);
else
kdWarning()<<"unknown font: "<<font<<endl;
}
TQString TESession::encoding()
{
return em->codec()->name();
}
void TESession::setEncoding(const TQString &encoding)
{
emit setSessionEncoding(this, encoding);
}
TQString TESession::keytab()
{
return keymap();
}
void TESession::setKeytab(const TQString &keytab)
{
setKeymap(keytab);
emit updateSessionKeytab(this, keytab);
emit updateSessionConfig(this);
}
TQSize TESession::size()
{
return em->imageSize();
}
void TESession::setSize(TQSize size)
{
if ((size.width() <= 1) || (size.height() <= 1))
return;
emit resizeSession(this, size);
}
#include "session.moc"