- Update KTHREAD and KUSER_SHARED_DATA to latest versions. This should make 2K3 drive...
[reactos.git] / reactos / subsys / win32k / ntuser / painting.c
index 7335d4e..acfcf84 100644 (file)
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-/* $Id: painting.c,v 1.33 2003/09/11 22:11:44 gvg Exp $
  *
- * COPYRIGHT:        See COPYING in the top level directory
- * PROJECT:          ReactOS kernel
- * PURPOSE:          Painting
- * FILE:             subsys/win32k/ntuser/painting.c
- * PROGRAMER:        Casper S. Hornstrup (chorns@users.sourceforge.net)
- * REVISION HISTORY:
- *       06-06-2001  CSH  Created
+ *  $Id$
+ *
+ *  COPYRIGHT:        See COPYING in the top level directory
+ *  PROJECT:          ReactOS kernel
+ *  PURPOSE:          Window painting function
+ *  FILE:             subsys/win32k/ntuser/painting.c
+ *  PROGRAMER:        Filip Navara (xnavara@volny.cz)
+ *  REVISION HISTORY:
+ *       06/06/2001   Created (?)
+ *       18/11/2003   Complete rewrite
  */
+
 /* INCLUDES ******************************************************************/
 
-#include <ddk/ntddk.h>
-#include <win32k/win32k.h>
-#include <include/object.h>
-#include <include/guicheck.h>
-#include <include/window.h>
-#include <include/class.h>
-#include <include/error.h>
-#include <include/winsta.h>
-#include <windows.h>
-#include <include/painting.h>
-#include <user32/wininternal.h>
-#include <include/rect.h>
-#include <win32k/coord.h>
-#include <win32k/region.h>
-#include <include/vis.h>
+#include <w32k.h>
 
 #define NDEBUG
 #include <debug.h>
 
+/* PRIVATE FUNCTIONS **********************************************************/
 
-/* GLOBALS *******************************************************************/
+/**
+ * @name IntIntersectWithParents
+ *
+ * Intersect window rectangle with all parent client rectangles.
+ *
+ * @param Child
+ *        Pointer to child window to start intersecting from.
+ * @param WindowRect
+ *        Pointer to rectangle that we want to intersect in screen
+ *        coordinates on input and intersected rectangle on output (if TRUE
+ *        is returned).
+ *
+ * @return
+ *    If any parent is minimized or invisible or the resulting rectangle
+ *    is empty then FALSE is returned. Otherwise TRUE is returned.
+ */
 
-/* client rect in window coordinates */
-#define GETCLIENTRECTW(wnd, r) (r).left = (wnd)->ClientRect.left - (wnd)->WindowRect.left; \
-                               (r).top = (wnd)->ClientRect.top - (wnd)->WindowRect.top; \
-                               (r).right = (wnd)->ClientRect.right - (wnd)->WindowRect.left; \
-                               (r).bottom = (wnd)->ClientRect.bottom - (wnd)->WindowRect.top
+BOOL FASTCALL
+IntIntersectWithParents(PWINDOW_OBJECT Child, PRECT WindowRect)
+{
+   PWINDOW_OBJECT ParentWindow;
 
-/* FUNCTIONS *****************************************************************/
+   ParentWindow = Child->Parent;
+   while (ParentWindow != NULL)
+   {
+      if (!(ParentWindow->Style & WS_VISIBLE) ||
+          (ParentWindow->Style & WS_MINIMIZE))
+      {
+         return FALSE;
+      }
+
+      if (!IntGdiIntersectRect(WindowRect, WindowRect, &ParentWindow->ClientRect))
+      {
+         return FALSE;
+      }
+
+      /* FIXME: Layered windows. */
+
+      ParentWindow = ParentWindow->Parent;
+   }
+
+   return TRUE;
+}
 
-HRGN STATIC STDCALL
-PaintDoPaint(PWINDOW_OBJECT Window, HRGN hRgn, ULONG Flags, ULONG ExFlags)
+VOID FASTCALL
+IntValidateParent(PWINDOW_OBJECT Child, HRGN ValidRegion)
 {
-  HDC hDC;
-  HWND hWnd = Window->Self;
-  BOOL bIcon = (0 != (Window->Style & WS_MINIMIZE)) &&
-               (0 != IntGetClassLong(Window, GCL_HICON, FALSE));
-
-  if (0 != (ExFlags & RDW_EX_DELAY_NCPAINT) ||
-      PaintHaveToDelayNCPaint(Window, 0))
-    {
-      ExFlags |= RDW_EX_DELAY_NCPAINT;
-    }
-
-  if (Flags & RDW_UPDATENOW)
-    {
-      if (NULL != Window->UpdateRegion)
-       {
-         if (IntIsDesktopWindow(Window))
-           {
-             VIS_RepaintDesktop(Window->Self, Window->UpdateRegion);
-           }
-         else
-           {
-             NtUserSendMessage(hWnd, bIcon ? WM_PAINTICON : WM_PAINT, bIcon, 0);
-           }
-       }
-    }
-  else if (Flags & RDW_ERASENOW || ExFlags & RDW_EX_TOPFRAME)
-    {
-      UINT Dcx = DCX_INTERSECTRGN | DCX_USESTYLE | DCX_KEEPCLIPRGN |
-       DCX_WINDOWPAINT | DCX_CACHE;
-      HRGN hRgnRet;
-
-      hRgnRet =
-       PaintUpdateNCRegion(Window,
-                        hRgn,
-                        UNC_REGION | UNC_CHECK |
-                        ((ExFlags & RDW_EX_TOPFRAME) ? UNC_ENTIRE : 0) |
-                        ((ExFlags & RDW_EX_DELAY_NCPAINT) ?
-                         UNC_DELAY_NCPAINT : 0));
-      if (NULL != hRgnRet)
-       {
-         if ((HRGN) 1 < hRgnRet)
-           {
-             hRgn = hRgnRet;
-           }
-         else
-           {
-             hRgnRet = NULL;
-           }
-         if (0 != (Window->Flags & WINDOWOBJECT_NEED_ERASEBACKGRD))
-           {
-             if (bIcon)
-               {
-                 Dcx |= DCX_WINDOW;
-               }
-             if (NULL != hRgnRet)
-               {
-                 NtGdiOffsetRgn(hRgnRet,
-                               Window->WindowRect.left -
-                               Window->ClientRect.left,
-                               Window->WindowRect.top -
-                               Window->ClientRect.top);
-               }
-             else
-               {
-                 Dcx &= ~DCX_INTERSECTRGN;
-               }
-             if (NULL != (hDC = NtUserGetDCEx(hWnd, hRgnRet, Dcx)))
-               {
-                 if (IntIsDesktopWindow(Window))
-                   {
-                     VIS_RepaintDesktop(Window->Self, Window->UpdateRegion);
-                     NtGdiDeleteObject(Window->UpdateRegion);
-                     Window->UpdateRegion = 0;
-                   }
-                 else
-                   {
-                     if (0 != NtUserSendMessage(hWnd, bIcon ? WM_ICONERASEBKGND :
-                                                WM_ERASEBKGND, (WPARAM)hDC, 0))
-                       {
-                         Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBACKGRD;
-                       }                     
-                   }
-                 NtUserReleaseDC(hWnd, hDC);
-               }
-           }
-       }
-    }
-
-  /* FIXME: Check that the window is still valid at this point. */
-
-  ExFlags &= ~RDW_EX_TOPFRAME;
-
-  /* FIXME: Paint child windows. */
-
-  return(hRgn);
+   PWINDOW_OBJECT ParentWindow = Child->Parent;
+
+   while (ParentWindow)
+   {
+      if (ParentWindow->Style & WS_CLIPCHILDREN)
+         break;      
+
+      if (ParentWindow->UpdateRegion != 0)
+      {
+         NtGdiCombineRgn(ParentWindow->UpdateRegion, ParentWindow->UpdateRegion,
+            ValidRegion, RGN_DIFF);
+         /* FIXME: If the resulting region is empty, remove fake posted paint message */
+      }
+
+      ParentWindow = ParentWindow->Parent;
+   }
 }
 
-VOID STATIC FASTCALL
-PaintUpdateInternalPaint(PWINDOW_OBJECT Window, ULONG Flags)
+/**
+ * @name IntCalcWindowRgn
+ *
+ * Get a window or client region.
+ */
+
+HRGN FASTCALL
+IntCalcWindowRgn(PWINDOW_OBJECT Window, BOOL Client)
 {
-  if (Flags & RDW_INTERNALPAINT)
-    {
-      if (Window->UpdateRegion == NULL &&
-         !(Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT))
-       {
-         MsqIncPaintCountQueue(Window->MessageQueue);
-       }
-      Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
-    }
-  else if (Flags & RDW_NOINTERNALPAINT)
-    {
-      if (Window->UpdateRegion == NULL &&
-         (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT))
-       {
-         MsqDecPaintCountQueue(Window->MessageQueue);
-       }
-      Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
-    }
+   HRGN hRgnWindow;
+   UINT RgnType;
+
+   if (Client)
+      hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
+   else
+      hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
+
+   if (Window->WindowRegion != NULL && !(Window->Style & WS_MINIMIZE))
+   {
+      NtGdiOffsetRgn(hRgnWindow,
+         -Window->WindowRect.left,
+         -Window->WindowRect.top);
+      RgnType = NtGdiCombineRgn(hRgnWindow, hRgnWindow, Window->WindowRegion, RGN_AND);
+      NtGdiOffsetRgn(hRgnWindow,
+         Window->WindowRect.left,
+         Window->WindowRect.top);
+   }
+
+   return hRgnWindow;
 }
 
