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