Web: Emscripten port updated to current tools. Add saving of options in local storage.
This commit is contained in:
parent
5ca6d04e02
commit
f7415048a5
@ -186,6 +186,10 @@ if(APPLE)
|
|||||||
set(CMAKE_FIND_FRAMEWORK LAST)
|
set(CMAKE_FIND_FRAMEWORK LAST)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(EMSCRIPTEN)
|
||||||
|
set(M_LIBRARY "" CACHE STRING "libm (not necessary)" FORCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
message(STATUS "Using in-tree libdxfrw")
|
message(STATUS "Using in-tree libdxfrw")
|
||||||
add_subdirectory(extlib/libdxfrw)
|
add_subdirectory(extlib/libdxfrw)
|
||||||
|
|
||||||
|
29
README.md
29
README.md
@ -168,37 +168,50 @@ command-line interface is built as `build/bin/solvespace-cli.exe`.
|
|||||||
|
|
||||||
Space Navigator support will not be available.
|
Space Navigator support will not be available.
|
||||||
|
|
||||||
## Building for web
|
### Building for web (very experimental)
|
||||||
|
|
||||||
You will need [Emscripten][]. First, install and prepare `emsdk`:
|
**Please note that this port contains many critical bugs and unimplemented core functions.**
|
||||||
|
|
||||||
git clone https://github.com/juj/emsdk.git
|
You will need the usual build tools, cmake and [Emscripten][]. On a Debian derivative (e.g. Ubuntu) dependencies other than Emscripten can be installed with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
apt-get install git build-essential cmake
|
||||||
|
```
|
||||||
|
|
||||||
|
First, install and prepare `emsdk`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/emscripten-core/emsdk
|
||||||
cd emsdk
|
cd emsdk
|
||||||
./emsdk install latest
|
./emsdk install latest
|
||||||
./emsdk update latest
|
./emsdk activate latest
|
||||||
source ./emsdk_env.sh
|
source ./emsdk_env.sh
|
||||||
cd ..
|
cd ..
|
||||||
|
```
|
||||||
|
|
||||||
Before building, check out the project and the necessary submodules:
|
Before building, check out the project and the necessary submodules:
|
||||||
|
|
||||||
|
```sh
|
||||||
git clone https://github.com/solvespace/solvespace
|
git clone https://github.com/solvespace/solvespace
|
||||||
cd solvespace
|
cd solvespace
|
||||||
git submodule update
|
git submodule update --init
|
||||||
|
```
|
||||||
|
|
||||||
After that, build SolveSpace as following:
|
After that, build SolveSpace as following:
|
||||||
|
|
||||||
|
```sh
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-emscripten.cmake \
|
emcmake cmake .. -DCMAKE_BUILD_TYPE=Release -DENABLE_LTO="ON" -DENABLE_TESTS="OFF" -DENABLE_CLI="OFF" -DENABLE_COVERAGE="OFF"
|
||||||
-DCMAKE_BUILD_TYPE=Release
|
|
||||||
make
|
make
|
||||||
|
```
|
||||||
|
|
||||||
The graphical interface is built as multiple files in the `build/bin` directory with names
|
The graphical interface is built as multiple files in the `build/bin` directory with names
|
||||||
starting with `solvespace`. It can be run locally with `emrun build/bin/solvespace.html`.
|
starting with `solvespace`. It can be run locally with `emrun build/bin/solvespace.html`.
|
||||||
|
|
||||||
The command-line interface is not available.
|
The command-line interface is not available.
|
||||||
|
|
||||||
[emscripten]: https://kripken.github.io/emscripten-site/
|
[emscripten]: https://emscripten.org/
|
||||||
|
|
||||||
## Building on macOS
|
## Building on macOS
|
||||||
|
|
||||||
|
@ -7,3 +7,14 @@ set(CMAKE_EXECUTABLE_SUFFIX ".html")
|
|||||||
set(CMAKE_SIZEOF_VOID_P 4)
|
set(CMAKE_SIZEOF_VOID_P 4)
|
||||||
|
|
||||||
set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE)
|
set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE)
|
||||||
|
|
||||||
|
# FIXME(emscripten): Suppress non-c-typedef-for-linkage warnings in solvespace.h
|
||||||
|
add_compile_options(-Wno-non-c-typedef-for-linkage)
|
||||||
|
|
||||||
|
|
||||||
|
# Enable optimization. Workaround for "too many locals" error when runs on browser.
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL Release)
|
||||||
|
add_compile_options(-O2)
|
||||||
|
else()
|
||||||
|
add_compile_options(-O1)
|
||||||
|
endif()
|
||||||
|
@ -6,3 +6,8 @@ add_executable(CDemo
|
|||||||
|
|
||||||
target_link_libraries(CDemo
|
target_link_libraries(CDemo
|
||||||
slvs)
|
slvs)
|
||||||
|
|
||||||
|
if(EMSCRIPTEN)
|
||||||
|
set_target_properties(CDemo PROPERTIES
|
||||||
|
LINK_FLAGS "-s TOTAL_MEMORY=134217728")
|
||||||
|
endif()
|
||||||
|
@ -304,35 +304,12 @@ if(ENABLE_GUI)
|
|||||||
XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME "YES"
|
XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME "YES"
|
||||||
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.solvespace"
|
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.solvespace"
|
||||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
||||||
else()
|
|
||||||
target_sources(solvespace PRIVATE
|
|
||||||
platform/guigtk.cpp)
|
|
||||||
|
|
||||||
target_include_directories(solvespace PRIVATE SYSTEM
|
|
||||||
${GTKMM_INCLUDE_DIRS}
|
|
||||||
${JSONC_INCLUDE_DIRS}
|
|
||||||
${FONTCONFIG_INCLUDE_DIRS})
|
|
||||||
target_link_directories(solvespace PRIVATE
|
|
||||||
${GTKMM_LIBRARY_DIRS}
|
|
||||||
${JSONC_LIBRARY_DIRS}
|
|
||||||
${FONTCONFIG_LIBRARY_DIRS})
|
|
||||||
target_link_libraries(solvespace PRIVATE
|
|
||||||
${GTKMM_LIBRARIES}
|
|
||||||
${JSONC_LIBRARIES}
|
|
||||||
${FONTCONFIG_LIBRARIES})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(MSVC)
|
|
||||||
set_target_properties(solvespace PROPERTIES
|
|
||||||
LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO /INCREMENTAL:NO /OPT:REF")
|
|
||||||
elseif(APPLE)
|
|
||||||
set_target_properties(solvespace PROPERTIES
|
|
||||||
OUTPUT_NAME SolveSpace)
|
|
||||||
elseif(EMSCRIPTEN)
|
elseif(EMSCRIPTEN)
|
||||||
set(SHELL ${CMAKE_CURRENT_SOURCE_DIR}/platform/html/emshell.html)
|
set(SHELL ${CMAKE_CURRENT_SOURCE_DIR}/platform/html/emshell.html)
|
||||||
set(LINK_FLAGS
|
set(LINK_FLAGS
|
||||||
--bind --shell-file ${SHELL}
|
--bind --shell-file ${SHELL}
|
||||||
--no-heap-copy -s ALLOW_MEMORY_GROWTH=1
|
--no-heap-copy -s ALLOW_MEMORY_GROWTH=1 -s WASM=1 -s ASYNCIFY=1
|
||||||
|
-s DYNCALLS=1 -s ASSERTIONS=1
|
||||||
-s TOTAL_STACK=33554432 -s TOTAL_MEMORY=134217728)
|
-s TOTAL_STACK=33554432 -s TOTAL_MEMORY=134217728)
|
||||||
|
|
||||||
get_target_property(resource_names resources NAMES)
|
get_target_property(resource_names resources NAMES)
|
||||||
@ -344,10 +321,12 @@ if(ENABLE_GUI)
|
|||||||
list(APPEND LINK_FLAGS
|
list(APPEND LINK_FLAGS
|
||||||
--emrun --emit-symbol-map
|
--emrun --emit-symbol-map
|
||||||
-s DEMANGLE_SUPPORT=1
|
-s DEMANGLE_SUPPORT=1
|
||||||
-s SAFE_HEAP=1
|
-s SAFE_HEAP=1)
|
||||||
-s WASM=1)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
target_sources(solvespace PRIVATE
|
||||||
|
platform/guihtml.cpp)
|
||||||
|
|
||||||
string(REPLACE ";" " " LINK_FLAGS "${LINK_FLAGS}")
|
string(REPLACE ";" " " LINK_FLAGS "${LINK_FLAGS}")
|
||||||
set_target_properties(solvespace PROPERTIES
|
set_target_properties(solvespace PROPERTIES
|
||||||
LINK_FLAGS "${LINK_FLAGS}")
|
LINK_FLAGS "${LINK_FLAGS}")
|
||||||
@ -368,6 +347,28 @@ if(ENABLE_GUI)
|
|||||||
${EXECUTABLE_OUTPUT_PATH}/solvespaceui.js
|
${EXECUTABLE_OUTPUT_PATH}/solvespaceui.js
|
||||||
COMMENT "Copying UI script"
|
COMMENT "Copying UI script"
|
||||||
VERBATIM)
|
VERBATIM)
|
||||||
|
|
||||||
|
else()
|
||||||
|
target_sources(solvespace PRIVATE
|
||||||
|
platform/guigtk.cpp)
|
||||||
|
|
||||||
|
target_include_directories(solvespace PRIVATE SYSTEM
|
||||||
|
${GTKMM_INCLUDE_DIRS}
|
||||||
|
${JSONC_INCLUDE_DIRS}
|
||||||
|
${FONTCONFIG_INCLUDE_DIRS})
|
||||||
|
target_link_directories(solvespace PRIVATE
|
||||||
|
${GTKMM_LIBRARY_DIRS}
|
||||||
|
${JSONC_LIBRARY_DIRS}
|
||||||
|
${FONTCONFIG_LIBRARY_DIRS})
|
||||||
|
target_link_libraries(solvespace PRIVATE
|
||||||
|
${GTKMM_LIBRARIES}
|
||||||
|
${JSONC_LIBRARIES}
|
||||||
|
${FONTCONFIG_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
set_target_properties(solvespace PROPERTIES
|
||||||
|
LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO /INCREMENTAL:NO /OPT:REF")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ static void HandleError(const char *file, int line, const char *function, const
|
|||||||
FatalError(message);
|
FatalError(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
static val Wrap(std::string str) {
|
static val Wrap(const std::string& str) {
|
||||||
// FIXME(emscripten): a nicer way to do this?
|
// FIXME(emscripten): a nicer way to do this?
|
||||||
EM_ASM($Wrap$ret = UTF8ToString($0), str.c_str());
|
EM_ASM($Wrap$ret = UTF8ToString($0), str.c_str());
|
||||||
return val::global("window")["$Wrap$ret"];
|
return val::global("window")["$Wrap$ret"];
|
||||||
@ -55,7 +55,7 @@ static std::string Unwrap(val emStr) {
|
|||||||
// FIXME(emscripten): a nicer way to do this?
|
// FIXME(emscripten): a nicer way to do this?
|
||||||
val emArray = val::global("window").call<val>("intArrayFromString", emStr, true) ;
|
val emArray = val::global("window").call<val>("intArrayFromString", emStr, true) ;
|
||||||
val::global("window").set("$Wrap$input", emArray);
|
val::global("window").set("$Wrap$input", emArray);
|
||||||
char *strC = (char *)EM_ASM_INT(return allocate($Wrap$input, 'i8', ALLOC_NORMAL));
|
char *strC = (char *)EM_ASM_INT(return allocate($Wrap$input, ALLOC_NORMAL));
|
||||||
std::string str(strC, emArray["length"].as<int>());
|
std::string str(strC, emArray["length"].as<int>());
|
||||||
free(strC);
|
free(strC);
|
||||||
return str;
|
return str;
|
||||||
@ -77,7 +77,7 @@ static val Wrap(std::function<void()> *func) {
|
|||||||
// Fatal errors
|
// Fatal errors
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
void FatalError(std::string message) {
|
void FatalError(const std::string &message) {
|
||||||
fprintf(stderr, "%s", message.c_str());
|
fprintf(stderr, "%s", message.c_str());
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
emscripten_debugger();
|
emscripten_debugger();
|
||||||
@ -92,31 +92,37 @@ void FatalError(std::string message) {
|
|||||||
class SettingsImplHtml : public Settings {
|
class SettingsImplHtml : public Settings {
|
||||||
public:
|
public:
|
||||||
void FreezeInt(const std::string &key, uint32_t value) {
|
void FreezeInt(const std::string &key, uint32_t value) {
|
||||||
// FIXME(emscripten): implement
|
val::global("localStorage").call<void>("setItem", Wrap(key), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ThawInt(const std::string &key, uint32_t defaultValue = 0) {
|
uint32_t ThawInt(const std::string &key, uint32_t defaultValue = 0) {
|
||||||
// FIXME(emscripten): implement
|
val value = val::global("localStorage").call<val>("getItem", Wrap(key));
|
||||||
|
if(value == val::null())
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
|
return val::global("parseInt")(value, 0).as<int>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FreezeFloat(const std::string &key, double value) {
|
void FreezeFloat(const std::string &key, double value) {
|
||||||
// FIXME(emscripten): implement
|
val::global("localStorage").call<void>("setItem", Wrap(key), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
double ThawFloat(const std::string &key, double defaultValue = 0.0) {
|
double ThawFloat(const std::string &key, double defaultValue = 0.0) {
|
||||||
// FIXME(emscripten): implement
|
val value = val::global("localStorage").call<val>("getItem", Wrap(key));
|
||||||
|
if(value == val::null())
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
|
return val::global("parseFloat")(value).as<double>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FreezeString(const std::string &key, const std::string &value) {
|
void FreezeString(const std::string &key, const std::string &value) {
|
||||||
// FIXME(emscripten): implement
|
val::global("localStorage").call<void>("setItem", Wrap(key), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ThawString(const std::string &key,
|
std::string ThawString(const std::string &key,
|
||||||
const std::string &defaultValue = "") {
|
const std::string &defaultValue = "") {
|
||||||
// FIXME(emscripten): implement
|
val value = val::global("localStorage").call<val>("getItem", Wrap(key));
|
||||||
|
if(value == val::null())
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
|
return Unwrap(value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -244,7 +250,7 @@ public:
|
|||||||
} else {
|
} else {
|
||||||
val htmlLabel = val::global("document").call<val>("createElement", val("span"));
|
val htmlLabel = val::global("document").call<val>("createElement", val("span"));
|
||||||
htmlLabel["classList"].call<void>("add", val("label"));
|
htmlLabel["classList"].call<void>("add", val("label"));
|
||||||
htmlLabel["innerText"] = Wrap(label);
|
htmlLabel.set("innerText", Wrap(label));
|
||||||
menuItem->htmlMenuItem.call<void>("appendChild", htmlLabel);
|
menuItem->htmlMenuItem.call<void>("appendChild", htmlLabel);
|
||||||
}
|
}
|
||||||
menuItem->htmlMenuItem.call<void>("addEventListener", val("trigger"),
|
menuItem->htmlMenuItem.call<void>("addEventListener", val("trigger"),
|
||||||
@ -342,7 +348,7 @@ static KeyboardEvent handledKeyboardEvent;
|
|||||||
|
|
||||||
class WindowImplHtml final : public Window {
|
class WindowImplHtml final : public Window {
|
||||||
public:
|
public:
|
||||||
std::string emCanvasId;
|
std::string emCanvasSel;
|
||||||
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE emContext = 0;
|
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE emContext = 0;
|
||||||
|
|
||||||
val htmlContainer;
|
val htmlContainer;
|
||||||
@ -351,8 +357,8 @@ public:
|
|||||||
std::function<void()> editingDoneFunc;
|
std::function<void()> editingDoneFunc;
|
||||||
std::shared_ptr<MenuBarImplHtml> menuBar;
|
std::shared_ptr<MenuBarImplHtml> menuBar;
|
||||||
|
|
||||||
WindowImplHtml(val htmlContainer, std::string emCanvasId) :
|
WindowImplHtml(val htmlContainer, std::string emCanvasSel) :
|
||||||
emCanvasId(emCanvasId),
|
emCanvasSel(emCanvasSel),
|
||||||
htmlContainer(htmlContainer),
|
htmlContainer(htmlContainer),
|
||||||
htmlEditor(val::global("document").call<val>("createElement", val("input")))
|
htmlEditor(val::global("document").call<val>("createElement", val("input")))
|
||||||
{
|
{
|
||||||
@ -367,43 +373,43 @@ public:
|
|||||||
htmlContainer.call<void>("appendChild", htmlEditor);
|
htmlContainer.call<void>("appendChild", htmlEditor);
|
||||||
|
|
||||||
sscheck(emscripten_set_resize_callback(
|
sscheck(emscripten_set_resize_callback(
|
||||||
"#window", this, /*useCapture=*/false,
|
EMSCRIPTEN_EVENT_TARGET_WINDOW, this, /*useCapture=*/false,
|
||||||
WindowImplHtml::ResizeCallback));
|
WindowImplHtml::ResizeCallback));
|
||||||
sscheck(emscripten_set_resize_callback(
|
sscheck(emscripten_set_resize_callback(
|
||||||
emCanvasId.c_str(), this, /*useCapture=*/false,
|
emCanvasSel.c_str(), this, /*useCapture=*/false,
|
||||||
WindowImplHtml::ResizeCallback));
|
WindowImplHtml::ResizeCallback));
|
||||||
sscheck(emscripten_set_mousemove_callback(
|
sscheck(emscripten_set_mousemove_callback(
|
||||||
emCanvasId.c_str(), this, /*useCapture=*/false,
|
emCanvasSel.c_str(), this, /*useCapture=*/false,
|
||||||
WindowImplHtml::MouseCallback));
|
WindowImplHtml::MouseCallback));
|
||||||
sscheck(emscripten_set_mousedown_callback(
|
sscheck(emscripten_set_mousedown_callback(
|
||||||
emCanvasId.c_str(), this, /*useCapture=*/false,
|
emCanvasSel.c_str(), this, /*useCapture=*/false,
|
||||||
WindowImplHtml::MouseCallback));
|
WindowImplHtml::MouseCallback));
|
||||||
sscheck(emscripten_set_click_callback(
|
sscheck(emscripten_set_click_callback(
|
||||||
emCanvasId.c_str(), this, /*useCapture=*/false,
|
emCanvasSel.c_str(), this, /*useCapture=*/false,
|
||||||
WindowImplHtml::MouseCallback));
|
WindowImplHtml::MouseCallback));
|
||||||
sscheck(emscripten_set_dblclick_callback(
|
sscheck(emscripten_set_dblclick_callback(
|
||||||
emCanvasId.c_str(), this, /*useCapture=*/false,
|
emCanvasSel.c_str(), this, /*useCapture=*/false,
|
||||||
WindowImplHtml::MouseCallback));
|
WindowImplHtml::MouseCallback));
|
||||||
sscheck(emscripten_set_mouseup_callback(
|
sscheck(emscripten_set_mouseup_callback(
|
||||||
emCanvasId.c_str(), this, /*useCapture=*/false,
|
emCanvasSel.c_str(), this, /*useCapture=*/false,
|
||||||
WindowImplHtml::MouseCallback));
|
WindowImplHtml::MouseCallback));
|
||||||
sscheck(emscripten_set_mouseleave_callback(
|
sscheck(emscripten_set_mouseleave_callback(
|
||||||
emCanvasId.c_str(), this, /*useCapture=*/false,
|
emCanvasSel.c_str(), this, /*useCapture=*/false,
|
||||||
WindowImplHtml::MouseCallback));
|
WindowImplHtml::MouseCallback));
|
||||||
sscheck(emscripten_set_wheel_callback(
|
sscheck(emscripten_set_wheel_callback(
|
||||||
emCanvasId.c_str(), this, /*useCapture=*/false,
|
emCanvasSel.c_str(), this, /*useCapture=*/false,
|
||||||
WindowImplHtml::WheelCallback));
|
WindowImplHtml::WheelCallback));
|
||||||
sscheck(emscripten_set_keydown_callback(
|
sscheck(emscripten_set_keydown_callback(
|
||||||
"#window", this, /*useCapture=*/false,
|
EMSCRIPTEN_EVENT_TARGET_WINDOW, this, /*useCapture=*/false,
|
||||||
WindowImplHtml::KeyboardCallback));
|
WindowImplHtml::KeyboardCallback));
|
||||||
sscheck(emscripten_set_keyup_callback(
|
sscheck(emscripten_set_keyup_callback(
|
||||||
"#window", this, /*useCapture=*/false,
|
EMSCRIPTEN_EVENT_TARGET_WINDOW, this, /*useCapture=*/false,
|
||||||
WindowImplHtml::KeyboardCallback));
|
WindowImplHtml::KeyboardCallback));
|
||||||
sscheck(emscripten_set_webglcontextlost_callback(
|
sscheck(emscripten_set_webglcontextlost_callback(
|
||||||
emCanvasId.c_str(), this, /*useCapture=*/false,
|
emCanvasSel.c_str(), this, /*useCapture=*/false,
|
||||||
WindowImplHtml::ContextLostCallback));
|
WindowImplHtml::ContextLostCallback));
|
||||||
sscheck(emscripten_set_webglcontextrestored_callback(
|
sscheck(emscripten_set_webglcontextrestored_callback(
|
||||||
emCanvasId.c_str(), this, /*useCapture=*/false,
|
emCanvasSel.c_str(), this, /*useCapture=*/false,
|
||||||
WindowImplHtml::ContextRestoredCallback));
|
WindowImplHtml::ContextRestoredCallback));
|
||||||
|
|
||||||
ResizeCanvasElement();
|
ResizeCanvasElement();
|
||||||
@ -576,13 +582,13 @@ public:
|
|||||||
emAttribs.alpha = false;
|
emAttribs.alpha = false;
|
||||||
emAttribs.failIfMajorPerformanceCaveat = true;
|
emAttribs.failIfMajorPerformanceCaveat = true;
|
||||||
|
|
||||||
sscheck(emContext = emscripten_webgl_create_context(emCanvasId.c_str(), &emAttribs));
|
sscheck(emContext = emscripten_webgl_create_context(emCanvasSel.c_str(), &emAttribs));
|
||||||
dbp("Canvas %s: got context %d", emCanvasId.c_str(), emContext);
|
dbp("Canvas %s: got context %d", emCanvasSel.c_str(), emContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ContextLostCallback(int eventType, const void *reserved, void *data) {
|
static int ContextLostCallback(int eventType, const void *reserved, void *data) {
|
||||||
WindowImplHtml *window = (WindowImplHtml *)data;
|
WindowImplHtml *window = (WindowImplHtml *)data;
|
||||||
dbp("Canvas %s: context lost", window->emCanvasId.c_str());
|
dbp("Canvas %s: context lost", window->emCanvasSel.c_str());
|
||||||
window->emContext = 0;
|
window->emContext = 0;
|
||||||
|
|
||||||
if(window->onContextLost) {
|
if(window->onContextLost) {
|
||||||
@ -593,30 +599,30 @@ public:
|
|||||||
|
|
||||||
static int ContextRestoredCallback(int eventType, const void *reserved, void *data) {
|
static int ContextRestoredCallback(int eventType, const void *reserved, void *data) {
|
||||||
WindowImplHtml *window = (WindowImplHtml *)data;
|
WindowImplHtml *window = (WindowImplHtml *)data;
|
||||||
dbp("Canvas %s: context restored", window->emCanvasId.c_str());
|
dbp("Canvas %s: context restored", window->emCanvasSel.c_str());
|
||||||
window->SetupWebGLContext();
|
window->SetupWebGLContext();
|
||||||
return EM_TRUE;
|
return EM_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResizeCanvasElement() {
|
void ResizeCanvasElement() {
|
||||||
double width, height;
|
double width, height;
|
||||||
std::string htmlContainerId = htmlContainer["id"].as<std::string>();
|
std::string htmlContainerSel = "#" + htmlContainer["id"].as<std::string>();
|
||||||
sscheck(emscripten_get_element_css_size(htmlContainerId.c_str(), &width, &height));
|
sscheck(emscripten_get_element_css_size(htmlContainerSel.c_str(), &width, &height));
|
||||||
width *= emscripten_get_device_pixel_ratio();
|
width *= emscripten_get_device_pixel_ratio();
|
||||||
height *= emscripten_get_device_pixel_ratio();
|
height *= emscripten_get_device_pixel_ratio();
|
||||||
int curWidth, curHeight;
|
int curWidth, curHeight;
|
||||||
sscheck(emscripten_get_canvas_element_size(emCanvasId.c_str(), &curWidth, &curHeight));
|
sscheck(emscripten_get_canvas_element_size(emCanvasSel.c_str(), &curWidth, &curHeight));
|
||||||
if(curWidth != (int)width || curHeight != (int)curHeight) {
|
if(curWidth != (int)width || curHeight != (int)curHeight) {
|
||||||
dbp("Canvas %s: resizing to (%g,%g)", emCanvasId.c_str(), width, height);
|
dbp("Canvas %s: resizing to (%g,%g)", emCanvasSel.c_str(), width, height);
|
||||||
sscheck(emscripten_set_canvas_element_size(
|
sscheck(emscripten_set_canvas_element_size(
|
||||||
emCanvasId.c_str(), (int)width, (int)height));
|
emCanvasSel.c_str(), (int)width, (int)height));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RenderCallback(void *data) {
|
static void RenderCallback(void *data) {
|
||||||
WindowImplHtml *window = (WindowImplHtml *)data;
|
WindowImplHtml *window = (WindowImplHtml *)data;
|
||||||
if(window->emContext == 0) {
|
if(window->emContext == 0) {
|
||||||
dbp("Canvas %s: cannot render: no context", window->emCanvasId.c_str());
|
dbp("Canvas %s: cannot render: no context", window->emCanvasSel.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -661,7 +667,7 @@ public:
|
|||||||
emStrategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF;
|
emStrategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF;
|
||||||
emStrategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
|
emStrategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
|
||||||
sscheck(emscripten_request_fullscreen_strategy(
|
sscheck(emscripten_request_fullscreen_strategy(
|
||||||
emCanvasId.c_str(), /*deferUntilInEventHandler=*/true, &emStrategy));
|
emCanvasSel.c_str(), /*deferUntilInEventHandler=*/true, &emStrategy));
|
||||||
} else {
|
} else {
|
||||||
sscheck(emscripten_exit_fullscreen());
|
sscheck(emscripten_exit_fullscreen());
|
||||||
}
|
}
|
||||||
@ -687,7 +693,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GetContentSize(double *width, double *height) override {
|
void GetContentSize(double *width, double *height) override {
|
||||||
sscheck(emscripten_get_element_css_size(emCanvasId.c_str(), width, height));
|
sscheck(emscripten_get_element_css_size(emCanvasSel.c_str(), width, height));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetMinContentSize(double width, double height) override {
|
void SetMinContentSize(double width, double height) override {
|
||||||
@ -711,8 +717,11 @@ public:
|
|||||||
htmlContainer["style"].set("cursor", htmlCursor);
|
htmlContainer["style"].set("cursor", htmlCursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetTooltip(const std::string &text) override {
|
void SetTooltip(const std::string &text, double x, double y,
|
||||||
// FIXME(emscripten): implement
|
double width, double height) override {
|
||||||
|
val htmlCanvas =
|
||||||
|
val::global("document").call<val>("querySelector", emCanvasSel);
|
||||||
|
htmlCanvas.set("title", Wrap(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsEditorVisible() override {
|
bool IsEditorVisible() override {
|
||||||
@ -766,10 +775,10 @@ WindowRef CreateWindow(Window::Kind kind, WindowRef parentWindow) {
|
|||||||
std::string htmlContainerId = std::string("container") + std::to_string(windowNum);
|
std::string htmlContainerId = std::string("container") + std::to_string(windowNum);
|
||||||
val htmlContainer =
|
val htmlContainer =
|
||||||
val::global("document").call<val>("getElementById", htmlContainerId);
|
val::global("document").call<val>("getElementById", htmlContainerId);
|
||||||
std::string emCanvasId = std::string("canvas") + std::to_string(windowNum);
|
std::string emCanvasSel = std::string("#canvas") + std::to_string(windowNum);
|
||||||
|
|
||||||
windowNum++;
|
windowNum++;
|
||||||
return std::make_shared<WindowImplHtml>(htmlContainer, emCanvasId);
|
return std::make_shared<WindowImplHtml>(htmlContainer, emCanvasSel);
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -836,7 +845,7 @@ public:
|
|||||||
htmlButton["classList"].call<void>("add", val("button"));
|
htmlButton["classList"].call<void>("add", val("button"));
|
||||||
val::global("window").call<void>("setLabelWithMnemonic", htmlButton, Wrap(label));
|
val::global("window").call<void>("setLabelWithMnemonic", htmlButton, Wrap(label));
|
||||||
if(isDefault) {
|
if(isDefault) {
|
||||||
htmlButton["classList"].call<void>("add", val("selected"));
|
htmlButton["classList"].call<void>("add", val("default"), val("selected"));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::function<void()> responseFunc = [this, response] {
|
std::function<void()> responseFunc = [this, response] {
|
||||||
@ -900,9 +909,15 @@ void OpenInBrowser(const std::string &url) {
|
|||||||
val::global("window").call<void>("open", Wrap(url));
|
val::global("window").call<void>("open", Wrap(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitGui(int argc, char **argv) {
|
std::vector<std::string> InitGui(int argc, char **argv) {
|
||||||
|
static std::function<void()> onBeforeUnload = std::bind(&SolveSpaceUI::Exit, &SS);
|
||||||
|
val::global("window").call<void>("addEventListener", val("beforeunload"),
|
||||||
|
Wrap(&onBeforeUnload));
|
||||||
|
|
||||||
// FIXME(emscripten): get locale from user preferences
|
// FIXME(emscripten): get locale from user preferences
|
||||||
SetLocale("en_US");
|
SetLocale("en_US");
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void MainLoopIteration() {
|
static void MainLoopIteration() {
|
||||||
@ -917,5 +932,7 @@ void ExitGui() {
|
|||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClearGui() {}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,4 +79,4 @@
|
|||||||
};
|
};
|
||||||
</script><!--
|
</script><!--
|
||||||
-->{{{ SCRIPT }}}<!--
|
-->{{{ SCRIPT }}}<!--
|
||||||
--></body></html><!--
|
--></body></html>
|
||||||
|
@ -85,6 +85,11 @@ body {
|
|||||||
background: hsl(0, 0%, 20%);
|
background: hsl(0, 0%, 20%);
|
||||||
color: white;
|
color: white;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
|
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Normal menu items */
|
/* Normal menu items */
|
||||||
|
@ -95,6 +95,8 @@ window.addEventListener('keydown', function(event) {
|
|||||||
}
|
}
|
||||||
} else if(event.key == 'Enter') {
|
} else if(event.key == 'Enter') {
|
||||||
selected.dispatchEvent(new Event('trigger'));
|
selected.dispatchEvent(new Event('trigger'));
|
||||||
|
} else if(event.key == 'Escape' && hasClass(selected, 'default')) {
|
||||||
|
selected.dispatchEvent(new Event('trigger'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(outSelected) removeClass(outSelected, 'selected');
|
if(outSelected) removeClass(outSelected, 'selected');
|
||||||
@ -127,7 +129,7 @@ window.addEventListener('keydown', function(event) {
|
|||||||
}
|
}
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
});
|
}, {capture: true});
|
||||||
|
|
||||||
/* Menu helpers */
|
/* Menu helpers */
|
||||||
function isMenubar(element) {
|
function isMenubar(element) {
|
||||||
|
@ -516,7 +516,7 @@ static Platform::Path ResourcePath(const std::string &name) {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(EMSCRIPTEN)
|
#elif defined(__EMSCRIPTEN__)
|
||||||
|
|
||||||
static Platform::Path ResourcePath(const std::string &name) {
|
static Platform::Path ResourcePath(const std::string &name) {
|
||||||
return Path::From("res/" + name);
|
return Path::From("res/" + name);
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#ifndef SOLVESPACE_GL3SHADER_H
|
#ifndef SOLVESPACE_GL3SHADER_H
|
||||||
#define SOLVESPACE_GL3SHADER_H
|
#define SOLVESPACE_GL3SHADER_H
|
||||||
|
|
||||||
#if defined(WIN32) || defined(EMSCRIPTEN)
|
#if defined(WIN32) || defined(__EMSCRIPTEN__)
|
||||||
# define GL_APICALL /*static linkage*/
|
# define GL_APICALL /*static linkage*/
|
||||||
# define GL_GLEXT_PROTOTYPES
|
# define GL_GLEXT_PROTOTYPES
|
||||||
# include <GLES2/gl2.h>
|
# include <GLES2/gl2.h>
|
||||||
|
Loading…
Reference in New Issue
Block a user