/* This file is part of Konsole, an X terminal. Copyright (C) 1997,1998 by Lars Doelle 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 #include #include #include #include #include #include #include #include #include #include #include #include #include /*! \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"< 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"<setSize()"<setSize(te->Lines(),te->Columns()); // not absolutely nessesary sh->useUtf8(em->utf8()); //kdDebug(1211)<<"TESession ctor() connecting"<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 "<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!"<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(""); 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 <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: "<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 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"