mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Android Bluetooth MIDI, pro-audio i/o and improved openGL support
This commit is contained in:
parent
02041328dc
commit
89ba69ab29
45 changed files with 4088 additions and 669 deletions
|
|
@ -22,14 +22,21 @@
|
|||
==============================================================================
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (layout, "layout", "(IIII)V") \
|
||||
METHOD (requestRender, "requestRender", "()V") \
|
||||
METHOD (getParent, "getParent", "()Landroid/view/ViewParent;") \
|
||||
METHOD (layout, "layout", "(IIII)V" ) \
|
||||
METHOD (getNativeSurface, "getNativeSurface", "()Landroid/view/Surface;") \
|
||||
|
||||
DECLARE_JNI_CLASS (OpenGLView, JUCE_ANDROID_ACTIVITY_CLASSPATH "$OpenGLView");
|
||||
DECLARE_JNI_CLASS (NativeSurfaceView, JUCE_ANDROID_ACTIVITY_CLASSPATH "$NativeSurfaceView")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
extern jobject createOpenGLView (ComponentPeer*);
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (addView, "addView", "(Landroid/view/View;)V") \
|
||||
METHOD (removeView, "removeView", "(Landroid/view/View;)V") \
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidViewGroup, "android/view/ViewGroup")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
class OpenGLContext::NativeContext
|
||||
|
|
@ -41,150 +48,259 @@ public:
|
|||
bool /*useMultisampling*/,
|
||||
OpenGLVersion)
|
||||
: component (comp),
|
||||
isInsideGLCallback (false)
|
||||
hasInitialised (false),
|
||||
juceContext (nullptr), surface (EGL_NO_SURFACE), context (EGL_NO_CONTEXT)
|
||||
{
|
||||
{
|
||||
const ScopedLock sl (contextListLock);
|
||||
glView = GlobalRef (createOpenGLView (component.getPeer()));
|
||||
contextList.add (this);
|
||||
}
|
||||
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 hierachy
|
||||
// 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 (component.getTopLevelComponent()
|
||||
->getLocalArea (&component, component.getLocalBounds()));
|
||||
updateWindowPosition (bounds);
|
||||
hasInitialised = true;
|
||||
}
|
||||
|
||||
~NativeContext()
|
||||
{
|
||||
{
|
||||
const ScopedLock sl (contextListLock);
|
||||
contextList.removeFirstMatchingValue (this);
|
||||
}
|
||||
JNIEnv* env = getEnv();
|
||||
|
||||
android.activity.callVoidMethod (JuceAppActivity.deleteOpenGLView, glView.get());
|
||||
glView.clear();
|
||||
if (jobject viewParent = env->CallObjectMethod (surfaceView.get(), NativeSurfaceView.getParent))
|
||||
env->CallVoidMethod (viewParent, AndroidViewGroup.removeView, surfaceView.get());
|
||||
}
|
||||
|
||||
void initialiseOnRenderThread (OpenGLContext&) {}
|
||||
void shutdownOnRenderThread() {}
|
||||
//==============================================================================
|
||||
void initialiseOnRenderThread (OpenGLContext& aContext)
|
||||
{
|
||||
jassert (hasInitialised);
|
||||
|
||||
bool makeActive() const noexcept { return isInsideGLCallback; }
|
||||
bool isActive() const noexcept { return isInsideGLCallback; }
|
||||
static void deactivateCurrentContext() {}
|
||||
// has the context already attached?
|
||||
jassert (surface == EGL_NO_SURFACE && context == EGL_NO_CONTEXT);
|
||||
|
||||
void swapBuffers() const noexcept {}
|
||||
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 getRawContext() != nullptr; }
|
||||
void* getRawContext() const noexcept { return glView.get(); }
|
||||
//==============================================================================
|
||||
bool createdOk() const noexcept { return hasInitialised; }
|
||||
void* getRawContext() const noexcept { return surfaceView.get(); }
|
||||
GLuint getFrameBufferID() const noexcept { return 0; }
|
||||
|
||||
//==============================================================================
|
||||
void updateWindowPosition (const Rectangle<int>& bounds)
|
||||
{
|
||||
if (lastBounds != bounds)
|
||||
{
|
||||
lastBounds = bounds;
|
||||
JNIEnv* env = getEnv();
|
||||
|
||||
lastBounds = bounds;
|
||||
Rectangle<int> r = bounds * Desktop::getInstance().getDisplays().getMainDisplay().scale;
|
||||
|
||||
glView.callVoidMethod (OpenGLView.layout,
|
||||
r.getX(), r.getY(),
|
||||
r.getRight(), r.getBottom());
|
||||
env->CallVoidMethod (surfaceView.get(), NativeSurfaceView.layout,
|
||||
(jint) r.getX(), (jint) r.getY(), (jint) r.getRight(), (jint) r.getBottom());
|
||||
}
|
||||
}
|
||||
|
||||
void triggerRepaint()
|
||||
{
|
||||
glView.callVoidMethod (OpenGLView.requestRender);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void contextCreatedCallback();
|
||||
void contextChangedSize() {}
|
||||
void renderCallback();
|
||||
// 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);
|
||||
|
||||
//==============================================================================
|
||||
static NativeContext* findContextFor (JNIEnv* env, jobject glView)
|
||||
{
|
||||
const ScopedLock sl (contextListLock);
|
||||
|
||||
for (int i = contextList.size(); --i >= 0;)
|
||||
{
|
||||
NativeContext* const c = contextList.getUnchecked(i);
|
||||
|
||||
if (env->IsSameObject (c->glView.get(), glView))
|
||||
return c;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static NativeContext* getActiveContext() noexcept
|
||||
{
|
||||
const ScopedLock sl (contextListLock);
|
||||
|
||||
for (int i = contextList.size(); --i >= 0;)
|
||||
{
|
||||
NativeContext* const c = contextList.getUnchecked(i);
|
||||
|
||||
if (c->isInsideGLCallback)
|
||||
return c;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct Locker { Locker (NativeContext&) {} };
|
||||
|
||||
Component& component;
|
||||
|
||||
private:
|
||||
GlobalRef glView;
|
||||
Rectangle<int> lastBounds;
|
||||
bool isInsideGLCallback;
|
||||
//==============================================================================
|
||||
bool initEGLDisplay()
|
||||
{
|
||||
// already initialised?
|
||||
if (display != EGL_NO_DISPLAY)
|
||||
return true;
|
||||
|
||||
typedef Array<NativeContext*> ContextArray;
|
||||
static CriticalSection contextListLock;
|
||||
static ContextArray contextList;
|
||||
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, hasBeenAddedToViewHierachy;
|
||||
|
||||
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)
|
||||
};
|
||||
|
||||
CriticalSection OpenGLContext::NativeContext::contextListLock;
|
||||
OpenGLContext::NativeContext::ContextArray OpenGLContext::NativeContext::contextList;
|
||||
//==============================================================================
|
||||
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 OpenGLContext::NativeContext::getActiveContext() != nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#define GL_VIEW_CLASS_NAME JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024OpenGLView)
|
||||
|
||||
JUCE_JNI_CALLBACK (GL_VIEW_CLASS_NAME, contextCreated, void, (JNIEnv* env, jobject view))
|
||||
{
|
||||
threadLocalJNIEnvHolder.removeCurrentThreadFromCache();
|
||||
threadLocalJNIEnvHolder.getOrAttach();
|
||||
|
||||
if (OpenGLContext::NativeContext* const context = OpenGLContext::NativeContext::findContextFor (env, view))
|
||||
context->contextCreatedCallback();
|
||||
else
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
JUCE_JNI_CALLBACK (GL_VIEW_CLASS_NAME, contextChangedSize, void, (JNIEnv* env, jobject view))
|
||||
{
|
||||
if (OpenGLContext::NativeContext* const context = OpenGLContext::NativeContext::findContextFor (env, view))
|
||||
context->contextChangedSize();
|
||||
}
|
||||
|
||||
JUCE_JNI_CALLBACK (GL_VIEW_CLASS_NAME, render, void, (JNIEnv* env, jobject view))
|
||||
{
|
||||
if (OpenGLContext::NativeContext* const context = OpenGLContext::NativeContext::findContextFor (env, view))
|
||||
context->renderCallback();
|
||||
return eglGetCurrentContext() != EGL_NO_CONTEXT;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue