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.

458 lines
16KB

  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 in juce_Windowing_linux.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. template <typename Traits>
  75. class ScopedGLXObject
  76. {
  77. public:
  78. using Type = typename Traits::Type;
  79. ScopedGLXObject() = default;
  80. explicit ScopedGLXObject (Type obj, ::Display* d)
  81. : object (obj), display (d) {}
  82. ScopedGLXObject (ScopedGLXObject&& other) noexcept
  83. : object (std::exchange (other.object, Type{})),
  84. display (std::exchange (other.display, nullptr)) {}
  85. ScopedGLXObject& operator= (ScopedGLXObject&& other) noexcept
  86. {
  87. ScopedGLXObject { std::move (other) }.swap (*this);
  88. return *this;
  89. }
  90. ~ScopedGLXObject() noexcept
  91. {
  92. if (object != Type{})
  93. Traits::destroy (display, object);
  94. }
  95. Type get() const { return object; }
  96. void reset() noexcept
  97. {
  98. *this = ScopedGLXObject();
  99. }
  100. void swap (ScopedGLXObject& other) noexcept
  101. {
  102. std::swap (other.object, object);
  103. std::swap (other.display, display);
  104. }
  105. bool operator== (const ScopedGLXObject& other) const
  106. {
  107. const auto tie = [] (const auto& x) { return std::tie (x.object, x.display); };
  108. return tie (*this) == tie (other);
  109. }
  110. bool operator!= (const ScopedGLXObject& other) const
  111. {
  112. return ! operator== (other);
  113. }
  114. private:
  115. Type object{};
  116. ::Display* display{};
  117. };
  118. struct TraitsGLXContext
  119. {
  120. using Type = GLXContext;
  121. static void destroy (::Display* display, Type t)
  122. {
  123. glXDestroyContext (display, t);
  124. }
  125. };
  126. struct TraitsGLXWindow
  127. {
  128. using Type = GLXWindow;
  129. static void destroy (::Display* display, Type t)
  130. {
  131. glXDestroyWindow (display, t);
  132. }
  133. };
  134. using PtrGLXContext = ScopedGLXObject<TraitsGLXContext>;
  135. using PtrGLXWindow = ScopedGLXObject<TraitsGLXWindow>;
  136. public:
  137. NativeContext (Component& comp,
  138. const OpenGLPixelFormat& cPixelFormat,
  139. void* shareContext,
  140. bool useMultisamplingIn,
  141. OpenGLVersion)
  142. : component (comp), contextToShareWith (shareContext), dummy (*this)
  143. {
  144. display = XWindowSystem::getInstance()->getDisplay();
  145. XWindowSystemUtilities::ScopedXLock xLock;
  146. X11Symbols::getInstance()->xSync (display, False);
  147. const std::vector<GLint> optionalAttribs
  148. {
  149. GLX_SAMPLE_BUFFERS, useMultisamplingIn ? 1 : 0,
  150. GLX_SAMPLES, cPixelFormat.multisamplingLevel
  151. };
  152. if (! tryChooseVisual (cPixelFormat, optionalAttribs) && ! tryChooseVisual (cPixelFormat, {}))
  153. return;
  154. auto* peer = component.getPeer();
  155. jassert (peer != nullptr);
  156. auto windowH = (Window) peer->getNativeHandle();
  157. auto visual = glXGetVisualFromFBConfig (display, *bestConfig);
  158. auto colourMap = X11Symbols::getInstance()->xCreateColormap (display, windowH, visual->visual, AllocNone);
  159. XSetWindowAttributes swa;
  160. swa.colormap = colourMap;
  161. swa.border_pixel = 0;
  162. swa.event_mask = embeddedWindowEventMask;
  163. auto glBounds = component.getTopLevelComponent()->getLocalArea (&component, component.getLocalBounds());
  164. glBounds = Desktop::getInstance().getDisplays().logicalToPhysical (glBounds);
  165. embeddedWindow = X11Symbols::getInstance()->xCreateWindow (display, windowH,
  166. glBounds.getX(), glBounds.getY(),
  167. (unsigned int) jmax (1, glBounds.getWidth()),
  168. (unsigned int) jmax (1, glBounds.getHeight()),
  169. 0, visual->depth,
  170. InputOutput,
  171. visual->visual,
  172. CWBorderPixel | CWColormap | CWEventMask,
  173. &swa);
  174. peerListener.emplace (component, embeddedWindow);
  175. X11Symbols::getInstance()->xMapWindow (display, embeddedWindow);
  176. X11Symbols::getInstance()->xFreeColormap (display, colourMap);
  177. X11Symbols::getInstance()->xSync (display, False);
  178. juce_LinuxAddRepaintListener (peer, &dummy);
  179. }
  180. ~NativeContext()
  181. {
  182. if (auto* peer = component.getPeer())
  183. {
  184. juce_LinuxRemoveRepaintListener (peer, &dummy);
  185. if (embeddedWindow != 0)
  186. {
  187. XWindowSystemUtilities::ScopedXLock xLock;
  188. X11Symbols::getInstance()->xUnmapWindow (display, embeddedWindow);
  189. X11Symbols::getInstance()->xDestroyWindow (display, embeddedWindow);
  190. X11Symbols::getInstance()->xSync (display, False);
  191. XEvent event;
  192. while (X11Symbols::getInstance()->xCheckWindowEvent (display,
  193. embeddedWindow,
  194. embeddedWindowEventMask,
  195. &event) == True)
  196. {
  197. }
  198. }
  199. }
  200. }
  201. InitResult initialiseOnRenderThread (OpenGLContext& c)
  202. {
  203. XWindowSystemUtilities::ScopedXLock xLock;
  204. const auto components = [&]() -> Optional<Version>
  205. {
  206. switch (c.versionRequired)
  207. {
  208. case OpenGLVersion::openGL3_2: return Version { 3, 2 };
  209. case OpenGLVersion::openGL4_1: return Version { 4, 1 };
  210. case OpenGLVersion::openGL4_3: return Version { 4, 3 };
  211. case OpenGLVersion::defaultGLVersion: break;
  212. }
  213. return {};
  214. }();
  215. if (components.hasValue())
  216. {
  217. using GLXCreateContextAttribsARB = GLXContext (*) (Display*, GLXFBConfig, GLXContext, Bool, const int*);
  218. if (const auto glXCreateContextAttribsARB = (GLXCreateContextAttribsARB) OpenGLHelpers::getExtensionFunction ("glXCreateContextAttribsARB"))
  219. {
  220. #if JUCE_DEBUG
  221. constexpr auto contextFlags = GLX_CONTEXT_DEBUG_BIT_ARB;
  222. #else
  223. constexpr auto contextFlags = 0;
  224. #endif
  225. const int attribs[]
  226. {
  227. GLX_CONTEXT_MAJOR_VERSION_ARB, components->major,
  228. GLX_CONTEXT_MINOR_VERSION_ARB, components->minor,
  229. GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
  230. GLX_CONTEXT_FLAGS_ARB, contextFlags,
  231. None
  232. };
  233. renderContext = PtrGLXContext { glXCreateContextAttribsARB (display, *bestConfig, (GLXContext) contextToShareWith, GL_TRUE, attribs),
  234. display };
  235. }
  236. }
  237. if (renderContext == PtrGLXContext{})
  238. renderContext = PtrGLXContext { glXCreateNewContext (display, *bestConfig, GLX_RGBA_TYPE, (GLXContext) contextToShareWith, GL_TRUE),
  239. display };
  240. if (renderContext == PtrGLXContext{})
  241. return InitResult::fatal;
  242. glxWindow = PtrGLXWindow { glXCreateWindow (display, *bestConfig, embeddedWindow, nullptr),
  243. display };
  244. c.makeActive();
  245. context = &c;
  246. return InitResult::success;
  247. }
  248. void shutdownOnRenderThread()
  249. {
  250. XWindowSystemUtilities::ScopedXLock xLock;
  251. context = nullptr;
  252. deactivateCurrentContext();
  253. renderContext.reset();
  254. glxWindow.reset();
  255. }
  256. bool makeActive() const noexcept
  257. {
  258. XWindowSystemUtilities::ScopedXLock xLock;
  259. return renderContext != PtrGLXContext{}
  260. && glXMakeContextCurrent (display, glxWindow.get(), glxWindow.get(), renderContext.get());
  261. }
  262. bool isActive() const noexcept
  263. {
  264. XWindowSystemUtilities::ScopedXLock xLock;
  265. return glXGetCurrentContext() == renderContext.get() && renderContext != PtrGLXContext{};
  266. }
  267. static void deactivateCurrentContext()
  268. {
  269. if (auto* display = XWindowSystem::getInstance()->getDisplay())
  270. {
  271. XWindowSystemUtilities::ScopedXLock xLock;
  272. glXMakeCurrent (display, None, nullptr);
  273. }
  274. }
  275. void swapBuffers()
  276. {
  277. glXSwapBuffers (display, glxWindow.get());
  278. }
  279. void updateWindowPosition (Rectangle<int> newBounds)
  280. {
  281. bounds = newBounds;
  282. auto physicalBounds = Desktop::getInstance().getDisplays().logicalToPhysical (bounds);
  283. XWindowSystemUtilities::ScopedXLock xLock;
  284. X11Symbols::getInstance()->xMoveResizeWindow (display, embeddedWindow,
  285. physicalBounds.getX(), physicalBounds.getY(),
  286. (unsigned int) jmax (1, physicalBounds.getWidth()),
  287. (unsigned int) jmax (1, physicalBounds.getHeight()));
  288. }
  289. bool setSwapInterval (int numFramesPerSwap)
  290. {
  291. if (numFramesPerSwap == swapFrames)
  292. return true;
  293. if (auto GLXSwapIntervalEXT
  294. = (PFNGLXSWAPINTERVALEXTPROC) OpenGLHelpers::getExtensionFunction ("glXSwapIntervalEXT"))
  295. {
  296. XWindowSystemUtilities::ScopedXLock xLock;
  297. swapFrames = numFramesPerSwap;
  298. GLXSwapIntervalEXT (display, glxWindow.get(), numFramesPerSwap);
  299. return true;
  300. }
  301. return false;
  302. }
  303. int getSwapInterval() const { return swapFrames; }
  304. bool createdOk() const noexcept { return true; }
  305. void* getRawContext() const noexcept { return renderContext.get(); }
  306. GLuint getFrameBufferID() const noexcept { return 0; }
  307. void triggerRepaint()
  308. {
  309. if (context != nullptr)
  310. context->triggerRepaint();
  311. }
  312. struct Locker
  313. {
  314. explicit Locker (NativeContext& ctx) : lock (ctx.mutex) {}
  315. const ScopedLock lock;
  316. };
  317. private:
  318. bool tryChooseVisual (const OpenGLPixelFormat& format, const std::vector<GLint>& optionalAttribs)
  319. {
  320. std::vector<GLint> allAttribs
  321. {
  322. GLX_RENDER_TYPE, GLX_RGBA_BIT,
  323. GLX_DOUBLEBUFFER, True,
  324. GLX_RED_SIZE, format.redBits,
  325. GLX_GREEN_SIZE, format.greenBits,
  326. GLX_BLUE_SIZE, format.blueBits,
  327. GLX_ALPHA_SIZE, format.alphaBits,
  328. GLX_DEPTH_SIZE, format.depthBufferBits,
  329. GLX_STENCIL_SIZE, format.stencilBufferBits,
  330. GLX_ACCUM_RED_SIZE, format.accumulationBufferRedBits,
  331. GLX_ACCUM_GREEN_SIZE, format.accumulationBufferGreenBits,
  332. GLX_ACCUM_BLUE_SIZE, format.accumulationBufferBlueBits,
  333. GLX_ACCUM_ALPHA_SIZE, format.accumulationBufferAlphaBits
  334. };
  335. allAttribs.insert (allAttribs.end(), optionalAttribs.begin(), optionalAttribs.end());
  336. allAttribs.push_back (None);
  337. int nElements = 0;
  338. bestConfig = makeXFreePtr (glXChooseFBConfig (display, X11Symbols::getInstance()->xDefaultScreen (display), allAttribs.data(), &nElements));
  339. return nElements != 0 && bestConfig != nullptr;
  340. }
  341. static constexpr int embeddedWindowEventMask = ExposureMask | StructureNotifyMask;
  342. CriticalSection mutex;
  343. Component& component;
  344. PtrGLXContext renderContext;
  345. PtrGLXWindow glxWindow;
  346. Window embeddedWindow = {};
  347. std::optional<PeerListener> peerListener;
  348. int swapFrames = 0;
  349. Rectangle<int> bounds;
  350. std::unique_ptr<GLXFBConfig, XFreeDeleter> bestConfig;
  351. void* contextToShareWith;
  352. OpenGLContext* context = nullptr;
  353. DummyComponent dummy;
  354. ::Display* display = nullptr;
  355. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
  356. };
  357. //==============================================================================
  358. bool OpenGLHelpers::isContextActive()
  359. {
  360. XWindowSystemUtilities::ScopedXLock xLock;
  361. return glXGetCurrentContext() != nullptr;
  362. }
  363. } // namespace juce