1 /*
2  * fg_init_x11.c
3  *
4  * Various freeglut initialization functions.
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 <limits.h>  /* LONG_MAX */
31 #include <GL/freeglut.h>
32 #include "fg_internal.h"
33 #include "fg_init.h"
34 #include "egl/fg_init_egl.h"
35 
36 #include <locale.h>
37 
38 /* Return the atom associated with "name". */
fghGetAtom(const char * name)39 static Atom fghGetAtom(const char * name)
40 {
41   return XInternAtom(fgDisplay.pDisplay.Display, name, False);
42 }
43 
44 char *fgClipboardBuffer[3] = { NULL, NULL, NULL };
45 
46 /*
47  * Check if "property" is set on "window".  The property's values are returned
48  * through "data".  If the property is set and is of type "type", return the
49  * number of elements in "data".  Return zero otherwise.  In both cases, use
50  * "Xfree()" to free "data".
51  */
fghGetWindowProperty(Window window,Atom property,Atom type,unsigned char ** data)52 static int fghGetWindowProperty(Window window,
53 				Atom property,
54 				Atom type,
55 				unsigned char ** data)
56 {
57   /*
58    * Caller always has to use "Xfree()" to free "data", since
59    * "XGetWindowProperty() always allocates one extra byte in prop_return
60    * [i.e. "data"] (even if the property is zero length) [..]".
61    */
62 
63   int status;  /*  Returned by "XGetWindowProperty". */
64 
65   Atom          type_returned;
66   int           temp_format;             /*  Not used. */
67   unsigned long number_of_elements;
68   unsigned long temp_bytes_after;        /*  Not used. */
69 
70 
71   status = XGetWindowProperty(fgDisplay.pDisplay.Display,
72 			      window,
73 			      property,
74 			      0,
75 			      LONG_MAX,
76 			      False,
77 			      type,
78 			      &type_returned,
79 			      &temp_format,
80 			      &number_of_elements,
81 			      &temp_bytes_after,
82 			      data);
83 
84   FREEGLUT_INTERNAL_ERROR_EXIT(status == Success,
85 			       "XGetWindowProperty failled",
86 			       "fghGetWindowProperty");
87 
88   if (type_returned != type)
89   {
90     number_of_elements = 0;
91   }
92 
93   return number_of_elements;
94 }
95 
96 /*  Check if the window manager is NET WM compliant. */
fghNetWMSupported(void)97 static int fghNetWMSupported(void)
98 {
99   Atom wm_check;
100   Window ** window_ptr_1;
101 
102   int number_of_windows;
103   int net_wm_supported;
104 
105 
106   net_wm_supported = 0;
107 
108   wm_check = fghGetAtom("_NET_SUPPORTING_WM_CHECK");
109   window_ptr_1 = malloc(sizeof(Window *));
110 
111   /*
112    * Check that the window manager has set this property on the root window.
113    * The property must be the ID of a child window.
114    */
115   number_of_windows = fghGetWindowProperty(fgDisplay.pDisplay.RootWindow,
116                                            wm_check,
117                                            XA_WINDOW,
118                                            (unsigned char **) window_ptr_1);
119   if (number_of_windows == 1)
120   {
121     Window ** window_ptr_2;
122 
123     window_ptr_2 = malloc(sizeof(Window *));
124 
125     /* Check that the window has the same property set to the same value. */
126     number_of_windows = fghGetWindowProperty(**window_ptr_1,
127                                              wm_check,
128                                              XA_WINDOW,
129                                              (unsigned char **) window_ptr_2);
130     if ((number_of_windows == 1) && (**window_ptr_1 == **window_ptr_2))
131     {
132       /* NET WM compliant */
133       net_wm_supported = 1;
134     }
135 
136     XFree(*window_ptr_2);
137     free(window_ptr_2);
138   }
139 
140   XFree(*window_ptr_1);
141   free(window_ptr_1);
142 
143   return net_wm_supported;
144 }
145 
146 /*  Check if "hint" is present in "property" for "window". */
fgHintPresent(Window window,Atom property,Atom hint)147 int fgHintPresent(Window window, Atom property, Atom hint)
148 {
149   Atom *atoms;
150   int number_of_atoms;
151   int supported;
152   int i;
153 
154   supported = 0;
155 
156   number_of_atoms = fghGetWindowProperty(window,
157 					 property,
158 					 XA_ATOM,
159 					 (unsigned char **) &atoms);
160   for (i = 0; i < number_of_atoms; i++)
161   {
162       if (atoms[i] == hint)
163       {
164           supported = 1;
165           break;
166       }
167   }
168 
169   XFree(atoms);
170   return supported;
171 }
172 
173 /*
174  * A call to this function should initialize all the display stuff...
175  */
fgPlatformInitialize(const char * displayName)176 void fgPlatformInitialize( const char* displayName )
177 {
178     fgDisplay.pDisplay.Display = XOpenDisplay( displayName );
179 
180     if( fgDisplay.pDisplay.Display == NULL )
181         fgError( "failed to open display '%s'", XDisplayName( displayName ) );
182 
183     if ( fgState.XSyncSwitch )
184         XSynchronize(fgDisplay.pDisplay.Display, True);
185 
186 #ifdef EGL_VERSION_1_0
187     fghPlatformInitializeEGL();
188 #else
189     if( !glXQueryExtension( fgDisplay.pDisplay.Display, NULL, NULL ) )
190         fgError( "OpenGL GLX extension not supported by display '%s'",
191             XDisplayName( displayName ) );
192 
193     /* This forces AMD Catalyst drivers to initialize and register a shutdown
194      * function, which must be done before our own call to atexit to prevent
195      * a crash if glutMainLoop is not called or is not exited cleanly.
196      * (see bug #206)
197      */
198     glXQueryExtensionsString( fgDisplay.pDisplay.Display,
199         DefaultScreen( fgDisplay.pDisplay.Display ));
200 #endif
201 
202     fgDisplay.pDisplay.Screen = DefaultScreen( fgDisplay.pDisplay.Display );
203     fgDisplay.pDisplay.RootWindow = RootWindow(
204         fgDisplay.pDisplay.Display,
205         fgDisplay.pDisplay.Screen
206     );
207 
208     fgDisplay.ScreenWidth  = DisplayWidth(
209         fgDisplay.pDisplay.Display,
210         fgDisplay.pDisplay.Screen
211     );
212     fgDisplay.ScreenHeight = DisplayHeight(
213         fgDisplay.pDisplay.Display,
214         fgDisplay.pDisplay.Screen
215     );
216 
217     fgDisplay.ScreenWidthMM = DisplayWidthMM(
218         fgDisplay.pDisplay.Display,
219         fgDisplay.pDisplay.Screen
220     );
221     fgDisplay.ScreenHeightMM = DisplayHeightMM(
222         fgDisplay.pDisplay.Display,
223         fgDisplay.pDisplay.Screen
224     );
225 
226     fgDisplay.pDisplay.Connection = ConnectionNumber( fgDisplay.pDisplay.Display );
227 
228     /* Create the window deletion atom */
229     fgDisplay.pDisplay.DeleteWindow = fghGetAtom("WM_DELETE_WINDOW");
230 
231     /* Create the state and full screen atoms */
232     fgDisplay.pDisplay.State           = None;
233     fgDisplay.pDisplay.StateFullScreen = None;
234     fgDisplay.pDisplay.NetWMPid        = None;
235     fgDisplay.pDisplay.ClientMachine   = None;
236 
237     fgDisplay.pDisplay.NetWMSupported = fghNetWMSupported();
238 
239     if (fgDisplay.pDisplay.NetWMSupported)
240     {
241       const Atom supported = fghGetAtom("_NET_SUPPORTED");
242       const Atom state     = fghGetAtom("_NET_WM_STATE");
243 
244       /* Check if the state hint is supported. */
245       if (fgHintPresent(fgDisplay.pDisplay.RootWindow, supported, state))
246       {
247         const Atom full_screen = fghGetAtom("_NET_WM_STATE_FULLSCREEN");
248 
249         fgDisplay.pDisplay.State = state;
250 
251         /* Check if the window manager supports full screen. */
252         /**  Check "_NET_WM_ALLOWED_ACTIONS" on our window instead? **/
253         if (fgHintPresent(fgDisplay.pDisplay.RootWindow, supported, full_screen))
254         {
255           fgDisplay.pDisplay.StateFullScreen = full_screen;
256         }
257       }
258 
259       fgDisplay.pDisplay.NetWMPid = fghGetAtom("_NET_WM_PID");
260       fgDisplay.pDisplay.ClientMachine = fghGetAtom("WM_CLIENT_MACHINE");
261     }
262 
263     /* Open an input method */
264     setlocale(LC_ALL, ""); /* ugh! but we can't force the client to do it for us... */
265     if (!XSupportsLocale())
266         fgWarning("X doesn't support the current locale.");
267     if (!XSetLocaleModifiers(""))
268         fgWarning("Couldn't set X locale modifiers.");
269     fgDisplay.pDisplay.IM = XOpenIM(fgDisplay.pDisplay.Display, NULL, NULL, NULL);
270     if (!fgDisplay.pDisplay.IM)
271 	fgWarning("Couldn't open X input method.");
272     else
273     {
274         XIMStyles *styles;
275         XIMStyle supported = XIMPreeditNothing | XIMStatusNothing;
276         XIMStyle best = 0;
277         unsigned int i;
278         char *res = XGetIMValues(fgDisplay.pDisplay.IM, XNQueryInputStyle, &styles, NULL);
279         if (res)
280             fgWarning("Couldn't get input method style: %s.", res);
281         else
282         {
283             for (i = 0; i < styles->count_styles; ++i)
284             {
285                 XIMStyle style = styles->supported_styles[i];
286                 if ((style & supported) == style)
287                     best = style;
288             }
289             fgDisplay.pDisplay.InputStyle = best;
290         }
291         XFree(styles);
292         if (best == 0)
293         {
294             fgWarning("Couldn't find a usable input method style.");
295             XCloseIM(fgDisplay.pDisplay.IM);
296             fgDisplay.pDisplay.IM = NULL;
297         }
298     }
299 
300     /* Get start time */
301     fgState.Time = fgSystemTime();
302 
303     fgState.Initialised = GL_TRUE;
304 
305     atexit(fgDeinitialize);
306 
307     /* InputDevice uses GlutTimerFunc(), so fgState.Initialised must be TRUE */
308     fgInitialiseInputDevices();
309 }
310 
fgPlatformDeinitialiseInputDevices(void)311 void fgPlatformDeinitialiseInputDevices ( void )
312 {
313 	fghCloseInputDevices ();
314 
315     fgState.JoysticksInitialised = GL_FALSE;
316     fgState.InputDevsInitialised = GL_FALSE;
317 }
318 
319 
fgPlatformCloseDisplay(void)320 void fgPlatformCloseDisplay ( void )
321 {
322     int i;
323 
324     /*
325      * Make sure all X-client data we have created will be destroyed on
326      * display closing
327      */
328     XSetCloseDownMode( fgDisplay.pDisplay.Display, DestroyAll );
329 
330     if (fgDisplay.pDisplay.IM)
331         XCloseIM(fgDisplay.pDisplay.IM);
332 
333     /*
334      * Close the display connection, destroying all windows we have
335      * created so far
336      */
337     XCloseDisplay( fgDisplay.pDisplay.Display );
338 
339     for (i = 0; i < 3; ++i)
340     {
341         free(fgClipboardBuffer[i]);
342         fgClipboardBuffer[i] = NULL;
343     }
344 }
345 
346 
347 #ifndef EGL_VERSION_1_0
fgPlatformDestroyContext(SFG_PlatformDisplay pDisplay,SFG_WindowContextType MContext)348 void fgPlatformDestroyContext ( SFG_PlatformDisplay pDisplay, SFG_WindowContextType MContext )
349 {
350     /* Note that the MVisualInfo is not owned by the MenuContext! */
351     glXDestroyContext( pDisplay.Display, MContext );
352 }
353 #endif
354