pthom / hello_imgui

Hello, Dear ImGui: unleash your creativity in app development and prototyping
https://pthom.github.io/hello_imgui
MIT License
606 stars 91 forks source link

IDBFS Support #57

Closed mrawlingst closed 10 months ago

mrawlingst commented 1 year ago

I am trying to implement support for IDBFS for Emscripten. Emscripten by default does not include IDBFS and we have to include it by -lidbfs.js.

I've been struggling to include it - granted I am not great with CMake yet. If anyone is able to help me resolve this, that would be great!

My CMake is essentially the same as the example integration file with almost zero changes (just listing include dirs and source files). This is what I tried to do:

if (EMSCRIPTEN)
    add_link_options(-lidbfs.js)
endif()
pthom commented 1 year ago

Hello,

I do not know much about IDBFS. Based on my tests, the following code succesfully stores persistent data inside /data/DUMMY_VALUE.txt

Cmake:

include(hello_imgui_add_app)
hello_imgui_add_app(hello_idbfs hello_idbfs.main.cpp)
if(EMSCRIPTEN)
    target_link_options(hello_idbfs PUBLIC -lidbfs.js)
endif()

C++: hello_idbfs.cpp contains:

#include "hello_imgui/hello_imgui.h"
#include <fstream>

#ifdef EMSCRIPTEN
#include "emscripten.h"
#else
void emscripten_run_script(const char* script)
{
    printf("emscripten_run_script implemented only for emscripten!");
}
#endif

// Dummy value that is store inside /data/DUMMY_VALUE.txt
int DUMMY_VALUE = 0;

// This mounts the IndexedDB at /data
void MountIdbfsData()
{
    std::string script = R"(
        FS.mkdir('/data');
        FS.mount(IDBFS, {}, '/data');

        // Sync IndexedDB => Emscripten
        FS.syncfs(true, function (err) {
            console.log("Sync IndexedDB => Emscripten ended: err= " + err);
        });
    )";
    emscripten_run_script(script.c_str());
}

// Sync Emscripten => IndexedDB
void SyncEmscriptenToIndexDB()
{
    std::string script = R"(
        FS.syncfs(false, function (err) {
            console.log("SyncEmscriptenToIndexDB ended: err= " + err);
        });
    )";
    emscripten_run_script(script.c_str());
}

void WriteDummyValue()
{
    std::ofstream ofs("/data/DUMMY_VALUE.txt");
    if (ofs.good())
        ofs << DUMMY_VALUE;
    else
        fprintf(stderr, "WriteDummyValue: Could not open /data/DUMMY_VALUE.txt");
}

void ReadDummyValue()
{
    std::ifstream ifs("/data/DUMMY_VALUE.txt");
    if (ifs.good())
        ifs >> DUMMY_VALUE;
    else
        fprintf(stderr, "ReadDummyValue: Could not open /data/DUMMY_VALUE.txt");
}

void ShowGui()
{
    ImGui::Text("Hello, IDBFS!");

    ImGui::SliderInt("DUMMY_VALUE", &DUMMY_VALUE, 0, 100);

    if (ImGui::Button("Mount /data via idbfs"))
        MountIdbfsData();
    if (ImGui::Button("WriteDummyValue"))
        WriteDummyValue();
    if (ImGui::Button("ReadDummyValue"))
        ReadDummyValue();
    if (ImGui::Button("SyncEmscriptenToIndexDB"))
        SyncEmscriptenToIndexDB();
}

int main(int , char *[])
{
    HelloImGui::Run(
        ShowGui,
        "IDBFS test", // Window title
        true          // Window size auto
    );
    return 0;
}

Note that you could mount the data at startup automatically, either via C++, or via javascript if you edit the html and add for example:

Module['onRuntimeInitialized'] = function() {
    FS.mkdir('/data');
    FS.mount(IDBFS, {}, '/data');

    // Sync IndexedDB => Emscripten
    FS.syncfs(true, function (err) {
            console.log("Sync IndexedDB => Emscripten ended: err= " + err);
    });
};