diff options
-rw-r--r-- | doc/GlueGen_Mapping.html | 313 | ||||
-rw-r--r-- | doc/GlueGen_Mapping.md | 257 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/CMethodBindingEmitter.java | 32 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/JavaConfiguration.java | 98 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/JavaEmitter.java | 62 | ||||
-rw-r--r-- | src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java | 221 | ||||
-rw-r--r-- | src/junit/com/jogamp/gluegen/test/junit/generation/Test4JavaCallback.java | 404 | ||||
-rw-r--r-- | src/junit/com/jogamp/gluegen/test/junit/generation/test2.c | 80 | ||||
-rw-r--r-- | src/junit/com/jogamp/gluegen/test/junit/generation/test2.cfg | 107 | ||||
-rw-r--r-- | src/junit/com/jogamp/gluegen/test/junit/generation/test2.h | 32 |
10 files changed, 1429 insertions, 177 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 diff --git a/doc/GlueGen_Mapping.md b/doc/GlueGen_Mapping.md index a8bcfbc..dbc66aa 100644 --- a/doc/GlueGen_Mapping.md +++ b/doc/GlueGen_Mapping.md @@ -761,6 +761,82 @@ 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. +### Required *LibraryOnLoad* +Note that [`LibraryOnLoad Bindingtest2`](#libraryonload-librarybasename-for-jni_onload-) 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 `JavaVM*` generating a new `JNIEnv*`in daemon mode - +or just to retrieve the thread's `JNIEnv*`, if already attached to the `JavaVM*`. + +### *JavaCallback* Configuration + +Configuration directives are as follows: + + JavaCallbackDef <SetCallbackFunctionName> <CallbackFunctionType> <CallbackFunction-UserParamIndex> [<SetCallback-KeyClassName>] + JavaCallbackKey <SetCallbackFunctionName> (SetCallback-ParamIdx)* + +`JavaCallbackDef` and `JavaCallbackKey` use the name of the `SetCallbackFunction` as its first attribute, +as it is core to the semantic mapping of all resources. + +`JavaCallbackDef` attributes: +- `SetCallbackFunction`: `SetCallbackFunction` name of the native toolkit API responsible to set the callback +- `CallbackFunctionType`: The native toolkit API typedef-name of the function-pointer-type, aka the callback type name +- `CallbackFunction-UserParamIndex`: The `userParam` parameter-index of the `CallbackFunctionType` +- `SetCallback-KeyClassName`: Name of an optional user-implemented `SetCallback-KeyClass`, providing the hash-map-key - see below + +The `SetCallbackFunction` is utilized to set the `CallbackFunction` as well as to remove it passing `null` for the `CallbackFunction`. + +If mapping the `CallbackFunction` to keys, the user must specify the same key arguments when setting and removing the ``CallbackFunction`. + +#### *JavaCallback* Key Definition + +If no keys are defined via `JavaCallbackKey`, or manually injected using a custom `SetCallback-KeyClass`, see below, +the `CallbackFunction` has global scope. + +Keys allow to limit the scope, i.e. map multiple `CallbackFunction` to the different keys. + +Key arguments must match in `SetCallbackFunction` to remove a previously set `CallbackFunction`. + +`JavaCallbackKey` attributes +- `SetCallbackFunction`: `SetCallbackFunction` name of the native toolkit API responsible to set the callback +- `SetCallback-ParamIdx`: List of parameter indices of the `SetCallbackFunction`, denoting the key(s) limiting the callback scope, i.e. the callback and all resources will be mapped to this key. The optional `SetCallback-KeyClass` may override this semantic. + +Beside generating the actual function mapping of the API, additional query methods are generated, passing the keys as its paramters +- `boolean is<SetCallbackFunctionName>Mapped((key-arg)*)` queries whether `SetCallbackFunctionName` is mapped. +- `ALBUFFERCALLBACKTYPESOFT get<SetCallbackFunctionName>((key-arg)*)` returns the mapped `CallbackFunction`, null if not mapped +- `Object get<SetCallbackFunctionName>UserParam((key-arg)*)` returns the mapped `userParam` object, null if not mapped + + +#### Custom `SetCallback-KeyClass` + +The `SetCallback-KeyClass` is the optional user-written hash-map-key definition +and shall handle all key parameter of the `SetCallbackFunction` as defined via `JavaCallbackKey`, see above. + +`SetCallback-KeyClass` may be used to add external key-components, e.g. current-thread or a toolkit dependent context. + +The `SetCallback-KeyClass` shall implement the following hash-map-key standard methods +- `boolean equals(Object)` +- `int hashCode()` +- `SetCallback-KeyClassName(...)` constructor receiving all key parameter of `SetCallbackFunction` as defined via `JavaCallbackKey`, see above. + + +### *JavaCallback* Notes +Please consider the following *currently enabled* constraints using JavaCallback +- Only one interface callback-method binding is allowed for a native callback function, e.g. `T2_CallbackFunc01` (see above) + - Implying that the native single function-pointer typedef must be mapped to a single Java method within its interface + - Hence it must be avoided that multiple method variation are produced, e.g. due to `char*` to `byte[]` and `String` mapping etc. +- The native callback function can only return no-value, i.e. `void`, or a primitive type. Usually `void` is being used in toolkit APIs. +- 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 `CMethodBindingEmitter.emitBodyMapCToJNIType(..)`. +- To remove a JavaCallback the `SetCallbackFunction` must be called with `null` for the `CallbackFunction` argument + but with the same [*key arguments* (see `JavaCallbackKey`)](#javacallback-key-definition) as previously called to set the callback. +- Exactly one native code-unit for the library must specify [`LibraryOnLoad libraryBasename`](#libraryonload-librarybasename-for-jni_onload-) +- `SetCallbackFunction` is thread safe +- ... + +### JavaCallback Example 1 +This is a generic example. + +The callback `T2_CallbackFunc01` has global scope, i.e. is not mapped to any key and can be only set globally. + C-API header snippet: ``` typedef void ( * T2_CallbackFunc01)(size_t id, const char* msg, void* usrParam); @@ -778,23 +854,27 @@ 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 ``` + Note that [`LibraryOnLoad Bindingtest2`](#libraryonload-librarybasename-for-jni_onload-) 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 `JavaVM*` generating a new `JNIEnv*`in daemon mode - or just to retrieve the thread's `JNIEnv*`, if already attached to the `JavaVM*`. -This will lead to the following result +This will lead to the following interface ``` public interface Bindingtest2 { @@ -808,23 +888,162 @@ public interface Bindingtest2 { /** 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); ``` -### JavaCallback Constraints -Please consider the following *currently enabled* constraints using `JavaCallbackDef` -- Only one interface callback-method binding is allowed for a native callback function, e.g. `T2_CallbackFunc01` (see above). -- The native callback function can only return no-value, i.e. `void`, or a primitive type. Usually `void` is being used in toolkit APIs. -- 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 `CMethodBindingEmitter.emitBodyMapCToJNIType(..)`. -- To remove a JavaCallback the specified and mapped setter function, e.g. `MessageCallback01`, must be called with `null` for the callback interface - but the very same `userParam` instance as previously called to set the callback. -- Exactly one native code-unit for the library must specify [`LibraryOnLoad libraryBasename`](#libraryonload-librarybasename-for-jni_onload-) -- ... +Implementation utilizes the default `SetCallback-KeyClass` implementation for `void MessageCallback01(T2_CallbackFunc01 cbFunc, Object usrParam)`, +which is key-less and hence minimalistic. +``` + 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; + } + } +``` + +### JavaCallback Example 2a (Default *KeyClass*) + +This examples is derived from OpenAL's `AL_SOFT_callback_buffer` extension. + +The callback `ALBUFFERCALLBACKTYPESOFT` is mapped to `buffer` name, i.e. one callback can be set for each buffer. + +C-API Header snipped +``` + 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); +``` + +and the following GlueGen configuration +``` + # 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 +``` + +leading to the following interface +``` + /** 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); +``` + +Implementation utilizes the default `SetCallback-KeyClass` implementation for `void alBufferCallback0(int buffer, int format, int freq, ALBUFFERCALLBACKTYPESOFT callback, Object userptr)`, +which uses one key, i.e. `buffer`. +``` + 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; + } + } +``` + +### JavaCallback Example 2b (Custom *KeyClass*) + +Same as example 2a, but implementing a custom `SetCallback-KeyClass`. + +Instead of `Callback0`, the unit `test2.*` uses `Callback1` to differentiate this case. + +GlueGen configuration snippet with the added option attribute for the `SetCallback-KeyClass` in directive `JavaCallbackDef`. +``` +JavaCallbackDef alBufferCallback1 ALBUFFERCALLBACKTYPESOFT 1 com.jogamp.gluegen.test.junit.generation.Test4JavaCallback.CustomAlBufferCallback1Key +JavaCallbackKey alBufferCallback1 0 +``` + +Implementation utilizes a custom `SetCallback-KeyClass` implementation for `void alBufferCallback1(int buffer, int format, int freq, ALBUFFERCALLBACKTYPESOFT callback, Object userptr)`, +which uses one key, i.e. `buffer`. +``` + 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+"]"; + } + } +``` *TODO: Enhance documentation* diff --git a/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java index 3975315..43d11b7 100644 --- a/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java +++ b/src/java/com/jogamp/gluegen/CMethodBindingEmitter.java @@ -154,7 +154,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { javaCallback = cfg.setFuncToJavaCallbackMap.get(binding.getName()); if( null != javaCallback ) { - jcbNativeBasename = CodeGenUtils.capitalizeString( javaCallback.setFuncName+javaCallback.simpleCbClazzName.replace("_", "") ); + jcbNativeBasename = CodeGenUtils.capitalizeString( javaCallback.setFuncName+javaCallback.cbSimpleClazzName.replace("_", "") ); jcbCMethodEmitter = new CMethodBindingEmitter(javaCallback.cbFuncBinding, unit, javaPackageName, javaClassName, isOverloadedBinding, isJavaMethodStatic, forImplementingMethodCall, @@ -324,12 +324,13 @@ public class CMethodBindingEmitter extends FunctionEmitter { */ public final MachineDataInfo getMachineDataInfo() { return machDesc; } + private static final boolean DEBUG_JAVACALLBACK = false; @Override protected void emitReturnType() { if( null != javaCallback ) { LOG.log(INFO, "BindCFunc.R.JavaCallback: {0}: {1}", binding.getName(), javaCallback); - final String userParamArgName = javaCallback.cbFuncBinding.getArgumentName(javaCallback.userParamIdx); + final String userParamArgName = javaCallback.cbFuncBinding.getArgumentName(javaCallback.cbFuncUserParamIdx); final Type cReturnType = javaCallback.cbFuncBinding.getCReturnType(); final JavaType jretType = javaCallback.cbFuncBinding.getJavaReturnType(); unit.emitln("typedef struct {"); @@ -362,7 +363,6 @@ public class CMethodBindingEmitter extends FunctionEmitter { unit.emitln(" T_"+jcbNativeBasename+"* cb = (T_"+jcbNativeBasename+"*) "+userParamArgName+";"); unit.emitln(" // C Params: "+javaCallback.cbFuncBinding.getCParameterList(new StringBuilder(), false, null).toString()); unit.emitln(" // J Params: "+javaCallback.cbFuncBinding.getJavaParameterList(new StringBuilder()).toString()); - // unit.emitln(" fprintf(stderr, \"YYY Callback01 user %p, id %ld, msg %s\\n\", cb, id, msg);"); if( !cReturnType.isVoid() ) { unit.emit(" "+cReturnType.getCName()+" _res = ("+cReturnType.getCName()+") "); @@ -371,10 +371,8 @@ public class CMethodBindingEmitter extends FunctionEmitter { } unit.emit("(*env)->Call" + CodeGenUtils.capitalizeString( jretType.getName() ) +"Method(env, cb->cbFunc, cb->cbMethodID, "); // javaCallback.cbFuncCEmitter.emitBodyPassCArguments(); - if( 0 < jcbCMethodEmitter.emitJavaCallbackBodyPassJavaArguments(javaCallback) ) { - unit.emit(", "); - } - unit.emitln("cb->userParam);"); + jcbCMethodEmitter.emitJavaCallbackBodyPassJavaArguments(javaCallback, "cb->userParam"); + unit.emitln(");"); // javaCallback.cbFuncCEmitter.emitBodyUserVariableAssignments(); // javaCallback.cbFuncCEmitter.emitBodyVariablePostCallCleanup(); @@ -393,7 +391,7 @@ public class CMethodBindingEmitter extends FunctionEmitter { /* pp */ int emitJavaCallbackBodyCToJavaPreCall(final JavaCallbackInfo jcbi) { int count = 0; for (int i = 0; i < binding.getNumArguments(); i++) { - if( i == jcbi.userParamIdx ) { + if( i == jcbi.cbFuncUserParamIdx ) { continue; } if( emitBodyMapCToJNIType(i, true /* addLocalVar */) ) { @@ -402,18 +400,19 @@ public class CMethodBindingEmitter extends FunctionEmitter { } return count; } - /* pp */ int emitJavaCallbackBodyPassJavaArguments(final JavaCallbackInfo jcbi) { + /* pp */ int emitJavaCallbackBodyPassJavaArguments(final JavaCallbackInfo jcbi, final String userParamVarName) { int count = 0; boolean needsComma = false; for (int i = 0; i < binding.getNumArguments(); i++) { - if( i == jcbi.userParamIdx ) { - continue; - } if (needsComma) { unit.emit(", "); needsComma = false; } - unit.emit( binding.getArgumentName(i) + "_jni" ); + if( i == jcbi.cbFuncUserParamIdx ) { + unit.emit( userParamVarName ); + } else { + unit.emit( binding.getArgumentName(i) + "_jni" ); + } needsComma = true; ++count; } @@ -552,7 +551,9 @@ public class CMethodBindingEmitter extends FunctionEmitter { unit.emitln(" {"); unit.emitln(" jlong v = (jlong) (intptr_t) "+nativeUserParamVarName+";"); unit.emitln(" (*env)->SetLongArrayRegion(env, jnativeUserParam, 0, (jsize)1, &v);"); - // unit.emitln(" fprintf(stderr, \"YYY MessageCallback01 user %p -> native %p\\n\", "+userParamArgName+", "+nativeUserParamVarName+");"); + if( DEBUG_JAVACALLBACK ) { + unit.emitln(" fprintf(stderr, \"YYY user %p -> native %p\\n\", "+userParamArgName+", "+nativeUserParamVarName+");"); + } unit.emitln(" }"); unit.emitln(); } @@ -565,9 +566,10 @@ public class CMethodBindingEmitter extends FunctionEmitter { unit.emitln("}"); unit.emitln(); if( null != jcb ) { + final String capIfaceName = CodeGenUtils.capitalizeString( getInterfaceName() ); unit.emitln("JNIEXPORT void JNICALL"); unit.emit(JavaEmitter.getJNIMethodNamePrefix(getJavaPackageName(), getJavaClassName())); - unit.emitln("_release"+getInterfaceName()+"Impl(JNIEnv *env, jobject _unused, jlong jnativeUserParam) {"); + unit.emitln("_release"+capIfaceName+"MapImpl(JNIEnv *env, jobject _unused, jlong jnativeUserParam) {"); unit.emitln(" T_"+jcbNativeBasename+"* nativeUserParam = (T_"+jcbNativeBasename+"*) (intptr_t) jnativeUserParam;"); unit.emitln(" if( NULL != nativeUserParam ) {"); unit.emitln(" (*env)->DeleteGlobalRef(env, nativeUserParam->cbFunc);"); diff --git a/src/java/com/jogamp/gluegen/JavaConfiguration.java b/src/java/com/jogamp/gluegen/JavaConfiguration.java index 17ac547..1fe2747 100644 --- a/src/java/com/jogamp/gluegen/JavaConfiguration.java +++ b/src/java/com/jogamp/gluegen/JavaConfiguration.java @@ -152,18 +152,21 @@ public class JavaConfiguration { /** JavaCallback configuration definition (static) */ public static class JavaCallbackDef { - final String setFuncName; final String cbFuncTypeName; - final int userParamIdx; - JavaCallbackDef(final String setFuncName, final String cbFuncTypeName, final int userParamIdx) { - this.setFuncName = setFuncName; + final int cbFuncUserParamIdx; + final String setFuncName; + final List<Integer> setFuncKeyIndices = new ArrayList<Integer>(); + final String setFuncKeyClassName; // optional + JavaCallbackDef(final String cbFuncTypeName, final int cbFuncUserParamIdx, final String setFuncName, final String setFuncKeyClassName) { this.cbFuncTypeName = cbFuncTypeName; - this.userParamIdx = userParamIdx; + this.cbFuncUserParamIdx = cbFuncUserParamIdx; + this.setFuncName = setFuncName; + this.setFuncKeyClassName = setFuncKeyClassName; } @Override public String toString() { - return String.format("JavaCallbackDef[set %s, cb %s, userParamIdx %d]", - setFuncName, cbFuncTypeName, userParamIdx); + return String.format("JavaCallbackDef[cbFunc[type %s, userParamIdx %d], set[%s, keys %s, KeyClass %s]]", + cbFuncTypeName, cbFuncUserParamIdx, setFuncName, setFuncKeyIndices.toString(), setFuncKeyClassName); } } private final List<JavaCallbackDef> javaCallbackList = new ArrayList<JavaCallbackDef>(); @@ -1377,6 +1380,8 @@ public class JavaConfiguration { readArgumentIsString(tok, filename, lineNo); } else if (cmd.equalsIgnoreCase("JavaCallbackDef")) { readJavaCallbackDef(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("JavaCallbackKey")) { + readJavaCallbackKey(tok, filename, lineNo); } else if (cmd.equalsIgnoreCase("ExtendedInterfaceSymbolsIgnore")) { readExtendedIntfImplSymbols(tok, filename, lineNo, true, false, false); } else if (cmd.equalsIgnoreCase("ExtendedInterfaceSymbolsOnly")) { @@ -1626,8 +1631,14 @@ public class JavaConfiguration { try { final String setFuncName = tok.nextToken(); final String cbFuncTypeName = tok.nextToken(); - final Integer userParamIdx = Integer.valueOf(tok.nextToken()); - final JavaCallbackDef jcd = new JavaCallbackDef(setFuncName, cbFuncTypeName, userParamIdx); + final Integer cbFuncUserParamIdx = Integer.valueOf(tok.nextToken()); + final String cbFuncKeyClassName; + if( tok.hasMoreTokens() ) { + cbFuncKeyClassName = tok.nextToken(); + } else { + cbFuncKeyClassName = null; + } + final JavaCallbackDef jcd = new JavaCallbackDef(cbFuncTypeName, cbFuncUserParamIdx, setFuncName, cbFuncKeyClassName); javaCallbackList.add(jcd); javaCallbackSetFuncToDef.put(setFuncName, jcd); } catch (final NoSuchElementException e) { @@ -1636,6 +1647,26 @@ public class JavaConfiguration { } } + protected void readJavaCallbackKey(final StringTokenizer tok, final String filename, final int lineNo) { + try { + final String setFuncName = tok.nextToken(); + final JavaCallbackDef jcd = javaCallbackSetFuncToDef.get(setFuncName); + if( null == jcd ) { + throw new IllegalArgumentException("JavaCallbackDef '"+setFuncName+"\' not (yet) defined."); + } + while( tok.hasMoreTokens() ) { + final int idx = Integer.valueOf(tok.nextToken()); + if( 0 > idx ) { + throw new IllegalArgumentException("JavaCallbackKey '"+setFuncName+"\' index "+idx+" not in range [0..n]."); + } + jcd.setFuncKeyIndices.add( idx ); + } + } catch (final NoSuchElementException e) { + throw new RuntimeException("Error parsing \"JavaCallbackKey\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + protected void readExtendedIntfImplSymbols(final StringTokenizer tok, final String filename, final int lineNo, final boolean forInterface, final boolean forImplementation, final boolean onlyList) { File javaFile; BufferedReader javaReader; @@ -2245,44 +2276,53 @@ public class JavaConfiguration { * @see JavaConfiguration#setFuncToJavaCallbackMap */ public static class JavaCallbackInfo { - final String setFuncName; final String cbFuncTypeName; - final String simpleCbClazzName; - final String fqCbClazzName; + final String cbSimpleClazzName; + final String cbFQClazzName; final String cbMethodSignature; final FunctionType cbFuncType; final MethodBinding cbFuncBinding; - final int userParamIdx; + final int cbFuncUserParamIdx; + final Type userParamType; final String userParamName; + + final String setFuncName; + final List<Integer> setFuncKeyIndices; + final String setFuncKeyClassName; boolean setFuncProcessed; int setFuncCBParamIdx; int setFuncUserParamIdx; - public JavaCallbackInfo(final String setFuncName, final String cbFuncTypeName, final String simpleClazzName, final String fqClazzName, final String methodSignature, - final FunctionType cbFuncType, final MethodBinding cbFuncBinding, final int userParamIdx) { - this.setFuncName = setFuncName; + public JavaCallbackInfo(final String cbFuncTypeName, final String cbSimpleClazzName, final String cbFQClazzName, final String cbMethodSignature, + final FunctionType cbFuncType, final MethodBinding cbFuncBinding, final int cbFuncUserParamIdx, + final String setFuncName, final List<Integer> setFuncKeyIndices, final String setFuncKeyClassName) { this.cbFuncTypeName = cbFuncTypeName; - this.simpleCbClazzName = simpleClazzName; - this.fqCbClazzName = fqClazzName; - this.cbMethodSignature = methodSignature; + this.cbSimpleClazzName = cbSimpleClazzName; + this.cbFQClazzName = cbFQClazzName; + this.cbMethodSignature = cbMethodSignature; this.cbFuncType = cbFuncType; this.cbFuncBinding = cbFuncBinding; int paramIdx = -2; Type paramType = null; String paramName = null; - if( 0 <= userParamIdx && userParamIdx < cbFuncType.getNumArguments() ) { - final Type t = cbFuncType.getArgumentType(userParamIdx); + if( 0 <= cbFuncUserParamIdx && cbFuncUserParamIdx < cbFuncType.getNumArguments() ) { + final Type t = cbFuncType.getArgumentType(cbFuncUserParamIdx); if( null != t && t.isPointer() ) { // OK '<something>*' - paramIdx = userParamIdx; - paramName = cbFuncType.getArgumentName(userParamIdx); + paramIdx = cbFuncUserParamIdx; + paramName = cbFuncType.getArgumentName(cbFuncUserParamIdx); paramType = t.getTargetType(); } } - this.userParamIdx = paramIdx; + this.cbFuncUserParamIdx = paramIdx; + this.userParamType = paramType; this.userParamName = paramName; + + this.setFuncName = setFuncName; + this.setFuncKeyIndices = setFuncKeyIndices; + this.setFuncKeyClassName = setFuncKeyClassName; this.setFuncProcessed = false; this.setFuncCBParamIdx = -1; this.setFuncUserParamIdx = -1; @@ -2303,12 +2343,16 @@ public class JavaConfiguration { @Override public String toString() { - return String.format("JavaCallbackInfo[set %s(ok %b, cbIdx %d, upIdx %d), cb %s%s, userParam[idx %d, '%s', %s], %s]", - setFuncName, setFuncProcessed, setFuncCBParamIdx, setFuncUserParamIdx, + return String.format("JavaCallbackInfo[cbFunc[%s%s, userParam[idx %d, '%s', %s], set[%s(ok %b, cbIdx %d, upIdx %d, keys %s, KeyClass '%s'], %s]", cbFuncTypeName, cbMethodSignature, - userParamIdx, userParamName, userParamType.getSignature(null).toString(), cbFuncType.toString(cbFuncTypeName, false, true)); + cbFuncUserParamIdx, userParamName, userParamType.getSignature(null).toString(), + setFuncName, setFuncProcessed, setFuncCBParamIdx, setFuncUserParamIdx, + setFuncKeyIndices.toString(), setFuncKeyClassName, + cbFuncType.toString(cbFuncTypeName, false, true)); } } /** Mapped binding name to {@link JavaCallbackInfo} */ /* pp */ final Map<String, JavaCallbackInfo> setFuncToJavaCallbackMap = new HashMap<String, JavaCallbackInfo>(); + final Set<String> emittedJavaCallbackUserParamClasses = new HashSet<String>(); + } diff --git a/src/java/com/jogamp/gluegen/JavaEmitter.java b/src/java/com/jogamp/gluegen/JavaEmitter.java index 7dad237..7674e1f 100644 --- a/src/java/com/jogamp/gluegen/JavaEmitter.java +++ b/src/java/com/jogamp/gluegen/JavaEmitter.java @@ -75,7 +75,6 @@ import com.jogamp.common.os.DynamicLookupHelper; import com.jogamp.common.os.MachineDataInfo; import com.jogamp.common.util.ArrayHashMap; import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider; -import com.jogamp.gluegen.FunctionEmitter.EmissionModifier; import com.jogamp.gluegen.JavaConfiguration.JavaCallbackDef; import com.jogamp.gluegen.JavaConfiguration.JavaCallbackInfo; import com.jogamp.gluegen.Logging.LoggerIf; @@ -1461,27 +1460,46 @@ public class JavaEmitter implements GlueEmitter { funcSym.addAliasedName(jcbd.cbFuncTypeName); LOG.log(INFO, "JavaCallback: fSym {0}, {1}", funcSym.getAliasedString(), jcbd); - final String simpleClazzName = CodeGenUtils.capitalizeString(jcbd.cbFuncTypeName); - final String fqClazzName = cfg.packageName()+"."+cfg.className()+"."+simpleClazzName; - final StringBuilder methodSignature = new StringBuilder(); - javaUnit.emitln(" /** JavaCallback interface: "+jcbd.cbFuncTypeName+" -> "+funcType.toString(jcbd.cbFuncTypeName, false, true)+" */"); - javaUnit.emitln(" public static interface "+simpleClazzName+" {"); - final List<MethodBinding> mbs = generateFunctionInterfaceCode(javaUnit, funcSym, jcbd.userParamIdx, methodSignature); - javaUnit.emitln(" }"); - javaUnit.emitln(); - if( 1 != mbs.size() ) { - throw new UnsupportedOperationException("Multiple bindings generated where only 1 is allowed for func "+funcType.toString(jcbd.cbFuncTypeName, false, true)); - } - final MethodBinding mb = mbs.get(0); - if( !mb.getJavaReturnType().isVoid() && !mb.getJavaReturnType().isPrimitive() ) { - throw new UnsupportedOperationException("Non void or non-primitive callback return types not suppored. Java "+ - mb.getJavaReturnType()+", func "+funcType.toString(jcbd.cbFuncTypeName, false, true)); - } - final JavaCallbackInfo jcbi = new JavaCallbackInfo(jcbd.setFuncName, jcbd.cbFuncTypeName, simpleClazzName, fqClazzName, - methodSignature.toString(), funcType, mb, jcbd.userParamIdx); - cfg.setFuncToJavaCallbackMap.put(jcbd.setFuncName, jcbi); - LOG.log(INFO, "JavaCallbackInfo: Added {0}", jcbi); + final String cbSimpleClazzName = CodeGenUtils.capitalizeString(jcbd.cbFuncTypeName); + final String cbFQClazzName = cfg.packageName()+"."+cfg.className()+"."+cbSimpleClazzName; + + final JavaCallbackInfo jcbi0 = javaCallbackInterfaceMap.get(cbFQClazzName); + if( null != jcbi0 ) { + // Reuse callback-func interface, can't duplicate + if( jcbi0.cbFuncUserParamIdx != jcbd.cbFuncUserParamIdx ) { + throw new UnsupportedOperationException("Reused FuncTypeName "+jcbd.cbFuncTypeName+" used with different FuncUserParamIdx "+jcbi0.cbFuncUserParamIdx+" -> "+jcbd.cbFuncUserParamIdx+". Func "+ + funcType.toString(jcbd.cbFuncTypeName, false, true)); + } + final JavaCallbackInfo jcbi1 = new JavaCallbackInfo(jcbd.cbFuncTypeName, cbSimpleClazzName, cbFQClazzName, jcbi0.cbMethodSignature, + funcType, jcbi0.cbFuncBinding, jcbi0.cbFuncUserParamIdx, + jcbd.setFuncName, jcbd.setFuncKeyIndices, jcbd.setFuncKeyClassName); + cfg.setFuncToJavaCallbackMap.put(jcbd.setFuncName, jcbi1); + LOG.log(INFO, "JavaCallbackInfo: Reusing {0} -> {1}", jcbd.setFuncName, jcbi0); + } else { + final StringBuilder cbMethodSignature = new StringBuilder(); + javaUnit.emitln(" /** JavaCallback interface: "+jcbd.cbFuncTypeName+" -> "+funcType.toString(jcbd.cbFuncTypeName, false, true)+" */"); + javaUnit.emitln(" public static interface "+cbSimpleClazzName+" {"); + final List<MethodBinding> mbs = generateFunctionInterfaceCode(javaUnit, funcSym, jcbd.cbFuncUserParamIdx, cbMethodSignature); + javaUnit.emitln(" }"); + javaUnit.emitln(); + if( 1 != mbs.size() ) { + throw new UnsupportedOperationException("Multiple bindings generated where only 1 is allowed for func "+funcType.toString(jcbd.cbFuncTypeName, false, true)); + } + final MethodBinding cbFuncBinding = mbs.get(0); + if( !cbFuncBinding.getJavaReturnType().isVoid() && !cbFuncBinding.getJavaReturnType().isPrimitive() ) { + throw new UnsupportedOperationException("Non void or non-primitive callback return types not suppored. Java "+ + cbFuncBinding.getJavaReturnType()+", func "+funcType.toString(jcbd.cbFuncTypeName, false, true)); + } + final JavaCallbackInfo jcbi1 = new JavaCallbackInfo(jcbd.cbFuncTypeName, cbSimpleClazzName, cbFQClazzName, cbMethodSignature.toString(), + funcType, cbFuncBinding, jcbd.cbFuncUserParamIdx, + jcbd.setFuncName, jcbd.setFuncKeyIndices, jcbd.setFuncKeyClassName); + cfg.setFuncToJavaCallbackMap.put(jcbd.setFuncName, jcbi1); + javaCallbackInterfaceMap.put(cbFQClazzName, jcbi1); + LOG.log(INFO, "JavaCallbackInfo: Added {0} -> {1}", jcbd.setFuncName, jcbi1); + } } + private final Map<String, JavaCallbackInfo> javaCallbackInterfaceMap = new HashMap<String, JavaCallbackInfo>(); + private List<MethodBinding> generateFunctionInterfaceCode(final JavaCodeUnit javaUnit, final FunctionSymbol funcSym, final int userParamIdx, final StringBuilder methodSignature) { // Emit method call and associated native code MethodBinding mb = bindFunction(funcSym, true /* forInterface */, machDescJava, null, null); @@ -3085,7 +3103,7 @@ public class JavaEmitter implements GlueEmitter { { // Replace JavaCallback type with generated interface name jcbiSetFuncCBParamIdx=i; - mappedType = JavaType.createForNamedClass( jcbi.fqCbClazzName ); + mappedType = JavaType.createForNamedClass( jcbi.cbFQClazzName ); } else if( null != jcbi && jcbi.userParamName.equals( cArgName ) && ( !jcbi.setFuncProcessed || i == jcbi.setFuncUserParamIdx ) && cArgType.isPointer() && jcbi.userParamType.equals( cArgType.getTargetType() ) ) diff --git a/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java index 047a637..2bc453d 100644 --- a/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java +++ b/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java @@ -1,4 +1,4 @@ -/* +/** * Copyright (c) 2010-2023 JogAmp Community. All rights reserved. * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * @@ -50,6 +50,8 @@ import com.jogamp.gluegen.cgram.types.Type; import java.io.PrintWriter; import java.text.MessageFormat; +import java.util.Arrays; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; @@ -455,6 +457,114 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { return getArgumentName(i) + "_offset"; } + private static final boolean DEBUG_JAVACALLBACK = false; + + private final void emitJavaCallbackKeyClass(final String KeyClassName) { + unit.emitln(" private static class "+KeyClassName+" {"); + binding.forEachParameter( ( final int idx, final int consumedCount, final Type cType, final JavaType jType, final String name ) -> { + if( !cType.isVoid() && javaCallback.setFuncKeyIndices.contains(idx) ) { + unit.emitln(" private final "+jType+" "+name+";"); + return true; + } else { + return false; + } + } ); + unit.emitln(" "+KeyClassName+"("+binding.getJavaSelectParameter(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+") {"); + binding.forEachParameter( ( final int idx, final int consumedCount, final Type cType, final JavaType jType, final String name ) -> { + if( !cType.isVoid() && javaCallback.setFuncKeyIndices.contains(idx) ) { + unit.emitln(" this."+name+" = "+name+";"); + return true; + } else { + return false; + } + } ); + unit.emitln(" }"); + unit.emitln(" @Override"); + unit.emitln(" public boolean equals(final Object o) {"); + unit.emitln(" if( this == o ) {"); + unit.emitln(" return true;"); + unit.emitln(" }"); + unit.emitln(" if( !(o instanceof "+KeyClassName+") ) {"); + unit.emitln(" return false;"); + unit.emitln(" }"); + { + final int count = binding.forEachParameter( ( final int idx, final int consumedCount, final Type cType, final JavaType jType, final String name ) -> { + if( !cType.isVoid() && javaCallback.setFuncKeyIndices.contains(idx) ) { + if( 0 == consumedCount ) { + unit.emitln(" final "+KeyClassName+" o2 = ("+KeyClassName+")o;"); + unit.emit (" return "); + } else { + unit.emitln(" &&"); + unit.emit (" "); + } + if( jType.isPrimitive() || idx == javaCallback.setFuncUserParamIdx ) { + unit.emit(name+" == o2."+name); + } else { + unit.emit(name+".equals( o2."+name+" )"); + } + return true; + } else { + return false; + } + } ); + if( 0 == count ) { + unit.emit(" return true"); + } + unit.emitln(";"); + } + unit.emitln(" }"); + unit.emitln(" @Override"); + unit.emitln(" public int hashCode() {"); + { + final int count = binding.forEachParameter( ( final int idx, final int consumedCount, final Type cType, final JavaType jType, final String name ) -> { + if( !cType.isVoid() && javaCallback.setFuncKeyIndices.contains(idx) ) { + if( 0 == consumedCount ) { + unit.emitln(" // 31 * x == (x << 5) - x"); + unit.emit (" int hash = "); + } else { + unit.emit (" hash = ((hash << 5) - hash) + "); + } + if( jType.isPrimitive() ) { + if( jType.isLong() ) { + unit.emitln("Long.valueOf( "+name+" ).hashCode();"); + } else { + unit.emitln(name+";"); + } + } else { + if( idx == javaCallback.setFuncUserParamIdx ) { + unit.emitln("System.identityHashCode( "+name+" );"); + } else { + unit.emitln(name+".hashCode();"); + } + } + return true; + } else { + return false; + } + } ); + if( 0 == count ) { + unit.emitln(" return 0;"); + } else { + unit.emitln(" return hash;"); + } + } + unit.emitln(" }"); + unit.emitln(" }"); + unit.emitln(); + } + private final void emitJavaCallbackUsrParamClass(final String UsrParamClassName) { + unit.emitln(" private static class "+UsrParamClassName+" {"); + unit.emitln(" final "+javaCallback.cbFuncTypeName+" func;"); + unit.emitln(" final Object param;"); + unit.emitln(" final long nativeParam;"); + unit.emitln(" "+UsrParamClassName+"("+javaCallback.cbFuncTypeName+" func, Object param, long nativeParam) {"); + unit.emitln(" this.func = func;"); + unit.emitln(" this.param = param;"); + unit.emitln(" this.nativeParam = nativeParam;"); + unit.emitln(" }"); + unit.emitln(" }"); + } + @Override protected void emitBody() { if (!emitBody) { @@ -474,37 +584,68 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { unit.emitln(" }"); } if( null != javaCallback && !isPrivateNativeMethod ) { + final String capIfaceName = CodeGenUtils.capitalizeString( getInterfaceName() ); + final String lowIfaceName = CodeGenUtils.decapitalizeString( getInterfaceName() ); unit.emitln(); - final String userParamArgName = binding.getArgumentName(javaCallback.setFuncUserParamIdx); - unit.emit(" public boolean is"+getInterfaceName()+"Mapped(final Object "+userParamArgName+")"); if( isInterface() ) { - unit.emitln(";"); + unit.emitln(" public boolean is"+capIfaceName+"Mapped("+binding.getJavaSelectParameter(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+");"); + unit.emitln(" public "+javaCallback.cbFuncTypeName+" get"+capIfaceName+"("+binding.getJavaSelectParameter(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+");"); + unit.emitln(" public Object get"+capIfaceName+"UserParam("+binding.getJavaSelectParameter(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+");"); } else { - unit.emitln(" {"); - unit.emitln(" return null != "+javaCallback.cbFuncTypeName+"UsrMap.get("+userParamArgName+");"); + final String usrMapInstanceName = lowIfaceName+"UsrMap"; + final boolean customKeyClass; + final String KeyClassName; + if( null != javaCallback.setFuncKeyClassName ) { + customKeyClass = true;; + KeyClassName = javaCallback.setFuncKeyClassName; + } else { + customKeyClass = false; + KeyClassName = CodeGenUtils.capitalizeString(capIfaceName+"Key"); + } + final String UsrParamClassName = CodeGenUtils.capitalizeString( javaCallback.cbFuncTypeName+"UserParam" ); + final String fqUsrParamClassName = cfg.packageName()+"."+cfg.className()+"."+UsrParamClassName; + unit.emitln(" public final boolean is"+capIfaceName+"Mapped("+binding.getJavaSelectParameter(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+") {"); + unit.emitln(" final "+KeyClassName+" key = new "+KeyClassName+"("+binding.getJavaCallSelectArguments(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+");"); + unit.emitln(" return null != "+usrMapInstanceName+".get(key);"); unit.emitln(" }"); - unit.emitln(" private final void release"+getInterfaceName()+"(final Object "+userParamArgName+") {"); - unit.emitln(" final "+javaCallback.cbFuncTypeName+"UserParam v = "+javaCallback.cbFuncTypeName+"UsrMap.remove("+userParamArgName+");"); - // unit.emitln(" System.err.println(\"ZZZ Release v \"+v+\", v.nativeParam 0x\"+Long.toHexString(null!=v?v.nativeParam:0));"); - unit.emitln(" if( null != v ) {"); - unit.emitln(" release"+getInterfaceName()+"Impl(v.nativeParam);"); - unit.emitln(" }"); + unit.emitln(" public final "+javaCallback.cbFuncTypeName+" get"+capIfaceName+"("+binding.getJavaSelectParameter(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+") {"); + unit.emitln(" final "+KeyClassName+" key = new "+KeyClassName+"("+binding.getJavaCallSelectArguments(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+");"); + unit.emitln(" final "+UsrParamClassName+" value = "+usrMapInstanceName+".get(key);"); + unit.emitln(" return null != value ? value.func : null;"); + unit.emitln(" }"); + unit.emitln(" public final Object get"+capIfaceName+"UserParam("+binding.getJavaSelectParameter(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+") {"); + unit.emitln(" final "+KeyClassName+" key = new "+KeyClassName+"("+binding.getJavaCallSelectArguments(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+");"); + unit.emitln(" final "+UsrParamClassName+" value = "+usrMapInstanceName+".get(key);"); + unit.emitln(" return null != value ? value.param : null;"); + unit.emitln(" }"); + unit.emitln(" private final void add"+capIfaceName+"Map("+binding.getJavaSelectParameter(new StringBuilder(), javaCallback.setFuncKeyIndices, true).toString()+UsrParamClassName+" value) {"); + unit.emitln(" final "+KeyClassName+" key = new "+KeyClassName+"("+binding.getJavaCallSelectArguments(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+");"); + unit.emitln(" "+usrMapInstanceName+".put(key, value);"); + if( DEBUG_JAVACALLBACK ) { + unit.emitln(" System.err.println(\"ZZZ Map \"+key+\" -> value.nativeParam 0x\"+Long.toHexString(null!=value?value.nativeParam:0));"); + } unit.emitln(" }"); - unit.emitln(" private static class "+javaCallback.cbFuncTypeName+"UserParam {"); - unit.emitln(" @SuppressWarnings(\"unused\")"); - unit.emitln(" final "+javaCallback.cbFuncTypeName+" func;"); - unit.emitln(" @SuppressWarnings(\"unused\")"); - unit.emitln(" final Object param;"); - unit.emitln(" @SuppressWarnings(\"unused\")"); - unit.emitln(" final long nativeParam;"); - unit.emitln(" "+javaCallback.cbFuncTypeName+"UserParam("+javaCallback.cbFuncTypeName+" func, Object param, long nativeParam) {"); - unit.emitln(" this.func = func;"); - unit.emitln(" this.param = param;"); - unit.emitln(" this.nativeParam = nativeParam;"); + unit.emitln(" private final void release"+capIfaceName+"Map("+binding.getJavaSelectParameter(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+") {"); + unit.emitln(" final "+KeyClassName+" key = new "+KeyClassName+"("+binding.getJavaCallSelectArguments(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+");"); + unit.emitln(" final "+UsrParamClassName+" value = "+usrMapInstanceName+".remove(key);"); + if( DEBUG_JAVACALLBACK ) { + unit.emitln(" System.err.println(\"ZZZ Release \"+key+\" -> value.nativeParam 0x\"+Long.toHexString(null!=value?value.nativeParam:0));"); + } + unit.emitln(" if( null != value ) {"); + unit.emitln(" release"+capIfaceName+"MapImpl(value.nativeParam);"); unit.emitln(" }"); unit.emitln(" }"); - unit.emitln(" private final java.util.Map<Object, "+javaCallback.cbFuncTypeName+"UserParam> "+javaCallback.cbFuncTypeName+"UsrMap = new java.util.HashMap<Object, "+javaCallback.cbFuncTypeName+"UserParam>();"); - unit.emitln(" private native void release"+getInterfaceName()+"Impl(long nativeUserParam);"); + unit.emitln(" private native void release"+capIfaceName+"MapImpl(long nativeUserParam);"); + unit.emitln(); + if( !customKeyClass ) { + emitJavaCallbackKeyClass(KeyClassName); + } + if( !cfg.emittedJavaCallbackUserParamClasses.contains(fqUsrParamClassName) ) { + emitJavaCallbackUsrParamClass(UsrParamClassName); + cfg.emittedJavaCallbackUserParamClasses.add(fqUsrParamClassName); + } + unit.emitln(" private final java.util.Map<"+KeyClassName+", "+UsrParamClassName+"> "+usrMapInstanceName+" = new java.util.HashMap<"+KeyClassName+", "+UsrParamClassName+">();"); + unit.emitln(); } } } @@ -620,14 +761,18 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { protected void emitReturnVariableSetupAndCall(final MethodBinding binding) { final JavaType returnType = binding.getJavaReturnType(); boolean needsResultAssignment = false; + final String capIfaceName = CodeGenUtils.capitalizeString( getInterfaceName() ); if( null != javaCallback ) { - final String userParamArgName = binding.getArgumentName(javaCallback.setFuncUserParamIdx); - unit.emitln(" release"+getInterfaceName()+"("+userParamArgName+"); // Ensure a previously mapped instance is released"); - unit.emitln(" final long[] nativeUserParam = { 0 };"); + final String lowIfaceName = CodeGenUtils.decapitalizeString( getInterfaceName() ); + final String usrMapInstanceName = lowIfaceName+"UsrMap"; + unit.emitln(" synchronized( "+usrMapInstanceName+" ) {"); + unit.emitln(" final long[] nativeUserParam = { 0 };"); + unit.emitln(" release"+capIfaceName+"Map("+binding.getJavaCallSelectArguments(new StringBuilder(), javaCallback.setFuncKeyIndices, false).toString()+"); // Ensure a previously mapped instance is released"); + unit.emitln(); } if (!returnType.isVoid()) { - unit.emit(" "); + unit.emit(" "); if (returnType.isCompoundTypeWrapper() || returnType.isNIOBuffer()) { unit.emitln("final ByteBuffer _res;"); @@ -645,9 +790,9 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { } if (needsResultAssignment) { - unit.emit(" _res = "); + unit.emit(" _res = "); } else { - unit.emit(" "); + unit.emit(" "); if (!returnType.isVoid()) { unit.emit("return "); } @@ -659,10 +804,16 @@ public class JavaMethodBindingEmitter extends FunctionEmitter { if( null != javaCallback ) { final String funcArgName = binding.getArgumentName(javaCallback.setFuncCBParamIdx); final String userParamArgName = binding.getArgumentName(javaCallback.setFuncUserParamIdx); - // unit.emitln(" System.err.println(\"ZZZ returned nativeUserParam \"+nativeUserParam[0]);"); - unit.emitln(" if( 0 != nativeUserParam[0] ) {"); - unit.emitln(" "+javaCallback.cbFuncTypeName+"UsrMap.put("+userParamArgName+", new "+javaCallback.cbFuncTypeName+"UserParam("+funcArgName+", "+userParamArgName+", nativeUserParam[0]));"); - unit.emitln(" }"); + final String UsrParamClassName = CodeGenUtils.capitalizeString( javaCallback.cbFuncTypeName+"UserParam" ); + if( DEBUG_JAVACALLBACK ) { + unit.emitln(" System.err.println(\"ZZZ returned nativeUserParam 0x\"+Long.toHexString(nativeUserParam[0]));"); + } + unit.emitln(); + unit.emitln(" if( 0 != nativeUserParam[0] ) {"); + unit.emitln(" add"+capIfaceName+"Map("+binding.getJavaCallSelectArguments(new StringBuilder(), javaCallback.setFuncKeyIndices, true).toString()+ + "new "+UsrParamClassName+"("+funcArgName+", "+userParamArgName+", nativeUserParam[0]));"); + unit.emitln(" }"); + unit.emitln(" } // synchronized "); } emitPostCallCleanup(binding); diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/Test4JavaCallback.java b/src/junit/com/jogamp/gluegen/test/junit/generation/Test4JavaCallback.java index e80be6f..e43d73e 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/Test4JavaCallback.java +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/Test4JavaCallback.java @@ -31,6 +31,7 @@ package com.jogamp.gluegen.test.junit.generation; import java.io.IOException; import com.jogamp.common.os.NativeLibrary; +import com.jogamp.gluegen.test.junit.generation.Bindingtest2.ALBUFFERCALLBACKTYPESOFT; import com.jogamp.gluegen.test.junit.generation.Bindingtest2.T2_CallbackFunc01; import com.jogamp.gluegen.test.junit.generation.impl.Bindingtest2Impl; @@ -83,30 +84,30 @@ public class Test4JavaCallback extends BaseClass { final T2_CallbackFunc01 myCallback01 = new T2_CallbackFunc01() { @Override public void callback(final long id, final String msg, final Object userParam) { - final MyUserParam myUserParam = (MyUserParam)userParam; + final MyUserParam01 myUserParam = (MyUserParam01)userParam; id_res[0] = id + myUserParam.i; msg_res[0] = msg; myUserParam.j += id_res[0]; - System.err.println("chapter10.myCallback01: "+id+", '"+msg+"'"); + System.err.println("chapter01.myCallback01: "+id+", '"+msg+"'"); } }; final T2_CallbackFunc01 myCallback02 = new T2_CallbackFunc01() { @Override public void callback(final long id, final String msg, final Object userParam) { - final MyUserParam myUserParam = (MyUserParam)userParam; + final MyUserParam01 myUserParam = (MyUserParam01)userParam; id_res[0] = id; msg_res[0] = msg; myUserParam.j += id_res[0]; - System.err.println("chapter10.myCallback02: "+id+", '"+msg+"'"); + System.err.println("chapter01.myCallback02: "+id+", '"+msg+"'"); } }; - final MyUserParam myUserParam01 = new MyUserParam(10); + final MyUserParam01 myUserParam01 = new MyUserParam01(10); Assert.assertEquals(10, myUserParam01.i); Assert.assertEquals( 0, myUserParam01.j); - Assert.assertEquals(false, bt2.isMessageCallback01Mapped(myUserParam01)); + Assert.assertEquals(false, bt2.isMessageCallback01Mapped()); bt2.MessageCallback01(myCallback01, myUserParam01); - Assert.assertEquals(true, bt2.isMessageCallback01Mapped(myUserParam01)); + Assert.assertEquals(true, bt2.isMessageCallback01Mapped()); Assert.assertEquals(-1, id_res[0]); Assert.assertEquals(null, msg_res[0]); Assert.assertEquals(10, myUserParam01.i); @@ -132,7 +133,7 @@ public class Test4JavaCallback extends BaseClass { // Switch the callback function // The previously mapped myUserParam01 gets released and remapped to new callback bt2.MessageCallback01(myCallback02, myUserParam01); - Assert.assertEquals(true, bt2.isMessageCallback01Mapped(myUserParam01)); + Assert.assertEquals(true, bt2.isMessageCallback01Mapped()); Assert.assertEquals( 42+10, id_res[0]); Assert.assertEquals( msgNo2, msg_res[0]); Assert.assertEquals( 10, myUserParam01.i); @@ -149,7 +150,7 @@ public class Test4JavaCallback extends BaseClass { // Just release the callback and mapped myUserParam01 bt2.MessageCallback01(null, myUserParam01); - Assert.assertEquals(false, bt2.isMessageCallback01Mapped(myUserParam01)); + Assert.assertEquals(false, bt2.isMessageCallback01Mapped()); { final String msgNo4 = "My Fourth JavaCallback message"; bt2.InjectMessageCallback01( 21, msgNo4); @@ -160,11 +161,392 @@ public class Test4JavaCallback extends BaseClass { Assert.assertEquals(1+42+10+404+10, myUserParam01.j); } } - private static class MyUserParam { + private static class MyUserParam01 { final long i; long j; - public MyUserParam(final long i) { this.i = i; j=0; } + public MyUserParam01(final long i) { this.i = i; j=0; } + + @Override + public boolean equals(final Object o) { + if( this == o ) { + return true; + } + if( !(o instanceof MyUserParam01) ) { + return false; + } + return false; // we require identity! + } + @Override + public int hashCode() { + return System.identityHashCode(this); // we require identity! + } + } + + /** + * Test Bindingtest2 with ALBUFFERCALLBACKTYPESOFT JavaCallback via alBufferCallback1() + * using the default AlBufferCallback1Key class. + */ + @Test + public void chapter02() throws Exception { + final Bindingtest2 bt2 = new Bindingtest2Impl(); + + final long[] id_res = { -1 }; + final ALBUFFERCALLBACKTYPESOFT myCallback01 = new ALBUFFERCALLBACKTYPESOFT() { + @Override + public void callback(final int buffer, final Object userptr, final int sampledata, final int numbytes) { + final MyUserParam02 myUserParam = (MyUserParam02)userptr; + id_res[0] = sampledata + numbytes + myUserParam.i; + myUserParam.j = id_res[0]; + myUserParam.buffer = buffer; + System.err.println("chapter02.myCallback01: buffer "+buffer+", sampledata "+sampledata+", numbytes "+numbytes); + } + }; + final ALBUFFERCALLBACKTYPESOFT myCallback02 = new ALBUFFERCALLBACKTYPESOFT() { + @Override + public void callback(final int buffer, final Object userptr, final int sampledata, final int numbytes) { + final MyUserParam02 myUserParam = (MyUserParam02)userptr; + id_res[0] = sampledata * numbytes + myUserParam.i; + myUserParam.j = id_res[0]; + myUserParam.buffer = buffer; + System.err.println("chapter02.myCallback02: buffer "+buffer+", sampledata "+sampledata+", numbytes "+numbytes); + } + }; + final int buffer1 = 1; + final int buffer2 = 2; + final int buffer3 = 3; + final MyUserParam02 myUserParam01 = new MyUserParam02( 1); + final MyUserParam02 myUserParam02 = new MyUserParam02( 2); + Assert.assertEquals( 1, myUserParam01.i); + Assert.assertEquals( 0, myUserParam01.j); + Assert.assertEquals( 0, myUserParam01.buffer); + Assert.assertEquals( 2, myUserParam02.i); + Assert.assertEquals( 0, myUserParam02.j); + Assert.assertEquals( 0, myUserParam02.buffer); + + Assert.assertEquals(false, bt2.isAlBufferCallback0Mapped(buffer1)); + Assert.assertEquals(false, bt2.isAlBufferCallback0Mapped(buffer2)); + Assert.assertEquals(false, bt2.isAlBufferCallback0Mapped(buffer3)); + + // 1st mapping: buffer1 -> myCallback01, myUserParam01 + bt2.alBufferCallback0(buffer1, 0, 0, myCallback01, myUserParam01); + Assert.assertEquals(true, bt2.isAlBufferCallback0Mapped(buffer1)); + Assert.assertEquals(false, bt2.isAlBufferCallback0Mapped(buffer2)); + Assert.assertEquals(false, bt2.isAlBufferCallback0Mapped(buffer3)); + Assert.assertEquals(myUserParam01, bt2.getAlBufferCallback0UserParam(buffer1)); + Assert.assertEquals(null, bt2.getAlBufferCallback0UserParam(buffer2)); + Assert.assertEquals(null, bt2.getAlBufferCallback0UserParam(buffer3)); + Assert.assertEquals(myCallback01, bt2.getAlBufferCallback0(buffer1)); + Assert.assertEquals(null, bt2.getAlBufferCallback0(buffer2)); + Assert.assertEquals(null, bt2.getAlBufferCallback0(buffer3)); + + // 2nd mapping: buffer2 -> myCallback02, myUserParam02 + bt2.alBufferCallback0(buffer2, 0, 0, myCallback02, myUserParam02); + Assert.assertEquals(true, bt2.isAlBufferCallback0Mapped(buffer1)); + Assert.assertEquals(true, bt2.isAlBufferCallback0Mapped(buffer2)); + Assert.assertEquals(false, bt2.isAlBufferCallback0Mapped(buffer3)); + Assert.assertEquals(myUserParam01, bt2.getAlBufferCallback0UserParam(buffer1)); + Assert.assertEquals(myUserParam02, bt2.getAlBufferCallback0UserParam(buffer2)); + Assert.assertEquals(null, bt2.getAlBufferCallback0UserParam(buffer3)); + Assert.assertEquals(myCallback01, bt2.getAlBufferCallback0(buffer1)); + Assert.assertEquals(myCallback02, bt2.getAlBufferCallback0(buffer2)); + Assert.assertEquals(null, bt2.getAlBufferCallback0(buffer3)); + + { + bt2.alBufferCallback0Inject(buffer1, 10, 100); // buffer1 -> myCallback01, myUserParam01 + Assert.assertEquals(10+100+1, id_res[0]); + Assert.assertEquals( 1, myUserParam01.i); + Assert.assertEquals(10+100+1, myUserParam01.j); + Assert.assertEquals( 1, myUserParam01.buffer); + Assert.assertEquals( 2, myUserParam02.i); + Assert.assertEquals( 0, myUserParam02.j); + Assert.assertEquals( 0, myUserParam02.buffer); + } + { + bt2.alBufferCallback0Inject(buffer2, 10, 100); // buffer2 -> myCallback02, myUserParam02 + Assert.assertEquals(10*100+2, id_res[0]); + Assert.assertEquals( 1, myUserParam01.i); + Assert.assertEquals(10+100+1, myUserParam01.j); + Assert.assertEquals( 1, myUserParam01.buffer); + Assert.assertEquals( 2, myUserParam02.i); + Assert.assertEquals(10*100+2, myUserParam02.j); + Assert.assertEquals( 2, myUserParam02.buffer); + } + + // Switch the callback function for buffer2 -> myCallback01, myUserParam02 + bt2.alBufferCallback0(buffer2, 0, 0, myCallback01, myUserParam02); + Assert.assertEquals(true, bt2.isAlBufferCallback0Mapped(buffer1)); + Assert.assertEquals(true, bt2.isAlBufferCallback0Mapped(buffer2)); + Assert.assertEquals(false, bt2.isAlBufferCallback0Mapped(buffer3)); + Assert.assertEquals(myUserParam01, bt2.getAlBufferCallback0UserParam(buffer1)); + Assert.assertEquals(myUserParam02, bt2.getAlBufferCallback0UserParam(buffer2)); + Assert.assertEquals(null, bt2.getAlBufferCallback0UserParam(buffer3)); + Assert.assertEquals(myCallback01, bt2.getAlBufferCallback0(buffer1)); + Assert.assertEquals(myCallback01, bt2.getAlBufferCallback0(buffer2)); + Assert.assertEquals(null, bt2.getAlBufferCallback0(buffer3)); + + { + bt2.alBufferCallback0Inject(buffer1, 11, 101); // buffer1 -> myCallback01, myUserParam01 + Assert.assertEquals(11+101+1, id_res[0]); + Assert.assertEquals( 1, myUserParam01.i); + Assert.assertEquals(11+101+1, myUserParam01.j); + Assert.assertEquals( 1, myUserParam01.buffer); + Assert.assertEquals( 2, myUserParam02.i); + Assert.assertEquals(10*100+2, myUserParam02.j); + Assert.assertEquals( 2, myUserParam02.buffer); + } + { + bt2.alBufferCallback0Inject(buffer2, 1, 10); // buffer2 -> myCallback01, myUserParam02 + Assert.assertEquals( 1+ 10+2, id_res[0]); + Assert.assertEquals( 1, myUserParam01.i); + Assert.assertEquals(11+101+1, myUserParam01.j); + Assert.assertEquals( 1, myUserParam01.buffer); + Assert.assertEquals( 2, myUserParam02.i); + Assert.assertEquals( 1+ 10+2, myUserParam02.j); + Assert.assertEquals( 2, myUserParam02.buffer); + } + + // Just release the buffer2 callback and mapped resources + bt2.alBufferCallback0(buffer2, 0, 0, null, myUserParam02); // usrptr is not key, only buffer is key! + Assert.assertEquals(true, bt2.isAlBufferCallback0Mapped(buffer1)); + Assert.assertEquals(false, bt2.isAlBufferCallback0Mapped(buffer2)); + Assert.assertEquals(false, bt2.isAlBufferCallback0Mapped(buffer3)); + Assert.assertEquals(myUserParam01, bt2.getAlBufferCallback0UserParam(buffer1)); + Assert.assertEquals(null, bt2.getAlBufferCallback0UserParam(buffer2)); + Assert.assertEquals(null, bt2.getAlBufferCallback0UserParam(buffer3)); + Assert.assertEquals(myCallback01, bt2.getAlBufferCallback0(buffer1)); + Assert.assertEquals(null, bt2.getAlBufferCallback0(buffer2)); + Assert.assertEquals(null, bt2.getAlBufferCallback0(buffer3)); + + // Just release the buffer1 callback and mapped resources + bt2.alBufferCallback0(buffer1, 0, 0, null, null); // usrptr is not key, only buffer is key! + Assert.assertEquals(false, bt2.isAlBufferCallback0Mapped(buffer1)); + Assert.assertEquals(false, bt2.isAlBufferCallback0Mapped(buffer2)); + Assert.assertEquals(false, bt2.isAlBufferCallback0Mapped(buffer3)); + Assert.assertEquals(null, bt2.getAlBufferCallback0UserParam(buffer1)); + Assert.assertEquals(null, bt2.getAlBufferCallback0UserParam(buffer2)); + Assert.assertEquals(null, bt2.getAlBufferCallback0UserParam(buffer3)); + Assert.assertEquals(null, bt2.getAlBufferCallback0(buffer1)); + Assert.assertEquals(null, bt2.getAlBufferCallback0(buffer2)); + Assert.assertEquals(null, bt2.getAlBufferCallback0(buffer3)); + + { + bt2.alBufferCallback0Inject(buffer2, 1, 10); // unmapped, no change in data + Assert.assertEquals( 1+ 10+2, id_res[0]); + Assert.assertEquals( 1, myUserParam01.i); + Assert.assertEquals(11+101+1, myUserParam01.j); + Assert.assertEquals( 1, myUserParam01.buffer); + Assert.assertEquals( 2, myUserParam02.i); + Assert.assertEquals( 1+ 10+2, myUserParam02.j); + Assert.assertEquals( 2, myUserParam02.buffer); + } + } + + /** + * Test Bindingtest2 with ALBUFFERCALLBACKTYPESOFT JavaCallback via alBufferCallback1() + * using our custom CustomAlBufferCallback1Key class. + */ + @Test + public void chapter03() throws Exception { + final Bindingtest2 bt2 = new Bindingtest2Impl(); + + final long[] id_res = { -1 }; + final ALBUFFERCALLBACKTYPESOFT myCallback01 = new ALBUFFERCALLBACKTYPESOFT() { + @Override + public void callback(final int buffer, final Object userptr, final int sampledata, final int numbytes) { + final MyUserParam02 myUserParam = (MyUserParam02)userptr; + id_res[0] = sampledata + numbytes + myUserParam.i; + myUserParam.j = id_res[0]; + myUserParam.buffer = buffer; + System.err.println("chapter03.myCallback01: buffer "+buffer+", sampledata "+sampledata+", numbytes "+numbytes); + } + }; + final ALBUFFERCALLBACKTYPESOFT myCallback02 = new ALBUFFERCALLBACKTYPESOFT() { + @Override + public void callback(final int buffer, final Object userptr, final int sampledata, final int numbytes) { + final MyUserParam02 myUserParam = (MyUserParam02)userptr; + id_res[0] = sampledata * numbytes + myUserParam.i; + myUserParam.j = id_res[0]; + myUserParam.buffer = buffer; + System.err.println("chapter03.myCallback02: buffer "+buffer+", sampledata "+sampledata+", numbytes "+numbytes); + } + }; + final int buffer1 = 1; + final int buffer2 = 2; + final int buffer3 = 3; + final MyUserParam02 myUserParam01 = new MyUserParam02( 1); + final MyUserParam02 myUserParam02 = new MyUserParam02( 2); + Assert.assertEquals( 1, myUserParam01.i); + Assert.assertEquals( 0, myUserParam01.j); + Assert.assertEquals( 0, myUserParam01.buffer); + Assert.assertEquals( 2, myUserParam02.i); + Assert.assertEquals( 0, myUserParam02.j); + Assert.assertEquals( 0, myUserParam02.buffer); + + Assert.assertEquals(false, bt2.isAlBufferCallback1Mapped(buffer1)); + Assert.assertEquals(false, bt2.isAlBufferCallback1Mapped(buffer2)); + Assert.assertEquals(false, bt2.isAlBufferCallback1Mapped(buffer3)); + + // 1st mapping: buffer1 -> myCallback01, myUserParam01 + bt2.alBufferCallback1(buffer1, 0, 0, myCallback01, myUserParam01); + Assert.assertEquals(true, bt2.isAlBufferCallback1Mapped(buffer1)); + Assert.assertEquals(false, bt2.isAlBufferCallback1Mapped(buffer2)); + Assert.assertEquals(false, bt2.isAlBufferCallback1Mapped(buffer3)); + Assert.assertEquals(myUserParam01, bt2.getAlBufferCallback1UserParam(buffer1)); + Assert.assertEquals(null, bt2.getAlBufferCallback1UserParam(buffer2)); + Assert.assertEquals(null, bt2.getAlBufferCallback1UserParam(buffer3)); + Assert.assertEquals(myCallback01, bt2.getAlBufferCallback1(buffer1)); + Assert.assertEquals(null, bt2.getAlBufferCallback1(buffer2)); + Assert.assertEquals(null, bt2.getAlBufferCallback1(buffer3)); + + // 2nd mapping: buffer2 -> myCallback02, myUserParam02 + bt2.alBufferCallback1(buffer2, 0, 0, myCallback02, myUserParam02); + Assert.assertEquals(true, bt2.isAlBufferCallback1Mapped(buffer1)); + Assert.assertEquals(true, bt2.isAlBufferCallback1Mapped(buffer2)); + Assert.assertEquals(false, bt2.isAlBufferCallback1Mapped(buffer3)); + Assert.assertEquals(myUserParam01, bt2.getAlBufferCallback1UserParam(buffer1)); + Assert.assertEquals(myUserParam02, bt2.getAlBufferCallback1UserParam(buffer2)); + Assert.assertEquals(null, bt2.getAlBufferCallback1UserParam(buffer3)); + Assert.assertEquals(myCallback01, bt2.getAlBufferCallback1(buffer1)); + Assert.assertEquals(myCallback02, bt2.getAlBufferCallback1(buffer2)); + Assert.assertEquals(null, bt2.getAlBufferCallback1(buffer3)); + + { + bt2.alBufferCallback1Inject(buffer1, 10, 100); // buffer1 -> myCallback01, myUserParam01 + Assert.assertEquals(10+100+1, id_res[0]); + Assert.assertEquals( 1, myUserParam01.i); + Assert.assertEquals(10+100+1, myUserParam01.j); + Assert.assertEquals( 1, myUserParam01.buffer); + Assert.assertEquals( 2, myUserParam02.i); + Assert.assertEquals( 0, myUserParam02.j); + Assert.assertEquals( 0, myUserParam02.buffer); + } + { + bt2.alBufferCallback1Inject(buffer2, 10, 100); // buffer2 -> myCallback02, myUserParam02 + Assert.assertEquals(10*100+2, id_res[0]); + Assert.assertEquals( 1, myUserParam01.i); + Assert.assertEquals(10+100+1, myUserParam01.j); + Assert.assertEquals( 1, myUserParam01.buffer); + Assert.assertEquals( 2, myUserParam02.i); + Assert.assertEquals(10*100+2, myUserParam02.j); + Assert.assertEquals( 2, myUserParam02.buffer); + } + + // Switch the callback function for buffer2 -> myCallback01, myUserParam02 + bt2.alBufferCallback1(buffer2, 0, 0, myCallback01, myUserParam02); + Assert.assertEquals(true, bt2.isAlBufferCallback1Mapped(buffer1)); + Assert.assertEquals(true, bt2.isAlBufferCallback1Mapped(buffer2)); + Assert.assertEquals(false, bt2.isAlBufferCallback1Mapped(buffer3)); + Assert.assertEquals(myUserParam01, bt2.getAlBufferCallback1UserParam(buffer1)); + Assert.assertEquals(myUserParam02, bt2.getAlBufferCallback1UserParam(buffer2)); + Assert.assertEquals(null, bt2.getAlBufferCallback1UserParam(buffer3)); + Assert.assertEquals(myCallback01, bt2.getAlBufferCallback1(buffer1)); + Assert.assertEquals(myCallback01, bt2.getAlBufferCallback1(buffer2)); + Assert.assertEquals(null, bt2.getAlBufferCallback1(buffer3)); + + { + bt2.alBufferCallback1Inject(buffer1, 11, 101); // buffer1 -> myCallback01, myUserParam01 + Assert.assertEquals(11+101+1, id_res[0]); + Assert.assertEquals( 1, myUserParam01.i); + Assert.assertEquals(11+101+1, myUserParam01.j); + Assert.assertEquals( 1, myUserParam01.buffer); + Assert.assertEquals( 2, myUserParam02.i); + Assert.assertEquals(10*100+2, myUserParam02.j); + Assert.assertEquals( 2, myUserParam02.buffer); + } + { + bt2.alBufferCallback1Inject(buffer2, 1, 10); // buffer2 -> myCallback01, myUserParam02 + Assert.assertEquals( 1+ 10+2, id_res[0]); + Assert.assertEquals( 1, myUserParam01.i); + Assert.assertEquals(11+101+1, myUserParam01.j); + Assert.assertEquals( 1, myUserParam01.buffer); + Assert.assertEquals( 2, myUserParam02.i); + Assert.assertEquals( 1+ 10+2, myUserParam02.j); + Assert.assertEquals( 2, myUserParam02.buffer); + } + + // Just release the buffer2 callback and mapped resources + bt2.alBufferCallback1(buffer2, 0, 0, null, myUserParam02); // usrptr is not key, only buffer is key! + Assert.assertEquals(true, bt2.isAlBufferCallback1Mapped(buffer1)); + Assert.assertEquals(false, bt2.isAlBufferCallback1Mapped(buffer2)); + Assert.assertEquals(false, bt2.isAlBufferCallback1Mapped(buffer3)); + Assert.assertEquals(myUserParam01, bt2.getAlBufferCallback1UserParam(buffer1)); + Assert.assertEquals(null, bt2.getAlBufferCallback1UserParam(buffer2)); + Assert.assertEquals(null, bt2.getAlBufferCallback1UserParam(buffer3)); + Assert.assertEquals(myCallback01, bt2.getAlBufferCallback1(buffer1)); + Assert.assertEquals(null, bt2.getAlBufferCallback1(buffer2)); + Assert.assertEquals(null, bt2.getAlBufferCallback1(buffer3)); + + // Just release the buffer1 callback and mapped resources + bt2.alBufferCallback1(buffer1, 0, 0, null, null); // usrptr is not key, only buffer is key! + Assert.assertEquals(false, bt2.isAlBufferCallback1Mapped(buffer1)); + Assert.assertEquals(false, bt2.isAlBufferCallback1Mapped(buffer2)); + Assert.assertEquals(false, bt2.isAlBufferCallback1Mapped(buffer3)); + Assert.assertEquals(null, bt2.getAlBufferCallback1UserParam(buffer1)); + Assert.assertEquals(null, bt2.getAlBufferCallback1UserParam(buffer2)); + Assert.assertEquals(null, bt2.getAlBufferCallback1UserParam(buffer3)); + Assert.assertEquals(null, bt2.getAlBufferCallback1(buffer1)); + Assert.assertEquals(null, bt2.getAlBufferCallback1(buffer2)); + Assert.assertEquals(null, bt2.getAlBufferCallback1(buffer3)); + + { + bt2.alBufferCallback1Inject(buffer2, 1, 10); // unmapped, no change in data + Assert.assertEquals( 1+ 10+2, id_res[0]); + Assert.assertEquals( 1, myUserParam01.i); + Assert.assertEquals(11+101+1, myUserParam01.j); + Assert.assertEquals( 1, myUserParam01.buffer); + Assert.assertEquals( 2, myUserParam02.i); + Assert.assertEquals( 1+ 10+2, myUserParam02.j); + Assert.assertEquals( 2, myUserParam02.buffer); + } + } + private static class MyUserParam02 { + final long i; + long j; + int buffer; + public MyUserParam02(final long i) { this.i = i; j=0; buffer=0; } + + @Override + public boolean equals(final Object o) { + if( this == o ) { + return true; + } + if( !(o instanceof MyUserParam02) ) { + return false; + } + return false; // we require identity! + } + @Override + public int hashCode() { + return System.identityHashCode(this); // we require identity! + } + } + + 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+"]"; + } } + static private String toHexString(final int v) { return "0x"+Integer.toHexString(v); } public static void main(final String args[]) throws IOException { final String tstname = Test4JavaCallback.class.getName(); diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/test2.c b/src/junit/com/jogamp/gluegen/test/junit/generation/test2.c index 9715f11..7a7cb43 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/test2.c +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/test2.c @@ -86,6 +86,10 @@ int Release(T2_InitializeOptions* Options) { Options->CustomFuncB2 = NULL; } +// +// +// + static T2_CallbackFunc01 t2_callback01 = NULL; static void* t2_callback01_userparam = NULL; @@ -104,3 +108,79 @@ void InjectMessageCallback01(size_t id, const char* msg) { } } +// +// +// + +static ALEVENTPROCSOFT alEventCallback_cb = NULL; +static void* alEventCallback_up = NULL; + +void alEventCallback(ALEVENTPROCSOFT callback, void *userParam) { + alEventCallback_cb = callback; + alEventCallback_up = userParam; +} +void alEventCallbackInject(int eventType, int object, int param, const char* msg) { + if( NULL != alEventCallback_cb ) { + fprintf(stderr, "XXX InjectMessageCallback01 func %p, user %p\n", alEventCallback_cb, alEventCallback_up); + fflush(NULL); + (*alEventCallback_cb)(eventType, object, param, strlen(msg), msg, alEventCallback_up); + } +} + +// +// +// + +static const int MAX_AL_BUFFER = 5; +static ALBUFFERCALLBACKTYPESOFT alBufferCallback0_callback[] = { NULL, NULL, NULL, NULL, NULL }; +static void* alBufferCallback0_userptr[] = { NULL, NULL, NULL, NULL, NULL }; + +void alBufferCallback0(int buffer /* key */, int format, int freq, ALBUFFERCALLBACKTYPESOFT callback, void *userptr) { + if( buffer < 0 || MAX_AL_BUFFER <= buffer ) { + fprintf(stderr, "Error: alBufferCallback0: buffer not in range [0..%d), is %d\n", MAX_AL_BUFFER, buffer); + } else { + alBufferCallback0_callback[buffer] = callback; + alBufferCallback0_userptr[buffer] = userptr; + fprintf(stderr, "XXX alBufferCallback0 buffer %d -> func %p, user %p\n", buffer, callback, userptr); + } + fflush(NULL); +} +void alBufferCallback0Inject(int buffer, int sampledata, int numbytes) { + if( buffer < 0 || MAX_AL_BUFFER <= buffer ) { + fprintf(stderr, "Error: alBufferCallback0Inject: buffer not in range [0..%d), is %d\n", MAX_AL_BUFFER, buffer); + } + if( NULL != alBufferCallback0_callback[buffer] ) { + fprintf(stderr, "XXX alBufferCallback0Inject: buffer %d, func %p, user %p\n", buffer, alBufferCallback0_callback[buffer], alBufferCallback0_userptr[buffer]); + fflush(NULL); + (*alBufferCallback0_callback[buffer])(buffer, alBufferCallback0_userptr[buffer], sampledata, numbytes); + } +} + +// +// +// + +static ALBUFFERCALLBACKTYPESOFT alBufferCallback1_callback[] = { NULL, NULL, NULL, NULL, NULL }; +static void* alBufferCallback1_userptr[] = { NULL, NULL, NULL, NULL, NULL }; + +void alBufferCallback1(int buffer /* key */, int format, int freq, ALBUFFERCALLBACKTYPESOFT callback, void *userptr) { + if( buffer < 0 || MAX_AL_BUFFER <= buffer ) { + fprintf(stderr, "Error: alBufferCallback1: buffer not in range [0..%d), is %d\n", MAX_AL_BUFFER, buffer); + } else { + alBufferCallback1_callback[buffer] = callback; + alBufferCallback1_userptr[buffer] = userptr; + fprintf(stderr, "XXX alBufferCallback1 buffer %d -> func %p, user %p\n", buffer, callback, userptr); + } + fflush(NULL); +} +void alBufferCallback1Inject(int buffer, int sampledata, int numbytes) { + if( buffer < 0 || MAX_AL_BUFFER <= buffer ) { + fprintf(stderr, "Error: alBufferCallback1Inject: buffer not in range [0..%d), is %d\n", MAX_AL_BUFFER, buffer); + } + if( NULL != alBufferCallback1_callback[buffer] ) { + fprintf(stderr, "XXX alBufferCallback1Inject: buffer %d, func %p, user %p\n", buffer, alBufferCallback1_callback[buffer], alBufferCallback1_userptr[buffer]); + fflush(NULL); + (*alBufferCallback1_callback[buffer])(buffer, alBufferCallback1_userptr[buffer], sampledata, numbytes); + } +} + diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/test2.cfg b/src/junit/com/jogamp/gluegen/test/junit/generation/test2.cfg index 94ea0af..de713cf 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/test2.cfg +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/test2.cfg @@ -55,24 +55,111 @@ 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. # -# Note: An explicit `isMessageCallback01Mapped(Object usrParam)` is being created to explicitly query whether `usrParam` maps to the associated resources. +# This callback has no keys defines, rendering it of global scope! +# +# 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 +# +# End JavaCallback +# Begin JavaCallback. +# +# typedef void ( * ALEVENTPROCSOFT)(int eventType, int object, int param, int length, const char *message, void *userParam); +# void alEventCallback(ALEVENTPROCSOFT callback, void *userParam /* identity-key */); +# void alEventCallbackInject(int eventType, int object, int param, const char* msg); +ArgumentIsString ALEVENTPROCSOFT 4 +ArgumentIsString alEventCallbackInject 3 + +# Define a JavaCallback (OpenAL AL_SOFT_events) +# Set JavaCallback via function `alEventCallback` if `ALEVENTPROCSOFT` argument is non-null, otherwise removes the mapped callback and associated resources. +# +# It uses the function-pointer argument `ALEVENTPROCSOFT` as the callback function type +# and marks `ALEVENTPROCSOFT`s 6th argument (index 5) as the mandatory user-param. +# +# This callback has no keys defines, rendering it of global scope! +# The global key-less scope matches `AL_SOFT_events` semantics. +# +# Explicit queries are generated, passing the keys as paramters +# - `boolean isAlEventCallbackMapped()` queries whether `alEventCallback` is mapped globally +# - `ALEVENTPROCSOFT getAlEventCallback()` returns the global ALEVENTPROCSOFT, null if not mapped +# - `Object getAlEventCallbackUserParam()` returns the global `userParam` object, null if not mapped +JavaCallbackDef alEventCallback ALEVENTPROCSOFT 5 +JavaCallbackKey alEventCallback # # End JavaCallback -# typedef void ( * T2_CallbackFunc02)(const T2_Callback02UserType* usrParam); -# void MessageCallback02(T2_CallbackFunc02 cbFunc, const T2_Callback02UserType* usrParam); -# void InjectMessageCallback02(size_t id, const char* msg); -ArgumentIsString InjectMessageCallback02 1 -JavaCallbackDef MessageCallback02 T2_CallbackFunc02 0 +# Begin JavaCallback (OpanAL AL_SOFT_callback_buffer) +# +# // typedef void ( * ALBUFFERCALLBACKTYPESOFT)(int buffer, void *userptr, void *sampledata, int numbytes); +# typedef void ( * ALBUFFERCALLBACKTYPESOFT)(int buffer, void *userptr, int sampledata, int numbytes); +# +# void alBufferCallback0(int buffer /* key */, int format, int freq, ALBUFFERCALLBACKTYPESOFT callback, void *userptr /* identity-key */); +# +# // void alBufferCallback0Inject(int buffer, void *sampledata, int numbytes); +# void alBufferCallback0Inject(int buffer, int sampledata, int numbytes); + +# 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 +# +# End JavaCallback + +# Begin JavaCallback (OpanAL AL_SOFT_callback_buffer, variant 2) +# +# Reuses: ALBUFFERCALLBACKTYPESOFT, see above. +# +# void alBufferCallback1(int buffer /* key */, int format, int freq, ALBUFFERCALLBACKTYPESOFT callback, void *userptr /* identity-key */); +# +# // void alBufferCallback1Inject(int buffer, void *sampledata, int numbytes); +# void alBufferCallback1Inject(int buffer, int sampledata, int numbytes); + +# Define a JavaCallback. +# Set JavaCallback via function `alBufferCallback1` 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 alBufferCallback1(..) 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 isAlBufferCallback1Mapped(int buffer)` queries whether `alBufferCallback1` is mapped to `buffer`. +# - `ALBUFFERCALLBACKTYPESOFT getAlBufferCallback1(int buffer)` returns the `buffer` mapped ALEVENTPROCSOFT, null if not mapped +# - `Object getAlBufferCallback1UserParam(int buffer)` returns the `buffer` mapped `userptr` object, null if not mapped +JavaCallbackDef alBufferCallback1 ALBUFFERCALLBACKTYPESOFT 1 com.jogamp.gluegen.test.junit.generation.Test4JavaCallback.CustomAlBufferCallback1Key +JavaCallbackKey alBufferCallback1 0 +# +# End JavaCallback + +# Begin JavaCallback +# +# typedef void ( * T2_CallbackFunc11)(const T2_Callback11UserType* usrParam); +# void MessageCallback11(T2_CallbackFunc11 cbFunc, const T2_Callback11UserType* usrParam); +# void InjectMessageCallback11(size_t id, const char* msg); +ArgumentIsString InjectMessageCallback11 1 +JavaCallbackDef MessageCallback11 T2_CallbackFunc11 0 +# +# End JavaCallback CustomCCode #include "test2.h" diff --git a/src/junit/com/jogamp/gluegen/test/junit/generation/test2.h b/src/junit/com/jogamp/gluegen/test/junit/generation/test2.h index afe94a5..d067390 100644 --- a/src/junit/com/jogamp/gluegen/test/junit/generation/test2.h +++ b/src/junit/com/jogamp/gluegen/test/junit/generation/test2.h @@ -67,15 +67,37 @@ void MessageCallback01(T2_CallbackFunc01 cbFunc, void* usrParam); void InjectMessageCallback01(size_t id, const char* msg); // -// T2_CallbackFunc02 +// ALEVENTPROCSOFT (similar to OpenAL's AL_SOFT_events) +// +typedef void ( * ALEVENTPROCSOFT)(int eventType, int object, int param, int length, const char *message, void *userParam); + +void alEventCallback(ALEVENTPROCSOFT callback, void *userParam); +void alEventCallbackInject(int eventType, int object, int param, const char* msg); + +// +// ALBUFFERCALLBACKTYPESOFT (similar to OpenAL's AL_SOFT_callback_buffer) +// +// typedef void ( * ALBUFFERCALLBACKTYPESOFT)(int buffer, void *userptr, void *sampledata, int numbytes); +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, void *sampledata, int numbytes); +void alBufferCallback0Inject(int buffer, int sampledata, int numbytes); + +void alBufferCallback1(int buffer /* key */, int format, int freq, ALBUFFERCALLBACKTYPESOFT callback, void *userptr); +// void alBufferCallback1Inject(int buffer, void *sampledata, int numbytes); +void alBufferCallback1Inject(int buffer, int sampledata, int numbytes); + +// +// T2_CallbackFunc11 // typedef struct { int32_t ApiVersion; void* Data; -} T2_Callback02UserType; +} T2_Callback11UserType; -typedef void ( * T2_CallbackFunc02)(const T2_Callback02UserType* usrParam); +typedef void ( * T2_CallbackFunc11)(const T2_Callback11UserType* usrParam); -void MessageCallback02(T2_CallbackFunc02 cbFunc, const T2_Callback02UserType* usrParam); -void InjectMessageCallback02(size_t id, const char* msg); +void MessageCallback11(T2_CallbackFunc11 cbFunc, const T2_Callback11UserType* usrParam); +void InjectMessageCallback11(size_t id, const char* msg); |