#include <config.h>
#if HAVE_GL
#include <FL/platform.H>
#include <FL/gl.h>
#include "../../Fl_Gl_Choice.H"
#include "../../Fl_Screen_Driver.H"
#include "Fl_Cocoa_Window_Driver.H"
#include "Fl_Cocoa_Gl_Window_Driver.H"
#include <FL/Fl_Graphics_Driver.H>
#include <OpenGL/OpenGL.h>
#include <FL/Fl_Image_Surface.H>
#include <dlfcn.h>
#import <Cocoa/Cocoa.h>
class Fl_Cocoa_Gl_Choice : public Fl_Gl_Choice {
friend class Fl_Cocoa_Gl_Window_Driver;
private:
NSOpenGLPixelFormat* pixelformat;
public:
Fl_Cocoa_Gl_Choice(int m, const int *alistp, Fl_Gl_Choice *n) : Fl_Gl_Choice(m, alistp, n) {
pixelformat = NULL;
}
};
Fl_Cocoa_Gl_Window_Driver::Fl_Cocoa_Gl_Window_Driver(Fl_Gl_Window *win) :
Fl_Gl_Window_Driver(win) {
gl1ctxt = NULL;
}
Fl_Gl_Window_Driver *Fl_Gl_Window_Driver::newGlWindowDriver(Fl_Gl_Window *w)
{
return new Fl_Cocoa_Gl_Window_Driver(w);
}
static NSOpenGLPixelFormat* mode_to_NSOpenGLPixelFormat(int m, const int *alistp)
{
NSOpenGLPixelFormatAttribute attribs[32];
int n = 0;
if (!alistp) {
if (m & FL_INDEX) {
} else {
attribs[n++] = NSOpenGLPFAColorSize;
attribs[n++] = (NSOpenGLPixelFormatAttribute)((m & FL_RGB8) ? 32 : 1);
if (m & FL_ALPHA) {
attribs[n++] = NSOpenGLPFAAlphaSize;
attribs[n++] = (NSOpenGLPixelFormatAttribute)((m & FL_RGB8) ? 8 : 1);
}
if (m & FL_ACCUM) {
attribs[n++] = NSOpenGLPFAAccumSize;
attribs[n++] = (NSOpenGLPixelFormatAttribute)1;
if (m & FL_ALPHA) {
}
}
}
if (m & FL_DOUBLE) {
attribs[n++] = NSOpenGLPFADoubleBuffer;
}
if (m & FL_DEPTH) {
attribs[n++] = NSOpenGLPFADepthSize;
attribs[n++] = (NSOpenGLPixelFormatAttribute)24;
}
if (m & FL_STENCIL) {
attribs[n++] = NSOpenGLPFAStencilSize;
attribs[n++] = (NSOpenGLPixelFormatAttribute)1;
}
if (m & FL_STEREO) {
attribs[n++] = 6;
}
if ((m & FL_MULTISAMPLE) && fl_mac_os_version >= 100400) {
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
attribs[n++] = NSOpenGLPFAMultisample; #endif
attribs[n++] = NSOpenGLPFASampleBuffers; attribs[n++] = (NSOpenGLPixelFormatAttribute)1;
attribs[n++] = NSOpenGLPFASamples; attribs[n++] = (NSOpenGLPixelFormatAttribute)4;
}
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
#define NSOpenGLPFAOpenGLProfile (NSOpenGLPixelFormatAttribute)99
#define kCGLPFAOpenGLProfile NSOpenGLPFAOpenGLProfile
#define NSOpenGLProfileVersionLegacy (NSOpenGLPixelFormatAttribute)0x1000
#define NSOpenGLProfileVersion3_2Core (NSOpenGLPixelFormatAttribute)0x3200
#define kCGLOGLPVersion_Legacy NSOpenGLProfileVersionLegacy
#endif
if (fl_mac_os_version >= 100700) {
attribs[n++] = NSOpenGLPFAOpenGLProfile;
attribs[n++] = (m & FL_OPENGL3) ? NSOpenGLProfileVersion3_2Core : NSOpenGLProfileVersionLegacy;
}
} else {
while (alistp[n] && n < 30) {
if (alistp[n] == kCGLPFAOpenGLProfile) {
if (fl_mac_os_version < 100700) {
if (alistp[n+1] != kCGLOGLPVersion_Legacy) return nil;
n += 2;
continue;
}
}
attribs[n] = (NSOpenGLPixelFormatAttribute)alistp[n];
n++;
}
}
attribs[n] = (NSOpenGLPixelFormatAttribute)0;
NSOpenGLPixelFormat *pixform = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
return pixform;
}
Fl_Gl_Choice *Fl_Cocoa_Gl_Window_Driver::find(int m, const int *alistp)
{
Fl::screen_driver()->open_display(); Fl_Cocoa_Gl_Choice *g = (Fl_Cocoa_Gl_Choice*)Fl_Gl_Window_Driver::find_begin(m, alistp);
if (g) return g;
NSOpenGLPixelFormat* fmt = mode_to_NSOpenGLPixelFormat(m, alistp);
if (!fmt) return 0;
g = new Fl_Cocoa_Gl_Choice(m, alistp, first);
first = g;
g->pixelformat = fmt;
return g;
}
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
# define NSOpenGLContextParameterSurfaceOpacity NSOpenGLCPSurfaceOpacity
#endif
static void remove_gl_context_opacity(NSOpenGLContext *ctx) {
GLint gl_opacity;
[ctx getValues:&gl_opacity forParameter:NSOpenGLContextParameterSurfaceOpacity];
if (gl_opacity != 0) {
gl_opacity = 0;
[ctx setValues:&gl_opacity forParameter:NSOpenGLContextParameterSurfaceOpacity];
}
}
static NSOpenGLContext *create_GLcontext_for_window(
NSOpenGLPixelFormat *pixelformat,
NSOpenGLContext *shared_ctx, Fl_Window *window)
{
NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:pixelformat shareContext:shared_ctx];
if (shared_ctx && !context) context = [[NSOpenGLContext alloc] initWithFormat:pixelformat shareContext:nil];
if (context) {
NSView *view = [fl_xid(window) contentView];
if (view && fl_mac_os_version >= 100700) {
typedef void (*bestResolutionIMP)(id, SEL, BOOL);
static bestResolutionIMP addr = (bestResolutionIMP)[NSView instanceMethodForSelector:@selector(setWantsBestResolutionOpenGLSurface:)];
addr(view, @selector(setWantsBestResolutionOpenGLSurface:), Fl::use_high_res_GL() != 0);
}
[context setView:view];
if (Fl_Cocoa_Window_Driver::driver(window)->subRect()) {
remove_gl_context_opacity(context);
}
}
return context;
}
GLContext Fl_Cocoa_Gl_Window_Driver::create_gl_context(Fl_Window* window, const Fl_Gl_Choice* g) {
GLContext context, shared_ctx = 0;
if (context_list && nContext) shared_ctx = context_list[0];
if (!shared_ctx) gl_texture_reset();
context = create_GLcontext_for_window(((Fl_Cocoa_Gl_Choice*)g)->pixelformat, (NSOpenGLContext*)shared_ctx, window);
if (!context) return 0;
add_context(context);
[(NSOpenGLContext*)context makeCurrentContext];
glClearColor(0., 0., 0., 1.);
apply_scissor();
return (context);
}
void Fl_Cocoa_Gl_Window_Driver::set_gl_context(Fl_Window* w, GLContext context) {
NSOpenGLContext *current_context = [NSOpenGLContext currentContext];
if (context != current_context || w != cached_window) {
cached_window = w;
[(NSOpenGLContext*)context makeCurrentContext];
}
}
void Fl_Cocoa_Gl_Window_Driver::delete_gl_context(GLContext context) {
NSOpenGLContext *current_context = [NSOpenGLContext currentContext];
if (current_context == context) {
cached_window = 0;
[current_context clearDrawable];
}
[(NSOpenGLContext*)context release];
del_context(context);
if (gl1ctxt) {
[[gl1ctxt view] release];
[gl1ctxt release];
gl1ctxt = 0;
}
}
void Fl_Cocoa_Gl_Window_Driver::make_overlay_current() {
if (pWindow->shown()) pWindow->make_current();
}
void Fl_Cocoa_Gl_Window_Driver::redraw_overlay() {
pWindow->redraw();
}
void Fl_Cocoa_Gl_Window_Driver::before_show(int& need_after) {
need_after = 1;
}
void Fl_Cocoa_Gl_Window_Driver::after_show() {
pWindow->make_current();
if ((mode() & FL_OPENGL3) && !gl1ctxt) {
NSView *view = [fl_mac_xid(pWindow) contentView];
NSView *gl1view = [[NSView alloc] initWithFrame:[view frame]];
[gl1view setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
static NSOpenGLContext *shared_gl1_ctxt = nil;
static NSOpenGLPixelFormat *gl1pixelformat = mode_to_NSOpenGLPixelFormat(
FL_RGB8 | FL_ALPHA | FL_SINGLE, NULL);
gl1ctxt = [[NSOpenGLContext alloc] initWithFormat:gl1pixelformat shareContext:shared_gl1_ctxt];
if (!shared_gl1_ctxt) {
shared_gl1_ctxt = gl1ctxt;
[shared_gl1_ctxt retain];
}
[view addSubview:gl1view];
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
if (fl_mac_os_version >= 100700 && Fl::use_high_res_GL()) {
[gl1view setWantsBestResolutionOpenGLSurface:YES];
}
#endif
[gl1ctxt setView:gl1view];
remove_gl_context_opacity(gl1ctxt);
}
}
float Fl_Cocoa_Gl_Window_Driver::pixels_per_unit()
{
int retina = (fl_mac_os_version >= 100700 && Fl::use_high_res_GL() && Fl_X::flx(pWindow) &&
Fl_Cocoa_Window_Driver::driver(pWindow)->mapped_to_retina()) ? 2 : 1;
return retina * Fl_Graphics_Driver::default_driver().scale();
}
int Fl_Cocoa_Gl_Window_Driver::mode_(int m, const int *a) {
if (a) { const int *aa = a;
while (*aa) {
if (*(aa++) ==
kCGLPFADoubleBuffer
) { m |= FL_DOUBLE; break; }
}
}
pWindow->context(0);
mode( m); alist(a);
if (pWindow->shown()) {
g( find(m, a) );
pWindow->redraw();
} else {
g(0);
}
return 1;
}
void Fl_Cocoa_Gl_Window_Driver::make_current_before() {
Fl_Cocoa_Window_Driver *d = Fl_Cocoa_Window_Driver::driver(pWindow);
if (d->changed_resolution()){
d->changed_resolution(false);
pWindow->invalidate();
[(NSOpenGLContext*)pWindow->context() update];
}
}
void Fl_Cocoa_Gl_Window_Driver::swap_buffers() {
if (overlay() != NULL) {
int wo = pWindow->pixel_w(), ho = pWindow->pixel_h();
GLint matrixmode;
GLfloat pos[4];
glGetIntegerv(GL_MATRIX_MODE, &matrixmode);
glGetFloatv(GL_CURRENT_RASTER_POSITION, pos); glMatrixMode(GL_PROJECTION); glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glScalef(2.0f/wo, 2.0f/ho, 1.0f);
glTranslatef(-wo/2.0f, -ho/2.0f, 0.0f); glRasterPos2i(0,0); {
glReadBuffer(GL_BACK);
glDrawBuffer(GL_FRONT);
glCopyPixels(0, 0, wo, ho, GL_COLOR); }
glPopMatrix(); glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(matrixmode);
glRasterPos3f(pos[0], pos[1], pos[2]); } else {
[(NSOpenGLContext*)pWindow->context() flushBuffer];
}
}
char Fl_Cocoa_Gl_Window_Driver::swap_type() {return copy;}
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
# define NSOpenGLContextParameterSwapInterval NSOpenGLCPSwapInterval
#endif
void Fl_Cocoa_Gl_Window_Driver::swap_interval(int n) {
GLint interval = (GLint)n;
NSOpenGLContext* ctx = (NSOpenGLContext*)pWindow->context();
if (ctx)
[ctx setValues:&interval forParameter:NSOpenGLContextParameterSwapInterval];
}
int Fl_Cocoa_Gl_Window_Driver::swap_interval() const {
GLint interval = (GLint)-1;
NSOpenGLContext* ctx = (NSOpenGLContext*)pWindow->context();
if (ctx)
[ctx getValues:&interval forParameter:NSOpenGLContextParameterSwapInterval];
return interval;
}
void Fl_Cocoa_Gl_Window_Driver::resize(int is_a_resize, int w, int h) {
if (pWindow->shown()) apply_scissor();
[(NSOpenGLContext*)pWindow->context() update];
if (gl1ctxt) {
[gl1ctxt update];
}
}
void Fl_Cocoa_Gl_Window_Driver::apply_scissor() {
if (glIsEnabled(GL_SCISSOR_TEST)) glDisable(GL_SCISSOR_TEST);
CGRect *extents = Fl_Cocoa_Window_Driver::driver(pWindow)->subRect();
if (extents) {
remove_gl_context_opacity((NSOpenGLContext*)pWindow->context());
GLdouble vals[4];
glGetDoublev(GL_COLOR_CLEAR_VALUE, vals);
glClearColor(0., 0., 0., 0.);
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(vals[0], vals[1], vals[2], vals[3]);
float s = pWindow->pixels_per_unit();
glScissor(s*extents->origin.x, s*extents->origin.y, s*extents->size.width, s*extents->size.height);
glEnable(GL_SCISSOR_TEST);
}
}
char *Fl_Cocoa_Gl_Window_Driver::alpha_mask_for_string(const char *str, int n, int w, int h, Fl_Fontsize fs)
{
Fl_Image_Surface *surf = new Fl_Image_Surface(w, h);
Fl_Font f=fl_font();
Fl_Surface_Device::push_current(surf);
fl_color(FL_WHITE);
fl_font(f, fs);
fl_draw(str, n, 0, fl_height() - fl_descent());
char *alpha_buf = new char[w*h], *r = alpha_buf, *q;
q = (char*)CGBitmapContextGetData((CGContextRef)surf->offscreen());
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
*r++ = *(q+3);
q += 4;
}
}
Fl_Surface_Device::pop_current();
delete surf;
return alpha_buf;
}
void Fl_Cocoa_Gl_Window_Driver::gl_start() {
[(NSOpenGLContext*)gl_start_context update];
}
static uchar *convert_BGRA_to_RGB(uchar *baseAddress, int w, int h, int mByteWidth)
{
uchar *newimg = new uchar[3*w*h];
uchar *to = newimg;
for (int i = h-1; i >= 0; i--) {
uchar *from = baseAddress + i * mByteWidth;
for (int j = 0; j < w; j++, from += 4) {
#if defined(__ppc__) && __ppc__
memcpy(to, from + 1, 3);
to += 3;
#else
*(to++) = *(from+2);
*(to++) = *(from+1);
*(to++) = *from;
#endif
}
}
delete[] baseAddress;
return newimg;
}
static Fl_RGB_Image *cgimage_to_rgb4(CGImageRef img) {
int w = (int)CGImageGetWidth(img);
int h = (int)CGImageGetHeight(img);
CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB();
uchar *rgba = new uchar[4 * w * h];
CGContextRef auxgc = CGBitmapContextCreate(rgba, w, h, 8, 4 * w, cspace,
kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(cspace);
CGContextDrawImage(auxgc, CGRectMake(0, 0, w, h), img);
CGContextRelease(auxgc);
Fl_RGB_Image *rgb = new Fl_RGB_Image(rgba, w, h, 4);
rgb->alloc_array = 1;
return rgb;
}
Fl_RGB_Image* Fl_Cocoa_Gl_Window_Driver::capture_gl_rectangle(int x, int y, int w, int h)
{
Fl_Gl_Window* glw = pWindow;
float factor = glw->pixels_per_unit();
if (factor != 1) {
w *= factor; h *= factor; x *= factor; y *= factor;
}
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
if (fl_mac_os_version >= 100500) {
NSWindow *nswin = (NSWindow*)fl_mac_xid(pWindow);
CGImageRef img_full = Fl_Cocoa_Window_Driver::capture_decorated_window_10_5(nswin);
int bt = [nswin frame].size.height - [[nswin contentView] frame].size.height;
bt *= (factor / Fl_Graphics_Driver::default_driver().scale());
CGRect cgr = CGRectMake(x, y + bt, w, h); CGImageRef cgimg = CGImageCreateWithImageInRect(img_full, cgr); CGImageRelease(img_full);
Fl_RGB_Image *rgb = cgimage_to_rgb4(cgimg);
CGImageRelease(cgimg);
return rgb;
}
#endif
[(NSOpenGLContext*)glw->context() makeCurrentContext];
[(NSOpenGLContext*)glw->context() flushBuffer];
glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
glPixelStorei(GL_PACK_ALIGNMENT, 4);
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
glPixelStorei(GL_PACK_SKIP_ROWS, 0);
glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
int mByteWidth = w * 4;
mByteWidth = (mByteWidth + 3) & ~3; uchar *baseAddress = new uchar[mByteWidth * h];
glReadPixels(x, glw->pixel_h() - (y+h), w, h,
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, baseAddress);
glPopClientAttrib();
baseAddress = convert_BGRA_to_RGB(baseAddress, w, h, mByteWidth);
Fl_RGB_Image *img = new Fl_RGB_Image(baseAddress, w, h, 3, 3 * w);
img->alloc_array = 1;
[(NSOpenGLContext*)glw->context() flushBuffer];
return img;
}
void* Fl_Cocoa_Gl_Window_Driver::GetProcAddress(const char *procName) {
return dlsym(RTLD_DEFAULT, procName);
}
FL_EXPORT NSOpenGLContext *fl_mac_glcontext(GLContext rc) {
return (NSOpenGLContext*)rc;
}
void Fl_Cocoa_Gl_Window_Driver::switch_to_GL1() {
[gl1ctxt makeCurrentContext];
glClearColor(0., 0., 0., 0.);
glClear(GL_COLOR_BUFFER_BIT);
}
void Fl_Cocoa_Gl_Window_Driver::switch_back() {
glFlush();
[(NSOpenGLContext*)pWindow->context() makeCurrentContext];
}
class Fl_Gl_Cocoa_Plugin : public Fl_Cocoa_Plugin {
public:
Fl_Gl_Cocoa_Plugin() : Fl_Cocoa_Plugin(name()) { }
const char *name() FL_OVERRIDE { return "gl.cocoa.fltk.org"; }
void resize(Fl_Gl_Window *glw, int x, int y, int w, int h) FL_OVERRIDE {
glw->Fl_Gl_Window::resize(x, y, w, h);
}
};
static Fl_Gl_Cocoa_Plugin Gl_Cocoa_Plugin;
#endif