Added change from Nigel to have resize events cause redraw events in WIN32.
[freeglut] / src / freeglut_structure.c
1 /*
2  * freeglut_structure.c
3  *
4  * Windows and menus need tree structure
5  *
6  * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
7  * Written by Pawel W. Olszta, <olszta@sourceforge.net>
8  * Creation date: Sat Dec 18 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 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include "../include/GL/freeglut.h"
33 #include "freeglut_internal.h"
34
35
36 /* -- GLOBAL EXPORTS ------------------------------------------------------- */
37
38 /*
39  * The SFG_Structure container holds information about windows and menus
40  * created between glutInit() and glutMainLoop() return.
41  */
42
43 SFG_Structure fgStructure = { { NULL, NULL },  /* The list of windows       */
44                               { NULL, NULL },  /* The list of menus         */
45                               NULL,            /* The current window        */
46                               NULL,            /* The current menu          */
47                               NULL,            /* The menu OpenGL context   */
48                               NULL,            /* The game mode window      */
49                               0,               /* The current new window ID */
50                               0 };             /* The current new menu ID   */
51
52
53 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
54
55 void fgClearCallBacks( SFG_Window *window )
56 {
57     if( window )
58     {
59         int i;
60         for( i = 0; i < TOTAL_CALLBACKS; ++i )
61             window->CallBacks[ i ] = NULL;
62     }
63 }
64
65 /*
66  * This private function creates, opens and adds to the hierarchy
67  * a freeglut window complete with OpenGL context and stuff...
68  *
69  * If parent is set to NULL, the window created will be a topmost one.
70  */
71 SFG_Window* fgCreateWindow( SFG_Window* parent, const char* title,
72                             int x, int y, int w, int h,
73                             GLboolean gameMode, GLboolean isMenu )
74 {
75     /*
76      * Have the window object created
77      */
78     SFG_Window *window = (SFG_Window *)calloc( sizeof(SFG_Window), 1 );
79     int fakeArgc = 0;
80
81     fgClearCallBacks( window );
82
83     /*
84      * If the freeglut internals haven't been initialized yet,
85      * do it now. Hack's idea courtesy of Chris Purnell...
86      */
87     if( !fgState.Initialised )
88         glutInit( &fakeArgc, NULL );
89
90     /*
91      * Initialize the object properties
92      */
93     window->ID = ++fgStructure.WindowID;
94
95     fgListInit( &window->Children );
96     if( parent )
97     {
98         fgListAppend( &parent->Children, &window->Node );
99         window->Parent = parent;
100     }
101     else
102         fgListAppend( &fgStructure.Windows, &window->Node );
103
104     /*
105      * Set the default mouse cursor and reset the modifiers value
106      */
107     window->State.Cursor    = GLUT_CURSOR_INHERIT;
108
109     window->IsMenu = isMenu;
110
111     /*
112      * Open the window now. The fgOpenWindow() function is system
113      * dependant, and resides in freeglut_window.c. Uses fgState.
114      */
115     fgOpenWindow( window, title, x, y, w, h, gameMode,
116                   parent ? GL_TRUE : GL_FALSE );
117
118     return window;
119 }
120
121 /*
122  * This private function creates a menu and adds it to the menus list
123  */
124 SFG_Menu* fgCreateMenu( FGCBMenu menuCallback )
125 {
126     int x = 100, y = 100, w = 100, h = 100;
127     SFG_Window *current_window = fgStructure.Window;
128
129     /*
130      * Have the menu object created
131      */
132     SFG_Menu* menu = (SFG_Menu *)calloc( sizeof(SFG_Menu), 1 );
133     int fakeArgc = 0;
134
135     /*
136      * If the freeglut internals haven't been initialized yet,
137      * do it now. Hack's idea courtesy of Chris Purnell...
138      */
139     if( !fgState.Initialised )
140         glutInit( &fakeArgc, NULL );
141
142     menu->ParentWindow = fgStructure.Window;
143
144     /*
145      * Create a window for the menu to reside in.
146      */
147
148     fgCreateWindow( NULL, NULL, x, y, w, h, GL_FALSE, GL_TRUE );
149     menu->Window = fgStructure.Window;
150     glutDisplayFunc( fgDisplayMenu );
151
152     glutHideWindow( );  /* Hide the window for now */
153     fgSetWindow( current_window );
154
155     /*
156      * Initialize the object properties:
157      */
158     menu->ID       = ++fgStructure.MenuID;
159     menu->Callback = menuCallback;
160     menu->ActiveEntry = NULL;
161
162     fgListInit( &menu->Entries );
163     fgListAppend( &fgStructure.Menus, &menu->Node );
164
165     /*
166      * Newly created menus implicitly become current ones
167      */
168     fgStructure.Menu = menu;
169
170     return menu;
171 }
172
173 /*
174  * Linked list of windows to destroy ... this is so we don't destroy a
175  * window from the middle of its callback.  Some C compilers take an
176  * extremely dim view of this.
177  */
178
179 static SFG_WindowList* WindowsToDestroy = ( SFG_WindowList* )NULL;
180
181 /*
182  * Function to add a window to the linked list of windows to destroy.
183  * Subwindows are automatically added because they hang from the window
184  * structure.
185  */
186 void fgAddToWindowDestroyList( SFG_Window* window, GLboolean needToClose )
187 {
188     SFG_WindowList *new_list_entry =
189         ( SFG_WindowList* )malloc( sizeof(SFG_WindowList ) );
190     new_list_entry->window = window;
191     new_list_entry->needToClose = needToClose;
192     new_list_entry->next = WindowsToDestroy;
193     WindowsToDestroy = new_list_entry;
194
195     /*
196      * Check if the window is the current one...
197      */
198     if( fgStructure.Window == window )
199         fgStructure.Window = NULL;
200
201     /*
202      * Clear all window callbacks except Destroy, which will
203      * be invoked later.  Right now, we are potentially carrying
204      * out a freeglut operation at the behest of a client callback,
205      * so we are reluctant to re-enter the client with the Destroy
206      * callback, right now.  The others are all wiped out, however,
207      * to ensure that they are no longer called after this point.
208      */
209     {
210         void *destroy = FETCH_WCB( *window, Destroy );
211         fgClearCallBacks( window );
212         FETCH_WCB( *window, Destroy ) = destroy;
213     }
214     
215     /*
216      * Check the execution state.  If this has been called from
217      * "glutDestroyWindow", a statement in that function will reset the
218      * "ExecState" after this function returns.
219      */
220     if( fgState.ActionOnWindowClose != GLUT_ACTION_CONTINUE_EXECUTION )
221         /*
222          * Set the execution state flag to drop out of the main loop.
223          */
224         if( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
225             fgState.ExecState = GLUT_EXEC_STATE_STOP;
226 }
227
228 /*
229  * Function to close down all the windows in the "WindowsToDestroy" list
230  */
231 void fgCloseWindows( )
232 {
233     SFG_WindowList *window_ptr = WindowsToDestroy;
234     WindowsToDestroy = ( SFG_WindowList* )NULL;
235     /* In case the destroy callbacks cause more windows to be closed */
236
237     while( window_ptr )
238     {
239         SFG_WindowList *next = window_ptr->next;
240         fgDestroyWindow( window_ptr->window, window_ptr->needToClose );
241         free( window_ptr );
242         window_ptr = next;
243
244         if( !window_ptr )
245         {
246             window_ptr = WindowsToDestroy;
247             WindowsToDestroy = ( SFG_WindowList* )NULL;
248         }
249     }
250 }
251
252 /*
253  * This function destroys a window and all of its subwindows. Actually,
254  * another function, defined in freeglut_window.c is called, but this is
255  * a whole different story...
256  */
257 void fgDestroyWindow( SFG_Window* window, GLboolean needToClose )
258 {
259     SFG_Window* subWindow;
260     int menu_index ;
261
262     assert( window );
263     freeglut_assert_ready;
264
265     while( subWindow = ( SFG_Window * )window->Children.First )
266         fgDestroyWindow( subWindow, needToClose );
267
268     {
269         SFG_Window *activeWindow = fgStructure.Window ;
270         INVOKE_WCB( *window, Destroy, ( ) );
271         fgSetWindow ( activeWindow );
272     }
273
274     if( window->Parent )
275         fgListRemove( &window->Parent->Children, &window->Node );
276     else
277         fgListRemove( &fgStructure.Windows, &window->Node );
278
279     if( window->ActiveMenu )
280       fgDeactivateMenu( window );
281
282     for ( menu_index = 0; menu_index < 3; menu_index ++ )
283     {
284       if ( window->Menu[menu_index] )
285         window->Menu[menu_index]->ParentWindow = NULL ;
286     }
287
288     fgClearCallBacks( window );
289     if( needToClose )
290         fgCloseWindow( window );
291     free( window );
292     if( fgStructure.Window == window )
293         fgStructure.Window = NULL;
294 }
295
296 /*
297  * This is a helper static function that removes a menu (given its pointer)
298  * from any windows that can be accessed from a given parent...
299  */
300 static void fghRemoveMenuFromWindow( SFG_Window* window, SFG_Menu* menu )
301 {
302     SFG_Window *subWindow;
303     int i;
304
305     /*
306      * Check if the menu is attached to the current window,
307      * if so, have it detached (by overwriting with a NULL):
308      */
309     for( i = 0; i < 3; i++ )
310         if( window->Menu[ i ] == menu )
311             window->Menu[ i ] = NULL;
312
313     /*
314      * Call this function for all of the window's children recursively:
315      */
316     for( subWindow = (SFG_Window *)window->Children.First;
317          subWindow;
318          subWindow = (SFG_Window *)subWindow->Node.Next)
319         fghRemoveMenuFromWindow( subWindow, menu );
320 }
321
322 /*
323  * This is a static helper function that removes menu references
324  * from another menu, given two pointers to them...
325  */
326 static void fghRemoveMenuFromMenu( SFG_Menu* from, SFG_Menu* menu )
327 {
328     SFG_MenuEntry *entry;
329
330     for( entry = (SFG_MenuEntry *)from->Entries.First;
331          entry;
332          entry = ( SFG_MenuEntry * )entry->Node.Next )
333         if( entry->SubMenu == menu )
334             entry->SubMenu = NULL;
335 }
336
337 /*
338  * This function destroys a menu specified by the parameter. All menus
339  * and windows are updated to make sure no ill pointers hang around.
340  */
341 void fgDestroyMenu( SFG_Menu* menu )
342 {
343     SFG_Window *window;
344     SFG_Menu *from;
345     SFG_MenuEntry *entry;
346
347     assert( menu );
348     freeglut_assert_ready;
349
350     /*
351      * First of all, have all references to this menu removed from all windows:
352      */
353     for( window = (SFG_Window *)fgStructure.Windows.First;
354          window;
355          window = (SFG_Window *)window->Node.Next )
356         fghRemoveMenuFromWindow( window, menu );
357
358     /*
359      * Now proceed with removing menu entries that lead to this menu
360      */
361     for( from = ( SFG_Menu * )fgStructure.Menus.First;
362          from;
363          from = ( SFG_Menu * )from->Node.Next )
364         fghRemoveMenuFromMenu( from, menu );
365
366     /*
367      * If the programmer defined a destroy callback, call it
368      * A. Donev: But first make this the active menu
369      */
370     if( menu->Destroy )
371     {
372         SFG_Menu *activeMenu=fgStructure.Menu;
373         fgStructure.Menu = menu;
374         menu->Destroy( );
375         fgStructure.Menu = activeMenu;
376     }
377
378     /*
379      * Now we are pretty sure the menu is not used anywhere
380      * and that we can remove all of its entries
381      */
382     while( entry = ( SFG_MenuEntry * )menu->Entries.First )
383     {
384         fgListRemove( &menu->Entries, &entry->Node );
385
386         if( entry->Text )
387             free( entry->Text );
388         entry->Text = NULL;
389
390         free( entry );
391         entry = NULL;
392     }
393
394     if( fgStructure.Window == menu->Window )
395         fgSetWindow( menu->ParentWindow );
396     fgDestroyWindow( menu->Window, GL_TRUE );
397     fgListRemove( &fgStructure.Menus, &menu->Node );
398     if( fgStructure.Menu == menu )
399         fgStructure.Menu = NULL;
400
401     free( menu );
402 }
403
404 /*
405  * This function should be called on glutInit(). It will prepare the internal
406  * structure of freeglut to be used in the application. The structure will be
407  * destroyed using fgDestroyStructure() on glutMainLoop() return. In that
408  * case further use of freeglut should be preceeded with a glutInit() call.
409  */
410 void fgCreateStructure( void )
411 {
412     /*
413      * We will be needing two lists: the first containing windows,
414      * and the second containing the user-defined menus.
415      * Also, no current window/menu is set, as none has been created yet.
416      */
417
418     fgListInit(&fgStructure.Windows);
419     fgListInit(&fgStructure.Menus);
420 }
421
422 /*
423  * This function is automatically called on glutMainLoop() return.
424  * It should deallocate and destroy all remnants of previous
425  * glutInit()-enforced structure initialization...
426  */
427 void fgDestroyStructure( void )
428 {
429     SFG_Window *window;
430     SFG_Menu *menu;
431
432     freeglut_assert_ready;
433
434     /*
435      * Make sure all windows and menus have been deallocated
436      */
437     while( menu = ( SFG_Menu * )fgStructure.Menus.First )
438         fgDestroyMenu( menu );
439
440     while( window = ( SFG_Window * )fgStructure.Windows.First )
441         fgDestroyWindow( window, GL_TRUE );
442 }
443
444 /*
445  * Helper function to enumerate through all registered top-level windows
446  */
447 void fgEnumWindows( FGCBenumerator enumCallback, SFG_Enumerator* enumerator )
448 {
449     SFG_Window *window;
450
451     assert( enumCallback && enumerator );
452     freeglut_assert_ready;
453
454     /*
455      * Check every of the top-level windows
456      */
457     for( window = ( SFG_Window * )fgStructure.Windows.First;
458          window;
459          window = ( SFG_Window * )window->Node.Next )
460     {
461         enumCallback( window, enumerator );
462         if( enumerator->found )
463             return;
464     }
465 }
466
467 /*
468  * Helper function to enumerate through all a window's subwindows
469  * (single level descent)
470  */
471 void fgEnumSubWindows( SFG_Window* window, FGCBenumerator enumCallback,
472                        SFG_Enumerator* enumerator )
473 {
474     SFG_Window *child;
475
476     assert( enumCallback && enumerator );
477     freeglut_assert_ready;
478
479     for( child = ( SFG_Window * )window->Children.First;
480          child;
481          child = ( SFG_Window * )child->Node.Next )
482     {
483         enumCallback( child, enumerator );
484         if( enumerator->found )
485             return;
486     }
487 }
488
489 /*
490  * A static helper function to look for a window given its handle
491  */
492 static void fghcbWindowByHandle( SFG_Window *window,
493                                  SFG_Enumerator *enumerator )
494 {
495     if ( enumerator->found )
496         return;
497
498     /*
499      * Check the window's handle. Hope this works. Looks ugly. That's for sure.
500      */
501     if( window->Window.Handle == (SFG_WindowHandleType) (enumerator->data) )
502     {
503         enumerator->found = GL_TRUE;
504         enumerator->data = window;
505
506         return;
507     }
508
509     /*
510      * Otherwise, check this window's children
511      */
512     fgEnumSubWindows( window, fghcbWindowByHandle, enumerator );
513 }
514
515 /*
516  * fgWindowByHandle returns a (SFG_Window *) value pointing to the
517  * first window in the queue matching the specified window handle.
518  * The function is defined in freeglut_structure.c file.
519  */
520 SFG_Window* fgWindowByHandle ( SFG_WindowHandleType hWindow )
521 {
522     SFG_Enumerator enumerator;
523
524     /*
525      * This is easy and makes use of the windows enumeration defined above
526      */
527     enumerator.found = GL_FALSE;
528     enumerator.data = (void *)hWindow;
529     fgEnumWindows( fghcbWindowByHandle, &enumerator );
530
531     if( enumerator.found )
532         return( SFG_Window *) enumerator.data;
533     return NULL;
534 }
535
536 /*
537  * A static helper function to look for a window given its ID
538  */
539 static void fghcbWindowByID( SFG_Window *window, SFG_Enumerator *enumerator )
540 {
541     /*
542      * Make sure we do not overwrite our precious results...
543      */
544     if ( enumerator->found )
545         return;
546
547     /*
548      * Check the window's handle. Hope this works. Looks ugly. That's for sure.
549      */
550     if( window->ID == (int) (enumerator->data) ) /* XXX int/ptr conversion! */
551     {
552         enumerator->found = GL_TRUE;
553         enumerator->data = window;
554
555         return;
556     }
557
558     /*
559      * Otherwise, check this window's children
560      */
561     fgEnumSubWindows( window, fghcbWindowByID, enumerator );
562 }
563
564 /*
565  * This function is similiar to the previous one, except it is
566  * looking for a specified (sub)window identifier. The function
567  * is defined in freeglut_structure.c file.
568  */
569 SFG_Window* fgWindowByID( int windowID )
570 {
571     SFG_Enumerator enumerator;
572
573     /*
574      * Uses a method very similiar for fgWindowByHandle...
575      */
576     enumerator.found = GL_FALSE;
577     enumerator.data = (void *) windowID; /* XXX int/pointer conversion! */
578     fgEnumWindows( fghcbWindowByID, &enumerator );
579     if( enumerator.found )
580         return( SFG_Window *) enumerator.data;
581     return NULL;
582 }
583
584 /*
585  * Looks up a menu given its ID. This is easier that fgWindowByXXX
586  * as all menus are placed in a single doubly linked list...
587  */
588 SFG_Menu* fgMenuByID( int menuID )
589 {
590     SFG_Menu *menu = NULL;
591
592     freeglut_assert_ready;
593
594     /*
595      * It's enough to check all entries in fgStructure.Menus...
596      */
597     for( menu = (SFG_Menu *)fgStructure.Menus.First;
598          menu;
599          menu = (SFG_Menu *)menu->Node.Next )
600         if( menu->ID == menuID )
601             return menu;
602     return NULL;
603 }
604
605 /*
606  * List functions...
607  */
608 void fgListInit(SFG_List *list)
609 {
610     list->First = NULL;
611     list->Last = NULL;
612 }
613
614 void fgListAppend(SFG_List *list, SFG_Node *node)
615 {
616     SFG_Node *ln;
617
618     if ( ln = (SFG_Node *)list->Last )
619     {
620         ln->Next = node;
621         node->Prev = ln;
622     }
623     else
624     {
625         node->Prev = NULL;
626         list->First = node;
627     }
628
629     node->Next = NULL;
630     list->Last = node;
631 }
632
633 void fgListRemove(SFG_List *list, SFG_Node *node)
634 {
635     SFG_Node *ln;
636
637     if( ln = (SFG_Node *)node->Next )
638         ln->Prev = node->Prev;
639     if( ln = (SFG_Node *)node->Prev )
640         ln->Next = node->Next;
641     if( (ln = (SFG_Node *)list->First) == node )
642         list->First = node->Next;
643     if( (ln = (SFG_Node *)list->Last) == node )
644         list->Last = node->Prev;
645 }
646
647 int fgListLength(SFG_List *list)
648 {
649     SFG_Node *node;
650     int length = 0;
651
652     for( node =( SFG_Node * )list->First;
653          node;
654          node = ( SFG_Node * )node->Next )
655         ++length;
656
657     return length;
658 }
659
660 /*** END OF FILE ***/