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". */ |
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 | */ |
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. */ |
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". */ |
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 | */ |
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 | |
311 | void fgPlatformDeinitialiseInputDevices ( void ) |
312 | { |
313 | fghCloseInputDevices (); |
314 | |
315 | fgState.JoysticksInitialised = GL_FALSE; |
316 | fgState.InputDevsInitialised = GL_FALSE; |
317 | } |
318 | |
319 | |
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 |
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 | |