Last active
June 6, 2024 18:52
-
-
Save tresf/8c1c2ede83a38dff59e35a5b9e163561 to your computer and use it in GitHub Desktop.
Thread safe wrapping of hid4java calls
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.hid4java.jna; | |
import com.sun.jna.Native; | |
import com.sun.jna.Pointer; | |
import com.sun.jna.WString; | |
import java.util.concurrent.ExecutionException; | |
import java.util.concurrent.ExecutorService; | |
import java.util.concurrent.Executors; | |
public class DarwinHidApiLibrary implements HidApiLibrary { | |
// WARNING: This is NOT a JNA interface, but rather a wrapped instance | |
public static HidApiLibrary INSTANCE = new DarwinHidApiLibrary(); | |
private static RealDarwinHidApiLibrary REAL_INSTANCE = Native.load("hidapi", RealDarwinHidApiLibrary.class); | |
/** | |
* Changes the behavior of all further calls to {@link #hid_open(short, short, WString)} or {@link #hid_open_path(String)}. | |
* <br> | |
* All devices opened by HIDAPI with {@link #hid_open(short, short, WString)} or {@link #hid_open_path(String)} | |
* are opened in exclusive mode per default. | |
* <br> | |
* Calling this function before {@link #hid_init()} or after {@link #hid_exit()} has no effect. | |
* | |
* @since hidapi 0.12.0 | |
* @param openExclusive When set to 0 - all further devices will be opened in non-exclusive mode. | |
* Otherwise - all further devices will be opened in exclusive mode. | |
*/ | |
// Wrap all macOS API calls into a single thread, per https://github.com/libusb/hidapi/issues/666 | |
private static final ExecutorService executor = Executors.newSingleThreadExecutor(); | |
private interface RealDarwinHidApiLibrary extends HidrawHidApiLibrary { | |
/** | |
* Changes the behavior of all further calls to {@link #hid_open(short, short, WString)} or {@link #hid_open_path(String)}. | |
* <p> | |
* All devices opened by HIDAPI with {@link #hid_open(short, short, WString)} or {@link #hid_open_path(String)} | |
* are opened in exclusive mode per default. | |
* <p> | |
* Calling this function before {@link #hid_init()} or after {@link #hid_exit()} has no effect. | |
* | |
* @since hidapi 0.12.0 | |
* @param openExclusive When set to 0 - all further devices will be opened in non-exclusive mode. | |
* Otherwise - all further devices will be opened in exclusive mode. | |
*/ | |
void hid_darwin_set_open_exclusive(int openExclusive); | |
} | |
@Override | |
public void hid_init() { | |
try { | |
executor.submit(REAL_INSTANCE::hid_init).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public void hid_exit() { | |
try { | |
executor.submit(REAL_INSTANCE::hid_exit).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public Pointer hid_open(short vendor_id, short product_id, WString serial_number) { | |
try { | |
return executor.submit(() -> REAL_INSTANCE.hid_open(vendor_id, product_id, serial_number)).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public void hid_close(Pointer device) { | |
try { | |
executor.submit(() -> REAL_INSTANCE.hid_close(device)).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public Pointer hid_error(Pointer device) { | |
try { | |
return executor.submit(() -> REAL_INSTANCE.hid_error(device)).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public int hid_read(Pointer device, WideStringBuffer.ByReference bytes, int length) { | |
try { | |
return executor.submit(() -> REAL_INSTANCE.hid_read(device, bytes, length)).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public int hid_read_timeout(Pointer device, WideStringBuffer.ByReference bytes, int length, int timeout) { | |
try { | |
return executor.submit(() -> REAL_INSTANCE.hid_read_timeout(device, bytes, length, timeout)).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public int hid_write(Pointer device, WideStringBuffer.ByReference data, int len) { | |
try { | |
return executor.submit(() -> REAL_INSTANCE.hid_write(device, data, len)).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public int hid_get_feature_report(Pointer device, WideStringBuffer.ByReference data, int length) { | |
try { | |
return executor.submit(() -> REAL_INSTANCE.hid_get_feature_report(device, data, length)).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public int hid_send_feature_report(Pointer device, WideStringBuffer.ByReference data, int length) { | |
try { | |
return executor.submit(() -> REAL_INSTANCE.hid_send_feature_report(device, data, length)).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public int hid_get_indexed_string(Pointer device, int idx, WideStringBuffer.ByReference string, int len) { | |
try { | |
return executor.submit(() -> REAL_INSTANCE.hid_get_indexed_string(device, idx, string, len)).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public int hid_get_report_descriptor(Pointer device, byte[] buffer, int size) { | |
try { | |
return executor.submit(() -> REAL_INSTANCE.hid_get_report_descriptor(device, buffer, size)).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public int hid_get_manufacturer_string(Pointer device, WideStringBuffer.ByReference str, int len) { | |
try { | |
return executor.submit(() -> REAL_INSTANCE.hid_get_manufacturer_string(device, str, len)).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public int hid_get_product_string(Pointer device, WideStringBuffer.ByReference str, int len) { | |
try { | |
return executor.submit(() -> REAL_INSTANCE.hid_get_product_string(device, str, len)).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public int hid_get_serial_number_string(Pointer device, WideStringBuffer.ByReference str, int len) { | |
try { | |
return executor.submit(() -> REAL_INSTANCE.hid_get_serial_number_string(device, str, len)).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public int hid_set_nonblocking(Pointer device, int nonblock) { | |
try { | |
return executor.submit(() -> REAL_INSTANCE.hid_set_nonblocking(device, nonblock)).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public HidDeviceInfoStructure hid_enumerate(short vendor_id, short product_id) { | |
try { | |
return executor.submit(() -> REAL_INSTANCE.hid_enumerate(vendor_id, product_id)).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public void hid_free_enumeration(Pointer devs) { | |
try { | |
executor.submit(() -> REAL_INSTANCE.hid_free_enumeration(devs)).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public Pointer hid_open_path(String path) { | |
try { | |
return executor.submit(() -> REAL_INSTANCE.hid_open_path(path)).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public String hid_version_str() { | |
try { | |
return executor.submit(REAL_INSTANCE::hid_version_str).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public void hid_darwin_set_open_exclusive(int openExclusive) { | |
try { | |
executor.submit(() -> REAL_INSTANCE.hid_darwin_set_open_exclusive(openExclusive)).get(); | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment