void KstJS::showConsole() { #ifdef KST_HAVE_READLINE if (!_konsolePart) { strcpy(shellStr, "SHELL=kstcmd"); putenv(shellStr); KLibFactory *f = KLibLoader::self()->factory("libkonsolepart"); if (!f) { KMessageBox::sorry(app(), i18n("Could not load konsole part. Please install kdebase.")); _showAction->setChecked(false); return; } if (!_splitter) { _splitter = new QSplitter(Qt::Vertical, app()); _oldCentralWidget = app()->centralWidget(); _oldCentralWidget->reparent(_splitter, QPoint(0, 0)); _splitter->show(); app()->setCentralWidget(_splitter); } KParts::Part *p = dynamic_cast<KParts::Part*>(f->create(_splitter, "kstcmd")); if (!p) { KMessageBox::sorry(app(), i18n("Konsole part appears to be incompatible. Please install kdebase correctly.")); _showAction->setChecked(false); return; } _splitter->moveToLast(p->widget()); connect(p, SIGNAL(destroyed()), this, SLOT(shellExited())); _konsolePart = p; } _konsolePart->widget()->show(); #endif }
QTerminal::QTerminal(QWidget *parent, Qt::WindowFlags f) : QTextEdit(parent) { setWindowFlags(f); qDebug() << "Constructing QTerminal"; savedCursor = this->textCursor(); sequenceState = NoSequence; insertMode = false; // The Following line causes the tab key to change focus... //setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); // //setTabChangesFocus(false); //setFocusPolicy(Qt::ClickFocus); /* * Launch a shell connected to the current process by a PTY */ shellPid = forkpty(&fdMaster, NULL, NULL, NULL); if (0 == shellPid) { /* fork off a shell */ /* if they don't have a shell, use bash */ setenv("SHELL", "/bin/bash", 0); /* no special control features are supported */ setenv("TERM", "xterm", 1); /* hand off to the shell of choice */ execl(getenv("SHELL"), getenv("SHELL"), (char *)0); /* If this exec returns, we should take some action. * Window systems do ugly things if two processes try * to use the same resources. */ if (errno == ENOENT) { execl("/bin/sh", "/bin/sh", (char*)0); } perror("QTerminal: exec"); // exit hard in case exit handlers mess with the window system _exit(127); } else if (-1 == shellPid) { // didn't fork this->setText(QString("QTerminal: fork: %1\n").arg(strerror(errno))); } else { /* * We will use non-blocking I/O to read from the shell */ fcntl(fdMaster, F_SETFL, fcntl(fdMaster, F_GETFL, 0) | O_NONBLOCK); qDebug() << "Shell PID " << shellPid << " on file descriptor " << fdMaster; /* * Arrange to be notified when the shell emits output */ watcher = new FileDescriptorMonitor(fdMaster, this); connect(watcher, SIGNAL(readyForRead(int)), this, SLOT(readOutput()), Qt::BlockingQueuedConnection); connect(this, SIGNAL(shellExited()), watcher, SLOT(stop())); watcher->start(); } }
void MainWindow::toggleTerminal(bool alive) { if (alive) { // we were called by a dock window reporting itself visible return; } QSettings settings; if (termWidget != NULL) { if (!termInDrawer) { termWidget->parent()->deleteLater(); } qDebug() << "Closing terminal"; termWidget->disconnect(); termWidget->deleteLater(); termWidget = NULL; if (openFiles.size() > 0) { getCurDoc()->setFocus(); } copyFromTerm = false; } else { qDebug() << "Opening terminal"; termWidget = new QTerminal(this); applyPrefsToTerminal(termWidget); if (openFiles.size() > 0 && !getCurFileObj()->fullName.isEmpty()) { termWidget->changeDir(getCurFileObj()->path); } if (termInDrawer) { termWidget->setWindowFlags(Qt::Drawer); termWidget->show(); } else { QDockWidget * termDock = new QDockWidget(tr("QSciTE Terminal"), this); termDock->setWidget(termWidget); termDock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); this->addDockWidget(Qt::BottomDockWidgetArea, termDock); } connect(termWidget, SIGNAL(shellExited()), this, SLOT(toggleTerminal())); connect(termWidget, SIGNAL(copyAvailable(bool)), this, SLOT(updateCopyAvailable(bool))); termWidget->setFocus(); copyFromTerm = true; } }
void QTerminal::readOutput() { this->setTextCursor(savedCursor); char rdBuf[256]; ssize_t count = read(fdMaster, rdBuf, sizeof(rdBuf)/sizeof(char)); if (0 == count || (-1 == count && errno == EIO)) { // EOF, child closed watcher->stop(); watcher->disconnect(); int childStatus; pid_t waitedPid = waitpid(shellPid, &childStatus, 0); if (waitedPid == shellPid) { shellPid = 0; if (WIFEXITED(childStatus)) { if(WEXITSTATUS(childStatus) == 0) { emit shellExited(); } else { this->insertPlainText(QString("%1 exited with status %2.\n").arg(waitedPid).arg(WEXITSTATUS(childStatus))); } } else if (WIFSIGNALED(childStatus)) { this->insertPlainText(QString("%1 exited with signal %2.\n").arg(waitedPid).arg(WTERMSIG(childStatus))); } else { this->insertPlainText("QTerminal: Why are WIFEXITED and WIFSIGNALED both false?\n"); } return; } else { perror("QTerminal: waitpid"); } } if ((-1 == count) && (errno != EAGAIN)) { perror("QTerminal: read"); return; } for (char * c = rdBuf; c < rdBuf + count; ++c) { switch (sequenceState) { case GotEsc: if (*c == '[') { sequenceState = InCS; } else if (*c == ']') { sequenceState = InOSC; } else { qWarning("No Can Do ESC %c", *c); sequenceState = NoSequence; } break; case InCS: if (*c >= '\x20' && *c <= '\x7e') { savedSequence += *c; if (*c >= '\x40') { doControlSeq(savedSequence); savedSequence.clear(); sequenceState = NoSequence; } } else { /* ANSI says control or high-half bytes shouldn't happen * inside a control sequence. ANSI didn't write every program * in existence. */ qWarning("No Can Do Esc [ %s", savedSequence.toHex().constData()); savedSequence.clear(); sequenceState = NoSequence; } break; case InOSC: savedSequence += *c; if (*c == '\x07') { doOSCommand(savedSequence); savedSequence.clear(); sequenceState = NoSequence; } else if (*c == '\x1b') { sequenceState = OSCHalfClosed; } break; case OSCHalfClosed: savedSequence += *c; if (*c == '\\') { doOSCommand(savedSequence); } else { qWarning("No Can Do Esc ] %s", savedSequence.toHex().constData()); } savedSequence.clear(); sequenceState = NoSequence; break; default: qWarning("QTerminal::sequenceState got set to %d", sequenceState); sequenceState = NoSequence; // FALL THROUGH case NoSequence: switch (*c) { case '\x1b': /* ASCII ESC. */ if (!savedSequence.isEmpty()) { this->insertPlainText(savedSequence); savedSequence.clear(); } sequenceState = GotEsc; break; case '\r': if (!savedSequence.isEmpty()) { this->insertPlainText(savedSequence); savedSequence.clear(); } { QTextCursor cursor = this->textCursor(); cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor); this->setTextCursor(cursor); } break; case '\x08': /* Backspace. We assume non-destructive. */ if (!savedSequence.isEmpty()) { this->insertPlainText(savedSequence); savedSequence.clear(); } this->moveCursor(QTextCursor::PreviousCharacter); break; case '\a': /* ASCII BEL. */ QApplication::beep(); break; case '\n': if (!savedSequence.isEmpty()) { this->insertPlainText(savedSequence); savedSequence.clear(); } { QTextCursor csr = this->textCursor(); QTextCursor tcStart = this->textCursor(); tcStart.movePosition(QTextCursor::StartOfBlock); int col = csr.position() - tcStart.position(); if (!csr.movePosition(QTextCursor::NextBlock)) { csr.movePosition(QTextCursor::EndOfBlock); csr.insertText(QString("\n") + QString().fill(' ', col)); } else { if (!csr.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, col)) { tcStart = csr; tcStart.movePosition(QTextCursor::StartOfBlock); int curCol = csr.position() - tcStart.position(); csr.insertText(QString().fill(' ', col - curCol)); } } this->setTextCursor(csr); } break; default: if (!(this->textCursor().atEnd()) && !insertMode) { this->textCursor().deleteChar(); this->insertPlainText(QChar(*c)); } else { savedSequence += *c; } break; } break; } } if (sequenceState == NoSequence && !savedSequence.isEmpty()) { this->insertPlainText(savedSequence); savedSequence.clear(); } savedCursor = this->textCursor(); ensureCursorVisible(); }