-VOID STATIC FASTCALL
-PaintValidateParent(PWINDOW_OBJECT Child)
+/**
+ * @name IntGetNCUpdateRgn
+ *
+ * Get non-client update region of a window and optionally validate it.
+ *
+ * @param Window
+ *        Pointer to window to get the NC update region from.
+ * @param Validate
+ *        Set to TRUE to force validating the NC update region.
+ *
+ * @return
+ *    Handle to NC update region. The caller is responsible for deleting
+ *    it.
+ */
+
+HRGN FASTCALL
+IntGetNCUpdateRgn(PWINDOW_OBJECT Window, BOOL Validate)
 {
-  HWND DesktopHandle = IntGetDesktopWindow();
-  PWINDOW_OBJECT Parent = Child->Parent;
-  PWINDOW_OBJECT Desktop = IntGetWindowObject(DesktopHandle);
-  HRGN hRgn;
-
-  if ((HRGN) 1 == Child->UpdateRegion)
-    {
-      RECT Rect;
-
-      Rect.left = Rect.top = 0;
-      Rect.right = Child->WindowRect.right - Child->WindowRect.left;
-      Rect.bottom = Child->WindowRect.bottom - Child->WindowRect.top;
-
-      hRgn = UnsafeIntCreateRectRgnIndirect(&Rect);
-    }
-  else
-    {
-      hRgn = Child->UpdateRegion;
-    }
-
-  while (NULL != Parent && Parent != Desktop)
-    {
-      if (0 == (Parent->Style & WS_CLIPCHILDREN))
-       {
-         if (NULL != Parent->UpdateRegion)
-           {
-             POINT Offset;
-
-             if ((HRGN) 1 == Parent->UpdateRegion)
-               {
-                 RECT Rect1;
-
-                 Rect1.left = Rect1.top = 0;
-                 Rect1.right = Parent->WindowRect.right - 
-                   Parent->WindowRect.left;
-                 Rect1.bottom = Parent->WindowRect.bottom -
-                   Parent->WindowRect.top;
-
-                 Parent->UpdateRegion = 
-                   UnsafeIntCreateRectRgnIndirect(&Rect1);
-               }
-             Offset.x = Child->WindowRect.left - Parent->WindowRect.left;
-             Offset.y = Child->WindowRect.top - Parent->WindowRect.top;
-             NtGdiOffsetRgn(hRgn, Offset.x, Offset.y);
-             NtGdiCombineRgn(Parent->UpdateRegion, Parent->UpdateRegion, hRgn,
-                            RGN_DIFF);
-             NtGdiOffsetRgn(hRgn, -Offset.x, -Offset.y);
-           }
-       }
-      Parent = Parent->Parent;
-    }
-  if (hRgn != Child->UpdateRegion)
-    {
-      NtGdiDeleteObject(Child->UpdateRegion);
-    }
-  IntReleaseWindowObject(Desktop);
+   HRGN hRgnNonClient;
+   HRGN hRgnWindow;
+   UINT RgnType;
+   
+   if (Window->UpdateRegion != NULL &&
+       Window->UpdateRegion != (HRGN)1)
+   {
+      hRgnNonClient = NtGdiCreateRectRgn(0, 0, 0, 0);
+      hRgnWindow = IntCalcWindowRgn(Window, TRUE);
+
+      /*
+       * If region creation fails it's safe to fallback to whole
+       * window region.
+       */
+
+      if (hRgnNonClient == NULL)
+      {
+         return (HRGN)1;
+      }
+
+      RgnType = NtGdiCombineRgn(hRgnNonClient, Window->UpdateRegion,
+                                hRgnWindow, RGN_DIFF);
+      if (RgnType == ERROR)
+      {
+         NtGdiDeleteObject(hRgnNonClient);
+         return (HRGN)1;
+      }
+      else if (RgnType == NULLREGION)
+      {
+         NtGdiDeleteObject(hRgnNonClient);
+         return NULL;
+      }
+
+      /*
+       * Remove the nonclient region from the standard update region if
+       * we were asked for it.
+       */
+
+      if (Validate)
+      {
+         if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
+                             hRgnWindow, RGN_AND) == NULLREGION)
+         {
+            GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
+            NtGdiDeleteObject(Window->UpdateRegion);
+            Window->UpdateRegion = NULL;
+            if (!(Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT))
+               MsqDecPaintCountQueue(Window->MessageQueue);
+         }
+      }
+
+      NtGdiDeleteObject(hRgnWindow);
+
+      return hRgnNonClient;
+   }
+   else
+   {
+      return Window->UpdateRegion;
+   }
 }
 
-VOID STATIC STDCALL
-PaintUpdateRgns(PWINDOW_OBJECT Window, HRGN hRgn, ULONG Flags,
-               BOOL First)
+/*
+ * IntPaintWindows
+ *
+ * Internal function used by IntRedrawWindow.
+ */
+
+STATIC VOID FASTCALL
+co_IntPaintWindows(PWINDOW_OBJECT Window, ULONG Flags)
 {
-  /*
-   * Called only when one of the following is set:
-   * (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT)
-   */
-
-  BOOL HadOne = NULL != Window->UpdateRegion && NULL != hRgn;
-  BOOL HasChildren = Window->FirstChild &&
-    !(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
-    ((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN));
-  RECT Rect;
-
-  Rect.left = Rect.top = 0;
-  Rect.right = Window->WindowRect.right - Window->WindowRect.left;
-  Rect.bottom = Window->WindowRect.bottom - Window->WindowRect.top;
-
-  if (Flags & RDW_INVALIDATE)
-    {
-      if ((HRGN) 1 < hRgn)
-       {
-         if ((HRGN) 1 != Window->UpdateRegion)
-           {
-             if ((HRGN) 1 < Window->UpdateRegion)
-               {
-                 NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
-                                hRgn, RGN_OR);
-               }
-             Window->UpdateRegion =
-               REGION_CropRgn(Window->UpdateRegion,
-                              Window->UpdateRegion ? Window->UpdateRegion : hRgn,
-                              &Rect, NULL);
-             if (! HadOne)
-               {
-                 UnsafeIntGetRgnBox(Window->UpdateRegion, &Rect);
-                 if (NtGdiIsEmptyRect(&Rect))
-                   {
-                     NtGdiDeleteObject(Window->UpdateRegion);
-                     Window->UpdateRegion = NULL;
-                     PaintUpdateInternalPaint(Window, Flags);
-                     return;
-                   }
-               }
-           }
-       }
-      else if ((HRGN) 1 == hRgn)
-       {
-         if ((HRGN) 1 < Window->UpdateRegion)
-           {
-             NtGdiDeleteObject(Window->UpdateRegion);
-           }
-         Window->UpdateRegion = (HRGN) 1;
-       }
+   HDC hDC;
+   HWND hWnd = Window->hSelf;
+   HRGN TempRegion;
+
+   if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
+   {
+      if (Window->UpdateRegion)
+      {
+         IntValidateParent(Window, Window->UpdateRegion);
+      }
+
+      if (Flags & RDW_UPDATENOW)
+      {
+         if (Window->UpdateRegion != NULL ||
+             Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
+         {
+            co_IntSendMessage(hWnd, WM_PAINT, 0, 0);
+         }
+      }
       else
-       {
-         hRgn = Window->UpdateRegion; /* this is a trick that depends on code in PaintRedrawWindow() */
-       }
+      {
+         if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
+         {
+            TempRegion = IntGetNCUpdateRgn(Window, TRUE);
+            Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
+            MsqDecPaintCountQueue(Window->MessageQueue);
+            co_IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)TempRegion, 0);
+            if ((HANDLE) 1 != TempRegion && NULL != TempRegion)
+            {
+               /* NOTE: The region can already be deleted! */
+               GDIOBJ_FreeObj(TempRegion, GDI_OBJECT_TYPE_REGION | GDI_OBJECT_TYPE_SILENT);
+            }
+         }
+
+         if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
+         {
+            if (Window->UpdateRegion)
+            {
+               hDC = UserGetDCEx(Window, Window->UpdateRegion,
+                                 DCX_CACHE | DCX_USESTYLE |
+                                 DCX_INTERSECTRGN | DCX_KEEPCLIPRGN);
+               if (co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)hDC, 0))
+               {
+                  Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
+               }
+               UserReleaseDC(Window, hDC, FALSE);
+            }
+         }
+      }
+   }
+
+   /*
+    * Check that the window is still valid at this point
+    */
+   if (!IntIsWindow(hWnd))
+   {
+      return;
+   }
+
+   /*
+    * Paint child windows.
+    */
+   if (!(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
+       ((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN)))
+   {
+      HWND *List, *phWnd;
+
+      if ((List = IntWinListChildren(Window)))
+      {
+         for (phWnd = List; *phWnd; ++phWnd)
+         {
+            Window = UserGetWindowObject(*phWnd);
+            if (Window && (Window->Style & WS_VISIBLE))
+            {
+               USER_REFERENCE_ENTRY Ref;
+               UserRefObjectCo(Window, &Ref);
+               co_IntPaintWindows(Window, Flags);
+               UserDerefObjectCo(Window);
+            }
+         }
+         ExFreePool(List);
+      }
+   }
+}
 
