forked from quickshell/quickshell
		
	io/ipchandler: add prop get
				
					
				
			This commit is contained in:
		
							parent
							
								
									9417d6fa57
								
							
						
					
					
						commit
						4f2610dece
					
				
					 10 changed files with 262 additions and 31 deletions
				
			
		| 
						 | 
				
			
			@ -1,9 +1,12 @@
 | 
			
		|||
#include "ipc.hpp"
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include <qcolor.h>
 | 
			
		||||
#include <qmetatype.h>
 | 
			
		||||
#include <qobjectdefs.h>
 | 
			
		||||
#include <qtypes.h>
 | 
			
		||||
#include <qvariant.h>
 | 
			
		||||
 | 
			
		||||
namespace qs::io::ipc {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +17,12 @@ const BoolIpcType BoolIpcType::INSTANCE {};
 | 
			
		|||
const DoubleIpcType DoubleIpcType::INSTANCE {};
 | 
			
		||||
const ColorIpcType ColorIpcType::INSTANCE {};
 | 
			
		||||
 | 
			
		||||
void* IpcType::copyStorage(const void* data) const {
 | 
			
		||||
	auto* storage = this->createStorage();
 | 
			
		||||
	memcpy(storage, data, this->size());
 | 
			
		||||
	return storage;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const IpcType* IpcType::ipcType(const QMetaType& metaType) {
 | 
			
		||||
	if (metaType.id() == QMetaType::Void) return &VoidIpcType::INSTANCE;
 | 
			
		||||
	if (metaType.id() == QMetaType::QString) return &StringIpcType::INSTANCE;
 | 
			
		||||
| 
						 | 
				
			
			@ -70,12 +79,18 @@ void IpcTypeSlot::replace(void* value) {
 | 
			
		|||
	this->storage = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void IpcTypeSlot::replace(const QVariant& value) {
 | 
			
		||||
	this->replace(this->mType->copyStorage(value.constData()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* VoidIpcType::name() const { return "void"; }
 | 
			
		||||
const char* VoidIpcType::genericArgumentName() const { return "void"; }
 | 
			
		||||
qsizetype VoidIpcType::size() const { return 0; }
 | 
			
		||||
 | 
			
		||||
// string
 | 
			
		||||
const char* StringIpcType::name() const { return "string"; }
 | 
			
		||||
const char* StringIpcType::genericArgumentName() const { return "QString"; }
 | 
			
		||||
qsizetype StringIpcType::size() const { return sizeof(QString); }
 | 
			
		||||
void* StringIpcType::fromString(const QString& string) const { return new QString(string); }
 | 
			
		||||
QString StringIpcType::toString(void* slot) const { return *static_cast<QString*>(slot); }
 | 
			
		||||
void* StringIpcType::createStorage() const { return new QString(); }
 | 
			
		||||
| 
						 | 
				
			
			@ -84,6 +99,7 @@ void StringIpcType::destroyStorage(void* slot) const { delete static_cast<QStrin
 | 
			
		|||
// int
 | 
			
		||||
const char* IntIpcType::name() const { return "int"; }
 | 
			
		||||
const char* IntIpcType::genericArgumentName() const { return "int"; }
 | 
			
		||||
qsizetype IntIpcType::size() const { return sizeof(int); }
 | 
			
		||||
 | 
			
		||||
void* IntIpcType::fromString(const QString& string) const {
 | 
			
		||||
	auto ok = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -100,6 +116,7 @@ void IntIpcType::destroyStorage(void* slot) const { delete static_cast<int*>(slo
 | 
			
		|||
// bool
 | 
			
		||||
const char* BoolIpcType::name() const { return "bool"; }
 | 
			
		||||
const char* BoolIpcType::genericArgumentName() const { return "bool"; }
 | 
			
		||||
qsizetype BoolIpcType::size() const { return sizeof(bool); }
 | 
			
		||||
 | 
			
		||||
void* BoolIpcType::fromString(const QString& string) const {
 | 
			
		||||
	if (string == "true") return new bool(true);
 | 
			
		||||
| 
						 | 
				
			
			@ -121,6 +138,7 @@ void BoolIpcType::destroyStorage(void* slot) const { delete static_cast<bool*>(s
 | 
			
		|||
// double
 | 
			
		||||
const char* DoubleIpcType::name() const { return "real"; }
 | 
			
		||||
const char* DoubleIpcType::genericArgumentName() const { return "double"; }
 | 
			
		||||
qsizetype DoubleIpcType::size() const { return sizeof(double); }
 | 
			
		||||
 | 
			
		||||
void* DoubleIpcType::fromString(const QString& string) const {
 | 
			
		||||
	auto ok = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -139,6 +157,7 @@ void DoubleIpcType::destroyStorage(void* slot) const { delete static_cast<double
 | 
			
		|||
// color
 | 
			
		||||
const char* ColorIpcType::name() const { return "color"; }
 | 
			
		||||
const char* ColorIpcType::genericArgumentName() const { return "QColor"; }
 | 
			
		||||
qsizetype ColorIpcType::size() const { return sizeof(QColor); }
 | 
			
		||||
 | 
			
		||||
void* ColorIpcType::fromString(const QString& string) const {
 | 
			
		||||
	auto color = QColor::fromString(string);
 | 
			
		||||
| 
						 | 
				
			
			@ -167,6 +186,10 @@ QString WireFunctionDefinition::toString() const {
 | 
			
		|||
	return "function " % this->name % '(' % paramString % "): " % this->returnType;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString WirePropertyDefinition::toString() const {
 | 
			
		||||
	return "property " % this->name % ": " % this->type;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString WireTargetDefinition::toString() const {
 | 
			
		||||
	QString accum = "target " % this->name;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -174,6 +197,10 @@ QString WireTargetDefinition::toString() const {
 | 
			
		|||
		accum += "\n  " % func.toString();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (const auto& prop: this->properties) {
 | 
			
		||||
		accum += "\n  " % prop.toString();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return accum;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@
 | 
			
		|||
#include <qcontainerfwd.h>
 | 
			
		||||
#include <qobjectdefs.h>
 | 
			
		||||
#include <qtclasshelpermacros.h>
 | 
			
		||||
#include <qtypes.h>
 | 
			
		||||
 | 
			
		||||
#include "../ipc/ipc.hpp"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -21,10 +22,12 @@ public:
 | 
			
		|||
 | 
			
		||||
	[[nodiscard]] virtual const char* name() const = 0;
 | 
			
		||||
	[[nodiscard]] virtual const char* genericArgumentName() const = 0;
 | 
			
		||||
	[[nodiscard]] virtual qsizetype size() const = 0;
 | 
			
		||||
	[[nodiscard]] virtual void* fromString(const QString& /*string*/) const { return nullptr; }
 | 
			
		||||
	[[nodiscard]] virtual QString toString(void* /*slot*/) const { return ""; }
 | 
			
		||||
	[[nodiscard]] virtual void* createStorage() const { return nullptr; }
 | 
			
		||||
	virtual void destroyStorage(void* /*slot*/) const {}
 | 
			
		||||
	void* copyStorage(const void* data) const;
 | 
			
		||||
 | 
			
		||||
	static const IpcType* ipcType(const QMetaType& metaType);
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -43,6 +46,7 @@ public:
 | 
			
		|||
	[[nodiscard]] QGenericReturnArgument asGenericReturnArgument();
 | 
			
		||||
 | 
			
		||||
	void replace(void* value);
 | 
			
		||||
	void replace(const QVariant& value);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	const IpcType* mType = nullptr;
 | 
			
		||||
| 
						 | 
				
			
			@ -53,6 +57,7 @@ class VoidIpcType: public IpcType {
 | 
			
		|||
public:
 | 
			
		||||
	[[nodiscard]] const char* name() const override;
 | 
			
		||||
	[[nodiscard]] const char* genericArgumentName() const override;
 | 
			
		||||
	[[nodiscard]] qsizetype size() const override;
 | 
			
		||||
 | 
			
		||||
	static const VoidIpcType INSTANCE;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -61,6 +66,7 @@ class StringIpcType: public IpcType {
 | 
			
		|||
public:
 | 
			
		||||
	[[nodiscard]] const char* name() const override;
 | 
			
		||||
	[[nodiscard]] const char* genericArgumentName() const override;
 | 
			
		||||
	[[nodiscard]] qsizetype size() const override;
 | 
			
		||||
	[[nodiscard]] void* fromString(const QString& string) const override;
 | 
			
		||||
	[[nodiscard]] QString toString(void* slot) const override;
 | 
			
		||||
	[[nodiscard]] void* createStorage() const override;
 | 
			
		||||
| 
						 | 
				
			
			@ -73,6 +79,7 @@ class IntIpcType: public IpcType {
 | 
			
		|||
public:
 | 
			
		||||
	[[nodiscard]] const char* name() const override;
 | 
			
		||||
	[[nodiscard]] const char* genericArgumentName() const override;
 | 
			
		||||
	[[nodiscard]] qsizetype size() const override;
 | 
			
		||||
	[[nodiscard]] void* fromString(const QString& string) const override;
 | 
			
		||||
	[[nodiscard]] QString toString(void* slot) const override;
 | 
			
		||||
	[[nodiscard]] void* createStorage() const override;
 | 
			
		||||
| 
						 | 
				
			
			@ -85,6 +92,7 @@ class BoolIpcType: public IpcType {
 | 
			
		|||
public:
 | 
			
		||||
	[[nodiscard]] const char* name() const override;
 | 
			
		||||
	[[nodiscard]] const char* genericArgumentName() const override;
 | 
			
		||||
	[[nodiscard]] qsizetype size() const override;
 | 
			
		||||
	[[nodiscard]] void* fromString(const QString& string) const override;
 | 
			
		||||
	[[nodiscard]] QString toString(void* slot) const override;
 | 
			
		||||
	[[nodiscard]] void* createStorage() const override;
 | 
			
		||||
| 
						 | 
				
			
			@ -97,6 +105,7 @@ class DoubleIpcType: public IpcType {
 | 
			
		|||
public:
 | 
			
		||||
	[[nodiscard]] const char* name() const override;
 | 
			
		||||
	[[nodiscard]] const char* genericArgumentName() const override;
 | 
			
		||||
	[[nodiscard]] qsizetype size() const override;
 | 
			
		||||
	[[nodiscard]] void* fromString(const QString& string) const override;
 | 
			
		||||
	[[nodiscard]] QString toString(void* slot) const override;
 | 
			
		||||
	[[nodiscard]] void* createStorage() const override;
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +118,7 @@ class ColorIpcType: public IpcType {
 | 
			
		|||
public:
 | 
			
		||||
	[[nodiscard]] const char* name() const override;
 | 
			
		||||
	[[nodiscard]] const char* genericArgumentName() const override;
 | 
			
		||||
	[[nodiscard]] qsizetype size() const override;
 | 
			
		||||
	[[nodiscard]] void* fromString(const QString& string) const override;
 | 
			
		||||
	[[nodiscard]] QString toString(void* slot) const override;
 | 
			
		||||
	[[nodiscard]] void* createStorage() const override;
 | 
			
		||||
| 
						 | 
				
			
			@ -127,13 +137,23 @@ struct WireFunctionDefinition {
 | 
			
		|||
 | 
			
		||||
DEFINE_SIMPLE_DATASTREAM_OPS(WireFunctionDefinition, data.name, data.returnType, data.arguments);
 | 
			
		||||
 | 
			
		||||
struct WireTargetDefinition {
 | 
			
		||||
struct WirePropertyDefinition {
 | 
			
		||||
	QString name;
 | 
			
		||||
	QVector<WireFunctionDefinition> functions;
 | 
			
		||||
	QString type;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] QString toString() const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DEFINE_SIMPLE_DATASTREAM_OPS(WireTargetDefinition, data.name, data.functions);
 | 
			
		||||
DEFINE_SIMPLE_DATASTREAM_OPS(WirePropertyDefinition, data.name, data.type);
 | 
			
		||||
 | 
			
		||||
struct WireTargetDefinition {
 | 
			
		||||
	QString name;
 | 
			
		||||
	QVector<WireFunctionDefinition> functions;
 | 
			
		||||
	QVector<WirePropertyDefinition> properties;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] QString toString() const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DEFINE_SIMPLE_DATASTREAM_OPS(WireTargetDefinition, data.name, data.functions, data.properties);
 | 
			
		||||
 | 
			
		||||
} // namespace qs::io::ipc
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,16 +21,17 @@ namespace qs::io::ipc::comm {
 | 
			
		|||
 | 
			
		||||
struct NoCurrentGeneration: std::monostate {};
 | 
			
		||||
struct TargetNotFound: std::monostate {};
 | 
			
		||||
struct FunctionNotFound: std::monostate {};
 | 
			
		||||
struct EntryNotFound: std::monostate {};
 | 
			
		||||
 | 
			
		||||
using QueryResponse = std::variant<
 | 
			
		||||
    std::monostate,
 | 
			
		||||
    NoCurrentGeneration,
 | 
			
		||||
    TargetNotFound,
 | 
			
		||||
    FunctionNotFound,
 | 
			
		||||
    EntryNotFound,
 | 
			
		||||
    QVector<WireTargetDefinition>,
 | 
			
		||||
    WireTargetDefinition,
 | 
			
		||||
    WireFunctionDefinition>;
 | 
			
		||||
    WireFunctionDefinition,
 | 
			
		||||
    WirePropertyDefinition>;
 | 
			
		||||
 | 
			
		||||
void QueryMetadataCommand::exec(qs::ipc::IpcServerConnection* conn) const {
 | 
			
		||||
	auto resp = conn->responseStream<QueryResponse>();
 | 
			
		||||
| 
						 | 
				
			
			@ -44,16 +45,24 @@ void QueryMetadataCommand::exec(qs::ipc::IpcServerConnection* conn) const {
 | 
			
		|||
			auto* handler = registry->findHandler(this->target);
 | 
			
		||||
 | 
			
		||||
			if (handler) {
 | 
			
		||||
				if (this->function.isEmpty()) {
 | 
			
		||||
				if (this->name.isEmpty()) {
 | 
			
		||||
					resp << handler->wireDef();
 | 
			
		||||
				} else {
 | 
			
		||||
					auto* func = handler->findFunction(this->function);
 | 
			
		||||
					auto* func = handler->findFunction(this->name);
 | 
			
		||||
 | 
			
		||||
					if (func) {
 | 
			
		||||
						resp << func->wireDef();
 | 
			
		||||
					} else {
 | 
			
		||||
						resp << FunctionNotFound();
 | 
			
		||||
						return;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					auto* prop = handler->findProperty(this->name);
 | 
			
		||||
 | 
			
		||||
					if (prop) {
 | 
			
		||||
						resp << prop->wireDef();
 | 
			
		||||
						return;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					resp << EntryNotFound();
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				resp << TargetNotFound();
 | 
			
		||||
| 
						 | 
				
			
			@ -64,8 +73,8 @@ void QueryMetadataCommand::exec(qs::ipc::IpcServerConnection* conn) const {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int queryMetadata(IpcClient* client, const QString& target, const QString& function) {
 | 
			
		||||
	client->sendMessage(IpcCommand(QueryMetadataCommand {.target = target, .function = function}));
 | 
			
		||||
int queryMetadata(IpcClient* client, const QString& target, const QString& name) {
 | 
			
		||||
	client->sendMessage(IpcCommand(QueryMetadataCommand {.target = target, .name = name}));
 | 
			
		||||
 | 
			
		||||
	QueryResponse slot;
 | 
			
		||||
	if (!client->waitForResponse(slot)) return -1;
 | 
			
		||||
| 
						 | 
				
			
			@ -82,9 +91,11 @@ int queryMetadata(IpcClient* client, const QString& target, const QString& funct
 | 
			
		|||
		qCInfo(logBare).noquote() << std::get<WireTargetDefinition>(slot).toString();
 | 
			
		||||
	} else if (std::holds_alternative<WireFunctionDefinition>(slot)) {
 | 
			
		||||
		qCInfo(logBare).noquote() << std::get<WireFunctionDefinition>(slot).toString();
 | 
			
		||||
	} else if (std::holds_alternative<WirePropertyDefinition>(slot)) {
 | 
			
		||||
		qCInfo(logBare).noquote() << std::get<WirePropertyDefinition>(slot).toString();
 | 
			
		||||
	} else if (std::holds_alternative<TargetNotFound>(slot)) {
 | 
			
		||||
		qCCritical(logBare) << "Target not found.";
 | 
			
		||||
	} else if (std::holds_alternative<FunctionNotFound>(slot)) {
 | 
			
		||||
	} else if (std::holds_alternative<EntryNotFound>(slot)) {
 | 
			
		||||
		qCCritical(logBare) << "Function not found.";
 | 
			
		||||
	} else if (std::holds_alternative<NoCurrentGeneration>(slot)) {
 | 
			
		||||
		qCCritical(logBare) << "Not ready to accept queries yet.";
 | 
			
		||||
| 
						 | 
				
			
			@ -119,7 +130,7 @@ using StringCallResponse = std::variant<
 | 
			
		|||
    std::monostate,
 | 
			
		||||
    NoCurrentGeneration,
 | 
			
		||||
    TargetNotFound,
 | 
			
		||||
    FunctionNotFound,
 | 
			
		||||
    EntryNotFound,
 | 
			
		||||
    ArgParseFailed,
 | 
			
		||||
    Completed>;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -137,7 +148,7 @@ void StringCallCommand::exec(qs::ipc::IpcServerConnection* conn) const {
 | 
			
		|||
 | 
			
		||||
		auto* func = handler->findFunction(this->function);
 | 
			
		||||
		if (!func) {
 | 
			
		||||
			resp << FunctionNotFound();
 | 
			
		||||
			resp << EntryNotFound();
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -223,7 +234,7 @@ int callFunction(
 | 
			
		|||
		qCCritical(logBare).noquote() << "Function definition:" << error.definition.toString();
 | 
			
		||||
	} else if (std::holds_alternative<TargetNotFound>(slot)) {
 | 
			
		||||
		qCCritical(logBare) << "Target not found.";
 | 
			
		||||
	} else if (std::holds_alternative<FunctionNotFound>(slot)) {
 | 
			
		||||
	} else if (std::holds_alternative<EntryNotFound>(slot)) {
 | 
			
		||||
		qCCritical(logBare) << "Function not found.";
 | 
			
		||||
	} else if (std::holds_alternative<NoCurrentGeneration>(slot)) {
 | 
			
		||||
		qCCritical(logBare) << "Not ready to accept queries yet.";
 | 
			
		||||
| 
						 | 
				
			
			@ -233,4 +244,74 @@ int callFunction(
 | 
			
		|||
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct PropertyValue {
 | 
			
		||||
	QString value;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DEFINE_SIMPLE_DATASTREAM_OPS(PropertyValue, data.value);
 | 
			
		||||
 | 
			
		||||
using StringPropReadResponse =
 | 
			
		||||
    std::variant<std::monostate, NoCurrentGeneration, TargetNotFound, EntryNotFound, PropertyValue>;
 | 
			
		||||
 | 
			
		||||
void StringPropReadCommand::exec(qs::ipc::IpcServerConnection* conn) const {
 | 
			
		||||
	auto resp = conn->responseStream<StringPropReadResponse>();
 | 
			
		||||
 | 
			
		||||
	if (auto* generation = EngineGeneration::currentGeneration()) {
 | 
			
		||||
		auto* registry = IpcHandlerRegistry::forGeneration(generation);
 | 
			
		||||
 | 
			
		||||
		auto* handler = registry->findHandler(this->target);
 | 
			
		||||
		if (!handler) {
 | 
			
		||||
			resp << TargetNotFound();
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto* prop = handler->findProperty(this->property);
 | 
			
		||||
		if (!prop) {
 | 
			
		||||
			resp << EntryNotFound();
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto slot = IpcTypeSlot(prop->type);
 | 
			
		||||
		prop->read(handler, slot);
 | 
			
		||||
 | 
			
		||||
		resp << PropertyValue {
 | 
			
		||||
		    .value = slot.type()->toString(slot.get()),
 | 
			
		||||
		};
 | 
			
		||||
	} else {
 | 
			
		||||
		conn->respond(StringCallResponse(NoCurrentGeneration()));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int getProperty(IpcClient* client, const QString& target, const QString& property) {
 | 
			
		||||
	if (target.isEmpty()) {
 | 
			
		||||
		qCCritical(logBare) << "Target required to send message.";
 | 
			
		||||
		return -1;
 | 
			
		||||
	} else if (property.isEmpty()) {
 | 
			
		||||
		qCCritical(logBare) << "Property required to send message.";
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	client->sendMessage(IpcCommand(StringPropReadCommand {.target = target, .property = property}));
 | 
			
		||||
 | 
			
		||||
	StringPropReadResponse slot;
 | 
			
		||||
	if (!client->waitForResponse(slot)) return -1;
 | 
			
		||||
 | 
			
		||||
	if (std::holds_alternative<PropertyValue>(slot)) {
 | 
			
		||||
		auto& result = std::get<PropertyValue>(slot);
 | 
			
		||||
		QTextStream(stdout) << result.value << Qt::endl;
 | 
			
		||||
		return 0;
 | 
			
		||||
	} else if (std::holds_alternative<TargetNotFound>(slot)) {
 | 
			
		||||
		qCCritical(logBare) << "Target not found.";
 | 
			
		||||
	} else if (std::holds_alternative<EntryNotFound>(slot)) {
 | 
			
		||||
		qCCritical(logBare) << "Property not found.";
 | 
			
		||||
	} else if (std::holds_alternative<NoCurrentGeneration>(slot)) {
 | 
			
		||||
		qCCritical(logBare) << "Not ready to accept queries yet.";
 | 
			
		||||
	} else {
 | 
			
		||||
		qCCritical(logIpc) << "Received invalid IPC response from" << client;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace qs::io::ipc::comm
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@
 | 
			
		|||
 | 
			
		||||
#include <qcontainerfwd.h>
 | 
			
		||||
#include <qflags.h>
 | 
			
		||||
#include <qtypes.h>
 | 
			
		||||
 | 
			
		||||
#include "../ipc/ipc.hpp"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -9,12 +10,12 @@ namespace qs::io::ipc::comm {
 | 
			
		|||
 | 
			
		||||
struct QueryMetadataCommand {
 | 
			
		||||
	QString target;
 | 
			
		||||
	QString function;
 | 
			
		||||
	QString name;
 | 
			
		||||
 | 
			
		||||
	void exec(qs::ipc::IpcServerConnection* conn) const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DEFINE_SIMPLE_DATASTREAM_OPS(QueryMetadataCommand, data.target, data.function);
 | 
			
		||||
DEFINE_SIMPLE_DATASTREAM_OPS(QueryMetadataCommand, data.target, data.name);
 | 
			
		||||
 | 
			
		||||
struct StringCallCommand {
 | 
			
		||||
	QString target;
 | 
			
		||||
| 
						 | 
				
			
			@ -27,7 +28,7 @@ struct StringCallCommand {
 | 
			
		|||
DEFINE_SIMPLE_DATASTREAM_OPS(StringCallCommand, data.target, data.function, data.arguments);
 | 
			
		||||
 | 
			
		||||
void handleMsg(qs::ipc::IpcServerConnection* conn);
 | 
			
		||||
int queryMetadata(qs::ipc::IpcClient* client, const QString& target, const QString& function);
 | 
			
		||||
int queryMetadata(qs::ipc::IpcClient* client, const QString& target, const QString& name);
 | 
			
		||||
 | 
			
		||||
int callFunction(
 | 
			
		||||
    qs::ipc::IpcClient* client,
 | 
			
		||||
| 
						 | 
				
			
			@ -36,4 +37,15 @@ int callFunction(
 | 
			
		|||
    const QVector<QString>& arguments
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
struct StringPropReadCommand {
 | 
			
		||||
	QString target;
 | 
			
		||||
	QString property;
 | 
			
		||||
 | 
			
		||||
	void exec(qs::ipc::IpcServerConnection* conn) const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DEFINE_SIMPLE_DATASTREAM_OPS(StringPropReadCommand, data.target, data.property);
 | 
			
		||||
 | 
			
		||||
int getProperty(qs::ipc::IpcClient* client, const QString& target, const QString& property);
 | 
			
		||||
 | 
			
		||||
} // namespace qs::io::ipc::comm
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -107,6 +107,32 @@ WireFunctionDefinition IpcFunction::wireDef() const {
 | 
			
		|||
	return wire;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool IpcProperty::resolve(QString& error) {
 | 
			
		||||
	this->type = IpcType::ipcType(this->property.metaType());
 | 
			
		||||
 | 
			
		||||
	if (!this->type) {
 | 
			
		||||
		error = QString("Type %1 cannot be used across IPC.").arg(this->property.metaType().name());
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void IpcProperty::read(QObject* target, IpcTypeSlot& slot) const {
 | 
			
		||||
	slot.replace(this->property.read(target));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString IpcProperty::toString() const {
 | 
			
		||||
	return QString("property ") % this->property.name() % ": " % this->type->name();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
WirePropertyDefinition IpcProperty::wireDef() const {
 | 
			
		||||
	WirePropertyDefinition wire;
 | 
			
		||||
	wire.name = this->property.name();
 | 
			
		||||
	wire.type = this->type->name();
 | 
			
		||||
	return wire;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IpcCallStorage::IpcCallStorage(const IpcFunction& function): returnSlot(function.returnType) {
 | 
			
		||||
	for (const auto& arg: function.argumentTypes) {
 | 
			
		||||
		this->argumentSlots.emplace_back(arg);
 | 
			
		||||
| 
						 | 
				
			
			@ -153,6 +179,21 @@ void IpcHandler::onPostReload() {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (auto i = smeta.propertyCount(); i != meta->propertyCount(); i++) {
 | 
			
		||||
		const auto& property = meta->property(i);
 | 
			
		||||
		if (!property.isReadable() || !property.hasNotifySignal()) continue;
 | 
			
		||||
 | 
			
		||||
		auto ipcProp = IpcProperty(property);
 | 
			
		||||
		QString error;
 | 
			
		||||
 | 
			
		||||
		if (!ipcProp.resolve(error)) {
 | 
			
		||||
			qmlWarning(this).nospace().noquote()
 | 
			
		||||
			    << "Error parsing property \"" << property.name() << "\": " << error;
 | 
			
		||||
		} else {
 | 
			
		||||
			this->propertyMap.insert(property.name(), ipcProp);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this->complete = true;
 | 
			
		||||
	this->updateRegistration();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -270,6 +311,10 @@ WireTargetDefinition IpcHandler::wireDef() const {
 | 
			
		|||
		wire.functions += func.wireDef();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (const auto& prop: this->propertyMap.values()) {
 | 
			
		||||
		wire.properties += prop.wireDef();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return wire;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -307,6 +352,13 @@ IpcFunction* IpcHandler::findFunction(const QString& name) {
 | 
			
		|||
	else return &*itr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IpcProperty* IpcHandler::findProperty(const QString& name) {
 | 
			
		||||
	auto itr = this->propertyMap.find(name);
 | 
			
		||||
 | 
			
		||||
	if (itr == this->propertyMap.end()) return nullptr;
 | 
			
		||||
	else return &*itr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IpcHandler* IpcHandlerRegistry::findHandler(const QString& target) {
 | 
			
		||||
	return this->handlers.value(target);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -53,14 +53,28 @@ private:
 | 
			
		|||
	friend class IpcFunction;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class IpcProperty {
 | 
			
		||||
public:
 | 
			
		||||
	explicit IpcProperty(QMetaProperty property): property(property) {}
 | 
			
		||||
 | 
			
		||||
	bool resolve(QString& error);
 | 
			
		||||
	void read(QObject* target, IpcTypeSlot& slot) const;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] QString toString() const;
 | 
			
		||||
	[[nodiscard]] WirePropertyDefinition wireDef() const;
 | 
			
		||||
 | 
			
		||||
	QMetaProperty property;
 | 
			
		||||
	const IpcType* type = nullptr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class IpcHandlerRegistry;
 | 
			
		||||
 | 
			
		||||
///! Handler for IPC message calls.
 | 
			
		||||
/// Each IpcHandler is registered into a per-instance map by its unique @@target.
 | 
			
		||||
/// Functions defined on the IpcHandler can be called by `qs msg`.
 | 
			
		||||
/// Functions and properties defined on the IpcHandler can be accessed via `qs ipc`.
 | 
			
		||||
///
 | 
			
		||||
/// #### Handler Functions
 | 
			
		||||
/// IPC handler functions can be called by `qs msg` as long as they have at most 10
 | 
			
		||||
/// IPC handler functions can be called by `qs ipc call` as long as they have at most 10
 | 
			
		||||
/// arguments, and all argument types along with the return type are listed below.
 | 
			
		||||
///
 | 
			
		||||
/// **Argument and return types must be explicitly specified or they will not
 | 
			
		||||
| 
						 | 
				
			
			@ -112,9 +126,9 @@ class IpcHandlerRegistry;
 | 
			
		|||
///   }
 | 
			
		||||
/// }
 | 
			
		||||
/// ```
 | 
			
		||||
/// The list of registered targets can be inspected using `qs msg -s`.
 | 
			
		||||
/// The list of registered targets can be inspected using `qs ipc show`.
 | 
			
		||||
/// ```sh
 | 
			
		||||
/// $ qs msg -s
 | 
			
		||||
/// $ qs ipc show
 | 
			
		||||
/// target rect
 | 
			
		||||
///   function setColor(color: color): void
 | 
			
		||||
///   function getColor(): color
 | 
			
		||||
| 
						 | 
				
			
			@ -124,18 +138,22 @@ class IpcHandlerRegistry;
 | 
			
		|||
///   function getRadius(): int
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// and then invoked using `qs msg`.
 | 
			
		||||
/// and then invoked using `qs ipc call`.
 | 
			
		||||
/// ```sh
 | 
			
		||||
/// $ qs msg rect setColor orange
 | 
			
		||||
/// $ qs msg rect setAngle 40.5
 | 
			
		||||
/// $ qs msg rect setRadius 30
 | 
			
		||||
/// $ qs msg rect getColor
 | 
			
		||||
/// $ qs ipc call rect setColor orange
 | 
			
		||||
/// $ qs ipc call rect setAngle 40.5
 | 
			
		||||
/// $ qs ipc call rect setRadius 30
 | 
			
		||||
/// $ qs ipc call rect getColor
 | 
			
		||||
/// #ffffa500
 | 
			
		||||
/// $ qs msg rect getAngle
 | 
			
		||||
/// $ qs ipc call rect getAngle
 | 
			
		||||
/// 40.5
 | 
			
		||||
/// $ qs msg rect getRadius
 | 
			
		||||
/// $ qs ipc call rect getRadius
 | 
			
		||||
/// 30
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// #### Properties
 | 
			
		||||
/// Properties of an IpcHanlder can be read using `qs ipc prop get` as long as they are
 | 
			
		||||
/// of an IPC compatible type. See the table above for compatible types.
 | 
			
		||||
class IpcHandler
 | 
			
		||||
    : public QObject
 | 
			
		||||
    , public PostReloadHook {
 | 
			
		||||
| 
						 | 
				
			
			@ -162,12 +180,16 @@ public:
 | 
			
		|||
 | 
			
		||||
	QString listMembers(qsizetype indent);
 | 
			
		||||
	[[nodiscard]] IpcFunction* findFunction(const QString& name);
 | 
			
		||||
	[[nodiscard]] IpcProperty* findProperty(const QString& name);
 | 
			
		||||
	[[nodiscard]] WireTargetDefinition wireDef() const;
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
	void enabledChanged();
 | 
			
		||||
	void targetChanged();
 | 
			
		||||
 | 
			
		||||
private slots:
 | 
			
		||||
	//void handleIpcPropertyChange();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	void updateRegistration(bool destroying = false);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -183,6 +205,7 @@ private:
 | 
			
		|||
	bool complete = false;
 | 
			
		||||
 | 
			
		||||
	QHash<QString, IpcFunction> functionMap;
 | 
			
		||||
	QHash<QString, IpcProperty> propertyMap;
 | 
			
		||||
 | 
			
		||||
	friend class IpcHandlerRegistry;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,7 @@ using IpcCommand = std::variant<
 | 
			
		|||
    std::monostate,
 | 
			
		||||
    IpcKillCommand,
 | 
			
		||||
    qs::io::ipc::comm::QueryMetadataCommand,
 | 
			
		||||
    qs::io::ipc::comm::StringCallCommand>;
 | 
			
		||||
    qs::io::ipc::comm::StringCallCommand,
 | 
			
		||||
    qs::io::ipc::comm::StringPropReadCommand>;
 | 
			
		||||
 | 
			
		||||
} // namespace qs::ipc
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -293,6 +293,8 @@ int ipcCommand(CommandState& cmd) {
 | 
			
		|||
	return IpcClient::connect(instance.instance.instanceId, [&](IpcClient& client) {
 | 
			
		||||
		if (*cmd.ipc.show || cmd.ipc.showOld) {
 | 
			
		||||
			return qs::io::ipc::comm::queryMetadata(&client, *cmd.ipc.target, *cmd.ipc.name);
 | 
			
		||||
		} else if (*cmd.ipc.getprop) {
 | 
			
		||||
			return qs::io::ipc::comm::getProperty(&client, *cmd.ipc.target, *cmd.ipc.name);
 | 
			
		||||
		} else {
 | 
			
		||||
			QVector<QString> arguments;
 | 
			
		||||
			for (auto& arg: cmd.ipc.arguments) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -71,6 +71,7 @@ struct CommandState {
 | 
			
		|||
		CLI::App* ipc = nullptr;
 | 
			
		||||
		CLI::App* show = nullptr;
 | 
			
		||||
		CLI::App* call = nullptr;
 | 
			
		||||
		CLI::App* getprop = nullptr;
 | 
			
		||||
		bool showOld = false;
 | 
			
		||||
		QStringOption target;
 | 
			
		||||
		QStringOption name;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -194,6 +194,18 @@ int parseCommand(int argc, char** argv, CommandState& state) {
 | 
			
		|||
			    ->description("Arguments to the called function.")
 | 
			
		||||
			    ->allow_extra_args();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			auto* prop =
 | 
			
		||||
			    sub->add_subcommand("prop", "Manipulate IpcHandler properties.")->require_subcommand();
 | 
			
		||||
 | 
			
		||||
			{
 | 
			
		||||
				auto* get = prop->add_subcommand("get", "Read the value of a property.");
 | 
			
		||||
				state.ipc.getprop = get;
 | 
			
		||||
				get->add_option("target", state.ipc.target, "The target to read the property of.");
 | 
			
		||||
				get->add_option("property", state.ipc.name)->description("The property to read.");
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue