Support for QCoreApplication, QGuiApplication, and QApplication.

This commit is contained in:
Paul Knopf 2019-01-20 22:29:28 -05:00
parent 28a2ae29b7
commit 72bee3f7af
10 changed files with 391 additions and 247 deletions

View file

@ -1,4 +1,4 @@
QT += gui qml core-private quickcontrols2
QT += gui qml core-private quickcontrols2 widgets
CONFIG(enable-webengine) {
QT += webengine

View file

@ -0,0 +1,132 @@
#include <QmlNet/qml/QCoreApplication.h>
#include <QmlNet/qml/NetVariantList.h>
#include <QGuiApplication>
#include <QApplication>
GuiThreadContextTriggerCallback::GuiThreadContextTriggerCallback() :
callback(nullptr) {
}
void GuiThreadContextTriggerCallback::trigger() {
if (callback) {
callback();
}
}
extern "C" {
Q_DECL_EXPORT QGuiApplicationContainer* qapp_fromExisting(QCoreApplication* rawPointer)
{
QGuiApplicationContainer* result = new QGuiApplicationContainer();
result->ownsApp = false;
result->app = rawPointer;
result->callback = QSharedPointer<GuiThreadContextTriggerCallback>(new GuiThreadContextTriggerCallback());
return result;
}
Q_DECL_EXPORT QGuiApplicationContainer* qapp_create(NetVariantListContainer* argsContainer, int flags, int type)
{
QGuiApplicationContainer* result = new QGuiApplicationContainer();
// Build our args
if(argsContainer != nullptr) {
QSharedPointer<NetVariantList> args = argsContainer->list;
for(int x = 0; x < args->count(); x++) {
QByteArray arg = args->get(x)->getString().toLatin1();
result->args.append(arg);
char* cstr = new char [size_t(arg.size())+1];
memcpy(cstr, arg.data(), size_t(arg.size())+1);
result->argsPointer.push_back(cstr);
}
result->argCount = result->args.size();
} else {
result->argCount = 0;
}
result->ownsApp = true;
switch(type)
{
case 0:
result->app = new QCoreApplication(result->argCount, &result->argsPointer[0], flags);
break;
case 1:
result->app = new QGuiApplication(result->argCount, &result->argsPointer[0], flags);
break;
case 2:
result->app = new QApplication(result->argCount, &result->argsPointer[0], flags);
break;
default:
qCritical("invalid app type %d", type);
delete result;
return nullptr;
}
result->callback = QSharedPointer<GuiThreadContextTriggerCallback>(new GuiThreadContextTriggerCallback());
return result;
}
Q_DECL_EXPORT void qapp_destroy(QGuiApplicationContainer* container)
{
for (auto i : container->argsPointer) {
delete[] i;
}
container->callback.clear();
if(container->ownsApp) {
delete container->app;
}
delete container;
}
Q_DECL_EXPORT int qapp_getType(QGuiApplicationContainer* container, QCoreApplication* rawPointer)
{
if (!container && !rawPointer) {
qCritical("invalid container and/or rawPointer");
return -1;
}
if (container && rawPointer) {
qCritical("invalid container and/or rawPointer");
return -1;
}
if(!rawPointer && container) {
rawPointer = container->app;
}
if (qobject_cast<QApplication*>(rawPointer) != nullptr){
return 2;
}
if (qobject_cast<QGuiApplication*>(rawPointer) != nullptr){
return 1;
}
// QCoreApplication
return 0;
}
Q_DECL_EXPORT int qapp_exec()
{
return QGuiApplication::exec();
}
Q_DECL_EXPORT void qapp_addTriggerCallback(QGuiApplicationContainer* container, guiThreadTriggerCb callback)
{
container->callback->callback = callback;
}
Q_DECL_EXPORT void qapp_requestTrigger(QGuiApplicationContainer* container)
{
QMetaObject::invokeMethod(container->callback.data(), "trigger", Qt::QueuedConnection);
}
Q_DECL_EXPORT void qapp_exit(int returnCode)
{
QGuiApplication::exit(returnCode);
}
Q_DECL_EXPORT QCoreApplication* qapp_internalPointer(QGuiApplicationContainer* container)
{
return container->app;
}
}

View file

@ -20,8 +20,8 @@ struct QGuiApplicationContainer {
int argCount;
QList<QString> args;
std::vector<char*> argsPointer;
QGuiApplication* guiApp;
bool ownsGuiApp;
QCoreApplication* app;
bool ownsApp;
QSharedPointer<GuiThreadContextTriggerCallback> callback;
};

View file

@ -1,78 +0,0 @@
#include <QmlNet/qml/QGuiApplication.h>
#include <QmlNet/qml/NetVariantList.h>
#include <QGuiApplication>
GuiThreadContextTriggerCallback::GuiThreadContextTriggerCallback() :
callback(nullptr) {
}
void GuiThreadContextTriggerCallback::trigger() {
if (callback) {
callback();
}
}
extern "C" {
Q_DECL_EXPORT QGuiApplicationContainer* qguiapplication_create(NetVariantListContainer* argsContainer, QGuiApplication* existingApp) {
QGuiApplicationContainer* result = new QGuiApplicationContainer();
if (existingApp != nullptr) {
result->ownsGuiApp = false;
result->guiApp = existingApp;
} else {
result->ownsGuiApp = true;
// Build our args
if(argsContainer != nullptr) {
QSharedPointer<NetVariantList> args = argsContainer->list;
for(int x = 0; x < args->count(); x++) {
QByteArray arg = args->get(x)->getString().toLatin1();
result->args.append(arg);
char* cstr = new char [size_t(arg.size())+1];
memcpy(cstr, arg.data(), size_t(arg.size())+1);
result->argsPointer.push_back(cstr);
}
result->argCount = result->args.size();
} else {
result->argCount = 0;
}
result->guiApp = new QGuiApplication(result->argCount, &result->argsPointer[0], 0);
}
result->callback = QSharedPointer<GuiThreadContextTriggerCallback>(new GuiThreadContextTriggerCallback());
return result;
}
Q_DECL_EXPORT void qguiapplication_destroy(QGuiApplicationContainer* container) {
for (auto i : container->argsPointer) {
delete[] i;
}
container->callback.clear();
if(container->ownsGuiApp) {
delete container->guiApp;
}
delete container;
}
Q_DECL_EXPORT int qguiapplication_exec() {
return QGuiApplication::exec();
}
Q_DECL_EXPORT void qguiapplication_addTriggerCallback(QGuiApplicationContainer* container, guiThreadTriggerCb callback) {
container->callback->callback = callback;
}
Q_DECL_EXPORT void qguiapplication_requestTrigger(QGuiApplicationContainer* container) {
QMetaObject::invokeMethod(container->callback.data(), "trigger", Qt::QueuedConnection);
}
Q_DECL_EXPORT void qguiapplication_exit(int returnCode) {
QGuiApplication::exit(returnCode);
}
Q_DECL_EXPORT QGuiApplication* qguiapplication_internalPointer(QGuiApplicationContainer* container) {
return container->guiApp;
}
}

View file

@ -1,5 +1,4 @@
HEADERS += \
$$PWD/QGuiApplication.h \
$$PWD/QQmlApplicationEngine.h \
$$PWD/NetVariant.h \
$$PWD/NetValue.h \
@ -14,10 +13,10 @@ HEADERS += \
$$PWD/NetValueMetaObjectPacker.h \
$$PWD/QCommon.h \
$$PWD/NetListModel.h \
$$PWD/QtWebEngine.h
$$PWD/QtWebEngine.h \
$$PWD/QCoreApplication.h
SOURCES += \
$$PWD/QGuiApplication.cpp \
$$PWD/QQmlApplicationEngine.cpp \
$$PWD/NetVariant.cpp \
$$PWD/NetValue.cpp \
@ -32,4 +31,5 @@ SOURCES += \
$$PWD/NetValueMetaObjectPacker.cpp \
$$PWD/QCommon.cpp \
$$PWD/NetListModel.cpp \
$$PWD/QtWebEngine.cpp
$$PWD/QtWebEngine.cpp \
$$PWD/QCoreApplication.cpp

View file

@ -3,6 +3,7 @@ using System.Linq;
using System.Runtime.InteropServices;
using NetNativeLibLoader.Loader;
using NetNativeLibLoader.PathResolver;
using Qml.Net.Internal;
namespace Qml.Net
{
@ -47,7 +48,7 @@ namespace Qml.Net
public delegate int NetRunCallbackDelegate();
public static int Run(string[] args, Func<string[], QGuiApplication, QQmlApplicationEngine, NetRunCallbackDelegate, int> action)
public static int Run(string[] args, Func<string[], QCoreApplication, QQmlApplicationEngine, NetRunCallbackDelegate, int> action)
{
if (args.Length < 4)
{
@ -60,7 +61,23 @@ namespace Qml.Net
var exportedSymbolPtr = new IntPtr((long)ulong.Parse(args[3]));
GetExportedSymbol = Marshal.GetDelegateForFunctionPointer<GetExportedSymbolDelegate>(exportedSymbolPtr);
using (var app = new QGuiApplication(appPtr))
QCoreApplication app = null;
switch (Interop.QCoreApplication.GetAppType(IntPtr.Zero, appPtr))
{
case 0:
app = new QCoreApplication(appPtr);
break;
case 1:
app = new QGuiApplication(appPtr);
break;
case 2:
app = new QApplication(appPtr);
break;
default:
throw new Exception("Invalid app type");
}
using (app)
{
using (var engine = new QQmlApplicationEngine(enginePtr))
{

View file

@ -99,7 +99,7 @@ namespace Qml.Net.Internal
NetMethodInfo = LoadInteropType<NetMethodInfoInterop>(library, loader);
NetPropertyInfo = LoadInteropType<NetPropertyInfoInterop>(library, loader);
NetTypeManager = LoadInteropType<NetTypeManagerInterop>(library, loader);
QGuiApplication = LoadInteropType<QGuiApplicationInterop>(library, loader);
QCoreApplication = LoadInteropType<QCoreApplicationInterop>(library, loader);
QQmlApplicationEngine = LoadInteropType<QQmlApplicationEngineInterop>(library, loader);
NetVariant = LoadInteropType<NetVariantInterop>(library, loader);
NetReference = LoadInteropType<NetReferenceInterop>(library, loader);
@ -136,7 +136,7 @@ namespace Qml.Net.Internal
public static NetTypeManagerInterop NetTypeManager { get; }
public static QGuiApplicationInterop QGuiApplication { get; }
public static QCoreApplicationInterop QCoreApplication { get; }
public static QQmlApplicationEngineInterop QQmlApplicationEngine { get; }

View file

@ -0,0 +1,25 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
using Qml.Net.Internal;
namespace Qml.Net
{
public class QApplication : QCoreApplication
{
public QApplication()
: this(null)
{
}
public QApplication(string[] args, int flags = 0)
: base(1, args, flags)
{
}
internal QApplication(IntPtr existingApp)
: base(existingApp)
{
}
}
}

View file

@ -0,0 +1,202 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Qml.Net.Internal;
using Qml.Net.Internal.Qml;
namespace Qml.Net
{
public class QCoreApplication : BaseDisposable
{
private readonly Queue<Action> _actionQueue = new Queue<Action>();
private readonly SynchronizationContext _oldSynchronizationContext;
private GCHandle _triggerHandle;
protected QCoreApplication(IntPtr handle, bool ownsHandle)
: base(handle, ownsHandle)
{
}
public QCoreApplication()
: this(null)
{
}
public QCoreApplication(string[] args, int flags = 0)
: this(0, args, flags)
{
}
internal QCoreApplication(int type, string[] args, int flags)
: base(Create(type, args?.ToList(), flags))
{
TriggerDelegate triggerDelegate = Trigger;
_triggerHandle = GCHandle.Alloc(triggerDelegate);
Interop.QCoreApplication.AddTriggerCallback(Handle, Marshal.GetFunctionPointerForDelegate(triggerDelegate));
_oldSynchronizationContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(new QtSynchronizationContext(this));
}
internal QCoreApplication(IntPtr existingApp)
: base(CreateFromExisting(existingApp))
{
TriggerDelegate triggerDelegate = Trigger;
_triggerHandle = GCHandle.Alloc(triggerDelegate);
Interop.QCoreApplication.AddTriggerCallback(Handle, Marshal.GetFunctionPointerForDelegate(triggerDelegate));
_oldSynchronizationContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(new QtSynchronizationContext(this));
}
public int Exec()
{
return Interop.QCoreApplication.Exec();
}
public void Dispatch(Action action)
{
lock (_actionQueue)
{
_actionQueue.Enqueue(action);
}
RequestTrigger();
}
public void Exit(int returnCode = 0)
{
Interop.QCoreApplication.Exit(returnCode);
}
public void Quit()
{
Exit();
}
private void RequestTrigger()
{
Interop.QCoreApplication.RequestTrigger(Handle);
}
internal IntPtr InternalPointer => Interop.QCoreApplication.InternalPointer(Handle);
private void Trigger()
{
Action action;
lock (_actionQueue)
{
action = _actionQueue.Dequeue();
}
action?.Invoke();
}
protected override void DisposeUnmanaged(IntPtr ptr)
{
SynchronizationContext.SetSynchronizationContext(_oldSynchronizationContext);
Interop.QCoreApplication.Destroy(ptr);
_triggerHandle.Free();
}
private static IntPtr CreateFromExisting(IntPtr app)
{
return Interop.QCoreApplication.FromExisting(app);
}
private static IntPtr Create(int type, List<string> args, int flags)
{
if (args == null)
{
args = new List<string>();
}
// By default, the argv[0] should be the process name.
// .NET doesn't pass that name, but Qt should get it
// since it does in a normal Qt environment.
args.Insert(0, System.Diagnostics.Process.GetCurrentProcess().ProcessName);
using (var strings = new NetVariantList())
{
foreach (var arg in args)
{
using (var variant = new NetVariant())
{
variant.String = arg;
strings.Add(variant);
}
}
return Interop.QCoreApplication.Create(strings.Handle, flags, type);
}
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void TriggerDelegate();
private class QtSynchronizationContext : SynchronizationContext
{
readonly QCoreApplication _app;
public QtSynchronizationContext(QCoreApplication guiApp)
{
_app = guiApp;
}
public override void Post(SendOrPostCallback d, object state)
{
_app.Dispatch(() => d.Invoke(state));
}
}
}
internal class QCoreApplicationInterop
{
[NativeSymbol(Entrypoint = "qapp_fromExisting")]
public FromExistingDel FromExisting { get; set; }
public delegate IntPtr FromExistingDel(IntPtr rawPointer);
[NativeSymbol(Entrypoint = "qapp_create")]
public CreateDel Create { get; set; }
public delegate IntPtr CreateDel(IntPtr args, int flags, int type);
[NativeSymbol(Entrypoint = "qapp_destroy")]
public DestroyDel Destroy { get; set; }
[NativeSymbol(Entrypoint = "qapp_getType")]
public GetAppTypeDel GetAppType { get; set; }
public delegate int GetAppTypeDel(IntPtr container, IntPtr rawPointer);
public delegate void DestroyDel(IntPtr container);
[NativeSymbol(Entrypoint = "qapp_exec")]
public ExecDel Exec { get; set; }
public delegate int ExecDel();
[NativeSymbol(Entrypoint = "qapp_addTriggerCallback")]
public AddTriggerCallbackDel AddTriggerCallback { get; set; }
public delegate void AddTriggerCallbackDel(IntPtr app, IntPtr callback);
[NativeSymbol(Entrypoint = "qapp_requestTrigger")]
public RequestTriggerDel RequestTrigger { get; set; }
public delegate void RequestTriggerDel(IntPtr app);
[NativeSymbol(Entrypoint = "qapp_exit")]
public ExitDel Exit { get; set; }
public delegate void ExitDel(int returnCode);
[NativeSymbol(Entrypoint = "qapp_internalPointer")]
public InternalPointerDel InternalPointer { get; set; }
public delegate IntPtr InternalPointerDel(IntPtr app);
}
}

View file

@ -8,175 +8,21 @@ using Qml.Net.Internal.Qml;
namespace Qml.Net
{
public sealed class QGuiApplication : BaseDisposable
public class QGuiApplication : QCoreApplication
{
readonly Queue<Action> _actionQueue = new Queue<Action>();
GCHandle _triggerHandle;
readonly SynchronizationContext _oldSynchronizationContext;
public QGuiApplication()
: this(null)
{
}
public QGuiApplication(string[] args)
: base(Create(args?.ToList()))
public QGuiApplication(string[] args, int flags = 0)
: base(1, args, flags)
{
TriggerDelegate triggerDelegate = Trigger;
_triggerHandle = GCHandle.Alloc(triggerDelegate);
Interop.QGuiApplication.AddTriggerCallback(Handle, Marshal.GetFunctionPointerForDelegate(triggerDelegate));
_oldSynchronizationContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(new QtSynchronizationContext(this));
}
internal QGuiApplication(IntPtr existingApp)
: base(CreateFromExisting(existingApp))
: base(existingApp)
{
TriggerDelegate triggerDelegate = Trigger;
_triggerHandle = GCHandle.Alloc(triggerDelegate);
Interop.QGuiApplication.AddTriggerCallback(Handle, Marshal.GetFunctionPointerForDelegate(triggerDelegate));
_oldSynchronizationContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(new QtSynchronizationContext(this));
}
public int Exec()
{
return Interop.QGuiApplication.Exec();
}
public void Dispatch(Action action)
{
lock (_actionQueue)
{
_actionQueue.Enqueue(action);
}
RequestTrigger();
}
public void Exit(int returnCode = 0)
{
Interop.QGuiApplication.Exit(returnCode);
}
public void Quit()
{
Exit();
}
private void RequestTrigger()
{
Interop.QGuiApplication.RequestTrigger(Handle);
}
internal IntPtr InternalPointer => Interop.QGuiApplication.InternalPointer(Handle);
private void Trigger()
{
Action action;
lock (_actionQueue)
{
action = _actionQueue.Dequeue();
}
action?.Invoke();
}
protected override void DisposeUnmanaged(IntPtr ptr)
{
SynchronizationContext.SetSynchronizationContext(_oldSynchronizationContext);
Interop.QGuiApplication.Destroy(ptr);
_triggerHandle.Free();
}
private static IntPtr CreateFromExisting(IntPtr app)
{
return Interop.QGuiApplication.Create(IntPtr.Zero, app);
}
private static IntPtr Create(List<string> args)
{
if (args == null)
{
args = new List<string>();
}
// By default, the argv[0] should be the process name.
// .NET doesn't pass that name, but Qt should get it
// since it does in a normal Qt environment.
args.Insert(0, System.Diagnostics.Process.GetCurrentProcess().ProcessName);
using (var strings = new NetVariantList())
{
foreach (var arg in args)
{
using (var variant = new NetVariant())
{
variant.String = arg;
strings.Add(variant);
}
}
return Interop.QGuiApplication.Create(strings.Handle, IntPtr.Zero);
}
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void TriggerDelegate();
private class QtSynchronizationContext : SynchronizationContext
{
readonly QGuiApplication _guiApp;
public QtSynchronizationContext(QGuiApplication guiApp)
{
_guiApp = guiApp;
}
public override void Post(SendOrPostCallback d, object state)
{
_guiApp.Dispatch(() => d.Invoke(state));
}
}
}
internal class QGuiApplicationInterop
{
[NativeSymbol(Entrypoint = "qguiapplication_create")]
public CreateDel Create { get; set; }
public delegate IntPtr CreateDel(IntPtr args, IntPtr existingApp);
[NativeSymbol(Entrypoint = "qguiapplication_destroy")]
public DestroyDel Destroy { get; set; }
public delegate void DestroyDel(IntPtr app);
[NativeSymbol(Entrypoint = "qguiapplication_exec")]
public ExecDel Exec { get; set; }
public delegate int ExecDel();
[NativeSymbol(Entrypoint = "qguiapplication_addTriggerCallback")]
public AddTriggerCallbackDel AddTriggerCallback { get; set; }
public delegate void AddTriggerCallbackDel(IntPtr app, IntPtr callback);
[NativeSymbol(Entrypoint = "qguiapplication_requestTrigger")]
public RequestTriggerDel RequestTrigger { get; set; }
public delegate void RequestTriggerDel(IntPtr app);
[NativeSymbol(Entrypoint = "qguiapplication_exit")]
public ExitDel Exit { get; set; }
public delegate void ExitDel(int returnCode);
[NativeSymbol(Entrypoint = "qguiapplication_internalPointer")]
public InternalPointerDel InternalPointer { get; set; }
public delegate IntPtr InternalPointerDel(IntPtr app);
}
}