|  | /*
  ==============================================================================
   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.
   JUCE is an open source library subject to commercial or open-source
   licensing.
   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).
   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy
   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.
  ==============================================================================
*/
namespace juce
{
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
 METHOD (getParent, "getParent", "()Landroid/view/ViewParent;") \
 METHOD (layout, "layout", "(IIII)V" ) \
 METHOD (getNativeSurface,     "getNativeSurface",        "()Landroid/view/Surface;") \
DECLARE_JNI_CLASS (NativeSurfaceView, JUCE_ANDROID_ACTIVITY_CLASSPATH "$NativeSurfaceView")
#undef JNI_CLASS_MEMBERS
//==============================================================================
class OpenGLContext::NativeContext
{
public:
    NativeContext (Component& comp,
                   const OpenGLPixelFormat& /*pixelFormat*/,
                   void* /*contextToShareWith*/,
                   bool /*useMultisampling*/,
                   OpenGLVersion)
        : component (comp),
          hasInitialised (false),
          juceContext (nullptr), surface (EGL_NO_SURFACE), context (EGL_NO_CONTEXT)
    {
        JNIEnv* env = getEnv();
        // Do we have a native peer that we can attach to?
        if (component.getPeer()->getNativeHandle() == nullptr)
            return;
        // Initialise the EGL display
        if (! initEGLDisplay())
            return;
        // create a native surface view
        surfaceView = GlobalRef (env->CallObjectMethod (android.activity.get(),
                                                        JuceAppActivity.createNativeSurfaceView,
                                                        reinterpret_cast<jlong> (this)));
        if (surfaceView.get() == nullptr)
            return;
        // add the view to the view hierarchy
        // after this the nativecontext can receive callbacks
        env->CallVoidMethod ((jobject) component.getPeer()->getNativeHandle(),
                             AndroidViewGroup.addView, surfaceView.get());
        // initialise the geometry of the view
        Rectangle<int> bounds = component.getTopLevelComponent()
            ->getLocalArea (&component, component.getLocalBounds());
        bounds *= component.getDesktopScaleFactor();
        updateWindowPosition (bounds);
        hasInitialised = true;
    }
    ~NativeContext()
    {
        JNIEnv* env = getEnv();
        if (jobject viewParent = env->CallObjectMethod (surfaceView.get(), NativeSurfaceView.getParent))
            env->CallVoidMethod (viewParent, AndroidViewGroup.removeView, surfaceView.get());
    }
    //==============================================================================
    void initialiseOnRenderThread (OpenGLContext& aContext)
    {
        jassert (hasInitialised);
        // has the context already attached?
        jassert (surface == EGL_NO_SURFACE && context == EGL_NO_CONTEXT);
        JNIEnv* env = getEnv();
        // get a pointer to the native window
        ANativeWindow* window = nullptr;
        if (jobject jSurface = env->CallObjectMethod (surfaceView.get(), NativeSurfaceView.getNativeSurface))
            window = ANativeWindow_fromSurface (env, jSurface);
        jassert (window != nullptr);
        // create the surface
        surface = eglCreateWindowSurface(display, config, window, 0);
        jassert (surface != EGL_NO_SURFACE);
        ANativeWindow_release (window);
        // create the OpenGL context
        EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
        context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
        jassert (context != EGL_NO_CONTEXT);
        juceContext = &aContext;
    }
    void shutdownOnRenderThread()
    {
        jassert (hasInitialised);
        // is there a context available to detach?
        jassert (surface != EGL_NO_SURFACE && context != EGL_NO_CONTEXT);
        eglDestroyContext (display, context);
        context = EGL_NO_CONTEXT;
        eglDestroySurface (display, surface);
        surface = EGL_NO_SURFACE;
    }
    //==============================================================================
    bool makeActive() const noexcept
    {
        if (! hasInitialised)
            return false;
        if (surface == EGL_NO_SURFACE || context == EGL_NO_CONTEXT)
            return false;
        if (! eglMakeCurrent (display, surface, surface, context))
            return false;
        return true;
    }
    bool isActive() const noexcept              { return eglGetCurrentContext() == context; }
    static void deactivateCurrentContext()
    {
        eglMakeCurrent (display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    }
    //==============================================================================
    void swapBuffers() const noexcept           { eglSwapBuffers (display, surface); }
    bool setSwapInterval (const int)            { return false; }
    int getSwapInterval() const                 { return 0; }
    //==============================================================================
    bool createdOk() const noexcept             { return hasInitialised; }
    void* getRawContext() const noexcept        { return surfaceView.get(); }
    GLuint getFrameBufferID() const noexcept    { return 0; }
    //==============================================================================
    void updateWindowPosition (Rectangle<int> bounds)
    {
        if (lastBounds != bounds)
        {
            JNIEnv* env = getEnv();
            lastBounds = bounds;
            Rectangle<int> r = bounds * Desktop::getInstance().getDisplays().getMainDisplay().scale;
            env->CallVoidMethod (surfaceView.get(), NativeSurfaceView.layout,
                                 (jint) r.getX(), (jint) r.getY(), (jint) r.getRight(), (jint) r.getBottom());
        }
    }
    //==============================================================================
    // Android Surface Callbacks:
    void dispatchDraw (jobject canvas)
    {
        ignoreUnused (canvas);
        if (juceContext != nullptr)
            juceContext->triggerRepaint();
    }
    void surfaceChanged (jobject holder, int format, int width, int height)
    {
        ignoreUnused (holder, format, width, height);
    }
    void surfaceCreated (jobject holder);
    void surfaceDestroyed (jobject holder);
    //==============================================================================
    struct Locker { Locker (NativeContext&) {} };
    Component& component;
private:
    //==============================================================================
    bool initEGLDisplay()
    {
        // already initialised?
        if (display != EGL_NO_DISPLAY)
            return true;
        const EGLint attribs[] =
        {
            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
            EGL_BLUE_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_RED_SIZE, 8,
            EGL_ALPHA_SIZE, 0,
            EGL_DEPTH_SIZE, 16,
            EGL_NONE
        };
        EGLint numConfigs;
        if ((display = eglGetDisplay (EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY)
        {
            jassertfalse;
            return false;
        }
        if (! eglInitialize (display, 0, 0))
        {
            jassertfalse;
            return false;
        }
        if (! eglChooseConfig (display, attribs, &config, 1, &numConfigs))
        {
            eglTerminate (display);
            jassertfalse;
            return false;
        }
        return true;
    }
    //==============================================================================
    bool hasInitialised;
    GlobalRef surfaceView;
    Rectangle<int> lastBounds;
    OpenGLContext* juceContext;
    EGLSurface surface;
    EGLContext context;
    static EGLDisplay display;
    static EGLConfig config;
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
};
//==============================================================================
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeSurfaceView), dispatchDrawNative,
                   void, (JNIEnv* env, jobject nativeView, jlong host, jobject canvas))
{
    ignoreUnused (nativeView);
    setEnv (env);
    reinterpret_cast<OpenGLContext::NativeContext*> (host)->dispatchDraw (canvas);
}
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeSurfaceView), surfaceChangedNative,
                   void, (JNIEnv* env, jobject nativeView, jlong host, jobject holder, jint format, jint width, jint height))
{
    ignoreUnused (nativeView);
    setEnv (env);
    reinterpret_cast<OpenGLContext::NativeContext*> (host)->surfaceChanged (holder, format, width, height);
}
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeSurfaceView), surfaceCreatedNative,
                   void, (JNIEnv* env, jobject nativeView, jlong host, jobject holder))
{
    ignoreUnused (nativeView);
    setEnv (env);
    reinterpret_cast<OpenGLContext::NativeContext*> (host)->surfaceCreated (holder);
}
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeSurfaceView), surfaceDestroyedNative,
                   void, (JNIEnv* env, jobject nativeView, jlong host, jobject holder))
{
    ignoreUnused (nativeView);
    setEnv (env);
    reinterpret_cast<OpenGLContext::NativeContext*> (host)->surfaceDestroyed (holder);
}
//==============================================================================
bool OpenGLHelpers::isContextActive()
{
    return eglGetCurrentContext() != EGL_NO_CONTEXT;
}
} // namespace juce
 |