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