Compare commits
3 Commits
icon.6
...
input_trai
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10b0ba413e | ||
|
|
e94649c5b5 | ||
|
|
9a0224a3f0 |
@@ -1,7 +1,5 @@
|
||||
# scrcpy (v1.19)
|
||||
|
||||

|
||||
|
||||
[Read in another language](#translations)
|
||||
|
||||
This application provides display and control of Android devices connected on
|
||||
|
||||
@@ -8,12 +8,12 @@ src = [
|
||||
'src/controller.c',
|
||||
'src/decoder.c',
|
||||
'src/device_msg.c',
|
||||
'src/event_converter.c',
|
||||
'src/icon.c',
|
||||
'src/file_handler.c',
|
||||
'src/fps_counter.c',
|
||||
'src/frame_buffer.c',
|
||||
'src/input_manager.c',
|
||||
'src/keyboard_inject.c',
|
||||
'src/mouse_inject.c',
|
||||
'src/opengl.c',
|
||||
'src/receiver.c',
|
||||
'src/recorder.c',
|
||||
@@ -21,6 +21,7 @@ src = [
|
||||
'src/screen.c',
|
||||
'src/server.c',
|
||||
'src/stream.c',
|
||||
'src/tiny_xpm.c',
|
||||
'src/video_buffer.c',
|
||||
'src/util/log.c',
|
||||
'src/util/net.c',
|
||||
@@ -150,9 +151,6 @@ executable('scrcpy', src,
|
||||
c_args: [])
|
||||
|
||||
install_man('scrcpy.1')
|
||||
install_data('../data/icon.png',
|
||||
rename: 'scrcpy.png',
|
||||
install_dir: 'share/icons/hicolor/256x256/apps')
|
||||
|
||||
|
||||
### TESTS
|
||||
|
||||
@@ -47,7 +47,7 @@ controller_destroy(struct controller *controller) {
|
||||
|
||||
bool
|
||||
controller_push_msg(struct controller *controller,
|
||||
const struct control_msg *msg) {
|
||||
const struct control_msg *msg) {
|
||||
if (sc_get_log_level() <= SC_LOG_LEVEL_VERBOSE) {
|
||||
control_msg_log(msg);
|
||||
}
|
||||
@@ -63,14 +63,14 @@ controller_push_msg(struct controller *controller,
|
||||
}
|
||||
|
||||
static bool
|
||||
process_msg(struct controller *controller, const struct control_msg *msg) {
|
||||
process_msg(struct controller *controller,
|
||||
const struct control_msg *msg) {
|
||||
static unsigned char serialized_msg[CONTROL_MSG_MAX_SIZE];
|
||||
size_t length = control_msg_serialize(msg, serialized_msg);
|
||||
if (!length) {
|
||||
return false;
|
||||
}
|
||||
ssize_t w =
|
||||
net_send_all(controller->control_socket, serialized_msg, length);
|
||||
ssize_t w = net_send_all(controller->control_socket, serialized_msg, length);
|
||||
return (size_t) w == length;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
#ifndef CONVERT_H
|
||||
#define CONVERT_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <SDL2/SDL_events.h>
|
||||
|
||||
#include "control_msg.h"
|
||||
|
||||
bool
|
||||
convert_keycode_action(SDL_EventType from, enum android_keyevent_action *to);
|
||||
|
||||
enum android_metastate
|
||||
convert_meta_state(SDL_Keymod mod);
|
||||
|
||||
bool
|
||||
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);
|
||||
|
||||
bool
|
||||
convert_mouse_action(SDL_EventType from, enum android_motionevent_action *to);
|
||||
|
||||
bool
|
||||
convert_touch_action(SDL_EventType from, enum android_motionevent_action *to);
|
||||
|
||||
#endif
|
||||
284
app/src/icon.c
284
app/src/icon.c
@@ -1,284 +0,0 @@
|
||||
#include "icon.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
#include <libavutil/pixfmt.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "compat.h"
|
||||
#include "util/log.h"
|
||||
#include "util/process.h"
|
||||
#include "util/str_util.h"
|
||||
|
||||
#define SCRCPY_PORTABLE_ICON_FILENAME "icon.png"
|
||||
#define SCRCPY_DEFAULT_ICON_PATH \
|
||||
PREFIX "/share/icons/hicolor/256x256/apps/scrcpy.png"
|
||||
|
||||
static char *
|
||||
get_envvar_icon_path(void) {
|
||||
#ifdef __WINDOWS__
|
||||
const wchar_t *icon_path_env = _wgetenv(L"SCRCPY_ICON_PATH");
|
||||
#else
|
||||
const char *icon_path_env = getenv("SCRCPY_ICON_PATH");
|
||||
#endif
|
||||
|
||||
if (!icon_path_env) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
char *icon_path = utf8_from_wide_char(icon_path_env);
|
||||
#else
|
||||
char *icon_path = strdup(icon_path_env);
|
||||
#endif
|
||||
if (!icon_path) {
|
||||
LOGE("Could not allocate memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return icon_path;
|
||||
}
|
||||
|
||||
static AVFrame *
|
||||
decode_image(const char *path) {
|
||||
AVFrame *result = NULL;
|
||||
|
||||
AVFormatContext *ctx = avformat_alloc_context();
|
||||
if (!ctx) {
|
||||
LOGE("Could not allocate image decoder context");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (avformat_open_input(&ctx, path, NULL, NULL) < 0) {
|
||||
LOGE("Could not open image codec: %s", path);
|
||||
goto free_ctx;
|
||||
}
|
||||
|
||||
if (avformat_find_stream_info(ctx, NULL) < 0) {
|
||||
LOGE("Could not find image stream info");
|
||||
goto close_input;
|
||||
}
|
||||
|
||||
int stream = av_find_best_stream(ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
|
||||
if (stream < 0 ) {
|
||||
LOGE("Could not find best image stream");
|
||||
goto close_input;
|
||||
}
|
||||
|
||||
AVCodecParameters *params = ctx->streams[stream]->codecpar;
|
||||
|
||||
AVCodec *codec = avcodec_find_decoder(params->codec_id);
|
||||
if (!codec) {
|
||||
LOGE("Could not find image decoder");
|
||||
goto close_input;
|
||||
}
|
||||
|
||||
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
|
||||
if (!codec_ctx) {
|
||||
LOGE("Could not allocate codec context");
|
||||
goto close_input;
|
||||
}
|
||||
|
||||
if (avcodec_parameters_to_context(codec_ctx, params) < 0) {
|
||||
LOGE("Could not fill codec context");
|
||||
goto free_codec_ctx;
|
||||
}
|
||||
|
||||
if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
|
||||
LOGE("Could not open image codec");
|
||||
goto free_codec_ctx;
|
||||
}
|
||||
|
||||
AVFrame *frame = av_frame_alloc();
|
||||
if (!frame) {
|
||||
LOGE("Could not allocate frame");
|
||||
goto close_codec;
|
||||
}
|
||||
|
||||
AVPacket *packet = av_packet_alloc();
|
||||
if (!packet) {
|
||||
LOGE("Could not allocate packet");
|
||||
av_frame_free(&frame);
|
||||
goto close_codec;
|
||||
}
|
||||
|
||||
if (av_read_frame(ctx, packet) < 0) {
|
||||
LOGE("Could not read frame");
|
||||
av_packet_free(&packet);
|
||||
av_frame_free(&frame);
|
||||
goto close_codec;
|
||||
}
|
||||
|
||||
int ret;
|
||||
if ((ret = avcodec_send_packet(codec_ctx, packet)) < 0) {
|
||||
LOGE("Could not send icon packet: %d", ret);
|
||||
av_packet_free(&packet);
|
||||
av_frame_free(&frame);
|
||||
goto close_codec;
|
||||
}
|
||||
|
||||
if ((ret = avcodec_receive_frame(codec_ctx, frame)) != 0) {
|
||||
LOGE("Could not receive icon frame: %d", ret);
|
||||
av_packet_free(&packet);
|
||||
av_frame_free(&frame);
|
||||
goto close_codec;
|
||||
}
|
||||
|
||||
av_packet_free(&packet);
|
||||
|
||||
result = frame;
|
||||
|
||||
close_codec:
|
||||
avcodec_close(codec_ctx);
|
||||
free_codec_ctx:
|
||||
avcodec_free_context(&codec_ctx);
|
||||
close_input:
|
||||
avformat_close_input(&ctx);
|
||||
free_ctx:
|
||||
avformat_free_context(ctx);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static SDL_PixelFormatEnum
|
||||
to_sdl_pixel_format(enum AVPixelFormat fmt) {
|
||||
switch (fmt) {
|
||||
case AV_PIX_FMT_RGB24: return SDL_PIXELFORMAT_RGB24;
|
||||
case AV_PIX_FMT_BGR24: return SDL_PIXELFORMAT_BGR24;
|
||||
case AV_PIX_FMT_ARGB: return SDL_PIXELFORMAT_ARGB32;
|
||||
case AV_PIX_FMT_RGBA: return SDL_PIXELFORMAT_RGBA32;
|
||||
case AV_PIX_FMT_ABGR: return SDL_PIXELFORMAT_ABGR32;
|
||||
case AV_PIX_FMT_BGRA: return SDL_PIXELFORMAT_BGRA32;
|
||||
case AV_PIX_FMT_RGB565BE: return SDL_PIXELFORMAT_RGB565;
|
||||
case AV_PIX_FMT_RGB555BE: return SDL_PIXELFORMAT_RGB555;
|
||||
case AV_PIX_FMT_BGR565BE: return SDL_PIXELFORMAT_BGR565;
|
||||
case AV_PIX_FMT_BGR555BE: return SDL_PIXELFORMAT_BGR555;
|
||||
case AV_PIX_FMT_RGB444BE: return SDL_PIXELFORMAT_RGB444;
|
||||
case AV_PIX_FMT_BGR444BE: return SDL_PIXELFORMAT_BGR444;
|
||||
case AV_PIX_FMT_PAL8: return SDL_PIXELFORMAT_INDEX8;
|
||||
default: return SDL_PIXELFORMAT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_Surface *
|
||||
load_from_path(const char *path) {
|
||||
AVFrame *frame = decode_image(path);
|
||||
if (!frame) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
|
||||
if (!desc) {
|
||||
LOGE("Could not get icon format descriptor");
|
||||
goto error;
|
||||
}
|
||||
|
||||
bool is_packed = !(desc->flags & AV_PIX_FMT_FLAG_PLANAR);
|
||||
if (!is_packed) {
|
||||
LOGE("Could not load non-packed icon");
|
||||
goto error;
|
||||
}
|
||||
|
||||
SDL_PixelFormatEnum format = to_sdl_pixel_format(frame->format);
|
||||
if (format == SDL_PIXELFORMAT_UNKNOWN) {
|
||||
LOGE("Unsupported icon pixel format: %s (%d)", desc->name,
|
||||
frame->format);
|
||||
goto error;
|
||||
}
|
||||
|
||||
int bits_per_pixel = av_get_bits_per_pixel(desc);
|
||||
SDL_Surface *surface =
|
||||
SDL_CreateRGBSurfaceWithFormatFrom(frame->data[0],
|
||||
frame->width, frame->height,
|
||||
bits_per_pixel,
|
||||
frame->linesize[0],
|
||||
format);
|
||||
|
||||
if (!surface) {
|
||||
LOGE("Could not create icon surface");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (frame->format == AV_PIX_FMT_PAL8) {
|
||||
// Initialize the SDL palette
|
||||
uint8_t *data = frame->data[1];
|
||||
SDL_Color colors[256];
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
SDL_Color *color = &colors[i];
|
||||
|
||||
// The palette is transported in AVFrame.data[1], is 1024 bytes
|
||||
// long (256 4-byte entries) and is formatted the same as in
|
||||
// AV_PIX_FMT_RGB32 described above (i.e., it is also
|
||||
// endian-specific).
|
||||
// <https://ffmpeg.org/doxygen/4.1/pixfmt_8h.html#a9a8e335cf3be472042bc9f0cf80cd4c5>
|
||||
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
||||
color->a = data[i * 4];
|
||||
color->r = data[i * 4 + 1];
|
||||
color->g = data[i * 4 + 2];
|
||||
color->b = data[i * 4 + 3];
|
||||
#else
|
||||
color->a = data[i * 4 + 3];
|
||||
color->r = data[i * 4 + 2];
|
||||
color->g = data[i * 4 + 1];
|
||||
color->b = data[i * 4];
|
||||
#endif
|
||||
}
|
||||
|
||||
SDL_Palette *palette = surface->format->palette;
|
||||
assert(palette);
|
||||
int ret = SDL_SetPaletteColors(palette, colors, 0, 256);
|
||||
if (ret) {
|
||||
LOGE("Could not set palette colors");
|
||||
SDL_FreeSurface(surface);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
surface->userdata = frame; // frame owns the data
|
||||
|
||||
return surface;
|
||||
|
||||
error:
|
||||
av_frame_free(&frame);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDL_Surface *
|
||||
scrcpy_icon_load() {
|
||||
SDL_Surface *icon = NULL;
|
||||
|
||||
char *icon_path = get_envvar_icon_path();
|
||||
if (icon_path) {
|
||||
LOGD("Using SCRCPY_ICON_PATH: %s", icon_path);
|
||||
// An icon path is forced by environment variable
|
||||
icon = load_from_path(icon_path);
|
||||
free(icon_path);
|
||||
// Do not fallback on default path even if icon is NULL
|
||||
return icon;
|
||||
}
|
||||
|
||||
#ifndef PORTABLE
|
||||
LOGD("Using icon: " SCRCPY_DEFAULT_ICON_PATH);
|
||||
icon = load_from_path(SCRCPY_DEFAULT_ICON_PATH);
|
||||
#else
|
||||
icon_path = get_local_file_path(SCRCPY_PORTABLE_ICON_FILENAME);
|
||||
if (!icon_path) {
|
||||
LOGE("Could not get icon path");
|
||||
}
|
||||
LOGD("Using icon (portable): %s", icon_path);
|
||||
icon = load_from_path(icon_path);
|
||||
free(icon_path);
|
||||
#endif
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
void
|
||||
scrcpy_icon_destroy(SDL_Surface *icon) {
|
||||
AVFrame *frame = icon->userdata;
|
||||
assert(frame);
|
||||
av_frame_free(&frame);
|
||||
SDL_FreeSurface(icon);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#ifndef ICON_H
|
||||
#define ICON_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <libavformat/avformat.h>
|
||||
|
||||
SDL_Surface *
|
||||
scrcpy_icon_load(void);
|
||||
|
||||
void
|
||||
scrcpy_icon_destroy(SDL_Surface *icon);
|
||||
|
||||
#endif
|
||||
53
app/src/icon.xpm
Normal file
53
app/src/icon.xpm
Normal file
@@ -0,0 +1,53 @@
|
||||
/* XPM */
|
||||
static char * icon_xpm[] = {
|
||||
"48 48 2 1",
|
||||
" c None",
|
||||
". c #96C13E",
|
||||
" .. .. ",
|
||||
" ... ... ",
|
||||
" ... ...... ... ",
|
||||
" ................ ",
|
||||
" .............. ",
|
||||
" ................ ",
|
||||
" .................. ",
|
||||
" .................... ",
|
||||
" ..... ........ ..... ",
|
||||
" ..... ........ ..... ",
|
||||
" ...................... ",
|
||||
" ........................ ",
|
||||
" ........................ ",
|
||||
" ........................ ",
|
||||
" ",
|
||||
" ",
|
||||
" .... ........................ .... ",
|
||||
" ...... ........................ ...... ",
|
||||
" ...... ........................ ...... ",
|
||||
" ...... ........................ ...... ",
|
||||
" ...... ........................ ...... ",
|
||||
" ...... ........................ ...... ",
|
||||
" ...... ........................ ...... ",
|
||||
" ...... ........................ ...... ",
|
||||
" ...... ........................ ...... ",
|
||||
" ...... ........................ ...... ",
|
||||
" ...... ........................ ...... ",
|
||||
" ...... ........................ ...... ",
|
||||
" ...... ........................ ...... ",
|
||||
" ...... ........................ ...... ",
|
||||
" ...... ........................ ...... ",
|
||||
" ...... ........................ ...... ",
|
||||
" ...... ........................ ...... ",
|
||||
" ...... ........................ ...... ",
|
||||
" ...... ........................ ...... ",
|
||||
" .... ........................ .... ",
|
||||
" ........................ ",
|
||||
" ...................... ",
|
||||
" ...... ...... ",
|
||||
" ...... ...... ",
|
||||
" ...... ...... ",
|
||||
" ...... ...... ",
|
||||
" ...... ...... ",
|
||||
" ...... ...... ",
|
||||
" ...... ...... ",
|
||||
" ...... ...... ",
|
||||
" ...... ...... ",
|
||||
" .... .... "};
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <assert.h>
|
||||
#include <SDL2/SDL_keycode.h>
|
||||
|
||||
#include "event_converter.h"
|
||||
#include "util/log.h"
|
||||
|
||||
static const int ACTION_DOWN = 1;
|
||||
@@ -53,15 +52,18 @@ is_shortcut_mod(struct input_manager *im, uint16_t sdl_mod) {
|
||||
|
||||
void
|
||||
input_manager_init(struct input_manager *im, struct controller *controller,
|
||||
struct screen *screen,
|
||||
struct screen *screen, struct sc_key_processor *kp,
|
||||
struct sc_mouse_processor *mp,
|
||||
const struct scrcpy_options *options) {
|
||||
assert(!options->control || (kp && kp->ops));
|
||||
assert(!options->control || (mp && mp->ops));
|
||||
|
||||
im->controller = controller;
|
||||
im->screen = screen;
|
||||
im->repeat = 0;
|
||||
im->kp = kp;
|
||||
im->mp = mp;
|
||||
|
||||
im->control = options->control;
|
||||
im->forward_key_repeat = options->forward_key_repeat;
|
||||
im->prefer_text = options->prefer_text;
|
||||
im->forward_all_clicks = options->forward_all_clicks;
|
||||
im->legacy_paste = options->legacy_paste;
|
||||
|
||||
@@ -323,26 +325,8 @@ input_manager_process_text_input(struct input_manager *im,
|
||||
// A shortcut must never generate text events
|
||||
return;
|
||||
}
|
||||
if (!im->prefer_text) {
|
||||
char c = event->text[0];
|
||||
if (isalpha(c) || c == ' ') {
|
||||
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 = strdup(event->text);
|
||||
if (!msg.inject_text.text) {
|
||||
LOGW("Could not strdup input text");
|
||||
return;
|
||||
}
|
||||
if (!controller_push_msg(im->controller, &msg)) {
|
||||
free(msg.inject_text.text);
|
||||
LOGW("Could not request 'inject text'");
|
||||
}
|
||||
im->kp->ops->process_text(im->kp, event);
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -375,27 +359,6 @@ inverse_point(struct point point, struct size size) {
|
||||
return point;
|
||||
}
|
||||
|
||||
static bool
|
||||
convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to,
|
||||
bool prefer_text, uint32_t repeat) {
|
||||
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,
|
||||
prefer_text)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
to->inject_keycode.repeat = repeat;
|
||||
to->inject_keycode.metastate = convert_meta_state(mod);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
input_manager_process_key(struct input_manager *im,
|
||||
const SDL_KeyboardEvent *event) {
|
||||
@@ -549,15 +512,6 @@ input_manager_process_key(struct input_manager *im,
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->repeat) {
|
||||
if (!im->forward_key_repeat) {
|
||||
return;
|
||||
}
|
||||
++im->repeat;
|
||||
} else {
|
||||
im->repeat = 0;
|
||||
}
|
||||
|
||||
if (ctrl && !shift && keycode == SDLK_v && down && !repeat) {
|
||||
if (im->legacy_paste) {
|
||||
// inject the text as input events
|
||||
@@ -569,27 +523,7 @@ input_manager_process_key(struct input_manager *im,
|
||||
set_device_clipboard(controller, false);
|
||||
}
|
||||
|
||||
struct control_msg msg;
|
||||
if (convert_input_key(event, &msg, im->prefer_text, im->repeat)) {
|
||||
if (!controller_push_msg(controller, &msg)) {
|
||||
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 =
|
||||
screen_convert_window_to_frame_coords(screen, from->x, from->y);
|
||||
to->inject_touch_event.pressure = 1.f;
|
||||
to->inject_touch_event.buttons = convert_mouse_buttons(from->state);
|
||||
|
||||
return true;
|
||||
im->kp->ops->process_key(im->kp, event);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -607,79 +541,22 @@ input_manager_process_mouse_motion(struct input_manager *im,
|
||||
// simulated from touch events, so it's a duplicate
|
||||
return;
|
||||
}
|
||||
struct control_msg msg;
|
||||
if (!convert_mouse_motion(event, im->screen, &msg)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!controller_push_msg(im->controller, &msg)) {
|
||||
LOGW("Could not request 'inject mouse motion event'");
|
||||
}
|
||||
im->mp->ops->process_mouse_motion(im->mp, event);
|
||||
|
||||
if (im->vfinger_down) {
|
||||
struct point mouse = msg.inject_touch_event.position.point;
|
||||
struct point mouse =
|
||||
screen_convert_window_to_frame_coords(im->screen, event->x,
|
||||
event->y);
|
||||
struct point vfinger = inverse_point(mouse, im->screen->frame_size);
|
||||
simulate_virtual_finger(im, AMOTION_EVENT_ACTION_MOVE, vfinger);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
to->inject_touch_event.pointer_id = from->fingerId;
|
||||
to->inject_touch_event.position.screen_size = screen->frame_size;
|
||||
|
||||
int dw;
|
||||
int dh;
|
||||
SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
|
||||
|
||||
// SDL touch event coordinates are normalized in the range [0; 1]
|
||||
int32_t x = from->x * dw;
|
||||
int32_t y = from->y * dh;
|
||||
to->inject_touch_event.position.point =
|
||||
screen_convert_drawable_to_frame_coords(screen, x, y);
|
||||
|
||||
to->inject_touch_event.pressure = from->pressure;
|
||||
to->inject_touch_event.buttons = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
input_manager_process_touch(struct input_manager *im,
|
||||
const SDL_TouchFingerEvent *event) {
|
||||
struct control_msg msg;
|
||||
if (convert_touch(event, im->screen, &msg)) {
|
||||
if (!controller_push_msg(im->controller, &msg)) {
|
||||
LOGW("Could not request 'inject touch event'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 =
|
||||
screen_convert_window_to_frame_coords(screen, from->x, from->y);
|
||||
to->inject_touch_event.pressure =
|
||||
from->type == SDL_MOUSEBUTTONDOWN ? 1.f : 0.f;
|
||||
to->inject_touch_event.buttons =
|
||||
convert_mouse_buttons(SDL_BUTTON(from->button));
|
||||
|
||||
return true;
|
||||
im->mp->ops->process_touch(im->mp, event);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -739,15 +616,7 @@ input_manager_process_mouse_button(struct input_manager *im,
|
||||
return;
|
||||
}
|
||||
|
||||
struct control_msg msg;
|
||||
if (!convert_mouse_button(event, im->screen, &msg)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!controller_push_msg(im->controller, &msg)) {
|
||||
LOGW("Could not request 'inject mouse button event'");
|
||||
return;
|
||||
}
|
||||
im->mp->ops->process_mouse_button(im->mp, event);
|
||||
|
||||
// Pinch-to-zoom simulation.
|
||||
//
|
||||
@@ -761,7 +630,9 @@ input_manager_process_mouse_button(struct input_manager *im,
|
||||
#define CTRL_PRESSED (SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL))
|
||||
if ((down && !im->vfinger_down && CTRL_PRESSED)
|
||||
|| (!down && im->vfinger_down)) {
|
||||
struct point mouse = msg.inject_touch_event.position.point;
|
||||
struct point mouse =
|
||||
screen_convert_window_to_frame_coords(im->screen, event->x,
|
||||
event->y);
|
||||
struct point vfinger = inverse_point(mouse, im->screen->frame_size);
|
||||
enum android_motionevent_action action = down
|
||||
? AMOTION_EVENT_ACTION_DOWN
|
||||
@@ -773,39 +644,10 @@ input_manager_process_mouse_button(struct input_manager *im,
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct screen *screen,
|
||||
struct control_msg *to) {
|
||||
|
||||
// mouse_x and mouse_y are expressed in pixels relative to the window
|
||||
int mouse_x;
|
||||
int mouse_y;
|
||||
SDL_GetMouseState(&mouse_x, &mouse_y);
|
||||
|
||||
struct position position = {
|
||||
.screen_size = screen->frame_size,
|
||||
.point = screen_convert_window_to_frame_coords(screen,
|
||||
mouse_x, mouse_y),
|
||||
};
|
||||
|
||||
to->type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT;
|
||||
|
||||
to->inject_scroll_event.position = position;
|
||||
to->inject_scroll_event.hscroll = from->x;
|
||||
to->inject_scroll_event.vscroll = from->y;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
input_manager_process_mouse_wheel(struct input_manager *im,
|
||||
const SDL_MouseWheelEvent *event) {
|
||||
struct control_msg msg;
|
||||
if (convert_mouse_wheel(event, im->screen, &msg)) {
|
||||
if (!controller_push_msg(im->controller, &msg)) {
|
||||
LOGW("Could not request 'inject mouse wheel event'");
|
||||
}
|
||||
}
|
||||
im->mp->ops->process_mouse_wheel(im->mp, event);
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -11,18 +11,17 @@
|
||||
#include "fps_counter.h"
|
||||
#include "scrcpy.h"
|
||||
#include "screen.h"
|
||||
#include "trait/key_processor.h"
|
||||
#include "trait/mouse_processor.h"
|
||||
|
||||
struct input_manager {
|
||||
struct controller *controller;
|
||||
struct screen *screen;
|
||||
|
||||
// SDL reports repeated events as a boolean, but Android expects the actual
|
||||
// number of repetitions. This variable keeps track of the count.
|
||||
unsigned repeat;
|
||||
struct sc_key_processor *kp;
|
||||
struct sc_mouse_processor *mp;
|
||||
|
||||
bool control;
|
||||
bool forward_key_repeat;
|
||||
bool prefer_text;
|
||||
bool forward_all_clicks;
|
||||
bool legacy_paste;
|
||||
|
||||
@@ -43,7 +42,9 @@ struct input_manager {
|
||||
|
||||
void
|
||||
input_manager_init(struct input_manager *im, struct controller *controller,
|
||||
struct screen *screen, const struct scrcpy_options *options);
|
||||
struct screen *screen, struct sc_key_processor *kp,
|
||||
struct sc_mouse_processor *mp,
|
||||
const struct scrcpy_options *options);
|
||||
|
||||
bool
|
||||
input_manager_handle_event(struct input_manager *im, SDL_Event *event);
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
#include "event_converter.h"
|
||||
#include "keyboard_inject.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <SDL2/SDL_events.h>
|
||||
|
||||
#include "android/input.h"
|
||||
#include "control_msg.h"
|
||||
#include "controller.h"
|
||||
#include "util/log.h"
|
||||
|
||||
/** Downcast key processor to sc_keyboard_inject */
|
||||
#define DOWNCAST(KP) \
|
||||
container_of(KP, struct sc_keyboard_inject, key_processor)
|
||||
|
||||
#define MAP(FROM, TO) case FROM: *to = TO; return true
|
||||
#define FAIL default: return false
|
||||
|
||||
bool
|
||||
static bool
|
||||
convert_keycode_action(SDL_EventType from, enum android_keyevent_action *to) {
|
||||
switch (from) {
|
||||
MAP(SDL_KEYDOWN, AKEY_EVENT_ACTION_DOWN);
|
||||
@@ -12,67 +23,7 @@ convert_keycode_action(SDL_EventType from, enum android_keyevent_action *to) {
|
||||
}
|
||||
}
|
||||
|
||||
static enum android_metastate
|
||||
autocomplete_metastate(enum android_metastate metastate) {
|
||||
// fill dependent flags
|
||||
if (metastate & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
|
||||
metastate |= AMETA_SHIFT_ON;
|
||||
}
|
||||
if (metastate & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
|
||||
metastate |= AMETA_CTRL_ON;
|
||||
}
|
||||
if (metastate & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
|
||||
metastate |= AMETA_ALT_ON;
|
||||
}
|
||||
if (metastate & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
|
||||
metastate |= AMETA_META_ON;
|
||||
}
|
||||
|
||||
return metastate;
|
||||
}
|
||||
|
||||
enum android_metastate
|
||||
convert_meta_state(SDL_Keymod mod) {
|
||||
enum android_metastate metastate = 0;
|
||||
if (mod & KMOD_LSHIFT) {
|
||||
metastate |= AMETA_SHIFT_LEFT_ON;
|
||||
}
|
||||
if (mod & KMOD_RSHIFT) {
|
||||
metastate |= AMETA_SHIFT_RIGHT_ON;
|
||||
}
|
||||
if (mod & KMOD_LCTRL) {
|
||||
metastate |= AMETA_CTRL_LEFT_ON;
|
||||
}
|
||||
if (mod & KMOD_RCTRL) {
|
||||
metastate |= AMETA_CTRL_RIGHT_ON;
|
||||
}
|
||||
if (mod & KMOD_LALT) {
|
||||
metastate |= AMETA_ALT_LEFT_ON;
|
||||
}
|
||||
if (mod & KMOD_RALT) {
|
||||
metastate |= AMETA_ALT_RIGHT_ON;
|
||||
}
|
||||
if (mod & KMOD_LGUI) { // Windows key
|
||||
metastate |= AMETA_META_LEFT_ON;
|
||||
}
|
||||
if (mod & KMOD_RGUI) { // Windows key
|
||||
metastate |= AMETA_META_RIGHT_ON;
|
||||
}
|
||||
if (mod & KMOD_NUM) {
|
||||
metastate |= AMETA_NUM_LOCK_ON;
|
||||
}
|
||||
if (mod & KMOD_CAPS) {
|
||||
metastate |= AMETA_CAPS_LOCK_ON;
|
||||
}
|
||||
if (mod & KMOD_MODE) { // Alt Gr
|
||||
// no mapping?
|
||||
}
|
||||
|
||||
// fill the dependent fields
|
||||
return autocomplete_metastate(metastate);
|
||||
}
|
||||
|
||||
bool
|
||||
static bool
|
||||
convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod,
|
||||
bool prefer_text) {
|
||||
switch (from) {
|
||||
@@ -154,42 +105,150 @@ convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod,
|
||||
}
|
||||
}
|
||||
|
||||
enum android_motionevent_buttons
|
||||
convert_mouse_buttons(uint32_t state) {
|
||||
enum android_motionevent_buttons buttons = 0;
|
||||
if (state & SDL_BUTTON_LMASK) {
|
||||
buttons |= AMOTION_EVENT_BUTTON_PRIMARY;
|
||||
static enum android_metastate
|
||||
autocomplete_metastate(enum android_metastate metastate) {
|
||||
// fill dependent flags
|
||||
if (metastate & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
|
||||
metastate |= AMETA_SHIFT_ON;
|
||||
}
|
||||
if (state & SDL_BUTTON_RMASK) {
|
||||
buttons |= AMOTION_EVENT_BUTTON_SECONDARY;
|
||||
if (metastate & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
|
||||
metastate |= AMETA_CTRL_ON;
|
||||
}
|
||||
if (state & SDL_BUTTON_MMASK) {
|
||||
buttons |= AMOTION_EVENT_BUTTON_TERTIARY;
|
||||
if (metastate & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
|
||||
metastate |= AMETA_ALT_ON;
|
||||
}
|
||||
if (state & SDL_BUTTON_X1MASK) {
|
||||
buttons |= AMOTION_EVENT_BUTTON_BACK;
|
||||
if (metastate & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
|
||||
metastate |= AMETA_META_ON;
|
||||
}
|
||||
if (state & SDL_BUTTON_X2MASK) {
|
||||
buttons |= AMOTION_EVENT_BUTTON_FORWARD;
|
||||
}
|
||||
return buttons;
|
||||
|
||||
return metastate;
|
||||
}
|
||||
|
||||
bool
|
||||
convert_mouse_action(SDL_EventType from, enum android_motionevent_action *to) {
|
||||
switch (from) {
|
||||
MAP(SDL_MOUSEBUTTONDOWN, AMOTION_EVENT_ACTION_DOWN);
|
||||
MAP(SDL_MOUSEBUTTONUP, AMOTION_EVENT_ACTION_UP);
|
||||
FAIL;
|
||||
static enum android_metastate
|
||||
convert_meta_state(SDL_Keymod mod) {
|
||||
enum android_metastate metastate = 0;
|
||||
if (mod & KMOD_LSHIFT) {
|
||||
metastate |= AMETA_SHIFT_LEFT_ON;
|
||||
}
|
||||
if (mod & KMOD_RSHIFT) {
|
||||
metastate |= AMETA_SHIFT_RIGHT_ON;
|
||||
}
|
||||
if (mod & KMOD_LCTRL) {
|
||||
metastate |= AMETA_CTRL_LEFT_ON;
|
||||
}
|
||||
if (mod & KMOD_RCTRL) {
|
||||
metastate |= AMETA_CTRL_RIGHT_ON;
|
||||
}
|
||||
if (mod & KMOD_LALT) {
|
||||
metastate |= AMETA_ALT_LEFT_ON;
|
||||
}
|
||||
if (mod & KMOD_RALT) {
|
||||
metastate |= AMETA_ALT_RIGHT_ON;
|
||||
}
|
||||
if (mod & KMOD_LGUI) { // Windows key
|
||||
metastate |= AMETA_META_LEFT_ON;
|
||||
}
|
||||
if (mod & KMOD_RGUI) { // Windows key
|
||||
metastate |= AMETA_META_RIGHT_ON;
|
||||
}
|
||||
if (mod & KMOD_NUM) {
|
||||
metastate |= AMETA_NUM_LOCK_ON;
|
||||
}
|
||||
if (mod & KMOD_CAPS) {
|
||||
metastate |= AMETA_CAPS_LOCK_ON;
|
||||
}
|
||||
if (mod & KMOD_MODE) { // Alt Gr
|
||||
// no mapping?
|
||||
}
|
||||
|
||||
// fill the dependent fields
|
||||
return autocomplete_metastate(metastate);
|
||||
}
|
||||
|
||||
static bool
|
||||
convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to,
|
||||
bool prefer_text, uint32_t repeat) {
|
||||
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,
|
||||
prefer_text)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
to->inject_keycode.repeat = repeat;
|
||||
to->inject_keycode.metastate = convert_meta_state(mod);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
sc_key_processor_process_key(struct sc_key_processor *kp,
|
||||
const SDL_KeyboardEvent *event) {
|
||||
struct sc_keyboard_inject *ki = DOWNCAST(kp);
|
||||
|
||||
if (event->repeat) {
|
||||
if (!ki->forward_key_repeat) {
|
||||
return;
|
||||
}
|
||||
++ki->repeat;
|
||||
} else {
|
||||
ki->repeat = 0;
|
||||
}
|
||||
|
||||
struct control_msg msg;
|
||||
if (convert_input_key(event, &msg, ki->prefer_text, ki->repeat)) {
|
||||
if (!controller_push_msg(ki->controller, &msg)) {
|
||||
LOGW("Could not request 'inject keycode'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
convert_touch_action(SDL_EventType from, enum android_motionevent_action *to) {
|
||||
switch (from) {
|
||||
MAP(SDL_FINGERMOTION, AMOTION_EVENT_ACTION_MOVE);
|
||||
MAP(SDL_FINGERDOWN, AMOTION_EVENT_ACTION_DOWN);
|
||||
MAP(SDL_FINGERUP, AMOTION_EVENT_ACTION_UP);
|
||||
FAIL;
|
||||
static void
|
||||
sc_key_processor_process_text(struct sc_key_processor *kp,
|
||||
const SDL_TextInputEvent *event) {
|
||||
struct sc_keyboard_inject *ki = DOWNCAST(kp);
|
||||
|
||||
if (!ki->prefer_text) {
|
||||
char c = event->text[0];
|
||||
if (isalpha(c) || c == ' ') {
|
||||
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 = strdup(event->text);
|
||||
if (!msg.inject_text.text) {
|
||||
LOGW("Could not strdup input text");
|
||||
return;
|
||||
}
|
||||
if (!controller_push_msg(ki->controller, &msg)) {
|
||||
free(msg.inject_text.text);
|
||||
LOGW("Could not request 'inject text'");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sc_keyboard_inject_init(struct sc_keyboard_inject *ki,
|
||||
struct controller *controller,
|
||||
const struct scrcpy_options *options) {
|
||||
ki->controller = controller;
|
||||
ki->prefer_text = options->prefer_text;
|
||||
ki->forward_key_repeat = options->forward_key_repeat;
|
||||
|
||||
ki->repeat = 0;
|
||||
|
||||
static const struct sc_key_processor_ops ops = {
|
||||
.process_key = sc_key_processor_process_key,
|
||||
.process_text = sc_key_processor_process_text,
|
||||
};
|
||||
|
||||
ki->key_processor.ops = &ops;
|
||||
}
|
||||
30
app/src/keyboard_inject.h
Normal file
30
app/src/keyboard_inject.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef SC_KEYBOARD_INJECT_H
|
||||
#define SC_KEYBOARD_INJECT_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "controller.h"
|
||||
#include "scrcpy.h"
|
||||
#include "trait/key_processor.h"
|
||||
|
||||
struct sc_keyboard_inject {
|
||||
struct sc_key_processor key_processor; // key processor trait
|
||||
|
||||
struct controller *controller;
|
||||
|
||||
// SDL reports repeated events as a boolean, but Android expects the actual
|
||||
// number of repetitions. This variable keeps track of the count.
|
||||
unsigned repeat;
|
||||
|
||||
bool prefer_text;
|
||||
bool forward_key_repeat;
|
||||
};
|
||||
|
||||
void
|
||||
sc_keyboard_inject_init(struct sc_keyboard_inject *ki,
|
||||
struct controller *controller,
|
||||
const struct scrcpy_options *options);
|
||||
|
||||
#endif
|
||||
211
app/src/mouse_inject.c
Normal file
211
app/src/mouse_inject.c
Normal file
@@ -0,0 +1,211 @@
|
||||
#include "mouse_inject.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <SDL2/SDL_events.h>
|
||||
|
||||
#include "android/input.h"
|
||||
#include "control_msg.h"
|
||||
#include "controller.h"
|
||||
#include "util/log.h"
|
||||
|
||||
/** Downcast mouse processor to sc_mouse_inject */
|
||||
#define DOWNCAST(MP) container_of(MP, struct sc_mouse_inject, mouse_processor)
|
||||
|
||||
static enum android_motionevent_buttons
|
||||
convert_mouse_buttons(uint32_t state) {
|
||||
enum android_motionevent_buttons buttons = 0;
|
||||
if (state & SDL_BUTTON_LMASK) {
|
||||
buttons |= AMOTION_EVENT_BUTTON_PRIMARY;
|
||||
}
|
||||
if (state & SDL_BUTTON_RMASK) {
|
||||
buttons |= AMOTION_EVENT_BUTTON_SECONDARY;
|
||||
}
|
||||
if (state & SDL_BUTTON_MMASK) {
|
||||
buttons |= AMOTION_EVENT_BUTTON_TERTIARY;
|
||||
}
|
||||
if (state & SDL_BUTTON_X1MASK) {
|
||||
buttons |= AMOTION_EVENT_BUTTON_BACK;
|
||||
}
|
||||
if (state & SDL_BUTTON_X2MASK) {
|
||||
buttons |= AMOTION_EVENT_BUTTON_FORWARD;
|
||||
}
|
||||
return buttons;
|
||||
}
|
||||
|
||||
#define MAP(FROM, TO) case FROM: *to = TO; return true
|
||||
#define FAIL default: return false
|
||||
static bool
|
||||
convert_mouse_action(SDL_EventType from, enum android_motionevent_action *to) {
|
||||
switch (from) {
|
||||
MAP(SDL_MOUSEBUTTONDOWN, AMOTION_EVENT_ACTION_DOWN);
|
||||
MAP(SDL_MOUSEBUTTONUP, AMOTION_EVENT_ACTION_UP);
|
||||
FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
convert_touch_action(SDL_EventType from, enum android_motionevent_action *to) {
|
||||
switch (from) {
|
||||
MAP(SDL_FINGERMOTION, AMOTION_EVENT_ACTION_MOVE);
|
||||
MAP(SDL_FINGERDOWN, AMOTION_EVENT_ACTION_DOWN);
|
||||
MAP(SDL_FINGERUP, AMOTION_EVENT_ACTION_UP);
|
||||
FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
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 =
|
||||
screen_convert_window_to_frame_coords(screen, from->x, 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(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;
|
||||
}
|
||||
|
||||
to->inject_touch_event.pointer_id = from->fingerId;
|
||||
to->inject_touch_event.position.screen_size = screen->frame_size;
|
||||
|
||||
int dw;
|
||||
int dh;
|
||||
SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
|
||||
|
||||
// SDL touch event coordinates are normalized in the range [0; 1]
|
||||
int32_t x = from->x * dw;
|
||||
int32_t y = from->y * dh;
|
||||
to->inject_touch_event.position.point =
|
||||
screen_convert_drawable_to_frame_coords(screen, x, y);
|
||||
|
||||
to->inject_touch_event.pressure = from->pressure;
|
||||
to->inject_touch_event.buttons = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
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 =
|
||||
screen_convert_window_to_frame_coords(screen, from->x, from->y);
|
||||
to->inject_touch_event.pressure =
|
||||
from->type == SDL_MOUSEBUTTONDOWN ? 1.f : 0.f;
|
||||
to->inject_touch_event.buttons =
|
||||
convert_mouse_buttons(SDL_BUTTON(from->button));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
convert_mouse_wheel(const SDL_MouseWheelEvent *from, struct screen *screen,
|
||||
struct control_msg *to) {
|
||||
|
||||
// mouse_x and mouse_y are expressed in pixels relative to the window
|
||||
int mouse_x;
|
||||
int mouse_y;
|
||||
SDL_GetMouseState(&mouse_x, &mouse_y);
|
||||
|
||||
struct position position = {
|
||||
.screen_size = screen->frame_size,
|
||||
.point = screen_convert_window_to_frame_coords(screen,
|
||||
mouse_x, mouse_y),
|
||||
};
|
||||
|
||||
to->type = CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT;
|
||||
|
||||
to->inject_scroll_event.position = position;
|
||||
to->inject_scroll_event.hscroll = from->x;
|
||||
to->inject_scroll_event.vscroll = from->y;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
sc_mouse_processor_process_mouse_motion(struct sc_mouse_processor *mp,
|
||||
const SDL_MouseMotionEvent *event) {
|
||||
struct sc_mouse_inject *mi = DOWNCAST(mp);
|
||||
|
||||
struct control_msg msg;
|
||||
if (!convert_mouse_motion(event, mi->screen, &msg)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!controller_push_msg(mi->controller, &msg)) {
|
||||
LOGW("Could not request 'inject mouse motion event'");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sc_mouse_processor_process_touch(struct sc_mouse_processor *mp,
|
||||
const SDL_TouchFingerEvent *event) {
|
||||
struct sc_mouse_inject *mi = DOWNCAST(mp);
|
||||
|
||||
struct control_msg msg;
|
||||
if (convert_touch(event, mi->screen, &msg)) {
|
||||
if (!controller_push_msg(mi->controller, &msg)) {
|
||||
LOGW("Could not request 'inject touch event'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sc_mouse_processor_process_mouse_button(struct sc_mouse_processor *mp,
|
||||
const SDL_MouseButtonEvent *event) {
|
||||
struct sc_mouse_inject *mi = DOWNCAST(mp);
|
||||
|
||||
struct control_msg msg;
|
||||
if (convert_mouse_button(event, mi->screen, &msg)) {
|
||||
if (!controller_push_msg(mi->controller, &msg)) {
|
||||
LOGW("Could not request 'inject mouse button event'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sc_mouse_processor_process_mouse_wheel(struct sc_mouse_processor *mp,
|
||||
const SDL_MouseWheelEvent *event) {
|
||||
struct sc_mouse_inject *mi = DOWNCAST(mp);
|
||||
|
||||
struct control_msg msg;
|
||||
if (convert_mouse_wheel(event, mi->screen, &msg)) {
|
||||
if (!controller_push_msg(mi->controller, &msg)) {
|
||||
LOGW("Could not request 'inject mouse wheel event'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sc_mouse_inject_init(struct sc_mouse_inject *mi, struct controller *controller,
|
||||
struct screen *screen) {
|
||||
mi->controller = controller;
|
||||
mi->screen = screen;
|
||||
|
||||
static const struct sc_mouse_processor_ops ops = {
|
||||
.process_mouse_motion = sc_mouse_processor_process_mouse_motion,
|
||||
.process_touch = sc_mouse_processor_process_touch,
|
||||
.process_mouse_button = sc_mouse_processor_process_mouse_button,
|
||||
.process_mouse_wheel = sc_mouse_processor_process_mouse_wheel,
|
||||
};
|
||||
|
||||
mi->mouse_processor.ops = &ops;
|
||||
}
|
||||
24
app/src/mouse_inject.h
Normal file
24
app/src/mouse_inject.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef SC_MOUSE_INJECT_H
|
||||
#define SC_MOUSE_INJECT_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "controller.h"
|
||||
#include "scrcpy.h"
|
||||
#include "screen.h"
|
||||
#include "trait/mouse_processor.h"
|
||||
|
||||
struct sc_mouse_inject {
|
||||
struct sc_mouse_processor mouse_processor; // mouse processor trait
|
||||
|
||||
struct controller *controller;
|
||||
struct screen *screen;
|
||||
};
|
||||
|
||||
void
|
||||
sc_mouse_inject_init(struct sc_mouse_inject *mi, struct controller *controller,
|
||||
struct screen *screen);
|
||||
|
||||
#endif
|
||||
@@ -18,10 +18,13 @@
|
||||
#include "events.h"
|
||||
#include "file_handler.h"
|
||||
#include "input_manager.h"
|
||||
#include "keyboard_inject.h"
|
||||
#include "mouse_inject.h"
|
||||
#include "recorder.h"
|
||||
#include "screen.h"
|
||||
#include "server.h"
|
||||
#include "stream.h"
|
||||
#include "tiny_xpm.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net.h"
|
||||
#ifdef HAVE_V4L2
|
||||
@@ -39,6 +42,8 @@ struct scrcpy {
|
||||
#endif
|
||||
struct controller controller;
|
||||
struct file_handler file_handler;
|
||||
struct sc_keyboard_inject keyboard_inject;
|
||||
struct sc_mouse_inject mouse_inject;
|
||||
struct input_manager input_manager;
|
||||
};
|
||||
|
||||
@@ -411,7 +416,19 @@ scrcpy(const struct scrcpy_options *options) {
|
||||
}
|
||||
stream_started = true;
|
||||
|
||||
input_manager_init(&s->input_manager, &s->controller, &s->screen, options);
|
||||
struct sc_key_processor *kp = NULL;
|
||||
struct sc_mouse_processor *mp = NULL;
|
||||
|
||||
if (options->control) {
|
||||
sc_keyboard_inject_init(&s->keyboard_inject, &s->controller, options);
|
||||
kp = &s->keyboard_inject.key_processor;
|
||||
|
||||
sc_mouse_inject_init(&s->mouse_inject, &s->controller, &s->screen);
|
||||
mp = &s->mouse_inject.mouse_processor;
|
||||
}
|
||||
|
||||
input_manager_init(&s->input_manager, &s->controller, &s->screen, kp, mp,
|
||||
options);
|
||||
|
||||
ret = event_loop(s, options);
|
||||
LOGD("quit...");
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "events.h"
|
||||
#include "icon.h"
|
||||
#include "icon.xpm"
|
||||
#include "scrcpy.h"
|
||||
#include "tiny_xpm.h"
|
||||
#include "video_buffer.h"
|
||||
#include "util/log.h"
|
||||
|
||||
@@ -404,10 +405,10 @@ screen_init(struct screen *screen, const struct screen_params *params) {
|
||||
LOGD("Trilinear filtering disabled (not an OpenGL renderer)");
|
||||
}
|
||||
|
||||
SDL_Surface *icon = scrcpy_icon_load();
|
||||
SDL_Surface *icon = read_xpm(icon_xpm);
|
||||
if (icon) {
|
||||
SDL_SetWindowIcon(screen->window, icon);
|
||||
scrcpy_icon_destroy(icon);
|
||||
SDL_FreeSurface(icon);
|
||||
} else {
|
||||
LOGW("Could not load icon");
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <SDL2/SDL_timer.h>
|
||||
#include <SDL2/SDL_platform.h>
|
||||
@@ -50,13 +51,48 @@ get_server_path(void) {
|
||||
// the absolute path is hardcoded
|
||||
return server_path;
|
||||
#else
|
||||
char *server_path = get_local_file_path(SERVER_FILENAME);
|
||||
if (!server_path) {
|
||||
LOGE("Could not get local file path, "
|
||||
|
||||
// use scrcpy-server in the same directory as the executable
|
||||
char *executable_path = get_executable_path();
|
||||
if (!executable_path) {
|
||||
LOGE("Could not get executable path, "
|
||||
"using " SERVER_FILENAME " from current directory");
|
||||
// not found, use current directory
|
||||
return strdup(SERVER_FILENAME);
|
||||
}
|
||||
|
||||
// dirname() does not work correctly everywhere, so get the parent
|
||||
// directory manually.
|
||||
// See <https://github.com/Genymobile/scrcpy/issues/2619>
|
||||
char *p = strrchr(executable_path, PATH_SEPARATOR);
|
||||
if (!p) {
|
||||
LOGE("Unexpected executable path: \"%s\" (it should contain a '%c')",
|
||||
executable_path, PATH_SEPARATOR);
|
||||
free(executable_path);
|
||||
return strdup(SERVER_FILENAME);
|
||||
}
|
||||
|
||||
*p = '\0'; // modify executable_path in place
|
||||
char *dir = executable_path;
|
||||
size_t dirlen = strlen(dir);
|
||||
|
||||
// sizeof(SERVER_FILENAME) gives statically the size including the null byte
|
||||
size_t len = dirlen + 1 + sizeof(SERVER_FILENAME);
|
||||
char *server_path = malloc(len);
|
||||
if (!server_path) {
|
||||
LOGE("Could not alloc server path string, "
|
||||
"using " SERVER_FILENAME " from current directory");
|
||||
free(executable_path);
|
||||
return strdup(SERVER_FILENAME);
|
||||
}
|
||||
|
||||
memcpy(server_path, dir, dirlen);
|
||||
server_path[dirlen] = PATH_SEPARATOR;
|
||||
memcpy(&server_path[dirlen + 1], SERVER_FILENAME, sizeof(SERVER_FILENAME));
|
||||
// the final null byte has been copied with SERVER_FILENAME
|
||||
|
||||
free(executable_path);
|
||||
|
||||
LOGD("Using server (portable): %s", server_path);
|
||||
return server_path;
|
||||
#endif
|
||||
|
||||
119
app/src/tiny_xpm.c
Normal file
119
app/src/tiny_xpm.c
Normal file
@@ -0,0 +1,119 @@
|
||||
#include "tiny_xpm.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
struct index {
|
||||
char c;
|
||||
uint32_t color;
|
||||
};
|
||||
|
||||
static bool
|
||||
find_color(struct index *index, int len, char c, uint32_t *color) {
|
||||
// there are typically very few color, so it's ok to iterate over the array
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (index[i].c == c) {
|
||||
*color = index[i].color;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
*color = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// We encounter some problems with SDL2_image on MSYS2 (Windows),
|
||||
// so here is our own XPM parsing not to depend on SDL_image.
|
||||
//
|
||||
// We do not hardcode the binary image to keep some flexibility to replace the
|
||||
// icon easily (just by replacing icon.xpm).
|
||||
//
|
||||
// Parameter is not "const char *" because XPM formats are generally stored in a
|
||||
// (non-const) "char *"
|
||||
SDL_Surface *
|
||||
read_xpm(char *xpm[]) {
|
||||
#ifndef NDEBUG
|
||||
// patch the XPM to change the icon color in debug mode
|
||||
xpm[2] = ". c #CC00CC";
|
||||
#endif
|
||||
|
||||
char *endptr;
|
||||
// *** No error handling, assume the XPM source is valid ***
|
||||
// (it's in our source repo)
|
||||
// Assertions are only checked in debug
|
||||
int width = strtol(xpm[0], &endptr, 10);
|
||||
int height = strtol(endptr + 1, &endptr, 10);
|
||||
int colors = strtol(endptr + 1, &endptr, 10);
|
||||
int chars = strtol(endptr + 1, &endptr, 10);
|
||||
|
||||
// sanity checks
|
||||
assert(0 <= width && width < 256);
|
||||
assert(0 <= height && height < 256);
|
||||
assert(0 <= colors && colors < 256);
|
||||
assert(chars == 1); // this implementation does not support more
|
||||
|
||||
(void) chars;
|
||||
|
||||
// init index
|
||||
struct index index[colors];
|
||||
for (int i = 0; i < colors; ++i) {
|
||||
const char *line = xpm[1+i];
|
||||
index[i].c = line[0];
|
||||
assert(line[1] == '\t');
|
||||
assert(line[2] == 'c');
|
||||
assert(line[3] == ' ');
|
||||
if (line[4] == '#') {
|
||||
index[i].color = 0xff000000 | strtol(&line[5], &endptr, 0x10);
|
||||
assert(*endptr == '\0');
|
||||
} else {
|
||||
assert(!strcmp("None", &line[4]));
|
||||
index[i].color = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// parse image
|
||||
uint32_t *pixels = SDL_malloc(4 * width * height);
|
||||
if (!pixels) {
|
||||
LOGE("Could not allocate icon memory");
|
||||
return NULL;
|
||||
}
|
||||
for (int y = 0; y < height; ++y) {
|
||||
const char *line = xpm[1 + colors + y];
|
||||
for (int x = 0; x < width; ++x) {
|
||||
char c = line[x];
|
||||
uint32_t color;
|
||||
bool color_found = find_color(index, colors, c, &color);
|
||||
assert(color_found);
|
||||
(void) color_found;
|
||||
pixels[y * width + x] = color;
|
||||
}
|
||||
}
|
||||
|
||||
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
||||
uint32_t amask = 0x000000ff;
|
||||
uint32_t rmask = 0x0000ff00;
|
||||
uint32_t gmask = 0x00ff0000;
|
||||
uint32_t bmask = 0xff000000;
|
||||
#else // little endian, like x86
|
||||
uint32_t amask = 0xff000000;
|
||||
uint32_t rmask = 0x00ff0000;
|
||||
uint32_t gmask = 0x0000ff00;
|
||||
uint32_t bmask = 0x000000ff;
|
||||
#endif
|
||||
|
||||
SDL_Surface *surface = SDL_CreateRGBSurfaceFrom(pixels,
|
||||
width, height,
|
||||
32, 4 * width,
|
||||
rmask, gmask, bmask, amask);
|
||||
if (!surface) {
|
||||
LOGE("Could not create icon surface");
|
||||
return NULL;
|
||||
}
|
||||
// make the surface own the raw pixels
|
||||
surface->flags &= ~SDL_PREALLOC;
|
||||
return surface;
|
||||
}
|
||||
11
app/src/tiny_xpm.h
Normal file
11
app/src/tiny_xpm.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef TINYXPM_H
|
||||
#define TINYXPM_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
SDL_Surface *
|
||||
read_xpm(char *xpm[]);
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef SC_FRAME_SINK
|
||||
#define SC_FRAME_SINK
|
||||
#ifndef SC_FRAME_SINK_H
|
||||
#define SC_FRAME_SINK_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
|
||||
29
app/src/trait/key_processor.h
Normal file
29
app/src/trait/key_processor.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef SC_KEY_PROCESSOR_H
|
||||
#define SC_KEY_PROCESSOR_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <SDL2/SDL_events.h>
|
||||
|
||||
/**
|
||||
* Key processor trait.
|
||||
*
|
||||
* Component able to process and inject keys should implement this trait.
|
||||
*/
|
||||
struct sc_key_processor {
|
||||
const struct sc_key_processor_ops *ops;
|
||||
};
|
||||
|
||||
struct sc_key_processor_ops {
|
||||
void
|
||||
(*process_key)(struct sc_key_processor *kp, const SDL_KeyboardEvent *event);
|
||||
|
||||
void
|
||||
(*process_text)(struct sc_key_processor *kp,
|
||||
const SDL_TextInputEvent *event);
|
||||
};
|
||||
|
||||
#endif
|
||||
39
app/src/trait/mouse_processor.h
Normal file
39
app/src/trait/mouse_processor.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef SC_MOUSE_PROCESSOR_H
|
||||
#define SC_MOUSE_PROCESSOR_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <SDL2/SDL_events.h>
|
||||
|
||||
/**
|
||||
* Mouse processor trait.
|
||||
*
|
||||
* Component able to process and inject mouse events should implement this
|
||||
* trait.
|
||||
*/
|
||||
struct sc_mouse_processor {
|
||||
const struct sc_mouse_processor_ops *ops;
|
||||
};
|
||||
|
||||
struct sc_mouse_processor_ops {
|
||||
void
|
||||
(*process_mouse_motion)(struct sc_mouse_processor *mp,
|
||||
const SDL_MouseMotionEvent *event);
|
||||
|
||||
void
|
||||
(*process_touch)(struct sc_mouse_processor *mp,
|
||||
const SDL_TouchFingerEvent *event);
|
||||
|
||||
void
|
||||
(*process_mouse_button)(struct sc_mouse_processor *mp,
|
||||
const SDL_MouseButtonEvent *event);
|
||||
|
||||
void
|
||||
(*process_mouse_wheel)(struct sc_mouse_processor *mp,
|
||||
const SDL_MouseWheelEvent *event);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef SC_PACKET_SINK
|
||||
#define SC_PACKET_SINK
|
||||
#ifndef SC_PACKET_SINK_H
|
||||
#define SC_PACKET_SINK_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
|
||||
@@ -116,8 +116,9 @@ net_send(socket_t socket, const void *buf, size_t len) {
|
||||
ssize_t
|
||||
net_send_all(socket_t socket, const void *buf, size_t len) {
|
||||
size_t copied = 0;
|
||||
ssize_t w = 0;
|
||||
while (len > 0) {
|
||||
ssize_t w = send(socket, buf, len, 0);
|
||||
w = send(socket, buf, len, 0);
|
||||
if (w == -1) {
|
||||
return copied ? (ssize_t) copied : -1;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "process.h"
|
||||
|
||||
#include <libgen.h>
|
||||
#include "log.h"
|
||||
|
||||
bool
|
||||
@@ -20,44 +19,3 @@ process_check_success(process_t proc, const char *name, bool close) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
char *
|
||||
get_local_file_path(const char *name) {
|
||||
char *executable_path = get_executable_path();
|
||||
if (!executable_path) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// dirname() does not work correctly everywhere, so get the parent
|
||||
// directory manually.
|
||||
// See <https://github.com/Genymobile/scrcpy/issues/2619>
|
||||
char *p = strrchr(executable_path, PATH_SEPARATOR);
|
||||
if (!p) {
|
||||
LOGE("Unexpected executable path: \"%s\" (it should contain a '%c')",
|
||||
executable_path, PATH_SEPARATOR);
|
||||
free(executable_path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*p = '\0'; // modify executable_path in place
|
||||
char *dir = executable_path;
|
||||
size_t dirlen = strlen(dir);
|
||||
size_t namelen = strlen(name);
|
||||
|
||||
size_t len = dirlen + namelen + 2; // +2: '/' and '\0'
|
||||
char *file_path = malloc(len);
|
||||
if (!file_path) {
|
||||
LOGE("Could not alloc path");
|
||||
free(executable_path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(file_path, dir, dirlen);
|
||||
file_path[dirlen] = PATH_SEPARATOR;
|
||||
// namelen + 1 to copy the final '\0'
|
||||
memcpy(&file_path[dirlen + 1], name, namelen + 1);
|
||||
|
||||
free(executable_path);
|
||||
|
||||
return file_path;
|
||||
}
|
||||
|
||||
@@ -74,11 +74,6 @@ search_executable(const char *file);
|
||||
char *
|
||||
get_executable_path(void);
|
||||
|
||||
// Return the absolute path of a file in the same directory as he executable.
|
||||
// May be NULL on error. To be freed by free().
|
||||
char *
|
||||
get_local_file_path(const char *name);
|
||||
|
||||
// returns true if the file exists and is not a directory
|
||||
bool
|
||||
is_regular_file(const char *path);
|
||||
|
||||
@@ -183,11 +183,8 @@ sc_v4l2_sink_open(struct sc_v4l2_sink *vs) {
|
||||
goto error_mutex_destroy;
|
||||
}
|
||||
|
||||
const AVOutputFormat *format = find_muxer("v4l2");
|
||||
if (!format) {
|
||||
// Alternative name
|
||||
format = find_muxer("video4linux2");
|
||||
}
|
||||
// FIXME
|
||||
const AVOutputFormat *format = find_muxer("video4linux2,v4l2");
|
||||
if (!format) {
|
||||
LOGE("Could not find v4l2 muxer");
|
||||
goto error_cond_destroy;
|
||||
|
||||
BIN
data/icon.png
BIN
data/icon.png
Binary file not shown.
|
Before Width: | Height: | Size: 6.4 KiB |
@@ -1,16 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" version="1.1">
|
||||
<path style="opacity:0.2" d="m 16.846877,12 c -1.116351,0 -2.227419,0.912229 -2.015075,2 l 3.122973,16 -5.596557,11.109375 C 11.959876,41.871734 11.885244,42.336988 12.177176,43 c 0.278672,0.632897 0.998812,1 1.747448,1 H 24 34.075375 c 0.748637,0 1.468777,-0.367103 1.747448,-1 0.291932,-0.663012 0.217302,-1.128266 -0.181041,-1.890625 L 30.045225,30 33.168198,14 c 0.212344,-1.087771 -0.898724,-2 -2.015075,-2 H 24 Z"/>
|
||||
<path style="fill:#cccccc" d="m 16.846877,11 c -1.116351,0 -2.227419,0.912229 -2.015075,2 l 3.122973,16 -5.596557,11.109375 C 11.959876,40.871734 11.885244,41.336988 12.177176,42 c 0.278672,0.632897 0.998812,1 1.747448,1 H 24 34.075375 c 0.748637,0 1.468777,-0.367103 1.747448,-1 0.291932,-0.663012 0.217302,-1.128266 -0.181041,-1.890625 L 30.045225,29 33.168198,13 c 0.212344,-1.087771 -0.898724,-2 -2.015075,-2 H 24 Z"/>
|
||||
<rect style="opacity:0.2" width="40" height="32" x="4" y="6" rx="2" ry="2"/>
|
||||
<path style="fill:#e4e4e4" d="m 4,33 v 2 c 0,1.108 0.892,2 2,2 h 36 c 1.108,0 2,-0.892 2,-2 v -2 z"/>
|
||||
<path style="opacity:0.1" d="m 11.494141,15 a 1.5,1.5 0 0 0 -0.832032,0.255859 1.5,1.5 0 0 0 -0.40625,2.082032 l 3.13086,4.654297 C 10.404945,24.606192 8.4012371,28.299159 8.0019531,32.460938 7.9284599,34.000879 9.5546875,34 9.5546875,34 H 38.40625 c 0,0 1.672856,-3.38e-4 1.591797,-1.617188 -0.416529,-4.131451 -2.415618,-7.796833 -5.380859,-10.394531 l 3.126953,-4.65039 a 1.5,1.5 0 0 0 -0.40625,-2.082032 1.5,1.5 0 0 0 -1.125,-0.228515 1.5,1.5 0 0 0 -0.957032,0.634765 l -3.072265,4.566407 C 29.78649,18.814887 26.990024,18 24.001953,18 c -2.989385,0 -5.786177,0.815488 -8.183594,2.230469 l -3.074218,-4.56836 A 1.5,1.5 0 0 0 11.787109,15.027344 1.5,1.5 0 0 0 11.494141,15 Z"/>
|
||||
<path style="fill:#077063" d="M 6,5 C 4.892,5 4,5.892 4,7 V 33 H 44 V 7 C 44,5.892 43.108,5 42,5 Z"/>
|
||||
<path style="opacity:0.1;fill:#ffffff" d="M 6,5 C 4.892,5 4,5.892 4,7 V 8 C 4,6.892 4.892,6 6,6 h 36 c 1.108,0 2,0.892 2,2 V 7 C 44,5.892 43.108,5 42,5 Z"/>
|
||||
<path style="fill:none;stroke:#30dd81;stroke-width:3;stroke-linecap:round" d="M 15.1998,21.000026 11.5,15.5"/>
|
||||
<path style="fill:none;stroke:#30dd81;stroke-width:3;stroke-linecap:round" d="M 32.799764,21.000026 36.5,15.5"/>
|
||||
<path style="fill:#30dd81" d="m 24.002386,17.000034 c -8.355868,0 -15.2214979,6.346843 -15.9999669,14.460906 C 7.9289259,33.000882 9.5544999,33 9.5544999,33 H 38.406003 c 0,0 1.672201,-3.35e-4 1.591142,-1.617185 C 39.182807,23.305596 32.331836,17.000034 24.002386,17.000034 Z"/>
|
||||
<path style="opacity:0.2" d="m 16,25 a 1.9999959,1.9999959 0 0 0 -2,2 1.9999959,1.9999959 0 0 0 2,2 1.9999959,1.9999959 0 0 0 2,-2 1.9999959,1.9999959 0 0 0 -2,-2 z m 16,0 a 1.9999959,1.9999959 0 0 0 -2,2 1.9999959,1.9999959 0 0 0 2,2 1.9999959,1.9999959 0 0 0 2,-2 1.9999959,1.9999959 0 0 0 -2,-2 z"/>
|
||||
<path style="fill:#ffffff" d="M 15.999996,24.000008 A 1.9999959,1.9999959 0 0 1 17.999992,26.000004 1.9999959,1.9999959 0 0 1 15.999996,28 1.9999959,1.9999959 0 0 1 14,26.000004 1.9999959,1.9999959 0 0 1 15.999996,24.000008 Z"/>
|
||||
<path style="fill:#ffffff" d="M 31.999996,24.000008 A 1.9999959,1.9999959 0 0 1 33.999991,26.000004 1.9999959,1.9999959 0 0 1 31.999996,28 1.9999959,1.9999959 0 0 1 30,26.000004 1.9999959,1.9999959 0 0 1 31.999996,24.000008 Z"/>
|
||||
<path style="fill:#ffffff;opacity:0.2" d="M 11.494141 14 A 1.5 1.5 0 0 0 10.662109 14.255859 A 1.5 1.5 0 0 0 10.115234 16.001953 A 1.5 1.5 0 0 1 10.662109 15.255859 A 1.5 1.5 0 0 1 11.494141 15 A 1.5 1.5 0 0 1 11.787109 15.027344 A 1.5 1.5 0 0 1 12.744141 15.662109 L 15.818359 20.230469 C 18.215776 18.815488 21.012568 18 24.001953 18 C 26.990024 18 29.78649 18.814887 32.183594 20.228516 L 35.255859 15.662109 A 1.5 1.5 0 0 1 36.212891 15.027344 A 1.5 1.5 0 0 1 37.337891 15.255859 A 1.5 1.5 0 0 1 37.910156 16.001953 A 1.5 1.5 0 0 0 37.337891 14.255859 A 1.5 1.5 0 0 0 36.212891 14.027344 A 1.5 1.5 0 0 0 35.255859 14.662109 L 32.183594 19.228516 C 29.78649 17.814887 26.990024 17 24.001953 17 C 21.012568 17 18.215776 17.815488 15.818359 19.230469 L 12.744141 14.662109 A 1.5 1.5 0 0 0 11.787109 14.027344 A 1.5 1.5 0 0 0 11.494141 14 z M 35.033203 21.369141 L 34.617188 21.988281 C 37.477056 24.493668 39.433905 27.993356 39.943359 31.945312 C 39.986866 31.783283 40.008864 31.598575 39.998047 31.382812 C 39.601372 27.448291 37.768055 23.938648 35.033203 21.369141 z M 12.970703 21.373047 C 10.220358 23.959215 8.3822757 27.496796 8.0019531 31.460938 C 7.9920657 31.668114 8.0150508 31.844846 8.0585938 32 C 8.5570234 28.027243 10.515755 24.509049 13.386719 21.992188 L 12.970703 21.373047 z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.5 KiB |
@@ -94,7 +94,6 @@ dist-win32: build-server build-win32
|
||||
cp "$(WIN32_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp data/scrcpy-console.bat "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||
cp data/scrcpy-noconsole.vbs "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||
cp data/icon.png "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avcodec-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
@@ -111,7 +110,6 @@ dist-win64: build-server build-win64
|
||||
cp "$(WIN64_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp data/scrcpy-console.bat "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
cp data/scrcpy-noconsole.vbs "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
cp data/icon.png "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avutil-56.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avcodec-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avformat-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
|
||||
Reference in New Issue
Block a user