mirror of
https://github.com/zhuzichu520/FluentUI.git
synced 2025-07-02 08:05:29 +08:00
update
This commit is contained in:
@ -9,7 +9,7 @@ Window {
|
||||
flags: Qt.SplashScreen
|
||||
Component.onCompleted: {
|
||||
FluApp.init(app)
|
||||
FluTheme.darkMode = FluDarkMode.System
|
||||
FluTheme.darkMode = FluThemeType.System
|
||||
FluTheme.enableAnimation = true
|
||||
FluApp.routes = {
|
||||
"/":"qrc:/example/qml/window/MainWindow.qml",
|
||||
|
@ -5,5 +5,5 @@ import QtQuick.Controls
|
||||
import FluentUI
|
||||
|
||||
QtObject {
|
||||
property int displayMode : FluNavigationView.Auto
|
||||
property int displayMode : FluNavigationViewType.Auto
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ FluScrollablePage{
|
||||
title:"友情提示"
|
||||
message:"确定要退出程序么?"
|
||||
negativeText:"取消"
|
||||
buttonFlags: FluContentDialog.NegativeButton | FluContentDialog.PositiveButton
|
||||
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
|
||||
onNegativeClicked:{
|
||||
showSuccess("点击取消按钮")
|
||||
}
|
||||
@ -47,7 +47,7 @@ FluScrollablePage{
|
||||
id:double_btn_dialog
|
||||
title:"友情提示"
|
||||
message:"确定要退出程序么?"
|
||||
buttonFlags: FluContentDialog.NegativeButton | FluContentDialog.PositiveButton
|
||||
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
|
||||
negativeText:"取消"
|
||||
onNegativeClicked:{
|
||||
showSuccess("点击取消按钮")
|
||||
@ -80,7 +80,7 @@ FluScrollablePage{
|
||||
title:"友情提示"
|
||||
message:"确定要退出程序么?"
|
||||
negativeText:"取消"
|
||||
buttonFlags: FluContentDialog.NeutralButton | FluContentDialog.NegativeButton | FluContentDialog.PositiveButton
|
||||
buttonFlags: FluContentDialogType.NeutralButton | FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
|
||||
negativeText:"取消"
|
||||
onNegativeClicked:{
|
||||
showSuccess("点击取消按钮")
|
||||
@ -101,7 +101,7 @@ FluScrollablePage{
|
||||
id:triple_btn_dialog
|
||||
title:"友情提示"
|
||||
message:"确定要退出程序么?"
|
||||
buttonFlags: FluContentDialog.NeutralButton | FluContentDialog.NegativeButton | FluContentDialog.PositiveButton
|
||||
buttonFlags: FluContentDialogType.NeutralButton | FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
|
||||
negativeText:"取消"
|
||||
onNegativeClicked:{
|
||||
showSuccess("点击取消按钮")
|
||||
|
@ -7,7 +7,7 @@ import FluentUI
|
||||
|
||||
FluScrollablePage{
|
||||
|
||||
launchMode: FluPage.SingleTask
|
||||
launchMode: FluPageType.SingleTask
|
||||
animDisabled: true
|
||||
|
||||
ListModel{
|
||||
|
@ -93,9 +93,9 @@ FluScrollablePage{
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -1
|
||||
code:'FluWindow{
|
||||
//launchMode: FluWindow.Standard
|
||||
//launchMode: FluWindow.SingleTask
|
||||
launchMode: FluWindow.SingleInstance
|
||||
//launchMode: FluWindowType.Standard
|
||||
//launchMode: FluWindowType.SingleTask
|
||||
launchMode: FluWindowType.SingleInstance
|
||||
}
|
||||
'
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import FluentUI
|
||||
import "qrc:///example/qml/component"
|
||||
|
||||
FluPage{
|
||||
launchMode: FluPage.SingleTop
|
||||
launchMode: FluPageType.SingleTop
|
||||
FluRemoteLoader{
|
||||
anchors.fill: parent
|
||||
source: "https://zhu-zichu.gitee.io/T_RemoteLoader.qml"
|
||||
|
@ -28,7 +28,7 @@ FluScrollablePage{
|
||||
Layout.bottomMargin: 4
|
||||
}
|
||||
Repeater{
|
||||
model: [{title:"System",mode:FluDarkMode.System},{title:"Light",mode:FluDarkMode.Light},{title:"Dark",mode:FluDarkMode.Dark}]
|
||||
model: [{title:"System",mode:FluThemeType.System},{title:"Light",mode:FluThemeType.Light},{title:"Dark",mode:FluThemeType.Dark}]
|
||||
delegate: FluRadioButton{
|
||||
checked : FluTheme.darkMode === modelData.mode
|
||||
text:modelData.title
|
||||
@ -58,7 +58,7 @@ FluScrollablePage{
|
||||
Layout.bottomMargin: 4
|
||||
}
|
||||
Repeater{
|
||||
model: [{title:"Open",mode:FluNavigationView.Open},{title:"Compact",mode:FluNavigationView.Compact},{title:"Minimal",mode:FluNavigationView.Minimal},{title:"Auto",mode:FluNavigationView.Auto}]
|
||||
model: [{title:"Open",mode:FluNavigationViewType.Open},{title:"Compact",mode:FluNavigationViewType.Compact},{title:"Minimal",mode:FluNavigationViewType.Minimal},{title:"Auto",mode:FluNavigationViewType.Auto}]
|
||||
delegate: FluRadioButton{
|
||||
checked : MainEvent.displayMode===modelData.mode
|
||||
text:modelData.title
|
||||
|
@ -25,28 +25,28 @@ FluScrollablePage{
|
||||
text:"Loading"
|
||||
onClicked: {
|
||||
btn_status_mode.text = text
|
||||
status_view.statusMode = FluStatusView.Loading
|
||||
status_view.statusMode = FluStatusViewType.Loading
|
||||
}
|
||||
}
|
||||
FluMenuItem{
|
||||
text:"Empty"
|
||||
onClicked: {
|
||||
btn_status_mode.text = text
|
||||
status_view.statusMode = FluStatusView.Empty
|
||||
status_view.statusMode = FluStatusViewType.Empty
|
||||
}
|
||||
}
|
||||
FluMenuItem{
|
||||
text:"Error"
|
||||
onClicked: {
|
||||
btn_status_mode.text = text
|
||||
status_view.statusMode = FluStatusView.Error
|
||||
status_view.statusMode = FluStatusViewType.Error
|
||||
}
|
||||
}
|
||||
FluMenuItem{
|
||||
text:"Success"
|
||||
onClicked: {
|
||||
btn_status_mode.text = text
|
||||
status_view.statusMode = FluStatusView.Success
|
||||
status_view.statusMode = FluStatusViewType.Success
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -75,7 +75,7 @@ FluScrollablePage{
|
||||
Layout.topMargin: -1
|
||||
code:'FluStatusView{
|
||||
anchors.fill: parent
|
||||
statusMode: FluStatusView.Loading
|
||||
statusMode: FluStatusViewType.Loading
|
||||
Rectangle{
|
||||
anchors.fill: parent
|
||||
color:FluTheme.primaryColor.dark
|
||||
|
@ -44,21 +44,21 @@ FluScrollablePage{
|
||||
text:"Equal"
|
||||
onClicked: {
|
||||
btn_tab_width_behavior.text = text
|
||||
tab_view.tabWidthBehavior = FluTabView.Equal
|
||||
tab_view.tabWidthBehavior = FluTabViewType.Equal
|
||||
}
|
||||
}
|
||||
FluMenuItem{
|
||||
text:"SizeToContent"
|
||||
onClicked: {
|
||||
btn_tab_width_behavior.text = text
|
||||
tab_view.tabWidthBehavior = FluTabView.SizeToContent
|
||||
tab_view.tabWidthBehavior = FluTabViewType.SizeToContent
|
||||
}
|
||||
}
|
||||
FluMenuItem{
|
||||
text:"Compact"
|
||||
onClicked: {
|
||||
btn_tab_width_behavior.text = text
|
||||
tab_view.tabWidthBehavior = FluTabView.Compact
|
||||
tab_view.tabWidthBehavior = FluTabViewType.Compact
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -70,21 +70,21 @@ FluScrollablePage{
|
||||
text:"Nerver"
|
||||
onClicked: {
|
||||
btn_close_button_visibility.text = text
|
||||
tab_view.closeButtonVisibility = FluTabView.Nerver
|
||||
tab_view.closeButtonVisibility = FluTabViewType.Nerver
|
||||
}
|
||||
}
|
||||
FluMenuItem{
|
||||
text:"Always"
|
||||
onClicked: {
|
||||
btn_close_button_visibility.text = text
|
||||
tab_view.closeButtonVisibility = FluTabView.Always
|
||||
tab_view.closeButtonVisibility = FluTabViewType.Always
|
||||
}
|
||||
}
|
||||
FluMenuItem{
|
||||
text:"OnHover"
|
||||
onClicked: {
|
||||
btn_close_button_visibility.text = text
|
||||
tab_view.closeButtonVisibility = FluTabView.OnHover
|
||||
tab_view.closeButtonVisibility = FluTabViewType.OnHover
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import "qrc:///example/qml/component"
|
||||
|
||||
FluScrollablePage{
|
||||
|
||||
launchMode: FluPage.SingleInstance
|
||||
launchMode: FluPageType.SingleInstance
|
||||
|
||||
title:"TextBox"
|
||||
FluArea{
|
||||
|
@ -55,9 +55,9 @@ FluScrollablePage{
|
||||
checked: FluTheme.dark
|
||||
onClicked: {
|
||||
if(FluTheme.dark){
|
||||
FluTheme.darkMode = FluDarkMode.Light
|
||||
FluTheme.darkMode = FluThemeType.Light
|
||||
}else{
|
||||
FluTheme.darkMode = FluDarkMode.Dark
|
||||
FluTheme.darkMode = FluThemeType.Dark
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import "qrc:///example/qml/component"
|
||||
FluScrollablePage{
|
||||
|
||||
title:"TimePicker"
|
||||
launchMode: FluPage.SingleInstance
|
||||
launchMode: FluPageType.SingleInstance
|
||||
FluArea{
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
@ -23,7 +23,7 @@ FluScrollablePage{
|
||||
}
|
||||
|
||||
FluText{
|
||||
text:"hourFormat=FluTimePicker.H"
|
||||
text:"hourFormat=FluTimePickerType.H"
|
||||
}
|
||||
|
||||
FluTimePicker{
|
||||
@ -56,11 +56,11 @@ FluScrollablePage{
|
||||
}
|
||||
|
||||
FluText{
|
||||
text:"hourFormat=FluTimePicker.HH"
|
||||
text:"hourFormat=FluTimePickerType.HH"
|
||||
}
|
||||
|
||||
FluTimePicker{
|
||||
hourFormat:FluTimePicker.HH
|
||||
hourFormat:FluTimePickerType.HH
|
||||
onCurrentChanged: {
|
||||
showSuccess(current.toLocaleTimeString(Qt.locale("de_DE")))
|
||||
}
|
||||
@ -72,7 +72,7 @@ FluScrollablePage{
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -1
|
||||
code:'FluTimePicker{
|
||||
hourFormat:FluTimePicker.HH
|
||||
hourFormat:FluTimePickerType.HH
|
||||
}'
|
||||
}
|
||||
|
||||
|
@ -64,38 +64,38 @@ FluScrollablePage {
|
||||
text:"None"
|
||||
onClicked: {
|
||||
btn_selection_model.text = text
|
||||
tree_view.selectionMode = FluTabView.Equal
|
||||
tree_view.selectionMode = FluTabViewType.Equal
|
||||
}
|
||||
}
|
||||
FluMenuItem{
|
||||
text:"Single"
|
||||
onClicked: {
|
||||
btn_selection_model.text = text
|
||||
tree_view.selectionMode = FluTabView.SizeToContent
|
||||
tree_view.selectionMode = FluTabViewType.SizeToContent
|
||||
}
|
||||
}
|
||||
FluMenuItem{
|
||||
text:"Muiltple"
|
||||
onClicked: {
|
||||
btn_selection_model.text = text
|
||||
tree_view.selectionMode = FluTabView.Compact
|
||||
tree_view.selectionMode = FluTabViewType.Compact
|
||||
}
|
||||
}
|
||||
}
|
||||
FluFilledButton{
|
||||
text:"获取选中的数据"
|
||||
onClicked: {
|
||||
if(tree_view.selectionMode === FluTreeView.None){
|
||||
if(tree_view.selectionMode === FluTreeViewType.None){
|
||||
showError("当前非选择模式,没有选中的数据")
|
||||
}
|
||||
if(tree_view.selectionMode === FluTreeView.Single){
|
||||
if(tree_view.selectionMode === FluTreeViewType.Single){
|
||||
if(!tree_view.signleData()){
|
||||
showError("没有选中数据")
|
||||
return
|
||||
}
|
||||
showSuccess(tree_view.signleData().text)
|
||||
}
|
||||
if(tree_view.selectionMode === FluTreeView.Multiple){
|
||||
if(tree_view.selectionMode === FluTreeViewType.Multiple){
|
||||
if(tree_view.multipData().length===0){
|
||||
showError("没有选中数据")
|
||||
return
|
||||
|
@ -11,7 +11,7 @@ CustomWindow {
|
||||
width: 600
|
||||
height: 600
|
||||
fixSize: true
|
||||
launchMode: FluWindow.SingleTask
|
||||
launchMode: FluWindowType.SingleTask
|
||||
|
||||
ColumnLayout{
|
||||
anchors{
|
||||
|
@ -13,7 +13,7 @@ CustomWindow {
|
||||
height: 600
|
||||
minimumWidth: 520
|
||||
minimumHeight: 200
|
||||
launchMode: FluWindow.SingleTask
|
||||
launchMode: FluWindowType.SingleTask
|
||||
FileWatcher{
|
||||
id:watcher
|
||||
onFileChanged: {
|
||||
@ -25,7 +25,7 @@ CustomWindow {
|
||||
FluRemoteLoader{
|
||||
id:loader
|
||||
anchors.fill: parent
|
||||
statusMode: FluStatusView.Success
|
||||
statusMode: FluStatusViewType.Success
|
||||
lazy: true
|
||||
errorItem: Item{
|
||||
FluText{
|
||||
@ -43,7 +43,7 @@ CustomWindow {
|
||||
text:"拖入qml文件"
|
||||
font.pixelSize: 26
|
||||
anchors.centerIn: parent
|
||||
visible: !loader.itemLodaer().item && loader.statusMode === FluStatusView.Success
|
||||
visible: !loader.itemLodaer().item && loader.statusMode === FluStatusViewType.Success
|
||||
}
|
||||
Rectangle{
|
||||
radius: 4
|
||||
|
@ -18,7 +18,7 @@ CustomWindow {
|
||||
minimumWidth: 520
|
||||
minimumHeight: 200
|
||||
appBarVisible: false
|
||||
launchMode: FluWindow.SingleTask
|
||||
launchMode: FluWindowType.SingleTask
|
||||
|
||||
closeFunc:function(event){
|
||||
dialog_close.open()
|
||||
@ -29,15 +29,6 @@ CustomWindow {
|
||||
FluTools.setQuitOnLastWindowClosed(false)
|
||||
}
|
||||
|
||||
Connections{
|
||||
target: appInfo
|
||||
function onActiveWindow(){
|
||||
window.show()
|
||||
window.raise()
|
||||
window.requestActivate()
|
||||
}
|
||||
}
|
||||
|
||||
SystemTrayIcon {
|
||||
id:system_tray
|
||||
visible: true
|
||||
@ -67,7 +58,7 @@ CustomWindow {
|
||||
title:"退出"
|
||||
message:"确定要退出程序吗?"
|
||||
negativeText:"最小化"
|
||||
buttonFlags: FluContentDialog.NeutralButton | FluContentDialog.NegativeButton | FluContentDialog.PositiveButton
|
||||
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.NeutralButton | FluContentDialogType.PositiveButton
|
||||
onNegativeClicked:{
|
||||
window.hide()
|
||||
system_tray.showMessage("友情提示","FluentUI已隐藏至托盘,点击托盘可再次激活窗口");
|
||||
@ -173,9 +164,9 @@ CustomWindow {
|
||||
height: parent.height
|
||||
z:999
|
||||
//Stack模式,每次切换都会将页面压入栈中,随着栈的页面增多,消耗的内存也越多,内存消耗多就会卡顿,这时候就需要按返回将页面pop掉,释放内存。该模式可以配合FluPage中的launchMode属性,设置页面的启动模式
|
||||
// pageMode: FluNavigationView.Stack
|
||||
// pageMode: FluNavigationViewType.Stack
|
||||
//NoStack模式,每次切换都会销毁之前的页面然后创建一个新的页面,只需消耗少量内存(推荐)
|
||||
// pageMode: FluNavigationView.NoStack
|
||||
// pageMode: FluNavigationViewType.NoStack
|
||||
items: ItemsOriginal
|
||||
footerItems:ItemsFooter
|
||||
topPadding:FluTools.isMacos() ? 20 : 5
|
||||
@ -238,9 +229,9 @@ CustomWindow {
|
||||
|
||||
function changeDark(){
|
||||
if(FluTheme.dark){
|
||||
FluTheme.darkMode = FluDarkMode.Light
|
||||
FluTheme.darkMode = FluThemeType.Light
|
||||
}else{
|
||||
FluTheme.darkMode = FluDarkMode.Dark
|
||||
FluTheme.darkMode = FluThemeType.Dark
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ CustomWindow {
|
||||
width: 500
|
||||
height: 600
|
||||
fixSize: true
|
||||
launchMode: FluWindow.SingleInstance
|
||||
launchMode: FluWindowType.SingleInstance
|
||||
|
||||
FluTextBox{
|
||||
anchors{
|
||||
|
@ -11,7 +11,7 @@ CustomWindow {
|
||||
width: 500
|
||||
height: 600
|
||||
fixSize: true
|
||||
launchMode: FluWindow.SingleTask
|
||||
launchMode: FluWindowType.SingleTask
|
||||
|
||||
FluText{
|
||||
anchors.centerIn: parent
|
||||
|
@ -11,7 +11,7 @@ CustomWindow {
|
||||
width: 500
|
||||
height: 600
|
||||
fixSize: true
|
||||
launchMode: FluWindow.Standard
|
||||
launchMode: FluWindowType.Standard
|
||||
|
||||
FluMenuBar {
|
||||
FluMenu {
|
||||
|
@ -39,18 +39,3 @@ void AppInfo::changeLang(const QString& locale){
|
||||
lang(new En());
|
||||
}
|
||||
}
|
||||
|
||||
bool AppInfo::isOwnerProcess(IPC *ipc){
|
||||
QString activeWindowEvent = "activeWindow";
|
||||
if(!ipc->isCurrentOwner()){
|
||||
ipc->postEvent(activeWindowEvent,QString().toUtf8(),0);
|
||||
return false;
|
||||
}
|
||||
if(ipc->isAttached()){
|
||||
ipc->registerEventHandler(activeWindowEvent,[=](const QByteArray&){
|
||||
Q_EMIT this->activeWindow();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include "tool/IPC.h"
|
||||
#include "lang/Lang.h"
|
||||
#include "stdafx.h"
|
||||
|
||||
@ -15,9 +14,7 @@ class AppInfo : public QObject
|
||||
public:
|
||||
explicit AppInfo(QObject *parent = nullptr);
|
||||
void init(QQmlApplicationEngine *engine);
|
||||
bool isOwnerProcess(IPC *ipc);
|
||||
Q_INVOKABLE void changeLang(const QString& locale);
|
||||
Q_SIGNAL void activeWindow();
|
||||
};
|
||||
|
||||
#endif // APPINFO_H
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
FRAMELESSHELPER_USE_NAMESPACE
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
//将样式设置为Basic,不然会导致组件显示异常
|
||||
qputenv("QT_QUICK_CONTROLS_STYLE","Basic");
|
||||
@ -32,10 +32,6 @@ FRAMELESSHELPER_USE_NAMESPACE
|
||||
FramelessConfig::instance()->set(Global::Option::ForceNonNativeBackgroundBlur,false);
|
||||
#endif
|
||||
AppInfo* appInfo = new AppInfo();
|
||||
IPC ipc(0);
|
||||
if(!appInfo->isOwnerProcess(&ipc)){
|
||||
return 0;
|
||||
}
|
||||
QQmlApplicationEngine engine;
|
||||
FramelessHelper::Quick::registerTypes(&engine);
|
||||
#ifdef FLUENTUI_BUILD_STATIC_LIB
|
||||
|
@ -1,250 +0,0 @@
|
||||
#include "IPC.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QThread>
|
||||
#include <ctime>
|
||||
#include <random>
|
||||
|
||||
|
||||
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 = qApp->applicationPid();
|
||||
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 != qApp->applicationPid()
|
||||
&& (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());
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
#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
|
Reference in New Issue
Block a user