Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'Create shortcut' button to instance toolbar #227

Merged
merged 25 commits into from Nov 25, 2022
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
30b2666
Add 'Create shortcut' button to instance toolbar
Leo40Git Oct 22, 2022
7076818
Windows: implement FS::createShortcut
Leo40Git Oct 22, 2022
f12117c
[UNTESTED] Linux: add icons to shortcuts
Leo40Git Oct 25, 2022
6ae3b18
Remove unnessecary "is defined" check
Leo40Git Oct 25, 2022
b83f9be
Add missing fail check for CoInitialize
Leo40Git Oct 28, 2022
4d4dfab
Windows: add instance icon to shortcut
Leo40Git Oct 28, 2022
cc5f82b
Windows: fix window icon being replaced
Leo40Git Oct 28, 2022
487e352
Fix nested #if directive
Leo40Git Nov 4, 2022
6043444
Apply suggestions from code review
Leo40Git Nov 9, 2022
9be48f1
this ain't static, falco
Leo40Git Nov 9, 2022
dbe0553
Move addAction call above left-align loop
Leo40Git Nov 9, 2022
7e5076b
Linux: fix path shortcut is written to
Leo40Git Nov 12, 2022
f7d7d76
Mac: now supported! [UNTESTED]
Leo40Git Nov 12, 2022
b813c86
refactor #if checks
Leo40Git Nov 12, 2022
45ce725
Windows: remove icon if shortcut creation fails
Leo40Git Nov 12, 2022
5322155
Mac: fix build
Leo40Git Nov 12, 2022
69bbb29
Mac: attempt 2
Leo40Git Nov 13, 2022
43b9d94
tabs -> spaces
Leo40Git Nov 13, 2022
b0269e6
Linux: fixes
Leo40Git Nov 13, 2022
c4cfec1
Undo accidental tomlplusplus downgrade
Leo40Git Nov 13, 2022
acd5096
Linux: remove TryExec entry from .desktop files
Leo40Git Nov 13, 2022
5be8545
Windows, Linux: prevent segfault on missing icon
Leo40Git Nov 13, 2022
ce892c9
Add icon
Leo40Git Nov 14, 2022
cca052c
Restore QRC headers
Leo40Git Nov 14, 2022
ba46ff6
Merge remote-tracking branch 'upstream/develop' into feature/instance…
Leo40Git Nov 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
187 changes: 161 additions & 26 deletions launcher/FileSystem.cpp
Expand Up @@ -49,6 +49,7 @@
#include "StringUtils.h"

#if defined Q_OS_WIN32
#define WIN32_LEAN_AND_MEAN
#include <objbase.h>
#include <objidl.h>
#include <shlguid.h>
Expand Down Expand Up @@ -339,12 +340,37 @@ QString getDesktopDir()
}

// Cross-platform Shortcut creation
bool createShortCut(QString location, QString dest, QStringList args, QString name, QString icon)
bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon)
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Show resolved Hide resolved
{
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
location = PathCombine(location, name + ".desktop");
#if defined(Q_OS_MACOS)
destination += ".command";

QFile f(location);
QFile f(destination);
f.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream stream(&f);

QString argstring;
if (!args.empty())
argstring = " \"" + args.join("\" \"") + "\"";

stream << "#!/bin/bash"
<< "\n";
stream << "\""
<< target
<< "\" "
<< argstring
<< "\n";

stream.flush();
f.close();

f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther);

return true;
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
destination += ".desktop";

QFile f(destination);
f.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream stream(&f);

Expand All @@ -356,36 +382,145 @@ bool createShortCut(QString location, QString dest, QStringList args, QString na
<< "\n";
stream << "Type=Application"
<< "\n";
stream << "TryExec=" << dest.toLocal8Bit() << "\n";
stream << "Exec=" << dest.toLocal8Bit() << argstring.toLocal8Bit() << "\n";
stream << "Exec=\"" << target.toLocal8Bit() << "\"" << argstring.toLocal8Bit() << "\n";
stream << "Name=" << name.toLocal8Bit() << "\n";
stream << "Icon=" << icon.toLocal8Bit() << "\n";
if (!icon.isEmpty())
{
stream << "Icon=" << icon.toLocal8Bit() << "\n";
}

stream.flush();
f.close();

f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther);

