First Commit

This commit is contained in:
Legaeli
2025-10-22 09:20:23 +02:00
commit a36f2ca925
4393 changed files with 1570219 additions and 0 deletions
@@ -0,0 +1,99 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
package com.rmsl.juce;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Region;
import android.view.SurfaceView;
public class JuceOpenGLView extends SurfaceView
{
private long host = 0;
JuceOpenGLView (Context context, long nativeThis)
{
super (context);
host = nativeThis;
}
public void cancel ()
{
host = 0;
}
//==============================================================================
@Override
public boolean gatherTransparentRegion (Region unused)
{
// Returning true indicates that the view is opaque at this point.
// Without this, the green TalkBack borders cannot be seen on OpenGL views.
return true;
}
@Override
protected void onAttachedToWindow ()
{
super.onAttachedToWindow ();
if (host != 0)
onAttchedWindowNative (host);
}
@Override
protected void onDetachedFromWindow ()
{
if (host != 0)
onDetachedFromWindowNative (host);
super.onDetachedFromWindow ();
}
@Override
protected void dispatchDraw (Canvas canvas)
{
super.dispatchDraw (canvas);
if (host != 0)
onDrawNative (host, canvas);
}
//==============================================================================
private native void onAttchedWindowNative (long nativeThis);
private native void onDetachedFromWindowNative (long nativeThis);
private native void onDrawNative (long nativeThis, Canvas canvas);
}
@@ -0,0 +1,149 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
/** @internal This macro contains a list of GL extension functions that need to be dynamically loaded on Windows/Linux.
@see OpenGLExtensionFunctions
*/
#define JUCE_GL_BASE_FUNCTIONS \
X (glActiveTexture) \
X (glBindBuffer) \
X (glDeleteBuffers) \
X (glGenBuffers) \
X (glBufferData) \
X (glBufferSubData) \
X (glCreateProgram) \
X (glDeleteProgram) \
X (glCreateShader) \
X (glDeleteShader) \
X (glShaderSource) \
X (glCompileShader) \
X (glAttachShader) \
X (glLinkProgram) \
X (glUseProgram) \
X (glGetShaderiv) \
X (glGetShaderInfoLog) \
X (glGetProgramInfoLog) \
X (glGetProgramiv) \
X (glGetUniformLocation) \
X (glGetAttribLocation) \
X (glVertexAttribPointer) \
X (glEnableVertexAttribArray) \
X (glDisableVertexAttribArray) \
X (glUniform1f) \
X (glUniform1i) \
X (glUniform2f) \
X (glUniform3f) \
X (glUniform4f) \
X (glUniform4i) \
X (glUniform1fv) \
X (glUniformMatrix2fv) \
X (glUniformMatrix3fv) \
X (glUniformMatrix4fv) \
X (glBindAttribLocation)
/** @internal This macro contains a list of GL extension functions that need to be dynamically loaded on Windows/Linux.
@see OpenGLExtensionFunctions
*/
#define JUCE_GL_EXTENSION_FUNCTIONS \
X (glIsRenderbuffer) \
X (glBindRenderbuffer) \
X (glDeleteRenderbuffers) \
X (glGenRenderbuffers) \
X (glRenderbufferStorage) \
X (glGetRenderbufferParameteriv) \
X (glIsFramebuffer) \
X (glBindFramebuffer) \
X (glDeleteFramebuffers) \
X (glGenFramebuffers) \
X (glCheckFramebufferStatus) \
X (glFramebufferTexture2D) \
X (glFramebufferRenderbuffer) \
X (glGetFramebufferAttachmentParameteriv)
/** @internal This macro contains a list of GL extension functions that need to be dynamically loaded on Windows/Linux.
@see OpenGLExtensionFunctions
*/
#define JUCE_GL_VERTEXBUFFER_FUNCTIONS \
X (glGenVertexArrays) \
X (glDeleteVertexArrays) \
X (glBindVertexArray)
/** This class contains a generated list of OpenGL extension functions, which are either dynamically loaded
for a specific GL context, or simply call-through to the appropriate OS function where available.
This class is provided for backwards compatibility. In new code, you should prefer to use
functions from the juce::gl namespace. By importing all these symbols with
`using namespace ::juce::gl;`, all GL enumerations and functions will be made available at
global scope. This may be helpful if you need to write code with C source compatibility, or
which is compatible with a different extension-loading library.
All the normal guidance about `using namespace` should still apply - don't do this in a header,
or at all if you can possibly avoid it!
@tags{OpenGL}
*/
struct OpenGLExtensionFunctions
{
//==============================================================================
#ifndef DOXYGEN
[[deprecated ("A more complete set of GL commands can be found in the juce::gl namespace. "
"You should use juce::gl::loadFunctions() to load GL functions.")]]
static void initialise();
#endif
#if JUCE_WINDOWS && ! defined (DOXYGEN)
typedef char GLchar;
typedef pointer_sized_int GLsizeiptr;
typedef pointer_sized_int GLintptr;
#endif
#define X(name) static decltype (::juce::gl::name)& name;
JUCE_GL_BASE_FUNCTIONS
JUCE_GL_EXTENSION_FUNCTIONS
JUCE_GL_VERTEXBUFFER_FUNCTIONS
#undef X
};
enum MissingOpenGLDefinitions
{
#if JUCE_ANDROID
JUCE_RGBA_FORMAT = ::juce::gl::GL_RGBA,
#else
JUCE_RGBA_FORMAT = ::juce::gl::GL_BGRA_EXT,
#endif
};
} // namespace juce
@@ -0,0 +1,418 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
// This byte-code is generated from native/java/com/rmsl/juce/JuceOpenGLView.java with min sdk version 16
// See juce_core/native/java/README.txt on how to generate this byte-code.
static const uint8 javaJuceOpenGLView[] =
{
0x1f, 0x8b, 0x08, 0x08, 0xac, 0xdb, 0x8b, 0x62, 0x00, 0x03, 0x4a, 0x61,
0x76, 0x61, 0x44, 0x65, 0x78, 0x42, 0x79, 0x74, 0x65, 0x43, 0x6f, 0x64,
0x65, 0x2e, 0x64, 0x65, 0x78, 0x00, 0x6d, 0x54, 0x4f, 0x48, 0x14, 0x51,
0x18, 0xff, 0x66, 0xe6, 0xed, 0x6e, 0xea, 0x3a, 0xae, 0xeb, 0x7f, 0xc1,
0xd8, 0x20, 0xea, 0x64, 0x6b, 0x7f, 0xac, 0x40, 0x0b, 0x4d, 0xfb, 0xb7,
0x0d, 0x4a, 0x69, 0x5b, 0x6c, 0x1d, 0x9a, 0x66, 0x27, 0x77, 0x44, 0x67,
0x96, 0xd9, 0xd9, 0x55, 0x28, 0x44, 0xba, 0x78, 0xf1, 0x54, 0x41, 0xd1,
0xd9, 0x43, 0x04, 0x45, 0x20, 0x05, 0x75, 0x48, 0xa2, 0x8b, 0xe1, 0xa1,
0x53, 0xd8, 0x21, 0xa8, 0x43, 0x07, 0x8f, 0x9d, 0xc2, 0x93, 0xf4, 0x7b,
0x6f, 0x9e, 0xad, 0x85, 0xc3, 0xfe, 0xe6, 0xfb, 0xfb, 0xbe, 0xef, 0xf7,
0xde, 0xce, 0xfb, 0xf2, 0xf6, 0x6c, 0x6d, 0xcf, 0xd1, 0x5e, 0x7a, 0xbc,
0x7e, 0xe1, 0x25, 0x7d, 0xb9, 0xba, 0x35, 0x37, 0x59, 0xff, 0xe2, 0xd1,
0xda, 0xeb, 0x7b, 0x2b, 0x67, 0xb6, 0x8c, 0xbb, 0xef, 0x2f, 0x0e, 0x3f,
0x89, 0x10, 0x15, 0x89, 0x68, 0x36, 0x7b, 0x2c, 0x49, 0xf2, 0xd9, 0x64,
0x44, 0x5d, 0x14, 0xfa, 0xf7, 0x00, 0x3f, 0x81, 0x18, 0xc0, 0x14, 0x22,
0xfc, 0xe8, 0x3a, 0x5e, 0xf5, 0x90, 0xb7, 0xa4, 0xbd, 0x8a, 0xd7, 0x2b,
0x8d, 0x68, 0x03, 0x32, 0x0a, 0xa9, 0x03, 0x8d, 0xc0, 0x01, 0x60, 0x10,
0xb8, 0x09, 0xcc, 0x00, 0x0f, 0x81, 0x65, 0xe0, 0x0d, 0xf0, 0x0e, 0x58,
0x01, 0x3e, 0x02, 0xab, 0xc0, 0x1a, 0xf0, 0x19, 0x58, 0x07, 0xbe, 0xf3,
0x5a, 0xc0, 0x6f, 0xa0, 0x01, 0x5c, 0x5a, 0x80, 0x7d, 0x40, 0x2f, 0x60,
0x00, 0xb7, 0x81, 0x39, 0x60, 0x11, 0x78, 0xc0, 0x42, 0x0e, 0x1a, 0xe7,
0x07, 0x60, 0x3b, 0x14, 0x95, 0x7c, 0x39, 0xf7, 0x7a, 0x29, 0xa3, 0x72,
0x6f, 0x35, 0x52, 0xff, 0xaa, 0x12, 0xd5, 0x4a, 0xfd, 0x07, 0xf4, 0x3a,
0xa9, 0x6f, 0x40, 0x8f, 0x4b, 0xfd, 0xd7, 0x0e, 0xff, 0x26, 0x74, 0x5d,
0xd6, 0xe5, 0xcd, 0x78, 0x9f, 0x66, 0xd1, 0x53, 0x13, 0x75, 0x19, 0x3c,
0x49, 0xc9, 0xa1, 0x55, 0xca, 0x76, 0xc1, 0x87, 0x89, 0x38, 0xcf, 0x6f,
0x10, 0x32, 0xcc, 0x8b, 0xa0, 0x6a, 0x93, 0xf4, 0xb7, 0x0a, 0xa9, 0x50,
0x9b, 0xb4, 0xdb, 0xa5, 0xdd, 0x21, 0xa4, 0x4a, 0x9d, 0xd2, 0xaf, 0xc8,
0xba, 0xfc, 0x51, 0xa5, 0xfc, 0x24, 0x1d, 0x51, 0x44, 0xb8, 0xef, 0x29,
0x0b, 0xf7, 0x55, 0x4c, 0x11, 0x1d, 0x11, 0x95, 0x73, 0xd0, 0x72, 0xfb,
0x39, 0x7b, 0x4d, 0x54, 0x20, 0x5a, 0x62, 0xd5, 0xbe, 0x3c, 0xaa, 0x8b,
0xf5, 0xaa, 0xa8, 0xfd, 0x1c, 0xaf, 0x46, 0x48, 0x2f, 0xa5, 0xd0, 0x38,
0x8d, 0x0d, 0x20, 0x0b, 0x65, 0x0f, 0xa3, 0xe1, 0x49, 0xec, 0x9d, 0xdb,
0xc5, 0x81, 0x38, 0xb1, 0xcb, 0xba, 0x38, 0x86, 0x90, 0xc5, 0x32, 0x0b,
0xf9, 0x24, 0x13, 0x0d, 0x82, 0x37, 0x3f, 0x91, 0xb7, 0xdb, 0x75, 0x12,
0xbc, 0xee, 0xae, 0x75, 0x7a, 0x6a, 0xf0, 0x45, 0xe9, 0x72, 0xaf, 0x7c,
0xcd, 0x07, 0xb9, 0x66, 0xf7, 0xec, 0x3a, 0x64, 0x7b, 0x09, 0x0d, 0xd5,
0x74, 0x79, 0x16, 0xd5, 0x73, 0x50, 0x85, 0xad, 0x48, 0xfb, 0x7f, 0x5d,
0xa3, 0x68, 0xbf, 0xe3, 0x3a, 0xc1, 0x69, 0x52, 0x32, 0xd4, 0x94, 0x29,
0x5b, 0xf6, 0x68, 0xd1, 0x76, 0xcf, 0x1b, 0x59, 0xc7, 0x9e, 0x39, 0x34,
0x69, 0x56, 0x4c, 0xea, 0x30, 0x4c, 0x37, 0xef, 0x7b, 0x4e, 0x3e, 0x6d,
0x79, 0x6e, 0x60, 0xbb, 0x41, 0x7a, 0x88, 0xcb, 0xd9, 0xa0, 0x6f, 0x47,
0x68, 0xc2, 0x37, 0x8b, 0x05, 0xc7, 0x2a, 0xa5, 0x87, 0x4c, 0xb7, 0x62,
0x96, 0x76, 0x0d, 0x5d, 0xb1, 0x27, 0x1c, 0xcf, 0xed, 0xa3, 0xce, 0xbf,
0xa1, 0x0a, 0x9a, 0xa4, 0xc7, 0xca, 0xfe, 0x1d, 0xd3, 0xb2, 0x79, 0xc3,
0x3e, 0xda, 0x6b, 0x58, 0xde, 0x74, 0xda, 0x9f, 0x2e, 0x4d, 0xa5, 0x27,
0xc1, 0x25, 0xfd, 0x2f, 0xa1, 0x3e, 0x52, 0xb2, 0xa4, 0x66, 0x33, 0xa4,
0x65, 0x33, 0x06, 0x14, 0x03, 0x8a, 0x91, 0x21, 0x25, 0x47, 0x6a, 0xce,
0xa0, 0xa8, 0x65, 0xba, 0x96, 0x3d, 0x25, 0x24, 0x38, 0x50, 0xcc, 0x0a,
0x79, 0x52, 0x3c, 0xef, 0x94, 0x8a, 0x66, 0x60, 0x15, 0x86, 0x7d, 0x73,
0x86, 0xda, 0x26, 0xcc, 0xa0, 0x60, 0xfb, 0xe3, 0xbe, 0xe9, 0xc2, 0xeb,
0x63, 0x43, 0x21, 0x31, 0x62, 0x05, 0xaf, 0x14, 0x50, 0xad, 0x6b, 0x06,
0x4e, 0xc5, 0x1e, 0x2f, 0x38, 0x25, 0x4a, 0x7a, 0xee, 0x60, 0x10, 0x98,
0x56, 0xc1, 0xce, 0x8f, 0x7b, 0xd7, 0x1c, 0x37, 0xef, 0xcd, 0x50, 0x8b,
0xf0, 0x71, 0x57, 0xe8, 0x18, 0x11, 0xe9, 0xd4, 0xec, 0xb9, 0xc3, 0x76,
0x98, 0x7a, 0xce, 0xf7, 0xa6, 0x65, 0x72, 0xe7, 0x6e, 0x5e, 0xb9, 0x22,
0x8e, 0x18, 0xf8, 0x48, 0x8b, 0x05, 0xbc, 0x61, 0xb4, 0xec, 0x96, 0x4b,
0x76, 0x9e, 0x0e, 0xaa, 0xc9, 0xd6, 0x98, 0x7e, 0x62, 0xb4, 0x9b, 0xba,
0x29, 0xa6, 0x5f, 0xa2, 0x11, 0xa5, 0x31, 0xa6, 0x9f, 0x5a, 0xc8, 0xd1,
0x71, 0xa5, 0x2b, 0xa6, 0x53, 0x3f, 0x85, 0xd6, 0x59, 0xc8, 0x85, 0x1b,
0xfd, 0xf8, 0x27, 0x19, 0xee, 0x02, 0x9b, 0x9f, 0x67, 0x1b, 0x5a, 0xe4,
0xbe, 0x4a, 0x2a, 0xa0, 0x00, 0x11, 0x65, 0x91, 0x29, 0xec, 0x19, 0x53,
0x94, 0x6f, 0x90, 0xbf, 0x98, 0xca, 0x96, 0x22, 0xf2, 0xde, 0xd3, 0x8e,
0xef, 0x84, 0xcb, 0xed, 0x99, 0xa6, 0x52, 0x75, 0xae, 0x69, 0x54, 0x9d,
0x6d, 0x8c, 0xaa, 0xf3, 0x6d, 0xbb, 0x06, 0x9f, 0x71, 0x51, 0xaa, 0xce,
0x39, 0x25, 0x25, 0xe7, 0x04, 0xd7, 0x13, 0xd5, 0x59, 0xa2, 0xa6, 0xc2,
0xfa, 0x7c, 0xfe, 0x69, 0x32, 0x87, 0xdf, 0x45, 0x4a, 0x85, 0x6b, 0xc5,
0x3d, 0x4d, 0x84, 0x3a, 0x9f, 0xaf, 0x7f, 0x00, 0x34, 0xf2, 0xd3, 0x47,
0x98, 0x05, 0x00, 0x00
};
//==============================================================================
//==============================================================================
class OpenGLContext::NativeContext : private SurfaceHolderCallback
{
public:
NativeContext (Component& comp,
const OpenGLPixelFormat& pixelFormat,
void* /*contextToShareWith*/,
bool useMultisamplingIn,
OpenGLVersion)
: component (comp)
{
auto 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 (pixelFormat, useMultisamplingIn))
return;
// create a native surface view
surfaceView = GlobalRef (LocalRef<jobject> (env->NewObject (JuceOpenGLViewSurface,
JuceOpenGLViewSurface.constructor,
getAppContext().get(),
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
auto bounds = component.getTopLevelComponent()->getLocalArea (&component, component.getLocalBounds());
bounds *= component.getDesktopScaleFactor();
updateWindowPosition (bounds);
hasInitialised = true;
}
~NativeContext() override
{
auto env = getEnv();
if (jobject viewParent = env->CallObjectMethod (surfaceView.get(), JuceOpenGLViewSurface.getParent))
env->CallVoidMethod (viewParent, AndroidViewGroup.removeView, surfaceView.get());
}
//==============================================================================
InitResult initialiseOnRenderThread (OpenGLContext& ctx)
{
// The "real" initialisation happens when the surface is created. Here, we'll
// just return true if the initialisation happened successfully, or false if
// it hasn't happened yet, or was unsuccessful.
const std::lock_guard lock { nativeHandleMutex };
if (! hasInitialised)
return InitResult::fatal;
if (context.get() == EGL_NO_CONTEXT && surface.get() == EGL_NO_SURFACE)
return InitResult::retry;
juceContext = &ctx;
return InitResult::success;
}
void shutdownOnRenderThread()
{
const std::lock_guard lock { nativeHandleMutex };
juceContext = nullptr;
}
//==============================================================================
bool makeActive() const noexcept
{
const std::lock_guard lock { nativeHandleMutex };
return hasInitialised
&& surface.get() != EGL_NO_SURFACE
&& context.get() != EGL_NO_CONTEXT
&& eglMakeCurrent (display, surface.get(), surface.get(), context.get());
}
bool isActive() const noexcept
{
const std::lock_guard lock { nativeHandleMutex };
return eglGetCurrentContext() == context.get();
}
static void deactivateCurrentContext()
{
eglMakeCurrent (display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
//==============================================================================
void swapBuffers() const noexcept { eglSwapBuffers (display, surface.get()); }
bool setSwapInterval (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)
{
auto env = getEnv();
lastBounds = bounds;
auto r = bounds * Desktop::getInstance().getDisplays().getPrimaryDisplay()->scale;
env->CallVoidMethod (surfaceView.get(), JuceOpenGLViewSurface.layout,
(jint) r.getX(), (jint) r.getY(), (jint) r.getRight(), (jint) r.getBottom());
}
}
//==============================================================================
// Android Surface Callbacks:
void surfaceChanged ([[maybe_unused]] LocalRef<jobject> holder,
[[maybe_unused]] int format,
[[maybe_unused]] int width,
[[maybe_unused]] int height) override
{
}
void surfaceCreated (LocalRef<jobject>) override;
void surfaceDestroyed (LocalRef<jobject>) override;
//==============================================================================
struct Locker
{
explicit Locker (NativeContext& ctx) : lock (ctx.mutex) {}
const ScopedLock lock;
};
Component& component;
private:
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (constructor, "<init>", "(Landroid/content/Context;J)V") \
METHOD (getParent, "getParent", "()Landroid/view/ViewParent;") \
METHOD (getHolder, "getHolder", "()Landroid/view/SurfaceHolder;") \
METHOD (layout, "layout", "(IIII)V" ) \
CALLBACK (generatedCallback<&NativeContext::attachedToWindow>, "onAttchedWindowNative", "(J)V") \
CALLBACK (generatedCallback<&NativeContext::detachedFromWindow>, "onDetachedFromWindowNative", "(J)V") \
CALLBACK (generatedCallback<&NativeContext::dispatchDraw>, "onDrawNative", "(JLandroid/graphics/Canvas;)V")
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceOpenGLViewSurface, "com/rmsl/juce/JuceOpenGLView", 16, javaJuceOpenGLView)
#undef JNI_CLASS_MEMBERS
//==============================================================================
static void attachedToWindow (JNIEnv* env, NativeContext& t)
{
LocalRef<jobject> holder (env->CallObjectMethod (t.surfaceView.get(), JuceOpenGLViewSurface.getHolder));
if (t.surfaceHolderCallback == nullptr)
t.surfaceHolderCallback = GlobalRef (CreateJavaInterface (&t, "android/view/SurfaceHolder$Callback"));
env->CallVoidMethod (holder, AndroidSurfaceHolder.addCallback, t.surfaceHolderCallback.get());
}
static void detachedFromWindow (JNIEnv* env, NativeContext& t)
{
if (t.surfaceHolderCallback != nullptr)
{
LocalRef<jobject> holder (env->CallObjectMethod (t.surfaceView.get(), JuceOpenGLViewSurface.getHolder));
env->CallVoidMethod (holder.get(), AndroidSurfaceHolder.removeCallback, t.surfaceHolderCallback.get());
t.surfaceHolderCallback.clear();
}
}
static void dispatchDraw (JNIEnv*, NativeContext& t, jobject /*canvas*/)
{
const std::lock_guard lock { t.nativeHandleMutex };
if (t.juceContext != nullptr)
t.juceContext->triggerRepaint();
}
bool tryChooseConfig (const std::vector<EGLint>& optionalAttribs)
{
std::vector<EGLint> allAttribs
{
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
};
allAttribs.insert (allAttribs.end(), optionalAttribs.begin(), optionalAttribs.end());
allAttribs.push_back (EGL_NONE);
EGLint numConfigs{};
return eglChooseConfig (display, allAttribs.data(), &config, 1, &numConfigs);
}
//==============================================================================
bool initEGLDisplay (const OpenGLPixelFormat& pixelFormat, bool multisample)
{
// already initialised?
if (display != EGL_NO_DISPLAY)
return true;
if ((display = eglGetDisplay (EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY)
{
jassertfalse;
return false;
}
if (! eglInitialize (display, nullptr, nullptr))
{
jassertfalse;
return false;
}
if (tryChooseConfig ({ EGL_SAMPLE_BUFFERS, multisample ? 1 : 0, EGL_SAMPLES, pixelFormat.multisamplingLevel }))
return true;
if (tryChooseConfig ({}))
return true;
eglTerminate (display);
jassertfalse;
return false;
}
struct NativeWindowReleaser
{
void operator() (ANativeWindow* ptr) const { if (ptr != nullptr) ANativeWindow_release (ptr); }
};
std::unique_ptr<ANativeWindow, NativeWindowReleaser> getNativeWindow() const
{
auto* env = getEnv();
const LocalRef<jobject> holder (env->CallObjectMethod (surfaceView.get(), JuceOpenGLViewSurface.getHolder));
if (holder == nullptr)
return nullptr;
const LocalRef<jobject> jSurface (env->CallObjectMethod (holder.get(), AndroidSurfaceHolder.getSurface));
if (jSurface == nullptr)
return nullptr;
constexpr auto numAttempts = 2;
for (auto i = 0; i < numAttempts; Thread::sleep (200), ++i)
if (auto* ptr = ANativeWindow_fromSurface (env, jSurface.get()))
return std::unique_ptr<ANativeWindow, NativeWindowReleaser> { ptr };
return nullptr;
}
//==============================================================================
CriticalSection mutex;
bool hasInitialised = false;
GlobalRef surfaceView;
Rectangle<int> lastBounds;
struct SurfaceDestructor
{
void operator() (EGLSurface x) const { if (x != EGL_NO_SURFACE) eglDestroySurface (display, x); }
};
struct ContextDestructor
{
void operator() (EGLContext x) const { if (x != EGL_NO_CONTEXT) eglDestroyContext (display, x); }
};
mutable std::mutex nativeHandleMutex;
OpenGLContext* juceContext = nullptr;
std::unique_ptr<std::remove_pointer_t<EGLSurface>, SurfaceDestructor> surface { EGL_NO_SURFACE };
std::unique_ptr<std::remove_pointer_t<EGLContext>, ContextDestructor> context { EGL_NO_CONTEXT };
GlobalRef surfaceHolderCallback;
static EGLDisplay display;
static EGLConfig config;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
};
EGLDisplay OpenGLContext::NativeContext::display = EGL_NO_DISPLAY;
EGLDisplay OpenGLContext::NativeContext::config;
//==============================================================================
bool OpenGLHelpers::isContextActive()
{
return eglGetCurrentContext() != EGL_NO_CONTEXT;
}
} // namespace juce
@@ -0,0 +1,319 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
@interface JuceGLView : UIView
{
}
+ (Class) layerClass;
@end
@implementation JuceGLView
+ (Class) layerClass
{
return [CAEAGLLayer class];
}
@end
extern "C" GLvoid glResolveMultisampleFramebufferAPPLE();
namespace juce
{
class OpenGLContext::NativeContext
{
public:
NativeContext (Component& c,
const OpenGLPixelFormat& pixFormat,
void* contextToShare,
bool multisampling,
OpenGLVersion version)
: component (c), openGLversion (version),
useDepthBuffer (pixFormat.depthBufferBits > 0),
useMSAA (multisampling)
{
JUCE_AUTORELEASEPOOL
{
if (auto* peer = component.getPeer())
{
auto bounds = peer->getAreaCoveredBy (component);
view = [[JuceGLView alloc] initWithFrame: convertToCGRect (bounds)];
view.opaque = YES;
view.hidden = NO;
view.backgroundColor = [UIColor blackColor];
view.userInteractionEnabled = NO;
glLayer = (CAEAGLLayer*) [view layer];
glLayer.opaque = true;
updateWindowPosition (bounds);
[((UIView*) peer->getNativeHandle()) addSubview: view];
const auto shouldUseES3 = version != defaultGLVersion
&& [[UIDevice currentDevice].systemVersion floatValue] >= 7.0;
[[maybe_unused]] const auto gotContext = (shouldUseES3 && createContext (kEAGLRenderingAPIOpenGLES3, contextToShare))
|| createContext (kEAGLRenderingAPIOpenGLES2, contextToShare);
jassert (gotContext);
if (context != nil)
{
// I'd prefer to put this stuff in the initialiseOnRenderThread() call, but doing
// so causes mysterious timing-related failures.
[EAGLContext setCurrentContext: context.get()];
gl::loadFunctions();
createGLBuffers();
deactivateCurrentContext();
}
else
{
jassertfalse;
}
}
else
{
jassertfalse;
}
}
}
~NativeContext()
{
context.reset();
[view removeFromSuperview];
[view release];
}
InitResult initialiseOnRenderThread (OpenGLContext&) { return InitResult::success; }
void shutdownOnRenderThread()
{
JUCE_CHECK_OPENGL_ERROR
freeGLBuffers();
deactivateCurrentContext();
}
bool createdOk() const noexcept { return getRawContext() != nullptr; }
void* getRawContext() const noexcept { return context.get(); }
GLuint getFrameBufferID() const noexcept { return useMSAA ? msaaBufferHandle : frameBufferHandle; }
bool makeActive() const noexcept
{
if (! [EAGLContext setCurrentContext: context.get()])
return false;
glBindFramebuffer (GL_FRAMEBUFFER, useMSAA ? msaaBufferHandle
: frameBufferHandle);
return true;
}
bool isActive() const noexcept
{
return [EAGLContext currentContext] == context.get();
}
static void deactivateCurrentContext()
{
[EAGLContext setCurrentContext: nil];
}
void swapBuffers()
{
if (useMSAA)
{
glBindFramebuffer (GL_DRAW_FRAMEBUFFER, frameBufferHandle);
glBindFramebuffer (GL_READ_FRAMEBUFFER, msaaBufferHandle);
if (openGLversion >= openGL3_2)
{
auto w = roundToInt (lastBounds.getWidth() * glLayer.contentsScale);
auto h = roundToInt (lastBounds.getHeight() * glLayer.contentsScale);
glBlitFramebuffer (0, 0, w, h,
0, 0, w, h,
GL_COLOR_BUFFER_BIT,
GL_NEAREST);
}
else
{
::glResolveMultisampleFramebufferAPPLE();
}
}
glBindRenderbuffer (GL_RENDERBUFFER, colorBufferHandle);
[context.get() presentRenderbuffer: GL_RENDERBUFFER];
if (needToRebuildBuffers)
{
needToRebuildBuffers = false;
freeGLBuffers();
createGLBuffers();
makeActive();
}
}
void updateWindowPosition (Rectangle<int> bounds)
{
view.frame = convertToCGRect (bounds);
glLayer.contentsScale = (CGFloat) (Desktop::getInstance().getDisplays().getPrimaryDisplay()->scale
/ component.getDesktopScaleFactor());
if (lastBounds != bounds)
{
lastBounds = bounds;
needToRebuildBuffers = true;
}
}
bool setSwapInterval (int numFramesPerSwap) noexcept
{
swapFrames = numFramesPerSwap;
return false;
}
int getSwapInterval() const noexcept { return swapFrames; }
struct Locker
{
explicit Locker (NativeContext& ctx) : lock (ctx.mutex) {}
const ScopedLock lock;
};
private:
CriticalSection mutex;
Component& component;
JuceGLView* view = nil;
CAEAGLLayer* glLayer = nil;
NSUniquePtr<EAGLContext> context;
const OpenGLVersion openGLversion;
const bool useDepthBuffer, useMSAA;
GLuint frameBufferHandle = 0, colorBufferHandle = 0, depthBufferHandle = 0,
msaaColorHandle = 0, msaaBufferHandle = 0;
Rectangle<int> lastBounds;
int swapFrames = 0;
bool needToRebuildBuffers = false;
bool createContext (EAGLRenderingAPI type, void* contextToShare)
{
jassert (context == nil);
context.reset ([EAGLContext alloc]);
if (contextToShare != nullptr)
[context.get() initWithAPI: type sharegroup: [(EAGLContext*) contextToShare sharegroup]];
else
[context.get() initWithAPI: type];
return context != nil;
}
//==============================================================================
void createGLBuffers()
{
glGenFramebuffers (1, &frameBufferHandle);
glGenRenderbuffers (1, &colorBufferHandle);
glBindFramebuffer (GL_FRAMEBUFFER, frameBufferHandle);
glBindRenderbuffer (GL_RENDERBUFFER, colorBufferHandle);
glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBufferHandle);
[[maybe_unused]] bool ok = [context.get() renderbufferStorage: GL_RENDERBUFFER fromDrawable: glLayer];
jassert (ok);
GLint width, height;
glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
if (useMSAA)
{
glGenFramebuffers (1, &msaaBufferHandle);
glGenRenderbuffers (1, &msaaColorHandle);
glBindFramebuffer (GL_FRAMEBUFFER, msaaBufferHandle);
glBindRenderbuffer (GL_RENDERBUFFER, msaaColorHandle);
glRenderbufferStorageMultisample (GL_RENDERBUFFER, 4, GL_RGBA8, width, height);
glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaaColorHandle);
}
if (useDepthBuffer)
{
glGenRenderbuffers (1, &depthBufferHandle);
glBindRenderbuffer (GL_RENDERBUFFER, depthBufferHandle);
if (useMSAA)
glRenderbufferStorageMultisample (GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, width, height);
else
glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBufferHandle);
}
jassert (glCheckFramebufferStatus (GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
JUCE_CHECK_OPENGL_ERROR
}
void freeGLBuffers()
{
JUCE_CHECK_OPENGL_ERROR
[context.get() renderbufferStorage: GL_RENDERBUFFER fromDrawable: nil];
deleteFrameBuffer (frameBufferHandle);
deleteFrameBuffer (msaaBufferHandle);
deleteRenderBuffer (colorBufferHandle);
deleteRenderBuffer (depthBufferHandle);
deleteRenderBuffer (msaaColorHandle);
JUCE_CHECK_OPENGL_ERROR
}
static void deleteFrameBuffer (GLuint& i) { if (i != 0) glDeleteFramebuffers (1, &i); i = 0; }
static void deleteRenderBuffer (GLuint& i) { if (i != 0) glDeleteRenderbuffers (1, &i); i = 0; }
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
};
//==============================================================================
bool OpenGLHelpers::isContextActive()
{
return [EAGLContext currentContext] != nil;
}
} // namespace juce
@@ -0,0 +1,466 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
struct XFreeDeleter
{
void operator() (void* ptr) const
{
if (ptr != nullptr)
X11Symbols::getInstance()->xFree (ptr);
}
};
template <typename Data>
std::unique_ptr<Data, XFreeDeleter> makeXFreePtr (Data* raw) { return std::unique_ptr<Data, XFreeDeleter> (raw); }
//==============================================================================
// Defined in juce_Windowing_linux.cpp
void juce_LinuxAddRepaintListener (ComponentPeer*, Component* dummy);
void juce_LinuxRemoveRepaintListener (ComponentPeer*, Component* dummy);
class PeerListener : private ComponentMovementWatcher
{
public:
PeerListener (Component& comp, Window embeddedWindow)
: ComponentMovementWatcher (&comp),
window (embeddedWindow),
association (comp.getPeer(), window) {}
private:
using ComponentMovementWatcher::componentMovedOrResized,
ComponentMovementWatcher::componentVisibilityChanged;
void componentMovedOrResized (bool, bool) override {}
void componentVisibilityChanged() override {}
void componentPeerChanged() override
{
// This should not be rewritten as a ternary expression or similar.
// The old association must be destroyed before the new one is created.
association = {};
if (auto* comp = getComponent())
association = ScopedWindowAssociation (comp->getPeer(), window);
}
Window window{};
ScopedWindowAssociation association;
};
//==============================================================================
class OpenGLContext::NativeContext
{
private:
struct DummyComponent : public Component
{
DummyComponent (OpenGLContext::NativeContext& nativeParentContext)
: native (nativeParentContext)
{
}
void handleCommandMessage (int commandId) override
{
if (commandId == 0)
native.triggerRepaint();
}
OpenGLContext::NativeContext& native;
};
template <typename Traits>
class ScopedGLXObject
{
public:
using Type = typename Traits::Type;
ScopedGLXObject() = default;
explicit ScopedGLXObject (Type obj, ::Display* d)
: object (obj), display (d) {}
ScopedGLXObject (ScopedGLXObject&& other) noexcept
: object (std::exchange (other.object, Type{})),
display (std::exchange (other.display, nullptr)) {}
ScopedGLXObject& operator= (ScopedGLXObject&& other) noexcept
{
ScopedGLXObject { std::move (other) }.swap (*this);
return *this;
}
~ScopedGLXObject() noexcept
{
if (object != Type{})
Traits::destroy (display, object);
}
Type get() const { return object; }
void reset() noexcept
{
*this = ScopedGLXObject();
}
void swap (ScopedGLXObject& other) noexcept
{
std::swap (other.object, object);
std::swap (other.display, display);
}
bool operator== (const ScopedGLXObject& other) const
{
const auto tie = [] (const auto& x) { return std::tie (x.object, x.display); };
return tie (*this) == tie (other);
}
bool operator!= (const ScopedGLXObject& other) const
{
return ! operator== (other);
}
private:
Type object{};
::Display* display{};
};
struct TraitsGLXContext
{
using Type = GLXContext;
static void destroy (::Display* display, Type t)
{
glXDestroyContext (display, t);
}
};
struct TraitsGLXWindow
{
using Type = GLXWindow;
static void destroy (::Display* display, Type t)
{
glXDestroyWindow (display, t);
}
};
using PtrGLXContext = ScopedGLXObject<TraitsGLXContext>;
using PtrGLXWindow = ScopedGLXObject<TraitsGLXWindow>;
public:
NativeContext (Component& comp,
const OpenGLPixelFormat& cPixelFormat,
void* shareContext,
bool useMultisamplingIn,
OpenGLVersion)
: component (comp), contextToShareWith (shareContext), dummy (*this)
{
display = XWindowSystem::getInstance()->getDisplay();
XWindowSystemUtilities::ScopedXLock xLock;
X11Symbols::getInstance()->xSync (display, False);
const std::vector<GLint> optionalAttribs
{
GLX_SAMPLE_BUFFERS, useMultisamplingIn ? 1 : 0,
GLX_SAMPLES, cPixelFormat.multisamplingLevel
};
if (! tryChooseVisual (cPixelFormat, optionalAttribs) && ! tryChooseVisual (cPixelFormat, {}))
return;
auto* peer = component.getPeer();
jassert (peer != nullptr);
auto windowH = (Window) peer->getNativeHandle();
auto visual = glXGetVisualFromFBConfig (display, *bestConfig);
auto colourMap = X11Symbols::getInstance()->xCreateColormap (display, windowH, visual->visual, AllocNone);
XSetWindowAttributes swa;
swa.colormap = colourMap;
swa.border_pixel = 0;
swa.event_mask = embeddedWindowEventMask;
auto glBounds = component.getTopLevelComponent()->getLocalArea (&component, component.getLocalBounds());
glBounds = Desktop::getInstance().getDisplays().logicalToPhysical (glBounds);
embeddedWindow = X11Symbols::getInstance()->xCreateWindow (display, windowH,
glBounds.getX(), glBounds.getY(),
(unsigned int) jmax (1, glBounds.getWidth()),
(unsigned int) jmax (1, glBounds.getHeight()),
0, visual->depth,
InputOutput,
visual->visual,
CWBorderPixel | CWColormap | CWEventMask,
&swa);
peerListener.emplace (component, embeddedWindow);
X11Symbols::getInstance()->xMapWindow (display, embeddedWindow);
X11Symbols::getInstance()->xFreeColormap (display, colourMap);
X11Symbols::getInstance()->xSync (display, False);
juce_LinuxAddRepaintListener (peer, &dummy);
}
~NativeContext()
{
if (auto* peer = component.getPeer())
{
juce_LinuxRemoveRepaintListener (peer, &dummy);
if (embeddedWindow != 0)
{
XWindowSystemUtilities::ScopedXLock xLock;
X11Symbols::getInstance()->xUnmapWindow (display, embeddedWindow);
X11Symbols::getInstance()->xDestroyWindow (display, embeddedWindow);
X11Symbols::getInstance()->xSync (display, False);
XEvent event;
while (X11Symbols::getInstance()->xCheckWindowEvent (display,
embeddedWindow,
embeddedWindowEventMask,
&event) == True)
{
}
}
}
}
InitResult initialiseOnRenderThread (OpenGLContext& c)
{
XWindowSystemUtilities::ScopedXLock xLock;
const auto components = [&]() -> Optional<Version>
{
switch (c.versionRequired)
{
case OpenGLVersion::openGL3_2: return Version { 3, 2 };
case OpenGLVersion::openGL4_1: return Version { 4, 1 };
case OpenGLVersion::openGL4_3: return Version { 4, 3 };
case OpenGLVersion::defaultGLVersion: break;
}
return {};
}();
if (components.hasValue())
{
using GLXCreateContextAttribsARB = GLXContext (*) (Display*, GLXFBConfig, GLXContext, Bool, const int*);
if (const auto glXCreateContextAttribsARB = (GLXCreateContextAttribsARB) OpenGLHelpers::getExtensionFunction ("glXCreateContextAttribsARB"))
{
#if JUCE_DEBUG
constexpr auto contextFlags = GLX_CONTEXT_DEBUG_BIT_ARB;
#else
constexpr auto contextFlags = 0;
#endif
const int attribs[]
{
GLX_CONTEXT_MAJOR_VERSION_ARB, components->major,
GLX_CONTEXT_MINOR_VERSION_ARB, components->minor,
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
GLX_CONTEXT_FLAGS_ARB, contextFlags,
None
};
renderContext = PtrGLXContext { glXCreateContextAttribsARB (display, *bestConfig, (GLXContext) contextToShareWith, GL_TRUE, attribs),
display };
}
}
if (renderContext == PtrGLXContext{})
renderContext = PtrGLXContext { glXCreateNewContext (display, *bestConfig, GLX_RGBA_TYPE, (GLXContext) contextToShareWith, GL_TRUE),
display };
if (renderContext == PtrGLXContext{})
return InitResult::fatal;
glxWindow = PtrGLXWindow { glXCreateWindow (display, *bestConfig, embeddedWindow, nullptr),
display };
c.makeActive();
context = &c;
return InitResult::success;
}
void shutdownOnRenderThread()
{
XWindowSystemUtilities::ScopedXLock xLock;
context = nullptr;
deactivateCurrentContext();
renderContext.reset();
glxWindow.reset();
}
bool makeActive() const noexcept
{
XWindowSystemUtilities::ScopedXLock xLock;
return renderContext != PtrGLXContext{}
&& glXMakeContextCurrent (display, glxWindow.get(), glxWindow.get(), renderContext.get());
}
bool isActive() const noexcept
{
XWindowSystemUtilities::ScopedXLock xLock;
return glXGetCurrentContext() == renderContext.get() && renderContext != PtrGLXContext{};
}
static void deactivateCurrentContext()
{
if (auto* display = XWindowSystem::getInstance()->getDisplay())
{
XWindowSystemUtilities::ScopedXLock xLock;
glXMakeCurrent (display, None, nullptr);
}
}
void swapBuffers()
{
glXSwapBuffers (display, glxWindow.get());
}
void updateWindowPosition (Rectangle<int> newBounds)
{
bounds = newBounds;
auto physicalBounds = Desktop::getInstance().getDisplays().logicalToPhysical (bounds);
XWindowSystemUtilities::ScopedXLock xLock;
X11Symbols::getInstance()->xMoveResizeWindow (display, embeddedWindow,
physicalBounds.getX(), physicalBounds.getY(),
(unsigned int) jmax (1, physicalBounds.getWidth()),
(unsigned int) jmax (1, physicalBounds.getHeight()));
}
bool setSwapInterval (int numFramesPerSwap)
{
if (numFramesPerSwap == swapFrames)
return true;
if (auto GLXSwapIntervalEXT
= (PFNGLXSWAPINTERVALEXTPROC) OpenGLHelpers::getExtensionFunction ("glXSwapIntervalEXT"))
{
XWindowSystemUtilities::ScopedXLock xLock;
swapFrames = numFramesPerSwap;
GLXSwapIntervalEXT (display, glxWindow.get(), numFramesPerSwap);
return true;
}
return false;
}
int getSwapInterval() const { return swapFrames; }
bool createdOk() const noexcept { return true; }
void* getRawContext() const noexcept { return renderContext.get(); }
GLuint getFrameBufferID() const noexcept { return 0; }
void triggerRepaint()
{
if (context != nullptr)
context->triggerRepaint();
}
struct Locker
{
explicit Locker (NativeContext& ctx) : lock (ctx.mutex) {}
const ScopedLock lock;
};
private:
bool tryChooseVisual (const OpenGLPixelFormat& format, const std::vector<GLint>& optionalAttribs)
{
std::vector<GLint> allAttribs
{
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_DOUBLEBUFFER, True,
GLX_RED_SIZE, format.redBits,
GLX_GREEN_SIZE, format.greenBits,
GLX_BLUE_SIZE, format.blueBits,
GLX_ALPHA_SIZE, format.alphaBits,
GLX_DEPTH_SIZE, format.depthBufferBits,
GLX_STENCIL_SIZE, format.stencilBufferBits,
GLX_ACCUM_RED_SIZE, format.accumulationBufferRedBits,
GLX_ACCUM_GREEN_SIZE, format.accumulationBufferGreenBits,
GLX_ACCUM_BLUE_SIZE, format.accumulationBufferBlueBits,
GLX_ACCUM_ALPHA_SIZE, format.accumulationBufferAlphaBits
};
allAttribs.insert (allAttribs.end(), optionalAttribs.begin(), optionalAttribs.end());
allAttribs.push_back (None);
int nElements = 0;
bestConfig = makeXFreePtr (glXChooseFBConfig (display, X11Symbols::getInstance()->xDefaultScreen (display), allAttribs.data(), &nElements));
return nElements != 0 && bestConfig != nullptr;
}
static constexpr int embeddedWindowEventMask = ExposureMask | StructureNotifyMask;
CriticalSection mutex;
Component& component;
PtrGLXContext renderContext;
PtrGLXWindow glxWindow;
Window embeddedWindow = {};
std::optional<PeerListener> peerListener;
int swapFrames = 0;
Rectangle<int> bounds;
std::unique_ptr<GLXFBConfig, XFreeDeleter> bestConfig;
void* contextToShareWith;
OpenGLContext* context = nullptr;
DummyComponent dummy;
::Display* display = nullptr;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
};
//==============================================================================
bool OpenGLHelpers::isContextActive()
{
XWindowSystemUtilities::ScopedXLock xLock;
return glXGetCurrentContext() != nullptr;
}
} // namespace juce
@@ -0,0 +1,351 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS
class OpenGLContext::NativeContext
{
public:
NativeContext (Component& component,
const OpenGLPixelFormat& pixFormat,
void* contextToShare,
bool shouldUseMultisampling,
OpenGLVersion version)
: owner (component)
{
const auto attribs = createAttribs (version, pixFormat, shouldUseMultisampling);
NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc] initWithAttributes: attribs.data()];
static MouseForwardingNSOpenGLViewClass cls;
view = [cls.createInstance() initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f)
pixelFormat: format];
if ([view respondsToSelector: @selector (setWantsBestResolutionOpenGLSurface:)])
[view setWantsBestResolutionOpenGLSurface: YES];
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
[[NSNotificationCenter defaultCenter] addObserver: view
selector: @selector (_surfaceNeedsUpdate:)
name: NSViewGlobalFrameDidChangeNotification
object: view];
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
renderContext = [[[NSOpenGLContext alloc] initWithFormat: format
shareContext: (NSOpenGLContext*) contextToShare] autorelease];
[view setOpenGLContext: renderContext];
[format release];
viewAttachment = NSViewComponent::attachViewToComponent (component, view);
}
~NativeContext()
{
[[NSNotificationCenter defaultCenter] removeObserver: view];
[renderContext clearDrawable];
[renderContext setView: nil];
[view setOpenGLContext: nil];
[view release];
}
static std::vector<NSOpenGLPixelFormatAttribute> createAttribs (OpenGLVersion version,
const OpenGLPixelFormat& pixFormat,
bool shouldUseMultisampling)
{
std::vector<NSOpenGLPixelFormatAttribute> attribs
{
NSOpenGLPFAOpenGLProfile, [version]
{
if (version == openGL3_2)
return NSOpenGLProfileVersion3_2Core;
if (version != defaultGLVersion)
return NSOpenGLProfileVersion4_1Core;
return NSOpenGLProfileVersionLegacy;
}(),
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAClosestPolicy,
NSOpenGLPFANoRecovery,
NSOpenGLPFAColorSize, static_cast<NSOpenGLPixelFormatAttribute> (pixFormat.redBits + pixFormat.greenBits + pixFormat.blueBits),
NSOpenGLPFAAlphaSize, static_cast<NSOpenGLPixelFormatAttribute> (pixFormat.alphaBits),
NSOpenGLPFADepthSize, static_cast<NSOpenGLPixelFormatAttribute> (pixFormat.depthBufferBits),
NSOpenGLPFAStencilSize, static_cast<NSOpenGLPixelFormatAttribute> (pixFormat.stencilBufferBits),
NSOpenGLPFAAccumSize, static_cast<NSOpenGLPixelFormatAttribute> (pixFormat.accumulationBufferRedBits + pixFormat.accumulationBufferGreenBits
+ pixFormat.accumulationBufferBlueBits + pixFormat.accumulationBufferAlphaBits)
};
if (shouldUseMultisampling)
{
attribs.insert (attribs.cend(),
{
NSOpenGLPFAMultisample,
NSOpenGLPFASampleBuffers, static_cast<NSOpenGLPixelFormatAttribute> (1),
NSOpenGLPFASamples, static_cast<NSOpenGLPixelFormatAttribute> (pixFormat.multisamplingLevel)
});
}
attribs.push_back (0);
return attribs;
}
InitResult initialiseOnRenderThread (OpenGLContext&) { return InitResult::success; }
void shutdownOnRenderThread() { deactivateCurrentContext(); }
bool createdOk() const noexcept { return getRawContext() != nullptr; }
NSOpenGLView* getNSView() const noexcept { return view; }
NSOpenGLContext* getRawContext() const noexcept { return renderContext; }
GLuint getFrameBufferID() const noexcept { return 0; }
bool makeActive() const noexcept
{
jassert (renderContext != nil);
if ([renderContext view] != view)
[renderContext setView: view];
if (NSOpenGLContext* context = [view openGLContext])
{
[context makeCurrentContext];
return true;
}
return false;
}
bool isActive() const noexcept
{
return [NSOpenGLContext currentContext] == renderContext;
}
static void deactivateCurrentContext()
{
[NSOpenGLContext clearCurrentContext];
}
struct Locker
{
Locker (NativeContext& nc) : cglContext ((CGLContextObj) [nc.renderContext CGLContextObj])
{
CGLLockContext (cglContext);
}
~Locker()
{
CGLUnlockContext (cglContext);
}
private:
CGLContextObj cglContext;
};
void swapBuffers()
{
auto now = Time::getMillisecondCounterHiRes();
[renderContext flushBuffer];
if (const auto minSwapTime = minSwapTimeMs.get(); minSwapTime > 0)
{
// When our window is entirely occluded by other windows, flushBuffer
// fails to wait for the swap interval, so the render loop spins at full
// speed, burning CPU. This hack detects when things are going too fast
// and sleeps if necessary.
auto swapTime = Time::getMillisecondCounterHiRes() - now;
auto frameTime = (int) std::min ((uint64_t) std::numeric_limits<int>::max(),
(uint64_t) now - (uint64_t) lastSwapTime);
if (swapTime < 0.5 && frameTime < minSwapTime - 3)
{
if (underrunCounter > 3)
{
Thread::sleep (2 * (minSwapTime - frameTime));
now = Time::getMillisecondCounterHiRes();
}
else
{
++underrunCounter;
}
}
else
{
if (underrunCounter > 0)
--underrunCounter;
}
}
lastSwapTime = now;
}
void updateWindowPosition (Rectangle<int>)
{
if (auto* peer = owner.getTopLevelComponent()->getPeer())
{
const auto newArea = peer->getAreaCoveredBy (owner);
if (convertToRectInt ([view frame]) != newArea)
[view setFrame: makeCGRect (newArea)];
}
}
bool setSwapInterval (int numFramesPerSwapIn)
{
// The macOS OpenGL programming guide says that numFramesPerSwap
// can only be 0 or 1.
jassert (isPositiveAndBelow (numFramesPerSwapIn, 2));
[renderContext setValues: (const GLint*) &numFramesPerSwapIn
forParameter: getSwapIntervalParameter()];
minSwapTimeMs.setFramesPerSwap (numFramesPerSwapIn);
return true;
}
int getSwapInterval() const
{
GLint numFrames = 0;
[renderContext getValues: &numFrames
forParameter: getSwapIntervalParameter()];
return numFrames;
}
void setNominalVideoRefreshPeriodS (double periodS)
{
jassert (periodS > 0.0);
minSwapTimeMs.setVideoRefreshPeriodS (periodS);
}
static NSOpenGLContextParameter getSwapIntervalParameter()
{
if (@available (macOS 10.12, *))
return NSOpenGLContextParameterSwapInterval;
return NSOpenGLCPSwapInterval;
}
class MinSwapTimeMs
{
public:
int get() const
{
return minSwapTimeMs;
}
void setFramesPerSwap (int n)
{
const std::scoped_lock lock { mutex };
numFramesPerSwap = n;
updateMinSwapTime();
}
void setVideoRefreshPeriodS (double n)
{
const std::scoped_lock lock { mutex };
videoRefreshPeriodS = n;
updateMinSwapTime();
}
private:
void updateMinSwapTime()
{
minSwapTimeMs = static_cast<int> (numFramesPerSwap * 1000 * videoRefreshPeriodS);
}
std::mutex mutex;
std::atomic<int> minSwapTimeMs { 0 };
int numFramesPerSwap = 0;
double videoRefreshPeriodS = 1.0 / 60.0;
};
Component& owner;
NSOpenGLContext* renderContext = nil;
NSOpenGLView* view = nil;
ReferenceCountedObjectPtr<ReferenceCountedObject> viewAttachment;
double lastSwapTime = 0;
int underrunCounter = 0;
MinSwapTimeMs minSwapTimeMs;
//==============================================================================
struct MouseForwardingNSOpenGLViewClass : public ObjCClass<NSOpenGLView>
{
MouseForwardingNSOpenGLViewClass() : ObjCClass ("JUCEGLView_")
{
addMethod (@selector (rightMouseDown:), [] (id self, SEL, NSEvent* ev)
{
[[(NSOpenGLView*) self superview] rightMouseDown: ev];
});
addMethod (@selector (rightMouseUp:), [] (id self, SEL, NSEvent* ev)
{
[[(NSOpenGLView*) self superview] rightMouseUp: ev];
});
addMethod (@selector (acceptsFirstMouse:), [] (id, SEL, NSEvent*) -> BOOL
{
return YES;
});
addMethod (@selector (accessibilityHitTest:), [] (id self, SEL, NSPoint p) -> id
{
return [[(NSOpenGLView*) self superview] accessibilityHitTest: p];
});
addMethod (@selector (hitTest:), [] (id, SEL, NSPoint) -> NSView*
{
return nil;
});
registerClass();
}
};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
};
//==============================================================================
bool OpenGLHelpers::isContextActive()
{
return CGLGetCurrentContext() != CGLContextObj();
}
JUCE_END_IGNORE_DEPRECATION_WARNINGS
} // namespace juce
@@ -0,0 +1,443 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
extern ComponentPeer* createNonRepaintingEmbeddedWindowsPeer (Component&, Component* parent);
//==============================================================================
class OpenGLContext::NativeContext : private ComponentPeer::ScaleFactorListener,
private AsyncUpdater
{
public:
NativeContext (Component& component,
const OpenGLPixelFormat& pixelFormat,
void* contextToShareWithIn,
bool /*useMultisampling*/,
OpenGLVersion version)
: sharedContext (contextToShareWithIn)
{
placeholderComponent.reset (new PlaceholderComponent (*this));
createNativeWindow (component);
PIXELFORMATDESCRIPTOR pfd;
initialisePixelFormatDescriptor (pfd, pixelFormat);
auto pixFormat = ChoosePixelFormat (dc.get(), &pfd);
if (pixFormat != 0)
SetPixelFormat (dc.get(), pixFormat, &pfd);
initialiseWGLExtensions (dc.get());
renderContext.reset (createRenderContext (version, dc.get()));
if (renderContext != nullptr)
{
makeActive();
auto wglFormat = wglChoosePixelFormatExtension (pixelFormat);
deactivateCurrentContext();
if (wglFormat != pixFormat && wglFormat != 0)
{
// can't change the pixel format of a window, so need to delete the
// old one and create a new one.
dc.reset();
nativeWindow = nullptr;
createNativeWindow (component);
if (SetPixelFormat (dc.get(), wglFormat, &pfd))
{
renderContext.reset();
renderContext.reset (createRenderContext (version, dc.get()));
}
}
component.getTopLevelComponent()->repaint();
component.repaint();
}
}
~NativeContext() override
{
cancelPendingUpdate();
renderContext.reset();
dc.reset();
if (safeComponent != nullptr)
if (auto* peer = safeComponent->getTopLevelComponent()->getPeer())
peer->removeScaleFactorListener (this);
}
InitResult initialiseOnRenderThread (OpenGLContext& c)
{
threadAwarenessSetter = std::make_unique<ScopedThreadDPIAwarenessSetter> (nativeWindow->getNativeHandle());
context = &c;
if (sharedContext != nullptr)
{
if (! wglShareLists ((HGLRC) sharedContext, renderContext.get()))
{
TCHAR messageBuffer[256] = {};
FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
GetLastError(),
MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
messageBuffer,
(DWORD) numElementsInArray (messageBuffer) - 1,
nullptr);
DBG (messageBuffer);
jassertfalse;
}
}
return InitResult::success;
}
void shutdownOnRenderThread()
{
deactivateCurrentContext();
context = nullptr;
threadAwarenessSetter = nullptr;
}
static void deactivateCurrentContext() { wglMakeCurrent (nullptr, nullptr); }
bool makeActive() const noexcept { return isActive() || wglMakeCurrent (dc.get(), renderContext.get()) != FALSE; }
bool isActive() const noexcept { return wglGetCurrentContext() == renderContext.get(); }
void swapBuffers() noexcept
{
SwapBuffers (dc.get());
if (! std::exchange (haveBuffersBeenSwapped, true))
triggerAsyncUpdate();
}
bool setSwapInterval (int numFramesPerSwap)
{
jassert (isActive()); // this can only be called when the context is active..
return wglSwapIntervalEXT != nullptr && wglSwapIntervalEXT (numFramesPerSwap) != FALSE;
}
int getSwapInterval() const
{
jassert (isActive()); // this can only be called when the context is active..
return wglGetSwapIntervalEXT != nullptr ? wglGetSwapIntervalEXT() : 0;
}
void updateWindowPosition (Rectangle<int> bounds)
{
if (nativeWindow != nullptr)
{
if (! approximatelyEqual (nativeScaleFactor, 1.0))
bounds = (bounds.toDouble() * nativeScaleFactor).toNearestInt();
SetWindowPos ((HWND) nativeWindow->getNativeHandle(), nullptr,
bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(),
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
}
}
bool createdOk() const noexcept { return getRawContext() != nullptr; }
void* getRawContext() const noexcept { return renderContext.get(); }
unsigned int getFrameBufferID() const noexcept { return 0; }
void triggerRepaint()
{
if (context != nullptr)
context->triggerRepaint();
}
struct Locker
{
explicit Locker (NativeContext& ctx) : lock (ctx.mutex) {}
const ScopedLock lock;
};
HWND getNativeHandle()
{
if (nativeWindow != nullptr)
return (HWND) nativeWindow->getNativeHandle();
return nullptr;
}
private:
//==============================================================================
void handleAsyncUpdate() override
{
nativeWindow->setVisible (true);
}
static void initialiseWGLExtensions (HDC dcIn)
{
static bool initialised = false;
if (initialised)
return;
initialised = true;
const auto dummyContext = wglCreateContext (dcIn);
wglMakeCurrent (dcIn, dummyContext);
#define JUCE_INIT_WGL_FUNCTION(name) name = (type_ ## name) OpenGLHelpers::getExtensionFunction (#name);
JUCE_INIT_WGL_FUNCTION (wglChoosePixelFormatARB)
JUCE_INIT_WGL_FUNCTION (wglSwapIntervalEXT)
JUCE_INIT_WGL_FUNCTION (wglGetSwapIntervalEXT)
JUCE_INIT_WGL_FUNCTION (wglCreateContextAttribsARB)
#undef JUCE_INIT_WGL_FUNCTION
wglMakeCurrent (nullptr, nullptr);
wglDeleteContext (dummyContext);
}
static void initialisePixelFormatDescriptor (PIXELFORMATDESCRIPTOR& pfd, const OpenGLPixelFormat& pixelFormat)
{
zerostruct (pfd);
pfd.nSize = sizeof (pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.iLayerType = PFD_MAIN_PLANE;
pfd.cColorBits = (BYTE) (pixelFormat.redBits + pixelFormat.greenBits + pixelFormat.blueBits);
pfd.cRedBits = (BYTE) pixelFormat.redBits;
pfd.cGreenBits = (BYTE) pixelFormat.greenBits;
pfd.cBlueBits = (BYTE) pixelFormat.blueBits;
pfd.cAlphaBits = (BYTE) pixelFormat.alphaBits;
pfd.cDepthBits = (BYTE) pixelFormat.depthBufferBits;
pfd.cStencilBits = (BYTE) pixelFormat.stencilBufferBits;
pfd.cAccumBits = (BYTE) (pixelFormat.accumulationBufferRedBits + pixelFormat.accumulationBufferGreenBits
+ pixelFormat.accumulationBufferBlueBits + pixelFormat.accumulationBufferAlphaBits);
pfd.cAccumRedBits = (BYTE) pixelFormat.accumulationBufferRedBits;
pfd.cAccumGreenBits = (BYTE) pixelFormat.accumulationBufferGreenBits;
pfd.cAccumBlueBits = (BYTE) pixelFormat.accumulationBufferBlueBits;
pfd.cAccumAlphaBits = (BYTE) pixelFormat.accumulationBufferAlphaBits;
}
static HGLRC createRenderContext (OpenGLVersion version, HDC dcIn)
{
const auto components = [&]() -> Optional<Version>
{
switch (version)
{
case OpenGLVersion::openGL3_2: return Version { 3, 2 };
case OpenGLVersion::openGL4_1: return Version { 4, 1 };
case OpenGLVersion::openGL4_3: return Version { 4, 3 };
case OpenGLVersion::defaultGLVersion: break;
}
return {};
}();
if (components.hasValue() && wglCreateContextAttribsARB != nullptr)
{
#if JUCE_DEBUG
constexpr auto contextFlags = WGL_CONTEXT_DEBUG_BIT_ARB;
constexpr auto noErrorChecking = GL_FALSE;
#else
constexpr auto contextFlags = 0;
constexpr auto noErrorChecking = GL_TRUE;
#endif
const int attribs[] =
{
WGL_CONTEXT_MAJOR_VERSION_ARB, components->major,
WGL_CONTEXT_MINOR_VERSION_ARB, components->minor,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
WGL_CONTEXT_FLAGS_ARB, contextFlags,
WGL_CONTEXT_OPENGL_NO_ERROR_ARB, noErrorChecking,
0
};
const auto c = wglCreateContextAttribsARB (dcIn, nullptr, attribs);
if (c != nullptr)
return c;
}
return wglCreateContext (dcIn);
}
//==============================================================================
struct PlaceholderComponent : public Component
{
explicit PlaceholderComponent (NativeContext& c)
: context (c)
{
setOpaque (true);
}
// The windowing code will call this when a paint callback happens
void handleCommandMessage (int) override { context.triggerRepaint(); }
NativeContext& context;
};
//==============================================================================
void nativeScaleFactorChanged (double newScaleFactor) override
{
if (approximatelyEqual (newScaleFactor, nativeScaleFactor)
|| safeComponent == nullptr)
return;
if (auto* peer = safeComponent->getTopLevelComponent()->getPeer())
{
nativeScaleFactor = newScaleFactor;
updateWindowPosition (peer->getAreaCoveredBy (*safeComponent));
}
}
void createNativeWindow (Component& component)
{
auto* topComp = component.getTopLevelComponent();
{
auto* parentHWND = topComp->getWindowHandle();
ScopedThreadDPIAwarenessSetter setter { parentHWND };
nativeWindow.reset (createNonRepaintingEmbeddedWindowsPeer (*placeholderComponent, topComp));
}
if (auto* peer = topComp->getPeer())
{
safeComponent = Component::SafePointer<Component> (&component);
nativeScaleFactor = peer->getPlatformScaleFactor();
updateWindowPosition (peer->getAreaCoveredBy (component));
peer->addScaleFactorListener (this);
}
dc = std::unique_ptr<std::remove_pointer_t<HDC>, DeviceContextDeleter> { GetDC ((HWND) nativeWindow->getNativeHandle()),
DeviceContextDeleter { (HWND) nativeWindow->getNativeHandle() } };
}
int wglChoosePixelFormatExtension (const OpenGLPixelFormat& pixelFormat) const
{
int format = 0;
if (wglChoosePixelFormatARB != nullptr)
{
int atts[64];
int n = 0;
atts[n++] = WGL_DRAW_TO_WINDOW_ARB; atts[n++] = GL_TRUE;
atts[n++] = WGL_SUPPORT_OPENGL_ARB; atts[n++] = GL_TRUE;
atts[n++] = WGL_DOUBLE_BUFFER_ARB; atts[n++] = GL_TRUE;
atts[n++] = WGL_PIXEL_TYPE_ARB; atts[n++] = WGL_TYPE_RGBA_ARB;
atts[n++] = WGL_ACCELERATION_ARB;
atts[n++] = WGL_FULL_ACCELERATION_ARB;
atts[n++] = WGL_COLOR_BITS_ARB; atts[n++] = pixelFormat.redBits + pixelFormat.greenBits + pixelFormat.blueBits;
atts[n++] = WGL_RED_BITS_ARB; atts[n++] = pixelFormat.redBits;
atts[n++] = WGL_GREEN_BITS_ARB; atts[n++] = pixelFormat.greenBits;
atts[n++] = WGL_BLUE_BITS_ARB; atts[n++] = pixelFormat.blueBits;
atts[n++] = WGL_ALPHA_BITS_ARB; atts[n++] = pixelFormat.alphaBits;
atts[n++] = WGL_DEPTH_BITS_ARB; atts[n++] = pixelFormat.depthBufferBits;
atts[n++] = WGL_STENCIL_BITS_ARB; atts[n++] = pixelFormat.stencilBufferBits;
atts[n++] = WGL_ACCUM_RED_BITS_ARB; atts[n++] = pixelFormat.accumulationBufferRedBits;
atts[n++] = WGL_ACCUM_GREEN_BITS_ARB; atts[n++] = pixelFormat.accumulationBufferGreenBits;
atts[n++] = WGL_ACCUM_BLUE_BITS_ARB; atts[n++] = pixelFormat.accumulationBufferBlueBits;
atts[n++] = WGL_ACCUM_ALPHA_BITS_ARB; atts[n++] = pixelFormat.accumulationBufferAlphaBits;
if (pixelFormat.multisamplingLevel > 0
&& OpenGLHelpers::isExtensionSupported ("GL_ARB_multisample"))
{
atts[n++] = WGL_SAMPLE_BUFFERS_ARB;
atts[n++] = 1;
atts[n++] = WGL_SAMPLES_ARB;
atts[n++] = pixelFormat.multisamplingLevel;
}
atts[n++] = 0;
jassert (n <= numElementsInArray (atts));
UINT formatsCount = 0;
wglChoosePixelFormatARB (dc.get(), atts, nullptr, 1, &format, &formatsCount);
}
return format;
}
//==============================================================================
#define JUCE_DECLARE_WGL_EXTENSION_FUNCTION(name, returnType, params) \
typedef returnType (__stdcall *type_ ## name) params; static type_ ## name name;
JUCE_DECLARE_WGL_EXTENSION_FUNCTION (wglChoosePixelFormatARB, BOOL, (HDC, const int*, const FLOAT*, UINT, int*, UINT*))
JUCE_DECLARE_WGL_EXTENSION_FUNCTION (wglSwapIntervalEXT, BOOL, (int))
JUCE_DECLARE_WGL_EXTENSION_FUNCTION (wglGetSwapIntervalEXT, int, ())
JUCE_DECLARE_WGL_EXTENSION_FUNCTION (wglCreateContextAttribsARB, HGLRC, (HDC, HGLRC, const int*))
#undef JUCE_DECLARE_WGL_EXTENSION_FUNCTION
//==============================================================================
struct RenderContextDeleter
{
void operator() (HGLRC ptr) const { wglDeleteContext (ptr); }
};
struct DeviceContextDeleter
{
void operator() (HDC ptr) const { ReleaseDC (hwnd, ptr); }
HWND hwnd;
};
CriticalSection mutex;
std::unique_ptr<PlaceholderComponent> placeholderComponent;
std::unique_ptr<ComponentPeer> nativeWindow;
std::unique_ptr<ScopedThreadDPIAwarenessSetter> threadAwarenessSetter;
Component::SafePointer<Component> safeComponent;
std::unique_ptr<std::remove_pointer_t<HGLRC>, RenderContextDeleter> renderContext;
std::unique_ptr<std::remove_pointer_t<HDC>, DeviceContextDeleter> dc;
OpenGLContext* context = nullptr;
void* sharedContext = nullptr;
double nativeScaleFactor = 1.0;
bool haveBuffersBeenSwapped = false;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeContext)
};
//==============================================================================
bool OpenGLHelpers::isContextActive()
{
return wglGetCurrentContext() != nullptr;
}
} // namespace juce