2 * freeglut_window_x11.c
4 * Window management methods for X11
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
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:
18 * The above copyright notice and this permission notice shall be included
19 * in all copies or substantial portions of the Software.
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.
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"
36 #ifdef EGL_VERSION_1_0
37 #include "egl/fg_window_egl.h"
38 #define fghCreateNewContext fghCreateNewContextEGL
40 #include "x11/fg_window_x11_glx.h"
43 /* Motif window hints, only define needed ones */
47 unsigned long functions;
48 unsigned long decorations;
52 #define MWM_HINTS_DECORATIONS (1L << 1)
53 #define MWM_DECOR_BORDER (1L << 1)
55 static int fghResizeFullscrToggle(void)
57 XWindowAttributes attributes;
58 SFG_Window *win = fgStructure.CurrentWindow;
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;
67 fgStructure.CurrentWindow->State.pWState.OldWidth = win->State.Width;
68 fgStructure.CurrentWindow->State.pWState.OldHeight = win->State.Height;
70 /* resize the window to cover the entire screen */
71 XGetWindowAttributes(fgDisplay.pDisplay.Display,
72 fgStructure.CurrentWindow->Window.Handle,
76 * The "x" and "y" members of "attributes" are the window's coordinates
77 * relative to its parent, i.e. to the decoration window.
79 XMoveResizeWindow(fgDisplay.pDisplay.Display,
80 fgStructure.CurrentWindow->Window.Handle,
83 fgDisplay.ScreenWidth,
84 fgDisplay.ScreenHeight);
89 #define _NET_WM_STATE_TOGGLE 2
90 static int fghEwmhFullscrToggle(void)
93 long evmask = SubstructureRedirectMask | SubstructureNotifyMask;
95 if(!fgDisplay.pDisplay.State || !fgDisplay.pDisplay.StateFullScreen) {
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 */
109 if(!XSendEvent(fgDisplay.pDisplay.Display, fgDisplay.pDisplay.RootWindow, 0, evmask, &xev)) {
115 static int fghToggleFullscreen(void)
117 /* first try the EWMH (_NET_WM_STATE) method ... */
118 if(fghEwmhFullscrToggle() != -1) {
122 /* fall back to resizing the window */
123 if(fghResizeFullscrToggle() != -1) {
129 static Bool fghWindowIsVisible( Display *display, XEvent *event, XPointer arg)
131 Window window = (Window)arg;
132 return (event->type == MapNotify) && (event->xmap.window == window);
136 * Opens a window. Requires a SFG_Window object created and attached
137 * to the freeglut structure. OpenGL context is created here.
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 )
144 XVisualInfo * visualInfo = NULL;
145 XSetWindowAttributes winAttr;
146 XTextProperty textProperty;
147 XSizeHints sizeHints;
149 XEvent eventReturnBuffer; /* return buffer required for a call */
151 unsigned int current_DisplayMode = fgState.DisplayMode ;
152 XEvent fakeEvent = {0};
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 ;
158 #ifdef EGL_VERSION_1_0
159 #define WINDOW_CONFIG window->Window.pContext.egl.Config
161 #define WINDOW_CONFIG window->Window.pContext.FBConfig
163 fghChooseConfig(&WINDOW_CONFIG);
165 if( window->IsMenu && ( ! fgStructure.MenuContext ) )
166 fgState.DisplayMode = current_DisplayMode ;
168 if( ! WINDOW_CONFIG )
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.
175 #ifndef EGL_VERSION_1_0
176 if( !( fgState.DisplayMode & GLUT_DOUBLE ) )
178 fgState.DisplayMode |= GLUT_DOUBLE ;
179 fghChooseConfig(&WINDOW_CONFIG);
180 fgState.DisplayMode &= ~GLUT_DOUBLE;
184 if( fgState.DisplayMode & GLUT_MULTISAMPLE )
186 fgState.DisplayMode &= ~GLUT_MULTISAMPLE ;
187 fghChooseConfig(&WINDOW_CONFIG);
188 fgState.DisplayMode |= GLUT_MULTISAMPLE;
192 FREEGLUT_INTERNAL_ERROR_EXIT( WINDOW_CONFIG != NULL,
193 "FBConfig with necessary capabilities not found", "fgOpenWindow" );
195 /* Get the X visual. */
196 #ifdef EGL_VERSION_1_0
198 XVisualInfo visualTemplate;
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);
205 visualInfo = glXGetVisualFromFBConfig( fgDisplay.pDisplay.Display,
206 window->Window.pContext.FBConfig );
209 FREEGLUT_INTERNAL_ERROR_EXIT( visualInfo != NULL,
210 "visualInfo could not be retrieved from FBConfig", "fgOpenWindow" );
213 * XXX HINT: the masks should be updated when adding/removing callbacks.
214 * XXX This might speed up message processing. Is that true?
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.)
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;
231 winAttr.colormap = XCreateColormap(
232 fgDisplay.pDisplay.Display, fgDisplay.pDisplay.RootWindow,
233 visualInfo->visual, AllocNone
236 mask = CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask;
238 if( window->IsMenu || ( gameMode == GL_TRUE ) )
240 winAttr.override_redirect = True;
241 mask |= CWOverrideRedirect;
245 x = y = -1; /* default window position */
247 w = h = 300; /* default window size */
249 window->Window.Handle = XCreateWindow(
250 fgDisplay.pDisplay.Display,
251 window->Parent == NULL ? fgDisplay.pDisplay.RootWindow :
252 window->Parent->Window.Handle,
254 visualInfo->depth, InputOutput,
255 visualInfo->visual, mask,
259 /* Fake configure event to force viewport setup
260 * even with no window manager.
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);
272 * The GLX context creation, possibly trying the direct context rendering
273 * or else use the current context if the user has so specified
279 * If there isn't already an OpenGL rendering context for menu
282 if( !fgStructure.MenuContext )
284 fgStructure.MenuContext =
285 (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
286 fgStructure.MenuContext->MContext = fghCreateNewContext( window );
289 /* window->Window.Context = fgStructure.MenuContext->MContext; */
290 window->Window.Context = fghCreateNewContext( window );
292 else if( fgState.UseCurrentContext )
295 #ifdef EGL_VERSION_1_0
296 window->Window.Context = eglGetCurrentContext( );
298 window->Window.Context = glXGetCurrentContext( );
301 if( ! window->Window.Context )
302 window->Window.Context = fghCreateNewContext( window );
305 window->Window.Context = fghCreateNewContext( window );
307 #if !defined( __FreeBSD__ ) && !defined( __NetBSD__ ) && !defined(EGL_VERSION_1_0)
308 if( !glXIsDirect( fgDisplay.pDisplay.Display, window->Window.Context ) )
310 if( fgState.DirectContext == GLUT_FORCE_DIRECT_CONTEXT )
311 fgError( "Unable to force direct context rendering for window '%s'",
318 sizeHints.flags |= USPosition;
320 sizeHints.flags |= USSize;
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.
333 sizeHints.height = h;
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 );
341 fgDisplay.pDisplay.Display,
342 window->Window.Handle,
351 XFree( textProperty.value );
353 XSetWMProtocols( fgDisplay.pDisplay.Display, window->Window.Handle,
354 &fgDisplay.pDisplay.DeleteWindow, 1 );
356 if (!isSubWindow && !window->IsMenu &&
357 ((fgState.DisplayMode & GLUT_BORDERLESS) || (fgState.DisplayMode & GLUT_CAPTIONLESS)))
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);
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,
371 (unsigned char*) &hints,
372 sizeof(MotifWmHints) / sizeof(long));
376 if (fgDisplay.pDisplay.NetWMSupported
377 && fgDisplay.pDisplay.NetWMPid != None
378 && fgDisplay.pDisplay.ClientMachine != None)
380 char hostname[HOST_NAME_MAX];
381 pid_t pid = getpid();
383 if (pid > 0 && gethostname(hostname, sizeof(hostname)) > -1)
385 hostname[sizeof(hostname) - 1] = '\0';
388 fgDisplay.pDisplay.Display,
389 window->Window.Handle,
390 fgDisplay.pDisplay.NetWMPid,
394 (unsigned char *) &pid,
399 fgDisplay.pDisplay.Display,
400 window->Window.Handle,
401 fgDisplay.pDisplay.ClientMachine,
405 (unsigned char *) hostname,
411 #ifdef EGL_VERSION_1_0
412 fghPlatformOpenWindowEGL(window);
414 glXMakeContextCurrent(
415 fgDisplay.pDisplay.Display,
416 window->Window.Handle,
417 window->Window.Handle,
418 window->Window.Context
422 /* register extension events _before_ window is mapped */
423 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
424 fgRegisterDevices( fgDisplay.pDisplay.Display, &(window->Window.Handle) );
427 if (!window->IsMenu) /* Don't show window after creation if its a menu */
429 XMapWindow( fgDisplay.pDisplay.Display, window->Window.Handle );
430 window->State.Visible = GL_TRUE;
435 /* wait till window visible */
436 if( !isSubWindow && !window->IsMenu)
437 XPeekIfEvent( fgDisplay.pDisplay.Display, &eventReturnBuffer, &fghWindowIsVisible, (XPointer)(window->Window.Handle) );
443 * Request a window resize
445 void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height )
447 XResizeWindow( fgDisplay.pDisplay.Display, window->Window.Handle,
449 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
454 * Closes a window, destroying the frame and OpenGL context
456 void fgPlatformCloseWindow( SFG_Window* window )
458 #ifdef EGL_VERSION_1_0
459 fghPlatformCloseWindowEGL(window);
461 if( window->Window.Context )
462 glXDestroyContext( fgDisplay.pDisplay.Display, window->Window.Context );
463 window->Window.pContext.FBConfig = NULL;
466 if( window->Window.Handle ) {
467 XDestroyWindow( fgDisplay.pDisplay.Display, window->Window.Handle );
469 /* XFlush( fgDisplay.pDisplay.Display ); */ /* XXX Shouldn't need this */
474 * This function makes the specified window visible
476 void fgPlatformShowWindow( SFG_Window *window )
478 XMapWindow( fgDisplay.pDisplay.Display, window->Window.Handle );
479 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
483 * This function hides the specified window
485 void fgPlatformHideWindow( SFG_Window *window )
487 if( window->Parent == NULL )
488 XWithdrawWindow( fgDisplay.pDisplay.Display,
489 window->Window.Handle,
490 fgDisplay.pDisplay.Screen );
492 XUnmapWindow( fgDisplay.pDisplay.Display,
493 window->Window.Handle );
494 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
498 * Iconify the specified window (top-level windows only)
500 void fgPlatformIconifyWindow( SFG_Window *window )
502 XIconifyWindow( fgDisplay.pDisplay.Display, window->Window.Handle,
503 fgDisplay.pDisplay.Screen );
504 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
506 fgStructure.CurrentWindow->State.Visible = GL_FALSE;
510 * Set the current window's title
512 void fgPlatformGlutSetWindowTitle( const char* title )
516 text.value = (unsigned char *) title;
517 text.encoding = XA_STRING;
519 text.nitems = strlen( title );
522 fgDisplay.pDisplay.Display,
523 fgStructure.CurrentWindow->Window.Handle,
527 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
531 * Set the current window's iconified title
533 void fgPlatformGlutSetIconTitle( const char* title )
537 text.value = (unsigned char *) title;
538 text.encoding = XA_STRING;
540 text.nitems = strlen( title );
543 fgDisplay.pDisplay.Display,
544 fgStructure.CurrentWindow->Window.Handle,
548 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
552 * Change the specified window's position
554 void fgPlatformPositionWindow( SFG_Window *window, int x, int y )
556 XMoveWindow( fgDisplay.pDisplay.Display, window->Window.Handle,
558 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
562 * Lowers the specified window (by Z order change)
564 void fgPlatformPushWindow( SFG_Window *window )
566 XLowerWindow( fgDisplay.pDisplay.Display, window->Window.Handle );
570 * Raises the specified window (by Z order change)
572 void fgPlatformPopWindow( SFG_Window *window )
574 XRaiseWindow( fgDisplay.pDisplay.Display, window->Window.Handle );
578 * Toggle the window's full screen state.
580 void fgPlatformFullScreenToggle( SFG_Window *win )
582 if(fghToggleFullscreen() != -1) {
583 win->State.IsFullscreen = !win->State.IsFullscreen;