1 /***********************************************************
2 * Copyright (C) 1997, Be Inc. Copyright (C) 1999, Jake Hamby.
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.
11 * DESCRIPTION: here it is, the BeOS GLUT event loop
12 ***********************************************************/
14 /***********************************************************
16 ***********************************************************/
19 #include "glutState.h"
20 #include "glutBlocker.h"
24 #define MOUSE_WHEEL_UP 3
25 #define MOUSE_WHEEL_DOWN 4
27 /***********************************************************
30 * DESCRIPTION: list of timer callbacks
31 ***********************************************************/
33 GLUTtimer
*next
; // list of timers
34 bigtime_t timeout
; // time to be called
35 GLUTtimerCB func
; // function to call
39 /***********************************************************
41 ***********************************************************/
42 static GLUTtimer
*__glutTimerList
= 0; // list of timer callbacks
43 static GLUTtimer
*freeTimerList
= 0;
45 /***********************************************************
46 * FUNCTION: glutTimerFunc (7.19)
48 * DESCRIPTION: register a new timer callback
49 ***********************************************************/
51 glutTimerFunc(unsigned int interval
, GLUTtimerCB timerFunc
, int value
)
53 GLUTtimer
*timer
, *other
;
60 timer
= freeTimerList
;
61 freeTimerList
= timer
->next
;
63 timer
= new GLUTtimer();
65 __glutFatalError("out of memory.");
68 timer
->func
= timerFunc
;
71 timer
->timeout
= system_time() + (interval
*1000); // 1000 ticks in a millisecond
72 prevptr
= &__glutTimerList
;
74 while (other
&& (other
->timeout
< timer
->timeout
)) {
75 prevptr
= &other
->next
;
82 /***********************************************************
83 * FUNCTION: handleTimeouts
85 * DESCRIPTION: private function to handle outstanding timeouts
86 ***********************************************************/
93 /* Assumption is that __glutTimerList is already determined
96 while (__glutTimerList
->timeout
<= now
) {
97 timer
= __glutTimerList
;
98 if(gState
.currentWindow
)
99 gState
.currentWindow
->LockGL();
100 timer
->func(timer
->value
);
101 if(gState
.currentWindow
)
102 gState
.currentWindow
->UnlockGL();
103 __glutTimerList
= timer
->next
;
104 timer
->next
= freeTimerList
;
105 freeTimerList
= timer
;
106 if (!__glutTimerList
)
112 /***********************************************************
113 * FUNCTION: processEventsAndTimeouts
115 * DESCRIPTION: clear gBlock, then check all windows for events
116 ***********************************************************/
118 processEventsAndTimeouts(void)
120 gBlock
.WaitEvent(); // if there is already an event, returns
121 // immediately, otherwise wait forever
122 gBlock
.ClearEvents();
125 exit(0); // exit handler cleans up windows and quits nicely
127 if (gState
.currentWindow
)
128 gState
.currentWindow
->LockGL();
129 for(int i
=0; i
<gState
.windowListSize
; i
++) {
130 if (gState
.windowList
[i
]) {
131 GlutWindow
*win
= gState
.windowList
[i
];
132 // NOTE: we can use win as a shortcut for gState.windowList[i]
133 // in callbacks, EXCEPT we need to check the original variable
134 // after each callback to make sure the window hasn't been destroyed
135 if (win
->anyevents
) {
136 win
->anyevents
= false;
137 if (win
->reshapeEvent
) {
138 win
->reshapeEvent
= false;
139 __glutSetWindow(win
);
140 win
->reshape(win
->m_width
, win
->m_height
);
142 if (!gState
.windowList
[i
])
143 continue; // window was destroyed by callback!
145 if (win
->displayEvent
) {
146 win
->displayEvent
= false;
147 __glutSetWindow(win
);
150 if (!gState
.windowList
[i
])
151 continue; // window was destroyed by callback!
153 if (win
->mouseEvent
) {
154 win
->mouseEvent
= false;
155 __glutSetWindow(win
);
157 gState
.modifierKeys
= win
->modifierKeys
;
158 win
->mouse(win
->button
, win
->mouseState
, win
->mouseX
, win
->mouseY
);
159 gState
.modifierKeys
= ~0;
162 if (!gState
.windowList
[i
])
163 continue; // window was destroyed by callback!
165 if (win
->menuEvent
) {
166 win
->menuEvent
= false;
167 __glutSetWindow(win
);
168 GlutMenu
*menu
= __glutGetMenuByNum(win
->menuNumber
);
170 gState
.currentMenu
= menu
;
171 menu
->select(win
->menuValue
);
174 if (!gState
.windowList
[i
])
175 continue; // window was destroyed by callback!
177 if (win
->statusEvent
) {
178 win
->statusEvent
= false;
179 __glutSetWindow(win
);
180 if (gState
.menuStatus
) {
181 gState
.currentMenu
= __glutGetMenuByNum(win
->menuNumber
);
182 gState
.menuStatus(win
->menuStatus
, win
->statusX
, win
->statusY
);
185 if (!gState
.windowList
[i
])
186 continue; // window was destroyed by callback!
188 if (win
->motionEvent
) {
189 win
->motionEvent
= false;
190 __glutSetWindow(win
);
192 win
->motion(win
->motionX
, win
->motionY
);
194 if (!gState
.windowList
[i
])
195 continue; // window was destroyed by callback!
197 if (win
->passiveEvent
) {
198 win
->passiveEvent
= false;
199 __glutSetWindow(win
);
201 win
->passive(win
->passiveX
, win
->passiveY
);
203 if (!gState
.windowList
[i
])
204 continue; // window was destroyed by callback!
206 if (win
->keybEvent
) {
207 win
->keybEvent
= false;
208 __glutSetWindow(win
);
210 gState
.modifierKeys
= win
->modifierKeys
;
211 win
->keyboard(win
->key
, win
->keyX
, win
->keyY
);
212 gState
.modifierKeys
= ~0;
215 if (!gState
.windowList
[i
])
216 continue; // window was destroyed by callback!
218 if (win
->specialEvent
) {
219 win
->specialEvent
= false;
220 __glutSetWindow(win
);
222 gState
.modifierKeys
= win
->modifierKeys
;
223 win
->special(win
->specialKey
, win
->specialX
, win
->specialY
);
224 gState
.modifierKeys
= ~0;
227 if (!gState
.windowList
[i
])
228 continue; // window was destroyed by callback!
230 if (win
->keybUpEvent
) {
231 win
->keybUpEvent
= false;
232 __glutSetWindow(win
);
233 if (win
->keyboardUp
) {
234 gState
.modifierKeys
= win
->modifierKeys
;
235 win
->keyboardUp(win
->key
, win
->keyX
, win
->keyY
);
236 gState
.modifierKeys
= ~0;
239 if (!gState
.windowList
[i
])
240 continue; // window was destroyed by callback!
242 if (win
->specialUpEvent
) {
243 win
->specialUpEvent
= false;
244 __glutSetWindow(win
);
245 if (win
->specialUp
) {
246 gState
.modifierKeys
= win
->modifierKeys
;
247 win
->specialUp(win
->specialKey
, win
->specialX
, win
->specialY
);
248 gState
.modifierKeys
= ~0;
251 if (!gState
.windowList
[i
])
252 continue; // window was destroyed by callback!
255 if (win
->entryEvent
) {
256 win
->entryEvent
= false;
257 __glutSetWindow(win
);
259 win
->entry(win
->entryState
);
261 if (!gState
.windowList
[i
])
262 continue; // window was destroyed by callback!
264 if (win
->windowStatusEvent
) {
265 win
->windowStatusEvent
= false;
266 __glutSetWindow(win
);
267 if (win
->windowStatus
)
268 win
->windowStatus(win
->visState
);
270 if (!gState
.windowList
[i
])
271 continue; // window was destroyed by callback!
275 if (gState
.currentWindow
)
276 gState
.currentWindow
->UnlockGL();
278 // This code isn't necessary since BGLView automatically traps errors
281 for(int i
=0; i
<gState
.windowListSize
; i
++) {
282 if (gState
.windowList
[i
]) {
283 gState
.windowList
[i
]->LockGL();
285 gState
.windowList
[i
]->UnlockGL();
290 if (__glutTimerList
) {
295 /***********************************************************
296 * FUNCTION: waitForSomething
298 * DESCRIPTION: use gBlock to wait for a new event or timeout
299 ***********************************************************/
301 waitForSomething(void)
303 bigtime_t timeout
= __glutTimerList
->timeout
;
304 bigtime_t now
= system_time();
306 if (gBlock
.PendingEvent())
307 goto immediatelyHandleEvent
;
310 gBlock
.WaitEvent(timeout
-now
);
311 if (gBlock
.PendingEvent()) {
312 immediatelyHandleEvent
:
313 processEventsAndTimeouts();
320 /***********************************************************
323 * DESCRIPTION: check for events, then call idle function
324 ***********************************************************/
328 if (gBlock
.PendingEvent()) {
329 processEventsAndTimeouts();
334 /* Make sure idle func still exists! */
335 if(gState
.currentWindow
)
336 gState
.currentWindow
->LockGL();
340 if(gState
.currentWindow
)
341 gState
.currentWindow
->UnlockGL();
344 /***********************************************************
345 * FUNCTION: glutMainLoop (3.1)
347 * DESCRIPTION: enter the event processing loop
348 ***********************************************************/
351 if (!gState
.windowListSize
)
352 __glutFatalUsage("main loop entered with no windows created.");
354 if(gState
.currentWindow
)
355 gState
.currentWindow
->UnlockGL();
361 if (__glutTimerList
) {
364 processEventsAndTimeouts();
371 void glutSetKeyRepeat(int repeatMode
)
374 case GLUT_KEY_REPEAT_DEFAULT
:
375 gState
.keyRepeatMode
= GLUT_KEY_REPEAT_ON
;
378 case GLUT_KEY_REPEAT_ON
:
379 case GLUT_KEY_REPEAT_OFF
:
380 gState
.keyRepeatMode
= repeatMode
;
384 __glutWarning("invalid glutSetKeyRepeat mode: %d", repeatMode
);
389 void glutIgnoreKeyRepeat(int ignore
)
391 if (gState
.currentWindow
)
392 gState
.currentWindow
->ignoreKeyRepeat
= (ignore
!= 0);
396 /***********************************************************
401 * DESCRIPTION: handles keyboard and special events
402 ***********************************************************/
403 void GlutWindow::KeyDown(const char *s
, int32 slen
)
406 BGLView::KeyDown(s
,slen
);
411 && Window()->CurrentMessage()->FindInt32("be:key_repeat") > 0)
416 switch(Window()->CurrentMessage()->FindInt32("key")) {
445 aChar
= GLUT_KEY_F10
;
448 aChar
= GLUT_KEY_F11
;
451 aChar
= GLUT_KEY_F12
;
457 aChar
= GLUT_KEY_LEFT
;
463 aChar
= GLUT_KEY_RIGHT
;
466 aChar
= GLUT_KEY_DOWN
;
469 aChar
= GLUT_KEY_PAGE_UP
;
472 aChar
= GLUT_KEY_PAGE_DOWN
;
475 aChar
= GLUT_KEY_HOME
;
478 aChar
= GLUT_KEY_END
;
481 aChar
= GLUT_KEY_INSERT
;
484 anyevents
= specialEvent
= true;
485 GetMouse(&p
,&m_buttons
);
489 goto setModifiers
; // set the modifier variable
498 anyevents
= keybEvent
= true;
499 GetMouse(&p
,&m_buttons
);
505 uint32 beMod
= Window()->CurrentMessage()->FindInt32("modifiers");
506 if(beMod
& B_SHIFT_KEY
)
507 modifierKeys
|= GLUT_ACTIVE_SHIFT
;
508 if(beMod
& B_CONTROL_KEY
)
509 modifierKeys
|= GLUT_ACTIVE_CTRL
;
510 if(beMod
& B_OPTION_KEY
) {
511 // since the window traps B_COMMAND_KEY, we'll have to settle
512 // for the option key.. but we need to get the raw character,
513 // not the Unicode-enhanced version
514 key
= Window()->CurrentMessage()->FindInt32("raw_char");
515 modifierKeys
|= GLUT_ACTIVE_ALT
;
521 /***********************************************************
526 * DESCRIPTION: handles keyboard and special events
527 ***********************************************************/
528 void GlutWindow::KeyUp(const char *s
, int32 slen
)
531 BGLView::KeyUp(s
,slen
);
537 switch(Window()->CurrentMessage()->FindInt32("key")) {
566 aChar
= GLUT_KEY_F10
;
569 aChar
= GLUT_KEY_F11
;
572 aChar
= GLUT_KEY_F12
;
578 aChar
= GLUT_KEY_LEFT
;
584 aChar
= GLUT_KEY_RIGHT
;
587 aChar
= GLUT_KEY_DOWN
;
590 aChar
= GLUT_KEY_PAGE_UP
;
593 aChar
= GLUT_KEY_PAGE_DOWN
;
596 aChar
= GLUT_KEY_HOME
;
599 aChar
= GLUT_KEY_END
;
602 aChar
= GLUT_KEY_INSERT
;
605 anyevents
= specialUpEvent
= true;
606 GetMouse(&p
,&m_buttons
);
610 goto setModifiers
; // set the modifier variable
619 anyevents
= keybUpEvent
= true;
620 GetMouse(&p
,&m_buttons
);
626 uint32 beMod
= Window()->CurrentMessage()->FindInt32("modifiers");
627 if(beMod
& B_SHIFT_KEY
)
628 modifierKeys
|= GLUT_ACTIVE_SHIFT
;
629 if(beMod
& B_CONTROL_KEY
)
630 modifierKeys
|= GLUT_ACTIVE_CTRL
;
631 if(beMod
& B_OPTION_KEY
) {
632 // since the window traps B_COMMAND_KEY, we'll have to settle
633 // for the option key.. but we need to get the raw character,
634 // not the Unicode-enhanced version
635 key
= Window()->CurrentMessage()->FindInt32("raw_char");
636 modifierKeys
|= GLUT_ACTIVE_ALT
;
642 /***********************************************************
645 * FUNCTION: MouseDown
647 * DESCRIPTION: handles mouse and menustatus events
648 ***********************************************************/
649 void GlutWindow::MouseDown(BPoint point
)
651 BGLView::MouseDown(point
);
655 /***********************************************************
658 * FUNCTION: MouseCheck
660 * DESCRIPTION: checks for button state changes
661 ***********************************************************/
662 void GlutWindow::MouseCheck()
665 return; // we already have an outstanding mouse event
669 GetMouse(&point
, &newButtons
);
670 if (m_buttons
!= newButtons
) {
671 if (newButtons
&B_PRIMARY_MOUSE_BUTTON
&& !(m_buttons
&B_PRIMARY_MOUSE_BUTTON
)) {
672 button
= GLUT_LEFT_BUTTON
;
673 mouseState
= GLUT_DOWN
;
674 } else if (m_buttons
&B_PRIMARY_MOUSE_BUTTON
&& !(newButtons
&B_PRIMARY_MOUSE_BUTTON
)) {
675 button
= GLUT_LEFT_BUTTON
;
676 mouseState
= GLUT_UP
;
677 } else if (newButtons
&B_SECONDARY_MOUSE_BUTTON
&& !(m_buttons
&B_SECONDARY_MOUSE_BUTTON
)) {
678 button
= GLUT_RIGHT_BUTTON
;
679 mouseState
= GLUT_DOWN
;
680 } else if (m_buttons
&B_SECONDARY_MOUSE_BUTTON
&& !(newButtons
&B_SECONDARY_MOUSE_BUTTON
)) {
681 button
= GLUT_RIGHT_BUTTON
;
682 mouseState
= GLUT_UP
;
683 } else if (newButtons
&B_TERTIARY_MOUSE_BUTTON
&& !(m_buttons
&B_TERTIARY_MOUSE_BUTTON
)) {
684 button
= GLUT_MIDDLE_BUTTON
;
685 mouseState
= GLUT_DOWN
;
686 } else if (m_buttons
&B_TERTIARY_MOUSE_BUTTON
&& !(newButtons
&B_TERTIARY_MOUSE_BUTTON
)) {
687 button
= GLUT_MIDDLE_BUTTON
;
688 mouseState
= GLUT_UP
;
691 return; // no change, return
693 m_buttons
= newButtons
;
695 if (mouseState
== GLUT_DOWN
) {
696 BWindow
*w
= Window();
697 GlutMenu
*m
= __glutGetMenuByNum(menu
[button
]);
699 if (gState
.menuStatus
) {
700 anyevents
= statusEvent
= true;
701 menuNumber
= menu
[button
];
702 menuStatus
= GLUT_MENU_IN_USE
;
703 statusX
= (int)point
.x
;
704 statusY
= (int)point
.y
;
707 BRect bounds
= w
->Frame();
708 point
.x
+= bounds
.left
;
709 point
.y
+= bounds
.top
;
710 GlutPopUp
*bmenu
= static_cast<GlutPopUp
*>(m
->CreateBMenu()); // start menu
711 bmenu
->point
= point
;
713 thread_id menu_thread
= spawn_thread(MenuThread
, "menu thread", B_NORMAL_PRIORITY
, bmenu
);
714 resume_thread(menu_thread
);
720 anyevents
= mouseEvent
= true;
721 mouseX
= (int)point
.x
;
722 mouseY
= (int)point
.y
;
724 uint32 beMod
= modifiers();
725 if(beMod
& B_SHIFT_KEY
)
726 modifierKeys
|= GLUT_ACTIVE_SHIFT
;
727 if(beMod
& B_CONTROL_KEY
)
728 modifierKeys
|= GLUT_ACTIVE_CTRL
;
729 if(beMod
& B_OPTION_KEY
) {
730 modifierKeys
|= GLUT_ACTIVE_ALT
;
736 /***********************************************************
739 * FUNCTION: MouseMoved
741 * DESCRIPTION: handles entry, motion, and passive events
742 ***********************************************************/
743 void GlutWindow::MouseMoved(BPoint point
,
744 uint32 transit
, const BMessage
*msg
)
746 BGLView::MouseMoved(point
,transit
,msg
);
748 if(transit
!= B_INSIDE_VIEW
) {
750 anyevents
= entryEvent
= true;
753 if (transit
== B_ENTERED_VIEW
) {
754 entryState
= GLUT_ENTERED
;
755 MakeFocus(); // make me the current focus
756 __glutSetCursor(cursor
);
758 entryState
= GLUT_LEFT
;
764 anyevents
= motionEvent
= true;
765 motionX
= (int)point
.x
;
766 motionY
= (int)point
.y
;
771 anyevents
= passiveEvent
= true;
772 passiveX
= (int)point
.x
;
773 passiveY
= (int)point
.y
;
779 /***********************************************************
782 * FUNCTION: MessageReceived
784 * DESCRIPTION: handles mouse wheel events
785 ***********************************************************/
787 void GlutWindow::MessageReceived(BMessage
*message
)
789 switch(message
->what
){
790 case B_MOUSE_WHEEL_CHANGED
:
793 if(message
->FindFloat("be:wheel_delta_y",&shift
)==B_OK
) {
794 if(shift
>0)button
= MOUSE_WHEEL_UP
;
795 if(shift
<0)button
= MOUSE_WHEEL_DOWN
;
797 anyevents
= mouseEvent
= true;
808 /***********************************************************
811 * FUNCTION: FrameResized
813 * DESCRIPTION: handles reshape event
814 ***********************************************************/
815 void GlutWindow::FrameResized(float width
, float height
)
817 BGLView::FrameResized(width
, height
);
819 anyevents
= reshapeEvent
= true;
820 m_width
= (int)(width
)+1;
821 m_height
= (int)(height
)+1;
826 /***********************************************************
831 * DESCRIPTION: handles reshape and display events
832 ***********************************************************/
833 void GlutWindow::Draw(BRect updateRect
)
835 BGLView::Draw(updateRect
);
836 BRect frame
= Frame();
837 if (m_width
!= (frame
.Width()+1) || m_height
!= (frame
.Height()+1)) {
838 FrameResized(frame
.Width(), frame
.Height());
842 anyevents
= displayEvent
= true;
848 /***********************************************************
853 * DESCRIPTION: handles mouse up event (MouseUp is broken)
854 ***********************************************************/
855 void GlutWindow::Pulse()
858 if (m_buttons
) { // if there are buttons pressed
863 /***********************************************************
866 * FUNCTION: ErrorCallback
868 * DESCRIPTION: handles GL error messages
869 ***********************************************************/
870 void GlutWindow::ErrorCallback(unsigned long errorCode
) {
871 __glutWarning("GL error: %s", gluErrorString(errorCode
));
874 /***********************************************************
877 * FUNCTION: MenuThread
879 * DESCRIPTION: a new thread to launch popup menu, wait
880 * wait for response, then clean up afterwards and
881 * send appropriate messages
882 ***********************************************************/
883 status_t
GlutWindow::MenuThread(void *m
) {
884 GlutPopUp
*bmenu
= static_cast<GlutPopUp
*>(m
);
885 GlutWindow
*win
= bmenu
->win
; // my window
886 GlutBMenuItem
*result
= (GlutBMenuItem
*)bmenu
->Go(bmenu
->point
);
887 win
->Window()->Lock();
888 win
->anyevents
= win
->statusEvent
= true;
889 win
->menuStatus
= GLUT_MENU_NOT_IN_USE
;
890 win
->menuNumber
= bmenu
->menu
;
893 win
->GetMouse(&cursor
, &buttons
);
894 win
->statusX
= (int)cursor
.x
;
895 win
->statusY
= (int)cursor
.y
;
896 if(result
&& result
->menu
) {
897 win
->menuEvent
= true;
898 win
->menuNumber
= result
->menu
; // in case it was a submenu
899 win
->menuValue
= result
->value
;
901 win
->Window()->Unlock();