-      if (! HadOne && 0 == (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT) &&
-         !IntIsDesktopWindow(Window))
-       {
-         MsqIncPaintCountQueue(Window->MessageQueue);
-       }
+/*
+ * IntInvalidateWindows
+ *
+ * Internal function used by IntRedrawWindow.
+ */
+
+VOID FASTCALL
+IntInvalidateWindows(PWINDOW_OBJECT Window, HRGN hRgn, ULONG Flags)
+{
+   INT RgnType;
+   BOOL HadPaintMessage, HadNCPaintMessage;
+   BOOL HasPaintMessage, HasNCPaintMessage;
+
+   /*
+    * Clip the given region with window rectangle (or region)
+    */
+
+   if (!Window->WindowRegion || (Window->Style & WS_MINIMIZE))
+   {
+      HRGN hRgnWindow;
+
+      hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
+      RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnWindow, RGN_AND);
+      NtGdiDeleteObject(hRgnWindow);
+   }
+   else
+   {
+      NtGdiOffsetRgn(hRgn,
+         -Window->WindowRect.left,
+         -Window->WindowRect.top);
+      RgnType = NtGdiCombineRgn(hRgn, hRgn, Window->WindowRegion, RGN_AND);
+      NtGdiOffsetRgn(hRgn,
+         Window->WindowRect.left,
+         Window->WindowRect.top);
+   }
+
+   /*
+    * Save current state of pending updates
+    */
+
+   HadPaintMessage = Window->UpdateRegion != NULL ||
+                     Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
+   HadNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
+
+   /*
+    * Update the region and flags
+    */
+
+   if (Flags & RDW_INVALIDATE && RgnType != NULLREGION)
+   {
+      if (Window->UpdateRegion == NULL)
+      {
+         Window->UpdateRegion = NtGdiCreateRectRgn(0, 0, 0, 0);
+         GDIOBJ_SetOwnership(Window->UpdateRegion, NULL);
+      }
+
+      if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
+                          hRgn, RGN_OR) == NULLREGION)
+      {
+         GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
+         NtGdiDeleteObject(Window->UpdateRegion);
+         Window->UpdateRegion = NULL;
+      }
 
       if (Flags & RDW_FRAME)
-       {
-         Window->Flags |= WINDOWOBJECT_NEED_NCPAINT;
-       }
+         Window->Flags |= WINDOWOBJECT_NEED_NCPAINT;
       if (Flags & RDW_ERASE)
-       {
-         Window->Flags |= WINDOWOBJECT_NEED_ERASEBACKGRD;
-       }
+         Window->Flags |= WINDOWOBJECT_NEED_ERASEBKGND;
+
       Flags |= RDW_FRAME;
-    }
-  else if (Flags & RDW_VALIDATE)
-    {
-      if (NULL != Window->UpdateRegion)
-       {
-         if ((HRGN) 1 < hRgn)
-           {
-             if ((HRGN) 1 == Window->UpdateRegion)
-               {
-                 /* If no NCPAINT needed or if we're going to turn it off
-                    the special value 1 means the whole client rect */
-                 if (0 == (Window->Flags & WINDOWOBJECT_NEED_NCPAINT) ||
-                     0 != (Flags & RDW_NOFRAME))
-                   {
-                     Rect.left = Window->ClientRect.left - Window->WindowRect.left;
-                     Rect.top = Window->ClientRect.top - Window->WindowRect.top;
-                     Rect.right = Window->ClientRect.right - Window->WindowRect.left;
-                     Rect.bottom = Window->ClientRect.bottom - Window->WindowRect.top;
-                   }
-                 Window->UpdateRegion = 
-                   UnsafeIntCreateRectRgnIndirect(&Rect);
-               }
-             if (NtGdiCombineRgn(Window->UpdateRegion, 
-                                Window->UpdateRegion, hRgn, 
-                                RGN_DIFF) == NULLREGION)
-               {
-                 NtGdiDeleteObject(Window->UpdateRegion);
-                 Window->UpdateRegion = NULL;
-               }
-           }
-         else /* validate everything */
-           {
-             if ((HRGN) 1 < Window->UpdateRegion)
-               {
-                 NtGdiDeleteObject(Window->UpdateRegion);
-               }
-             Window->UpdateRegion = NULL;
-           }
-
-         if (NULL != Window->UpdateRegion)
-           {
-             Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBACKGRD;
-             if (0 != (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT))
-               {
-                 MsqDecPaintCountQueue(Window->MessageQueue);
-               }
-           }
-       }
+   }
+
+   if (Flags & RDW_VALIDATE && RgnType != NULLREGION)
+   {
+      if (Window->UpdateRegion != NULL)
+      {
+         if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
+                             hRgn, RGN_DIFF) == NULLREGION)
+         {
+            GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
+            NtGdiDeleteObject(Window->UpdateRegion);
+            Window->UpdateRegion = NULL;
+         }
+      }
 
+      if (Window->UpdateRegion == NULL)
+         Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
       if (Flags & RDW_NOFRAME)
-       {
-         Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
-       }
+         Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
       if (Flags & RDW_NOERASE)
-       {
-         Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBACKGRD;
-       }
-    }
-
-  if (First && NULL != Window->UpdateRegion && 0 != (Flags & RDW_UPDATENOW))
-    {
-      PaintValidateParent(Window); /* validate parent covered by region */
-    }
-
-  /* in/validate child windows that intersect with the region if it
-   * is a valid handle. */
-
-  if (0 != (Flags & (RDW_INVALIDATE | RDW_VALIDATE)))
-    {
-      if ((HRGN) 1 < hRgn && HasChildren)
-       {
-         POINT Total = {0, 0};
-         POINT PrevOrign = {0, 0};
-         PWINDOW_OBJECT Child;
-
-         ExAcquireFastMutexUnsafe(&Window->ChildrenListLock);
-         Child = Window->FirstChild;
-         while (Child)
-           {
-             if (0 != (Child->Style & WS_VISIBLE))
-               {
-                 POINT Offset;
-
-                 Rect.left = Child->WindowRect.left - Window->WindowRect.left;
-                 Rect.top = Child->WindowRect.top - Window->WindowRect.top;
-                 Rect.right = Child->WindowRect.right - Window->WindowRect.left;
-                 Rect.bottom = Child->WindowRect.bottom - Window->WindowRect.top;
-
-                 Offset.x = Rect.left - PrevOrign.x;
-                 Offset.y = Rect.top - PrevOrign.y;
-                 NtGdiOffsetRect(&Rect, -Total.x, -Total.y);
-
-                 if (UnsafeIntRectInRegion(hRgn, &Rect))
-                   {
-                     NtGdiOffsetRgn(hRgn, -Offset.x, -Offset.y);
-                     PaintUpdateRgns(Child, hRgn, Flags, FALSE);
-                     PrevOrign.x = Rect.left + Total.x;
-                     PrevOrign.y = Rect.top + Total.y;
-                     Total.x += Offset.x;
-                     Total.y += Offset.y;
-                   }
-               }
-             Child = Child->NextSibling;
-           }
-         ExReleaseFastMutexUnsafe(&Window->ChildrenListLock);
-
-         NtGdiOffsetRgn(hRgn, Total.x, Total.y);
-         HasChildren = FALSE;
-       }
-    }
-
-  if (HasChildren)
-  {
-    PWINDOW_OBJECT Child;
-
-    ExAcquireFastMutexUnsafe(&Window->ChildrenListLock);
-    Child = Window->FirstChild;
-    while (Child)
-    {
-      if (Child->Style & WS_VISIBLE)
+         Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
+   }
+
+   if (Flags & RDW_INTERNALPAINT)
+   {
+      Window->Flags |= WINDOWOBJECT_NEED_INTERNALPAINT;
+   }
+
+   if (Flags & RDW_NOINTERNALPAINT)
+   {
+      Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
+   }
+
+   /*
+    * Process children if needed
+    */
+
+   if (!(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
+         ((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN)))
+   {
+      PWINDOW_OBJECT Child;
+
+      for (Child = Window->FirstChild; Child; Child = Child->NextSibling)
       {
-        PaintUpdateRgns(Child, hRgn, Flags, FALSE);
+         if (Child->Style & WS_VISIBLE)
+         {
+            /*
+             * Recursive call to update children UpdateRegion
+             */
+            HRGN hRgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
+            NtGdiCombineRgn(hRgnTemp, hRgn, 0, RGN_COPY);
+            IntInvalidateWindows(Child, hRgnTemp, Flags);
+            NtGdiDeleteObject(hRgnTemp);
+         }
+
       }
-      Child = Child->NextSibling;
-    }
-    ExReleaseFastMutexUnsafe(&Window->ChildrenListLock);
-  }
+   }
+
+   /*
+    * Fake post paint messages to window message queue if needed
+    */
+
+   HasPaintMessage = Window->UpdateRegion != NULL ||
+                     Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
+   HasNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
+
+   if (HasPaintMessage != HadPaintMessage)
+   {
+      if (HadPaintMessage)
+         MsqDecPaintCountQueue(Window->MessageQueue);
+      else
+         MsqIncPaintCountQueue(Window->MessageQueue);
+   }
+
+   if (HasNCPaintMessage != HadNCPaintMessage)
+   {
+      if (HadNCPaintMessage)
+         MsqDecPaintCountQueue(Window->MessageQueue);
+      else
+         MsqIncPaintCountQueue(Window->MessageQueue);
+   }
 
-  PaintUpdateInternalPaint(Window, Flags);
 }
 
