mirror of
https://github.com/ocornut/imgui.git
synced 2026-09-26 16:05:45 +03:00
Merge aaa8b8f4cb into aa0181478b
This commit is contained in:
commit
fd1776e8f4
13 changed files with 1757 additions and 5 deletions
14
.github/workflows/build.yml
vendored
14
.github/workflows/build.yml
vendored
|
|
@ -640,6 +640,20 @@ jobs:
|
|||
popd
|
||||
make -C examples/example_sdl2_opengl3 -f Makefile.emscripten
|
||||
|
||||
- name: Build example_emscripten_opengl3
|
||||
run: |
|
||||
pushd emsdk-master
|
||||
source ./emsdk_env.sh
|
||||
popd
|
||||
make -C examples/example_emscripten_opengl3 -f Makefile.emscripten
|
||||
|
||||
- name: Build example_emscripten_wgpu
|
||||
run: |
|
||||
pushd emsdk-master
|
||||
source ./emsdk_env.sh
|
||||
popd
|
||||
make -C examples/example_emscripten_wgpu -f Makefile.emscripten
|
||||
|
||||
# This build compiles example_glfw_wgpu using Makefile.emscripten and Emscripten GLFW built-in implementation (-sUSE_GLFW=3)
|
||||
# This ensures 2 things: the make build works, and the GLFW built-in implementation is tested
|
||||
- name: Build example_glfw_wgpu with Emscripten/Makefile
|
||||
|
|
|
|||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -47,6 +47,8 @@ examples/*/*.dSYM
|
|||
examples/*.o.tmp
|
||||
examples/*.out.js
|
||||
examples/*.out.wasm
|
||||
examples/example_emscripten_opengl3/web/*
|
||||
examples/example_emscripten_wgpu/web/*
|
||||
examples/example_glfw_opengl3/web/*
|
||||
examples/example_glfw_wgpu/web/*
|
||||
examples/example_glfw_wgpu/external/*
|
||||
|
|
|
|||
857
backends/imgui_impl_emscripten.cpp
Normal file
857
backends/imgui_impl_emscripten.cpp
Normal file
|
|
@ -0,0 +1,857 @@
|
|||
// dear imgui: Platform Backend for Emscripten HTML5
|
||||
//
|
||||
// See documentation in imgui_impl_emscripten.h.
|
||||
//
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2026-09-06: DPI: Added public ImGui_ImplEmscripten_UpdateDisplayProperties() wrapper.
|
||||
// 2026-09-06: Inputs: Exposed mouse button and key translation functions as public.
|
||||
// 2026-09-06: DPI: Added ImGui_ImplEmscripten_GetCssToImGuiScale() getter.
|
||||
// 2026-09-05: DPI: Use the canvas framebuffer dimensions for display sizing and exact per-axis mouse coordinate scaling.
|
||||
// 2026-09-04: Replaced TargetDevicePixelRatio global with an Init() parameter and runtime setter.
|
||||
// 2026-09-03: Updated coding style and simplified key translation table setup.
|
||||
// 2026-04-02: Inputs: Replaced custom KeyboardEvent.code parser with ImHashStr()/ImGuiStorage lookup to match Dear ImGui backend style.
|
||||
// 2026-03-31: Added configurable TargetDevicePixelRatio to control how browser device pixels map to Dear ImGui pixels.
|
||||
// 2026-03-31: Inputs: Added BrowserBack/Forward and F13-F24 key mappings.
|
||||
// 2026-03-31: Moved cursor state into backend userdata and replaced cursor restore storage with owned C strings.
|
||||
// 2024-12-09: Inputs: Added special handling for modifier keys to also generate modifier key events.
|
||||
// 2024-12-08: Inputs: Prevent "Delete" key from getting printed in text input.
|
||||
// 2024-12-06: Inputs: Added special handling for Tab and Enter event capture.
|
||||
// 2024-12-06: Inputs: Handle blur and focus events correctly, focusin and focusout aren't enough.
|
||||
// 2024-12-06: Don't rely on devicePixelRatio for WebGPU framebuffer sizing; CSS->ImGui scaling may still use it.
|
||||
// 2024-11-22: Initial version by Eugene Hopkinson. (#8178)
|
||||
|
||||
#include "imgui.h"
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
#include "imgui_impl_emscripten.h"
|
||||
#include <emscripten.h>
|
||||
#include <emscripten/html5.h>
|
||||
|
||||
extern ImGuiID ImHashStr(char const* data, size_t data_size = 0, ImGuiID seed = 0); // Declared in imgui_internal.h.
|
||||
|
||||
// Browser cursor helpers, adapted from https://github.com/Armchair-Software/emscripten-browser-cursor
|
||||
|
||||
enum ImGui_ImplEmscripten_Cursor
|
||||
{
|
||||
// General
|
||||
ImGui_ImplEmscripten_Cursor_Auto, // The UA will determine the cursor to display based on the current context. E.g., equivalent to text when hovering text.
|
||||
ImGui_ImplEmscripten_Cursor_Default, // The platform-dependent default cursor. Typically an arrow.
|
||||
ImGui_ImplEmscripten_Cursor_None, // No cursor is rendered.
|
||||
|
||||
// Links & status
|
||||
ImGui_ImplEmscripten_Cursor_ContextMenu, // cursor slightly obscuring a menu icon - A context menu is available.
|
||||
ImGui_ImplEmscripten_Cursor_Help, // cursor next to a question mark - Help information is available.
|
||||
ImGui_ImplEmscripten_Cursor_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.
|
||||
ImGui_ImplEmscripten_Cursor_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).
|
||||
ImGui_ImplEmscripten_Cursor_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
|
||||
ImGui_ImplEmscripten_Cursor_Cell, // plus symbol - The table cell or set of cells can be selected.
|
||||
ImGui_ImplEmscripten_Cursor_Crosshair, // crosshair - Cross cursor, often used to indicate selection in a bitmap.
|
||||
ImGui_ImplEmscripten_Cursor_Text, // vertical i-beam - The text can be selected. Typically the shape of an I-beam.
|
||||
ImGui_ImplEmscripten_Cursor_VerticalText, // horizontal i-beam - The vertical text can be selected. Typically the shape of a sideways I-beam.
|
||||
|
||||
// Drag & drop
|
||||
ImGui_ImplEmscripten_Cursor_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.
|
||||
ImGui_ImplEmscripten_Cursor_Copy, // cursor next to a smaller folder icon with a plus sign - Something is to be copied.
|
||||
ImGui_ImplEmscripten_Cursor_Move, // plus sign made of two thin lines, with small arrows facing out - Something is to be moved.
|
||||
ImGui_ImplEmscripten_Cursor_NoDrop, // cursor next to circle with a line through it - An item may not be dropped at the current location.
|
||||
ImGui_ImplEmscripten_Cursor_NotAllowed, // circle with a line through it - The requested action will not be carried out.
|
||||
ImGui_ImplEmscripten_Cursor_Grab, // fully opened hand - Something can be grabbed (dragged to be moved).
|
||||
ImGui_ImplEmscripten_Cursor_Grabbing, // closed hand - Something is being grabbed (dragged to be moved).
|
||||
|
||||
// Resizing & scrolling
|
||||
ImGui_ImplEmscripten_Cursor_AllScroll, // dot with four triangles around it - Something can be scrolled in any direction (panned).
|
||||
ImGui_ImplEmscripten_Cursor_ColResize, // The item/column can be resized horizontally. Often rendered as arrows pointing left and right with a vertical bar separating them.
|
||||
ImGui_ImplEmscripten_Cursor_RowResize, // The item/row can be resized vertically. Often rendered as arrows pointing up and down with a horizontal bar separating them.
|
||||
ImGui_ImplEmscripten_Cursor_NResize, // 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.
|
||||
ImGui_ImplEmscripten_Cursor_EResize, // arrow pointing right
|
||||
ImGui_ImplEmscripten_Cursor_SResize, // arrow pointing down
|
||||
ImGui_ImplEmscripten_Cursor_WResize, // arrow pointing left
|
||||
ImGui_ImplEmscripten_Cursor_NEResize, // arrow pointing top-right
|
||||
ImGui_ImplEmscripten_Cursor_NWResize, // arrow pointing top-left
|
||||
ImGui_ImplEmscripten_Cursor_SEResize, // arrow pointing bottom-right
|
||||
ImGui_ImplEmscripten_Cursor_SWResize, // arrow pointing bottom-left
|
||||
ImGui_ImplEmscripten_Cursor_EWResize, // arrow pointing left and right - Bidirectional resize cursor.
|
||||
ImGui_ImplEmscripten_Cursor_NSResize, // arrow pointing up and down
|
||||
ImGui_ImplEmscripten_Cursor_NESWResize, // arrow pointing both to the top-right and bottom-left
|
||||
ImGui_ImplEmscripten_Cursor_NWSEResize, // arrow pointing both to the top-left and bottom-right
|
||||
|
||||
// Zooming
|
||||
ImGui_ImplEmscripten_Cursor_ZoomIn, // magnifying glass with a plus sign - Something can be zoomed (magnified) in or out.
|
||||
ImGui_ImplEmscripten_Cursor_ZoomOut,
|
||||
|
||||
// Special invalid value
|
||||
ImGui_ImplEmscripten_Cursor_Invalid
|
||||
};
|
||||
|
||||
static void ImGui_ImplEmscripten_SetBrowserCursor(ImGui_ImplEmscripten_Cursor new_cursor); // set a new cursor from a cursor enum
|
||||
static char* ImGui_ImplEmscripten_GetBrowserCursor(); // read the current cursor setting as an owned string, caller must free()
|
||||
static void ImGui_ImplEmscripten_SetBrowserCursor(char const* new_cursor); // set the cursor from an arbitrary string
|
||||
|
||||
struct ImGui_ImplEmscripten_Data
|
||||
{
|
||||
float TargetDevicePixelRatio = 1.0f;
|
||||
ImVec2 CssToImGuiScale = ImVec2(1.0f, 1.0f);
|
||||
ImVec2 CanvasFramebufferSize = ImVec2(0.0f, 0.0f);
|
||||
ImGui_ImplEmscripten_Cursor CurrentCursor = ImGui_ImplEmscripten_Cursor_Invalid;
|
||||
char* CursorToRestore = nullptr;
|
||||
bool LastMouseDrawCursor = false;
|
||||
bool LastNoMouseCursorChange = false;
|
||||
};
|
||||
|
||||
static ImVec2 ImGui_ImplEmscripten_GetCanvasFramebufferSize()
|
||||
{
|
||||
return ImVec2(
|
||||
(float)EM_ASM_INT({ return Module["canvas"] ? Module["canvas"].width : 0; }),
|
||||
(float)EM_ASM_INT({ return Module["canvas"] ? Module["canvas"].height : 0; }));
|
||||
}
|
||||
|
||||
static void ImGui_ImplEmscripten_UpdateDisplayProperties(ImGuiIO& io, ImGui_ImplEmscripten_Data* bd)
|
||||
{
|
||||
double const css_width = EM_ASM_DOUBLE({ return Module["canvas"] ? Module["canvas"].getBoundingClientRect().width : window.innerWidth; });
|
||||
double const css_height = EM_ASM_DOUBLE({ return Module["canvas"] ? Module["canvas"].getBoundingClientRect().height : window.innerHeight; });
|
||||
float const target_device_pixel_ratio = bd->TargetDevicePixelRatio;
|
||||
ImVec2 framebuffer_size = ImGui_ImplEmscripten_GetCanvasFramebufferSize();
|
||||
if (framebuffer_size.x <= 0.0f || framebuffer_size.y <= 0.0f)
|
||||
{
|
||||
double const device_pixel_ratio = emscripten_get_device_pixel_ratio();
|
||||
framebuffer_size = ImVec2((float)(int)(css_width * device_pixel_ratio + 0.5), (float)(int)(css_height * device_pixel_ratio + 0.5));
|
||||
}
|
||||
bd->CanvasFramebufferSize = framebuffer_size;
|
||||
io.DisplaySize.x = framebuffer_size.x / target_device_pixel_ratio;
|
||||
io.DisplaySize.y = framebuffer_size.y / target_device_pixel_ratio;
|
||||
io.DisplayFramebufferScale = ImVec2(target_device_pixel_ratio, target_device_pixel_ratio);
|
||||
bd->CssToImGuiScale.x = css_width > 0.0 ? (float)((double)io.DisplaySize.x / css_width) : 1.0f;
|
||||
bd->CssToImGuiScale.y = css_height > 0.0 ? (float)((double)io.DisplaySize.y / css_height) : 1.0f;
|
||||
}
|
||||
|
||||
// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
|
||||
static ImGui_ImplEmscripten_Data* ImGui_ImplEmscripten_GetBackendData()
|
||||
{
|
||||
return ImGui::GetCurrentContext() ? (ImGui_ImplEmscripten_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr;
|
||||
}
|
||||
|
||||
bool ImGui_ImplEmscripten_Init(float target_device_pixel_ratio)
|
||||
{
|
||||
// Initialise the Emscripten backend, setting input callbacks
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IMGUI_CHECKVERSION();
|
||||
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
|
||||
IM_ASSERT(target_device_pixel_ratio > 0.0f && "Target device pixel ratio must be positive.");
|
||||
if (target_device_pixel_ratio <= 0.0f) return false;
|
||||
ImGui_ImplEmscripten_Data* bd = IM_NEW(ImGui_ImplEmscripten_Data)();
|
||||
bd->TargetDevicePixelRatio = target_device_pixel_ratio;
|
||||
io.BackendPlatformUserData = (void*)bd;
|
||||
io.BackendPlatformName = "imgui_impl_emscripten";
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;
|
||||
|
||||
// Set up initial display size values
|
||||
ImGui_ImplEmscripten_UpdateDisplayProperties(io, bd);
|
||||
|
||||
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_ImplEmscripten_Data* bd = ImGui_ImplEmscripten_GetBackendData();
|
||||
ImVec2 const css_to_imgui_scale = bd ? bd->CssToImGuiScale : ImVec2(1.0f, 1.0f);
|
||||
ImGui::GetIO().AddMousePosEvent(
|
||||
(float)mouse_event->clientX * css_to_imgui_scale.x,
|
||||
(float)mouse_event->clientY * css_to_imgui_scale.y
|
||||
);
|
||||
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(ImGui_ImplEmscripten_TranslateMouseButton(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(ImGui_ImplEmscripten_TranslateMouseButton(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_ImplEmscripten_Data* bd = ImGui_ImplEmscripten_GetBackendData();
|
||||
ImVec2 const css_to_imgui_scale = bd ? bd->CssToImGuiScale : ImVec2(1.0f, 1.0f);
|
||||
ImGui::GetIO().AddMousePosEvent(
|
||||
(float)mouse_event->clientX * css_to_imgui_scale.x,
|
||||
(float)mouse_event->clientY * css_to_imgui_scale.y
|
||||
);
|
||||
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
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); // cursor is not in the window
|
||||
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
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.AddMouseWheelEvent(
|
||||
-(float)wheel_event->deltaX * scale,
|
||||
-(float)wheel_event->deltaY * scale
|
||||
);
|
||||
return io.WantCaptureMouse; // consume the event when imgui wants to capture mouse input
|
||||
}
|
||||
);
|
||||
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
|
||||
{
|
||||
const ImGuiKey key = ImGui_ImplEmscripten_TranslateKey(key_event->code);
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.AddKeyEvent(key, true);
|
||||
switch (key) // special cases for certain key events
|
||||
{
|
||||
case ImGuiKey_LeftCtrl: // additional events for modifier keys
|
||||
case ImGuiKey_RightCtrl:
|
||||
io.AddKeyEvent(ImGuiMod_Ctrl, true);
|
||||
break;
|
||||
case ImGuiKey_LeftShift:
|
||||
case ImGuiKey_RightShift:
|
||||
io.AddKeyEvent(ImGuiMod_Shift, true);
|
||||
break;
|
||||
case ImGuiKey_LeftAlt:
|
||||
case ImGuiKey_RightAlt:
|
||||
io.AddKeyEvent(ImGuiMod_Alt, true);
|
||||
break;
|
||||
case ImGuiKey_LeftSuper:
|
||||
case ImGuiKey_RightSuper:
|
||||
io.AddKeyEvent(ImGuiMod_Super, true);
|
||||
break;
|
||||
// TODO: case ImGuiKey_Menu: Do we want to do anything with this?
|
||||
case ImGuiKey_Tab: // consuming tab prevents the user tabbing to other parts of the browser interface outside the window content
|
||||
return io.WantCaptureKeyboard; // the event was consumed only if imgui wants to capture the keyboard
|
||||
case ImGuiKey_Enter: // consuming enter prevents the word "Enter" appearing in text input via the keypress callback
|
||||
case ImGuiKey_Delete: // consuming enter prevents the word "Delete" appearing in text input via the keypress callback
|
||||
return io.WantTextInput; // the event was consumed only if we're currently accepting text input
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false; // if no special handling, 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
|
||||
{
|
||||
const ImGuiKey key = ImGui_ImplEmscripten_TranslateKey(key_event->code);
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.AddKeyEvent(key, false);
|
||||
switch (key) // special cases for certain key events
|
||||
{
|
||||
case ImGuiKey_LeftCtrl: // additional events for modifier keys
|
||||
case ImGuiKey_RightCtrl:
|
||||
io.AddKeyEvent(ImGuiMod_Ctrl, false);
|
||||
break;
|
||||
case ImGuiKey_LeftShift:
|
||||
case ImGuiKey_RightShift:
|
||||
io.AddKeyEvent(ImGuiMod_Shift, false);
|
||||
break;
|
||||
case ImGuiKey_LeftAlt:
|
||||
case ImGuiKey_RightAlt:
|
||||
io.AddKeyEvent(ImGuiMod_Alt, false);
|
||||
break;
|
||||
case ImGuiKey_LeftSuper:
|
||||
case ImGuiKey_RightSuper:
|
||||
io.AddKeyEvent(ImGuiMod_Super, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
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
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.AddInputCharactersUTF8(key_event->key);
|
||||
return io.WantCaptureKeyboard; // the event was consumed only if imgui wants to capture the keyboard
|
||||
}
|
||||
);
|
||||
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
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGui_ImplEmscripten_Data* bd = ImGui_ImplEmscripten_GetBackendData();
|
||||
if (bd != nullptr) ImGui_ImplEmscripten_UpdateDisplayProperties(io, bd);
|
||||
return true; // the event was consumed
|
||||
}
|
||||
);
|
||||
emscripten_set_blur_callback(
|
||||
EMSCRIPTEN_EVENT_TARGET_WINDOW, // target
|
||||
nullptr, // userData
|
||||
false, // useCapture
|
||||
[](int /*event_type*/, EmscriptenFocusEvent const* /*event*/, void* /*data*/) // event_type == EMSCRIPTEN_EVENT_BLUR
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.AddFocusEvent(false);
|
||||
io.ClearInputKeys(); // clear pending input keys on focus loss
|
||||
return true; // the event was consumed
|
||||
}
|
||||
);
|
||||
emscripten_set_focus_callback(
|
||||
EMSCRIPTEN_EVENT_TARGET_WINDOW, // target
|
||||
nullptr, // userData
|
||||
false, // useCapture
|
||||
[](int /*event_type*/, EmscriptenFocusEvent const* /*event*/, void* /*data*/) // event_type == EMSCRIPTEN_EVENT_FOCUS
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.AddFocusEvent(true);
|
||||
io.ClearInputKeys(); // clear pending input keys on focus gain - for example if you press tab to cycle back into the browser window
|
||||
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
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.AddFocusEvent(true);
|
||||
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
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.AddFocusEvent(false);
|
||||
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
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplEmscripten_SetTargetDevicePixelRatio(float target_device_pixel_ratio)
|
||||
{
|
||||
ImGui_ImplEmscripten_Data* bd = ImGui_ImplEmscripten_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "Context or backend not initialized? Did you call ImGui_ImplEmscripten_Init()?");
|
||||
IM_ASSERT(target_device_pixel_ratio > 0.0f && "Target device pixel ratio must be positive.");
|
||||
if (bd == nullptr || target_device_pixel_ratio <= 0.0f || target_device_pixel_ratio == bd->TargetDevicePixelRatio) return;
|
||||
|
||||
bd->TargetDevicePixelRatio = target_device_pixel_ratio;
|
||||
ImGui_ImplEmscripten_UpdateDisplayProperties(ImGui::GetIO(), bd);
|
||||
}
|
||||
|
||||
ImVec2 ImGui_ImplEmscripten_GetCssToImGuiScale()
|
||||
{
|
||||
ImGui_ImplEmscripten_Data* bd = ImGui_ImplEmscripten_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "Context or backend not initialized? Did you call ImGui_ImplEmscripten_Init()?");
|
||||
return bd != nullptr ? bd->CssToImGuiScale : ImVec2(1.0f, 1.0f);
|
||||
}
|
||||
|
||||
void ImGui_ImplEmscripten_UpdateDisplayProperties()
|
||||
{
|
||||
ImGui_ImplEmscripten_Data* bd = ImGui_ImplEmscripten_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "Context or backend not initialized? Did you call ImGui_ImplEmscripten_Init()?");
|
||||
if (bd != nullptr) ImGui_ImplEmscripten_UpdateDisplayProperties(ImGui::GetIO(), bd);
|
||||
}
|
||||
|
||||
void ImGui_ImplEmscripten_Shutdown()
|
||||
{
|
||||
ImGui_ImplEmscripten_Data* bd = ImGui_ImplEmscripten_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already 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_blur_callback( EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, false, nullptr);
|
||||
emscripten_set_focus_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
|
||||
|
||||
if (bd->CursorToRestore != nullptr)
|
||||
{
|
||||
ImGui_ImplEmscripten_SetBrowserCursor(bd->CursorToRestore); // restore the previous cursor state if imgui still owns the cursor on shutdown
|
||||
free(bd->CursorToRestore);
|
||||
}
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.BackendPlatformName = nullptr;
|
||||
io.BackendPlatformUserData = nullptr;
|
||||
io.BackendFlags &= ~ImGuiBackendFlags_HasMouseCursors;
|
||||
IM_DELETE(bd);
|
||||
}
|
||||
|
||||
static void ImGui_ImplEmscripten_RestoreMouseCursor(ImGui_ImplEmscripten_Data* bd)
|
||||
{
|
||||
if (bd->CursorToRestore == nullptr) return;
|
||||
ImGui_ImplEmscripten_SetBrowserCursor(bd->CursorToRestore); // restore the previous cursor state when leaving imgui cursor ownership
|
||||
free(bd->CursorToRestore);
|
||||
bd->CursorToRestore = nullptr;
|
||||
bd->CurrentCursor = ImGui_ImplEmscripten_Cursor_Invalid; // select an unused value for current cursor to force a set next time
|
||||
}
|
||||
|
||||
static void ImGui_ImplEmscripten_SetMouseCursor(ImGui_ImplEmscripten_Data* bd, ImGui_ImplEmscripten_Cursor new_cursor)
|
||||
{
|
||||
if (new_cursor == bd->CurrentCursor) return; // don't do anything if the current cursor is already set
|
||||
if (bd->CursorToRestore == nullptr) bd->CursorToRestore = ImGui_ImplEmscripten_GetBrowserCursor(); // back up the existing cursor state when first taking cursor ownership
|
||||
bd->CurrentCursor = new_cursor;
|
||||
ImGui_ImplEmscripten_SetBrowserCursor(new_cursor);
|
||||
}
|
||||
|
||||
static void ImGui_ImplEmscripten_UpdateMouseCursor(ImGui_ImplEmscripten_Data* bd)
|
||||
{
|
||||
// Sync any cursor changes due to ImGui to the browser's cursor
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
|
||||
{
|
||||
if (!bd->LastNoMouseCursorChange)
|
||||
{
|
||||
free(bd->CursorToRestore);
|
||||
bd->CursorToRestore = nullptr;
|
||||
bd->CurrentCursor = ImGui_ImplEmscripten_Cursor_Invalid;
|
||||
bd->LastMouseDrawCursor = false;
|
||||
bd->LastNoMouseCursorChange = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
bd->LastNoMouseCursorChange = false;
|
||||
|
||||
if (io.MouseDrawCursor)
|
||||
{
|
||||
if (bd->LastMouseDrawCursor) return;
|
||||
|
||||
ImGui_ImplEmscripten_SetMouseCursor(bd, ImGui_ImplEmscripten_Cursor_None); // hide the cursor for the entire window if imgui is handling cursor drawing - not just when imgui wants to capture the mouse
|
||||
bd->LastMouseDrawCursor = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bd->LastMouseDrawCursor)
|
||||
{
|
||||
ImGui_ImplEmscripten_RestoreMouseCursor(bd);
|
||||
bd->LastMouseDrawCursor = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (io.WantCaptureMouse) // mouse is hovering over the gui
|
||||
{
|
||||
switch (ImGui::GetMouseCursor())
|
||||
{
|
||||
case ImGuiMouseCursor_None:
|
||||
ImGui_ImplEmscripten_SetMouseCursor(bd, ImGui_ImplEmscripten_Cursor_None);
|
||||
break;
|
||||
case ImGuiMouseCursor_Arrow:
|
||||
ImGui_ImplEmscripten_SetMouseCursor(bd, ImGui_ImplEmscripten_Cursor_Default);
|
||||
break;
|
||||
case ImGuiMouseCursor_TextInput: // When hovering over InputText, etc.
|
||||
ImGui_ImplEmscripten_SetMouseCursor(bd, ImGui_ImplEmscripten_Cursor_Text);
|
||||
break;
|
||||
case ImGuiMouseCursor_ResizeAll: // (Unused by Dear ImGui functions)
|
||||
ImGui_ImplEmscripten_SetMouseCursor(bd, ImGui_ImplEmscripten_Cursor_Move);
|
||||
break;
|
||||
case ImGuiMouseCursor_ResizeNS: // When hovering over a horizontal border
|
||||
ImGui_ImplEmscripten_SetMouseCursor(bd, ImGui_ImplEmscripten_Cursor_NSResize);
|
||||
break;
|
||||
case ImGuiMouseCursor_ResizeEW: // When hovering over a vertical border or a column
|
||||
ImGui_ImplEmscripten_SetMouseCursor(bd, ImGui_ImplEmscripten_Cursor_EWResize);
|
||||
break;
|
||||
case ImGuiMouseCursor_ResizeNESW: // When hovering over the bottom-left corner of a window
|
||||
ImGui_ImplEmscripten_SetMouseCursor(bd, ImGui_ImplEmscripten_Cursor_NESWResize);
|
||||
break;
|
||||
case ImGuiMouseCursor_ResizeNWSE: // When hovering over the bottom-right corner of a window
|
||||
ImGui_ImplEmscripten_SetMouseCursor(bd, ImGui_ImplEmscripten_Cursor_NWSEResize);
|
||||
break;
|
||||
case ImGuiMouseCursor_Hand: // (Unused by Dear ImGui functions. Use for e.g. hyperlinks)
|
||||
ImGui_ImplEmscripten_SetMouseCursor(bd, ImGui_ImplEmscripten_Cursor_Pointer);
|
||||
break;
|
||||
case ImGuiMouseCursor_NotAllowed: // When hovering something with disallowed interaction. Usually a crossed circle.
|
||||
ImGui_ImplEmscripten_SetMouseCursor(bd, ImGui_ImplEmscripten_Cursor_NotAllowed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else // mouse is away from the gui, hovering over some other part of the viewport
|
||||
{
|
||||
ImGui_ImplEmscripten_RestoreMouseCursor(bd);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGui_ImplEmscripten_NewFrame()
|
||||
{
|
||||
ImGui_ImplEmscripten_Data* bd = ImGui_ImplEmscripten_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "Context or backend not initialized? Did you call ImGui_ImplEmscripten_Init()?");
|
||||
|
||||
// Update any state that needs to be polled
|
||||
ImVec2 const canvas_framebuffer_size = ImGui_ImplEmscripten_GetCanvasFramebufferSize();
|
||||
if (canvas_framebuffer_size.x != bd->CanvasFramebufferSize.x || canvas_framebuffer_size.y != bd->CanvasFramebufferSize.y)
|
||||
ImGui_ImplEmscripten_UpdateDisplayProperties(ImGui::GetIO(), bd);
|
||||
ImGui_ImplEmscripten_UpdateMouseCursor(bd);
|
||||
}
|
||||
|
||||
static char* ImGui_ImplEmscripten_GetBrowserCursor()
|
||||
{
|
||||
// Return the current cursor setting as a newly-allocated string, caller must free it.
|
||||
return (char*)EM_ASM_PTR(
|
||||
return stringToNewUTF8(document.body.style.cursor);
|
||||
);
|
||||
}
|
||||
|
||||
static void ImGui_ImplEmscripten_SetBrowserCursor(ImGui_ImplEmscripten_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 ImGui_ImplEmscripten_Cursor_None:
|
||||
EM_ASM(document.body.style.cursor = 'none';);
|
||||
break;
|
||||
case ImGui_ImplEmscripten_Cursor_Default:
|
||||
default:
|
||||
EM_ASM(document.body.style.cursor = 'default';);
|
||||
break;
|
||||
case ImGui_ImplEmscripten_Cursor_Pointer:
|
||||
EM_ASM(document.body.style.cursor = 'pointer';);
|
||||
break;
|
||||
case ImGui_ImplEmscripten_Cursor_Text:
|
||||
EM_ASM(document.body.style.cursor = 'text';);
|
||||
break;
|
||||
case ImGui_ImplEmscripten_Cursor_Move:
|
||||
EM_ASM(document.body.style.cursor = 'move';);
|
||||
break;
|
||||
case ImGui_ImplEmscripten_Cursor_NotAllowed:
|
||||
EM_ASM(document.body.style.cursor = 'not-allowed';);
|
||||
break;
|
||||
case ImGui_ImplEmscripten_Cursor_EWResize:
|
||||
EM_ASM(document.body.style.cursor = 'ew-resize';);
|
||||
break;
|
||||
case ImGui_ImplEmscripten_Cursor_NSResize:
|
||||
EM_ASM(document.body.style.cursor = 'ns-resize';);
|
||||
break;
|
||||
case ImGui_ImplEmscripten_Cursor_NESWResize:
|
||||
EM_ASM(document.body.style.cursor = 'nesw-resize';);
|
||||
break;
|
||||
case ImGui_ImplEmscripten_Cursor_NWSEResize:
|
||||
EM_ASM(document.body.style.cursor = 'nwse-resize';);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ImGui_ImplEmscripten_SetBrowserCursor(char const* new_cursor)
|
||||
{
|
||||
// Set the cursor from an arbitrary string
|
||||
EM_ASM({
|
||||
document.body.style.cursor = UTF8ToString($0);
|
||||
}, new_cursor);
|
||||
}
|
||||
|
||||
ImGuiMouseButton ImGui_ImplEmscripten_TranslateMouseButton(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
|
||||
}
|
||||
|
||||
static ImGuiStorage const& ImGui_ImplEmscripten_GetKeyTranslationStorage()
|
||||
{
|
||||
struct ImGui_ImplEmscripten_KeyTranslation
|
||||
{
|
||||
const char* EmscriptenKey;
|
||||
ImGuiKey Key;
|
||||
};
|
||||
static const ImGui_ImplEmscripten_KeyTranslation key_translations[] =
|
||||
{
|
||||
// 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 },
|
||||
|
||||
// Browser key group
|
||||
{ "BrowserBack", ImGuiKey_AppBack }, // Pass through so the embedding app can decide
|
||||
//{ "BrowserFavorites", ImGuiKey_None }, // No direct mapping
|
||||
{ "BrowserForward", ImGuiKey_AppForward }, // Pass through so the embedding app can decide
|
||||
//{ "BrowserHome", ImGuiKey_None }, // No direct mapping
|
||||
//{ "BrowserRefresh", ImGuiKey_None }, // No direct mapping
|
||||
//{ "BrowserSearch", ImGuiKey_None }, // No direct mapping
|
||||
//{ "BrowserStop", ImGuiKey_None }, // No direct mapping
|
||||
|
||||
// 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_None }, // No defined Dear ImGui mapping
|
||||
//{ "NumpadClearEntry", ImGuiKey_None }, // No defined Dear ImGui mapping
|
||||
{ "NumpadComma", ImGuiKey_KeypadDecimal }, // Closest match
|
||||
{ "NumpadDecimal", ImGuiKey_KeypadDecimal },
|
||||
{ "NumpadDivide", ImGuiKey_KeypadDivide },
|
||||
{ "NumpadEnter", ImGuiKey_KeypadEnter },
|
||||
{ "NumpadEqual", ImGuiKey_KeypadEqual },
|
||||
{ "NumpadHash", ImGuiKey_Backslash }, // Mapped to generic backslash for telephone-style '#'
|
||||
//{ "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 group
|
||||
{ "Escape", ImGuiKey_Escape },
|
||||
{ "F1", ImGuiKey_F1 },
|
||||
{ "F2", ImGuiKey_F2 },
|
||||
{ "F3", ImGuiKey_F3 },
|
||||
{ "F4", ImGuiKey_F4 },
|
||||
{ "F5", ImGuiKey_F5 },
|
||||
{ "F6", ImGuiKey_F6 },
|
||||
{ "F7", ImGuiKey_F7 },
|
||||
{ "F8", ImGuiKey_F8 },
|
||||
{ "F9", ImGuiKey_F9 },
|
||||
{ "F10", ImGuiKey_F10 },
|
||||
{ "F11", ImGuiKey_F11 },
|
||||
{ "F12", ImGuiKey_F12 },
|
||||
{ "F13", ImGuiKey_F13 },
|
||||
{ "F14", ImGuiKey_F14 },
|
||||
{ "F15", ImGuiKey_F15 },
|
||||
{ "F16", ImGuiKey_F16 },
|
||||
{ "F17", ImGuiKey_F17 },
|
||||
{ "F18", ImGuiKey_F18 },
|
||||
{ "F19", ImGuiKey_F19 },
|
||||
{ "F20", ImGuiKey_F20 },
|
||||
{ "F21", ImGuiKey_F21 },
|
||||
{ "F22", ImGuiKey_F22 },
|
||||
{ "F23", ImGuiKey_F23 },
|
||||
{ "F24", ImGuiKey_F24 },
|
||||
//{ "Fn", ImGuiKey_None }, // No direct mapping
|
||||
//{ "FnLock", ImGuiKey_None }, // No direct mapping
|
||||
{ "PrintScreen", ImGuiKey_PrintScreen },
|
||||
{ "ScrollLock", ImGuiKey_ScrollLock },
|
||||
{ "Pause", ImGuiKey_Pause },
|
||||
|
||||
// Clipboard/editing keys without direct mapping
|
||||
//{ "Abort", ImGuiKey_None },
|
||||
//{ "Again", ImGuiKey_None },
|
||||
//{ "Convert", ImGuiKey_None },
|
||||
//{ "Copy", ImGuiKey_None },
|
||||
//{ "Cut", ImGuiKey_None },
|
||||
//{ "Find", ImGuiKey_None },
|
||||
//{ "Open", ImGuiKey_None },
|
||||
//{ "Paste", ImGuiKey_None },
|
||||
//{ "Props", ImGuiKey_None },
|
||||
//{ "Resume", ImGuiKey_None },
|
||||
//{ "Select", ImGuiKey_None },
|
||||
//{ "Undo", ImGuiKey_None },
|
||||
|
||||
// IME and international keys without direct mapping
|
||||
//{ "Hiragana", ImGuiKey_None },
|
||||
//{ "KanaMode", ImGuiKey_None },
|
||||
//{ "Katakana", ImGuiKey_None },
|
||||
//{ "Lang1", ImGuiKey_None },
|
||||
//{ "Lang2", ImGuiKey_None },
|
||||
//{ "NonConvert", ImGuiKey_None },
|
||||
|
||||
// Media and launcher keys without direct mapping
|
||||
//{ "AudioVolumeDown", ImGuiKey_None },
|
||||
//{ "AudioVolumeMute", ImGuiKey_None },
|
||||
//{ "AudioVolumeUp", ImGuiKey_None },
|
||||
//{ "LaunchApp1", ImGuiKey_None },
|
||||
//{ "LaunchApp2", ImGuiKey_None },
|
||||
//{ "LaunchMail", ImGuiKey_None },
|
||||
//{ "MediaPlayPause", ImGuiKey_None },
|
||||
//{ "MediaSelect", ImGuiKey_None },
|
||||
//{ "MediaStop", ImGuiKey_None },
|
||||
//{ "MediaTrackNext", ImGuiKey_None },
|
||||
//{ "MediaTrackPrevious", ImGuiKey_None },
|
||||
|
||||
// System keys without direct mapping
|
||||
//{ "Eject", ImGuiKey_None },
|
||||
//{ "Hyper", ImGuiKey_None },
|
||||
//{ "Power", ImGuiKey_None },
|
||||
//{ "Sleep", ImGuiKey_None },
|
||||
//{ "Super", ImGuiKey_None },
|
||||
//{ "Suspend", ImGuiKey_None },
|
||||
//{ "Turbo", ImGuiKey_None },
|
||||
//{ "Unidentified", ImGuiKey_None },
|
||||
//{ "WakeUp", ImGuiKey_None },
|
||||
};
|
||||
|
||||
static ImGuiStorage storage;
|
||||
static bool is_initialized = false;
|
||||
if (is_initialized) return storage;
|
||||
is_initialized = true;
|
||||
storage.Data.reserve(IM_ARRAYSIZE(key_translations));
|
||||
for (int n = 0; n != IM_ARRAYSIZE(key_translations); ++n)
|
||||
{
|
||||
storage.Data.push_back(ImGuiStoragePair(ImHashStr(key_translations[n].EmscriptenKey), key_translations[n].Key));
|
||||
}
|
||||
storage.BuildSortByKey();
|
||||
return storage;
|
||||
}
|
||||
|
||||
ImGuiKey ImGui_ImplEmscripten_TranslateKey(const char* emscripten_key)
|
||||
{
|
||||
// Translate a W3C KeyboardEvent.code string into an ImGuiKey.
|
||||
if (emscripten_key == nullptr || emscripten_key[0] == '\0') return ImGuiKey_None;
|
||||
|
||||
ImGuiStorage const& storage = ImGui_ImplEmscripten_GetKeyTranslationStorage();
|
||||
return (ImGuiKey)storage.GetInt(ImHashStr(emscripten_key), ImGuiKey_None);
|
||||
}
|
||||
|
||||
#endif // IMGUI_DISABLE
|
||||
70
backends/imgui_impl_emscripten.h
Normal file
70
backends/imgui_impl_emscripten.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// 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, 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
|
||||
|
||||
// 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
|
||||
#include "imgui.h"
|
||||
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#error The imgui_impl_emscripten backend requires Emscripten.
|
||||
#endif
|
||||
|
||||
// Initialise the Emscripten backend, setting input callbacks. This should be called after ImGui::CreateContext();
|
||||
// The target_device_pixel_ratio parameter controls how many device pixels Dear ImGui should target per Dear ImGui pixel. Default 1.0f gives 1:1 device-pixel rendering.
|
||||
IMGUI_IMPL_API bool ImGui_ImplEmscripten_Init(float target_device_pixel_ratio = 1.0f);
|
||||
|
||||
// Change the target device pixel ratio at runtime and update display properties immediately.
|
||||
IMGUI_IMPL_API void ImGui_ImplEmscripten_SetTargetDevicePixelRatio(float target_device_pixel_ratio);
|
||||
|
||||
// Return the scale used to convert browser CSS pixel coordinates to Dear ImGui coordinates. Valid after Init().
|
||||
IMGUI_IMPL_API ImVec2 ImGui_ImplEmscripten_GetCssToImGuiScale();
|
||||
|
||||
// Refresh display size and scaling properties immediately. Call after changing the canvas framebuffer or CSS size.
|
||||
IMGUI_IMPL_API void ImGui_ImplEmscripten_UpdateDisplayProperties();
|
||||
|
||||
// Input translation helpers.
|
||||
IMGUI_IMPL_API ImGuiMouseButton ImGui_ImplEmscripten_TranslateMouseButton(unsigned short emscripten_button);
|
||||
IMGUI_IMPL_API ImGuiKey ImGui_ImplEmscripten_TranslateKey(const char* emscripten_key);
|
||||
|
||||
// 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, unless you intend to reset the backend (i.e. with ImGui::DestroyContext()).
|
||||
// Otherwise, 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, so you don't need to worry about exiting cleanly.
|
||||
IMGUI_IMPL_API void ImGui_ImplEmscripten_Shutdown();
|
||||
|
||||
// Call every frame to synchronize Dear ImGui's cursor state with the browser's native cursors.
|
||||
// If you are not using browser native cursor rendering (i.e. if Dear ImGui is rendering cursors internally), you don't need to call this.
|
||||
IMGUI_IMPL_API void ImGui_ImplEmscripten_NewFrame();
|
||||
|
||||
#endif // IMGUI_DISABLE
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
// dear imgui: Renderer for WebGPU
|
||||
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL2, SDL3)
|
||||
// (Please note that WebGPU is a recent API, may not be supported by all browser, and its ecosystem is generally a mess)
|
||||
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL2, SDL3, Emscripten)
|
||||
// (Please note that WebGPU is a recent API, may not be supported by all browsers, and its ecosystem is generally a mess)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID/ImTextureRef!
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// dear imgui: Renderer for WebGPU
|
||||
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL2, SDL3)
|
||||
// (Please note that WebGPU is a recent API, may not be supported by all browser, and its ecosystem is generally a mess)
|
||||
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL2, SDL3, Emscripten)
|
||||
// (Please note that WebGPU is a recent API, may not be supported by all browsers, and its ecosystem is generally a mess)
|
||||
|
||||
// When targeting native platforms:
|
||||
// - One of IMGUI_IMPL_WEBGPU_BACKEND_DAWN, IMGUI_IMPL_WEBGPU_BACKEND_WGPU or IMGUI_IMPL_WEBGPU_BACKEND_WGVK *must* be provided.
|
||||
|
|
|
|||
|
|
@ -75,6 +75,14 @@ OSX + OpenGL2 example. <BR>
|
|||
OSX + OpenGL3 example. <BR>
|
||||
= main.mm + imgui_impl_osx.mm + imgui_impl_opengl3.cpp <BR>
|
||||
|
||||
[example_emscripten_opengl3/](https://github.com/ocornut/imgui/blob/master/examples/example_emscripten_opengl3/) <BR>
|
||||
Emscripten (browser) + OpenGL3/ES2/ES3 example. Uses imgui_impl_emscripten for platform input and imgui_impl_opengl3 for rendering. <BR>
|
||||
= main.cpp + imgui_impl_emscripten.cpp + imgui_impl_opengl3.cpp
|
||||
|
||||
[example_emscripten_wgpu/](https://github.com/ocornut/imgui/blob/master/examples/example_emscripten_wgpu/) <BR>
|
||||
Emscripten (browser) + WebGPU example. Uses imgui_impl_emscripten for platform input and imgui_impl_wgpu for rendering. <BR>
|
||||
= main.cpp + imgui_impl_emscripten.cpp + imgui_impl_wgpu.cpp
|
||||
|
||||
[example_glfw_wgpu/](https://github.com/ocornut/imgui/blob/master/examples/example_glfw_wgpu/) <BR>
|
||||
GLFW + WebGPU example. Supports Emscripten (web), Dawn (native), WGPU (native). <BR>
|
||||
= main.cpp + imgui_impl_glfw.cpp + imgui_impl_wgpu.cpp
|
||||
|
|
@ -271,4 +279,3 @@ when an interactive drag is in progress.
|
|||
Note that some setup configurations or GPU drivers may introduce additional display lag depending on their settings.
|
||||
If you notice that dragging windows is laggy and you are not sure what the cause is: try drawing a simple
|
||||
2D shape directly under the mouse cursor to help identify the issue!
|
||||
|
||||
|
|
|
|||
94
examples/example_emscripten_opengl3/Makefile.emscripten
Normal file
94
examples/example_emscripten_opengl3/Makefile.emscripten
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
#
|
||||
# Makefile to use with emscripten
|
||||
# See https://emscripten.org/docs/getting_started/downloads.html
|
||||
# for installation instructions.
|
||||
#
|
||||
# This Makefile assumes you have loaded emscripten's environment.
|
||||
# (On Windows, you may need to execute emsdk_env.bat or encmdprompt.bat ahead)
|
||||
#
|
||||
# Running `make -f Makefile.emscripten` will produce three files:
|
||||
# - web/index.html
|
||||
# - web/index.js
|
||||
# - web/index.wasm
|
||||
#
|
||||
# All three are needed to run the demo.
|
||||
|
||||
CC = emcc
|
||||
CXX = em++
|
||||
WEB_DIR = web
|
||||
EXE = $(WEB_DIR)/index.html
|
||||
IMGUI_DIR = ../..
|
||||
SOURCES = main.cpp
|
||||
SOURCES += $(IMGUI_DIR)/backends/imgui_impl_emscripten.cpp $(IMGUI_DIR)/backends/imgui_impl_opengl3.cpp
|
||||
SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp
|
||||
OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES))))
|
||||
UNAME_S := $(shell uname -s)
|
||||
CPPFLAGS =
|
||||
LDFLAGS =
|
||||
EMS =
|
||||
|
||||
##---------------------------------------------------------------------
|
||||
## EMSCRIPTEN OPTIONS
|
||||
##---------------------------------------------------------------------
|
||||
|
||||
# ("EMS" options gets added to both CPPFLAGS and LDFLAGS, whereas some options are for linker only)
|
||||
EMS += -s DISABLE_EXCEPTION_CATCHING=1
|
||||
LDFLAGS += -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s NO_EXIT_RUNTIME=0 -s ASSERTIONS=1
|
||||
|
||||
# Use WebGL 1 by default. Set USE_WEBGL2=1 to use OpenGL ES 3 and WebGL 2 instead.
|
||||
USE_WEBGL2 ?= 0
|
||||
ifeq ($(USE_WEBGL2), 1)
|
||||
CPPFLAGS += -DIMGUI_IMPL_OPENGL_ES3
|
||||
LDFLAGS += -s MIN_WEBGL_VERSION=2 -s MAX_WEBGL_VERSION=2
|
||||
endif
|
||||
|
||||
# Build as single file (binary text encoded in .html file)
|
||||
#LDFLAGS += -sSINGLE_FILE
|
||||
|
||||
# Emscripten allows preloading a file or folder to be accessible at runtime.
|
||||
# The Makefile for this example project suggests embedding the misc/fonts/ folder into our application, it will then be accessible as "/fonts"
|
||||
# See documentation for more details: https://emscripten.org/docs/porting/files/packaging_files.html
|
||||
# (Default value is 0. Set to 1 to enable file-system and include the misc/fonts/ folder as part of the build.)
|
||||
USE_FILE_SYSTEM ?= 0
|
||||
ifeq ($(USE_FILE_SYSTEM), 0)
|
||||
LDFLAGS += -s NO_FILESYSTEM=1
|
||||
CPPFLAGS += -DIMGUI_DISABLE_FILE_FUNCTIONS
|
||||
endif
|
||||
ifeq ($(USE_FILE_SYSTEM), 1)
|
||||
LDFLAGS += --no-heap-copy --preload-file ../../misc/fonts@/fonts
|
||||
endif
|
||||
|
||||
##---------------------------------------------------------------------
|
||||
## FINAL BUILD FLAGS
|
||||
##---------------------------------------------------------------------
|
||||
|
||||
CPPFLAGS += -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends
|
||||
#CPPFLAGS += -g
|
||||
CPPFLAGS += -Wall -Wformat -Os $(EMS)
|
||||
LDFLAGS += --shell-file ../libs/emscripten/shell_minimal.html
|
||||
LDFLAGS += $(EMS)
|
||||
|
||||
##---------------------------------------------------------------------
|
||||
## BUILD RULES
|
||||
##---------------------------------------------------------------------
|
||||
|
||||
%.o:%.cpp
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
%.o:$(IMGUI_DIR)/%.cpp
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
%.o:$(IMGUI_DIR)/backends/%.cpp
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
all: $(EXE)
|
||||
@echo Build complete for $(EXE)
|
||||
|
||||
$(WEB_DIR):
|
||||
mkdir $@
|
||||
|
||||
$(EXE): $(OBJS) $(WEB_DIR)
|
||||
$(CXX) -o $@ $(OBJS) $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f $(EXE) $(OBJS) $(WEB_DIR)/*.js $(WEB_DIR)/*.wasm $(WEB_DIR)/*.wasm.pre
|
||||
23
examples/example_emscripten_opengl3/README.md
Normal file
23
examples/example_emscripten_opengl3/README.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
## How to Build
|
||||
|
||||
- You need to install Emscripten from https://emscripten.org/docs/getting_started/downloads.html, and have the environment variables set, as described in https://emscripten.org/docs/getting_started/downloads.html#installation-instructions
|
||||
|
||||
- Depending on your configuration, in Windows you may need to run `emsdk/emsdk_env.bat` in your console to access the Emscripten command-line tools.
|
||||
|
||||
- You may also refer to our [Continuous Integration setup](https://github.com/ocornut/imgui/tree/master/.github/workflows) for Emscripten setup.
|
||||
|
||||
- Then build using `make -f Makefile.emscripten` while in the `example_emscripten_opengl3/` directory.
|
||||
|
||||
- This example is browser-only. It uses `imgui_impl_emscripten` for platform input and `imgui_impl_opengl3` for rendering.
|
||||
|
||||
- The default build targets OpenGL ES 2 and WebGL 1. Build with `make -f Makefile.emscripten USE_WEBGL2=1` to target OpenGL ES 3 and WebGL 2 instead.
|
||||
|
||||
## How to Run
|
||||
|
||||
To run on a local machine:
|
||||
- `emrun web/index.html` will spawn a temporary local webserver and open the example in your browser. See https://emscripten.org/docs/compiling/Running-html-files-with-emrun.html for details.
|
||||
- Otherwise, generally you will need a local webserver:
|
||||
- Quoting [https://emscripten.org/docs/getting_started](https://emscripten.org/docs/getting_started/Tutorial.html#generating-html):<br>
|
||||
_"Unfortunately several browsers (including Chrome, Safari, and Internet Explorer) do not support file:// [XHR](https://emscripten.org/docs/site/glossary.html#term-xhr) requests, and can't load extra files needed by the HTML (like a .wasm file, or packaged file data as mentioned lower down). For these browsers you'll need to serve the files using a [local webserver](https://emscripten.org/docs/getting_started/FAQ.html#faq-local-webserver) and then open http://localhost:8000/hello.html."_
|
||||
- You may use Python 3 builtin webserver: `python -m http.server -d web`.
|
||||
- You may use Python 2 builtin webserver: `cd web && python -m SimpleHTTPServer`.
|
||||
210
examples/example_emscripten_opengl3/main.cpp
Normal file
210
examples/example_emscripten_opengl3/main.cpp
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
// Dear ImGui: standalone example application for using Emscripten + OpenGL 3
|
||||
// - This uses imgui_impl_emscripten for platform inputs and imgui_impl_opengl3 for rendering.
|
||||
// - Emscripten is required to build this example. See https://emscripten.org.
|
||||
// - WebGL 1 is used by default. Define IMGUI_IMPL_OPENGL_ES3 and enable WebGL 2 to use WebGL 2 instead.
|
||||
|
||||
// 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
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_emscripten.h"
|
||||
#include "imgui_impl_opengl3.h"
|
||||
#include <stdio.h>
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
#include <GLES2/gl2.h>
|
||||
#elif defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
#include <GLES3/gl3.h>
|
||||
#endif
|
||||
#include <emscripten.h>
|
||||
#include <emscripten/html5.h>
|
||||
#include "../libs/emscripten/emscripten_mainloop_stub.h"
|
||||
|
||||
// Data
|
||||
static int gl_framebuffer_width = 0;
|
||||
static int gl_framebuffer_height = 0;
|
||||
|
||||
// Forward declarations
|
||||
static void GetFramebufferSizeFromDisplaySize(int width, int height, int* framebuffer_width, int* framebuffer_height);
|
||||
static void ResizeCanvas(int width, int height);
|
||||
|
||||
static void GetFramebufferSizeFromDisplaySize(int width, int height, int* framebuffer_width, int* framebuffer_height)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
*framebuffer_width = (int)(width * io.DisplayFramebufferScale.x + 0.5f);
|
||||
*framebuffer_height = (int)(height * io.DisplayFramebufferScale.y + 0.5f);
|
||||
}
|
||||
|
||||
static void ResizeCanvas(int width, int height)
|
||||
{
|
||||
if (width <= 0 || height <= 0)
|
||||
return;
|
||||
|
||||
int framebuffer_width = 0;
|
||||
int framebuffer_height = 0;
|
||||
GetFramebufferSizeFromDisplaySize(width, height, &framebuffer_width, &framebuffer_height);
|
||||
if (framebuffer_width <= 0 || framebuffer_height <= 0)
|
||||
return;
|
||||
|
||||
emscripten_set_canvas_element_size("#canvas", framebuffer_width, framebuffer_height);
|
||||
gl_framebuffer_width = framebuffer_width;
|
||||
gl_framebuffer_height = framebuffer_height;
|
||||
}
|
||||
|
||||
// Main code
|
||||
int main(int, char**)
|
||||
{
|
||||
// Decide GL+GLSL versions
|
||||
EmscriptenWebGLContextAttributes context_attributes;
|
||||
emscripten_webgl_init_context_attributes(&context_attributes);
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
// GL ES 2.0 + GLSL 100 (WebGL 1.0)
|
||||
const char* glsl_version = "#version 100";
|
||||
context_attributes.majorVersion = 1;
|
||||
context_attributes.minorVersion = 0;
|
||||
#elif defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
// GL ES 3.0 + GLSL 300 es (WebGL 2.0)
|
||||
const char* glsl_version = "#version 300 es";
|
||||
context_attributes.majorVersion = 2;
|
||||
context_attributes.minorVersion = 0;
|
||||
#endif
|
||||
|
||||
// Create window with graphics context
|
||||
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE gl_context = emscripten_webgl_create_context("#canvas", &context_attributes);
|
||||
if (gl_context == 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to create WebGL context.\n");
|
||||
return 1;
|
||||
}
|
||||
if (emscripten_webgl_make_context_current(gl_context) != EMSCRIPTEN_RESULT_SUCCESS)
|
||||
{
|
||||
fprintf(stderr, "Failed to make WebGL context current.\n");
|
||||
emscripten_webgl_destroy_context(gl_context);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Setup Dear ImGui context
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
||||
|
||||
// Setup Dear ImGui style
|
||||
ImGui::StyleColorsDark();
|
||||
//ImGui::StyleColorsLight();
|
||||
|
||||
// Setup Platform/Renderer backends
|
||||
ImGui_ImplEmscripten_Init();
|
||||
if (io.DisplaySize.x > 0.0f && io.DisplaySize.y > 0.0f)
|
||||
ResizeCanvas((int)io.DisplaySize.x, (int)io.DisplaySize.y);
|
||||
ImGui_ImplOpenGL3_Init(glsl_version);
|
||||
|
||||
// Load Fonts
|
||||
// - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap().
|
||||
// This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold.
|
||||
// - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
|
||||
// - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit).
|
||||
// - Read 'docs/FONTS.md' for more instructions and details.
|
||||
// - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering.
|
||||
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
|
||||
// - Our Emscripten build process allows embedding fonts to be accessible at runtime from the "fonts/" folder. See Makefile.emscripten for details.
|
||||
//style.FontSizeBase = 20.0f;
|
||||
//io.Fonts->AddFontDefaultVector();
|
||||
//io.Fonts->AddFontDefaultBitmap();
|
||||
#ifndef IMGUI_DISABLE_FILE_FUNCTIONS
|
||||
//io.Fonts->AddFontFromFileTTF("fonts/segoeui.ttf");
|
||||
//io.Fonts->AddFontFromFileTTF("fonts/DroidSans.ttf");
|
||||
//io.Fonts->AddFontFromFileTTF("fonts/Roboto-Medium.ttf");
|
||||
//io.Fonts->AddFontFromFileTTF("fonts/Cousine-Regular.ttf");
|
||||
//ImFont* font = io.Fonts->AddFontFromFileTTF("fonts/ArialUni.ttf");
|
||||
//IM_ASSERT(font != nullptr);
|
||||
#endif
|
||||
|
||||
// Our state
|
||||
bool show_demo_window = true;
|
||||
bool show_another_window = false;
|
||||
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
||||
|
||||
// Main loop
|
||||
// For an Emscripten build we are disabling file-system access, so let's not attempt to do a fopen() of the imgui.ini file.
|
||||
// You may manually call LoadIniSettingsFromMemory() to load settings from your own storage.
|
||||
io.IniFilename = nullptr;
|
||||
EMSCRIPTEN_MAINLOOP_BEGIN
|
||||
{
|
||||
// Input handling is callback-driven via imgui_impl_emscripten, so there is no event pump here.
|
||||
|
||||
// React to changes in browser window size.
|
||||
int width = (int)io.DisplaySize.x;
|
||||
int height = (int)io.DisplaySize.y;
|
||||
if (width <= 0 || height <= 0)
|
||||
continue;
|
||||
|
||||
int framebuffer_width = 0;
|
||||
int framebuffer_height = 0;
|
||||
GetFramebufferSizeFromDisplaySize(width, height, &framebuffer_width, &framebuffer_height);
|
||||
if (framebuffer_width != gl_framebuffer_width || framebuffer_height != gl_framebuffer_height)
|
||||
ResizeCanvas(width, height);
|
||||
|
||||
// Start the Dear ImGui frame
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplEmscripten_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
// 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
|
||||
if (show_demo_window)
|
||||
ImGui::ShowDemoWindow(&show_demo_window);
|
||||
|
||||
// 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
|
||||
{
|
||||
static float f = 0.0f;
|
||||
static int counter = 0;
|
||||
|
||||
ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.
|
||||
|
||||
ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
|
||||
ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state
|
||||
ImGui::Checkbox("Another Window", &show_another_window);
|
||||
|
||||
ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
|
||||
ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
|
||||
|
||||
if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
|
||||
counter++;
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("counter = %d", counter);
|
||||
|
||||
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// 3. Show another simple window.
|
||||
if (show_another_window)
|
||||
{
|
||||
ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
|
||||
ImGui::Text("Hello from another window!");
|
||||
if (ImGui::Button("Close Me"))
|
||||
show_another_window = false;
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// Rendering
|
||||
ImGui::Render();
|
||||
glViewport(0, 0, framebuffer_width, framebuffer_height);
|
||||
glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
}
|
||||
EMSCRIPTEN_MAINLOOP_END;
|
||||
|
||||
// Cleanup
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplEmscripten_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
emscripten_webgl_destroy_context(gl_context);
|
||||
|
||||
return 0;
|
||||
}
|
||||
99
examples/example_emscripten_wgpu/Makefile.emscripten
Normal file
99
examples/example_emscripten_wgpu/Makefile.emscripten
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
#
|
||||
# Makefile to use with emscripten
|
||||
# See https://emscripten.org/docs/getting_started/downloads.html
|
||||
# for installation instructions.
|
||||
#
|
||||
# This Makefile assumes you have loaded emscripten's environment.
|
||||
# (On Windows, you may need to execute emsdk_env.bat or encmdprompt.bat ahead)
|
||||
#
|
||||
# Running `make -f Makefile.emscripten` will produce three files:
|
||||
# - web/index.html
|
||||
# - web/index.js
|
||||
# - web/index.wasm
|
||||
#
|
||||
# All three are needed to run the demo.
|
||||
|
||||
CC = emcc
|
||||
CXX = em++
|
||||
WEB_DIR = web
|
||||
EXE = $(WEB_DIR)/index.html
|
||||
IMGUI_DIR = ../..
|
||||
SOURCES = main.cpp
|
||||
SOURCES += $(IMGUI_DIR)/backends/imgui_impl_emscripten.cpp $(IMGUI_DIR)/backends/imgui_impl_wgpu.cpp
|
||||
SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp
|
||||
OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES))))
|
||||
UNAME_S := $(shell uname -s)
|
||||
CPPFLAGS =
|
||||
LDFLAGS =
|
||||
EMS =
|
||||
|
||||
##---------------------------------------------------------------------
|
||||
## EMSCRIPTEN OPTIONS
|
||||
##---------------------------------------------------------------------
|
||||
|
||||
# ("EMS" options gets added to both CPPFLAGS and LDFLAGS, whereas some options are for linker only)
|
||||
EMS += -s DISABLE_EXCEPTION_CATCHING=1
|
||||
LDFLAGS += -s WASM=1
|
||||
LDFLAGS += -s ALLOW_MEMORY_GROWTH=1
|
||||
LDFLAGS += -s ASYNCIFY=1
|
||||
LDFLAGS += -s NO_EXIT_RUNTIME=0
|
||||
LDFLAGS += -s ASSERTIONS=1
|
||||
|
||||
# This example uses imgui_impl_emscripten directly, so no GLFW port is required.
|
||||
# Use Emscripten's Dawn-based WebGPU port.
|
||||
EMS += --use-port=emdawnwebgpu
|
||||
LDFLAGS += --use-port=emdawnwebgpu
|
||||
|
||||
# Build as single file (binary text encoded in .html file)
|
||||
#LDFLAGS += -sSINGLE_FILE
|
||||
|
||||
# Emscripten allows preloading a file or folder to be accessible at runtime.
|
||||
# The Makefile for this example project suggests embedding the misc/fonts/ folder into our application, it will then be accessible as "/fonts"
|
||||
# See documentation for more details: https://emscripten.org/docs/porting/files/packaging_files.html
|
||||
# (Default value is 0. Set to 1 to enable file-system and include the misc/fonts/ folder as part of the build.)
|
||||
USE_FILE_SYSTEM ?= 0
|
||||
ifeq ($(USE_FILE_SYSTEM), 0)
|
||||
LDFLAGS += -s NO_FILESYSTEM=1
|
||||
CPPFLAGS += -DIMGUI_DISABLE_FILE_FUNCTIONS
|
||||
endif
|
||||
ifeq ($(USE_FILE_SYSTEM), 1)
|
||||
LDFLAGS += --no-heap-copy --preload-file ../../misc/fonts@/fonts
|
||||
endif
|
||||
|
||||
##---------------------------------------------------------------------
|
||||
## FINAL BUILD FLAGS
|
||||
##---------------------------------------------------------------------
|
||||
|
||||
CPPFLAGS += -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends
|
||||
#CPPFLAGS += -g
|
||||
CPPFLAGS += -Wall -Wformat -Os $(EMS)
|
||||
LDFLAGS += --shell-file ../libs/emscripten/shell_minimal.html
|
||||
LDFLAGS += $(EMS)
|
||||
|
||||
##---------------------------------------------------------------------
|
||||
## BUILD RULES
|
||||
##---------------------------------------------------------------------
|
||||
|
||||
%.o:%.cpp
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
%.o:$(IMGUI_DIR)/%.cpp
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
%.o:$(IMGUI_DIR)/backends/%.cpp
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
all: $(EXE)
|
||||
@echo Build complete for $(EXE)
|
||||
|
||||
$(WEB_DIR):
|
||||
mkdir $@
|
||||
|
||||
serve: all
|
||||
python3 -m http.server -d $(WEB_DIR)
|
||||
|
||||
$(EXE): $(OBJS) $(WEB_DIR)
|
||||
$(CXX) -o $@ $(OBJS) $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f $(EXE) $(OBJS) $(WEB_DIR)/*.js $(WEB_DIR)/*.wasm $(WEB_DIR)/*.wasm.pre
|
||||
24
examples/example_emscripten_wgpu/README.md
Normal file
24
examples/example_emscripten_wgpu/README.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
## How to Build
|
||||
|
||||
- You need to install Emscripten from https://emscripten.org/docs/getting_started/downloads.html, and have the environment variables set, as described in https://emscripten.org/docs/getting_started/downloads.html#installation-instructions
|
||||
|
||||
- Depending on your configuration, in Windows you may need to run `emsdk/emsdk_env.bat` in your console to access the Emscripten command-line tools.
|
||||
|
||||
- You may also refer to our [Continuous Integration setup](https://github.com/ocornut/imgui/tree/master/.github/workflows) for Emscripten setup.
|
||||
|
||||
- Then build using `make -f Makefile.emscripten` while in the `example_emscripten_wgpu/` directory.
|
||||
|
||||
- This example is browser-only. It uses `imgui_impl_emscripten` for platform input and `imgui_impl_wgpu` for rendering.
|
||||
|
||||
- Requires recent Emscripten with the `emdawnwebgpu` port available.
|
||||
|
||||
## How to Run
|
||||
|
||||
To run on a local machine:
|
||||
- Make sure your browser supports WebGPU and it is enabled.
|
||||
- `emrun web/index.html` will spawn a temporary local webserver and open the example in your browser. See https://emscripten.org/docs/compiling/Running-html-files-with-emrun.html for details.
|
||||
- Otherwise, generally you will need a local webserver:
|
||||
- Quoting [https://emscripten.org/docs/getting_started](https://emscripten.org/docs/getting_started/Tutorial.html#generating-html):<br>
|
||||
_"Unfortunately several browsers (including Chrome, Safari, and Internet Explorer) do not support file:// [XHR](https://emscripten.org/docs/site/glossary.html#term-xhr) requests, and can’t load extra files needed by the HTML (like a .wasm file, or packaged file data as mentioned lower down). For these browsers you’ll need to serve the files using a [local webserver](https://emscripten.org/docs/getting_started/FAQ.html#faq-local-webserver) and then open http://localhost:8000/hello.html."_
|
||||
- You may use Python 3 builtin webserver: `python -m http.server -d web` (this is what `make serve` uses).
|
||||
- You may use Python 2 builtin webserver: `cd web && python -m SimpleHTTPServer`.
|
||||
352
examples/example_emscripten_wgpu/main.cpp
Normal file
352
examples/example_emscripten_wgpu/main.cpp
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
// Dear ImGui: standalone example application for using Emscripten + WebGPU
|
||||
// - This uses imgui_impl_emscripten for platform inputs and imgui_impl_wgpu for rendering.
|
||||
// - Emscripten is required to build this example. See https://emscripten.org.
|
||||
// - Dawn is used as the WebGPU implementation via Emscripten's emdawnwebgpu port.
|
||||
|
||||
// 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
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_emscripten.h"
|
||||
#include "imgui_impl_wgpu.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <utility>
|
||||
#include <emscripten.h>
|
||||
#include <emscripten/html5.h>
|
||||
#include "../libs/emscripten/emscripten_mainloop_stub.h"
|
||||
#include <webgpu/webgpu.h>
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#if !defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
|
||||
#error This example requires IMGUI_IMPL_WEBGPU_BACKEND_DAWN.
|
||||
#endif
|
||||
|
||||
// Data
|
||||
static WGPUInstance wgpu_instance = nullptr;
|
||||
static WGPUDevice wgpu_device = nullptr;
|
||||
static WGPUSurface wgpu_surface = nullptr;
|
||||
static WGPUQueue wgpu_queue = nullptr;
|
||||
static WGPUSurfaceConfiguration wgpu_surface_configuration = {};
|
||||
static int wgpu_surface_width = 1280;
|
||||
static int wgpu_surface_height = 800;
|
||||
|
||||
// Forward declarations
|
||||
static bool InitWGPU();
|
||||
static void GetFramebufferSizeFromDisplaySize(int width, int height, int* framebuffer_width, int* framebuffer_height);
|
||||
static void ResizeSurface(int width, int height);
|
||||
|
||||
static void GetFramebufferSizeFromDisplaySize(int width, int height, int* framebuffer_width, int* framebuffer_height)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
*framebuffer_width = (int)(width * io.DisplayFramebufferScale.x + 0.5f);
|
||||
*framebuffer_height = (int)(height * io.DisplayFramebufferScale.y + 0.5f);
|
||||
}
|
||||
|
||||
static void ResizeSurface(int width, int height)
|
||||
{
|
||||
if (width <= 0 || height <= 0)
|
||||
return;
|
||||
|
||||
int framebuffer_width = 0;
|
||||
int framebuffer_height = 0;
|
||||
GetFramebufferSizeFromDisplaySize(width, height, &framebuffer_width, &framebuffer_height);
|
||||
if (framebuffer_width <= 0 || framebuffer_height <= 0)
|
||||
return;
|
||||
|
||||
emscripten_set_canvas_element_size("#canvas", framebuffer_width, framebuffer_height);
|
||||
wgpu_surface_configuration.width = wgpu_surface_width = framebuffer_width;
|
||||
wgpu_surface_configuration.height = wgpu_surface_height = framebuffer_height;
|
||||
wgpuSurfaceConfigure(wgpu_surface, &wgpu_surface_configuration);
|
||||
}
|
||||
|
||||
// Main code
|
||||
int main(int, char**)
|
||||
{
|
||||
if (!InitWGPU())
|
||||
return 1;
|
||||
|
||||
// Setup Dear ImGui context
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
||||
|
||||
// Setup Dear ImGui style
|
||||
ImGui::StyleColorsDark();
|
||||
//ImGui::StyleColorsLight();
|
||||
|
||||
// Setup Platform/Renderer backends
|
||||
ImGui_ImplEmscripten_Init();
|
||||
if (io.DisplaySize.x > 0.0f && io.DisplaySize.y > 0.0f)
|
||||
ResizeSurface((int)io.DisplaySize.x, (int)io.DisplaySize.y);
|
||||
|
||||
ImGui_ImplWGPU_InitInfo init_info;
|
||||
init_info.Device = wgpu_device;
|
||||
init_info.NumFramesInFlight = 3;
|
||||
init_info.RenderTargetFormat = wgpu_surface_configuration.format;
|
||||
init_info.DepthStencilFormat = WGPUTextureFormat_Undefined;
|
||||
ImGui_ImplWGPU_Init(&init_info);
|
||||
|
||||
// Load Fonts
|
||||
// - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap().
|
||||
// This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold.
|
||||
// - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
|
||||
// - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit).
|
||||
// - Read 'docs/FONTS.md' for more instructions and details.
|
||||
// - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering.
|
||||
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
|
||||
// - Our Emscripten build process allows embedding fonts to be accessible at runtime from the "fonts/" folder. See Makefile.emscripten for details.
|
||||
//style.FontSizeBase = 20.0f;
|
||||
//io.Fonts->AddFontDefaultVector();
|
||||
//io.Fonts->AddFontDefaultBitmap();
|
||||
#ifndef IMGUI_DISABLE_FILE_FUNCTIONS
|
||||
//io.Fonts->AddFontFromFileTTF("fonts/segoeui.ttf");
|
||||
//io.Fonts->AddFontFromFileTTF("fonts/DroidSans.ttf");
|
||||
//io.Fonts->AddFontFromFileTTF("fonts/Roboto-Medium.ttf");
|
||||
//io.Fonts->AddFontFromFileTTF("fonts/Cousine-Regular.ttf");
|
||||
//ImFont* font = io.Fonts->AddFontFromFileTTF("fonts/ArialUni.ttf");
|
||||
//IM_ASSERT(font != nullptr);
|
||||
#endif
|
||||
|
||||
// Our state
|
||||
bool show_demo_window = true;
|
||||
bool show_another_window = false;
|
||||
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
||||
|
||||
// Main loop
|
||||
// For an Emscripten build we are disabling file-system access, so let's not attempt to do a fopen() of the imgui.ini file.
|
||||
// You may manually call LoadIniSettingsFromMemory() to load settings from your own storage.
|
||||
io.IniFilename = nullptr;
|
||||
EMSCRIPTEN_MAINLOOP_BEGIN
|
||||
{
|
||||
// Input handling is callback-driven via imgui_impl_emscripten, so there is no event pump here.
|
||||
|
||||
// React to changes in browser window size.
|
||||
int width = (int)io.DisplaySize.x;
|
||||
int height = (int)io.DisplaySize.y;
|
||||
if (width <= 0 || height <= 0)
|
||||
continue;
|
||||
|
||||
int framebuffer_width = 0;
|
||||
int framebuffer_height = 0;
|
||||
GetFramebufferSizeFromDisplaySize(width, height, &framebuffer_width, &framebuffer_height);
|
||||
if (framebuffer_width != wgpu_surface_width || framebuffer_height != wgpu_surface_height)
|
||||
ResizeSurface(width, height);
|
||||
|
||||
// Check surface status for error. If texture is not optimal, try to reconfigure the surface.
|
||||
WGPUSurfaceTexture surface_texture;
|
||||
wgpuSurfaceGetCurrentTexture(wgpu_surface, &surface_texture);
|
||||
if (ImGui_ImplWGPU_IsSurfaceStatusError(surface_texture.status))
|
||||
{
|
||||
fprintf(stderr, "Unrecoverable Surface Texture status=%#.8x\n", surface_texture.status);
|
||||
abort();
|
||||
}
|
||||
if (ImGui_ImplWGPU_IsSurfaceStatusSubOptimal(surface_texture.status))
|
||||
{
|
||||
if (surface_texture.texture)
|
||||
wgpuTextureRelease(surface_texture.texture);
|
||||
ResizeSurface(width, height);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Start the Dear ImGui frame
|
||||
ImGui_ImplWGPU_NewFrame();
|
||||
ImGui_ImplEmscripten_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
// 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
|
||||
if (show_demo_window)
|
||||
ImGui::ShowDemoWindow(&show_demo_window);
|
||||
|
||||
// 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
|
||||
{
|
||||
static float f = 0.0f;
|
||||
static int counter = 0;
|
||||
|
||||
ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.
|
||||
|
||||
ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
|
||||
ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state
|
||||
ImGui::Checkbox("Another Window", &show_another_window);
|
||||
|
||||
ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
|
||||
ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
|
||||
|
||||
if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
|
||||
counter++;
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("counter = %d", counter);
|
||||
|
||||
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// 3. Show another simple window.
|
||||
if (show_another_window)
|
||||
{
|
||||
ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
|
||||
ImGui::Text("Hello from another window!");
|
||||
if (ImGui::Button("Close Me"))
|
||||
show_another_window = false;
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// Rendering
|
||||
ImGui::Render();
|
||||
|
||||
WGPUTextureViewDescriptor view_desc = {};
|
||||
view_desc.format = wgpu_surface_configuration.format;
|
||||
view_desc.dimension = WGPUTextureViewDimension_2D;
|
||||
view_desc.mipLevelCount = WGPU_MIP_LEVEL_COUNT_UNDEFINED;
|
||||
view_desc.arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED;
|
||||
view_desc.aspect = WGPUTextureAspect_All;
|
||||
|
||||
WGPUTextureView texture_view = wgpuTextureCreateView(surface_texture.texture, &view_desc);
|
||||
|
||||
WGPURenderPassColorAttachment color_attachments = {};
|
||||
color_attachments.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
|
||||
color_attachments.loadOp = WGPULoadOp_Clear;
|
||||
color_attachments.storeOp = WGPUStoreOp_Store;
|
||||
color_attachments.clearValue = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w };
|
||||
color_attachments.view = texture_view;
|
||||
|
||||
WGPURenderPassDescriptor render_pass_desc = {};
|
||||
render_pass_desc.colorAttachmentCount = 1;
|
||||
render_pass_desc.colorAttachments = &color_attachments;
|
||||
render_pass_desc.depthStencilAttachment = nullptr;
|
||||
|
||||
WGPUCommandEncoderDescriptor enc_desc = {};
|
||||
WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(wgpu_device, &enc_desc);
|
||||
|
||||
WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &render_pass_desc);
|
||||
ImGui_ImplWGPU_RenderDrawData(ImGui::GetDrawData(), pass);
|
||||
wgpuRenderPassEncoderEnd(pass);
|
||||
|
||||
WGPUCommandBufferDescriptor cmd_buffer_desc = {};
|
||||
WGPUCommandBuffer cmd_buffer = wgpuCommandEncoderFinish(encoder, &cmd_buffer_desc);
|
||||
wgpuQueueSubmit(wgpu_queue, 1, &cmd_buffer);
|
||||
|
||||
wgpuTextureViewRelease(texture_view);
|
||||
wgpuRenderPassEncoderRelease(pass);
|
||||
wgpuCommandEncoderRelease(encoder);
|
||||
wgpuCommandBufferRelease(cmd_buffer);
|
||||
}
|
||||
EMSCRIPTEN_MAINLOOP_END;
|
||||
|
||||
// Cleanup
|
||||
ImGui_ImplWGPU_Shutdown();
|
||||
ImGui_ImplEmscripten_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
wgpuSurfaceUnconfigure(wgpu_surface);
|
||||
wgpuSurfaceRelease(wgpu_surface);
|
||||
wgpuQueueRelease(wgpu_queue);
|
||||
wgpuDeviceRelease(wgpu_device);
|
||||
wgpuInstanceRelease(wgpu_instance);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static WGPUAdapter RequestAdapter(wgpu::Instance& instance)
|
||||
{
|
||||
wgpu::Adapter acquired_adapter;
|
||||
wgpu::RequestAdapterOptions adapter_options;
|
||||
auto onRequestAdapter = [&](wgpu::RequestAdapterStatus status, wgpu::Adapter adapter, wgpu::StringView message)
|
||||
{
|
||||
if (status != wgpu::RequestAdapterStatus::Success)
|
||||
{
|
||||
printf("Failed to get an adapter: %s\n", message.data);
|
||||
return;
|
||||
}
|
||||
acquired_adapter = std::move(adapter);
|
||||
};
|
||||
|
||||
// Synchronously (wait until) acquire Adapter
|
||||
wgpu::Future waitAdapterFunc { instance.RequestAdapter(&adapter_options, wgpu::CallbackMode::WaitAnyOnly, onRequestAdapter) };
|
||||
wgpu::WaitStatus waitStatusAdapter = instance.WaitAny(waitAdapterFunc, UINT64_MAX);
|
||||
IM_ASSERT(acquired_adapter != nullptr && waitStatusAdapter == wgpu::WaitStatus::Success && "Error on Adapter request");
|
||||
return acquired_adapter.MoveToCHandle();
|
||||
}
|
||||
|
||||
static WGPUDevice RequestDevice(wgpu::Instance& instance, wgpu::Adapter& adapter)
|
||||
{
|
||||
// Set device callback functions
|
||||
wgpu::DeviceDescriptor device_desc;
|
||||
device_desc.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous,
|
||||
[](const wgpu::Device&, wgpu::DeviceLostReason type, wgpu::StringView msg) { fprintf(stderr, "%s error: %s\n", ImGui_ImplWGPU_GetDeviceLostReasonName((WGPUDeviceLostReason)type), msg.data); }
|
||||
);
|
||||
device_desc.SetUncapturedErrorCallback(
|
||||
[](const wgpu::Device&, wgpu::ErrorType type, wgpu::StringView msg) { fprintf(stderr, "%s error: %s\n", ImGui_ImplWGPU_GetErrorTypeName((WGPUErrorType)type), msg.data); }
|
||||
);
|
||||
|
||||
wgpu::Device acquired_device;
|
||||
auto onRequestDevice = [&](wgpu::RequestDeviceStatus status, wgpu::Device local_device, wgpu::StringView message)
|
||||
{
|
||||
if (status != wgpu::RequestDeviceStatus::Success)
|
||||
{
|
||||
printf("Failed to get a device: %s\n", message.data);
|
||||
return;
|
||||
}
|
||||
acquired_device = std::move(local_device);
|
||||
};
|
||||
|
||||
// Synchronously (wait until) get Device
|
||||
wgpu::Future waitDeviceFunc { adapter.RequestDevice(&device_desc, wgpu::CallbackMode::WaitAnyOnly, onRequestDevice) };
|
||||
wgpu::WaitStatus waitStatusDevice = instance.WaitAny(waitDeviceFunc, UINT64_MAX);
|
||||
IM_ASSERT(acquired_device != nullptr && waitStatusDevice == wgpu::WaitStatus::Success && "Error on Device request");
|
||||
return acquired_device.MoveToCHandle();
|
||||
}
|
||||
|
||||
static bool InitWGPU()
|
||||
{
|
||||
WGPUTextureFormat preferred_fmt = WGPUTextureFormat_Undefined; // acquired from SurfaceCapabilities
|
||||
|
||||
// Google DAWN backend: Adapter and Device acquisition, Surface creation
|
||||
wgpu::InstanceDescriptor instance_desc = {};
|
||||
static constexpr wgpu::InstanceFeatureName timedWaitAny = wgpu::InstanceFeatureName::TimedWaitAny;
|
||||
instance_desc.requiredFeatureCount = 1;
|
||||
instance_desc.requiredFeatures = &timedWaitAny;
|
||||
wgpu::Instance instance = wgpu::CreateInstance(&instance_desc);
|
||||
|
||||
wgpu::Adapter adapter = RequestAdapter(instance);
|
||||
ImGui_ImplWGPU_DebugPrintAdapterInfo(adapter.Get());
|
||||
|
||||
wgpu_device = RequestDevice(instance, adapter);
|
||||
|
||||
// Create the surface.
|
||||
wgpu::EmscriptenSurfaceSourceCanvasHTMLSelector canvas_desc = {};
|
||||
canvas_desc.selector = "#canvas";
|
||||
|
||||
wgpu::SurfaceDescriptor surface_desc = {};
|
||||
surface_desc.nextInChain = &canvas_desc;
|
||||
wgpu::Surface surface = instance.CreateSurface(&surface_desc);
|
||||
if (!surface)
|
||||
return false;
|
||||
|
||||
// Moving Dawn objects into WGPU handles
|
||||
wgpu_instance = instance.MoveToCHandle();
|
||||
wgpu_surface = surface.MoveToCHandle();
|
||||
|
||||
WGPUSurfaceCapabilities surface_capabilities = {};
|
||||
wgpuSurfaceGetCapabilities(wgpu_surface, adapter.Get(), &surface_capabilities);
|
||||
|
||||
preferred_fmt = surface_capabilities.formats[0];
|
||||
|
||||
wgpu_surface_configuration.presentMode = WGPUPresentMode_Fifo;
|
||||
wgpu_surface_configuration.alphaMode = WGPUCompositeAlphaMode_Auto;
|
||||
wgpu_surface_configuration.usage = WGPUTextureUsage_RenderAttachment;
|
||||
wgpu_surface_configuration.width = wgpu_surface_width;
|
||||
wgpu_surface_configuration.height = wgpu_surface_height;
|
||||
wgpu_surface_configuration.device = wgpu_device;
|
||||
wgpu_surface_configuration.format = preferred_fmt;
|
||||
|
||||
wgpuSurfaceConfigure(wgpu_surface, &wgpu_surface_configuration);
|
||||
wgpu_queue = wgpuDeviceGetQueue(wgpu_device);
|
||||
|
||||
return true;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue