After the following patch DXGrab works again. However, this patch has quite a few potential races, so I am not going to submit this to wine-patches.
Storing old windows procedure in struct x11drv_win_data would remove the worst race, but unless window procedure update is atomic, it would not remove all races.
I'm starting to think that QueueUserAPC used together with OpenThread (which does exist in Windows 2000/Me, but not yet in Wine) is the correct way to fix DXGrab.
Index: wine/dlls/x11drv/x11ddraw.c =================================================================== RCS file: /home/wine/wine/dlls/x11drv/x11ddraw.c,v retrieving revision 1.9 diff -u -r1.9 x11ddraw.c --- wine/dlls/x11drv/x11ddraw.c 2001/06/04 21:55:17 1.9 +++ wine/dlls/x11drv/x11ddraw.c 2001/07/29 13:41:52 @@ -45,20 +45,64 @@ } }
-static void GrabPointer(HWND hWnd) -{ +static LRESULT WINAPI GrabWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ Display *display = thread_display(); - if (hWnd) { - /* find the X11 window that ddraw uses */ - Window win = X11DRV_get_whole_window(hWnd); - TRACE("WND: %x win: %ld\n", hWnd, win); - if (!win) { - TRACE("host off desktop\n"); - win = root_window; - } - TSXGrabPointer(display, win, True, 0, GrabModeAsync, GrabModeAsync, win, None, CurrentTime); + + if(message != 0x7777) { + ERR("Unrecognized message %d, expect trouble!\n", message); + return 0; } - else TSXUngrabPointer(display, CurrentTime); + + TRACE("hwnd=%d, grab=%d\n", hWnd, wParam); + + if (wParam) + { + /* find the X11 window that ddraw uses */ + Window win = X11DRV_get_whole_window(hWnd); + TRACE("X11 window: %ld\n", win); + if (!win) { + TRACE("host off desktop\n"); + win = root_window; + } + + TSXGrabPointer(display, win, True, 0, GrabModeAsync, GrabModeAsync, win, None, CurrentTime); + TSXSetInputFocus(display, win, RevertToParent, CurrentTime); + } + else + { + TSXUngrabPointer(display, CurrentTime); + } + + return 0; +} + +static void GrabPointer(HWND hWnd, BOOL grab) +{ + WND* pWnd; + WNDPROC pOldProcedure; + + pWnd = WIN_FindWndPtr(hWnd); + if(!pWnd) + return; + + pOldProcedure = pWnd->winproc; + pWnd->winproc = GrabWndProc; + + WIN_ReleaseWndPtr(pWnd); + + SendMessageA(hWnd, 0x7777, grab ? 1 : 0, 0); + + pWnd = WIN_FindWndPtr(hWnd); + if(!pWnd) + return; + + if(pWnd->winproc != GrabWndProc) + ERR("Window procedure has been changed!\n"); + else + pWnd->winproc = pOldProcedure; + + WIN_ReleaseWndPtr(pWnd); }
static DWORD PASCAL X11DRV_DDHAL_DestroyDriver(LPDDHAL_DESTROYDRIVERDATA data) @@ -75,7 +119,7 @@ X11DRV_DD_PrimaryGbl = X11DRV_DD_Primary->lpGbl; SetPrimaryDIB(GET_LPDDRAWSURFACE_GBL_MORE(X11DRV_DD_PrimaryGbl)->hKernelSurface); X11DRV_DD_UserClass = GlobalFindAtomA("WINE_DDRAW"); - if (dxgrab) GrabPointer(X11DRV_DD_PrimaryWnd); + if (dxgrab) GrabPointer(X11DRV_DD_PrimaryWnd, TRUE); } data->ddRVal = DD_OK; return DDHAL_DRIVER_NOTHANDLED; @@ -108,12 +152,12 @@ static DWORD PASCAL X11DRV_DDHAL_DestroySurface(LPDDHAL_DESTROYSURFACEDATA data) { if (data->lpDDSurface == X11DRV_DD_Primary) { + if (dxgrab) GrabPointer(X11DRV_DD_PrimaryWnd, FALSE); X11DRV_DD_Primary = NULL; X11DRV_DD_PrimaryWnd = 0; X11DRV_DD_PrimaryGbl = NULL; SetPrimaryDIB(0); X11DRV_DD_UserClass = 0; - if (dxgrab) GrabPointer(0); } data->ddRVal = DD_OK; return DDHAL_DRIVER_HANDLED;