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.

307 lines
11KB

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