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