Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
message_loop_integration.hpp
Go to the documentation of this file.
1#pragma once
2
3// Per-OS message-loop integration shims.
4//
5// This header exposes a thin, cross-platform API surface that callers can
6// use to introspect and cooperate with the host's native main message loop
7// (Cocoa NSRunLoop / Win MsgWaitForMultipleObjects / Linux GLib MainLoop
8// or X11/Wayland event sources).
9//
10// Background:
11// `MainThreadDispatcher` is a process-wide bridge that lets any Pulp
12// subsystem post work to whatever native main-thread queue a platform
13// owner has registered. The macOS plugin-mode helper
14// (`plugin_main_thread.hpp`) adds a refcounted Cocoa backend so format
15// adapters can register at instantiation time. This header sits one
16// level up: it exposes the *kind* of native loop that's currently
17// integrated, so cross-platform code can branch on capabilities (e.g.
18// "do I have a Cocoa run loop I can attach a CFRunLoopObserver to?")
19// without including platform headers.
20//
21// What this header DOES:
22// - Reports the active backend kind (`MainLoopKind`).
23// - Forwards `call_async` / `call_sync` / `is_main_thread` to the
24// underlying `MainThreadDispatcher` so callers can write one piece
25// of cross-platform code.
26// - Documents the per-OS contract (callback thread, re-entrancy, etc.)
27// for the four loops Pulp targets.
28//
29// What this header additionally does for headless native hosts:
30// - Offers one bounded main-loop service slice on Apple platforms. This
31// lets offline tools advance plug-ins whose initialization depends on
32// XPC, timers, or main-queue callbacks without taking ownership of the
33// application's event loop.
34//
35// Platform coverage:
36// - macOS / iOS — Cocoa NSRunLoop backend via the plugin-mode helper
37// in `plugin_main_thread.hpp`.
38// - Windows — MsgWaitForMultipleObjects backend pending; the
39// dispatcher will report `MainLoopKind::None` until the standalone
40// Win32 host or VST3 editor registers one.
41// - Linux — GLib / X11 / Wayland backends pending; same
42// `MainLoopKind::None` story until a backend lands.
43
45
46#include <chrono>
47#include <cstdint>
48#include <string_view>
49
50namespace pulp::events {
51
58enum class MainLoopKind : std::uint8_t {
59 None = 0,
60 Cocoa = 1,
61 Win32 = 2,
62 GLib = 3,
63 X11 = 4,
64 Wayland = 5,
65 Custom = 255,
66};
67
69enum class MainLoopPumpResult : std::uint8_t {
72 Stopped = 2,
76};
77
85public:
88
92 static std::string_view active_name();
93
98 static bool available();
99
101 static bool is_main_thread();
102
105 static bool post(Task task);
106
109 static bool call_sync(Task task);
110
122 static MainLoopPumpResult
123 pump_main_loop_for(std::chrono::milliseconds max_duration);
124
137
141};
142
143// ─── Per-OS contract notes (documentation surface) ──────────────────────
144//
145// macOS / iOS — Cocoa (MainLoopKind::Cocoa)
146// - Callback thread: the AppKit / UIKit main thread.
147// - Re-entrancy: `call_sync()` from the main thread runs inline; from
148// a worker thread it dispatches via `dispatch_get_main_queue()` and
149// blocks until the block runs.
150// - Cooperation with hosts: the plugin-mode backend in
151// `plugin_main_thread.hpp` is refcounted; safe to call register /
152// unregister once per loaded plugin instance.
153//
154// Windows — Win32 (MainLoopKind::Win32) — backend pending
155// - Expected callback thread: the thread that owns the message pump
156// (the thread that called `GetMessage` / `MsgWaitForMultipleObjects`).
157// - Re-entrancy: `call_sync()` from the pump thread should run inline;
158// from a worker thread it should `PostMessage` to a hidden window
159// and block on an event.
160//
161// Linux — GLib / X11 / Wayland — backends pending
162// - GLib hosts: register via `g_main_context_invoke` for `call_async`
163// and a `GMutex` + `GCond` for `call_sync`.
164// - X11 hosts: post to a self-pipe that the main thread reads under
165// `select()`; the X11 event loop ferries the wakeup.
166// - Wayland hosts: use `wl_display_dispatch_pending()` plus a self-pipe
167// wakeup; otherwise indistinguishable from X11 from the caller's POV.
168
169} // namespace pulp::events
std::uint64_t Token
Definition main_thread_dispatcher.hpp:24
Cross-platform introspection of the active main loop.
Definition message_loop_integration.hpp:84
static void register_kind(MainThreadDispatcher::Token token, MainLoopKind kind)
Register the kind tag for a custom backend.
static bool available()
Convenience: any non-None backend is registered.
static void unregister_kind(MainThreadDispatcher::Token token)
Clear the kind tag for a token registered via register_kind.
static MainLoopKind active_kind()
Returns the kind of native loop currently active, or None.
static bool call_sync(Task task)
Forwards to MainThreadDispatcher::call_sync().
static bool is_main_thread()
Forwards to MainThreadDispatcher::is_main_thread().
static std::string_view active_name()
Human-readable name for diagnostics ("cocoa", "win32", "glib", "x11", "wayland", "custom",...
static MainLoopPumpResult pump_main_loop_for(std::chrono::milliseconds max_duration)
Service the native process main loop for at most max_duration.
static bool post(Task task)
Forwards to MainThreadDispatcher::call_async().
Definition child_process_manager.hpp:18
MainLoopKind
The kind of native main loop currently integrated.
Definition message_loop_integration.hpp:58
@ None
No native main loop integration.
Definition message_loop_integration.hpp:59
@ Cocoa
macOS / iOS NSRunLoop / CFRunLoop.
Definition message_loop_integration.hpp:60
@ Custom
Caller-registered loop (SDL, custom dispatcher).
Definition message_loop_integration.hpp:65
@ Win32
Windows MsgWaitForMultipleObjects / GetMessage.
Definition message_loop_integration.hpp:61
@ Wayland
Wayland wl_display_dispatch loop.
Definition message_loop_integration.hpp:64
@ X11
Bare X11 XNextEvent loop.
Definition message_loop_integration.hpp:63
@ GLib
Linux glib GMainLoop (GNOME / GTK hosts).
Definition message_loop_integration.hpp:62
MainLoopPumpResult
Outcome of a bounded native-main-loop service slice.
Definition message_loop_integration.hpp:69
@ HandledSource
At least one native source was handled.
Definition message_loop_integration.hpp:70
@ Finished
The native loop has no sources or timers.
Definition message_loop_integration.hpp:73
@ TimedOut
The bounded interval elapsed without a source.
Definition message_loop_integration.hpp:71
@ Unsupported
This platform has no bounded pump implementation.
Definition message_loop_integration.hpp:74
@ Stopped
The native loop was explicitly stopped.
Definition message_loop_integration.hpp:72
@ WrongThread
The call was not made on the process main thread.
Definition message_loop_integration.hpp:75
std::function< void()> Task
Definition event_loop.hpp:18