- Fixed multiwin.exe painting problem.
[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.38 2003/11/21 21:12:08 navaraf 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/class.h>
40 #include <include/error.h>
41 #include <include/winsta.h>
42 #include <windows.h>
43 #include <include/painting.h>
44 #include <user32/wininternal.h>
45 #include <include/rect.h>
46 #include <win32k/coord.h>
47 #include <win32k/region.h>
48 #include <include/vis.h>
49
50 #define NDEBUG
51 #include <debug.h>
52
53 /* GLOBALS ********************************************************************/
54
55 /*
56 * Define this after the desktop will be moved to CSRSS and will
57 * get proper message queue.
58 */
59 /* #define DESKTOP_IN_CSRSS */
60
61 /* PRIVATE FUNCTIONS **********************************************************/
62
63 VOID FASTCALL
64 IntValidateParent(PWINDOW_OBJECT Child)
65 {
66 HWND Parent;
67 PWINDOW_OBJECT ParentWindow;
68
69 Parent = NtUserGetAncestor(Child->Self, GA_PARENT);
70 while (Parent && Parent != IntGetDesktopWindow())
71 {
72 ParentWindow = IntGetWindowObject(Parent);
73 if (ParentWindow && !(ParentWindow->Style & WS_CLIPCHILDREN))
74 {
75 if (ParentWindow->UpdateRegion != 0)
76 {
77 INT OffsetX, OffsetY;
78
79 /*
80 * We must offset the child region by the offset of the
81 * child rect in the parent.
82 */
83 OffsetX = Child->WindowRect.left - ParentWindow->WindowRect.left;
84 OffsetY = Child->WindowRect.top - ParentWindow->WindowRect.top;
85 NtGdiOffsetRgn(Child->UpdateRegion, OffsetX, OffsetY );
86 NtGdiCombineRgn(ParentWindow->UpdateRegion, ParentWindow->UpdateRegion,
87 Child->UpdateRegion, RGN_DIFF);
88 /* FIXME: If the resulting region is empty, remove fake posted paint message */
89 NtGdiOffsetRgn(Child->UpdateRegion, -OffsetX, -OffsetY);
90 }
91 }
92 IntReleaseWindowObject(ParentWindow);
93 Parent = NtUserGetAncestor(Parent, GA_PARENT);
94 }
95 }
96
97 /*
98 * IntGetNCUpdateRegion
99 *
100 * Get nonclient part of window update region.
101 *
102 * Return Value
103 * Handle to region that represents invalid nonclient window area. The
104 * caller is responsible for deleting it.
105 *
106 * Remarks
107 * This function also marks the nonclient update region of window
108 * as valid, clears the WINDOWOBJECT_NEED_NCPAINT flag and removes
109 * the fake paint message from message queue if the Remove is set
110 * to TRUE.
111 */
112
113 HRGN FASTCALL
114 IntGetNCUpdateRegion(PWINDOW_OBJECT Window, BOOL Remove)
115 {
116 HRGN WindowRgn;
117 HRGN NonclientRgn;
118
119 WindowRgn = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
120 NtGdiOffsetRgn(WindowRgn,
121 -Window->WindowRect.left,
122 -Window->WindowRect.top);
123 NonclientRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
124 if (NtGdiCombineRgn(NonclientRgn, Window->UpdateRegion,
125 WindowRgn, RGN_DIFF) == NULLREGION)
126 {
127 NtGdiDeleteObject(NonclientRgn);
128 NonclientRgn = NULL;
129 }
130 if (Remove)
131 {
132 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
133 WindowRgn, RGN_AND) == NULLREGION)
134 {
135 NtGdiDeleteObject(Window->UpdateRegion);
136 Window->UpdateRegion = NULL;
137 }
138 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
139 MsqDecPaintCountQueue(Window->MessageQueue);
140 }
141
142 return NonclientRgn;
143 }
144
145 /*
146 * IntPaintWindows
147 *
148 * Internal function used by IntRedrawWindow.
149 */
150
151 VOID FASTCALL
152 IntPaintWindows(PWINDOW_OBJECT Window, ULONG Flags)
153 {
154 HDC hDC;
155 HWND hWnd = Window->Self;
156
157 if (!(Window->Style & WS_VISIBLE))
158 {
159 return;
160 }
161
162 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
163 {
164 if (IntIsDesktopWindow(Window))
165 {
166 /*
167 * Repainting of desktop window
168 */
169
170 #ifndef DESKTOP_IN_CSRSS
171 VIS_RepaintDesktop(hWnd, Window->UpdateRegion);
172 Window->Flags &= ~(WINDOWOBJECT_NEED_NCPAINT |
173 WINDOWOBJECT_NEED_INTERNALPAINT | WINDOWOBJECT_NEED_ERASEBKGND);
174 NtGdiDeleteObject(Window->UpdateRegion);
175 Window->UpdateRegion = NULL;
176 #else
177 if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
178 {
179 MsqDecPaintCountQueue(Window->MessageQueue);
180 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
181 }
182 if (Window->UpdateRegion ||
183 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
184 {
185 MsqDecPaintCountQueue(Window->MessageQueue);
186 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
187 }
188 if (Window->UpdateRegion)
189 {
190 hDC = NtUserGetDCEx(hWnd, 0, DCX_CACHE | DCX_USESTYLE |
191 DCX_INTERSECTUPDATE);
192 if (hDC != NULL)
193 {
194 NtUserSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)hDC, 0);
195 NtUserReleaseDC(hWnd, hDC);
196 NtGdiDeleteObject(Window->UpdateRegion);
197 Window->UpdateRegion = NULL;
198 }
199 }
200 #endif
201 }
202 else
203 {
204 /*
205 * Repainting of non-desktop window
206 */
207
208 if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
209 {
210 NtUserSendMessage(hWnd, WM_NCPAINT, (WPARAM)IntGetNCUpdateRegion(Window, TRUE), 0);
211 }
212
213 if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
214 {
215 if (Window->UpdateRegion)
216 {
217 hDC = NtUserGetDCEx(hWnd, 0, DCX_CACHE | DCX_USESTYLE |
218 DCX_INTERSECTUPDATE);
219 if (hDC != NULL)
220 {
221 if (NtUserSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)hDC, 0))
222 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
223 NtUserReleaseDC(hWnd, hDC);
224 }
225 }
226 }
227
228 if (Flags & RDW_UPDATENOW)
229 {
230 if (Window->UpdateRegion != NULL ||
231 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
232 {
233 NtUserSendMessage(hWnd, WM_PAINT, 0, 0);
234 if (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
235 {
236 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
237 if (Window->UpdateRegion == NULL)
238 {
239 MsqDecPaintCountQueue(Window->MessageQueue);
240 }
241 }
242 }
243 }
244 }
245 }
246
247 /*
248 * Check that the window is still valid at this point
249 */
250
251 if (!IntIsWindow(hWnd))
252 return;
253
254 /*
255 * Paint child windows.
256 */
257
258 if (!(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
259 ((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN)))
260 {
261 HWND *List, *phWnd;
262
263 if ((List = IntWinListChildren(Window)))
264 {
265 for (phWnd = List; *phWnd; ++phWnd)
266 {
267 Window = IntGetWindowObject(*phWnd);
268 if (Window)
269 {
270 IntPaintWindows(Window, Flags);
271 IntReleaseWindowObject(Window);
272 }
273 }
274 ExFreePool(List);
275 }
276 }
277 }
278
279 /*
280 * IntInvalidateWindows
281 *
282 * Internal function used by IntRedrawWindow.
283 */
284
285 VOID FASTCALL
286 IntInvalidateWindows(PWINDOW_OBJECT Window, HRGN hRgn, ULONG Flags,
287 BOOL ValidateParent)
288 {
289 INT RgnType;
290 BOOL HadPaintMessage, HadNCPaintMessage;
291 BOOL HasPaintMessage, HasNCPaintMessage;
292 HRGN hRgnWindow;
293
294 /*
295 * Clip the given region with window rectangle (or region)
296 */
297
298 #ifdef TODO
299 if (!Window->WindowRegion)
300 #endif
301 {
302 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
303 NtGdiOffsetRgn(hRgnWindow,
304 -Window->WindowRect.left,
305 -Window->WindowRect.top);
306 RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnWindow, RGN_AND);
307 NtGdiDeleteObject(hRgnWindow);
308 }
309 #ifdef TODO
310 else
311 {
312 RgnType = NtGdiCombineRgn(hRgn, hRgn, Window->WindowRegion, RGN_AND);
313 }
314 #endif
315
316 /*
317 * Save current state of pending updates
318 */
319
320 HadPaintMessage = Window->UpdateRegion != NULL ||
321 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
322 HadNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
323
324 /*
325 * Update the region and flags
326 */
327
328 if (Flags & RDW_INVALIDATE && RgnType != NULLREGION)
329 {
330 if (Window->UpdateRegion == NULL)
331 {
332 Window->UpdateRegion = NtGdiCreateRectRgn(0, 0, 0, 0);
333 }
334
335 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
336 hRgn, RGN_OR) == NULLREGION)
337 {
338 NtGdiDeleteObject(Window->UpdateRegion);
339 Window->UpdateRegion = NULL;
340 }
341
342 if (Flags & RDW_FRAME)
343 Window->Flags |= WINDOWOBJECT_NEED_NCPAINT;
344 if (Flags & RDW_ERASE)
345 Window->Flags |= WINDOWOBJECT_NEED_ERASEBKGND;
346
347 Flags |= RDW_FRAME;
348 }
349
350 if (Flags & RDW_VALIDATE && RgnType != NULLREGION)
351 {
352 if (Window->UpdateRegion != NULL)
353 {
354 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
355 hRgn, RGN_DIFF) == NULLREGION)
356 {
357 NtGdiDeleteObject(Window->UpdateRegion);
358 Window->UpdateRegion = NULL;
359 }
360 }
361
362 if (Window->UpdateRegion == NULL)
363 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
364 if (Flags & RDW_NOFRAME)
365 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
366 if (Flags & RDW_NOERASE)
367 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
368 }
369
370 if (Flags & RDW_INTERNALPAINT)
371 {
372 Window->Flags |= WINDOWOBJECT_NEED_INTERNALPAINT;
373 }
374
375 if (Flags & RDW_NOINTERNALPAINT)
376 {
377 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
378 }
379
380 /*
381 * Validate parent covered by region
382 */
383
384 if (ValidateParent)
385 {
386 IntValidateParent(Window);
387 }
388
389 /*
390 * Process children if needed
391 */
392
393 if (!(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
394 ((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN)))
395 {
396 HWND *List, *phWnd;
397 PWINDOW_OBJECT Child;
398
399 if ((List = IntWinListChildren(Window)))
400 {
401 for (phWnd = List; *phWnd; ++phWnd)
402 {
403 Child = IntGetWindowObject(*phWnd);
404 if ((Child->Style & (WS_VISIBLE | WS_MINIMIZE)) == WS_VISIBLE)
405 {
406 /*
407 * Recursive call to update children UpdateRegion
408 */
409 HRGN hRgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
410 NtGdiCombineRgn(hRgnTemp, hRgn, 0, RGN_COPY);
411 NtGdiOffsetRgn(hRgnTemp,
412 Window->WindowRect.left - Child->WindowRect.left,
413 Window->WindowRect.top - Child->WindowRect.top);
414 IntInvalidateWindows(Child, hRgnTemp, Flags, FALSE);
415
416 /*
417 * Update our UpdateRegion depending on children
418 */
419 NtGdiCombineRgn(hRgnTemp, Child->UpdateRegion, 0, RGN_COPY);
420 NtGdiOffsetRgn(hRgnTemp,
421 Child->WindowRect.left - Window->WindowRect.left,
422 Child->WindowRect.top - Window->WindowRect.top);
423 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
424 NtGdiOffsetRgn(hRgnWindow,
425 -Window->WindowRect.left,
426 -Window->WindowRect.top);
427 NtGdiCombineRgn(hRgnTemp, hRgnTemp, hRgnWindow, RGN_AND);
428 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
429 hRgnTemp, RGN_DIFF) == NULLREGION)
430 {
431 NtGdiDeleteObject(Window->UpdateRegion);
432 Window->UpdateRegion = NULL;
433 }
434 NtGdiDeleteObject(hRgnTemp);
435 }
436 IntReleaseWindowObject(Child);
437 }
438 ExFreePool(List);
439 }
440 }
441
442 /*
443 * Fake post paint messages to window message queue if needed
444 */
445
446 #ifndef DESKTOP_IN_CSRSS
447 if (Window->MessageQueue)
448 #endif
449 {
450 HasPaintMessage = Window->UpdateRegion != NULL ||
451 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
452 HasNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
453
454 if (HasPaintMessage != HadPaintMessage)
455 {
456 if (HadPaintMessage)
457 MsqDecPaintCountQueue(Window->MessageQueue);
458 else
459 MsqIncPaintCountQueue(Window->MessageQueue);
460 }
461
462 if (HasNCPaintMessage != HadNCPaintMessage)
463 {
464 if (HadNCPaintMessage)
465 MsqDecPaintCountQueue(Window->MessageQueue);
466 else
467 MsqIncPaintCountQueue(Window->MessageQueue);
468 }
469 #ifndef DESKTOP_IN_CSRSS
470 }
471 #endif
472 }
473
474 /*
475 * IntIsWindowDrawable
476 *
477 * Remarks
478 * Window is drawable when it is visible, all parents are not
479 * minimized, and it is itself not minimized.
480 */
481
482 BOOL FASTCALL
483 IntIsWindowDrawable(PWINDOW_OBJECT Window)
484 {
485 for (; Window; Window = Window->Parent)
486 {
487 if ((Window->Style & (WS_VISIBLE | WS_MINIMIZE)) != WS_VISIBLE)
488 return FALSE;
489 }
490
491 return TRUE;
492 }
493
494 /*
495 * IntRedrawWindow
496 *
497 * Internal version of NtUserRedrawWindow that takes WINDOW_OBJECT as
498 * first parameter.
499 */
500
501 BOOL FASTCALL
502 IntRedrawWindow(PWINDOW_OBJECT Window, const RECT* UpdateRect, HRGN UpdateRgn,
503 ULONG Flags)
504 {
505 HRGN hRgn = NULL;
506
507 /*
508 * Step 1.
509 * Validation of passed parameters.
510 */
511
512 if (!IntIsWindowDrawable(Window) ||
513 (Flags & (RDW_VALIDATE | RDW_INVALIDATE)) ==
514 (RDW_VALIDATE | RDW_INVALIDATE))
515 {
516 return FALSE;
517 }
518
519 /*
520 * Step 2.
521 * Transform the parameters UpdateRgn and UpdateRect into
522 * a region hRgn specified in window coordinates.
523 */
524
525 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE))
526 {
527 if (UpdateRgn != NULL)
528 {
529 hRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
530 NtGdiCombineRgn(hRgn, UpdateRgn, NULL, RGN_COPY);
531 NtGdiOffsetRgn(hRgn,
532 Window->ClientRect.left - Window->WindowRect.left,
533 Window->ClientRect.top - Window->WindowRect.top);
534 } else
535 if (UpdateRect != NULL)
536 {
537 hRgn = UnsafeIntCreateRectRgnIndirect((RECT *)UpdateRect);
538 NtGdiOffsetRgn(hRgn,
539 Window->ClientRect.left - Window->WindowRect.left,
540 Window->ClientRect.top - Window->WindowRect.top);
541 } else
542 if ((Flags & (RDW_INVALIDATE | RDW_FRAME)) == (RDW_INVALIDATE | RDW_FRAME) ||
543 (Flags & (RDW_VALIDATE | RDW_NOFRAME)) == (RDW_VALIDATE | RDW_NOFRAME))
544 {
545 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
546 NtGdiOffsetRgn(hRgn,
547 -Window->WindowRect.left,
548 -Window->WindowRect.top);
549 }
550 else
551 {
552 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
553 NtGdiOffsetRgn(hRgn,
554 -Window->WindowRect.left,
555 -Window->WindowRect.top);
556 }
557 }
558
559 /*
560 * Step 3.
561 * Adjust the window update region depending on hRgn and flags.
562 */
563
564 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT))
565 {
566 IntInvalidateWindows(Window, hRgn, Flags, TRUE);
567 } else
568 if (Window->UpdateRegion != NULL && Flags & RDW_ERASENOW)
569 {
570 /* Validate parent covered by region. */
571 IntValidateParent(Window);
572 }
573
574 /*
575 * Step 4.
576 * Repaint and erase windows if needed.
577 */
578
579 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
580 {
581 IntPaintWindows(Window, Flags);
582 }
583
584 /*
585 * Step 5.
586 * Cleanup ;-)
587 */
588
589 NtGdiDeleteObject(hRgn);
590
591 return TRUE;
592 }
593
594 HWND STDCALL
595 IntFindWindowToRepaint(HWND hWnd, PW32THREAD Thread)
596 {
597 PWINDOW_OBJECT Window;
598 PWINDOW_OBJECT Child;
599 HWND hFoundWnd = NULL;
600
601 if (hWnd == NULL)
602 {
603 PLIST_ENTRY CurrentEntry;
604
605 ExAcquireFastMutex(&Thread->WindowListLock);
606
607 for (CurrentEntry = Thread->WindowListHead.Flink;
608 CurrentEntry != &Thread->WindowListHead;
609 CurrentEntry = CurrentEntry->Flink)
610 {
611 Window = CONTAINING_RECORD(CurrentEntry, WINDOW_OBJECT, ThreadListEntry);
612 if (Window->Parent != NULL && !IntIsDesktopWindow(Window->Parent))
613 {
614 continue;
615 }
616 if (Window->Style & WS_VISIBLE)
617 {
618 hFoundWnd = IntFindWindowToRepaint(Window->Self, Thread);
619 if (hFoundWnd != NULL)
620 {
621 ExReleaseFastMutex(&Thread->WindowListLock);
622 return hFoundWnd;
623 }
624 }
625 }
626
627 ExReleaseFastMutex(&Thread->WindowListLock);
628 return NULL;
629 }
630 else
631 {
632 Window = IntGetWindowObject(hWnd);
633 if (Window == NULL)
634 return NULL;
635
636 if (Window->UpdateRegion != NULL ||
637 Window->Flags & (WINDOWOBJECT_NEED_INTERNALPAINT | WINDOWOBJECT_NEED_NCPAINT))
638 {
639 IntReleaseWindowObject(Window);
640 return hWnd;
641 }
642
643 ExAcquireFastMutex(&Window->ChildrenListLock);
644
645 for (Child = Window->FirstChild; Child; Child = Child->NextSibling)
646 {
647 if (Child->Style & WS_VISIBLE &&
648 (Child->UpdateRegion != NULL ||
649 Child->Flags & WINDOWOBJECT_NEED_INTERNALPAINT ||
650 Child->Flags & WINDOWOBJECT_NEED_NCPAINT))
651 {
652 hFoundWnd = Child->Self;
653 break;
654 }
655 }
656
657 if (hFoundWnd == NULL)
658 {
659 for (Child = Window->FirstChild; Child; Child = Child->NextSibling)
660 {
661 if (Child->Style & WS_VISIBLE)
662 {
663 hFoundWnd = IntFindWindowToRepaint(Child->Self, Thread);
664 if (hFoundWnd != NULL)
665 break;
666 }
667 }
668 }
669
670 ExReleaseFastMutex(&Window->ChildrenListLock);
671 IntReleaseWindowObject(Window);
672
673 return hFoundWnd;
674 }
675 }
676
677 BOOL FASTCALL
678 IntGetPaintMessage(PWINDOW_OBJECT Wnd, PW32THREAD Thread, MSG *Message,
679 BOOL Remove)
680 {
681 PWINDOW_OBJECT Window;
682 PUSER_MESSAGE_QUEUE MessageQueue = (PUSER_MESSAGE_QUEUE)Thread->MessageQueue;
683
684 if (!MessageQueue->PaintPosted)
685 return FALSE;
686
687 if (Wnd)
688 Message->hwnd = IntFindWindowToRepaint(Wnd->Self, PsGetWin32Thread());
689 else
690 Message->hwnd = IntFindWindowToRepaint(NULL, PsGetWin32Thread());
691
692 if (Message->hwnd == NULL)
693 {
694 DPRINT1("PAINTING BUG: Thread marked as containing dirty windows, but no dirty windows found!\n");
695 return FALSE;
696 }
697
698 Window = IntGetWindowObject(Message->hwnd);
699 if (Window != NULL)
700 {
701 if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
702 {
703 Message->message = WM_NCPAINT;
704 Message->wParam = (WPARAM)IntGetNCUpdateRegion(Window, Remove);
705 Message->lParam = 0;
706 } else
707 {
708 Message->message = WM_PAINT;
709 Message->wParam = Message->lParam = 0;
710 if (Remove && Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
711 {
712 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
713 if (Window->UpdateRegion == NULL)
714 {
715 MsqDecPaintCountQueue(Window->MessageQueue);
716 }
717 }
718 }
719
720 IntReleaseWindowObject(Window);
721 return TRUE;
722 }
723
724 return FALSE;
725 }
726
727 /* PUBLIC FUNCTIONS ***********************************************************/
728
729 /*
730 * NtUserBeginPaint
731 *
732 * Status
733 * @implemented
734 */
735
736 HDC STDCALL
737 NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* lPs)
738 {
739 PWINDOW_OBJECT Window;
740 RECT ClientRect;
741 RECT ClipRect;
742 INT DcxFlags;
743
744 if (!(Window = IntGetWindowObject(hWnd)))
745 {
746 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
747 return NULL;
748 }
749
750 NtUserHideCaret(hWnd);
751
752 DcxFlags = DCX_INTERSECTUPDATE | DCX_WINDOWPAINT | DCX_USESTYLE;
753 if (IntGetClassLong(Window, GCL_STYLE, FALSE) & CS_PARENTDC)
754 {
755 /* FIXME: Is this correct? */
756 /* Don't clip the output to the update region for CS_PARENTDC window */
757 DcxFlags &= ~DCX_INTERSECTUPDATE;
758 }
759
760 lPs->hdc = NtUserGetDCEx(hWnd, 0, DcxFlags);
761
762 if (!lPs->hdc)
763 {
764 return NULL;
765 }
766
767 /* IntRedrawWindow(Window, NULL, 0, RDW_NOINTERNALPAINT | RDW_VALIDATE | RDW_NOCHILDREN);*/
768 if (Window->UpdateRegion != NULL)
769 {
770 MsqDecPaintCountQueue(Window->MessageQueue);
771 IntValidateParent(Window);
772 NtGdiDeleteObject(Window->UpdateRegion);
773 Window->UpdateRegion = NULL;
774 }
775
776 IntGetClientRect(Window, &ClientRect);
777 NtGdiGetClipBox(lPs->hdc, &ClipRect);
778 NtGdiLPtoDP(lPs->hdc, (LPPOINT)&ClipRect, 2);
779 NtGdiIntersectRect(&lPs->rcPaint, &ClientRect, &ClipRect);
780 NtGdiDPtoLP(lPs->hdc, (LPPOINT)&lPs->rcPaint, 2);
781
782 if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
783 {
784 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
785 lPs->fErase = !NtUserSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)lPs->hdc, 0);
786 }
787 else
788 {
789 lPs->fErase = FALSE;
790 }
791
792 IntReleaseWindowObject(Window);
793
794 return lPs->hdc;
795 }
796
797 /*
798 * NtUserEndPaint
799 *
800 * Status
801 * @implemented
802 */
803
804 BOOL STDCALL
805 NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* lPs)
806 {
807 NtUserReleaseDC(hWnd, lPs->hdc);
808 NtUserShowCaret(hWnd);
809
810 return TRUE;
811 }
812
813 /*
814 * NtUserInvalidateRect
815 *
816 * Status
817 * @implemented
818 */
819
820 DWORD STDCALL
821 NtUserInvalidateRect(HWND hWnd, CONST RECT *Rect, BOOL Erase)
822 {
823 return NtUserRedrawWindow(hWnd, Rect, 0, RDW_INVALIDATE | (Erase ? RDW_ERASE : 0));
824 }
825
826 /*
827 * NtUserInvalidateRgn
828 *
829 * Status
830 * @implemented
831 */
832
833 DWORD STDCALL
834 NtUserInvalidateRgn(HWND hWnd, HRGN Rgn, BOOL Erase)
835 {
836 return NtUserRedrawWindow(hWnd, NULL, Rgn, RDW_INVALIDATE | (Erase ? RDW_ERASE : 0));
837 }
838
839 /*
840 * NtUserValidateRgn
841 *
842 * Status
843 * @implemented
844 */
845
846 BOOL STDCALL
847 NtUserValidateRgn(HWND hWnd, HRGN hRgn)
848 {
849 return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_VALIDATE | RDW_NOCHILDREN);
850 }
851
852 /*
853 * NtUserUpdateWindow
854 *
855 * Status
856 * @implemented
857 */
858
859 BOOL STDCALL
860 NtUserUpdateWindow(HWND hWnd)
861 {
862 return NtUserRedrawWindow(hWnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN);
863 }
864
865 /*
866 * NtUserGetUpdateRgn
867 *
868 * Status
869 * @implemented
870 */
871
872 INT STDCALL
873 NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
874 {
875 PWINDOW_OBJECT Window;
876 int RegionType;
877
878 if (!(Window = IntGetWindowObject(hWnd)))
879 {
880 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
881 return ERROR;
882 }
883
884 if (Window->UpdateRegion == NULL)
885 {
886 RegionType = (NtGdiSetRectRgn(hRgn, 0, 0, 0, 0) ? NULLREGION : ERROR);
887 }
888 else
889 {
890 RegionType = NtGdiCombineRgn(hRgn, Window->UpdateRegion, hRgn, RGN_COPY);
891 NtGdiOffsetRgn(
892 hRgn,
893 Window->WindowRect.left - Window->ClientRect.left,
894 Window->WindowRect.top - Window->ClientRect.top);
895 }
896
897 IntReleaseWindowObject(Window);
898
899 if (bErase && RegionType != NULLREGION && RegionType != ERROR)
900 {
901 NtUserRedrawWindow(hWnd, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
902 }
903
904 return RegionType;
905 }
906
907 /*
908 * NtUserGetUpdateRect
909 *
910 * Status
911 * @implemented
912 */
913
914 BOOL STDCALL
915 NtUserGetUpdateRect(HWND hWnd, LPRECT lpRect, BOOL fErase)
916 {
917 HRGN hRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
918
919 if (!lpRect)
920 {
921 SetLastWin32Error(ERROR_INVALID_PARAMETER);
922 return FALSE;
923 }
924
925 NtUserGetUpdateRgn(hWnd, hRgn, fErase);
926 NtGdiGetRgnBox(hRgn, lpRect);
927
928 return lpRect->left < lpRect->right && lpRect->top < lpRect->bottom;
929 }
930
931 /*
932 * NtUserRedrawWindow
933 *
934 * Status
935 * @implemented
936 */
937
938 BOOL STDCALL
939 NtUserRedrawWindow(HWND hWnd, CONST RECT *lprcUpdate, HRGN hrgnUpdate,
940 UINT flags)
941 {
942 RECT SafeUpdateRect;
943 NTSTATUS Status;
944 PWINDOW_OBJECT Wnd;
945
946 if (!(Wnd = IntGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow())))
947 {
948 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
949 return FALSE;
950 }
951
952 if (lprcUpdate != NULL)
953 {
954 Status = MmCopyFromCaller(&SafeUpdateRect, (PRECT)lprcUpdate,
955 sizeof(RECT));
956
957 if (!NT_SUCCESS(Status))
958 {
959 SetLastWin32Error(ERROR_INVALID_PARAMETER);
960 return FALSE;
961 }
962 }
963
964 Status = IntRedrawWindow(Wnd, NULL == lprcUpdate ? NULL : &SafeUpdateRect,
965 hrgnUpdate, flags);
966
967 if (!NT_SUCCESS(Status))
968 {
969 /* IntRedrawWindow fails only in case that flags are invalid */
970 SetLastWin32Error(ERROR_INVALID_PARAMETER);
971 return FALSE;
972 }
973
974 return TRUE;
975 }
976
977 /* EOF */