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.

320 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  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 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  22. METHOD (getParent, "getParent", "()Landroid/view/ViewParent;") \
  23. METHOD (layout, "layout", "(IIII)V" ) \
  24. METHOD (getNativeSurface, "getNativeSurface", "()Landroid/view/Surface;") \
  25. DECLARE_JNI_CLASS (NativeSurfaceView, JUCE_ANDROID_ACTIVITY_CLASSPATH "$NativeSurfaceView")
  26. #undef JNI_CLASS_MEMBERS
  27. //==============================================================================
  28. class OpenGLContext::NativeContext
  29. {
  30. public:
  31. NativeContext (Component& comp,
  32. const OpenGLPixelFormat& /*pixelFormat*/,
  33. void* /*contextToShareWith*/,
  34. bool /*useMultisampling*/,
  35. OpenGLVersion)
  36. : component (comp),
  37. surface (EGL_NO_SURFACE), context (EGL_NO_CONTEXT)
  38. {
  39. auto env = getEnv();
  40. // Do we have a native peer that we can attach to?
  41. if (component.getPeer()->getNativeHandle() == nullptr)
  42. return;
  43. // Initialise the EGL display
  44. if (! initEGLDisplay())
  45. return;
  46. // create a native surface view
  47. surfaceView = GlobalRef (env->CallObjectMethod (android.activity.get(),
  48. JuceAppActivity.createNativeSurfaceView,
  49. reinterpret_cast<jlong> (this),
  50. false));
  51. if (surfaceView.get() == nullptr)
  52. return;
  53. // add the view to the view hierarchy
  54. // after this the nativecontext can receive callbacks
  55. env->CallVoidMethod ((jobject) component.getPeer()->getNativeHandle(),
  56. AndroidViewGroup.addView, surfaceView.get());
  57. // initialise the geometry of the view
  58. auto bounds = component.getTopLevelComponent()->getLocalArea (&component, component.getLocalBounds());
  59. bounds *= component.getDesktopScaleFactor();
  60. updateWindowPosition (bounds);
  61. hasInitialised = true;
  62. }
  63. ~NativeContext()
  64. {
  65. auto env = getEnv();
  66. if (jobject viewParent = env->CallObjectMethod (surfaceView.get(), NativeSurfaceView.getParent))
  67. env->CallVoidMethod (viewParent, AndroidViewGroup.removeView, surfaceView.get());
  68. }
  69. //==============================================================================
  70. bool initialiseOnRenderThread (OpenGLContext& aContext)
  71. {
  72. jassert (hasInitialised);
  73. // has the context already attached?
  74. jassert (surface == EGL_NO_SURFACE && context == EGL_NO_CONTEXT);
  75. auto env = getEnv();
  76. ANativeWindow* window = nullptr;
  77. if (jobject jSurface = env->CallObjectMethod (surfaceView.get(), NativeSurfaceView.getNativeSurface))
  78. {
  79. window = ANativeWindow_fromSurface (env, jSurface);
  80. // if we didn't succeed the first time, wait briefly and try again..
  81. if (window == nullptr)
  82. {
  83. Thread::sleep (200);
  84. window = ANativeWindow_fromSurface (env, jSurface);
  85. }
  86. }
  87. if (window == nullptr)
  88. {
  89. // failed to get a pointer to the native window after second try so bail out
  90. jassertfalse;
  91. return false;
  92. }
  93. // create the surface
  94. surface = eglCreateWindowSurface (display, config, window, 0);
  95. jassert (surface != EGL_NO_SURFACE);
  96. ANativeWindow_release (window);
  97. // create the OpenGL context
  98. EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
  99. context = eglCreateContext (display, config, EGL_NO_CONTEXT, contextAttribs);
  100. jassert (context != EGL_NO_CONTEXT);
  101. juceContext = &aContext;
  102. return true;
  103. }
  104. void shutdownOnRenderThread()
  105. {
  106. jassert (hasInitialised);
  107. // is there a context available to detach?
  108. jassert (surface != EGL_NO_SURFACE && context != EGL_NO_CONTEXT);
  109. eglDestroyContext (display, context);
  110. context = EGL_NO_CONTEXT;
  111. eglDestroySurface (display, surface);
  112. surface = EGL_NO_SURFACE;
  113. }
  114. //==============================================================================
  115. bool makeActive() const noexcept
  116. {
  117. if (! hasInitialised)
  118. return false;
  119. if (surface == EGL_NO_SURFACE || context == EGL_NO_CONTEXT)
  120. return false;
  121. if (! eglMakeCurrent (display, surface, surface, context))
  122. return false;
  123. return true;
  124. }
  125. bool isActive() const noexcept { return eglGetCurrentContext() == context; }
  126. static void deactivateCurrentContext()
  127. {
  128. eglMakeCurrent (display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
  129. }
  130. //==============================================================================
  131. void swapBuffers() const noexcept { eglSwapBuffers (display, surface); }
  132. bool setSwapInterval (const int) { return false; }
  133. int getSwapInterval() const { return 0; }
  134. //==============================================================================
  135. bool createdOk() const noexcept { return hasInitialised; }
  136. void* getRawContext() const noexcept { return surfaceView.get(); }
  137. GLuint getFrameBufferID() const noexcept { return 0; }
  138. //==============================================================================
  139. void updateWindowPosition (Rectangle<int> bounds)
  140. {
  141. if (lastBounds != bounds)
  142. {
  143. auto env = getEnv();
  144. lastBounds = bounds;
  145. auto r = bounds * Desktop::getInstance().getDisplays().getMainDisplay().scale;
  146. env->CallVoidMethod (surfaceView.get(), NativeSurfaceView.layout,
  147. (jint) r.getX(), (jint) r.getY(), (jint) r.getRight(), (jint) r.getBottom());
  148. }
  149. }
  150. //==============================================================================
  151. // Android Surface Callbacks:
  152. void dispatchDraw (jobject canvas)
  153. {
  154. ignoreUnused (canvas);
  155. if (juceContext != nullptr)
  156. juceContext->triggerRepaint();
  157. }
  158. void surfaceChanged (jobject holder, int format, int width, int height)
  159. {
  160. ignoreUnused (holder, format, width, height);
  161. }
  162. void surfaceCreated (jobject holder);
  163. void surfaceDestroyed (jobject holder);
  164. //==============================================================================
  165. struct Locker { Locker (NativeContext&) {} };
  166. Component& component;
  167. private:
  168. //==============================================================================
  169. bool initEGLDisplay()
  170. {
  171. // already initialised?
  172. if (display != EGL_NO_DISPLAY)
  173. return true;
  174. const EGLint attribs[] =
  175. {
  176. EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
  177. EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
  178. EGL_BLUE_SIZE, 8,
  179. EGL_GREEN_SIZE, 8,
  180. EGL_RED_SIZE, 8,
  181. EGL_ALPHA_SIZE, 0,
  182. EGL_DEPTH_SIZE, 16,
  183. EGL_NONE
  184. };
  185. EGLint numConfigs;
  186. if ((display = eglGetDisplay (EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY)
  187. {
  188. jassertfalse;
  189. return false;
  190. }
  191. if (! eglInitialize (display, 0, 0))
  192. {
  193. jassertfalse;
  194. return false;
  195. }
  196. if (! eglChooseConfig (display, attribs, &config, 1, &numConfigs))
  197. {
  198. eglTerminate (display);
  199. jassertfalse;
  200. return false;
  201. }
  202. return true;
  203. }
  204. //==============================================================================
  205. bool hasInitialised = false;
  206. GlobalRef surfaceView;
  207. Rectangle<int> lastBounds;
  208. OpenGLContext* juceContext = nullptr;
  209. EGLSurface surface;
  210. EGLContext context;
  211. static EGLDisplay display;
  212. static EGLConfig config;
  213. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
  214. };
  215. //==============================================================================
  216. JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeSurfaceView), dispatchDrawNative,
  217. void, (JNIEnv* env, jobject nativeView, jlong host, jobject canvas))
  218. {
  219. ignoreUnused (nativeView);
  220. setEnv (env);
  221. reinterpret_cast<OpenGLContext::NativeContext*> (host)->dispatchDraw (canvas);
  222. }
  223. JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeSurfaceView), surfaceChangedNative,
  224. void, (JNIEnv* env, jobject nativeView, jlong host, jobject holder, jint format, jint width, jint height))
  225. {
  226. ignoreUnused (nativeView);
  227. setEnv (env);
  228. reinterpret_cast<OpenGLContext::NativeContext*> (host)->surfaceChanged (holder, format, width, height);
  229. }
  230. JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeSurfaceView), surfaceCreatedNative,
  231. void, (JNIEnv* env, jobject nativeView, jlong host, jobject holder))
  232. {
  233. ignoreUnused (nativeView);
  234. setEnv (env);
  235. reinterpret_cast<OpenGLContext::NativeContext*> (host)->surfaceCreated (holder);
  236. }
  237. JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeSurfaceView), surfaceDestroyedNative,
  238. void, (JNIEnv* env, jobject nativeView, jlong host, jobject holder))
  239. {
  240. ignoreUnused (nativeView);
  241. setEnv (env);
  242. reinterpret_cast<OpenGLContext::NativeContext*> (host)->surfaceDestroyed (holder);
  243. }
  244. //==============================================================================
  245. bool OpenGLHelpers::isContextActive()
  246. {
  247. return eglGetCurrentContext() != EGL_NO_CONTEXT;
  248. }
  249. } // namespace juce