remove relatives lock, window props lock, classes locks, thread window lock, menu...
[reactos.git] / reactos / subsys / win32k / ntuser / painting.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 * $Id$
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * PURPOSE: Window painting function
24 * FILE: subsys/win32k/ntuser/painting.c
25 * PROGRAMER: Filip Navara (xnavara@volny.cz)
26 * REVISION HISTORY:
27 * 06/06/2001 Created (?)
28 * 18/11/2003 Complete rewrite
29 */
30
31 /* INCLUDES ******************************************************************/
32
33 #include <w32k.h>
34
35 #define NDEBUG
36 #include <debug.h>
37
38 #define DCX_USESTYLE 0x10000
39
40 /* PRIVATE FUNCTIONS **********************************************************/
41
42 VOID FASTCALL
43 IntValidateParent(PWINDOW_OBJECT Child, HRGN ValidRegion)
44 {
45 PWINDOW_OBJECT ParentWindow = IntGetParentObject(Child), OldWindow;
46
47 while (ParentWindow)
48 {
49 if (!(ParentWindow->Style & WS_CLIPCHILDREN))
50 {
51 IntLockWindowUpdate(ParentWindow);
52 if (ParentWindow->UpdateRegion != 0)
53 {
54 INT OffsetX, OffsetY;
55
56 /*
57 * We must offset the child region by the offset of the
58 * child rect in the parent.
59 */
60 OffsetX = Child->WindowRect.left - ParentWindow->WindowRect.left;
61 OffsetY = Child->WindowRect.top - ParentWindow->WindowRect.top;
62 NtGdiOffsetRgn(ValidRegion, OffsetX, OffsetY);
63 NtGdiCombineRgn(ParentWindow->UpdateRegion, ParentWindow->UpdateRegion,
64 ValidRegion, RGN_DIFF);
65 /* FIXME: If the resulting region is empty, remove fake posted paint message */
66 NtGdiOffsetRgn(ValidRegion, -OffsetX, -OffsetY);
67 }
68 IntUnLockWindowUpdate(ParentWindow);
69 }
70 OldWindow = ParentWindow;
71 ParentWindow = IntGetParentObject(ParentWindow);
72 IntReleaseWindowObject(OldWindow);
73 }
74 }
75
76 /*
77 * IntPaintWindows
78 *
79 * Internal function used by IntRedrawWindow.
80 */
81
82 STATIC VOID FASTCALL
83 co_IntPaintWindows(PWINDOW_OBJECT Window, ULONG Flags)
84 {
85 HDC hDC;
86 HWND hWnd = Window->Self;
87 HRGN TempRegion;
88
89 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
90 {
91 if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
92 {
93 IntLockWindowUpdate(Window);
94 if (Window->NCUpdateRegion)
95 {
96 IntValidateParent(Window, Window->NCUpdateRegion);
97 }
98 TempRegion = Window->NCUpdateRegion;
99 if ((HANDLE) 1 != TempRegion && NULL != TempRegion)
100 {
101 GDIOBJ_SetOwnership(TempRegion, PsGetCurrentProcess());
102 }
103 Window->NCUpdateRegion = NULL;
104 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
105 MsqDecPaintCountQueue(Window->MessageQueue);
106 IntUnLockWindowUpdate(Window);
107 co_IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)TempRegion, 0);
108 if ((HANDLE) 1 != TempRegion && NULL != TempRegion)
109 {
110 /* NOTE: The region can already be deleted! */
111 GDIOBJ_FreeObj(TempRegion, GDI_OBJECT_TYPE_REGION | GDI_OBJECT_TYPE_SILENT);
112 }
113 }
114
115 if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
116 {
117 if (Window->UpdateRegion)
118 {
119 /*
120 * This surely wrong! Why we would want to validate the parent?
121 * It breaks quite a few things including dummy WM_ERASEBKGND
122 * implementations that return only TRUE and have corresponding
123 * WM_PAINT that doesn't paint the whole client area.
124 * I left the code here so that no one will readd it again!
125 * - Filip
126 */
127 /* IntValidateParent(Window, Window->UpdateRegion); */
128 hDC = UserGetDCEx(Window, 0, DCX_CACHE | DCX_USESTYLE |
129 DCX_INTERSECTUPDATE);
130 if (hDC != NULL)
131 {
132 if (co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)hDC, 0))
133 {
134 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
135 }
136 UserReleaseDC(Window, hDC);
137 }
138 }
139 }
140
141 if (Flags & RDW_UPDATENOW)
142 {
143 if (Window->UpdateRegion != NULL ||
144 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
145 {
146 co_IntSendMessage(hWnd, WM_PAINT, 0, 0);
147 }
148 }
149 }
150
151 /*
152 * Check that the window is still valid at this point
153 */
154
155 if (! IntIsWindow(hWnd))
156 {
157 return;
158 }
159
160 /*
161 * Paint child windows.
162 */
163 if (!(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
164 ((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN)))
165 {
166 HWND *List, *phWnd;
167
168 if ((List = IntWinListChildren(Window)))
169 {
170 for (phWnd = List; *phWnd; ++phWnd)
171 {
172 Window = IntGetWindowObject(*phWnd);
173 if (Window && (Window->Style & WS_VISIBLE))
174 {
175 co_IntPaintWindows(Window, Flags);
176 IntReleaseWindowObject(Window);
177 }
178 }
179 ExFreePool(List);
180 }
181 }
182 }
183
184 /*
185 * IntInvalidateWindows
186 *
187 * Internal function used by IntRedrawWindow.
188 */
189
190 VOID FASTCALL
191 IntInvalidateWindows(PWINDOW_OBJECT Window, HRGN hRgn, ULONG Flags)
192 {
193 INT RgnType;
194 BOOL HadPaintMessage, HadNCPaintMessage;
195 BOOL HasPaintMessage, HasNCPaintMessage;
196
197 /*
198 * Clip the given region with window rectangle (or region)
199 */
200
201 IntLockWindowUpdate(Window);
202 if (!Window->WindowRegion || (Window->Style & WS_MINIMIZE))
203 {
204 HRGN hRgnWindow;
205
206 IntUnLockWindowUpdate(Window);
207 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
208 NtGdiOffsetRgn(hRgnWindow,
209 -Window->WindowRect.left,
210 -Window->WindowRect.top);
211 RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnWindow, RGN_AND);
212 NtGdiDeleteObject(hRgnWindow);
213 }
214 else
215 {
216 RgnType = NtGdiCombineRgn(hRgn, hRgn, Window->WindowRegion, RGN_AND);
217 IntUnLockWindowUpdate(Window);
218 }
219
220 /*
221 * Save current state of pending updates
222 */
223
224 IntLockWindowUpdate(Window);
225 HadPaintMessage = Window->UpdateRegion != NULL ||
226 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
227 HadNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
228
229 /*
230 * Update the region and flags
231 */
232
233 if (Flags & RDW_INVALIDATE && RgnType != NULLREGION)
234 {
235 if (Window->UpdateRegion == NULL)
236 {
237 Window->UpdateRegion = NtGdiCreateRectRgn(0, 0, 0, 0);
238 GDIOBJ_SetOwnership(Window->UpdateRegion, NULL);
239 }
240
241 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
242 hRgn, RGN_OR) == NULLREGION)
243 {
244 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
245 NtGdiDeleteObject(Window->UpdateRegion);
246 Window->UpdateRegion = NULL;
247 }
248
249 if (Flags & RDW_FRAME)
250 Window->Flags |= WINDOWOBJECT_NEED_NCPAINT;
251 if (Flags & RDW_ERASE)
252 Window->Flags |= WINDOWOBJECT_NEED_ERASEBKGND;
253
254 Flags |= RDW_FRAME;
255 }
256
257 if (Flags & RDW_VALIDATE && RgnType != NULLREGION)
258 {
259 if (Window->UpdateRegion != NULL)
260 {
261 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
262 hRgn, RGN_DIFF) == NULLREGION)
263 {
264 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
265 NtGdiDeleteObject(Window->UpdateRegion);
266 Window->UpdateRegion = NULL;
267 }
268 }
269
270 if (Window->UpdateRegion == NULL)
271 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
272 if (Flags & RDW_NOFRAME)
273 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
274 if (Flags & RDW_NOERASE)
275 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
276 }
277
278 if (Flags & RDW_INTERNALPAINT)
279 {
280 Window->Flags |= WINDOWOBJECT_NEED_INTERNALPAINT;
281 }
282
283 if (Flags & RDW_NOINTERNALPAINT)
284 {
285 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
286 }
287
288 /*
289 * Split the nonclient update region.
290 */
291
292 if (NULL != Window->UpdateRegion)
293 {
294 HRGN hRgnWindow, hRgnNonClient;
295
296 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
297 NtGdiOffsetRgn(hRgnWindow,
298 -Window->WindowRect.left,
299 -Window->WindowRect.top);
300
301 hRgnNonClient = NtGdiCreateRectRgn(0, 0, 0, 0);
302 if (NtGdiCombineRgn(hRgnNonClient, Window->UpdateRegion,
303 hRgnWindow, RGN_DIFF) == NULLREGION)
304 {
305 NtGdiDeleteObject(hRgnNonClient);
306 hRgnNonClient = NULL;
307 }
308 else
309 {
310 GDIOBJ_SetOwnership(hRgnNonClient, NULL);
311 }
312
313 /*
314 * Remove the nonclient region from the standard update region.
315 */
316 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
317 hRgnWindow, RGN_AND) == NULLREGION)
318 {
319 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
320 NtGdiDeleteObject(Window->UpdateRegion);
321 Window->UpdateRegion = NULL;
322 }
323
324 if (Window->NCUpdateRegion == NULL)
325 {
326 Window->NCUpdateRegion = hRgnNonClient;
327 }
328 else
329 {
330 if(NULL != hRgnNonClient)
331 {
332 NtGdiCombineRgn(Window->NCUpdateRegion, Window->NCUpdateRegion,
333 hRgnNonClient, RGN_OR);
334 GDIOBJ_SetOwnership(hRgnNonClient, PsGetCurrentProcess());
335 NtGdiDeleteObject(hRgnNonClient);
336 }
337 }
338
339 NtGdiDeleteObject(hRgnWindow);
340 }
341
342 /*
343 * Process children if needed
344 */
345
346 if (!(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
347 ((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN)))
348 {
349 HWND *List, *phWnd;
350 PWINDOW_OBJECT Child;
351
352 if ((List = IntWinListChildren(Window)))
353 {
354 for (phWnd = List; *phWnd; ++phWnd)
355 {
356 Child = IntGetWindowObject(*phWnd);
357 if(!Child)
358 {
359 continue;
360 }
361 if (Child->Style & WS_VISIBLE)
362 {
363 /*
364 * Recursive call to update children UpdateRegion
365 */
366 HRGN hRgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
367 NtGdiCombineRgn(hRgnTemp, hRgn, 0, RGN_COPY);
368 NtGdiOffsetRgn(hRgnTemp,
369 Window->WindowRect.left - Child->WindowRect.left,
370 Window->WindowRect.top - Child->WindowRect.top);
371 IntInvalidateWindows(Child, hRgnTemp, Flags);
372 NtGdiDeleteObject(hRgnTemp);
373 }
374 IntReleaseWindowObject(Child);
375 }
376 ExFreePool(List);
377 }
378 }
379
380 /*
381 * Fake post paint messages to window message queue if needed
382 */
383
384 HasPaintMessage = Window->UpdateRegion != NULL ||
385 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
386 HasNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
387
388 if (HasPaintMessage != HadPaintMessage)
389 {
390 if (HadPaintMessage)
391 MsqDecPaintCountQueue(Window->MessageQueue);
392 else
393 MsqIncPaintCountQueue(Window->MessageQueue);
394 }
395
396 if (HasNCPaintMessage != HadNCPaintMessage)
397 {
398 if (HadNCPaintMessage)
399 MsqDecPaintCountQueue(Window->MessageQueue);
400 else
401 MsqIncPaintCountQueue(Window->MessageQueue);
402 }
403
404 IntUnLockWindowUpdate(Window);
405 }
406
407 /*
408 * IntIsWindowDrawable
409 *
410 * Remarks
411 * Window is drawable when it is visible and all parents are not
412 * minimized.
413 */
414
415 BOOL FASTCALL
416 IntIsWindowDrawable(PWINDOW_OBJECT Window)
417 {
418 PWINDOW_OBJECT Old, Wnd = Window;
419
420 IntReferenceWindowObject(Wnd);
421 do
422 {
423 if (!(Wnd->Style & WS_VISIBLE) ||
424 ((Wnd->Style & WS_MINIMIZE) && (Wnd != Window)))
425 {
426 IntReleaseWindowObject(Wnd);
427 return FALSE;
428 }
429 Old = Wnd;
430 Wnd = IntGetParentObject(Wnd);
431 IntReleaseWindowObject(Old);
432 } while(Wnd);
433
434 return TRUE;
435 }
436
437 /*
438 * IntRedrawWindow
439 *
440 * Internal version of NtUserRedrawWindow that takes WINDOW_OBJECT as
441 * first parameter.
442 */
443
444 BOOL FASTCALL
445 co_UserRedrawWindow(PWINDOW_OBJECT Window, const RECT* UpdateRect, HRGN UpdateRgn,
446 ULONG Flags)
447 {
448 HRGN hRgn = NULL;
449
450 /*
451 * Step 1.
452 * Validation of passed parameters.
453 */
454
455 if (!IntIsWindowDrawable(Window) ||
456 (Flags & (RDW_VALIDATE | RDW_INVALIDATE)) ==
457 (RDW_VALIDATE | RDW_INVALIDATE))
458 {
459 return FALSE;
460 }
461
462 /*
463 * Step 2.
464 * Transform the parameters UpdateRgn and UpdateRect into
465 * a region hRgn specified in window coordinates.
466 */
467
468 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE))
469 {
470 if (UpdateRgn != NULL)
471 {
472 hRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
473 NtGdiCombineRgn(hRgn, UpdateRgn, NULL, RGN_COPY);
474 NtGdiOffsetRgn(hRgn,
475 Window->ClientRect.left - Window->WindowRect.left,
476 Window->ClientRect.top - Window->WindowRect.top);
477 } else
478 if (UpdateRect != NULL)
479 {
480 hRgn = UnsafeIntCreateRectRgnIndirect((RECT *)UpdateRect);
481 NtGdiOffsetRgn(hRgn,
482 Window->ClientRect.left - Window->WindowRect.left,
483 Window->ClientRect.top - Window->WindowRect.top);
484 } else
485 if ((Flags & (RDW_INVALIDATE | RDW_FRAME)) == (RDW_INVALIDATE | RDW_FRAME) ||
486 (Flags & (RDW_VALIDATE | RDW_NOFRAME)) == (RDW_VALIDATE | RDW_NOFRAME))
487 {
488 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
489 NtGdiOffsetRgn(hRgn,
490 -Window->WindowRect.left,
491 -Window->WindowRect.top);
492 }
493 else
494 {
495 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
496 NtGdiOffsetRgn(hRgn,
497 -Window->WindowRect.left,
498 -Window->WindowRect.top);
499 }
500 }
501
502 /*
503 * Step 3.
504 * Adjust the window update region depending on hRgn and flags.
505 */
506
507 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT))
508 {
509 IntInvalidateWindows(Window, hRgn, Flags);
510 }
511
512 /*
513 * Step 4.
514 * Repaint and erase windows if needed.
515 */
516
517 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
518 {
519 co_IntPaintWindows(Window, Flags);
520 }
521
522 /*
523 * Step 5.
524 * Cleanup ;-)
525 */
526
527 if (hRgn != NULL)
528 {
529 NtGdiDeleteObject(hRgn);
530 }
531
532 return TRUE;
533 }
534
535 BOOL FASTCALL
536 IntIsWindowDirty(PWINDOW_OBJECT Window)
537 {
538 return (Window->Style & WS_VISIBLE) &&
539 ((Window->UpdateRegion != NULL) ||
540 (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT) ||
541 (Window->Flags & WINDOWOBJECT_NEED_NCPAINT));
542 }
543
544 HWND STDCALL
545 IntFindWindowToRepaint(HWND hWnd, PW32THREAD Thread)
546 {
547 PWINDOW_OBJECT Window;
548 PWINDOW_OBJECT Child;
549 HWND hFoundWnd = NULL;
550
551 Window = IntGetWindowObject(hWnd);
552 if (Window == NULL)
553 return NULL;
554
555 if (IntIsWindowDirty(Window) &&
556 IntWndBelongsToThread(Window, Thread))
557 {
558 IntReleaseWindowObject(Window);
559 return hWnd;
560 }
561
562 for (Child = Window->FirstChild; Child; Child = Child->NextSibling)
563 {
564 if (IntIsWindowDirty(Child) &&
565 IntWndBelongsToThread(Child, Thread))
566 {
567 hFoundWnd = Child->Self;
568 break;
569 }
570 }
571
572 if (hFoundWnd == NULL)
573 {
574 HWND *List;
575 INT i;
576
577 List = IntWinListChildren(Window);
578 if (List != NULL)
579 {
580 for (i = 0; List[i]; i++)
581 {
582 hFoundWnd = IntFindWindowToRepaint(List[i], Thread);
583 if (hFoundWnd != NULL)
584 break;
585 }
586 ExFreePool(List);
587 }
588 }
589
590 IntReleaseWindowObject(Window);
591
592 return hFoundWnd;
593 }
594
595 BOOL FASTCALL
596 IntGetPaintMessage(HWND hWnd, UINT MsgFilterMin, UINT MsgFilterMax,
597 PW32THREAD Thread, MSG *Message, BOOL Remove)
598 {
599 PWINDOW_OBJECT Window;
600 PUSER_MESSAGE_QUEUE MessageQueue = (PUSER_MESSAGE_QUEUE)Thread->MessageQueue;
601
602 if (!MessageQueue->PaintPosted)
603 return FALSE;
604
605 if ((MsgFilterMin != 0 || MsgFilterMax != 0) &&
606 (MsgFilterMin > WM_PAINT || MsgFilterMax < WM_PAINT))
607 return FALSE;
608
609 if (hWnd)
610 Message->hwnd = IntFindWindowToRepaint(hWnd, PsGetWin32Thread());
611 else
612 Message->hwnd = IntFindWindowToRepaint(IntGetDesktopWindow(), PsGetWin32Thread());
613
614 if (Message->hwnd == NULL)
615 {
616 if (NULL == hWnd)
617 {
618 DPRINT1("PAINTING BUG: Thread marked as containing dirty windows, but no dirty windows found!\n");
619 IntLockMessageQueue(MessageQueue);
620 MessageQueue->PaintPosted = 0;
621 MessageQueue->PaintCount = 0;
622 IntUnLockMessageQueue(MessageQueue);
623 }
624 return FALSE;
625 }
626
627 Window = IntGetWindowObject(Message->hwnd);
628 if (Window != NULL)
629 {
630 Message->message = WM_PAINT;
631 Message->wParam = Message->lParam = 0;
632 IntReleaseWindowObject(Window);
633
634 return TRUE;
635 }
636
637 return FALSE;
638 }
639
640 static
641 HWND FASTCALL
642 co_IntFixCaret(HWND hWnd, LPRECT lprc, UINT flags)
643 {
644 PDESKTOP_OBJECT Desktop;
645 PTHRDCARETINFO CaretInfo;
646 HWND hWndCaret;
647
648 Desktop = PsGetCurrentThread()->Tcb.Win32Thread->Desktop;
649 CaretInfo = ((PUSER_MESSAGE_QUEUE)Desktop->ActiveMessageQueue)->CaretInfo;
650 hWndCaret = CaretInfo->hWnd;
651 if (hWndCaret == hWnd ||
652 ((flags & SW_SCROLLCHILDREN) && IntIsChildWindow(hWnd, hWndCaret)))
653 {
654 POINT pt, FromOffset, ToOffset, Offset;
655 RECT rcCaret;
656
657 pt.x = CaretInfo->Pos.x;
658 pt.y = CaretInfo->Pos.y;
659 IntGetClientOrigin(hWndCaret, &FromOffset);
660 IntGetClientOrigin(hWnd, &ToOffset);
661 Offset.x = FromOffset.x - ToOffset.x;
662 Offset.y = FromOffset.y - ToOffset.y;
663 rcCaret.left = pt.x;
664 rcCaret.top = pt.y;
665 rcCaret.right = pt.x + CaretInfo->Size.cx;
666 rcCaret.bottom = pt.y + CaretInfo->Size.cy;
667 if (IntGdiIntersectRect(lprc, lprc, &rcCaret))
668 {
669 co_UserHideCaret(0);
670 lprc->left = pt.x;
671 lprc->top = pt.y;
672 return hWndCaret;
673 }
674 }
675
676 return 0;
677 }
678
679 /* PUBLIC FUNCTIONS ***********************************************************/
680
681 /*
682 * NtUserBeginPaint
683 *
684 * Status
685 * @implemented
686 */
687
688 HDC STDCALL
689 NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* UnsafePs)
690 {
691 PWINDOW_OBJECT Window;
692 PAINTSTRUCT Ps;
693 PROSRGNDATA Rgn;
694 NTSTATUS Status;
695 DECLARE_RETURN(HDC);
696
697 DPRINT("Enter NtUserBeginPaint\n");
698 UserEnterExclusive();
699
700 if (!(Window = IntGetWindowObject(hWnd)))
701 {
702 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
703 RETURN( NULL);
704 }
705
706 co_UserHideCaret(Window);
707
708 if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
709 {
710 HRGN hRgn;
711
712 if (Window->NCUpdateRegion != (HANDLE)1 &&
713 Window->NCUpdateRegion != NULL)
714 {
715 GDIOBJ_SetOwnership(Window->NCUpdateRegion, PsGetCurrentProcess());
716 }
717 hRgn = Window->NCUpdateRegion;
718 IntValidateParent(Window, Window->NCUpdateRegion);
719 Window->NCUpdateRegion = NULL;
720 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
721 MsqDecPaintCountQueue(Window->MessageQueue);
722 co_IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)hRgn, 0);
723 if (hRgn != (HANDLE)1 && hRgn != NULL)
724 {
725 /* NOTE: The region can already by deleted! */
726 GDIOBJ_FreeObj(hRgn, GDI_OBJECT_TYPE_REGION | GDI_OBJECT_TYPE_SILENT);
727 }
728 }
729
730 RtlZeroMemory(&Ps, sizeof(PAINTSTRUCT));
731
732 Ps.hdc = UserGetDCEx(Window, 0, DCX_INTERSECTUPDATE | DCX_WINDOWPAINT | DCX_USESTYLE);
733
734 if (!Ps.hdc)
735 {
736 IntReleaseWindowObject(Window);
737 RETURN( NULL);
738 }
739
740 IntLockWindowUpdate(Window);
741 if (Window->UpdateRegion != NULL)
742 {
743 MsqDecPaintCountQueue(Window->MessageQueue);
744 IntValidateParent(Window, Window->UpdateRegion);
745 Rgn = RGNDATA_LockRgn(Window->UpdateRegion);
746 if (NULL != Rgn)
747 {
748 UnsafeIntGetRgnBox(Rgn, &Ps.rcPaint);
749 RGNDATA_UnlockRgn(Rgn);
750 IntGdiOffsetRect(&Ps.rcPaint,
751 Window->WindowRect.left - Window->ClientRect.left,
752 Window->WindowRect.top - Window->ClientRect.top);
753 }
754 else
755 {
756 IntGetClientRect(Window, &Ps.rcPaint);
757 }
758 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
759 NtGdiDeleteObject(Window->UpdateRegion);
760 Window->UpdateRegion = NULL;
761 }
762 else
763 {
764 if (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
765 MsqDecPaintCountQueue(Window->MessageQueue);
766 IntGetClientRect(Window, &Ps.rcPaint);
767 }
768 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
769 IntUnLockWindowUpdate(Window);
770
771 if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
772 {
773 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
774 Ps.fErase = !co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)Ps.hdc, 0);
775 }
776 else
777 {
778 Ps.fErase = FALSE;
779 }
780
781 IntReleaseWindowObject(Window);
782
783 Status = MmCopyToCaller(UnsafePs, &Ps, sizeof(PAINTSTRUCT));
784 if (! NT_SUCCESS(Status))
785 {
786 SetLastNtError(Status);
787 RETURN( NULL);
788 }
789
790 RETURN( Ps.hdc);
791
792 CLEANUP:
793 DPRINT("Leave NtUserBeginPaint, ret=%i\n",_ret_);
794 UserLeave();
795 END_CLEANUP;
796
797 }
798
799 /*
800 * NtUserEndPaint
801 *
802 * Status
803 * @implemented
804 */
805
806 BOOL STDCALL
807 NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* lPs)
808 {
809 PWINDOW_OBJECT Window;
810 DECLARE_RETURN(BOOL);
811
812 DPRINT("Enter NtUserEndPaint\n");
813 UserEnterExclusive();
814
815 if (!(Window = IntGetWindowObject(hWnd)))
816 {
817 RETURN(FALSE);
818 }
819
820 UserReleaseDC(Window, lPs->hdc);
821 co_UserShowCaret(Window);
822
823 IntReleaseWindowObject(Window); //temp hack
824
825 RETURN(TRUE);
826
827 CLEANUP:
828 DPRINT("Leave NtUserEndPaint, ret=%i\n",_ret_);
829 UserLeave();
830 END_CLEANUP;
831 }
832
833 /*
834 * NtUserInvalidateRect
835 *
836 * Status
837 * @implemented
838 */
839
840 DWORD STDCALL
841 NtUserInvalidateRect(HWND hWnd, CONST RECT *Rect, BOOL Erase)
842 {
843 return NtUserRedrawWindow(hWnd, Rect, 0, RDW_INVALIDATE | (Erase ? RDW_ERASE : 0));
844 }
845
846 /*
847 * NtUserInvalidateRgn
848 *
849 * Status
850 * @implemented
851 */
852
853 DWORD STDCALL
854 NtUserInvalidateRgn(HWND hWnd, HRGN Rgn, BOOL Erase)
855 {
856 return NtUserRedrawWindow(hWnd, NULL, Rgn, RDW_INVALIDATE | (Erase ? RDW_ERASE : 0));
857 }
858
859
860
861 BOOL FASTCALL
862 UserValidateRgn(HWND hWnd, HRGN hRgn)
863 {
864 PWINDOW_OBJECT Window;
865 BOOL ret;
866
867 if (!(Window = IntGetWindowObject(hWnd))) return FALSE;
868 ret = co_UserRedrawWindow(Window, NULL, hRgn, RDW_VALIDATE | RDW_NOCHILDREN);
869 IntReleaseWindowObject(Window);//temp hack
870 return ret;
871 }
872
873 /*
874 * NtUserValidateRgn
875 *
876 * Status
877 * @implemented
878 */
879
880 BOOL STDCALL
881 NtUserValidateRgn(HWND hWnd, HRGN hRgn)
882 {
883 return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_VALIDATE | RDW_NOCHILDREN);
884 }
885
886 /*
887 * NtUserUpdateWindow
888 *
889 * Status
890 * @implemented
891 */
892
893 BOOL STDCALL
894 NtUserUpdateWindow(HWND hWnd)
895 {
896 return NtUserRedrawWindow(hWnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN);
897 }
898
899
900
901
902
903 INT FASTCALL
904 co_UserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
905 {
906 PWINDOW_OBJECT Window;
907 int RegionType;
908
909 if (!(Window = IntGetWindowObject(hWnd)))
910 {
911 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
912 return ERROR;
913 }
914
915 IntLockWindowUpdate(Window);
916 if (Window->UpdateRegion == NULL)
917 {
918 RegionType = (NtGdiSetRectRgn(hRgn, 0, 0, 0, 0) ? NULLREGION : ERROR);
919 }
920 else
921 {
922 RegionType = NtGdiCombineRgn(hRgn, Window->UpdateRegion, hRgn, RGN_COPY);
923 NtGdiOffsetRgn(
924 hRgn,
925 Window->WindowRect.left - Window->ClientRect.left,
926 Window->WindowRect.top - Window->ClientRect.top);
927 }
928 IntUnLockWindowUpdate(Window);
929
930 if (bErase && RegionType != NULLREGION && RegionType != ERROR)
931 {
932 co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
933 }
934
935 IntReleaseWindowObject(Window);
936
937 return RegionType;
938 }
939 /*
940 * NtUserGetUpdateRgn
941 *
942 * Status
943 * @implemented
944 */
945
946 INT STDCALL
947 NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
948 {
949 DECLARE_RETURN(INT);
950
951 DPRINT("Enter NtUserGetUpdateRgn\n");
952 UserEnterExclusive();
953
954 RETURN(co_UserGetUpdateRgn(hWnd, hRgn, bErase));
955
956 CLEANUP:
957 DPRINT("Leave NtUserGetUpdateRgn, ret=%i\n",_ret_);
958 UserLeave();
959 END_CLEANUP;
960 }
961
962 /*
963 * NtUserGetUpdateRect
964 *
965 * Status
966 * @implemented
967 */
968
969 BOOL STDCALL
970 NtUserGetUpdateRect(HWND hWnd, LPRECT UnsafeRect, BOOL bErase)
971 {
972 PWINDOW_OBJECT Window;
973 RECT Rect;
974 INT RegionType;
975 PROSRGNDATA RgnData;
976 BOOL AlwaysPaint;
977 NTSTATUS Status;
978 DECLARE_RETURN(BOOL);
979
980 DPRINT("Enter NtUserGetUpdateRect\n");
981 UserEnterExclusive();
982
983 if (!(Window = IntGetWindowObject(hWnd)))
984 {
985 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
986 RETURN( ERROR);
987 }
988
989 IntLockWindowUpdate(Window);
990 if (Window->UpdateRegion == NULL)
991 {
992 Rect.left = Rect.top = Rect.right = Rect.bottom = 0;
993 }
994 else
995 {
996 RgnData = RGNDATA_LockRgn(Window->UpdateRegion);
997 ASSERT(RgnData != NULL);
998 RegionType = UnsafeIntGetRgnBox(RgnData, &Rect);
999 ASSERT(RegionType != ERROR);
1000 RGNDATA_UnlockRgn(RgnData);
1001 }
1002 AlwaysPaint = (Window->Flags & WINDOWOBJECT_NEED_NCPAINT) ||
1003 (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT);
1004 IntUnLockWindowUpdate(Window);
1005
1006 if (bErase && Rect.left < Rect.right && Rect.top < Rect.bottom)
1007 {
1008 co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
1009 }
1010
1011 IntReleaseWindowObject(Window);
1012
1013 if (UnsafeRect != NULL)
1014 {
1015 Status = MmCopyToCaller(UnsafeRect, &Rect, sizeof(RECT));
1016 if (!NT_SUCCESS(Status))
1017 {
1018 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1019 RETURN( FALSE);
1020 }
1021 }
1022
1023 RETURN( (Rect.left < Rect.right && Rect.top < Rect.bottom) || AlwaysPaint);
1024
1025 CLEANUP:
1026 DPRINT("Leave NtUserGetUpdateRect, ret=%i\n",_ret_);
1027 UserLeave();
1028 END_CLEANUP;
1029 }
1030
1031 /*
1032 * NtUserRedrawWindow
1033 *
1034 * Status
1035 * @implemented
1036 */
1037
1038 BOOL STDCALL
1039 NtUserRedrawWindow(HWND hWnd, CONST RECT *lprcUpdate, HRGN hrgnUpdate,
1040 UINT flags)
1041 {
1042 RECT SafeUpdateRect;
1043 NTSTATUS Status;
1044 PWINDOW_OBJECT Wnd;
1045 DECLARE_RETURN(BOOL);
1046
1047 DPRINT("Enter NtUserRedrawWindow\n");
1048 UserEnterExclusive();
1049
1050 if (!(Wnd = IntGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow())))
1051 {
1052 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
1053 RETURN( FALSE);
1054 }
1055
1056 if (lprcUpdate != NULL)
1057 {
1058 Status = MmCopyFromCaller(&SafeUpdateRect, (PRECT)lprcUpdate,
1059 sizeof(RECT));
1060
1061 if (!NT_SUCCESS(Status))
1062 {
1063 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1064 IntReleaseWindowObject(Wnd);
1065 RETURN( FALSE);
1066 }
1067 }
1068
1069 Status = co_UserRedrawWindow(Wnd, NULL == lprcUpdate ? NULL : &SafeUpdateRect,
1070 hrgnUpdate, flags);
1071
1072 if (!NT_SUCCESS(Status))
1073 {
1074 /* IntRedrawWindow fails only in case that flags are invalid */
1075 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1076 IntReleaseWindowObject(Wnd);
1077 RETURN( FALSE);
1078 }
1079
1080 IntReleaseWindowObject(Wnd);
1081 RETURN( TRUE);
1082
1083 CLEANUP:
1084 DPRINT("Leave NtUserRedrawWindow, ret=%i\n",_ret_);
1085 UserLeave();
1086 END_CLEANUP;
1087 }
1088
1089
1090
1091 static
1092 DWORD FASTCALL
1093 UserScrollDC(HDC hDC, INT dx, INT dy, const RECT *lprcScroll,
1094 const RECT *lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate)
1095 {
1096 RECT rSrc, rClipped_src, rClip, rDst, offset;
1097 PDC DC;
1098
1099 /*
1100 * Compute device clipping region (in device coordinates).
1101 */
1102
1103 DC = DC_LockDc(hDC);
1104 if (NULL == DC)
1105 {
1106 return FALSE;
1107 }
1108 if (lprcScroll)
1109 rSrc = *lprcScroll;
1110 else
1111 IntGdiGetClipBox(hDC, &rSrc);
1112 IntLPtoDP(DC, (LPPOINT)&rSrc, 2);
1113
1114 if (lprcClip)
1115 rClip = *lprcClip;
1116 else
1117 IntGdiGetClipBox(hDC, &rClip);
1118 IntLPtoDP(DC, (LPPOINT)&rClip, 2);
1119
1120 IntGdiIntersectRect(&rClipped_src, &rSrc, &rClip);
1121
1122 rDst = rClipped_src;
1123 IntGdiSetRect(&offset, 0, 0, dx, dy);
1124 IntLPtoDP(DC, (LPPOINT)&offset, 2);
1125 IntGdiOffsetRect(&rDst, offset.right - offset.left, offset.bottom - offset.top);
1126 IntGdiIntersectRect(&rDst, &rDst, &rClip);
1127
1128 /*
1129 * Copy bits, if possible.
1130 */
1131
1132 if (rDst.bottom > rDst.top && rDst.right > rDst.left)
1133 {
1134 RECT rDst_lp = rDst, rSrc_lp = rDst;
1135
1136 IntGdiOffsetRect(&rSrc_lp, offset.left - offset.right, offset.top - offset.bottom);
1137 IntDPtoLP(DC, (LPPOINT)&rDst_lp, 2);
1138 IntDPtoLP(DC, (LPPOINT)&rSrc_lp, 2);
1139 DC_UnlockDc(DC);
1140
1141 if (!NtGdiBitBlt(hDC, rDst_lp.left, rDst_lp.top, rDst_lp.right - rDst_lp.left,
1142 rDst_lp.bottom - rDst_lp.top, hDC, rSrc_lp.left, rSrc_lp.top,
1143 SRCCOPY))
1144 return FALSE;
1145 }
1146 else
1147 {
1148 DC_UnlockDc(DC);
1149 }
1150
1151 /*
1152 * Compute update areas. This is the clipped source or'ed with the
1153 * unclipped source translated minus the clipped src translated (rDst)
1154 * all clipped to rClip.
1155 */
1156
1157 if (hrgnUpdate || lprcUpdate)
1158 {
1159 HRGN hRgn = hrgnUpdate, hRgn2;
1160
1161 if (hRgn)
1162 NtGdiSetRectRgn(hRgn, rClipped_src.left, rClipped_src.top, rClipped_src.right, rClipped_src.bottom);
1163 else
1164 hRgn = NtGdiCreateRectRgn(rClipped_src.left, rClipped_src.top, rClipped_src.right, rClipped_src.bottom);
1165
1166 hRgn2 = UnsafeIntCreateRectRgnIndirect(&rSrc);
1167 NtGdiOffsetRgn(hRgn2, offset.right - offset.left, offset.bottom - offset.top);
1168 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_OR);
1169
1170 NtGdiSetRectRgn(hRgn2, rDst.left, rDst.top, rDst.right, rDst.bottom);
1171 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_DIFF);
1172
1173 NtGdiSetRectRgn(hRgn2, rClip.left, rClip.top, rClip.right, rClip.bottom);
1174 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_AND);
1175
1176 if (lprcUpdate)
1177 {
1178 NtGdiGetRgnBox(hRgn, lprcUpdate);
1179
1180 /* Put the lprcUpdate in logical coordinate */
1181 NtGdiDPtoLP(hDC, (LPPOINT)lprcUpdate, 2);
1182 }
1183 if (!hrgnUpdate)
1184 NtGdiDeleteObject(hRgn);
1185 NtGdiDeleteObject(hRgn2);
1186 }
1187 return TRUE;
1188 }
1189
1190
1191
1192
1193 /*
1194 * NtUserScrollDC
1195 *
1196 * Status
1197 * @implemented
1198 */
1199
1200 DWORD STDCALL
1201 NtUserScrollDC(HDC hDC, INT dx, INT dy, const RECT *lprcScroll,
1202 const RECT *lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate)
1203 {
1204 DECLARE_RETURN(DWORD);
1205
1206 DPRINT("Enter NtUserScrollDC\n");
1207 UserEnterExclusive();
1208
1209 RETURN( UserScrollDC(hDC, dx, dy, lprcScroll, lprcClip, hrgnUpdate, lprcUpdate));
1210
1211 CLEANUP:
1212 DPRINT("Leave NtUserScrollDC, ret=%i\n",_ret_);
1213 UserLeave();
1214 END_CLEANUP;
1215
1216 }
1217
1218 /*
1219 * NtUserScrollWindowEx
1220 *
1221 * Status
1222 * @implemented
1223 */
1224
1225 DWORD STDCALL
1226 NtUserScrollWindowEx(HWND hWnd, INT dx, INT dy, const RECT *UnsafeRect,
1227 const RECT *UnsafeClipRect, HRGN hrgnUpdate, LPRECT rcUpdate, UINT flags)
1228 {
1229 RECT rc, cliprc, caretrc, rect, clipRect;
1230 INT Result;
1231 PWINDOW_OBJECT Window, CaretWnd;
1232 HDC hDC;
1233 HRGN hrgnTemp;
1234 HWND hwndCaret;
1235 BOOL bUpdate = (rcUpdate || hrgnUpdate || flags & (SW_INVALIDATE | SW_ERASE));
1236 BOOL bOwnRgn = TRUE;
1237 NTSTATUS Status;
1238 DECLARE_RETURN(DWORD);
1239
1240 DPRINT("Enter NtUserScrollWindowEx\n");
1241 UserEnterExclusive();
1242
1243 Window = IntGetWindowObject(hWnd);
1244 if (!Window || !IntIsWindowDrawable(Window))
1245 {
1246 IntReleaseWindowObject(Window);
1247 RETURN( ERROR);
1248 }
1249
1250 IntGetClientRect(Window, &rc);
1251 if (NULL != UnsafeRect)
1252 {
1253 Status = MmCopyFromCaller(&rect, UnsafeRect, sizeof(RECT));
1254 if (! NT_SUCCESS(Status))
1255 {
1256 SetLastNtError(Status);
1257 RETURN( ERROR);
1258 }
1259 IntGdiIntersectRect(&rc, &rc, &rect);
1260 }
1261
1262 if (NULL != UnsafeClipRect)
1263 {
1264 Status = MmCopyFromCaller(&clipRect, UnsafeClipRect, sizeof(RECT));
1265 if (! NT_SUCCESS(Status))
1266 {
1267 SetLastNtError(Status);
1268 RETURN( ERROR);
1269 }
1270 IntGdiIntersectRect(&cliprc, &rc, &clipRect);
1271 }
1272 else
1273 cliprc = rc;
1274
1275 if (cliprc.right <= cliprc.left || cliprc.bottom <= cliprc.top ||
1276 (dx == 0 && dy == 0))
1277 {
1278 RETURN( NULLREGION);
1279 }
1280
1281 caretrc = rc;
1282 hwndCaret = co_IntFixCaret(hWnd, &caretrc, flags);
1283
1284 if (hrgnUpdate)
1285 bOwnRgn = FALSE;
1286 else if (bUpdate)
1287 hrgnUpdate = NtGdiCreateRectRgn(0, 0, 0, 0);
1288
1289 hDC = UserGetDCEx(Window, 0, DCX_CACHE | DCX_USESTYLE);
1290 if (hDC)
1291 {
1292 UserScrollDC(hDC, dx, dy, &rc, &cliprc, hrgnUpdate, rcUpdate);
1293 UserReleaseDC(Window, hDC);
1294 }
1295
1296 /*
1297 * Take into account the fact that some damage may have occurred during
1298 * the scroll.
1299 */
1300
1301 hrgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
1302 Result = co_UserGetUpdateRgn(hWnd, hrgnTemp, FALSE);
1303 if (Result != NULLREGION)
1304 {
1305 HRGN hrgnClip = UnsafeIntCreateRectRgnIndirect(&cliprc);
1306 NtGdiOffsetRgn(hrgnTemp, dx, dy);
1307 NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnClip, RGN_AND);
1308 co_UserRedrawWindow(Window, NULL, hrgnTemp, RDW_INVALIDATE | RDW_ERASE);
1309 NtGdiDeleteObject(hrgnClip);
1310 }
1311 NtGdiDeleteObject(hrgnTemp);
1312
1313 if (flags & SW_SCROLLCHILDREN)
1314 {
1315 HWND *List = IntWinListChildren(Window);
1316 if (List)
1317 {
1318 int i;
1319 RECT r, dummy;
1320 POINT ClientOrigin;
1321 PWINDOW_OBJECT WindowObject;
1322
1323 IntGetClientOrigin(hWnd, &ClientOrigin);
1324 for (i = 0; List[i]; i++)
1325 {
1326 WindowObject = IntGetWindowObject(List[i]);
1327 if (!WindowObject) continue;
1328 r = WindowObject->WindowRect;
1329 r.left -= ClientOrigin.x;
1330 r.top -= ClientOrigin.y;
1331 r.right -= ClientOrigin.x;
1332 r.bottom -= ClientOrigin.y;
1333 IntReleaseWindowObject(WindowObject);
1334 if (! UnsafeRect || IntGdiIntersectRect(&dummy, &r, &rc))
1335 co_WinPosSetWindowPos(List[i], 0, r.left + dx, r.top + dy, 0, 0,
1336 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
1337 SWP_NOREDRAW);
1338 }
1339 ExFreePool(List);
1340 }
1341 }
1342
1343 if (flags & (SW_INVALIDATE | SW_ERASE))
1344 co_UserRedrawWindow(Window, NULL, hrgnUpdate, RDW_INVALIDATE | RDW_ERASE |
1345 ((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
1346 ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0));
1347
1348 if (bOwnRgn && hrgnUpdate)
1349 NtGdiDeleteObject(hrgnUpdate);
1350
1351 if ((CaretWnd = IntGetWindowObject(hwndCaret)))
1352 {
1353 co_IntSetCaretPos(caretrc.left + dx, caretrc.top + dy);
1354 co_UserShowCaret(CaretWnd);
1355 IntReleaseWindowObject(CaretWnd);
1356 }
1357
1358 IntReleaseWindowObject(Window);
1359
1360 RETURN( Result);
1361
1362 CLEANUP:
1363 DPRINT("Leave NtUserScrollWindowEx, ret=%i\n",_ret_);
1364 UserLeave();
1365 END_CLEANUP;
1366 }
1367
1368 /* EOF */