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.

325 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. hasInitialised (false),
  38. juceContext (nullptr), surface (EGL_NO_SURFACE), context (EGL_NO_CONTEXT)
  39. {
  40. JNIEnv* env = getEnv();
  41. // Do we have a native peer that we can attach to?
  42. if (component.getPeer()->getNativeHandle() == nullptr)
  43. return;
  44. // Initialise the EGL display
  45. if (! initEGLDisplay())
  46. return;
  47. // create a native surface view
  48. surfaceView = GlobalRef (env->CallObjectMethod (android.activity.get(),
  49. JuceAppActivity.createNativeSurfaceView,
  50. reinterpret_cast<jlong> (this),
  51. false));
  52. if (surfaceView.get() == nullptr)
  53. return;
  54. // add the view to the view hierarchy
  55. // after this the nativecontext can receive callbacks
  56. env->CallVoidMethod ((jobject) component.getPeer()->getNativeHandle(),
  57. AndroidViewGroup.addView, surfaceView.get());
  58. // initialise the geometry of the view
  59. Rectangle<int> bounds = component.getTopLevelComponent()
  60. ->getLocalArea (&component, component.getLocalBounds());
  61. bounds *= component.getDesktopScaleFactor();
  62. updateWindowPosition (bounds);
  63. hasInitialised = true;
  64. }
  65. ~NativeContext()
  66. {
  67. JNIEnv* env = getEnv();
  68. if (jobject viewParent = env->CallObjectMethod (surfaceView.get(), NativeSurfaceView.getParent))
  69. env->CallVoidMethod (viewParent, AndroidViewGroup.removeView, surfaceView.get());
  70. }
  71. //==============================================================================
  72. bool initialiseOnRenderThread (OpenGLContext& aContext)
  73. {
  74. jassert (hasInitialised);
  75. // has the context already attached?
  76. jassert (surface == EGL_NO_SURFACE && context == EGL_NO_CONTEXT);
  77. JNIEnv* env = getEnv();
  78. // get a pointer to the native window
  79. ANativeWindow* window = nullptr;
  80. if (jobject jSurface = env->CallObjectMethod (surfaceView.get(), NativeSurfaceView.getNativeSurface))
  81. {
  82. window = ANativeWindow_fromSurface(env, jSurface);
  83. // if we didn't succeed the first time, wait 25ms and try again
  84. if (window == nullptr)
  85. {
  86. Thread::sleep (25);
  87. window = ANativeWindow_fromSurface (env, jSurface);
  88. }
  89. }
  90. if (window == nullptr)
  91. {
  92. // failed to get a pointer to the native window after second try so
  93. // bail out
  94. jassertfalse;
  95. return false;
  96. }
  97. // create the surface
  98. surface = eglCreateWindowSurface(display, config, window, 0);
  99. jassert (surface != EGL_NO_SURFACE);
  100. ANativeWindow_release (window);
  101. // create the OpenGL context
  102. EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
  103. context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
  104. jassert (context != EGL_NO_CONTEXT);
  105. juceContext = &aContext;
  106. return true;
  107. }
  108. void shutdownOnRenderThread()
  109. {
  110. jassert (hasInitialised);
  111. // is there a context available to detach?
  112. jassert (surface != EGL_NO_SURFACE && context != EGL_NO_CONTEXT);
  113. eglDestroyContext (display, context);
  114. context = EGL_NO_CONTEXT;
  115. eglDestroySurface (display, surface);
  116. surface = EGL_NO_SURFACE;
  117. }
  118. //==============================================================================
  119. bool makeActive() const noexcept
  120. {
  121. if (! hasInitialised)
  122. return false;
  123. if (surface == EGL_NO_SURFACE || context == EGL_NO_CONTEXT)
  124. return false;
  125. if (! eglMakeCurrent (display, surface, surface, context))
  126. return false;
  127. return true;
  128. }
  129. bool isActive() const noexcept { return eglGetCurrentContext() == context; }
  130. static void deactivateCurrentContext()
  131. {
  132. eglMakeCurrent (display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
  133. }
  134. //==============================================================================
  135. void swapBuffers() const noexcept { eglSwapBuffers (display, surface); }
  136. bool setSwapInterval (const int) { return false; }
  137. int getSwapInterval() const { return 0; }
  138. //==============================================================================
  139. bool createdOk() const noexcept { return hasInitialised; }
  140. void* getRawContext() const noexcept { return surfaceView.get(); }
  141. GLuint getFrameBufferID() const noexcept { return 0; }
  142. //==============================================================================
  143. void updateWindowPosition (Rectangle<int> bounds)
  144. {
  145. if (lastBounds != bounds)
  146. {
  147. JNIEnv* env = getEnv();
  148. lastBounds = bounds;
  149. Rectangle<int> r = bounds * Desktop::getInstance().getDisplays().getMainDisplay().scale;
  150. env->CallVoidMethod (surfaceView.get(), NativeSurfaceView.layout,
  151. (jint) r.getX(), (jint) r.getY(), (jint) r.getRight(), (jint) r.getBottom());
  152. }
  153. }
  154. //==============================================================================
  155. // Android Surface Callbacks:
  156. void dispatchDraw (jobject canvas)
  157. {
  158. ignoreUnused (canvas);
  159. if (juceContext != nullptr)
  160. juceContext->triggerRepaint();
  161. }
  162. void surfaceChanged (jobject holder, int format, int width, int height)
  163. {
  164. ignoreUnused (holder, format, width, height);
  165. }
  166. void surfaceCreated (jobject holder);
  167. void surfaceDestroyed (jobject holder);
  168. //==============================================================================
  169. struct Locker { Locker (NativeContext&) {} };
  170. Component& component;
  171. private:
  172. //==============================================================================
  173. bool initEGLDisplay()
  174. {
  175. // already initialised?
  176. if (display != EGL_NO_DISPLAY)
  177. return true;
  178. const EGLint attribs[] =
  179. {
  180. EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
  181. EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
  182. EGL_BLUE_SIZE, 8,
  183. EGL_GREEN_SIZE, 8,
  184. EGL_RED_SIZE, 8,
  185. EGL_ALPHA_SIZE, 0,
  186. EGL_DEPTH_SIZE, 16,
  187. EGL_NONE
  188. };
  189. EGLint numConfigs;
  190. if ((display = eglGetDisplay (EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY)
  191. {
  192. jassertfalse;
  193. return false;
  194. }
  195. if (! eglInitialize (display, 0, 0))
  196. {
  197. jassertfalse;
  198. return false;
  199. }
  200. if (! eglChooseConfig (display, attribs, &config, 1, &numConfigs))
  201. {
  202. eglTerminate (display);
  203. jassertfalse;
  204. return false;
  205. }
  206. return true;
  207. }
  208. //==============================================================================
  209. bool hasInitialised;
  210. GlobalRef surfaceView;
  211. Rectangle<int> lastBounds;
  212. OpenGLContext* juceContext;
  213. EGLSurface surface;
  214. EGLContext context;
  215. static EGLDisplay display;
  216. static EGLConfig config;
  217. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
  218. };
  219. //==============================================================================
  220. JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeSurfaceView), dispatchDrawNative,
  221. void, (JNIEnv* env, jobject nativeView, jlong host, jobject canvas))
  222. {
  223. ignoreUnused (nativeView);
  224. setEnv (env);
  225. reinterpret_cast<OpenGLContext::NativeContext*> (host)->dispatchDraw (canvas);
  226. }
  227. JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeSurfaceView), surfaceChangedNative,
  228. void, (JNIEnv* env, jobject nativeView, jlong host, jobject holder, jint format, jint width, jint height))
  229. {
  230. ignoreUnused (nativeView);
  231. setEnv (env);
  232. reinterpret_cast<OpenGLContext::NativeContext*> (host)->surfaceChanged (holder, format, width, height);
  233. }
  234. JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeSurfaceView), surfaceCreatedNative,
  235. void, (JNIEnv* env, jobject nativeView, jlong host, jobject holder))
  236. {
  237. ignoreUnused (nativeView);
  238. setEnv (env);
  239. reinterpret_cast<OpenGLContext::NativeContext*> (host)->surfaceCreated (holder);
  240. }
  241. JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeSurfaceView), surfaceDestroyedNative,
  242. void, (JNIEnv* env, jobject nativeView, jlong host, jobject holder))
  243. {
  244. ignoreUnused (nativeView);
  245. setEnv (env);
  246. reinterpret_cast<OpenGLContext::NativeContext*> (host)->surfaceDestroyed (holder);
  247. }
  248. //==============================================================================
  249. bool OpenGLHelpers::isContextActive()
  250. {
  251. return eglGetCurrentContext() != EGL_NO_CONTEXT;
  252. }
  253. } // namespace juce