af851f1f28da5f05501b29d3dc2f60475a3edaa9
[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 "../include/GL/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 gchar* title, gint x, gint y, gint w, gint h, gboolean gameMode )
56 {
57     /*
58      * Have the window object created
59      */
60     SFG_Window* window = g_new0( SFG_Window, 1 );
61     gint 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.Timer == NULL )
68         glutInit( &fakeArgc, NULL );
69
70     /*
71      * Initialize the object properties
72      */
73     window->ID = ++fgStructure.WindowID;
74
75     /*
76      * Does this window have a parent?
77      */
78     if( parent != NULL )
79     {
80         /*
81          * That's quite right, attach this windows as a child window
82          */
83         parent->Children = g_list_append( parent->Children, window );
84         window->Parent = parent;
85     }
86     else
87     {
88         /*
89          * Otherwise add the newly created window to the topmost windows list
90          */
91         fgStructure.Windows = g_list_append( fgStructure.Windows, window );
92     }
93
94     /*
95      * Set the default mouse cursor and reset the modifiers value
96      */
97     window->State.Cursor    = GLUT_CURSOR_INHERIT;
98     window->State.Modifiers = 0xffffffff;
99
100     /*
101      * Open the window now. The fgOpenWindow() function is system
102      * dependant, and resides in freeglut_window.c. Uses fgState.
103      */
104     fgOpenWindow( window, title, x, y, w, h, gameMode );
105
106     /*
107      * Return a pointer to the newly created window
108      */
109     return( window );
110 }
111
112 /*
113  * This private function creates a menu and adds it to the menus list
114  */
115 SFG_Menu* fgCreateMenu( FGCBmenu menuCallback )
116 {
117     /*
118      * Have the menu object created
119      */
120     SFG_Menu* menu = g_new0( SFG_Menu, 1 );
121     gint fakeArgc = 0;
122
123     /*
124      * If the freeglut internals haven't been initialized yet,
125      * do it now. Hack's idea courtesy of Chris Purnell...
126      */
127     if( fgState.Timer == NULL )
128         glutInit( &fakeArgc, NULL );
129
130     /*
131      * Initialize the object properties:
132      */
133     menu->ID       = ++fgStructure.MenuID;
134     menu->Callback = menuCallback;
135
136     /*
137      * Add it to the menu structure hierarchy
138      */
139     fgStructure.Menus = g_list_append( fgStructure.Menus, menu );
140
141     /*
142      * Newly created menus implicitly become current ones
143      */
144     fgStructure.Menu = menu;
145
146     /*
147      * Return the result to the caller
148      */
149     return( menu );
150 }
151
152 /*
153  * This function destroys a window and all of it's subwindows. Actually,
154  * another function, defined in freeglut_window.c is called, but this is
155  * a whole different story...
156  */
157 void fgDestroyWindow( SFG_Window* window, gboolean needToClose )
158 {
159     int i;
160
161     g_assert( window != NULL );
162     freeglut_assert_ready;
163
164     /*
165      * Does this window have any subwindows?
166      */
167     if( window->Children != NULL )
168     {
169         /*
170          * OKi, while there are any subwindows left...
171          */
172         while( g_list_first( window->Children ) != NULL )
173         {
174             SFG_Window* subWindow = g_list_first( window->Children )->data;
175
176             /*
177              * Destroy the first window in the list (possibly destroying
178              * it's subwindows too. This is not very effective, but works
179              */
180             fgDestroyWindow( subWindow, TRUE );
181
182             /*
183              * Remove the just destroyed node from the subwindows list
184              */
185             window->Children = g_list_remove( window->Children, subWindow );
186         }
187
188         /*
189          * Have the list freed now (probably it already is, but you can
190          * never be sure with no GLib documentation on your hdd...)
191          */
192         g_list_free( window->Children );
193         window->Children = NULL;
194     }
195
196     /*
197      * Now we should remove the reference to this window from it's parent
198      */
199     if( window->Parent != NULL )
200         window->Parent->Children = g_list_remove( window->Parent->Children, window );
201
202     /*
203      * OK, this window seems disconnected from the structure enough
204      * in order to be closed without any bigger risks...
205      */
206         if( needToClose == TRUE )
207                 fgCloseWindow( window );
208
209     /*
210      * Try removing the window from the parents list in fgStructure.
211      * This might fail as the window is not guaranteed to be there:
212      */
213     fgStructure.Windows = g_list_remove( fgStructure.Windows, window );
214
215     /*
216      * Check if the window is the current one...
217      */
218     if( fgStructure.Window == window )
219         fgStructure.Window = NULL;
220
221     /*
222      * Finally, we can delete the window's object. It hopefully does
223      * have everything inside it freed and we do not have to care...
224      */
225     g_free( window );
226 }
227
228 /*
229  * This is a helper static function that removes a menu (given it's pointer)
230  * from any windows that can be accessed from a given parent...
231  */
232 static void fghRemoveMenuFromWindow( SFG_Window* window, SFG_Menu* menu )
233 {
234     gint i;
235
236     /*
237      * Check if the menu is attached to the current window,
238      * if so, have it detached (by overwriting with a NULL):
239      */
240     for( i=0; i<3; i++ )
241         if( window->Menu[ i ] == menu )
242             window->Menu[ i ] = NULL;
243
244     /*
245      * Call this function for all of the window's children recursively:
246      */
247     for( i=0; i<(gint) g_list_length( window->Children ); i++ )
248         fghRemoveMenuFromWindow(
249             (SFG_Window *) g_list_nth( window->Children, i )->data,
250             menu
251         );
252 }
253
254 /*
255  * This is a static helper function that removes menu references
256  * from another menu, given two pointers to them...
257  */
258 static void fghRemoveMenuFromMenu( SFG_Menu* from, SFG_Menu* menu )
259 {
260     gboolean found = FALSE;
261
262     /*
263      * Do not allow removing a menu from itself...
264      */
265     if( from == menu )
266         return;
267
268     /*
269      * Look up for the first entry that matches the given
270      * menu and have it removed, then search again and again:
271      */
272     do
273     {
274         /*
275          * Try searching for the incriminated menu entry
276          */
277         GList* where = g_list_find( from->Entries, menu );
278
279         /*
280          * Make sure we still have a list to be searched
281          */
282         if( where != NULL )
283         {
284             /*
285              * Did we actually find the menu entry we want to remove?
286              */
287             found = ((SFG_Menu *) where->data == menu);
288
289             /*
290              * Need to check that, as the search might have failed
291              */
292             if( found )
293                 from->Entries = g_list_remove( from->Entries, menu );
294         }
295         else
296         {
297             /*
298              * It would be nice if we had a stop rule ;-)
299              */
300             found = FALSE;
301         }
302     } while( found == TRUE );
303 }
304
305 /*
306  * This function destroys a menu specified by the parameter. All menus
307  * and windows are updated to make sure no ill pointers hang around.
308  */
309 void fgDestroyMenu( SFG_Menu* menu )
310 {
311     gint i;
312
313     g_assert( menu != NULL );
314     freeglut_assert_ready;
315
316     /*
317      * First of all, have all references to this menu removed from all windows:
318      */
319     for( i=0; i<(gint) g_list_length( fgStructure.Windows ); i++ )
320         fghRemoveMenuFromWindow(
321             (SFG_Window *) g_list_nth( fgStructure.Windows, i )->data,
322             menu
323         );
324
325     /*
326      * Now proceed with removing menu entries that lead to this menu
327      */
328     for( i=0; i<(gint) g_list_length( fgStructure.Menus ); i++ )
329         fghRemoveMenuFromMenu(
330             (SFG_Menu *) g_list_nth( fgStructure.Menus, i )->data,
331             menu
332         );
333
334     /*
335      * Now we are pretty sure the menu is not used anywhere
336      * and that we can remove all of it's entries
337      */
338     for( i=0; i<(gint) g_list_length( menu->Entries ); i++ )
339     {
340         SFG_MenuEntry* entry = (SFG_MenuEntry *) g_list_nth( menu->Entries, i )->data;
341
342         /*
343          * There might be a string allocated, have it freed:
344          */
345         g_string_free( entry->Text, TRUE );
346
347         /*
348          * Deallocate the entry itself:
349          */
350         g_free( entry );
351     }
352
353     /*
354      * Deallocate the entries list
355      */
356     g_list_free( menu->Entries );
357     menu->Entries = NULL;
358
359     /*
360      * Remove the menu from the menus list
361      */
362     fgStructure.Menus = g_list_remove( fgStructure.Menus, menu );
363
364     /*
365      * If that menu was the current one...
366      */
367     if( fgStructure.Menu == menu )
368         fgStructure.Menu = NULL;
369
370     /*
371      * Have the menu structure freed
372      */
373     g_free( menu );
374 }
375
376 /*
377  * This function should be called on glutInit(). It will prepare the internal
378  * structure of freeglut to be used in the application. The structure will be
379  * destroyed using fgDestroyStructure() on glutMainLoop() return. In that
380  * case further use of freeglut should be preceeded with a glutInit() call.
381  */
382 void fgCreateStructure( void )
383 {
384     /*
385      * We will be needing two lists: the first containing windows, and the second
386          * containing the user-defined menus. However we do not need allocating anything, 
387          * as it is done automagically by GLib when appending new entries to both of them. 
388          * Also, no current window/menu is set, as none has been created yet.
389      */
390 }
391
392 /*
393  * This function is automatically called on glutMainLoop() return. It should deallocate 
394  * and destroy all remnants of previous glutInit()-enforced structure initialization...
395  */
396 void fgDestroyStructure( void )
397 {
398         /*
399          * Just make sure we are not called in vain...
400          */
401     freeglut_assert_ready;
402
403     /*
404      * Make sure all windows and menus have been deallocated
405      */
406     while( fgStructure.Windows != NULL )
407         fgDestroyWindow( (SFG_Window *) g_list_first( fgStructure.Windows )->data, TRUE );
408
409     while( fgStructure.Menus != NULL )
410         fgDestroyMenu( (SFG_Menu *) g_list_first( fgStructure.Menus )->data );
411 }
412
413 /*
414  * Helper function to enumerate through all registered top-level windows
415  */
416 void fgEnumWindows( GFunc enumCallback, SFG_Enumerator* enumerator )
417 {
418     gint i;
419
420     g_assert( (enumCallback != NULL) && (enumerator != NULL) );
421     freeglut_assert_ready;
422
423     /*
424      * Check every of the top-level windows
425      */
426     for( i=0; i<(gint) g_list_length( fgStructure.Windows ); i++ )
427     {
428         /*
429          * Execute the callback...
430          */
431         enumCallback( (gpointer) g_list_nth( fgStructure.Windows, i )->data, (gpointer) enumerator );
432
433         /*
434          * If it has been marked as 'found', stop searching
435          */
436         if( enumerator->found == TRUE )
437             return;
438     }
439 }
440
441 /*
442  * Helper function to enumerate through all a window's subwindows (single level descent)
443  */
444 void fgEnumSubWindows( SFG_Window* window, GFunc enumCallback, SFG_Enumerator* enumerator )
445 {
446     gint i;
447
448     g_assert( (enumCallback != NULL) && (enumerator != NULL) );
449     freeglut_assert_ready;
450
451     /*
452      * Check every of the window's children:
453      */
454     for( i=0; i<(gint) g_list_length( window->Children ); i++ )
455     {
456         /*
457          * Execute the callback...
458          */
459         enumCallback( (gpointer) g_list_nth( window->Children, i )->data, (gpointer) enumerator );
460
461         /*
462          * If it has been marked as 'found', stop searching
463          */
464         if( enumerator->found == TRUE )
465             return;
466     }
467 }
468
469 /*
470  * A static helper function to look for a window given it's handle
471  */
472 static void fghcbWindowByHandle( gpointer window, gpointer enumerator )
473 {
474     /*
475      * Make sure we do not overwrite our precious results...
476      */
477     freeglut_return_if_fail( ((SFG_Enumerator *) enumerator)->found == FALSE );
478
479 #if TARGET_HOST_UNIX_X11
480     #define WBHANDLE (Window)
481 #elif TARGET_HOST_WIN32
482     #define WBHANDLE (HWND)
483 #endif
484
485     /*
486      * Check the window's handle. Hope this works. Looks ugly. That's for sure.
487      */
488     if( ((SFG_Window *) window)->Window.Handle == (WBHANDLE ((SFG_Enumerator *) enumerator)->data) )
489     {
490         ((SFG_Enumerator *) enumerator)->found = TRUE;
491         ((SFG_Enumerator *) enumerator)->data = (gpointer) window;
492
493         return;
494     }
495
496     /*
497      * Otherwise, check this window's children
498      */
499     fgEnumSubWindows( (SFG_Window *) window, fghcbWindowByHandle, enumerator );
500
501 #undef WBHANDLE
502 }
503
504 /*
505  * fgWindowByHandle returns a (SFG_Window *) value pointing to the
506  * first window in the queue matching the specified window handle.
507  * The function is defined in freeglut_structure.c file.
508  */
509 SFG_Window* fgWindowByHandle
510 #if TARGET_HOST_UNIX_X11
511 ( Window hWindow )
512 #elif TARGET_HOST_WIN32
513 ( HWND hWindow )
514 #endif
515 {
516     SFG_Enumerator enumerator;
517
518     /*
519      * This is easy and makes use of the windows enumeration defined above
520      */
521     enumerator.found = FALSE;
522     enumerator.data = (gpointer) hWindow;
523
524     /*
525      * Start the enumeration now:
526      */
527     fgEnumWindows( fghcbWindowByHandle, &enumerator );
528
529     /*
530      * Check if the window has been found or not:
531      */
532     if( enumerator.found == TRUE )
533         return( (SFG_Window *) enumerator.data );
534
535     /*
536      * Otherwise return NULL to mark the failure
537      */
538     return( NULL );
539 }
540
541 /*
542  * A static helper function to look for a window given it's ID
543  */
544 static void fghcbWindowByID( gpointer window, gpointer enumerator )
545 {
546     /*
547      * Make sure we do not overwrite our precious results...
548      */
549     g_return_if_fail( ((SFG_Enumerator *) enumerator)->found == FALSE );
550
551     /*
552      * Check the window's handle. Hope this works. Looks ugly. That's for sure.
553      */
554     if( ((SFG_Window *) window)->ID == (gint) (((SFG_Enumerator *) enumerator)->data) )
555     {
556         ((SFG_Enumerator *) enumerator)->found = TRUE;
557         ((SFG_Enumerator *) enumerator)->data = (gpointer) window;
558
559         return;
560     }
561
562     /*
563      * Otherwise, check this window's children
564      */
565     fgEnumSubWindows( (SFG_Window *) window, fghcbWindowByID, enumerator );
566 }
567
568 /*
569  * This function is similiar to the previous one, except it is
570  * looking for a specified (sub)window identifier. The function
571  * is defined in freeglut_structure.c file.
572  */
573 SFG_Window* fgWindowByID( gint windowID )
574 {
575     SFG_Enumerator enumerator;
576
577     /*
578      * Uses a method very similiar for fgWindowByHandle...
579      */
580     enumerator.found = FALSE;
581     enumerator.data = (gpointer) windowID;
582
583     /*
584      * Start the enumeration now:
585      */
586     fgEnumWindows( fghcbWindowByID, &enumerator );
587
588     /*
589      * Check if the window has been found or not:
590      */
591     if( enumerator.found == TRUE )
592         return( (SFG_Window *) enumerator.data );
593
594     /*
595      * Otherwise return NULL to mark the failure
596      */
597     return( NULL );
598 }
599
600 /*
601  * Looks up a menu given it's ID. This is easier that fgWindowByXXX
602  * as all menus are placed in a single doubly linked list...
603  */
604 SFG_Menu* fgMenuByID( gint menuID )
605 {
606     SFG_Menu *menu = NULL;
607     gint i;
608
609     freeglut_assert_ready;
610
611     /*
612      * It's enough to check all entries in fgStructure.Menus...
613      */
614     for( i=0; i<(gint) g_list_length( fgStructure.Menus ); i++ )
615     {
616         /*
617          * Grab the n-th element of the menu objects list...
618          */
619         menu = (SFG_Menu *) g_list_nth( fgStructure.Menus, i )->data;
620
621         /*
622          * Does the ID number match?
623          */
624         if( menu->ID == menuID )
625             return( menu );
626     }
627
628     /*
629      * We have not found the requested menu ID
630      */
631     return( NULL );
632 }
633
634 /*** END OF FILE ***/
635
636
637
638
639
640
641
642
643