Compare commits

..

4 Commits

Author SHA1 Message Date
Diego Fernando Díaz A
ad6b8847d4 Add option to disable window decoration
Add --window-borderless parameter.

Signed-off-by: Romain Vimont <rom@rom1v.com>
2019-11-03 18:18:41 +01:00
Romain Vimont
fba89e6f73 Add option to specify the initial window size
Add --window-width and --window-height parameters.

If only one is provided, the other is computed so that the aspect ratio
is preserved.
2019-11-03 18:18:41 +01:00
Romain Vimont
4bf2b80c75 Update manpage for --window-{x,y} options 2019-11-03 18:18:41 +01:00
Diego Fernando Díaz A
f983ec67fa Add option to specify the initial window position
Add --window-x and --window-y parameters.

Signed-off-by: Romain Vimont <rom@rom1v.com>
2019-11-03 18:18:39 +01:00
15 changed files with 416 additions and 354 deletions

View File

@@ -268,33 +268,3 @@ For more details, go read the code!
If you find a bug, or have an awesome idea to implement, please discuss and If you find a bug, or have an awesome idea to implement, please discuss and
contribute ;-) contribute ;-)
### Debug the server
The server is pushed to the device by the client on startup.
To debug it, enable the server debugger during configuration:
```bash
meson x -Dserver_debugger=true
# or, if x is already configured
meson configure x -Dserver_debugger=true
```
Then recompile.
When you start scrcpy, it will start a debugger on port 5005 on the device.
Redirect that port to the computer:
```bash
adb forward tcp:5005 tcp:5005
```
In Android Studio, _Run_ > _Debug_ > _Edit configurations..._ On the left, click on
`+`, _Remote_, and fill the form:
- Host: `localhost`
- Port: `5005`
Then click on _Debug_.

View File

@@ -115,9 +115,6 @@ conf.set('HIDPI_SUPPORT', get_option('hidpi_support'))
# disable console on Windows # disable console on Windows
conf.set('WINDOWS_NOCONSOLE', get_option('windows_noconsole')) conf.set('WINDOWS_NOCONSOLE', get_option('windows_noconsole'))
# run a server debugger and wait for a client to be attached
conf.set('SERVER_DEBUGGER', get_option('server_debugger'))
configure_file(configuration: conf, output: 'config.h') configure_file(configuration: conf, output: 'config.h')
src_dir = include_directories('src') src_dir = include_directories('src')

View File

@@ -61,16 +61,6 @@ Set the TCP port the client listens on.
Default is 27183. Default is 27183.
.TP
.BI \-\-prefer\-text\-events " mode
Configure how key/text events are forwarded to the Android device.
Possible \fImode\fRs are "always" (every text is sent as text), "non-alpha"
(only letters are sent as a sequence of key events, other characters are sent
as text) and "never" (every text is sent as a sequence of key events).
Default is "always".
.TP .TP
.BI "\-\-push\-target " path .BI "\-\-push\-target " path
Set the target directory for pushing files to the device by drag & drop. It is passed as\-is to "adb push". Set the target directory for pushing files to the device by drag & drop. It is passed as\-is to "adb push".
@@ -116,6 +106,29 @@ Print the version of scrcpy.
.BI \-\-window\-title " text .BI \-\-window\-title " text
Set a custom window title. Set a custom window title.
.TP
.BI \-\-window\-x " value
Set the initial window horizontal position.
Default is -1 (automatic).\n
.TP
.BI \-\-window\-y " value
Set the initial window vertical position.
Default is -1 (automatic).\n
.TP
.BI \-\-window\-width " value
Set the initial window width.
Default is 0 (automatic).\n
.TP
.BI \-\-window\-height " value
Set the initial window height.
Default is 0 (automatic).\n
.SH SHORTCUTS .SH SHORTCUTS

View File

