Simpler code to support FLTK widgets in macOS OpenGL 3 windows.

Also, the application-level code to add widgets to a GL3 window becomes
platform-independent.
This commit is contained in:
ManoloFLTK 2022-09-27 14:12:39 +02:00
parent 2ffd4e4f1a
commit 59fc60ea4c
11 changed files with 101 additions and 101 deletions

View File

@ -154,9 +154,6 @@ extern FLWindow *fl_mac_xid(const Fl_Window *win);
/** Returns the Fl_Window corresponding to the given macOS-specific window reference */
extern Fl_Window *fl_mac_find(FLWindow *);
class Fl_Gl_Window;
/** Call this to make possible the addition of FLTK widgets to a GL3-using Fl_Gl_Window.
\see \ref opengl3 */
extern Fl_Gl_Window *fl_mac_prepare_add_widgets_to_GL3_win(Fl_Gl_Window *);
/** The version number of the running Mac OS X (e.g., 100604 for 10.6.4, 101300 for 10.13).
FLTK initializes this global variable before main() begins running. If

View File

@ -605,36 +605,6 @@ to be created among your Fl_Gl_Window-derived classes:
\endcode
after the first glutCreateWindow() call.
\li If the GL3-using window is intended to contain FLTK widgets laid over
the GL3 scene (see \ref opengl_with_fltk_widgets), extra steps are necessary to make this possible in a
cross-platform way.
<ol><li>Create a function called, say, add_widgets(), charged of the creation
of all FLTK widgets expected to be drawn above the GL3 scene, as follows
\code
void add_widgets(Fl_Gl_Window *g) {
#ifdef __APPLE__
g = fl_mac_prepare_add_widgets_to_GL3_win(g);
#endif
g->begin();
// … Create here FLTK widgets expected to be drawn above the GL3 scene …
g->end();
}
\endcode
and call this function with the GL3-using window as argument to populate it
with FLTK widgets.
<li>
Put
\code
#ifndef __APPLE__
glUseProgram(0); // Switch from GL3-style to GL1-style drawing
Fl_Gl_Window::draw(); // Draw FLTK child widgets.
#endif
\endcode
at the end of your GL3 window's draw() function. This is not necessary if
the GL3 window is built by GLUT, because Fl_Glut_Window::draw() does it.
</ol>
If GLEW is installed on the Mac OS development platform, it is possible
to use the same code for all platforms, with one exception: put
\code

View File

@ -128,23 +128,17 @@ public:
glEnableVertexAttribArray((GLuint)colourAttribute );
glVertexAttribPointer((GLuint)positionAttribute, 4, GL_FLOAT, GL_FALSE, 8*sizeof(GLfloat), 0);
glVertexAttribPointer((GLuint)colourAttribute , 4, GL_FLOAT, GL_FALSE, 8*sizeof(GLfloat), (char*)0+4*sizeof(GLfloat));
glUseProgram(shaderProgram);
}
else if ((!valid())) {
glViewport(0, 0, pixel_w(), pixel_h());
}
glClearColor(0.08, 0.8, 0.8, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
GLfloat p[]={0,0};
glUniform2fv(positionUniform, 1, (const GLfloat *)&p);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
#ifndef __APPLE__
// suggested by https://stackoverflow.com/questions/22293870/mixing-fixed-function-pipeline-and-programmable-pipeline-in-opengl
// Switch from GL3-style to GL1-style drawing;
// good under Windows, X11 and Wayland; impossible under macOS.
glUseProgram(0);
Fl_Gl_Window::draw(); // Draw FLTK child widgets.
#endif
}
virtual int handle(int event) {
static int first = 1;
@ -233,9 +227,6 @@ void button_cb(Fl_Widget *, void *) {
}
void add_widgets(Fl_Gl_Window *g) {
#ifdef __APPLE__
g = fl_mac_prepare_add_widgets_to_GL3_win(g);
#endif
Fl::set_color(FL_FREE_COLOR, 255, 0, 0, 140); // partially transparent red
g->begin();
// Create here widgets to go above the GL3 scene

View File

@ -28,7 +28,10 @@
#include "Fl_Gl_Window_Driver.H"
#include <FL/gl_draw.H>
#include <stdlib.h>
#ifndef GL_CURRENT_PROGRAM
// from glew.h in Windows, glext.h in Unix, not used by FLTK's macOS platform
# define GL_CURRENT_PROGRAM 0x8B8D
#endif
GLContext *Fl_Gl_Window_Driver::context_list = 0;
int Fl_Gl_Window_Driver::nContext = 0;
static int NContext = 0;
@ -57,6 +60,23 @@ void Fl_Gl_Window_Driver::del_context(GLContext ctx) {
if (!nContext) gl_remove_displaylist_fonts();
}
Fl_Gl_Window_Driver::glUseProgram_type Fl_Gl_Window_Driver::glUseProgram_f = NULL;
void Fl_Gl_Window_Driver::switch_to_GL1() {
if (!glUseProgram_f) {
glUseProgram_f = (glUseProgram_type)GetProcAddress("glUseProgram");
}
glGetIntegerv(GL_CURRENT_PROGRAM, &current_prog);
// Switch from GL3-style to GL1-style drawing;
// good under Windows, X11 and Wayland; not appropriate under macOS.
// suggested by https://stackoverflow.com/questions/22293870/mixing-fixed-function-pipeline-and-programmable-pipeline-in-opengl
if (current_prog) glUseProgram_f(0);
}
void Fl_Gl_Window_Driver::switch_back() {
if (current_prog) glUseProgram_f((GLuint)current_prog);
}
Fl_Gl_Choice *Fl_Gl_Window_Driver::first;
// this assumes one of the two arguments is zero:

View File

@ -262,6 +262,7 @@ void Fl_Gl_Window::resize(int X,int Y,int W,int H) {
if (is_a_resize) valid(0);
pGlWindowDriver->resize(is_a_resize, W, H);
Fl_Window::resize(X,Y,W,H);
//pGlWindowDriver->resize(is_a_resize, W, H);//essai
}
/**
@ -343,6 +344,7 @@ void Fl_Gl_Window::draw_overlay() {}
\see \ref opengl_with_fltk_widgets
*/
void Fl_Gl_Window::draw_begin() {
if (mode() & FL_OPENGL3) pGlWindowDriver->switch_to_GL1();
Fl_Surface_Device::push_current( Fl_OpenGL_Display_Device::display_device() );
Fl_OpenGL_Graphics_Driver *drv = (Fl_OpenGL_Graphics_Driver*)Fl_Surface_Device::surface()->driver();
drv->pixels_per_unit_ = pixels_per_unit();
@ -391,6 +393,7 @@ void Fl_Gl_Window::draw_end() {
glPopAttrib(); // GL_ENABLE_BIT
Fl_Surface_Device::pop_current();
if (mode() & FL_OPENGL3) pGlWindowDriver->switch_back();
}
/** Draws the Fl_Gl_Window.

View File

@ -25,6 +25,7 @@
#define Fl_Gl_Window_Driver_H
#include <FL/Fl_Gl_Window.H>
#include <FL/gl.h> // for GLint
class Fl_Gl_Choice;
class Fl_Font_Descriptor;
@ -33,6 +34,10 @@ class Fl_Font_Descriptor;
platform-specific derived class from this class.
*/
class Fl_Gl_Window_Driver {
private:
GLint current_prog;
typedef void (*glUseProgram_type)(GLint);
static glUseProgram_type glUseProgram_f;
protected:
Fl_Gl_Window *pWindow;
public:
@ -53,7 +58,7 @@ public:
void* overlay() {return pWindow->overlay;}
void draw_overlay() {pWindow->draw_overlay();}
Fl_Gl_Window_Driver(Fl_Gl_Window *win) : pWindow(win) {}
Fl_Gl_Window_Driver(Fl_Gl_Window *win) : pWindow(win) {current_prog=0;}
virtual ~Fl_Gl_Window_Driver() {}
static Fl_Gl_Window_Driver *newGlWindowDriver(Fl_Gl_Window *w);
static Fl_Gl_Window_Driver *global();
@ -104,6 +109,8 @@ public:
// true means the platform uses glScissor() to make sure GL subwindows
// don't leak outside their parent window
virtual bool need_scissor() { return false; }
virtual void switch_to_GL1();
virtual void switch_back();
};
#endif /* Fl_Gl_Window_Driver_H */

View File

@ -2940,6 +2940,28 @@ NSOpenGLContext* Fl_Cocoa_Window_Driver::create_GLcontext_for_window(NSOpenGLPix
return context;
}
NSOpenGLContext *Fl_Cocoa_Window_Driver::gl1ctxt_create() {
FLView *view = (FLView*)[fl_xid(pWindow) contentView];
NSView *gl1view = [[NSView alloc] initWithFrame:[view frame]];
[view addSubview:gl1view];
[gl1view release];
NSOpenGLPixelFormat *gl1pixelformat =
Fl_Cocoa_Window_Driver::mode_to_NSOpenGLPixelFormat(
FL_RGB8 | FL_ALPHA | FL_SINGLE, NULL);
NSOpenGLContext *gl1ctxt = [[NSOpenGLContext alloc]
initWithFormat:gl1pixelformat shareContext:nil];
[gl1pixelformat release];
remove_gl_context_opacity(gl1ctxt);
[gl1ctxt setView:gl1view];
return gl1ctxt;
}
void Fl_Cocoa_Window_Driver::gl1ctxt_resize(NSOpenGLContext *ctxt) {
[[ctxt view] setFrame:[[[ctxt view] superview] frame]];
}
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_VERSION_12_0
# define NSOpenGLContextParameterSurfaceOpacity NSOpenGLCPSurfaceOpacity
#endif

View File

@ -20,10 +20,17 @@
#include "../../Fl_Gl_Window_Driver.H"
class Fl_Gl_Choice;
#ifdef __OBJC__
@class NSOpenGLContext;
#else
class NSOpenGLContext;
#endif
class Fl_Cocoa_Gl_Window_Driver : public Fl_Gl_Window_Driver {
NSOpenGLContext *gl1ctxt; // GL1 context in addition to GL3 context
friend Fl_Gl_Window_Driver* Fl_Gl_Window_Driver::newGlWindowDriver(Fl_Gl_Window *);
Fl_Cocoa_Gl_Window_Driver(Fl_Gl_Window *win) : Fl_Gl_Window_Driver(win) {}
Fl_Cocoa_Gl_Window_Driver(Fl_Gl_Window *win);
~Fl_Cocoa_Gl_Window_Driver();
virtual float pixels_per_unit();
virtual void before_show(int& need_after);
virtual void after_show();
@ -44,6 +51,8 @@ class Fl_Cocoa_Gl_Window_Driver : public Fl_Gl_Window_Driver {
virtual bool need_scissor() { return true; }
virtual void* GetProcAddress(const char *procName);
void apply_scissor();
virtual void switch_to_GL1();
virtual void switch_back();
};

View File

@ -48,6 +48,15 @@ public:
};
Fl_Cocoa_Gl_Window_Driver::Fl_Cocoa_Gl_Window_Driver(Fl_Gl_Window *win) :
Fl_Gl_Window_Driver(win) {
gl1ctxt = NULL;
}
Fl_Cocoa_Gl_Window_Driver::~Fl_Cocoa_Gl_Window_Driver() {
if (gl1ctxt) Fl_Cocoa_Window_Driver::GLcontext_release(gl1ctxt);
}
Fl_Gl_Choice *Fl_Cocoa_Gl_Window_Driver::find(int m, const int *alistp)
{
Fl::screen_driver()->open_display(); // useful when called through gl_start()
@ -188,9 +197,18 @@ void Fl_Cocoa_Gl_Window_Driver::swap_buffers() {
char Fl_Cocoa_Gl_Window_Driver::swap_type() {return copy;}
static void delayed_redraw(Fl_Gl_Window *win) {
win->redraw();
}
void Fl_Cocoa_Gl_Window_Driver::resize(int is_a_resize, int w, int h) {
if (pWindow->shown()) apply_scissor();
Fl_Cocoa_Window_Driver::GLcontext_update((NSOpenGLContext*)pWindow->context());
if (gl1ctxt) {
Fl_Cocoa_Window_Driver::gl1ctxt_resize(gl1ctxt);
Fl_Cocoa_Window_Driver::GLcontext_update(gl1ctxt);
Fl::add_timeout(0.01, (Fl_Timeout_Handler)delayed_redraw, pWindow);
}
}
void Fl_Cocoa_Gl_Window_Driver::apply_scissor() {
@ -304,44 +322,26 @@ FL_EXPORT NSOpenGLContext *fl_mac_glcontext(GLContext rc) {
}
/* macOS offers only core contexts when using GL3. This forbids to add
FLTK widgets to a GL3-using Fl_Gl_Window because these widgets are drawn
/* macOS offers only core contexts when using GL3. This forbids to draw
FLTK widgets in a GL3-using NSOpenGLContext because these widgets are drawn
with the GL1-based Fl_OpenGL_Graphics_Driver. The solution implemented here
is to create, with public function fl_mac_prepare_add_widgets_to_GL3_win(),
an additional Fl_Gl_Window placed above and sized as the GL3-based window,
to give it a non opaque, GL1-based context, and to put the FLTK widgets
in that additional window.
is to create an additional NSView and an associated additional NSOpenGLContext
placed above and sized as the GL3-based window, to set the new NSOpenGLContext
non opaque and GL1-based, and to draw the FLTK widgets in the new
view/GL context.
*/
class transparentGlWindow : public Fl_Gl_Window { // utility class
bool need_remove_opacity;
public:
transparentGlWindow(int x, int y, int w, int h) : Fl_Gl_Window(x, y, w, h) {
mode(FL_RGB8 | FL_ALPHA | FL_SINGLE);
need_remove_opacity = true;
void Fl_Cocoa_Gl_Window_Driver::switch_to_GL1() {
if (!gl1ctxt) {
gl1ctxt = Fl_Cocoa_Window_Driver::driver(pWindow)->gl1ctxt_create();
Fl::add_timeout(0.01, (Fl_Timeout_Handler)delayed_redraw, pWindow);
}
void show() {
Fl_Gl_Window::show();
if (need_remove_opacity) {
need_remove_opacity = false;
Fl_Cocoa_Window_Driver *d = Fl_Cocoa_Window_Driver::driver(this);
d->remove_gl_context_opacity((NSOpenGLContext*)context());
}
}
};
Fl_Cocoa_Window_Driver::GLcontext_makecurrent(gl1ctxt);
}
Fl_Gl_Window *fl_mac_prepare_add_widgets_to_GL3_win(Fl_Gl_Window *gl3win) {
gl3win->begin();
transparentGlWindow *transp = new transparentGlWindow(0, 0,
gl3win->w(), gl3win->h());
gl3win->end();
if (!gl3win->resizable()) gl3win->resizable(gl3win);
if (gl3win->shown()) {
transp->show();
gl3win->make_current();
}
return transp;
void Fl_Cocoa_Gl_Window_Driver::switch_back() {
glFlush();
Fl_Cocoa_Window_Driver::GLcontext_makecurrent((NSOpenGLContext*)pWindow->context());
}

View File

@ -155,6 +155,8 @@ public:
static void GL_cleardrawable(void); // uses Objective-c
static void gl_start(NSOpenGLContext*); // uses Objective-c
static void remove_gl_context_opacity(NSOpenGLContext*); // uses Objective-c
NSOpenGLContext *gl1ctxt_create(); // uses Objective-c
static void gl1ctxt_resize(NSOpenGLContext*); // uses Objective-c
//icons
virtual void icons(const Fl_RGB_Image *icons[], int count);

View File

@ -30,9 +30,6 @@
# include "Fl_Screen_Driver.H"
# include <FL/glut.H>
# define MAXWINDOWS 32
# ifndef GL_CURRENT_PROGRAM
# define GL_CURRENT_PROGRAM 0x8B8D // from glew.h
# endif
static Fl_Glut_Window *windows[MAXWINDOWS+1];
@ -58,25 +55,7 @@ void Fl_Glut_Window::draw() {
if (!valid()) {reshape(pixel_w(),pixel_h()); valid(1);}
display();
if (children()) {
if ((mode() & FL_OPENGL3)) {
#ifndef __APPLE__
typedef void (*glUseProgram_type)(GLint);
static glUseProgram_type glUseProgram_f = NULL;
if (!glUseProgram_f) {
Fl_Gl_Window_Driver *dr = Fl_Gl_Window_Driver::driver(this);
glUseProgram_f = (glUseProgram_type)dr->GetProcAddress("glUseProgram");
}
GLint current_prog = 0;
glGetIntegerv(GL_CURRENT_PROGRAM, &current_prog);
// Switch from GL3-style to GL1-style drawing;
// good under Windows, X11 and Wayland; impossible under macOS.
glUseProgram_f(0);
// Draw FLTK child widgets
Fl_Gl_Window::draw();
// Switch back to GL3-style drawing
glUseProgram_f((GLuint)current_prog);
#endif // ! __APPLE__
} else Fl_Gl_Window::draw(); // Draw FLTK child widgets
Fl_Gl_Window::draw(); // Draw FLTK child widgets
}
indraw = 0;
}