The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

364 lines
14KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 7 technical preview.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For the technical preview this file cannot be licensed commercially.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. namespace juce
  14. {
  15. extern ComponentPeer* createNonRepaintingEmbeddedWindowsPeer (Component&, void* parent);
  16. //==============================================================================
  17. class OpenGLContext::NativeContext : private ComponentPeer::ScaleFactorListener
  18. {
  19. public:
  20. NativeContext (Component& component,
  21. const OpenGLPixelFormat& pixelFormat,
  22. void* contextToShareWithIn,
  23. bool /*useMultisampling*/,
  24. OpenGLVersion version)
  25. {
  26. dummyComponent.reset (new DummyComponent (*this));
  27. createNativeWindow (component);
  28. PIXELFORMATDESCRIPTOR pfd;
  29. initialisePixelFormatDescriptor (pfd, pixelFormat);
  30. auto pixFormat = ChoosePixelFormat (dc, &pfd);
  31. if (pixFormat != 0)
  32. SetPixelFormat (dc, pixFormat, &pfd);
  33. initialiseWGLExtensions (dc);
  34. renderContext = createRenderContext (version, dc);
  35. if (renderContext != nullptr)
  36. {
  37. makeActive();
  38. auto wglFormat = wglChoosePixelFormatExtension (pixelFormat);
  39. deactivateCurrentContext();
  40. if (wglFormat != pixFormat && wglFormat != 0)
  41. {
  42. // can't change the pixel format of a window, so need to delete the
  43. // old one and create a new one..
  44. releaseDC();
  45. nativeWindow = nullptr;
  46. createNativeWindow (component);
  47. if (SetPixelFormat (dc, wglFormat, &pfd))
  48. {
  49. deleteRenderContext();
  50. renderContext = createRenderContext (version, dc);
  51. }
  52. }
  53. if (contextToShareWithIn != nullptr)
  54. wglShareLists ((HGLRC) contextToShareWithIn, renderContext);
  55. component.getTopLevelComponent()->repaint();
  56. component.repaint();
  57. }
  58. }
  59. ~NativeContext() override
  60. {
  61. deleteRenderContext();
  62. releaseDC();
  63. if (safeComponent != nullptr)
  64. if (auto* peer = safeComponent->getTopLevelComponent()->getPeer())
  65. peer->removeScaleFactorListener (this);
  66. }
  67. bool initialiseOnRenderThread (OpenGLContext& c)
  68. {
  69. threadAwarenessSetter = std::make_unique<ScopedThreadDPIAwarenessSetter> (nativeWindow->getNativeHandle());
  70. context = &c;
  71. return true;
  72. }
  73. void shutdownOnRenderThread()
  74. {
  75. deactivateCurrentContext();
  76. context = nullptr;
  77. threadAwarenessSetter = nullptr;
  78. }
  79. static void deactivateCurrentContext() { wglMakeCurrent (nullptr, nullptr); }
  80. bool makeActive() const noexcept { return isActive() || wglMakeCurrent (dc, renderContext) != FALSE; }
  81. bool isActive() const noexcept { return wglGetCurrentContext() == renderContext; }
  82. void swapBuffers() const noexcept { SwapBuffers (dc); }
  83. bool setSwapInterval (int numFramesPerSwap)
  84. {
  85. jassert (isActive()); // this can only be called when the context is active..
  86. return wglSwapIntervalEXT != nullptr && wglSwapIntervalEXT (numFramesPerSwap) != FALSE;
  87. }
  88. int getSwapInterval() const
  89. {
  90. jassert (isActive()); // this can only be called when the context is active..
  91. return wglGetSwapIntervalEXT != nullptr ? wglGetSwapIntervalEXT() : 0;
  92. }
  93. void updateWindowPosition (Rectangle<int> bounds)
  94. {
  95. if (nativeWindow != nullptr)
  96. {
  97. if (! approximatelyEqual (nativeScaleFactor, 1.0))
  98. bounds = (bounds.toDouble() * nativeScaleFactor).toNearestInt();
  99. SetWindowPos ((HWND) nativeWindow->getNativeHandle(), nullptr,
  100. bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(),
  101. SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
  102. }
  103. }
  104. bool createdOk() const noexcept { return getRawContext() != nullptr; }
  105. void* getRawContext() const noexcept { return renderContext; }
  106. unsigned int getFrameBufferID() const noexcept { return 0; }
  107. void triggerRepaint()
  108. {
  109. if (context != nullptr)
  110. context->triggerRepaint();
  111. }
  112. struct Locker { Locker (NativeContext&) {} };
  113. HWND getNativeHandle()
  114. {
  115. if (nativeWindow != nullptr)
  116. return (HWND) nativeWindow->getNativeHandle();
  117. return nullptr;
  118. }
  119. private:
  120. //==============================================================================
  121. static void initialiseWGLExtensions (HDC dcIn)
  122. {
  123. static bool initialised = false;
  124. if (initialised)
  125. return;
  126. initialised = true;
  127. const auto dummyContext = wglCreateContext (dcIn);
  128. wglMakeCurrent (dcIn, dummyContext);
  129. #define JUCE_INIT_WGL_FUNCTION(name) name = (type_ ## name) OpenGLHelpers::getExtensionFunction (#name);
  130. JUCE_INIT_WGL_FUNCTION (wglChoosePixelFormatARB)
  131. JUCE_INIT_WGL_FUNCTION (wglSwapIntervalEXT)
  132. JUCE_INIT_WGL_FUNCTION (wglGetSwapIntervalEXT)
  133. JUCE_INIT_WGL_FUNCTION (wglCreateContextAttribsARB)
  134. #undef JUCE_INIT_WGL_FUNCTION
  135. wglMakeCurrent (nullptr, nullptr);
  136. wglDeleteContext (dummyContext);
  137. }
  138. static void initialisePixelFormatDescriptor (PIXELFORMATDESCRIPTOR& pfd, const OpenGLPixelFormat& pixelFormat)
  139. {
  140. zerostruct (pfd);
  141. pfd.nSize = sizeof (pfd);
  142. pfd.nVersion = 1;
  143. pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
  144. pfd.iPixelType = PFD_TYPE_RGBA;
  145. pfd.iLayerType = PFD_MAIN_PLANE;
  146. pfd.cColorBits = (BYTE) (pixelFormat.redBits + pixelFormat.greenBits + pixelFormat.blueBits);
  147. pfd.cRedBits = (BYTE) pixelFormat.redBits;
  148. pfd.cGreenBits = (BYTE) pixelFormat.greenBits;
  149. pfd.cBlueBits = (BYTE) pixelFormat.blueBits;
  150. pfd.cAlphaBits = (BYTE) pixelFormat.alphaBits;
  151. pfd.cDepthBits = (BYTE) pixelFormat.depthBufferBits;
  152. pfd.cStencilBits = (BYTE) pixelFormat.stencilBufferBits;
  153. pfd.cAccumBits = (BYTE) (pixelFormat.accumulationBufferRedBits + pixelFormat.accumulationBufferGreenBits
  154. + pixelFormat.accumulationBufferBlueBits + pixelFormat.accumulationBufferAlphaBits);
  155. pfd.cAccumRedBits = (BYTE) pixelFormat.accumulationBufferRedBits;
  156. pfd.cAccumGreenBits = (BYTE) pixelFormat.accumulationBufferGreenBits;
  157. pfd.cAccumBlueBits = (BYTE) pixelFormat.accumulationBufferBlueBits;
  158. pfd.cAccumAlphaBits = (BYTE) pixelFormat.accumulationBufferAlphaBits;
  159. }
  160. static HGLRC createRenderContext (OpenGLVersion version, HDC dcIn)
  161. {
  162. if (version >= openGL3_2 && wglCreateContextAttribsARB != nullptr)
  163. {
  164. const int attribs[] =
  165. {
  166. WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
  167. WGL_CONTEXT_MINOR_VERSION_ARB, 2,
  168. WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
  169. 0
  170. };
  171. const auto c = wglCreateContextAttribsARB (dcIn, nullptr, attribs);
  172. if (c != nullptr)
  173. return c;
  174. }
  175. return wglCreateContext (dcIn);
  176. }
  177. //==============================================================================
  178. struct DummyComponent : public Component
  179. {
  180. DummyComponent (NativeContext& c) : context (c) {}
  181. // The windowing code will call this when a paint callback happens
  182. void handleCommandMessage (int) override { context.triggerRepaint(); }
  183. NativeContext& context;
  184. };
  185. //==============================================================================
  186. void nativeScaleFactorChanged (double newScaleFactor) override
  187. {
  188. if (approximatelyEqual (newScaleFactor, nativeScaleFactor)
  189. || safeComponent == nullptr)
  190. return;
  191. if (auto* peer = safeComponent->getTopLevelComponent()->getPeer())
  192. {
  193. nativeScaleFactor = newScaleFactor;
  194. updateWindowPosition (peer->getAreaCoveredBy (*safeComponent));
  195. }
  196. }
  197. void createNativeWindow (Component& component)
  198. {
  199. auto* topComp = component.getTopLevelComponent();
  200. {
  201. auto* parentHWND = topComp->getWindowHandle();
  202. ScopedThreadDPIAwarenessSetter setter { parentHWND };
  203. nativeWindow.reset (createNonRepaintingEmbeddedWindowsPeer (*dummyComponent, parentHWND));
  204. }
  205. if (auto* peer = topComp->getPeer())
  206. {
  207. safeComponent = Component::SafePointer<Component> (&component);
  208. nativeScaleFactor = peer->getPlatformScaleFactor();
  209. updateWindowPosition (peer->getAreaCoveredBy (component));
  210. peer->addScaleFactorListener (this);
  211. }
  212. nativeWindow->setVisible (true);
  213. dc = GetDC ((HWND) nativeWindow->getNativeHandle());
  214. }
  215. void deleteRenderContext()
  216. {
  217. if (renderContext != nullptr)
  218. {
  219. wglDeleteContext (renderContext);
  220. renderContext = nullptr;
  221. }
  222. }
  223. void releaseDC()
  224. {
  225. ReleaseDC ((HWND) nativeWindow->getNativeHandle(), dc);
  226. }
  227. int wglChoosePixelFormatExtension (const OpenGLPixelFormat& pixelFormat) const
  228. {
  229. int format = 0;
  230. if (wglChoosePixelFormatARB != nullptr)
  231. {
  232. int atts[64];
  233. int n = 0;
  234. atts[n++] = WGL_DRAW_TO_WINDOW_ARB; atts[n++] = GL_TRUE;
  235. atts[n++] = WGL_SUPPORT_OPENGL_ARB; atts[n++] = GL_TRUE;
  236. atts[n++] = WGL_DOUBLE_BUFFER_ARB; atts[n++] = GL_TRUE;
  237. atts[n++] = WGL_PIXEL_TYPE_ARB; atts[n++] = WGL_TYPE_RGBA_ARB;
  238. atts[n++] = WGL_ACCELERATION_ARB;
  239. atts[n++] = WGL_FULL_ACCELERATION_ARB;
  240. atts[n++] = WGL_COLOR_BITS_ARB; atts[n++] = pixelFormat.redBits + pixelFormat.greenBits + pixelFormat.blueBits;
  241. atts[n++] = WGL_RED_BITS_ARB; atts[n++] = pixelFormat.redBits;
  242. atts[n++] = WGL_GREEN_BITS_ARB; atts[n++] = pixelFormat.greenBits;
  243. atts[n++] = WGL_BLUE_BITS_ARB; atts[n++] = pixelFormat.blueBits;
  244. atts[n++] = WGL_ALPHA_BITS_ARB; atts[n++] = pixelFormat.alphaBits;
  245. atts[n++] = WGL_DEPTH_BITS_ARB; atts[n++] = pixelFormat.depthBufferBits;
  246. atts[n++] = WGL_STENCIL_BITS_ARB; atts[n++] = pixelFormat.stencilBufferBits;
  247. atts[n++] = WGL_ACCUM_RED_BITS_ARB; atts[n++] = pixelFormat.accumulationBufferRedBits;
  248. atts[n++] = WGL_ACCUM_GREEN_BITS_ARB; atts[n++] = pixelFormat.accumulationBufferGreenBits;
  249. atts[n++] = WGL_ACCUM_BLUE_BITS_ARB; atts[n++] = pixelFormat.accumulationBufferBlueBits;
  250. atts[n++] = WGL_ACCUM_ALPHA_BITS_ARB; atts[n++] = pixelFormat.accumulationBufferAlphaBits;
  251. if (pixelFormat.multisamplingLevel > 0
  252. && OpenGLHelpers::isExtensionSupported ("GL_ARB_multisample"))
  253. {
  254. atts[n++] = WGL_SAMPLE_BUFFERS_ARB;
  255. atts[n++] = 1;
  256. atts[n++] = WGL_SAMPLES_ARB;
  257. atts[n++] = pixelFormat.multisamplingLevel;
  258. }
  259. atts[n++] = 0;
  260. jassert (n <= numElementsInArray (atts));
  261. UINT formatsCount = 0;
  262. wglChoosePixelFormatARB (dc, atts, nullptr, 1, &format, &formatsCount);
  263. }
  264. return format;
  265. }
  266. //==============================================================================
  267. #define JUCE_DECLARE_WGL_EXTENSION_FUNCTION(name, returnType, params) \
  268. typedef returnType (__stdcall *type_ ## name) params; static type_ ## name name;
  269. JUCE_DECLARE_WGL_EXTENSION_FUNCTION (wglChoosePixelFormatARB, BOOL, (HDC, const int*, const FLOAT*, UINT, int*, UINT*))
  270. JUCE_DECLARE_WGL_EXTENSION_FUNCTION (wglSwapIntervalEXT, BOOL, (int))
  271. JUCE_DECLARE_WGL_EXTENSION_FUNCTION (wglGetSwapIntervalEXT, int, ())
  272. JUCE_DECLARE_WGL_EXTENSION_FUNCTION (wglCreateContextAttribsARB, HGLRC, (HDC, HGLRC, const int*))
  273. #undef JUCE_DECLARE_WGL_EXTENSION_FUNCTION
  274. //==============================================================================
  275. std::unique_ptr<DummyComponent> dummyComponent;
  276. std::unique_ptr<ComponentPeer> nativeWindow;
  277. std::unique_ptr<ScopedThreadDPIAwarenessSetter> threadAwarenessSetter;
  278. Component::SafePointer<Component> safeComponent;
  279. HGLRC renderContext;
  280. HDC dc;
  281. OpenGLContext* context = nullptr;
  282. double nativeScaleFactor = 1.0;
  283. //==============================================================================
  284. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
  285. };
  286. //==============================================================================
  287. bool OpenGLHelpers::isContextActive()
  288. {
  289. return wglGetCurrentContext() != nullptr;
  290. }
  291. } // namespace juce