Make AOA open and close asynchronous
For AOA keyboard and mouse, only input reports were asynchronous. Register/unregister were called from the main thread. This had the benefit to fail immediately if the AOA registration failed, but we want to open/close AOA devices dynamically in order to add gamepad support. PR #5270 <https://github.com/Genymobile/scrcpy/pull/5270>
This commit is contained in:
parent
33ddccf9f6
commit
952fc75676
@ -16,7 +16,8 @@
|
|||||||
|
|
||||||
#define DEFAULT_TIMEOUT 1000
|
#define DEFAULT_TIMEOUT 1000
|
||||||
|
|
||||||
#define SC_AOA_EVENT_QUEUE_MAX 64
|
// Drop droppable events above this limit
|
||||||
|
#define SC_AOA_EVENT_QUEUE_LIMIT 60
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sc_hid_event_log(const struct sc_hid_event *event) {
|
sc_hid_event_log(const struct sc_hid_event *event) {
|
||||||
@ -35,7 +36,8 @@ sc_aoa_init(struct sc_aoa *aoa, struct sc_usb *usb,
|
|||||||
struct sc_acksync *acksync) {
|
struct sc_acksync *acksync) {
|
||||||
sc_vecdeque_init(&aoa->queue);
|
sc_vecdeque_init(&aoa->queue);
|
||||||
|
|
||||||
if (!sc_vecdeque_reserve(&aoa->queue, SC_AOA_EVENT_QUEUE_MAX)) {
|
// Add 4 to support 4 non-droppable events without re-allocation
|
||||||
|
if (!sc_vecdeque_reserve(&aoa->queue, SC_AOA_EVENT_QUEUE_LIMIT + 4)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +151,7 @@ sc_aoa_send_hid_event(struct sc_aoa *aoa, const struct sc_hid_event *event) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
static bool
|
||||||
sc_aoa_unregister_hid(struct sc_aoa *aoa, uint16_t accessory_id) {
|
sc_aoa_unregister_hid(struct sc_aoa *aoa, uint16_t accessory_id) {
|
||||||
uint8_t request_type = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR;
|
uint8_t request_type = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR;
|
||||||
uint8_t request = ACCESSORY_UNREGISTER_HID;
|
uint8_t request = ACCESSORY_UNREGISTER_HID;
|
||||||
@ -172,7 +174,7 @@ sc_aoa_unregister_hid(struct sc_aoa *aoa, uint16_t accessory_id) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
static bool
|
||||||
sc_aoa_setup_hid(struct sc_aoa *aoa, uint16_t accessory_id,
|
sc_aoa_setup_hid(struct sc_aoa *aoa, uint16_t accessory_id,
|
||||||
const uint8_t *report_desc, uint16_t report_desc_size) {
|
const uint8_t *report_desc, uint16_t report_desc_size) {
|
||||||
bool ok = sc_aoa_register_hid(aoa, accessory_id, report_desc_size);
|
bool ok = sc_aoa_register_hid(aoa, accessory_id, report_desc_size);
|
||||||
@ -201,55 +203,145 @@ sc_aoa_push_hid_event_with_ack_to_wait(struct sc_aoa *aoa,
|
|||||||
}
|
}
|
||||||
|
|
||||||
sc_mutex_lock(&aoa->mutex);
|
sc_mutex_lock(&aoa->mutex);
|
||||||
bool full = sc_vecdeque_is_full(&aoa->queue);
|
|
||||||
if (!full) {
|
bool pushed = false;
|
||||||
|
|
||||||
|
size_t size = sc_vecdeque_size(&aoa->queue);
|
||||||
|
if (size < SC_AOA_EVENT_QUEUE_LIMIT) {
|
||||||
bool was_empty = sc_vecdeque_is_empty(&aoa->queue);
|
bool was_empty = sc_vecdeque_is_empty(&aoa->queue);
|
||||||
|
|
||||||
struct sc_aoa_event *aoa_event =
|
struct sc_aoa_event *aoa_event =
|
||||||
sc_vecdeque_push_hole_noresize(&aoa->queue);
|
sc_vecdeque_push_hole_noresize(&aoa->queue);
|
||||||
aoa_event->hid = *event;
|
aoa_event->type = SC_AOA_EVENT_TYPE_INPUT;
|
||||||
aoa_event->ack_to_wait = ack_to_wait;
|
aoa_event->input.hid = *event;
|
||||||
|
aoa_event->input.ack_to_wait = ack_to_wait;
|
||||||
|
pushed = true;
|
||||||
|
|
||||||
if (was_empty) {
|
if (was_empty) {
|
||||||
sc_cond_signal(&aoa->event_cond);
|
sc_cond_signal(&aoa->event_cond);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Otherwise (if the queue is full), the event is discarded
|
// Otherwise, the event is discarded
|
||||||
|
|
||||||
sc_mutex_unlock(&aoa->mutex);
|
sc_mutex_unlock(&aoa->mutex);
|
||||||
|
|
||||||
return !full;
|
return pushed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
sc_aoa_push_open(struct sc_aoa *aoa, uint16_t accessory_id,
|
||||||
|
const uint8_t *report_desc, uint16_t report_desc_size) {
|
||||||
|
// TODO log verbose
|
||||||
|
|
||||||
|
sc_mutex_lock(&aoa->mutex);
|
||||||
|
bool was_empty = sc_vecdeque_is_empty(&aoa->queue);
|
||||||
|
|
||||||
|
// an OPEN event is non-droppable, so push it to the queue even above the
|
||||||
|
// SC_AOA_EVENT_QUEUE_LIMIT
|
||||||
|
struct sc_aoa_event *aoa_event = sc_vecdeque_push_hole(&aoa->queue);
|
||||||
|
if (!aoa_event) {
|
||||||
|
LOG_OOM();
|
||||||
|
sc_mutex_unlock(&aoa->mutex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
aoa_event->type = SC_AOA_EVENT_TYPE_OPEN;
|
||||||
|
aoa_event->open.hid_id = accessory_id;
|
||||||
|
aoa_event->open.report_desc = report_desc;
|
||||||
|
aoa_event->open.report_desc_size = report_desc_size;
|
||||||
|
|
||||||
|
if (was_empty) {
|
||||||
|
sc_cond_signal(&aoa->event_cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
sc_mutex_unlock(&aoa->mutex);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
sc_aoa_push_close(struct sc_aoa *aoa, uint16_t accessory_id) {
|
||||||
|
// TODO log verbose
|
||||||
|
|
||||||
|
sc_mutex_lock(&aoa->mutex);
|
||||||
|
bool was_empty = sc_vecdeque_is_empty(&aoa->queue);
|
||||||
|
|
||||||
|
// a CLOSE event is non-droppable, so push it to the queue even above the
|
||||||
|
// SC_AOA_EVENT_QUEUE_LIMIT
|
||||||
|
struct sc_aoa_event *aoa_event = sc_vecdeque_push_hole(&aoa->queue);
|
||||||
|
if (!aoa_event) {
|
||||||
|
LOG_OOM();
|
||||||
|
sc_mutex_unlock(&aoa->mutex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
aoa_event->type = SC_AOA_EVENT_TYPE_CLOSE;
|
||||||
|
aoa_event->close.hid_id = accessory_id;
|
||||||
|
|
||||||
|
if (was_empty) {
|
||||||
|
sc_cond_signal(&aoa->event_cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
sc_mutex_unlock(&aoa->mutex);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
sc_aoa_process_event(struct sc_aoa *aoa, struct sc_aoa_event *event) {
|
sc_aoa_process_event(struct sc_aoa *aoa, struct sc_aoa_event *event) {
|
||||||
uint64_t ack_to_wait = event->ack_to_wait;
|
switch (event->type) {
|
||||||
if (ack_to_wait != SC_SEQUENCE_INVALID) {
|
case SC_AOA_EVENT_TYPE_INPUT: {
|
||||||
LOGD("Waiting ack from server sequence=%" PRIu64_, ack_to_wait);
|
uint64_t ack_to_wait = event->input.ack_to_wait;
|
||||||
|
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
|
// If some events have ack_to_wait set, then sc_aoa must have
|
||||||
// initialized with a non NULL acksync
|
// been initialized with a non NULL acksync
|
||||||
assert(aoa->acksync);
|
assert(aoa->acksync);
|
||||||
|
|
||||||
// Do not block the loop indefinitely if the ack never comes (it
|
// Do not block the loop indefinitely if the ack never comes (it
|
||||||
// should never happen)
|
// should never happen)
|
||||||
sc_tick deadline = sc_tick_now() + SC_TICK_FROM_MS(500);
|
sc_tick deadline = sc_tick_now() + SC_TICK_FROM_MS(500);
|
||||||
enum sc_acksync_wait_result result =
|
enum sc_acksync_wait_result result =
|
||||||
sc_acksync_wait(aoa->acksync, ack_to_wait, deadline);
|
sc_acksync_wait(aoa->acksync, ack_to_wait, deadline);
|
||||||
|
|
||||||
if (result == SC_ACKSYNC_WAIT_TIMEOUT) {
|
if (result == SC_ACKSYNC_WAIT_TIMEOUT) {
|
||||||
LOGW("Ack not received after 500ms, discarding HID event");
|
LOGW("Ack not received after 500ms, discarding HID event");
|
||||||
// continue to process events
|
// continue to process events
|
||||||
return true;
|
return true;
|
||||||
} else if (result == SC_ACKSYNC_WAIT_INTR) {
|
} else if (result == SC_ACKSYNC_WAIT_INTR) {
|
||||||
// stopped
|
// stopped
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok = sc_aoa_send_hid_event(aoa, &event->input.hid);
|
||||||
|
if (!ok) {
|
||||||
|
LOGW("Could not send HID event to USB device: %" PRIu16,
|
||||||
|
event->input.hid.hid_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
case SC_AOA_EVENT_TYPE_OPEN: {
|
||||||
|
bool ok = sc_aoa_setup_hid(aoa, event->open.hid_id,
|
||||||
|
event->open.report_desc,
|
||||||
|
event->open.report_desc_size);
|
||||||
|
if (!ok) {
|
||||||
|
LOGW("Could not open AOA device: %" PRIu16, event->open.hid_id);
|
||||||
|
}
|
||||||
|
|
||||||
bool ok = sc_aoa_send_hid_event(aoa, &event->hid);
|
break;
|
||||||
if (!ok) {
|
}
|
||||||
LOGW("Could not send HID event to USB device");
|
case SC_AOA_EVENT_TYPE_CLOSE: {
|
||||||
|
bool ok = sc_aoa_unregister_hid(aoa, event->close.hid_id);
|
||||||
|
if (!ok) {
|
||||||
|
LOGW("Could not close AOA device: %" PRIu16,
|
||||||
|
event->close.hid_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// continue to process events
|
// continue to process events
|
||||||
|
@ -13,9 +13,28 @@
|
|||||||
#include "util/tick.h"
|
#include "util/tick.h"
|
||||||
#include "util/vecdeque.h"
|
#include "util/vecdeque.h"
|
||||||
|
|
||||||
|
enum sc_aoa_event_type {
|
||||||
|
SC_AOA_EVENT_TYPE_OPEN,
|
||||||
|
SC_AOA_EVENT_TYPE_INPUT,
|
||||||
|
SC_AOA_EVENT_TYPE_CLOSE,
|
||||||
|
};
|
||||||
|
|
||||||
struct sc_aoa_event {
|
struct sc_aoa_event {
|
||||||
struct sc_hid_event hid;
|
enum sc_aoa_event_type type;
|
||||||
uint64_t ack_to_wait;
|
union {
|
||||||
|
struct {
|
||||||
|
uint16_t hid_id;
|
||||||
|
const uint8_t *report_desc; // pointer to static memory
|
||||||
|
uint16_t report_desc_size;
|
||||||
|
} open;
|
||||||
|
struct {
|
||||||
|
uint16_t hid_id;
|
||||||
|
} close;
|
||||||
|
struct {
|
||||||
|
struct sc_hid_event hid;
|
||||||
|
uint64_t ack_to_wait;
|
||||||
|
} input;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sc_aoa_event_queue SC_VECDEQUE(struct sc_aoa_event);
|
struct sc_aoa_event_queue SC_VECDEQUE(struct sc_aoa_event);
|
||||||
@ -46,12 +65,21 @@ sc_aoa_stop(struct sc_aoa *aoa);
|
|||||||
void
|
void
|
||||||
sc_aoa_join(struct sc_aoa *aoa);
|
sc_aoa_join(struct sc_aoa *aoa);
|
||||||
|
|
||||||
|
//bool
|
||||||
|
//sc_aoa_setup_hid(struct sc_aoa *aoa, uint16_t accessory_id,
|
||||||
|
// const uint8_t *report_desc, uint16_t report_desc_size);
|
||||||
|
//
|
||||||
|
//bool
|
||||||
|
//sc_aoa_unregister_hid(struct sc_aoa *aoa, uint16_t accessory_id);
|
||||||
|
|
||||||
|
// report_desc must be a pointer to static memory, accessed at any time from
|
||||||
|
// another thread
|
||||||
bool
|
bool
|
||||||
sc_aoa_setup_hid(struct sc_aoa *aoa, uint16_t accessory_id,
|
sc_aoa_push_open(struct sc_aoa *aoa, uint16_t accessory_id,
|
||||||
const uint8_t *report_desc, uint16_t report_desc_size);
|
const uint8_t *report_desc, uint16_t report_desc_size);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sc_aoa_unregister_hid(struct sc_aoa *aoa, uint16_t accessory_id);
|
sc_aoa_push_close(struct sc_aoa *aoa, uint16_t accessory_id);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sc_aoa_push_hid_event_with_ack_to_wait(struct sc_aoa *aoa,
|
sc_aoa_push_hid_event_with_ack_to_wait(struct sc_aoa *aoa,
|
||||||
|
@ -66,11 +66,11 @@ bool
|
|||||||
sc_keyboard_aoa_init(struct sc_keyboard_aoa *kb, struct sc_aoa *aoa) {
|
sc_keyboard_aoa_init(struct sc_keyboard_aoa *kb, struct sc_aoa *aoa) {
|
||||||
kb->aoa = aoa;
|
kb->aoa = aoa;
|
||||||
|
|
||||||
bool ok = sc_aoa_setup_hid(aoa, SC_HID_ID_KEYBOARD,
|
bool ok = sc_aoa_push_open(aoa, SC_HID_ID_KEYBOARD,
|
||||||
SC_HID_KEYBOARD_REPORT_DESC,
|
SC_HID_KEYBOARD_REPORT_DESC,
|
||||||
SC_HID_KEYBOARD_REPORT_DESC_LEN);
|
SC_HID_KEYBOARD_REPORT_DESC_LEN);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGW("Register HID keyboard failed");
|
LOGW("Could not push AOA keyboard open request");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,9 +97,8 @@ sc_keyboard_aoa_init(struct sc_keyboard_aoa *kb, struct sc_aoa *aoa) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
sc_keyboard_aoa_destroy(struct sc_keyboard_aoa *kb) {
|
sc_keyboard_aoa_destroy(struct sc_keyboard_aoa *kb) {
|
||||||
// Unregister HID keyboard so the soft keyboard shows again on Android
|
bool ok = sc_aoa_push_close(kb->aoa, SC_HID_ID_KEYBOARD);
|
||||||
bool ok = sc_aoa_unregister_hid(kb->aoa, SC_HID_ID_KEYBOARD);
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGW("Could not unregister HID keyboard");
|
LOGW("Could not push AOA keyboard close request");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,11 +52,11 @@ bool
|
|||||||
sc_mouse_aoa_init(struct sc_mouse_aoa *mouse, struct sc_aoa *aoa) {
|
sc_mouse_aoa_init(struct sc_mouse_aoa *mouse, struct sc_aoa *aoa) {
|
||||||
mouse->aoa = aoa;
|
mouse->aoa = aoa;
|
||||||
|
|
||||||
bool ok = sc_aoa_setup_hid(aoa, SC_HID_ID_MOUSE,
|
bool ok = sc_aoa_push_open(aoa, SC_HID_ID_MOUSE,
|
||||||
SC_HID_MOUSE_REPORT_DESC,
|
SC_HID_MOUSE_REPORT_DESC,
|
||||||
SC_HID_MOUSE_REPORT_DESC_LEN);
|
SC_HID_MOUSE_REPORT_DESC_LEN);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGW("Register HID mouse failed");
|
LOGW("Could not push AOA mouse open request");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,8 +77,8 @@ sc_mouse_aoa_init(struct sc_mouse_aoa *mouse, struct sc_aoa *aoa) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
sc_mouse_aoa_destroy(struct sc_mouse_aoa *mouse) {
|
sc_mouse_aoa_destroy(struct sc_mouse_aoa *mouse) {
|
||||||
bool ok = sc_aoa_unregister_hid(mouse->aoa, SC_HID_ID_MOUSE);
|
bool ok = sc_aoa_push_close(mouse->aoa, SC_HID_ID_MOUSE);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGW("Could not unregister HID mouse");
|
LOGW("Could not push AOA mouse close request");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user