1
0
mirror of https://github.com/ocornut/imgui.git synced 2024-11-23 23:31:08 +01:00

add imgui_impl_emscripten platform backend

This commit is contained in:
SlowRiot 2024-11-22 18:01:47 +00:00
parent 5b7feebfd8
commit 39bd33a192
2 changed files with 595 additions and 0 deletions

View File

@ -0,0 +1,543 @@
// dear imgui: Platform Backend for Emscripten HTML5
//
// See documentation in imgui_impl_emscripten.h.
//
// Copyright 2024 Eugene Hopkinson
#include "imgui.h"
#ifndef IMGUI_DISABLE
#include "imgui_impl_emscripten.h"
#include <emscripten.h>
#include <emscripten/html5.h>
#include <emscripten/val.h>
namespace {
/// A map of HTML5 key names to imgui keys
static const std::unordered_map<std::string, ImGuiKey> key_translate_lookup{
// main character keys
{"Backquote", ImGuiKey_GraveAccent},
{"Backslash", ImGuiKey_Backslash},
{"BracketLeft", ImGuiKey_LeftBracket},
{"BracketRight", ImGuiKey_RightBracket},
{"Comma", ImGuiKey_Comma},
{"Digit0", ImGuiKey_0},
{"Digit1", ImGuiKey_1},
{"Digit2", ImGuiKey_2},
{"Digit3", ImGuiKey_3},
{"Digit4", ImGuiKey_4},
{"Digit5", ImGuiKey_5},
{"Digit6", ImGuiKey_6},
{"Digit7", ImGuiKey_7},
{"Digit8", ImGuiKey_8},
{"Digit9", ImGuiKey_9},
{"Equal", ImGuiKey_Equal},
{"IntlBackslash", ImGuiKey_Backslash}, // Mapping to generic backslash
{"IntlRo", ImGuiKey_Slash}, // Closest match for non-standard layouts
{"IntlYen", ImGuiKey_Backslash}, // Closest match for non-standard layouts
{"KeyA", ImGuiKey_A},
{"KeyB", ImGuiKey_B},
{"KeyC", ImGuiKey_C},
{"KeyD", ImGuiKey_D},
{"KeyE", ImGuiKey_E},
{"KeyF", ImGuiKey_F},
{"KeyG", ImGuiKey_G},
{"KeyH", ImGuiKey_H},
{"KeyI", ImGuiKey_I},
{"KeyJ", ImGuiKey_J},
{"KeyK", ImGuiKey_K},
{"KeyL", ImGuiKey_L},
{"KeyM", ImGuiKey_M},
{"KeyN", ImGuiKey_N},
{"KeyO", ImGuiKey_O},
{"KeyP", ImGuiKey_P},
{"KeyQ", ImGuiKey_Q},
{"KeyR", ImGuiKey_R},
{"KeyS", ImGuiKey_S},
{"KeyT", ImGuiKey_T},
{"KeyU", ImGuiKey_U},
{"KeyV", ImGuiKey_V},
{"KeyW", ImGuiKey_W},
{"KeyX", ImGuiKey_X},
{"KeyY", ImGuiKey_Y},
{"KeyZ", ImGuiKey_Z},
{"Minus", ImGuiKey_Minus},
{"Period", ImGuiKey_Period},
{"Quote", ImGuiKey_Apostrophe},
{"Semicolon", ImGuiKey_Semicolon},
{"Slash", ImGuiKey_Slash},
// control keys
{"AltLeft", ImGuiKey_LeftAlt},
{"AltRight", ImGuiKey_RightAlt},
{"Backspace", ImGuiKey_Backspace},
{"CapsLock", ImGuiKey_CapsLock},
{"ContextMenu", ImGuiKey_Menu},
{"ControlLeft", ImGuiKey_LeftCtrl},
{"ControlRight", ImGuiKey_RightCtrl},
{"Enter", ImGuiKey_Enter},
{"MetaLeft", ImGuiKey_LeftSuper},
{"MetaRight", ImGuiKey_RightSuper},
{"ShiftLeft", ImGuiKey_LeftShift},
{"ShiftRight", ImGuiKey_RightShift},
{"Space", ImGuiKey_Space},
{"Tab", ImGuiKey_Tab},
// navigation key group
{"Delete", ImGuiKey_Delete},
{"End", ImGuiKey_End},
//{"Help", ImGuiKey_PrintScreen}, // Best approximation
{"Home", ImGuiKey_Home},
{"Insert", ImGuiKey_Insert},
{"PageDown", ImGuiKey_PageDown},
{"PageUp", ImGuiKey_PageUp},
// arrow key group
{"ArrowDown", ImGuiKey_DownArrow},
{"ArrowLeft", ImGuiKey_LeftArrow},
{"ArrowRight", ImGuiKey_RightArrow},
{"ArrowUp", ImGuiKey_UpArrow},
// number pad group
{"NumLock", ImGuiKey_NumLock},
{"Numpad0", ImGuiKey_Keypad0},
{"Numpad1", ImGuiKey_Keypad1},
{"Numpad2", ImGuiKey_Keypad2},
{"Numpad3", ImGuiKey_Keypad3},
{"Numpad4", ImGuiKey_Keypad4},
{"Numpad5", ImGuiKey_Keypad5},
{"Numpad6", ImGuiKey_Keypad6},
{"Numpad7", ImGuiKey_Keypad7},
{"Numpad8", ImGuiKey_Keypad8},
{"Numpad9", ImGuiKey_Keypad9},
{"NumpadAdd", ImGuiKey_KeypadAdd},
{"NumpadBackspace", ImGuiKey_Backspace}, // No direct mapping; backspace functionality
//{"NumpadClear", ImGuiKey_KeypadClear}, // Custom-defined if needed
//{"NumpadClearEntry", ImGuiKey_KeypadClear}, // Custom-defined if needed
{"NumpadComma", ImGuiKey_KeypadDecimal}, // Closest match
{"NumpadDecimal", ImGuiKey_KeypadDecimal},
{"NumpadDivide", ImGuiKey_KeypadDivide},
{"NumpadEnter", ImGuiKey_KeypadEnter},
{"NumpadEqual", ImGuiKey_KeypadEqual},
{"NumpadHash", ImGuiKey_Backslash}, // Mapped to # on UK keyboard
//{"NumpadMemoryAdd", ImGuiKey_None}, // No defined mapping
//{"NumpadMemoryClear", ImGuiKey_None}, // No defined mapping
//{"NumpadMemoryRecall", ImGuiKey_None}, // No defined mapping
//{"NumpadMemoryStore", ImGuiKey_None}, // No defined mapping
//{"NumpadMemorySubtract", ImGuiKey_None}, // No defined mapping
{"NumpadMultiply", ImGuiKey_KeypadMultiply},
{"NumpadParenLeft", ImGuiKey_LeftBracket}, // Closest available
{"NumpadParenRight", ImGuiKey_RightBracket}, // Closest available
{"NumpadStar", ImGuiKey_KeypadMultiply}, // Same as multiply
{"NumpadSubtract", ImGuiKey_KeypadSubtract},
// top row key groups
{"Escape", ImGuiKey_Escape},
{"F1", ImGuiKey_F1},
{"F2", ImGuiKey_F2},
{"F3", ImGuiKey_F3},
{"F4", ImGuiKey_F4},
{"F5", ImGuiKey_F5},
{"F6", ImGuiKey_F6},
{"F6", ImGuiKey_F6},
{"F7", ImGuiKey_F7},
{"F8", ImGuiKey_F8},
{"F9", ImGuiKey_F9},
{"F10", ImGuiKey_F10},
{"F11", ImGuiKey_F11},
{"F12", ImGuiKey_F12},
//{"Fn", ImGuiKey_None}, // No direct mapping
//{"FnLock", ImGuiKey_None}, // No direct mapping
{"PrintScreen", ImGuiKey_PrintScreen},
{"ScrollLock", ImGuiKey_ScrollLock},
{"Pause", ImGuiKey_Pause},
};
ImGuiKey translate_key(char const* emscripten_key) __attribute__((__const__));
ImGuiKey translate_key(char const* emscripten_key) {
/// Translate an emscripten-provided browser string describing a keycode to an imgui key code
if(auto it{key_translate_lookup.find(emscripten_key)}; it != key_translate_lookup.end()) {
return it->second;
}
return ImGuiKey_None;
}
constexpr ImGuiMouseButton translate_mousebutton(unsigned short emscripten_button) __attribute__((__const__));
constexpr ImGuiMouseButton translate_mousebutton(unsigned short emscripten_button) {
/// Translate an emscripten-provided integer describing a mouse button to an imgui mouse button
if(emscripten_button == 1) return ImGuiMouseButton_Middle; // 1 = middle mouse button
if(emscripten_button == 2) return ImGuiMouseButton_Right; // 2 = right mouse button
if(emscripten_button > ImGuiMouseButton_COUNT) return ImGuiMouseButton_Middle; // treat any weird clicks on unexpected buttons (button 6 upwards) as middle mouse
return emscripten_button; // any other button translates 1:1
}
} // anonymous namespace
namespace emscripten_browser_cursor_internal {
////////////////////////////////// Interface ///////////////////////////////////
enum class cursor {
// General
cursor_auto, // The UA will determine the cursor to display based on the current context. E.g., equivalent to text when hovering text.
cursor_default, // The platform-dependent default cursor. Typically an arrow.
none, // No cursor is rendered.
// Links & status
context_menu, // cursor slightly obscuring a menu icon - A context menu is available.
help, // cursor next to a question mark - Help information is available.
pointer, // right hand with an index finger pointing up - The cursor is a pointer that indicates a link. Typically an image of a pointing hand.
progress, // cursor and hour glass - The program is busy in the background, but the user can still interact with the interface (in contrast to wait).
wait, // hour glass - The program is busy, and the user can't interact with the interface (in contrast to progress). Sometimes an image of an hourglass or a watch.
// Selection
cell, // plus symbol - The table cell or set of cells can be selected.
crosshair, // crosshair - Cross cursor, often used to indicate selection in a bitmap.
text, // vertical i-beam - The text can be selected. Typically the shape of an I-beam.
vertical_text, // horizontal i-beam - The vertical text can be selected. Typically the shape of a sideways I-beam.
// Drag & drop
alias, // cursor next to a folder icon with a curved arrow pointing up and to the right - An alias or shortcut is to be created.
copy, // cursor next to a smaller folder icon with a plus sign - Something is to be copied.
move, // plus sign made of two thin lines, with small arrows facing out - Something is to be moved.
no_drop, // cursor next to circle with a line through it - An item may not be dropped at the current location.
not_allowed, // Circle with a line through it - The requested action will not be carried out.
grab, // fully opened hand - Something can be grabbed (dragged to be moved).
grabbing, // closed hand - Something is being grabbed (dragged to be moved).
// Resizing & scrolling
all_scroll, // dot with four triangles around it - Something can be scrolled in any direction (panned).
col_resize, // The item/column can be resized horizontally. Often rendered as arrows pointing left and right with a vertical bar separating them.
row_resize, // The item/row can be resized vertically. Often rendered as arrows pointing up and down with a horizontal bar separating them.
n_resize, // arrow pointing up - Some edge is to be moved. For example, the se-resize cursor is used when the movement starts from the south-east corner of the box.
e_resize, // arrow pointing right
s_resize, // arrow pointing down
w_resize, // arrow pointing left
ne_resize, // arrow pointing top-right
nw_resize, // arrow pointing top-left
se_resize, // arrow pointing bottom-right
sw_resize, // arrow pointing bottom-left
ew_resize, // arrow pointing left and right - Bidirectional resize cursor.
ns_resize, // arrow pointing up and down
nesw_resize, // arrow pointing both to the top-right and bottom-left
nwse_resize, // arrow pointing both to the top-left and bottom-right
// Zooming
zoom_in, // magnifying glass with a plus sign - Something can be zoomed (magnified) in or out.
zoom_out,
// Special invalid value
invalid = std::numeric_limits<int>::max()
};
void set(cursor new_cursor); // set a new cursor from a cursor enum
void unset(); // clear the current cursor setting
//////////////////////////////// Implementation ////////////////////////////////
bool is_set() {
/// Returns whether the cursor is currently set
return EM_ASM_INT(
return !(!document.body.style.cursor || document.body.style.cursor.length === 0 );
);
}
std::string get_string() {
/// Return the current cursor setting as a string
auto cursor_str_ptr{reinterpret_cast<char*>(EM_ASM_PTR(
return stringToNewUTF8(document.body.style.cursor);
))};
std::string const cursor_str{cursor_str_ptr};
free(cursor_str_ptr);
return cursor_str;
}
void set(cursor new_cursor) {
/// Set the cursor according to the given enum
// Note, implementations omitted for cursors not used by imgui. For full implementation, use https://github.com/Armchair-Software/emscripten-browser-cursor
switch(new_cursor) {
case cursor::cursor_default:
default:
EM_ASM(document.body.style.cursor = 'default';);
break;
case cursor::pointer:
EM_ASM(document.body.style.cursor = 'pointer';);
break;
case cursor::text:
EM_ASM(document.body.style.cursor = 'text';);
break;
case cursor::move:
EM_ASM(document.body.style.cursor = 'move';);
break;
case cursor::not_allowed:
EM_ASM(document.body.style.cursor = 'not-allowed';);
break;
case cursor::ew_resize:
EM_ASM(document.body.style.cursor = 'ew-resize';);
break;
case cursor::ns_resize:
EM_ASM(document.body.style.cursor = 'ns-resize';);
break;
case cursor::nesw_resize:
EM_ASM(document.body.style.cursor = 'nesw-resize';);
break;
case cursor::nwse_resize:
EM_ASM(document.body.style.cursor = 'nwse-resize';);
break;
}
}
void set(std::string const &new_cursor) {
/// Set the cursor from an arbitrary string
EM_ASM({
document.body.style.cursor = UTF8ToString($0);
}, new_cursor.c_str());
}
} // namespace emscripten_browser_cursor
namespace {
void update_cursor() {
/// Sync any cursor changes due to ImGUI to the browser's cursor
static emscripten_browser_cursor_internal::cursor current_cursor{emscripten_browser_cursor_internal::cursor::invalid};
static std::optional<std::string> cursor_to_restore;
auto set_cursor_if_necessary{[&](emscripten_browser_cursor_internal::cursor new_cursor){
if(new_cursor == current_cursor) return; // don't do anything if the current cursor is already set
current_cursor = new_cursor;
emscripten_browser_cursor_internal::set(new_cursor);
}};
if(ImGui::GetIO().WantCaptureMouse) { // mouse is hovering over the gui
if(!cursor_to_restore && emscripten_browser_cursor_internal::is_set()) {
cursor_to_restore = emscripten_browser_cursor_internal::get_string(); // back up the existing cursor when entering the imgui capture space
}
switch(ImGui::GetMouseCursor()) {
case ImGuiMouseCursor_Arrow:
set_cursor_if_necessary(emscripten_browser_cursor_internal::cursor::cursor_default);
break;
case ImGuiMouseCursor_TextInput: // When hovering over InputText, etc.
set_cursor_if_necessary(emscripten_browser_cursor_internal::cursor::text);
break;
case ImGuiMouseCursor_ResizeAll: // (Unused by Dear ImGui functions)
set_cursor_if_necessary(emscripten_browser_cursor_internal::cursor::move);
break;
case ImGuiMouseCursor_ResizeNS: // When hovering over a horizontal border
set_cursor_if_necessary(emscripten_browser_cursor_internal::cursor::ns_resize);
break;
case ImGuiMouseCursor_ResizeEW: // When hovering over a vertical border or a column
set_cursor_if_necessary(emscripten_browser_cursor_internal::cursor::ew_resize);
break;
case ImGuiMouseCursor_ResizeNESW: // When hovering over the bottom-left corner of a window
set_cursor_if_necessary(emscripten_browser_cursor_internal::cursor::nesw_resize);
break;
case ImGuiMouseCursor_ResizeNWSE: // When hovering over the bottom-right corner of a window
set_cursor_if_necessary(emscripten_browser_cursor_internal::cursor::nwse_resize);
break;
case ImGuiMouseCursor_Hand: // (Unused by Dear ImGui functions. Use for e.g. hyperlinks)
set_cursor_if_necessary(emscripten_browser_cursor_internal::cursor::pointer);
break;
case ImGuiMouseCursor_NotAllowed: // When hovering something with disallowed interaction. Usually a crossed circle.
set_cursor_if_necessary(emscripten_browser_cursor_internal::cursor::not_allowed);
break;
}
} else { // mouse is away from the gui, hovering over some other part of the viewport
if(cursor_to_restore) {
emscripten_browser_cursor_internal::set(*cursor_to_restore); // restore the previous cursor when leaving the imgui capture space
cursor_to_restore = std::nullopt;
current_cursor = emscripten_browser_cursor_internal::cursor::invalid; // select an unused value for current cursor to force a set next time set_cursor_if_necessary is called
}
}
}
} // anonymous namespace
void ImGui_ImplEmscripten_Init() {
/// Initialise the Emscripten backend, setting input callbacks
auto &imgui_io{ImGui::GetIO()};
imgui_io.BackendPlatformName = "imgui_impl_emscripten";
imgui_io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;
// set up initial display size values
imgui_io.DisplaySize.x = emscripten::val::global("window")["innerWidth"].as<float>();
imgui_io.DisplaySize.y = emscripten::val::global("window")["innerHeight"].as<float>();
{
auto const device_pixel_ratio{emscripten::val::global("window")["devicePixelRatio"].as<float>()};
imgui_io.DisplayFramebufferScale.x = device_pixel_ratio;
imgui_io.DisplayFramebufferScale.y = device_pixel_ratio;
}
emscripten_set_mousemove_callback(
EMSCRIPTEN_EVENT_TARGET_WINDOW, // target
nullptr, // userData
false, // useCapture
[](int /*event_type*/, EmscriptenMouseEvent const *mouse_event, void */*data*/){ // callback, event_type == EMSCRIPTEN_EVENT_MOUSEMOVE
ImGui::GetIO().AddMousePosEvent(
static_cast<float>(mouse_event->clientX),
static_cast<float>(mouse_event->clientY)
);
return true; // the event was consumed
}
);
emscripten_set_mousedown_callback(
EMSCRIPTEN_EVENT_TARGET_WINDOW, // target
nullptr, // userData
false, // useCapture
[](int /*event_type*/, EmscriptenMouseEvent const *mouse_event, void */*data*/){ // callback, event_type == EMSCRIPTEN_EVENT_MOUSEDOWN
ImGui::GetIO().AddMouseButtonEvent(translate_mousebutton(mouse_event->button), true); // translated button, down
return true; // the event was consumed
}
);
emscripten_set_mouseup_callback(
EMSCRIPTEN_EVENT_TARGET_WINDOW, // target
nullptr, // userData
false, // useCapture
[](int /*event_type*/, EmscriptenMouseEvent const *mouse_event, void */*data*/){ // callback, event_type == EMSCRIPTEN_EVENT_MOUSEUP
ImGui::GetIO().AddMouseButtonEvent(translate_mousebutton(mouse_event->button), false); // translated button, up
return true; // the event was consumed
}
);
emscripten_set_mouseenter_callback(
EMSCRIPTEN_EVENT_TARGET_DOCUMENT, // target - WINDOW doesn't produce mouseenter events
nullptr, // userData
false, // useCapture
[](int /*event_type*/, EmscriptenMouseEvent const *mouse_event, void */*data*/){ // callback, event_type == EMSCRIPTEN_EVENT_MOUSEENTER
ImGui::GetIO().AddMousePosEvent(
static_cast<float>(mouse_event->clientX),
static_cast<float>(mouse_event->clientY)
);
return true; // the event was consumed
}
);
emscripten_set_mouseleave_callback(
EMSCRIPTEN_EVENT_TARGET_DOCUMENT, // target - WINDOW doesn't produce mouseenter events
nullptr, // userData
false, // useCapture
[](int /*event_type*/, EmscriptenMouseEvent const */*mouse_event*/, void */*data*/){ // callback, event_type == EMSCRIPTEN_EVENT_MOUSELEAVE
auto &imgui_io{ImGui::GetIO()};
imgui_io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); // cursor is not in the window
imgui_io.ClearInputKeys(); // clear pending input keys on mouse exit
return true; // the event was consumed
}
);
emscripten_set_wheel_callback(
EMSCRIPTEN_EVENT_TARGET_WINDOW, // target
nullptr, // userData
false, // useCapture
[](int /*event_type*/, EmscriptenWheelEvent const *wheel_event, void */*data*/){ // callback, event_type == EMSCRIPTEN_EVENT_WHEEL
float scale{1.0f};
switch(wheel_event->deltaMode) {
case DOM_DELTA_PIXEL: // scrolling in pixels
scale = 1.0f / 100.0f;
break;
case DOM_DELTA_LINE: // scrolling by lines
scale = 1.0f / 3.0f;
break;
case DOM_DELTA_PAGE: // scrolling by pages
scale = 80.0f;
break;
}
// TODO: make scrolling speeds configurable
ImGui::GetIO().AddMouseWheelEvent(
-static_cast<float>(wheel_event->deltaX) * scale,
-static_cast<float>(wheel_event->deltaY) * scale
);
return false; // the event was not consumed
}
);
emscripten_set_keydown_callback(
EMSCRIPTEN_EVENT_TARGET_WINDOW, // target
nullptr, // userData
false, // useCapture
[](int /*event_type*/, EmscriptenKeyboardEvent const *key_event, void */*data*/){ // callback, event_type == EMSCRIPTEN_EVENT_KEYDOWN
ImGui::GetIO().AddKeyEvent(translate_key(key_event->code), true);
return false; // the event was not consumed
}
);
emscripten_set_keyup_callback(
EMSCRIPTEN_EVENT_TARGET_WINDOW, // target
nullptr, // userData
false, // useCapture
[](int /*event_type*/, EmscriptenKeyboardEvent const *key_event, void */*data*/){ // callback, event_type == EMSCRIPTEN_EVENT_KEYUP
ImGui::GetIO().AddKeyEvent(translate_key(key_event->code), false);
return false; // the event was not consumed
}
);
emscripten_set_keypress_callback(
EMSCRIPTEN_EVENT_TARGET_WINDOW, // target
nullptr, // userData
false, // useCapture
[](int /*event_type*/, EmscriptenKeyboardEvent const *key_event, void */*data*/){ // callback, event_type == EMSCRIPTEN_EVENT_KEYPRESS
auto &imgui_io{ImGui::GetIO()};
ImGui::GetIO().AddInputCharactersUTF8(key_event->key);
return true; // the event was consumed
}
);
emscripten_set_resize_callback(
EMSCRIPTEN_EVENT_TARGET_WINDOW, // target
nullptr, // userData
false, // useCapture
[](int /*event_type*/, EmscriptenUiEvent const *event, void */*data*/) { // event_type == EMSCRIPTEN_EVENT_RESIZE
auto &imgui_io{ImGui::GetIO()};
imgui_io.DisplaySize.x = static_cast<float>(event->windowInnerWidth);
imgui_io.DisplaySize.y = static_cast<float>(event->windowInnerHeight);
return true; // the event was consumed
}
);
emscripten_set_focusin_callback(
EMSCRIPTEN_EVENT_TARGET_WINDOW, // target
nullptr, // userData
false, // useCapture
[](int /*event_type*/, EmscriptenFocusEvent const */*event*/, void */*data*/) { // event_type == EMSCRIPTEN_EVENT_FOCUSIN
auto &imgui_io{ImGui::GetIO()};
imgui_io.AddFocusEvent(true);
imgui_io.ClearInputKeys(); // clear pending input keys on focus gain
return true; // the event was consumed
}
);
emscripten_set_focusout_callback(
EMSCRIPTEN_EVENT_TARGET_WINDOW, // target
nullptr, // userData
false, // useCapture
[](int /*event_type*/, EmscriptenFocusEvent const */*event*/, void */*data*/) { // event_type == EMSCRIPTEN_EVENT_FOCUSOUT
auto &imgui_io{ImGui::GetIO()};
imgui_io.AddFocusEvent(false);
imgui_io.ClearInputKeys(); // clear pending input keys on focus loss - for example if you press tab to cycle to another part of the UI
return true; // the event was consumed
}
);
// TODO: touch events
}
void ImGui_ImplEmscripten_Shutdown() {
/// Unset any callbacks set by Init
emscripten_set_mousemove_callback( EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, false, nullptr);
emscripten_set_mousedown_callback( EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, false, nullptr);
emscripten_set_mouseup_callback( EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, false, nullptr);
emscripten_set_mouseenter_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, false, nullptr);
emscripten_set_mouseleave_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, false, nullptr);
emscripten_set_wheel_callback( EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, false, nullptr);
emscripten_set_keydown_callback( EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, false, nullptr);
emscripten_set_keyup_callback( EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, false, nullptr);
emscripten_set_keypress_callback( EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, false, nullptr);
emscripten_set_resize_callback( EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, false, nullptr);
emscripten_set_focusin_callback( EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, false, nullptr);
emscripten_set_focusout_callback( EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, false, nullptr);
// TODO: touch events
auto &imgui_io{ImGui::GetIO()};
imgui_io.BackendPlatformName = nullptr;
imgui_io.BackendPlatformUserData = nullptr;
imgui_io.BackendFlags &= ~ImGuiBackendFlags_HasMouseCursors;
}
void ImGui_ImplEmscripten_NewFrame() {
/// Update any state that needs to be polled
update_cursor();
}
#endif // IMGUI_DISABLE

