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