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