cffbc1aa543c5656143797a3baf59c17a545f94c
[mesa.git] / src / glut / beos / glutEvent.cpp
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 ***********************************************************/
27 struct 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 ***********************************************************/
37 static GLUTtimer *__glutTimerList = 0; // list of timer callbacks
38 static GLUTtimer *freeTimerList = 0;
39
40 /***********************************************************
41 * FUNCTION: glutTimerFunc (7.19)
42 *
43 * DESCRIPTION: register a new timer callback
44 ***********************************************************/
45 void APIENTRY
46 glutTimerFunc(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 ***********************************************************/
82 static void
83 handleTimeouts(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 ***********************************************************/
112 static void
113 processEventsAndTimeouts(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 ***********************************************************/
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 }
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 ***********************************************************/
319 void 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 ***********************************************************/
347 void 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;
422 specialLabel:
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;
443 setModifiers:
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 ***********************************************************/
468 void 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 ***********************************************************/
481 void 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 ***********************************************************/
562 void 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 ***********************************************************/
605 void 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 ***********************************************************/
623 void 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 ***********************************************************/
645 void 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 ***********************************************************/
660 void 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 ***********************************************************/
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;
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 }