mirror of https://github.com/cutefishos/terminal
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
406 lines
11 KiB
C++
406 lines
11 KiB
C++
/*
|
|
This file is part of Konsole QML plugin,
|
|
which is a terminal emulator from KDE.
|
|
|
|
Copyright 2013 by Dmitry Zagnoyko <hiroshidi@gmail.com>
|
|
|
|
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.
|
|
*/
|
|
|
|
// Own
|
|
#include "ksession.h"
|
|
#include "TerminalDisplay.h"
|
|
|
|
// Qt
|
|
#include <QApplication>
|
|
#include <QDir>
|
|
#include <QGuiApplication>
|
|
#include <QPointer>
|
|
#include <QRegularExpression>
|
|
#include <QTimer>
|
|
|
|
// Konsole
|
|
#include "KeyboardTranslator.h"
|
|
#include "HistorySearch.h"
|
|
|
|
KSession::KSession(QObject *parent) :
|
|
QObject(parent), m_session(createSession("")), m_foregroundCheck(new QTimer(this))
|
|
{
|
|
connect(m_session, SIGNAL(started()), this, SIGNAL(started()));
|
|
connect(m_session, SIGNAL(finished()), this, SLOT(sessionFinished()));
|
|
|
|
// Each update notes the process group in front, the one getTitle() went by.
|
|
const auto updateTitle = [this] {
|
|
m_foregroundGroup = m_session->foregroundProcessId();
|
|
emit titleChanged();
|
|
};
|
|
connect(m_session, &Konsole::Session::titleChanged, this, updateTitle);
|
|
|
|
connect(m_session->emulation(), &Konsole::Emulation::titleChanged, this,
|
|
[this, updateTitle](int what, const QString &title) {
|
|
if (what != 0 && what != 2)
|
|
return;
|
|
m_programTitle = title;
|
|
m_programTitleGroup = m_session->foregroundProcessId();
|
|
updateTitle();
|
|
});
|
|
|
|
// Nothing announces a program starting or ending, so the foreground is
|
|
// looked at after output or a key, at most every 250 ms.
|
|
m_foregroundCheck->setSingleShot(true);
|
|
m_foregroundCheck->setInterval(250);
|
|
connect(m_foregroundCheck, &QTimer::timeout, this, [this, updateTitle] {
|
|
if (m_session->foregroundProcessId() != m_foregroundGroup)
|
|
updateTitle();
|
|
});
|
|
|
|
const auto checkForeground = [this] {
|
|
if (!m_foregroundCheck->isActive())
|
|
m_foregroundCheck->start();
|
|
};
|
|
connect(m_session, &Konsole::Session::receivedData, this, checkForeground);
|
|
connect(m_session->emulation(), &Konsole::Emulation::outputFromKeypressEvent, this, checkForeground);
|
|
}
|
|
|
|
KSession::~KSession()
|
|
{
|
|
if (m_session) {
|
|
m_session->close();
|
|
m_session->disconnect();
|
|
delete m_session;
|
|
}
|
|
}
|
|
|
|
void KSession::setTitle(QString name)
|
|
{
|
|
m_session->setTitle(Session::NameRole, name);
|
|
}
|
|
|
|
|
|
Konsole::Session *KSession::createSession(QString name)
|
|
{
|
|
Session *session = new Session();
|
|
|
|
session->setTitle(Session::NameRole, name);
|
|
|
|
/* Thats a freaking bad idea!!!!
|
|
* /bin/bash is not there on every system
|
|
* better set it to the current $SHELL
|
|
* Maybe you can also make a list available and then let the widget-owner decide what to use.
|
|
* By setting it to $SHELL right away we actually make the first filecheck obsolete.
|
|
* But as iam not sure if you want to do anything else ill just let both checks in and set this to $SHELL anyway.
|
|
*/
|
|
|
|
//cool-old-term: There is another check in the code. Not sure if useful.
|
|
|
|
const QByteArray envshell = qgetenv("SHELL");
|
|
const QString shellProg = envshell.isEmpty() ? QStringLiteral("/bin/bash") : QString::fromUtf8(envshell);
|
|
m_shellProgram = shellProg;
|
|
session->setProgram(shellProg);
|
|
|
|
setenv("TERM", "xterm-256color", 1);
|
|
|
|
//session->setProgram();
|
|
|
|
QStringList args("");
|
|
m_shellArgs = args;
|
|
session->setArguments(args);
|
|
session->setAutoClose(true);
|
|
|
|
session->setFlowControlEnabled(true);
|
|
session->setHistoryType(HistoryTypeBuffer(1000));
|
|
|
|
session->setDarkBackground(true);
|
|
|
|
session->setKeyBindings("");
|
|
|
|
return session;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
int KSession::getRandomSeed()
|
|
{
|
|
return m_session->sessionId() * 31;
|
|
}
|
|
|
|
void KSession::addView(Konsole::TerminalDisplay *display)
|
|
{
|
|
m_session->addView(display);
|
|
}
|
|
|
|
void KSession::removeView(Konsole::TerminalDisplay *display)
|
|
{
|
|
m_session->removeView(display);
|
|
}
|
|
|
|
void KSession::sessionFinished()
|
|
{
|
|
emit finished();
|
|
}
|
|
|
|
void KSession::selectionChanged(bool textSelected)
|
|
{
|
|
Q_UNUSED(textSelected)
|
|
}
|
|
|
|
void KSession::startShellProgram()
|
|
{
|
|
if ( m_session->isRunning() ) {
|
|
return;
|
|
}
|
|
|
|
m_session->run();
|
|
}
|
|
|
|
bool KSession::sendSignal(int signal)
|
|
{
|
|
if ( !m_session->isRunning() ) {
|
|
return false;
|
|
}
|
|
|
|
return m_session->sendSignal(signal);
|
|
}
|
|
|
|
int KSession::getShellPID()
|
|
{
|
|
return m_session->processId();
|
|
}
|
|
|
|
void KSession::changeDir(const QString &dir)
|
|
{
|
|
/*
|
|
this is a very hackish way of trying to determine if the shell is in
|
|
the foreground before attempting to change the directory. It may not
|
|
be portable to anything other than Linux.
|
|
*/
|
|
QString strCmd;
|
|
strCmd.setNum(getShellPID());
|
|
strCmd.prepend("ps -j ");
|
|
strCmd.append(" | tail -1 | awk '{ print $5 }' | grep -q \\+");
|
|
int retval = system(strCmd.toStdString().c_str());
|
|
|
|
if (!retval) {
|
|
QString cmd = "cd " + dir + "\n";
|
|
sendText(cmd);
|
|
}
|
|
}
|
|
|
|
void KSession::setEnvironment(const QStringList &environment)
|
|
{
|
|
m_session->setEnvironment(environment);
|
|
}
|
|
|
|
|
|
void KSession::setShellProgram(const QString &progname)
|
|
{
|
|
m_shellProgram = progname;
|
|
m_session->setProgram(progname);
|
|
}
|
|
|
|
void KSession::setInitialWorkingDirectory(const QString &dir)
|
|
{
|
|
if ( _initialWorkingDirectory != dir ) {
|
|
_initialWorkingDirectory = dir;
|
|
m_session->setInitialWorkingDirectory(dir);
|
|
emit initialWorkingDirectoryChanged();
|
|
} }
|
|
|
|
QString KSession::getInitialWorkingDirectory()
|
|
{
|
|
return _initialWorkingDirectory;
|
|
}
|
|
|
|
QString KSession::getShellProgram() const
|
|
{
|
|
return m_shellProgram;
|
|
}
|
|
|
|
QStringList KSession::getShellProgramArgs() const
|
|
{
|
|
return m_shellArgs;
|
|
}
|
|
|
|
void KSession::setArgs(const QStringList &args)
|
|
{
|
|
m_shellArgs = args;
|
|
m_session->setArguments(args);
|
|
}
|
|
|
|
void KSession::setHistorySize(int lines)
|
|
{
|
|
if ( historySize() != lines ) {
|
|
if (lines < 0)
|
|
m_session->setHistoryType(HistoryTypeFile());
|
|
else
|
|
m_session->setHistoryType(HistoryTypeBuffer(lines));
|
|
emit historySizeChanged();
|
|
}
|
|
}
|
|
|
|
int KSession::historySize() const
|
|
{
|
|
if ( m_session->historyType().isUnlimited() ) {
|
|
return -1;
|
|
} else {
|
|
return m_session->historyType().maximumLineCount();
|
|
}
|
|
}
|
|
|
|
QString KSession::getHistory() const
|
|
{
|
|
QString history;
|
|
QTextStream historyStream(&history);
|
|
PlainTextDecoder historyDecoder;
|
|
|
|
historyDecoder.begin(&historyStream);
|
|
m_session->emulation()->writeToStream(&historyDecoder);
|
|
historyDecoder.end();
|
|
|
|
return history;
|
|
}
|
|
|
|
void KSession::sendText(QString text)
|
|
{
|
|
m_session->sendText(text);
|
|
}
|
|
|
|
void KSession::sendKey(int rep, int key, int mod) const
|
|
{
|
|
//TODO implement or remove this function.
|
|
// Qt::KeyboardModifier kbm = Qt::KeyboardModifier(mod);
|
|
|
|
// QKeyEvent qkey(QEvent::KeyPress, key, kbm);
|
|
|
|
// while (rep > 0){
|
|
// m_session->sendKey(&qkey);
|
|
// --rep;
|
|
// }
|
|
}
|
|
|
|
void KSession::clearScreen()
|
|
{
|
|
m_session->emulation()->clearEntireScreen();
|
|
}
|
|
|
|
void KSession::clearScrollback()
|
|
{
|
|
m_session->clearHistory();
|
|
}
|
|
|
|
bool KSession::clearScreenAndScrollback()
|
|
{
|
|
return m_session->emulation()->clearScreenAndHistory(!hasActiveProcess());
|
|
}
|
|
|
|
void KSession::resetTerminal()
|
|
{
|
|
// Resetting clears the screen into the history, so the history goes after it.
|
|
m_session->emulation()->reset();
|
|
m_session->clearHistory();
|
|
// A size change makes the shell, or a full-screen program, draw itself again;
|
|
// Ctrl+L would clear the screen into the history once more.
|
|
m_session->refresh();
|
|
}
|
|
|
|
void KSession::search(const QString ®exp, int startLine, int startColumn, bool forwards)
|
|
{
|
|
HistorySearch *history = new HistorySearch(QPointer<Emulation>(m_session->emulation()),
|
|
QRegularExpression(regexp),
|
|
forwards,
|
|
startColumn,
|
|
startLine,
|
|
this);
|
|
connect( history, SIGNAL(matchFound(int,int,int,int)), this, SIGNAL(matchFound(int,int,int,int)));
|
|
connect( history, SIGNAL(noMatchFound()), this, SIGNAL(noMatchFound()));
|
|
history->search();
|
|
}
|
|
|
|
void KSession::setFlowControlEnabled(bool enabled)
|
|
{
|
|
m_session->setFlowControlEnabled(enabled);
|
|
}
|
|
|
|
bool KSession::flowControlEnabled()
|
|
{
|
|
return m_session->flowControlEnabled();
|
|
}
|
|
|
|
void KSession::setKeyBindings(const QString &kb)
|
|
{
|
|
m_session->setKeyBindings(kb);
|
|
emit changedKeyBindings(kb);
|
|
}
|
|
|
|
QString KSession::getKeyBindings()
|
|
{
|
|
return m_session->keyBindings();
|
|
}
|
|
|
|
QStringList KSession::availableKeyBindings()
|
|
{
|
|
return KeyboardTranslatorManager::instance()->allTranslators();
|
|
}
|
|
|
|
QString KSession::keyBindings()
|
|
{
|
|
return m_session->keyBindings();
|
|
}
|
|
|
|
QString KSession::getTitle()
|
|
{
|
|
// A program in front of the shell gives the title it set, or else its name.
|
|
const int foreground = m_session->foregroundProcessId();
|
|
if (foreground > 0 && foreground != m_session->processId()) {
|
|
if (foreground == m_programTitleGroup && !m_programTitle.isEmpty())
|
|
return m_programTitle;
|
|
|
|
const QString name = m_session->foregroundProcessName();
|
|
if (!name.isEmpty())
|
|
return name;
|
|
}
|
|
|
|
const QString dir = m_session->currentDir();
|
|
|
|
// At the prompt tabs are titled by the working directory; home and root keep their full path.
|
|
if (dir == QDir::homePath() || dir == QLatin1String("/"))
|
|
return dir;
|
|
|
|
return QDir(dir).dirName();
|
|
}
|
|
|
|
bool KSession::hasActiveProcess() const
|
|
{
|
|
return m_session->processId() != m_session->foregroundProcessId();
|
|
}
|
|
|
|
QString KSession::foregroundProcessName()
|
|
{
|
|
return m_session->foregroundProcessName();
|
|
}
|
|
|
|
Konsole::Emulation *KSession::emulation() const
|
|
{
|
|
return m_session->emulation();
|
|
}
|
|
|
|
QString KSession::currentDir()
|
|
{
|
|
return m_session->currentDir();
|
|
}
|