2022-01-22 18:09:41 +08:00
|
|
|
#include "screen_otg.h"
|
|
|
|
|
|
|
|
#include "icon.h"
|
|
|
|
#include "options.h"
|
|
|
|
#include "util/log.h"
|
|
|
|
|
|
|
|
static void
|
2022-02-18 04:37:14 +08:00
|
|
|
sc_screen_otg_set_mouse_capture(struct sc_screen_otg *screen, bool capture) {
|
2022-02-18 04:46:13 +08:00
|
|
|
#ifdef __APPLE__
|
|
|
|
// Workaround for SDL bug on macOS:
|
|
|
|
// <https://github.com/libsdl-org/SDL/issues/5340>
|
|
|
|
if (capture) {
|
|
|
|
int mouse_x, mouse_y;
|
|
|
|
SDL_GetGlobalMouseState(&mouse_x, &mouse_y);
|
|
|
|
|
|
|
|
int x, y, w, h;
|
|
|
|
SDL_GetWindowPosition(screen->window, &x, &y);
|
|
|
|
SDL_GetWindowSize(screen->window, &w, &h);
|
|
|
|
|
|
|
|
bool outside_window = mouse_x < x || mouse_x >= x + w
|
|
|
|
|| mouse_y < y || mouse_y >= y + h;
|
|
|
|
if (outside_window) {
|
|
|
|
SDL_WarpMouseInWindow(screen->window, w / 2, h / 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
2022-02-18 04:37:14 +08:00
|
|
|
(void) screen;
|
2022-02-18 04:46:13 +08:00
|
|
|
#endif
|
2022-01-22 18:09:41 +08:00
|
|
|
if (SDL_SetRelativeMouseMode(capture)) {
|
|
|
|
LOGE("Could not set relative mouse mode to %s: %s",
|
|
|
|
capture ? "true" : "false", SDL_GetError());
|
|
|
|
}
|
2022-02-10 04:06:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool
|
2022-02-18 04:37:14 +08:00
|
|
|
sc_screen_otg_get_mouse_capture(struct sc_screen_otg *screen) {
|
|
|
|
(void) screen;
|
2022-02-10 04:06:16 +08:00
|
|
|
return SDL_GetRelativeMouseMode();
|
|
|
|
}
|
2022-01-22 18:09:41 +08:00
|
|
|
|
2022-02-10 04:06:16 +08:00
|
|
|
static inline void
|
2022-02-18 04:37:14 +08:00
|
|
|
sc_screen_otg_toggle_mouse_capture(struct sc_screen_otg *screen) {
|
|
|
|
(void) screen;
|
|
|
|
bool new_value = !sc_screen_otg_get_mouse_capture(screen);
|
|
|
|
sc_screen_otg_set_mouse_capture(screen, new_value);
|
2022-01-22 18:09:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sc_screen_otg_render(struct sc_screen_otg *screen) {
|
|
|
|
SDL_RenderClear(screen->renderer);
|
|
|
|
if (screen->texture) {
|
|
|
|
SDL_RenderCopy(screen->renderer, screen->texture, NULL, NULL);
|
|
|
|
}
|
|
|
|
SDL_RenderPresent(screen->renderer);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
sc_screen_otg_init(struct sc_screen_otg *screen,
|
|
|
|
const struct sc_screen_otg_params *params) {
|
|
|
|
screen->keyboard = params->keyboard;
|
|
|
|
screen->mouse = params->mouse;
|
|
|
|
|
|
|
|
screen->mouse_capture_key_pressed = 0;
|
|
|
|
|
|
|
|
const char *title = params->window_title;
|
|
|
|
assert(title);
|
|
|
|
|
|
|
|
int x = params->window_x != SC_WINDOW_POSITION_UNDEFINED
|
|
|
|
? params->window_x : (int) SDL_WINDOWPOS_UNDEFINED;
|
|
|
|
int y = params->window_y != SC_WINDOW_POSITION_UNDEFINED
|
|
|
|
? params->window_y : (int) SDL_WINDOWPOS_UNDEFINED;
|
2022-04-26 00:44:42 +08:00
|
|
|
int width = params->window_width ? params->window_width : 256;
|
|
|
|
int height = params->window_height ? params->window_height : 256;
|
2022-01-22 18:09:41 +08:00
|
|
|
|
2022-04-29 01:11:42 +08:00
|
|
|
uint32_t window_flags = SDL_WINDOW_ALLOW_HIGHDPI
|
|
|
|
| SDL_WINDOW_RESIZABLE;
|
2022-01-22 18:09:41 +08:00
|
|
|
if (params->always_on_top) {
|
|
|
|
window_flags |= SDL_WINDOW_ALWAYS_ON_TOP;
|
|
|
|
}
|
|
|
|
if (params->window_borderless) {
|
|
|
|
window_flags |= SDL_WINDOW_BORDERLESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
screen->window = SDL_CreateWindow(title, x, y, width, height, window_flags);
|
|
|
|
if (!screen->window) {
|
|
|
|
LOGE("Could not create window: %s", SDL_GetError());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
screen->renderer = SDL_CreateRenderer(screen->window, -1, 0);
|
|
|
|
if (!screen->renderer) {
|
|
|
|
LOGE("Could not create renderer: %s", SDL_GetError());
|
|
|
|
goto error_destroy_window;
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_Surface *icon = scrcpy_icon_load();
|
|
|
|
|
|
|
|
if (icon) {
|
|
|
|
SDL_SetWindowIcon(screen->window, icon);
|
|
|
|
|
2022-04-29 02:40:20 +08:00
|
|
|
if (SDL_RenderSetLogicalSize(screen->renderer, icon->w, icon->h)) {
|
2022-04-26 00:44:42 +08:00
|
|
|
LOGW("Could not set renderer logical size: %s", SDL_GetError());
|
|
|
|
// don't fail
|
|
|
|
}
|
|
|
|
|
2022-01-22 18:09:41 +08:00
|
|
|
screen->texture = SDL_CreateTextureFromSurface(screen->renderer, icon);
|
|
|
|
scrcpy_icon_destroy(icon);
|
|
|
|
if (!screen->texture) {
|
|
|
|
goto error_destroy_renderer;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
screen->texture = NULL;
|
|
|
|
LOGW("Could not load icon");
|
|
|
|
}
|
|
|
|
|
2022-01-27 05:50:10 +08:00
|
|
|
if (screen->mouse) {
|
|
|
|
// Capture mouse on start
|
2022-02-18 04:37:14 +08:00
|
|
|
sc_screen_otg_set_mouse_capture(screen, true);
|
2022-01-27 05:50:10 +08:00
|
|
|
}
|
2022-01-22 18:09:41 +08:00
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
error_destroy_window:
|
|
|
|
SDL_DestroyWindow(screen->window);
|
|
|
|
error_destroy_renderer:
|
|
|
|
SDL_DestroyRenderer(screen->renderer);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sc_screen_otg_destroy(struct sc_screen_otg *screen) {
|
|
|
|
if (screen->texture) {
|
|
|
|
SDL_DestroyTexture(screen->texture);
|
|
|
|
}
|
|
|
|
SDL_DestroyRenderer(screen->renderer);
|
|
|
|
SDL_DestroyWindow(screen->window);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool
|
|
|
|
sc_screen_otg_is_mouse_capture_key(SDL_Keycode key) {
|
|
|
|
return key == SDLK_LALT || key == SDLK_LGUI || key == SDLK_RGUI;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sc_screen_otg_process_key(struct sc_screen_otg *screen,
|
|
|
|
const SDL_KeyboardEvent *event) {
|
|
|
|
assert(screen->keyboard);
|
|
|
|
struct sc_key_processor *kp = &screen->keyboard->key_processor;
|
|
|
|
|
|
|
|
struct sc_key_event evt = {
|
|
|
|
.action = sc_action_from_sdl_keyboard_type(event->type),
|
|
|
|
.keycode = sc_keycode_from_sdl(event->keysym.sym),
|
|
|
|
.scancode = sc_scancode_from_sdl(event->keysym.scancode),
|
|
|
|
.repeat = event->repeat,
|
|
|
|
.mods_state = sc_mods_state_from_sdl(event->keysym.mod),
|
|
|
|
};
|
|
|
|
|
|
|
|
assert(kp->ops->process_key);
|
|
|
|
kp->ops->process_key(kp, &evt, SC_SEQUENCE_INVALID);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sc_screen_otg_process_mouse_motion(struct sc_screen_otg *screen,
|
|
|
|
const SDL_MouseMotionEvent *event) {
|
|
|
|
assert(screen->mouse);
|
|
|
|
struct sc_mouse_processor *mp = &screen->mouse->mouse_processor;
|
|
|
|
|
|
|
|
struct sc_mouse_motion_event evt = {
|
|
|
|
// .position not used for HID events
|
|
|
|
.xrel = event->xrel,
|
|
|
|
.yrel = event->yrel,
|
|
|
|
.buttons_state = sc_mouse_buttons_state_from_sdl(event->state, true),
|
|
|
|
};
|
|
|
|
|
|
|
|
assert(mp->ops->process_mouse_motion);
|
|
|
|
mp->ops->process_mouse_motion(mp, &evt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sc_screen_otg_process_mouse_button(struct sc_screen_otg *screen,
|
|
|
|
const SDL_MouseButtonEvent *event) {
|
|
|
|
assert(screen->mouse);
|
|
|
|
struct sc_mouse_processor *mp = &screen->mouse->mouse_processor;
|
|
|
|
|
|
|
|
uint32_t sdl_buttons_state = SDL_GetMouseState(NULL, NULL);
|
|
|
|
|
|
|
|
struct sc_mouse_click_event evt = {
|
|
|
|
// .position not used for HID events
|
|
|
|
.action = sc_action_from_sdl_mousebutton_type(event->type),
|
|
|
|
.button = sc_mouse_button_from_sdl(event->button),
|
|
|
|
.buttons_state =
|
|
|
|
sc_mouse_buttons_state_from_sdl(sdl_buttons_state, true),
|
|
|
|
};
|
|
|
|
|
|
|
|
assert(mp->ops->process_mouse_click);
|
|
|
|
mp->ops->process_mouse_click(mp, &evt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sc_screen_otg_process_mouse_wheel(struct sc_screen_otg *screen,
|
|
|
|
const SDL_MouseWheelEvent *event) {
|
|
|
|
assert(screen->mouse);
|
|
|
|
struct sc_mouse_processor *mp = &screen->mouse->mouse_processor;
|
|
|
|
|
|
|
|
uint32_t sdl_buttons_state = SDL_GetMouseState(NULL, NULL);
|
|
|
|
|
|
|
|
struct sc_mouse_scroll_event evt = {
|
|
|
|
// .position not used for HID events
|
|
|
|
.hscroll = event->x,
|
|
|
|
.vscroll = event->y,
|
|
|
|
.buttons_state =
|
|
|
|
sc_mouse_buttons_state_from_sdl(sdl_buttons_state, true),
|
|
|
|
};
|
|
|
|
|
|
|
|
assert(mp->ops->process_mouse_scroll);
|
|
|
|
mp->ops->process_mouse_scroll(mp, &evt);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sc_screen_otg_handle_event(struct sc_screen_otg *screen, SDL_Event *event) {
|
|
|
|
switch (event->type) {
|
|
|
|
case SDL_WINDOWEVENT:
|
|
|
|
switch (event->window.event) {
|
|
|
|
case SDL_WINDOWEVENT_EXPOSED:
|
|
|
|
sc_screen_otg_render(screen);
|
|
|
|
break;
|
|
|
|
case SDL_WINDOWEVENT_FOCUS_LOST:
|
2022-01-27 05:50:10 +08:00
|
|
|
if (screen->mouse) {
|
2022-02-18 04:37:14 +08:00
|
|
|
sc_screen_otg_set_mouse_capture(screen, false);
|
2022-01-27 05:50:10 +08:00
|
|
|
}
|
2022-01-22 18:09:41 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
2022-01-27 05:50:10 +08:00
|
|
|
case SDL_KEYDOWN:
|
|
|
|
if (screen->mouse) {
|
|
|
|
SDL_Keycode key = event->key.keysym.sym;
|
|
|
|
if (sc_screen_otg_is_mouse_capture_key(key)) {
|
|
|
|
if (!screen->mouse_capture_key_pressed) {
|
|
|
|
screen->mouse_capture_key_pressed = key;
|
|
|
|
} else {
|
|
|
|
// Another mouse capture key has been pressed, cancel
|
|
|
|
// mouse (un)capture
|
|
|
|
screen->mouse_capture_key_pressed = 0;
|
|
|
|
}
|
|
|
|
// Mouse capture keys are never forwarded to the device
|
|
|
|
return;
|
2022-01-22 18:09:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-27 05:50:10 +08:00
|
|
|
if (screen->keyboard) {
|
|
|
|
sc_screen_otg_process_key(screen, &event->key);
|
|
|
|
}
|
2022-01-22 18:09:41 +08:00
|
|
|
break;
|
2022-01-27 05:50:10 +08:00
|
|
|
case SDL_KEYUP:
|
|
|
|
if (screen->mouse) {
|
|
|
|
SDL_Keycode key = event->key.keysym.sym;
|
|
|
|
SDL_Keycode cap = screen->mouse_capture_key_pressed;
|
|
|
|
screen->mouse_capture_key_pressed = 0;
|
|
|
|
if (sc_screen_otg_is_mouse_capture_key(key)) {
|
|
|
|
if (key == cap) {
|
|
|
|
// A mouse capture key has been pressed then released:
|
|
|
|
// toggle the capture mouse mode
|
2022-02-18 04:37:14 +08:00
|
|
|
sc_screen_otg_toggle_mouse_capture(screen);
|
2022-01-27 05:50:10 +08:00
|
|
|
}
|
|
|
|
// Mouse capture keys are never forwarded to the device
|
|
|
|
return;
|
2022-01-22 18:09:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-27 05:50:10 +08:00
|
|
|
if (screen->keyboard) {
|
|
|
|
sc_screen_otg_process_key(screen, &event->key);
|
|
|
|
}
|
2022-01-22 18:09:41 +08:00
|
|
|
break;
|
|
|
|
case SDL_MOUSEMOTION:
|
2022-02-18 04:37:14 +08:00
|
|
|
if (screen->mouse && sc_screen_otg_get_mouse_capture(screen)) {
|
2022-01-22 18:09:41 +08:00
|
|
|
sc_screen_otg_process_mouse_motion(screen, &event->motion);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SDL_MOUSEBUTTONDOWN:
|
2022-02-18 04:37:14 +08:00
|
|
|
if (screen->mouse && sc_screen_otg_get_mouse_capture(screen)) {
|
2022-01-22 18:09:41 +08:00
|
|
|
sc_screen_otg_process_mouse_button(screen, &event->button);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SDL_MOUSEBUTTONUP:
|
2022-01-27 05:50:10 +08:00
|
|
|
if (screen->mouse) {
|
2022-02-18 04:37:14 +08:00
|
|
|
if (sc_screen_otg_get_mouse_capture(screen)) {
|
2022-01-27 05:50:10 +08:00
|
|
|
sc_screen_otg_process_mouse_button(screen, &event->button);
|
|
|
|
} else {
|
2022-02-18 04:37:14 +08:00
|
|
|
sc_screen_otg_set_mouse_capture(screen, true);
|
2022-01-27 05:50:10 +08:00
|
|
|
}
|
2022-01-22 18:09:41 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SDL_MOUSEWHEEL:
|
2022-02-18 04:37:14 +08:00
|
|
|
if (screen->mouse && sc_screen_otg_get_mouse_capture(screen)) {
|
2022-01-22 18:09:41 +08:00
|
|
|
sc_screen_otg_process_mouse_wheel(screen, &event->wheel);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|