Fix race condition when one thread changes the update region of a window
[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: painting.c,v 1.67 2004/02/04 23:01:07 gvg Exp $
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 <ddk/ntddk.h>
34 #include <internal/safe.h>
35 #include <win32k/win32k.h>
36 #include <include/object.h>
37 #include <include/guicheck.h>
38 #include <include/window.h>
39 #include <include/desktop.h>
40 #include <include/winpos.h>
41 #include <include/class.h>
42 #include <include/caret.h>
43 #include <include/error.h>
44 #include <include/winsta.h>
45 #include <windows.h>
46 #include <include/painting.h>
47 #include <user32/wininternal.h>
48 #include <include/rect.h>
49 #include <win32k/coord.h>
50 #include <win32k/region.h>
51 #include <include/vis.h>
52 #include <include/intgdi.h>
53
54 #define NDEBUG
55 #include <win32k/debug1.h>
56
57 /* PRIVATE FUNCTIONS **********************************************************/
58
59 VOID FASTCALL
60 IntValidateParent(PWINDOW_OBJECT Child, HRGN ValidRegion)
61 {
62 HWND Parent;
63 PWINDOW_OBJECT ParentWindow;
64
65 Parent = NtUserGetAncestor(Child->Self, GA_PARENT);
66 while (Parent)
67 {
68 ParentWindow = IntGetWindowObject(Parent);
69 if (ParentWindow && !(ParentWindow->Style & WS_CLIPCHILDREN))
70 {
71 ExAcquireFastMutex(&ParentWindow->UpdateLock);
72 if (ParentWindow->UpdateRegion != 0)
73 {
74 INT OffsetX, OffsetY;
75
76 /*
77 * We must offset the child region by the offset of the
78 * child rect in the parent.
79 */
80 OffsetX = Child->WindowRect.left - ParentWindow->WindowRect.left;
81 OffsetY = Child->WindowRect.top - ParentWindow->WindowRect.top;
82 NtGdiOffsetRgn(ValidRegion, OffsetX, OffsetY);
83 NtGdiCombineRgn(ParentWindow->UpdateRegion, ParentWindow->UpdateRegion,
84 ValidRegion, RGN_DIFF);
85 /* FIXME: If the resulting region is empty, remove fake posted paint message */
86 NtGdiOffsetRgn(ValidRegion, -OffsetX, -OffsetY);
87 }
88 ExReleaseFastMutex(&ParentWindow->UpdateLock);
89 }
90 IntReleaseWindowObject(ParentWindow);
91 Parent = NtUserGetAncestor(Parent, GA_PARENT);
92 }
93 }
94
95 /*
96 * IntGetNCUpdateRegion
97 *
98 * Get nonclient part of window update region.
99 *
100 * Return Value
101 * Handle to region that represents invalid nonclient window area. The
102 * caller is responsible for deleting it.
103 *
104 * Remarks
105 * This function also marks the nonclient update region of window
106 * as valid, clears the WINDOWOBJECT_NEED_NCPAINT flag.
107 */
108
109 STATIC HRGN FASTCALL
110 IntGetNCUpdateRegion(PWINDOW_OBJECT Window)
111 {
112 HRGN WindowRgn;
113 HRGN NonclientRgn;
114
115 ASSERT(! ExTryToAcquireFastMutex(&Window->UpdateLock));
116
117 /*
118 * Generate the update region.
119 */
120
121 WindowRgn = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
122 NtGdiOffsetRgn(WindowRgn,
123 -Window->WindowRect.left,
124 -Window->WindowRect.top);
125 NonclientRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
126 if (NtGdiCombineRgn(NonclientRgn, Window->UpdateRegion,
127 WindowRgn, RGN_DIFF) == NULLREGION)
128 {
129 NtGdiDeleteObject(NonclientRgn);
130 NonclientRgn = NULL;
131 }
132
133 /*
134 * Remove the nonclient region from the standard update region.
135 */
136
137 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
138 WindowRgn, RGN_AND) == NULLREGION)
139 {
140 NtGdiDeleteObject(Window->UpdateRegion);
141 Window->UpdateRegion = NULL;
142 }
143
144 NtGdiDeleteObject(WindowRgn);
145
146 return NonclientRgn;
147 }
148
149 /*
150 * IntPaintWindows
151 *
152 * Internal function used by IntRedrawWindow.
153 */
154
155 STATIC VOID FASTCALL
156 IntPaintWindows(PWINDOW_OBJECT Window, ULONG Flags)
157 {
158 HDC hDC;
159 HWND hWnd = Window->Self;
160
161 if (! (Window->Style & WS_VISIBLE))
162 {
163 return;
164 }
165
166 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
167 {
168 if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
169 {
170 ExAcquireFastMutex(&Window->UpdateLock);
171 if (Window->NCUpdateRegion)
172 {
173 IntValidateParent(Window, Window->NCUpdateRegion);
174 }
175 IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)Window->NCUpdateRegion, 0);
176 Window->NCUpdateRegion = NULL;
177 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
178 MsqDecPaintCountQueue(Window->MessageQueue);
179 ExReleaseFastMutex(&Window->UpdateLock);
180 }
181
182 if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
183 {
184 if (Window->UpdateRegion)
185 {
186 IntValidateParent(Window, Window->UpdateRegion);
187 hDC = NtUserGetDCEx(hWnd, 0, DCX_CACHE | DCX_USESTYLE |
188 DCX_INTERSECTUPDATE);
189 if (hDC != NULL)
190 {
191 if (IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)hDC, 0))
192 {
193 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
194 }
195 NtUserReleaseDC(hWnd, hDC);
196 }
197 }
198 }
199
200 if (Flags & RDW_UPDATENOW)
201 {
202 if (Window->UpdateRegion != NULL ||
203 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
204 {
205 IntSendMessage(hWnd, WM_PAINT, 0, 0);
206 ExAcquireFastMutex(&Window->UpdateLock);
207 if (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
208 {
209 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
210 if (Window->UpdateRegion == NULL)
211 {
212 MsqDecPaintCountQueue(Window->MessageQueue);
213 }
214 }
215 ExReleaseFastMutex(&Window->UpdateLock);
216 }
217 }
218 }
219
220 /*
221 * Check that the window is still valid at this point
222 */
223
224 if (! IntIsWindow(hWnd))
225 {
226 return;
227 }
228
229 /*
230 * Paint child windows.
231 */
232 if (!(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
233 ((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN)))
234 {
235 HWND *List, *phWnd;
236
237 if ((List = IntWinListChildren(Window)))
238 {
239 for (phWnd = List; *phWnd; ++phWnd)
240 {
241 Window = IntGetWindowObject(*phWnd);
242 if (Window)
243 {
244 IntPaintWindows(Window, Flags);
245 IntReleaseWindowObject(Window);
246 }
247 }
248 ExFreePool(List);
249 }
250 }
251 }
252
253 /*
254 * IntInvalidateWindows
255 *
256 * Internal function used by IntRedrawWindow.
257 */
258
259 VOID FASTCALL
260 IntInvalidateWindows(PWINDOW_OBJECT Window, HRGN hRgn, ULONG Flags)
261 {
262 INT RgnType;
263 BOOL HadPaintMessage, HadNCPaintMessage;
264 BOOL HasPaintMessage, HasNCPaintMessage;
265 HRGN hRgnWindow;
266
267 /*
268 * Clip the given region with window rectangle (or region)
269 */
270
271 #ifdef TODO
272 if (!Window->WindowRegion)
273 #endif
274 {
275 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
276 NtGdiOffsetRgn(hRgnWindow,
277 -Window->WindowRect.left,
278 -Window->WindowRect.top);
279 RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnWindow, RGN_AND);
280 NtGdiDeleteObject(hRgnWindow);
281 }
282 #ifdef TODO
283 else
284 {
285 RgnType = NtGdiCombineRgn(hRgn, hRgn, Window->WindowRegion, RGN_AND);
286 }
287 #endif
288
289 /*
290 * Save current state of pending updates
291 */
292
293 ExAcquireFastMutex(&Window->UpdateLock);
294 HadPaintMessage = Window->UpdateRegion != NULL ||
295 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
296 HadNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
297
298 /*
299 * Update the region and flags
300 */
301
302 if (Flags & RDW_INVALIDATE && RgnType != NULLREGION)
303 {
304 if (Window->UpdateRegion == NULL)
305 {
306 Window->UpdateRegion = NtGdiCreateRectRgn(0, 0, 0, 0);
307 }
308
309 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
310 hRgn, RGN_OR) == NULLREGION)
311 {
312 NtGdiDeleteObject(Window->UpdateRegion);
313 Window->UpdateRegion = NULL;
314 }
315
316 if (Flags & RDW_FRAME)
317 Window->Flags |= WINDOWOBJECT_NEED_NCPAINT;
318 if (Flags & RDW_ERASE)
319 Window->Flags |= WINDOWOBJECT_NEED_ERASEBKGND;
320
321 Flags |= RDW_FRAME;
322 }
323
324 if (Flags & RDW_VALIDATE && RgnType != NULLREGION)
325 {
326 if (Window->UpdateRegion != NULL)
327 {
328 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
329 hRgn, RGN_DIFF) == NULLREGION)
330 {
331 NtGdiDeleteObject(Window->UpdateRegion);
332 Window->UpdateRegion = NULL;
333 }
334 }
335
336 if (Window->UpdateRegion == NULL)
337 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
338 if (Flags & RDW_NOFRAME)
339 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
340 if (Flags & RDW_NOERASE)
341 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
342 }
343
344 if (Flags & RDW_INTERNALPAINT)
345 {
346 Window->Flags |= WINDOWOBJECT_NEED_INTERNALPAINT;
347 }
348
349 if (Flags & RDW_NOINTERNALPAINT)
350 {
351 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
352 }
353
354 /*
355 * Split the nonclient update region.
356 */
357
358 if (Window->NCUpdateRegion == NULL)
359 {
360 Window->NCUpdateRegion = IntGetNCUpdateRegion(Window);
361 }
362 else
363 {
364 HRGN hRgnNonClient = IntGetNCUpdateRegion(Window);
365 NtGdiCombineRgn(Window->NCUpdateRegion, Window->NCUpdateRegion,
366 hRgnNonClient, RGN_OR);
367 NtGdiDeleteObject(hRgnNonClient);
368 }
369
370 /*
371 * Process children if needed
372 */
373
374 if (!(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
375 ((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN)))
376 {
377 HWND *List, *phWnd;
378 PWINDOW_OBJECT Child;
379
380 if ((List = IntWinListChildren(Window)))
381 {
382 for (phWnd = List; *phWnd; ++phWnd)
383 {
384 Child = IntGetWindowObject(*phWnd);
385 if(!Child)
386 {
387 continue;
388 }
389 if (Child->Style & WS_VISIBLE)
390 {
391 /*
392 * Recursive call to update children UpdateRegion
393 */
394 HRGN hRgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
395 NtGdiCombineRgn(hRgnTemp, hRgn, 0, RGN_COPY);
396 NtGdiOffsetRgn(hRgnTemp,
397 Window->WindowRect.left - Child->WindowRect.left,
398 Window->WindowRect.top - Child->WindowRect.top);
399 IntInvalidateWindows(Child, hRgnTemp, Flags);
400 NtGdiDeleteObject(hRgnTemp);
401 }
402 IntReleaseWindowObject(Child);
403 }
404 ExFreePool(List);
405 }
406 }
407
408 /*
409 * Fake post paint messages to window message queue if needed
410 */
411 HasPaintMessage = Window->UpdateRegion != NULL ||
412 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
413 HasNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
414
415 if (HasPaintMessage != HadPaintMessage)
416 {
417 if (HadPaintMessage)
418 MsqDecPaintCountQueue(Window->MessageQueue);
419 else
420 MsqIncPaintCountQueue(Window->MessageQueue);
421 }
422
423 if (HasNCPaintMessage != HadNCPaintMessage)
424 {
425 if (HadNCPaintMessage)
426 MsqDecPaintCountQueue(Window->MessageQueue);
427 else
428 MsqIncPaintCountQueue(Window->MessageQueue);
429 }
430
431 ExReleaseFastMutex(&Window->UpdateLock);
432 }
433
434 /*
435 * IntIsWindowDrawable
436 *
437 * Remarks
438 * Window is drawable when it is visible and all parents are not
439 * minimized.
440 */
441
442 BOOL FASTCALL
443 IntIsWindowDrawable(PWINDOW_OBJECT Window)
444 {
445 PWINDOW_OBJECT Wnd = Window;
446
447 for (; Wnd; Wnd = Wnd->Parent)
448 {
449 if (!(Wnd->Style & WS_VISIBLE) ||
450 ((Wnd->Style & WS_MINIMIZE) && (Wnd != Window)))
451 return FALSE;
452 }
453
454 return TRUE;
455 }
456
457 /*
458 * IntRedrawWindow
459 *
460 * Internal version of NtUserRedrawWindow that takes WINDOW_OBJECT as
461 * first parameter.
462 */
463
464 BOOL FASTCALL
465 IntRedrawWindow(PWINDOW_OBJECT Window, const RECT* UpdateRect, HRGN UpdateRgn,
466 ULONG Flags)
467 {
468 HRGN hRgn = NULL;
469
470 /*
471 * Step 1.
472 * Validation of passed parameters.
473 */
474
475 if (!IntIsWindowDrawable(Window) ||
476 (Flags & (RDW_VALIDATE | RDW_INVALIDATE)) ==
477 (RDW_VALIDATE | RDW_INVALIDATE))
478 {
479 return FALSE;
480 }
481
482 /*
483 * Step 2.
484 * Transform the parameters UpdateRgn and UpdateRect into
485 * a region hRgn specified in window coordinates.
486 */
487
488 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE))
489 {
490 if (UpdateRgn != NULL)
491 {
492 hRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
493 NtGdiCombineRgn(hRgn, UpdateRgn, NULL, RGN_COPY);
494 NtGdiOffsetRgn(hRgn,
495 Window->ClientRect.left - Window->WindowRect.left,
496 Window->ClientRect.top - Window->WindowRect.top);
497 } else
498 if (UpdateRect != NULL)
499 {
500 hRgn = UnsafeIntCreateRectRgnIndirect((RECT *)UpdateRect);
501 NtGdiOffsetRgn(hRgn,
502 Window->ClientRect.left - Window->WindowRect.left,
503 Window->ClientRect.top - Window->WindowRect.top);
504 } else
505 if ((Flags & (RDW_INVALIDATE | RDW_FRAME)) == (RDW_INVALIDATE | RDW_FRAME) ||
506 (Flags & (RDW_VALIDATE | RDW_NOFRAME)) == (RDW_VALIDATE | RDW_NOFRAME))
507 {
508 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
509 NtGdiOffsetRgn(hRgn,
510 -Window->WindowRect.left,
511 -Window->WindowRect.top);
512 }
513 else
514 {
515 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
516 NtGdiOffsetRgn(hRgn,
517 -Window->WindowRect.left,
518 -Window->WindowRect.top);
519 }
520 }
521
522 /*
523 * Step 3.
524 * Adjust the window update region depending on hRgn and flags.
525 */
526
527 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT))
528 {
529 IntInvalidateWindows(Window, hRgn, Flags);
530 }
531
532 /*
533 * Step 4.
534 * Repaint and erase windows if needed.
535 */
536
537 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
538 {
539 IntPaintWindows(Window, Flags);
540 }
541
542 /*
543 * Step 5.
544 * Cleanup ;-)
545 */
546
547 if (hRgn != NULL)
548 {
549 NtGdiDeleteObject(hRgn);
550 }
551
552 return TRUE;
553 }
554
555 HWND STDCALL
556 IntFindWindowToRepaint(HWND hWnd, PW32THREAD Thread)
557 {
558 PWINDOW_OBJECT Window;
559 PWINDOW_OBJECT Child;
560 HWND hFoundWnd = NULL;
561
562 Window = IntGetWindowObject(hWnd);
563 if (Window == NULL)
564 return NULL;
565
566 if ((Window->UpdateRegion != NULL ||
567 Window->Flags & (WINDOWOBJECT_NEED_INTERNALPAINT | WINDOWOBJECT_NEED_NCPAINT)) &&
568 IntWndBelongsToThread(Window, Thread))
569 {
570 IntReleaseWindowObject(Window);
571 return hWnd;
572 }
573
574 ExAcquireFastMutex(&Window->ChildrenListLock);
575
576 for (Child = Window->FirstChild; Child; Child = Child->NextSibling)
577 {
578 if (Child->Style & WS_VISIBLE &&
579 (Child->UpdateRegion != NULL ||
580 Child->Flags & WINDOWOBJECT_NEED_INTERNALPAINT ||
581 Child->Flags & WINDOWOBJECT_NEED_NCPAINT)
582 && IntWndBelongsToThread(Child, Thread))
583 {
584 hFoundWnd = Child->Self;
585 break;
586 }
587 }
588
589 if (hFoundWnd == NULL)
590 {
591 for (Child = Window->FirstChild; Child; Child = Child->NextSibling)
592 {
593 if (Child->Style & WS_VISIBLE)
594 {
595 hFoundWnd = IntFindWindowToRepaint(Child->Self, Thread);
596 if (hFoundWnd != NULL)
597 break;
598 }
599 }
600 }
601
602 ExReleaseFastMutex(&Window->ChildrenListLock);
603 IntReleaseWindowObject(Window);
604
605 return hFoundWnd;
606 }
607
608 BOOL FASTCALL
609 IntGetPaintMessage(HWND hWnd, PW32THREAD Thread, MSG *Message,
610 BOOL Remove)
611 {
612 PWINDOW_OBJECT Window;
613 PUSER_MESSAGE_QUEUE MessageQueue = (PUSER_MESSAGE_QUEUE)Thread->MessageQueue;
614
615 if (!MessageQueue->PaintPosted)
616 return FALSE;
617
618 if (hWnd)
619 Message->hwnd = IntFindWindowToRepaint(hWnd, PsGetWin32Thread());
620 else
621 Message->hwnd = IntFindWindowToRepaint(IntGetDesktopWindow(), PsGetWin32Thread());
622
623 if (Message->hwnd == NULL)
624 {
625 #if 0
626 DPRINT1("PAINTING BUG: Thread marked as containing dirty windows, but no dirty windows found!\n");
627 #endif
628 ExAcquireFastMutex(&MessageQueue->Lock);
629 MessageQueue->PaintPosted = 0;
630 MessageQueue->PaintCount = 0;
631 ExReleaseFastMutex(&MessageQueue->Lock);
632 return FALSE;
633 }
634
635 Window = IntGetWindowObject(Message->hwnd);
636 if (Window != NULL)
637 {
638 ExAcquireFastMutex(&Window->UpdateLock);
639 if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
640 {
641 Message->message = WM_NCPAINT;
642 Message->wParam = (WPARAM)Window->NCUpdateRegion;
643 Message->lParam = 0;
644 if (Remove)
645 {
646 Window->NCUpdateRegion = NULL;
647 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
648 MsqDecPaintCountQueue(Window->MessageQueue);
649 }
650 } else
651 {
652 Message->message = WM_PAINT;
653 Message->wParam = Message->lParam = 0;
654 if (Remove && Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
655 {
656 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
657 if (Window->UpdateRegion == NULL)
658 {
659 MsqDecPaintCountQueue(Window->MessageQueue);
660 }
661 }
662 }
663 ExReleaseFastMutex(&Window->UpdateLock);
664
665 IntReleaseWindowObject(Window);
666 return TRUE;
667 }
668
669 return FALSE;
670 }
671
672 HWND FASTCALL
673 IntFixCaret(HWND hWnd, LPRECT lprc, UINT flags)
674 {
675 PDESKTOP_OBJECT Desktop;
676 PTHRDCARETINFO CaretInfo;
677 HWND hWndCaret;
678
679 Desktop = PsGetCurrentThread()->Win32Thread->Desktop;
680 CaretInfo = ((PUSER_MESSAGE_QUEUE)Desktop->ActiveMessageQueue)->CaretInfo;
681 hWndCaret = CaretInfo->hWnd;
682 if (hWndCaret == hWnd ||
683 ((flags & SW_SCROLLCHILDREN) && IntIsChildWindow(hWnd, hWndCaret)))
684 {
685 POINT pt, FromOffset, ToOffset, Offset;
686 RECT rcCaret;
687
688 pt.x = CaretInfo->Pos.x;
689 pt.y = CaretInfo->Pos.y;
690 IntGetClientOrigin(hWndCaret, &FromOffset);
691 IntGetClientOrigin(hWnd, &ToOffset);
692 Offset.x = FromOffset.x - ToOffset.x;
693 Offset.y = FromOffset.y - ToOffset.y;
694 rcCaret.left = pt.x;
695 rcCaret.top = pt.y;
696 rcCaret.right = pt.x + CaretInfo->Size.cx;
697 rcCaret.bottom = pt.y + CaretInfo->Size.cy;
698 if (NtGdiIntersectRect(lprc, lprc, &rcCaret))
699 {
700 NtUserHideCaret(0);
701 lprc->left = pt.x;
702 lprc->top = pt.y;
703 return hWndCaret;
704 }
705 }
706
707 return 0;
708 }
709
710 /* PUBLIC FUNCTIONS ***********************************************************/
711
712 /*
713 * NtUserBeginPaint
714 *
715 * Status
716 * @implemented
717 */
718
719 HDC STDCALL
720 NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* lPs)
721 {
722 PWINDOW_OBJECT Window;
723
724 if (!(Window = IntGetWindowObject(hWnd)))
725 {
726 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
727 return NULL;
728 }
729
730 NtUserHideCaret(hWnd);
731
732 lPs->hdc = NtUserGetDCEx(hWnd, 0, DCX_INTERSECTUPDATE | DCX_WINDOWPAINT |
733 DCX_USESTYLE);
734
735 if (!lPs->hdc)
736 {
737 IntReleaseWindowObject(Window);
738 return NULL;
739 }
740
741 ExAcquireFastMutex(&Window->UpdateLock);
742 if (Window->UpdateRegion != NULL)
743 {
744 MsqDecPaintCountQueue(Window->MessageQueue);
745 IntValidateParent(Window, Window->UpdateRegion);
746 NtGdiGetRgnBox(Window->UpdateRegion, &lPs->rcPaint);
747 NtGdiOffsetRect(&lPs->rcPaint,
748 Window->WindowRect.left - Window->ClientRect.left,
749 Window->WindowRect.top - Window->ClientRect.top);
750 NtGdiDeleteObject(Window->UpdateRegion);
751 Window->UpdateRegion = NULL;
752 }
753 else
754 {
755 NtUserGetClientRect(Window, &lPs->rcPaint);
756 }
757 ExReleaseFastMutex(&Window->UpdateLock);
758
759 if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
760 {
761 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
762 lPs->fErase = !IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)lPs->hdc, 0);
763 }
764 else
765 {
766 lPs->fErase = FALSE;
767 }
768
769 IntReleaseWindowObject(Window);
770
771 return lPs->hdc;
772 }
773
774 /*
775 * NtUserEndPaint
776 *
777 * Status
778 * @implemented
779 */
780
781 BOOL STDCALL
782 NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* lPs)
783 {
784 NtUserReleaseDC(hWnd, lPs->hdc);
785 NtUserShowCaret(hWnd);
786
787 return TRUE;
788 }
789
790 /*
791 * NtUserInvalidateRect
792 *
793 * Status
794 * @implemented
795 */
796
797 DWORD STDCALL
798 NtUserInvalidateRect(HWND hWnd, CONST RECT *Rect, BOOL Erase)
799 {
800 return NtUserRedrawWindow(hWnd, Rect, 0, RDW_INVALIDATE | (Erase ? RDW_ERASE : 0));
801 }
802
803 /*
804 * NtUserInvalidateRgn
805 *
806 * Status
807 * @implemented
808 */
809
810 DWORD STDCALL
811 NtUserInvalidateRgn(HWND hWnd, HRGN Rgn, BOOL Erase)
812 {
813 return NtUserRedrawWindow(hWnd, NULL, Rgn, RDW_INVALIDATE | (Erase ? RDW_ERASE : 0));
814 }
815
816 /*
817 * NtUserValidateRgn
818 *
819 * Status
820 * @implemented
821 */
822
823 BOOL STDCALL
824 NtUserValidateRgn(HWND hWnd, HRGN hRgn)
825 {
826 return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_VALIDATE | RDW_NOCHILDREN);
827 }
828
829 /*
830 * NtUserUpdateWindow
831 *
832 * Status
833 * @implemented
834 */
835
836 BOOL STDCALL
837 NtUserUpdateWindow(HWND hWnd)
838 {
839 return NtUserRedrawWindow(hWnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN);
840 }
841
842 /*
843 * NtUserGetUpdateRgn
844 *
845 * Status
846 * @implemented
847 */
848
849 INT STDCALL
850 NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
851 {
852 PWINDOW_OBJECT Window;
853 int RegionType;
854
855 if (!(Window = IntGetWindowObject(hWnd)))
856 {
857 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
858 return ERROR;
859 }
860
861 if (Window->UpdateRegion == NULL)
862 {
863 RegionType = (NtGdiSetRectRgn(hRgn, 0, 0, 0, 0) ? NULLREGION : ERROR);
864 }
865 else
866 {
867 RegionType = NtGdiCombineRgn(hRgn, Window->UpdateRegion, hRgn, RGN_COPY);
868 NtGdiOffsetRgn(
869 hRgn,
870 Window->WindowRect.left - Window->ClientRect.left,
871 Window->WindowRect.top - Window->ClientRect.top);
872 }
873
874 IntReleaseWindowObject(Window);
875
876 if (bErase && RegionType != NULLREGION && RegionType != ERROR)
877 {
878 NtUserRedrawWindow(hWnd, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
879 }
880
881 return RegionType;
882 }
883
884 /*
885 * NtUserGetUpdateRect
886 *
887 * Status
888 * @implemented
889 */
890
891 BOOL STDCALL
892 NtUserGetUpdateRect(HWND hWnd, LPRECT lpRect, BOOL fErase)
893 {
894 HRGN hRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
895
896 if (!lpRect)
897 {
898 SetLastWin32Error(ERROR_INVALID_PARAMETER);
899 return FALSE;
900 }
901
902 NtUserGetUpdateRgn(hWnd, hRgn, fErase);
903 NtGdiGetRgnBox(hRgn, lpRect);
904 NtGdiDeleteObject(hRgn);
905
906 return lpRect->left < lpRect->right && lpRect->top < lpRect->bottom;
907 }
908
909 /*
910 * NtUserRedrawWindow
911 *
912 * Status
913 * @implemented
914 */
915
916 BOOL STDCALL
917 NtUserRedrawWindow(HWND hWnd, CONST RECT *lprcUpdate, HRGN hrgnUpdate,
918 UINT flags)
919 {
920 RECT SafeUpdateRect;
921 NTSTATUS Status;
922 PWINDOW_OBJECT Wnd;
923
924 if (!(Wnd = IntGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow())))
925 {
926 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
927 return FALSE;
928 }
929
930 if (lprcUpdate != NULL)
931 {
932 Status = MmCopyFromCaller(&SafeUpdateRect, (PRECT)lprcUpdate,
933 sizeof(RECT));
934
935 if (!NT_SUCCESS(Status))
936 {
937 SetLastWin32Error(ERROR_INVALID_PARAMETER);
938 return FALSE;
939 }
940 }
941
942 Status = IntRedrawWindow(Wnd, NULL == lprcUpdate ? NULL : &SafeUpdateRect,
943 hrgnUpdate, flags);
944
945 if (!NT_SUCCESS(Status))
946 {
947 /* IntRedrawWindow fails only in case that flags are invalid */
948 SetLastWin32Error(ERROR_INVALID_PARAMETER);
949 return FALSE;
950 }
951
952 return TRUE;
953 }
954
955 /*
956 * NtUserScrollDC
957 *
958 * Status
959 * @implemented
960 */
961
962 DWORD STDCALL
963 NtUserScrollDC(HDC hDC, INT dx, INT dy, const RECT *lprcScroll,
964 const RECT *lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate)
965 {
966 RECT rSrc, rClipped_src, rClip, rDst, offset;
967 PDC DC;
968
969 /*
970 * Compute device clipping region (in device coordinates).
971 */
972
973 DC = DC_LockDc(hDC);
974 if (NULL == DC)
975 {
976 return FALSE;
977 }
978 if (lprcScroll)
979 rSrc = *lprcScroll;
980 else
981 IntGdiGetClipBox(hDC, &rSrc);
982 IntLPtoDP(DC, (LPPOINT)&rSrc, 2);
983
984 if (lprcClip)
985 rClip = *lprcClip;
986 else
987 IntGdiGetClipBox(hDC, &rClip);
988 IntLPtoDP(DC, (LPPOINT)&rClip, 2);
989
990 NtGdiIntersectRect(&rClipped_src, &rSrc, &rClip);
991
992 rDst = rClipped_src;
993 NtGdiSetRect(&offset, 0, 0, dx, dy);
994 IntLPtoDP(DC, (LPPOINT)&offset, 2);
995 NtGdiOffsetRect(&rDst, offset.right - offset.left, offset.bottom - offset.top);
996 NtGdiIntersectRect(&rDst, &rDst, &rClip);
997
998 /*
999 * Copy bits, if possible.
1000 */
1001
1002 if (rDst.bottom > rDst.top && rDst.right > rDst.left)
1003 {
1004 RECT rDst_lp = rDst, rSrc_lp = rDst;
1005
1006 NtGdiOffsetRect(&rSrc_lp, offset.left - offset.right, offset.top - offset.bottom);
1007 IntDPtoLP(DC, (LPPOINT)&rDst_lp, 2);
1008 IntDPtoLP(DC, (LPPOINT)&rSrc_lp, 2);
1009 DC_UnlockDc(hDC);
1010
1011 if (!NtGdiBitBlt(hDC, rDst_lp.left, rDst_lp.top, rDst_lp.right - rDst_lp.left,
1012 rDst_lp.bottom - rDst_lp.top, hDC, rSrc_lp.left, rSrc_lp.top,
1013 SRCCOPY))
1014 return FALSE;
1015 }
1016 else
1017 {
1018 DC_UnlockDc(hDC);
1019 }
1020
1021 /*
1022 * Compute update areas. This is the clipped source or'ed with the
1023 * unclipped source translated minus the clipped src translated (rDst)
1024 * all clipped to rClip.
1025 */
1026
1027 if (hrgnUpdate || lprcUpdate)
1028 {
1029 HRGN hRgn = hrgnUpdate, hRgn2;
1030
1031 if (hRgn)
1032 NtGdiSetRectRgn(hRgn, rClipped_src.left, rClipped_src.top, rClipped_src.right, rClipped_src.bottom);
1033 else
1034 hRgn = NtGdiCreateRectRgn(rClipped_src.left, rClipped_src.top, rClipped_src.right, rClipped_src.bottom);
1035
1036 hRgn2 = UnsafeIntCreateRectRgnIndirect(&rSrc);
1037 NtGdiOffsetRgn(hRgn2, offset.right - offset.left, offset.bottom - offset.top);
1038 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_OR);
1039
1040 NtGdiSetRectRgn(hRgn2, rDst.left, rDst.top, rDst.right, rDst.bottom);
1041 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_DIFF);
1042
1043 NtGdiSetRectRgn(hRgn2, rClip.left, rClip.top, rClip.right, rClip.bottom);
1044 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_AND);
1045
1046 if (lprcUpdate)
1047 {
1048 NtGdiGetRgnBox(hRgn, lprcUpdate);
1049
1050 /* Put the lprcUpdate in logical coordinate */
1051 NtGdiDPtoLP(hDC, (LPPOINT)lprcUpdate, 2);
1052 }
1053 if (!hrgnUpdate)
1054 NtGdiDeleteObject(hRgn);
1055 NtGdiDeleteObject(hRgn2);
1056 }
1057 return TRUE;
1058 }
1059
1060 /*
1061 * NtUserScrollWindowEx
1062 *
1063 * Status
1064 * @implemented
1065 */
1066
1067 DWORD STDCALL
1068 NtUserScrollWindowEx(HWND hWnd, INT dx, INT dy, const RECT *rect,
1069 const RECT *clipRect, HRGN hrgnUpdate, LPRECT rcUpdate, UINT flags)
1070 {
1071 RECT rc, cliprc, caretrc;
1072 INT Result;
1073 PWINDOW_OBJECT Window;
1074 HDC hDC;
1075 HRGN hrgnTemp;
1076 HWND hwndCaret;
1077 BOOL bUpdate = (rcUpdate || hrgnUpdate || flags & (SW_INVALIDATE | SW_ERASE));
1078 BOOL bOwnRgn = TRUE;
1079
1080 Window = IntGetWindowObject(hWnd);
1081 if (!Window || !IntIsWindowDrawable(Window))
1082 {
1083 IntReleaseWindowObject(Window);
1084 return ERROR;
1085 }
1086
1087 IntGetClientRect(Window, &rc);
1088 if (rect)
1089 NtGdiIntersectRect(&rc, &rc, rect);
1090
1091 if (clipRect)
1092 NtGdiIntersectRect(&cliprc, &rc, clipRect);
1093 else
1094 cliprc = rc;
1095
1096 if (cliprc.right <= cliprc.left || cliprc.bottom <= cliprc.top ||
1097 (dx == 0 && dy == 0))
1098 {
1099 return NULLREGION;
1100 }
1101
1102 caretrc = rc;
1103 hwndCaret = IntFixCaret(hWnd, &caretrc, flags);
1104
1105 if (hrgnUpdate)
1106 bOwnRgn = FALSE;
1107 else if (bUpdate)
1108 hrgnUpdate = NtGdiCreateRectRgn(0, 0, 0, 0);
1109
1110 hDC = NtUserGetDCEx(hWnd, 0, DCX_CACHE | DCX_USESTYLE);
1111 if (hDC)
1112 {
1113 NtUserScrollDC(hDC, dx, dy, &rc, &cliprc, hrgnUpdate, rcUpdate);
1114 NtUserReleaseDC(hWnd, hDC);
1115 }
1116
1117 /*
1118 * Take into account the fact that some damage may have occurred during
1119 * the scroll.
1120 */
1121
1122 hrgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
1123 Result = NtUserGetUpdateRgn(hWnd, hrgnTemp, FALSE);
1124 if (Result != NULLREGION)
1125 {
1126 HRGN hrgnClip = UnsafeIntCreateRectRgnIndirect(&cliprc);
1127 NtGdiOffsetRgn(hrgnTemp, dx, dy);
1128 NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnClip, RGN_AND);
1129 NtUserRedrawWindow(hWnd, NULL, hrgnTemp, RDW_INVALIDATE | RDW_ERASE);
1130 NtGdiDeleteObject(hrgnClip);
1131 }
1132 NtGdiDeleteObject(hrgnTemp);
1133
1134 if (flags & SW_SCROLLCHILDREN)
1135 {
1136 HWND *List = IntWinListChildren(Window);
1137 if (List)
1138 {
1139 int i;
1140 RECT r, dummy;
1141 POINT ClientOrigin;
1142
1143 for (i = 0; List[i]; i++)
1144 {
1145 NtUserGetWindowRect(List[i], &r);
1146 IntGetClientOrigin(hWnd, &ClientOrigin);
1147 r.left -= ClientOrigin.x;
1148 r.top -= ClientOrigin.y;
1149 r.right -= ClientOrigin.x;
1150 r.bottom -= ClientOrigin.y;
1151 if (!rect || NtGdiIntersectRect(&dummy, &r, &rc))
1152 WinPosSetWindowPos(List[i], 0, r.left + dx, r.top + dy, 0, 0,
1153 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
1154 SWP_NOREDRAW);
1155 }
1156 ExFreePool(List);
1157 }
1158 }
1159
1160 if (flags & (SW_INVALIDATE | SW_ERASE))
1161 NtUserRedrawWindow(hWnd, NULL, hrgnUpdate, RDW_INVALIDATE | RDW_ERASE |
1162 ((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
1163 ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0));
1164
1165 if (bOwnRgn && hrgnUpdate)
1166 NtGdiDeleteObject(hrgnUpdate);
1167
1168 if (hwndCaret)
1169 {
1170 IntSetCaretPos(caretrc.left + dx, caretrc.top + dy);
1171 NtUserShowCaret(hwndCaret);
1172 }
1173
1174 IntReleaseWindowObject(Window);
1175
1176 return Result;
1177 }
1178
1179 /* EOF */