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.

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