我正在尝试让
pavucontrol-qt
(pvcq) 在 Mac 上正常工作(是的,PulseAudio 可以在那里工作)。
这里的障碍是 Qt/Mac 事件调度程序不是基于 GLib 的,因此 qvcq 当前使用
pa_glib_mainloop
的实现(以某种方式挂接到 Qt 的基于 GLib 的主循环)不起作用。
因此,我实现了替代代码路径,其中使用 pa_threaded_mainloop
来代替,并注意与 GUI 交互的所有 libpulse 回调通过 QMetaObject::invokeMethod(qApp, [=]() {qApp->pa_callback(...);}, Qt::BlockingQueuedConnection)
在主循环上执行此操作。
这可行,但应用程序还使用通过
g_idle_add()
安装的延迟事件处理程序,我似乎无法编写替代的基于 GLib 的实现。
我曾希望这将是一个简单的问题,即确保对
g_idle_add()
的呼叫是从 pa_threaded_mainloop
发出的,但到目前为止,我已经证明这个问题根本不简单。
调用所需
g_idle_add()
的函数在主线程上执行,因为GUI功能的一部分。所以我尝试的是创建一个继承 paThread
的自定义类的实例(QObject
),并在初始启动期间当 QThread::currentThread()
返回 true 时的适当时刻将其发送到 pa_threaded_mainloop_in_thread()
。这一点是有效的,就像 QMetaObject::invokeMethod(paThread, [=]() { g_idle_add(idle_callback, data);}, Qt::QueuedConnection)
一样,但显然我忘记了它不会自动创建事件循环。因此,永远不会发出对 g_idle_add
的延迟调用。所以我尝试推导QEventLoop
并调用paThread->processEvents(AllEvents,1000);
或paThread->wakeUp()
,这两个都没有任何效果。
然后我尝试通过直接从
g_idle_add
线程调用 pa_threaded_mainloop
来测试上面简单问题的前提,安装一个打印跟踪的回调。它似乎根本没有被调用,所以我开始意识到我一直在以错误的方式处理这个问题?
我没有看到任何通过 libpulse() 进入
g_idle_XXXX
的钩子,甚至没有一种获得“我们空闲”信号的方法。我是否应该将 g_idle
回调替换为由 Qt 本身定期调用的回调?
嗯,哦。如果我先看看 libpulse 的实现,我就可以省去很多工作。
pa_threaded_mainloop
实现根本不基于 GLib。此外,由 idle_callback
调用的函数主要执行与 GUI 相关的操作,这些操作必须在主线程上执行 - 这就是我们需要注册回调时所在的位置。
由于当
pa_threaded_mainloop
空闲时无法执行回调,因此我决定在主事件调度程序空闲(或近似空闲)时调用它:
if (!idle_source) {
idle_source = 1;
// Do the Qt equivalent of having idle_callback() execute when the event pump is idle:
pvcApp->sendPostedEvents(nullptr, 0);
pvcApp->eventDispatcher()->processEvents(QEventLoop::AllEvents);
idle_callback(data); // resets idle_source
}
我并不完全满意,因为整个回调设计显然是为了将 GUI 显示的多个方面的多个更新事件聚合到对更新整个 GUI 的函数的单个调用中。我无法检测到上面代码片段的任何递归调用,因此我不会丢失更新事件;我想这是最重要的事情。