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"
22
/***********************************************************
25
* DESCRIPTION: list of timer callbacks
26
***********************************************************/
28
GLUTtimer *next; // list of timers
29
bigtime_t timeout; // time to be called
30
GLUTtimerCB func; // function to call
34
/***********************************************************
36
***********************************************************/
37
static GLUTtimer *__glutTimerList = 0; // list of timer callbacks
38
static GLUTtimer *freeTimerList = 0;
40
/***********************************************************
41
* FUNCTION: glutTimerFunc (7.19)
43
* DESCRIPTION: register a new timer callback
44
***********************************************************/
46
glutTimerFunc(unsigned int interval, GLUTtimerCB timerFunc, int value)
48
GLUTtimer *timer, *other;
55
timer = freeTimerList;
56
freeTimerList = timer->next;
58
timer = new GLUTtimer();
60
__glutFatalError("out of memory.");
63
timer->func = timerFunc;
66
timer->timeout = system_time() + (interval*1000); // 1000 ticks in a millisecond
67
prevptr = &__glutTimerList;
69
while (other && (other->timeout < timer->timeout)) {
70
prevptr = &other->next;
77
/***********************************************************
78
* FUNCTION: handleTimeouts
80
* DESCRIPTION: private function to handle outstanding timeouts
81
***********************************************************/
88
/* Assumption is that __glutTimerList is already determined
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)
107
/***********************************************************
108
* FUNCTION: processEventsAndTimeouts
110
* DESCRIPTION: clear gBlock, then check all windows for events
111
***********************************************************/
113
processEventsAndTimeouts(void)
115
gBlock.WaitEvent(); // if there is already an event, returns
116
// immediately, otherwise wait forever
117
gBlock.ClearEvents();
120
exit(0); // exit handler cleans up windows and quits nicely
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);
137
if (!gState.windowList[i])
138
continue; // window was destroyed by callback!
140
if (win->displayEvent) {
141
win->displayEvent = false;
142
__glutSetWindow(win);
145
if (!gState.windowList[i])
146
continue; // window was destroyed by callback!
148
if (win->mouseEvent) {
149
win->mouseEvent = false;
150
__glutSetWindow(win);
152
gState.modifierKeys = win->modifierKeys;
153
win->mouse(win->button, win->mouseState, win->mouseX, win->mouseY);
154
gState.modifierKeys = ~0;
157
if (!gState.windowList[i])
158
continue; // window was destroyed by callback!
160
if (win->menuEvent) {
161
win->menuEvent = false;
162
__glutSetWindow(win);
163
GlutMenu *menu = __glutGetMenuByNum(win->menuNumber);
165
gState.currentMenu = menu;
166
menu->select(win->menuValue);
169
if (!gState.windowList[i])
170
continue; // window was destroyed by callback!
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);
180
if (!gState.windowList[i])
181
continue; // window was destroyed by callback!
183
if (win->motionEvent) {
184
win->motionEvent = false;
185
__glutSetWindow(win);
187
win->motion(win->motionX, win->motionY);
189
if (!gState.windowList[i])
190
continue; // window was destroyed by callback!
192
if (win->passiveEvent) {
193
win->passiveEvent = false;
194
__glutSetWindow(win);
196
win->passive(win->passiveX, win->passiveY);
198
if (!gState.windowList[i])
199
continue; // window was destroyed by callback!
201
if (win->keybEvent) {
202
win->keybEvent = false;
203
__glutSetWindow(win);
205
gState.modifierKeys = win->modifierKeys;
206
win->keyboard(win->key, win->keyX, win->keyY);
207
gState.modifierKeys = ~0;
210
if (!gState.windowList[i])
211
continue; // window was destroyed by callback!
213
if (win->specialEvent) {
214
win->specialEvent = false;
215
__glutSetWindow(win);
217
gState.modifierKeys = win->modifierKeys;
218
win->special(win->specialKey, win->specialX, win->specialY);
219
gState.modifierKeys = ~0;
222
if (!gState.windowList[i])
223
continue; // window was destroyed by callback!
225
if (win->entryEvent) {
226
win->entryEvent = false;
227
__glutSetWindow(win);
229
win->entry(win->entryState);
231
if (!gState.windowList[i])
232
continue; // window was destroyed by callback!
234
if (win->windowStatusEvent) {
235
win->windowStatusEvent = false;
236
__glutSetWindow(win);
237
if (win->windowStatus)
238
win->windowStatus(win->visState);
240
if (!gState.windowList[i])
241
continue; // window was destroyed by callback!
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();
310
if(gState.currentWindow)
311
gState.currentWindow->UnlockGL();
314
/***********************************************************
315
* FUNCTION: glutMainLoop (3.1)
317
* DESCRIPTION: enter the event processing loop
318
***********************************************************/
321
if (!gState.windowListSize)
322
__glutFatalUsage("main loop entered with no windows created.");
324
if(gState.currentWindow)
325
gState.currentWindow->UnlockGL();
331
if (__glutTimerList) {
334
processEventsAndTimeouts();
340
/***********************************************************
345
* DESCRIPTION: handles keyboard and special events
346
***********************************************************/
347
void GlutWindow::KeyDown(const char *s, int32 slen)
350
BGLView::KeyDown(s,slen);
356
switch(Window()->CurrentMessage()->FindInt32("key")) {
385
aChar = GLUT_KEY_F10;
388
aChar = GLUT_KEY_F11;
391
aChar = GLUT_KEY_F12;
397
aChar = GLUT_KEY_LEFT;
403
aChar = GLUT_KEY_RIGHT;
406
aChar = GLUT_KEY_DOWN;
409
aChar = GLUT_KEY_PAGE_UP;
412
aChar = GLUT_KEY_PAGE_DOWN;
415
aChar = GLUT_KEY_HOME;
418
aChar = GLUT_KEY_END;
421
aChar = GLUT_KEY_INSERT;
424
anyevents = specialEvent = true;
425
GetMouse(&p,&m_buttons);
429
goto setModifiers; // set the modifier variable
438
anyevents = keybEvent = true;
439
GetMouse(&p,&m_buttons);
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;
461
/***********************************************************
464
* FUNCTION: MouseDown
466
* DESCRIPTION: handles mouse and menustatus events
467
***********************************************************/
468
void GlutWindow::MouseDown(BPoint point)
470
BGLView::MouseDown(point);
474
/***********************************************************
477
* FUNCTION: MouseCheck
479
* DESCRIPTION: checks for button state changes
480
***********************************************************/
481
void GlutWindow::MouseCheck()
484
return; // we already have an outstanding mouse event
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;
510
return; // no change, return
512
m_buttons = newButtons;
514
if (mouseState == GLUT_DOWN) {
515
BWindow *w = Window();
516
GlutMenu *m = __glutGetMenuByNum(menu[button]);
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;
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;
532
thread_id menu_thread = spawn_thread(MenuThread, "menu thread", B_NORMAL_PRIORITY, bmenu);
533
resume_thread(menu_thread);
539
anyevents = mouseEvent = true;
540
mouseX = (int)point.x;
541
mouseY = (int)point.y;
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;
555
/***********************************************************
558
* FUNCTION: MouseMoved
560
* DESCRIPTION: handles entry, motion, and passive events
561
***********************************************************/
562
void GlutWindow::MouseMoved(BPoint point,
563
ulong transit, const BMessage *msg)
565
BGLView::MouseMoved(point,transit,msg);
567
if(transit != B_INSIDE_VIEW) {
569
anyevents = entryEvent = true;
572
if (transit == B_ENTERED_VIEW) {
573
entryState = GLUT_ENTERED;
574
MakeFocus(); // make me the current focus
575
__glutSetCursor(cursor);
577
entryState = GLUT_LEFT;
583
anyevents = motionEvent = true;
584
motionX = (int)point.x;
585
motionY = (int)point.y;
590
anyevents = passiveEvent = true;
591
passiveX = (int)point.x;
592
passiveY = (int)point.y;
598
/***********************************************************
601
* FUNCTION: FrameResized
603
* DESCRIPTION: handles reshape event
604
***********************************************************/
605
void GlutWindow::FrameResized(float width, float height)
607
BGLView::FrameResized(width, height);
609
anyevents = reshapeEvent = true;
610
m_width = (int)(width)+1;
611
m_height = (int)(height)+1;
616
/***********************************************************
621
* DESCRIPTION: handles reshape and display events
622
***********************************************************/
623
void GlutWindow::Draw(BRect updateRect)
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());
632
anyevents = displayEvent = true;
638
/***********************************************************
643
* DESCRIPTION: handles mouse up event (MouseUp is broken)
644
***********************************************************/
645
void GlutWindow::Pulse()
648
if (m_buttons) { // if there are buttons pressed
653
/***********************************************************
656
* FUNCTION: ErrorCallback
658
* DESCRIPTION: handles GL error messages
659
***********************************************************/
660
void GlutWindow::ErrorCallback(GLenum errorCode) {
661
__glutWarning("GL error: %s", gluErrorString(errorCode));
664
/***********************************************************
667
* FUNCTION: MenuThread
669
* DESCRIPTION: a new thread to launch popup menu, wait
670
* wait for response, then clean up afterwards and
671
* send appropriate messages
672
***********************************************************/
673
long 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;
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;
691
win->Window()->Unlock();