-- v2: win32u: Move NtUserGetRawInputBuffer from user32. win32u: Move NtUserGetRawInputData from user32. user32: Correctly fill the RAWINPUT structure on WoW64. user32/tests: Add some more tests related to the RAWINPUT structure.
From: Zebediah Figura zfigura@codeweavers.com
--- dlls/user32/tests/input.c | 96 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 5 deletions(-)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 6232a7456e8..0d003c6cc21 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -2101,13 +2101,29 @@ static void test_RegisterRawInputDevices(void)
static int rawinputbuffer_wndproc_count;
+typedef struct +{ + DWORD dwType; + DWORD dwSize; + ULONG hDevice; + ULONG wParam; +} RAWINPUTHEADER32; + #ifdef _WIN64 +typedef RAWINPUTHEADER RAWINPUTHEADER64; typedef RAWINPUT RAWINPUT64; #else typedef struct { - RAWINPUTHEADER header; - char pad[8]; + DWORD dwType; + DWORD dwSize; + ULONGLONG hDevice; + ULONGLONG wParam; +} RAWINPUTHEADER64; + +typedef struct +{ + RAWINPUTHEADER64 header; union { RAWMOUSE mouse; RAWKEYBOARD keyboard; @@ -2124,9 +2140,9 @@ static int rawinput_buffer_mouse_x(void *buffer, size_t index)
static LRESULT CALLBACK rawinputbuffer_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + UINT i, size, count, rawinput_size, iteration = rawinputbuffer_wndproc_count++; RAWINPUT ri; char buffer[16 * sizeof(RAWINPUT64)]; - UINT size, count, rawinput_size, iteration = rawinputbuffer_wndproc_count++; MSG message;
if (is_wow64) rawinput_size = sizeof(RAWINPUT64); @@ -2134,8 +2150,10 @@ static LRESULT CALLBACK rawinputbuffer_wndproc(HWND hwnd, UINT msg, WPARAM wpara
if (msg == WM_INPUT) { + SetLastError(0xdeadbeef); count = GetRawInputBuffer(NULL, NULL, sizeof(RAWINPUTHEADER)); ok(count == ~0U, "GetRawInputBuffer succeeded\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "got error %lu\n", GetLastError());
size = sizeof(buffer); count = GetRawInputBuffer(NULL, &size, sizeof(RAWINPUTHEADER)); @@ -2147,6 +2165,27 @@ static LRESULT CALLBACK rawinputbuffer_wndproc(HWND hwnd, UINT msg, WPARAM wpara count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER)); ok(count == 3, "GetRawInputBuffer returned %u\n", count); ok(size == sizeof(buffer), "GetRawInputBuffer returned unexpected size: %u\n", size); + + for (i = 0; i < 3; ++i) + { + if (is_wow64) + { + const RAWINPUT64 *data = &((RAWINPUT64 *)buffer)[i]; + ok(data->header.dwType == RIM_TYPEMOUSE, "Unexpected rawinput type: %lu\n", data->header.dwType); + ok(data->header.dwSize == sizeof(*data), "Unexpected rawinput size: %lu\n", data->header.dwSize); + todo_wine_if (wparam) + ok(data->header.wParam == wparam, "Unexpected wparam: %#I64x\n", data->header.wParam); + } + else + { + const RAWINPUT *data = &((RAWINPUT *)buffer)[i]; + ok(data->header.dwType == RIM_TYPEMOUSE, "Unexpected rawinput type: %lu\n", data->header.dwType); + ok(data->header.dwSize == sizeof(*data), "Unexpected rawinput size: %lu\n", data->header.dwSize); + todo_wine_if (wparam) + ok(data->header.wParam == wparam, "Unexpected wparam: %#Ix\n", data->header.wParam); + } + } + ok(rawinput_buffer_mouse_x(buffer, 0) == 2, "Unexpected rawinput data: %d\n", rawinput_buffer_mouse_x(buffer, 0)); ok(rawinput_buffer_mouse_x(buffer, 1) == 3, "Unexpected rawinput data: %d\n", rawinput_buffer_mouse_x(buffer, 1)); ok(rawinput_buffer_mouse_x(buffer, 2) == 4, "Unexpected rawinput data: %d\n", rawinput_buffer_mouse_x(buffer, 2)); @@ -2183,7 +2222,12 @@ static LRESULT CALLBACK rawinputbuffer_wndproc(HWND hwnd, UINT msg, WPARAM wpara { SetLastError(0xdeadbeef); count = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &ri, &size, 0); - ok(count == ~0U, "GetRawInputData succeeded\n"); + ok(count == ~0u, "GetRawInputData returned %d\n", count); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputData returned %08lx\n", GetLastError()); + + SetLastError(0xdeadbeef); + count = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &ri, &size, sizeof(RAWINPUTHEADER) + 1); + ok(count == ~0u, "GetRawInputData returned %d\n", count); ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputData returned %08lx\n", GetLastError());
SetLastError(0xdeadbeef); @@ -2204,6 +2248,30 @@ static LRESULT CALLBACK rawinputbuffer_wndproc(HWND hwnd, UINT msg, WPARAM wpara ok(count == sizeof(ri), "GetRawInputData failed\n"); ok(ri.data.mouse.lLastX == 6, "Unexpected rawinput data: %ld\n", ri.data.mouse.lLastX); ok(GetLastError() == 0xdeadbeef, "GetRawInputData returned %08lx\n", GetLastError()); + + SetLastError(0xdeadbeef); + size = sizeof(buffer); + if (sizeof(void *) == 8) + { + count = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &ri, &size, sizeof(RAWINPUTHEADER32)); + ok(count == ~0u, "GetRawInputData returned %d\n", count); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputData returned %08lx\n", GetLastError()); + } + else + { + count = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &ri, &size, sizeof(RAWINPUTHEADER64)); + if (is_wow64) + { + todo_wine ok(count == sizeof(ri), "GetRawInputData returned %d\n", count); + ok(ri.data.mouse.lLastX == 6, "Unexpected rawinput data: %ld\n", ri.data.mouse.lLastX); + todo_wine ok(GetLastError() == 0xdeadbeef, "GetRawInputData returned %08lx\n", GetLastError()); + } + else + { + ok(count == ~0u, "GetRawInputData returned %d\n", count); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputData returned %08lx\n", GetLastError()); + } + } } else { @@ -2219,9 +2287,9 @@ static LRESULT CALLBACK rawinputbuffer_wndproc(HWND hwnd, UINT msg, WPARAM wpara
static void test_GetRawInputBuffer(void) { + unsigned int size, count, rawinput_size, header_size; RAWINPUTDEVICE raw_devices[1]; char buffer[16 * sizeof(RAWINPUT64)]; - UINT size, count, rawinput_size; HWND hwnd; BOOL ret; POINT pt; @@ -2295,6 +2363,21 @@ static void test_GetRawInputBuffer(void) ok(count == ~0U, "GetRawInputBuffer succeeded\n"); ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputBuffer returned %08lx\n", GetLastError());
+ SetLastError(0xdeadbeef); + size = sizeof(buffer); + count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER) + 1); + ok(count == ~0U, "GetRawInputBuffer succeeded\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputBuffer returned %08lx\n", GetLastError()); + + /* the function returns 64-bit RAWINPUT structures on WoW64, but still + * forbids sizeof(RAWINPUTHEADER) from the wrong architecture */ + SetLastError(0xdeadbeef); + size = sizeof(buffer); + header_size = (sizeof(void *) == 8 ? sizeof(RAWINPUTHEADER32) : sizeof(RAWINPUTHEADER64)); + count = GetRawInputBuffer((RAWINPUT*)buffer, &size, header_size); + ok(count == ~0U, "GetRawInputBuffer succeeded\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetRawInputBuffer returned %08lx\n", GetLastError()); + size = sizeof(buffer); memset(buffer, 0, sizeof(buffer)); count = GetRawInputBuffer((RAWINPUT*)buffer, &size, sizeof(RAWINPUTHEADER)); @@ -2365,6 +2448,9 @@ static LRESULT CALLBACK rawinput_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPA ret = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &raw, &raw_size, sizeof(RAWINPUTHEADER)); ok(ret > 0 && ret != (UINT)-1, "GetRawInputData failed\n"); ok(raw.header.dwType == RIM_TYPEMOUSE, "Unexpected rawinput type: %lu\n", raw.header.dwType); + ok(raw.header.dwSize == raw_size, "Expected size %u, got %lu\n", raw_size, raw.header.dwSize); + todo_wine_if (wparam) + ok(raw.header.wParam == wparam, "Expected wparam %Iu, got %Iu\n", wparam, raw.header.wParam);
ok(!(raw.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE), "Unexpected absolute rawinput motion\n"); ok(!(raw.data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP), "Unexpected virtual desktop rawinput motion\n");
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
1 TestBot errors prevented a full analysis of your patch. If the test caused the operating system (e.g. Windows) to crash or reboot you will probably have to modify it to avoid that. Other issues should be reported to the TestBot administrators.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=117159
Your paranoid android.
=== w7u_2qxl (32 bit report) ===
user32: input.c:745: Test failed: 0 (a4/0): 00 from 00 -> 80 unexpected input.c:745: Test failed: 0 (a4/0): 41 from 01 -> 00 unexpected
=== w7u_el (32 bit report) ===
user32: input.c:4525: Test failed: SendInput triggered unexpected message 0xc042
=== w1064v1809 (64 bit report) ===
user32: input.c:1421: Test failed: Wrong new pos: (150,150)
=== w1064_tsign (64 bit report) ===
user32: input.c:1291: Test failed: Wrong set pos: (100,100) input.c:1311: Test failed: GetCursorPos: (100,100)
=== w10pro64 (testbot log) ===
WineRunTask.pl:error: BotError: The test VM is powered off! Did the test shut it down?
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=117159
Your paranoid android.
=== w7u_2qxl (32 bit report) ===
user32: input.c:745: Test failed: 0 (a4/0): 00 from 00 -> 80 unexpected input.c:745: Test failed: 0 (a4/0): 41 from 01 -> 00 unexpected
=== w7u_el (32 bit report) ===
user32: input.c:4525: Test failed: SendInput triggered unexpected message 0xc042
=== w1064 (32 bit report) ===
user32: input.c:3513: Test failed: expected WM_RBUTTONDOWN message input.c:3514: Test failed: expected WM_RBUTTONUP message
=== w1064v1809 (64 bit report) ===
user32: input.c:1311: Test failed: GetCursorPos: (100,100)
=== w1064_tsign (64 bit report) ===
user32: input.c:1291: Test failed: Wrong set pos: (100,98)
From: Zebediah Figura zfigura@codeweavers.com
This changes the offset of the wParam field. --- dlls/user32/rawinput.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/dlls/user32/rawinput.c b/dlls/user32/rawinput.c index ab542956f11..99e2270b77c 100644 --- a/dlls/user32/rawinput.c +++ b/dlls/user32/rawinput.c @@ -646,12 +646,20 @@ UINT WINAPI GetRawInputData(HRAWINPUT rawinput, UINT command, void *data, UINT * }
#ifdef _WIN64 +typedef RAWINPUTHEADER RAWINPUTHEADER64; typedef RAWINPUT RAWINPUT64; #else typedef struct { - RAWINPUTHEADER header; - char pad[8]; + DWORD dwType; + DWORD dwSize; + ULONGLONG hDevice; + ULONGLONG wParam; +} RAWINPUTHEADER64; + +typedef struct +{ + RAWINPUTHEADER64 header; union { RAWMOUSE mouse; RAWKEYBOARD keyboard;
From: Zebediah Figura zfigura@codeweavers.com
--- dlls/user32/rawinput.c | 54 +-------------------- dlls/user32/user32.spec | 2 +- dlls/user32/user_main.c | 1 + dlls/user32/user_private.h | 8 +--- dlls/win32u/Makefile.in | 1 + dlls/win32u/gdiobj.c | 1 + dlls/win32u/ntuser_private.h | 7 +++ dlls/win32u/rawinput.c | 91 ++++++++++++++++++++++++++++++++++++ dlls/win32u/win32u.spec | 2 +- dlls/win32u/win32u_private.h | 2 + dlls/win32u/wrappers.c | 6 +++ include/ntuser.h | 1 + 12 files changed, 114 insertions(+), 62 deletions(-) create mode 100644 dlls/win32u/rawinput.c
diff --git a/dlls/user32/rawinput.c b/dlls/user32/rawinput.c index 99e2270b77c..b1c79b4e186 100644 --- a/dlls/user32/rawinput.c +++ b/dlls/user32/rawinput.c @@ -351,7 +351,7 @@ BOOL rawinput_device_get_usages(HANDLE handle, USAGE *usage_page, USAGE *usage) }
-struct rawinput_thread_data *rawinput_thread_data(void) +struct rawinput_thread_data * WINAPI rawinput_thread_data(void) { struct user_thread_info *thread_info = get_user_thread_info(); struct rawinput_thread_data *data = thread_info->rawinput; @@ -593,58 +593,6 @@ BOOL WINAPI DECLSPEC_HOTPATCH RegisterRawInputDevices(const RAWINPUTDEVICE *devi return ret; }
-/*********************************************************************** - * GetRawInputData (USER32.@) - */ -UINT WINAPI GetRawInputData(HRAWINPUT rawinput, UINT command, void *data, UINT *data_size, UINT header_size) -{ - struct rawinput_thread_data *thread_data = rawinput_thread_data(); - UINT size; - - TRACE("rawinput %p, command %#x, data %p, data_size %p, header_size %u.\n", - rawinput, command, data, data_size, header_size); - - if (!rawinput || thread_data->hw_id != (UINT_PTR)rawinput) - { - SetLastError(ERROR_INVALID_HANDLE); - return ~0U; - } - - if (header_size != sizeof(RAWINPUTHEADER)) - { - WARN("Invalid structure size %u.\n", header_size); - SetLastError(ERROR_INVALID_PARAMETER); - return ~0U; - } - - switch (command) - { - case RID_INPUT: - size = thread_data->buffer->header.dwSize; - break; - case RID_HEADER: - size = sizeof(RAWINPUTHEADER); - break; - default: - SetLastError(ERROR_INVALID_PARAMETER); - return ~0U; - } - - if (!data) - { - *data_size = size; - return 0; - } - - if (*data_size < size) - { - SetLastError(ERROR_INSUFFICIENT_BUFFER); - return ~0U; - } - memcpy(data, thread_data->buffer, size); - return size; -} - #ifdef _WIN64 typedef RAWINPUTHEADER RAWINPUTHEADER64; typedef RAWINPUT RAWINPUT64; diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec index d0262a004f1..de20e692157 100644 --- a/dlls/user32/user32.spec +++ b/dlls/user32/user32.spec @@ -367,7 +367,7 @@ @ stdcall GetPropW(long wstr) @ stdcall GetQueueStatus(long) NtUserGetQueueStatus @ stdcall GetRawInputBuffer(ptr ptr long) -@ stdcall GetRawInputData(ptr long ptr ptr long) +@ stdcall GetRawInputData(ptr long ptr ptr long) NtUserGetRawInputData @ stdcall GetRawInputDeviceInfoA(ptr long ptr ptr) @ stdcall GetRawInputDeviceInfoW(ptr long ptr ptr) @ stdcall GetRawInputDeviceList(ptr ptr long) diff --git a/dlls/user32/user_main.c b/dlls/user32/user_main.c index 3cb42e15b1e..4901bcf7193 100644 --- a/dlls/user32/user_main.c +++ b/dlls/user32/user_main.c @@ -169,6 +169,7 @@ static const struct user_callbacks user_funcs = register_imm, unregister_imm, try_finally, + rawinput_thread_data, };
static NTSTATUS WINAPI User32CopyImage( const struct copy_image_params *params, ULONG size ) diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h index a3a3d28c2d9..a31a2bd8a01 100644 --- a/dlls/user32/user_private.h +++ b/dlls/user32/user_private.h @@ -51,12 +51,6 @@ struct wm_char_mapping_data /* hold up to 10s of 1kHz mouse rawinput events */ #define RAWINPUT_BUFFER_SIZE (512*1024)
-struct rawinput_thread_data -{ - UINT hw_id; /* current rawinput message id */ - RAWINPUT buffer[1]; /* rawinput message data buffer */ -}; - extern BOOL (WINAPI *imm_register_window)(HWND) DECLSPEC_HIDDEN; extern void (WINAPI *imm_unregister_window)(HWND) DECLSPEC_HIDDEN;
@@ -73,7 +67,7 @@ struct tagWND; struct hardware_msg_data; extern BOOL rawinput_from_hardware_message(RAWINPUT *rawinput, const struct hardware_msg_data *msg_data); extern BOOL rawinput_device_get_usages(HANDLE handle, USAGE *usage_page, USAGE *usage); -extern struct rawinput_thread_data *rawinput_thread_data(void); +extern struct rawinput_thread_data * WINAPI rawinput_thread_data(void); extern void rawinput_update_device_list(void);
extern BOOL post_dde_message( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, DWORD dest_tid, diff --git a/dlls/win32u/Makefile.in b/dlls/win32u/Makefile.in index d58ed6bb41c..cc429836de3 100644 --- a/dlls/win32u/Makefile.in +++ b/dlls/win32u/Makefile.in @@ -43,6 +43,7 @@ C_SRCS = \ path.c \ pen.c \ printdrv.c \ + rawinput.c \ region.c \ spy.c \ syscall.c \ diff --git a/dlls/win32u/gdiobj.c b/dlls/win32u/gdiobj.c index da2e78be2dd..77c3e16242a 100644 --- a/dlls/win32u/gdiobj.c +++ b/dlls/win32u/gdiobj.c @@ -1183,6 +1183,7 @@ static struct unix_funcs unix_funcs = NtUserGetMessage, NtUserGetPriorityClipboardFormat, NtUserGetQueueStatus, + NtUserGetRawInputData, NtUserGetSystemMenu, NtUserGetUpdateRect, NtUserGetUpdateRgn, diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 075fc714744..babbc8208bc 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -50,6 +50,7 @@ struct user_callbacks void (WINAPI *unregister_imm)( HWND hwnd ); NTSTATUS (CDECL *try_finally)( NTSTATUS (CDECL *func)( void *), void *arg, void (CALLBACK *finally_func)( BOOL )); + struct rawinput_thread_data *(WINAPI *get_rawinput_thread_data)(void); };
#define WM_SYSTIMER 0x0118 @@ -61,6 +62,12 @@ enum system_timer_id SYSTEM_TIMER_CARET = 0xffff, };
+struct rawinput_thread_data +{ + UINT hw_id; /* current rawinput message id */ + RAWINPUT buffer[1]; /* rawinput message data buffer */ +}; + struct user_object { HANDLE handle; diff --git a/dlls/win32u/rawinput.c b/dlls/win32u/rawinput.c new file mode 100644 index 00000000000..0d2c585c36e --- /dev/null +++ b/dlls/win32u/rawinput.c @@ -0,0 +1,91 @@ +/* + * Raw Input + * + * Copyright 2012 Henri Verbeet + * Copyright 2018 Zebediah Figura 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 + */ + +#if 0 +#pragma makedep unix +#endif + +#include "win32u_private.h" +#include "ntuser_private.h" +#include "wine/server.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(rawinput); + +/********************************************************************** + * NtUserGetRawInputData (win32u.@) + */ +UINT WINAPI NtUserGetRawInputData( HRAWINPUT rawinput, UINT command, void *data, UINT *data_size, UINT header_size ) +{ + struct rawinput_thread_data *thread_data; + UINT size; + + TRACE( "rawinput %p, command %#x, data %p, data_size %p, header_size %u.\n", + rawinput, command, data, data_size, header_size ); + + if (!user_callbacks || !(thread_data = user_callbacks->get_rawinput_thread_data())) + { + SetLastError( ERROR_OUTOFMEMORY ); + return ~0u; + } + + if (!rawinput || thread_data->hw_id != (UINT_PTR)rawinput) + { + SetLastError( ERROR_INVALID_HANDLE ); + return ~0u; + } + + if (header_size != sizeof(RAWINPUTHEADER)) + { + WARN( "Invalid structure size %u.\n", header_size ); + SetLastError( ERROR_INVALID_PARAMETER ); + return ~0u; + } + + switch (command) + { + case RID_INPUT: + size = thread_data->buffer->header.dwSize; + break; + + case RID_HEADER: + size = sizeof(RAWINPUTHEADER); + break; + + default: + SetLastError( ERROR_INVALID_PARAMETER ); + return ~0u; + } + + if (!data) + { + *data_size = size; + return 0; + } + + if (*data_size < size) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return ~0u; + } + memcpy( data, thread_data->buffer, size ); + return size; +} diff --git a/dlls/win32u/win32u.spec b/dlls/win32u/win32u.spec index 6f9dc0e921b..c984316e7c9 100644 --- a/dlls/win32u/win32u.spec +++ b/dlls/win32u/win32u.spec @@ -984,7 +984,7 @@ @ stdcall NtUserGetQueueStatus(long) @ stub NtUserGetQueueStatusReadonly @ stub NtUserGetRawInputBuffer -@ stub NtUserGetRawInputData +@ stdcall NtUserGetRawInputData(ptr long ptr ptr long) @ stub NtUserGetRawInputDeviceInfo @ stub NtUserGetRawInputDeviceList @ stub NtUserGetRawPointerDeviceData diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index dc62f6846a5..f431b967c32 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -245,6 +245,8 @@ struct unix_funcs BOOL (WINAPI *pNtUserGetMessage)( MSG *msg, HWND hwnd, UINT first, UINT last ); INT (WINAPI *pNtUserGetPriorityClipboardFormat)( UINT *list, INT count ); DWORD (WINAPI *pNtUserGetQueueStatus)( UINT flags ); + UINT (WINAPI *pNtUserGetRawInputData)( HRAWINPUT rawinput, UINT command, + void *data, UINT *data_size, UINT header_size ); HMENU (WINAPI *pNtUserGetSystemMenu)( HWND hwnd, BOOL revert ); BOOL (WINAPI *pNtUserGetUpdateRect)( HWND hwnd, RECT *rect, BOOL erase ); INT (WINAPI *pNtUserGetUpdateRgn)( HWND hwnd, HRGN hrgn, BOOL erase ); diff --git a/dlls/win32u/wrappers.c b/dlls/win32u/wrappers.c index ed4337eae84..56ffef8f478 100644 --- a/dlls/win32u/wrappers.c +++ b/dlls/win32u/wrappers.c @@ -1041,6 +1041,12 @@ DWORD WINAPI NtUserGetQueueStatus( UINT flags ) return unix_funcs->pNtUserGetQueueStatus( flags ); }
+UINT WINAPI NtUserGetRawInputData( HRAWINPUT rawinput, UINT command, void *data, UINT *data_size, UINT header_size ) +{ + if (!unix_funcs) return ~0u; + return unix_funcs->pNtUserGetRawInputData( rawinput, command, data, data_size, header_size ); +} + BOOL WINAPI NtUserGetUpdatedClipboardFormats( UINT *formats, UINT size, UINT *out_size ) { if (!unix_funcs) return FALSE; diff --git a/include/ntuser.h b/include/ntuser.h index 56c920aacdf..269ba3ae490 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -604,6 +604,7 @@ HWINSTA WINAPI NtUserGetProcessWindowStation(void); HANDLE WINAPI NtUserGetProp( HWND hwnd, const WCHAR *str ); ULONG WINAPI NtUserGetProcessDpiAwarenessContext( HANDLE process ); DWORD WINAPI NtUserGetQueueStatus( UINT flags ); +UINT WINAPI NtUserGetRawInputData( HRAWINPUT rawinput, UINT command, void *data, UINT *data_size, UINT header_size ); ULONG WINAPI NtUserGetSystemDpiForProcess( HANDLE process ); HMENU WINAPI NtUserGetSystemMenu( HWND hwnd, BOOL revert ); HDESK WINAPI NtUserGetThreadDesktop( DWORD thread );
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=117161
Your paranoid android.
=== debian11 (64 bit WoW report) ===
user32: win.c:10982: Test failed: Expected foreground window 00000000001A0124, got 0000000001430052 win.c:10985: Test failed: Received WM_ACTIVATEAPP(0), did not expect it. win.c:10992: Test failed: Expected foreground window 00000000001A0124, got 0000000000000000 win.c:10994: Test failed: GetActiveWindow() = 0000000000000000 win.c:10994: Test failed: GetFocus() = 0000000000000000 win.c:10999: Test failed: Expected foreground window 00000000001A0124, got 0000000000000000
From: Zebediah Figura zfigura@codeweavers.com
--- dlls/user32/rawinput.c | 111 ---------------- dlls/user32/user32.spec | 2 +- dlls/user32/user_private.h | 4 - dlls/win32u/gdiobj.c | 1 + dlls/win32u/ntuser_private.h | 4 + dlls/win32u/rawinput.c | 245 +++++++++++++++++++++++++++++++++++ dlls/win32u/win32u.spec | 2 +- dlls/win32u/win32u_private.h | 1 + dlls/win32u/wrappers.c | 6 + include/ntuser.h | 1 + 10 files changed, 260 insertions(+), 117 deletions(-)
diff --git a/dlls/user32/rawinput.c b/dlls/user32/rawinput.c index b1c79b4e186..f8980f03e9e 100644 --- a/dlls/user32/rawinput.c +++ b/dlls/user32/rawinput.c @@ -593,117 +593,6 @@ BOOL WINAPI DECLSPEC_HOTPATCH RegisterRawInputDevices(const RAWINPUTDEVICE *devi return ret; }
-#ifdef _WIN64 -typedef RAWINPUTHEADER RAWINPUTHEADER64; -typedef RAWINPUT RAWINPUT64; -#else -typedef struct -{ - DWORD dwType; - DWORD dwSize; - ULONGLONG hDevice; - ULONGLONG wParam; -} RAWINPUTHEADER64; - -typedef struct -{ - RAWINPUTHEADER64 header; - union { - RAWMOUSE mouse; - RAWKEYBOARD keyboard; - RAWHID hid; - } data; -} RAWINPUT64; -#endif - -/*********************************************************************** - * GetRawInputBuffer (USER32.@) - */ -UINT WINAPI DECLSPEC_HOTPATCH GetRawInputBuffer(RAWINPUT *data, UINT *data_size, UINT header_size) -{ - struct hardware_msg_data *msg_data; - struct rawinput_thread_data *thread_data; - RAWINPUT *rawinput; - UINT count = 0, remaining, rawinput_size, next_size, overhead; - BOOL is_wow64; - int i; - - if (IsWow64Process( GetCurrentProcess(), &is_wow64 ) && is_wow64) - rawinput_size = sizeof(RAWINPUT64); - else - rawinput_size = sizeof(RAWINPUT); - overhead = rawinput_size - sizeof(RAWINPUT); - - if (header_size != sizeof(RAWINPUTHEADER)) - { - WARN("Invalid structure size %u.\n", header_size); - SetLastError(ERROR_INVALID_PARAMETER); - return ~0U; - } - - if (!data_size) - { - SetLastError(ERROR_INVALID_PARAMETER); - return ~0U; - } - - if (!data) - { - TRACE("data %p, data_size %p (%u), header_size %u\n", data, data_size, *data_size, header_size); - SERVER_START_REQ( get_rawinput_buffer ) - { - req->rawinput_size = rawinput_size; - req->buffer_size = 0; - if (wine_server_call( req )) return ~0U; - *data_size = reply->next_size; - } - SERVER_END_REQ; - return 0; - } - - if (!(thread_data = rawinput_thread_data())) return ~0U; - rawinput = thread_data->buffer; - - /* first RAWINPUT block in the buffer is used for WM_INPUT message data */ - msg_data = (struct hardware_msg_data *)NEXTRAWINPUTBLOCK(rawinput); - SERVER_START_REQ( get_rawinput_buffer ) - { - req->rawinput_size = rawinput_size; - req->buffer_size = *data_size; - wine_server_set_reply( req, msg_data, RAWINPUT_BUFFER_SIZE - rawinput->header.dwSize ); - if (wine_server_call( req )) return ~0U; - next_size = reply->next_size; - count = reply->count; - } - SERVER_END_REQ; - - remaining = *data_size; - for (i = 0; i < count; ++i) - { - data->header.dwSize = remaining; - if (!rawinput_from_hardware_message(data, msg_data)) break; - if (overhead) memmove((char *)&data->data + overhead, &data->data, - data->header.dwSize - sizeof(RAWINPUTHEADER)); - data->header.dwSize += overhead; - remaining -= data->header.dwSize; - data = NEXTRAWINPUTBLOCK(data); - msg_data = (struct hardware_msg_data *)((char *)msg_data + msg_data->size); - } - - if (count == 0 && next_size == 0) *data_size = 0; - else if (next_size == 0) next_size = rawinput_size; - - if (next_size && *data_size <= next_size) - { - SetLastError(ERROR_INSUFFICIENT_BUFFER); - *data_size = next_size; - count = ~0U; - } - - if (count) TRACE("data %p, data_size %p (%u), header_size %u, count %u\n", data, data_size, *data_size, header_size, count); - return count; -} - /*********************************************************************** * GetRawInputDeviceInfoA (USER32.@) */ diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec index de20e692157..70f523da804 100644 --- a/dlls/user32/user32.spec +++ b/dlls/user32/user32.spec @@ -366,7 +366,7 @@ @ stdcall GetPropA(long str) @ stdcall GetPropW(long wstr) @ stdcall GetQueueStatus(long) NtUserGetQueueStatus -@ stdcall GetRawInputBuffer(ptr ptr long) +@ stdcall GetRawInputBuffer(ptr ptr long) NtUserGetRawInputBuffer @ stdcall GetRawInputData(ptr long ptr ptr long) NtUserGetRawInputData @ stdcall GetRawInputDeviceInfoA(ptr long ptr ptr) @ stdcall GetRawInputDeviceInfoW(ptr long ptr ptr) diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h index a31a2bd8a01..9a2b7de4509 100644 --- a/dlls/user32/user_private.h +++ b/dlls/user32/user_private.h @@ -47,10 +47,6 @@ struct wm_char_mapping_data MSG get_msg; };
-/* on windows the buffer capacity is quite large as well, enough to */ -/* hold up to 10s of 1kHz mouse rawinput events */ -#define RAWINPUT_BUFFER_SIZE (512*1024) - extern BOOL (WINAPI *imm_register_window)(HWND) DECLSPEC_HIDDEN; extern void (WINAPI *imm_unregister_window)(HWND) DECLSPEC_HIDDEN;
diff --git a/dlls/win32u/gdiobj.c b/dlls/win32u/gdiobj.c index 77c3e16242a..3eb24a7bc32 100644 --- a/dlls/win32u/gdiobj.c +++ b/dlls/win32u/gdiobj.c @@ -1183,6 +1183,7 @@ static struct unix_funcs unix_funcs = NtUserGetMessage, NtUserGetPriorityClipboardFormat, NtUserGetQueueStatus, + NtUserGetRawInputBuffer, NtUserGetRawInputData, NtUserGetSystemMenu, NtUserGetUpdateRect, diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index babbc8208bc..e8051ac39d3 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -68,6 +68,10 @@ struct rawinput_thread_data RAWINPUT buffer[1]; /* rawinput message data buffer */ };
+/* on windows the buffer capacity is quite large as well, enough to */ +/* hold up to 10s of 1kHz mouse rawinput events */ +#define RAWINPUT_BUFFER_SIZE (512 * 1024) + struct user_object { HANDLE handle; diff --git a/dlls/win32u/rawinput.c b/dlls/win32u/rawinput.c index 0d2c585c36e..d6f38382b4b 100644 --- a/dlls/win32u/rawinput.c +++ b/dlls/win32u/rawinput.c @@ -23,6 +23,7 @@ #pragma makedep unix #endif
+#include <stdbool.h> #include "win32u_private.h" #include "ntuser_private.h" #include "wine/server.h" @@ -30,6 +31,250 @@
WINE_DEFAULT_DEBUG_CHANNEL(rawinput);
+#define WINE_MOUSE_HANDLE ((HANDLE)1) +#define WINE_KEYBOARD_HANDLE ((HANDLE)2) + +#ifdef _WIN64 +typedef RAWINPUTHEADER RAWINPUTHEADER64; +typedef RAWINPUT RAWINPUT64; +#else +typedef struct +{ + DWORD dwType; + DWORD dwSize; + ULONGLONG hDevice; + ULONGLONG wParam; +} RAWINPUTHEADER64; + +typedef struct +{ + RAWINPUTHEADER64 header; + union + { + RAWMOUSE mouse; + RAWKEYBOARD keyboard; + RAWHID hid; + } data; +} RAWINPUT64; +#endif + +static bool rawinput_from_hardware_message( RAWINPUT *rawinput, const struct hardware_msg_data *msg_data ) +{ + SIZE_T size; + + rawinput->header.dwType = msg_data->rawinput.type; + if (msg_data->rawinput.type == RIM_TYPEMOUSE) + { + static const unsigned int button_flags[] = + { + 0, /* MOUSEEVENTF_MOVE */ + RI_MOUSE_LEFT_BUTTON_DOWN, /* MOUSEEVENTF_LEFTDOWN */ + RI_MOUSE_LEFT_BUTTON_UP, /* MOUSEEVENTF_LEFTUP */ + RI_MOUSE_RIGHT_BUTTON_DOWN, /* MOUSEEVENTF_RIGHTDOWN */ + RI_MOUSE_RIGHT_BUTTON_UP, /* MOUSEEVENTF_RIGHTUP */ + RI_MOUSE_MIDDLE_BUTTON_DOWN, /* MOUSEEVENTF_MIDDLEDOWN */ + RI_MOUSE_MIDDLE_BUTTON_UP, /* MOUSEEVENTF_MIDDLEUP */ + }; + unsigned int i; + + rawinput->header.dwSize = FIELD_OFFSET(RAWINPUT, data) + sizeof(RAWMOUSE); + rawinput->header.hDevice = WINE_MOUSE_HANDLE; + rawinput->header.wParam = 0; + + rawinput->data.mouse.usFlags = MOUSE_MOVE_RELATIVE; + rawinput->data.mouse.usButtonFlags = 0; + rawinput->data.mouse.usButtonData = 0; + for (i = 1; i < ARRAY_SIZE(button_flags); ++i) + { + if (msg_data->flags & (1 << i)) + rawinput->data.mouse.usButtonFlags |= button_flags[i]; + } + if (msg_data->flags & MOUSEEVENTF_WHEEL) + { + rawinput->data.mouse.usButtonFlags |= RI_MOUSE_WHEEL; + rawinput->data.mouse.usButtonData = msg_data->rawinput.mouse.data; + } + if (msg_data->flags & MOUSEEVENTF_HWHEEL) + { + rawinput->data.mouse.usButtonFlags |= RI_MOUSE_HORIZONTAL_WHEEL; + rawinput->data.mouse.usButtonData = msg_data->rawinput.mouse.data; + } + if (msg_data->flags & MOUSEEVENTF_XDOWN) + { + if (msg_data->rawinput.mouse.data == XBUTTON1) + rawinput->data.mouse.usButtonFlags |= RI_MOUSE_BUTTON_4_DOWN; + else if (msg_data->rawinput.mouse.data == XBUTTON2) + rawinput->data.mouse.usButtonFlags |= RI_MOUSE_BUTTON_5_DOWN; + } + if (msg_data->flags & MOUSEEVENTF_XUP) + { + if (msg_data->rawinput.mouse.data == XBUTTON1) + rawinput->data.mouse.usButtonFlags |= RI_MOUSE_BUTTON_4_UP; + else if (msg_data->rawinput.mouse.data == XBUTTON2) + rawinput->data.mouse.usButtonFlags |= RI_MOUSE_BUTTON_5_UP; + } + + rawinput->data.mouse.ulRawButtons = 0; + rawinput->data.mouse.lLastX = msg_data->rawinput.mouse.x; + rawinput->data.mouse.lLastY = msg_data->rawinput.mouse.y; + rawinput->data.mouse.ulExtraInformation = msg_data->info; + } + else if (msg_data->rawinput.type == RIM_TYPEKEYBOARD) + { + rawinput->header.dwSize = FIELD_OFFSET(RAWINPUT, data) + sizeof(RAWKEYBOARD); + rawinput->header.hDevice = WINE_KEYBOARD_HANDLE; + rawinput->header.wParam = 0; + + rawinput->data.keyboard.MakeCode = msg_data->rawinput.kbd.scan; + rawinput->data.keyboard.Flags = (msg_data->flags & KEYEVENTF_KEYUP) ? RI_KEY_BREAK : RI_KEY_MAKE; + if (msg_data->flags & KEYEVENTF_EXTENDEDKEY) + rawinput->data.keyboard.Flags |= RI_KEY_E0; + rawinput->data.keyboard.Reserved = 0; + + switch (msg_data->rawinput.kbd.vkey) + { + case VK_LSHIFT: + case VK_RSHIFT: + rawinput->data.keyboard.VKey = VK_SHIFT; + rawinput->data.keyboard.Flags &= ~RI_KEY_E0; + break; + + case VK_LCONTROL: + case VK_RCONTROL: + rawinput->data.keyboard.VKey = VK_CONTROL; + break; + + case VK_LMENU: + case VK_RMENU: + rawinput->data.keyboard.VKey = VK_MENU; + break; + + default: + rawinput->data.keyboard.VKey = msg_data->rawinput.kbd.vkey; + break; + } + + rawinput->data.keyboard.Message = msg_data->rawinput.kbd.message; + rawinput->data.keyboard.ExtraInformation = msg_data->info; + } + else if (msg_data->rawinput.type == RIM_TYPEHID) + { + size = msg_data->size - sizeof(*msg_data); + if (size > rawinput->header.dwSize - sizeof(*rawinput)) return false; + + rawinput->header.dwSize = FIELD_OFFSET( RAWINPUT, data.hid.bRawData ) + size; + rawinput->header.hDevice = ULongToHandle( msg_data->rawinput.hid.device ); + rawinput->header.wParam = 0; + + rawinput->data.hid.dwCount = msg_data->rawinput.hid.count; + rawinput->data.hid.dwSizeHid = msg_data->rawinput.hid.length; + memcpy( rawinput->data.hid.bRawData, msg_data + 1, size ); + } + else + { + FIXME( "Unhandled rawinput type %#x.\n", msg_data->rawinput.type ); + return false; + } + + return true; +} + +/********************************************************************** + * NtUserGetRawInputBuffer (win32u.@) + */ +UINT WINAPI NtUserGetRawInputBuffer( RAWINPUT *data, UINT *data_size, UINT header_size ) +{ + unsigned int count = 0, remaining, rawinput_size, next_size, overhead; + struct rawinput_thread_data *thread_data; + struct hardware_msg_data *msg_data; + RAWINPUT *rawinput; + int i; + + if (NtCurrentTeb()->WowTebOffset) + rawinput_size = sizeof(RAWINPUT64); + else + rawinput_size = sizeof(RAWINPUT); + overhead = rawinput_size - sizeof(RAWINPUT); + + if (header_size != sizeof(RAWINPUTHEADER)) + { + WARN( "Invalid structure size %u.\n", header_size ); + SetLastError( ERROR_INVALID_PARAMETER ); + return ~0u; + } + + if (!data_size) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return ~0u; + } + + if (!data) + { + TRACE( "data %p, data_size %p (%u), header_size %u\n", data, data_size, *data_size, header_size ); + SERVER_START_REQ( get_rawinput_buffer ) + { + req->rawinput_size = rawinput_size; + req->buffer_size = 0; + if (wine_server_call( req )) return ~0u; + *data_size = reply->next_size; + } + SERVER_END_REQ; + return 0; + } + + if (!user_callbacks || !(thread_data = user_callbacks->get_rawinput_thread_data())) return ~0u; + rawinput = thread_data->buffer; + + /* first RAWINPUT block in the buffer is used for WM_INPUT message data */ + msg_data = (struct hardware_msg_data *)NEXTRAWINPUTBLOCK(rawinput); + SERVER_START_REQ( get_rawinput_buffer ) + { + req->rawinput_size = rawinput_size; + req->buffer_size = *data_size; + wine_server_set_reply( req, msg_data, RAWINPUT_BUFFER_SIZE - rawinput->header.dwSize ); + if (wine_server_call( req )) return ~0u; + next_size = reply->next_size; + count = reply->count; + } + SERVER_END_REQ; + + remaining = *data_size; + for (i = 0; i < count; ++i) + { + data->header.dwSize = remaining; + if (!rawinput_from_hardware_message( data, msg_data )) break; + if (overhead) + { + memmove( (char *)&data->data + overhead, &data->data, + data->header.dwSize - sizeof(RAWINPUTHEADER) ); + } + data->header.dwSize += overhead; + remaining -= data->header.dwSize; + data = NEXTRAWINPUTBLOCK(data); + msg_data = (struct hardware_msg_data *)((char *)msg_data + msg_data->size); + } + + if (!next_size) + { + if (!count) + *data_size = 0; + else + next_size = rawinput_size; + } + + if (next_size && *data_size <= next_size) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + *data_size = next_size; + count = ~0u; + } + + TRACE( "data %p, data_size %p (%u), header_size %u, count %u\n", + data, data_size, *data_size, header_size, count ); + return count; +} + /********************************************************************** * NtUserGetRawInputData (win32u.@) */ diff --git a/dlls/win32u/win32u.spec b/dlls/win32u/win32u.spec index c984316e7c9..02c2cddcf5e 100644 --- a/dlls/win32u/win32u.spec +++ b/dlls/win32u/win32u.spec @@ -983,7 +983,7 @@ @ stdcall -syscall NtUserGetProp(long wstr) @ stdcall NtUserGetQueueStatus(long) @ stub NtUserGetQueueStatusReadonly -@ stub NtUserGetRawInputBuffer +@ stdcall NtUserGetRawInputBuffer(ptr ptr long) @ stdcall NtUserGetRawInputData(ptr long ptr ptr long) @ stub NtUserGetRawInputDeviceInfo @ stub NtUserGetRawInputDeviceList diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index f431b967c32..d6885e97291 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -245,6 +245,7 @@ struct unix_funcs BOOL (WINAPI *pNtUserGetMessage)( MSG *msg, HWND hwnd, UINT first, UINT last ); INT (WINAPI *pNtUserGetPriorityClipboardFormat)( UINT *list, INT count ); DWORD (WINAPI *pNtUserGetQueueStatus)( UINT flags ); + UINT (WINAPI *pNtUserGetRawInputBuffer)( RAWINPUT *data, UINT *data_size, UINT header_size ); UINT (WINAPI *pNtUserGetRawInputData)( HRAWINPUT rawinput, UINT command, void *data, UINT *data_size, UINT header_size ); HMENU (WINAPI *pNtUserGetSystemMenu)( HWND hwnd, BOOL revert ); diff --git a/dlls/win32u/wrappers.c b/dlls/win32u/wrappers.c index 56ffef8f478..40c037b3fd7 100644 --- a/dlls/win32u/wrappers.c +++ b/dlls/win32u/wrappers.c @@ -1041,6 +1041,12 @@ DWORD WINAPI NtUserGetQueueStatus( UINT flags ) return unix_funcs->pNtUserGetQueueStatus( flags ); }
+UINT WINAPI DECLSPEC_HOTPATCH NtUserGetRawInputBuffer( RAWINPUT *data, UINT *data_size, UINT header_size ) +{ + if (!unix_funcs) return ~0u; + return unix_funcs->pNtUserGetRawInputBuffer( data, data_size, header_size ); +} + UINT WINAPI NtUserGetRawInputData( HRAWINPUT rawinput, UINT command, void *data, UINT *data_size, UINT header_size ) { if (!unix_funcs) return ~0u; diff --git a/include/ntuser.h b/include/ntuser.h index 269ba3ae490..bacba52db76 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -604,6 +604,7 @@ HWINSTA WINAPI NtUserGetProcessWindowStation(void); HANDLE WINAPI NtUserGetProp( HWND hwnd, const WCHAR *str ); ULONG WINAPI NtUserGetProcessDpiAwarenessContext( HANDLE process ); DWORD WINAPI NtUserGetQueueStatus( UINT flags ); +UINT WINAPI NtUserGetRawInputBuffer( RAWINPUT *data, UINT *data_size, UINT header_size ); UINT WINAPI NtUserGetRawInputData( HRAWINPUT rawinput, UINT command, void *data, UINT *data_size, UINT header_size ); ULONG WINAPI NtUserGetSystemDpiForProcess( HANDLE process ); HMENU WINAPI NtUserGetSystemMenu( HWND hwnd, BOOL revert );
This merge request was approved by Rémi Bernon.