ldc-tid-logistique/app/src/main/cpp/native-vnc.cpp

408 lines
13 KiB
C++
Raw Normal View History

2020-08-18 13:40:08 +05:30
/*
* Copyright (c) 2020 Gaurav Ujjwal.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* See COPYING.txt for more details.
*/
#include <jni.h>
#include <GLES2/gl2.h>
2020-08-18 13:40:08 +05:30
#include <rfb/rfbclient.h>
#include "ClientEx.h"
#include "Utility.h"
2020-08-18 13:40:08 +05:30
/******************************************************************************
* Library Initialization
*****************************************************************************/
struct JniContext {
JavaVM *vm; //JVM Instance
jclass managedCls; //Managed `VncClient` class
jmethodID cbFramebufferUpdated; //Cached reference to managed callback
JNIEnv *getEnv() {
JNIEnv *env = nullptr;
if (vm != nullptr && vm->GetEnv((void **) &env, JNI_VERSION_1_6) == JNI_OK)
return env;
return nullptr; //Should not happen
}
};
static JniContext context{};
/**
* Called when our library is loaded.
*/
JNIEXPORT jint
JNI_OnLoad(JavaVM *vm, void *unused) {
context.vm = vm;
if (context.getEnv() == nullptr)
return JNI_ERR;
return JNI_VERSION_1_6;
}
JNIEXPORT void
JNI_OnUnload(JavaVM *vm, void *reserved) {
if (context.managedCls != nullptr)
context.getEnv()->DeleteGlobalRef(context.managedCls);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_gaurav_avnc_vnc_VncClient_initLibrary(JNIEnv *env, jclass clazz) {
context.managedCls = (jclass) env->NewGlobalRef(clazz);
context.cbFramebufferUpdated = env->GetMethodID(clazz, "cbFinishedFrameBufferUpdate", "()V");
//TODO: Cache more method IDs so we don't have to repeatedly search them
rfbClientLog = &log_info;
rfbClientErr = &log_error;
2020-08-18 13:40:08 +05:30
}
2020-08-18 13:40:08 +05:30
/******************************************************************************
* rfbClient Callbacks
*****************************************************************************/
static char *onGetPassword(rfbClient *client) {
auto obj = getManagedClient(client);
auto env = context.getEnv();
auto cls = context.managedCls;
auto mid = env->GetMethodID(cls, "cbGetPassword", "()Ljava/lang/String;");
auto jPassword = (jstring) env->CallObjectMethod(obj, mid);
return getNativeStrCopy(env, jPassword);
}
static rfbCredential *onGetCredential(rfbClient *client, int credentialType) {
if (credentialType != rfbCredentialTypeUser) {
//Only user credentials (i.e. username & password) are currently supported
rfbClientErr("Unsupported credential type requested");
return nullptr;
}
auto obj = getManagedClient(client);
auto env = context.getEnv();
auto cls = context.managedCls;
//Retrieve credentials
jmethodID mid = env->GetMethodID(cls, "cbGetCredential",
"()Lcom/gaurav/avnc/vnc/UserCredential;");
jobject jCredential = env->CallObjectMethod(obj, mid);
if (jCredential == nullptr) {
return nullptr;
}
//Extract username & password
auto jCredentialCls = env->GetObjectClass(jCredential);
auto usernameField = env->GetFieldID(jCredentialCls, "username", "Ljava/lang/String;");
auto jUsername = env->GetObjectField(jCredential, usernameField);
auto passwordField = env->GetFieldID(jCredentialCls, "password", "Ljava/lang/String;");
auto jPassword = env->GetObjectField(jCredential, passwordField);
//Create native rfbCredential
auto credential = (rfbCredential *) malloc(sizeof(rfbCredential));
credential->userCredential.username = getNativeStrCopy(env, (jstring) jUsername);
credential->userCredential.password = getNativeStrCopy(env, (jstring) jPassword);
return credential;
}
static void onBell(rfbClient *client) {
auto obj = getManagedClient(client);
auto env = context.getEnv();
auto cls = context.managedCls;
jmethodID mid = env->GetMethodID(cls, "cbBell", "()V");
env->CallVoidMethod(obj, mid);
}
static void onGotXCutText(rfbClient *client, const char *text, int len) {
auto obj = getManagedClient(client);
auto env = context.getEnv();
auto cls = context.managedCls;
jmethodID mid = env->GetMethodID(cls, "cbGotXCutText", "([B)V");
jbyteArray bytes = env->NewByteArray(len);
env->SetByteArrayRegion(bytes, 0, len, reinterpret_cast<const jbyte *>(text));
env->CallVoidMethod(obj, mid, bytes);
2020-08-18 13:40:08 +05:30
}
static rfbBool onHandleCursorPos(rfbClient *client, int x, int y) {
auto obj = getManagedClient(client);
auto env = context.getEnv();
auto cls = context.managedCls;
jmethodID mid = env->GetMethodID(cls, "cbHandleCursorPos", "(II)Z");
jboolean result = env->CallBooleanMethod(obj, mid, x, y);
return result == JNI_TRUE ? TRUE : FALSE;
}
static void onFinishedFrameBufferUpdate(rfbClient *client) {
auto obj = getManagedClient(client);
auto env = context.getEnv();
env->CallVoidMethod(obj, context.cbFramebufferUpdated);
}
/**
* We need to use our own allocator to know when frame size has changed.
* and to acquire framebuffer lock during modification.
*/
static rfbBool onMallocFrameBuffer(rfbClient *client) {
auto requestedSize = (uint64_t) client->width * client->height * client->format.bitsPerPixel / 8;
if (requestedSize >= SIZE_MAX) {
rfbClientErr("CRITICAL: cannot allocate frameBuffer, requested size is too large\n");
return FALSE;
}
auto allocSize = (size_t) requestedSize;
LOCK(client->fbMutex);
{
if (client->frameBuffer)
free(client->frameBuffer);
client->frameBuffer = static_cast<uint8_t *>(malloc(allocSize));
if (client->frameBuffer) {
client->fbRealWidth = client->width;
client->fbRealHeight = client->height;
memset(client->frameBuffer, 0, allocSize); //Clear any garbage
} else {
client->fbRealWidth = 0;
client->fbRealHeight = 0;
}
}
UNLOCK(client->fbMutex);
if (client->frameBuffer == nullptr) {
rfbClientErr("CRITICAL: frameBuffer allocation failed\n");
return FALSE;
}
auto obj = getManagedClient(client);
auto env = context.getEnv();
auto cls = context.managedCls;
auto mid = env->GetMethodID(cls, "cbFramebufferSizeChanged", "(II)V");
env->CallVoidMethod(obj, mid, client->width, client->height);
return TRUE;
}
2020-08-18 13:40:08 +05:30
/**
* Hooks callbacks to rfbClient.
*/
static void setCallbacks(rfbClient *client) {
client->GetPassword = onGetPassword;
client->GetCredential = onGetCredential;
client->Bell = onBell;
client->GotXCutText = onGotXCutText;
client->HandleCursorPos = onHandleCursorPos;
client->FinishedFrameBufferUpdate = onFinishedFrameBufferUpdate;
client->MallocFrameBuffer = onMallocFrameBuffer;
2020-08-18 13:40:08 +05:30
}
/******************************************************************************
* Native method Implementation
*****************************************************************************/
extern "C"
JNIEXPORT jlong JNICALL
Java_com_gaurav_avnc_vnc_VncClient_nativeClientCreate(JNIEnv *env, jobject thiz) {
rfbClient *client = rfbGetClient(8, 3, 4);
if (client == nullptr)
return 0;
if (!assignClientExtension(client))
return 0;
2020-08-18 13:40:08 +05:30
setCallbacks(client);
client->canHandleNewFBSize = TRUE;
2020-08-18 13:40:08 +05:30
//Attach reference to managed object
auto obj = env->NewGlobalRef(thiz);
setManagedClient(client, obj);
return (jlong) client;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_gaurav_avnc_vnc_VncClient_nativeConfigure(JNIEnv *env, jobject thiz, jlong client_ptr,
jint securityType, jboolean use_local_cursor) {
auto client = (rfbClient *) client_ptr;
// 0 means all auth types
if (securityType != 0) {
uint32_t auth[1] = {static_cast<uint32_t>(securityType)};
SetClientAuthSchemes(client, auth, 1);
}
client->appData.useRemoteCursor = use_local_cursor;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_gaurav_avnc_vnc_VncClient_nativeSetDest(JNIEnv *env, jobject thiz, jlong client_ptr,
jstring host, jint port) {
auto client = (rfbClient *) client_ptr;
client->destHost = getNativeStrCopy(env, host);
client->destPort = port;
}
2020-08-18 13:40:08 +05:30
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_gaurav_avnc_vnc_VncClient_nativeInit(JNIEnv *env, jobject thiz, jlong client_ptr,
jstring host, jint port) {
auto client = (rfbClient *) client_ptr;
client->serverHost = getNativeStrCopy(env, host);
client->serverPort = port < 100 ? port + 5900 : port;
if (rfbInitClient(client, nullptr, nullptr)) {
return JNI_TRUE;
2020-08-18 13:40:08 +05:30
}
return JNI_FALSE;
2020-08-18 13:40:08 +05:30
}
extern "C"
JNIEXPORT void JNICALL
Java_com_gaurav_avnc_vnc_VncClient_nativeCleanup(JNIEnv *env, jobject thiz,
jlong client_ptr) {
auto client = (rfbClient *) client_ptr;
if (client->frameBuffer) {
free(client->frameBuffer);
client->frameBuffer = nullptr;
}
auto managedClient = getManagedClient(client);
env->DeleteGlobalRef(managedClient);
freeClientExtension(client);
2020-08-18 13:40:08 +05:30
rfbClientCleanup(client);
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_gaurav_avnc_vnc_VncClient_nativeProcessServerMessage(JNIEnv *env, jobject thiz,
jlong client_ptr,
jint u_sec_timeout) {
auto client = (rfbClient *) client_ptr;
auto waitResult = WaitForMessage(client, static_cast<unsigned int>(u_sec_timeout));
if (waitResult == 0) // Timeout
return JNI_TRUE;
if (waitResult > 0 && HandleRFBServerMessage(client))
return JNI_TRUE;
2020-08-18 13:40:08 +05:30
return JNI_FALSE;
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_gaurav_avnc_vnc_VncClient_nativeGetLastErrorStr(JNIEnv *env, jobject thiz) {
auto str = errnoToStr(errno);
return env->NewStringUTF(str);
}
2020-08-18 13:40:08 +05:30
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_gaurav_avnc_vnc_VncClient_nativeSendKeyEvent(JNIEnv *env, jobject thiz, jlong client_ptr,
jlong key, jboolean is_down) {
2020-08-18 13:40:08 +05:30
return (jboolean) SendKeyEvent((rfbClient *) client_ptr, (uint32_t) key, is_down);
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_gaurav_avnc_vnc_VncClient_nativeSendPointerEvent(JNIEnv *env, jobject thiz, jlong client_ptr, jint x, jint y,
2020-08-18 13:40:08 +05:30
jint mask) {
return (jboolean) SendPointerEvent((rfbClient *) client_ptr, x, y, mask);
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_gaurav_avnc_vnc_VncClient_nativeSendCutText(JNIEnv *env, jobject thiz, jlong client_ptr, jbyteArray bytes) {
auto textBuffer = env->GetByteArrayElements(bytes, nullptr);
auto textLen = env->GetArrayLength(bytes);
auto textChars = reinterpret_cast<char *>(textBuffer);
rfbBool result = SendClientCutText((rfbClient *) client_ptr, textChars, textLen);
env->ReleaseByteArrayElements(bytes, textBuffer, JNI_ABORT);
2020-08-18 13:40:08 +05:30
return (jboolean) result;
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_gaurav_avnc_vnc_VncClient_nativeRefreshFrameBuffer(JNIEnv *env, jobject thiz, jlong clientPtr) {
auto client = (rfbClient *) clientPtr;
return (jboolean) SendFramebufferUpdateRequest(client, 0, 0, client->width, client->height, FALSE);
2020-08-18 13:40:08 +05:30
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_gaurav_avnc_vnc_VncClient_nativeGetDesktopName(JNIEnv *env, jobject thiz, jlong client_ptr) {
auto client = (rfbClient *) client_ptr;
return env->NewStringUTF(client->desktopName ? client->desktopName : "");
}
2020-08-18 13:40:08 +05:30
extern "C"
JNIEXPORT jint JNICALL
Java_com_gaurav_avnc_vnc_VncClient_nativeGetWidth(JNIEnv *env, jobject thiz, jlong client_ptr) {
return ((rfbClient *) client_ptr)->width;
}
2020-08-18 13:40:08 +05:30
extern "C"
JNIEXPORT jint JNICALL
Java_com_gaurav_avnc_vnc_VncClient_nativeGetHeight(JNIEnv *env, jobject thiz, jlong client_ptr) {
return ((rfbClient *) client_ptr)->height;
}
2020-08-18 13:40:08 +05:30
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_gaurav_avnc_vnc_VncClient_nativeIsEncrypted(JNIEnv *env, jobject thiz, jlong client_ptr) {
2021-01-06 10:38:02 +05:30
return static_cast<jboolean>(((rfbClient *) client_ptr)->tlsSession ? JNI_TRUE : JNI_FALSE);
2020-08-18 13:40:08 +05:30
}
extern "C"
JNIEXPORT void JNICALL
Java_com_gaurav_avnc_vnc_VncClient_nativeUploadFrameTexture(JNIEnv *env, jobject thiz,
jlong client_ptr) {
auto client = (rfbClient *) client_ptr;
LOCK(client->fbMutex);
if (client->frameBuffer) {
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA,
client->fbRealWidth,
client->fbRealHeight,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
client->frameBuffer);
}
UNLOCK(client->fbMutex);
}