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