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