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.

329 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 6 technical preview.
  4. Copyright (c) 2020 - 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 this technical preview, this file is not subject to commercial licensing.
  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. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  17. extern void setThreadDPIAwarenessForWindow (HWND);
  18. #endif
  19. //==============================================================================
  20. class OpenGLContext::NativeContext
  21. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  22. : private Timer
  23. #endif
  24. {
  25. public:
  26. NativeContext (Component& component,
  27. const OpenGLPixelFormat& pixelFormat,
  28. void* contextToShareWith,
  29. bool /*useMultisampling*/,
  30. OpenGLVersion)
  31. {
  32. dummyComponent.reset (new DummyComponent (*this));
  33. createNativeWindow (component);
  34. PIXELFORMATDESCRIPTOR pfd;
  35. initialisePixelFormatDescriptor (pfd, pixelFormat);
  36. auto pixFormat = ChoosePixelFormat (dc, &pfd);
  37. if (pixFormat != 0)
  38. SetPixelFormat (dc, pixFormat, &pfd);
  39. renderContext = wglCreateContext (dc);
  40. if (renderContext != nullptr)
  41. {
  42. makeActive();
  43. initialiseGLExtensions();
  44. auto wglFormat = wglChoosePixelFormatExtension (pixelFormat);
  45. deactivateCurrentContext();
  46. if (wglFormat != pixFormat && wglFormat != 0)
  47. {
  48. // can't change the pixel format of a window, so need to delete the
  49. // old one and create a new one..
  50. releaseDC();
  51. nativeWindow = nullptr;
  52. createNativeWindow (component);
  53. if (SetPixelFormat (dc, wglFormat, &pfd))
  54. {
  55. deleteRenderContext();
  56. renderContext = wglCreateContext (dc);
  57. }
  58. }
  59. if (contextToShareWith != nullptr)
  60. wglShareLists ((HGLRC) contextToShareWith, renderContext);
  61. component.getTopLevelComponent()->repaint();
  62. component.repaint();
  63. }
  64. }
  65. ~NativeContext()
  66. {
  67. deleteRenderContext();
  68. releaseDC();
  69. }
  70. bool initialiseOnRenderThread (OpenGLContext& c)
  71. {
  72. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  73. setThreadDPIAwarenessForWindow ((HWND) nativeWindow->getNativeHandle());
  74. #endif
  75. context = &c;
  76. return true;
  77. }
  78. void shutdownOnRenderThread() { deactivateCurrentContext(); context = nullptr; }
  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. struct DummyComponent : public Component
  121. {
  122. DummyComponent (NativeContext& c) : context (c) {}
  123. // The windowing code will call this when a paint callback happens
  124. void handleCommandMessage (int) override { context.triggerRepaint(); }
  125. NativeContext& context;
  126. };
  127. std::unique_ptr<DummyComponent> dummyComponent;
  128. std::unique_ptr<ComponentPeer> nativeWindow;
  129. HGLRC renderContext;
  130. HDC dc;
  131. OpenGLContext* context = {};
  132. double nativeScaleFactor = 1.0;
  133. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  134. Component::SafePointer<Component> safeComponent;
  135. #endif
  136. #define JUCE_DECLARE_WGL_EXTENSION_FUNCTION(name, returnType, params) \
  137. typedef returnType (__stdcall *type_ ## name) params; type_ ## name name;
  138. JUCE_DECLARE_WGL_EXTENSION_FUNCTION (wglChoosePixelFormatARB, BOOL, (HDC, const int*, const FLOAT*, UINT, int*, UINT*))
  139. JUCE_DECLARE_WGL_EXTENSION_FUNCTION (wglSwapIntervalEXT, BOOL, (int))
  140. JUCE_DECLARE_WGL_EXTENSION_FUNCTION (wglGetSwapIntervalEXT, int, ())
  141. #undef JUCE_DECLARE_WGL_EXTENSION_FUNCTION
  142. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  143. void timerCallback() override
  144. {
  145. if (safeComponent != nullptr)
  146. {
  147. if (auto* peer = safeComponent->getTopLevelComponent()->getPeer())
  148. {
  149. auto newScale = peer->getPlatformScaleFactor();
  150. if (! approximatelyEqual (newScale, nativeScaleFactor))
  151. {
  152. nativeScaleFactor = newScale;
  153. updateWindowPosition (peer->getAreaCoveredBy (*safeComponent));
  154. }
  155. }
  156. }
  157. }
  158. #endif
  159. void initialiseGLExtensions()
  160. {
  161. #define JUCE_INIT_WGL_FUNCTION(name) name = (type_ ## name) OpenGLHelpers::getExtensionFunction (#name);
  162. JUCE_INIT_WGL_FUNCTION (wglChoosePixelFormatARB);
  163. JUCE_INIT_WGL_FUNCTION (wglSwapIntervalEXT);
  164. JUCE_INIT_WGL_FUNCTION (wglGetSwapIntervalEXT);
  165. #undef JUCE_INIT_WGL_FUNCTION
  166. }
  167. void createNativeWindow (Component& component)
  168. {
  169. auto* topComp = component.getTopLevelComponent();
  170. nativeWindow.reset (createNonRepaintingEmbeddedWindowsPeer (*dummyComponent, topComp->getWindowHandle()));
  171. if (auto* peer = topComp->getPeer())
  172. {
  173. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  174. safeComponent = Component::SafePointer<Component> (&component);
  175. nativeScaleFactor = peer->getPlatformScaleFactor();
  176. startTimer (50);
  177. #endif
  178. updateWindowPosition (peer->getAreaCoveredBy (component));
  179. }
  180. nativeWindow->setVisible (true);
  181. dc = GetDC ((HWND) nativeWindow->getNativeHandle());
  182. }
  183. void deleteRenderContext()
  184. {
  185. if (renderContext != nullptr)
  186. {
  187. wglDeleteContext (renderContext);
  188. renderContext = nullptr;
  189. }
  190. }
  191. void releaseDC()
  192. {
  193. ReleaseDC ((HWND) nativeWindow->getNativeHandle(), dc);
  194. }
  195. static void initialisePixelFormatDescriptor (PIXELFORMATDESCRIPTOR& pfd, const OpenGLPixelFormat& pixelFormat)
  196. {
  197. zerostruct (pfd);
  198. pfd.nSize = sizeof (pfd);
  199. pfd.nVersion = 1;
  200. pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
  201. pfd.iPixelType = PFD_TYPE_RGBA;
  202. pfd.iLayerType = PFD_MAIN_PLANE;
  203. pfd.cColorBits = (BYTE) (pixelFormat.redBits + pixelFormat.greenBits + pixelFormat.blueBits);
  204. pfd.cRedBits = (BYTE) pixelFormat.redBits;
  205. pfd.cGreenBits = (BYTE) pixelFormat.greenBits;
  206. pfd.cBlueBits = (BYTE) pixelFormat.blueBits;
  207. pfd.cAlphaBits = (BYTE) pixelFormat.alphaBits;
  208. pfd.cDepthBits = (BYTE) pixelFormat.depthBufferBits;
  209. pfd.cStencilBits = (BYTE) pixelFormat.stencilBufferBits;
  210. pfd.cAccumBits = (BYTE) (pixelFormat.accumulationBufferRedBits + pixelFormat.accumulationBufferGreenBits
  211. + pixelFormat.accumulationBufferBlueBits + pixelFormat.accumulationBufferAlphaBits);
  212. pfd.cAccumRedBits = (BYTE) pixelFormat.accumulationBufferRedBits;
  213. pfd.cAccumGreenBits = (BYTE) pixelFormat.accumulationBufferGreenBits;
  214. pfd.cAccumBlueBits = (BYTE) pixelFormat.accumulationBufferBlueBits;
  215. pfd.cAccumAlphaBits = (BYTE) pixelFormat.accumulationBufferAlphaBits;
  216. }
  217. int wglChoosePixelFormatExtension (const OpenGLPixelFormat& pixelFormat) const
  218. {
  219. int format = 0;
  220. if (wglChoosePixelFormatARB != nullptr)
  221. {
  222. int atts[64];
  223. int n = 0;
  224. atts[n++] = WGL_DRAW_TO_WINDOW_ARB; atts[n++] = GL_TRUE;
  225. atts[n++] = WGL_SUPPORT_OPENGL_ARB; atts[n++] = GL_TRUE;
  226. atts[n++] = WGL_DOUBLE_BUFFER_ARB; atts[n++] = GL_TRUE;
  227. atts[n++] = WGL_PIXEL_TYPE_ARB; atts[n++] = WGL_TYPE_RGBA_ARB;
  228. atts[n++] = WGL_ACCELERATION_ARB;
  229. atts[n++] = WGL_FULL_ACCELERATION_ARB;
  230. atts[n++] = WGL_COLOR_BITS_ARB; atts[n++] = pixelFormat.redBits + pixelFormat.greenBits + pixelFormat.blueBits;
  231. atts[n++] = WGL_RED_BITS_ARB; atts[n++] = pixelFormat.redBits;
  232. atts[n++] = WGL_GREEN_BITS_ARB; atts[n++] = pixelFormat.greenBits;
  233. atts[n++] = WGL_BLUE_BITS_ARB; atts[n++] = pixelFormat.blueBits;
  234. atts[n++] = WGL_ALPHA_BITS_ARB; atts[n++] = pixelFormat.alphaBits;
  235. atts[n++] = WGL_DEPTH_BITS_ARB; atts[n++] = pixelFormat.depthBufferBits;
  236. atts[n++] = WGL_STENCIL_BITS_ARB; atts[n++] = pixelFormat.stencilBufferBits;
  237. atts[n++] = WGL_ACCUM_RED_BITS_ARB; atts[n++] = pixelFormat.accumulationBufferRedBits;
  238. atts[n++] = WGL_ACCUM_GREEN_BITS_ARB; atts[n++] = pixelFormat.accumulationBufferGreenBits;
  239. atts[n++] = WGL_ACCUM_BLUE_BITS_ARB; atts[n++] = pixelFormat.accumulationBufferBlueBits;
  240. atts[n++] = WGL_ACCUM_ALPHA_BITS_ARB; atts[n++] = pixelFormat.accumulationBufferAlphaBits;
  241. if (pixelFormat.multisamplingLevel > 0
  242. && OpenGLHelpers::isExtensionSupported ("GL_ARB_multisample"))
  243. {
  244. atts[n++] = WGL_SAMPLE_BUFFERS_ARB;
  245. atts[n++] = 1;
  246. atts[n++] = WGL_SAMPLES_ARB;
  247. atts[n++] = pixelFormat.multisamplingLevel;
  248. }
  249. atts[n++] = 0;
  250. jassert (n <= numElementsInArray (atts));
  251. UINT formatsCount = 0;
  252. wglChoosePixelFormatARB (dc, atts, nullptr, 1, &format, &formatsCount);
  253. }
  254. return format;
  255. }
  256. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
  257. };
  258. //==============================================================================
  259. bool OpenGLHelpers::isContextActive()
  260. {
  261. return wglGetCurrentContext() != nullptr;
  262. }
  263. } // namespace juce