diff options
author | Sven Gothel <[email protected]> | 2023-06-30 11:36:33 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2023-06-30 11:36:33 +0200 |
commit | a73c992290930e617c78241bae9fe20cb18a01a9 (patch) | |
tree | 85115a5cf25d8b5018a8c2d78244272f504851e0 /doc/GlueGen_Mapping.html | |
parent | b35d62425311ec50e6c80b07372bc411aa287bb4 (diff) |
GlueGen JavaCallback: Resolve key mapping of callback and associated resources via 'JavaCallbackKey' config and custom `SetCallback-KeyClass`
Updated unit test and doc accordingly.
Unit tests handle OpenAL's AL_SOFT_callback_buffer and AL_SOFT_events.
Tested global scope (no key, default) and 1 key (default) and 1 key (custom class).
Added more query functions, which all only take the `SetCallbackFunction` key arguments as specified.
Cleaned up JavaCallback* config class field naminig scheme.
Added 'synchronized (..Map) { }' block in crucial `SetCallbackFunction`,
rendering implementation thread safe.
Diffstat (limited to 'doc/GlueGen_Mapping.html')
-rw-r--r-- | doc/GlueGen_Mapping.html | 313 |
1 files changed, 280 insertions, 33 deletions
diff --git a/doc/GlueGen_Mapping.html b/doc/GlueGen_Mapping.html index 5881a9d..2bbc07a 100644 --- a/doc/GlueGen_Mapping.html +++ b/doc/GlueGen_Mapping.html @@ -452,8 +452,16 @@ <li><a href="#java-callback-from-native-c-api-support">Java Callback from Native C-API Support</a> <ul> - <li><a href="#javacallback-constraints">JavaCallback - Constraints</a></li> + <li><a href="#required-libraryonload">Required + <em>LibraryOnLoad</em></a></li> + <li><a href="#javacallback-configuration"><em>JavaCallback</em> + Configuration</a></li> + <li><a href="#javacallback-notes"><em>JavaCallback</em> Notes</a></li> + <li><a href="#javacallback-example-1">JavaCallback Example 1</a></li> + <li><a href="#javacallback-example-2a-default-keyclass">JavaCallback + Example 2a (Default <em>KeyClass</em>)</a></li> + <li><a href="#javacallback-example-2b-custom-keyclass">JavaCallback + Example 2b (Custom <em>KeyClass</em>)</a></li> </ul></li> <li><a href="#misc-configurations">Misc Configurations</a> <ul> @@ -1929,6 +1937,126 @@ Native C-API Support</h2> <p>GlueGen supports registering Java callback methods to receive asynchronous and off-thread native toolkit events, where a generated native callback function dispatches the events to Java.</p> +<h3 id="required-libraryonload">Required <em>LibraryOnLoad</em></h3> +<p>Note that <a +href="#libraryonload-librarybasename-for-jni_onload-"><code>LibraryOnLoad Bindingtest2</code></a> +must be specified in exactly one native code-unit. It provides code to +allow the generated native callback-function to attach the current +thread to the <code>JavaVM*</code> generating a new +<code>JNIEnv*</code>in daemon mode - or just to retrieve the thread's +<code>JNIEnv*</code>, if already attached to the +<code>JavaVM*</code>.</p> +<h3 id="javacallback-configuration"><em>JavaCallback</em> +Configuration</h3> +<p>Configuration directives are as follows:</p> +<pre><code>JavaCallbackDef <SetCallbackFunctionName> <CallbackFunctionType> <CallbackFunction-UserParamIndex> [<SetCallback-KeyClassName>] +JavaCallbackKey <SetCallbackFunctionName> (SetCallback-ParamIdx)*</code></pre> +<p><code>JavaCallbackDef</code> and <code>JavaCallbackKey</code> use the +name of the <code>SetCallbackFunction</code> as its first attribute, as +it is core to the semantic mapping of all resources.</p> +<p><code>JavaCallbackDef</code> attributes:</p> +<ul> +<li><code>SetCallbackFunction</code>: <code>SetCallbackFunction</code> +name of the native toolkit API responsible to set the callback</li> +<li><code>CallbackFunctionType</code>: The native toolkit API +typedef-name of the function-pointer-type, aka the callback type +name</li> +<li><code>CallbackFunction-UserParamIndex</code>: The +<code>userParam</code> parameter-index of the +<code>CallbackFunctionType</code></li> +<li><code>SetCallback-KeyClassName</code>: Name of an optional +user-implemented <code>SetCallback-KeyClass</code>, providing the +hash-map-key - see below</li> +</ul> +<p>The <code>SetCallbackFunction</code> is utilized to set the +<code>CallbackFunction</code> as well as to remove it passing +<code>null</code> for the <code>CallbackFunction</code>.</p> +<p>If mapping the <code>CallbackFunction</code> to keys, the user must +specify the same key arguments when setting and removing the +``CallbackFunction`.</p> +<h4 id="javacallback-key-definition"><em>JavaCallback</em> Key +Definition</h4> +<p>If no keys are defined via <code>JavaCallbackKey</code>, or manually +injected using a custom <code>SetCallback-KeyClass</code>, see below, +the <code>CallbackFunction</code> has global scope.</p> +<p>Keys allow to limit the scope, i.e. map multiple +<code>CallbackFunction</code> to the different keys.</p> +<p>Key arguments must match in <code>SetCallbackFunction</code> to +remove a previously set <code>CallbackFunction</code>.</p> +<p><code>JavaCallbackKey</code> attributes</p> +<ul> +<li><code>SetCallbackFunction</code>: <code>SetCallbackFunction</code> +name of the native toolkit API responsible to set the callback</li> +<li><code>SetCallback-ParamIdx</code>: List of parameter indices of the +<code>SetCallbackFunction</code>, denoting the key(s) limiting the +callback scope, i.e. the callback and all resources will be mapped to +this key. The optional <code>SetCallback-KeyClass</code> may override +this semantic.</li> +</ul> +<p>Beside generating the actual function mapping of the API, additional +query methods are generated, passing the keys as its paramters</p> +<ul> +<li><code>boolean is<SetCallbackFunctionName>Mapped((key-arg)*)</code> +queries whether <code>SetCallbackFunctionName</code> is mapped.</li> +<li><code>ALBUFFERCALLBACKTYPESOFT get<SetCallbackFunctionName>((key-arg)*)</code> +returns the mapped <code>CallbackFunction</code>, null if not +mapped</li> +<li><code>Object get<SetCallbackFunctionName>UserParam((key-arg)*)</code> +returns the mapped <code>userParam</code> object, null if not +mapped</li> +</ul> +<h4 id="custom-setcallback-keyclass">Custom +<code>SetCallback-KeyClass</code></h4> +<p>The <code>SetCallback-KeyClass</code> is the optional user-written +hash-map-key definition and shall handle all key parameter of the +<code>SetCallbackFunction</code> as defined via +<code>JavaCallbackKey</code>, see above.</p> +<p><code>SetCallback-KeyClass</code> may be used to add external +key-components, e.g. current-thread or a toolkit dependent context.</p> +<p>The <code>SetCallback-KeyClass</code> shall implement the following +hash-map-key standard methods</p> +<ul> +<li><code>boolean equals(Object)</code></li> +<li><code>int hashCode()</code></li> +<li><code>SetCallback-KeyClassName(...)</code> constructor receiving all +key parameter of <code>SetCallbackFunction</code> as defined via +<code>JavaCallbackKey</code>, see above.</li> +</ul> +<h3 id="javacallback-notes"><em>JavaCallback</em> Notes</h3> +<p>Please consider the following <em>currently enabled</em> constraints +using JavaCallback</p> +<ul> +<li>Only one interface callback-method binding is allowed for a native +callback function, e.g. <code>T2_CallbackFunc01</code> (see above) +<ul> +<li>Implying that the native single function-pointer typedef must be +mapped to a single Java method within its interface</li> +<li>Hence it must be avoided that multiple method variation are +produced, e.g. due to <code>char*</code> to <code>byte[]</code> and +<code>String</code> mapping etc.</li> +</ul></li> +<li>The native callback function can only return no-value, i.e. +<code>void</code>, or a primitive type. Usually <code>void</code> is +being used in toolkit APIs.</li> +<li>The native callback function argument types must be convertible to +JNI Java types as (previously) supported for function return values, +using the same conversion function +<code>CMethodBindingEmitter.emitBodyMapCToJNIType(..)</code>.</li> +<li>To remove a JavaCallback the <code>SetCallbackFunction</code> must +be called with <code>null</code> for the <code>CallbackFunction</code> +argument but with the same <a +href="#javacallback-key-definition"><em>key arguments</em> (see +<code>JavaCallbackKey</code>)</a> as previously called to set the +callback.</li> +<li>Exactly one native code-unit for the library must specify <a +href="#libraryonload-librarybasename-for-jni_onload-"><code>LibraryOnLoad libraryBasename</code></a></li> +<li><code>SetCallbackFunction</code> is thread safe</li> +<li>...</li> +</ul> +<h3 id="javacallback-example-1">JavaCallback Example 1</h3> +<p>This is a generic example.</p> +<p>The callback <code>T2_CallbackFunc01</code> has global scope, i.e. is +not mapped to any key and can be only set globally.</p> <p>C-API header snippet:</p> <pre><code>typedef void ( * T2_CallbackFunc01)(size_t id, const char* msg, void* usrParam); @@ -1942,16 +2070,19 @@ LibraryOnLoad Bindingtest2 ArgumentIsString T2_CallbackFunc01 1 ArgumentIsString InjectMessageCallback01 1 - + # Define a JavaCallback. -# Set JavaCallback via function `MessageCallback01` if `T2_CallbackFunc01` argument is non-null, otherwise removes the callback and associated resources. -# It uses `usrParam` as the resource-key to map to the hidden native-usrParam object, -# hence a matching 'usrParam' must be passed for setting and removal of the callback. +# Set JavaCallback via function `MessageCallback01` if `T2_CallbackFunc01` argument is non-null, otherwise removes the mapped callback and associated resources. # # It uses the function-pointer argument `T2_CallbackFunc01` as the callback function type -# and marks `T2_CallbackFunc01`s 3rd argument (index 2) as the mandatory user-param for Java Object mapping. +# and marks `T2_CallbackFunc01`s 3rd argument (index 2) as the mandatory user-param. +# +# This callback has no keys defines, rendering it of global scope! # -# Note: An explicit `isMessageCallback01Mapped(Object usrParam)` is being created to explicitly query whether `usrParam` maps to the associated resources. +# Explicit queries are generated, passing the keys as paramters +# - `boolean isMessageCallback01Mapped()` queries whether `MessageCallback0` is mapped globally +# - `T2_CallbackFunc01 getMessageCallback01()` returns the global T2_CallbackFunc01, null if not mapped +# - `Object getMessageCallback01UserParam()` returns the global `usrParam` object, null if not mapped JavaCallbackDef MessageCallback01 T2_CallbackFunc01 2</code></pre> <p>Note that <a href="#libraryonload-librarybasename-for-jni_onload-"><code>LibraryOnLoad Bindingtest2</code></a> @@ -1961,7 +2092,7 @@ thread to the <code>JavaVM*</code> generating a new <code>JNIEnv*</code>in daemon mode - or just to retrieve the thread's <code>JNIEnv*</code>, if already attached to the <code>JavaVM*</code>.</p> -<p>This will lead to the following result</p> +<p>This will lead to the following interface</p> <pre><code>public interface Bindingtest2 { /** JavaCallback interface: T2_CallbackFunc01 -> void (*T2_CallbackFunc01)(size_t id, const char * msg, void * usrParam) */ @@ -1974,33 +2105,149 @@ thread to the <code>JavaVM*</code> generating a new /** Entry point (through function pointer) to C language function: <br> <code>void MessageCallback01(T2_CallbackFunc01 cbFunc, void * usrParam)</code><br> */ public void MessageCallback01(T2_CallbackFunc01 cbFunc, Object usrParam); - - public boolean isMessageCallback01Mapped(final Object usrParam); + + public boolean isMessageCallback01Mapped(); + public T2_CallbackFunc01 getMessageCallback01(); + public Object getMessageCallback01UserParam(); /** Entry point (through function pointer) to C language function: <br> <code>void InjectMessageCallback01(size_t id, const char * msg)</code><br> */ public void InjectMessageCallback01(long id, String msg);</code></pre> -<h3 id="javacallback-constraints">JavaCallback Constraints</h3> -<p>Please consider the following <em>currently enabled</em> constraints -using <code>JavaCallbackDef</code></p> -<ul> -<li>Only one interface callback-method binding is allowed for a native -callback function, e.g. <code>T2_CallbackFunc01</code> (see above).</li> -<li>The native callback function can only return no-value, i.e. -<code>void</code>, or a primitive type. Usually <code>void</code> is -being used in toolkit APIs.</li> -<li>The native callback function argument types must be able to be -mapped to JNI Java types as supported for return values of all native -functions, the same code path is being used within -<code>CMethodBindingEmitter.emitBodyMapCToJNIType(..)</code>.</li> -<li>To remove a JavaCallback the specified and mapped setter function, -e.g. <code>MessageCallback01</code>, must be called with -<code>null</code> for the callback interface but the very same -<code>userParam</code> instance as previously called to set the -callback.</li> -<li>Exactly one native code-unit for the library must specify <a -href="#libraryonload-librarybasename-for-jni_onload-"><code>LibraryOnLoad libraryBasename</code></a></li> -<li>...</li> -</ul> +<p>Implementation utilizes the default <code>SetCallback-KeyClass</code> +implementation for +<code>void MessageCallback01(T2_CallbackFunc01 cbFunc, Object usrParam)</code>, +which is key-less and hence minimalistic.</p> +<pre><code> private static class MessageCallback01Key { + MessageCallback01Key() { + } + @Override + public boolean equals(final Object o) { + if( this == o ) { + return true; + } + if( !(o instanceof MessageCallback01Key) ) { + return false; + } + return true; + } + @Override + public int hashCode() { + return 0; + } + }</code></pre> +<h3 id="javacallback-example-2a-default-keyclass">JavaCallback Example +2a (Default <em>KeyClass</em>)</h3> +<p>This examples is derived from OpenAL's +<code>AL_SOFT_callback_buffer</code> extension.</p> +<p>The callback <code>ALBUFFERCALLBACKTYPESOFT</code> is mapped to +<code>buffer</code> name, i.e. one callback can be set for each +buffer.</p> +<p>C-API Header snipped</p> +<pre><code> typedef void ( * ALBUFFERCALLBACKTYPESOFT)(int buffer, void *userptr, int sampledata, int numbytes); + + void alBufferCallback0(int buffer /* key */, int format, int freq, ALBUFFERCALLBACKTYPESOFT callback, void *userptr); + + void alBufferCallback0Inject(int buffer, int sampledata, int numbytes);</code></pre> +<p>and the following GlueGen configuration</p> +<pre><code> # Define a JavaCallback. + # Set JavaCallback via function `alBufferCallback0` if `ALBUFFERCALLBACKTYPESOFT` argument is non-null, otherwise removes the mapped callback and associated resources. + # + # It uses the function-pointer argument `ALBUFFERCALLBACKTYPESOFT` as the callback function type + # and marks `ALBUFFERCALLBACKTYPESOFT`s 2nd argument (index 1) as the mandatory user-param. + # + # This callback defines one key, `buffer`, index 0 of alBufferCallback0(..) parameter list, limiting it to buffer-name scope! + # The `buffer` key allows setting one callback per buffer-name, compatible with the `AL_SOFT_callback_buffer` spec. + # + # Explicit queries are generated, passing the keys as paramters + # - `boolean isAlBufferCallback0Mapped(int buffer)` queries whether `alBufferCallback0` is mapped to `buffer`. + # - `ALBUFFERCALLBACKTYPESOFT getAlBufferCallback0(int buffer)` returns the `buffer` mapped ALEVENTPROCSOFT, null if not mapped + # - `Object getAlBufferCallback0UserParam(int buffer)` returns the `buffer` mapped `userptr` object, null if not mapped + JavaCallbackDef alBufferCallback0 ALBUFFERCALLBACKTYPESOFT 1 + JavaCallbackKey alBufferCallback0 0</code></pre> +<p>leading to the following interface</p> +<pre><code> /** JavaCallback interface: ALBUFFERCALLBACKTYPESOFT -> void (*ALBUFFERCALLBACKTYPESOFT)(int buffer, void * userptr, int sampledata, int numbytes) */ + public static interface ALBUFFERCALLBACKTYPESOFT { + /** Interface to C language function: <br> <code>void callback(int buffer, void * userptr, int sampledata, int numbytes)</code><br>Alias for: <code>ALBUFFERCALLBACKTYPESOFT</code> */ + public void callback(int buffer, Object userptr, int sampledata, int numbytes); + } + + ... + + /** Entry point (through function pointer) to C language function: <br> <code>void alBufferCallback0(int buffer, int format, int freq, ALBUFFERCALLBACKTYPESOFT callback, void * userptr)</code><br> */ + public void alBufferCallback0(int buffer, int format, int freq, ALBUFFERCALLBACKTYPESOFT callback, Object userptr); + + public boolean isAlBufferCallback0Mapped(int buffer); + public ALBUFFERCALLBACKTYPESOFT getAlBufferCallback0(int buffer); + public Object getAlBufferCallback0UserParam(int buffer); + + /** Entry point (through function pointer) to C language function: <br> <code>void alEventCallbackInject(int eventType, int object, int param, const char * msg)</code><br> */ + public void alEventCallbackInject(int eventType, int object, int param, String msg); </code></pre> +<p>Implementation utilizes the default <code>SetCallback-KeyClass</code> +implementation for +<code>void alBufferCallback0(int buffer, int format, int freq, ALBUFFERCALLBACKTYPESOFT callback, Object userptr)</code>, +which uses one key, i.e. <code>buffer</code>.</p> +<pre><code> private static class AlBufferCallback0Key { + private final int buffer; + AlBufferCallback0Key(int buffer) { + this.buffer = buffer; + } + @Override + public boolean equals(final Object o) { + if( this == o ) { + return true; + } + if( !(o instanceof AlBufferCallback0Key) ) { + return false; + } + final AlBufferCallback0Key o2 = (AlBufferCallback0Key)o; + return buffer == o2.buffer; + } + @Override + public int hashCode() { + // 31 * x == (x << 5) - x + int hash = buffer; + return hash; + } + }</code></pre> +<h3 id="javacallback-example-2b-custom-keyclass">JavaCallback Example 2b +(Custom <em>KeyClass</em>)</h3> +<p>Same as example 2a, but implementing a custom +<code>SetCallback-KeyClass</code>.</p> +<p>Instead of <code>Callback0</code>, the unit <code>test2.*</code> uses +<code>Callback1</code> to differentiate this case.</p> +<p>GlueGen configuration snippet with the added option attribute for the +<code>SetCallback-KeyClass</code> in directive +<code>JavaCallbackDef</code>.</p> +<pre><code>JavaCallbackDef alBufferCallback1 ALBUFFERCALLBACKTYPESOFT 1 com.jogamp.gluegen.test.junit.generation.Test4JavaCallback.CustomAlBufferCallback1Key +JavaCallbackKey alBufferCallback1 0</code></pre> +<p>Implementation utilizes a custom <code>SetCallback-KeyClass</code> +implementation for +<code>void alBufferCallback1(int buffer, int format, int freq, ALBUFFERCALLBACKTYPESOFT callback, Object userptr)</code>, +which uses one key, i.e. <code>buffer</code>.</p> +<pre><code> public static class CustomAlBufferCallback1Key { + private final int buffer; + public CustomAlBufferCallback1Key(final int buffer) { + this.buffer = buffer; + } + @Override + public boolean equals(final Object o) { + if( this == o ) { + return true; + } + if( !(o instanceof CustomAlBufferCallback1Key) ) { + return false; + } + final CustomAlBufferCallback1Key o2 = (CustomAlBufferCallback1Key)o; + return buffer == o2.buffer; + } + @Override + public int hashCode() { + return buffer; + } + @Override + public String toString() { + return "CustomALKey[this "+toHexString(System.identityHashCode(this))+", buffer "+buffer+"]"; + } + }</code></pre> <p><em>TODO: Enhance documentation</em></p> <h2 id="misc-configurations">Misc Configurations</h2> <h3 |