Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/hidclass.sys/device.c | 55 +++++++++++++------------------------- 1 file changed, 19 insertions(+), 36 deletions(-)
diff --git a/dlls/hidclass.sys/device.c b/dlls/hidclass.sys/device.c index c252125d8eb..d742ea841e5 100644 --- a/dlls/hidclass.sys/device.c +++ b/dlls/hidclass.sys/device.c @@ -375,40 +375,6 @@ void HID_StartDeviceThread(DEVICE_OBJECT *device) ext->u.pdo.thread = CreateThread(NULL, 0, hid_device_thread, device, 0, NULL); }
-static void handle_IOCTL_HID_GET_COLLECTION_INFORMATION( IRP *irp, BASE_DEVICE_EXTENSION *ext ) -{ - IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); - if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(HID_COLLECTION_INFORMATION)) - { - irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; - irp->IoStatus.Information = 0; - } - else - { - memcpy(irp->AssociatedIrp.SystemBuffer, &ext->u.pdo.information, sizeof(HID_COLLECTION_INFORMATION)); - irp->IoStatus.Information = sizeof(HID_COLLECTION_INFORMATION); - irp->IoStatus.Status = STATUS_SUCCESS; - } -} - -static void handle_IOCTL_HID_GET_COLLECTION_DESCRIPTOR( IRP *irp, BASE_DEVICE_EXTENSION *ext ) -{ - HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc; - IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); - - if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < desc->PreparsedDataLength) - { - irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE; - irp->IoStatus.Information = 0; - } - else - { - memcpy( irp->UserBuffer, desc->PreparsedData, desc->PreparsedDataLength ); - irp->IoStatus.Information = desc->PreparsedDataLength; - irp->IoStatus.Status = STATUS_SUCCESS; - } -} - struct device_strings { const WCHAR *id; @@ -619,12 +585,29 @@ NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp) } case IOCTL_HID_GET_COLLECTION_INFORMATION: { - handle_IOCTL_HID_GET_COLLECTION_INFORMATION( irp, ext ); + irp->IoStatus.Information = sizeof(HID_COLLECTION_INFORMATION); + if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(HID_COLLECTION_INFORMATION)) + irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + else + { + memcpy( irp->AssociatedIrp.SystemBuffer, &ext->u.pdo.information, + sizeof(HID_COLLECTION_INFORMATION) ); + irp->IoStatus.Status = STATUS_SUCCESS; + } break; } case IOCTL_HID_GET_COLLECTION_DESCRIPTOR: { - handle_IOCTL_HID_GET_COLLECTION_DESCRIPTOR( irp, ext ); + HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc; + + irp->IoStatus.Information = desc->PreparsedDataLength; + if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < desc->PreparsedDataLength) + irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE; + else + { + memcpy( irp->UserBuffer, desc->PreparsedData, desc->PreparsedDataLength ); + irp->IoStatus.Status = STATUS_SUCCESS; + } break; } case IOCTL_SET_NUM_DEVICE_INPUT_BUFFERS:
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/hidclass.sys/device.c | 62 +++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 35 deletions(-)
diff --git a/dlls/hidclass.sys/device.c b/dlls/hidclass.sys/device.c index d742ea841e5..78f9601ee0d 100644 --- a/dlls/hidclass.sys/device.c +++ b/dlls/hidclass.sys/device.c @@ -403,11 +403,13 @@ static const struct device_strings device_strings[] = { .id = L"VID_054C&PID_0CE6", .product = L"Wireless Controller" }, };
-static const WCHAR *find_product_string( const WCHAR *device_id ) +static const WCHAR *find_device_string( const WCHAR *device_id, ULONG index ) { const WCHAR *match_id = wcsrchr( device_id, '\' ) + 1; DWORD i;
+ if (index != HID_STRING_ID_IPRODUCT) return NULL; + for (i = 0; i < ARRAY_SIZE(device_strings); ++i) if (!wcsnicmp( device_strings[i].id, match_id, 17 )) return device_strings[i].product; @@ -415,30 +417,6 @@ static const WCHAR *find_product_string( const WCHAR *device_id ) return NULL; }
-static void handle_minidriver_string( BASE_DEVICE_EXTENSION *ext, IRP *irp, ULONG index ) -{ - IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); - WCHAR *output_buf = MmGetSystemAddressForMdlSafe( irp->MdlAddress, NormalPagePriority ); - ULONG output_len = stack->Parameters.DeviceIoControl.OutputBufferLength; - const WCHAR *str = NULL; - - if (index == HID_STRING_ID_IPRODUCT) str = find_product_string( ext->device_id ); - - if (!str) call_minidriver( IOCTL_HID_GET_STRING, ext->u.pdo.parent_fdo, ULongToPtr( index ), - sizeof(index), output_buf, output_len, &irp->IoStatus ); - else - { - irp->IoStatus.Information = (wcslen( str ) + 1) * sizeof(WCHAR); - if (irp->IoStatus.Information > output_len) - irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; - else - { - memcpy( output_buf, str, irp->IoStatus.Information ); - irp->IoStatus.Status = STATUS_SUCCESS; - } - } -} - static void hid_device_xfer_report( BASE_DEVICE_EXTENSION *ext, ULONG code, IRP *irp ) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); @@ -520,9 +498,10 @@ NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp) struct hid_queue *queue = irp->Tail.Overlay.OriginalFileObject->FsContext; IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); BASE_DEVICE_EXTENSION *ext = device->DeviceExtension; + ULONG code, index; + const WCHAR *str; NTSTATUS status; BOOL removed; - ULONG code; KIRQL irql;
irp->IoStatus.Information = 0; @@ -569,18 +548,31 @@ NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp) break; } case IOCTL_HID_GET_PRODUCT_STRING: - { - handle_minidriver_string( ext, irp, HID_STRING_ID_IPRODUCT ); - break; - } case IOCTL_HID_GET_SERIALNUMBER_STRING: - { - handle_minidriver_string( ext, irp, HID_STRING_ID_ISERIALNUMBER ); - break; - } case IOCTL_HID_GET_MANUFACTURER_STRING: { - handle_minidriver_string( ext, irp, HID_STRING_ID_IMANUFACTURER ); + WCHAR *output_buf = MmGetSystemAddressForMdlSafe( irp->MdlAddress, NormalPagePriority ); + ULONG output_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength; + + if (code == IOCTL_HID_GET_PRODUCT_STRING) index = HID_STRING_ID_IPRODUCT; + if (code == IOCTL_HID_GET_SERIALNUMBER_STRING) index = HID_STRING_ID_ISERIALNUMBER; + if (code == IOCTL_HID_GET_MANUFACTURER_STRING) index = HID_STRING_ID_IMANUFACTURER; + + if ((str = find_device_string( ext->device_id, index ))) + { + irp->IoStatus.Information = (wcslen( str ) + 1) * sizeof(WCHAR); + if (irp->IoStatus.Information > output_len) + irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + else + { + memcpy( output_buf, str, irp->IoStatus.Information ); + irp->IoStatus.Status = STATUS_SUCCESS; + } + break; + } + + call_minidriver( IOCTL_HID_GET_STRING, ext->u.pdo.parent_fdo, ULongToPtr( index ), + sizeof(index), output_buf, output_len, &irp->IoStatus ); break; } case IOCTL_HID_GET_COLLECTION_INFORMATION:
So we can handle pending asynchronous calls to minidriver more easily.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/hidclass.sys/device.c | 91 +++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 51 deletions(-)
diff --git a/dlls/hidclass.sys/device.c b/dlls/hidclass.sys/device.c index 78f9601ee0d..9d5f78cc27e 100644 --- a/dlls/hidclass.sys/device.c +++ b/dlls/hidclass.sys/device.c @@ -417,7 +417,7 @@ static const WCHAR *find_device_string( const WCHAR *device_id, ULONG index ) return NULL; }
-static void hid_device_xfer_report( BASE_DEVICE_EXTENSION *ext, ULONG code, IRP *irp ) +static NTSTATUS hid_device_xfer_report( BASE_DEVICE_EXTENSION *ext, ULONG code, IRP *irp ) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); ULONG offset = 0, report_len = 0, buffer_len = 0; @@ -442,11 +442,7 @@ static void hid_device_xfer_report( BASE_DEVICE_EXTENSION *ext, ULONG code, IRP buffer = irp->AssociatedIrp.SystemBuffer; break; } - if (!buffer || !buffer_len) - { - irp->IoStatus.Status = STATUS_INVALID_USER_BUFFER; - return; - } + if (!buffer || !buffer_len) return STATUS_INVALID_USER_BUFFER;
switch (code) { @@ -465,12 +461,7 @@ static void hid_device_xfer_report( BASE_DEVICE_EXTENSION *ext, ULONG code, IRP if (report) report_len = report->FeatureLength; break; } - - if (!report || buffer_len < report_len) - { - irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - return; - } + if (!report || buffer_len < report_len) return STATUS_INVALID_PARAMETER;
if (!report->ReportID) offset = 1; packet.reportId = report->ReportID; @@ -491,6 +482,8 @@ static void hid_device_xfer_report( BASE_DEVICE_EXTENSION *ext, ULONG code, IRP if (code == IOCTL_HID_WRITE_REPORT && packet.reportId) irp->IoStatus.Information--; break; } + + return irp->IoStatus.Status; }
NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp) @@ -498,9 +491,9 @@ NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp) struct hid_queue *queue = irp->Tail.Overlay.OriginalFileObject->FsContext; IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); BASE_DEVICE_EXTENSION *ext = device->DeviceExtension; + NTSTATUS status = irp->IoStatus.Status; ULONG code, index; const WCHAR *str; - NTSTATUS status; BOOL removed; KIRQL irql;
@@ -522,29 +515,26 @@ NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp) switch ((code = irpsp->Parameters.DeviceIoControl.IoControlCode)) { case IOCTL_HID_GET_POLL_FREQUENCY_MSEC: - TRACE("IOCTL_HID_GET_POLL_FREQUENCY_MSEC\n"); if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG)) + status = STATUS_BUFFER_OVERFLOW; + else { - irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; - irp->IoStatus.Information = 0; - break; + *(ULONG *)irp->AssociatedIrp.SystemBuffer = ext->u.pdo.poll_interval; + irp->IoStatus.Information = sizeof(ULONG); + status = STATUS_SUCCESS; } - *(ULONG *)irp->AssociatedIrp.SystemBuffer = ext->u.pdo.poll_interval; - irp->IoStatus.Information = sizeof(ULONG); - irp->IoStatus.Status = STATUS_SUCCESS; break; case IOCTL_HID_SET_POLL_FREQUENCY_MSEC: { ULONG poll_interval; - TRACE("IOCTL_HID_SET_POLL_FREQUENCY_MSEC\n"); if (irpsp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG)) + status = STATUS_BUFFER_TOO_SMALL; + else { - irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; - break; + poll_interval = *(ULONG *)irp->AssociatedIrp.SystemBuffer; + if (poll_interval) ext->u.pdo.poll_interval = min( poll_interval, MAX_POLL_INTERVAL_MSEC ); + status = STATUS_SUCCESS; } - poll_interval = *(ULONG *)irp->AssociatedIrp.SystemBuffer; - if (poll_interval) ext->u.pdo.poll_interval = min(poll_interval, MAX_POLL_INTERVAL_MSEC); - irp->IoStatus.Status = STATUS_SUCCESS; break; } case IOCTL_HID_GET_PRODUCT_STRING: @@ -562,29 +552,30 @@ NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp) { irp->IoStatus.Information = (wcslen( str ) + 1) * sizeof(WCHAR); if (irp->IoStatus.Information > output_len) - irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + status = STATUS_BUFFER_TOO_SMALL; else { memcpy( output_buf, str, irp->IoStatus.Information ); - irp->IoStatus.Status = STATUS_SUCCESS; + status = STATUS_SUCCESS; } break; }
call_minidriver( IOCTL_HID_GET_STRING, ext->u.pdo.parent_fdo, ULongToPtr( index ), sizeof(index), output_buf, output_len, &irp->IoStatus ); + status = irp->IoStatus.Status; break; } case IOCTL_HID_GET_COLLECTION_INFORMATION: { irp->IoStatus.Information = sizeof(HID_COLLECTION_INFORMATION); if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(HID_COLLECTION_INFORMATION)) - irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + status = STATUS_BUFFER_OVERFLOW; else { memcpy( irp->AssociatedIrp.SystemBuffer, &ext->u.pdo.information, sizeof(HID_COLLECTION_INFORMATION) ); - irp->IoStatus.Status = STATUS_SUCCESS; + status = STATUS_SUCCESS; } break; } @@ -594,36 +585,31 @@ NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp)
irp->IoStatus.Information = desc->PreparsedDataLength; if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < desc->PreparsedDataLength) - irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE; + status = STATUS_INVALID_BUFFER_SIZE; else { memcpy( irp->UserBuffer, desc->PreparsedData, desc->PreparsedDataLength ); - irp->IoStatus.Status = STATUS_SUCCESS; + status = STATUS_SUCCESS; } break; } case IOCTL_SET_NUM_DEVICE_INPUT_BUFFERS: { - irp->IoStatus.Information = 0; - if (irpsp->Parameters.DeviceIoControl.InputBufferLength != sizeof(ULONG)) - irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + status = STATUS_BUFFER_OVERFLOW; else - irp->IoStatus.Status = hid_queue_resize( queue, *(ULONG *)irp->AssociatedIrp.SystemBuffer ); + status = hid_queue_resize( queue, *(ULONG *)irp->AssociatedIrp.SystemBuffer ); break; } case IOCTL_GET_NUM_DEVICE_INPUT_BUFFERS: { if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG)) - { - irp->IoStatus.Information = 0; - irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; - } + status = STATUS_BUFFER_TOO_SMALL; else { *(ULONG *)irp->AssociatedIrp.SystemBuffer = queue->length; irp->IoStatus.Information = sizeof(ULONG); - irp->IoStatus.Status = STATUS_SUCCESS; + status = STATUS_SUCCESS; } break; } @@ -631,20 +617,23 @@ NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp) case IOCTL_HID_SET_FEATURE: case IOCTL_HID_GET_INPUT_REPORT: case IOCTL_HID_SET_OUTPUT_REPORT: - hid_device_xfer_report( ext, code, irp ); + status = hid_device_xfer_report( ext, code, irp ); break; default: { ULONG code = irpsp->Parameters.DeviceIoControl.IoControlCode; FIXME( "Unsupported ioctl %#lx (device=%lx access=%lx func=%lx method=%lx)\n", code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3 ); - irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + status = STATUS_NOT_SUPPORTED; break; } }
- status = irp->IoStatus.Status; - if (status != STATUS_PENDING) IoCompleteRequest( irp, IO_NO_INCREMENT ); + if (status != STATUS_PENDING) + { + irp->IoStatus.Status = status; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + } return status; }
@@ -695,12 +684,12 @@ NTSTATUS WINAPI pdo_read(DEVICE_OBJECT *device, IRP *irp) NTSTATUS WINAPI pdo_write(DEVICE_OBJECT *device, IRP *irp) { BASE_DEVICE_EXTENSION *ext = device->DeviceExtension; - NTSTATUS status; - - hid_device_xfer_report( ext, IOCTL_HID_WRITE_REPORT, irp ); - - status = irp->IoStatus.Status; - IoCompleteRequest( irp, IO_NO_INCREMENT ); + NTSTATUS status = hid_device_xfer_report( ext, IOCTL_HID_WRITE_REPORT, irp ); + if (status != STATUS_PENDING) + { + irp->IoStatus.Status = status; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + } return status; }
Instead of calling it synchronously. Use a completion routine to wait for their completion before returning.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/hidclass.sys/device.c | 60 ++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 12 deletions(-)
diff --git a/dlls/hidclass.sys/device.c b/dlls/hidclass.sys/device.c index 9d5f78cc27e..9d917bbc674 100644 --- a/dlls/hidclass.sys/device.c +++ b/dlls/hidclass.sys/device.c @@ -417,12 +417,34 @@ static const WCHAR *find_device_string( const WCHAR *device_id, ULONG index ) return NULL; }
+struct completion_params +{ + HID_XFER_PACKET packet; + ULONG padding; + IRP *irp; +}; + +static NTSTATUS CALLBACK xfer_completion( DEVICE_OBJECT *device, IRP *irp, void *context ) +{ + struct completion_params *params = context; + IRP *orig_irp = params->irp; + + TRACE( "device %p, irp %p, context %p\n", device, irp, context ); + + orig_irp->IoStatus = irp->IoStatus; + orig_irp->IoStatus.Information -= params->padding; + IoCompleteRequest( orig_irp, IO_NO_INCREMENT ); + + free( params ); + return STATUS_SUCCESS; +} + static NTSTATUS hid_device_xfer_report( BASE_DEVICE_EXTENSION *ext, ULONG code, IRP *irp ) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); - ULONG offset = 0, report_len = 0, buffer_len = 0; + ULONG offset, report_len = 0, buffer_len = 0; + struct completion_params *params; HIDP_REPORT_IDS *report = NULL; - HID_XFER_PACKET packet; BYTE *buffer = NULL;
switch (code) @@ -462,28 +484,42 @@ static NTSTATUS hid_device_xfer_report( BASE_DEVICE_EXTENSION *ext, ULONG code, break; } if (!report || buffer_len < report_len) return STATUS_INVALID_PARAMETER; + offset = report->ReportID ? 0 : 1;
- if (!report->ReportID) offset = 1; - packet.reportId = report->ReportID; - packet.reportBuffer = buffer + offset; + if (!(params = calloc( 1, sizeof(struct completion_params) ))) return STATUS_NO_MEMORY; + params->packet.reportId = report->ReportID; + params->packet.reportBuffer = buffer + offset; + params->irp = irp;
switch (code) { case IOCTL_HID_GET_FEATURE: case IOCTL_HID_GET_INPUT_REPORT: - packet.reportBufferLen = buffer_len - offset; - call_minidriver( code, ext->u.pdo.parent_fdo, NULL, 0, &packet, sizeof(packet), &irp->IoStatus ); + params->packet.reportBufferLen = buffer_len - offset; + irp = IoBuildDeviceIoControlRequest( code, ext->u.pdo.parent_fdo, NULL, 0, ¶ms->packet, + sizeof(params->packet), TRUE, NULL, NULL ); break; + case IOCTL_HID_WRITE_REPORT: + params->padding = 1 - offset; + /* fallthrough */ case IOCTL_HID_SET_FEATURE: case IOCTL_HID_SET_OUTPUT_REPORT: - case IOCTL_HID_WRITE_REPORT: - packet.reportBufferLen = report_len - offset; - call_minidriver( code, ext->u.pdo.parent_fdo, NULL, sizeof(packet), &packet, 0, &irp->IoStatus ); - if (code == IOCTL_HID_WRITE_REPORT && packet.reportId) irp->IoStatus.Information--; + params->packet.reportBufferLen = report_len - offset; + irp = IoBuildDeviceIoControlRequest( code, ext->u.pdo.parent_fdo, NULL, sizeof(params->packet), + ¶ms->packet, 0, TRUE, NULL, NULL ); break; }
- return irp->IoStatus.Status; + if (!irp) + { + free( params ); + return STATUS_NO_MEMORY; + } + + IoMarkIrpPending( params->irp ); + IoSetCompletionRoutine( irp, xfer_completion, params, TRUE, TRUE, TRUE ); + IoCallDriver( ext->u.pdo.parent_fdo, irp ); + return STATUS_PENDING; }
NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp)
Adapting it for the IAsyncOperation_boolean interface and use it to implement IForceFeedbackMotor_Try(Enable|Disable|Reset)Async.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/tests/force_feedback.c | 32 +- dlls/windows.gaming.input/Makefile.in | 1 + dlls/windows.gaming.input/async.c | 369 +++++++++++++++++++++ dlls/windows.gaming.input/force_feedback.c | 57 +++- dlls/windows.gaming.input/private.h | 4 + 5 files changed, 431 insertions(+), 32 deletions(-) create mode 100644 dlls/windows.gaming.input/async.c
diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index 6d8701a8c24..d9dad38446c 100644 --- a/dlls/dinput/tests/force_feedback.c +++ b/dlls/dinput/tests/force_feedback.c @@ -4482,7 +4482,7 @@ static void check_bool_async_( int line, IAsyncOperation_boolean *async, UINT32 hr = IAsyncInfo_get_ErrorCode( async_info, &async_hr ); if (expect_status < 4) ok_(__FILE__, line)( hr == S_OK, "get_ErrorCode returned %#lx\n", hr ); else ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "get_ErrorCode returned %#lx\n", hr ); - if (expect_status < 4) ok_(__FILE__, line)( async_hr == expect_hr, "got error %#lx\n", async_hr ); + if (expect_status < 4) todo_wine_if(FAILED(expect_hr)) ok_(__FILE__, line)( async_hr == expect_hr, "got error %#lx\n", async_hr ); else ok_(__FILE__, line)( async_hr == E_ILLEGAL_METHOD_CALL, "got error %#lx\n", async_hr );
IAsyncInfo_Release( async_info ); @@ -4493,6 +4493,7 @@ static void check_bool_async_( int line, IAsyncOperation_boolean *async, UINT32 { case Completed: case Error: + todo_wine_if(FAILED(expect_hr)) ok_(__FILE__, line)( hr == expect_hr, "GetResults returned %#lx\n", hr ); ok_(__FILE__, line)( result == expect_result, "got result %u\n", result ); break; @@ -5091,7 +5092,6 @@ static void test_windows_gaming_input(void) .report_id = 1, .report_len = 2, .report_buf = {1, 0x05}, - .todo = TRUE, }; static struct hid_expect expect_enable = { @@ -5099,7 +5099,6 @@ static void test_windows_gaming_input(void) .report_id = 1, .report_len = 2, .report_buf = {1, 0x04}, - .todo = TRUE, }; static struct hid_expect expect_enable_fail = { @@ -5108,7 +5107,6 @@ static void test_windows_gaming_input(void) .report_id = 1, .report_len = 2, .report_buf = {1, 0x04}, - .todo = TRUE, }; static struct hid_expect expect_reset_delay[] = { @@ -5119,7 +5117,6 @@ static void test_windows_gaming_input(void) .report_id = 1, .report_len = 2, .report_buf = {1, 0x01}, - .todo = TRUE, }, /* device gain */ { @@ -5127,7 +5124,6 @@ static void test_windows_gaming_input(void) .report_id = 6, .report_len = 2, .report_buf = {6, 0x7f}, - .todo = TRUE, }, }; struct hid_expect expect_reset[] = @@ -5138,7 +5134,6 @@ static void test_windows_gaming_input(void) .report_id = 1, .report_len = 2, .report_buf = {1, 0x01}, - .todo = TRUE, }, /* device gain */ { @@ -5146,7 +5141,6 @@ static void test_windows_gaming_input(void) .report_id = 6, .report_len = 2, .report_buf = {6, 0x7f}, - .todo = TRUE, }, }; static const WCHAR *force_feedback_motor = RuntimeClass_Windows_Gaming_Input_ForceFeedback_ForceFeedbackMotor; @@ -5309,9 +5303,7 @@ static void test_windows_gaming_input(void)
enabled = FALSE; hr = IForceFeedbackMotor_get_IsEnabled( motor, &enabled ); - todo_wine ok( hr == S_OK, "get_IsEnabled returned %#lx\n", hr ); - todo_wine ok( enabled == TRUE, "got enabled %u\n", enabled );
supported_axes = 0xdeadbeef; @@ -5335,10 +5327,8 @@ static void test_windows_gaming_input(void)
set_hid_expect( file, &expect_disable, sizeof(expect_disable) ); hr = IForceFeedbackMotor_TryDisableAsync( motor, &bool_async ); - todo_wine ok( hr == S_OK, "TryDisableAsync returned %#lx\n", hr ); - wait_hid_expect_( __FILE__, __LINE__, file, 100, TRUE ); - if (hr != S_OK) goto skip_tests; + wait_hid_expect( file, 100 ); check_bool_async( bool_async, 1, Completed, S_OK, TRUE );
check_interface( bool_async, &IID_IUnknown, TRUE ); @@ -5382,9 +5372,8 @@ static void test_windows_gaming_input(void)
set_hid_expect( file, &expect_enable_fail, sizeof(expect_enable_fail) ); hr = IForceFeedbackMotor_TryEnableAsync( motor, &bool_async ); - todo_wine ok( hr == S_OK, "TryEnableAsync returned %#lx\n", hr ); - wait_hid_expect_( __FILE__, __LINE__, file, 100, TRUE ); + wait_hid_expect( file, 100 ); check_bool_async( bool_async, 1, Error, 0x8685400d, FALSE );
bool_async_handler = default_bool_async_handler; @@ -5411,7 +5400,6 @@ static void test_windows_gaming_input(void)
set_hid_expect( file, expect_reset_delay, sizeof(expect_reset_delay) ); hr = IForceFeedbackMotor_TryResetAsync( motor, &bool_async ); - todo_wine ok( hr == S_OK, "TryResetAsync returned %#lx\n", hr ); check_bool_async( bool_async, 1, Started, S_OK, FALSE );
@@ -5435,7 +5423,7 @@ static void test_windows_gaming_input(void) ok( !bool_async_handler.invoked, "handler invoked\n" ); IAsyncInfo_Release( async_info );
- wait_hid_expect_( __FILE__, __LINE__, file, 100, TRUE ); + wait_hid_expect( file, 100 ); ret = WaitForSingleObject( bool_async_handler.event, 100 ); ok( ret == 0, "WaitForSingleObject returned %#lx\n", ret ); CloseHandle( bool_async_handler.event ); @@ -5455,7 +5443,6 @@ static void test_windows_gaming_input(void)
set_hid_expect( file, expect_reset_delay, sizeof(expect_reset_delay) ); hr = IForceFeedbackMotor_TryResetAsync( motor, &bool_async ); - todo_wine ok( hr == S_OK, "TryResetAsync returned %#lx\n", hr ); check_bool_async( bool_async, 1, Started, S_OK, FALSE );
@@ -5481,7 +5468,7 @@ static void test_windows_gaming_input(void) ok( !bool_async_handler.invoked, "handler invoked\n" ); IAsyncInfo_Release( async_info );
- wait_hid_expect_( __FILE__, __LINE__, file, 100, TRUE ); + wait_hid_expect( file, 100 ); ret = WaitForSingleObject( bool_async_handler.event, 100 ); ok( ret == 0, "WaitForSingleObject returned %#lx\n", ret ); CloseHandle( bool_async_handler.event ); @@ -5498,21 +5485,18 @@ static void test_windows_gaming_input(void)
set_hid_expect( file, &expect_enable, sizeof(expect_enable) ); hr = IForceFeedbackMotor_TryEnableAsync( motor, &bool_async ); - todo_wine ok( hr == S_OK, "TryEnableAsync returned %#lx\n", hr ); - wait_hid_expect_( __FILE__, __LINE__, file, 100, TRUE ); + wait_hid_expect( file, 100 ); IAsyncOperation_boolean_Release( bool_async );
set_hid_expect( file, expect_reset, sizeof(expect_reset) ); hr = IForceFeedbackMotor_TryResetAsync( motor, &bool_async ); - todo_wine ok( hr == S_OK, "TryResetAsync returned %#lx\n", hr ); - wait_hid_expect_( __FILE__, __LINE__, file, 100, TRUE ); + wait_hid_expect( file, 100 ); IAsyncOperation_boolean_Release( bool_async );
-skip_tests: IForceFeedbackMotor_Release( motor );
IRawGameController_Release( raw_controller ); diff --git a/dlls/windows.gaming.input/Makefile.in b/dlls/windows.gaming.input/Makefile.in index b9a56992142..463f6b97f74 100644 --- a/dlls/windows.gaming.input/Makefile.in +++ b/dlls/windows.gaming.input/Makefile.in @@ -2,6 +2,7 @@ MODULE = windows.gaming.input.dll IMPORTS = combase uuid user32 dinput8 setupapi hid
C_SRCS = \ + async.c \ controller.c \ event_handlers.c \ force_feedback.c \ diff --git a/dlls/windows.gaming.input/async.c b/dlls/windows.gaming.input/async.c new file mode 100644 index 00000000000..91ecdeb9550 --- /dev/null +++ b/dlls/windows.gaming.input/async.c @@ -0,0 +1,369 @@ +/* WinRT Windows.Gaming.Input implementation + * + * Copyright 2022 Bernhard Kölbl for CodeWeavers + * Copyright 2022 Rémi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "private.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(input); + +#define Closed 4 +#define HANDLER_NOT_SET ((void *)~(ULONG_PTR)0) + +struct async_bool +{ + IAsyncOperation_boolean IAsyncOperation_boolean_iface; + IAsyncInfo IAsyncInfo_iface; + LONG ref; + + IAsyncOperationCompletedHandler_boolean *handler; + BOOLEAN result; + + async_operation_boolean_callback callback; + TP_WORK *async_run_work; + IInspectable *invoker; + + CRITICAL_SECTION cs; + AsyncStatus status; + HRESULT hr; +}; + +DEFINE_IINSPECTABLE( async_info, IAsyncInfo, struct async_bool, IAsyncOperation_boolean_iface ) + +static HRESULT WINAPI async_info_get_Id( IAsyncInfo *iface, UINT32 *id ) +{ + struct async_bool *impl = impl_from_IAsyncInfo( iface ); + HRESULT hr = S_OK; + + TRACE( "iface %p, id %p.\n", iface, id ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Closed) hr = E_ILLEGAL_METHOD_CALL; + *id = 1; + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_info_get_Status( IAsyncInfo *iface, AsyncStatus *status ) +{ + struct async_bool *impl = impl_from_IAsyncInfo( iface ); + HRESULT hr = S_OK; + + TRACE( "iface %p, status %p.\n", iface, status ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Closed) hr = E_ILLEGAL_METHOD_CALL; + *status = impl->status; + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_info_get_ErrorCode( IAsyncInfo *iface, HRESULT *error_code ) +{ + struct async_bool *impl = impl_from_IAsyncInfo( iface ); + HRESULT hr = S_OK; + + TRACE( "iface %p, error_code %p.\n", iface, error_code ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Closed) *error_code = hr = E_ILLEGAL_METHOD_CALL; + else *error_code = impl->hr; + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_info_Cancel( IAsyncInfo *iface ) +{ + struct async_bool *impl = impl_from_IAsyncInfo( iface ); + HRESULT hr = S_OK; + + TRACE( "iface %p.\n", iface ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Closed) hr = E_ILLEGAL_METHOD_CALL; + else if (impl->status == Started) impl->status = Canceled; + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_info_Close( IAsyncInfo *iface ) +{ + struct async_bool *impl = impl_from_IAsyncInfo( iface ); + HRESULT hr = S_OK; + + TRACE( "iface %p.\n", iface ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Started) + hr = E_ILLEGAL_STATE_CHANGE; + else if (impl->status != Closed) + { + CloseThreadpoolWork( impl->async_run_work ); + impl->async_run_work = NULL; + impl->status = Closed; + } + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static const struct IAsyncInfoVtbl async_info_vtbl = +{ + /* IUnknown methods */ + async_info_QueryInterface, + async_info_AddRef, + async_info_Release, + /* IInspectable methods */ + async_info_GetIids, + async_info_GetRuntimeClassName, + async_info_GetTrustLevel, + /* IAsyncInfo */ + async_info_get_Id, + async_info_get_Status, + async_info_get_ErrorCode, + async_info_Cancel, + async_info_Close, +}; + +static inline struct async_bool *impl_from_IAsyncOperation_boolean( IAsyncOperation_boolean *iface ) +{ + return CONTAINING_RECORD( iface, struct async_bool, IAsyncOperation_boolean_iface ); +} + +static HRESULT WINAPI async_bool_QueryInterface( IAsyncOperation_boolean *iface, REFIID iid, void **out ) +{ + struct async_bool *impl = impl_from_IAsyncOperation_boolean( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IAsyncOperation_boolean )) + { + IInspectable_AddRef( (*out = &impl->IAsyncOperation_boolean_iface) ); + return S_OK; + } + + if (IsEqualGUID( iid, &IID_IAsyncInfo )) + { + IInspectable_AddRef( (*out = &impl->IAsyncInfo_iface) ); + return S_OK; + } + + WARN( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI async_bool_AddRef( IAsyncOperation_boolean *iface ) +{ + struct async_bool *impl = impl_from_IAsyncOperation_boolean( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI async_bool_Release( IAsyncOperation_boolean *iface ) +{ + struct async_bool *impl = impl_from_IAsyncOperation_boolean( iface ); + + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + + if (!ref) + { + IAsyncInfo_Close( &impl->IAsyncInfo_iface ); + if (impl->handler && impl->handler != HANDLER_NOT_SET) IAsyncOperationCompletedHandler_boolean_Release( impl->handler ); + IInspectable_Release( impl->invoker ); + DeleteCriticalSection( &impl->cs ); + free( impl ); + } + + return ref; +} + +static HRESULT WINAPI async_bool_GetIids( IAsyncOperation_boolean *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI async_bool_GetRuntimeClassName( IAsyncOperation_boolean *iface, HSTRING *class_name ) +{ + return WindowsCreateString( L"Windows.Foundation.IAsyncOperation`1<Boolean>", + ARRAY_SIZE(L"Windows.Foundation.IAsyncOperation`1<Boolean>"), + class_name ); +} + +static HRESULT WINAPI async_bool_GetTrustLevel( IAsyncOperation_boolean *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI async_bool_put_Completed( IAsyncOperation_boolean *iface, IAsyncOperationCompletedHandler_boolean *handler ) +{ + struct async_bool *impl = impl_from_IAsyncOperation_boolean( iface ); + HRESULT hr = S_OK; + + TRACE( "iface %p, handler %p.\n", iface, handler ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Closed) hr = E_ILLEGAL_METHOD_CALL; + else if (impl->handler != HANDLER_NOT_SET) hr = E_ILLEGAL_DELEGATE_ASSIGNMENT; + else if ((impl->handler = handler)) + { + IAsyncOperationCompletedHandler_boolean_AddRef( impl->handler ); + + if (impl->status > Started) + { + IAsyncOperation_boolean *operation = &impl->IAsyncOperation_boolean_iface; + AsyncStatus status = impl->status; + impl->handler = NULL; /* Prevent concurrent invoke. */ + LeaveCriticalSection( &impl->cs ); + + IAsyncOperationCompletedHandler_boolean_Invoke( handler, operation, status ); + IAsyncOperationCompletedHandler_boolean_Release( handler ); + + return S_OK; + } + } + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_bool_get_Completed( IAsyncOperation_boolean *iface, IAsyncOperationCompletedHandler_boolean **handler ) +{ + struct async_bool *impl = impl_from_IAsyncOperation_boolean( iface ); + HRESULT hr = S_OK; + + FIXME( "iface %p, handler %p semi stub!\n", iface, handler ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Closed) hr = E_ILLEGAL_METHOD_CALL; + *handler = (impl->handler != HANDLER_NOT_SET) ? impl->handler : NULL; + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_bool_GetResults( IAsyncOperation_boolean *iface, BOOLEAN *results ) +{ + struct async_bool *impl = impl_from_IAsyncOperation_boolean( iface ); + HRESULT hr; + + TRACE( "iface %p, results %p.\n", iface, results ); + + EnterCriticalSection( &impl->cs ); + if (impl->status != Completed && impl->status != Error) hr = E_ILLEGAL_METHOD_CALL; + else + { + *results = impl->result; + hr = impl->hr; + } + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static const struct IAsyncOperation_booleanVtbl async_bool_vtbl = +{ + /* IUnknown methods */ + async_bool_QueryInterface, + async_bool_AddRef, + async_bool_Release, + /* IInspectable methods */ + async_bool_GetIids, + async_bool_GetRuntimeClassName, + async_bool_GetTrustLevel, + /* IAsyncOperation<boolean> */ + async_bool_put_Completed, + async_bool_get_Completed, + async_bool_GetResults, +}; + +static void CALLBACK async_run_cb( TP_CALLBACK_INSTANCE *instance, void *data, TP_WORK *work ) +{ + IAsyncOperation_boolean *operation = data; + struct async_bool *impl = impl_from_IAsyncOperation_boolean( operation ); + BOOLEAN result = FALSE; + HRESULT hr; + + hr = impl->callback( impl->invoker, &result ); + + EnterCriticalSection( &impl->cs ); + if (impl->status != Closed) impl->status = FAILED(hr) ? Error : Completed; + impl->result = result; + impl->hr = hr; + + if (impl->handler != NULL && impl->handler != HANDLER_NOT_SET) + { + IAsyncOperationCompletedHandler_boolean *handler = impl->handler; + AsyncStatus status = impl->status; + impl->handler = NULL; /* Prevent concurrent invoke. */ + LeaveCriticalSection( &impl->cs ); + + IAsyncOperationCompletedHandler_boolean_Invoke( handler, operation, status ); + IAsyncOperationCompletedHandler_boolean_Release( handler ); + } + else LeaveCriticalSection( &impl->cs ); + + IAsyncOperation_boolean_Release( operation ); +} + +HRESULT async_operation_boolean_create( IInspectable *invoker, async_operation_boolean_callback callback, + IAsyncOperation_boolean **out ) +{ + struct async_bool *impl; + + *out = NULL; + if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY; + impl->IAsyncOperation_boolean_iface.lpVtbl = &async_bool_vtbl; + impl->IAsyncInfo_iface.lpVtbl = &async_info_vtbl; + impl->ref = 1; + + impl->handler = HANDLER_NOT_SET; + impl->callback = callback; + impl->status = Started; + + if (!(impl->async_run_work = CreateThreadpoolWork( async_run_cb, &impl->IAsyncOperation_boolean_iface, NULL ))) + { + free( impl ); + return HRESULT_FROM_WIN32( GetLastError() ); + } + + IInspectable_AddRef( (impl->invoker = invoker) ); + InitializeCriticalSection( &impl->cs ); + impl->cs.DebugInfo->Spare[0] = (DWORD_PTR)( __FILE__ ": async_operation.cs" ); + + /* keep the async alive in the callback */ + IAsyncOperation_boolean_AddRef( &impl->IAsyncOperation_boolean_iface ); + SubmitThreadpoolWork( impl->async_run_work ); + + *out = &impl->IAsyncOperation_boolean_iface; + TRACE( "created IAsyncOperation_boolean %p\n", *out ); + return S_OK; +} diff --git a/dlls/windows.gaming.input/force_feedback.c b/dlls/windows.gaming.input/force_feedback.c index fad5ade402c..a9a875c48e3 100644 --- a/dlls/windows.gaming.input/force_feedback.c +++ b/dlls/windows.gaming.input/force_feedback.c @@ -161,8 +161,16 @@ static HRESULT WINAPI motor_put_MasterGain( IForceFeedbackMotor *iface, double v
static HRESULT WINAPI motor_get_IsEnabled( IForceFeedbackMotor *iface, BOOLEAN *value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + struct motor *impl = impl_from_IForceFeedbackMotor( iface ); + DWORD state; + HRESULT hr; + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (FAILED(hr = IDirectInputDevice8_GetForceFeedbackState( impl->device, &state ))) *value = FALSE; + else *value = !(state & DIGFFS_ACTUATORSOFF); + + return hr; }
static HRESULT WINAPI motor_get_SupportedAxes( IForceFeedbackMotor *iface, enum ForceFeedbackEffectAxes *value ) @@ -205,22 +213,55 @@ static HRESULT WINAPI motor_StopAllEffects( IForceFeedbackMotor *iface ) return IDirectInputDevice8_SendForceFeedbackCommand( impl->device, DISFFC_STOPALL ); }
+static HRESULT WINAPI motor_try_disable_async( IInspectable *iface, BOOLEAN *result ) +{ + struct motor *impl = impl_from_IForceFeedbackMotor( (IForceFeedbackMotor *)iface ); + HRESULT hr; + + hr = IDirectInputDevice8_SendForceFeedbackCommand( impl->device, DISFFC_SETACTUATORSOFF ); + *result = SUCCEEDED(hr); + + return hr; +} + static HRESULT WINAPI motor_TryDisableAsync( IForceFeedbackMotor *iface, IAsyncOperation_boolean **async_op ) { - FIXME( "iface %p, async_op %p stub!\n", iface, async_op ); - return E_NOTIMPL; + TRACE( "iface %p, async_op %p.\n", iface, async_op ); + return async_operation_boolean_create( (IInspectable *)iface, motor_try_disable_async, async_op ); +} + +static HRESULT WINAPI motor_try_enable_async( IInspectable *iface, BOOLEAN *result ) +{ + struct motor *impl = impl_from_IForceFeedbackMotor( (IForceFeedbackMotor *)iface ); + HRESULT hr; + + hr = IDirectInputDevice8_SendForceFeedbackCommand( impl->device, DISFFC_SETACTUATORSON ); + *result = SUCCEEDED(hr); + + return hr; }
static HRESULT WINAPI motor_TryEnableAsync( IForceFeedbackMotor *iface, IAsyncOperation_boolean **async_op ) { - FIXME( "iface %p, async_op %p stub!\n", iface, async_op ); - return E_NOTIMPL; + TRACE( "iface %p, async_op %p.\n", iface, async_op ); + return async_operation_boolean_create( (IInspectable *)iface, motor_try_enable_async, async_op ); +} + +static HRESULT WINAPI motor_try_reset_async( IInspectable *iface, BOOLEAN *result ) +{ + struct motor *impl = impl_from_IForceFeedbackMotor( (IForceFeedbackMotor *)iface ); + HRESULT hr; + + hr = IDirectInputDevice8_SendForceFeedbackCommand( impl->device, DISFFC_RESET ); + *result = SUCCEEDED(hr); + + return hr; }
static HRESULT WINAPI motor_TryResetAsync( IForceFeedbackMotor *iface, IAsyncOperation_boolean **async_op ) { - FIXME( "iface %p, async_op %p stub!\n", iface, async_op ); - return E_NOTIMPL; + TRACE( "iface %p, async_op %p.\n", iface, async_op ); + return async_operation_boolean_create( (IInspectable *)iface, motor_try_reset_async, async_op ); }
static HRESULT WINAPI motor_TryUnloadEffectAsync( IForceFeedbackMotor *iface, IForceFeedbackEffect *effect, diff --git a/dlls/windows.gaming.input/private.h b/dlls/windows.gaming.input/private.h index 562a84d49ed..2c25aca9027 100644 --- a/dlls/windows.gaming.input/private.h +++ b/dlls/windows.gaming.input/private.h @@ -67,6 +67,10 @@ extern void event_handlers_notify( struct list *list, IInspectable *element );
extern HRESULT force_feedback_motor_create( IDirectInputDevice8W *device, IForceFeedbackMotor **out );
+typedef HRESULT (WINAPI *async_operation_boolean_callback)( IInspectable *invoker, BOOLEAN *result ); +extern HRESULT async_operation_boolean_create( IInspectable *invoker, async_operation_boolean_callback callback, + IAsyncOperation_boolean **out ); + #define DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from, iface_mem, expr ) \ static inline impl_type *impl_from( iface_type *iface ) \ { \