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"
44 #define HOST_NAME_MAX 255
47 /* Motif window hints, only define needed ones */
51 unsigned long functions;
52 unsigned long decorations;
56 #define MWM_HINTS_DECORATIONS (1L << 1)
57 #define MWM_DECOR_BORDER (1L << 1)
59 static int fghResizeFullscrToggle(void)
61 XWindowAttributes attributes;
62 SFG_Window *win = fgStructure.CurrentWindow;
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;
71 fgStructure.CurrentWindow->State.pWState.OldWidth = win->State.Width;
72 fgStructure.CurrentWindow->State.pWState.OldHeight = win->State.Height;
74 /* resize the window to cover the entire screen */
75 XGetWindowAttributes(fgDisplay.pDisplay.Display,
76 fgStructure.CurrentWindow->Window.Handle,
80 * The "x" and "y" members of "attributes" are the window's coordinates
81 * relative to its parent, i.e. to the decoration window.
83 XMoveResizeWindow(fgDisplay.pDisplay.Display,
84 fgStructure.CurrentWindow->Window.Handle,
87 fgDisplay.ScreenWidth,
88 fgDisplay.ScreenHeight);
93 #define _NET_WM_STATE_TOGGLE 2
94 static int fghEwmhFullscrToggle(void)
97 long evmask = SubstructureRedirectMask | SubstructureNotifyMask;
99 if(!fgDisplay.pDisplay.State || !fgDisplay.pDisplay.StateFullScreen) {
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 */
113 if(!XSendEvent(fgDisplay.pDisplay.Display, fgDisplay.pDisplay.RootWindow, 0, evmask, &xev)) {
119 static int fghToggleFullscreen(void)
121 /* first try the EWMH (_NET_WM_STATE) method ... */
122 if(fghEwmhFullscrToggle() != -1) {
126 /* fall back to resizing the window */
127 if(fghResizeFullscrToggle() != -1) {
133 static Bool fghWindowIsVisible( Display *display, XEvent *event, XPointer arg)
135 Window window = (Window)arg;
136 return (event->type == MapNotify) && (event->xmap.window == window);
140 * Opens a window. Requires a SFG_Window object created and attached
141 * to the freeglut structure. OpenGL context is created here.
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 )
148 XVisualInfo * visualInfo = NULL;
149 XSetWindowAttributes winAttr;
150 XTextProperty textProperty;
151 XSizeHints sizeHints;
153 XEvent eventReturnBuffer; /* return buffer required for a call */
155 unsigned int current_DisplayMode = fgState.DisplayMode ;
156 XEvent fakeEvent = {0};
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 ;
162 #ifdef EGL_VERSION_1_0
163 #define WINDOW_CONFIG window->Window.pContext.egl.Config
165 #define WINDOW_CONFIG window->Window.pContext.FBConfig
167 fghChooseConfig(&WINDOW_CONFIG);
169 if( window->IsMenu && ( ! fgStructure.MenuContext ) )
170 fgState.DisplayMode = current_DisplayMode ;
172 if( ! WINDOW_CONFIG )
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.
179 #ifndef EGL_VERSION_1_0
180 if( !( fgState.DisplayMode & GLUT_DOUBLE ) )
182 fgState.DisplayMode |= GLUT_DOUBLE ;
183 fghChooseConfig(&WINDOW_CONFIG);
184 fgState.DisplayMode &= ~GLUT_DOUBLE;
186 if( WINDOW_CONFIG ) goto done_retry;
190 if( fgState.DisplayMode & GLUT_MULTISAMPLE )
192 fgState.DisplayMode &= ~GLUT_MULTISAMPLE ;
193 fghChooseConfig(&WINDOW_CONFIG);
194 fgState.DisplayMode |= GLUT_MULTISAMPLE;
196 if( WINDOW_CONFIG ) goto done_retry;
199 if( fgState.DisplayMode & GLUT_SRGB )
201 fgState.DisplayMode &= ~GLUT_SRGB ;
202 fghChooseConfig(&WINDOW_CONFIG);
203 fgState.DisplayMode |= GLUT_SRGB;
205 if( WINDOW_CONFIG ) goto done_retry;
210 FREEGLUT_INTERNAL_ERROR_EXIT( WINDOW_CONFIG != NULL,
211 "FBConfig with necessary capabilities not found", "fgOpenWindow" );
213 /* Get the X visual. */
214 #ifdef EGL_VERSION_1_0
216 XVisualInfo visualTemplate;
218 if (!eglGetConfigAttrib(fgDisplay.pDisplay.egl.Display, window->Window.pContext.egl.Config, EGL_NATIVE_VISUAL_ID, &vid))
219 fgError("eglGetConfigAttrib(EGL_NATIVE_VISUAL_ID) failed");
220 visualTemplate.visualid = vid;
221 visualInfo = XGetVisualInfo(fgDisplay.pDisplay.Display, VisualIDMask, &visualTemplate, &num_visuals);
223 visualInfo = glXGetVisualFromFBConfig( fgDisplay.pDisplay.Display,
224 window->Window.pContext.FBConfig );
227 FREEGLUT_INTERNAL_ERROR_EXIT( visualInfo != NULL,
228 "visualInfo could not be retrieved from FBConfig", "fgOpenWindow" );
231 * XXX HINT: the masks should be updated when adding/removing callbacks.
232 * XXX This might speed up message processing. Is that true?
234 * XXX A: Not appreciably, but it WILL make it easier to debug.
235 * XXX Try tracing old GLUT and try tracing freeglut. Old GLUT
236 * XXX turns off events that it doesn't need and is a whole lot
237 * XXX more pleasant to trace. (Think mouse-motion! Tons of
238 * XXX ``bonus'' GUI events stream in.)
241 StructureNotifyMask | SubstructureNotifyMask | ExposureMask |
242 ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
243 VisibilityChangeMask | EnterWindowMask | LeaveWindowMask |
244 PointerMotionMask | ButtonMotionMask;
245 winAttr.background_pixmap = None;
246 winAttr.background_pixel = 0;
247 winAttr.border_pixel = 0;
249 winAttr.colormap = XCreateColormap(
250 fgDisplay.pDisplay.Display, fgDisplay.pDisplay.RootWindow,
251 visualInfo->visual, AllocNone
254 mask = CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask;
256 if( window->IsMenu || ( gameMode == GL_TRUE ) )
258 winAttr.override_redirect = True;
259 mask |= CWOverrideRedirect;
263 x = y = -1; /* default window position */
265 w = h = 300; /* default window size */
267 window->Window.Handle = XCreateWindow(
268 fgDisplay.pDisplay.Display,
269 window->Parent == NULL ? fgDisplay.pDisplay.RootWindow :
270 window->Parent->Window.Handle,
272 visualInfo->depth, InputOutput,
273 visualInfo->visual, mask,
277 /* Fake configure event to force viewport setup
278 * even with no window manager.
280 fakeEvent.xconfigure.type = ConfigureNotify;
281 fakeEvent.xconfigure.display = fgDisplay.pDisplay.Display;
282 fakeEvent.xconfigure.window = window->Window.Handle;
283 fakeEvent.xconfigure.x = x;
284 fakeEvent.xconfigure.y = y;
285 fakeEvent.xconfigure.width = w;
286 fakeEvent.xconfigure.height = h;
287 XPutBackEvent(fgDisplay.pDisplay.Display, &fakeEvent);
290 * The GLX context creation, possibly trying the direct context rendering
291 * or else use the current context if the user has so specified
297 * If there isn't already an OpenGL rendering context for menu
300 if( !fgStructure.MenuContext )
302 fgStructure.MenuContext =
303 (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
304 fgStructure.MenuContext->MContext = fghCreateNewContext( window );
307 /* window->Window.Context = fgStructure.MenuContext->MContext; */
308 window->Window.Context = fghCreateNewContext( window );
310 else if( fgState.UseCurrentContext )
313 #ifdef EGL_VERSION_1_0
314 window->Window.Context = eglGetCurrentContext( );
316 window->Window.Context = glXGetCurrentContext( );
319 if( ! window->Window.Context )
320 window->Window.Context = fghCreateNewContext( window );
323 window->Window.Context = fghCreateNewContext( window );
325 #if !defined( __FreeBSD__ ) && !defined( __NetBSD__ ) && !defined(EGL_VERSION_1_0)
326 if( !glXIsDirect( fgDisplay.pDisplay.Display, window->Window.Context ) )
328 if( fgState.DirectContext == GLUT_FORCE_DIRECT_CONTEXT )
329 fgError( "Unable to force direct context rendering for window '%s'",
336 sizeHints.flags |= USPosition;
338 sizeHints.flags |= USSize;
341 * Fill in the size hints values now (the x, y, width and height
342 * settings are obsolete, are there any more WMs that support them?)
343 * Unless the X servers actually stop supporting these, we should
344 * continue to fill them in. It is *not* our place to tell the user
345 * that they should replace a window manager that they like, and which
346 * works, just because *we* think that it's not "modern" enough.
351 sizeHints.height = h;
353 wmHints.flags = StateHint;
354 wmHints.initial_state = fgState.ForceIconic ? IconicState : NormalState;
355 /* Prepare the window and iconified window names... */
356 XStringListToTextProperty( (char **) &title, 1, &textProperty );
359 fgDisplay.pDisplay.Display,
360 window->Window.Handle,
369 XFree( textProperty.value );
371 XSetWMProtocols( fgDisplay.pDisplay.Display, window->Window.Handle,
372 &fgDisplay.pDisplay.DeleteWindow, 1 );
374 if (!isSubWindow && !window->IsMenu &&
375 ((fgState.DisplayMode & GLUT_BORDERLESS) || (fgState.DisplayMode & GLUT_CAPTIONLESS)))
377 /* _MOTIF_WM_HINTS is replaced by _NET_WM_WINDOW_TYPE, but that property does not allow precise
378 * control over the visual style of the window, which is what we are trying to achieve here.
379 * Stick with Motif and hope for the best... */
380 MotifWmHints hints = {0};
381 hints.flags = MWM_HINTS_DECORATIONS;
382 hints.decorations = (fgState.DisplayMode & GLUT_CAPTIONLESS) ? MWM_DECOR_BORDER:0;
384 XChangeProperty(fgDisplay.pDisplay.Display, window->Window.Handle,
385 XInternAtom( fgDisplay.pDisplay.Display, "_MOTIF_WM_HINTS", False ),
386 XInternAtom( fgDisplay.pDisplay.Display, "_MOTIF_WM_HINTS", False ), 32,
388 (unsigned char*) &hints,
389 sizeof(MotifWmHints) / sizeof(long));
393 if (fgDisplay.pDisplay.NetWMSupported
394 && fgDisplay.pDisplay.NetWMPid != None
395 && fgDisplay.pDisplay.ClientMachine != None)
397 char hostname[HOST_NAME_MAX];
398 pid_t pid = getpid();
400 if (pid > 0 && gethostname(hostname, sizeof(hostname)) > -1)
402 hostname[sizeof(hostname) - 1] = '\0';
405 fgDisplay.pDisplay.Display,
406 window->Window.Handle,
407 fgDisplay.pDisplay.NetWMPid,
411 (unsigned char *) &pid,
416 fgDisplay.pDisplay.Display,
417 window->Window.Handle,
418 fgDisplay.pDisplay.ClientMachine,
422 (unsigned char *) hostname,
428 #ifdef EGL_VERSION_1_0
429 fghPlatformOpenWindowEGL(window);
431 glXMakeContextCurrent(
432 fgDisplay.pDisplay.Display,
433 window->Window.Handle,
434 window->Window.Handle,
435 window->Window.Context
439 /* register extension events _before_ window is mapped */
440 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
441 fgRegisterDevices( fgDisplay.pDisplay.Display, &(window->Window.Handle) );
444 if (!window->IsMenu) /* Don't show window after creation if its a menu */
446 XMapWindow( fgDisplay.pDisplay.Display, window->Window.Handle );
447 window->State.Visible = GL_TRUE;
452 /* wait till window visible */
453 if( !isSubWindow && !window->IsMenu)
454 XPeekIfEvent( fgDisplay.pDisplay.Display, &eventReturnBuffer, &fghWindowIsVisible, (XPointer)(window->Window.Handle) );
460 * Request a window resize
462 void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height )
464 XResizeWindow( fgDisplay.pDisplay.Display, window->Window.Handle,
466 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
471 * Closes a window, destroying the frame and OpenGL context
473 void fgPlatformCloseWindow( SFG_Window* window )
475 #ifdef EGL_VERSION_1_0
476 fghPlatformCloseWindowEGL(window);
478 if( window->Window.Context )
479 glXDestroyContext( fgDisplay.pDisplay.Display, window->Window.Context );
480 window->Window.pContext.FBConfig = NULL;
483 if( window->Window.Handle ) {
484 XDestroyWindow( fgDisplay.pDisplay.Display, window->Window.Handle );
486 /* XFlush( fgDisplay.pDisplay.Display ); */ /* XXX Shouldn't need this */
491 * This function makes the specified window visible
493 void fgPlatformShowWindow( SFG_Window *window )
495 XMapWindow( fgDisplay.pDisplay.Display, window->Window.Handle );
496 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
500 * This function hides the specified window
502 void fgPlatformHideWindow( SFG_Window *window )
504 if( window->Parent == NULL )
505 XWithdrawWindow( fgDisplay.pDisplay.Display,
506 window->Window.Handle,
507 fgDisplay.pDisplay.Screen );
509 XUnmapWindow( fgDisplay.pDisplay.Display,
510 window->Window.Handle );
511 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
515 * Iconify the specified window (top-level windows only)
517 void fgPlatformIconifyWindow( SFG_Window *window )
519 XIconifyWindow( fgDisplay.pDisplay.Display, window->Window.Handle,
520 fgDisplay.pDisplay.Screen );
521 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
523 fgStructure.CurrentWindow->State.Visible = GL_FALSE;
527 * Set the current window's title
529 void fgPlatformGlutSetWindowTitle( const char* title )
533 text.value = (unsigned char *) title;
534 text.encoding = XA_STRING;
536 text.nitems = strlen( title );
539 fgDisplay.pDisplay.Display,
540 fgStructure.CurrentWindow->Window.Handle,
544 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
548 * Set the current window's iconified title
550 void fgPlatformGlutSetIconTitle( const char* title )
554 text.value = (unsigned char *) title;
555 text.encoding = XA_STRING;
557 text.nitems = strlen( title );
560 fgDisplay.pDisplay.Display,
561 fgStructure.CurrentWindow->Window.Handle,
565 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
569 * Change the specified window's position
571 void fgPlatformPositionWindow( SFG_Window *window, int x, int y )
573 XMoveWindow( fgDisplay.pDisplay.Display, window->Window.Handle,
575 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
579 * Lowers the specified window (by Z order change)
581 void fgPlatformPushWindow( SFG_Window *window )
583 XLowerWindow( fgDisplay.pDisplay.Display, window->Window.Handle );
587 * Raises the specified window (by Z order change)
589 void fgPlatformPopWindow( SFG_Window *window )
591 XRaiseWindow( fgDisplay.pDisplay.Display, window->Window.Handle );
595 * Toggle the window's full screen state.
597 void fgPlatformFullScreenToggle( SFG_Window *win )
599 if(fghToggleFullscreen() != -1) {
600 win->State.IsFullscreen = !win->State.IsFullscreen;