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