mirror of
https://github.com/rizinorg/cutter.git
synced 2025-01-18 18:38:51 +00:00
Stdin redirection for locally debugged processes (#1892)
This commit is contained in:
parent
49d58b3624
commit
f284f9d209
@ -275,6 +275,7 @@ SOURCES += \
|
||||
common/HexHighlighter.cpp \
|
||||
common/Highlighter.cpp \
|
||||
common/MdHighlighter.cpp \
|
||||
common/DirectionalComboBox.cpp \
|
||||
dialogs/preferences/AsmOptionsWidget.cpp \
|
||||
dialogs/NewFileDialog.cpp \
|
||||
common/AnalTask.cpp \
|
||||
@ -408,6 +409,7 @@ HEADERS += \
|
||||
core/MainWindow.h \
|
||||
common/Highlighter.h \
|
||||
common/MdHighlighter.h \
|
||||
common/DirectionalComboBox.h \
|
||||
dialogs/InitialOptionsDialog.h \
|
||||
dialogs/NewFileDialog.h \
|
||||
common/AnalTask.h \
|
||||
|
24
src/common/DirectionalComboBox.cpp
Normal file
24
src/common/DirectionalComboBox.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include "DirectionalComboBox.h"
|
||||
|
||||
DirectionalComboBox::DirectionalComboBox(QWidget *parent, bool upwards)
|
||||
: QComboBox(parent), popupUpwards(upwards)
|
||||
{
|
||||
}
|
||||
|
||||
void DirectionalComboBox::showPopup()
|
||||
{
|
||||
QComboBox::showPopup();
|
||||
QWidget *popup = this->findChild<QFrame *>();
|
||||
if (popupUpwards) {
|
||||
popup->move(popup->x(),
|
||||
mapToGlobal(this->rect().bottomLeft()).y() - popup->height());
|
||||
} else {
|
||||
popup->move(popup->x(),
|
||||
mapToGlobal(this->rect().topLeft()).y());
|
||||
}
|
||||
}
|
||||
|
||||
void DirectionalComboBox::setPopupDirection(bool upwards)
|
||||
{
|
||||
popupUpwards = upwards;
|
||||
}
|
23
src/common/DirectionalComboBox.h
Normal file
23
src/common/DirectionalComboBox.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <QComboBox>
|
||||
/**
|
||||
* @brief Custom QComboBox created to prevent the menu popup from opening up at different
|
||||
* offsets for different items, which may result in list items being rendered outside
|
||||
* of the screen/containing widget.
|
||||
*/
|
||||
class DirectionalComboBox : public QComboBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DirectionalComboBox(QWidget *parent = nullptr, bool upwards = true);
|
||||
|
||||
void setPopupDirection(bool upwards);
|
||||
|
||||
private:
|
||||
bool popupUpwards;
|
||||
|
||||
void showPopup();
|
||||
};
|
||||
|
@ -313,6 +313,24 @@ QString CutterCore::cmd(const char *str)
|
||||
return o;
|
||||
}
|
||||
|
||||
bool CutterCore::isRedirectableDebugee()
|
||||
{
|
||||
if (!currentlyDebugging || currentlyAttachedToPID != -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We are only able to redirect locally debugged unix processes
|
||||
QJsonArray openFilesArray = cmdj("oj").array();;
|
||||
for (QJsonValue value : openFilesArray) {
|
||||
QJsonObject openFile = value.toObject();
|
||||
QString URI = openFile["uri"].toString();
|
||||
if (URI.contains("ptrace") | URI.contains("mach")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CutterCore::isDebugTaskInProgress()
|
||||
{
|
||||
if (!debugTask.isNull()) {
|
||||
|
@ -298,6 +298,10 @@ public:
|
||||
QStringList getDebugPlugins();
|
||||
void setDebugPlugin(QString plugin);
|
||||
bool isDebugTaskInProgress();
|
||||
/**
|
||||
* @brief Check if we can use output/input redirection with the currently debugged process
|
||||
*/
|
||||
bool isRedirectableDebugee();
|
||||
bool currentlyDebugging = false;
|
||||
bool currentlyEmulating = false;
|
||||
int currentlyAttachedToPID = -1;
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <QStringListModel>
|
||||
#include <QTimer>
|
||||
#include <QSettings>
|
||||
#include <QDir>
|
||||
#include <QUuid>
|
||||
#include <iostream>
|
||||
#include "core/Cutter.h"
|
||||
#include "ConsoleWidget.h"
|
||||
@ -15,7 +17,6 @@
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <io.h>
|
||||
#include <QUuid>
|
||||
#define dup2 _dup2
|
||||
#define dup _dup
|
||||
#define fileno _fileno
|
||||
@ -26,8 +27,12 @@
|
||||
#include <unistd.h>
|
||||
#define PIPE_READ (0)
|
||||
#define PIPE_WRITE (1)
|
||||
#define STDIN_PIPE_NAME "%1/cutter-stdin-%2"
|
||||
#endif
|
||||
|
||||
#define CONSOLE_R2_INPUT ("R2 Console")
|
||||
#define CONSOLE_DEBUGEE_INPUT ("Debugee Input")
|
||||
|
||||
static const int invalidHistoryPos = -1;
|
||||
|
||||
static const char *consoleWrapSettingsKey = "console.wrap";
|
||||
@ -45,7 +50,8 @@ ConsoleWidget::ConsoleWidget(MainWindow *main, QAction *action) :
|
||||
ui->setupUi(this);
|
||||
|
||||
// Adjust console lineedit
|
||||
ui->inputLineEdit->setTextMargins(10, 0, 0, 0);
|
||||
ui->r2InputLineEdit->setTextMargins(10, 0, 0, 0);
|
||||
ui->debugeeInputLineEdit->setTextMargins(10, 0, 0, 0);
|
||||
|
||||
setupFont();
|
||||
|
||||
@ -71,9 +77,9 @@ ConsoleWidget::ConsoleWidget(MainWindow *main, QAction *action) :
|
||||
completer->setMaxVisibleItems(20);
|
||||
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
||||
completer->setFilterMode(Qt::MatchStartsWith);
|
||||
ui->inputLineEdit->setCompleter(completer);
|
||||
ui->r2InputLineEdit->setCompleter(completer);
|
||||
|
||||
connect(ui->inputLineEdit, &QLineEdit::textEdited, this, &ConsoleWidget::updateCompletion);
|
||||
connect(ui->r2InputLineEdit, &QLineEdit::textEdited, this, &ConsoleWidget::updateCompletion);
|
||||
updateCompletion();
|
||||
|
||||
// Set console output context menu
|
||||
@ -81,28 +87,46 @@ ConsoleWidget::ConsoleWidget(MainWindow *main, QAction *action) :
|
||||
connect(ui->outputTextEdit, SIGNAL(customContextMenuRequested(const QPoint &)),
|
||||
this, SLOT(showCustomContextMenu(const QPoint &)));
|
||||
|
||||
// Esc clears inputLineEdit (like OmniBar)
|
||||
QShortcut *clear_shortcut = new QShortcut(QKeySequence(Qt::Key_Escape), ui->inputLineEdit);
|
||||
connect(clear_shortcut, SIGNAL(activated()), this, SLOT(clear()));
|
||||
clear_shortcut->setContext(Qt::WidgetShortcut);
|
||||
// Esc clears r2InputLineEdit and debugeeInputLineEdit (like OmniBar)
|
||||
QShortcut *r2_clear_shortcut = new QShortcut(QKeySequence(Qt::Key_Escape), ui->r2InputLineEdit);
|
||||
connect(r2_clear_shortcut, SIGNAL(activated()), this, SLOT(clear()));
|
||||
r2_clear_shortcut->setContext(Qt::WidgetShortcut);
|
||||
|
||||
QShortcut *debugee_clear_shortcut = new QShortcut(QKeySequence(Qt::Key_Escape), ui->debugeeInputLineEdit);
|
||||
connect(debugee_clear_shortcut, SIGNAL(activated()), this, SLOT(clear()));
|
||||
debugee_clear_shortcut->setContext(Qt::WidgetShortcut);
|
||||
|
||||
// Up and down arrows show history
|
||||
historyUpShortcut = new QShortcut(QKeySequence(Qt::Key_Up), ui->inputLineEdit);
|
||||
historyUpShortcut = new QShortcut(QKeySequence(Qt::Key_Up), ui->r2InputLineEdit);
|
||||
connect(historyUpShortcut, SIGNAL(activated()), this, SLOT(historyPrev()));
|
||||
historyUpShortcut->setContext(Qt::WidgetShortcut);
|
||||
|
||||
historyDownShortcut = new QShortcut(QKeySequence(Qt::Key_Down), ui->inputLineEdit);
|
||||
historyDownShortcut = new QShortcut(QKeySequence(Qt::Key_Down), ui->r2InputLineEdit);
|
||||
connect(historyDownShortcut, SIGNAL(activated()), this, SLOT(historyNext()));
|
||||
historyDownShortcut->setContext(Qt::WidgetShortcut);
|
||||
|
||||
QShortcut *completionShortcut = new QShortcut(QKeySequence(Qt::Key_Tab), ui->inputLineEdit);
|
||||
QShortcut *completionShortcut = new QShortcut(QKeySequence(Qt::Key_Tab), ui->r2InputLineEdit);
|
||||
connect(completionShortcut, &QShortcut::activated, this, &ConsoleWidget::triggerCompletion);
|
||||
|
||||
connect(ui->inputLineEdit, &QLineEdit::editingFinished, this, &ConsoleWidget::disableCompletion);
|
||||
connect(ui->r2InputLineEdit, &QLineEdit::editingFinished, this, &ConsoleWidget::disableCompletion);
|
||||
|
||||
connect(Config(), &Configuration::fontsUpdated, this, &ConsoleWidget::setupFont);
|
||||
connect(Config(), &Configuration::interfaceThemeChanged, this, &ConsoleWidget::setupFont);
|
||||
|
||||
connect(ui->inputCombo,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, &ConsoleWidget::onIndexChange);
|
||||
|
||||
connect(Core(), &CutterCore::debugTaskStateChanged, this, [ = ]() {
|
||||
if (Core()->isRedirectableDebugee()) {
|
||||
ui->inputCombo->setVisible(true);
|
||||
} else {
|
||||
ui->inputCombo->setVisible(false);
|
||||
// Return to the r2 console
|
||||
ui->inputCombo->setCurrentIndex(ui->inputCombo->findText(CONSOLE_R2_INPUT));
|
||||
}
|
||||
});
|
||||
|
||||
completer->popup()->installEventFilter(this);
|
||||
|
||||
redirectOutput();
|
||||
@ -111,6 +135,11 @@ ConsoleWidget::ConsoleWidget(MainWindow *main, QAction *action) :
|
||||
ConsoleWidget::~ConsoleWidget()
|
||||
{
|
||||
delete completer;
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
::close(stdinFile);
|
||||
remove(stdinFifoPath.toStdString().c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ConsoleWidget::eventFilter(QObject *obj, QEvent *event)
|
||||
@ -150,7 +179,7 @@ void ConsoleWidget::addDebugOutput(const QString &msg)
|
||||
|
||||
void ConsoleWidget::focusInputLineEdit()
|
||||
{
|
||||
ui->inputLineEdit->setFocus();
|
||||
ui->r2InputLineEdit->setFocus();
|
||||
}
|
||||
|
||||
void ConsoleWidget::removeLastLine()
|
||||
@ -170,7 +199,7 @@ void ConsoleWidget::executeCommand(const QString &command)
|
||||
if (!commandTask.isNull()) {
|
||||
return;
|
||||
}
|
||||
ui->inputLineEdit->setEnabled(false);
|
||||
ui->r2InputLineEdit->setEnabled(false);
|
||||
|
||||
QString cmd_line = "[" + RAddressString(Core()->getOffset()) + "]> " + command;
|
||||
addOutput(cmd_line);
|
||||
@ -184,8 +213,8 @@ void ConsoleWidget::executeCommand(const QString &command)
|
||||
scrollOutputToEnd();
|
||||
historyAdd(command);
|
||||
commandTask.clear();
|
||||
ui->inputLineEdit->setEnabled(true);
|
||||
ui->inputLineEdit->setFocus();
|
||||
ui->r2InputLineEdit->setEnabled(true);
|
||||
ui->r2InputLineEdit->setFocus();
|
||||
|
||||
if (oldOffset != Core()->getOffset()) {
|
||||
Core()->updateSeek();
|
||||
@ -195,6 +224,32 @@ void ConsoleWidget::executeCommand(const QString &command)
|
||||
Core()->getAsyncTaskManager()->start(commandTask);
|
||||
}
|
||||
|
||||
void ConsoleWidget::sendToStdin(const QString &input)
|
||||
{
|
||||
#ifndef Q_OS_WIN
|
||||
write(stdinFile, (input + "\n").toStdString().c_str(), input.size() + 1);
|
||||
fsync(stdinFile);
|
||||
addOutput("Sent input: '" + input + "'");
|
||||
#else
|
||||
// Stdin redirection isn't currently available in windows because console applications
|
||||
// with stdin already get their own console window with stdin when they are launched
|
||||
// that the user can type into.
|
||||
addOutput("Unsupported feature");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ConsoleWidget::onIndexChange()
|
||||
{
|
||||
QString console = ui->inputCombo->currentText();
|
||||
if (console == CONSOLE_DEBUGEE_INPUT) {
|
||||
ui->r2InputLineEdit->setVisible(false);
|
||||
ui->debugeeInputLineEdit->setVisible(true);
|
||||
} else if (console == CONSOLE_R2_INPUT) {
|
||||
ui->r2InputLineEdit->setVisible(true);
|
||||
ui->debugeeInputLineEdit->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
void ConsoleWidget::setWrap(bool wrap)
|
||||
{
|
||||
QSettings().setValue(consoleWrapSettingsKey, wrap);
|
||||
@ -202,19 +257,29 @@ void ConsoleWidget::setWrap(bool wrap)
|
||||
ui->outputTextEdit->setLineWrapMode(wrap ? QPlainTextEdit::WidgetWidth: QPlainTextEdit::NoWrap);
|
||||
}
|
||||
|
||||
void ConsoleWidget::on_inputLineEdit_returnPressed()
|
||||
void ConsoleWidget::on_r2InputLineEdit_returnPressed()
|
||||
{
|
||||
QString input = ui->inputLineEdit->text();
|
||||
QString input = ui->r2InputLineEdit->text();
|
||||
if (input.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
executeCommand(input);
|
||||
ui->inputLineEdit->clear();
|
||||
ui->r2InputLineEdit->clear();
|
||||
}
|
||||
|
||||
void ConsoleWidget::on_debugeeInputLineEdit_returnPressed()
|
||||
{
|
||||
QString input = ui->debugeeInputLineEdit->text();
|
||||
if (input.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
sendToStdin(input);
|
||||
ui->debugeeInputLineEdit->clear();
|
||||
}
|
||||
|
||||
void ConsoleWidget::on_execButton_clicked()
|
||||
{
|
||||
on_inputLineEdit_returnPressed();
|
||||
on_r2InputLineEdit_returnPressed();
|
||||
}
|
||||
|
||||
void ConsoleWidget::showCustomContextMenu(const QPoint &pt)
|
||||
@ -238,9 +303,9 @@ void ConsoleWidget::historyNext()
|
||||
--lastHistoryPosition;
|
||||
|
||||
if (lastHistoryPosition >= 0) {
|
||||
ui->inputLineEdit->setText(history.at(lastHistoryPosition));
|
||||
ui->r2InputLineEdit->setText(history.at(lastHistoryPosition));
|
||||
} else {
|
||||
ui->inputLineEdit->clear();
|
||||
ui->r2InputLineEdit->clear();
|
||||
}
|
||||
|
||||
|
||||
@ -255,7 +320,7 @@ void ConsoleWidget::historyPrev()
|
||||
lastHistoryPosition = history.size() - 2;
|
||||
}
|
||||
|
||||
ui->inputLineEdit->setText(history.at(++lastHistoryPosition));
|
||||
ui->r2InputLineEdit->setText(history.at(++lastHistoryPosition));
|
||||
}
|
||||
}
|
||||
|
||||
@ -286,7 +351,7 @@ void ConsoleWidget::updateCompletion()
|
||||
return;
|
||||
}
|
||||
|
||||
auto current = ui->inputLineEdit->text();
|
||||
auto current = ui->r2InputLineEdit->text();
|
||||
auto completions = Core()->autocomplete(current, R_LINE_PROMPT_DEFAULT);
|
||||
int lastSpace = current.lastIndexOf(' ');
|
||||
if (lastSpace >= 0) {
|
||||
@ -301,13 +366,14 @@ void ConsoleWidget::updateCompletion()
|
||||
void ConsoleWidget::clear()
|
||||
{
|
||||
disableCompletion();
|
||||
ui->inputLineEdit->clear();
|
||||
ui->r2InputLineEdit->clear();
|
||||
ui->debugeeInputLineEdit->clear();
|
||||
|
||||
invalidateHistoryPosition();
|
||||
|
||||
// Close the potential shown completer popup
|
||||
ui->inputLineEdit->clearFocus();
|
||||
ui->inputLineEdit->setFocus();
|
||||
ui->r2InputLineEdit->clearFocus();
|
||||
ui->r2InputLineEdit->setFocus();
|
||||
}
|
||||
|
||||
void ConsoleWidget::scrollOutputToEnd()
|
||||
@ -357,15 +423,16 @@ void ConsoleWidget::redirectOutput()
|
||||
|
||||
pipeSocket = new QLocalSocket(this);
|
||||
|
||||
origStdin = fdopen(dup(fileno(stderr)), "r");
|
||||
origStderr = fdopen(dup(fileno(stderr)), "a");
|
||||
origStdout = fdopen(dup(fileno(stdout)), "a");
|
||||
#ifdef Q_OS_WIN
|
||||
QString pipeName = QString::fromLatin1(PIPE_NAME).arg(QUuid::createUuid().toString());
|
||||
|
||||
SECURITY_ATTRIBUTES attributes = {sizeof(SECURITY_ATTRIBUTES), 0, false};
|
||||
hWrite = CreateNamedPipeW((wchar_t*)pipeName.utf16(), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
||||
hWrite = CreateNamedPipeW((wchar_t *)pipeName.utf16(), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
||||
PIPE_TYPE_BYTE | PIPE_WAIT, 1, PIPE_SIZE, PIPE_SIZE, 0, &attributes);
|
||||
|
||||
|
||||
int writeFd = _open_osfhandle((intptr_t)hWrite, _O_WRONLY | _O_TEXT);
|
||||
dup2(writeFd, fileno(stdout));
|
||||
dup2(writeFd, fileno(stderr));
|
||||
@ -373,6 +440,11 @@ void ConsoleWidget::redirectOutput()
|
||||
pipeSocket->connectToServer(pipeName, QIODevice::ReadOnly);
|
||||
#else
|
||||
pipe(redirectPipeFds);
|
||||
stdinFifoPath = QString(STDIN_PIPE_NAME).arg(QDir::tempPath(), QUuid::createUuid().toString());
|
||||
mkfifo(stdinFifoPath.toStdString().c_str(), (mode_t) 0777);
|
||||
stdinFile = open(stdinFifoPath.toStdString().c_str(), O_RDWR | O_ASYNC);
|
||||
|
||||
dup2(stdinFile, fileno(stdin));
|
||||
dup2(redirectPipeFds[PIPE_WRITE], fileno(stderr));
|
||||
dup2(redirectPipeFds[PIPE_WRITE], fileno(stdout));
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "core/MainWindow.h"
|
||||
#include "CutterDockWidget.h"
|
||||
#include "common/CommandTask.h"
|
||||
#include "common/DirectionalComboBox.h"
|
||||
|
||||
#include <QStringListModel>
|
||||
#include <QSocketNotifier>
|
||||
@ -49,7 +50,9 @@ public slots:
|
||||
private slots:
|
||||
void setupFont();
|
||||
|
||||
void on_inputLineEdit_returnPressed();
|
||||
void on_r2InputLineEdit_returnPressed();
|
||||
void on_debugeeInputLineEdit_returnPressed();
|
||||
void onIndexChange();
|
||||
|
||||
void on_execButton_clicked();
|
||||
|
||||
@ -75,6 +78,7 @@ private:
|
||||
void invalidateHistoryPosition();
|
||||
void removeLastLine();
|
||||
void executeCommand(const QString &command);
|
||||
void sendToStdin(const QString &input);
|
||||
void setWrap(bool wrap);
|
||||
|
||||
/**
|
||||
@ -99,12 +103,15 @@ private:
|
||||
QShortcut *historyDownShortcut;
|
||||
FILE *origStderr;
|
||||
FILE *origStdout;
|
||||
FILE *origStdin;
|
||||
QLocalSocket *pipeSocket;
|
||||
#ifdef Q_OS_WIN
|
||||
HANDLE hRead;
|
||||
HANDLE hWrite;
|
||||
#else
|
||||
int redirectPipeFds[2];
|
||||
int stdinFile;
|
||||
QString stdinFifoPath;
|
||||
QVector<char> *redirectionBuffer;
|
||||
QSocketNotifier *outputNotifier;
|
||||
#endif
|
||||
|
@ -64,9 +64,9 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="spacing">
|
||||
<number>4</number>
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>4</number>
|
||||
@ -80,8 +80,28 @@
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="inputLineEdit">
|
||||
<item row="0" column="0">
|
||||
<widget class="DirectionalComboBox" name="inputCombo">
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToContents</enum>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>R2 Console</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Debugee Input</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="r2InputLineEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
@ -99,7 +119,29 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="debugeeInputLineEdit">
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frame">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string> Enter input for the debugee</string>
|
||||
</property>
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QToolButton" name="execButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
@ -133,8 +175,17 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>DirectionalComboBox</class>
|
||||
<extends>QComboBox</extends>
|
||||
<header>common/DirectionalComboBox.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user