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