-BOOL STDCALL
-PaintRedrawWindow( PWINDOW_OBJECT Window, 
-                  const RECT* UpdateRect, 
-                  HRGN UpdateRgn,
-                  ULONG Flags, 
-                  ULONG ExFlags)
-{
-  RECT Rect, Rect2;
-  POINT Pt;
-  HRGN hRgn = NULL;
-
-  DPRINT("[win32k.sys:painting] In PaintRedrawWindow()\n");
-
-  if ((RDW_INVALIDATE | RDW_FRAME) == (Flags & (RDW_INVALIDATE | RDW_FRAME)) ||
-      (RDW_VALIDATE | RDW_NOFRAME) == (Flags & (RDW_VALIDATE | RDW_NOFRAME)))
-    {
-      Rect = Window->WindowRect;
-    }
-  else
-    {
-      Rect = Window->ClientRect;
-    }
-
-  if (ExFlags & RDW_EX_XYWINDOW)
-    {
-      Pt.x = Pt.y = 0;
-      NtGdiOffsetRect(&Rect, -Window->WindowRect.left, -Window->WindowRect.top);
-    }
-  else
-    {
-      Pt.x = Window->ClientRect.left - Window->WindowRect.left;
-      Pt.y = Window->ClientRect.top - Window->WindowRect.top;
-      NtGdiOffsetRect(&Rect, -Window->ClientRect.left, -Window->ClientRect.top);
-    }
-
-  if (0 != (Flags & RDW_INVALIDATE))  /* ------------------------- Invalidate */
-    {
-      if (NULL != UpdateRgn)
-       {
-         if (NULL != Window->UpdateRegion)
-           {
-             hRgn = REGION_CropRgn(NULL, UpdateRgn, NULL, &Pt);
-           }
-         else
-           {
-             Window->UpdateRegion = REGION_CropRgn(NULL, UpdateRgn, &Rect, &Pt);
-           }
-       }
-      else if (NULL != UpdateRect)
-       {
-         if (! NtGdiIntersectRect(&Rect2, &Rect, UpdateRect))
-           {
-             
-             if ((HRGN) 1 < hRgn && hRgn != UpdateRgn)
-               {
-                 NtGdiDeleteObject(hRgn);
-               }
-             return TRUE;
-           }
-         NtGdiOffsetRect(&Rect2, Pt.x, Pt.y);
-         if (NULL == Window->UpdateRegion)
-           {
-             Window->UpdateRegion =
-               UnsafeIntCreateRectRgnIndirect(&Rect2);
-           }
-         else
-           {
-             hRgn = UnsafeIntCreateRectRgnIndirect(&Rect2);
-           }
-       }
-      else /* entire window or client depending on RDW_FRAME */
-       {
-         if (Flags & RDW_FRAME)
-           {
-             if (NULL != Window->UpdateRegion)
-               {
-                 hRgn = (HRGN) 1;
-               }
-             else
-               {
-               Window->UpdateRegion = (HRGN) 1;
-               }
-           }
-         else
-           {
-             GETCLIENTRECTW(Window, Rect2);
-             if (NULL == Window->UpdateRegion)
-               {
-                 Window->UpdateRegion = UnsafeIntCreateRectRgnIndirect(&Rect2);
-               }
-             else
-               {
-                 hRgn = UnsafeIntCreateRectRgnIndirect(&Rect2);
-               }
-           }
-       }
-    }
-  else if (Flags & RDW_VALIDATE)
-    {
-      /* In this we cannot leave with zero hRgn */
-      if (NULL != UpdateRgn)
-       {
-         hRgn = REGION_CropRgn(hRgn, UpdateRgn,  &Rect, &Pt);
-         UnsafeIntGetRgnBox(hRgn, &Rect2);
-         if (NtGdiIsEmptyRect(&Rect2))
-           {
-             
-             if ((HRGN) 1 < hRgn && hRgn != UpdateRgn)
-               {
-                 NtGdiDeleteObject(hRgn);
-               }
-             return TRUE;
-           }
-       }
-      else if (NULL != UpdateRect)
-       {
-         if (! NtGdiIntersectRect(&Rect2, &Rect, UpdateRect))
-           {
-             
-             if ((HRGN) 1 < hRgn && hRgn != UpdateRgn)
-               {
-                 NtGdiDeleteObject(hRgn);
-               }
-             return TRUE;
-           }
-         NtGdiOffsetRect(&Rect2, Pt.x, Pt.y);
-         hRgn = UnsafeIntCreateRectRgnIndirect(&Rect2);
-       }
-      else /* entire window or client depending on RDW_NOFRAME */
-        {
-         if (0 != (Flags & RDW_NOFRAME))
-           {
-             hRgn = (HRGN) 1;
-           }
-         else
-           {
-             GETCLIENTRECTW(Window, Rect2);
-             hRgn = UnsafeIntCreateRectRgnIndirect(&Rect2);
-            }
-        }
-    }
+/*
+ * IntIsWindowDrawable
+ *
+ * Remarks
+ *    Window is drawable when it is visible and all parents are not
+ *    minimized.
+ */
 
-  /* At this point hRgn is either an update region in window coordinates or 1 or 0 */
+BOOL FASTCALL
+IntIsWindowDrawable(PWINDOW_OBJECT Window)
+{
+   PWINDOW_OBJECT Wnd;
 
-  PaintUpdateRgns(Window, hRgn, Flags, TRUE);
+   for (Wnd = Window; Wnd != NULL; Wnd = Wnd->Parent)
+   {
+      if (!(Wnd->Style & WS_VISIBLE) ||
+            ((Wnd->Style & WS_MINIMIZE) && (Wnd != Window)))
+      {
+         return FALSE;
+      }
+   }
 
-  /* Erase/update windows, from now on hRgn is a scratch region */
+   return TRUE;
+}
 
-  hRgn = PaintDoPaint(Window, (HRGN) 1 == hRgn ? NULL : hRgn, Flags, ExFlags);
+/*
+ * IntRedrawWindow
+ *
+ * Internal version of NtUserRedrawWindow that takes WINDOW_OBJECT as
+ * first parameter.
+ */
 
-  if ((HRGN) 1 < hRgn && hRgn != UpdateRgn)
-    {
+BOOL FASTCALL
+co_UserRedrawWindow(PWINDOW_OBJECT Window, const RECT* UpdateRect, HRGN UpdateRgn,
+                    ULONG Flags)
+{
+   HRGN hRgn = NULL;
+
+   /*
+    * Step 1.
+    * Validation of passed parameters.
+    */
+
+   if (!IntIsWindowDrawable(Window) ||
+         (Flags & (RDW_VALIDATE | RDW_INVALIDATE)) ==
+         (RDW_VALIDATE | RDW_INVALIDATE))
+   {
+      return FALSE;
+   }
+
+   /*
+    * Step 2.
+    * Transform the parameters UpdateRgn and UpdateRect into
+    * a region hRgn specified in screen coordinates.
+    */
+
+   if (Flags & (RDW_INVALIDATE | RDW_VALIDATE))
+   {
+      if (UpdateRgn != NULL)
+      {
+         hRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
+         if (NtGdiCombineRgn(hRgn, UpdateRgn, NULL, RGN_COPY) == NULLREGION)
+            NtGdiDeleteObject(hRgn);
+         else
+            NtGdiOffsetRgn(hRgn, Window->ClientRect.left, Window->ClientRect.top);
+      }
+      else if (UpdateRect != NULL)
+      {
+         if (!IntGdiIsEmptyRect(UpdateRect))
+         {
+            hRgn = UnsafeIntCreateRectRgnIndirect((RECT *)UpdateRect);
+            NtGdiOffsetRgn(hRgn, Window->ClientRect.left, Window->ClientRect.top);
+         }
+      }
+      else if ((Flags & (RDW_INVALIDATE | RDW_FRAME)) == (RDW_INVALIDATE | RDW_FRAME) ||
+               (Flags & (RDW_VALIDATE | RDW_NOFRAME)) == (RDW_VALIDATE | RDW_NOFRAME))
+      {
+         if (!IntGdiIsEmptyRect(&Window->WindowRect))
+            hRgn = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
+      }
+      else
+      {
+         if (!IntGdiIsEmptyRect(&Window->ClientRect))
+            hRgn = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
+      }
+   }
+
+   /*
+    * Step 3.
+    * Adjust the window update region depending on hRgn and flags.
+    */
+
+   if (Flags & (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT) &&
+       hRgn != NULL)
+   {
+      IntInvalidateWindows(Window, hRgn, Flags);
+   }
+
+   /*
+    * Step 4.
+    * Repaint and erase windows if needed.
+    */
+
+   if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
+   {
+      co_IntPaintWindows(Window, Flags);
+   }
+
+   /*
+    * Step 5.
+    * Cleanup ;-)
+    */
+
+   if (hRgn != NULL)
+   {
       NtGdiDeleteObject(hRgn);
-    }
+   }
 
-  return TRUE;
+   return TRUE;
 }
 
-BOOL STDCALL
-PaintHaveToDelayNCPaint(PWINDOW_OBJECT Window, ULONG Flags)
+BOOL FASTCALL
+IntIsWindowDirty(PWINDOW_OBJECT Window)
 {
-  if (Flags & UNC_DELAY_NCPAINT)
-    {
-      return(TRUE);
-    }
-
-  if (Flags & UNC_IN_BEGINPAINT)
-    {
-      return(FALSE);
-    }
-
-  Window = Window->Parent;
-  while (Window != NULL)
-    {
-      if (Window->Style & WS_CLIPCHILDREN && Window->UpdateRegion != NULL)
-       {
-         return TRUE;
-       }
-      Window = Window->Parent;
-    }
-
-  return FALSE;
+   return (Window->Style & WS_VISIBLE) &&
+          ((Window->UpdateRegion != NULL) ||
+           (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT) ||
+           (Window->Flags & WINDOWOBJECT_NEED_NCPAINT));
 }
 
-HWND STDCALL
-PaintingFindWinToRepaint(HWND hWnd, PW32THREAD Thread)
+HWND FASTCALL
+IntFindWindowToRepaint(PWINDOW_OBJECT Window, PW32THREAD Thread)
 {
-  PWINDOW_OBJECT Window;
-  PWINDOW_OBJECT BaseWindow;
-  PLIST_ENTRY current_entry;
-  HWND hFoundWnd = NULL;
-
-  if (hWnd == NULL)
-    {
-      ExAcquireFastMutex(&Thread->WindowListLock);
-      current_entry = Thread->WindowListHead.Flink;
-      while (current_entry != &Thread->WindowListHead)
-       {
-         Window = CONTAINING_RECORD(current_entry, WINDOW_OBJECT,
-                                    ThreadListEntry);
-         if (Window->Style & WS_VISIBLE)
-           {
-             hFoundWnd = 
-               PaintingFindWinToRepaint(Window->Self, Thread);
-             if (hFoundWnd != NULL)
-               {
-                 ExReleaseFastMutex(&Thread->WindowListLock);
-                 return(hFoundWnd);
-               }
-           }
-         current_entry = current_entry->Flink;
-       }
-      ExReleaseFastMutex(&Thread->WindowListLock);
-      return(NULL);
-    }
-
-  BaseWindow = IntGetWindowObject(hWnd);
-  if (BaseWindow == NULL)
-    {
-      return(NULL);
-    }
-  if (BaseWindow->UpdateRegion != NULL ||
-      BaseWindow->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
-    {
-      IntReleaseWindowObject(BaseWindow);
-      return(hWnd);
-    }
-
-  ExAcquireFastMutex(&BaseWindow->ChildrenListLock);
-  Window = BaseWindow->FirstChild;
-  while (Window)
-  {
-    if (Window->Style & WS_VISIBLE)
-    {
-      hFoundWnd = PaintingFindWinToRepaint(Window->Self, Thread);
-      if (hFoundWnd != NULL)
+   HWND hChild;
+   PWINDOW_OBJECT TempWindow;
+
+   for (; Window != NULL; Window = Window->NextSibling)
+   {
+      if (IntWndBelongsToThread(Window, Thread) &&
+          IntIsWindowDirty(Window))
+      {
+         /* Make sure all non-transparent siblings are already drawn. */
+         if (Window->ExStyle & WS_EX_TRANSPARENT)
+         {
+            for (TempWindow = Window->NextSibling; TempWindow != NULL;
+                 TempWindow = TempWindow->NextSibling)
+            {
+               if (!(TempWindow->ExStyle & WS_EX_TRANSPARENT) &&
+                   IntWndBelongsToThread(TempWindow, Thread) &&
+                   IntIsWindowDirty(TempWindow))
+               {
+                  return TempWindow->hSelf;
+               }
+            }
+         }
+
+         return Window->hSelf;
+      }
+
+      if (Window->FirstChild)
       {
-        break;
+         hChild = IntFindWindowToRepaint(Window->FirstChild, Thread);
+         if (hChild != NULL)
+            return hChild;
       }
-    }
-    Window = Window->NextSibling;
-  }
-  ExReleaseFastMutex(&BaseWindow->ChildrenListLock);
+   }
 
-  IntReleaseWindowObject(BaseWindow);
-  return(hFoundWnd);
+   return NULL;
 }
 
-HRGN STDCALL
-PaintUpdateNCRegion(PWINDOW_OBJECT Window, HRGN hRgn, ULONG Flags)
+BOOL FASTCALL
+IntGetPaintMessage(HWND hWnd, UINT MsgFilterMin, UINT MsgFilterMax,
+                   PW32THREAD Thread, MSG *Message, BOOL Remove)
 {
-  HRGN hRgnRet;
-  RECT ClientRect;
-  HRGN hClip = NULL;
+   PUSER_MESSAGE_QUEUE MessageQueue = (PUSER_MESSAGE_QUEUE)Thread->MessageQueue;
 
-  /* Desktop has no parent. */
-  if (Window->Parent == NULL)
-    {
-      Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
-      if ((HRGN) 1 < Window->UpdateRegion)
-       {
-         hRgnRet = REGION_CropRgn(hRgn, Window->UpdateRegion, NULL, NULL);
-       }
-      else
-       {
-         hRgnRet = Window->UpdateRegion;
-       }
-      return(hRgnRet);
-    }
-
-#if 0 /* NtUserGetFOregroundWindow() not implemented yet */
-  if ((Window->Self == NtUserGetForegroundWindow()) &&
-      0 == (Window->Flags & WIN_NCACTIVATED) )
-    {
-      Window->Flags |= WIN_NCACTIVATED;
-      Flags |= UNC_ENTIRE;
-    }
-#endif
-
-    /*
-     * If the window's non-client area needs to be painted,
-     */
-  if (0 != (Window->Flags & WINDOWOBJECT_NEED_NCPAINT) &&
-      ! PaintHaveToDelayNCPaint(Window, Flags))
-    {
-      RECT UpdateRegionBox;
-      RECT Rect;
+   if (!MessageQueue->PaintCount)
+      return FALSE;
+
+   if ((MsgFilterMin != 0 || MsgFilterMax != 0) &&
+         (MsgFilterMin > WM_PAINT || MsgFilterMax < WM_PAINT))
+      return FALSE;
+
+   Message->hwnd = IntFindWindowToRepaint(UserGetDesktopWindow(), PsGetWin32Thread());
+
+   if (Message->hwnd == NULL)
+   {
+      DPRINT1("PAINTING BUG: Thread marked as containing dirty windows, but no dirty windows found!\n");
+      return FALSE;
+   }
+
+   if (hWnd != NULL && Message->hwnd != hWnd)
+      return FALSE;
+
+   Message->message = WM_PAINT;
+   Message->wParam = Message->lParam = 0;
+
+   return TRUE;
+}
+
+static
+HWND FASTCALL
+co_IntFixCaret(PWINDOW_OBJECT Window, LPRECT lprc, UINT flags)
+{
+   PDESKTOP_OBJECT Desktop;
+   PTHRDCARETINFO CaretInfo;
+   HWND hWndCaret;
+   PWINDOW_OBJECT WndCaret;
+
+   ASSERT_REFS_CO(Window);
+
+   Desktop = ((PW32THREAD)PsGetCurrentThread()->Tcb.Win32Thread)->Desktop;
+   CaretInfo = ((PUSER_MESSAGE_QUEUE)Desktop->ActiveMessageQueue)->CaretInfo;
+   hWndCaret = CaretInfo->hWnd;
+
+   WndCaret = UserGetWindowObject(hWndCaret);
+
+   //fix: check for WndCaret can be null
+   if (WndCaret == Window ||
+         ((flags & SW_SCROLLCHILDREN) && IntIsChildWindow(Window, WndCaret)))
+   {
+      POINT pt, FromOffset, ToOffset, Offset;
+      RECT rcCaret;
+
+      pt.x = CaretInfo->Pos.x;
+      pt.y = CaretInfo->Pos.y;
+      IntGetClientOrigin(WndCaret, &FromOffset);
+      IntGetClientOrigin(Window, &ToOffset);
+      Offset.x = FromOffset.x - ToOffset.x;
+      Offset.y = FromOffset.y - ToOffset.y;
+      rcCaret.left = pt.x;
+      rcCaret.top = pt.y;
+      rcCaret.right = pt.x + CaretInfo->Size.cx;
+      rcCaret.bottom = pt.y + CaretInfo->Size.cy;
+      if (IntGdiIntersectRect(lprc, lprc, &rcCaret))
+      {
+         co_UserHideCaret(0);
+         lprc->left = pt.x;
+         lprc->top = pt.y;
+         return hWndCaret;
+      }
+   }
+
+   return 0;
+}
+
+/* PUBLIC FUNCTIONS ***********************************************************/
 
+/*
+ * NtUserBeginPaint
+ *
+ * Status
+ *    @implemented
+ */
+
+HDC STDCALL
+NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* UnsafePs)
+{
+   PWINDOW_OBJECT Window = NULL;
+   PAINTSTRUCT Ps;
+   PROSRGNDATA Rgn;
+   NTSTATUS Status;
+   DECLARE_RETURN(HDC);
+   USER_REFERENCE_ENTRY Ref;
+
+   DPRINT("Enter NtUserBeginPaint\n");
+   UserEnterExclusive();
+
+   if (!(Window = UserGetWindowObject(hWnd)))
+   {
+      RETURN( NULL);
+   }
+
+   UserRefObjectCo(Window, &Ref);
+   
+   co_UserHideCaret(Window);
+
+   if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
+   {
+      HRGN hRgn;
+
+      hRgn = IntGetNCUpdateRgn(Window, FALSE);
       Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
-      GETCLIENTRECTW(Window, ClientRect);
-
-      if ((HRGN) 1 < Window->UpdateRegion)
-       {
-         UnsafeIntGetRgnBox(Window->UpdateRegion, &UpdateRegionBox);
-         NtGdiUnionRect(&Rect, &ClientRect, &UpdateRegionBox);
-         if (Rect.left != ClientRect.left || Rect.top != ClientRect.top ||
-             Rect.right != ClientRect.right || Rect.bottom != ClientRect.bottom)
-           {
-             hClip = Window->UpdateRegion;
-             Window->UpdateRegion = REGION_CropRgn(hRgn, hClip,
-                                                   &ClientRect, NULL);
-             if (Flags & UNC_REGION)
-               {
-                 hRgnRet = hClip;
-               }
-           }
-
-         if (Flags & UNC_CHECK)
-           {
-             UnsafeIntGetRgnBox(Window->UpdateRegion, &UpdateRegionBox);
-             if (NtGdiIsEmptyRect(&UpdateRegionBox))
-               {
-                 NtGdiDeleteObject(Window->UpdateRegion);
-                 Window->UpdateRegion = NULL;
-                 if (0 == (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT))
-                   {
-                     MsqDecPaintCountQueue(Window->MessageQueue);
-                   }
-                 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBACKGRD;
-               }
-           }
-
-         if (0 == hClip && 0 != Window->UpdateRegion)
-           {
-           goto copyrgn;
-           }
-       }
-      else if ((HRGN) 1 == Window->UpdateRegion)
-       {
-         if (0 != (Flags & UNC_UPDATE))
-           {
-             Window->UpdateRegion =
-               UnsafeIntCreateRectRgnIndirect(&ClientRect);
-           }
-         if (Flags & UNC_REGION)
-           {
-             hRgnRet = (HRGN) 1;
-           }
-         Flags |= UNC_ENTIRE;
-       }
-    }
-  else /* no WM_NCPAINT unless forced */
-    {
-      if ((HRGN) 1 < Window->UpdateRegion)
-       {
-copyrgn:
-         if (0 != (Flags & UNC_REGION))
-           {
-             hRgnRet = REGION_CropRgn(hRgn, Window->UpdateRegion, NULL, NULL);
-           }
-       }
-      else if ((HRGN) 1 == Window->UpdateRegion && 0 != (Flags & UNC_UPDATE))
-       {
-         GETCLIENTRECTW(Window, ClientRect);
-         Window->UpdateRegion =
-           UnsafeIntCreateRectRgnIndirect(&ClientRect);
-         if (Flags & UNC_REGION)
-           {
-             hRgnRet = (HRGN) 1;
-           }
-       }
-    }
-
-  if (NULL == hClip && 0 != (Flags & UNC_ENTIRE))
-    {
-      if (RtlCompareMemory(&Window->WindowRect, &Window->ClientRect,
-                          sizeof(RECT)) != sizeof(RECT))
-       {
-         hClip = (HRGN) 1;
-       }
+      MsqDecPaintCountQueue(Window->MessageQueue);
+      co_IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)hRgn, 0);
+      if (hRgn != (HANDLE)1 && hRgn != NULL)
+      {
+         /* NOTE: The region can already by deleted! */
+         GDIOBJ_FreeObj(hRgn, GDI_OBJECT_TYPE_REGION | GDI_OBJECT_TYPE_SILENT);
+      }
+   }
+
+   RtlZeroMemory(&Ps, sizeof(PAINTSTRUCT));
+
+   Ps.hdc = UserGetDCEx(Window, Window->UpdateRegion, DCX_INTERSECTRGN | DCX_USESTYLE);
+   if (!Ps.hdc)
+   {
+      RETURN(NULL);
+   }
+
+   if (Window->UpdateRegion != NULL)
+   {
+      MsqDecPaintCountQueue(Window->MessageQueue);
+      Rgn = RGNDATA_LockRgn(Window->UpdateRegion);
+      if (NULL != Rgn)
+      {
+         UnsafeIntGetRgnBox(Rgn, &Ps.rcPaint);
+         RGNDATA_UnlockRgn(Rgn);
+         IntGdiIntersectRect(&Ps.rcPaint, &Ps.rcPaint, &Window->ClientRect);
+         IntGdiOffsetRect(&Ps.rcPaint,
+                          -Window->ClientRect.left,
+                          -Window->ClientRect.top);
+      }
       else
-       {
-         hClip = NULL;
-       }
-    }
-
-  if (NULL != hClip) /* NOTE: WM_NCPAINT allows wParam to be 1 */
-    {
-      if (hClip == hRgnRet && (HRGN) 1 < hRgnRet)
-       {
-         hClip = NtGdiCreateRectRgn(0, 0, 0, 0);
-         NtGdiCombineRgn(hClip, hRgnRet, 0, RGN_COPY);
-       }
-
-      NtUserSendMessage(Window->Self, WM_NCPAINT, (WPARAM) hClip, 0);
-
-      if ((HRGN) 1 < hClip && hClip != hRgn && hClip != hRgnRet)
-       {
-         NtGdiDeleteObject(hClip);
-       }
-
-      /* FIXME: Need to check the window is still valid. */
-    }
-  return(hRgnRet);
+      {
+         IntGetClientRect(Window, &Ps.rcPaint);
+      }
+      GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
+      Window->UpdateRegion = NULL;
+   }
+   else
+   {
+      if (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
+         MsqDecPaintCountQueue(Window->MessageQueue);
+         
+      IntGetClientRect(Window, &Ps.rcPaint);
+   }
+
+   Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
+
+   if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
+   {
+      Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
+      Ps.fErase = !co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)Ps.hdc, 0);
+   }
+   else
+   {
+      Ps.fErase = FALSE;
+   }
+
+   Status = MmCopyToCaller(UnsafePs, &Ps, sizeof(PAINTSTRUCT));
+   if (! NT_SUCCESS(Status))
+   {
+      SetLastNtError(Status);
+      RETURN(NULL);
+   }
+
+   RETURN(Ps.hdc);
+
+CLEANUP:
+   if (Window) UserDerefObjectCo(Window);
+   
+   DPRINT("Leave NtUserBeginPaint, ret=%i\n",_ret_);
+   UserLeave();
+   END_CLEANUP;
+
 }
 
