From b6ae2d8037eb990ff03168112ddf5e1d6b3c820c Mon Sep 17 00:00:00 2001 From: Leon Krause Date: Sun, 8 Jul 2018 02:23:19 +0200 Subject: Refactor OS_JavaScript --- platform/javascript/dom_keys.h | 385 ---------- platform/javascript/dom_keys.inc | 380 ++++++++++ platform/javascript/javascript_main.cpp | 27 +- platform/javascript/os_javascript.cpp | 1157 +++++++++++++++---------------- platform/javascript/os_javascript.h | 133 ++-- 5 files changed, 1013 insertions(+), 1069 deletions(-) delete mode 100644 platform/javascript/dom_keys.h create mode 100644 platform/javascript/dom_keys.inc (limited to 'platform/javascript') diff --git a/platform/javascript/dom_keys.h b/platform/javascript/dom_keys.h deleted file mode 100644 index 4edca63c6d..0000000000 --- a/platform/javascript/dom_keys.h +++ /dev/null @@ -1,385 +0,0 @@ -/*************************************************************************/ -/* dom_keys.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef DOM_KEYS_H -#define DOM_KEYS_H - -#include "os/keyboard.h" - -// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#Constants_for_keyCode_value -#define DOM_VK_CANCEL 0x03 -#define DOM_VK_HELP 0x06 -#define DOM_VK_BACK_SPACE 0x08 -#define DOM_VK_TAB 0x09 -#define DOM_VK_CLEAR 0x0C -#define DOM_VK_RETURN 0x0D -#define DOM_VK_ENTER 0x0E // "Reserved, but not used." -#define DOM_VK_SHIFT 0x10 -#define DOM_VK_CONTROL 0x11 -#define DOM_VK_ALT 0x12 -#define DOM_VK_PAUSE 0x13 -#define DOM_VK_CAPS_LOCK 0x14 -#define DOM_VK_KANA 0x15 -#define DOM_VK_HANGUL 0x15 -#define DOM_VK_EISU 0x16 -#define DOM_VK_JUNJA 0x17 -#define DOM_VK_FINAL 0x18 -#define DOM_VK_HANJA 0x19 -#define DOM_VK_KANJI 0x19 -#define DOM_VK_ESCAPE 0x1B -#define DOM_VK_CONVERT 0x1C -#define DOM_VK_NONCONVERT 0x1D -#define DOM_VK_ACCEPT 0x1E -#define DOM_VK_MODECHANGE 0x1F -#define DOM_VK_SPACE 0x20 -#define DOM_VK_PAGE_UP 0x21 -#define DOM_VK_PAGE_DOWN 0x22 -#define DOM_VK_END 0x23 -#define DOM_VK_HOME 0x24 -#define DOM_VK_LEFT 0x25 -#define DOM_VK_UP 0x26 -#define DOM_VK_RIGHT 0x27 -#define DOM_VK_DOWN 0x28 -#define DOM_VK_SELECT 0x29 -#define DOM_VK_PRINT 0x2A -#define DOM_VK_EXECUTE 0x2B -#define DOM_VK_PRINTSCREEN 0x2C -#define DOM_VK_INSERT 0x2D -#define DOM_VK_DELETE 0x2E -#define DOM_VK_0 0x30 -#define DOM_VK_1 0x31 -#define DOM_VK_2 0x32 -#define DOM_VK_3 0x33 -#define DOM_VK_4 0x34 -#define DOM_VK_5 0x35 -#define DOM_VK_6 0x36 -#define DOM_VK_7 0x37 -#define DOM_VK_8 0x38 -#define DOM_VK_9 0x39 -#define DOM_VK_COLON 0x3A -#define DOM_VK_SEMICOLON 0x3B -#define DOM_VK_LESS_THAN 0x3C -#define DOM_VK_EQUALS 0x3D -#define DOM_VK_GREATER_THAN 0x3E -#define DOM_VK_QUESTION_MARK 0x3F -#define DOM_VK_AT 0x40 -#define DOM_VK_A 0x41 -#define DOM_VK_B 0x42 -#define DOM_VK_C 0x43 -#define DOM_VK_D 0x44 -#define DOM_VK_E 0x45 -#define DOM_VK_F 0x46 -#define DOM_VK_G 0x47 -#define DOM_VK_H 0x48 -#define DOM_VK_I 0x49 -#define DOM_VK_J 0x4A -#define DOM_VK_K 0x4B -#define DOM_VK_L 0x4C -#define DOM_VK_M 0x4D -#define DOM_VK_N 0x4E -#define DOM_VK_O 0x4F -#define DOM_VK_P 0x50 -#define DOM_VK_Q 0x51 -#define DOM_VK_R 0x52 -#define DOM_VK_S 0x53 -#define DOM_VK_T 0x54 -#define DOM_VK_U 0x55 -#define DOM_VK_V 0x56 -#define DOM_VK_W 0x57 -#define DOM_VK_X 0x58 -#define DOM_VK_Y 0x59 -#define DOM_VK_Z 0x5A -#define DOM_VK_WIN 0x5B -#define DOM_VK_CONTEXT_MENU 0x5D -#define DOM_VK_SLEEP 0x5F -#define DOM_VK_NUMPAD0 0x60 -#define DOM_VK_NUMPAD1 0x61 -#define DOM_VK_NUMPAD2 0x62 -#define DOM_VK_NUMPAD3 0x63 -#define DOM_VK_NUMPAD4 0x64 -#define DOM_VK_NUMPAD5 0x65 -#define DOM_VK_NUMPAD6 0x66 -#define DOM_VK_NUMPAD7 0x67 -#define DOM_VK_NUMPAD8 0x68 -#define DOM_VK_NUMPAD9 0x69 -#define DOM_VK_MULTIPLY 0x6A -#define DOM_VK_ADD 0x6B -#define DOM_VK_SEPARATOR 0x6C -#define DOM_VK_SUBTRACT 0x6D -#define DOM_VK_DECIMAL 0x6E -#define DOM_VK_DIVIDE 0x6F -#define DOM_VK_F1 0x70 -#define DOM_VK_F2 0x71 -#define DOM_VK_F3 0x72 -#define DOM_VK_F4 0x73 -#define DOM_VK_F5 0x74 -#define DOM_VK_F6 0x75 -#define DOM_VK_F7 0x76 -#define DOM_VK_F8 0x77 -#define DOM_VK_F9 0x78 -#define DOM_VK_F10 0x79 -#define DOM_VK_F11 0x7A -#define DOM_VK_F12 0x7B -#define DOM_VK_F13 0x7C -#define DOM_VK_F14 0x7D -#define DOM_VK_F15 0x7E -#define DOM_VK_F16 0x7F -#define DOM_VK_F17 0x80 -#define DOM_VK_F18 0x81 -#define DOM_VK_F19 0x82 -#define DOM_VK_F20 0x83 -#define DOM_VK_F21 0x84 -#define DOM_VK_F22 0x85 -#define DOM_VK_F23 0x86 -#define DOM_VK_F24 0x87 -#define DOM_VK_NUM_LOCK 0x90 -#define DOM_VK_SCROLL_LOCK 0x91 -#define DOM_VK_WIN_OEM_FJ_JISHO 0x92 -#define DOM_VK_WIN_OEM_FJ_MASSHOU 0x93 -#define DOM_VK_WIN_OEM_FJ_TOUROKU 0x94 -#define DOM_VK_WIN_OEM_FJ_LOYA 0x95 -#define DOM_VK_WIN_OEM_FJ_ROYA 0x96 -#define DOM_VK_CIRCUMFLEX 0xA0 -#define DOM_VK_EXCLAMATION 0xA1 -#define DOM_VK_DOUBLE_QUOTE 0xA2 -#define DOM_VK_HASH 0xA3 -#define DOM_VK_DOLLAR 0xA4 -#define DOM_VK_PERCENT 0xA5 -#define DOM_VK_AMPERSAND 0xA6 -#define DOM_VK_UNDERSCORE 0xA7 -#define DOM_VK_OPEN_PAREN 0xA8 -#define DOM_VK_CLOSE_PAREN 0xA9 -#define DOM_VK_ASTERISK 0xAA -#define DOM_VK_PLUS 0xAB -#define DOM_VK_PIPE 0xAC -#define DOM_VK_HYPHEN_MINUS 0xAD -#define DOM_VK_OPEN_CURLY_BRACKET 0xAE -#define DOM_VK_CLOSE_CURLY_BRACKET 0xAF -#define DOM_VK_TILDE 0xB0 -#define DOM_VK_VOLUME_MUTE 0xB5 -#define DOM_VK_VOLUME_DOWN 0xB6 -#define DOM_VK_VOLUME_UP 0xB7 -#define DOM_VK_COMMA 0xBC -#define DOM_VK_PERIOD 0xBE -#define DOM_VK_SLASH 0xBF -#define DOM_VK_BACK_QUOTE 0xC0 -#define DOM_VK_OPEN_BRACKET 0xDB -#define DOM_VK_BACK_SLASH 0xDC -#define DOM_VK_CLOSE_BRACKET 0xDD -#define DOM_VK_QUOTE 0xDE -#define DOM_VK_META 0xE0 -#define DOM_VK_ALTGR 0xE1 -#define DOM_VK_WIN_ICO_HELP 0xE3 -#define DOM_VK_WIN_ICO_00 0xE4 -#define DOM_VK_WIN_ICO_CLEAR 0xE6 -#define DOM_VK_WIN_OEM_RESET 0xE9 -#define DOM_VK_WIN_OEM_JUMP 0xEA -#define DOM_VK_WIN_OEM_PA1 0xEB -#define DOM_VK_WIN_OEM_PA2 0xEC -#define DOM_VK_WIN_OEM_PA3 0xED -#define DOM_VK_WIN_OEM_WSCTRL 0xEE -#define DOM_VK_WIN_OEM_CUSEL 0xEF -#define DOM_VK_WIN_OEM_ATTN 0xF0 -#define DOM_VK_WIN_OEM_FINISH 0xF1 -#define DOM_VK_WIN_OEM_COPY 0xF2 -#define DOM_VK_WIN_OEM_AUTO 0xF3 -#define DOM_VK_WIN_OEM_ENLW 0xF4 -#define DOM_VK_WIN_OEM_BACKTAB 0xF5 -#define DOM_VK_ATTN 0xF6 -#define DOM_VK_CRSEL 0xF7 -#define DOM_VK_EXSEL 0xF8 -#define DOM_VK_EREOF 0xF9 -#define DOM_VK_PLAY 0xFA -#define DOM_VK_ZOOM 0xFB -#define DOM_VK_PA1 0xFD -#define DOM_VK_WIN_OEM_CLEAR 0xFE - -int dom2godot_scancode(int dom_keycode) { - - if (DOM_VK_0 <= dom_keycode && dom_keycode <= DOM_VK_Z) { - // ASCII intersection - return dom_keycode; - } - - if (DOM_VK_NUMPAD0 <= dom_keycode && dom_keycode <= DOM_VK_NUMPAD9) { - // Numpad numbers - return KEY_KP_0 + (dom_keycode - DOM_VK_NUMPAD0); - } - - if (DOM_VK_F1 <= dom_keycode && dom_keycode <= DOM_VK_F16) { - // F1-F16 - return KEY_F1 + (dom_keycode - DOM_VK_F1); - } - - switch (dom_keycode) { - //case DOM_VK_CANCEL: return KEY_UNKNOWN; - case DOM_VK_HELP: return KEY_HELP; - case DOM_VK_BACK_SPACE: return KEY_BACKSPACE; - case DOM_VK_TAB: return KEY_TAB; - - case DOM_VK_CLEAR: - case DOM_VK_WIN_OEM_CLEAR: // OEM duplicate - return KEY_CLEAR; - - case DOM_VK_RETURN: - case DOM_VK_ENTER: // unused according to MDN - return KEY_ENTER; - - case DOM_VK_SHIFT: return KEY_SHIFT; - case DOM_VK_CONTROL: return KEY_CONTROL; - - case DOM_VK_ALT: - case DOM_VK_ALTGR: - return KEY_ALT; - - case DOM_VK_PAUSE: return KEY_PAUSE; - case DOM_VK_CAPS_LOCK: - return KEY_CAPSLOCK; - - /* - case DOM_VK_KANA: return KEY_UNKNOWN; - case DOM_VK_HANGUL: return KEY_UNKNOWN; - case DOM_VK_EISU: return KEY_UNKNOWN; - case DOM_VK_JUNJA: return KEY_UNKNOWN; - case DOM_VK_FINAL: return KEY_UNKNOWN; - case DOM_VK_HANJA: return KEY_UNKNOWN; - case DOM_VK_KANJI: return KEY_UNKNOWN; - */ - - case DOM_VK_ESCAPE: - return KEY_ESCAPE; - /* - case DOM_VK_CONVERT: return KEY_UNKNOWN; - case DOM_VK_NONCONVERT: return KEY_UNKNOWN; - case DOM_VK_ACCEPT: return KEY_UNKNOWN; - case DOM_VK_MODECHANGE: return KEY_UNKNOWN; - */ - - case DOM_VK_SPACE: return KEY_SPACE; - case DOM_VK_PAGE_UP: return KEY_PAGEUP; - case DOM_VK_PAGE_DOWN: return KEY_PAGEDOWN; - case DOM_VK_END: return KEY_END; - case DOM_VK_HOME: return KEY_HOME; - case DOM_VK_LEFT: return KEY_LEFT; - case DOM_VK_UP: return KEY_UP; - case DOM_VK_RIGHT: return KEY_RIGHT; - case DOM_VK_DOWN: - return KEY_DOWN; - - //case DOM_VK_SELECT: return KEY_UNKNOWN; - - case DOM_VK_PRINTSCREEN: // this is the usual printScreen key - case DOM_VK_PRINT: // maybe for alt+printScreen or physical printers? - return KEY_PRINT; - - //case DOM_VK_EXECUTE: return KEY_UNKNOWN; - case DOM_VK_INSERT: return KEY_INSERT; - case DOM_VK_DELETE: return KEY_DELETE; - - case DOM_VK_META: - case DOM_VK_WIN: - return KEY_META; - - case DOM_VK_CONTEXT_MENU: return KEY_MENU; - case DOM_VK_SLEEP: - return KEY_STANDBY; - - // these are numpad keys according to MDN - case DOM_VK_MULTIPLY: return KEY_KP_MULTIPLY; - case DOM_VK_ADD: return KEY_KP_ADD; - case DOM_VK_SEPARATOR: - return KEY_KP_PERIOD; // good enough? - case DOM_VK_SUBTRACT: return KEY_KP_SUBTRACT; - case DOM_VK_DECIMAL: return KEY_KP_PERIOD; - case DOM_VK_DIVIDE: - return KEY_KP_DIVIDE; - - /* - case DOM_VK_F17: return KEY_UNKNOWN; - case DOM_VK_F18: return KEY_UNKNOWN; - case DOM_VK_F19: return KEY_UNKNOWN; - case DOM_VK_F20: return KEY_UNKNOWN; - case DOM_VK_F21: return KEY_UNKNOWN; - case DOM_VK_F22: return KEY_UNKNOWN; - case DOM_VK_F23: return KEY_UNKNOWN; - case DOM_VK_F24: return KEY_UNKNOWN; - */ - - case DOM_VK_NUM_LOCK: return KEY_NUMLOCK; - case DOM_VK_SCROLL_LOCK: - return KEY_SCROLLLOCK; - - /* - case DOM_VK_WIN_OEM_FJ_JISHO: return KEY_UNKNOWN; - case DOM_VK_WIN_OEM_FJ_MASSHOU: return KEY_UNKNOWN; - case DOM_VK_WIN_OEM_FJ_TOUROKU: return KEY_UNKNOWN; - case DOM_VK_WIN_OEM_FJ_LOYA: return KEY_UNKNOWN; - case DOM_VK_WIN_OEM_FJ_ROYA: return KEY_UNKNOWN; - */ - - case DOM_VK_CIRCUMFLEX: return KEY_ASCIICIRCUM; - case DOM_VK_EXCLAMATION: return KEY_EXCLAM; - case DOM_VK_DOUBLE_QUOTE: return KEY_QUOTEDBL; - case DOM_VK_HASH: return KEY_NUMBERSIGN; - case DOM_VK_DOLLAR: return KEY_DOLLAR; - case DOM_VK_PERCENT: return KEY_PERCENT; - case DOM_VK_AMPERSAND: return KEY_AMPERSAND; - case DOM_VK_UNDERSCORE: return KEY_UNDERSCORE; - case DOM_VK_OPEN_PAREN: return KEY_PARENLEFT; - case DOM_VK_CLOSE_PAREN: return KEY_PARENRIGHT; - case DOM_VK_ASTERISK: return KEY_ASTERISK; - case DOM_VK_PLUS: return KEY_PLUS; - case DOM_VK_PIPE: return KEY_BAR; - case DOM_VK_HYPHEN_MINUS: return KEY_MINUS; - case DOM_VK_OPEN_CURLY_BRACKET: return KEY_BRACELEFT; - case DOM_VK_CLOSE_CURLY_BRACKET: return KEY_BRACERIGHT; - case DOM_VK_TILDE: return KEY_ASCIITILDE; - - case DOM_VK_VOLUME_MUTE: return KEY_VOLUMEMUTE; - case DOM_VK_VOLUME_DOWN: return KEY_VOLUMEDOWN; - case DOM_VK_VOLUME_UP: return KEY_VOLUMEUP; - - case DOM_VK_COMMA: return KEY_COMMA; - case DOM_VK_PERIOD: return KEY_PERIOD; - case DOM_VK_SLASH: return KEY_SLASH; - case DOM_VK_BACK_QUOTE: return KEY_QUOTELEFT; - case DOM_VK_OPEN_BRACKET: return KEY_BRACKETLEFT; - case DOM_VK_BACK_SLASH: return KEY_BACKSLASH; - case DOM_VK_CLOSE_BRACKET: return KEY_BRACKETRIGHT; - case DOM_VK_QUOTE: - return KEY_APOSTROPHE; - - // rest is OEM/unusual - - default: return KEY_UNKNOWN; - }; -} - -#endif diff --git a/platform/javascript/dom_keys.inc b/platform/javascript/dom_keys.inc new file mode 100644 index 0000000000..dc8d67d52b --- /dev/null +++ b/platform/javascript/dom_keys.inc @@ -0,0 +1,380 @@ +/*************************************************************************/ +/* dom_keys.inc */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "os/keyboard.h" + +// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#Constants_for_keyCode_value +#define DOM_VK_CANCEL 0x03 +#define DOM_VK_HELP 0x06 +#define DOM_VK_BACK_SPACE 0x08 +#define DOM_VK_TAB 0x09 +#define DOM_VK_CLEAR 0x0C +#define DOM_VK_RETURN 0x0D +#define DOM_VK_ENTER 0x0E // "Reserved, but not used." +#define DOM_VK_SHIFT 0x10 +#define DOM_VK_CONTROL 0x11 +#define DOM_VK_ALT 0x12 +#define DOM_VK_PAUSE 0x13 +#define DOM_VK_CAPS_LOCK 0x14 +#define DOM_VK_KANA 0x15 +#define DOM_VK_HANGUL 0x15 +#define DOM_VK_EISU 0x16 +#define DOM_VK_JUNJA 0x17 +#define DOM_VK_FINAL 0x18 +#define DOM_VK_HANJA 0x19 +#define DOM_VK_KANJI 0x19 +#define DOM_VK_ESCAPE 0x1B +#define DOM_VK_CONVERT 0x1C +#define DOM_VK_NONCONVERT 0x1D +#define DOM_VK_ACCEPT 0x1E +#define DOM_VK_MODECHANGE 0x1F +#define DOM_VK_SPACE 0x20 +#define DOM_VK_PAGE_UP 0x21 +#define DOM_VK_PAGE_DOWN 0x22 +#define DOM_VK_END 0x23 +#define DOM_VK_HOME 0x24 +#define DOM_VK_LEFT 0x25 +#define DOM_VK_UP 0x26 +#define DOM_VK_RIGHT 0x27 +#define DOM_VK_DOWN 0x28 +#define DOM_VK_SELECT 0x29 +#define DOM_VK_PRINT 0x2A +#define DOM_VK_EXECUTE 0x2B +#define DOM_VK_PRINTSCREEN 0x2C +#define DOM_VK_INSERT 0x2D +#define DOM_VK_DELETE 0x2E +#define DOM_VK_0 0x30 +#define DOM_VK_1 0x31 +#define DOM_VK_2 0x32 +#define DOM_VK_3 0x33 +#define DOM_VK_4 0x34 +#define DOM_VK_5 0x35 +#define DOM_VK_6 0x36 +#define DOM_VK_7 0x37 +#define DOM_VK_8 0x38 +#define DOM_VK_9 0x39 +#define DOM_VK_COLON 0x3A +#define DOM_VK_SEMICOLON 0x3B +#define DOM_VK_LESS_THAN 0x3C +#define DOM_VK_EQUALS 0x3D +#define DOM_VK_GREATER_THAN 0x3E +#define DOM_VK_QUESTION_MARK 0x3F +#define DOM_VK_AT 0x40 +#define DOM_VK_A 0x41 +#define DOM_VK_B 0x42 +#define DOM_VK_C 0x43 +#define DOM_VK_D 0x44 +#define DOM_VK_E 0x45 +#define DOM_VK_F 0x46 +#define DOM_VK_G 0x47 +#define DOM_VK_H 0x48 +#define DOM_VK_I 0x49 +#define DOM_VK_J 0x4A +#define DOM_VK_K 0x4B +#define DOM_VK_L 0x4C +#define DOM_VK_M 0x4D +#define DOM_VK_N 0x4E +#define DOM_VK_O 0x4F +#define DOM_VK_P 0x50 +#define DOM_VK_Q 0x51 +#define DOM_VK_R 0x52 +#define DOM_VK_S 0x53 +#define DOM_VK_T 0x54 +#define DOM_VK_U 0x55 +#define DOM_VK_V 0x56 +#define DOM_VK_W 0x57 +#define DOM_VK_X 0x58 +#define DOM_VK_Y 0x59 +#define DOM_VK_Z 0x5A +#define DOM_VK_WIN 0x5B +#define DOM_VK_CONTEXT_MENU 0x5D +#define DOM_VK_SLEEP 0x5F +#define DOM_VK_NUMPAD0 0x60 +#define DOM_VK_NUMPAD1 0x61 +#define DOM_VK_NUMPAD2 0x62 +#define DOM_VK_NUMPAD3 0x63 +#define DOM_VK_NUMPAD4 0x64 +#define DOM_VK_NUMPAD5 0x65 +#define DOM_VK_NUMPAD6 0x66 +#define DOM_VK_NUMPAD7 0x67 +#define DOM_VK_NUMPAD8 0x68 +#define DOM_VK_NUMPAD9 0x69 +#define DOM_VK_MULTIPLY 0x6A +#define DOM_VK_ADD 0x6B +#define DOM_VK_SEPARATOR 0x6C +#define DOM_VK_SUBTRACT 0x6D +#define DOM_VK_DECIMAL 0x6E +#define DOM_VK_DIVIDE 0x6F +#define DOM_VK_F1 0x70 +#define DOM_VK_F2 0x71 +#define DOM_VK_F3 0x72 +#define DOM_VK_F4 0x73 +#define DOM_VK_F5 0x74 +#define DOM_VK_F6 0x75 +#define DOM_VK_F7 0x76 +#define DOM_VK_F8 0x77 +#define DOM_VK_F9 0x78 +#define DOM_VK_F10 0x79 +#define DOM_VK_F11 0x7A +#define DOM_VK_F12 0x7B +#define DOM_VK_F13 0x7C +#define DOM_VK_F14 0x7D +#define DOM_VK_F15 0x7E +#define DOM_VK_F16 0x7F +#define DOM_VK_F17 0x80 +#define DOM_VK_F18 0x81 +#define DOM_VK_F19 0x82 +#define DOM_VK_F20 0x83 +#define DOM_VK_F21 0x84 +#define DOM_VK_F22 0x85 +#define DOM_VK_F23 0x86 +#define DOM_VK_F24 0x87 +#define DOM_VK_NUM_LOCK 0x90 +#define DOM_VK_SCROLL_LOCK 0x91 +#define DOM_VK_WIN_OEM_FJ_JISHO 0x92 +#define DOM_VK_WIN_OEM_FJ_MASSHOU 0x93 +#define DOM_VK_WIN_OEM_FJ_TOUROKU 0x94 +#define DOM_VK_WIN_OEM_FJ_LOYA 0x95 +#define DOM_VK_WIN_OEM_FJ_ROYA 0x96 +#define DOM_VK_CIRCUMFLEX 0xA0 +#define DOM_VK_EXCLAMATION 0xA1 +#define DOM_VK_DOUBLE_QUOTE 0xA2 +#define DOM_VK_HASH 0xA3 +#define DOM_VK_DOLLAR 0xA4 +#define DOM_VK_PERCENT 0xA5 +#define DOM_VK_AMPERSAND 0xA6 +#define DOM_VK_UNDERSCORE 0xA7 +#define DOM_VK_OPEN_PAREN 0xA8 +#define DOM_VK_CLOSE_PAREN 0xA9 +#define DOM_VK_ASTERISK 0xAA +#define DOM_VK_PLUS 0xAB +#define DOM_VK_PIPE 0xAC +#define DOM_VK_HYPHEN_MINUS 0xAD +#define DOM_VK_OPEN_CURLY_BRACKET 0xAE +#define DOM_VK_CLOSE_CURLY_BRACKET 0xAF +#define DOM_VK_TILDE 0xB0 +#define DOM_VK_VOLUME_MUTE 0xB5 +#define DOM_VK_VOLUME_DOWN 0xB6 +#define DOM_VK_VOLUME_UP 0xB7 +#define DOM_VK_COMMA 0xBC +#define DOM_VK_PERIOD 0xBE +#define DOM_VK_SLASH 0xBF +#define DOM_VK_BACK_QUOTE 0xC0 +#define DOM_VK_OPEN_BRACKET 0xDB +#define DOM_VK_BACK_SLASH 0xDC +#define DOM_VK_CLOSE_BRACKET 0xDD +#define DOM_VK_QUOTE 0xDE +#define DOM_VK_META 0xE0 +#define DOM_VK_ALTGR 0xE1 +#define DOM_VK_WIN_ICO_HELP 0xE3 +#define DOM_VK_WIN_ICO_00 0xE4 +#define DOM_VK_WIN_ICO_CLEAR 0xE6 +#define DOM_VK_WIN_OEM_RESET 0xE9 +#define DOM_VK_WIN_OEM_JUMP 0xEA +#define DOM_VK_WIN_OEM_PA1 0xEB +#define DOM_VK_WIN_OEM_PA2 0xEC +#define DOM_VK_WIN_OEM_PA3 0xED +#define DOM_VK_WIN_OEM_WSCTRL 0xEE +#define DOM_VK_WIN_OEM_CUSEL 0xEF +#define DOM_VK_WIN_OEM_ATTN 0xF0 +#define DOM_VK_WIN_OEM_FINISH 0xF1 +#define DOM_VK_WIN_OEM_COPY 0xF2 +#define DOM_VK_WIN_OEM_AUTO 0xF3 +#define DOM_VK_WIN_OEM_ENLW 0xF4 +#define DOM_VK_WIN_OEM_BACKTAB 0xF5 +#define DOM_VK_ATTN 0xF6 +#define DOM_VK_CRSEL 0xF7 +#define DOM_VK_EXSEL 0xF8 +#define DOM_VK_EREOF 0xF9 +#define DOM_VK_PLAY 0xFA +#define DOM_VK_ZOOM 0xFB +#define DOM_VK_PA1 0xFD +#define DOM_VK_WIN_OEM_CLEAR 0xFE + +int dom2godot_scancode(int dom_keycode) { + + if (DOM_VK_0 <= dom_keycode && dom_keycode <= DOM_VK_Z) { + // ASCII intersection + return dom_keycode; + } + + if (DOM_VK_NUMPAD0 <= dom_keycode && dom_keycode <= DOM_VK_NUMPAD9) { + // Numpad numbers + return KEY_KP_0 + (dom_keycode - DOM_VK_NUMPAD0); + } + + if (DOM_VK_F1 <= dom_keycode && dom_keycode <= DOM_VK_F16) { + // F1-F16 + return KEY_F1 + (dom_keycode - DOM_VK_F1); + } + + switch (dom_keycode) { + //case DOM_VK_CANCEL: return KEY_UNKNOWN; + case DOM_VK_HELP: return KEY_HELP; + case DOM_VK_BACK_SPACE: return KEY_BACKSPACE; + case DOM_VK_TAB: return KEY_TAB; + + case DOM_VK_CLEAR: + case DOM_VK_WIN_OEM_CLEAR: // OEM duplicate + return KEY_CLEAR; + + case DOM_VK_RETURN: + case DOM_VK_ENTER: // unused according to MDN + return KEY_ENTER; + + case DOM_VK_SHIFT: return KEY_SHIFT; + case DOM_VK_CONTROL: return KEY_CONTROL; + + case DOM_VK_ALT: + case DOM_VK_ALTGR: + return KEY_ALT; + + case DOM_VK_PAUSE: return KEY_PAUSE; + case DOM_VK_CAPS_LOCK: + return KEY_CAPSLOCK; + + /* + case DOM_VK_KANA: return KEY_UNKNOWN; + case DOM_VK_HANGUL: return KEY_UNKNOWN; + case DOM_VK_EISU: return KEY_UNKNOWN; + case DOM_VK_JUNJA: return KEY_UNKNOWN; + case DOM_VK_FINAL: return KEY_UNKNOWN; + case DOM_VK_HANJA: return KEY_UNKNOWN; + case DOM_VK_KANJI: return KEY_UNKNOWN; + */ + + case DOM_VK_ESCAPE: + return KEY_ESCAPE; + /* + case DOM_VK_CONVERT: return KEY_UNKNOWN; + case DOM_VK_NONCONVERT: return KEY_UNKNOWN; + case DOM_VK_ACCEPT: return KEY_UNKNOWN; + case DOM_VK_MODECHANGE: return KEY_UNKNOWN; + */ + + case DOM_VK_SPACE: return KEY_SPACE; + case DOM_VK_PAGE_UP: return KEY_PAGEUP; + case DOM_VK_PAGE_DOWN: return KEY_PAGEDOWN; + case DOM_VK_END: return KEY_END; + case DOM_VK_HOME: return KEY_HOME; + case DOM_VK_LEFT: return KEY_LEFT; + case DOM_VK_UP: return KEY_UP; + case DOM_VK_RIGHT: return KEY_RIGHT; + case DOM_VK_DOWN: + return KEY_DOWN; + + //case DOM_VK_SELECT: return KEY_UNKNOWN; + + case DOM_VK_PRINTSCREEN: + case DOM_VK_PRINT: + return KEY_PRINT; + + //case DOM_VK_EXECUTE: return KEY_UNKNOWN; + case DOM_VK_INSERT: return KEY_INSERT; + case DOM_VK_DELETE: return KEY_DELETE; + + case DOM_VK_META: + case DOM_VK_WIN: + return KEY_META; + + case DOM_VK_CONTEXT_MENU: return KEY_MENU; + case DOM_VK_SLEEP: + return KEY_STANDBY; + + // Numpad keys + case DOM_VK_MULTIPLY: return KEY_KP_MULTIPLY; + case DOM_VK_ADD: return KEY_KP_ADD; + case DOM_VK_SEPARATOR: + return KEY_KP_PERIOD; // Good enough? + case DOM_VK_SUBTRACT: return KEY_KP_SUBTRACT; + case DOM_VK_DECIMAL: return KEY_KP_PERIOD; + case DOM_VK_DIVIDE: + return KEY_KP_DIVIDE; + + /* + case DOM_VK_F17: return KEY_UNKNOWN; + case DOM_VK_F18: return KEY_UNKNOWN; + case DOM_VK_F19: return KEY_UNKNOWN; + case DOM_VK_F20: return KEY_UNKNOWN; + case DOM_VK_F21: return KEY_UNKNOWN; + case DOM_VK_F22: return KEY_UNKNOWN; + case DOM_VK_F23: return KEY_UNKNOWN; + case DOM_VK_F24: return KEY_UNKNOWN; + */ + + case DOM_VK_NUM_LOCK: return KEY_NUMLOCK; + case DOM_VK_SCROLL_LOCK: + return KEY_SCROLLLOCK; + + /* + case DOM_VK_WIN_OEM_FJ_JISHO: return KEY_UNKNOWN; + case DOM_VK_WIN_OEM_FJ_MASSHOU: return KEY_UNKNOWN; + case DOM_VK_WIN_OEM_FJ_TOUROKU: return KEY_UNKNOWN; + case DOM_VK_WIN_OEM_FJ_LOYA: return KEY_UNKNOWN; + case DOM_VK_WIN_OEM_FJ_ROYA: return KEY_UNKNOWN; + */ + + case DOM_VK_CIRCUMFLEX: return KEY_ASCIICIRCUM; + case DOM_VK_EXCLAMATION: return KEY_EXCLAM; + case DOM_VK_DOUBLE_QUOTE: return KEY_QUOTEDBL; + case DOM_VK_HASH: return KEY_NUMBERSIGN; + case DOM_VK_DOLLAR: return KEY_DOLLAR; + case DOM_VK_PERCENT: return KEY_PERCENT; + case DOM_VK_AMPERSAND: return KEY_AMPERSAND; + case DOM_VK_UNDERSCORE: return KEY_UNDERSCORE; + case DOM_VK_OPEN_PAREN: return KEY_PARENLEFT; + case DOM_VK_CLOSE_PAREN: return KEY_PARENRIGHT; + case DOM_VK_ASTERISK: return KEY_ASTERISK; + case DOM_VK_PLUS: return KEY_PLUS; + case DOM_VK_PIPE: return KEY_BAR; + case DOM_VK_HYPHEN_MINUS: return KEY_MINUS; + case DOM_VK_OPEN_CURLY_BRACKET: return KEY_BRACELEFT; + case DOM_VK_CLOSE_CURLY_BRACKET: return KEY_BRACERIGHT; + case DOM_VK_TILDE: return KEY_ASCIITILDE; + + case DOM_VK_VOLUME_MUTE: return KEY_VOLUMEMUTE; + case DOM_VK_VOLUME_DOWN: return KEY_VOLUMEDOWN; + case DOM_VK_VOLUME_UP: return KEY_VOLUMEUP; + + case DOM_VK_COMMA: return KEY_COMMA; + case DOM_VK_PERIOD: return KEY_PERIOD; + case DOM_VK_SLASH: return KEY_SLASH; + case DOM_VK_BACK_QUOTE: return KEY_QUOTELEFT; + case DOM_VK_OPEN_BRACKET: return KEY_BRACKETLEFT; + case DOM_VK_BACK_SLASH: return KEY_BACKSLASH; + case DOM_VK_CLOSE_BRACKET: return KEY_BRACKETRIGHT; + case DOM_VK_QUOTE: + return KEY_APOSTROPHE; + + // The rest is OEM/unusual. + + default: return KEY_UNKNOWN; + }; +} diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp index 68a2d72464..3829e8d406 100644 --- a/platform/javascript/javascript_main.cpp +++ b/platform/javascript/javascript_main.cpp @@ -28,17 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "emscripten.h" #include "io/resource_loader.h" #include "main/main.h" #include "os_javascript.h" -OS_JavaScript *os = NULL; - -static void main_loop() { - - os->main_loop_iterate(); -} +#include extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) { @@ -46,18 +40,18 @@ extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) { if (!idbfs_err.empty()) { print_line("IndexedDB not available: " + idbfs_err); } - os->set_idbfs_available(idbfs_err.empty()); - // Ease up compatibility + OS_JavaScript *os = OS_JavaScript::get_singleton(); + os->set_idb_available(idbfs_err.empty()); + // Ease up compatibility. ResourceLoader::set_abort_on_missing_resources(false); Main::start(); - os->main_loop_begin(); - emscripten_set_main_loop(main_loop, 0, false); + os->run_async(); } int main(int argc, char *argv[]) { - // sync from persistent state into memory and then - // run the 'main_after_fs_sync' function + // Sync from persistent state into memory and then + // run the 'main_after_fs_sync' function. /* clang-format off */ EM_ASM( FS.mkdir('/userfs'); @@ -68,9 +62,10 @@ int main(int argc, char *argv[]) { ); /* clang-format on */ - os = new OS_JavaScript(argv[0], NULL); - Error err = Main::setup(argv[0], argc - 1, &argv[1]); + new OS_JavaScript(argc, argv); + // TODO: Check error return value. + Main::setup(argv[0], argc - 1, &argv[1]); return 0; - // continued async in main_after_fs_sync() from syncfs() callback + // Continued async in main_after_fs_sync() from the syncfs() callback. } diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index d99bc69f00..c05ae03ec6 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -30,293 +30,196 @@ #include "os_javascript.h" -#include "core/engine.h" -#include "core/io/file_access_buffered_fa.h" -#include "dom_keys.h" -#include "drivers/gles2/rasterizer_gles2.h" -#include "drivers/gles3/rasterizer_gles3.h" -#include "drivers/unix/dir_access_unix.h" -#include "drivers/unix/file_access_unix.h" +#include "gles2/rasterizer_gles2.h" +#include "gles3/rasterizer_gles3.h" +#include "io/file_access_buffered_fa.h" #include "main/main.h" #include "servers/visual/visual_server_raster.h" +#include "unix/dir_access_unix.h" +#include "unix/file_access_unix.h" #include #include +#include "dom_keys.inc" + #define DOM_BUTTON_LEFT 0 #define DOM_BUTTON_MIDDLE 1 #define DOM_BUTTON_RIGHT 2 #define DOM_BUTTON_XBUTTON1 3 #define DOM_BUTTON_XBUTTON2 4 -template -static void dom2godot_mod(T emscripten_event_ptr, Ref godot_event) { - - godot_event->set_shift(emscripten_event_ptr->shiftKey); - godot_event->set_alt(emscripten_event_ptr->altKey); - godot_event->set_control(emscripten_event_ptr->ctrlKey); - godot_event->set_metakey(emscripten_event_ptr->metaKey); -} - -int OS_JavaScript::get_video_driver_count() const { - - return VIDEO_DRIVER_MAX; -} - -const char *OS_JavaScript::get_video_driver_name(int p_driver) const { - - switch (p_driver) { - case VIDEO_DRIVER_GLES3: - return "GLES3"; - case VIDEO_DRIVER_GLES2: - return "GLES2"; - } - ERR_EXPLAIN("Invalid video driver index " + itos(p_driver)); - ERR_FAIL_V(NULL); -} - -int OS_JavaScript::get_audio_driver_count() const { - - return 1; -} +// Window (canvas) -const char *OS_JavaScript::get_audio_driver_name(int p_driver) const { +static void focus_canvas() { - return "JavaScript"; + /* clang-format off */ + EM_ASM( + Module.canvas.focus(); + ); + /* clang-format on */ } -void OS_JavaScript::initialize_core() { +static bool is_canvas_focused() { - OS_Unix::initialize_core(); - FileAccess::make_default >(FileAccess::ACCESS_RESOURCES); + /* clang-format off */ + return EM_ASM_INT_V( + return document.activeElement == Module.canvas; + ); + /* clang-format on */ } -static EM_BOOL _browser_resize_callback(int event_type, const EmscriptenUiEvent *ui_event, void *user_data) { +static bool cursor_inside_canvas = true; - ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_RESIZE, false); +EM_BOOL OS_JavaScript::browser_resize_callback(int p_event_type, const EmscriptenUiEvent *p_event, void *p_user_data) { - OS_JavaScript *os = static_cast(user_data); // The order of the fullscreen change event and the window size change - // event varies, even within just one browser, so defer handling - os->request_canvas_size_adjustment(); + // event varies, even within just one browser, so defer handling. + get_singleton()->canvas_size_adjustment_requested = true; return false; } -static EM_BOOL _fullscreen_change_callback(int event_type, const EmscriptenFullscreenChangeEvent *event, void *user_data) { - - ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_FULLSCREENCHANGE, false); +EM_BOOL OS_JavaScript::fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data) { - OS_JavaScript *os = static_cast(user_data); - String id = String::utf8(event->id); - // empty id is canvas - if (id.empty() || id == "canvas") { - - OS::VideoMode vm = os->get_video_mode(); - // this event property is the only reliable information on - // browser fullscreen state - vm.fullscreen = event->isFullscreen; - os->set_video_mode(vm); - os->request_canvas_size_adjustment(); + OS_JavaScript *os = get_singleton(); + // Empty ID is canvas. + String target_id = String::utf8(p_event->id); + if (target_id.empty() || target_id == "canvas") { + // This event property is the only reliable data on + // browser fullscreen state. + os->video_mode.fullscreen = p_event->isFullscreen; + os->canvas_size_adjustment_requested = true; } return false; } -static InputDefault *_input; - -static bool is_canvas_focused() { +void OS_JavaScript::set_video_mode(const VideoMode &p_video_mode, int p_screen) { - /* clang-format off */ - return EM_ASM_INT_V( - return document.activeElement == Module.canvas; - ); - /* clang-format on */ + video_mode = p_video_mode; } -static void focus_canvas() { +OS::VideoMode OS_JavaScript::get_video_mode(int p_screen) const { - /* clang-format off */ - EM_ASM( - Module.canvas.focus(); - ); - /* clang-format on */ + return video_mode; } -static bool _cursor_inside_canvas = true; - -static bool is_cursor_inside_canvas() { +Size2 OS_JavaScript::get_screen_size(int p_screen) const { - return _cursor_inside_canvas; + EmscriptenFullscreenChangeEvent ev; + EMSCRIPTEN_RESULT result = emscripten_get_fullscreen_status(&ev); + ERR_FAIL_COND_V(result != EMSCRIPTEN_RESULT_SUCCESS, Size2()); + return Size2(ev.screenWidth, ev.screenHeight); } -static EM_BOOL _mousebutton_callback(int event_type, const EmscriptenMouseEvent *mouse_event, void *user_data) { - - ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_MOUSEDOWN && event_type != EMSCRIPTEN_EVENT_MOUSEUP, false); - - Ref ev; - ev.instance(); - ev->set_pressed(event_type == EMSCRIPTEN_EVENT_MOUSEDOWN); - ev->set_position(Point2(mouse_event->canvasX, mouse_event->canvasY)); - ev->set_global_position(ev->get_position()); - dom2godot_mod(mouse_event, ev); - switch (mouse_event->button) { - case DOM_BUTTON_LEFT: ev->set_button_index(BUTTON_LEFT); break; - case DOM_BUTTON_MIDDLE: ev->set_button_index(BUTTON_MIDDLE); break; - case DOM_BUTTON_RIGHT: ev->set_button_index(BUTTON_RIGHT); break; - case DOM_BUTTON_XBUTTON1: ev->set_button_index(BUTTON_XBUTTON1); break; - case DOM_BUTTON_XBUTTON2: ev->set_button_index(BUTTON_XBUTTON2); break; - default: return false; - } +void OS_JavaScript::set_window_size(const Size2 p_size) { - int mask = _input->get_mouse_button_mask(); - int button_flag = 1 << (ev->get_button_index() - 1); - if (ev->is_pressed()) { - // Since the event is consumed, focus manually. The containing iframe, - // if used, may not have focus yet, so focus even if already focused. - focus_canvas(); - mask |= button_flag; - } else if (mask & button_flag) { - mask &= ~button_flag; + windowed_size = p_size; + if (is_window_fullscreen()) { + window_maximized = false; + set_window_fullscreen(false); + } else if (is_window_maximized()) { + set_window_maximized(false); } else { - // release event, but press was outside the canvas, so ignore - return false; + video_mode.width = p_size.x; + video_mode.height = p_size.y; + emscripten_set_canvas_size(p_size.x, p_size.y); } - ev->set_button_mask(mask); - - _input->parse_input_event(ev); - // Prevent multi-click text selection and wheel-click scrolling anchor. - // Context menu is prevented through contextmenu event. - return true; } -static EM_BOOL _mousemove_callback(int event_type, const EmscriptenMouseEvent *mouse_event, void *user_data) { - - ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_MOUSEMOVE, false); - OS_JavaScript *os = static_cast(user_data); - int input_mask = _input->get_mouse_button_mask(); - Point2 pos = Point2(mouse_event->canvasX, mouse_event->canvasY); - // outside the canvas, only read mouse movement if dragging started inside - // the canvas; imitating desktop app behaviour - if (!is_cursor_inside_canvas() && !input_mask) - return false; - - Ref ev; - ev.instance(); - dom2godot_mod(mouse_event, ev); - ev->set_button_mask(input_mask); - - ev->set_position(pos); - ev->set_global_position(ev->get_position()); - - ev->set_relative(Vector2(mouse_event->movementX, mouse_event->movementY)); - _input->set_mouse_position(ev->get_position()); - ev->set_speed(_input->get_last_mouse_speed()); +Size2 OS_JavaScript::get_window_size() const { - _input->parse_input_event(ev); - // don't suppress mouseover/leave events - return false; + int canvas[3]; + emscripten_get_canvas_size(canvas, canvas + 1, canvas + 2); + return Size2(canvas[0], canvas[1]); } -static EM_BOOL _wheel_callback(int event_type, const EmscriptenWheelEvent *wheel_event, void *user_data) { +void OS_JavaScript::set_window_maximized(bool p_enabled) { - ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_WHEEL, false); - if (!is_canvas_focused()) { - if (is_cursor_inside_canvas()) { - focus_canvas(); - } else { - return false; - } + window_maximized = p_enabled; + if (is_window_fullscreen()) { + set_window_fullscreen(false); + return; } + // Calling emscripten_enter_soft_fullscreen mutltiple times hides all + // page elements except the canvas permanently, so track state. + if (p_enabled && !soft_fullscreen_enabled) { - Ref ev; - ev.instance(); - ev->set_button_mask(_input->get_mouse_button_mask()); - ev->set_position(_input->get_mouse_position()); - ev->set_global_position(ev->get_position()); - - ev->set_shift(_input->is_key_pressed(KEY_SHIFT)); - ev->set_alt(_input->is_key_pressed(KEY_ALT)); - ev->set_control(_input->is_key_pressed(KEY_CONTROL)); - ev->set_metakey(_input->is_key_pressed(KEY_META)); - - if (wheel_event->deltaY < 0) - ev->set_button_index(BUTTON_WHEEL_UP); - else if (wheel_event->deltaY > 0) - ev->set_button_index(BUTTON_WHEEL_DOWN); - else if (wheel_event->deltaX > 0) - ev->set_button_index(BUTTON_WHEEL_LEFT); - else if (wheel_event->deltaX < 0) - ev->set_button_index(BUTTON_WHEEL_RIGHT); - else - return false; - - // Different browsers give wildly different delta values, and we can't - // interpret deltaMode, so use default value for wheel events' factor + EmscriptenFullscreenStrategy strategy; + strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; + strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF; + strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT; + strategy.canvasResizedCallback = NULL; + emscripten_enter_soft_fullscreen(NULL, &strategy); + soft_fullscreen_enabled = true; + video_mode.width = get_window_size().width; + video_mode.height = get_window_size().height; + } else if (!p_enabled) { - ev->set_pressed(true); - _input->parse_input_event(ev); + emscripten_exit_soft_fullscreen(); + soft_fullscreen_enabled = false; + video_mode.width = windowed_size.width; + video_mode.height = windowed_size.height; + emscripten_set_canvas_size(video_mode.width, video_mode.height); + } +} - ev->set_pressed(false); - _input->parse_input_event(ev); +bool OS_JavaScript::is_window_maximized() const { - return true; + return window_maximized; } -static Point2 _prev_touches[32]; - -static EM_BOOL _touchpress_callback(int event_type, const EmscriptenTouchEvent *touch_event, void *user_data) { +void OS_JavaScript::set_window_fullscreen(bool p_enabled) { - ERR_FAIL_COND_V( - event_type != EMSCRIPTEN_EVENT_TOUCHSTART && - event_type != EMSCRIPTEN_EVENT_TOUCHEND && - event_type != EMSCRIPTEN_EVENT_TOUCHCANCEL, - false); + if (p_enabled == is_window_fullscreen()) { + return; + } - Ref ev; - ev.instance(); - int lowest_id_index = -1; - for (int i = 0; i < touch_event->numTouches; ++i) { + // Just request changes here, if successful, canvas is resized in + // _browser_resize_callback or _fullscreen_change_callback. + EMSCRIPTEN_RESULT result; + if (p_enabled) { + if (window_maximized) { + // Soft fullsreen during real fulllscreen can cause issues. + set_window_maximized(false); + window_maximized = true; + } + EmscriptenFullscreenStrategy strategy; + strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; + strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF; + strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT; + strategy.canvasResizedCallback = NULL; + emscripten_request_fullscreen_strategy(NULL, false, &strategy); + } else { + result = emscripten_exit_fullscreen(); + if (result != EMSCRIPTEN_RESULT_SUCCESS) { + ERR_PRINTS("Failed to exit fullscreen: Code " + itos(result)); + } + } +} - const EmscriptenTouchPoint &touch = touch_event->touches[i]; - if (lowest_id_index == -1 || touch.identifier < touch_event->touches[lowest_id_index].identifier) - lowest_id_index = i; - if (!touch.isChanged) - continue; - ev->set_index(touch.identifier); - ev->set_position(Point2(touch.canvasX, touch.canvasY)); - _prev_touches[i] = ev->get_position(); - ev->set_pressed(event_type == EMSCRIPTEN_EVENT_TOUCHSTART); +bool OS_JavaScript::is_window_fullscreen() const { - _input->parse_input_event(ev); - } - return true; + return video_mode.fullscreen; } -static EM_BOOL _touchmove_callback(int event_type, const EmscriptenTouchEvent *touch_event, void *user_data) { +void OS_JavaScript::get_fullscreen_mode_list(List *p_list, int p_screen) const { - ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_TOUCHMOVE, false); + Size2 screen = get_screen_size(); + p_list->push_back(OS::VideoMode(screen.width, screen.height, true)); +} - Ref ev; - ev.instance(); - int lowest_id_index = -1; - for (int i = 0; i < touch_event->numTouches; ++i) { +// Keys - const EmscriptenTouchPoint &touch = touch_event->touches[i]; - if (lowest_id_index == -1 || touch.identifier < touch_event->touches[lowest_id_index].identifier) - lowest_id_index = i; - if (!touch.isChanged) - continue; - ev->set_index(touch.identifier); - ev->set_position(Point2(touch.canvasX, touch.canvasY)); - Point2 &prev = _prev_touches[i]; - ev->set_relative(ev->get_position() - prev); - prev = ev->get_position(); +template +static void dom2godot_mod(T *emscripten_event_ptr, Ref godot_event) { - _input->parse_input_event(ev); - } - return true; + godot_event->set_shift(emscripten_event_ptr->shiftKey); + godot_event->set_alt(emscripten_event_ptr->altKey); + godot_event->set_control(emscripten_event_ptr->ctrlKey); + godot_event->set_metakey(emscripten_event_ptr->metaKey); } -static Ref _setup_key_event(const EmscriptenKeyboardEvent *emscripten_event) { +static Ref setup_key_event(const EmscriptenKeyboardEvent *emscripten_event) { Ref ev; ev.instance(); @@ -325,9 +228,9 @@ static Ref _setup_key_event(const EmscriptenKeyboardEvent *emscri ev->set_scancode(dom2godot_scancode(emscripten_event->keyCode)); String unicode = String::utf8(emscripten_event->key); - // check if empty or multi-character (e.g. `CapsLock`) + // Check if empty or multi-character (e.g. `CapsLock`). if (unicode.length() != 1) { - // might be empty as well, but better than nonsense + // Might be empty as well, but better than nonsense. unicode = String::utf8(emscripten_event->charValue); } if (unicode.length() == 1) { @@ -337,175 +240,115 @@ static Ref _setup_key_event(const EmscriptenKeyboardEvent *emscri return ev; } -static Ref deferred_key_event; - -static EM_BOOL _keydown_callback(int event_type, const EmscriptenKeyboardEvent *key_event, void *user_data) { - - ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_KEYDOWN, false); +EM_BOOL OS_JavaScript::keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) { - Ref ev = _setup_key_event(key_event); + OS_JavaScript *os = get_singleton(); + Ref ev = setup_key_event(p_event); ev->set_pressed(true); if (ev->get_unicode() == 0 && keycode_has_unicode(ev->get_scancode())) { - // defer to keypress event for legacy unicode retrieval - deferred_key_event = ev; - return false; // do not suppress keypress event + // Defer to keypress event for legacy unicode retrieval. + os->deferred_key_event = ev; + // Do not suppress keypress event. + return false; } - _input->parse_input_event(ev); + os->input->parse_input_event(ev); return true; } -static EM_BOOL _keypress_callback(int event_type, const EmscriptenKeyboardEvent *key_event, void *user_data) { - - ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_KEYPRESS, false); +EM_BOOL OS_JavaScript::keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) { - deferred_key_event->set_unicode(key_event->charCode); - _input->parse_input_event(deferred_key_event); + OS_JavaScript *os = get_singleton(); + os->deferred_key_event->set_unicode(p_event->charCode); + os->input->parse_input_event(os->deferred_key_event); return true; } -static EM_BOOL _keyup_callback(int event_type, const EmscriptenKeyboardEvent *key_event, void *user_data) { - - ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_KEYUP, false); +EM_BOOL OS_JavaScript::keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) { - Ref ev = _setup_key_event(key_event); + Ref ev = setup_key_event(p_event); ev->set_pressed(false); - _input->parse_input_event(ev); + get_singleton()->input->parse_input_event(ev); return ev->get_scancode() != KEY_UNKNOWN && ev->get_scancode() != 0; } -static EM_BOOL joy_callback_func(int p_type, const EmscriptenGamepadEvent *p_event, void *p_user) { - OS_JavaScript *os = (OS_JavaScript *)OS::get_singleton(); - if (os) { - return os->joy_connection_changed(p_type, p_event); - } - return false; +// Mouse + +Point2 OS_JavaScript::get_mouse_position() const { + + return input->get_mouse_position(); } -extern "C" EMSCRIPTEN_KEEPALIVE void send_notification(int notif) { +int OS_JavaScript::get_mouse_button_state() const { - if (notif == MainLoop::NOTIFICATION_WM_MOUSE_ENTER || notif == MainLoop::NOTIFICATION_WM_MOUSE_EXIT) { - _cursor_inside_canvas = notif == MainLoop::NOTIFICATION_WM_MOUSE_ENTER; - } - OS_JavaScript::get_singleton()->get_main_loop()->notification(notif); + return input->get_mouse_button_mask(); } -Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { +EM_BOOL OS_JavaScript::mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data) { - print_line("Init OS"); + OS_JavaScript *os = get_singleton(); - EmscriptenWebGLContextAttributes attributes; - emscripten_webgl_init_context_attributes(&attributes); - attributes.alpha = false; - attributes.antialias = false; - ERR_FAIL_INDEX_V(p_video_driver, VIDEO_DRIVER_MAX, ERR_INVALID_PARAMETER); - switch (p_video_driver) { - case VIDEO_DRIVER_GLES3: - attributes.majorVersion = 2; - RasterizerGLES3::register_config(); - RasterizerGLES3::make_current(); - break; - case VIDEO_DRIVER_GLES2: - attributes.majorVersion = 1; - RasterizerGLES2::register_config(); - RasterizerGLES2::make_current(); - break; + Ref ev; + ev.instance(); + ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_MOUSEDOWN); + ev->set_position(Point2(p_event->canvasX, p_event->canvasY)); + ev->set_global_position(ev->get_position()); + dom2godot_mod(p_event, ev); + switch (p_event->button) { + case DOM_BUTTON_LEFT: ev->set_button_index(BUTTON_LEFT); break; + case DOM_BUTTON_MIDDLE: ev->set_button_index(BUTTON_MIDDLE); break; + case DOM_BUTTON_RIGHT: ev->set_button_index(BUTTON_RIGHT); break; + case DOM_BUTTON_XBUTTON1: ev->set_button_index(BUTTON_XBUTTON1); break; + case DOM_BUTTON_XBUTTON2: ev->set_button_index(BUTTON_XBUTTON2); break; + default: return false; } - EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(NULL, &attributes); - ERR_EXPLAIN("WebGL " + itos(attributes.majorVersion) + ".0 not available"); - ERR_FAIL_COND_V(emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS, ERR_UNAVAILABLE); - video_mode = p_desired; - // can't fulfil fullscreen request due to browser security - video_mode.fullscreen = false; - /* clang-format off */ - if (EM_ASM_INT_V({ return Module.resizeCanvasOnStart })) { - /* clang-format on */ - set_window_size(Size2(video_mode.width, video_mode.height)); + int mask = os->input->get_mouse_button_mask(); + int button_flag = 1 << (ev->get_button_index() - 1); + if (ev->is_pressed()) { + // Since the event is consumed, focus manually. The containing iframe, + // if exists, may not have focus yet, so focus even if already focused. + focus_canvas(); + mask |= button_flag; + } else if (mask & button_flag) { + mask &= ~button_flag; } else { - set_window_size(get_window_size()); + // Received release event, but press was outside the canvas, so ignore. + return false; } + ev->set_button_mask(mask); - char locale_ptr[16]; - /* clang-format off */ - EM_ASM_ARGS({ - stringToUTF8(Module.locale, $0, 16); - }, locale_ptr); - /* clang-format on */ - setenv("LANG", locale_ptr, true); - - print_line("Init Audio"); - - AudioDriverManager::initialize(p_audio_driver); - - print_line("Init VS"); - - visual_server = memnew(VisualServerRaster()); - // visual_server->cursor_set_visible(false, 0); - - print_line("Init Physicsserver"); - - input = memnew(InputDefault); - _input = input; - -#define EM_CHECK(ev) \ - if (result != EMSCRIPTEN_RESULT_SUCCESS) \ - ERR_PRINTS("Error while setting " #ev " callback: Code " + itos(result)) -#define SET_EM_CALLBACK(target, ev, cb) \ - result = emscripten_set_##ev##_callback(target, this, true, &cb); \ - EM_CHECK(ev) -#define SET_EM_CALLBACK_NODATA(ev, cb) \ - result = emscripten_set_##ev##_callback(NULL, true, &cb); \ - EM_CHECK(ev) - - EMSCRIPTEN_RESULT result; - SET_EM_CALLBACK("#window", mousemove, _mousemove_callback) - SET_EM_CALLBACK("#canvas", mousedown, _mousebutton_callback) - SET_EM_CALLBACK("#window", mouseup, _mousebutton_callback) - SET_EM_CALLBACK("#window", wheel, _wheel_callback) - SET_EM_CALLBACK("#window", touchstart, _touchpress_callback) - SET_EM_CALLBACK("#window", touchmove, _touchmove_callback) - SET_EM_CALLBACK("#window", touchend, _touchpress_callback) - SET_EM_CALLBACK("#window", touchcancel, _touchpress_callback) - SET_EM_CALLBACK("#canvas", keydown, _keydown_callback) - SET_EM_CALLBACK("#canvas", keypress, _keypress_callback) - SET_EM_CALLBACK("#canvas", keyup, _keyup_callback) - SET_EM_CALLBACK(NULL, resize, _browser_resize_callback) - SET_EM_CALLBACK(NULL, fullscreenchange, _fullscreen_change_callback) - SET_EM_CALLBACK_NODATA(gamepadconnected, joy_callback_func) - SET_EM_CALLBACK_NODATA(gamepaddisconnected, joy_callback_func) - -#undef SET_EM_CALLBACK_NODATA -#undef SET_EM_CALLBACK -#undef EM_CHECK - - visual_server->init(); - - return OK; + os->input->parse_input_event(ev); + // Prevent multi-click text selection and wheel-click scrolling anchor. + // Context menu is prevented through contextmenu event. + return true; } -void OS_JavaScript::set_main_loop(MainLoop *p_main_loop) { +EM_BOOL OS_JavaScript::mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data) { - main_loop = p_main_loop; - input->set_main_loop(p_main_loop); -} + OS_JavaScript *os = get_singleton(); -void OS_JavaScript::delete_main_loop() { - - memdelete(main_loop); -} + int input_mask = os->input->get_mouse_button_mask(); + Point2 pos = Point2(p_event->canvasX, p_event->canvasY); + // For motion outside the canvas, only read mouse movement if dragging + // started inside the canvas; imitating desktop app behaviour. + if (!cursor_inside_canvas && !input_mask) + return false; -void OS_JavaScript::finalize() { + Ref ev; + ev.instance(); + dom2godot_mod(p_event, ev); + ev->set_button_mask(input_mask); - memdelete(input); -} + ev->set_position(pos); + ev->set_global_position(ev->get_position()); -void OS_JavaScript::alert(const String &p_alert, const String &p_title) { + ev->set_relative(Vector2(p_event->movementX, p_event->movementY)); + os->input->set_mouse_position(ev->get_position()); + ev->set_speed(os->input->get_last_mouse_speed()); - /* clang-format off */ - EM_ASM_({ - window.alert(UTF8ToString($0)); - }, p_alert.utf8().get_data()); - /* clang-format on */ + os->input->parse_input_event(ev); + // Don't suppress mouseover/-leave events. + return false; } static const char *godot2dom_cursor(OS::CursorShape p_shape) { @@ -533,7 +376,7 @@ static const char *godot2dom_cursor(OS::CursorShape p_shape) { } } -void OS_JavaScript::set_css_cursor(const char *p_cursor) { +static void set_css_cursor(const char *p_cursor) { /* clang-format off */ EM_ASM_({ @@ -542,7 +385,7 @@ void OS_JavaScript::set_css_cursor(const char *p_cursor) { /* clang-format on */ } -const char *OS_JavaScript::get_css_cursor() const { +static const char *get_css_cursor() { char cursor[16]; /* clang-format off */ @@ -553,9 +396,20 @@ const char *OS_JavaScript::get_css_cursor() const { return cursor; } +void OS_JavaScript::set_cursor_shape(CursorShape p_shape) { + + ERR_FAIL_INDEX(p_shape, CURSOR_MAX); + + cursor_shape = p_shape; + if (get_mouse_mode() != MOUSE_MODE_HIDDEN) + set_css_cursor(godot2dom_cursor(cursor_shape)); +} + +void OS_JavaScript::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { +} + void OS_JavaScript::set_mouse_mode(OS::MouseMode p_mode) { - ERR_FAIL_INDEX(p_mode, MOUSE_MODE_CONFINED + 1); ERR_EXPLAIN("MOUSE_MODE_CONFINED is not supported for the HTML5 platform"); ERR_FAIL_COND(p_mode == MOUSE_MODE_CONFINED); if (p_mode == get_mouse_mode()) @@ -583,190 +437,303 @@ void OS_JavaScript::set_mouse_mode(OS::MouseMode p_mode) { OS::MouseMode OS_JavaScript::get_mouse_mode() const { - if (!strcmp(get_css_cursor(), "none")) + if (String::utf8(get_css_cursor()) == "none") return MOUSE_MODE_HIDDEN; EmscriptenPointerlockChangeEvent ev; emscripten_get_pointerlock_status(&ev); - return ev.isActive && (strcmp(ev.id, "canvas") == 0) ? MOUSE_MODE_CAPTURED : MOUSE_MODE_VISIBLE; + return (ev.isActive && String::utf8(ev.id) == "canvas") ? MOUSE_MODE_CAPTURED : MOUSE_MODE_VISIBLE; } -Point2 OS_JavaScript::get_mouse_position() const { +// Wheel - return input->get_mouse_position(); -} +EM_BOOL OS_JavaScript::wheel_callback(int p_event_type, const EmscriptenWheelEvent *p_event, void *p_user_data) { -int OS_JavaScript::get_mouse_button_state() const { + ERR_FAIL_COND_V(p_event_type != EMSCRIPTEN_EVENT_WHEEL, false); + if (!is_canvas_focused()) { + if (cursor_inside_canvas) { + focus_canvas(); + } else { + return false; + } + } - return input->get_mouse_button_mask(); -} + InputDefault *input = get_singleton()->input; + Ref ev; + ev.instance(); + ev->set_button_mask(input->get_mouse_button_mask()); + ev->set_position(input->get_mouse_position()); + ev->set_global_position(ev->get_position()); -void OS_JavaScript::set_window_title(const String &p_title) { + ev->set_shift(input->is_key_pressed(KEY_SHIFT)); + ev->set_alt(input->is_key_pressed(KEY_ALT)); + ev->set_control(input->is_key_pressed(KEY_CONTROL)); + ev->set_metakey(input->is_key_pressed(KEY_META)); - /* clang-format off */ - EM_ASM_({ - document.title = UTF8ToString($0); - }, p_title.utf8().get_data()); - /* clang-format on */ -} + if (p_event->deltaY < 0) + ev->set_button_index(BUTTON_WHEEL_UP); + else if (p_event->deltaY > 0) + ev->set_button_index(BUTTON_WHEEL_DOWN); + else if (p_event->deltaX > 0) + ev->set_button_index(BUTTON_WHEEL_LEFT); + else if (p_event->deltaX < 0) + ev->set_button_index(BUTTON_WHEEL_RIGHT); + else + return false; -//interesting byt not yet -//void set_clipboard(const String& p_text); -//String get_clipboard() const; + // Different browsers give wildly different delta values, and we can't + // interpret deltaMode, so use default value for wheel events' factor. -void OS_JavaScript::set_video_mode(const VideoMode &p_video_mode, int p_screen) { + ev->set_pressed(true); + input->parse_input_event(ev); - video_mode = p_video_mode; + ev->set_pressed(false); + input->parse_input_event(ev); + + return true; } -OS::VideoMode OS_JavaScript::get_video_mode(int p_screen) const { +// Touch - return video_mode; +bool OS_JavaScript::has_touchscreen_ui_hint() const { + + /* clang-format off */ + return EM_ASM_INT_V( + return 'ontouchstart' in window; + ); + /* clang-format on */ } -Size2 OS_JavaScript::get_screen_size(int p_screen) const { +EM_BOOL OS_JavaScript::touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data) { - EmscriptenFullscreenChangeEvent ev; - EMSCRIPTEN_RESULT result = emscripten_get_fullscreen_status(&ev); - ERR_FAIL_COND_V(result != EMSCRIPTEN_RESULT_SUCCESS, Size2()); - return Size2(ev.screenWidth, ev.screenHeight); -} + OS_JavaScript *os = get_singleton(); + Ref ev; + ev.instance(); + int lowest_id_index = -1; + for (int i = 0; i < p_event->numTouches; ++i) { -void OS_JavaScript::set_window_size(const Size2 p_size) { + const EmscriptenTouchPoint &touch = p_event->touches[i]; + if (lowest_id_index == -1 || touch.identifier < p_event->touches[lowest_id_index].identifier) + lowest_id_index = i; + if (!touch.isChanged) + continue; + ev->set_index(touch.identifier); + ev->set_position(Point2(touch.canvasX, touch.canvasY)); + os->touches[i] = ev->get_position(); + ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_TOUCHSTART); - windowed_size = p_size; - if (is_window_fullscreen()) { - window_maximized = false; - set_window_fullscreen(false); - } else if (is_window_maximized()) { - set_window_maximized(false); - } else { - video_mode.width = p_size.x; - video_mode.height = p_size.y; - emscripten_set_canvas_size(p_size.x, p_size.y); + os->input->parse_input_event(ev); } + return true; } -Size2 OS_JavaScript::get_window_size() const { +EM_BOOL OS_JavaScript::touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data) { - int canvas[3]; - emscripten_get_canvas_size(canvas, canvas + 1, canvas + 2); - return Size2(canvas[0], canvas[1]); -} + OS_JavaScript *os = get_singleton(); + Ref ev; + ev.instance(); + int lowest_id_index = -1; + for (int i = 0; i < p_event->numTouches; ++i) { -void OS_JavaScript::set_window_maximized(bool p_enabled) { + const EmscriptenTouchPoint &touch = p_event->touches[i]; + if (lowest_id_index == -1 || touch.identifier < p_event->touches[lowest_id_index].identifier) + lowest_id_index = i; + if (!touch.isChanged) + continue; + ev->set_index(touch.identifier); + ev->set_position(Point2(touch.canvasX, touch.canvasY)); + Point2 &prev = os->touches[i]; + ev->set_relative(ev->get_position() - prev); + prev = ev->get_position(); - window_maximized = p_enabled; - if (is_window_fullscreen()) { - set_window_fullscreen(false); - return; + os->input->parse_input_event(ev); } - // Calling emscripten_enter_soft_fullscreen mutltiple times hides all - // page elements except the canvas permanently, so track state - if (p_enabled && !soft_fs_enabled) { + return true; +} - EmscriptenFullscreenStrategy strategy; - strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; - strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF; - strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT; - strategy.canvasResizedCallback = NULL; - emscripten_enter_soft_fullscreen(NULL, &strategy); - soft_fs_enabled = true; - video_mode.width = get_window_size().width; - video_mode.height = get_window_size().height; - } else if (!p_enabled) { +// Gamepad - emscripten_exit_soft_fullscreen(); - soft_fs_enabled = false; - video_mode.width = windowed_size.width; - video_mode.height = windowed_size.height; - emscripten_set_canvas_size(video_mode.width, video_mode.height); +EM_BOOL OS_JavaScript::gamepad_change_callback(int p_event_type, const EmscriptenGamepadEvent *p_event, void *p_user_data) { + + InputDefault *input = get_singleton()->input; + if (p_event_type == EMSCRIPTEN_EVENT_GAMEPADCONNECTED) { + + String guid = ""; + if (String::utf8(p_event->mapping) == "standard") + guid = "Default HTML5 Gamepad"; + input->joy_connection_changed(p_event->index, true, String::utf8(p_event->id), guid); + } else { + input->joy_connection_changed(p_event->index, false, ""); } + return true; } -void OS_JavaScript::set_window_fullscreen(bool p_enable) { +void OS_JavaScript::process_joypads() { - if (p_enable == is_window_fullscreen()) { - return; - } + int joypad_count = emscripten_get_num_gamepads(); + for (int joypad = 0; joypad < joypad_count; joypad++) { + EmscriptenGamepadEvent state; + emscripten_get_gamepad_status(joypad, &state); + if (state.connected) { - // only requesting changes here, if successful, canvas is resized in - // _browser_resize_callback or _fullscreen_change_callback - EMSCRIPTEN_RESULT result; - if (p_enable) { - if (window_maximized) { - // soft fs during real fs can cause issues - set_window_maximized(false); - window_maximized = true; - } - EmscriptenFullscreenStrategy strategy; - strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; - strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF; - strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT; - strategy.canvasResizedCallback = NULL; - emscripten_request_fullscreen_strategy(NULL, false, &strategy); - } else { - result = emscripten_exit_fullscreen(); - if (result != EMSCRIPTEN_RESULT_SUCCESS) { - ERR_PRINTS("Failed to exit fullscreen: Code " + itos(result)); + int button_count = MIN(state.numButtons, 18); + int axis_count = MIN(state.numAxes, 8); + for (int button = 0; button < button_count; button++) { + + float value = state.analogButton[button]; + if (String::utf8(state.mapping) == "standard" && (button == JOY_ANALOG_L2 || button == JOY_ANALOG_R2)) { + InputDefault::JoyAxis joy_axis; + joy_axis.min = 0; + joy_axis.value = value; + input->joy_axis(joypad, button, joy_axis); + } else { + input->joy_button(joypad, button, value); + } + } + for (int axis = 0; axis < axis_count; axis++) { + + InputDefault::JoyAxis joy_axis; + joy_axis.min = -1; + joy_axis.value = state.axis[axis]; + input->joy_axis(joypad, axis, joy_axis); + } } } } -bool OS_JavaScript::is_window_fullscreen() const { +bool OS_JavaScript::is_joy_known(int p_device) { - return video_mode.fullscreen; + return input->is_joy_mapped(p_device); } -void OS_JavaScript::request_canvas_size_adjustment() { +String OS_JavaScript::get_joy_guid(int p_device) const { - canvas_size_adjustment_requested = true; + return input->get_joy_guid_remapped(p_device); } -void OS_JavaScript::get_fullscreen_mode_list(List *p_list, int p_screen) const { +// Video - Size2 screen = get_screen_size(); - p_list->push_back(OS::VideoMode(screen.width, screen.height, true)); +int OS_JavaScript::get_video_driver_count() const { + + return VIDEO_DRIVER_MAX; } -String OS_JavaScript::get_name() { +const char *OS_JavaScript::get_video_driver_name(int p_driver) const { - return "HTML5"; + switch (p_driver) { + case VIDEO_DRIVER_GLES3: + return "GLES3"; + case VIDEO_DRIVER_GLES2: + return "GLES2"; + } + ERR_EXPLAIN("Invalid video driver index " + itos(p_driver)); + ERR_FAIL_V(NULL); } -MainLoop *OS_JavaScript::get_main_loop() const { +// Audio - return main_loop; +int OS_JavaScript::get_audio_driver_count() const { + + return 1; } -bool OS_JavaScript::can_draw() const { +const char *OS_JavaScript::get_audio_driver_name(int p_driver) const { - return true; //always? + return "JavaScript"; } -void OS_JavaScript::set_cursor_shape(CursorShape p_shape) { +// Lifecycle - ERR_FAIL_INDEX(p_shape, CURSOR_MAX); +void OS_JavaScript::initialize_core() { - cursor_shape = p_shape; - if (get_mouse_mode() != MOUSE_MODE_HIDDEN) - set_css_cursor(godot2dom_cursor(cursor_shape)); + OS_Unix::initialize_core(); + FileAccess::make_default >(FileAccess::ACCESS_RESOURCES); } -void OS_JavaScript::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { -} +Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { + + EmscriptenWebGLContextAttributes attributes; + emscripten_webgl_init_context_attributes(&attributes); + attributes.alpha = false; + attributes.antialias = false; + ERR_FAIL_INDEX_V(p_video_driver, VIDEO_DRIVER_MAX, ERR_INVALID_PARAMETER); + switch (p_video_driver) { + case VIDEO_DRIVER_GLES3: + attributes.majorVersion = 2; + RasterizerGLES3::register_config(); + RasterizerGLES3::make_current(); + break; + case VIDEO_DRIVER_GLES2: + attributes.majorVersion = 1; + RasterizerGLES2::register_config(); + RasterizerGLES2::make_current(); + break; + } + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(NULL, &attributes); + ERR_EXPLAIN("WebGL " + itos(attributes.majorVersion) + ".0 not available"); + ERR_FAIL_COND_V(emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS, ERR_UNAVAILABLE); + + video_mode = p_desired; + // Can't fulfil fullscreen request during start-up due to browser security. + video_mode.fullscreen = false; + /* clang-format off */ + if (EM_ASM_INT_V({ return Module.resizeCanvasOnStart })) { + /* clang-format on */ + set_window_size(Size2(video_mode.width, video_mode.height)); + } else { + set_window_size(get_window_size()); + } + + char locale_ptr[16]; + /* clang-format off */ + EM_ASM_ARGS({ + stringToUTF8(Module.locale, $0, 16); + }, locale_ptr); + /* clang-format on */ + setenv("LANG", locale_ptr, true); -void OS_JavaScript::main_loop_begin() { + AudioDriverManager::initialize(p_audio_driver); + VisualServer *visual_server = memnew(VisualServerRaster()); + input = memnew(InputDefault); - if (main_loop) - main_loop->init(); + EMSCRIPTEN_RESULT result; +#define EM_CHECK(ev) \ + if (result != EMSCRIPTEN_RESULT_SUCCESS) \ + ERR_PRINTS("Error while setting " #ev " callback: Code " + itos(result)) +#define SET_EM_CALLBACK(target, ev, cb) \ + result = emscripten_set_##ev##_callback(target, NULL, true, &cb); \ + EM_CHECK(ev) +#define SET_EM_CALLBACK_NOTARGET(ev, cb) \ + result = emscripten_set_##ev##_callback(NULL, true, &cb); \ + EM_CHECK(ev) + // These callbacks from Emscripten's html5.h suffice to access most + // JavaScript APIs. For APIs that are not (sufficiently) exposed, EM_ASM + // is used below. + SET_EM_CALLBACK("#window", mousemove, mousemove_callback) + SET_EM_CALLBACK("#canvas", mousedown, mouse_button_callback) + SET_EM_CALLBACK("#window", mouseup, mouse_button_callback) + SET_EM_CALLBACK("#window", wheel, wheel_callback) + SET_EM_CALLBACK("#window", touchstart, touch_press_callback) + SET_EM_CALLBACK("#window", touchmove, touchmove_callback) + SET_EM_CALLBACK("#window", touchend, touch_press_callback) + SET_EM_CALLBACK("#window", touchcancel, touch_press_callback) + SET_EM_CALLBACK("#canvas", keydown, keydown_callback) + SET_EM_CALLBACK("#canvas", keypress, keypress_callback) + SET_EM_CALLBACK("#canvas", keyup, keyup_callback) + SET_EM_CALLBACK(NULL, resize, browser_resize_callback) + SET_EM_CALLBACK(NULL, fullscreenchange, fullscreen_change_callback) + SET_EM_CALLBACK_NOTARGET(gamepadconnected, gamepad_change_callback) + SET_EM_CALLBACK_NOTARGET(gamepaddisconnected, gamepad_change_callback) +#undef SET_EM_CALLBACK_NODATA +#undef SET_EM_CALLBACK +#undef EM_CHECK /* clang-format off */ EM_ASM_ARGS({ const send_notification = cwrap('send_notification', null, ['number']); - const notifs = arguments; - (['mouseover', 'mouseleave', 'focus', 'blur']).forEach(function(event, i) { - Module.canvas.addEventListener(event, send_notification.bind(null, notifs[i])); + const notifications = arguments; + (['mouseover', 'mouseleave', 'focus', 'blur']).forEach(function(event, index) { + Module.canvas.addEventListener(event, send_notification.bind(null, notifications[index])); }); }, MainLoop::NOTIFICATION_WM_MOUSE_ENTER, @@ -775,22 +742,44 @@ void OS_JavaScript::main_loop_begin() { MainLoop::NOTIFICATION_WM_FOCUS_OUT ); /* clang-format on */ + + visual_server->init(); + + return OK; } -bool OS_JavaScript::main_loop_iterate() { +void OS_JavaScript::set_main_loop(MainLoop *p_main_loop) { - if (!main_loop) - return false; + main_loop = p_main_loop; + input->set_main_loop(p_main_loop); +} + +MainLoop *OS_JavaScript::get_main_loop() const { + + return main_loop; +} + +void OS_JavaScript::run_async() { + + main_loop->init(); + emscripten_set_main_loop(main_loop_callback, -1, false); +} + +void OS_JavaScript::main_loop_callback() { + + get_singleton()->main_loop_iterate(); +} - if (idbfs_available && time_to_save_sync >= 0) { - int64_t newtime = get_ticks_msec(); - int64_t elapsed = newtime - last_sync_time; - last_sync_time = newtime; +bool OS_JavaScript::main_loop_iterate() { + + if (is_userfs_persistent() && sync_wait_time >= 0) { + int64_t current_time = get_ticks_msec(); + int64_t elapsed_time = current_time - last_sync_check_time; + last_sync_check_time = current_time; - time_to_save_sync -= elapsed; + sync_wait_time -= elapsed_time; - if (time_to_save_sync < 0) { - //time to sync, for real + if (sync_wait_time < 0) { /* clang-format off */ EM_ASM( FS.syncfs(function(err) { @@ -815,121 +804,101 @@ bool OS_JavaScript::main_loop_iterate() { return Main::iteration(); } -void OS_JavaScript::main_loop_end() { +void OS_JavaScript::delete_main_loop() { - if (main_loop) - main_loop->finish(); + memdelete(main_loop); } -void OS_JavaScript::process_accelerometer(const Vector3 &p_accelerometer) { +void OS_JavaScript::finalize() { - input->set_accelerometer(p_accelerometer); + memdelete(input); } -bool OS_JavaScript::has_touchscreen_ui_hint() const { +// Miscellaneous - /* clang-format off */ - return EM_ASM_INT_V( - return 'ontouchstart' in window; - ); - /* clang-format on */ +extern "C" EMSCRIPTEN_KEEPALIVE void send_notification(int p_notification) { + + if (p_notification == MainLoop::NOTIFICATION_WM_MOUSE_ENTER || p_notification == MainLoop::NOTIFICATION_WM_MOUSE_EXIT) { + cursor_inside_canvas = p_notification == MainLoop::NOTIFICATION_WM_MOUSE_ENTER; + } + OS_JavaScript::get_singleton()->get_main_loop()->notification(p_notification); } -void OS_JavaScript::main_loop_request_quit() { +bool OS_JavaScript::_check_internal_feature_support(const String &p_feature) { + + if (p_feature == "HTML5" || p_feature == "web") + return true; + +#ifdef JAVASCRIPT_EVAL_ENABLED + if (p_feature == "JavaScript") + return true; +#endif + + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_get_current_context(); + // All extensions are already automatically enabled, this function allows + // checking WebGL extension support without inline JavaScript + if (p_feature == "s3tc") + return emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_s3tc_srgb"); + if (p_feature == "etc") + return emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_etc1"); + if (p_feature == "etc2") + return emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_etc"); - if (main_loop) - main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST); + return false; } -Error OS_JavaScript::shell_open(String p_uri) { +void OS_JavaScript::alert(const String &p_alert, const String &p_title) { + /* clang-format off */ EM_ASM_({ - window.open(UTF8ToString($0), '_blank'); - }, p_uri.utf8().get_data()); + window.alert(UTF8ToString($0)); + }, p_alert.utf8().get_data()); /* clang-format on */ - return OK; } -String OS_JavaScript::get_resource_dir() const { +void OS_JavaScript::set_window_title(const String &p_title) { - return "/"; //javascript has it's own filesystem for resources inside the APK + /* clang-format off */ + EM_ASM_({ + document.title = UTF8ToString($0); + }, p_title.utf8().get_data()); + /* clang-format on */ } -String OS_JavaScript::get_user_data_dir() const { - - /* - if (get_user_data_dir_func) - return get_user_data_dir_func(); - */ - return "/userfs"; -}; - String OS_JavaScript::get_executable_path() const { return OS::get_executable_path(); } -void OS_JavaScript::_close_notification_funcs(const String &p_file, int p_flags) { +Error OS_JavaScript::shell_open(String p_uri) { - OS_JavaScript *os = static_cast(get_singleton()); - if (os->idbfs_available && p_file.begins_with("/userfs") && p_flags & FileAccess::WRITE) { - os->last_sync_time = OS::get_singleton()->get_ticks_msec(); - os->time_to_save_sync = 5000; //five seconds since last save - } + // Open URI in a new tab, browser will deal with it by protocol. + /* clang-format off */ + EM_ASM_({ + window.open(UTF8ToString($0), '_blank'); + }, p_uri.utf8().get_data()); + /* clang-format on */ + return OK; } -void OS_JavaScript::process_joypads() { - - int joy_count = emscripten_get_num_gamepads(); - for (int i = 0; i < joy_count; i++) { - EmscriptenGamepadEvent state; - emscripten_get_gamepad_status(i, &state); - if (state.connected) { +String OS_JavaScript::get_name() { - int num_buttons = MIN(state.numButtons, 18); - int num_axes = MIN(state.numAxes, 8); - for (int j = 0; j < num_buttons; j++) { + return "HTML5"; +} - float value = state.analogButton[j]; - if (String(state.mapping) == "standard" && (j == 6 || j == 7)) { - InputDefault::JoyAxis jx; - jx.min = 0; - jx.value = value; - input->joy_axis(i, j, jx); - } else { - input->joy_button(i, j, value); - } - } - for (int j = 0; j < num_axes; j++) { +bool OS_JavaScript::can_draw() const { - InputDefault::JoyAxis jx; - jx.min = -1; - jx.value = state.axis[j]; - input->joy_axis(i, j, jx); - } - } - } + return true; // Always? } -bool OS_JavaScript::joy_connection_changed(int p_type, const EmscriptenGamepadEvent *p_event) { - if (p_type == EMSCRIPTEN_EVENT_GAMEPADCONNECTED) { +String OS_JavaScript::get_user_data_dir() const { - String guid = ""; - if (String(p_event->mapping) == "standard") - guid = "Default HTML5 Gamepad"; - input->joy_connection_changed(p_event->index, true, String(p_event->id), guid); - } else { - input->joy_connection_changed(p_event->index, false, ""); - } - return true; -} + return "/userfs"; +}; -bool OS_JavaScript::is_joy_known(int p_device) { - return input->is_joy_mapped(p_device); -} +String OS_JavaScript::get_resource_dir() const { -String OS_JavaScript::get_joy_guid(int p_device) const { - return input->get_joy_guid_remapped(p_device); + return "/"; } OS::PowerState OS_JavaScript::get_power_state() { @@ -950,59 +919,53 @@ int OS_JavaScript::get_power_percent_left() { return -1; } -bool OS_JavaScript::_check_internal_feature_support(const String &p_feature) { - - if (p_feature == "HTML5" || p_feature == "web") - return true; +void OS_JavaScript::file_access_close_callback(const String &p_file, int p_flags) { -#ifdef JAVASCRIPT_EVAL_ENABLED - if (p_feature == "JavaScript") - return true; -#endif + OS_JavaScript *os = get_singleton(); + if (os->is_userfs_persistent() && p_file.begins_with("/userfs") && p_flags & FileAccess::WRITE) { + os->last_sync_check_time = OS::get_singleton()->get_ticks_msec(); + // Wait five seconds in case more files are about to be closed. + os->sync_wait_time = 5000; + } +} - EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_get_current_context(); - // all extensions are already automatically enabled, this function allows - // checking WebGL extension support without inline JavaScript - if (p_feature == "s3tc" && emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_s3tc_srgb")) - return true; - if (p_feature == "etc" && emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_etc1")) - return true; - if (p_feature == "etc2" && emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_etc")) - return true; +void OS_JavaScript::set_idb_available(bool p_idb_available) { - return false; + idb_available = p_idb_available; } -void OS_JavaScript::set_idbfs_available(bool p_idbfs_available) { +bool OS_JavaScript::is_userfs_persistent() const { - idbfs_available = p_idbfs_available; + return idb_available; } -bool OS_JavaScript::is_userfs_persistent() const { +OS_JavaScript *OS_JavaScript::get_singleton() { - return idbfs_available; + return static_cast(OS::get_singleton()); } -OS_JavaScript::OS_JavaScript(const char *p_execpath, GetUserDataDirFunc p_get_user_data_dir_func) { +OS_JavaScript::OS_JavaScript(int p_argc, char *p_argv[]) { + + List arguments; + for (int i = 1; i < p_argc; i++) { + arguments.push_back(String::utf8(p_argv[i])); + } + set_cmdline(p_argv[0], arguments); - set_cmdline(p_execpath, get_cmdline_args()); - main_loop = NULL; window_maximized = false; - soft_fs_enabled = false; + soft_fullscreen_enabled = false; canvas_size_adjustment_requested = false; - get_user_data_dir_func = p_get_user_data_dir_func; - FileAccessUnix::close_notification_func = _close_notification_funcs; + main_loop = NULL; + + idb_available = false; + sync_wait_time = -1; - idbfs_available = false; - time_to_save_sync = -1; + AudioDriverManager::add_driver(&audio_driver_javascript); Vector loggers; loggers.push_back(memnew(StdLogger)); _set_logger(memnew(CompositeLogger(loggers))); - AudioDriverManager::add_driver(&audio_driver_javascript); -} - -OS_JavaScript::~OS_JavaScript() { + FileAccessUnix::close_notification_func = file_access_close_callback; } diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h index 46eb1b3f13..503c92585b 100644 --- a/platform/javascript/os_javascript.h +++ b/platform/javascript/os_javascript.h @@ -32,52 +32,56 @@ #define OS_JAVASCRIPT_H #include "audio_driver_javascript.h" -#include "drivers/unix/os_unix.h" #include "main/input_default.h" -#include "os/input.h" -#include "os/main_loop.h" #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" +#include "unix/os_unix.h" #include -typedef String (*GetUserDataDirFunc)(); - class OS_JavaScript : public OS_Unix { - bool idbfs_available; - int64_t time_to_save_sync; - int64_t last_sync_time; - - VisualServer *visual_server; - AudioDriverJavaScript audio_driver_javascript; - - InputDefault *input; + VideoMode video_mode; Vector2 windowed_size; bool window_maximized; - bool soft_fs_enabled; + bool soft_fullscreen_enabled; bool canvas_size_adjustment_requested; - VideoMode video_mode; + + InputDefault *input; + Ref deferred_key_event; CursorShape cursor_shape; + Point2 touches[32]; + MainLoop *main_loop; + AudioDriverJavaScript audio_driver_javascript; - GetUserDataDirFunc get_user_data_dir_func; + bool idb_available; + int64_t sync_wait_time; + int64_t last_sync_check_time; - static void _close_notification_funcs(const String &p_file, int p_flags); + static EM_BOOL browser_resize_callback(int p_event_type, const EmscriptenUiEvent *p_event, void *p_user_data); + static EM_BOOL fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data); - void process_joypads(); + static EM_BOOL keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data); + static EM_BOOL keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data); + static EM_BOOL keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data); - void set_css_cursor(const char *); - const char *get_css_cursor() const; + static EM_BOOL mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data); + static EM_BOOL mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data); -public: - // functions used by main to initialize/deintialize the OS - virtual int get_video_driver_count() const; - virtual const char *get_video_driver_name(int p_driver) const; + static EM_BOOL wheel_callback(int p_event_type, const EmscriptenWheelEvent *p_event, void *p_user_data); - virtual int get_audio_driver_count() const; - virtual const char *get_audio_driver_name(int p_driver) const; + static EM_BOOL touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data); + static EM_BOOL touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data); + + static EM_BOOL gamepad_change_callback(int p_event_type, const EmscriptenGamepadEvent *p_event, void *p_user_data); + void process_joypads(); + static void main_loop_callback(); + + static void file_access_close_callback(const String &p_file, int p_flags); + +protected: virtual void initialize_core(); virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); @@ -86,77 +90,64 @@ public: virtual void finalize(); - typedef int64_t ProcessID; - - //static OS* get_singleton(); - - virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); - - virtual void set_mouse_mode(MouseMode p_mode); - virtual MouseMode get_mouse_mode() const; - virtual Point2 get_mouse_position() const; - virtual int get_mouse_button_state() const; - virtual void set_window_title(const String &p_title); + virtual bool _check_internal_feature_support(const String &p_feature); - //virtual void set_clipboard(const String& p_text); - //virtual String get_clipboard() const; +public: + // Override return type to make writing static callbacks less tedious. + static OS_JavaScript *get_singleton(); virtual void set_video_mode(const VideoMode &p_video_mode, int p_screen = 0); virtual VideoMode get_video_mode(int p_screen = 0) const; virtual void get_fullscreen_mode_list(List *p_list, int p_screen = 0) const; - virtual Size2 get_screen_size(int p_screen = -1) const; - virtual void set_window_size(const Size2); virtual Size2 get_window_size() const; virtual void set_window_maximized(bool p_enabled); - virtual bool is_window_maximized() const { return window_maximized; } - virtual void set_window_fullscreen(bool p_enable); + virtual bool is_window_maximized() const; + virtual void set_window_fullscreen(bool p_enabled); virtual bool is_window_fullscreen() const; + virtual Size2 get_screen_size(int p_screen = -1) const; - void request_canvas_size_adjustment(); + virtual Point2 get_mouse_position() const; + virtual int get_mouse_button_state() const; + virtual void set_cursor_shape(CursorShape p_shape); + virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); + virtual void set_mouse_mode(MouseMode p_mode); + virtual MouseMode get_mouse_mode() const; - virtual String get_name(); - virtual MainLoop *get_main_loop() const; + virtual bool has_touchscreen_ui_hint() const; - virtual bool can_draw() const; + virtual bool is_joy_known(int p_device); + virtual String get_joy_guid(int p_device) const; - virtual bool is_userfs_persistent() const; + virtual int get_video_driver_count() const; + virtual const char *get_video_driver_name(int p_driver) const; - virtual void set_cursor_shape(CursorShape p_shape); - virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); + virtual int get_audio_driver_count() const; + virtual const char *get_audio_driver_name(int p_driver) const; - void main_loop_begin(); + virtual MainLoop *get_main_loop() const; + void run_async(); bool main_loop_iterate(); - void main_loop_request_quit(); - void main_loop_end(); - void main_loop_focusout(); - void main_loop_focusin(); - virtual bool has_touchscreen_ui_hint() const; - - virtual Error shell_open(String p_uri); - virtual String get_user_data_dir() const; + virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); + virtual void set_window_title(const String &p_title); String get_executable_path() const; - virtual String get_resource_dir() const; - - void process_accelerometer(const Vector3 &p_accelerometer); - void push_input(const Ref &p_ev); + virtual Error shell_open(String p_uri); + virtual String get_name(); + virtual bool can_draw() const; - virtual bool is_joy_known(int p_device); - virtual String get_joy_guid(int p_device) const; - bool joy_connection_changed(int p_type, const EmscriptenGamepadEvent *p_event); + virtual String get_resource_dir() const; + virtual String get_user_data_dir() const; virtual OS::PowerState get_power_state(); virtual int get_power_seconds_left(); virtual int get_power_percent_left(); - virtual bool _check_internal_feature_support(const String &p_feature); - - void set_idbfs_available(bool p_idbfs_available); + void set_idb_available(bool p_idb_available); + virtual bool is_userfs_persistent() const; - OS_JavaScript(const char *p_execpath, GetUserDataDirFunc p_get_user_data_dir_func); - ~OS_JavaScript(); + OS_JavaScript(int p_argc, char *p_argv[]); }; #endif -- cgit v1.2.3