mirror of
https://github.com/ocornut/imgui.git
synced 2026-01-09 23:54:20 +00:00
644 lines
24 KiB
C++
644 lines
24 KiB
C++
// dear imgui: Platform Backend for Emscripten
|
|
// This needs to be used along with a Renderer (e.g. OpenGL3)
|
|
|
|
// Implemented features:
|
|
// [X] Platform: Clipboard support (with IMGUI_IMPL_EMSCRIPTEN_ENABLE_CLIPBOARD).
|
|
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (with IMGUI_IMPL_EMSCRIPTEN_POINTER_EVENTS).
|
|
// [X] Platform: Keyboard support.
|
|
// [X] Platform: Gamepad support.
|
|
// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
|
|
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
|
// Learn about Dear ImGui:
|
|
// - FAQ https://dearimgui.com/faq
|
|
// - Getting Started https://dearimgui.com/getting-started
|
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
|
// - Introduction, links and more at the top of imgui.cpp
|
|
|
|
// Configuration flags to add in your imconfig file:
|
|
// #define IMGUI_IMPL_EMSCRIPTEN_ENABLE_CLIPBOARD // Enable clipboard support, requires "Module['_ImGui_ImplEmscripten_ClipboardPasteCallback']"
|
|
// #define IMGUI_IMPL_EMSCRIPTEN_POINTER_EVENTS // Handle inputs using pointer events, requires "Module['_ImGui_ImplEmscripten_PointerEvent']" and "touch-action: none" to properly accept touch inputs.
|
|
|
|
#include "imgui.h"
|
|
#ifndef IMGUI_DISABLE
|
|
|
|
#include <emscripten/html5.h>
|
|
|
|
struct ImGui_ImplEmscripten_BackendData
|
|
{
|
|
const char* ClipboardTextData;
|
|
char* TargetId;
|
|
double Time;
|
|
int Width;
|
|
int Height;
|
|
ImGuiMouseCursor LastMouseCursor;
|
|
int GamepadsCount;
|
|
int PressedButtons;
|
|
};
|
|
|
|
// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
|
|
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
|
static ImGui_ImplEmscripten_BackendData* ImGui_ImplEmscripten_GetBackendData()
|
|
{
|
|
return ImGui::GetCurrentContext() ? (ImGui_ImplEmscripten_BackendData*)ImGui::GetIO().BackendPlatformUserData : nullptr;
|
|
}
|
|
static ImGui_ImplEmscripten_BackendData* ImGui_ImplEmscripten_GetBackendData(ImGuiIO& io)
|
|
{
|
|
return (ImGui_ImplEmscripten_BackendData*)io.BackendPlatformUserData;
|
|
}
|
|
|
|
// Functions
|
|
static bool ImGui_ImplEmscripten_UpdateMouseCursor(ImGuiIO& io, ImGuiMouseCursor imgui_cursor)
|
|
{
|
|
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
|
|
return false;
|
|
|
|
ImGui_ImplEmscripten_BackendData* bd = ImGui_ImplEmscripten_GetBackendData(io);
|
|
|
|
const char* name = NULL;
|
|
if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
|
|
{
|
|
name = "none";
|
|
}
|
|
else
|
|
{
|
|
switch (imgui_cursor)
|
|
{
|
|
case ImGuiMouseCursor_Arrow: name = "default"; break;
|
|
case ImGuiMouseCursor_TextInput: name = "text"; break;
|
|
case ImGuiMouseCursor_ResizeAll: name = "move"; break;
|
|
case ImGuiMouseCursor_ResizeNS: name = "ns-resize"; break;
|
|
case ImGuiMouseCursor_ResizeEW: name = "ew-resize"; break;
|
|
case ImGuiMouseCursor_ResizeNESW: name = "nesw-resize"; break;
|
|
case ImGuiMouseCursor_ResizeNWSE: name = "nwse-resize"; break;
|
|
case ImGuiMouseCursor_Hand: name = "pointer"; break;
|
|
case ImGuiMouseCursor_Wait: name = "wait"; break;
|
|
case ImGuiMouseCursor_Progress: name = "progress"; break;
|
|
case ImGuiMouseCursor_NotAllowed: name = "not-allowed"; break;
|
|
}
|
|
}
|
|
|
|
if (name)
|
|
{
|
|
EM_ASM({
|
|
document.querySelector(UTF8ToString($0)).style.cursor = UTF8ToString($1);
|
|
}, bd->TargetId, name);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// FIXME: Lookup complex keys in the hash map
|
|
// Not using charCode/keyCode because they're deprecated, tho SDL still uses them.
|
|
static ImGuiKey ImGui_ImplEmscripten_KeyToImGuiKey(const EM_UTF8(&key)[32])
|
|
{
|
|
if (key[1] == '\0')
|
|
{
|
|
char c = key[0];
|
|
if (c >= 'a' && c <= 'z')
|
|
return (ImGuiKey)((c - 'a') + ImGuiKey_A);
|
|
else if (c >= '0' && c <= '9')
|
|
return (ImGuiKey)((c - '0') + ImGuiKey_0);
|
|
else
|
|
{
|
|
switch (c)
|
|
{
|
|
case '\'': return ImGuiKey_Apostrophe;
|
|
case ',': return ImGuiKey_Comma;
|
|
case '-': return ImGuiKey_Minus;
|
|
case '.': return ImGuiKey_Period;
|
|
case '/': return ImGuiKey_Slash;
|
|
case ';': return ImGuiKey_Semicolon;
|
|
case '=': return ImGuiKey_Equal;
|
|
case '[': return ImGuiKey_LeftBracket;
|
|
case '\\': return ImGuiKey_Backslash;
|
|
case ']': return ImGuiKey_RightBracket;
|
|
case '`': return ImGuiKey_GraveAccent;
|
|
case ' ': return ImGuiKey_Space;
|
|
case 0x09: return ImGuiKey_Tab;
|
|
case '+': return ImGuiKey_KeypadAdd;
|
|
case '*': return ImGuiKey_KeypadMultiply;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char c0 = key[0];
|
|
char c1 = key[1];
|
|
char c2 = key[2];
|
|
if (c0 == 'F' && c1 != '\0' && (c2 == '\0' || key[3] == '\0'))
|
|
{
|
|
// F1-F9
|
|
if (c2 == '\0' && (c1 >= '1' && c1 <= '9'))
|
|
return (ImGuiKey)((c1 - '1') + ImGuiKey_F1);
|
|
else if (c1 == '1' && (c2 >= '0' && c2 <= '9')) // F10-F19
|
|
return (ImGuiKey)((c2 - '0') + ImGuiKey_F10);
|
|
else if (c1 == '2' && (c2 >= '0' && c2 <= '4')) // F20-F24
|
|
return (ImGuiKey)((c2 - '0') + ImGuiKey_F20);
|
|
}
|
|
|
|
// Slowest path
|
|
if (!strcmp(key, "Tab"))
|
|
return ImGuiKey_Tab;
|
|
else if (!strcmp(key, "ArrowLeft"))
|
|
return ImGuiKey_LeftArrow;
|
|
else if (!strcmp(key, "ArrowRight"))
|
|
return ImGuiKey_RightArrow;
|
|
else if (!strcmp(key, "ArrowUp"))
|
|
return ImGuiKey_UpArrow;
|
|
else if (!strcmp(key, "ArrowDown"))
|
|
return ImGuiKey_DownArrow;
|
|
else if (!strcmp(key, "PageUp"))
|
|
return ImGuiKey_PageUp;
|
|
else if (!strcmp(key, "PageDown"))
|
|
return ImGuiKey_PageDown;
|
|
else if (!strcmp(key, "Home"))
|
|
return ImGuiKey_Home;
|
|
else if (!strcmp(key, "End"))
|
|
return ImGuiKey_End;
|
|
else if (!strcmp(key, "Insert"))
|
|
return ImGuiKey_Insert;
|
|
else if (!strcmp(key, "Delete"))
|
|
return ImGuiKey_Delete;
|
|
else if (!strcmp(key, "Backspace"))
|
|
return ImGuiKey_Backspace;
|
|
else if (!strcmp(key, "Space"))
|
|
return ImGuiKey_Space;
|
|
else if (!strcmp(key, "Enter"))
|
|
return ImGuiKey_Enter;
|
|
else if (!strcmp(key, "Escape"))
|
|
return ImGuiKey_Escape;
|
|
else if (!strcmp(key, "ContextMenu"))
|
|
return ImGuiKey_Menu;
|
|
else if (!strcmp(key, "CapsLock"))
|
|
return ImGuiKey_CapsLock;
|
|
else if (!strcmp(key, "ScrollLock"))
|
|
return ImGuiKey_ScrollLock;
|
|
else if (!strcmp(key, "NumLock"))
|
|
return ImGuiKey_NumLock;
|
|
else if (!strcmp(key, "PrintScreen"))
|
|
return ImGuiKey_PrintScreen;
|
|
else if (!strcmp(key, "Pause"))
|
|
return ImGuiKey_Pause;
|
|
}
|
|
|
|
return ImGuiKey_None;
|
|
}
|
|
|
|
static bool ImGui_ImplEmscripten_KeyCallback(int type, const EmscriptenKeyboardEvent* event, void* user_data)
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
switch (type)
|
|
{
|
|
case EMSCRIPTEN_EVENT_KEYDOWN:
|
|
case EMSCRIPTEN_EVENT_KEYUP:
|
|
{
|
|
if (event->repeat)
|
|
{
|
|
if (event->key[1] == '\0')
|
|
io.AddInputCharacter(event->key[0]);
|
|
return false;
|
|
}
|
|
|
|
io.AddKeyEvent(ImGuiMod_Ctrl, event->ctrlKey);
|
|
io.AddKeyEvent(ImGuiMod_Shift, event->shiftKey);
|
|
io.AddKeyEvent(ImGuiMod_Alt, event->altKey);
|
|
io.AddKeyEvent(ImGuiMod_Super, event->metaKey);
|
|
|
|
ImGuiKey key = ImGui_ImplEmscripten_KeyToImGuiKey(event->key);
|
|
if (key != ImGuiKey_None)
|
|
{
|
|
bool down = type == EMSCRIPTEN_EVENT_KEYDOWN;
|
|
if (down && event->key[1] == '\0')
|
|
io.AddInputCharacter(event->key[0]);
|
|
io.AddKeyEvent(key, down);
|
|
}
|
|
|
|
// Without this those keys won't be dispatched with "keyup"
|
|
if (key == ImGuiKey_Tab || key == ImGuiKey_Backspace)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#ifndef IMGUI_IMPL_EMSCRIPTEN_POINTER_EVENTS
|
|
static bool ImGui_ImplEmscripten_MouseCallback(int type, const EmscriptenMouseEvent* event, void* user_data)
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
switch (type)
|
|
{
|
|
case EMSCRIPTEN_EVENT_MOUSEDOWN:
|
|
case EMSCRIPTEN_EVENT_MOUSEUP:
|
|
{
|
|
unsigned short button = event->button;
|
|
switch (button)
|
|
{
|
|
case 0: button = 0; break;
|
|
case 1: button = 2; break;
|
|
case 2: button = 1; break;
|
|
default: return false;
|
|
}
|
|
|
|
io.AddMouseSourceEvent(ImGuiMouseSource_Mouse);
|
|
io.AddMouseButtonEvent(button, type == EMSCRIPTEN_EVENT_MOUSEDOWN);
|
|
return false;
|
|
}
|
|
case EMSCRIPTEN_EVENT_MOUSEMOVE:
|
|
{
|
|
io.AddMouseSourceEvent(ImGuiMouseSource_Mouse);
|
|
io.AddMousePosEvent((float)event->clientX, (float)event->clientY);
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
static bool ImGui_ImplEmscripten_WheelCallback(int type, const EmscriptenWheelEvent* event, void* user_data)
|
|
{
|
|
if (type == EMSCRIPTEN_EVENT_WHEEL)
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
switch (event->deltaMode)
|
|
{
|
|
case DOM_DELTA_PIXEL:
|
|
{
|
|
io.AddMouseWheelEvent(-(float)event->deltaX / 100.f, -(float)event->deltaY / 100.f);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool ImGui_ImplEmscripten_FocusCallback(int type, const EmscriptenFocusEvent* event, void* user_data)
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
switch (type)
|
|
{
|
|
case EMSCRIPTEN_EVENT_FOCUSIN:
|
|
case EMSCRIPTEN_EVENT_FOCUSOUT:
|
|
{
|
|
io.AddFocusEvent(type == EMSCRIPTEN_EVENT_FOCUSIN);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#ifdef IMGUI_IMPL_EMSCRIPTEN_ENABLE_CLIPBOARD
|
|
static const char* ImGui_ImplEmscripten_GetClipboardText(ImGuiContext*)
|
|
{
|
|
ImGui_ImplEmscripten_BackendData* bd = ImGui_ImplEmscripten_GetBackendData();
|
|
return bd->ClipboardTextData;
|
|
}
|
|
|
|
static void ImGui_ImplEmscripten_SetClipboardText(ImGuiContext*, const char* text)
|
|
{
|
|
ImGui_ImplEmscripten_BackendData* bd = ImGui_ImplEmscripten_GetBackendData();
|
|
if (bd->ClipboardTextData)
|
|
free((void*)bd->ClipboardTextData);
|
|
bd->ClipboardTextData = strdup(text);
|
|
|
|
if (bd->ClipboardTextData)
|
|
{
|
|
EM_ASM({
|
|
try { navigator.clipboard.writeText(UTF8ToString($0)); }
|
|
catch (err) { console.error(err); }
|
|
}, bd->ClipboardTextData);
|
|
}
|
|
}
|
|
|
|
extern "C"
|
|
{
|
|
|
|
EMSCRIPTEN_KEEPALIVE
|
|
void ImGui_ImplEmscripten_ClipboardPasteCallback(const char* text)
|
|
{
|
|
ImGui_ImplEmscripten_BackendData* bd = ImGui_ImplEmscripten_GetBackendData();
|
|
if (bd->ClipboardTextData)
|
|
free((void*)bd->ClipboardTextData);
|
|
bd->ClipboardTextData = strdup(text);
|
|
}
|
|
|
|
};
|
|
|
|
static void ImGui_ImplEmscripten_SetupClipboardCallbacks(ImGui_ImplEmscripten_BackendData* bd)
|
|
{
|
|
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
|
platform_io.Platform_SetClipboardTextFn = ImGui_ImplEmscripten_SetClipboardText;
|
|
platform_io.Platform_GetClipboardTextFn = ImGui_ImplEmscripten_GetClipboardText;
|
|
|
|
EM_ASM({
|
|
const el = document.querySelector(UTF8ToString($0));
|
|
|
|
document.addEventListener('paste', (ev) => {
|
|
Module['_ImGui_ImplEmscripten_ClipboardPasteCallback'](stringToNewUTF8(ev.clipboardData.getData('text/plain')));
|
|
});
|
|
}, bd->TargetId);
|
|
}
|
|
#endif
|
|
|
|
#ifdef IMGUI_IMPL_EMSCRIPTEN_POINTER_EVENTS
|
|
extern "C"
|
|
{
|
|
|
|
#define IMGUI_IMPL_EMSCRIPTEN_POINTER_EVENT_HAS_POS 1
|
|
#define IMGUI_IMPL_EMSCRIPTEN_POINTER_EVENT_HAS_BUTTON 2
|
|
#define IMGUI_IMPL_EMSCRIPTEN_POINTER_EVENT_IS_PRIMARY 4
|
|
|
|
EMSCRIPTEN_KEEPALIVE
|
|
void ImGui_ImplEmscripten_PointerEvent(int type, int pointer_type, int flags, int x, int y, int buttons)
|
|
{
|
|
if (!(flags & IMGUI_IMPL_EMSCRIPTEN_POINTER_EVENT_IS_PRIMARY))
|
|
return;
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
ImGui_ImplEmscripten_BackendData* bd = ImGui_ImplEmscripten_GetBackendData(io);
|
|
|
|
if (type == 5)
|
|
{
|
|
for (int i = 0; i < ImGuiMouseButton_COUNT; i++)
|
|
io.AddMouseButtonEvent((ImGuiMouseButton)i, false);
|
|
}
|
|
else if (pointer_type >= 0 && pointer_type <= 2)
|
|
{
|
|
ImGuiMouseSource source;
|
|
switch (pointer_type)
|
|
{
|
|
case 0: source = ImGuiMouseSource_Mouse; break;
|
|
case 1: source = ImGuiMouseSource_TouchScreen; break;
|
|
case 2: source = ImGuiMouseSource_Pen; break;
|
|
}
|
|
|
|
io.AddMouseSourceEvent(source);
|
|
if (flags & IMGUI_IMPL_EMSCRIPTEN_POINTER_EVENT_HAS_POS)
|
|
io.AddMousePosEvent((float)x, (float)y);
|
|
if (flags & IMGUI_IMPL_EMSCRIPTEN_POINTER_EVENT_HAS_BUTTON)
|
|
{
|
|
// HACK: Browser executes pointerup/down only for single mouse button if multiple buttons were pressed at the same time
|
|
#define PROCESS_BUTTON(key, flag) if ((buttons & flag) != (bd->PressedButtons & flag)) { io.AddMouseButtonEvent(key, buttons & flag); }
|
|
PROCESS_BUTTON(ImGuiMouseButton_Left, 1);
|
|
PROCESS_BUTTON(ImGuiMouseButton_Right, 2);
|
|
PROCESS_BUTTON(ImGuiMouseButton_Middle, 4);
|
|
PROCESS_BUTTON(3, 8);
|
|
PROCESS_BUTTON(4, 16);
|
|
#undef PROCESS_BUTTON
|
|
}
|
|
}
|
|
|
|
if (type == 0 || type == 1 || type == 3)
|
|
bd->PressedButtons = buttons;
|
|
}
|
|
|
|
};
|
|
|
|
static void ImGui_ImplEmscripten_SetupPointerEvents(ImGui_ImplEmscripten_BackendData* bd)
|
|
{
|
|
EM_ASM({
|
|
function t(name)
|
|
{
|
|
switch (name)
|
|
{
|
|
case "mouse": return 0;
|
|
case "touch": return 1;
|
|
case "pen": return 2;
|
|
default: return -1;
|
|
}
|
|
}
|
|
|
|
function p(ev)
|
|
{
|
|
return ev.isPrimary ? 4 : 0;
|
|
}
|
|
|
|
const el = document.querySelector(UTF8ToString($0));
|
|
const cb = Module['_ImGui_ImplEmscripten_PointerEvent'];
|
|
el.imguiHandlePointerDown = function(ev) { cb(0, t(ev.pointerType), 1 | 2 | p(ev), ev.clientX, ev.clientY, ev.buttons); };
|
|
el.imguiHandlePointerUp = function(ev) { cb(1, t(ev.pointerType), 1 | 2 | p(ev), ev.clientX, ev.clientY, ev.buttons); };
|
|
el.imguiHandlePointerMove = function(ev) { cb(2, t(ev.pointerType), 1 | p(ev), ev.clientX, ev.clientY, ev.buttons); };
|
|
el.imguiHandlePointerCancel = function(ev) { cb(3, t(ev.pointerType), 2 | p(ev), 0, 0, ev.buttons); };
|
|
el.imguiHandlePointerEnter = function(ev) { cb(4, t(ev.pointerType), 0 | p(ev), 0, 0, ev.buttons); };
|
|
el.imguiHandlePointerLeave = function(ev) { cb(5, t(ev.pointerType), 0 | p(ev), 0, 0, ev.buttons); };
|
|
|
|
el.addEventListener('pointerdown', el.imguiHandlePointerDown);
|
|
el.addEventListener('pointerup', el.imguiHandlePointerUp);
|
|
el.addEventListener('pointermove', el.imguiHandlePointerMove);
|
|
el.addEventListener('pointercancel', el.imguiHandlePointerCancel);
|
|
el.addEventListener('pointerenter', el.imguiHandlePointerEnter);
|
|
el.addEventListener('pointerleave', el.imguiHandlePointerLeave);
|
|
}, bd->TargetId);
|
|
}
|
|
|
|
static void ImGui_ImplEmscripten_ClearPointerEvents(ImGui_ImplEmscripten_BackendData* bd)
|
|
{
|
|
EM_ASM({
|
|
const el = document.querySelector(UTF8ToString($0));
|
|
el.removeEventListener('pointerdown', el.imguiHandlePointerDown);
|
|
el.removeEventListener('pointerup', el.imguiHandlePointerUp);
|
|
el.removeEventListener('pointermove', el.imguiHandlePointerMove);
|
|
el.removeEventListener('pointercancel', el.imguiHandlePointerCancel);
|
|
el.removeEventListener('pointerenter', el.imguiHandlePointerEnter);
|
|
el.removeEventListener('pointerleave', el.imguiHandlePointerLeave);
|
|
|
|
delete el.imguiHandlePointerDown;
|
|
delete el.imguiHandlePointerUp;
|
|
delete el.imguiHandlePointerMove;
|
|
delete el.imguiHandlePointerCancel;
|
|
delete el.imguiHandlePointerEnter;
|
|
delete el.imguiHandlePointerLeave;
|
|
}, bd->TargetId);
|
|
}
|
|
#endif
|
|
|
|
static bool ImGui_ImplEmscripten_GamepadCallback(int type, const EmscriptenGamepadEvent* event, void* user_data)
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
ImGui_ImplEmscripten_BackendData* bd = ImGui_ImplEmscripten_GetBackendData(io);
|
|
|
|
if (type == EMSCRIPTEN_EVENT_GAMEPADCONNECTED)
|
|
bd->GamepadsCount++;
|
|
else if (type == EMSCRIPTEN_EVENT_GAMEPADDISCONNECTED)
|
|
bd->GamepadsCount--;
|
|
|
|
if (bd->GamepadsCount > 0)
|
|
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
|
else
|
|
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
|
|
return false;
|
|
}
|
|
|
|
static void ImGui_ImplEmscripten_UpdateGamepads(ImGuiIO& io)
|
|
{
|
|
if (!(io.BackendFlags & ImGuiBackendFlags_HasGamepad))
|
|
return;
|
|
|
|
EmscriptenGamepadEvent state;
|
|
if (emscripten_sample_gamepad_data() != EMSCRIPTEN_RESULT_SUCCESS || emscripten_get_gamepad_status(0, &state) != EMSCRIPTEN_RESULT_SUCCESS)
|
|
return;
|
|
|
|
#define IM_SATURATE(V) (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V)
|
|
#define MAP_BUTTON(KEY_NO, BUTTON_IDX) { io.AddKeyEvent(KEY_NO, state.digitalButton[BUTTON_IDX]); }
|
|
#define MAP_ANALOG(KEY_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); io.AddKeyAnalogEvent(KEY_NO, vn > 0.10f, IM_SATURATE(vn)); }
|
|
MAP_BUTTON(ImGuiKey_GamepadStart, 9);
|
|
MAP_BUTTON(ImGuiKey_GamepadBack, 8);
|
|
MAP_BUTTON(ImGuiKey_GamepadFaceLeft, 2);
|
|
MAP_BUTTON(ImGuiKey_GamepadFaceRight, 1);
|
|
MAP_BUTTON(ImGuiKey_GamepadFaceUp, 3);
|
|
MAP_BUTTON(ImGuiKey_GamepadFaceDown, 0);
|
|
MAP_BUTTON(ImGuiKey_GamepadDpadLeft, 14);
|
|
MAP_BUTTON(ImGuiKey_GamepadDpadRight, 15);
|
|
MAP_BUTTON(ImGuiKey_GamepadDpadUp, 12);
|
|
MAP_BUTTON(ImGuiKey_GamepadDpadDown, 13);
|
|
MAP_BUTTON(ImGuiKey_GamepadL1, 4);
|
|
MAP_BUTTON(ImGuiKey_GamepadR1, 5);
|
|
MAP_ANALOG(ImGuiKey_GamepadL2, state.analogButton[6], 0.125, 1.0);
|
|
MAP_ANALOG(ImGuiKey_GamepadR2, state.analogButton[7], 0.125, 1.0);
|
|
MAP_BUTTON(ImGuiKey_GamepadL3, 10);
|
|
MAP_BUTTON(ImGuiKey_GamepadR3, 11);
|
|
MAP_ANALOG(ImGuiKey_GamepadLStickLeft, state.axis[0], -0.25, -1.0);
|
|
MAP_ANALOG(ImGuiKey_GamepadLStickRight, state.axis[0], +0.25, +1.0);
|
|
MAP_ANALOG(ImGuiKey_GamepadLStickUp, state.axis[1], -0.25, -1.0);
|
|
MAP_ANALOG(ImGuiKey_GamepadLStickDown, state.axis[1], +0.25, +1.0);
|
|
MAP_ANALOG(ImGuiKey_GamepadRStickLeft, state.axis[2], -0.25, -1.0);
|
|
MAP_ANALOG(ImGuiKey_GamepadRStickRight, state.axis[2], +0.25, +1.0);
|
|
MAP_ANALOG(ImGuiKey_GamepadRStickUp, state.axis[3], -0.25, -1.0);
|
|
MAP_ANALOG(ImGuiKey_GamepadRStickDown, state.axis[3], +0.25, +1.0);
|
|
#undef MAP_BUTTON
|
|
#undef MAP_ANALOG
|
|
#undef IM_SATURATE
|
|
}
|
|
|
|
bool ImGui_ImplEmscripten_Init(const char* target_id)
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
IMGUI_CHECKVERSION();
|
|
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
|
|
|
|
ImGui_ImplEmscripten_BackendData* bd = IM_NEW(ImGui_ImplEmscripten_BackendData)();
|
|
io.BackendPlatformUserData = (void*)bd;
|
|
io.BackendPlatformName = "imgui_impl_emscripten";
|
|
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
|
|
|
|
double width, height;
|
|
emscripten_get_element_css_size(target_id, &width, &height);
|
|
bd->Width = width;
|
|
bd->Height = height;
|
|
bd->Time = emscripten_performance_now();
|
|
bd->LastMouseCursor = ImGuiMouseCursor_COUNT;
|
|
bd->ClipboardTextData = nullptr;
|
|
bd->GamepadsCount = 0;
|
|
bd->PressedButtons = 0;
|
|
|
|
size_t target_id_size = strlen(target_id);
|
|
bd->TargetId = (char*)IM_ALLOC(target_id_size + 1);
|
|
memcpy(bd->TargetId, target_id, target_id_size + 1);
|
|
|
|
io.DisplaySize = { (float)bd->Width, (float)bd->Height };
|
|
|
|
emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, &ImGui_ImplEmscripten_KeyCallback);
|
|
emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, &ImGui_ImplEmscripten_KeyCallback);
|
|
|
|
#ifdef IMGUI_IMPL_EMSCRIPTEN_ENABLE_CLIPBOARD
|
|
ImGui_ImplEmscripten_SetupClipboardCallbacks(bd);
|
|
#endif
|
|
|
|
#ifndef IMGUI_IMPL_EMSCRIPTEN_POINTER_EVENTS
|
|
emscripten_set_click_callback(target_id, NULL, false, &ImGui_ImplEmscripten_MouseCallback);
|
|
emscripten_set_mousedown_callback(target_id, NULL, false, &ImGui_ImplEmscripten_MouseCallback);
|
|
emscripten_set_mouseup_callback(target_id, NULL, false, &ImGui_ImplEmscripten_MouseCallback);
|
|
emscripten_set_dblclick_callback(target_id, NULL, false, &ImGui_ImplEmscripten_MouseCallback);
|
|
emscripten_set_mousemove_callback(target_id, NULL, false, &ImGui_ImplEmscripten_MouseCallback);
|
|
emscripten_set_mouseenter_callback(target_id, NULL, false, &ImGui_ImplEmscripten_MouseCallback);
|
|
emscripten_set_mouseleave_callback(target_id, NULL, false, &ImGui_ImplEmscripten_MouseCallback);
|
|
#else
|
|
ImGui_ImplEmscripten_SetupPointerEvents(bd);
|
|
#endif
|
|
|
|
emscripten_set_wheel_callback(target_id, NULL, false, &ImGui_ImplEmscripten_WheelCallback);
|
|
|
|
emscripten_set_focusin_callback(target_id, NULL, false, &ImGui_ImplEmscripten_FocusCallback);
|
|
emscripten_set_focusout_callback(target_id, NULL, false, &ImGui_ImplEmscripten_FocusCallback);
|
|
|
|
emscripten_set_gamepadconnected_callback(NULL, false, &ImGui_ImplEmscripten_GamepadCallback);
|
|
emscripten_set_gamepaddisconnected_callback(NULL, false, &ImGui_ImplEmscripten_GamepadCallback);
|
|
|
|
return true;
|
|
}
|
|
|
|
void ImGui_ImplEmscripten_Shutdown()
|
|
{
|
|
ImGui_ImplEmscripten_BackendData* bd = ImGui_ImplEmscripten_GetBackendData();
|
|
|
|
emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, NULL);
|
|
emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, NULL);
|
|
|
|
#ifndef IMGUI_IMPL_EMSCRIPTEN_POINTER_EVENTS
|
|
emscripten_set_click_callback(bd->TargetId, NULL, false, NULL);
|
|
emscripten_set_mousedown_callback(bd->TargetId, NULL, false, NULL);
|
|
emscripten_set_mouseup_callback(bd->TargetId, NULL, false, NULL);
|
|
emscripten_set_dblclick_callback(bd->TargetId, NULL, false, NULL);
|
|
emscripten_set_mousemove_callback(bd->TargetId, NULL, false, NULL);
|
|
emscripten_set_mouseenter_callback(bd->TargetId, NULL, false, NULL);
|
|
emscripten_set_mouseleave_callback(bd->TargetId, NULL, false, NULL);
|
|
#else
|
|
ImGui_ImplEmscripten_ClearPointerEvents(bd);
|
|
#endif
|
|
|
|
emscripten_set_wheel_callback(bd->TargetId, NULL, false, NULL);
|
|
|
|
emscripten_set_focusin_callback(bd->TargetId, NULL, false, NULL);
|
|
emscripten_set_focusout_callback(bd->TargetId, NULL, false, NULL);
|
|
|
|
emscripten_set_gamepadconnected_callback(NULL, false, NULL);
|
|
emscripten_set_gamepaddisconnected_callback(NULL, false, NULL);
|
|
|
|
free((void*)bd->TargetId);
|
|
IM_DELETE(bd);
|
|
}
|
|
|
|
void ImGui_ImplEmscripten_NewFrame()
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
ImGui_ImplEmscripten_BackendData* bd = ImGui_ImplEmscripten_GetBackendData(io);
|
|
|
|
double current_time = emscripten_performance_now();
|
|
io.DeltaTime = (float)((current_time - bd->Time) / 1000.0);
|
|
bd->Time = current_time;
|
|
|
|
// Update OS mouse cursor with the cursor requested by imgui
|
|
ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
|
|
if (bd->LastMouseCursor != mouse_cursor)
|
|
{
|
|
bd->LastMouseCursor = mouse_cursor;
|
|
ImGui_ImplEmscripten_UpdateMouseCursor(io, mouse_cursor);
|
|
}
|
|
|
|
ImGui_ImplEmscripten_UpdateGamepads(io);
|
|
}
|
|
|
|
void ImGui_ImplEmscripten_UpdateCanvasSize(int width, int height)
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
ImGui_ImplEmscripten_BackendData* bd = ImGui_ImplEmscripten_GetBackendData(io);
|
|
|
|
bd->Width = width;
|
|
bd->Height = height;
|
|
|
|
io.DisplaySize = { (float)bd->Width, (float)bd->Height };
|
|
}
|
|
|
|
#endif // #ifndef IMGUI_DISABLE
|