1 | //======================================================================== |
2 | // GLFW 3.4 - www.glfw.org |
3 | //------------------------------------------------------------------------ |
4 | // Copyright (c) 2002-2006 Marcus Geelnard |
5 | // Copyright (c) 2006-2018 Camilla Löwy <[email protected]> |
6 | // |
7 | // This software is provided 'as-is', without any express or implied |
8 | // warranty. In no event will the authors be held liable for any damages |
9 | // arising from the use of this software. |
10 | // |
11 | // Permission is granted to anyone to use this software for any purpose, |
12 | // including commercial applications, and to alter it and redistribute it |
13 | // freely, subject to the following restrictions: |
14 | // |
15 | // 1. The origin of this software must not be misrepresented; you must not |
16 | // claim that you wrote the original software. If you use this software |
17 | // in a product, an acknowledgment in the product documentation would |
18 | // be appreciated but is not required. |
19 | // |
20 | // 2. Altered source versions must be plainly marked as such, and must not |
21 | // be misrepresented as being the original software. |
22 | // |
23 | // 3. This notice may not be removed or altered from any source |
24 | // distribution. |
25 | // |
26 | //======================================================================== |
27 | // Please use C89 style variable declarations in this file because VS 2010 |
28 | //======================================================================== |
29 | |
30 | #include "internal.h" |
31 | |
32 | #include <string.h> |
33 | #include <stdlib.h> |
34 | #include <stdio.h> |
35 | #include <stdarg.h> |
36 | #include <assert.h> |
37 | |
38 | |
39 | // The global variables below comprise all mutable global data in GLFW |
40 | // |
41 | // Any other global variable is a bug |
42 | |
43 | // Global state shared between compilation units of GLFW |
44 | // |
45 | _GLFWlibrary _glfw = { GLFW_FALSE }; |
46 | |
47 | // These are outside of _glfw so they can be used before initialization and |
48 | // after termination |
49 | // |
50 | static _GLFWerror _glfwMainThreadError; |
51 | static GLFWerrorfun _glfwErrorCallback; |
52 | static GLFWallocator _glfwInitAllocator; |
53 | static _GLFWinitconfig _glfwInitHints = |
54 | { |
55 | GLFW_TRUE, // hat buttons |
56 | GLFW_ANGLE_PLATFORM_TYPE_NONE, // ANGLE backend |
57 | GLFW_ANY_PLATFORM, // preferred platform |
58 | NULL, // vkGetInstanceProcAddr function |
59 | { |
60 | GLFW_TRUE, // macOS menu bar |
61 | GLFW_TRUE // macOS bundle chdir |
62 | }, |
63 | { |
64 | GLFW_TRUE, // X11 XCB Vulkan surface |
65 | }, |
66 | }; |
67 | |
68 | // The allocation function used when no custom allocator is set |
69 | // |
70 | static void* defaultAllocate(size_t size, void* user) |
71 | { |
72 | return malloc(size); |
73 | } |
74 | |
75 | // The deallocation function used when no custom allocator is set |
76 | // |
77 | static void defaultDeallocate(void* block, void* user) |
78 | { |
79 | free(block); |
80 | } |
81 | |
82 | // The reallocation function used when no custom allocator is set |
83 | // |
84 | static void* defaultReallocate(void* block, size_t size, void* user) |
85 | { |
86 | return realloc(block, size); |
87 | } |
88 | |
89 | // Terminate the library |
90 | // |
91 | static void terminate(void) |
92 | { |
93 | int i; |
94 | |
95 | memset(&_glfw.callbacks, 0, sizeof(_glfw.callbacks)); |
96 | |
97 | while (_glfw.windowListHead) |
98 | glfwDestroyWindow((GLFWwindow*) _glfw.windowListHead); |
99 | |
100 | while (_glfw.cursorListHead) |
101 | glfwDestroyCursor((GLFWcursor*) _glfw.cursorListHead); |
102 | |
103 | for (i = 0; i < _glfw.monitorCount; i++) |
104 | { |
105 | _GLFWmonitor* monitor = _glfw.monitors[i]; |
106 | if (monitor->originalRamp.size) |
107 | _glfw.platform.setGammaRamp(monitor, &monitor->originalRamp); |
108 | _glfwFreeMonitor(monitor); |
109 | } |
110 | |
111 | _glfw_free(_glfw.monitors); |
112 | _glfw.monitors = NULL; |
113 | _glfw.monitorCount = 0; |
114 | |
115 | _glfw_free(_glfw.mappings); |
116 | _glfw.mappings = NULL; |
117 | _glfw.mappingCount = 0; |
118 | |
119 | _glfwTerminateVulkan(); |
120 | _glfw.platform.terminateJoysticks(); |
121 | _glfw.platform.terminate(); |
122 | |
123 | _glfw.initialized = GLFW_FALSE; |
124 | |
125 | while (_glfw.errorListHead) |
126 | { |
127 | _GLFWerror* error = _glfw.errorListHead; |
128 | _glfw.errorListHead = error->next; |
129 | _glfw_free(error); |
130 | } |
131 | |
132 | _glfwPlatformDestroyTls(&_glfw.contextSlot); |
133 | _glfwPlatformDestroyTls(&_glfw.errorSlot); |
134 | _glfwPlatformDestroyMutex(&_glfw.errorLock); |
135 | |
136 | memset(&_glfw, 0, sizeof(_glfw)); |
137 | } |
138 | |
139 | |
140 | ////////////////////////////////////////////////////////////////////////// |
141 | ////// GLFW internal API ////// |
142 | ////////////////////////////////////////////////////////////////////////// |
143 | |
144 | char* _glfw_strdup(const char* source) |
145 | { |
146 | const size_t length = strlen(source); |
147 | char* result = _glfw_calloc(length + 1, 1); |
148 | strcpy(result, source); |
149 | return result; |
150 | } |
151 | |
152 | float _glfw_fminf(float a, float b) |
153 | { |
154 | if (a != a) |
155 | return b; |
156 | else if (b != b) |
157 | return a; |
158 | else if (a < b) |
159 | return a; |
160 | else |
161 | return b; |
162 | } |
163 | |
164 | float _glfw_fmaxf(float a, float b) |
165 | { |
166 | if (a != a) |
167 | return b; |
168 | else if (b != b) |
169 | return a; |
170 | else if (a > b) |
171 | return a; |
172 | else |
173 | return b; |
174 | } |
175 | |
176 | void* _glfw_calloc(size_t count, size_t size) |
177 | { |
178 | if (count && size) |
179 | { |
180 | void* block; |
181 | |
182 | if (count > SIZE_MAX / size) |
183 | { |
184 | _glfwInputError(GLFW_INVALID_VALUE, "Allocation size overflow" ); |
185 | return NULL; |
186 | } |
187 | |
188 | block = _glfw.allocator.allocate(count * size, _glfw.allocator.user); |
189 | if (block) |
190 | return memset(block, 0, count * size); |
191 | else |
192 | { |
193 | _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); |
194 | return NULL; |
195 | } |
196 | } |
197 | else |
198 | return NULL; |
199 | } |
200 | |
201 | void* _glfw_realloc(void* block, size_t size) |
202 | { |
203 | if (block && size) |
204 | { |
205 | void* resized = _glfw.allocator.reallocate(block, size, _glfw.allocator.user); |
206 | if (resized) |
207 | return resized; |
208 | else |
209 | { |
210 | _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); |
211 | return NULL; |
212 | } |
213 | } |
214 | else if (block) |
215 | { |
216 | _glfw_free(block); |
217 | return NULL; |
218 | } |
219 | else |
220 | return _glfw_calloc(1, size); |
221 | } |
222 | |
223 | void _glfw_free(void* block) |
224 | { |
225 | if (block) |
226 | _glfw.allocator.deallocate(block, _glfw.allocator.user); |
227 | } |
228 | |
229 | |
230 | ////////////////////////////////////////////////////////////////////////// |
231 | ////// GLFW event API ////// |
232 | ////////////////////////////////////////////////////////////////////////// |
233 | |
234 | // Notifies shared code of an error |
235 | // |
236 | void _glfwInputError(int code, const char* format, ...) |
237 | { |
238 | _GLFWerror* error; |
239 | char description[_GLFW_MESSAGE_SIZE]; |
240 | |
241 | if (format) |
242 | { |
243 | va_list vl; |
244 | |
245 | va_start(vl, format); |
246 | vsnprintf(description, sizeof(description), format, vl); |
247 | va_end(vl); |
248 | |
249 | description[sizeof(description) - 1] = '\0'; |
250 | } |
251 | else |
252 | { |
253 | if (code == GLFW_NOT_INITIALIZED) |
254 | strcpy(description, "The GLFW library is not initialized" ); |
255 | else if (code == GLFW_NO_CURRENT_CONTEXT) |
256 | strcpy(description, "There is no current context" ); |
257 | else if (code == GLFW_INVALID_ENUM) |
258 | strcpy(description, "Invalid argument for enum parameter" ); |
259 | else if (code == GLFW_INVALID_VALUE) |
260 | strcpy(description, "Invalid value for parameter" ); |
261 | else if (code == GLFW_OUT_OF_MEMORY) |
262 | strcpy(description, "Out of memory" ); |
263 | else if (code == GLFW_API_UNAVAILABLE) |
264 | strcpy(description, "The requested API is unavailable" ); |
265 | else if (code == GLFW_VERSION_UNAVAILABLE) |
266 | strcpy(description, "The requested API version is unavailable" ); |
267 | else if (code == GLFW_PLATFORM_ERROR) |
268 | strcpy(description, "A platform-specific error occurred" ); |
269 | else if (code == GLFW_FORMAT_UNAVAILABLE) |
270 | strcpy(description, "The requested format is unavailable" ); |
271 | else if (code == GLFW_NO_WINDOW_CONTEXT) |
272 | strcpy(description, "The specified window has no context" ); |
273 | else if (code == GLFW_CURSOR_UNAVAILABLE) |
274 | strcpy(description, "The specified cursor shape is unavailable" ); |
275 | else if (code == GLFW_FEATURE_UNAVAILABLE) |
276 | strcpy(description, "The requested feature cannot be implemented for this platform" ); |
277 | else if (code == GLFW_FEATURE_UNIMPLEMENTED) |
278 | strcpy(description, "The requested feature has not yet been implemented for this platform" ); |
279 | else if (code == GLFW_PLATFORM_UNAVAILABLE) |
280 | strcpy(description, "The requested platform is unavailable" ); |
281 | else |
282 | strcpy(description, "ERROR: UNKNOWN GLFW ERROR" ); |
283 | } |
284 | |
285 | if (_glfw.initialized) |
286 | { |
287 | error = _glfwPlatformGetTls(&_glfw.errorSlot); |
288 | if (!error) |
289 | { |
290 | error = _glfw_calloc(1, sizeof(_GLFWerror)); |
291 | _glfwPlatformSetTls(&_glfw.errorSlot, error); |
292 | _glfwPlatformLockMutex(&_glfw.errorLock); |
293 | error->next = _glfw.errorListHead; |
294 | _glfw.errorListHead = error; |
295 | _glfwPlatformUnlockMutex(&_glfw.errorLock); |
296 | } |
297 | } |
298 | else |
299 | error = &_glfwMainThreadError; |
300 | |
301 | error->code = code; |
302 | strcpy(error->description, description); |
303 | |
304 | if (_glfwErrorCallback) |
305 | _glfwErrorCallback(code, description); |
306 | } |
307 | |
308 | |
309 | ////////////////////////////////////////////////////////////////////////// |
310 | ////// GLFW public API ////// |
311 | ////////////////////////////////////////////////////////////////////////// |
312 | |
313 | GLFWAPI int glfwInit(void) |
314 | { |
315 | if (_glfw.initialized) |
316 | return GLFW_TRUE; |
317 | |
318 | memset(&_glfw, 0, sizeof(_glfw)); |
319 | _glfw.hints.init = _glfwInitHints; |
320 | |
321 | _glfw.allocator = _glfwInitAllocator; |
322 | if (!_glfw.allocator.allocate) |
323 | { |
324 | _glfw.allocator.allocate = defaultAllocate; |
325 | _glfw.allocator.reallocate = defaultReallocate; |
326 | _glfw.allocator.deallocate = defaultDeallocate; |
327 | } |
328 | |
329 | if (!_glfwSelectPlatform(_glfw.hints.init.platformID, &_glfw.platform)) |
330 | return GLFW_FALSE; |
331 | |
332 | if (!_glfw.platform.init()) |
333 | { |
334 | terminate(); |
335 | return GLFW_FALSE; |
336 | } |
337 | |
338 | if (!_glfwPlatformCreateMutex(&_glfw.errorLock) || |
339 | !_glfwPlatformCreateTls(&_glfw.errorSlot) || |
340 | !_glfwPlatformCreateTls(&_glfw.contextSlot)) |
341 | { |
342 | terminate(); |
343 | return GLFW_FALSE; |
344 | } |
345 | |
346 | _glfwPlatformSetTls(&_glfw.errorSlot, &_glfwMainThreadError); |
347 | |
348 | _glfwInitGamepadMappings(); |
349 | |
350 | _glfwPlatformInitTimer(); |
351 | _glfw.timer.offset = _glfwPlatformGetTimerValue(); |
352 | |
353 | _glfw.initialized = GLFW_TRUE; |
354 | |
355 | glfwDefaultWindowHints(); |
356 | return GLFW_TRUE; |
357 | } |
358 | |
359 | GLFWAPI void glfwTerminate(void) |
360 | { |
361 | if (!_glfw.initialized) |
362 | return; |
363 | |
364 | terminate(); |
365 | } |
366 | |
367 | GLFWAPI void glfwInitHint(int hint, int value) |
368 | { |
369 | switch (hint) |
370 | { |
371 | case GLFW_JOYSTICK_HAT_BUTTONS: |
372 | _glfwInitHints.hatButtons = value; |
373 | return; |
374 | case GLFW_ANGLE_PLATFORM_TYPE: |
375 | _glfwInitHints.angleType = value; |
376 | return; |
377 | case GLFW_PLATFORM: |
378 | _glfwInitHints.platformID = value; |
379 | return; |
380 | case GLFW_COCOA_CHDIR_RESOURCES: |
381 | _glfwInitHints.ns.chdir = value; |
382 | return; |
383 | case GLFW_COCOA_MENUBAR: |
384 | _glfwInitHints.ns.menubar = value; |
385 | return; |
386 | case GLFW_X11_XCB_VULKAN_SURFACE: |
387 | _glfwInitHints.x11.xcbVulkanSurface = value; |
388 | return; |
389 | } |
390 | |
391 | _glfwInputError(GLFW_INVALID_ENUM, |
392 | "Invalid init hint 0x%08X" , hint); |
393 | } |
394 | |
395 | GLFWAPI void glfwInitAllocator(const GLFWallocator* allocator) |
396 | { |
397 | if (allocator) |
398 | { |
399 | if (allocator->allocate && allocator->reallocate && allocator->deallocate) |
400 | _glfwInitAllocator = *allocator; |
401 | else |
402 | _glfwInputError(GLFW_INVALID_VALUE, "Missing function in allocator" ); |
403 | } |
404 | else |
405 | memset(&_glfwInitAllocator, 0, sizeof(GLFWallocator)); |
406 | } |
407 | |
408 | GLFWAPI void glfwInitVulkanLoader(PFN_vkGetInstanceProcAddr loader) |
409 | { |
410 | _glfwInitHints.vulkanLoader = loader; |
411 | } |
412 | |
413 | GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev) |
414 | { |
415 | if (major != NULL) |
416 | *major = GLFW_VERSION_MAJOR; |
417 | if (minor != NULL) |
418 | *minor = GLFW_VERSION_MINOR; |
419 | if (rev != NULL) |
420 | *rev = GLFW_VERSION_REVISION; |
421 | } |
422 | |
423 | GLFWAPI int glfwGetError(const char** description) |
424 | { |
425 | _GLFWerror* error; |
426 | int code = GLFW_NO_ERROR; |
427 | |
428 | if (description) |
429 | *description = NULL; |
430 | |
431 | if (_glfw.initialized) |
432 | error = _glfwPlatformGetTls(&_glfw.errorSlot); |
433 | else |
434 | error = &_glfwMainThreadError; |
435 | |
436 | if (error) |
437 | { |
438 | code = error->code; |
439 | error->code = GLFW_NO_ERROR; |
440 | if (description && code) |
441 | *description = error->description; |
442 | } |
443 | |
444 | return code; |
445 | } |
446 | |
447 | GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun) |
448 | { |
449 | _GLFW_SWAP(GLFWerrorfun, _glfwErrorCallback, cbfun); |
450 | return cbfun; |
451 | } |
452 | |
453 | |