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