Visibility functions better implemented now. All goes through
[freeglut] / src / x11 / fg_window_x11.c
1 /*
2  * freeglut_window_x11.c
3  *
4  * Window management methods for X11
5  *
6  * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
7  * Written by Pawel W. Olszta, <olszta@sourceforge.net>
8  * Copied for Platform code by Evan Felix <karcaw at gmail.com>
9  * Creation date: Thur Feb 2 2012
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
24  * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
25  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27  */
28
29 #define FREEGLUT_BUILDING_LIB
30 #include <GL/freeglut.h>
31 #include <limits.h>  /* LONG_MAX */
32 #include <unistd.h>  /* usleep */
33 #include "../fg_internal.h"
34
35 extern void fghRedrawWindow(SFG_Window *window);
36
37 #ifdef EGL_VERSION_1_0
38 #include "egl/fg_window_egl.h"
39 #define fghCreateNewContext fghCreateNewContextEGL
40 #else
41 #include "x11/fg_window_x11_glx.h"
42 #endif
43
44 static int fghResizeFullscrToggle(void)
45 {
46     XWindowAttributes attributes;
47
48     if(glutGet(GLUT_FULL_SCREEN)) {
49         /* restore original window size */
50         SFG_Window *win = fgStructure.CurrentWindow;
51         fgStructure.CurrentWindow->State.NeedToResize = GL_TRUE;
52         fgStructure.CurrentWindow->State.Width  = win->State.pWState.OldWidth;
53         fgStructure.CurrentWindow->State.Height = win->State.pWState.OldHeight;
54
55     } else {
56         /* resize the window to cover the entire screen */
57         XGetWindowAttributes(fgDisplay.pDisplay.Display,
58                 fgStructure.CurrentWindow->Window.Handle,
59                 &attributes);
60         
61         /*
62          * The "x" and "y" members of "attributes" are the window's coordinates
63          * relative to its parent, i.e. to the decoration window.
64          */
65         XMoveResizeWindow(fgDisplay.pDisplay.Display,
66                 fgStructure.CurrentWindow->Window.Handle,
67                 -attributes.x,
68                 -attributes.y,
69                 fgDisplay.ScreenWidth,
70                 fgDisplay.ScreenHeight);
71     }
72     return 0;
73 }
74
75 #define _NET_WM_STATE_TOGGLE    2
76 static int fghEwmhFullscrToggle(void)
77 {
78     XEvent xev;
79     long evmask = SubstructureRedirectMask | SubstructureNotifyMask;
80
81     if(!fgDisplay.pDisplay.State || !fgDisplay.pDisplay.StateFullScreen) {
82         return -1;
83     }
84
85     xev.type = ClientMessage;
86     xev.xclient.window = fgStructure.CurrentWindow->Window.Handle;
87     xev.xclient.message_type = fgDisplay.pDisplay.State;
88     xev.xclient.format = 32;
89     xev.xclient.data.l[0] = _NET_WM_STATE_TOGGLE;
90     xev.xclient.data.l[1] = fgDisplay.pDisplay.StateFullScreen;
91     xev.xclient.data.l[2] = 0;  /* no second property to toggle */
92     xev.xclient.data.l[3] = 1;  /* source indication: application */
93     xev.xclient.data.l[4] = 0;  /* unused */
94
95     if(!XSendEvent(fgDisplay.pDisplay.Display, fgDisplay.pDisplay.RootWindow, 0, evmask, &xev)) {
96         return -1;
97     }
98     return 0;
99 }
100
101 static int fghToggleFullscreen(void)
102 {
103     /* first try the EWMH (_NET_WM_STATE) method ... */
104     if(fghEwmhFullscrToggle() != -1) {
105         return 0;
106     }
107
108     /* fall back to resizing the window */
109     if(fghResizeFullscrToggle() != -1) {
110         return 0;
111     }
112     return -1;
113 }
114
115 static Bool fghWindowIsVisible( Display *display, XEvent *event, XPointer arg)
116 {
117     Window window = (Window)arg;
118     return (event->type == MapNotify) && (event->xmap.window == window);
119 }
120
121 /*
122  * Opens a window. Requires a SFG_Window object created and attached
123  * to the freeglut structure. OpenGL context is created here.
124  */
125 void fgPlatformOpenWindow( SFG_Window* window, const char* title,
126                            GLboolean positionUse, int x, int y,
127                            GLboolean sizeUse, int w, int h,
128                            GLboolean gameMode, GLboolean isSubWindow )
129 {
130     XVisualInfo * visualInfo = NULL;
131     XSetWindowAttributes winAttr;
132     XTextProperty textProperty;
133     XSizeHints sizeHints;
134     XWMHints wmHints;
135     XEvent eventReturnBuffer; /* return buffer required for a call */
136     unsigned long mask;
137     unsigned int current_DisplayMode = fgState.DisplayMode ;
138     XConfigureEvent fakeEvent = {0};
139
140     /* Save the display mode if we are creating a menu window */
141     if( window->IsMenu && ( ! fgStructure.MenuContext ) )
142         fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB ;
143
144 #ifdef EGL_VERSION_1_0
145 #define WINDOW_CONFIG window->Window.pContext.egl.Config
146 #else
147 #define WINDOW_CONFIG window->Window.pContext.FBConfig
148 #endif
149     fghChooseConfig(&WINDOW_CONFIG);
150
151     if( window->IsMenu && ( ! fgStructure.MenuContext ) )
152         fgState.DisplayMode = current_DisplayMode ;
153
154     if( ! WINDOW_CONFIG )
155     {
156         /*
157          * The "fghChooseConfig" returned a null meaning that the visual
158          * context is not available.
159          * Try a couple of variations to see if they will work.
160          */
161 #ifndef EGL_VERSION_1_0
162         if( !( fgState.DisplayMode & GLUT_DOUBLE ) )
163         {
164             fgState.DisplayMode |= GLUT_DOUBLE ;
165             fghChooseConfig(&WINDOW_CONFIG);
166             fgState.DisplayMode &= ~GLUT_DOUBLE;
167         }
168 #endif
169
170         if( fgState.DisplayMode & GLUT_MULTISAMPLE )
171         {
172             fgState.DisplayMode &= ~GLUT_MULTISAMPLE ;
173             fghChooseConfig(&WINDOW_CONFIG);
174             fgState.DisplayMode |= GLUT_MULTISAMPLE;
175         }
176     }
177
178     FREEGLUT_INTERNAL_ERROR_EXIT( WINDOW_CONFIG != NULL,
179                                   "FBConfig with necessary capabilities not found", "fgOpenWindow" );
180
181     /*  Get the X visual.  */
182 #ifdef EGL_VERSION_1_0
183     EGLint vid = 0;
184     XVisualInfo visualTemplate;
185     int num_visuals;
186     if (!eglGetConfigAttrib(fgDisplay.pDisplay.egl.Display, window->Window.pContext.egl.Config, EGL_NATIVE_VISUAL_ID, &vid))
187       fgError("eglGetConfigAttrib(EGL_NATIVE_VISUAL_ID) failed");
188     visualTemplate.visualid = vid;
189     visualInfo = XGetVisualInfo(fgDisplay.pDisplay.Display, VisualIDMask, &visualTemplate, &num_visuals);
190 #else
191     visualInfo = glXGetVisualFromFBConfig( fgDisplay.pDisplay.Display,
192                                            window->Window.pContext.FBConfig );
193 #endif
194
195     FREEGLUT_INTERNAL_ERROR_EXIT( visualInfo != NULL,
196                                   "visualInfo could not be retrieved from FBConfig", "fgOpenWindow" );
197
198     /*
199      * XXX HINT: the masks should be updated when adding/removing callbacks.
200      * XXX       This might speed up message processing. Is that true?
201      * XXX
202      * XXX A: Not appreciably, but it WILL make it easier to debug.
203      * XXX    Try tracing old GLUT and try tracing freeglut.  Old GLUT
204      * XXX    turns off events that it doesn't need and is a whole lot
205      * XXX    more pleasant to trace.  (Think mouse-motion!  Tons of
206      * XXX    ``bonus'' GUI events stream in.)
207      */
208     winAttr.event_mask        =
209         StructureNotifyMask | SubstructureNotifyMask | ExposureMask |
210         ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
211         VisibilityChangeMask | EnterWindowMask | LeaveWindowMask |
212         PointerMotionMask | ButtonMotionMask;
213     winAttr.background_pixmap = None;
214     winAttr.background_pixel  = 0;
215     winAttr.border_pixel      = 0;
216
217     winAttr.colormap = XCreateColormap(
218         fgDisplay.pDisplay.Display, fgDisplay.pDisplay.RootWindow,
219         visualInfo->visual, AllocNone
220     );
221
222     mask = CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask;
223
224     if( window->IsMenu || ( gameMode == GL_TRUE ) )
225     {
226         winAttr.override_redirect = True;
227         mask |= CWOverrideRedirect;
228     }
229
230     if( ! positionUse )
231         x = y = -1; /* default window position */
232     if( ! sizeUse )
233         w = h = 300; /* default window size */
234
235     window->Window.Handle = XCreateWindow(
236         fgDisplay.pDisplay.Display,
237         window->Parent == NULL ? fgDisplay.pDisplay.RootWindow :
238         window->Parent->Window.Handle,
239         x, y, w, h, 0,
240         visualInfo->depth, InputOutput,
241         visualInfo->visual, mask,
242         &winAttr
243     );
244
245     /* Fake configure event to force viewport setup
246      * even with no window manager.
247      */
248     fakeEvent.type = ConfigureNotify;
249     fakeEvent.display = fgDisplay.pDisplay.Display;
250     fakeEvent.window = window->Window.Handle;
251     fakeEvent.x = x;
252     fakeEvent.y = y;
253     fakeEvent.width = w;
254     fakeEvent.height = h;
255     XPutBackEvent(fgDisplay.pDisplay.Display, (XEvent*)&fakeEvent);
256
257     /*
258      * The GLX context creation, possibly trying the direct context rendering
259      *  or else use the current context if the user has so specified
260      */
261
262     if( window->IsMenu )
263     {
264         /*
265          * If there isn't already an OpenGL rendering context for menu
266          * windows, make one
267          */
268         if( !fgStructure.MenuContext )
269         {
270             fgStructure.MenuContext =
271                 (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
272             fgStructure.MenuContext->MContext = fghCreateNewContext( window );
273         }
274
275         /* window->Window.Context = fgStructure.MenuContext->MContext; */
276         window->Window.Context = fghCreateNewContext( window );
277     }
278     else if( fgState.UseCurrentContext )
279     {
280
281 #ifdef EGL_VERSION_1_0
282         window->Window.Context = eglGetCurrentContext( );
283 #else
284         window->Window.Context = glXGetCurrentContext( );
285 #endif
286
287         if( ! window->Window.Context )
288             window->Window.Context = fghCreateNewContext( window );
289     }
290     else
291         window->Window.Context = fghCreateNewContext( window );
292
293 #if !defined( __FreeBSD__ ) && !defined( __NetBSD__ ) && !defined(EGL_VERSION_1_0)
294     if(  !glXIsDirect( fgDisplay.pDisplay.Display, window->Window.Context ) )
295     {
296       if( fgState.DirectContext == GLUT_FORCE_DIRECT_CONTEXT )
297         fgError( "Unable to force direct context rendering for window '%s'",
298                  title );
299     }
300 #endif
301
302     /*
303      * XXX Assume the new window is visible by default
304      * XXX Is this a  safe assumption?
305      */
306     window->State.Visible = GL_TRUE;
307
308     sizeHints.flags = 0;
309     if ( positionUse )
310         sizeHints.flags |= USPosition;
311     if ( sizeUse )
312         sizeHints.flags |= USSize;
313
314     /*
315      * Fill in the size hints values now (the x, y, width and height
316      * settings are obsolete, are there any more WMs that support them?)
317      * Unless the X servers actually stop supporting these, we should
318      * continue to fill them in.  It is *not* our place to tell the user
319      * that they should replace a window manager that they like, and which
320      * works, just because *we* think that it's not "modern" enough.
321      */
322     sizeHints.x      = x;
323     sizeHints.y      = y;
324     sizeHints.width  = w;
325     sizeHints.height = h;
326
327     wmHints.flags = StateHint;
328     wmHints.initial_state = fgState.ForceIconic ? IconicState : NormalState;
329     /* Prepare the window and iconified window names... */
330     XStringListToTextProperty( (char **) &title, 1, &textProperty );
331
332     XSetWMProperties(
333         fgDisplay.pDisplay.Display,
334         window->Window.Handle,
335         &textProperty,
336         &textProperty,
337         0,
338         0,
339         &sizeHints,
340         &wmHints,
341         NULL
342     );
343     XFree( textProperty.value );
344
345     XSetWMProtocols( fgDisplay.pDisplay.Display, window->Window.Handle,
346                      &fgDisplay.pDisplay.DeleteWindow, 1 );
347
348 #ifdef EGL_VERSION_1_0
349     fghPlatformOpenWindowEGL(window);
350 #else
351     glXMakeContextCurrent(
352         fgDisplay.pDisplay.Display,
353         window->Window.Handle,
354         window->Window.Handle,
355         window->Window.Context
356     );
357 #endif
358
359     /* register extension events _before_ window is mapped */
360     #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
361        fgRegisterDevices( fgDisplay.pDisplay.Display, &(window->Window.Handle) );
362     #endif
363
364     XMapWindow( fgDisplay.pDisplay.Display, window->Window.Handle );
365
366     XFree(visualInfo);
367
368     if( !isSubWindow)
369         XPeekIfEvent( fgDisplay.pDisplay.Display, &eventReturnBuffer, &fghWindowIsVisible, (XPointer)(window->Window.Handle) );
370 #undef WINDOW_CONFIG
371 }
372
373
374 /*
375  * Request a window resize
376  */
377 void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height )
378 {
379     XResizeWindow( fgDisplay.pDisplay.Display, window->Window.Handle,
380                    width, height );
381     XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
382 }
383
384
385 /*
386  * A static helper function to execute display callback for a window
387  */
388 void fgPlatformDisplayWindow ( SFG_Window *window )
389 {
390         fghRedrawWindow ( window ) ;
391 }
392
393
394 /*
395  * Closes a window, destroying the frame and OpenGL context
396  */
397 void fgPlatformCloseWindow( SFG_Window* window )
398 {
399 #ifdef EGL_VERSION_1_0
400     fghPlatformCloseWindowEGL(window);
401 #else
402     if( window->Window.Context )
403         glXDestroyContext( fgDisplay.pDisplay.Display, window->Window.Context );
404     window->Window.pContext.FBConfig = NULL;
405 #endif
406
407     if( window->Window.Handle ) {
408         XDestroyWindow( fgDisplay.pDisplay.Display, window->Window.Handle );
409     }
410     /* XFlush( fgDisplay.pDisplay.Display ); */ /* XXX Shouldn't need this */
411 }
412
413
414 /*
415  * This function makes the current window visible
416  */
417 void fgPlatformGlutShowWindow( void )
418 {
419     XMapWindow( fgDisplay.pDisplay.Display, fgStructure.CurrentWindow->Window.Handle );
420     XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
421 }
422
423 /*
424  * This function hides the current window
425  */
426 void fgPlatformGlutHideWindow( void )
427 {
428     if( fgStructure.CurrentWindow->Parent == NULL )
429         XWithdrawWindow( fgDisplay.pDisplay.Display,
430                          fgStructure.CurrentWindow->Window.Handle,
431                          fgDisplay.pDisplay.Screen );
432     else
433         XUnmapWindow( fgDisplay.pDisplay.Display,
434                       fgStructure.CurrentWindow->Window.Handle );
435     XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
436 }
437
438 /*
439  * Iconify the current window (top-level windows only)
440  */
441 void fgPlatformGlutIconifyWindow( void )
442 {
443     XIconifyWindow( fgDisplay.pDisplay.Display, fgStructure.CurrentWindow->Window.Handle,
444                     fgDisplay.pDisplay.Screen );
445     XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
446
447     fgStructure.CurrentWindow->State.Visible   = GL_FALSE;
448 }
449
450 /*
451  * Set the current window's title
452  */
453 void fgPlatformGlutSetWindowTitle( const char* title )
454 {
455     XTextProperty text;
456
457     text.value = (unsigned char *) title;
458     text.encoding = XA_STRING;
459     text.format = 8;
460     text.nitems = strlen( title );
461
462     XSetWMName(
463         fgDisplay.pDisplay.Display,
464         fgStructure.CurrentWindow->Window.Handle,
465         &text
466     );
467
468     XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
469 }
470
471 /*
472  * Set the current window's iconified title
473  */
474 void fgPlatformGlutSetIconTitle( const char* title )
475 {
476     XTextProperty text;
477
478     text.value = (unsigned char *) title;
479     text.encoding = XA_STRING;
480     text.format = 8;
481     text.nitems = strlen( title );
482
483     XSetWMIconName(
484         fgDisplay.pDisplay.Display,
485         fgStructure.CurrentWindow->Window.Handle,
486         &text
487     );
488
489     XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
490 }
491
492 /*
493  * Change the current window's position
494  */
495 void fgPlatformGlutPositionWindow( int x, int y )
496 {
497     XMoveWindow( fgDisplay.pDisplay.Display, fgStructure.CurrentWindow->Window.Handle,
498                  x, y );
499     XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
500 }
501
502 /*
503  * Lowers the current window (by Z order change)
504  */
505 void fgPlatformGlutPushWindow( void )
506 {
507     XLowerWindow( fgDisplay.pDisplay.Display, fgStructure.CurrentWindow->Window.Handle );
508 }
509
510 /*
511  * Raises the current window (by Z order change)
512  */
513 void fgPlatformGlutPopWindow( void )
514 {
515     XRaiseWindow( fgDisplay.pDisplay.Display, fgStructure.CurrentWindow->Window.Handle );
516 }
517
518 /*
519  * Resize the current window so that it fits the whole screen
520  */
521 void fgPlatformGlutFullScreen( SFG_Window *win )
522 {
523     if(!glutGet(GLUT_FULL_SCREEN)) {
524         if(fghToggleFullscreen() != -1) {
525             win->State.IsFullscreen = GL_TRUE;
526         }
527     }
528 }
529
530 /*
531  * If we are fullscreen, resize the current window back to its original size
532  */
533 void fgPlatformGlutLeaveFullScreen( SFG_Window *win )
534 {
535     if(glutGet(GLUT_FULL_SCREEN)) {
536         if(fghToggleFullscreen() != -1) {
537             win->State.IsFullscreen = GL_FALSE;
538         }
539     }
540 }
541
542 /*
543  * Toggle the window's full screen state.
544  */
545 void fgPlatformGlutFullScreenToggle( SFG_Window *win )
546 {
547     if(fghToggleFullscreen() != -1) {
548         win->State.IsFullscreen = !win->State.IsFullscreen;
549     }
550 }
551