Automatically Update shadPS4 on OS Startup (#2700)

Created a separate .exe for updating. Added a checkbox in the settings
to enable the option to update on Windows startup. When the option is
enabled, a PowerShell script adds the new updater exe to the Windows
Task Scheduler and sets it to run at startup. If there are updates
available at startup, the update windows pops up. Disabling the option
runs another script to remove the updater from the Task Scheduler.

Co-authored-by: Joao Ribeiro <joao.viegas.ribeiro@tecnico.ulisboa.pt>
This commit is contained in:
David Antunes 2025-06-06 04:26:15 +01:00
parent 64bbedeb82
commit bbfb799802
9 changed files with 375 additions and 47 deletions

View File

@ -34,6 +34,7 @@ endif()
option(ENABLE_QT_GUI "Enable the Qt GUI. If not selected then the emulator uses a minimal SDL-based UI instead" OFF)
option(ENABLE_DISCORD_RPC "Enable the Discord RPC integration" ON)
option(ENABLE_UPDATER "Enables the options to updater" ON)
option(ENABLE_UPDATER_EXE "Enables the new executable for updates" ON)
# First, determine whether to use CMAKE_OSX_ARCHITECTURES or CMAKE_SYSTEM_PROCESSOR.
if (APPLE AND CMAKE_OSX_ARCHITECTURES)
@ -1074,6 +1075,26 @@ if (ENABLE_QT_GUI)
${EMULATOR}
src/images/shadPS4.icns
)
if (ENABLE_UPDATER_EXE)
qt_add_executable(shadps4_updater
${AUDIO_CORE}
${IMGUI}
${INPUT}
${COMMON}
${CORE}
${SHADER_RECOMPILER}
${VIDEO_CORE}
${EMULATOR}
${UPDATER}
${RESOURCE_FILES}
src/qt_gui/main_window_themes.cpp
src/qt_gui/main_window_themes.h
src/qt_gui/background_music_player.cpp
src/qt_gui/background_music_player.h
src/updater/main.cpp
src/images/shadPS4.icns
)
endif()
else()
add_executable(shadps4
${AUDIO_CORE}
@ -1097,20 +1118,55 @@ create_target_directory_groups(shadps4)
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG)
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers libusb::usb)
if (ENABLE_UPDATER_EXE)
create_target_directory_groups(shadps4_updater)
target_link_libraries(shadps4_updater PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG)
target_link_libraries(shadps4_updater PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers libusb::usb)
endif()
#Hide console for shadps4_updater
if (ENABLE_UPDATER_EXE)
if (WIN32)
set_target_properties(shadps4_updater PROPERTIES WIN32_EXECUTABLE TRUE)
endif()
endif()
if(WIN32)
target_link_libraries(shadps4 PRIVATE shell32)
if (ENABLE_UPDATER_EXE)
target_link_libraries(shadps4_updater PRIVATE shell32)
endif()
endif()
target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h")
target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h")
if (ENABLE_UPDATER_EXE)
target_compile_definitions(shadps4_updater PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h")
endif()
if (ENABLE_DISCORD_RPC)
target_compile_definitions(shadps4 PRIVATE ENABLE_DISCORD_RPC)
if (ENABLE_UPDATER_EXE)
target_compile_definitions(shadps4_updater PRIVATE ENABLE_DISCORD_RPC)
endif()
endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
# Optional due to https://github.com/shadps4-emu/shadPS4/issues/1704
if (ENABLE_USERFAULTFD)
target_compile_definitions(shadps4 PRIVATE ENABLE_USERFAULTFD)
if (ENABLE_UPDATER_EXE)
target_compile_definitions(shadps4_updater PRIVATE ENABLE_USERFAULTFD)
endif()
endif()
target_link_libraries(shadps4 PRIVATE uuid)
if (ENABLE_UPDATER_EXE)
target_link_libraries(shadps4_updater PRIVATE uuid)
endif()
endif()
if (APPLE)
@ -1118,9 +1174,15 @@ if (APPLE)
if (ENABLE_QT_GUI)
set(MVK_BUNDLE_PATH "Resources/vulkan/icd.d")
set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path/../${MVK_BUNDLE_PATH}")
if (ENABLE_UPDATER_EXE)
set_property(TARGET shadps4_updater APPEND PROPERTY BUILD_RPATH "@executable_path/../${MVK_BUNDLE_PATH}")
endif()
set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR}/shadps4.app/Contents/${MVK_BUNDLE_PATH})
else()
set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path")
if (ENABLE_UPDATER_EXE)
set_property(TARGET shadps4_updater APPEND PROPERTY BUILD_RPATH "@executable_path")
endif()
set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR})
endif()
@ -1143,14 +1205,23 @@ if (APPLE)
add_custom_target(CopyMoltenVK DEPENDS ${MVK_ICD_DST} ${MVK_DYLIB_DST})
add_dependencies(CopyMoltenVK MoltenVK)
add_dependencies(shadps4 CopyMoltenVK)
if (ENABLE_UPDATER_EXE)
add_dependencies(shadps4_updater CopyMoltenVK)
endif()
if (ARCHITECTURE STREQUAL "x86_64")
# Reserve system-managed memory space.
target_link_options(shadps4 PRIVATE -Wl,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,SYSTEM_MANAGED,0x400000,-segaddr,SYSTEM_RESERVED,0x7FFFFC000,-image_base,0x20000000000)
if (ENABLE_UPDATER_EXE)
target_link_options(shadps4_updater PRIVATE -Wl,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,SYSTEM_MANAGED,0x400000,-segaddr,SYSTEM_RESERVED,0x7FFFFC000,-image_base,0x20000000000)
endif()
endif()
# Replacement for std::chrono::time_zone
target_link_libraries(shadps4 PRIVATE date::date-tz)
if (ENABLE_UPDATER_EXE)
target_link_libraries(shadps4_updater PRIVATE date::date-tz)
endif()
endif()
if (NOT ENABLE_QT_GUI)
@ -1159,6 +1230,9 @@ endif()
if (ENABLE_QT_GUI)
target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network Qt6::Multimedia)
if (ENABLE_UPDATER_EXE)
target_link_libraries(shadps4_updater PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network Qt6::Multimedia)
endif()
add_definitions(-DENABLE_QT_GUI)
if (ENABLE_UPDATER)
add_definitions(-DENABLE_UPDATER)
@ -1167,6 +1241,9 @@ endif()
if (WIN32)
target_link_libraries(shadps4 PRIVATE mincore)
if (ENABLE_UPDATER_EXE)
target_link_libraries(shadps4_updater PRIVATE mincore)
endif()
if (MSVC)
# MSVC likes putting opinions on what people can use, disable:
@ -1185,37 +1262,64 @@ if (WIN32)
if (MSVC)
target_link_libraries(shadps4 PRIVATE clang_rt.builtins-x86_64.lib)
if (ENABLE_UPDATER_EXE)
target_link_libraries(shadps4_updater PRIVATE clang_rt.builtins-x86_64.lib)
endif()
endif()
# Disable ASLR so we can reserve the user area
if (MSVC)
target_link_options(shadps4 PRIVATE /DYNAMICBASE:NO)
if (ENABLE_UPDATER_EXE)
target_link_options(shadps4_updater PRIVATE /DYNAMICBASE:NO)
endif()
else()
target_link_options(shadps4 PRIVATE -Wl,--disable-dynamicbase)
if (ENABLE_UPDATER_EXE)
target_link_options(shadps4_updater PRIVATE -Wl,--disable-dynamicbase)
endif()
endif()
# Increase stack commit area (Needed, otherwise there are crashes)
if (MSVC)
target_link_options(shadps4 PRIVATE /STACK:0x200000,0x200000)
if (ENABLE_UPDATER_EXE)
target_link_options(shadps4_updater PRIVATE /STACK:0x200000,0x200000)
endif()
else()
target_link_options(shadps4 PRIVATE -Wl,--stack,2097152)
if (ENABLE_UPDATER_EXE)
target_link_options(shadps4_updater PRIVATE -Wl,--stack,2097152)
endif()
endif()
endif()
if (WIN32)
target_sources(shadps4 PRIVATE src/shadps4.rc)
if (ENABLE_UPDATER_EXE)
target_sources(shadps4_updater PRIVATE src/shadps4.rc)
endif()
endif()
add_definitions(-DBOOST_ASIO_STANDALONE)
target_include_directories(shadps4 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
if (ENABLE_UPDATER_EXE)
target_include_directories(shadps4_updater PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
endif()
# Shaders sources
set(HOST_SHADERS_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/src/video_core/host_shaders)
add_subdirectory(${HOST_SHADERS_INCLUDE})
add_dependencies(shadps4 host_shaders)
if (ENABLE_UPDATER_EXE)
add_dependencies(shadps4_updater host_shaders)
endif()
target_include_directories(shadps4 PRIVATE ${HOST_SHADERS_INCLUDE})
if (ENABLE_UPDATER_EXE)
target_include_directories(shadps4_updater PRIVATE ${HOST_SHADERS_INCLUDE})
endif()
# embed resources
@ -1228,11 +1332,20 @@ cmrc_add_resource_library(embedded-resources
src/images/platinum.png
src/images/silver.png)
target_link_libraries(shadps4 PRIVATE res::embedded)
if (ENABLE_UPDATER_EXE)
target_link_libraries(shadps4_updater PRIVATE res::embedded)
endif()
# ImGui resources
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/imgui/renderer)
add_dependencies(shadps4 ImGui_Resources)
if (ENABLE_UPDATER_EXE)
add_dependencies(shadps4_updater ImGui_Resources)
endif()
target_include_directories(shadps4 PRIVATE ${IMGUI_RESOURCES_INCLUDE})
if (ENABLE_UPDATER_EXE)
target_include_directories(shadps4_updater PRIVATE ${IMGUI_RESOURCES_INCLUDE})
endif()
if (ENABLE_QT_GUI)
set_target_properties(shadps4 PROPERTIES
@ -1242,7 +1355,15 @@ if (ENABLE_QT_GUI)
MACOSX_BUNDLE_ICON_FILE "shadPS4.icns"
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APP_VERSION}"
)
if (ENABLE_UPDATER_EXE)
set_target_properties(shadps4 PROPERTIES
# WIN32_EXECUTABLE ON
MACOSX_BUNDLE ON
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/dist/MacOSBundleInfo.plist.in"
MACOSX_BUNDLE_ICON_FILE "shadPS4.icns"
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APP_VERSION}"
)
endif()
set_source_files_properties(src/images/shadPS4.icns PROPERTIES
MACOSX_PACKAGE_LOCATION Resources)
endif()
@ -1251,12 +1372,18 @@ if (UNIX AND NOT APPLE)
if (ENABLE_QT_GUI)
find_package(OpenSSL REQUIRED)
target_link_libraries(shadps4 PRIVATE ${OPENSSL_LIBRARIES})
if (ENABLE_UPDATER_EXE)
target_link_libraries(shadps4_updater PRIVATE ${OPENSSL_LIBRARIES})
endif()
endif()
endif()
# Discord RPC
if (ENABLE_DISCORD_RPC)
target_link_libraries(shadps4 PRIVATE discord-rpc)
if (ENABLE_UPDATER_EXE)
target_link_libraries(shadps4_updater PRIVATE discord-rpc)
endif()
endif()
# Install rules

View File

@ -53,6 +53,7 @@ static bool isDebugDump = false;
static bool isShaderDebug = false;
static bool isShowSplash = false;
static bool isAutoUpdate = false;
static bool isStartupUpdate = false;
static bool isAlwaysShowChangelog = false;
static std::string isSideTrophy = "right";
static bool isNullGpu = false;
@ -280,6 +281,10 @@ bool autoUpdate() {
return isAutoUpdate;
}
bool startupUpdate() {
return isStartupUpdate;
}
bool alwaysShowChangelog() {
return isAlwaysShowChangelog;
}
@ -388,6 +393,10 @@ void setAutoUpdate(bool enable) {
isAutoUpdate = enable;
}
void setStartupUpdate(bool enable) {
isStartupUpdate = enable;
}
void setAlwaysShowChangelog(bool enable) {
isAlwaysShowChangelog = enable;
}
@ -780,6 +789,7 @@ void load(const std::filesystem::path& path) {
}
isShowSplash = toml::find_or<bool>(general, "showSplash", true);
isAutoUpdate = toml::find_or<bool>(general, "autoUpdate", false);
isStartupUpdate = toml::find_or<bool>(general, "startupUpdate", false);
isAlwaysShowChangelog = toml::find_or<bool>(general, "alwaysShowChangelog", false);
isSideTrophy = toml::find_or<std::string>(general, "sideTrophy", "right");
compatibilityData = toml::find_or<bool>(general, "compatibilityEnabled", false);
@ -976,6 +986,7 @@ void save(const std::filesystem::path& path) {
data["General"]["chooseHomeTab"] = chooseHomeTab;
data["General"]["showSplash"] = isShowSplash;
data["General"]["autoUpdate"] = isAutoUpdate;
data["General"]["startupUpdate"] = isStartupUpdate;
data["General"]["alwaysShowChangelog"] = isAlwaysShowChangelog;
data["General"]["sideTrophy"] = isSideTrophy;
data["General"]["compatibilityEnabled"] = compatibilityData;
@ -1136,6 +1147,7 @@ void setDefaultValues() {
isShaderDebug = false;
isShowSplash = false;
isAutoUpdate = false;
isStartupUpdate = false;
isAlwaysShowChangelog = false;
isSideTrophy = "right";
isNullGpu = false;

View File

@ -70,6 +70,7 @@ bool debugDump();
bool collectShadersForDebug();
bool showSplash();
bool autoUpdate();
bool startupUpdate();
bool alwaysShowChangelog();
std::string sideTrophy();
bool nullGpu();
@ -84,6 +85,7 @@ void setDebugDump(bool enable);
void setCollectShaderForDebug(bool enable);
void setShowSplash(bool enable);
void setAutoUpdate(bool enable);
void setStartupUpdate(bool enable);
void setAlwaysShowChangelog(bool enable);
void setSideTrophy(std::string side);
void setNullGpu(bool enable);

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <filesystem>
#include <QCoreApplication>
#include <QDateTime>
#include <QDir>
#include <QFile>
@ -444,8 +445,7 @@ void CheckUpdate::Install() {
QString userPath;
Common::FS::PathToQString(userPath, Common::FS::GetUserPath(Common::FS::PathType::UserDir));
QString rootPath;
Common::FS::PathToQString(rootPath, std::filesystem::current_path());
QString rootPath = QCoreApplication::applicationDirPath();
QString tempDirPath = userPath + "/temp_download_update";
QString startingUpdate = tr("Starting Update...");

View File

@ -9,12 +9,14 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
qApp->setStyleSheet("");
switch (theme) {
case Theme::Dark:
mw_searchbar->setStyleSheet(
"QLineEdit {"
"background-color: #1e1e1e; color: #ffffff; border: 1px solid #ffffff; "
"border-radius: 4px; padding: 5px; }"
"QLineEdit:focus {"
"border: 1px solid #2A82DA; }");
if (mw_searchbar) {
mw_searchbar->setStyleSheet(
"QLineEdit {"
"background-color: #1e1e1e; color: #ffffff; border: 1px solid #ffffff; "
"border-radius: 4px; padding: 5px; }"
"QLineEdit:focus {"
"border: 1px solid #2A82DA; }");
}
themePalette.setColor(QPalette::Window, QColor(50, 50, 50));
themePalette.setColor(QPalette::WindowText, Qt::white);
themePalette.setColor(QPalette::Base, QColor(20, 20, 20));
@ -31,12 +33,14 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
qApp->setPalette(themePalette);
break;
case Theme::Light:
mw_searchbar->setStyleSheet(
"QLineEdit {"
"background-color: #ffffff; color: #000000; border: 1px solid #000000; "
"border-radius: 4px; padding: 5px; }"
"QLineEdit:focus {"
"border: 1px solid #2A82DA; }");
if (mw_searchbar) {
mw_searchbar->setStyleSheet(
"QLineEdit {"
"background-color: #ffffff; color: #000000; border: 1px solid #000000; "
"border-radius: 4px; padding: 5px; }"
"QLineEdit:focus {"
"border: 1px solid #2A82DA; }");
}
themePalette.setColor(QPalette::Window, QColor(240, 240, 240)); // Light gray
themePalette.setColor(QPalette::WindowText, Qt::black); // Black
themePalette.setColor(QPalette::Base, QColor(230, 230, 230, 80)); // Grayish
@ -52,12 +56,14 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
qApp->setPalette(themePalette);
break;
case Theme::Green:
mw_searchbar->setStyleSheet(
"QLineEdit {"
"background-color: #192819; color: #ffffff; border: 1px solid #ffffff; "
"border-radius: 4px; padding: 5px; }"
"QLineEdit:focus {"
"border: 1px solid #2A82DA; }");
if (mw_searchbar) {
mw_searchbar->setStyleSheet(
"QLineEdit {"
"background-color: #192819; color: #ffffff; border: 1px solid #ffffff; "
"border-radius: 4px; padding: 5px; }"
"QLineEdit:focus {"
"border: 1px solid #2A82DA; }");
}
themePalette.setColor(QPalette::Window, QColor(53, 69, 53)); // Dark green background
themePalette.setColor(QPalette::WindowText, Qt::white); // White text
themePalette.setColor(QPalette::Base, QColor(25, 40, 25)); // Darker green base
@ -76,12 +82,14 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
qApp->setPalette(themePalette);
break;
case Theme::Blue:
mw_searchbar->setStyleSheet(
"QLineEdit {"
"background-color: #14283c; color: #ffffff; border: 1px solid #ffffff; "
"border-radius: 4px; padding: 5px; }"
"QLineEdit:focus {"
"border: 1px solid #2A82DA; }");
if (mw_searchbar) {
mw_searchbar->setStyleSheet(
"QLineEdit {"
"background-color: #14283c; color: #ffffff; border: 1px solid #ffffff; "
"border-radius: 4px; padding: 5px; }"
"QLineEdit:focus {"
"border: 1px solid #2A82DA; }");
}
themePalette.setColor(QPalette::Window, QColor(40, 60, 90)); // Dark blue background
themePalette.setColor(QPalette::WindowText, Qt::white); // White text
themePalette.setColor(QPalette::Base, QColor(20, 40, 60)); // Darker blue base
@ -101,12 +109,14 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
qApp->setPalette(themePalette);
break;
case Theme::Violet:
mw_searchbar->setStyleSheet(
"QLineEdit {"
"background-color: #501e5a; color: #ffffff; border: 1px solid #ffffff; "
"border-radius: 4px; padding: 5px; }"
"QLineEdit:focus {"
"border: 1px solid #2A82DA; }");
if (mw_searchbar) {
mw_searchbar->setStyleSheet(
"QLineEdit {"
"background-color: #501e5a; color: #ffffff; border: 1px solid #ffffff; "
"border-radius: 4px; padding: 5px; }"
"QLineEdit:focus {"
"border: 1px solid #2A82DA; }");
}
themePalette.setColor(QPalette::Window, QColor(100, 50, 120)); // Violet background
themePalette.setColor(QPalette::WindowText, Qt::white); // White text
themePalette.setColor(QPalette::Base, QColor(80, 30, 90)); // Darker violet base
@ -126,12 +136,14 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
qApp->setPalette(themePalette);
break;
case Theme::Gruvbox:
mw_searchbar->setStyleSheet(
"QLineEdit {"
"background-color: #1d2021; color: #f9f5d7; border: 1px solid #f9f5d7; "
"border-radius: 4px; padding: 5px; }"
"QLineEdit:focus {"
"border: 1px solid #83A598; }");
if (mw_searchbar) {
mw_searchbar->setStyleSheet(
"QLineEdit {"
"background-color: #1d2021; color: #f9f5d7; border: 1px solid #f9f5d7; "
"border-radius: 4px; padding: 5px; }"
"QLineEdit:focus {"
"border: 1px solid #83A598; }");
}
themePalette.setColor(QPalette::Window, QColor(29, 32, 33));
themePalette.setColor(QPalette::WindowText, QColor(249, 245, 215));
themePalette.setColor(QPalette::Base, QColor(29, 32, 33));
@ -148,12 +160,14 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
qApp->setPalette(themePalette);
break;
case Theme::TokyoNight:
mw_searchbar->setStyleSheet(
"QLineEdit {"
"background-color: #1a1b26; color: #9d7cd8; border: 1px solid #9d7cd8; "
"border-radius: 4px; padding: 5px; }"
"QLineEdit:focus {"
"border: 1px solid #7aa2f7; }");
if (mw_searchbar) {
mw_searchbar->setStyleSheet(
"QLineEdit {"
"background-color: #1a1b26; color: #9d7cd8; border: 1px solid #9d7cd8; "
"border-radius: 4px; padding: 5px; }"
"QLineEdit:focus {"
"border: 1px solid #7aa2f7; }");
}
themePalette.setColor(QPalette::Window, QColor(31, 35, 53));
themePalette.setColor(QPalette::WindowText, QColor(192, 202, 245));
themePalette.setColor(QPalette::Base, QColor(25, 28, 39));
@ -170,8 +184,10 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
qApp->setPalette(themePalette);
break;
case Theme::Oled:
mw_searchbar->setStyleSheet("QLineEdit:focus {"
"border: 1px solid #2A82DA; }");
if (mw_searchbar) {
mw_searchbar->setStyleSheet("QLineEdit:focus {"
"border: 1px solid #2A82DA; }");
}
themePalette.setColor(QPalette::Window, Qt::black);
themePalette.setColor(QPalette::WindowText, Qt::white);
themePalette.setColor(QPalette::Base, Qt::black);

View File

@ -3,11 +3,21 @@
#include <vector>
#include <QCompleter>
#include <QCoreApplication>
#include <QDirIterator>
#include <QFile>
#include <QFileDialog>
#include <QHoverEvent>
#include <QMessageBox>
#include <QProcess>
#include <QStandardPaths>
#include <QTemporaryFile>
#include <QTextStream>
#include <QUuid>
#include <fmt/format.h>
#include <windows.h>
#include <shellapi.h>
#include "common/config.h"
#include "common/scm_rev.h"
@ -178,12 +188,29 @@ SettingsDialog::SettingsDialog(std::shared_ptr<CompatibilityInfoClass> m_compat_
connect(ui->updateCheckBox, &QCheckBox::stateChanged, this,
[](int state) { Config::setAutoUpdate(state == Qt::Checked); });
connect(ui->startupUpdateCheckBox, &QCheckBox::stateChanged, this, [this](int state) {
Config::setStartupUpdate(state == Qt::Checked);
if (state == Qt::Checked)
AddUpdaterToStartup();
else
RemoveUpdaterFromStartup();
});
connect(ui->changelogCheckBox, &QCheckBox::stateChanged, this,
[](int state) { Config::setAlwaysShowChangelog(state == Qt::Checked); });
#else
connect(ui->updateCheckBox, &QCheckBox::checkStateChanged, this,
[](Qt::CheckState state) { Config::setAutoUpdate(state == Qt::Checked); });
connect(ui->startupUpdateCheckBox, &QCheckBox::checkStateChanged, this,
[this](Qt::CheckState state) {
Config::setStartupUpdate(state == Qt::Checked);
if (state == Qt::Checked)
AddUpdaterToStartup();
else
RemoveUpdaterFromStartup();
});
connect(ui->changelogCheckBox, &QCheckBox::checkStateChanged, this,
[](Qt::CheckState state) { Config::setAlwaysShowChangelog(state == Qt::Checked); });
#endif
@ -502,6 +529,8 @@ void SettingsDialog::LoadValuesFromConfig() {
#ifdef ENABLE_UPDATER
ui->updateCheckBox->setChecked(toml::find_or<bool>(data, "General", "autoUpdate", false));
ui->startupUpdateCheckBox->setChecked(
toml::find_or<bool>(data, "General", "startupUpdate", false));
ui->changelogCheckBox->setChecked(
toml::find_or<bool>(data, "General", "alwaysShowChangelog", false));
@ -785,6 +814,7 @@ void SettingsDialog::UpdateSettings() {
Config::setCollectShaderForDebug(ui->collectShaderCheckBox->isChecked());
Config::setCopyGPUCmdBuffers(ui->copyGPUBuffersCheckBox->isChecked());
Config::setAutoUpdate(ui->updateCheckBox->isChecked());
Config::setStartupUpdate(ui->startupUpdateCheckBox->isChecked());
Config::setAlwaysShowChangelog(ui->changelogCheckBox->isChecked());
Config::setUpdateChannel(channelMap.value(ui->updateComboBox->currentText()).toStdString());
Config::setChooseHomeTab(
@ -862,3 +892,89 @@ void SettingsDialog::ResetInstallFolders() {
Config::setAllGameInstallDirs(settings_install_dirs_config);
}
}
void SettingsDialog::AddUpdaterToStartup() {
#ifdef _WIN32
QString tempDirPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) +
"/Temp/temp_download_update";
QDir().mkpath(tempDirPath);
QString scriptFileName =
tempDirPath + "/create_task_" + QUuid::createUuid().toString(QUuid::WithoutBraces) + ".ps1";
QString taskName = "ShadPS4Updater";
QString exePath = QCoreApplication::applicationFilePath()
.replace("shadps4.exe", "shadps4_updater.exe")
.replace("/", "\\");
QString scriptContent =
QStringLiteral("$Action = New-ScheduledTaskAction -Execute '%1'\n"
"$Trigger = New-ScheduledTaskTrigger -AtLogOn\n"
"$Principal = New-ScheduledTaskPrincipal -UserId \"$env:USERNAME\" "
"-LogonType Interactive -RunLevel Highest\n"
"$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries "
"-DontStopIfGoingOnBatteries\n"
"Register-ScheduledTask -TaskName '%2' -Action $Action -Trigger $Trigger "
"-Principal $Principal -Settings $Settings -Force\n")
.arg(exePath, taskName);
QFile scriptFile(scriptFileName);
if (scriptFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream out(&scriptFile);
out << scriptContent;
scriptFile.close();
// Elevate PowerShell to run the script
ShellExecuteW(
nullptr,
L"runas", // Triggers UAC prompt
L"powershell.exe",
(L"-ExecutionPolicy Bypass -File \"" + scriptFileName.toStdWString() + L"\"").c_str(),
nullptr, SW_HIDE);
}
#endif
}
void SettingsDialog::RemoveUpdaterFromStartup() {
#ifdef _WIN32
QString tempDirPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) +
"/Temp/temp_download_update";
QDir().mkpath(tempDirPath);
QString scriptFileName =
tempDirPath + "/remove_task_" + QUuid::createUuid().toString(QUuid::WithoutBraces) + ".ps1";
QString taskName = "ShadPS4Updater";
QString scriptContent =
QStringLiteral(
"$taskName = '%1'\n"
"if (Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue) {\n"
" Unregister-ScheduledTask -TaskName $taskName -Confirm:$false\n"
"}\n"
"Remove-Item -LiteralPath $MyInvocation.MyCommand.Path -Force\n" // Optional:
// self-delete the
// script
)
.arg(taskName);
QFile scriptFile(scriptFileName);
if (scriptFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream out(&scriptFile);
out << scriptContent;
scriptFile.close();
// Run PowerShell script with admin privileges
ShellExecuteW(
nullptr, L"runas", L"powershell.exe",
(L"-ExecutionPolicy Bypass -File \"" + scriptFileName.toStdWString() + L"\"").c_str(),
nullptr, SW_HIDE);
}
#endif
}

View File

@ -42,6 +42,8 @@ private:
void OnLanguageChanged(int index);
void OnCursorStateChanged(s16 index);
void closeEvent(QCloseEvent* event) override;
void AddUpdaterToStartup();
void RemoveUpdaterFromStartup();
std::unique_ptr<Ui::SettingsDialog> ui;

View File

@ -415,6 +415,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="startupUpdateCheckBox">
<property name="text">
<string>Check for Updates at OS Startup</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="changelogCheckBox">
<property name="text">

46
src/updater/main.cpp Normal file
View File

@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <QApplication>
#include "common/config.h"
#include "common/path_util.h"
#include "core/file_sys/fs.h"
#include "qt_gui/check_update.h"
#include "qt_gui/main_window_themes.h"
#ifdef _WIN32
#include <windows.h>
#endif
// Custom message handler to ignore Qt logs
void customMessageHandler(QtMsgType, const QMessageLogContext&, const QString&) {}
int main(int argc, char* argv[]) {
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8);
#endif
QApplication u(argc, argv);
QApplication::setDesktopFileName("net.shadps4.shadPS4_updater");
QApplication::setStyle("Fusion");
// Load configurations
const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
Config::load(user_dir / "config.toml");
WindowThemes m_window_themes;
Theme lastTheme = static_cast<Theme>(Config::getMainWindowTheme());
m_window_themes.SetWindowTheme(lastTheme, nullptr);
if (Config::startupUpdate()) {
auto checkUpdate = new CheckUpdate(false);
checkUpdate->exec();
}
// Ignore Qt logs
qInstallMessageHandler(customMessageHandler);
return 0;
}