Fixes on paining code to get PuTTY and RegEdit working correctly.
[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.59 2004/01/12 00:07:34 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/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 /*
405 * Recursive call to update children UpdateRegion
406 */
407 HRGN hRgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
408 NtGdiCombineRgn(hRgnTemp, hRgn, 0, RGN_COPY);
409 NtGdiOffsetRgn(hRgnTemp,
410 Window->WindowRect.left - Child->WindowRect.left,
411 Window->WindowRect.top - Child->WindowRect.top);
412 IntInvalidateWindows(Child, hRgnTemp, Flags, FALSE);
413
414 /*
415 * WINDOWS REALLY DON'T DO THIS!!!
416 */
417 #if 0
418 /*
419 * Update our UpdateRegion depending on children
420 */
421
422 if (Window->UpdateRegion != NULL)
423 {
424 RECT TempRect;
425
426 UnsafeIntGetRgnBox(Window->UpdateRegion, &TempRect);
427 NtGdiCombineRgn(hRgnTemp, Child->UpdateRegion, 0, RGN_COPY);
428 NtGdiCombineRgn(hRgnTemp, hRgnTemp, Child->NCUpdateRegion, RGN_OR);
429 NtGdiOffsetRgn(hRgnTemp,
430 Child->WindowRect.left - Window->WindowRect.left,
431 Child->WindowRect.top - Window->WindowRect.top);
432 UnsafeIntGetRgnBox(hRgnTemp, &TempRect);
433 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
434 hRgnTemp, RGN_DIFF) == NULLREGION)
435 {
436 NtGdiDeleteObject(Window->UpdateRegion);
437 Window->UpdateRegion = NULL;
438 }
439 }
440 #endif
441 NtGdiDeleteObject(hRgnTemp);
442 }
443 IntReleaseWindowObject(Child);
444 }
445 ExFreePool(List);
446 }
447 }
448
449 /*
450 * Fake post paint messages to window message queue if needed
451 */
452 HasPaintMessage = Window->UpdateRegion != NULL ||
453 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
454 HasNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
455
456 if (HasPaintMessage != HadPaintMessage)
457 {
458 if (HadPaintMessage)
459 MsqDecPaintCountQueue(Window->MessageQueue);
460 else
461 MsqIncPaintCountQueue(Window->MessageQueue);
462 }
463
464 if (HasNCPaintMessage != HadNCPaintMessage)
465 {
466 if (HadNCPaintMessage)
467 MsqDecPaintCountQueue(Window->MessageQueue);
468 else
469 MsqIncPaintCountQueue(Window->MessageQueue);
470 }
471 }
472
473 /*
474 * IntIsWindowDrawable
475 *
476 * Remarks
477 * Window is drawable when it is visible and all parents are not
478 * minimized.
479 */
480
481 BOOL FASTCALL
482 IntIsWindowDrawable(PWINDOW_OBJECT Window)
483 {
484 PWINDOW_OBJECT Wnd = Window;
485
486 for (; Wnd; Wnd = Wnd->Parent)
487 {
488 if (!(Wnd->Style & WS_VISIBLE) ||
489 ((Wnd->Style & WS_MINIMIZE) && (Wnd != Window)))
490 return FALSE;
491 }
492
493 return TRUE;
494 }
495
496 /*
497 * IntRedrawWindow
498 *
499 * Internal version of NtUserRedrawWindow that takes WINDOW_OBJECT as
500 * first parameter.
501 */
502
503 BOOL FASTCALL
504 IntRedrawWindow(PWINDOW_OBJECT Window, const RECT* UpdateRect, HRGN UpdateRgn,
505 ULONG Flags)
506 {
507 HRGN hRgn = NULL;
508
509 /*
510 * Step 1.
511 * Validation of passed parameters.
512 */
513
514 if (!IntIsWindowDrawable(Window) ||
515 (Flags & (RDW_VALIDATE | RDW_INVALIDATE)) ==
516 (RDW_VALIDATE | RDW_INVALIDATE))
517 {
518 return FALSE;
519 }
520
521 /*
522 * Step 2.
523 * Transform the parameters UpdateRgn and UpdateRect into
524 * a region hRgn specified in window coordinates.
525 */
526
527 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE))
528 {
529 if (UpdateRgn != NULL)
530 {
531 hRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
532 NtGdiCombineRgn(hRgn, UpdateRgn, NULL, RGN_COPY);
533 NtGdiOffsetRgn(hRgn,
534 Window->ClientRect.left - Window->WindowRect.left,
535 Window->ClientRect.top - Window->WindowRect.top);
536 } else
537 if (UpdateRect != NULL)
538 {
539 hRgn = UnsafeIntCreateRectRgnIndirect((RECT *)UpdateRect);
540 NtGdiOffsetRgn(hRgn,
541 Window->ClientRect.left - Window->WindowRect.left,
542 Window->ClientRect.top - Window->WindowRect.top);
543 } else
544 if ((Flags & (RDW_INVALIDATE | RDW_FRAME)) == (RDW_INVALIDATE | RDW_FRAME) ||
545 (Flags & (RDW_VALIDATE | RDW_NOFRAME)) == (RDW_VALIDATE | RDW_NOFRAME))
546 {
547 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
548 NtGdiOffsetRgn(hRgn,
549 -Window->WindowRect.left,
550 -Window->WindowRect.top);
551 }
552 else
553 {
554 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
555 NtGdiOffsetRgn(hRgn,
556 -Window->WindowRect.left,
557 -Window->WindowRect.top);
558 }
559 }
560
561 /*
562 * Step 3.
563 * Adjust the window update region depending on hRgn and flags.
564 */
565
566 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT))
567 {
568 IntInvalidateWindows(Window, hRgn, Flags, TRUE);
569 } else
570 if (Window->UpdateRegion != NULL && Flags & RDW_ERASENOW)
571 {
572 /* Validate parent covered by region. */
573 IntValidateParent(Window, Window->UpdateRegion);
574 }
575
576 /*
577 * Step 4.
578 * Repaint and erase windows if needed.
579 */
580
581 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
582 {
583 IntPaintWindows(Window, Flags);
584 }
585
586 /*
587 * Step 5.
588 * Cleanup ;-)
589 */
590
591 if (NULL != hRgn)
592 {
593 NtGdiDeleteObject(hRgn);
594 }
595
596 return TRUE;
597 }
598
599 HWND STDCALL
600 IntFindWindowToRepaint(HWND hWnd, PW32THREAD Thread)
601 {
602 PWINDOW_OBJECT Window;
603 PWINDOW_OBJECT Child;
604 HWND hFoundWnd = NULL;
605
606 if (hWnd == NULL)
607 {
608 PLIST_ENTRY CurrentEntry;
609
610 ExAcquireFastMutex(&Thread->WindowListLock);
611
612 for (CurrentEntry = Thread->WindowListHead.Flink;
613 CurrentEntry != &Thread->WindowListHead;
614 CurrentEntry = CurrentEntry->Flink)
615 {
616 Window = CONTAINING_RECORD(CurrentEntry, WINDOW_OBJECT, ThreadListEntry);
617 if (Window->Parent != NULL && !IntIsDesktopWindow(Window->Parent))
618 {
619 continue;
620 }
621 if (Window->Style & WS_VISIBLE)
622 {
623 hFoundWnd = IntFindWindowToRepaint(Window->Self, Thread);
624 if (hFoundWnd != NULL)
625 {
626 ExReleaseFastMutex(&Thread->WindowListLock);
627 return hFoundWnd;
628 }
629 }
630 }
631
632 ExReleaseFastMutex(&Thread->WindowListLock);
633 return NULL;
634 }
635 else
636 {
637 Window = IntGetWindowObject(hWnd);
638 if (Window == NULL)
639 return NULL;
640
641 if ((Window->UpdateRegion != NULL ||
642 Window->Flags & (WINDOWOBJECT_NEED_INTERNALPAINT | WINDOWOBJECT_NEED_NCPAINT)) &&
643 IntWndBelongsToThread(Window, Thread))
644 {
645 IntReleaseWindowObject(Window);
646 return hWnd;
647 }
648
649 ExAcquireFastMutex(&Window->ChildrenListLock);
650
651 for (Child = Window->FirstChild; Child; Child = Child->NextSibling)
652 {
653 if (Child->Style & WS_VISIBLE &&
654 (Child->UpdateRegion != NULL ||
655 Child->Flags & WINDOWOBJECT_NEED_INTERNALPAINT ||
656 Child->Flags & WINDOWOBJECT_NEED_NCPAINT)
657 && IntWndBelongsToThread(Child, Thread))
658 {
659 hFoundWnd = Child->Self;
660 break;
661 }
662 }
663
664 if (hFoundWnd == NULL)
665 {
666 for (Child = Window->FirstChild; Child; Child = Child->NextSibling)
667 {
668 if (Child->Style & WS_VISIBLE)
669 {
670 hFoundWnd = IntFindWindowToRepaint(Child->Self, Thread);
671 if (hFoundWnd != NULL)
672 break;
673 }
674 }
675 }
676
677 ExReleaseFastMutex(&Window->ChildrenListLock);
678 IntReleaseWindowObject(Window);
679
680 return hFoundWnd;
681 }
682 }
683
684 BOOL FASTCALL
685 IntGetPaintMessage(HWND hWnd, PW32THREAD Thread, MSG *Message,
686 BOOL Remove)
687 {
688 PWINDOW_OBJECT Window;
689 PUSER_MESSAGE_QUEUE MessageQueue = (PUSER_MESSAGE_QUEUE)Thread->MessageQueue;
690
691 if (!MessageQueue->PaintPosted)
692 return FALSE;
693
694 if (hWnd)
695 Message->hwnd = IntFindWindowToRepaint(hWnd, PsGetWin32Thread());
696 else
697 Message->hwnd = IntFindWindowToRepaint(IntGetDesktopWindow(), PsGetWin32Thread());
698
699 if (Message->hwnd == NULL)
700 {
701 #if 0
702 DPRINT1("PAINTING BUG: Thread marked as containing dirty windows, but no dirty windows found!\n");
703 #endif
704 /* FIXME: Lock the queue! */
705 MessageQueue->PaintPosted = 0;
706 MessageQueue->PaintCount = 0;
707 return FALSE;
708 }
709
710 Window = IntGetWindowObject(Message->hwnd);
711 if (Window != NULL)
712 {
713 if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
714 {
715 Message->message = WM_NCPAINT;
716 Message->wParam = (WPARAM)Window->NCUpdateRegion;
717 Message->lParam = 0;
718 if (Remove)
719 {
720 Window->NCUpdateRegion = NULL;
721 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
722 MsqDecPaintCountQueue(Window->MessageQueue);
723 }
724 } else
725 {
726 #ifdef FIN_DEBUG
727 {
728 RECT TempRect;
729 UnsafeIntGetRgnBox(Window->UpdateRegion, &TempRect);
730 DPRINT1("Sending WM_PAINT[2]: %d,%d-%d,%d\n",
731 TempRect.left, TempRect.top, TempRect.right, TempRect.bottom);
732 }
733 #endif
734 Message->message = WM_PAINT;
735 Message->wParam = Message->lParam = 0;
736 if (Remove && Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
737 {
738 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
739 if (Window->UpdateRegion == NULL)
740 {
741 MsqDecPaintCountQueue(Window->MessageQueue);
742 }
743 }
744 }
745
746 IntReleaseWindowObject(Window);
747 return TRUE;
748 }
749
750 return FALSE;
751 }
752
753 HWND FASTCALL
754 IntFixCaret(HWND hWnd, LPRECT lprc, UINT flags)
755 {
756 GUITHREADINFO info;
757
758 if (!NtUserGetGUIThreadInfo(0, &info))
759 return 0;
760 if (!info.hwndCaret)
761 return 0;
762 if (info.hwndCaret == hWnd ||
763 ((flags & SW_SCROLLCHILDREN) && IntIsChildWindow(hWnd, info.hwndCaret)))
764 {
765 POINT pt, FromOffset, ToOffset, Offset;
766
767 pt.x = info.rcCaret.left;
768 pt.y = info.rcCaret.top;
769
770 IntGetClientOrigin(info.hwndCaret, &FromOffset);
771 IntGetClientOrigin(hWnd, &ToOffset);
772 Offset.x = FromOffset.x - ToOffset.x;
773 Offset.y = FromOffset.y - ToOffset.y;
774 info.rcCaret.left += Offset.x;
775 info.rcCaret.top += Offset.y;
776 info.rcCaret.right += Offset.x;
777 info.rcCaret.bottom += Offset.y;
778 if (NtGdiIntersectRect(lprc, lprc, &info.rcCaret))
779 {
780 NtUserHideCaret(0);
781 lprc->left = pt.x;
782 lprc->top = pt.y;
783 return info.hwndCaret;
784 }
785 }
786
787 return 0;
788 }
789
790 /* PUBLIC FUNCTIONS ***********************************************************/
791
792 /*
793 * NtUserBeginPaint
794 *
795 * Status
796 * @implemented
797 */
798
799 HDC STDCALL
800 NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* lPs)
801 {
802 PWINDOW_OBJECT Window;
803 RECT ClientRect;
804 RECT ClipRect;
805 INT DcxFlags;
806 PDC DC;
807
808 if (!(Window = IntGetWindowObject(hWnd)))
809 {
810 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
811 return NULL;
812 }
813
814 NtUserHideCaret(hWnd);
815
816 DcxFlags = DCX_INTERSECTUPDATE | DCX_WINDOWPAINT | DCX_USESTYLE;
817
818 lPs->hdc = NtUserGetDCEx(hWnd, 0, DcxFlags);
819
820 if (!lPs->hdc)
821 {
822 IntReleaseWindowObject(Window);
823 return NULL;
824 }
825
826 /* IntRedrawWindow(Window, NULL, 0, RDW_NOINTERNALPAINT | RDW_VALIDATE | RDW_NOCHILDREN);*/
827 #ifdef FIN_DEBUG
828 if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
829 {
830 RECT TempRect;
831 UnsafeIntGetRgnBox(Window->UpdateRegion, &TempRect);
832 DPRINT1("Sending WM_ERASEBKGND[2]: %d,%d-%d,%d\n",
833 TempRect.left, TempRect.top, TempRect.right, TempRect.bottom);
834 }
835 #endif
836
837 if (Window->UpdateRegion != NULL)
838 {
839 MsqDecPaintCountQueue(Window->MessageQueue);
840 IntValidateParent(Window, Window->UpdateRegion);
841 NtGdiDeleteObject(Window->UpdateRegion);
842 Window->UpdateRegion = NULL;
843 }
844
845 IntGetClientRect(Window, &ClientRect);
846 IntGdiGetClipBox(lPs->hdc, &ClipRect);
847 DC = DC_LockDc(lPs->hdc);
848 if (NULL == DC)
849 {
850 IntReleaseWindowObject(Window);
851 return NULL;
852 }
853 IntLPtoDP(DC, (LPPOINT)&ClipRect, 2);
854 DC_UnlockDc(lPs->hdc);
855 NtGdiIntersectRect(&lPs->rcPaint, &ClientRect, &ClipRect);
856 NtGdiDPtoLP(lPs->hdc, (LPPOINT)&lPs->rcPaint, 2);
857
858 if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
859 {
860 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
861 lPs->fErase = !IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)lPs->hdc, 0);
862 }
863 else
864 {
865 lPs->fErase = FALSE;
866 }
867
868 IntReleaseWindowObject(Window);
869
870 return lPs->hdc;
871 }
872
873 /*
874 * NtUserEndPaint
875 *
876 * Status
877 * @implemented
878 */
879
880 BOOL STDCALL
881 NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* lPs)
882 {
883 NtUserReleaseDC(hWnd, lPs->hdc);
884 NtUserShowCaret(hWnd);
885
886 return TRUE;
887 }
888
889 /*
890 * NtUserInvalidateRect
891 *
892 * Status
893 * @implemented
894 */
895
896 DWORD STDCALL
897 NtUserInvalidateRect(HWND hWnd, CONST RECT *Rect, BOOL Erase)
898 {
899 return NtUserRedrawWindow(hWnd, Rect, 0, RDW_INVALIDATE | (Erase ? RDW_ERASE : 0));
900 }
901
902 /*
903 * NtUserInvalidateRgn
904 *
905 * Status
906 * @implemented
907 */
908
909 DWORD STDCALL
910 NtUserInvalidateRgn(HWND hWnd, HRGN Rgn, BOOL Erase)
911 {
912 return NtUserRedrawWindow(hWnd, NULL, Rgn, RDW_INVALIDATE | (Erase ? RDW_ERASE : 0));
913 }
914
915 /*
916 * NtUserValidateRgn
917 *
918 * Status
919 * @implemented
920 */
921
922 BOOL STDCALL
923 NtUserValidateRgn(HWND hWnd, HRGN hRgn)
924 {
925 return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_VALIDATE | RDW_NOCHILDREN);
926 }
927
928 /*
929 * NtUserUpdateWindow
930 *
931 * Status
932 * @implemented
933 */
934
935 BOOL STDCALL
936 NtUserUpdateWindow(HWND hWnd)
937 {
938 return NtUserRedrawWindow(hWnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN);
939 }
940
941 /*
942 * NtUserGetUpdateRgn
943 *
944 * Status
945 * @implemented
946 */
947
948 INT STDCALL
949 NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
950 {
951 PWINDOW_OBJECT Window;
952 int RegionType;
953
954 if (!(Window = IntGetWindowObject(hWnd)))
955 {
956 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
957 return ERROR;
958 }
959
960 if (Window->UpdateRegion == NULL)
961 {
962 RegionType = (NtGdiSetRectRgn(hRgn, 0, 0, 0, 0) ? NULLREGION : ERROR);
963 }
964 else
965 {
966 RegionType = NtGdiCombineRgn(hRgn, Window->UpdateRegion, hRgn, RGN_COPY);
967 NtGdiOffsetRgn(
968 hRgn,
969 Window->WindowRect.left - Window->ClientRect.left,
970 Window->WindowRect.top - Window->ClientRect.top);
971 }
972
973 IntReleaseWindowObject(Window);
974
975 if (bErase && RegionType != NULLREGION && RegionType != ERROR)
976 {
977 NtUserRedrawWindow(hWnd, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
978 }
979
980 return RegionType;
981 }
982
983 /*
984 * NtUserGetUpdateRect
985 *
986 * Status
987 * @implemented
988 */
989
990 BOOL STDCALL
991 NtUserGetUpdateRect(HWND hWnd, LPRECT lpRect, BOOL fErase)
992 {
993 HRGN hRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
994
995 if (!lpRect)
996 {
997 SetLastWin32Error(ERROR_INVALID_PARAMETER);
998 return FALSE;
999 }
1000
1001 NtUserGetUpdateRgn(hWnd, hRgn, fErase);
1002 NtGdiGetRgnBox(hRgn, lpRect);
1003
1004 return lpRect->left < lpRect->right && lpRect->top < lpRect->bottom;
1005 }
1006
1007 /*
1008 * NtUserRedrawWindow
1009 *
1010 * Status
1011 * @implemented
1012 */
1013
1014 BOOL STDCALL
1015 NtUserRedrawWindow(HWND hWnd, CONST RECT *lprcUpdate, HRGN hrgnUpdate,
1016 UINT flags)
1017 {
1018 RECT SafeUpdateRect;
1019 NTSTATUS Status;
1020 PWINDOW_OBJECT Wnd;
1021
1022 if (!(Wnd = IntGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow())))
1023 {
1024 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
1025 return FALSE;
1026 }
1027
1028 if (lprcUpdate != NULL)
1029 {
1030 Status = MmCopyFromCaller(&SafeUpdateRect, (PRECT)lprcUpdate,
1031 sizeof(RECT));
1032
1033 if (!NT_SUCCESS(Status))
1034 {
1035 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1036 return FALSE;
1037 }
1038 }
1039
1040 Status = IntRedrawWindow(Wnd, NULL == lprcUpdate ? NULL : &SafeUpdateRect,
1041 hrgnUpdate, flags);
1042
1043 if (!NT_SUCCESS(Status))
1044 {
1045 /* IntRedrawWindow fails only in case that flags are invalid */
1046 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1047 return FALSE;
1048 }
1049
1050 return TRUE;
1051 }
1052
1053 /*
1054 * NtUserScrollDC
1055 *
1056 * Status
1057 * @implemented
1058 */
1059
1060 DWORD STDCALL
1061 NtUserScrollDC(HDC hDC, INT dx, INT dy, const RECT *lprcScroll,
1062 const RECT *lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate)
1063 {
1064 RECT rSrc, rClipped_src, rClip, rDst, offset;
1065 PDC DC;
1066
1067 /*
1068 * Compute device clipping region (in device coordinates).
1069 */
1070
1071 DC = DC_LockDc(hDC);
1072 if (NULL == DC)
1073 {
1074 return FALSE;
1075 }
1076 if (lprcScroll)
1077 rSrc = *lprcScroll;
1078 else
1079 IntGdiGetClipBox(hDC, &rSrc);
1080 IntLPtoDP(DC, (LPPOINT)&rSrc, 2);
1081
1082 if (lprcClip)
1083 rClip = *lprcClip;
1084 else
1085 IntGdiGetClipBox(hDC, &rClip);
1086 IntLPtoDP(DC, (LPPOINT)&rClip, 2);
1087
1088 NtGdiIntersectRect(&rClipped_src, &rSrc, &rClip);
1089
1090 rDst = rClipped_src;
1091 NtGdiSetRect(&offset, 0, 0, dx, dy);
1092 IntLPtoDP(DC, (LPPOINT)&offset, 2);
1093 NtGdiOffsetRect(&rDst, offset.right - offset.left, offset.bottom - offset.top);
1094 NtGdiIntersectRect(&rDst, &rDst, &rClip);
1095 DC_UnlockDc(hDC);
1096
1097 /*
1098 * Copy bits, if possible.
1099 */
1100
1101 if (rDst.bottom > rDst.top && rDst.right > rDst.left)
1102 {
1103 RECT rDst_lp = rDst, rSrc_lp = rDst;
1104
1105 NtGdiOffsetRect(&rSrc_lp, offset.left - offset.right, offset.top - offset.bottom);
1106 NtGdiDPtoLP(hDC, (LPPOINT)&rDst_lp, 2);
1107 NtGdiDPtoLP(hDC, (LPPOINT)&rSrc_lp, 2);
1108
1109 if (!NtGdiBitBlt(hDC, rDst_lp.left, rDst_lp.top, rDst_lp.right - rDst_lp.left,
1110 rDst_lp.bottom - rDst_lp.top, hDC, rSrc_lp.left, rSrc_lp.top,
1111 SRCCOPY))
1112 return FALSE;
1113 }
1114
1115 /*
1116 * Compute update areas. This is the clipped source or'ed with the
1117 * unclipped source translated minus the clipped src translated (rDst)
1118 * all clipped to rClip.
1119 */
1120
1121 if (hrgnUpdate || lprcUpdate)
1122 {
1123 HRGN hRgn = hrgnUpdate, hRgn2;
1124
1125 if (hRgn)
1126 NtGdiSetRectRgn(hRgn, rClipped_src.left, rClipped_src.top, rClipped_src.right, rClipped_src.bottom);
1127 else
1128 hRgn = NtGdiCreateRectRgn(rClipped_src.left, rClipped_src.top, rClipped_src.right, rClipped_src.bottom);
1129
1130 hRgn2 = NtGdiCreateRectRgnIndirect(&rSrc);
1131 NtGdiOffsetRgn(hRgn2, offset.right - offset.left, offset.bottom - offset.top);
1132 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_OR);
1133
1134 NtGdiSetRectRgn(hRgn2, rDst.left, rDst.top, rDst.right, rDst.bottom);
1135 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_DIFF);
1136
1137 NtGdiSetRectRgn(hRgn2, rClip.left, rClip.top, rClip.right, rClip.bottom);
1138 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_AND);
1139
1140 if (lprcUpdate)
1141 {
1142 NtGdiGetRgnBox(hRgn, lprcUpdate);
1143
1144 /* Put the lprcUpdate in logical coordinate */
1145 NtGdiDPtoLP(hDC, (LPPOINT)lprcUpdate, 2);
1146 }
1147 if (!hrgnUpdate)
1148 NtGdiDeleteObject(hRgn);
1149 NtGdiDeleteObject(hRgn2);
1150 }
1151 return TRUE;
1152 }
1153
1154 /*
1155 * NtUserScrollWindowEx
1156 *
1157 * Status
1158 * @implemented
1159 */
1160
1161 DWORD STDCALL
1162 NtUserScrollWindowEx(HWND hWnd, INT dx, INT dy, const RECT *rect,
1163 const RECT *clipRect, HRGN hrgnUpdate, LPRECT rcUpdate, UINT flags)
1164 {
1165 RECT rc, cliprc, caretrc;
1166 INT Result;
1167 PWINDOW_OBJECT Window;
1168 HDC hDC;
1169 HRGN hrgnTemp;
1170 HWND hwndCaret;
1171 BOOL bUpdate = (rcUpdate || hrgnUpdate || flags & (SW_INVALIDATE | SW_ERASE));
1172 BOOL bOwnRgn = TRUE;
1173
1174 Window = IntGetWindowObject(hWnd);
1175 if (!Window || !IntIsWindowDrawable(Window))
1176 {
1177 IntReleaseWindowObject(Window);
1178 return ERROR;
1179 }
1180
1181 IntGetClientRect(Window, &rc);
1182 if (rect)
1183 NtGdiIntersectRect(&rc, &rc, rect);
1184
1185 if (clipRect)
1186 NtGdiIntersectRect(&cliprc, &rc, clipRect);
1187 else
1188 cliprc = rc;
1189
1190 if (cliprc.right <= cliprc.left || cliprc.bottom <= cliprc.top ||
1191 (dx == 0 && dy == 0))
1192 {
1193 return NULLREGION;
1194 }
1195
1196 caretrc = rc;
1197 hwndCaret = IntFixCaret(hWnd, &caretrc, flags);
1198
1199 if (hrgnUpdate)
1200 bOwnRgn = FALSE;
1201 else if (bUpdate)
1202 hrgnUpdate = NtGdiCreateRectRgn(0, 0, 0, 0);
1203
1204 hDC = NtUserGetDCEx( hWnd, 0, DCX_CACHE | DCX_USESTYLE );
1205 if (hDC)
1206 {
1207 HRGN hRgn = NtGdiCreateRectRgn( 0, 0, 0, 0 );
1208
1209 NtUserScrollDC(hDC, dx, dy, &rc, &cliprc, hrgnUpdate, rcUpdate);
1210 NtUserReleaseDC(hWnd, hDC);
1211 if (bUpdate)
1212 NtGdiCombineRgn(hrgnUpdate, hrgnUpdate, hRgn, RGN_OR);
1213 else
1214 NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_INVALIDATE | RDW_ERASE);
1215 NtGdiDeleteObject(hRgn);
1216 }
1217
1218 /*
1219 * Take into account the fact that some damage may have occurred during
1220 * the scroll.
1221 */
1222
1223 hrgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
1224 Result = NtUserGetUpdateRgn(hWnd, hrgnTemp, FALSE);
1225 if (Result != NULLREGION)
1226 {
1227 HRGN hrgnClip = NtGdiCreateRectRgnIndirect(&cliprc);
1228 NtGdiOffsetRgn(hrgnTemp, dx, dy);
1229 NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnClip, RGN_AND);
1230 NtUserRedrawWindow(hWnd, NULL, hrgnTemp, RDW_INVALIDATE | RDW_ERASE);
1231 NtGdiDeleteObject(hrgnClip);
1232 }
1233 NtGdiDeleteObject(hrgnTemp);
1234
1235 if (flags & SW_SCROLLCHILDREN)
1236 {
1237 HWND *List = IntWinListChildren(Window);
1238 if (List)
1239 {
1240 int i;
1241 RECT r, dummy;
1242 POINT ClientOrigin;
1243
1244 for (i = 0; List[i]; i++)
1245 {
1246 NtUserGetWindowRect(List[i], &r);
1247 IntGetClientOrigin(hWnd, &ClientOrigin);
1248 r.left -= ClientOrigin.x;
1249 r.top -= ClientOrigin.y;
1250 r.right -= ClientOrigin.x;
1251 r.bottom -= ClientOrigin.y;
1252 if (!rect || NtGdiIntersectRect(&dummy, &r, &rc))
1253 WinPosSetWindowPos(List[i], 0, r.left + dx, r.top + dy, 0, 0,
1254 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
1255 SWP_NOREDRAW);
1256 }
1257 ExFreePool(List);
1258 }
1259 }
1260
1261 if (flags & (SW_INVALIDATE | SW_ERASE))
1262 NtUserRedrawWindow(hWnd, NULL, hrgnUpdate, RDW_INVALIDATE | RDW_ERASE |
1263 ((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
1264 ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0));
1265
1266 if (bOwnRgn && hrgnUpdate)
1267 NtGdiDeleteObject(hrgnUpdate);
1268
1269 if (hwndCaret)
1270 {
1271 IntSetCaretPos(caretrc.left + dx, caretrc.top + dy);
1272 NtUserShowCaret(hwndCaret);
1273 }
1274
1275 IntReleaseWindowObject(Window);
1276
1277 return Result;
1278 }
1279
1280 /* EOF */