179cf652b210390c310bcdc9ac897db36007d4f4
[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$
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 <w32k.h>
34
35 #define NDEBUG
36 #include <debug.h>
37
38 /* PRIVATE FUNCTIONS **********************************************************/
39
40 /**
41 * @name IntIntersectWithParents
42 *
43 * Intersect window rectangle with all parent client rectangles.
44 *
45 * @param Child
46 * Pointer to child window to start intersecting from.
47 * @param WindowRect
48 * Pointer to rectangle that we want to intersect in screen
49 * coordinates on input and intersected rectangle on output (if TRUE
50 * is returned).
51 *
52 * @return
53 * If any parent is minimized or invisible or the resulting rectangle
54 * is empty then FALSE is returned. Otherwise TRUE is returned.
55 */
56
57 BOOL FASTCALL
58 IntIntersectWithParents(PWINDOW_OBJECT Child, PRECT WindowRect)
59 {
60 PWINDOW_OBJECT ParentWindow;
61
62 ParentWindow = Child->Parent;
63 while (ParentWindow != NULL)
64 {
65 if (!(ParentWindow->Style & WS_VISIBLE) ||
66 (ParentWindow->Style & WS_MINIMIZE))
67 {
68 return FALSE;
69 }
70
71 if (!IntGdiIntersectRect(WindowRect, WindowRect, &ParentWindow->ClientRect))
72 {
73 return FALSE;
74 }
75
76 /* FIXME: Layered windows. */
77
78 ParentWindow = ParentWindow->Parent;
79 }
80
81 return TRUE;
82 }
83
84 VOID FASTCALL
85 IntValidateParent(PWINDOW_OBJECT Child, HRGN ValidRegion)
86 {
87 PWINDOW_OBJECT ParentWindow = Child->Parent;
88
89 while (ParentWindow)
90 {
91 if (ParentWindow->Style & WS_CLIPCHILDREN)
92 break;
93
94 if (ParentWindow->UpdateRegion != 0)
95 {
96 NtGdiCombineRgn(ParentWindow->UpdateRegion, ParentWindow->UpdateRegion,
97 ValidRegion, RGN_DIFF);
98 /* FIXME: If the resulting region is empty, remove fake posted paint message */
99 }
100
101 ParentWindow = ParentWindow->Parent;
102 }
103 }
104
105 /**
106 * @name IntCalcWindowRgn
107 *
108 * Get a window or client region.
109 */
110
111 HRGN FASTCALL
112 IntCalcWindowRgn(PWINDOW_OBJECT Window, BOOL Client)
113 {
114 HRGN hRgnWindow;
115 UINT RgnType;
116
117 if (Client)
118 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
119 else
120 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
121
122 if (Window->WindowRegion != NULL && !(Window->Style & WS_MINIMIZE))
123 {
124 NtGdiOffsetRgn(hRgnWindow,
125 -Window->WindowRect.left,
126 -Window->WindowRect.top);
127 RgnType = NtGdiCombineRgn(hRgnWindow, hRgnWindow, Window->WindowRegion, RGN_AND);
128 NtGdiOffsetRgn(hRgnWindow,
129 Window->WindowRect.left,
130 Window->WindowRect.top);
131 }
132
133 return hRgnWindow;
134 }
135
136 /**
137 * @name IntGetNCUpdateRgn
138 *
139 * Get non-client update region of a window and optionally validate it.
140 *
141 * @param Window
142 * Pointer to window to get the NC update region from.
143 * @param Validate
144 * Set to TRUE to force validating the NC update region.
145 *
146 * @return
147 * Handle to NC update region. The caller is responsible for deleting
148 * it.
149 */
150
151 HRGN FASTCALL
152 IntGetNCUpdateRgn(PWINDOW_OBJECT Window, BOOL Validate)
153 {
154 HRGN hRgnNonClient;
155 HRGN hRgnWindow;
156 UINT RgnType;
157
158 if (Window->UpdateRegion != NULL &&
159 Window->UpdateRegion != (HRGN)1)
160 {
161 hRgnNonClient = NtGdiCreateRectRgn(0, 0, 0, 0);
162 hRgnWindow = IntCalcWindowRgn(Window, TRUE);
163
164 /*
165 * If region creation fails it's safe to fallback to whole
166 * window region.
167 */
168
169 if (hRgnNonClient == NULL)
170 {
171 return (HRGN)1;
172 }
173
174 RgnType = NtGdiCombineRgn(hRgnNonClient, Window->UpdateRegion,
175 hRgnWindow, RGN_DIFF);
176 if (RgnType == ERROR)
177 {
178 NtGdiDeleteObject(hRgnNonClient);
179 return (HRGN)1;
180 }
181 else if (RgnType == NULLREGION)
182 {
183 NtGdiDeleteObject(hRgnNonClient);
184 return NULL;
185 }
186
187 /*
188 * Remove the nonclient region from the standard update region if
189 * we were asked for it.
190 */
191
192 if (Validate)
193 {
194 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
195 hRgnWindow, RGN_AND) == NULLREGION)
196 {
197 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
198 NtGdiDeleteObject(Window->UpdateRegion);
199 Window->UpdateRegion = NULL;
200 if (!(Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT))
201 MsqDecPaintCountQueue(Window->MessageQueue);
202 }
203 }
204
205 NtGdiDeleteObject(hRgnWindow);
206
207 return hRgnNonClient;
208 }
209 else
210 {
211 return Window->UpdateRegion;
212 }
213 }
214
215 /*
216 * IntPaintWindows
217 *
218 * Internal function used by IntRedrawWindow.
219 */
220
221 STATIC VOID FASTCALL
222 co_IntPaintWindows(PWINDOW_OBJECT Window, ULONG Flags)
223 {
224 HDC hDC;
225 HWND hWnd = Window->hSelf;
226 HRGN TempRegion;
227
228 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
229 {
230 if (Window->UpdateRegion)
231 {
232 IntValidateParent(Window, Window->UpdateRegion);
233 }
234
235 if (Flags & RDW_UPDATENOW)
236 {
237 if (Window->UpdateRegion != NULL ||
238 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
239 {
240 co_IntSendMessage(hWnd, WM_PAINT, 0, 0);
241 }
242 }
243 else
244 {
245 if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
246 {
247 TempRegion = IntGetNCUpdateRgn(Window, TRUE);
248 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
249 MsqDecPaintCountQueue(Window->MessageQueue);
250 co_IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)TempRegion, 0);
251 if ((HANDLE) 1 != TempRegion && NULL != TempRegion)
252 {
253 /* NOTE: The region can already be deleted! */
254 GDIOBJ_FreeObj(TempRegion, GDI_OBJECT_TYPE_REGION | GDI_OBJECT_TYPE_SILENT);
255 }
256 }
257
258 if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
259 {
260 if (Window->UpdateRegion)
261 {
262 hDC = UserGetDCEx(Window, Window->UpdateRegion,
263 DCX_CACHE | DCX_USESTYLE |
264 DCX_INTERSECTRGN | DCX_KEEPCLIPRGN);
265 if (co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)hDC, 0))
266 {
267 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
268 }
269 UserReleaseDC(Window, hDC, FALSE);
270 }
271 }
272 }
273 }
274
275 /*
276 * Check that the window is still valid at this point
277 */
278 if (!IntIsWindow(hWnd))
279 {
280 return;
281 }
282
283 /*
284 * Paint child windows.
285 */
286 if (!(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
287 ((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN)))
288 {
289 HWND *List, *phWnd;
290
291 if ((List = IntWinListChildren(Window)))
292 {
293 for (phWnd = List; *phWnd; ++phWnd)
294 {
295 Window = UserGetWindowObject(*phWnd);
296 if (Window && (Window->Style & WS_VISIBLE))
297 {
298 UserRefObjectCo(Window);
299 co_IntPaintWindows(Window, Flags);
300 UserDerefObjectCo(Window);
301 }
302 }
303 ExFreePool(List);
304 }
305 }
306 }
307
308 /*
309 * IntInvalidateWindows
310 *
311 * Internal function used by IntRedrawWindow.
312 */
313
314 VOID FASTCALL
315 IntInvalidateWindows(PWINDOW_OBJECT Window, HRGN hRgn, ULONG Flags)
316 {
317 INT RgnType;
318 BOOL HadPaintMessage, HadNCPaintMessage;
319 BOOL HasPaintMessage, HasNCPaintMessage;
320
321 /*
322 * Clip the given region with window rectangle (or region)
323 */
324
325 if (!Window->WindowRegion || (Window->Style & WS_MINIMIZE))
326 {
327 HRGN hRgnWindow;
328
329 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
330 RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnWindow, RGN_AND);
331 NtGdiDeleteObject(hRgnWindow);
332 }
333 else
334 {
335 NtGdiOffsetRgn(hRgn,
336 -Window->WindowRect.left,
337 -Window->WindowRect.top);
338 RgnType = NtGdiCombineRgn(hRgn, hRgn, Window->WindowRegion, RGN_AND);
339 NtGdiOffsetRgn(hRgn,
340 Window->WindowRect.left,
341 Window->WindowRect.top);
342 }
343
344 /*
345 * Save current state of pending updates
346 */
347
348 HadPaintMessage = Window->UpdateRegion != NULL ||
349 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
350 HadNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
351
352 /*
353 * Update the region and flags
354 */
355
356 if (Flags & RDW_INVALIDATE && RgnType != NULLREGION)
357 {
358 if (Window->UpdateRegion == NULL)
359 {
360 Window->UpdateRegion = NtGdiCreateRectRgn(0, 0, 0, 0);
361 GDIOBJ_SetOwnership(Window->UpdateRegion, NULL);
362 }
363
364 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
365 hRgn, RGN_OR) == NULLREGION)
366 {
367 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
368 NtGdiDeleteObject(Window->UpdateRegion);
369 Window->UpdateRegion = NULL;
370 }
371
372 if (Flags & RDW_FRAME)
373 Window->Flags |= WINDOWOBJECT_NEED_NCPAINT;
374 if (Flags & RDW_ERASE)
375 Window->Flags |= WINDOWOBJECT_NEED_ERASEBKGND;
376
377 Flags |= RDW_FRAME;
378 }
379
380 if (Flags & RDW_VALIDATE && RgnType != NULLREGION)
381 {
382 if (Window->UpdateRegion != NULL)
383 {
384 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
385 hRgn, RGN_DIFF) == NULLREGION)
386 {
387 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
388 NtGdiDeleteObject(Window->UpdateRegion);
389 Window->UpdateRegion = NULL;
390 }
391 }
392
393 if (Window->UpdateRegion == NULL)
394 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
395 if (Flags & RDW_NOFRAME)
396 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
397 if (Flags & RDW_NOERASE)
398 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
399 }
400
401 if (Flags & RDW_INTERNALPAINT)
402 {
403 Window->Flags |= WINDOWOBJECT_NEED_INTERNALPAINT;
404 }
405
406 if (Flags & RDW_NOINTERNALPAINT)
407 {
408 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
409 }
410
411 /*
412 * Process children if needed
413 */
414
415 if (!(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
416 ((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN)))
417 {
418 PWINDOW_OBJECT Child;
419
420 for (Child = Window->FirstChild; Child; Child = Child->NextSibling)
421 {
422 if (Child->Style & WS_VISIBLE)
423 {
424 /*
425 * Recursive call to update children UpdateRegion
426 */
427 HRGN hRgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
428 NtGdiCombineRgn(hRgnTemp, hRgn, 0, RGN_COPY);
429 IntInvalidateWindows(Child, hRgnTemp, Flags);
430 NtGdiDeleteObject(hRgnTemp);
431 }
432
433 }
434 }
435
436 /*
437 * Fake post paint messages to window message queue if needed
438 */
439
440 HasPaintMessage = Window->UpdateRegion != NULL ||
441 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
442 HasNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
443
444 if (HasPaintMessage != HadPaintMessage)
445 {
446 if (HadPaintMessage)
447 MsqDecPaintCountQueue(Window->MessageQueue);
448 else
449 MsqIncPaintCountQueue(Window->MessageQueue);
450 }
451
452 if (HasNCPaintMessage != HadNCPaintMessage)
453 {
454 if (HadNCPaintMessage)
455 MsqDecPaintCountQueue(Window->MessageQueue);
456 else
457 MsqIncPaintCountQueue(Window->MessageQueue);
458 }
459
460 }
461
462 /*
463 * IntIsWindowDrawable
464 *
465 * Remarks
466 * Window is drawable when it is visible and all parents are not
467 * minimized.
468 */
469
470 BOOL FASTCALL
471 IntIsWindowDrawable(PWINDOW_OBJECT Window)
472 {
473 PWINDOW_OBJECT Wnd;
474
475 for (Wnd = Window; Wnd != NULL; Wnd = Wnd->Parent)
476 {
477 if (!(Wnd->Style & WS_VISIBLE) ||
478 ((Wnd->Style & WS_MINIMIZE) && (Wnd != Window)))
479 {
480 return FALSE;
481 }
482 }
483
484 return TRUE;
485 }
486
487 /*
488 * IntRedrawWindow
489 *
490 * Internal version of NtUserRedrawWindow that takes WINDOW_OBJECT as
491 * first parameter.
492 */
493
494 BOOL FASTCALL
495 co_UserRedrawWindow(PWINDOW_OBJECT Window, const RECT* UpdateRect, HRGN UpdateRgn,
496 ULONG Flags)
497 {
498 HRGN hRgn = NULL;
499
500 /*
501 * Step 1.
502 * Validation of passed parameters.
503 */
504
505 if (!IntIsWindowDrawable(Window) ||
506 (Flags & (RDW_VALIDATE | RDW_INVALIDATE)) ==
507 (RDW_VALIDATE | RDW_INVALIDATE))
508 {
509 return FALSE;
510 }
511
512 /*
513 * Step 2.
514 * Transform the parameters UpdateRgn and UpdateRect into
515 * a region hRgn specified in screen coordinates.
516 */
517
518 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE))
519 {
520 if (UpdateRgn != NULL)
521 {
522 hRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
523 if (NtGdiCombineRgn(hRgn, UpdateRgn, NULL, RGN_COPY) == NULLREGION)
524 NtGdiDeleteObject(hRgn);
525 else
526 NtGdiOffsetRgn(hRgn, Window->ClientRect.left, Window->ClientRect.top);
527 }
528 else if (UpdateRect != NULL)
529 {
530 if (!IntGdiIsEmptyRect(UpdateRect))
531 {
532 hRgn = UnsafeIntCreateRectRgnIndirect((RECT *)UpdateRect);
533 NtGdiOffsetRgn(hRgn, Window->ClientRect.left, Window->ClientRect.top);
534 }
535 }
536 else if ((Flags & (RDW_INVALIDATE | RDW_FRAME)) == (RDW_INVALIDATE | RDW_FRAME) ||
537 (Flags & (RDW_VALIDATE | RDW_NOFRAME)) == (RDW_VALIDATE | RDW_NOFRAME))
538 {
539 if (!IntGdiIsEmptyRect(&Window->WindowRect))
540 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
541 }
542 else
543 {
544 if (!IntGdiIsEmptyRect(&Window->ClientRect))
545 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
546 }
547 }
548
549 /*
550 * Step 3.
551 * Adjust the window update region depending on hRgn and flags.
552 */
553
554 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT) &&
555 hRgn != NULL)
556 {
557 IntInvalidateWindows(Window, hRgn, Flags);
558 }
559
560 /*
561 * Step 4.
562 * Repaint and erase windows if needed.
563 */
564
565 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
566 {
567 co_IntPaintWindows(Window, Flags);
568 }
569
570 /*
571 * Step 5.
572 * Cleanup ;-)
573 */
574
575 if (hRgn != NULL)
576 {
577 NtGdiDeleteObject(hRgn);
578 }
579
580 return TRUE;
581 }
582
583 BOOL FASTCALL
584 IntIsWindowDirty(PWINDOW_OBJECT Window)
585 {
586 return (Window->Style & WS_VISIBLE) &&
587 ((Window->UpdateRegion != NULL) ||
588 (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT) ||
589 (Window->Flags & WINDOWOBJECT_NEED_NCPAINT));
590 }
591
592 HWND FASTCALL
593 IntFindWindowToRepaint(PWINDOW_OBJECT Window, PW32THREAD Thread)
594 {
595 HWND hChild;
596 PWINDOW_OBJECT TempWindow;
597
598 for (; Window != NULL; Window = Window->NextSibling)
599 {
600 if (IntWndBelongsToThread(Window, Thread) &&
601 IntIsWindowDirty(Window))
602 {
603 /* Make sure all non-transparent siblings are already drawn. */
604 if (Window->ExStyle & WS_EX_TRANSPARENT)
605 {
606 for (TempWindow = Window->NextSibling; TempWindow != NULL;
607 TempWindow = TempWindow->NextSibling)
608 {
609 if (!(TempWindow->ExStyle & WS_EX_TRANSPARENT) &&
610 IntWndBelongsToThread(TempWindow, Thread) &&
611 IntIsWindowDirty(TempWindow))
612 {
613 return TempWindow->hSelf;
614 }
615 }
616 }
617
618 return Window->hSelf;
619 }
620
621 if (Window->FirstChild)
622 {
623 hChild = IntFindWindowToRepaint(Window->FirstChild, Thread);
624 if (hChild != NULL)
625 return hChild;
626 }
627 }
628
629 return NULL;
630 }
631
632 BOOL FASTCALL
633 IntGetPaintMessage(HWND hWnd, UINT MsgFilterMin, UINT MsgFilterMax,
634 PW32THREAD Thread, MSG *Message, BOOL Remove)
635 {
636 PUSER_MESSAGE_QUEUE MessageQueue = (PUSER_MESSAGE_QUEUE)Thread->MessageQueue;
637
638 if (!MessageQueue->PaintCount)
639 return FALSE;
640
641 if ((MsgFilterMin != 0 || MsgFilterMax != 0) &&
642 (MsgFilterMin > WM_PAINT || MsgFilterMax < WM_PAINT))
643 return FALSE;
644
645 Message->hwnd = IntFindWindowToRepaint(UserGetDesktopWindow(), PsGetWin32Thread());
646
647 if (Message->hwnd == NULL)
648 {
649 DPRINT1("PAINTING BUG: Thread marked as containing dirty windows, but no dirty windows found!\n");
650 return FALSE;
651 }
652
653 if (hWnd != NULL && Message->hwnd != hWnd)
654 return FALSE;
655
656 Message->message = WM_PAINT;
657 Message->wParam = Message->lParam = 0;
658
659 return TRUE;
660 }
661
662 static
663 HWND FASTCALL
664 co_IntFixCaret(PWINDOW_OBJECT Window, LPRECT lprc, UINT flags)
665 {
666 PDESKTOP_OBJECT Desktop;
667 PTHRDCARETINFO CaretInfo;
668 HWND hWndCaret;
669 PWINDOW_OBJECT WndCaret;
670
671 ASSERT_REFS_CO(Window);
672
673 Desktop = PsGetCurrentThread()->Tcb.Win32Thread->Desktop;
674 CaretInfo = ((PUSER_MESSAGE_QUEUE)Desktop->ActiveMessageQueue)->CaretInfo;
675 hWndCaret = CaretInfo->hWnd;
676
677 WndCaret = UserGetWindowObject(hWndCaret);
678
679 //fix: check for WndCaret can be null
680 if (WndCaret == Window ||
681 ((flags & SW_SCROLLCHILDREN) && IntIsChildWindow(Window, WndCaret)))
682 {
683 POINT pt, FromOffset, ToOffset, Offset;
684 RECT rcCaret;
685
686 pt.x = CaretInfo->Pos.x;
687 pt.y = CaretInfo->Pos.y;
688 IntGetClientOrigin(WndCaret, &FromOffset);
689 IntGetClientOrigin(Window, &ToOffset);
690 Offset.x = FromOffset.x - ToOffset.x;
691 Offset.y = FromOffset.y - ToOffset.y;
692 rcCaret.left = pt.x;
693 rcCaret.top = pt.y;
694 rcCaret.right = pt.x + CaretInfo->Size.cx;
695 rcCaret.bottom = pt.y + CaretInfo->Size.cy;
696 if (IntGdiIntersectRect(lprc, lprc, &rcCaret))
697 {
698 co_UserHideCaret(0);
699 lprc->left = pt.x;
700 lprc->top = pt.y;
701 return hWndCaret;
702 }
703 }
704
705 return 0;
706 }
707
708 /* PUBLIC FUNCTIONS ***********************************************************/
709
710 /*
711 * NtUserBeginPaint
712 *
713 * Status
714 * @implemented
715 */
716
717 HDC STDCALL
718 NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* UnsafePs)
719 {
720 PWINDOW_OBJECT Window = NULL;
721 PAINTSTRUCT Ps;
722 PROSRGNDATA Rgn;
723 NTSTATUS Status;
724 DECLARE_RETURN(HDC);
725
726 DPRINT("Enter NtUserBeginPaint\n");
727 UserEnterExclusive();
728
729 if (!(Window = UserGetWindowObject(hWnd)))
730 {
731 RETURN( NULL);
732 }
733
734 UserRefObjectCo(Window);
735
736 co_UserHideCaret(Window);
737
738 if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
739 {
740 HRGN hRgn;
741
742 hRgn = IntGetNCUpdateRgn(Window, FALSE);
743 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
744 MsqDecPaintCountQueue(Window->MessageQueue);
745 co_IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)hRgn, 0);
746 if (hRgn != (HANDLE)1 && hRgn != NULL)
747 {
748 /* NOTE: The region can already by deleted! */
749 GDIOBJ_FreeObj(hRgn, GDI_OBJECT_TYPE_REGION | GDI_OBJECT_TYPE_SILENT);
750 }
751 }
752
753 RtlZeroMemory(&Ps, sizeof(PAINTSTRUCT));
754
755 Ps.hdc = UserGetDCEx(Window, Window->UpdateRegion, DCX_INTERSECTRGN | DCX_USESTYLE);
756 if (!Ps.hdc)
757 {
758 RETURN(NULL);
759 }
760
761 if (Window->UpdateRegion != NULL)
762 {
763 MsqDecPaintCountQueue(Window->MessageQueue);
764 Rgn = RGNDATA_LockRgn(Window->UpdateRegion);
765 if (NULL != Rgn)
766 {
767 UnsafeIntGetRgnBox(Rgn, &Ps.rcPaint);
768 RGNDATA_UnlockRgn(Rgn);
769 IntGdiIntersectRect(&Ps.rcPaint, &Ps.rcPaint, &Window->ClientRect);
770 IntGdiOffsetRect(&Ps.rcPaint,
771 -Window->ClientRect.left,
772 -Window->ClientRect.top);
773 }
774 else
775 {
776 IntGetClientRect(Window, &Ps.rcPaint);
777 }
778 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
779 Window->UpdateRegion = NULL;
780 }
781 else
782 {
783 if (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
784 MsqDecPaintCountQueue(Window->MessageQueue);
785
786 IntGetClientRect(Window, &Ps.rcPaint);
787 }
788
789 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
790
791 if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
792 {
793 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
794 Ps.fErase = !co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)Ps.hdc, 0);
795 }
796 else
797 {
798 Ps.fErase = FALSE;
799 }
800
801 Status = MmCopyToCaller(UnsafePs, &Ps, sizeof(PAINTSTRUCT));
802 if (! NT_SUCCESS(Status))
803 {
804 SetLastNtError(Status);
805 RETURN(NULL);
806 }
807
808 RETURN(Ps.hdc);
809
810 CLEANUP:
811 if (Window) UserDerefObjectCo(Window);
812
813 DPRINT("Leave NtUserBeginPaint, ret=%i\n",_ret_);
814 UserLeave();
815 END_CLEANUP;
816
817 }
818
819 /*
820 * NtUserEndPaint
821 *
822 * Status
823 * @implemented
824 */
825
826 BOOL STDCALL
827 NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* lPs)
828 {
829 PWINDOW_OBJECT Window;
830 DECLARE_RETURN(BOOL);
831
832 DPRINT("Enter NtUserEndPaint\n");
833 UserEnterExclusive();
834
835 if (!(Window = UserGetWindowObject(hWnd)))
836 {
837 RETURN(FALSE);
838 }
839
840 UserReleaseDC(Window, lPs->hdc, TRUE);
841
842 UserRefObjectCo(Window);
843 co_UserShowCaret(Window);
844 UserDerefObjectCo(Window);
845
846 RETURN(TRUE);
847
848 CLEANUP:
849 DPRINT("Leave NtUserEndPaint, ret=%i\n",_ret_);
850 UserLeave();
851 END_CLEANUP;
852 }
853
854
855 INT FASTCALL
856 co_UserGetUpdateRgn(PWINDOW_OBJECT Window, HRGN hRgn, BOOL bErase)
857 {
858 int RegionType;
859 RECT Rect;
860
861 ASSERT_REFS_CO(Window);
862
863 if (Window->UpdateRegion == NULL)
864 {
865 RegionType = (NtGdiSetRectRgn(hRgn, 0, 0, 0, 0) ? NULLREGION : ERROR);
866 }
867 else
868 {
869 Rect = Window->ClientRect;
870 IntIntersectWithParents(Window, &Rect);
871 NtGdiSetRectRgn(hRgn, Rect.left, Rect.top, Rect.right, Rect.bottom);
872 RegionType = NtGdiCombineRgn(hRgn, hRgn, Window->UpdateRegion, RGN_AND);
873 NtGdiOffsetRgn(hRgn, -Window->ClientRect.left, -Window->ClientRect.top);
874 }
875
876 if (bErase && RegionType != NULLREGION && RegionType != ERROR)
877 {
878 co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
879 }
880
881 return RegionType;
882 }
883
884 /*
885 * NtUserGetUpdateRgn
886 *
887 * Status
888 * @implemented
889 */
890
891 INT STDCALL
892 NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
893 {
894 DECLARE_RETURN(INT);
895 PWINDOW_OBJECT Window;
896 INT ret;
897
898 DPRINT("Enter NtUserGetUpdateRgn\n");
899 UserEnterExclusive();
900
901 if (!(Window = UserGetWindowObject(hWnd)))
902 {
903 RETURN(ERROR);
904 }
905
906 UserRefObjectCo(Window);
907 ret = co_UserGetUpdateRgn(Window, hRgn, bErase);
908 UserDerefObjectCo(Window);
909
910 RETURN(ret);
911
912 CLEANUP:
913 DPRINT("Leave NtUserGetUpdateRgn, ret=%i\n",_ret_);
914 UserLeave();
915 END_CLEANUP;
916 }
917
918 /*
919 * NtUserGetUpdateRect
920 *
921 * Status
922 * @implemented
923 */
924
925 BOOL STDCALL
926 NtUserGetUpdateRect(HWND hWnd, LPRECT UnsafeRect, BOOL bErase)
927 {
928 PWINDOW_OBJECT Window;
929 RECT Rect;
930 INT RegionType;
931 PROSRGNDATA RgnData;
932 NTSTATUS Status;
933 DECLARE_RETURN(BOOL);
934
935 DPRINT("Enter NtUserGetUpdateRect\n");
936 UserEnterExclusive();
937
938 if (!(Window = UserGetWindowObject(hWnd)))
939 {
940 RETURN(FALSE);
941 }
942
943 if (Window->UpdateRegion == NULL)
944 {
945 Rect.left = Rect.top = Rect.right = Rect.bottom = 0;
946 }
947 else
948 {
949 /* Get the update region bounding box. */
950 if (Window->UpdateRegion == (HRGN)1)
951 {
952 Rect = Window->ClientRect;
953 }
954 else
955 {
956 RgnData = RGNDATA_LockRgn(Window->UpdateRegion);
957 ASSERT(RgnData != NULL);
958 RegionType = UnsafeIntGetRgnBox(RgnData, &Rect);
959 RGNDATA_UnlockRgn(RgnData);
960
961 if (RegionType != ERROR && RegionType != NULLREGION)
962 IntGdiIntersectRect(&Rect, &Rect, &Window->ClientRect);
963 }
964
965 if (IntIntersectWithParents(Window, &Rect))
966 {
967 IntGdiOffsetRect(&Rect,
968 -Window->ClientRect.left,
969 -Window->ClientRect.top);
970 } else
971 {
972 Rect.left = Rect.top = Rect.right = Rect.bottom = 0;
973 }
974 }
975
976 if (bErase && !IntGdiIsEmptyRect(&Rect))
977 {
978 UserRefObjectCo(Window);
979 co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
980 UserDerefObjectCo(Window);
981 }
982
983 if (UnsafeRect != NULL)
984 {
985 Status = MmCopyToCaller(UnsafeRect, &Rect, sizeof(RECT));
986 if (!NT_SUCCESS(Status))
987 {
988 SetLastWin32Error(ERROR_INVALID_PARAMETER);
989 RETURN(FALSE);
990 }
991 }
992
993 RETURN(Window->UpdateRegion != NULL);
994
995 CLEANUP:
996 DPRINT("Leave NtUserGetUpdateRect, ret=%i\n",_ret_);
997 UserLeave();
998 END_CLEANUP;
999 }
1000
1001 /*
1002 * NtUserRedrawWindow
1003 *
1004 * Status
1005 * @implemented
1006 */
1007
1008 BOOL STDCALL
1009 NtUserRedrawWindow(HWND hWnd, CONST RECT *lprcUpdate, HRGN hrgnUpdate,
1010 UINT flags)
1011 {
1012 RECT SafeUpdateRect;
1013 NTSTATUS Status;
1014 PWINDOW_OBJECT Wnd;
1015 DECLARE_RETURN(BOOL);
1016
1017 DPRINT("Enter NtUserRedrawWindow\n");
1018 UserEnterExclusive();
1019
1020 if (!(Wnd = UserGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow())))
1021 {
1022 RETURN( FALSE);
1023 }
1024
1025 if (lprcUpdate != NULL)
1026 {
1027 Status = MmCopyFromCaller(&SafeUpdateRect, (PRECT)lprcUpdate,
1028 sizeof(RECT));
1029
1030 if (!NT_SUCCESS(Status))
1031 {
1032 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1033 RETURN( FALSE);
1034 }
1035 }
1036
1037 UserRefObjectCo(Wnd);
1038
1039 Status = co_UserRedrawWindow(Wnd, NULL == lprcUpdate ? NULL : &SafeUpdateRect,
1040 hrgnUpdate, flags);
1041
1042 UserDerefObjectCo(Wnd);
1043
1044 if (!NT_SUCCESS(Status))
1045 {
1046 /* IntRedrawWindow fails only in case that flags are invalid */
1047 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1048 RETURN( FALSE);
1049 }
1050
1051 RETURN( TRUE);
1052
1053 CLEANUP:
1054 DPRINT("Leave NtUserRedrawWindow, ret=%i\n",_ret_);
1055 UserLeave();
1056 END_CLEANUP;
1057 }
1058
1059
1060
1061 static
1062 DWORD FASTCALL
1063 UserScrollDC(HDC hDC, INT dx, INT dy, const RECT *lprcScroll,
1064 const RECT *lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate)
1065 {
1066 RECT rSrc, rClipped_src, rClip, rDst, offset;
1067 PDC DC;
1068
1069 /*
1070 * Compute device clipping region (in device coordinates).
1071 */
1072
1073 DC = DC_LockDc(hDC);
1074 if (NULL == DC)
1075 {
1076 return FALSE;
1077 }
1078 if (lprcScroll)
1079 rSrc = *lprcScroll;
1080 else
1081 IntGdiGetClipBox(hDC, &rSrc);
1082 IntLPtoDP(DC, (LPPOINT)&rSrc, 2);
1083
1084 if (lprcClip)
1085 rClip = *lprcClip;
1086 else
1087 IntGdiGetClipBox(hDC, &rClip);
1088 IntLPtoDP(DC, (LPPOINT)&rClip, 2);
1089
1090 IntGdiIntersectRect(&rClipped_src, &rSrc, &rClip);
1091
1092 rDst = rClipped_src;
1093 IntGdiSetRect(&offset, 0, 0, dx, dy);
1094 IntLPtoDP(DC, (LPPOINT)&offset, 2);
1095 IntGdiOffsetRect(&rDst, offset.right - offset.left, offset.bottom - offset.top);
1096 IntGdiIntersectRect(&rDst, &rDst, &rClip);
1097
1098 /*
1099 * Copy bits, if possible.
1100 */
1101
1102 if (rDst.bottom > rDst.top && rDst.right > rDst.left)
1103 {
1104 RECT rDst_lp = rDst, rSrc_lp = rDst;
1105
1106 IntGdiOffsetRect(&rSrc_lp, offset.left - offset.right, offset.top - offset.bottom);
1107 IntDPtoLP(DC, (LPPOINT)&rDst_lp, 2);
1108 IntDPtoLP(DC, (LPPOINT)&rSrc_lp, 2);
1109 DC_UnlockDc(DC);
1110
1111 if (!NtGdiBitBlt(hDC, rDst_lp.left, rDst_lp.top, rDst_lp.right - rDst_lp.left,
1112 rDst_lp.bottom - rDst_lp.top, hDC, rSrc_lp.left, rSrc_lp.top,
1113 SRCCOPY))
1114 return FALSE;
1115 }
1116 else
1117 {
1118 DC_UnlockDc(DC);
1119 }
1120
1121 /*
1122 * Compute update areas. This is the clipped source or'ed with the
1123 * unclipped source translated minus the clipped src translated (rDst)
1124 * all clipped to rClip.
1125 */
1126
1127 if (hrgnUpdate || lprcUpdate)
1128 {
1129 HRGN hRgn = hrgnUpdate, hRgn2;
1130
1131 if (hRgn)
1132 NtGdiSetRectRgn(hRgn, rClipped_src.left, rClipped_src.top, rClipped_src.right, rClipped_src.bottom);
1133 else
1134 hRgn = NtGdiCreateRectRgn(rClipped_src.left, rClipped_src.top, rClipped_src.right, rClipped_src.bottom);
1135
1136 hRgn2 = UnsafeIntCreateRectRgnIndirect(&rSrc);
1137 NtGdiOffsetRgn(hRgn2, offset.right - offset.left, offset.bottom - offset.top);
1138 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_OR);
1139
1140 NtGdiSetRectRgn(hRgn2, rDst.left, rDst.top, rDst.right, rDst.bottom);
1141 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_DIFF);
1142
1143 NtGdiSetRectRgn(hRgn2, rClip.left, rClip.top, rClip.right, rClip.bottom);
1144 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_AND);
1145
1146 if (lprcUpdate)
1147 {
1148 NtGdiGetRgnBox(hRgn, lprcUpdate);
1149
1150 /* Put the lprcUpdate in logical coordinate */
1151 NtGdiDPtoLP(hDC, (LPPOINT)lprcUpdate, 2);
1152 }
1153 if (!hrgnUpdate)
1154 NtGdiDeleteObject(hRgn);
1155 NtGdiDeleteObject(hRgn2);
1156 }
1157 return TRUE;
1158 }
1159
1160
1161
1162
1163 /*
1164 * NtUserScrollDC
1165 *
1166 * Status
1167 * @implemented
1168 */
1169
1170 DWORD STDCALL
1171 NtUserScrollDC(HDC hDC, INT dx, INT dy, const RECT *lprcScroll,
1172 const RECT *lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate)
1173 {
1174 DECLARE_RETURN(DWORD);
1175
1176 DPRINT("Enter NtUserScrollDC\n");
1177 UserEnterExclusive();
1178
1179 RETURN( UserScrollDC(hDC, dx, dy, lprcScroll, lprcClip, hrgnUpdate, lprcUpdate));
1180
1181 CLEANUP:
1182 DPRINT("Leave NtUserScrollDC, ret=%i\n",_ret_);
1183 UserLeave();
1184 END_CLEANUP;
1185
1186 }
1187
1188 /*
1189 * NtUserScrollWindowEx
1190 *
1191 * Status
1192 * @implemented
1193 */
1194
1195 DWORD STDCALL
1196 NtUserScrollWindowEx(HWND hWnd, INT dx, INT dy, const RECT *UnsafeRect,
1197 const RECT *UnsafeClipRect, HRGN hrgnUpdate, LPRECT rcUpdate, UINT flags)
1198 {
1199 RECT rc, cliprc, caretrc, rect, clipRect;
1200 INT Result;
1201 PWINDOW_OBJECT Window = NULL, CaretWnd;
1202 HDC hDC;
1203 HRGN hrgnTemp;
1204 HWND hwndCaret;
1205 BOOL bUpdate = (rcUpdate || hrgnUpdate || flags & (SW_INVALIDATE | SW_ERASE));
1206 BOOL bOwnRgn = TRUE;
1207 NTSTATUS Status;
1208 DECLARE_RETURN(DWORD);
1209
1210 DPRINT("Enter NtUserScrollWindowEx\n");
1211 UserEnterExclusive();
1212
1213 Window = UserGetWindowObject(hWnd);
1214 if (!Window || !IntIsWindowDrawable(Window))
1215 {
1216 Window = NULL; /* prevent deref at cleanup */
1217 RETURN( ERROR);
1218 }
1219 UserRefObjectCo(Window);
1220
1221 IntGetClientRect(Window, &rc);
1222
1223 if (NULL != UnsafeRect)
1224 {
1225 Status = MmCopyFromCaller(&rect, UnsafeRect, sizeof(RECT));
1226 if (! NT_SUCCESS(Status))
1227 {
1228 SetLastNtError(Status);
1229 RETURN( ERROR);
1230 }
1231 IntGdiIntersectRect(&rc, &rc, &rect);
1232 }
1233
1234 if (NULL != UnsafeClipRect)
1235 {
1236 Status = MmCopyFromCaller(&clipRect, UnsafeClipRect, sizeof(RECT));
1237 if (! NT_SUCCESS(Status))
1238 {
1239 SetLastNtError(Status);
1240 RETURN( ERROR);
1241 }
1242 IntGdiIntersectRect(&cliprc, &rc, &clipRect);
1243 }
1244 else
1245 cliprc = rc;
1246
1247 if (cliprc.right <= cliprc.left || cliprc.bottom <= cliprc.top ||
1248 (dx == 0 && dy == 0))
1249 {
1250 RETURN( NULLREGION);
1251 }
1252
1253 caretrc = rc;
1254 hwndCaret = co_IntFixCaret(Window, &caretrc, flags);
1255
1256 if (hrgnUpdate)
1257 bOwnRgn = FALSE;
1258 else if (bUpdate)
1259 hrgnUpdate = NtGdiCreateRectRgn(0, 0, 0, 0);
1260
1261 hDC = UserGetDCEx(Window, 0, DCX_CACHE | DCX_USESTYLE);
1262 if (hDC)
1263 {
1264 UserScrollDC(hDC, dx, dy, &rc, &cliprc, hrgnUpdate, rcUpdate);
1265 UserReleaseDC(Window, hDC, FALSE);
1266 }
1267
1268 /*
1269 * Take into account the fact that some damage may have occurred during
1270 * the scroll.
1271 */
1272
1273 hrgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
1274 Result = co_UserGetUpdateRgn(Window, hrgnTemp, FALSE);
1275 if (Result != NULLREGION)
1276 {
1277 HRGN hrgnClip = UnsafeIntCreateRectRgnIndirect(&cliprc);
1278 NtGdiOffsetRgn(hrgnTemp, dx, dy);
1279 NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnClip, RGN_AND);
1280 co_UserRedrawWindow(Window, NULL, hrgnTemp, RDW_INVALIDATE | RDW_ERASE);
1281 NtGdiDeleteObject(hrgnClip);
1282 }
1283
1284 NtGdiDeleteObject(hrgnTemp);
1285
1286 if (flags & SW_SCROLLCHILDREN)
1287 {
1288 HWND *List = IntWinListChildren(Window);
1289 if (List)
1290 {
1291 int i;
1292 RECT r, dummy;
1293 POINT ClientOrigin;
1294 PWINDOW_OBJECT Wnd;
1295
1296 IntGetClientOrigin(Window, &ClientOrigin);
1297 for (i = 0; List[i]; i++)
1298 {
1299 if (!(Wnd = UserGetWindowObject(List[i])))
1300 continue;
1301
1302 r = Wnd->WindowRect;
1303 r.left -= ClientOrigin.x;
1304 r.top -= ClientOrigin.y;
1305 r.right -= ClientOrigin.x;
1306 r.bottom -= ClientOrigin.y;
1307
1308 if (! UnsafeRect || IntGdiIntersectRect(&dummy, &r, &rc))
1309 {
1310 UserRefObjectCo(Wnd);
1311 co_WinPosSetWindowPos(Wnd, 0, r.left + dx, r.top + dy, 0, 0,
1312 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
1313 SWP_NOREDRAW);
1314 UserDerefObjectCo(Wnd);
1315 }
1316
1317 }
1318 ExFreePool(List);
1319 }
1320 }
1321
1322 if (flags & (SW_INVALIDATE | SW_ERASE))
1323 co_UserRedrawWindow(Window, NULL, hrgnUpdate, RDW_INVALIDATE | RDW_ERASE |
1324 ((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
1325 ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0));
1326
1327 if (bOwnRgn && hrgnUpdate)
1328 NtGdiDeleteObject(hrgnUpdate);
1329
1330 if ((CaretWnd = UserGetWindowObject(hwndCaret)))
1331 {
1332 UserRefObjectCo(CaretWnd);
1333
1334 co_IntSetCaretPos(caretrc.left + dx, caretrc.top + dy);
1335 co_UserShowCaret(CaretWnd);
1336
1337 UserDerefObjectCo(CaretWnd);
1338 }
1339
1340 RETURN(Result);
1341
1342 CLEANUP:
1343 if (Window)
1344 UserDerefObjectCo(Window);
1345
1346 DPRINT("Leave NtUserScrollWindowEx, ret=%i\n",_ret_);
1347 UserLeave();
1348 END_CLEANUP;
1349 }
1350
1351 /* EOF */