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 */
21static int
22getUnusedWindowSlot()
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 */
45static 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 */
57void
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*/
72GlutWindow::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 */
164int
165glutCreateWindow(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*/
190int
191glutCreateSubWindow(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) */
206void
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 */
217void
218glutSetWindow(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
236int
237glutGetWindow()
238{
239	if (gState.currentWindow)
240		return gState.currentWindow->num + 1;
241
242	return 0;
243}
244
245
246/*!	Recursively set entries to 0. */
247static 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. */
283void
284glutDestroyWindow(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*/
326void
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 */
343void
344glutPostRedisplay()
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 */
355void
356glutPostWindowRedisplay(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
367void
368glutSwapBuffers()
369{
370	gState.currentWindow->SwapBuffers();
371}
372
373
374/*!	Move window */
375void
376glutPositionWindow(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*/
398void
399glutReshapeWindow(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 */
421void
422glutFullScreen()
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*/
439void
440glutPopWindow()
441{
442}
443
444
445/*!	Same problem as glutPopWindow() */
446void
447glutPushWindow()
448{
449}
450
451
452void
453glutShowWindow()
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
467void
468glutHideWindow()
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 */
482void
483glutIconifyWindow()
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 */
495void
496glutSetWindowTitle(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() */
508void
509glutSetIconTitle(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*/
522int
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 */
572GlutBWindow::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
585void
586GlutBWindow::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
618GlutBWindow::~GlutBWindow()
619{
620	fConnectionDisabled = true;
621	if (bgl)
622		bgl->EnableDirectMode(false);
623
624	if (!IsHidden())
625		Hide();
626
627	Sync();
628}
629
630
631bool
632GlutBWindow::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
641void
642GlutBWindow::Minimize(bool minimize)
643{
644	bgl->visible = !minimize;
645	BWindow::Minimize(minimize);
646}
647
648
649void
650GlutBWindow::Hide()
651{
652	BWindow::Hide();
653	bgl->visible = false;
654}
655
656
657void
658GlutBWindow::Show()
659{
660	BWindow::Show();
661	bgl->visible = true;
662}
663