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//
50static _GLFWerror _glfwMainThreadError;
51static GLFWerrorfun _glfwErrorCallback;
52static GLFWallocator _glfwInitAllocator;
53static _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//
70static 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//
77static void defaultDeallocate(void* block, void* user)
78{
79 free(block);
80}
81
82// The reallocation function used when no custom allocator is set
83//
84static void* defaultReallocate(void* block, size_t size, void* user)
85{
86 return realloc(block, size);
87}
88
89// Terminate the library
90//
91static 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
144char* _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
152float _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
164float _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
176void* _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
201void* _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
223void _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//
236void _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
313GLFWAPI 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
359GLFWAPI void glfwTerminate(void)
360{
361 if (!_glfw.initialized)
362 return;
363
364 terminate();
365}
366
367GLFWAPI 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
395GLFWAPI 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
408GLFWAPI void glfwInitVulkanLoader(PFN_vkGetInstanceProcAddr loader)
409{
410 _glfwInitHints.vulkanLoader = loader;
411}
412
413GLFWAPI 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
423GLFWAPI 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
447GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun)
448{
449 _GLFW_SWAP(GLFWerrorfun, _glfwErrorCallback, cbfun);
450 return cbfun;
451}
452
453