You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Found while reviewing #572. That one was a VST3 problem — an inverted tresult and a cached rect handed to the host while a reentrant getSize was writing to it. AUv2 does not have that shape, but the same corner is wrong here in a different way, and AUv2 is the only one of the four wrappers left with it.
_window holds the CLAP_WRAPPER_COCOA_CLASS_NSVIEW instance, and that class overrides -setFrame:, so the message lands on the override and set_size() goes straight back into the plugin — from inside the plugin's own request_resize() call, with the numbers it just passed in.
1. The plugin is reentered synchronously
Every other wrapper deliberately avoids this:
VST3 calls IPlugFrame::resizeView and lets the host come back through onSize later. That is the sequence Fix VST3 request_resize #572 just aligned with.
AUv3 hands the request to the main queue (src/detail/auv3/auv3_audiounit.mm:577) and returns.
The standalone forwards to onRequestResize and returns false when there is nothing wired up.
A plugin asks for 800x600 and gets set_size(800, 600) called on it before request_resize has returned. Editors that recompute their layout in set_size and ask again for the size they settle on will bounce between the two.
src/detail/auv2/wrappedview.asinclude.mm:29 already declares the flag for this:
bool setSizeByZoom; // use this flag to see if resize comes from here or from external
It is never read or written anywhere in the tree. Same for underlyingUISize and lastScale on the two lines above it.
2. Nothing marshals to the main thread
request_resize is [thread-safe] in clap/ext/gui.h, so a plugin may call it from a worker. AUv2 then calls -setFrame: off that thread, which AppKit does not allow, and set_size() off it as well, which CLAP marks [main-thread].
Since #536 the -setFrame: override also takes the AU main-thread mutex, and the SDK holds that across its whole non-realtime dispatch, so an off-thread request_resize can park a plugin worker behind host initialization.
VST3 checks the thread id and defers through an atomic serviced in onIdle (src/wrapasvst3.cpp:1665). AUv3 dispatches. AUv2 does neither.
3. The answer to the plugin is always true
src/detail/auv2/auv2_shared.mm:17 returns true unconditionally and gui_request_resize passes it through, so the plugin never finds out the host did nothing with the request.
That also contradicts gui_can_resize() four lines above it (src/detail/auv2/auv2_base_classes.h:674), which returns false. The wrapper tells the plugin the host cannot resize, then tells it the resize worked.
4. setFrame: does not ask adjust_size
src/detail/auv2/wrappedview.asinclude.mm:204 pushes the host's size into set_size as-is. VST3 runs it through adjust_size in both onSize and checkSizeConstraint and uses what comes back, and #565 did the same for AUv3.
Suggested shape
Marshal the request to the main thread. Either dispatch_async onto the main queue the way AUv3 does, or park it and service it from the idle tick the way VST3 does — AUv2 already has onIdle().
Use setSizeByZoom for what it was declared for: set it around the wrapper-initiated -setFrame: so the override skips the set_size echo, and clear it after. A resize the host initiates still has to reach the plugin.
Run the size through adjust_size in -setFrame: before calling set_size, and honour the result.
Decide what the return value should say. AUv2 Cocoa gives no synchronous answer from the host, so either keep returning true — clap/ext/gui.h allows that for a request that is handled asynchronously, provided the host calls set_size to revert if it cannot satisfy it — or make gui_can_resize() honest first. Right now the two disagree with each other.
Note for whoever picks this up
ui_connection::_window is declared clap_window_t * but carries the NSView. That is the contract on AUv2, not an oversight — the cast in at wrappedview.asinclude.mm:99 and back out at auv2_shared.mm:14 is deliberate and the compiler cannot check it. Leave it alone.
The wrapper targets build with -Wall -Wextra -Wpedantic -Werror, and commits have to be clang-formatted against .clang-format.
auval never opens the editor, so it will not exercise any of this. It is there to show nothing else broke. Confirming the fix itself needs a real host — Logic or Live with a plugin that resizes itself.
Found while reviewing #572. That one was a VST3 problem — an inverted
tresultand a cached rect handed to the host while a reentrantgetSizewas writing to it. AUv2 does not have that shape, but the same corner is wrong here in a different way, and AUv2 is the only one of the four wrappers left with it.The call chain
_windowholds theCLAP_WRAPPER_COCOA_CLASS_NSVIEWinstance, and that class overrides-setFrame:, so the message lands on the override andset_size()goes straight back into the plugin — from inside the plugin's ownrequest_resize()call, with the numbers it just passed in.1. The plugin is reentered synchronously
Every other wrapper deliberately avoids this:
IPlugFrame::resizeViewand lets the host come back throughonSizelater. That is the sequence Fix VST3 request_resize #572 just aligned with.src/detail/auv3/auv3_audiounit.mm:577) and returns.onRequestResizeand returns false when there is nothing wired up.A plugin asks for 800x600 and gets
set_size(800, 600)called on it beforerequest_resizehas returned. Editors that recompute their layout inset_sizeand ask again for the size they settle on will bounce between the two.src/detail/auv2/wrappedview.asinclude.mm:29already declares the flag for this:It is never read or written anywhere in the tree. Same for
underlyingUISizeandlastScaleon the two lines above it.2. Nothing marshals to the main thread
request_resizeis[thread-safe]in clap/ext/gui.h, so a plugin may call it from a worker. AUv2 then calls-setFrame:off that thread, which AppKit does not allow, andset_size()off it as well, which CLAP marks[main-thread].Since #536 the
-setFrame:override also takes the AU main-thread mutex, and the SDK holds that across its whole non-realtime dispatch, so an off-threadrequest_resizecan park a plugin worker behind host initialization.VST3 checks the thread id and defers through an atomic serviced in
onIdle(src/wrapasvst3.cpp:1665). AUv3 dispatches. AUv2 does neither.3. The answer to the plugin is always true
src/detail/auv2/auv2_shared.mm:17returnstrueunconditionally andgui_request_resizepasses it through, so the plugin never finds out the host did nothing with the request.That also contradicts
gui_can_resize()four lines above it (src/detail/auv2/auv2_base_classes.h:674), which returnsfalse. The wrapper tells the plugin the host cannot resize, then tells it the resize worked.4. setFrame: does not ask adjust_size
src/detail/auv2/wrappedview.asinclude.mm:204pushes the host's size intoset_sizeas-is. VST3 runs it throughadjust_sizein bothonSizeandcheckSizeConstraintand uses what comes back, and #565 did the same for AUv3.Suggested shape
dispatch_asynconto the main queue the way AUv3 does, or park it and service it from the idle tick the way VST3 does — AUv2 already hasonIdle().setSizeByZoomfor what it was declared for: set it around the wrapper-initiated-setFrame:so the override skips theset_sizeecho, and clear it after. A resize the host initiates still has to reach the plugin.adjust_sizein-setFrame:before callingset_size, and honour the result.set_sizeto revert if it cannot satisfy it — or makegui_can_resize()honest first. Right now the two disagree with each other.Note for whoever picks this up
ui_connection::_windowis declaredclap_window_t *but carries theNSView. That is the contract on AUv2, not an oversight — the cast in atwrappedview.asinclude.mm:99and back out atauv2_shared.mm:14is deliberate and the compiler cannot check it. Leave it alone.Building and checking
macOS only, AUv2 is not built anywhere else.
and the clap-first component through auval, which is what CI runs:
The wrapper targets build with
-Wall -Wextra -Wpedantic -Werror, and commits have to be clang-formatted against.clang-format.auval never opens the editor, so it will not exercise any of this. It is there to show nothing else broke. Confirming the fix itself needs a real host — Logic or Live with a plugin that resizes itself.