This implementation is a bit slower than Microsoft's, as on unlock we always invalidate the entire window rather than tracking what changed. Until the accumulated bounding rectangle is implemented however we can't make this optimisation. This patch makes the halflife menu screen look a bit better, with less flashing. Unfortunately it also makes it feel a bit slower, but I think that's just an optical illusion caused by less screen activity. The bottleneck is elsewhere. ChangeLog: Implement LockWindowUpdate Index: include/win.h =================================================================== RCS file: /home/wine/wine/include/win.h,v retrieving revision 1.80 diff -u -r1.80 win.h --- include/win.h 31 Dec 2003 23:51:52 -0000 1.80 +++ include/win.h 12 Jan 2004 23:58:59 -0000 @@ -54,6 +54,7 @@ struct tagDCE *dce; /* Window DCE (if CS_OWNDC or CS_CLASSDC) */ HRGN hrgnUpdate; /* Update region */ HRGN hrgnWnd; /* window's region */ + BOOL locked; /* Locked using LockWindowUpdate */ DWORD dwStyle; /* Window style (from CreateWindow) */ DWORD dwExStyle; /* Extended style (from CreateWindowEx) */ DWORD clsStyle; /* Class style at window creation */ Index: windows/dce.c =================================================================== RCS file: /home/wine/wine/windows/dce.c,v retrieving revision 1.78 diff -u -r1.78 dce.c --- windows/dce.c 18 Mar 2003 18:35:48 -0000 1.78 +++ windows/dce.c 12 Jan 2004 23:59:00 -0000 @@ -661,30 +661,63 @@ /*********************************************************************** * LockWindowUpdate (USER32.@) + * + * Locking a window makes BeginPaint, GetDC and GetDCEX for that + * window or any of its child windows return a DC with an empty + * visible region, ie that clips all drawing. It can be used to + * temporarily suspend drawing to a window. + * + * Only one window can be locked at once. + * + * Calling with a NULL hwnd releases the lock. When locked GetDCEx can + * be used to get a DC you can draw onto for that window (to draw + * overlays, for instance). + * */ BOOL WINAPI LockWindowUpdate( HWND hwnd ) { - static HWND lockedWnd; - - FIXME("(%p), partial stub!\n",hwnd); - + static HWND locked_hwnd = NULL; + WND *win; + USER_Lock(); - if (lockedWnd) + + if (locked_hwnd) { + TRACE("unlocking hwnd 0x%x\n", locked_hwnd); + + win = WIN_GetPtr( locked_hwnd ); + win->locked = FALSE; + WIN_ReleasePtr( locked_hwnd ); + + /* FIXME: Windows tracks drawing and only invalidates the part of the window that changed. + * We should do the same, but currently [Get/Set]BoundsRect is not implemented so we just + * refresh the entire window. It's a bit slower, but this should not prove problematic. + */ + InvalidateRgn( locked_hwnd, NULL, FALSE ); + + locked_hwnd = NULL; + if (!hwnd) - { - /* Unlock lockedWnd */ - /* FIXME: Do something */ - } - else - { - /* Attempted to lock a second window */ - /* Return FALSE and do nothing */ - USER_Unlock(); - return FALSE; - } + { + /* No more work to do */ + USER_Unlock(); + return TRUE; + } + } + + TRACE("locking hwnd 0x%x\n", hwnd); + + locked_hwnd = hwnd; + win = WIN_GetPtr( locked_hwnd ); + if (win == WND_OTHER_PROCESS) FIXME("Can't lock other process window\n"); + if (!win || (win == WND_OTHER_PROCESS)) + { + USER_Unlock(); + return FALSE; } - lockedWnd = hwnd; + win->locked = TRUE; + WIN_ReleasePtr( win ); + USER_Unlock(); return TRUE; } Index: dlls/x11drv/winpos.c =================================================================== RCS file: /home/wine/wine/dlls/x11drv/winpos.c,v retrieving revision 1.72 diff -u -r1.72 winpos.c --- dlls/x11drv/winpos.c 27 Nov 2003 00:56:05 -0000 1.72 +++ dlls/x11drv/winpos.c 12 Jan 2004 23:59:05 -0000 @@ -412,6 +412,19 @@ expose_window( hwnd, &rect, 0, flags ); } +BOOL check_locked( HWND hwnd ) { + WND *win; + BOOL locked; + + win = WIN_GetPtr( hwnd ); + if (!win || win == WND_OTHER_PROCESS) return FALSE; + + locked = win->locked; + + WIN_ReleasePtr( win ); + + return locked; +} /*********************************************************************** * GetDC (X11DRV.@) @@ -425,13 +438,37 @@ HWND top = 0; X11DRV_WND_DATA *data = win->pDriverData; Drawable drawable; - BOOL visible; + BOOL visible, locked; POINT org, drawable_org; int mode = IncludeInferiors; /* don't clip siblings if using parent clip region */ if (flags & DCX_PARENTCLIP) flags &= ~DCX_CLIPSIBLINGS; + /* check if window or a parent is locked, in which case we suppress the visible region */ + locked = win->locked; + if (!locked) { + HWND *list = WIN_ListParents( hwnd ); + + if (list) { + int i; + + for (i = 0; list[i] != GetDesktopWindow(); i++) { + WND *win = WIN_GetPtr( list[i] ); + if (!win || (win == WND_OTHER_PROCESS)) break; + + if (win->locked) locked = TRUE; + WIN_ReleasePtr( win ); + + if (locked) break; + } + + HeapFree( GetProcessHeap(), 0, list ); + } + }; + + if (locked) TRACE("window %p is locked, suppressing visible region\n", hwnd); + /* find the top parent in the hierarchy that isn't clipping siblings */ visible = (win->dwStyle & WS_VISIBLE) != 0; @@ -509,7 +546,7 @@ /* need to recompute the visible region */ HRGN visRgn; - if (visible) + if (visible && !locked) { visRgn = get_visible_region( win, top, flags, mode );