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.

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