Fixed bug #252: menu windows are drawn with immediate mode and the fixed
[freeglut] / src / fg_window.c
1 /*
2  * fg_window.c
3  *
4  * Window management methods.
5  *
6  * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
7  * Written by Pawel W. Olszta, <olszta@sourceforge.net>
8  * Creation date: Fri Dec 3 1999
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23  * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
24  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  */
27
28 #define FREEGLUT_BUILDING_LIB
29 #include <GL/freeglut.h>
30 #include "fg_internal.h"
31 #include "fg_gl2.h"
32
33 /*
34  * TODO BEFORE THE STABLE RELEASE:
35  *
36  *  fgSetupPixelFormat      -- ignores the display mode settings
37  *  fgOpenWindow()          -- check the Win32 version, -iconic handling!
38  *  fgCloseWindow()         -- check the Win32 version
39  *  glutCreateWindow()      -- Check when default position and size is {-1,-1}
40  *  glutCreateSubWindow()   -- Check when default position and size is {-1,-1}
41  *  glutDestroyWindow()     -- check the Win32 version
42  *  glutSetWindow()         -- check the Win32 version
43  *  glutSetWindowTitle()    -- check the Win32 version
44  *  glutSetIconTitle()      -- check the Win32 version
45  *  glutShowWindow()        -- check the Win32 version
46  *  glutHideWindow()        -- check the Win32 version
47  *  glutIconifyWindow()     -- check the Win32 version
48  *  glutPushWindow()        -- check the Win32 version
49  *  glutPopWindow()         -- check the Win32 version
50  */
51
52
53 extern void fgPlatformSetWindow ( SFG_Window *window );
54 extern void fgPlatformOpenWindow( SFG_Window* window, const char* title,
55                                   GLboolean positionUse, int x, int y,
56                                   GLboolean sizeUse, int w, int h,
57                                   GLboolean gameMode, GLboolean isSubWindow );
58 extern void fgPlatformCloseWindow( SFG_Window* window );
59 extern void fgPlatformGlutSetWindowTitle( const char* title );
60 extern void fgPlatformGlutSetIconTitle( const char* title );
61
62
63 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
64
65 int fghIsLegacyContextRequested( SFG_Window *win )
66 {
67         int vmajor = fgState.MajorVersion;
68         int vminor = fgState.MinorVersion;
69         /* XXX: menu windows are drawn with the fixed function pipeline, therefore
70          * the context created for them can't be a modern core-profile context.
71          * Force the traditional context creation for menu windows.
72          */
73     return vmajor < 2 || (vmajor == 2 && vminor <= 1) || win->IsMenu;
74 }
75
76 int fghNumberOfAuxBuffersRequested( void )
77 {
78   if ( fgState.DisplayMode & GLUT_AUX4 ) {
79     return 4;
80   }
81   if ( fgState.DisplayMode & GLUT_AUX3 ) {
82     return 3;
83   }
84   if ( fgState.DisplayMode & GLUT_AUX2 ) {
85     return 2;
86   }
87   if ( fgState.DisplayMode & GLUT_AUX1 ) { /* NOTE: Same as GLUT_AUX! */
88     return fgState.AuxiliaryBufferNumber;
89   }
90   return 0;
91 }
92
93 int fghMapBit( int mask, int from, int to )
94 {
95   return ( mask & from ) ? to : 0;
96
97 }
98
99 void fghContextCreationError( void )
100 {
101     fgError( "Unable to create OpenGL %d.%d context (flags %x, profile %x)",
102              fgState.MajorVersion, fgState.MinorVersion, fgState.ContextFlags,
103              fgState.ContextProfile );
104 }
105
106
107 /* -- SYSTEM-DEPENDENT PRIVATE FUNCTIONS ------------------------------------ */
108
109 /*
110  * Sets the OpenGL context and the fgStructure "Current Window" pointer to
111  * the window structure passed in.
112  */
113 void fgSetWindow ( SFG_Window *window )
114 {
115     fgPlatformSetWindow ( window );
116
117     fgStructure.CurrentWindow = window;
118 }
119
120 /*
121  * Opens a window. Requires a SFG_Window object created and attached
122  * to the freeglut structure. OpenGL context is created here.
123  */
124 void fgOpenWindow( SFG_Window* window, const char* title,
125                    GLboolean positionUse, int x, int y,
126                    GLboolean sizeUse, int w, int h,
127                    GLboolean gameMode, GLboolean isSubWindow )
128 {
129     fgPlatformOpenWindow( window, title,
130                           positionUse, x, y,
131                           sizeUse, w, h,
132                           gameMode, isSubWindow );
133
134     fgSetWindow( window );
135
136 #ifndef EGL_VERSION_1_0
137     window->Window.DoubleBuffered =
138         ( fgState.DisplayMode & GLUT_DOUBLE ) ? 1 : 0;
139
140     if ( ! window->Window.DoubleBuffered )
141     {
142         glDrawBuffer ( GL_FRONT );
143         glReadBuffer ( GL_FRONT );
144     }
145 #else
146     /* - EGL is always double-buffered */
147     /* - No glDrawBuffer/glReadBuffer in GLES */
148     window->Window.DoubleBuffered = 1;
149 #endif
150     window->Window.attribute_v_coord = -1;
151     window->Window.attribute_v_normal = -1;
152     window->Window.attribute_v_texture = -1;
153
154     fgInitGL2();
155
156     window->State.WorkMask |= GLUT_INIT_WORK;
157 }
158
159 /*
160  * Closes a window, destroying the frame and OpenGL context
161  */
162 void fgCloseWindow( SFG_Window* window )
163 {
164     /* if we're in gamemode and we're closing the gamemode window,
165      * call glutLeaveGameMode first to make sure the gamemode is
166      * properly closed before closing the window
167      */
168     if (fgStructure.GameModeWindow != NULL && fgStructure.GameModeWindow->ID==window->ID)
169         glutLeaveGameMode();
170
171     fgPlatformCloseWindow ( window );
172 }
173
174
175 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
176
177 /*
178  * Creates a new top-level freeglut window
179  */
180 int FGAPIENTRY glutCreateWindow( const char* title )
181 {
182     /* XXX GLUT does not exit; it simply calls "glutInit" quietly if the
183      * XXX application has not already done so.  The "freeglut" community
184      * XXX decided not to go this route (freeglut-developer e-mail from
185      * XXX Steve Baker, 12/16/04, 4:22 PM CST, "Re: [Freeglut-developer]
186      * XXX Desired 'freeglut' behaviour when there is no current window")
187      */
188     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutCreateWindow" );
189
190     return fgCreateWindow( NULL, title, 
191                            fgState.Position.Use, fgState.Position.X, fgState.Position.Y,
192                            fgState.Size.Use, fgState.Size.X, fgState.Size.Y,
193                            GL_FALSE, GL_FALSE )->ID;
194 }
195
196 /*
197  * This function creates a sub window.
198  */
199 int FGAPIENTRY glutCreateSubWindow( int parentID, int x, int y, int w, int h )
200 {
201     int ret = 0;
202     SFG_Window* window = NULL;
203     SFG_Window* parent = NULL;
204
205     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutCreateSubWindow" );
206     parent = fgWindowByID( parentID );
207     freeglut_return_val_if_fail( parent != NULL, 0 );
208
209     if ( fgState.AllowNegativeWindowPosition )
210     {
211         /* XXX This results in different widths/heights than if AllowNegativeWindowPosition
212          * XXX was false. The "freeglut" community defined this logic.
213          * XXX (freeglut-developer e-mail from Diederick C. Niehorster, 11/15/2015, 4:06 PM EST.
214          * XXX "Re: [Freeglut-developer] glutInitWindowPosition with negative coordinate(s)")
215          */
216
217         if ( w < 0 ) w = parent->State.Width + w ;
218         if ( h < 0 ) h = parent->State.Height + h ;
219     }
220     else
221     {
222         if ( ( x < 0 ) )
223         {
224             x = parent->State.Width + x ;
225             if ( w > 0 ) x -= w ;
226         }
227
228         if ( w < 0 ) w = parent->State.Width - x + w ;
229         if ( w < 0 )
230         {
231             x += w ;
232             w = -w ;
233         }
234
235         if ( ( y < 0 ) )
236         {
237             y = parent->State.Height + y ;
238             if ( h > 0 ) y -= h ;
239         }
240
241         if ( h < 0 ) h = parent->State.Height - y + h ;
242         if ( h < 0 )
243         {
244             y += h ;
245             h = -h ;
246         }
247     }
248
249     window = fgCreateWindow( parent, "", 
250                              GL_TRUE, x, y, 
251                              GL_TRUE, w, h, 
252                              GL_FALSE, GL_FALSE );
253     ret = window->ID;
254
255     return ret;
256 }
257
258 /*
259  * Destroys a window and all of its subwindows
260  */
261 void FGAPIENTRY glutDestroyWindow( int windowID )
262 {
263     SFG_Window* window;
264     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutDestroyWindow" );
265     window = fgWindowByID( windowID );
266     freeglut_return_if_fail( window != NULL );
267     {
268         fgExecutionState ExecState = fgState.ExecState;
269         fgAddToWindowDestroyList( window );
270         fgState.ExecState = ExecState;
271     }
272 }
273
274 /*
275  * This function selects the specified window as the current window
276  */
277 void FGAPIENTRY glutSetWindow( int ID )
278 {
279     SFG_Window* window = NULL;
280
281     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSetWindow" );
282     if( fgStructure.CurrentWindow != NULL )
283         if( fgStructure.CurrentWindow->ID == ID )
284             return;
285
286     window = fgWindowByID( ID );
287     if( window == NULL )
288     {
289         fgWarning( "glutSetWindow(): window ID %d not found!", ID );
290         return;
291     }
292
293     fgSetWindow( window );
294 }
295
296 /*
297  * This function returns the ID number of the current window, 0 if none exists
298  */
299 int FGAPIENTRY glutGetWindow( void )
300 {
301     SFG_Window *win = fgStructure.CurrentWindow;
302     /*
303      * Since GLUT did not throw an error if this function was called without a prior call to
304      * "glutInit", this function shouldn't do so here.  Instead let us return a zero.
305      * See Feature Request "[ 1307049 ] glutInit check".
306      */
307     if ( ! fgState.Initialised )
308         return 0;
309
310     while ( win && win->IsMenu )
311         win = win->Parent;
312     return win ? win->ID : 0;
313 }
314
315 /*
316  * This function makes the current window visible
317  */
318 void FGAPIENTRY glutShowWindow( void )
319 {
320     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutShowWindow" );
321     FREEGLUT_EXIT_IF_NO_WINDOW ( "glutShowWindow" );
322
323     fgStructure.CurrentWindow->State.WorkMask |= GLUT_VISIBILITY_WORK;
324     fgStructure.CurrentWindow->State.DesiredVisibility = DesireNormalState;
325
326     fgStructure.CurrentWindow->State.WorkMask |= GLUT_DISPLAY_WORK;
327 }
328
329 /*
330  * This function hides the current window
331  */
332 void FGAPIENTRY glutHideWindow( void )
333 {
334     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutHideWindow" );
335     FREEGLUT_EXIT_IF_NO_WINDOW ( "glutHideWindow" );
336
337     fgStructure.CurrentWindow->State.WorkMask |= GLUT_VISIBILITY_WORK;
338     fgStructure.CurrentWindow->State.DesiredVisibility = DesireHiddenState;
339
340     fgStructure.CurrentWindow->State.WorkMask &= ~GLUT_DISPLAY_WORK;
341 }
342
343 /*
344  * Iconify the current window (top-level windows only)
345  */
346 void FGAPIENTRY glutIconifyWindow( void )
347 {
348     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutIconifyWindow" );
349     FREEGLUT_EXIT_IF_NO_WINDOW ( "glutIconifyWindow" );
350
351     fgStructure.CurrentWindow->State.WorkMask |= GLUT_VISIBILITY_WORK;
352     fgStructure.CurrentWindow->State.DesiredVisibility = DesireIconicState;
353
354     fgStructure.CurrentWindow->State.WorkMask &= ~GLUT_DISPLAY_WORK;
355 }
356
357 /*
358  * Set the current window's title
359  */
360 void FGAPIENTRY glutSetWindowTitle( const char* title )
361 {
362     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSetWindowTitle" );
363     FREEGLUT_EXIT_IF_NO_WINDOW ( "glutSetWindowTitle" );
364     if( ! fgStructure.CurrentWindow->Parent )
365     {
366         fgPlatformGlutSetWindowTitle ( title );
367     }
368 }
369
370 /*
371  * Set the current window's iconified title
372  */
373 void FGAPIENTRY glutSetIconTitle( const char* title )
374 {
375     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSetIconTitle" );
376     FREEGLUT_EXIT_IF_NO_WINDOW ( "glutSetIconTitle" );
377
378     if( ! fgStructure.CurrentWindow->Parent )
379     {
380         fgPlatformGlutSetIconTitle ( title );
381     }
382 }
383
384 /*
385  * Change the current window's size
386  */
387 void FGAPIENTRY glutReshapeWindow( int width, int height )
388 {
389     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutReshapeWindow" );
390     FREEGLUT_EXIT_IF_NO_WINDOW ( "glutReshapeWindow" );
391
392     if (glutGet(GLUT_FULL_SCREEN))
393     {
394       /*  Leave full screen state before resizing. */
395       glutLeaveFullScreen();
396     }
397
398     fgStructure.CurrentWindow->State.WorkMask |= GLUT_SIZE_WORK;
399     fgStructure.CurrentWindow->State.DesiredWidth  = width ;
400     fgStructure.CurrentWindow->State.DesiredHeight = height;
401 }
402
403 /*
404  * Change the current window's position
405  */
406 void FGAPIENTRY glutPositionWindow( int x, int y )
407 {
408     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutPositionWindow" );
409     FREEGLUT_EXIT_IF_NO_WINDOW ( "glutPositionWindow" );
410
411     if (glutGet(GLUT_FULL_SCREEN))
412     {
413       /*  Leave full screen state before moving. */
414       glutLeaveFullScreen();
415     }
416
417     fgStructure.CurrentWindow->State.WorkMask |= GLUT_POSITION_WORK;
418     fgStructure.CurrentWindow->State.DesiredXpos = x;
419     fgStructure.CurrentWindow->State.DesiredYpos = y;
420 }
421
422 /*
423  * Lowers the current window (by Z order change)
424  */
425 void FGAPIENTRY glutPushWindow( void )
426 {
427     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutPushWindow" );
428     FREEGLUT_EXIT_IF_NO_WINDOW ( "glutPushWindow" );
429
430     fgStructure.CurrentWindow->State.WorkMask |= GLUT_ZORDER_WORK;
431     fgStructure.CurrentWindow->State.DesiredZOrder = -1;
432 }
433
434 /*
435  * Raises the current window (by Z order change)
436  */
437 void FGAPIENTRY glutPopWindow( void )
438 {
439     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutPopWindow" );
440     FREEGLUT_EXIT_IF_NO_WINDOW ( "glutPopWindow" );
441
442     fgStructure.CurrentWindow->State.WorkMask |= GLUT_ZORDER_WORK;
443     fgStructure.CurrentWindow->State.DesiredZOrder = 1;
444 }
445
446 /*
447  * Resize the current window so that it fits the whole screen
448  */
449 void FGAPIENTRY glutFullScreen( void )
450 {
451     SFG_Window *win;
452
453     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutFullScreen" );
454     FREEGLUT_EXIT_IF_NO_WINDOW ( "glutFullScreen" );
455
456     win = fgStructure.CurrentWindow;
457
458     if (win->Parent)
459     {
460         /* Child windows cannot be made fullscreen, consistent with GLUT's behavior
461          * Also, what would it mean for a child window to be fullscreen, given that it
462          * is confined to its parent?
463          */
464         fgWarning("glutFullScreen called on a child window, ignoring...");
465         return;
466     }
467     else if (fgStructure.GameModeWindow != NULL && fgStructure.GameModeWindow->ID==win->ID && win->State.IsFullscreen)
468     {
469         /* Ignore fullscreen call on GameMode window, those are always fullscreen already
470          * only exception is when first entering GameMode
471          */
472         return;
473     }
474
475     if (!win->State.IsFullscreen)
476         win->State.WorkMask |= GLUT_FULL_SCREEN_WORK;
477 }
478
479 /*
480  * If we are fullscreen, resize the current window back to its original size
481  */
482 void FGAPIENTRY glutLeaveFullScreen( void )
483 {
484     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutFullScreen" );
485     FREEGLUT_EXIT_IF_NO_WINDOW ( "glutFullScreen" );
486
487     if (fgStructure.CurrentWindow->State.IsFullscreen)
488         fgStructure.CurrentWindow->State.WorkMask |= GLUT_FULL_SCREEN_WORK;
489 }
490
491 /*
492  * Toggle the window's full screen state.
493  */
494 void FGAPIENTRY glutFullScreenToggle( void )
495 {
496     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutFullScreenToggle" );
497     FREEGLUT_EXIT_IF_NO_WINDOW ( "glutFullScreenToggle" );
498
499     fgStructure.CurrentWindow->State.WorkMask |= GLUT_FULL_SCREEN_WORK;
500 }
501
502 /*
503  * A.Donev: Set and retrieve the window's user data
504  */
505 void* FGAPIENTRY glutGetWindowData( void )
506 {
507     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutGetWindowData" );
508     FREEGLUT_EXIT_IF_NO_WINDOW ( "glutGetWindowData" );
509     return fgStructure.CurrentWindow->UserData;
510 }
511
512 void FGAPIENTRY glutSetWindowData(void* data)
513 {
514     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSetWindowData" );
515     FREEGLUT_EXIT_IF_NO_WINDOW ( "glutSetWindowData" );
516     fgStructure.CurrentWindow->UserData = data;
517 }
518
519 /*** END OF FILE ***/