return true;
#elif defined Q_OS_WIN
// TODO: Fix
// QFile file(PathCombine(location, name + ".lnk"));
// WCHAR *file_w;
// WCHAR *dest_w;
// WCHAR *args_w;
// file.fileName().toWCharArray(file_w);
// dest.toWCharArray(dest_w);

// QString argStr;
// for (int i = 0; i < args.count(); i++)
// {
// argStr.append(args[i]);
// argStr.append(" ");
// }
// argStr.toWCharArray(args_w);

// return SUCCEEDED(CreateLink(file_w, dest_w, args_w));
return false;
#elif defined(Q_OS_WIN)
QFileInfo targetInfo(target);

if (!targetInfo.exists())
{
qWarning() << "Target file does not exist!";
return false;
}

target = targetInfo.absoluteFilePath();

if (target.length() >= MAX_PATH)
{
qWarning() << "Target file path is too long!";
return false;
}

if (!icon.isEmpty() && icon.length() >= MAX_PATH)
{
qWarning() << "Icon path is too long!";
return false;
}

destination += ".lnk";

if (destination.length() >= MAX_PATH)
{
qWarning() << "Destination path is too long!";
return false;
}

QString argStr;
int argCount = args.count();
for (int i = 0; i < argCount; i++)
{
if (args[i].contains(' '))
{
argStr.append('"').append(args[i]).append('"');
}
else
{
argStr.append(args[i]);
}

if (i < argCount - 1)
{
argStr.append(" ");
}
}

if (argStr.length() >= MAX_PATH)
{
qWarning() << "Arguments string is too long!";
return false;
}

HRESULT hres;

// ...yes, you need to initialize the entire COM stack just to make a shortcut
hres = CoInitialize(nullptr);
if (FAILED(hres))
{
qWarning() << "Failed to initialize COM!";
return false;
}

WCHAR wsz[MAX_PATH];

IShellLink* psl;

// create an IShellLink instance - this stores the shortcut's attributes
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (SUCCEEDED(hres))
{
wmemset(wsz, 0, MAX_PATH);
target.toWCharArray(wsz);
psl->SetPath(wsz);

wmemset(wsz, 0, MAX_PATH);
argStr.toWCharArray(wsz);
psl->SetArguments(wsz);

wmemset(wsz, 0, MAX_PATH);
targetInfo.absolutePath().toWCharArray(wsz);
psl->SetWorkingDirectory(wsz); // "Starts in" attribute

if (!icon.isEmpty())
{
wmemset(wsz, 0, MAX_PATH);
icon.toWCharArray(wsz);
psl->SetIconLocation(wsz, 0);
}

// query an IPersistFile interface from our IShellLink instance
// this is the interface that will actually let us save the shortcut to disk!
IPersistFile* ppf;
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
if (SUCCEEDED(hres))
{
wmemset(wsz, 0, MAX_PATH);
destination.toWCharArray(wsz);
hres = ppf->Save(wsz, TRUE);
if (FAILED(hres))
{
qWarning() << "IPresistFile->Save() failed";
qWarning() << "hres = " << hres;
}
ppf->Release();
}
else
{
qWarning() << "Failed to query IPersistFile interface from IShellLink instance";
qWarning() << "hres = " << hres;
}
psl->Release();
}
else
{
qWarning() << "Failed to create IShellLink instance";
qWarning() << "hres = " << hres;
}

// go away COM, nobody likes you
CoUninitialize();

return SUCCEEDED(hres);
#else
qWarning("Desktop Shortcuts not supported on your platform!");
return false;
Expand Down
5 changes: 5 additions & 0 deletions launcher/FileSystem.h
Expand Up @@ -156,4 +156,9 @@ QString getDesktopDir();
// Overrides one folder with the contents of another, preserving items exclusive to the first folder
// Equivalent to doing QDir::rename, but allowing for overrides
bool overrideFolder(QString overwritten_path, QString override_path);

/**
* Creates a shortcut to the specified target file at the specified destination path.
*/
bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon);
}