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