glutEvent.cpp revision c041511d
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 * 9 * FILE: glutEvent.cpp 10 * 11 * DESCRIPTION: here it is, the BeOS GLUT event loop 12 ***********************************************************/ 13 14/*********************************************************** 15 * Headers 16 ***********************************************************/ 17#include <GL/glut.h> 18#include "glutint.h" 19#include "glutState.h" 20#include "glutBlocker.h" 21 22/*********************************************************** 23 * CLASS: GLUTtimer 24 * 25 * DESCRIPTION: list of timer callbacks 26 ***********************************************************/ 27struct GLUTtimer { 28 GLUTtimer *next; // list of timers 29 bigtime_t timeout; // time to be called 30 GLUTtimerCB func; // function to call 31 int value; // value 32}; 33 34/*********************************************************** 35 * Private variables 36 ***********************************************************/ 37static GLUTtimer *__glutTimerList = 0; // list of timer callbacks 38static GLUTtimer *freeTimerList = 0; 39 40/*********************************************************** 41 * FUNCTION: glutTimerFunc (7.19) 42 * 43 * DESCRIPTION: register a new timer callback 44 ***********************************************************/ 45void APIENTRY 46glutTimerFunc(unsigned int interval, GLUTtimerCB timerFunc, int value) 47{ 48 GLUTtimer *timer, *other; 49 GLUTtimer **prevptr; 50 51 if (!timerFunc) 52 return; 53 54 if (freeTimerList) { 55 timer = freeTimerList; 56 freeTimerList = timer->next; 57 } else { 58 timer = new GLUTtimer(); 59 if (!timer) 60 __glutFatalError("out of memory."); 61 } 62 63 timer->func = timerFunc; 64 timer->value = value; 65 timer->next = NULL; 66 timer->timeout = system_time() + (interval*1000); // 1000 ticks in a millisecond 67 prevptr = &__glutTimerList; 68 other = *prevptr; 69 while (other && (other->timeout < timer->timeout)) { 70 prevptr = &other->next; 71 other = *prevptr; 72 } 73 timer->next = other; 74 *prevptr = timer; 75} 76 77/*********************************************************** 78 * FUNCTION: handleTimeouts 79 * 80 * DESCRIPTION: private function to handle outstanding timeouts 81 ***********************************************************/ 82static void 83handleTimeouts(void) 84{ 85 bigtime_t now; 86 GLUTtimer *timer; 87 88 /* Assumption is that __glutTimerList is already determined 89 to be non-NULL. */ 90 now = system_time(); 91 while (__glutTimerList->timeout <= now) { 92 timer = __glutTimerList; 93 if(gState.currentWindow) 94 gState.currentWindow->LockGL(); 95 timer->func(timer->value); 96 if(gState.currentWindow) 97 gState.currentWindow->UnlockGL(); 98 __glutTimerList = timer->next; 99 timer->next = freeTimerList; 100 freeTimerList = timer; 101 if (!__glutTimerList) 102 break; 103 } 104} 105 106 107/*********************************************************** 108 * FUNCTION: processEventsAndTimeouts 109 * 110 * DESCRIPTION: clear gBlock, then check all windows for events 111 ***********************************************************/ 112static void 113processEventsAndTimeouts(void) 114{ 115 gBlock.WaitEvent(); // if there is already an event, returns 116 // immediately, otherwise wait forever 117 gBlock.ClearEvents(); 118 119 if(gState.quitAll) 120 exit(0); // exit handler cleans up windows and quits nicely 121 122 if (gState.currentWindow) 123 gState.currentWindow->LockGL(); 124 for(int i=0; i<gState.windowListSize; i++) { 125 if (gState.windowList[i]) { 126 GlutWindow *win = gState.windowList[i]; 127 // NOTE: we can use win as a shortcut for gState.windowList[i] 128 // in callbacks, EXCEPT we need to check the original variable 129 // after each callback to make sure the window hasn't been destroyed 130 if (win->anyevents) { 131 win->anyevents = false; 132 if (win->reshapeEvent) { 133 win->reshapeEvent = false; 134 __glutSetWindow(win); 135 win->reshape(win->m_width, win->m_height); 136 } 137 if (!gState.windowList[i]) 138 continue; // window was destroyed by callback! 139 140 if (win->displayEvent) { 141 win->displayEvent = false; 142 __glutSetWindow(win); 143 win->display(); 144 } 145 if (!gState.windowList[i]) 146 continue; // window was destroyed by callback! 147 148 if (win->mouseEvent) { 149 win->mouseEvent = false; 150 __glutSetWindow(win); 151 if (win->mouse) { 152 gState.modifierKeys = win->modifierKeys; 153 win->mouse(win->button, win->mouseState, win->mouseX, win->mouseY); 154 gState.modifierKeys = ~0; 155 } 156 } 157 if (!gState.windowList[i]) 158 continue; // window was destroyed by callback! 159 160 if (win->menuEvent) { 161 win->menuEvent = false; 162 __glutSetWindow(win); 163 GlutMenu *menu = __glutGetMenuByNum(win->menuNumber); 164 if (menu) { 165 gState.currentMenu = menu; 166 menu->select(win->menuValue); 167 } 168 } 169 if (!gState.windowList[i]) 170 continue; // window was destroyed by callback! 171 172 if (win->statusEvent) { 173 win->statusEvent = false; 174 __glutSetWindow(win); 175 if (gState.menuStatus) { 176 gState.currentMenu = __glutGetMenuByNum(win->menuNumber); 177 gState.menuStatus(win->menuStatus, win->statusX, win->statusY); 178 } 179 } 180 if (!gState.windowList[i]) 181 continue; // window was destroyed by callback! 182 183 if (win->motionEvent) { 184 win->motionEvent = false; 185 __glutSetWindow(win); 186 if (win->motion) 187 win->motion(win->motionX, win->motionY); 188 } 189 if (!gState.windowList[i]) 190 continue; // window was destroyed by callback! 191 192 if (win->passiveEvent) { 193 win->passiveEvent = false; 194 __glutSetWindow(win); 195 if (win->passive) 196 win->passive(win->passiveX, win->passiveY); 197 } 198 if (!gState.windowList[i]) 199 continue; // window was destroyed by callback! 200 201 if (win->keybEvent) { 202 win->keybEvent = false; 203 __glutSetWindow(win); 204 if (win->keyboard) { 205 gState.modifierKeys = win->modifierKeys; 206 win->keyboard(win->key, win->keyX, win->keyY); 207 gState.modifierKeys = ~0; 208 } 209 } 210 if (!gState.windowList[i]) 211 continue; // window was destroyed by callback! 212 213 if (win->specialEvent) { 214 win->specialEvent = false; 215 __glutSetWindow(win); 216 if (win->special) { 217 gState.modifierKeys = win->modifierKeys; 218 win->special(win->specialKey, win->specialX, win->specialY); 219 gState.modifierKeys = ~0; 220 } 221 } 222 if (!gState.windowList[i]) 223 continue; // window was destroyed by callback! 224 225 if (win->entryEvent) { 226 win->entryEvent = false; 227 __glutSetWindow(win); 228 if (win->entry) 229 win->entry(win->entryState); 230 } 231 if (!gState.windowList[i]) 232 continue; // window was destroyed by callback! 233 234 if (win->windowStatusEvent) { 235 win->windowStatusEvent = false; 236 __glutSetWindow(win); 237 if (win->windowStatus) 238 win->windowStatus(win->visState); 239 } 240 if (!gState.windowList[i]) 241 continue; // window was destroyed by callback! 242 } 243 } 244 } 245 if (gState.currentWindow) 246 gState.currentWindow->UnlockGL(); 247 248 // This code isn't necessary since BGLView automatically traps errors 249#if 0 250 if(gState.debug) { 251 for(int i=0; i<gState.windowListSize; i++) { 252 if (gState.windowList[i]) { 253 gState.windowList[i]->LockGL(); 254 glutReportErrors(); 255 gState.windowList[i]->UnlockGL(); 256 } 257 } 258 } 259#endif 260 if (__glutTimerList) { 261 handleTimeouts(); 262 } 263} 264 265/*********************************************************** 266 * FUNCTION: waitForSomething 267 * 268 * DESCRIPTION: use gBlock to wait for a new event or timeout 269 ***********************************************************/ 270static void 271waitForSomething(void) 272{ 273 bigtime_t timeout = __glutTimerList->timeout; 274 bigtime_t now = system_time(); 275 276 if (gBlock.PendingEvent()) 277 goto immediatelyHandleEvent; 278 279 if(timeout>now) 280 gBlock.WaitEvent(timeout-now); 281 if (gBlock.PendingEvent()) { 282 immediatelyHandleEvent: 283 processEventsAndTimeouts(); 284 } else { 285 if (__glutTimerList) 286 handleTimeouts(); 287 } 288} 289 290/*********************************************************** 291 * FUNCTION: idleWait 292 * 293 * DESCRIPTION: check for events, then call idle function 294 ***********************************************************/ 295static void 296idleWait(void) 297{ 298 if (gBlock.PendingEvent()) { 299 processEventsAndTimeouts(); 300 } else { 301 if (__glutTimerList) 302 handleTimeouts(); 303 } 304 /* Make sure idle func still exists! */ 305 if(gState.currentWindow) 306 gState.currentWindow->LockGL(); 307 if (gState.idle) { 308 gState.idle(); 309 } 310 if(gState.currentWindow) 311 gState.currentWindow->UnlockGL(); 312} 313 314/*********************************************************** 315 * FUNCTION: glutMainLoop (3.1) 316 * 317 * DESCRIPTION: enter the event processing loop 318 ***********************************************************/ 319void glutMainLoop() 320{ 321 if (!gState.windowListSize) 322 __glutFatalUsage("main loop entered with no windows created."); 323 324 if(gState.currentWindow) 325 gState.currentWindow->UnlockGL(); 326 327 for (;;) { 328 if (gState.idle) { 329 idleWait(); 330 } else { 331 if (__glutTimerList) { 332 waitForSomething(); 333 } else { 334 processEventsAndTimeouts(); 335 } 336 } 337 } 338} 339 340/*********************************************************** 341 * CLASS: GlutWindow 342 * 343 * FUNCTION: KeyDown 344 * 345 * DESCRIPTION: handles keyboard and special events 346 ***********************************************************/ 347void GlutWindow::KeyDown(const char *s, int32 slen) 348{ 349 ulong aChar = s[0]; 350 BGLView::KeyDown(s,slen); 351 352 BPoint p; 353 354 switch (aChar) { 355 case B_FUNCTION_KEY: 356 switch(Window()->CurrentMessage()->FindInt32("key")) { 357 case B_F1_KEY: 358 aChar = GLUT_KEY_F1; 359 goto specialLabel; 360 case B_F2_KEY: 361 aChar = GLUT_KEY_F2; 362 goto specialLabel; 363 case B_F3_KEY: 364 aChar = GLUT_KEY_F3; 365 goto specialLabel; 366 case B_F4_KEY: 367 aChar = GLUT_KEY_F4; 368 goto specialLabel; 369 case B_F5_KEY: 370 aChar = GLUT_KEY_F5; 371 goto specialLabel; 372 case B_F6_KEY: 373 aChar = GLUT_KEY_F6; 374 goto specialLabel; 375 case B_F7_KEY: 376 aChar = GLUT_KEY_F7; 377 goto specialLabel; 378 case B_F8_KEY: 379 aChar = GLUT_KEY_F8; 380 goto specialLabel; 381 case B_F9_KEY: 382 aChar = GLUT_KEY_F9; 383 goto specialLabel; 384 case B_F10_KEY: 385 aChar = GLUT_KEY_F10; 386 goto specialLabel; 387 case B_F11_KEY: 388 aChar = GLUT_KEY_F11; 389 goto specialLabel; 390 case B_F12_KEY: 391 aChar = GLUT_KEY_F12; 392 goto specialLabel; 393 default: 394 return; 395 } 396 case B_LEFT_ARROW: 397 aChar = GLUT_KEY_LEFT; 398 goto specialLabel; 399 case B_UP_ARROW: 400 aChar = GLUT_KEY_UP; 401 goto specialLabel; 402 case B_RIGHT_ARROW: 403 aChar = GLUT_KEY_RIGHT; 404 goto specialLabel; 405 case B_DOWN_ARROW: 406 aChar = GLUT_KEY_DOWN; 407 goto specialLabel; 408 case B_PAGE_UP: 409 aChar = GLUT_KEY_PAGE_UP; 410 goto specialLabel; 411 case B_PAGE_DOWN: 412 aChar = GLUT_KEY_PAGE_DOWN; 413 goto specialLabel; 414 case B_HOME: 415 aChar = GLUT_KEY_HOME; 416 goto specialLabel; 417 case B_END: 418 aChar = GLUT_KEY_END; 419 goto specialLabel; 420 case B_INSERT: 421 aChar = GLUT_KEY_INSERT; 422specialLabel: 423 if (special) { 424 anyevents = specialEvent = true; 425 GetMouse(&p,&m_buttons); 426 specialKey = aChar; 427 specialX = (int)p.x; 428 specialY = (int)p.y; 429 goto setModifiers; // set the modifier variable 430 } 431 return; 432 433 default: 434 break; 435 } 436 437 if (keyboard) { 438 anyevents = keybEvent = true; 439 GetMouse(&p,&m_buttons); 440 key = aChar; 441 keyX = (int)p.x; 442 keyY = (int)p.y; 443setModifiers: 444 modifierKeys = 0; 445 uint32 beMod = Window()->CurrentMessage()->FindInt32("modifiers"); 446 if(beMod & B_SHIFT_KEY) 447 modifierKeys |= GLUT_ACTIVE_SHIFT; 448 if(beMod & B_CONTROL_KEY) 449 modifierKeys |= GLUT_ACTIVE_CTRL; 450 if(beMod & B_OPTION_KEY) { 451 // since the window traps B_COMMAND_KEY, we'll have to settle 452 // for the option key.. but we need to get the raw character, 453 // not the Unicode-enhanced version 454 key = Window()->CurrentMessage()->FindInt32("raw_char"); 455 modifierKeys |= GLUT_ACTIVE_ALT; 456 } 457 gBlock.NewEvent(); 458 } 459} 460 461/*********************************************************** 462 * CLASS: GlutWindow 463 * 464 * FUNCTION: MouseDown 465 * 466 * DESCRIPTION: handles mouse and menustatus events 467 ***********************************************************/ 468void GlutWindow::MouseDown(BPoint point) 469{ 470 BGLView::MouseDown(point); 471 MouseCheck(); 472} 473 474/*********************************************************** 475 * CLASS: GlutWindow 476 * 477 * FUNCTION: MouseCheck 478 * 479 * DESCRIPTION: checks for button state changes 480 ***********************************************************/ 481void GlutWindow::MouseCheck() 482{ 483 if (mouseEvent) 484 return; // we already have an outstanding mouse event 485 486 BPoint point; 487 uint32 newButtons; 488 GetMouse(&point, &newButtons); 489 if (m_buttons != newButtons) { 490 if (newButtons&B_PRIMARY_MOUSE_BUTTON && !(m_buttons&B_PRIMARY_MOUSE_BUTTON)) { 491 button = GLUT_LEFT_BUTTON; 492 mouseState = GLUT_DOWN; 493 } else if (m_buttons&B_PRIMARY_MOUSE_BUTTON && !(newButtons&B_PRIMARY_MOUSE_BUTTON)) { 494 button = GLUT_LEFT_BUTTON; 495 mouseState = GLUT_UP; 496 } else if (newButtons&B_SECONDARY_MOUSE_BUTTON && !(m_buttons&B_SECONDARY_MOUSE_BUTTON)) { 497 button = GLUT_RIGHT_BUTTON; 498 mouseState = GLUT_DOWN; 499 } else if (m_buttons&B_SECONDARY_MOUSE_BUTTON && !(newButtons&B_SECONDARY_MOUSE_BUTTON)) { 500 button = GLUT_RIGHT_BUTTON; 501 mouseState = GLUT_UP; 502 } else if (newButtons&B_TERTIARY_MOUSE_BUTTON && !(m_buttons&B_TERTIARY_MOUSE_BUTTON)) { 503 button = GLUT_MIDDLE_BUTTON; 504 mouseState = GLUT_DOWN; 505 } else if (m_buttons&B_TERTIARY_MOUSE_BUTTON && !(newButtons&B_TERTIARY_MOUSE_BUTTON)) { 506 button = GLUT_MIDDLE_BUTTON; 507 mouseState = GLUT_UP; 508 } 509 } else { 510 return; // no change, return 511 } 512 m_buttons = newButtons; 513 514 if (mouseState == GLUT_DOWN) { 515 BWindow *w = Window(); 516 GlutMenu *m = __glutGetMenuByNum(menu[button]); 517 if (m) { 518 if (gState.menuStatus) { 519 anyevents = statusEvent = true; 520 menuNumber = menu[button]; 521 menuStatus = GLUT_MENU_IN_USE; 522 statusX = (int)point.x; 523 statusY = (int)point.y; 524 gBlock.NewEvent(); 525 } 526 BRect bounds = w->Frame(); 527 point.x += bounds.left; 528 point.y += bounds.top; 529 GlutPopUp *bmenu = static_cast<GlutPopUp*>(m->CreateBMenu()); // start menu 530 bmenu->point = point; 531 bmenu->win = this; 532 thread_id menu_thread = spawn_thread(MenuThread, "menu thread", B_NORMAL_PRIORITY, bmenu); 533 resume_thread(menu_thread); 534 return; 535 } 536 } 537 538 if (mouse) { 539 anyevents = mouseEvent = true; 540 mouseX = (int)point.x; 541 mouseY = (int)point.y; 542 modifierKeys = 0; 543 uint32 beMod = modifiers(); 544 if(beMod & B_SHIFT_KEY) 545 modifierKeys |= GLUT_ACTIVE_SHIFT; 546 if(beMod & B_CONTROL_KEY) 547 modifierKeys |= GLUT_ACTIVE_CTRL; 548 if(beMod & B_OPTION_KEY) { 549 modifierKeys |= GLUT_ACTIVE_ALT; 550 } 551 gBlock.NewEvent(); 552 } 553} 554 555/*********************************************************** 556 * CLASS: GlutWindow 557 * 558 * FUNCTION: MouseMoved 559 * 560 * DESCRIPTION: handles entry, motion, and passive events 561 ***********************************************************/ 562void GlutWindow::MouseMoved(BPoint point, 563 ulong transit, const BMessage *msg) 564{ 565 BGLView::MouseMoved(point,transit,msg); 566 567 if(transit != B_INSIDE_VIEW) { 568 if (entry) { 569 anyevents = entryEvent = true; 570 gBlock.NewEvent(); 571 } 572 if (transit == B_ENTERED_VIEW) { 573 entryState = GLUT_ENTERED; 574 MakeFocus(); // make me the current focus 575 __glutSetCursor(cursor); 576 } else 577 entryState = GLUT_LEFT; 578 } 579 580 MouseCheck(); 581 if(m_buttons) { 582 if(motion) { 583 anyevents = motionEvent = true; 584 motionX = (int)point.x; 585 motionY = (int)point.y; 586 gBlock.NewEvent(); 587 } 588 } else { 589 if(passive) { 590 anyevents = passiveEvent = true; 591 passiveX = (int)point.x; 592 passiveY = (int)point.y; 593 gBlock.NewEvent(); 594 } 595 } 596} 597 598/*********************************************************** 599 * CLASS: GlutWindow 600 * 601 * FUNCTION: FrameResized 602 * 603 * DESCRIPTION: handles reshape event 604 ***********************************************************/ 605void GlutWindow::FrameResized(float width, float height) 606{ 607 BGLView::FrameResized(width, height); 608 if (visible) { 609 anyevents = reshapeEvent = true; 610 m_width = (int)(width)+1; 611 m_height = (int)(height)+1; 612 gBlock.NewEvent(); 613 } 614} 615 616/*********************************************************** 617 * CLASS: GlutWindow 618 * 619 * FUNCTION: Draw 620 * 621 * DESCRIPTION: handles reshape and display events 622 ***********************************************************/ 623void GlutWindow::Draw(BRect updateRect) 624{ 625 BGLView::Draw(updateRect); 626 BRect frame = Frame(); 627 if (m_width != (frame.Width()+1) || m_height != (frame.Height()+1)) { 628 FrameResized(frame.Width(), frame.Height()); 629 } 630 Window()->Lock(); 631 if (visible) { 632 anyevents = displayEvent = true; 633 gBlock.NewEvent(); 634 } 635 Window()->Unlock(); 636} 637 638/*********************************************************** 639 * CLASS: GlutWindow 640 * 641 * FUNCTION: Pulse 642 * 643 * DESCRIPTION: handles mouse up event (MouseUp is broken) 644 ***********************************************************/ 645void GlutWindow::Pulse() 646{ 647 BGLView::Pulse(); 648 if (m_buttons) { // if there are buttons pressed 649 MouseCheck(); 650 } 651} 652 653/*********************************************************** 654 * CLASS: GlutWindow 655 * 656 * FUNCTION: ErrorCallback 657 * 658 * DESCRIPTION: handles GL error messages 659 ***********************************************************/ 660void GlutWindow::ErrorCallback(GLenum errorCode) { 661 __glutWarning("GL error: %s", gluErrorString(errorCode)); 662} 663 664/*********************************************************** 665 * CLASS: GlutWindow 666 * 667 * FUNCTION: MenuThread 668 * 669 * DESCRIPTION: a new thread to launch popup menu, wait 670 * wait for response, then clean up afterwards and 671 * send appropriate messages 672 ***********************************************************/ 673long GlutWindow::MenuThread(void *m) { 674 GlutPopUp *bmenu = static_cast<GlutPopUp*>(m); 675 GlutWindow *win = bmenu->win; // my window 676 GlutBMenuItem *result = (GlutBMenuItem*)bmenu->Go(bmenu->point); 677 win->Window()->Lock(); 678 win->anyevents = win->statusEvent = true; 679 win->menuStatus = GLUT_MENU_NOT_IN_USE; 680 win->menuNumber = bmenu->menu; 681 BPoint cursor; 682 uint32 buttons; 683 win->GetMouse(&cursor, &buttons); 684 win->statusX = (int)cursor.x; 685 win->statusY = (int)cursor.y; 686 if(result && result->menu) { 687 win->menuEvent = true; 688 win->menuNumber = result->menu; // in case it was a submenu 689 win->menuValue = result->value; 690 } 691 win->Window()->Unlock(); 692 gBlock.NewEvent(); 693 delete bmenu; 694 return 0; 695} 696