Add "inject touch" control message
Add a control message type in the protocol to forward touch events to the device.
This commit is contained in:
parent
d90549d1e6
commit
77f876e29c
6 changed files with 140 additions and 7 deletions
|
@ -1,6 +1,7 @@
|
||||||
#include "control_msg.h"
|
#include "control_msg.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <SDL2/SDL_assert.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "buffer_util.h"
|
#include "buffer_util.h"
|
||||||
|
@ -24,6 +25,16 @@ write_string(const char *utf8, size_t max_len, unsigned char *buf) {
|
||||||
return 2 + len;
|
return 2 + len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint16_t
|
||||||
|
to_fixed_point_16(float f) {
|
||||||
|
SDL_assert(f >= 0.0f && f <= 1.0f);
|
||||||
|
uint32_t u = f * 0x1p16f; // 2^16
|
||||||
|
if (u >= 0xffff) {
|
||||||
|
u = 0xffff;
|
||||||
|
}
|
||||||
|
return (uint16_t) u;
|
||||||
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
|
control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
|
||||||
buf[0] = msg->type;
|
buf[0] = msg->type;
|
||||||
|
@ -43,6 +54,14 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
|
||||||
buffer_write32be(&buf[2], msg->inject_mouse_event.buttons);
|
buffer_write32be(&buf[2], msg->inject_mouse_event.buttons);
|
||||||
write_position(&buf[6], &msg->inject_mouse_event.position);
|
write_position(&buf[6], &msg->inject_mouse_event.position);
|
||||||
return 18;
|
return 18;
|
||||||
|
case CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT:
|
||||||
|
buf[1] = msg->inject_touch_event.action;
|
||||||
|
buffer_write64be(&buf[2], msg->inject_touch_event.pointer_id);
|
||||||
|
write_position(&buf[10], &msg->inject_touch_event.position);
|
||||||
|
uint16_t pressure =
|
||||||
|
to_fixed_point_16(msg->inject_touch_event.pressure);
|
||||||
|
buffer_write16be(&buf[22], pressure);
|
||||||
|
return 24;
|
||||||
case CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT:
|
case CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT:
|
||||||
write_position(&buf[1], &msg->inject_scroll_event.position);
|
write_position(&buf[1], &msg->inject_scroll_event.position);
|
||||||
buffer_write32be(&buf[13],
|
buffer_write32be(&buf[13],
|
||||||
|
|
|
@ -19,6 +19,7 @@ enum control_msg_type {
|
||||||
CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
CONTROL_MSG_TYPE_INJECT_KEYCODE,
|
||||||
CONTROL_MSG_TYPE_INJECT_TEXT,
|
CONTROL_MSG_TYPE_INJECT_TEXT,
|
||||||
CONTROL_MSG_TYPE_INJECT_MOUSE_EVENT,
|
CONTROL_MSG_TYPE_INJECT_MOUSE_EVENT,
|
||||||
|
CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||||
CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
|
CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
|
||||||
CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
|
CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON,
|
||||||
CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL,
|
CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL,
|
||||||
|
@ -50,6 +51,12 @@ struct control_msg {
|
||||||
enum android_motionevent_buttons buttons;
|
enum android_motionevent_buttons buttons;
|
||||||
struct position position;
|
struct position position;
|
||||||
} inject_mouse_event;
|
} inject_mouse_event;
|
||||||
|
struct {
|
||||||
|
enum android_motionevent_action action;
|
||||||
|
uint64_t pointer_id;
|
||||||
|
struct position position;
|
||||||
|
float pressure;
|
||||||
|
} inject_touch_event;
|
||||||
struct {
|
struct {
|
||||||
struct position position;
|
struct position position;
|
||||||
int32_t hscroll;
|
int32_t hscroll;
|
||||||
|
|
|
@ -100,6 +100,41 @@ static void test_serialize_inject_mouse_event(void) {
|
||||||
assert(!memcmp(buf, expected, sizeof(expected)));
|
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_serialize_inject_touch_event(void) {
|
||||||
|
struct control_msg msg = {
|
||||||
|
.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||||
|
.inject_touch_event = {
|
||||||
|
.action = AMOTION_EVENT_ACTION_DOWN,
|
||||||
|
.pointer_id = 0x1234567887654321L,
|
||||||
|
.position = {
|
||||||
|
.point = {
|
||||||
|
.x = 100,
|
||||||
|
.y = 200,
|
||||||
|
},
|
||||||
|
.screen_size = {
|
||||||
|
.width = 1080,
|
||||||
|
.height = 1920,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.pressure = 1.0f,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
|
||||||
|
int size = control_msg_serialize(&msg, buf);
|
||||||
|
assert(size == 24);
|
||||||
|
|
||||||
|
const unsigned char expected[] = {
|
||||||
|
CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT,
|
||||||
|
0x00, // AKEY_EVENT_ACTION_DOWN
|
||||||
|
0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21, // pointer id
|
||||||
|
0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0xc8, // 100 200
|
||||||
|
0x04, 0x38, 0x07, 0x80, // 1080 1920
|
||||||
|
0xff, 0xff, // pressure
|
||||||
|
};
|
||||||
|
assert(!memcmp(buf, expected, sizeof(expected)));
|
||||||
|
}
|
||||||
|
|
||||||
static void test_serialize_inject_scroll_event(void) {
|
static void test_serialize_inject_scroll_event(void) {
|
||||||
struct control_msg msg = {
|
struct control_msg msg = {
|
||||||
.type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
|
.type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT,
|
||||||
|
@ -237,6 +272,7 @@ int main(void) {
|
||||||
test_serialize_inject_text();
|
test_serialize_inject_text();
|
||||||
test_serialize_inject_text_long();
|
test_serialize_inject_text_long();
|
||||||
test_serialize_inject_mouse_event();
|
test_serialize_inject_mouse_event();
|
||||||
|
test_serialize_inject_touch_event();
|
||||||
test_serialize_inject_scroll_event();
|
test_serialize_inject_scroll_event();
|
||||||
test_serialize_back_or_screen_on();
|
test_serialize_back_or_screen_on();
|
||||||
test_serialize_expand_notification_panel();
|
test_serialize_expand_notification_panel();
|
||||||
|
|
|
@ -8,13 +8,14 @@ public final class ControlMessage {
|
||||||
public static final int TYPE_INJECT_KEYCODE = 0;
|
public static final int TYPE_INJECT_KEYCODE = 0;
|
||||||
public static final int TYPE_INJECT_TEXT = 1;
|
public static final int TYPE_INJECT_TEXT = 1;
|
||||||
public static final int TYPE_INJECT_MOUSE_EVENT = 2;
|
public static final int TYPE_INJECT_MOUSE_EVENT = 2;
|
||||||
public static final int TYPE_INJECT_SCROLL_EVENT = 3;
|
public static final int TYPE_INJECT_TOUCH_EVENT = 3;
|
||||||
public static final int TYPE_BACK_OR_SCREEN_ON = 4;
|
public static final int TYPE_INJECT_SCROLL_EVENT = 4;
|
||||||
public static final int TYPE_EXPAND_NOTIFICATION_PANEL = 5;
|
public static final int TYPE_BACK_OR_SCREEN_ON = 5;
|
||||||
public static final int TYPE_COLLAPSE_NOTIFICATION_PANEL = 6;
|
public static final int TYPE_EXPAND_NOTIFICATION_PANEL = 6;
|
||||||
public static final int TYPE_GET_CLIPBOARD = 7;
|
public static final int TYPE_COLLAPSE_NOTIFICATION_PANEL = 7;
|
||||||
public static final int TYPE_SET_CLIPBOARD = 8;
|
public static final int TYPE_GET_CLIPBOARD = 8;
|
||||||
public static final int TYPE_SET_SCREEN_POWER_MODE = 9;
|
public static final int TYPE_SET_CLIPBOARD = 9;
|
||||||
|
public static final int TYPE_SET_SCREEN_POWER_MODE = 10;
|
||||||
|
|
||||||
private int type;
|
private int type;
|
||||||
private String text;
|
private String text;
|
||||||
|
@ -22,6 +23,8 @@ public final class ControlMessage {
|
||||||
private int action; // KeyEvent.ACTION_* or MotionEvent.ACTION_* or POWER_MODE_*
|
private int action; // KeyEvent.ACTION_* or MotionEvent.ACTION_* or POWER_MODE_*
|
||||||
private int keycode; // KeyEvent.KEYCODE_*
|
private int keycode; // KeyEvent.KEYCODE_*
|
||||||
private int buttons; // MotionEvent.BUTTON_*
|
private int buttons; // MotionEvent.BUTTON_*
|
||||||
|
private long pointerId;
|
||||||
|
private float pressure;
|
||||||
private Position position;
|
private Position position;
|
||||||
private int hScroll;
|
private int hScroll;
|
||||||
private int vScroll;
|
private int vScroll;
|
||||||
|
@ -54,6 +57,16 @@ public final class ControlMessage {
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ControlMessage createInjectTouchEvent(int action, long pointerId, Position position, float pressure) {
|
||||||
|
ControlMessage msg = new ControlMessage();
|
||||||
|
msg.type = TYPE_INJECT_TOUCH_EVENT;
|
||||||
|
msg.action = action;
|
||||||
|
msg.pointerId = pointerId;
|
||||||
|
msg.pressure = pressure;
|
||||||
|
msg.position = position;
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
public static ControlMessage createInjectScrollEvent(Position position, int hScroll, int vScroll) {
|
public static ControlMessage createInjectScrollEvent(Position position, int hScroll, int vScroll) {
|
||||||
ControlMessage msg = new ControlMessage();
|
ControlMessage msg = new ControlMessage();
|
||||||
msg.type = TYPE_INJECT_SCROLL_EVENT;
|
msg.type = TYPE_INJECT_SCROLL_EVENT;
|
||||||
|
@ -110,6 +123,14 @@ public final class ControlMessage {
|
||||||
return buttons;
|
return buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getPointerId() {
|
||||||
|
return pointerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getPressure() {
|
||||||
|
return pressure;
|
||||||
|
}
|
||||||
|
|
||||||
public Position getPosition() {
|
public Position getPosition() {
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ public class ControlMessageReader {
|
||||||
|
|
||||||
private static final int INJECT_KEYCODE_PAYLOAD_LENGTH = 9;
|
private static final int INJECT_KEYCODE_PAYLOAD_LENGTH = 9;
|
||||||
private static final int INJECT_MOUSE_EVENT_PAYLOAD_LENGTH = 17;
|
private static final int INJECT_MOUSE_EVENT_PAYLOAD_LENGTH = 17;
|
||||||
|
private static final int INJECT_TOUCH_EVENT_PAYLOAD_LENGTH = 21;
|
||||||
private static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
|
private static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
|
||||||
private static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
|
private static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
|
||||||
|
|
||||||
|
@ -62,6 +63,9 @@ public class ControlMessageReader {
|
||||||
case ControlMessage.TYPE_INJECT_MOUSE_EVENT:
|
case ControlMessage.TYPE_INJECT_MOUSE_EVENT:
|
||||||
msg = parseInjectMouseEvent();
|
msg = parseInjectMouseEvent();
|
||||||
break;
|
break;
|
||||||
|
case ControlMessage.TYPE_INJECT_TOUCH_EVENT:
|
||||||
|
msg = parseInjectTouchEvent();
|
||||||
|
break;
|
||||||
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
|
||||||
msg = parseInjectScrollEvent();
|
msg = parseInjectScrollEvent();
|
||||||
break;
|
break;
|
||||||
|
@ -130,6 +134,21 @@ public class ControlMessageReader {
|
||||||
return ControlMessage.createInjectMouseEvent(action, buttons, position);
|
return ControlMessage.createInjectMouseEvent(action, buttons, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
|
private ControlMessage parseInjectTouchEvent() {
|
||||||
|
if (buffer.remaining() < INJECT_TOUCH_EVENT_PAYLOAD_LENGTH) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int action = toUnsigned(buffer.get());
|
||||||
|
long pointerId = buffer.getLong();
|
||||||
|
Position position = readPosition(buffer);
|
||||||
|
// 16 bits fixed-point
|
||||||
|
int pressureInt = toUnsigned(buffer.getShort());
|
||||||
|
// convert it to a float between 0 and 1 (0x1p16f is 2^16 as float)
|
||||||
|
float pressure = pressureInt == 0xffff ? 1f : (pressureInt / 0x1p16f);
|
||||||
|
return ControlMessage.createInjectTouchEvent(action, pointerId, position, pressure);
|
||||||
|
}
|
||||||
|
|
||||||
private ControlMessage parseInjectScrollEvent() {
|
private ControlMessage parseInjectScrollEvent() {
|
||||||
if (buffer.remaining() < INJECT_SCROLL_EVENT_PAYLOAD_LENGTH) {
|
if (buffer.remaining() < INJECT_SCROLL_EVENT_PAYLOAD_LENGTH) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -105,6 +105,37 @@ public class ControlMessageReaderTest {
|
||||||
Assert.assertEquals(1920, event.getPosition().getScreenSize().getHeight());
|
Assert.assertEquals(1920, event.getPosition().getScreenSize().getHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
|
public void testParseTouchEvent() throws IOException {
|
||||||
|
ControlMessageReader reader = new ControlMessageReader();
|
||||||
|
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
DataOutputStream dos = new DataOutputStream(bos);
|
||||||
|
dos.writeByte(ControlMessage.TYPE_INJECT_TOUCH_EVENT);
|
||||||
|
dos.writeByte(MotionEvent.ACTION_DOWN);
|
||||||
|
dos.writeLong(-42); // pointerId
|
||||||
|
dos.writeInt(100);
|
||||||
|
dos.writeInt(200);
|
||||||
|
dos.writeShort(1080);
|
||||||
|
dos.writeShort(1920);
|
||||||
|
dos.writeShort(0xffff); // pressure
|
||||||
|
|
||||||
|
byte[] packet = bos.toByteArray();
|
||||||
|
|
||||||
|
reader.readFrom(new ByteArrayInputStream(packet));
|
||||||
|
ControlMessage event = reader.next();
|
||||||
|
|
||||||
|
Assert.assertEquals(ControlMessage.TYPE_INJECT_TOUCH_EVENT, event.getType());
|
||||||
|
Assert.assertEquals(MotionEvent.ACTION_DOWN, event.getAction());
|
||||||
|
Assert.assertEquals(-42, event.getPointerId());
|
||||||
|
Assert.assertEquals(100, event.getPosition().getPoint().getX());
|
||||||
|
Assert.assertEquals(200, event.getPosition().getPoint().getY());
|
||||||
|
Assert.assertEquals(1080, event.getPosition().getScreenSize().getWidth());
|
||||||
|
Assert.assertEquals(1920, event.getPosition().getScreenSize().getHeight());
|
||||||
|
Assert.assertEquals(1f, event.getPressure(), 0f); // must be exact
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("checkstyle:MagicNumber")
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
public void testParseScrollEvent() throws IOException {
|
public void testParseScrollEvent() throws IOException {
|
||||||
|
|
Loading…
Reference in a new issue