Mercurial
comparison mrjunejune/src/public/raylib.js @ 100:65e5a5b89a4e
[Seobeo] Migrated everything to this page.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Sat, 03 Jan 2026 07:48:07 -0800 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 99:684edfaf93b7 | 100:65e5a5b89a4e |
|---|---|
| 1 /** | |
| 2 * I Stole this from https://github.com/tsoding/c3-demo/blob/main/raylib.js and added few functions for functionality. | |
| 3 * It seems to be from raylib.com/examples | |
| 4 */ | |
| 5 function make_environment(env) { | |
| 6 return new Proxy(env, { | |
| 7 get(_target, prop, _receiver) { | |
| 8 if (env[prop] !== undefined) { | |
| 9 return env[prop].bind(env); | |
| 10 } | |
| 11 return (...args) => { | |
| 12 throw new Error(`NOT IMPLEMENTED: ${prop} ${args}`); | |
| 13 }; | |
| 14 }, | |
| 15 }); | |
| 16 } | |
| 17 | |
| 18 let iota = 0; | |
| 19 const LOG_ALL = iota++; // Display all logs | |
| 20 const LOG_TRACE = iota++; // Trace logging, intended for internal use only | |
| 21 const LOG_DEBUG = iota++; // Debug logging, used for internal debugging, it should be disabled on release builds | |
| 22 const LOG_INFO = iota++; // Info logging, used for program execution info | |
| 23 const LOG_WARNING = iota++; // Warning logging, used on recoverable failures | |
| 24 const LOG_ERROR = iota++; // Error logging, used on unrecoverable failures | |
| 25 const LOG_FATAL = iota++; // Fatal logging, used to abort program: exit(EXIT_FAILURE) | |
| 26 const LOG_NONE = iota++; // Disable logging | |
| 27 | |
| 28 class RaylibJs { | |
| 29 #FONT_SCALE_MAGIC = 0.65; | |
| 30 | |
| 31 #reset() { | |
| 32 this.previous = undefined; | |
| 33 this.wasm = undefined; | |
| 34 this.ctx = undefined; | |
| 35 this.dt = undefined; | |
| 36 this.targetFPS = 60; | |
| 37 this.entryFunction = undefined; | |
| 38 this.prevPressedKeyState = new Set(); | |
| 39 this.currentPressedKeyState = new Set(); | |
| 40 this.currentMouseWheelMoveState = 0; | |
| 41 /* -1 since 0 is left click, 1 middle, 2 is right click */ | |
| 42 this.currentIsMouseButtonPressed = -1; | |
| 43 this.currentMousePosition = { x: 0, y: 0 }; | |
| 44 this.images = []; | |
| 45 this.quit = false; | |
| 46 } | |
| 47 | |
| 48 constructor() { | |
| 49 this.#reset(); | |
| 50 } | |
| 51 | |
| 52 stop() { | |
| 53 this.quit = true; | |
| 54 } | |
| 55 | |
| 56 async start({ wasmPath, canvasId }) { | |
| 57 if (this.wasm !== undefined) { | |
| 58 console.error("The game is already running. Please stop() it first."); | |
| 59 return; | |
| 60 } | |
| 61 | |
| 62 const canvas = document.getElementById(canvasId); | |
| 63 this.ctx = canvas.getContext("2d"); | |
| 64 if (this.ctx === null) { | |
| 65 throw new Error("Could not create 2d canvas context"); | |
| 66 } | |
| 67 | |
| 68 this.wasm = await WebAssembly.instantiateStreaming(fetch(wasmPath), { | |
| 69 env: make_environment(this), | |
| 70 }); | |
| 71 | |
| 72 const keyDown = (e) => { | |
| 73 this.currentPressedKeyState.add(glfwKeyMapping[e.code]); | |
| 74 }; | |
| 75 const keyUp = (e) => { | |
| 76 this.currentPressedKeyState.delete(glfwKeyMapping[e.code]); | |
| 77 }; | |
| 78 const wheelMove = (e) => { | |
| 79 this.currentMouseWheelMoveState = Math.sign(-e.deltaY); | |
| 80 }; | |
| 81 const mouseMove = (e) => { | |
| 82 this.currentMousePosition = { x: e.clientX, y: e.clientY }; | |
| 83 }; | |
| 84 const mouseClick = (e) => { | |
| 85 this.currentIsMouseButtonPressed = e.button; | |
| 86 }; | |
| 87 const touchStarted = () => { | |
| 88 this.currentIsMouseButtonPressed = 0; | |
| 89 }; | |
| 90 const touchOrClickEnded = () => { | |
| 91 this.currentIsMouseButtonPressed = -1; | |
| 92 }; | |
| 93 window.addEventListener("keydown", keyDown); | |
| 94 window.addEventListener("keyup", keyUp); | |
| 95 window.addEventListener("wheel", wheelMove); | |
| 96 window.addEventListener("mousemove", mouseMove); | |
| 97 window.addEventListener("mousedown", mouseClick); | |
| 98 window.addEventListener("mouseup", touchOrClickEnded); | |
| 99 | |
| 100 /* For phones */ | |
| 101 window.addEventListener("touchstart", touchStarted); | |
| 102 window.addEventListener("touchend", touchOrClickEnded); | |
| 103 window.addEventListener("touchcancel", touchOrClickEnded); | |
| 104 | |
| 105 this.wasm.instance.exports.main(); | |
| 106 const next = (timestamp) => { | |
| 107 if (this.quit) { | |
| 108 this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); | |
| 109 window.removeEventListener("keydown", keyDown); | |
| 110 this.#reset(); | |
| 111 return; | |
| 112 } | |
| 113 this.dt = (timestamp - this.previous) / 1000.0; | |
| 114 this.previous = timestamp; | |
| 115 this.entryFunction(); | |
| 116 window.requestAnimationFrame(next); | |
| 117 }; | |
| 118 window.requestAnimationFrame((timestamp) => { | |
| 119 this.previous = timestamp; | |
| 120 window.requestAnimationFrame(next); | |
| 121 }); | |
| 122 } | |
| 123 | |
| 124 InitWindow(width, height, title_ptr) { | |
| 125 this.ctx.canvas.width = width; | |
| 126 this.ctx.canvas.height = height; | |
| 127 const buffer = this.wasm.instance.exports.memory.buffer; | |
| 128 document.title = cstr_by_ptr(buffer, title_ptr); | |
| 129 } | |
| 130 | |
| 131 WindowShouldClose() { | |
| 132 return false; | |
| 133 } | |
| 134 | |
| 135 SetTargetFPS(fps) { | |
| 136 console.log( | |
| 137 `The game wants to run at ${fps} FPS, but in Web we gonna just ignore it.`, | |
| 138 ); | |
| 139 this.targetFPS = fps; | |
| 140 } | |
| 141 | |
| 142 GetScreenWidth() { | |
| 143 return this.ctx.canvas.width; | |
| 144 } | |
| 145 | |
| 146 GetScreenHeight() { | |
| 147 return this.ctx.canvas.height; | |
| 148 } | |
| 149 | |
| 150 GetFrameTime() { | |
| 151 // TODO: This is a stopgap solution to prevent sudden jumps in dt when the user switches to a differen tab. | |
| 152 // We need a proper handling of Target FPS here. | |
| 153 return Math.min(this.dt, 1.0 / this.targetFPS); | |
| 154 } | |
| 155 | |
| 156 BeginDrawing() {} | |
| 157 | |
| 158 EndDrawing() { | |
| 159 this.prevPressedKeyState.clear(); | |
| 160 this.prevPressedKeyState = new Set(this.currentPressedKeyState); | |
| 161 this.currentMouseWheelMoveState = 0.0; | |
| 162 } | |
| 163 | |
| 164 DrawCircleV(center_ptr, radius, color_ptr) { | |
| 165 const buffer = this.wasm.instance.exports.memory.buffer; | |
| 166 const [x, y] = new Float32Array(buffer, center_ptr, 2); | |
| 167 const [r, g, b, a] = new Uint8Array(buffer, color_ptr, 4); | |
| 168 const color = color_hex_unpacked(r, g, b, a); | |
| 169 this.ctx.beginPath(); | |
| 170 this.ctx.arc(x, y, radius, 0, 2 * Math.PI, false); | |
| 171 this.ctx.fillStyle = color; | |
| 172 this.ctx.fill(); | |
| 173 } | |
| 174 | |
| 175 ClearBackground(color_ptr) { | |
| 176 this.ctx.fillStyle = getColorFromMemory( | |
| 177 this.wasm.instance.exports.memory.buffer, | |
| 178 color_ptr, | |
| 179 ); | |
| 180 this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); | |
| 181 } | |
| 182 | |
| 183 // RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) | |
| 184 DrawText(text_ptr, posX, posY, fontSize, color_ptr) { | |
| 185 const buffer = this.wasm.instance.exports.memory.buffer; | |
| 186 const text = cstr_by_ptr(buffer, text_ptr); | |
| 187 const color = getColorFromMemory(buffer, color_ptr); | |
| 188 fontSize *= this.#FONT_SCALE_MAGIC; | |
| 189 this.ctx.fillStyle = color; | |
| 190 // TODO: since the default font is part of Raylib the css that defines it should be located in raylib.js and not in index.html | |
| 191 this.ctx.font = `${fontSize}px grixel`; | |
| 192 | |
| 193 const lines = text.split("\n"); | |
| 194 for (var i = 0; i < lines.length; i++) { | |
| 195 this.ctx.fillText(lines[i], posX, posY + fontSize + i * fontSize); | |
| 196 } | |
| 197 } | |
| 198 | |
| 199 // RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle | |
| 200 DrawRectangle(posX, posY, width, height, color_ptr) { | |
| 201 const buffer = this.wasm.instance.exports.memory.buffer; | |
| 202 const color = getColorFromMemory(buffer, color_ptr); | |
| 203 this.ctx.fillStyle = color; | |
| 204 this.ctx.fillRect(posX, posY, width, height); | |
| 205 } | |
| 206 | |
| 207 DrawRectangleV(position_ptr, size_ptr, color_ptr) { | |
| 208 const buffer = this.wasm.instance.exports.memory.buffer; | |
| 209 const color = getColorFromMemory(buffer, color_ptr); | |
| 210 const position = new Float32Array(buffer, position_ptr, 2); | |
| 211 const size = new Float32Array(buffer, size_ptr, 2); | |
| 212 this.ctx.fillStyle = color; | |
| 213 this.ctx.fillRect(position[0], position[1], size[0], size[1]); | |
| 214 } | |
| 215 | |
| 216 IsKeyPressed(key) { | |
| 217 return ( | |
| 218 !this.prevPressedKeyState.has(key) && this.currentPressedKeyState.has(key) | |
| 219 ); | |
| 220 } | |
| 221 IsKeyDown(key) { | |
| 222 return this.currentPressedKeyState.has(key); | |
| 223 } | |
| 224 GetMouseWheelMove() { | |
| 225 return this.currentMouseWheelMoveState; | |
| 226 } | |
| 227 IsGestureDetected() { | |
| 228 return false; | |
| 229 } | |
| 230 IsMouseButtonPressed(key) { | |
| 231 return this.currentIsMouseButtonPressed == key; | |
| 232 } | |
| 233 | |
| 234 TextFormat(...args) { | |
| 235 // TODO: Implement printf style formatting for TextFormat | |
| 236 return args[0]; | |
| 237 } | |
| 238 | |
| 239 TraceLog(logLevel, text_ptr, ...args) { | |
| 240 // TODO: Implement printf style formatting for TraceLog | |
| 241 const buffer = this.wasm.instance.exports.memory.buffer; | |
| 242 const text = cstr_by_ptr(buffer, text_ptr); | |
| 243 switch (logLevel) { | |
| 244 case LOG_ALL: | |
| 245 console.log(`ALL: ${text} ${args}`); | |
| 246 break; | |
| 247 case LOG_TRACE: | |
| 248 console.log(`TRACE: ${text} ${args}`); | |
| 249 break; | |
| 250 case LOG_DEBUG: | |
| 251 console.log(`DEBUG: ${text} ${args}`); | |
| 252 break; | |
| 253 case LOG_INFO: | |
| 254 console.log(`INFO: ${text} ${args}`); | |
| 255 break; | |
| 256 case LOG_WARNING: | |
| 257 console.log(`WARNING: ${text} ${args}`); | |
| 258 break; | |
| 259 case LOG_ERROR: | |
| 260 console.log(`ERROR: ${text} ${args}`); | |
| 261 break; | |
| 262 case LOG_FATAL: | |
| 263 throw new Error(`FATAL: ${text}`); | |
| 264 case LOG_NONE: | |
| 265 console.log(`NONE: ${text} ${args}`); | |
| 266 break; | |
| 267 } | |
| 268 } | |
| 269 | |
| 270 GetMousePosition(result_ptr) { | |
| 271 const bcrect = this.ctx.canvas.getBoundingClientRect(); | |
| 272 const x = this.currentMousePosition.x - bcrect.left; | |
| 273 const y = this.currentMousePosition.y - bcrect.top; | |
| 274 | |
| 275 const buffer = this.wasm.instance.exports.memory.buffer; | |
| 276 new Float32Array(buffer, result_ptr, 2).set([x, y]); | |
| 277 } | |
| 278 | |
| 279 CheckCollisionPointRec(point_ptr, rec_ptr) { | |
| 280 const buffer = this.wasm.instance.exports.memory.buffer; | |
| 281 const [x, y] = new Float32Array(buffer, point_ptr, 2); | |
| 282 const [rx, ry, rw, rh] = new Float32Array(buffer, rec_ptr, 4); | |
| 283 return x >= rx && x <= rx + rw && y >= ry && y <= ry + rh; | |
| 284 } | |
| 285 | |
| 286 Fade(result_ptr, color_ptr, alpha) { | |
| 287 const buffer = this.wasm.instance.exports.memory.buffer; | |
| 288 const [r, g, b, _] = new Uint8Array(buffer, color_ptr, 4); | |
| 289 const newA = Math.max(0, Math.min(255, 255.0 * alpha)); | |
| 290 new Uint8Array(buffer, result_ptr, 4).set([r, g, b, newA]); | |
| 291 } | |
| 292 | |
| 293 DrawRectangleRec(rec_ptr, color_ptr) { | |
| 294 const buffer = this.wasm.instance.exports.memory.buffer; | |
| 295 const [x, y, w, h] = new Float32Array(buffer, rec_ptr, 4); | |
| 296 const color = getColorFromMemory(buffer, color_ptr); | |
| 297 this.ctx.fillStyle = color; | |
| 298 this.ctx.fillRect(x, y, w, h); | |
| 299 } | |
| 300 | |
| 301 DrawRectangleLinesEx(rec_ptr, lineThick, color_ptr) { | |
| 302 const buffer = this.wasm.instance.exports.memory.buffer; | |
| 303 const [x, y, w, h] = new Float32Array(buffer, rec_ptr, 4); | |
| 304 const color = getColorFromMemory(buffer, color_ptr); | |
| 305 this.ctx.strokeStyle = color; | |
| 306 this.ctx.lineWidth = lineThick; | |
| 307 this.ctx.strokeRect( | |
| 308 x + lineThick / 2, | |
| 309 y + lineThick / 2, | |
| 310 w - lineThick, | |
| 311 h - lineThick, | |
| 312 ); | |
| 313 } | |
| 314 | |
| 315 MeasureText(text_ptr, fontSize) { | |
| 316 const buffer = this.wasm.instance.exports.memory.buffer; | |
| 317 const text = cstr_by_ptr(buffer, text_ptr); | |
| 318 fontSize *= this.#FONT_SCALE_MAGIC; | |
| 319 this.ctx.font = `${fontSize}px grixel`; | |
| 320 return this.ctx.measureText(text).width; | |
| 321 } | |
| 322 | |
| 323 TextSubtext(text_ptr, position, length) { | |
| 324 const buffer = this.wasm.instance.exports.memory.buffer; | |
| 325 const text = cstr_by_ptr(buffer, text_ptr); | |
| 326 const subtext = text.substring(position, length); | |
| 327 | |
| 328 var bytes = new Uint8Array(buffer, 0, subtext.length + 1); | |
| 329 for (var i = 0; i < subtext.length; i++) { | |
| 330 bytes[i] = subtext.charCodeAt(i); | |
| 331 } | |
| 332 bytes[subtext.length] = 0; | |
| 333 | |
| 334 return bytes; | |
| 335 } | |
| 336 | |
| 337 // RLAPI Texture2D LoadTexture(const char *fileName); | |
| 338 LoadTexture(result_ptr, filename_ptr) { | |
| 339 const buffer = this.wasm.instance.exports.memory.buffer; | |
| 340 const filename = cstr_by_ptr(buffer, filename_ptr); | |
| 341 | |
| 342 var result = new Uint32Array(buffer, result_ptr, 5); | |
| 343 var img = new Image(); | |
| 344 const isLocalhost = window.location.hostname === "localhost"; | |
| 345 const baseUrl = isLocalhost | |
| 346 ? "http://localhost:6969/" | |
| 347 : `https://${window.location.hostname}/`; | |
| 348 img.src = `${baseUrl}${filename}`; | |
| 349 this.images.push(img); | |
| 350 | |
| 351 result[0] = this.images.indexOf(img); | |
| 352 // TODO: get the true width and height of the image | |
| 353 result[1] = 256; // width | |
| 354 result[2] = 256; // height | |
| 355 result[3] = 1; // mipmaps | |
| 356 result[4] = 7; // format PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 | |
| 357 | |
| 358 return result; | |
| 359 } | |
| 360 | |
| 361 // RLAPI void DrawTexture(Texture2D texture, int posX, int posY, Color tint); | |
| 362 DrawTexture(texture_ptr, posX, posY, color_ptr) { | |
| 363 const buffer = this.wasm.instance.exports.memory.buffer; | |
| 364 const [id, width, height, mipmaps, format] = new Uint32Array( | |
| 365 buffer, | |
| 366 texture_ptr, | |
| 367 5, | |
| 368 ); | |
| 369 // // TODO: implement tinting for DrawTexture | |
| 370 // const tint = getColorFromMemory(buffer, color_ptr); | |
| 371 | |
| 372 this.ctx.drawImage(this.images[id], posX, posY); | |
| 373 } | |
| 374 | |
| 375 // TODO: codepoints are not implemented | |
| 376 LoadFontEx( | |
| 377 result_ptr, | |
| 378 fileName_ptr /*, fontSize, codepoints, codepointCount*/, | |
| 379 ) { | |
| 380 const buffer = this.wasm.instance.exports.memory.buffer; | |
| 381 const fileName = cstr_by_ptr(buffer, fileName_ptr); | |
| 382 // TODO: dynamically generate the name for the font | |
| 383 // Support more than one custom font | |
| 384 const font = new FontFace("myfont", `url(${fileName})`); | |
| 385 document.fonts.add(font); | |
| 386 font.load(); | |
| 387 } | |
| 388 | |
| 389 GenTextureMipmaps() {} | |
| 390 SetTextureFilter() {} | |
| 391 | |
| 392 MeasureTextEx(result_ptr, font, text_ptr, fontSize, spacing) { | |
| 393 const buffer = this.wasm.instance.exports.memory.buffer; | |
| 394 const text = cstr_by_ptr(buffer, text_ptr); | |
| 395 const result = new Float32Array(buffer, result_ptr, 2); | |
| 396 this.ctx.font = fontSize + "px myfont"; | |
| 397 const metrics = this.ctx.measureText(text); | |
| 398 result[0] = metrics.width; | |
| 399 result[1] = fontSize; | |
| 400 } | |
| 401 | |
| 402 DrawTextEx(font, text_ptr, position_ptr, fontSize, spacing, tint_ptr) { | |
| 403 const buffer = this.wasm.instance.exports.memory.buffer; | |
| 404 const text = cstr_by_ptr(buffer, text_ptr); | |
| 405 const [posX, posY] = new Float32Array(buffer, position_ptr, 2); | |
| 406 const tint = getColorFromMemory(buffer, tint_ptr); | |
| 407 this.ctx.fillStyle = tint; | |
| 408 this.ctx.font = fontSize + "px myfont"; | |
| 409 this.ctx.fillText(text, posX, posY + fontSize); | |
| 410 } | |
| 411 | |
| 412 GetRandomValue(min, max) { | |
| 413 return min + Math.floor(Math.random() * (max - min + 1)); | |
| 414 } | |
| 415 | |
| 416 ColorFromHSV(result_ptr, hue, saturation, value) { | |
| 417 const buffer = this.wasm.instance.exports.memory.buffer; | |
| 418 const result = new Uint8Array(buffer, result_ptr, 4); | |
| 419 | |
| 420 // Red channel | |
| 421 let k = (5.0 + hue / 60.0) % 6; | |
| 422 let t = 4.0 - k; | |
| 423 k = t < k ? t : k; | |
| 424 k = k < 1 ? k : 1; | |
| 425 k = k > 0 ? k : 0; | |
| 426 result[0] = Math.floor((value - value * saturation * k) * 255.0); | |
| 427 | |
| 428 // Green channel | |
| 429 k = (3.0 + hue / 60.0) % 6; | |
| 430 t = 4.0 - k; | |
| 431 k = t < k ? t : k; | |
| 432 k = k < 1 ? k : 1; | |
| 433 k = k > 0 ? k : 0; | |
| 434 result[1] = Math.floor((value - value * saturation * k) * 255.0); | |
| 435 | |
| 436 // Blue channel | |
| 437 k = (1.0 + hue / 60.0) % 6; | |
| 438 t = 4.0 - k; | |
| 439 k = t < k ? t : k; | |
| 440 k = k < 1 ? k : 1; | |
| 441 k = k > 0 ? k : 0; | |
| 442 result[2] = Math.floor((value - value * saturation * k) * 255.0); | |
| 443 | |
| 444 result[3] = 255; | |
| 445 } | |
| 446 | |
| 447 raylib_js_set_entry(entry) { | |
| 448 this.entryFunction = | |
| 449 this.wasm.instance.exports.__indirect_function_table.get(entry); | |
| 450 } | |
| 451 } | |
| 452 | |
| 453 const glfwKeyMapping = { | |
| 454 Space: 32, | |
| 455 Quote: 39, | |
| 456 Comma: 44, | |
| 457 Minus: 45, | |
| 458 Period: 46, | |
| 459 Slash: 47, | |
| 460 Digit0: 48, | |
| 461 Digit1: 49, | |
| 462 Digit2: 50, | |
| 463 Digit3: 51, | |
| 464 Digit4: 52, | |
| 465 Digit5: 53, | |
| 466 Digit6: 54, | |
| 467 Digit7: 55, | |
| 468 Digit8: 56, | |
| 469 Digit9: 57, | |
| 470 Semicolon: 59, | |
| 471 Equal: 61, | |
| 472 KeyA: 65, | |
| 473 KeyB: 66, | |
| 474 KeyC: 67, | |
| 475 KeyD: 68, | |
| 476 KeyE: 69, | |
| 477 KeyF: 70, | |
| 478 KeyG: 71, | |
| 479 KeyH: 72, | |
| 480 KeyI: 73, | |
| 481 KeyJ: 74, | |
| 482 KeyK: 75, | |
| 483 KeyL: 76, | |
| 484 KeyM: 77, | |
| 485 KeyN: 78, | |
| 486 KeyO: 79, | |
| 487 KeyP: 80, | |
| 488 KeyQ: 81, | |
| 489 KeyR: 82, | |
| 490 KeyS: 83, | |
| 491 KeyT: 84, | |
| 492 KeyU: 85, | |
| 493 KeyV: 86, | |
| 494 KeyW: 87, | |
| 495 KeyX: 88, | |
| 496 KeyY: 89, | |
| 497 KeyZ: 90, | |
| 498 BracketLeft: 91, | |
| 499 Backslash: 92, | |
| 500 BracketRight: 93, | |
| 501 Backquote: 96, | |
| 502 // GLFW_KEY_WORLD_1 161 /* non-US #1 */ | |
| 503 // GLFW_KEY_WORLD_2 162 /* non-US #2 */ | |
| 504 Escape: 256, | |
| 505 Enter: 257, | |
| 506 Tab: 258, | |
| 507 Backspace: 259, | |
| 508 Insert: 260, | |
| 509 Delete: 261, | |
| 510 ArrowRight: 262, | |
| 511 ArrowLeft: 263, | |
| 512 ArrowDown: 264, | |
| 513 ArrowUp: 265, | |
| 514 PageUp: 266, | |
| 515 PageDown: 267, | |
| 516 Home: 268, | |
| 517 End: 269, | |
| 518 CapsLock: 280, | |
| 519 ScrollLock: 281, | |
| 520 NumLock: 282, | |
| 521 PrintScreen: 283, | |
| 522 Pause: 284, | |
| 523 F1: 290, | |
| 524 F2: 291, | |
| 525 F3: 292, | |
| 526 F4: 293, | |
| 527 F5: 294, | |
| 528 F6: 295, | |
| 529 F7: 296, | |
| 530 F8: 297, | |
| 531 F9: 298, | |
| 532 F10: 299, | |
| 533 F11: 300, | |
| 534 F12: 301, | |
| 535 F13: 302, | |
| 536 F14: 303, | |
| 537 F15: 304, | |
| 538 F16: 305, | |
| 539 F17: 306, | |
| 540 F18: 307, | |
| 541 F19: 308, | |
| 542 F20: 309, | |
| 543 F21: 310, | |
| 544 F22: 311, | |
| 545 F23: 312, | |
| 546 F24: 313, | |
| 547 F25: 314, | |
| 548 NumPad0: 320, | |
| 549 NumPad1: 321, | |
| 550 NumPad2: 322, | |
| 551 NumPad3: 323, | |
| 552 NumPad4: 324, | |
| 553 NumPad5: 325, | |
| 554 NumPad6: 326, | |
| 555 NumPad7: 327, | |
| 556 NumPad8: 328, | |
| 557 NumPad9: 329, | |
| 558 NumpadDecimal: 330, | |
| 559 NumpadDivide: 331, | |
| 560 NumpadMultiply: 332, | |
| 561 NumpadSubtract: 333, | |
| 562 NumpadAdd: 334, | |
| 563 NumpadEnter: 335, | |
| 564 NumpadEqual: 336, | |
| 565 ShiftLeft: 340, | |
| 566 ControlLeft: 341, | |
| 567 AltLeft: 342, | |
| 568 MetaLeft: 343, | |
| 569 ShiftRight: 344, | |
| 570 ControlRight: 345, | |
| 571 AltRight: 346, | |
| 572 MetaRight: 347, | |
| 573 ContextMenu: 348, | |
| 574 // GLFW_KEY_LAST GLFW_KEY_MENU | |
| 575 }; | |
| 576 | |
| 577 function cstrlen(mem, ptr) { | |
| 578 let len = 0; | |
| 579 while (mem[ptr] != 0) { | |
| 580 len++; | |
| 581 ptr++; | |
| 582 } | |
| 583 return len; | |
| 584 } | |
| 585 | |
| 586 function cstr_by_ptr(mem_buffer, ptr) { | |
| 587 const mem = new Uint8Array(mem_buffer); | |
| 588 const len = cstrlen(mem, ptr); | |
| 589 const bytes = new Uint8Array(mem_buffer, ptr, len); | |
| 590 return new TextDecoder().decode(bytes); | |
| 591 } | |
| 592 | |
| 593 function color_hex_unpacked(r, g, b, a) { | |
| 594 r = r.toString(16).padStart(2, "0"); | |
| 595 g = g.toString(16).padStart(2, "0"); | |
| 596 b = b.toString(16).padStart(2, "0"); | |
| 597 a = a.toString(16).padStart(2, "0"); | |
| 598 return "#" + r + g + b + a; | |
| 599 } | |
| 600 | |
| 601 function color_hex(color) { | |
| 602 const r = ((color >> (0 * 8)) & 0xff).toString(16).padStart(2, "0"); | |
| 603 const g = ((color >> (1 * 8)) & 0xff).toString(16).padStart(2, "0"); | |
| 604 const b = ((color >> (2 * 8)) & 0xff).toString(16).padStart(2, "0"); | |
| 605 const a = ((color >> (3 * 8)) & 0xff).toString(16).padStart(2, "0"); | |
| 606 return "#" + r + g + b + a; | |
| 607 } | |
| 608 | |
| 609 function getColorFromMemory(buffer, color_ptr) { | |
| 610 const [r, g, b, a] = new Uint8Array(buffer, color_ptr, 4); | |
| 611 return color_hex_unpacked(r, g, b, a); | |
| 612 } |