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