/* * libbrlapi - A library providing access to braille terminals for applications. * * Copyright (C) 2006-2021 by * Samuel Thibault * Sébastien Hinderer * * libbrlapi comes with ABSOLUTELY NO WARRANTY. * * This is free software, placed under the terms of the * GNU Lesser General Public License, as published by the Free Software * Foundation; either version 2.1 of the License, or (at your option) any * later version. Please see the file LICENSE-LGPL for details. * * Web Page: http://brltty.app/ * * This software is maintained by Dave Mielke . */ #include #include #include #include #include #include #include #include "bindings.h" #define NEW_PRIMITIVE_WRAPPER(name, type, sig) \ static jobject \ new##name (JNIEnv *env, type value) { \ static JAVA_CLASS_VARIABLE(class); \ static JAVA_METHOD_VARIABLE(constructor); \ \ if (!javaFindClassAndMethod( \ env, \ &class, JAVA_OBJ_LANG(#name), \ &constructor, JAVA_CONSTRUCTOR_NAME, \ JAVA_SIG_CONSTRUCTOR(JAVA_SIG_##sig) \ )) return NULL; \ \ return (*env)->NewObject(env, class, constructor, value); \ } NEW_PRIMITIVE_WRAPPER(Long, jlong, LONG) #define BRLAPI_NO_DEPRECATED #define BRLAPI_NO_SINGLE_SESSION #include "brlapi.h" #define BRLAPI_OBJECT(name) "org/a11y/brlapi/" name static jint jniVersion = 0; static int libraryVersion_major = 0; static int libraryVersion_minor = 0; static int libraryVersion_revision = 0; JAVA_STATIC_METHOD( org_a11y_brlapi_NativeComponent, initializeNativeData, void ) { jniVersion = (*env)->GetVersion(env); brlapi_getLibraryVersion( &libraryVersion_major, &libraryVersion_minor, &libraryVersion_revision ); } static void logJavaVirtualMachineError (jint error, const char *method) { const char *message; switch (error) { case JNI_OK: message = "success"; break; default: #ifdef JNI_ERR case JNI_ERR: #endif /* JNI_ERR */ message = "unknown error"; break; #ifdef JNI_EDETACHED case JNI_EDETACHED: message = "thread not attached to virtual machine"; break; #endif /* JNI_EDETACHED */ #ifdef JNI_EVERSION case JNI_EVERSION: message = "version error"; break; #endif /* JNI_EVERSION */ #ifdef JNI_ENOMEM case JNI_ENOMEM: message = "not enough memory"; break; #endif /* JNI_ENOMEM */ #ifdef JNI_EEXIST case JNI_EEXIST: message = "virtual machine already created"; break; #endif /* JNI_EEXIST */ #ifdef JNI_EINVAL case JNI_EINVAL: message = "invalid argument"; break; #endif /* JNI_EINVAL */ } fprintf(stderr, "Java virtual machine error %d in %s: %s\n", error, method, message); } static pthread_key_t threadKey_vm; static void destroyThreadKey_vm (void *value) { JavaVM *vm = value; (*vm)->DetachCurrentThread(vm); } static void createThreadKey_vm (void) { pthread_key_create(&threadKey_vm, destroyThreadKey_vm); } static void setThreadExitHandler (JavaVM *vm) { static pthread_once_t once = PTHREAD_ONCE_INIT; pthread_once(&once, createThreadKey_vm); pthread_setspecific(threadKey_vm, vm); } static JNIEnv * getJavaEnvironment (brlapi_handle_t *handle) { JavaVM *vm = brlapi__getClientData(handle); void *env = NULL; if (vm) { jint result = (*vm)->GetEnv(vm, &env, jniVersion); if (result != JNI_OK) { if (result == JNI_EDETACHED) { JavaVMAttachArgs args = { .version = jniVersion, .name = NULL, .group = NULL }; if ((result = (*vm)->AttachCurrentThread(vm, &env, &args)) == JNI_OK) { setThreadExitHandler(env); } else { logJavaVirtualMachineError(result, "AttachCurrentThread"); } } else { logJavaVirtualMachineError(result, "GetEnv"); } } } return env; } static void throwJavaError (JNIEnv *env, const char *object, const char *message) { if ((*env)->ExceptionCheck(env)) return; jclass class = (*env)->FindClass(env, object); if (class) (*env)->ThrowNew(env, class, message); } static void logBrlapiError (const char *label) { fprintf(stderr, "%s: API=%d Libc=%d GAI=%d: %s\n", label, brlapi_errno, brlapi_libcerrno, brlapi_gaierrno, brlapi_strerror(&brlapi_error) ); } static void throwAPIError (JNIEnv *env) { if (0) logBrlapiError("API Error"); if ((*env)->ExceptionCheck(env)) return; { const char *object = NULL; switch (brlapi_errno) { case BRLAPI_ERROR_NOMEM: object = JAVA_OBJ_OUT_OF_MEMORY_ERROR; break; case BRLAPI_ERROR_EOF: object = BRLAPI_OBJECT("LostConnectionException"); break; case BRLAPI_ERROR_LIBCERR: { switch (brlapi_libcerrno) { case EINTR: object = JAVA_OBJ_INTERRUPTED_IO_EXCEPTION; break; } break; } } if (object) { throwJavaError(env, object, brlapi_errfun); return; } } static JAVA_CLASS_VARIABLE(class); if (!javaFindClass(env, &class, BRLAPI_OBJECT("APIError"))) return; static JAVA_METHOD_VARIABLE(constructor); if (!JAVA_FIND_CONSTRUCTOR(env, &constructor, class, JAVA_SIG_INT // api error JAVA_SIG_INT // os error JAVA_SIG_INT // gai error JAVA_SIG_STRING // function name )) return; jstring jFunction; if (!brlapi_errfun) { jFunction = NULL; } else if (!(jFunction = (*env)->NewStringUTF(env, brlapi_errfun))) { return; } jobject object = (*env)->NewObject( env, class, constructor, brlapi_errno, brlapi_libcerrno, brlapi_gaierrno, jFunction ); if (object) { (*env)->Throw(env, object); } else if (jFunction) { (*env)->ReleaseStringUTFChars(env, jFunction, brlapi_errfun); } } static void throwConnectError (JNIEnv *env, const brlapi_connectionSettings_t *settings) { if (0) logBrlapiError("Connect Error"); const char *object = NULL; const char *message = NULL; const char *host = NULL; const char *auth = NULL; if (settings) { host = settings->host; auth = settings->auth; } switch (brlapi_errno) { case BRLAPI_ERROR_CONNREFUSED: object = BRLAPI_OBJECT("UnavailableServiceException"); message = host; break; case BRLAPI_ERROR_AUTHENTICATION: object = BRLAPI_OBJECT("AuthenticationException"); message = auth; break; case BRLAPI_ERROR_GAIERR: { switch (brlapi_gaierrno) { case EAI_SYSTEM: goto SYSTEM_ERROR; case EAI_MEMORY: object = JAVA_OBJ_OUT_OF_MEMORY_ERROR; break; case EAI_NODATA: case EAI_NONAME: object = BRLAPI_OBJECT("UnknownHostException"); message = host; break; } break; } SYSTEM_ERROR: case BRLAPI_ERROR_LIBCERR: { switch (brlapi_libcerrno) { } break; } } if (object) { if (!message) message = ""; throwJavaError(env, object, message); } else { throwAPIError(env); } } static void BRLAPI_STDCALL handleAPIException (brlapi_handle_t *handle, int error, brlapi_packetType_t type, const void *packet, size_t size) { JNIEnv *env = getJavaEnvironment(handle); if ((*env)->ExceptionCheck(env)) return; jbyteArray jPacket = (*env)->NewByteArray(env, size); if (!jPacket) return; (*env)->SetByteArrayRegion(env, jPacket, 0, size, (jbyte *) packet); static JAVA_CLASS_VARIABLE(class); if (!javaFindClass(env, &class, BRLAPI_OBJECT("APIException"))) return; static JAVA_METHOD_VARIABLE(constructor); if (!JAVA_FIND_CONSTRUCTOR(env, &constructor, class, JAVA_SIG_LONG // handle JAVA_SIG_INT // error JAVA_SIG_INT // type JAVA_SIG_ARRAY(JAVA_SIG_BYTE) // packet )) return; jclass object = (*env)->NewObject( env, class, constructor, (jlong) (intptr_t) handle, error, type, jPacket ); if (!object) return; (*env)->Throw(env, object); } JAVA_STATIC_METHOD( org_a11y_brlapi_APIVersion, getMajor, jint ) { return libraryVersion_major; } JAVA_STATIC_METHOD( org_a11y_brlapi_APIVersion, getMinor, jint ) { return libraryVersion_minor; } JAVA_STATIC_METHOD( org_a11y_brlapi_APIVersion, getRevision, jint ) { return libraryVersion_revision; } #define GET_CLASS(env, class, object, ret) \ jclass class; \ do { \ if (!((class) = (*(env))->GetObjectClass((env), (object)))) return ret; \ } while (0) #define FIND_FIELD(env, field, class, name, signature, ret) \ jfieldID field; \ do { \ if (!(field = (*(env))->GetFieldID((env), (class), (name), (signature)))) return ret; \ } while (0) #define FIND_CONNECTION_HANDLE(env, object, ret) \ GET_CLASS((env), class, (object), ret); \ FIND_FIELD((env), field, class, "connectionHandle", JAVA_SIG_LONG, ret); #define GET_CONNECTION_HANDLE(env, object, ret) \ brlapi_handle_t *handle; \ do { \ FIND_CONNECTION_HANDLE((env), (object), ret); \ handle = (void*) (intptr_t) JAVA_GET_FIELD((env), Long, (object), field); \ if (!handle) { \ throwJavaError((env), JAVA_OBJ_ILLEGAL_STATE_EXCEPTION, "connection has been closed"); \ return ret; \ } \ } while (0) #define SET_CONNECTION_HANDLE(env, object, value, ret) \ do { \ FIND_CONNECTION_HANDLE((env), (object), ret); \ JAVA_SET_FIELD((env), Long, (object), field, (jlong) (intptr_t) (value)); \ } while (0) JAVA_STATIC_METHOD( org_a11y_brlapi_ConnectionSettings, getKeyfileDirectory, jstring ) { return (*env)->NewStringUTF(env, BRLAPI_ETCDIR); } JAVA_STATIC_METHOD( org_a11y_brlapi_ConnectionSettings, getKeyfileName, jstring ) { return (*env)->NewStringUTF(env, BRLAPI_AUTHKEYFILE); } static int openConnection ( JNIEnv *env, jobject connection, jobject jRequestedSettings, brlapi_connectionSettings_t *cRequestedSettings, jobject jActualSettings, brlapi_connectionSettings_t *cActualSettings, brlapi_handle_t **handle, int *fileDescriptor, jobject *jRequestedHost, jobject *jRequestedAuth ) { if (jRequestedSettings) { GET_CLASS(env, class, jRequestedSettings, 0); { FIND_FIELD(env, field, class, "serverHost", JAVA_SIG_STRING, 0); *jRequestedHost = JAVA_GET_FIELD(env, Object, jRequestedSettings, field); if (*jRequestedHost) { if (!(cRequestedSettings->host = (*env)->GetStringUTFChars(env, *jRequestedHost, NULL))) { return 0; } } } { FIND_FIELD(env, field, class, "authenticationScheme", JAVA_SIG_STRING, 0); *jRequestedAuth = JAVA_GET_FIELD(env, Object, jRequestedSettings, field); if (*jRequestedAuth) { if (!(cRequestedSettings->auth = (*env)->GetStringUTFChars(env, *jRequestedAuth, NULL))) { return 0; } } } } if (!(*handle = malloc(brlapi_getHandleSize()))) { throwJavaError(env, JAVA_OBJ_OUT_OF_MEMORY_ERROR, __func__); return 0; } *fileDescriptor = brlapi__openConnection( *handle, cRequestedSettings, cActualSettings ); if (*fileDescriptor < 0) { throwConnectError(env, cRequestedSettings); return 0; } if (cActualSettings) { GET_CLASS(env, class, jActualSettings, 0); if (cActualSettings->host) { jstring host = (*env)->NewStringUTF(env, cActualSettings->host); if (!host) return 0; FIND_FIELD(env, field, class, "serverHost", JAVA_SIG_STRING, 0); JAVA_SET_FIELD(env, Object, jActualSettings, field, host); if ((*env)->ExceptionCheck(env)) return 0; } if (cActualSettings->auth) { jstring auth = (*env)->NewStringUTF(env, cActualSettings->auth); if (!auth) return 0; FIND_FIELD(env, field, class, "authenticationScheme", JAVA_SIG_STRING, 0); JAVA_SET_FIELD(env, Object, jActualSettings, field, auth); if ((*env)->ExceptionCheck(env)) return 0; } } { JavaVM *vm; (*env)->GetJavaVM(env, &vm); brlapi__setClientData(*handle, vm); } brlapi__setExceptionHandler(*handle, handleAPIException); SET_CONNECTION_HANDLE(env, connection, *handle, 0); return 1; } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, openConnection, jint, jobject jRequestedSettings, jobject jActualSettings ) { brlapi_connectionSettings_t cRequestedSettings, *pRequestedSettings; memset(&cRequestedSettings, 0, sizeof(cRequestedSettings)); pRequestedSettings = jRequestedSettings? &cRequestedSettings: NULL; brlapi_connectionSettings_t cActualSettings, *pActualSettings; memset(&cActualSettings, 0, sizeof(cActualSettings)); pActualSettings = jActualSettings? &cActualSettings: NULL; brlapi_handle_t *handle = NULL; int fileDescriptor = -1; jobject jRequestedHost = NULL; jobject jRequestedAuth = NULL; int opened = openConnection( env, this, jRequestedSettings, pRequestedSettings, jActualSettings, pActualSettings, &handle, &fileDescriptor, &jRequestedHost, &jRequestedAuth ); if (cRequestedSettings.host) { (*env)->ReleaseStringUTFChars(env, jRequestedHost, cRequestedSettings.host); } if (cRequestedSettings.auth) { (*env)->ReleaseStringUTFChars(env, jRequestedAuth, cRequestedSettings.auth); } if (opened) return fileDescriptor; if (fileDescriptor >= 0) brlapi__closeConnection(handle); if (handle) free(handle); return -1; } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, closeConnection, void ) { GET_CONNECTION_HANDLE(env, this, ); brlapi__closeConnection(handle); free(handle); SET_CONNECTION_HANDLE(env, this, NULL, ); } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, getDriverName, jstring ) { GET_CONNECTION_HANDLE(env, this, NULL); char name[0X20]; if (brlapi__getDriverName(handle, name, sizeof(name)) < 0) { throwAPIError(env); return NULL; } name[sizeof(name)-1] = 0; return (*env)->NewStringUTF(env, name); } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, getModelIdentifier, jstring ) { GET_CONNECTION_HANDLE(env, this, NULL); char identifier[0X20]; if (brlapi__getModelIdentifier(handle, identifier, sizeof(identifier)) < 0) { throwAPIError(env); return NULL; } identifier[sizeof(identifier)-1] = 0; return (*env)->NewStringUTF(env, identifier); } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, getDisplaySize, jobject ) { GET_CONNECTION_HANDLE(env, this, NULL); unsigned int width, height; if (brlapi__getDisplaySize(handle, &width, &height) < 0) { throwAPIError(env); return NULL; } jclass class = (*env)->FindClass(env, BRLAPI_OBJECT("DisplaySize")); if (!class) return NULL; jmethodID constructor = JAVA_GET_CONSTRUCTOR(env, class, JAVA_SIG_INT // width JAVA_SIG_INT // height ); if (!constructor) return NULL; jobject object = (*env)->NewObject(env, class, constructor, width, height); if (!object) return NULL; return object; } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, pause, void, jint milliseconds ) { GET_CONNECTION_HANDLE(env, this, ); int result = brlapi__pause(handle, milliseconds); if (result < 0) { throwAPIError(env); } } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, enterTtyMode, jint, jint jtty, jstring jdriver ) { int tty ; char *driver; int result; GET_CONNECTION_HANDLE(env, this, -1); tty = (int)jtty; if (!jdriver) driver = NULL; else if (!(driver = (char *)(*env)->GetStringUTFChars(env, jdriver, NULL))) { throwJavaError(env, JAVA_OBJ_OUT_OF_MEMORY_ERROR, __func__); return -1; } result = brlapi__enterTtyMode(handle, tty,driver); if (result < 0) { throwAPIError(env); return -1; } return (jint) result; } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, enterTtyModeWithPath, void, jstring jdriver, jintArray jttys ) { jint *ttys ; char *driver; int result; GET_CONNECTION_HANDLE(env, this, ); if (!jttys) { throwJavaError(env, JAVA_OBJ_NULL_POINTER_EXCEPTION, __func__); return; } if (!(ttys = (*env)->GetIntArrayElements(env, jttys, NULL))) { throwJavaError(env, JAVA_OBJ_OUT_OF_MEMORY_ERROR, __func__); return; } if (!jdriver) { driver = NULL; } else if (!(driver = (char *)(*env)->GetStringUTFChars(env, jdriver, NULL))) { throwJavaError(env, JAVA_OBJ_OUT_OF_MEMORY_ERROR, __func__); return; } result = brlapi__enterTtyModeWithPath(handle, ttys,(*env)->GetArrayLength(env,jttys),driver); (*env)->ReleaseIntArrayElements(env, jttys, ttys, JNI_ABORT); if (result < 0) { throwAPIError(env); return; } } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, leaveTtyMode, void ) { GET_CONNECTION_HANDLE(env, this, ); if (brlapi__leaveTtyMode(handle) < 0) { throwAPIError(env); return; } } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, setFocus, void, jint tty ) { GET_CONNECTION_HANDLE(env, this, ); if (brlapi__setFocus(handle, tty) < 0) throwAPIError(env); } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, writeText, void, jint cursor, jstring jText ) { GET_CONNECTION_HANDLE(env, this, ); const char *cText; if (!jText) { cText = NULL; } else if (!(cText = (*env)->GetStringUTFChars(env, jText, NULL))) { throwJavaError(env, JAVA_OBJ_OUT_OF_MEMORY_ERROR, __func__); return; } int result = brlapi__writeText(handle, cursor, cText); if (jText) (*env)->ReleaseStringUTFChars(env, jText, cText); if (result < 0) throwAPIError(env); } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, writeDots, void, jbyteArray jDots ) { GET_CONNECTION_HANDLE(env, this, ); if (!jDots) { throwJavaError(env, JAVA_OBJ_NULL_POINTER_EXCEPTION, __func__); return; } jbyte *cDots = (*env)->GetByteArrayElements(env, jDots, NULL); if (!cDots) return; int result = brlapi__writeDots(handle, (const unsigned char *)cDots); (*env)->ReleaseByteArrayElements(env, jDots, cDots, JNI_ABORT); if (result < 0) { throwAPIError(env); return; } } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, write, void, jobject jArguments ) { if (!jArguments) { throwJavaError(env, JAVA_OBJ_NULL_POINTER_EXCEPTION, __func__); return; } GET_CONNECTION_HANDLE(env, this, ); GET_CLASS(env, class, jArguments, ); brlapi_writeArguments_t cArguments = BRLAPI_WRITEARGUMENTS_INITIALIZER; { FIND_FIELD(env, field, class, "displayNumber", JAVA_SIG_INT, ); cArguments.displayNumber = JAVA_GET_FIELD(env, Int, jArguments, field); } { FIND_FIELD(env, field, class, "regionBegin", JAVA_SIG_INT, ); cArguments.regionBegin = JAVA_GET_FIELD(env, Int, jArguments, field); } { FIND_FIELD(env, field, class, "regionSize", JAVA_SIG_INT, ); cArguments.regionSize = JAVA_GET_FIELD(env, Int, jArguments, field); } jstring jText; { FIND_FIELD(env, field, class, "text", JAVA_SIG_STRING, ); if ((jText = JAVA_GET_FIELD(env, Object, jArguments, field))) { cArguments.text = (char *) (*env)->GetStringUTFChars(env, jText, NULL); cArguments.charset = "UTF-8"; } else { cArguments.text = NULL; } } jbyteArray jAndMask; { FIND_FIELD(env, field, class, "andMask", JAVA_SIG_ARRAY(JAVA_SIG_BYTE), ); if ((jAndMask = JAVA_GET_FIELD(env, Object, jArguments, field))) { cArguments.andMask = (unsigned char *) (*env)->GetByteArrayElements(env, jAndMask, NULL); } else { cArguments.andMask = NULL; } } jbyteArray jOrMask; { FIND_FIELD(env, field, class, "orMask", JAVA_SIG_ARRAY(JAVA_SIG_BYTE), ); if ((jOrMask = JAVA_GET_FIELD(env, Object, jArguments, field))) { cArguments.orMask = (unsigned char *) (*env)->GetByteArrayElements(env, jOrMask, NULL); } else { cArguments.orMask = NULL; } } { FIND_FIELD(env, field, class, "cursorPosition", JAVA_SIG_INT, ); cArguments.cursor = JAVA_GET_FIELD(env, Int, jArguments, field); } int result = brlapi__write(handle, &cArguments); if (jText) (*env)->ReleaseStringUTFChars(env, jText, cArguments.text); if (jAndMask) (*env)->ReleaseByteArrayElements(env, jAndMask, (jbyte*) cArguments.andMask, JNI_ABORT); if (jOrMask) (*env)->ReleaseByteArrayElements(env, jOrMask, (jbyte*) cArguments.orMask, JNI_ABORT); if (result < 0) throwAPIError(env); } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, readKey, jobject, jboolean jWait ) { GET_CONNECTION_HANDLE(env, this, NULL); int cWait = jWait != JNI_FALSE; brlapi_keyCode_t code; int result = brlapi__readKey(handle, cWait, &code); if (result < 0) throwAPIError(env); if (!result) return NULL; return newLong(env, code); } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, readKeyWithTimeout, jlong, jint milliseconds ) { GET_CONNECTION_HANDLE(env, this, -1); brlapi_keyCode_t code; int result = brlapi__readKeyWithTimeout(handle, milliseconds, &code); if (result < 0) { throwAPIError(env); } else if (!result) { throwJavaError(env, JAVA_OBJ_TIMEOUT_EXCEPTION, __func__); } return (jlong)code; } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, ignoreKeys, void, jlong jrange, jlongArray js ) { jlong *s; unsigned int n; int result; GET_CONNECTION_HANDLE(env, this, ); if (!js) { throwJavaError(env, JAVA_OBJ_NULL_POINTER_EXCEPTION, __func__); return; } n = (unsigned int) (*env)->GetArrayLength(env, js); s = (*env)->GetLongArrayElements(env, js, NULL); // XXX jlong != brlapi_keyCode_t probably result = brlapi__ignoreKeys(handle, jrange, (const brlapi_keyCode_t *)s, n); (*env)->ReleaseLongArrayElements(env, js, s, JNI_ABORT); if (result < 0) { throwAPIError(env); return; } } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, acceptKeys, void, jlong jrange, jlongArray js ) { jlong *s; unsigned int n; int result; GET_CONNECTION_HANDLE(env, this, ); if (!js) { throwJavaError(env, JAVA_OBJ_NULL_POINTER_EXCEPTION, __func__); return; } n = (unsigned int) (*env)->GetArrayLength(env, js); s = (*env)->GetLongArrayElements(env, js, NULL); // XXX jlong != brlapi_keyCode_t probably result = brlapi__acceptKeys(handle, jrange, (const brlapi_keyCode_t *)s, n); (*env)->ReleaseLongArrayElements(env, js, s, JNI_ABORT); if (result < 0) { throwAPIError(env); return; } } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, ignoreAllKeys, void ) { GET_CONNECTION_HANDLE(env, this, ); if (brlapi__ignoreAllKeys(handle) < 0) throwAPIError(env); } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, acceptAllKeys, void ) { GET_CONNECTION_HANDLE(env, this, ); if (brlapi__acceptAllKeys(handle) < 0) throwAPIError(env); } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, ignoreKeyRanges, void, jobjectArray js ) { unsigned int n; GET_CONNECTION_HANDLE(env, this, ); if (!js) { throwJavaError(env, JAVA_OBJ_NULL_POINTER_EXCEPTION, __func__); return; } n = (unsigned int) (*env)->GetArrayLength(env, js); { unsigned int i; brlapi_range_t s[n]; for (i=0; iGetObjectArrayElement(env, js, i); jlong *l = (*env)->GetLongArrayElements(env, jl, NULL); s[i].first = l[0]; s[i].last = l[1]; (*env)->ReleaseLongArrayElements(env, jl, l, JNI_ABORT); } if (brlapi__ignoreKeyRanges(handle, s, n)) { throwAPIError(env); return; } } } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, acceptKeyRanges, void, jobjectArray js ) { unsigned int n; GET_CONNECTION_HANDLE(env, this, ); if (!js) { throwJavaError(env, JAVA_OBJ_NULL_POINTER_EXCEPTION, __func__); return; } n = (unsigned int) (*env)->GetArrayLength(env, js); { unsigned int i; brlapi_range_t s[n]; for (i=0; iGetObjectArrayElement(env, js, i); jlong *l = (*env)->GetLongArrayElements(env, jl, NULL); s[i].first = l[0]; s[i].last = l[1]; (*env)->ReleaseLongArrayElements(env, jl, l, JNI_ABORT); } if (brlapi__acceptKeyRanges(handle, s, n)) { throwAPIError(env); return; } } } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, enterRawMode, void, jstring jdriver ) { char *driver; int res; GET_CONNECTION_HANDLE(env, this, ); if (!jdriver) { throwJavaError(env, JAVA_OBJ_NULL_POINTER_EXCEPTION, __func__); return; } else if (!(driver = (char *)(*env)->GetStringUTFChars(env, jdriver, NULL))) { throwJavaError(env, JAVA_OBJ_NULL_POINTER_EXCEPTION, __func__); return; } res = brlapi__enterRawMode(handle, driver); if (jdriver) (*env)->ReleaseStringUTFChars(env, jdriver, driver); if (res < 0) { throwAPIError(env); return; } } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, leaveRawMode, void ) { GET_CONNECTION_HANDLE(env, this, ); if (brlapi__leaveRawMode(handle) < 0) { throwAPIError(env); return; } } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, sendRaw, jint, jbyteArray jbuf ) { jbyte *buf; unsigned int n; int result; GET_CONNECTION_HANDLE(env, this, -1); if (!jbuf) { throwJavaError(env, JAVA_OBJ_NULL_POINTER_EXCEPTION, __func__); return -1; } n = (unsigned int) (*env)->GetArrayLength(env, jbuf); buf = (*env)->GetByteArrayElements(env, jbuf, NULL); result = brlapi__sendRaw(handle, (const unsigned char *)buf, n); (*env)->ReleaseByteArrayElements(env, jbuf, buf, JNI_ABORT); if (result < 0) { throwAPIError(env); return -1; } return (jint) result; } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, recvRaw, jint, jbyteArray jbuf ) { jbyte *buf; unsigned int n; int result; GET_CONNECTION_HANDLE(env, this, -1); if (!jbuf) { throwJavaError(env, JAVA_OBJ_NULL_POINTER_EXCEPTION, __func__); return -1; } n = (unsigned int) (*env)->GetArrayLength(env, jbuf); buf = (*env)->GetByteArrayElements(env, jbuf, NULL); result = brlapi__recvRaw(handle, (unsigned char *)buf, n); if (result < 0) { (*env)->ReleaseByteArrayElements(env, jbuf, buf, JNI_ABORT); throwAPIError(env); return -1; } (*env)->ReleaseByteArrayElements(env, jbuf, buf, 0); return (jint) result; } static int checkParameter ( JNIEnv *env, jint parameter, jlong subparam, jboolean global, const brlapi_param_properties_t **properties, brlapi_param_flags_t *flags ) { if (!(*properties = brlapi_getParameterProperties(parameter))) { throwJavaError(env, JAVA_OBJ_ILLEGAL_ARGUMENT_EXCEPTION, "parameter out of range"); return 0; } if (!(*properties)->hasSubparam && (subparam != 0)) { throwJavaError(env, JAVA_OBJ_ILLEGAL_ARGUMENT_EXCEPTION, "nonzero subparam"); return 0; } *flags = 0; if (global == JNI_TRUE) { *flags |= BRLAPI_PARAMF_GLOBAL; } else if (global == JNI_FALSE) { *flags |= BRLAPI_PARAMF_LOCAL; } return 1; } static jobject newParameterValueObject ( JNIEnv *env, const brlapi_param_properties_t *properties, const void *value, size_t size ) { jobject result = NULL; size_t count = size; switch (properties->type) { case BRLAPI_PARAM_TYPE_STRING: { result = (*env)->NewStringUTF(env, value); break; } case BRLAPI_PARAM_TYPE_BOOLEAN: { const brlapi_param_bool_t *cBooleans = value; count /= sizeof(*cBooleans); result = (*env)->NewBooleanArray(env, count); if (result && count) { jboolean jBooleans[count]; for (jsize i=0; iSetBooleanArrayRegion(env, result, 0, count, jBooleans); } break; } case BRLAPI_PARAM_TYPE_UINT8: { result = (*env)->NewByteArray(env, count); if (result && count) { (*env)->SetByteArrayRegion(env, result, 0, count, value); } break; } case BRLAPI_PARAM_TYPE_UINT16: { count /= 2; result = (*env)->NewShortArray(env, count); if (result && count) { (*env)->SetShortArrayRegion(env, result, 0, count, value); } break; } case BRLAPI_PARAM_TYPE_UINT32: { count /= 4; result = (*env)->NewIntArray(env, count); if (result && count) { (*env)->SetIntArrayRegion(env, result, 0, count, value); } break; } case BRLAPI_PARAM_TYPE_UINT64: { count /= 8; result = (*env)->NewLongArray(env, count); if (result && count) { (*env)->SetLongArrayRegion(env, result, 0, count, value); } break; } } return result; } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, getParameter, jobject, jint parameter, jlong subparam, jboolean global ) { GET_CONNECTION_HANDLE(env, this, NULL); jobject result = NULL; const brlapi_param_properties_t *properties; brlapi_param_flags_t flags; if (checkParameter(env, parameter, subparam, global, &properties, &flags)) { void *value; size_t size; if ((value = brlapi__getParameterAlloc(handle, parameter, subparam, flags, &size))) { result = newParameterValueObject(env, properties, value, size); free(value); } else { throwAPIError(env); } } return result; } static void setParameter ( JNIEnv *env, brlapi_handle_t *handle, jint parameter, jlong subparam, brlapi_param_flags_t flags, const void *data, size_t size ) { if (brlapi__setParameter(handle, parameter, subparam, flags, data, size) < 0) { throwAPIError(env); } } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, setParameter, void, jint parameter, jlong subparam, jboolean global, jobject value ) { GET_CONNECTION_HANDLE(env, this, ); const brlapi_param_properties_t *properties; brlapi_param_flags_t flags; if (checkParameter(env, parameter, subparam, global, &properties, &flags)) { switch (properties->type) { case BRLAPI_PARAM_TYPE_STRING: { jboolean isCopy; const char *string = (*env)->GetStringUTFChars(env, value, &isCopy); if (string) { setParameter( env, handle, parameter, subparam, flags, string, strlen(string) ); (*env)->ReleaseStringUTFChars(env, value, string); } break; } case BRLAPI_PARAM_TYPE_BOOLEAN: { jsize count = (*env)->GetArrayLength(env, value); if (!javaHasExceptionOccurred(env)) { jboolean values[count + 1]; (*env)->GetBooleanArrayRegion(env, value, 0, count, values); if (!javaHasExceptionOccurred(env)) { brlapi_param_bool_t booleans[count + 1]; for (unsigned int i=0; iGetArrayLength(env, value); if (!javaHasExceptionOccurred(env)) { jbyte values[count + 1]; (*env)->GetByteArrayRegion(env, value, 0, count, values); if (!javaHasExceptionOccurred(env)) { setParameter( env, handle, parameter, subparam, flags, values, count ); } } break; } case BRLAPI_PARAM_TYPE_UINT16: { jsize count = (*env)->GetArrayLength(env, value); if (!javaHasExceptionOccurred(env)) { jshort values[count + 1]; (*env)->GetShortArrayRegion(env, value, 0, count, values); if (!javaHasExceptionOccurred(env)) { setParameter( env, handle, parameter, subparam, flags, values, (count * 2) ); } } break; } case BRLAPI_PARAM_TYPE_UINT32: { jsize count = (*env)->GetArrayLength(env, value); if (!javaHasExceptionOccurred(env)) { jint values[count + 1]; (*env)->GetIntArrayRegion(env, value, 0, count, values); if (!javaHasExceptionOccurred(env)) { setParameter( env, handle, parameter, subparam, flags, values, (count * 4) ); } } break; } case BRLAPI_PARAM_TYPE_UINT64: { jsize count = (*env)->GetArrayLength(env, value); if (!javaHasExceptionOccurred(env)) { jlong values[count + 1]; (*env)->GetLongArrayRegion(env, value, 0, count, values); if (!javaHasExceptionOccurred(env)) { setParameter( env, handle, parameter, subparam, flags, values, (count * 8) ); } } break; } } } } typedef struct { brlapi_handle_t *handle; brlapi_paramCallbackDescriptor_t descriptor; struct { jobject object; jclass class; jmethodID method; } watcher; } WatchedParameterData; static void handleWatchedParameter ( brlapi_param_t parameter, brlapi_param_subparam_t subparam, brlapi_param_flags_t flags, void *identifier, const void *data, size_t length ) { WatchedParameterData *wpd = (WatchedParameterData *)identifier; brlapi_handle_t *handle = wpd->handle; JNIEnv *env = getJavaEnvironment(handle); if (!env) return; jobject value = newParameterValueObject( env, brlapi_getParameterProperties(parameter), data, length ); if (value) { (*env)->CallVoidMethod( env, wpd->watcher.object, wpd->watcher.method, parameter, subparam, value ); } } JAVA_INSTANCE_METHOD( org_a11y_brlapi_ConnectionBase, watchParameter, jlong, jint parameter, jlong subparam, jboolean global, jobject watcher ) { GET_CONNECTION_HANDLE(env, this, 0); const brlapi_param_properties_t *properties; brlapi_param_flags_t flags; if (checkParameter(env, parameter, subparam, global, &properties, &flags)) { WatchedParameterData *wpd; if ((wpd = malloc(sizeof(*wpd)))) { memset(wpd, 0, sizeof(*wpd)); wpd->handle = handle; if ((wpd->watcher.object = (*env)->NewGlobalRef(env, watcher))) { wpd->watcher.class = (*env)->FindClass( env, BRLAPI_OBJECT("ParameterWatcher") ); if (wpd->watcher.class) { wpd->watcher.method = (*env)->GetMethodID( env, wpd->watcher.class, "onParameterUpdated", JAVA_SIG_METHOD(JAVA_SIG_VOID, JAVA_SIG_INT // parameter JAVA_SIG_LONG // subparam JAVA_SIG_OBJECT(JAVA_OBJ_OBJECT) // value ) ); if (wpd->watcher.method) { wpd->descriptor = brlapi__watchParameter( handle, parameter, subparam, flags, handleWatchedParameter, wpd, NULL, 0 ); if (wpd->descriptor) return (intptr_t)wpd; throwAPIError(env); } } (*env)->DeleteGlobalRef(env, wpd->watcher.object); } free(wpd); } else { throwJavaError(env, JAVA_OBJ_OUT_OF_MEMORY_ERROR, __func__); } } return 0; } JAVA_STATIC_METHOD( org_a11y_brlapi_ConnectionBase, unwatchParameter, void, jlong identifier ) { WatchedParameterData *wpd = (WatchedParameterData *)(intptr_t)identifier; if (brlapi__unwatchParameter(wpd->handle, wpd->descriptor) < 0) { throwAPIError(env); } (*env)->DeleteGlobalRef(env, wpd->watcher.object); free(wpd); } JAVA_INSTANCE_METHOD( org_a11y_brlapi_APIError, toString, jstring ) { GET_CLASS(env, class, this, NULL); brlapi_error_t error; memset(&error, 0, sizeof(error)); { FIND_FIELD(env, field, class, "apiError", JAVA_SIG_INT, NULL); error.brlerrno = JAVA_GET_FIELD(env, Int, this, field); } { FIND_FIELD(env, field, class, "osError", JAVA_SIG_INT, NULL); error.libcerrno = JAVA_GET_FIELD(env, Int, this, field); } { FIND_FIELD(env, field, class, "gaiError", JAVA_SIG_INT, NULL); error.gaierrno = JAVA_GET_FIELD(env, Int, this, field); } jstring jFunction; { FIND_FIELD(env, field, class, "functionName", JAVA_SIG_STRING, NULL); if ((jFunction = JAVA_GET_FIELD(env, Object, this, field))) { const char *cFunction = (*env)->GetStringUTFChars(env, jFunction, NULL); if (!cFunction) return NULL; error.errfun = cFunction; } else { error.errfun = NULL; } } const char *cMessage = brlapi_strerror(&error); if (jFunction) (*env)->ReleaseStringUTFChars(env, jFunction, error.errfun); if (!cMessage) return NULL; size_t length = strlen(cMessage); char buffer[length + 1]; int copy = 0; while (length > 0) { size_t last = length - 1; if (cMessage[last] != '\n') break; length = last; copy = 1; } if (copy) { memcpy(buffer, cMessage, length); buffer[length] = 0; cMessage = buffer; } return (*env)->NewStringUTF(env, cMessage); } JAVA_INSTANCE_METHOD( org_a11y_brlapi_APIException, toString, jstring ) { GET_CONNECTION_HANDLE(env, this, NULL); GET_CLASS(env, class, this, NULL); jint error; { FIND_FIELD(env, field, class, "errorNumber", JAVA_SIG_INT, NULL); error = JAVA_GET_FIELD(env, Int, this, field); } jint type; { FIND_FIELD(env, field, class, "packetType", JAVA_SIG_INT, NULL); type = JAVA_GET_FIELD(env, Int, this, field); } jbyteArray jPacket; { FIND_FIELD(env, field, class, "failedPacket", JAVA_SIG_ARRAY(JAVA_SIG_BYTE), NULL); if (!(jPacket = JAVA_GET_FIELD(env, Object, this, field))) { throwJavaError(env, JAVA_OBJ_NULL_POINTER_EXCEPTION, __func__); return NULL; } } jint size = (*env)->GetArrayLength(env, jPacket); jbyte *cPacket = (*env)->GetByteArrayElements(env, jPacket, NULL); if (!cPacket) { throwJavaError(env, JAVA_OBJ_OUT_OF_MEMORY_ERROR, __func__); return NULL; } char buffer[0X100]; brlapi__strexception(handle, buffer, sizeof(buffer), error, type, cPacket, size); jstring message = (*env)->NewStringUTF(env, buffer); (*env)->ReleaseByteArrayElements(env, jPacket, cPacket, JNI_ABORT); return message; } JAVA_STATIC_METHOD( org_a11y_brlapi_APIException, getPacketTypeName, jstring, jint type ) { const char *name = brlapi_getPacketTypeName((brlapi_packetType_t) type); if (!name) return NULL; return (*env)->NewStringUTF(env, name); } JAVA_INSTANCE_METHOD( org_a11y_brlapi_CommandKeycode, expandKeycode, void, jlong code ) { brlapi_expandedKeyCode_t ekc; GET_CLASS(env, class, this, ); if (brlapi_expandKeyCode((brlapi_keyCode_t)code, &ekc) < 0) { memset(&ekc, 0, sizeof(ekc)); ekc.type = code & BRLAPI_KEY_TYPE_MASK; ekc.command = code & BRLAPI_KEY_CODE_MASK; ekc.flags = (code & BRLAPI_KEY_FLAGS_MASK) >> BRLAPI_KEY_FLAGS_SHIFT; } { FIND_FIELD(env, field, class, "typeValue", JAVA_SIG_INT, ); JAVA_SET_FIELD(env, Int, this, field, ekc.type); } { FIND_FIELD(env, field, class, "commandValue", JAVA_SIG_INT, ); JAVA_SET_FIELD(env, Int, this, field, ekc.command); } { FIND_FIELD(env, field, class, "argumentValue", JAVA_SIG_INT, ); JAVA_SET_FIELD(env, Int, this, field, ekc.argument); } { FIND_FIELD(env, field, class, "flagsValue", JAVA_SIG_INT, ); JAVA_SET_FIELD(env, Int, this, field, ekc.flags); } } JAVA_INSTANCE_METHOD( org_a11y_brlapi_CommandKeycode, describeKeycode, void, jlong code ) { brlapi_describedKeyCode_t dkc; GET_CLASS(env, class, this, ); if (brlapi_describeKeyCode((brlapi_keyCode_t)code, &dkc) < 0) { memset(&dkc, 0, sizeof(dkc)); dkc.type = "UNSUPPORTED"; } { jstring name = (*env)->NewStringUTF(env, dkc.type); if (!name) return; FIND_FIELD(env, field, class, "typeName", JAVA_SIG_STRING, ); JAVA_SET_FIELD(env, Object, this, field, name); } { jstring name = (*env)->NewStringUTF(env, dkc.command); if (!name) return; FIND_FIELD(env, field, class, "commandName", JAVA_SIG_STRING, ); JAVA_SET_FIELD(env, Object, this, field, name); } { jclass stringClass = (*env)->FindClass(env, JAVA_OBJ_STRING); if (!stringClass) return; jsize count = dkc.flags; jobjectArray names = (*env)->NewObjectArray(env, count, stringClass, NULL); if (!names) return; for (unsigned int index=0; indexNewStringUTF(env, dkc.flag[index]); if (!name) return; (*env)->SetObjectArrayElement(env, names, index, name); if ((*env)->ExceptionCheck(env)) return; } { FIND_FIELD(env, field, class, "flagNames", JAVA_SIG_ARRAY(JAVA_SIG_STRING), ); JAVA_SET_FIELD(env, Object, this, field, names); } } } JAVA_STATIC_METHOD( org_a11y_brlapi_DriverKeycode, isPress, jboolean, jlong code ) { return (code & BRLAPI_DRV_KEY_PRESS)? JNI_TRUE: JNI_FALSE; } JAVA_STATIC_METHOD( org_a11y_brlapi_DriverKeycode, getValue, jlong, jlong code ) { return code & BRLAPI_DRV_KEY_VALUE_MASK; } JAVA_STATIC_METHOD( org_a11y_brlapi_DriverKeycode, getGroup, jint, jlong code ) { return BRLAPI_DRV_KEY_GROUP(code); } JAVA_STATIC_METHOD( org_a11y_brlapi_DriverKeycode, getNumber, jint, jlong code ) { return BRLAPI_DRV_KEY_NUMBER(code); } JAVA_STATIC_METHOD( org_a11y_brlapi_DriverKeycode, getNumberAny, jint ) { return BRLAPI_DRV_KEY_NUMBER_ANY; }