From 5cf030ddac45993913bcf353275f4ebd97dcb764 Mon Sep 17 00:00:00 2001 From: crueter Date: Mon, 11 Aug 2025 13:34:43 -0400 Subject: [PATCH] [cmake, desktop] feat: show dependencies and versions Signed-off-by: crueter --- CMakeLists.txt | 27 ++++- CMakeModules/CPMUtil.cmake | 17 ++- CMakeModules/GenerateDepHashes.cmake | 21 ++++ externals/CMakeLists.txt | 2 +- src/CMakeLists.txt | 2 + src/common/CMakeLists.txt | 3 +- src/dep_hashes.h.in | 20 ++++ src/yuzu/CMakeLists.txt | 4 + src/yuzu/aboutdialog.ui | 2 +- src/yuzu/deps_dialog.cpp | 121 +++++++++++++++++++ src/yuzu/deps_dialog.h | 41 +++++++ src/yuzu/deps_dialog.ui | 166 +++++++++++++++++++++++++++ src/yuzu/main.cpp | 9 +- src/yuzu/main.h | 1 + src/yuzu/main.ui | 6 + 15 files changed, 433 insertions(+), 9 deletions(-) create mode 100644 CMakeModules/GenerateDepHashes.cmake create mode 100644 src/dep_hashes.h.in create mode 100644 src/yuzu/deps_dialog.cpp create mode 100644 src/yuzu/deps_dialog.h create mode 100644 src/yuzu/deps_dialog.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e9d7c5a48..b47596333b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -223,7 +223,7 @@ if (YUZU_USE_BUNDLED_VCPKG) DOWNLOAD_ONLY YES URL "https://github.com/microsoft/vcpkg.git" GIT_TAG "ea2a964f93" - KEY "ea2a" + SHA "ea2a964f93" ) include(${vcpkg_SOURCE_DIR}/scripts/buildsystems/vcpkg.cmake) @@ -277,6 +277,30 @@ function(check_submodules_present) message(FATAL_ERROR "Git submodule ${module} not found. " "Please run: \ngit submodule update --init --recursive") endif() + + set(SUBMODULE_DIR "${PROJECT_SOURCE_DIR}/${module}") + + execute_process( + COMMAND git rev-parse --short=10 HEAD + WORKING_DIRECTORY ${SUBMODULE_DIR} + OUTPUT_VARIABLE SUBMODULE_SHA + ) + + # would probably be better to do string parsing, but whatever + execute_process( + COMMAND git remote get-url origin + WORKING_DIRECTORY ${SUBMODULE_DIR} + OUTPUT_VARIABLE SUBMODULE_URL + ) + + string(REGEX REPLACE "\n|\r" "" SUBMODULE_SHA ${SUBMODULE_SHA}) + string(REGEX REPLACE "\n|\r|\\.git" "" SUBMODULE_URL ${SUBMODULE_URL}) + + get_filename_component(SUBMODULE_NAME ${SUBMODULE_DIR} NAME) + + set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_NAMES ${SUBMODULE_NAME}) + set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS ${SUBMODULE_SHA}) + set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_URLS ${SUBMODULE_URL}) endforeach() endfunction() @@ -722,7 +746,6 @@ else() set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu-cmd) endif() - # Installation instructions # ========================= diff --git a/CMakeModules/CPMUtil.cmake b/CMakeModules/CPMUtil.cmake index 7de9b2c7e4..fadb3c951c 100644 --- a/CMakeModules/CPMUtil.cmake +++ b/CMakeModules/CPMUtil.cmake @@ -2,8 +2,8 @@ # SPDX-License-Identifier: GPL-3.0-or-later # Created-By: crueter -# Docs will come at a later date, mostly this is to just reduce boilerplate and some cmake magic -# to allow for runtime viewing of dependency versions +# Docs will come at a later date, mostly this is to just reduce boilerplate +# and some cmake magic to allow for runtime viewing of dependency versions cmake_minimum_required(VERSION 3.22) include(CPM) @@ -35,12 +35,14 @@ function(AddPackage) if (NOT DEFINED PKG_ARGS_URL) if (DEFINED PKG_ARGS_REPO AND DEFINED PKG_ARGS_SHA) - set(PKG_URL "https://github.com/${PKG_ARGS_REPO}/archive/${PKG_ARGS_SHA}.zip") + set(PKG_GIT_URL https://github.com/${PKG_ARGS_REPO}) + set(PKG_URL "${PKG_GIT_URL}/archive/${PKG_ARGS_SHA}.zip") else() message(FATAL_ERROR "CPMUtil: No custom URL and no repository + sha defined") endif() else() set(PKG_URL ${PKG_ARGS_URL}) + set(PKG_GIT_URL ${PKG_URL}) endif() message(STATUS "CPMUtil: Downloading package ${PKG_ARGS_NAME} from ${PKG_URL}") @@ -86,6 +88,15 @@ function(AddPackage) ${PKG_ARGS_UNPARSED_ARGUMENTS} ) + set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_NAMES ${PKG_ARGS_NAME}) + set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_URLS ${PKG_GIT_URL}) + + if (${PKG_ARGS_NAME}_ADDED) + set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS ${PKG_ARGS_SHA}) + else() + set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS "${CPM_PACKAGE_${PKG_ARGS_NAME}_VERSION} (system)") + endif() + # pass stuff to parent scope set(${PKG_ARGS_NAME}_ADDED "${${PKG_ARGS_NAME}_ADDED}" PARENT_SCOPE) set(${PKG_ARGS_NAME}_SOURCE_DIR "${${PKG_ARGS_NAME}_SOURCE_DIR}" PARENT_SCOPE) diff --git a/CMakeModules/GenerateDepHashes.cmake b/CMakeModules/GenerateDepHashes.cmake new file mode 100644 index 0000000000..d0d59bd22f --- /dev/null +++ b/CMakeModules/GenerateDepHashes.cmake @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2025 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + +get_property(NAMES GLOBAL PROPERTY CPM_PACKAGE_NAMES) +get_property(SHAS GLOBAL PROPERTY CPM_PACKAGE_SHAS) +get_property(URLS GLOBAL PROPERTY CPM_PACKAGE_URLS) + +list(LENGTH NAMES DEPS_LENGTH) + +list(JOIN NAMES "\",\n\t\"" DEP_NAME_DIRTY) +set(DEP_NAMES "\t\"${DEP_NAME_DIRTY}\"") + +list(JOIN SHAS "\",\n\t\"" DEP_SHAS_DIRTY) +set(DEP_SHAS "\t\"${DEP_SHAS_DIRTY}\"") + +list(JOIN URLS "\",\n\t\"" DEP_URLS_DIRTY) +set(DEP_URLS "\t\"${DEP_URLS_DIRTY}\"") + +configure_file(dep_hashes.h.in dep_hashes.h @ONLY) +target_sources(common PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/dep_hashes.h) +target_include_directories(common PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 8958a80352..8e86a7452b 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -203,7 +203,7 @@ endif() AddPackage( NAME SPIRV-Headers REPO "KhronosGroup/SPIRV-Headers" - SHA 04b76709bf.zip + SHA 04b76709bf HASH 954bbc4794bd369c828937c7d4106b6c7bd17a992c672bf1c2a24c128f2bcf1a13295111f47ce4a0fa77641db424359b153dfea2f4e9d19fe64effb29f411c5c ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index aa23f1b50e..bd1285b2bc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -232,3 +232,5 @@ if (ANDROID) add_subdirectory(android/app/src/main/jni) target_include_directories(yuzu-android PRIVATE android/app/src/main) endif() + +include(GenerateDepHashes) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index f97dca2432..580f984ad8 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -159,7 +159,8 @@ add_library( wall_clock.cpp wall_clock.h zstd_compression.cpp - zstd_compression.h) + zstd_compression.h +) if(YUZU_ENABLE_PORTABLE) add_compile_definitions(YUZU_ENABLE_PORTABLE) diff --git a/src/dep_hashes.h.in b/src/dep_hashes.h.in new file mode 100644 index 0000000000..cee9df0537 --- /dev/null +++ b/src/dep_hashes.h.in @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +namespace Common { + +static const constexpr std::array dep_names = { +@DEP_NAMES@ +}; + +static const constexpr std::array dep_hashes = { +@DEP_SHAS@ +}; + +static const constexpr std::array dep_urls = { +@DEP_URLS@ +}; + +} // namespace Common diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 2902b695ad..fb42962840 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -238,6 +238,10 @@ add_executable(yuzu migration_dialog.h migration_dialog.cpp migration_worker.h migration_worker.cpp + + deps_dialog.cpp + deps_dialog.h + deps_dialog.ui ) set_target_properties(yuzu PROPERTIES OUTPUT_NAME "eden") diff --git a/src/yuzu/aboutdialog.ui b/src/yuzu/aboutdialog.ui index c220472142..1fedb01814 100644 --- a/src/yuzu/aboutdialog.ui +++ b/src/yuzu/aboutdialog.ui @@ -11,7 +11,7 @@ - About eden + About Eden diff --git a/src/yuzu/deps_dialog.cpp b/src/yuzu/deps_dialog.cpp new file mode 100644 index 0000000000..4cef05e054 --- /dev/null +++ b/src/yuzu/deps_dialog.cpp @@ -0,0 +1,121 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "yuzu/deps_dialog.h" +#include +#include +#include +#include +#include +#include +#include "dep_hashes.h" +#include "ui_deps_dialog.h" +#include + +DepsDialog::DepsDialog(QWidget* parent) + : QDialog(parent) + , ui{std::make_unique()} +{ + ui->setupUi(this); + + constexpr size_t rows = Common::dep_hashes.size(); + ui->tableDeps->setRowCount(rows); + + QStringList labels; + labels << tr("Dependency") << tr("Version"); + + ui->tableDeps->setHorizontalHeaderLabels(labels); + ui->tableDeps->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch); + ui->tableDeps->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeMode::Fixed); + ui->tableDeps->horizontalHeader()->setMinimumSectionSize(200); + + for (size_t i = 0; i < rows; ++i) { + const std::string name = Common::dep_names.at(i); + const std::string sha = Common::dep_hashes.at(i); + const std::string url = Common::dep_urls.at(i); + + std::string dependency = fmt::format("{}", url, name); + + QTableWidgetItem *nameItem = new QTableWidgetItem(QString::fromStdString(dependency)); + QTableWidgetItem *shaItem = new QTableWidgetItem(QString::fromStdString(sha)); + + ui->tableDeps->setItem(i, 0, nameItem); + ui->tableDeps->setItem(i, 1, shaItem); + } + + ui->tableDeps->setItemDelegateForColumn(0, new LinkItemDelegate(this)); +} + +DepsDialog::~DepsDialog() = default; + +LinkItemDelegate::LinkItemDelegate(QObject *parent) + : QStyledItemDelegate(parent) +{} + +void LinkItemDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + auto options = option; + initStyleOption(&options, index); + + QTextDocument doc; + QString html = index.data(Qt::DisplayRole).toString(); + doc.setHtml(html); + + options.text.clear(); + + painter->save(); + painter->translate(options.rect.topLeft()); + doc.drawContents(painter, QRectF(0, 0, options.rect.width(), options.rect.height())); + painter->restore(); +} + +QSize LinkItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItem options = option; + initStyleOption(&options, index); + + QTextDocument doc; + doc.setHtml(options.text); + doc.setTextWidth(options.rect.width()); + return QSize(doc.idealWidth(), doc.size().height()); +} + +bool LinkItemDelegate::editorEvent(QEvent *event, + QAbstractItemModel *model, + const QStyleOptionViewItem &option, + const QModelIndex &index) +{ + if (event->type() == QEvent::MouseButtonRelease) { + QMouseEvent *mouseEvent = static_cast(event); + if (mouseEvent->button() == Qt::LeftButton) { + QString html = index.data(Qt::DisplayRole).toString(); + QTextDocument doc; + doc.setHtml(html); + doc.setTextWidth(option.rect.width()); + + // this is kinda silly but it werks + QAbstractTextDocumentLayout *layout = doc.documentLayout(); + + QPoint pos = mouseEvent->pos() - option.rect.topLeft(); + int charPos = layout->hitTest(pos, Qt::ExactHit); + + if (charPos >= 0) { + QTextCursor cursor(&doc); + cursor.setPosition(charPos); + + QTextCharFormat format = cursor.charFormat(); + + if (format.isAnchor()) { + QString href = format.anchorHref(); + if (!href.isEmpty()) { + QDesktopServices::openUrl(QUrl(href)); + return true; + } + } + } + } + } + return QStyledItemDelegate::editorEvent(event, model, option, index); +} diff --git a/src/yuzu/deps_dialog.h b/src/yuzu/deps_dialog.h new file mode 100644 index 0000000000..f8e7f1d987 --- /dev/null +++ b/src/yuzu/deps_dialog.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include +#include + +namespace Ui { class DepsDialog; } + +class DepsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit DepsDialog(QWidget *parent); + ~DepsDialog() override; + +private: + std::unique_ptr ui; +}; + +class LinkItemDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + explicit LinkItemDelegate(QObject *parent = 0); + +protected: + void paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; + bool editorEvent(QEvent *event, + QAbstractItemModel *model, + const QStyleOptionViewItem &option, + const QModelIndex &index) override; +}; diff --git a/src/yuzu/deps_dialog.ui b/src/yuzu/deps_dialog.ui new file mode 100644 index 0000000000..5c539d50e2 --- /dev/null +++ b/src/yuzu/deps_dialog.ui @@ -0,0 +1,166 @@ + + + DepsDialog + + + + 0 + 0 + 701 + 500 + + + + Eden Dependencies + + + + + + + + + + + 0 + 0 + + + + + 200 + 200 + + + + + + + :/icons/default/256x256/eden.png + + + true + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + + 0 + 0 + + + + <html><head/><body><p><span style=" font-size:28pt;">Eden Dependencies</span></p></body></html> + + + + + + + + 0 + 0 + + + + <html><head/><body><p>The projects that make Eden possible</p></body></html> + + + + + + + QAbstractItemView::EditTrigger::NoEditTriggers + + + true + + + QAbstractItemView::SelectionMode::NoSelection + + + 2 + + + false + + + false + + + + + + + + + + + + + Qt::Orientation::Horizontal + + + QDialogButtonBox::StandardButton::Ok + + + + + + + + + + + buttonBox + accepted() + DepsDialog + accept() + + + 20 + 20 + + + 20 + 20 + + + + + buttonBox + rejected() + DepsDialog + reject() + + + 20 + 20 + + + 20 + 20 + + + + + diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 38c0e4b3ce..6c522794e5 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -160,6 +160,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "yuzu/debugger/console.h" #include "yuzu/debugger/controller.h" #include "yuzu/debugger/wait_tree.h" +#include "yuzu/deps_dialog.h" #include "yuzu/discord.h" #include "yuzu/game_list.h" #include "yuzu/game_list_p.h" @@ -1769,6 +1770,7 @@ void GMainWindow::ConnectMenuEvents() { connect_menu(ui->action_Firmware_From_ZIP, &GMainWindow::OnInstallFirmwareFromZIP); connect_menu(ui->action_Install_Keys, &GMainWindow::OnInstallDecryptionKeys); connect_menu(ui->action_About, &GMainWindow::OnAbout); + connect_menu(ui->action_Eden_Dependencies, &GMainWindow::OnEdenDependencies); } void GMainWindow::UpdateMenuState() { @@ -3742,7 +3744,7 @@ void GMainWindow::OnOpenFAQ() { } void GMainWindow::OnOpenDiscord() { - OpenURL(QUrl(QStringLiteral("https://discord.gg/kXAmGCXBGD"))); + OpenURL(QUrl(QStringLiteral("https://discord.gg/HstXbPch7X"))); } void GMainWindow::OnOpenRevolt() { @@ -4582,6 +4584,11 @@ void GMainWindow::OnAbout() { aboutDialog.exec(); } +void GMainWindow::OnEdenDependencies() { + DepsDialog depsDialog(this); + depsDialog.exec(); +} + void GMainWindow::OnToggleFilterBar() { game_list->SetFilterVisible(ui->action_Show_Filter_Bar->isChecked()); if (ui->action_Show_Filter_Bar->isChecked()) { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 5e0405ee7f..c7dd2a45fa 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -397,6 +397,7 @@ private slots: void OnInstallFirmwareFromZIP(); void OnInstallDecryptionKeys(); void OnAbout(); + void OnEdenDependencies(); void OnToggleFilterBar(); void OnToggleStatusBar(); void OnGameListRefresh(); diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 25aaf6ad1c..ac8a2b594e 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -217,6 +217,7 @@ + @@ -575,6 +576,11 @@ + + + &Eden Dependencies + +