+/*
+ * NtUserEndPaint
+ *
+ * Status
+ *    @implemented
+ */
+
 BOOL STDCALL
 NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* lPs)
 {
-  NtUserReleaseDC(hWnd, lPs->hdc);
-  /* FIXME: Show claret. */
-  return(TRUE);
+   PWINDOW_OBJECT Window;
+   DECLARE_RETURN(BOOL);
+   USER_REFERENCE_ENTRY Ref;
+
+   DPRINT("Enter NtUserEndPaint\n");
+   UserEnterExclusive();
+
+   if (!(Window = UserGetWindowObject(hWnd)))
+   {
+      RETURN(FALSE);
+   }
+
+   UserReleaseDC(Window, lPs->hdc, TRUE);
+
+   UserRefObjectCo(Window, &Ref);
+   co_UserShowCaret(Window);
+   UserDerefObjectCo(Window);
+
+   RETURN(TRUE);
+
+CLEANUP:
+   DPRINT("Leave NtUserEndPaint, ret=%i\n",_ret_);
+   UserLeave();
+   END_CLEANUP;
+}
+
+
+INT FASTCALL
+co_UserGetUpdateRgn(PWINDOW_OBJECT Window, HRGN hRgn, BOOL bErase)
+{
+   int RegionType;
+   RECT Rect;
+
+   ASSERT_REFS_CO(Window);
+
+   if (Window->UpdateRegion == NULL)
+   {
+      RegionType = (NtGdiSetRectRgn(hRgn, 0, 0, 0, 0) ? NULLREGION : ERROR);
+   }
+   else
+   {
+      Rect = Window->ClientRect;
+      IntIntersectWithParents(Window, &Rect);
+      NtGdiSetRectRgn(hRgn, Rect.left, Rect.top, Rect.right, Rect.bottom);
+      RegionType = NtGdiCombineRgn(hRgn, hRgn, Window->UpdateRegion, RGN_AND);
+      NtGdiOffsetRgn(hRgn, -Window->ClientRect.left, -Window->ClientRect.top);
+   }
+
+   if (bErase && RegionType != NULLREGION && RegionType != ERROR)
+   {
+      co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
+   }
+
+   return RegionType;
 }
 
