forked from quickshell/quickshell
		
	core/desktopentry: expose exec command and use execDetached on call
This commit is contained in:
		
							parent
							
								
									de25787451
								
							
						
					
					
						commit
						71334bfcaf
					
				
					 4 changed files with 58 additions and 26 deletions
				
			
		| 
						 | 
					@ -1,11 +1,9 @@
 | 
				
			||||||
#include "common.hpp"
 | 
					#include "common.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <qdatetime.h>
 | 
					#include <qdatetime.h>
 | 
				
			||||||
#include <qprocess.h>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace qs {
 | 
					namespace qs {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const QDateTime Common::LAUNCH_TIME = QDateTime::currentDateTime();
 | 
					const QDateTime Common::LAUNCH_TIME = QDateTime::currentDateTime();
 | 
				
			||||||
QProcessEnvironment Common::INITIAL_ENVIRONMENT = {}; // NOLINT
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace qs
 | 
					} // namespace qs
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@ namespace qs {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Common {
 | 
					struct Common {
 | 
				
			||||||
	static const QDateTime LAUNCH_TIME;
 | 
						static const QDateTime LAUNCH_TIME;
 | 
				
			||||||
	static QProcessEnvironment INITIAL_ENVIRONMENT; // NOLINT
 | 
						static inline QProcessEnvironment INITIAL_ENVIRONMENT = {}; // NOLINT
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace qs
 | 
					} // namespace qs
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,14 +11,14 @@
 | 
				
			||||||
#include <qnamespace.h>
 | 
					#include <qnamespace.h>
 | 
				
			||||||
#include <qobject.h>
 | 
					#include <qobject.h>
 | 
				
			||||||
#include <qpair.h>
 | 
					#include <qpair.h>
 | 
				
			||||||
#include <qprocess.h>
 | 
					 | 
				
			||||||
#include <qstringview.h>
 | 
					#include <qstringview.h>
 | 
				
			||||||
#include <qtenvironmentvariables.h>
 | 
					#include <qtenvironmentvariables.h>
 | 
				
			||||||
#include <ranges>
 | 
					#include <ranges>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "common.hpp"
 | 
					#include "../io/processcore.hpp"
 | 
				
			||||||
#include "logcat.hpp"
 | 
					#include "logcat.hpp"
 | 
				
			||||||
#include "model.hpp"
 | 
					#include "model.hpp"
 | 
				
			||||||
 | 
					#include "qmlglobal.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace {
 | 
					namespace {
 | 
				
			||||||
QS_LOGGING_CATEGORY(logDesktopEntry, "quickshell.desktopentry", QtWarningMsg);
 | 
					QS_LOGGING_CATEGORY(logDesktopEntry, "quickshell.desktopentry", QtWarningMsg);
 | 
				
			||||||
| 
						 | 
					@ -111,8 +111,10 @@ void DesktopEntry::parseEntry(const QString& text) {
 | 
				
			||||||
				else if (key == "NoDisplay") this->mNoDisplay = value == "true";
 | 
									else if (key == "NoDisplay") this->mNoDisplay = value == "true";
 | 
				
			||||||
				else if (key == "Comment") this->mComment = value;
 | 
									else if (key == "Comment") this->mComment = value;
 | 
				
			||||||
				else if (key == "Icon") this->mIcon = value;
 | 
									else if (key == "Icon") this->mIcon = value;
 | 
				
			||||||
				else if (key == "Exec") this->mExecString = value;
 | 
									else if (key == "Exec") {
 | 
				
			||||||
				else if (key == "Path") this->mWorkingDirectory = value;
 | 
										this->mExecString = value;
 | 
				
			||||||
 | 
										this->mCommand = DesktopEntry::parseExecString(value);
 | 
				
			||||||
 | 
									} else if (key == "Path") this->mWorkingDirectory = value;
 | 
				
			||||||
				else if (key == "Terminal") this->mTerminal = value == "true";
 | 
									else if (key == "Terminal") this->mTerminal = value == "true";
 | 
				
			||||||
				else if (key == "Categories") this->mCategories = value.split(u';', Qt::SkipEmptyParts);
 | 
									else if (key == "Categories") this->mCategories = value.split(u';', Qt::SkipEmptyParts);
 | 
				
			||||||
				else if (key == "Keywords") this->mKeywords = value.split(u';', Qt::SkipEmptyParts);
 | 
									else if (key == "Keywords") this->mKeywords = value.split(u';', Qt::SkipEmptyParts);
 | 
				
			||||||
| 
						 | 
					@ -127,7 +129,10 @@ void DesktopEntry::parseEntry(const QString& text) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (key == "Name") action->mName = value;
 | 
									if (key == "Name") action->mName = value;
 | 
				
			||||||
				else if (key == "Icon") action->mIcon = value;
 | 
									else if (key == "Icon") action->mIcon = value;
 | 
				
			||||||
				else if (key == "Exec") action->mExecString = value;
 | 
									else if (key == "Exec") {
 | 
				
			||||||
 | 
										action->mExecString = value;
 | 
				
			||||||
 | 
										action->mCommand = DesktopEntry::parseExecString(value);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			this->mActions.insert(actionName, action);
 | 
								this->mActions.insert(actionName, action);
 | 
				
			||||||
| 
						 | 
					@ -179,7 +184,7 @@ void DesktopEntry::parseEntry(const QString& text) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void DesktopEntry::execute() const {
 | 
					void DesktopEntry::execute() const {
 | 
				
			||||||
	DesktopEntry::doExec(this->mExecString, this->mWorkingDirectory);
 | 
						DesktopEntry::doExec(this->mCommand, this->mWorkingDirectory);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool DesktopEntry::isValid() const { return !this->mName.isEmpty(); }
 | 
					bool DesktopEntry::isValid() const { return !this->mName.isEmpty(); }
 | 
				
			||||||
| 
						 | 
					@ -251,23 +256,15 @@ QVector<QString> DesktopEntry::parseExecString(const QString& execString) {
 | 
				
			||||||
	return arguments;
 | 
						return arguments;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void DesktopEntry::doExec(const QString& execString, const QString& workingDirectory) {
 | 
					void DesktopEntry::doExec(const QList<QString>& execString, const QString& workingDirectory) {
 | 
				
			||||||
	auto args = DesktopEntry::parseExecString(execString);
 | 
						qs::io::process::ProcessContext ctx;
 | 
				
			||||||
	if (args.isEmpty()) {
 | 
						ctx.setCommand(execString);
 | 
				
			||||||
		qCWarning(logDesktopEntry) << "Tried to exec string" << execString << "which parsed as empty.";
 | 
						ctx.setWorkingDirectory(workingDirectory);
 | 
				
			||||||
		return;
 | 
						QuickshellGlobal::execDetached(ctx);
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto process = QProcess();
 | 
					 | 
				
			||||||
	process.setProgram(args.at(0));
 | 
					 | 
				
			||||||
	process.setArguments(args.sliced(1));
 | 
					 | 
				
			||||||
	if (!workingDirectory.isEmpty()) process.setWorkingDirectory(workingDirectory);
 | 
					 | 
				
			||||||
	process.setProcessEnvironment(qs::Common::INITIAL_ENVIRONMENT);
 | 
					 | 
				
			||||||
	process.startDetached();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void DesktopAction::execute() const {
 | 
					void DesktopAction::execute() const {
 | 
				
			||||||
	DesktopEntry::doExec(this->mExecString, this->entry->mWorkingDirectory);
 | 
						DesktopEntry::doExec(this->mCommand, this->entry->mWorkingDirectory);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DesktopEntryManager::DesktopEntryManager() {
 | 
					DesktopEntryManager::DesktopEntryManager() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,8 +28,19 @@ class DesktopEntry: public QObject {
 | 
				
			||||||
	Q_PROPERTY(QString comment MEMBER mComment CONSTANT);
 | 
						Q_PROPERTY(QString comment MEMBER mComment CONSTANT);
 | 
				
			||||||
	/// Name of the icon associated with this application. May be empty.
 | 
						/// Name of the icon associated with this application. May be empty.
 | 
				
			||||||
	Q_PROPERTY(QString icon MEMBER mIcon CONSTANT);
 | 
						Q_PROPERTY(QString icon MEMBER mIcon CONSTANT);
 | 
				
			||||||
	/// The raw `Exec` string from the desktop entry. You probably want @@execute().
 | 
						/// The raw `Exec` string from the desktop entry.
 | 
				
			||||||
 | 
						///
 | 
				
			||||||
 | 
						/// > [!WARNING] This cannot be reliably run as a command. See @@command for one you can run.
 | 
				
			||||||
	Q_PROPERTY(QString execString MEMBER mExecString CONSTANT);
 | 
						Q_PROPERTY(QString execString MEMBER mExecString CONSTANT);
 | 
				
			||||||
 | 
						/// The parsed `Exec` command in the desktop entry.
 | 
				
			||||||
 | 
						///
 | 
				
			||||||
 | 
						/// The entry can be run with @@execute(), or by using this command in
 | 
				
			||||||
 | 
						/// @@Quickshell.Quickshell.execDetached() or @@Quickshell.Io.Process.
 | 
				
			||||||
 | 
						/// If used in `execDetached` or a `Process`, @@workingDirectory should also be passed to
 | 
				
			||||||
 | 
						/// the invoked process. See @@execute() for details.
 | 
				
			||||||
 | 
						///
 | 
				
			||||||
 | 
						/// > [!NOTE]	The provided command does not invoke a terminal even if @@runInTerminal is true.
 | 
				
			||||||
 | 
						Q_PROPERTY(QVector<QString> command MEMBER mCommand CONSTANT);
 | 
				
			||||||
	/// The working directory to execute from.
 | 
						/// The working directory to execute from.
 | 
				
			||||||
	Q_PROPERTY(QString workingDirectory MEMBER mWorkingDirectory CONSTANT);
 | 
						Q_PROPERTY(QString workingDirectory MEMBER mWorkingDirectory CONSTANT);
 | 
				
			||||||
	/// If the application should run in a terminal.
 | 
						/// If the application should run in a terminal.
 | 
				
			||||||
| 
						 | 
					@ -46,6 +57,16 @@ public:
 | 
				
			||||||
	void parseEntry(const QString& text);
 | 
						void parseEntry(const QString& text);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Run the application. Currently ignores @@runInTerminal and field codes.
 | 
						/// Run the application. Currently ignores @@runInTerminal and field codes.
 | 
				
			||||||
 | 
						///
 | 
				
			||||||
 | 
						/// This is equivalent to calling @@Quickshell.Quickshell.execDetached() with @@command
 | 
				
			||||||
 | 
						/// and @@DesktopEntry.workingDirectory as shown below:
 | 
				
			||||||
 | 
						///
 | 
				
			||||||
 | 
						/// ```qml
 | 
				
			||||||
 | 
						/// Quickshell.execDetached({
 | 
				
			||||||
 | 
						///   command: desktopEntry.command,
 | 
				
			||||||
 | 
						///   workingDirectory: desktopEntry.workingDirectory,
 | 
				
			||||||
 | 
						/// });
 | 
				
			||||||
 | 
						/// ```
 | 
				
			||||||
	Q_INVOKABLE void execute() const;
 | 
						Q_INVOKABLE void execute() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	[[nodiscard]] bool isValid() const;
 | 
						[[nodiscard]] bool isValid() const;
 | 
				
			||||||
| 
						 | 
					@ -54,7 +75,7 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// currently ignores all field codes.
 | 
						// currently ignores all field codes.
 | 
				
			||||||
	static QVector<QString> parseExecString(const QString& execString);
 | 
						static QVector<QString> parseExecString(const QString& execString);
 | 
				
			||||||
	static void doExec(const QString& execString, const QString& workingDirectory);
 | 
						static void doExec(const QList<QString>& execString, const QString& workingDirectory);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	QString mId;
 | 
						QString mId;
 | 
				
			||||||
| 
						 | 
					@ -64,6 +85,7 @@ public:
 | 
				
			||||||
	QString mComment;
 | 
						QString mComment;
 | 
				
			||||||
	QString mIcon;
 | 
						QString mIcon;
 | 
				
			||||||
	QString mExecString;
 | 
						QString mExecString;
 | 
				
			||||||
 | 
						QVector<QString> mCommand;
 | 
				
			||||||
	QString mWorkingDirectory;
 | 
						QString mWorkingDirectory;
 | 
				
			||||||
	bool mTerminal = false;
 | 
						bool mTerminal = false;
 | 
				
			||||||
	QVector<QString> mCategories;
 | 
						QVector<QString> mCategories;
 | 
				
			||||||
| 
						 | 
					@ -82,8 +104,19 @@ class DesktopAction: public QObject {
 | 
				
			||||||
	Q_PROPERTY(QString id MEMBER mId CONSTANT);
 | 
						Q_PROPERTY(QString id MEMBER mId CONSTANT);
 | 
				
			||||||
	Q_PROPERTY(QString name MEMBER mName CONSTANT);
 | 
						Q_PROPERTY(QString name MEMBER mName CONSTANT);
 | 
				
			||||||
	Q_PROPERTY(QString icon MEMBER mIcon CONSTANT);
 | 
						Q_PROPERTY(QString icon MEMBER mIcon CONSTANT);
 | 
				
			||||||
	/// The raw `Exec` string from the desktop entry. You probably want @@execute().
 | 
						/// The raw `Exec` string from the action.
 | 
				
			||||||
 | 
						///
 | 
				
			||||||
 | 
						/// > [!WARNING] This cannot be reliably run as a command. See @@command for one you can run.
 | 
				
			||||||
	Q_PROPERTY(QString execString MEMBER mExecString CONSTANT);
 | 
						Q_PROPERTY(QString execString MEMBER mExecString CONSTANT);
 | 
				
			||||||
 | 
						/// The parsed `Exec` command in the action.
 | 
				
			||||||
 | 
						///
 | 
				
			||||||
 | 
						/// The entry can be run with @@execute(), or by using this command in
 | 
				
			||||||
 | 
						/// @@Quickshell.Quickshell.execDetached() or @@Quickshell.Io.Process.
 | 
				
			||||||
 | 
						/// If used in `execDetached` or a `Process`, @@DesktopEntry.workingDirectory should also be passed to
 | 
				
			||||||
 | 
						/// the invoked process.
 | 
				
			||||||
 | 
						///
 | 
				
			||||||
 | 
						/// > [!NOTE]	The provided command does not invoke a terminal even if @@runInTerminal is true.
 | 
				
			||||||
 | 
						Q_PROPERTY(QVector<QString> command MEMBER mCommand CONSTANT);
 | 
				
			||||||
	QML_ELEMENT;
 | 
						QML_ELEMENT;
 | 
				
			||||||
	QML_UNCREATABLE("DesktopAction instances must be retrieved from a DesktopEntry");
 | 
						QML_UNCREATABLE("DesktopAction instances must be retrieved from a DesktopEntry");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -94,6 +127,9 @@ public:
 | 
				
			||||||
	    , mId(std::move(id)) {}
 | 
						    , mId(std::move(id)) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Run the application. Currently ignores @@DesktopEntry.runInTerminal and field codes.
 | 
						/// Run the application. Currently ignores @@DesktopEntry.runInTerminal and field codes.
 | 
				
			||||||
 | 
						///
 | 
				
			||||||
 | 
						/// This is equivalent to calling @@Quickshell.Quickshell.execDetached() with @@command
 | 
				
			||||||
 | 
						/// and @@DesktopEntry.workingDirectory.
 | 
				
			||||||
	Q_INVOKABLE void execute() const;
 | 
						Q_INVOKABLE void execute() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
| 
						 | 
					@ -102,6 +138,7 @@ private:
 | 
				
			||||||
	QString mName;
 | 
						QString mName;
 | 
				
			||||||
	QString mIcon;
 | 
						QString mIcon;
 | 
				
			||||||
	QString mExecString;
 | 
						QString mExecString;
 | 
				
			||||||
 | 
						QVector<QString> mCommand;
 | 
				
			||||||
	QHash<QString, QString> mEntries;
 | 
						QHash<QString, QString> mEntries;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	friend class DesktopEntry;
 | 
						friend class DesktopEntry;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue