blob: ada35db54000c9b9b1c9d2990e03bda72ec54215 [file] [log] [blame] [edit]
/*
* libbrlapi - A library providing access to braille terminals for applications.
*
* Copyright (C) 2006-2023 by
* Samuel Thibault <Samuel.Thibault@ens-lyon.org>
* Sébastien Hinderer <Sebastien.Hinderer@ens-lyon.org>
*
* 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 <dave@mielke.cc>.
*/
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <pthread.h>
#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
};
#ifdef __ANDROID__
JNIEnv *e = env;
result = (*vm)->AttachCurrentThread(vm, &e, &args);
env = e;
#else /* __ANDROID__ */
result = (*vm)->AttachCurrentThread(vm, &env, &args);
#endif /* __ANDROID__ */
if (result == 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) {
size_t size = brlapi_strerror_r(&brlapi_error, NULL, 0);
char msg[size+1];
brlapi_strerror_r(&brlapi_error, msg, sizeof(msg));
fprintf(stderr,
"%s: API=%d Libc=%d GAI=%d: %s\n",
label, brlapi_errno, brlapi_libcerrno, brlapi_gaierrno, msg
);
}
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_SUCCESS:
break;
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;
}
default:
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_SUCCESS:
break;
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;
#ifdef EAI_NODATA
case EAI_NODATA: // obsoleted on RFC 2553bis-02
#endif /* EAI_NODATA */
case EAI_NONAME:
object = BRLAPI_OBJECT("UnknownHostException");
message = host;
break;
}
break;
}
SYSTEM_ERROR:
case BRLAPI_ERROR_LIBCERR: {
switch (brlapi_libcerrno) {
}
break;
}
default:
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; i<n; i++) {
jlongArray jl = (*env)->GetObjectArrayElement(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; i<n; i++) {
jlongArray jl = (*env)->GetObjectArrayElement(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; i<count; i+=1) {
jBooleans[i] = cBooleans[i]? JNI_TRUE: JNI_FALSE;
}
(*env)->SetBooleanArrayRegion(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; i<count; i+=1) {
booleans[i] = values[i] != JNI_FALSE;
}
setParameter(
env, handle,
parameter, subparam, flags,
booleans, count
);
}
}
break;
}
case BRLAPI_PARAM_TYPE_UINT8: {
jsize count = (*env)->GetArrayLength(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;
}
}
size_t size = brlapi_strerror_r(&error, NULL, 0);
char cMessage[size+1];
brlapi_strerror_r(&error, cMessage, sizeof(cMessage));
if (jFunction) (*env)->ReleaseStringUTFChars(env, jFunction, error.errfun);
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; index<count; index+=1) {
jstring name = (*env)->NewStringUTF(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;
}