xref: /haiku/src/libs/glut/glutWindow.cpp (revision a3e794ae459fec76826407f8ba8c94cd3535f128)
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
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
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
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 */
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
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
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
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
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
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
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
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
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
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
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
368 glutSwapBuffers()
369 {
370 	gState.currentWindow->SwapBuffers();
371 }
372 
373 
374 /*!	Move window */
375 void
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
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
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
440 glutPopWindow()
441 {
442 }
443 
444 
445 /*!	Same problem as glutPopWindow() */
446 void
447 glutPushWindow()
448 {
449 }
450 
451 
452 void
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
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
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
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
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
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 */
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
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 
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
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
642 GlutBWindow::Minimize(bool minimize)
643 {
644 	bgl->visible = !minimize;
645 	BWindow::Minimize(minimize);
646 }
647 
648 
649 void
650 GlutBWindow::Hide()
651 {
652 	BWindow::Hide();
653 	bgl->visible = false;
654 }
655 
656 
657 void
658 GlutBWindow::Show()
659 {
660 	BWindow::Show();
661 	bgl->visible = true;
662 }
663