@@ -5,7 +5,7 @@
#define MAP(FROM, TO) case FROM: *to = TO; return true #define MAP(FROM, TO) case FROM: *to = TO; return true
#define FAIL default: return false #define FAIL default: return false
bool static bool
convert_keycode_action(SDL_EventType from, enum android_keyevent_action *to) { convert_keycode_action(SDL_EventType from, enum android_keyevent_action *to) {
switch (from) { switch (from) {
MAP(SDL_KEYDOWN, AKEY_EVENT_ACTION_DOWN); MAP(SDL_KEYDOWN, AKEY_EVENT_ACTION_DOWN);
@@ -33,7 +33,7 @@ autocomplete_metastate(enum android_metastate metastate) {
return metastate; return metastate;
} }
enum android_metastate static enum android_metastate
convert_meta_state(SDL_Keymod mod) { convert_meta_state(SDL_Keymod mod) {
enum android_metastate metastate = 0; enum android_metastate metastate = 0;
if (mod & KMOD_LSHIFT) { if (mod & KMOD_LSHIFT) {
@@ -74,9 +74,8 @@ convert_meta_state(SDL_Keymod mod) {
return autocomplete_metastate(metastate); return autocomplete_metastate(metastate);
} }
bool static bool
convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod, convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod) {
enum text_events_pref pref) {
switch (from) { switch (from) {
MAP(SDLK_RETURN, AKEYCODE_ENTER); MAP(SDLK_RETURN, AKEYCODE_ENTER);
MAP(SDLK_KP_ENTER, AKEYCODE_NUMPAD_ENTER); MAP(SDLK_KP_ENTER, AKEYCODE_NUMPAD_ENTER);
@@ -93,16 +92,6 @@ convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod,
MAP(SDLK_DOWN, AKEYCODE_DPAD_DOWN); MAP(SDLK_DOWN, AKEYCODE_DPAD_DOWN);
MAP(SDLK_UP, AKEYCODE_DPAD_UP); MAP(SDLK_UP, AKEYCODE_DPAD_UP);
} }
if (pref == PREFER_TEXT_EVENTS_ALWAYS) {
// never forward key events
return false;
}
// forward all supported key events
SDL_assert(pref == PREFER_TEXT_EVENTS_NEVER ||
pref == PREFER_TEXT_EVENTS_NON_ALPHA);
if (mod & (KMOD_LALT | KMOD_RALT | KMOD_LGUI | KMOD_RGUI)) { if (mod & (KMOD_LALT | KMOD_RALT | KMOD_LGUI | KMOD_RGUI)) {
return false; return false;
} }
@@ -139,7 +128,7 @@ convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod,
} }
} }
enum android_motionevent_buttons static enum android_motionevent_buttons
convert_mouse_buttons(uint32_t state) { convert_mouse_buttons(uint32_t state) {
enum android_motionevent_buttons buttons = 0; enum android_motionevent_buttons buttons = 0;
if (state & SDL_BUTTON_LMASK) { if (state & SDL_BUTTON_LMASK) {
@@ -161,6 +150,24 @@ convert_mouse_buttons(uint32_t state) {
} }
bool bool
convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to) {
to->type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
if (!convert_keycode_action(from->type, &to->inject_keycode.action)) {
return false;
}
uint16_t mod = from->keysym.mod;
if (!convert_keycode(from->keysym.sym, &to->inject_keycode.keycode, mod)) {
return false;
}
to->inject_keycode.metastate = convert_meta_state(mod);
return true;
}
static bool
convert_mouse_action(SDL_EventType from, enum android_motionevent_action *to) { convert_mouse_action(SDL_EventType from, enum android_motionevent_action *to) {
switch (from) { switch (from) {
MAP(SDL_MOUSEBUTTONDOWN, AMOTION_EVENT_ACTION_DOWN); MAP(SDL_MOUSEBUTTONDOWN, AMOTION_EVENT_ACTION_DOWN);
@@ -170,6 +177,41 @@ convert_mouse_action(SDL_EventType from, enum android_motionevent_action *to) {
} }
bool bool
convert_mouse_button(const SDL_MouseButtonEvent *from, struct size screen_size,
struct control_msg *to) {
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
if (!convert_mouse_action(from->type, &to->inject_touch_event.action)) {
return false;
}
to->inject_touch_event.pointer_id = POINTER_ID_MOUSE;
to->inject_touch_event.position.screen_size = screen_size;
to->inject_touch_event.position.point.x = from->x;
to->inject_touch_event.position.point.y = from->y;
to->inject_touch_event.pressure = 1.f;
to->inject_touch_event.buttons =
convert_mouse_buttons(SDL_BUTTON(from->button));
return true;
}
bool
convert_mouse_motion(const SDL_MouseMotionEvent *from, struct size screen_size,
struct control_msg *to) {
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
to->inject_touch_event.action = AMOTION_EVENT_ACTION_MOVE;
to->inject_touch_event.pointer_id = POINTER_ID_MOUSE;
to->inject_touch_event.position.screen_size = screen_size;
to->inject_touch_event.position.point.x = from->x;
to->inject_touch_event.position.point.y = from->y;
to->inject_touch_event.pressure = 1.f;
to->inject_touch_event.buttons = convert_mouse_buttons(from->state);
return true;
}
static bool
convert_touch_action(SDL_EventType from, enum android_motionevent_action *to) { convert_touch_action(SDL_EventType from, enum android_motionevent_action *to) {
switch (from) { switch (from) {
MAP(SDL_FINGERMOTION, AMOTION_EVENT_ACTION_MOVE); MAP(SDL_FINGERMOTION, AMOTION_EVENT_ACTION_MOVE);
@@ -178,3 +220,39 @@ convert_touch_action(SDL_EventType from, enum android_motionevent_action *to) {
FAIL; FAIL;
} }
} }
bool
convert_touch(const SDL_TouchFingerEvent *from, struct size screen_size,
struct control_msg *to) {
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
if (!convert_touch_action(from->type, &to->inject_touch_event.action)) {
return false;
}
to->inject_touch_event.pointer_id = from->fingerId;
to->inject_touch_event.position.screen_size = screen_size;
// SDL touch event coordinates are normalized in the range [0; 1]
to->inject_touch_event.position.point.x = from->x * screen_size.width;
to->inject_touch_event.position.point.y = from->y * screen_size.height;
to->inject_touch_event.pressure = from->pressure;
to->inject_touch_event.buttons = 0;
return true;
}
bool
convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct position position,
struct control_msg *to) {
to->type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT;
to->inject_scroll_event.position = position;
int mul = from->direction == SDL_MOUSEWHEEL_NORMAL ? 1 : -1;
// SDL behavior seems inconsistent between horizontal and vertical scrolling
// so reverse the horizontal
// <https://wiki.libsdl.org/SDL_MouseWheelEvent#Remarks>
to->inject_scroll_event.hscroll = -mul * from->x;
to->inject_scroll_event.vscroll = mul * from->y;
return true;
}

View File

@@ -2,30 +2,41 @@
#define CONVERT_H #define CONVERT_H
#include <stdbool.h> #include <stdbool.h>
#include <SDL2/SDL_assert.h>
#include <SDL2/SDL_events.h> #include <SDL2/SDL_events.h>
#include "config.h" #include "config.h"
#include "control_msg.h" #include "control_msg.h"
#include "input_manager.h"
struct complete_mouse_motion_event {
SDL_MouseMotionEvent *mouse_motion_event;
struct size screen_size;
};
struct complete_mouse_wheel_event {
SDL_MouseWheelEvent *mouse_wheel_event;
struct point position;
};
bool bool
convert_keycode_action(SDL_EventType from, enum android_keyevent_action *to); convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to);
enum android_metastate
convert_meta_state(SDL_Keymod mod);
bool bool
convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod, convert_mouse_button(const SDL_MouseButtonEvent *from, struct size screen_size,
enum text_events_pref pref); struct control_msg *to);
enum android_motionevent_buttons // the video size may be different from the real device size, so we need the
convert_mouse_buttons(uint32_t state); // size to which the absolute position apply, to scale it accordingly
bool
convert_mouse_motion(const SDL_MouseMotionEvent *from, struct size screen_size,
struct control_msg *to);
bool bool
convert_mouse_action(SDL_EventType from, enum android_motionevent_action *to); convert_touch(const SDL_TouchFingerEvent *from, struct size screen_size,
struct control_msg *to);
// on Android, a scroll event requires the current mouse position
bool bool
convert_touch_action(SDL_EventType from, enum android_motionevent_action *to); convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct position position,
struct control_msg *to);
#endif #endif

View File

@@ -212,22 +212,14 @@ clipboard_paste(struct controller *controller) {
} }
void void
input_manager_process_text_input(struct input_manager *im, input_manager_process_text_input(struct input_manager *input_manager,
const SDL_TextInputEvent *event) { const SDL_TextInputEvent *event) {
if (im->text_events_pref == PREFER_TEXT_EVENTS_NEVER) { char c = event->text[0];
// ignore all text events (key events will be injected instead) if (isalpha(c) || c == ' ') {
SDL_assert(event->text[1] == '\0');
// letters and space are handled as raw key event
return; return;
} }
if (im->text_events_pref == PREFER_TEXT_EVENTS_NON_ALPHA) {
char c = event->text[0];
if (isalpha(c) || c == ' ') {
SDL_assert(event->text[1] == '\0');
// letters and space are handled as raw key event
return;
}
}
struct control_msg msg; struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_INJECT_TEXT; msg.type = CONTROL_MSG_TYPE_INJECT_TEXT;
msg.inject_text.text = SDL_strdup(event->text); msg.inject_text.text = SDL_strdup(event->text);
@@ -235,34 +227,14 @@ input_manager_process_text_input(struct input_manager *im,
LOGW("Could not strdup input text"); LOGW("Could not strdup input text");
return; return;
} }
if (!controller_push_msg(im->controller, &msg)) { if (!controller_push_msg(input_manager->controller, &msg)) {
SDL_free(msg.inject_text.text); SDL_free(msg.inject_text.text);
LOGW("Could not request 'inject text'"); LOGW("Could not request 'inject text'");
} }
} }
static bool
convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to,
enum text_events_pref pref) {
to->type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
if (!convert_keycode_action(from->type, &to->inject_keycode.action)) {
return false;
}
uint16_t mod = from->keysym.mod;
if (!convert_keycode(from->keysym.sym, &to->inject_keycode.keycode, mod,
pref)) {
return false;
}
to->inject_keycode.metastate = convert_meta_state(mod);
return true;
}
void void
input_manager_process_key(struct input_manager *im, input_manager_process_key(struct input_manager *input_manager,
const SDL_KeyboardEvent *event, const SDL_KeyboardEvent *event,
bool control) { bool control) {
// control: indicates the state of the command-line option --no-control // control: indicates the state of the command-line option --no-control
@@ -289,7 +261,7 @@ input_manager_process_key(struct input_manager *im,
return; return;
} }
struct controller *controller = im->controller; struct controller *controller = input_manager->controller;
// capture all Ctrl events // capture all Ctrl events
if (ctrl || cmd) { if (ctrl || cmd) {
@@ -364,23 +336,23 @@ input_manager_process_key(struct input_manager *im,
return; return;
case SDLK_f: case SDLK_f:
if (!shift && cmd && !repeat && down) { if (!shift && cmd && !repeat && down) {
screen_switch_fullscreen(im->screen); screen_switch_fullscreen(input_manager->screen);
} }
return; return;
case SDLK_x: case SDLK_x:
if (!shift && cmd && !repeat && down) { if (!shift && cmd && !repeat && down) {
screen_resize_to_fit(im->screen); screen_resize_to_fit(input_manager->screen);
} }
return; return;
case SDLK_g: case SDLK_g:
if (!shift && cmd && !repeat && down) { if (!shift && cmd && !repeat && down) {
screen_resize_to_pixel_perfect(im->screen); screen_resize_to_pixel_perfect(input_manager->screen);
} }
return; return;
case SDLK_i: case SDLK_i:
if (!shift && cmd && !repeat && down) { if (!shift && cmd && !repeat && down) {
struct fps_counter *fps_counter = struct fps_counter *fps_counter =
im->video_buffer->fps_counter; input_manager->video_buffer->fps_counter;
switch_fps_counter_state(fps_counter); switch_fps_counter_state(fps_counter);
} }
return; return;
@@ -403,30 +375,15 @@ input_manager_process_key(struct input_manager *im,
} }
struct control_msg msg; struct control_msg msg;
if (convert_input_key(event, &msg, im->text_events_pref)) { if (convert_input_key(event, &msg)) {
if (!controller_push_msg(controller, &msg)) { if (!controller_push_msg(controller, &msg)) {
LOGW("Could not request 'inject keycode'"); LOGW("Could not request 'inject keycode'");
} }
} }
} }
static bool
convert_mouse_motion(const SDL_MouseMotionEvent *from, struct screen *screen,
struct control_msg *to) {
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
to->inject_touch_event.action = AMOTION_EVENT_ACTION_MOVE;
to->inject_touch_event.pointer_id = POINTER_ID_MOUSE;
to->inject_touch_event.position.screen_size = screen->frame_size;
to->inject_touch_event.position.point.x = from->x;
to->inject_touch_event.position.point.y = from->y;
to->inject_touch_event.pressure = 1.f;
to->inject_touch_event.buttons = convert_mouse_buttons(from->state);
return true;
}
void void
input_manager_process_mouse_motion(struct input_manager *im, input_manager_process_mouse_motion(struct input_manager *input_manager,
const SDL_MouseMotionEvent *event) { const SDL_MouseMotionEvent *event) {
if (!event->state) { if (!event->state) {
// do not send motion events when no button is pressed // do not send motion events when no button is pressed
@@ -437,74 +394,33 @@ input_manager_process_mouse_motion(struct input_manager *im,
return; return;
} }
struct control_msg msg; struct control_msg msg;
if (convert_mouse_motion(event, im->screen, &msg)) { if (convert_mouse_motion(event, input_manager->screen->frame_size, &msg)) {
if (!controller_push_msg(im->controller, &msg)) { if (!controller_push_msg(input_manager->controller, &msg)) {
LOGW("Could not request 'inject mouse motion event'"); LOGW("Could not request 'inject mouse motion event'");
} }
} }
} }
static bool
convert_touch(const SDL_TouchFingerEvent *from, struct screen *screen,
struct control_msg *to) {
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
if (!convert_touch_action(from->type, &to->inject_touch_event.action)) {
return false;
}
struct size frame_size = screen->frame_size;
to->inject_touch_event.pointer_id = from->fingerId;
to->inject_touch_event.position.screen_size = frame_size;
// SDL touch event coordinates are normalized in the range [0; 1]
to->inject_touch_event.position.point.x = from->x * frame_size.width;
to->inject_touch_event.position.point.y = from->y * frame_size.height;
to->inject_touch_event.pressure = from->pressure;
to->inject_touch_event.buttons = 0;
return true;
}
void void
input_manager_process_touch(struct input_manager *im, input_manager_process_touch(struct input_manager *input_manager,
const SDL_TouchFingerEvent *event) { const SDL_TouchFingerEvent *event) {
struct control_msg msg; struct control_msg msg;
if (convert_touch(event, im->screen, &msg)) { if (convert_touch(event, input_manager->screen->frame_size, &msg)) {
if (!controller_push_msg(im->controller, &msg)) { if (!controller_push_msg(input_manager->controller, &msg)) {
LOGW("Could not request 'inject touch event'"); LOGW("Could not request 'inject touch event'");
} }
} }
} }
static bool static bool
is_outside_device_screen(struct input_manager *im, int x, int y) is_outside_device_screen(struct input_manager *input_manager, int x, int y)
{ {
return x < 0 || x >= im->screen->frame_size.width || return x < 0 || x >= input_manager->screen->frame_size.width ||
y < 0 || y >= im->screen->frame_size.height; y < 0 || y >= input_manager->screen->frame_size.height;
}
static bool
convert_mouse_button(const SDL_MouseButtonEvent *from, struct screen *screen,
struct control_msg *to) {
to->type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
if (!convert_mouse_action(from->type, &to->inject_touch_event.action)) {
return false;
}
to->inject_touch_event.pointer_id = POINTER_ID_MOUSE;
to->inject_touch_event.position.screen_size = screen->frame_size;
to->inject_touch_event.position.point.x = from->x;
to->inject_touch_event.position.point.y = from->y;
to->inject_touch_event.pressure = 1.f;
to->inject_touch_event.buttons =
convert_mouse_buttons(SDL_BUTTON(from->button));
return true;
} }
void void
input_manager_process_mouse_button(struct input_manager *im, input_manager_process_mouse_button(struct input_manager *input_manager,
const SDL_MouseButtonEvent *event, const SDL_MouseButtonEvent *event,
bool control) { bool control) {
if (event->which == SDL_TOUCH_MOUSEID) { if (event->which == SDL_TOUCH_MOUSEID) {
@@ -513,19 +429,19 @@ input_manager_process_mouse_button(struct input_manager *im,
} }
if (event->type == SDL_MOUSEBUTTONDOWN) { if (event->type == SDL_MOUSEBUTTONDOWN) {
if (control && event->button == SDL_BUTTON_RIGHT) { if (control && event->button == SDL_BUTTON_RIGHT) {
press_back_or_turn_screen_on(im->controller); press_back_or_turn_screen_on(input_manager->controller);
return; return;
} }
if (control && event->button == SDL_BUTTON_MIDDLE) { if (control && event->button == SDL_BUTTON_MIDDLE) {
action_home(im->controller, ACTION_DOWN | ACTION_UP); action_home(input_manager->controller, ACTION_DOWN | ACTION_UP);
return; return;
} }
// double-click on black borders resize to fit the device screen // double-click on black borders resize to fit the device screen
if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) { if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) {
bool outside = bool outside =
is_outside_device_screen(im, event->x, event->y); is_outside_device_screen(input_manager, event->x, event->y);
if (outside) { if (outside) {
screen_resize_to_fit(im->screen); screen_resize_to_fit(input_manager->screen);
return; return;
} }
} }
@@ -537,41 +453,23 @@ input_manager_process_mouse_button(struct input_manager *im,
} }
struct control_msg msg; struct control_msg msg;
if (convert_mouse_button(event, im->screen, &msg)) { if (convert_mouse_button(event, input_manager->screen->frame_size, &msg)) {
if (!controller_push_msg(im->controller, &msg)) { if (!controller_push_msg(input_manager->controller, &msg)) {
LOGW("Could not request 'inject mouse button event'"); LOGW("Could not request 'inject mouse button event'");
} }
} }
} }
static bool
convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct screen *screen,
struct control_msg *to) {
struct position position = {
.screen_size = screen->frame_size,
.point = get_mouse_point(screen),
};
to->type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT;
to->inject_scroll_event.position = position;
int mul = from->direction == SDL_MOUSEWHEEL_NORMAL ? 1 : -1;
// SDL behavior seems inconsistent between horizontal and vertical scrolling
// so reverse the horizontal
// <https://wiki.libsdl.org/SDL_MouseWheelEvent#Remarks>
to->inject_scroll_event.hscroll = -mul * from->x;
to->inject_scroll_event.vscroll = mul * from->y;
return true;
}
void void
input_manager_process_mouse_wheel(struct input_manager *im, input_manager_process_mouse_wheel(struct input_manager *input_manager,
const SDL_MouseWheelEvent *event) { const SDL_MouseWheelEvent *event) {
struct position position = {
.screen_size = input_manager->screen->frame_size,
.point = get_mouse_point(input_manager->screen),
};
struct control_msg msg; struct control_msg msg;
if (convert_mouse_wheel(event, im->screen, &msg)) { if (convert_mouse_wheel(event, position, &msg)) {
if (!controller_push_msg(im->controller, &msg)) { if (!controller_push_msg(input_manager->controller, &msg)) {
LOGW("Could not request 'inject mouse wheel event'"); LOGW("Could not request 'inject mouse wheel event'");
} }
} }

View File

@@ -10,43 +10,36 @@
#include "video_buffer.h" #include "video_buffer.h"
#include "screen.h" #include "screen.h"
enum text_events_pref {
PREFER_TEXT_EVENTS_ALWAYS,
PREFER_TEXT_EVENTS_NON_ALPHA,
PREFER_TEXT_EVENTS_NEVER,
};
struct input_manager { struct input_manager {
struct controller *controller; struct controller *controller;
struct video_buffer *video_buffer; struct video_buffer *video_buffer;
struct screen *screen; struct screen *screen;
enum text_events_pref text_events_pref;
}; };
void void
input_manager_process_text_input(struct input_manager *im, input_manager_process_text_input(struct input_manager *input_manager,
const SDL_TextInputEvent *event); const SDL_TextInputEvent *event);
void void
input_manager_process_key(struct input_manager *im, input_manager_process_key(struct input_manager *input_manager,
const SDL_KeyboardEvent *event, const SDL_KeyboardEvent *event,
bool control); bool control);
void void
input_manager_process_mouse_motion(struct input_manager *im, input_manager_process_mouse_motion(struct input_manager *input_manager,
const SDL_MouseMotionEvent *event); const SDL_MouseMotionEvent *event);
void void
input_manager_process_touch(struct input_manager *im, input_manager_process_touch(struct input_manager *input_manager,
const SDL_TouchFingerEvent *event); const SDL_TouchFingerEvent *event);
void void
input_manager_process_mouse_button(struct input_manager *im, input_manager_process_mouse_button(struct input_manager *input_manager,
const SDL_MouseButtonEvent *event, const SDL_MouseButtonEvent *event,
bool control); bool control);
void void
input_manager_process_mouse_wheel(struct input_manager *im, input_manager_process_mouse_wheel(struct input_manager *input_manager,
const SDL_MouseWheelEvent *event); const SDL_MouseWheelEvent *event);
#endif #endif

View File

@@ -11,13 +11,32 @@
#include "config.h" #include "config.h"
#include "compat.h" #include "compat.h"
#include "log.h" #include "log.h"
#include "input_manager.h"
#include "recorder.h" #include "recorder.h"
struct args { struct args {
struct scrcpy_options opts; const char *serial;
const char *crop;
const char *record_filename;
const char *window_title;
const char *push_target;
enum recorder_format record_format;
bool fullscreen;
bool no_control;
bool no_display;
bool help; bool help;
bool version; bool version;
bool show_touches;
uint16_t port;
uint16_t max_size;
uint32_t bit_rate;
int16_t window_x;
int16_t window_y;
uint16_t window_width;
uint16_t window_height;
bool always_on_top;
bool turn_screen_off;
bool render_expired_frames;
bool window_borderless;
}; };
static void usage(const char *arg0) { static void usage(const char *arg0) {
@@ -68,18 +87,6 @@ static void usage(const char *arg0) {
" Set the TCP port the client listens on.\n" " Set the TCP port the client listens on.\n"
" Default is %d.\n" " Default is %d.\n"
"\n" "\n"
" --prefer-text-events mode\n"
" Configure how key/text events are forwarded to the Android\n"
" device.\n"
" Possible values are:\n"
" always:\n"
" Every text is sent as text. (default)\n"
" non-alpha:\n"
" Only letters are sent as a sequence of key events, other\n"
" characters are sent as text.\n"
" never:\n"
" Every text is sent as a sequence of key events.\n"
"\n"
" --push-target path\n" " --push-target path\n"
" Set the target directory for pushing files to the device by\n" " Set the target directory for pushing files to the device by\n"
" drag & drop. It is passed as-is to \"adb push\".\n" " drag & drop. It is passed as-is to \"adb push\".\n"
@@ -113,9 +120,28 @@ static void usage(const char *arg0) {
" -v, --version\n" " -v, --version\n"
" Print the version of scrcpy.\n" " Print the version of scrcpy.\n"
"\n" "\n"
" --window_borderless\n"
" Disable window decorations (display borderless window).\n"
"\n"
" --window-title text\n" " --window-title text\n"
" Set a custom window title.\n" " Set a custom window title.\n"
"\n" "\n"
" --window-x value\n"
" Set the initial window horizontal position.\n"
" Default is -1 (automatic).\n"
"\n"
" --window-y value\n"
" Set the initial window vertical position.\n"
" Default is -1 (automatic).\n"
"\n"
" --window-width value\n"
" Set the initial window width.\n"
" Default is -1 (automatic).\n"
"\n"
" --window-height value\n"
" Set the initial window width.\n"
" Default is -1 (automatic).\n"
"\n"
"Shortcuts:\n" "Shortcuts:\n"
"\n" "\n"
" " CTRL_OR_CMD "+f\n" " " CTRL_OR_CMD "+f\n"
@@ -256,6 +282,48 @@ parse_max_size(char *optarg, uint16_t *max_size) {
return true; return true;
} }
static bool
parse_window_position(char *optarg, int16_t *position) {
char *endptr;
if (*optarg == '\0') {
LOGE("Window position parameter is empty");
return false;
}
long value = strtol(optarg, &endptr, 0);
if (*endptr != '\0') {
LOGE("Invalid window position: %s", optarg);
return false;
}
if (value < -1 || value > 0x7fff) {
LOGE("Window position must be between -1 and 32767: %ld", value);
return false;
}
*position = (int16_t) value;
return true;
}
static bool
parse_window_dimension(char *optarg, uint16_t *dimension) {
char *endptr;
if (*optarg == '\0') {
LOGE("Window dimension parameter is empty");
return false;
}
long value = strtol(optarg, &endptr, 0);
if (*endptr != '\0') {
LOGE("Invalid window dimension: %s", optarg);
return false;
}
if (value & ~0xffff) {
LOGE("Window position must be between 0 and 65535: %ld", value);
return false;
}
*dimension = (uint16_t) value;
return true;
}
static bool static bool
parse_port(char *optarg, uint16_t *port) { parse_port(char *optarg, uint16_t *port) {
char *endptr; char *endptr;
@@ -307,33 +375,14 @@ guess_record_format(const char *filename) {
return 0; return 0;
} }
static bool
parse_prefer_text_events(const char *optarg,
enum text_events_pref *pref) {
if (!strcmp(optarg, "always")) {
*pref = PREFER_TEXT_EVENTS_ALWAYS;
return true;
}
if (!strcmp(optarg, "non-alpha")) {
*pref = PREFER_TEXT_EVENTS_NON_ALPHA;
return true;
}
if (!strcmp(optarg, "never")) {
*pref = PREFER_TEXT_EVENTS_NEVER;
return true;
}
LOGE("Unsupported text events preference: %s"
"(expected 'always', 'non-alpha' or 'never')", optarg);
return false;
}
#define OPT_RENDER_EXPIRED_FRAMES 1000 #define OPT_RENDER_EXPIRED_FRAMES 1000
#define OPT_WINDOW_TITLE 1001 #define OPT_PUSH_TARGET 1001
#define OPT_PUSH_TARGET 1002 #define OPT_WINDOW_TITLE 1002
#define OPT_PREFER_TEXT_EVENTS 1003 #define OPT_WINDOW_X 1003
#define OPT_WINDOW_Y 1004
#define OPT_WINDOW_WIDTH 1005
#define OPT_WINDOW_HEIGHT 1006
#define OPT_WINDOW_BORDERLESS 1007
static bool static bool
parse_args(struct args *args, int argc, char *argv[]) { parse_args(struct args *args, int argc, char *argv[]) {
@@ -346,6 +395,8 @@ parse_args(struct args *args, int argc, char *argv[]) {
{"max-size", required_argument, NULL, 'm'}, {"max-size", required_argument, NULL, 'm'},
{"no-control", no_argument, NULL, 'n'}, {"no-control", no_argument, NULL, 'n'},
{"no-display", no_argument, NULL, 'N'}, {"no-display", no_argument, NULL, 'N'},
{"window-borderless", no_argument, NULL,
OPT_WINDOW_BORDERLESS},
{"port", required_argument, NULL, 'p'}, {"port", required_argument, NULL, 'p'},
{"push-target", required_argument, NULL, {"push-target", required_argument, NULL,
OPT_PUSH_TARGET}, OPT_PUSH_TARGET},
@@ -356,33 +407,31 @@ parse_args(struct args *args, int argc, char *argv[]) {
{"serial", required_argument, NULL, 's'}, {"serial", required_argument, NULL, 's'},
{"show-touches", no_argument, NULL, 't'}, {"show-touches", no_argument, NULL, 't'},
{"turn-screen-off", no_argument, NULL, 'S'}, {"turn-screen-off", no_argument, NULL, 'S'},
{"prefer-text-events", required_argument, NULL,
OPT_PREFER_TEXT_EVENTS},
{"version", no_argument, NULL, 'v'}, {"version", no_argument, NULL, 'v'},
{"window-title", required_argument, NULL, {"window-title", required_argument, NULL, OPT_WINDOW_TITLE},
OPT_WINDOW_TITLE}, {"window-x", required_argument, NULL, OPT_WINDOW_X},
{"window-y", required_argument, NULL, OPT_WINDOW_Y},
{"window-width", required_argument, NULL, OPT_WINDOW_WIDTH},
{"window-height", required_argument, NULL, OPT_WINDOW_HEIGHT},
{NULL, 0, NULL, 0 }, {NULL, 0, NULL, 0 },
}; };
struct scrcpy_options *opts = &args->opts;
int c; int c;
while ((c = getopt_long(argc, argv, "b:c:fF:hm:nNp:r:s:StTv", long_options, while ((c = getopt_long(argc, argv, "b:c:fF:hm:nNp:r:s:StTv", long_options,
NULL)) != -1) { NULL)) != -1) {
switch (c) { switch (c) {
case 'b': case 'b':
if (!parse_bit_rate(optarg, &opts->bit_rate)) { if (!parse_bit_rate(optarg, &args->bit_rate)) {
return false; return false;
} }
break; break;
case 'c': case 'c':
opts->crop = optarg; args->crop = optarg;
break; break;
case 'f': case 'f':
opts->fullscreen = true; args->fullscreen = true;
break; break;
case 'F': case 'F':
if (!parse_record_format(optarg, &opts->record_format)) { if (!parse_record_format(optarg, &args->record_format)) {
return false; return false;
} }
break; break;
@@ -390,66 +439,83 @@ parse_args(struct args *args, int argc, char *argv[]) {
args->help = true; args->help = true;
break; break;
case 'm': case 'm':
if (!parse_max_size(optarg, &opts->max_size)) { if (!parse_max_size(optarg, &args->max_size)) {
return false; return false;
} }
break; break;
case 'n': case 'n':
opts->control = false; args->no_control = true;
break; break;
case 'N': case 'N':
opts->display = false; args->no_display = true;
break; break;
case 'p': case 'p':
if (!parse_port(optarg, &opts->port)) { if (!parse_port(optarg, &args->port)) {
return false; return false;
} }
break; break;
case 'r': case 'r':
opts->record_filename = optarg; args->record_filename = optarg;
break; break;
case 's': case 's':
opts->serial = optarg; args->serial = optarg;
break; break;
case 'S': case 'S':
opts->turn_screen_off = true; args->turn_screen_off = true;
break; break;
case 't': case 't':
opts->show_touches = true; args->show_touches = true;
break; break;
case 'T': case 'T':
opts->always_on_top = true; args->always_on_top = true;
break; break;
case 'v': case 'v':
args->version = true; args->version = true;
break; break;
case OPT_RENDER_EXPIRED_FRAMES: case OPT_RENDER_EXPIRED_FRAMES:
opts->render_expired_frames = true; args->render_expired_frames = true;
break; break;
case OPT_WINDOW_TITLE: case OPT_WINDOW_TITLE:
opts->window_title = optarg; args->window_title = optarg;
break; break;
case OPT_PUSH_TARGET: case OPT_WINDOW_X:
opts->push_target = optarg; if (!parse_window_position(optarg, &args->window_x)) {
break;
case OPT_PREFER_TEXT_EVENTS:
if (!parse_prefer_text_events(optarg,
&opts->text_events_pref)) {
return false; return false;
} }
break; break;
case OPT_WINDOW_Y:
if (!parse_window_position(optarg, &args->window_y)) {
return false;
}
break;
case OPT_WINDOW_WIDTH:
if (!parse_window_dimension(optarg, &args->window_width)) {
return false;
}
break;
case OPT_WINDOW_HEIGHT:
if (!parse_window_dimension(optarg, &args->window_height)) {
return false;
}
break;
case OPT_WINDOW_BORDERLESS:
args->window_borderless = true;
break;
case OPT_PUSH_TARGET:
args->push_target = optarg;
break;
default: default:
// getopt prints the error message on stderr // getopt prints the error message on stderr
return false; return false;
} }
} }
if (!opts->display && !opts->record_filename) { if (args->no_display && !args->record_filename) {
LOGE("-N/--no-display requires screen recording (-r/--record)"); LOGE("-N/--no-display requires screen recording (-r/--record)");
return false; return false;
} }
if (!opts->display && opts->fullscreen) { if (args->no_display && args->fullscreen) {
LOGE("-f/--fullscreen-window is incompatible with -N/--no-display"); LOGE("-f/--fullscreen-window is incompatible with -N/--no-display");
return false; return false;
} }
@@ -460,21 +526,21 @@ parse_args(struct args *args, int argc, char *argv[]) {
return false; return false;
} }
if (opts->record_format && !opts->record_filename) { if (args->record_format && !args->record_filename) {
LOGE("Record format specified without recording"); LOGE("Record format specified without recording");
return false; return false;
} }
if (opts->record_filename && !opts->record_format) { if (args->record_filename && !args->record_format) {
opts->record_format = guess_record_format(opts->record_filename); args->record_format = guess_record_format(args->record_filename);
if (!opts->record_format) { if (!args->record_format) {
LOGE("No format specified for \"%s\" (try with -F mkv)", LOGE("No format specified for \"%s\" (try with -F mkv)",
opts->record_filename); args->record_filename);
return false; return false;
} }
} }
if (!opts->control && opts->turn_screen_off) { if (args->no_control && args->turn_screen_off) {
LOGE("Could not request to turn screen off if control is disabled"); LOGE("Could not request to turn screen off if control is disabled");
return false; return false;
} }
@@ -491,11 +557,29 @@ main(int argc, char *argv[]) {
setbuf(stderr, NULL); setbuf(stderr, NULL);
#endif #endif
struct args args = { struct args args = {
.opts = SCRCPY_OPTIONS_DEFAULT, .serial = NULL,
.crop = NULL,
.record_filename = NULL,
.window_title = NULL,
.push_target = NULL,
.record_format = 0,
.help = false, .help = false,
.version = false, .version = false,
.show_touches = false,
.port = DEFAULT_LOCAL_PORT,
.max_size = DEFAULT_MAX_SIZE,
.bit_rate = DEFAULT_BIT_RATE,
.window_x = -1,
.window_y = -1,
.window_width = 0,
.window_height = 0,
.always_on_top = false,
.no_control = false,
.no_display = false,
.turn_screen_off = false,
.render_expired_frames = false,
.window_borderless = false,
}; };
if (!parse_args(&args, argc, argv)) { if (!parse_args(&args, argc, argv)) {
return 1; return 1;
} }
@@ -524,7 +608,30 @@ main(int argc, char *argv[]) {
SDL_LogSetAllPriority(SDL_LOG_PRIORITY_DEBUG); SDL_LogSetAllPriority(SDL_LOG_PRIORITY_DEBUG);
#endif #endif
int res = scrcpy(&args.opts) ? 0 : 1; struct scrcpy_options options = {
.serial = args.serial,
.crop = args.crop,
.port = args.port,
.record_filename = args.record_filename,
.window_title = args.window_title,
.push_target = args.push_target,
.record_format = args.record_format,
.max_size = args.max_size,
.bit_rate = args.bit_rate,
.window_x = args.window_x,
.window_y = args.window_y,
.window_width = args.window_width,
.window_height = args.window_height,
.show_touches = args.show_touches,
.fullscreen = args.fullscreen,
.always_on_top = args.always_on_top,
.control = !args.no_control,
.display = !args.no_display,
.turn_screen_off = args.turn_screen_off,
.render_expired_frames = args.render_expired_frames,
.window_borderless = args.window_borderless,
};
int res = scrcpy(&options) ? 0 : 1;
avformat_network_deinit(); // ignore failure avformat_network_deinit(); // ignore failure

View File

@@ -11,8 +11,7 @@
#include "queue.h" #include "queue.h"
enum recorder_format { enum recorder_format {
RECORDER_FORMAT_AUTO, RECORDER_FORMAT_MP4 = 1,
RECORDER_FORMAT_MP4,
RECORDER_FORMAT_MKV, RECORDER_FORMAT_MKV,
}; };

View File

@@ -42,7 +42,6 @@ static struct input_manager input_manager = {
.controller = &controller, .controller = &controller,
.video_buffer = &video_buffer, .video_buffer = &video_buffer,
.screen = &screen, .screen = &screen,
.text_events_pref = 0, // initialized later
}; };
// init SDL and set appropriate hints // init SDL and set appropriate hints
@@ -391,7 +390,10 @@ scrcpy(const struct scrcpy_options *options) {
options->window_title ? options->window_title : device_name; options->window_title ? options->window_title : device_name;
if (!screen_init_rendering(&screen, window_title, frame_size, if (!screen_init_rendering(&screen, window_title, frame_size,
options->always_on_top)) { options->always_on_top, options->window_x,
options->window_y, options->window_width,
options->window_height,
options->window_borderless)) {
goto end; goto end;
} }
@@ -415,8 +417,6 @@ scrcpy(const struct scrcpy_options *options) {
show_touches_waited = true; show_touches_waited = true;
} }
input_manager.text_events_pref = options->text_events_pref;
ret = event_loop(options->display, options->control); ret = event_loop(options->display, options->control);
LOGD("quit..."); LOGD("quit...");

View File

@@ -3,10 +3,9 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <recorder.h>
#include "config.h" #include "config.h"
#include "input_manager.h"
#include "recorder.h"
struct scrcpy_options { struct scrcpy_options {
const char *serial; const char *serial;
@@ -15,10 +14,13 @@ struct scrcpy_options {
const char *window_title; const char *window_title;
const char *push_target; const char *push_target;
enum recorder_format record_format; enum recorder_format record_format;
enum text_events_pref text_events_pref;
uint16_t port; uint16_t port;
uint16_t max_size; uint16_t max_size;
uint32_t bit_rate; uint32_t bit_rate;
int16_t window_x;
int16_t window_y;
uint16_t window_width;
uint16_t window_height;
bool show_touches; bool show_touches;
bool fullscreen; bool fullscreen;
bool always_on_top; bool always_on_top;
@@ -26,28 +28,9 @@ struct scrcpy_options {
bool display; bool display;
bool turn_screen_off; bool turn_screen_off;
bool render_expired_frames; bool render_expired_frames;
bool window_borderless;
}; };
#define SCRCPY_OPTIONS_DEFAULT { \
.serial = NULL, \
.crop = NULL, \
.record_filename = NULL, \
.window_title = NULL, \
.push_target = NULL, \
.record_format = RECORDER_FORMAT_AUTO, \
.text_events_pref = PREFER_TEXT_EVENTS_ALWAYS, \
.port = DEFAULT_LOCAL_PORT, \
.max_size = DEFAULT_LOCAL_PORT, \
.bit_rate = DEFAULT_BIT_RATE, \
.show_touches = false, \
.fullscreen = false, \
.always_on_top = false, \
.control = true, \
.display = true, \
.turn_screen_off = false, \
.render_expired_frames = false, \
}
bool bool
scrcpy(const struct scrcpy_options *options); scrcpy(const struct scrcpy_options *options);

View File

@@ -117,9 +117,30 @@ get_optimal_window_size(const struct screen *screen, struct size frame_size) {
} }
// initially, there is no current size, so use the frame size as current size // initially, there is no current size, so use the frame size as current size
// req_width and req_height, if not 0, are the sizes requested by the user
static inline struct size static inline struct size
get_initial_optimal_size(struct size frame_size) { get_initial_optimal_size(struct size frame_size, uint16_t req_width,
return get_optimal_size(frame_size, frame_size); uint16_t req_height) {
struct size window_size;
if (!req_width && !req_height) {
window_size = get_optimal_size(frame_size, frame_size);
} else {
if (req_width) {
window_size.width = req_width;
} else {
// compute from the requested height
window_size.width = (uint32_t) req_height * frame_size.width
/ frame_size.height;
}
if (req_height) {
window_size.height = req_height;
} else {
// compute from the requested width
window_size.height = (uint32_t) req_width * frame_size.height
/ frame_size.width;
}
}
return window_size;
} }
void void
@@ -136,10 +157,13 @@ create_texture(SDL_Renderer *renderer, struct size frame_size) {
bool bool
screen_init_rendering(struct screen *screen, const char *window_title, screen_init_rendering(struct screen *screen, const char *window_title,
struct size frame_size, bool always_on_top) { struct size frame_size, bool always_on_top,
int16_t window_x, int16_t window_y, uint16_t window_width,
uint16_t window_height, bool window_borderless) {
screen->frame_size = frame_size; screen->frame_size = frame_size;
struct size window_size = get_initial_optimal_size(frame_size); struct size window_size =
get_initial_optimal_size(frame_size, window_width, window_height);
uint32_t window_flags = SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE; uint32_t window_flags = SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE;
#ifdef HIDPI_SUPPORT #ifdef HIDPI_SUPPORT
window_flags |= SDL_WINDOW_ALLOW_HIGHDPI; window_flags |= SDL_WINDOW_ALLOW_HIGHDPI;
@@ -152,9 +176,13 @@ screen_init_rendering(struct screen *screen, const char *window_title,
"(compile with SDL >= 2.0.5 to enable it)"); "(compile with SDL >= 2.0.5 to enable it)");
#endif #endif
} }
if (window_borderless) {
window_flags |= SDL_WINDOW_BORDERLESS;
}
screen->window = SDL_CreateWindow(window_title, SDL_WINDOWPOS_UNDEFINED, int x = window_x != -1 ? window_x : SDL_WINDOWPOS_UNDEFINED;
SDL_WINDOWPOS_UNDEFINED, int y = window_y != -1 ? window_y : SDL_WINDOWPOS_UNDEFINED;
screen->window = SDL_CreateWindow(window_title, x, y,
window_size.width, window_size.height, window_size.width, window_size.height,
window_flags); window_flags);
if (!screen->window) { if (!screen->window) {

View File

@@ -46,7 +46,9 @@ screen_init(struct screen *screen);
// initialize screen, create window, renderer and texture (window is hidden) // initialize screen, create window, renderer and texture (window is hidden)
bool bool
screen_init_rendering(struct screen *screen, const char *window_title, screen_init_rendering(struct screen *screen, const char *window_title,
struct size frame_size, bool always_on_top); struct size frame_size, bool always_on_top,
int16_t window_x, int16_t window_y, uint16_t window_width,
uint16_t window_height, bool window_borderless);
// show the window // show the window
void void

View File

@@ -124,11 +124,6 @@ execute_server(struct server *server, const struct server_params *params) {
"shell", "shell",
"CLASSPATH=/data/local/tmp/" SERVER_FILENAME, "CLASSPATH=/data/local/tmp/" SERVER_FILENAME,
"app_process", "app_process",
#ifdef SERVER_DEBUGGER
# define SERVER_DEBUGGER_PORT "5005"
"-agentlib:jdwp=transport=dt_socket,suspend=y,server=y,address="
SERVER_DEBUGGER_PORT,
#endif
"/", // unused "/", // unused
"com.genymobile.scrcpy.Server", "com.genymobile.scrcpy.Server",
max_size_string, max_size_string,
@@ -138,17 +133,6 @@ execute_server(struct server *server, const struct server_params *params) {
"true", // always send frame meta (packet boundaries + timestamp) "true", // always send frame meta (packet boundaries + timestamp)
params->control ? "true" : "false", params->control ? "true" : "false",
}; };
#ifdef SERVER_DEBUGGER
LOGI("Server debugger waiting for a client on device port "
SERVER_DEBUGGER_PORT "...");
// From the computer, run
// adb forward tcp:5005 tcp:5005
// Then, from Android Studio: Run > Debug > Edit configurations...
// On the left, click on '+', "Remote", with:
// Host: localhost
// Port: 5005
// Then click on "Debug"
#endif
return adb_execute(server->serial, cmd, sizeof(cmd) / sizeof(cmd[0])); return adb_execute(server->serial, cmd, sizeof(cmd) / sizeof(cmd[0]));
} }

View File

@@ -5,4 +5,3 @@ option('windows_noconsole', type: 'boolean', value: false, description: 'Disable
option('prebuilt_server', type: 'string', description: 'Path of the prebuilt server') option('prebuilt_server', type: 'string', description: 'Path of the prebuilt server')
option('portable', type: 'boolean', value: false, description: 'Use scrcpy-server from the same directory as the scrcpy executable') option('portable', type: 'boolean', value: false, description: 'Use scrcpy-server from the same directory as the scrcpy executable')
option('hidpi_support', type: 'boolean', value: true, description: 'Enable High DPI support') option('hidpi_support', type: 'boolean', value: true, description: 'Enable High DPI support')
option('server_debugger', type: 'boolean', value: false, description: 'Run a server debugger and wait for a client to be attached')