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