Implemented native client
This commit is contained in:
parent
d99c442ccf
commit
5185e8efad
5 changed files with 646 additions and 0 deletions
31
app/CMakeLists.txt
Normal file
31
app/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(NativeVNC C CXX)
|
||||
|
||||
#LibVNCClient
|
||||
set(NATIVE_LIBVNC_SOURCE ../libvncserver)
|
||||
add_subdirectory(${NATIVE_LIBVNC_SOURCE} libvncserver) # (source dir, build dir)
|
||||
|
||||
|
||||
# Native VNC Helper
|
||||
#
|
||||
# It contains implementation of JNI native methods, some NDK scaffolding and
|
||||
# some helpers for OpenGL ES rendring.
|
||||
#
|
||||
# It also wraps LibVNCClient so that we have to manage only one native library
|
||||
# from Java side.
|
||||
set(NATIVE_VNC_SOURCE src/main/cpp/native-vnc.cpp)
|
||||
|
||||
add_library(native-vnc SHARED ${NATIVE_VNC_SOURCE})
|
||||
|
||||
target_link_libraries(native-vnc vncclient)
|
||||
target_include_directories(native-vnc PRIVATE ../libvncserver)
|
||||
target_include_directories(native-vnc PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/libvncserver)
|
||||
|
||||
# Link NDK libraries
|
||||
find_library(LIB_LOG log)
|
||||
target_link_libraries(native-vnc ${LIB_LOG})
|
||||
|
||||
find_library(LIB_GLES GLESv2)
|
||||
target_link_libraries(native-vnc ${LIB_GLES})
|
||||
|
|
@ -13,6 +13,15 @@ android {
|
|||
targetSdkVersion 29
|
||||
versionCode 1
|
||||
versionName "0.1.0"
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
//We are not using STL
|
||||
arguments '-DANDROID_STL=none'
|
||||
|
||||
targets 'vncclient', 'native-vnc'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
|
@ -36,6 +45,12 @@ android {
|
|||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path file('CMakeLists.txt')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
|
|||
355
app/src/main/cpp/native-vnc.cpp
Normal file
355
app/src/main/cpp/native-vnc.cpp
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Gaurav Ujjwal.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* See COPYING.txt for more details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <jni.h>
|
||||
#include <android/log.h>
|
||||
#include <rfb/rfbclient.h>
|
||||
|
||||
/******************************************************************************
|
||||
* Logging
|
||||
*****************************************************************************/
|
||||
static const char *TAG = "NativeVnc";
|
||||
|
||||
struct Logger {
|
||||
static void i(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
__android_log_vprint(ANDROID_LOG_INFO, TAG, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static void w(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
__android_log_vprint(ANDROID_LOG_WARN, TAG, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static void e(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
__android_log_vprint(ANDROID_LOG_ERROR, TAG, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* 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 = &Logger::i;
|
||||
rfbClientErr = &Logger::e;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Utilities
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Returns a native copy of the given jstring.
|
||||
* Caller is responsible for releasing the memory.
|
||||
*/
|
||||
static char *getNativeStrCopy(JNIEnv *env, jstring jStr) {
|
||||
const char *cStr = env->GetStringUTFChars(jStr, nullptr);
|
||||
char *str = strdup(cStr);
|
||||
env->ReleaseStringUTFChars(jStr, cStr);
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns reference to managed `VncClient` associated with given rfbClient.
|
||||
*/
|
||||
static jobject getManagedClient(rfbClient *client) {
|
||||
return (jobject) rfbClientGetClientData(client, context.managedCls);
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate given rfbClient with a managed `VncClient`.
|
||||
*/
|
||||
static void setManagedClient(rfbClient *client, jobject managedClient) {
|
||||
rfbClientSetClientData(client, context.managedCls, managedClient);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* 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", "(Ljava/lang/String;)V");
|
||||
jstring jText = env->NewStringUTF(text);
|
||||
env->CallVoidMethod(obj, mid, jText);
|
||||
env->DeleteLocalRef(jText);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* 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);
|
||||
setCallbacks(client);
|
||||
|
||||
//Attach reference to managed object
|
||||
auto obj = env->NewGlobalRef(thiz);
|
||||
setManagedClient(client, obj);
|
||||
|
||||
return (jlong) client;
|
||||
}
|
||||
|
||||
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)) {
|
||||
rfbClientErr("rfbInitClient() failed inside nativeInit().");
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
return JNI_TRUE;
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
if (WaitForMessage(client, u_sec_timeout) >= 0) {
|
||||
if (HandleRFBServerMessage(client))
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_gaurav_avnc_vnc_VncClient_nativeSendKeyEvent(JNIEnv *env, jobject thiz, jlong client_ptr,
|
||||
jlong key, jboolean is_down) {
|
||||
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,
|
||||
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,
|
||||
jstring text) {
|
||||
char *cText = getNativeStrCopy(env, text);
|
||||
rfbBool result = SendClientCutText((rfbClient *) client_ptr, cText, strlen(cText));
|
||||
free(cText);
|
||||
return (jboolean) result;
|
||||
}
|
||||
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_gaurav_avnc_vnc_VncClient_nativeSendFrameBufferUpdateRequest(JNIEnv *env, jobject thiz,
|
||||
jlong clientPtr,
|
||||
jint x, jint y, jint w,
|
||||
jint h,
|
||||
jboolean incremental) {
|
||||
return (jboolean) SendFramebufferUpdateRequest((rfbClient *) clientPtr, x, y, w, h,
|
||||
incremental);
|
||||
}
|
||||
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_com_gaurav_avnc_vnc_VncClient_nativeGetConnectionInfo(JNIEnv *env,
|
||||
jobject thiz,
|
||||
jlong clientPtr) {
|
||||
auto client = (rfbClient *) clientPtr;
|
||||
|
||||
jclass infoClass = env->FindClass("com/gaurav/avnc/vnc/VncClient$ConnectionInfo");
|
||||
jmethodID ctorId = env->GetMethodID(infoClass, "<init>", "(Ljava/lang/String;IIZ)V");
|
||||
if (ctorId == nullptr) {
|
||||
Logger::e("Could not find the constructor for 'ConnectionInfo'. "
|
||||
"Constructor signature may be incorrect");
|
||||
}
|
||||
|
||||
// Important: Keep the arguments in sync with 'ConnectionInfo' constructor.
|
||||
jobject infoObject = env->NewObject(infoClass, ctorId,
|
||||
env->NewStringUTF(client->desktopName), // desktopName
|
||||
client->width, // frameWidth
|
||||
client->height, // frameHeight
|
||||
client->tlsSession ? JNI_TRUE : JNI_FALSE); // isEncrypted
|
||||
|
||||
return infoObject;
|
||||
}
|
||||
20
app/src/main/java/com/gaurav/avnc/vnc/UserCredential.kt
Normal file
20
app/src/main/java/com/gaurav/avnc/vnc/UserCredential.kt
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Gaurav Ujjwal.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* See COPYING.txt for more details.
|
||||
*/
|
||||
|
||||
package com.gaurav.avnc.vnc
|
||||
|
||||
/**
|
||||
* This class is used for returning user credentials from callbacks.
|
||||
*/
|
||||
data class UserCredential(
|
||||
@JvmField
|
||||
var username: String = "",
|
||||
|
||||
@JvmField
|
||||
var password: String = ""
|
||||
)
|
||||
225
app/src/main/java/com/gaurav/avnc/vnc/VncClient.kt
Normal file
225
app/src/main/java/com/gaurav/avnc/vnc/VncClient.kt
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
package com.gaurav.avnc.vnc
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
/**
|
||||
* This is a thin wrapper around native RFB client.
|
||||
*
|
||||
*
|
||||
* - +------------+ +----------+
|
||||
* - | Public API | | Listener |
|
||||
* - +------------+ +-----A----+
|
||||
* - | |
|
||||
* - | |
|
||||
* - JNI -------|------------------------------------------------|-----------
|
||||
* - | |
|
||||
* - | |
|
||||
* - +-------v--------+ +--------------+ +--------v---------+
|
||||
* - | Native Methods |------>| LibVNCClient |<----->| Native Callbacks |
|
||||
* - +----------------+ +--------------+ +------------------+
|
||||
*/
|
||||
class VncClient(
|
||||
/**
|
||||
* RFB callback listener.
|
||||
*/
|
||||
private val observer: Observer
|
||||
) {
|
||||
/**
|
||||
* Value of the pointer to native 'rfbClient'.
|
||||
*/
|
||||
val nativePtr: Long
|
||||
|
||||
/**
|
||||
* Holds information about the current connection.
|
||||
*/
|
||||
private var connectionInfo: ConnectionInfo? = null
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* After successful construction, you must call cleanup() on this object
|
||||
* once you are done with it so that allocated resources can be freed.
|
||||
*/
|
||||
init {
|
||||
nativePtr = nativeClientCreate()
|
||||
if (nativePtr == 0L)
|
||||
throw RuntimeException("Could not create native rfbClient!")
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
private external fun initLibrary()
|
||||
|
||||
init {
|
||||
System.loadLibrary("native-vnc")
|
||||
initLibrary()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes VNC connection.
|
||||
* TODO: Add Repeater support
|
||||
*
|
||||
* @param host Server address
|
||||
* @param port Port number
|
||||
* @return true if initialization was successful
|
||||
*/
|
||||
fun init(host: String, port: Int): Boolean {
|
||||
connectionInfo = null
|
||||
return nativeInit(nativePtr, host, port)
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for incoming server message, parses it and then invokes appropriate
|
||||
* callbacks.
|
||||
*
|
||||
* @param uSecTimeout Timeout in microseconds.
|
||||
* @return true if message was successfully handled or no message was received within timeout,
|
||||
* false otherwise.
|
||||
*/
|
||||
fun processServerMessage(uSecTimeout: Int): Boolean {
|
||||
return nativeProcessServerMessage(nativePtr, uSecTimeout)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends Key event to remote server.
|
||||
*
|
||||
* @param key Key code
|
||||
* @param isDown Whether it is an DOWN or UP event
|
||||
* @return true if sent successfully, false otherwise
|
||||
*/
|
||||
fun sendKeyEvent(key: Long, isDown: Boolean): Boolean {
|
||||
return nativeSendKeyEvent(nativePtr, key, isDown)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends pointer event to remote server.
|
||||
*
|
||||
* @param x Horizontal pointer coordinate
|
||||
* @param y Vertical pointer coordinate
|
||||
* @param mask Button mask to identify which button was pressed.
|
||||
* @return true if sent successfully, false otherwise
|
||||
*/
|
||||
fun sendPointerEvent(x: Int, y: Int, mask: Int): Boolean {
|
||||
return nativeSendPointerEvent(nativePtr, x, y, mask)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends text to remote desktop's clipboard.
|
||||
*
|
||||
* @param text Text to send
|
||||
* @return Whether sent successfully.
|
||||
*/
|
||||
fun sendCutText(text: String): Boolean {
|
||||
return nativeSendCutText(nativePtr, text)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request for full frame buffer update to remote server.
|
||||
*
|
||||
* @return Whether sent successfully
|
||||
*/
|
||||
fun refreshFrameBuffer(): Boolean {
|
||||
return nativeSendFrameBufferUpdateRequest(nativePtr,
|
||||
0,
|
||||
0,
|
||||
getConnectionInfo().frameWidth,
|
||||
getConnectionInfo().frameHeight,
|
||||
true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases all resource (native & managed) currently held.
|
||||
* After cleanup, this object should not be used any more.
|
||||
*/
|
||||
fun cleanup() {
|
||||
nativeCleanup(nativePtr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information about current connection.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
fun getConnectionInfo(): ConnectionInfo {
|
||||
return getConnectionInfo(false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information about current connection.
|
||||
*
|
||||
* @param refresh Whether information should be reloaded from native rfbClient.
|
||||
*/
|
||||
fun getConnectionInfo(refresh: Boolean): ConnectionInfo {
|
||||
var info = connectionInfo
|
||||
if (info == null || refresh) {
|
||||
info = nativeGetConnectionInfo(nativePtr)
|
||||
connectionInfo = info
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is used for representing information about the current connection.
|
||||
*
|
||||
* Note: This class is instantiated by the native code. Any change in fields & constructor
|
||||
* arguments should be synchronized with native side.
|
||||
*
|
||||
* TODO: Should we make this a standalone class?
|
||||
* TODO: Add info about encoding etc.
|
||||
*/
|
||||
class ConnectionInfo @Keep constructor(val desktopName: String, val frameWidth: Int, val frameHeight: Int, val isEncrypted: Boolean)
|
||||
|
||||
private external fun nativeClientCreate(): Long
|
||||
private external fun nativeInit(clientPtr: Long, host: String, port: Int): Boolean
|
||||
private external fun nativeProcessServerMessage(clientPtr: Long, uSecTimeout: Int): Boolean
|
||||
private external fun nativeSendKeyEvent(clientPtr: Long, key: Long, isDown: Boolean): Boolean
|
||||
private external fun nativeSendPointerEvent(clientPtr: Long, x: Int, y: Int, mask: Int): Boolean
|
||||
private external fun nativeSendCutText(clientPtr: Long, text: String): Boolean
|
||||
private external fun nativeSendFrameBufferUpdateRequest(clientPtr: Long, x: Int, y: Int, w: Int, h: Int, incremental: Boolean): Boolean
|
||||
private external fun nativeGetConnectionInfo(clientPtr: Long): ConnectionInfo
|
||||
private external fun nativeCleanup(clientPtr: Long)
|
||||
|
||||
@Keep
|
||||
private fun cbGetPassword(): String {
|
||||
return observer.rfbGetPassword()
|
||||
}
|
||||
|
||||
@Keep
|
||||
private fun cbGetCredential(): UserCredential {
|
||||
return observer.rfbGetCredential()
|
||||
}
|
||||
|
||||
@Keep
|
||||
private fun cbBell() {
|
||||
observer.rfbBell()
|
||||
}
|
||||
|
||||
@Keep
|
||||
private fun cbGotXCutText(text: String) {
|
||||
observer.rfbGotXCutText(text)
|
||||
}
|
||||
|
||||
@Keep
|
||||
private fun cbHandleCursorPos(x: Int, y: Int): Boolean {
|
||||
return observer.rfbHandleCursorPos(x, y)
|
||||
}
|
||||
|
||||
@Keep
|
||||
private fun cbFinishedFrameBufferUpdate() {
|
||||
observer.rfbFinishedFrameBufferUpdate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for RFB callback listener.
|
||||
*/
|
||||
interface Observer {
|
||||
fun rfbGetPassword(): String
|
||||
fun rfbGetCredential(): UserCredential
|
||||
fun rfbBell()
|
||||
fun rfbGotXCutText(text: String)
|
||||
fun rfbFinishedFrameBufferUpdate()
|
||||
fun rfbHandleCursorPos(x: Int, y: Int): Boolean
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue