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.

346 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. extern XContext windowHandleXContext;
  21. struct XFreeDeleter
  22. {
  23. void operator() (void* ptr) const
  24. {
  25. if (ptr != nullptr)
  26. X11Symbols::getInstance()->xFree (ptr);
  27. }
  28. };
  29. template <typename Data>
  30. std::unique_ptr<Data, XFreeDeleter> makeXFreePtr (Data* raw) { return std::unique_ptr<Data, XFreeDeleter> (raw); }
  31. //==============================================================================
  32. // Defined juce_linux_Windowing.cpp
  33. void juce_LinuxAddRepaintListener (ComponentPeer*, Component* dummy);
  34. void juce_LinuxRemoveRepaintListener (ComponentPeer*, Component* dummy);
  35. //==============================================================================
  36. class OpenGLContext::NativeContext
  37. {
  38. private:
  39. struct DummyComponent : public Component
  40. {
  41. DummyComponent (OpenGLContext::NativeContext& nativeParentContext)
  42. : native (nativeParentContext)
  43. {
  44. }
  45. void handleCommandMessage (int commandId) override
  46. {
  47. if (commandId == 0)
  48. native.triggerRepaint();
  49. }
  50. OpenGLContext::NativeContext& native;
  51. };
  52. public:
  53. NativeContext (Component& comp,
  54. const OpenGLPixelFormat& cPixelFormat,
  55. void* shareContext,
  56. bool useMultisamplingIn,
  57. OpenGLVersion)
  58. : component (comp), contextToShareWith (shareContext), dummy (*this)
  59. {
  60. display = XWindowSystem::getInstance()->getDisplay();
  61. XWindowSystemUtilities::ScopedXLock xLock;
  62. X11Symbols::getInstance()->xSync (display, False);
  63. const std::vector<GLint> optionalAttribs
  64. {
  65. GLX_SAMPLE_BUFFERS, useMultisamplingIn ? 1 : 0,
  66. GLX_SAMPLES, cPixelFormat.multisamplingLevel
  67. };
  68. if (! tryChooseVisual (cPixelFormat, optionalAttribs) && ! tryChooseVisual (cPixelFormat, {}))
  69. return;
  70. auto* peer = component.getPeer();
  71. jassert (peer != nullptr);
  72. auto windowH = (Window) peer->getNativeHandle();
  73. auto visual = glXGetVisualFromFBConfig (display, *bestConfig);
  74. auto colourMap = X11Symbols::getInstance()->xCreateColormap (display, windowH, visual->visual, AllocNone);
  75. XSetWindowAttributes swa;
  76. swa.colormap = colourMap;
  77. swa.border_pixel = 0;
  78. swa.event_mask = embeddedWindowEventMask;
  79. auto glBounds = component.getTopLevelComponent()->getLocalArea (&component, component.getLocalBounds());
  80. glBounds = Desktop::getInstance().getDisplays().logicalToPhysical (glBounds);
  81. embeddedWindow = X11Symbols::getInstance()->xCreateWindow (display, windowH,
  82. glBounds.getX(), glBounds.getY(),
  83. (unsigned int) jmax (1, glBounds.getWidth()),
  84. (unsigned int) jmax (1, glBounds.getHeight()),
  85. 0, visual->depth,
  86. InputOutput,
  87. visual->visual,
  88. CWBorderPixel | CWColormap | CWEventMask,
  89. &swa);
  90. X11Symbols::getInstance()->xSaveContext (display, (XID) embeddedWindow, windowHandleXContext, (XPointer) peer);
  91. X11Symbols::getInstance()->xMapWindow (display, embeddedWindow);
  92. X11Symbols::getInstance()->xFreeColormap (display, colourMap);
  93. X11Symbols::getInstance()->xSync (display, False);
  94. juce_LinuxAddRepaintListener (peer, &dummy);
  95. }
  96. ~NativeContext()
  97. {
  98. if (auto* peer = component.getPeer())
  99. {
  100. juce_LinuxRemoveRepaintListener (peer, &dummy);
  101. if (embeddedWindow != 0)
  102. {
  103. XWindowSystemUtilities::ScopedXLock xLock;
  104. X11Symbols::getInstance()->xUnmapWindow (display, embeddedWindow);
  105. X11Symbols::getInstance()->xDestroyWindow (display, embeddedWindow);
  106. X11Symbols::getInstance()->xSync (display, False);
  107. XEvent event;
  108. while (X11Symbols::getInstance()->xCheckWindowEvent (display,
  109. embeddedWindow,
  110. embeddedWindowEventMask,
  111. &event) == True)
  112. {
  113. }
  114. }
  115. }
  116. }
  117. bool initialiseOnRenderThread (OpenGLContext& c)
  118. {
  119. XWindowSystemUtilities::ScopedXLock xLock;
  120. const auto components = [&]() -> Optional<Version>
  121. {
  122. switch (c.versionRequired)
  123. {
  124. case OpenGLVersion::openGL3_2: return Version { 3, 2 };
  125. case OpenGLVersion::openGL4_1: return Version { 4, 1 };
  126. case OpenGLVersion::openGL4_3: return Version { 4, 3 };
  127. case OpenGLVersion::defaultGLVersion: break;
  128. }
  129. return {};
  130. }();
  131. if (components.hasValue())
  132. {
  133. using GLXCreateContextAttribsARB = GLXContext (*) (Display*, GLXFBConfig, GLXContext, Bool, const int*);
  134. if (const auto glXCreateContextAttribsARB = (GLXCreateContextAttribsARB) OpenGLHelpers::getExtensionFunction ("glXCreateContextAttribsARB"))
  135. {
  136. #if JUCE_DEBUG
  137. constexpr auto contextFlags = GLX_CONTEXT_DEBUG_BIT_ARB;
  138. #else
  139. constexpr auto contextFlags = 0;
  140. #endif
  141. const int attribs[]
  142. {
  143. GLX_CONTEXT_MAJOR_VERSION_ARB, components->major,
  144. GLX_CONTEXT_MINOR_VERSION_ARB, components->minor,
  145. GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
  146. GLX_CONTEXT_FLAGS_ARB, contextFlags,
  147. None
  148. };
  149. renderContext = glXCreateContextAttribsARB (display, *bestConfig, (GLXContext) contextToShareWith, GL_TRUE, attribs);
  150. }
  151. }
  152. if (renderContext == nullptr)
  153. renderContext = glXCreateNewContext (display, *bestConfig, GLX_RGBA_TYPE, (GLXContext) contextToShareWith, GL_TRUE);
  154. if (renderContext == nullptr)
  155. return false;
  156. c.makeActive();
  157. context = &c;
  158. return true;
  159. }
  160. void shutdownOnRenderThread()
  161. {
  162. XWindowSystemUtilities::ScopedXLock xLock;
  163. context = nullptr;
  164. deactivateCurrentContext();
  165. glXDestroyContext (display, renderContext);
  166. renderContext = nullptr;
  167. }
  168. bool makeActive() const noexcept
  169. {
  170. XWindowSystemUtilities::ScopedXLock xLock;
  171. return renderContext != nullptr
  172. && glXMakeCurrent (display, embeddedWindow, renderContext);
  173. }
  174. bool isActive() const noexcept
  175. {
  176. XWindowSystemUtilities::ScopedXLock xLock;
  177. return glXGetCurrentContext() == renderContext && renderContext != nullptr;
  178. }
  179. static void deactivateCurrentContext()
  180. {
  181. if (auto* display = XWindowSystem::getInstance()->getDisplay())
  182. {
  183. XWindowSystemUtilities::ScopedXLock xLock;
  184. glXMakeCurrent (display, None, nullptr);
  185. }
  186. }
  187. void swapBuffers()
  188. {
  189. XWindowSystemUtilities::ScopedXLock xLock;
  190. glXSwapBuffers (display, embeddedWindow);
  191. }
  192. void updateWindowPosition (Rectangle<int> newBounds)
  193. {
  194. bounds = newBounds;
  195. auto physicalBounds = Desktop::getInstance().getDisplays().logicalToPhysical (bounds);
  196. XWindowSystemUtilities::ScopedXLock xLock;
  197. X11Symbols::getInstance()->xMoveResizeWindow (display, embeddedWindow,
  198. physicalBounds.getX(), physicalBounds.getY(),
  199. (unsigned int) jmax (1, physicalBounds.getWidth()),
  200. (unsigned int) jmax (1, physicalBounds.getHeight()));
  201. }
  202. bool setSwapInterval (int numFramesPerSwap)
  203. {
  204. if (numFramesPerSwap == swapFrames)
  205. return true;
  206. if (auto GLXSwapIntervalSGI
  207. = (PFNGLXSWAPINTERVALSGIPROC) OpenGLHelpers::getExtensionFunction ("glXSwapIntervalSGI"))
  208. {
  209. XWindowSystemUtilities::ScopedXLock xLock;
  210. swapFrames = numFramesPerSwap;
  211. GLXSwapIntervalSGI (numFramesPerSwap);
  212. return true;
  213. }
  214. return false;
  215. }
  216. int getSwapInterval() const { return swapFrames; }
  217. bool createdOk() const noexcept { return true; }
  218. void* getRawContext() const noexcept { return renderContext; }
  219. GLuint getFrameBufferID() const noexcept { return 0; }
  220. void triggerRepaint()
  221. {
  222. if (context != nullptr)
  223. context->triggerRepaint();
  224. }
  225. struct Locker
  226. {
  227. explicit Locker (NativeContext& ctx) : lock (ctx.mutex) {}
  228. const ScopedLock lock;
  229. };
  230. private:
  231. bool tryChooseVisual (const OpenGLPixelFormat& format, const std::vector<GLint>& optionalAttribs)
  232. {
  233. std::vector<GLint> allAttribs
  234. {
  235. GLX_RENDER_TYPE, GLX_RGBA_BIT,
  236. GLX_DOUBLEBUFFER, True,
  237. GLX_RED_SIZE, format.redBits,
  238. GLX_GREEN_SIZE, format.greenBits,
  239. GLX_BLUE_SIZE, format.blueBits,
  240. GLX_ALPHA_SIZE, format.alphaBits,
  241. GLX_DEPTH_SIZE, format.depthBufferBits,
  242. GLX_STENCIL_SIZE, format.stencilBufferBits,
  243. GLX_ACCUM_RED_SIZE, format.accumulationBufferRedBits,
  244. GLX_ACCUM_GREEN_SIZE, format.accumulationBufferGreenBits,
  245. GLX_ACCUM_BLUE_SIZE, format.accumulationBufferBlueBits,
  246. GLX_ACCUM_ALPHA_SIZE, format.accumulationBufferAlphaBits
  247. };
  248. allAttribs.insert (allAttribs.end(), optionalAttribs.begin(), optionalAttribs.end());
  249. allAttribs.push_back (None);
  250. int nElements = 0;
  251. bestConfig = makeXFreePtr (glXChooseFBConfig (display, X11Symbols::getInstance()->xDefaultScreen (display), allAttribs.data(), &nElements));
  252. return nElements != 0 && bestConfig != nullptr;
  253. }
  254. static constexpr int embeddedWindowEventMask = ExposureMask | StructureNotifyMask;
  255. CriticalSection mutex;
  256. Component& component;
  257. GLXContext renderContext = {};
  258. Window embeddedWindow = {};
  259. int swapFrames = 1;
  260. Rectangle<int> bounds;
  261. std::unique_ptr<GLXFBConfig, XFreeDeleter> bestConfig;
  262. void* contextToShareWith;
  263. OpenGLContext* context = nullptr;
  264. DummyComponent dummy;
  265. ::Display* display = nullptr;
  266. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
  267. };
  268. //==============================================================================
  269. bool OpenGLHelpers::isContextActive()
  270. {
  271. XWindowSystemUtilities::ScopedXLock xLock;
  272. return glXGetCurrentContext() != nullptr;
  273. }
  274. } // namespace juce