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;
188 if( fgState.DisplayMode & GLUT_MULTISAMPLE )
190 fgState.DisplayMode &= ~GLUT_MULTISAMPLE ;
191 fghChooseConfig(&WINDOW_CONFIG);
192 fgState.DisplayMode |= GLUT_MULTISAMPLE;
196 FREEGLUT_INTERNAL_ERROR_EXIT( WINDOW_CONFIG != NULL,
197 "FBConfig with necessary capabilities not found", "fgOpenWindow" );
199 /* Get the X visual. */
200 #ifdef EGL_VERSION_1_0
202 XVisualInfo visualTemplate;
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);
209 visualInfo = glXGetVisualFromFBConfig( fgDisplay.pDisplay.Display,
210 window->Window.pContext.FBConfig );
213 FREEGLUT_INTERNAL_ERROR_EXIT( visualInfo != NULL,
214 "visualInfo could not be retrieved from FBConfig", "fgOpenWindow" );
217 * XXX HINT: the masks should be updated when adding/removing callbacks.
218 * XXX This might speed up message processing. Is that true?
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.)
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;
235 winAttr.colormap = XCreateColormap(
236 fgDisplay.pDisplay.Display, fgDisplay.pDisplay.RootWindow,
237 visualInfo->visual, AllocNone
240 mask = CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask;
242 if( window->IsMenu || ( gameMode == GL_TRUE ) )
244 winAttr.override_redirect = True;
245 mask |= CWOverrideRedirect;
249 x = y = -1; /* default window position */
251 w = h = 300; /* default window size */
253 window->Window.Handle = XCreateWindow(
254 fgDisplay.pDisplay.Display,
255 window->Parent == NULL ? fgDisplay.pDisplay.RootWindow :
256 window->Parent->Window.Handle,
258 visualInfo->depth, InputOutput,
259 visualInfo->visual, mask,
263 /* Fake configure event to force viewport setup
264 * even with no window manager.
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);
276 * The GLX context creation, possibly trying the direct context rendering
277 * or else use the current context if the user has so specified
283 * If there isn't already an OpenGL rendering context for menu
286 if( !fgStructure.MenuContext )
288 fgStructure.MenuContext =
289 (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
290 fgStructure.MenuContext->MContext = fghCreateNewContext( window );
293 /* window->Window.Context = fgStructure.MenuContext->MContext; */
294 window->Window.Context = fghCreateNewContext( window );
296 else if( fgState.UseCurrentContext )
299 #ifdef EGL_VERSION_1_0
300 window->Window.Context = eglGetCurrentContext( );
302 window->Window.Context = glXGetCurrentContext( );
305 if( ! window->Window.Context )
306 window->Window.Context = fghCreateNewContext( window );
309 window->Window.Context = fghCreateNewContext( window );
311 #if !defined( __FreeBSD__ ) && !defined( __NetBSD__ ) && !defined(EGL_VERSION_1_0)
312 if( !glXIsDirect( fgDisplay.pDisplay.Display, window->Window.Context ) )
314 if( fgState.DirectContext == GLUT_FORCE_DIRECT_CONTEXT )
315 fgError( "Unable to force direct context rendering for window '%s'",
322 sizeHints.flags |= USPosition;
324 sizeHints.flags |= USSize;
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.
337 sizeHints.height = h;
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 );
345 fgDisplay.pDisplay.Display,
346 window->Window.Handle,
355 XFree( textProperty.value );
357 XSetWMProtocols( fgDisplay.pDisplay.Display, window->Window.Handle,
358 &fgDisplay.pDisplay.DeleteWindow, 1 );
360 if (!isSubWindow && !window->IsMenu &&
361 ((fgState.DisplayMode & GLUT_BORDERLESS) || (fgState.DisplayMode & GLUT_CAPTIONLESS)))
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;
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,
374 (unsigned char*) &hints,
375 sizeof(MotifWmHints) / sizeof(long));
379 if (fgDisplay.pDisplay.NetWMSupported
380 && fgDisplay.pDisplay.NetWMPid != None
381 && fgDisplay.pDisplay.ClientMachine != None)
383 char hostname[HOST_NAME_MAX];
384 pid_t pid = getpid();
386 if (pid > 0 && gethostname(hostname, sizeof(hostname)) > -1)
388 hostname[sizeof(hostname) - 1] = '\0';
391 fgDisplay.pDisplay.Display,
392 window->Window.Handle,
393 fgDisplay.pDisplay.NetWMPid,
397 (unsigned char *) &pid,
402 fgDisplay.pDisplay.Display,
403 window->Window.Handle,
404 fgDisplay.pDisplay.ClientMachine,
408 (unsigned char *) hostname,
414 #ifdef EGL_VERSION_1_0
415 fghPlatformOpenWindowEGL(window);
417 glXMakeContextCurrent(
418 fgDisplay.pDisplay.Display,
419 window->Window.Handle,
420 window->Window.Handle,
421 window->Window.Context
425 /* register extension events _before_ window is mapped */
426 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
427 fgRegisterDevices( fgDisplay.pDisplay.Display, &(window->Window.Handle) );
430 if (!window->IsMenu) /* Don't show window after creation if its a menu */
432 XMapWindow( fgDisplay.pDisplay.Display, window->Window.Handle );
433 window->State.Visible = GL_TRUE;
438 /* wait till window visible */
439 if( !isSubWindow && !window->IsMenu)
440 XPeekIfEvent( fgDisplay.pDisplay.Display, &eventReturnBuffer, &fghWindowIsVisible, (XPointer)(window->Window.Handle) );
446 * Request a window resize
448 void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height )
450 XResizeWindow( fgDisplay.pDisplay.Display, window->Window.Handle,
452 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
457 * Closes a window, destroying the frame and OpenGL context
459 void fgPlatformCloseWindow( SFG_Window* window )
461 #ifdef EGL_VERSION_1_0
462 fghPlatformCloseWindowEGL(window);
464 if( window->Window.Context )
465 glXDestroyContext( fgDisplay.pDisplay.Display, window->Window.Context );
466 window->Window.pContext.FBConfig = NULL;
469 if( window->Window.Handle ) {
470 XDestroyWindow( fgDisplay.pDisplay.Display, window->Window.Handle );
472 /* XFlush( fgDisplay.pDisplay.Display ); */ /* XXX Shouldn't need this */
477 * This function makes the specified window visible
479 void fgPlatformShowWindow( SFG_Window *window )
481 XMapWindow( fgDisplay.pDisplay.Display, window->Window.Handle );
482 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
486 * This function hides the specified window
488 void fgPlatformHideWindow( SFG_Window *window )
490 if( window->Parent == NULL )
491 XWithdrawWindow( fgDisplay.pDisplay.Display,
492 window->Window.Handle,
493 fgDisplay.pDisplay.Screen );
495 XUnmapWindow( fgDisplay.pDisplay.Display,
496 window->Window.Handle );
497 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
501 * Iconify the specified window (top-level windows only)
503 void fgPlatformIconifyWindow( SFG_Window *window )
505 XIconifyWindow( fgDisplay.pDisplay.Display, window->Window.Handle,
506 fgDisplay.pDisplay.Screen );
507 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
509 fgStructure.CurrentWindow->State.Visible = GL_FALSE;
513 * Set the current window's title
515 void fgPlatformGlutSetWindowTitle( const char* title )
519 text.value = (unsigned char *) title;
520 text.encoding = XA_STRING;
522 text.nitems = strlen( title );
525 fgDisplay.pDisplay.Display,
526 fgStructure.CurrentWindow->Window.Handle,
530 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
534 * Set the current window's iconified title
536 void fgPlatformGlutSetIconTitle( const char* title )
540 text.value = (unsigned char *) title;
541 text.encoding = XA_STRING;
543 text.nitems = strlen( title );
546 fgDisplay.pDisplay.Display,
547 fgStructure.CurrentWindow->Window.Handle,
551 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
555 * Change the specified window's position
557 void fgPlatformPositionWindow( SFG_Window *window, int x, int y )
559 XMoveWindow( fgDisplay.pDisplay.Display, window->Window.Handle,
561 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
565 * Lowers the specified window (by Z order change)
567 void fgPlatformPushWindow( SFG_Window *window )
569 XLowerWindow( fgDisplay.pDisplay.Display, window->Window.Handle );
573 * Raises the specified window (by Z order change)
575 void fgPlatformPopWindow( SFG_Window *window )
577 XRaiseWindow( fgDisplay.pDisplay.Display, window->Window.Handle );
581 * Toggle the window's full screen state.
583 void fgPlatformFullScreenToggle( SFG_Window *win )
585 if(fghToggleFullscreen() != -1) {
586 win->State.IsFullscreen = !win->State.IsFullscreen;