Compare commits
7 Commits
fixhidpi.4
...
mainlooper
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9da4c93582 | ||
|
|
b08a98324d | ||
|
|
c916af0984 | ||
|
|
ff061b4f30 | ||
|
|
157c60feb4 | ||
|
|
2d90e1befd | ||
|
|
0e301ddf19 |
@@ -143,7 +143,6 @@ This is useful for example to mirror only one eye of the Oculus Go:
|
||||
|
||||
```bash
|
||||
scrcpy --crop 1224:1440:0:0 # 1224x1440 at offset (0,0)
|
||||
scrcpy -c 1224:1440:0:0 # short version
|
||||
```
|
||||
|
||||
If `--max-size` is also specified, resizing is applied after cropping.
|
||||
@@ -226,7 +225,6 @@ The window of app can always be above others by:
|
||||
|
||||
```bash
|
||||
scrcpy --always-on-top
|
||||
scrcpy -T # short version
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -109,15 +109,15 @@ conf.set('DEFAULT_MAX_SIZE', '0') # 0: unlimited
|
||||
# overridden by option --bit-rate
|
||||
conf.set('DEFAULT_BIT_RATE', '8000000') # 8Mbps
|
||||
|
||||
# enable High DPI support
|
||||
conf.set('HIDPI_SUPPORT', get_option('hidpi_support'))
|
||||
|
||||
# disable console on Windows
|
||||
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'))
|
||||
|
||||
# enable a workaround for bug #15
|
||||
conf.set('HIDPI_WORKAROUND', get_option('hidpi_workaround'))
|
||||
|
||||
configure_file(configuration: conf, output: 'config.h')
|
||||
|
||||
src_dir = include_directories('src')
|
||||
|
||||
@@ -61,6 +61,13 @@ Set the TCP port the client listens on.
|
||||
|
||||
Default is 27183.
|
||||
|
||||
.TP
|
||||
.B \-\-prefer\-text
|
||||
Inject alpha characters and space as text events instead of key events.
|
||||
|
||||
This avoids issues when combining multiple keys to enter special characters,
|
||||
but breaks the expected behavior of alpha keys in games (typically WASD).
|
||||
|
||||
.TP
|
||||
.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".
|
||||
|
||||
@@ -75,7 +75,8 @@ convert_meta_state(SDL_Keymod mod) {
|
||||
}
|
||||
|
||||
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,
|
||||
bool prefer_text) {
|
||||
switch (from) {
|
||||
MAP(SDLK_RETURN, AKEYCODE_ENTER);
|
||||
MAP(SDLK_KP_ENTER, AKEYCODE_NUMPAD_ENTER);
|
||||
@@ -92,6 +93,12 @@ convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod) {
|
||||
MAP(SDLK_DOWN, AKEYCODE_DPAD_DOWN);
|
||||
MAP(SDLK_UP, AKEYCODE_DPAD_UP);
|
||||
}
|
||||
|
||||
if (prefer_text) {
|
||||
// do not forward alpha and space key events
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mod & (KMOD_LALT | KMOD_RALT | KMOD_LGUI | KMOD_RGUI)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ enum android_metastate
|
||||
convert_meta_state(SDL_Keymod mod);
|
||||
|
||||
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,
|
||||
bool prefer_text);
|
||||
|
||||
enum android_motionevent_buttons
|
||||
convert_mouse_buttons(uint32_t state);
|
||||
|
||||
@@ -214,12 +214,15 @@ clipboard_paste(struct controller *controller) {
|
||||
void
|
||||
input_manager_process_text_input(struct input_manager *im,
|
||||
const SDL_TextInputEvent *event) {
|
||||
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;
|
||||
if (!im->prefer_text) {
|
||||
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;
|
||||
msg.type = CONTROL_MSG_TYPE_INJECT_TEXT;
|
||||
msg.inject_text.text = SDL_strdup(event->text);
|
||||
@@ -234,7 +237,8 @@ input_manager_process_text_input(struct input_manager *im,
|
||||
}
|
||||
|
||||
static bool
|
||||
convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to) {
|
||||
convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to,
|
||||
bool prefer_text) {
|
||||
to->type = CONTROL_MSG_TYPE_INJECT_KEYCODE;
|
||||
|
||||
if (!convert_keycode_action(from->type, &to->inject_keycode.action)) {
|
||||
@@ -242,7 +246,8 @@ convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to) {
|
||||
}
|
||||
|
||||
uint16_t mod = from->keysym.mod;
|
||||
if (!convert_keycode(from->keysym.sym, &to->inject_keycode.keycode, mod)) {
|
||||
if (!convert_keycode(from->keysym.sym, &to->inject_keycode.keycode, mod,
|
||||
prefer_text)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -393,7 +398,7 @@ input_manager_process_key(struct input_manager *im,
|
||||
}
|
||||
|
||||
struct control_msg msg;
|
||||
if (convert_input_key(event, &msg)) {
|
||||
if (convert_input_key(event, &msg, im->prefer_text)) {
|
||||
if (!controller_push_msg(controller, &msg)) {
|
||||
LOGW("Could not request 'inject keycode'");
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ struct input_manager {
|
||||
struct controller *controller;
|
||||
struct video_buffer *video_buffer;
|
||||
struct screen *screen;
|
||||
bool prefer_text;
|
||||
};
|
||||
|
||||
void
|
||||
|
||||
142
app/src/main.c
142
app/src/main.c
@@ -14,24 +14,9 @@
|
||||
#include "recorder.h"
|
||||
|
||||
struct args {
|
||||
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;
|
||||
struct scrcpy_options opts;
|
||||
bool help;
|
||||
bool version;
|
||||
bool show_touches;
|
||||
uint16_t port;
|
||||
uint16_t max_size;
|
||||
uint32_t bit_rate;
|
||||
bool always_on_top;
|
||||
bool turn_screen_off;
|
||||
bool render_expired_frames;
|
||||
};
|
||||
|
||||
static void usage(const char *arg0) {
|
||||
@@ -82,6 +67,13 @@ static void usage(const char *arg0) {
|
||||
" Set the TCP port the client listens on.\n"
|
||||
" Default is %d.\n"
|
||||
"\n"
|
||||
" --prefer-text\n"
|
||||
" Inject alpha characters and space as text events instead of\n"
|
||||
" key events.\n"
|
||||
" This avoids issues when combining multiple keys to enter a\n"
|
||||
" special character, but breaks the expected behavior of alpha\n"
|
||||
" keys in games (typically WASD).\n"
|
||||
"\n"
|
||||
" --push-target path\n"
|
||||
" Set the target directory for pushing files to the device by\n"
|
||||
" drag & drop. It is passed as-is to \"adb push\".\n"
|
||||
@@ -312,50 +304,63 @@ guess_record_format(const char *filename) {
|
||||
#define OPT_RENDER_EXPIRED_FRAMES 1000
|
||||
#define OPT_WINDOW_TITLE 1001
|
||||
#define OPT_PUSH_TARGET 1002
|
||||
#define OPT_ALWAYS_ON_TOP 1003
|
||||
#define OPT_CROP 1004
|
||||
#define OPT_RECORD_FORMAT 1005
|
||||
#define OPT_PREFER_TEXT 1006
|
||||
|
||||
static bool
|
||||
parse_args(struct args *args, int argc, char *argv[]) {
|
||||
static const struct option long_options[] = {
|
||||
{"always-on-top", no_argument, NULL, 'T'},
|
||||
{"always-on-top", no_argument, NULL, OPT_ALWAYS_ON_TOP},
|
||||
{"bit-rate", required_argument, NULL, 'b'},
|
||||
{"crop", required_argument, NULL, 'c'},
|
||||
{"crop", required_argument, NULL, OPT_CROP},
|
||||
{"fullscreen", no_argument, NULL, 'f'},
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"max-size", required_argument, NULL, 'm'},
|
||||
{"no-control", no_argument, NULL, 'n'},
|
||||
{"no-display", no_argument, NULL, 'N'},
|
||||
{"port", required_argument, NULL, 'p'},
|
||||
{"push-target", required_argument, NULL,
|
||||
OPT_PUSH_TARGET},
|
||||
{"push-target", required_argument, NULL, OPT_PUSH_TARGET},
|
||||
{"record", required_argument, NULL, 'r'},
|
||||
{"record-format", required_argument, NULL, 'F'},
|
||||
{"record-format", required_argument, NULL, OPT_RECORD_FORMAT},
|
||||
{"render-expired-frames", no_argument, NULL,
|
||||
OPT_RENDER_EXPIRED_FRAMES},
|
||||
OPT_RENDER_EXPIRED_FRAMES},
|
||||
{"serial", required_argument, NULL, 's'},
|
||||
{"show-touches", no_argument, NULL, 't'},
|
||||
{"turn-screen-off", no_argument, NULL, 'S'},
|
||||
{"prefer-text", no_argument, NULL, OPT_PREFER_TEXT},
|
||||
{"version", no_argument, NULL, 'v'},
|
||||
{"window-title", required_argument, NULL,
|
||||
OPT_WINDOW_TITLE},
|
||||
OPT_WINDOW_TITLE},
|
||||
{NULL, 0, NULL, 0 },
|
||||
};
|
||||
|
||||
struct scrcpy_options *opts = &args->opts;
|
||||
|
||||
int c;
|
||||
while ((c = getopt_long(argc, argv, "b:c:fF:hm:nNp:r:s:StTv", long_options,
|
||||
NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'b':
|
||||
if (!parse_bit_rate(optarg, &args->bit_rate)) {
|
||||
if (!parse_bit_rate(optarg, &opts->bit_rate)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
args->crop = optarg;
|
||||
LOGW("Deprecated option -c. Use --crop instead.");
|
||||
// fall through
|
||||
case OPT_CROP:
|
||||
opts->crop = optarg;
|
||||
break;
|
||||
case 'f':
|
||||
args->fullscreen = true;
|
||||
opts->fullscreen = true;
|
||||
break;
|
||||
case 'F':
|
||||
if (!parse_record_format(optarg, &args->record_format)) {
|
||||
LOGW("Deprecated option -F. Use --record-format instead.");
|
||||
// fall through
|
||||
case OPT_RECORD_FORMAT:
|
||||
if (!parse_record_format(optarg, &opts->record_format)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
@@ -363,47 +368,53 @@ parse_args(struct args *args, int argc, char *argv[]) {
|
||||
args->help = true;
|
||||
break;
|
||||
case 'm':
|
||||
if (!parse_max_size(optarg, &args->max_size)) {
|
||||
if (!parse_max_size(optarg, &opts->max_size)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
args->no_control = true;
|
||||
opts->control = false;
|
||||
break;
|
||||
case 'N':
|
||||
args->no_display = true;
|
||||
opts->display = false;
|
||||
break;
|
||||
case 'p':
|
||||
if (!parse_port(optarg, &args->port)) {
|
||||
if (!parse_port(optarg, &opts->port)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
args->record_filename = optarg;
|
||||
opts->record_filename = optarg;
|
||||
break;
|
||||
case 's':
|
||||
args->serial = optarg;
|
||||
opts->serial = optarg;
|
||||
break;
|
||||
case 'S':
|
||||
args->turn_screen_off = true;
|
||||
opts->turn_screen_off = true;
|
||||
break;
|
||||
case 't':
|
||||
args->show_touches = true;
|
||||
opts->show_touches = true;
|
||||
break;
|
||||
case 'T':
|
||||
args->always_on_top = true;
|
||||
LOGW("Deprecated option -T. Use --always-on-top instead.");
|
||||
// fall through
|
||||
case OPT_ALWAYS_ON_TOP:
|
||||
opts->always_on_top = true;
|
||||
break;
|
||||
case 'v':
|
||||
args->version = true;
|
||||
break;
|
||||
case OPT_RENDER_EXPIRED_FRAMES:
|
||||
args->render_expired_frames = true;
|
||||
opts->render_expired_frames = true;
|
||||
break;
|
||||
case OPT_WINDOW_TITLE:
|
||||
args->window_title = optarg;
|
||||
opts->window_title = optarg;
|
||||
break;
|
||||
case OPT_PUSH_TARGET:
|
||||
args->push_target = optarg;
|
||||
opts->push_target = optarg;
|
||||
break;
|
||||
case OPT_PREFER_TEXT:
|
||||
opts->prefer_text = true;
|
||||
break;
|
||||
default:
|
||||
// getopt prints the error message on stderr
|
||||
@@ -411,12 +422,12 @@ parse_args(struct args *args, int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (args->no_display && !args->record_filename) {
|
||||
if (!opts->display && !opts->record_filename) {
|
||||
LOGE("-N/--no-display requires screen recording (-r/--record)");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args->no_display && args->fullscreen) {
|
||||
if (!opts->display && opts->fullscreen) {
|
||||
LOGE("-f/--fullscreen-window is incompatible with -N/--no-display");
|
||||
return false;
|
||||
}
|
||||
@@ -427,21 +438,21 @@ parse_args(struct args *args, int argc, char *argv[]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args->record_format && !args->record_filename) {
|
||||
if (opts->record_format && !opts->record_filename) {
|
||||
LOGE("Record format specified without recording");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args->record_filename && !args->record_format) {
|
||||
args->record_format = guess_record_format(args->record_filename);
|
||||
if (!args->record_format) {
|
||||
if (opts->record_filename && !opts->record_format) {
|
||||
opts->record_format = guess_record_format(opts->record_filename);
|
||||
if (!opts->record_format) {
|
||||
LOGE("No format specified for \"%s\" (try with -F mkv)",
|
||||
args->record_filename);
|
||||
opts->record_filename);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (args->no_control && args->turn_screen_off) {
|
||||
if (!opts->control && opts->turn_screen_off) {
|
||||
LOGE("Could not request to turn screen off if control is disabled");
|
||||
return false;
|
||||
}
|
||||
@@ -458,24 +469,11 @@ main(int argc, char *argv[]) {
|
||||
setbuf(stderr, NULL);
|
||||
#endif
|
||||
struct args args = {
|
||||
.serial = NULL,
|
||||
.crop = NULL,
|
||||
.record_filename = NULL,
|
||||
.window_title = NULL,
|
||||
.push_target = NULL,
|
||||
.record_format = 0,
|
||||
.opts = SCRCPY_OPTIONS_DEFAULT,
|
||||
.help = false,
|
||||
.version = false,
|
||||
.show_touches = false,
|
||||
.port = DEFAULT_LOCAL_PORT,
|
||||
.max_size = DEFAULT_MAX_SIZE,
|
||||
.bit_rate = DEFAULT_BIT_RATE,
|
||||
.always_on_top = false,
|
||||
.no_control = false,
|
||||
.no_display = false,
|
||||
.turn_screen_off = false,
|
||||
.render_expired_frames = false,
|
||||
};
|
||||
|
||||
if (!parse_args(&args, argc, argv)) {
|
||||
return 1;
|
||||
}
|
||||
@@ -504,25 +502,7 @@ main(int argc, char *argv[]) {
|
||||
SDL_LogSetAllPriority(SDL_LOG_PRIORITY_DEBUG);
|
||||
#endif
|
||||
|
||||
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,
|
||||
.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,
|
||||
};
|
||||
int res = scrcpy(&options) ? 0 : 1;
|
||||
int res = scrcpy(&args.opts) ? 0 : 1;
|
||||
|
||||
avformat_network_deinit(); // ignore failure
|
||||
|
||||
|
||||
@@ -174,9 +174,14 @@ recorder_open(struct recorder *recorder, const AVCodec *input_codec) {
|
||||
|
||||
void
|
||||
recorder_close(struct recorder *recorder) {
|
||||
int ret = av_write_trailer(recorder->ctx);
|
||||
if (ret < 0) {
|
||||
LOGE("Failed to write trailer to %s", recorder->filename);
|
||||
if (recorder->header_written) {
|
||||
int ret = av_write_trailer(recorder->ctx);
|
||||
if (ret < 0) {
|
||||
LOGE("Failed to write trailer to %s", recorder->filename);
|
||||
recorder->failed = true;
|
||||
}
|
||||
} else {
|
||||
// the recorded file is empty
|
||||
recorder->failed = true;
|
||||
}
|
||||
avio_close(recorder->ctx->pb);
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
#include "queue.h"
|
||||
|
||||
enum recorder_format {
|
||||
RECORDER_FORMAT_MP4 = 1,
|
||||
RECORDER_FORMAT_AUTO,
|
||||
RECORDER_FORMAT_MP4,
|
||||
RECORDER_FORMAT_MKV,
|
||||
};
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ static struct input_manager input_manager = {
|
||||
.controller = &controller,
|
||||
.video_buffer = &video_buffer,
|
||||
.screen = &screen,
|
||||
.prefer_text = false, // initialized later
|
||||
};
|
||||
|
||||
// init SDL and set appropriate hints
|
||||
@@ -145,10 +146,8 @@ handle_event(SDL_Event *event, bool control) {
|
||||
case SDL_WINDOWEVENT:
|
||||
switch (event->window.event) {
|
||||
case SDL_WINDOWEVENT_EXPOSED:
|
||||
screen_render(&screen);
|
||||
break;
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
screen_window_resized(&screen);
|
||||
screen_render(&screen);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -416,6 +415,8 @@ scrcpy(const struct scrcpy_options *options) {
|
||||
show_touches_waited = true;
|
||||
}
|
||||
|
||||
input_manager.prefer_text = options->prefer_text;
|
||||
|
||||
ret = event_loop(options->display, options->control);
|
||||
LOGD("quit...");
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <recorder.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "input_manager.h"
|
||||
#include "recorder.h"
|
||||
|
||||
struct scrcpy_options {
|
||||
const char *serial;
|
||||
@@ -24,8 +25,29 @@ struct scrcpy_options {
|
||||
bool display;
|
||||
bool turn_screen_off;
|
||||
bool render_expired_frames;
|
||||
bool prefer_text;
|
||||
};
|
||||
|
||||
#define SCRCPY_OPTIONS_DEFAULT { \
|
||||
.serial = NULL, \
|
||||
.crop = NULL, \
|
||||
.record_filename = NULL, \
|
||||
.window_title = NULL, \
|
||||
.push_target = NULL, \
|
||||
.record_format = RECORDER_FORMAT_AUTO, \
|
||||
.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, \
|
||||
.prefer_text = false, \
|
||||
}
|
||||
|
||||
bool
|
||||
scrcpy(const struct scrcpy_options *options);
|
||||
|
||||
|
||||
105
app/src/screen.c
105
app/src/screen.c
@@ -134,57 +134,16 @@ create_texture(SDL_Renderer *renderer, struct size frame_size) {
|
||||
frame_size.width, frame_size.height);
|
||||
}
|
||||
|
||||
#ifdef HIDPI_WORKAROUND
|
||||
static void
|
||||
screen_get_sizes(struct screen *screen, struct screen_sizes *out) {
|
||||
int ww, wh, dw, dh;
|
||||
SDL_GetWindowSize(screen->window, &ww, &wh);
|
||||
SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
|
||||
out->window.width = ww;
|
||||
out->window.height = wh;
|
||||
out->window.width = dw;
|
||||
out->window.height = dh;
|
||||
}
|
||||
#endif
|
||||
|
||||
// This may be called more than once to work around SDL bugs
|
||||
static bool
|
||||
screen_init_renderer_and_texture(struct screen *screen) {
|
||||
screen->renderer = SDL_CreateRenderer(screen->window, -1,
|
||||
SDL_RENDERER_ACCELERATED);
|
||||
if (!screen->renderer) {
|
||||
LOGC("Could not create renderer: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SDL_RenderSetLogicalSize(screen->renderer, screen->frame_size.width,
|
||||
screen->frame_size.height)) {
|
||||
LOGE("Could not set renderer logical size: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
screen->texture = create_texture(screen->renderer, screen->frame_size);
|
||||
if (!screen->texture) {
|
||||
LOGC("Could not create texture: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef HIDPI_WORKAROUND
|
||||
screen_get_sizes(screen, &screen->sizes);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
screen_init_rendering(struct screen *screen, const char *window_title,
|
||||
struct size frame_size, bool always_on_top) {
|
||||
screen->frame_size = frame_size;
|
||||
|
||||
struct size window_size = get_initial_optimal_size(frame_size);
|
||||
uint32_t window_flags = SDL_WINDOW_HIDDEN
|
||||
| SDL_WINDOW_RESIZABLE
|
||||
| SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
uint32_t window_flags = SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE;
|
||||
#ifdef HIDPI_SUPPORT
|
||||
window_flags |= SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
#endif
|
||||
if (always_on_top) {
|
||||
#ifdef SCRCPY_SDL_HAS_WINDOW_ALWAYS_ON_TOP
|
||||
window_flags |= SDL_WINDOW_ALWAYS_ON_TOP;
|
||||
@@ -203,6 +162,21 @@ screen_init_rendering(struct screen *screen, const char *window_title,
|
||||
return false;
|
||||
}
|
||||
|
||||
screen->renderer = SDL_CreateRenderer(screen->window, -1,
|
||||
SDL_RENDERER_ACCELERATED);
|
||||
if (!screen->renderer) {
|
||||
LOGC("Could not create renderer: %s", SDL_GetError());
|
||||
screen_destroy(screen);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SDL_RenderSetLogicalSize(screen->renderer, frame_size.width,
|
||||
frame_size.height)) {
|
||||
LOGE("Could not set renderer logical size: %s", SDL_GetError());
|
||||
screen_destroy(screen);
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_Surface *icon = read_xpm(icon_xpm);
|
||||
if (icon) {
|
||||
SDL_SetWindowIcon(screen->window, icon);
|
||||
@@ -213,7 +187,9 @@ screen_init_rendering(struct screen *screen, const char *window_title,
|
||||
|
||||
LOGI("Initial texture: %" PRIu16 "x%" PRIu16, frame_size.width,
|
||||
frame_size.height);
|
||||
if (!screen_init_renderer_and_texture(screen)) {
|
||||
screen->texture = create_texture(screen->renderer, frame_size);
|
||||
if (!screen->texture) {
|
||||
LOGC("Could not create texture: %s", SDL_GetError());
|
||||
screen_destroy(screen);
|
||||
return false;
|
||||
}
|
||||
@@ -302,43 +278,6 @@ screen_update_frame(struct screen *screen, struct video_buffer *vb) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef HIDPI_WORKAROUND
|
||||
// workaround for <https://github.com/Genymobile/scrcpy/issues/15>
|
||||
static inline bool
|
||||
screen_fix_hidpi(struct screen *screen) {
|
||||
struct screen_sizes cur;
|
||||
screen_get_sizes(screen, &cur);
|
||||
|
||||
struct screen_sizes *prev = &screen->sizes;
|
||||
|
||||
bool width_ratio_changed = cur.window.width * prev->drawable.width !=
|
||||
cur.drawable.width * prev->window.width;
|
||||
bool height_ratio_changed = cur.window.height * prev->drawable.height !=
|
||||
cur.drawable.height * prev->window.height;
|
||||
if (width_ratio_changed || height_ratio_changed) {
|
||||
SDL_DestroyTexture(screen->texture);
|
||||
SDL_DestroyRenderer(screen->renderer);
|
||||
if (!screen_init_renderer_and_texture(screen)) {
|
||||
screen->texture = NULL;
|
||||
screen->renderer = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGI("Renderer reset after hidpi scaling changed");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
screen_window_resized(struct screen *screen) {
|
||||
#ifdef HIDPI_WORKAROUND
|
||||
screen_fix_hidpi(screen);
|
||||
#endif
|
||||
screen_render(screen);
|
||||
}
|
||||
|
||||
void
|
||||
screen_render(struct screen *screen) {
|
||||
SDL_RenderClear(screen->renderer);
|
||||
|
||||
@@ -20,12 +20,6 @@ struct screen {
|
||||
bool has_frame;
|
||||
bool fullscreen;
|
||||
bool no_window;
|
||||
#ifdef HIDPI_WORKAROUND
|
||||
struct screen_sizes {
|
||||
struct size window;
|
||||
struct size drawable;
|
||||
} sizes;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define SCREEN_INITIALIZER { \
|
||||
@@ -66,10 +60,6 @@ screen_destroy(struct screen *screen);
|
||||
bool
|
||||
screen_update_frame(struct screen *screen, struct video_buffer *vb);
|
||||
|
||||
// update content after window resizing
|
||||
void
|
||||
screen_window_resized(struct screen *screen);
|
||||
|
||||
// render the texture to the renderer
|
||||
void
|
||||
screen_render(struct screen *screen);
|
||||
|
||||
@@ -4,5 +4,5 @@ option('crossbuild_windows', type: 'boolean', value: false, description: 'Build
|
||||
option('windows_noconsole', type: 'boolean', value: false, description: 'Disable console on Windows (pass -mwindows flag)')
|
||||
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('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')
|
||||
option('hidpi_workaround', type: 'boolean', value: true, description: 'Enable a workaround for bug #15')
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.media.MediaCodec;
|
||||
import android.media.MediaCodecInfo;
|
||||
import android.media.MediaFormat;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.view.Surface;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
@@ -54,6 +55,11 @@ public class ScreenEncoder implements Device.RotationListener {
|
||||
}
|
||||
|
||||
public void streamScreen(Device device, FileDescriptor fd) throws IOException {
|
||||
// Some devices internally create a Handler when creating an input Surface, causing an exception:
|
||||
// "Can't create handler inside thread that has not called Looper.prepare()"
|
||||
// <https://github.com/Genymobile/scrcpy/issues/240>
|
||||
Looper.prepareMainLooper();
|
||||
|
||||
MediaFormat format = createFormat(bitRate, frameRate, iFrameInterval);
|
||||
device.setRotationListener(this);
|
||||
boolean alive;
|
||||
|
||||
Reference in New Issue
Block a user