1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2020 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11 Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12
13 End User License Agreement: www.juce.com/juce-6-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24 */
25
26 namespace juce
27 {
28
29 //==============================================================================
30 // This byte-code is generated from native/java/com/rmsl/juce/JuceOpenGLView.java with min sdk version 16
31 // See juce_core/native/java/README.txt on how to generate this byte-code.
32 static const uint8 javaJuceOpenGLView[] =
33 {31,139,8,8,95,114,161,94,0,3,74,117,99,101,79,112,101,110,71,76,86,105,101,119,83,117,114,102,97,99,101,46,100,101,120,0,109,
34 84,75,107,19,81,20,62,119,230,38,169,105,76,99,218,138,86,144,32,5,55,173,83,53,98,33,173,86,90,170,196,193,34,173,81,106,93,
35 12,147,169,153,210,204,196,100,154,22,65,40,110,234,170,43,23,34,46,93,22,87,62,138,27,65,168,235,174,213,133,11,23,254,129,
36 162,136,130,223,125,196,164,208,129,239,126,231,125,206,188,78,217,91,75,142,156,191,64,151,223,190,187,182,183,251,251,212,240,
37 247,216,223,197,205,174,170,255,233,209,230,135,123,183,222,252,225,68,53,34,90,43,229,179,164,175,5,216,142,145,178,199,129,
38 93,205,63,0,6,140,224,72,129,71,153,210,159,225,152,50,137,182,193,239,13,162,143,192,14,240,25,216,3,50,240,13,1,54,48,3,204,
39 2,119,128,5,192,1,124,224,1,240,16,120,2,108,2,207,129,87,192,14,240,5,248,9,196,184,234,7,145,32,82,76,207,149,208,136,233,
40 249,187,180,252,20,189,15,105,249,5,228,164,150,95,66,238,214,242,86,135,253,181,161,234,102,100,15,83,214,50,225,233,209,61,179,
41 154,251,100,127,46,253,226,76,73,86,113,92,199,113,76,218,171,245,62,173,247,75,54,232,168,182,183,238,69,92,134,230,188,42,139,
42 251,226,210,214,205,213,124,181,28,209,57,153,57,15,105,126,144,40,141,92,38,99,251,185,154,191,229,77,203,124,67,246,56,129,
43 227,8,56,204,49,154,163,217,9,68,161,236,89,52,28,197,51,19,122,109,162,155,248,205,180,124,6,76,206,51,216,202,201,136,26,7,
44 230,140,116,17,103,157,57,67,58,231,224,232,36,162,67,124,6,92,206,198,246,221,179,33,117,166,245,182,28,31,243,3,63,186,68,172,
45 72,189,197,21,215,155,169,121,193,85,187,228,123,171,103,150,156,166,67,199,109,39,40,215,67,191,108,185,97,16,121,65,100,77,
46 10,94,139,10,29,174,251,117,167,86,241,221,134,53,233,4,77,167,81,160,129,255,174,38,42,89,179,43,245,69,199,245,68,213,2,157,
47 180,221,176,106,213,171,141,101,107,9,13,173,253,93,11,196,74,100,148,138,100,150,138,54,4,27,130,93,164,184,235,4,174,183,44,
48 25,29,40,225,170,41,40,85,246,27,53,39,114,43,83,117,103,149,120,37,108,68,148,12,156,200,111,122,115,21,191,65,217,48,184,18,
49 69,142,91,241,202,115,225,109,63,40,135,171,212,47,109,194,164,12,55,100,56,245,133,193,148,167,66,167,235,97,85,7,15,28,100,
50 213,25,41,248,208,86,107,60,18,13,79,27,61,217,68,250,226,204,48,13,83,34,125,157,166,89,58,145,30,223,152,167,60,41,30,7,111,
51 220,29,195,11,224,248,248,248,250,58,223,54,249,99,131,12,128,1,49,246,213,100,252,23,176,197,13,254,141,31,214,239,145,117,
52 112,107,111,24,29,187,195,236,216,31,173,239,94,236,144,24,181,247,72,156,218,187,132,229,148,79,236,19,150,105,255,203,70,78,
53 213,23,59,198,212,49,226,255,160,156,202,205,235,159,87,200,98,135,253,3,40,26,5,36,252,4,0,0,0,0};
54
55 //==============================================================================
56 struct AndroidGLCallbacks
57 {
58 static void attachedToWindow (JNIEnv*, jobject, jlong);
59 static void detachedFromWindow (JNIEnv*, jobject, jlong);
60 static void dispatchDraw (JNIEnv*, jobject, jlong, jobject);
61 };
62
63 //==============================================================================
64 #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
65 METHOD (constructor, "<init>", "(Landroid/content/Context;J)V") \
66 METHOD (getParent, "getParent", "()Landroid/view/ViewParent;") \
67 METHOD (getHolder, "getHolder", "()Landroid/view/SurfaceHolder;") \
68 METHOD (layout, "layout", "(IIII)V" ) \
69 CALLBACK (AndroidGLCallbacks::attachedToWindow, "onAttchedWindowNative", "(J)V") \
70 CALLBACK (AndroidGLCallbacks::detachedFromWindow, "onDetachedFromWindowNative", "(J)V") \
71 CALLBACK (AndroidGLCallbacks::dispatchDraw, "onDrawNative", "(JLandroid/graphics/Canvas;)V")
72
73 DECLARE_JNI_CLASS_WITH_BYTECODE (JuceOpenGLViewSurface, "com/rmsl/juce/JuceOpenGLView", 16, javaJuceOpenGLView, sizeof(javaJuceOpenGLView))
74 #undef JNI_CLASS_MEMBERS
75
76 //==============================================================================
77 class OpenGLContext::NativeContext : private SurfaceHolderCallback
78 {
79 public:
NativeContext(Component & comp,const OpenGLPixelFormat &,void *,bool,OpenGLVersion)80 NativeContext (Component& comp,
81 const OpenGLPixelFormat& /*pixelFormat*/,
82 void* /*contextToShareWith*/,
83 bool /*useMultisampling*/,
84 OpenGLVersion)
85 : component (comp),
86 surface (EGL_NO_SURFACE), context (EGL_NO_CONTEXT)
87 {
88 auto env = getEnv();
89
90 // Do we have a native peer that we can attach to?
91 if (component.getPeer()->getNativeHandle() == nullptr)
92 return;
93
94 // Initialise the EGL display
95 if (! initEGLDisplay())
96 return;
97
98 // create a native surface view
99 surfaceView = GlobalRef (LocalRef<jobject>(env->NewObject (JuceOpenGLViewSurface,
100 JuceOpenGLViewSurface.constructor,
101 getAppContext().get(),
102 reinterpret_cast<jlong> (this))));
103 if (surfaceView.get() == nullptr)
104 return;
105
106 // add the view to the view hierarchy
107 // after this the nativecontext can receive callbacks
108 env->CallVoidMethod ((jobject) component.getPeer()->getNativeHandle(),
109 AndroidViewGroup.addView, surfaceView.get());
110
111 // initialise the geometry of the view
112 auto bounds = component.getTopLevelComponent()->getLocalArea (&component, component.getLocalBounds());
113 bounds *= component.getDesktopScaleFactor();
114
115 updateWindowPosition (bounds);
116 hasInitialised = true;
117 }
118
~NativeContext()119 ~NativeContext() override
120 {
121 auto env = getEnv();
122
123 if (jobject viewParent = env->CallObjectMethod (surfaceView.get(), JuceOpenGLViewSurface.getParent))
124 env->CallVoidMethod (viewParent, AndroidViewGroup.removeView, surfaceView.get());
125 }
126
127 //==============================================================================
initialiseOnRenderThread(OpenGLContext & aContext)128 bool initialiseOnRenderThread (OpenGLContext& aContext)
129 {
130 jassert (hasInitialised);
131
132 // has the context already attached?
133 jassert (surface == EGL_NO_SURFACE && context == EGL_NO_CONTEXT);
134
135 auto env = getEnv();
136
137 ANativeWindow* window = nullptr;
138
139 LocalRef<jobject> holder (env->CallObjectMethod (surfaceView.get(), JuceOpenGLViewSurface.getHolder));
140
141 if (holder != nullptr)
142 {
143 LocalRef<jobject> jSurface (env->CallObjectMethod (holder.get(), AndroidSurfaceHolder.getSurface));
144
145 if (jSurface != nullptr)
146 {
147 window = ANativeWindow_fromSurface(env, jSurface.get());
148
149 // if we didn't succeed the first time, wait 25ms and try again
150 if (window == nullptr)
151 {
152 Thread::sleep (200);
153 window = ANativeWindow_fromSurface (env, jSurface.get());
154 }
155 }
156 }
157
158 if (window == nullptr)
159 {
160 // failed to get a pointer to the native window after second try so bail out
161 jassertfalse;
162 return false;
163 }
164
165 // create the surface
166 surface = eglCreateWindowSurface (display, config, window, nullptr);
167 jassert (surface != EGL_NO_SURFACE);
168
169 ANativeWindow_release (window);
170
171 // create the OpenGL context
172 EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
173 context = eglCreateContext (display, config, EGL_NO_CONTEXT, contextAttribs);
174 jassert (context != EGL_NO_CONTEXT);
175
176 juceContext = &aContext;
177 return true;
178 }
179
shutdownOnRenderThread()180 void shutdownOnRenderThread()
181 {
182 jassert (hasInitialised);
183
184 // is there a context available to detach?
185 jassert (surface != EGL_NO_SURFACE && context != EGL_NO_CONTEXT);
186
187 eglDestroyContext (display, context);
188 context = EGL_NO_CONTEXT;
189
190 eglDestroySurface (display, surface);
191 surface = EGL_NO_SURFACE;
192 }
193
194 //==============================================================================
makeActive()195 bool makeActive() const noexcept
196 {
197 if (! hasInitialised)
198 return false;
199
200 if (surface == EGL_NO_SURFACE || context == EGL_NO_CONTEXT)
201 return false;
202
203 if (! eglMakeCurrent (display, surface, surface, context))
204 return false;
205
206 return true;
207 }
208
isActive()209 bool isActive() const noexcept { return eglGetCurrentContext() == context; }
210
deactivateCurrentContext()211 static void deactivateCurrentContext()
212 {
213 eglMakeCurrent (display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
214 }
215
216 //==============================================================================
swapBuffers()217 void swapBuffers() const noexcept { eglSwapBuffers (display, surface); }
setSwapInterval(const int)218 bool setSwapInterval (const int) { return false; }
getSwapInterval()219 int getSwapInterval() const { return 0; }
220
221 //==============================================================================
createdOk()222 bool createdOk() const noexcept { return hasInitialised; }
getRawContext()223 void* getRawContext() const noexcept { return surfaceView.get(); }
getFrameBufferID()224 GLuint getFrameBufferID() const noexcept { return 0; }
225
226 //==============================================================================
updateWindowPosition(Rectangle<int> bounds)227 void updateWindowPosition (Rectangle<int> bounds)
228 {
229 if (lastBounds != bounds)
230 {
231 auto env = getEnv();
232
233 lastBounds = bounds;
234 auto r = bounds * Desktop::getInstance().getDisplays().getPrimaryDisplay()->scale;
235
236 env->CallVoidMethod (surfaceView.get(), JuceOpenGLViewSurface.layout,
237 (jint) r.getX(), (jint) r.getY(), (jint) r.getRight(), (jint) r.getBottom());
238 }
239 }
240
241 //==============================================================================
242 // Android Surface Callbacks:
surfaceChanged(LocalRef<jobject> holder,int format,int width,int height)243 void surfaceChanged (LocalRef<jobject> holder, int format, int width, int height) override
244 {
245 ignoreUnused (holder, format, width, height);
246 }
247
248 void surfaceCreated (LocalRef<jobject> holder) override;
249 void surfaceDestroyed (LocalRef<jobject> holder) override;
250
251 //==============================================================================
LockerLocker252 struct Locker { Locker (NativeContext&) {} };
253
254 Component& component;
255
256 private:
257 //==============================================================================
258 friend struct AndroidGLCallbacks;
259
attachedToWindow()260 void attachedToWindow()
261 {
262 auto* env = getEnv();
263
264 LocalRef<jobject> holder (env->CallObjectMethod (surfaceView.get(), JuceOpenGLViewSurface.getHolder));
265
266 if (surfaceHolderCallback == nullptr)
267 surfaceHolderCallback = GlobalRef (CreateJavaInterface (this, "android/view/SurfaceHolder$Callback"));
268
269 env->CallVoidMethod (holder, AndroidSurfaceHolder.addCallback, surfaceHolderCallback.get());
270 }
271
detachedFromWindow()272 void detachedFromWindow()
273 {
274 if (surfaceHolderCallback != nullptr)
275 {
276 auto* env = getEnv();
277
278 LocalRef<jobject> holder (env->CallObjectMethod (surfaceView.get(), JuceOpenGLViewSurface.getHolder));
279
280 env->CallVoidMethod (holder.get(), AndroidSurfaceHolder.removeCallback, surfaceHolderCallback.get());
281 surfaceHolderCallback.clear();
282 }
283 }
284
dispatchDraw(jobject)285 void dispatchDraw (jobject /*canvas*/)
286 {
287 if (juceContext != nullptr)
288 juceContext->triggerRepaint();
289 }
290
291 //==============================================================================
initEGLDisplay()292 bool initEGLDisplay()
293 {
294 // already initialised?
295 if (display != EGL_NO_DISPLAY)
296 return true;
297
298 const EGLint attribs[] =
299 {
300 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
301 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
302 EGL_BLUE_SIZE, 8,
303 EGL_GREEN_SIZE, 8,
304 EGL_RED_SIZE, 8,
305 EGL_ALPHA_SIZE, 0,
306 EGL_DEPTH_SIZE, 16,
307 EGL_NONE
308 };
309
310 EGLint numConfigs;
311
312 if ((display = eglGetDisplay (EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY)
313 {
314 jassertfalse;
315 return false;
316 }
317
318 if (! eglInitialize (display, nullptr, nullptr))
319 {
320 jassertfalse;
321 return false;
322 }
323
324 if (! eglChooseConfig (display, attribs, &config, 1, &numConfigs))
325 {
326 eglTerminate (display);
327 jassertfalse;
328 return false;
329 }
330
331 return true;
332 }
333
334 //==============================================================================
335 bool hasInitialised = false;
336
337 GlobalRef surfaceView;
338 Rectangle<int> lastBounds;
339
340 OpenGLContext* juceContext = nullptr;
341 EGLSurface surface;
342 EGLContext context;
343
344 GlobalRef surfaceHolderCallback;
345
346 static EGLDisplay display;
347 static EGLConfig config;
348
349 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
350 };
351
352 //==============================================================================
attachedToWindow(JNIEnv *,jobject,jlong host)353 void AndroidGLCallbacks::attachedToWindow (JNIEnv*, jobject /*this*/, jlong host)
354 {
355 if (auto* nativeContext = reinterpret_cast<OpenGLContext::NativeContext*> (host))
356 nativeContext->attachedToWindow();
357 }
358
detachedFromWindow(JNIEnv *,jobject,jlong host)359 void AndroidGLCallbacks::detachedFromWindow (JNIEnv*, jobject /*this*/, jlong host)
360 {
361 if (auto* nativeContext = reinterpret_cast<OpenGLContext::NativeContext*> (host))
362 nativeContext->detachedFromWindow();
363 }
364
dispatchDraw(JNIEnv *,jobject,jlong host,jobject canvas)365 void AndroidGLCallbacks::dispatchDraw (JNIEnv*, jobject /*this*/, jlong host, jobject canvas)
366 {
367 if (auto* nativeContext = reinterpret_cast<OpenGLContext::NativeContext*> (host))
368 nativeContext->dispatchDraw (canvas);
369 }
370
371 //==============================================================================
isContextActive()372 bool OpenGLHelpers::isContextActive()
373 {
374 return eglGetCurrentContext() != EGL_NO_CONTEXT;
375 }
376
377 } // namespace juce
378