Eero Pajarre's icon code.
[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      */
256     if ( window->Callbacks.Destroy != NULL )
257       window->Callbacks.Destroy () ;
258
259     /*
260      * Now we should remove the reference to this window from its parent
261      */
262     if ( window->Parent != NULL )
263         fgListRemove( &window->Parent->Children, &window->Node );
264     else
265         fgListRemove( &fgStructure.Windows, &window->Node );
266
267     /*
268      * OK, this window seems disconnected from the structure enough
269      * in order to be closed without any bigger risks...
270      */
271     if( needToClose == TRUE )
272         fgCloseWindow( window );
273
274     /*
275      * Finally, we can delete the window's object. It hopefully does
276      * have everything inside it freed and we do not have to care...
277      */
278     free( window );
279 }
280
281 /*
282  * This is a helper static function that removes a menu (given its pointer)
283  * from any windows that can be accessed from a given parent...
284  */
285 static void fghRemoveMenuFromWindow( SFG_Window* window, SFG_Menu* menu )
286 {
287     SFG_Window *subWindow;
288     int i;
289
290     /*
291      * Check if the menu is attached to the current window,
292      * if so, have it detached (by overwriting with a NULL):
293      */
294     for( i=0; i<3; i++ )
295     {
296         if( window->Menu[ i ] == menu )
297             window->Menu[ i ] = NULL;
298     }
299
300     /*
301      * Call this function for all of the window's children recursively:
302      */
303     for( subWindow = window->Children.First; subWindow;
304          subWindow = subWindow->Node.Next)
305     {
306         fghRemoveMenuFromWindow( subWindow, menu );
307     }
308 }
309
310 /*
311  * This is a static helper function that removes menu references
312  * from another menu, given two pointers to them...
313  */
314 static void fghRemoveMenuFromMenu( SFG_Menu* from, SFG_Menu* menu )
315 {
316   SFG_MenuEntry *entry;
317
318   for( entry = from->Entries.First; entry; entry = entry->Node.Next )
319   {
320     if (entry->SubMenu == menu)
321     {
322       entry->SubMenu = NULL;
323     }
324   }
325 }
326
327 /*
328  * This function destroys a menu specified by the parameter. All menus
329  * and windows are updated to make sure no ill pointers hang around.
330  */
331 void fgDestroyMenu( SFG_Menu* menu )
332 {
333   SFG_Window *window;
334   SFG_Menu *from;
335   SFG_MenuEntry *entry;
336
337   assert( menu != NULL );
338   freeglut_assert_ready;
339
340   /*
341    * First of all, have all references to this menu removed from all windows:
342    */
343   for( window = fgStructure.Windows.First; window; window = window->Node.Next )
344   {
345     fghRemoveMenuFromWindow( window, menu );
346   }
347
348   /*
349    * Now proceed with removing menu entries that lead to this menu
350    */
351   for( from = fgStructure.Menus.First; from; from = from->Node.Next )
352   {
353     fghRemoveMenuFromMenu( from, menu );
354   }
355
356   /*
357    * Now we are pretty sure the menu is not used anywhere
358    * and that we can remove all of its entries
359    */
360   while( (entry = menu->Entries.First) != NULL )
361   {
362     fgListRemove(&menu->Entries, &entry->Node);
363
364     /*
365      * There might be a string allocated, have it freed:
366      */
367     free( entry->Text );
368
369     /*
370      * Deallocate the entry itself:
371      */
372     free( entry );
373   }
374
375   /*
376    * Remove the menu from the menus list
377    */
378   fgListRemove( &fgStructure.Menus, &menu->Node );
379
380   /*
381    * If that menu was the current one...
382    */
383   if( fgStructure.Menu == menu )
384     fgStructure.Menu = NULL;
385
386   /*
387    * Have the menu structure freed
388    */
389   free( menu );
390 }
391
392 /*
393  * This function should be called on glutInit(). It will prepare the internal
394  * structure of freeglut to be used in the application. The structure will be
395  * destroyed using fgDestroyStructure() on glutMainLoop() return. In that
396  * case further use of freeglut should be preceeded with a glutInit() call.
397  */
398 void fgCreateStructure( void )
399 {
400   /*
401    * We will be needing two lists: the first containing windows,
402    * and the second containing the user-defined menus.
403    * Also, no current window/menu is set, as none has been created yet.
404    */
405
406   fgListInit(&fgStructure.Windows);
407   fgListInit(&fgStructure.Menus);
408 }
409
410 /*
411  * This function is automatically called on glutMainLoop() return. It should deallocate 
412  * and destroy all remnants of previous glutInit()-enforced structure initialization...
413  */
414 void fgDestroyStructure( void )
415 {
416   SFG_Window *window;
417   SFG_Menu *menu;
418
419   /*
420    * Just make sure we are not called in vain...
421    */
422   freeglut_assert_ready;
423
424   /*
425    * Make sure all windows and menus have been deallocated
426    */
427   while( (window = fgStructure.Windows.First) != NULL )
428     fgDestroyWindow( window, TRUE );
429
430   while( (menu = fgStructure.Menus.First) != NULL )
431     fgDestroyMenu( menu );
432 }
433
434 /*
435  * Helper function to enumerate through all registered top-level windows
436  */
437 void fgEnumWindows( FGCBenumerator enumCallback, SFG_Enumerator* enumerator )
438 {
439   SFG_Window *window;
440
441   assert( (enumCallback != NULL) && (enumerator != NULL) );
442   freeglut_assert_ready;
443
444   /*
445    * Check every of the top-level windows
446    */
447   for( window = fgStructure.Windows.First; window;
448        window = window->Node.Next )
449   {
450     /*
451      * Execute the callback...
452      */
453     enumCallback( window, enumerator );
454
455     /*
456      * If it has been marked as 'found', stop searching
457      */
458     if( enumerator->found == TRUE )
459       return;
460   }
461 }
462
463 /*
464  * Helper function to enumerate through all a window's subwindows (single level descent)
465  */
466 void fgEnumSubWindows( SFG_Window* window, FGCBenumerator enumCallback, SFG_Enumerator* enumerator )
467 {
468   SFG_Window *child;
469
470   assert( (enumCallback != NULL) && (enumerator != NULL) );
471   freeglut_assert_ready;
472
473   /*
474    * Check every of the window's children:
475    */
476   for( child = window->Children.First; child; child = child->Node.Next )
477   {
478     /*
479      * Execute the callback...
480      */
481     enumCallback( child, enumerator );
482
483     /*
484      * If it has been marked as 'found', stop searching
485      */
486     if( enumerator->found == TRUE )
487       return;
488   }
489 }
490
491 /*
492  * A static helper function to look for a window given its handle
493  */
494 static void fghcbWindowByHandle( SFG_Window *window, SFG_Enumerator *enumerator )
495 {
496     /*
497      * Make sure we do not overwrite our precious results...
498      */
499     freeglut_return_if_fail( enumerator->found == FALSE );
500
501 #if TARGET_HOST_UNIX_X11
502     #define WBHANDLE (Window)
503 #elif TARGET_HOST_WIN32
504     #define WBHANDLE (HWND)
505 #endif
506
507     /*
508      * Check the window's handle. Hope this works. Looks ugly. That's for sure.
509      */
510     if( window->Window.Handle == WBHANDLE (enumerator->data) )
511     {
512         enumerator->found = TRUE;
513         enumerator->data = window;
514
515         return;
516     }
517
518     /*
519      * Otherwise, check this window's children
520      */
521     fgEnumSubWindows( window, fghcbWindowByHandle, enumerator );
522
523 #undef WBHANDLE
524 }
525
526 /*
527  * fgWindowByHandle returns a (SFG_Window *) value pointing to the
528  * first window in the queue matching the specified window handle.
529  * The function is defined in freeglut_structure.c file.
530  */
531 SFG_Window* fgWindowByHandle
532 #if TARGET_HOST_UNIX_X11
533 ( Window hWindow )
534 #elif TARGET_HOST_WIN32
535 ( HWND hWindow )
536 #endif
537 {
538     SFG_Enumerator enumerator;
539
540     /*
541      * This is easy and makes use of the windows enumeration defined above
542      */
543     enumerator.found = FALSE;
544     enumerator.data = (void *)hWindow;
545
546     /*
547      * Start the enumeration now:
548      */
549     fgEnumWindows( fghcbWindowByHandle, &enumerator );
550
551     /*
552      * Check if the window has been found or not:
553      */
554     if( enumerator.found == TRUE )
555         return( (SFG_Window *) enumerator.data );
556
557     /*
558      * Otherwise return NULL to mark the failure
559      */
560     return( NULL );
561 }
562
563 /*
564  * A static helper function to look for a window given its ID
565  */
566 static void fghcbWindowByID( SFG_Window *window, SFG_Enumerator *enumerator )
567 {
568     /*
569      * Make sure we do not overwrite our precious results...
570      */
571     freeglut_return_if_fail( enumerator->found == FALSE );
572
573     /*
574      * Check the window's handle. Hope this works. Looks ugly. That's for sure.
575      */
576     if( window->ID == (int) (enumerator->data) )
577     {
578         enumerator->found = TRUE;
579         enumerator->data = window;
580
581         return;
582     }
583
584     /*
585      * Otherwise, check this window's children
586      */
587     fgEnumSubWindows( window, fghcbWindowByID, enumerator );
588 }
589
590 /*
591  * This function is similiar to the previous one, except it is
592  * looking for a specified (sub)window identifier. The function
593  * is defined in freeglut_structure.c file.
594  */
595 SFG_Window* fgWindowByID( int windowID )
596 {
597     SFG_Enumerator enumerator;
598
599     /*
600      * Uses a method very similiar for fgWindowByHandle...
601      */
602     enumerator.found = FALSE;
603     enumerator.data = (void *) windowID;
604
605     /*
606      * Start the enumeration now:
607      */
608     fgEnumWindows( fghcbWindowByID, &enumerator );
609
610     /*
611      * Check if the window has been found or not:
612      */
613     if( enumerator.found == TRUE )
614         return( (SFG_Window *) enumerator.data );
615
616     /*
617      * Otherwise return NULL to mark the failure
618      */
619     return( NULL );
620 }
621
622 /*
623  * Looks up a menu given its ID. This is easier that fgWindowByXXX
624  * as all menus are placed in a single doubly linked list...
625  */
626 SFG_Menu* fgMenuByID( int menuID )
627 {
628     SFG_Menu *menu = NULL;
629
630     freeglut_assert_ready;
631
632     /*
633      * It's enough to check all entries in fgStructure.Menus...
634      */
635     for( menu = fgStructure.Menus.First; menu; menu = menu->Node.Next )
636     {
637         /*
638          * Does the ID number match?
639          */
640         if( menu->ID == menuID )
641             return( menu );
642     }
643
644     /*
645      * We have not found the requested menu ID
646      */
647     return( NULL );
648 }
649
650 /*
651  * List functions...
652  */
653 void fgListInit(SFG_List *list)
654 {
655     list->First = NULL;
656     list->Last = NULL;
657 }
658
659 void fgListAppend(SFG_List *list, SFG_Node *node)
660 {
661     SFG_Node *ln;
662
663     if ( (ln = list->Last) != NULL )
664     {
665         ln->Next = node;
666         node->Prev = ln;
667     }
668     else
669     {
670         node->Prev = NULL;
671         list->First = node;
672     }
673
674     node->Next = NULL;
675     list->Last = node;
676 }
677
678 void fgListRemove(SFG_List *list, SFG_Node *node)
679 {
680     SFG_Node *ln;
681
682     if ( (ln = node->Next) != NULL )
683         ln->Prev = node->Prev;
684     if ( (ln = node->Prev) != NULL )
685         ln->Next = node->Next;
686     if ( (ln = list->First) == node )
687         list->First = node->Next;
688     if ( (ln = list->Last) == node )
689         list->Last = node->Prev;
690 }
691
692 int fgListLength(SFG_List *list)
693 {
694     SFG_Node *node;
695     int length = 0;
696
697     for( node = list->First; node; node = node->Next )
698         ++length;
699
700     return( length );
701 }
702
703 /*** END OF FILE ***/