Compare commits

..

15 Commits

Author SHA1 Message Date
Romain Vimont
0308ef43f2 Do not set buttons on touch events
BUTTON_PRIMARY must not be set for touch events:

> This button constant is not set in response to simple touches with a
> finger or stylus tip. The user must actually push a button.

<https://developer.android.com/reference/android/view/MotionEvent#BUTTON_PRIMARY>

Fixes #2169 <https://github.com/Genymobile/scrcpy/issues/2169>
2021-03-15 18:46:56 +01:00
Romain Vimont
40febf4a91 Use device id 0 for touch/mouse events
Virtual device is only for keyboard sources, not mouse or touchscreen
sources. Here is the value of InputDevice.getDevice(-1).toString():

    Input Device -1: Virtual
      Descriptor: ...
      Generation: 2
      Location: built-in
      Keyboard Type: alphabetic
      Has Vibrator: false
      Has mic: false
      Sources: 0x301 ( keyboard dpad )

InputDevice.getDeviceId() documentation says:

> An id of zero indicates that the event didn't come from a physical
> device and maps to the default keymap.

<https://developer.android.com/reference/android/view/InputEvent#getDeviceId()>

However, injecting events with a device id of 0 causes event.getDevice()
to be null on the client-side.

Commit 26529d377f used -1 as a workaround
to avoid a NPE on a specific Android TV device. But this is a bug in the
device system, which wrongly assumes that input device may not be null.

A similar issue was present in Flutter, but it is now fixed:
 - <https://github.com/flutter/flutter/issues/30665>
 - <https://github.com/flutter/engine/pull/7986>

On the other hand, using an id of -1 for touchscreen events (which is
invalid) causes issues for some apps:
<https://github.com/Genymobile/scrcpy/issues/2125#issuecomment-790535792>

Therefore, use a device id of 0.

An alternative could be to find an existing device matching the source,
like "adb shell input" does. See getInputDeviceId():
<https://android.googlesource.com/platform/frameworks/base.git/+/master/cmds/input/src/com/android/commands/input/Input.java>

But it seems better to indicate that the event didn't come from a
physical device, and it would not solve #962 anyway, because an Android
TV has no touchscreen.

Refs #962 <https://github.com/Genymobile/scrcpy/issues/962>
Fixes #2125 <https://github.com/Genymobile/scrcpy/issues/2125>
2021-03-14 18:30:56 +01:00
Romain Vimont
429fdef04f Fix encoder parameter suggestion
The option is --encoder, not --encoder-name.
2021-03-11 10:55:12 +01:00
Biswapriyo Nath
d1789f082a meson: Do not use full path with mingw tools name
This helps to use mingw toolchains which are not in /usr/bin path.

PR #2185 <https://github.com/Genymobile/scrcpy/pull/2185>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2021-03-09 09:59:57 +01:00
Romain Vimont
eb7e1070cf Release frame data as soon as possible
During a frame swap, one of the two frames involved can be released.
2021-03-06 22:58:03 +01:00
Romain Vimont
386f017ba9 Factorize frame swap 2021-03-06 22:58:03 +01:00
Romain Vimont
cc48b24324 Simplify screen initialization
Use a single function to initialize the screen instance.
2021-03-06 22:58:03 +01:00
Romain Vimont
597c54f049 Group screen parameters into a struct
The function screen_init_rendering had too many parameters.
2021-03-06 22:58:03 +01:00
Romain Vimont
955da3b578 Remove screen static initializer
Most of the fields are initialized dynamically.
2021-03-06 22:58:03 +01:00
Romain Vimont
cb9c42bdcb Use a callback to notify frame skip
A skipped frame is detected when the producer offers a frame while the
current pending frame has not been consumed.

However, the producer (in practice the decoder) is not interested in the
fact that a frame has been skipped, only the consumer (the renderer) is.

Therefore, notify frame skip via a consumer callback. This allows to
manage the skipped and rendered frames count at the same place, and
remove fps_counter from decoder.
2021-03-06 22:58:03 +01:00
Romain Vimont
fb9f9848bd Use a callback to notify a new frame
Make the decoder independant of the SDL even mechanism, by making the
consumer register a callback on the video_buffer.
2021-03-06 22:58:03 +01:00
Romain Vimont
c50b958ee4 Initialize screen before starting the stream
As soon as the stream is started, the video buffer could notify a new
frame available.

