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.

375 lines
13KB

  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. struct XFreeDeleter
  21. {
  22. void operator() (void* ptr) const
  23. {
  24. if (ptr != nullptr)
  25. X11Symbols::getInstance()->xFree (ptr);
  26. }
  27. };
  28. template <typename Data>
  29. std::unique_ptr<Data, XFreeDeleter> makeXFreePtr (Data* raw) { return std::unique_ptr<Data, XFreeDeleter> (raw); }
  30. //==============================================================================
  31. // Defined juce_linux_Windowing.cpp
  32. void juce_LinuxAddRepaintListener (ComponentPeer*, Component* dummy);
  33. void juce_LinuxRemoveRepaintListener (ComponentPeer*, Component* dummy);
  34. class PeerListener : private ComponentMovementWatcher
  35. {
  36. public:
  37. PeerListener (Component& comp, Window embeddedWindow)
  38. : ComponentMovementWatcher (&comp),
  39. window (embeddedWindow),
  40. association (comp.getPeer(), window) {}
  41. private:
  42. using ComponentMovementWatcher::componentMovedOrResized,
  43. ComponentMovementWatcher::componentVisibilityChanged;
  44. void componentMovedOrResized (bool, bool) override {}
  45. void componentVisibilityChanged() override {}
  46. void componentPeerChanged() override
  47. {
  48. // This should not be rewritten as a ternary expression or similar.
  49. // The old association must be destroyed before the new one is created.
  50. association = {};
  51. if (auto* comp = getComponent())
  52. association = ScopedWindowAssociation (comp->getPeer(), window);
  53. }
  54. Window window{};
  55. ScopedWindowAssociation association;
  56. };
  57. //==============================================================================
  58. class OpenGLContext::NativeContext
  59. {
  60. private:
  61. struct DummyComponent : public Component
  62. {
  63. DummyComponent (OpenGLContext::NativeContext& nativeParentContext)
  64. : native (nativeParentContext)
  65. {
  66. }
  67. void handleCommandMessage (int commandId) override
  68. {
  69. if (commandId == 0)
  70. native.triggerRepaint();
  71. }
  72. OpenGLContext::NativeContext& native;
  73. };
  74. public:
  75. NativeContext (Component& comp,
  76. const OpenGLPixelFormat& cPixelFormat,
  77. void* shareContext,
  78. bool useMultisamplingIn,
  79. OpenGLVersion)
  80. : component (comp), contextToShareWith (shareContext), dummy (*this)
  81. {
  82. display = XWindowSystem::getInstance()->getDisplay();
  83. XWindowSystemUtilities::ScopedXLock xLock;
  84. X11Symbols::getInstance()->xSync (display, False);
  85. const std::vector<GLint> optionalAttribs
  86. {
  87. GLX_SAMPLE_BUFFERS, useMultisamplingIn ? 1 : 0,
  88. GLX_SAMPLES, cPixelFormat.multisamplingLevel
  89. };
  90. if (! tryChooseVisual (cPixelFormat, optionalAttribs) && ! tryChooseVisual (cPixelFormat, {}))
  91. return;
  92. auto* peer = component.getPeer();
  93. jassert (peer != nullptr);
  94. auto windowH = (Window) peer->getNativeHandle();
  95. auto visual = glXGetVisualFromFBConfig (display, *bestConfig);
  96. auto colourMap = X11Symbols::getInstance()->xCreateColormap (display, windowH, visual->visual, AllocNone);
  97. XSetWindowAttributes swa;
  98. swa.colormap = colourMap;
  99. swa.border_pixel = 0;
  100. swa.event_mask = embeddedWindowEventMask;
  101. auto glBounds = component.getTopLevelComponent()->getLocalArea (&component, component.getLocalBounds());
  102. glBounds = Desktop::getInstance().getDisplays().logicalToPhysical (glBounds);
  103. embeddedWindow = X11Symbols::getInstance()->xCreateWindow (display, windowH,
  104. glBounds.getX(), glBounds.getY(),
  105. (unsigned int) jmax (1, glBounds.getWidth()),
  106. (unsigned int) jmax (1, glBounds.getHeight()),
  107. 0, visual->depth,
  108. InputOutput,
  109. visual->visual,
  110. CWBorderPixel | CWColormap | CWEventMask,
  111. &swa);
  112. peerListener.emplace (component, embeddedWindow);
  113. X11Symbols::getInstance()->xMapWindow (display, embeddedWindow);
  114. X11Symbols::getInstance()->xFreeColormap (display, colourMap);
  115. X11Symbols::getInstance()->xSync (display, False);
  116. juce_LinuxAddRepaintListener (peer, &dummy);
  117. }
  118. ~NativeContext()
  119. {
  120. if (auto* peer = component.getPeer())
  121. {
  122. juce_LinuxRemoveRepaintListener (peer, &dummy);
  123. if (embeddedWindow != 0)
  124. {
  125. XWindowSystemUtilities::ScopedXLock xLock;
  126. X11Symbols::getInstance()->xUnmapWindow (display, embeddedWindow);
  127. X11Symbols::getInstance()->xDestroyWindow (display, embeddedWindow);
  128. X11Symbols::getInstance()->xSync (display, False);
  129. XEvent event;
  130. while (X11Symbols::getInstance()->xCheckWindowEvent (display,
  131. embeddedWindow,
  132. embeddedWindowEventMask,
  133. &event) == True)
  134. {
  135. }
  136. }
  137. }
  138. }
  139. InitResult initialiseOnRenderThread (OpenGLContext& c)
  140. {
  141. XWindowSystemUtilities::ScopedXLock xLock;
  142. const auto components = [&]() -> Optional<Version>
  143. {
  144. switch (c.versionRequired)
  145. {
  146. case OpenGLVersion::openGL3_2: return Version { 3, 2 };
  147. case OpenGLVersion::openGL4_1: return Version { 4, 1 };
  148. case OpenGLVersion::openGL4_3: return Version { 4, 3 };
  149. case OpenGLVersion::defaultGLVersion: break;
  150. }
  151. return {};
  152. }();
  153. if (components.hasValue())
  154. {
  155. using GLXCreateContextAttribsARB = GLXContext (*) (Display*, GLXFBConfig, GLXContext, Bool, const int*);
  156. if (const auto glXCreateContextAttribsARB = (GLXCreateContextAttribsARB) OpenGLHelpers::getExtensionFunction ("glXCreateContextAttribsARB"))
  157. {
  158. #if JUCE_DEBUG
  159. constexpr auto contextFlags = GLX_CONTEXT_DEBUG_BIT_ARB;
  160. #else
  161. constexpr auto contextFlags = 0;
  162. #endif
  163. const int attribs[]
  164. {
  165. GLX_CONTEXT_MAJOR_VERSION_ARB, components->major,
  166. GLX_CONTEXT_MINOR_VERSION_ARB, components->minor,
  167. GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
  168. GLX_CONTEXT_FLAGS_ARB, contextFlags,
  169. None
  170. };
  171. renderContext = glXCreateContextAttribsARB (display, *bestConfig, (GLXContext) contextToShareWith, GL_TRUE, attribs);
  172. }
  173. }
  174. if (renderContext == nullptr)
  175. renderContext = glXCreateNewContext (display, *bestConfig, GLX_RGBA_TYPE, (GLXContext) contextToShareWith, GL_TRUE);
  176. if (renderContext == nullptr)
  177. return InitResult::fatal;
  178. c.makeActive();
  179. context = &c;
  180. return InitResult::success;
  181. }
  182. void shutdownOnRenderThread()
  183. {
  184. XWindowSystemUtilities::ScopedXLock xLock;
  185. context = nullptr;
  186. deactivateCurrentContext();
  187. glXDestroyContext (display, renderContext);
  188. renderContext = nullptr;
  189. }
  190. bool makeActive() const noexcept
  191. {
  192. XWindowSystemUtilities::ScopedXLock xLock;
  193. return renderContext != nullptr
  194. && glXMakeCurrent (display, embeddedWindow, renderContext);
  195. }
  196. bool isActive() const noexcept
  197. {
  198. XWindowSystemUtilities::ScopedXLock xLock;
  199. return glXGetCurrentContext() == renderContext && renderContext != nullptr;
  200. }
  201. static void deactivateCurrentContext()
  202. {
  203. if (auto* display = XWindowSystem::getInstance()->getDisplay())
  204. {
  205. XWindowSystemUtilities::ScopedXLock xLock;
  206. glXMakeCurrent (display, None, nullptr);
  207. }
  208. }
  209. void swapBuffers()
  210. {
  211. XWindowSystemUtilities::ScopedXLock xLock;
  212. glXSwapBuffers (display, embeddedWindow);
  213. }
  214. void updateWindowPosition (Rectangle<int> newBounds)
  215. {
  216. bounds = newBounds;
  217. auto physicalBounds = Desktop::getInstance().getDisplays().logicalToPhysical (bounds);
  218. XWindowSystemUtilities::ScopedXLock xLock;
  219. X11Symbols::getInstance()->xMoveResizeWindow (display, embeddedWindow,
  220. physicalBounds.getX(), physicalBounds.getY(),
  221. (unsigned int) jmax (1, physicalBounds.getWidth()),
  222. (unsigned int) jmax (1, physicalBounds.getHeight()));
  223. }
  224. bool setSwapInterval (int numFramesPerSwap)
  225. {
  226. if (numFramesPerSwap == swapFrames)
  227. return true;
  228. if (auto GLXSwapIntervalSGI
  229. = (PFNGLXSWAPINTERVALSGIPROC) OpenGLHelpers::getExtensionFunction ("glXSwapIntervalSGI"))
  230. {
  231. XWindowSystemUtilities::ScopedXLock xLock;
  232. swapFrames = numFramesPerSwap;
  233. GLXSwapIntervalSGI (numFramesPerSwap);
  234. return true;
  235. }
  236. return false;
  237. }
  238. int getSwapInterval() const { return swapFrames; }
  239. bool createdOk() const noexcept { return true; }
  240. void* getRawContext() const noexcept { return renderContext; }
  241. GLuint getFrameBufferID() const noexcept { return 0; }
  242. void triggerRepaint()
  243. {
  244. if (context != nullptr)
  245. context->triggerRepaint();
  246. }
  247. struct Locker
  248. {
  249. explicit Locker (NativeContext& ctx) : lock (ctx.mutex) {}
  250. const ScopedLock lock;
  251. };
  252. private:
  253. bool tryChooseVisual (const OpenGLPixelFormat& format, const std::vector<GLint>& optionalAttribs)
  254. {
  255. std::vector<GLint> allAttribs
  256. {
  257. GLX_RENDER_TYPE, GLX_RGBA_BIT,
  258. GLX_DOUBLEBUFFER, True,
  259. GLX_RED_SIZE, format.redBits,
  260. GLX_GREEN_SIZE, format.greenBits,
  261. GLX_BLUE_SIZE, format.blueBits,
  262. GLX_ALPHA_SIZE, format.alphaBits,
  263. GLX_DEPTH_SIZE, format.depthBufferBits,
  264. GLX_STENCIL_SIZE, format.stencilBufferBits,
  265. GLX_ACCUM_RED_SIZE, format.accumulationBufferRedBits,
  266. GLX_ACCUM_GREEN_SIZE, format.accumulationBufferGreenBits,
  267. GLX_ACCUM_BLUE_SIZE, format.accumulationBufferBlueBits,
  268. GLX_ACCUM_ALPHA_SIZE, format.accumulationBufferAlphaBits
  269. };
  270. allAttribs.insert (allAttribs.end(), optionalAttribs.begin(), optionalAttribs.end());
  271. allAttribs.push_back (None);
  272. int nElements = 0;
  273. bestConfig = makeXFreePtr (glXChooseFBConfig (display, X11Symbols::getInstance()->xDefaultScreen (display), allAttribs.data(), &nElements));
  274. return nElements != 0 && bestConfig != nullptr;
  275. }
  276. static constexpr int embeddedWindowEventMask = ExposureMask | StructureNotifyMask;
  277. CriticalSection mutex;
  278. Component& component;
  279. GLXContext renderContext = {};
  280. Window embeddedWindow = {};
  281. std::optional<PeerListener> peerListener;
  282. int swapFrames = 1;
  283. Rectangle<int> bounds;
  284. std::unique_ptr<GLXFBConfig, XFreeDeleter> bestConfig;
  285. void* contextToShareWith;
  286. OpenGLContext* context = nullptr;
  287. DummyComponent dummy;
  288. ::Display* display = nullptr;
  289. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
  290. };
  291. //==============================================================================
  292. bool OpenGLHelpers::isContextActive()
  293. {
  294. XWindowSystemUtilities::ScopedXLock xLock;
  295. return glXGetCurrentContext() != nullptr;
  296. }
  297. } // namespace juce