diff --git a/qrenderdoc/Code/QRDUtils.cpp b/qrenderdoc/Code/QRDUtils.cpp index 1a846c58f..01fb19073 100644 --- a/qrenderdoc/Code/QRDUtils.cpp +++ b/qrenderdoc/Code/QRDUtils.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -557,6 +558,16 @@ QTreeWidgetItem *makeTreeNode(const QVariantList &values) return ret; } +void deleteChildren(QTreeWidgetItem *item) +{ + while(item->childCount() > 0) + { + QTreeWidgetItem *child = item->takeChild(0); + deleteChildren(child); + delete child; + } +} + int Formatter::m_minFigures = 2, Formatter::m_maxFigures = 5, Formatter::m_expNegCutoff = 5, Formatter::m_expPosCutoff = 7; double Formatter::m_expNegValue = 0.00001; // 10^(-5) @@ -699,3 +710,16 @@ void ShowProgressDialog(QWidget *window, const QString &labelText, ProgressFinis tickerSemaphore.tryAcquire(); progressTickerThread.wait(); } + +QString GetSystemUsername() +{ + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + + QString username = env.value("USER"); + if(username == QString()) + username = env.value("USERNAME"); + if(username == QString()) + username = "Unknown_User"; + + return username; +} diff --git a/qrenderdoc/Code/QRDUtils.h b/qrenderdoc/Code/QRDUtils.h index b5603f434..518babbc9 100644 --- a/qrenderdoc/Code/QRDUtils.h +++ b/qrenderdoc/Code/QRDUtils.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include "renderdoc_replay.h" @@ -564,6 +565,13 @@ public: bool isCurrentThread() { return QThread::currentThread() == m_Thread; } }; +class RDProcess : public QProcess +{ +public: + RDProcess(QObject *parent = NULL) : QProcess(parent) {} + void detach() { setProcessState(QProcess::NotRunning); } +}; + class QFileFilterModel : public QSortFilterProxyModel { Q_OBJECT @@ -671,6 +679,7 @@ class QTreeWidgetItem; QTreeWidgetItem *makeTreeNode(const std::initializer_list &values); QTreeWidgetItem *makeTreeNode(const QVariantList &values); +void deleteChildren(QTreeWidgetItem *item); class QProgressDialog; @@ -679,3 +688,5 @@ typedef std::function ProgressFinishedMethod; void ShowProgressDialog(QWidget *window, const QString &labelText, ProgressFinishedMethod finished, ProgressUpdateMethod update = ProgressUpdateMethod()); + +QString GetSystemUsername(); diff --git a/qrenderdoc/Code/RemoteHost.cpp b/qrenderdoc/Code/RemoteHost.cpp index b6680180f..39cff14c6 100644 --- a/qrenderdoc/Code/RemoteHost.cpp +++ b/qrenderdoc/Code/RemoteHost.cpp @@ -25,6 +25,7 @@ #include "RemoteHost.h" #include #include +#include "Code/QRDUtils.h" #include "renderdoc_replay.h" RemoteHost::RemoteHost() @@ -102,7 +103,8 @@ void RemoteHost::CheckStatus() void RemoteHost::Launch() { - QProcess process; + RDProcess process; process.start(RunCommand); process.waitForFinished(2000); + process.detach(); } diff --git a/qrenderdoc/Widgets/Extended/RDTreeWidget.cpp b/qrenderdoc/Widgets/Extended/RDTreeWidget.cpp index 9881e639f..a70673e19 100644 --- a/qrenderdoc/Widgets/Extended/RDTreeWidget.cpp +++ b/qrenderdoc/Widgets/Extended/RDTreeWidget.cpp @@ -120,7 +120,8 @@ void RDTreeWidget::leaveEvent(QEvent *e) void RDTreeWidget::focusOutEvent(QFocusEvent *event) { - clearSelection(); + if(m_clearSelectionOnFocusLoss) + clearSelection(); QTreeWidget::focusOutEvent(event); } diff --git a/qrenderdoc/Windows/Dialogs/LiveCapture.cpp b/qrenderdoc/Windows/Dialogs/LiveCapture.cpp index e41e192d6..62a16273e 100644 --- a/qrenderdoc/Windows/Dialogs/LiveCapture.cpp +++ b/qrenderdoc/Windows/Dialogs/LiveCapture.cpp @@ -916,16 +916,8 @@ void LiveCapture::connectionClosed() void LiveCapture::connectionThreadEntry() { - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - - QString username = env.value("USER"); - if(username == QString()) - username = env.value("USERNAME"); - if(username == QString()) - username = "Unknown_User"; - m_Connection = RENDERDOC_CreateTargetControl(m_Hostname.toUtf8().data(), m_RemoteIdent, - username.toUtf8().data(), true); + GetSystemUsername().toUtf8().data(), true); if(m_Connection == NULL || !m_Connection->Connected()) { diff --git a/qrenderdoc/Windows/Dialogs/RemoteManager.cpp b/qrenderdoc/Windows/Dialogs/RemoteManager.cpp new file mode 100644 index 000000000..590168349 --- /dev/null +++ b/qrenderdoc/Windows/Dialogs/RemoteManager.cpp @@ -0,0 +1,676 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2017 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "RemoteManager.h" +#include +#include "3rdparty/flowlayout/FlowLayout.h" +#include "Code/RemoteHost.h" +#include "Windows/Dialogs/LiveCapture.h" +#include "Windows/MainWindow.h" +#include "ui_RemoteManager.h" + +struct RemoteConnect +{ + RemoteConnect() {} + RemoteConnect(const QString &h, uint32_t i) : host(h), ident(i) {} + QString host; + uint32_t ident = 0; +}; + +Q_DECLARE_METATYPE(RemoteConnect); + +static void setRemoteConnect(QTreeWidgetItem *item, const RemoteConnect &connect) +{ + if(!item) + return; + + item->setData(0, Qt::UserRole, QVariant::fromValue(connect)); +} + +static RemoteConnect getRemoteConnect(QTreeWidgetItem *item) +{ + if(!item) + return RemoteConnect(); + + return item->data(0, Qt::UserRole).value(); +} + +static void setRemoteHost(QTreeWidgetItem *item, RemoteHost *host) +{ + if(!item) + return; + + item->setData(0, Qt::UserRole + 1, QVariant::fromValue((uintptr_t)host)); +} + +static RemoteHost *getRemoteHost(QTreeWidgetItem *item) +{ + if(!item) + return NULL; + + return (RemoteHost *)item->data(0, Qt::UserRole + 1).value(); +} + +static void setItalic(QTreeWidgetItem *node, bool italic) +{ + QFont f = node->font(0); + f.setItalic(italic); + node->setFont(0, f); + node->setFont(1, f); +} + +RemoteManager::RemoteManager(CaptureContext *ctx, MainWindow *main) + : QDialog(NULL), ui(new Ui::RemoteManager), m_Ctx(ctx), m_Main(main) +{ + ui->setupUi(this); + + ui->hosts->setClearSelectionOnFocusLoss(false); + + ui->hosts->header()->setSectionResizeMode(0, QHeaderView::Stretch); + ui->hosts->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + lookupsProgressFlow = new QWidget(this); + + FlowLayout *flow = new FlowLayout(lookupsProgressFlow, 0, 3, 3); + + lookupsProgressFlow->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); + + flow->addWidget(ui->progressIcon); + flow->addWidget(ui->progressText); + flow->addWidget(ui->progressCount); + + QVBoxLayout *vertical = new QVBoxLayout(this); + + vertical->addWidget(ui->hosts); + vertical->addWidget(lookupsProgressFlow); + vertical->addWidget(ui->bottomLayout->parentWidget()); + + m_Ctx->Config.AddAndroidHosts(); + + for(RemoteHost *h : m_Ctx->Config.RemoteHosts) + addHost(h); + + on_hosts_itemClicked(ui->hosts->topLevelItem(0), 0); +} + +RemoteManager::~RemoteManager() +{ + delete ui; +} + +void RemoteManager::setRemoteServerLive(QTreeWidgetItem *node, bool live, bool busy) +{ + RemoteHost *host = getRemoteHost(node); + + if(!host) + return; + + host->ServerRunning = live; + host->Busy = busy; + + if(host->Hostname == "localhost") + { + node->setIcon(0, QIcon()); + node->setText(1, ""); + } + else + { + QString text = live ? tr("Remote server running") : tr("No remote server"); + + if(host->Connected) + text += tr(" (Active Context)"); + else if(host->VersionMismatch) + text += tr(" (Version Mismatch)"); + else if(host->Busy) + text += tr(" (Busy)"); + + node->setText(1, text); + + node->setIcon(0, + QIcon(QPixmap(QString::fromUtf8(live ? ":/connect.png" : ":/disconnect.png")))); + } +} + +bool RemoteManager::isRemoteServerLive(QTreeWidgetItem *node) +{ + RemoteHost *host = getRemoteHost(node); + return host && host->ServerRunning; +} + +void RemoteManager::addHost(RemoteHost *host) +{ + QTreeWidgetItem *node = makeTreeNode({host->Hostname, "..."}); + + setItalic(node, true); + node->setIcon(0, QIcon(QPixmap(QString::fromUtf8(":/hourglass.png")))); + setRemoteHost(node, host); + + ui->hosts->addTopLevelItem(node); + ui->hosts->clearSelection(); + node->setSelected(true); + + ui->refreshOne->setEnabled(false); + ui->refreshAll->setEnabled(false); + + m_Lookups.release(); + + refreshHost(node); + + updateLookupsStatus(); +} + +void RemoteManager::updateLookupsStatus() +{ + lookupsProgressFlow->setVisible(!ui->refreshAll->isEnabled()); + ui->progressCount->setText(tr("%1 lookups remaining").arg(m_Lookups.available())); +} + +void RemoteManager::runRemoteServer(QTreeWidgetItem *node) +{ + RemoteHost *host = getRemoteHost(node); + + if(!host) + return; + + host->Launch(); + + // now refresh this host + refreshHost(node); +} + +void RemoteManager::refreshHost(QTreeWidgetItem *node) +{ + RemoteHost *host = getRemoteHost(node); + + if(!host) + return; + + // this function looks up the remote connections and for each one open + // queries it for the API, target (usually executable name) and if any user is already connected + LambdaThread *th = new LambdaThread([this, node, host]() { + QString hostname = node->text(0); + + QByteArray username = GetSystemUsername().toUtf8(); + + host->CheckStatus(); + + GUIInvoke::call( + [this, node, host]() { setRemoteServerLive(node, host->ServerRunning, host->Busy); }); + + QByteArray hostnameBytes = hostname.toUtf8(); + + uint32_t nextIdent = 0; + + for(;;) + { + // just a sanity check to make sure we don't hit some unexpected case and infinite loop + uint32_t prevIdent = nextIdent; + + nextIdent = RENDERDOC_EnumerateRemoteTargets(hostnameBytes.data(), nextIdent); + + if(nextIdent == ~0U || prevIdent >= nextIdent) + break; + + TargetControl *conn = + RENDERDOC_CreateTargetControl(hostnameBytes.data(), nextIdent, username.data(), false); + + if(conn) + { + QString target = QString::fromUtf8(conn->GetTarget()); + QString api = QString::fromUtf8(conn->GetAPI()); + QString busy = QString::fromUtf8(conn->GetBusyClient()); + + QString running; + + if(busy != "") + running = tr("Running %1, %2 is connected").arg(api).arg(busy); + else + running = tr("Running %1").arg(api); + + RemoteConnect tag(hostname, nextIdent); + + GUIInvoke::call([this, node, target, running, tag]() { + QTreeWidgetItem *child = makeTreeNode({target, running}); + setRemoteConnect(child, tag); + node->addChild(child); + ui->hosts->expandItem(node); + }); + + conn->Shutdown(); + } + } + + GUIInvoke::call([node]() { setItalic(node, false); }); + + m_Lookups.acquire(); + + GUIInvoke::call([this]() { lookupComplete(); }); + }); + th->selfDelete(true); + th->start(); +} + +// don't allow the user to refresh until all pending connections have been checked +// (to stop flooding) +void RemoteManager::lookupComplete() +{ + if(m_Lookups.available() == 0) + { + ui->refreshOne->setEnabled(true); + ui->refreshAll->setEnabled(true); + + if(!isVisible()) + { + delete this; + return; + } + } + + updateConnectButton(); + updateLookupsStatus(); +} + +void RemoteManager::connectToApp(QTreeWidgetItem *node) +{ + if(node) + { + RemoteConnect connect = getRemoteConnect(node); + + if(connect.ident > 0) + { + LiveCapture *live = new LiveCapture(m_Ctx, connect.host, connect.ident, m_Main, m_Main); + m_Main->ShowLiveCapture(live); + accept(); + lookupComplete(); + } + } +} + +void RemoteManager::updateConnectButton() +{ + if(!ui->hosts->selectedItems().isEmpty()) + { + QTreeWidgetItem *item = ui->hosts->selectedItems()[0]; + + ui->connect->setEnabled(true); + ui->connect->setText(tr("Connect to App")); + + RemoteHost *host = getRemoteHost(item); + + if(host) + { + if(host->Hostname == "localhost") + { + ui->connect->setEnabled(false); + } + else if(host->ServerRunning) + { + ui->connect->setText(tr("Shutdown")); + + if(host->Busy && !host->Connected) + ui->connect->setEnabled(false); + } + else + { + ui->connect->setText(tr("Run Server")); + + if(host->RunCommand == "") + ui->connect->setEnabled(false); + } + } + } + else + { + ui->connect->setEnabled(false); + } +} + +void RemoteManager::addNewHost() +{ + QString host = ui->hostname->text().trimmed(); + if(!host.isEmpty()) + { + bool found = false; + + for(int i = 0; i < m_Ctx->Config.RemoteHosts.count(); i++) + { + if(m_Ctx->Config.RemoteHosts[i]->Hostname.compare(host, Qt::CaseInsensitive) == 0) + { + found = true; + break; + } + } + + if(!found) + { + RemoteHost *h = new RemoteHost(); + h->Hostname = host; + h->RunCommand = ui->runCommand->text().trimmed(); + + m_Ctx->Config.RemoteHosts.push_back(h); + m_Ctx->Config.Save(); + + addHost(h); + } + } + ui->hostname->setText(host); + on_hostname_textEdited(host); +} + +void RemoteManager::setRunCommand() +{ + if(ui->hosts->selectedItems().isEmpty()) + return; + + RemoteHost *h = getRemoteHost(ui->hosts->selectedItems()[0]); + + if(h) + { + h->RunCommand = ui->runCommand->text().trimmed(); + m_Ctx->Config.Save(); + } +} + +void RemoteManager::on_hosts_itemActivated(QTreeWidgetItem *item, int column) +{ + RemoteConnect connect = getRemoteConnect(item); + if(connect.ident > 0) + connectToApp(item); +} + +void RemoteManager::on_hosts_itemClicked(QTreeWidgetItem *item, int column) +{ + ui->addUpdateHost->setText(tr("Add")); + ui->addUpdateHost->setEnabled(true); + ui->deleteHost->setEnabled(false); + ui->refreshOne->setEnabled(false); + ui->hostname->setEnabled(true); + ui->addUpdateHost->setEnabled(true); + ui->runCommand->setEnabled(true); + + ui->runCommand->setText(""); + ui->hostname->setText(""); + + RemoteHost *host = getRemoteHost(item); + + if(host) + { + if(ui->refreshAll->isEnabled()) + ui->refreshOne->setEnabled(true); + + if(host->Hostname == "localhost") + { + ui->runCommand->setText(""); + ui->hostname->setText(""); + } + else + { + ui->deleteHost->setEnabled(true); + ui->runCommand->setText(host->RunCommand); + ui->hostname->setText(host->Hostname); + + ui->addUpdateHost->setText(tr("Update")); + } + } + + updateConnectButton(); +} + +void RemoteManager::on_hostname_textEdited(const QString &text) +{ + ui->addUpdateHost->setText(tr("Add")); + ui->addUpdateHost->setEnabled(true); + ui->deleteHost->setEnabled(false); + ui->refreshOne->setEnabled(false); + ui->hostname->setEnabled(true); + ui->addUpdateHost->setEnabled(true); + ui->runCommand->setEnabled(true); + + QTreeWidgetItem *node = NULL; + + for(int i = 0; i < ui->hosts->topLevelItemCount(); i++) + { + QTreeWidgetItem *n = ui->hosts->topLevelItem(i); + + RemoteHost *host = getRemoteHost(n); + + if(n->text(0) == text) + { + if(ui->refreshAll->isEnabled()) + ui->refreshOne->setEnabled(true); + + ui->addUpdateHost->setText(tr("Update")); + + if(text == "localhost") + { + ui->hostname->setEnabled(false); + ui->addUpdateHost->setEnabled(false); + ui->runCommand->setEnabled(false); + } + else + { + ui->deleteHost->setEnabled(true); + ui->runCommand->setText(host->RunCommand); + } + + node = n; + break; + } + } + + ui->hosts->clearSelection(); + if(node) + node->setSelected(true); + + updateConnectButton(); +} + +void RemoteManager::on_hosts_keyPress(QKeyEvent *event) +{ + if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) + { + if(ui->connect->isEnabled()) + on_connect_clicked(); + } + + if(event->key() == Qt::Key_Delete) + on_deleteHost_clicked(); +} + +void RemoteManager::on_hostname_keyPress(QKeyEvent *event) +{ + if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) + { + if(ui->addUpdateHost->isEnabled()) + on_addUpdateHost_clicked(); + } +} + +void RemoteManager::on_runCommand_keyPress(QKeyEvent *event) +{ + if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) + { + if(ui->addUpdateHost->isEnabled()) + on_addUpdateHost_clicked(); + } +} + +void RemoteManager::on_addUpdateHost_clicked() +{ + if(!ui->hosts->selectedItems().isEmpty() && getRemoteHost(ui->hosts->selectedItems()[0])) + setRunCommand(); + else + addNewHost(); +} + +void RemoteManager::on_refreshAll_clicked() +{ + if(m_Lookups.available()) + return; + + ui->refreshOne->setEnabled(false); + ui->refreshAll->setEnabled(false); + + for(int i = 0; i < ui->hosts->topLevelItemCount(); i++) + { + QTreeWidgetItem *n = ui->hosts->topLevelItem(i); + + deleteChildren(n); + setItalic(n, true); + n->setIcon(0, QIcon(QPixmap(QString::fromUtf8(":/hourglass.png")))); + + refreshHost(n); + } + + updateLookupsStatus(); +} + +void RemoteManager::on_refreshOne_clicked() +{ + if(m_Lookups.available() || ui->hosts->selectedItems().isEmpty()) + return; + + ui->refreshOne->setEnabled(false); + ui->refreshAll->setEnabled(false); + + QTreeWidgetItem *n = ui->hosts->selectedItems()[0]; + { + deleteChildren(n); + setItalic(n, true); + n->setIcon(0, QIcon(QPixmap(QString::fromUtf8(":/hourglass.png")))); + + refreshHost(n); + } + + updateLookupsStatus(); +} + +void RemoteManager::on_connect_clicked() +{ + if(ui->hosts->selectedItems().isEmpty()) + return; + + QTreeWidgetItem *node = ui->hosts->selectedItems()[0]; + + RemoteConnect connect = getRemoteConnect(node); + RemoteHost *host = getRemoteHost(node); + + if(connect.ident > 0) + { + connectToApp(node); + } + else if(host) + { + if(host->ServerRunning) + { + QMessageBox::StandardButton res = RDDialog::question( + this, tr("Remote server shutdown"), + tr("Are you sure you wish to shut down running remote server on %1?").arg(host->Hostname), + RDDialog::YesNoCancel); + + if(res == QMessageBox::Cancel || res == QMessageBox::No) + return; + + // shut down + if(host->Connected) + { + m_Ctx->Renderer()->ShutdownServer(); + setRemoteServerLive(node, false, false); + } + else + { + RemoteServer *server = NULL; + ReplayCreateStatus status = + RENDERDOC_CreateRemoteServerConnection(host->Hostname.toUtf8().data(), 0, &server); + if(server) + server->ShutdownServerAndConnection(); + setRemoteServerLive(node, false, false); + + if(status != eReplayCreate_Success) + RDDialog::critical(this, tr("Shutdown error"), + tr("Error shutting down remote server: %1").arg(ToQStr(status))); + } + + updateConnectButton(); + } + else + { + // try to run + ui->refreshOne->setEnabled(false); + ui->refreshAll->setEnabled(false); + + m_Lookups.release(); + + LambdaThread *th = new LambdaThread([this, node]() { runRemoteServer(node); }); + th->selfDelete(true); + th->start(); + + updateLookupsStatus(); + } + } +} + +void RemoteManager::on_deleteHost_clicked() +{ + if(ui->hosts->selectedItems().isEmpty()) + return; + + QTreeWidgetItem *item = ui->hosts->selectedItems()[0]; + + RemoteHost *host = getRemoteHost(item); + + // don't delete running instances on a host + if(item->parent() != NULL || !host) + return; + + QString hostname = item->text(0); + + if(hostname == "localhost") + return; + + QMessageBox::StandardButton res = RDDialog::question( + this, tr("Deleting host"), tr("Are you sure you wish to delete %1?").arg(hostname), + RDDialog::YesNoCancel); + + if(res == QMessageBox::Cancel || res == QMessageBox::No) + return; + + if(res == QMessageBox::Yes) + { + int idx = m_Ctx->Config.RemoteHosts.indexOf(host); + delete m_Ctx->Config.RemoteHosts.takeAt(idx); + m_Ctx->Config.Save(); + + deleteChildren(item); + + delete ui->hosts->takeTopLevelItem(ui->hosts->indexOfTopLevelItem(item)); + + ui->hosts->clearSelection(); + + ui->hostname->setText(hostname); + on_hostname_textEdited(hostname); + } +} diff --git a/qrenderdoc/Windows/Dialogs/RemoteManager.h b/qrenderdoc/Windows/Dialogs/RemoteManager.h new file mode 100644 index 000000000..1f23c6424 --- /dev/null +++ b/qrenderdoc/Windows/Dialogs/RemoteManager.h @@ -0,0 +1,84 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2017 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#pragma once + +#include +#include + +namespace Ui +{ +class RemoteManager; +} + +class QTreeWidgetItem; +class CaptureContext; +class MainWindow; +class RemoteHost; + +class RemoteManager : public QDialog +{ + Q_OBJECT + +public: + explicit RemoteManager(CaptureContext *ctx, MainWindow *main); + ~RemoteManager(); + +private slots: + // automatic slots + void on_hosts_itemClicked(QTreeWidgetItem *item, int column); + void on_hosts_itemActivated(QTreeWidgetItem *item, int column); + void on_hostname_textEdited(const QString &text); + void on_hosts_keyPress(QKeyEvent *event); + void on_hostname_keyPress(QKeyEvent *event); + void on_runCommand_keyPress(QKeyEvent *event); + void on_refreshOne_clicked(); + void on_addUpdateHost_clicked(); + void on_refreshAll_clicked(); + void on_connect_clicked(); + void on_deleteHost_clicked(); + +private: + Ui::RemoteManager *ui; + CaptureContext *m_Ctx; + MainWindow *m_Main; + QWidget *lookupsProgressFlow; + + QSemaphore m_Lookups; + + bool isRemoteServerLive(QTreeWidgetItem *node); + void setRemoteServerLive(QTreeWidgetItem *node, bool live, bool busy); + + void addHost(RemoteHost *host); + void updateLookupsStatus(); + void runRemoteServer(QTreeWidgetItem *node); + + void refreshHost(QTreeWidgetItem *node); + void lookupComplete(); + void connectToApp(QTreeWidgetItem *node); + + void updateConnectButton(); + void addNewHost(); + void setRunCommand(); +}; diff --git a/qrenderdoc/Windows/Dialogs/RemoteManager.ui b/qrenderdoc/Windows/Dialogs/RemoteManager.ui new file mode 100644 index 000000000..e1334a364 --- /dev/null +++ b/qrenderdoc/Windows/Dialogs/RemoteManager.ui @@ -0,0 +1,201 @@ + + + RemoteManager + + + + 0 + 0 + 783 + 545 + + + + Remote Host Manager + + + + + 180 + 20 + 256 + 192 + + + + + 0 + 0 + + + + Qt::ScrollBarAlwaysOn + + + false + + + + Hostname + + + + + Running + + + + + + + 180 + 300 + 47 + 13 + + + + + + + :/hourglass.png + + + + + + 260 + 300 + 47 + 13 + + + + Remote connections in progress. Please wait... + + + + + + 320 + 300 + 47 + 13 + + + + 999 connections active + + + + + + 140 + 360 + 381 + 155 + + + + + + + Host configuration + + + + + + Hostname: + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + + + + Add + + + + + + + Run Command: Configure a command to run that launches the remote server on this host. + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + true + + + + + + + + + + + + + Operations + + + + + + Refresh Selected + + + + + + + Refresh All + + + + + + + Connect to App + + + + + + + Delete + + + + + + + + + + + + RDTreeWidget + QTreeWidget +
Widgets/Extended/RDTreeWidget.h
+
+ + RDLineEdit + QLineEdit +
Widgets/Extended/RDLineEdit.h
+
+
+ + + + +
diff --git a/qrenderdoc/Windows/MainWindow.cpp b/qrenderdoc/Windows/MainWindow.cpp index 38c8055dd..e21673227 100644 --- a/qrenderdoc/Windows/MainWindow.cpp +++ b/qrenderdoc/Windows/MainWindow.cpp @@ -37,6 +37,7 @@ #include "Windows/Dialogs/AboutDialog.h" #include "Windows/Dialogs/CaptureDialog.h" #include "Windows/Dialogs/LiveCapture.h" +#include "Windows/Dialogs/RemoteManager.h" #include "Windows/Dialogs/SettingsDialog.h" #include "Windows/Dialogs/SuggestRemoteDialog.h" #include "APIInspector.h" @@ -1333,6 +1334,17 @@ void MainWindow::on_action_Resolve_Symbols_triggered() m_Ctx->apiInspector()->on_apiEvents_itemSelectionChanged(); } +void MainWindow::on_action_Attach_to_Running_Instance_triggered() +{ + on_action_Manage_Remote_Servers_triggered(); +} + +void MainWindow::on_action_Manage_Remote_Servers_triggered() +{ + // the manager deletes itself when all lookups terminate + RDDialog::show(new RemoteManager(m_Ctx, this)); +} + void MainWindow::on_action_Start_Android_Remote_Server_triggered() { RENDERDOC_StartAndroidRemoteServer(); diff --git a/qrenderdoc/Windows/MainWindow.h b/qrenderdoc/Windows/MainWindow.h index 7632c62cc..00a7839e7 100644 --- a/qrenderdoc/Windows/MainWindow.h +++ b/qrenderdoc/Windows/MainWindow.h @@ -98,6 +98,8 @@ private slots: void on_action_Statistics_Viewer_triggered(); void on_action_Inject_into_Process_triggered(); void on_action_Resolve_Symbols_triggered(); + void on_action_Attach_to_Running_Instance_triggered(); + void on_action_Manage_Remote_Servers_triggered(); void on_action_Start_Android_Remote_Server_triggered(); void on_action_Settings_triggered(); diff --git a/qrenderdoc/qrenderdoc.pro b/qrenderdoc/qrenderdoc.pro index 98ff08d9a..a3d555021 100644 --- a/qrenderdoc/qrenderdoc.pro +++ b/qrenderdoc/qrenderdoc.pro @@ -141,7 +141,8 @@ SOURCES += Code/qrenderdoc.cpp \ Windows/Dialogs/OrderedListEditor.cpp \ Widgets/Extended/RDTableWidget.cpp \ Windows/Dialogs/SuggestRemoteDialog.cpp \ - Windows/Dialogs/VirtualFileDialog.cpp + Windows/Dialogs/VirtualFileDialog.cpp \ + Windows/Dialogs/RemoteManager.cpp HEADERS += Code/CaptureContext.h \ Code/qprocessinfo.h \ @@ -185,7 +186,8 @@ HEADERS += Code/CaptureContext.h \ Windows/Dialogs/OrderedListEditor.h \ Widgets/Extended/RDTableWidget.h \ Windows/Dialogs/SuggestRemoteDialog.h \ - Windows/Dialogs/VirtualFileDialog.h + Windows/Dialogs/VirtualFileDialog.h \ + Windows/Dialogs/RemoteManager.h FORMS += Windows/Dialogs/AboutDialog.ui \ Windows/MainWindow.ui \ @@ -211,7 +213,8 @@ FORMS += Windows/Dialogs/AboutDialog.ui \ Windows/Dialogs/SettingsDialog.ui \ Windows/Dialogs/OrderedListEditor.ui \ Windows/Dialogs/SuggestRemoteDialog.ui \ - Windows/Dialogs/VirtualFileDialog.ui + Windows/Dialogs/VirtualFileDialog.ui \ + Windows/Dialogs/RemoteManager.ui RESOURCES += Resources/resources.qrc diff --git a/qrenderdoc/qrenderdoc_local.vcxproj b/qrenderdoc/qrenderdoc_local.vcxproj index e8b2610a4..8f68d94d6 100644 --- a/qrenderdoc/qrenderdoc_local.vcxproj +++ b/qrenderdoc/qrenderdoc_local.vcxproj @@ -657,6 +657,7 @@ + @@ -718,6 +719,7 @@ + @@ -865,6 +867,7 @@ + @@ -1022,6 +1025,12 @@ MOC %(Filename).h generated\moc_%(Filename).cpp + + %(Fullpath);$(ProjectDir)3rdparty\qt\$(Platform)\bin\moc.exe;%(AdditionalInputs) + $(ProjectDir)3rdparty\qt\$(Platform)\bin\moc.exe -DUNICODE -DWIN32 -DWIN64 -D_WIN32 -D_WIN64 -DRENDERDOC_PLATFORM_WIN32 -DSCINTILLA_QT=1 -DSCI_LEXER=1 -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1900 -I$(ProjectDir) -I$(SolutionDir)\renderdoc\api\replay -I$(ProjectDir)3rdparty\qt\$(Platform)\mkspecs/win32-msvc2015 -I$(ProjectDir)3rdparty\qt\$(Platform)\include -I$(ProjectDir)3rdparty\qt\$(Platform)\include\QtWidgets -I$(ProjectDir)3rdparty\qt\$(Platform)\include\QtGui -I$(ProjectDir)3rdparty\qt\$(Platform)\include\QtCore %(Fullpath) -o $(ProjectDir)generated\moc_%(Filename).cpp + MOC %(Filename).h + generated\moc_%(Filename).cpp + %(Fullpath);$(ProjectDir)3rdparty\qt\$(Platform)\bin\moc.exe;%(AdditionalInputs) $(ProjectDir)3rdparty\qt\$(Platform)\bin\moc.exe -DUNICODE -DWIN32 -DWIN64 -D_WIN32 -D_WIN64 -DRENDERDOC_PLATFORM_WIN32 -DSCINTILLA_QT=1 -DSCI_LEXER=1 -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1900 -I$(ProjectDir) -I$(SolutionDir)\renderdoc\api\replay -I$(ProjectDir)3rdparty\qt\$(Platform)\mkspecs/win32-msvc2015 -I$(ProjectDir)3rdparty\qt\$(Platform)\include -I$(ProjectDir)3rdparty\qt\$(Platform)\include\QtWidgets -I$(ProjectDir)3rdparty\qt\$(Platform)\include\QtGui -I$(ProjectDir)3rdparty\qt\$(Platform)\include\QtCore %(Fullpath) -o $(ProjectDir)generated\moc_%(Filename).cpp @@ -1175,6 +1184,12 @@ UIC %(Filename).ui generated\ui_%(Filename).h + + %(Fullpath);$(ProjectDir)3rdparty\qt\$(Platform)\bin\uic.exe;%(AdditionalInputs) + $(ProjectDir)3rdparty\qt\$(Platform)\bin\uic.exe %(Fullpath) -o $(ProjectDir)generated\ui_%(Filename).h + UIC %(Filename).ui + generated\ui_%(Filename).h + %(Fullpath);$(ProjectDir)3rdparty\qt\$(Platform)\bin\uic.exe;%(AdditionalInputs) $(ProjectDir)3rdparty\qt\$(Platform)\bin\uic.exe %(Fullpath) -o $(ProjectDir)generated\ui_%(Filename).h diff --git a/qrenderdoc/qrenderdoc_local.vcxproj.filters b/qrenderdoc/qrenderdoc_local.vcxproj.filters index 8a41660af..6604ba132 100644 --- a/qrenderdoc/qrenderdoc_local.vcxproj.filters +++ b/qrenderdoc/qrenderdoc_local.vcxproj.filters @@ -504,6 +504,12 @@ Code + + Generated Files + + + Windows\Dialogs + @@ -773,6 +779,9 @@ Code + + Generated Files + @@ -1180,5 +1189,11 @@ 3rdparty\Scintilla\qt\ScintillaEditBase + + Windows\Dialogs + + + Windows\Dialogs + \ No newline at end of file