Integrating GLib event processing in a Qt/Cocoa Mac application
-
wrote on 3 Aug 2024, 11:34 last edited by
Hi,
I'm trying to get a Unix desktop Qt application to function properly as a "native" Mac application. Long story short: AFAICT it relies on Qt using a GLib-based event loop.
The qtbase code suggests that event processing never uses GLib on
Q_OS_DARWIN
, even when GLib support is built in. Yet the application does work correctly when I start it using the XCB QPA (which can be built on Mac with a little bit of build system tweaking). I have yet to figure out how and why that is (via QGenericUnixEventDispatcher?) but it does show that the application can be functional on the platform.From what I gather I will need to make certain that
g_main_context_iteration
is being called regularly, preferably linked to Qt's own message pump.I did find this topic
https://forum.qt.io/topic/132556/use-gstreamer-gtk-signaling-in-qt-application/3that suggests using QEventDispatcherGlib but fails to give examples how to do this. I cannot seem to find a way to obtain a reference to the qApp's QEventLoop.
Would overloading
QApplication::processEvents()
with a version that integrates the code fromQEventDispatcherGlib::processEvents
be a plausible solution, or should I simply follow this QTimer approach (from
Qt 5.9)?QGstreamerBusHelperPrivate(QGstreamerBusHelper *parent, GstBus* bus) : [...] { // glib event loop can be disabled either by env variable or QT_NO_GLIB define, so check the dispacher QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher(); const bool hasGlib = dispatcher && dispatcher->inherits("QEventDispatcherGlib"); if (!hasGlib) { m_intervalTimer = new QTimer(this); m_intervalTimer->setInterval(250); connect(m_intervalTimer, SIGNAL(timeout()), SLOT(interval())); m_intervalTimer->start();
Other potential mechanisms I can see:
QApplication::eventDispatcher::registerTimer()
if one exists (but what should the QObject be?)- Force the use of a QEventDispatcherGlib eventDispatcher by calling
QApplication::setEventDispatcher(new QEventDispatcherGlib)
beforeQApplication app(argc, argv)
(?)
Pointers and suggestions appreciated, thanks!
NB: I'm using Qt5 in this "retro-computing" project.
-
Hi,
I'm trying to get a Unix desktop Qt application to function properly as a "native" Mac application. Long story short: AFAICT it relies on Qt using a GLib-based event loop.
The qtbase code suggests that event processing never uses GLib on
Q_OS_DARWIN
, even when GLib support is built in. Yet the application does work correctly when I start it using the XCB QPA (which can be built on Mac with a little bit of build system tweaking). I have yet to figure out how and why that is (via QGenericUnixEventDispatcher?) but it does show that the application can be functional on the platform.From what I gather I will need to make certain that
g_main_context_iteration
is being called regularly, preferably linked to Qt's own message pump.I did find this topic
https://forum.qt.io/topic/132556/use-gstreamer-gtk-signaling-in-qt-application/3that suggests using QEventDispatcherGlib but fails to give examples how to do this. I cannot seem to find a way to obtain a reference to the qApp's QEventLoop.
Would overloading
QApplication::processEvents()
with a version that integrates the code fromQEventDispatcherGlib::processEvents
be a plausible solution, or should I simply follow this QTimer approach (from
Qt 5.9)?QGstreamerBusHelperPrivate(QGstreamerBusHelper *parent, GstBus* bus) : [...] { // glib event loop can be disabled either by env variable or QT_NO_GLIB define, so check the dispacher QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher(); const bool hasGlib = dispatcher && dispatcher->inherits("QEventDispatcherGlib"); if (!hasGlib) { m_intervalTimer = new QTimer(this); m_intervalTimer->setInterval(250); connect(m_intervalTimer, SIGNAL(timeout()), SLOT(interval())); m_intervalTimer->start();
Other potential mechanisms I can see:
QApplication::eventDispatcher::registerTimer()
if one exists (but what should the QObject be?)- Force the use of a QEventDispatcherGlib eventDispatcher by calling
QApplication::setEventDispatcher(new QEventDispatcherGlib)
beforeQApplication app(argc, argv)
(?)
Pointers and suggestions appreciated, thanks!
NB: I'm using Qt5 in this "retro-computing" project.
wrote on 3 Aug 2024, 12:03 last edited by@RJV-B said in Integrating GLib event processing in a Qt/Cocoa Mac application:
- Force the use of a QEventDispatcherGlib eventDispatcher by calling
QApplication::setEventDispatcher(new QEventDispatcherGlib)
beforeQApplication app(argc, argv)
(?)
Well, testing that with the Linux version of the app somehow puts it in a state where it doesn't appear to have an event dispatcher at all, or one that doesn't do anything?!
Yet from looking at
QCoreApplicationPrivate::init()
there shouldn't be anything different! -
Hi,
I'm trying to get a Unix desktop Qt application to function properly as a "native" Mac application. Long story short: AFAICT it relies on Qt using a GLib-based event loop.
The qtbase code suggests that event processing never uses GLib on
Q_OS_DARWIN
, even when GLib support is built in. Yet the application does work correctly when I start it using the XCB QPA (which can be built on Mac with a little bit of build system tweaking). I have yet to figure out how and why that is (via QGenericUnixEventDispatcher?) but it does show that the application can be functional on the platform.From what I gather I will need to make certain that
g_main_context_iteration
is being called regularly, preferably linked to Qt's own message pump.I did find this topic
https://forum.qt.io/topic/132556/use-gstreamer-gtk-signaling-in-qt-application/3that suggests using QEventDispatcherGlib but fails to give examples how to do this. I cannot seem to find a way to obtain a reference to the qApp's QEventLoop.
Would overloading
QApplication::processEvents()
with a version that integrates the code fromQEventDispatcherGlib::processEvents
be a plausible solution, or should I simply follow this QTimer approach (from
Qt 5.9)?QGstreamerBusHelperPrivate(QGstreamerBusHelper *parent, GstBus* bus) : [...] { // glib event loop can be disabled either by env variable or QT_NO_GLIB define, so check the dispacher QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher(); const bool hasGlib = dispatcher && dispatcher->inherits("QEventDispatcherGlib"); if (!hasGlib) { m_intervalTimer = new QTimer(this); m_intervalTimer->setInterval(250); connect(m_intervalTimer, SIGNAL(timeout()), SLOT(interval())); m_intervalTimer->start();
Other potential mechanisms I can see:
QApplication::eventDispatcher::registerTimer()
if one exists (but what should the QObject be?)- Force the use of a QEventDispatcherGlib eventDispatcher by calling
QApplication::setEventDispatcher(new QEventDispatcherGlib)
beforeQApplication app(argc, argv)
(?)
Pointers and suggestions appreciated, thanks!
NB: I'm using Qt5 in this "retro-computing" project.
wrote on 3 Aug 2024, 13:47 last edited by@RJV-B said in Integrating GLib event processing in a Qt/Cocoa Mac application:
Would overloading QApplication::processEvents()
Tried that but my overload doesn't appear to get called?!
class PVCApplication : public QApplication { Q_OBJECT public: PVCApplication(int &argc, char **argv) : QApplication(argc, argv) { gContext = g_main_context_default(); dispatcher = eventDispatcher(); qWarning() << Q_FUNC_INFO << "eventDispatcher:" << dispatcher; hasGlib = dispatcher && dispatcher->inherits("QEventDispatcherGlib"); } static void processEvents(QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents) { qWarning() << Q_FUNC_INFO << hasGlib; if (!hasGlib) { g_main_context_iteration(gContext, false); } QApplication::processEvents(flags); } static void processEvents(QEventLoop::ProcessEventsFlags flags, int maxtime) { qWarning() << Q_FUNC_INFO << hasGlib; if (!hasGlib) { g_main_context_iteration(gContext, false); } QApplication::processEvents(flags, maxtime); } private: static _GMainContext *gContext; QAbstractEventDispatcher *dispatcher = nullptr; static bool hasGlib; }; _GMainContext *PVCApplication::gContext = nullptr; bool PVCApplication::hasGlib = true;
-
@RJV-B said in Integrating GLib event processing in a Qt/Cocoa Mac application:
Would overloading QApplication::processEvents()
Tried that but my overload doesn't appear to get called?!
class PVCApplication : public QApplication { Q_OBJECT public: PVCApplication(int &argc, char **argv) : QApplication(argc, argv) { gContext = g_main_context_default(); dispatcher = eventDispatcher(); qWarning() << Q_FUNC_INFO << "eventDispatcher:" << dispatcher; hasGlib = dispatcher && dispatcher->inherits("QEventDispatcherGlib"); } static void processEvents(QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents) { qWarning() << Q_FUNC_INFO << hasGlib; if (!hasGlib) { g_main_context_iteration(gContext, false); } QApplication::processEvents(flags); } static void processEvents(QEventLoop::ProcessEventsFlags flags, int maxtime) { qWarning() << Q_FUNC_INFO << hasGlib; if (!hasGlib) { g_main_context_iteration(gContext, false); } QApplication::processEvents(flags, maxtime); } private: static _GMainContext *gContext; QAbstractEventDispatcher *dispatcher = nullptr; static bool hasGlib; }; _GMainContext *PVCApplication::gContext = nullptr; bool PVCApplication::hasGlib = true;
@RJV-B said in Integrating GLib event processing in a Qt/Cocoa Mac application:
Tried that but my overload doesn't appear to get called?!
Because these are static functions - how should they be called from inside the Qt code?
-
@RJV-B said in Integrating GLib event processing in a Qt/Cocoa Mac application:
Tried that but my overload doesn't appear to get called?!
Because these are static functions - how should they be called from inside the Qt code?
wrote on 3 Aug 2024, 15:45 last edited by@Christian-Ehrlicher said in Integrating GLib event processing in a Qt/Cocoa Mac application:
Because these are static functions - how should they be called from inside the Qt code?
I'll have to admit that I never tried to overload a static class function before; I simply assumed that C++ had a trick up its sleeve to allow overloading them too.
I noticed that on Linux the default eventDispatcher is of type
QPAEventDispatcherGlib
instead ofQEventDispatcherGlib
. So I triedextern QAbstractEventDispatcher *createUnixEventDispatcher(); QApplication::setEventDispatcher(createUnixEventDispatcher()); QApplication app(argc, argv);
which does give me a dispatcher of the proper type, but it still doesn't seem to be pumping the GLib event loop at all (Qt's own event processing works).
I'd really like to avoid using a timer because that will either give too coarse GLib event processing or else burn more CPU than necessary. Plus it's clearly possible to run into race conditions on exit.
-
@Christian-Ehrlicher said in Integrating GLib event processing in a Qt/Cocoa Mac application:
Because these are static functions - how should they be called from inside the Qt code?
I'll have to admit that I never tried to overload a static class function before; I simply assumed that C++ had a trick up its sleeve to allow overloading them too.
I noticed that on Linux the default eventDispatcher is of type
QPAEventDispatcherGlib
instead ofQEventDispatcherGlib
. So I triedextern QAbstractEventDispatcher *createUnixEventDispatcher(); QApplication::setEventDispatcher(createUnixEventDispatcher()); QApplication app(argc, argv);
which does give me a dispatcher of the proper type, but it still doesn't seem to be pumping the GLib event loop at all (Qt's own event processing works).
I'd really like to avoid using a timer because that will either give too coarse GLib event processing or else burn more CPU than necessary. Plus it's clearly possible to run into race conditions on exit.
@RJV-B said in Integrating GLib event processing in a Qt/Cocoa Mac application:
I'll have to admit that I never tried to overload a static class function before; I simply assumed that C++ had a trick up its sleeve to allow overloading them too.
You can not overload a static function - basic c++.
-
wrote on 3 Aug 2024, 19:33 last edited by
Well, yes. Or rather (I think), a derived class can't override a static member function of a parent class with the same reciprocal effect you get with regular member function. Anyway, now I know, and it's not the topic at hand.
The sample applications I checked that do work in with PulseAudio in native/Cocoa mode only play or record audio so it's (much) easier for them to use the threaded mainloop mechanism from libpulse.
I might be able to do that by calling the toplevel GUI functions as slots but it may be easier to just add a "source" (that pumps the glib event loop) to the CoreFoundation main runloop.
-
Moderatorswrote on 4 Aug 2024, 06:21 last edited by Axel Spoerl 8 Apr 2024, 06:22
While the requirement to have a glib event loop running on macOS feels strange, using a tweaked XCB for that purpose sounds like breaking a butterfly on a wheel.
If memory serves well, GStreamer runs an event loop on windows and macOS. You might wanna check their sources and see how it’s done there.
-
While the requirement to have a glib event loop running on macOS feels strange, using a tweaked XCB for that purpose sounds like breaking a butterfly on a wheel.
If memory serves well, GStreamer runs an event loop on windows and macOS. You might wanna check their sources and see how it’s done there.
wrote on 4 Aug 2024, 10:55 last edited by@Axel-Spoerl said in Integrating GLib event processing in a Qt/Cocoa Mac application:
While the requirement to have a glib event loop running on macOS feels strange, using a tweaked XCB for that purpose sounds like breaking a butterfly on a wheel.
Running PulseAudio itself on Mac "feels strange" but this is cross-platform code of course, and Darwin is a Unix so ultimately this software should be more at home there than on MSWin.
Who said anything about a tweaked XCB? I had to tweak Qt's build system so it would consider building the X11 components there, but that aside I didn't have to do anything naughty (and at some point the Qt devs were open to incorporating those changes under the principle that any QPA that can build and run on a platform is supported).GStreamer is pure GTk code, and while GTk do offer a "quartz" backend I never experimented with that for various reasons. I'm not expecting to learn much from how they do things. GLib itself has works fine but I get it from MacPorts and haven't looked at how complex their patches are (not very large in any case).
After looking at the code of the Cocoa event dispatcher I had hoped I could just add my own CF source and have them be picked up by the observer that has to be in place AFAICT. It's not, so now I'm going to try again to use the threaded GLib mainloop that libpulse also provides and that's used in VLC, QMPlay2 and PortAudio (all those have functional PulseAudio support on Mac). It will mean that I need to modify the "pa callback" functions to execute on the Qt main loop. They do get a
userdata
pointer of course, current theMainWindow*
but I plan to change that to a pointer to a derivedQApplication
instance with member functions that contain the actual payload. I can't recall doing this before, but I think that a global callback function of the form below should work?void card_cb(const pa_card_info *i, int eol, void *userdata) { if (userdata) { PVCApplication *app = static_cast<PVCApplication*>(userdata); QMetaObject::invokeMethod(app, [=]() { app->card_cb(i, eol); }, Qt::BlockingQueuedConnection); } else { // for being callable on the main thread PVCApplication::instance()->card_cb(i, eol); } } void PVCApplication::card_cb(const pa_card_info *i, int eol) { // payload code }
1/9