void CaptureContext::LoadLogfile(const QString &logFile, const QString &origFilename, bool temporary, bool local) { m_Progress = new QProgressDialog(QString("Loading Log"), QString(), 0, 1000, m_MainWindow); m_Progress->setWindowTitle("Please Wait"); m_Progress->setWindowFlags(Qt::CustomizeWindowHint | Qt::Dialog | Qt::WindowTitleHint); m_Progress->setWindowIcon(QIcon()); m_Progress->setMinimumSize(QSize(250, 0)); m_Progress->setMaximumSize(QSize(250, 10000)); m_Progress->setCancelButton(NULL); m_Progress->setMinimumDuration(0); m_Progress->setWindowModality(Qt::ApplicationModal); m_Progress->setValue(0); QLabel *label = new QLabel(m_Progress); label->setText(QString("Loading Log: %1").arg(origFilename)); label->setAlignment(Qt::AlignCenter); label->setWordWrap(true); m_Progress->setLabel(label); LambdaThread *thread = new LambdaThread([this, logFile, origFilename, temporary, local]() { LoadLogfileThreaded(logFile, origFilename, temporary, local); GUIInvoke::call([this, origFilename]() { delete m_Progress; m_Progress = NULL; }); }); thread->selfDelete(true); thread->start(); }
void CaptureDialog::CheckAndroidSetup(QString &filename) { ui->androidScan->setVisible(true); ui->androidWarn->setVisible(false); LambdaThread *scan = new LambdaThread([this, filename]() { rdcstr host = m_Ctx.Replay().CurrentRemote()->hostname; RENDERDOC_CheckAndroidPackage(host.c_str(), filename.toUtf8().data(), &m_AndroidFlags); const bool debuggable = bool(m_AndroidFlags & AndroidFlags::Debuggable); const bool hasroot = bool(m_AndroidFlags & AndroidFlags::RootAccess); if(!debuggable && !hasroot) { // Check failed - set the warning visible GUIInvoke::call([this]() { ui->androidScan->setVisible(false); ui->androidWarn->setVisible(true); }); } else { // Check passed, either app is debuggable or we have root - no warnings needed GUIInvoke::call([this]() { ui->androidScan->setVisible(false); ui->androidWarn->setVisible(false); }); } }); scan->start(); scan->deleteLater(); }
void MainWindow::on_action_Open_Log_triggered() { QString filename = QFileDialog::getOpenFileName(this, "Select Logfile to open", "", "Log Files (*.rdc);;Image Files (*.dds *.hdr *.exr *.bmp *.jpg *.jpeg *.png *.tga *.gif *.psd;;All Files (*.*)"); QFileInfo checkFile(filename); if(filename != "" && checkFile.exists() && checkFile.isFile()) { LambdaThread *thread = new LambdaThread([filename, this]() { m_Core->LoadLogfile(filename, false); }); thread->start(); } }
void MainWindow::on_action_Resolve_Symbols_triggered() { m_Ctx->Renderer()->AsyncInvoke([this](IReplayRenderer *r) { r->InitResolver(); }); QProgressDialog *m_Progress = new QProgressDialog(tr("Please Wait - Resolving Symbols"), QString(), 0, 0, this); m_Progress->setWindowTitle("Please Wait"); m_Progress->setWindowFlags(Qt::CustomizeWindowHint | Qt::Dialog | Qt::WindowTitleHint); m_Progress->setWindowIcon(QIcon()); m_Progress->setMinimumSize(QSize(250, 0)); m_Progress->setMaximumSize(QSize(250, 10000)); m_Progress->setCancelButton(NULL); m_Progress->setMinimumDuration(0); m_Progress->setWindowModality(Qt::ApplicationModal); m_Progress->setValue(0); QLabel *label = new QLabel(m_Progress); label->setText(tr("Please Wait - Resolving Symbols")); label->setAlignment(Qt::AlignCenter); label->setWordWrap(true); m_Progress->setLabel(label); LambdaThread *thread = new LambdaThread([this, m_Progress]() { bool running = true; while(running) { // just bail if we managed to get here without a resolver. m_Ctx->Renderer()->BlockInvoke( [&running](IReplayRenderer *r) { running = r->HasCallstacks() && !r->InitResolver(); }); } GUIInvoke::call([this, m_Progress]() { m_Progress->hide(); delete m_Progress; if(m_Ctx->hasAPIInspector()) m_Ctx->apiInspector()->on_apiEvents_itemSelectionChanged(); }); }); thread->selfDelete(true); thread->start(); }
void RemoteManager::on_connect_clicked() { RDTreeWidgetItem *node = ui->hosts->selectedItem(); if(!node) return; 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->Name()), RDDialog::YesNoCancel); if(res == QMessageBox::Cancel || res == QMessageBox::No) return; // shut down if(host->connected) { m_Ctx.Replay().ShutdownServer(); setRemoteServerLive(node, false, false); } else { IRemoteServer *server = NULL; ReplayStatus status = RENDERDOC_CreateRemoteServerConnection(host->hostname.c_str(), 0, &server); if(server) server->ShutdownServerAndConnection(); setRemoteServerLive(node, false, false); if(status != ReplayStatus::Succeeded) 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::refreshHost(RDTreeWidgetItem *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]() { QByteArray username = GetSystemUsername().toUtf8(); host->CheckStatus(); GUIInvoke::call( [this, node, host]() { setRemoteServerLive(node, host->serverRunning, host->busy); }); 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(host->hostname.c_str(), nextIdent); if(nextIdent == 0 || prevIdent >= nextIdent) break; ITargetControl *conn = RENDERDOC_CreateTargetControl(host->hostname.c_str(), 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.isEmpty()) running = tr("Running %1, %2 is connected").arg(api).arg(busy); else running = tr("Running %1").arg(api); RemoteConnect tag(host->hostname, host->Name(), nextIdent); GUIInvoke::call([this, node, target, running, tag]() { RDTreeWidgetItem *child = new RDTreeWidgetItem({target, running}); setRemoteConnect(child, tag); node->addChild(child); ui->hosts->expandItem(node); }); conn->Shutdown(); } } GUIInvoke::call([node]() { node->setItalic(false); }); m_Lookups.acquire(); GUIInvoke::call([this]() { updateStatus(); }); }); th->selfDelete(true); th->start(); }
void CaptureContext::RecompressCapture() { QString destFilename = GetCaptureFilename(); QString tempFilename; ICaptureFile *cap = NULL; ICaptureFile *tempCap = NULL; bool inplace = false; if(IsCaptureTemporary() || !IsCaptureLocal()) { QMessageBox::StandardButton res = RDDialog::question(m_MainWindow, tr("Unsaved capture"), tr("To recompress a capture you must save it first. Save this capture?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); if(res == QMessageBox::Cancel || res == QMessageBox::No) return; destFilename = m_MainWindow->GetSavePath(); // if it's already local, we'll do the save as part of the recompression convert. If it's // remote, we need to copy it first, but we copy it to a temporary so we can do the conversion // to the target location if(IsCaptureLocal()) { tempFilename = GetCaptureFilename(); } else { tempFilename = TempCaptureFilename(lit("recompress")); Replay().CopyCaptureFromRemote(GetCaptureFilename(), tempFilename, m_MainWindow); if(!QFile::exists(tempFilename)) { RDDialog::critical(m_MainWindow, tr("Failed to save capture"), tr("Capture couldn't be saved from remote.")); return; } } } else { // if we're doing this inplace on an already saved capture, then we need to recompress to a // temporary and close/move it afterwards. inplace = true; destFilename = TempCaptureFilename(lit("recompress")); } if(IsCaptureLocal()) { // for local files we already have a handle. We'll reuse it, then re-open cap = Replay().GetCaptureFile(); } else { // for remote files we open a new short-lived handle on the temporary file tempCap = cap = RENDERDOC_OpenCaptureFile(); cap->OpenFile(tempFilename.toUtf8().data(), "rdc"); } if(!cap) { RDDialog::critical(m_MainWindow, tr("Unexpected missing handle"), tr("Couldn't get open handle to file for recompression.")); return; } int index = cap->FindSectionByType(SectionType::FrameCapture); SectionProperties props = cap->GetSectionProperties(index); if(props.flags & SectionFlags::ZstdCompressed) { RDDialog::information(m_MainWindow, tr("Capture already compressed"), tr("This capture is already compressed as much as is possible.")); if(tempCap) tempCap->Shutdown(); if(!tempFilename.isEmpty()) QFile::remove(tempFilename); return; } // convert from the currently open cap to the destination float progress = 0.0f; LambdaThread *th = new LambdaThread([this, cap, destFilename, &progress]() { cap->Convert(destFilename.toUtf8().data(), "rdc", [&progress](float p) { progress = p; }); }); th->start(); // wait a few ms before popping up a progress bar th->wait(500); if(th->isRunning()) { ShowProgressDialog(m_MainWindow, tr("Recompressing file."), [th]() { return !th->isRunning(); }, [&progress]() { return progress; }); } th->deleteLater(); if(inplace) { // if we're recompressing "in place", we need to close our capture, move the temporary over // the original, then re-open. // this releases the hold over the real desired location. cap->OpenFile("", ""); // now remove the old capture QFile::remove(GetCaptureFilename()); // move the recompressed one over QFile::rename(destFilename, GetCaptureFilename()); // and re-open cap->OpenFile(GetCaptureFilename().c_str(), "rdc"); } else { // we've converted into the desired location. We don't have to do anything else but mark our // new locally saved non-temporary status. m_CaptureFile = destFilename; m_CaptureLocal = true; m_CaptureTemporary = false; // open the saved capture file. This will let us remove the old file too Replay().ReopenCaptureFile(m_CaptureFile); m_CaptureMods = CaptureModifications::All; SaveChanges(); } // close any temporary resources if(tempCap) tempCap->Shutdown(); if(!tempFilename.isEmpty()) QFile::remove(tempFilename); }
void CaptureContext::LoadCapture(const rdcstr &captureFile, const rdcstr &origFilename, bool temporary, bool local) { m_LoadInProgress = true; if(local) m_Config.CrashReport_LastOpenedCapture = origFilename; bool newCapture = (!temporary && !Config().RecentCaptureFiles.contains(origFilename)); LambdaThread *thread = new LambdaThread([this, captureFile, origFilename, temporary, local]() { LoadCaptureThreaded(captureFile, origFilename, temporary, local); }); thread->selfDelete(true); thread->start(); QElapsedTimer loadTimer; loadTimer.start(); ShowProgressDialog(m_MainWindow, tr("Loading Capture: %1").arg(origFilename), [this]() { return !m_LoadInProgress; }, [this]() { return UpdateLoadProgress(); }); ANALYTIC_ADDAVG(Performance.LoadTime, double(loadTimer.nsecsElapsed() * 1.0e-9)); ANALYTIC_SET(CaptureFeatures.ShaderLinkage, m_APIProps.ShaderLinkage); ANALYTIC_SET(CaptureFeatures.YUVTextures, m_APIProps.YUVTextures); ANALYTIC_SET(CaptureFeatures.SparseResources, m_APIProps.SparseResources); ANALYTIC_SET(CaptureFeatures.MultiGPU, m_APIProps.MultiGPU); ANALYTIC_SET(CaptureFeatures.D3D12Bundle, m_APIProps.D3D12Bundle); m_MainWindow->setProgress(-1.0f); if(m_CaptureLoaded) { m_CaptureTemporary = temporary; m_CaptureMods = CaptureModifications::NoModifications; rdcarray<ICaptureViewer *> viewers(m_CaptureViewers); // make sure we're on a consistent event before invoking viewer forms if(m_LastDrawcall) SetEventID(viewers, m_LastDrawcall->eventId, true); else if(!m_Drawcalls.empty()) SetEventID(viewers, m_Drawcalls.back().eventId, true); GUIInvoke::blockcall([&viewers]() { // notify all the registers viewers that a capture has been loaded for(ICaptureViewer *viewer : viewers) { if(viewer) viewer->OnCaptureLoaded(); } }); if(newCapture && m_Notes.contains(lit("comments"))) { if(!HasCommentView()) ShowCommentView(); RaiseDockWindow(GetCommentView()->Widget()); } } }
bool RunProcessAsAdmin(const QString &fullExecutablePath, const QStringList ¶ms, std::function<void()> finishedCallback) { #if defined(Q_OS_WIN32) std::wstring wideExe = fullExecutablePath.toStdWString(); std::wstring wideParams = params.join(QChar(' ')).toStdWString(); SHELLEXECUTEINFOW info = {}; info.cbSize = sizeof(info); info.fMask = SEE_MASK_NOCLOSEPROCESS; info.lpVerb = L"runas"; info.lpFile = wideExe.c_str(); info.lpParameters = wideParams.c_str(); info.nShow = SW_SHOWNORMAL; ShellExecuteExW(&info); if((uintptr_t)info.hInstApp > 32 && info.hProcess != NULL) { if(finishedCallback) { HANDLE h = info.hProcess; // do the wait on another thread LambdaThread *thread = new LambdaThread([h, finishedCallback]() { WaitForSingleObject(h, 30000); CloseHandle(h); GUIInvoke::call(finishedCallback); }); thread->selfDelete(true); thread->start(); } else { CloseHandle(info.hProcess); } return true; } return false; #else // try to find a way to run the application elevated. const QString graphicalSudo[] = { "pkexec", "kdesudo", "gksudo", "beesu", }; // if none of the graphical options, then look for sudo and either const QString termEmulator[] = { "x-terminal-emulator", "gnome-terminal", "knosole", "xterm", }; for(const QString &sudo : graphicalSudo) { QString inPath = QStandardPaths::findExecutable(sudo); // can't find in path if(inPath.isEmpty()) continue; QProcess *process = new QProcess; QStringList sudoParams; sudoParams << fullExecutablePath; for(const QString &p : params) sudoParams << p; qInfo() << "Running" << sudo << "with params" << sudoParams; // run with sudo process->start(sudo, sudoParams); // when the process exits, call the callback and delete QObject::connect(process, OverloadedSlot<int>::of(&QProcess::finished), [process, finishedCallback](int exitCode) { process->deleteLater(); GUIInvoke::call(finishedCallback); }); return true; } QString sudo = QStandardPaths::findExecutable("sudo"); if(sudo.isEmpty()) { qCritical() << "Couldn't find graphical or terminal sudo program!\n" << "Please run " << fullExecutablePath << "with args" << params << "manually."; return false; } for(const QString &term : termEmulator) { QString inPath = QStandardPaths::findExecutable(term); // can't find in path if(inPath.isEmpty()) continue; QProcess *process = new QProcess; // run terminal sudo with emulator QStringList termParams; termParams << "-e" << QString("bash -c 'sudo %1 %2'").arg(fullExecutablePath).arg(params.join(QChar(' '))); process->start(term, termParams); // when the process exits, call the callback and delete QObject::connect(process, OverloadedSlot<int>::of(&QProcess::finished), [process, finishedCallback](int exitCode) { process->deleteLater(); GUIInvoke::call(finishedCallback); }); return true; } qCritical() << "Couldn't find graphical or terminal emulator to launch sudo.\n" << "Please run " << fullExecutablePath << "with args" << params << "manually."; return false; #endif }