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