1 /***********************************************************
2 * Copyright (C) 1997, Be Inc. All rights reserved.
6 * DESCRIPTION: here it is, the BeOS GLUT event loop
7 ***********************************************************/
9 /***********************************************************
11 ***********************************************************/
14 #include "glutState.h"
15 #include "glutBlocker.h"
17 /***********************************************************
20 * DESCRIPTION: list of timer callbacks
21 ***********************************************************/
23 GLUTtimer
*next
; // list of timers
24 bigtime_t timeout
; // time to be called
25 GLUTtimerCB func
; // function to call
29 /***********************************************************
31 ***********************************************************/
32 static GLUTtimer
*__glutTimerList
= 0; // list of timer callbacks
33 static GLUTtimer
*freeTimerList
= 0;
35 /***********************************************************
36 * FUNCTION: glutTimerFunc (7.19)
38 * DESCRIPTION: register a new timer callback
39 ***********************************************************/
41 glutTimerFunc(unsigned int interval
, GLUTtimerCB timerFunc
, int value
)
43 GLUTtimer
*timer
, *other
;
50 timer
= freeTimerList
;
51 freeTimerList
= timer
->next
;
53 timer
= new GLUTtimer();
55 __glutFatalError("out of memory.");
58 timer
->func
= timerFunc
;
61 timer
->timeout
= system_time() + (interval
*1000); // 1000 ticks in a millisecond
62 prevptr
= &__glutTimerList
;
64 while (other
&& (other
->timeout
< timer
->timeout
)) {
65 prevptr
= &other
->next
;
72 /***********************************************************
73 * FUNCTION: handleTimeouts
75 * DESCRIPTION: private function to handle outstanding timeouts
76 ***********************************************************/
83 /* Assumption is that __glutTimerList is already determined
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
;
102 /***********************************************************
103 * FUNCTION: processEventsAndTimeouts
105 * DESCRIPTION: clear gBlock, then check all windows for events
106 ***********************************************************/
108 processEventsAndTimeouts(void)
110 gBlock
.WaitEvent(); // if there is already an event, returns
111 // immediately, otherwise wait forever
112 gBlock
.ClearEvents();
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
);
130 if (!gState
.windowList
[i
])
131 continue; // window was destroyed by callback!
133 if (win
->displayEvent
) {
134 win
->displayEvent
= false;
135 __glutSetWindow(win
);
137 if (gState
.windowList
[i
] && win
->swapHack
) {
138 // fake single buffering by swapping buffers
139 __glutSetWindow(win
);
143 if (!gState
.windowList
[i
])
144 continue; // window was destroyed by callback!
146 if (win
->mouseEvent
) {
147 win
->mouseEvent
= false;
148 __glutSetWindow(win
);
150 gState
.modifierKeys
= win
->modifierKeys
;
151 win
->mouse(win
->button
, win
->mouseState
, win
->mouseX
, win
->mouseY
);
152 gState
.modifierKeys
= ~0;
155 if (!gState
.windowList
[i
])
156 continue; // window was destroyed by callback!
158 if (win
->menuEvent
) {
159 win
->menuEvent
= false;
160 __glutSetWindow(win
);
161 GlutMenu
*menu
= __glutGetMenuByNum(win
->menuNumber
);
163 gState
.currentMenu
= menu
;
164 menu
->select(win
->menuValue
);
167 if (!gState
.windowList
[i
])
168 continue; // window was destroyed by callback!
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
);
178 if (!gState
.windowList
[i
])
179 continue; // window was destroyed by callback!
181 if (win
->motionEvent
) {
182 win
->motionEvent
= false;
183 __glutSetWindow(win
);
185 win
->motion(win
->motionX
, win
->motionY
);
187 if (!gState
.windowList
[i
])
188 continue; // window was destroyed by callback!
190 if (win
->passiveEvent
) {
191 win
->passiveEvent
= false;
192 __glutSetWindow(win
);
194 win
->passive(win
->passiveX
, win
->passiveY
);
196 if (!gState
.windowList
[i
])
197 continue; // window was destroyed by callback!
199 if (win
->keybEvent
) {
200 win
->keybEvent
= false;
201 __glutSetWindow(win
);
203 gState
.modifierKeys
= win
->modifierKeys
;
204 win
->keyboard(win
->key
, win
->keyX
, win
->keyY
);
205 gState
.modifierKeys
= ~0;
208 if (!gState
.windowList
[i
])
209 continue; // window was destroyed by callback!
211 if (win
->specialEvent
) {
212 win
->specialEvent
= false;
213 __glutSetWindow(win
);
215 gState
.modifierKeys
= win
->modifierKeys
;
216 win
->special(win
->specialKey
, win
->specialX
, win
->specialY
);
217 gState
.modifierKeys
= ~0;
220 if (!gState
.windowList
[i
])
221 continue; // window was destroyed by callback!
223 if (win
->entryEvent
) {
224 win
->entryEvent
= false;
225 __glutSetWindow(win
);
227 win
->entry(win
->entryState
);
229 if (!gState
.windowList
[i
])
230 continue; // window was destroyed by callback!
233 win
->visEvent
= false;
234 __glutSetWindow(win
);
236 win
->visibility(win
->visState
);
238 if (!gState
.windowList
[i
])
239 continue; // window was destroyed by callback!
241 if(gState
.windowList
[i
]) // window hasn't been destroyed
242 win
->Window()->Unlock();
245 if (gState
.currentWindow
)
246 gState
.currentWindow
->UnlockGL();
248 // This code isn't necessary since BGLView automatically traps errors
251 for(int i
=0; i
<gState
.windowListSize
; i
++) {
252 if (gState
.windowList
[i
]) {
253 gState
.windowList
[i
]->LockGL();
255 gState
.windowList
[i
]->UnlockGL();
260 if (__glutTimerList
) {
265 /***********************************************************
266 * FUNCTION: waitForSomething
268 * DESCRIPTION: use gBlock to wait for a new event or timeout
269 ***********************************************************/
271 waitForSomething(void)
273 bigtime_t timeout
= __glutTimerList
->timeout
;
274 bigtime_t now
= system_time();
276 if (gBlock
.PendingEvent())
277 goto immediatelyHandleEvent
;
280 gBlock
.WaitEvent(timeout
-now
);
281 if (gBlock
.PendingEvent()) {
282 immediatelyHandleEvent
:
283 processEventsAndTimeouts();
290 /***********************************************************
293 * DESCRIPTION: check for events, then call idle function
294 ***********************************************************/
298 if (gBlock
.PendingEvent()) {
299 processEventsAndTimeouts();
304 /* Make sure idle func still exists! */
305 if(gState
.currentWindow
)
306 gState
.currentWindow
->LockGL();
309 if(gState
.currentWindow
)
310 gState
.currentWindow
->UnlockGL();
313 /***********************************************************
314 * FUNCTION: glutMainLoop (3.1)
316 * DESCRIPTION: enter the event processing loop
317 ***********************************************************/
320 if (!gState
.windowListSize
)
321 __glutFatalUsage("main loop entered with no windows created.");
323 if(gState
.currentWindow
)
324 gState
.currentWindow
->UnlockGL();
330 if (__glutTimerList
) {
333 processEventsAndTimeouts();
339 /***********************************************************
344 * DESCRIPTION: handles keyboard and special events
345 ***********************************************************/
346 void GlutWindow::KeyDown(const char *s
, int32 slen
)
349 BGLView::KeyDown(s
,slen
);
355 switch(Window()->CurrentMessage()->FindInt32("key")) {
384 aChar
= GLUT_KEY_F10
;
387 aChar
= GLUT_KEY_F11
;
390 aChar
= GLUT_KEY_F12
;
396 aChar
= GLUT_KEY_LEFT
;
402 aChar
= GLUT_KEY_RIGHT
;
405 aChar
= GLUT_KEY_DOWN
;
408 aChar
= GLUT_KEY_PAGE_UP
;
411 aChar
= GLUT_KEY_PAGE_DOWN
;
414 aChar
= GLUT_KEY_HOME
;
417 aChar
= GLUT_KEY_END
;
420 aChar
= GLUT_KEY_INSERT
;
423 anyevents
= specialEvent
= true;
424 GetMouse(&p
,&m_buttons
);
428 goto setModifiers
; // set the modifier variable
437 anyevents
= keybEvent
= true;
438 GetMouse(&p
,&m_buttons
);
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
;
460 /***********************************************************
463 * FUNCTION: MouseDown
465 * DESCRIPTION: handles mouse and menustatus events
466 ***********************************************************/
467 void GlutWindow::MouseDown(BPoint point
)
469 BGLView::MouseDown(point
);
473 /***********************************************************
476 * FUNCTION: MouseCheck
478 * DESCRIPTION: checks for button state changes
479 ***********************************************************/
480 void GlutWindow::MouseCheck()
483 return; // we already have an outstanding mouse event
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
;
509 return; // no change, return
511 m_buttons
= newButtons
;
513 if (mouseState
== GLUT_DOWN
) {
514 BWindow
*w
= Window();
515 GlutMenu
*m
= __glutGetMenuByNum(menu
[button
]);
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
;
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
;
531 thread_id menu_thread
= spawn_thread(MenuThread
, "menu thread", B_NORMAL_PRIORITY
, bmenu
);
532 resume_thread(menu_thread
);
538 anyevents
= mouseEvent
= true;
539 mouseX
= (int)point
.x
;
540 mouseY
= (int)point
.y
;
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
;
554 /***********************************************************
557 * FUNCTION: MouseMoved
559 * DESCRIPTION: handles entry, motion, and passive events
560 ***********************************************************/
561 void GlutWindow::MouseMoved(BPoint point
,
562 ulong transit
, const BMessage
*msg
)
564 BGLView::MouseMoved(point
,transit
,msg
);
566 if(transit
!= B_INSIDE_VIEW
) {
568 anyevents
= entryEvent
= true;
571 if (transit
== B_ENTERED_VIEW
) {
572 entryState
= GLUT_ENTERED
;
573 MakeFocus(); // make me the current focus
574 __glutSetCursor(cursor
);
576 entryState
= GLUT_LEFT
;
582 anyevents
= motionEvent
= true;
583 motionX
= (int)point
.x
;
584 motionY
= (int)point
.y
;
589 anyevents
= passiveEvent
= true;
590 passiveX
= (int)point
.x
;
591 passiveY
= (int)point
.y
;
597 /***********************************************************
600 * FUNCTION: FrameResized
602 * DESCRIPTION: handles reshape event
603 ***********************************************************/
604 void GlutWindow::FrameResized(float width
, float height
)
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;
615 /***********************************************************
620 * DESCRIPTION: handles reshape and display events
621 ***********************************************************/
622 void GlutWindow::Draw(BRect updateRect
)
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());
629 if (visState
== GLUT_VISIBLE
) {
630 anyevents
= displayEvent
= true;
635 /***********************************************************
641 * DESCRIPTION: handles visibility event
642 ***********************************************************/
643 void GlutWindow::Hide()
647 anyevents
= visEvent
= true;
648 visState
= GLUT_NOT_VISIBLE
;
649 displayEvent
= false; // display callbacks not allowed when hidden
654 void GlutWindow::Show()
658 anyevents
= visEvent
= true;
659 visState
= GLUT_VISIBLE
;
664 /***********************************************************
669 * DESCRIPTION: handles mouse up event (MouseUp is broken)
670 ***********************************************************/
671 void GlutWindow::Pulse()
674 if (m_buttons
) { // if there are buttons pressed
679 /***********************************************************
682 * FUNCTION: ErrorCallback
684 * DESCRIPTION: handles GL error messages
685 ***********************************************************/
686 void GlutWindow::ErrorCallback(GLenum errorCode
) {
687 __glutWarning("GL error: %s", gluErrorString(errorCode
));
690 /***********************************************************
693 * FUNCTION: MenuThread
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
;
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
;
717 win
->Window()->Unlock();