Fix text input event segfault
The text input control_event was initially designed for mapping SDL_TextInputEvent, limited to 32 characters. For simplicity, the copy/paste feature was implemented using the same control_event: it just sends the text to paste. However, the pasted text might have a length breaking some assumptions: - on the client, the event max-size was smaller than the text max-length, - on the server, the raw buffer storing the events was smaller than the max event size. Fix these inconsistencies, and encode the length on 2 bytes, to accept more than 256 characters. Fixes <https://github.com/Genymobile/scrcpy/issues/10>.
This commit is contained in:
parent
f9562f537a
commit
9396ea6d42
5 changed files with 57 additions and 15 deletions
|
@ -39,9 +39,9 @@ int control_event_serialize(const struct control_event *event, unsigned char *bu
|
||||||
// injecting a text takes time, so limit the text length
|
// injecting a text takes time, so limit the text length
|
||||||
len = TEXT_MAX_LENGTH;
|
len = TEXT_MAX_LENGTH;
|
||||||
}
|
}
|
||||||
buf[1] = (Uint8) len;
|
write16(&buf[1], (Uint16) len);
|
||||||
memcpy(&buf[2], event->text_event.text, len);
|
memcpy(&buf[3], event->text_event.text, len);
|
||||||
return 2 + len;
|
return 3 + len;
|
||||||
}
|
}
|
||||||
case CONTROL_EVENT_TYPE_MOUSE:
|
case CONTROL_EVENT_TYPE_MOUSE:
|
||||||
buf[1] = event->mouse_event.action;
|
buf[1] = event->mouse_event.action;
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#define CONTROL_EVENT_QUEUE_SIZE 64
|
#define CONTROL_EVENT_QUEUE_SIZE 64
|
||||||
#define SERIALIZED_EVENT_MAX_SIZE 33
|
#define TEXT_MAX_LENGTH 300
|
||||||
#define TEXT_MAX_LENGTH 256
|
#define SERIALIZED_EVENT_MAX_SIZE 3 + TEXT_MAX_LENGTH
|
||||||
|
|
||||||
enum control_event_type {
|
enum control_event_type {
|
||||||
CONTROL_EVENT_TYPE_KEYCODE,
|
CONTROL_EVENT_TYPE_KEYCODE,
|
||||||
|
|
|
@ -35,16 +35,36 @@ static void test_serialize_text_event() {
|
||||||
|
|
||||||
unsigned char buf[SERIALIZED_EVENT_MAX_SIZE];
|
unsigned char buf[SERIALIZED_EVENT_MAX_SIZE];
|
||||||
int size = control_event_serialize(&event, buf);
|
int size = control_event_serialize(&event, buf);
|
||||||
assert(size == 15);
|
assert(size == 16);
|
||||||
|
|
||||||
const unsigned char expected[] = {
|
const unsigned char expected[] = {
|
||||||
0x01, // CONTROL_EVENT_TYPE_KEYCODE
|
0x01, // CONTROL_EVENT_TYPE_KEYCODE
|
||||||
0x0d, // text length
|
0x00, 0x0d, // text length
|
||||||
'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', // text
|
'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', // text
|
||||||
};
|
};
|
||||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_serialize_long_text_event() {
|
||||||
|
struct control_event event;
|
||||||
|
event.type = CONTROL_EVENT_TYPE_TEXT;
|
||||||
|
char text[TEXT_MAX_LENGTH];
|
||||||
|
memset(text, 'a', sizeof(text));
|
||||||
|
event.text_event.text = text;
|
||||||
|
|
||||||
|
unsigned char buf[SERIALIZED_EVENT_MAX_SIZE];
|
||||||
|
int size = control_event_serialize(&event, buf);
|
||||||
|
assert(size == 3 + sizeof(text));
|
||||||
|
|
||||||
|
unsigned char expected[3 + TEXT_MAX_LENGTH];
|
||||||
|
expected[0] = 0x01; // CONTROL_EVENT_TYPE_KEYCODE
|
||||||
|
expected[1] = 0x01;
|
||||||
|
expected[2] = 0x2c; // text length (16 bits)
|
||||||
|
memset(&expected[3], 'a', TEXT_MAX_LENGTH);
|
||||||
|
|
||||||
|
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
static void test_serialize_mouse_event() {
|
static void test_serialize_mouse_event() {
|
||||||
struct control_event event = {
|
struct control_event event = {
|
||||||
.type = CONTROL_EVENT_TYPE_MOUSE,
|
.type = CONTROL_EVENT_TYPE_MOUSE,
|
||||||
|
@ -114,6 +134,7 @@ static void test_serialize_scroll_event() {
|
||||||
int main() {
|
int main() {
|
||||||
test_serialize_keycode_event();
|
test_serialize_keycode_event();
|
||||||
test_serialize_text_event();
|
test_serialize_text_event();
|
||||||
|
test_serialize_long_text_event();
|
||||||
test_serialize_mouse_event();
|
test_serialize_mouse_event();
|
||||||
test_serialize_scroll_event();
|
test_serialize_scroll_event();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -13,8 +13,8 @@ public class ControlEventReader {
|
||||||
private static final int SCROLL_PAYLOAD_LENGTH = 16;
|
private static final int SCROLL_PAYLOAD_LENGTH = 16;
|
||||||
private static final int COMMAND_PAYLOAD_LENGTH = 1;
|
private static final int COMMAND_PAYLOAD_LENGTH = 1;
|
||||||
|
|
||||||
private static final int TEXT_MAX_LENGTH = 256;
|
public static final int TEXT_MAX_LENGTH = 300;
|
||||||
private static final int RAW_BUFFER_SIZE = 128;
|
private static final int RAW_BUFFER_SIZE = 1024;
|
||||||
|
|
||||||
private final byte[] rawBuffer = new byte[RAW_BUFFER_SIZE];
|
private final byte[] rawBuffer = new byte[RAW_BUFFER_SIZE];
|
||||||
private final ByteBuffer buffer = ByteBuffer.wrap(rawBuffer);
|
private final ByteBuffer buffer = ByteBuffer.wrap(rawBuffer);
|
||||||
|
@ -94,7 +94,7 @@ public class ControlEventReader {
|
||||||
if (buffer.remaining() < 1) {
|
if (buffer.remaining() < 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
int len = toUnsigned(buffer.get());
|
int len = toUnsigned(buffer.getShort());
|
||||||
if (buffer.remaining() < len) {
|
if (buffer.remaining() < len) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,15 @@ package com.genymobile.scrcpy;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
public class ControlEventReaderTest {
|
public class ControlEventReaderTest {
|
||||||
|
|
||||||
|
@ -43,8 +44,8 @@ public class ControlEventReaderTest {
|
||||||
DataOutputStream dos = new DataOutputStream(bos);
|
DataOutputStream dos = new DataOutputStream(bos);
|
||||||
dos.writeByte(ControlEvent.TYPE_TEXT);
|
dos.writeByte(ControlEvent.TYPE_TEXT);
|
||||||
byte[] text = "testé".getBytes(StandardCharsets.UTF_8);
|
byte[] text = "testé".getBytes(StandardCharsets.UTF_8);
|
||||||
dos.writeByte(text.length);
|
dos.writeShort(text.length);
|
||||||
dos.write("testé".getBytes(StandardCharsets.UTF_8));
|
dos.write(text);
|
||||||
byte[] packet = bos.toByteArray();
|
byte[] packet = bos.toByteArray();
|
||||||
|
|
||||||
reader.readFrom(new ByteArrayInputStream(packet));
|
reader.readFrom(new ByteArrayInputStream(packet));
|
||||||
|
@ -54,6 +55,26 @@ public class ControlEventReaderTest {
|
||||||
Assert.assertEquals("testé", event.getText());
|
Assert.assertEquals("testé", event.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseLongTextEvent() throws IOException {
|
||||||
|
ControlEventReader reader = new ControlEventReader();
|
||||||
|
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
DataOutputStream dos = new DataOutputStream(bos);
|
||||||
|
dos.writeByte(ControlEvent.TYPE_TEXT);
|
||||||
|
byte[] text = new byte[ControlEventReader.TEXT_MAX_LENGTH];
|
||||||
|
Arrays.fill(text, (byte) 'a');
|
||||||
|
dos.writeShort(text.length);
|
||||||
|
dos.write(text);
|
||||||
|
byte[] packet = bos.toByteArray();
|
||||||
|
|
||||||
|
reader.readFrom(new ByteArrayInputStream(packet));
|
||||||
|
ControlEvent event = reader.next();
|
||||||
|
|
||||||
|
Assert.assertEquals(ControlEvent.TYPE_TEXT, event.getType());
|
||||||
|
Assert.assertEquals(new String(text, StandardCharsets.US_ASCII), event.getText());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testParseMouseEvent() throws IOException {
|
public void testParseMouseEvent() throws IOException {
|
||||||
ControlEventReader reader = new ControlEventReader();
|
ControlEventReader reader = new ControlEventReader();
|
||||||
|
|
Loading…
Reference in a new issue