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