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