-static
-HRGN FASTCALL
-GetClientUpdateRegion(PWINDOW_OBJECT Window)
+/*
+ * NtUserGetUpdateRgn
+ *
+ * Status
+ *    @implemented
+ */
+
+INT STDCALL
+NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
 {
-  POINT Offset;
-  RECT Rect;
+   DECLARE_RETURN(INT);
+   PWINDOW_OBJECT Window;
+   INT ret;
+   USER_REFERENCE_ENTRY Ref;
 
-  if ((DWORD) Window->UpdateRegion <= 1)
-    {
-      return Window->UpdateRegion;
-    }
+   DPRINT("Enter NtUserGetUpdateRgn\n");
+   UserEnterExclusive();
+
+   if (!(Window = UserGetWindowObject(hWnd)))
+   {
+      RETURN(ERROR);
+   }
+
+   UserRefObjectCo(Window, &Ref);
+   ret = co_UserGetUpdateRgn(Window, hRgn, bErase);
+   UserDerefObjectCo(Window);
 
-  Offset.x = Window->WindowRect.left - Window->ClientRect.left;
-  Offset.y = Window->WindowRect.top - Window->ClientRect.top;
-  Rect.left = - Offset.x;
-  Rect.top = - Offset.y;
-  Rect.right = Rect.left + (Window->ClientRect.right - Window->ClientRect.left);
-  Rect.bottom = Rect.top + (Window->ClientRect.bottom - Window->ClientRect.top);
+   RETURN(ret);
 
-  return REGION_CropRgn(NULL, Window->UpdateRegion, &Rect, &Offset);
+CLEANUP:
+   DPRINT("Leave NtUserGetUpdateRgn, ret=%i\n",_ret_);
+   UserLeave();
+   END_CLEANUP;
 }
 
