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 * DESCRIPTION: all the routines for dealing with GlutWindows
9 ***********************************************************/
10
11 #include <stdlib.h>
12
13 #include <GL/glut.h>
14
15 #include "glutint.h"
16 #include "glutState.h"
17 #include "glutBlocker.h"
18
19
20 /*! Helper function to get a new window slot */
21 static int
getUnusedWindowSlot()22 getUnusedWindowSlot()
23 {
24 int i;
25
26 /* Look for allocated, unused slot. */
27 for (i = 0; i < gState.windowListSize; i++) {
28 if (!gState.windowList[i])
29 return i;
30 }
31
32 /* Allocate a new slot. */
33 gState.windowListSize++;
34 gState.windowList = (GlutWindow **)realloc(gState.windowList,
35 gState.windowListSize * sizeof(GlutWindow *));
36 if (gState.windowList == NULL)
37 __glutFatalError("out of memory.");
38
39 gState.windowList[gState.windowListSize - 1] = NULL;
40 return gState.windowListSize - 1;
41 }
42
43
44 /*! Default display function */
45 static void
__glutDefaultDisplay(void)46 __glutDefaultDisplay(void)
47 {
48 /* XXX Remove the warning after GLUT 3.0. */
49 __glutWarning("The following is a new check for GLUT 3.0; update your "
50 "code.");
51 __glutFatalError("redisplay needed for window %d, but no display callback.",
52 gState.currentWindow->num + 1);
53 }
54
55
56 /*! Default reshape function */
57 void
__glutDefaultReshape(int width,int height)58 __glutDefaultReshape(int width, int height)
59 {
60 /* Adjust the viewport of the window */
61 glViewport(0, 0, (GLsizei) width, (GLsizei) height);
62 }
63
64
65 // #pragma mark -
66
67
68 /*! Creates a new GLUT window
69 Note: subwindows don't resize, but top-level windows
70 follow all sides.
71 */
GlutWindow(GlutWindow * nparent,const char * name,int x,int y,int width,int height,ulong options)72 GlutWindow::GlutWindow(GlutWindow *nparent, const char *name,
73 int x, int y, int width, int height, ulong options)
74 : BGLView(nparent != NULL ? BRect(x, y, x + width - 1, y + height - 1)
75 : BRect(0, 0, width - 1, height - 1), name,
76 nparent != NULL ? B_FOLLOW_NONE : B_FOLLOW_ALL_SIDES,
77 B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE | B_PULSE_NEEDED,
78 options)
79 {
80 // add myself to window list
81 num = getUnusedWindowSlot();
82 gState.windowList[num] = this;
83
84 // set up parent/children relationships
85 parent = nparent;
86 if (parent) {
87 siblings = parent->children;
88 parent->children = this;
89 } else {
90 siblings = 0;
91 }
92 children = 0;
93
94 // initialize variables
95 cursor = GLUT_CURSOR_INHERIT; // default cursor
96 for (int i = 0; i < GLUT_MAX_MENUS; i++) {
97 menu[i] = 0;
98 }
99 m_width = width;
100 m_height = height;
101 m_buttons = 0;
102
103 // clear callbacks
104 display = __glutDefaultDisplay;
105 reshape = __glutDefaultReshape;
106 mouse = NULL;
107 motion = NULL;
108 passive = NULL;
109 entry = NULL;
110 keyboard = NULL;
111 keyboardUp = NULL;
112 visibility = NULL;
113 special = NULL;
114 specialUp = NULL;
115 windowStatus = NULL;
116
117 // clear event counters
118 anyevents = 1;
119 displayEvent = 1; // get a reshape and a display event right away
120 reshapeEvent = 1;
121 mouseEvent = 0;
122 motionEvent = 0;
123 passiveEvent = 0;
124 entryEvent = 0;
125 keybEvent = 0;
126 keybUpEvent = 0;
127 windowStatusEvent = 0; // DirectConnected() will report change in
128 visState = -1; // visibility
129 specialEvent = 0;
130 specialUpEvent = 0;
131 statusEvent = 0;
132 menuEvent = 0;
133 visible = true;
134 ignoreKeyRepeat = (gState.keyRepeatMode == GLUT_KEY_REPEAT_OFF);
135
136 gBlock.QuickNewEvent();
137
138 // if i'm a subwindow, add me to my parent view
139 if (parent) {
140 parent->Window()->Lock();
141 parent->AddChild(this);
142 parent->Window()->Unlock();
143 } else {
144 // if I'm a top-level window, create my BWindow
145 GlutBWindow *mybwindow = new GlutBWindow(
146 BRect(x, y, x + width - 1, y + height - 1), name);
147 mybwindow->AddChild(this);
148 mybwindow->bgl = this;
149 mybwindow->Show();
150 }
151
152 // give me the keyboard focus (focus follows mouse, X style, as
153 // implemented in GlutWindow::MouseMoved())
154 Window()->Lock();
155 MakeFocus();
156 Window()->Unlock();
157
158 // make myself the default window
159 __glutSetWindow(this);
160 }
161
162
163 /*! Creates a new GLUT window */
164 int
glutCreateWindow(const char * name)165 glutCreateWindow(const char *name)
166 {
167 if (!be_app)
168 __glutInit();
169
170 ulong options;
171 if (!__glutConvertDisplayMode(&options)) {
172 __glutWarning("visual with necessary capabilities not found.");
173 }
174
175 // if X or Y is negative, then start at a reasonable position
176 bool defaultxy = gState.initX < 0 || gState.initY < 0;
177
178 GlutWindow *window = new GlutWindow(0, name,
179 defaultxy ? 50 : gState.initX, defaultxy ? 50 : gState.initY,
180 gState.initWidth, gState.initHeight, options);
181
182 return window->num + 1;
183 }
184
185
186 /*! Creates a new GLUT subwindow
187 Note: a subwindow is a GlutWindow (which is actually
188 a BGLView) without its own BWindow
189 */
190 int
glutCreateSubWindow(int win,int x,int y,int width,int height)191 glutCreateSubWindow(int win, int x, int y, int width, int height)
192 {
193 ulong options;
194 if (!__glutConvertDisplayMode(&options)) {
195 __glutFatalError("visual with necessary capabilities not found.");
196 }
197
198 GlutWindow *window = new GlutWindow(gState.windowList[win-1], "child",
199 x, y, width, height, options);
200
201 return window->num + 1;
202 }
203
204
205 /*! Set the current window (utility function) */
206 void
__glutSetWindow(GlutWindow * window)207 __glutSetWindow(GlutWindow *window)
208 {
209 if (gState.currentWindow)
210 gState.currentWindow->UnlockGL();
211 gState.currentWindow = window;
212 gState.currentWindow->LockGL();
213 }
214
215
216 /*! Set and get the current window */
217 void
glutSetWindow(int win)218 glutSetWindow(int win)
219 {
220 GlutWindow *window;
221
222 if (win < 1 || win > gState.windowListSize) {
223 __glutWarning("glutSetWindow attempted on bogus window.");
224 return;
225 }
226
227 window = gState.windowList[win - 1];
228 if (!window) {
229 __glutWarning("glutSetWindow attempted on bogus window.");
230 return;
231 }
232 __glutSetWindow(window);
233 }
234
235
236 int
glutGetWindow()237 glutGetWindow()
238 {
239 if (gState.currentWindow)
240 return gState.currentWindow->num + 1;
241
242 return 0;
243 }
244
245
246 /*! Recursively set entries to 0. */
247 static void
__glutDestroyWindow(GlutWindow * window,GlutWindow * initialWindow)248 __glutDestroyWindow(GlutWindow *window, GlutWindow *initialWindow)
249 {
250 // first, find all children recursively and set their entries to 0
251 GlutWindow *cur = window->children;
252 while (cur) {
253 GlutWindow *siblings = cur->siblings;
254 __glutDestroyWindow(cur, initialWindow);
255 cur = siblings;
256 }
257
258 /* Remove from parent's children list (only necessary for
259 non-initial windows and subwindows!). */
260 GlutWindow *parent = window->parent;
261 if (parent && parent == initialWindow->parent) {
262 GlutWindow **prev = &parent->children;
263 cur = parent->children;
264 while (cur) {
265 if (cur == window) {
266 *prev = cur->siblings;
267 break;
268 }
269 prev = &(cur->siblings);
270 cur = cur->siblings;
271 }
272 }
273
274 // finally, check if we are the current window, and set to 0
275 if (gState.currentWindow == window)
276 gState.currentWindow = 0;
277
278 gState.windowList[window->num] = 0;
279 }
280
281
282 /*! Destroy window and all its children. */
283 void
glutDestroyWindow(int win)284 glutDestroyWindow(int win)
285 {
286 // can't destroy a window if another window has the GL context
287 if (gState.currentWindow)
288 gState.currentWindow->UnlockGL();
289
290 // lock the window
291 GlutWindow *window = gState.windowList[win - 1];
292 BWindow *bwindow = window->Window();
293 bwindow->Lock();
294
295 // if win is the current window, set current window to 0
296 if (gState.currentWindow == window)
297 gState.currentWindow = 0;
298
299 // recursively set child entries to 0
300 __glutDestroyWindow(window, window);
301
302 // try flushing OpenGL
303 window->LockGL();
304 glFlush();
305 window->UnlockGL();
306
307 // now, if the window was top-level, delete its BWindow
308 if (!window->parent) {
309 bwindow->Quit();
310 } else {
311 // else, detach it from the BWindow and delete it
312 window->RemoveSelf();
313 delete window;
314 bwindow->Unlock();
315 }
316
317 // relock GL if the current window is still valid
318 if (gState.currentWindow)
319 gState.currentWindow->LockGL();
320 }
321
322
323 /*! Destroy all windows when exit() is called this seems to be necessary
324 to avoid delays and crashes when using BDirectWindow.
325 */
326 void
__glutDestroyAllWindows()327 __glutDestroyAllWindows()
328 {
329 for (int i = 0; i < gState.windowListSize; i++) {
330 if (gState.windowList[i])
331 glutDestroyWindow(i + 1);
332 }
333 gState.display->Lock();
334 gState.display->Quit();
335 gState.display->Unlock();
336
337 status_t ignored;
338 wait_for_thread(gState.appthread, &ignored);
339 }
340
341
342 /*! Mark window as needing redisplay */
343 void
glutPostRedisplay()344 glutPostRedisplay()
345 {
346 gState.currentWindow->Window()->Lock();
347 gState.currentWindow->anyevents = true;
348 gState.currentWindow->displayEvent = true;
349 gState.currentWindow->Window()->Unlock();
350 gBlock.QuickNewEvent();
351 }
352
353
354 /*! Mark window as needing redisplay */
355 void
glutPostWindowRedisplay(int win)356 glutPostWindowRedisplay(int win)
357 {
358 GlutWindow *gwin = gState.windowList[win - 1];
359 gwin->Window()->Lock();
360 gwin->anyevents = true;
361 gwin->displayEvent = true;
362 gwin->Window()->Unlock();
363 gBlock.QuickNewEvent();
364 }
365
366
367 void
glutSwapBuffers()368 glutSwapBuffers()
369 {
370 gState.currentWindow->SwapBuffers();
371 }
372
373
374 /*! Move window */
375 void
glutPositionWindow(int x,int y)376 glutPositionWindow(int x, int y)
377 {
378 BDirectWindow *win = dynamic_cast<BDirectWindow*>(gState.currentWindow->Window());
379 if (win == NULL)
380 return;
381
382 win->Lock();
383 if (gState.currentWindow->parent)
384 gState.currentWindow->MoveTo(x, y); // move the child view
385 else {
386 if (win->IsFullScreen())
387 win->SetFullScreen(false);
388
389 win->MoveTo(x, y); // move the window
390 }
391 win->Unlock();
392 }
393
394
395 /*! Reshape window (we'll catch the callback when the view gets
396 a Draw() message).
397 */
398 void
glutReshapeWindow(int width,int height)399 glutReshapeWindow(int width, int height)
400 {
401 BDirectWindow *win = dynamic_cast<BDirectWindow*>(gState.currentWindow->Window());
402 if (win == NULL)
403 return;
404
405 win->Lock();
406 if (gState.currentWindow->parent) {
407 // resize the child
408 gState.currentWindow->ResizeTo(width - 1, height - 1);
409 } else {
410 if (win->IsFullScreen())
411 win->SetFullScreen(false);
412
413 // resize the parent
414 win->ResizeTo(width - 1, height - 1);
415 }
416 win->Unlock();
417 }
418
419
420 /*! Makes the window full screen */
421 void
glutFullScreen()422 glutFullScreen()
423 {
424 BDirectWindow *win = dynamic_cast<BDirectWindow*>(gState.currentWindow->Window());
425 if (win == NULL)
426 return;
427
428 win->Lock();
429 win->SetFullScreen(true);
430 win->Unlock();
431 }
432
433
434 /*! Supposed to change the stacking order of the current window
435 NOTE: I can't figure out how to do this for windows,
436 and there is no concept of "stacking order" for
437 subwindows, so these are currently no-ops.
438 */
439 void
glutPopWindow()440 glutPopWindow()
441 {
442 }
443
444
445 /*! Same problem as glutPopWindow() */
446 void
glutPushWindow()447 glutPushWindow()
448 {
449 }
450
451
452 void
glutShowWindow()453 glutShowWindow()
454 {
455 gState.currentWindow->Window()->Lock();
456 if (gState.currentWindow->parent) // subwindow
457 gState.currentWindow->Show();
458 else {
459 if(gState.currentWindow->Window()->IsHidden())
460 gState.currentWindow->Window()->Show(); // show the actual BWindow
461 gState.currentWindow->Window()->Minimize(false);
462 }
463 gState.currentWindow->Window()->Unlock();
464 }
465
466
467 void
glutHideWindow()468 glutHideWindow()
469 {
470 gState.currentWindow->Window()->Lock();
471
472 if (gState.currentWindow->parent) // subwindow
473 gState.currentWindow->Hide();
474 else
475 gState.currentWindow->Window()->Hide(); // show the actual BWindow
476
477 gState.currentWindow->Window()->Unlock();
478 }
479
480
481 /*! Minimizes window */
482 void
glutIconifyWindow()483 glutIconifyWindow()
484 {
485 if (gState.currentWindow->parent)
486 __glutFatalError("can't iconify a subwindow");
487
488 gState.currentWindow->Window()->Lock();
489 gState.currentWindow->Window()->Minimize(true);
490 gState.currentWindow->Window()->Unlock();
491 }
492
493
494 /*! Sets the window title */
495 void
glutSetWindowTitle(const char * name)496 glutSetWindowTitle(const char *name)
497 {
498 if (gState.currentWindow->parent)
499 __glutFatalError("glutSetWindowTitle: isn't a top-level window");
500
501 gState.currentWindow->Window()->Lock();
502 gState.currentWindow->Window()->SetTitle(name);
503 gState.currentWindow->Window()->Unlock();
504 }
505
506
507 /*! Same as glutSetWindowTitle() */
508 void
glutSetIconTitle(const char * name)509 glutSetIconTitle(const char *name)
510 {
511 glutSetWindowTitle(name);
512 }
513
514
515 /*! Converts the current display mode into a BGLView
516 display mode, printing warnings as appropriate.
517
518 \param options if non-NULL, the current display mode is
519 returned in it.
520 \return 1 if the current display mode is possible, else 0
521 */
522 int
__glutConvertDisplayMode(unsigned long * options)523 __glutConvertDisplayMode(unsigned long *options)
524 {
525 if (gState.displayString) {
526 /* __glutDisplayString should be NULL except if
527 glutInitDisplayString has been called to register a
528 different display string. Calling glutInitDisplayString
529 means using a string instead of an integer mask determine
530 the visual to use. This big ugly code is in glutDstr.cpp */
531 return __glutConvertDisplayModeFromString(options);
532 }
533
534 if (options) {
535 ulong newoptions = 0;
536 if (gState.displayMode & GLUT_ACCUM)
537 newoptions |= BGL_ACCUM;
538 if (gState.displayMode & GLUT_ALPHA)
539 newoptions |= BGL_ALPHA;
540 if (gState.displayMode & GLUT_DEPTH)
541 newoptions |= BGL_DEPTH;
542 if (gState.displayMode & GLUT_DOUBLE)
543 newoptions |= BGL_DOUBLE;
544 if (gState.displayMode & GLUT_STENCIL)
545 newoptions |= BGL_STENCIL;
546 *options = newoptions;
547 }
548
549 if (gState.displayMode & GLUT_INDEX) {
550 __glutWarning("BeOS doesn't support indexed color");
551 return 0;
552 }
553 if (gState.displayMode & GLUT_MULTISAMPLE) {
554 return 1; // try to go without multisampling
555 }
556 if (gState.displayMode & GLUT_STEREO) {
557 __glutWarning("BeOS doesn't support stereo windows");
558 return 0;
559 }
560 if (gState.displayMode & GLUT_LUMINANCE) {
561 __glutWarning("BeOS doesn't support luminance color model");
562 return 0;
563 }
564 return 1; // visual supported
565 }
566
567
568 // #pragma mark -
569
570
571 /*! Very thin wrapper around BWindow */
GlutBWindow(BRect frame,const char * name)572 GlutBWindow::GlutBWindow(BRect frame, const char *name)
573 : BDirectWindow(frame, name, B_TITLED_WINDOW, 0)
574 {
575 fConnectionDisabled = false;
576 bgl = 0;
577 SetPulseRate(100000);
578
579 if (!SupportsWindowMode()) {
580 __glutFatalError("video card doesn't support windowed operation");
581 }
582 }
583
584
585 void
DirectConnected(direct_buffer_info * info)586 GlutBWindow::DirectConnected(direct_buffer_info *info)
587 {
588 if (!bgl)
589 return;
590
591 bgl->DirectConnected(info);
592 if (!fConnectionDisabled)
593 bgl->EnableDirectMode(true);
594
595 int newVisState;
596 if ((info->buffer_state & B_DIRECT_MODE_MASK) == B_DIRECT_START)
597 bgl->visible = true;
598
599 if (!bgl->visible || info->buffer_state == B_DIRECT_STOP)
600 newVisState = GLUT_HIDDEN;
601 else {
602 if (info->clip_list_count == 0)
603 newVisState = GLUT_FULLY_COVERED;
604 else if (info->clip_list_count == 1)
605 newVisState = GLUT_FULLY_RETAINED;
606 else
607 newVisState = GLUT_PARTIALLY_RETAINED;
608 }
609
610 if (newVisState != bgl->visState) {
611 bgl->visState = newVisState;
612 bgl->anyevents = bgl->windowStatusEvent = true;
613 gBlock.NewEvent();
614 }
615 }
616
617
~GlutBWindow()618 GlutBWindow::~GlutBWindow()
619 {
620 fConnectionDisabled = true;
621 if (bgl)
622 bgl->EnableDirectMode(false);
623
624 if (!IsHidden())
625 Hide();
626
627 Sync();
628 }
629
630
631 bool
QuitRequested()632 GlutBWindow::QuitRequested()
633 {
634 gState.quitAll = true;
635 gBlock.NewEvent();
636 return false;
637 // don't quit now, wait for main thread to do it
638 }
639
640
641 void
Minimize(bool minimize)642 GlutBWindow::Minimize(bool minimize)
643 {
644 bgl->visible = !minimize;
645 BWindow::Minimize(minimize);
646 }
647
648
649 void
Hide()650 GlutBWindow::Hide()
651 {
652 BWindow::Hide();
653 bgl->visible = false;
654 }
655
656
657 void
Show()658 GlutBWindow::Show()
659 {
660 BWindow::Show();
661 bgl->visible = true;
662 }
663