/*********************************************************** * Copyright (C) 1997, Be Inc. Copyright (C) 1999, Jake Hamby. * * This program is freely distributable without licensing fees * and is provided without guarantee or warrantee expressed or * implied. This program is -not- in the public domain. * * * FILE: glutWindow.cpp * * DESCRIPTION: all the routines for dealing with GlutWindows ***********************************************************/ /*********************************************************** * Headers ***********************************************************/ #include #include #include "glutint.h" #include "glutState.h" #include "glutBlocker.h" /*********************************************************** * FUNCTION: getUnusedWindowSlot * * DESCRIPTION: helper function to get a new window slot ***********************************************************/ static int getUnusedWindowSlot() { int i; /* Look for allocated, unused slot. */ for (i = 0; i < gState.windowListSize; i++) { if (!gState.windowList[i]) { return i; } } /* Allocate a new slot. */ gState.windowListSize++; gState.windowList = (GlutWindow **) realloc(gState.windowList, gState.windowListSize * sizeof(GlutWindow *)); if (!gState.windowList) __glutFatalError("out of memory."); gState.windowList[gState.windowListSize - 1] = NULL; return gState.windowListSize - 1; } /*********************************************************** * FUNCTION: __glutDefaultDisplay * __glutDefaultReshape * * DESCRIPTION: default display and reshape functions ***********************************************************/ static void __glutDefaultDisplay(void) { /* XXX Remove the warning after GLUT 3.0. */ __glutWarning("The following is a new check for GLUT 3.0; update your code."); __glutFatalError( "redisplay needed for window %d, but no display callback.", gState.currentWindow->num + 1); } void __glutDefaultReshape(int width, int height) { /* Adjust the viewport of the window */ glViewport(0, 0, (GLsizei) width, (GLsizei) height); } /*********************************************************** * CLASS: GlutWindow * * FUNCTION: (constructor) * * DESCRIPTION: creates a new GLUT window * note: subwindows don't resize, but top-level windows * follow all sides ***********************************************************/ GlutWindow::GlutWindow(GlutWindow *nparent, char *name, int x, int y, int width, int height, ulong options) : BGLView( (nparent ? BRect(x,y,x+width-1,y+height-1) : BRect(0,0,width-1,height-1)), name, (nparent ? B_FOLLOW_NONE : B_FOLLOW_ALL_SIDES), B_WILL_DRAW|B_FRAME_EVENTS|B_FULL_UPDATE_ON_RESIZE|B_PULSE_NEEDED, options) { // add myself to window list num = getUnusedWindowSlot(); gState.windowList[num] = this; // set up parent/children relationships parent = nparent; if (parent) { siblings = parent->children; parent->children = this; } else { siblings = 0; } children = 0; // initialize variables cursor = GLUT_CURSOR_INHERIT; // default cursor for (int i = 0; i < GLUT_MAX_MENUS; i++) { menu[i] = 0; } m_width = width; m_height = height; m_buttons = 0; // clear callbacks display = __glutDefaultDisplay; reshape = __glutDefaultReshape; mouse = 0; motion = 0; passive = 0; entry = 0; keyboard = 0; visibility = 0; special = 0; windowStatus = 0; // clear event counters anyevents = 1; displayEvent = 1; // get a reshape and a display event right away reshapeEvent = 1; mouseEvent = 0; motionEvent = 0; passiveEvent = 0; entryEvent = 0; keybEvent = 0; windowStatusEvent = 0; // DirectConnected() will report change in visState = -1; // visibility specialEvent = 0; statusEvent = 0; menuEvent = 0; visible = true; gBlock.QuickNewEvent(); // if i'm a subwindow, add me to my parent view if (parent) { parent->Window()->Lock(); parent->AddChild(this); parent->Window()->Unlock(); } else { // if I'm a top-level window, create my BWindow GlutBWindow *mybwindow = new GlutBWindow( BRect(x,y,x+width-1,y+height-1), name); mybwindow->AddChild(this); mybwindow->bgl = this; mybwindow->Show(); } // give me the keyboard focus (focus follows mouse, X style, as // implemented in GlutWindow::MouseMoved()) Window()->Lock(); MakeFocus(); Window()->Unlock(); // make myself the default window __glutSetWindow(this); } /*********************************************************** * FUNCTION: glutCreateWindow (4.1) * * DESCRIPTION: creates a new GLUT window ***********************************************************/ int glutCreateWindow(const char *name) { if (!be_app) __glutInit(); ulong options; if (!__glutConvertDisplayMode(&options)) { __glutWarning("visual with necessary capabilities not found."); } // if X or Y is negative, then start at a reasonable position bool defaultxy = (gState.initX < 0) || (gState.initY < 0); GlutWindow *window = new GlutWindow(0, const_cast(name), (defaultxy ? 50 : gState.initX), (defaultxy ? 50 : gState.initY), gState.initWidth, gState.initHeight, options); return window->num + 1; } /*********************************************************** * FUNCTION: glutCreateSubWindow (4.2) * * DESCRIPTION: creates a new GLUT subwindow * Note: a subwindow is a GlutWindow (which is actually * a BGLView) without its own BWindow ***********************************************************/ int glutCreateSubWindow(int win, int x, int y, int width, int height) { ulong options; if (!__glutConvertDisplayMode(&options)) { __glutFatalError("visual with necessary capabilities not found."); } GlutWindow *window = new GlutWindow(gState.windowList[win-1], "child", x, y, width, height, options); return window->num + 1; } /*********************************************************** * FUNCTION: __glutSetWindow * * DESCRIPTION: set the current window (utility function) ***********************************************************/ void __glutSetWindow(GlutWindow * window) { if (gState.currentWindow) gState.currentWindow->UnlockGL(); gState.currentWindow = window; gState.currentWindow->LockGL(); } /*********************************************************** * FUNCTION: glutSetWindow (4.3) * glutGetWindow * * DESCRIPTION: set and get the current window ***********************************************************/ void glutSetWindow(int win) { GlutWindow *window; if (win < 1 || win > gState.windowListSize) { __glutWarning("glutSetWindow attempted on bogus window."); return; } window = gState.windowList[win - 1]; if (!window) { __glutWarning("glutSetWindow attempted on bogus window."); return; } __glutSetWindow(window); } int glutGetWindow() { if (gState.currentWindow) { return gState.currentWindow->num + 1; } else { return 0; } } /*********************************************************** * FUNCTION: __glutDestroyWindow * * DESCRIPTION: recursively set entries to 0 ***********************************************************/ static void __glutDestroyWindow(GlutWindow *window, GlutWindow *initialWindow) { // first, find all children recursively and set their entries to 0 GlutWindow *cur = window->children; while (cur) { GlutWindow *siblings = cur->siblings; __glutDestroyWindow(cur, initialWindow); cur = siblings; } /* Remove from parent's children list (only necessary for non-initial windows and subwindows!). */ GlutWindow *parent = window->parent; if (parent && parent == initialWindow->parent) { GlutWindow **prev = &parent->children; cur = parent->children; while (cur) { if (cur == window) { *prev = cur->siblings; break; } prev = &(cur->siblings); cur = cur->siblings; } } // finally, check if we are the current window, and set to 0 if (gState.currentWindow == window) { gState.currentWindow = 0; } gState.windowList[window->num] = 0; } /*********************************************************** * FUNCTION: glutDestroyWindow (4.4) * * DESCRIPTION: destroy window and all its children ***********************************************************/ void glutDestroyWindow(int win) { // can't destroy a window if another window has the GL context if (gState.currentWindow) gState.currentWindow->UnlockGL(); // lock the window GlutWindow *window = gState.windowList[win-1]; BWindow *bwindow = window->Window(); bwindow->Lock(); // if win is the current window, set current window to 0 if (gState.currentWindow == window) { gState.currentWindow = 0; } // recursively set child entries to 0 __glutDestroyWindow(window, window); // try flushing OpenGL window->LockGL(); glFlush(); window->UnlockGL(); // now, if the window was top-level, delete its BWindow if(!window->parent) { bwindow->Quit(); } else { // else, detach it from the BWindow and delete it window->RemoveSelf(); delete window; bwindow->Unlock(); } // relock GL if the current window is still valid if(gState.currentWindow) gState.currentWindow->LockGL(); } /*********************************************************** * FUNCTION: __glutDestroyAllWindows * * DESCRIPTION: destroy all windows when exit() is called * this seems to be necessary to avoid delays * and crashes when using BDirectWindow ***********************************************************/ void __glutDestroyAllWindows() { for(int i=0; iLock(); gState.display->Quit(); status_t ignored; wait_for_thread(gState.appthread, &ignored); } /*********************************************************** * FUNCTION: glutPostRedisplay (4.5) * * DESCRIPTION: mark window as needing redisplay ***********************************************************/ void glutPostRedisplay() { gState.currentWindow->Window()->Lock(); gState.currentWindow->anyevents = true; gState.currentWindow->displayEvent = true; gState.currentWindow->Window()->Unlock(); gBlock.QuickNewEvent(); } /*********************************************************** * FUNCTION: glutPostWindowRedisplay * * DESCRIPTION: mark window as needing redisplay ***********************************************************/ void glutPostWindowRedisplay(int win) { GlutWindow *gwin = gState.windowList[win - 1]; gwin->Window()->Lock(); gwin->anyevents = true; gwin->displayEvent = true; gwin->Window()->Unlock(); gBlock.QuickNewEvent(); } /*********************************************************** * FUNCTION: glutSwapBuffers (4.6) * * DESCRIPTION: swap buffers ***********************************************************/ void glutSwapBuffers() { gState.currentWindow->SwapBuffers(); } /*********************************************************** * FUNCTION: glutPositionWindow (4.7) * * DESCRIPTION: move window ***********************************************************/ void glutPositionWindow(int x, int y) { BDirectWindow *win = dynamic_cast(gState.currentWindow->Window()); win->Lock(); if (gState.currentWindow->parent) gState.currentWindow->MoveTo(x, y); // move the child view else { if(win->IsFullScreen()) { win->SetFullScreen(false); } win->MoveTo(x, y); // move the window } win->Unlock(); } /*********************************************************** * FUNCTION: glutReshapeWindow (4.8) * * DESCRIPTION: reshape window (we'll catch the callback * when the view gets a Draw() message ***********************************************************/ void glutReshapeWindow(int width, int height) { BDirectWindow *win = dynamic_cast(gState.currentWindow->Window()); win->Lock(); if (gState.currentWindow->parent) gState.currentWindow->ResizeTo(width-1, height-1); // resize the child else { if(win->IsFullScreen()) { win->SetFullScreen(false); } win->ResizeTo(width-1, height-1); // resize the parent } win->Unlock(); } /*********************************************************** * FUNCTION: glutFullScreen (4.9) * * DESCRIPTION: makes the window full screen ***********************************************************/ void glutFullScreen() { BDirectWindow *win = dynamic_cast(gState.currentWindow->Window()); win->Lock(); win->SetFullScreen(true); win->Unlock(); } /*********************************************************** * FUNCTION: glutPopWindow (4.10) * glutPushWindow * * DESCRIPTION: change the stacking order of the current window * NOTE: I can't figure out how to do this for windows, * and there is no concept of "stacking order" for * subwindows, so these are currently no-ops. ***********************************************************/ void glutPopWindow() { } void glutPushWindow() { } /*********************************************************** * FUNCTION: glutShowWindow (4.11) * glutHideWindow * glutIconifyWindow * * DESCRIPTION: change display status of current window ***********************************************************/ void glutShowWindow() { gState.currentWindow->Window()->Lock(); if (gState.currentWindow->parent) // subwindow gState.currentWindow->Show(); else { if(gState.currentWindow->Window()->IsHidden()) gState.currentWindow->Window()->Show(); // show the actual BWindow gState.currentWindow->Window()->Minimize(false); } gState.currentWindow->Window()->Unlock(); } void glutHideWindow() { gState.currentWindow->Window()->Lock(); if (gState.currentWindow->parent) // subwindow gState.currentWindow->Hide(); else gState.currentWindow->Window()->Hide(); // show the actual BWindow gState.currentWindow->Window()->Unlock(); } void glutIconifyWindow() { if(gState.currentWindow->parent) __glutFatalError("can't iconify a subwindow"); gState.currentWindow->Window()->Lock(); gState.currentWindow->Window()->Minimize(true); gState.currentWindow->Window()->Unlock(); } /*********************************************************** * FUNCTION: glutSetWindowTitle (4.12) * glutSetIconTitle * * DESCRIPTION: set the window title (icon title is same) ***********************************************************/ void glutSetWindowTitle(const char *name) { if (gState.currentWindow->parent) __glutFatalError("glutSetWindowTitle: isn't a top-level window"); gState.currentWindow->Window()->Lock(); gState.currentWindow->Window()->SetTitle(name); gState.currentWindow->Window()->Unlock(); } void glutSetIconTitle(const char *name) { glutSetWindowTitle(name); } /*********************************************************** * FUNCTION: __glutConvertDisplayMode * * DESCRIPTION: converts the current display mode into a BGLView * display mode, printing warnings as appropriate. * * PARAMETERS: if options is non-NULL, the current display mode is * returned in it. * * RETURNS: 1 if the current display mode is possible, else 0 ***********************************************************/ int __glutConvertDisplayMode(unsigned long *options) { if (gState.displayString) { /* __glutDisplayString should be NULL except if glutInitDisplayString has been called to register a different display string. Calling glutInitDisplayString means using a string instead of an integer mask determine the visual to use. This big ugly code is in glutDstr.cpp */ return __glutConvertDisplayModeFromString(options); } if(options) { ulong newoptions = 0; if(gState.displayMode & GLUT_ACCUM) newoptions |= BGL_ACCUM; if(gState.displayMode & GLUT_ALPHA) newoptions |= BGL_ALPHA; if(gState.displayMode & GLUT_DEPTH) newoptions |= BGL_DEPTH; if(gState.displayMode & GLUT_DOUBLE) newoptions |= BGL_DOUBLE; if(gState.displayMode & GLUT_STENCIL) newoptions |= BGL_STENCIL; *options = newoptions; } if(gState.displayMode & GLUT_INDEX) { __glutWarning("BeOS doesn't support indexed color"); return 0; } if(gState.displayMode & GLUT_MULTISAMPLE) { return 1; // try to go without multisampling } if(gState.displayMode & GLUT_STEREO) { __glutWarning("BeOS doesn't support stereo windows"); return 0; } if(gState.displayMode & GLUT_LUMINANCE) { __glutWarning("BeOS doesn't support luminance color model"); return 0; } return 1; // visual supported } /*********************************************************** * CLASS: GlutBWindow * * DESCRIPTION: very thin wrapper around BWindow ***********************************************************/ GlutBWindow::GlutBWindow(BRect frame, char *name) : BDirectWindow(frame, name, B_TITLED_WINDOW, 0) { fConnectionDisabled = false; bgl = 0; SetPulseRate(100000); if (!SupportsWindowMode()) { __glutFatalError("video card doesn't support windowed operation"); } } void GlutBWindow::DirectConnected( direct_buffer_info *info ) { bgl->DirectConnected(info); if(bgl && !fConnectionDisabled) { bgl->EnableDirectMode(true); } int newVisState; if((info->buffer_state & B_DIRECT_MODE_MASK) == B_DIRECT_START) { bgl->visible = true; } if(!bgl->visible || info->buffer_state == B_DIRECT_STOP) newVisState = GLUT_HIDDEN; else { if (info->clip_list_count == 0) newVisState = GLUT_FULLY_COVERED; else if (info->clip_list_count == 1) newVisState = GLUT_FULLY_RETAINED; else newVisState = GLUT_PARTIALLY_RETAINED; } if(newVisState != bgl->visState) { bgl->visState = newVisState; bgl->anyevents = bgl->windowStatusEvent = true; gBlock.NewEvent(); } } GlutBWindow::~GlutBWindow() { fConnectionDisabled = true; if(bgl) { bgl->EnableDirectMode(false); } if(!IsHidden()) Hide(); Sync(); } bool GlutBWindow::QuitRequested() { gState.quitAll = true; gBlock.NewEvent(); return false; // don't quit now, wait for main thread to do it } void GlutBWindow::Minimize(bool minimize) { bgl->visible = !minimize; BWindow::Minimize(minimize); } void GlutBWindow::Hide() { BWindow::Hide(); bgl->visible = false; } void GlutBWindow::Show() { BWindow::Show(); bgl->visible = true; }