Instead of calling process_hid_report.
This adds a reference count on unix devices to make sure they are kept alive until all their input report events have been processed.
This also uses a bus-specific device list, to be able to find devices from joystick ids without having to call back to the win32 side.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winebus.sys/bus_sdl.c | 78 ++++++++++++++++++++++----------- dlls/winebus.sys/main.c | 31 ++++++++++++- dlls/winebus.sys/unix_private.h | 4 ++ dlls/winebus.sys/unixlib.c | 50 +++++++++++++++++++-- dlls/winebus.sys/unixlib.h | 8 ++++ 5 files changed, 140 insertions(+), 31 deletions(-)
diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index a0b44283182..6eafcb75285 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -59,12 +59,22 @@ WINE_DEFAULT_DEBUG_CHANNEL(plugplay);
WINE_DECLARE_DEBUG_CHANNEL(hid_report);
+static CRITICAL_SECTION sdl_cs; +static CRITICAL_SECTION_DEBUG sdl_cs_debug = +{ + 0, 0, &sdl_cs, + { &sdl_cs_debug.ProcessLocksList, &sdl_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": sdl_cs") } +}; +static CRITICAL_SECTION sdl_cs = { &sdl_cs_debug, -1, 0, 0, 0, 0 }; + static const WCHAR sdl_busidW[] = {'S','D','L','J','O','Y',0}; static struct sdl_bus_options options;
static void *sdl_handle = NULL; static UINT quit_event = -1; static struct list event_queue = LIST_INIT(event_queue); +static struct list device_list = LIST_INIT(device_list);
#define MAKE_FUNCPTR(f) static typeof(f) * p##f = NULL MAKE_FUNCPTR(SDL_GetError); @@ -136,9 +146,14 @@ static inline struct platform_private *impl_from_unix_device(struct unix_device return CONTAINING_RECORD(iface, struct platform_private, unix_device); }
-static inline struct platform_private *impl_from_DEVICE_OBJECT(DEVICE_OBJECT *device) +static struct platform_private *find_device_from_id(SDL_JoystickID id) { - return impl_from_unix_device(get_unix_device(device)); + struct platform_private *device; + + LIST_FOR_EACH_ENTRY(device, &device_list, struct platform_private, unix_device.entry) + if (device->id == id) return device; + + return NULL; }
#define CONTROLLER_NUM_BUTTONS 11 @@ -501,6 +516,10 @@ static void sdl_device_stop(struct unix_device *iface) pSDL_JoystickClose(private->sdl_joystick); if (private->sdl_controller) pSDL_GameControllerClose(private->sdl_controller); if (private->sdl_haptic) pSDL_HapticClose(private->sdl_haptic); + + EnterCriticalSection(&sdl_cs); + list_remove(&private->unix_device.entry); + LeaveCriticalSection(&sdl_cs); }
static NTSTATUS sdl_device_get_reportdescriptor(struct unix_device *iface, BYTE *buffer, @@ -586,11 +605,11 @@ static const struct unix_device_vtbl sdl_device_vtbl = sdl_device_set_feature_report, };
-static BOOL set_report_from_event(DEVICE_OBJECT *device, SDL_Event *event) +static BOOL set_report_from_event(struct platform_private *device, SDL_Event *event) { - struct platform_private *private; - private = impl_from_DEVICE_OBJECT(device); - if (private->sdl_controller) + struct unix_device *iface = &device->unix_device; + + if (device->sdl_controller) { /* We want mapped events */ return TRUE; @@ -603,9 +622,9 @@ static BOOL set_report_from_event(DEVICE_OBJECT *device, SDL_Event *event) { SDL_JoyButtonEvent *ie = &event->jbutton;
- set_button_value(private, ie->button, ie->state); + set_button_value(device, ie->button, ie->state);
- process_hid_report(device, private->report_buffer, private->buffer_length); + bus_event_queue_input_report(&event_queue, iface, device->report_buffer, device->buffer_length); break; } case SDL_JOYAXISMOTION: @@ -614,8 +633,8 @@ static BOOL set_report_from_event(DEVICE_OBJECT *device, SDL_Event *event)
if (ie->axis < 6) { - set_axis_value(private, ie->axis, ie->value, FALSE); - process_hid_report(device, private->report_buffer, private->buffer_length); + set_axis_value(device, ie->axis, ie->value, FALSE); + bus_event_queue_input_report(&event_queue, iface, device->report_buffer, device->buffer_length); } break; } @@ -623,16 +642,16 @@ static BOOL set_report_from_event(DEVICE_OBJECT *device, SDL_Event *event) { SDL_JoyBallEvent *ie = &event->jball;
- set_ball_value(private, ie->ball, ie->xrel, ie->yrel); - process_hid_report(device, private->report_buffer, private->buffer_length); + set_ball_value(device, ie->ball, ie->xrel, ie->yrel); + bus_event_queue_input_report(&event_queue, iface, device->report_buffer, device->buffer_length); break; } case SDL_JOYHATMOTION: { SDL_JoyHatEvent *ie = &event->jhat;
- set_hat_value(private, ie->hat, ie->value); - process_hid_report(device, private->report_buffer, private->buffer_length); + set_hat_value(device, ie->hat, ie->value); + bus_event_queue_input_report(&event_queue, iface, device->report_buffer, device->buffer_length); break; } default: @@ -641,10 +660,9 @@ static BOOL set_report_from_event(DEVICE_OBJECT *device, SDL_Event *event) return FALSE; }
-static BOOL set_mapped_report_from_event(DEVICE_OBJECT *device, SDL_Event *event) +static BOOL set_mapped_report_from_event(struct platform_private *device, SDL_Event *event) { - struct platform_private *private; - private = impl_from_DEVICE_OBJECT(device); + struct unix_device *iface = &device->unix_device;
switch(event->type) { @@ -672,8 +690,8 @@ static BOOL set_mapped_report_from_event(DEVICE_OBJECT *device, SDL_Event *event case SDL_CONTROLLER_BUTTON_DPAD_DOWN: case SDL_CONTROLLER_BUTTON_DPAD_LEFT: case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: - set_hat_value(private, 0, compose_dpad_value(private->sdl_controller)); - process_hid_report(device, private->report_buffer, private->buffer_length); + set_hat_value(device, 0, compose_dpad_value(device->sdl_controller)); + bus_event_queue_input_report(&event_queue, iface, device->report_buffer, device->buffer_length); break;
default: @@ -682,8 +700,8 @@ static BOOL set_mapped_report_from_event(DEVICE_OBJECT *device, SDL_Event *event
if (usage >= 0) { - set_button_value(private, usage, ie->state); - process_hid_report(device, private->report_buffer, private->buffer_length); + set_button_value(device, usage, ie->state); + bus_event_queue_input_report(&event_queue, iface, device->report_buffer, device->buffer_length); } break; } @@ -691,8 +709,8 @@ static BOOL set_mapped_report_from_event(DEVICE_OBJECT *device, SDL_Event *event { SDL_ControllerAxisEvent *ie = &event->caxis;
- set_axis_value(private, ie->axis, ie->value, TRUE); - process_hid_report(device, private->report_buffer, private->buffer_length); + set_axis_value(device, ie->axis, ie->value, TRUE); + bus_event_queue_input_report(&event_queue, iface, device->report_buffer, device->buffer_length); break; } default: @@ -759,6 +777,7 @@ static void sdl_add_device(unsigned int index) TRACE("%s id %d, desc %s.\n", controller ? "controller" : "joystick", id, debugstr_device_desc(&desc));
if (!(private = unix_device_create(&sdl_device_vtbl, sizeof(struct platform_private)))) return; + list_add_tail(&device_list, &private->unix_device.entry); private->sdl_joystick = joystick; private->sdl_controller = controller; private->id = id; @@ -768,11 +787,13 @@ static void sdl_add_device(unsigned int index)
static void process_device_event(SDL_Event *event) { - DEVICE_OBJECT *device; + struct platform_private *device; SDL_JoystickID id;
TRACE_(hid_report)("Received action %x\n", event->type);
+ EnterCriticalSection(&sdl_cs); + if (event->type == SDL_JOYDEVICEADDED) sdl_add_device(((SDL_JoyDeviceEvent *)event)->which); else if (event->type == SDL_JOYDEVICEREMOVED) @@ -783,17 +804,19 @@ static void process_device_event(SDL_Event *event) else if (event->type >= SDL_JOYAXISMOTION && event->type <= SDL_JOYBUTTONUP) { id = ((SDL_JoyButtonEvent *)event)->which; - device = bus_find_hid_device(sdl_busidW, ULongToPtr(id)); + device = find_device_from_id(id); if (device) set_report_from_event(device, event); else WARN("failed to find device with id %d\n", id); } else if (event->type >= SDL_CONTROLLERAXISMOTION && event->type <= SDL_CONTROLLERBUTTONUP) { id = ((SDL_ControllerButtonEvent *)event)->which; - device = bus_find_hid_device(sdl_busidW, ULongToPtr(id)); + device = find_device_from_id(id); if (device) set_mapped_report_from_event(device, event); else WARN("failed to find device with id %d\n", id); } + + LeaveCriticalSection(&sdl_cs); }
static void sdl_load_mappings(void) @@ -938,6 +961,9 @@ NTSTATUS sdl_bus_wait(void *args) struct bus_event *result = args; SDL_Event event;
+ /* cleanup previously returned event */ + bus_event_cleanup(result); + do { if (bus_event_queue_pop(&event_queue, result)) return STATUS_PENDING; diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c index 72e1f7ded5d..87526fc2129 100644 --- a/dlls/winebus.sys/main.c +++ b/dlls/winebus.sys/main.c @@ -393,6 +393,25 @@ DEVICE_OBJECT *bus_find_hid_device(const WCHAR *bus_id, void *platform_dev) return ret; }
+static DEVICE_OBJECT *bus_find_unix_device(struct unix_device *unix_device) +{ + struct device_extension *ext; + DEVICE_OBJECT *ret = NULL; + + EnterCriticalSection(&device_list_cs); + LIST_FOR_EACH_ENTRY(ext, &device_list, struct device_extension, entry) + { + if (ext->unix_device == unix_device) + { + ret = ext->device; + break; + } + } + LeaveCriticalSection(&device_list_cs); + + return ret; +} + static void bus_unlink_hid_device(DEVICE_OBJECT *device) { struct device_extension *ext = (struct device_extension *)device->DeviceExtension; @@ -635,6 +654,13 @@ static DWORD CALLBACK bus_main_thread(void *args) winebus_call(device_remove, event->device_created.device); } break; + case BUS_EVENT_TYPE_INPUT_REPORT: + EnterCriticalSection(&device_list_cs); + device = bus_find_unix_device(event->input_report.device); + if (!device) WARN("could not find device for %s bus device %p\n", debugstr_w(bus.name), event->input_report.device); + else process_hid_report(device, event->input_report.buffer, event->input_report.length); + LeaveCriticalSection(&device_list_cs); + break; } }
@@ -646,7 +672,7 @@ static DWORD CALLBACK bus_main_thread(void *args)
static NTSTATUS bus_main_thread_start(struct bus_main_params *bus) { - DWORD i = bus_count++; + DWORD i = bus_count++, max_size;
if (!(bus->init_done = CreateEventW(NULL, FALSE, FALSE, NULL))) { @@ -655,7 +681,8 @@ static NTSTATUS bus_main_thread_start(struct bus_main_params *bus) return STATUS_UNSUCCESSFUL; }
- if (!(bus->bus_event = HeapAlloc(GetProcessHeap(), 0, sizeof(struct bus_event)))) + max_size = offsetof(struct bus_event, input_report.buffer[0x10000]); + if (!(bus->bus_event = HeapAlloc(GetProcessHeap(), 0, max_size))) { ERR("failed to allocate %s bus event.\n", debugstr_w(bus->name)); CloseHandle(bus->init_done); diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h index 6234c2c7bbc..611873db17f 100644 --- a/dlls/winebus.sys/unix_private.h +++ b/dlls/winebus.sys/unix_private.h @@ -45,6 +45,7 @@ struct unix_device { const struct unix_device_vtbl *vtbl; struct list entry; + LONG ref; };
extern void *unix_device_create(const struct unix_device_vtbl *vtbl, SIZE_T size) DECLSPEC_HIDDEN; @@ -61,9 +62,12 @@ extern NTSTATUS iohid_bus_init(void *) DECLSPEC_HIDDEN; extern NTSTATUS iohid_bus_wait(void *) DECLSPEC_HIDDEN; extern NTSTATUS iohid_bus_stop(void *) DECLSPEC_HIDDEN;
+extern void bus_event_cleanup(struct bus_event *event) DECLSPEC_HIDDEN; extern void bus_event_queue_destroy(struct list *queue) DECLSPEC_HIDDEN; extern BOOL bus_event_queue_device_removed(struct list *queue, const WCHAR *bus_id, void *context) DECLSPEC_HIDDEN; extern BOOL bus_event_queue_device_created(struct list *queue, struct unix_device *device, struct device_desc *desc) DECLSPEC_HIDDEN; +extern BOOL bus_event_queue_input_report(struct list *queue, struct unix_device *device, + BYTE *report, USHORT length) DECLSPEC_HIDDEN; extern BOOL bus_event_queue_pop(struct list *queue, struct bus_event *event) DECLSPEC_HIDDEN;
struct hid_descriptor diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c index ade38c19854..b91de06e73c 100644 --- a/dlls/winebus.sys/unixlib.c +++ b/dlls/winebus.sys/unixlib.c @@ -248,16 +248,30 @@ void *unix_device_create(const struct unix_device_vtbl *vtbl, SIZE_T size)
if (!(iface = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size))) return NULL; iface->vtbl = vtbl; + iface->ref = 1;
return iface; }
+static void unix_device_decref(struct unix_device *iface) +{ + if (!InterlockedDecrement(&iface->ref)) + { + iface->vtbl->destroy(iface); + HeapFree(GetProcessHeap(), 0, iface); + } +} + +static ULONG unix_device_incref(struct unix_device *iface) +{ + return InterlockedIncrement(&iface->ref); +} + static NTSTATUS unix_device_remove(void *args) { struct unix_device *iface = args; iface->vtbl->stop(iface); - iface->vtbl->destroy(iface); - HeapFree(GetProcessHeap(), 0, iface); + unix_device_decref(iface); return STATUS_SUCCESS; }
@@ -328,12 +342,21 @@ const unixlib_entry_t __wine_unix_call_funcs[] = unix_device_set_feature_report, };
+void bus_event_cleanup(struct bus_event *event) +{ + if (event->type == BUS_EVENT_TYPE_INPUT_REPORT) + unix_device_decref(event->input_report.device); +} + void bus_event_queue_destroy(struct list *queue) { struct bus_event *event, *next;
LIST_FOR_EACH_ENTRY_SAFE(event, next, queue, struct bus_event, entry) + { + bus_event_cleanup(event); HeapFree(GetProcessHeap(), 0, event); + } }
BOOL bus_event_queue_device_removed(struct list *queue, const WCHAR *bus_id, void *context) @@ -364,17 +387,38 @@ BOOL bus_event_queue_device_created(struct list *queue, struct unix_device *devi return TRUE; }
+BOOL bus_event_queue_input_report(struct list *queue, struct unix_device *device, BYTE *report, USHORT length) +{ + ULONG size = offsetof(struct bus_event, input_report.buffer[length]); + struct bus_event *event = HeapAlloc(GetProcessHeap(), 0, size); + if (!event) return FALSE; + + if (unix_device_incref(device) == 1) return FALSE; /* being destroyed */ + + event->type = BUS_EVENT_TYPE_INPUT_REPORT; + event->input_report.device = device; + event->input_report.length = length; + memcpy(event->input_report.buffer, report, length); + list_add_tail(queue, &event->entry); + + return TRUE; +} + BOOL bus_event_queue_pop(struct list *queue, struct bus_event *event) { struct list *entry = list_head(queue); struct bus_event *tmp; + ULONG size;
if (!entry) return FALSE;
tmp = LIST_ENTRY(entry, struct bus_event, entry); list_remove(entry);
- memcpy(event, tmp, sizeof(*event)); + if (event->type != BUS_EVENT_TYPE_INPUT_REPORT) size = sizeof(*event); + else size = offsetof(struct bus_event, input_report.buffer[event->input_report.length]); + + memcpy(event, tmp, size); HeapFree(GetProcessHeap(), 0, tmp);
return TRUE; diff --git a/dlls/winebus.sys/unixlib.h b/dlls/winebus.sys/unixlib.h index 12df5d8bd5b..47256e80740 100644 --- a/dlls/winebus.sys/unixlib.h +++ b/dlls/winebus.sys/unixlib.h @@ -69,6 +69,7 @@ enum bus_event_type BUS_EVENT_TYPE_NONE, BUS_EVENT_TYPE_DEVICE_REMOVED, BUS_EVENT_TYPE_DEVICE_CREATED, + BUS_EVENT_TYPE_INPUT_REPORT, };
struct bus_event @@ -89,6 +90,13 @@ struct bus_event struct unix_device *device; struct device_desc desc; } device_created; + + struct + { + struct unix_device *device; + USHORT length; + BYTE buffer[1]; + } input_report; }; };