// Copyright (c) Wojciech Figat. All rights reserved. #if PLATFORM_WEB #include "WebPlatform.h" #include "WebThread.h" #include "WebFileSystem.h" #include "Engine/Core/Log.h" #include "Engine/Core/Types/String.h" #include "Engine/Core/Types/StringView.h" #include "Engine/Core/Types/Version.h" #include "Engine/Core/Types/Guid.h" #include "Engine/Core/Collections/Dictionary.h" #include "Engine/Platform/CPUInfo.h" #include "Engine/Platform/MemoryStats.h" #include "Engine/Platform/MessageBox.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerMemory.h" #include "Engine/Engine/Engine.h" #include "Engine/Engine/Web/WebGame.h" #include "Engine/Utilities/StringConverter.h" #include #include #include #include #include #include #include #include namespace { CPUInfo Cpu; double DateStart = emscripten_get_now(); }; void WebGame::InitMainWindowSettings(CreateWindowSettings& settings) { // Set window size matching the canvas item in HTML settings.Fullscreen = false; int width = 1, height = 1; emscripten_get_canvas_element_size(WEB_CANVAS_ID, &width, &height); settings.Size.X = width; settings.Size.Y = height; } DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon) { StringAnsi textAnsi(text); StringAnsi captionAnsi(caption); EM_ASM({ alert(UTF8ToString($0) + "\n\n" + UTF8ToString($1)); }, captionAnsi.Get(), textAnsi.Get()); return DialogResult::None; } WebThread* WebThread::Create(IRunnable* runnable, const String& name, ThreadPriority priority, uint32 stackSize) { #ifdef __EMSCRIPTEN_PTHREADS__ pthread_attr_t attr; pthread_attr_init(&attr); if (stackSize != 0) pthread_attr_setstacksize(&attr, stackSize); auto thread = New(runnable, name, priority); const int result = pthread_create(&thread->_thread, &attr, ThreadProc, thread); if (result != 0) { LOG(Warning, "Failed to spawn a thread. Result code: {0}", result); Delete(thread); return nullptr; } return thread; #else LOG(Fatal, "Threading is not supported."); return nullptr; #endif } void WebFileSystem::GetSpecialFolderPath(const SpecialFolder type, String& result) { result = TEXT("/"); } #if 0 void* WebPlatform::Allocate(uint64 size, uint64 alignment) { void* ptr = nullptr; if (alignment && size) { // Alignment always has to be power of two ASSERT_LOW_LAYER((alignment & (alignment - 1)) == 0); ptr = emscripten_builtin_memalign(alignment, size); if (!ptr) OutOfMemory(); #if COMPILE_WITH_PROFILER OnMemoryAlloc(ptr, size); #endif } return ptr; } void WebPlatform::Free(void* ptr) { if (ptr) { #if COMPILE_WITH_PROFILER OnMemoryFree(ptr); #endif emscripten_builtin_free(ptr); } } #endif String WebPlatform::GetSystemName() { return TEXT("Browser"); } Version WebPlatform::GetSystemVersion() { return Version(1, 0); } CPUInfo WebPlatform::GetCPUInfo() { return Cpu; } MemoryStats WebPlatform::GetMemoryStats() { MemoryStats result; result.TotalPhysicalMemory = emscripten_get_heap_max(); result.UsedPhysicalMemory = emscripten_get_heap_size(); result.TotalVirtualMemory = 2ull * 1024 * 1024 * 1024; // Max 2GB result.UsedVirtualMemory = result.UsedPhysicalMemory; result.ProgramSizeMemory = 0; return result; } ProcessMemoryStats WebPlatform::GetProcessMemoryStats() { // Mock memory stats ProcessMemoryStats result; result.UsedPhysicalMemory = 1 * 1024 * 1024; result.UsedVirtualMemory = result.UsedPhysicalMemory; return result; } void WebPlatform::SetThreadPriority(ThreadPriority priority) { // Not supported } void WebPlatform::SetThreadAffinityMask(uint64 affinityMask) { // Not supported } void WebPlatform::Sleep(int32 milliseconds) { emscripten_thread_sleep(milliseconds); } void WebPlatform::Yield() { Sleep(0); } double WebPlatform::GetTimeSeconds() { double time = emscripten_get_now(); return time * 0.001; } uint64 WebPlatform::GetTimeCycles() { return (uint64)(emscripten_get_now() * 1000.0); } void WebPlatform::GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond) { // Get local time using namespace std::chrono; system_clock::time_point now = system_clock::now(); time_t tt = system_clock::to_time_t(now); tm time = *localtime(&tt); // Extract time year = time.tm_year + 1900; month = time.tm_mon + 1; dayOfWeek = time.tm_wday; day = time.tm_mday; hour = time.tm_hour; minute = time.tm_min; second = time.tm_sec; millisecond = (int64)(emscripten_get_now() - DateStart) % 1000; // Fake it based on other timer } void WebPlatform::GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond) { // Get UTC time using namespace std::chrono; system_clock::time_point now = system_clock::now(); time_t tt = system_clock::to_time_t(now); tm time = *gmtime(&tt); // Extract time year = time.tm_year + 1900; month = time.tm_mon + 1; dayOfWeek = time.tm_wday; day = time.tm_mday; hour = time.tm_hour; minute = time.tm_min; second = time.tm_sec; millisecond = (int64)(emscripten_get_now() - DateStart) % 1000; // Fake it based on other timer } void WebPlatform::Log(const StringView& msg, int32 logType) { const StringAsANSI<512> msgAnsi(*msg, msg.Length()); // Fix % characters that should not be formatted auto buffer = (char*)msgAnsi.Get(); for (int32 i = 0; buffer[i]; i++) { if (buffer[i] == '%') buffer[i] = 'p'; } int flags = EM_LOG_CONSOLE; if (logType == (int32)LogType::Warning) flags |= EM_LOG_WARN; if (logType == (int32)LogType::Error) flags |= EM_LOG_ERROR; emscripten_log(flags, buffer); } String WebPlatform::GetComputerName() { return TEXT("Web"); } bool WebPlatform::GetHasFocus() { return true; } String WebPlatform::GetMainDirectory() { return TEXT("/"); } String WebPlatform::GetExecutableFilePath() { return TEXT("/index.html"); } Guid WebPlatform::GetUniqueDeviceId() { return Guid(1, 2, 3, 4); } String WebPlatform::GetWorkingDirectory() { return GetMainDirectory(); } bool WebPlatform::SetWorkingDirectory(const String& path) { return true; } bool WebPlatform::Init() { if (PlatformBase::Init()) return true; #if COMPILE_WITH_PROFILER // Setup platform-specific memory profiler tags ProfilerMemory::RenameGroup(ProfilerMemory::Groups::WEB_MEM_TAG_HEAP_SIZE, TEXT("Emscripten/HeapSize")); ProfilerMemory::RenameGroup(ProfilerMemory::Groups::WEB_MEM_TAG_HEAP_MAX, TEXT("Emscripten/HeapMax")); #endif // Set info about the CPU Platform::MemoryClear(&Cpu, sizeof(Cpu)); Cpu.ProcessorPackageCount = 1; Cpu.ProcessorCoreCount = Math::Min(emscripten_num_logical_cores(), PLATFORM_THREADS_LIMIT); Cpu.LogicalProcessorCount = Cpu.ProcessorCoreCount; Cpu.ClockSpeed = GetClockFrequency(); return false; } void WebPlatform::LogInfo() { PlatformBase::LogInfo(); #ifdef __EMSCRIPTEN_major__ LOG(Info, "Emscripten {}.{}.{}", __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__); #elif defined(__EMSCRIPTEN_MAJOR__) LOG(Info, "Emscripten {}.{}.{}", __EMSCRIPTEN_MAJOR__, __EMSCRIPTEN_MINOR__, __EMSCRIPTEN_TINY__); #else LOG(Info, "Emscripten"); #endif #ifdef __EMSCRIPTEN_PTHREADS__ LOG(Info, "Threading: pthreads"); #else LOG(Info, "Threading: disabled"); #endif } void WebPlatform::Tick() { #if COMPILE_WITH_PROFILER ProfilerMemory::OnGroupSet(ProfilerMemory::Groups::WEB_MEM_TAG_HEAP_SIZE, (int64)emscripten_get_heap_size(), 1); ProfilerMemory::OnGroupSet(ProfilerMemory::Groups::WEB_MEM_TAG_HEAP_MAX, (int64)emscripten_get_heap_max(), 1); #endif } void WebPlatform::Exit() { // Exit runtime emscripten_cancel_main_loop(); emscripten_force_exit(Engine::ExitCode); } extern char** environ; void WebPlatform::GetEnvironmentVariables(Dictionary& result) { char** s = environ; for (; *s; s++) { char* var = *s; int32 split = -1; for (int32 i = 0; var[i]; i++) { if (var[i] == '=') { split = i; break; } } if (split == -1) result[String(var)] = String::Empty; else result[String(var, split)] = String(var + split + 1); } } bool WebPlatform::GetEnvironmentVariable(const String& name, String& value) { char* env = getenv(StringAsANSI<>(*name).Get()); if (env) { value = String(env); return false; } return true; } bool WebPlatform::SetEnvironmentVariable(const String& name, const String& value) { return setenv(StringAsANSI<>(*name).Get(), StringAsANSI<>(*value).Get(), true) != 0; } void* WebPlatform::LoadLibrary(const Char* filename) { PROFILE_CPU(); ZoneText(filename, StringUtils::Length(filename)); const StringAsANSI<> filenameANSI(filename); void* result = dlopen(filenameANSI.Get(), RTLD_NOW); if (!result) { LOG(Error, "Failed to load {0} because {1}", filename, String(dlerror())); } return result; } void WebPlatform::FreeLibrary(void* handle) { dlclose(handle); } void* WebPlatform::GetProcAddress(void* handle, const char* symbol) { return dlsym(handle, symbol); } Array WebPlatform::GetStackFrames(int32 skipCount, int32 maxDepth, void* context) { Array result; #if CRASH_LOG_ENABLE // Get callstack char callstack[2000]; emscripten_get_callstack(EM_LOG_JS_STACK, callstack, sizeof(callstack)); // Parse callstack int32 pos = 0; while (callstack[pos]) { StackFrame& frame = result.AddOne(); frame.ProgramCounter = 0; frame.ModuleName[0] = 0; frame.FileName[0] = 0; frame.LineNumber = 0; // Skip leading spaces while (callstack[pos] == ' ') pos++; // Skip 'at ' pos += 3; // Read function name int32 dst = 0; while (callstack[pos] && callstack[pos] != '\n' && dst < ARRAY_COUNT(frame.FileName) - 1) frame.FunctionName[dst++] = callstack[pos++]; frame.FunctionName[dst] = 0; // Skip '\n' if (callstack[pos]) pos++; } // Adjust frames range skipCount += 2; // Skip this function and utility JS frames while (skipCount-- && result.HasItems()) result.RemoveAtKeepOrder(0); if (result.Count() > maxDepth) result.Resize(maxDepth); #endif return result; } #if 1 // Fix linker errors when using '-sAUTO_JS_LIBRARIES=0' due to unused WebGL functions (referenced by SDL3 port itself from emscripten: SDL_emscriptenopengles.c) extern "C" EMSCRIPTEN_WEBGL_CONTEXT_HANDLE emscripten_webgl_create_context(const char* target, const EmscriptenWebGLContextAttributes* attributes) { return 0; } extern "C" EMSCRIPTEN_RESULT emscripten_webgl_make_context_current(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context) { return 0; } extern "C" EMSCRIPTEN_RESULT emscripten_webgl_destroy_context(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context) { return 0; } extern "C" void* emscripten_webgl_get_proc_address(const char* name) { return nullptr; } extern "C" void emscripten_webgl_init_context_attributes(EmscriptenWebGLContextAttributes* attributes) { } // Reference: emscripten\system\lib\html5\dom_pk_codes.c #include extern "C" DOM_PK_CODE_TYPE emscripten_compute_dom_pk_code(const char* keyCodeString) { if (!keyCodeString) return 0; /* Compute the collision free hash. */ unsigned int hash = 0; while (*keyCodeString) hash = ((hash ^ 0x7E057D79U) << 3) ^ (unsigned int)*keyCodeString++; /* * Don't expose the hash values out to the application, but map to fixed IDs. * This is useful for mapping back codes to MDN documentation page at * * https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code */ switch (hash) { case 0x98051284U /* Unidentified */: return DOM_PK_UNKNOWN; /* 0x0000 */ case 0x67243A2DU /* Escape */: return DOM_PK_ESCAPE; /* 0x0001 */ case 0x67251058U /* Digit0 */: return DOM_PK_0; /* 0x0002 */ case 0x67251059U /* Digit1 */: return DOM_PK_1; /* 0x0003 */ case 0x6725105AU /* Digit2 */: return DOM_PK_2; /* 0x0004 */ case 0x6725105BU /* Digit3 */: return DOM_PK_3; /* 0x0005 */ case 0x6725105CU /* Digit4 */: return DOM_PK_4; /* 0x0006 */ case 0x6725105DU /* Digit5 */: return DOM_PK_5; /* 0x0007 */ case 0x6725105EU /* Digit6 */: return DOM_PK_6; /* 0x0008 */ case 0x6725105FU /* Digit7 */: return DOM_PK_7; /* 0x0009 */ case 0x67251050U /* Digit8 */: return DOM_PK_8; /* 0x000A */ case 0x67251051U /* Digit9 */: return DOM_PK_9; /* 0x000B */ case 0x92E14DD3U /* Minus */: return DOM_PK_MINUS; /* 0x000C */ case 0x92E1FBACU /* Equal */: return DOM_PK_EQUAL; /* 0x000D */ case 0x36BF1CB5U /* Backspace */: return DOM_PK_BACKSPACE; /* 0x000E */ case 0x7B8E51E2U /* Tab */: return DOM_PK_TAB; /* 0x000F */ case 0x2C595B51U /* KeyQ */: return DOM_PK_Q; /* 0x0010 */ case 0x2C595B57U /* KeyW */: return DOM_PK_W; /* 0x0011 */ case 0x2C595B45U /* KeyE */: return DOM_PK_E; /* 0x0012 */ case 0x2C595B52U /* KeyR */: return DOM_PK_R; /* 0x0013 */ case 0x2C595B54U /* KeyT */: return DOM_PK_T; /* 0x0014 */ case 0x2C595B59U /* KeyY */: return DOM_PK_Y; /* 0x0015 */ case 0x2C595B55U /* KeyU */: return DOM_PK_U; /* 0x0016 */ case 0x2C595B49U /* KeyI */: return DOM_PK_I; /* 0x0017 */ case 0x2C595B4FU /* KeyO */: return DOM_PK_O; /* 0x0018 */ case 0x2C595B50U /* KeyP */: return DOM_PK_P; /* 0x0019 */ case 0x45D8158CU /* BracketLeft */: return DOM_PK_BRACKET_LEFT; /* 0x001A */ case 0xDEEABF7CU /* BracketRight */: return DOM_PK_BRACKET_RIGHT; /* 0x001B */ case 0x92E1C5D2U /* Enter */: return DOM_PK_ENTER; /* 0x001C */ case 0xE058958CU /* ControlLeft */: return DOM_PK_CONTROL_LEFT; /* 0x001D */ case 0x2C595B41U /* KeyA */: return DOM_PK_A; /* 0x001E */ case 0x2C595B53U /* KeyS */: return DOM_PK_S; /* 0x001F */ case 0x2C595B44U /* KeyD */: return DOM_PK_D; /* 0x0020 */ case 0x2C595B46U /* KeyF */: return DOM_PK_F; /* 0x0021 */ case 0x2C595B47U /* KeyG */: return DOM_PK_G; /* 0x0022 */ case 0x2C595B48U /* KeyH */: return DOM_PK_H; /* 0x0023 */ case 0x2C595B4AU /* KeyJ */: return DOM_PK_J; /* 0x0024 */ case 0x2C595B4BU /* KeyK */: return DOM_PK_K; /* 0x0025 */ case 0x2C595B4CU /* KeyL */: return DOM_PK_L; /* 0x0026 */ case 0x2707219EU /* Semicolon */: return DOM_PK_SEMICOLON; /* 0x0027 */ case 0x92E0B58DU /* Quote */: return DOM_PK_QUOTE; /* 0x0028 */ case 0x36BF358DU /* Backquote */: return DOM_PK_BACKQUOTE; /* 0x0029 */ case 0x26B1958CU /* ShiftLeft */: return DOM_PK_SHIFT_LEFT; /* 0x002A */ case 0x36BF2438U /* Backslash */: return DOM_PK_BACKSLASH; /* 0x002B */ case 0x2C595B5AU /* KeyZ */: return DOM_PK_Z; /* 0x002C */ case 0x2C595B58U /* KeyX */: return DOM_PK_X; /* 0x002D */ case 0x2C595B43U /* KeyC */: return DOM_PK_C; /* 0x002E */ case 0x2C595B56U /* KeyV */: return DOM_PK_V; /* 0x002F */ case 0x2C595B42U /* KeyB */: return DOM_PK_B; /* 0x0030 */ case 0x2C595B4EU /* KeyN */: return DOM_PK_N; /* 0x0031 */ case 0x2C595B4DU /* KeyM */: return DOM_PK_M; /* 0x0032 */ case 0x92E1A1C1U /* Comma */: return DOM_PK_COMMA; /* 0x0033 */ case 0x672FFAD4U /* Period */: return DOM_PK_PERIOD; /* 0x0034 */ case 0x92E0A438U /* Slash */: return DOM_PK_SLASH; /* 0x0035 */ case 0xC5A6BF7CU /* ShiftRight */: return DOM_PK_SHIFT_RIGHT; /* 0x0036 */ case 0x5D64DA91U /* NumpadMultiply */: return DOM_PK_NUMPAD_MULTIPLY; /* 0x0037 */ case 0xC914958CU /* AltLeft */: return DOM_PK_ALT_LEFT; /* 0x0038 */ case 0x92E09CB5U /* Space */: return DOM_PK_SPACE; /* 0x0039 */ case 0xB8FAE73BU /* CapsLock */: return DOM_PK_CAPS_LOCK; /* 0x003A */ case 0x7174B789U /* F1 */: return DOM_PK_F1; /* 0x003B */ case 0x7174B78AU /* F2 */: return DOM_PK_F2; /* 0x003C */ case 0x7174B78BU /* F3 */: return DOM_PK_F3; /* 0x003D */ case 0x7174B78CU /* F4 */: return DOM_PK_F4; /* 0x003E */ case 0x7174B78DU /* F5 */: return DOM_PK_F5; /* 0x003F */ case 0x7174B78EU /* F6 */: return DOM_PK_F6; /* 0x0040 */ case 0x7174B78FU /* F7 */: return DOM_PK_F7; /* 0x0041 */ case 0x7174B780U /* F8 */: return DOM_PK_F8; /* 0x0042 */ case 0x7174B781U /* F9 */: return DOM_PK_F9; /* 0x0043 */ case 0x7B8E57B0U /* F10 */: return DOM_PK_F10; /* 0x0044 */ case 0x92E08B35U /* Pause */: return DOM_PK_PAUSE; /* 0x0045 */ case 0xCDED173BU /* ScrollLock */: return DOM_PK_SCROLL_LOCK; /* 0x0046 */ case 0xC925FCDFU /* Numpad7 */: return DOM_PK_NUMPAD_7; /* 0x0047 */ case 0xC925FCD0U /* Numpad8 */: return DOM_PK_NUMPAD_8; /* 0x0048 */ case 0xC925FCD1U /* Numpad9 */: return DOM_PK_NUMPAD_9; /* 0x0049 */ case 0x5EA3E8A4U /* NumpadSubtract */: return DOM_PK_NUMPAD_SUBTRACT; /* 0x004A */ case 0xC925FCDCU /* Numpad4 */: return DOM_PK_NUMPAD_4; /* 0x004B */ case 0xC925FCDDU /* Numpad5 */: return DOM_PK_NUMPAD_5; /* 0x004C */ case 0xC925FCDEU /* Numpad6 */: return DOM_PK_NUMPAD_6; /* 0x004D */ case 0x380B9C8CU /* NumpadAdd */: return DOM_PK_NUMPAD_ADD; /* 0x004E */ case 0xC925FCD9U /* Numpad1 */: return DOM_PK_NUMPAD_1; /* 0x004F */ case 0xC925FCDAU /* Numpad2 */: return DOM_PK_NUMPAD_2; /* 0x0050 */ case 0xC925FCDBU /* Numpad3 */: return DOM_PK_NUMPAD_3; /* 0x0051 */ case 0xC925FCD8U /* Numpad0 */: return DOM_PK_NUMPAD_0; /* 0x0052 */ case 0x95852DACU /* NumpadDecimal */: return DOM_PK_NUMPAD_DECIMAL; /* 0x0053 */ case 0xCC1E198EU /* PrintScreen */: return DOM_PK_PRINT_SCREEN; /* 0x0054 */ case 0x16BF2438U /* IntlBackslash */: return DOM_PK_INTL_BACKSLASH; /* 0x0056 */ case 0x7B8E57B1U /* F11 */: return DOM_PK_F11; /* 0x0057 */ case 0x7B8E57B2U /* F12 */: return DOM_PK_F12; /* 0x0058 */ case 0x7393FBACU /* NumpadEqual */: return DOM_PK_NUMPAD_EQUAL; /* 0x0059 */ case 0x7B8E57B3U /* F13 */: return DOM_PK_F13; /* 0x0064 */ case 0x7B8E57B4U /* F14 */: return DOM_PK_F14; /* 0x0065 */ case 0x7B8E57B5U /* F15 */: return DOM_PK_F15; /* 0x0066 */ case 0x7B8E57B6U /* F16 */: return DOM_PK_F16; /* 0x0067 */ case 0x7B8E57B7U /* F17 */: return DOM_PK_F17; /* 0x0068 */ case 0x7B8E57B8U /* F18 */: return DOM_PK_F18; /* 0x0069 */ case 0x7B8E57B9U /* F19 */: return DOM_PK_F19; /* 0x006A */ case 0x7B8E57A8U /* F20 */: return DOM_PK_F20; /* 0x006B */ case 0x7B8E57A9U /* F21 */: return DOM_PK_F21; /* 0x006C */ case 0x7B8E57AAU /* F22 */: return DOM_PK_F22; /* 0x006D */ case 0x7B8E57ABU /* F23 */: return DOM_PK_F23; /* 0x006E */ case 0xB9F4C50DU /* KanaMode */: return DOM_PK_KANA_MODE; /* 0x0070 */ case 0x92E14D02U /* Lang2 */: return DOM_PK_LANG_2; /* 0x0071 */ case 0x92E14D01U /* Lang1 */: return DOM_PK_LANG_1; /* 0x0072 */ case 0x6723C677U /* IntlRo */: return DOM_PK_INTL_RO; /* 0x0073 */ case 0x7B8E57ACU /* F24 */: return DOM_PK_F24; /* 0x0076 */ case 0xC91CC12CU /* Convert */: return DOM_PK_CONVERT; /* 0x0079 */ case 0x2ADCC12CU /* NonConvert */: return DOM_PK_NON_CONVERT; /* 0x007B */ case 0xC935DA8EU /* IntlYen */: return DOM_PK_INTL_YEN; /* 0x007D */ case 0x7393A1C1U /* NumpadComma */: return DOM_PK_NUMPAD_COMMA; /* 0x007E */ case 0x92E08A8DU /* Paste */: return DOM_PK_PASTE; /* 0xE00A */ case 0x01DC7D93U /* MediaTrackPrevious */: return DOM_PK_MEDIA_TRACK_PREVIOUS; /* 0xE010 */ case 0x7B8E5494U /* Cut */: return DOM_PK_CUT; /* 0xE017 */ case 0x2C5949B1U /* Copy */: return DOM_PK_COPY; /* 0xE018 */ case 0x2AD2E17CU /* MediaTrackNext */: return DOM_PK_MEDIA_TRACK_NEXT; /* 0xE019 */ case 0x7393C5D2U /* NumpadEnter */: return DOM_PK_NUMPAD_ENTER; /* 0xE01C */ case 0xF2EEBF7CU /* ControlRight */: return DOM_PK_CONTROL_RIGHT; /* 0xE01D */ case 0x2A45030DU /* AudioVolumeMute */: return DOM_PK_AUDIO_VOLUME_MUTE; /* 0xE020 */ case 0xEA45030DU /* VolumeMute */: return DOM_PK_AUDIO_VOLUME_MUTE; /* 0xE020 */ case 0x370ECA3AU /* LaunchApp2 */: return DOM_PK_LAUNCH_APP_2; /* 0xE021 */ case 0x2D1C0B35U /* MediaPlayPause */: return DOM_PK_MEDIA_PLAY_PAUSE; /* 0xE022 */ case 0x39237F80U /* MediaStop */: return DOM_PK_MEDIA_STOP; /* 0xE024 */ case 0x92E1C9A4U /* Eject */: return DOM_PK_EJECT; /* 0xE02C */ case 0x2A45179EU /* AudioVolumeDown */: return DOM_PK_AUDIO_VOLUME_DOWN; /* 0xE02E */ case 0xEA45179EU /* VolumeDown */: return DOM_PK_AUDIO_VOLUME_DOWN; /* 0xE02E */ case 0x156CC610U /* AudioVolumeUp */: return DOM_PK_AUDIO_VOLUME_UP; /* 0xE030 */ case 0xBA6CC610U /* VolumeUp */: return DOM_PK_AUDIO_VOLUME_UP; /* 0xE030 */ case 0x49387F45U /* BrowserHome */: return DOM_PK_BROWSER_HOME; /* 0xE032 */ case 0x6CB5328DU /* NumpadDivide */: return DOM_PK_NUMPAD_DIVIDE; /* 0xE035 */ case 0xB88EBF7CU /* AltRight */: return DOM_PK_ALT_RIGHT; /* 0xE038 */ case 0x2C595DD8U /* Help */: return DOM_PK_HELP; /* 0xE03B */ case 0xC925873BU /* NumLock */: return DOM_PK_NUM_LOCK; /* 0xE045 */ case 0x2C595F45U /* Home */: return DOM_PK_HOME; /* 0xE047 */ case 0xC91BB690U /* ArrowUp */: return DOM_PK_ARROW_UP; /* 0xE048 */ case 0x672F9210U /* PageUp */: return DOM_PK_PAGE_UP; /* 0xE049 */ case 0x3799258CU /* ArrowLeft */: return DOM_PK_ARROW_LEFT; /* 0xE04B */ case 0x4CE33F7CU /* ArrowRight */: return DOM_PK_ARROW_RIGHT; /* 0xE04D */ case 0x7B8E55DCU /* End */: return DOM_PK_END; /* 0xE04F */ case 0x3799379EU /* ArrowDown */: return DOM_PK_ARROW_DOWN; /* 0xE050 */ case 0xBA90179EU /* PageDown */: return DOM_PK_PAGE_DOWN; /* 0xE051 */ case 0x6723CB2CU /* Insert */: return DOM_PK_INSERT; /* 0xE052 */ case 0x6725C50DU /* Delete */: return DOM_PK_DELETE; /* 0xE053 */ case 0xB929C58CU /* MetaLeft */: return DOM_PK_META_LEFT; /* 0xE05B */ case 0x6723658CU /* OSLeft */: return DOM_PK_OS_LEFT; /* 0xE05B */ case 0x39643F7CU /* MetaRight */: return DOM_PK_META_RIGHT; /* 0xE05C */ case 0xC9313F7CU /* OSRight */: return DOM_PK_OS_RIGHT; /* 0xE05C */ case 0xE00E97CDU /* ContextMenu */: return DOM_PK_CONTEXT_MENU; /* 0xE05D */ case 0x92E09712U /* Power */: return DOM_PK_POWER; /* 0xE05E */ case 0x3F665A78U /* BrowserSearch */: return DOM_PK_BROWSER_SEARCH; /* 0xE065 */ case 0xA2E93BD3U /* BrowserFavorites */: return DOM_PK_BROWSER_FAVORITES; /* 0xE066 */ case 0x0B1D4938U /* BrowserRefresh */: return DOM_PK_BROWSER_REFRESH; /* 0xE067 */ case 0x49384F80U /* BrowserStop */: return DOM_PK_BROWSER_STOP; /* 0xE068 */ case 0x0B49023CU /* BrowserForward */: return DOM_PK_BROWSER_FORWARD; /* 0xE069 */ case 0x493868BBU /* BrowserBack */: return DOM_PK_BROWSER_BACK; /* 0xE06A */ case 0x370ECA39U /* LaunchApp1 */: return DOM_PK_LAUNCH_APP_1; /* 0xE06B */ case 0x370ED6ECU /* LaunchMail */: return DOM_PK_LAUNCH_MAIL; /* 0xE06C */ case 0x39AB4892U /* LaunchMediaPlayer */: return DOM_PK_LAUNCH_MEDIA_PLAYER; /* 0xE06D */ case 0x39AA45A4U /* MediaSelect */: return DOM_PK_MEDIA_SELECT; /* 0xE06D */ default: return DOM_PK_UNKNOWN; } } #endif #endif