dinput & [Get|Set]CursorPos conandrum

Vitaliy Margolen wine-devel at kievinfo.com
Tue Aug 8 08:21:01 CDT 2006


Tuesday, August 8, 2006, 1:48:51 AM, Alexandre Julliard wrote:
> Vitaliy Margolen <wine-devel at kievinfo.com> 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



-------------- next part --------------
#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;
}
-------------- next part --------------
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;
 }
 


More information about the wine-devel mailing list