File indexing completed on 2025-01-19 04:23:28

0001 /*
0002     This file is part of Konsole QML plugin,
0003     which is a terminal emulator from KDE.
0004 
0005     Copyright 2013      by Dmitry Zagnoyko <hiroshidi@gmail.com>
0006 
0007     This program is free software; you can redistribute it and/or modify
0008     it under the terms of the GNU General Public License as published by
0009     the Free Software Foundation; either version 2 of the License, or
0010     (at your option) any later version.
0011 
0012     This program is distributed in the hope that it will be useful,
0013     but WITHOUT ANY WARRANTY; without even the implied warranty of
0014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0015     GNU General Public License for more details.
0016 
0017     You should have received a copy of the GNU General Public License
0018     along with this program; if not, write to the Free Software
0019     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0020     02110-1301  USA.
0021 */
0022 
0023 // Own
0024 #include "ksession.h"
0025 #include <KShell>
0026 
0027 // Qt
0028 #include <QTextCodec>
0029 #include <QDir>
0030 #include <QDebug>
0031 
0032 // Konsole
0033 #include "KeyboardTranslator.h"
0034 #include "HistorySearch.h"
0035 #include "History.h"
0036 
0037 #include "Emulation.h"
0038 
0039 KSession::KSession(QObject *parent) :
0040     QObject(parent), m_session(createSession(""))
0041 {
0042     connect(m_session, SIGNAL(started()), this, SIGNAL(started()));
0043     connect(m_session, SIGNAL(finished()), this, SLOT(sessionFinished()));
0044     connect(m_session, SIGNAL(titleChanged()), this, SIGNAL(titleChanged()));
0045     connect(m_session, &Session::stateChanged, [this](int state)
0046     {
0047         qDebug() << m_session->iconText() << m_session->iconName() << m_session->isMonitorSilence() << m_session->program() << state;
0048      Q_EMIT hasActiveProcessChanged();     
0049      
0050      if(m_processName != m_session->foregroundProcessName())
0051      {
0052          m_processName = m_session->foregroundProcessName();
0053          Q_EMIT foregroundProcessNameChanged();
0054      }
0055     });
0056     
0057     // m_session->setMonitorSilence(true);
0058     m_session->setMonitorSilenceSeconds(30);
0059     
0060     connect(m_session, &Session::bellRequest, [this](QString message)
0061     {
0062         Q_EMIT bellRequest(message);
0063     });
0064     
0065     connect(m_session, &Session::changeTabTextColorRequest, [this](int state)
0066     {
0067         qDebug() << "changeTabTextColorRequest" << state;
0068     });
0069     
0070     connect(m_session, &Session::changeTabTextColorRequest, [this](int state)
0071     {
0072         qDebug() << "changeTabTextColorRequest" << state;
0073     });
0074     
0075     connect(m_session, &Session::changeBackgroundColorRequest, [this](QColor state)
0076     {
0077         qDebug() << "changeBackgroundColorRequest" << state;
0078     });
0079     
0080     connect(m_session, &Session::openUrlRequest, [this](QString state)
0081     {
0082         qDebug() << "openUrlRequest" << state;
0083     });
0084     
0085     connect(m_session, &Session::activity, [this]()
0086     {
0087         qDebug() << "activity";
0088         Q_EMIT processHasSilent(false);
0089     });
0090     
0091     connect(m_session, &Session::silence, [this]()
0092     {
0093         qDebug() << "silence";
0094         Q_EMIT processHasSilent(true);
0095     });
0096 }
0097 
0098 KSession::~KSession()
0099 {
0100     if (m_session) {
0101         m_session->close();
0102         m_session->disconnect();
0103         delete m_session;
0104     }
0105 }
0106 
0107 void KSession::setMonitorSilence(bool value)
0108 {
0109     if(m_session->isMonitorSilence() == value)
0110         return;
0111         
0112     m_session->setMonitorSilence(value);
0113     Q_EMIT monitorSilenceChanged();
0114 }
0115 
0116 bool KSession::monitorSilence() const
0117 {
0118     return m_session->isMonitorSilence();
0119 }
0120 
0121 void KSession::setTitle(QString name)
0122 {
0123     m_session->setTitle(Session::NameRole, name);
0124 }
0125 
0126 Session *KSession::createSession(QString name)
0127 {
0128     Session *session = new Session();
0129 
0130     session->setTitle(Session::NameRole, name);
0131 
0132     /* Thats a freaking bad idea!!!!
0133      * /bin/bash is not there on every system
0134      * better set it to the current $SHELL
0135      * Maybe you can also make a list available and then let the widget-owner decide what to use.
0136      * By setting it to $SHELL right away we actually make the first filecheck obsolete.
0137      * But as iam not sure if you want to do anything else ill just let both checks in and set this to $SHELL anyway.
0138      */
0139 
0140     //cool-old-term: There is another check in the code. Not sure if useful.
0141 
0142     QString envshell = getenv("SHELL");
0143     QString shellProg = envshell != NULL ? envshell : "/bin/bash";
0144     session->setProgram(shellProg);
0145 
0146     setenv("TERM", "xterm-256color", 1);
0147 
0148     //session->setProgram();
0149 
0150     QStringList args("");
0151     session->setArguments(args);
0152     session->setAutoClose(true);
0153 
0154     session->setCodec(QTextCodec::codecForName("UTF-8"));
0155 
0156     session->setFlowControlEnabled(true);
0157     session->setHistoryType(HistoryTypeBuffer(1000));
0158 
0159     session->setDarkBackground(true);
0160 
0161     session->setKeyBindings("");
0162 
0163     return session;
0164 }
0165 
0166 /////////////////////////////////////////////////////////////////////////////////////
0167 /////////////////////////////////////////////////////////////////////////////////////
0168 
0169 
0170 int  KSession::getRandomSeed()
0171 {
0172     return m_session->sessionId() * 31;
0173 }
0174 
0175 void  KSession::addView(TerminalDisplay *display)
0176 {
0177     m_session->addView(display);
0178 }
0179 
0180 void KSession::removeView(TerminalDisplay *display)
0181 {
0182     m_session->removeView(display);
0183 }
0184 
0185 void KSession::sessionFinished()
0186 {
0187     Q_EMIT finished();
0188 }
0189 
0190 void KSession::selectionChanged(bool textSelected)
0191 {
0192     Q_UNUSED(textSelected)
0193 }
0194 
0195 void KSession::startShellProgram()
0196 {
0197     if ( m_session->isRunning() ) {
0198         return;
0199     }
0200 
0201     m_session->run();
0202 }
0203 
0204 bool KSession::sendSignal(int signal)
0205 {
0206     if ( !m_session->isRunning() ) {
0207         return false;
0208     }
0209 
0210     return m_session->sendSignal(signal);
0211 }
0212 
0213 int KSession::getShellPID()
0214 {
0215     return m_session->processId();
0216 }
0217 
0218 void KSession::changeDir(const QString &dir)
0219 {
0220     /*
0221        this is a very hackish way of trying to determine if the shell is in
0222        the foreground before attempting to change the directory.  It may not
0223        be portable to anything other than Linux.
0224     */
0225     QString strCmd;
0226     strCmd.setNum(getShellPID());
0227     strCmd.prepend("ps -j ");
0228     strCmd.append(" | tail -1 | awk '{ print $5 }' | grep -q \\+");
0229     int retval = system(strCmd.toStdString().c_str());
0230 
0231     if (!retval) {        
0232         // Send prior Ctrl-E, Ctrl-U to ensure the line is empty. This is
0233         // mandatory, otherwise sending a 'cd x\n' to a prompt with 'rm -rf *'
0234         // would result in data loss.
0235         sendText(QStringLiteral("\x05\x15"));    
0236         
0237         sendText(" cd " + KShell::quoteArg(dir) + '\r');
0238         Q_EMIT currentDirChanged();
0239     }
0240 }
0241 
0242 void KSession::setEnvironment(const QStringList &environment)
0243 {
0244     m_session->setEnvironment(environment);
0245 }
0246 
0247 
0248 void KSession::setShellProgram(const QString &progname)
0249 {
0250     m_session->setProgram(progname);
0251 }
0252 
0253 void KSession::setInitialWorkingDirectory(const QString &dir)
0254 {
0255     if ( _initialWorkingDirectory != dir ) {
0256         _initialWorkingDirectory = dir;
0257         m_session->setInitialWorkingDirectory(dir);
0258         Q_EMIT initialWorkingDirectoryChanged();
0259         Q_EMIT currentDirChanged();
0260 }   }
0261 
0262 QString KSession::getInitialWorkingDirectory()
0263 {
0264     return _initialWorkingDirectory;
0265 }
0266 
0267 void KSession::setArgs(const QStringList &args)
0268 {
0269     m_session->setArguments(args);
0270 }
0271 
0272 void KSession::setTextCodec(QTextCodec *codec)
0273 {
0274     m_session->setCodec(codec);
0275 }
0276 
0277 void KSession::setHistorySize(int lines)
0278 {
0279     if ( historySize() != lines ) {
0280         if (lines < 0)
0281             m_session->setHistoryType(HistoryTypeFile());
0282         else
0283             m_session->setHistoryType(HistoryTypeBuffer(lines));
0284         Q_EMIT historySizeChanged();
0285     }
0286 }
0287 
0288 int KSession::historySize() const
0289 {
0290     if ( m_session->historyType().isUnlimited() ) {
0291         return -1;
0292     } else {
0293         return m_session->historyType().maximumLineCount();
0294     }
0295 }
0296 
0297 QString KSession::getHistory() const
0298 {
0299     QString history;
0300     QTextStream historyStream(&history);
0301     PlainTextDecoder historyDecoder;
0302 
0303     historyDecoder.begin(&historyStream);
0304     m_session->emulation()->writeToStream(&historyDecoder);
0305     historyDecoder.end();
0306 
0307     return history;
0308 }
0309 
0310 void KSession::sendText(QString text)
0311 {
0312     m_session->sendText(text);
0313 }
0314 
0315 void KSession::sendKey(int rep, int key, int mod) const
0316 {
0317     Q_UNUSED(rep);
0318     Q_UNUSED(key);
0319     Q_UNUSED(mod);
0320 
0321     //TODO implement or remove this function.
0322 //    Qt::KeyboardModifier kbm = Qt::KeyboardModifier(mod);
0323 
0324 //    QKeyEvent qkey(QEvent::KeyPress, key, kbm);
0325 
0326 //    while (rep > 0){
0327 //        m_session->sendKey(&qkey);
0328 //        --rep;
0329     //    }
0330 }
0331 
0332 void KSession::clearScreen()
0333 {
0334     m_session->emulation()->clearEntireScreen();
0335 }
0336 
0337 void KSession::search(const QString &regexp, int startLine, int startColumn, bool forwards)
0338 {
0339     HistorySearch *history = new HistorySearch( QPointer<Emulation>(m_session->emulation()), QRegExp(regexp), forwards, startColumn, startLine, this);
0340     connect( history, SIGNAL(matchFound(int,int,int,int)), this, SIGNAL(matchFound(int,int,int,int)));
0341     connect( history, SIGNAL(noMatchFound()), this, SIGNAL(noMatchFound()));
0342     history->search();
0343 }
0344 
0345 void KSession::setFlowControlEnabled(bool enabled)
0346 {
0347     m_session->setFlowControlEnabled(enabled);
0348 }
0349 
0350 bool KSession::flowControlEnabled()
0351 {
0352     return m_session->flowControlEnabled();
0353 }
0354 
0355 void KSession::setKeyBindings(const QString &kb)
0356 {
0357     m_session->setKeyBindings(kb);
0358     Q_EMIT changedKeyBindings(kb);
0359 }
0360 
0361 QString KSession::getKeyBindings()
0362 {
0363    return m_session->keyBindings();
0364 }
0365 
0366 QStringList KSession::availableKeyBindings()
0367 {
0368     return KeyboardTranslatorManager::instance()->allTranslators();
0369 }
0370 
0371 QString KSession::keyBindings()
0372 {
0373     return m_session->keyBindings();
0374 }
0375 
0376 QString KSession::getTitle()
0377 {
0378     // if (m_session->currentDir() == QDir::homePath()) {
0379     //     return m_session->currentDir();
0380     // }
0381     // 
0382     // if (m_session->currentDir() == "/")
0383     //     return m_session->currentDir();
0384     // 
0385     // return QDir(m_session->currentDir()).dirName();
0386 
0387     return m_session->userTitle();
0388 }
0389 
0390 bool KSession::hasActiveProcess() const
0391 {
0392     return m_session->processId() != m_session->foregroundProcessId();
0393 }
0394 
0395 QString KSession::foregroundProcessName()
0396 {
0397     return m_session->foregroundProcessName();
0398 }
0399 
0400 QString KSession::currentDir() 
0401 {
0402     return m_session->currentDir();
0403 }