Files
2025-12-05 08:54:02 +01:00

458 lines
16 KiB
Plaintext

// Copyright (C) 2010, Guy Barrand. All rights reserved.
// See the file tools.license for terms.
#ifndef toolx_Xt_OpenGLArea
#define toolx_Xt_OpenGLArea
#include <X11/IntrinsicP.h>
#include <X11/CoreP.h>
#include <X11/CompositeP.h>
#include <X11/StringDefs.h>
#ifndef GL_GLEXT_LEGACY
#define GL_GLEXT_LEGACY
#endif
#include <GL/gl.h>
#ifndef GL_GLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES
#endif
#include <GL/glext.h>
#include <GL/glx.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
namespace toolx {
namespace Xt {
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
typedef struct _OpenGLAreaClassRec* OpenGLAreaWidgetClass;
typedef struct _OpenGLAreaRec* OpenGLAreaWidget;
typedef struct {
int reason;
XEvent* event;
} XoAnyCallbackStruct;
#define XoCR_PAINT 1
#define XoCR_EVENT 2
#define XoNdoubleBufferOn toolx::Xt::OpenGLArea::XoN_doubleBufferOn()
#define XoNpaintCallback toolx::Xt::OpenGLArea::XoN_paintCallback()
#define XoNeventCallback toolx::Xt::OpenGLArea::XoN_eventCallback()
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
typedef struct {
void* extension;
} OpenGLAreaClassPart;
typedef struct _OpenGLAreaClassRec {
CoreClassPart core_class;
CompositeClassPart composite_class;
OpenGLAreaClassPart openGLArea_class;
} OpenGLAreaClassRec;
typedef struct {
Boolean doubleBufferOn;
XtCallbackList paintCallback;
XtCallbackList eventCallback;
Visual* visual;
Boolean installColormap;
GLXContext glContext;
} OpenGLAreaPart;
typedef struct _OpenGLAreaRec {
CorePart core;
CompositePart composite;
OpenGLAreaPart openGLArea;
} OpenGLAreaRec;
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
class OpenGLArea {
protected:
static const char* class_name() {static const char* s_s = "OpenGLArea";return s_s;}
static const char* XoC_DoubleBufferOn() {static const char* s_s = "DoubleBufferOn";return s_s;}
public:
static const char* XoN_doubleBufferOn() {static const char* s_s = "doubleBufferOn";return s_s;}
static const char* XoN_paintCallback() {static const char* s_s = "paintCallback";return s_s;}
static const char* XoN_eventCallback() {static const char* s_s = "eventCallback";return s_s;}
public:
static void paint(Widget a_this) {
if(!XtIsRealized(a_this)) return;
if(make_current(a_this)==1) {
XoAnyCallbackStruct value;
value.reason = XoCR_PAINT;
value.event = 0;
::XtCallCallbacks(a_this,XoNpaintCallback,(XtPointer)&value);
::glXSwapBuffers(XtDisplay(a_this),XtWindow(a_this));
::glXMakeCurrent(XtDisplay(a_this),None,NULL);
}
}
protected:
static void initialize_class(void) {}
static void initialize_widget(Widget a_request,Widget a_this,ArgList,Cardinal*) {
if(a_request->core.width<=0) a_this->core.width = 100;
if(a_request->core.height<=0) a_this->core.height = 100;
OpenGLAreaPart& athis = _athis(a_this);
athis.visual = CopyFromParent;
athis.installColormap = False;
athis.glContext = NULL;
Display* display;
Screen* screen;
int iscreen;
XVisualInfo* vinfo;
display = XtDisplay(a_this);
screen = XtScreen(a_this);
iscreen = XScreenNumberOfScreen(screen);
{int error,event;
if(::glXQueryExtension(display,&error,&event)==0) {
CWarn ("X server does not have OpenGL extensions.\n");
}}
if(athis.doubleBufferOn==True) {
int atbs_1[] = {
GLX_RGBA,
GLX_RED_SIZE, 1,
GLX_GREEN_SIZE, 1,
GLX_BLUE_SIZE, 1,
GLX_ALPHA_SIZE, 1,
GLX_DEPTH_SIZE, 1,
GLX_DOUBLEBUFFER,
None
};
vinfo = ::glXChooseVisual (display,iscreen,atbs_1);
if(vinfo==NULL) { //GLX_ALPHA_SIZE : problem with Xming. Try without it.
int atbs_2[] = {
GLX_RGBA,
GLX_RED_SIZE, 1,
GLX_GREEN_SIZE, 1,
GLX_BLUE_SIZE, 1,
GLX_DEPTH_SIZE, 1,
GLX_DOUBLEBUFFER,
None
};
vinfo = ::glXChooseVisual (display,iscreen,atbs_2);
}
} else {
int atbs_1[] = {
GLX_RGBA,
GLX_RED_SIZE, 1,
GLX_GREEN_SIZE, 1,
GLX_BLUE_SIZE, 1,
GLX_ALPHA_SIZE, 1,
GLX_DEPTH_SIZE, 1,
None
};
vinfo = ::glXChooseVisual (display,iscreen,atbs_1);
if(vinfo==NULL) { //GLX_ALPHA_SIZE : problem with Xming. Try without it.
int atbs_2[] = {
GLX_RGBA,
GLX_RED_SIZE, 1,
GLX_GREEN_SIZE, 1,
GLX_BLUE_SIZE, 1,
GLX_DEPTH_SIZE, 1,
None
};
vinfo = ::glXChooseVisual (display,iscreen,atbs_2);
}
}
if(vinfo==NULL) {
CWarn ("Can't choose a visual.\n");
} else {
a_this->core.depth = vinfo->depth;
athis.visual = vinfo->visual;
if( (vinfo->depth ==DefaultDepth (display,iscreen)) &&
(vinfo->visual==DefaultVisual(display,iscreen)) ) {
a_this->core.colormap = XDefaultColormap (display,iscreen);
athis.installColormap = False;
} else {
a_this->core.colormap =
XCreateColormap (display,XRootWindow(display,iscreen),vinfo->visual, AllocNone);
athis.installColormap = True;
}
if(a_this->core.colormap==0L) {
CWarn ("Can't get/create a X colormap.\n");
}
athis.glContext = ::glXCreateContext (display,vinfo,NULL,GL_TRUE);
if(athis.glContext==NULL) {
athis.glContext = ::glXCreateContext (display,vinfo,NULL,GL_FALSE);
if(athis.glContext==NULL) {
CWarn ("Can't create a GLX context.\n");
}
}
::XFree(vinfo);
}
::XtAddEventHandler
(a_this,ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|KeyPressMask|KeyReleaseMask,0,event_handler,NULL);
}
static void realize_widget(Widget a_this,XtValueMask* a_mask,XSetWindowAttributes* a_watbs) {
// Have to create window ourselves due to OpenGL that compells it's visual.
// In principle colormap is correctly set in a_watbsy.
OpenGLAreaPart& athis = _athis(a_this);
::XtCreateWindow(a_this,(unsigned int)InputOutput,athis.visual,*a_mask,a_watbs);
// Call the Realize procedure (XtInheritRealize) :
if(widget_class()->core_class.superclass->core_class.realize!=NULL)
(widget_class()->core_class.superclass->core_class.realize)(a_this, a_mask, a_watbs);
// If window is delete, all seems ok :
if(athis.installColormap==True) install_colormap(a_this);
}
static void destroy_widget(Widget a_this) {
OpenGLAreaPart& athis = _athis(a_this);
if(athis.installColormap==True) {
uninstall_colormap (a_this);
athis.installColormap = False;
::XFreeColormap(XtDisplay(a_this),a_this->core.colormap);
}
if(athis.glContext!=NULL) {
::glXMakeCurrent(XtDisplay(a_this),None,NULL);
::glXDestroyContext(XtDisplay(a_this),athis.glContext);
athis.glContext = NULL;
}
}
static Boolean set_values(Widget a_current,Widget,Widget a_this,ArgList,Cardinal*) {
OpenGLAreaPart& athis = _athis(a_this);
if(athis.doubleBufferOn != ((OpenGLAreaWidget)a_current)->openGLArea.doubleBufferOn) {
// Can't change buffering here if X window is created.
// With OpenGL, buffering fix parameter of the X window.
// Buffering must be choosen before the execution of the
// Realize method that create the window.
if(XtIsRealized(a_this) && (athis.installColormap==True)) {
CWarn("Can't change buffering after \"realization\" of the widget.\n");
athis.doubleBufferOn = ((OpenGLAreaWidget)a_current)->openGLArea.doubleBufferOn;
}
}
return False;
}
static void change_widget_size(Widget a_this) {
// Call the Resize procedure (XtInheritResize) :
if(widget_class()->core_class.superclass->core_class.resize!=NULL)
(widget_class()->core_class.superclass->core_class.resize)(a_this);
}
static void draw_widget(Widget a_this,XEvent* a_event,Region a_region) {
if(widget_class()->core_class.superclass->core_class.expose!=NULL)
(widget_class()->core_class.superclass->core_class.expose)(a_this,a_event,a_region);
if(make_current(a_this)==1) {
XoAnyCallbackStruct value;
value.reason = XoCR_PAINT;
value.event = a_event;
::XtCallCallbacks(a_this,XoNpaintCallback,(XtPointer)&value);
::glXSwapBuffers(XtDisplay(a_this),XtWindow(a_this));
::glXMakeCurrent(XtDisplay(a_this),None,NULL);
}
}
protected:
static OpenGLAreaPart& _athis(Widget a_this) {return *(&(((OpenGLAreaWidget)a_this)->openGLArea));}
static void CWarn(const char* a_msg) {
::printf("%s",a_msg);
}
static void CWarnF(const char* a_format,...) {
va_list args;
va_start(args,a_format);
::vprintf(a_format,args);
va_end(args);
}
static int make_current(Widget a_this) {
if(!XtIsRealized(a_this)) return 0;
OpenGLAreaPart& athis = _athis(a_this);
if(athis.glContext==NULL) return 0;
return (int)::glXMakeCurrent(XtDisplay(a_this),XtWindow(a_this),athis.glContext);
}
static void event_handler(Widget a_this,XtPointer,XEvent* a_event ,Boolean*) {
XoAnyCallbackStruct value;
value.reason = XoCR_EVENT;
value.event = a_event;
::XtCallCallbacks(a_this,XoNeventCallback,(XtPointer)&value);
}
static void install_colormap(Widget a_this) {
// From Mesa/widgets/GLwDrawingArea.c/post_colormap routine.
// Could use also XtSetWMColormapWindows.
if( !XtIsWidget(a_this) || !XtIsRealized(a_this) ) return;
Widget shell = get_shell(a_this);
if(shell==NULL) return;
Display* display = XtDisplay (a_this);
Window wthis = XtWindow (a_this);
Window wshell = XtWindow (shell);
Window* ws = NULL;
int wn = 0;
::XGetWMColormapWindows (display,wshell, &ws, &wn);
// Check if colormap of this is a colormap of a Window in list :
XWindowAttributes watbs;
XGetWindowAttributes (display,wthis,&watbs);
Colormap cmapthis = watbs.colormap;
int found = -1;
for(int count=0;count<wn;count++) {
Colormap cmap;
XGetWindowAttributes (display,ws[count],&watbs);
cmap = watbs.colormap;
if(cmap==cmapthis) {
::XFree (ws);
return;
}
if(ws[count]==wshell) {
found = count;
}
}
// Have to add window of this in list :
if(wn==0) {
if(ws!=NULL) ::XFree(ws);
ws = (Window*)::malloc( 2 * sizeof(Window));
} else {
ws = (Window*)::realloc(ws,(wn + 2) * sizeof(Window));
}
if(ws==NULL) return;
if(found==-1) {
// Window of shell not in list :
ws[wn] = wthis; wn++;
ws[wn] = wshell;wn++;
} else {
ws[found] = wthis;
ws[wn] = wshell; wn++; // Shell must be last.
}
if (::XSetWMColormapWindows(display,wshell, ws, wn)==0) {
CWarnF ("install_colormap: can't install colormap of %s in %s.\n",XtName(a_this),XtName(shell));
}
::free(ws);
}
static void uninstall_colormap(Widget a_this) {
if( !XtIsWidget(a_this) || !XtIsRealized(a_this) ) return;
Widget shell = get_shell (a_this);
if(shell==NULL) return;
Display* display = XtDisplay (a_this);
Window wthis = XtWindow (a_this);
Window wshell = XtWindow (shell);
Window* ws = NULL;
int wn = 0;
::XGetWMColormapWindows (display,wshell, &ws, &wn);
if( (wn==0) || (ws==NULL) ) return;
Window* nws = (Window*)::malloc( wn * sizeof(Window));
if(nws==NULL) {
::XFree (ws);
return;
}
int nwn = 0;
for(int count=0;count<wn;count++) {
if(ws[count]!=wthis) {
nws[nwn] = ws[count];
nwn++;
}
}
if(wn!=nwn) {
if (::XSetWMColormapWindows(display,wshell, nws, nwn)==0) {
CWarnF("uninstall_colormap: can't install colormap of %s in %s.\n",XtName(a_this),XtName(shell));
}
}
::XFree (ws);
::free (nws);
}
static Widget get_shell(Widget a_this) {
Widget widget = a_this;
while(1) {
if(widget==NULL) return NULL;
if(XtIsShell(widget)) return widget;
widget = XtParent(widget);
}
}
public:
static WidgetClass widget_class() {
static XtResource s_resources[] = {
{(String)XoN_doubleBufferOn(),(String)XoC_DoubleBufferOn(),XtRBoolean,sizeof(Boolean),
XtOffset(OpenGLAreaWidget,openGLArea.doubleBufferOn),XtRImmediate,(XtPointer)True},
{(String)XoN_paintCallback(),XtCCallback,XtRCallback,sizeof(XtCallbackList),
XtOffset(OpenGLAreaWidget,openGLArea.paintCallback),XtRImmediate,(XtPointer)NULL},
{(String)XoN_eventCallback(),XtCCallback,XtRCallback,sizeof(XtCallbackList),
XtOffset(OpenGLAreaWidget,openGLArea.eventCallback),XtRImmediate,(XtPointer)NULL}
};
static OpenGLAreaClassRec s_openGLAreaClassRec = {
// Core Class Part :
{
(WidgetClass) &compositeClassRec, // pointer to superclass ClassRec
(String)class_name(), // widget resource class name
sizeof(OpenGLAreaRec), // size in bytes of widget record
initialize_class, // class_initialize
NULL, // dynamic initialization
FALSE, // has class been initialized?
initialize_widget, // initialize
NULL, // notify that initialize called
realize_widget, // XCreateWindow for widget
NULL, // widget semantics name to proc mapWidget
0, // number of entries in actions
s_resources, // resources for subclass fields
XtNumber(s_resources), // number of entries in resources
NULLQUARK, // resource class quarkified
TRUE, // compress MotionNotify for widget
TRUE, // compress Expose events for widget
TRUE, // compress enter and leave events
TRUE, // select for VisibilityNotify
destroy_widget, // free data for subclass pointers
change_widget_size, // geom manager changed widget size
draw_widget, // rediplay window
set_values, // set subclass resource values
NULL, // notify that SetValues called
XtInheritSetValuesAlmost, // SetValues got "Almost" geo reply
NULL, // notify that get_values called
XtInheritAcceptFocus, // assign input focus to widget
XtVersion, // version of intrinsics used
NULL, // list of callback offsets
XtInheritTranslations, // translations
XtInheritQueryGeometry, // return preferred geometry
XtInheritDisplayAccelerator, // display your accelerator
NULL // pointer to extension record
},
// Composite Class Part :
{
XtInheritGeometryManager, // geometry manager for children
XtInheritChangeManaged, // change managed state of child
XtInheritInsertChild, // physically add child to parent
XtInheritDeleteChild, // physically remove child
NULL // pointer to extension record
},
// OpenGLArea :
{
NULL
}
};
return (WidgetClass)&s_openGLAreaClassRec;
}
};
}}
#endif