#1721·JUCE

[Bug]: Windows peer reports Direct2D as an available renderer under Wine but never selects it

Author: giang17Created Aug 29, 2026Updated Aug 29, 2026

Detailed steps on how to reproduce the bug

The Windows peer's renderer selection contradicts its own public API when juce_isRunningInWine() is true.

Component::createNewPeer hard-selects index 0 (juce_Windowing_windows.cpp, develop @ a18ae60e46, 2026-08-27):

cpp
extern bool juce_isRunningInWine();

ComponentPeer* Component::createNewPeer (int styleFlags, void* parentHWND)
{
    const auto renderer = juce_isRunningInWine() ? 0 : 1;
    return new HWNDComponentPeer { *this, styleFlags, (HWND) parentHWND, false, renderer };
}

getAvailableRenderingEngines() and setCurrentRenderingEngine() in the same file know nothing about that condition — both are unconditional over contextDescriptorList<GDIRenderContext, D2DRenderContext>.

I built a minimal JUCE 8.0.15 GUI app (MinGW cross-compile) that logs the peer's renderer API and then calls setCurrentRenderingEngine(). Run under Wine with wine_get_version left visible. The tree is the 8.0.15 tag plus nine small MinGW compile fixes (__uuidof on interface types instead of variables, one header-signature argument, a missing CLSID definition, CPUID); none of them touch renderer or image-type selection:

--- renderer API state as reported by JUCE ---
getAvailableRenderingEngines(): Software Renderer | Direct2D   (count=2)
getCurrentRenderingEngine():    0  -> Software Renderer
calling setCurrentRenderingEngine(1)
getCurrentRenderingEngine() after call: 1  -> Direct2D
RESULT: switch ACCEPTED

So at run time, under Wine:

  1. getAvailableRenderingEngines() advertises Direct2D as available.
  2. createNewPeer never selects it.
  3. setCurrentRenderingEngine (1) accepts it and builds the D2DRenderContext.

Counting d2d1 entry points with WINEDEBUG=+d2d over identical 40-second runs of that app confirms the switch is real and not just a change of name:

setCurrentRenderingEngine d2d1 calls of which d2d_device_context_*
not called (peer's own choice) 28 0
(0) — Software Renderer 28 0
(1) — Direct2D 889 590

Either Direct2D is unusable in this configuration, in which case (1) and (3) should not offer or accept it; or it is usable, in which case (2) is the odd one out. Right now the API asserts both.

There is a second, related asymmetry. NativeImageType::create (juce_Direct2DImage_windows.cpp) applies the same detection:

cpp
if (! juce_isRunningInWine())
{
    ...
    return new Direct2DPixelData (format, width, height, clearImage);
}
return new SoftwarePixelData { format, width, height, clearImage };

That one has no peer, and so no equivalent of setCurrentRenderingEngineDirect2DPixelData is not reachable through the public API (the class lives in a native header the module does not export). A caller who switches the peer to Direct2D through the public API still gets software-backed images, so the two halves of the decision can disagree with each other.

What is the expected behaviour?

That the renderer API agrees with itself. Either:

  • getAvailableRenderingEngines() omits Direct2D and setCurrentRenderingEngine rejects it while the forced selection is in effect, so the constraint is visible through the public API and NativeImageType::create is consistent with it; or
  • the forced selection in createNewPeer acts as a default rather than an override, so that a caller who explicitly asks for Direct2D — which the API already lets them do — gets it consistently, images included.

Which of the two is right is your call. The current state is that getAvailableRenderingEngines() reports a renderer that createNewPeer will never pick and that NativeImageType::create will never honour.

For context: 5179690ff7 ("Restore Wine functionality") introduced the forced selection between 8.0.12 and 8.0.13. This is not a request to support Wine, and the default behaviour introduced there is not in question — only that one selection path bypasses the API that describes it.

Operating systems

Windows

What versions of the operating systems?

Observed on Linux under a Wine build based on 11.0 stable (giang17/wine, d2d1-dcomp-11.0, which implements the Direct2D 1.3 surface the renderer needs), which is where juce_isRunningInWine() returns true. The API behaviour above does not depend on that build — setCurrentRenderingEngine constructs the context unconditionally — but the d2d1 call counts do. The inconsistency is in the Windows peer itself; on Windows the predicate is simply always false, so it is latent there.

Architectures

x86_64

Stacktrace

No response

Plug-in formats (if applicable)

Standalone

Plug-in host applications (DAWs) (if applicable)

No response

Testing on the develop branch

The bug is present on the develop branch (read in the source at a18ae60e46; the run-time test above was built from the 8.0.15 tag, where the functions involved are the same)

Code of Conduct

  • I agree to follow the Code of Conduct