Compare commits
4 Commits
otg
...
open_a_ter
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a79f44ecc | ||
|
|
d66d708e55 | ||
|
|
0a16272000 | ||
|
|
93ac6a347e |
33
README.md
33
README.md
@@ -2,8 +2,6 @@
|
||||
|
||||
<img src="data/icon.svg" width="128" height="128" alt="scrcpy" align="right" />
|
||||
|
||||
_pronounced "**scr**een **c**o**py**"_
|
||||
|
||||
[Read in another language](#translations)
|
||||
|
||||
This application provides display and control of Android devices connected via
|
||||
@@ -35,7 +33,6 @@ Its features include:
|
||||
(Linux-only)
|
||||
- [physical mouse simulation (HID)](#physical-mouse-simulation-hid)
|
||||
(Linux-only)
|
||||
- [OTG mode](#otg) (Linux-only)
|
||||
- and more…
|
||||
|
||||
## Requirements
|
||||
@@ -850,36 +847,6 @@ Special capture keys, either <kbd>Alt</kbd> or <kbd>Super</kbd>, toggle
|
||||
the mouse back to the computer.
|
||||
|
||||
|
||||
#### OTG
|
||||
|
||||
It is possible to run _scrcpy_ with only physical keyboard and mouse simulation
|
||||
(HID), as if the computer keyboard and mouse were plugged directly to the device
|
||||
via an OTG cable.
|
||||
|
||||
In this mode, _adb_ (USB debugging) is not necessary, and mirroring is disabled.
|
||||
|
||||
To enable OTG mode:
|
||||
|
||||
```bash
|
||||
scrcpy --otg
|
||||
# Pass the serial if several USB devices are available
|
||||
scrcpy --otg -s 0123456789abcdef
|
||||
```
|
||||
|
||||
It is possible to enable only HID keyboard or HID mouse:
|
||||
|
||||
```bash
|
||||
scrcpy --otg --hid-keyboard # keyboard only
|
||||
scrcpy --otg --hid-mouse # mouse only
|
||||
scrcpy --otg --hid-keyboard --hid-mouse # keyboard and mouse
|
||||
# for convenience, enable both by default
|
||||
scrcpy --otg # keyboard and mouse
|
||||
```
|
||||
|
||||
Like `--hid-keyboard` and `--hid-mouse`, it only works if the device is
|
||||
connected by USB, and is currently only supported on Linux.
|
||||
|
||||
|
||||
#### Text injection preference
|
||||
|
||||
There are two kinds of [events][textevents] generated when typing text:
|
||||
|
||||
@@ -72,15 +72,12 @@ if v4l2_support
|
||||
src += [ 'src/v4l2_sink.c' ]
|
||||
endif
|
||||
|
||||
usb_support = host_machine.system() == 'linux'
|
||||
if usb_support
|
||||
aoa_hid_support = host_machine.system() == 'linux'
|
||||
if aoa_hid_support
|
||||
src += [
|
||||
'src/usb/aoa_hid.c',
|
||||
'src/usb/hid_keyboard.c',
|
||||
'src/usb/hid_mouse.c',
|
||||
'src/usb/scrcpy_otg.c',
|
||||
'src/usb/screen_otg.c',
|
||||
'src/usb/usb.c',
|
||||
'src/aoa_hid.c',
|
||||
'src/hid_keyboard.c',
|
||||
'src/hid_mouse.c',
|
||||
]
|
||||
endif
|
||||
|
||||
@@ -102,7 +99,7 @@ if not crossbuild_windows
|
||||
dependencies += dependency('libavdevice')
|
||||
endif
|
||||
|
||||
if usb_support
|
||||
if aoa_hid_support
|
||||
dependencies += dependency('libusb-1.0')
|
||||
endif
|
||||
|
||||
@@ -196,7 +193,7 @@ conf.set('SERVER_DEBUGGER_METHOD_NEW', get_option('server_debugger_method') == '
|
||||
conf.set('HAVE_V4L2', v4l2_support)
|
||||
|
||||
# enable HID over AOA support (linux only)
|
||||
conf.set('HAVE_USB', usb_support)
|
||||
conf.set('HAVE_AOA_HID', aoa_hid_support)
|
||||
|
||||
configure_file(configuration: conf, output: 'config.h')
|
||||
|
||||
|
||||
14
app/scrcpy.1
14
app/scrcpy.1
@@ -162,20 +162,6 @@ Do not forward repeated key events when a key is held down.
|
||||
.B \-\-no\-mipmaps
|
||||
If the renderer is OpenGL 3.0+ or OpenGL ES 2.0+, then mipmaps are automatically generated to improve downscaling quality. This option disables the generation of mipmaps.
|
||||
|
||||
.TP
|
||||
.B \-\-otg
|
||||
Run in OTG mode: simulate physical keyboard and mouse, as if the computer keyboard and mouse were plugged directly to the device via an OTG cable.
|
||||
|
||||
In this mode, adb (USB debugging) is not necessary, and mirroring is disabled.
|
||||
|
||||
LAlt, LSuper or RSuper toggle the mouse capture mode, to give control of the mouse back to the computer.
|
||||
|
||||
If any of \fB\-\-hid\-keyboard\fR or \fB\-\-hid\-mouse\fR is set, only enable keyboard or mouse respectively, otherwise enable both.
|
||||
|
||||
It may only work over USB, and is currently only supported on Linux.
|
||||
|
||||
See \fB\-\-hid\-keyboard\fR and \fB\-\-hid\-mouse\fR.
|
||||
|
||||
.TP
|
||||
.BI "\-p, \-\-port " port[:port]
|
||||
Set the TCP port (range) used by the client to listen.
|
||||
|
||||
@@ -425,7 +425,7 @@ adb_get_serialno(struct sc_intr *intr, unsigned flags) {
|
||||
}
|
||||
|
||||
if (r == -1) {
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
sc_str_truncate(buf, r, " \r\n");
|
||||
@@ -455,7 +455,7 @@ adb_get_device_ip(struct sc_intr *intr, const char *serial, unsigned flags) {
|
||||
}
|
||||
|
||||
if (r == -1) {
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
assert((size_t) r <= sizeof(buf));
|
||||
|
||||
@@ -50,9 +50,79 @@ log_libusb_error(enum libusb_error errcode) {
|
||||
LOGW("libusb error: %s", libusb_strerror(errcode));
|
||||
}
|
||||
|
||||
static bool
|
||||
accept_device(libusb_device *device, const char *serial) {
|
||||
// do not log any USB error in this function, it is expected that many USB
|
||||
// devices available on the computer have permission restrictions
|
||||
|
||||
struct libusb_device_descriptor desc;
|
||||
int result = libusb_get_device_descriptor(device, &desc);
|
||||
if (result < 0 || !desc.iSerialNumber) {
|
||||
return false;
|
||||
}
|
||||
|
||||
libusb_device_handle *handle;
|
||||
result = libusb_open(device, &handle);
|
||||
if (result < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char buffer[128];
|
||||
result = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber,
|
||||
(unsigned char *) buffer,
|
||||
sizeof(buffer));
|
||||
libusb_close(handle);
|
||||
if (result < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer[sizeof(buffer) - 1] = '\0'; // just in case
|
||||
|
||||
// accept the device if its serial matches
|
||||
return !strcmp(buffer, serial);
|
||||
}
|
||||
|
||||
static libusb_device *
|
||||
sc_aoa_find_usb_device(const char *serial) {
|
||||
if (!serial) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
libusb_device **list;
|
||||
libusb_device *result = NULL;
|
||||
ssize_t count = libusb_get_device_list(NULL, &list);
|
||||
if (count < 0) {
|
||||
log_libusb_error((enum libusb_error) count);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < (size_t) count; ++i) {
|
||||
libusb_device *device = list[i];
|
||||
|
||||
if (accept_device(device, serial)) {
|
||||
result = libusb_ref_device(device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
libusb_free_device_list(list, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
sc_aoa_open_usb_handle(libusb_device *device, libusb_device_handle **handle) {
|
||||
int result = libusb_open(device, handle);
|
||||
if (result < 0) {
|
||||
log_libusb_error((enum libusb_error) result);
|
||||
return result;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
sc_aoa_init(struct sc_aoa *aoa, struct sc_usb *usb,
|
||||
sc_aoa_init(struct sc_aoa *aoa, const char *serial,
|
||||
struct sc_acksync *acksync) {
|
||||
assert(acksync);
|
||||
|
||||
cbuf_init(&aoa->queue);
|
||||
|
||||
if (!sc_mutex_init(&aoa->mutex)) {
|
||||
@@ -60,15 +130,39 @@ sc_aoa_init(struct sc_aoa *aoa, struct sc_usb *usb,
|
||||
}
|
||||
|
||||
if (!sc_cond_init(&aoa->event_cond)) {
|
||||
sc_mutex_destroy(&aoa->mutex);
|
||||
goto error_destroy_mutex;
|
||||
}
|
||||
|
||||
if (libusb_init(&aoa->usb_context) != LIBUSB_SUCCESS) {
|
||||
goto error_destroy_cond;
|
||||
}
|
||||
|
||||
aoa->usb_device = sc_aoa_find_usb_device(serial);
|
||||
if (!aoa->usb_device) {
|
||||
LOGW("USB device of serial %s not found", serial);
|
||||
goto error_exit_libusb;
|
||||
}
|
||||
|
||||
if (sc_aoa_open_usb_handle(aoa->usb_device, &aoa->usb_handle) < 0) {
|
||||
LOGW("Open USB handle failed");
|
||||
goto error_unref_device;
|
||||
return false;
|
||||
}
|
||||
|
||||
aoa->stopped = false;
|
||||
aoa->acksync = acksync;
|
||||
aoa->usb = usb;
|
||||
|
||||
return true;
|
||||
|
||||
error_unref_device:
|
||||
libusb_unref_device(aoa->usb_device);
|
||||
error_exit_libusb:
|
||||
libusb_exit(aoa->usb_context);
|
||||
error_destroy_cond:
|
||||
sc_cond_destroy(&aoa->event_cond);
|
||||
error_destroy_mutex:
|
||||
sc_mutex_destroy(&aoa->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -79,6 +173,9 @@ sc_aoa_destroy(struct sc_aoa *aoa) {
|
||||
sc_hid_event_destroy(&event);
|
||||
}
|
||||
|
||||
libusb_close(aoa->usb_handle);
|
||||
libusb_unref_device(aoa->usb_device);
|
||||
libusb_exit(aoa->usb_context);
|
||||
sc_cond_destroy(&aoa->event_cond);
|
||||
sc_mutex_destroy(&aoa->mutex);
|
||||
}
|
||||
@@ -95,8 +192,8 @@ sc_aoa_register_hid(struct sc_aoa *aoa, uint16_t accessory_id,
|
||||
uint16_t index = report_desc_size;
|
||||
unsigned char *buffer = NULL;
|
||||
uint16_t length = 0;
|
||||
int result = libusb_control_transfer(aoa->usb->handle, request_type,
|
||||
request, value, index, buffer, length,
|
||||
int result = libusb_control_transfer(aoa->usb_handle, request_type, request,
|
||||
value, index, buffer, length,
|
||||
DEFAULT_TIMEOUT);
|
||||
if (result < 0) {
|
||||
log_libusb_error((enum libusb_error) result);
|
||||
@@ -131,8 +228,8 @@ sc_aoa_set_hid_report_desc(struct sc_aoa *aoa, uint16_t accessory_id,
|
||||
// libusb_control_transfer expects a pointer to non-const
|
||||
unsigned char *buffer = (unsigned char *) report_desc;
|
||||
uint16_t length = report_desc_size;
|
||||
int result = libusb_control_transfer(aoa->usb->handle, request_type,
|
||||
request, value, index, buffer, length,
|
||||
int result = libusb_control_transfer(aoa->usb_handle, request_type, request,
|
||||
value, index, buffer, length,
|
||||
DEFAULT_TIMEOUT);
|
||||
if (result < 0) {
|
||||
log_libusb_error((enum libusb_error) result);
|
||||
@@ -173,8 +270,8 @@ sc_aoa_send_hid_event(struct sc_aoa *aoa, const struct sc_hid_event *event) {
|
||||
uint16_t index = 0;
|
||||
unsigned char *buffer = event->buffer;
|
||||
uint16_t length = event->size;
|
||||
int result = libusb_control_transfer(aoa->usb->handle, request_type,
|
||||
request, value, index, buffer, length,
|
||||
int result = libusb_control_transfer(aoa->usb_handle, request_type, request,
|
||||
value, index, buffer, length,
|
||||
DEFAULT_TIMEOUT);
|
||||
if (result < 0) {
|
||||
log_libusb_error((enum libusb_error) result);
|
||||
@@ -195,8 +292,8 @@ sc_aoa_unregister_hid(struct sc_aoa *aoa, const uint16_t accessory_id) {
|
||||
uint16_t index = 0;
|
||||
unsigned char *buffer = NULL;
|
||||
uint16_t length = 0;
|
||||
int result = libusb_control_transfer(aoa->usb->handle, request_type,
|
||||
request, value, index, buffer, length,
|
||||
int result = libusb_control_transfer(aoa->usb_handle, request_type, request,
|
||||
value, index, buffer, length,
|
||||
DEFAULT_TIMEOUT);
|
||||
if (result < 0) {
|
||||
log_libusb_error((enum libusb_error) result);
|
||||
@@ -246,11 +343,6 @@ run_aoa_thread(void *data) {
|
||||
|
||||
if (ack_to_wait != SC_SEQUENCE_INVALID) {
|
||||
LOGD("Waiting ack from server sequence=%" PRIu64_, ack_to_wait);
|
||||
|
||||
// If some events have ack_to_wait set, then sc_aoa must have been
|
||||
// initialized with a non NULL acksync
|
||||
assert(aoa->acksync);
|
||||
|
||||
// Do not block the loop indefinitely if the ack never comes (it
|
||||
// should never happen)
|
||||
sc_tick deadline = sc_tick_now() + SC_TICK_FROM_MS(500);
|
||||
@@ -297,9 +389,7 @@ sc_aoa_stop(struct sc_aoa *aoa) {
|
||||
sc_cond_signal(&aoa->event_cond);
|
||||
sc_mutex_unlock(&aoa->mutex);
|
||||
|
||||
if (aoa->acksync) {
|
||||
sc_acksync_interrupt(aoa->acksync);
|
||||
}
|
||||
sc_acksync_interrupt(aoa->acksync);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include <libusb-1.0/libusb.h>
|
||||
|
||||
#include "usb.h"
|
||||
#include "util/acksync.h"
|
||||
#include "util/cbuf.h"
|
||||
#include "util/thread.h"
|
||||
@@ -30,7 +29,9 @@ sc_hid_event_destroy(struct sc_hid_event *hid_event);
|
||||
struct sc_hid_event_queue CBUF(struct sc_hid_event, 64);
|
||||
|
||||
struct sc_aoa {
|
||||
struct sc_usb *usb;
|
||||
libusb_context *usb_context;
|
||||
libusb_device *usb_device;
|
||||
libusb_device_handle *usb_handle;
|
||||
sc_thread thread;
|
||||
sc_mutex mutex;
|
||||
sc_cond event_cond;
|
||||
@@ -41,7 +42,7 @@ struct sc_aoa {
|
||||
};
|
||||
|
||||
bool
|
||||
sc_aoa_init(struct sc_aoa *aoa, struct sc_usb *usb, struct sc_acksync *acksync);
|
||||
sc_aoa_init(struct sc_aoa *aoa, const char *serial, struct sc_acksync *acksync);
|
||||
|
||||
void
|
||||
sc_aoa_destroy(struct sc_aoa *aoa);
|
||||
@@ -53,7 +53,6 @@
|
||||
#define OPT_TCPIP 1033
|
||||
#define OPT_RAW_KEY_EVENTS 1034
|
||||
#define OPT_NO_DOWNSIZE_ON_ERROR 1035
|
||||
#define OPT_OTG 1036
|
||||
|
||||
struct sc_option {
|
||||
char shortopt;
|
||||
@@ -277,22 +276,6 @@ static const struct sc_option options[] = {
|
||||
"mipmaps are automatically generated to improve downscaling "
|
||||
"quality. This option disables the generation of mipmaps.",
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_OTG,
|
||||
.longopt = "otg",
|
||||
.text = "Run in OTG mode: simulate physical keyboard and mouse, "
|
||||
"as if the computer keyboard and mouse were plugged directly "
|
||||
"to the device via an OTG cable.\n"
|
||||
"In this mode, adb (USB debugging) is not necessary, and "
|
||||
"mirroring is disabled.\n"
|
||||
"LAlt, LSuper or RSuper toggle the mouse capture mode, to give "
|
||||
"control of the mouse back to the computer.\n"
|
||||
"If any of --hid-keyboard or --hid-mouse is set, only enable "
|
||||
"keyboard or mouse respectively, otherwise enable both."
|
||||
"It may only work over USB, and is currently only supported "
|
||||
"on Linux.\n"
|
||||
"See --hid-keyboard and --hid-mouse.",
|
||||
},
|
||||
{
|
||||
.shortopt = 'p',
|
||||
.longopt = "port",
|
||||
@@ -1335,7 +1318,7 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
args->help = true;
|
||||
break;
|
||||
case 'K':
|
||||
#ifdef HAVE_USB
|
||||
#ifdef HAVE_AOA_HID
|
||||
opts->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_HID;
|
||||
break;
|
||||
#else
|
||||
@@ -1354,7 +1337,7 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
}
|
||||
break;
|
||||
case 'M':
|
||||
#ifdef HAVE_USB
|
||||
#ifdef HAVE_AOA_HID
|
||||
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_HID;
|
||||
break;
|
||||
#else
|
||||
@@ -1517,15 +1500,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
case OPT_NO_DOWNSIZE_ON_ERROR:
|
||||
opts->downsize_on_error = false;
|
||||
break;
|
||||
case OPT_OTG:
|
||||
#ifdef HAVE_USB
|
||||
opts->otg = true;
|
||||
break;
|
||||
#else
|
||||
LOGE("OTG mode (--otg) is not supported on this platform. It "
|
||||
"is only available on Linux.");
|
||||
return false;
|
||||
#endif
|
||||
case OPT_V4L2_SINK:
|
||||
#ifdef HAVE_V4L2
|
||||
opts->v4l2_device = optarg;
|
||||
@@ -1636,43 +1610,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_USB
|
||||
if (opts->otg) {
|
||||
// OTG mode is compatible with only very few options.
|
||||
// Only report obvious errors.
|
||||
if (opts->record_filename) {
|
||||
LOGE("OTG mode: could not record");
|
||||
return false;
|
||||
}
|
||||
if (opts->turn_screen_off) {
|
||||
LOGE("OTG mode: could not turn screen off");
|
||||
return false;
|
||||
}
|
||||
if (opts->stay_awake) {
|
||||
LOGE("OTG mode: could not stay awake");
|
||||
return false;
|
||||
}
|
||||
if (opts->show_touches) {
|
||||
LOGE("OTG mode: could not request to show touches");
|
||||
return false;
|
||||
}
|
||||
if (opts->power_off_on_close) {
|
||||
LOGE("OTG mode: could not request power off on close");
|
||||
return false;
|
||||
}
|
||||
if (opts->display_id) {
|
||||
LOGE("OTG mode: could not select display");
|
||||
return false;
|
||||
}
|
||||
#ifdef HAVE_V4L2
|
||||
if (opts->v4l2_device) {
|
||||
LOGE("OTG mode: could not sink to V4L2 device");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,4 +2,3 @@
|
||||
#define EVENT_STREAM_STOPPED (SDL_USEREVENT + 1)
|
||||
#define EVENT_SERVER_CONNECTION_FAILED (SDL_USEREVENT + 2)
|
||||
#define EVENT_SERVER_CONNECTED (SDL_USEREVENT + 3)
|
||||
#define EVENT_USB_DEVICE_DISCONNECTED (SDL_USEREVENT + 4)
|
||||
|
||||
@@ -50,7 +50,9 @@ swap_frames(AVFrame **lhs, AVFrame **rhs) {
|
||||
|
||||
bool
|
||||
sc_frame_buffer_push(struct sc_frame_buffer *fb, const AVFrame *frame,
|
||||
bool *previous_frame_skipped) {
|
||||
bool *previous_frame_skipped) {
|
||||
sc_mutex_lock(&fb->mutex);
|
||||
|
||||
// Use a temporary frame to preserve pending_frame in case of error.
|
||||
// tmp_frame is an empty frame, no need to call av_frame_unref() beforehand.
|
||||
int r = av_frame_ref(fb->tmp_frame, frame);
|
||||
@@ -59,8 +61,6 @@ sc_frame_buffer_push(struct sc_frame_buffer *fb, const AVFrame *frame,
|
||||
return false;
|
||||
}
|
||||
|
||||
sc_mutex_lock(&fb->mutex);
|
||||
|
||||
// Now that av_frame_ref() succeeded, we can replace the previous
|
||||
// pending_frame
|
||||
swap_frames(&fb->pending_frame, &fb->tmp_frame);
|
||||
|
||||
@@ -377,76 +377,4 @@ struct sc_touch_event {
|
||||
float pressure;
|
||||
};
|
||||
|
||||
static inline uint16_t
|
||||
sc_mods_state_from_sdl(uint16_t mods_state) {
|
||||
return mods_state;
|
||||
}
|
||||
|
||||
static inline enum sc_keycode
|
||||
sc_keycode_from_sdl(SDL_Keycode keycode) {
|
||||
return (enum sc_keycode) keycode;
|
||||
}
|
||||
|
||||
static inline enum sc_scancode
|
||||
sc_scancode_from_sdl(SDL_Scancode scancode) {
|
||||
return (enum sc_scancode) scancode;
|
||||
}
|
||||
|
||||
static inline enum sc_action
|
||||
sc_action_from_sdl_keyboard_type(uint32_t type) {
|
||||
assert(type == SDL_KEYDOWN || type == SDL_KEYUP);
|
||||
if (type == SDL_KEYDOWN) {
|
||||
return SC_ACTION_DOWN;
|
||||
}
|
||||
return SC_ACTION_UP;
|
||||
}
|
||||
|
||||
static inline enum sc_action
|
||||
sc_action_from_sdl_mousebutton_type(uint32_t type) {
|
||||
assert(type == SDL_MOUSEBUTTONDOWN || type == SDL_MOUSEBUTTONUP);
|
||||
if (type == SDL_MOUSEBUTTONDOWN) {
|
||||
return SC_ACTION_DOWN;
|
||||
}
|
||||
return SC_ACTION_UP;
|
||||
}
|
||||
|
||||
static inline enum sc_touch_action
|
||||
sc_touch_action_from_sdl(uint32_t type) {
|
||||
assert(type == SDL_FINGERMOTION || type == SDL_FINGERDOWN ||
|
||||
type == SDL_FINGERUP);
|
||||
if (type == SDL_FINGERMOTION) {
|
||||
return SC_TOUCH_ACTION_MOVE;
|
||||
}
|
||||
if (type == SDL_FINGERDOWN) {
|
||||
return SC_TOUCH_ACTION_DOWN;
|
||||
}
|
||||
return SC_TOUCH_ACTION_UP;
|
||||
}
|
||||
|
||||
static inline enum sc_mouse_button
|
||||
sc_mouse_button_from_sdl(uint8_t button) {
|
||||
if (button >= SDL_BUTTON_LEFT && button <= SDL_BUTTON_X2) {
|
||||
// SC_MOUSE_BUTTON_* constants are initialized from SDL_BUTTON(index)
|
||||
return SDL_BUTTON(button);
|
||||
}
|
||||
|
||||
return SC_MOUSE_BUTTON_UNKNOWN;
|
||||
}
|
||||
|
||||
static inline uint8_t
|
||||
sc_mouse_buttons_state_from_sdl(uint32_t buttons_state,
|
||||
bool forward_all_clicks) {
|
||||
assert(buttons_state < 0x100); // fits in uint8_t
|
||||
|
||||
uint8_t mask = SC_MOUSE_BUTTON_LEFT;
|
||||
if (forward_all_clicks) {
|
||||
mask |= SC_MOUSE_BUTTON_RIGHT
|
||||
| SC_MOUSE_BUTTON_MIDDLE
|
||||
| SC_MOUSE_BUTTON_X1
|
||||
| SC_MOUSE_BUTTON_X2;
|
||||
}
|
||||
|
||||
return buttons_state & mask;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -7,6 +7,78 @@
|
||||
#include "screen.h"
|
||||
#include "util/log.h"
|
||||
|
||||
static inline uint16_t
|
||||
sc_mods_state_from_sdl(uint16_t mods_state) {
|
||||
return mods_state;
|
||||
}
|
||||
|
||||
static inline enum sc_keycode
|
||||
sc_keycode_from_sdl(SDL_Keycode keycode) {
|
||||
return (enum sc_keycode) keycode;
|
||||
}
|
||||
|
||||
static inline enum sc_scancode
|
||||
sc_scancode_from_sdl(SDL_Scancode scancode) {
|
||||
return (enum sc_scancode) scancode;
|
||||
}
|
||||
|
||||
static inline enum sc_action
|
||||
sc_action_from_sdl_keyboard_type(uint32_t type) {
|
||||
assert(type == SDL_KEYDOWN || type == SDL_KEYUP);
|
||||
if (type == SDL_KEYDOWN) {
|
||||
return SC_ACTION_DOWN;
|
||||
}
|
||||
return SC_ACTION_UP;
|
||||
}
|
||||
|
||||
static inline enum sc_action
|
||||
sc_action_from_sdl_mousebutton_type(uint32_t type) {
|
||||
assert(type == SDL_MOUSEBUTTONDOWN || type == SDL_MOUSEBUTTONUP);
|
||||
if (type == SDL_MOUSEBUTTONDOWN) {
|
||||
return SC_ACTION_DOWN;
|
||||
}
|
||||
return SC_ACTION_UP;
|
||||
}
|
||||
|
||||
static inline enum sc_touch_action
|
||||
sc_touch_action_from_sdl(uint32_t type) {
|
||||
assert(type == SDL_FINGERMOTION || type == SDL_FINGERDOWN ||
|
||||
type == SDL_FINGERUP);
|
||||
if (type == SDL_FINGERMOTION) {
|
||||
return SC_TOUCH_ACTION_MOVE;
|
||||
}
|
||||
if (type == SDL_FINGERDOWN) {
|
||||
return SC_TOUCH_ACTION_DOWN;
|
||||
}
|
||||
return SC_TOUCH_ACTION_UP;
|
||||
}
|
||||
|
||||
static inline enum sc_mouse_button
|
||||
sc_mouse_button_from_sdl(uint8_t button) {
|
||||
if (button >= SDL_BUTTON_LEFT && button <= SDL_BUTTON_X2) {
|
||||
// SC_MOUSE_BUTTON_* constants are initialized from SDL_BUTTON(index)
|
||||
return SDL_BUTTON(button);
|
||||
}
|
||||
|
||||
return SC_MOUSE_BUTTON_UNKNOWN;
|
||||
}
|
||||
|
||||
static inline uint8_t
|
||||
sc_mouse_buttons_state_from_sdl(uint32_t buttons_state,
|
||||
bool forward_all_clicks) {
|
||||
assert(buttons_state < 0x100); // fits in uint8_t
|
||||
|
||||
uint8_t mask = SC_MOUSE_BUTTON_LEFT;
|
||||
if (forward_all_clicks) {
|
||||
mask |= SC_MOUSE_BUTTON_RIGHT
|
||||
| SC_MOUSE_BUTTON_MIDDLE
|
||||
| SC_MOUSE_BUTTON_X1
|
||||
| SC_MOUSE_BUTTON_X2;
|
||||
}
|
||||
|
||||
return buttons_state & mask;
|
||||
}
|
||||
|
||||
#define SC_SDL_SHORTCUT_MODS_MASK (KMOD_CTRL | KMOD_ALT | KMOD_GUI)
|
||||
|
||||
static inline uint16_t
|
||||
|
||||
@@ -13,27 +13,28 @@
|
||||
#include "cli.h"
|
||||
#include "options.h"
|
||||
#include "scrcpy.h"
|
||||
#include "usb/scrcpy_otg.h"
|
||||
#include "util/log.h"
|
||||
|
||||
static void
|
||||
print_version(void) {
|
||||
printf("\ndependencies:\n");
|
||||
printf(" - SDL %d.%d.%d\n", SDL_MAJOR_VERSION, SDL_MINOR_VERSION,
|
||||
SDL_PATCHLEVEL);
|
||||
printf(" - libavcodec %d.%d.%d\n", LIBAVCODEC_VERSION_MAJOR,
|
||||
LIBAVCODEC_VERSION_MINOR,
|
||||
LIBAVCODEC_VERSION_MICRO);
|
||||
printf(" - libavformat %d.%d.%d\n", LIBAVFORMAT_VERSION_MAJOR,
|
||||
LIBAVFORMAT_VERSION_MINOR,
|
||||
LIBAVFORMAT_VERSION_MICRO);
|
||||
printf(" - libavutil %d.%d.%d\n", LIBAVUTIL_VERSION_MAJOR,
|
||||
LIBAVUTIL_VERSION_MINOR,
|
||||
LIBAVUTIL_VERSION_MICRO);
|
||||
fprintf(stderr, "scrcpy %s\n\n", SCRCPY_VERSION);
|
||||
|
||||
fprintf(stderr, "dependencies:\n");
|
||||
fprintf(stderr, " - SDL %d.%d.%d\n", SDL_MAJOR_VERSION, SDL_MINOR_VERSION,
|
||||
SDL_PATCHLEVEL);
|
||||
fprintf(stderr, " - libavcodec %d.%d.%d\n", LIBAVCODEC_VERSION_MAJOR,
|
||||
LIBAVCODEC_VERSION_MINOR,
|
||||
LIBAVCODEC_VERSION_MICRO);
|
||||
fprintf(stderr, " - libavformat %d.%d.%d\n", LIBAVFORMAT_VERSION_MAJOR,
|
||||
LIBAVFORMAT_VERSION_MINOR,
|
||||
LIBAVFORMAT_VERSION_MICRO);
|
||||
fprintf(stderr, " - libavutil %d.%d.%d\n", LIBAVUTIL_VERSION_MAJOR,
|
||||
LIBAVUTIL_VERSION_MINOR,
|
||||
LIBAVUTIL_VERSION_MICRO);
|
||||
#ifdef HAVE_V4L2
|
||||
printf(" - libavdevice %d.%d.%d\n", LIBAVDEVICE_VERSION_MAJOR,
|
||||
LIBAVDEVICE_VERSION_MINOR,
|
||||
LIBAVDEVICE_VERSION_MICRO);
|
||||
fprintf(stderr, " - libavdevice %d.%d.%d\n", LIBAVDEVICE_VERSION_MAJOR,
|
||||
LIBAVDEVICE_VERSION_MINOR,
|
||||
LIBAVDEVICE_VERSION_MICRO);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -89,14 +90,9 @@ main(int argc, char *argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_USB
|
||||
bool ok = args.opts.otg ? scrcpy_otg(&args.opts)
|
||||
: scrcpy(&args.opts);
|
||||
#else
|
||||
bool ok = scrcpy(&args.opts);
|
||||
#endif
|
||||
int res = scrcpy(&args.opts) ? 0 : 1;
|
||||
|
||||
avformat_network_deinit(); // ignore failure
|
||||
|
||||
return ok ? 0 : 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -37,9 +37,6 @@ const struct scrcpy_options scrcpy_options_default = {
|
||||
.display_id = 0,
|
||||
.display_buffer = 0,
|
||||
.v4l2_buffer = 0,
|
||||
#ifdef HAVE_USB
|
||||
.otg = false,
|
||||
#endif
|
||||
.show_touches = false,
|
||||
.fullscreen = false,
|
||||
.always_on_top = false,
|
||||
|
||||
@@ -112,9 +112,6 @@ struct scrcpy_options {
|
||||
uint32_t display_id;
|
||||
sc_tick display_buffer;
|
||||
sc_tick v4l2_buffer;
|
||||
#ifdef HAVE_USB
|
||||
bool otg;
|
||||
#endif
|
||||
bool show_touches;
|
||||
bool fullscreen;
|
||||
bool always_on_top;
|
||||
|
||||
@@ -17,18 +17,16 @@
|
||||
#include "decoder.h"
|
||||
#include "events.h"
|
||||
#include "file_pusher.h"
|
||||
#ifdef HAVE_AOA_HID
|
||||
# include "hid_keyboard.h"
|
||||
# include "hid_mouse.h"
|
||||
#endif
|
||||
#include "keyboard_inject.h"
|
||||
#include "mouse_inject.h"
|
||||
#include "recorder.h"
|
||||
#include "screen.h"
|
||||
#include "server.h"
|
||||
#include "stream.h"
|
||||
#ifdef HAVE_USB
|
||||
# include "usb/aoa_hid.h"
|
||||
# include "usb/hid_keyboard.h"
|
||||
# include "usb/hid_mouse.h"
|
||||
# include "usb/usb.h"
|
||||
#endif
|
||||
#include "util/acksync.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net.h"
|
||||
@@ -47,21 +45,20 @@ struct scrcpy {
|
||||
#endif
|
||||
struct sc_controller controller;
|
||||
struct sc_file_pusher file_pusher;
|
||||
#ifdef HAVE_USB
|
||||
struct sc_usb usb;
|
||||
#ifdef HAVE_AOA_HID
|
||||
struct sc_aoa aoa;
|
||||
// sequence/ack helper to synchronize clipboard and Ctrl+v via HID
|
||||
struct sc_acksync acksync;
|
||||
#endif
|
||||
union {
|
||||
struct sc_keyboard_inject keyboard_inject;
|
||||
#ifdef HAVE_USB
|
||||
#ifdef HAVE_AOA_HID
|
||||
struct sc_hid_keyboard keyboard_hid;
|
||||
#endif
|
||||
};
|
||||
union {
|
||||
struct sc_mouse_inject mouse_inject;
|
||||
#ifdef HAVE_USB
|
||||
#ifdef HAVE_AOA_HID
|
||||
struct sc_hid_mouse mouse_hid;
|
||||
#endif
|
||||
};
|
||||
@@ -286,7 +283,7 @@ scrcpy(struct scrcpy_options *options) {
|
||||
bool v4l2_sink_initialized = false;
|
||||
#endif
|
||||
bool stream_started = false;
|
||||
#ifdef HAVE_USB
|
||||
#ifdef HAVE_AOA_HID
|
||||
bool aoa_hid_initialized = false;
|
||||
bool hid_keyboard_initialized = false;
|
||||
bool hid_mouse_initialized = false;
|
||||
@@ -413,7 +410,7 @@ scrcpy(struct scrcpy_options *options) {
|
||||
struct sc_mouse_processor *mp = NULL;
|
||||
|
||||
if (options->control) {
|
||||
#ifdef HAVE_USB
|
||||
#ifdef HAVE_AOA_HID
|
||||
bool use_hid_keyboard =
|
||||
options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_HID;
|
||||
bool use_hid_mouse =
|
||||
@@ -424,52 +421,9 @@ scrcpy(struct scrcpy_options *options) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
ok = sc_usb_init(&s->usb);
|
||||
if (!ok) {
|
||||
LOGE("Failed to initialize USB");
|
||||
sc_acksync_destroy(&s->acksync);
|
||||
goto aoa_hid_end;
|
||||
}
|
||||
|
||||
assert(serial);
|
||||
struct sc_usb_device usb_devices[16];
|
||||
ssize_t count = sc_usb_find_devices(&s->usb, serial, usb_devices,
|
||||
ARRAY_LEN(usb_devices));
|
||||
if (count <= 0) {
|
||||
LOGE("Could not find USB device %s", serial);
|
||||
sc_usb_destroy(&s->usb);
|
||||
sc_acksync_destroy(&s->acksync);
|
||||
goto aoa_hid_end;
|
||||
}
|
||||
|
||||
if (count > 1) {
|
||||
LOGE("Multiple (%d) devices with serial %s", (int) count, serial);
|
||||
sc_usb_device_destroy_all(usb_devices, count);
|
||||
sc_usb_destroy(&s->usb);
|
||||
sc_acksync_destroy(&s->acksync);
|
||||
goto aoa_hid_end;
|
||||
}
|
||||
|
||||
struct sc_usb_device *usb_device = &usb_devices[0];
|
||||
|
||||
LOGI("USB device: %s (%04" PRIx16 ":%04" PRIx16 ") %s %s",
|
||||
usb_device->serial, usb_device->vid, usb_device->pid,
|
||||
usb_device->manufacturer, usb_device->product);
|
||||
|
||||
ok = sc_usb_connect(&s->usb, usb_device->device, NULL, NULL);
|
||||
sc_usb_device_destroy(usb_device);
|
||||
if (!ok) {
|
||||
LOGE("Failed to connect to USB device %s", serial);
|
||||
sc_usb_destroy(&s->usb);
|
||||
sc_acksync_destroy(&s->acksync);
|
||||
goto aoa_hid_end;
|
||||
}
|
||||
|
||||
ok = sc_aoa_init(&s->aoa, &s->usb, &s->acksync);
|
||||
ok = sc_aoa_init(&s->aoa, serial, &s->acksync);
|
||||
if (!ok) {
|
||||
LOGE("Failed to enable HID over AOA");
|
||||
sc_usb_disconnect(&s->usb);
|
||||
sc_usb_destroy(&s->usb);
|
||||
sc_acksync_destroy(&s->acksync);
|
||||
goto aoa_hid_end;
|
||||
}
|
||||
@@ -496,8 +450,6 @@ scrcpy(struct scrcpy_options *options) {
|
||||
|
||||
if (!need_aoa || !sc_aoa_start(&s->aoa)) {
|
||||
sc_acksync_destroy(&s->acksync);
|
||||
sc_usb_disconnect(&s->usb);
|
||||
sc_usb_destroy(&s->usb);
|
||||
sc_aoa_destroy(&s->aoa);
|
||||
goto aoa_hid_end;
|
||||
}
|
||||
@@ -641,7 +593,7 @@ aoa_hid_end:
|
||||
end:
|
||||
// The stream is not stopped explicitly, because it will stop by itself on
|
||||
// end-of-stream
|
||||
#ifdef HAVE_USB
|
||||
#ifdef HAVE_AOA_HID
|
||||
if (aoa_hid_initialized) {
|
||||
if (hid_keyboard_initialized) {
|
||||
sc_hid_keyboard_destroy(&s->keyboard_hid);
|
||||
@@ -650,7 +602,6 @@ end:
|
||||
sc_hid_mouse_destroy(&s->mouse_hid);
|
||||
}
|
||||
sc_aoa_stop(&s->aoa);
|
||||
sc_usb_stop(&s->usb);
|
||||
}
|
||||
if (acksync) {
|
||||
sc_acksync_destroy(acksync);
|
||||
@@ -683,13 +634,10 @@ end:
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_USB
|
||||
#ifdef HAVE_AOA_HID
|
||||
if (aoa_hid_initialized) {
|
||||
sc_aoa_join(&s->aoa);
|
||||
sc_aoa_destroy(&s->aoa);
|
||||
sc_usb_join(&s->usb);
|
||||
sc_usb_disconnect(&s->usb);
|
||||
sc_usb_destroy(&s->usb);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -769,10 +769,12 @@ run_server(void *data) {
|
||||
// Interrupt sockets to wake up socket blocking calls on the server
|
||||
assert(server->video_socket != SC_SOCKET_NONE);
|
||||
net_interrupt(server->video_socket);
|
||||
net_close(server->video_socket);
|
||||
|
||||
if (server->control_socket != SC_SOCKET_NONE) {
|
||||
// There is no control_socket if --no-control is set
|
||||
net_interrupt(server->control_socket);
|
||||
net_close(server->control_socket);
|
||||
}
|
||||
|
||||
// Give some delay for the server to terminate properly
|
||||
@@ -828,13 +830,6 @@ sc_server_stop(struct sc_server *server) {
|
||||
|
||||
void
|
||||
sc_server_destroy(struct sc_server *server) {
|
||||
if (server->video_socket != SC_SOCKET_NONE) {
|
||||
net_close(server->video_socket);
|
||||
}
|
||||
if (server->control_socket != SC_SOCKET_NONE) {
|
||||
net_close(server->control_socket);
|
||||
}
|
||||
|
||||
sc_server_params_destroy(&server->params);
|
||||
sc_intr_destroy(&server->intr);
|
||||
sc_cond_destroy(&server->cond_stopped);
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
#include "scrcpy_otg.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "events.h"
|
||||
#include "screen_otg.h"
|
||||
#include "util/log.h"
|
||||
|
||||
struct scrcpy_otg {
|
||||
struct sc_usb usb;
|
||||
struct sc_aoa aoa;
|
||||
struct sc_hid_keyboard keyboard;
|
||||
struct sc_hid_mouse mouse;
|
||||
|
||||
struct sc_screen_otg screen_otg;
|
||||
};
|
||||
|
||||
static void
|
||||
sc_usb_on_disconnected(struct sc_usb *usb, void *userdata) {
|
||||
(void) usb;
|
||||
(void) userdata;
|
||||
|
||||
SDL_Event event;
|
||||
event.type = EVENT_USB_DEVICE_DISCONNECTED;
|
||||
int ret = SDL_PushEvent(&event);
|
||||
if (ret < 0) {
|
||||
LOGE("Could not post USB disconnection event: %s", SDL_GetError());
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
event_loop(struct scrcpy_otg *s) {
|
||||
SDL_Event event;
|
||||
while (SDL_WaitEvent(&event)) {
|
||||
switch (event.type) {
|
||||
case EVENT_USB_DEVICE_DISCONNECTED:
|
||||
LOGW("Device disconnected");
|
||||
return false;
|
||||
case SDL_QUIT:
|
||||
LOGD("User requested to quit");
|
||||
return true;
|
||||
default:
|
||||
sc_screen_otg_handle_event(&s->screen_otg, &event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
scrcpy_otg(struct scrcpy_options *options) {
|
||||
static struct scrcpy_otg scrcpy_otg;
|
||||
struct scrcpy_otg *s = &scrcpy_otg;
|
||||
|
||||
const char *serial = options->serial;
|
||||
|
||||
// Minimal SDL initialization
|
||||
if (SDL_Init(SDL_INIT_EVENTS)) {
|
||||
LOGC("Could not initialize SDL: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
atexit(SDL_Quit);
|
||||
|
||||
if (!SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1")) {
|
||||
LOGW("Could not enable mouse focus clickthrough");
|
||||
}
|
||||
|
||||
bool ret = false;
|
||||
|
||||
struct sc_hid_keyboard *keyboard = NULL;
|
||||
struct sc_hid_mouse *mouse = NULL;
|
||||
bool usb_device_initialized = false;
|
||||
bool usb_connected = false;
|
||||
bool aoa_started = false;
|
||||
bool aoa_initialized = false;
|
||||
|
||||
static const struct sc_usb_callbacks cbs = {
|
||||
.on_disconnected = sc_usb_on_disconnected,
|
||||
};
|
||||
bool ok = sc_usb_init(&s->usb);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct sc_usb_device usb_devices[16];
|
||||
ssize_t count = sc_usb_find_devices(&s->usb, serial, usb_devices,
|
||||
ARRAY_LEN(usb_devices));
|
||||
if (count < 0) {
|
||||
LOGE("Could not list USB devices");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
if (serial) {
|
||||
LOGE("Could not find USB device %s", serial);
|
||||
} else {
|
||||
LOGE("Could not find any USB device");
|
||||
}
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (count > 1) {
|
||||
if (serial) {
|
||||
LOGE("Multiple (%d) USB devices with serial %s:", (int) count,
|
||||
serial);
|
||||
} else {
|
||||
LOGE("Multiple (%d) USB devices:", (int) count);
|
||||
}
|
||||
for (size_t i = 0; i < (size_t) count; ++i) {
|
||||
struct sc_usb_device *d = &usb_devices[i];
|
||||
LOGE(" %-18s (%04" PRIx16 ":%04" PRIx16 ") %s %s",
|
||||
d->serial, d->vid, d->pid, d->manufacturer, d->product);
|
||||
}
|
||||
if (!serial) {
|
||||
LOGE("Specify the device via -s or --serial");
|
||||
}
|
||||
sc_usb_device_destroy_all(usb_devices, count);
|
||||
goto end;
|
||||
}
|
||||
usb_device_initialized = true;
|
||||
|
||||
struct sc_usb_device *usb_device = &usb_devices[0];
|
||||
|
||||
LOGI("USB device: %s (%04" PRIx16 ":%04" PRIx16 ") %s %s",
|
||||
usb_device->serial, usb_device->vid, usb_device->pid,
|
||||
usb_device->manufacturer, usb_device->product);
|
||||
|
||||
ok = sc_usb_connect(&s->usb, usb_device->device, &cbs, NULL);
|
||||
if (!ok) {
|
||||
goto end;
|
||||
}
|
||||
usb_connected = true;
|
||||
|
||||
ok = sc_aoa_init(&s->aoa, &s->usb, NULL);
|
||||
if (!ok) {
|
||||
goto end;
|
||||
}
|
||||
aoa_initialized = true;
|
||||
|
||||
bool enable_keyboard =
|
||||
options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_HID;
|
||||
bool enable_mouse =
|
||||
options->mouse_input_mode == SC_MOUSE_INPUT_MODE_HID;
|
||||
|
||||
// If neither --hid-keyboard or --hid-mouse is passed, enable both
|
||||
if (!enable_keyboard && !enable_mouse) {
|
||||
enable_keyboard = true;
|
||||
enable_mouse = true;
|
||||
}
|
||||
|
||||
if (enable_keyboard) {
|
||||
ok = sc_hid_keyboard_init(&s->keyboard, &s->aoa);
|
||||
if (!ok) {
|
||||
goto end;
|
||||
}
|
||||
keyboard = &s->keyboard;
|
||||
}
|
||||
|
||||
if (enable_mouse) {
|
||||
ok = sc_hid_mouse_init(&s->mouse, &s->aoa);
|
||||
if (!ok) {
|
||||
goto end;
|
||||
}
|
||||
mouse = &s->mouse;
|
||||
}
|
||||
|
||||
ok = sc_aoa_start(&s->aoa);
|
||||
if (!ok) {
|
||||
goto end;
|
||||
}
|
||||
aoa_started = true;
|
||||
|
||||
const char *window_title = options->window_title;
|
||||
if (!window_title) {
|
||||
window_title = usb_device->product ? usb_device->product : "scrcpy";
|
||||
}
|
||||
|
||||
struct sc_screen_otg_params params = {
|
||||
.keyboard = keyboard,
|
||||
.mouse = mouse,
|
||||
.window_title = window_title,
|
||||
.always_on_top = options->always_on_top,
|
||||
.window_x = options->window_x,
|
||||
.window_y = options->window_y,
|
||||
.window_borderless = options->window_borderless,
|
||||
};
|
||||
|
||||
ok = sc_screen_otg_init(&s->screen_otg, ¶ms);
|
||||
if (!ok) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
// usb_device not needed anymore
|
||||
sc_usb_device_destroy(usb_device);
|
||||
usb_device_initialized = false;
|
||||
|
||||
ret = event_loop(s);
|
||||
LOGD("quit...");
|
||||
|
||||
end:
|
||||
if (aoa_started) {
|
||||
sc_aoa_stop(&s->aoa);
|
||||
}
|
||||
sc_usb_stop(&s->usb);
|
||||
|
||||
if (mouse) {
|
||||
sc_hid_mouse_destroy(&s->mouse);
|
||||
}
|
||||
if (keyboard) {
|
||||
sc_hid_keyboard_destroy(&s->keyboard);
|
||||
}
|
||||
|
||||
if (aoa_initialized) {
|
||||
sc_aoa_join(&s->aoa);
|
||||
sc_aoa_destroy(&s->aoa);
|
||||
}
|
||||
|
||||
sc_usb_join(&s->usb);
|
||||
|
||||
if (usb_connected) {
|
||||
sc_usb_disconnect(&s->usb);
|
||||
}
|
||||
|
||||
if (usb_device_initialized) {
|
||||
sc_usb_device_destroy(usb_device);
|
||||
}
|
||||
|
||||
sc_usb_destroy(&s->usb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#ifndef SCRCPY_OTG_H
|
||||
#define SCRCPY_OTG_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "options.h"
|
||||
|
||||
bool
|
||||
scrcpy_otg(struct scrcpy_options *options);
|
||||
|
||||
#endif
|
||||
@@ -1,267 +0,0 @@
|
||||
#include "screen_otg.h"
|
||||
|
||||
#include "icon.h"
|
||||
#include "options.h"
|
||||
#include "util/log.h"
|
||||
|
||||
static void
|
||||
sc_screen_otg_capture_mouse(struct sc_screen_otg *screen, bool capture) {
|
||||
assert(screen->mouse);
|
||||
if (SDL_SetRelativeMouseMode(capture)) {
|
||||
LOGE("Could not set relative mouse mode to %s: %s",
|
||||
capture ? "true" : "false", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
screen->mouse_captured = capture;
|
||||
}
|
||||
|
||||
static void
|
||||
sc_screen_otg_render(struct sc_screen_otg *screen) {
|
||||
SDL_RenderClear(screen->renderer);
|
||||
if (screen->texture) {
|
||||
SDL_RenderCopy(screen->renderer, screen->texture, NULL, NULL);
|
||||
}
|
||||
SDL_RenderPresent(screen->renderer);
|
||||
}
|
||||
|
||||
bool
|
||||
sc_screen_otg_init(struct sc_screen_otg *screen,
|
||||
const struct sc_screen_otg_params *params) {
|
||||
screen->keyboard = params->keyboard;
|
||||
screen->mouse = params->mouse;
|
||||
|
||||
screen->mouse_captured = false;
|
||||
screen->mouse_capture_key_pressed = 0;
|
||||
|
||||
const char *title = params->window_title;
|
||||
assert(title);
|
||||
|
||||
int x = params->window_x != SC_WINDOW_POSITION_UNDEFINED
|
||||
? params->window_x : (int) SDL_WINDOWPOS_UNDEFINED;
|
||||
int y = params->window_y != SC_WINDOW_POSITION_UNDEFINED
|
||||
? params->window_y : (int) SDL_WINDOWPOS_UNDEFINED;
|
||||
int width = 256;
|
||||
int height = 256;
|
||||
|
||||
uint32_t window_flags = 0;
|
||||
if (params->always_on_top) {
|
||||
window_flags |= SDL_WINDOW_ALWAYS_ON_TOP;
|
||||
}
|
||||
if (params->window_borderless) {
|
||||
window_flags |= SDL_WINDOW_BORDERLESS;
|
||||
}
|
||||
|
||||
screen->window = SDL_CreateWindow(title, x, y, width, height, window_flags);
|
||||
if (!screen->window) {
|
||||
LOGE("Could not create window: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
screen->renderer = SDL_CreateRenderer(screen->window, -1, 0);
|
||||
if (!screen->renderer) {
|
||||
LOGE("Could not create renderer: %s", SDL_GetError());
|
||||
goto error_destroy_window;
|
||||
}
|
||||
|
||||
SDL_Surface *icon = scrcpy_icon_load();
|
||||
|
||||
if (icon) {
|
||||
SDL_SetWindowIcon(screen->window, icon);
|
||||
|
||||
screen->texture = SDL_CreateTextureFromSurface(screen->renderer, icon);
|
||||
scrcpy_icon_destroy(icon);
|
||||
if (!screen->texture) {
|
||||
goto error_destroy_renderer;
|
||||
}
|
||||
} else {
|
||||
screen->texture = NULL;
|
||||
LOGW("Could not load icon");
|
||||
}
|
||||
|
||||
if (screen->mouse) {
|
||||
// Capture mouse on start
|
||||
sc_screen_otg_capture_mouse(screen, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
error_destroy_window:
|
||||
SDL_DestroyWindow(screen->window);
|
||||
error_destroy_renderer:
|
||||
SDL_DestroyRenderer(screen->renderer);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
sc_screen_otg_destroy(struct sc_screen_otg *screen) {
|
||||
if (screen->texture) {
|
||||
SDL_DestroyTexture(screen->texture);
|
||||
}
|
||||
SDL_DestroyRenderer(screen->renderer);
|
||||
SDL_DestroyWindow(screen->window);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
sc_screen_otg_is_mouse_capture_key(SDL_Keycode key) {
|
||||
return key == SDLK_LALT || key == SDLK_LGUI || key == SDLK_RGUI;
|
||||
}
|
||||
|
||||
static void
|
||||
sc_screen_otg_process_key(struct sc_screen_otg *screen,
|
||||
const SDL_KeyboardEvent *event) {
|
||||
assert(screen->keyboard);
|
||||
struct sc_key_processor *kp = &screen->keyboard->key_processor;
|
||||
|
||||
struct sc_key_event evt = {
|
||||
.action = sc_action_from_sdl_keyboard_type(event->type),
|
||||
.keycode = sc_keycode_from_sdl(event->keysym.sym),
|
||||
.scancode = sc_scancode_from_sdl(event->keysym.scancode),
|
||||
.repeat = event->repeat,
|
||||
.mods_state = sc_mods_state_from_sdl(event->keysym.mod),
|
||||
};
|
||||
|
||||
assert(kp->ops->process_key);
|
||||
kp->ops->process_key(kp, &evt, SC_SEQUENCE_INVALID);
|
||||
}
|
||||
|
||||
static void
|
||||
sc_screen_otg_process_mouse_motion(struct sc_screen_otg *screen,
|
||||
const SDL_MouseMotionEvent *event) {
|
||||
assert(screen->mouse);
|
||||
struct sc_mouse_processor *mp = &screen->mouse->mouse_processor;
|
||||
|
||||
struct sc_mouse_motion_event evt = {
|
||||
// .position not used for HID events
|
||||
.xrel = event->xrel,
|
||||
.yrel = event->yrel,
|
||||
.buttons_state = sc_mouse_buttons_state_from_sdl(event->state, true),
|
||||
};
|
||||
|
||||
assert(mp->ops->process_mouse_motion);
|
||||
mp->ops->process_mouse_motion(mp, &evt);
|
||||
}
|
||||
|
||||
static void
|
||||
sc_screen_otg_process_mouse_button(struct sc_screen_otg *screen,
|
||||
const SDL_MouseButtonEvent *event) {
|
||||
assert(screen->mouse);
|
||||
struct sc_mouse_processor *mp = &screen->mouse->mouse_processor;
|
||||
|
||||
uint32_t sdl_buttons_state = SDL_GetMouseState(NULL, NULL);
|
||||
|
||||
struct sc_mouse_click_event evt = {
|
||||
// .position not used for HID events
|
||||
.action = sc_action_from_sdl_mousebutton_type(event->type),
|
||||
.button = sc_mouse_button_from_sdl(event->button),
|
||||
.buttons_state =
|
||||
sc_mouse_buttons_state_from_sdl(sdl_buttons_state, true),
|
||||
};
|
||||
|
||||
assert(mp->ops->process_mouse_click);
|
||||
mp->ops->process_mouse_click(mp, &evt);
|
||||
}
|
||||
|
||||
static void
|
||||
sc_screen_otg_process_mouse_wheel(struct sc_screen_otg *screen,
|
||||
const SDL_MouseWheelEvent *event) {
|
||||
assert(screen->mouse);
|
||||
struct sc_mouse_processor *mp = &screen->mouse->mouse_processor;
|
||||
|
||||
uint32_t sdl_buttons_state = SDL_GetMouseState(NULL, NULL);
|
||||
|
||||
struct sc_mouse_scroll_event evt = {
|
||||
// .position not used for HID events
|
||||
.hscroll = event->x,
|
||||
.vscroll = event->y,
|
||||
.buttons_state =
|
||||
sc_mouse_buttons_state_from_sdl(sdl_buttons_state, true),
|
||||
};
|
||||
|
||||
assert(mp->ops->process_mouse_scroll);
|
||||
mp->ops->process_mouse_scroll(mp, &evt);
|
||||
}
|
||||
|
||||
void
|
||||
sc_screen_otg_handle_event(struct sc_screen_otg *screen, SDL_Event *event) {
|
||||
switch (event->type) {
|
||||
case SDL_WINDOWEVENT:
|
||||
switch (event->window.event) {
|
||||
case SDL_WINDOWEVENT_EXPOSED:
|
||||
sc_screen_otg_render(screen);
|
||||
break;
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
if (screen->mouse) {
|
||||
sc_screen_otg_capture_mouse(screen, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return;
|
||||
case SDL_KEYDOWN:
|
||||
if (screen->mouse) {
|
||||
SDL_Keycode key = event->key.keysym.sym;
|
||||
if (sc_screen_otg_is_mouse_capture_key(key)) {
|
||||
if (!screen->mouse_capture_key_pressed) {
|
||||
screen->mouse_capture_key_pressed = key;
|
||||
} else {
|
||||
// Another mouse capture key has been pressed, cancel
|
||||
// mouse (un)capture
|
||||
screen->mouse_capture_key_pressed = 0;
|
||||
}
|
||||
// Mouse capture keys are never forwarded to the device
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (screen->keyboard) {
|
||||
sc_screen_otg_process_key(screen, &event->key);
|
||||
}
|
||||
break;
|
||||
case SDL_KEYUP:
|
||||
if (screen->mouse) {
|
||||
SDL_Keycode key = event->key.keysym.sym;
|
||||
SDL_Keycode cap = screen->mouse_capture_key_pressed;
|
||||
screen->mouse_capture_key_pressed = 0;
|
||||
if (sc_screen_otg_is_mouse_capture_key(key)) {
|
||||
if (key == cap) {
|
||||
// A mouse capture key has been pressed then released:
|
||||
// toggle the capture mouse mode
|
||||
sc_screen_otg_capture_mouse(screen,
|
||||
!screen->mouse_captured);
|
||||
}
|
||||
// Mouse capture keys are never forwarded to the device
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (screen->keyboard) {
|
||||
sc_screen_otg_process_key(screen, &event->key);
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEMOTION:
|
||||
if (screen->mouse && screen->mouse_captured) {
|
||||
sc_screen_otg_process_mouse_motion(screen, &event->motion);
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
if (screen->mouse && screen->mouse_captured) {
|
||||
sc_screen_otg_process_mouse_button(screen, &event->button);
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
if (screen->mouse) {
|
||||
if (screen->mouse_captured) {
|
||||
sc_screen_otg_process_mouse_button(screen, &event->button);
|
||||
} else {
|
||||
sc_screen_otg_capture_mouse(screen, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEWHEEL:
|
||||
if (screen->mouse && screen->mouse_captured) {
|
||||
sc_screen_otg_process_mouse_wheel(screen, &event->wheel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
#ifndef SC_SCREEN_OTG_H
|
||||
#define SC_SCREEN_OTG_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "hid_keyboard.h"
|
||||
#include "hid_mouse.h"
|
||||
|
||||
struct sc_screen_otg {
|
||||
struct sc_hid_keyboard *keyboard;
|
||||
struct sc_hid_mouse *mouse;
|
||||
|
||||
SDL_Window *window;
|
||||
SDL_Renderer *renderer;
|
||||
SDL_Texture *texture;
|
||||
|
||||
// See equivalent mechanism in screen.h
|
||||
bool mouse_captured;
|
||||
SDL_Keycode mouse_capture_key_pressed;
|
||||
};
|
||||
|
||||
struct sc_screen_otg_params {
|
||||
struct sc_hid_keyboard *keyboard;
|
||||
struct sc_hid_mouse *mouse;
|
||||
|
||||
const char *window_title;
|
||||
bool always_on_top;
|
||||
int16_t window_x; // accepts SC_WINDOW_POSITION_UNDEFINED
|
||||
int16_t window_y; // accepts SC_WINDOW_POSITION_UNDEFINED
|
||||
bool window_borderless;
|
||||
};
|
||||
|
||||
bool
|
||||
sc_screen_otg_init(struct sc_screen_otg *screen,
|
||||
const struct sc_screen_otg_params *params);
|
||||
|
||||
void
|
||||
sc_screen_otg_destroy(struct sc_screen_otg *screen);
|
||||
|
||||
void
|
||||
sc_screen_otg_handle_event(struct sc_screen_otg *screen, SDL_Event *event);
|
||||
|
||||
#endif
|
||||
@@ -1,264 +0,0 @@
|
||||
#include "usb.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static inline void
|
||||
log_libusb_error(enum libusb_error errcode) {
|
||||
LOGW("libusb error: %s", libusb_strerror(errcode));
|
||||
}
|
||||
|
||||
static char *
|
||||
read_string(libusb_device_handle *handle, uint8_t desc_index) {
|
||||
char buffer[128];
|
||||
int result =
|
||||
libusb_get_string_descriptor_ascii(handle, desc_index,
|
||||
(unsigned char *) buffer,
|
||||
sizeof(buffer));
|
||||
if (result < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assert((size_t) result <= sizeof(buffer));
|
||||
|
||||
// When non-negative, 'result' contains the number of bytes written
|
||||
char *s = malloc(result + 1);
|
||||
memcpy(s, buffer, result);
|
||||
s[result] = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
static bool
|
||||
accept_device(libusb_device *device, const char *serial,
|
||||
struct sc_usb_device *out) {
|
||||
// Do not log any USB error in this function, it is expected that many USB
|
||||
// devices available on the computer have permission restrictions
|
||||
|
||||
struct libusb_device_descriptor desc;
|
||||
int result = libusb_get_device_descriptor(device, &desc);
|
||||
if (result < 0 || !desc.iSerialNumber) {
|
||||
return false;
|
||||
}
|
||||
|
||||
libusb_device_handle *handle;
|
||||
result = libusb_open(device, &handle);
|
||||
if (result < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char *device_serial = read_string(handle, desc.iSerialNumber);
|
||||
if (!device_serial) {
|
||||
libusb_close(handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (serial) {
|
||||
// Filter by serial
|
||||
bool matches = !strcmp(serial, device_serial);
|
||||
if (!matches) {
|
||||
free(device_serial);
|
||||
libusb_close(handle);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
out->device = libusb_ref_device(device);
|
||||
out->serial = device_serial;
|
||||
out->vid = desc.idVendor;
|
||||
out->pid = desc.idProduct;
|
||||
out->manufacturer = read_string(handle, desc.iManufacturer);
|
||||
out->product = read_string(handle, desc.iProduct);
|
||||
|
||||
libusb_close(handle);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
sc_usb_device_destroy(struct sc_usb_device *usb_device) {
|
||||
libusb_unref_device(usb_device->device);
|
||||
free(usb_device->serial);
|
||||
free(usb_device->manufacturer);
|
||||
free(usb_device->product);
|
||||
}
|
||||
|
||||
void
|
||||
sc_usb_device_destroy_all(struct sc_usb_device *usb_devices, size_t count) {
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
sc_usb_device_destroy(&usb_devices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t
|
||||
sc_usb_find_devices(struct sc_usb *usb, const char *serial,
|
||||
struct sc_usb_device *devices, size_t len) {
|
||||
libusb_device **list;
|
||||
ssize_t count = libusb_get_device_list(usb->context, &list);
|
||||
if (count < 0) {
|
||||
log_libusb_error((enum libusb_error) count);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t idx = 0;
|
||||
for (size_t i = 0; i < (size_t) count && idx < len; ++i) {
|
||||
libusb_device *device = list[i];
|
||||
|
||||
if (accept_device(device, serial, &devices[idx])) {
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
|
||||
libusb_free_device_list(list, 1);
|
||||
return idx;
|
||||
}
|
||||
|
||||
static libusb_device_handle *
|
||||
sc_usb_open_handle(libusb_device *device) {
|
||||
libusb_device_handle *handle;
|
||||
int result = libusb_open(device, &handle);
|
||||
if (result < 0) {
|
||||
log_libusb_error((enum libusb_error) result);
|
||||
return NULL;
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
bool
|
||||
sc_usb_init(struct sc_usb *usb) {
|
||||
usb->handle = NULL;
|
||||
return libusb_init(&usb->context) == LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
void
|
||||
sc_usb_destroy(struct sc_usb *usb) {
|
||||
libusb_exit(usb->context);
|
||||
}
|
||||
|
||||
static int
|
||||
sc_usb_libusb_callback(libusb_context *ctx, libusb_device *device,
|
||||
libusb_hotplug_event event, void *userdata) {
|
||||
(void) ctx;
|
||||
(void) device;
|
||||
(void) event;
|
||||
|
||||
struct sc_usb *usb = userdata;
|
||||
|
||||
libusb_device *dev = libusb_get_device(usb->handle);
|
||||
assert(dev);
|
||||
if (dev != device) {
|
||||
// Not the connected device
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(usb->cbs && usb->cbs->on_disconnected);
|
||||
usb->cbs->on_disconnected(usb, usb->cbs_userdata);
|
||||
|
||||
// Do not automatically deregister the callback by returning 1. Instead,
|
||||
// manually deregister to interrupt libusb_handle_events() from the libusb
|
||||
// event thread: <https://stackoverflow.com/a/60119225/1987178>
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
run_libusb_event_handler(void *data) {
|
||||
struct sc_usb *usb = data;
|
||||
while (!atomic_load(&usb->stopped)) {
|
||||
// Interrupted by events or by libusb_hotplug_deregister_callback()
|
||||
libusb_handle_events(usb->context);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
sc_usb_register_callback(struct sc_usb *usb) {
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
|
||||
LOGW("libusb does not have hotplug capability");
|
||||
return false;
|
||||
}
|
||||
|
||||
libusb_device *device = libusb_get_device(usb->handle);
|
||||
assert(device);
|
||||
|
||||
struct libusb_device_descriptor desc;
|
||||
int result = libusb_get_device_descriptor(device, &desc);
|
||||
if (result < 0) {
|
||||
log_libusb_error((enum libusb_error) result);
|
||||
LOGW("Could not read USB device descriptor");
|
||||
return false;
|
||||
}
|
||||
|
||||
int events = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT;
|
||||
int flags = LIBUSB_HOTPLUG_NO_FLAGS;
|
||||
int vendor_id = desc.idVendor;
|
||||
int product_id = desc.idProduct;
|
||||
int dev_class = LIBUSB_HOTPLUG_MATCH_ANY;
|
||||
result = libusb_hotplug_register_callback(usb->context, events, flags,
|
||||
vendor_id, product_id, dev_class,
|
||||
sc_usb_libusb_callback, usb,
|
||||
&usb->callback_handle);
|
||||
if (result < 0) {
|
||||
log_libusb_error((enum libusb_error) result);
|
||||
LOGW("Could not register USB callback");
|
||||
return false;
|
||||
}
|
||||
|
||||
usb->has_callback_handle = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
sc_usb_connect(struct sc_usb *usb, libusb_device *device,
|
||||
const struct sc_usb_callbacks *cbs, void *cbs_userdata) {
|
||||
usb->handle = sc_usb_open_handle(device);
|
||||
if (!usb->handle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
usb->has_callback_handle = false;
|
||||
usb->has_libusb_event_thread = false;
|
||||
|
||||
// If cbs is set, then cbs->on_disconnected must be set
|
||||
assert(!cbs || cbs->on_disconnected);
|
||||
usb->cbs = cbs;
|
||||
usb->cbs_userdata = cbs_userdata;
|
||||
|
||||
if (cbs) {
|
||||
atomic_init(&usb->stopped, false);
|
||||
if (sc_usb_register_callback(usb)) {
|
||||
// Create a thread to process libusb events, so that device
|
||||
// disconnection could be detected immediately
|
||||
usb->has_libusb_event_thread =
|
||||
sc_thread_create(&usb->libusb_event_thread,
|
||||
run_libusb_event_handler, "scrcpy-usbev", usb);
|
||||
if (!usb->has_libusb_event_thread) {
|
||||
LOGW("Libusb event thread handler could not be created, USB "
|
||||
"device disconnection might not be detected immediately");
|
||||
}
|
||||
} else {
|
||||
LOGW("Could not register USB device disconnection callback");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
sc_usb_disconnect(struct sc_usb *usb) {
|
||||
libusb_close(usb->handle);
|
||||
}
|
||||
|
||||
void
|
||||
sc_usb_stop(struct sc_usb *usb) {
|
||||
if (usb->has_callback_handle) {
|
||||
atomic_store(&usb->stopped, true);
|
||||
libusb_hotplug_deregister_callback(usb->context, usb->callback_handle);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sc_usb_join(struct sc_usb *usb) {
|
||||
if (usb->has_libusb_event_thread) {
|
||||
sc_thread_join(&usb->libusb_event_thread, NULL);
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
#ifndef SC_USB_H
|
||||
#define SC_USB_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <libusb-1.0/libusb.h>
|
||||
|
||||
#include "util/thread.h"
|
||||
|
||||
struct sc_usb {
|
||||
libusb_context *context;
|
||||
libusb_device_handle *handle;
|
||||
|
||||
const struct sc_usb_callbacks *cbs;
|
||||
void *cbs_userdata;
|
||||
|
||||
bool has_callback_handle;
|
||||
libusb_hotplug_callback_handle callback_handle;
|
||||
|
||||
bool has_libusb_event_thread;
|
||||
sc_thread libusb_event_thread;
|
||||
|
||||
atomic_bool stopped; // only used if cbs != NULL
|
||||
};
|
||||
|
||||
struct sc_usb_callbacks {
|
||||
void (*on_disconnected)(struct sc_usb *usb, void *userdata);
|
||||
};
|
||||
|
||||
struct sc_usb_device {
|
||||
libusb_device *device;
|
||||
char *serial;
|
||||
char *manufacturer;
|
||||
char *product;
|
||||
uint16_t vid;
|
||||
uint16_t pid;
|
||||
};
|
||||
|
||||
void
|
||||
sc_usb_device_destroy(struct sc_usb_device *usb_device);
|
||||
|
||||
void
|
||||
sc_usb_device_destroy_all(struct sc_usb_device *usb_devices, size_t count);
|
||||
|
||||
bool
|
||||
sc_usb_init(struct sc_usb *usb);
|
||||
|
||||
void
|
||||
sc_usb_destroy(struct sc_usb *usb);
|
||||
|
||||
ssize_t
|
||||
sc_usb_find_devices(struct sc_usb *usb, const char *serial,
|
||||
struct sc_usb_device *devices, size_t len);
|
||||
|
||||
bool
|
||||
sc_usb_connect(struct sc_usb *usb, libusb_device *device,
|
||||
const struct sc_usb_callbacks *cbs, void *cbs_userdata);
|
||||
|
||||
void
|
||||
sc_usb_disconnect(struct sc_usb *usb);
|
||||
|
||||
void
|
||||
sc_usb_stop(struct sc_usb *usb);
|
||||
|
||||
void
|
||||
sc_usb_join(struct sc_usb *usb);
|
||||
|
||||
#endif
|
||||
@@ -20,4 +20,4 @@ ffmpeg_avcodec = 'avcodec-58'
|
||||
ffmpeg_avformat = 'avformat-58'
|
||||
ffmpeg_avutil = 'avutil-56'
|
||||
prebuilt_ffmpeg = 'ffmpeg-4.3.1-win32-shared'
|
||||
prebuilt_sdl2 = 'SDL2-2.0.20/i686-w64-mingw32'
|
||||
prebuilt_sdl2 = 'SDL2-2.0.18/i686-w64-mingw32'
|
||||
|
||||
@@ -20,4 +20,4 @@ ffmpeg_avcodec = 'avcodec-59'
|
||||
ffmpeg_avformat = 'avformat-59'
|
||||
ffmpeg_avutil = 'avutil-57'
|
||||
prebuilt_ffmpeg = 'ffmpeg-5.0-full_build-shared'
|
||||
prebuilt_sdl2 = 'SDL2-2.0.20/x86_64-w64-mingw32'
|
||||
prebuilt_sdl2 = 'SDL2-2.0.18/x86_64-w64-mingw32'
|
||||
|
||||
@@ -23,11 +23,11 @@ prepare-ffmpeg-win64:
|
||||
ffmpeg-5.0-full_build-shared
|
||||
|
||||
prepare-sdl2:
|
||||
@./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.20-mingw.tar.gz \
|
||||
38094d82a857d6c62352e5c5cdec74948c5b4d25c59cbd298d6d233568976bd1 \
|
||||
SDL2-2.0.20
|
||||
@./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.18-mingw.tar.gz \
|
||||
bbad7c6947f6ca3e05292f065852ed8b62f319fc5533047e7708769c4dbae394 \
|
||||
SDL2-2.0.18
|
||||
|
||||
prepare-adb:
|
||||
@./prepare-dep https://dl.google.com/android/repository/platform-tools_r32.0.0-windows.zip \
|
||||
41f4c7512b32cbb3f8c624c20b56326abb692a6f169b03b4b63b6c5a6fdbb08c \
|
||||
@./prepare-dep https://dl.google.com/android/repository/platform-tools_r31.0.3-windows.zip \
|
||||
0f4b8fdd26af2c3733539d6eebb3c2ed499ea1d4bb1f4e0ecc2d6016961a6e24 \
|
||||
platform-tools
|
||||
|
||||
@@ -102,7 +102,7 @@ dist-win32: build-server build-win32
|
||||
cp prebuilt-deps/platform-tools/adb.exe "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/SDL2-2.0.20/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/SDL2-2.0.18/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
|
||||
dist-win64: build-server build-win64
|
||||
mkdir -p "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
@@ -120,7 +120,7 @@ dist-win64: build-server build-win64
|
||||
cp prebuilt-deps/platform-tools/adb.exe "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/SDL2-2.0.20/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/SDL2-2.0.18/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
|
||||
zip-win32: dist-win32
|
||||
cd "$(DIST)/$(WIN32_TARGET_DIR)"; \
|
||||
|
||||
@@ -52,8 +52,7 @@ public final class Server {
|
||||
}
|
||||
|
||||
try {
|
||||
CleanUp.configure(options.getDisplayId(), restoreStayOn, mustDisableShowTouchesOnCleanUp, restoreNormalPowerMode,
|
||||
options.getPowerOffScreenOnClose());
|
||||
CleanUp.configure(options.getDisplayId(), restoreStayOn, mustDisableShowTouchesOnCleanUp, restoreNormalPowerMode, options.getPowerOffScreenOnClose());
|
||||
} catch (IOException e) {
|
||||
Ln.e("Could not configure cleanup", e);
|
||||
}
|
||||
@@ -262,7 +261,6 @@ public final class Server {
|
||||
options.setSendFrameMeta(false);
|
||||
options.setSendDummyByte(false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Ln.w("Unknown server option: " + key);
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user