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