458 lines
16 KiB
Plaintext
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
|