From 1ac0e2258919b56e34b62a0bd1edbca21dad1b6b Mon Sep 17 00:00:00 2001 From: Christian Heimlich Date: Wed, 18 Oct 2023 05:31:14 -0400 Subject: [PATCH] Add registerUriScheme() to qx-system --- lib/core/include/qx/core/qx-system.h | 2 ++ lib/core/src/qx-system.cpp | 13 ++++++++++ lib/core/src/qx-system_linux.cpp | 39 ++++++++++++++++++++++++++++ lib/core/src/qx-system_win.cpp | 23 ++++++++++++++++ 4 files changed, 77 insertions(+) diff --git a/lib/core/include/qx/core/qx-system.h b/lib/core/include/qx/core/qx-system.h index 780840dd..be3162a5 100644 --- a/lib/core/include/qx/core/qx-system.h +++ b/lib/core/include/qx/core/qx-system.h @@ -25,6 +25,8 @@ QX_CORE_EXPORT SystemError cleanKillProcess(quint32 processId); QX_CORE_EXPORT SystemError forceKillProcess(quint32 processId); QX_CORE_EXPORT bool enforceSingleInstance(QString uniqueAppId); + +QX_CORE_EXPORT bool registerUriScheme(const QString& scheme, const QString& command); } #endif // QX_SYSTEM_H diff --git a/lib/core/src/qx-system.cpp b/lib/core/src/qx-system.cpp index 976e3e63..c6ab281e 100644 --- a/lib/core/src/qx-system.cpp +++ b/lib/core/src/qx-system.cpp @@ -123,4 +123,17 @@ bool processIsRunning(quint32 processId) { return processName(processId).isNull( * changed in future revisions once set. */ +/*! + * @fn bool registerUriScheme(const QString& scheme, const QString& command) + * + * Registers the custom URI scheme @a scheme with the system. The scheme is configued so that + * when a URL that uses the scheme is followed, the command @a command will be executed with + * the scheme URL automatically passed as the last argument. + * + * @a scheme cannot contains whitespace. + * + * @note On Linux this function only works for distibutions that can utilize FreeDesktop + * XDG Desktop Entries. + */ + } diff --git a/lib/core/src/qx-system_linux.cpp b/lib/core/src/qx-system_linux.cpp index 6167fccf..2f2c63bd 100644 --- a/lib/core/src/qx-system_linux.cpp +++ b/lib/core/src/qx-system_linux.cpp @@ -13,6 +13,9 @@ #include #include #include +#include +#include +#include // Inner-component Includes #include @@ -297,4 +300,40 @@ bool enforceSingleInstance(QString uniqueAppId) return true; } +bool registerUriScheme(const QString& scheme, const QString& command) +{ + if(scheme.contains(QChar::Space)) + return false; + + // Get desktop entry path + QString XDG_DATA_HOME = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); + QString dEntryFilename = scheme + u"-scheme-handler.desktop"_s; + QString dEntryPath = XDG_DATA_HOME + u"/applications/"_s + dEntryFilename; + QString xSchemeHandler = u"x-scheme-handler/"_s + scheme; + + // Create desktop entry + QSettings de(dEntryPath, QSettings::IniFormat); + de.beginGroup(u"Desktop Entry"_s); + de.setValue(u"Type"_s, u"Application"_s); + de.setValue(u"Name"_s, scheme.toUpper() + u" Scheme Handler"_s); + de.setValue(u"Exec"_s, command + u" %u"_s); // %u is already passed as single param, no need for quotes + de.setValue(u"StartupNotify"_s, u"false"_s); + de.setValue(u"MimeType"_s, xSchemeHandler); + de.endGroup(); + + de.sync(); + if(de.status() != QSettings::NoError) + return false; + + // Register MIME type + QProcess xdgMime; + xdgMime.setProgram(u"xdg-mime"_s); + xdgMime.setArguments({u"default"_s, dEntryFilename, xSchemeHandler}); + xdgMime.setStandardOutputFile(QProcess::nullDevice()); + xdgMime.setStandardErrorFile(QProcess::nullDevice()); + xdgMime.start(); + + return xdgMime.waitForFinished(3000) && xdgMime.exitStatus() == xdgMime.NormalExit && xdgMime.exitCode() == 0; +} + } diff --git a/lib/core/src/qx-system_win.cpp b/lib/core/src/qx-system_win.cpp index b6fbe6fb..12a115a0 100644 --- a/lib/core/src/qx-system_win.cpp +++ b/lib/core/src/qx-system_win.cpp @@ -5,6 +5,7 @@ #include #include #include +#include // Extra-component Includes #include "qx/core/qx-integrity.h" @@ -223,4 +224,26 @@ bool enforceSingleInstance(QString uniqueAppId) return createMutex(hashId); } +bool registerUriScheme(const QString& scheme, const QString& command) +{ + if(scheme.contains(QChar::Space)) + return false; + + // Set registry keys + QSettings schemeKey(u"HKEY_CLASSES_ROOT\\"_s + scheme, QSettings::NativeFormat); + schemeKey.setValue(u"."_s, u"URL:"_s + scheme + u" Protocol"_s); + schemeKey.setValue("URL Protocol", ""); + schemeKey.setValue(u"shell\\open\\command"_s, command + uR"( "%1")"_s); + + // Save and return status + schemeKey.sync(); + return schemeKey.status() == QSettings::NoError; + + /* NOTE: The Microsoft specification recommends adding a DefaultIcon key to these entries + * with an executable based icon path, though I'm not sure if/how that's actually + * used. If that is ever added, we should check if adding an icon to the desktop entry + * of the Linux equivalent has an appreciable effect as well. + */ +} + }