Only offset the rect if it's not empty, otherwise we might return negative
[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 if (! IntGdiIsEmptyRect(&Ps.rcPaint))
773 {
774 IntGdiOffsetRect(&Ps.rcPaint,
775 -Window->ClientRect.left,
776 -Window->ClientRect.top);
777 }
778 }
779 else
780 {
781 IntGetClientRect(Window, &Ps.rcPaint);
782 }
783 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
784 Window->UpdateRegion = NULL;
785 }
786 else
787 {
788 if (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
789 MsqDecPaintCountQueue(Window->MessageQueue);
790
791 IntGetClientRect(Window, &Ps.rcPaint);
792 }
793
794 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
795
796 if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
797 {
798 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
799 Ps.fErase = !co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)Ps.hdc, 0);
800 }
801 else
802 {
803 Ps.fErase = FALSE;
804 }
805
806 Status = MmCopyToCaller(UnsafePs, &Ps, sizeof(PAINTSTRUCT));
807 if (! NT_SUCCESS(Status))
808 {
809 SetLastNtError(Status);
810 RETURN(NULL);
811 }
812
813 RETURN(Ps.hdc);
814
815 CLEANUP:
816 if (Window) UserDerefObjectCo(Window);
817
818 DPRINT("Leave NtUserBeginPaint, ret=%i\n",_ret_);
819 UserLeave();
820 END_CLEANUP;
821
822 }
823
824 /*
825 * NtUserEndPaint
826 *
827 * Status
828 * @implemented
829 */
830
831 BOOL STDCALL
832 NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* lPs)
833 {
834 PWINDOW_OBJECT Window;
835 DECLARE_RETURN(BOOL);
836 USER_REFERENCE_ENTRY Ref;
837
838 DPRINT("Enter NtUserEndPaint\n");
839 UserEnterExclusive();
840
841 if (!(Window = UserGetWindowObject(hWnd)))
842 {
843 RETURN(FALSE);
844 }
845
846 UserReleaseDC(Window, lPs->hdc, TRUE);
847
848 UserRefObjectCo(Window, &Ref);
849 co_UserShowCaret(Window);
850 UserDerefObjectCo(Window);
851
852 RETURN(TRUE);
853
854 CLEANUP:
855 DPRINT("Leave NtUserEndPaint, ret=%i\n",_ret_);
856 UserLeave();
857 END_CLEANUP;
858 }
859
860
861 INT FASTCALL
862 co_UserGetUpdateRgn(PWINDOW_OBJECT Window, HRGN hRgn, BOOL bErase)
863 {
864 int RegionType;
865 RECT Rect;
866
867 ASSERT_REFS_CO(Window);
868
869 if (Window->UpdateRegion == NULL)
870 {
871 RegionType = (NtGdiSetRectRgn(hRgn, 0, 0, 0, 0) ? NULLREGION : ERROR);
872 }
873 else
874 {
875 Rect = Window->ClientRect;
876 IntIntersectWithParents(Window, &Rect);
877 NtGdiSetRectRgn(hRgn, Rect.left, Rect.top, Rect.right, Rect.bottom);
878 RegionType = NtGdiCombineRgn(hRgn, hRgn, Window->UpdateRegion, RGN_AND);
879 NtGdiOffsetRgn(hRgn, -Window->ClientRect.left, -Window->ClientRect.top);
880 }
881
882 if (bErase && RegionType != NULLREGION && RegionType != ERROR)
883 {
884 co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
885 }
886
887 return RegionType;
888 }
889
890 /*
891 * NtUserGetUpdateRgn
892 *
893 * Status
894 * @implemented
895 */
896
897 INT STDCALL
898 NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
899 {
900 DECLARE_RETURN(INT);
901 PWINDOW_OBJECT Window;
902 INT ret;
903 USER_REFERENCE_ENTRY Ref;
904
905 DPRINT("Enter NtUserGetUpdateRgn\n");
906 UserEnterExclusive();
907
908 if (!(Window = UserGetWindowObject(hWnd)))
909 {
910 RETURN(ERROR);
911 }
912
913 UserRefObjectCo(Window, &Ref);
914 ret = co_UserGetUpdateRgn(Window, hRgn, bErase);
915 UserDerefObjectCo(Window);
916
917 RETURN(ret);
918
919 CLEANUP:
920 DPRINT("Leave NtUserGetUpdateRgn, ret=%i\n",_ret_);
921 UserLeave();
922 END_CLEANUP;
923 }
924
925 /*
926 * NtUserGetUpdateRect
927 *
928 * Status
929 * @implemented
930 */
931
932 BOOL STDCALL
933 NtUserGetUpdateRect(HWND hWnd, LPRECT UnsafeRect, BOOL bErase)
934 {
935 PWINDOW_OBJECT Window;
936 RECT Rect;
937 INT RegionType;
938 PROSRGNDATA RgnData;
939 NTSTATUS Status;
940 DECLARE_RETURN(BOOL);
941
942 DPRINT("Enter NtUserGetUpdateRect\n");
943 UserEnterExclusive();
944
945 if (!(Window = UserGetWindowObject(hWnd)))
946 {
947 RETURN(FALSE);
948 }
949
950 if (Window->UpdateRegion == NULL)
951 {
952 Rect.left = Rect.top = Rect.right = Rect.bottom = 0;
953 }
954 else
955 {
956 /* Get the update region bounding box. */
957 if (Window->UpdateRegion == (HRGN)1)
958 {
959 Rect = Window->ClientRect;
960 }
961 else
962 {
963 RgnData = RGNDATA_LockRgn(Window->UpdateRegion);
964 ASSERT(RgnData != NULL);
965 RegionType = UnsafeIntGetRgnBox(RgnData, &Rect);
966 RGNDATA_UnlockRgn(RgnData);
967
968 if (RegionType != ERROR && RegionType != NULLREGION)
969 IntGdiIntersectRect(&Rect, &Rect, &Window->ClientRect);
970 }
971
972 if (IntIntersectWithParents(Window, &Rect))
973 {
974 IntGdiOffsetRect(&Rect,
975 -Window->ClientRect.left,
976 -Window->ClientRect.top);
977 } else
978 {
979 Rect.left = Rect.top = Rect.right = Rect.bottom = 0;
980 }
981 }
982
983 if (bErase && !IntGdiIsEmptyRect(&Rect))
984 {
985 USER_REFERENCE_ENTRY Ref;
986 UserRefObjectCo(Window, &Ref);
987 co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
988 UserDerefObjectCo(Window);
989 }
990
991 if (UnsafeRect != NULL)
992 {
993 Status = MmCopyToCaller(UnsafeRect, &Rect, sizeof(RECT));
994 if (!NT_SUCCESS(Status))
995 {
996 SetLastWin32Error(ERROR_INVALID_PARAMETER);
997 RETURN(FALSE);
998 }
999 }
1000
1001 RETURN(!IntGdiIsEmptyRect(&Rect));
1002
1003 CLEANUP:
1004 DPRINT("Leave NtUserGetUpdateRect, ret=%i\n",_ret_);
1005 UserLeave();
1006 END_CLEANUP;
1007 }
1008
1009 /*
1010 * NtUserRedrawWindow
1011 *
1012 * Status
1013 * @implemented
1014 */
1015
1016 BOOL STDCALL
1017 NtUserRedrawWindow(HWND hWnd, CONST RECT *lprcUpdate, HRGN hrgnUpdate,
1018 UINT flags)
1019 {
1020 RECT SafeUpdateRect;
1021 NTSTATUS Status;
1022 PWINDOW_OBJECT Wnd;
1023 DECLARE_RETURN(BOOL);
1024 USER_REFERENCE_ENTRY Ref;
1025
1026 DPRINT("Enter NtUserRedrawWindow\n");
1027 UserEnterExclusive();
1028
1029 if (!(Wnd = UserGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow())))
1030 {
1031 RETURN( FALSE);
1032 }
1033
1034 if (lprcUpdate != NULL)
1035 {
1036 Status = MmCopyFromCaller(&SafeUpdateRect, (PRECT)lprcUpdate,
1037 sizeof(RECT));
1038
1039 if (!NT_SUCCESS(Status))
1040 {
1041 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1042 RETURN( FALSE);
1043 }
1044 }
1045
1046 UserRefObjectCo(Wnd, &Ref);
1047
1048 Status = co_UserRedrawWindow(Wnd, NULL == lprcUpdate ? NULL : &SafeUpdateRect,
1049 hrgnUpdate, flags);
1050
1051 UserDerefObjectCo(Wnd);
1052
1053 if (!NT_SUCCESS(Status))
1054 {
1055 /* IntRedrawWindow fails only in case that flags are invalid */
1056 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1057 RETURN( FALSE);
1058 }
1059
1060 RETURN( TRUE);
1061
1062 CLEANUP:
1063 DPRINT("Leave NtUserRedrawWindow, ret=%i\n",_ret_);
1064 UserLeave();
1065 END_CLEANUP;
1066 }
1067
1068
1069
1070 static
1071 DWORD FASTCALL
1072 UserScrollDC(HDC hDC, INT dx, INT dy, const RECT *lprcScroll,
1073 const RECT *lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate)
1074 {
1075 RECT rSrc, rClipped_src, rClip, rDst, offset;
1076 PDC DC;
1077
1078 /*
1079 * Compute device clipping region (in device coordinates).
1080 */
1081
1082 DC = DC_LockDc(hDC);
1083 if (NULL == DC)
1084 {
1085 return FALSE;
1086 }
1087 if (lprcScroll)
1088 rSrc = *lprcScroll;
1089 else
1090 IntGdiGetClipBox(hDC, &rSrc);
1091 IntLPtoDP(DC, (LPPOINT)&rSrc, 2);
1092
1093 if (lprcClip)
1094 rClip = *lprcClip;
1095 else
1096 IntGdiGetClipBox(hDC, &rClip);
1097 IntLPtoDP(DC, (LPPOINT)&rClip, 2);
1098
1099 IntGdiIntersectRect(&rClipped_src, &rSrc, &rClip);
1100
1101 rDst = rClipped_src;
1102 IntGdiSetRect(&offset, 0, 0, dx, dy);
1103 IntLPtoDP(DC, (LPPOINT)&offset, 2);
1104 IntGdiOffsetRect(&rDst, offset.right - offset.left, offset.bottom - offset.top);
1105 IntGdiIntersectRect(&rDst, &rDst, &rClip);
1106
1107 /*
1108 * Copy bits, if possible.
1109 */
1110
1111 if (rDst.bottom > rDst.top && rDst.right > rDst.left)
1112 {
1113 RECT rDst_lp = rDst, rSrc_lp = rDst;
1114
1115 IntGdiOffsetRect(&rSrc_lp, offset.left - offset.right, offset.top - offset.bottom);
1116 IntDPtoLP(DC, (LPPOINT)&rDst_lp, 2);
1117 IntDPtoLP(DC, (LPPOINT)&rSrc_lp, 2);
1118 DC_UnlockDc(DC);
1119
1120 if (!NtGdiBitBlt(hDC, rDst_lp.left, rDst_lp.top, rDst_lp.right - rDst_lp.left,
1121 rDst_lp.bottom - rDst_lp.top, hDC, rSrc_lp.left, rSrc_lp.top,
1122 SRCCOPY))
1123 return FALSE;
1124 }
1125 else
1126 {
1127 DC_UnlockDc(DC);
1128 }
1129
1130 /*
1131 * Compute update areas. This is the clipped source or'ed with the
1132 * unclipped source translated minus the clipped src translated (rDst)
1133 * all clipped to rClip.
1134 */
1135
1136 if (hrgnUpdate || lprcUpdate)
1137 {
1138 HRGN hRgn = hrgnUpdate, hRgn2;
1139
1140 if (hRgn)
1141 NtGdiSetRectRgn(hRgn, rClipped_src.left, rClipped_src.top, rClipped_src.right, rClipped_src.bottom);
1142 else
1143 hRgn = NtGdiCreateRectRgn(rClipped_src.left, rClipped_src.top, rClipped_src.right, rClipped_src.bottom);
1144
1145 hRgn2 = UnsafeIntCreateRectRgnIndirect(&rSrc);
1146 NtGdiOffsetRgn(hRgn2, offset.right - offset.left, offset.bottom - offset.top);
1147 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_OR);
1148
1149 NtGdiSetRectRgn(hRgn2, rDst.left, rDst.top, rDst.right, rDst.bottom);
1150 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_DIFF);
1151
1152 NtGdiSetRectRgn(hRgn2, rClip.left, rClip.top, rClip.right, rClip.bottom);
1153 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_AND);
1154
1155 if (lprcUpdate)
1156 {
1157 NtGdiGetRgnBox(hRgn, lprcUpdate);
1158
1159 /* Put the lprcUpdate in logical coordinate */
1160 NtGdiDPtoLP(hDC, (LPPOINT)lprcUpdate, 2);
1161 }
1162 if (!hrgnUpdate)
1163 NtGdiDeleteObject(hRgn);
1164 NtGdiDeleteObject(hRgn2);
1165 }
1166 return TRUE;
1167 }
1168
1169
1170
1171
1172 /*
1173 * NtUserScrollDC
1174 *
1175 * Status
1176 * @implemented
1177 */
1178
1179 DWORD STDCALL
1180 NtUserScrollDC(HDC hDC, INT dx, INT dy, const RECT *lprcScroll,
1181 const RECT *lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate)
1182 {
1183 DECLARE_RETURN(DWORD);
1184
1185 DPRINT("Enter NtUserScrollDC\n");
1186 UserEnterExclusive();
1187
1188 RETURN( UserScrollDC(hDC, dx, dy, lprcScroll, lprcClip, hrgnUpdate, lprcUpdate));
1189
1190 CLEANUP:
1191 DPRINT("Leave NtUserScrollDC, ret=%i\n",_ret_);
1192 UserLeave();
1193 END_CLEANUP;
1194
1195 }
1196
1197 /*
1198 * NtUserScrollWindowEx
1199 *
1200 * Status
1201 * @implemented
1202 */
1203
1204 DWORD STDCALL
1205 NtUserScrollWindowEx(HWND hWnd, INT dx, INT dy, const RECT *UnsafeRect,
1206 const RECT *UnsafeClipRect, HRGN hrgnUpdate, LPRECT rcUpdate, UINT flags)
1207 {
1208 RECT rc, cliprc, caretrc, rect, clipRect;
1209 INT Result;
1210 PWINDOW_OBJECT Window = NULL, CaretWnd;
1211 HDC hDC;
1212 HRGN hrgnTemp;
1213 HWND hwndCaret;
1214 BOOL bUpdate = (rcUpdate || hrgnUpdate || flags & (SW_INVALIDATE | SW_ERASE));
1215 BOOL bOwnRgn = TRUE;
1216 NTSTATUS Status;
1217 DECLARE_RETURN(DWORD);
1218 USER_REFERENCE_ENTRY Ref, CaretRef;
1219
1220 DPRINT("Enter NtUserScrollWindowEx\n");
1221 UserEnterExclusive();
1222
1223 Window = UserGetWindowObject(hWnd);
1224 if (!Window || !IntIsWindowDrawable(Window))
1225 {
1226 Window = NULL; /* prevent deref at cleanup */
1227 RETURN( ERROR);
1228 }
1229 UserRefObjectCo(Window, &Ref);
1230
1231 IntGetClientRect(Window, &rc);
1232
1233 if (NULL != UnsafeRect)
1234 {
1235 Status = MmCopyFromCaller(&rect, UnsafeRect, sizeof(RECT));
1236 if (! NT_SUCCESS(Status))
1237 {
1238 SetLastNtError(Status);
1239 RETURN( ERROR);
1240 }
1241 IntGdiIntersectRect(&rc, &rc, &rect);
1242 }
1243
1244 if (NULL != UnsafeClipRect)
1245 {
1246 Status = MmCopyFromCaller(&clipRect, UnsafeClipRect, sizeof(RECT));
1247 if (! NT_SUCCESS(Status))
1248 {
1249 SetLastNtError(Status);
1250 RETURN( ERROR);
1251 }
1252 IntGdiIntersectRect(&cliprc, &rc, &clipRect);
1253 }
1254 else
1255 cliprc = rc;
1256
1257 if (cliprc.right <= cliprc.left || cliprc.bottom <= cliprc.top ||
1258 (dx == 0 && dy == 0))
1259 {
1260 RETURN( NULLREGION);
1261 }
1262
1263 caretrc = rc;
1264 hwndCaret = co_IntFixCaret(Window, &caretrc, flags);
1265
1266 if (hrgnUpdate)
1267 bOwnRgn = FALSE;
1268 else if (bUpdate)
1269 hrgnUpdate = NtGdiCreateRectRgn(0, 0, 0, 0);
1270
1271 hDC = UserGetDCEx(Window, 0, DCX_CACHE | DCX_USESTYLE);
1272 if (hDC)
1273 {
1274 UserScrollDC(hDC, dx, dy, &rc, &cliprc, hrgnUpdate, rcUpdate);
1275 UserReleaseDC(Window, hDC, FALSE);
1276 }
1277
1278 /*
1279 * Take into account the fact that some damage may have occurred during
1280 * the scroll.
1281 */
1282
1283 hrgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
1284 Result = co_UserGetUpdateRgn(Window, hrgnTemp, FALSE);
1285 if (Result != NULLREGION)
1286 {
1287 HRGN hrgnClip = UnsafeIntCreateRectRgnIndirect(&cliprc);
1288 NtGdiOffsetRgn(hrgnTemp, dx, dy);
1289 NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnClip, RGN_AND);
1290 co_UserRedrawWindow(Window, NULL, hrgnTemp, RDW_INVALIDATE | RDW_ERASE);
1291 NtGdiDeleteObject(hrgnClip);
1292 }
1293
1294 NtGdiDeleteObject(hrgnTemp);
1295
1296 if (flags & SW_SCROLLCHILDREN)
1297 {
1298 HWND *List = IntWinListChildren(Window);
1299 if (List)
1300 {
1301 int i;
1302 RECT r, dummy;
1303 POINT ClientOrigin;
1304 PWINDOW_OBJECT Wnd;
1305 USER_REFERENCE_ENTRY WndRef;
1306
1307 IntGetClientOrigin(Window, &ClientOrigin);
1308 for (i = 0; List[i]; i++)
1309 {
1310 if (!(Wnd = UserGetWindowObject(List[i])))
1311 continue;
1312
1313 r = Wnd->WindowRect;
1314 r.left -= ClientOrigin.x;
1315 r.top -= ClientOrigin.y;
1316 r.right -= ClientOrigin.x;
1317 r.bottom -= ClientOrigin.y;
1318
1319 if (! UnsafeRect || IntGdiIntersectRect(&dummy, &r, &rc))
1320 {
1321 UserRefObjectCo(Wnd, &WndRef);
1322 co_WinPosSetWindowPos(Wnd, 0, r.left + dx, r.top + dy, 0, 0,
1323 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
1324 SWP_NOREDRAW);
1325 UserDerefObjectCo(Wnd);
1326 }
1327
1328 }
1329 ExFreePool(List);
1330 }
1331 }
1332
1333 if (flags & (SW_INVALIDATE | SW_ERASE))
1334 co_UserRedrawWindow(Window, NULL, hrgnUpdate, RDW_INVALIDATE | RDW_ERASE |
1335 ((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
1336 ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0));
1337
1338 if (bOwnRgn && hrgnUpdate)
1339 NtGdiDeleteObject(hrgnUpdate);
1340
1341 if ((CaretWnd = UserGetWindowObject(hwndCaret)))
1342 {
1343 UserRefObjectCo(CaretWnd, &CaretRef);
1344
1345 co_IntSetCaretPos(caretrc.left + dx, caretrc.top + dy);
1346 co_UserShowCaret(CaretWnd);
1347
1348 UserDerefObjectCo(CaretWnd);
1349 }
1350
1351 RETURN(Result);
1352
1353 CLEANUP:
1354 if (Window)
1355 UserDerefObjectCo(Window);
1356
1357 DPRINT("Leave NtUserScrollWindowEx, ret=%i\n",_ret_);
1358 UserLeave();
1359 END_CLEANUP;
1360 }
1361
1362 /* EOF */