diff --git a/AUTHORS b/AUTHORS index bb53424e405c5..6144947aac49e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -164,4 +164,8 @@ a license to everyone to use it as detailed in LICENSE.) * Paul Holland * James Long * David Anderson (copyright owned by Mozilla Foundation) +* Eric Rannaud (copyright owned by Nanocritical Corp.) +* William Furr +* Dan Glastonbury (copyright owned by Mozilla Foundation) +* Warren Seine (copyright owned by Aerys SAS) diff --git a/emcc b/emcc index 0e295d2f75b67..41f91840f73c7 100755 --- a/emcc +++ b/emcc @@ -1048,6 +1048,7 @@ try: args = newargs + shared.EMSDK_CXX_OPTS + headers if specified_target: args += ['-o', specified_target] + args = system_libs.process_args(args, shared.Settings) logging.debug("running (for precompiled headers): " + call + ' ' + ' '.join(args)) execute([call] + args) # let compiler frontend print directly, so colors are saved (PIPE kills that) sys.exit(0) @@ -1391,12 +1392,24 @@ try: js_transform_tempfiles.append(final) if DEBUG: save_intermediate('js_opts') else: - for name in js_optimizer_queue: + for i in range(len(js_optimizer_queue)): + name = js_optimizer_queue[i] passes = add_opt_args([name]) if shared.Settings.ASM_JS: passes = ['asm'] + passes + just_split = False + just_concat = False + + # XXX experimental code to emit JSON in intermediate phases + #if i > 0: + # passes = ['receiveJSON'] + passes + # just_split = True + #if i < len(js_optimizer_queue)-1: + # passes += ['emitJSON'] + # just_concat = True + logging.debug('applying js optimization pass: %s', passes) - final = shared.Building.js_optimizer(final, passes, jcache, debug_level >= 4, js_optimizer_extra_info) + final = shared.Building.js_optimizer(final, passes, jcache, debug_level >= 4, js_optimizer_extra_info, just_split=just_split, just_concat=just_concat) js_transform_tempfiles.append(final) save_intermediate(name) js_optimizer_queue_history += js_optimizer_queue diff --git a/emscripten-version.txt b/emscripten-version.txt index 958bb4d7634f3..9f67c6b08c1c4 100644 --- a/emscripten-version.txt +++ b/emscripten-version.txt @@ -1,2 +1,2 @@ -1.26.0 +1.26.1 diff --git a/site/source/_templates/layout.html b/site/source/_templates/layout.html deleted file mode 100644 index ca6d84b674303..0000000000000 --- a/site/source/_templates/layout.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends '!layout.html' %} - -{% block footer %} - - - - -{% endblock %} - diff --git a/site/source/docs/api_reference/bind.h.rst b/site/source/docs/api_reference/bind.h.rst index 12371d08eb84e..fda0e5ff4c48e 100644 --- a/site/source/docs/api_reference/bind.h.rst +++ b/site/source/docs/api_reference/bind.h.rst @@ -11,42 +11,45 @@ Guide documentation for this API can be found in :ref:`Embind`. .. contents:: Table of Contents :local: :depth: 1 - + How to use this API =================== - + Defines -------- +------- .. cpp:namespace: emscripten .. cpp:function:: EMSCRIPTEN_BINDINGS(name) - **HamishW** Confirm this is correct. + **HamishW** Confirm this is correct. + + This define is used to bind C++ classes, functions and other constructs to + JavaScript. It is used differently depending on the construct being mapped + — see the :ref:`embind guide ` for examples. + + :param name: This is a label to mark a group of related bindings (for example ``EMSCRIPTEN_BINDINGS(physics)``, ``EMSCRIPTEN_BINDINGS(components)``, etc.) + + - This define is used to bind C++ classes, functions and other constructs to JavaScript. It is used differently depending on the construct being mapped — see the :ref:`embind guide ` for examples. +.. cpp:type:: sharing_policy - :param name: This is a label to mark a group of related bindings (for example ``EMSCRIPTEN_BINDINGS(physics)``, ``EMSCRIPTEN_BINDINGS(components)``, etc.) - + **HamishW**-Replace with description. Note this is a strongly typed enum. + I can't see better way in Sphinx to represent it. + .. cpp:type:: sharing_policy::NONE -.. cpp:type:: sharing_policy + **HamishW**-Replace with description. - **HamishW**-Replace with description. Note this is a strongly typed enum. I can't see better way in Sphinx to represent it. + .. cpp:type:: sharing_policy::INTRUSIVE - .. cpp:type:: sharing_policy::NONE - - **HamishW**-Replace with description. - - .. cpp:type:: sharing_policy::INTRUSIVE - - **HamishW**-Replace with description. - - .. cpp:type:: sharing_policy::BY_EMVAL - - **HamishW**-Replace with description. + **HamishW**-Replace with description. + + .. cpp:type:: sharing_policy::BY_EMVAL + + **HamishW**-Replace with description. .. _bind-h-policies: @@ -54,52 +57,56 @@ Defines Policies ======== -Currently only :cpp:type:`allow_raw_pointers` policy is supported. Eventually we hope to implement `Boost.Python-like raw pointer policies `_ for managing object ownership. - +Currently only :cpp:type:`allow_raw_pointers` policy is supported. +Eventually we hope to implement `Boost.Python-like raw pointer policies +`_ for managing +object ownership. + .. cpp:type:: arg - .. cpp:member:: static int index - - .. code-block:: cpp - - // Prototype - static constexpr int index - - **HamishW** Add description. - + .. cpp:member:: static int index + + .. code-block:: cpp + + // Prototype + static constexpr int index + + **HamishW** Add description. + .. cpp:type:: ret_val - .. cpp:member:: static int index - - .. code-block:: cpp - - // Prototype - static constexpr int index - - **HamishW** Add description. - + .. cpp:member:: static int index + + .. code-block:: cpp + + // Prototype + static constexpr int index + + **HamishW** Add description. + .. cpp:type:: allow_raw_pointers - This policy is used to whitelist raw pointers. + This policy is used to whitelist raw pointers. + + .. cpp:type:: Transform::type + + **HamishW** Add description. - .. cpp:type:: Transform::type - **HamishW** Add description. - - .. cpp:type:: allow_raw_pointer - .. code-block: cpp - - // Prototype - template - struct allow_raw_pointer : public allow_raw_pointers + .. code-block: cpp + + // Prototype + template + struct allow_raw_pointer : public allow_raw_pointers + + **HamishW** Add description. Note from source: "This type is temporary, + it will be changed when arg policies are reworked" - **HamishW** Add description. Note from source: "This type is temporary, it will be changed when arg policies are reworked" - select_overload and select_const ====================================== @@ -109,63 +116,63 @@ select_overload and select_const .. cpp:function:: typename std::add_pointer::type select_overload(typename std::add_pointer::type fn) - .. code-block:: cpp - - // Prototype - template - typename std::add_pointer::type select_overload(typename std::add_pointer::type fn) + .. code-block:: cpp - **HamishW** Add description. + // Prototype + template + typename std::add_pointer::type select_overload(typename std::add_pointer::type fn) - :param typename std\:\:add_pointer::type fn: **HamishW** Add description. - - :returns: **HamishW** Add description. + **HamishW** Add description. + + :param typename std\:\:add_pointer::type fn: **HamishW** Add description. + + :returns: **HamishW** Add description. .. cpp:function:: typename internal::MemberFunctionType::type select_overload() - .. code-block:: cpp - - // Prototype - template - typename internal::MemberFunctionType::type select_overload(Signature (ClassType::*fn)) + .. code-block:: cpp + + // Prototype + template + typename internal::MemberFunctionType::type select_overload(Signature (ClassType::*fn)) + + **HamishW** Add description. + + :param Signature (ClassType::*fn): **HamishW** Add description. - **HamishW** Add description. + :returns: **HamishW** Add description. - :param Signature (ClassType::*fn): **HamishW** Add description. - - :returns: **HamishW** Add description. - .. cpp:function:: auto select_const() - .. code-block:: cpp - - // Prototype - template - auto select_const(ReturnType (ClassType::*method)(Args...) const) + .. code-block:: cpp - **HamishW** Add description. + // Prototype + template + auto select_const(ReturnType (ClassType::*method)(Args...) const) - :param ReturnType (ClassType::*method)(Args...) const: **HamishW** Add description. - - :returns: **HamishW** Add description. + **HamishW** Add description. + + :param ReturnType (ClassType::*method)(Args...) const: **HamishW** Add description. + + :returns: **HamishW** Add description. .. cpp:function:: typename internal::CalculateLambdaSignature::type optional_override(const LambdaType& fp) - .. code-block:: cpp - - // Prototype - template - typename internal::CalculateLambdaSignature::type optional_override(const LambdaType& fp) + .. code-block:: cpp + + // Prototype + template + typename internal::CalculateLambdaSignature::type optional_override(const LambdaType& fp) + + **HamishW** Add description. + + :param const LambdaType& fp: **HamishW** Add description. - **HamishW** Add description. + :returns: **HamishW** Add description. - :param const LambdaType& fp: **HamishW** Add description. - - :returns: **HamishW** Add description. - Functions @@ -173,54 +180,56 @@ Functions .. cpp:function:: void* __getDynamicPointerType(void* p) - **HamishW** Add description. + **HamishW** Add description. + + :param void* p: **HamishW** Add description. + :returns: **HamishW** Add description. - :param void* p: **HamishW** Add description. - :returns: **HamishW** Add description. - .. cpp:function:: void* __getDynamicPointerType(void* p) - **HamishW** Add description. + **HamishW** Add description. + + :param void* p: **HamishW** Add description. + :returns: **HamishW** Add description. - :param void* p: **HamishW** Add description. - :returns: **HamishW** Add description. - .. cpp:function:: void function() - - .. code-block:: cpp - - //prototype - template - void function(const char* name, ReturnType (*fn)(Args...), Policies...) - Registers a function to export to JavaScript. This is called from within an :cpp:func:`EMSCRIPTEN_BINDINGS` block. - - For example to export the function ``lerp()`` - - .. code:: cpp + .. code-block:: cpp + + //prototype + template + void function(const char* name, ReturnType (*fn)(Args...), Policies...) + + Registers a function to export to JavaScript. This is called from within + an :cpp:func:`EMSCRIPTEN_BINDINGS` block. + + For example to export the function ``lerp()`` - // quick_example.cpp - #include + .. code:: cpp - using namespace emscripten; + // quick_example.cpp + #include - float lerp(float a, float b, float t) { - return (1 - t) * a + t * b; - } + using namespace emscripten; - EMSCRIPTEN_BINDINGS(my_module) { - function("lerp", &lerp); - } + float lerp(float a, float b, float t) { + return (1 - t) * a + t * b; + } - - - **HamishW** Check description. Note that Sphinx could not cope with the prototype, so have moved it into the body above. + EMSCRIPTEN_BINDINGS(my_module) { + function("lerp", &lerp); + } - :param const char* name: The name of the function to export (e.g. ``"lerp"``) **HamishW** Check description. - :param ReturnType (\*fn)(Args...): Function pointer address for the exported function (e.g. ``&lerp``). - :param Policies...: |policies-argument| + + + **HamishW** Check description. Note that Sphinx could not cope with the + prototype, so have moved it into the body above. + + :param const char* name: The name of the function to export (e.g. ``"lerp"``) **HamishW** Check description. + :param ReturnType (\*fn)(Args...): Function pointer address for the exported function (e.g. ``&lerp``). + :param Policies...: |policies-argument| @@ -229,109 +238,109 @@ Value tuples .. cpp:class:: value_array : public internal::noncopyable - **HamishW** Add description. - - .. cpp:type:: class_type - - A typedef of ``ClassType``, the typename of the templated type for the class. - - - .. cpp:function:: value_array(const char* name) - - Constructor. **HamishW** Add description. - - :param const char* name: **HamishW** Add description. - - - .. cpp:function:: ~value_array() - - Destructor. **HamishW** Add description. - - - .. cpp:function:: value_array& element(ElementType InstanceType::*field) - - **HamishW** Add description. - - :param ElementType InstanceType::*field: **HamishW** Add description. Note that ``ElementType`` and ``InstanceType`` are typenames (templated types). - :returns: **HamishW** Add description. - - - .. cpp:function:: value_array& element(Getter getter, Setter setter) - - **HamishW** Add description. - - :param Getter getter: **HamishW** Add description. Note that ``Getter`` is a typename (templated type). - :param Setter setter: **HamishW** Add description. Note that ``Setter`` is a typename (templated type). - :returns: **HamishW** Add description. - - - .. cpp:function:: value_array& element(index) - - **HamishW** Add description. - - :param index: **HamishW** Add description. Note that ``Index`` is an integer template parameter. - :returns: **HamishW** Add description. - - - - - + **HamishW** Add description. + + .. cpp:type:: class_type + + A typedef of ``ClassType``, the typename of the templated type for the class. + + + .. cpp:function:: value_array(const char* name) + + Constructor. **HamishW** Add description. + + :param const char* name: **HamishW** Add description. + + + .. cpp:function:: ~value_array() + + Destructor. **HamishW** Add description. + + + .. cpp:function:: value_array& element(ElementType InstanceType::*field) + + **HamishW** Add description. + + :param ElementType InstanceType::*field: **HamishW** Add description. Note that ``ElementType`` and ``InstanceType`` are typenames (templated types). + :returns: **HamishW** Add description. + + + .. cpp:function:: value_array& element(Getter getter, Setter setter) + + **HamishW** Add description. + + :param Getter getter: **HamishW** Add description. Note that ``Getter`` is a typename (templated type). + :param Setter setter: **HamishW** Add description. Note that ``Setter`` is a typename (templated type). + :returns: **HamishW** Add description. + + + .. cpp:function:: value_array& element(index) + + **HamishW** Add description. + + :param index: **HamishW** Add description. Note that ``Index`` is an integer template parameter. + :returns: **HamishW** Add description. + + + + + Value structs ====================================== .. cpp:class:: value_object : public internal::noncopyable - **HamishW** Add description. - - .. cpp:type:: class_type - - A typedef of ``ClassType``, the typename of the templated type for the class. - - - .. cpp:function:: value_object(const char* name) - - Constructor. **HamishW** Add description. - - :param const char* name: **HamishW** Add description. - - - .. cpp:function:: ~value_object() - - Destructor. **HamishW** Add description. - - - .. cpp:function:: value_object& field(const char* fieldName, FieldType InstanceType::*field) - - **HamishW** Add description. - - :param const char* fieldName: **HamishW** Add description. - :param FieldType InstanceType::*field: **HamishW** Add description. - - :returns: **HamishW** Add description. - - - - .. cpp:function:: value_object& field(const char* fieldName, Getter getter, Setter setter) - - **HamishW** Add description. - - :param const char* fieldName: **HamishW** Add description. - :param Getter getter: **HamishW** Add description. Note that ``Getter`` is a typename (templated type). - :param Setter setter: **HamishW** Add description. Note that ``Setter`` is a typename (templated type). - :returns: **HamishW** Add description. - - - .. cpp:function:: value_object& field(const char* fieldName, index) - - **HamishW** Add description. - - :param const char* fieldName: **HamishW** Add description. - :param index: **HamishW** Add description. Note that ``Index`` is an integer template parameter. - :returns: **HamishW** Add description. - - - - + **HamishW** Add description. + + .. cpp:type:: class_type + + A typedef of ``ClassType``, the typename of the templated type for the class. + + + .. cpp:function:: value_object(const char* name) + + Constructor. **HamishW** Add description. + + :param const char* name: **HamishW** Add description. + + + .. cpp:function:: ~value_object() + + Destructor. **HamishW** Add description. + + + .. cpp:function:: value_object& field(const char* fieldName, FieldType InstanceType::*field) + + **HamishW** Add description. + + :param const char* fieldName: **HamishW** Add description. + :param FieldType InstanceType::*field: **HamishW** Add description. + + :returns: **HamishW** Add description. + + + + .. cpp:function:: value_object& field(const char* fieldName, Getter getter, Setter setter) + + **HamishW** Add description. + + :param const char* fieldName: **HamishW** Add description. + :param Getter getter: **HamishW** Add description. Note that ``Getter`` is a typename (templated type). + :param Setter setter: **HamishW** Add description. Note that ``Setter`` is a typename (templated type). + :returns: **HamishW** Add description. + + + .. cpp:function:: value_object& field(const char* fieldName, index) + + **HamishW** Add description. + + :param const char* fieldName: **HamishW** Add description. + :param index: **HamishW** Add description. Note that ``Index`` is an integer template parameter. + :returns: **HamishW** Add description. + + + + Smart pointers ====================================== @@ -339,113 +348,113 @@ Smart pointers .. cpp:type:: default_smart_ptr_trait - .. code-block:: cpp - - //prototype - template - struct default_smart_ptr_trait - - **HamishW** Add description. - - .. cpp:function:: static sharing_policy get_sharing_policy() - - **HamishW** Add description. - - :returns: **HamishW** Add description. - - - .. cpp:function:: static void* share(void* v) - - **HamishW** Add description. - - :param void* v: **HamishW** Add description. - :returns: **HamishW** Add description. - - - .. cpp:function:: static PointerType* construct_null() - - **HamishW** Add description. - - :returns: **HamishW** Add description. Note that the ``PointerType`` returned is a typename (templated type). + .. code-block:: cpp + + //prototype + template + struct default_smart_ptr_trait + + **HamishW** Add description. + + .. cpp:function:: static sharing_policy get_sharing_policy() + + **HamishW** Add description. + + :returns: **HamishW** Add description. + + + .. cpp:function:: static void* share(void* v) + + **HamishW** Add description. + + :param void* v: **HamishW** Add description. + :returns: **HamishW** Add description. + + + .. cpp:function:: static PointerType* construct_null() + + **HamishW** Add description. + + :returns: **HamishW** Add description. Note that the ``PointerType`` returned is a typename (templated type). .. cpp:type:: smart_ptr_trait - .. code-block:: cpp - - //prototype - template - struct smart_ptr_trait : public default_smart_ptr_trait - - **HamishW** Add description. Note from source is: // specialize if you have a different pointer type - - .. cpp:type:: PointerType::element_type element_type - - .. code-block:: cpp - - //prototype - typedef typename PointerType::element_type element_type; - - - **HamishW** Add description. A typedef for the PointerType::element_type, where ``PointerType`` is a typename (templated type). - - - .. cpp:function:: static element_type* get(const PointerType& ptr) - - **HamishW** Add description. - - :param const PointerType& ptr: **HamishW** Add description. Note that ``PointerType`` is a typename (templated type) - :returns: **HamishW** Add description. - - + .. code-block:: cpp + + //prototype + template + struct smart_ptr_trait : public default_smart_ptr_trait + + **HamishW** Add description. Note from source is: // specialize if you have a different pointer type + + .. cpp:type:: PointerType::element_type element_type + + .. code-block:: cpp + + //prototype + typedef typename PointerType::element_type element_type; + + + **HamishW** Add description. A typedef for the PointerType::element_type, where ``PointerType`` is a typename (templated type). + + + .. cpp:function:: static element_type* get(const PointerType& ptr) + + **HamishW** Add description. + + :param const PointerType& ptr: **HamishW** Add description. Note that ``PointerType`` is a typename (templated type) + :returns: **HamishW** Add description. + + .. cpp:type:: smart_ptr_trait> - .. code-block:: cpp - - //prototype - template - struct smart_ptr_trait> - - **HamishW** Add description. - - .. cpp:type:: PointerType - - **HamishW** Add description. A typedef to std::shared_ptr, where ``PointeeType`` is a typename (templated type). - - .. cpp:type:: element_type - - **HamishW** Add description. A typedef for the ``PointerType::element_type``. - - - .. cpp:function:: static element_type* get(const PointerType& ptr) - - **HamishW** Add description. - - :param const PointerType& ptr: **HamishW** Add description. - :returns: **HamishW** Add description. - - .. cpp:function:: static sharing_policy get_sharing_policy() - - **HamishW** Add description. - - :returns: **HamishW** Add description. - - - .. cpp:function:: static std::shared_ptr* share(PointeeType* p, internal::EM_VAL v) - - **HamishW** Add description. - - :param PointeeType* p: **HamishW** Add description. Note that ``PointeeType`` is a typename (templated type). - :param internal::EM_VAL v: **HamishW** Add description. - :returns: **HamishW** Add description. - - .. cpp:function:: static PointerType* construct_null() - - **HamishW** Add description. - - :returns: **HamishW** Add description. + .. code-block:: cpp + + //prototype + template + struct smart_ptr_trait> + + **HamishW** Add description. + + .. cpp:type:: PointerType + + **HamishW** Add description. A typedef to std::shared_ptr, where ``PointeeType`` is a typename (templated type). + + .. cpp:type:: element_type + + **HamishW** Add description. A typedef for the ``PointerType::element_type``. + + + .. cpp:function:: static element_type* get(const PointerType& ptr) + + **HamishW** Add description. + + :param const PointerType& ptr: **HamishW** Add description. + :returns: **HamishW** Add description. + + .. cpp:function:: static sharing_policy get_sharing_policy() + + **HamishW** Add description. + + :returns: **HamishW** Add description. + + + .. cpp:function:: static std::shared_ptr* share(PointeeType* p, internal::EM_VAL v) + + **HamishW** Add description. + + :param PointeeType* p: **HamishW** Add description. Note that ``PointeeType`` is a typename (templated type). + :param internal::EM_VAL v: **HamishW** Add description. + :returns: **HamishW** Add description. + + .. cpp:function:: static PointerType* construct_null() + + **HamishW** Add description. + + :returns: **HamishW** Add description. **HamishW** Note, did not include private class val_deleter. I am assuming all private classes are internal. Delete this Chad when read! @@ -459,444 +468,447 @@ Classes .. cpp:class:: class wrapper : public T, public internal::WrapperBase - .. code-block:: cpp - - //prototype - template - class wrapper : public T, public internal::WrapperBase - - **HamishW** Add description. - - .. cpp:type:: class_type - - **HamishW** Add description. A typedef of ``T``, the typename of the templated type for the class. - - - .. cpp:function:: wrapper(val&& wrapped, Args&&... args) - - .. code-block:: cpp - - //prototype - template - explicit wrapper(val&& wrapped, Args&&... args) - : T(std::forward(args)...) - , wrapped(std::forward(wrapped)) - - Constructor. **HamishW** Add description. - - :param val&& wrapped: **HamishW** Add description. - :param Args&&... args: **HamishW** Add description. Note that ``Args`` is a typename (templated type). - :returns: **HamishW** Add description. - - - .. cpp:function:: ~wrapper() - - Destructor. **HamishW** Add description. - - - .. cpp:function:: ReturnType call(const char* name, Args&&... args) const - - Constructor. **HamishW** Add description. - - :param const char* name: **HamishW** Add description. - :param Args&&... args: **HamishW** Add description. Note that ``Args`` is a typename (templated type). - :returns: **HamishW** Add description. Note that ``ReturnType`` is a typename (templated type). - - -.. cpp:function:: EMSCRIPTEN_WRAPPER(T) - - **HamishW** Add description. Note that this is actually a define, but I've implemented it as a function, because that is how it behaves, and it allows me to have the T as shown, which isn't possible on Sphinx type declaration. - - :param T: **HamishW** Add description. + .. code-block:: cpp + + //prototype + template + class wrapper : public T, public internal::WrapperBase + + **HamishW** Add description. + + .. cpp:type:: class_type + + **HamishW** Add description. A typedef of ``T``, the typename of the templated type for the class. + + + .. cpp:function:: wrapper(val&& wrapped, Args&&... args) + + .. code-block:: cpp + + //prototype + template + explicit wrapper(val&& wrapped, Args&&... args) + : T(std::forward(args)...) + , wrapped(std::forward(wrapped)) + + Constructor. **HamishW** Add description. + + :param val&& wrapped: **HamishW** Add description. + :param Args&&... args: **HamishW** Add description. Note that ``Args`` is a typename (templated type). + :returns: **HamishW** Add description. + + + .. cpp:function:: ~wrapper() + + Destructor. **HamishW** Add description. + + + .. cpp:function:: ReturnType call(const char* name, Args&&... args) const + + Constructor. **HamishW** Add description. + + :param const char* name: **HamishW** Add description. + :param Args&&... args: **HamishW** Add description. Note that ``Args`` is a typename (templated type). + :returns: **HamishW** Add description. Note that ``ReturnType`` is a typename (templated type). + + +.. cpp:function:: EMSCRIPTEN_WRAPPER(T) + + **HamishW** Add description. Note that this is actually a define, but I've implemented it as a function, because that is how it behaves, and it allows me to have the T as shown, which isn't possible on Sphinx type declaration. + + :param T: **HamishW** Add description. .. cpp:type:: base - **HamishW** Add description. - - .. cpp:type:: class_type - - **HamishW** Add description. A typedef of ``BaseClass``, the typename of the templated type for the class. - - - .. cpp:function:: static void verify() - - **HamishW** Add description. Note, is templated function which takes typename ``ClassType``. - - - .. cpp:function:: static internal::TYPEID get() - - **HamishW** Add description. - - :returns: **HamishW** Add description. - - - - .. cpp:function:: HAMISHW_ HELP_Needed - - **HamishW** I don't understand this C++, so not sure how to document. Putting code here for Chad to advise on how to document - - .. code-block:: cpp - - template - using Upcaster = BaseClass* (*)(ClassType*); - - template - using Downcaster = ClassType* (*)(BaseClass*); - - - .. cpp:function:: static Upcaster getUpcaster() - - .. code-block:: cpp - - //prototype - template - static Upcaster getUpcaster() - - **HamishW** Add description. - - :returns: **HamishW** Add description. - - - .. cpp:function:: static Downcaster getDowncaster() - - .. code-block:: cpp - - //prototype - template - static Downcaster getDowncaster() - - **HamishW** Add description. - - :returns: **HamishW** Add description. - - - .. cpp:function:: static To* convertPointer(From* ptr) - - .. code-block:: cpp - - //prototype - template - static To* convertPointer(From* ptr) - - **HamishW** Add description. - - :param From* ptr: **HamishW** Add description. - :returns: **HamishW** Add description. - - + **HamishW** Add description. + + .. cpp:type:: class_type + + **HamishW** Add description. A typedef of ``BaseClass``, the typename of the templated type for the class. + + + .. cpp:function:: static void verify() + + **HamishW** Add description. Note, is templated function which takes typename ``ClassType``. + + + .. cpp:function:: static internal::TYPEID get() + + **HamishW** Add description. + + :returns: **HamishW** Add description. + + + + .. cpp:function:: HAMISHW_ HELP_Needed + + **HamishW** I don't understand this C++, so not sure how to document. Putting code here for Chad to advise on how to document + + .. code-block:: cpp + + template + using Upcaster = BaseClass* (*)(ClassType*); + + template + using Downcaster = ClassType* (*)(BaseClass*); + + + .. cpp:function:: static Upcaster getUpcaster() + + .. code-block:: cpp + + //prototype + template + static Upcaster getUpcaster() + + **HamishW** Add description. + + :returns: **HamishW** Add description. + + + .. cpp:function:: static Downcaster getDowncaster() + + .. code-block:: cpp + + //prototype + template + static Downcaster getDowncaster() + + **HamishW** Add description. + + :returns: **HamishW** Add description. + + + .. cpp:function:: static To* convertPointer(From* ptr) + + .. code-block:: cpp + + //prototype + template + static To* convertPointer(From* ptr) + + **HamishW** Add description. + + :param From* ptr: **HamishW** Add description. + :returns: **HamishW** Add description. + + .. cpp:type:: pure_virtual - **HamishW** Add description. - - .. cpp:type:: Transform - - **HamishW** Add description. Note that this is a templated struct taking typename parameter ``InputType`` and integer ``Index``. - - .. cpp:type:: type - - **HamishW** Add description. This is a typdef to the parent struct typename parameter ``InputType``. + **HamishW** Add description. + + .. cpp:type:: Transform + + **HamishW** Add description. Note that this is a templated struct taking typename parameter ``InputType`` and integer ``Index``. + + .. cpp:type:: type + + **HamishW** Add description. This is a typdef to the parent struct typename parameter ``InputType``. .. cpp:type:: constructor - **HamishW** Add description. Note that this is a template struct taking typename ``... ConstructorArgs``. + **HamishW** Add description. Note that this is a template struct taking typename ``... ConstructorArgs``. + - .. cpp:class:: class_ - **HamishW** Add description. Note that this is a templated class with typename parameters ``ClassType`` and ``BaseSpecifier``. - - .. cpp:type:: class_type - - **HamishW** Add description. A typedef of ``ClassType`` (a typename for the class). - - - .. cpp:type:: base_specifier - - **HamishW** Add description. A typedef of ``BaseSpecifier`` (a typename for the class). - - - .. cpp:type:: HELPNEEDEDHERE - - **HamishW** Don't know what do do with this: :: - - class_() = delete; - - - .. cpp:function:: explicit class_(const char* name) - - .. code-block:: cpp - - //prototype - EMSCRIPTEN_ALWAYS_INLINE explicit class_(const char* name) - - - Constructor. **HamishW** Add description. - - :param const char* name: **HamishW** Add description. - :returns: **HamishW** Add description. - - - .. cpp:function:: const class_& smart_ptr(const char* name) const - - .. code-block:: cpp - - //prototype - template - EMSCRIPTEN_ALWAYS_INLINE const class_& smart_ptr(const char* name) const - - **HamishW** Add description. - - :param const char* name: **HamishW** Add description. - :returns: |class_-function-returns| - - - .. _embind-class-zero-argument-constructor: - - .. cpp:function:: const class_& constructor() const - - .. code-block:: cpp - - //prototype - template - EMSCRIPTEN_ALWAYS_INLINE const class_& constructor(Policies... policies) const - - Zero-argument form of the class constructor. This invokes the natural constructor with the arguments specified in the template. See :ref:`embind-external-constructors` for more information. - - **HamishW** Check description. Note that prototype moved into block as was breaking Sphinx. - - :param Policies... policies: |policies-argument| - :returns: |class_-function-returns| - - .. _embind-class-function-pointer-constructor: - - - .. cpp:function:: const class_& constructor() const - - .. code-block:: cpp - - //prototype - template - EMSCRIPTEN_ALWAYS_INLINE const class_& constructor(ReturnType (*factory)(Args...), Policies...) const - - Class constructor for objects that use a factory function to create the object. See :ref:`embind-external-constructors` for more information. - - :param ReturnType (\*factory)(Args...): The address of the class factory function. - :param Policies... policies: |policies-argument| - :returns: |class_-function-returns| - - - .. cpp:function:: const class_& smart_ptr_constructor() const - - .. code-block:: cpp - - //prototype - template - EMSCRIPTEN_ALWAYS_INLINE const class_& smart_ptr_constructor(const char* smartPtrName, SmartPtr (*factory)(Args...), Policies...) const - - **HamishW** Add description. Note that Sphinx could NOT cope with the prototype, so have pulled it into the body of the text. - - :param const char* smartPtrName: **HamishW** Add description. - :param SmartPtr (\*factory)(Args...): **HamishW** Add description. - :param Policies... policies: |policies-argument| - :returns: |class_-function-returns| - - - .. cpp:function:: const class_& allow_subclass() const - - .. code-block:: cpp - - //prototype - template - EMSCRIPTEN_ALWAYS_INLINE const class_& allow_subclass( - const char* wrapperClassName, - const char* pointerName, - ::emscripten::constructor = ::emscripten::constructor<>() - ) const - - **HamishW** Add description. - - :param const char* wrapperClassName: **HamishW** Add description. - :param const char* pointerName: **HamishW** Add description. - :param ::emscripten::constructor constructor): **HamishW** Add description. - :returns: |class_-function-returns| - - - .. cpp:function:: const class_& allow_subclass() const - - .. code-block:: cpp - - //prototype - template - EMSCRIPTEN_ALWAYS_INLINE const class_& allow_subclass( - const char* wrapperClassName, - ::emscripten::constructor constructor = ::emscripten::constructor<>() - ) const - - **HamishW** Add description. Explain how this constructor differs from other one. - - :param const char* wrapperClassName: **HamishW** Add description. - :param ::emscripten::constructor constructor): **HamishW** Add description. - - :returns: |class_-function-returns| - - - .. cpp:function:: const class_& function() const - - .. code-block:: cpp - - //prototype - template - EMSCRIPTEN_ALWAYS_INLINE const class_& function(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...), Policies...) const - - This method is for declaring a method belonging to a class. - - On the JavaScript side this is a function that gets bound as a property of the prototype. For example ``.function("myClassMember", &MyClass::myClassMember)`` would bind ``myClassMember`` to ``MyClass.prototype.myClassMember`` in the JavaScript. - - **HamishW** Check description. Note prototype moved to "prototype" block above because syntax broke Sphinx. Also explain how this method differs from the other overloads. - - :param const char* methodName: **HamishW** Add description. - :param ReturnType (ClassType::*memberFunction)(Args...): **HamishW** Add description. Note that ``ReturnType`` is a template typename for this function and ``ClassType`` is a template typename for the class. - :param typename... Policies: |policies-argument| - :returns: |class_-function-returns| - - - .. cpp:function:: const class_& function() const - - .. code-block:: cpp - - //prototype - template - EMSCRIPTEN_ALWAYS_INLINE const class_& function(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...) const, Policies...) const - - **HamishW** Add description. Note, prototype moved into block above as it broke Sphinx. Also this only differs by a const on the ReturnType from the previous function - - :param const char* methodName: **HamishW** Add description. - :param ReturnType (ClassType::*memberFunction)(Args...) const: **HamishW** Add description. Note that ``ReturnType`` is a template typename for this function and ``ClassType`` is a template typename for the class. - :param typename... Policies: |policies-argument| - :returns: |class_-function-returns| - - - .. cpp:function:: const class_& function() const - - .. code-block:: cpp - - //prototype - template - EMSCRIPTEN_ALWAYS_INLINE const class_& function(const char* methodName, ReturnType (*function)(ThisType, Args...), Policies...) const - - **HamishW** Add description. Note, prototype moved into block above as it broke Sphinx. - - :param const char* methodName: **HamishW** Add description. - :param ReturnType (\*function)(ThisType, Args...): **HamishW** Add description. - :param typename... Policies: |policies-argument| - :returns: |class_-function-returns| - - - .. cpp:function:: const class_& property() const - - .. code-block:: cpp - - //prototype - template::value>::type> - EMSCRIPTEN_ALWAYS_INLINE const class_& property(const char* fieldName, const FieldType ClassType::*field) const - - **HamishW** Add description. Note, signature copied to prototype block above because proper signature broke Sphinx. Also because it is useful to include the template information. - - :param const char* fieldName: **HamishW** Add description. - :param const FieldType ClassType::*field: **HamishW** Add description. - - :returns: |class_-function-returns| - - - .. cpp:function:: const class_& property(const char* fieldName, FieldType ClassType::*field) const - - .. code-block:: cpp - - //prototype - template::value>::type> - EMSCRIPTEN_ALWAYS_INLINE const class_& property(const char* fieldName, FieldType ClassType::*field) const - - **HamishW** Add description. - - :param const char* fieldName: **HamishW** Add description. - :param FieldType ClassType::*field: **HamishW** Add description. - - :returns: |class_-function-returns| - - - .. cpp:function:: const class_& property(const char* fieldName, Getter getter) const - - .. code-block:: cpp - - //prototype - template - EMSCRIPTEN_ALWAYS_INLINE const class_& property(const char* fieldName, Getter getter) const - - **HamishW** Add description. - - :param const char* fieldName: **HamishW** Add description. - :param Getter getter: **HamishW** Add description. Note that ``Getter`` is a function template typename. - :returns: |class_-function-returns| - - - .. cpp:function:: const class_& property(const char* fieldName, Getter getter, Setter setter) const - - .. code-block:: cpp - - //prototype - template - EMSCRIPTEN_ALWAYS_INLINE const class_& property(const char* fieldName, Getter getter, Setter setter) const - - **HamishW** Add description. Note that this is a function template taking typenames ``Setter`` and ``Getter``: ``template`` - - :param const char* fieldName: **HamishW** Add description. - :param Getter getter: **HamishW** Add description. Note that ``Getter`` is a function template typename. - :param Setter setter: **HamishW** Add description. Note that ``Setter`` is a function template typename. - :returns: |class_-function-returns| - - .. cpp:function:: const class_& class_function() const - - .. code-block:: cpp - - //prototype - template - EMSCRIPTEN_ALWAYS_INLINE const class_& class_function(const char* methodName, ReturnType (*classMethod)(Args...), Policies...) const - - This method is for declaring a static function belonging to a class. - - On the JavaScript side this is a function that gets bound as a property of the constructor. For example ``.class_function("myStaticFunction", &MyClass::myStaticFunction)`` binds ``myStaticFunction`` to ``MyClass.myStaticFunction``. - - **HamishW** Check description. Note prototype moved to "prototype" block above because syntax broke Sphinx. - - :param const char* methodName: **HamishW** Add description. - :param ReturnType (\*classMethod)(Args...): **HamishW** Add description. - :param Policies...: |policies-argument| - :returns: |class_-function-returns| - - - - + **HamishW** Add description. Note that this is a templated class with typename parameters ``ClassType`` and ``BaseSpecifier``. + + .. cpp:type:: class_type + + **HamishW** Add description. A typedef of ``ClassType`` (a typename for the class). + + + .. cpp:type:: base_specifier + + **HamishW** Add description. A typedef of ``BaseSpecifier`` (a typename for the class). + + + .. cpp:type:: HELPNEEDEDHERE + + **HamishW** Don't know what do do with this: :: + + class_() = delete; + + + .. cpp:function:: explicit class_(const char* name) + + .. code-block:: cpp + + //prototype + EMSCRIPTEN_ALWAYS_INLINE explicit class_(const char* name) + + + Constructor. **HamishW** Add description. + + :param const char* name: **HamishW** Add description. + :returns: **HamishW** Add description. + + + .. cpp:function:: const class_& smart_ptr(const char* name) const + + .. code-block:: cpp + + //prototype + template + EMSCRIPTEN_ALWAYS_INLINE const class_& smart_ptr(const char* name) const + + **HamishW** Add description. + + :param const char* name: **HamishW** Add description. + :returns: |class_-function-returns| + + + .. _embind-class-zero-argument-constructor: + + .. cpp:function:: const class_& constructor() const + + .. code-block:: cpp + + //prototype + template + EMSCRIPTEN_ALWAYS_INLINE const class_& constructor(Policies... policies) const + + Zero-argument form of the class constructor. This invokes the natural constructor with the arguments specified in the template. See :ref:`embind-external-constructors` for more information. + + **HamishW** Check description. Note that prototype moved into block as was breaking Sphinx. + + :param Policies... policies: |policies-argument| + :returns: |class_-function-returns| + + .. _embind-class-function-pointer-constructor: + + + .. cpp:function:: const class_& constructor() const + + .. code-block:: cpp + + //prototype + template + EMSCRIPTEN_ALWAYS_INLINE const class_& constructor(ReturnType (*factory)(Args...), Policies...) const + + Class constructor for objects that use a factory function to create the object. See :ref:`embind-external-constructors` for more information. + + :param ReturnType (\*factory)(Args...): The address of the class factory function. + :param Policies... policies: |policies-argument| + :returns: |class_-function-returns| + + + .. cpp:function:: const class_& smart_ptr_constructor() const + + .. code-block:: cpp + + //prototype + template + EMSCRIPTEN_ALWAYS_INLINE const class_& smart_ptr_constructor(const char* smartPtrName, SmartPtr (*factory)(Args...), Policies...) const + + **HamishW** Add description. Note that Sphinx could NOT cope with the prototype, so have pulled it into the body of the text. + + :param const char* smartPtrName: **HamishW** Add description. + :param SmartPtr (\*factory)(Args...): **HamishW** Add description. + :param Policies... policies: |policies-argument| + :returns: |class_-function-returns| + + + .. cpp:function:: const class_& allow_subclass() const + + .. code-block:: cpp + + //prototype + template + EMSCRIPTEN_ALWAYS_INLINE const class_& allow_subclass( + const char* wrapperClassName, + const char* pointerName, + ::emscripten::constructor = ::emscripten::constructor<>() + ) const + + **HamishW** Add description. + + :param const char* wrapperClassName: **HamishW** Add description. + :param const char* pointerName: **HamishW** Add description. + :param ::emscripten::constructor constructor): **HamishW** Add description. + :returns: |class_-function-returns| + + + .. cpp:function:: const class_& allow_subclass() const + + .. code-block:: cpp + + //prototype + template + EMSCRIPTEN_ALWAYS_INLINE const class_& allow_subclass( + const char* wrapperClassName, + ::emscripten::constructor constructor = ::emscripten::constructor<>() + ) const + + **HamishW** Add description. Explain how this constructor differs from other one. + + :param const char* wrapperClassName: **HamishW** Add description. + :param ::emscripten::constructor constructor): **HamishW** Add description. + + :returns: |class_-function-returns| + + + .. cpp:function:: const class_& function() const + + .. code-block:: cpp + + //prototype + template + EMSCRIPTEN_ALWAYS_INLINE const class_& function(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...), Policies...) const + + This method is for declaring a method belonging to a class. + + On the JavaScript side this is a function that gets bound as a property of the prototype. For example ``.function("myClassMember", &MyClass::myClassMember)`` would bind ``myClassMember`` to ``MyClass.prototype.myClassMember`` in the JavaScript. + + **HamishW** Check description. Note prototype moved to "prototype" block above because syntax broke Sphinx. Also explain how this method differs from the other overloads. + + :param const char* methodName: **HamishW** Add description. + :param ReturnType (ClassType::*memberFunction)(Args...): **HamishW** Add description. Note that ``ReturnType`` is a template typename for this function and ``ClassType`` is a template typename for the class. + :param typename... Policies: |policies-argument| + :returns: |class_-function-returns| + + + .. cpp:function:: const class_& function() const + + .. code-block:: cpp + + //prototype + template + EMSCRIPTEN_ALWAYS_INLINE const class_& function(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...) const, Policies...) const + + **HamishW** Add description. Note, prototype moved into block above as it broke Sphinx. Also this only differs by a const on the ReturnType from the previous function + + :param const char* methodName: **HamishW** Add description. + :param ReturnType (ClassType::*memberFunction)(Args...) const: **HamishW** Add description. Note that ``ReturnType`` is a template typename for this function and ``ClassType`` is a template typename for the class. + :param typename... Policies: |policies-argument| + :returns: |class_-function-returns| + + + .. cpp:function:: const class_& function() const + + .. code-block:: cpp + + //prototype + template + EMSCRIPTEN_ALWAYS_INLINE const class_& function(const char* methodName, ReturnType (*function)(ThisType, Args...), Policies...) const + + **HamishW** Add description. Note, prototype moved into block above as it broke Sphinx. + + :param const char* methodName: **HamishW** Add description. + :param ReturnType (\*function)(ThisType, Args...): **HamishW** Add description. + :param typename... Policies: |policies-argument| + :returns: |class_-function-returns| + + + .. cpp:function:: const class_& property() const + + .. code-block:: cpp + + //prototype + template::value>::type> + EMSCRIPTEN_ALWAYS_INLINE const class_& property(const char* fieldName, const FieldType ClassType::*field) const + + **HamishW** Add description. Note, signature copied to prototype block above because proper signature broke Sphinx. Also because it is useful to include the template information. + + :param const char* fieldName: **HamishW** Add description. + :param const FieldType ClassType::*field: **HamishW** Add description. + + :returns: |class_-function-returns| + + + .. cpp:function:: const class_& property(const char* fieldName, FieldType ClassType::*field) const + + .. code-block:: cpp + + //prototype + template::value>::type> + EMSCRIPTEN_ALWAYS_INLINE const class_& property(const char* fieldName, FieldType ClassType::*field) const + + **HamishW** Add description. + + :param const char* fieldName: **HamishW** Add description. + :param FieldType ClassType::*field: **HamishW** Add description. + + :returns: |class_-function-returns| + + + .. cpp:function:: const class_& property(const char* fieldName, Getter getter) const + + .. code-block:: cpp + + //prototype + template + EMSCRIPTEN_ALWAYS_INLINE const class_& property(const char* fieldName, Getter getter) const + + **HamishW** Add description. + + :param const char* fieldName: **HamishW** Add description. + :param Getter getter: **HamishW** Add description. Note that ``Getter`` is a function template typename. + :returns: |class_-function-returns| + + + .. cpp:function:: const class_& property(const char* fieldName, Getter getter, Setter setter) const + + .. code-block:: cpp + + //prototype + template + EMSCRIPTEN_ALWAYS_INLINE const class_& property(const char* fieldName, Getter getter, Setter setter) const + + **HamishW** Add description. Note that this is a function template taking typenames ``Setter`` and ``Getter``: ``template`` + + :param const char* fieldName: **HamishW** Add description. + :param Getter getter: **HamishW** Add description. Note that ``Getter`` is a function template typename. + :param Setter setter: **HamishW** Add description. Note that ``Setter`` is a function template typename. + :returns: |class_-function-returns| + + .. cpp:function:: const class_& class_function() const + + .. code-block:: cpp + + //prototype + template + EMSCRIPTEN_ALWAYS_INLINE const class_& class_function(const char* methodName, ReturnType (*classMethod)(Args...), Policies...) const + + This method is for declaring a static function belonging to a class. + + On the JavaScript side this is a function that gets bound as a property + of the constructor. For example ``.class_function("myStaticFunction", + &MyClass::myStaticFunction)`` binds ``myStaticFunction`` to + ``MyClass.myStaticFunction``. + + **HamishW** Check description. Note prototype moved to "prototype" block above because syntax broke Sphinx. + + :param const char* methodName: **HamishW** Add description. + :param ReturnType (\*classMethod)(Args...): **HamishW** Add description. + :param Policies...: |policies-argument| + :returns: |class_-function-returns| + + + + Vectors ======= .. cpp:function:: class_> register_vector(const char* name) - .. code-block:: cpp - - //prototype - template - class_> register_vector(const char* name) + .. code-block:: cpp - **HamishW** Check description. - - A function to register a ``std::vector``. + //prototype + template + class_> register_vector(const char* name) - :param const char* name: **HamishW** Add description. - :returns: **HamishW** Add description. + **HamishW** Check description. + + A function to register a ``std::vector``. + + :param const char* name: **HamishW** Add description. + :returns: **HamishW** Add description. Maps @@ -904,18 +916,18 @@ Maps .. cpp:function:: class_> register_map(const char* name) - .. code-block:: cpp - - //prototype - template - class_> register_map(const char* name) - - **HamishW** Check description. - - A function to register a ``std::map``. + .. code-block:: cpp + + //prototype + template + class_> register_map(const char* name) + + **HamishW** Check description. + + A function to register a ``std::map``. - :param const char* name: **HamishW** Add description. - :returns: **HamishW** Add description. + :param const char* name: **HamishW** Add description. + :returns: **HamishW** Add description. @@ -925,35 +937,37 @@ Enums .. cpp:class:: enum_ - .. code-block:: cpp - - //prototype - template - class enum_ - - Registers an enum to export to JavaScript. This is called from within an :cpp:func:`EMSCRIPTEN_BINDINGS` block and works with both C++98 enums and C++11 "enum classes". See :ref:`embind-enums` for more information. + .. code-block:: cpp + //prototype + template + class enum_ - .. cpp:type:: enum_type - - **HamishW** Add description. A typedef of ``EnumType`` (a typename for the class). + Registers an enum to export to JavaScript. This is called from within an + :cpp:func:`EMSCRIPTEN_BINDINGS` block and works with both C++98 enums + and C++11 "enum classes". See :ref:`embind-enums` for more information. - .. cpp:function:: enum_(const char* name) + .. cpp:type:: enum_type - Constructor. **HamishW** Add description. + **HamishW** Add description. A typedef of ``EnumType`` (a typename for the class). - :param const char* name: **HamishW** Add description. - :returns: **HamishW** Add description. - - - .. cpp:function:: enum_& value(const char* name, EnumType value) - Registers an enum value. **HamishW** Check description. + .. cpp:function:: enum_(const char* name) - :param const char* name: The name of the enumerated value. - :param EnumType value: The type of the enumerated value. - :returns: A reference to the current object. This allows chaining of multiple enum values in the :cpp:func:`EMSCRIPTEN_BINDINGS` block. + Constructor. **HamishW** Add description. + + :param const char* name: **HamishW** Add description. + :returns: **HamishW** Add description. + + + .. cpp:function:: enum_& value(const char* name, EnumType value) + + Registers an enum value. **HamishW** Check description. + + :param const char* name: The name of the enumerated value. + :param EnumType value: The type of the enumerated value. + :returns: A reference to the current object. This allows chaining of multiple enum values in the :cpp:func:`EMSCRIPTEN_BINDINGS` block. @@ -962,31 +976,32 @@ Constants .. cpp:function:: void constant(const char* name, const ConstantType& v) - .. code-block:: cpp - - //prototype - template - void constant(const char* name, const ConstantType& v) + .. code-block:: cpp + + //prototype + template + void constant(const char* name, const ConstantType& v) + + **HamishW** Check description. + + Registers a constant to export to JavaScript. This is called from within + an :cpp:func:`EMSCRIPTEN_BINDINGS` block. + + .. code:: cpp + + EMSCRIPTEN_BINDINGS(my_constant_example) { + constant("SOME_CONSTANT", SOME_CONSTANT); + } - **HamishW** Check description. - - Registers a constant to export to JavaScript. This is called from within an :cpp:func:`EMSCRIPTEN_BINDINGS` block. - - .. code:: cpp + :param const char* name: The name of the constant. + :param const ConstantType& v: The constant type. This can be any type known to *embind*. - EMSCRIPTEN_BINDINGS(my_constant_example) { - constant("SOME_CONSTANT", SOME_CONSTANT); - } - :param const char* name: The name of the constant. - :param const ConstantType& v: The constant type. This can be any type known to *embind*. - - .. COMMENT (not rendered): Following values are common to many functions, and currently only updated in one place (here). .. COMMENT (not rendered): These can be properly replaced if required either wholesale or on an individual basis. .. |policies-argument| replace:: :ref:`Policy ` for managing raw pointer object ownership. Currently must be :cpp:type:`allow_raw_pointers`. -.. |class_-function-returns| replace:: A ``const`` reference to the current object. This allows chaining of the :cpp:class:`class_` functions that define the binding in the :cpp:func:`EMSCRIPTEN_BINDINGS` block. \ No newline at end of file +.. |class_-function-returns| replace:: A ``const`` reference to the current object. This allows chaining of the :cpp:class:`class_` functions that define the binding in the :cpp:func:`EMSCRIPTEN_BINDINGS` block. diff --git a/site/source/docs/api_reference/html5.h.rst b/site/source/docs/api_reference/html5.h.rst index 690ece59746eb..c025ef654bb91 100644 --- a/site/source/docs/api_reference/html5.h.rst +++ b/site/source/docs/api_reference/html5.h.rst @@ -1011,6 +1011,66 @@ Defines Emscripten `fullscreenchange `_ event. +.. c:macro:: EMSCRIPTEN_FULLSCREEN_SCALE + + An enum-like type which specifies how the Emscripten runtime should treat the CSS size of the target element when displaying it in fullscreen mode via calls to functions + :c:func:`emscripten_request_fullscreen_strategy` and :c:func:`emscripten_enter_soft_fullscreen`. + +.. c:macro:: EMSCRIPTEN_FULLSCREEN_SCALE_DEFAULT + + Specifies that the DOM element should not be resized by Emscripten runtime when transitioning between fullscreen and windowed modes. The browser will be responsible for + scaling the DOM element to the fullscreen size. The proper browser behavior in this mode is to stretch the element to fit the full display ignoring aspect ratio, but at the + time of writing, browsers implement different behavior here. See the discussion at https://github.com/kripken/emscripten/issues/2556 for more information. + +.. c:macro:: EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH + + Specifies that the Emscripten runtime should explicitly stretch the CSS size of the target element to cover the whole screen when trasnsitioning to fullscreen mode. This + will change the aspect ratio of the displayed content. + +.. c:macro:: EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT + + Specifies that the Emscripten runtime should explicitly scale the CSS size of the target element to cover the whole screen, while adding either vertical or horizontal + black letterbox padding to preserve the aspect ratio of the content. The aspect ratio that is used here is the render target size of the canvas element. To change the + desired aspect ratio, call :c:func:`emscripten_set_canvas_size` before entering fullscreen mode. + +.. c:macro:: EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE + + An enum-like type which specifies how the Emscripten runtime should treat the pixel size (render target resolution) of the target canvas element when displaying it in + fullscreen mode via calls to functions :c:func:`emscripten_request_fullscreen_strategy` and :c:func:`emscripten_enter_soft_fullscreen`. To better understand the + underlying distinction between the CSS size of a canvas element versus the render target size of a canvas element, see https://www.khronos.org/webgl/wiki/HandlingHighDPI. + +.. c:macro:: EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE + + Specifies that the Emscripten runtime should not do any changes to the render target resolution of the target canvas element that is displayed in fullscreen mode. Use + this mode when your application is set up to render to a single fixed resolution that cannot be changed under any condition. + +.. c:macro:: EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF + + Specifies that the Emscripten runtime should resize the render target of the canvas element to match 1:1 with the CSS size of the element in fullscreen mode. On high DPI + displays (`window.devicePixelRatio` > 1), the CSS size is not the same as the physical screen resolution of the device. Call :c:func:`emscripten_get_device_pixel_ratio` + to obtain the pixel ratio between CSS pixels and actual device pixels of the screen. Use this mode when you want to render to a pixel resolution that is DPI-independent. + +.. c:macro:: EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF + + Specifies that the Emscripten runtime should resize the canvas render target size to match 1:1 with the physical screen resolution on the device. This corresponds to high + definition displays on retina iOS and other mobile and desktop devices with high DPI. Use this mode to match and render 1:1 to the native display resolution. + +.. c:macro:: EMSCRIPTEN_FULLSCREEN_FILTERING + + An enum-like type that specifies what kind of image filtering algorithm to apply to the element when it is presented in fullscreen mode. + +.. c:macro:: EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT + + Specifies that the image filtering mode should not be changed from the existing setting in the CSS style. + +.. c:macro:: EMSCRIPTEN_FULLSCREEN_FILTERING_NEAREST + + Applies a CSS style to the element that displays the content using a nearest-neighbor image filtering algorithm in fullscreen mode. + +.. c:macro:: EMSCRIPTEN_FULLSCREEN_FILTERING_BILINEAR + + Applies a CSS style to the element that displays the content using a bilinear image filtering algorithm in fullscreen mode. This is the default browser behavior. + Struct ------ @@ -1054,6 +1114,31 @@ Struct The size of the whole screen, in pixels. +.. c:type:: EmscriptenFullscreenStrategy + + The options structure that is passed in to functions :c:func:`emscripten_request_fullscreen_strategy` and :c:func:`emscripten_enter_soft_fullscreen` to configure how the target + element should be displayed in fullscreen mode. + + .. c:member:: EMSCRIPTEN_FULLSCREEN_SCALE scaleMode + + Specifies the rule how the CSS size (the displayed size) of the target element is resized when displayed in fullscreen mode. + + .. c:member:: EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE canvasResolutionScaleMode + + Specifies how the render target size (the pixel resolution) of the target element is adjusted when displayed in fullscreen mode. + + .. c:member:: EMSCRIPTEN_FULLSCREEN_FILTERING filteringMode + + Specifies the image filtering algorithm to apply to the element in fullscreen mode. + + .. c:member:: em_canvasresized_callback_func canvasResizedCallback + + If nonzero, points to a user-provided callback function which will be called whenever either the CSS or the canvas render target size changes. Use this callback to reliably + obtain information about canvas resize events. + + .. c:member:: void *canvasResizedCallbackUserData + + Stores a custom data field which will be passed to all calls to the user-provided callback function. Callback functions ------------------ @@ -1107,6 +1192,8 @@ Functions .. note:: This function can be called anywhere, but for web security reasons its associated *request* can only be raised inside the event handler for a user-generated event (for example a key, mouse or touch press/release). This has implications for porting and the value of ``deferUntilInEventHandler`` — see :ref:`web-security-functions-html5-api` for more information. + .. note:: This function only performs a fullscreen request without changing any parameters of the DOM element that is to be displayed in fullscreen mode. At the time of writing, there are differences in how browsers present elements in fullscreen mode. For more information, read the discussion at https://github.com/kripken/emscripten/issues/2556. To display an element in fullscreen mode in a way that is consistent across browsers, prefer calling the function :c:func:`emscripten_request_fullscreen_strategy` instead. This function is best called only in scenarios where the preconfigured presets defined by :c:func:`emscripten_request_fullscreen_strategy` conflict with the developer's use case in some way. + :param target: |target-parameter-doc| :type target: const char* :param EM_BOOL deferUntilInEventHandler: If ``true`` requests made outside of a user-generated event handler are automatically deferred until the user next presses a keyboard or mouse button. If ``false`` the request will fail if called outside of a user-generated event handler. @@ -1114,15 +1201,32 @@ Functions :returns: :c:data:`EMSCRIPTEN_RESULT_SUCCESS`, or one of the other result values. :rtype: **EMSCRIPTEN_RESULT** +.. c:function:: EMSCRIPTEN_RESULT emscripten_request_fullscreen_strategy(const char *target, EM_BOOL deferUntilInEventHandler, const EmscriptenFullscreenStrategy *fullscreenStrategy) + + Requests the given target element to transition to full screen mode, using a custom presentation mode for the element. This function is otherwise the same as :c:func:`emscripten_request_fullscreen`, but this function adds options to control how resizing and aspect ratio, and ensures that the behavior is consistent across browsers. + + .. note:: This function makes changes to the DOM to satisfy consistent presentation across browsers. These changes have been designed to intrude as little as possible, and the changes are cleared once windowed browsing is restored. If any of these changes are conflicting, see the function :c:func:`emscripten_request_fullscreen` instead, which performs a bare fullscreen request without any modifications to the DOM. + + :param const EmscriptenFullscreenStrategy *fullscreenStrategy: [in] Points to a configuration structure filled by the caller which specifies display options for the fullscreen mode. .. c:function:: EMSCRIPTEN_RESULT emscripten_exit_fullscreen(void) - Returns back to windowed browsing mode. + Returns back to windowed browsing mode from a proper fullscreen mode. + + Do not call this function to attempt to return to windowed browsing mode from a soft fullscreen mode, or vice versa. :returns: :c:data:`EMSCRIPTEN_RESULT_SUCCESS`, or one of the other result values. :rtype: |EMSCRIPTEN_RESULT| +.. c:function:: EMSCRIPTEN_RESULT emscripten_enter_soft_fullscreen(const char *target, const EmscriptenFullscreenStrategy *fullscreenStrategy) + + Enters a "soft" fullscreen mode, where the given target element is displayed in the whole client area of the page and all other elements are hidden, but does not actually request fullscreen mode for the browser. This function is useful in cases where the actual Fullscreen API is not desirable or needed, for example in packaged apps for Firefox OS, where applications essentially already cover the whole screen. + + Pressing the esc button does not automatically exit the soft fullscreen mode. To return to windowed presentation mode, manually call the function :c:func:`emscripten_exit_soft_fullscreen`. + +.. c:function:: EMSCRIPTEN_RESULT emscripten_exit_soft_fullscreen() + Returns back to windowed browsing mode from a soft fullscreen mode. Do not call this function to attempt to return to windowed browsing mode from a real fullscreen mode, or vice versa. Pointerlock =========== diff --git a/site/source/docs/api_reference/trace.h.rst b/site/source/docs/api_reference/trace.h.rst index 24877ca879b75..5aeac6a3240b8 100644 --- a/site/source/docs/api_reference/trace.h.rst +++ b/site/source/docs/api_reference/trace.h.rst @@ -131,6 +131,14 @@ memory, you can annotate the address: emscripten_trace_annotate_address_type(model, "UI::Model"); +Additionally, some applications may want to associate the size +of additional storage with an allocation. This can be done via +:c:func:`emscripten_trace_associate_storage_size`: + +.. code-block:: c + + emscripten_trace_associate_storage_size(mesh, mesh->GetTotalMemoryUsage()); + Overall Memory Usage -------------------- @@ -164,6 +172,49 @@ cause large amounts of memory activity, like loading a new model or game asset, is very useful when analyzing memory usage behavior patterns. +Tasks +----- + +Specific tasks can be recorded and analyzed. A task is typically +a unit of work that is not repeating. It may be suspended or +blocked due to having portions performed asynchronously. + +An example of a task is loading an asset which usually involves +chains of callbacks. + +The application should keep track of task IDs (integers) and +ensure that they are unique. + +The task ID need not be passed to every trace call involving +tasks as most calls operate on the current task. + +Tasks can be started and stopped with: + +.. code-block:: c + + emscripten_trace_task_start(taskID, name); + emscripten_trace_task_end(); + +If a task is suspended / blocked, this can be noted via: + +.. code-block:: c + + emscripten_trace_task_suspend("loading via HTTP"); + +And when it is resumed: + +.. code-block:: c + + emscripten_trace_task_resume(taskID, "parsing"); + +It is common to need to associate additional data with the +current task for use when examining task data later. An example +of this would be the URL of an asset that was loaded: + +.. code-block:: c + + emscripten_trace_task_associate_data("url", url); + Reporting Errors ---------------- @@ -371,6 +422,26 @@ Functions stored there. This is used by the server to help breakdown what is in memory. +.. c:function:: void emscripten_trace_associate_storage_size(const void *address, int32_t size) + + :param address: Memory address which should be annotated. + :type address: void* + :param size: Size of the memory associated with this allocation. + :type type: int32_t + :rtype: void + + Associate an amount of additional storage with this address. This + does not represent the size of the allocation itself, but rather + associated memory that should be taken into account when looking + at the size of this object. + + This associated storage is application specific in nature. + + An example is when an object contains a vector or string, you may + want to be aware of that when analyzing memory usage and this + provides a way to let the server be aware of that additional + storage. + .. c:function:: void emscripten_trace_report_memory_layout(void) :rtype: void @@ -401,15 +472,72 @@ Functions The current timestamp is associated with this data. - *This is not yet used on the server side.* - .. c:function:: void emscripten_trace_exit_context(void) :rtype: void The current timestamp is associated with this data. - *This is not yet used on the server side.* +.. c:function:: void emscripten_trace_task_start(int task_id, const char *name); + + :param task_id: Task ID + :type task_id: int + :param name: Task name + :type name: const char* + :rtype: void + + A task is initiated. The task ID should be unique over the lifetime of + the application. It should be managed / tracked by the application. + + The current timestamp is associated with this data. + +.. c:function:: void emscripten_trace_task_associate_data(const char *key, const char *value); + + :param key: Key + :type key: const char* + :param value: Value + :type value: const char* + :rtype: void + + Associate a key / value pair with the current task. + +.. c:function:: void emscripten_trace_task_suspend(const char *explanation); + + :param explanation: Why the task is suspending. + :type explanation: const char* + :rtype: void + + The current task is suspended. + + The explanation should indicate why the task is being suspended + so that this information can be made available when viewing the + task's history. + + The current timestamp is associated with this data. + +.. c:function:: void emscripten_trace_task_resume(int task_id, const char *explanation); + + :param task_id: Task ID + :type task_id: int + :param explanation: Why the task is being resumed. + :type explanation: const char* + :rtype: void + + The task identified by ``task_id`` is resumed and made the current task. + + The explanation should indicate what the task is being resumed to do + so that this information can be made available when viewing the task's + history. + + The current timestamp is associated with this data. + +.. c:function:: void emscripten_trace_task_end(void); + + :rtype: void + + The current task is ended. + + The current timestamp is associated with this data. .. c:function:: void emscripten_trace_close(void) diff --git a/site/source/docs/porting/connecting_cpp_and_javascript/embind.rst b/site/source/docs/porting/connecting_cpp_and_javascript/embind.rst index b34d064a23a8f..1a50a52c86a4a 100644 --- a/site/source/docs/porting/connecting_cpp_and_javascript/embind.rst +++ b/site/source/docs/porting/connecting_cpp_and_javascript/embind.rst @@ -4,24 +4,39 @@ Embind ====== -*Embind* is used to bind C++ functions and classes to JavaScript, so that the compiled code can be used in a natural way by "normal" JavaScript. *Embind* also supports :ref:`calling JavaScript classes from C++ `. - -Embind has support for binding most C++ constructs, including those introduced in C++11 and C++14. Its only significant limitation is that it does not currently support :ref:`raw pointers with complicated lifetime semantics `. - -This article shows how to use :cpp:func:`EMSCRIPTEN_BINDINGS` blocks to create bindings for functions, classes, value types, pointers (including both raw and smart pointers), enums, and constants, and how to create bindings for abstract classes that can be overridden in JavaScript. It also briefly explains how to manage the memory of C++ object handles passed to JavaScript. +*Embind* is used to bind C++ functions and classes to JavaScript, so +that the compiled code can be used in a natural way by "normal" +JavaScript. *Embind* also supports :ref:`calling JavaScript classes +from C++ `. + +Embind has support for binding most C++ constructs, including those +introduced in C++11 and C++14. Its only significant limitation is +that it does not currently support :ref:`raw pointers with complicated +lifetime semantics `. + +This article shows how to use :cpp:func:`EMSCRIPTEN_BINDINGS` blocks to +create bindings for functions, classes, value types, pointers (including +both raw and smart pointers), enums, and constants, and how to create +bindings for abstract classes that can be overridden in JavaScript. It +also briefly explains how to manage the memory of C++ object handles +passed to JavaScript. .. tip:: In addition to the code in this article: - - There are many other examples of how to use *Embind* in the `Test Suite `_. - - `Connecting C++ and JavaScript on the Web with Embind `_ (slides from CppCon 2014) contains more examples and information about *Embind*'s design philosophy and implementation. + - There are many other examples of how to use *Embind* in the `Test Suite`_. + - `Connecting C++ and JavaScript on the Web with Embind>`_ (slides from + CppCon 2014) contains more examples and information about *Embind*'s + design philosophy and implementation. -.. note:: *Embind* was inspired by `Boost.Python `_ and uses a very similar approach for defining bindings. +.. note:: *Embind* was inspired by `Boost.Python`_ and uses a very similar + approach for defining bindings. A quick example =============== -The following code uses an :cpp:func:`EMSCRIPTEN_BINDINGS` block to expose the simple C++ ``lerp()`` :cpp:func:`function` to JavaScript. +The following code uses an :cpp:func:`EMSCRIPTEN_BINDINGS` block to expose +the simple C++ ``lerp()`` :cpp:func:`function` to JavaScript. .. code:: cpp @@ -38,9 +53,13 @@ The following code uses an :cpp:func:`EMSCRIPTEN_BINDINGS` block to expose the s function("lerp", &lerp); } -To compile the above example using *embind*, we invoke *emcc* with the :ref:`bind ` option: ``emcc --bind -o quick_example.js quick_example.cpp``. +To compile the above example using *embind*, we invoke *emcc* with the +:ref:`bind ` option:: + + emcc --bind -o quick_example.js quick_example.cpp -The resulting **quick_example.js** file can be loaded as a node module or via a `` -The code in an :cpp:func:`EMSCRIPTEN_BINDINGS` block runs when the JavaScript file is initially loaded (at the same time as the global constructors). The function ``lerp()``'s parameter types and return type are automatically inferred by *embind*. +The code in an :cpp:func:`EMSCRIPTEN_BINDINGS` block runs when the JavaScript +file is initially loaded (at the same time as the global constructors). The +function ``lerp()``'s parameter types and return type are automatically +inferred by *embind*. -All symbols exposed by *embind* are available on the Emscripten ``Module`` object. +All symbols exposed by *embind* are available on the Emscripten ``Module`` +object. -.. important:: Always access objects through the :ref:`module` object, as shown above. +.. important:: Always access objects through the :ref:`module` object, as + shown above. + + While the objects are also available in the global namespace by default, + there are cases where they will not be (for example, if you use the + :term:`closure compiler` to minify code or wrap compiled code in a + function to avoid polluting the global namespace). You can of course + use whatever name you like for the module by assigning it to a new + variable: ``var MyModuleName = Module;``. - While the objects are also available in the global namespace by default, there are cases where they will not be (for example, if you use the :term:`closure compiler` to minify code or wrap compiled code in a function to avoid polluting the global namespace). You can of course use whatever name you like for the module by assigning it to a new variable: ``var MyModuleName = Module;``. - Classes ======= -Exposing classes to JavaScript requires a more complicated binding statement. For example: +Exposing classes to JavaScript requires a more complicated binding statement. +For example: .. code:: cpp - class MyClass { - public: - MyClass(int x, std::string y) - : x(x) - , y(y) - {} - - void incrementX() { - ++x; - } - - int getX() const { return x; } - void setX(int x_) { x = x_; } - - static std::string getStringFromInstance(const MyClass& instance) { - return instance.y; - } - - private: - int x; - std::string y; - }; - - // Binding code - EMSCRIPTEN_BINDINGS(my_class_example) { - class_("MyClass") - .constructor() - .function("incrementX", &MyClass::incrementX) - .property("x", &MyClass::getX, &MyClass::setX) - .class_function("getStringFromInstance", &MyClass::getStringFromInstance) - ; - } - -The binding block defines a chain of member function calls on the temporary :cpp:class:`class_` object (this same style is used in *Boost.Python*). The functions register the class, its :cpp:func:`~class_::constructor`, member :cpp:func:`~class_::function`, :cpp:func:`~class_::class_function` (static) and :cpp:func:`~class_::property`. - -.. note:: This binding block binds the class and all its methods. As a rule you should bind only those items that are actually needed, as each binding increases the code size. For example, it would be rare to bind private/internal methods. - -An instance of ``MyClass`` can then be created and used in JavaScript as shown below: + class MyClass { + public: + MyClass(int x, std::string y) + : x(x) + , y(y) + {} + + void incrementX() { + ++x; + } + + int getX() const { return x; } + void setX(int x_) { x = x_; } + + static std::string getStringFromInstance(const MyClass& instance) { + return instance.y; + } + + private: + int x; + std::string y; + }; + + // Binding code + EMSCRIPTEN_BINDINGS(my_class_example) { + class_("MyClass") + .constructor() + .function("incrementX", &MyClass::incrementX) + .property("x", &MyClass::getX, &MyClass::setX) + .class_function("getStringFromInstance", &MyClass::getStringFromInstance) + ; + } + +The binding block defines a chain of member function calls on the temporary +:cpp:class:`class_` object (this same style is used in *Boost.Python*). The +functions register the class, its :cpp:func:`~class_::constructor`, member +:cpp:func:`~class_::function`, :cpp:func:`~class_::class_function` (static) +and :cpp:func:`~class_::property`. + +.. note:: This binding block binds the class and all its methods. As a rule + you should bind only those items that are actually needed, as each binding + increases the code size. For example, it would be rare to bind private or + internal methods. + +An instance of ``MyClass`` can then be created and used in JavaScript as +shown below: .. code:: javascript - var instance = new Module.MyClass(10, "hello"); - instance.incrementX(); - instance.x; // 12 - instance.x = 20; // 20 - Module.MyClass.getStringFromInstance(instance); // "hello" - instance.delete(); + var instance = new Module.MyClass(10, "hello"); + instance.incrementX(); + instance.x; // 12 + instance.x = 20; // 20 + Module.MyClass.getStringFromInstance(instance); // "hello" + instance.delete(); Memory management ================= -JavaScript, specifically ECMA-262 Edition 5.1, does not support `finalizers `_ or weak references with callbacks. Therefore there is no way for Emscripten to automatically call the destructors on C++ objects. +JavaScript, specifically ECMA-262 Edition 5.1, does not support `finalizers`_ +or weak references with callbacks. Therefore there is no way for Emscripten +to automatically call the destructors on C++ objects. -.. warning:: JavaScript code must explicitly delete any C++ object handles it has received, or the Emscripten heap will grow indefinitely. +.. warning:: JavaScript code must explicitly delete any C++ object handles + it has received, or the Emscripten heap will grow indefinitely. .. code:: javascript @@ -138,7 +179,10 @@ JavaScript, specifically ECMA-262 Edition 5.1, does not support `finalizers ` are converted to and from JavaScript Arrays and :cpp:class:`value objects ` are converted to and from JavaScript Objects. +Manual memory management for basic types is onerous, so *embind* provides +support for value types. :cpp:class:`Value arrays ` are +converted to and from JavaScript Arrays and :cpp:class:`value objects +` are converted to and from JavaScript Objects. Consider the example below: @@ -177,7 +221,7 @@ The JavaScript code does not need to worry about lifetime management. var person = Module.findPersonAtLocation([10.2, 156.5]); console.log('Found someone! Their name is ' + person.name + ' and they are ' + person.age + ' years old'); - + Advanced class concepts ======================= @@ -186,7 +230,8 @@ Advanced class concepts Raw pointers ------------ -Because raw pointers have unclear lifetime semantics, *embind* requires their use to be marked with :cpp:type:`allow_raw_pointers`. +Because raw pointers have unclear lifetime semantics, *embind* requires +their use to be marked with :cpp:type:`allow_raw_pointers`. For example: @@ -199,9 +244,12 @@ For example: function("passThrough", &passThrough, allow_raw_pointers()); } -.. note:: +.. note:: - Currently the markup serves only to whitelist smart pointer use, and show that you've thought about the use of the raw pointers. Eventually we hope to implement `Boost.Python-like raw pointer policies `_ for managing object ownership. + Currently the markup serves only to whitelist smart pointer use, and + show that you've thought about the use of the raw pointers. Eventually + we hope to implement `Boost.Python-like raw pointer policies`_ for + managing object ownership. .. _embind-external-constructors: @@ -210,54 +258,63 @@ External constructors There are two ways to specify constructors for a class. -The :ref:`zero-argument template form ` invokes the natural constructor with the arguments specified in the template. For example: +The :ref:`zero-argument template form ` +invokes the natural constructor with the arguments specified in the template. +For example: .. code:: cpp - class MyClass { - public: - MyClass(int, float); - void someFunction(); - }; + class MyClass { + public: + MyClass(int, float); + void someFunction(); + }; - EMSCRIPTEN_BINDINGS(external_constructors) { - class_("MyClass") - .constructor() - .function("someFunction", &MyClass::someFunction) - ; - } + EMSCRIPTEN_BINDINGS(external_constructors) { + class_("MyClass") + .constructor() + .function("someFunction", &MyClass::someFunction) + ; + } -The :ref:`second form of the constructor ` takes a function pointer argument, and is used for classes that construct themselves using a factory function. For example: +The :ref:`second form of the constructor ` +takes a function pointer argument, and is used for classes that construct +themselves using a factory function. For example: .. code:: cpp - class MyClass { - virtual void someFunction() = 0; - }; - MyClass* makeMyClass(int, float); //Factory function. + class MyClass { + virtual void someFunction() = 0; + }; + MyClass* makeMyClass(int, float); //Factory function. - EMSCRIPTEN_BINDINGS(external_constructors) { - class_("MyClass") - .constructor(&makeMyClass, allow_raw_pointers()) - .function("someFunction", &MyClass::someFunction) - ; - } + EMSCRIPTEN_BINDINGS(external_constructors) { + class_("MyClass") + .constructor(&makeMyClass, allow_raw_pointers()) + .function("someFunction", &MyClass::someFunction) + ; + } -The two constructors present *exactly the same interface* for constructing the object in JavaScript. Continuing the example above: +The two constructors present *exactly the same interface* for constructing +the object in JavaScript. Continuing the example above: .. code-block:: cpp - - var instance = new MyClass(10, 15.5); - // instance is backed by a raw pointer to a MyClass in the Emscripten heap + + var instance = new MyClass(10, 15.5); + // instance is backed by a raw pointer to a MyClass in the Emscripten heap Smart pointers -------------- -To manage object lifetime with smart pointers, *embind* must be told about the smart pointer type. +To manage object lifetime with smart pointers, *embind* must be told about +the smart pointer type. -For example, consider managing a class ``C``'s lifetime with ``std::shared_ptr``. The best way to do this is to use :cpp:func:`~class_::smart_ptr_constructor` to register the smart pointer type: +For example, consider managing a class ``C``'s lifetime with +``std::shared_ptr``. The best way to do this is to use +:cpp:func:`~class_::smart_ptr_constructor` to register the +smart pointer type: .. code:: cpp @@ -267,9 +324,11 @@ For example, consider managing a class ``C``'s lifetime with ``std::shared_ptr``. +When an object of this type is constructed (e.g. using ``new Module.C()``) +it returns a ``std::shared_ptr``. -An alternative is to use :cpp:func:`~class_::smart_ptr` in the :cpp:func:`EMSCRIPTEN_BINDINGS` block: +An alternative is to use :cpp:func:`~class_::smart_ptr` in the +:cpp:func:`EMSCRIPTEN_BINDINGS` block: .. code:: cpp @@ -280,7 +339,9 @@ An alternative is to use :cpp:func:`~class_::smart_ptr` in the :cpp:func:`EMSCRI ; } -Using this definition, functions can return ``std::shared_ptr`` or take ``std::shared_ptr`` as arguments, but ``new Module.C()`` would still return a raw pointer. +Using this definition, functions can return ``std::shared_ptr`` or take +``std::shared_ptr`` as arguments, but ``new Module.C()`` would still +return a raw pointer. unique_ptr @@ -291,14 +352,18 @@ unique_ptr Custom smart pointers +++++++++++++++++++++ -To teach *embind* about custom smart pointer templates, you must specialize the :cpp:type:`smart_ptr_trait` template. +To teach *embind* about custom smart pointer templates, you must specialize +the :cpp:type:`smart_ptr_trait` template. Non-member-functions on the JavaScript prototype ------------------------------------------------ -Methods on the JavaScript class prototype can be non-member functions, as long as the instance handle can be converted to the first argument of the non-member function. The classic example is when the function exposed to JavaScript does not exactly match the behavior of a C++ method. +Methods on the JavaScript class prototype can be non-member functions, as +long as the instance handle can be converted to the first argument of the +non-member function. The classic example is when the function exposed to +JavaScript does not exactly match the behavior of a C++ method. .. code:: cpp @@ -323,17 +388,22 @@ Methods on the JavaScript class prototype can be non-member functions, as long a ; } -If JavaScript calls ``Array10.prototype.get`` with an invalid index, it will return ``undefined``. +If JavaScript calls ``Array10.prototype.get`` with an invalid index, it will +return ``undefined``. Deriving from C++ classes in JavaScript --------------------------------------- -If C++ classes have virtual or abstract member functions, it's possible to override them in JavaScript. Because JavaScript has no knowledge of the C++ vtable, *embind* needs a bit of glue code to convert C++ virtual function calls into JavaScript calls. +If C++ classes have virtual or abstract member functions, it's possible to +override them in JavaScript. Because JavaScript has no knowledge of the C++ +vtable, *embind* needs a bit of glue code to convert C++ virtual function +calls into JavaScript calls. Abstract methods ++++++++++++++++ -Let's begin with a simple case: pure virtual functions that must be implemented in JavaScript. +Let's begin with a simple case: pure virtual functions that must be +implemented in JavaScript. .. code:: cpp @@ -351,13 +421,21 @@ Let's begin with a simple case: pure virtual functions that must be implemented EMSCRIPTEN_BINDINGS(interface) { class_("Interface") .function("invoke", &Interface::invoke, pure_virtual()) - .allow_subclass() + .allow_subclass("InterfaceWrapper") ; } -:cpp:func:`~class_::allow_subclass` adds two special methods to the Interface binding: ``extend`` and ``implement``. ``extend`` allows JavaScript to subclass in the style exemplified by `Backbone.js `_. ``implement`` is used when you have a JavaScript object, perhaps provided by the browser or some other library, and you want to use it to implement a C++ interface. +:cpp:func:`~class_::allow_subclass` adds two special methods to the +Interface binding: ``extend`` and ``implement``. ``extend`` allows +JavaScript to subclass in the style exemplified by `Backbone.js`_. +``implement`` is used when you have a JavaScript object, perhaps +provided by the browser or some other library, and you want to +use it to implement a C++ interface. -.. note:: The :cpp:type:`pure_virtual` annotation on the function binding allows JavaScript to throw a helpful error if the JavaScript class does not override ``invoke()``. Otherwise, you may run into confusing errors. +.. note:: The :cpp:type:`pure_virtual` annotation on the function binding + allows JavaScript to throw a helpful error if the JavaScript class + does not override ``invoke()``. Otherwise, you may run into confusing + errors. ``extend`` example @@ -395,12 +473,15 @@ Let's begin with a simple case: pure virtual functions that must be implemented }; var interfaceObject = Module.Interface.implement(x); -Now ``interfaceObject`` can be passed to any function that takes an ``Interface`` pointer or reference. +Now ``interfaceObject`` can be passed to any function that takes an +``Interface`` pointer or reference. Non-abstract virtual methods ++++++++++++++++++++++++++++ -If a C++ class has a non-pure virtual function, it can be overridden — but does not have to be. This requires a slightly different wrapper implementation: +If a C++ class has a non-pure virtual function, it can be overridden — but +does not have to be. This requires a slightly different wrapper +implementation: .. code:: cpp @@ -419,14 +500,16 @@ If a C++ class has a non-pure virtual function, it can be overridden — but doe EMSCRIPTEN_BINDINGS(interface) { class_("Base") - .allow_subclass() + .allow_subclass("BaseWrapper") .function("invoke", optional_override([](Base& self, const std::string& str) { return self.Base::invoke(str); })) ; } -When implementing ``Base`` with a JavaScript object, overriding ``invoke`` is optional. The special lambda binding for invoke is necessary to avoid infinite mutual recursion between the wrapper and JavaScript. +When implementing ``Base`` with a JavaScript object, overriding ``invoke`` is +optional. The special lambda binding for invoke is necessary to avoid infinite +mutual recursion between the wrapper and JavaScript. Base classes ------------ @@ -440,12 +523,15 @@ Base class bindings are defined as shown: class_>("DerivedClass"); } -Any member functions defined on ``BaseClass`` are then accessible to instances of ``DerivedClass``. In addition, any function that accepts an instance of ``BaseClass`` can be given an instance of ``DerivedClass``. +Any member functions defined on ``BaseClass`` are then accessible to +instances of ``DerivedClass``. In addition, any function that accepts +an instance of ``BaseClass`` can be given an instance of ``DerivedClass``. Automatic downcasting +++++++++++++++++++++ -If a C++ class is polymorphic (that is, it has a virtual method), then *embind* supports automatic downcasting of function return values. +If a C++ class is polymorphic (that is, it has a virtual method), then +*embind* supports automatic downcasting of function return values. .. code:: cpp @@ -460,15 +546,21 @@ If a C++ class is polymorphic (that is, it has a virtual method), then *embind* function("getDerivedInstance", &getDerivedInstance, allow_raw_pointers()); } -Calling ``Module.getDerivedInstance`` from JavaScript will return a ``Derived`` instance handle from which all of ``Derived``'s methods are available. +Calling ``Module.getDerivedInstance`` from JavaScript will return a +``Derived`` instance handle from which all of ``Derived``'s methods +are available. -.. note:: *Embind* must understand the fully-derived type for automatic downcasting to work. +.. note:: *Embind* must understand the fully-derived type for automatic + downcasting to work. Overloaded functions ==================== -Constructors and functions can be overloaded on the number of arguments, but *embind* does not support overloading based on type. When specifying an overload, use the :cpp:func:`select_overload` helper function to select the appropriate signature. +Constructors and functions can be overloaded on the number of arguments, +but *embind* does not support overloading based on type. When specifying +an overload, use the :cpp:func:`select_overload` helper function to select +the appropriate signature. .. code:: cpp @@ -487,11 +579,12 @@ Constructors and functions can be overloaded on the number of arguments, but *em } .. _embind-enums: - + Enums ===== -*Embind*'s :cpp:class:`enumeration support ` works with both C++98 enums and C++11 "enum classes". +*Embind*'s :cpp:class:`enumeration support ` works with both C++98 +enums and C++11 "enum classes". .. code:: cpp @@ -516,7 +609,8 @@ Enums ; } -In both cases, JavaScript accesses enumeration values as properties of the type. +In both cases, JavaScript accesses enumeration values as properties of the +type. .. code:: javascript @@ -544,75 +638,85 @@ To expose a C++ :cpp:func:`constant` to JavaScript, simply write: Using ``val`` to transliterate JavaScript to C++ ================================================ -*Embind* provides a C++ class, :cpp:class:`emscripten::val`, which you can use to transliterate JavaScript code to C++. Using ``val`` you can call JavaScript objects from your C++, read and write their properties, or coerce them to C++ values like a ``bool``, ``int``, or ``std::string``. +*Embind* provides a C++ class, :cpp:class:`emscripten::val`, which you can +use to transliterate JavaScript code to C++. Using ``val`` you can call +JavaScript objects from your C++, read and write their properties, or +coerce them to C++ values like a ``bool``, ``int``, or ``std::string``. .. _Using-Web-Audio-API-from-Cpp-with-the-Embind-val-class: - -The example below shows how you can use ``val`` to call the JavaScript `Web Audio API `_ from C++: -.. note:: This example is based on the excellent Web Audio tutorial: `Making sine, square, sawtooth and triangle waves `_ (stuartmemo.com). There is an even simpler example in the :cpp:class:`emscripten::val` documentation. +The example below shows how you can use ``val`` to call the JavaScript +`Web Audio API`_ from C++: + +.. note:: This example is based on the excellent Web Audio tutorial: + `Making sine, square, sawtooth and triangle waves`_ (stuartmemo.com). + There is an even simpler example in the :cpp:class:`emscripten::val` + documentation. First consider the JavaScript below, which shows how to use the API: .. code-block:: javascript - // Get web audio api context - var AudioContext = window.AudioContext || window.webkitAudioContext; - - // Got an AudioContext: Create context and OscillatorNode - var context = new AudioContext(); - var oscillator = context.createOscillator(); - - //Configuring oscillator: set OscillatorNode type and frequency - oscillator.type = 'triangle'; - oscillator.frequency.value = 261.63; // value in hertz - middle C - - //Playing - oscillator.connect(context.destination); - oscillator.start(); - - //All done! - + // Get web audio api context + var AudioContext = window.AudioContext || window.webkitAudioContext; + + // Got an AudioContext: Create context and OscillatorNode + var context = new AudioContext(); + var oscillator = context.createOscillator(); + + // Configuring oscillator: set OscillatorNode type and frequency + oscillator.type = 'triangle'; + oscillator.frequency.value = 261.63; // value in hertz - middle C + + // Playing + oscillator.connect(context.destination); + oscillator.start(); + + // All done! + The code can be transliterated to C++ using ``val``, as shown below: .. code-block:: cpp - #include - #include - #include + #include + #include + #include - using namespace emscripten; + using namespace emscripten; - int main() { - val AudioContext = val::global("AudioContext"); - if (!AudioContext.as()) { - printf("No global AudioContext, trying webkitAudioContext\n"); - AudioContext = val::global("webkitAudioContext"); - } - - printf("Got an AudioContext\n"); - val context = AudioContext.new_(); - val oscillator = context.call("createOscillator"); + int main() { + val AudioContext = val::global("AudioContext"); + if (!AudioContext.as()) { + printf("No global AudioContext, trying webkitAudioContext\n"); + AudioContext = val::global("webkitAudioContext"); + } - printf("Configuring oscillator\n"); - oscillator.set("type", val("triangle")); - oscillator["frequency"].set("value", val(261.63)); // Middle C + printf("Got an AudioContext\n"); + val context = AudioContext.new_(); + val oscillator = context.call("createOscillator"); - printf("Playing\n"); - oscillator.call("connect", context["destination"]); - oscillator.call("start", 0); + printf("Configuring oscillator\n"); + oscillator.set("type", val("triangle")); + oscillator["frequency"].set("value", val(261.63)); // Middle C - printf("All done!\n"); - } + printf("Playing\n"); + oscillator.call("connect", context["destination"]); + oscillator.call("start", 0); -First we use :cpp:func:`~emscripten::val::global` to get the symbol for the global ``AudioContext`` object (or ``webkitAudioContext`` if that does not exist). We then use :cpp:func:`~emscripten::val::new_` to create the context, and from this context we can create an ``oscillator``, :cpp:func:`~emscripten::val::set` it's properties (again using ``val``) and then play the tone. + printf("All done!\n"); + } -The example can be compiled on the Linux/Mac OS X terminal with: +First we use :cpp:func:`~emscripten::val::global` to get the symbol for +the global ``AudioContext`` object (or ``webkitAudioContext`` if that +does not exist). We then use :cpp:func:`~emscripten::val::new_` to create +the context, and from this context we can create an ``oscillator``, +:cpp:func:`~emscripten::val::set` it's properties (again using ``val``) +and then play the tone. -:: +The example can be compiled on the Linux/Mac OS X terminal with:: - ./emcc -O2 -Wall -Werror --bind -o oscillator.html oscillator.cpp + ./emcc -O2 -Wall -Werror --bind -o oscillator.html oscillator.cpp Built-in type conversions @@ -656,7 +760,9 @@ Out of the box, *embind* provides converters for many standard C++ types: | ``emscripten::val`` | anything | +---------------------+-------------------------------------------------+ -For convenience, *embind* provides factory functions to register ``std::vector`` (:cpp:func:`register_vector`) and ``std::map`` (:cpp:func:`register_map`) types: +For convenience, *embind* provides factory functions to register +``std::vector`` (:cpp:func:`register_vector`) and ``std::map`` +(:cpp:func:`register_map`) types: .. code:: cpp @@ -669,7 +775,19 @@ For convenience, *embind* provides factory functions to register ``std::vector string @@ -26,7 +28,7 @@ var LibraryEmVal = { $count_emval_handles__deps: ['$emval_handle_array'], $count_emval_handles: function() { var count = 0; - for (var i = 1; i < emval_handle_array.length; ++i) { + for (var i = 5; i < emval_handle_array.length; ++i) { if (emval_handle_array[i] !== undefined) { ++count; } @@ -69,24 +71,33 @@ var LibraryEmVal = { _emval_register__deps: ['$emval_free_list', '$emval_handle_array', '$init_emval'], _emval_register: function(value) { - var handle = emval_free_list.length ? - emval_free_list.pop() : - emval_handle_array.length; - emval_handle_array[handle] = {refcount: 1, value: value}; - return handle; + switch(value){ + case undefined :{ return 1; } + case null :{ return 2; } + case true :{ return 3; } + case false :{ return 4; } + default:{ + var handle = emval_free_list.length ? + emval_free_list.pop() : + emval_handle_array.length; + + emval_handle_array[handle] = {refcount: 1, value: value}; + return handle; + } + } }, _emval_incref__deps: ['$emval_handle_array'], _emval_incref: function(handle) { - if (handle) { + if (handle > 4) { emval_handle_array[handle].refcount += 1; } }, _emval_decref__deps: ['$emval_free_list', '$emval_handle_array'], _emval_decref: function(handle) { - if (handle && 0 === --emval_handle_array[handle].refcount) { + if (handle > 4 && 0 === --emval_handle_array[handle].refcount) { emval_handle_array[handle] = undefined; emval_free_list.push(handle); } @@ -109,16 +120,6 @@ var LibraryEmVal = { return __emval_register({}); }, - _emval_undefined__deps: ['_emval_register'], - _emval_undefined: function() { - return __emval_register(undefined); - }, - - _emval_null__deps: ['_emval_register'], - _emval_null: function() { - return __emval_register(null); - }, - _emval_new_cstring__deps: ['$getStringOrSymbol', '_emval_register'], _emval_new_cstring: function(v) { return __emval_register(getStringOrSymbol(v)); @@ -222,6 +223,20 @@ var LibraryEmVal = { return returnType['toWireType'](destructors, handle); }, + _emval_equals__deps: ['_emval_register', '$requireHandle'], + _emval_equals: function(first, second ){ + first = requireHandle(first); + second = requireHandle(second); + return first==second; + }, + + _emval_strictly_equals__deps: ['_emval_register', '$requireHandle'], + _emval_strictly_equals: function(first, second ){ + first = requireHandle(first); + second = requireHandle(second); + return first===second; + }, + _emval_call__deps: ['_emval_lookupTypes', '_emval_register', '$requireHandle'], _emval_call: function(handle, argCount, argTypes, argv) { handle = requireHandle(handle); diff --git a/src/library.js b/src/library.js index c27998dff7964..8b37ea26a10b4 100644 --- a/src/library.js +++ b/src/library.js @@ -1461,9 +1461,9 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/000095399/functions/usleep.html // We're single-threaded, so use a busy loop. Super-ugly. var msec = useconds / 1000; - if (ENVIRONMENT_IS_WEB && window['performance'] && window['performance']['now']) { - var start = window['performance']['now'](); - while (window['performance']['now']() - start < msec) { + if ((ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) && self['performance'] && self['performance']['now']) { + var start = self['performance']['now'](); + while (self['performance']['now']() - start < msec) { // Do nothing. } } else { @@ -8565,8 +8565,8 @@ LibraryManager.library = { } } else if (typeof dateNow !== 'undefined') { _emscripten_get_now.actual = dateNow; - } else if (ENVIRONMENT_IS_WEB && window['performance'] && window['performance']['now']) { - _emscripten_get_now.actual = function _emscripten_get_now_actual() { return window['performance']['now'](); }; + } else if ((ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) && self['performance'] && self['performance']['now']) { + _emscripten_get_now.actual = function _emscripten_get_now_actual() { return self['performance']['now'](); }; } else { _emscripten_get_now.actual = Date.now; } @@ -8578,7 +8578,7 @@ LibraryManager.library = { if (ENVIRONMENT_IS_NODE) { return 1; // nanoseconds } else if (typeof dateNow !== 'undefined' || - (ENVIRONMENT_IS_WEB && window['performance'] && window['performance']['now'])) { + ((ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) && self['performance'] && self['performance']['now'])) { return 1000; // microseconds (1/1000 of a millisecond) } else { return 1000*1000; // milliseconds @@ -8590,7 +8590,7 @@ LibraryManager.library = { // return whether emscripten_get_now is guaranteed monotonic; the Date.now // implementation is not :( return ENVIRONMENT_IS_NODE || (typeof dateNow !== 'undefined') || - (ENVIRONMENT_IS_WEB && window['performance'] && window['performance']['now']); + ((ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) && self['performance'] && self['performance']['now']); }, // Returns [parentFuncArguments, functionName, paramListName] diff --git a/src/library_fs.js b/src/library_fs.js index 7c59c79680e60..76f82bf9eab72 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -1068,7 +1068,9 @@ mergeInto(LibraryManager.library, { if (!stream.seekable || !stream.stream_ops.llseek) { throw new FS.ErrnoError(ERRNO_CODES.ESPIPE); } - return stream.stream_ops.llseek(stream, offset, whence); + stream.position = stream.stream_ops.llseek(stream, offset, whence); + stream.ungotten = []; + return stream.position; }, read: function(stream, buffer, offset, length, position) { if (length < 0 || position < 0) { diff --git a/src/library_gl.js b/src/library_gl.js index 670c7954c66f2..65d5e796b95bc 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -22,6 +22,11 @@ var LibraryGL = { shaders: [], vaos: [], contexts: [], +#if USE_WEBGL2 + queries: [], + samplers: [], + transformFeedbacks: [], +#endif #if USES_GL_EMULATION currArrayBuffer: 0, @@ -808,9 +813,11 @@ var LibraryGL = { var handle = GL.getNewId(GL.contexts); var context = { handle: handle, - version: webGLContextAttributes.majorVersion + version: webGLContextAttributes.majorVersion, + GLctx: ctx }; - context.GLctx = ctx; + // Store the created context object so that we can access the context given a canvas without having to pass the parameters again. + ctx.canvas.GLctxObject = context; GL.contexts[handle] = context; if (typeof webGLContextAttributes['webGLContextAttributes'] === 'undefined' || webGLContextAttributes.enableExtensionsByDefault) { GL.initExtensions(context); @@ -832,6 +839,7 @@ var LibraryGL = { deleteContext: function(contextHandle) { if (GL.currentContext === GL.contexts[contextHandle]) GL.currentContext = 0; + if (GL.contexts[contextHandle]) GL.contexts[contextHandle].GLctx.canvas.GLctxObject = undefined; // Make sure the canvas object no longer refers to the context object so there are no GC surprises. GL.contexts[contextHandle] = null; }, @@ -1152,11 +1160,29 @@ var LibraryGL = { glGetTexParameterfv__sig: 'viii', glGetTexParameterfv: function(target, pname, params) { +#if GL_ASSERTIONS + if (!params) { + // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense + // if p == null, issue a GL error to notify user about it. + Module.printErr('GL_INVALID_VALUE in glGetTexParameterfv(target=' + target +', pname=' + pname + ', params=0): Function called with null out pointer!'); + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } +#endif {{{ makeSetValue('params', '0', 'GLctx.getTexParameter(target, pname)', 'float') }}}; }, glGetTexParameteriv__sig: 'viii', glGetTexParameteriv: function(target, pname, params) { +#if GL_ASSERTIONS + if (!params) { + // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense + // if p == null, issue a GL error to notify user about it. + Module.printErr('GL_INVALID_VALUE in glGetTexParameteriv(target=' + target +', pname=' + pname + ', params=0): Function called with null out pointer!'); + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } +#endif {{{ makeSetValue('params', '0', 'GLctx.getTexParameter(target, pname)', 'i32') }}}; }, @@ -1211,6 +1237,15 @@ var LibraryGL = { glGetBufferParameteriv__sig: 'viii', glGetBufferParameteriv: function(target, value, data) { +#if GL_ASSERTIONS + if (!data) { + // GLES2 specification does not specify how to behave if data is a null pointer. Since calling this function does not make sense + // if data == null, issue a GL error to notify user about it. + Module.printErr('GL_INVALID_VALUE in glGetBufferParameteriv(target=' + target + ', value=' + value + ', data=0): Function called with null out pointer!'); + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } +#endif {{{ makeSetValue('data', '0', 'GLctx.getBufferParameter(target, value)', 'i32') }}}; }, @@ -1363,6 +1398,506 @@ var LibraryGL = { GLctx.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, HEAPU8.subarray(data)); }, + + // Framebuffer objects + glBlitFramebuffer__sig: 'viiiiiiiiii', + glBlitFramebuffer: function(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter) { + GLctx.blitFramebuffer(srcX0, srcY0, srxC1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + }, + + glReadBuffer__sig: 'vi', + glReadBuffer: function(src) { + GLctx.readBuffer(src); + }, + + // Queries + glGenQueries__sig: 'vii', + glGenQueries: function(n, ids) { + for (var i = 0; i < n; i++) { + var id = GL.getNewId(GL.queries); + var query = GLctx.createQuery(); + query.name = id; + GL.queries[id] = query; + {{{ makeSetValue('ids', 'i*4', 'id', 'i32') }}}; + } + }, + + glDeleteQueries__sig: 'vii', + glDeleteQueries: function(n, ids) { + for (var i = 0; i < n; i++) { + var id = {{{ makeGetValue('ids', 'i*4', 'i32') }}}; + var query = GL.queries[id]; + if (!query) continue; // GL spec: "unused names in ids are ignored, as is the name zero." + GLctx.deleteQuery(query); + query.name = 0; + GL.queries[id] = null; + } + }, + + glIsQuery__sig: 'ii', + glIsQuery: function(id) { + var query = GL.queries[query]; + if (!query) return 0; + return GLctx.isQuery(query); + }, + + glBeginQuery__sig: 'vii', + glBeginQuery: function(target, id) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.queries, id, 'glBeginQuery', 'id'); +#endif + GLctx.beginQuery(target, id ? GL.queries[id] : null); + }, + + glEndQuery__sig: 'vi', + glEndQuery: function(target) { + GLctx.endQuery(target); + }, + + glGetQueryiv__sig: 'viii', + glGetQueryiv: function(target, pname, params) { +#if GL_ASSERTIONS + if (!params) { + // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense + // if p == null, issue a GL error to notify user about it. + Module.printErr('GL_INVALID_VALUE in glGetQueryiv(target=' + target +', pname=' + pname + ', params=0): Function called with null out pointer!'); + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } +#endif + {{{ makeSetValue('params', '0', 'GLctx.getQuery(target, pname)', 'i32') }}}; + }, + + glGetQueryObjectuiv__sig: 'viii', + glGetQueryObjectuiv: function(id, pname, params) { +#if GL_ASSERTIONS + if (!params) { + // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense + // if p == null, issue a GL error to notify user about it. + Module.printErr('GL_INVALID_VALUE in glGetQueryObjectuiv(id=' + id +', pname=' + pname + ', params=0): Function called with null out pointer!'); + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } + GL.validateGLObjectID(GL.queries, id, 'glGetQueryObjectuiv', 'id'); +#endif + var query = GL.queries[id]; + var param = GLctx.getQueryParameter(query, pname); + var ret; + if (typeof param == 'boolean') { + ret = param ? 1 : 0; + } else { + ret = param; + } + {{{ makeSetValue('params', '0', 'ret', 'i32') }}}; + }, + + // Renderbuffer objects + glRenderbufferStorageMultisample__sig: 'viiiii', + glRenderbufferStorageMultisample: function(target, samples, internalformat, width, height) { + GLctx.renderbufferStorageMultisample(target, samples, internalformat, width, height); + }, + + // Sampler objects + glGenSamplers__sig: 'vii', + glGenSamplers: function(n, samplers) { + for (var i = 0; i < n; i++) { + var id = GL.getNewId(GL.samplers); + var sampler = GLctx.createSampler(); + sampler.name = id; + GL.samplers[id] = sampler; + {{{ makeSetValue('samplers', 'i*4', 'id', 'i32') }}}; + } + }, + + glDeleteSamplers__sig: 'vii', + glDeleteSamplers: function(n, samplers) { + for (var i = 0; i < n; i++) { + var id = {{{ makeGetValue('samplers', 'i*4', 'i32') }}}; + var sampler = GL.samplers[id]; + if (!sampler) continue; + GLctx.deleteSampler(sampler); + sampler.name = 0; + GL.samplers[id] = null; + } + }, + + glIsSampler__sig: 'ii', + glIsSampler: function(id) { + var sampler = GL.samplers[id]; + if (!sampler) return 0; + return GLctx.isSampler(sampler); + }, + + glBindSampler__sig: 'vii', + glBindSampler: function(unit, sampler) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.samplers, sampler, 'glBindSampler', 'sampler'); +#endif + GLctx.bindSampler(unit, sampler ? GL.samplers[sampler] : null); + }, + + glSamplerParameterf__sig: 'viif', + glSamplerParameterf: function(sampler, pname, param) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.samplers, sampler, 'glBindSampler', 'sampler'); +#endif + GLctx.samplerParameterf(sampler ? GL.samplers[sampler] : null, pname, param); + }, + + glSamplerParameteri__sig: 'viii', + glSamplerParameteri: function(sampler, pname, param) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.samplers, sampler, 'glBindSampler', 'sampler'); +#endif + GLctx.samplerParameteri(sampler ? GL.samplers[sampler] : null, pname, param); + }, + + glSamplerParameterfv__sig: 'viii', + glSamplerParameterfv: function(sampler, pname, params) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.samplers, sampler, 'glBindSampler', 'sampler'); +#endif + var param = {{{ makeGetValue('params', '0', 'float') }}}; + GLctx.samplerParameterf(sampler ? GL.samplers[sampler] : null, pname, param); + }, + + glSamplerParameteriv__sig: 'viii', + glSamplerParameteriv: function(sampler, pname, params) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.samplers, sampler, 'glBindSampler', 'sampler'); +#endif + var param = {{{ makeGetValue('params', '0', 'i32') }}}; + GLctx.samplerParameteri(sampler ? GL.samplers[sampler] : null, pname, param); + }, + + glGetSamplerParameterfv__sig: 'viii', + glGetSamplerParameterfv: function(sampler, pname, params) { +#if GL_ASSERTIONS + if (!params) { + // GLES3 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense + // if p == null, issue a GL error to notify user about it. + Module.printErr('GL_INVALID_VALUE in glGetSamplerParameterfv(sampler=' + sampler +', pname=' + pname + ', params=0): Function called with null out pointer!'); + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } +#endif + sampler = GL.samplers[sampler]; + {{{ makeSetValue('params', '0', 'GLctx.getSamplerParameter(sampler, pname)', 'float') }}}; + }, + + glGetSamplerParameteriv__sig: 'viii', + glGetSamplerParameteriv: function(sampler, pname, params) { +#if GL_ASSERTIONS + if (!params) { + // GLES3 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense + // if p == null, issue a GL error to notify user about it. + Module.printErr('GL_INVALID_VALUE in glGetSamplerParameteriv(sampler=' + sampler +', pname=' + pname + ', params=0): Function called with null out pointer!'); + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } +#endif + sampler = GL.samplers[sampler]; + {{{ makeSetValue('params', '0', 'GLctx.getSamplerParameter(sampler, pname)', 'i32') }}}; + }, + + // Transform Feedback + glGenTransformFeedbacks__sig: 'vii', + glGenTransformFeedbacks: function(n, ids) { + for (var i = 0; i < n; i++) { + var id = GL.getNewId(GL.transformFeedbacks); + var transformFeedback = GLctx.createTransformFeedback(); + transformFeedback.name = id; + GL.transformFeedbacks[id] = transformFeedback; + {{{ makeSetValue('ids', 'i*4', 'id', 'i32') }}}; + } + }, + + glDeleteTransformFeedbacks__sig: 'vii', + glDeleteTransformFeedbacks: function(n, ids) { + for (var i = 0; i < n; i++) { + var id = {{{ makeGetValue('ids', 'i*4', 'i32') }}}; + var transformFeedback = GL.transformFeedbacks[id]; + if (!transformFeedback) continue; // GL spec: "unused names in ids are ignored, as is the name zero." + GLctx.deleteTransformFeedback(transformFeedback); + transformFeedback.name = 0; + GL.transformFeedbacks[id] = null; + } + }, + + glIsTransformFeedback__sig: 'ii', + glIsTransformFeedback: function(transformFeedback) { + var transformFeedback = GL.transformFeedbacks[transformFeedback]; + if (!transformFeedback) return 0; + return GLctx.isTransformFeedback(transformFeedback); + }, + + glBindTransformFeedback__sig: 'vii', + glBindTransformFeedback: function(target, id) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.transformFeedbacks, id, 'glBindTransformFeedback', 'id'); +#endif + var transformFeedback = id ? GL.transformFeedbacks[id] : null; + if (id && !transformFeedback) { // Passing an nonexisting or an already deleted id is an error. + GL.recordError(0x0502 /* GL_INVALID_OPERATION */); + return; + } + GLctx.bindTransformFeedback(target, transformFeedback); + }, + + glBeginTransformFeedback__sig: 'vi', + glBeginTransformFeedback: function(primitiveMode) { + GLctx.beginTransformFeedback(primitiveMode); + }, + + glEndTransformFeedback__sig: 'v', + glEndTransformFeedback: function() { + GLctx.endTransformFeedback(); + }, + + glPauseTransformFeedback__sig: 'v', + glPauseTransformFeedback: function() { + GLctx.pauseTransformFeedback(); + }, + + glResumeTransformFeedback__sig: 'v', + glResumeTransformFeedback: function() { + GLctx.resumeTransformFeedback(); + }, + + glTransformFeedbackVaryings__sig: 'viiii', + glTransformFeedbackVaryings: function(program, count, varyings, bufferMode) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glTransformFeedbackVaryings', 'program'); +#endif + program = GL.programs[program]; + var vars = []; + for (var i = 0; i < count; i++) + vars.push(Pointer_stringify({{{ makeGetValue('varyings', 'i*4', 'i32') }}})); + + GLctx.transformFeedbackVaryings(program, vars, bufferMode); + }, + + glGetTransformFeedbackVarying__sig: 'viiiiiii', + glGetTransformFeedbackVarying: function(program, index, bufSize, length, size, type, name) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glGetTransformFeedbackVarying', 'program'); +#endif + program = GL.programs[program]; + var info = GLctx.getTransformFeedbackVarying(program, index); + if (!info) return; // If an error occurred, the return parameters length, size, type and name will be unmodified. + + var infoname = info.name.slice(0, Math.max(0, bufSize - 1)); + if (name && bufSize > 0) { + writeStringToMemory(infoname, name); + if (length) {{{ makeSetValue('length', '0', 'infoname.length', 'i32') }}}; + } else { + if (length) {{{ makeSetValue('length', '0', 0, 'i32') }}}; + } + + if (size) {{{ makeSetValue('size', '0', 'info.size', 'i32') }}}; + if (type) {{{ makeSetValue('type', '0', 'info.type', 'i32') }}}; + }, + + glGetIntegeri_v__sig: 'viii', + glGetIntegeri_v: function(target, index, data) { +#if GL_ASSERTIONS + if (!data) { + // GLES2 specification does not specify how to behave if data is a null pointer. Since calling this function does not make sense + // if data == null, issue a GL error to notify user about it. + Module.printErr('GL_INVALID_VALUE in glGetIntegeri_v(target=' + target + ', index=' + index + ', data=0): Function called with null out pointer!'); + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } +#endif + var result = GLctx.getIndexedParameter(target, index); + var ret; + switch (typeof result) { + case 'boolean': + ret = result ? 1 : 0; + break; + case 'number': + ret = result; + break; + case 'object': + if (result === null) { + switch (target) { + case 0x8C8F: // TRANSFORM_FEEDBACK_BUFFER_BINDING + case 0x8A28: // UNIFORM_BUFFER_BINDING + ret = 0; + break; + default: { + GL.recordError(0x0500); // GL_INVALID_ENUM +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glGetIntegeri_v(' + target + ') and it returns null!'); +#endif + return; + } + } + } else if (result instanceof WebGLBuffer) { + ret = result.name | 0; + } else { + GL.recordError(0x0500); // GL_INVALID_ENUM +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glGetIntegeri_v: Unknown object returned from WebGL getIndexedParameter(' + target + ')!'); +#endif + return; + } + break; + default: + GL.recordError(0x0500); // GL_INVALID_ENUM +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glGetIntegeri_v: Native code calling glGetIntegeri_v(' + target + ') and it returns ' + result + ' of type ' + typeof(result) + '!'); +#endif + return; + } + + {{{ makeSetValue('data', '0', 'ret', 'i32') }}}; + }, + + // Uniform Buffer objects + glBindBufferBase__sig: 'viii', + glBindBufferBase: function(target, index, buffer) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.buffers, buffer, 'glBindBufferBase', 'buffer'); +#endif + var bufferObj = buffer ? GL.buffers[buffer] : null; + GLctx.bindBufferBase(target, index, bufferObj); + }, + + glBindBufferRange__sig: 'viiiii', + glBindBufferRange: function(target, index, buffer, offset, ptrsize) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.buffers, buffer, 'glBindBufferRange', 'buffer'); +#endif + var bufferObj = buffer ? GL.buffers[buffer] : null; + GLctx.bindBufferRange(target, index, bufferObj, offset, ptrsize); + }, + + glGetUniformIndices__sig: 'viiii', + glGetUniformIndices: function(program, uniformCount, uniformNames, uniformIndices) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glGetUniformIndices', 'program'); + if (!uniformIndices) { + // GLES2 specification does not specify how to behave if uniformIndices is a null pointer. Since calling this function does not make sense + // if uniformIndices == null, issue a GL error to notify user about it. + Module.printErr('GL_INVALID_VALUE in glGetUniformIndices(program=' + program + ', uniformCount=' + uniformCount + ', uniformNames=' + uniformNames + ', uniformIndices=0): Function called with null out pointer!'); + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } +#endif + if (uniformCount > 0 && (uniformNames == 0 || uniformIndices == 0)) { + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } + program = GL.programs[program]; + var names = []; + for (var i = 0; i < uniformCount; i++) + names.push(Pointer_stringify({{{ makeGetValue('uniformNames', 'i*4', 'i32') }}})); + + var result = GLctx.getUniformIndices(program, names); + if (!result) return; // GL spec: If an error is generated, nothing is written out to uniformIndices. + + var len = result.length; + for (var i = 0; i < len; i++) { + {{{ makeSetValue('uniformIndices', 'i*4', 'result[i]', 'i32') }}}; + } + }, + + glGetActiveUniformsiv__sig: 'viiiii', + glGetActiveUniformsiv: function(program, uniformCount, uniformIndices, pname, params) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glGetActiveUniformsiv', 'program'); + if (!params) { + // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense + // if params == null, issue a GL error to notify user about it. + Module.printErr('GL_INVALID_VALUE in glGetActiveUniformsiv(program=' + program + ', uniformCount=' + uniformCount + ', uniformIndices=' + uniformIndices + ', pname=' + pname + ', params=0): Function called with null out pointer!'); + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } +#endif + if (uniformCount > 0 && uniformIndices == 0) { + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } + program = GL.programs[program]; + var ids = []; + for (var i = 0; i < uniformCount; i++) { + ids.push({{{ makeGetValue('uniformIndices', 'i*4', 'i32') }}}); + } + + var result = GLctx.getActiveUniforms(program, ids, pname); + if (!result) return; // GL spec: If an error is generated, nothing is written out to params. + + var len = result.length; + for (var i = 0; i < len; i++) { + {{{ makeSetValue('params', 'i*4', 'result[i]', 'i32') }}}; + } + }, + + glGetUniformBlockIndex__sig: 'iii', + glGetUniformBlockIndex: function(program, uniformBlockName) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glGetUniformBlockIndex', 'program'); +#endif + program = GL.programs[program]; + uniformBlockName = Pointer_stringify(uniformBlockName); + return GLctx.getUniformBlockIndex(program, uniformBlockName); + }, + + glGetActiveUniformBlockiv__sig: 'viiii', + glGetActiveUniformBlockiv: function(program, uniformBlockIndex, pname, params) { +#if GL_ASSERTIONS + if (!params) { + // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense + // if params == null, issue a GL error to notify user about it. + Module.printErr('GL_INVALID_VALUE in glGetActiveUniformBlockiv(program=' + program + ', uniformBlockIndex=' + uniformBlockIndex + ', pname=' + pname + ', params=0): Function called with null out pointer!'); + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } + GL.validateGLObjectID(GL.programs, program, 'glGetActiveUniformBlockiv', 'program'); +#endif + program = GL.programs[program]; + + var result = GLctx.getActiveUniformBlockParameter(program, uniformBlockIndex, pname); + if (!result) return; // If an error occurs, nothing will be written to params. + if (typeof result == 'number') { + {{{ makeSetValue('params', '0', 'result', 'i32') }}}; + } else { + for (var i = 0; i < result.length; i++) { + {{{ makeSetValue('params', 'i*4', 'result[i]', 'i32') }}}; + } + } + }, + + glGetActiveUniformBlockName__sig: 'viiiii', + glGetActiveUniformBlockName: function(program, uniformBlockIndex, bufSize, length, uniformBlockName) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glGetActiveUniformBlockName', 'program'); +#endif + program = GL.programs[program]; + + var result = GLctx.getActiveUniformBlockName(program, uniformBlockIndex); + if (!result) return; // If an error occurs, nothing will be written to uniformBlockName or length. + var name = result.slice(0, Math.max(0, bufSize - 1)); + if (uniformBlockName && bufSize > 0) { + writeStringToMemory(name, uniformBlockName); + if (length) {{{ makeSetValue('length', '0', 'name.length', 'i32') }}}; + } else { + if (length) {{{ makeSetValue('length', '0', 0, 'i32') }}}; + } + }, + + glUniformBlockBinding__sig: 'viii', + glUniformBlockBinding: function(program, uniformBlockIndex, uniformBlockBinding) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glUniformBlockBinding', 'program'); +#endif + program = GL.programs[program]; + + GLctx.uniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding); + }, + +// ~USE_WEBGL2 #endif glIsBuffer__sig: 'ii', @@ -1405,6 +1940,15 @@ var LibraryGL = { glGetRenderbufferParameteriv__sig: 'viii', glGetRenderbufferParameteriv: function(target, pname, params) { +#if GL_ASSERTIONS + if (!params) { + // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense + // if params == null, issue a GL error to notify user about it. + Module.printErr('GL_INVALID_VALUE in glGetRenderbufferParameteriv(target=' + target + ', pname=' + pname + ', params=0): Function called with null out pointer!'); + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } +#endif {{{ makeSetValue('params', '0', 'GLctx.getRenderbufferParameter(target, pname)', 'i32') }}}; }, @@ -1418,6 +1962,13 @@ var LibraryGL = { glGetUniformfv__sig: 'viii', glGetUniformfv: function(program, location, params) { #if GL_ASSERTIONS + if (!params) { + // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense + // if params == null, issue a GL error to notify user about it. + Module.printErr('GL_INVALID_VALUE in glGetUniformfv(program=' + program + ', location=' + location + ', params=0): Function called with null out pointer!'); + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } GL.validateGLObjectID(GL.programs, program, 'glGetUniformfv', 'program'); GL.validateGLObjectID(GL.uniforms, location, 'glGetUniformfv', 'location'); #endif @@ -1434,6 +1985,13 @@ var LibraryGL = { glGetUniformiv__sig: 'viii', glGetUniformiv: function(program, location, params) { #if GL_ASSERTIONS + if (!params) { + // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense + // if params == null, issue a GL error to notify user about it. + Module.printErr('GL_INVALID_VALUE in glGetUniformiv(program=' + program + ', location=' + location + ', params=0): Function called with null out pointer!'); + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } GL.validateGLObjectID(GL.programs, program, 'glGetUniformiv', 'program'); GL.validateGLObjectID(GL.uniforms, location, 'glGetUniformiv', 'location'); #endif @@ -1483,6 +2041,15 @@ var LibraryGL = { glGetVertexAttribfv__sig: 'viii', glGetVertexAttribfv: function(index, pname, params) { +#if GL_ASSERTIONS + if (!params) { + // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense + // if params == null, issue a GL error to notify user about it. + Module.printErr('GL_INVALID_VALUE in glGetVertexAttribfv(index=' + index + ', pname=' + pname + ', params=0): Function called with null out pointer!'); + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } +#endif #if FULL_ES2 if (GL.currentContext.clientBuffers[index].enabled) { Module.printErr("glGetVertexAttribfv on client-side array: not supported, bad data returned"); @@ -1500,6 +2067,15 @@ var LibraryGL = { glGetVertexAttribiv__sig: 'viii', glGetVertexAttribiv: function(index, pname, params) { +#if GL_ASSERTIONS + if (!params) { + // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense + // if params == null, issue a GL error to notify user about it. + Module.printErr('GL_INVALID_VALUE in glGetVertexAttribiv(index=' + index + ', pname=' + pname + ', params=0): Function called with null out pointer!'); + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } +#endif #if FULL_ES2 if (GL.currentContext.clientBuffers[index].enabled) { Module.printErr("glGetVertexAttribiv on client-side array: not supported, bad data returned"); @@ -1517,6 +2093,15 @@ var LibraryGL = { glGetVertexAttribPointerv__sig: 'viii', glGetVertexAttribPointerv: function(index, pname, pointer) { +#if GL_ASSERTIONS + if (!pointer) { + // GLES2 specification does not specify how to behave if pointer is a null pointer. Since calling this function does not make sense + // if pointer == null, issue a GL error to notify user about it. + Module.printErr('GL_INVALID_VALUE in glGetVertexAttribPointerv(index=' + index + ', pname=' + pname + ', pointer=0): Function called with null out pointer!'); + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } +#endif #if FULL_ES2 if (GL.currentContext.clientBuffers[index].enabled) { Module.printErr("glGetVertexAttribPointer on client-side array: not supported, bad data returned"); @@ -1532,19 +2117,18 @@ var LibraryGL = { #endif program = GL.programs[program]; var info = GLctx.getActiveUniform(program, index); + if (!info) return; // If an error occurs, nothing will be written to length, size, type and name. var infoname = info.name.slice(0, Math.max(0, bufSize - 1)); - writeStringToMemory(infoname, name); - - if (length) { - {{{ makeSetValue('length', '0', 'infoname.length', 'i32') }}}; - } - if (size) { - {{{ makeSetValue('size', '0', 'info.size', 'i32') }}}; - } - if (type) { - {{{ makeSetValue('type', '0', 'info.type', 'i32') }}}; + if (bufSize > 0 && name) { + writeStringToMemory(infoname, name); + if (length) {{{ makeSetValue('length', '0', 'infoname.length', 'i32') }}}; + } else { + if (length) {{{ makeSetValue('length', '0', 0, 'i32') }}}; } + + if (size) {{{ makeSetValue('size', '0', 'info.size', 'i32') }}}; + if (type) {{{ makeSetValue('type', '0', 'info.type', 'i32') }}}; }, glUniform1f__sig: 'vif', @@ -1852,19 +2436,18 @@ var LibraryGL = { #endif program = GL.programs[program]; var info = GLctx.getActiveAttrib(program, index); + if (!info) return; // If an error occurs, nothing will be written to length, size and type and name. var infoname = info.name.slice(0, Math.max(0, bufSize - 1)); - writeStringToMemory(infoname, name); - - if (length) { - {{{ makeSetValue('length', '0', 'infoname.length', 'i32') }}}; - } - if (size) { - {{{ makeSetValue('size', '0', 'info.size', 'i32') }}}; - } - if (type) { - {{{ makeSetValue('type', '0', 'info.type', 'i32') }}}; + if (bufSize > 0 && name) { + writeStringToMemory(infoname, name); + if (length) {{{ makeSetValue('length', '0', 'infoname.length', 'i32') }}}; + } else { + if (length) {{{ makeSetValue('length', '0', 0, 'i32') }}}; } + + if (size) {{{ makeSetValue('size', '0', 'info.size', 'i32') }}}; + if (type) {{{ makeSetValue('type', '0', 'info.type', 'i32') }}}; }, glCreateShader__sig: 'ii', @@ -1921,10 +2504,13 @@ var LibraryGL = { GL.validateGLObjectID(GL.shaders, shader, 'glGetShaderSource', 'shader'); #endif var result = GLctx.getShaderSource(GL.shaders[shader]); + if (!result) return; // If an error occurs, nothing will be written to length or source. result = result.slice(0, Math.max(0, bufSize - 1)); - writeStringToMemory(result, source); - if (length) { - {{{ makeSetValue('length', '0', 'result.length', 'i32') }}}; + if (bufSize > 0 && source) { + writeStringToMemory(result, source); + if (length) {{{ makeSetValue('length', '0', 'result.length', 'i32') }}}; + } else { + if (length) {{{ makeSetValue('length', '0', 0, 'i32') }}}; } }, @@ -1945,20 +2531,29 @@ var LibraryGL = { // Work around a bug in Chromium which causes getShaderInfoLog to return null if (!log) log = '(unknown error)'; log = log.substr(0, maxLength - 1); - writeStringToMemory(log, infoLog); - if (length) { - {{{ makeSetValue('length', '0', 'log.length', 'i32') }}} + if (maxLength > 0 && infoLog) { + writeStringToMemory(log, infoLog); + if (length) {{{ makeSetValue('length', '0', 'log.length', 'i32') }}}; + } else { + if (length) {{{ makeSetValue('length', '0', 0, 'i32') }}}; } }, glGetShaderiv__sig: 'viii', glGetShaderiv : function(shader, pname, p) { #if GL_ASSERTIONS + if (!p) { + // GLES2 specification does not specify how to behave if p is a null pointer. Since calling this function does not make sense + // if p == null, issue a GL error to notify user about it. + Module.printErr('GL_INVALID_VALUE in glGetShaderiv(shader=' + shader + ', pname=' + pname + ', p=0): Function called with null out pointer!'); + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } GL.validateGLObjectID(GL.shaders, shader, 'glGetShaderiv', 'shader'); #endif if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH var log = GLctx.getShaderInfoLog(GL.shaders[shader]); - // Work around a bug in Chromium which causes getShaderInfoLog to return null + // Work around a bug in Chromium which causes getShaderInfoLog to return null: https://code.google.com/p/chromium/issues/detail?id=111337 if (!log) log = '(unknown error)'; {{{ makeSetValue('p', '0', 'log.length + 1', 'i32') }}}; } else { @@ -1969,6 +2564,13 @@ var LibraryGL = { glGetProgramiv__sig: 'viii', glGetProgramiv : function(program, pname, p) { #if GL_ASSERTIONS + if (!p) { + // GLES2 specification does not specify how to behave if p is a null pointer. Since calling this function does not make sense + // if p == null, issue a GL error to notify user about it. + Module.printErr('GL_INVALID_VALUE in glGetProgramiv(program=' + program + ', pname=' + pname + ', p=0): Function called with null out pointer!'); + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } GL.validateGLObjectID(GL.programs, program, 'glGetProgramiv', 'program'); #endif if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH @@ -2092,14 +2694,18 @@ var LibraryGL = { GL.validateGLObjectID(GL.programs, program, 'glGetProgramInfoLog', 'program'); #endif var log = GLctx.getProgramInfoLog(GL.programs[program]); - // Work around a bug in Chromium which causes getProgramInfoLog to return null - if (!log) { - log = ""; - } + // Work around a bug in Chromium which causes getProgramInfoLog to return null: https://code.google.com/p/chromium/issues/detail?id=111337 + // Note that this makes glGetProgramInfoLog behavior to be inconsistent. If an error occurs, GL functions should not write anything + // to the output parameters, however with this workaround in place, we will always write an empty string out to 'infoLog', even if an + // error did occur. + if (!log) log = ""; + log = log.substr(0, maxLength - 1); - writeStringToMemory(log, infoLog); - if (length) { - {{{ makeSetValue('length', '0', 'log.length', 'i32') }}} + if (maxLength > 0 && infoLog) { + writeStringToMemory(log, infoLog); + if (length) {{{ makeSetValue('length', '0', 'log.length', 'i32') }}}; + } else { + if (length) {{{ makeSetValue('length', '0', 0, 'i32') }}}; } }, @@ -5483,7 +6089,9 @@ var LibraryGL = { glRotatef: 'glRotated', glDrawBuffer: function() { throw 'glDrawBuffer: TODO' }, +#if !USE_WEBGL2 glReadBuffer: function() { throw 'glReadBuffer: TODO' }, +#endif glLightfv: function() { throw 'glLightfv: TODO' }, glLightModelfv: function() { throw 'glLightModelfv: TODO' }, @@ -5628,6 +6236,33 @@ var LibraryGL = { GLctx.vertexAttribPointer(index, size, type, normalized, stride, ptr); }, +#if USE_WEBGL2 + glVertexAttribIPointer__sig: 'viiiii', + glVertexAttribIPointer: function(index, size, type, stride, ptr) { +#if FULL_ES3 + var cb = GL.currentContext.clientBuffers[index]; +#if ASSERTIONS + assert(cb, index); +#endif + if (!GL.currArrayBuffer) { + cb.size = size; + cb.type = type; + cb.normalized = false; + cb.stride = stride; + cb.ptr = ptr; + cb.clientside = true; + return; + } + cb.clientside = false; +#endif +#if GL_ASSERTIONS + GL.validateVertexAttribPointer(size, type, stride, ptr); +#endif + GLctx.vertexAttribIPointer(index, size, type, stride, ptr); + }, +// ~USE_WEBGL2 +#endif + glEnableVertexAttribArray__sig: 'vi', glEnableVertexAttribArray: function(index) { #if FULL_ES2 diff --git a/src/library_html5.js b/src/library_html5.js index 1b02cf6b1148d..1a7cfc3ca56fc 100644 --- a/src/library_html5.js +++ b/src/library_html5.js @@ -582,7 +582,62 @@ var LibraryJSEvents = { JSEvents.registerOrRemoveHandler(eventHandler); }, - requestFullscreen: function(target) { + resizeCanvasForFullscreen: function(target, strategy) { + var restoreOldStyle = __registerRestoreOldStyle(target); + var cssWidth = strategy.softFullscreen ? window.innerWidth : screen.width; + var cssHeight = strategy.softFullscreen ? window.innerHeight : screen.height; + var rect = target.getBoundingClientRect(); + var windowedCssWidth = rect.right - rect.left; + var windowedCssHeight = rect.bottom - rect.top; + var windowedRttWidth = target.width; + var windowedRttHeight = target.height; + + if (strategy.scaleMode == {{{ cDefine('EMSCRIPTEN_FULLSCREEN_SCALE_CENTER') }}}) { + __setLetterbox(target, (cssHeight - windowedCssHeight) / 2, (cssWidth - windowedCssWidth) / 2); + cssWidth = windowedCssWidth; + cssHeight = windowedCssHeight; + } else if (strategy.scaleMode == {{{ cDefine('EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT') }}}) { + if (cssWidth*windowedRttHeight < windowedRttWidth*cssHeight) { + var desiredCssHeight = windowedRttHeight * cssWidth / windowedRttWidth; + __setLetterbox(target, (cssHeight - desiredCssHeight) / 2, 0); + cssHeight = desiredCssHeight; + } else { + var desiredCssWidth = windowedRttWidth * cssHeight / windowedRttHeight; + __setLetterbox(target, 0, (cssWidth - desiredCssWidth) / 2); + cssWidth = desiredCssWidth; + } + } + + // If we are adding padding, must choose a background color or otherwise Chrome will give the + // padding a default white color. Do it only if user has not customized their own background color. + if (!target.style.backgroundColor) target.style.backgroundColor = 'black'; + // IE11 does the same, but requires the color to be set in the document body. + if (!document.body.style.backgroundColor) document.body.style.backgroundColor = 'black'; // IE11 + // Firefox always shows black letterboxes independent of style color. + + target.style.width = cssWidth + 'px'; + target.style.height = cssHeight + 'px'; + + if (strategy.filteringMode == {{{ cDefine('EMSCRIPTEN_FULLSCREEN_FILTERING_NEAREST') }}}) { + target.style.imageRendering = '-moz-crisp-edges'; + target.style['-ms-interpolation-mode'] = 'nearest-neighbor'; + } + + var dpiScale = (strategy.canvasResolutionScaleMode == {{{ cDefine('EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF') }}}) ? window.devicePixelRatio : 1; + if (strategy.canvasResolutionScaleMode != {{{ cDefine('EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE') }}}) { + target.width = cssWidth * dpiScale; + target.height = cssHeight * dpiScale; + if (target.GLctxObject) target.GLctxObject.GLctx.viewport(0, 0, target.width, target.height); + } + return restoreOldStyle; + }, + + requestFullscreen: function(target, strategy) { + // EMSCRIPTEN_FULLSCREEN_SCALE_DEFAULT + EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE is a mode where no extra logic is performed to the DOM elements. + if (strategy.scaleMode != {{{ cDefine('EMSCRIPTEN_FULLSCREEN_SCALE_DEFAULT') }}} || strategy.canvasResolutionScaleMode != {{{ cDefine('EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE') }}}) { + JSEvents.resizeCanvasForFullscreen(target, strategy); + } + if (target.requestFullscreen) { target.requestFullscreen(); } else if (target.msRequestFullscreen) { @@ -600,6 +655,11 @@ var LibraryJSEvents = { return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_TARGET') }}}; } } + + if (strategy.canvasResizedCallback) { + Runtime.dynCall('iiii', strategy.canvasResizedCallback, [{{{ cDefine('EMSCRIPTEN_EVENT_CANVASRESIZED') }}}, 0, strategy.canvasResizedCallbackUserData]); + } + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, @@ -1144,8 +1204,180 @@ var LibraryJSEvents = { return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, - // https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Using_full_screen_mode - emscripten_request_fullscreen: function(target, deferUntilInEventHandler) { + _registerRestoreOldStyle: function(canvas) { + var oldWidth = canvas.width; + var oldHeight = canvas.height; + var oldCssWidth = canvas.style.width; + var oldCssHeight = canvas.style.height; + var oldBackgroundColor = canvas.style.backgroundColor; // Chrome reads color from here. + var oldDocumentBackgroundColor = document.body.style.backgroundColor; // IE11 reads color from here. + // Firefox always has black background color. + var oldPaddingLeft = canvas.style.paddingLeft; // Chrome, FF, Safari + var oldPaddingRight = canvas.style.paddingRight; + var oldPaddingTop = canvas.style.paddingTop; + var oldPaddingBottom = canvas.style.paddingBottom; + var oldMarginLeft = canvas.style.marginLeft; // IE11 + var oldMarginRight = canvas.style.marginRight; + var oldMarginTop = canvas.style.marginTop; + var oldMarginBottom = canvas.style.marginBottom; + var oldDocumentBodyMargin = document.body.style.margin; + var oldDocumentOverflow = document.documentElement.style.overflow; // Chrome, Firefox + var oldDocumentScroll = document.body.scroll; // IE + var oldImageRendering = canvas.style.imageRendering; + var oldInterpolationMode = canvas.style['-ms-interpolation-mode']; + + function restoreOldStyle() { + var fullscreenElement = document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement; + if (!fullscreenElement) { + document.removeEventListener('fullscreenchange', restoreOldStyle); + document.removeEventListener('mozfullscreenchange', restoreOldStyle); + document.removeEventListener('webkitfullscreenchange', restoreOldStyle); + document.removeEventListener('MSFullscreenChange', restoreOldStyle); + + canvas.width = oldWidth; + canvas.height = oldHeight; + canvas.style.width = oldCssWidth; + canvas.style.height = oldCssHeight; + canvas.style.backgroundColor = oldBackgroundColor; // Chrome + // IE11 hack: assigning 'undefined' or an empty string to document.body.style.backgroundColor has no effect, so first assign back the default color + // before setting the undefined value. Setting undefined value is also important, or otherwise we would later treat that as something that the user + // had explicitly set so subsequent fullscreen transitions would not set background color properly. + if (!oldDocumentBackgroundColor) document.body.style.backgroundColor = 'white'; + document.body.style.backgroundColor = oldDocumentBackgroundColor; // IE11 + canvas.style.paddingLeft = oldPaddingLeft; // Chrome, FF, Safari + canvas.style.paddingRight = oldPaddingRight; + canvas.style.paddingTop = oldPaddingTop; + canvas.style.paddingBottom = oldPaddingBottom; + canvas.style.marginLeft = oldMarginLeft; // IE11 + canvas.style.marginRight = oldMarginRight; + canvas.style.marginTop = oldMarginTop; + canvas.style.marginBottom = oldMarginBottom; + document.body.style.margin = oldDocumentBodyMargin; + document.documentElement.style.overflow = oldDocumentOverflow; // Chrome, Firefox + document.body.scroll = oldDocumentScroll; // IE + canvas.style.imageRendering = oldImageRendering; + canvas.style['-ms-interpolation-mode'] = oldInterpolationMode; + if (canvas.GLctxObject) canvas.GLctxObject.GLctx.viewport(0, 0, oldWidth, oldHeight); + + if (__currentFullscreenStrategy.canvasResizedCallback) { + Runtime.dynCall('iiii', __currentFullscreenStrategy.canvasResizedCallback, [{{{ cDefine('EMSCRIPTEN_EVENT_CANVASRESIZED') }}}, 0, __currentFullscreenStrategy.canvasResizedCallbackUserData]); + } + } + } + document.addEventListener('fullscreenchange', restoreOldStyle); + document.addEventListener('mozfullscreenchange', restoreOldStyle); + document.addEventListener('webkitfullscreenchange', restoreOldStyle); + document.addEventListener('MSFullscreenChange', restoreOldStyle); + return restoreOldStyle; + }, + + // Walks the DOM tree and hides every element by setting "display: none;" except the given element. + // Returns a list of [{node: element, displayState: oldDisplayStyle}] entries to allow restoring previous + // visibility states after done. + _hideEverythingExceptGivenElement: function (onlyVisibleElement) { + var child = onlyVisibleElement; + var parent = child.parentNode; + var hiddenElements = []; + while (child != document.body) { + var children = parent.children; + for (var i = 0; i < children.length; ++i) { + if (children[i] != child) { + hiddenElements.push({ node: children[i], displayState: children[i].style.display }); + children[i].style.display = 'none'; + } + } + child = parent; + parent = parent.parentNode; + } + return hiddenElements; + }, + + // Applies old visibility states, given a list of changes returned by hideEverythingExceptGivenElement(). + _restoreHiddenElements: function(hiddenElements) { + for (var i = 0; i < hiddenElements.length; ++i) { + hiddenElements[i].node.style.display = hiddenElements[i].displayState; + } + }, + + // Add letterboxes to a fullscreen element in a cross-browser way. + _setLetterbox__deps: ['$JSEvents'], + _setLetterbox: function(element, topBottom, leftRight) { + if (JSEvents.isInternetExplorer()) { + // Cannot use padding on IE11, because IE11 computes padding in addition to the size, unlike + // other browsers, which treat padding to be part of the size. + // e.g. + // FF, Chrome: If CSS size = 1920x1080, padding-leftright = 460, padding-topbottomx40, then content size = (1920 - 2*460) x (1080-2*40) = 1000x1000px, and total element size = 1920x1080px. + // IE11: If CSS size = 1920x1080, padding-leftright = 460, padding-topbottomx40, then content size = 1920x1080px and total element size = (1920+2*460) x (1080+2*40)px. + // IE11 treats margin like Chrome and FF treat padding. + element.style.marginLeft = element.style.marginRight = leftRight + 'px'; + element.style.marginTop = element.style.marginBottom = topBottom + 'px'; + } else { + // Cannot use margin to specify letterboxes in FF or Chrome, since those ignore margins in fullscreen mode. + element.style.paddingLeft = element.style.paddingRight = leftRight + 'px'; + element.style.paddingTop = element.style.paddingBottom = topBottom + 'px'; + } + }, + + _currentFullscreenStrategy: {}, + _restoreOldWindowedStyle: null, + + _softFullscreenResizeWebGLRenderTarget__deps: ['_setLetterbox', '_currentFullscreenStrategy'], + _softFullscreenResizeWebGLRenderTarget: function() { + var inHiDPIFullscreenMode = __currentFullscreenStrategy.canvasResolutionScaleMode == {{{ cDefine('EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF') }}}; + var inAspectRatioFixedFullscreenMode = __currentFullscreenStrategy.scaleMode == {{{ cDefine('EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT') }}}; + var inPixelPerfectFullscreenMode = __currentFullscreenStrategy.canvasResolutionScaleMode != {{{ cDefine('EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE') }}}; + var inCenteredWithoutScalingFullscreenMode = __currentFullscreenStrategy.scaleMode == {{{ cDefine('EMSCRIPTEN_FULLSCREEN_SCALE_CENTER') }}}; + var screenWidth = inHiDPIFullscreenMode ? Math.round(window.innerWidth*window.devicePixelRatio) : window.innerWidth; + var screenHeight = inHiDPIFullscreenMode ? Math.round(window.innerHeight*window.devicePixelRatio) : window.innerHeight; + var w = screenWidth; + var h = screenHeight; + var canvas = __currentFullscreenStrategy.target; + var x = canvas.width; + var y = canvas.height; + var topMargin; + + if (inAspectRatioFixedFullscreenMode) { + if (w*y < x*h) h = (w * y / x) | 0; + else if (w*y > x*h) w = (h * x / y) | 0; + topMargin = ((screenHeight - h) / 2) | 0; + } + + if (inPixelPerfectFullscreenMode) { + canvas.width = w; + canvas.height = h; + if (canvas.GLctxObject) canvas.GLctxObject.GLctx.viewport(0, 0, canvas.width, canvas.height); + } + + // Back to CSS pixels. + if (inHiDPIFullscreenMode) { + topMargin /= window.devicePixelRatio; + w /= window.devicePixelRatio; + h /= window.devicePixelRatio; + // Round to nearest 4 digits of precision. + w = Math.round(w*1e4)/1e4; + h = Math.round(h*1e4)/1e4; + topMargin = Math.round(topMargin*1e4)/1e4; + } + + if (inCenteredWithoutScalingFullscreenMode) { + var t = (window.innerHeight - parseInt(canvas.style.height)) / 2; + var b = (window.innerWidth - parseInt(canvas.style.width)) / 2; + __setLetterbox(canvas, t, b); + } else { + canvas.style.width = w + 'px'; + canvas.style.height = h + 'px'; + var b = (window.innerWidth - w) / 2; + __setLetterbox(canvas, topMargin, b); + } + + if (!inCenteredWithoutScalingFullscreenMode && __currentFullscreenStrategy.canvasResizedCallback) { + Runtime.dynCall('iiii', __currentFullscreenStrategy.canvasResizedCallback, [{{{ cDefine('EMSCRIPTEN_EVENT_CANVASRESIZED') }}}, 0, __currentFullscreenStrategy.canvasResizedCallbackUserData]); + } + }, + + // https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Using_full_screen_mode + emscripten_do_request_fullscreen__deps: ['_setLetterbox'], + emscripten_do_request_fullscreen: function(target, strategy) { if (typeof JSEvents.fullscreenEnabled() === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; if (!JSEvents.fullscreenEnabled()) return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_TARGET') }}}; if (!target) target = '#canvas'; @@ -1160,17 +1392,94 @@ var LibraryJSEvents = { // Queue this function call if we're not currently in an event handler and the user saw it appropriate to do so. if (!canPerformRequests) { - if (deferUntilInEventHandler) { - JSEvents.deferCall(JSEvents.requestFullscreen, 1 /* priority over pointer lock */, [target]); + if (strategy.deferUntilInEventHandler) { + JSEvents.deferCall(JSEvents.requestFullscreen, 1 /* priority over pointer lock */, [target, strategy]); return {{{ cDefine('EMSCRIPTEN_RESULT_DEFERRED') }}}; } else { return {{{ cDefine('EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED') }}}; } } - return JSEvents.requestFullscreen(target); + return JSEvents.requestFullscreen(target, strategy); }, - + + emscripten_request_fullscreen__deps: ['emscripten_do_request_fullscreen'], + emscripten_request_fullscreen: function(target, deferUntilInEventHandler) { + var strategy = {}; + // These options perform no added logic, but just bare request fullscreen. + strategy.scaleMode = {{{ cDefine('EMSCRIPTEN_FULLSCREEN_SCALE_DEFAULT') }}}; + strategy.canvasResolutionScaleMode = {{{ cDefine('EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE') }}}; + strategy.filteringMode = {{{ cDefine('EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT') }}}; + strategy.deferUntilInEventHandler = deferUntilInEventHandler; + + return _emscripten_do_request_fullscreen(target, strategy); + }, + + emscripten_request_fullscreen_strategy__deps: ['emscripten_do_request_fullscreen', '_currentFullscreenStrategy'], + emscripten_request_fullscreen_strategy: function(target, deferUntilInEventHandler, fullscreenStrategy) { + var strategy = {}; + strategy.scaleMode = {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.scaleMode, 'i32') }}}; + strategy.canvasResolutionScaleMode = {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.canvasResolutionScaleMode, 'i32') }}}; + strategy.filteringMode = {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.filteringMode, 'i32') }}}; + strategy.deferUntilInEventHandler = deferUntilInEventHandler; + strategy.canvasResizedCallback = {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.canvasResizedCallback, 'i32') }}}; + strategy.canvasResizedCallbackUserData = {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.canvasResizedCallbackUserData, 'i32') }}}; + __currentFullscreenStrategy = strategy; + + return _emscripten_do_request_fullscreen(target, strategy); + }, + + emscripten_enter_soft_fullscreen__deps: ['_setLetterbox', '_hideEverythingExceptGivenElement', '_restoreOldWindowedStyle', '_registerRestoreOldStyle', '_restoreHiddenElements', '_currentFullscreenStrategy', '_softFullscreenResizeWebGLRenderTarget'], + emscripten_enter_soft_fullscreen: function(target, fullscreenStrategy) { + if (!target) target = '#canvas'; + target = JSEvents.findEventTarget(target); + if (!target) return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}}; + + var strategy = {}; + strategy.scaleMode = {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.scaleMode, 'i32') }}}; + strategy.canvasResolutionScaleMode = {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.canvasResolutionScaleMode, 'i32') }}}; + strategy.filteringMode = {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.filteringMode, 'i32') }}}; + strategy.canvasResizedCallback = {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.canvasResizedCallback, 'i32') }}}; + strategy.canvasResizedCallbackUserData = {{{ makeGetValue('fullscreenStrategy', C_STRUCTS.EmscriptenFullscreenStrategy.canvasResizedCallbackUserData, 'i32') }}}; + strategy.target = target; + strategy.softFullscreen = true; + + var restoreOldStyle = JSEvents.resizeCanvasForFullscreen(target, strategy); + + document.documentElement.style.overflow = 'hidden'; // Firefox, Chrome + document.body.scroll = "no"; // IE11 + document.body.style.margin = '0px'; // Override default document margin area on all browsers. + + var hiddenElements = __hideEverythingExceptGivenElement(target); + + function restoreWindowedState() { + restoreOldStyle(); + __restoreHiddenElements(hiddenElements); + window.removeEventListener('resize', __softFullscreenResizeWebGLRenderTarget); + if (strategy.canvasResizedCallback) { + Runtime.dynCall('iiii', strategy.canvasResizedCallback, [{{{ cDefine('EMSCRIPTEN_EVENT_CANVASRESIZED') }}}, 0, strategy.canvasResizedCallbackUserData]); + } + } + __restoreOldWindowedStyle = restoreWindowedState; + __currentFullscreenStrategy = strategy; + window.addEventListener('resize', __softFullscreenResizeWebGLRenderTarget); + + // Inform the caller that the canvas size has changed. + if (strategy.canvasResizedCallback) { + Runtime.dynCall('iiii', strategy.canvasResizedCallback, [{{{ cDefine('EMSCRIPTEN_EVENT_CANVASRESIZED') }}}, 0, strategy.canvasResizedCallbackUserData]); + } + + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_exit_soft_fullscreen__deps: ['_restoreOldWindowedStyle'], + emscripten_exit_soft_fullscreen: function() { + if (__restoreOldWindowedStyle) __restoreOldWindowedStyle(); + __restoreOldWindowedStyle = null; + + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + emscripten_exit_fullscreen: function() { if (typeof JSEvents.fullscreenEnabled() === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; // Make sure no queued up calls will fire after this. @@ -1187,6 +1496,11 @@ var LibraryJSEvents = { } else { return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; } + + if (strategy.canvasResizedCallback) { + Runtime.dynCall('iiii', strategy.canvasResizedCallback, [{{{ cDefine('EMSCRIPTEN_EVENT_CANVASRESIZED') }}}, 0, strategy.canvasResizedCallbackUserData]); + } + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, diff --git a/src/library_memfs.js b/src/library_memfs.js index dc4a7fb472f3e..8d4df98f8c45f 100644 --- a/src/library_memfs.js +++ b/src/library_memfs.js @@ -335,8 +335,6 @@ mergeInto(LibraryManager.library, { if (position < 0) { throw new FS.ErrnoError(ERRNO_CODES.EINVAL); } - stream.ungotten = []; - stream.position = position; return position; }, allocate: function(stream, offset, length) { diff --git a/src/library_nodefs.js b/src/library_nodefs.js index 7686f3f2861e0..623be260d4eda 100644 --- a/src/library_nodefs.js +++ b/src/library_nodefs.js @@ -233,6 +233,7 @@ mergeInto(LibraryManager.library, { } }, read: function (stream, buffer, offset, length, position) { + if (length === 0) return 0; // node errors on 0 length reads // FIXME this is terrible. var nbuffer = new Buffer(length); var res; @@ -278,7 +279,6 @@ mergeInto(LibraryManager.library, { throw new FS.ErrnoError(ERRNO_CODES.EINVAL); } - stream.position = position; return position; } } diff --git a/src/library_sdl.js b/src/library_sdl.js index eb8c6efc8011f..84f7d168edd63 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -269,6 +269,27 @@ var LibrarySDL = { }; }, + updateRect: function(rect, r) { + {{{ makeSetValue('rect', C_STRUCTS.SDL_Rect.x, 'r.x', 'i32') }}}; + {{{ makeSetValue('rect', C_STRUCTS.SDL_Rect.y, 'r.y', 'i32') }}}; + {{{ makeSetValue('rect', C_STRUCTS.SDL_Rect.w, 'r.w', 'i32') }}}; + {{{ makeSetValue('rect', C_STRUCTS.SDL_Rect.h, 'r.h', 'i32') }}}; + }, + + intersectionOfRects: function(first, second) { + var leftX = Math.max(first.x, second.x); + var leftY = Math.max(first.y, second.y); + var rightX = Math.min(first.x + first.w, second.x + second.w); + var rightY = Math.min(first.y + first.h, second.y + second.h); + + return { + x: leftX, + y: leftY, + w: Math.max(leftX, rightX) - leftX, + h: Math.max(leftY, rightY) - leftY + } + }, + checkPixelFormat: function(fmt) { #if ASSERTIONS // Canvas screens are always RGBA. @@ -455,7 +476,20 @@ var LibrarySDL = { if (dstrect) { dr = SDL.loadRect(dstrect); } else { - dr = { x: 0, y: 0, w: -1, h: -1 }; + dr = { x: 0, y: 0, w: srcData.width, h: srcData.height }; + } + if (dstData.clipRect) { + var widthScale = (!scale || sr.w === 0) ? 1 : sr.w / dr.w; + var heightScale = (!scale || sr.h === 0) ? 1 : sr.h / dr.h; + + dr = SDL.intersectionOfRects(dstData.clipRect, dr); + + sr.w = dr.w * widthScale; + sr.h = dr.h * heightScale; + + if (dstrect) { + SDL.updateRect(dstrect, dr); + } } var blitw, blitr; if (scale) { @@ -1034,6 +1068,15 @@ var LibrarySDL = { return ret; }, + setPannerPosition: function(info, x, y, z) { + if (!info) return 0; + if (info.audio) { + if (info.audio.webAudioPannerNode) + info.audio.webAudioPannerNode['setPosition'](x, y, z); + } + return ret; + }, + // Plays out an SDL audio resource that was loaded with the Mix_Load APIs, when using Web Audio.. playWebAudio: function(audio) { if (!audio) return; @@ -1050,13 +1093,19 @@ var LibrarySDL = { audio.webAudioNode = SDL.audioContext['createBufferSource'](); audio.webAudioNode['buffer'] = webAudio.decodedBuffer; audio.webAudioNode['loop'] = audio.loop; - audio.webAudioNode['onended'] = function() { audio.onended(); } // For element compatibility, route the onended signal to the instance. + audio.webAudioNode['onended'] = function() { audio['onended'](); } // For element compatibility, route the onended signal to the instance. + + audio.webAudioPannerNode = SDL.audioContext['createPanner'](); + audio.webAudioPannerNode['panningModel'] = 'equalpower'; // Add an intermediate gain node to control volume. audio.webAudioGainNode = SDL.audioContext['createGain'](); audio.webAudioGainNode['gain']['value'] = audio.volume; - audio.webAudioNode['connect'](audio.webAudioGainNode); + + audio.webAudioNode['connect'](audio.webAudioPannerNode); + audio.webAudioPannerNode['connect'](audio.webAudioGainNode); audio.webAudioGainNode['connect'](SDL.audioContext['destination']); + audio.webAudioNode['start'](0, audio.currentPosition); audio.startTime = SDL.audioContext['currentTime'] - audio.currentPosition; } catch(e) { @@ -1756,6 +1805,24 @@ var LibrarySDL = { SDL_LowerBlit: 'SDL_UpperBlit', SDL_LowerBlitScaled: 'SDL_UpperBlitScaled', + SDL_GetClipRect: function(surf, rect) { + assert(rect); + + var surfData = SDL.surfaces[surf]; + var r = surfData.clipRect || { x: 0, y: 0, w: surfData.width, h: surfData.height }; + SDL.updateRect(rect, r); + }, + + SDL_SetClipRect: function(surf, rect) { + var surfData = SDL.surfaces[surf]; + + if (rect) { + surfData.clipRect = SDL.intersectionOfRects({ x: 0, y: 0, w: surfData.width, h: surfData.height }, SDL.loadRect(rect)); + } else { + delete surfData.clipRect; + } + }, + SDL_FillRect: function(surf, rect, color) { var surfData = SDL.surfaces[surf]; assert(!surfData.locked); // but we could unlock and re-lock if we must.. @@ -1768,6 +1835,15 @@ var LibrarySDL = { } var r = rect ? SDL.loadRect(rect) : { x: 0, y: 0, w: surfData.width, h: surfData.height }; + + if (surfData.clipRect) { + r = SDL.intersectionOfRects(surfData.clipRect, r); + + if (rect) { + SDL.updateRect(rect, r); + } + } + surfData.ctx.save(); surfData.ctx.fillStyle = SDL.translateColorToCSSRGBA(color); surfData.ctx.fillRect(r.x, r.y, r.w, r.h); @@ -2435,8 +2511,18 @@ var LibrarySDL = { return SDL.setGetVolume(SDL.channels[channel], volume); }, - Mix_SetPanning: function() { - return 0; // error +// Note: Mix_SetPanning requires WebAudio (file loaded from memory). + Mix_SetPanning: function(channel, left, right) { + // SDL API uses [0-255], while PannerNode has an (x, y, z) position. + + // Normalizing. + left /= 255; + right /= 255; + + // Set the z coordinate a little forward, otherwise there won't be any + // smooth transition between left and right. + SDL.setPannerPosition(SDL.channels[channel], right - left, 0, 0.1); + return 1; }, Mix_LoadWAV_RW: function(rwopsID, freesrc) { @@ -3267,4 +3353,3 @@ var LibrarySDL = { autoAddDeps(LibrarySDL, '$SDL'); mergeInto(LibraryManager.library, LibrarySDL); - diff --git a/src/library_trace.js b/src/library_trace.js index 791f99afd120a..4a9d501ae4fc9 100644 --- a/src/library_trace.js +++ b/src/library_trace.js @@ -16,6 +16,7 @@ var LibraryTracing = { EVENT_ALLOCATE: 'allocate', EVENT_ANNOTATE_TYPE: 'annotate-type', EVENT_APPLICATION_NAME: 'application-name', + EVENT_ASSOCIATE_STORAGE_SIZE: 'associate-storage-size', EVENT_ENTER_CONTEXT: 'enter-context', EVENT_EXIT_CONTEXT: 'exit-context', EVENT_FRAME_END: 'frame-end', @@ -28,6 +29,11 @@ var LibraryTracing = { EVENT_REALLOCATE: 'reallocate', EVENT_REPORT_ERROR: 'report-error', EVENT_SESSION_NAME: 'session-name', + EVENT_TASK_ASSOCIATE_DATA: 'task-associate-data', + EVENT_TASK_END: 'task-end', + EVENT_TASK_RESUME: 'task-resume', + EVENT_TASK_START: 'task-start', + EVENT_TASK_SUSPEND: 'task-suspend', EVENT_USER_NAME: 'user-name', init: function() { @@ -179,6 +185,13 @@ var LibraryTracing = { } }, + emscripten_trace_associate_storage_size: function(address, size) { + if (EmscriptenTrace.enabled) { + EmscriptenTrace.post([EmscriptenTrace.EVENT_ASSOCIATE_STORAGE_SIZE, + address, size]); + } + }, + emscripten_trace_report_memory_layout: function() { if (EmscriptenTrace.enabled) { var memory_layout = { @@ -243,6 +256,45 @@ var LibraryTracing = { } }, + emscripten_trace_task_start: function(task_id, name) { + if (EmscriptenTrace.enabled) { + var now = EmscriptenTrace.now(); + EmscriptenTrace.post([EmscriptenTrace.EVENT_TASK_START, + now, task_id, Pointer_stringify(name)]); + } + }, + + emscripten_trace_task_associate_data: function(key, value) { + if (EmscriptenTrace.enabled) { + EmscriptenTrace.post([EmscriptenTrace.EVENT_TASK_ASSOCIATE_DATA, + Pointer_stringify(key), + Pointer_stringify(value)]); + } + }, + + emscripten_trace_task_suspend: function(explanation) { + if (EmscriptenTrace.enabled) { + var now = EmscriptenTrace.now(); + EmscriptenTrace.post([EmscriptenTrace.EVENT_TASK_SUSPEND, + now, Pointer_stringify(explanation)]); + } + }, + + emscripten_trace_task_resume: function(task_id, explanation) { + if (EmscriptenTrace.enabled) { + var now = EmscriptenTrace.now(); + EmscriptenTrace.post([EmscriptenTrace.EVENT_TASK_RESUME, + now, task_id, Pointer_stringify(explanation)]); + } + }, + + emscripten_trace_task_end: function() { + if (EmscriptenTrace.enabled) { + var now = EmscriptenTrace.now(); + EmscriptenTrace.post([EmscriptenTrace.EVENT_TASK_END, now]); + } + }, + emscripten_trace_close: function() { EmscriptenTrace.configured = false; EmscriptenTrace.enabled = false; diff --git a/src/preamble.js b/src/preamble.js index a1bc48ecefedf..e992525bba8e9 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -634,7 +634,7 @@ function allocate(slab, types, allocator, ptr) { Module['allocate'] = allocate; function Pointer_stringify(ptr, /* optional */ length) { - if (length === 0) return ''; + if (length === 0 || !ptr) return ''; // TODO: use TextDecoder // Find the length, and check for UTF while doing so var hasUtf = false; diff --git a/src/settings.js b/src/settings.js index eb34d2dba67cf..aeeaf27fc07d4 100644 --- a/src/settings.js +++ b/src/settings.js @@ -593,6 +593,7 @@ var USE_SDL = 1; // Specify the SDL version that is being linked against. // 1, the default, is 1.3, which is implemented in JS // 2 is a port of the SDL C code on emscripten-ports var USE_SDL_IMAGE = 1; // Specify the SDL_image version that is being linked against. Must match USE_SDL +var USE_ZLIB = 0; // 1 = use zlib from emscripten-ports // Compiler debugging options diff --git a/src/shell.js b/src/shell.js index e536359199059..b1a705c70dc6f 100644 --- a/src/shell.js +++ b/src/shell.js @@ -70,7 +70,8 @@ if (ENVIRONMENT_IS_NODE) { globalEval(read(f)); }; - Module['thisProgram'] = process['argv'][1].replace(/\\/g, '/'); + if (process['argv'].length > 1) + Module['thisProgram'] = process['argv'][1].replace(/\\/g, '/'); Module['arguments'] = process['argv'].slice(2); if (typeof module !== 'undefined') { diff --git a/src/struct_info.json b/src/struct_info.json index b6c7d26ae091b..e46d2784d2157 100644 --- a/src/struct_info.json +++ b/src/struct_info.json @@ -1156,6 +1156,7 @@ "EMSCRIPTEN_EVENT_MOUSELEAVE", "EMSCRIPTEN_EVENT_MOUSEOVER", "EMSCRIPTEN_EVENT_MOUSEOUT", + "EMSCRIPTEN_EVENT_CANVASRESIZED", "EMSCRIPTEN_RESULT_SUCCESS", "EMSCRIPTEN_RESULT_DEFERRED", @@ -1165,7 +1166,18 @@ "EMSCRIPTEN_RESULT_INVALID_PARAM", "EMSCRIPTEN_RESULT_NOT_SUPPORTED", "EMSCRIPTEN_RESULT_FAILED", - "EMSCRIPTEN_RESULT_NO_DATA" + "EMSCRIPTEN_RESULT_NO_DATA", + + "EMSCRIPTEN_FULLSCREEN_SCALE_DEFAULT", + "EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH", + "EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT", + "EMSCRIPTEN_FULLSCREEN_SCALE_CENTER", + "EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE", + "EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF", + "EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF", + "EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT", + "EMSCRIPTEN_FULLSCREEN_FILTERING_NEAREST", + "EMSCRIPTEN_FULLSCREEN_FILTERING_BILINEAR" ], "structs": { "EmscriptenKeyboardEvent": [ @@ -1319,6 +1331,13 @@ "majorVersion", "minorVersion", "enableExtensionsByDefault" + ], + "EmscriptenFullscreenStrategy": [ + "scaleMode", + "canvasResolutionScaleMode", + "filteringMode", + "canvasResizedCallback", + "canvasResizedCallbackUserData" ] } } diff --git a/system/include/emscripten/html5.h b/system/include/emscripten/html5.h index 9ebb85b0c0d92..975a52e72c1a0 100644 --- a/system/include/emscripten/html5.h +++ b/system/include/emscripten/html5.h @@ -52,6 +52,7 @@ extern "C" { #define EMSCRIPTEN_EVENT_MOUSELEAVE 34 #define EMSCRIPTEN_EVENT_MOUSEOVER 35 #define EMSCRIPTEN_EVENT_MOUSEOUT 36 +#define EMSCRIPTEN_EVENT_CANVASRESIZED 37 #define EMSCRIPTEN_RESULT int @@ -121,7 +122,7 @@ typedef struct EmscriptenMouseEvent { } EmscriptenMouseEvent; -typedef EM_BOOL (*em_mouse_callback_func)(int eventType, const EmscriptenMouseEvent *keyEvent, void *userData); +typedef EM_BOOL (*em_mouse_callback_func)(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); extern EMSCRIPTEN_RESULT emscripten_set_click_callback(const char *target, void *userData, EM_BOOL useCapture, em_mouse_callback_func callback); extern EMSCRIPTEN_RESULT emscripten_set_mousedown_callback(const char *target, void *userData, EM_BOOL useCapture, em_mouse_callback_func callback); extern EMSCRIPTEN_RESULT emscripten_set_mouseup_callback(const char *target, void *userData, EM_BOOL useCapture, em_mouse_callback_func callback); @@ -147,7 +148,7 @@ typedef struct EmscriptenWheelEvent { } EmscriptenWheelEvent; -typedef EM_BOOL (*em_wheel_callback_func)(int eventType, const EmscriptenWheelEvent *keyEvent, void *userData); +typedef EM_BOOL (*em_wheel_callback_func)(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData); extern EMSCRIPTEN_RESULT emscripten_set_wheel_callback(const char *target, void *userData, EM_BOOL useCapture, em_wheel_callback_func callback); typedef struct EmscriptenUiEvent { @@ -163,7 +164,7 @@ typedef struct EmscriptenUiEvent { } EmscriptenUiEvent; -typedef EM_BOOL (*em_ui_callback_func)(int eventType, const EmscriptenUiEvent *keyEvent, void *userData); +typedef EM_BOOL (*em_ui_callback_func)(int eventType, const EmscriptenUiEvent *uiEvent, void *userData); extern EMSCRIPTEN_RESULT emscripten_set_resize_callback(const char *target, void *userData, EM_BOOL useCapture, em_ui_callback_func callback); extern EMSCRIPTEN_RESULT emscripten_set_scroll_callback(const char *target, void *userData, EM_BOOL useCapture, em_ui_callback_func callback); @@ -172,7 +173,7 @@ typedef struct EmscriptenFocusEvent { EM_UTF8 id[128]; } EmscriptenFocusEvent; -typedef EM_BOOL (*em_focus_callback_func)(int eventType, const EmscriptenFocusEvent *keyEvent, void *userData); +typedef EM_BOOL (*em_focus_callback_func)(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData); extern EMSCRIPTEN_RESULT emscripten_set_blur_callback(const char *target, void *userData, EM_BOOL useCapture, em_focus_callback_func callback); extern EMSCRIPTEN_RESULT emscripten_set_focus_callback(const char *target, void *userData, EM_BOOL useCapture, em_focus_callback_func callback); extern EMSCRIPTEN_RESULT emscripten_set_focusin_callback(const char *target, void *userData, EM_BOOL useCapture, em_focus_callback_func callback); @@ -187,7 +188,7 @@ typedef struct EmscriptenDeviceOrientationEvent { } EmscriptenDeviceOrientationEvent; -typedef EM_BOOL (*em_deviceorientation_callback_func)(int eventType, const EmscriptenDeviceOrientationEvent *keyEvent, void *userData); +typedef EM_BOOL (*em_deviceorientation_callback_func)(int eventType, const EmscriptenDeviceOrientationEvent *deviceOrientationEvent, void *userData); extern EMSCRIPTEN_RESULT emscripten_set_deviceorientation_callback(void *userData, EM_BOOL useCapture, em_deviceorientation_callback_func callback); extern EMSCRIPTEN_RESULT emscripten_get_deviceorientation_status(EmscriptenDeviceOrientationEvent *orientationState); @@ -206,7 +207,7 @@ typedef struct EmscriptenDeviceMotionEvent { } EmscriptenDeviceMotionEvent; -typedef EM_BOOL (*em_devicemotion_callback_func)(int eventType, const EmscriptenDeviceMotionEvent *keyEvent, void *userData); +typedef EM_BOOL (*em_devicemotion_callback_func)(int eventType, const EmscriptenDeviceMotionEvent *deviceMotionEvent, void *userData); extern EMSCRIPTEN_RESULT emscripten_set_devicemotion_callback(void *userData, EM_BOOL useCapture, em_devicemotion_callback_func callback); extern EMSCRIPTEN_RESULT emscripten_get_devicemotion_status(EmscriptenDeviceMotionEvent *motionState); @@ -221,7 +222,7 @@ typedef struct EmscriptenOrientationChangeEvent { } EmscriptenOrientationChangeEvent; -typedef EM_BOOL (*em_orientationchange_callback_func)(int eventType, const EmscriptenOrientationChangeEvent *keyEvent, void *userData); +typedef EM_BOOL (*em_orientationchange_callback_func)(int eventType, const EmscriptenOrientationChangeEvent *orientationChangeEvent, void *userData); extern EMSCRIPTEN_RESULT emscripten_set_orientationchange_callback(void *userData, EM_BOOL useCapture, em_orientationchange_callback_func callback); extern EMSCRIPTEN_RESULT emscripten_get_orientation_status(EmscriptenOrientationChangeEvent *orientationStatus); extern EMSCRIPTEN_RESULT emscripten_lock_orientation(int allowedOrientations); @@ -239,15 +240,46 @@ typedef struct EmscriptenFullscreenChangeEvent { } EmscriptenFullscreenChangeEvent; -typedef EM_BOOL (*em_fullscreenchange_callback_func)(int eventType, const EmscriptenFullscreenChangeEvent *keyEvent, void *userData); +typedef EM_BOOL (*em_fullscreenchange_callback_func)(int eventType, const EmscriptenFullscreenChangeEvent *fullscreenChangeEvent, void *userData); extern EMSCRIPTEN_RESULT emscripten_set_fullscreenchange_callback(const char *target, void *userData, EM_BOOL useCapture, em_fullscreenchange_callback_func callback); extern EMSCRIPTEN_RESULT emscripten_get_fullscreen_status(EmscriptenFullscreenChangeEvent *fullscreenStatus); +#define EMSCRIPTEN_FULLSCREEN_SCALE int +#define EMSCRIPTEN_FULLSCREEN_SCALE_DEFAULT 0 +#define EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH 1 +#define EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT 2 +#define EMSCRIPTEN_FULLSCREEN_SCALE_CENTER 3 + +#define EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE int +#define EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE 0 +#define EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF 1 +#define EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF 2 + +#define EMSCRIPTEN_FULLSCREEN_FILTERING int +#define EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT 0 +#define EMSCRIPTEN_FULLSCREEN_FILTERING_NEAREST 1 +#define EMSCRIPTEN_FULLSCREEN_FILTERING_BILINEAR 2 + +typedef EM_BOOL (*em_canvasresized_callback_func)(int eventType, const void *reserved, void *userData); + +typedef struct EmscriptenFullscreenStrategy { + EMSCRIPTEN_FULLSCREEN_SCALE scaleMode; + EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE canvasResolutionScaleMode; + EMSCRIPTEN_FULLSCREEN_FILTERING filteringMode; + em_canvasresized_callback_func canvasResizedCallback; + void *canvasResizedCallbackUserData; +} EmscriptenFullscreenStrategy; + extern EMSCRIPTEN_RESULT emscripten_request_fullscreen(const char *target, EM_BOOL deferUntilInEventHandler); +extern EMSCRIPTEN_RESULT emscripten_request_fullscreen_strategy(const char *target, EM_BOOL deferUntilInEventHandler, const EmscriptenFullscreenStrategy *fullscreenStrategy); extern EMSCRIPTEN_RESULT emscripten_exit_fullscreen(void); +extern EMSCRIPTEN_RESULT emscripten_enter_soft_fullscreen(const char *target, const EmscriptenFullscreenStrategy *fullscreenStrategy); + +extern EMSCRIPTEN_RESULT emscripten_exit_soft_fullscreen(void); + typedef struct EmscriptenPointerlockChangeEvent { EM_BOOL isActive; EM_UTF8 nodeName[128]; @@ -255,7 +287,7 @@ typedef struct EmscriptenPointerlockChangeEvent { } EmscriptenPointerlockChangeEvent; -typedef EM_BOOL (*em_pointerlockchange_callback_func)(int eventType, const EmscriptenPointerlockChangeEvent *keyEvent, void *userData); +typedef EM_BOOL (*em_pointerlockchange_callback_func)(int eventType, const EmscriptenPointerlockChangeEvent *pointerlockChangeEvent, void *userData); extern EMSCRIPTEN_RESULT emscripten_set_pointerlockchange_callback(const char *target, void *userData, EM_BOOL useCapture, em_pointerlockchange_callback_func callback); extern EMSCRIPTEN_RESULT emscripten_get_pointerlock_status(EmscriptenPointerlockChangeEvent *pointerlockStatus); @@ -274,7 +306,7 @@ typedef struct EmscriptenVisibilityChangeEvent { int visibilityState; } EmscriptenVisibilityChangeEvent; -typedef EM_BOOL (*em_visibilitychange_callback_func)(int eventType, const EmscriptenVisibilityChangeEvent *keyEvent, void *userData); +typedef EM_BOOL (*em_visibilitychange_callback_func)(int eventType, const EmscriptenVisibilityChangeEvent *visibilityChangeEvent, void *userData); extern EMSCRIPTEN_RESULT emscripten_set_visibilitychange_callback(void *userData, EM_BOOL useCapture, em_visibilitychange_callback_func callback); extern EMSCRIPTEN_RESULT emscripten_get_visibility_status(EmscriptenVisibilityChangeEvent *visibilityStatus); @@ -307,7 +339,7 @@ typedef struct EmscriptenTouchEvent { } EmscriptenTouchEvent; -typedef EM_BOOL (*em_touch_callback_func)(int eventType, const EmscriptenTouchEvent *keyEvent, void *userData); +typedef EM_BOOL (*em_touch_callback_func)(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData); extern EMSCRIPTEN_RESULT emscripten_set_touchstart_callback(const char *target, void *userData, EM_BOOL useCapture, em_touch_callback_func callback); extern EMSCRIPTEN_RESULT emscripten_set_touchend_callback(const char *target, void *userData, EM_BOOL useCapture, em_touch_callback_func callback); extern EMSCRIPTEN_RESULT emscripten_set_touchmove_callback(const char *target, void *userData, EM_BOOL useCapture, em_touch_callback_func callback); @@ -328,7 +360,7 @@ typedef struct EmscriptenGamepadEvent { } EmscriptenGamepadEvent; -typedef EM_BOOL (*em_gamepad_callback_func)(int eventType, const EmscriptenGamepadEvent *keyEvent, void *userData); +typedef EM_BOOL (*em_gamepad_callback_func)(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData); extern EMSCRIPTEN_RESULT emscripten_set_gamepadconnected_callback(void *userData, EM_BOOL useCapture, em_gamepad_callback_func callback); extern EMSCRIPTEN_RESULT emscripten_set_gamepaddisconnected_callback(void *userData, EM_BOOL useCapture, em_gamepad_callback_func callback); @@ -342,7 +374,7 @@ typedef struct EmscriptenBatteryEvent { EM_BOOL charging; } EmscriptenBatteryEvent; -typedef EM_BOOL (*em_battery_callback_func)(int eventType, const EmscriptenBatteryEvent *keyEvent, void *userData); +typedef EM_BOOL (*em_battery_callback_func)(int eventType, const EmscriptenBatteryEvent *batteryEvent, void *userData); extern EMSCRIPTEN_RESULT emscripten_set_batterychargingchange_callback(void *userData, em_battery_callback_func callback); extern EMSCRIPTEN_RESULT emscripten_set_batterylevelchange_callback(void *userData, em_battery_callback_func callback); diff --git a/system/include/emscripten/trace.h b/system/include/emscripten/trace.h index e99294c158498..00f60f3e94368 100644 --- a/system/include/emscripten/trace.h +++ b/system/include/emscripten/trace.h @@ -34,6 +34,8 @@ void emscripten_trace_record_free(const void *address); void emscripten_trace_annotate_address_type(const void *address, const char *type); +void emscripten_trace_associate_storage_size(const void *address, int32_t size); + void emscripten_trace_report_memory_layout(void); void emscripten_trace_report_off_heap_data(void); @@ -42,6 +44,16 @@ void emscripten_trace_enter_context(const char *name); void emscripten_trace_exit_context(void); +void emscripten_trace_task_start(int task_id, const char *name); + +void emscripten_trace_task_associate_data(const char *key, const char *value); + +void emscripten_trace_task_suspend(const char *explanation); + +void emscripten_trace_task_resume(int task_id, const char *explanation); + +void emscripten_trace_task_end(void); + void emscripten_trace_close(void); #else @@ -62,6 +74,11 @@ void emscripten_trace_close(void); #define emscripten_trace_report_off_heap_data() #define emscripten_trace_enter_context(name) #define emscripten_trace_exit_context() +#define emscripten_trace_task_start(task_id, taskname) +#define emscripten_trace_task_associate_data(key, value); +#define emscripten_trace_task_suspend(explanation); +#define emscripten_trace_task_resume(task_id, explanation); +#define emscripten_trace_task_end(); #define emscripten_trace_close() #endif diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index a40935bb56938..b8a789bc20094 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -5,12 +5,20 @@ #include #include + namespace emscripten { namespace internal { // Implemented in JavaScript. Don't call these directly. extern "C" { void _emval_register_symbol(const char*); + enum { + _EMVAL_UNDEFINED = 1, + _EMVAL_NULL = 2, + _EMVAL_TRUE = 3, + _EMVAL_FALSE = 4 + }; + typedef struct _EM_VAL* EM_VAL; typedef struct _EM_DESTRUCTORS* EM_DESTRUCTORS; typedef struct _EM_METHOD_CALLER* EM_METHOD_CALLER; @@ -24,8 +32,6 @@ namespace emscripten { EM_VAL _emval_new_array(); EM_VAL _emval_new_object(); - EM_VAL _emval_undefined(); - EM_VAL _emval_null(); EM_VAL _emval_new_cstring(const char*); EM_VAL _emval_take_value(TYPEID type, EM_VAR_ARGS argv); @@ -42,6 +48,9 @@ namespace emscripten { void _emval_set_property(EM_VAL object, EM_VAL key, EM_VAL value); EM_GENERIC_WIRE_TYPE _emval_as(EM_VAL value, TYPEID returnType, EM_DESTRUCTORS* destructors); + bool _emval_equals(EM_VAL first, EM_VAL second); + bool _emval_strictly_equals(EM_VAL first, EM_VAL second); + EM_VAL _emval_call( EM_VAL value, unsigned argCount, @@ -273,11 +282,11 @@ namespace emscripten { } static val undefined() { - return val(internal::_emval_undefined()); + return val(internal::EM_VAL(internal::_EMVAL_UNDEFINED)); } static val null() { - return val(internal::_emval_null()); + return val(internal::EM_VAL(internal::_EMVAL_NULL)); } static val take_ownership(internal::EM_VAL e) { @@ -306,7 +315,7 @@ namespace emscripten { val() = delete; explicit val(const char* v) - : handle(internal::_emval_new_cstring(v)) + : handle(internal::_emval_new_cstring(v)) {} val(val&& v) @@ -343,6 +352,30 @@ namespace emscripten { return val::global("Object")["prototype"]["hasOwnProperty"].call("call", *this, val(key)); } + bool isNull() const { + return handle == internal::EM_VAL(internal::_EMVAL_NULL); + } + + bool isUndefined() const { + return handle == internal::EM_VAL(internal::_EMVAL_UNDEFINED); + } + + bool isTrue() const { + return handle == internal::EM_VAL(internal::_EMVAL_TRUE); + } + + bool isFalse() const { + return handle == internal::EM_VAL(internal::_EMVAL_FALSE); + } + + bool equals(const val& v) const { + return internal::_emval_equals(handle, v.handle); + } + + bool strictlyEquals(const val& v) const { + return internal::_emval_strictly_equals(handle, v.handle); + } + template val new_(Args&&... args) const { using namespace internal; diff --git a/system/include/emscripten/xmmintrin.h b/system/include/emscripten/xmmintrin.h index 5e4407717b83b..0ee8c265efbc9 100644 --- a/system/include/emscripten/xmmintrin.h +++ b/system/include/emscripten/xmmintrin.h @@ -37,12 +37,32 @@ _mm_load_ps(const float *__p) return *(__m128 *)__p; } +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_loadu_ps(const float *__p) +{ + struct __unaligned { + __m128 __v; + } __attribute__((__packed__, __may_alias__)); + + return ((struct __unaligned *)__p)->__v; +} + static __inline__ void __attribute__((__always_inline__)) _mm_store_ps(float *__p, __m128 __a) { *(__m128 *)__p = __a; } +static __inline__ void __attribute__((__always_inline__)) +_mm_storeu_ps(float *__p, __m128 __a) +{ + struct __unaligned { + __m128 __v; + } __attribute__((__packed__, __may_alias__)); + + ((struct __unaligned *)__p)->__v = __a; +} + static __inline__ int __attribute__((__always_inline__)) _mm_movemask_ps(__m128 __a) { diff --git a/tests/core/test_nested_struct_varargs.c b/tests/core/test_nested_struct_varargs.c new file mode 100644 index 0000000000000..126751eada749 --- /dev/null +++ b/tests/core/test_nested_struct_varargs.c @@ -0,0 +1,45 @@ +#include +#include + +struct A { + int x; +}; + +struct B { + double x; +}; + +struct C { + char c[9]; + struct A a; + struct B b; +}; + +void foo(int unused, ...) +{ + va_list vl; + va_start(vl, unused); + struct C c = va_arg(vl, struct C); + va_end(vl); + + printf("%d\n", c.a.x); + printf("%f\n", c.b.x); + printf("%s\n", c.c); +} + +int main() { + struct A a = { + .x = 42, + }; + struct B b = { + .x = 42.314, + }; + struct C c = { + .a = a, + .b = b, + .c = "nicetest", + }; + foo(0, c); + return 0; +} + diff --git a/tests/core/test_nested_struct_varargs.out b/tests/core/test_nested_struct_varargs.out new file mode 100644 index 0000000000000..10dd54227cade --- /dev/null +++ b/tests/core/test_nested_struct_varargs.out @@ -0,0 +1,3 @@ +42 +42.314000 +nicetest diff --git a/tests/core/test_struct_varargs.c b/tests/core/test_struct_varargs.c new file mode 100644 index 0000000000000..a1fd3bff7a333 --- /dev/null +++ b/tests/core/test_struct_varargs.c @@ -0,0 +1,34 @@ +#include +#include + +struct A { + int x; +}; + +struct B { + double x; +}; + +void foo(int unused, ...) +{ + va_list vl; + va_start(vl, unused); + struct A a = va_arg(vl, struct A); + struct B b = va_arg(vl, struct B); + va_end(vl); + + printf("%d\n", a.x); + printf("%f\n", b.x); +} + +int main() { + struct A a = { + .x = 42, + }; + struct B b = { + .x = 42.314, + }; + foo(0, a, b); + return 0; +} + diff --git a/tests/core/test_struct_varargs.out b/tests/core/test_struct_varargs.out new file mode 100644 index 0000000000000..7cd08beb34c25 --- /dev/null +++ b/tests/core/test_struct_varargs.out @@ -0,0 +1,2 @@ +42 +42.314000 diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js index e52368ba7cc2d..192e2e904cd44 100644 --- a/tests/embind/embind.test.js +++ b/tests/embind/embind.test.js @@ -476,6 +476,65 @@ module({ assert.equal(true, cm.emval_test_not(false)); }); + test("val.is_undefined() is functionnal",function() { + assert.equal(true, cm.emval_test_is_undefined(undefined)); + assert.equal(false, cm.emval_test_is_undefined(true)); + assert.equal(false, cm.emval_test_is_undefined(false)); + assert.equal(false, cm.emval_test_is_undefined(null)); + assert.equal(false, cm.emval_test_is_undefined({})); + }); + + test("val.is_null() is functionnal",function() { + assert.equal(true, cm.emval_test_is_null(null)); + assert.equal(false, cm.emval_test_is_null(true)); + assert.equal(false, cm.emval_test_is_null(false)); + assert.equal(false, cm.emval_test_is_null(undefined)); + assert.equal(false, cm.emval_test_is_null({})); + }); + + test("val.is_true() is functionnal",function() { + assert.equal(true, cm.emval_test_is_true(true)); + assert.equal(false, cm.emval_test_is_true(false)); + assert.equal(false, cm.emval_test_is_true(null)); + assert.equal(false, cm.emval_test_is_true(undefined)); + assert.equal(false, cm.emval_test_is_true({})); + }); + + test("val.is_false() is functionnal",function() { + assert.equal(true, cm.emval_test_is_false(false)); + assert.equal(false, cm.emval_test_is_false(true)); + assert.equal(false, cm.emval_test_is_false(null)); + assert.equal(false, cm.emval_test_is_false(undefined)); + assert.equal(false, cm.emval_test_is_false({})); + }); + + test("val.equals() is functionnal",function() { + values = [undefined, null, true, false, {}]; + + for(var i=0;i(); } @@ -1560,6 +1584,13 @@ EMSCRIPTEN_BINDINGS(tests) { function("emval_test_return_void", &emval_test_return_void); function("emval_test_not", &emval_test_not); + function("emval_test_is_true", &emval_test_is_true); + function("emval_test_is_false", &emval_test_is_false); + function("emval_test_is_null", &emval_test_is_null); + function("emval_test_is_undefined", &emval_test_is_undefined); + function("emval_test_equals", &emval_test_equals); + function("emval_test_strictly_equals", &emval_test_strictly_equals); + function("emval_test_as_unsigned", &emval_test_as_unsigned); function("emval_test_get_length", &emval_test_get_length); function("emval_test_add", &emval_test_add); diff --git a/tests/sdl_audio.c b/tests/sdl_audio.c index 7373d22010fff..7482cb549f419 100644 --- a/tests/sdl_audio.c +++ b/tests/sdl_audio.c @@ -44,7 +44,7 @@ int main(int argc, char **argv) { sound = Mix_LoadWAV("sound.ogg"); assert(sound); - + { struct stat info; int result = stat("noise.ogg", &info); @@ -65,7 +65,7 @@ int main(int argc, char **argv) { sound2 = Mix_LoadWAV("sound2.wav"); - assert(sound); + assert(sound2); int channel = play(); printf( "Pausing Channel %d", channel ); @@ -90,4 +90,3 @@ int main(int argc, char **argv) { return 0; } - diff --git a/tests/sdl_audio_panning.c b/tests/sdl_audio_panning.c new file mode 100644 index 0000000000000..c6bf38270a86a --- /dev/null +++ b/tests/sdl_audio_panning.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +Mix_Chunk *sound; + +void done() { + int result = 1; + REPORT_RESULT(); +} + +void pan() { + static int frames = 0; + frames++; + + float x = (sin(frames / 30.f) + 1) / 2; + + int channel = 0; + int left = x * 255; + int right = (1 - x) * 255; + printf("%f %d %d\n", x, left, right); + int panning = Mix_SetPanning(channel, left, right); + assert(panning != 0); + + if (frames > 30 * 100) + done(); +} + +int play() { + int channel = Mix_PlayChannel(-1, sound, -1); + assert(channel == 0); + + pan(); + + return channel; +} + +Mix_Chunk* +load(const char* filename) +{ + struct stat info; + int result = stat(filename, &info); + char * bytes = malloc( info.st_size ); + FILE * f = fopen( filename, "rb" ); + fread( bytes, 1, info.st_size, f ); + fclose(f); + + SDL_RWops * ops = SDL_RWFromConstMem(bytes, info.st_size); + Mix_Chunk * chunk = Mix_LoadWAV_RW(ops, 0); + SDL_FreeRW(ops); + free(bytes); + + return chunk; +} + +int main(int argc, char **argv) { + SDL_Init(SDL_INIT_AUDIO); + + int ret = Mix_OpenAudio(0, 0, 0, 0); // we ignore all these.. + assert(ret == 0); + + sound = load("the_entertainer.wav"); + assert(sound); + + int channel = play(); + + emscripten_set_main_loop(pan, 30, 0); + + emscripten_run_script("element = document.createElement('input');" + "element.setAttribute('type', 'button');" + "element.setAttribute('value', 'replay!');" + "element.setAttribute('onclick', 'Module[\"_play\"]()');" + "document.body.appendChild(element);"); + + printf("you should hear the sound moving from left to right. press the button to replay!\n"); + + return 0; +} diff --git a/tests/sdl_set_clip_rect.c b/tests/sdl_set_clip_rect.c new file mode 100644 index 0000000000000..8bb4071ee2d7a --- /dev/null +++ b/tests/sdl_set_clip_rect.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#define width 320 +#define height 240 + +int main() { + SDL_Rect rect = {0, 0, width, height}; + SDL_Rect firstRect = {10, 10, 50, 100}; + SDL_Rect secondRect = {30, 50, 100, 100}; + + SDL_Rect firstRectForFill = {240 + 10, 10, 50, 100}; + SDL_Rect secondRectForFill = {240 + 30, 50, 100, 100}; + + SDL_Rect rectForTest = {0, 0, 0, 0}; + SDL_Rect lastRect = { 100, 150, 120, 40}; + + SDL_Init(SDL_INIT_VIDEO); + SDL_Surface *dst = SDL_SetVideoMode(width, height, 32, SDL_HWSURFACE); + SDL_Surface *src = SDL_CreateRGBSurface(0, width, height, 32, + 0x000000ff, + 0x0000ff00, + 0x00ff0000, + 0xff000000); + + /* Fill dst with yellow color */ + SDL_FillRect(src, &rect, SDL_MapRGB(src->format, 255, 255, 0)); + SDL_FillRect(dst, &rect, SDL_MapRGB(dst->format, 0, 0, 0)); + SDL_SetClipRect(dst, NULL); + SDL_BlitSurface(src, &rect, dst, &rect); + + /* Draw red on dst */ + SDL_FillRect(src, &rect, SDL_MapRGB(src->format, 255, 0, 0)); + SDL_SetClipRect(dst, &firstRect); + SDL_BlitSurface(src, &rect, dst, &rect); + + assert(rect.x = firstRect.x); + assert(rect.y = firstRect.y); + assert(rect.w = firstRect.w); + assert(rect.h = firstRect.h); + + /* Draw green rect on red rect */ + SDL_FillRect(src, &rect, SDL_MapRGB(src->format, 0, 255, 0)); + SDL_SetClipRect(dst, &secondRect); + SDL_BlitSurface(src, &rect, dst, &rect); + + assert(rect.x = secondRect.x); + assert(rect.y = secondRect.y); + assert(rect.w = firstRect.x + firstRect.w); + assert(rect.h = firstRect.h + firstRect.h); + + /* Same with fill rect */ + rect.x = 0; rect.y = 0; + rect.w = width; rect.h = height; + + SDL_SetClipRect(dst, &firstRectForFill); + SDL_FillRect(dst, &rect, SDL_MapRGB(dst->format, 0, 0, 255)); + + assert(rect.x = firstRectForFill.x); + assert(rect.y = firstRectForFill.y); + assert(rect.w = firstRectForFill.w); + assert(rect.h = firstRectForFill.h); + + SDL_SetClipRect(dst, &secondRectForFill); + SDL_FillRect(dst, &rect, SDL_MapRGBA(dst->format, 255, 0, 255, 255)); + + assert(rect.x = secondRectForFill.x); + assert(rect.y = secondRectForFill.y); + assert(rect.w = firstRectForFill.x + firstRectForFill.w); + assert(rect.h = firstRectForFill.h + firstRectForFill.h); + + SDL_GetClipRect(dst, &rectForTest); + assert(rectForTest.x == 270); + assert(rectForTest.y == 50); + assert(rectForTest.w == 50); + assert(rectForTest.h == 100); + + SDL_SetClipRect(dst, 0); + SDL_FillRect(dst, &lastRect, SDL_MapRGBA(dst->format, 255, 0, 0, 255)); + SDL_UpdateRect(dst, 0, 0, width, height); + + printf("There should be yellow background\n"); + printf("One the left side there should be red rect with green rect inside\n"); + printf("One the right side there should be blue rect with pink rect inside\n"); + + SDL_Quit(); + + return 0; +} diff --git a/tests/sdl_set_clip_rect.png b/tests/sdl_set_clip_rect.png new file mode 100644 index 0000000000000..bc46ad4cd9866 Binary files /dev/null and b/tests/sdl_set_clip_rect.png differ diff --git a/tests/stdio/test_fgetc_ungetc.c b/tests/stdio/test_fgetc_ungetc.c index 0eea4007b0b5a..9a9b7cae3bd5c 100644 --- a/tests/stdio/test_fgetc_ungetc.c +++ b/tests/stdio/test_fgetc_ungetc.c @@ -5,6 +5,7 @@ #include #include #include +#include static void create_file(const char *path, const char *buffer, int mode) { int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, mode); @@ -17,11 +18,11 @@ static void create_file(const char *path, const char *buffer, int mode) { } void setup() { - create_file("file.txt", "cd", 0666); + create_file("/tmp/file.txt", "cd", 0666); } void cleanup() { - unlink("file.txt"); + unlink("/tmp/file.txt"); } void test() { @@ -29,7 +30,7 @@ void test() { int err; char buffer[256]; - file = fopen("file.txt", "r"); + file = fopen("/tmp/file.txt", "r"); assert(file); // pushing EOF always returns EOF @@ -82,6 +83,11 @@ void test() { } int main() { +#ifdef NODEFS + EM_ASM(FS.mount(NODEFS, { root: '.' }, '/tmp')); +#elif MEMFS + EM_ASM(FS.mount(MEMFS, {}, '/tmp')); +#endif atexit(cleanup); signal(SIGABRT, cleanup); setup(); diff --git a/tests/test_browser.py b/tests/test_browser.py index 0b7744bf3d622..51e0e41d80f82 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -1620,6 +1620,9 @@ def test_sdl_canvas_palette(self): def test_sdl_canvas_twice(self): self.btest('sdl_canvas_twice.c', reference='sdl_canvas_twice.png') + def test_sdl_set_clip_rect(self): + self.btest('sdl_set_clip_rect.c', reference='sdl_set_clip_rect.png') + def test_sdl_maprgba(self): self.btest('sdl_maprgba.c', reference='sdl_maprgba.png', reference_slack=3) diff --git a/tests/test_core.py b/tests/test_core.py index 1231155b7cace..b94a10a00f984 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -435,6 +435,18 @@ def test_double_varargs(self): src, output = (test_path + s for s in ('.c', '.out')) self.do_run_from_file(src, output) + def test_struct_varargs(self): + if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('struct varargs requires fastcomp') + test_path = path_from_root('tests', 'core', 'test_struct_varargs') + src, output = (test_path + s for s in ('.c', '.out')) + self.do_run_from_file(src, output) + + def zzztest_nested_struct_varargs(self): + if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('struct varargs requires fastcomp') + test_path = path_from_root('tests', 'core', 'test_nested_struct_varargs') + src, output = (test_path + s for s in ('.c', '.out')) + self.do_run_from_file(src, output) + def test_i32_mul_precise(self): if self.emcc_args == None: return self.skip('needs ta2') @@ -4264,8 +4276,13 @@ def test_fwrite_0(self): self.do_run_from_file(src, output) def test_fgetc_ungetc(self): - src = open(path_from_root('tests', 'stdio', 'test_fgetc_ungetc.c'), 'r').read() - self.do_run(src, 'success', force_c=True) + self.clear() + if not self.is_emscripten_abi(): return self.skip('asmjs-unknown-emscripten needed for inline js') + orig_compiler_opts = Building.COMPILER_TEST_OPTS[:] + for fs in ['MEMFS', 'NODEFS']: + src = open(path_from_root('tests', 'stdio', 'test_fgetc_ungetc.c'), 'r').read() + Building.COMPILER_TEST_OPTS = orig_compiler_opts + ['-D' + fs] + self.do_run(src, 'success', force_c=True, js_engines=[NODE_JS]) def test_fgetc_unsigned(self): if self.emcc_args is None: return self.skip('requires emcc') @@ -4542,10 +4559,11 @@ def test_fs_append(self): def test_unistd_access(self): self.clear() if not self.is_emscripten_abi(): return self.skip('asmjs-unknown-emscripten needed for inline js') + orig_compiler_opts = Building.COMPILER_TEST_OPTS[:] for fs in ['MEMFS', 'NODEFS']: src = open(path_from_root('tests', 'unistd', 'access.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'access.out'), 'r').read() - Building.COMPILER_TEST_OPTS += ['-D' + fs] + Building.COMPILER_TEST_OPTS = orig_compiler_opts + ['-D' + fs] self.do_run(src, expected, js_engines=[NODE_JS]) def test_unistd_curdir(self): @@ -4582,10 +4600,11 @@ def test_unistd_pathconf(self): def test_unistd_truncate(self): self.clear() if not self.is_emscripten_abi(): return self.skip('asmjs-unknown-emscripten needed for inline js') + orig_compiler_opts = Building.COMPILER_TEST_OPTS[:] for fs in ['MEMFS', 'NODEFS']: src = open(path_from_root('tests', 'unistd', 'truncate.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'truncate.out'), 'r').read() - Building.COMPILER_TEST_OPTS += ['-D' + fs] + Building.COMPILER_TEST_OPTS = orig_compiler_opts + ['-D' + fs] self.do_run(src, expected, js_engines=[NODE_JS]) def test_unistd_swab(self): @@ -4611,14 +4630,16 @@ def test_unistd_unlink(self): self.clear() if self.emcc_args is None: return self.skip('requires emcc') if not self.is_emscripten_abi(): return self.skip('asmjs-unknown-emscripten needed for inline js') + orig_compiler_opts = Building.COMPILER_TEST_OPTS[:] for fs in ['MEMFS', 'NODEFS']: src = open(path_from_root('tests', 'unistd', 'unlink.c'), 'r').read() - Building.COMPILER_TEST_OPTS += ['-D' + fs] + Building.COMPILER_TEST_OPTS = orig_compiler_opts + ['-D' + fs] self.do_run(src, 'success', force_c=True, js_engines=[NODE_JS]) def test_unistd_links(self): self.clear() if not self.is_emscripten_abi(): return self.skip('asmjs-unknown-emscripten needed for inline js') + orig_compiler_opts = Building.COMPILER_TEST_OPTS[:] for fs in ['MEMFS', 'NODEFS']: if WINDOWS and fs == 'NODEFS': print >> sys.stderr, 'Skipping NODEFS part of this test for test_unistd_links on Windows, since it would require administrative privileges.' @@ -4628,7 +4649,7 @@ def test_unistd_links(self): continue src = open(path_from_root('tests', 'unistd', 'links.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'links.out'), 'r').read() - Building.COMPILER_TEST_OPTS += ['-D' + fs] + Building.COMPILER_TEST_OPTS = orig_compiler_opts + ['-D' + fs] self.do_run(src, expected, js_engines=[NODE_JS]) def test_unistd_sleep(self): @@ -4641,19 +4662,21 @@ def test_unistd_io(self): if not self.is_emscripten_abi(): return self.skip('asmjs-unknown-emscripten needed for inline js') if self.run_name == 'o2': return self.skip('non-asm optimized builds can fail with inline js') if self.emcc_args is None: return self.skip('requires emcc') + orig_compiler_opts = Building.COMPILER_TEST_OPTS[:] for fs in ['MEMFS', 'NODEFS']: src = open(path_from_root('tests', 'unistd', 'io.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'io.out'), 'r').read() - Building.COMPILER_TEST_OPTS += ['-D' + fs] + Building.COMPILER_TEST_OPTS = orig_compiler_opts + ['-D' + fs] self.do_run(src, expected, js_engines=[NODE_JS]) def test_unistd_misc(self): if self.emcc_args is None: return self.skip('requires emcc') if not self.is_emscripten_abi(): return self.skip('asmjs-unknown-emscripten needed for inline js') + orig_compiler_opts = Building.COMPILER_TEST_OPTS[:] for fs in ['MEMFS', 'NODEFS']: src = open(path_from_root('tests', 'unistd', 'misc.c'), 'r').read() expected = open(path_from_root('tests', 'unistd', 'misc.out'), 'r').read() - Building.COMPILER_TEST_OPTS += ['-D' + fs] + Building.COMPILER_TEST_OPTS = orig_compiler_opts + ['-D' + fs] self.do_run(src, expected, js_engines=[NODE_JS]) def test_posixtime(self): diff --git a/tests/test_html5_fullscreen.c b/tests/test_html5_fullscreen.c index 54b834bdf81a7..0b9f8acf22bf0 100644 --- a/tests/test_html5_fullscreen.c +++ b/tests/test_html5_fullscreen.c @@ -2,6 +2,8 @@ #include #include #include +#include +#include void report_result(int result) { @@ -64,7 +66,9 @@ EM_BOOL key_callback(int eventType, const EmscriptenKeyboardEvent *e, void *user } } } - + else if (eventType == EMSCRIPTEN_EVENT_KEYPRESS && (!strcmp(e->key, "Esc") || !strcmp(e->key, "Escape") || e->which == 27)) { + emscripten_exit_soft_fullscreen(); + } return 0; } @@ -95,8 +99,119 @@ EM_BOOL mouse_callback(int eventType, const EmscriptenMouseEvent *e, void *userD 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); @@ -116,11 +231,27 @@ int main() 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"); - /* 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); + emscripten_set_main_loop(draw, 0, 0); return 0; } diff --git a/tests/test_html5_fullscreen.html b/tests/test_html5_fullscreen.html new file mode 100644 index 0000000000000..b131f987043d5 --- /dev/null +++ b/tests/test_html5_fullscreen.html @@ -0,0 +1,177 @@ + + + + + + Emscripten-Generated Code + + + +
+
emscripten
+
Downloading...
+
+ +
+
+ +
+
+
+ Resize canvas + Lock/hide mouse pointer +     + +
+ +
+ +
+ + {{{ SCRIPT }}} + + +
+ This simply requests fullscreen, use when no extra behavior is desirable. This is subject to a wild number of per-browser differences, see https://github.com/kripken/emscripten/issues/2556.
+ Stretch the WebGL render target to cover the whole screen in pixel-perfect manner, in standard definition.
+ Same as above, but use the actual native display resolution, instead of CSS resolution.
+ Stretch the WebGL render target to cover the full screen, but retain aspect ratio.
+ Same as above, but use the actual native display resolution, instead of CSS resolution.
+ Don't resize WebGL render target, and don't care about aspect ratio, simply stretch over the whole screen. (current Firefox and IE default behavior)
+ Don't resize WebGL render target, but scale over the whole screen, preserving aspect ratio.
+ Same as above, but perform pixelated nearest-neighbor filtering instead of bilinear filtering.
+ Don't resize the WebGL render target and don't scale the displayed content, but show it full screen. (current Chrome and Safari default behavior)
+
+
+
+ The Soft fullscreen modes are otherwise exactly like the above, except that they don't actually request fullscreen, but instead they present the canvas maximized in the client area of the page. This is more desirable in Firefox OS mobile packaged apps, where the application is already displayed full screen. Also it allows client desktop apps to use F11 for transitioning between fullscreen mode.
+
+
+
+
+
+
+
+
+
+
+ + diff --git a/tests/test_interactive.py b/tests/test_interactive.py index 8ebf7bf5f25cd..3e567059ac8eb 100644 --- a/tests/test_interactive.py +++ b/tests/test_interactive.py @@ -20,7 +20,7 @@ def setUpClass(self): print def test_html5_fullscreen(self): - self.btest(path_from_root('tests', 'test_html5_fullscreen.c'), expected='0') + self.btest(path_from_root('tests', 'test_html5_fullscreen.c'), expected='0', args=['-s', 'EXPORTED_FUNCTIONS=["_requestFullscreen","_enterSoftFullscreen","_main"]']) def test_html5_mouse(self): self.btest(path_from_root('tests', 'test_html5_mouse.c'), expected='0') @@ -62,6 +62,14 @@ def test_sdl_audio_mix(self): Popen([PYTHON, EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_mix.c'), '--preload-file', 'sound.ogg', '--preload-file', 'music.ogg', '--preload-file', 'noise.ogg', '-o', 'page.html']).communicate() self.run_browser('page.html', '', '/report_result?1') + def test_sdl_audio_panning(self): + shutil.copyfile(path_from_root('tests', 'sounds', 'the_entertainer.wav'), os.path.join(self.get_dir(), 'the_entertainer.wav')) + open(os.path.join(self.get_dir(), 'sdl_audio_panning.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_panning.c')).read())) + + # use closure to check for a possible bug with closure minifying away newer Audio() attributes + Popen([PYTHON, EMCC, '-O2', '--closure', '1', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_panning.c'), '--preload-file', 'the_entertainer.wav', '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play"]']).communicate() + self.run_browser('page.html', '', '/report_result?1') + def test_sdl_audio_beeps(self): open(os.path.join(self.get_dir(), 'sdl_audio_beep.cpp'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_beep.cpp')).read())) diff --git a/tests/test_other.py b/tests/test_other.py index 25ccfffc006d5..b444a476f2d1a 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -4383,3 +4383,9 @@ def test_link_with_a_static(self): Popen([PYTHON, EMCC, 'z.o', 'libtest.a'] + args).communicate() out = run_js('a.out.js', assert_returncode=161) + def test_require(self): + inname = path_from_root('tests', 'hello_world.c') + Building.emcc(inname, output_filename='a.out.js') + output = Popen(listify(NODE_JS) + ['-e', 'require("./a.out.js")'], stdout=PIPE, stderr=PIPE).communicate() + assert output == ('hello, world!\n \n', ''), 'expected no output, got\n===\nSTDOUT\n%s\n===\nSTDERR\n%s\n===\n' % output + diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index dfb2ace9e281d..f348811370046 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -7611,7 +7611,7 @@ function asmLastOpts(ast) { // Passes table -var minifyWhitespace = false, printMetadata = true, asm = false, asmPreciseF32 = false, last = false; +var minifyWhitespace = false, printMetadata = true, asm = false, asmPreciseF32 = false, emitJSON = false, last = false; var passes = { // passes @@ -7648,6 +7648,8 @@ var passes = { noPrintMetadata: function() { printMetadata = false }, asm: function() { asm = true }, asmPreciseF32: function() { asmPreciseF32 = true }, + emitJSON: function() { emitJSON = true }, + receiveJSON: function() { }, // handled in a special way, before passes are run last: function() { last = true }, }; @@ -7664,13 +7666,22 @@ arguments_ = arguments_.filter(function (arg) { var src = read(arguments_[0]); -var ast = srcToAst(src); -//printErr(JSON.stringify(ast)); throw 1; generatedFunctions = src.lastIndexOf(GENERATED_FUNCTIONS_MARKER) >= 0; var extraInfoStart = src.lastIndexOf('// EXTRA_INFO:') if (extraInfoStart > 0) extraInfo = JSON.parse(src.substr(extraInfoStart + 14)); //printErr(JSON.stringify(extraInfo)); +var ast; +if (arguments_.indexOf('receiveJSON') < 0) { + ast = srcToAst(src); +} else { + var commentStart = src.indexOf('//'); + if (commentStart >= 0) { + src = src.substr(0, commentStart); // JSON.parse will error on a trailing comment + } + ast = JSON.parse(src); +} +//printErr('ast: ' + JSON.stringify(ast)); var emitAst = true; @@ -7690,19 +7701,22 @@ if (asm && last) { } if (emitAst) { - var js = astToSrc(ast, minifyWhitespace), old; - if (asm && last) { - js = fixDotZero(js); + if (!emitJSON) { + var js = astToSrc(ast, minifyWhitespace), old; + if (asm && last) { + js = fixDotZero(js); + } + // remove unneeded newlines+spaces, and print + do { + old = js; + js = js.replace(/\n *\n/g, '\n'); + } while (js != old); + print(js); + print('\n'); + print(suffix); + } else { + print(JSON.stringify(ast)); } - - // remove unneeded newlines+spaces, and print - do { - old = js; - js = js.replace(/\n *\n/g, '\n'); - } while (js != old); - print(js); - print('\n'); - print(suffix); } else { //print('/* not printing ast */'); } diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py index e658e29f7ec8d..2ec01c2d8da74 100644 --- a/tools/js_optimizer.py +++ b/tools/js_optimizer.py @@ -100,7 +100,7 @@ def run_on_chunk(command): # avoid throwing keyboard interrupts from a child process raise Exception() -def run_on_js(filename, passes, js_engine, jcache, source_map=False, extra_info=None, just_concat=False): +def run_on_js(filename, passes, js_engine, jcache, source_map=False, extra_info=None, just_split=False, just_concat=False): if isinstance(jcache, bool) and jcache: jcache = shared.JCache if jcache: shared.JCache.ensure() @@ -199,7 +199,8 @@ def check_symbol_mapping(p): pre = '' post = '' - def split_funcs(js): + def split_funcs(js, just_split=False): + if just_split: return map(lambda line: ('(json)', line), js.split('\n')) # Pick where to split into chunks, so that (1) they do not oom in node/uglify, and (2) we can run them in parallel # If we have metadata, we split only the generated code, and save the pre and post on the side (and do not optimize them) parts = map(lambda part: part, js.split('\n}\n')) @@ -218,7 +219,7 @@ def split_funcs(js): return funcs total_size = len(js) - funcs = split_funcs(js) + funcs = split_funcs(js, just_split) js = None # if we are making source maps, we want our debug numbering to start from the @@ -228,6 +229,7 @@ def split_funcs(js): chunk_size = min(MAX_CHUNK_SIZE, max(MIN_CHUNK_SIZE, total_size / intended_num_chunks)) chunks = shared.chunkify(funcs, chunk_size, jcache.get_cachename('jsopt') if jcache else None) + chunks = filter(lambda chunk: len(chunk) > 0, chunks) if DEBUG and len(chunks) > 0: print >> sys.stderr, 'chunkification: intended size:', chunk_size, 'num funcs:', len(funcs), 'actual num chunks:', len(chunks), 'chunk size range:', max(map(len, chunks)), '-', min(map(len, chunks)) funcs = None @@ -376,9 +378,9 @@ def sorter(x, y): return filename -def run(filename, passes, js_engine=shared.NODE_JS, jcache=False, source_map=False, extra_info=None, just_concat=False): +def run(filename, passes, js_engine=shared.NODE_JS, jcache=False, source_map=False, extra_info=None, just_split=False, just_concat=False): js_engine = shared.listify(js_engine) - return temp_files.run_and_clean(lambda: run_on_js(filename, passes, js_engine, jcache, source_map, extra_info, just_concat)) + return temp_files.run_and_clean(lambda: run_on_js(filename, passes, js_engine, jcache, source_map, extra_info, just_split, just_concat)) if __name__ == '__main__': last = sys.argv[-1] diff --git a/tools/ports/__init__.py b/tools/ports/__init__.py index 113c141bfba05..bdd81383b6f71 100644 --- a/tools/ports/__init__.py +++ b/tools/ports/__init__.py @@ -1,4 +1,4 @@ -import sdl, sdl_image +import sdl, sdl_image, zlib -ports = [sdl, sdl_image] +ports = [sdl, sdl_image, zlib] diff --git a/tools/ports/sdl_image.py b/tools/ports/sdl_image.py index 9a62a897525e1..ad0474a6e8146 100644 --- a/tools/ports/sdl_image.py +++ b/tools/ports/sdl_image.py @@ -1,6 +1,6 @@ import os, shutil, logging -VERSION = 2 +VERSION = 3 def get(ports, settings, shared): if settings.USE_SDL_IMAGE == 2: diff --git a/tools/ports/zlib.py b/tools/ports/zlib.py new file mode 100644 index 0000000000000..27952939ef62e --- /dev/null +++ b/tools/ports/zlib.py @@ -0,0 +1,22 @@ +import os, shutil, logging + +VERSION = 1 + +def get(ports, settings, shared): # not currently used; no real need for configure on emscripten users' machines! + if settings.USE_ZLIB == 1: + ports.fetch_project('zlib', 'https://github.com/emscripten-ports/zlib/archive/master.zip', VERSION) + return [ports.build_project('zlib', 'zlib-master', + ['sh', './configure'], + ['libz.a'])] + else: + return [] + +def process_args(ports, args, settings, shared): + if settings.USE_ZLIB == 1: + get(ports, settings, shared) + args += ['-Xclang', '-isystem' + os.path.join(shared.Cache.get_path('ports-builds'), 'zlib')] + return args + +def show(): + return 'zlib (zlib license)' + diff --git a/tools/shared.py b/tools/shared.py index 85e8a906c0209..92b57910f8df0 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -1599,8 +1599,8 @@ def pick_llvm_opts(optimization_level): return opts @staticmethod - def js_optimizer(filename, passes, jcache=False, debug=False, extra_info=None, output_filename=None, just_concat=False): - ret = js_optimizer.run(filename, passes, listify(NODE_JS), jcache, debug, extra_info, just_concat) + def js_optimizer(filename, passes, jcache=False, debug=False, extra_info=None, output_filename=None, just_split=False, just_concat=False): + ret = js_optimizer.run(filename, passes, listify(NODE_JS), jcache, debug, extra_info, just_split, just_concat) if output_filename: safe_move(ret, output_filename) ret = output_filename diff --git a/tools/system_libs.py b/tools/system_libs.py index 210735e5f936a..e0f6fecabbc86 100644 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -11,7 +11,8 @@ def call_process(cmd): proc = Popen(cmd, stdout=stdout, stderr=stderr) proc.communicate() if proc.returncode != 0: - raise CalledProcessError(proc.returncode, cmd) + # Deliberately do not use CalledProcessError, see issue #2944 + raise Exception('Command \'%s\' returned non-zero exit status %s' % (cmd, proc.returncode)) CORES = int(os.environ.get('EMCC_CORES') or multiprocessing.cpu_count())