moved IntGetDesktopWindow() to desktop.c and a few minor fixes
[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.45 2003/12/12 18:18:21 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
53 #define NDEBUG
54 #include <debug.h>
55
56 /* PRIVATE FUNCTIONS **********************************************************/
57
58 VOID FASTCALL
59 IntValidateParent(PWINDOW_OBJECT Child)
60 {
61 HWND Parent;
62 PWINDOW_OBJECT ParentWindow;
63
64 Parent = NtUserGetAncestor(Child->Self, GA_PARENT);
65 while (Parent && Parent != IntGetDesktopWindow())
66 {
67 ParentWindow = IntGetWindowObject(Parent);
68 if (ParentWindow && !(ParentWindow->Style & WS_CLIPCHILDREN))
69 {
70 if (ParentWindow->UpdateRegion != 0)
71 {
72 INT OffsetX, OffsetY;
73
74 /*
75 * We must offset the child region by the offset of the
76 * child rect in the parent.
77 */
78 OffsetX = Child->WindowRect.left - ParentWindow->WindowRect.left;
79 OffsetY = Child->WindowRect.top - ParentWindow->WindowRect.top;
80 NtGdiOffsetRgn(Child->UpdateRegion, OffsetX, OffsetY );
81 NtGdiCombineRgn(ParentWindow->UpdateRegion, ParentWindow->UpdateRegion,
82 Child->UpdateRegion, RGN_DIFF);
83 /* FIXME: If the resulting region is empty, remove fake posted paint message */
84 NtGdiOffsetRgn(Child->UpdateRegion, -OffsetX, -OffsetY);
85 }
86 }
87 IntReleaseWindowObject(ParentWindow);
88 Parent = NtUserGetAncestor(Parent, GA_PARENT);
89 }
90 }
91
92 /*
93 * IntGetNCUpdateRegion
94 *
95 * Get nonclient part of window update region.
96 *
97 * Return Value
98 * Handle to region that represents invalid nonclient window area. The
99 * caller is responsible for deleting it.
100 *
101 * Remarks
102 * This function also marks the nonclient update region of window
103 * as valid, clears the WINDOWOBJECT_NEED_NCPAINT flag and removes
104 * the fake paint message from message queue if the Remove is set
105 * to TRUE.
106 */
107
108 HRGN FASTCALL
109 IntGetNCUpdateRegion(PWINDOW_OBJECT Window, BOOL Remove)
110 {
111 HRGN WindowRgn;
112 HRGN NonclientRgn;
113
114 WindowRgn = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
115 NtGdiOffsetRgn(WindowRgn,
116 -Window->WindowRect.left,
117 -Window->WindowRect.top);
118 NonclientRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
119 if (NtGdiCombineRgn(NonclientRgn, Window->UpdateRegion,
120 WindowRgn, RGN_DIFF) == NULLREGION)
121 {
122 NtGdiDeleteObject(NonclientRgn);
123 NonclientRgn = NULL;
124 }
125 if (Remove)
126 {
127 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
128 WindowRgn, RGN_AND) == NULLREGION)
129 {
130 NtGdiDeleteObject(Window->UpdateRegion);
131 Window->UpdateRegion = NULL;
132 }
133 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
134 MsqDecPaintCountQueue(Window->MessageQueue);
135 }
136
137 return NonclientRgn;
138 }
139
140 /*
141 * IntPaintWindows
142 *
143 * Internal function used by IntRedrawWindow.
144 */
145
146 VOID FASTCALL
147 IntPaintWindows(PWINDOW_OBJECT Window, ULONG Flags)
148 {
149 HDC hDC;
150 HWND hWnd = Window->Self;
151
152 if (! (Window->Style & WS_VISIBLE))
153 {
154 return;
155 }
156
157 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
158 {
159 if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
160 {
161 NtUserSendMessage(hWnd, WM_NCPAINT, (WPARAM)IntGetNCUpdateRegion(Window, TRUE), 0);
162 }
163
164 if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
165 {
166 if (Window->UpdateRegion)
167 {
168 hDC = NtUserGetDCEx(hWnd, 0, DCX_CACHE | DCX_USESTYLE |
169 DCX_INTERSECTUPDATE);
170 if (hDC != NULL)
171 {
172 if (NtUserSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)hDC, 0))
173 {
174 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
175 }
176 NtUserReleaseDC(hWnd, hDC);
177 }
178 }
179 }
180
181 if (Flags & RDW_UPDATENOW)
182 {
183 if (Window->UpdateRegion != NULL ||
184 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
185 {
186 NtUserSendMessage(hWnd, WM_PAINT, 0, 0);
187 if (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
188 {
189 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
190 if (Window->UpdateRegion == NULL)
191 {
192 MsqDecPaintCountQueue(Window->MessageQueue);
193 }
194 }
195 }
196 }
197 }
198
199 /*
200 * Check that the window is still valid at this point
201 */
202
203 if (! IntIsWindow(hWnd))
204 {
205 return;
206 }
207
208 /*
209 * Paint child windows.
210 */
211 if (!(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
212 ((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN)) &&
213 ! IntIsDesktopWindow(Window))
214 {
215 HWND *List, *phWnd;
216
217 if ((List = IntWinListChildren(Window)))
218 {
219 for (phWnd = List; *phWnd; ++phWnd)
220 {
221 Window = IntGetWindowObject(*phWnd);
222 if (Window)
223 {
224 IntPaintWindows(Window, Flags);
225 IntReleaseWindowObject(Window);
226 }
227 }
228 ExFreePool(List);
229 }
230 }
231 }
232
233 /*
234 * IntInvalidateWindows
235 *
236 * Internal function used by IntRedrawWindow.
237 */
238
239 VOID FASTCALL
240 IntInvalidateWindows(PWINDOW_OBJECT Window, HRGN hRgn, ULONG Flags,
241 BOOL ValidateParent)
242 {
243 INT RgnType;
244 BOOL HadPaintMessage, HadNCPaintMessage;
245 BOOL HasPaintMessage, HasNCPaintMessage;
246 HRGN hRgnWindow;
247
248 /*
249 * Clip the given region with window rectangle (or region)
250 */
251
252 #ifdef TODO
253 if (!Window->WindowRegion)
254 #endif
255 {
256 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
257 NtGdiOffsetRgn(hRgnWindow,
258 -Window->WindowRect.left,
259 -Window->WindowRect.top);
260 RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnWindow, RGN_AND);
261 NtGdiDeleteObject(hRgnWindow);
262 }
263 #ifdef TODO
264 else
265 {
266 RgnType = NtGdiCombineRgn(hRgn, hRgn, Window->WindowRegion, RGN_AND);
267 }
268 #endif
269
270 /*
271 * Save current state of pending updates
272 */
273
274 HadPaintMessage = Window->UpdateRegion != NULL ||
275 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
276 HadNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
277
278 /*
279 * Update the region and flags
280 */
281
282 if (Flags & RDW_INVALIDATE && RgnType != NULLREGION)
283 {
284 if (Window->UpdateRegion == NULL)
285 {
286 Window->UpdateRegion = NtGdiCreateRectRgn(0, 0, 0, 0);
287 }
288
289 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
290 hRgn, RGN_OR) == NULLREGION)
291 {
292 NtGdiDeleteObject(Window->UpdateRegion);
293 Window->UpdateRegion = NULL;
294 }
295
296 if (Flags & RDW_FRAME)
297 Window->Flags |= WINDOWOBJECT_NEED_NCPAINT;
298 if (Flags & RDW_ERASE)
299 Window->Flags |= WINDOWOBJECT_NEED_ERASEBKGND;
300
301 Flags |= RDW_FRAME;
302 }
303
304 if (Flags & RDW_VALIDATE && RgnType != NULLREGION)
305 {
306 if (Window->UpdateRegion != NULL)
307 {
308 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
309 hRgn, RGN_DIFF) == NULLREGION)
310 {
311 NtGdiDeleteObject(Window->UpdateRegion);
312 Window->UpdateRegion = NULL;
313 }
314 }
315
316 if (Window->UpdateRegion == NULL)
317 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
318 if (Flags & RDW_NOFRAME)
319 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
320 if (Flags & RDW_NOERASE)
321 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
322 }
323
324 if (Flags & RDW_INTERNALPAINT)
325 {
326 Window->Flags |= WINDOWOBJECT_NEED_INTERNALPAINT;
327 }
328
329 if (Flags & RDW_NOINTERNALPAINT)
330 {
331 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
332 }
333
334 /*
335 * Validate parent covered by region
336 */
337
338 if (ValidateParent)
339 {
340 IntValidateParent(Window);
341 }
342
343 /*
344 * Process children if needed
345 */
346
347 if (!(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
348 ((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN)))
349 {
350 HWND *List, *phWnd;
351 PWINDOW_OBJECT Child;
352
353 if ((List = IntWinListChildren(Window)))
354 {
355 for (phWnd = List; *phWnd; ++phWnd)
356 {
357 Child = IntGetWindowObject(*phWnd);
358 if ((Child->Style & (WS_VISIBLE | WS_MINIMIZE)) == WS_VISIBLE)
359 {
360 /*
361 * Recursive call to update children UpdateRegion
362 */
363 HRGN hRgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
364 NtGdiCombineRgn(hRgnTemp, hRgn, 0, RGN_COPY);
365 NtGdiOffsetRgn(hRgnTemp,
366 Window->WindowRect.left - Child->WindowRect.left,
367 Window->WindowRect.top - Child->WindowRect.top);
368 IntInvalidateWindows(Child, hRgnTemp, Flags, FALSE);
369
370 /*
371 * Update our UpdateRegion depending on children
372 */
373 NtGdiCombineRgn(hRgnTemp, Child->UpdateRegion, 0, RGN_COPY);
374 NtGdiOffsetRgn(hRgnTemp,
375 Child->WindowRect.left - Window->WindowRect.left,
376 Child->WindowRect.top - Window->WindowRect.top);
377 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
378 NtGdiOffsetRgn(hRgnWindow,
379 -Window->WindowRect.left,
380 -Window->WindowRect.top);
381 NtGdiCombineRgn(hRgnTemp, hRgnTemp, hRgnWindow, RGN_AND);
382 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
383 hRgnTemp, RGN_DIFF) == NULLREGION)
384 {
385 NtGdiDeleteObject(Window->UpdateRegion);
386 Window->UpdateRegion = NULL;
387 }
388 NtGdiDeleteObject(hRgnTemp);
389 }
390 IntReleaseWindowObject(Child);
391 }
392 ExFreePool(List);
393 }
394 }
395
396 /*
397 * Fake post paint messages to window message queue if needed
398 */
399 HasPaintMessage = Window->UpdateRegion != NULL ||
400 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
401 HasNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
402
403 if (HasPaintMessage != HadPaintMessage)
404 {
405 if (HadPaintMessage)
406 MsqDecPaintCountQueue(Window->MessageQueue);
407 else
408 MsqIncPaintCountQueue(Window->MessageQueue);
409 }
410
411 if (HasNCPaintMessage != HadNCPaintMessage)
412 {
413 if (HadNCPaintMessage)
414 MsqDecPaintCountQueue(Window->MessageQueue);
415 else
416 MsqIncPaintCountQueue(Window->MessageQueue);
417 }
418 }
419
420 /*
421 * IntIsWindowDrawable
422 *
423 * Remarks
424 * Window is drawable when it is visible, all parents are not
425 * minimized, and it is itself not minimized.
426 */
427
428 BOOL FASTCALL
429 IntIsWindowDrawable(PWINDOW_OBJECT Window)
430 {
431 for (; Window; Window = Window->Parent)
432 {
433 if ((Window->Style & (WS_VISIBLE | WS_MINIMIZE)) != WS_VISIBLE)
434 return FALSE;
435 }
436
437 return TRUE;
438 }
439
440 /*
441 * IntRedrawWindow
442 *
443 * Internal version of NtUserRedrawWindow that takes WINDOW_OBJECT as
444 * first parameter.
445 */
446
447 BOOL FASTCALL
448 IntRedrawWindow(PWINDOW_OBJECT Window, const RECT* UpdateRect, HRGN UpdateRgn,
449 ULONG Flags)
450 {
451 HRGN hRgn = NULL;
452
453 /*
454 * Step 1.
455 * Validation of passed parameters.
456 */
457
458 if (!IntIsWindowDrawable(Window) ||
459 (Flags & (RDW_VALIDATE | RDW_INVALIDATE)) ==
460 (RDW_VALIDATE | RDW_INVALIDATE))
461 {
462 return FALSE;
463 }
464
465 /*
466 * Step 2.
467 * Transform the parameters UpdateRgn and UpdateRect into
468 * a region hRgn specified in window coordinates.
469 */
470
471 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE))
472 {
473 if (UpdateRgn != NULL)
474 {
475 hRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
476 NtGdiCombineRgn(hRgn, UpdateRgn, NULL, RGN_COPY);
477 NtGdiOffsetRgn(hRgn,
478 Window->ClientRect.left - Window->WindowRect.left,
479 Window->ClientRect.top - Window->WindowRect.top);
480 } else
481 if (UpdateRect != NULL)
482 {
483 hRgn = UnsafeIntCreateRectRgnIndirect((RECT *)UpdateRect);
484 NtGdiOffsetRgn(hRgn,
485 Window->ClientRect.left - Window->WindowRect.left,
486 Window->ClientRect.top - Window->WindowRect.top);
487 } else
488 if ((Flags & (RDW_INVALIDATE | RDW_FRAME)) == (RDW_INVALIDATE | RDW_FRAME) ||
489 (Flags & (RDW_VALIDATE | RDW_NOFRAME)) == (RDW_VALIDATE | RDW_NOFRAME))
490 {
491 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
492 NtGdiOffsetRgn(hRgn,
493 -Window->WindowRect.left,
494 -Window->WindowRect.top);
495 }
496 else
497 {
498 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
499 NtGdiOffsetRgn(hRgn,
500 -Window->WindowRect.left,
501 -Window->WindowRect.top);
502 }
503 }
504
505 /*
506 * Step 3.
507 * Adjust the window update region depending on hRgn and flags.
508 */
509
510 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT))
511 {
512 IntInvalidateWindows(Window, hRgn, Flags, TRUE);
513 } else
514 if (Window->UpdateRegion != NULL && Flags & RDW_ERASENOW)
515 {
516 /* Validate parent covered by region. */
517 IntValidateParent(Window);
518 }
519
520 /*
521 * Step 4.
522 * Repaint and erase windows if needed.
523 */
524
525 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
526 {
527 IntPaintWindows(Window, Flags);
528 }
529
530 /*
531 * Step 5.
532 * Cleanup ;-)
533 */
534
535 if (NULL != hRgn)
536 {
537 NtGdiDeleteObject(hRgn);
538 }
539
540 return TRUE;
541 }
542
543 HWND STDCALL
544 IntFindWindowToRepaint(HWND hWnd, PW32THREAD Thread)
545 {
546 PWINDOW_OBJECT Window;
547 PWINDOW_OBJECT Child;
548 HWND hFoundWnd = NULL;
549
550 if (hWnd == NULL)
551 {
552 PLIST_ENTRY CurrentEntry;
553
554 ExAcquireFastMutex(&Thread->WindowListLock);
555
556 for (CurrentEntry = Thread->WindowListHead.Flink;
557 CurrentEntry != &Thread->WindowListHead;
558 CurrentEntry = CurrentEntry->Flink)
559 {
560 Window = CONTAINING_RECORD(CurrentEntry, WINDOW_OBJECT, ThreadListEntry);
561 if (Window->Parent != NULL && !IntIsDesktopWindow(Window->Parent))
562 {
563 continue;
564 }
565 if (Window->Style & WS_VISIBLE)
566 {
567 hFoundWnd = IntFindWindowToRepaint(Window->Self, Thread);
568 if (hFoundWnd != NULL)
569 {
570 ExReleaseFastMutex(&Thread->WindowListLock);
571 return hFoundWnd;
572 }
573 }
574 }
575
576 ExReleaseFastMutex(&Thread->WindowListLock);
577 return NULL;
578 }
579 else
580 {
581 Window = IntGetWindowObject(hWnd);
582 if (Window == NULL)
583 return NULL;
584
585 if (Window->UpdateRegion != NULL ||
586 Window->Flags & (WINDOWOBJECT_NEED_INTERNALPAINT | WINDOWOBJECT_NEED_NCPAINT))
587 {
588 IntReleaseWindowObject(Window);
589 return hWnd;
590 }
591
592 ExAcquireFastMutex(&Window->ChildrenListLock);
593
594 for (Child = Window->FirstChild; Child; Child = Child->NextSibling)
595 {
596 if (Child->Style & WS_VISIBLE &&
597 (Child->UpdateRegion != NULL ||
598 Child->Flags & WINDOWOBJECT_NEED_INTERNALPAINT ||
599 Child->Flags & WINDOWOBJECT_NEED_NCPAINT))
600 {
601 hFoundWnd = Child->Self;
602 break;
603 }
604 }
605
606 if (hFoundWnd == NULL)
607 {
608 for (Child = Window->FirstChild; Child; Child = Child->NextSibling)
609 {
610 if (Child->Style & WS_VISIBLE)
611 {
612 hFoundWnd = IntFindWindowToRepaint(Child->Self, Thread);
613 if (hFoundWnd != NULL)
614 break;
615 }
616 }
617 }
618
619 ExReleaseFastMutex(&Window->ChildrenListLock);
620 IntReleaseWindowObject(Window);
621
622 return hFoundWnd;
623 }
624 }
625
626 BOOL FASTCALL
627 IntGetPaintMessage(HWND hWnd, PW32THREAD Thread, MSG *Message,
628 BOOL Remove)
629 {
630 PWINDOW_OBJECT Window;
631 PUSER_MESSAGE_QUEUE MessageQueue = (PUSER_MESSAGE_QUEUE)Thread->MessageQueue;
632
633 if (!MessageQueue->PaintPosted)
634 return FALSE;
635
636 if (hWnd)
637 Message->hwnd = IntFindWindowToRepaint(hWnd, PsGetWin32Thread());
638 else
639 Message->hwnd = IntFindWindowToRepaint(NULL, PsGetWin32Thread());
640
641 if (Message->hwnd == NULL)
642 {
643 #if 0
644 DPRINT1("PAINTING BUG: Thread marked as containing dirty windows, but no dirty windows found!\n");
645 /* FIXME: Lock the queue! */
646 MessageQueue->PaintPosted = 0;
647 MessageQueue->PaintCount = 0;
648 #endif
649 return FALSE;
650 }
651
652 Window = IntGetWindowObject(Message->hwnd);
653 if (Window != NULL)
654 {
655 if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
656 {
657 Message->message = WM_NCPAINT;
658 Message->wParam = (WPARAM)IntGetNCUpdateRegion(Window, Remove);
659 Message->lParam = 0;
660 } else
661 {
662 Message->message = WM_PAINT;
663 Message->wParam = Message->lParam = 0;
664 if (Remove && Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
665 {
666 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
667 if (Window->UpdateRegion == NULL)
668 {
669 MsqDecPaintCountQueue(Window->MessageQueue);
670 }
671 }
672 }
673
674 IntReleaseWindowObject(Window);
675 return TRUE;
676 }
677
678 return FALSE;
679 }
680
681 HWND FASTCALL
682 IntFixCaret(HWND hWnd, LPRECT lprc, UINT flags)
683 {
684 GUITHREADINFO info;
685
686 if (!NtUserGetGUIThreadInfo(0, &info))
687 return 0;
688 if (!info.hwndCaret)
689 return 0;
690 if (info.hwndCaret == hWnd ||
691 ((flags & SW_SCROLLCHILDREN) && IntIsChildWindow(hWnd, info.hwndCaret)))
692 {
693 POINT pt, FromOffset, ToOffset, Offset;
694
695 pt.x = info.rcCaret.left;
696 pt.y = info.rcCaret.top;
697
698 NtUserGetClientOrigin(info.hwndCaret, &FromOffset);
699 NtUserGetClientOrigin(hWnd, &ToOffset);
700 Offset.x = FromOffset.x - ToOffset.x;
701 Offset.y = FromOffset.y - ToOffset.y;
702 info.rcCaret.left += Offset.x;
703 info.rcCaret.top += Offset.y;
704 info.rcCaret.right += Offset.x;
705 info.rcCaret.bottom += Offset.y;
706 if (NtGdiIntersectRect(lprc, lprc, &info.rcCaret))
707 {
708 NtUserHideCaret(0);
709 lprc->left = pt.x;
710 lprc->top = pt.y;
711 return info.hwndCaret;
712 }
713 }
714
715 return 0;
716 }
717
718 /* PUBLIC FUNCTIONS ***********************************************************/
719
720 /*
721 * NtUserBeginPaint
722 *
723 * Status
724 * @implemented
725 */
726
727 HDC STDCALL
728 NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* lPs)
729 {
730 PWINDOW_OBJECT Window;
731 RECT ClientRect;
732 RECT ClipRect;
733 INT DcxFlags;
734
735 if (!(Window = IntGetWindowObject(hWnd)))
736 {
737 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
738 return NULL;
739 }
740
741 NtUserHideCaret(hWnd);
742
743 DcxFlags = DCX_INTERSECTUPDATE | DCX_WINDOWPAINT | DCX_USESTYLE;
744 if (IntGetClassLong(Window, GCL_STYLE, FALSE) & CS_PARENTDC)
745 {
746 /* FIXME: Is this correct? */
747 /* Don't clip the output to the update region for CS_PARENTDC window */
748 DcxFlags &= ~DCX_INTERSECTUPDATE;
749 }
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 NtGdiGetClipBox(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 = !NtUserSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)lPs->hdc, 0);
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 NtGdiGetClipBox(hDC, &rSrc);
990 NtGdiLPtoDP(hDC, (LPPOINT)&rSrc, 2);
991
992 if (lprcClip)
993 rClip = *lprcClip;
994 else
995 NtGdiGetClipBox(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 * @unimplemented
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 NtUserGetClientOrigin(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 */