Scale mouse events

The video screen size on the client may differ from the real device
screen size (e.g. the video stream may be scaled down). As a
consequence, mouse events must be scaled to match the real device
coordinates.

For this purpose, make the client send the video screen size along with
the absolute pointer location, and the server scale the location to
match the real device size before injecting mouse events.
This commit is contained in:
Romain Vimont
2018-01-22 16:50:08 +01:00
parent 11a60e5767
commit 8984c1a7c4
13 changed files with 246 additions and 96 deletions

26
app/src/common.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef COMMON_H
#define COMMON_H
#include <SDL2/SDL_stdinc.h>
#define MIN(X,Y) (X) < (Y) ? (X) : (Y)
#define MAX(X,Y) (X) > (Y) ? (X) : (Y)
struct size {
Uint16 width;
Uint16 height;
};
struct position {
Uint16 x;
Uint16 y;
};
struct point {
// The video screen size may be different from the real device screen size,
// so store to which size the absolute position apply, to scale it accordingly.
struct size screen_size;
struct position position;
};
#endif

View File

@@ -17,6 +17,13 @@ static inline void write32(Uint8 *buf, Uint32 value) {
buf[3] = value;
}
static void write_point(Uint8 *buf, const struct point *point) {
write16(&buf[0], point->position.x);
write16(&buf[2], point->position.y);
write16(&buf[4], point->screen_size.width);
write16(&buf[6], point->screen_size.height);
}
int control_event_serialize(const struct control_event *event, unsigned char *buf) {
buf[0] = event->type;
switch (event->type) {
@@ -38,12 +45,10 @@ int control_event_serialize(const struct control_event *event, unsigned char *bu
case CONTROL_EVENT_TYPE_MOUSE:
buf[1] = event->mouse_event.action;
write32(&buf[2], event->mouse_event.buttons);
write32(&buf[6], (Uint32) event->mouse_event.x);
write32(&buf[10], (Uint32) event->mouse_event.y);
write_point(&buf[6], &event->mouse_event.point);
return 14;
case CONTROL_EVENT_TYPE_SCROLL:
write32(&buf[1], (Uint32) event->scroll_event.x);
write32(&buf[5], (Uint32) event->scroll_event.y);
write_point(&buf[1], &event->scroll_event.point);
write32(&buf[9], (Uint32) event->scroll_event.hscroll);
write32(&buf[13], (Uint32) event->scroll_event.vscroll);
return 17;

View File

@@ -6,6 +6,7 @@
#include "android/input.h"
#include "android/keycodes.h"
#include "common.h"
#define CONTROL_EVENT_QUEUE_SIZE 64
#define SERIALIZED_EVENT_MAX_SIZE 33
@@ -32,12 +33,10 @@ struct control_event {
struct {
enum android_motionevent_action action;
enum android_motionevent_buttons buttons;
Sint32 x;
Sint32 y;
struct point point;
} mouse_event;
struct {
Sint32 x;
Sint32 y;
struct point point;
Sint32 hscroll;
Sint32 vscroll;
} scroll_event;

View File

@@ -117,7 +117,8 @@ static enum android_motionevent_buttons convert_mouse_buttons(Uint32 state) {
return buttons;
}
SDL_bool input_key_from_sdl_to_android(const SDL_KeyboardEvent *from, struct control_event *to) {
SDL_bool input_key_from_sdl_to_android(const SDL_KeyboardEvent *from,
struct control_event *to) {
to->type = CONTROL_EVENT_TYPE_KEYCODE;
if (!convert_keycode_action(from->type, &to->keycode_event.action)) {
@@ -133,7 +134,9 @@ SDL_bool input_key_from_sdl_to_android(const SDL_KeyboardEvent *from, struct con
return SDL_TRUE;
}
SDL_bool mouse_button_from_sdl_to_android(const SDL_MouseButtonEvent *from, struct control_event *to) {
SDL_bool mouse_button_from_sdl_to_android(const SDL_MouseButtonEvent *from,
struct size screen_size,
struct control_event *to) {
to->type = CONTROL_EVENT_TYPE_MOUSE;
if (!convert_mouse_action(from->type, &to->mouse_event.action)) {
@@ -141,32 +144,36 @@ SDL_bool mouse_button_from_sdl_to_android(const SDL_MouseButtonEvent *from, stru
}
to->mouse_event.buttons = convert_mouse_buttons(SDL_BUTTON(from->button));
to->mouse_event.x = from->x;
to->mouse_event.y = from->y;
to->mouse_event.point.screen_size = screen_size;
to->mouse_event.point.position.x = (Uint16) from->x;
to->mouse_event.point.position.y = (Uint16) from->y;
return SDL_TRUE;
}
SDL_bool mouse_motion_from_sdl_to_android(const SDL_MouseMotionEvent *from, struct control_event *to) {
SDL_bool mouse_motion_from_sdl_to_android(const SDL_MouseMotionEvent *from,
const struct size screen_size,
struct control_event *to) {
to->type = CONTROL_EVENT_TYPE_MOUSE;
to->mouse_event.action = AMOTION_EVENT_ACTION_MOVE;
to->mouse_event.buttons = convert_mouse_buttons(from->state);
to->mouse_event.x = from->x;
to->mouse_event.y = from->y;
to->mouse_event.point.screen_size = screen_size;
to->mouse_event.point.position.x = from->x;
to->mouse_event.point.position.y = from->y;
return SDL_TRUE;
}
SDL_bool mouse_wheel_from_sdl_to_android(const struct complete_mouse_wheel_event *from, struct control_event *to) {
SDL_bool mouse_wheel_from_sdl_to_android(const SDL_MouseWheelEvent *from,
const struct point point,
struct control_event *to) {
to->type = CONTROL_EVENT_TYPE_SCROLL;
to->scroll_event.x = from->x;
to->scroll_event.y = from->y;
to->scroll_event.point = point;
SDL_MouseWheelEvent *wheel = from->mouse_wheel_event;
int mul = wheel->direction == SDL_MOUSEWHEEL_NORMAL ? 1 : -1;
to->scroll_event.hscroll = mul * wheel->x;
to->scroll_event.vscroll = mul * wheel->y;
int mul = from->direction == SDL_MOUSEWHEEL_NORMAL ? 1 : -1;
to->scroll_event.hscroll = mul * from->x;
to->scroll_event.vscroll = mul * from->y;
return SDL_TRUE;
}

View File

@@ -5,16 +5,31 @@
#include <SDL2/SDL_events.h>
#include "controlevent.h"
// on Android, a scroll event requires the current mouse position
struct complete_mouse_wheel_event {
SDL_MouseWheelEvent *mouse_wheel_event;
Sint32 x;
Sint32 y;
struct complete_mouse_motion_event {
SDL_MouseMotionEvent *mouse_motion_event;
struct size screen_size;
};
SDL_bool input_key_from_sdl_to_android(const SDL_KeyboardEvent *from, struct control_event *to);
SDL_bool mouse_button_from_sdl_to_android(const SDL_MouseButtonEvent *from, struct control_event *to);
SDL_bool mouse_motion_from_sdl_to_android(const SDL_MouseMotionEvent *from, struct control_event *to);
SDL_bool mouse_wheel_from_sdl_to_android(const struct complete_mouse_wheel_event *from, struct control_event *to);
struct complete_mouse_wheel_event {
SDL_MouseWheelEvent *mouse_wheel_event;
struct point position;
};
SDL_bool input_key_from_sdl_to_android(const SDL_KeyboardEvent *from,
struct control_event *to);
SDL_bool mouse_button_from_sdl_to_android(const SDL_MouseButtonEvent *from,
struct size screen_size,
struct control_event *to);
// the video size may be different from the real device size, so we need the size
// to which the absolute position apply, to scale it accordingly
SDL_bool mouse_motion_from_sdl_to_android(const SDL_MouseMotionEvent *from,
struct size screen_size,
struct control_event *to);
// on Android, a scroll event requires the current mouse position
SDL_bool mouse_wheel_from_sdl_to_android(const SDL_MouseWheelEvent *from,
struct point point,
struct control_event *to);
#endif

View File

@@ -9,6 +9,7 @@
#include <SDL2/SDL_net.h>
#include "command.h"
#include "common.h"
#include "control.h"
#include "convert.h"
#include "decoder.h"
@@ -20,13 +21,6 @@
#define DEVICE_NAME_FIELD_LENGTH 64
#define DISPLAY_MARGINS 96
#define MIN(X,Y) (X) < (Y) ? (X) : (Y)
#define MAX(X,Y) (X) > (Y) ? (X) : (Y)
struct size {
Uint16 width;
Uint16 height;
};
static struct frames frames;
static struct decoder decoder;
@@ -110,6 +104,17 @@ static inline struct size get_window_size(SDL_Window *window) {
return size;
}
static inline struct position get_mouse_position() {
int x;
int y;
SDL_GetMouseState(&x, &y);
SDL_assert_release(x >= 0 && x < 0x10000 && y >= 0 && y < 0x10000);
return (struct position) {
.x = (Uint16) x,
.y = (Uint16) y,
};
}
// return the optimal size of the window, with the following constraints:
// - it attempts to keep at least one dimension of the current_size (i.e. it crops the black borders)
// - it keeps the aspect ratio
@@ -140,7 +145,7 @@ static struct size get_optimal_size(struct size current_size, struct size frame_
}
// w and h must fit into 16 bits
SDL_assert_release(!(w & ~0xffff) && !(h & ~0xffff));
SDL_assert_release(w < 0x10000 && h < 0x10000);
return (struct size) {w, h};
}
@@ -287,27 +292,27 @@ static void handle_key(const SDL_KeyboardEvent *event) {
}
}
static void handle_mouse_motion(const SDL_MouseMotionEvent *event) {
static void handle_mouse_motion(const SDL_MouseMotionEvent *event, struct size screen_size) {
struct control_event control_event;
if (mouse_motion_from_sdl_to_android(event, &control_event)) {
if (mouse_motion_from_sdl_to_android(event, screen_size, &control_event)) {
if (!controller_push_event(&controller, &control_event)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Cannot send mouse motion event");
}
}
}
static void handle_mouse_button(const SDL_MouseButtonEvent *event) {
static void handle_mouse_button(const SDL_MouseButtonEvent *event, struct size screen_size) {
struct control_event control_event;
if (mouse_button_from_sdl_to_android(event, &control_event)) {
if (mouse_button_from_sdl_to_android(event, screen_size, &control_event)) {
if (!controller_push_event(&controller, &control_event)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Cannot send mouse button event");
}
}
}
static void handle_mouse_wheel(const struct complete_mouse_wheel_event *event) {
static void handle_mouse_wheel(const SDL_MouseWheelEvent *event, struct point point) {
struct control_event control_event;
if (mouse_wheel_from_sdl_to_android(event, &control_event)) {
if (mouse_wheel_from_sdl_to_android(event, point, &control_event)) {
if (!controller_push_event(&controller, &control_event)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Cannot send wheel button event");
}
@@ -346,24 +351,22 @@ void event_loop(void) {
handle_key(&event.key);
break;
case SDL_MOUSEMOTION:
handle_mouse_motion(&event.motion);
handle_mouse_motion(&event.motion, frame_size);
break;
case SDL_MOUSEWHEEL: {
struct complete_mouse_wheel_event complete_event;
complete_event.mouse_wheel_event = &event.wheel;
int x;
int y;
SDL_GetMouseState(&x, &y);
complete_event.x = (Sint32) x;
complete_event.y = (Sint32) y;
handle_mouse_wheel(&complete_event);
struct point point = {
.screen_size = frame_size,
.position = get_mouse_position(),
};
handle_mouse_wheel(&event.wheel, point);
break;
}
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
handle_mouse_button(&event.button);
case SDL_MOUSEBUTTONUP: {
handle_mouse_button(&event.button, frame_size);
break;
}
}
}
}

View File

@@ -51,8 +51,16 @@ static void test_serialize_mouse_event() {
.mouse_event = {
.action = AMOTION_EVENT_ACTION_DOWN,
.buttons = AMOTION_EVENT_BUTTON_PRIMARY,
.x = 260,
.y = 1026,
.point = {
.position = {
.x = 260,
.y = 1026,
},
.screen_size = {
.width = 1080,
.height = 1920,
},
},
},
};
@@ -64,8 +72,8 @@ static void test_serialize_mouse_event() {
0x02, // CONTROL_EVENT_TYPE_MOUSE
0x00, // AKEY_EVENT_ACTION_DOWN
0x00, 0x00, 0x00, 0x01, // AMOTION_EVENT_BUTTON_PRIMARY
0x00, 0x00, 0x01, 0x04, // 260
0x00, 0x00, 0x04, 0x02, // 1026
0x01, 0x04, 0x04, 0x02, // 260 1026
0x04, 0x38, 0x07, 0x80, // 1080 1920
};
assert(!memcmp(buf, expected, sizeof(expected)));
}
@@ -74,8 +82,16 @@ static void test_serialize_scroll_event() {
struct control_event event = {
.type = CONTROL_EVENT_TYPE_SCROLL,
.scroll_event = {
.x = 260,
.y = 1026,
.point = {
.position = {
.x = 260,
.y = 1026,
},
.screen_size = {
.width = 1080,
.height = 1920,
},
},
.hscroll = 1,
.vscroll = -1,
},
@@ -87,8 +103,8 @@ static void test_serialize_scroll_event() {
const unsigned char expected[] = {
0x03, // CONTROL_EVENT_TYPE_SCROLL
0x00, 0x00, 0x01, 0x04, // 260
0x00, 0x00, 0x04, 0x02, // 1026
0x01, 0x04, 0x04, 0x02, // 260 1026
0x04, 0x38, 0x07, 0x80, // 1080 1920
0x00, 0x00, 0x00, 0x01, // 1
0xFF, 0xFF, 0xFF, 0xFF, // -1
};