-HDC STDCALL
-NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* lPs)
+/*
+ * NtUserGetUpdateRect
+ *
+ * Status
+ *    @implemented
+ */
+
+BOOL STDCALL
+NtUserGetUpdateRect(HWND hWnd, LPRECT UnsafeRect, BOOL bErase)
 {
-  BOOL IsIcon;
-  PWINDOW_OBJECT Window;
-  HRGN UpdateRegion;
-  RECT ClientRect;
-  RECT ClipRect;
-  //NTSTATUS Status;
-  INT DcxFlags;
-
-  if (!(Window = IntGetWindowObject(hWnd)))
-  {
-    SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
-    return NULL;
-  }
-  
-  /* Send WM_NCPAINT */
-  PaintUpdateNCRegion(Window, 0, UNC_UPDATE | UNC_IN_BEGINPAINT);
-
-  /* Check ifthe window is still valid. */
-  if (!IntGetWindowObject(hWnd))
-  {
-    return 0;
-  }
-
-  /* retrieve update region */
-  UpdateRegion = GetClientUpdateRegion(Window);
-  if (1 < (DWORD) Window->UpdateRegion)
-    {
-      NtGdiDeleteObject(Window->UpdateRegion);
-    }
-  Window->UpdateRegion = 0;
-  if (UpdateRegion != NULL || (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT))
-    {
-      MsqDecPaintCountQueue(Window->MessageQueue);
-    }
-  Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
-
-  /* FIXME: Hide caret. */
-
-  IsIcon = (Window->Style & WS_MINIMIZE) && IntGetClassLong(Window, GCL_HICON, FALSE);
-
-  DcxFlags = DCX_INTERSECTRGN | DCX_WINDOWPAINT | DCX_USESTYLE;
-  if (IsIcon)
-    {
-    DcxFlags |= DCX_WINDOW;
-    }
-  if (IntGetClassLong(Window, GCL_STYLE, FALSE) & CS_PARENTDC)
-    {
-      /* Don't clip the output to the update region for CS_PARENTDC window */
-      if ((HRGN) 1 < UpdateRegion)
-       {
-         NtGdiDeleteObject(UpdateRegion);
-       }
-      UpdateRegion = NULL;
-      DcxFlags &= ~DCX_INTERSECTRGN;
-    }
-  else
-    {
-      if (NULL == UpdateRegion)  /* empty region, clip everything */
-       {
-          UpdateRegion = NtGdiCreateRectRgn(0, 0, 0, 0);
-       }
-      else if ((HRGN) 1 == UpdateRegion)  /* whole client area, don't clip */
-       {
-         UpdateRegion = NULL;
-         DcxFlags &= ~DCX_INTERSECTRGN;
-       }
-    }
-  lPs->hdc = NtUserGetDCEx(hWnd, UpdateRegion, DcxFlags);
-
-  /* FIXME: Check for DC creation failure. */
-
-  IntGetClientRect(Window, &ClientRect);
-  NtGdiGetClipBox(lPs->hdc, &ClipRect);
-  NtGdiLPtoDP(lPs->hdc, (LPPOINT)&ClipRect, 2);
-  NtGdiIntersectRect(&lPs->rcPaint, &ClientRect, &ClipRect);
-  NtGdiDPtoLP(lPs->hdc, (LPPOINT)&lPs->rcPaint, 2);
-
-  if (Window->Flags & WINDOWOBJECT_NEED_ERASEBACKGRD)
-    {
-      BOOLEAN Result;
-      Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBACKGRD;
-      Result = NtUserSendMessage(hWnd,
-                                IsIcon ? WM_ICONERASEBKGND : WM_ERASEBKGND,
-                                (WPARAM)lPs->hdc,
-                                0);
-      lPs->fErase = !Result;
-    }
-  else
-    {
-      lPs->fErase = FALSE;
-    }
-
-  ObmDereferenceObject(Window);
-  return(lPs->hdc);
+   PWINDOW_OBJECT Window;
+   RECT Rect;
+   INT RegionType;
+   PROSRGNDATA RgnData;
+   NTSTATUS Status;
+   DECLARE_RETURN(BOOL);
+
+   DPRINT("Enter NtUserGetUpdateRect\n");
+   UserEnterExclusive();
+
+   if (!(Window = UserGetWindowObject(hWnd)))
+   {
+      RETURN(FALSE);
+   }
+
+   if (Window->UpdateRegion == NULL)
+   {
+      Rect.left = Rect.top = Rect.right = Rect.bottom = 0;
+   }
+   else
+   {
+      /* Get the update region bounding box. */
+      if (Window->UpdateRegion == (HRGN)1)
+      {
+         Rect = Window->ClientRect;
+      }
+      else
+      {
+         RgnData = RGNDATA_LockRgn(Window->UpdateRegion);
+         ASSERT(RgnData != NULL);
+         RegionType = UnsafeIntGetRgnBox(RgnData, &Rect);
+         RGNDATA_UnlockRgn(RgnData);
+
+         if (RegionType != ERROR && RegionType != NULLREGION)
+            IntGdiIntersectRect(&Rect, &Rect, &Window->ClientRect);
+      }
+
+      if (IntIntersectWithParents(Window, &Rect))
+      {
+         IntGdiOffsetRect(&Rect,
+                          -Window->ClientRect.left,
+                          -Window->ClientRect.top);
+      } else
+      {
+         Rect.left = Rect.top = Rect.right = Rect.bottom = 0;
+      }
+   }
+
+   if (bErase && !IntGdiIsEmptyRect(&Rect))
+   {
+      USER_REFERENCE_ENTRY Ref;
+      UserRefObjectCo(Window, &Ref);
+      co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
+      UserDerefObjectCo(Window);
+   }
+
+   if (UnsafeRect != NULL)
+   {
+      Status = MmCopyToCaller(UnsafeRect, &Rect, sizeof(RECT));
+      if (!NT_SUCCESS(Status))
+      {
+         SetLastWin32Error(ERROR_INVALID_PARAMETER);
+         RETURN(FALSE);
+      }
+   }
+
+   RETURN(Window->UpdateRegion != NULL);
+
+CLEANUP:
+   DPRINT("Leave NtUserGetUpdateRect, ret=%i\n",_ret_);
+   UserLeave();
+   END_CLEANUP;
 }
 
-DWORD
-STDCALL
-NtUserInvalidateRect(
-  HWND hWnd,
-  CONST RECT *Rect,
-  WINBOOL Erase)
+/*
+ * NtUserRedrawWindow
+ *
+ * Status
+ *    @implemented
+ */
+
+BOOL STDCALL
+NtUserRedrawWindow(HWND hWnd, CONST RECT *lprcUpdate, HRGN hrgnUpdate,
+                   UINT flags)
 {
-  return NtUserRedrawWindow(hWnd, Rect, 0, RDW_INVALIDATE | (Erase ? RDW_ERASE : 0));
+   RECT SafeUpdateRect;
+   NTSTATUS Status;
+   PWINDOW_OBJECT Wnd;
+   DECLARE_RETURN(BOOL);
+   USER_REFERENCE_ENTRY Ref;
+
+   DPRINT("Enter NtUserRedrawWindow\n");
+   UserEnterExclusive();
+
+   if (!(Wnd = UserGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow())))
+   {
+      RETURN( FALSE);
+   }
+
+   if (lprcUpdate != NULL)
+   {
+      Status = MmCopyFromCaller(&SafeUpdateRect, (PRECT)lprcUpdate,
+                                sizeof(RECT));
+
+      if (!NT_SUCCESS(Status))
+      {
+         SetLastWin32Error(ERROR_INVALID_PARAMETER);
+         RETURN( FALSE);
+      }
+   }
+
+   UserRefObjectCo(Wnd, &Ref);
+
+   Status = co_UserRedrawWindow(Wnd, NULL == lprcUpdate ? NULL : &SafeUpdateRect,
+                                hrgnUpdate, flags);
+
+   UserDerefObjectCo(Wnd);
+
+   if (!NT_SUCCESS(Status))
+   {
+      /* IntRedrawWindow fails only in case that flags are invalid */
+      SetLastWin32Error(ERROR_INVALID_PARAMETER);
+      RETURN( FALSE);
+   }
+
+   RETURN( TRUE);
+
+CLEANUP:
+   DPRINT("Leave NtUserRedrawWindow, ret=%i\n",_ret_);
+   UserLeave();
+   END_CLEANUP;
 }
 
-DWORD
-STDCALL
-NtUserInvalidateRgn(
-  HWND hWnd,
-  HRGN Rgn,
-  WINBOOL Erase)
+
+
+static
+DWORD FASTCALL
+UserScrollDC(HDC hDC, INT dx, INT dy, const RECT *lprcScroll,
+             const RECT *lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate)
 {
-  return NtUserRedrawWindow(hWnd, NULL, Rgn, RDW_INVALIDATE | (Erase ? RDW_ERASE : 0));
+   RECT rSrc, rClipped_src, rClip, rDst, offset;
+   PDC DC;
+
+   /*
+    * Compute device clipping region (in device coordinates).
+    */
+
+   DC = DC_LockDc(hDC);
+   if (NULL == DC)
+   {
+      return FALSE;
+   }
+   if (lprcScroll)
+      rSrc = *lprcScroll;
+   else
+      IntGdiGetClipBox(hDC, &rSrc);
+   IntLPtoDP(DC, (LPPOINT)&rSrc, 2);
+
+   if (lprcClip)
+      rClip = *lprcClip;
+   else
+      IntGdiGetClipBox(hDC, &rClip);
+   IntLPtoDP(DC, (LPPOINT)&rClip, 2);
+
+   IntGdiIntersectRect(&rClipped_src, &rSrc, &rClip);
+
+   rDst = rClipped_src;
+   IntGdiSetRect(&offset, 0, 0, dx, dy);
+   IntLPtoDP(DC, (LPPOINT)&offset, 2);
+   IntGdiOffsetRect(&rDst, offset.right - offset.left,  offset.bottom - offset.top);
+   IntGdiIntersectRect(&rDst, &rDst, &rClip);
+
+   /*
+    * Copy bits, if possible.
+    */
+
+   if (rDst.bottom > rDst.top && rDst.right > rDst.left)
+   {
+      RECT rDst_lp = rDst, rSrc_lp = rDst;
+
+      IntGdiOffsetRect(&rSrc_lp, offset.left - offset.right, offset.top - offset.bottom);
+      IntDPtoLP(DC, (LPPOINT)&rDst_lp, 2);
+      IntDPtoLP(DC, (LPPOINT)&rSrc_lp, 2);
+      DC_UnlockDc(DC);
+
+      if (!NtGdiBitBlt(hDC, rDst_lp.left, rDst_lp.top, rDst_lp.right - rDst_lp.left,
+                       rDst_lp.bottom - rDst_lp.top, hDC, rSrc_lp.left, rSrc_lp.top,
+                       SRCCOPY))
+         return FALSE;
+   }
+   else
+   {
+      DC_UnlockDc(DC);
+   }
+
+   /*
+    * Compute update areas.  This is the clipped source or'ed with the
+    * unclipped source translated minus the clipped src translated (rDst)
+    * all clipped to rClip.
+    */
+
+   if (hrgnUpdate || lprcUpdate)
+   {
+      HRGN hRgn = hrgnUpdate, hRgn2;
+
+      if (hRgn)
+         NtGdiSetRectRgn(hRgn, rClipped_src.left, rClipped_src.top, rClipped_src.right, rClipped_src.bottom);
+      else
+         hRgn = NtGdiCreateRectRgn(rClipped_src.left, rClipped_src.top, rClipped_src.right, rClipped_src.bottom);
+
+      hRgn2 = UnsafeIntCreateRectRgnIndirect(&rSrc);
+      NtGdiOffsetRgn(hRgn2, offset.right - offset.left,  offset.bottom - offset.top);
+      NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_OR);
+
+      NtGdiSetRectRgn(hRgn2, rDst.left, rDst.top, rDst.right, rDst.bottom);
+      NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_DIFF);
+
+      NtGdiSetRectRgn(hRgn2, rClip.left, rClip.top, rClip.right, rClip.bottom);
+      NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_AND);
+
+      if (lprcUpdate)
+      {
+         NtGdiGetRgnBox(hRgn, lprcUpdate);
+
+         /* Put the lprcUpdate in logical coordinate */
+         NtGdiDPtoLP(hDC, (LPPOINT)lprcUpdate, 2);
+      }
+      if (!hrgnUpdate)
+         NtGdiDeleteObject(hRgn);
+      NtGdiDeleteObject(hRgn2);
+   }
+   return TRUE;
 }
 
