Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/src/lib/libglfw.js
4150 views
1
/**
2
* @license
3
* Copyright 2013 The Emscripten Authors
4
* SPDX-License-Identifier: MIT
5
*/
6
7
/*
8
* EMSCRIPTEN GLFW 2.x-3.x emulation.
9
* It tries to emulate the behavior described in
10
* http://www.glfw.org/docs/latest/
11
*
12
* This also implements parts of GLFW 2.x on top of GLFW 3.x.
13
*
14
* What it does:
15
* - Creates a GL context.
16
* - Manage keyboard and mouse events.
17
* - GL Extensions support.
18
*
19
* What it does not but should probably do:
20
* - Transmit events when glfwPollEvents, glfwWaitEvents or glfwSwapBuffers is
21
* called. Events callbacks are called as soon as event are received.
22
* - Input modes.
23
* - Gamma ramps.
24
* - Video modes.
25
* - Monitors.
26
* - Clipboard (not possible from javascript?).
27
* - Multiple windows.
28
* - Error codes && messages through callback.
29
* - Thread emulation. (removed in GLFW3).
30
* - Image/Texture I/O support (removed in GLFW 3).
31
*
32
* Authors:
33
* - Jari Vetoniemi <[email protected]>
34
* - Éloi Rivard <[email protected]>
35
* - Thomas Borsos <[email protected]>
36
*/
37
38
var LibraryGLFW = {
39
$GLFW_Window__docs: '/** @constructor */',
40
$GLFW_Window: function(id, width, height, framebufferWidth, framebufferHeight, title, monitor, share) {
41
this.id = id;
42
this.x = 0;
43
this.y = 0;
44
this.fullscreen = false; // Used to determine if app in fullscreen mode
45
this.storedX = 0; // Used to store X before fullscreen
46
this.storedY = 0; // Used to store Y before fullscreen
47
this.width = width;
48
this.height = height;
49
this.framebufferWidth = framebufferWidth;
50
this.framebufferHeight = framebufferHeight;
51
this.storedWidth = width; // Used to store width before fullscreen
52
this.storedHeight = height; // Used to store height before fullscreen
53
this.title = title;
54
this.monitor = monitor;
55
this.share = share;
56
this.attributes = {...GLFW.hints};
57
this.inputModes = {
58
0x00033001:0x00034001, // GLFW_CURSOR (GLFW_CURSOR_NORMAL)
59
0x00033002:0, // GLFW_STICKY_KEYS
60
0x00033003:0, // GLFW_STICKY_MOUSE_BUTTONS
61
};
62
this.buttons = 0;
63
this.keys = new Array();
64
this.domKeys = new Array();
65
this.shouldClose = 0;
66
this.title = null;
67
this.windowPosFunc = 0; // GLFWwindowposfun
68
this.windowSizeFunc = 0; // GLFWwindowsizefun
69
this.windowCloseFunc = 0; // GLFWwindowclosefun
70
this.windowRefreshFunc = 0; // GLFWwindowrefreshfun
71
this.windowFocusFunc = 0; // GLFWwindowfocusfun
72
this.windowIconifyFunc = 0; // GLFWwindowiconifyfun
73
this.windowMaximizeFunc = 0; // GLFWwindowmaximizefun
74
this.framebufferSizeFunc = 0; // GLFWframebuffersizefun
75
this.windowContentScaleFunc = 0; // GLFWwindowcontentscalefun
76
this.mouseButtonFunc = 0; // GLFWmousebuttonfun
77
this.cursorPosFunc = 0; // GLFWcursorposfun
78
this.cursorEnterFunc = 0; // GLFWcursorenterfun
79
this.scrollFunc = 0; // GLFWscrollfun
80
this.dropFunc = 0; // GLFWdropfun
81
this.keyFunc = 0; // GLFWkeyfun
82
this.charFunc = 0; // GLFWcharfun
83
this.userptr = 0;
84
},
85
86
$GLFW__deps: ['emscripten_get_now', '$GL', '$Browser', '$GLFW_Window',
87
'malloc', 'free',
88
'$MainLoop',
89
'$stringToNewUTF8',
90
'$getFullscreenElement',
91
'emscripten_set_window_title',
92
#if FILESYSTEM
93
'$FS',
94
#endif
95
],
96
$GLFW: {
97
WindowFromId: (id) => {
98
if (id <= 0 || !GLFW.windows) return null;
99
return GLFW.windows[id - 1];
100
},
101
102
joystickFunc: 0, // GLFWjoystickfun
103
errorFunc: 0, // GLFWerrorfun
104
monitorFunc: 0, // GLFWmonitorfun
105
active: null, // active window
106
scale: null,
107
windows: null,
108
monitors: null,
109
monitorString: null,
110
versionString: null,
111
initialTime: null,
112
extensions: null,
113
devicePixelRatioMQL: null, // MediaQueryList from window.matchMedia
114
hints: null,
115
primaryTouchId: null,
116
defaultHints: {
117
0x00020001:0, // GLFW_FOCUSED
118
0x00020002:0, // GLFW_ICONIFIED
119
0x00020003:1, // GLFW_RESIZABLE
120
0x00020004:1, // GLFW_VISIBLE
121
0x00020005:1, // GLFW_DECORATED
122
0x0002000A:0, // GLFW_TRANSPARENT_FRAMEBUFFER
123
0x0002200C:0, // GLFW_SCALE_TO_MONITOR
124
125
0x00021001:8, // GLFW_RED_BITS
126
0x00021002:8, // GLFW_GREEN_BITS
127
0x00021003:8, // GLFW_BLUE_BITS
128
0x00021004:8, // GLFW_ALPHA_BITS
129
0x00021005:24, // GLFW_DEPTH_BITS
130
0x00021006:8, // GLFW_STENCIL_BITS
131
0x00021007:0, // GLFW_ACCUM_RED_BITS
132
0x00021008:0, // GLFW_ACCUM_GREEN_BITS
133
0x00021009:0, // GLFW_ACCUM_BLUE_BITS
134
0x0002100A:0, // GLFW_ACCUM_ALPHA_BITS
135
0x0002100B:0, // GLFW_AUX_BUFFERS
136
0x0002100C:0, // GLFW_STEREO
137
0x0002100D:0, // GLFW_SAMPLES
138
0x0002100E:0, // GLFW_SRGB_CAPABLE
139
0x0002100F:0, // GLFW_REFRESH_RATE
140
141
0x00022001:0x00030001, // GLFW_CLIENT_API (GLFW_OPENGL_API)
142
0x00022002:1, // GLFW_CONTEXT_VERSION_MAJOR
143
0x00022003:0, // GLFW_CONTEXT_VERSION_MINOR
144
0x00022004:0, // GLFW_CONTEXT_REVISION
145
0x00022005:0, // GLFW_CONTEXT_ROBUSTNESS
146
0x00022006:0, // GLFW_OPENGL_FORWARD_COMPAT
147
0x00022007:0, // GLFW_OPENGL_DEBUG_CONTEXT
148
0x00022008:0, // GLFW_OPENGL_PROFILE
149
},
150
151
/*******************************************************************************
152
* DOM EVENT CALLBACKS
153
******************************************************************************/
154
155
/* https://developer.mozilla.org/en/Document_Object_Model_%28DOM%29/KeyboardEvent and GLFW/glfw3.h */
156
DOMToGLFWKeyCode: (keycode) => {
157
switch (keycode) {
158
// these keycodes are only defined for GLFW3, assume they are the same for GLFW2
159
case 0x20:return 32; // DOM_VK_SPACE -> GLFW_KEY_SPACE
160
case 0xDE:return 39; // DOM_VK_QUOTE -> GLFW_KEY_APOSTROPHE
161
case 0xBC:return 44; // DOM_VK_COMMA -> GLFW_KEY_COMMA
162
case 0xAD:return 45; // DOM_VK_HYPHEN_MINUS -> GLFW_KEY_MINUS
163
case 0xBD:return 45; // DOM_VK_MINUS -> GLFW_KEY_MINUS
164
case 0xBE:return 46; // DOM_VK_PERIOD -> GLFW_KEY_PERIOD
165
case 0xBF:return 47; // DOM_VK_SLASH -> GLFW_KEY_SLASH
166
case 0x30:return 48; // DOM_VK_0 -> GLFW_KEY_0
167
case 0x31:return 49; // DOM_VK_1 -> GLFW_KEY_1
168
case 0x32:return 50; // DOM_VK_2 -> GLFW_KEY_2
169
case 0x33:return 51; // DOM_VK_3 -> GLFW_KEY_3
170
case 0x34:return 52; // DOM_VK_4 -> GLFW_KEY_4
171
case 0x35:return 53; // DOM_VK_5 -> GLFW_KEY_5
172
case 0x36:return 54; // DOM_VK_6 -> GLFW_KEY_6
173
case 0x37:return 55; // DOM_VK_7 -> GLFW_KEY_7
174
case 0x38:return 56; // DOM_VK_8 -> GLFW_KEY_8
175
case 0x39:return 57; // DOM_VK_9 -> GLFW_KEY_9
176
case 0x3B:return 59; // DOM_VK_SEMICOLON -> GLFW_KEY_SEMICOLON
177
case 0x3D:return 61; // DOM_VK_EQUALS -> GLFW_KEY_EQUAL
178
case 0xBB:return 61; // DOM_VK_EQUALS -> GLFW_KEY_EQUAL
179
case 0x41:return 65; // DOM_VK_A -> GLFW_KEY_A
180
case 0x42:return 66; // DOM_VK_B -> GLFW_KEY_B
181
case 0x43:return 67; // DOM_VK_C -> GLFW_KEY_C
182
case 0x44:return 68; // DOM_VK_D -> GLFW_KEY_D
183
case 0x45:return 69; // DOM_VK_E -> GLFW_KEY_E
184
case 0x46:return 70; // DOM_VK_F -> GLFW_KEY_F
185
case 0x47:return 71; // DOM_VK_G -> GLFW_KEY_G
186
case 0x48:return 72; // DOM_VK_H -> GLFW_KEY_H
187
case 0x49:return 73; // DOM_VK_I -> GLFW_KEY_I
188
case 0x4A:return 74; // DOM_VK_J -> GLFW_KEY_J
189
case 0x4B:return 75; // DOM_VK_K -> GLFW_KEY_K
190
case 0x4C:return 76; // DOM_VK_L -> GLFW_KEY_L
191
case 0x4D:return 77; // DOM_VK_M -> GLFW_KEY_M
192
case 0x4E:return 78; // DOM_VK_N -> GLFW_KEY_N
193
case 0x4F:return 79; // DOM_VK_O -> GLFW_KEY_O
194
case 0x50:return 80; // DOM_VK_P -> GLFW_KEY_P
195
case 0x51:return 81; // DOM_VK_Q -> GLFW_KEY_Q
196
case 0x52:return 82; // DOM_VK_R -> GLFW_KEY_R
197
case 0x53:return 83; // DOM_VK_S -> GLFW_KEY_S
198
case 0x54:return 84; // DOM_VK_T -> GLFW_KEY_T
199
case 0x55:return 85; // DOM_VK_U -> GLFW_KEY_U
200
case 0x56:return 86; // DOM_VK_V -> GLFW_KEY_V
201
case 0x57:return 87; // DOM_VK_W -> GLFW_KEY_W
202
case 0x58:return 88; // DOM_VK_X -> GLFW_KEY_X
203
case 0x59:return 89; // DOM_VK_Y -> GLFW_KEY_Y
204
case 0x5a:return 90; // DOM_VK_Z -> GLFW_KEY_Z
205
case 0xDB:return 91; // DOM_VK_OPEN_BRACKET -> GLFW_KEY_LEFT_BRACKET
206
case 0xDC:return 92; // DOM_VK_BACKSLASH -> GLFW_KEY_BACKSLASH
207
case 0xDD:return 93; // DOM_VK_CLOSE_BRACKET -> GLFW_KEY_RIGHT_BRACKET
208
case 0xC0:return 96; // DOM_VK_BACK_QUOTE -> GLFW_KEY_GRAVE_ACCENT
209
210
#if USE_GLFW == 2
211
//#define GLFW_KEY_SPECIAL 256
212
case 0x1B:return (256+1); // DOM_VK_ESCAPE -> GLFW_KEY_ESC
213
case 0x70:return (256+2); // DOM_VK_F1 -> GLFW_KEY_F1
214
case 0x71:return (256+3); // DOM_VK_F2 -> GLFW_KEY_F2
215
case 0x72:return (256+4); // DOM_VK_F3 -> GLFW_KEY_F3
216
case 0x73:return (256+5); // DOM_VK_F4 -> GLFW_KEY_F4
217
case 0x74:return (256+6); // DOM_VK_F5 -> GLFW_KEY_F5
218
case 0x75:return (256+7); // DOM_VK_F6 -> GLFW_KEY_F6
219
case 0x76:return (256+8); // DOM_VK_F7 -> GLFW_KEY_F7
220
case 0x77:return (256+9); // DOM_VK_F8 -> GLFW_KEY_F8
221
case 0x78:return (256+10); // DOM_VK_F9 -> GLFW_KEY_F9
222
case 0x79:return (256+11); // DOM_VK_F10 -> GLFW_KEY_F10
223
case 0x7A:return (256+12); // DOM_VK_F11 -> GLFW_KEY_F11
224
case 0x7B:return (256+13); // DOM_VK_F12 -> GLFW_KEY_F12
225
case 0x7C:return (256+14); // DOM_VK_F13 -> GLFW_KEY_F13
226
case 0x7D:return (256+15); // DOM_VK_F14 -> GLFW_KEY_F14
227
case 0x7E:return (256+16); // DOM_VK_F15 -> GLFW_KEY_F15
228
case 0x7F:return (256+17); // DOM_VK_F16 -> GLFW_KEY_F16
229
case 0x80:return (256+18); // DOM_VK_F17 -> GLFW_KEY_F17
230
case 0x81:return (256+19); // DOM_VK_F18 -> GLFW_KEY_F18
231
case 0x82:return (256+20); // DOM_VK_F19 -> GLFW_KEY_F19
232
case 0x83:return (256+21); // DOM_VK_F20 -> GLFW_KEY_F20
233
case 0x84:return (256+22); // DOM_VK_F21 -> GLFW_KEY_F21
234
case 0x85:return (256+23); // DOM_VK_F22 -> GLFW_KEY_F22
235
case 0x86:return (256+24); // DOM_VK_F23 -> GLFW_KEY_F23
236
case 0x87:return (256+25); // DOM_VK_F24 -> GLFW_KEY_F24
237
case 0x88:return (256+26); // 0x88 (not used?) -> GLFW_KEY_F25
238
case 0x27:return (256+30); // DOM_VK_RIGHT -> GLFW_KEY_RIGHT
239
case 0x25:return (256+29); // DOM_VK_LEFT -> GLFW_KEY_LEFT
240
case 0x28:return (256+28); // DOM_VK_DOWN -> GLFW_KEY_DOWN
241
case 0x26:return (256+27); // DOM_VK_UP -> GLFW_KEY_UP
242
case 0x10:return (256+31); // DOM_VK_SHIFT -> GLFW_KEY_LSHIFT
243
// #define GLFW_KEY_RSHIFT (GLFW_KEY_SPECIAL+32)
244
case 0x11:return (256+33); // DOM_VK_CONTROL -> GLFW_KEY_LCTRL
245
// #define GLFW_KEY_RCTRL (GLFW_KEY_SPECIAL+34)
246
case 0x12:return (256+35); // DOM_VK_ALT -> GLFW_KEY_LALT
247
// #define GLFW_KEY_RALT (GLFW_KEY_SPECIAL+36)
248
case 0x09:return (256+37); // DOM_VK_TAB -> GLFW_KEY_TAB
249
case 0x0D:return (256+38); // DOM_VK_RETURN -> GLFW_KEY_ENTER
250
case 0x08:return (256+39); // DOM_VK_BACK -> GLFW_KEY_BACKSPACE
251
case 0x2D:return (256+40); // DOM_VK_INSERT -> GLFW_KEY_INSERT
252
case 0x2E:return (256+41); // DOM_VK_DELETE -> GLFW_KEY_DEL
253
case 0x21:return (256+42); // DOM_VK_PAGE_UP -> GLFW_KEY_PAGEUP
254
case 0x22:return (256+43); // DOM_VK_PAGE_DOWN -> GLFW_KEY_PAGEDOWN
255
case 0x24:return (256+44); // DOM_VK_HOME -> GLFW_KEY_HOME
256
case 0x23:return (256+45); // DOM_VK_END -> GLFW_KEY_END
257
case 0x60:return (256+46); // DOM_VK_NUMPAD0 -> GLFW_KEY_KP_0
258
case 0x61:return (256+47); // DOM_VK_NUMPAD1 -> GLFW_KEY_KP_1
259
case 0x62:return (256+48); // DOM_VK_NUMPAD2 -> GLFW_KEY_KP_2
260
case 0x63:return (256+49); // DOM_VK_NUMPAD3 -> GLFW_KEY_KP_3
261
case 0x64:return (256+50); // DOM_VK_NUMPAD4 -> GLFW_KEY_KP_4
262
case 0x65:return (256+51); // DOM_VK_NUMPAD5 -> GLFW_KEY_KP_5
263
case 0x66:return (256+52); // DOM_VK_NUMPAD6 -> GLFW_KEY_KP_6
264
case 0x67:return (256+53); // DOM_VK_NUMPAD7 -> GLFW_KEY_KP_7
265
case 0x68:return (256+54); // DOM_VK_NUMPAD8 -> GLFW_KEY_KP_8
266
case 0x69:return (256+55); // DOM_VK_NUMPAD9 -> GLFW_KEY_KP_9
267
case 0x6F:return (256+56); // DOM_VK_DIVIDE -> GLFW_KEY_KP_DIVIDE
268
case 0x6A:return (256+57); // DOM_VK_MULTIPLY -> GLFW_KEY_KP_MULTIPLY
269
case 0x6D:return (256+58); // DOM_VK_SUBTRACT -> GLFW_KEY_KP_SUBTRACT
270
case 0x6B:return (256+59); // DOM_VK_ADD -> GLFW_KEY_KP_ADD
271
case 0x6E:return (256+60); // DOM_VK_DECIMAL -> GLFW_KEY_KP_DECIMAL
272
// #define GLFW_KEY_KP_EQUAL (GLFW_KEY_SPECIAL+61)
273
// #define GLFW_KEY_KP_ENTER (GLFW_KEY_SPECIAL+62)
274
case 0x90:return (256+63); // DOM_VK_NUM_LOCK -> GLFW_KEY_KP_NUM_LOCK
275
case 0x14:return (256+64); // DOM_VK_CAPS_LOCK -> GLFW_KEY_CAPS_LOCK
276
case 0x91:return (256+65); // DOM_VK_SCROLL_LOCK -> GLFW_KEY_SCROLL_LOCK
277
case 0x13:return (256+66); // DOM_VK_PAUSE -> GLFW_KEY_PAUSE
278
case 0x5B:return (256+67); // DOM_VK_WIN -> GLFW_KEY_LSUPER
279
// #define GLFW_KEY_RSUPER (GLFW_KEY_SPECIAL+68)
280
case 0x5D:return (256+69); // DOM_VK_CONTEXT_MENU -> GLFW_KEY_MENU
281
#endif
282
283
#if USE_GLFW == 3
284
case 0x1B:return 256; // DOM_VK_ESCAPE -> GLFW_KEY_ESCAPE
285
case 0x0D:return 257; // DOM_VK_RETURN -> GLFW_KEY_ENTER
286
case 0x09:return 258; // DOM_VK_TAB -> GLFW_KEY_TAB
287
case 0x08:return 259; // DOM_VK_BACK -> GLFW_KEY_BACKSPACE
288
case 0x2D:return 260; // DOM_VK_INSERT -> GLFW_KEY_INSERT
289
case 0x2E:return 261; // DOM_VK_DELETE -> GLFW_KEY_DELETE
290
case 0x27:return 262; // DOM_VK_RIGHT -> GLFW_KEY_RIGHT
291
case 0x25:return 263; // DOM_VK_LEFT -> GLFW_KEY_LEFT
292
case 0x28:return 264; // DOM_VK_DOWN -> GLFW_KEY_DOWN
293
case 0x26:return 265; // DOM_VK_UP -> GLFW_KEY_UP
294
case 0x21:return 266; // DOM_VK_PAGE_UP -> GLFW_KEY_PAGE_UP
295
case 0x22:return 267; // DOM_VK_PAGE_DOWN -> GLFW_KEY_PAGE_DOWN
296
case 0x24:return 268; // DOM_VK_HOME -> GLFW_KEY_HOME
297
case 0x23:return 269; // DOM_VK_END -> GLFW_KEY_END
298
case 0x14:return 280; // DOM_VK_CAPS_LOCK -> GLFW_KEY_CAPS_LOCK
299
case 0x91:return 281; // DOM_VK_SCROLL_LOCK -> GLFW_KEY_SCROLL_LOCK
300
case 0x90:return 282; // DOM_VK_NUM_LOCK -> GLFW_KEY_NUM_LOCK
301
case 0x2C:return 283; // DOM_VK_SNAPSHOT -> GLFW_KEY_PRINT_SCREEN
302
case 0x13:return 284; // DOM_VK_PAUSE -> GLFW_KEY_PAUSE
303
case 0x70:return 290; // DOM_VK_F1 -> GLFW_KEY_F1
304
case 0x71:return 291; // DOM_VK_F2 -> GLFW_KEY_F2
305
case 0x72:return 292; // DOM_VK_F3 -> GLFW_KEY_F3
306
case 0x73:return 293; // DOM_VK_F4 -> GLFW_KEY_F4
307
case 0x74:return 294; // DOM_VK_F5 -> GLFW_KEY_F5
308
case 0x75:return 295; // DOM_VK_F6 -> GLFW_KEY_F6
309
case 0x76:return 296; // DOM_VK_F7 -> GLFW_KEY_F7
310
case 0x77:return 297; // DOM_VK_F8 -> GLFW_KEY_F8
311
case 0x78:return 298; // DOM_VK_F9 -> GLFW_KEY_F9
312
case 0x79:return 299; // DOM_VK_F10 -> GLFW_KEY_F10
313
case 0x7A:return 300; // DOM_VK_F11 -> GLFW_KEY_F11
314
case 0x7B:return 301; // DOM_VK_F12 -> GLFW_KEY_F12
315
case 0x7C:return 302; // DOM_VK_F13 -> GLFW_KEY_F13
316
case 0x7D:return 303; // DOM_VK_F14 -> GLFW_KEY_F14
317
case 0x7E:return 304; // DOM_VK_F15 -> GLFW_KEY_F15
318
case 0x7F:return 305; // DOM_VK_F16 -> GLFW_KEY_F16
319
case 0x80:return 306; // DOM_VK_F17 -> GLFW_KEY_F17
320
case 0x81:return 307; // DOM_VK_F18 -> GLFW_KEY_F18
321
case 0x82:return 308; // DOM_VK_F19 -> GLFW_KEY_F19
322
case 0x83:return 309; // DOM_VK_F20 -> GLFW_KEY_F20
323
case 0x84:return 310; // DOM_VK_F21 -> GLFW_KEY_F21
324
case 0x85:return 311; // DOM_VK_F22 -> GLFW_KEY_F22
325
case 0x86:return 312; // DOM_VK_F23 -> GLFW_KEY_F23
326
case 0x87:return 313; // DOM_VK_F24 -> GLFW_KEY_F24
327
case 0x88:return 314; // 0x88 (not used?) -> GLFW_KEY_F25
328
case 0x60:return 320; // DOM_VK_NUMPAD0 -> GLFW_KEY_KP_0
329
case 0x61:return 321; // DOM_VK_NUMPAD1 -> GLFW_KEY_KP_1
330
case 0x62:return 322; // DOM_VK_NUMPAD2 -> GLFW_KEY_KP_2
331
case 0x63:return 323; // DOM_VK_NUMPAD3 -> GLFW_KEY_KP_3
332
case 0x64:return 324; // DOM_VK_NUMPAD4 -> GLFW_KEY_KP_4
333
case 0x65:return 325; // DOM_VK_NUMPAD5 -> GLFW_KEY_KP_5
334
case 0x66:return 326; // DOM_VK_NUMPAD6 -> GLFW_KEY_KP_6
335
case 0x67:return 327; // DOM_VK_NUMPAD7 -> GLFW_KEY_KP_7
336
case 0x68:return 328; // DOM_VK_NUMPAD8 -> GLFW_KEY_KP_8
337
case 0x69:return 329; // DOM_VK_NUMPAD9 -> GLFW_KEY_KP_9
338
case 0x6E:return 330; // DOM_VK_DECIMAL -> GLFW_KEY_KP_DECIMAL
339
case 0x6F:return 331; // DOM_VK_DIVIDE -> GLFW_KEY_KP_DIVIDE
340
case 0x6A:return 332; // DOM_VK_MULTIPLY -> GLFW_KEY_KP_MULTIPLY
341
case 0x6D:return 333; // DOM_VK_SUBTRACT -> GLFW_KEY_KP_SUBTRACT
342
case 0x6B:return 334; // DOM_VK_ADD -> GLFW_KEY_KP_ADD
343
// case 0x0D:return 335; // DOM_VK_RETURN -> GLFW_KEY_KP_ENTER (DOM_KEY_LOCATION_RIGHT)
344
// case 0x61:return 336; // DOM_VK_EQUALS -> GLFW_KEY_KP_EQUAL (DOM_KEY_LOCATION_RIGHT)
345
case 0x10:return 340; // DOM_VK_SHIFT -> GLFW_KEY_LEFT_SHIFT
346
case 0x11:return 341; // DOM_VK_CONTROL -> GLFW_KEY_LEFT_CONTROL
347
case 0x12:return 342; // DOM_VK_ALT -> GLFW_KEY_LEFT_ALT
348
case 0x5B:return 343; // DOM_VK_WIN -> GLFW_KEY_LEFT_SUPER
349
case 0xE0:return 343; // DOM_VK_META -> GLFW_KEY_LEFT_SUPER
350
// case 0x10:return 344; // DOM_VK_SHIFT -> GLFW_KEY_RIGHT_SHIFT (DOM_KEY_LOCATION_RIGHT)
351
// case 0x11:return 345; // DOM_VK_CONTROL -> GLFW_KEY_RIGHT_CONTROL (DOM_KEY_LOCATION_RIGHT)
352
// case 0x12:return 346; // DOM_VK_ALT -> GLFW_KEY_RIGHT_ALT (DOM_KEY_LOCATION_RIGHT)
353
// case 0x5B:return 347; // DOM_VK_WIN -> GLFW_KEY_RIGHT_SUPER (DOM_KEY_LOCATION_RIGHT)
354
case 0x5D:return 348; // DOM_VK_CONTEXT_MENU -> GLFW_KEY_MENU
355
// XXX: GLFW_KEY_WORLD_1, GLFW_KEY_WORLD_2 what are these?
356
#endif
357
default:return -1; // GLFW_KEY_UNKNOWN
358
};
359
},
360
361
getModBits: (win) => {
362
var mod = 0;
363
if (win.keys[340]) mod |= 0x0001; // GLFW_MOD_SHIFT
364
if (win.keys[341]) mod |= 0x0002; // GLFW_MOD_CONTROL
365
if (win.keys[342]) mod |= 0x0004; // GLFW_MOD_ALT
366
if (win.keys[343] || win.keys[348]) mod |= 0x0008; // GLFW_MOD_SUPER
367
// add caps and num lock keys? only if lock_key_mod is set
368
return mod;
369
},
370
371
onKeyPress: (event) => {
372
if (!GLFW.active || !GLFW.active.charFunc) return;
373
if (event.ctrlKey || event.metaKey) return;
374
375
// correct unicode charCode is only available with onKeyPress event
376
var charCode = event.charCode;
377
if (charCode == 0 || (charCode >= 0x00 && charCode <= 0x1F)) return;
378
379
#if USE_GLFW == 2
380
{{{ makeDynCall('vii', 'GLFW.active.charFunc') }}}(charCode, 1);
381
#endif
382
#if USE_GLFW == 3
383
{{{ makeDynCall('vpi', 'GLFW.active.charFunc') }}}(GLFW.active.id, charCode);
384
#endif
385
},
386
387
onKeyChanged: (keyCode, status) => {
388
if (!GLFW.active) return;
389
390
var key = GLFW.DOMToGLFWKeyCode(keyCode);
391
if (key == -1) return;
392
393
#if USE_GLFW == 3
394
var repeat = status && GLFW.active.keys[key];
395
#endif
396
GLFW.active.keys[key] = status;
397
GLFW.active.domKeys[keyCode] = status;
398
399
if (GLFW.active.keyFunc) {
400
#if USE_GLFW == 2
401
{{{ makeDynCall('vii', 'GLFW.active.keyFunc') }}}(key, status);
402
#endif
403
#if USE_GLFW == 3
404
if (repeat) status = 2; // GLFW_REPEAT
405
{{{ makeDynCall('vpiiii', 'GLFW.active.keyFunc') }}}(GLFW.active.id, key, keyCode, status, GLFW.getModBits(GLFW.active));
406
#endif
407
}
408
},
409
410
onGamepadConnected: (event) => {
411
GLFW.refreshJoysticks();
412
},
413
414
onGamepadDisconnected: (event) => {
415
GLFW.refreshJoysticks();
416
},
417
418
onKeydown: (event) => {
419
GLFW.onKeyChanged(event.keyCode, 1); // GLFW_PRESS or GLFW_REPEAT
420
421
// This logic comes directly from the sdl implementation. We cannot
422
// call preventDefault on all keydown events otherwise onKeyPress will
423
// not get called
424
if (event.key == 'Backspace' || event.key == 'Tab') {
425
event.preventDefault();
426
}
427
},
428
429
onKeyup: (event) => {
430
GLFW.onKeyChanged(event.keyCode, 0); // GLFW_RELEASE
431
},
432
433
onBlur: (event) => {
434
if (!GLFW.active) return;
435
436
for (var i = 0; i < GLFW.active.domKeys.length; ++i) {
437
if (GLFW.active.domKeys[i]) {
438
GLFW.onKeyChanged(i, 0); // GLFW_RELEASE
439
}
440
}
441
},
442
443
onMousemove: (event) => {
444
if (!GLFW.active) return;
445
446
if (event.type === 'touchmove') {
447
// Handling for touch events that are being converted to mouse input.
448
449
// Don't let the browser fire a duplicate mouse event.
450
event.preventDefault();
451
452
let primaryChanged = false;
453
for (let i of event.changedTouches) {
454
// If our chosen primary touch moved, update Browser mouse coords
455
if (GLFW.primaryTouchId === i.identifier) {
456
Browser.setMouseCoords(i.pageX, i.pageY);
457
primaryChanged = true;
458
break;
459
}
460
}
461
462
if (!primaryChanged) {
463
// Do not send mouse events if some touch other than the primary triggered this.
464
return;
465
}
466
467
} else {
468
// Handling for non-touch mouse input events.
469
Browser.calculateMouseEvent(event);
470
}
471
472
if (event.target != Browser.getCanvas() || !GLFW.active.cursorPosFunc) return;
473
474
if (GLFW.active.cursorPosFunc) {
475
#if USE_GLFW == 2
476
{{{ makeDynCall('vii', 'GLFW.active.cursorPosFunc') }}}(Browser.mouseX, Browser.mouseY);
477
#endif
478
#if USE_GLFW == 3
479
{{{ makeDynCall('vpdd', 'GLFW.active.cursorPosFunc') }}}(GLFW.active.id, Browser.mouseX, Browser.mouseY);
480
#endif
481
}
482
},
483
484
DOMToGLFWMouseButton: (event) => {
485
// DOM and glfw have different button codes.
486
// See http://www.w3schools.com/jsref/event_button.asp.
487
var eventButton = event['button'];
488
if (eventButton > 0) {
489
if (eventButton == 1) {
490
eventButton = 2;
491
} else {
492
eventButton = 1;
493
}
494
}
495
return eventButton;
496
},
497
498
onMouseenter: (event) => {
499
if (!GLFW.active) return;
500
501
if (event.target != Browser.getCanvas()) return;
502
503
#if USE_GLFW == 3
504
if (GLFW.active.cursorEnterFunc) {
505
{{{ makeDynCall('vpi', 'GLFW.active.cursorEnterFunc') }}}(GLFW.active.id, 1);
506
}
507
#endif
508
},
509
510
onMouseleave: (event) => {
511
if (!GLFW.active) return;
512
513
if (event.target != Browser.getCanvas()) return;
514
515
#if USE_GLFW == 3
516
if (GLFW.active.cursorEnterFunc) {
517
{{{ makeDynCall('vpi', 'GLFW.active.cursorEnterFunc') }}}(GLFW.active.id, 0);
518
}
519
#endif
520
},
521
522
onMouseButtonChanged: (event, status) => {
523
if (!GLFW.active) return;
524
525
if (event.target != Browser.getCanvas()) return;
526
527
// Is this from a touch event?
528
const isTouchType = event.type === 'touchstart' || event.type === 'touchend' || event.type === 'touchcancel';
529
530
// Only emulating mouse left-click behavior for touches.
531
let eventButton = 0;
532
if (isTouchType) {
533
// Handling for touch events that are being converted to mouse input.
534
535
// Don't let the browser fire a duplicate mouse event.
536
event.preventDefault();
537
538
let primaryChanged = false;
539
540
// Set a primary touch if we have none.
541
if (GLFW.primaryTouchId === null && event.type === 'touchstart' && event.targetTouches.length > 0) {
542
// Pick the first touch that started in the canvas and treat it as primary.
543
const chosenTouch = event.targetTouches[0];
544
GLFW.primaryTouchId = chosenTouch.identifier;
545
546
Browser.setMouseCoords(chosenTouch.pageX, chosenTouch.pageY);
547
primaryChanged = true;
548
} else if (event.type === 'touchend' || event.type === 'touchcancel') {
549
// Clear the primary touch if it ended.
550
for (let i of event.changedTouches) {
551
// If our chosen primary touch ended, remove it.
552
if (GLFW.primaryTouchId === i.identifier) {
553
GLFW.primaryTouchId = null;
554
primaryChanged = true;
555
break;
556
}
557
}
558
}
559
560
if (!primaryChanged) {
561
// Do not send mouse events if some touch other than the primary triggered this.
562
return;
563
}
564
565
} else {
566
// Handling for non-touch mouse input events.
567
Browser.calculateMouseEvent(event);
568
eventButton = GLFW.DOMToGLFWMouseButton(event);
569
}
570
571
if (status == 1) { // GLFW_PRESS
572
GLFW.active.buttons |= (1 << eventButton);
573
try {
574
event.target.setCapture();
575
} catch (e) {}
576
} else { // GLFW_RELEASE
577
GLFW.active.buttons &= ~(1 << eventButton);
578
}
579
580
// Send mouse event to GLFW.
581
if (GLFW.active.mouseButtonFunc) {
582
#if USE_GLFW == 2
583
{{{ makeDynCall('vii', 'GLFW.active.mouseButtonFunc') }}}(eventButton, status);
584
#endif
585
#if USE_GLFW == 3
586
{{{ makeDynCall('vpiii', 'GLFW.active.mouseButtonFunc') }}}(GLFW.active.id, eventButton, status, GLFW.getModBits(GLFW.active));
587
#endif
588
}
589
},
590
591
onMouseButtonDown: (event) => {
592
if (!GLFW.active) return;
593
GLFW.onMouseButtonChanged(event, 1); // GLFW_PRESS
594
},
595
596
onMouseButtonUp: (event) => {
597
if (!GLFW.active) return;
598
GLFW.onMouseButtonChanged(event, 0); // GLFW_RELEASE
599
},
600
601
onMouseWheel: (event) => {
602
// Note the minus sign that flips browser wheel direction (positive direction scrolls page down) to native wheel direction (positive direction is mouse wheel up)
603
var delta = -Browser.getMouseWheelDelta(event);
604
delta = (delta == 0) ? 0 : (delta > 0 ? Math.max(delta, 1) : Math.min(delta, -1)); // Quantize to integer so that minimum scroll is at least +/- 1.
605
GLFW.wheelPos += delta;
606
607
if (!GLFW.active || !GLFW.active.scrollFunc || event.target != Browser.getCanvas()) return;
608
#if USE_GLFW == 2
609
{{{ makeDynCall('vi', 'GLFW.active.scrollFunc') }}}(GLFW.wheelPos);
610
#endif
611
#if USE_GLFW == 3
612
var sx = 0;
613
var sy = delta;
614
if (event.type == 'mousewheel') {
615
sx = event.wheelDeltaX;
616
} else {
617
sx = event.deltaX;
618
}
619
620
{{{ makeDynCall('vpdd', 'GLFW.active.scrollFunc') }}}(GLFW.active.id, sx, sy);
621
#endif
622
623
event.preventDefault();
624
},
625
626
// width/height are the dimensions in screen coordinates the user interact with (ex: drawing, mouse coordinates...)
627
// framebufferWidth/framebufferHeight are the dimensions in pixel coordinates used for rendering
628
// in a HiDPI scenario framebufferWidth = devicePixelRatio * width
629
onCanvasResize: (width, height, framebufferWidth, framebufferHeight) => {
630
if (!GLFW.active) return;
631
632
var resizeNeeded = false;
633
634
// If the client is requesting fullscreen mode
635
if (getFullscreenElement()) {
636
if (!GLFW.active.fullscreen) {
637
resizeNeeded = width != screen.width || height != screen.height;
638
GLFW.active.storedX = GLFW.active.x;
639
GLFW.active.storedY = GLFW.active.y;
640
GLFW.active.storedWidth = GLFW.active.width;
641
GLFW.active.storedHeight = GLFW.active.height;
642
GLFW.active.x = GLFW.active.y = 0;
643
GLFW.active.width = screen.width;
644
GLFW.active.height = screen.height;
645
GLFW.active.fullscreen = true;
646
}
647
// If the client is reverting from fullscreen mode
648
} else if (GLFW.active.fullscreen == true) {
649
resizeNeeded = width != GLFW.active.storedWidth || height != GLFW.active.storedHeight;
650
GLFW.active.x = GLFW.active.storedX;
651
GLFW.active.y = GLFW.active.storedY;
652
GLFW.active.width = GLFW.active.storedWidth;
653
GLFW.active.height = GLFW.active.storedHeight;
654
GLFW.active.fullscreen = false;
655
}
656
657
if (resizeNeeded) {
658
// width or height is changed (fullscreen / exit fullscreen) which will call this listener back
659
// with proper framebufferWidth/framebufferHeight
660
Browser.setCanvasSize(GLFW.active.width, GLFW.active.height);
661
} else if (GLFW.active.width != width ||
662
GLFW.active.height != height ||
663
GLFW.active.framebufferWidth != framebufferWidth ||
664
GLFW.active.framebufferHeight != framebufferHeight) {
665
GLFW.active.width = width;
666
GLFW.active.height = height;
667
GLFW.active.framebufferWidth = framebufferWidth;
668
GLFW.active.framebufferHeight = framebufferHeight;
669
GLFW.onWindowSizeChanged();
670
GLFW.onFramebufferSizeChanged();
671
}
672
},
673
674
onWindowSizeChanged: () => {
675
if (!GLFW.active) return;
676
677
if (GLFW.active.windowSizeFunc) {
678
#if USE_GLFW == 2
679
{{{ makeDynCall('vii', 'GLFW.active.windowSizeFunc') }}}(GLFW.active.width, GLFW.active.height);
680
#endif
681
#if USE_GLFW == 3
682
{{{ makeDynCall('vpii', 'GLFW.active.windowSizeFunc') }}}(GLFW.active.id, GLFW.active.width, GLFW.active.height);
683
#endif
684
}
685
},
686
687
onFramebufferSizeChanged: () => {
688
if (!GLFW.active) return;
689
690
#if USE_GLFW == 3
691
if (GLFW.active.framebufferSizeFunc) {
692
{{{ makeDynCall('vpii', 'GLFW.active.framebufferSizeFunc') }}}(GLFW.active.id, GLFW.active.framebufferWidth, GLFW.active.framebufferHeight);
693
}
694
#endif
695
},
696
697
onWindowContentScaleChanged: (scale) => {
698
GLFW.scale = scale;
699
if (!GLFW.active) return;
700
701
#if USE_GLFW == 3
702
if (GLFW.active.windowContentScaleFunc) {
703
{{{ makeDynCall('vpff', 'GLFW.active.windowContentScaleFunc') }}}(GLFW.active.id, GLFW.scale, GLFW.scale);
704
}
705
#endif
706
},
707
708
getTime: () => _emscripten_get_now() / 1000,
709
710
/* GLFW2 wrapping */
711
712
setWindowTitle: (winid, title) => {
713
var win = GLFW.WindowFromId(winid);
714
if (!win) return;
715
716
win.title = title;
717
if (GLFW.active.id == win.id) {
718
_emscripten_set_window_title(title);
719
}
720
},
721
722
setJoystickCallback: (cbfun) => {
723
var prevcbfun = GLFW.joystickFunc;
724
GLFW.joystickFunc = cbfun;
725
GLFW.refreshJoysticks();
726
return prevcbfun;
727
},
728
729
joys: {}, // glfw joystick data
730
lastGamepadState: [],
731
lastGamepadStateFrame: null, // The integer value of MainLoop.currentFrameNumber of when the last gamepad state was produced.
732
733
refreshJoysticks: () => {
734
// Produce a new Gamepad API sample if we are ticking a new game frame, or if not using emscripten_set_main_loop() at all to drive animation.
735
if (MainLoop.currentFrameNumber !== GLFW.lastGamepadStateFrame || !MainLoop.currentFrameNumber) {
736
GLFW.lastGamepadState = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads || []);
737
GLFW.lastGamepadStateFrame = MainLoop.currentFrameNumber;
738
739
for (var joy = 0; joy < GLFW.lastGamepadState.length; ++joy) {
740
var gamepad = GLFW.lastGamepadState[joy];
741
742
if (gamepad) {
743
if (!GLFW.joys[joy]) {
744
out('glfw joystick connected:',joy);
745
GLFW.joys[joy] = {
746
id: stringToNewUTF8(gamepad.id),
747
buttonsCount: gamepad.buttons.length,
748
axesCount: gamepad.axes.length,
749
buttons: _malloc(gamepad.buttons.length),
750
axes: _malloc(gamepad.axes.length*4),
751
};
752
753
if (GLFW.joystickFunc) {
754
{{{ makeDynCall('vii', 'GLFW.joystickFunc') }}}(joy, 0x00040001); // GLFW_CONNECTED
755
}
756
}
757
758
var data = GLFW.joys[joy];
759
760
for (var i = 0; i < gamepad.buttons.length; ++i) {
761
{{{ makeSetValue('data.buttons + i', '0', 'gamepad.buttons[i].pressed', 'i8') }}};
762
}
763
764
for (var i = 0; i < gamepad.axes.length; ++i) {
765
{{{ makeSetValue('data.axes + i*4', '0', 'gamepad.axes[i]', 'float') }}};
766
}
767
} else {
768
if (GLFW.joys[joy]) {
769
out('glfw joystick disconnected',joy);
770
771
if (GLFW.joystickFunc) {
772
{{{ makeDynCall('vii', 'GLFW.joystickFunc') }}}(joy, 0x00040002); // GLFW_DISCONNECTED
773
}
774
775
_free(GLFW.joys[joy].id);
776
_free(GLFW.joys[joy].buttons);
777
_free(GLFW.joys[joy].axes);
778
779
delete GLFW.joys[joy];
780
}
781
}
782
}
783
}
784
},
785
786
setKeyCallback: (winid, cbfun) => {
787
var win = GLFW.WindowFromId(winid);
788
if (!win) return null;
789
var prevcbfun = win.keyFunc;
790
win.keyFunc = cbfun;
791
return prevcbfun;
792
},
793
794
setCharCallback: (winid, cbfun) => {
795
var win = GLFW.WindowFromId(winid);
796
if (!win) return null;
797
var prevcbfun = win.charFunc;
798
win.charFunc = cbfun;
799
return prevcbfun;
800
},
801
802
setMouseButtonCallback: (winid, cbfun) => {
803
var win = GLFW.WindowFromId(winid);
804
if (!win) return null;
805
var prevcbfun = win.mouseButtonFunc;
806
win.mouseButtonFunc = cbfun;
807
return prevcbfun;
808
},
809
810
setCursorPosCallback: (winid, cbfun) => {
811
var win = GLFW.WindowFromId(winid);
812
if (!win) return null;
813
var prevcbfun = win.cursorPosFunc;
814
win.cursorPosFunc = cbfun;
815
return prevcbfun;
816
},
817
818
setScrollCallback: (winid, cbfun) => {
819
var win = GLFW.WindowFromId(winid);
820
if (!win) return null;
821
var prevcbfun = win.scrollFunc;
822
win.scrollFunc = cbfun;
823
return prevcbfun;
824
},
825
826
setDropCallback: (winid, cbfun) => {
827
var win = GLFW.WindowFromId(winid);
828
if (!win) return null;
829
var prevcbfun = win.dropFunc;
830
win.dropFunc = cbfun;
831
return prevcbfun;
832
},
833
834
onDrop: (event) => {
835
if (!GLFW.active || !GLFW.active.dropFunc) return;
836
if (!event.dataTransfer || !event.dataTransfer.files || event.dataTransfer.files.length == 0) return;
837
838
event.preventDefault();
839
840
#if FILESYSTEM
841
var filenames = _malloc(event.dataTransfer.files.length * {{{ POINTER_SIZE }}});
842
var filenamesArray = [];
843
var count = event.dataTransfer.files.length;
844
845
// Read and save the files to emscripten's FS
846
var written = 0;
847
var drop_dir = '.glfw_dropped_files';
848
FS.createPath('/', drop_dir);
849
850
function save(file) {
851
var path = '/' + drop_dir + '/' + file.name.replace(/\//g, '_');
852
var reader = new FileReader();
853
reader.onloadend = (e) => {
854
if (reader.readyState != 2) { // not DONE
855
++written;
856
out('failed to read dropped file: '+file.name+': '+reader.error);
857
return;
858
}
859
860
var data = e.target.result;
861
FS.writeFile(path, new Uint8Array(data));
862
if (++written === count) {
863
{{{ makeDynCall('vpip', 'GLFW.active.dropFunc') }}}(GLFW.active.id, count, filenames);
864
865
for (var i = 0; i < filenamesArray.length; ++i) {
866
_free(filenamesArray[i]);
867
}
868
_free(filenames);
869
}
870
};
871
reader.readAsArrayBuffer(file);
872
873
var filename = stringToNewUTF8(path);
874
filenamesArray.push(filename);
875
{{{ makeSetValue('filenames', `i*${POINTER_SIZE}` , 'filename', '*') }}};
876
}
877
878
for (var i = 0; i < count; ++i) {
879
save(event.dataTransfer.files[i]);
880
}
881
#endif // FILESYSTEM
882
883
return false;
884
},
885
886
onDragover: (event) => {
887
if (!GLFW.active || !GLFW.active.dropFunc) return;
888
889
event.preventDefault();
890
return false;
891
},
892
893
setWindowSizeCallback: (winid, cbfun) => {
894
var win = GLFW.WindowFromId(winid);
895
if (!win) return null;
896
var prevcbfun = win.windowSizeFunc;
897
win.windowSizeFunc = cbfun;
898
899
#if USE_GLFW == 2
900
// As documented in GLFW2 API (http://www.glfw.org/GLFWReference27.pdf#page=22), when size
901
// callback function is set, it will be called with the current window size before this
902
// function returns.
903
// GLFW3 on the over hand doesn't have this behavior (https://github.com/glfw/glfw/issues/62).
904
if (!win.windowSizeFunc) return null;
905
{{{ makeDynCall('vii', 'win.windowSizeFunc') }}}(win.width, win.height);
906
#endif
907
908
return prevcbfun;
909
},
910
911
setWindowCloseCallback: (winid, cbfun) => {
912
var win = GLFW.WindowFromId(winid);
913
if (!win) return null;
914
var prevcbfun = win.windowCloseFunc;
915
win.windowCloseFunc = cbfun;
916
return prevcbfun;
917
},
918
919
setWindowRefreshCallback: (winid, cbfun) => {
920
var win = GLFW.WindowFromId(winid);
921
if (!win) return null;
922
var prevcbfun = win.windowRefreshFunc;
923
win.windowRefreshFunc = cbfun;
924
return prevcbfun;
925
},
926
927
onClickRequestPointerLock: (e) => {
928
var canvas = Browser.getCanvas();
929
if (!Browser.pointerLock && canvas.requestPointerLock) {
930
canvas.requestPointerLock();
931
e.preventDefault();
932
}
933
},
934
935
setInputMode: (winid, mode, value) => {
936
var win = GLFW.WindowFromId(winid);
937
if (!win) return;
938
939
switch (mode) {
940
case 0x00033001: { // GLFW_CURSOR
941
var canvas = Browser.getCanvas();
942
switch (value) {
943
case 0x00034001: { // GLFW_CURSOR_NORMAL
944
win.inputModes[mode] = value;
945
canvas.removeEventListener('click', GLFW.onClickRequestPointerLock, true);
946
document.exitPointerLock();
947
break;
948
}
949
case 0x00034002: { // GLFW_CURSOR_HIDDEN
950
err('glfwSetInputMode called with GLFW_CURSOR_HIDDEN value not implemented');
951
break;
952
}
953
case 0x00034003: { // GLFW_CURSOR_DISABLED
954
win.inputModes[mode] = value;
955
canvas.addEventListener('click', GLFW.onClickRequestPointerLock, true);
956
canvas.requestPointerLock();
957
break;
958
}
959
default: {
960
err(`glfwSetInputMode called with unknown value parameter value: ${value}`);
961
break;
962
}
963
}
964
break;
965
}
966
case 0x00033002: { // GLFW_STICKY_KEYS
967
err('glfwSetInputMode called with GLFW_STICKY_KEYS mode not implemented');
968
break;
969
}
970
case 0x00033003: { // GLFW_STICKY_MOUSE_BUTTONS
971
err('glfwSetInputMode called with GLFW_STICKY_MOUSE_BUTTONS mode not implemented');
972
break;
973
}
974
case 0x00033004: { // GLFW_LOCK_KEY_MODS
975
err('glfwSetInputMode called with GLFW_LOCK_KEY_MODS mode not implemented');
976
break;
977
}
978
case 0x000330005: { // GLFW_RAW_MOUSE_MOTION
979
err('glfwSetInputMode called with GLFW_RAW_MOUSE_MOTION mode not implemented');
980
break;
981
}
982
default: {
983
err(`glfwSetInputMode called with unknown mode parameter value: ${mode}`);
984
break;
985
}
986
}
987
},
988
989
getKey: (winid, key) => {
990
var win = GLFW.WindowFromId(winid);
991
if (!win) return 0;
992
return win.keys[key];
993
},
994
995
getMouseButton: (winid, button) => {
996
var win = GLFW.WindowFromId(winid);
997
if (!win) return 0;
998
return (win.buttons & (1 << button)) > 0;
999
},
1000
1001
getCursorPos: (winid, x, y) => {
1002
{{{ makeSetValue('x', '0', 'Browser.mouseX', 'double') }}};
1003
{{{ makeSetValue('y', '0', 'Browser.mouseY', 'double') }}};
1004
},
1005
1006
getMousePos: (winid, x, y) => {
1007
{{{ makeSetValue('x', '0', 'Browser.mouseX', 'i32') }}};
1008
{{{ makeSetValue('y', '0', 'Browser.mouseY', 'i32') }}};
1009
},
1010
1011
setCursorPos: (winid, x, y) => {
1012
},
1013
1014
getWindowPos: (winid, x, y) => {
1015
var wx = 0;
1016
var wy = 0;
1017
1018
var win = GLFW.WindowFromId(winid);
1019
if (win) {
1020
wx = win.x;
1021
wy = win.y;
1022
}
1023
1024
if (x) {
1025
{{{ makeSetValue('x', '0', 'wx', 'i32') }}};
1026
}
1027
1028
if (y) {
1029
{{{ makeSetValue('y', '0', 'wy', 'i32') }}};
1030
}
1031
},
1032
1033
setWindowPos: (winid, x, y) => {
1034
var win = GLFW.WindowFromId(winid);
1035
if (!win) return;
1036
win.x = x;
1037
win.y = y;
1038
},
1039
1040
getWindowSize: (winid, width, height) => {
1041
var ww = 0;
1042
var wh = 0;
1043
1044
var win = GLFW.WindowFromId(winid);
1045
if (win) {
1046
ww = win.width;
1047
wh = win.height;
1048
}
1049
1050
if (width) {
1051
{{{ makeSetValue('width', '0', 'ww', 'i32') }}};
1052
}
1053
1054
if (height) {
1055
{{{ makeSetValue('height', '0', 'wh', 'i32') }}};
1056
}
1057
},
1058
1059
setWindowSize: (winid, width, height) => {
1060
var win = GLFW.WindowFromId(winid);
1061
if (!win) return;
1062
1063
if (GLFW.active.id == win.id) {
1064
Browser.setCanvasSize(width, height); // triggers the listener (onCanvasResize) + windowSizeFunc
1065
}
1066
},
1067
1068
defaultWindowHints: () => {
1069
GLFW.hints = {...GLFW.defaultHints};
1070
},
1071
1072
createWindow: (width, height, title, monitor, share) => {
1073
var i, id;
1074
for (i = 0; i < GLFW.windows.length && GLFW.windows[i] !== null; i++) {
1075
// no-op
1076
}
1077
if (i > 0) throw "glfwCreateWindow only supports one window at time currently";
1078
1079
// id for window
1080
id = i + 1;
1081
1082
// not valid
1083
if (width <= 0 || height <= 0) return 0;
1084
1085
if (monitor) {
1086
Browser.requestFullscreen();
1087
} else {
1088
Browser.setCanvasSize(width, height);
1089
}
1090
1091
// Create context when there are no existing alive windows
1092
for (i = 0; i < GLFW.windows.length && GLFW.windows[i] == null; i++) {
1093
// no-op
1094
}
1095
1096
const canvas = Browser.getCanvas();
1097
1098
var useWebGL = GLFW.hints[0x00022001] > 0; // Use WebGL when we are told to based on GLFW_CLIENT_API
1099
if (i == GLFW.windows.length) {
1100
if (useWebGL) {
1101
var contextAttributes = {
1102
antialias: (GLFW.hints[0x0002100D] > 1), // GLFW_SAMPLES
1103
depth: (GLFW.hints[0x00021005] > 0), // GLFW_DEPTH_BITS
1104
stencil: (GLFW.hints[0x00021006] > 0), // GLFW_STENCIL_BITS
1105
alpha: (GLFW.hints[0x00021004] > 0) // GLFW_ALPHA_BITS
1106
}
1107
#if OFFSCREEN_FRAMEBUFFER
1108
// TODO: Make GLFW explicitly aware of whether it is being proxied or not, and set these to true only when proxying is being performed.
1109
GL.enableOffscreenFramebufferAttributes(contextAttributes);
1110
#endif
1111
Browser.createContext(canvas, /*useWebGL=*/true, /*setInModule=*/true, contextAttributes);
1112
} else {
1113
Browser.init();
1114
}
1115
}
1116
1117
// If context creation failed, do not return a valid window
1118
if (!Module['ctx'] && useWebGL) return 0;
1119
1120
// Initializes the framebuffer size from the canvas
1121
var win = new GLFW_Window(id, width, height, canvas.width, canvas.height, title, monitor, share);
1122
1123
// Set window to array
1124
if (id - 1 == GLFW.windows.length) {
1125
GLFW.windows.push(win);
1126
} else {
1127
GLFW.windows[id - 1] = win;
1128
}
1129
1130
GLFW.active = win;
1131
GLFW.adjustCanvasDimensions();
1132
return win.id;
1133
},
1134
1135
destroyWindow: (winid) => {
1136
var win = GLFW.WindowFromId(winid);
1137
if (!win) return;
1138
1139
#if USE_GLFW == 3
1140
if (win.windowCloseFunc) {
1141
{{{ makeDynCall('vp', 'win.windowCloseFunc') }}}(win.id);
1142
}
1143
#endif
1144
1145
GLFW.windows[win.id - 1] = null;
1146
if (GLFW.active.id == win.id) {
1147
GLFW.active = null;
1148
}
1149
1150
// Destroy context when no alive windows
1151
for (win of GLFW.windows) {
1152
if (win !== null) return;
1153
}
1154
1155
delete Module['ctx'];
1156
},
1157
1158
swapBuffers: (winid) => {
1159
},
1160
1161
// Overrides Browser.requestFullscreen to notify listeners even if Browser.resizeCanvas is false
1162
requestFullscreen(lockPointer, resizeCanvas) {
1163
Browser.lockPointer = lockPointer;
1164
Browser.resizeCanvas = resizeCanvas;
1165
if (typeof Browser.lockPointer == 'undefined') Browser.lockPointer = true;
1166
if (typeof Browser.resizeCanvas == 'undefined') Browser.resizeCanvas = false;
1167
1168
var canvas = Browser.getCanvas();
1169
function fullscreenChange() {
1170
Browser.isFullscreen = false;
1171
var canvasContainer = canvas.parentNode;
1172
if (getFullscreenElement() === canvasContainer) {
1173
canvas.exitFullscreen = Browser.exitFullscreen;
1174
if (Browser.lockPointer) canvas.requestPointerLock();
1175
Browser.isFullscreen = true;
1176
if (Browser.resizeCanvas) {
1177
Browser.setFullscreenCanvasSize();
1178
} else {
1179
Browser.updateCanvasDimensions(canvas);
1180
Browser.updateResizeListeners();
1181
}
1182
} else {
1183
// remove the full screen specific parent of the canvas again to restore the HTML structure from before going full screen
1184
canvasContainer.parentNode.insertBefore(canvas, canvasContainer);
1185
canvasContainer.parentNode.removeChild(canvasContainer);
1186
1187
if (Browser.resizeCanvas) {
1188
Browser.setWindowedCanvasSize();
1189
} else {
1190
Browser.updateCanvasDimensions(canvas);
1191
Browser.updateResizeListeners();
1192
}
1193
}
1194
Module['onFullScreen']?.(Browser.isFullscreen);
1195
Module['onFullscreen']?.(Browser.isFullscreen);
1196
}
1197
1198
if (!Browser.fullscreenHandlersInstalled) {
1199
Browser.fullscreenHandlersInstalled = true;
1200
document.addEventListener('fullscreenchange', fullscreenChange, false);
1201
document.addEventListener('mozfullscreenchange', fullscreenChange, false);
1202
document.addEventListener('webkitfullscreenchange', fullscreenChange, false);
1203
document.addEventListener('MSFullscreenChange', fullscreenChange, false);
1204
}
1205
1206
// create a new parent to ensure the canvas has no siblings. this allows browsers to optimize full screen performance when its parent is the full screen root
1207
var canvasContainer = document.createElement("div");
1208
canvas.parentNode.insertBefore(canvasContainer, canvas);
1209
canvasContainer.appendChild(canvas);
1210
1211
// use parent of canvas as full screen root to allow aspect ratio correction (Firefox stretches the root to screen size)
1212
canvasContainer.requestFullscreen = canvasContainer['requestFullscreen'] ||
1213
canvasContainer['mozRequestFullScreen'] ||
1214
canvasContainer['msRequestFullscreen'] ||
1215
(canvasContainer['webkitRequestFullscreen'] ? () => canvasContainer['webkitRequestFullscreen'](Element['ALLOW_KEYBOARD_INPUT']) : null) ||
1216
(canvasContainer['webkitRequestFullScreen'] ? () => canvasContainer['webkitRequestFullScreen'](Element['ALLOW_KEYBOARD_INPUT']) : null);
1217
1218
canvasContainer.requestFullscreen();
1219
},
1220
1221
// Overrides Browser.updateCanvasDimensions to account for hi dpi scaling
1222
updateCanvasDimensions(canvas, wNative, hNative) {
1223
const scale = GLFW.getHiDPIScale();
1224
1225
if (wNative && hNative) {
1226
canvas.widthNative = wNative;
1227
canvas.heightNative = hNative;
1228
} else {
1229
wNative = canvas.widthNative;
1230
hNative = canvas.heightNative;
1231
}
1232
var w = wNative;
1233
var h = hNative;
1234
if (Module['forcedAspectRatio'] && Module['forcedAspectRatio'] > 0) {
1235
if (w/h < Module['forcedAspectRatio']) {
1236
w = Math.round(h * Module['forcedAspectRatio']);
1237
} else {
1238
h = Math.round(w / Module['forcedAspectRatio']);
1239
}
1240
}
1241
if ((getFullscreenElement() === canvas.parentNode) && (typeof screen != 'undefined')) {
1242
var factor = Math.min(screen.width / w, screen.height / h);
1243
w = Math.round(w * factor);
1244
h = Math.round(h * factor);
1245
}
1246
if (Browser.resizeCanvas) {
1247
wNative = w;
1248
hNative = h;
1249
}
1250
const wNativeScaled = Math.floor(wNative * scale);
1251
const hNativeScaled = Math.floor(hNative * scale);
1252
if (canvas.width != wNativeScaled) canvas.width = wNativeScaled;
1253
if (canvas.height != hNativeScaled) canvas.height = hNativeScaled;
1254
if (typeof canvas.style != 'undefined') {
1255
if (!GLFW.isCSSScalingEnabled()) {
1256
canvas.style.setProperty( "width", wNative + "px", "important");
1257
canvas.style.setProperty("height", hNative + "px", "important");
1258
} else {
1259
canvas.style.removeProperty( "width");
1260
canvas.style.removeProperty("height");
1261
}
1262
}
1263
},
1264
1265
// Overrides Browser.calculateMouseCoords to account for HiDPI scaling and CSS scaling
1266
calculateMouseCoords(pageX, pageY) {
1267
// Calculate the movement based on the changes
1268
// in the coordinates.
1269
const rect = Browser.getCanvas().getBoundingClientRect();
1270
1271
// Neither .scrollX or .pageXOffset are defined in a spec, but
1272
// we prefer .scrollX because it is currently in a spec draft.
1273
// (see: http://www.w3.org/TR/2013/WD-cssom-view-20131217/)
1274
var scrollX = ((typeof window.scrollX != 'undefined') ? window.scrollX : window.pageXOffset);
1275
var scrollY = ((typeof window.scrollY != 'undefined') ? window.scrollY : window.pageYOffset);
1276
#if ASSERTIONS
1277
// If this assert lands, it's likely because the browser doesn't support scrollX or pageXOffset
1278
// and we have no viable fallback.
1279
assert((typeof scrollX != 'undefined') && (typeof scrollY != 'undefined'), 'Unable to retrieve scroll position, mouse positions likely broken.');
1280
#endif
1281
var adjustedX = pageX - (scrollX + rect.left);
1282
var adjustedY = pageY - (scrollY + rect.top);
1283
1284
// getBoundingClientRect() returns dimension affected by CSS, so as a result:
1285
// - when CSS scaling is enabled, this will fix the mouse coordinates to match the width/height of the window
1286
// - otherwise the CSS width/height are forced to the width/height of the GLFW window (see updateCanvasDimensions),
1287
// so there is no need to adjust the position
1288
if (GLFW.isCSSScalingEnabled() && GLFW.active) {
1289
adjustedX = adjustedX * (GLFW.active.width / rect.width);
1290
adjustedY = adjustedY * (GLFW.active.height / rect.height);
1291
}
1292
1293
return { x: adjustedX, y: adjustedY };
1294
},
1295
1296
setWindowAttrib: (winid, attrib, value) => {
1297
var win = GLFW.WindowFromId(winid);
1298
if (!win) return;
1299
const isHiDPIAware = GLFW.isHiDPIAware();
1300
win.attributes[attrib] = value;
1301
if (isHiDPIAware !== GLFW.isHiDPIAware())
1302
GLFW.adjustCanvasDimensions();
1303
},
1304
1305
getDevicePixelRatio() {
1306
return (typeof devicePixelRatio == 'number' && devicePixelRatio) || 1.0;
1307
},
1308
1309
isHiDPIAware() {
1310
if (GLFW.active)
1311
return GLFW.active.attributes[0x0002200C] > 0; // GLFW_SCALE_TO_MONITOR
1312
else
1313
return false;
1314
},
1315
1316
/**
1317
* CSS Scaling is a feature that is NOT part of the GLFW API, but for historical reasons, it is available
1318
* in Emscripten.
1319
* It is automatically disabled when using Hi DPI (the library overrides CSS sizes). */
1320
isCSSScalingEnabled() {
1321
return !GLFW.isHiDPIAware();
1322
},
1323
1324
adjustCanvasDimensions() {
1325
if (GLFW.active) {
1326
Browser.updateCanvasDimensions(Browser.getCanvas(), GLFW.active.width, GLFW.active.height);
1327
Browser.updateResizeListeners();
1328
}
1329
},
1330
1331
getHiDPIScale() {
1332
return GLFW.isHiDPIAware() ? GLFW.scale : 1.0;
1333
},
1334
1335
onDevicePixelRatioChange() {
1336
GLFW.onWindowContentScaleChanged(GLFW.getDevicePixelRatio());
1337
GLFW.adjustCanvasDimensions();
1338
},
1339
1340
GLFW2ParamToGLFW3Param: (param) => {
1341
var table = {
1342
0x00030001:0, // GLFW_MOUSE_CURSOR
1343
0x00030002:0, // GLFW_STICKY_KEYS
1344
0x00030003:0, // GLFW_STICKY_MOUSE_BUTTONS
1345
0x00030004:0, // GLFW_SYSTEM_KEYS
1346
0x00030005:0, // GLFW_KEY_REPEAT
1347
0x00030006:0, // GLFW_AUTO_POLL_EVENTS
1348
0x00020001:0, // GLFW_OPENED
1349
0x00020002:0, // GLFW_ACTIVE
1350
0x00020003:0, // GLFW_ICONIFIED
1351
0x00020004:0, // GLFW_ACCELERATED
1352
0x00020005:0x00021001, // GLFW_RED_BITS
1353
0x00020006:0x00021002, // GLFW_GREEN_BITS
1354
0x00020007:0x00021003, // GLFW_BLUE_BITS
1355
0x00020008:0x00021004, // GLFW_ALPHA_BITS
1356
0x00020009:0x00021005, // GLFW_DEPTH_BITS
1357
0x0002000A:0x00021006, // GLFW_STENCIL_BITS
1358
0x0002000B:0x0002100F, // GLFW_REFRESH_RATE
1359
0x0002000C:0x00021007, // GLFW_ACCUM_RED_BITS
1360
0x0002000D:0x00021008, // GLFW_ACCUM_GREEN_BITS
1361
0x0002000E:0x00021009, // GLFW_ACCUM_BLUE_BITS
1362
0x0002000F:0x0002100A, // GLFW_ACCUM_ALPHA_BITS
1363
0x00020010:0x0002100B, // GLFW_AUX_BUFFERS
1364
0x00020011:0x0002100C, // GLFW_STEREO
1365
0x00020012:0, // GLFW_WINDOW_NO_RESIZE
1366
0x00020013:0x0002100D, // GLFW_FSAA_SAMPLES
1367
0x00020014:0x00022002, // GLFW_OPENGL_VERSION_MAJOR
1368
0x00020015:0x00022003, // GLFW_OPENGL_VERSION_MINOR
1369
0x00020016:0x00022006, // GLFW_OPENGL_FORWARD_COMPAT
1370
0x00020017:0x00022007, // GLFW_OPENGL_DEBUG_CONTEXT
1371
0x00020018:0x00022008, // GLFW_OPENGL_PROFILE
1372
};
1373
return table[param];
1374
}
1375
},
1376
1377
/*******************************************************************************
1378
* GLFW FUNCTIONS
1379
******************************************************************************/
1380
glfwInit: () => {
1381
if (GLFW.windows) return 1; // GL_TRUE
1382
1383
GLFW.initialTime = GLFW.getTime();
1384
GLFW.defaultWindowHints();
1385
GLFW.windows = new Array()
1386
GLFW.active = null;
1387
GLFW.scale = GLFW.getDevicePixelRatio();
1388
1389
1390
window.addEventListener('gamepadconnected', GLFW.onGamepadConnected, true);
1391
window.addEventListener('gamepaddisconnected', GLFW.onGamepadDisconnected, true);
1392
window.addEventListener('keydown', GLFW.onKeydown, true);
1393
window.addEventListener('keypress', GLFW.onKeyPress, true);
1394
window.addEventListener('keyup', GLFW.onKeyup, true);
1395
window.addEventListener('blur', GLFW.onBlur, true);
1396
1397
// watch for devicePixelRatio changes
1398
GLFW.devicePixelRatioMQL = window.matchMedia('(resolution: ' + GLFW.getDevicePixelRatio() + 'dppx)');
1399
GLFW.devicePixelRatioMQL.addEventListener('change', GLFW.onDevicePixelRatioChange);
1400
1401
var canvas = Browser.getCanvas();
1402
canvas.addEventListener('touchmove', GLFW.onMousemove, true);
1403
canvas.addEventListener('touchstart', GLFW.onMouseButtonDown, true);
1404
canvas.addEventListener('touchcancel', GLFW.onMouseButtonUp, true);
1405
canvas.addEventListener('touchend', GLFW.onMouseButtonUp, true);
1406
canvas.addEventListener('mousemove', GLFW.onMousemove, true);
1407
canvas.addEventListener('mousedown', GLFW.onMouseButtonDown, true);
1408
canvas.addEventListener("mouseup", GLFW.onMouseButtonUp, true);
1409
canvas.addEventListener('wheel', GLFW.onMouseWheel, true);
1410
canvas.addEventListener('mousewheel', GLFW.onMouseWheel, true);
1411
canvas.addEventListener('mouseenter', GLFW.onMouseenter, true);
1412
canvas.addEventListener('mouseleave', GLFW.onMouseleave, true);
1413
canvas.addEventListener('drop', GLFW.onDrop, true);
1414
canvas.addEventListener('dragover', GLFW.onDragover, true);
1415
1416
// Overriding implementation to account for HiDPI
1417
Browser.requestFullscreen = GLFW.requestFullscreen;
1418
Browser.calculateMouseCoords = GLFW.calculateMouseCoords;
1419
Browser.updateCanvasDimensions = GLFW.updateCanvasDimensions;
1420
1421
Browser.resizeListeners.push((width, height) => {
1422
if (GLFW.isHiDPIAware()) {
1423
var canvas = Browser.getCanvas();
1424
GLFW.onCanvasResize(canvas.clientWidth, canvas.clientHeight, width, height);
1425
} else {
1426
GLFW.onCanvasResize(width, height, width, height);
1427
}
1428
});
1429
1430
return 1; // GL_TRUE
1431
},
1432
1433
glfwTerminate: () => {
1434
window.removeEventListener('gamepadconnected', GLFW.onGamepadConnected, true);
1435
window.removeEventListener('gamepaddisconnected', GLFW.onGamepadDisconnected, true);
1436
window.removeEventListener('keydown', GLFW.onKeydown, true);
1437
window.removeEventListener('keypress', GLFW.onKeyPress, true);
1438
window.removeEventListener('keyup', GLFW.onKeyup, true);
1439
window.removeEventListener('blur', GLFW.onBlur, true);
1440
var canvas = Browser.getCanvas();
1441
canvas.removeEventListener('touchmove', GLFW.onMousemove, true);
1442
canvas.removeEventListener('touchstart', GLFW.onMouseButtonDown, true);
1443
canvas.removeEventListener('touchcancel', GLFW.onMouseButtonUp, true);
1444
canvas.removeEventListener('touchend', GLFW.onMouseButtonUp, true);
1445
canvas.removeEventListener('mousemove', GLFW.onMousemove, true);
1446
canvas.removeEventListener('mousedown', GLFW.onMouseButtonDown, true);
1447
canvas.removeEventListener('mouseup', GLFW.onMouseButtonUp, true);
1448
canvas.removeEventListener('wheel', GLFW.onMouseWheel, true);
1449
canvas.removeEventListener('mousewheel', GLFW.onMouseWheel, true);
1450
canvas.removeEventListener('mouseenter', GLFW.onMouseenter, true);
1451
canvas.removeEventListener('mouseleave', GLFW.onMouseleave, true);
1452
canvas.removeEventListener('drop', GLFW.onDrop, true);
1453
canvas.removeEventListener('dragover', GLFW.onDragover, true);
1454
1455
if (GLFW.devicePixelRatioMQL)
1456
GLFW.devicePixelRatioMQL.removeEventListener('change', GLFW.onDevicePixelRatioChange);
1457
1458
canvas.width = canvas.height = 1;
1459
GLFW.windows = null;
1460
GLFW.active = null;
1461
},
1462
1463
glfwGetVersion: (major, minor, rev) => {
1464
#if USE_GLFW == 2
1465
{{{ makeSetValue('major', '0', '2', 'i32') }}};
1466
{{{ makeSetValue('minor', '0', '7', 'i32') }}};
1467
{{{ makeSetValue('rev', '0', '7', 'i32') }}};
1468
#endif
1469
1470
#if USE_GLFW == 3
1471
{{{ makeSetValue('major', '0', '3', 'i32') }}};
1472
{{{ makeSetValue('minor', '0', '2', 'i32') }}};
1473
{{{ makeSetValue('rev', '0', '1', 'i32') }}};
1474
#endif
1475
},
1476
1477
glfwPollEvents: () => 0,
1478
1479
glfwWaitEvents: () => 0,
1480
1481
glfwGetTime: () => GLFW.getTime() - GLFW.initialTime,
1482
1483
glfwSetTime: (time) => {
1484
GLFW.initialTime = GLFW.getTime() - time;
1485
},
1486
1487
glfwExtensionSupported__deps: ['glGetString', '$webglGetExtensions'],
1488
glfwExtensionSupported: (extension) => {
1489
GLFW.extensions ||= webglGetExtensions();
1490
1491
if (GLFW.extensions.includes(extension)) return 1;
1492
1493
// extensions from GLEmulations do not come unprefixed
1494
// so, try with prefix
1495
return (GLFW.extensions.includes("GL_" + extension));
1496
},
1497
1498
glfwSwapInterval__deps: ['emscripten_set_main_loop_timing'],
1499
glfwSwapInterval: (interval) => {
1500
interval = Math.abs(interval); // GLFW uses negative values to enable GLX_EXT_swap_control_tear, which we don't have, so just treat negative and positive the same.
1501
if (interval == 0) _emscripten_set_main_loop_timing({{{ cDefs.EM_TIMING_SETTIMEOUT }}}, 0);
1502
else _emscripten_set_main_loop_timing({{{ cDefs.EM_TIMING_RAF }}}, interval);
1503
},
1504
1505
#if USE_GLFW == 3
1506
glfwGetVersionString: () => {
1507
GLFW.versionString ||= stringToNewUTF8("3.2.1 JS WebGL Emscripten");
1508
return GLFW.versionString;
1509
},
1510
1511
glfwSetErrorCallback: (cbfun) => {
1512
var prevcbfun = GLFW.errorFunc;
1513
GLFW.errorFunc = cbfun;
1514
return prevcbfun;
1515
},
1516
1517
glfwWaitEventsTimeout: (timeout) => 0,
1518
1519
glfwPostEmptyEvent: () => 0,
1520
1521
glfwGetMonitors__deps: ['malloc'],
1522
glfwGetMonitors: (count) => {
1523
{{{ makeSetValue('count', '0', '1', 'i32') }}};
1524
if (!GLFW.monitors) {
1525
GLFW.monitors = _malloc({{{ POINTER_SIZE }}});
1526
{{{ makeSetValue('GLFW.monitors', '0', '1', 'i32') }}};
1527
}
1528
return GLFW.monitors;
1529
},
1530
1531
glfwGetPrimaryMonitor: () => 1,
1532
1533
glfwGetMonitorPos: (monitor, x, y) => {
1534
{{{ makeSetValue('x', '0', '0', 'i32') }}};
1535
{{{ makeSetValue('y', '0', '0', 'i32') }}};
1536
},
1537
1538
glfwGetMonitorWorkarea: (monitor, x, y, w, h) => {
1539
{{{ makeSetValue('x', '0', '0', 'i32') }}};
1540
{{{ makeSetValue('y', '0', '0', 'i32') }}};
1541
1542
{{{ makeSetValue('w', '0', 'screen.availWidth', 'i32') }}};
1543
{{{ makeSetValue('h', '0', 'screen.availHeight', 'i32') }}};
1544
},
1545
1546
glfwGetMonitorPhysicalSize: (monitor, width, height) => {
1547
// AFAIK there is no way to do this in javascript
1548
// Maybe with platform specific ccalls?
1549
//
1550
// Lets report 0 now which is wrong as it can get for end user.
1551
{{{ makeSetValue('width', '0', '0', 'i32') }}};
1552
{{{ makeSetValue('height', '0', '0', 'i32') }}};
1553
},
1554
1555
glfwGetMonitorContentScale: (monitor, x, y) => {
1556
{{{ makeSetValue('x', '0', 'GLFW.scale', 'float') }}};
1557
{{{ makeSetValue('y', '0', 'GLFW.scale', 'float') }}};
1558
},
1559
1560
glfwGetMonitorName: (mon) => {
1561
GLFW.monitorString ||= stringToNewUTF8("HTML5 WebGL Canvas");
1562
return GLFW.monitorString;
1563
},
1564
1565
glfwSetMonitorCallback: (cbfun) => {
1566
var prevcbfun = GLFW.monitorFunc;
1567
GLFW.monitorFunc = cbfun;
1568
return prevcbfun;
1569
},
1570
1571
// TODO: implement
1572
glfwGetVideoModes: (monitor, count) => {
1573
{{{ makeSetValue('count', '0', '0', 'i32') }}};
1574
return 0;
1575
},
1576
1577
// TODO: implement
1578
glfwGetVideoMode: (monitor) => 0,
1579
1580
// TODO: implement
1581
glfwSetGamma: (monitor, gamma) => 0,
1582
1583
glfwGetGammaRamp: (monitor) => { throw "glfwGetGammaRamp not implemented."; },
1584
1585
glfwSetGammaRamp: (monitor, ramp) => { throw "glfwSetGammaRamp not implemented."; },
1586
1587
glfwDefaultWindowHints: () => GLFW.defaultWindowHints(),
1588
1589
glfwWindowHint: (target, hint) => {
1590
GLFW.hints[target] = hint;
1591
},
1592
1593
glfwWindowHintString: (hint, value) => {
1594
// from glfw docs -> we just ignore this.
1595
// Some hints are platform specific. These may be set on any platform but they
1596
// will only affect their specific platform. Other platforms will ignore them.
1597
},
1598
1599
glfwCreateWindow: (width, height, title, monitor, share) => GLFW.createWindow(width, height, title, monitor, share),
1600
1601
glfwDestroyWindow: (winid) => GLFW.destroyWindow(winid),
1602
1603
glfwWindowShouldClose: (winid) => {
1604
var win = GLFW.WindowFromId(winid);
1605
if (!win) return 0;
1606
return win.shouldClose;
1607
},
1608
1609
glfwSetWindowShouldClose: (winid, value) => {
1610
var win = GLFW.WindowFromId(winid);
1611
if (!win) return;
1612
win.shouldClose = value;
1613
},
1614
1615
glfwSetWindowTitle: (winid, title) => GLFW.setWindowTitle(winid, title),
1616
1617
glfwGetWindowPos: (winid, x, y) => GLFW.getWindowPos(winid, x, y),
1618
1619
glfwSetWindowPos: (winid, x, y) => GLFW.setWindowPos(winid, x, y),
1620
1621
glfwGetWindowSize: (winid, width, height) => GLFW.getWindowSize(winid, width, height),
1622
1623
glfwSetWindowSize: (winid, width, height) => GLFW.setWindowSize(winid, width, height),
1624
1625
glfwGetFramebufferSize: (winid, width, height) => {
1626
var ww = 0;
1627
var wh = 0;
1628
1629
var win = GLFW.WindowFromId(winid);
1630
if (win) {
1631
ww = win.framebufferWidth;
1632
wh = win.framebufferHeight;
1633
}
1634
1635
if (width) {
1636
{{{ makeSetValue('width', '0', 'ww', 'i32') }}};
1637
}
1638
1639
if (height) {
1640
{{{ makeSetValue('height', '0', 'wh', 'i32') }}};
1641
}
1642
},
1643
1644
glfwGetWindowContentScale: (winid, x, y) => {
1645
// winid doesn't matter. all windows will use same scale anyway.
1646
// hope i used this makeSetValue correctly
1647
{{{ makeSetValue('x', '0', 'GLFW.scale', 'float') }}};
1648
{{{ makeSetValue('y', '0', 'GLFW.scale', 'float') }}};
1649
},
1650
1651
glfwGetWindowOpacity: (winid) => 1.0,
1652
1653
glfwSetWindowOpacity: (winid, opacity) => { /* error */ },
1654
1655
glfwIconifyWindow: (winid) => {
1656
#if ASSERTIONS
1657
warnOnce('glfwIconifyWindow is not implemented');
1658
#endif
1659
},
1660
1661
glfwRestoreWindow: (winid) => {
1662
#if ASSERTIONS
1663
warnOnce('glfwRestoreWindow is not implemented');
1664
#endif
1665
},
1666
1667
glfwShowWindow: (winid) => 0,
1668
1669
glfwHideWindow: (winid) => 0,
1670
1671
glfwGetWindowMonitor: (winid) => {
1672
var win = GLFW.WindowFromId(winid);
1673
if (!win) return 0;
1674
return win.monitor;
1675
},
1676
1677
glfwGetWindowAttrib: (winid, attrib) => {
1678
var win = GLFW.WindowFromId(winid);
1679
if (!win) return 0;
1680
return win.attributes[attrib];
1681
},
1682
1683
glfwSetWindowAttrib: (winid, attrib, value) => GLFW.setWindowAttrib(winid, attrib, value),
1684
1685
glfwSetWindowUserPointer: (winid, ptr) => {
1686
var win = GLFW.WindowFromId(winid);
1687
if (!win) return;
1688
win.userptr = ptr;
1689
},
1690
1691
glfwGetWindowUserPointer: (winid) => {
1692
var win = GLFW.WindowFromId(winid);
1693
if (!win) return 0;
1694
return win.userptr;
1695
},
1696
1697
glfwSetWindowPosCallback: (winid, cbfun) => {
1698
var win = GLFW.WindowFromId(winid);
1699
if (!win) return null;
1700
var prevcbfun = win.windowPosFunc;
1701
win.windowPosFunc = cbfun;
1702
return prevcbfun;
1703
},
1704
1705
glfwSetWindowSizeCallback: (winid, cbfun) => GLFW.setWindowSizeCallback(winid, cbfun),
1706
1707
glfwSetWindowCloseCallback: (winid, cbfun) => GLFW.setWindowCloseCallback(winid, cbfun),
1708
1709
glfwSetWindowRefreshCallback: (winid, cbfun) => GLFW.setWindowRefreshCallback(winid, cbfun),
1710
1711
glfwSetWindowFocusCallback: (winid, cbfun) => {
1712
var win = GLFW.WindowFromId(winid);
1713
if (!win) return null;
1714
var prevcbfun = win.windowFocusFunc;
1715
win.windowFocusFunc = cbfun;
1716
return prevcbfun;
1717
},
1718
1719
glfwSetWindowIconifyCallback: (winid, cbfun) => {
1720
var win = GLFW.WindowFromId(winid);
1721
if (!win) return null;
1722
var prevcbfun = win.windowIconifyFunc;
1723
win.windowIconifyFunc = cbfun;
1724
return prevcbfun;
1725
},
1726
1727
glfwSetWindowMaximizeCallback: (winid, cbfun) => {
1728
var win = GLFW.WindowFromId(winid);
1729
if (!win) return null;
1730
var prevcbfun = win.windowMaximizeFunc;
1731
win.windowMaximizeFunc = cbfun;
1732
return prevcbfun;
1733
},
1734
1735
glfwSetWindowIcon: (winid, count, images) => 0,
1736
1737
glfwSetWindowSizeLimits: (winid, minwidth, minheight, maxwidth, maxheight) => 0,
1738
1739
glfwSetWindowAspectRatio: (winid, numer, denom) => 0,
1740
1741
glfwGetWindowFrameSize: (winid, left, top, right, bottom) => { throw "glfwGetWindowFrameSize not implemented."; },
1742
1743
glfwMaximizeWindow: (winid) => 0,
1744
1745
glfwFocusWindow: (winid) => 0,
1746
1747
glfwRequestWindowAttention: (winid) => 0, // maybe do window.focus()?
1748
1749
glfwSetWindowMonitor: (winid, monitor, xpos, ypos, width, height, refreshRate) => { throw "glfwSetWindowMonitor not implemented."; },
1750
1751
glfwCreateCursor: (image, xhot, yhot) => 0,
1752
1753
glfwCreateStandardCursor: (shape) => 0,
1754
1755
glfwDestroyCursor: (cursor) => 0,
1756
1757
glfwSetCursor: (winid, cursor) => 0,
1758
1759
glfwSetFramebufferSizeCallback: (winid, cbfun) => {
1760
var win = GLFW.WindowFromId(winid);
1761
if (!win) return null;
1762
var prevcbfun = win.framebufferSizeFunc;
1763
win.framebufferSizeFunc = cbfun;
1764
return prevcbfun;
1765
},
1766
1767
glfwSetWindowContentScaleCallback: (winid, cbfun) => {
1768
var win = GLFW.WindowFromId(winid);
1769
if (!win) return null;
1770
var prevcbfun = win.windowContentScaleFunc;
1771
win.windowContentScaleFunc = cbfun;
1772
return prevcbfun;
1773
},
1774
1775
glfwGetInputMode: (winid, mode) => {
1776
var win = GLFW.WindowFromId(winid);
1777
if (!win) return;
1778
1779
switch (mode) {
1780
case 0x00033001: { // GLFW_CURSOR
1781
if (Browser.pointerLock) {
1782
win.inputModes[mode] = 0x00034003; // GLFW_CURSOR_DISABLED
1783
} else {
1784
win.inputModes[mode] = 0x00034001; // GLFW_CURSOR_NORMAL
1785
}
1786
}
1787
}
1788
1789
return win.inputModes[mode];
1790
},
1791
1792
glfwSetInputMode: (winid, mode, value) => {
1793
GLFW.setInputMode(winid, mode, value);
1794
},
1795
1796
glfwRawMouseMotionSupported: () => 0,
1797
1798
glfwGetKey: (winid, key) => GLFW.getKey(winid, key),
1799
1800
glfwGetKeyName: (key, scancode) => { throw "glfwGetKeyName not implemented."; },
1801
1802
glfwGetKeyScancode: (key) => { throw "glfwGetKeyScancode not implemented."; },
1803
1804
glfwGetMouseButton: (winid, button) => GLFW.getMouseButton(winid, button),
1805
1806
glfwGetCursorPos: (winid, x, y) => GLFW.getCursorPos(winid, x, y),
1807
1808
// I believe it is not possible to move the mouse with javascript
1809
glfwSetCursorPos: (winid, x, y) => GLFW.setCursorPos(winid, x, y),
1810
1811
glfwSetKeyCallback: (winid, cbfun) => GLFW.setKeyCallback(winid, cbfun),
1812
1813
glfwSetCharCallback: (winid, cbfun) => GLFW.setCharCallback(winid, cbfun),
1814
1815
glfwSetCharModsCallback: (winid, cbfun) => { throw "glfwSetCharModsCallback not implemented."; },
1816
1817
glfwSetMouseButtonCallback: (winid, cbfun) => GLFW.setMouseButtonCallback(winid, cbfun),
1818
1819
glfwSetCursorPosCallback: (winid, cbfun) => GLFW.setCursorPosCallback(winid, cbfun),
1820
1821
glfwSetCursorEnterCallback: (winid, cbfun) => {
1822
var win = GLFW.WindowFromId(winid);
1823
if (!win) return null;
1824
var prevcbfun = win.cursorEnterFunc;
1825
win.cursorEnterFunc = cbfun;
1826
return prevcbfun;
1827
},
1828
1829
glfwSetScrollCallback: (winid, cbfun) => GLFW.setScrollCallback(winid, cbfun),
1830
1831
glfwVulkanSupported: () => 0,
1832
1833
glfwSetDropCallback: (winid, cbfun) => GLFW.setDropCallback(winid, cbfun),
1834
1835
glfwGetTimerValue: () => { throw "glfwGetTimerValue is not implemented."; },
1836
1837
glfwGetTimerFrequency: () => { throw "glfwGetTimerFrequency is not implemented."; },
1838
1839
glfwGetRequiredInstanceExtensions: (count) => { throw "glfwGetRequiredInstanceExtensions is not implemented."; },
1840
1841
glfwJoystickPresent: (joy) => {
1842
GLFW.refreshJoysticks();
1843
1844
return GLFW.joys[joy] !== undefined;
1845
},
1846
1847
glfwGetJoystickAxes: (joy, count) => {
1848
GLFW.refreshJoysticks();
1849
1850
var state = GLFW.joys[joy];
1851
if (!state || !state.axes) {
1852
{{{ makeSetValue('count', '0', '0', 'i32') }}};
1853
return;
1854
}
1855
1856
{{{ makeSetValue('count', '0', 'state.axesCount', 'i32') }}};
1857
return state.axes;
1858
},
1859
1860
glfwGetJoystickButtons: (joy, count) => {
1861
GLFW.refreshJoysticks();
1862
1863
var state = GLFW.joys[joy];
1864
if (!state || !state.buttons) {
1865
{{{ makeSetValue('count', '0', '0', 'i32') }}};
1866
return;
1867
}
1868
1869
{{{ makeSetValue('count', '0', 'state.buttonsCount', 'i32') }}};
1870
return state.buttons;
1871
},
1872
1873
glfwGetJoystickHats: (joy, count) => { throw "glfwGetJoystickHats is not implemented"; },
1874
1875
glfwGetJoystickName: (joy) => {
1876
if (GLFW.joys[joy]) {
1877
return GLFW.joys[joy].id;
1878
}
1879
return 0;
1880
},
1881
1882
glfwGetJoystickGUID: (jid) => { throw "glfwGetJoystickGUID not implemented"; },
1883
1884
glfwSetJoystickUserPointer: (jid, ptr) => { throw "glfwSetJoystickUserPointer not implemented"; },
1885
1886
glfwGetJoystickUserPointer: (jid) => { throw "glfwGetJoystickUserPointer not implemented"; },
1887
1888
glfwJoystickIsGamepad: (jid) => { throw "glfwJoystickIsGamepad not implemented"; },
1889
1890
glfwSetJoystickCallback: (cbfun) => GLFW.setJoystickCallback(cbfun),
1891
1892
glfwSetClipboardString: (win, string) => 0,
1893
1894
glfwGetClipboardString: (win) => 0,
1895
1896
glfwMakeContextCurrent: (winid) => 0,
1897
1898
glfwGetCurrentContext: () => GLFW.active ? GLFW.active.id : 0,
1899
1900
glfwSwapBuffers: (winid) => GLFW.swapBuffers(winid),
1901
1902
#elif USE_GLFW == 2
1903
glfwOpenWindow: (width, height, redbits, greenbits, bluebits, alphabits, depthbits, stencilbits, mode) => {
1904
GLFW.hints[0x00021001] = redbits; // GLFW_RED_BITS
1905
GLFW.hints[0x00021002] = greenbits; // GLFW_GREEN_BITS
1906
GLFW.hints[0x00021003] = bluebits; // GLFW_BLUE_BITS
1907
GLFW.hints[0x00021004] = alphabits; // GLFW_ALPHA_BITS
1908
GLFW.hints[0x00021005] = depthbits; // GLFW_DEPTH_BITS
1909
GLFW.hints[0x00021006] = stencilbits; // GLFW_STENCIL_BITS
1910
GLFW.createWindow(width, height, "GLFW2 Window", 0, 0);
1911
return 1; // GL_TRUE
1912
},
1913
1914
glfwCloseWindow: () => GLFW.destroyWindow(GLFW.active.id),
1915
1916
glfwOpenWindowHint: (target, hint) => {
1917
target = GLFW.GLFW2ParamToGLFW3Param(target);
1918
GLFW.hints[target] = hint;
1919
},
1920
1921
glfwGetWindowSize_v2: (width, height) => GLFW.getWindowSize(GLFW.active.id, width, height),
1922
1923
glfwSetWindowSize_v2: (width, height) => GLFW.setWindowSize(GLFW.active.id, width, height),
1924
1925
glfwSetWindowPos_v2: (x, y) => GLFW.setWindowPos(GLFW.active.id, x, y),
1926
1927
glfwSetWindowTitle_v2: (title) => GLFW.setWindowTitle(GLFW.active.id, title),
1928
1929
glfwIconifyWindow_v2: () => {
1930
#if ASSERTIONS
1931
warnOnce('glfwIconifyWindow is not implemented');
1932
#endif
1933
},
1934
1935
glfwRestoreWindow_v2: () => {
1936
#if ASSERTIONS
1937
warnOnce('glfwRestoreWindow is not implemented');
1938
#endif
1939
},
1940
1941
glfwSwapBuffers_v2: () => GLFW.swapBuffers(GLFW.active.id),
1942
1943
glfwGetWindowParam: (param) => {
1944
param = GLFW.GLFW2ParamToGLFW3Param(param);
1945
return GLFW.hints[param];
1946
},
1947
1948
glfwSetWindowSizeCallback_v2: (cbfun) => {
1949
GLFW.setWindowSizeCallback(GLFW.active.id, cbfun);
1950
},
1951
1952
glfwSetWindowCloseCallback_v2: (cbfun) => {
1953
GLFW.setWindowCloseCallback(GLFW.active.id, cbfun);
1954
},
1955
1956
glfwSetWindowRefreshCallback_v2: (cbfun) => GLFW.setWindowRefreshCallback(GLFW.active.id, cbfun),
1957
1958
glfwGetKey_v2: (key) => GLFW.getKey(GLFW.active.id, key),
1959
1960
glfwGetMouseButton_v2: (button) => GLFW.getMouseButton(GLFW.active.id, button),
1961
1962
glfwGetMousePos: (x, y) => {
1963
GLFW.getMousePos(GLFW.active.id, x, y);
1964
},
1965
1966
glfwSetMousePos: (x, y) => {
1967
GLFW.setCursorPos(GLFW.active.id, x, y);
1968
},
1969
1970
glfwGetMouseWheel: () => 0,
1971
1972
glfwSetMouseWheel: (pos) => 0,
1973
1974
glfwSetKeyCallback_v2: (cbfun) => {
1975
GLFW.setKeyCallback(GLFW.active.id, cbfun);
1976
},
1977
1978
glfwSetCharCallback_v2: (cbfun) => {
1979
GLFW.setCharCallback(GLFW.active.id, cbfun);
1980
},
1981
1982
glfwSetMouseButtonCallback_v2: (cbfun) => {
1983
GLFW.setMouseButtonCallback(GLFW.active.id, cbfun);
1984
},
1985
1986
glfwSetMousePosCallback: (cbfun) => {
1987
GLFW.setCursorPosCallback(GLFW.active.id, cbfun);
1988
},
1989
1990
glfwSetMouseWheelCallback: (cbfun) => {
1991
GLFW.setScrollCallback(GLFW.active.id, cbfun);
1992
},
1993
1994
glfwGetDesktopMode: (mode) => { throw "glfwGetDesktopMode is not implemented."; },
1995
1996
glfwSleep__deps: ['sleep'],
1997
glfwSleep: (time) => _sleep(time),
1998
1999
glfwEnable: (target) => {
2000
target = GLFW.GLFW2ParamToGLFW3Param(target);
2001
GLFW.hints[target] = false;
2002
},
2003
2004
glfwDisable: (target) => {
2005
target = GLFW.GLFW2ParamToGLFW3Param(target);
2006
GLFW.hints[target] = true;
2007
},
2008
2009
glfwGetGLVersion: (major, minor, rev) => {
2010
{{{ makeSetValue('major', '0', '0', 'i32') }}};
2011
{{{ makeSetValue('minor', '0', '0', 'i32') }}};
2012
{{{ makeSetValue('rev', '0', '1', 'i32') }}};
2013
},
2014
2015
glfwCreateThread: (fun, arg) => {
2016
{{{ makeDynCall('vp', 'fun') }}}(arg);
2017
// One single thread
2018
return 0;
2019
},
2020
2021
glfwDestroyThread: (ID) => 0,
2022
2023
glfwWaitThread: (ID, waitmode) => 0,
2024
2025
// One single thread
2026
glfwGetThreadID: () => 0,
2027
2028
glfwCreateMutex: () => { throw "glfwCreateMutex is not implemented."; },
2029
2030
glfwDestroyMutex: (mutex) => { throw "glfwDestroyMutex is not implemented."; },
2031
2032
glfwLockMutex: (mutex) => { throw "glfwLockMutex is not implemented."; },
2033
2034
glfwUnlockMutex: (mutex) => { throw "glfwUnlockMutex is not implemented."; },
2035
2036
glfwCreateCond: () => { throw "glfwCreateCond is not implemented."; },
2037
2038
glfwDestroyCond: (cond) => { throw "glfwDestroyCond is not implemented."; },
2039
2040
glfwWaitCond: (cond, mutex, timeout) => { throw "glfwWaitCond is not implemented."; },
2041
2042
glfwSignalCond: (cond) => { throw "glfwSignalCond is not implemented."; },
2043
2044
glfwBroadcastCond: (cond) => { throw "glfwBroadcastCond is not implemented."; },
2045
2046
glfwGetNumberOfProcessors: () => 1, // Threads are disabled anyway…
2047
2048
glfwReadImage: (name, img, flags) => { throw "glfwReadImage is not implemented."; },
2049
2050
glfwReadMemoryImage: (data, size, img, flags) => { throw "glfwReadMemoryImage is not implemented."; },
2051
2052
glfwFreeImage: (img) => { throw "glfwFreeImage is not implemented."; },
2053
2054
glfwLoadTexture2D: (name, flags) => { throw "glfwLoadTexture2D is not implemented."; },
2055
2056
glfwLoadMemoryTexture2D: (data, size, flags) => { throw "glfwLoadMemoryTexture2D is not implemented."; },
2057
2058
glfwLoadTextureImage2D: (img, flags) => { throw "glfwLoadTextureImage2D is not implemented."; },
2059
#endif // GLFW2
2060
};
2061
2062
autoAddDeps(LibraryGLFW, '$GLFW');
2063
addToLibrary(LibraryGLFW);
2064
2065