In order to pass this event to the screen without race condition, the
screen must be initialized before the screen is started.
2021-03-06 22:58:03 +01:00
Romain Vimont
441d3fb119 Make video buffer more generic
Video buffer is a tool between a frame producer and a frame consumer.

For now, it is used between a decoder and a renderer, but in the future
another instance might be used to swscale decoded frames.
2021-03-06 22:58:03 +01:00
Romain Vimont
cb197ee3a2 Move fps counter out of video buffer
In order to make video buffer more generic, move out its specific
responsibility to count the fps between the decoder and the renderer.
2021-03-06 22:58:03 +01:00
Romain Vimont
218636dc10 Inject touch events with smallest detectable size
A value of 1 means the "largest detectable size", a value of 0 means the
"smallest detectable size".

https://developer.android.com/reference/android/view/MotionEvent.PointerCoords#size
https://developer.android.com/reference/android/view/MotionEvent#AXIS_SIZE

Fixes #2125 <https://github.com/Genymobile/scrcpy/issues/2125>
2021-03-03 18:22:54 +01:00
17 changed files with 72 additions and 294 deletions

View File

@@ -15,7 +15,6 @@ src = [
'src/opengl.c',
'src/receiver.c',
'src/recorder.c',
'src/resizer.c',
'src/scrcpy.c',
'src/screen.c',
'src/server.c',

View File

@@ -167,13 +167,6 @@ Set the initial display rotation. Possibles values are 0, 1, 2 and 3. Each incre
.BI "\-s, \-\-serial " number
The device serial number. Mandatory only if several devices are connected to adb.
.TP
.BI "\-\-scale\-filter filter
Supported filters are "none" and "trilinear".
Trilinear filtering is only available if the renderer is OpenGL 3.0+ or OpenGL
ES 2.0+.
.TP
.BI "\-\-shortcut\-mod " key[+...]][,...]
Specify the modifiers to use for scrcpy shortcuts. Possible keys are "lctrl", "rctrl", "lalt", "ralt", "lsuper" and "rsuper".

View File

