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