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
-3
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 */ /** Returns the Fl_Window corresponding to the given macOS-specific window reference */
extern Fl_Window *fl_mac_find(FLWindow *); extern Fl_Window *fl_mac_find(FLWindow *);
class Fl_Gl_Window; 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). /** 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 FLTK initializes this global variable before main() begins running. If
-30
View File
@@ -605,36 +605,6 @@ to be created among your Fl_Gl_Window-derived classes:
\endcode \endcode
after the first glutCreateWindow() call. 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 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 to use the same code for all platforms, with one exception: put
\code \code
+1 -10
View File
@@ -128,23 +128,17 @@ public:
glEnableVertexAttribArray((GLuint)colourAttribute ); glEnableVertexAttribArray((GLuint)colourAttribute );
glVertexAttribPointer((GLuint)positionAttribute, 4, GL_FLOAT, GL_FALSE, 8*sizeof(GLfloat), 0); 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)); glVertexAttribPointer((GLuint)colourAttribute , 4, GL_FLOAT, GL_FALSE, 8*sizeof(GLfloat), (char*)0+4*sizeof(GLfloat));
glUseProgram(shaderProgram);
} }
else if ((!valid())) { else if ((!valid())) {
glViewport(0, 0, pixel_w(), pixel_h()); glViewport(0, 0, pixel_w(), pixel_h());
} }
glClearColor(0.08, 0.8, 0.8, 1.0); glClearColor(0.08, 0.8, 0.8, 1.0);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
GLfloat p[]={0,0}; GLfloat p[]={0,0};
glUniform2fv(positionUniform, 1, (const GLfloat *)&p); glUniform2fv(positionUniform, 1, (const GLfloat *)&p);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4); 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. Fl_Gl_Window::draw(); // Draw FLTK child widgets.
#endif
} }
virtual int handle(int event) { virtual int handle(int event) {
static int first = 1; static int first = 1;
@@ -233,9 +227,6 @@ void button_cb(Fl_Widget *, void *) {
} }
void add_widgets(Fl_Gl_Window *g) { 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 Fl::set_color(FL_FREE_COLOR, 255, 0, 0, 140); // partially transparent red
g->begin(); g->begin();
// Create here widgets to go above the GL3 scene // Create here widgets to go above the GL3 scene
+21 -1
View File
@@ -28,7 +28,10 @@
#include "Fl_Gl_Window_Driver.H" #include "Fl_Gl_Window_Driver.H"
#include <FL/gl_draw.H> #include <FL/gl_draw.H>
#include <stdlib.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; GLContext *Fl_Gl_Window_Driver::context_list = 0;
int Fl_Gl_Window_Driver::nContext = 0; int Fl_Gl_Window_Driver::nContext = 0;
static int 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(); 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; Fl_Gl_Choice *Fl_Gl_Window_Driver::first;
// this assumes one of the two arguments is zero: // this assumes one of the two arguments is zero:
+3
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); if (is_a_resize) valid(0);
pGlWindowDriver->resize(is_a_resize, W, H); pGlWindowDriver->resize(is_a_resize, W, H);
Fl_Window::resize(X,Y,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 \see \ref opengl_with_fltk_widgets
*/ */
void Fl_Gl_Window::draw_begin() { 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_Surface_Device::push_current( Fl_OpenGL_Display_Device::display_device() );
Fl_OpenGL_Graphics_Driver *drv = (Fl_OpenGL_Graphics_Driver*)Fl_Surface_Device::surface()->driver(); Fl_OpenGL_Graphics_Driver *drv = (Fl_OpenGL_Graphics_Driver*)Fl_Surface_Device::surface()->driver();
drv->pixels_per_unit_ = pixels_per_unit(); drv->pixels_per_unit_ = pixels_per_unit();
@@ -391,6 +393,7 @@ void Fl_Gl_Window::draw_end() {
glPopAttrib(); // GL_ENABLE_BIT glPopAttrib(); // GL_ENABLE_BIT
Fl_Surface_Device::pop_current(); Fl_Surface_Device::pop_current();
if (mode() & FL_OPENGL3) pGlWindowDriver->switch_back();
} }
/** Draws the Fl_Gl_Window. /** Draws the Fl_Gl_Window.
+8 -1
View File
@@ -25,6 +25,7 @@
#define Fl_Gl_Window_Driver_H #define Fl_Gl_Window_Driver_H
#include <FL/Fl_Gl_Window.H> #include <FL/Fl_Gl_Window.H>
#include <FL/gl.h> // for GLint
class Fl_Gl_Choice; class Fl_Gl_Choice;
class Fl_Font_Descriptor; class Fl_Font_Descriptor;
@@ -33,6 +34,10 @@ class Fl_Font_Descriptor;
platform-specific derived class from this class. platform-specific derived class from this class.
*/ */
class Fl_Gl_Window_Driver { class Fl_Gl_Window_Driver {
private:
GLint current_prog;
typedef void (*glUseProgram_type)(GLint);
static glUseProgram_type glUseProgram_f;
protected: protected:
Fl_Gl_Window *pWindow; Fl_Gl_Window *pWindow;
public: public:
@@ -53,7 +58,7 @@ public:
void* overlay() {return pWindow->overlay;} void* overlay() {return pWindow->overlay;}
void draw_overlay() {pWindow->draw_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() {} virtual ~Fl_Gl_Window_Driver() {}
static Fl_Gl_Window_Driver *newGlWindowDriver(Fl_Gl_Window *w); static Fl_Gl_Window_Driver *newGlWindowDriver(Fl_Gl_Window *w);
static Fl_Gl_Window_Driver *global(); static Fl_Gl_Window_Driver *global();
@@ -104,6 +109,8 @@ public:
// true means the platform uses glScissor() to make sure GL subwindows // true means the platform uses glScissor() to make sure GL subwindows
// don't leak outside their parent window // don't leak outside their parent window
virtual bool need_scissor() { return false; } virtual bool need_scissor() { return false; }
virtual void switch_to_GL1();
virtual void switch_back();
}; };
#endif /* Fl_Gl_Window_Driver_H */ #endif /* Fl_Gl_Window_Driver_H */
+22
View File
@@ -2940,6 +2940,28 @@ NSOpenGLContext* Fl_Cocoa_Window_Driver::create_GLcontext_for_window(NSOpenGLPix
return context; 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 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_VERSION_12_0
# define NSOpenGLContextParameterSurfaceOpacity NSOpenGLCPSurfaceOpacity # define NSOpenGLContextParameterSurfaceOpacity NSOpenGLCPSurfaceOpacity
#endif #endif
+10 -1
View File
@@ -20,10 +20,17 @@
#include "../../Fl_Gl_Window_Driver.H" #include "../../Fl_Gl_Window_Driver.H"
class Fl_Gl_Choice; class Fl_Gl_Choice;
#ifdef __OBJC__
@class NSOpenGLContext;
#else
class NSOpenGLContext;
#endif
class Fl_Cocoa_Gl_Window_Driver : public Fl_Gl_Window_Driver { 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 *); 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 float pixels_per_unit();
virtual void before_show(int& need_after); virtual void before_show(int& need_after);
virtual void after_show(); 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 bool need_scissor() { return true; }
virtual void* GetProcAddress(const char *procName); virtual void* GetProcAddress(const char *procName);
void apply_scissor(); void apply_scissor();
virtual void switch_to_GL1();
virtual void switch_back();
}; };
+33 -33
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_Gl_Choice *Fl_Cocoa_Gl_Window_Driver::find(int m, const int *alistp)
{ {
Fl::screen_driver()->open_display(); // useful when called through gl_start() 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;} 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) { void Fl_Cocoa_Gl_Window_Driver::resize(int is_a_resize, int w, int h) {
if (pWindow->shown()) apply_scissor(); if (pWindow->shown()) apply_scissor();
Fl_Cocoa_Window_Driver::GLcontext_update((NSOpenGLContext*)pWindow->context()); 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() { 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 /* macOS offers only core contexts when using GL3. This forbids to draw
FLTK widgets to a GL3-using Fl_Gl_Window because these widgets are drawn FLTK widgets in a GL3-using NSOpenGLContext because these widgets are drawn
with the GL1-based Fl_OpenGL_Graphics_Driver. The solution implemented here 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(), is to create an additional NSView and an associated additional NSOpenGLContext
an additional Fl_Gl_Window placed above and sized as the GL3-based window, placed above and sized as the GL3-based window, to set the new NSOpenGLContext
to give it a non opaque, GL1-based context, and to put the FLTK widgets non opaque and GL1-based, and to draw the FLTK widgets in the new
in that additional window. view/GL context.
*/ */
class transparentGlWindow : public Fl_Gl_Window { // utility class void Fl_Cocoa_Gl_Window_Driver::switch_to_GL1() {
bool need_remove_opacity; if (!gl1ctxt) {
public: gl1ctxt = Fl_Cocoa_Window_Driver::driver(pWindow)->gl1ctxt_create();
transparentGlWindow(int x, int y, int w, int h) : Fl_Gl_Window(x, y, w, h) { Fl::add_timeout(0.01, (Fl_Timeout_Handler)delayed_redraw, pWindow);
mode(FL_RGB8 | FL_ALPHA | FL_SINGLE);
need_remove_opacity = true;
} }
void show() { Fl_Cocoa_Window_Driver::GLcontext_makecurrent(gl1ctxt);
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());
}
}
};
void Fl_Cocoa_Gl_Window_Driver::switch_back() {
Fl_Gl_Window *fl_mac_prepare_add_widgets_to_GL3_win(Fl_Gl_Window *gl3win) { glFlush();
gl3win->begin(); Fl_Cocoa_Window_Driver::GLcontext_makecurrent((NSOpenGLContext*)pWindow->context());
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;
} }
@@ -155,6 +155,8 @@ public:
static void GL_cleardrawable(void); // uses Objective-c static void GL_cleardrawable(void); // uses Objective-c
static void gl_start(NSOpenGLContext*); // uses Objective-c static void gl_start(NSOpenGLContext*); // uses Objective-c
static void remove_gl_context_opacity(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 //icons
virtual void icons(const Fl_RGB_Image *icons[], int count); virtual void icons(const Fl_RGB_Image *icons[], int count);
+1 -22
View File
@@ -30,9 +30,6 @@
# include "Fl_Screen_Driver.H" # include "Fl_Screen_Driver.H"
# include <FL/glut.H> # include <FL/glut.H>
# define MAXWINDOWS 32 # define MAXWINDOWS 32
# ifndef GL_CURRENT_PROGRAM
# define GL_CURRENT_PROGRAM 0x8B8D // from glew.h
# endif
static Fl_Glut_Window *windows[MAXWINDOWS+1]; 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);} if (!valid()) {reshape(pixel_w(),pixel_h()); valid(1);}
display(); display();
if (children()) { if (children()) {
if ((mode() & FL_OPENGL3)) { Fl_Gl_Window::draw(); // Draw FLTK child widgets
#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
} }
indraw = 0; indraw = 0;
} }