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