-BOOL
-STDCALL
-NtUserValidateRgn(
-  HWND hWnd,
-  HRGN hRgn)
+
+
+
+/*
+ * NtUserScrollDC
+ *
+ * Status
+ *    @implemented
+ */
+
+DWORD STDCALL
+NtUserScrollDC(HDC hDC, INT dx, INT dy, const RECT *lprcScroll,
+               const RECT *lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate)
 {
-  return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_VALIDATE | RDW_NOCHILDREN);
+   DECLARE_RETURN(DWORD);
+
+   DPRINT("Enter NtUserScrollDC\n");
+   UserEnterExclusive();
+
+   RETURN( UserScrollDC(hDC, dx, dy, lprcScroll, lprcClip, hrgnUpdate, lprcUpdate));
+
+CLEANUP:
+   DPRINT("Leave NtUserScrollDC, ret=%i\n",_ret_);
+   UserLeave();
+   END_CLEANUP;
+
 }
 
-int
-STDCALL
-NtUserGetUpdateRgn(
-  HWND hWnd,
-  HRGN hRgn,
-  WINBOOL bErase)
+/*
+ * NtUserScrollWindowEx
+ *
+ * Status
+ *    @implemented
+ */
+
+DWORD STDCALL
+NtUserScrollWindowEx(HWND hWnd, INT dx, INT dy, const RECT *UnsafeRect,
+                     const RECT *UnsafeClipRect, HRGN hrgnUpdate, LPRECT rcUpdate, UINT flags)
 {
-  PWINDOW_OBJECT Window;
-  int RegionType;
-
-  if (!(Window = IntGetWindowObject(hWnd)))
-  {
-    SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
-    return ERROR;
-  }
-    
-  if (NULL == Window->UpdateRegion)
-    {
-      RegionType = (NtGdiSetRectRgn(hRgn, 0, 0, 0, 0) ? NULLREGION : ERROR);
-    }
-  else if ((HRGN) 1 == Window->UpdateRegion)
-    {
-      RegionType = (NtGdiSetRectRgn(hRgn,
-                                   0, 0,
-                                   Window->ClientRect.right - Window->ClientRect.left,
-                                   Window->ClientRect.bottom - Window->ClientRect.top) ?
-                    SIMPLEREGION : ERROR);
-    }
-  else
-    {
-      RegionType = NtGdiCombineRgn(hRgn, Window->UpdateRegion, hRgn, RGN_COPY);
-      NtGdiOffsetRgn(hRgn, Window->WindowRect.left - Window->ClientRect.left,
-                         Window->WindowRect.top - Window->ClientRect.top );
-    }
-
-  if (bErase &&
-      (SIMPLEREGION == RegionType || COMPLEXREGION == RegionType))
-    {
-      PaintRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN, 0);
-    }
-  
-  IntReleaseWindowObject(Window);
-
-  return RegionType;
+   RECT rc, cliprc, caretrc, rect, clipRect;
+   INT Result;
+   PWINDOW_OBJECT Window = NULL, CaretWnd;
+   HDC hDC;
+   HRGN hrgnTemp;
+   HWND hwndCaret;
+   BOOL bUpdate = (rcUpdate || hrgnUpdate || flags & (SW_INVALIDATE | SW_ERASE));
+   BOOL bOwnRgn = TRUE;
+   NTSTATUS Status;
+   DECLARE_RETURN(DWORD);
+   USER_REFERENCE_ENTRY Ref, CaretRef;
+
+   DPRINT("Enter NtUserScrollWindowEx\n");
+   UserEnterExclusive();
+
+   Window = UserGetWindowObject(hWnd);
+   if (!Window || !IntIsWindowDrawable(Window))
+   {
+      Window = NULL; /* prevent deref at cleanup */
+      RETURN( ERROR);
+   }
+   UserRefObjectCo(Window, &Ref);
+
+   IntGetClientRect(Window, &rc);
+
+   if (NULL != UnsafeRect)
+   {
+      Status = MmCopyFromCaller(&rect, UnsafeRect, sizeof(RECT));
+      if (! NT_SUCCESS(Status))
+      {
+         SetLastNtError(Status);
+         RETURN( ERROR);
+      }
+      IntGdiIntersectRect(&rc, &rc, &rect);
+   }
+
+   if (NULL != UnsafeClipRect)
+   {
+      Status = MmCopyFromCaller(&clipRect, UnsafeClipRect, sizeof(RECT));
+      if (! NT_SUCCESS(Status))
+      {
+         SetLastNtError(Status);
+         RETURN( ERROR);
+      }
+      IntGdiIntersectRect(&cliprc, &rc, &clipRect);
+   }
+   else
+      cliprc = rc;
+
+   if (cliprc.right <= cliprc.left || cliprc.bottom <= cliprc.top ||
+         (dx == 0 && dy == 0))
+   {
+      RETURN( NULLREGION);
+   }
+
+   caretrc = rc;
+   hwndCaret = co_IntFixCaret(Window, &caretrc, flags);
+
+   if (hrgnUpdate)
+      bOwnRgn = FALSE;
+   else if (bUpdate)
+      hrgnUpdate = NtGdiCreateRectRgn(0, 0, 0, 0);
+
+   hDC = UserGetDCEx(Window, 0, DCX_CACHE | DCX_USESTYLE);
+   if (hDC)
+   {
+      UserScrollDC(hDC, dx, dy, &rc, &cliprc, hrgnUpdate, rcUpdate);
+      UserReleaseDC(Window, hDC, FALSE);
+   }
+
+   /*
+    * Take into account the fact that some damage may have occurred during
+    * the scroll.
+    */
+
+   hrgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
+   Result = co_UserGetUpdateRgn(Window, hrgnTemp, FALSE);
+   if (Result != NULLREGION)
+   {
+      HRGN hrgnClip = UnsafeIntCreateRectRgnIndirect(&cliprc);
+      NtGdiOffsetRgn(hrgnTemp, dx, dy);
+      NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnClip, RGN_AND);
+      co_UserRedrawWindow(Window, NULL, hrgnTemp, RDW_INVALIDATE | RDW_ERASE);
+      NtGdiDeleteObject(hrgnClip);
+   }
+
+   NtGdiDeleteObject(hrgnTemp);
+
+   if (flags & SW_SCROLLCHILDREN)
+   {
+      HWND *List = IntWinListChildren(Window);
+      if (List)
+      {
+         int i;
+         RECT r, dummy;
+         POINT ClientOrigin;
+         PWINDOW_OBJECT Wnd;
+         USER_REFERENCE_ENTRY WndRef;
+
+         IntGetClientOrigin(Window, &ClientOrigin);
+         for (i = 0; List[i]; i++)
+         {
+            if (!(Wnd = UserGetWindowObject(List[i])))
+               continue;
+
+            r = Wnd->WindowRect;
+            r.left -= ClientOrigin.x;
+            r.top -= ClientOrigin.y;
+            r.right -= ClientOrigin.x;
+            r.bottom -= ClientOrigin.y;
+
+            if (! UnsafeRect || IntGdiIntersectRect(&dummy, &r, &rc))
+            {
+               UserRefObjectCo(Wnd, &WndRef);
+               co_WinPosSetWindowPos(Wnd, 0, r.left + dx, r.top + dy, 0, 0,
+                                     SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
+                                     SWP_NOREDRAW);
+               UserDerefObjectCo(Wnd);
+            }
+
+         }
+         ExFreePool(List);
+      }
+   }
+
+   if (flags & (SW_INVALIDATE | SW_ERASE))
+      co_UserRedrawWindow(Window, NULL, hrgnUpdate, RDW_INVALIDATE | RDW_ERASE |
+                          ((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
+                          ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0));
+
+   if (bOwnRgn && hrgnUpdate)
+      NtGdiDeleteObject(hrgnUpdate);
+
+   if ((CaretWnd = UserGetWindowObject(hwndCaret)))
+   {
+      UserRefObjectCo(CaretWnd, &CaretRef);
+
+      co_IntSetCaretPos(caretrc.left + dx, caretrc.top + dy);
+      co_UserShowCaret(CaretWnd);
+
+      UserDerefObjectCo(CaretWnd);
+   }
+
+   RETURN(Result);
+
+CLEANUP:
+   if (Window)
+      UserDerefObjectCo(Window);
+
+   DPRINT("Leave NtUserScrollWindowEx, ret=%i\n",_ret_);
+   UserLeave();
+   END_CLEANUP;
 }
 
 /* EOF */