/
githubmirror
/
x64dbg
Обзор
Документация
Войти
/
githubmirror
/
x64dbg
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
development
src/gui/Src/Gui/MainWindow.cpp
2 896 строк
107 KB
Xander Steyn
Show hotkeys in toolbar tooltips (#3864)
27 май 2026, 01:01
Не верифицирован
27 май 2026, 01:01
9bb33a8
Код
Авторство
О чём код?
#include "MainWindow.h" #include "ui_MainWindow.h" #include <QMutex> #include <QMessageBox> #include <QToolButton> #include <QIcon> #include <QUrl> #include <QFileDialog> #include <QMimeData> #include <QDesktopServices> #include <QStatusTipEvent> #include "Configuration.h" #include "SettingsDialog.h" #include "AppearanceDialog.h" #include "ShortcutsDialog.h" #include "AttachDialog.h" #include "LineEditDialog.h" #include "StringUtil.h" #include "MiscUtil.h" #include "FavouriteTools.h" #include "CPUDisassembly.h" #include "CloseDialog.h" #include "CommandLineEdit.h" #include "TabWidget.h" #include "CPUWidget.h" #include "MemoryMapView.h" #include "CallStackView.h" #include "SEHChainView.h" #include "LogView.h" #include "SymbolView.h" #include "BreakpointsView.h" #include "ScriptView.h" #include "ReferenceManager.h" #include "ThreadView.h" #include "PatchDialog.h" #include "CalculatorDialog.h" #include "DebugStatusLabel.h" #include "LogStatusLabel.h" #include "SourceViewerManager.h" #include "HandlesView.h" #include "MainWindowCloseThread.h" #include "TimeWastedCounter.h" #include "NotesManager.h" #include "SettingsDialog.h" #include "DisassemblerGraphView.h" #include "CPUMultiDump.h" #include "CPUStack.h" #include "GotoDialog.h" #include "SystemBreakpointScriptDialog.h" #include "CustomizeMenuDialog.h" #include "main.h" #include "SimpleTraceDialog.h" #include "CPUArgumentWidget.h" #include "MRUList.h" #include "AboutDialog.h" #include "UpdateChecker.h" #include "Gui/ReleaseNotesDialog.h" #include "Tracer/TraceManager.h" //#include "Tracer/TraceWidget.h" #include "Utils/MethodInvoker.h" MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); // Build information { const char* debugEngine = [] { switch(DbgGetDebugEngine()) { case DebugEngineTitanEngine: return "TitanEngine"; case DebugEngineGleeBug: return "GleeBug"; case DebugEngineStaticEngine: return "StaticEngine"; } return ""; }(); QAction* buildInfo = new QAction(tr("%1 (%2)").arg(ToDateString(GetCompileDate())).arg(debugEngine), this); buildInfo->setEnabled(false); ui->menuBar->addAction(buildInfo); } // Setup bridge signals connect(Bridge::getBridge(), SIGNAL(updateWindowTitle(QString)), this, SLOT(updateWindowTitleSlot(QString))); connect(Bridge::getBridge(), SIGNAL(addRecentFile(QString)), this, SLOT(addRecentFile(QString))); connect(Bridge::getBridge(), SIGNAL(setLastException(uint)), this, SLOT(setLastException(uint))); connect(Bridge::getBridge(), SIGNAL(getStrWindow(QString, QString*)), this, SLOT(getStrWindow(QString, QString*))); connect(Bridge::getBridge(), SIGNAL(showCpu()), this, SLOT(displayCpuWidget())); connect(Bridge::getBridge(), SIGNAL(showReferences()), this, SLOT(displayReferencesWidget())); connect(Bridge::getBridge(), SIGNAL(showThreads()), this, SLOT(displayThreadsWidget())); connect(Bridge::getBridge(), SIGNAL(addQWidgetTab(QWidget*)), this, SLOT(addQWidgetTab(QWidget*))); connect(Bridge::getBridge(), SIGNAL(showQWidgetTab(QWidget*)), this, SLOT(showQWidgetTab(QWidget*))); connect(Bridge::getBridge(), SIGNAL(closeQWidgetTab(QWidget*)), this, SLOT(closeQWidgetTab(QWidget*))); connect(Bridge::getBridge(), SIGNAL(executeOnGuiThread(void*, void*)), this, SLOT(executeOnGuiThread(void*, void*))); connect(Bridge::getBridge(), SIGNAL(dbgStateChanged(DBGSTATE)), this, SLOT(dbgStateChangedSlot(DBGSTATE))); connect(Bridge::getBridge(), SIGNAL(addFavouriteItem(int, QString, QString)), this, SLOT(addFavouriteItem(int, QString, QString))); connect(Bridge::getBridge(), SIGNAL(setFavouriteItemShortcut(int, QString, QString)), this, SLOT(setFavouriteItemShortcut(int, QString, QString))); connect(Bridge::getBridge(), SIGNAL(selectInMemoryMap(duint)), this, SLOT(displayMemMapWidget())); connect(Bridge::getBridge(), SIGNAL(symbolSelectModule(duint)), this, SLOT(displaySymbolWidget())); connect(Bridge::getBridge(), SIGNAL(closeApplication()), this, SLOT(close())); connect(Bridge::getBridge(), SIGNAL(showTraceBrowser()), this, SLOT(displayTraceWidget())); connect(Bridge::getBridge(), SIGNAL(focusMemmap()), this, SLOT(displayMemMapWidget())); connect(Bridge::getBridge(), SIGNAL(focusSymmod()), this, SLOT(displaySymbolWidget())); // Setup menu API // Because of race conditions with this API we create a direct connection. This means that the slot will directly execute on the thread that emits the signal. // Inside the slots we need to take special care to only do bookkeeping and not interact with the QWidgets without scheduling it on the main thread auto menuType = (Qt::ConnectionType)(Qt::UniqueConnection | Qt::DirectConnection); connect(Bridge::getBridge(), SIGNAL(menuAddMenuToList(QWidget*, QMenu*, GUIMENUTYPE, int)), this, SLOT(addMenuToList(QWidget*, QMenu*, GUIMENUTYPE, int)), menuType); connect(Bridge::getBridge(), SIGNAL(menuAddMenu(int, QString)), this, SLOT(addMenu(int, QString)), menuType); connect(Bridge::getBridge(), SIGNAL(menuAddMenuEntry(int, QString)), this, SLOT(addMenuEntry(int, QString)), menuType); connect(Bridge::getBridge(), SIGNAL(menuAddSeparator(int)), this, SLOT(addSeparator(int)), menuType); connect(Bridge::getBridge(), SIGNAL(menuClearMenu(int, bool)), this, SLOT(clearMenu(int, bool)), menuType); connect(Bridge::getBridge(), SIGNAL(menuRemoveMenuEntry(int)), this, SLOT(removeMenuEntry(int)), menuType); connect(Bridge::getBridge(), SIGNAL(setIconMenu(int, QIcon)), this, SLOT(setIconMenu(int, QIcon)), menuType); connect(Bridge::getBridge(), SIGNAL(setIconMenuEntry(int, QIcon)), this, SLOT(setIconMenuEntry(int, QIcon)), menuType); connect(Bridge::getBridge(), SIGNAL(setCheckedMenuEntry(int, bool)), this, SLOT(setCheckedMenuEntry(int, bool)), menuType); connect(Bridge::getBridge(), SIGNAL(setHotkeyMenuEntry(int, QString, QString)), this, SLOT(setHotkeyMenuEntry(int, QString, QString)), menuType); connect(Bridge::getBridge(), SIGNAL(setVisibleMenuEntry(int, bool)), this, SLOT(setVisibleMenuEntry(int, bool)), menuType); connect(Bridge::getBridge(), SIGNAL(setVisibleMenu(int, bool)), this, SLOT(setVisibleMenu(int, bool)), menuType); connect(Bridge::getBridge(), SIGNAL(setNameMenuEntry(int, QString)), this, SLOT(setNameMenuEntry(int, QString)), menuType); connect(Bridge::getBridge(), SIGNAL(setNameMenu(int, QString)), this, SLOT(setNameMenu(int, QString)), menuType); initMenuApi(); Bridge::getBridge()->emitMenuAddToList(this, ui->menuPlugins, GUI_PLUGIN_MENU); // Set window title to executable name if(BridgeIsProcessElevated()) { mWindowMainTitle = tr("%1 [Elevated]").arg(QCoreApplication::applicationName()); ui->actionRestartAdmin->setEnabled(false); } else mWindowMainTitle = QCoreApplication::applicationName(); setWindowTitle(QString(mWindowMainTitle)); // Load application icon SetApplicationIcon(MainWindow::winId()); // Load recent files mMRUList = new MRUList(this, "Recent Files"); connect(mMRUList, SIGNAL(openFile(QString)), this, SLOT(openRecentFileSlot(QString))); mMRUList->load(); updateMRUMenu(); // Log view mLogView = new LogView(); mLogView->setWindowTitle(tr("Log")); mLogView->setWindowIcon(DIcon("log")); mLogView->hide(); // Symbol view mSymbolView = new SymbolView(); Bridge::getBridge()->mSymbolView = mSymbolView; mSymbolView->setWindowTitle(tr("Symbols")); mSymbolView->setWindowIcon(DIcon("pdb")); mSymbolView->hide(); // Source view mSourceViewManager = new SourceViewerManager(); mSourceViewManager->setWindowTitle(tr("Source")); mSourceViewManager->setWindowIcon(DIcon("source")); mSourceViewManager->hide(); // Breakpoints mBreakpointsView = new BreakpointsView(); mBreakpointsView->setWindowTitle(tr("Breakpoints")); mBreakpointsView->setWindowIcon(DIcon("breakpoint")); mBreakpointsView->hide(); // Memory map view mMemMapView = new MemoryMapView(); connect(mMemMapView, SIGNAL(showReferences()), this, SLOT(displayReferencesWidget())); mMemMapView->setWindowTitle(tr("Memory Map")); mMemMapView->setWindowIcon(DIcon("memory-map")); mMemMapView->hide(); // Callstack view mCallStackView = new CallStackView(); mCallStackView->setWindowTitle(tr("Call Stack")); mCallStackView->setWindowIcon(DIcon("callstack")); // SEH Chain view mSEHChainView = new SEHChainView(); mSEHChainView->setWindowTitle(tr("SEH")); mSEHChainView->setWindowIcon(DIcon("seh-chain")); // Script view mScriptView = new ScriptView(); mScriptView->setWindowTitle(tr("Script")); mScriptView->setWindowIcon(DIcon("script-code")); mScriptView->hide(); // CPU view mCpuWidget = new CPUWidget(Bridge::getArchitecture()); mCpuWidget->setWindowTitle(tr("CPU")); #ifdef _WIN64 mCpuWidget->setWindowIcon(DIcon("processor64")); #else mCpuWidget->setWindowIcon(DIcon("processor32")); ui->actionCpu->setIcon(DIcon("processor32")); #endif //_WIN64 // Reference manager mReferenceManager = new ReferenceManager(this); Bridge::getBridge()->mReferenceManager = mReferenceManager; mReferenceManager->setWindowTitle(tr("References")); mReferenceManager->setWindowIcon(DIcon("search")); // Thread view mThreadView = new ThreadView(); mThreadView->setWindowTitle(tr("Threads")); mThreadView->setWindowIcon(DIcon("arrow-threads")); // Notes manager mNotesManager = new NotesManager(this); mNotesManager->setWindowTitle(tr("Notes")); mNotesManager->setWindowIcon(DIcon("notes")); // Handles view mHandlesView = new HandlesView(this); mHandlesView->setWindowTitle(tr("Handles")); mHandlesView->setWindowIcon(DIcon("handles")); // Trace view mTraceWidget = new TraceManager(this); mTraceWidget->setWindowTitle(tr("Trace")); mTraceWidget->setWindowIcon(DIcon("trace")); connect(mTraceWidget, SIGNAL(displayLogWidget()), this, SLOT(displayLogWidget())); mTabWidget = new MHTabWidget(this, true, true); // Add all widgets to the list mWidgetList.push_back(WidgetInfo(mCpuWidget, "CPUTab")); mWidgetList.push_back(WidgetInfo(mLogView, "LogTab")); mWidgetList.push_back(WidgetInfo(mNotesManager, "NotesTab")); mWidgetList.push_back(WidgetInfo(mBreakpointsView, "BreakpointsTab")); mWidgetList.push_back(WidgetInfo(mMemMapView, "MemoryMapTab")); mWidgetList.push_back(WidgetInfo(mCallStackView, "CallStackTab")); mWidgetList.push_back(WidgetInfo(mSEHChainView, "SEHTab")); mWidgetList.push_back(WidgetInfo(mScriptView, "ScriptTab")); mWidgetList.push_back(WidgetInfo(mSymbolView, "SymbolsTab")); mWidgetList.push_back(WidgetInfo(mSourceViewManager, "SourceTab")); mWidgetList.push_back(WidgetInfo(mReferenceManager, "ReferencesTab")); mWidgetList.push_back(WidgetInfo(mThreadView, "ThreadsTab")); mWidgetList.push_back(WidgetInfo(mHandlesView, "HandlesTab")); mWidgetList.push_back(WidgetInfo(mTraceWidget, "TraceTab")); // If LoadSaveTabOrder disabled, load tabs in default order if(!ConfigBool("Gui", "LoadSaveTabOrder")) loadTabDefaultOrder(); else loadTabSavedOrder(); setCentralWidget(mTabWidget); displayCpuWidget(); // Accept drops setAcceptDrops(true); // Setup the command and status bars setupCommandBar(); setupStatusBar(); // Patch dialog mPatchDialog = new PatchDialog(this); mCalculatorDialog = new CalculatorDialog(this); // Setup signals/slots connect(mCmdLineEdit, SIGNAL(returnPressed()), this, SLOT(executeCommand())); makeCommandAction(ui->actionRestartAdmin, "restartadmin"); makeCommandAction(ui->actionStepOver, "StepOver"); makeCommandAction(ui->actionStepInto, "StepInto"); connect(ui->actionCommand, SIGNAL(triggered()), this, SLOT(setFocusToCommandBar())); makeCommandAction(ui->actionClose, "stop"); connect(ui->actionMemoryMap, SIGNAL(triggered()), this, SLOT(displayMemMapWidget())); connect(ui->actionRun, SIGNAL(triggered()), this, SLOT(runSlot())); makeCommandAction(ui->actionRtr, "rtr"); connect(ui->actionLog, SIGNAL(triggered()), this, SLOT(displayLogWidget())); connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(displayAboutWidget())); connect(ui->actionOpen, SIGNAL(triggered()), this, SLOT(openFileSlot())); makeCommandAction(ui->actionPause, "pause"); makeCommandAction(ui->actionScylla, "StartScylla"); connect(ui->actionRestart, SIGNAL(triggered()), this, SLOT(restartDebugging())); connect(ui->actionBreakpoints, SIGNAL(triggered()), this, SLOT(displayBreakpointWidget())); makeCommandAction(ui->actioneStepOver, "eStepOver"); makeCommandAction(ui->actioneStepInto, "eStepInto"); makeCommandAction(ui->actioneRun, "eRun"); makeCommandAction(ui->actioneRtr, "eRtr"); makeCommandAction(ui->actionRtu, "rtu"); connect(ui->actionTicnd, SIGNAL(triggered()), this, SLOT(execTicnd())); connect(ui->actionTocnd, SIGNAL(triggered()), this, SLOT(execTocnd())); connect(ui->actionTRBit, SIGNAL(triggered()), mCpuWidget->getDisasmWidget(), SLOT(traceCoverageBitSlot())); connect(ui->actionTRByte, SIGNAL(triggered()), mCpuWidget->getDisasmWidget(), SLOT(traceCoverageByteSlot())); connect(ui->actionTRWord, SIGNAL(triggered()), mCpuWidget->getDisasmWidget(), SLOT(traceCoverageWordSlot())); connect(ui->actionTRReset, SIGNAL(triggered()), mCpuWidget->getDisasmWidget(), SLOT(traceCoverageResetSlot())); connect(ui->actionTRNone, SIGNAL(triggered()), mCpuWidget->getDisasmWidget(), SLOT(traceCoverageDisableSlot())); makeCommandAction(ui->actionTRTIBT, "tibt"); makeCommandAction(ui->actionTRTOBT, "tobt"); makeCommandAction(ui->actionTRTIIT, "tiit"); makeCommandAction(ui->actionTRTOIT, "toit"); makeCommandAction(ui->actionInstrUndo, "InstrUndo"); makeCommandAction(ui->actionSkipNextInstruction, "skip"); connect(ui->actionScript, SIGNAL(triggered()), this, SLOT(displayScriptWidget())); connect(ui->actionRunSelection, SIGNAL(triggered()), this, SLOT(runSelection())); connect(ui->actionRunExpression, SIGNAL(triggered(bool)), this, SLOT(runExpression())); makeCommandAction(ui->actionHideDebugger, "hide"); connect(ui->actionCpu, SIGNAL(triggered()), this, SLOT(displayCpuWidgetShowCpu())); connect(ui->actionSymbolInfo, SIGNAL(triggered()), this, SLOT(displaySymbolWidget())); connect(ui->actionModules, SIGNAL(triggered()), this, SLOT(displaySymbolWidget())); connect(ui->actionSource, SIGNAL(triggered()), this, SLOT(displaySourceViewWidget())); connect(mSymbolView, SIGNAL(showReferences()), this, SLOT(displayReferencesWidget())); connect(ui->actionReferences, SIGNAL(triggered()), this, SLOT(displayReferencesWidget())); connect(ui->actionThreads, SIGNAL(triggered()), this, SLOT(displayThreadsWidget())); connect(ui->actionSettings, SIGNAL(triggered()), this, SLOT(openSettings())); connect(ui->actionStrings, SIGNAL(triggered()), this, SLOT(findStrings())); connect(ui->actionCalls, SIGNAL(triggered()), this, SLOT(findModularCalls())); connect(ui->actionAppearance, SIGNAL(triggered()), this, SLOT(openAppearance())); connect(ui->actionShortcuts, SIGNAL(triggered()), this, SLOT(openShortcuts())); connect(ui->actionTopmost, SIGNAL(toggled(bool)), this, SLOT(changeTopmost(bool))); connect(ui->actionCalculator, SIGNAL(triggered()), this, SLOT(openCalculator())); connect(ui->actionPatches, SIGNAL(triggered()), this, SLOT(patchWindow())); connect(ui->actionComments, SIGNAL(triggered()), this, SLOT(displayComments())); connect(ui->actionLabels, SIGNAL(triggered()), this, SLOT(displayLabels())); connect(ui->actionBookmarks, SIGNAL(triggered()), this, SLOT(displayBookmarks())); connect(ui->actionFunctions, SIGNAL(triggered()), this, SLOT(displayFunctions())); connect(ui->actionCallStack, SIGNAL(triggered()), this, SLOT(displayCallstack())); connect(ui->actionSEHChain, SIGNAL(triggered()), this, SLOT(displaySEHChain())); connect(ui->actionTrace, SIGNAL(triggered()), this, SLOT(displayTraceWidget())); connect(ui->actionDonate, SIGNAL(triggered()), this, SLOT(donate())); connect(ui->actionReportBug, SIGNAL(triggered()), this, SLOT(reportBug())); connect(ui->actionBlog, SIGNAL(triggered()), this, SLOT(blog())); connect(ui->actionCrashDump, SIGNAL(triggered()), this, SLOT(crashDump())); connect(ui->actionMnemonic_Help, SIGNAL(triggered()), this, SLOT(mnemonicHelp())); connect(ui->actionAttach, SIGNAL(triggered()), this, SLOT(displayAttach())); makeCommandAction(ui->actionDetach, "detach"); connect(ui->actionChangeCommandLine, SIGNAL(triggered()), this, SLOT(changeCommandLine())); connect(ui->actionManual, SIGNAL(triggered()), this, SLOT(displayManual())); connect(ui->actionNotes, SIGNAL(triggered()), this, SLOT(displayNotesWidget())); connect(ui->actionHandles, SIGNAL(triggered()), this, SLOT(displayHandlesWidget())); connect(ui->actionGraph, SIGNAL(triggered()), this, SLOT(displayGraphWidget())); connect(ui->actionPreviousTab, SIGNAL(triggered()), mTabWidget, SLOT(showPreviousTab())); connect(ui->actionNextTab, SIGNAL(triggered()), mTabWidget, SLOT(showNextTab())); connect(ui->actionPreviousView, SIGNAL(triggered()), mTabWidget, SLOT(showPreviousView())); connect(ui->actionNextView, SIGNAL(triggered()), mTabWidget, SLOT(showNextView())); connect(ui->actionHideTab, SIGNAL(triggered()), mTabWidget, SLOT(deleteCurrentTab())); makeCommandAction(ui->actionStepIntoSource, "TraceIntoConditional src.line(cip) && !src.disp(cip)"); makeCommandAction(ui->actionStepOverSource, "TraceOverConditional src.line(cip) && !src.disp(cip)"); makeCommandAction(ui->actionseStepInto, "seStepInto"); makeCommandAction(ui->actionseStepOver, "seStepOver"); makeCommandAction(ui->actionseRun, "seRun"); connect(ui->actionAnimateInto, SIGNAL(triggered()), this, SLOT(animateIntoSlot())); connect(ui->actionAnimateOver, SIGNAL(triggered()), this, SLOT(animateOverSlot())); connect(ui->actionAnimateCommand, SIGNAL(triggered()), this, SLOT(animateCommandSlot())); connect(ui->actionSetInitializationScript, SIGNAL(triggered()), this, SLOT(setInitializationScript())); connect(ui->actionCustomizeMenus, SIGNAL(triggered()), this, SLOT(customizeMenu())); connect(ui->actionVariables, SIGNAL(triggered()), this, SLOT(displayVariables())); makeCommandAction(ui->actionDbsave, "dbsave"); makeCommandAction(ui->actionDbload, "dbload"); makeCommandAction(ui->actionDbrecovery, "dbload bak"); makeCommandAction(ui->actionDbclear, "dbclear"); connect(mCpuWidget->getDisasmWidget(), SIGNAL(updateWindowTitle(QString)), this, SLOT(updateWindowTitleSlot(QString))); connect(mCpuWidget->getDisasmWidget(), SIGNAL(displayReferencesWidget()), this, SLOT(displayReferencesWidget())); connect(mCpuWidget->getDisasmWidget(), SIGNAL(displaySourceManagerWidget()), this, SLOT(displaySourceViewWidget())); connect(mCpuWidget->getDisasmWidget(), SIGNAL(displayLogWidget()), this, SLOT(displayLogWidget())); connect(mCpuWidget->getDisasmWidget(), SIGNAL(displaySymbolsWidget()), this, SLOT(displaySymbolWidget())); connect(mThreadView, SIGNAL(displayThreadsView()), this, SLOT(displayThreadsWidget())); connect(mCpuWidget->getDisasmWidget(), SIGNAL(showPatches()), this, SLOT(patchWindow())); connect(mCpuWidget->getGraphWidget(), SIGNAL(displayLogWidget()), this, SLOT(displayLogWidget())); connect(mCpuWidget->getDumpWidget(), SIGNAL(displayReferencesWidget()), this, SLOT(displayReferencesWidget())); connect(mCpuWidget->getStackWidget(), SIGNAL(displayReferencesWidget()), this, SLOT(displayReferencesWidget())); connect(mTabWidget, SIGNAL(tabMovedTabWidget(int, int)), this, SLOT(tabMovedSlot(int, int))); connect(Config(), SIGNAL(shortcutsUpdated()), this, SLOT(refreshShortcuts())); connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(updateStyle())); connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(updateFont())); // Menu stuff actionManageFavourites = nullptr; mFavouriteToolbar = new QToolBar(tr("Favourite Toolbox"), this); updateFavouriteTools(); setupLanguagesMenu(); setupThemesMenu(); setupMenuCustomization(); ui->actionAbout_Qt->setIcon(QApplication::style()->standardIcon(QStyle::SP_TitleBarMenuButton)); auto toolButton = qobject_cast<QToolButton*>(ui->mainToolBar->widgetForAction(ui->actionCheckUpdates)); if(toolButton) { toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); } // Set default setttings (when not set) SettingsDialog defaultSettings; lastException = 0; defaultSettings.SaveSettings(); // Don't need to set shortcuts because the code above will signal refreshShortcuts() mSimpleTraceDialog = new SimpleTraceDialog(this); // Update checker mUpdateChecker = new UpdateChecker(this); // Setup close thread and dialog bCanClose = false; bExitWhenDetached = false; mCloseThread = new MainWindowCloseThread(this); connect(mCloseThread, SIGNAL(canClose()), this, SLOT(canClose())); mCloseDialog = new CloseDialog(this); mCpuWidget->setDisasmFocus(); char setting[MAX_SETTING_SIZE] = ""; // To avoid the window from flashing briefly at the initial position before being moved to saved position and size, initialize the main window position and size here // NOTE: apparently this is not correct (https://forum.qt.io/topic/76589/proper-way-to-restore-state-geometry-on-application-start), but it works on some machines. if(BridgeSettingGet("Main Window Settings", "Geometry", setting)) restoreGeometry(QByteArray::fromBase64(QByteArray(setting))); QTimer::singleShot(0, this, SLOT(loadWindowSettings())); updateDarkTitleBar(this); // Hide the menu icons if the setting is enabled duint noIcons = 0; BridgeSettingGetUint("Gui", "NoIcons", &noIcons); if(noIcons) { QList<QList<QAction*>> stack; stack.push_back(ui->menuBar->actions()); while(!stack.isEmpty()) { auto actions = stack.back(); stack.pop_back(); for(auto action : actions) { action->setIconVisibleInMenu(false); if(action->menu()) { stack.push_back(action->menu()->actions()); } } } } } MainWindow::~MainWindow() { delete ui; mMenuMutex->lock(); mMenuMutex->unlock(); delete mMenuMutex; } void MainWindow::setupCommandBar() { mCmdLineEdit = new CommandLineEdit(ui->cmdBar); ui->cmdBar->addWidget(new QLabel(tr("Command: "))); ui->cmdBar->addWidget(mCmdLineEdit); ui->cmdBar->addWidget(mCmdLineEdit->selectorWidget()); } void MainWindow::setupStatusBar() { // Status label (Ready, Paused, ...) mStatusLabel = new DebugStatusLabel(ui->statusBar); mStatusLabel->setText(tr("Ready")); ui->statusBar->addWidget(mStatusLabel); // Log line mLastLogLabel = new LogStatusLabel(ui->statusBar); ui->statusBar->addPermanentWidget(mLastLogLabel, 1); // Time wasted counter QLabel* timeWastedLabel = new QLabel(this); ui->statusBar->addPermanentWidget(timeWastedLabel); mTimeWastedCounter = new TimeWastedCounter(this, timeWastedLabel); } void MainWindow::setupLanguagesMenu() { QMenu* languageMenu; if(tr("Languages") == QString("Languages")) languageMenu = new QMenu(QString("Languages")); else languageMenu = new QMenu(tr("Languages") + QString(" Languages"), this); languageMenu->setIcon(DIcon("codepage")); QLocale enUS(QLocale::English, QLocale::UnitedStates); QAction* action_enUS = new QAction(QString("[%1] %2 - %3").arg(enUS.name()).arg(enUS.nativeLanguageName()).arg(enUS.nativeCountryName()), languageMenu); connect(action_enUS, SIGNAL(triggered()), this, SLOT(chooseLanguage())); action_enUS->setCheckable(true); action_enUS->setChecked(false); languageMenu->addAction(action_enUS); connect(languageMenu, SIGNAL(aboutToShow()), this, SLOT(setupLanguagesMenu2())); //Load this menu later, since it requires directory scanning. ui->menuOptions->addMenu(languageMenu); } #include "../src/bridge/Utf8Ini.h" static void importSettings(const QString & filename, const QSet<QString> & sectionWhitelist = {}) { QFile f(QDir::toNativeSeparators(filename)); if(f.open(QFile::ReadOnly | QFile::Text)) { QTextStream in(&f); auto style = in.readAll(); f.close(); Utf8Ini ini; int errorLine; if(ini.Deserialize(style.toStdString(), errorLine)) { auto sections = ini.Sections(); for(const auto & section : sections) { if(!sectionWhitelist.isEmpty() && !sectionWhitelist.contains(QString::fromStdString(section))) continue; auto keys = ini.Keys(section); for(const auto & key : keys) BridgeSettingSet(section.c_str(), key.c_str(), ini.GetValue(section, key).c_str()); } Config()->load(); DbgSettingsUpdated(); emit Config()->colorsUpdated(); emit Config()->fontsUpdated(); emit Config()->guiOptionsUpdated(); emit Config()->shortcutsUpdated(); emit Config()->tokenizerConfigUpdated(); // Before startup we don't need to update all views if(Bridge::getBridge()) GuiUpdateAllViews(); } } } void MainWindow::loadSelectedTheme(bool reloadOnlyStyleCss) { if(BridgeGetNtBuildNumber() >= 14393 /* darkmode registry release */) { duint lastAppsUseLightTheme = -1; BridgeSettingGetUint("Theme", "AppsUseLightTheme", &lastAppsUseLightTheme); auto readRegistryDword = [](HKEY hRootKey, const wchar_t* lpSubKey, const wchar_t* lpValueName, DWORD & result) { auto success = false; HKEY hKey = 0; if(RegOpenKeyExW(hRootKey, lpSubKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { DWORD dwBufferSize = sizeof(DWORD); DWORD dwData = 0; if(RegQueryValueExW(hKey, lpValueName, 0, NULL, reinterpret_cast<LPBYTE>(&dwData), &dwBufferSize) == ERROR_SUCCESS) { result = dwData; success = true; } RegCloseKey(hKey); } return success; }; DWORD appsUseLightTheme = 1; auto subKey = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; auto valueName = L"AppsUseLightTheme"; if(!readRegistryDword(HKEY_CURRENT_USER, subKey, valueName, appsUseLightTheme)) readRegistryDword(HKEY_LOCAL_MACHINE, subKey, valueName, appsUseLightTheme); // If the user changed the setting since last startup, adjust the default theme if(appsUseLightTheme != lastAppsUseLightTheme) { BridgeSettingSet("Theme", "Selected", appsUseLightTheme ? "Default" : "Dark"); BridgeSettingSetUint("Theme", "AppsUseLightTheme", appsUseLightTheme); reloadOnlyStyleCss = false; } } char selectedTheme[MAX_SETTING_SIZE] = "Default"; if(!BridgeSettingGet("Theme", "Selected", selectedTheme)) BridgeSettingSet("Theme", "Selected", selectedTheme); QString stylePath(":/css/default.css"); QString settingsPath; QString applicationDirPath = QCoreApplication::applicationDirPath(); if(*selectedTheme) { // Handle the icon theme QStringList searchPaths = { ":/" }; if(strcmp(selectedTheme, "Default") == 0) { // The Default theme needs some special handling to allow overriding auto overrideDir = applicationDirPath + "/../themes/Default"; if(QDir(overrideDir).exists("index.theme")) { /* HACK: for some reason this allows you to override icons from the themes/Default folder. You need themes/Default/index.theme and then you can put images in themes/Default/icons: [Icon Theme] Name=DefaultOverride Comment=Default icon theme override Directories=icons Inherits=Default [icons] Size=16 Type=Scalable */ searchPaths << overrideDir; QIcon::setThemeName("DefaultOverride"); } else { QIcon::setThemeName("Default"); } QIcon::setThemeSearchPaths(searchPaths); } else { auto themesDir = applicationDirPath + "/../themes"; if(QDir(themesDir).exists(QString("%1/index.theme").arg(selectedTheme))) { searchPaths << themesDir; QIcon::setThemeName(selectedTheme); } else { // If there is no icon theme, use the default icons QIcon::setThemeName("Default"); } QIcon::setThemeSearchPaths(searchPaths); } QString themePath = QString("%1/../themes/%2/style.css").arg(applicationDirPath).arg(selectedTheme); if(!QFile(themePath).exists()) themePath = QString("%1/../themes/%2/theme.css").arg(applicationDirPath).arg(selectedTheme); if(QFile(themePath).exists()) stylePath = themePath; auto tryIni = [&applicationDirPath, &settingsPath, &selectedTheme](const char* name) { if(!settingsPath.isEmpty()) return; QString iniPath = QString("%1/../themes/%2/%3").arg(applicationDirPath, selectedTheme, name); if(QFile(iniPath).exists()) settingsPath = iniPath; }; tryIni("style.ini"); tryIni("colors.ini"); tryIni("theme.ini"); } else { // This code path should never be executed QIcon::setThemeName("Default"); } QFile cssFile(stylePath); if(cssFile.open(QFile::ReadOnly | QFile::Text)) { auto style = QTextStream(&cssFile).readAll(); cssFile.close(); style = style.replace("url(./", QString("url(approot:/themes/%1/").arg(selectedTheme)); style = style.replace("url(\"./", QString("url(\"approot:/themes/%1/").arg(selectedTheme)); style = style.replace("url('./", QString("url('approot:/themes/%1/").arg(selectedTheme)); style = style.replace("$RELPATH", QString("approot:/themes/%1").arg(selectedTheme)); qApp->setStyleSheet(style); } // Skip changing the settings when only reloading the CSS // On startup we want to preserve the user's appearance if(reloadOnlyStyleCss) return; if(!settingsPath.isEmpty()) { // TODO: add an 'inherit' option to style.ini to inherit from another theme importSettings(settingsPath, { "Colors", "Fonts" }); } else { // Reset [Colors] to default Config()->Colors = Config()->defaultColors; Config()->writeColors(); BridgeSettingSetUint("Colors", "DarkTitleBar", 0); // Reset [Fonts] to default (TODO: https://github.com/x64dbg/x64dbg/issues/2422) //Config()->Fonts = Config()->defaultFonts; //Config()->writeFonts(); // Remove custom colors BridgeSettingSet("Colors", "CustomColorCount", nullptr); } } void MainWindow::themeTriggeredSlot() { QAction* action = qobject_cast<QAction*>(sender()); if(action == nullptr) return; QString dir = action->data().toString(); int nameIdx = dir.lastIndexOf('/'); QString name = dir.mid(nameIdx + 1); BridgeSettingSet("Theme", "Selected", name.toUtf8().constData()); loadSelectedTheme(); updateDarkTitleBar(this); } void MainWindow::setupThemesMenu() { char selectedTheme[MAX_SETTING_SIZE]; BridgeSettingGet("Theme", "Selected", selectedTheme); QDirIterator it(QString("%1/../themes").arg(QCoreApplication::applicationDirPath()), QDir::NoDotAndDotDot | QDir::Dirs); auto actionGroup = new QActionGroup(ui->menuTheme); actionGroup->addAction(ui->actionDefaultTheme); while(it.hasNext()) { auto dir = it.next(); auto nameIdx = dir.lastIndexOf('/'); auto name = dir.mid(nameIdx + 1); // The Default theme folder is a hidden theme to override the default theme if(name == "Default") continue; // Translation support for the built-in 'Dark' theme if(name == "Dark") name = tr("Dark"); auto action = ui->menuTheme->addAction(name); connect(action, SIGNAL(triggered()), this, SLOT(themeTriggeredSlot())); action->setText(name); action->setData(dir); action->setCheckable(true); actionGroup->addAction(action); if(name == selectedTheme) action->setChecked(true); } } void MainWindow::setupLanguagesMenu2() { QMenu* languageMenu = dynamic_cast<QMenu*>(sender()); //The only sender is languageMenu QAction* action_enUS = languageMenu->actions()[0]; //There is only one action "action_enUS" created by setupLanguagesMenu() QDir translationsDir(QString("%1/../translations/").arg(QCoreApplication::applicationDirPath())); QString currentLocale(gCurrentLocale); if(!translationsDir.exists()) { // translations dir do not exist action_enUS->setChecked(true); disconnect(languageMenu, SIGNAL(aboutToShow()), this, 0); return; } if(currentLocale == QString("en_US")) action_enUS->setChecked(true); QStringList filter; filter << "x64dbg_*.qm"; QFileInfoList fileList = translationsDir.entryInfoList(filter, QDir::Readable | QDir::Files, QDir::Size); //Search for all translations auto allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry); for(auto i : fileList) { QString localeName = i.baseName().mid(7); for(auto j : allLocales) { if(j.name().startsWith(localeName)) { QAction* actionLanguage = new QAction(QString("[%1] %2 - %3").arg(localeName).arg(j.nativeLanguageName()).arg(j.nativeCountryName()), languageMenu); connect(actionLanguage, SIGNAL(triggered()), this, SLOT(chooseLanguage())); actionLanguage->setCheckable(true); actionLanguage->setChecked(localeName == currentLocale); languageMenu->addAction(actionLanguage); break; } } } disconnect(languageMenu, SIGNAL(aboutToShow()), this, 0); //Done. Let's disconnect it to prevent it from initializing twice. } void MainWindow::closeEvent(QCloseEvent* event) { if(DbgIsDebugging()) { duint detachOnExit = 0; if(BridgeSettingGetUint("Engine", "DetachOnExit", &detachOnExit) && detachOnExit) { bExitWhenDetached = true; DbgCmdExec("detach"); event->ignore(); return; } } if(DbgIsDebugging() && ConfigBool("Gui", "ShowExitConfirmation")) { auto cb = new QCheckBox(tr("Remember my choice")); QMessageBox msgbox(this); msgbox.setText(tr("The debuggee is still running and will be terminated if you exit. What do you want to do?")); msgbox.setWindowTitle(tr("Debuggee is still running")); msgbox.setWindowIcon(DIcon("bug")); auto exitButton = msgbox.addButton(QMessageBox::Yes); exitButton->setText(tr("&Exit")); exitButton->setToolTip(tr("Stop the debuggee and exit x64dbg.")); auto detachButton = msgbox.addButton(QMessageBox::Abort); detachButton->setText(tr("&Detach and exit")); detachButton->setToolTip(tr("Detach from the debuggee (leaving it running) and exit x64dbg.")); auto restartButton = msgbox.addButton(QMessageBox::Retry); restartButton->setText(tr("&Restart debugging")); restartButton->setToolTip(tr("Restart the debuggee and keep x64dbg open.")); auto continueButton = msgbox.addButton(QMessageBox::Cancel); continueButton->setText(tr("&Continue debugging")); continueButton->setToolTip(tr("Close this dialog and continue where you left off.")); msgbox.setDefaultButton(QMessageBox::Cancel); msgbox.setEscapeButton(QMessageBox::Cancel); msgbox.setCheckBox(cb); auto code = msgbox.exec(); if(code == QMessageBox::Retry) restartDebugging(); else if(code == QMessageBox::Abort) { if(cb->isChecked()) BridgeSettingSetUint("Engine", "DetachOnExit", 1); bExitWhenDetached = true; DbgCmdExec("detach"); } else if(code == QMessageBox::Yes && cb->isChecked()) { Config()->setBool("Gui", "ShowExitConfirmation", false); } if(code != QMessageBox::Yes) { event->ignore(); return; } } duint noClose = 0; if(bCanClose) { saveWindowSettings(); } if(BridgeSettingGetUint("Gui", "NoCloseDialog", &noClose) && noClose) mCloseDialog->hide(); else { mCloseDialog->show(); mCloseDialog->setFocus(); } static volatile bool bExecuteThread = true; if(bExecuteThread) { bExecuteThread = false; Sleep(100); mCloseThread->start(); emit Bridge::getBridge()->close(); } if(bCanClose) { mCloseDialog->allowClose(); mCloseDialog->close(); event->accept(); } else event->ignore(); } void MainWindow::setTab(QWidget* widget) { // shown tabs for(int i = 0; i < mTabWidget->count(); i++) { if(mTabWidget->widget(i) == widget) { mTabWidget->setCurrentIndex(i); return; } } // hidden tabs for(int i = 0; i < mWidgetList.count(); i++) { if(mWidgetList[i].widget == widget) { addQWidgetTab(mWidgetList[i].widget, mWidgetList[i].nativeName); mTabWidget->setCurrentIndex(mTabWidget->count() - 1); return; } } //TODO: restore configuration index } void MainWindow::loadTabDefaultOrder() { clearTabWidget(); // Setup tabs for(int i = 0; i < mWidgetList.size(); i++) addQWidgetTab(mWidgetList[i].widget, mWidgetList[i].nativeName); // Add plugin tabs to the end // TODO: this collection is always empty for(const auto & widget : mPluginWidgetList) addQWidgetTab(widget.widget, widget.nativeName); } void MainWindow::loadTabSavedOrder() { clearTabWidget(); QMap<duint, std::pair<QWidget*, QString>> tabIndexToWidget; // Get tabIndex for each widget and add them to tabIndexToWidget duint lastValidTabIndex = 0; for(int i = 0; i < mWidgetList.size(); i++) { auto tabName = mWidgetList[i].nativeName; duint tabIndex = 0; if(BridgeSettingGetUint("TabOrder", tabName.toUtf8().constData(), &tabIndex)) { lastValidTabIndex = tabIndex; } else { tabIndex = lastValidTabIndex; } if(!tabIndexToWidget.contains(tabIndex)) tabIndexToWidget.insert(tabIndex, std::make_pair(mWidgetList[i].widget, tabName)); else { // Conflicts. Try to find an unused tab index. for(int j = 0; j < mWidgetList.size(); j++) { auto item = tabIndexToWidget.find(j); if(item == tabIndexToWidget.end()) { tabIndexToWidget.insert(j, std::make_pair(mWidgetList[i].widget, tabName)); break; } } } } // Setup tabs for(auto & widget : tabIndexToWidget) addQWidgetTab(widget.first, widget.second); // 'Restore' deleted tabs for(int i = 0; i < mWidgetList.size(); i++) { duint isDeleted = 0; BridgeSettingGetUint("Deleted Tabs", mWidgetList[i].nativeName.toUtf8().constData(), &isDeleted); if(isDeleted) mTabWidget->DeleteTab(mTabWidget->indexOf(mWidgetList[i].widget)); } // Add plugin tabs to the end // TODO: this collection is always empty for(const auto & widget : mPluginWidgetList) addQWidgetTab(widget.widget, widget.nativeName); } void MainWindow::clearTabWidget() { if(mTabWidget->count() <= 0) return; // Remove all tabs starting from the end for(int i = mTabWidget->count() - 1; i >= 0; i--) mTabWidget->removeTab(i); } void MainWindow::saveWindowSettings() { // Save favourite toolbar BridgeSettingSetUint("Main Window Settings", "FavToolbarVisible", mFavouriteToolbar->isVisible() ? 1 : 0); removeToolBar(mFavouriteToolbar); //Remove it before saving main window settings, otherwise it crashes // Main Window settings BridgeSettingSet("Main Window Settings", "Geometry", saveGeometry().toBase64().data()); BridgeSettingSet("Main Window Settings", "State", saveState().toBase64().data()); // Set of currently detached tabs QSet<QWidget*> detachedTabWindows = mTabWidget->windows().toSet(); // For all tabs, save detached status. If detached, save geometry. for(int i = 0; i < mWidgetList.size(); i++) { bool isDetached = detachedTabWindows.contains(mWidgetList[i].widget); bool isDeleted = !isDetached && mTabWidget->indexOf(mWidgetList[i].widget) == -1; BridgeSettingSetUint("Detached Windows", mWidgetList[i].nativeName.toUtf8().constData(), isDetached); BridgeSettingSetUint("Deleted Tabs", mWidgetList[i].nativeName.toUtf8().constData(), isDeleted); if(isDetached) BridgeSettingSet("Tab Window Settings", mWidgetList[i].nativeName.toUtf8().constData(), mWidgetList[i].widget->parentWidget()->saveGeometry().toBase64().data()); } mCpuWidget->saveWindowSettings(); mSymbolView->saveWindowSettings(); } void MainWindow::loadWindowSettings() { // Main Window settings char setting[MAX_SETTING_SIZE] = ""; if(BridgeSettingGet("Main Window Settings", "Geometry", setting)) restoreGeometry(QByteArray::fromBase64(QByteArray(setting))); if(BridgeSettingGet("Main Window Settings", "State", setting)) restoreState(QByteArray::fromBase64(QByteArray(setting))); // Restore detached windows size and position // If a tab was detached last session, manually detach it now to populate MHTabWidget::windows for(int i = 0; i < mWidgetList.size(); i++) { duint isDetached = 0; BridgeSettingGetUint("Detached Windows", mWidgetList[i].nativeName.toUtf8().constData(), &isDetached); if(isDetached) mTabWidget->DetachTab(mTabWidget->indexOf(mWidgetList[i].widget), QPoint()); } // Restore geometry for every tab we just detached QSet<QWidget*> detachedTabWindows = mTabWidget->windows().toSet(); for(int i = 0; i < mWidgetList.size(); i++) { if(detachedTabWindows.contains(mWidgetList[i].widget)) { if(BridgeSettingGet("Tab Window Settings", mWidgetList[i].nativeName.toUtf8().constData(), setting)) mWidgetList[i].widget->parentWidget()->restoreGeometry(QByteArray::fromBase64(QByteArray(setting))); } } // 'Restore' deleted tabs for(int i = 0; i < mWidgetList.size(); i++) { duint isDeleted = 0; BridgeSettingGetUint("Deleted Tabs", mWidgetList[i].nativeName.toUtf8().constData(), &isDeleted); if(isDeleted) mTabWidget->DeleteTab(mTabWidget->indexOf(mWidgetList[i].widget)); } // Load favourite toolbar duint isVisible = 0; BridgeSettingGetUint("Main Window Settings", "FavToolbarVisible", &isVisible); addToolBar(mFavouriteToolbar); mFavouriteToolbar->setVisible(isVisible == 1); mCpuWidget->loadWindowSettings(); mSymbolView->loadWindowSettings(); // Make x64dbg topmost if(ConfigBool("Gui", "Topmost")) ui->actionTopmost->setChecked(true); // Display support notifications if(BridgeIsARM64Emulated()) { SimpleErrorBox( this, tr("Unsupported system"), tr("You are running x64dbg in ARM64 emulation mode. <b>This system is not supported by x64dbg and will cause unexpected behavior.</b> Analyzing malware in this environment is dangerous and you should switch to an actual Intel/AMD CPU.<br><br>For more information, see the <a href=\"%1\">FAQ</a>.").arg("https://faq.x64dbg.com") ); } if(BridgeGetNtBuildNumber() < 10000) { SimpleErrorBox( this, tr("Unsupported system"), tr("You are running x64dbg on an unsupported operating system version. <b>Future updates will completely stop running on this system.</b><br><br>For more information, see the official <a href=\"%1\">announcement</a>.").arg("https://transition.x64dbg.com"), "HideErrorUnsupportedSystem" ); } #ifdef X64DBG_RELEASE auto compileDate = QDateTime(GetCompileDate()); compileDate.setTimeSpec(Qt::UTC); auto compileEpoch = compileDate.toSecsSinceEpoch(); duint releaseNotesEpoch = 0; BridgeSettingGetUint("Gui", "ReleaseNotesEpoch", &releaseNotesEpoch); if(releaseNotesEpoch < compileEpoch) { showReleaseNotes(releaseNotesEpoch); BridgeSettingSetUint("Gui", "ReleaseNotesPrevEpoch", releaseNotesEpoch); BridgeSettingSetUint("Gui", "ReleaseNotesEpoch", compileEpoch); BridgeSettingFlush(); } #endif // X64DBG_RELEASE } void MainWindow::setGlobalShortcut(QAction* action, const QKeySequence & key) { action->setShortcut(key); action->setShortcutContext(Qt::ApplicationShortcut); QString tooltip = action->text().remove('&'); if(!key.isEmpty()) { tooltip = QStringLiteral("%1 (%2)").arg(tooltip, key.toString(QKeySequence::NativeText)); } action->setToolTip(tooltip); } void MainWindow::refreshShortcuts() { setGlobalShortcut(ui->actionOpen, ConfigShortcut("FileOpen")); setGlobalShortcut(ui->actionAttach, ConfigShortcut("FileAttach")); setGlobalShortcut(ui->actionDetach, ConfigShortcut("FileDetach")); setGlobalShortcut(ui->actionDbload, ConfigShortcut("FileDbload")); setGlobalShortcut(ui->actionDbsave, ConfigShortcut("FileDbsave")); setGlobalShortcut(ui->actionDbclear, ConfigShortcut("FileDbclear")); setGlobalShortcut(ui->actionDbrecovery, ConfigShortcut("FileDbrecovery")); setGlobalShortcut(ui->actionImportdatabase, ConfigShortcut("FileImportDatabase")); setGlobalShortcut(ui->actionExportdatabase, ConfigShortcut("FileExportDatabase")); setGlobalShortcut(ui->actionRestartAdmin, ConfigShortcut("FileRestartAdmin")); setGlobalShortcut(ui->actionExit, ConfigShortcut("FileExit")); setGlobalShortcut(ui->actionCpu, ConfigShortcut("ViewCpu")); setGlobalShortcut(ui->actionLog, ConfigShortcut("ViewLog")); setGlobalShortcut(ui->actionBreakpoints, ConfigShortcut("ViewBreakpoints")); setGlobalShortcut(ui->actionMemoryMap, ConfigShortcut("ViewMemoryMap")); setGlobalShortcut(ui->actionCallStack, ConfigShortcut("ViewCallStack")); setGlobalShortcut(ui->actionSEHChain, ConfigShortcut("ViewSEHChain")); setGlobalShortcut(ui->actionScript, ConfigShortcut("ViewScript")); setGlobalShortcut(ui->actionSymbolInfo, ConfigShortcut("ViewSymbolInfo")); setGlobalShortcut(ui->actionModules, ConfigShortcut("ViewModules")); setGlobalShortcut(ui->actionSource, ConfigShortcut("ViewSource")); setGlobalShortcut(ui->actionReferences, ConfigShortcut("ViewReferences")); setGlobalShortcut(ui->actionThreads, ConfigShortcut("ViewThreads")); setGlobalShortcut(ui->actionPatches, ConfigShortcut("ViewPatches")); setGlobalShortcut(ui->actionComments, ConfigShortcut("ViewComments")); setGlobalShortcut(ui->actionLabels, ConfigShortcut("ViewLabels")); setGlobalShortcut(ui->actionBookmarks, ConfigShortcut("ViewBookmarks")); setGlobalShortcut(ui->actionFunctions, ConfigShortcut("ViewFunctions")); setGlobalShortcut(ui->actionVariables, ConfigShortcut("ViewVariables")); setGlobalShortcut(ui->actionHandles, ConfigShortcut("ViewHandles")); setGlobalShortcut(ui->actionGraph, ConfigShortcut("ViewGraph")); setGlobalShortcut(ui->actionPreviousTab, ConfigShortcut("ViewPreviousTab")); setGlobalShortcut(ui->actionNextTab, ConfigShortcut("ViewNextTab")); setGlobalShortcut(ui->actionPreviousView, ConfigShortcut("ViewPreviousHistory")); setGlobalShortcut(ui->actionNextView, ConfigShortcut("ViewNextHistory")); setGlobalShortcut(ui->actionHideTab, ConfigShortcut("ViewHideTab")); setGlobalShortcut(ui->actionRun, ConfigShortcut("DebugRun")); setGlobalShortcut(ui->actioneRun, ConfigShortcut("DebugeRun")); setGlobalShortcut(ui->actionseRun, ConfigShortcut("DebugseRun")); setGlobalShortcut(ui->actionRunSelection, ConfigShortcut("DebugRunSelection")); setGlobalShortcut(ui->actionRunExpression, ConfigShortcut("DebugRunExpression")); setGlobalShortcut(ui->actionPause, ConfigShortcut("DebugPause")); setGlobalShortcut(ui->actionRestart, ConfigShortcut("DebugRestart")); setGlobalShortcut(ui->actionClose, ConfigShortcut("DebugClose")); setGlobalShortcut(ui->actionStepInto, ConfigShortcut("DebugStepInto")); setGlobalShortcut(ui->actioneStepInto, ConfigShortcut("DebugeStepInto")); setGlobalShortcut(ui->actionseStepInto, ConfigShortcut("DebugseStepInto")); setGlobalShortcut(ui->actionStepIntoSource, ConfigShortcut("DebugStepIntoSource")); setGlobalShortcut(ui->actionStepOver, ConfigShortcut("DebugStepOver")); setGlobalShortcut(ui->actioneStepOver, ConfigShortcut("DebugeStepOver")); setGlobalShortcut(ui->actionseStepOver, ConfigShortcut("DebugseStepOver")); setGlobalShortcut(ui->actionStepOverSource, ConfigShortcut("DebugStepOverSource")); setGlobalShortcut(ui->actionRtr, ConfigShortcut("DebugRtr")); setGlobalShortcut(ui->actioneRtr, ConfigShortcut("DebugeRtr")); setGlobalShortcut(ui->actionRtu, ConfigShortcut("DebugRtu")); setGlobalShortcut(ui->actionCommand, ConfigShortcut("DebugCommand")); setGlobalShortcut(ui->actionSkipNextInstruction, ConfigShortcut("DebugSkipNextInstruction")); setGlobalShortcut(ui->actionTicnd, ConfigShortcut("DebugTraceIntoConditional")); setGlobalShortcut(ui->actionTocnd, ConfigShortcut("DebugTraceOverConditional")); setGlobalShortcut(ui->actionTRBit, ConfigShortcut("DebugEnableTraceRecordBit")); setGlobalShortcut(ui->actionTRNone, ConfigShortcut("DebugTraceRecordNone")); setGlobalShortcut(ui->actionInstrUndo, ConfigShortcut("DebugInstrUndo")); setGlobalShortcut(ui->actionAnimateInto, ConfigShortcut("DebugAnimateInto")); setGlobalShortcut(ui->actionAnimateOver, ConfigShortcut("DebugAnimateOver")); setGlobalShortcut(ui->actionAnimateCommand, ConfigShortcut("DebugAnimateCommand")); setGlobalShortcut(ui->actionTRTIIT, ConfigShortcut("DebugTraceIntoIntoTracerecord")); setGlobalShortcut(ui->actionTRTOIT, ConfigShortcut("DebugTraceOverIntoTracerecord")); setGlobalShortcut(ui->actionTRTIBT, ConfigShortcut("DebugTraceIntoBeyondTracerecord")); setGlobalShortcut(ui->actionTRTOBT, ConfigShortcut("DebugTraceOverBeyondTracerecord")); setGlobalShortcut(ui->actionScylla, ConfigShortcut("PluginsScylla")); setGlobalShortcut(actionManageFavourites, ConfigShortcut("FavouritesManage")); setGlobalShortcut(ui->actionSettings, ConfigShortcut("OptionsPreferences")); setGlobalShortcut(ui->actionAppearance, ConfigShortcut("OptionsAppearance")); setGlobalShortcut(ui->actionShortcuts, ConfigShortcut("OptionsShortcuts")); setGlobalShortcut(ui->actionTopmost, ConfigShortcut("OptionsTopmost")); setGlobalShortcut(ui->actionReloadStylesheet, ConfigShortcut("OptionsReloadStylesheet")); setGlobalShortcut(ui->actionAbout, ConfigShortcut("HelpAbout")); setGlobalShortcut(ui->actionBlog, ConfigShortcut("HelpBlog")); setGlobalShortcut(ui->actionDonate, ConfigShortcut("HelpDonate")); setGlobalShortcut(ui->actionCalculator, ConfigShortcut("HelpCalculator")); setGlobalShortcut(ui->actionReportBug, ConfigShortcut("HelpReportBug")); setGlobalShortcut(ui->actionManual, ConfigShortcut("HelpManual")); setGlobalShortcut(ui->actionCrashDump, ConfigShortcut("HelpCrashDump")); setGlobalShortcut(ui->actionStrings, ConfigShortcut("ActionFindStrings")); setGlobalShortcut(ui->actionCalls, ConfigShortcut("ActionFindIntermodularCalls")); for(const MenuEntryInfo & entry : mEntryList) if(!entry.hotkeyId.isEmpty()) entry.mAction->setShortcut(ConfigShortcut(entry.hotkeyId)); } void MainWindow::updateMRUMenu() { QMenu* fileMenu = ui->menuRecentFiles; QList<QAction*> list = fileMenu->actions(); for(int i = 1; i < list.length(); ++i) fileMenu->removeAction(list.at(i)); mMRUList->appendMenu(fileMenu); } void MainWindow::executeCommand() { mCmdLineEdit->execute(); } QAction* MainWindow::makeCommandAction(QAction* action, const QString & command) { action->setData(QVariant(command)); connect(action, SIGNAL(triggered()), this, SLOT(execCommandSlot())); return action; } void MainWindow::showReleaseNotes(duint cutoffEpoch) { QFile file(QString("%1/../release-notes.md").arg(QCoreApplication::applicationDirPath())); if(!file.open(QFile::ReadOnly)) { SimpleErrorBox( this, tr("Error"), tr("Release notes are not available, see <a href=\"%1\">%2</a> for the latest updates.") .arg("https://update.x64dbg.com") .arg("update.x64dbg.com") ); return; } auto markdown = QString::fromUtf8(file.readAll()); file.close(); if(cutoffEpoch) { static QRegularExpression re(R"(<!-- *(\d\d\d\d.\d\d.\d\d) *-->)"); auto i = re.globalMatch(markdown); bool seenCutoff = false; QStringList words; while(i.hasNext()) { QRegularExpressionMatch match = i.next(); auto matchText = match.captured(1); auto matchDate = QDateTime::fromString(matchText, "yyyy.MM.dd"); matchDate.setTimeSpec(Qt::UTC); auto matchEpoch = matchDate.toSecsSinceEpoch(); if(matchEpoch <= cutoffEpoch && seenCutoff) { markdown = markdown.left(match.capturedStart(0)); break; } seenCutoff = true; } } ReleaseNotesDialog dialog({}, this); auto titleBarHeight = frameGeometry().height() - geometry().height(); auto position = frameGeometry().center() - dialog.frameGeometry().center(); position.setY(position.y() - titleBarHeight / 2); dialog.move(position); dialog.setMarkdown(markdown, "https://github.com/x64dbg/x64dbg/issues/"); dialog.setWindowIcon(DIcon("bug")); dialog.exec(); } void MainWindow::execCommandSlot() { QAction* action = qobject_cast<QAction*>(sender()); if(action) DbgCmdExec(action->data().toString()); } void MainWindow::setFocusToCommandBar() { mCmdLineEdit->setFocus(); } void MainWindow::execTicnd() { if(!DbgIsDebugging()) return; if(DbgIsRunning()) { SimpleErrorBox(this, tr("Error"), tr("Cannot start a trace when running, pause execution first.")); return; } mSimpleTraceDialog->setTraceCommand("TraceIntoConditional"); mSimpleTraceDialog->setWindowTitle(tr("Trace into...")); mSimpleTraceDialog->setWindowIcon(DIcon("traceinto")); mSimpleTraceDialog->exec(); } void MainWindow::execTocnd() { if(!DbgIsDebugging()) return; if(DbgIsRunning()) { SimpleErrorBox(this, tr("Error"), tr("Cannot start a trace when running, pause execution first.")); return; } mSimpleTraceDialog->setTraceCommand("TraceOverConditional"); mSimpleTraceDialog->setWindowTitle(tr("Trace over...")); mSimpleTraceDialog->setWindowIcon(DIcon("traceover")); mSimpleTraceDialog->exec(); } void MainWindow::displayMemMapWidget() { showQWidgetTab(mMemMapView); } void MainWindow::displayVariables() { DbgCmdExec("varlist"); showQWidgetTab(mReferenceManager); } void MainWindow::displayLogWidget() { showQWidgetTab(mLogView); } void MainWindow::displayScriptWidget() { showQWidgetTab(mScriptView); } void MainWindow::displayAboutWidget() { AboutDialog dialog(mUpdateChecker, this); dialog.exec(); } void MainWindow::openFileSlot() { auto filename = QFileDialog::getOpenFileName(this, tr("Open file"), mMRUList->getEntry(0), tr("Executables (*.exe *.dll);;All files (*.*)")); if(!filename.length()) return; filename = QDir::toNativeSeparators(filename); //convert to native path format (with backlashes) openRecentFileSlot(filename); } void MainWindow::openRecentFileSlot(QString filename) { DbgCmdExec(QString().sprintf("init \"%s\"", DbgCmdEscape(filename).toUtf8().constData())); } void MainWindow::runSlot() { if(DbgIsDebugging()) DbgCmdExec("run"); else restartDebugging(); } void MainWindow::restartDebugging() { auto last = mMRUList->getEntry(0); if(!last.isEmpty()) DbgCmdExec(QString("init \"%1\"").arg(DbgCmdEscape(last))); } void MainWindow::displayBreakpointWidget() { showQWidgetTab(mBreakpointsView); } void MainWindow::dragEnterEvent(QDragEnterEvent* pEvent) { if(pEvent->mimeData()->hasUrls()) { pEvent->acceptProposedAction(); } } void MainWindow::dropEvent(QDropEvent* pEvent) { if(pEvent->mimeData()->hasUrls()) { QString filename = QDir::toNativeSeparators(pEvent->mimeData()->urls()[0].toLocalFile()); DbgCmdExec(QString().sprintf("init \"%s\"", DbgCmdEscape(filename).toUtf8().constData())); pEvent->acceptProposedAction(); } } bool MainWindow::event(QEvent* event) { // just make sure mTabWidget take current view as the latest if(event->type() == QEvent::WindowActivate && this->isActiveWindow()) { mTabWidget->setCurrentIndex(mTabWidget->currentIndex()); } else if(event->type() == QEvent::StatusTip) { QStatusTipEvent* tip = dynamic_cast<QStatusTipEvent*>(event); mLastLogLabel->showMessage(tip->tip()); return true; } return QMainWindow::event(event); } bool MainWindow::nativeEvent(const QByteArray & eventType, void* message, long* result) { const auto msg = (MSG*)message; if(msg->message == WM_SYSCOMMAND && (msg->wParam & 0xFFF0) == SC_CLOSE) { // When a modal dialog is open (QDialog::exec), the main window is disabled. // DefWindowProc ignores WM_SYSCOMMAND/SC_CLOSE for disabled windows, // so taskbar right-click -> "Close window" silently fails. We intercept // the message here before DefWindowProc and handle it ourselves. if(QApplication::activeModalWidget()) { QApplication::closeAllWindows(); *result = 0; return true; } } return QMainWindow::nativeEvent(eventType, message, result); } void MainWindow::updateWindowTitleSlot(QString filename) { if(filename.length()) { setWindowTitle(filename + QString(" - ") + mWindowMainTitle); } else { setWindowTitle(mWindowMainTitle); } } void MainWindow::updateDarkTitleBar(QWidget* widget) { auto NtBuildNumber = BridgeGetNtBuildNumber(); if(NtBuildNumber < 17763) return; duint darkTitleBar = 0; BridgeSettingGetUint("Colors", "DarkTitleBar", &darkTitleBar); // Do not make the title bar dark/light when already done auto darkProp = widget->property("DarkTitleBar"); if(darkProp.isValid() && darkProp.toUInt() == darkTitleBar) { return; } widget->setProperty("DarkTitleBar", QVariant(darkTitleBar != 0)); static auto hdwmapi = LoadLibraryW(L"dwmapi.dll"); if(hdwmapi) { typedef int(WINAPI * DWMSETWINDOWATTRIBUTE)(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute); static auto DwmSetWindowAttribute = (DWMSETWINDOWATTRIBUTE)GetProcAddress(hdwmapi, "DwmSetWindowAttribute"); auto hwnd = (HWND)widget->winId(); DwmSetWindowAttribute(hwnd, (NtBuildNumber >= 18985) ? 20 : 19, &darkTitleBar, sizeof(uint32_t)); // HACK: Create a 1x1 pixel frameless window on top of the title bar to force Windows to redraw it auto w = new QWidget(nullptr, Qt::FramelessWindowHint); w->resize(1, 1); w->move(widget->pos()); w->show(); delete w; } } // Used by View->CPU void MainWindow::displayCpuWidgetShowCpu() { showQWidgetTab(mCpuWidget); mCpuWidget->setDisasmFocus(); } // GuiShowCpu() void MainWindow::displayCpuWidget() { showQWidgetTab(mCpuWidget); } void MainWindow::displayThreadsWidget() { showQWidgetTab(mThreadView); } void MainWindow::displaySymbolWidget() { showQWidgetTab(mSymbolView); } void MainWindow::displaySourceViewWidget() { showQWidgetTab(mSourceViewManager); } void MainWindow::displayReferencesWidget() { showQWidgetTab(mReferenceManager); } void MainWindow::displayGraphWidget() { showQWidgetTab(mCpuWidget); mCpuWidget->setGraphFocus(); } void MainWindow::openSettings() { SettingsDialog settings(this); connect(&settings, SIGNAL(chkSaveLoadTabOrderStateChanged(bool)), this, SLOT(chkSaveloadTabSavedOrderStateChangedSlot(bool))); settings.lastException = lastException; settings.exec(); } void MainWindow::openAppearance() { AppearanceDialog appearance(this); appearance.exec(); } void MainWindow::openCalculator() { mCalculatorDialog->showNormal(); mCalculatorDialog->setFocus(); mCalculatorDialog->setExpressionFocus(); } void MainWindow::openShortcuts() { ShortcutsDialog shortcuts(this); shortcuts.exec(); } void MainWindow::changeTopmost(bool checked) { if(checked) { if(SetWindowPos((HWND)this->winId(), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE)) { Config()->setBool("Gui", "Topmost", true); return; } } else SetWindowPos((HWND)this->winId(), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); Config()->setBool("Gui", "Topmost", false); } void MainWindow::addRecentFile(QString file) { mMRUList->addEntry(file); updateMRUMenu(); mMRUList->save(); } void MainWindow::setLastException(unsigned int exceptionCode) { lastException = exceptionCode; } void MainWindow::findStrings() { DbgCmdExec(QString("strref " + ToPtrString(mCpuWidget->getDisasmWidget()->getSelectedVa()))); displayReferencesWidget(); } void MainWindow::findModularCalls() { DbgCmdExec(QString("modcallfind " + ToPtrString(mCpuWidget->getDisasmWidget()->getSelectedVa()))); displayReferencesWidget(); } void MainWindow::initMenuApi() { mMenuMutex = new QMutex(QMutex::Recursive); //256 entries are reserved hEntryMenuPool = 256; mEntryList.reserve(1024); mMenuList.reserve(1024); } void MainWindow::menuEntrySlot() { QAction* action = qobject_cast<QAction*>(sender()); if(action && action->objectName().startsWith("ENTRY|")) { int hEntry = -1; if(sscanf_s(action->objectName().mid(6).toUtf8().constData(), "%d", &hEntry) == 1) DbgMenuEntryClicked(hEntry); } } MainWindow::MenuInfo* MainWindow::findMenu(int hMenu) { if(hMenu == -1) return nullptr; // TODO: optimize with a map for(auto & menu : mMenuList) if(menu.hMenu == hMenu) return menu.deleted ? nullptr : &menu; return nullptr; } MainWindow::MenuEntryInfo* MainWindow::findMenuEntry(int hEntry) { if(hEntry == -1) return nullptr; // TODO: optimize with a map for(auto & entry : mEntryList) if(entry.hEntry == hEntry) return entry.deleted ? nullptr : &entry; return nullptr; } void MainWindow::addMenuToList(QWidget* parent, QMenu* menu, GUIMENUTYPE hMenu, int hParentMenu) { QMutexLocker locker(mMenuMutex); if(!findMenu(hMenu)) mMenuList.push_back(MenuInfo(parent, menu, hMenu, hParentMenu, hMenu == GUI_PLUGIN_MENU)); Bridge::getBridge()->setResult(BridgeResult::MenuAddToList); } void MainWindow::addMenu(int hMenu, QString title) { QMutexLocker locker(mMenuMutex); auto parentMenu = findMenu(hMenu); if(hMenu != -1 && parentMenu == nullptr) { Bridge::getBridge()->setResult(BridgeResult::MenuAdd, -1); return; } int hMenuNew = hEntryMenuPool++; MenuInfo newInfo; newInfo.hMenu = hMenuNew; newInfo.hParentMenu = hMenu; newInfo.globalMenu = !parentMenu || parentMenu->globalMenu; mMenuList.push_back(newInfo); MethodInvoker::invokeMethod([this, hMenuNew, title] { QMutexLocker locker(mMenuMutex); // Abort if another thread deleted the entry or the parent menu auto menuInfo = findMenu(hMenuNew); if(!menuInfo) return; auto parentMenu = findMenu(menuInfo->hParentMenu); if(parentMenu == nullptr && menuInfo->hParentMenu != -1) return; // Actually create the menu QWidget* parent = menuInfo->hParentMenu == -1 ? this : parentMenu->parent; menuInfo->parent = parent; QMenu* menu = new QMenu(title, parent); menuInfo->mMenu = menu; menu->menuAction()->setVisible(false); if(menuInfo->hParentMenu == -1) //top-level ui->menuBar->addMenu(menu); else //deeper level { parentMenu->mMenu->addMenu(menu); parentMenu->mMenu->menuAction()->setVisible(true); } }); Bridge::getBridge()->setResult(BridgeResult::MenuAdd, hMenuNew); } void MainWindow::addMenuEntry(int hMenu, QString title) { QMutexLocker locker(mMenuMutex); if(hMenu != -1 && findMenu(hMenu) == nullptr) { Bridge::getBridge()->setResult(BridgeResult::MenuAddEntry, -1); return; } MenuEntryInfo newInfo; int hEntryNew = hEntryMenuPool++; newInfo.hEntry = hEntryNew; newInfo.hParentMenu = hMenu; mEntryList.push_back(newInfo); MethodInvoker::invokeMethod([this, hEntryNew, title] { QMutexLocker locker(mMenuMutex); // Abort if another thread deleted the entry or the parent menu auto entry = findMenuEntry(hEntryNew); if(entry == nullptr) return; auto menu = findMenu(entry->hParentMenu); if(menu == nullptr && entry->hParentMenu != -1) return; // Actually create the menu action QWidget* parent = entry->hParentMenu == -1 ? this : menu->parent; QAction* action = new QAction(title, parent); parent->addAction(action); action->setObjectName(QString().sprintf("ENTRY|%d", hEntryNew)); action->setShortcutContext((!menu || menu->globalMenu) ? Qt::ApplicationShortcut : Qt::WidgetShortcut); parent->addAction(action); // TODO: something is wrong here connect(action, SIGNAL(triggered()), this, SLOT(menuEntrySlot())); entry->mAction = action; if(entry->hParentMenu == -1) //top level ui->menuBar->addAction(action); else //deeper level { menu->mMenu->addAction(action); menu->mMenu->menuAction()->setVisible(true); } }); Bridge::getBridge()->setResult(BridgeResult::MenuAddEntry, hEntryNew); } void MainWindow::addSeparator(int hMenu) { QMutexLocker locker(mMenuMutex); if(findMenu(hMenu) == nullptr) { Bridge::getBridge()->setResult(BridgeResult::MenuAddSeparator, -1); return; } MenuEntryInfo newInfo; auto hEntryNew = hEntryMenuPool++; newInfo.hEntry = hEntryNew; newInfo.hParentMenu = hMenu; mEntryList.push_back(newInfo); MethodInvoker::invokeMethod([this, hEntryNew] { QMutexLocker locker(mMenuMutex); // Abort if another thread deleted the entry or the parent menu auto entry = findMenuEntry(hEntryNew); if(entry == nullptr) return; auto menu = findMenu(entry->hParentMenu); if(menu == nullptr) return; // Actually create the separator entry->mAction = menu->mMenu->addSeparator(); }); Bridge::getBridge()->setResult(BridgeResult::MenuAddSeparator, hEntryNew); } void MainWindow::clearMenuHelper(int hMenu, bool markAsDeleted) { //delete menu entries for(int i = mEntryList.size() - 1; i != -1; i--) { if(hMenu == mEntryList[i].hParentMenu) //we found an entry that has the menu as parent { if(markAsDeleted) mEntryList[i].deleted = true; else { mEntryList[i].mAction->setShortcut(QKeySequence()); mEntryList.erase(mEntryList.begin() + i); } } } //delete the menus std::vector<int> menuClearQueue; for(int i = mMenuList.size() - 1; i != -1; i--) { if(hMenu == mMenuList[i].hParentMenu) //we found a menu that has the menu as parent { menuClearQueue.push_back(mMenuList[i].hMenu); if(markAsDeleted) mMenuList[i].deleted = true; else mMenuList.erase(mMenuList.begin() + i); } } //recursively clear the menus for(auto & hMenu : menuClearQueue) clearMenuHelper(hMenu, markAsDeleted); } void MainWindow::clearMenuImpl(int hMenu, bool erase) { //this recursively removes the entries from mEntryList and mMenuList clearMenuHelper(hMenu, false); for(auto it = mMenuList.begin(); it != mMenuList.end(); ++it) { auto & curMenu = *it; if(hMenu == curMenu.hMenu) { if(erase) { auto parentMenu = findMenu(curMenu.hParentMenu); if(parentMenu) { parentMenu->mMenu->removeAction(curMenu.mMenu->menuAction()); //remove the QMenu from the parent if(parentMenu->mMenu->actions().empty()) //hide the parent if it is now empty parentMenu->mMenu->menuAction()->setVisible(false); } it = mMenuList.erase(it); } else { curMenu.mMenu->clear(); //clear the QMenu curMenu.mMenu->menuAction()->setVisible(false); } break; } } } void MainWindow::clearMenu(int hMenu, bool erase) { QMutexLocker locker(mMenuMutex); if(findMenu(hMenu) == nullptr) { Bridge::getBridge()->setResult(BridgeResult::MenuClear, -1); return; } // Mark all the children of the menu as deleted clearMenuHelper(hMenu, true); MethodInvoker::invokeMethod([this, hMenu, erase] { QMutexLocker locker(mMenuMutex); // Actually clear the menu clearMenuImpl(hMenu, erase); }); Bridge::getBridge()->setResult(BridgeResult::MenuClear); } void MainWindow::removeMenuEntry(int hEntryMenu) { QMutexLocker locker(mMenuMutex); auto entry = findMenuEntry(hEntryMenu); if(entry != nullptr) { // Delete a single menu entry entry->deleted = true; MethodInvoker::invokeMethod([this, hEntryMenu] { QMutexLocker locker(mMenuMutex); for(int i = 0; i < mEntryList.size(); i++) { if(mEntryList.at(i).hEntry == hEntryMenu) { auto & entry = mEntryList.at(i); auto parentMenu = findMenu(entry.hParentMenu); if(parentMenu) { parentMenu->mMenu->removeAction(entry.mAction); if(parentMenu->mMenu->actions().empty()) parentMenu->mMenu->menuAction()->setVisible(false); mEntryList.erase(mEntryList.begin() + i); } break; } } }); Bridge::getBridge()->setResult(BridgeResult::MenuRemove); return; } auto menu = findMenu(hEntryMenu); if(menu != nullptr) { // Mark the menu and all submenus as deleted menu->deleted = true; clearMenuHelper(hEntryMenu, true); MethodInvoker::invokeMethod([this, hEntryMenu] { // Actually delete the menu and all submenus clearMenuImpl(hEntryMenu, true); }); Bridge::getBridge()->setResult(BridgeResult::MenuRemove); return; } Bridge::getBridge()->setResult(BridgeResult::MenuRemove, -1); } void MainWindow::setIconMenuEntry(int hEntry, QIcon icon) { MethodInvoker::invokeMethod([this, hEntry, icon] { QMutexLocker locker(mMenuMutex); auto entry = findMenuEntry(hEntry); if(entry == nullptr) return; entry->mAction->setIcon(icon); }); Bridge::getBridge()->setResult(BridgeResult::MenuSetEntryIcon); } void MainWindow::setIconMenu(int hMenu, QIcon icon) { MethodInvoker::invokeMethod([this, hMenu, icon] { QMutexLocker locker(mMenuMutex); auto menu = findMenu(hMenu); if(menu == nullptr) return; menu->mMenu->setIcon(icon); }); Bridge::getBridge()->setResult(BridgeResult::MenuSetIcon); } void MainWindow::setCheckedMenuEntry(int hEntry, bool checked) { MethodInvoker::invokeMethod([this, hEntry, checked] { QMutexLocker locker(mMenuMutex); auto entry = findMenuEntry(hEntry); if(entry == nullptr) return; entry->mAction->setCheckable(true); entry->mAction->setChecked(checked); }); Bridge::getBridge()->setResult(BridgeResult::MenuSetEntryChecked); } QString MainWindow::nestedMenuDescription(const MenuInfo* menu) { auto found = findMenu(menu->hParentMenu); if(!found) return menu->mMenu->title(); auto nest = nestedMenuDescription(found); if(nest.isEmpty()) { switch(menu->hParentMenu) { case GUI_DISASM_MENU: nest = tr("&Plugins") + " -> " + tr("Disassembly"); break; case GUI_DUMP_MENU: nest = tr("&Plugins") + " -> " + tr("Dump"); break; case GUI_STACK_MENU: nest = tr("&Plugins") + " -> " + tr("Stack"); break; } } nest += " -> "; return nest + menu->mMenu->title(); } QString MainWindow::nestedMenuEntryDescription(const MenuEntryInfo & entry) { return QString(nestedMenuDescription(findMenu(entry.hParentMenu)) + " -> " + entry.mAction->text()).replace("&", ""); } void MainWindow::setHotkeyMenuEntry(int hEntry, QString hotkey, QString id) { MethodInvoker::invokeMethod([this, hEntry, hotkey, id] { QMutexLocker locker(mMenuMutex); auto entry = findMenuEntry(hEntry); if(entry == nullptr) return; entry->hotkeyId = QString("Plugin_") + id; entry->hotkey = hotkey; entry->hotkeyGlobal = entry->mAction->shortcutContext() == Qt::ApplicationShortcut; Config()->setPluginShortcut(entry->hotkeyId, nestedMenuEntryDescription(*entry), hotkey, entry->hotkeyGlobal); refreshShortcuts(); }); Bridge::getBridge()->setResult(BridgeResult::MenuSetEntryHotkey); } void MainWindow::setVisibleMenuEntry(int hEntry, bool visible) { MethodInvoker::invokeMethod([this, hEntry, visible] { QMutexLocker locker(mMenuMutex); auto entry = findMenuEntry(hEntry); if(entry == nullptr) return; entry->mAction->setVisible(visible); }); Bridge::getBridge()->setResult(BridgeResult::MenuSetEntryVisible); } void MainWindow::setVisibleMenu(int hMenu, bool visible) { MethodInvoker::invokeMethod([this, hMenu, visible] { QMutexLocker locker(mMenuMutex); auto menu = findMenu(hMenu); if(menu == nullptr) return; menu->mMenu->setVisible(visible); }); Bridge::getBridge()->setResult(BridgeResult::MenuSetVisible); } void MainWindow::setNameMenuEntry(int hEntry, QString name) { MethodInvoker::invokeMethod([this, hEntry, name] { QMutexLocker locker(mMenuMutex); auto entry = findMenuEntry(hEntry); if(entry == nullptr) return; entry->mAction->setText(name); Config()->setPluginShortcut(entry->hotkeyId, nestedMenuEntryDescription(*entry), entry->hotkey, entry->hotkeyGlobal); }); Bridge::getBridge()->setResult(BridgeResult::MenuSetEntryName); } void MainWindow::setNameMenu(int hMenu, QString name) { MethodInvoker::invokeMethod([this, hMenu, name] { QMutexLocker locker(mMenuMutex); auto menu = findMenu(hMenu); if(menu == nullptr) return; menu->mMenu->setTitle(name); }); Bridge::getBridge()->setResult(BridgeResult::MenuSetName); } void MainWindow::runSelection() { if(DbgIsDebugging()) { duint addr = 0; if(mTabWidget->currentWidget() == mCpuWidget || (mCpuWidget->window() != this && mCpuWidget->isActiveWindow())) addr = mCpuWidget->getSelectionVa(); else if(mTabWidget->currentWidget() == mCallStackView || (mCallStackView->window() != this && mCallStackView->isActiveWindow())) addr = mCallStackView->getSelectionVa(); if(addr) DbgCmdExec("run " + ToPtrString(addr)); } } void MainWindow::runExpression() { if(!DbgIsDebugging()) return; GotoDialog gotoDialog(this); gotoDialog.setWindowTitle(tr("Enter expression to run to...")); if(gotoDialog.exec() != QDialog::Accepted) return; if(DbgCmdExecDirect(QString("bp \"%1\", ss").arg(gotoDialog.expressionText).toUtf8().constData())) DbgCmdExecDirect("run"); } void MainWindow::getStrWindow(const QString title, QString* text) { LineEditDialog mLineEdit(this); mLineEdit.setWindowTitle(title); bool bResult = true; if(mLineEdit.exec() != QDialog::Accepted) bResult = false; *text = mLineEdit.editText; Bridge::getBridge()->setResult(BridgeResult::GetlineWindow, bResult); } void MainWindow::patchWindow() { if(!DbgIsDebugging()) { SimpleErrorBox(this, tr("Error!"), tr("Patches can only be shown while debugging...")); return; } GuiUpdatePatches(); mPatchDialog->showNormal(); mPatchDialog->setFocus(); } void MainWindow::displayComments() { if(!DbgIsDebugging()) return; DbgCmdExec("commentlist"); displayReferencesWidget(); } void MainWindow::displayLabels() { if(!DbgIsDebugging()) return; DbgCmdExec("labellist"); displayReferencesWidget(); } void MainWindow::displayBookmarks() { if(!DbgIsDebugging()) return; DbgCmdExec("bookmarklist"); displayReferencesWidget(); } void MainWindow::displayFunctions() { if(!DbgIsDebugging()) return; DbgCmdExec("functionlist"); displayReferencesWidget(); } void MainWindow::displayCallstack() { showQWidgetTab(mCallStackView); } void MainWindow::displaySEHChain() { showQWidgetTab(mSEHChainView); } void MainWindow::displayTraceWidget() { showQWidgetTab(mTraceWidget); } void MainWindow::donate() { QMessageBox msg(QMessageBox::Information, tr("Donate"), tr("All the money will go to x64dbg development.")); msg.setWindowIcon(DIcon("donate")); msg.setParent(this, Qt::Dialog); msg.setWindowFlags(msg.windowFlags() & (~Qt::WindowContextHelpButtonHint)); msg.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); msg.setDefaultButton(QMessageBox::Ok); if(msg.exec() != QMessageBox::Ok) return; QDesktopServices::openUrl(QUrl("https://donate.x64dbg.com")); } void MainWindow::blog() { QMessageBox msg(QMessageBox::Information, tr("Blog"), tr("You will visit x64dbg's official blog.")); msg.setWindowIcon(DIcon("hex")); msg.setParent(this, Qt::Dialog); msg.setWindowFlags(msg.windowFlags() & (~Qt::WindowContextHelpButtonHint)); msg.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); msg.setDefaultButton(QMessageBox::Ok); if(msg.exec() != QMessageBox::Ok) return; QDesktopServices::openUrl(QUrl("https://blog.x64dbg.com")); } void MainWindow::reportBug() { QMessageBox msg(QMessageBox::Information, tr("Report Bug"), tr("You will be taken to a website where you can report a bug.\nMake sure to fill in as much information as possible.")); msg.setWindowIcon(DIcon("bug-report")); msg.setParent(this, Qt::Dialog); msg.setWindowFlags(msg.windowFlags() & (~Qt::WindowContextHelpButtonHint)); msg.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); msg.setDefaultButton(QMessageBox::Ok); if(msg.exec() != QMessageBox::Ok) return; QDesktopServices::openUrl(QUrl("https://report.x64dbg.com")); } void MainWindow::crashDump() { QMessageBox msg(QMessageBox::Critical, tr("Generate crash dump"), tr("This action will crash the debugger and generate a crash dump. You will LOSE ALL YOUR UNSAVED DATA. Do you really want to continue?")); msg.setWindowIcon(DIcon("fatal-error")); msg.setParent(this, Qt::Dialog); msg.setWindowFlags(msg.windowFlags() & (~Qt::WindowContextHelpButtonHint)); msg.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); msg.setDefaultButton(QMessageBox::Cancel); if(msg.exec() != QMessageBox::Ok) return; // Fatal error __debugbreak(); // Congratulations! We survived a fatal error! SimpleWarningBox(this, tr("Have fun debugging the debugger!"), tr("Debugger detected!")); } void MainWindow::mnemonicHelp() { QString mnemonic; if(SimpleInputBox(this, tr("Mnemonic help"), "", mnemonic, "call")) { DbgCmdExec(QString("mnemonichelp ").append(mnemonic).toUtf8().constData()); showQWidgetTab(mLogView); } } void MainWindow::displayAttach() { AttachDialog attach(this); attach.exec(); mCpuWidget->setDisasmFocus(); } static bool getCmdLine(QString & cmdLine) { auto result = false; size_t cbsize = 0; if(DbgFunctions()->GetCmdline(0, &cbsize)) { auto buffer = new char[cbsize]; if(DbgFunctions()->GetCmdline(buffer, 0)) { cmdLine = buffer; result = true; } delete[] buffer; } return result; } void MainWindow::changeCommandLine() { if(!DbgIsDebugging()) return; LineEditDialog mLineEdit(this); mLineEdit.setText(""); mLineEdit.setWindowTitle(tr("Change Command Line")); mLineEdit.setWindowIcon(DIcon("changeargs")); QString cmdLine; if(!getCmdLine(cmdLine)) mLineEdit.setText(tr("Cannot get remote command line, use the 'getcmdline' command for more information.")); else mLineEdit.setText(cmdLine); mLineEdit.setCursorPosition(0); if(mLineEdit.exec() != QDialog::Accepted) return; //pressed cancel if(!DbgFunctions()->SetCmdline((char*)mLineEdit.editText.toUtf8().constData())) SimpleErrorBox(this, tr("Error!"), tr("Could not set command line!")); else { DbgFunctions()->MemUpdateMap(); GuiUpdateMemoryView(); getCmdLine(cmdLine); GuiAddLogMessage((tr("New command line: ") + cmdLine + "\n").toUtf8().constData()); } } static void onlineManual() { QDesktopServices::openUrl(QUrl("https://help.x64dbg.com")); } void MainWindow::displayManual() { duint setting = 0; if(BridgeSettingGetUint("Misc", "UseLocalHelpFile", &setting) && setting) { // Open the Windows CHM in the upper directory if(!QDesktopServices::openUrl(QUrl(QUrl::fromLocalFile(QString("%1/../x64dbg.chm").arg(QCoreApplication::applicationDirPath()))))) { QMessageBox messagebox(QMessageBox::Critical, tr("Error"), tr("Manual cannot be opened. Please check if x64dbg.chm exists and ensure there is no other problems with your system.") + '\n' + tr("Do you want to open online manual at https://help.x64dbg.com ?"), QMessageBox::Yes | QMessageBox::No); if(messagebox.exec() == QMessageBox::Yes) onlineManual(); } } else onlineManual(); } void MainWindow::canClose() { bCanClose = true; close(); } void MainWindow::addQWidgetTab(QWidget* qWidget, QString nativeName) { mTabWidget->addTabEx(qWidget, qWidget->windowIcon(), qWidget->windowTitle(), nativeName); } void MainWindow::addQWidgetTab(QWidget* qWidget) { QString nativeName = qWidget->objectName(); if(nativeName.isEmpty()) nativeName = qWidget->metaObject()->className(); nativeName = "Plugin" + nativeName.replace(" ", "_").replace("=", "_"); WidgetInfo info(qWidget, nativeName); addQWidgetTab(info.widget, info.nativeName); mPluginWidgetList.append(info); } void MainWindow::showQWidgetTab(QWidget* qWidget) { qWidget->show(); qWidget->setFocus(); setTab(qWidget); } void MainWindow::closeQWidgetTab(QWidget* qWidget) { for(int i = 0; i < mTabWidget->count(); i++) { if(mTabWidget->widget(i) == qWidget) { mTabWidget->DeleteTab(i); break; } } } void MainWindow::executeOnGuiThread(void* cbGuiThread, void* userdata) { ((GUICALLBACKEX)cbGuiThread)(userdata); } void MainWindow::tabMovedSlot(int from, int to) { Q_UNUSED(from); Q_UNUSED(to); for(int i = 0; i < mTabWidget->count(); i++) { auto tabName = mTabWidget->getNativeName(i); BridgeSettingSetUint("TabOrder", tabName.toUtf8().constData(), i); } } void MainWindow::chkSaveloadTabSavedOrderStateChangedSlot(bool state) { if(state) loadTabSavedOrder(); else loadTabDefaultOrder(); } void MainWindow::dbgStateChangedSlot(DBGSTATE state) { if(state == initialized) //fixes a crash when restarting with certain settings in another tab displayCpuWidget(); if(bExitWhenDetached && state == stopped) //detach and exit: the debugger has detached, no exit confirmation dialog this time close(); } void MainWindow::on_actionFaq_triggered() { QDesktopServices::openUrl(QUrl("https://faq.x64dbg.com")); } void MainWindow::on_actionReloadStylesheet_triggered() { loadSelectedTheme(true); ensurePolished(); update(); } void MainWindow::displayNotesWidget() { showQWidgetTab(mNotesManager); } void MainWindow::displayHandlesWidget() { showQWidgetTab(mHandlesView); } void MainWindow::manageFavourites() { FavouriteTools favToolsDialog(this); favToolsDialog.exec(); updateFavouriteTools(); } static void splitToolPath(const QString & toolPath, QString & file, QString & cmd) { if(toolPath.startsWith('\"')) { auto endQuote = toolPath.indexOf('\"', 1); if(endQuote == -1) //"failure with spaces file = toolPath.mid(1); else //"path with spaces" arguments { file = toolPath.mid(1, endQuote - 1); cmd = toolPath.mid(endQuote + 1); } } else { auto firstSpace = toolPath.indexOf(' '); if(firstSpace == -1) //pathwithoutspaces file = toolPath; else //pathwithoutspaces argument { file = toolPath.left(firstSpace); cmd = toolPath.mid(firstSpace + 1); } } file = file.trimmed(); cmd = cmd.trimmed(); } void MainWindow::updateFavouriteTools() { char buffer[MAX_SETTING_SIZE]; bool isanythingexists = false; ui->menuFavourites->clear(); delete actionManageFavourites; mFavouriteToolbar->clear(); actionManageFavourites = new QAction(DIcon("star"), tr("&Manage Favourite Tools..."), this); actionManageFavourites->setStatusTip(tr("Open the Favourites dialog to manage the favourites menu")); for(unsigned int i = 1; BridgeSettingGet("Favourite", QString("Tool%1").arg(i).toUtf8().constData(), buffer); i++) { QString toolPath = QString(buffer); QAction* newAction = new QAction(actionManageFavourites); // Auto delete these actions on updateFavouriteTools() // Set up user data to be used in clickFavouriteTool() newAction->setData(QVariant(QString("Tool,%1").arg(toolPath))); if(BridgeSettingGet("Favourite", QString("ToolShortcut%1").arg(i).toUtf8().constData(), buffer)) if(*buffer && strcmp(buffer, "NOT_SET") != 0) setGlobalShortcut(newAction, QKeySequence(QString(buffer))); QString description; if(BridgeSettingGet("Favourite", QString("ToolDescription%1").arg(i).toUtf8().constData(), buffer)) description = QString(buffer); else description = toolPath; newAction->setText(description); newAction->setStatusTip(description); // Get the icon of the executable QString file, cmd; QIcon icon; splitToolPath(toolPath, file, cmd); icon = getFileIcon(file); if(icon.isNull()) icon = DIcon("plugin"); newAction->setIcon(icon); connect(newAction, SIGNAL(triggered()), this, SLOT(clickFavouriteTool())); ui->menuFavourites->addAction(newAction); mFavouriteToolbar->addAction(newAction); isanythingexists = true; } if(isanythingexists) { isanythingexists = false; ui->menuFavourites->addSeparator(); mFavouriteToolbar->addSeparator(); } for(unsigned int i = 1; BridgeSettingGet("Favourite", QString("Script%1").arg(i).toUtf8().constData(), buffer); i++) { QString scriptPath = QString(buffer); QAction* newAction = new QAction(actionManageFavourites); // Set up user data to be used in clickFavouriteTool() newAction->setData(QVariant(QString("Script,%1").arg(scriptPath))); if(BridgeSettingGet("Favourite", QString("ScriptShortcut%1").arg(i).toUtf8().constData(), buffer)) if(*buffer && strcmp(buffer, "NOT_SET") != 0) setGlobalShortcut(newAction, QKeySequence(QString(buffer))); QString description; if(BridgeSettingGet("Favourite", QString("ScriptDescription%1").arg(i).toUtf8().constData(), buffer)) description = QString(buffer); else description = scriptPath; newAction->setText(description); newAction->setStatusTip(description); connect(newAction, SIGNAL(triggered()), this, SLOT(clickFavouriteTool())); newAction->setIcon(DIcon("script-code")); ui->menuFavourites->addAction(newAction); mFavouriteToolbar->addAction(newAction); isanythingexists = true; } if(isanythingexists) { isanythingexists = false; ui->menuFavourites->addSeparator(); mFavouriteToolbar->addSeparator(); } for(unsigned int i = 1; BridgeSettingGet("Favourite", QString("Command%1").arg(i).toUtf8().constData(), buffer); i++) { QAction* newAction = new QAction(QString(buffer), actionManageFavourites); newAction->setStatusTip(QString(buffer)); // Set up user data to be used in clickFavouriteTool() newAction->setData(QVariant(QString("Command"))); if(BridgeSettingGet("Favourite", QString("CommandShortcut%1").arg(i).toUtf8().constData(), buffer)) if(*buffer && strcmp(buffer, "NOT_SET") != 0) setGlobalShortcut(newAction, QKeySequence(QString(buffer))); connect(newAction, SIGNAL(triggered()), this, SLOT(clickFavouriteTool())); newAction->setIcon(DIcon("star")); ui->menuFavourites->addAction(newAction); mFavouriteToolbar->addAction(newAction); isanythingexists = true; } if(isanythingexists) { ui->menuFavourites->addSeparator(); mFavouriteToolbar->addSeparator(); } ui->menuFavourites->addAction(actionManageFavourites); setGlobalShortcut(actionManageFavourites, ConfigShortcut("FavouritesManage")); connect(ui->menuFavourites->actions().last(), SIGNAL(triggered()), this, SLOT(manageFavourites())); mFavouriteToolbar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); ui->menuFavourites->addAction(mFavouriteToolbar->toggleViewAction()); } void MainWindow::clickFavouriteTool() { QAction* action = qobject_cast<QAction*>(sender()); if(action == nullptr) return; QString data = action->data().toString(); if(data.startsWith("Tool,")) { QString toolPath = data.mid(5); duint PID = DbgValFromString("$pid"); toolPath.replace(QString("%PID%"), QString::number(PID), Qt::CaseInsensitive); toolPath.replace(QString("%DEBUGGEE%"), mMRUList->getEntry(0), Qt::CaseInsensitive); char modpath[MAX_MODULE_SIZE] = ""; DbgFunctions()->ModPathFromAddr(DbgValFromString("dis.sel()"), modpath, MAX_MODULE_SIZE); toolPath.replace(QString("%MODULE%"), modpath, Qt::CaseInsensitive); while(true) { auto sfStart = toolPath.indexOf("%-"); auto sfEnd = toolPath.indexOf("-%"); if(sfStart < 0 || sfEnd < 0 || sfEnd < sfStart) break; auto format = toolPath.mid(sfStart + 2, sfEnd - sfStart - 2); toolPath.replace(sfStart, sfEnd - sfStart + 2, StringFormatInline(format)); } GuiAddLogMessage(tr("Starting tool %1\n").arg(toolPath).toUtf8().constData()); PROCESS_INFORMATION procinfo; STARTUPINFOW startupinfo; memset(&procinfo, 0, sizeof(PROCESS_INFORMATION)); memset(&startupinfo, 0, sizeof(startupinfo)); startupinfo.cb = sizeof(startupinfo); if(CreateProcessW(nullptr, (LPWSTR)toolPath.toStdWString().c_str(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startupinfo, &procinfo)) { CloseHandle(procinfo.hThread); CloseHandle(procinfo.hProcess); } else if(GetLastError() == ERROR_ELEVATION_REQUIRED) { QString file, cmd; splitToolPath(toolPath, file, cmd); ShellExecuteW(nullptr, L"runas", file.toStdWString().c_str(), cmd.toStdWString().c_str(), nullptr, SW_SHOWNORMAL); } } else if(data.startsWith("Script,")) { QString scriptPath = data.mid(7); mScriptView->openRecentFile(scriptPath); displayScriptWidget(); } else if(data.compare("Command") == 0) { DbgCmdExec(action->text()); } } void MainWindow::chooseLanguage() { QAction* action = qobject_cast<QAction*>(sender()); QString localeName = action->text(); localeName = localeName.mid(1, localeName.indexOf(QChar(']')) - 1); action->setChecked(localeName == QString(gCurrentLocale)); if(localeName != "en_US") { QDir translationsDir(QString("%1/../translations/").arg(QCoreApplication::applicationDirPath())); QFile file(translationsDir.absoluteFilePath(QString("x64dbg_%1.qm").arg(localeName))); // A translation file less than 0.5KB is probably not useful if(file.size() < 512) { QMessageBox msg(this); msg.setWindowIcon(DIcon("codepage")); msg.setIcon(QMessageBox::Information); if(tr("Languages") == QString("Languages")) { msg.setWindowTitle(QString("Languages")); msg.setText(QString("The translation is nearly empty. Do you still want to use this language?")); } else { msg.setWindowTitle(tr("Languages") + QString(" Languages")); msg.setText(tr("The translation is nearly empty. Do you still want to use this language?") + QString("\r\nThe translation is nearly empty. Do you still want to use this language?")); } msg.addButton(QMessageBox::Yes); msg.addButton(QMessageBox::No); if(msg.exec() == QMessageBox::No) return; } } BridgeSettingSet("Engine", "Language", localeName.toUtf8().constData()); QMessageBox msg(this); msg.setIcon(QMessageBox::Information); msg.setWindowIcon(DIcon("codepage")); if(tr("Languages") == QString("Languages")) { msg.setWindowTitle(QString("Languages")); msg.setText(QString("New language setting will take effect upon restart.")); } else { msg.setWindowTitle(tr("Languages") + QString(" Languages")); msg.setText(tr("New language setting will take effect upon restart.") + QString("\r\nNew language setting will take effect upon restart.")); } msg.exec(); } void MainWindow::addFavouriteItem(int type, const QString & name, const QString & description) { if(type == 0) // Tools { char buffer[MAX_SETTING_SIZE]; unsigned int i; for(i = 1; BridgeSettingGet("Favourite", (QString("Tool") + QString::number(i)).toUtf8().constData(), buffer); i++) { } BridgeSettingSet("Favourite", (QString("Tool") + QString::number(i)).toUtf8().constData(), name.toUtf8().constData()); BridgeSettingSet("Favourite", (QString("ToolDescription") + QString::number(i)).toUtf8().constData(), description.toUtf8().constData()); if(BridgeSettingGet("Favourite", (QString("Tool") + QString::number(i + 1)).toUtf8().constData(), buffer)) { buffer[0] = 0; BridgeSettingSet("Favourite", (QString("Tool") + QString::number(i + 1)).toUtf8().constData(), buffer); } updateFavouriteTools(); } else if(type == 2) // Commands { char buffer[MAX_SETTING_SIZE]; unsigned int i; for(i = 1; BridgeSettingGet("Favourite", (QString("Command") + QString::number(i)).toUtf8().constData(), buffer); i++) { } BridgeSettingSet("Favourite", (QString("Command") + QString::number(i)).toUtf8().constData(), name.toUtf8().constData()); BridgeSettingSet("Favourite", (QString("CommandShortcut") + QString::number(i)).toUtf8().constData(), description.toUtf8().constData()); if(BridgeSettingGet("Favourite", (QString("Command") + QString::number(i + 1)).toUtf8().constData(), buffer)) { buffer[0] = 0; BridgeSettingSet("Favourite", (QString("Command") + QString::number(i + 1)).toUtf8().constData(), buffer); } updateFavouriteTools(); } } void MainWindow::setFavouriteItemShortcut(int type, const QString & name, const QString & shortcut) { if(type == 0) { char buffer[MAX_SETTING_SIZE]; for(unsigned int i = 1; BridgeSettingGet("Favourite", QString("Tool%1").arg(i).toUtf8().constData(), buffer); i++) { if(QString(buffer) == name) { BridgeSettingSet("Favourite", (QString("ToolShortcut") + QString::number(i)).toUtf8().constData(), shortcut.toUtf8().constData()); updateFavouriteTools(); break; } } } } void MainWindow::animateIntoSlot() { if(DbgIsDebugging()) DbgFunctions()->AnimateCommand("StepInto;AnimateWait"); } void MainWindow::animateOverSlot() { if(DbgIsDebugging()) DbgFunctions()->AnimateCommand("StepOver;AnimateWait"); } void MainWindow::animateCommandSlot() { QString command; if(SimpleInputBox(this, tr("Animate command"), "", command, tr("Example: StepInto"))) DbgFunctions()->AnimateCommand(command.toUtf8().constData()); } void MainWindow::setInitializationScript() { SystemBreakpointScriptDialog dialog(this); dialog.exec(); } void MainWindow::customizeMenu() { CustomizeMenuDialog customMenuDialog(this); customMenuDialog.setWindowTitle(tr("Customize Menus")); customMenuDialog.setWindowIcon(DIcon("analysis")); customMenuDialog.exec(); onMenuCustomized(); } void MainWindow::on_actionImportSettings_triggered() { auto filename = QFileDialog::getOpenFileName(this, tr("Open file"), QString::fromWCharArray(BridgeUserDirectory()), tr("Settings (*.ini);;All files (*.*)")); if(!filename.length()) return; importSettings(filename); } void MainWindow::on_actionImportdatabase_triggered() { if(!DbgIsDebugging()) return; auto filename = QFileDialog::getOpenFileName(this, tr("Import database"), QString(), tr("Databases (%1);;Database backup (%1.bak);;All files (*.*)").arg(ArchValue("*.dd32", "*.dd64"))); if(!filename.length()) return; DbgCmdExec(QString("dbload \"%1\"").arg(QDir::toNativeSeparators(filename))); } void MainWindow::on_actionExportdatabase_triggered() { if(!DbgIsDebugging()) return; auto filename = QFileDialog::getSaveFileName(this, tr("Export database"), QString(), tr("Databases (%1);;All files (*.*)").arg(ArchValue("*.dd32", "*.dd64"))); if(!filename.length()) return; DbgCmdExec(QString("dbsave \"%1\"").arg(QDir::toNativeSeparators(filename))); } static void setupMenuCustomizationHelper(QMenu* parentMenu, QList<QAction*> & stringList) { for(int i = 0; i < parentMenu->actions().size(); i++) { QAction* action = parentMenu->actions().at(i); stringList.append(action); } } void MainWindow::setupMenuCustomization() { mFileMenuStrings.append(new QAction("File", this)); setupMenuCustomizationHelper(ui->menuFile, mFileMenuStrings); mDebugMenuStrings.append(new QAction("Debug", this)); setupMenuCustomizationHelper(ui->menuDebug, mDebugMenuStrings); mOptionsMenuStrings.append(new QAction("Option", this)); setupMenuCustomizationHelper(ui->menuOptions, mOptionsMenuStrings); mHelpMenuStrings.append(new QAction("Help", this)); setupMenuCustomizationHelper(ui->menuHelp, mHelpMenuStrings); mViewMenuStrings.append(new QAction("View", this)); setupMenuCustomizationHelper(ui->menuView, mViewMenuStrings); onMenuCustomized(); Config()->registerMainMenuStringList(&mFileMenuStrings); Config()->registerMainMenuStringList(&mDebugMenuStrings); Config()->registerMainMenuStringList(&mOptionsMenuStrings); Config()->registerMainMenuStringList(&mHelpMenuStrings); Config()->registerMainMenuStringList(&mViewMenuStrings); } void MainWindow::onMenuCustomized() { QList<QMenu*> menus; QList<QString> menuNativeNames; QList<QList<QAction*>*> menuTextStrings; menus << ui->menuFile << ui->menuDebug << ui->menuOptions << ui->menuHelp << ui->menuView; menuNativeNames << "File" << "Debug" << "Option" << "Help" << "View"; menuTextStrings << &mFileMenuStrings << &mDebugMenuStrings << &mOptionsMenuStrings << &mHelpMenuStrings << &mViewMenuStrings; for(int i = 0; i < menus.size(); i++) { QMenu* currentMenu = menus[i]; QMenu* moreCommands = nullptr; bool moreCommandsUsed = false; QList<QAction*> list = currentMenu->actions(); moreCommands = list.last()->menu(); if(moreCommands && moreCommands->title().compare(tr("More Commands")) == 0) { for(auto & j : moreCommands->actions()) moreCommands->removeAction(j); QAction* separatorMoreCommands = list.at(list.length() - 2); currentMenu->removeAction(separatorMoreCommands); // Separator delete separatorMoreCommands; } else { moreCommands = new QMenu(tr("More Commands"), currentMenu); } for(auto & j : list) currentMenu->removeAction(j); for(int j = 0; j < menuTextStrings.at(i)->size() - 1; j++) { QAction* a = menuTextStrings.at(i)->at(j + 1); if(Config()->getBool("Gui", QString("Menu%1Hidden%2").arg(menuNativeNames[i]).arg(j))) { moreCommands->addAction(a); moreCommandsUsed = true; } else { currentMenu->addAction(a); } } if(moreCommandsUsed) { currentMenu->addSeparator(); currentMenu->addMenu(moreCommands); } else delete moreCommands; } } void MainWindow::on_actionPlugins_triggered() { QDesktopServices::openUrl(QUrl("https://plugins.x64dbg.com")); } void MainWindow::on_actionCheckUpdates_triggered() { mUpdateChecker->checkForUpdates(); } void MainWindow::on_actionDefaultTheme_triggered() { // Revert to the Default theme BridgeSettingSet("Theme", "Selected", "Default"); // Load style loadSelectedTheme(); updateDarkTitleBar(this); } void MainWindow::on_actionAbout_Qt_triggered() { auto w = new QWidget(this); w->setWindowIcon(QApplication::style()->standardIcon(QStyle::SP_TitleBarMenuButton)); QMessageBox::aboutQt(w); delete w; } void MainWindow::on_actionReleaseNotes_triggered() { duint cutoffEpoch = 0; BridgeSettingGetUint("Gui", "ReleaseNotesPrevEpoch", &cutoffEpoch); showReleaseNotes(cutoffEpoch); } void MainWindow::updateStyle() { // Set configured link color QPalette appPalette = QApplication::palette(); appPalette.setColor(QPalette::Link, ConfigColor("LinkColor")); QApplication::setPalette(appPalette); } void MainWindow::updateFont() { QApplication::setFont(ConfigFont("Application")); }