diff --git a/qt/mainthread/mainthread.cpp b/qt/mainthread/mainthread.cpp new file mode 100644 index 00000000..b5e52510 --- /dev/null +++ b/qt/mainthread/mainthread.cpp @@ -0,0 +1,14 @@ +#include +#include + +#include "mainthread.h" + +extern "C" { + void mainthread_exec_handle(intptr_t); +} + +void mainthread_exec(intptr_t cb) { + QMetaObject::invokeMethod(qApp, [=]{ + mainthread_exec_handle(cb); + }, Qt::QueuedConnection); +} diff --git a/qt/mainthread/mainthread.go b/qt/mainthread/mainthread.go new file mode 100644 index 00000000..eb9505e5 --- /dev/null +++ b/qt/mainthread/mainthread.go @@ -0,0 +1,69 @@ +package mainthread + +import ( + "sync" + "runtime/cgo" +) + +/* +#cgo pkg-config: Qt5Core + +#include "mainthread.h" +*/ +import "C" + +// Start runs the callback in the main Qt thread. You should use this whenever +// accessing the main Qt GUI from inside a goroutine. +// This function is non-blocking. +func Start(gofunc func()) { + h := cgo.NewHandle(gofunc) + + C.mainthread_exec(C.intptr_t(h)) +} + +// Wait runs the callback in the main Qt thread. You should use this whenever +// accessing the main Qt GUI from inside a goroutine. +// The call blocks until the callback is executed in the main thread's eventloop. +func Wait(gofunc func()) { + // It's possible to use Qt::BlockingQueuedConnection to implement the + // blocking, but it has a deadlock risk + var wg sync.WaitGroup + wg.Add(1) + outerfunc := func() { + gofunc() + wg.Done() + } + Start(outerfunc) + wg.Wait() +} + +func Wait2[T any](gofunc func() T) (ret T) { + outerfunc := func() { + ret = gofunc() + } + Wait(outerfunc) + return ret +} + +func Wait3[T any](gofunc func() (T, error)) (ret T, err error) { + outerfunc := func() { + ret, err = gofunc() + } + Wait(outerfunc) + return ret, err +} + +//export mainthread_exec_handle +func mainthread_exec_handle(u uintptr) { + h := cgo.Handle(u) + + gofunc, ok := h.Value().(func()) + if !ok { + panic("miqt: callback of non-callback type (heap corruption?)") + } + + gofunc() + + // Free handle after use + h.Delete() +} diff --git a/qt/mainthread/mainthread.h b/qt/mainthread/mainthread.h new file mode 100644 index 00000000..eee3c085 --- /dev/null +++ b/qt/mainthread/mainthread.h @@ -0,0 +1,18 @@ +#pragma once + +#ifndef MIQT_QT_MAINTHREAD_H +#define MIQT_QT_MAINTHREAD_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void mainthread_exec(intptr_t cb); + +#ifdef __cplusplus +} +#endif + +#endif