mirror of
https://github.com/zhuzichu520/FluentUI.git
synced 2024-11-27 05:38:37 +08:00
update
This commit is contained in:
parent
020a75ac93
commit
f7c17a27b6
@ -13,6 +13,7 @@ class AppInfo : public QObject
|
|||||||
public:
|
public:
|
||||||
explicit AppInfo(QObject *parent = nullptr);
|
explicit AppInfo(QObject *parent = nullptr);
|
||||||
Q_INVOKABLE void changeLang(const QString& locale);
|
Q_INVOKABLE void changeLang(const QString& locale);
|
||||||
|
Q_SIGNAL void activeWindow();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // APPINFO_H
|
#endif // APPINFO_H
|
||||||
|
@ -30,6 +30,7 @@ set(SOURCES
|
|||||||
lang/Lang.cpp
|
lang/Lang.cpp
|
||||||
lang/Zh.cpp
|
lang/Zh.cpp
|
||||||
lang/En.cpp
|
lang/En.cpp
|
||||||
|
IPC.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
@ -39,6 +40,7 @@ set(HEADERS
|
|||||||
lang/Lang.h
|
lang/Lang.h
|
||||||
lang/Zh.h
|
lang/Zh.h
|
||||||
lang/En.h
|
lang/En.h
|
||||||
|
IPC.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(RESOURCES
|
set(RESOURCES
|
||||||
|
255
example/IPC.cpp
Normal file
255
example/IPC.cpp
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
#include "IPC.h"
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QThread>
|
||||||
|
#include <ctime>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
IPC::IPC(uint32_t profileId)
|
||||||
|
: profileId{profileId}
|
||||||
|
, globalMemory{"ipc-" IPC_PROTOCOL_VERSION}
|
||||||
|
{
|
||||||
|
qRegisterMetaType<IPCEventHandler>("IPCEventHandler");
|
||||||
|
timer.setInterval(EVENT_TIMER_MS);
|
||||||
|
timer.setSingleShot(true);
|
||||||
|
connect(&timer, &QTimer::timeout, this, &IPC::processEvents);
|
||||||
|
std::default_random_engine randEngine((std::random_device())());
|
||||||
|
std::uniform_int_distribution<uint64_t> distribution;
|
||||||
|
globalId = distribution(randEngine);
|
||||||
|
qDebug() << "Our global IPC ID is " << globalId;
|
||||||
|
if (globalMemory.create(sizeof(IPCMemory))) {
|
||||||
|
if (globalMemory.lock()) {
|
||||||
|
IPCMemory* mem = global();
|
||||||
|
memset(mem, 0, sizeof(IPCMemory));
|
||||||
|
mem->globalId = globalId;
|
||||||
|
mem->lastProcessed = time(nullptr);
|
||||||
|
globalMemory.unlock();
|
||||||
|
} else {
|
||||||
|
qWarning() << "Couldn't lock to take ownership";
|
||||||
|
}
|
||||||
|
} else if (globalMemory.attach()) {
|
||||||
|
qDebug() << "Attaching to the global shared memory";
|
||||||
|
} else {
|
||||||
|
qDebug() << "Failed to attach to the global shared memory, giving up. Error:"
|
||||||
|
<< globalMemory.error();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
processEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::~IPC()
|
||||||
|
{
|
||||||
|
if (!globalMemory.lock()) {
|
||||||
|
qWarning() << "Failed to lock in ~IPC";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCurrentOwnerNoLock()) {
|
||||||
|
global()->globalId = 0;
|
||||||
|
}
|
||||||
|
globalMemory.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t IPC::postEvent(const QString& name, const QByteArray& data, uint32_t dest)
|
||||||
|
{
|
||||||
|
QByteArray binName = name.toUtf8();
|
||||||
|
if (binName.length() > (int32_t)sizeof(IPCEvent::name)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.length() > (int32_t)sizeof(IPCEvent::data)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!globalMemory.lock()) {
|
||||||
|
qDebug() << "Failed to lock in postEvent()";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCEvent* evt = nullptr;
|
||||||
|
IPCMemory* mem = global();
|
||||||
|
time_t result = 0;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; !evt && i < EVENT_QUEUE_SIZE; ++i) {
|
||||||
|
if (mem->events[i].posted == 0) {
|
||||||
|
evt = &mem->events[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evt) {
|
||||||
|
memset(evt, 0, sizeof(IPCEvent));
|
||||||
|
memcpy(evt->name, binName.constData(), binName.length());
|
||||||
|
memcpy(evt->data, data.constData(), data.length());
|
||||||
|
mem->lastEvent = evt->posted = result = qMax(mem->lastEvent + 1, time(nullptr));
|
||||||
|
evt->dest = dest;
|
||||||
|
evt->sender = GetCurrentProcessId();
|
||||||
|
qDebug() << "postEvent " << name << "to" << dest;
|
||||||
|
}
|
||||||
|
globalMemory.unlock();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPC::isCurrentOwner()
|
||||||
|
{
|
||||||
|
if (globalMemory.lock()) {
|
||||||
|
const bool isOwner = isCurrentOwnerNoLock();
|
||||||
|
globalMemory.unlock();
|
||||||
|
return isOwner;
|
||||||
|
} else {
|
||||||
|
qWarning() << "isCurrentOwner failed to lock, returning false";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IPC::registerEventHandler(const QString& name, IPCEventHandler handler)
|
||||||
|
{
|
||||||
|
eventHandlers[name] = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPC::isEventAccepted(time_t time)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
if (!globalMemory.lock()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (difftime(global()->lastProcessed, time) > 0) {
|
||||||
|
IPCMemory* mem = global();
|
||||||
|
for (uint32_t i = 0; i < EVENT_QUEUE_SIZE; ++i) {
|
||||||
|
if (mem->events[i].posted == time && mem->events[i].processed) {
|
||||||
|
result = mem->events[i].accepted;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
globalMemory.unlock();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPC::waitUntilAccepted(time_t postTime, int32_t timeout /*=-1*/)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
time_t start = time(nullptr);
|
||||||
|
forever
|
||||||
|
{
|
||||||
|
result = isEventAccepted(postTime);
|
||||||
|
if (result || (timeout > 0 && difftime(time(nullptr), start) >= timeout)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
qApp->processEvents();
|
||||||
|
QThread::msleep(0);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPC::isAttached() const
|
||||||
|
{
|
||||||
|
return globalMemory.isAttached();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IPC::setProfileId(uint32_t profileId)
|
||||||
|
{
|
||||||
|
this->profileId = profileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::IPCEvent* IPC::fetchEvent()
|
||||||
|
{
|
||||||
|
IPCMemory* mem = global();
|
||||||
|
for (uint32_t i = 0; i < EVENT_QUEUE_SIZE; ++i) {
|
||||||
|
IPCEvent* evt = &mem->events[i];
|
||||||
|
if ((evt->processed && difftime(time(nullptr), evt->processed) > EVENT_GC_TIMEOUT)
|
||||||
|
|| (!evt->processed && difftime(time(nullptr), evt->posted) > EVENT_GC_TIMEOUT)) {
|
||||||
|
memset(evt, 0, sizeof(IPCEvent));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evt->posted && !evt->processed && evt->sender != GetCurrentProcessId()
|
||||||
|
&& (evt->dest == profileId || (evt->dest == 0 && isCurrentOwnerNoLock()))) {
|
||||||
|
return evt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPC::runEventHandler(IPCEventHandler handler, const QByteArray& arg)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
if (QThread::currentThread() == qApp->thread()) {
|
||||||
|
result = handler(arg);
|
||||||
|
} else {
|
||||||
|
QMetaObject::invokeMethod(this, "runEventHandler", Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(bool, result), Q_ARG(IPCEventHandler, handler),
|
||||||
|
Q_ARG(const QByteArray&, arg));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IPC::processEvents()
|
||||||
|
{
|
||||||
|
if (!globalMemory.lock()) {
|
||||||
|
timer.start();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCMemory* mem = global();
|
||||||
|
|
||||||
|
if (mem->globalId == globalId) {
|
||||||
|
mem->lastProcessed = time(nullptr);
|
||||||
|
} else {
|
||||||
|
if (difftime(time(nullptr), mem->lastProcessed) >= OWNERSHIP_TIMEOUT_S) {
|
||||||
|
qDebug() << "Previous owner timed out, taking ownership" << mem->globalId << "->"
|
||||||
|
<< globalId;
|
||||||
|
memset(mem, 0, sizeof(IPCMemory));
|
||||||
|
mem->globalId = globalId;
|
||||||
|
mem->lastProcessed = time(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (IPCEvent* evt = fetchEvent()) {
|
||||||
|
QString name = QString::fromUtf8(evt->name);
|
||||||
|
auto it = eventHandlers.find(name);
|
||||||
|
if (it != eventHandlers.end()) {
|
||||||
|
evt->accepted = runEventHandler(it.value(), evt->data);
|
||||||
|
qDebug() << "Processed event:" << name << "posted:" << evt->posted
|
||||||
|
<< "accepted:" << evt->accepted;
|
||||||
|
if (evt->dest == 0) {
|
||||||
|
if (evt->accepted) {
|
||||||
|
evt->processed = time(nullptr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
evt->processed = time(nullptr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << "Received event:" << name << "without handler";
|
||||||
|
qDebug() << "Available handlers:" << eventHandlers.keys();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
globalMemory.unlock();
|
||||||
|
timer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPC::isCurrentOwnerNoLock()
|
||||||
|
{
|
||||||
|
const void* const data = globalMemory.data();
|
||||||
|
if (!data) {
|
||||||
|
qWarning() << "isCurrentOwnerNoLock failed to access the memory, returning false";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (*static_cast<const uint64_t*>(data) == globalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::IPCMemory* IPC::global()
|
||||||
|
{
|
||||||
|
return static_cast<IPCMemory*>(globalMemory.data());
|
||||||
|
}
|
75
example/IPC.h
Normal file
75
example/IPC.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#ifndef IPC_H
|
||||||
|
#define IPC_H
|
||||||
|
|
||||||
|
#include <QMap>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSharedMemory>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <ctime>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
using IPCEventHandler = std::function<bool(const QByteArray&)>;
|
||||||
|
|
||||||
|
#define IPC_PROTOCOL_VERSION "1"
|
||||||
|
|
||||||
|
class IPC : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static const int EVENT_TIMER_MS = 1000;
|
||||||
|
static const int EVENT_GC_TIMEOUT = 5;
|
||||||
|
static const int EVENT_QUEUE_SIZE = 32;
|
||||||
|
static const int OWNERSHIP_TIMEOUT_S = 5;
|
||||||
|
|
||||||
|
public:
|
||||||
|
IPC(uint32_t profileId);
|
||||||
|
~IPC();
|
||||||
|
|
||||||
|
struct IPCEvent
|
||||||
|
{
|
||||||
|
uint32_t dest;
|
||||||
|
int32_t sender;
|
||||||
|
char name[16];
|
||||||
|
char data[128];
|
||||||
|
time_t posted;
|
||||||
|
time_t processed;
|
||||||
|
uint32_t flags;
|
||||||
|
bool accepted;
|
||||||
|
bool global;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IPCMemory
|
||||||
|
{
|
||||||
|
uint64_t globalId;
|
||||||
|
time_t lastEvent;
|
||||||
|
time_t lastProcessed;
|
||||||
|
IPCEvent events[IPC::EVENT_QUEUE_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
time_t postEvent(const QString& name, const QByteArray& data = QByteArray(), uint32_t dest = 0);
|
||||||
|
bool isCurrentOwner();
|
||||||
|
void registerEventHandler(const QString& name, IPCEventHandler handler);
|
||||||
|
bool isEventAccepted(time_t time);
|
||||||
|
bool waitUntilAccepted(time_t time, int32_t timeout = -1);
|
||||||
|
bool isAttached() const;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setProfileId(uint32_t profileId);
|
||||||
|
|
||||||
|
private:
|
||||||
|
IPCMemory* global();
|
||||||
|
bool runEventHandler(IPCEventHandler handler, const QByteArray& arg);
|
||||||
|
IPCEvent* fetchEvent();
|
||||||
|
void processEvents();
|
||||||
|
bool isCurrentOwnerNoLock();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTimer timer;
|
||||||
|
uint64_t globalId;
|
||||||
|
uint32_t profileId;
|
||||||
|
QSharedMemory globalMemory;
|
||||||
|
QMap<QString, IPCEventHandler> eventHandlers;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // IPC_H
|
@ -8,7 +8,8 @@ HEADERS += \
|
|||||||
lang/Zh.h \
|
lang/Zh.h \
|
||||||
stdafx.h \
|
stdafx.h \
|
||||||
ChatController.h \
|
ChatController.h \
|
||||||
AppInfo.h
|
AppInfo.h \
|
||||||
|
IPC.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
ChatController.cpp \
|
ChatController.cpp \
|
||||||
@ -16,7 +17,8 @@ SOURCES += \
|
|||||||
lang/En.cpp \
|
lang/En.cpp \
|
||||||
lang/Lang.cpp \
|
lang/Lang.cpp \
|
||||||
lang/Zh.cpp \
|
lang/Zh.cpp \
|
||||||
main.cpp
|
main.cpp \
|
||||||
|
IPC.cpp
|
||||||
|
|
||||||
RESOURCES += qml.qrc
|
RESOURCES += qml.qrc
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "lang/Lang.h"
|
#include "lang/Lang.h"
|
||||||
#include "AppInfo.h"
|
#include "AppInfo.h"
|
||||||
#include "ChatController.h"
|
#include "ChatController.h"
|
||||||
|
#include "IPC.h"
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
@ -19,12 +20,25 @@ int main(int argc, char *argv[])
|
|||||||
QGuiApplication::setOrganizationName("ZhuZiChu");
|
QGuiApplication::setOrganizationName("ZhuZiChu");
|
||||||
QGuiApplication::setOrganizationDomain("https://zhuzichu520.github.io");
|
QGuiApplication::setOrganizationDomain("https://zhuzichu520.github.io");
|
||||||
QGuiApplication::setApplicationName("FluentUI");
|
QGuiApplication::setApplicationName("FluentUI");
|
||||||
// QQuickWindow::setGraphicsApi(QSGRendererInterface::Software);
|
// QQuickWindow::setGraphicsApi(QSGRendererInterface::Software);
|
||||||
QGuiApplication app(argc, argv);
|
QGuiApplication app(argc, argv);
|
||||||
|
AppInfo* appInfo = new AppInfo();
|
||||||
|
IPC ipc(0);
|
||||||
|
QString activeWindowEvent = "activeWindow";
|
||||||
|
if(!ipc.isCurrentOwner()){
|
||||||
|
ipc.postEvent(activeWindowEvent,QString().toUtf8(),0);
|
||||||
|
delete appInfo;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(ipc.isAttached()){
|
||||||
|
ipc.registerEventHandler(activeWindowEvent,[&appInfo](const QByteArray& data){
|
||||||
|
Q_EMIT appInfo->activeWindow();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
app.setQuitOnLastWindowClosed(false);
|
app.setQuitOnLastWindowClosed(false);
|
||||||
QQmlApplicationEngine engine;
|
QQmlApplicationEngine engine;
|
||||||
qmlRegisterType<ChatController>("Controller",1,0,"ChatController");
|
qmlRegisterType<ChatController>("Controller",1,0,"ChatController");
|
||||||
AppInfo* appInfo = new AppInfo();
|
|
||||||
QQmlContext * context = engine.rootContext();
|
QQmlContext * context = engine.rootContext();
|
||||||
Lang* lang = appInfo->lang();
|
Lang* lang = appInfo->lang();
|
||||||
context->setContextProperty("lang",lang);
|
context->setContextProperty("lang",lang);
|
||||||
|
@ -20,6 +20,15 @@ FluWindow {
|
|||||||
event.accepted = false
|
event.accepted = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections{
|
||||||
|
target: appInfo
|
||||||
|
function onActiveWindow(){
|
||||||
|
window.show()
|
||||||
|
window.raise()
|
||||||
|
window.requestActivate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FluAppBar{
|
FluAppBar{
|
||||||
id:appbar
|
id:appbar
|
||||||
z:9
|
z:9
|
||||||
|
Loading…
Reference in New Issue
Block a user