/* Auxiliary functions for making simple OpenGL programs */ #include #include #include #include #include #include #include #include #include "pgl.h" #include typedef struct { int doneInit; GLXContext context; Widget widget; Widget parent; Widget shell; Widget menu; pglCallback* initCB; pglCallback* exposeCB; pglCallback* resizeCB; pglCallback* inputCB; pglCallback* idleCB; pglCallback* timerCB; unsigned long timeo; /* milliseconds */ XtIntervalId timid; XtWorkProcId wpid; } pglContext; static pglContext* ctxt; static XtAppContext xcontext; static Widget xwidget = NULL; static int gwinid = 0; /* next unallocated window index */ static int nslots = 8; /* current size of ctxt array */ static int pglDoOpen(Widget parent, char* name, pglCallback initRoutine, ArgList xargs, int xn); static void QCB(Widget w, XtPointer client_data, XtPointer call_data); static void initCB(Widget w, XtPointer client_data, XtPointer call_data); static void exposeCB(Widget w, XtPointer client_data, XtPointer call_data); static void resizeCB(Widget w, XtPointer client_data, XtPointer call_data); static void inputCB(Widget w, XtPointer client_data, XtPointer call_data); static void destroyCB(Widget w, XtPointer client_data, XtPointer call_data); static void timerCB(XtPointer client_data, XtIntervalId* tid); static Boolean idleCB(XtPointer client_data); static pglCallback stdResize; static void pglMenuPost(Widget w, XtPointer client, XEvent *event, Boolean *dispatch); static String pglResources[] = { "*pglWindow.geometry: 200x200", "*pglMenuShell*background: Gray80", NULL }; /******************* IMPORTANT routines *************/ Widget pglInitialize(char* appClassName, char** userResources) { Arg xargs[10]; int xn = 0; int n = 1; String* res; if ( xwidget ) return xwidget; /* decide on a set of fallback resources */ if ( userResources ) { int nres = 0; String* rp; /* count the resources passed in by user */ while ( userResources[nres] ) nres++; /* build a big resource list */ res = (String*)malloc(nres*sizeof(String) + sizeof(pglResources)); nres = 0; for ( rp = pglResources; *rp; rp++ ) res[nres++] = *rp; for ( rp = userResources; *rp; rp++ ) res[nres++] = *rp; res[nres] = NULL; } else { res = pglResources; } /* Do X11 initialization */ XtSetArg(xargs[xn], XmNmappedWhenManaged, False); xn++; xwidget = XtAppInitialize(&xcontext, appClassName, NULL, 0, &n, &appClassName, res, xargs, xn); ctxt = (pglContext*) malloc(nslots*sizeof(pglContext)); free(res); return xwidget; } void pglHandleEvents(void) { XtInputMask m; while ( (m=XtAppPending(xcontext)) != 0 ) { XtAppProcessEvent(xcontext, m); } } void pglMainLoop(void) { XtAppMainLoop(xcontext); } int pglOpenWindow(char* title, pglCallback initRoutine, int flags) { Atom DELETE_ATOM; Widget shell; Arg xargs[10]; int xn = 0; XtSetArg(xargs[xn], XmNtitle, title); xn++; shell = XtCreatePopupShell("pglWindow", topLevelShellWidgetClass, xwidget, xargs, xn); DELETE_ATOM = XmInternAtom(XtDisplay(shell), "WM_DELETE_WINDOW", False); XmAddWMProtocolCallback(shell, DELETE_ATOM, QCB, NULL); xn = 0; XtSetArg(xargs[xn], GLwNdoublebuffer, !!(flags&PGL_DOUBLE)); xn++; XtSetArg(xargs[xn], GLwNrgba, !!(flags&PGL_RGBA)); xn++; /* ASSUMES 12-bit Z-BUFFER!! */ XtSetArg(xargs[xn], GLwNdepthSize, 12); xn++; ctxt[gwinid].shell = shell; return(pglDoOpen(shell, "pglCanvas", initRoutine, xargs, xn)); } int pglOpenSubwindow(Widget parent, char* name, pglCallback initRoutine, int flags) { Arg xargs[10]; int xn = 0; XtSetArg(xargs[xn], GLwNdoublebuffer, !!(flags&PGL_DOUBLE)); xn++; XtSetArg(xargs[xn], GLwNrgba, !!(flags&PGL_RGBA)); xn++; /* ASSUMES 12-bit Z-BUFFER!! */ XtSetArg(xargs[xn], GLwNdepthSize, 12); xn++; ctxt[gwinid].shell = NULL; return(pglDoOpen(parent, name, initRoutine, xargs, xn)); } void pglManageWindows(void) { int j; for ( j = 0; j < gwinid; j++ ) if ( ctxt[j].widget ) { XtManageChild(ctxt[j].widget); XtRealizeWidget(ctxt[j].parent); if ( ctxt[j].shell ) XtPopup(ctxt[j].shell, XtGrabNone); } } void pglManageWindow(int winid) { XtManageChild(ctxt[winid].widget); } void pglUnmanageWindow(int winid) { XtUnmanageChild(ctxt[winid].widget); } void pglSwapBuffers(int winid) { GLwDrawingAreaSwapBuffers(ctxt[winid].widget); } void pglMakeMenu(int winid, char* title) { Arg xargs[20]; int xn = 0; Widget popupMenu; Widget menuShell; XmString xmstr = XmStringCreateLtoR(title, XmSTRING_DEFAULT_CHARSET); /* make top level menu shell widget */ XtInitializeWidgetClass(xmMenuShellWidgetClass); XtSetArg(xargs[xn], XmNwidth, 1); xn++; XtSetArg(xargs[xn], XmNheight, 1); xn++; menuShell = XtCreatePopupShell("pglMenuShell", xmMenuShellWidgetClass, ctxt[winid].parent, xargs, xn); /* ...put a popup widget inside it */ xn = 0; XtSetArg(xargs[xn], XmNrowColumnType, XmMENU_POPUP); xn++; XtSetArg(xargs[xn], XmNx, 0); xn++; XtSetArg(xargs[xn], XmNy, 0); xn++; XtSetArg(xargs[xn], XmNwidth, 195); xn++; XtSetArg(xargs[xn], XmNheight, 79); xn++; popupMenu = XtCreateWidget("popupMenu", xmRowColumnWidgetClass, menuShell, xargs, xn); /* ...give it a title */ XtVaCreateManagedWidget("menuTitle", xmLabelWidgetClass, popupMenu, XmNlabelString, xmstr, NULL); XmStringFree(xmstr); /* register the event handler to pop it up on RIGHTMOUSE */ XtAddEventHandler(XtParent(XtParent(popupMenu)), ButtonPressMask, False, pglMenuPost, (XtPointer)popupMenu); /* save a pointer to this menu */ if ( ctxt[winid].menu ) XtDestroyWidget(ctxt[winid].menu); ctxt[winid].menu = popupMenu; } void pglAddMenuItem(int winid, char* item, int value, void (*cb)(Widget, XtPointer, XtPointer)) { Widget button; XmString xmstr = XmStringCreateLtoR(item, XmSTRING_DEFAULT_CHARSET); if ( ctxt[winid].menu ) { /* add a pushbutton to the appropriate menu */ button = XtVaCreateManagedWidget("menuButton", xmPushButtonWidgetClass, ctxt[winid].menu, XmNlabelString, xmstr, NULL); if ( cb ) XtAddCallback(button, XmNactivateCallback, cb, (XtPointer)value); } XmStringFree(xmstr); } /******************* "GET" routines *************/ XtAppContext pglGetAppContext(void) { return xcontext; } GLXContext pglGetGLContext(int winid) { return ctxt[winid].context; } Widget pglGetAppWidget(void) { return xwidget; } Widget pglGetGLWidget(int winid) { return ctxt[winid].widget; } Widget pglGetShell(int winid) { return ctxt[winid].parent; } /******************* "SET" routines *************/ void pglSetCurrentWindow(int winid) { GLwDrawingAreaMakeCurrent(ctxt[winid].widget, ctxt[winid].context); } void pglSetCallback(int winid, int reason, pglCallback func) { switch ( reason ) { case PGL_GINIT: ctxt[winid].initCB = func; break; case PGL_EXPOSE: ctxt[winid].exposeCB = func; break; case PGL_RESIZE: ctxt[winid].resizeCB = func; break; case PGL_INPUT: ctxt[winid].inputCB = func; break; case PGL_TIMEOUT: ctxt[winid].timerCB = func; if ( ctxt[winid].doneInit ) if ( func && !ctxt[winid].timid ) { ctxt[winid].timid = XtAppAddTimeOut(xcontext, ctxt[winid].timeo, (XtTimerCallbackProc)timerCB, (XtPointer)winid); } else if ( !func && ctxt[winid].timid ) { XtRemoveTimeOut(ctxt[winid].timid); ctxt[winid].timid = 0; } break; case PGL_IDLE: ctxt[winid].idleCB = func; if ( ctxt[winid].doneInit ) if ( func && !ctxt[winid].wpid ) { ctxt[winid].wpid = XtAppAddWorkProc(xcontext, idleCB, (XtPointer)winid); } else if ( !func && ctxt[winid].wpid ) { XtRemoveWorkProc(ctxt[winid].wpid); ctxt[winid].wpid = 0; } break; } } void pglSetColors(int winid, int num, float*rgb) { Arg xargs; Colormap map; XColor* c; int j; XtSetArg(xargs, XmNcolormap, &map); XtGetValues(ctxt[winid].widget, &xargs, 1); c = (XColor*)malloc(num*sizeof(XColor)); for ( j = 0; j < num; j++ ) { c[j].pixel = j; c[j].red = (unsigned short)(rgb[3*j] * 65535.0 + 0.5); c[j].green = (unsigned short)(rgb[3*j+1] * 65535.0 + 0.5); c[j].blue = (unsigned short)(rgb[3*j+2] * 65535.0 + 0.5); c[j].flags = DoRed | DoGreen | DoBlue; } XStoreColors(XtDisplay(xwidget), map, c, num); } void pglSetTimeout(int winid, float seconds) { /* translate seconds into (rounded) milliseconds, and save it */ ctxt[winid].timeo = (int)(seconds*1000.0 + 0.5); } /********************** INTERNALS *************************/ static int pglDoOpen(Widget parent, char* name, pglCallback initRoutine, ArgList xargs, int xn) { Widget glw; if ( gwinid >= nslots ) { nslots *= 2; ctxt = (pglContext*) realloc(ctxt, nslots*sizeof(pglContext)); } ctxt[gwinid].menu = NULL; ctxt[gwinid].context = NULL; ctxt[gwinid].exposeCB = NULL; ctxt[gwinid].resizeCB = stdResize; ctxt[gwinid].inputCB = NULL; ctxt[gwinid].idleCB = NULL; ctxt[gwinid].timerCB = NULL; ctxt[gwinid].timeo = 33; ctxt[gwinid].wpid = 0; ctxt[gwinid].timid = 0; ctxt[gwinid].parent = parent; ctxt[gwinid].initCB = initRoutine; XtAddCallback(parent, XmNdestroyCallback, destroyCB, (XtPointer)gwinid); ctxt[gwinid].widget = glw = GLwCreateMDrawingArea(parent, name, xargs, xn); XtAddCallback(glw, GLwNginitCallback, initCB, (XtPointer)gwinid); XtAddCallback(glw, GLwNexposeCallback, exposeCB, (XtPointer)gwinid); XtAddCallback(glw, GLwNresizeCallback, resizeCB, (XtPointer)gwinid); XtAddCallback(glw, GLwNinputCallback, inputCB, (XtPointer)gwinid); return(gwinid++); } static void QCB(Widget w, XtPointer client_data, XtPointer call_data) { exit(0); } static void initCB(Widget w, XtPointer client_data, XtPointer call_data) { Arg xargs[1]; XVisualInfo *vi; int winid = (int)client_data; XtSetArg(xargs[0], GLwNvisualInfo, &vi); XtGetValues(w, xargs, 1); ctxt[winid].context = glXCreateContext(XtDisplay(w), vi, 0, GL_TRUE); ctxt[winid].doneInit = 1; GLwDrawingAreaMakeCurrent(w, ctxt[winid].context); if ( ctxt[winid].initCB ) ctxt[winid].initCB(winid, (pglCallbackInfo)call_data); if ( ctxt[winid].wpid ) XtRemoveWorkProc(ctxt[winid].wpid); if ( ctxt[winid].idleCB ) ctxt[winid].wpid = XtAppAddWorkProc(xcontext, idleCB, (XtPointer)winid); if ( ctxt[winid].timid ) XtRemoveTimeOut(ctxt[winid].timid); if ( ctxt[winid].timerCB ) ctxt[winid].timid = XtAppAddTimeOut(xcontext, ctxt[winid].timeo, timerCB, (XtPointer)winid); } static void exposeCB(Widget w, XtPointer client_data, XtPointer call_data) { int winid = (int)client_data; GLwDrawingAreaMakeCurrent(w, ctxt[winid].context); if ( ctxt[winid].exposeCB ) ctxt[winid].exposeCB(winid, (pglCallbackInfo)call_data); } static void resizeCB(Widget w, XtPointer client_data, XtPointer call_data) { int winid = (int)client_data; GLwDrawingAreaMakeCurrent(w, ctxt[winid].context); if ( ctxt[winid].resizeCB ) ctxt[winid].resizeCB(winid, (pglCallbackInfo)call_data); } static void inputCB(Widget w, XtPointer client_data, XtPointer call_data) { int winid = (int)client_data; if ( ctxt[winid].inputCB ) { GLwDrawingAreaMakeCurrent(w, ctxt[winid].context); ctxt[winid].inputCB(winid, (pglCallbackInfo)call_data); } } static void destroyCB(Widget w, XtPointer client_data, XtPointer call_data) { int winid = (int)client_data; if ( ctxt[winid].wpid ) XtRemoveWorkProc(ctxt[winid].wpid); if ( ctxt[winid].timid ) XtRemoveTimeOut(ctxt[winid].timid); if ( ctxt[winid].menu ) XtDestroyWidget(ctxt[winid].menu); if ( ctxt[winid].shell ) XtDestroyWidget(ctxt[winid].shell); ctxt[winid].menu = NULL; ctxt[winid].shell = NULL; ctxt[winid].widget = NULL; ctxt[winid].context = NULL; ctxt[winid].initCB = NULL; ctxt[winid].exposeCB = NULL; ctxt[winid].resizeCB = NULL; ctxt[winid].inputCB = NULL; } static struct _pglCallbackInfo idleinfo = { PGL_IDLE, NULL, 0, 0 }; static struct _pglCallbackInfo timerinfo = { PGL_TIMEOUT, NULL, 0, 0 }; static Boolean idleCB(XtPointer client_data) { int winid = (int)client_data; if ( ctxt[winid].idleCB ) { GLwDrawingAreaMakeCurrent(ctxt[winid].widget, ctxt[winid].context); ctxt[winid].idleCB(winid, &idleinfo); } return False; } static void timerCB(XtPointer client_data, XtIntervalId* tid) { int winid = (int)client_data; if ( ctxt[winid].timerCB ) { /* re-queue for next period! */ ctxt[winid].timid = XtAppAddTimeOut(xcontext, ctxt[winid].timeo, timerCB, (XtPointer)winid); GLwDrawingAreaMakeCurrent(ctxt[winid].widget, ctxt[winid].context); ctxt[winid].timerCB(winid, &timerinfo); } } static void stdResize(int winid, pglCallbackInfo info) { glViewport(0, 0, info->width, info->height); if ( ctxt[winid].exposeCB ) ctxt[winid].exposeCB(winid, info); } static void pglMenuPost(Widget w, XtPointer client, XEvent *event, Boolean *dispatch) { Arg xargs[2]; int xn; int button; Widget me = (Widget)client; XButtonEvent bev = event->xbutton; xn = 0; XtSetArg(xargs[xn], XmNwhichButton, &button); xn++; XtGetValues(me, xargs, xn); if( bev.button != button) return; XmMenuPosition(me, &bev); XtManageChild(me); } GLuint pglMakeFont(char* fontname) { unsigned int first; unsigned int last; Font id; GLuint base; XFontStruct* fontInfo; fontInfo = XLoadQueryFont(XtDisplay(xwidget), fontname); if (fontInfo == NULL) { fprintf(stderr, "no font found\n"); exit(0); } id = fontInfo->fid; first = fontInfo->min_char_or_byte2; last = fontInfo->max_char_or_byte2; base = glGenLists(last+1); if (base == 0) { fprintf(stderr, "out of display lists\n"); exit(0); } glXUseXFont(id, first, last-first+1, base+first); return base; } void pglDrawString(char *s, GLuint fontid) { glPushAttrib(GL_LIST_BIT); glListBase(fontid); glCallLists(strlen(s), GL_UNSIGNED_BYTE, (unsigned char *)s); glPopAttrib(); }