Tuesday, August 8, 2006, 1:48:51 AM, Alexandre Julliard wrote: > Vitaliy Margolen <[EMAIL PROTECTED]> writes:
>>> GetCursorPos really needs to query X, because there's no guarantee >>> that the app is processing X events, and even if we hack around that >> What do you mean here? X will always send notifies to a window (or am I wrong >> here?) And in notify handler we pretty much setting the cursor_pos. > But that requires that we pull events from the queue, and > GetCursorPos() doesn't do that (and can't since we don't want to > generate repaints etc. in there). Ah you mean Wine's message queue? I thought we get X's notifies via direct callback. Is this not the case then? >>> we won't receive events from other processes anyway. Now it probably >> That's the thing. We either need to send mouse notifications to all processes >> (via wineserver?) or don't really need them, as long as we can synch >> cursor_pos >> between processes. > That doesn't help, the other process is not necessarily a Wine > process. Well app shouldn't really need to know if cursor is outside it's window. And since we won't be generating WM_* messages in such case why even bother? Or... we can do something what native does - spurious WM_MOUSEMOVE messages (well known ... feature it seems) that probably come from querying mouse on some interval. >> Not so. According to few tests native always sets cursor_pos (what ever it's >> called internally - I'll call it that ;-) in SetCursorPos(). But on mouse >> moves >> it's not being set immediately but after calling hooks and generating WM_ >> messages. And that's the part lots of games relay on, including native >> dinput. > Any chance you could write a small test case that replicates the > behavior you have observed? Attached. Small test and patch to fix it (against current git). Vitaliy
#define _WIN32_WINNT 0x0501 #include <windows.h> #include <stdio.h> static HINSTANCE g_hInstance; LRESULT CALLBACK hook_proc( int code, WPARAM wparam, LPARAM lparam ) { MSLLHOOKSTRUCT *hook = (MSLLHOOKSTRUCT *)lparam; POINT pt; GetCursorPos(&pt); if (code == HC_ACTION) { printf("code=%d w=%p [(%ld %ld) %lx %lx %p] cur(%ld %ld) dl(%ld %ld)\n", code, wparam, hook->pt.x, hook->pt.y, hook->mouseData, hook->flags, hook->dwExtraInfo, pt.x, pt.y, hook->pt.x - pt.x, hook->pt.y - pt.y); } return CallNextHookEx( 0, code, wparam, lparam ); } #define SEND(x,y) \ i.mi.time = GetCurrentTime(); \ i.mi.dx = x; i.mi.dy = y; \ SendInput(1, &i, sizeof(INPUT)); inline void send_abs(int x, int y, LPINPUT i, LPPOINT pt) { int accel[3], xMult = 1, yMult = 1; int width = GetSystemMetrics(SM_CXSCREEN); int height = GetSystemMetrics(SM_CYSCREEN); SystemParametersInfoW(SPI_GETMOUSE, 0, accel, 0); if (abs(x) > accel[0] && accel[2] != 0) { if ((abs(x) > accel[1]) && (accel[2] == 2)) xMult = 4; else xMult = 2; } if (abs(y) > accel[0] && accel[2] != 0) { if ((abs(y) > accel[1]) && (accel[2] == 2)) yMult = 4; else yMult = 2; } i->mi.dx = ((pt->x + (long)(x) * xMult) << 16) / width; i->mi.dy = ((pt->y + (long)(y) * yMult) << 16) / height; i->mi.time = GetCurrentTime(); SendInput(1, i, sizeof(INPUT)); } void send_input(void) { INPUT i; POINT pt, ptorg; GetCursorPos(&ptorg); pt = ptorg; printf("Cur pos: %ld %ld\n", ptorg.x, ptorg.y); i.type = INPUT_MOUSE; i.mi.dwFlags = MOUSEEVENTF_MOVE; i.mi.dwExtraInfo = 0; i.mi.mouseData = 0; SEND(-4, 0) SEND(+4, 0) SEND(0, -4) SEND(0, +4) SetCursorPos(100, 100); GetCursorPos(&pt); fprintf(stderr, "Moved cursor to 100-100 cur(%ld %ld)\n", pt.x, pt.y); i.mi.dwFlags = MOUSEEVENTF_MOVE; SEND(-4, 0) i.mi.dwFlags |= MOUSEEVENTF_ABSOLUTE; send_abs(-10, 0, &i, &pt); send_abs(+10, 0, &i, &pt); /* Restopre position */ SetCursorPos(ptorg.x, ptorg.y); } void test1(void) { HHOOK hook; hook = SetWindowsHookExA(WH_MOUSE_LL, hook_proc, g_hInstance, 0); send_input(); UnhookWindowsHookEx(hook); } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wcex; HWND hwnd; g_hInstance = hInstance; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = NULL; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL; wcex.lpszClassName = "MainWindow"; wcex.hIconSm = NULL; RegisterClassEx(&wcex); hwnd = CreateWindow("MainWindow", "Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 200, 200, NULL, NULL, NULL, NULL); ShowWindow(hwnd, SW_SHOW); test1(); DestroyWindow(hwnd); return 0; }
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 1373a88..badb44a 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -69,7 +69,18 @@ static const UINT button_up_flags[NB_BUT MOUSEEVENTF_XUP }; -POINT cursor_pos; +POINT cursor_pos = {-10, -10}; + +static CRITICAL_SECTION cursor_CritSection; +static CRITICAL_SECTION_DEBUG critsect_debug = +{ + 0, 0, &cursor_CritSection, + { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": cursor_CritSection") } +}; +static CRITICAL_SECTION cursor_CritSection = { &critsect_debug, -1, 0, 0, 0, 0 }; + +BOOL X11DRV_SetCursorPos( INT x, INT y ); /*********************************************************************** * get_coords @@ -109,8 +120,10 @@ static inline void update_button_state( */ static inline void update_key_state( unsigned int state ) { - key_state_table[VK_SHIFT] = (state & ShiftMask ? 0x80 : 0); - key_state_table[VK_CONTROL] = (state & ControlMask ? 0x80 : 0); + DWORD time = GetCurrentTime(); + + KEYBOARD_UpdateOneState( VK_SHIFT, 0x2a, (state & ShiftMask) != 0, time ); + KEYBOARD_UpdateOneState( VK_CONTROL, 0x1d, (state & ControlMask) != 0, time ); } @@ -241,9 +254,6 @@ void X11DRV_send_mouse_input( HWND hwnd, pt.x = x; pt.y = y; } - wine_tsx11_lock(); - cursor_pos = pt; - wine_tsx11_unlock(); } else if (flags & MOUSEEVENTF_MOVE) { @@ -263,23 +273,22 @@ void X11DRV_send_mouse_input( HWND hwnd, if ((abs(y) > accel[1]) && (accel[2] == 2)) yMult = 4; } - wine_tsx11_lock(); + EnterCriticalSection( &cursor_CritSection ); pt.x = cursor_pos.x + (long)x * xMult; pt.y = cursor_pos.y + (long)y * yMult; + LeaveCriticalSection( &cursor_CritSection ); /* Clip to the current screen size */ if (pt.x < 0) pt.x = 0; else if (pt.x >= screen_width) pt.x = screen_width - 1; if (pt.y < 0) pt.y = 0; else if (pt.y >= screen_height) pt.y = screen_height - 1; - cursor_pos = pt; - wine_tsx11_unlock(); } else { - wine_tsx11_lock(); + EnterCriticalSection( &cursor_CritSection ); pt = cursor_pos; - wine_tsx11_unlock(); + LeaveCriticalSection( &cursor_CritSection ); } if (flags & MOUSEEVENTF_MOVE) @@ -289,10 +298,11 @@ void X11DRV_send_mouse_input( HWND hwnd, if ((injected_flags & LLMHF_INJECTED) && ((flags & MOUSEEVENTF_ABSOLUTE) || x || y)) /* we have to actually move the cursor */ { - TRACE( "warping to (%ld,%ld)\n", pt.x, pt.y ); - wine_tsx11_lock(); - XWarpPointer( thread_display(), root_window, root_window, 0, 0, 0, 0, pt.x, pt.y ); - wine_tsx11_unlock(); + X11DRV_SetCursorPos(pt.x, pt.y); + } else { + EnterCriticalSection( &cursor_CritSection ); + cursor_pos = pt; + LeaveCriticalSection( &cursor_CritSection ); } } if (flags & MOUSEEVENTF_LEFTDOWN) @@ -689,16 +699,29 @@ void X11DRV_SetCursor( CURSORICONINFO *l */ BOOL X11DRV_SetCursorPos( INT x, INT y ) { - Display *display = thread_display(); + Display *display; + + /* Cursor position hasn't changed at all or not yet. */ + EnterCriticalSection(&cursor_CritSection); + if (cursor_pos.x == x && cursor_pos.y == y) + { + LeaveCriticalSection(&cursor_CritSection); + return TRUE; + } + LeaveCriticalSection(&cursor_CritSection); TRACE( "warping to (%d,%d)\n", x, y ); + display = thread_display(); wine_tsx11_lock(); XWarpPointer( display, root_window, root_window, 0, 0, 0, 0, x, y ); XFlush( display ); /* avoids bad mouse lag in games that do their own mouse warping */ + wine_tsx11_unlock(); + + EnterCriticalSection( &cursor_CritSection ); cursor_pos.x = x; cursor_pos.y = y; - wine_tsx11_unlock(); + LeaveCriticalSection( &cursor_CritSection ); return TRUE; } @@ -707,23 +730,33 @@ BOOL X11DRV_SetCursorPos( INT x, INT y ) */ BOOL X11DRV_GetCursorPos(LPPOINT pos) { - Display *display = thread_display(); - Window root, child; - int rootX, rootY, winX, winY; - unsigned int xstate; - - wine_tsx11_lock(); - if (XQueryPointer( display, root_window, &root, &child, - &rootX, &rootY, &winX, &winY, &xstate )) + if (cursor_pos.x == -10 && cursor_pos.y == -10) { - update_key_state( xstate ); - update_button_state( xstate ); - TRACE("pointer at (%d,%d)\n", winX, winY ); - cursor_pos.x = winX; - cursor_pos.y = winY; + Display *display = thread_display(); + Window root, child; + int rootX, rootY, winX, winY; + unsigned int xstate; + BOOL res; + + wine_tsx11_lock(); + res = XQueryPointer( display, root_window, &root, &child, + &rootX, &rootY, &winX, &winY, &xstate ); + wine_tsx11_unlock(); + if (res) + { + update_key_state( xstate ); + update_button_state( xstate ); + TRACE("pointer at (%d,%d)\n", winX, winY ); + EnterCriticalSection( &cursor_CritSection ); + cursor_pos.x = winX; + cursor_pos.y = winY; + LeaveCriticalSection( &cursor_CritSection ); + } } + EnterCriticalSection( &cursor_CritSection ); *pos = cursor_pos; - wine_tsx11_unlock(); + LeaveCriticalSection( &cursor_CritSection ); + return TRUE; }