View File

@ -0,0 +1,52 @@
// dear imgui: Platform Backend for Emscripten HTML5
//
// This is a platform back-end, similar to and offering an alternative to imgui_impl_glfw.
// The intended use-case is for applications built with Emscripten, running in the browser, but *not* using GLFW.
// It uses Emscripten's HTML5 interface to tie callbacks to imgui input, handling window resizing,
// focus, cursor, keyboard input, touch, gamepad devices etc. It does not attempt to handle rendering.
//
// A note about GLFW on Emscripten: Emscripten includes its own GLFW implementation, which wraps browser HTML5 callbacks to provide the standard GLFW input interface. So there are two levels of indirection.
// This backend removes the middleman for input, providing a more efficient direct interface between Emscripten's functionality and imgui input.
//
// This is a useful accompaniment for WebGPU rendering (i.e. with imgui_impl_wgpu), where GLFW is not needed for rendering.
// In that case, this backend replaces all non-rendering-related functionality from GLFW, making it possible to avoid depending on GLFW altogether.
//
// For native cursor rendering, this includes a cut-down implementation of the Emscripten Browser Cursor library: https://github.com/Armchair-Software/emscripten-browser-cursor
//
// Copyright 2024 Eugene Hopkinson
// Supported features:
// - Keyboard input
// - Window resizing
// - Cursor position
// - Cursor enters and leaves the window
// - Application focus
// - Browser cursors
// TODO:
// - Touch events
// A note on gamepad input: This back-end does not attempt to handle gamepad events, for the simple
// reason that any time you intend to provide gamepad input to imgui, you will inevitably want to
// also use gamepad input in your own game logic, so duplicating this processing can add a lot of
// inefficiency. For an example of how to handle Emscripten HTML5 gamepad events efficiently, and
// pass the relevant events to imgui, see https://github.com/Armchair-Software/webgpu-demo2
// Don't forget to set io.BackendFlags |= ImGuiBackendFlags_HasGamepad when a gamepad is connected.
#pragma once
#ifndef __EMSCRIPTEN__
#error The imgui_impl_emscripten backend reqiuires Emscripten.
#endif
/// Initialise the Emscripten backend, setting input callbacks. This should be called after ImGui::CreateContext();
void ImGui_ImplEmscripten_Init();
/// Shut down the Emscripten backend. This unsets all Emscripten input callbacks set by Init.
/// Note it'll also unset any Emscripten input callbacks set elsewhere in the program!
/// Note also there is no obligation to ever call this, as there is not necessarily any such concept as "shutting down" when running in the browser, and we have no resources to release. The user can just close the tab.
void ImGui_ImplEmscripten_Shutdown();
/// Call every frame to update polled input events, i.e. gamepads, and update imgui's cursors.
/// If you aren't using gamepad input to control imgui, and you're not using browser native cursor rendering (i.e. if imgui is rendering cursors internally), you don't need to call this.
void ImGui_ImplEmscripten_NewFrame();