thant@horus.esd.sgi.com (Thant Tessman) (11/20/90)
I wrote a C++ class for GL Windows that has some nifty features. I hope there are some folks on the net who find it usefull. NOTE: This code is NOT supported by SGI or me, and is not guaranteed to work. It is only offered as a convenience and to serve as an example of how one might create a C++ application for the GL. Thanks to Gavin Bell who pointed out a few bugs and made a couple suggestions. The Window class packages gl window functionality into a C++ object. The advantages it has over straight gl windows: o each window gets to pretend it has its own gl input queue, o purely event driven (no busy wait loops) o a new pseudodevice QEMPTY is available to allow continuous action in an event driven fashion. o MOUSEX and MOUSEY events are automatically compressed and are given in relationship to the window's origin. (If there are a stack of mouse position event pairs on the queue, only the last pair are delivered to the window.) The Window class is designed to serve as a virtual base class. There are two virtual functions for the derived class to overload. The first is virtual void Window::event(Device dev, short val) This gets called to deliver gl input events to the window. Only input events for a given window will be delivered to that window. This means the app doesn't have to keep track of a 'current window.' Also, the window class does a 'winset' to the window about to receive the input event, so the app doesn't have to (under most circumstances). The second virtual function is virtual void Window::preferences() which gets called before the window is opened. The derived class should put window preferences (like 'prefsize' or 'keepaspect') in this function. The List class is kinda nifty too. It's not a proper object array, but it's small and it fit in my brain. Enjoy, thant ------------------------------------------------------------- This post should contain ten files separated by "--------"... They are: List.h List.c++ - a list class used by Window Window.h Window.c++ - a GL Window base class MyWindow.h MyWindow.c++ - an example app derived from Window Cube.h Cube.c++ - used by MyWindow main.c++ Makefile ----------------------------------(List.h)-------------------------------- #pragma once // For now Lists only hold things as big as a pointer (like pointers). // See the union below. struct List { List(); ~List(); void insert(void const *item, int index=0); void remove(void const *); void remove(int index); int find(void const *); // returns -1 if not found void* operator[](int index); int size() {return listsize;}; // To add onto the end of the list, call: // mylist.insert(item, mylist.size()); private: // this struct is supposed to be limited to this scope // but C++ isn't propper C++ yet struct ListNode { ListNode* prev; ListNode* next; // guarantee space for all these things until done right union { void* item; int dummy1; double dummy2; long dummy3; }; }; ListNode* head; ListNode* tail; ListNode* current; int index; int listsize; void gotoNode(int index); }; #define declareList(LIST,ITEM) \ \ struct LIST : List { \ LIST(); \ ~LIST(); \ void insert(ITEM, int index=0); \ void remove(ITEM); \ void remove(int index); \ int find(ITEM); \ ITEM operator[](int index); \ int size(); \ }; \ #define implementList(LIST,ITEM) \ \ LIST::LIST() {} \ LIST::~LIST() {} \ void LIST::insert(ITEM item, int index) \ {List::insert((void*)item, index);} \ void LIST::remove(ITEM item) {List::remove((void*)item);} \ void LIST::remove(int index) {List::remove(index);} \ int LIST::find(ITEM item) {return List::find((void*)item);}; \ ITEM LIST::operator[](int index) \ {return (ITEM) List::operator[](index);} \ int LIST::size() {return List::size();} \ ----------------------------(List.c++)----------------------------------- #include <stdio.h> #include "List.h" #include <stdlib.h> List::List() { head = NULL; tail = NULL; current = NULL; index = 0; listsize = 0; } List::~List() { ListNode* loop = head; ListNode* tmp; if (loop) do { tmp = loop; loop = loop->next; delete tmp; } while (loop); } void List::gotoNode(int index) { if (index==this->index) return; if (index==0) { this->index = 0; current = head; return; } if (index==listsize-1) { this->index = index; current = tail; return; } if (index>this->index) { for (int i=this->index; i<index; i++) current = current->next; } else { for (int i=this->index; i>index; i--) current = current->prev; } this->index = index; } void List::insert(void const * item, int index) { if ((index>listsize) || (index<0)) { fprintf(stderr, "\nList::insert(%d) out of bounds. (size: %d)\n", index, listsize); abort(); } ListNode* n = new ListNode(); n->item = item; if (listsize == 0) { head=n; tail=n; n->prev = NULL; n->next = NULL; this->index = 0; } else if (index == 0) { n->next = head; head->prev = n; head = n; n->prev = NULL; this->index = 0; } else if (index == listsize) { n->prev = tail; tail->next = n; tail = n; n->next = NULL; this->index = listsize; } else { gotoNode(index); n->next = current; n->prev = current->prev; (current->prev)->next = n; current->prev = n; } listsize++; current = n; } int List::find(void const * item) { if (listsize && (current->item == item)) return this->index; ListNode* look = head; int index = 0; if (look) do { if (look->item == item) { this->index = index; current = look; return index; } index++; } while (look = look->next); return -1; } void List::remove(int index) { if ( (index<0) || (index>=listsize) ) { fprintf(stderr, "\nList::remove(%d) out of bounds.\n", index); abort(); } gotoNode(index); if (head==current) head = current->next; if (tail==current) tail = current->prev; if (current->prev) (current->prev)->next = current->next; if (current->next) (current->next)->prev = current->prev; ListNode* tmp = current; if (current->next) { current = current->next; } else { current = current->prev; this->index--; } delete tmp; listsize--; } void List::remove(void const * item) { int index = find(item); if (index<0) { fprintf(stderr, "\nList::remove(item) item doesn't exist.\n"); abort(); } remove(index); } void* List::operator[](int index) { if ( (index<0) || (index>=listsize) ) { fprintf(stderr, "\nList::[%d] out of bounds\n", index); abort(); } gotoNode(index); return current->item; } ----------------------------------(Window.h)-------------------------------- #pragma once #include <gl.h> #include <device.h> #include "List.h" // device for doing stuff when there's nuthin better to do #define QEMPTY 0x4000 struct Window; declareList(WindowPtrList,Window*); declareList(DeviceList,Device); struct Window { virtual void open(); virtual void close(); int isOpened(); // called by main to start processing queue // exits when all windows are gone static void Window::Go(); virtual void qdevice(Device dev); virtual void unqdevice(Device dev); virtual void event(Device dev, short val)=0; virtual ~Window(); protected: virtual void preferences(); Window(char* name = NULL); long originX, originY, sizeX, sizeY; long gid; private: static WindowPtrList windowList; static WindowPtrList QEMPTYqueued; static Window* currentWindow; static Window* getWindow(long gid); DeviceList deviceList; static int init; char* name; }; --------------------------------(Window.c++)---------------------------------- #include "Window.h" #include "stdio.h" implementList(WindowPtrList,Window*); implementList(DeviceList,Device); WindowPtrList Window::windowList; WindowPtrList Window::QEMPTYqueued; Window* Window::currentWindow = 0; int Window::init = 0; void Window::preferences() {} Window::Window(char* n) { if (n==NULL) name = ""; else name = n; gid = -1; } int Window::isOpened() { return (gid != -1); } void Window::open() { if (!init) { foreground(); } preferences(); gid = winopen(name); if (gid == -1) { fprintf(stderr, "Window: no additional windows available.\n"); return; } glcompat(GLC_OLDPOLYGON, 0); windowList.insert(this); ::getorigin(&originX, &originY); ::getsize(&sizeX, &sizeY); color(0); clear(); } void Window::close() { if (gid == -1) return; winclose(gid); int i=QEMPTYqueued.find(this); if (i>=0) QEMPTYqueued.remove(i); windowList.remove(this); gid = -1; } Window::~Window() { close(); } void Window::qdevice(Device dev) { if (deviceList.find(dev)<0) deviceList.insert(dev); if (dev==QEMPTY && QEMPTYqueued.find(this)<0) QEMPTYqueued.insert(this); else ::qdevice(dev); } void Window::unqdevice(Device dev) { if (deviceList.find(dev)>=0) deviceList.remove(dev); if (dev==QEMPTY && QEMPTYqueued.find(this)>=0) QEMPTYqueued.remove(this); } static long nextqueue(short*); void Window::Go() { Device dev; short val; Window* w; ::qdevice(WINSHUT); ::qdevice(WINQUIT); while(windowList.size()>0) { if (qtest() || QEMPTYqueued.size()==0) { switch(dev = (Device)nextqueue(&val)) { case WINSHUT: w = getWindow((long)val); delete w; currentWindow = NULL; break; case WINQUIT: while (windowList.size()) { w = windowList[0]; delete w; }; break; case REDRAW: w = getWindow((long)val); ::winset(w->gid); ::getorigin(&(w->originX), &(w->originY)); ::getsize(&(w->sizeX), &(w->sizeY)); ::reshapeviewport(); w->event(dev, val); break; case INPUTCHANGE: if (val==0) { if (currentWindow) { ::winset(currentWindow->gid); currentWindow->event(dev, val); currentWindow = NULL; } } else { currentWindow = getWindow((long)val); if (currentWindow) { // i'd like to replace this with a query // from the window if (currentWindow->deviceList.find(MOUSEX)>=0 && currentWindow->deviceList.find(MOUSEY)>=0) { ::winset(currentWindow->gid); short mx = short(getvaluator(MOUSEX)); mx -= short(currentWindow->originX); short my = short(getvaluator(MOUSEY)); my -= short(currentWindow->originY); currentWindow->event(MOUSEX,mx); currentWindow->event(MOUSEY,my); } currentWindow->event(dev, val); } } break; case MOUSEX: if (currentWindow) { if (currentWindow->deviceList.find(MOUSEX)>=0) { val -= short(currentWindow->originX); ::winset(currentWindow->gid); currentWindow->event(dev, val); } } break; case MOUSEY: if (currentWindow) { if (currentWindow->deviceList.find(MOUSEY)>=0) { val -= short(currentWindow->originY); ::winset(currentWindow->gid); currentWindow->event(dev, val); } } break; default: if (currentWindow && currentWindow->deviceList.find(dev)>=0) { ::winset(currentWindow->gid); currentWindow->event(dev, val); } break; } } else { for (int i=0; i<QEMPTYqueued.size(); i++) { ::winset(QEMPTYqueued[i]->gid); QEMPTYqueued[i]->event(QEMPTY, 0); } } } } Window* Window::getWindow(long gid) { int i=0; while ((i<windowList.size()) && (windowList[i]->gid!=gid)) i++; if (i==windowList.size()) { fprintf(stderr, "Window::getWindow can't find it.\n"); return NULL; } return windowList[i]; } static long queuebuf(short* val) { short dev; static short qbuf[100]; static long num=0; static int ptr=0; if (ptr>=num) { ptr = 0; num = ::blkqread(qbuf, 100); } dev = qbuf[ptr]; *val = qbuf[ptr+1]; ptr+=2; return (long)dev; } long nextqueue(short* val) { static long devb[4]; static short valb[4]; static int ptr=0; static int num=0; long dev; if (ptr>=num) { ptr=0; num=1; if ((devb[0]=queuebuf(&valb[0]))==MOUSEX && ::qtest()) { num=2; if ((devb[1]=queuebuf(&valb[1]))==MOUSEY && ::qtest()) { do { num=3; if ((devb[2]=queuebuf(&valb[2]))==MOUSEX && ::qtest()) { num=4; if ((devb[3]=queuebuf(&valb[3]))==MOUSEY) { devb[0] = devb[2]; valb[0] = valb[2]; devb[1] = devb[3]; valb[1] = valb[3]; num=2; } } } while (num==2); } } } *val = valb[ptr]; dev = devb[ptr]; ptr++; return dev; } --------------------------------(MyWindow.h)---------------------------------- #pragma once #include "Window.h" #include "Cube.h" struct MyWindow : Window { MyWindow(); ~MyWindow(); virtual void open(); virtual void event(Device dev, short val); virtual void preferences(); private: Matrix objmat; Matrix idmat; enum Mode {NOTHING, ORIENT, TUMBLE} mode; void orient(); void tumble(); void draw(); int mx, my, omx, omy; // stuff for tumble double a, b, c; Cube cube; }; --------------------------------(MyWindow.c++)-------------------------------- #include "MyWindow.h" #include "math.h" #include "stdio.h" void MyWindow::preferences() { keepaspect(5,4); } MyWindow::MyWindow() : Window("My Window") { mode = NOTHING; // initialize matrices to identity matrices for (int i=0; i<4; i++) for (int j=0; j<4; j++) { objmat[i][j] = float(i==j); idmat[i][j] = float(i==j); } } void MyWindow::open() { Window::open(); doublebuffer(); RGBmode(); gconfig(); qdevice(LEFTMOUSE); qdevice(MIDDLEMOUSE); qdevice(MOUSEX); qdevice(MOUSEY); draw(); } MyWindow::~MyWindow(){} void MyWindow::event(Device dev, short val) { switch (dev) { case REDRAW: draw(); break; case MIDDLEMOUSE: if (val) { if (mode==NOTHING) { mode = ORIENT; omx = mx; omy = my; } } else { if (mode==ORIENT) mode = NOTHING; } break; case LEFTMOUSE: if (!val) { if (mode==NOTHING) { mode = TUMBLE; qdevice(QEMPTY); a = b = c = 0.0; } else if (mode==TUMBLE) { mode = NOTHING; unqdevice(QEMPTY); } } break; case MOUSEX: omx = mx; mx = val; break; case MOUSEY: omy = my; my = val; if (mode==ORIENT) orient(); break; case QEMPTY: tumble(); break; } } void MyWindow::orient() { pushmatrix(); loadmatrix(idmat); rotate(mx-omx, 'y'); rotate(omy-my, 'x'); multmatrix(objmat); getmatrix(objmat); popmatrix(); draw(); } void MyWindow::tumble() { pushmatrix(); loadmatrix(objmat); rot(sin(a*M_PI/180.0), 'y'); rot(sin(b*M_PI/180.0), 'x'); rot(sin(c*M_PI/180.0), 'z'); getmatrix(objmat); popmatrix(); draw(); if ((a += 0.1) > 360.0) a-=360.0; if ((b += 0.3) > 360.0) b-=360.0; if ((c += 0.7) > 360.0) c-=360.0; } void MyWindow::draw() { RGBcolor(0, 0, 0); clear(); perspective(400, 5.0/4.0, 2.0, 6.0); translate(0.0, 0.0, -4.0); multmatrix(objmat); cube.draw(); swapbuffers(); } -------------------------------(Cube.h)----------------------------------- #pragma once struct Cube { virtual void draw(); private: static float verts[8][3]; }; -------------------------------(Cube.c++)--------------------------------- #include "Cube.h" #include "gl.h" float Cube::verts[8][3] = { {1.0, 1.0, -1.0,}, {1.0, -1.0, -1.0,}, {-1.0, -1.0, -1.0,}, {-1.0, 1.0, -1.0,}, {1.0, 1.0, 1.0,}, {1.0, -1.0, 1.0,}, {-1.0, -1.0, 1.0,}, {-1.0, 1.0, 1.0,}, }; void Cube::draw() { RGBcolor(255, 255, 255); bgnclosedline(); v3f(verts[0]); v3f(verts[1]); v3f(verts[2]); v3f(verts[3]); endclosedline(); bgnclosedline(); v3f(verts[4]); v3f(verts[5]); v3f(verts[6]); v3f(verts[7]); endclosedline(); bgnline(); v3f(verts[0]); v3f(verts[4]); endline(); bgnline(); v3f(verts[1]); v3f(verts[5]); endline(); bgnline(); v3f(verts[2]); v3f(verts[6]); endline(); bgnline(); v3f(verts[3]); v3f(verts[7]); endline(); } -------------------------------(main.c++)----------------------------------- #include "MyWindow.h" main() { Window* w1 = new MyWindow(); Window* w2 = new MyWindow(); w1->open(); w2->open(); Window::Go(); } ----------------------------------(Makefile)----------------------------- #!smake # include ${ROOT}/usr/include/make/commondefs C++FILES = \ main.c++ \ List.c++ \ Window.c++ \ MyWindow.c++ \ Cube.c++ \ ${NULL} OPTIMIZER = -O LDFLAGS = -lgl_s -lm TARGETS = main all default: ${TARGETS} include ${ROOT}/usr/include/make/commonrules ${TARGETS}: ${OBJECTS} ${C++F} -o $@ ${OBJECTS} ${LDFLAGS} -----------------------------------(end)------------------------------