1 /*********************************************************** 2 * Copyright (C) 1997, Be Inc. Copyright (C) 1999, Jake Hamby. 3 * 4 * This program is freely distributable without licensing fees 5 * and is provided without guarantee or warrantee expressed or 6 * implied. This program is -not- in the public domain. 7 * 8 * DESCRIPTION: all the routines for dealing with GlutWindows 9 ***********************************************************/ 10 11 #include <stdlib.h> 12 13 #include <GL/glut.h> 14 15 #include "glutint.h" 16 #include "glutState.h" 17 #include "glutBlocker.h" 18 19 20 /*! Helper function to get a new window slot */ 21 static int 22 getUnusedWindowSlot() 23 { 24 int i; 25 26 /* Look for allocated, unused slot. */ 27 for (i = 0; i < gState.windowListSize; i++) { 28 if (!gState.windowList[i]) 29 return i; 30 } 31 32 /* Allocate a new slot. */ 33 gState.windowListSize++; 34 gState.windowList = (GlutWindow **)realloc(gState.windowList, 35 gState.windowListSize * sizeof(GlutWindow *)); 36 if (gState.windowList == NULL) 37 __glutFatalError("out of memory."); 38 39 gState.windowList[gState.windowListSize - 1] = NULL; 40 return gState.windowListSize - 1; 41 } 42 43 44 /*! Default display function */ 45 static void 46 __glutDefaultDisplay(void) 47 { 48 /* XXX Remove the warning after GLUT 3.0. */ 49 __glutWarning("The following is a new check for GLUT 3.0; update your " 50 "code."); 51 __glutFatalError("redisplay needed for window %d, but no display callback.", 52 gState.currentWindow->num + 1); 53 } 54 55 56 /*! Default reshape function */ 57 void 58 __glutDefaultReshape(int width, int height) 59 { 60 /* Adjust the viewport of the window */ 61 glViewport(0, 0, (GLsizei) width, (GLsizei) height); 62 } 63 64 65 // #pragma mark - 66 67 68 /*! Creates a new GLUT window 69 Note: subwindows don't resize, but top-level windows 70 follow all sides. 71 */ 72 GlutWindow::GlutWindow(GlutWindow *nparent, const char *name, 73 int x, int y, int width, int height, ulong options) 74 : BGLView(nparent != NULL ? BRect(x, y, x + width - 1, y + height - 1) 75 : BRect(0, 0, width - 1, height - 1), name, 76 nparent != NULL ? B_FOLLOW_NONE : B_FOLLOW_ALL_SIDES, 77 B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE | B_PULSE_NEEDED, 78 options) 79 { 80 // add myself to window list 81 num = getUnusedWindowSlot(); 82 gState.windowList[num] = this; 83 84 // set up parent/children relationships 85 parent = nparent; 86 if (parent) { 87 siblings = parent->children; 88 parent->children = this; 89 } else { 90 siblings = 0; 91 } 92 children = 0; 93 94 // initialize variables 95 cursor = GLUT_CURSOR_INHERIT; // default cursor 96 for (int i = 0; i < GLUT_MAX_MENUS; i++) { 97 menu[i] = 0; 98 } 99 m_width = width; 100 m_height = height; 101 m_buttons = 0; 102 103 // clear callbacks 104 display = __glutDefaultDisplay; 105 reshape = __glutDefaultReshape; 106 mouse = NULL; 107 motion = NULL; 108 passive = NULL; 109 entry = NULL; 110 keyboard = NULL; 111 keyboardUp = NULL; 112 visibility = NULL; 113 special = NULL; 114 specialUp = NULL; 115 windowStatus = NULL; 116 117 // clear event counters 118 anyevents = 1; 119 displayEvent = 1; // get a reshape and a display event right away 120 reshapeEvent = 1; 121 mouseEvent = 0; 122 motionEvent = 0; 123 passiveEvent = 0; 124 entryEvent = 0; 125 keybEvent = 0; 126 keybUpEvent = 0; 127 windowStatusEvent = 0; // DirectConnected() will report change in 128 visState = -1; // visibility 129 specialEvent = 0; 130 specialUpEvent = 0; 131 statusEvent = 0; 132 menuEvent = 0; 133 visible = true; 134 ignoreKeyRepeat = (gState.keyRepeatMode == GLUT_KEY_REPEAT_OFF); 135 136 gBlock.QuickNewEvent(); 137 138 // if i'm a subwindow, add me to my parent view 139 if (parent) { 140 parent->Window()->Lock(); 141 parent->AddChild(this); 142 parent->Window()->Unlock(); 143 } else { 144 // if I'm a top-level window, create my BWindow 145 GlutBWindow *mybwindow = new GlutBWindow( 146 BRect(x, y, x + width - 1, y + height - 1), name); 147 mybwindow->AddChild(this); 148 mybwindow->bgl = this; 149 mybwindow->Show(); 150 } 151 152 // give me the keyboard focus (focus follows mouse, X style, as 153 // implemented in GlutWindow::MouseMoved()) 154 Window()->Lock(); 155 MakeFocus(); 156 Window()->Unlock(); 157 158 // make myself the default window 159 __glutSetWindow(this); 160 } 161 162 163 /*! Creates a new GLUT window */ 164 int 165 glutCreateWindow(const char *name) 166 { 167 if (!be_app) 168 __glutInit(); 169 170 ulong options; 171 if (!__glutConvertDisplayMode(&options)) { 172 __glutWarning("visual with necessary capabilities not found."); 173 } 174 175 // if X or Y is negative, then start at a reasonable position 176 bool defaultxy = gState.initX < 0 || gState.initY < 0; 177 178 GlutWindow *window = new GlutWindow(0, name, 179 defaultxy ? 50 : gState.initX, defaultxy ? 50 : gState.initY, 180 gState.initWidth, gState.initHeight, options); 181 182 return window->num + 1; 183 } 184 185 186 /*! Creates a new GLUT subwindow 187 Note: a subwindow is a GlutWindow (which is actually 188 a BGLView) without its own BWindow 189 */ 190 int 191 glutCreateSubWindow(int win, int x, int y, int width, int height) 192 { 193 ulong options; 194 if (!__glutConvertDisplayMode(&options)) { 195 __glutFatalError("visual with necessary capabilities not found."); 196 } 197 198 GlutWindow *window = new GlutWindow(gState.windowList[win-1], "child", 199 x, y, width, height, options); 200 201 return window->num + 1; 202 } 203 204 205 /*! Set the current window (utility function) */ 206 void 207 __glutSetWindow(GlutWindow *window) 208 { 209 if (gState.currentWindow) 210 gState.currentWindow->UnlockGL(); 211 gState.currentWindow = window; 212 gState.currentWindow->LockGL(); 213 } 214 215 216 /*! Set and get the current window */ 217 void 218 glutSetWindow(int win) 219 { 220 GlutWindow *window; 221 222 if (win < 1 || win > gState.windowListSize) { 223 __glutWarning("glutSetWindow attempted on bogus window."); 224 return; 225 } 226 227 window = gState.windowList[win - 1]; 228 if (!window) { 229 __glutWarning("glutSetWindow attempted on bogus window."); 230 return; 231 } 232 __glutSetWindow(window); 233 } 234 235 236 int 237 glutGetWindow() 238 { 239 if (gState.currentWindow) 240 return gState.currentWindow->num + 1; 241 242 return 0; 243 } 244 245 246 /*! Recursively set entries to 0. */ 247 static void 248 __glutDestroyWindow(GlutWindow *window, GlutWindow *initialWindow) 249 { 250 // first, find all children recursively and set their entries to 0 251 GlutWindow *cur = window->children; 252 while (cur) { 253 GlutWindow *siblings = cur->siblings; 254 __glutDestroyWindow(cur, initialWindow); 255 cur = siblings; 256 } 257 258 /* Remove from parent's children list (only necessary for 259 non-initial windows and subwindows!). */ 260 GlutWindow *parent = window->parent; 261 if (parent && parent == initialWindow->parent) { 262 GlutWindow **prev = &parent->children; 263 cur = parent->children; 264 while (cur) { 265 if (cur == window) { 266 *prev = cur->siblings; 267 break; 268 } 269 prev = &(cur->siblings); 270 cur = cur->siblings; 271 } 272 } 273 274 // finally, check if we are the current window, and set to 0 275 if (gState.currentWindow == window) 276 gState.currentWindow = 0; 277 278 gState.windowList[window->num] = 0; 279 } 280 281 282 /*! Destroy window and all its children. */ 283 void 284 glutDestroyWindow(int win) 285 { 286 // can't destroy a window if another window has the GL context 287 if (gState.currentWindow) 288 gState.currentWindow->UnlockGL(); 289 290 // lock the window 291 GlutWindow *window = gState.windowList[win - 1]; 292 BWindow *bwindow = window->Window(); 293 bwindow->Lock(); 294 295 // if win is the current window, set current window to 0 296 if (gState.currentWindow == window) 297 gState.currentWindow = 0; 298 299 // recursively set child entries to 0 300 __glutDestroyWindow(window, window); 301 302 // try flushing OpenGL 303 window->LockGL(); 304 glFlush(); 305 window->UnlockGL(); 306 307 // now, if the window was top-level, delete its BWindow 308 if (!window->parent) { 309 bwindow->Quit(); 310 } else { 311 // else, detach it from the BWindow and delete it 312 window->RemoveSelf(); 313 delete window; 314 bwindow->Unlock(); 315 } 316 317 // relock GL if the current window is still valid 318 if (gState.currentWindow) 319 gState.currentWindow->LockGL(); 320 } 321 322 323 /*! Destroy all windows when exit() is called this seems to be necessary 324 to avoid delays and crashes when using BDirectWindow. 325 */ 326 void 327 __glutDestroyAllWindows() 328 { 329 for (int i = 0; i < gState.windowListSize; i++) { 330 if (gState.windowList[i]) 331 glutDestroyWindow(i + 1); 332 } 333 gState.display->Lock(); 334 gState.display->Quit(); 335 gState.display->Unlock(); 336 337 status_t ignored; 338 wait_for_thread(gState.appthread, &ignored); 339 } 340 341 342 /*! Mark window as needing redisplay */ 343 void 344 glutPostRedisplay() 345 { 346 gState.currentWindow->Window()->Lock(); 347 gState.currentWindow->anyevents = true; 348 gState.currentWindow->displayEvent = true; 349 gState.currentWindow->Window()->Unlock(); 350 gBlock.QuickNewEvent(); 351 } 352 353 354 /*! Mark window as needing redisplay */ 355 void 356 glutPostWindowRedisplay(int win) 357 { 358 GlutWindow *gwin = gState.windowList[win - 1]; 359 gwin->Window()->Lock(); 360 gwin->anyevents = true; 361 gwin->displayEvent = true; 362 gwin->Window()->Unlock(); 363 gBlock.QuickNewEvent(); 364 } 365 366 367 void 368 glutSwapBuffers() 369 { 370 gState.currentWindow->SwapBuffers(); 371 } 372 373 374 /*! Move window */ 375 void 376 glutPositionWindow(int x, int y) 377 { 378 BDirectWindow *win = dynamic_cast<BDirectWindow*>(gState.currentWindow->Window()); 379 if (win == NULL) 380 return; 381 382 win->Lock(); 383 if (gState.currentWindow->parent) 384 gState.currentWindow->MoveTo(x, y); // move the child view 385 else { 386 if (win->IsFullScreen()) 387 win->SetFullScreen(false); 388 389 win->MoveTo(x, y); // move the window 390 } 391 win->Unlock(); 392 } 393 394 395 /*! Reshape window (we'll catch the callback when the view gets 396 a Draw() message). 397 */ 398 void 399 glutReshapeWindow(int width, int height) 400 { 401 BDirectWindow *win = dynamic_cast<BDirectWindow*>(gState.currentWindow->Window()); 402 if (win == NULL) 403 return; 404 405 win->Lock(); 406 if (gState.currentWindow->parent) { 407 // resize the child 408 gState.currentWindow->ResizeTo(width - 1, height - 1); 409 } else { 410 if (win->IsFullScreen()) 411 win->SetFullScreen(false); 412 413 // resize the parent 414 win->ResizeTo(width - 1, height - 1); 415 } 416 win->Unlock(); 417 } 418 419 420 /*! Makes the window full screen */ 421 void 422 glutFullScreen() 423 { 424 BDirectWindow *win = dynamic_cast<BDirectWindow*>(gState.currentWindow->Window()); 425 if (win == NULL) 426 return; 427 428 win->Lock(); 429 win->SetFullScreen(true); 430 win->Unlock(); 431 } 432 433 434 /*! Supposed to change the stacking order of the current window 435 NOTE: I can't figure out how to do this for windows, 436 and there is no concept of "stacking order" for 437 subwindows, so these are currently no-ops. 438 */ 439 void 440 glutPopWindow() 441 { 442 } 443 444 445 /*! Same problem as glutPopWindow() */ 446 void 447 glutPushWindow() 448 { 449 } 450 451 452 void 453 glutShowWindow() 454 { 455 gState.currentWindow->Window()->Lock(); 456 if (gState.currentWindow->parent) // subwindow 457 gState.currentWindow->Show(); 458 else { 459 if(gState.currentWindow->Window()->IsHidden()) 460 gState.currentWindow->Window()->Show(); // show the actual BWindow 461 gState.currentWindow->Window()->Minimize(false); 462 } 463 gState.currentWindow->Window()->Unlock(); 464 } 465 466 467 void 468 glutHideWindow() 469 { 470 gState.currentWindow->Window()->Lock(); 471 472 if (gState.currentWindow->parent) // subwindow 473 gState.currentWindow->Hide(); 474 else 475 gState.currentWindow->Window()->Hide(); // show the actual BWindow 476 477 gState.currentWindow->Window()->Unlock(); 478 } 479 480 481 /*! Minimizes window */ 482 void 483 glutIconifyWindow() 484 { 485 if (gState.currentWindow->parent) 486 __glutFatalError("can't iconify a subwindow"); 487 488 gState.currentWindow->Window()->Lock(); 489 gState.currentWindow->Window()->Minimize(true); 490 gState.currentWindow->Window()->Unlock(); 491 } 492 493 494 /*! Sets the window title */ 495 void 496 glutSetWindowTitle(const char *name) 497 { 498 if (gState.currentWindow->parent) 499 __glutFatalError("glutSetWindowTitle: isn't a top-level window"); 500 501 gState.currentWindow->Window()->Lock(); 502 gState.currentWindow->Window()->SetTitle(name); 503 gState.currentWindow->Window()->Unlock(); 504 } 505 506 507 /*! Same as glutSetWindowTitle() */ 508 void 509 glutSetIconTitle(const char *name) 510 { 511 glutSetWindowTitle(name); 512 } 513 514 515 /*! Converts the current display mode into a BGLView 516 display mode, printing warnings as appropriate. 517 518 \param options if non-NULL, the current display mode is 519 returned in it. 520 \return 1 if the current display mode is possible, else 0 521 */ 522 int 523 __glutConvertDisplayMode(unsigned long *options) 524 { 525 if (gState.displayString) { 526 /* __glutDisplayString should be NULL except if 527 glutInitDisplayString has been called to register a 528 different display string. Calling glutInitDisplayString 529 means using a string instead of an integer mask determine 530 the visual to use. This big ugly code is in glutDstr.cpp */ 531 return __glutConvertDisplayModeFromString(options); 532 } 533 534 if (options) { 535 ulong newoptions = 0; 536 if (gState.displayMode & GLUT_ACCUM) 537 newoptions |= BGL_ACCUM; 538 if (gState.displayMode & GLUT_ALPHA) 539 newoptions |= BGL_ALPHA; 540 if (gState.displayMode & GLUT_DEPTH) 541 newoptions |= BGL_DEPTH; 542 if (gState.displayMode & GLUT_DOUBLE) 543 newoptions |= BGL_DOUBLE; 544 if (gState.displayMode & GLUT_STENCIL) 545 newoptions |= BGL_STENCIL; 546 *options = newoptions; 547 } 548 549 if (gState.displayMode & GLUT_INDEX) { 550 __glutWarning("BeOS doesn't support indexed color"); 551 return 0; 552 } 553 if (gState.displayMode & GLUT_MULTISAMPLE) { 554 return 1; // try to go without multisampling 555 } 556 if (gState.displayMode & GLUT_STEREO) { 557 __glutWarning("BeOS doesn't support stereo windows"); 558 return 0; 559 } 560 if (gState.displayMode & GLUT_LUMINANCE) { 561 __glutWarning("BeOS doesn't support luminance color model"); 562 return 0; 563 } 564 return 1; // visual supported 565 } 566 567 568 // #pragma mark - 569 570 571 /*! Very thin wrapper around BWindow */ 572 GlutBWindow::GlutBWindow(BRect frame, const char *name) 573 : BDirectWindow(frame, name, B_TITLED_WINDOW, 0) 574 { 575 fConnectionDisabled = false; 576 bgl = 0; 577 SetPulseRate(100000); 578 579 if (!SupportsWindowMode()) { 580 __glutFatalError("video card doesn't support windowed operation"); 581 } 582 } 583 584 585 void 586 GlutBWindow::DirectConnected(direct_buffer_info *info) 587 { 588 if (!bgl) 589 return; 590 591 bgl->DirectConnected(info); 592 if (!fConnectionDisabled) 593 bgl->EnableDirectMode(true); 594 595 int newVisState; 596 if ((info->buffer_state & B_DIRECT_MODE_MASK) == B_DIRECT_START) 597 bgl->visible = true; 598 599 if (!bgl->visible || info->buffer_state == B_DIRECT_STOP) 600 newVisState = GLUT_HIDDEN; 601 else { 602 if (info->clip_list_count == 0) 603 newVisState = GLUT_FULLY_COVERED; 604 else if (info->clip_list_count == 1) 605 newVisState = GLUT_FULLY_RETAINED; 606 else 607 newVisState = GLUT_PARTIALLY_RETAINED; 608 } 609 610 if (newVisState != bgl->visState) { 611 bgl->visState = newVisState; 612 bgl->anyevents = bgl->windowStatusEvent = true; 613 gBlock.NewEvent(); 614 } 615 } 616 617 618 GlutBWindow::~GlutBWindow() 619 { 620 fConnectionDisabled = true; 621 if (bgl) 622 bgl->EnableDirectMode(false); 623 624 if (!IsHidden()) 625 Hide(); 626 627 Sync(); 628 } 629 630 631 bool 632 GlutBWindow::QuitRequested() 633 { 634 gState.quitAll = true; 635 gBlock.NewEvent(); 636 return false; 637 // don't quit now, wait for main thread to do it 638 } 639 640 641 void 642 GlutBWindow::Minimize(bool minimize) 643 { 644 bgl->visible = !minimize; 645 BWindow::Minimize(minimize); 646 } 647 648 649 void 650 GlutBWindow::Hide() 651 { 652 BWindow::Hide(); 653 bgl->visible = false; 654 } 655 656 657 void 658 GlutBWindow::Show() 659 { 660 BWindow::Show(); 661 bgl->visible = true; 662 } 663