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