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