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 */
33 #include "../fg_internal.h"
35 #ifdef EGL_VERSION_1_0
36 #include "egl/fg_window_egl.h"
37 #define fghCreateNewContext fghCreateNewContextEGL
39 #include "x11/fg_window_x11_glx.h"
42 static int fghResizeFullscrToggle(void)
44 XWindowAttributes attributes;
46 if(glutGet(GLUT_FULL_SCREEN)) {
47 /* restore original window size */
48 SFG_Window *win = fgStructure.CurrentWindow;
49 fgStructure.CurrentWindow->State.NeedToResize = GL_TRUE;
50 fgStructure.CurrentWindow->State.Width = win->State.pWState.OldWidth;
51 fgStructure.CurrentWindow->State.Height = win->State.pWState.OldHeight;
54 /* resize the window to cover the entire screen */
55 XGetWindowAttributes(fgDisplay.pDisplay.Display,
56 fgStructure.CurrentWindow->Window.Handle,
60 * The "x" and "y" members of "attributes" are the window's coordinates
61 * relative to its parent, i.e. to the decoration window.
63 XMoveResizeWindow(fgDisplay.pDisplay.Display,
64 fgStructure.CurrentWindow->Window.Handle,
67 fgDisplay.ScreenWidth,
68 fgDisplay.ScreenHeight);
73 #define _NET_WM_STATE_TOGGLE 2
74 static int fghEwmhFullscrToggle(void)
77 long evmask = SubstructureRedirectMask | SubstructureNotifyMask;
79 if(!fgDisplay.pDisplay.State || !fgDisplay.pDisplay.StateFullScreen) {
83 xev.type = ClientMessage;
84 xev.xclient.window = fgStructure.CurrentWindow->Window.Handle;
85 xev.xclient.message_type = fgDisplay.pDisplay.State;
86 xev.xclient.format = 32;
87 xev.xclient.data.l[0] = _NET_WM_STATE_TOGGLE;
88 xev.xclient.data.l[1] = fgDisplay.pDisplay.StateFullScreen;
89 xev.xclient.data.l[2] = 0; /* no second property to toggle */
90 xev.xclient.data.l[3] = 1; /* source indication: application */
91 xev.xclient.data.l[4] = 0; /* unused */
93 if(!XSendEvent(fgDisplay.pDisplay.Display, fgDisplay.pDisplay.RootWindow, 0, evmask, &xev)) {
99 static int fghToggleFullscreen(void)
101 /* first try the EWMH (_NET_WM_STATE) method ... */
102 if(fghEwmhFullscrToggle() != -1) {
106 /* fall back to resizing the window */
107 if(fghResizeFullscrToggle() != -1) {
113 static Bool fghWindowIsVisible( Display *display, XEvent *event, XPointer arg)
115 Window window = (Window)arg;
116 return (event->type == MapNotify) && (event->xmap.window == window);
120 * Opens a window. Requires a SFG_Window object created and attached
121 * to the freeglut structure. OpenGL context is created here.
123 void fgPlatformOpenWindow( SFG_Window* window, const char* title,
124 GLboolean positionUse, int x, int y,
125 GLboolean sizeUse, int w, int h,
126 GLboolean gameMode, GLboolean isSubWindow )
128 XVisualInfo * visualInfo = NULL;
129 XSetWindowAttributes winAttr;
130 XTextProperty textProperty;
131 XSizeHints sizeHints;
133 XEvent eventReturnBuffer; /* return buffer required for a call */
135 unsigned int current_DisplayMode = fgState.DisplayMode ;
136 XConfigureEvent fakeEvent = {0};
138 /* Save the display mode if we are creating a menu window */
139 if( window->IsMenu && ( ! fgStructure.MenuContext ) )
140 fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB ;
142 #ifdef EGL_VERSION_1_0
143 #define WINDOW_CONFIG window->Window.pContext.egl.Config
145 #define WINDOW_CONFIG window->Window.pContext.FBConfig
147 fghChooseConfig(&WINDOW_CONFIG);
149 if( window->IsMenu && ( ! fgStructure.MenuContext ) )
150 fgState.DisplayMode = current_DisplayMode ;
152 if( ! WINDOW_CONFIG )
155 * The "fghChooseConfig" returned a null meaning that the visual
156 * context is not available.
157 * Try a couple of variations to see if they will work.
159 #ifndef EGL_VERSION_1_0
160 if( !( fgState.DisplayMode & GLUT_DOUBLE ) )
162 fgState.DisplayMode |= GLUT_DOUBLE ;
163 fghChooseConfig(&WINDOW_CONFIG);
164 fgState.DisplayMode &= ~GLUT_DOUBLE;
168 if( fgState.DisplayMode & GLUT_MULTISAMPLE )
170 fgState.DisplayMode &= ~GLUT_MULTISAMPLE ;
171 fghChooseConfig(&WINDOW_CONFIG);
172 fgState.DisplayMode |= GLUT_MULTISAMPLE;
176 FREEGLUT_INTERNAL_ERROR_EXIT( WINDOW_CONFIG != NULL,
177 "FBConfig with necessary capabilities not found", "fgOpenWindow" );
179 /* Get the X visual. */
180 #ifdef EGL_VERSION_1_0
182 XVisualInfo visualTemplate;
184 if (!eglGetConfigAttrib(fgDisplay.pDisplay.egl.Display, window->Window.pContext.egl.Config, EGL_NATIVE_VISUAL_ID, &vid))
185 fgError("eglGetConfigAttrib(EGL_NATIVE_VISUAL_ID) failed");
186 visualTemplate.visualid = vid;
187 visualInfo = XGetVisualInfo(fgDisplay.pDisplay.Display, VisualIDMask, &visualTemplate, &num_visuals);
189 visualInfo = glXGetVisualFromFBConfig( fgDisplay.pDisplay.Display,
190 window->Window.pContext.FBConfig );
193 FREEGLUT_INTERNAL_ERROR_EXIT( visualInfo != NULL,
194 "visualInfo could not be retrieved from FBConfig", "fgOpenWindow" );
197 * XXX HINT: the masks should be updated when adding/removing callbacks.
198 * XXX This might speed up message processing. Is that true?
200 * XXX A: Not appreciably, but it WILL make it easier to debug.
201 * XXX Try tracing old GLUT and try tracing freeglut. Old GLUT
202 * XXX turns off events that it doesn't need and is a whole lot
203 * XXX more pleasant to trace. (Think mouse-motion! Tons of
204 * XXX ``bonus'' GUI events stream in.)
207 StructureNotifyMask | SubstructureNotifyMask | ExposureMask |
208 ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
209 VisibilityChangeMask | EnterWindowMask | LeaveWindowMask |
210 PointerMotionMask | ButtonMotionMask;
211 winAttr.background_pixmap = None;
212 winAttr.background_pixel = 0;
213 winAttr.border_pixel = 0;
215 winAttr.colormap = XCreateColormap(
216 fgDisplay.pDisplay.Display, fgDisplay.pDisplay.RootWindow,
217 visualInfo->visual, AllocNone
220 mask = CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask;
222 if( window->IsMenu || ( gameMode == GL_TRUE ) )
224 winAttr.override_redirect = True;
225 mask |= CWOverrideRedirect;
229 x = y = -1; /* default window position */
231 w = h = 300; /* default window size */
233 window->Window.Handle = XCreateWindow(
234 fgDisplay.pDisplay.Display,
235 window->Parent == NULL ? fgDisplay.pDisplay.RootWindow :
236 window->Parent->Window.Handle,
238 visualInfo->depth, InputOutput,
239 visualInfo->visual, mask,
243 /* Fake configure event to force viewport setup
244 * even with no window manager.
246 fakeEvent.type = ConfigureNotify;
247 fakeEvent.display = fgDisplay.pDisplay.Display;
248 fakeEvent.window = window->Window.Handle;
252 fakeEvent.height = h;
253 XPutBackEvent(fgDisplay.pDisplay.Display, (XEvent*)&fakeEvent);
256 * The GLX context creation, possibly trying the direct context rendering
257 * or else use the current context if the user has so specified
263 * If there isn't already an OpenGL rendering context for menu
266 if( !fgStructure.MenuContext )
268 fgStructure.MenuContext =
269 (SFG_MenuContext *)malloc( sizeof(SFG_MenuContext) );
270 fgStructure.MenuContext->MContext = fghCreateNewContext( window );
273 /* window->Window.Context = fgStructure.MenuContext->MContext; */
274 window->Window.Context = fghCreateNewContext( window );
276 else if( fgState.UseCurrentContext )
279 #ifdef EGL_VERSION_1_0
280 window->Window.Context = eglGetCurrentContext( );
282 window->Window.Context = glXGetCurrentContext( );
285 if( ! window->Window.Context )
286 window->Window.Context = fghCreateNewContext( window );
289 window->Window.Context = fghCreateNewContext( window );
291 #if !defined( __FreeBSD__ ) && !defined( __NetBSD__ ) && !defined(EGL_VERSION_1_0)
292 if( !glXIsDirect( fgDisplay.pDisplay.Display, window->Window.Context ) )
294 if( fgState.DirectContext == GLUT_FORCE_DIRECT_CONTEXT )
295 fgError( "Unable to force direct context rendering for window '%s'",
301 * XXX Assume the new window is visible by default
302 * XXX Is this a safe assumption?
304 window->State.Visible = GL_TRUE;
308 sizeHints.flags |= USPosition;
310 sizeHints.flags |= USSize;
313 * Fill in the size hints values now (the x, y, width and height
314 * settings are obsolete, are there any more WMs that support them?)
315 * Unless the X servers actually stop supporting these, we should
316 * continue to fill them in. It is *not* our place to tell the user
317 * that they should replace a window manager that they like, and which
318 * works, just because *we* think that it's not "modern" enough.
323 sizeHints.height = h;
325 wmHints.flags = StateHint;
326 wmHints.initial_state = fgState.ForceIconic ? IconicState : NormalState;
327 /* Prepare the window and iconified window names... */
328 XStringListToTextProperty( (char **) &title, 1, &textProperty );
331 fgDisplay.pDisplay.Display,
332 window->Window.Handle,
341 XFree( textProperty.value );
343 XSetWMProtocols( fgDisplay.pDisplay.Display, window->Window.Handle,
344 &fgDisplay.pDisplay.DeleteWindow, 1 );
346 #ifdef EGL_VERSION_1_0
347 fghPlatformOpenWindowEGL(window);
349 glXMakeContextCurrent(
350 fgDisplay.pDisplay.Display,
351 window->Window.Handle,
352 window->Window.Handle,
353 window->Window.Context
357 /* register extension events _before_ window is mapped */
358 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
359 fgRegisterDevices( fgDisplay.pDisplay.Display, &(window->Window.Handle) );
362 XMapWindow( fgDisplay.pDisplay.Display, window->Window.Handle );
367 XPeekIfEvent( fgDisplay.pDisplay.Display, &eventReturnBuffer, &fghWindowIsVisible, (XPointer)(window->Window.Handle) );
373 * Closes a window, destroying the frame and OpenGL context
375 void fgPlatformCloseWindow( SFG_Window* window )
377 #ifdef EGL_VERSION_1_0
378 fghPlatformCloseWindowEGL(window);
380 if( window->Window.Context )
381 glXDestroyContext( fgDisplay.pDisplay.Display, window->Window.Context );
382 window->Window.pContext.FBConfig = NULL;
385 if( window->Window.Handle ) {
386 XDestroyWindow( fgDisplay.pDisplay.Display, window->Window.Handle );
388 /* XFlush( fgDisplay.pDisplay.Display ); */ /* XXX Shouldn't need this */
393 * This function makes the current window visible
395 void fgPlatformGlutShowWindow( void )
397 XMapWindow( fgDisplay.pDisplay.Display, fgStructure.CurrentWindow->Window.Handle );
398 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
402 * This function hides the current window
404 void fgPlatformGlutHideWindow( void )
406 if( fgStructure.CurrentWindow->Parent == NULL )
407 XWithdrawWindow( fgDisplay.pDisplay.Display,
408 fgStructure.CurrentWindow->Window.Handle,
409 fgDisplay.pDisplay.Screen );
411 XUnmapWindow( fgDisplay.pDisplay.Display,
412 fgStructure.CurrentWindow->Window.Handle );
413 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
417 * Iconify the current window (top-level windows only)
419 void fgPlatformGlutIconifyWindow( void )
421 XIconifyWindow( fgDisplay.pDisplay.Display, fgStructure.CurrentWindow->Window.Handle,
422 fgDisplay.pDisplay.Screen );
423 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
427 * Set the current window's title
429 void fgPlatformGlutSetWindowTitle( const char* title )
433 text.value = (unsigned char *) title;
434 text.encoding = XA_STRING;
436 text.nitems = strlen( title );
439 fgDisplay.pDisplay.Display,
440 fgStructure.CurrentWindow->Window.Handle,
444 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
448 * Set the current window's iconified title
450 void fgPlatformGlutSetIconTitle( const char* title )
454 text.value = (unsigned char *) title;
455 text.encoding = XA_STRING;
457 text.nitems = strlen( title );
460 fgDisplay.pDisplay.Display,
461 fgStructure.CurrentWindow->Window.Handle,
465 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
469 * Change the current window's position
471 void fgPlatformGlutPositionWindow( int x, int y )
473 XMoveWindow( fgDisplay.pDisplay.Display, fgStructure.CurrentWindow->Window.Handle,
475 XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */
479 * Lowers the current window (by Z order change)
481 void fgPlatformGlutPushWindow( void )
483 XLowerWindow( fgDisplay.pDisplay.Display, fgStructure.CurrentWindow->Window.Handle );
487 * Raises the current window (by Z order change)
489 void fgPlatformGlutPopWindow( void )
491 XRaiseWindow( fgDisplay.pDisplay.Display, fgStructure.CurrentWindow->Window.Handle );
495 * Resize the current window so that it fits the whole screen
497 void fgPlatformGlutFullScreen( SFG_Window *win )
499 if(!glutGet(GLUT_FULL_SCREEN)) {
500 if(fghToggleFullscreen() != -1) {
501 win->State.IsFullscreen = GL_TRUE;
507 * If we are fullscreen, resize the current window back to its original size
509 void fgPlatformGlutLeaveFullScreen( SFG_Window *win )
511 if(glutGet(GLUT_FULL_SCREEN)) {
512 if(fghToggleFullscreen() != -1) {
513 win->State.IsFullscreen = GL_FALSE;
519 * Toggle the window's full screen state.
521 void fgPlatformGlutFullScreenToggle( SFG_Window *win )
523 if(fghToggleFullscreen() != -1) {
524 win->State.IsFullscreen = !win->State.IsFullscreen;