@@ -106,6 +106,11 @@ scrcpy_print_usage(const char *arg0) {
" --no-key-repeat\n"
" Do not forward repeated key events when a key is held down.\n"
"\n"
" --no-mipmaps\n"
" If the renderer is OpenGL 3.0+ or OpenGL ES 2.0+, then\n"
" mipmaps are automatically generated to improve downscaling\n"
" quality. This option disables the generation of mipmaps.\n"
"\n"
" -p, --port port[:port]\n"
" Set the TCP port (range) used by the client to listen.\n"
" Default is " STR(DEFAULT_LOCAL_PORT_RANGE_FIRST) ":"
@@ -153,11 +158,6 @@ scrcpy_print_usage(const char *arg0) {
" The device serial number. Mandatory only if several devices\n"
" are connected to adb.\n"
"\n"
" --scale-filter filter\n"
" Supported filters are \"none\" and \"trilinear\".\n"
" Trilinear filtering is only available if the renderer is\n"
" OpenGL 3.0+ or OpenGL ES 2.0+.\n"
"\n"
" --shortcut-mod key[+...]][,...]\n"
" Specify the modifiers to use for scrcpy shortcuts.\n"
" Possible keys are \"lctrl\", \"rctrl\", \"lalt\", \"ralt\",\n"
@@ -640,21 +640,6 @@ guess_record_format(const char *filename) {
return 0;
}
static bool
parse_scale_filter(const char *optarg, enum sc_scale_filter *filter) {
if (!strcmp(optarg, "none")) {
*filter = SC_SCALE_FILTER_NONE;
return true;
}
if (!strcmp(optarg, "trilinear")) {
*filter = SC_SCALE_FILTER_TRILINEAR;
return true;
}
LOGE("Unsupported scale filter: %s "
"(expected \"none\" or \"trilinear\")", optarg);
return false;
}
#define OPT_RENDER_EXPIRED_FRAMES 1000
#define OPT_WINDOW_TITLE 1001
#define OPT_PUSH_TARGET 1002
@@ -681,7 +666,6 @@ parse_scale_filter(const char *optarg, enum sc_scale_filter *filter) {
#define OPT_FORWARD_ALL_CLICKS 1023
#define OPT_LEGACY_PASTE 1024
#define OPT_ENCODER_NAME 1025
#define OPT_SCALE_FILTER 1026
bool
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
@@ -718,7 +702,6 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
{"render-expired-frames", no_argument, NULL,
OPT_RENDER_EXPIRED_FRAMES},
{"rotation", required_argument, NULL, OPT_ROTATION},
{"scale-filter", required_argument, NULL, OPT_SCALE_FILTER},
{"serial", required_argument, NULL, 's'},
{"shortcut-mod", required_argument, NULL, OPT_SHORTCUT_MOD},
{"show-touches", no_argument, NULL, 't'},
@@ -873,9 +856,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
opts->render_driver = optarg;
break;
case OPT_NO_MIPMAPS:
LOGW("Deprecated option --no-mipmaps. "
"Use --scale-filter=none instead.");
opts->scale_filter = SC_SCALE_FILTER_NONE;
opts->mipmaps = false;
break;
case OPT_NO_KEY_REPEAT:
opts->forward_key_repeat = false;
@@ -903,11 +884,6 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
case OPT_LEGACY_PASTE:
opts->legacy_paste = true;
break;
case OPT_SCALE_FILTER:
if (!parse_scale_filter(optarg, &opts->scale_filter)) {
return false;
}
break;
default:
// getopt prints the error message on stderr
return false;

View File

@@ -30,20 +30,11 @@ decoder_open(struct decoder *decoder, const AVCodec *codec) {
return false;
}
decoder->frame = av_frame_alloc();
if (!decoder->frame) {
LOGE("Could not create decoder frame");
avcodec_close(decoder->codec_ctx);
avcodec_free_context(&decoder->codec_ctx);
return false;
}
return true;
}
void
decoder_close(struct decoder *decoder) {
av_frame_free(&decoder->frame);
avcodec_close(decoder->codec_ctx);
avcodec_free_context(&decoder->codec_ctx);
}
@@ -58,11 +49,11 @@ decoder_push(struct decoder *decoder, const AVPacket *packet) {
LOGE("Could not send video packet: %d", ret);
return false;
}
ret = avcodec_receive_frame(decoder->codec_ctx, decoder->frame);
ret = avcodec_receive_frame(decoder->codec_ctx,
decoder->video_buffer->producer_frame);
if (!ret) {
// a frame was received
video_buffer_producer_offer_frame(decoder->video_buffer,
&decoder->frame);
video_buffer_producer_offer_frame(decoder->video_buffer);
} else if (ret != AVERROR(EAGAIN)) {
LOGE("Could not receive video frame: %d", ret);
return false;
@@ -70,7 +61,7 @@ decoder_push(struct decoder *decoder, const AVPacket *packet) {
#else
int got_picture;
int len = avcodec_decode_video2(decoder->codec_ctx,
decoder->frame,
decoder->video_buffer->decoding_frame,
&got_picture,
packet);
if (len < 0) {

View File

@@ -12,7 +12,6 @@ struct decoder {
struct video_buffer *video_buffer;
AVCodecContext *codec_ctx;
AVFrame *frame;
};
void

View File

@@ -1,132 +0,0 @@
#include "resizer.h"
#include <assert.h>
#include "util/log.h"
bool
sc_resizer_init(struct sc_resizer *resizer, struct video_buffer *vb_in,
struct video_buffer *vb_out, struct size size) {
bool ok = sc_mutex_init(&resizer->mutex);
if (!ok) {
return false;
}
ok = sc_cond_init(&resizer->req_cond);
if (!ok) {
sc_mutex_destroy(&resizer->mutex);
return false;
}
resizer->resized_frame = av_frame_alloc();
if (!resizer->resized_frame) {
sc_cond_destroy(&resizer->req_cond);
sc_mutex_destroy(&resizer->mutex);
return false;
}
resizer->vb_in = vb_in;
resizer->vb_out = vb_out;
resizer->size = size;
resizer->input_frame = NULL;
resizer->has_request = false;
resizer->has_new_frame = false;
resizer->interrupted = false;
return true;
}
void
sc_resizer_destroy(struct sc_resizer *resizer) {
av_frame_free(&resizer->resized_frame);
sc_cond_destroy(&resizer->req_cond);
sc_mutex_destroy(&resizer->mutex);
}
static bool
sc_resizer_swscale(struct sc_resizer *resizer) {
assert(!resizer->resized_frame->buf[0]); // The frame must be "empty"
return false;
}
static int
run_resizer(void *data) {
struct sc_resizer *resizer = data;
sc_mutex_lock(&resizer->mutex);
while (!resizer->interrupted) {
while (!resizer->interrupted && !resizer->has_request) {
sc_cond_wait(&resizer->req_cond, &resizer->mutex);
}
if (resizer->has_new_frame) {
resizer->input_frame =
video_buffer_consumer_take_frame(resizer->vb_in);
resizer->has_new_frame = false;
}
resizer->has_request = false;
sc_mutex_unlock(&resizer->mutex);
// Do the actual work without mutex
sc_resizer_swscale(resizer);
video_buffer_producer_offer_frame(resizer->vb_out,
&resizer->resized_frame);
sc_mutex_lock(&resizer->mutex);
}
sc_mutex_unlock(&resizer->mutex);
return 0;
}
bool
sc_resizer_start(struct sc_resizer *resizer) {
LOGD("Starting resizer thread");
bool ok = sc_thread_create(&resizer->thread, run_resizer, "resizer",
resizer);
if (!ok) {
LOGE("Could not start resizer thread");
return false;
}
return true;
}
void
sc_resizer_stop(struct sc_resizer *resizer) {
sc_mutex_lock(&resizer->mutex);
resizer->interrupted = true;
sc_cond_signal(&resizer->req_cond);
sc_mutex_unlock(&resizer->mutex);
video_buffer_interrupt(resizer->vb_out);
}
void
sc_resizer_join(struct sc_resizer *resizer) {
sc_thread_join(&resizer->thread, NULL);
}
void
sc_resizer_process_new_frame(struct sc_resizer *resizer) {
sc_mutex_lock(&resizer->mutex);
resizer->has_request = true;
resizer->has_new_frame = true;
sc_cond_signal(&resizer->req_cond);
sc_mutex_unlock(&resizer->mutex);
}
void
sc_resizer_process_new_size(struct sc_resizer *resizer, struct size size) {
sc_mutex_lock(&resizer->mutex);
resizer->size = size;
resizer->has_request = true;
sc_cond_signal(&resizer->req_cond);
sc_mutex_unlock(&resizer->mutex);
}

View File

@@ -1,53 +0,0 @@
#ifndef SC_RESIZER
#define SC_RESIZER
#include "common.h"
#include <stdbool.h>
#include <libavformat/avformat.h>
#include "coords.h"
#include "util/thread.h"
#include "video_buffer.h"
struct sc_resizer {
struct video_buffer *vb_in;
struct video_buffer *vb_out;
struct size size;
// valid until the next call to video_buffer_consumer_take_frame(vb_in)
const AVFrame *input_frame;
AVFrame *resized_frame;
sc_thread thread;
sc_mutex mutex;
sc_cond req_cond;
bool has_request;
bool has_new_frame;
bool interrupted;
};
bool
sc_resizer_init(struct sc_resizer *resizer, struct video_buffer *vb_in,
struct video_buffer *vb_out, struct size initial_size);
void
sc_resizer_destroy(struct sc_resizer *resizer);
bool
sc_resizer_start(struct sc_resizer *resizer);
void
sc_resizer_stop(struct sc_resizer *resizer);
void
sc_resizer_join(struct sc_resizer *resizer);
void
sc_resizer_process_new_frame(struct sc_resizer *resizer);
void
sc_resizer_process_new_size(struct sc_resizer *resizer, struct size size);
#endif

View File

@@ -392,7 +392,7 @@ scrcpy(const struct scrcpy_options *options) {
.window_height = options->window_height,
.window_borderless = options->window_borderless,
.rotation = options->rotation,
.scale_filter = options->scale_filter,
.mipmaps = options->mipmaps,
};
if (!screen_init(&screen, &video_buffer, &fps_counter,

View File

@@ -41,11 +41,6 @@ struct sc_port_range {
uint16_t last;
};
enum sc_scale_filter {
SC_SCALE_FILTER_NONE,
SC_SCALE_FILTER_TRILINEAR, // mipmaps
};
#define SC_WINDOW_POSITION_UNDEFINED (-0x8000)
struct scrcpy_options {
@@ -61,7 +56,6 @@ struct scrcpy_options {
enum sc_record_format record_format;
struct sc_port_range port_range;
struct sc_shortcut_mods shortcut_mods;
enum sc_scale_filter scale_filter;
uint16_t max_size;
uint32_t bit_rate;
uint16_t max_fps;
@@ -81,6 +75,7 @@ struct scrcpy_options {
bool render_expired_frames;
bool prefer_text;
bool window_borderless;
bool mipmaps;
bool stay_awake;
bool force_adb_forward;
bool disable_screensaver;
@@ -108,7 +103,6 @@ struct scrcpy_options {
.data = {SC_MOD_LALT, SC_MOD_LSUPER}, \
.count = 2, \
}, \
.scale_filter = SC_SCALE_FILTER_TRILINEAR, \
.max_size = 0, \
.bit_rate = DEFAULT_BIT_RATE, \
.max_fps = 0, \
@@ -128,6 +122,7 @@ struct scrcpy_options {
.render_expired_frames = false, \
.prefer_text = false, \
.window_borderless = false, \
.mipmaps = true, \
.stay_awake = false, \
.force_adb_forward = false, \
.disable_screensaver = false, \

View File

@@ -6,6 +6,7 @@
#include "events.h"
#include "icon.xpm"
#include "scrcpy.h"
#include "tiny_xpm.h"
#include "video_buffer.h"
#include "util/log.h"
@@ -313,14 +314,13 @@ screen_init(struct screen *screen, struct video_buffer *vb,
// starts with "opengl"
bool use_opengl = renderer_name && !strncmp(renderer_name, "opengl", 6);
bool mipmaps = params->scale_filter == SC_SCALE_FILTER_TRILINEAR;
if (use_opengl) {
struct sc_opengl *gl = &screen->gl;
sc_opengl_init(gl);
LOGI("OpenGL version: %s", gl->version);
if (mipmaps) {
if (params->mipmaps) {
bool supports_mipmaps =
sc_opengl_version_at_least(gl, 3, 0, /* OpenGL 3.0+ */
2, 0 /* OpenGL ES 2.0+ */);
@@ -334,7 +334,7 @@ screen_init(struct screen *screen, struct video_buffer *vb,
} else {
LOGI("Trilinear filtering disabled");
}
} else if (mipmaps) {
} else if (params->mipmaps) {
LOGD("Trilinear filtering disabled (not an OpenGL renderer)");
}
@@ -356,15 +356,6 @@ screen_init(struct screen *screen, struct video_buffer *vb,
return false;
}
screen->frame = av_frame_alloc();
if (!screen->frame) {
LOGC("Could not create screen frame");
SDL_DestroyTexture(screen->texture);
SDL_DestroyRenderer(screen->renderer);
SDL_DestroyWindow(screen->window);
return false;
}
// Reset the window size to trigger a SIZE_CHANGED event, to workaround
// HiDPI issues with some SDL renderers when several displays having
// different HiDPI scaling are connected
@@ -382,7 +373,6 @@ screen_show_window(struct screen *screen) {
void
screen_destroy(struct screen *screen) {
av_frame_free(&screen->frame);
SDL_DestroyTexture(screen->texture);
SDL_DestroyRenderer(screen->renderer);
SDL_DestroyWindow(screen->window);
@@ -490,8 +480,7 @@ update_texture(struct screen *screen, const AVFrame *frame) {
static bool
screen_update_frame(struct screen *screen) {
video_buffer_consumer_take_frame(screen->vb, &screen->frame);
AVFrame *frame = screen->frame;
const AVFrame *frame = video_buffer_consumer_take_frame(screen->vb);
fps_counter_add_rendered_frame(screen->fps_counter);

View File

@@ -9,7 +9,6 @@
#include "coords.h"
#include "opengl.h"
#include "scrcpy.h"
struct video_buffer;
@@ -37,8 +36,6 @@ struct screen {
bool fullscreen;
bool maximized;
bool mipmaps;
AVFrame *frame;
};
struct screen_params {
@@ -54,7 +51,7 @@ struct screen_params {
bool window_borderless;
uint8_t rotation;
enum sc_scale_filter scale_filter;
bool mipmaps;
};
// initialize screen, create window, renderer and texture (window is hidden)

View File

@@ -8,14 +8,24 @@
bool
video_buffer_init(struct video_buffer *vb, bool wait_consumer) {
vb->producer_frame = av_frame_alloc();
if (!vb->producer_frame) {
goto error_0;
}
vb->pending_frame = av_frame_alloc();
if (!vb->pending_frame) {
goto error_0;
goto error_1;
}
vb->consumer_frame = av_frame_alloc();
if (!vb->consumer_frame) {
goto error_2;
}
bool ok = sc_mutex_init(&vb->mutex);
if (!ok) {
goto error_1;
goto error_3;
}
vb->wait_consumer = wait_consumer;
@@ -23,7 +33,7 @@ video_buffer_init(struct video_buffer *vb, bool wait_consumer) {
ok = sc_cond_init(&vb->pending_frame_consumed_cond);
if (!ok) {
sc_mutex_destroy(&vb->mutex);
goto error_1;
goto error_2;
}
// interrupted is not used if wait_consumer is disabled since offering
// a frame will never block
@@ -39,8 +49,12 @@ video_buffer_init(struct video_buffer *vb, bool wait_consumer) {
return true;
error_1:
error_3:
av_frame_free(&vb->consumer_frame);
error_2:
av_frame_free(&vb->pending_frame);
error_1:
av_frame_free(&vb->producer_frame);
error_0:
return false;
}
@@ -51,7 +65,9 @@ video_buffer_destroy(struct video_buffer *vb) {
sc_cond_destroy(&vb->pending_frame_consumed_cond);
}
sc_mutex_destroy(&vb->mutex);
av_frame_free(&vb->consumer_frame);
av_frame_free(&vb->pending_frame);
av_frame_free(&vb->producer_frame);
}
static inline void
@@ -73,7 +89,7 @@ video_buffer_set_consumer_callbacks(struct video_buffer *vb,
}
void
video_buffer_producer_offer_frame(struct video_buffer *vb, AVFrame **pframe) {
video_buffer_producer_offer_frame(struct video_buffer *vb) {
assert(vb->cbs);
sc_mutex_lock(&vb->mutex);
@@ -85,7 +101,7 @@ video_buffer_producer_offer_frame(struct video_buffer *vb, AVFrame **pframe) {
}
av_frame_unref(vb->pending_frame);
swap_frames(pframe, &vb->pending_frame);
swap_frames(&vb->producer_frame, &vb->pending_frame);
bool skipped = !vb->pending_frame_consumed;
vb->pending_frame_consumed = false;
@@ -100,13 +116,13 @@ video_buffer_producer_offer_frame(struct video_buffer *vb, AVFrame **pframe) {
}
}
void
video_buffer_consumer_take_frame(struct video_buffer *vb, AVFrame **pframe) {
const AVFrame *
video_buffer_consumer_take_frame(struct video_buffer *vb) {
sc_mutex_lock(&vb->mutex);
assert(!vb->pending_frame_consumed);
vb->pending_frame_consumed = true;
swap_frames(pframe, &vb->pending_frame);
swap_frames(&vb->consumer_frame, &vb->pending_frame);
av_frame_unref(vb->pending_frame);
if (vb->wait_consumer) {
@@ -114,6 +130,9 @@ video_buffer_consumer_take_frame(struct video_buffer *vb, AVFrame **pframe) {
sc_cond_signal(&vb->pending_frame_consumed_cond);
}
sc_mutex_unlock(&vb->mutex);
// consumer_frame is only written from this thread, no need to lock
return vb->consumer_frame;
}
void

View File

@@ -29,7 +29,9 @@ typedef struct AVFrame AVFrame;
*/
struct video_buffer {
AVFrame *producer_frame;
AVFrame *pending_frame;
AVFrame *consumer_frame;
sc_mutex mutex;
bool wait_consumer; // never overwrite a pending frame if it is not consumed
@@ -65,14 +67,13 @@ video_buffer_set_consumer_callbacks(struct video_buffer *vb,
void *cbs_userdata);
// set the producer frame as ready for consuming
// the produced frame is exchanged with an unused allocated frame
void
video_buffer_producer_offer_frame(struct video_buffer *vb, AVFrame **pframe);
video_buffer_producer_offer_frame(struct video_buffer *vb);
// mark the consumer frame as consumed and exchange it with an unused allocated
// frame
void
video_buffer_consumer_take_frame(struct video_buffer *vb, AVFrame **pframe);
// mark the consumer frame as consumed and return it
// the frame is valid until the next call to this function
const AVFrame *
video_buffer_consumer_take_frame(struct video_buffer *vb);
// wake up and avoid any blocking call
void

View File

@@ -2,11 +2,11 @@
[binaries]
name = 'mingw'
c = '/usr/bin/i686-w64-mingw32-gcc'
cpp = '/usr/bin/i686-w64-mingw32-g++'
ar = '/usr/bin/i686-w64-mingw32-ar'
strip = '/usr/bin/i686-w64-mingw32-strip'
pkgconfig = '/usr/bin/i686-w64-mingw32-pkg-config'
c = 'i686-w64-mingw32-gcc'
cpp = 'i686-w64-mingw32-g++'
ar = 'i686-w64-mingw32-ar'
strip = 'i686-w64-mingw32-strip'
pkgconfig = 'i686-w64-mingw32-pkg-config'
[host_machine]
system = 'windows'

View File

@@ -2,11 +2,11 @@
[binaries]
name = 'mingw'
c = '/usr/bin/x86_64-w64-mingw32-gcc'
cpp = '/usr/bin/x86_64-w64-mingw32-g++'
ar = '/usr/bin/x86_64-w64-mingw32-ar'
strip = '/usr/bin/x86_64-w64-mingw32-strip'
pkgconfig = '/usr/bin/x86_64-w64-mingw32-pkg-config'
c = 'x86_64-w64-mingw32-gcc'
cpp = 'x86_64-w64-mingw32-g++'
ar = 'x86_64-w64-mingw32-ar'
strip = 'x86_64-w64-mingw32-strip'
pkgconfig = 'x86_64-w64-mingw32-pkg-config'
[host_machine]
system = 'windows'

View File

@@ -14,7 +14,7 @@ import java.util.concurrent.TimeUnit;
public class Controller {
private static final int DEVICE_ID_VIRTUAL = -1;
private static final int DEFAULT_DEVICE_ID = 0;
private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor();
@@ -45,7 +45,7 @@ public class Controller {
MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
coords.orientation = 0;
coords.size = 1;
coords.size = 0;
pointerProperties[i] = props;
pointerCoords[i] = coords;
@@ -208,9 +208,13 @@ public class Controller {
// Right-click and middle-click only work if the source is a mouse
boolean nonPrimaryButtonPressed = (buttons & ~MotionEvent.BUTTON_PRIMARY) != 0;
int source = nonPrimaryButtonPressed ? InputDevice.SOURCE_MOUSE : InputDevice.SOURCE_TOUCHSCREEN;
if (source != InputDevice.SOURCE_MOUSE) {
// Buttons must not be set for touch events
buttons = 0;
}
MotionEvent event = MotionEvent
.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, DEVICE_ID_VIRTUAL, 0, source,
.obtain(lastTouchDown, now, action, pointerCount, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, source,
0);
return device.injectEvent(event);
}
@@ -233,7 +237,7 @@ public class Controller {
coords.setAxisValue(MotionEvent.AXIS_VSCROLL, vScroll);
MotionEvent event = MotionEvent
.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, 0, 1f, 1f, DEVICE_ID_VIRTUAL, 0,
.obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, 0, 1f, 1f, DEFAULT_DEVICE_ID, 0,
InputDevice.SOURCE_TOUCHSCREEN, 0);
return device.injectEvent(event);
}

View File

@@ -230,7 +230,7 @@ public final class Server {
if (encoders != null && encoders.length > 0) {
Ln.e("Try to use one of the available encoders:");
for (MediaCodecInfo encoder : encoders) {
Ln.e(" scrcpy --encoder-name '" + encoder.getName() + "'");
Ln.e(" scrcpy --encoder '" + encoder.getName() + "'");
}
}
}