aboutsummaryrefslogtreecommitdiff
path: root/raylib/src/platforms/rcore_web.c
diff options
context:
space:
mode:
Diffstat (limited to 'raylib/src/platforms/rcore_web.c')
-rw-r--r--raylib/src/platforms/rcore_web.c1792
1 files changed, 1792 insertions, 0 deletions
diff --git a/raylib/src/platforms/rcore_web.c b/raylib/src/platforms/rcore_web.c
new file mode 100644
index 0000000..f9d93e5
--- /dev/null
+++ b/raylib/src/platforms/rcore_web.c
@@ -0,0 +1,1792 @@
1/**********************************************************************************************
2*
3* rcore_web - Functions to manage window, graphics device and inputs
4*
5* PLATFORM: WEB
6* - HTML5 (WebAssembly)
7*
8* LIMITATIONS:
9* - Limitation 01
10* - Limitation 02
11*
12* POSSIBLE IMPROVEMENTS:
13* - Replace glfw3 dependency by direct browser API calls (same as library_glfw3.js)
14*
15* ADDITIONAL NOTES:
16* - TRACELOG() function is located in raylib [utils] module
17*
18* CONFIGURATION:
19* #define RCORE_PLATFORM_CUSTOM_FLAG
20* Custom flag for rcore on target platform -not used-
21*
22* DEPENDENCIES:
23* - emscripten: Allow interaction between browser API and C
24* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
25*
26*
27* LICENSE: zlib/libpng
28*
29* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
30*
31* This software is provided "as-is", without any express or implied warranty. In no event
32* will the authors be held liable for any damages arising from the use of this software.
33*
34* Permission is granted to anyone to use this software for any purpose, including commercial
35* applications, and to alter it and redistribute it freely, subject to the following restrictions:
36*
37* 1. The origin of this software must not be misrepresented; you must not claim that you
38* wrote the original software. If you use this software in a product, an acknowledgment
39* in the product documentation would be appreciated but is not required.
40*
41* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
42* as being the original software.
43*
44* 3. This notice may not be removed or altered from any source distribution.
45*
46**********************************************************************************************/
47
48#define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3
49 // NOTE: Already provided by rlgl implementation (on glad.h)
50#include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management
51
52#include <emscripten/emscripten.h> // Emscripten functionality for C
53#include <emscripten/html5.h> // Emscripten HTML5 library
54
55#include <sys/time.h> // Required for: timespec, nanosleep(), select() - POSIX
56
57//----------------------------------------------------------------------------------
58// Defines and Macros
59//----------------------------------------------------------------------------------
60// TODO: HACK: Added flag if not provided by GLFW when using external library
61// Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev
62#if !defined(GLFW_MOUSE_PASSTHROUGH)
63 #define GLFW_MOUSE_PASSTHROUGH 0x0002000D
64#endif
65
66#if (_POSIX_C_SOURCE < 199309L)
67 #undef _POSIX_C_SOURCE
68 #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext.
69#endif
70
71//----------------------------------------------------------------------------------
72// Types and Structures Definition
73//----------------------------------------------------------------------------------
74typedef struct {
75 GLFWwindow *handle; // GLFW window handle (graphic device)
76 bool ourFullscreen; // Internal var to filter our handling of fullscreen vs the user handling of fullscreen
77 int unmaximizedWidth; // Internal var to store the unmaximized window (canvas) width
78 int unmaximizedHeight; // Internal var to store the unmaximized window (canvas) height
79} PlatformData;
80
81//----------------------------------------------------------------------------------
82// Global Variables Definition
83//----------------------------------------------------------------------------------
84extern CoreData CORE; // Global CORE state context
85
86static PlatformData platform = { 0 }; // Platform specific data
87
88//----------------------------------------------------------------------------------
89// Local Variables Definition
90//----------------------------------------------------------------------------------
91static const char cursorLUT[11][12] = {
92 "default", // 0 MOUSE_CURSOR_DEFAULT
93 "default", // 1 MOUSE_CURSOR_ARROW
94 "text", // 2 MOUSE_CURSOR_IBEAM
95 "crosshair", // 3 MOUSE_CURSOR_CROSSHAIR
96 "pointer", // 4 MOUSE_CURSOR_POINTING_HAND
97 "ew-resize", // 5 MOUSE_CURSOR_RESIZE_EW
98 "ns-resize", // 6 MOUSE_CURSOR_RESIZE_NS
99 "nwse-resize", // 7 MOUSE_CURSOR_RESIZE_NWSE
100 "nesw-resize", // 8 MOUSE_CURSOR_RESIZE_NESW
101 "move", // 9 MOUSE_CURSOR_RESIZE_ALL
102 "not-allowed" // 10 MOUSE_CURSOR_NOT_ALLOWED
103};
104
105Vector2 lockedMousePos = { 0 };
106
107//----------------------------------------------------------------------------------
108// Module Internal Functions Declaration
109//----------------------------------------------------------------------------------
110int InitPlatform(void); // Initialize platform (graphics, inputs and more)
111void ClosePlatform(void); // Close platform
112
113// Error callback event
114static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error
115
116// Window callbacks events
117static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized
118static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored
119//static void WindowMaximizeCallback(GLFWwindow *window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized
120static void WindowFocusCallback(GLFWwindow *window, int focused); // GLFW3 WindowFocus Callback, runs when window get/lose focus
121static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window
122static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley); // GLFW3 Window Content Scale Callback, runs when window changes scale
123
124// Input callbacks events
125static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed
126static void CharCallback(GLFWwindow *window, unsigned int key); // GLFW3 Char Key Callback, runs on key pressed (get char value)
127static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods); // GLFW3 Mouse Button Callback, runs on mouse button pressed
128static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); // GLFW3 Cursor Position Callback, runs on mouse move
129static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel
130static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area
131
132// Emscripten window callback events
133static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData);
134// static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
135static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
136
137// Emscripten input callback events
138static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
139static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
140static EM_BOOL EmscriptenPointerlockCallback(int eventType, const EmscriptenPointerlockChangeEvent *pointerlockChangeEvent, void *userData);
141static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData);
142static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData);
143
144//----------------------------------------------------------------------------------
145// Module Functions Declaration
146//----------------------------------------------------------------------------------
147// NOTE: Functions declaration is provided by raylib.h
148
149//----------------------------------------------------------------------------------
150// Module Functions Definition: Window and Graphics Device
151//----------------------------------------------------------------------------------
152
153// Check if application should close
154bool WindowShouldClose(void)
155{
156 // Emterpreter-Async required to run sync code
157 // https://github.com/emscripten-core/emscripten/wiki/Emterpreter#emterpreter-async-run-synchronous-code
158 // By default, this function is never called on a web-ready raylib example because we encapsulate
159 // frame code in a UpdateDrawFrame() function, to allow browser manage execution asynchronously
160 // but now emscripten allows sync code to be executed in an interpreted way, using emterpreter!
161 emscripten_sleep(16);
162 return false;
163}
164
165// Toggle fullscreen mode
166void ToggleFullscreen(void)
167{
168 platform.ourFullscreen = true;
169 bool enterFullscreen = false;
170
171 const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
172 if (wasFullscreen)
173 {
174 if (CORE.Window.flags & FLAG_FULLSCREEN_MODE) enterFullscreen = false;
175 else if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) enterFullscreen = true;
176 else
177 {
178 const int canvasWidth = EM_ASM_INT( { return document.getElementById('canvas').width; }, 0);
179 const int canvasStyleWidth = EM_ASM_INT( { return parseInt(document.getElementById('canvas').style.width); }, 0);
180 if (canvasStyleWidth > canvasWidth) enterFullscreen = false;
181 else enterFullscreen = true;
182 }
183
184 EM_ASM(document.exitFullscreen(););
185
186 CORE.Window.fullscreen = false;
187 CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
188 CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
189 }
190 else enterFullscreen = true;
191
192 if (enterFullscreen)
193 {
194 // NOTE: The setTimeouts handle the browser mode change delay
195 EM_ASM
196 (
197 setTimeout(function()
198 {
199 Module.requestFullscreen(false, false);
200 }, 100);
201 );
202 CORE.Window.fullscreen = true;
203 CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
204 }
205
206 // NOTE: Old notes below:
207 /*
208 EM_ASM
209 (
210 // This strategy works well while using raylib minimal web shell for emscripten,
211 // it re-scales the canvas to fullscreen using monitor resolution, for tools this
212 // is a good strategy but maybe games prefer to keep current canvas resolution and
213 // display it in fullscreen, adjusting monitor resolution if possible
214 if (document.fullscreenElement) document.exitFullscreen();
215 else Module.requestFullscreen(true, true); //false, true);
216 );
217 */
218 // EM_ASM(Module.requestFullscreen(false, false););
219 /*
220 if (!CORE.Window.fullscreen)
221 {
222 // Option 1: Request fullscreen for the canvas element
223 // This option does not seem to work at all:
224 // emscripten_request_pointerlock() and emscripten_request_fullscreen() are affected by web security,
225 // the user must click once on the canvas to hide the pointer or transition to full screen
226 //emscripten_request_fullscreen("#canvas", false);
227
228 // Option 2: Request fullscreen for the canvas element with strategy
229 // This option does not seem to work at all
230 // Ref: https://github.com/emscripten-core/emscripten/issues/5124
231 // EmscriptenFullscreenStrategy strategy = {
232 // .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH, //EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT,
233 // .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF,
234 // .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT,
235 // .canvasResizedCallback = EmscriptenWindowResizedCallback,
236 // .canvasResizedCallbackUserData = NULL
237 // };
238 //emscripten_request_fullscreen_strategy("#canvas", EM_FALSE, &strategy);
239
240 // Option 3: Request fullscreen for the canvas element with strategy
241 // It works as expected but only inside the browser (client area)
242 EmscriptenFullscreenStrategy strategy = {
243 .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT,
244 .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF,
245 .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT,
246 .canvasResizedCallback = EmscriptenWindowResizedCallback,
247 .canvasResizedCallbackUserData = NULL
248 };
249 emscripten_enter_soft_fullscreen("#canvas", &strategy);
250
251 int width, height;
252 emscripten_get_canvas_element_size("#canvas", &width, &height);
253 TRACELOG(LOG_WARNING, "Emscripten: Enter fullscreen: Canvas size: %i x %i", width, height);
254
255 CORE.Window.fullscreen = true; // Toggle fullscreen flag
256 CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
257 }
258 else
259 {
260 //emscripten_exit_fullscreen();
261 //emscripten_exit_soft_fullscreen();
262
263 int width, height;
264 emscripten_get_canvas_element_size("#canvas", &width, &height);
265 TRACELOG(LOG_WARNING, "Emscripten: Exit fullscreen: Canvas size: %i x %i", width, height);
266
267 CORE.Window.fullscreen = false; // Toggle fullscreen flag
268 CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
269 }
270 */
271}
272
273// Toggle borderless windowed mode
274void ToggleBorderlessWindowed(void)
275{
276 platform.ourFullscreen = true;
277 bool enterBorderless = false;
278
279 const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
280 if (wasFullscreen)
281 {
282 if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) enterBorderless = false;
283 else if (CORE.Window.flags & FLAG_FULLSCREEN_MODE) enterBorderless = true;
284 else
285 {
286 const int canvasWidth = EM_ASM_INT( { return document.getElementById('canvas').width; }, 0);
287 const int screenWidth = EM_ASM_INT( { return screen.width; }, 0);
288 if (screenWidth == canvasWidth) enterBorderless = false;
289 else enterBorderless = true;
290 }
291
292 EM_ASM(document.exitFullscreen(););
293
294 CORE.Window.fullscreen = false;
295 CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
296 CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
297 }
298 else enterBorderless = true;
299
300 if (enterBorderless)
301 {
302 // NOTE: 1. The setTimeouts handle the browser mode change delay
303 // 2. The style unset handles the possibility of a width="value%" like on the default shell.html file
304 EM_ASM
305 (
306 setTimeout(function()
307 {
308 Module.requestFullscreen(false, true);
309 setTimeout(function()
310 {
311 canvas.style.width="unset";
312 }, 100);
313 }, 100);
314 );
315 CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE;
316 }
317}
318
319// Set window state: maximized, if resizable
320void MaximizeWindow(void)
321{
322 if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE && !(CORE.Window.flags & FLAG_WINDOW_MAXIMIZED))
323 {
324 platform.unmaximizedWidth = CORE.Window.screen.width;
325 platform.unmaximizedHeight = CORE.Window.screen.height;
326
327 const int tabWidth = EM_ASM_INT( return window.innerWidth; );
328 const int tabHeight = EM_ASM_INT( return window.innerHeight; );
329
330 if (tabWidth && tabHeight) glfwSetWindowSize(platform.handle, tabWidth, tabHeight);
331
332 CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
333 }
334}
335
336// Set window state: minimized
337void MinimizeWindow(void)
338{
339 TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform");
340}
341
342// Set window state: not minimized/maximized
343void RestoreWindow(void)
344{
345 if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE && (CORE.Window.flags & FLAG_WINDOW_MAXIMIZED))
346 {
347 if (platform.unmaximizedWidth && platform.unmaximizedHeight) glfwSetWindowSize(platform.handle, platform.unmaximizedWidth, platform.unmaximizedHeight);
348
349 CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
350 }
351}
352
353// Set window configuration state using flags
354void SetWindowState(unsigned int flags)
355{
356 // Check previous state and requested state to apply required changes
357 // NOTE: In most cases the functions already change the flags internally
358
359 // State change: FLAG_VSYNC_HINT
360 if ((flags & FLAG_VSYNC_HINT) > 0)
361 {
362 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_VSYNC_HINT) not available on target platform");
363 }
364
365 // State change: FLAG_BORDERLESS_WINDOWED_MODE
366 if ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)
367 {
368 // NOTE: Window state flag updated inside ToggleBorderlessWindowed() function
369 const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
370 if (wasFullscreen)
371 {
372 const int canvasWidth = EM_ASM_INT( { return document.getElementById('canvas').width; }, 0);
373 const int canvasStyleWidth = EM_ASM_INT( { return parseInt(document.getElementById('canvas').style.width); }, 0);
374 if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) || canvasStyleWidth > canvasWidth) ToggleBorderlessWindowed();
375 }
376 else ToggleBorderlessWindowed();
377 }
378
379 // State change: FLAG_FULLSCREEN_MODE
380 if ((flags & FLAG_FULLSCREEN_MODE) > 0)
381 {
382 // NOTE: Window state flag updated inside ToggleFullscreen() function
383 const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
384 if (wasFullscreen)
385 {
386 const int canvasWidth = EM_ASM_INT( { return document.getElementById('canvas').width; }, 0);
387 const int screenWidth = EM_ASM_INT( { return screen.width; }, 0);
388 if ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) || screenWidth == canvasWidth ) ToggleFullscreen();
389 }
390 else ToggleFullscreen();
391 }
392
393 // State change: FLAG_WINDOW_RESIZABLE
394 if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
395 {
396 glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_TRUE);
397 CORE.Window.flags |= FLAG_WINDOW_RESIZABLE;
398 }
399
400 // State change: FLAG_WINDOW_UNDECORATED
401 if ((flags & FLAG_WINDOW_UNDECORATED) > 0)
402 {
403 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_UNDECORATED) not available on target platform");
404 }
405
406 // State change: FLAG_WINDOW_HIDDEN
407 if ((flags & FLAG_WINDOW_HIDDEN) > 0)
408 {
409 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_HIDDEN) not available on target platform");
410 }
411
412 // State change: FLAG_WINDOW_MINIMIZED
413 if ((flags & FLAG_WINDOW_MINIMIZED) > 0)
414 {
415 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_MINIMIZED) not available on target platform");
416 }
417
418 // State change: FLAG_WINDOW_MAXIMIZED
419 if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) != (flags & FLAG_WINDOW_MAXIMIZED)) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
420 {
421 if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE)
422 {
423 platform.unmaximizedWidth = CORE.Window.screen.width;
424 platform.unmaximizedHeight = CORE.Window.screen.height;
425
426 const int tabWidth = EM_ASM_INT( return window.innerWidth; );
427 const int tabHeight = EM_ASM_INT( return window.innerHeight; );
428
429 if (tabWidth && tabHeight) glfwSetWindowSize(platform.handle, tabWidth, tabHeight);
430
431 CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
432 }
433 }
434
435 // State change: FLAG_WINDOW_UNFOCUSED
436 if ((flags & FLAG_WINDOW_UNFOCUSED) > 0)
437 {
438 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_UNFOCUSED) not available on target platform");
439 }
440
441 // State change: FLAG_WINDOW_TOPMOST
442 if ((flags & FLAG_WINDOW_TOPMOST) > 0)
443 {
444 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_TOPMOST) not available on target platform");
445 }
446
447 // State change: FLAG_WINDOW_ALWAYS_RUN
448 if ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0)
449 {
450 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_ALWAYS_RUN) not available on target platform");
451 }
452
453 // The following states can not be changed after window creation
454 // NOTE: Review for PLATFORM_WEB
455
456 // State change: FLAG_WINDOW_TRANSPARENT
457 if ((flags & FLAG_WINDOW_TRANSPARENT) > 0)
458 {
459 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_TRANSPARENT) not available on target platform");
460 }
461
462 // State change: FLAG_WINDOW_HIGHDPI
463 if ((flags & FLAG_WINDOW_HIGHDPI) > 0)
464 {
465 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_HIGHDPI) not available on target platform");
466 }
467
468 // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
469 if ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)
470 {
471 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_MOUSE_PASSTHROUGH) not available on target platform");
472 }
473
474 // State change: FLAG_MSAA_4X_HINT
475 if ((flags & FLAG_MSAA_4X_HINT) > 0)
476 {
477 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_MSAA_4X_HINT) not available on target platform");
478 }
479
480 // State change: FLAG_INTERLACED_HINT
481 if ((flags & FLAG_INTERLACED_HINT) > 0)
482 {
483 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_INTERLACED_HINT) not available on target platform");
484 }
485}
486
487// Clear window configuration state flags
488void ClearWindowState(unsigned int flags)
489{
490 // Check previous state and requested state to apply required changes
491 // NOTE: In most cases the functions already change the flags internally
492
493 // State change: FLAG_VSYNC_HINT
494 if ((flags & FLAG_VSYNC_HINT) > 0)
495 {
496 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_VSYNC_HINT) not available on target platform");
497 }
498
499 // State change: FLAG_BORDERLESS_WINDOWED_MODE
500 if ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)
501 {
502 const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
503 if (wasFullscreen)
504 {
505 const int canvasWidth = EM_ASM_INT( { return document.getElementById('canvas').width; }, 0);
506 const int screenWidth = EM_ASM_INT( { return screen.width; }, 0);
507 if ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) || (screenWidth == canvasWidth)) EM_ASM(document.exitFullscreen(););
508 }
509
510 CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
511 }
512
513 // State change: FLAG_FULLSCREEN_MODE
514 if ((flags & FLAG_FULLSCREEN_MODE) > 0)
515 {
516 const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
517 if (wasFullscreen)
518 {
519 const int canvasWidth = EM_ASM_INT( { return document.getElementById('canvas').width; }, 0);
520 const int canvasStyleWidth = EM_ASM_INT( { return parseInt(document.getElementById('canvas').style.width); }, 0);
521 if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) || (canvasStyleWidth > canvasWidth)) EM_ASM(document.exitFullscreen(););
522 }
523
524 CORE.Window.fullscreen = false;
525 CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
526 }
527
528 // State change: FLAG_WINDOW_RESIZABLE
529 if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
530 {
531 glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_FALSE);
532 CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE;
533 }
534
535 // State change: FLAG_WINDOW_HIDDEN
536 if ((flags & FLAG_WINDOW_HIDDEN) > 0)
537 {
538 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_HIDDEN) not available on target platform");
539 }
540
541 // State change: FLAG_WINDOW_MINIMIZED
542 if ((flags & FLAG_WINDOW_MINIMIZED) > 0)
543 {
544 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_MINIMIZED) not available on target platform");
545 }
546
547 // State change: FLAG_WINDOW_MAXIMIZED
548 if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
549 {
550 if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE)
551 {
552 if (platform.unmaximizedWidth && platform.unmaximizedHeight) glfwSetWindowSize(platform.handle, platform.unmaximizedWidth, platform.unmaximizedHeight);
553
554 CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
555 }
556 }
557
558 // State change: FLAG_WINDOW_UNDECORATED
559 if ((flags & FLAG_WINDOW_UNDECORATED) > 0)
560 {
561 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_UNDECORATED) not available on target platform");
562 }
563
564 // State change: FLAG_WINDOW_UNFOCUSED
565 if ((flags & FLAG_WINDOW_UNFOCUSED) > 0)
566 {
567 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_UNFOCUSED) not available on target platform");
568 }
569
570 // State change: FLAG_WINDOW_TOPMOST
571 if ((flags & FLAG_WINDOW_TOPMOST) > 0)
572 {
573 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_TOPMOST) not available on target platform");
574 }
575
576 // State change: FLAG_WINDOW_ALWAYS_RUN
577 if ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0)
578 {
579 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_ALWAYS_RUN) not available on target platform");
580 }
581
582 // The following states can not be changed after window creation
583 // NOTE: Review for PLATFORM_WEB
584
585 // State change: FLAG_WINDOW_TRANSPARENT
586 if ((flags & FLAG_WINDOW_TRANSPARENT) > 0)
587 {
588 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_TRANSPARENT) not available on target platform");
589 }
590
591 // State change: FLAG_WINDOW_HIGHDPI
592 if ((flags & FLAG_WINDOW_HIGHDPI) > 0)
593 {
594 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_HIGHDPI) not available on target platform");
595 }
596
597 // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
598 if ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)
599 {
600 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_MOUSE_PASSTHROUGH) not available on target platform");
601 }
602
603 // State change: FLAG_MSAA_4X_HINT
604 if ((flags & FLAG_MSAA_4X_HINT) > 0)
605 {
606 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_MSAA_4X_HINT) not available on target platform");
607 }
608
609 // State change: FLAG_INTERLACED_HINT
610 if ((flags & FLAG_INTERLACED_HINT) > 0)
611 {
612 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_INTERLACED_HINT) not available on target platform");
613 }
614}
615
616// Set icon for window
617void SetWindowIcon(Image image)
618{
619 TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform");
620}
621
622// Set icon for window, multiple images
623void SetWindowIcons(Image *images, int count)
624{
625 TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform");
626}
627
628// Set title for window
629void SetWindowTitle(const char *title)
630{
631 CORE.Window.title = title;
632 emscripten_set_window_title(title);
633}
634
635// Set window position on screen (windowed mode)
636void SetWindowPosition(int x, int y)
637{
638 TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform");
639}
640
641// Set monitor for the current window
642void SetWindowMonitor(int monitor)
643{
644 TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform");
645}
646
647// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
648void SetWindowMinSize(int width, int height)
649{
650 CORE.Window.screenMin.width = width;
651 CORE.Window.screenMin.height = height;
652
653 // Trigger the resize event once to update the window minimum width and height
654 if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
655}
656
657// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
658void SetWindowMaxSize(int width, int height)
659{
660 CORE.Window.screenMax.width = width;
661 CORE.Window.screenMax.height = height;
662
663 // Trigger the resize event once to update the window maximum width and height
664 if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
665}
666
667// Set window dimensions
668void SetWindowSize(int width, int height)
669{
670 glfwSetWindowSize(platform.handle, width, height);
671}
672
673// Set window opacity, value opacity is between 0.0 and 1.0
674void SetWindowOpacity(float opacity)
675{
676 if (opacity >= 1.0f) opacity = 1.0f;
677 else if (opacity <= 0.0f) opacity = 0.0f;
678 EM_ASM({ document.getElementById('canvas').style.opacity = $0; }, opacity);
679}
680
681// Set window focused
682void SetWindowFocused(void)
683{
684 TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform");
685}
686
687// Get native window handle
688void *GetWindowHandle(void)
689{
690 TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform");
691 return NULL;
692}
693
694// Get number of monitors
695int GetMonitorCount(void)
696{
697 TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform");
698 return 1;
699}
700
701// Get number of monitors
702int GetCurrentMonitor(void)
703{
704 TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform");
705 return 0;
706}
707
708// Get selected monitor position
709Vector2 GetMonitorPosition(int monitor)
710{
711 TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform");
712 return (Vector2){ 0, 0 };
713}
714
715// Get selected monitor width (currently used by monitor)
716int GetMonitorWidth(int monitor)
717{
718 // NOTE: Returned value is limited to the current monitor where the browser window is located
719 int width = 0;
720 width = EM_ASM_INT( { return screen.width; }, 0);
721 return width;
722}
723
724// Get selected monitor height (currently used by monitor)
725int GetMonitorHeight(int monitor)
726{
727 // NOTE: Returned value is limited to the current monitor where the browser window is located
728 int height = 0;
729 height = EM_ASM_INT( { return screen.height; }, 0);
730 return height;
731}
732
733// Get selected monitor physical width in millimetres
734int GetMonitorPhysicalWidth(int monitor)
735{
736 TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform");
737 return 0;
738}
739
740// Get selected monitor physical height in millimetres
741int GetMonitorPhysicalHeight(int monitor)
742{
743 TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform");
744 return 0;
745}
746
747// Get selected monitor refresh rate
748int GetMonitorRefreshRate(int monitor)
749{
750 TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform");
751 return 0;
752}
753
754// Get the human-readable, UTF-8 encoded name of the selected monitor
755const char *GetMonitorName(int monitor)
756{
757 TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform");
758 return "";
759}
760
761// Get window position XY on monitor
762Vector2 GetWindowPosition(void)
763{
764 // NOTE: Returned position is relative to the current monitor where the browser window is located
765 Vector2 position = { 0, 0 };
766 position.x = (float)EM_ASM_INT( { return window.screenX; }, 0);
767 position.y = (float)EM_ASM_INT( { return window.screenY; }, 0);
768 return position;
769}
770
771// Get window scale DPI factor for current monitor
772Vector2 GetWindowScaleDPI(void)
773{
774 TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform");
775 return (Vector2){ 1.0f, 1.0f };
776}
777
778// Set clipboard text content
779void SetClipboardText(const char *text)
780{
781 // Security check to (partially) avoid malicious code
782 if (strchr(text, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided Clipboard could be potentially malicious, avoid [\'] character");
783 else EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, text);
784}
785
786// Get clipboard text content
787// NOTE: returned string is allocated and freed by GLFW
788const char *GetClipboardText(void)
789{
790/*
791 // Accessing clipboard data from browser is tricky due to security reasons
792 // The method to use is navigator.clipboard.readText() but this is an asynchronous method
793 // that will return at some moment after the function is called with the required data
794 emscripten_run_script_string("navigator.clipboard.readText() \
795 .then(text => { document.getElementById('clipboard').innerText = text; console.log('Pasted content: ', text); }) \
796 .catch(err => { console.error('Failed to read clipboard contents: ', err); });"
797 );
798
799 // The main issue is getting that data, one approach could be using ASYNCIFY and wait
800 // for the data but it requires adding Asyncify emscripten library on compilation
801
802 // Another approach could be just copy the data in a HTML text field and try to retrieve it
803 // later on if available... and clean it for future accesses
804*/
805 return NULL;
806}
807
808// Show mouse cursor
809void ShowCursor(void)
810{
811 if (CORE.Input.Mouse.cursorHidden)
812 {
813 EM_ASM( { document.getElementById("canvas").style.cursor = UTF8ToString($0); }, cursorLUT[CORE.Input.Mouse.cursor]);
814
815 CORE.Input.Mouse.cursorHidden = false;
816 }
817}
818
819// Hides mouse cursor
820void HideCursor(void)
821{
822 if (!CORE.Input.Mouse.cursorHidden)
823 {
824 EM_ASM(document.getElementById('canvas').style.cursor = 'none';);
825
826 CORE.Input.Mouse.cursorHidden = true;
827 }
828}
829
830// Enables cursor (unlock cursor)
831void EnableCursor(void)
832{
833 emscripten_exit_pointerlock();
834
835 // Set cursor position in the middle
836 SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
837
838 // NOTE: CORE.Input.Mouse.cursorHidden handled by EmscriptenPointerlockCallback()
839}
840
841// Disables cursor (lock cursor)
842void DisableCursor(void)
843{
844 // TODO: figure out how not to hard code the canvas ID here.
845 emscripten_request_pointerlock("#canvas", 1);
846
847 // Set cursor position in the middle
848 SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
849
850 // NOTE: CORE.Input.Mouse.cursorHidden handled by EmscriptenPointerlockCallback()
851}
852
853// Swap back buffer with front buffer (screen drawing)
854void SwapScreenBuffer(void)
855{
856 glfwSwapBuffers(platform.handle);
857}
858
859//----------------------------------------------------------------------------------
860// Module Functions Definition: Misc
861//----------------------------------------------------------------------------------
862
863// Get elapsed time measure in seconds since InitTimer()
864double GetTime(void)
865{
866 double time = glfwGetTime(); // Elapsed time since glfwInit()
867 return time;
868}
869
870// Open URL with default system browser (if available)
871// NOTE: This function is only safe to use if you control the URL given.
872// A user could craft a malicious string performing another action.
873// Only call this function yourself not with user input or make sure to check the string yourself.
874// Ref: https://github.com/raysan5/raylib/issues/686
875void OpenURL(const char *url)
876{
877 // Security check to (partially) avoid malicious code on target platform
878 if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
879 else emscripten_run_script(TextFormat("window.open('%s', '_blank')", url));
880}
881
882//----------------------------------------------------------------------------------
883// Module Functions Definition: Inputs
884//----------------------------------------------------------------------------------
885
886// Set internal gamepad mappings
887int SetGamepadMappings(const char *mappings)
888{
889 TRACELOG(LOG_INFO, "SetGamepadMappings not implemented in rcore_web.c");
890
891 return 0;
892}
893
894// Set gamepad vibration
895void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
896{
897 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (duration > 0.0f))
898 {
899 if (leftMotor < 0.0f) leftMotor = 0.0f;
900 if (leftMotor > 1.0f) leftMotor = 1.0f;
901 if (rightMotor < 0.0f) rightMotor = 0.0f;
902 if (rightMotor > 1.0f) rightMotor = 1.0f;
903 if (duration > MAX_GAMEPAD_VIBRATION_TIME) duration = MAX_GAMEPAD_VIBRATION_TIME;
904 duration *= 1000.0f; // Convert duration to ms
905
906 // Note: At the moment (2024.10.21) Chrome, Edge, Opera, Safari, Android Chrome, Android Webview only support the vibrationActuator API,
907 // and Firefox only supports the hapticActuators API
908 EM_ASM({
909 try
910 {
911 navigator.getGamepads()[$0].vibrationActuator.playEffect('dual-rumble', { startDelay: 0, duration: $3, weakMagnitude: $1, strongMagnitude: $2 });
912 }
913 catch (e)
914 {
915 try
916 {
917 navigator.getGamepads()[$0].hapticActuators[0].pulse($2, $3);
918 }
919 catch (e) { }
920 }
921 }, gamepad, leftMotor, rightMotor, duration);
922 }
923}
924
925// Set mouse position XY
926void SetMousePosition(int x, int y)
927{
928 CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
929 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
930
931 if (CORE.Input.Mouse.cursorHidden) lockedMousePos = CORE.Input.Mouse.currentPosition;
932
933 // NOTE: emscripten not implemented
934 glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y);
935}
936
937// Set mouse cursor
938void SetMouseCursor(int cursor)
939{
940 if (CORE.Input.Mouse.cursor != cursor)
941 {
942 if (!CORE.Input.Mouse.cursorHidden) EM_ASM( { document.getElementById('canvas').style.cursor = UTF8ToString($0); }, cursorLUT[cursor]);
943
944 CORE.Input.Mouse.cursor = cursor;
945 }
946}
947
948// Get physical key name.
949const char *GetKeyName(int key)
950{
951 TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
952 return "";
953}
954
955// Register all input events
956void PollInputEvents(void)
957{
958#if defined(SUPPORT_GESTURES_SYSTEM)
959 // NOTE: Gestures update must be called every frame to reset gestures correctly
960 // because ProcessGestureEvent() is just called on an event, not every frame
961 UpdateGestures();
962#endif
963
964 // Reset keys/chars pressed registered
965 CORE.Input.Keyboard.keyPressedQueueCount = 0;
966 CORE.Input.Keyboard.charPressedQueueCount = 0;
967
968 // Reset last gamepad button/axis registered state
969 CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
970 //CORE.Input.Gamepad.axisCount = 0;
971
972 // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback)
973
974 // Register previous keys states
975 for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
976 {
977 CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
978 CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
979 }
980
981 // Register previous mouse states
982 for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i];
983
984 // Register previous mouse wheel state
985 CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove;
986 CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f };
987
988 // Register previous mouse position
989 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
990
991 // Register previous touch states
992 for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
993
994 // Reset touch positions
995 // TODO: It resets on target platform the mouse position and not filled again until a move-event,
996 // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed!
997 //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 };
998
999 // Gamepad support using emscripten API
1000 // NOTE: GLFW3 joystick functionality not available in web
1001
1002 // Get number of gamepads connected
1003 int numGamepads = 0;
1004 if (emscripten_sample_gamepad_data() == EMSCRIPTEN_RESULT_SUCCESS) numGamepads = emscripten_get_num_gamepads();
1005
1006 for (int i = 0; (i < numGamepads) && (i < MAX_GAMEPADS); i++)
1007 {
1008 // Register previous gamepad button states
1009 for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
1010
1011 EmscriptenGamepadEvent gamepadState;
1012
1013 int result = emscripten_get_gamepad_status(i, &gamepadState);
1014
1015 if (result == EMSCRIPTEN_RESULT_SUCCESS)
1016 {
1017 // Register buttons data for every connected gamepad
1018 for (int j = 0; (j < gamepadState.numButtons) && (j < MAX_GAMEPAD_BUTTONS); j++)
1019 {
1020 GamepadButton button = -1;
1021
1022 // Gamepad Buttons reference: https://www.w3.org/TR/gamepad/#gamepad-interface
1023 switch (j)
1024 {
1025 case 0: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break;
1026 case 1: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
1027 case 2: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break;
1028 case 3: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
1029 case 4: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break;
1030 case 5: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break;
1031 case 6: button = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break;
1032 case 7: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break;
1033 case 8: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break;
1034 case 9: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break;
1035 case 10: button = GAMEPAD_BUTTON_LEFT_THUMB; break;
1036 case 11: button = GAMEPAD_BUTTON_RIGHT_THUMB; break;
1037 case 12: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break;
1038 case 13: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break;
1039 case 14: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break;
1040 case 15: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break;
1041 default: break;
1042 }
1043
1044 if (button + 1 != 0) // Check for valid button
1045 {
1046 if (gamepadState.digitalButton[j] == 1)
1047 {
1048 CORE.Input.Gamepad.currentButtonState[i][button] = 1;
1049 CORE.Input.Gamepad.lastButtonPressed = button;
1050 }
1051 else CORE.Input.Gamepad.currentButtonState[i][button] = 0;
1052 }
1053
1054 //TRACELOGD("INPUT: Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]);
1055 }
1056
1057 // Register axis data for every connected gamepad
1058 for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXIS); j++)
1059 {
1060 CORE.Input.Gamepad.axisState[i][j] = gamepadState.axis[j];
1061 }
1062
1063 CORE.Input.Gamepad.axisCount[i] = gamepadState.numAxes;
1064 }
1065 }
1066
1067 CORE.Window.resizedLastFrame = false;
1068
1069 // TODO: This code does not seem to do anything??
1070 //if (CORE.Window.eventWaiting) glfwWaitEvents(); // Wait for in input events before continue (drawing is paused)
1071 //else glfwPollEvents(); // Poll input events: keyboard/mouse/window events (callbacks) --> WARNING: Where is key input reset?
1072}
1073
1074//----------------------------------------------------------------------------------
1075// Module Internal Functions Definition
1076//----------------------------------------------------------------------------------
1077
1078// Initialize platform: graphics, inputs and more
1079int InitPlatform(void)
1080{
1081 glfwSetErrorCallback(ErrorCallback);
1082
1083 // Initialize GLFW internal global state
1084 int result = glfwInit();
1085 if (result == GLFW_FALSE) { TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); return -1; }
1086
1087 // Initialize graphic device: display/window and graphic context
1088 //----------------------------------------------------------------------------
1089 glfwDefaultWindowHints(); // Set default windows hints
1090 // glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits
1091 // glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits
1092 // glfwWindowHint(GLFW_BLUE_BITS, 8); // Framebuffer blue color component bits
1093 // glfwWindowHint(GLFW_ALPHA_BITS, 8); // Framebuffer alpha color component bits
1094 // glfwWindowHint(GLFW_DEPTH_BITS, 24); // Depthbuffer bits
1095 // glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window
1096 // glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API
1097 // glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers
1098
1099 // Check window creation flags
1100 if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true;
1101
1102 if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window
1103 else glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); // Window initially hidden
1104
1105 if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window
1106 else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); // Decorated window
1107
1108 if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window
1109 else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); // Avoid window being resizable
1110
1111 // Disable FLAG_WINDOW_MINIMIZED, not supported on initialization
1112 if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
1113
1114 // Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization
1115 if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
1116
1117 if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
1118 else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE);
1119
1120 if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
1121 else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
1122
1123 // NOTE: Some GLFW flags are not supported on HTML5
1124 // e.g.: GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_MOUSE_PASSTHROUGH
1125
1126 // Scale content area based on the monitor content scale where window is placed on
1127 // NOTE: This feature requires emscripten 3.1.51
1128 //if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
1129 //else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE);
1130
1131 if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
1132 {
1133 // NOTE: MSAA is only enabled for main framebuffer, not user-created FBOs
1134 TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
1135 glfwWindowHint(GLFW_SAMPLES, 4); // Tries to enable multisampling x4 (MSAA), default is 0
1136 }
1137
1138 // NOTE: When asking for an OpenGL context version, most drivers provide the highest supported version
1139 // with backward compatibility to older OpenGL versions.
1140 // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context.
1141
1142 // Check selection OpenGL version
1143 if (rlGetVersion() == RL_OPENGL_21)
1144 {
1145 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); // Choose OpenGL major version (just hint)
1146 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); // Choose OpenGL minor version (just hint)
1147 }
1148 else if (rlGetVersion() == RL_OPENGL_33)
1149 {
1150 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint)
1151 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint)
1152 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above!
1153 // Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE
1154 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Forward Compatibility Hint: Only 3.3 and above!
1155 // glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context
1156 }
1157 else if (rlGetVersion() == RL_OPENGL_43)
1158 {
1159 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Choose OpenGL major version (just hint)
1160 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint)
1161 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
1162 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE);
1163#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT)
1164 glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context
1165#endif
1166 }
1167 else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context
1168 {
1169 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
1170 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
1171 glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
1172 glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API);
1173 }
1174 else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context
1175 {
1176 // TODO: It seems WebGL 2.0 context is not set despite being requested
1177 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
1178 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
1179 glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
1180 glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API);
1181 }
1182
1183 // NOTE: Getting video modes is not implemented in emscripten GLFW3 version
1184 CORE.Window.display.width = CORE.Window.screen.width;
1185 CORE.Window.display.height = CORE.Window.screen.height;
1186
1187 // Init fullscreen toggle required var:
1188 platform.ourFullscreen = false;
1189
1190 if (CORE.Window.fullscreen)
1191 {
1192 // remember center for switchinging from fullscreen to window
1193 if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width))
1194 {
1195 // If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed.
1196 // Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11.
1197 CORE.Window.position.x = CORE.Window.display.width/4;
1198 CORE.Window.position.y = CORE.Window.display.height/4;
1199 }
1200 else
1201 {
1202 CORE.Window.position.x = CORE.Window.display.width/2 - CORE.Window.screen.width/2;
1203 CORE.Window.position.y = CORE.Window.display.height/2 - CORE.Window.screen.height/2;
1204 }
1205
1206 if (CORE.Window.position.x < 0) CORE.Window.position.x = 0;
1207 if (CORE.Window.position.y < 0) CORE.Window.position.y = 0;
1208
1209 // Obtain recommended CORE.Window.display.width/CORE.Window.display.height from a valid videomode for the monitor
1210 int count = 0;
1211 const GLFWvidmode *modes = glfwGetVideoModes(glfwGetPrimaryMonitor(), &count);
1212
1213 // Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height
1214 for (int i = 0; i < count; i++)
1215 {
1216 if ((unsigned int)modes[i].width >= CORE.Window.screen.width)
1217 {
1218 if ((unsigned int)modes[i].height >= CORE.Window.screen.height)
1219 {
1220 CORE.Window.display.width = modes[i].width;
1221 CORE.Window.display.height = modes[i].height;
1222 break;
1223 }
1224 }
1225 }
1226
1227 TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
1228
1229 // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example,
1230 // for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3),
1231 // framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched
1232 // by the sides to fit all monitor space...
1233
1234 // Try to setup the most appropriate fullscreen framebuffer for the requested screenWidth/screenHeight
1235 // It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset)
1236 // Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale
1237 // TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed...
1238 // HighDPI monitors are properly considered in a following similar function: SetupViewport()
1239 SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
1240
1241 platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL);
1242
1243 // NOTE: Full-screen change, not working properly...
1244 // glfwSetWindowMonitor(platform.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
1245 }
1246 else
1247 {
1248 // No-fullscreen window creation
1249 platform.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL);
1250
1251 if (platform.handle)
1252 {
1253 CORE.Window.render.width = CORE.Window.screen.width;
1254 CORE.Window.render.height = CORE.Window.screen.height;
1255 }
1256 }
1257
1258 if (!platform.handle)
1259 {
1260 glfwTerminate();
1261 TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window");
1262 return -1;
1263 }
1264
1265 // WARNING: glfwCreateWindow() title doesn't work with emscripten
1266 emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " ");
1267
1268 // Set window callback events
1269 glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default!
1270 glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback);
1271 glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback);
1272 glfwSetDropCallback(platform.handle, WindowDropCallback);
1273
1274 if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
1275 {
1276 glfwSetWindowContentScaleCallback(platform.handle, WindowContentScaleCallback);
1277 }
1278
1279 // Set input callback events
1280 glfwSetKeyCallback(platform.handle, KeyCallback);
1281 glfwSetCharCallback(platform.handle, CharCallback);
1282 glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback);
1283 glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes
1284 glfwSetScrollCallback(platform.handle, MouseScrollCallback);
1285 glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback);
1286
1287 glfwMakeContextCurrent(platform.handle);
1288 result = true; // TODO: WARNING: glfwGetError(NULL); symbol can not be found in Web
1289
1290 // Check context activation
1291 if (result == true) //(result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR))
1292 {
1293 CORE.Window.ready = true;
1294
1295 int fbWidth = CORE.Window.screen.width;
1296 int fbHeight = CORE.Window.screen.height;
1297
1298 CORE.Window.render.width = fbWidth;
1299 CORE.Window.render.height = fbHeight;
1300 CORE.Window.currentFbo.width = fbWidth;
1301 CORE.Window.currentFbo.height = fbHeight;
1302
1303 TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
1304 TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
1305 TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
1306 TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height);
1307 TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
1308 }
1309 else
1310 {
1311 TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device");
1312 return -1;
1313 }
1314
1315 if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
1316
1317 // If graphic device is no properly initialized, we end program
1318 if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; }
1319
1320 // Load OpenGL extensions
1321 // NOTE: GL procedures address loader is required to load extensions
1322 rlLoadExtensions(glfwGetProcAddress);
1323 //----------------------------------------------------------------------------
1324
1325 // Initialize input events callbacks
1326 //----------------------------------------------------------------------------
1327 // Setup callback functions for the DOM events
1328 emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenFullscreenChangeCallback);
1329
1330 // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review
1331 // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas)
1332 // emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
1333 // Check Resize event (note this is done on the window since most browsers don't support this on #canvas)
1334 emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
1335
1336 // Trigger this once to get initial window sizing
1337 EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
1338
1339 // Support keyboard events -> Not used, GLFW.JS takes care of that
1340 // emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback);
1341 // emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback);
1342
1343 // Support mouse events
1344 emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback);
1345 emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenPointerlockCallback);
1346
1347 // Following the mouse delta when the mouse is locked
1348 emscripten_set_mousemove_callback("#canvas", NULL, 1, EmscriptenMouseMoveCallback);
1349
1350 // Support touch events
1351 emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
1352 emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
1353 emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
1354 emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
1355
1356 // Support gamepad events (not provided by GLFW3 on emscripten)
1357 emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback);
1358 emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback);
1359 //----------------------------------------------------------------------------
1360
1361 // Initialize timing system
1362 //----------------------------------------------------------------------------
1363 InitTimer();
1364 //----------------------------------------------------------------------------
1365
1366 // Initialize storage system
1367 //----------------------------------------------------------------------------
1368 CORE.Storage.basePath = GetWorkingDirectory();
1369 //----------------------------------------------------------------------------
1370
1371 TRACELOG(LOG_INFO, "PLATFORM: WEB: Initialized successfully");
1372
1373 return 0;
1374}
1375
1376// Close platform
1377void ClosePlatform(void)
1378{
1379 glfwDestroyWindow(platform.handle);
1380 glfwTerminate();
1381}
1382
1383// GLFW3 Error Callback, runs on GLFW3 error
1384static void ErrorCallback(int error, const char *description)
1385{
1386 TRACELOG(LOG_WARNING, "GLFW: Error: %i Description: %s", error, description);
1387}
1388
1389// GLFW3 WindowSize Callback, runs when window is resizedLastFrame
1390// NOTE: Window resizing not allowed by default
1391static void WindowSizeCallback(GLFWwindow *window, int width, int height)
1392{
1393 // Reset viewport and projection matrix for new size
1394 SetupViewport(width, height);
1395
1396 CORE.Window.currentFbo.width = width;
1397 CORE.Window.currentFbo.height = height;
1398 CORE.Window.resizedLastFrame = true;
1399
1400 if (IsWindowFullscreen()) return;
1401
1402 // Set current screen size
1403 if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
1404 {
1405 Vector2 windowScaleDPI = GetWindowScaleDPI();
1406
1407 CORE.Window.screen.width = (unsigned int)(width/windowScaleDPI.x);
1408 CORE.Window.screen.height = (unsigned int)(height/windowScaleDPI.y);
1409 }
1410 else
1411 {
1412 CORE.Window.screen.width = width;
1413 CORE.Window.screen.height = height;
1414 }
1415
1416 // NOTE: Postprocessing texture is not scaled to new size
1417}
1418
1419static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley)
1420{
1421 CORE.Window.screenScale = MatrixScale(scalex, scaley, 1.0f);
1422}
1423
1424// GLFW3 WindowIconify Callback, runs when window is minimized/restored
1425static void WindowIconifyCallback(GLFWwindow *window, int iconified)
1426{
1427 if (iconified) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; // The window was iconified
1428 else CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // The window was restored
1429}
1430
1431/*
1432// GLFW3 Window Maximize Callback, runs when window is maximized
1433static void WindowMaximizeCallback(GLFWwindow *window, int maximized)
1434{
1435 // TODO.
1436}
1437*/
1438
1439// GLFW3 WindowFocus Callback, runs when window get/lose focus
1440static void WindowFocusCallback(GLFWwindow *window, int focused)
1441{
1442 if (focused) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // The window was focused
1443 else CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; // The window lost focus
1444}
1445
1446// GLFW3 Window Drop Callback, runs when drop files into window
1447static void WindowDropCallback(GLFWwindow *window, int count, const char **paths)
1448{
1449 if (count > 0)
1450 {
1451 // In case previous dropped filepaths have not been freed, we free them
1452 if (CORE.Window.dropFileCount > 0)
1453 {
1454 for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilepaths[i]);
1455
1456 RL_FREE(CORE.Window.dropFilepaths);
1457
1458 CORE.Window.dropFileCount = 0;
1459 CORE.Window.dropFilepaths = NULL;
1460 }
1461
1462 // WARNING: Paths are freed by GLFW when the callback returns, we must keep an internal copy
1463 CORE.Window.dropFileCount = count;
1464 CORE.Window.dropFilepaths = (char **)RL_CALLOC(CORE.Window.dropFileCount, sizeof(char *));
1465
1466 for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++)
1467 {
1468 CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
1469 strcpy(CORE.Window.dropFilepaths[i], paths[i]);
1470 }
1471 }
1472}
1473
1474// GLFW3 Keyboard Callback, runs on key pressed
1475static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
1476{
1477 if (key < 0) return; // Security check, macOS fn key generates -1
1478
1479 // WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1
1480 // to work properly with our implementation (IsKeyDown/IsKeyUp checks)
1481 if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0;
1482 else if(action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1;
1483 else if(action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
1484
1485 // Check if there is space available in the key queue
1486 if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS))
1487 {
1488 // Add character to the queue
1489 CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key;
1490 CORE.Input.Keyboard.keyPressedQueueCount++;
1491 }
1492
1493 // Check the exit key to set close window
1494 if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE);
1495}
1496
1497// GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value)
1498static void CharCallback(GLFWwindow *window, unsigned int key)
1499{
1500 //TRACELOG(LOG_DEBUG, "Char Callback: KEY:%i(%c)", key, key);
1501
1502 // NOTE: Registers any key down considering OS keyboard layout but
1503 // does not detect action events, those should be managed by user...
1504 // Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907
1505 // Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char
1506
1507 // Check if there is space available in the queue
1508 if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
1509 {
1510 // Add character to the queue
1511 CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = key;
1512 CORE.Input.Keyboard.charPressedQueueCount++;
1513 }
1514}
1515
1516// GLFW3 Mouse Button Callback, runs on mouse button pressed
1517static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods)
1518{
1519 // WARNING: GLFW could only return GLFW_PRESS (1) or GLFW_RELEASE (0) for now,
1520 // but future releases may add more actions (i.e. GLFW_REPEAT)
1521 CORE.Input.Mouse.currentButtonState[button] = action;
1522 CORE.Input.Touch.currentTouchState[button] = action;
1523
1524#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)
1525 // Process mouse events as touches to be able to use mouse-gestures
1526 GestureEvent gestureEvent = { 0 };
1527
1528 // Register touch actions
1529 if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
1530 else if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) gestureEvent.touchAction = TOUCH_ACTION_UP;
1531
1532 // NOTE: TOUCH_ACTION_MOVE event is registered in MouseCursorPosCallback()
1533
1534 // Assign a pointer ID
1535 gestureEvent.pointId[0] = 0;
1536
1537 // Register touch points count
1538 gestureEvent.pointCount = 1;
1539
1540 // Register touch points position, only one point registered
1541 gestureEvent.position[0] = GetMousePosition();
1542
1543 // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
1544 gestureEvent.position[0].x /= (float)GetScreenWidth();
1545 gestureEvent.position[0].y /= (float)GetScreenHeight();
1546
1547 // Gesture data is sent to gestures-system for processing
1548 // Prevent calling ProcessGestureEvent() when Emscripten is present and there's a touch gesture, so EmscriptenTouchCallback() can handle it itself
1549 if (GetMouseX() != 0 || GetMouseY() != 0) ProcessGestureEvent(gestureEvent);
1550
1551#endif
1552}
1553
1554// GLFW3 Cursor Position Callback, runs on mouse move
1555static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
1556{
1557 // If the pointer is not locked, follow the position
1558 if (!CORE.Input.Mouse.cursorHidden)
1559 {
1560 CORE.Input.Mouse.currentPosition.x = (float)x;
1561 CORE.Input.Mouse.currentPosition.y = (float)y;
1562 CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
1563 }
1564
1565#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)
1566 // Process mouse events as touches to be able to use mouse-gestures
1567 GestureEvent gestureEvent = { 0 };
1568
1569 gestureEvent.touchAction = TOUCH_ACTION_MOVE;
1570
1571 // Assign a pointer ID
1572 gestureEvent.pointId[0] = 0;
1573
1574 // Register touch points count
1575 gestureEvent.pointCount = 1;
1576
1577 // Register touch points position, only one point registered
1578 gestureEvent.position[0] = CORE.Input.Touch.position[0];
1579
1580 // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
1581 gestureEvent.position[0].x /= (float)GetScreenWidth();
1582 gestureEvent.position[0].y /= (float)GetScreenHeight();
1583
1584 // Gesture data is sent to gestures-system for processing
1585 ProcessGestureEvent(gestureEvent);
1586#endif
1587}
1588
1589static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
1590{
1591 // To emulate the GLFW_RAW_MOUSE_MOTION property.
1592 if (CORE.Input.Mouse.cursorHidden)
1593 {
1594 CORE.Input.Mouse.previousPosition.x = lockedMousePos.x - mouseEvent->movementX;
1595 CORE.Input.Mouse.previousPosition.y = lockedMousePos.y - mouseEvent->movementY;
1596 }
1597
1598 return 1; // The event was consumed by the callback handler
1599}
1600
1601// GLFW3 Scrolling Callback, runs on mouse wheel
1602static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset)
1603{
1604 CORE.Input.Mouse.currentWheelMove = (Vector2){ (float)xoffset, (float)yoffset };
1605}
1606
1607// GLFW3 CursorEnter Callback, when cursor enters the window
1608static void CursorEnterCallback(GLFWwindow *window, int enter)
1609{
1610 if (enter) CORE.Input.Mouse.cursorOnScreen = true;
1611 else CORE.Input.Mouse.cursorOnScreen = false;
1612}
1613
1614// Register fullscreen change events
1615static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData)
1616{
1617 // NOTE: 1. Reset the fullscreen flags if the user left fullscreen manually by pressing the Escape key
1618 // 2. Which is a necessary safeguard because that case will bypass the toggles CORE.Window.flags resets
1619 if (platform.ourFullscreen) platform.ourFullscreen = false;
1620 else
1621 {
1622 const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
1623 if (!wasFullscreen)
1624 {
1625 CORE.Window.fullscreen = false;
1626 CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
1627 CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
1628 }
1629 }
1630
1631 return 1; // The event was consumed by the callback handler
1632}
1633
1634// Register window resize event
1635// static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData)
1636// {
1637// // TODO: Implement EmscriptenWindowResizedCallback()?
1638
1639// return 1; // The event was consumed by the callback handler
1640// }
1641
1642// Register DOM element resize event
1643static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData)
1644{
1645 // Don't resize non-resizeable windows
1646 if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) return 1;
1647
1648 // This event is called whenever the window changes sizes,
1649 // so the size of the canvas object is explicitly retrieved below
1650 int width = EM_ASM_INT( return window.innerWidth; );
1651 int height = EM_ASM_INT( return window.innerHeight; );
1652
1653 if (width < (int)CORE.Window.screenMin.width) width = CORE.Window.screenMin.width;
1654 else if (width > (int)CORE.Window.screenMax.width && CORE.Window.screenMax.width > 0) width = CORE.Window.screenMax.width;
1655
1656 if (height < (int)CORE.Window.screenMin.height) height = CORE.Window.screenMin.height;
1657 else if (height > (int)CORE.Window.screenMax.height && CORE.Window.screenMax.height > 0) height = CORE.Window.screenMax.height;
1658
1659 emscripten_set_canvas_element_size("#canvas", width, height);
1660
1661 SetupViewport(width, height); // Reset viewport and projection matrix for new size
1662
1663 CORE.Window.currentFbo.width = width;
1664 CORE.Window.currentFbo.height = height;
1665 CORE.Window.resizedLastFrame = true;
1666
1667 if (IsWindowFullscreen()) return 1;
1668
1669 // Set current screen size
1670 CORE.Window.screen.width = width;
1671 CORE.Window.screen.height = height;
1672
1673 // NOTE: Postprocessing texture is not scaled to new size
1674
1675 return 0;
1676}
1677
1678// Register mouse input events
1679static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
1680{
1681 // This is only for registering mouse click events with emscripten and doesn't need to do anything
1682
1683 return 1; // The event was consumed by the callback handler
1684}
1685
1686// Register pointer lock events
1687static EM_BOOL EmscriptenPointerlockCallback(int eventType, const EmscriptenPointerlockChangeEvent *pointerlockChangeEvent, void *userData)
1688{
1689 CORE.Input.Mouse.cursorHidden = EM_ASM_INT( { if (document.pointerLockElement) return 1; }, 0);
1690
1691 if (CORE.Input.Mouse.cursorHidden)
1692 {
1693 lockedMousePos = CORE.Input.Mouse.currentPosition;
1694 CORE.Input.Mouse.previousPosition = lockedMousePos;
1695 }
1696
1697 return 1; // The event was consumed by the callback handler
1698}
1699
1700// Register connected/disconnected gamepads events
1701static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
1702{
1703 /*
1704 TRACELOGD("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"",
1705 eventType != 0? emscripten_event_type_to_string(eventType) : "Gamepad state",
1706 gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping);
1707
1708 for (int i = 0; i < gamepadEvent->numAxes; ++i) TRACELOGD("Axis %d: %g", i, gamepadEvent->axis[i]);
1709 for (int i = 0; i < gamepadEvent->numButtons; ++i) TRACELOGD("Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]);
1710 */
1711
1712 if ((gamepadEvent->connected) && (gamepadEvent->index < MAX_GAMEPADS))
1713 {
1714 CORE.Input.Gamepad.ready[gamepadEvent->index] = true;
1715 sprintf(CORE.Input.Gamepad.name[gamepadEvent->index], "%s", gamepadEvent->id);
1716 }
1717 else CORE.Input.Gamepad.ready[gamepadEvent->index] = false;
1718
1719 return 1; // The event was consumed by the callback handler
1720}
1721
1722// Register touch input events
1723static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
1724{
1725 // Register touch points count
1726 CORE.Input.Touch.pointCount = touchEvent->numTouches;
1727
1728 double canvasWidth = 0.0;
1729 double canvasHeight = 0.0;
1730 // NOTE: emscripten_get_canvas_element_size() returns canvas.width and canvas.height but
1731 // we are looking for actual CSS size: canvas.style.width and canvas.style.height
1732 // EMSCRIPTEN_RESULT res = emscripten_get_canvas_element_size("#canvas", &canvasWidth, &canvasHeight);
1733 emscripten_get_element_css_size("#canvas", &canvasWidth, &canvasHeight);
1734
1735 for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++)
1736 {
1737 // Register touch points id
1738 CORE.Input.Touch.pointId[i] = touchEvent->touches[i].identifier;
1739
1740 // Register touch points position
1741 CORE.Input.Touch.position[i] = (Vector2){touchEvent->touches[i].targetX, touchEvent->touches[i].targetY};
1742
1743 // Normalize gestureEvent.position[x] for CORE.Window.screen.width and CORE.Window.screen.height
1744 CORE.Input.Touch.position[i].x *= ((float)GetScreenWidth()/(float)canvasWidth);
1745 CORE.Input.Touch.position[i].y *= ((float)GetScreenHeight()/(float)canvasHeight);
1746
1747 if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) CORE.Input.Touch.currentTouchState[i] = 1;
1748 else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) CORE.Input.Touch.currentTouchState[i] = 0;
1749 }
1750
1751 // Update mouse position if we detect a single touch.
1752 if (CORE.Input.Touch.pointCount == 1)
1753 {
1754 CORE.Input.Mouse.currentPosition.x = CORE.Input.Touch.position[0].x;
1755 CORE.Input.Mouse.currentPosition.y = CORE.Input.Touch.position[0].y;
1756 }
1757
1758#if defined(SUPPORT_GESTURES_SYSTEM)
1759 GestureEvent gestureEvent = {0};
1760
1761 gestureEvent.pointCount = CORE.Input.Touch.pointCount;
1762
1763 // Register touch actions
1764 if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
1765 else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) gestureEvent.touchAction = TOUCH_ACTION_UP;
1766 else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE;
1767 else if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL;
1768
1769 for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++)
1770 {
1771 gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i];
1772 gestureEvent.position[i] = CORE.Input.Touch.position[i];
1773
1774 // Normalize gestureEvent.position[i]
1775 gestureEvent.position[i].x /= (float)GetScreenWidth();
1776 gestureEvent.position[i].y /= (float)GetScreenHeight();
1777 }
1778
1779 // Gesture data is sent to gestures system for processing
1780 ProcessGestureEvent(gestureEvent);
1781#endif
1782
1783 if (eventType == EMSCRIPTEN_EVENT_TOUCHEND)
1784 {
1785 CORE.Input.Touch.pointCount--;
1786 if (CORE.Input.Touch.pointCount < 0) CORE.Input.Touch.pointCount = 0;
1787 }
1788
1789 return 1; // The event was consumed by the callback handler
1790}
1791
1792// EOF

Generated with cgit - Back to sebastiano.tronto.net