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