xref: /haiku/src/libs/glut/glutEvent.cpp (revision 1a3518cf757c2da8006753f83962da5935bbc82b)
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 #include <stdio.h>
22 #include <stdlib.h>
23 
24 #define MOUSE_WHEEL_UP   3
25 #define MOUSE_WHEEL_DOWN 4
26 
27 /***********************************************************
28  *	CLASS:	GLUTtimer
29  *
30  *	DESCRIPTION:	list of timer callbacks
31  ***********************************************************/
32 struct GLUTtimer {
33 	GLUTtimer *next;	// list of timers
34 	bigtime_t timeout;	// time to be called
35 	GLUTtimerCB func;	// function to call
36 	int value;			// value
37 };
38 
39 /***********************************************************
40  *	Private variables
41  ***********************************************************/
42 static GLUTtimer *__glutTimerList = 0;			// list of timer callbacks
43 static GLUTtimer *freeTimerList = 0;
44 
45 /***********************************************************
46  *	FUNCTION:	glutTimerFunc (7.19)
47  *
48  *	DESCRIPTION:  register a new timer callback
49  ***********************************************************/
50 void APIENTRY
51 glutTimerFunc(unsigned int interval, GLUTtimerCB timerFunc, int value)
52 {
53   GLUTtimer *timer, *other;
54   GLUTtimer **prevptr;
55 
56   if (!timerFunc)
57     return;
58 
59   if (freeTimerList) {
60     timer = freeTimerList;
61     freeTimerList = timer->next;
62   } else {
63     timer = new GLUTtimer();
64     if (!timer)
65       __glutFatalError("out of memory.");
66   }
67 
68   timer->func = timerFunc;
69   timer->value = value;
70   timer->next = NULL;
71   timer->timeout = system_time() + (interval*1000);	// 1000 ticks in a millisecond
72   prevptr = &__glutTimerList;
73   other = *prevptr;
74   while (other && (other->timeout < timer->timeout)) {
75     prevptr = &other->next;
76     other = *prevptr;
77   }
78   timer->next = other;
79   *prevptr = timer;
80 }
81 
82 /***********************************************************
83  *	FUNCTION:	handleTimeouts
84  *
85  *	DESCRIPTION:  private function to handle outstanding timeouts
86  ***********************************************************/
87 static void
88 handleTimeouts(void)
89 {
90   bigtime_t now;
91   GLUTtimer *timer;
92 
93   /* Assumption is that __glutTimerList is already determined
94      to be non-NULL. */
95   now = system_time();
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)
107       break;
108   }
109 }
110 
111 
112 /***********************************************************
113  *	FUNCTION:	processEventsAndTimeouts
114  *
115  *	DESCRIPTION:  clear gBlock, then check all windows for events
116  ***********************************************************/
117 static void
118 processEventsAndTimeouts(void)
119 {
120 	gBlock.WaitEvent();		// if there is already an event, returns
121 							// immediately, otherwise wait forever
122 	gBlock.ClearEvents();
123 
124 	if(gState.quitAll)
125 		exit(0);		// exit handler cleans up windows and quits nicely
126 
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);
141 				}
142 				if (!gState.windowList[i])
143 					continue;	// window was destroyed by callback!
144 
145 				if (win->displayEvent) {
146 					win->displayEvent = false;
147 					__glutSetWindow(win);
148 					win->display();
149 				}
150 				if (!gState.windowList[i])
151 					continue;	// window was destroyed by callback!
152 
153 				if (win->mouseEvent) {
154 					win->mouseEvent = false;
155 					__glutSetWindow(win);
156 					if (win->mouse) {
157 						gState.modifierKeys = win->modifierKeys;
158 						win->mouse(win->button, win->mouseState, win->mouseX, win->mouseY);
159 						gState.modifierKeys = ~0;
160 					}
161 				}
162 				if (!gState.windowList[i])
163 					continue;	// window was destroyed by callback!
164 
165 				if (win->menuEvent) {
166 					win->menuEvent = false;
167 					__glutSetWindow(win);
168 					GlutMenu *menu = __glutGetMenuByNum(win->menuNumber);
169 					if (menu) {
170 						gState.currentMenu = menu;
171 						menu->select(win->menuValue);
172 					}
173 				}
174 				if (!gState.windowList[i])
175 					continue;	// window was destroyed by callback!
176 
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);
183 					}
184 				}
185 				if (!gState.windowList[i])
186 					continue;	// window was destroyed by callback!
187 
188 				if (win->motionEvent) {
189 					win->motionEvent = false;
190 					__glutSetWindow(win);
191 					if (win->motion)
192 						win->motion(win->motionX, win->motionY);
193 				}
194 				if (!gState.windowList[i])
195 					continue;	// window was destroyed by callback!
196 
197 				if (win->passiveEvent) {
198 					win->passiveEvent = false;
199 					__glutSetWindow(win);
200 					if (win->passive)
201 						win->passive(win->passiveX, win->passiveY);
202 				}
203 				if (!gState.windowList[i])
204 					continue;	// window was destroyed by callback!
205 
206 				if (win->keybEvent) {
207 					win->keybEvent = false;
208 					__glutSetWindow(win);
209 					if (win->keyboard) {
210 						gState.modifierKeys = win->modifierKeys;
211 						win->keyboard(win->key, win->keyX, win->keyY);
212 						gState.modifierKeys = ~0;
213 					}
214 				}
215 				if (!gState.windowList[i])
216 					continue;	// window was destroyed by callback!
217 
218 				if (win->specialEvent) {
219 					win->specialEvent = false;
220 					__glutSetWindow(win);
221 					if (win->special) {
222 						gState.modifierKeys = win->modifierKeys;
223 						win->special(win->specialKey, win->specialX, win->specialY);
224 						gState.modifierKeys = ~0;
225 					}
226 				}
227 				if (!gState.windowList[i])
228 					continue;	// window was destroyed by callback!
229 
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;
237 					}
238 				}
239 				if (!gState.windowList[i])
240 					continue;	// window was destroyed by callback!
241 
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;
249 					}
250 				}
251 				if (!gState.windowList[i])
252 					continue;	// window was destroyed by callback!
253 
254 
255 				if (win->entryEvent) {
256 					win->entryEvent = false;
257 					__glutSetWindow(win);
258 					if (win->entry)
259 						win->entry(win->entryState);
260 				}
261 				if (!gState.windowList[i])
262 					continue;	// window was destroyed by callback!
263 
264 				if (win->windowStatusEvent) {
265 					win->windowStatusEvent = false;
266 					__glutSetWindow(win);
267 					if (win->windowStatus)
268 						win->windowStatus(win->visState);
269 				}
270 				if (!gState.windowList[i])
271 					continue;	// window was destroyed by callback!
272 			}
273 		}
274 	}
275 	if (gState.currentWindow)
276 		gState.currentWindow->UnlockGL();
277 
278 	// This code isn't necessary since BGLView automatically traps errors
279 #if 0
280 	if(gState.debug) {
281 		for(int i=0; i<gState.windowListSize; i++) {
282 			if (gState.windowList[i]) {
283 				gState.windowList[i]->LockGL();
284 				glutReportErrors();
285 				gState.windowList[i]->UnlockGL();
286 			}
287 		}
288 	}
289 #endif
290 	if (__glutTimerList) {
291       handleTimeouts();
292     }
293 }
294 
295 /***********************************************************
296  *	FUNCTION:	waitForSomething
297  *
298  *	DESCRIPTION:  use gBlock to wait for a new event or timeout
299  ***********************************************************/
300 static void
301 waitForSomething(void)
302 {
303 	bigtime_t timeout = __glutTimerList->timeout;
304 	bigtime_t now = system_time();
305 
306 	if (gBlock.PendingEvent())
307 		goto immediatelyHandleEvent;
308 
309 	if(timeout>now)
310 		gBlock.WaitEvent(timeout-now);
311 	if (gBlock.PendingEvent()) {
312 	immediatelyHandleEvent:
313 		processEventsAndTimeouts();
314 	} else {
315 		if (__glutTimerList)
316 			handleTimeouts();
317 	}
318 }
319 
320 /***********************************************************
321  *	FUNCTION:	idleWait
322  *
323  *	DESCRIPTION:  check for events, then call idle function
324  ***********************************************************/
325 static void
326 idleWait(void)
327 {
328   if (gBlock.PendingEvent()) {
329     processEventsAndTimeouts();
330   } else {
331     if (__glutTimerList)
332       handleTimeouts();
333   }
334   /* Make sure idle func still exists! */
335   if(gState.currentWindow)
336 	  gState.currentWindow->LockGL();
337   if (gState.idle) {
338     gState.idle();
339   }
340   if(gState.currentWindow)
341 	  gState.currentWindow->UnlockGL();
342 }
343 
344 /***********************************************************
345  *	FUNCTION:	glutMainLoop (3.1)
346  *
347  *	DESCRIPTION:  enter the event processing loop
348  ***********************************************************/
349 void glutMainLoop()
350 {
351   if (!gState.windowListSize)
352     __glutFatalUsage("main loop entered with no windows created.");
353 
354   if(gState.currentWindow)
355 	  gState.currentWindow->UnlockGL();
356 
357   for (;;) {
358     if (gState.idle) {
359       idleWait();
360     } else {
361       if (__glutTimerList) {
362         waitForSomething();
363       } else {
364         processEventsAndTimeouts();
365       }
366     }
367   }
368 }
369 
370 
371 void glutSetKeyRepeat(int repeatMode)
372 {
373 	switch(repeatMode) {
374 		case GLUT_KEY_REPEAT_DEFAULT:
375 			gState.keyRepeatMode = GLUT_KEY_REPEAT_ON;
376 			break;
377 
378 		case GLUT_KEY_REPEAT_ON:
379 		case GLUT_KEY_REPEAT_OFF:
380 			gState.keyRepeatMode = repeatMode;
381 			break;
382 
383 		default:
384 			__glutWarning("invalid glutSetKeyRepeat mode: %d", repeatMode);
385 	}
386 }
387 
388 
389 void glutIgnoreKeyRepeat(int ignore)
390 {
391 	if (gState.currentWindow)
392 		gState.currentWindow->ignoreKeyRepeat = (ignore != 0);
393 }
394 
395 
396 /***********************************************************
397  *	CLASS:		GlutWindow
398  *
399  *	FUNCTION:	KeyDown
400  *
401  *	DESCRIPTION:  handles keyboard and special events
402  ***********************************************************/
403 void GlutWindow::KeyDown(const char *s, int32 slen)
404 {
405   ulong aChar = s[0];
406   BGLView::KeyDown(s,slen);
407 
408   BPoint p;
409 
410 	if (ignoreKeyRepeat
411 		&& Window()->CurrentMessage()->FindInt32("be:key_repeat") > 0)
412 		return;
413 
414 	switch (aChar) {
415 		case B_FUNCTION_KEY:
416 		switch(Window()->CurrentMessage()->FindInt32("key")) {
417 			case B_F1_KEY:
418 				aChar = GLUT_KEY_F1;
419 				goto specialLabel;
420 			case B_F2_KEY:
421 				aChar = GLUT_KEY_F2;
422 				goto specialLabel;
423 			case B_F3_KEY:
424 				aChar = GLUT_KEY_F3;
425 				goto specialLabel;
426 			case B_F4_KEY:
427 				aChar = GLUT_KEY_F4;
428 				goto specialLabel;
429 			case B_F5_KEY:
430 				aChar = GLUT_KEY_F5;
431 				goto specialLabel;
432 			case B_F6_KEY:
433 				aChar = GLUT_KEY_F6;
434 				goto specialLabel;
435 			case B_F7_KEY:
436 				aChar = GLUT_KEY_F7;
437 				goto specialLabel;
438 			case B_F8_KEY:
439 				aChar = GLUT_KEY_F8;
440 				goto specialLabel;
441 			case B_F9_KEY:
442 				aChar = GLUT_KEY_F9;
443 				goto specialLabel;
444 			case B_F10_KEY:
445 				aChar = GLUT_KEY_F10;
446 				goto specialLabel;
447 			case B_F11_KEY:
448 				aChar = GLUT_KEY_F11;
449 				goto specialLabel;
450 			case B_F12_KEY:
451 				aChar = GLUT_KEY_F12;
452 				goto specialLabel;
453 			default:
454 				return;
455 		}
456 		case B_LEFT_ARROW:
457 			aChar = GLUT_KEY_LEFT;
458 			goto specialLabel;
459 		case B_UP_ARROW:
460 			aChar = GLUT_KEY_UP;
461 			goto specialLabel;
462 		case B_RIGHT_ARROW:
463 			aChar = GLUT_KEY_RIGHT;
464 			goto specialLabel;
465 		case B_DOWN_ARROW:
466 			aChar = GLUT_KEY_DOWN;
467 			goto specialLabel;
468 		case B_PAGE_UP:
469 			aChar = GLUT_KEY_PAGE_UP;
470 			goto specialLabel;
471 		case B_PAGE_DOWN:
472 			aChar = GLUT_KEY_PAGE_DOWN;
473 			goto specialLabel;
474 		case B_HOME:
475 			aChar = GLUT_KEY_HOME;
476 			goto specialLabel;
477 		case B_END:
478 			aChar = GLUT_KEY_END;
479 			goto specialLabel;
480 		case B_INSERT:
481             aChar = GLUT_KEY_INSERT;
482 specialLabel:
483 			if (special) {
484 				anyevents = specialEvent = true;
485 				GetMouse(&p,&m_buttons);
486 				specialKey = aChar;
487 				specialX = (int)p.x;
488 				specialY = (int)p.y;
489 				goto setModifiers;	// set the modifier variable
490 			}
491 			return;
492 
493 		default:
494 			break;
495 	}
496 
497 	if (keyboard) {
498 		anyevents = keybEvent = true;
499 		GetMouse(&p,&m_buttons);
500 		key = aChar;
501 		keyX = (int)p.x;
502 		keyY = (int)p.y;
503 setModifiers:
504 		modifierKeys = 0;
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;
516 		}
517 		gBlock.NewEvent();
518 	}
519 }
520 
521 /***********************************************************
522  *	CLASS:		GlutWindow
523  *
524  *	FUNCTION:	KeyUp
525  *
526  *	DESCRIPTION:  handles keyboard and special events
527  ***********************************************************/
528 void GlutWindow::KeyUp(const char *s, int32 slen)
529 {
530   ulong aChar = s[0];
531   BGLView::KeyUp(s,slen);
532 
533   BPoint p;
534 
535 	switch (aChar) {
536 		case B_FUNCTION_KEY:
537 		switch(Window()->CurrentMessage()->FindInt32("key")) {
538 			case B_F1_KEY:
539 				aChar = GLUT_KEY_F1;
540 				goto specialLabel;
541 			case B_F2_KEY:
542 				aChar = GLUT_KEY_F2;
543 				goto specialLabel;
544 			case B_F3_KEY:
545 				aChar = GLUT_KEY_F3;
546 				goto specialLabel;
547 			case B_F4_KEY:
548 				aChar = GLUT_KEY_F4;
549 				goto specialLabel;
550 			case B_F5_KEY:
551 				aChar = GLUT_KEY_F5;
552 				goto specialLabel;
553 			case B_F6_KEY:
554 				aChar = GLUT_KEY_F6;
555 				goto specialLabel;
556 			case B_F7_KEY:
557 				aChar = GLUT_KEY_F7;
558 				goto specialLabel;
559 			case B_F8_KEY:
560 				aChar = GLUT_KEY_F8;
561 				goto specialLabel;
562 			case B_F9_KEY:
563 				aChar = GLUT_KEY_F9;
564 				goto specialLabel;
565 			case B_F10_KEY:
566 				aChar = GLUT_KEY_F10;
567 				goto specialLabel;
568 			case B_F11_KEY:
569 				aChar = GLUT_KEY_F11;
570 				goto specialLabel;
571 			case B_F12_KEY:
572 				aChar = GLUT_KEY_F12;
573 				goto specialLabel;
574 			default:
575 				return;
576 		}
577 		case B_LEFT_ARROW:
578 			aChar = GLUT_KEY_LEFT;
579 			goto specialLabel;
580 		case B_UP_ARROW:
581 			aChar = GLUT_KEY_UP;
582 			goto specialLabel;
583 		case B_RIGHT_ARROW:
584 			aChar = GLUT_KEY_RIGHT;
585 			goto specialLabel;
586 		case B_DOWN_ARROW:
587 			aChar = GLUT_KEY_DOWN;
588 			goto specialLabel;
589 		case B_PAGE_UP:
590 			aChar = GLUT_KEY_PAGE_UP;
591 			goto specialLabel;
592 		case B_PAGE_DOWN:
593 			aChar = GLUT_KEY_PAGE_DOWN;
594 			goto specialLabel;
595 		case B_HOME:
596 			aChar = GLUT_KEY_HOME;
597 			goto specialLabel;
598 		case B_END:
599 			aChar = GLUT_KEY_END;
600 			goto specialLabel;
601 		case B_INSERT:
602             aChar = GLUT_KEY_INSERT;
603 specialLabel:
604 			if (specialUp!=0) {
605 				anyevents = specialUpEvent = true;
606 				GetMouse(&p,&m_buttons);
607 				specialKey = aChar;
608 				specialX = (int)p.x;
609 				specialY = (int)p.y;
610 				goto setModifiers;	// set the modifier variable
611 			}
612 			return;
613 
614 		default:
615 			break;
616 	}
617 
618 	if (keyboardUp!=0) {
619 		anyevents = keybUpEvent = true;
620 		GetMouse(&p,&m_buttons);
621 		key = aChar;
622 		keyX = (int)p.x;
623 		keyY = (int)p.y;
624 setModifiers:
625 		modifierKeys = 0;
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;
637 		}
638 		gBlock.NewEvent();
639 	}
640 }
641 
642 /***********************************************************
643  *	CLASS:		GlutWindow
644  *
645  *	FUNCTION:	MouseDown
646  *
647  *	DESCRIPTION:  handles mouse and menustatus events
648  ***********************************************************/
649 void GlutWindow::MouseDown(BPoint point)
650 {
651 	BGLView::MouseDown(point);
652 	MouseCheck();
653 }
654 
655 /***********************************************************
656  *	CLASS:		GlutWindow
657  *
658  *	FUNCTION:	MouseCheck
659  *
660  *	DESCRIPTION:  checks for button state changes
661  ***********************************************************/
662 void GlutWindow::MouseCheck()
663 {
664 	if (mouseEvent)
665 		return;		// we already have an outstanding mouse event
666 
667 	BPoint point;
668 	uint32 newButtons;
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;
689 		}
690 	} else {
691 		return;		// no change, return
692 	}
693 	m_buttons = newButtons;
694 
695 	if (mouseState == GLUT_DOWN) {
696 		BWindow *w = Window();
697 		GlutMenu *m = __glutGetMenuByNum(menu[button]);
698 		if (m) {
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;
705 				gBlock.NewEvent();
706 			}
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;
712 			bmenu->win = this;
713 			thread_id menu_thread = spawn_thread(MenuThread, "menu thread", B_NORMAL_PRIORITY, bmenu);
714 			resume_thread(menu_thread);
715 			return;
716 		}
717 	}
718 
719 	if (mouse) {
720 		anyevents = mouseEvent = true;
721 		mouseX = (int)point.x;
722 		mouseY = (int)point.y;
723 		modifierKeys = 0;
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;
731 		}
732 		gBlock.NewEvent();
733 	}
734 }
735 
736 /***********************************************************
737  *	CLASS:		GlutWindow
738  *
739  *	FUNCTION:	MouseMoved
740  *
741  *	DESCRIPTION:  handles entry, motion, and passive events
742  ***********************************************************/
743 void GlutWindow::MouseMoved(BPoint point,
744 						uint32 transit, const BMessage *msg)
745 {
746 	BGLView::MouseMoved(point,transit,msg);
747 
748 	if(transit != B_INSIDE_VIEW) {
749 		if (entry) {
750 			anyevents = entryEvent = true;
751 			gBlock.NewEvent();
752 		}
753 		if (transit == B_ENTERED_VIEW) {
754 			entryState = GLUT_ENTERED;
755 			MakeFocus();	// make me the current focus
756 			__glutSetCursor(cursor);
757 		} else
758 			entryState = GLUT_LEFT;
759 	}
760 
761 	MouseCheck();
762 	if(m_buttons) {
763 		if(motion) {
764 			anyevents = motionEvent = true;
765 			motionX = (int)point.x;
766 			motionY = (int)point.y;
767 			gBlock.NewEvent();
768 		}
769 	} else {
770 		if(passive) {
771 			anyevents = passiveEvent = true;
772 			passiveX = (int)point.x;
773 			passiveY = (int)point.y;
774 			gBlock.NewEvent();
775 		}
776 	}
777 }
778 
779 /***********************************************************
780  *	CLASS:		GlutWindow
781  *
782  *	FUNCTION:	MessageReceived
783  *
784  *	DESCRIPTION:  handles mouse wheel events
785  ***********************************************************/
786 
787 void GlutWindow::MessageReceived(BMessage *message)
788 {
789 	switch(message->what){
790 	case B_MOUSE_WHEEL_CHANGED:
791 	{
792 	 	float shift=0;
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;
796 	 	if(shift!=0) {
797 		  anyevents = mouseEvent = true;
798 		  gBlock.NewEvent();
799 		 }
800 		}
801 	 	break;
802 	}
803 	default:
804 		BGLView::MessageReceived(message);
805 	 	break;
806 	}
807 }
808 
809 /***********************************************************
810  *	CLASS:		GlutWindow
811  *
812  *	FUNCTION:	FrameResized
813  *
814  *	DESCRIPTION:  handles reshape event
815  ***********************************************************/
816 void GlutWindow::FrameResized(float width, float height)
817 {
818 	BGLView::FrameResized(width, height);
819 	if (visible) {
820 		anyevents = reshapeEvent = true;
821 		m_width = (int)(width)+1;
822 		m_height = (int)(height)+1;
823 		gBlock.NewEvent();
824 	}
825 }
826 
827 /***********************************************************
828  *	CLASS:		GlutWindow
829  *
830  *	FUNCTION:	Draw
831  *
832  *	DESCRIPTION:  handles reshape and display events
833  ***********************************************************/
834 void GlutWindow::Draw(BRect updateRect)
835 {
836 	BGLView::Draw(updateRect);
837 	BRect frame = Frame();
838 	if (m_width != (frame.Width()+1) || m_height != (frame.Height()+1)) {
839 		FrameResized(frame.Width(), frame.Height());
840 	}
841 	Window()->Lock();
842 	if (visible) {
843 		anyevents = displayEvent = true;
844 		gBlock.NewEvent();
845 	}
846 	Window()->Unlock();
847 }
848 
849 /***********************************************************
850  *	CLASS:		GlutWindow
851  *
852  *	FUNCTION:	Pulse
853  *
854  *	DESCRIPTION:  handles mouse up event (MouseUp is broken)
855  ***********************************************************/
856 void GlutWindow::Pulse()
857 {
858 	BGLView::Pulse();
859 	if (m_buttons) {	// if there are buttons pressed
860 		MouseCheck();
861 	}
862 }
863 
864 /***********************************************************
865  *	CLASS:		GlutWindow
866  *
867  *	FUNCTION:	ErrorCallback
868  *
869  *	DESCRIPTION:  handles GL error messages
870  ***********************************************************/
871 void GlutWindow::ErrorCallback(unsigned long errorCode) {
872 	__glutWarning("GL error: %s", gluErrorString(errorCode));
873 }
874 
875 /***********************************************************
876  *	CLASS:		GlutWindow
877  *
878  *	FUNCTION:	MenuThread
879  *
880  *	DESCRIPTION:  a new thread to launch popup menu, wait
881  *			wait for response, then clean up afterwards and
882  *			send appropriate messages
883  ***********************************************************/
884 status_t GlutWindow::MenuThread(void *m) {
885 	GlutPopUp *bmenu = static_cast<GlutPopUp*>(m);
886 	GlutWindow *win = bmenu->win;	// my window
887 	GlutBMenuItem *result = (GlutBMenuItem*)bmenu->Go(bmenu->point);
888 	win->Window()->Lock();
889 	win->anyevents = win->statusEvent = true;
890 	win->menuStatus = GLUT_MENU_NOT_IN_USE;
891 	win->menuNumber = bmenu->menu;
892 	BPoint cursor;
893 	uint32 buttons;
894 	win->GetMouse(&cursor, &buttons);
895 	win->statusX = (int)cursor.x;
896 	win->statusY = (int)cursor.y;
897 	if(result && result->menu) {
898 		win->menuEvent = true;
899 		win->menuNumber = result->menu;  // in case it was a submenu
900 		win->menuValue = result->value;
901 	}
902 	win->Window()->Unlock();
903 	gBlock.NewEvent();
904 	delete bmenu;
905 	return B_OK;
906 }
907