mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-02-06 18:40:36 +00:00
Qt: create rpcs3 shortcuts
This commit is contained in:
parent
e5bb0ba004
commit
0a34403ef8
@ -16,6 +16,8 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
LOG_CHANNEL(sys_log, "SYS");
|
||||
@ -113,6 +115,31 @@ namespace rpcs3::utils
|
||||
const usz last = path_to_exe.find_last_of('\\');
|
||||
return last == std::string::npos ? std::string("") : path_to_exe.substr(0, last + 1);
|
||||
}
|
||||
#elif !defined(__APPLE__)
|
||||
std::string get_executable_path()
|
||||
{
|
||||
if (const char* appimage_path = ::getenv("APPIMAGE"))
|
||||
{
|
||||
sys_log.notice("Found AppImage path: %s", appimage_path);
|
||||
return std::string(appimage_path);
|
||||
}
|
||||
|
||||
sys_log.warning("Failed to find AppImage path");
|
||||
|
||||
char exe_path[PATH_MAX];
|
||||
const ssize_t len = ::readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1);
|
||||
|
||||
if (len == -1)
|
||||
{
|
||||
sys_log.error("Failed to find executable path");
|
||||
return {};
|
||||
}
|
||||
|
||||
exe_path[len] = '\0';
|
||||
sys_log.trace("Found exec path: %s", exe_path);
|
||||
|
||||
return std::string(exe_path);
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string get_emu_dir()
|
||||
|
@ -15,6 +15,8 @@ namespace rpcs3::utils
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string get_exe_dir();
|
||||
#elif !defined(__APPLE__)
|
||||
std::string get_executable_path();
|
||||
#endif
|
||||
|
||||
std::string get_emu_dir();
|
||||
|
@ -616,6 +616,7 @@
|
||||
<ClCompile Include="rpcs3qt\screenshot_preview.cpp" />
|
||||
<ClCompile Include="rpcs3qt\sendmessage_dialog_frame.cpp" />
|
||||
<ClCompile Include="rpcs3qt\settings.cpp" />
|
||||
<ClCompile Include="rpcs3qt\shortcut_utils.cpp" />
|
||||
<ClCompile Include="rpcs3qt\skylander_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\tooltips.cpp" />
|
||||
<ClCompile Include="rpcs3qt\update_manager.cpp" />
|
||||
@ -1137,6 +1138,7 @@
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="rpcs3qt\shortcut_utils.h" />
|
||||
<ClInclude Include="rpcs3qt\stylesheets.h" />
|
||||
<CustomBuild Include="rpcs3qt\skylander_dialog.h">
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
@ -1517,4 +1519,4 @@
|
||||
<UserProperties MocDir=".\QTGeneratedFiles\$(ConfigurationName)" Qt5Version_x0020_x64="$(DefaultQtVersion)" RccDir=".\QTGeneratedFiles" UicDir=".\QTGeneratedFiles" />
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
||||
</Project>
|
@ -804,6 +804,9 @@
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_camera_settings_dialog.cpp">
|
||||
<Filter>Generated Files\Release</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\shortcut_utils.cpp">
|
||||
<Filter>Gui\utils</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Input\ds4_pad_handler.h">
|
||||
@ -947,6 +950,9 @@
|
||||
<ClInclude Include="QTGeneratedFiles\ui_camera_settings_dialog.h">
|
||||
<Filter>Generated Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rpcs3qt\shortcut_utils.h">
|
||||
<Filter>Gui\utils</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="debug\moc_predefs.h.cbt">
|
||||
|
@ -68,6 +68,7 @@ set(SRC_FILES
|
||||
sendmessage_dialog_frame.cpp
|
||||
settings.cpp
|
||||
settings_dialog.cpp
|
||||
shortcut_utils.cpp
|
||||
skylander_dialog.cpp
|
||||
syntax_highlighter.cpp
|
||||
tooltips.cpp
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "game_list_frame.h"
|
||||
#include "qt_utils.h"
|
||||
#include "shortcut_utils.h"
|
||||
#include "settings_dialog.h"
|
||||
#include "pad_settings_dialog.h"
|
||||
#include "table_item_delegate.h"
|
||||
@ -980,6 +981,34 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
|
||||
: tr("&Create Custom Gamepad Configuration"));
|
||||
QAction* configure_patches = menu.addAction(tr("&Manage Game Patches"));
|
||||
QAction* create_ppu_cache = menu.addAction(tr("&Create PPU Cache"));
|
||||
#ifndef __APPLE__
|
||||
menu.addSeparator();
|
||||
const auto on_shortcut = [this, gameinfo](bool is_desktop_shortcut)
|
||||
{
|
||||
const std::string target_cli_args = fmt::format("--no-gui \"%s\"", gameinfo->info.path);
|
||||
const std::string target_icon_dir = fmt::format("%sIcons/game_icons/%s/", fs::get_config_dir(), gameinfo->info.serial);
|
||||
|
||||
if (gui::utils::create_shortcut(gameinfo->info.name, target_cli_args, gameinfo->info.name, gameinfo->info.icon_path, target_icon_dir, is_desktop_shortcut))
|
||||
{
|
||||
game_list_log.success("Created %s shortcut for %s", is_desktop_shortcut ? "desktop" : "application menu", sstr(qstr(gameinfo->info.name).simplified()));
|
||||
QMessageBox::information(this, tr("Success!"), tr("Successfully created a shortcut."));
|
||||
}
|
||||
else
|
||||
{
|
||||
game_list_log.error("Failed to create %s shortcut for %s", is_desktop_shortcut ? "desktop" : "application menu", sstr(qstr(gameinfo->info.name).simplified()));
|
||||
QMessageBox::warning(this, tr("Warning!"), tr("Failed to create a shortcut!"));
|
||||
}
|
||||
};
|
||||
QMenu* shortcut_menu = menu.addMenu(tr("&Create Shortcut"));
|
||||
QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("&Create Desktop Shortcut"));
|
||||
connect(create_desktop_shortcut, &QAction::triggered, this, [this, gameinfo, on_shortcut](){ on_shortcut(true); });
|
||||
#ifdef _WIN32
|
||||
QAction* create_start_menu_shortcut = shortcut_menu->addAction(tr("&Create Start Menu Shortcut"));
|
||||
#else
|
||||
QAction* create_start_menu_shortcut = shortcut_menu->addAction(tr("&Create Application Menu Shortcut"));
|
||||
#endif
|
||||
connect(create_start_menu_shortcut, &QAction::triggered, this, [this, gameinfo, on_shortcut](){ on_shortcut(false); });
|
||||
#endif
|
||||
menu.addSeparator();
|
||||
QAction* rename_title = menu.addAction(tr("&Rename In Game List"));
|
||||
QAction* hide_serial = menu.addAction(tr("&Hide From Game List"));
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "Emu/system_utils.hpp"
|
||||
#include "Utilities/File.h"
|
||||
#include <cmath>
|
||||
|
||||
inline std::string sstr(const QString& _in) { return _in.toStdString(); }
|
||||
constexpr auto qstr = QString::fromStdString;
|
||||
@ -40,6 +41,60 @@ namespace gui
|
||||
return QRect(frame_x, frame_y, target_width, target_height);
|
||||
}
|
||||
|
||||
bool create_square_pixmap(QPixmap& pixmap, int target_size)
|
||||
{
|
||||
if (pixmap.isNull())
|
||||
return false;
|
||||
|
||||
QSize canvas_size(target_size, target_size);
|
||||
QSize pixmap_size(pixmap.size());
|
||||
QPoint target_pos;
|
||||
|
||||
// Let's upscale the original pixmap to at least fit into the outer rect.
|
||||
if (pixmap_size.width() < target_size || pixmap_size.height() < target_size)
|
||||
{
|
||||
pixmap_size.scale(target_size, target_size, Qt::KeepAspectRatio);
|
||||
}
|
||||
|
||||
canvas_size = pixmap_size;
|
||||
|
||||
// Calculate the centered size and position of the icon on our canvas.
|
||||
if (pixmap_size.width() != target_size || pixmap_size.height() != target_size)
|
||||
{
|
||||
ensure(pixmap_size.height() > 0);
|
||||
constexpr double target_ratio = 1.0; // square icon
|
||||
|
||||
if ((pixmap_size.width() / static_cast<double>(pixmap_size.height())) > target_ratio)
|
||||
{
|
||||
canvas_size.setHeight(std::ceil(pixmap_size.width() / target_ratio));
|
||||
}
|
||||
else
|
||||
{
|
||||
canvas_size.setWidth(std::ceil(pixmap_size.height() * target_ratio));
|
||||
}
|
||||
|
||||
target_pos.setX(std::max<int>(0, (canvas_size.width() - pixmap_size.width()) / 2.0));
|
||||
target_pos.setY(std::max<int>(0, (canvas_size.height() - pixmap_size.height()) / 2.0));
|
||||
}
|
||||
|
||||
// Create a canvas large enough to fit our entire scaled icon
|
||||
QPixmap canvas(canvas_size);
|
||||
canvas.fill(Qt::transparent);
|
||||
|
||||
// Create a painter for our canvas
|
||||
QPainter painter(&canvas);
|
||||
painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
||||
|
||||
// Draw the icon onto our canvas
|
||||
painter.drawPixmap(target_pos.x(), target_pos.y(), pixmap_size.width(), pixmap_size.height(), pixmap);
|
||||
|
||||
// Finish the painting
|
||||
painter.end();
|
||||
|
||||
pixmap = canvas;
|
||||
return true;
|
||||
}
|
||||
|
||||
QPixmap get_colorized_pixmap(const QPixmap& old_pixmap, const QColor& old_color, const QColor& new_color, bool use_special_masks, bool colorize_all)
|
||||
{
|
||||
QPixmap pixmap = old_pixmap;
|
||||
|
@ -45,6 +45,9 @@ namespace gui
|
||||
// while still considering screen boundaries.
|
||||
QRect create_centered_window_geometry(const QScreen* screen, const QRect& base, s32 target_width, s32 target_height);
|
||||
|
||||
// Creates a square pixmap while keeping the original aspect ratio of the image.
|
||||
bool create_square_pixmap(QPixmap& pixmap, int target_size);
|
||||
|
||||
// Returns a custom colored QPixmap based on another QPixmap.
|
||||
// use colorize_all to repaint every opaque pixel with the chosen color
|
||||
// use_special_masks is only used for pixmaps with multiple predefined colors
|
||||
|
289
rpcs3/rpcs3qt/shortcut_utils.cpp
Normal file
289
rpcs3/rpcs3qt/shortcut_utils.cpp
Normal file
@ -0,0 +1,289 @@
|
||||
#include "stdafx.h"
|
||||
#include "shortcut_utils.h"
|
||||
#include "qt_utils.h"
|
||||
#include "Emu/system_utils.hpp"
|
||||
#include "Emu/VFS.h"
|
||||
#include "Utilities/StrUtil.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#include <shlobj.h>
|
||||
#include <winnls.h>
|
||||
#include <shobjidl.h>
|
||||
#include <objbase.h>
|
||||
#include <objidl.h>
|
||||
#include <shlguid.h>
|
||||
#include <comdef.h>
|
||||
#elif !defined(__APPLE__)
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include <QFile>
|
||||
#include <QPixmap>
|
||||
|
||||
LOG_CHANNEL(sys_log, "SYS");
|
||||
|
||||
namespace gui::utils
|
||||
{
|
||||
bool create_square_shortcut_icon_file(const std::string& src_icon_path, const std::string& target_icon_dir, std::string& target_icon_path, const std::string& extension, int size)
|
||||
{
|
||||
if (src_icon_path.empty() || target_icon_dir.empty() || extension.empty())
|
||||
{
|
||||
sys_log.error("Failed to create shortcut. Icon parameters empty.");
|
||||
return false;
|
||||
}
|
||||
|
||||
QPixmap icon(QString::fromStdString(src_icon_path));
|
||||
if (!gui::utils::create_square_pixmap(icon, size))
|
||||
{
|
||||
sys_log.error("Failed to create shortcut. Icon empty.");
|
||||
return false;
|
||||
}
|
||||
|
||||
target_icon_path = target_icon_dir + "shortcut." + fmt::to_lower(extension);
|
||||
|
||||
QFile icon_file(QString::fromStdString(target_icon_path));
|
||||
if (!icon_file.open(QFile::OpenModeFlag::ReadWrite | QFile::OpenModeFlag::Truncate))
|
||||
{
|
||||
sys_log.error("Failed to create icon file: %s", target_icon_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!icon.save(&icon_file, fmt::to_upper(extension).c_str()))
|
||||
{
|
||||
sys_log.error("Failed to write icon file: %s", target_icon_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
icon_file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool create_shortcut(const std::string& name,
|
||||
[[maybe_unused]] const std::string& target_cli_args,
|
||||
[[maybe_unused]] const std::string& description,
|
||||
[[maybe_unused]] const std::string& src_icon_path,
|
||||
[[maybe_unused]] const std::string& target_icon_dir,
|
||||
bool is_desktop_shortcut)
|
||||
{
|
||||
if (name.empty())
|
||||
{
|
||||
sys_log.error("Cannot create shortcuts without a name");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove illegal characters from filename
|
||||
const std::string simple_name = QString::fromStdString(vfs::escape(name, true)).simplified().toStdString();
|
||||
if (simple_name.empty() || simple_name == "." || simple_name == "..")
|
||||
{
|
||||
sys_log.error("Failed to create shortcut: Cleaned file name empty or not allowed");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string link_file;
|
||||
|
||||
if (const char* home = getenv("USERPROFILE"))
|
||||
{
|
||||
if (is_desktop_shortcut)
|
||||
{
|
||||
link_file = fmt::format("%s/Desktop/%s.lnk", home, simple_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string programs_dir = fmt::format("%s/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/RPCS3", home);
|
||||
if (!fs::create_path(programs_dir))
|
||||
{
|
||||
sys_log.error("Failed to create shortcut: Could not create start menu directory: %s", programs_dir);
|
||||
return false;
|
||||
}
|
||||
link_file = fmt::format("%s/%s.lnk", programs_dir, simple_name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sys_log.error("Failed to create shortcut: home path empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
sys_log.notice("Creating shortcut '%s' with arguments '%s' and .ico dir '%s'", link_file, target_cli_args, target_icon_dir);
|
||||
|
||||
const auto str_error = [](HRESULT hr) -> std::string
|
||||
{
|
||||
_com_error err(hr);
|
||||
const TCHAR* errMsg = err.ErrorMessage();
|
||||
return fmt::format("%s [%d]", wchar_to_utf8(errMsg), hr);
|
||||
};
|
||||
|
||||
// https://stackoverflow.com/questions/3906974/how-to-programmatically-create-a-shortcut-using-win32
|
||||
HRESULT res = CoInitialize(NULL);
|
||||
if (FAILED(res))
|
||||
{
|
||||
sys_log.error("Failed to create shortcut: CoInitialize failed (%s)", str_error(res));
|
||||
return false;
|
||||
}
|
||||
|
||||
IShellLink* pShellLink = nullptr;
|
||||
IPersistFile* pPersistFile = nullptr;
|
||||
|
||||
const auto cleanup = [&](bool return_value, const std::string& fail_reason) -> bool
|
||||
{
|
||||
if (!return_value) sys_log.error("Failed to create shortcut: %s", fail_reason);
|
||||
if (pPersistFile) pPersistFile->Release();
|
||||
if (pShellLink) pShellLink->Release();
|
||||
CoUninitialize();
|
||||
return return_value;
|
||||
};
|
||||
|
||||
res = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&pShellLink);
|
||||
if (FAILED(res))
|
||||
return cleanup(false, "CoCreateInstance failed");
|
||||
|
||||
const std::string working_dir{ rpcs3::utils::get_exe_dir() };
|
||||
const std::string rpcs3_path{ working_dir + "rpcs3.exe" };
|
||||
|
||||
const std::wstring w_target_file = utf8_to_wchar(rpcs3_path);
|
||||
res = pShellLink->SetPath(w_target_file.c_str());
|
||||
if (FAILED(res))
|
||||
return cleanup(false, fmt::format("SetPath failed (%s)", str_error(res)));
|
||||
|
||||
const std::wstring w_working_dir = utf8_to_wchar(working_dir);
|
||||
res = pShellLink->SetWorkingDirectory(w_working_dir.c_str());
|
||||
if (FAILED(res))
|
||||
return cleanup(false, fmt::format("SetWorkingDirectory failed (%s)", str_error(res)));
|
||||
|
||||
if (!target_cli_args.empty())
|
||||
{
|
||||
const std::wstring w_target_cli_args = utf8_to_wchar(target_cli_args);
|
||||
res = pShellLink->SetArguments(w_target_cli_args.c_str());
|
||||
if (FAILED(res))
|
||||
return cleanup(false, fmt::format("SetArguments failed (%s)", str_error(res)));
|
||||
}
|
||||
|
||||
if (!description.empty())
|
||||
{
|
||||
const std::wstring w_descpription = utf8_to_wchar(description);
|
||||
res = pShellLink->SetDescription(w_descpription.c_str());
|
||||
if (FAILED(res))
|
||||
return cleanup(false, fmt::format("SetDescription failed (%s)", str_error(res)));
|
||||
}
|
||||
|
||||
if (!src_icon_path.empty() && !target_icon_dir.empty())
|
||||
{
|
||||
std::string target_icon_path;
|
||||
if (!create_square_shortcut_icon_file(src_icon_path, target_icon_dir, target_icon_path, "ico", 512))
|
||||
return cleanup(false, ".ico creation failed");
|
||||
|
||||
const std::wstring w_icon_path = utf8_to_wchar(target_icon_path);
|
||||
res = pShellLink->SetIconLocation(w_icon_path.c_str(), 0);
|
||||
if (FAILED(res))
|
||||
return cleanup(false, fmt::format("SetIconLocation failed (%s)", str_error(res)));
|
||||
}
|
||||
|
||||
// Use the IPersistFile object to save the shell link
|
||||
res = pShellLink->QueryInterface(IID_IPersistFile, (LPVOID*)&pPersistFile);
|
||||
if (FAILED(res))
|
||||
return cleanup(false, fmt::format("QueryInterface failed (%s)", str_error(res)));
|
||||
|
||||
// Save shortcut
|
||||
const std::wstring w_link_file = utf8_to_wchar(link_file);
|
||||
res = pPersistFile->Save(w_link_file.c_str(), TRUE);
|
||||
if (FAILED(res))
|
||||
{
|
||||
if (is_desktop_shortcut)
|
||||
{
|
||||
return cleanup(false, fmt::format("Saving file to desktop failed (%s)", str_error(res)));
|
||||
}
|
||||
else
|
||||
{
|
||||
return cleanup(false, fmt::format("Saving file to start menu failed (%s)", str_error(res)));
|
||||
}
|
||||
}
|
||||
|
||||
return cleanup(true, {});
|
||||
|
||||
#elif !defined(__APPLE__)
|
||||
|
||||
const std::string exe_path = rpcs3::utils::get_executable_path();
|
||||
if (exe_path.empty())
|
||||
{
|
||||
sys_log.error("Failed to create shortcut. Executable path empty.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string link_path;
|
||||
|
||||
if (const char* home = ::getenv("HOME"))
|
||||
{
|
||||
if (is_desktop_shortcut)
|
||||
{
|
||||
link_path = fmt::format("%s/Desktop/%s.desktop", home, simple_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
link_path = fmt::format("%s/.local/share/applications/%s.desktop", home, simple_name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sys_log.error("Failed to create shortcut. home path empty.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string file_content;
|
||||
fmt::append(file_content, "[Desktop Entry]\n");
|
||||
fmt::append(file_content, "Encoding=UTF-8\n");
|
||||
fmt::append(file_content, "Version=1.0\n");
|
||||
fmt::append(file_content, "Type=Application\n");
|
||||
fmt::append(file_content, "Terminal=false\n");
|
||||
fmt::append(file_content, "Exec=\"%s\" %s\n", exe_path, target_cli_args);
|
||||
fmt::append(file_content, "Name=%s\n", name);
|
||||
fmt::append(file_content, "Categories=Application;Game\n");
|
||||
|
||||
if (!description.empty())
|
||||
{
|
||||
fmt::append(file_content, "Comment=%s\n", QString::fromStdString(description).simplified().toStdString());
|
||||
}
|
||||
|
||||
if (!src_icon_path.empty() && !target_icon_dir.empty())
|
||||
{
|
||||
std::string target_icon_path;
|
||||
if (!create_square_shortcut_icon_file(src_icon_path, target_icon_dir, target_icon_path, "png", 512))
|
||||
{
|
||||
// Error is logged in create_square_shortcut_icon_file
|
||||
return false;
|
||||
}
|
||||
|
||||
fmt::append(file_content, "Icon=%s\n", src_icon_path);
|
||||
}
|
||||
|
||||
fs::file shortcut_file(link_path, fs::read + fs::rewrite);
|
||||
if (!shortcut_file)
|
||||
{
|
||||
sys_log.error("Failed to create .desktop file: %s", link_path);
|
||||
return false;
|
||||
}
|
||||
if (shortcut_file.write(file_content.data(), file_content.size()) != file_content.size())
|
||||
{
|
||||
sys_log.error("Failed to write .desktop file: %s", link_path);
|
||||
return false;
|
||||
}
|
||||
shortcut_file.close();
|
||||
|
||||
if (is_desktop_shortcut)
|
||||
{
|
||||
if (chmod(link_path.c_str(), S_IRWXU) != 0) // enables user to execute file
|
||||
{
|
||||
// Simply log failure. At least we have the file.
|
||||
sys_log.error("Failed to change file permissions for .desktop file: %s (%d)", strerror(errno), errno);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
sys_log.error("Cannot create shortcuts on this operating system");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
11
rpcs3/rpcs3qt/shortcut_utils.h
Normal file
11
rpcs3/rpcs3qt/shortcut_utils.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
namespace gui::utils
|
||||
{
|
||||
bool create_shortcut(const std::string& name,
|
||||
const std::string& target_cli_args,
|
||||
const std::string& description,
|
||||
const std::string& src_icon_path,
|
||||
const std::string& target_icon_dir,
|
||||
bool is_desktop_shortcut);
|
||||
}
|
@ -584,30 +584,10 @@ bool update_manager::handle_rpcs3(const QByteArray& data, bool auto_accept)
|
||||
|
||||
#else
|
||||
|
||||
std::string replace_path;
|
||||
|
||||
const char* appimage_path = ::getenv("APPIMAGE");
|
||||
if (appimage_path != nullptr)
|
||||
std::string replace_path = rpcs3::utils::get_executable_path();
|
||||
if (replace_path.empty())
|
||||
{
|
||||
replace_path = appimage_path;
|
||||
update_log.notice("Found AppImage path: %s", appimage_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
update_log.warning("Failed to find AppImage path");
|
||||
char exe_path[PATH_MAX];
|
||||
ssize_t len = ::readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1);
|
||||
|
||||
if (len == -1)
|
||||
{
|
||||
update_log.error("Failed to find executable path");
|
||||
return false;
|
||||
}
|
||||
|
||||
exe_path[len] = '\0';
|
||||
update_log.trace("Found exec path: %s", exe_path);
|
||||
|
||||
replace_path = exe_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move the appimage/exe and replace with new appimage
|
||||
|
Loading…
x
Reference in New Issue
Block a user