mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2025-07-20 12:55:45 +00:00
fix appimage shortcuts (#137)
Properly recognizes AppImage launches and deduplicates some code b/t home menu and game list shortcuts Signed-off-by: swurl <swurl@swurl.xyz> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/137 Co-authored-by: swurl <swurl@swurl.xyz> Co-committed-by: swurl <swurl@swurl.xyz>
This commit is contained in:
parent
e9e17b8fc2
commit
54bfb6e905
2 changed files with 129 additions and 164 deletions
|
@ -97,6 +97,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||||
|
|
||||||
#ifdef HAVE_SDL2
|
#ifdef HAVE_SDL2
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
|
#include <QStringLiteral>
|
||||||
#include <SDL.h> // For SDL ScreenSaver functions
|
#include <SDL.h> // For SDL ScreenSaver functions
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -3096,99 +3097,10 @@ bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_vi
|
||||||
|
|
||||||
void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
|
void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
|
||||||
GameListShortcutTarget target) {
|
GameListShortcutTarget target) {
|
||||||
// Get path to yuzu executable
|
|
||||||
const QStringList args = QApplication::arguments();
|
|
||||||
std::filesystem::path yuzu_command = args[0].toStdString();
|
|
||||||
// If relative path, make it an absolute path
|
|
||||||
if (yuzu_command.c_str()[0] == '.') {
|
|
||||||
yuzu_command = Common::FS::GetCurrentDir() / yuzu_command;
|
|
||||||
}
|
|
||||||
// Shortcut path
|
|
||||||
std::filesystem::path shortcut_path{};
|
|
||||||
if (target == GameListShortcutTarget::Desktop) {
|
|
||||||
shortcut_path =
|
|
||||||
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString();
|
|
||||||
} else if (target == GameListShortcutTarget::Applications) {
|
|
||||||
shortcut_path =
|
|
||||||
QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!std::filesystem::exists(shortcut_path)) {
|
|
||||||
GMainWindow::CreateShortcutMessagesGUI(
|
|
||||||
this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR,
|
|
||||||
QString::fromStdString(shortcut_path.generic_string()));
|
|
||||||
LOG_ERROR(Frontend, "Invalid shortcut target {}", shortcut_path.generic_string());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get title from game file
|
|
||||||
const FileSys::PatchManager pm{program_id, system->GetFileSystemController(),
|
|
||||||
system->GetContentProvider()};
|
|
||||||
const auto control = pm.GetControlMetadata();
|
|
||||||
const auto loader =
|
|
||||||
Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::OpenMode::Read));
|
|
||||||
std::string game_title = fmt::format("{:016X}", program_id);
|
|
||||||
if (control.first != nullptr) {
|
|
||||||
game_title = control.first->GetApplicationName();
|
|
||||||
} else {
|
|
||||||
loader->ReadTitle(game_title);
|
|
||||||
}
|
|
||||||
// Delete illegal characters from title
|
|
||||||
const std::string illegal_chars = "<>:\"/\\|?*.";
|
|
||||||
for (auto it = game_title.rbegin(); it != game_title.rend(); ++it) {
|
|
||||||
if (illegal_chars.find(*it) != std::string::npos) {
|
|
||||||
game_title.erase(it.base() - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const QString qt_game_title = QString::fromStdString(game_title);
|
|
||||||
// Get icon from game file
|
|
||||||
std::vector<u8> icon_image_file{};
|
|
||||||
if (control.second != nullptr) {
|
|
||||||
icon_image_file = control.second->ReadAllBytes();
|
|
||||||
} else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) {
|
|
||||||
LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path);
|
|
||||||
}
|
|
||||||
QImage icon_data =
|
|
||||||
QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size()));
|
|
||||||
std::filesystem::path out_icon_path;
|
|
||||||
if (GMainWindow::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) {
|
|
||||||
if (!SaveIconToFile(out_icon_path, icon_data)) {
|
|
||||||
LOG_ERROR(Frontend, "Could not write icon to file");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__linux__)
|
|
||||||
// Special case for AppImages
|
|
||||||
// Warn once if we are making a shortcut to a volatile AppImage
|
|
||||||
const std::string appimage_ending =
|
|
||||||
std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage");
|
|
||||||
if (yuzu_command.string().ends_with(appimage_ending) &&
|
|
||||||
!UISettings::values.shortcut_already_warned) {
|
|
||||||
if (GMainWindow::CreateShortcutMessagesGUI(
|
|
||||||
this, GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING, qt_game_title)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
UISettings::values.shortcut_already_warned = true;
|
|
||||||
}
|
|
||||||
#endif // __linux__
|
|
||||||
// Create shortcut
|
// Create shortcut
|
||||||
std::string arguments = fmt::format("-g \"{:s}\"", game_path);
|
std::string arguments = fmt::format("-g \"{:s}\"", game_path);
|
||||||
if (GMainWindow::CreateShortcutMessagesGUI(
|
|
||||||
this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, qt_game_title)) {
|
|
||||||
arguments = "-f " + arguments;
|
|
||||||
}
|
|
||||||
const std::string comment = fmt::format("Start {:s} with the eden Emulator", game_title);
|
|
||||||
const std::string categories = "Game;Emulator;Qt;";
|
|
||||||
const std::string keywords = "Switch;Nintendo;";
|
|
||||||
|
|
||||||
if (GMainWindow::CreateShortcutLink(shortcut_path, comment, out_icon_path, yuzu_command,
|
CreateShortcut(game_path, program_id, "", target, arguments, true);
|
||||||
arguments, categories, keywords, game_title)) {
|
|
||||||
GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS,
|
|
||||||
qt_game_title);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR,
|
|
||||||
qt_game_title);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
|
void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
|
||||||
|
@ -4628,16 +4540,28 @@ void GMainWindow::OnCreateHomeMenuApplicationMenuShortcut()
|
||||||
OnCreateHomeMenuShortcut(GameListShortcutTarget::Applications);
|
OnCreateHomeMenuShortcut(GameListShortcutTarget::Applications);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnCreateHomeMenuShortcut(GameListShortcutTarget target)
|
std::filesystem::path GMainWindow::GetEdenCommand()
|
||||||
{
|
{
|
||||||
// Get path to yuzu executable
|
std::filesystem::path command;
|
||||||
|
|
||||||
|
QString appimage = QString::fromLocal8Bit(getenv("APPIMAGE"));
|
||||||
|
if (!appimage.isEmpty()) {
|
||||||
|
command = std::filesystem::path{appimage.toStdString()};
|
||||||
|
} else {
|
||||||
const QStringList args = QApplication::arguments();
|
const QStringList args = QApplication::arguments();
|
||||||
std::filesystem::path yuzu_command = args[0].toStdString();
|
command = args[0].toStdString();
|
||||||
// If relative path, make it an absolute path
|
|
||||||
if (yuzu_command.c_str()[0] == '.') {
|
|
||||||
yuzu_command = Common::FS::GetCurrentDir() / yuzu_command;
|
|
||||||
}
|
}
|
||||||
// Shortcut path
|
|
||||||
|
// If relative path, make it an absolute path
|
||||||
|
if (command.c_str()[0] == '.') {
|
||||||
|
command = Common::FS::GetCurrentDir() / command;
|
||||||
|
}
|
||||||
|
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::path GMainWindow::GetShortcutPath(GameListShortcutTarget target)
|
||||||
|
{
|
||||||
std::filesystem::path shortcut_path{};
|
std::filesystem::path shortcut_path{};
|
||||||
if (target == GameListShortcutTarget::Desktop) {
|
if (target == GameListShortcutTarget::Desktop) {
|
||||||
shortcut_path =
|
shortcut_path =
|
||||||
|
@ -4647,6 +4571,16 @@ void GMainWindow::OnCreateHomeMenuShortcut(GameListShortcutTarget target)
|
||||||
QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString();
|
QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return shortcut_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GMainWindow::CreateShortcut(const std::string &game_path, const u64 program_id, const std::string& game_title_, GameListShortcutTarget target, std::string arguments_, const bool needs_title) {
|
||||||
|
// Get path to yuzu executable
|
||||||
|
std::filesystem::path command = GetEdenCommand();
|
||||||
|
|
||||||
|
// Shortcut path
|
||||||
|
std::filesystem::path shortcut_path = GetShortcutPath(target);
|
||||||
|
|
||||||
if (!std::filesystem::exists(shortcut_path)) {
|
if (!std::filesystem::exists(shortcut_path)) {
|
||||||
GMainWindow::CreateShortcutMessagesGUI(
|
GMainWindow::CreateShortcutMessagesGUI(
|
||||||
this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR,
|
this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR,
|
||||||
|
@ -4655,11 +4589,89 @@ void GMainWindow::OnCreateHomeMenuShortcut(GameListShortcutTarget target)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FileSys::PatchManager pm{program_id, system->GetFileSystemController(),
|
||||||
|
system->GetContentProvider()};
|
||||||
|
const auto control = pm.GetControlMetadata();
|
||||||
|
const auto loader =
|
||||||
|
Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::OpenMode::Read));
|
||||||
|
|
||||||
|
std::string game_title{game_title_};
|
||||||
|
|
||||||
|
// Delete illegal characters from title
|
||||||
|
if (needs_title) {
|
||||||
|
game_title = fmt::format("{:016X}", program_id);
|
||||||
|
if (control.first != nullptr) {
|
||||||
|
game_title = control.first->GetApplicationName();
|
||||||
|
} else {
|
||||||
|
loader->ReadTitle(game_title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string illegal_chars = "<>:\"/\\|?*.";
|
||||||
|
for (auto it = game_title.rbegin(); it != game_title.rend(); ++it) {
|
||||||
|
if (illegal_chars.find(*it) != std::string::npos) {
|
||||||
|
game_title.erase(it.base() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString qgame_title = QString::fromStdString(game_title);
|
||||||
|
|
||||||
|
// Get icon from game file
|
||||||
|
std::vector<u8> icon_image_file{};
|
||||||
|
if (control.second != nullptr) {
|
||||||
|
icon_image_file = control.second->ReadAllBytes();
|
||||||
|
} else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) {
|
||||||
|
LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage icon_data =
|
||||||
|
QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size()));
|
||||||
|
std::filesystem::path out_icon_path;
|
||||||
|
if (GMainWindow::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) {
|
||||||
|
if (!SaveIconToFile(out_icon_path, icon_data)) {
|
||||||
|
LOG_ERROR(Frontend, "Could not write icon to file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
// Special case for AppImages
|
||||||
|
// Warn once if we are making a shortcut to a volatile AppImage
|
||||||
|
if (command.string().ends_with(".AppImage") && !UISettings::values.shortcut_already_warned) {
|
||||||
|
if (!GMainWindow::CreateShortcutMessagesGUI(
|
||||||
|
this, GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING, qgame_title)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UISettings::values.shortcut_already_warned = true;
|
||||||
|
}
|
||||||
|
#endif // __linux__
|
||||||
|
|
||||||
|
// Create shortcut
|
||||||
|
std::string arguments{arguments_};
|
||||||
|
if (GMainWindow::CreateShortcutMessagesGUI(
|
||||||
|
this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, qgame_title)) {
|
||||||
|
arguments = "-f " + arguments;
|
||||||
|
}
|
||||||
|
const std::string comment = fmt::format("Start {:s} with the eden Emulator", game_title);
|
||||||
|
const std::string categories = "Game;Emulator;Qt;";
|
||||||
|
const std::string keywords = "Switch;Nintendo;";
|
||||||
|
|
||||||
|
if (GMainWindow::CreateShortcutLink(shortcut_path, comment, out_icon_path, command,
|
||||||
|
arguments, categories, keywords, game_title)) {
|
||||||
|
GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS,
|
||||||
|
qgame_title);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR,
|
||||||
|
qgame_title);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GMainWindow::OnCreateHomeMenuShortcut(GameListShortcutTarget target)
|
||||||
|
{
|
||||||
constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch);
|
constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch);
|
||||||
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
|
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
|
||||||
if (!bis_system) {
|
if (!bis_system) {
|
||||||
QMessageBox::warning(this, tr("No firmware available"),
|
QMessageBox::warning(this, tr("No firmware available"),
|
||||||
tr("Please install the firmware to use the home menu."));
|
tr("Please install firmware to use the home menu."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4670,67 +4682,10 @@ void GMainWindow::OnCreateHomeMenuShortcut(GameListShortcutTarget target)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string game_title = "QLaunch";
|
|
||||||
const QString qt_game_title = tr("QLaunch");
|
|
||||||
|
|
||||||
auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
|
auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
|
||||||
const auto game_path = QString::fromStdString((qlaunch_applet_nca->GetFullPath()));
|
const auto game_path = qlaunch_applet_nca->GetFullPath();
|
||||||
|
|
||||||
const FileSys::PatchManager pm{static_cast<u64>(Service::AM::AppletProgramId::QLaunch), system->GetFileSystemController(),
|
CreateShortcut(game_path, QLaunchId, "Switch Home Menu", target, "-qlaunch", false);
|
||||||
system->GetContentProvider()};
|
|
||||||
const auto control = pm.GetControlMetadata();
|
|
||||||
const auto loader =
|
|
||||||
Loader::GetLoader(*system, vfs->OpenFile(game_path.toStdString(), FileSys::OpenMode::Read));
|
|
||||||
|
|
||||||
// Get icon from game file
|
|
||||||
std::vector<u8> icon_image_file{};
|
|
||||||
if (control.second != nullptr) {
|
|
||||||
icon_image_file = control.second->ReadAllBytes();
|
|
||||||
} else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) {
|
|
||||||
LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path.toStdString());
|
|
||||||
}
|
|
||||||
QImage icon_data =
|
|
||||||
QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size()));
|
|
||||||
std::filesystem::path out_icon_path;
|
|
||||||
if (GMainWindow::MakeShortcutIcoPath(QLaunchId, game_title, out_icon_path)) {
|
|
||||||
if (!SaveIconToFile(out_icon_path, icon_data)) {
|
|
||||||
LOG_ERROR(Frontend, "Could not write icon to file");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__linux__)
|
|
||||||
// Special case for AppImages
|
|
||||||
// Warn once if we are making a shortcut to a volatile AppImage
|
|
||||||
const std::string appimage_ending =
|
|
||||||
std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage");
|
|
||||||
if (yuzu_command.string().ends_with(appimage_ending) &&
|
|
||||||
!UISettings::values.shortcut_already_warned) {
|
|
||||||
if (GMainWindow::CreateShortcutMessagesGUI(
|
|
||||||
this, GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING, qt_game_title)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
UISettings::values.shortcut_already_warned = true;
|
|
||||||
}
|
|
||||||
#endif // __linux__
|
|
||||||
|
|
||||||
// Create shortcut
|
|
||||||
std::string arguments = "-qlaunch";
|
|
||||||
if (GMainWindow::CreateShortcutMessagesGUI(
|
|
||||||
this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, qt_game_title)) {
|
|
||||||
arguments = "-f " + arguments;
|
|
||||||
}
|
|
||||||
const std::string comment = fmt::format("Start {:s} with the eden Emulator", game_title);
|
|
||||||
const std::string categories = "Game;Emulator;Qt;";
|
|
||||||
const std::string keywords = "Switch;Nintendo;";
|
|
||||||
|
|
||||||
if (GMainWindow::CreateShortcutLink(shortcut_path, comment, out_icon_path, yuzu_command,
|
|
||||||
arguments, categories, keywords, game_title)) {
|
|
||||||
GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS,
|
|
||||||
qt_game_title);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR,
|
|
||||||
qt_game_title);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnCaptureScreenshot() {
|
void GMainWindow::OnCaptureScreenshot() {
|
||||||
|
@ -5572,7 +5527,7 @@ int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
// Fix the Wayland appId. This needs to match the name of the .desktop file without the .desktop
|
// Fix the Wayland appId. This needs to match the name of the .desktop file without the .desktop
|
||||||
// suffix.
|
// suffix.
|
||||||
QGuiApplication::setDesktopFileName(QStringLiteral("org.eden_emu.eden"));
|
QGuiApplication::setDesktopFileName(QStringLiteral("eden"));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SetHighDPIAttributes();
|
SetHighDPIAttributes();
|
||||||
|
@ -5587,7 +5542,6 @@ int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
OverrideWindowsFont();
|
OverrideWindowsFont();
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -178,6 +178,8 @@ public:
|
||||||
bool DropAction(QDropEvent* event);
|
bool DropAction(QDropEvent* event);
|
||||||
void AcceptDropEvent(QDropEvent* event);
|
void AcceptDropEvent(QDropEvent* event);
|
||||||
|
|
||||||
|
std::filesystem::path GetShortcutPath(GameListShortcutTarget target);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -593,6 +595,15 @@ private:
|
||||||
static std::array<int, 3> sig_interrupt_fds;
|
static std::array<int, 3> sig_interrupt_fds;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
std::filesystem::path GetEdenCommand();
|
||||||
|
|
||||||
|
void CreateShortcut(const std::string& game_path,
|
||||||
|
const u64 program_id,
|
||||||
|
const std::string& game_title,
|
||||||
|
GameListShortcutTarget target,
|
||||||
|
std::string arguments,
|
||||||
|
const bool needs_title);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void dropEvent(QDropEvent* event) override;
|
void dropEvent(QDropEvent* event) override;
|
||||||
void dragEnterEvent(QDragEnterEvent* event) override;
|
void dragEnterEvent(QDragEnterEvent* event) override;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue