mirror of
https://github.com/vectras-team/termux-x11.git
synced 2024-11-30 10:20:53 +00:00
Refactoring
Moving clipboard-related stuff out of `InitOutput.c` to `clipboard.c` Detecting clipboard-related JNI stuff once on client connecting instead of searching for it every time we receive clipboard. Sending screen configuration event to main thread instead of handling it in input thread (to make it thread-safe). Making `handleLorieEvents` handle as many events as possible instead of relying on epoll capabilities. Removing `maybe_unused` macro in prior to use `__unused` builtin Small fixes.
This commit is contained in:
parent
91f34561ab
commit
103b7ab46f
@ -120,7 +120,6 @@ static DevPrivateKeyRec loriePixmapPrivateKeyRec;
|
||||
static Bool TrueNoop() { return TRUE; }
|
||||
static Bool FalseNoop() { return FALSE; }
|
||||
static void VoidNoop() {}
|
||||
static void lorieInitSelectionCallback();
|
||||
|
||||
void
|
||||
ddxGiveUp(unused enum ExitCode error) {
|
||||
@ -728,7 +727,7 @@ InitOutput(ScreenInfo * screen_info, int argc, char **argv) {
|
||||
|
||||
renderer_init(pvfb->env, &pvfb->root.legacyDrawing, &pvfb->root.flip);
|
||||
xorgGlxCreateVendor();
|
||||
lorieInitSelectionCallback();
|
||||
lorieInitClipboard();
|
||||
|
||||
if (-1 == AddScreen(lorieScreenInit, argc, argv)) {
|
||||
FatalError("Couldn't add screen %d\n", i);
|
||||
@ -802,158 +801,4 @@ __GLXprovider __glXDRISWRastProvider = {
|
||||
glXDRIscreenProbe,
|
||||
"DRISWRAST",
|
||||
NULL
|
||||
};
|
||||
|
||||
/*################################################################################################*/
|
||||
|
||||
static int (*origProcSendEvent)(ClientPtr) = NULL;
|
||||
static Atom xaCLIPBOARD = 0, xaTARGETS = 0, xaSTRING = 0, xaUTF8_STRING = 0;
|
||||
static Bool clipboardEnabled = FALSE;
|
||||
|
||||
void lorieEnableClipboardSync(Bool enable) {
|
||||
clipboardEnabled = enable;
|
||||
}
|
||||
|
||||
static void lorieSelectionRequest(Atom selection, Atom target) {
|
||||
Selection *pSel;
|
||||
|
||||
if (clipboardEnabled && dixLookupSelection(&pSel, selection, serverClient, DixGetAttrAccess) == Success) {
|
||||
xEvent event = {0};
|
||||
event.u.u.type = SelectionRequest;
|
||||
event.u.selectionRequest.owner = pSel->window;
|
||||
event.u.selectionRequest.time = currentTime.milliseconds;
|
||||
event.u.selectionRequest.requestor = pScreenPtr->root->drawable.id;
|
||||
event.u.selectionRequest.selection = selection;
|
||||
event.u.selectionRequest.target = target;
|
||||
event.u.selectionRequest.property = target;
|
||||
WriteEventsToClient(pSel->client, 1, &event);
|
||||
}
|
||||
}
|
||||
|
||||
static Bool lorieHasAtom(Atom atom, const Atom list[], size_t size) {
|
||||
for (size_t i = 0; i < size; i++)
|
||||
if (list[i] == atom)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static inline void lorieConvertLF(const char* src, char *dst, size_t bytes) {
|
||||
size_t i = 0, j = 0;
|
||||
for (; i < bytes; i++)
|
||||
if (src[i] != '\r')
|
||||
dst[j++] = src[i];
|
||||
}
|
||||
|
||||
static inline void lorieLatin1ToUTF8(unsigned char* out, const unsigned char* in) {
|
||||
while (*in)
|
||||
if (*in < 128)
|
||||
*out++ = *in++;
|
||||
else
|
||||
*out++ = 0xc2 + (*in > 0xbf), *out++ = (*in++ & 0x3f) + 0x80;
|
||||
}
|
||||
|
||||
static inline int lorieCheckUTF8(const unsigned char *utf, size_t size) {
|
||||
int ix;
|
||||
unsigned char c;
|
||||
|
||||
for (ix = 0; (c = utf[ix]) && ix < size;) {
|
||||
if (c & 0x80) {
|
||||
if ((utf[ix + 1] & 0xc0) != 0x80)
|
||||
return 0;
|
||||
if ((c & 0xe0) == 0xe0) {
|
||||
if ((utf[ix + 2] & 0xc0) != 0x80)
|
||||
return 0;
|
||||
if ((c & 0xf0) == 0xf0) {
|
||||
if ((c & 0xf8) != 0xf0 || (utf[ix + 3] & 0xc0) != 0x80)
|
||||
return 0;
|
||||
ix += 4;
|
||||
/* 4-byte code */
|
||||
} else
|
||||
/* 3-byte code */
|
||||
ix += 3;
|
||||
} else
|
||||
/* 2-byte code */
|
||||
ix += 2;
|
||||
} else
|
||||
/* 1-byte code */
|
||||
ix++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void lorieHandleSelection(Atom target) {
|
||||
PropertyPtr prop;
|
||||
if (target != xaTARGETS && target != xaSTRING && target != xaUTF8_STRING)
|
||||
return;
|
||||
|
||||
if (dixLookupProperty(&prop, pScreenPtr->root, target, serverClient, DixReadAccess) != Success)
|
||||
return;
|
||||
|
||||
log(DEBUG, "Selection notification for CLIPBOARD (target %s, type %s)\n", NameForAtom(target), NameForAtom(prop->type));
|
||||
|
||||
if (target == xaTARGETS && prop->type == XA_ATOM && prop->format == 32) {
|
||||
if (lorieHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size))
|
||||
lorieSelectionRequest(xaCLIPBOARD, xaUTF8_STRING);
|
||||
else if (lorieHasAtom(xaSTRING, (const Atom*)prop->data, prop->size))
|
||||
lorieSelectionRequest(xaCLIPBOARD, xaSTRING);
|
||||
} else if (target == xaSTRING && prop->type == xaSTRING && prop->format == 8) {
|
||||
if (prop->format != 8 || prop->type != xaSTRING)
|
||||
return;
|
||||
|
||||
char filtered[prop->size + 1], utf8[(prop->size + 1) * 2];
|
||||
memset(filtered, 0, sizeof(filtered));
|
||||
memset(utf8, 0, sizeof(utf8));
|
||||
|
||||
lorieConvertLF(prop->data, filtered, prop->size);
|
||||
lorieLatin1ToUTF8((unsigned char*) utf8, (unsigned char*) filtered);
|
||||
log(DEBUG, "Sending clipboard to clients (%zu bytes)\n", strlen(utf8));
|
||||
lorieSendClipboardData(utf8);
|
||||
} else if (target == xaUTF8_STRING && prop->type == xaUTF8_STRING && prop->format == 8) {
|
||||
char filtered[prop->size + 1];
|
||||
|
||||
if (!lorieCheckUTF8(prop->data, prop->size)) {
|
||||
dprintf(2, "Invalid UTF-8 sequence in clipboard\n");
|
||||
return;
|
||||
}
|
||||
|
||||
memset(filtered, 0, prop->size + 1);
|
||||
lorieConvertLF(prop->data, filtered, prop->size);
|
||||
|
||||
log(DEBUG, "Sending clipboard to clients (%zu bytes)\n", strlen(filtered));
|
||||
lorieSendClipboardData(filtered);
|
||||
}
|
||||
}
|
||||
|
||||
static int lorieProcSendEvent(ClientPtr client)
|
||||
{
|
||||
REQUEST(xSendEventReq)
|
||||
REQUEST_SIZE_MATCH(xSendEventReq);
|
||||
__typeof__(stuff->event.u.selectionNotify)* e = &stuff->event.u.selectionNotify;
|
||||
|
||||
if (clipboardEnabled && e->requestor == pScreenPtr->root->drawable.id &&
|
||||
stuff->event.u.u.type == SelectionNotify && e->selection == xaCLIPBOARD && e->target == e->property)
|
||||
lorieHandleSelection(e->target);
|
||||
|
||||
return origProcSendEvent(client);
|
||||
}
|
||||
|
||||
static void lorieSelectionCallback(maybe_unused CallbackListPtr *callbacks, maybe_unused void * data, void * args) {
|
||||
SelectionInfoRec *info = (SelectionInfoRec *) args;
|
||||
|
||||
if (clipboardEnabled && info->selection->selection == xaCLIPBOARD && info->kind == SelectionSetOwner)
|
||||
lorieSelectionRequest(xaCLIPBOARD, xaTARGETS);
|
||||
}
|
||||
|
||||
static void lorieInitSelectionCallback() {
|
||||
#define ATOM(name) xa##name = MakeAtom(#name, strlen(#name), TRUE)
|
||||
ATOM(CLIPBOARD); ATOM(TARGETS); ATOM(STRING); ATOM(UTF8_STRING);
|
||||
|
||||
if (!origProcSendEvent) {
|
||||
origProcSendEvent = ProcVector[X_SendEvent];
|
||||
ProcVector[X_SendEvent] = lorieProcSendEvent;
|
||||
}
|
||||
|
||||
if (!AddCallback(&SelectionCallback, lorieSelectionCallback, NULL))
|
||||
FatalError("Adding SelectionCallback failed\n");
|
||||
}
|
||||
};
|
@ -12,6 +12,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <libgen.h>
|
||||
#include <globals.h>
|
||||
#include <xkbsrv.h>
|
||||
@ -37,7 +38,14 @@ char *xtrans_unix_path_x11 = NULL;
|
||||
char *xtrans_unix_dir_x11 = NULL;
|
||||
|
||||
typedef enum {
|
||||
EVENT_SCREEN_SIZE, EVENT_TOUCH, EVENT_MOUSE, EVENT_KEY, EVENT_UNICODE, EVENT_CLIPBOARD_SYNC
|
||||
EVENT_SCREEN_SIZE,
|
||||
EVENT_TOUCH,
|
||||
EVENT_MOUSE,
|
||||
EVENT_KEY,
|
||||
EVENT_UNICODE,
|
||||
EVENT_CLIPBOARD_ENABLE,
|
||||
EVENT_CLIPBOARD_ANNOUNCE,
|
||||
EVENT_CLIPBOARD_SEND,
|
||||
} eventType;
|
||||
typedef union {
|
||||
uint8_t type;
|
||||
@ -66,15 +74,57 @@ typedef union {
|
||||
struct {
|
||||
uint8_t t;
|
||||
uint8_t enable;
|
||||
} clipboardSync;
|
||||
} clipboardEnable;
|
||||
struct {
|
||||
uint8_t t;
|
||||
uint32_t count;
|
||||
} clipboardSend;
|
||||
} lorieEvent;
|
||||
|
||||
static struct {
|
||||
jclass self;
|
||||
jmethodID forName;
|
||||
jmethodID decode;
|
||||
} Charset = {0};
|
||||
|
||||
static struct {
|
||||
jclass self;
|
||||
jmethodID toString;
|
||||
} CharBuffer = {0};
|
||||
|
||||
static void* startServer(unused void* cookie) {
|
||||
lorieSetVM((JavaVM*) cookie);
|
||||
char* envp[] = { NULL };
|
||||
exit(dix_main(argc, (char**) argv, envp));
|
||||
}
|
||||
|
||||
static jclass FindClassOrDie(JNIEnv *env, const char* name) {
|
||||
jclass clazz = (*env)->FindClass(env, name);
|
||||
if (!clazz) {
|
||||
char buffer[1024] = {0};
|
||||
sprintf(buffer, "class %s not found", name);
|
||||
log(ERROR, "%s", buffer);
|
||||
(*env)->FatalError(env, buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (*env)->NewGlobalRef(env, clazz);
|
||||
}
|
||||
|
||||
static jclass FindMethodOrDie(JNIEnv *env, jclass clazz, const char* name, const char* signature, jboolean isStatic) {
|
||||
__typeof__((*env)->GetMethodID) getMethodID = isStatic ? (*env)->GetStaticMethodID : (*env)->GetMethodID;
|
||||
jmethodID method = getMethodID(env, clazz, name, signature);
|
||||
if (!method) {
|
||||
char buffer[1024] = {0};
|
||||
sprintf(buffer, "method %s %s not found", name, signature);
|
||||
log(ERROR, "%s", buffer);
|
||||
(*env)->FatalError(env, buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_termux_x11_CmdEntryPoint_start(JNIEnv *env, unused jclass cls, jobjectArray args) {
|
||||
pthread_t t;
|
||||
@ -168,6 +218,7 @@ Java_com_termux_x11_CmdEntryPoint_start(JNIEnv *env, unused jclass cls, jobjectA
|
||||
if (access(current_path, F_OK) == 0)
|
||||
setenv("XKB_CONFIG_ROOT", current_path, 1);
|
||||
}
|
||||
|
||||
if (!getenv("XKB_CONFIG_ROOT")) {
|
||||
// proot case
|
||||
if (access("/usr/share/X11/xkb", F_OK) == 0)
|
||||
@ -202,13 +253,20 @@ Java_com_termux_x11_CmdEntryPoint_windowChanged(JNIEnv *env, unused jobject cls,
|
||||
QueueWorkProc(lorieChangeWindow, NULL, surface ? (*env)->NewGlobalRef(env, surface) : NULL);
|
||||
}
|
||||
|
||||
void handleLorieEvents(int fd, maybe_unused int ready, maybe_unused void *data) {
|
||||
static Bool lorieSendConfigureNotify(unused ClientPtr pClient, void *closure) {
|
||||
lorieEvent* e = closure;
|
||||
__android_log_print(ANDROID_LOG_ERROR, "tx11-request", "window changed: %d %d", e->screenSize.width, e->screenSize.height);
|
||||
lorieConfigureNotify(e->screenSize.width, e->screenSize.height, e->screenSize.framerate);
|
||||
free(e);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void handleLorieEvents(int fd, __unused int ready, __unused void *data) {
|
||||
ValuatorMask mask;
|
||||
lorieEvent e = {0};
|
||||
valuator_mask_zero(&mask);
|
||||
|
||||
if (ready & X_NOTIFY_ERROR) {
|
||||
// RemoveNotifyFd(fd);
|
||||
InputThreadUnregisterDev(fd);
|
||||
close(fd);
|
||||
conn_fd = -1;
|
||||
@ -216,12 +274,15 @@ void handleLorieEvents(int fd, maybe_unused int ready, maybe_unused void *data)
|
||||
return;
|
||||
}
|
||||
|
||||
again:
|
||||
if (read(fd, &e, sizeof(e)) == sizeof(e)) {
|
||||
switch(e.type) {
|
||||
case EVENT_SCREEN_SIZE:
|
||||
__android_log_print(ANDROID_LOG_ERROR, "tx11-request", "window changed: %d %d", e.screenSize.width, e.screenSize.height);
|
||||
lorieConfigureNotify(e.screenSize.width, e.screenSize.height, e.screenSize.framerate);
|
||||
case EVENT_SCREEN_SIZE: {
|
||||
lorieEvent *copy = calloc(1, sizeof(lorieEvent));
|
||||
*copy = e;
|
||||
QueueWorkProc(lorieSendConfigureNotify, NULL, copy);
|
||||
break;
|
||||
}
|
||||
case EVENT_TOUCH: {
|
||||
double x, y;
|
||||
DDXTouchPointInfoPtr touch = TouchFindByDDXID(lorieTouch, e.touch.id, FALSE);
|
||||
@ -297,20 +358,27 @@ void handleLorieEvents(int fd, maybe_unused int ready, maybe_unused void *data)
|
||||
lorieKeysymKeyboardEvent(ks, FALSE);
|
||||
break;
|
||||
}
|
||||
case EVENT_CLIPBOARD_SYNC:
|
||||
lorieEnableClipboardSync(e.clipboardSync.enable);
|
||||
case EVENT_CLIPBOARD_ENABLE:
|
||||
lorieEnableClipboardSync(e.clipboardEnable.enable);
|
||||
break;
|
||||
}
|
||||
|
||||
int n;
|
||||
if (ioctl(fd, FIONREAD, &n) >= 0 && n > sizeof(e))
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
void lorieSendClipboardData(const char* data) {
|
||||
if (data && conn_fd != -1)
|
||||
write(conn_fd, data, strlen(data));
|
||||
if (data && conn_fd != -1) {
|
||||
size_t len = strlen(data);
|
||||
lorieEvent e = { .clipboardSend = { .t = EVENT_CLIPBOARD_SEND, .count = len } };
|
||||
write(conn_fd, &e, sizeof(e));
|
||||
write(conn_fd, data, len);
|
||||
}
|
||||
}
|
||||
|
||||
static Bool addFd(unused ClientPtr pClient, void *closure) {
|
||||
// SetNotifyFd((int) (int64_t) closure, handleLorieEvents, X_NOTIFY_READ, NULL);
|
||||
InputThreadRegisterDev((int) (int64_t) closure, handleLorieEvents, NULL);
|
||||
conn_fd = (int) (int64_t) closure;
|
||||
return TRUE;
|
||||
@ -378,56 +446,51 @@ static inline void checkConnection(JNIEnv* env) {
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_termux_x11_LorieView_connect(unused JNIEnv* env, unused jobject cls, jint fd) {
|
||||
if (!Charset.self) {
|
||||
// Init clipboard-related JNI stuff
|
||||
Charset.self = FindClassOrDie(env, "java/nio/charset/Charset");
|
||||
Charset.forName = FindMethodOrDie(env, Charset.self, "forName", "(Ljava/lang/String;)Ljava/nio/charset/Charset;", JNI_TRUE);
|
||||
Charset.decode = FindMethodOrDie(env, Charset.self, "decode", "(Ljava/nio/ByteBuffer;)Ljava/nio/CharBuffer;", JNI_FALSE);
|
||||
|
||||
CharBuffer.self = FindClassOrDie(env, "java/nio/CharBuffer");
|
||||
CharBuffer.toString = FindMethodOrDie(env, CharBuffer.self, "toString", "()Ljava/lang/String;", JNI_FALSE);
|
||||
}
|
||||
|
||||
conn_fd = fd;
|
||||
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
|
||||
checkConnection(env);
|
||||
log(DEBUG, "XCB connection is successfull");
|
||||
}
|
||||
|
||||
static char clipboard[1024*1024] = {0};
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_termux_x11_LorieView_handleXEvents(JNIEnv *env, maybe_unused jobject thiz) {
|
||||
Java_com_termux_x11_LorieView_handleXEvents(JNIEnv *env, __unused jobject thiz) {
|
||||
checkConnection(env);
|
||||
if (conn_fd != -1) {
|
||||
char none;
|
||||
memset(clipboard, 0, sizeof(clipboard));
|
||||
if (read(conn_fd, clipboard, sizeof(clipboard)) > 0) {
|
||||
clipboard[sizeof(clipboard) - 1] = 0;
|
||||
log(DEBUG, "Clipboard content (%zu symbols) is %s", strlen(clipboard), clipboard);
|
||||
jmethodID id = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, thiz), "setClipboardText","(Ljava/lang/String;)V");
|
||||
lorieEvent e = {0};
|
||||
|
||||
jclass cls_Charset = (*env)->FindClass(env, "java/nio/charset/Charset");
|
||||
jclass cls_CharBuffer = (*env)->FindClass(env, "java/nio/CharBuffer");
|
||||
jmethodID mid_Charset_forName = cls_Charset ? (*env)->GetStaticMethodID(env, cls_Charset, "forName", "(Ljava/lang/String;)Ljava/nio/charset/Charset;") : NULL;
|
||||
jmethodID mid_Charset_decode = cls_Charset ? (*env)->GetMethodID(env, cls_Charset, "decode", "(Ljava/nio/ByteBuffer;)Ljava/nio/CharBuffer;") : NULL;
|
||||
jmethodID mid_CharBuffer_toString = cls_CharBuffer ? (*env)->GetMethodID(env, cls_CharBuffer, "toString", "()Ljava/lang/String;") : NULL;
|
||||
again:
|
||||
if (read(conn_fd, &e, sizeof(e)) == sizeof(e)) {
|
||||
switch(e.type) {
|
||||
case EVENT_CLIPBOARD_SEND: {
|
||||
char clipboard[e.clipboardSend.count + 1];
|
||||
read(conn_fd, clipboard, sizeof(clipboard));
|
||||
clipboard[e.clipboardSend.count] = 0;
|
||||
log(DEBUG, "Clipboard content (%zu symbols) is %s", strlen(clipboard), clipboard);
|
||||
jmethodID id = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, thiz), "setClipboardText","(Ljava/lang/String;)V");
|
||||
jobject bb = (*env)->NewDirectByteBuffer(env, clipboard, strlen(clipboard));
|
||||
jobject charset = (*env)->CallStaticObjectMethod(env, Charset.self, Charset.forName, (*env)->NewStringUTF(env, "UTF-8"));
|
||||
jobject cb = (*env)->CallObjectMethod(env, charset, Charset.decode, bb);
|
||||
(*env)->DeleteLocalRef(env, bb);
|
||||
|
||||
if (!id)
|
||||
log(ERROR, "setClipboardText method not found");
|
||||
if (!cls_Charset)
|
||||
log(ERROR, "java.nio.charset.Charset class not found");
|
||||
if (!cls_CharBuffer)
|
||||
log(ERROR, "java.nio.CharBuffer class not found");
|
||||
if (!mid_Charset_forName)
|
||||
log(ERROR, "java.nio.charset.Charset.forName method not found");
|
||||
if (!mid_Charset_decode)
|
||||
log(ERROR, "java.nio.charset.Charset.decode method not found");
|
||||
if (!mid_CharBuffer_toString)
|
||||
log(ERROR, "java.nio.CharBuffer.toString method not found");
|
||||
|
||||
if (id && cls_Charset && cls_CharBuffer && mid_Charset_forName && mid_Charset_decode && mid_CharBuffer_toString) {
|
||||
jobject bb = (*env)->NewDirectByteBuffer(env, clipboard, strlen(clipboard));
|
||||
jobject charset = (*env)->CallStaticObjectMethod(env, cls_Charset, mid_Charset_forName, (*env)->NewStringUTF(env, "UTF-8"));
|
||||
jobject cb = (*env)->CallObjectMethod(env, charset, mid_Charset_decode, bb);
|
||||
(*env)->DeleteLocalRef(env, bb);
|
||||
|
||||
jstring str = (*env)->CallObjectMethod(env, cb, mid_CharBuffer_toString);
|
||||
(*env)->CallVoidMethod(env, thiz, id, str);
|
||||
jstring str = (*env)->CallObjectMethod(env, cb, CharBuffer.toString);
|
||||
(*env)->CallVoidMethod(env, thiz, id, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while(read(conn_fd, &none, sizeof(none)) > 0);
|
||||
int n;
|
||||
if (ioctl(conn_fd, FIONREAD, &n) >= 0 && n > sizeof(e))
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
@ -454,7 +517,7 @@ Java_com_termux_x11_LorieView_startLogcat(JNIEnv *env, unused jobject cls, jint
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_termux_x11_LorieView_setClipboardSyncEnabled(unused JNIEnv* env, unused jobject cls, jboolean enable) {
|
||||
if (conn_fd != -1) {
|
||||
lorieEvent e = { .clipboardSync = { .t = EVENT_CLIPBOARD_SYNC, .enable = enable } };
|
||||
lorieEvent e = { .clipboardEnable = { .t = EVENT_CLIPBOARD_ENABLE, .enable = enable } };
|
||||
write(conn_fd, &e, sizeof(e));
|
||||
checkConnection(env);
|
||||
}
|
||||
@ -504,7 +567,7 @@ JNIEXPORT void JNICALL
|
||||
Java_com_termux_x11_LorieView_sendTextEvent(JNIEnv *env, unused jobject thiz, jbyteArray text) {
|
||||
if (conn_fd != -1 && text) {
|
||||
jsize length = (*env)->GetArrayLength(env, text);
|
||||
jbyte *str = (*env)->GetByteArrayElements(env, text, JNI_FALSE);
|
||||
jbyte *str = (*env)->GetByteArrayElements(env, text, NULL);
|
||||
char *p = (char*) str;
|
||||
mbstate_t state = { 0 };
|
||||
log(DEBUG, "Parsing text: %.*s", length, str);
|
||||
|
308
app/src/main/cpp/lorie/clipboard.c
Normal file
308
app/src/main/cpp/lorie/clipboard.c
Normal file
@ -0,0 +1,308 @@
|
||||
/* Copyright 2016-2019 Pierre Ossman for Cendio AB
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#pragma clang diagnostic ignored "-Wunknown-pragmas"
|
||||
|
||||
#ifdef HAVE_DIX_CONFIG_H
|
||||
#include <dix-config.h>
|
||||
#endif
|
||||
|
||||
#include <android/log.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <windowstr.h>
|
||||
#include <selection.h>
|
||||
#include <propertyst.h>
|
||||
|
||||
#include "lorie.h"
|
||||
|
||||
/* utility functions for text conversion */
|
||||
|
||||
static inline void lorieConvertLF(const char* src, char *dst, size_t bytes) {
|
||||
size_t i = 0, j = 0;
|
||||
for (; i < bytes; i++)
|
||||
if (src[i] != '\r')
|
||||
dst[j++] = src[i];
|
||||
}
|
||||
|
||||
static inline void lorieLatin1ToUTF8(unsigned char* out, const unsigned char* in) {
|
||||
while (*in)
|
||||
if (*in < 128)
|
||||
*out++ = *in++;
|
||||
else
|
||||
*out++ = 0xc2 + (*in > 0xbf), *out++ = (*in++ & 0x3f) + 0x80;
|
||||
}
|
||||
|
||||
static inline int lorieCheckUTF8(const unsigned char *utf, size_t size) {
|
||||
int ix;
|
||||
unsigned char c;
|
||||
|
||||
for (ix = 0; (c = utf[ix]) && ix < size;) {
|
||||
if (c & 0x80) {
|
||||
if ((utf[ix + 1] & 0xc0) != 0x80)
|
||||
return 0;
|
||||
if ((c & 0xe0) == 0xe0) {
|
||||
if ((utf[ix + 2] & 0xc0) != 0x80)
|
||||
return 0;
|
||||
if ((c & 0xf0) == 0xf0) {
|
||||
if ((c & 0xf8) != 0xf0 || (utf[ix + 3] & 0xc0) != 0x80)
|
||||
return 0;
|
||||
ix += 4;
|
||||
/* 4-byte code */
|
||||
} else
|
||||
/* 3-byte code */
|
||||
ix += 3;
|
||||
} else
|
||||
/* 2-byte code */
|
||||
ix += 2;
|
||||
} else
|
||||
/* 1-byte code */
|
||||
ix++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static size_t lorieUtf8ToUCS4(const char* src, size_t max, unsigned* dst) {
|
||||
size_t count, consumed;
|
||||
|
||||
*dst = 0xfffd;
|
||||
|
||||
if (max == 0)
|
||||
return 0;
|
||||
|
||||
consumed = 1;
|
||||
|
||||
if ((*src & 0x80) == 0) {
|
||||
*dst = *src;
|
||||
count = 0;
|
||||
} else if ((*src & 0xe0) == 0xc0) {
|
||||
*dst = *src & 0x1f;
|
||||
count = 1;
|
||||
} else if ((*src & 0xf0) == 0xe0) {
|
||||
*dst = *src & 0x0f;
|
||||
count = 2;
|
||||
} else if ((*src & 0xf8) == 0xf0) {
|
||||
*dst = *src & 0x07;
|
||||
count = 3;
|
||||
} else {
|
||||
// Invalid sequence, consume all continuation characters
|
||||
src++;
|
||||
max--;
|
||||
while ((max-- > 0) && ((*src++ & 0xc0) == 0x80))
|
||||
consumed++;
|
||||
return consumed;
|
||||
}
|
||||
|
||||
src++;
|
||||
max--;
|
||||
|
||||
while (count--) {
|
||||
consumed++;
|
||||
|
||||
// Invalid or truncated sequence?
|
||||
if ((max == 0) || ((*src & 0xc0) != 0x80)) {
|
||||
*dst = 0xfffd;
|
||||
return consumed;
|
||||
}
|
||||
|
||||
*dst <<= 6;
|
||||
*dst |= *src & 0x3f;
|
||||
|
||||
src++;
|
||||
max--;
|
||||
}
|
||||
|
||||
// UTF-16 surrogate code point?
|
||||
if ((*dst >= 0xd800) && (*dst < 0xe000))
|
||||
*dst = 0xfffd;
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
static const char *lorieUtf8ToLatin1(const char *src) {
|
||||
size_t sz;
|
||||
|
||||
const char* in;
|
||||
size_t in_len;
|
||||
|
||||
// Compute output size
|
||||
sz = 0;
|
||||
in = src;
|
||||
in_len = -1;
|
||||
while ((in_len > 0) && (*in != '\0')) {
|
||||
size_t len;
|
||||
unsigned ucs;
|
||||
|
||||
len = lorieUtf8ToUCS4(in, in_len, &ucs);
|
||||
in += len;
|
||||
in_len -= len;
|
||||
sz++;
|
||||
}
|
||||
|
||||
// Reserve space
|
||||
unsigned char out[sz + 1];
|
||||
memset(out, 0, sz + 1);
|
||||
size_t position = 0;
|
||||
|
||||
// And convert
|
||||
in = src;
|
||||
in_len = 4.294967295E9;
|
||||
while ((in_len > 0) && (*in != '\0')) {
|
||||
size_t len;
|
||||
unsigned ucs;
|
||||
|
||||
len = lorieUtf8ToUCS4(in, in_len, &ucs);
|
||||
in += len;
|
||||
in_len -= len;
|
||||
|
||||
if (ucs > 0xff)
|
||||
out[position++] = '?';
|
||||
else
|
||||
out[position++] = (unsigned char)ucs;
|
||||
}
|
||||
|
||||
return strdup((const char*) out);
|
||||
}
|
||||
|
||||
/* end utility functions */
|
||||
|
||||
#define log(prio, ...) __android_log_print(ANDROID_LOG_ ## prio, "LorieNative", __VA_ARGS__)
|
||||
extern ScreenPtr pScreenPtr;
|
||||
|
||||
static int (*origProcSendEvent)(ClientPtr) = NULL;
|
||||
static int (*origProcConvertSelection)(ClientPtr) = NULL;
|
||||
static Atom xaTIMESTAMP = 0, xaTEXT = 0, xaCLIPBOARD = 0, xaTARGETS = 0, xaSTRING = 0, xaUTF8_STRING = 0;
|
||||
static Bool clipboardEnabled = FALSE;
|
||||
static const char* cachedData = NULL;
|
||||
|
||||
struct LorieDataTarget {
|
||||
ClientPtr client;
|
||||
Atom selection;
|
||||
Atom target;
|
||||
Atom property;
|
||||
Window requestor;
|
||||
CARD32 time;
|
||||
struct LorieDataTarget* next;
|
||||
} *lorieDataTargetHead;
|
||||
|
||||
void lorieEnableClipboardSync(Bool enable) {
|
||||
clipboardEnabled = enable;
|
||||
}
|
||||
|
||||
static void lorieSelectionRequest(Atom selection, Atom target) {
|
||||
Selection *pSel;
|
||||
|
||||
if (clipboardEnabled && dixLookupSelection(&pSel, selection, serverClient, DixGetAttrAccess) == Success) {
|
||||
xEvent event = {0};
|
||||
event.u.u.type = SelectionRequest;
|
||||
event.u.selectionRequest.owner = pSel->window;
|
||||
event.u.selectionRequest.time = currentTime.milliseconds;
|
||||
event.u.selectionRequest.requestor = pScreenPtr->root->drawable.id;
|
||||
event.u.selectionRequest.selection = selection;
|
||||
event.u.selectionRequest.target = target;
|
||||
event.u.selectionRequest.property = target;
|
||||
WriteEventsToClient(pSel->client, 1, &event);
|
||||
}
|
||||
}
|
||||
|
||||
static Bool lorieHasAtom(Atom atom, const Atom list[], size_t size) {
|
||||
for (size_t i = 0; i < size; i++)
|
||||
if (list[i] == atom)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void lorieHandleSelection(Atom target) {
|
||||
PropertyPtr prop;
|
||||
if (target != xaTARGETS && target != xaSTRING && target != xaUTF8_STRING)
|
||||
return;
|
||||
|
||||
if (dixLookupProperty(&prop, pScreenPtr->root, target, serverClient, DixReadAccess) != Success)
|
||||
return;
|
||||
|
||||
log(DEBUG, "Selection notification for CLIPBOARD (target %s, type %s)\n", NameForAtom(target), NameForAtom(prop->type));
|
||||
|
||||
if (target == xaTARGETS && prop->type == XA_ATOM && prop->format == 32) {
|
||||
if (lorieHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size))
|
||||
lorieSelectionRequest(xaCLIPBOARD, xaUTF8_STRING);
|
||||
else if (lorieHasAtom(xaSTRING, (const Atom*)prop->data, prop->size))
|
||||
lorieSelectionRequest(xaCLIPBOARD, xaSTRING);
|
||||
} else if (target == xaSTRING && prop->type == xaSTRING && prop->format == 8) {
|
||||
if (prop->format != 8 || prop->type != xaSTRING)
|
||||
return;
|
||||
|
||||
char filtered[prop->size + 1], utf8[(prop->size + 1) * 2];
|
||||
memset(filtered, 0, sizeof(filtered));
|
||||
memset(utf8, 0, sizeof(utf8));
|
||||
|
||||
lorieConvertLF(prop->data, filtered, prop->size);
|
||||
lorieLatin1ToUTF8((unsigned char*) utf8, (unsigned char*) filtered);
|
||||
log(DEBUG, "Sending clipboard to clients (%zu bytes)\n", strlen(utf8));
|
||||
lorieSendClipboardData(utf8);
|
||||
} else if (target == xaUTF8_STRING && prop->type == xaUTF8_STRING && prop->format == 8) {
|
||||
char filtered[prop->size + 1];
|
||||
|
||||
if (!lorieCheckUTF8(prop->data, prop->size)) {
|
||||
dprintf(2, "Invalid UTF-8 sequence in clipboard\n");
|
||||
return;
|
||||
}
|
||||
|
||||
memset(filtered, 0, prop->size + 1);
|
||||
lorieConvertLF(prop->data, filtered, prop->size);
|
||||
|
||||
log(DEBUG, "Sending clipboard to clients (%zu bytes)\n", strlen(filtered));
|
||||
lorieSendClipboardData(filtered);
|
||||
}
|
||||
}
|
||||
|
||||
static int lorieProcSendEvent(ClientPtr client) {
|
||||
REQUEST(xSendEventReq)
|
||||
REQUEST_SIZE_MATCH(xSendEventReq);
|
||||
__typeof__(stuff->event.u.selectionNotify)* e = &stuff->event.u.selectionNotify;
|
||||
|
||||
if (clipboardEnabled && e->requestor == pScreenPtr->root->drawable.id &&
|
||||
stuff->event.u.u.type == SelectionNotify && e->selection == xaCLIPBOARD && e->target == e->property)
|
||||
lorieHandleSelection(e->target);
|
||||
|
||||
return origProcSendEvent(client);
|
||||
}
|
||||
|
||||
static void lorieSelectionCallback(__unused CallbackListPtr *callbacks, __unused void * data, void * args) {
|
||||
SelectionInfoRec *info = (SelectionInfoRec *) args;
|
||||
|
||||
if (clipboardEnabled && info->selection->selection == xaCLIPBOARD && info->kind == SelectionSetOwner)
|
||||
lorieSelectionRequest(xaCLIPBOARD, xaTARGETS);
|
||||
}
|
||||
|
||||
void lorieInitClipboard(void) {
|
||||
#define ATOM(name) xa##name = MakeAtom(#name, strlen(#name), TRUE)
|
||||
ATOM(TIMESTAMP); ATOM(TEXT); ATOM(CLIPBOARD); ATOM(TARGETS); ATOM(STRING); ATOM(UTF8_STRING);
|
||||
|
||||
if (!origProcConvertSelection) {
|
||||
origProcConvertSelection = ProcVector[X_ConvertSelection];
|
||||
// ProcVector[X_ConvertSelection] = lorieProcConvertSelection;
|
||||
}
|
||||
|
||||
if (!origProcSendEvent) {
|
||||
origProcSendEvent = ProcVector[X_SendEvent];
|
||||
ProcVector[X_SendEvent] = lorieProcSendEvent;
|
||||
}
|
||||
|
||||
if (!AddCallback(&SelectionCallback, lorieSelectionCallback, NULL))
|
||||
FatalError("Adding SelectionCallback failed\n");
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <X11/keysymdef.h>
|
||||
#include <jni.h>
|
||||
#include "linux/input-event-codes.h"
|
||||
#define unused __attribute__((unused))
|
||||
|
||||
@ -9,6 +10,7 @@ Bool lorieChangeWindow(ClientPtr pClient, void *closure);
|
||||
void lorieConfigureNotify(int width, int height, int framerate);
|
||||
void lorieEnableClipboardSync(Bool enable);
|
||||
void lorieSendClipboardData(const char* data);
|
||||
void lorieInitClipboard(void);
|
||||
|
||||
static int android_to_linux_keycode[304] = {
|
||||
[ 4 /* ANDROID_KEYCODE_BACK */] = KEY_ESC,
|
||||
|
@ -696,7 +696,7 @@ static void draw(GLuint id, float x0, float y0, float x1, float y1, uint8_t flip
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); checkGlError();
|
||||
}
|
||||
|
||||
maybe_unused static void draw_cursor(void) {
|
||||
__unused static void draw_cursor(void) {
|
||||
float x, y, w, h;
|
||||
|
||||
if (!cursor.width || !cursor.height)
|
||||
|
@ -2,26 +2,22 @@
|
||||
#include <jni.h>
|
||||
#include <android/hardware_buffer.h>
|
||||
|
||||
#ifndef maybe_unused
|
||||
#define maybe_unused __attribute__((__unused__))
|
||||
#endif
|
||||
|
||||
// X server is already linked to mesa so linking to Android's GLESv2 will confuse the linker.
|
||||
// That is a reason why we should compile renderer as separate hared library with its own dependencies.
|
||||
// In that case part of X server's api is unavailable,
|
||||
// so we should pass addresses to all needed functions to the renderer lib.
|
||||
typedef void (*renderer_message_func_type) (int type, int verb, const char *format, ...);
|
||||
maybe_unused void renderer_message_func(renderer_message_func_type function);
|
||||
__unused void renderer_message_func(renderer_message_func_type function);
|
||||
|
||||
maybe_unused int renderer_init(JNIEnv* env, int* legacy_drawing, uint8_t* flip);
|
||||
maybe_unused void renderer_set_buffer(JNIEnv* env, AHardwareBuffer* buffer);
|
||||
maybe_unused void renderer_set_window(JNIEnv* env, jobject surface, AHardwareBuffer* buffer);
|
||||
maybe_unused int renderer_should_redraw(void);
|
||||
maybe_unused int renderer_redraw(JNIEnv* env, uint8_t flip);
|
||||
maybe_unused void renderer_print_fps(float millis);
|
||||
__unused int renderer_init(JNIEnv* env, int* legacy_drawing, uint8_t* flip);
|
||||
__unused void renderer_set_buffer(JNIEnv* env, AHardwareBuffer* buffer);
|
||||
__unused void renderer_set_window(JNIEnv* env, jobject surface, AHardwareBuffer* buffer);
|
||||
__unused int renderer_should_redraw(void);
|
||||
__unused int renderer_redraw(JNIEnv* env, uint8_t flip);
|
||||
__unused void renderer_print_fps(float millis);
|
||||
|
||||
maybe_unused void renderer_update_root(int w, int h, void* data, uint8_t flip);
|
||||
maybe_unused void renderer_update_cursor(int w, int h, int xhot, int yhot, void* data);
|
||||
maybe_unused void renderer_set_cursor_coordinates(int x, int y);
|
||||
__unused void renderer_update_root(int w, int h, void* data, uint8_t flip);
|
||||
__unused void renderer_update_cursor(int w, int h, int xhot, int yhot, void* data);
|
||||
__unused void renderer_set_cursor_coordinates(int x, int y);
|
||||
|
||||
#define AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM 5 // Stands to HAL_PIXEL_FORMAT_BGRA_8888
|
@ -266,6 +266,7 @@ add_library(Xlorie SHARED
|
||||
"libxcvt/lib/libxcvt.c"
|
||||
"lorie/shm/shmem.c"
|
||||
"lorie/android.c"
|
||||
"lorie/clipboard.c"
|
||||
"lorie/InitOutput.c"
|
||||
"lorie/InitInput.c"
|
||||
"lorie/InputXKB.c"
|
||||
|
@ -8,7 +8,6 @@ import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
@ -19,7 +18,6 @@ import android.view.KeyEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
@ -30,7 +28,7 @@ import java.util.regex.PatternSyntaxException;
|
||||
@SuppressLint("WrongConstant")
|
||||
@SuppressWarnings("deprecation")
|
||||
public class LorieView extends SurfaceView implements InputStub {
|
||||
interface Callback {
|
||||
public interface Callback {
|
||||
void changed(Surface sfc, int surfaceWidth, int surfaceHeight, int screenWidth, int screenHeight);
|
||||
}
|
||||
|
||||
@ -38,6 +36,9 @@ public class LorieView extends SurfaceView implements InputStub {
|
||||
int BGRA_8888 = 5; // Stands for HAL_PIXEL_FORMAT_BGRA_8888
|
||||
}
|
||||
|
||||
private ClipboardManager clipboard;
|
||||
private long lastClipboardTimestamp = System.currentTimeMillis();
|
||||
private static boolean clipboardSyncEnabled = false;
|
||||
private Callback mCallback;
|
||||
private final Point p = new Point();
|
||||
private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
|
||||
@ -71,6 +72,7 @@ public class LorieView extends SurfaceView implements InputStub {
|
||||
|
||||
private void init() {
|
||||
getHolder().addCallback(mSurfaceCallback);
|
||||
clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
}
|
||||
|
||||
public void setCallback(Callback callback) {
|
||||
@ -205,10 +207,19 @@ public class LorieView extends SurfaceView implements InputStub {
|
||||
|
||||
// It is used in native code
|
||||
void setClipboardText(String text) {
|
||||
ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
clipboard.setPrimaryClip(ClipData.newPlainText("X11 clipboard", text));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
if (hasFocus)
|
||||
regenerate();
|
||||
|
||||
requestFocus();
|
||||
|
||||
}
|
||||
|
||||
static native void connect(int fd);
|
||||
native void handleXEvents();
|
||||
static native void startLogcat(int fd);
|
||||
|
@ -502,7 +502,7 @@ public class MainActivity extends AppCompatActivity implements View.OnApplyWindo
|
||||
|
||||
setTerminalToolbarView();
|
||||
onWindowFocusChanged(true);
|
||||
LorieView.setClipboardSyncEnabled(p.getBoolean("clipboardSync", false));
|
||||
LorieView.setClipboardSyncEnabled(p.getBoolean("clipboardEnable", false));
|
||||
|
||||
lorieView.triggerCallback();
|
||||
|
||||
@ -760,11 +760,6 @@ public class MainActivity extends AppCompatActivity implements View.OnApplyWindo
|
||||
|
||||
((FrameLayout) findViewById(android.R.id.content)).getChildAt(0).setFitsSystemWindows(!fullscreen);
|
||||
SamsungDexUtils.dexMetaKeyCapture(this, hasFocus && p.getBoolean("dexMetaKeyCapture", false));
|
||||
|
||||
if (hasFocus)
|
||||
getLorieView().regenerate();
|
||||
|
||||
getLorieView().requestFocus();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -594,11 +594,12 @@ public class TouchInputHandler {
|
||||
|
||||
checkButtons(e);
|
||||
return true;
|
||||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
case MotionEvent.ACTION_HOVER_MOVE: {
|
||||
float scaledX = e.getX() * mRenderData.scale.x, scaledY = e.getY() * mRenderData.scale.y;
|
||||
if (mRenderData.setCursorPosition(scaledX, scaledY))
|
||||
mInjector.sendCursorMove(scaledX, scaledY, false);
|
||||
return true;
|
||||
}
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
checkButtons(e);
|
||||
if (hasFlags(e, 0x14000000)) {
|
||||
@ -625,7 +626,7 @@ public class TouchInputHandler {
|
||||
if (mIsScrolling && hasFlags(e, 0x14000000))
|
||||
mScroller.onTouchEvent(e);
|
||||
else if (mIsDragging && hasFlags(e, 0x4000000)) {
|
||||
scaledX = e.getX() * mRenderData.scale.x; scaledY = e.getY() * mRenderData.scale.y;
|
||||
float scaledX = e.getX() * mRenderData.scale.x, scaledY = e.getY() * mRenderData.scale.y;
|
||||
if (mRenderData.setCursorPosition(scaledX, scaledY))
|
||||
mInjector.sendCursorMove(scaledX, scaledY, false);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user