diff --git a/.travis.yml b/.travis.yml
index 64243cd..638478b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -64,6 +64,10 @@ script:
- ./build/voidstar --list
- mv -v build/voidstar "build/voidstar-$(git describe --abbrev --dirty --always --tags)-$TRAVIS_OS_NAME-$(basename $CC)"
- if [[ "$TRAVIS_OS_NAME" = 'linux' ]]; then cppcheck --error-exitcode=1 --enable=all -Isrc/include/ -Isrc/common/tdogl/ src/; fi
+ - |
+ if [[ "$TRAVIS_OS_NAME" = 'linux' ]] && [[ "$TRAVIS_BRANCH" != 'master' ]] && [[ "$CC" = 'gcc' ]]; then
+ docker run --rm -it -v "$PWD":/src trzeci/emscripten:sdk-tag-1.37.18-64bit bash -c 'rm -rf build && mkdir build && cd build && cp -vr ../data . && apt update && apt install -y libglm-dev && emcmake cmake --DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_CXX_COMPILER=em++ -DCMAKE_BUILD_TYPE=Release -DCMAKE_MAKE_PROGRAM=emmake .. && emmake make'
+ fi
after_script:
- set +e
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5ec076c..f1882e8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8)
+cmake_minimum_required(VERSION 3.0)
# set cmake module path, to search in cmake/modules first
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")
@@ -11,7 +11,12 @@ include(static_analysis)
#-----------------------------------------------------------------------------
# GENERAL CONFIGURATION
#-----------------------------------------------------------------------------
-project(ProjectName CXX)
+project(voidstar CXX)
+
+## These are cmake >= 3.1
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS ON)
#-----------------------------------------------------------------------------
# DEPENDENCIES
#-----------------------------------------------------------------------------
@@ -20,13 +25,25 @@ project(ProjectName CXX)
# ==> and cmake --help-module FindBoost to known which variables are set.
# find_package(Boost COMPONENTS filesystem threads)
# include_directories(${Boost_INCLUDE_DIRS})
-find_package(OpenGL REQUIRED)
-include_directories(${OpenGL_INCLUDE_DIR})
-find_package(GLEW REQUIRED)
-include_directories(${GLEW_INCLUDE_DIRS})
-find_package(PkgConfig REQUIRED)
-pkg_search_module(GLFW REQUIRED glfw3)
-include_directories(${GLFW_INCLUDE_DIRS})
+
+if(EMSCRIPTEN)
+ message(STATUS "here!")
+ include_directories( /usr/include )
+
+ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH)
+ message(STATUS "Cmake prefix path: ${CMAKE_PREFIX_PATH}")
+endif(EMSCRIPTEN)
+
+if(NOT EMSCRIPTEN)
+ message(STATUS "not here!")
+ find_package(OpenGL REQUIRED)
+ include_directories(${OpenGL_INCLUDE_DIR})
+ find_package(GLEW REQUIRED)
+ include_directories(${GLEW_INCLUDE_DIRS})
+ find_package(PkgConfig REQUIRED)
+ pkg_search_module(GLFW REQUIRED glfw3)
+ include_directories(${GLFW_INCLUDE_DIRS})
+endif()
#-----------------------------------------------------------------------------
# BUILD TYPES & FLAGS
@@ -34,17 +51,18 @@ include_directories(${GLFW_INCLUDE_DIRS})
include(sanitizers)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g3 -O0")
-set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Werror -O2")
+#set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Werror -O2")
+set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Werror -O0")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -std=c++1y ${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}}")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.9")
else(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.9")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color")
endif()
-# strip binary in release mode
-if(CMAKE_BUILD_TYPE MATCHES "RELEASE")
- set(CMAKE_EXE_LINKER_FLAGS "-s")
-endif()
+# # strip binary in release mode
+# if(CMAKE_BUILD_TYPE MATCHES "RELEASE")
+# set(CMAKE_EXE_LINKER_FLAGS "-s")
+# endif()
#-----------------------------------------------------------------------------
# SOURCES
@@ -101,23 +119,29 @@ if(APPLE)
endif(APPLE)
# create your executable with
-add_executable(voidstar ${GUI_TYPE} ${SOURCES})
+add_executable(${PROJECT_NAME} ${GUI_TYPE} ${SOURCES})
-add_dependencies(voidstar shaders_out)
+add_dependencies(${PROJECT_NAME} shaders_out)
# create a library with
# add_library(my_lib SHARED ${SOURCES})
-# you can link both with
-target_link_libraries(voidstar
- m
- ${OPENGL_gl_LIBRARY}
- ${GLEW_LIBRARIES}
- ${GLFW_STATIC_LIBRARIES} # This needs manual addition of ${GLFW_STATIC_LDFLAGS}
- ${EXTRA_LIBS}
-)
+if (NOT EMSCRIPTEN)
+ target_link_libraries(${PROJECT_NAME}
+ m
+ ${OPENGL_gl_LIBRARY}
+ ${GLEW_LIBRARIES}
+ ${GLFW_STATIC_LIBRARIES} # This needs manual addition of ${GLFW_STATIC_LDFLAGS}
+ ${EXTRA_LIBS}
+ )
string(REPLACE ";" " " glfw_static_ldflags "${GLFW_STATIC_LDFLAGS}")
-set_property(TARGET voidstar APPEND_STRING PROPERTY LINK_FLAGS "${glfw_static_ldflags}")
+set_property(TARGET ${PROJECT_NAME} APPEND_STRING PROPERTY LINK_FLAGS "${glfw_static_ldflags}")
+endif()
+
+if(EMSCRIPTEN)
+ set_target_properties(${PROJECT_NAME} PROPERTIES SUFFIX ".html")
+ set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-s USE_GLFW=3 -s USE_WEBGL2=1 -s FULL_ES3=1 -s DISABLE_EXCEPTION_CATCHING=2 --preload-file data/rgb555.xraw -s ALLOW_MEMORY_GROWTH=1")
+endif()
#-----------------------------------------------------------------------------
# PACKAGING
@@ -134,7 +158,7 @@ set_property(TARGET voidstar APPEND_STRING PROPERTY LINK_FLAGS "${glfw_static_ld
# OR
# install(PROGRAMS myexecutable DESTINATION
)
-install(TARGETS voidstar RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX})
+install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX})
include(uninstall)
# add_subdirectory("${CMAKE_SOURCE_DIR}/cmake/cpack") # enable packaging with CPack
diff --git a/emscDisplayMgr.h b/emscDisplayMgr.h
new file mode 100644
index 0000000..b0d1de7
--- /dev/null
+++ b/emscDisplayMgr.h
@@ -0,0 +1,48 @@
+#pragma once
+//------------------------------------------------------------------------------
+/**
+ @class Oryol::_priv::emscDisplayMgr
+ @ingroup _priv
+ @brief display manager class for emscripten platform
+*/
+#include "Gfx/private/displayMgrBase.h"
+#include
+
+namespace Oryol {
+
+// emscripten-specific soft-fullscreen control functions
+extern "C" {
+extern void enter_soft_fullscreen();
+extern void leave_soft_fullscreen();
+extern bool is_soft_fullscreen_active();
+}
+
+namespace _priv {
+
+class emscDisplayMgr : public displayMgrBase {
+public:
+ /// constructor
+ emscDisplayMgr();
+ /// destructor
+ ~emscDisplayMgr();
+
+ /// setup the display system, must happen before rendering
+ void SetupDisplay(const GfxSetup& gfxSetup, const gfxPointers& ptrs);
+ /// discard the display, rendering cannot happen after
+ void DiscardDisplay();
+
+ /// bind the default frame buffer
+ void glBindDefaultFramebuffer();
+ /// emscripten callback when canvas size has changed (for soft-fullscreen)
+ static EM_BOOL emscCanvasSizeChanged(int eventType, const void* reserved, void* userData);
+ /// emscripten callback when window size has changed (for HTMLUseCanvasSize)
+ static EM_BOOL emscWindowSizeChanged(int eventType, const EmscriptenUiEvent* uiEvent, void* userData);
+
+ static emscDisplayMgr* self;
+ int storedCanvasWidth;
+ int storedCanvasHeight;
+ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx;
+};
+
+} // namespace _priv
+} // namespace Oryol
diff --git a/emscInputMgr.h b/emscInputMgr.h
new file mode 100644
index 0000000..4fd6fc7
--- /dev/null
+++ b/emscInputMgr.h
@@ -0,0 +1,73 @@
+#pragma once
+//------------------------------------------------------------------------------
+/**
+ @class Oryol::_priv::emscInputMgr
+ @ingroup _priv
+ @brief provide input on emscripten platform
+*/
+#include "Input/private/inputMgrBase.h"
+#include "Core/RunLoop.h"
+#include
+
+namespace Oryol {
+namespace _priv {
+
+class emscInputMgr : public inputMgrBase {
+public:
+ /// constructor
+ emscInputMgr();
+ /// destructor
+ ~emscInputMgr();
+
+ /// setup the input manager
+ void setup(const InputSetup& setup);
+ /// discard the input manager
+ void discard();
+
+ /// setup game pad mappings for known gamepads
+ void setupGamepadMappings();
+ /// polling for the gamepad
+ void updateGamepads();
+ /// setup the key mapping table
+ void setupKeyTable();
+ /// setup emscripten input handling callbacks
+ void setupCallbacks();
+ /// detach emscripten callbacks
+ void discardCallbacks();
+ /// map HTML5 key code to Oryol keycode
+ Key::Code mapKey(unsigned long html5KeyCode) const;
+ /// map HTML5 mouse button code to Oryol mouse button
+ MouseButton::Code mapMouseButton(unsigned short html5Btn) const;
+
+ /// update mouse pointer lock state
+ static bool updatePointerLockMode(PointerLockMode::Code lockMode);
+ /// key down callback
+ static EM_BOOL emscKeyDown(int eventType, const EmscriptenKeyboardEvent* e, void* userData);
+ /// key up callback
+ static EM_BOOL emscKeyUp(int eventType, const EmscriptenKeyboardEvent* e, void* userData);
+ /// key press callback
+ static EM_BOOL emscKeyPress(int eventType, const EmscriptenKeyboardEvent* e, void* userData);
+ /// mouse-button-down callback
+ static EM_BOOL emscMouseDown(int eventType, const EmscriptenMouseEvent* e, void* userData);
+ /// mouse-button-up callback
+ static EM_BOOL emscMouseUp(int eventType, const EmscriptenMouseEvent* e, void* userData);
+ /// mouse-move callback
+ static EM_BOOL emscMouseMove(int eventType, const EmscriptenMouseEvent* e, void* userData);
+ /// wheel callback
+ static EM_BOOL emscWheel(int eventType, const EmscriptenWheelEvent* e, void* userData);
+ /// touch event callback (same callback for touchstart, touchmove, touchend, touchcancel)
+ static EM_BOOL emscTouch(int eventType, const EmscriptenTouchEvent* e, void* userData);
+ /// device motion callback
+ static EM_BOOL emscDeviceMotion(int eventType, const EmscriptenDeviceMotionEvent* e, void* userData);
+ /// device orientation callback
+ static EM_BOOL emscDeviceOrientation(int eventType, const EmscriptenDeviceOrientationEvent* e, void* userData);
+
+ static const int MaxNumKeys = 256;
+ RunLoop::Id runLoopId;
+ RunLoop::Id updateGamepadsRunLoopId;
+ bool pointerLockActive;
+ Key::Code keyTable[MaxNumKeys];
+};
+
+} // namespace _priv
+} // namespace Oryol
diff --git a/src/GlfwManager.cc b/src/GlfwManager.cc
index 33d563a..593da74 100644
--- a/src/GlfwManager.cc
+++ b/src/GlfwManager.cc
@@ -71,7 +71,9 @@ GlfwManager::init() {
glfwSetKeyCallback(window_, onKeyEvent);
// GLFW settings
+#ifndef __EMSCRIPTEN__
glfwSetInputMode(window_, GLFW_STICKY_KEYS, GL_TRUE);
+#endif
glfwSetCursorPos(window_, 0, 0);
glfwSetScrollCallback(window_, onScroll);
glfwMakeContextCurrent(window_);
diff --git a/src/common/tdogl/Camera.h b/src/common/tdogl/Camera.h
index a28fff2..2660f5f 100644
--- a/src/common/tdogl/Camera.h
+++ b/src/common/tdogl/Camera.h
@@ -18,6 +18,7 @@
#pragma once
+#define GLM_FORCE_RADIANS
#include
diff --git a/src/common/tdogl/Program.h b/src/common/tdogl/Program.h
index bba2f3e..d2bc84c 100644
--- a/src/common/tdogl/Program.h
+++ b/src/common/tdogl/Program.h
@@ -20,6 +20,7 @@
#include "Shader.h"
#include
+#define GLM_FORCE_RADIANS
#include
namespace tdogl {
diff --git a/src/include/Arguments.hh b/src/include/Arguments.hh
index 5d6118d..db430ad 100644
--- a/src/include/Arguments.hh
+++ b/src/include/Arguments.hh
@@ -11,13 +11,15 @@ struct Arguments {
manager("glfw"),
width(800), height(600),
range_begin(0), range_end(0),
- fullscreen(false),
- keep_chrome(false),
sliding_window_length(2*1024), sliding_step(1024), move_window(false),
- spin_shape(false)
+ spin_shape(false),
+ fullscreen(false),
+ keep_chrome(false)
+#ifdef __EMSCRIPTEN__
+ ,input({"../data/rgb555.xraw"})
+#endif
{}
- std::vector input;
std::string name;
std::string algo;
std::string manager;
@@ -25,12 +27,13 @@ struct Arguments {
size_t height;
size_t range_begin;
size_t range_end;
- bool fullscreen;
- bool keep_chrome;
size_t sliding_window_length;
size_t sliding_step;
bool move_window;
bool spin_shape;
+ bool fullscreen;
+ bool keep_chrome;
+ std::vector input;
};
std::shared_ptr parseArgs(int argc, char *argv[]);
diff --git a/test_html5.c b/test_html5.c
new file mode 100644
index 0000000..ad8fba4
--- /dev/null
+++ b/test_html5.c
@@ -0,0 +1,482 @@
+#include
+#include
+#include
+#include
+
+static inline const char *emscripten_event_type_to_string(int eventType) {
+ const char *events[] = { "(invalid)", "(none)", "keypress", "keydown", "keyup", "click", "mousedown", "mouseup", "dblclick", "mousemove", "wheel", "resize",
+ "scroll", "blur", "focus", "focusin", "focusout", "deviceorientation", "devicemotion", "orientationchange", "fullscreenchange", "pointerlockchange",
+ "visibilitychange", "touchstart", "touchend", "touchmove", "touchcancel", "gamepadconnected", "gamepaddisconnected", "beforeunload",
+ "batterychargingchange", "batterylevelchange", "webglcontextlost", "webglcontextrestored", "mouseenter", "mouseleave", "mouseover", "mouseout", "(invalid)" };
+ ++eventType;
+ if (eventType < 0) eventType = 0;
+ if (eventType >= sizeof(events)/sizeof(events[0])) eventType = sizeof(events)/sizeof(events[0])-1;
+ return events[eventType];
+}
+
+const char *emscripten_result_to_string(EMSCRIPTEN_RESULT result) {
+ if (result == EMSCRIPTEN_RESULT_SUCCESS) return "EMSCRIPTEN_RESULT_SUCCESS";
+ if (result == EMSCRIPTEN_RESULT_DEFERRED) return "EMSCRIPTEN_RESULT_DEFERRED";
+ if (result == EMSCRIPTEN_RESULT_NOT_SUPPORTED) return "EMSCRIPTEN_RESULT_NOT_SUPPORTED";
+ if (result == EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED) return "EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED";
+ if (result == EMSCRIPTEN_RESULT_INVALID_TARGET) return "EMSCRIPTEN_RESULT_INVALID_TARGET";
+ if (result == EMSCRIPTEN_RESULT_UNKNOWN_TARGET) return "EMSCRIPTEN_RESULT_UNKNOWN_TARGET";
+ if (result == EMSCRIPTEN_RESULT_INVALID_PARAM) return "EMSCRIPTEN_RESULT_INVALID_PARAM";
+ if (result == EMSCRIPTEN_RESULT_FAILED) return "EMSCRIPTEN_RESULT_FAILED";
+ if (result == EMSCRIPTEN_RESULT_NO_DATA) return "EMSCRIPTEN_RESULT_NO_DATA";
+ return "Unknown EMSCRIPTEN_RESULT!";
+}
+
+#define TEST_RESULT(x) if (ret != EMSCRIPTEN_RESULT_SUCCESS) printf("%s returned %s.\n", #x, emscripten_result_to_string(ret));
+
+// The event handler functions can return 1 to suppress the event and disable the default action. That calls event.preventDefault();
+// Returning 0 signals that the event was not consumed by the code, and will allow the event to pass on and bubble up normally.
+EM_BOOL key_callback(int eventType, const EmscriptenKeyboardEvent *e, void *userData)
+{
+ printf("%s, key: \"%s\", code: \"%s\", location: %lu,%s%s%s%s repeat: %d, locale: \"%s\", char: \"%s\", charCode: %lu, keyCode: %lu, which: %lu\n",
+ emscripten_event_type_to_string(eventType), e->key, e->code, e->location,
+ e->ctrlKey ? " CTRL" : "", e->shiftKey ? " SHIFT" : "", e->altKey ? " ALT" : "", e->metaKey ? " META" : "",
+ e->repeat, e->locale, e->charValue, e->charCode, e->keyCode, e->which);
+
+ if (eventType == EMSCRIPTEN_EVENT_KEYPRESS && (!strcmp(e->key, "f") || e->which == 102)) {
+ EmscriptenFullscreenChangeEvent fsce;
+ EMSCRIPTEN_RESULT ret = emscripten_get_fullscreen_status(&fsce);
+ TEST_RESULT(emscripten_get_fullscreen_status);
+ if (!fsce.isFullscreen) {
+ printf("Requesting fullscreen..\n");
+ ret = emscripten_request_fullscreen(0, 1);
+ TEST_RESULT(emscripten_request_fullscreen);
+ } else {
+ printf("Exiting fullscreen..\n");
+ ret = emscripten_exit_fullscreen();
+ TEST_RESULT(emscripten_exit_fullscreen);
+ ret = emscripten_get_fullscreen_status(&fsce);
+ TEST_RESULT(emscripten_get_fullscreen_status);
+ if (fsce.isFullscreen) {
+ fprintf(stderr, "Fullscreen exit did not work!\n");
+ }
+ }
+ }
+
+ if (eventType == EMSCRIPTEN_EVENT_KEYPRESS && (!strcmp(e->key, "p") || e->which == 112)) {
+ EmscriptenPointerlockChangeEvent plce;
+ EMSCRIPTEN_RESULT ret = emscripten_get_pointerlock_status(&plce);
+ TEST_RESULT(emscripten_get_pointerlock_status);
+ if (!plce.isActive) {
+ printf("Requesting pointer lock..\n");
+ ret = emscripten_request_pointerlock(0, 1);
+ TEST_RESULT(emscripten_request_pointerlock);
+ } else {
+ printf("Exiting pointer lock..\n");
+ ret = emscripten_exit_pointerlock();
+ TEST_RESULT(emscripten_exit_pointerlock);
+ ret = emscripten_get_pointerlock_status(&plce);
+ TEST_RESULT(emscripten_get_pointerlock_status);
+ if (plce.isActive) {
+ fprintf(stderr, "Pointer lock exit did not work!\n");
+ }
+ }
+ }
+
+ return 0;
+}
+
+EM_BOOL mouse_callback(int eventType, const EmscriptenMouseEvent *e, void *userData)
+{
+ printf("%s, screen: (%ld,%ld), client: (%ld,%ld),%s%s%s%s button: %hu, buttons: %hu, movement: (%ld,%ld), canvas: (%ld,%ld)\n",
+ emscripten_event_type_to_string(eventType), e->screenX, e->screenY, e->clientX, e->clientY,
+ e->ctrlKey ? " CTRL" : "", e->shiftKey ? " SHIFT" : "", e->altKey ? " ALT" : "", e->metaKey ? " META" : "",
+ e->button, e->buttons, e->movementX, e->movementY, e->canvasX, e->canvasY);
+
+ return 0;
+}
+
+EM_BOOL wheel_callback(int eventType, const EmscriptenWheelEvent *e, void *userData)
+{
+ printf("%s, screen: (%ld,%ld), client: (%ld,%ld),%s%s%s%s button: %hu, buttons: %hu, canvas: (%ld,%ld), delta:(%g,%g,%g), deltaMode:%lu\n",
+ emscripten_event_type_to_string(eventType), e->mouse.screenX, e->mouse.screenY, e->mouse.clientX, e->mouse.clientY,
+ e->mouse.ctrlKey ? " CTRL" : "", e->mouse.shiftKey ? " SHIFT" : "", e->mouse.altKey ? " ALT" : "", e->mouse.metaKey ? " META" : "",
+ e->mouse.button, e->mouse.buttons, e->mouse.canvasX, e->mouse.canvasY,
+ (float)e->deltaX, (float)e->deltaY, (float)e->deltaZ, e->deltaMode);
+
+ return 0;
+}
+
+EM_BOOL uievent_callback(int eventType, const EmscriptenUiEvent *e, void *userData)
+{
+ printf("%s, detail: %ld, document.body.client size: (%d,%d), window.inner size: (%d,%d), scrollPos: (%d, %d)\n",
+ emscripten_event_type_to_string(eventType), e->detail, e->documentBodyClientWidth, e->documentBodyClientHeight,
+ e->windowInnerWidth, e->windowInnerHeight, e->scrollTop, e->scrollLeft);
+
+ return 0;
+}
+
+EM_BOOL focusevent_callback(int eventType, const EmscriptenFocusEvent *e, void *userData)
+{
+ printf("%s, nodeName: \"%s\", id: \"%s\"\n", emscripten_event_type_to_string(eventType), e->nodeName, e->id[0] == '\0' ? "(empty string)" : e->id);
+
+ return 0;
+}
+
+EM_BOOL deviceorientation_callback(int eventType, const EmscriptenDeviceOrientationEvent *e, void *userData)
+{
+ printf("%s, (%g, %g, %g)\n", emscripten_event_type_to_string(eventType), e->alpha, e->beta, e->gamma);
+
+ return 0;
+}
+
+EM_BOOL devicemotion_callback(int eventType, const EmscriptenDeviceMotionEvent *e, void *userData)
+{
+ printf("%s, accel: (%g, %g, %g), accelInclGravity: (%g, %g, %g), rotationRate: (%g, %g, %g)\n",
+ emscripten_event_type_to_string(eventType),
+ e->accelerationX, e->accelerationY, e->accelerationZ,
+ e->accelerationIncludingGravityX, e->accelerationIncludingGravityY, e->accelerationIncludingGravityZ,
+ e->rotationRateAlpha, e->rotationRateBeta, e->rotationRateGamma);
+
+ return 0;
+}
+
+EM_BOOL orientationchange_callback(int eventType, const EmscriptenOrientationChangeEvent *e, void *userData)
+{
+ printf("%s, orientationAngle: %d, orientationIndex: %d\n", emscripten_event_type_to_string(eventType), e->orientationAngle, e->orientationIndex);
+
+ return 0;
+}
+
+EM_BOOL fullscreenchange_callback(int eventType, const EmscriptenFullscreenChangeEvent *e, void *userData)
+{
+ printf("%s, isFullscreen: %d, fullscreenEnabled: %d, fs element nodeName: \"%s\", fs element id: \"%s\". New size: %dx%d pixels. Screen size: %dx%d pixels.\n",
+ emscripten_event_type_to_string(eventType), e->isFullscreen, e->fullscreenEnabled, e->nodeName, e->id, e->elementWidth, e->elementHeight, e->screenWidth, e->screenHeight);
+
+ return 0;
+}
+
+EM_BOOL pointerlockchange_callback(int eventType, const EmscriptenPointerlockChangeEvent *e, void *userData)
+{
+ printf("%s, isActive: %d, pointerlock element nodeName: \"%s\", id: \"%s\"\n",
+ emscripten_event_type_to_string(eventType), e->isActive, e->nodeName, e->id);
+
+ return 0;
+}
+
+EM_BOOL visibilitychange_callback(int eventType, const EmscriptenVisibilityChangeEvent *e, void *userData)
+{
+ printf("%s, hidden: %d, visibilityState: %d\n", emscripten_event_type_to_string(eventType), e->hidden, e->visibilityState);
+
+ return 0;
+}
+
+EM_BOOL touch_callback(int eventType, const EmscriptenTouchEvent *e, void *userData)
+{
+ printf("%s, numTouches: %d %s%s%s%s\n",
+ emscripten_event_type_to_string(eventType), e->numTouches,
+ e->ctrlKey ? " CTRL" : "", e->shiftKey ? " SHIFT" : "", e->altKey ? " ALT" : "", e->metaKey ? " META" : "");
+ for(int i = 0; i < e->numTouches; ++i)
+ {
+ const EmscriptenTouchPoint *t = &e->touches[i];
+ printf(" %ld: screen: (%ld,%ld), client: (%ld,%ld), page: (%ld,%ld), isChanged: %d, onTarget: %d, canvas: (%ld, %ld)\n",
+ t->identifier, t->screenX, t->screenY, t->clientX, t->clientY, t->pageX, t->pageY, t->isChanged, t->onTarget, t->canvasX, t->canvasY);
+ }
+
+ return 0;
+}
+
+EM_BOOL gamepad_callback(int eventType, const EmscriptenGamepadEvent *e, void *userData)
+{
+ printf("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"\n",
+ eventType != 0 ? emscripten_event_type_to_string(eventType) : "Gamepad state", e->timestamp, e->connected, e->index,
+ e->numAxes, e->numButtons, e->id, e->mapping);
+
+ if (e->connected)
+ {
+ for(int i = 0; i < e->numAxes; ++i)
+ printf("Axis %d: %g\n", i, e->axis[i]);
+
+ for(int i = 0; i < e->numButtons; ++i)
+ printf("Button %d: Digital: %d, Analog: %g\n", i, e->digitalButton[i], e->analogButton[i]);
+ }
+
+ return 0;
+}
+
+const char *beforeunload_callback(int eventType, const void *reserved, void *userData)
+{
+#ifdef REPORT_RESULT
+ return ""; // For test harness, don't show a confirmation dialog to not block and keep the test runner automated.
+#else
+ return "Do you really want to leave the page?";
+#endif
+}
+
+void formatTime(char *str, int seconds)
+{
+ int h = seconds / (60*60);
+ seconds -= h*60*60;
+ int m = seconds / 60;
+ seconds -= m*60;
+ if (h > 0)
+ {
+ sprintf(str, "%dh:%02dm:%02ds", h, m, seconds);
+ }
+ else
+ {
+ sprintf(str, "%02dm:%02ds", m, seconds);
+ }
+}
+
+EM_BOOL battery_callback(int eventType, const EmscriptenBatteryEvent *e, void *userData)
+{
+ char t1[64];
+ formatTime(t1, (int)e->chargingTime);
+ char t2[64];
+ formatTime(t2, (int)e->dischargingTime);
+ printf("%s: chargingTime: %s, dischargingTime: %s, level: %g%%, charging: %d\n",
+ emscripten_event_type_to_string(eventType), t1, t2, e->level*100, e->charging);
+
+ return 0;
+}
+
+EM_BOOL webglcontext_callback(int eventType, const void *reserved, void *userData)
+{
+ printf("%s.\n", emscripten_event_type_to_string(eventType));
+
+ return 0;
+}
+
+EmscriptenGamepadEvent prevState[32];
+int prevNumGamepads = 0;
+
+void mainloop()
+{
+ int numGamepads = emscripten_get_num_gamepads();
+ if (numGamepads != prevNumGamepads)
+ {
+ if (numGamepads == EMSCRIPTEN_RESULT_NOT_SUPPORTED) {
+ printf("emscripten_get_num_gamepads returned EMSCRIPTEN_RESULT_NOT_SUPPORTED.\n");
+ emscripten_cancel_main_loop();
+ return;
+ } else {
+ printf("Number of connected gamepads: %d\n", numGamepads);
+ }
+ prevNumGamepads = numGamepads;
+ }
+
+ for(int i = 0; i < numGamepads && i < 32; ++i)
+ {
+ EmscriptenGamepadEvent ge;
+ int ret = emscripten_get_gamepad_status(i, &ge);
+ if (ret == EMSCRIPTEN_RESULT_SUCCESS)
+ {
+ int g = ge.index;
+ for(int j = 0; j < ge.numAxes; ++j)
+ {
+ if (ge.axis[j] != prevState[g].axis[j])
+ printf("Gamepad %d, axis %d: %g\n", g, j, ge.axis[j]);
+ }
+
+ for(int j = 0; j < ge.numButtons; ++j)
+ {
+ if (ge.analogButton[j] != prevState[g].analogButton[j] || ge.digitalButton[j] != prevState[g].digitalButton[j])
+ printf("Gamepad %d, button %d: Digital: %d, Analog: %g\n", g, j, ge.digitalButton[j], ge.analogButton[j]);
+ }
+ prevState[g] = ge;
+ }
+ }
+
+}
+
+#ifdef REPORT_RESULT
+void report_result(void *arg)
+{
+ int result = 0;
+ REPORT_RESULT();
+}
+#endif
+
+int main()
+{
+
+ EMSCRIPTEN_RESULT ret = emscripten_set_keypress_callback(0, 0, 1, key_callback);
+ TEST_RESULT(emscripten_set_keypress_callback);
+ ret = emscripten_set_keydown_callback(0, 0, 1, key_callback);
+ TEST_RESULT(emscripten_set_keydown_callback);
+ ret = emscripten_set_keyup_callback(0, 0, 1, key_callback);
+ TEST_RESULT(emscripten_set_keyup_callback);
+
+ ret = emscripten_set_click_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_click_callback);
+ ret = emscripten_set_mousedown_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_mousedown_callback);
+ ret = emscripten_set_mouseup_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_mouseup_callback);
+ ret = emscripten_set_dblclick_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_dblclick_callback);
+ ret = emscripten_set_mousemove_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_mousemove_callback);
+ ret = emscripten_set_mouseenter_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_mouseenter_callback);
+ ret = emscripten_set_mouseleave_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_mouseleave_callback);
+ ret = emscripten_set_mouseover_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_mouseover_callback);
+ ret = emscripten_set_mouseout_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_mouseout_callback);
+
+ ret = emscripten_set_wheel_callback(0, 0, 1, wheel_callback);
+ TEST_RESULT(emscripten_set_wheel_callback);
+
+ ret = emscripten_set_resize_callback(0, 0, 1, uievent_callback);
+ TEST_RESULT(emscripten_set_resize_callback);
+ ret = emscripten_set_scroll_callback(0, 0, 1, uievent_callback);
+ TEST_RESULT(emscripten_set_scroll_callback);
+
+ ret = emscripten_set_blur_callback(0, 0, 1, focusevent_callback);
+ TEST_RESULT(emscripten_set_blur_callback);
+ ret = emscripten_set_focus_callback(0, 0, 1, focusevent_callback);
+ TEST_RESULT(emscripten_set_focus_callback);
+ ret = emscripten_set_focusin_callback(0, 0, 1, focusevent_callback);
+ TEST_RESULT(emscripten_set_focusin_callback);
+ ret = emscripten_set_focusout_callback(0, 0, 1, focusevent_callback);
+ TEST_RESULT(emscripten_set_focusout_callback);
+
+ ret = emscripten_set_deviceorientation_callback(0, 1, deviceorientation_callback);
+ TEST_RESULT(emscripten_set_deviceorientation_callback);
+ ret = emscripten_set_devicemotion_callback(0, 1, devicemotion_callback);
+ TEST_RESULT(emscripten_set_devicemotion_callback);
+
+ ret = emscripten_set_orientationchange_callback(0, 1, orientationchange_callback);
+ TEST_RESULT(emscripten_set_orientationchange_callback);
+
+ // Test the polling of orientation.
+ EmscriptenOrientationChangeEvent oce;
+ ret = emscripten_get_orientation_status(&oce);
+ TEST_RESULT(emscripten_get_orientation_status);
+ if (ret == EMSCRIPTEN_RESULT_SUCCESS) {
+ printf("The current orientation is:\n");
+ orientationchange_callback(EMSCRIPTEN_EVENT_ORIENTATIONCHANGE, &oce, 0);
+ }
+
+ int newOrientation = (oce.orientationIndex == EMSCRIPTEN_ORIENTATION_PORTRAIT_PRIMARY
+ || oce.orientationIndex == EMSCRIPTEN_ORIENTATION_PORTRAIT_SECONDARY) ? EMSCRIPTEN_ORIENTATION_LANDSCAPE_PRIMARY : EMSCRIPTEN_ORIENTATION_PORTRAIT_PRIMARY;
+ // Test locking of orientation.
+ ret = emscripten_lock_orientation(newOrientation);
+ TEST_RESULT(emscripten_lock_orientation);
+ if (ret == EMSCRIPTEN_RESULT_SUCCESS) {
+ printf("Locked orientation to state %d.\n", newOrientation);
+ }
+
+ ret = emscripten_get_orientation_status(&oce);
+ TEST_RESULT(emscripten_get_orientation_status);
+ if (ret == EMSCRIPTEN_RESULT_SUCCESS) {
+ printf("The current orientation is after locking:\n");
+ orientationchange_callback(18, &oce, 0);
+ }
+
+ ret = emscripten_unlock_orientation();
+ TEST_RESULT(emscripten_unlock_orientation);
+ if (ret == EMSCRIPTEN_RESULT_SUCCESS) {
+ printf("Unlocked orientation.\n");
+ }
+
+ EmscriptenFullscreenChangeEvent fsce;
+ ret = emscripten_get_fullscreen_status(&fsce);
+ TEST_RESULT(emscripten_get_fullscreen_status);
+ if (ret == EMSCRIPTEN_RESULT_SUCCESS) {
+ printf("The current fullscreen status is:\n");
+ fullscreenchange_callback(EMSCRIPTEN_EVENT_FULLSCREENCHANGE, &fsce, 0);
+ }
+
+ ret = emscripten_set_fullscreenchange_callback(0, 0, 1, fullscreenchange_callback);
+ TEST_RESULT(emscripten_set_fullscreenchange_callback);
+
+ // These won't do anything, since fullscreen must be requested in an event handler,
+ // but call these anyways to confirm that they don't crash in an exception in the test suite.
+ ret = emscripten_request_fullscreen(0, 1);
+ TEST_RESULT(emscripten_request_fullscreen);
+ ret = emscripten_exit_fullscreen();
+ TEST_RESULT(emscripten_exit_fullscreen);
+
+ EmscriptenPointerlockChangeEvent plce;
+ ret = emscripten_get_pointerlock_status(&plce);
+ TEST_RESULT(emscripten_get_pointerlock_status);
+ if (ret == EMSCRIPTEN_RESULT_SUCCESS) {
+ printf("The current pointerlock status is:\n");
+ pointerlockchange_callback(EMSCRIPTEN_EVENT_POINTERLOCKCHANGE, &plce, 0);
+ }
+
+ ret = emscripten_set_pointerlockchange_callback(0, 0, 1, pointerlockchange_callback);
+ TEST_RESULT(emscripten_set_pointerlockchange_callback);
+
+ // These won't do anything, since pointer lock must be requested in an event handler,
+ // but call these anyways to confirm that they don't crash in an exception in the test suite.
+ ret = emscripten_request_pointerlock(0, 1);
+ TEST_RESULT(emscripten_request_pointerlock);
+ ret = emscripten_exit_pointerlock();
+ TEST_RESULT(emscripten_exit_pointerlock);
+
+ int vibratePattern[] = {
+ 150, 500,
+ 300, 500,
+ 450
+ };
+ ret = emscripten_vibrate_pattern(vibratePattern, sizeof(vibratePattern)/sizeof(vibratePattern[0]));
+ TEST_RESULT(emscripten_vibrate_pattern);
+
+ EmscriptenVisibilityChangeEvent vce;
+ ret = emscripten_get_visibility_status(&vce);
+ TEST_RESULT(emscripten_get_visibility_status);
+ if (ret == EMSCRIPTEN_RESULT_SUCCESS) {
+ printf("Current visibility status:\n");
+ visibilitychange_callback(EMSCRIPTEN_EVENT_VISIBILITYCHANGE, &vce, 0);
+ }
+
+ ret = emscripten_set_visibilitychange_callback(0, 1, visibilitychange_callback);
+ TEST_RESULT(emscripten_set_visibilitychange_callback);
+
+ ret = emscripten_set_touchstart_callback(0, 0, 1, touch_callback);
+ TEST_RESULT(emscripten_set_touchstart_callback);
+ ret = emscripten_set_touchend_callback(0, 0, 1, touch_callback);
+ TEST_RESULT(emscripten_set_touchend_callback);
+ ret = emscripten_set_touchmove_callback(0, 0, 1, touch_callback);
+ TEST_RESULT(emscripten_set_touchmove_callback);
+ ret = emscripten_set_touchcancel_callback(0, 0, 1, touch_callback);
+ TEST_RESULT(emscripten_set_touchcancel_callback);
+
+ ret = emscripten_set_gamepadconnected_callback(0, 1, gamepad_callback);
+ TEST_RESULT(emscripten_set_gamepadconnected_callback);
+ ret = emscripten_set_gamepaddisconnected_callback(0, 1, gamepad_callback);
+ TEST_RESULT(emscripten_set_gamepaddisconnected_callback);
+
+ emscripten_set_main_loop(mainloop, 10, 0);
+
+ ret = emscripten_set_beforeunload_callback(0, beforeunload_callback);
+ TEST_RESULT(emscripten_set_beforeunload_callback);
+
+ ret = emscripten_set_batterychargingchange_callback(0, battery_callback);
+ TEST_RESULT(emscripten_set_batterychargingchange_callback);
+ ret = emscripten_set_batterylevelchange_callback(0, battery_callback);
+ TEST_RESULT(emscripten_set_batterylevelchange_callback);
+
+ EmscriptenBatteryEvent bs;
+ ret = emscripten_get_battery_status(&bs);
+ TEST_RESULT(emscripten_get_battery_status);
+ if (ret == EMSCRIPTEN_RESULT_SUCCESS) {
+ printf("Current battery status:\n");
+ battery_callback(EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE, &bs, 0);
+ }
+
+ ret = emscripten_set_webglcontextlost_callback(0, 0, 1, webglcontext_callback);
+ TEST_RESULT(emscripten_set_webglcontextlost_callback);
+ ret = emscripten_set_webglcontextrestored_callback(0, 0, 1, webglcontext_callback);
+ TEST_RESULT(emscripten_set_webglcontextrestored_callback);
+
+ /* For the events to function, one must either call emscripten_set_main_loop or enable Module.noExitRuntime by some other means.
+ Otherwise the application will exit after leaving main(), and the atexit handlers will clean up all event hooks (by design). */
+ EM_ASM(Module['noExitRuntime'] = true);
+
+#ifdef REPORT_RESULT
+ // Keep the page running for a moment.
+ emscripten_async_call(report_result, 0, 5000);
+#endif
+ return 0;
+}
diff --git a/test_html5_fullscreen.c b/test_html5_fullscreen.c
new file mode 100644
index 0000000..0b9f8ac
--- /dev/null
+++ b/test_html5_fullscreen.c
@@ -0,0 +1,257 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+void report_result(int result)
+{
+ if (result == 0) {
+ printf("Test successful!\n");
+ } else {
+ printf("Test failed!\n");
+ }
+#ifdef REPORT_RESULT
+ REPORT_RESULT();
+#endif
+}
+
+static inline const char *emscripten_event_type_to_string(int eventType) {
+ const char *events[] = { "(invalid)", "(none)", "keypress", "keydown", "keyup", "click", "mousedown", "mouseup", "dblclick", "mousemove", "wheel", "resize",
+ "scroll", "blur", "focus", "focusin", "focusout", "deviceorientation", "devicemotion", "orientationchange", "fullscreenchange", "pointerlockchange",
+ "visibilitychange", "touchstart", "touchend", "touchmove", "touchcancel", "gamepadconnected", "gamepaddisconnected", "beforeunload",
+ "batterychargingchange", "batterylevelchange", "webglcontextlost", "webglcontextrestored", "(invalid)" };
+ ++eventType;
+ if (eventType < 0) eventType = 0;
+ if (eventType >= sizeof(events)/sizeof(events[0])) eventType = sizeof(events)/sizeof(events[0])-1;
+ return events[eventType];
+}
+
+const char *emscripten_result_to_string(EMSCRIPTEN_RESULT result) {
+ if (result == EMSCRIPTEN_RESULT_SUCCESS) return "EMSCRIPTEN_RESULT_SUCCESS";
+ if (result == EMSCRIPTEN_RESULT_DEFERRED) return "EMSCRIPTEN_RESULT_DEFERRED";
+ if (result == EMSCRIPTEN_RESULT_NOT_SUPPORTED) return "EMSCRIPTEN_RESULT_NOT_SUPPORTED";
+ if (result == EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED) return "EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED";
+ if (result == EMSCRIPTEN_RESULT_INVALID_TARGET) return "EMSCRIPTEN_RESULT_INVALID_TARGET";
+ if (result == EMSCRIPTEN_RESULT_UNKNOWN_TARGET) return "EMSCRIPTEN_RESULT_UNKNOWN_TARGET";
+ if (result == EMSCRIPTEN_RESULT_INVALID_PARAM) return "EMSCRIPTEN_RESULT_INVALID_PARAM";
+ if (result == EMSCRIPTEN_RESULT_FAILED) return "EMSCRIPTEN_RESULT_FAILED";
+ if (result == EMSCRIPTEN_RESULT_NO_DATA) return "EMSCRIPTEN_RESULT_NO_DATA";
+ return "Unknown EMSCRIPTEN_RESULT!";
+}
+
+#define TEST_RESULT(x) if (ret != EMSCRIPTEN_RESULT_SUCCESS) printf("%s returned %s.\n", #x, emscripten_result_to_string(ret));
+
+// The event handler functions can return 1 to suppress the event and disable the default action. That calls event.preventDefault();
+// Returning 0 signals that the event was not consumed by the code, and will allow the event to pass on and bubble up normally.
+EM_BOOL key_callback(int eventType, const EmscriptenKeyboardEvent *e, void *userData)
+{
+ if (eventType == EMSCRIPTEN_EVENT_KEYPRESS && (!strcmp(e->key, "f") || e->which == 102)) {
+ EmscriptenFullscreenChangeEvent fsce;
+ EMSCRIPTEN_RESULT ret = emscripten_get_fullscreen_status(&fsce);
+ TEST_RESULT(emscripten_get_fullscreen_status);
+ if (!fsce.isFullscreen) {
+ printf("Requesting fullscreen..\n");
+ ret = emscripten_request_fullscreen(0, 1);
+ TEST_RESULT(emscripten_request_fullscreen);
+ } else {
+ printf("Exiting fullscreen..\n");
+ ret = emscripten_exit_fullscreen();
+ TEST_RESULT(emscripten_exit_fullscreen);
+ ret = emscripten_get_fullscreen_status(&fsce);
+ TEST_RESULT(emscripten_get_fullscreen_status);
+ if (fsce.isFullscreen) {
+ fprintf(stderr, "Fullscreen exit did not work!\n");
+ }
+ }
+ }
+ else if (eventType == EMSCRIPTEN_EVENT_KEYPRESS && (!strcmp(e->key, "Esc") || !strcmp(e->key, "Escape") || e->which == 27)) {
+ emscripten_exit_soft_fullscreen();
+ }
+ return 0;
+}
+
+int callCount = 0;
+
+EM_BOOL fullscreenchange_callback(int eventType, const EmscriptenFullscreenChangeEvent *e, void *userData)
+{
+ printf("%s, isFullscreen: %d, fullscreenEnabled: %d, fs element nodeName: \"%s\", fs element id: \"%s\". New size: %dx%d pixels. Screen size: %dx%d pixels.\n",
+ emscripten_event_type_to_string(eventType), e->isFullscreen, e->fullscreenEnabled, e->nodeName, e->id, e->elementWidth, e->elementHeight, e->screenWidth, e->screenHeight);
+
+ ++callCount;
+ if (callCount == 1) { // Transitioned to fullscreen.
+ if (!e->isFullscreen) {
+ report_result(1);
+ }
+ } else if (callCount == 2) { // Transitioned to windowed, we must be back to the default pixel size 300x150.
+ if (e->isFullscreen || e->elementWidth != 300 || e->elementHeight != 150) {
+ report_result(1);
+ } else {
+ report_result(0);
+ }
+ }
+ return 0;
+}
+
+EM_BOOL mouse_callback(int eventType, const EmscriptenMouseEvent *e, void *userData)
+{
+ return 0;
+}
+
+GLuint program;
+
+void draw()
+{
+ int w, h, fs;
+ emscripten_get_canvas_size(&w, &h, &fs);
+ float t = emscripten_get_now() / 1000.0f;
+ float xs = (float)h / w;
+ float ys = 1.0f;
+ float mat[] = { cosf(t) * xs, sinf(t) * ys, 0, 0, -sinf(t) * xs, cosf(t) * ys, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
+ glUniformMatrix4fv(glGetUniformLocation(program, "mat"), 1, 0, mat);
+ glClearColor(0,0,1,1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+}
+
+EM_BOOL on_canvassize_changed(int eventType, const void *reserved, void *userData)
+{
+ int w, h, fs;
+ emscripten_get_canvas_size(&w, &h, &fs);
+ double cssW, cssH;
+ emscripten_get_element_css_size(0, &cssW, &cssH);
+ printf("Canvas resized: WebGL RTT size: %dx%d, canvas CSS size: %02gx%02g\n", w, h, cssW, cssH);
+ return 0;
+}
+
+void requestFullscreen(int scaleMode, int canvasResolutionScaleMode, int filteringMode)
+{
+ EmscriptenFullscreenStrategy s;
+ memset(&s, 0, sizeof(s));
+ s.scaleMode = scaleMode;
+ s.canvasResolutionScaleMode = canvasResolutionScaleMode;
+ s.filteringMode = filteringMode;
+ s.canvasResizedCallback = on_canvassize_changed;
+ EMSCRIPTEN_RESULT ret = emscripten_request_fullscreen_strategy(0, 1, &s);
+ TEST_RESULT(requestFullscreen);
+}
+
+void enterSoftFullscreen(int scaleMode, int canvasResolutionScaleMode, int filteringMode)
+{
+ EmscriptenFullscreenStrategy s;
+ memset(&s, 0, sizeof(s));
+ s.scaleMode = scaleMode;
+ s.canvasResolutionScaleMode = canvasResolutionScaleMode;
+ s.filteringMode = filteringMode;
+ s.canvasResizedCallback = on_canvassize_changed;
+ EMSCRIPTEN_RESULT ret = emscripten_enter_soft_fullscreen(0, &s);
+ TEST_RESULT(enterSoftFullscreen);
+}
+
+int on_button_click(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
+{
+ switch((int)userData)
+ {
+ case 0: requestFullscreen(EMSCRIPTEN_FULLSCREEN_SCALE_DEFAULT, EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE, EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT); break;
+ case 1: requestFullscreen(EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH, EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF, EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT); break;
+ case 2: requestFullscreen(EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH, EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF, EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT); break;
+ case 3: requestFullscreen(EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT, EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF, EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT); break;
+ case 4: requestFullscreen(EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT, EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF, EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT); break;
+ case 5: requestFullscreen(EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH, EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE, EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT); break;
+ case 6: requestFullscreen(EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT, EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE, EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT); break;
+ case 7: requestFullscreen(EMSCRIPTEN_FULLSCREEN_SCALE_CENTER, EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE, EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT); break;
+
+ case 8: enterSoftFullscreen(EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH, EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF, EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT); break;
+ case 9: enterSoftFullscreen(EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH, EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF, EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT); break;
+ case 10: enterSoftFullscreen(EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT, EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF, EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT); break;
+ case 11: enterSoftFullscreen(EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT, EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF, EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT); break;
+ case 12: enterSoftFullscreen(EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH, EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE, EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT); break;
+ case 13: enterSoftFullscreen(EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT, EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE, EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT); break;
+ case 14: enterSoftFullscreen(EMSCRIPTEN_FULLSCREEN_SCALE_CENTER, EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE, EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT); break;
+
+ case 15: requestFullscreen(EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT, EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE, EMSCRIPTEN_FULLSCREEN_FILTERING_NEAREST); break;
+ case 16: enterSoftFullscreen(EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT, EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE, EMSCRIPTEN_FULLSCREEN_FILTERING_NEAREST); break;
+ default: return 0;
+ }
+ return 1;
+}
+
+int main()
+{
+ EmscriptenWebGLContextAttributes attr;
+ emscripten_webgl_init_context_attributes(&attr);
+ attr.alpha = attr.depth = attr.stencil = attr.antialias = attr.preserveDrawingBuffer = attr.preferLowPowerToHighPerformance = attr.failIfMajorPerformanceCaveat = 0;
+ attr.enableExtensionsByDefault = 1;
+ attr.premultipliedAlpha = 0;
+ attr.majorVersion = 1;
+ attr.minorVersion = 0;
+ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(0, &attr);
+ emscripten_webgl_make_context_current(ctx);
+ GLuint vs = glCreateShader(GL_VERTEX_SHADER);
+ const char *vss = "attribute vec4 vPosition; uniform mat4 mat; void main() { gl_Position = mat * vPosition; }";
+ glShaderSource(vs, 1, &vss, 0);
+ glCompileShader(vs);
+ GLuint ps = glCreateShader(GL_FRAGMENT_SHADER);
+ const char *pss = "precision lowp float; uniform vec3 colors[3]; void main() { gl_FragColor = vec4(1,0,0,1); }";
+ glShaderSource(ps, 1, &pss, 0);
+ glCompileShader(ps);
+ program = glCreateProgram();
+ glAttachShader(program, vs);
+ glAttachShader(program, ps);
+ glBindAttribLocation(program, 0, "vPosition");
+ glLinkProgram(program);
+ glUseProgram(program);
+
+ GLuint vbo;
+ glGenBuffers(1, &vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+
+ float verts[] = { 0.0, 0.5, 0.0, -0.5, -0.5, 0.0, 0.5, -0.5, 0.0 };
+ glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
+ glVertexAttribPointer(0, 3, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(0);
+
+ EMSCRIPTEN_RESULT ret = emscripten_set_keypress_callback(0, 0, 1, key_callback);
+ TEST_RESULT(emscripten_set_keypress_callback);
+
+ ret = emscripten_set_fullscreenchange_callback(0, 0, 1, fullscreenchange_callback);
+ TEST_RESULT(emscripten_set_fullscreenchange_callback);
+
+ // For Internet Explorer, fullscreen and pointer lock requests cannot be run
+ // from inside keyboard event handlers. Therefore we must register a callback to
+ // mouse events (any other than mousedown) to activate deferred fullscreen/pointerlock
+ // requests to occur for IE. The callback itself can be a no-op.
+ ret = emscripten_set_click_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_click_callback);
+ ret = emscripten_set_mousedown_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_mousedown_callback);
+ ret = emscripten_set_mouseup_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_mouseup_callback);
+ ret = emscripten_set_dblclick_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_dblclick_callback);
+
+ emscripten_set_click_callback("b0", (void*)0, 1, on_button_click);
+ emscripten_set_click_callback("b1", (void*)1, 1, on_button_click);
+ emscripten_set_click_callback("b2", (void*)2, 1, on_button_click);
+ emscripten_set_click_callback("b3", (void*)3, 1, on_button_click);
+ emscripten_set_click_callback("b4", (void*)4, 1, on_button_click);
+ emscripten_set_click_callback("b5", (void*)5, 1, on_button_click);
+ emscripten_set_click_callback("b6", (void*)6, 1, on_button_click);
+ emscripten_set_click_callback("b7", (void*)7, 1, on_button_click);
+ emscripten_set_click_callback("b8", (void*)8, 1, on_button_click);
+ emscripten_set_click_callback("b9", (void*)9, 1, on_button_click);
+ emscripten_set_click_callback("b10", (void*)10, 1, on_button_click);
+ emscripten_set_click_callback("b11", (void*)11, 1, on_button_click);
+ emscripten_set_click_callback("b12", (void*)12, 1, on_button_click);
+ emscripten_set_click_callback("b13", (void*)13, 1, on_button_click);
+ emscripten_set_click_callback("b14", (void*)14, 1, on_button_click);
+ emscripten_set_click_callback("b15", (void*)15, 1, on_button_click);
+ emscripten_set_click_callback("b16", (void*)16, 1, on_button_click);
+
+ printf("To finish this test, press f to enter fullscreen mode, and then exit it.\n");
+ printf("On IE, press a mouse key over the canvas after pressing f to activate the fullscreen request event.\n");
+
+ emscripten_set_main_loop(draw, 0, 0);
+ return 0;
+}
diff --git a/test_html5_mouse.c b/test_html5_mouse.c
new file mode 100644
index 0000000..01f54de
--- /dev/null
+++ b/test_html5_mouse.c
@@ -0,0 +1,160 @@
+#include
+#include
+#include
+#include
+
+void report_result(int result)
+{
+ if (result == 0) {
+ printf("Test successful!\n");
+ } else {
+ printf("Test failed!\n");
+ }
+#ifdef REPORT_RESULT
+ REPORT_RESULT();
+#endif
+}
+
+static inline const char *emscripten_event_type_to_string(int eventType) {
+ const char *events[] = { "(invalid)", "(none)", "keypress", "keydown", "keyup", "click", "mousedown", "mouseup", "dblclick", "mousemove", "wheel", "resize",
+ "scroll", "blur", "focus", "focusin", "focusout", "deviceorientation", "devicemotion", "orientationchange", "fullscreenchange", "pointerlockchange",
+ "visibilitychange", "touchstart", "touchend", "touchmove", "touchcancel", "gamepadconnected", "gamepaddisconnected", "beforeunload",
+ "batterychargingchange", "batterylevelchange", "webglcontextlost", "webglcontextrestored", "(invalid)" };
+ ++eventType;
+ if (eventType < 0) eventType = 0;
+ if (eventType >= sizeof(events)/sizeof(events[0])) eventType = sizeof(events)/sizeof(events[0])-1;
+ return events[eventType];
+}
+
+const char *emscripten_result_to_string(EMSCRIPTEN_RESULT result) {
+ if (result == EMSCRIPTEN_RESULT_SUCCESS) return "EMSCRIPTEN_RESULT_SUCCESS";
+ if (result == EMSCRIPTEN_RESULT_DEFERRED) return "EMSCRIPTEN_RESULT_DEFERRED";
+ if (result == EMSCRIPTEN_RESULT_NOT_SUPPORTED) return "EMSCRIPTEN_RESULT_NOT_SUPPORTED";
+ if (result == EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED) return "EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED";
+ if (result == EMSCRIPTEN_RESULT_INVALID_TARGET) return "EMSCRIPTEN_RESULT_INVALID_TARGET";
+ if (result == EMSCRIPTEN_RESULT_UNKNOWN_TARGET) return "EMSCRIPTEN_RESULT_UNKNOWN_TARGET";
+ if (result == EMSCRIPTEN_RESULT_INVALID_PARAM) return "EMSCRIPTEN_RESULT_INVALID_PARAM";
+ if (result == EMSCRIPTEN_RESULT_FAILED) return "EMSCRIPTEN_RESULT_FAILED";
+ if (result == EMSCRIPTEN_RESULT_NO_DATA) return "EMSCRIPTEN_RESULT_NO_DATA";
+ return "Unknown EMSCRIPTEN_RESULT!";
+}
+
+#define TEST_RESULT(x) if (ret != EMSCRIPTEN_RESULT_SUCCESS) printf("%s returned %s.\n", #x, emscripten_result_to_string(ret));
+
+int gotClick = 0;
+int gotMouseDown = 0;
+int gotMouseUp = 0;
+int gotDblClick = 0;
+int gotMouseMove = 0;
+int gotWheel = 0;
+
+void instruction()
+{
+ if (!gotClick) { printf("Please click on the canvas.\n"); return; }
+ if (!gotMouseDown) { printf("Please click on the canvas.\n"); return; }
+ if (!gotMouseUp) { printf("Please click on the canvas.\n"); return; }
+ if (!gotDblClick) { printf("Please double-click on the canvas.\n"); return; }
+ if (!gotMouseMove) { printf("Please move the mouse on the canvas.\n"); return; }
+ if (!gotWheel) { printf("Please scroll the mouse wheel.\n"); return; }
+
+ if (gotClick && gotMouseDown && gotMouseUp && gotDblClick && gotMouseMove && gotWheel) report_result(0);
+}
+
+EM_BOOL mouse_callback(int eventType, const EmscriptenMouseEvent *e, void *userData)
+{
+ printf("%s, screen: (%ld,%ld), client: (%ld,%ld),%s%s%s%s button: %hu, buttons: %hu, movement: (%ld,%ld), canvas: (%ld,%ld), target: (%ld, %ld)\n",
+ emscripten_event_type_to_string(eventType), e->screenX, e->screenY, e->clientX, e->clientY,
+ e->ctrlKey ? " CTRL" : "", e->shiftKey ? " SHIFT" : "", e->altKey ? " ALT" : "", e->metaKey ? " META" : "",
+ e->button, e->buttons, e->movementX, e->movementY, e->canvasX, e->canvasY, e->targetX, e->targetY);
+
+ if (e->screenX != 0 && e->screenY != 0 && e->clientX != 0 && e->clientY != 0 && e->canvasX != 0 && e->canvasY != 0 && e->targetX != 0 && e->targetY != 0)
+ {
+ if (eventType == EMSCRIPTEN_EVENT_CLICK) gotClick = 1;
+ if (eventType == EMSCRIPTEN_EVENT_MOUSEDOWN && e->buttons != 0) gotMouseDown = 1;
+ if (eventType == EMSCRIPTEN_EVENT_DBLCLICK) gotDblClick = 1;
+ if (eventType == EMSCRIPTEN_EVENT_MOUSEUP) gotMouseUp = 1;
+ if (eventType == EMSCRIPTEN_EVENT_MOUSEMOVE && (e->movementX != 0 || e->movementY != 0)) gotMouseMove = 1;
+ }
+
+ if (eventType == EMSCRIPTEN_EVENT_CLICK && e->screenX == -500000)
+ {
+ printf("ERROR! Received an event to a callback that should have been unregistered!\n");
+ gotClick = 0;
+ report_result(1);
+ }
+
+ instruction();
+ return 0;
+}
+
+EM_BOOL wheel_callback(int eventType, const EmscriptenWheelEvent *e, void *userData)
+{
+ printf("%s, screen: (%ld,%ld), client: (%ld,%ld),%s%s%s%s button: %hu, buttons: %hu, canvas: (%ld,%ld), target: (%ld, %ld), delta:(%g,%g,%g), deltaMode:%lu\n",
+ emscripten_event_type_to_string(eventType), e->mouse.screenX, e->mouse.screenY, e->mouse.clientX, e->mouse.clientY,
+ e->mouse.ctrlKey ? " CTRL" : "", e->mouse.shiftKey ? " SHIFT" : "", e->mouse.altKey ? " ALT" : "", e->mouse.metaKey ? " META" : "",
+ e->mouse.button, e->mouse.buttons, e->mouse.canvasX, e->mouse.canvasY, e->mouse.targetX, e->mouse.targetY,
+ (float)e->deltaX, (float)e->deltaY, (float)e->deltaZ, e->deltaMode);
+
+ if (e->deltaY > 0.f || e->deltaY < 0.f)
+ gotWheel = 1;
+
+ instruction();
+ return 0;
+}
+
+int main()
+{
+ // Make the canvas area stand out from the background.
+ emscripten_set_canvas_size(400, 300);
+ EM_ASM(Module['canvas'].style.backgroundColor = 'black';);
+
+ EMSCRIPTEN_RESULT ret = emscripten_set_click_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_click_callback);
+ ret = emscripten_set_mousedown_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_mousedown_callback);
+ ret = emscripten_set_mouseup_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_mouseup_callback);
+ ret = emscripten_set_dblclick_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_dblclick_callback);
+ ret = emscripten_set_mousemove_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_mousemove_callback);
+
+ ret = emscripten_set_wheel_callback(0, 0, 1, wheel_callback);
+ TEST_RESULT(emscripten_set_wheel_callback);
+
+#ifdef AUTOMATE_SUCCESS
+ EM_ASM(
+ function sendEvent(type, data) {
+ var event = document.createEvent('Event');
+ event.initEvent(type, true, true);
+ for(var d in data) event[d] = data[d];
+ window.dispatchEvent(event);
+ }
+ sendEvent('click', { screenX: 1, screenY: 1, clientX: 1, clientY: 1, button: 0, buttons: 1 });
+ );
+ // Test that unregistering a callback works. Clicks should no longer be received.
+ ret = emscripten_set_click_callback(0, 0, 1, 0);
+ TEST_RESULT(emscripten_set_click_callback);
+
+ EM_ASM(
+ function sendEvent(type, data) {
+ var event = document.createEvent('Event');
+ event.initEvent(type, true, true);
+ for(var d in data) event[d] = data[d];
+ window.dispatchEvent(event);
+ }
+ sendEvent('click', { screenX: -500000, screenY: -500000, clientX: -500000, clientY: -500000, button: 0, buttons: 0 }); // Send a dummy event that should not be received.
+ sendEvent('mousedown', { screenX: 1, screenY: 1, clientX: 1, clientY: 1, button: 0, buttons: 1 });
+ sendEvent('mouseup', { screenX: 1, screenY: 1, clientX: 1, clientY: 1, button: 0, buttons: 0 });
+ sendEvent('dblclick', { screenX: 1, screenY: 1, clientX: 1, clientY: 1, button: 0, buttons: 0 });
+ sendEvent('mousemove', { screenX: 1, screenY: 1, clientX: 1, clientY: 1, button: 0, buttons: 0, 'movementX': 1, 'movementY': 1 });
+ sendEvent('wheel', { screenX: 1, screenY: 1, clientX: 1, clientY: 1, button: 0, buttons: 0, 'deltaX': 1, 'deltaY': 1, 'deltaZ': 1, 'deltaMode': 1 });
+ sendEvent('mousewheel', { screenX: 1, screenY: 1, clientX: 1, clientY: 1, button: 0, buttons: 0, 'wheelDeltaX': 1, 'wheelDeltaY': 1 });
+ );
+#endif
+
+ /* For the events to function, one must either call emscripten_set_main_loop or enable Module.noExitRuntime by some other means.
+ Otherwise the application will exit after leaving main(), and the atexit handlers will clean up all event hooks (by design). */
+ EM_ASM(Module['noExitRuntime'] = true);
+ return 0;
+}