[Win32k]
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / painting.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Window painting function
5 * FILE: subsys/win32k/ntuser/painting.c
6 * PROGRAMER: Filip Navara (xnavara@volny.cz)
7 * REVISION HISTORY:
8 * 06/06/2001 Created (?)
9 * 18/11/2003 Complete rewrite
10 */
11
12 /* INCLUDES ******************************************************************/
13
14 #include <win32k.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 /* PRIVATE FUNCTIONS **********************************************************/
20
21 /**
22 * @name IntIntersectWithParents
23 *
24 * Intersect window rectangle with all parent client rectangles.
25 *
26 * @param Child
27 * Pointer to child window to start intersecting from.
28 * @param WindowRect
29 * Pointer to rectangle that we want to intersect in screen
30 * coordinates on input and intersected rectangle on output (if TRUE
31 * is returned).
32 *
33 * @return
34 * If any parent is minimized or invisible or the resulting rectangle
35 * is empty then FALSE is returned. Otherwise TRUE is returned.
36 */
37
38 BOOL FASTCALL
39 IntIntersectWithParents(PWINDOW_OBJECT Child, RECTL *WindowRect)
40 {
41 PWINDOW_OBJECT ParentWindow;
42 PWND ParentWnd;
43
44 ParentWindow = Child->spwndParent;
45 while (ParentWindow != NULL)
46 {
47 ParentWnd = ParentWindow->Wnd;
48 if (!(ParentWnd->style & WS_VISIBLE) ||
49 (ParentWnd->style & WS_MINIMIZE))
50 {
51 return FALSE;
52 }
53
54 if (!RECTL_bIntersectRect(WindowRect, WindowRect, &ParentWnd->rcClient))
55 {
56 return FALSE;
57 }
58
59 /* FIXME: Layered windows. */
60
61 ParentWindow = ParentWindow->spwndParent;
62 }
63
64 return TRUE;
65 }
66
67 BOOL FASTCALL
68 IntValidateParent(PWINDOW_OBJECT Child, HRGN hValidateRgn, BOOL Recurse)
69 {
70 PWINDOW_OBJECT ParentWindow = Child->spwndParent;
71 PWND ParentWnd;
72
73 while (ParentWindow)
74 {
75 ParentWnd = ParentWindow->Wnd;
76 if (ParentWnd->style & WS_CLIPCHILDREN)
77 break;
78
79 if (ParentWindow->hrgnUpdate != 0)
80 {
81 if (Recurse)
82 return FALSE;
83
84 IntInvalidateWindows( ParentWindow,
85 hValidateRgn,
86 RDW_VALIDATE | RDW_NOCHILDREN);
87 }
88
89 ParentWindow = ParentWindow->spwndParent;
90 }
91
92 return TRUE;
93 }
94
95 /**
96 * @name IntCalcWindowRgn
97 *
98 * Get a window or client region.
99 */
100
101 HRGN FASTCALL
102 IntCalcWindowRgn(PWINDOW_OBJECT Window, BOOL Client)
103 {
104 PWND Wnd;
105 HRGN hRgnWindow;
106
107 Wnd = Window->Wnd;
108 if (Client)
109 hRgnWindow = IntSysCreateRectRgnIndirect(&Wnd->rcClient);
110 else
111 hRgnWindow = IntSysCreateRectRgnIndirect(&Wnd->rcWindow);
112
113 if (Window->hrgnClip != NULL && !(Wnd->style & WS_MINIMIZE))
114 {
115 NtGdiOffsetRgn(hRgnWindow,
116 -Wnd->rcWindow.left,
117 -Wnd->rcWindow.top);
118 NtGdiCombineRgn(hRgnWindow, hRgnWindow, Window->hrgnClip, RGN_AND);
119 NtGdiOffsetRgn(hRgnWindow,
120 Wnd->rcWindow.left,
121 Wnd->rcWindow.top);
122 }
123
124 return hRgnWindow;
125 }
126
127 /**
128 * @name IntGetNCUpdateRgn
129 *
130 * Get non-client update region of a window and optionally validate it.
131 *
132 * @param Window
133 * Pointer to window to get the NC update region from.
134 * @param Validate
135 * Set to TRUE to force validating the NC update region.
136 *
137 * @return
138 * Handle to NC update region. The caller is responsible for deleting
139 * it.
140 */
141
142 HRGN FASTCALL
143 IntGetNCUpdateRgn(PWINDOW_OBJECT Window, BOOL Validate)
144 {
145 HRGN hRgnNonClient;
146 HRGN hRgnWindow;
147 UINT RgnType;
148
149 if (Window->hrgnUpdate != NULL &&
150 Window->hrgnUpdate != (HRGN)1)
151 {
152 hRgnNonClient = IntCalcWindowRgn(Window, FALSE);
153
154 /*
155 * If region creation fails it's safe to fallback to whole
156 * window region.
157 */
158 if (hRgnNonClient == NULL)
159 {
160 return (HRGN)1;
161 }
162
163 hRgnWindow = IntCalcWindowRgn(Window, TRUE);
164 if (hRgnWindow == NULL)
165 {
166 REGION_FreeRgnByHandle(hRgnNonClient);
167 return (HRGN)1;
168 }
169
170 RgnType = NtGdiCombineRgn(hRgnNonClient, hRgnNonClient,
171 hRgnWindow, RGN_DIFF);
172 if (RgnType == ERROR)
173 {
174 REGION_FreeRgnByHandle(hRgnWindow);
175 REGION_FreeRgnByHandle(hRgnNonClient);
176 return (HRGN)1;
177 }
178 else if (RgnType == NULLREGION)
179 {
180 REGION_FreeRgnByHandle(hRgnWindow);
181 REGION_FreeRgnByHandle(hRgnNonClient);
182 return NULL;
183 }
184
185 /*
186 * Remove the nonclient region from the standard update region if
187 * we were asked for it.
188 */
189
190 if (Validate)
191 {
192 if (NtGdiCombineRgn(Window->hrgnUpdate, Window->hrgnUpdate,
193 hRgnWindow, RGN_AND) == NULLREGION)
194 {
195 IntGdiSetRegionOwner(Window->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
196 REGION_FreeRgnByHandle(Window->hrgnUpdate);
197 Window->hrgnUpdate = NULL;
198 if (!(Window->Wnd->state & WNDS_INTERNALPAINT))
199 MsqDecPaintCountQueue(Window->pti->MessageQueue);
200 }
201 }
202
203 REGION_FreeRgnByHandle(hRgnWindow);
204
205 return hRgnNonClient;
206 }
207 else
208 {
209 return Window->hrgnUpdate;
210 }
211 }
212
213 /*
214 * IntPaintWindows
215 *
216 * Internal function used by IntRedrawWindow.
217 */
218
219 VOID FASTCALL
220 co_IntPaintWindows(PWINDOW_OBJECT Window, ULONG Flags, BOOL Recurse)
221 {
222 HDC hDC;
223 HWND hWnd = Window->hSelf;
224 HRGN TempRegion;
225 PWND Wnd;
226
227 Wnd = Window->Wnd;
228
229 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
230 {
231 if (Window->hrgnUpdate)
232 {
233 if (!IntValidateParent(Window, Window->hrgnUpdate, Recurse))
234 return;
235 }
236
237 if (Flags & RDW_UPDATENOW)
238 {
239 if (Window->hrgnUpdate != NULL ||
240 Wnd->state & WNDS_INTERNALPAINT)
241 {
242 co_IntSendMessage(hWnd, WM_PAINT, 0, 0);
243 }
244 }
245 else
246 {
247 if (Window->state & WINDOWOBJECT_NEED_NCPAINT)
248 {
249 TempRegion = IntGetNCUpdateRgn(Window, TRUE);
250 Window->state &= ~WINDOWOBJECT_NEED_NCPAINT;
251 MsqDecPaintCountQueue(Window->pti->MessageQueue);
252 co_IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)TempRegion, 0);
253
254 if ( (HANDLE) 1 != TempRegion &&
255 NULL != TempRegion)
256 {
257 /* NOTE: The region can already be deleted! */
258 GDIOBJ_FreeObjByHandle(TempRegion, GDI_OBJECT_TYPE_REGION | GDI_OBJECT_TYPE_SILENT);
259 }
260 }
261
262 if (Window->state & WINDOWOBJECT_NEED_ERASEBKGND)
263 {
264 if (Window->hrgnUpdate)
265 {
266 hDC = UserGetDCEx( Window,
267 Window->hrgnUpdate,
268 DCX_CACHE|DCX_USESTYLE|DCX_INTERSECTRGN|DCX_KEEPCLIPRGN);
269
270 if (co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)hDC, 0))
271 {
272 Window->state &= ~WINDOWOBJECT_NEED_ERASEBKGND;
273 }
274 UserReleaseDC(Window, hDC, FALSE);
275 }
276 }
277 }
278 }
279
280 /*
281 * Check that the window is still valid at this point
282 */
283 if (!IntIsWindow(hWnd))
284 {
285 return;
286 }
287
288 /*
289 * Paint child windows.
290 */
291 if (!(Flags & RDW_NOCHILDREN) &&
292 !(Wnd->style & WS_MINIMIZE) &&
293 ((Flags & RDW_ALLCHILDREN) || !(Wnd->style & WS_CLIPCHILDREN)) )
294 {
295 HWND *List, *phWnd;
296
297 if ((List = IntWinListChildren(Window)))
298 {
299 /* FIXME: Handle WS_EX_TRANSPARENT */
300 for (phWnd = List; *phWnd; ++phWnd)
301 {
302 Window = UserGetWindowObject(*phWnd);
303 Wnd = Window->Wnd;
304 if (Window && (Wnd->style & WS_VISIBLE))
305 {
306 USER_REFERENCE_ENTRY Ref;
307 UserRefObjectCo(Window, &Ref);
308 co_IntPaintWindows(Window, Flags, TRUE);
309 UserDerefObjectCo(Window);
310 }
311 }
312 ExFreePool(List);
313 }
314 }
315 }
316
317 /*
318 * IntInvalidateWindows
319 *
320 * Internal function used by IntRedrawWindow.
321 */
322
323 VOID FASTCALL
324 IntInvalidateWindows(PWINDOW_OBJECT Window, HRGN hRgn, ULONG Flags)
325 {
326 INT RgnType;
327 PWND Wnd;
328 BOOL HadPaintMessage, HadNCPaintMessage;
329 BOOL HasPaintMessage, HasNCPaintMessage;
330
331 Wnd = Window->Wnd;
332 DPRINT("IntInvalidateWindows start\n");
333 /*
334 * If the nonclient is not to be redrawn, clip the region to the client
335 * rect
336 */
337 if (0 != (Flags & RDW_INVALIDATE) && 0 == (Flags & RDW_FRAME))
338 {
339 HRGN hRgnClient;
340
341 hRgnClient = IntSysCreateRectRgnIndirect(&Window->Wnd->rcClient);
342 RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnClient, RGN_AND);
343 REGION_FreeRgnByHandle(hRgnClient);
344 }
345
346 /*
347 * Clip the given region with window rectangle (or region)
348 */
349
350 if (!Window->hrgnClip || (Wnd->style & WS_MINIMIZE))
351 {
352 HRGN hRgnWindow;
353
354 hRgnWindow = IntSysCreateRectRgnIndirect(&Window->Wnd->rcWindow);
355 RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnWindow, RGN_AND);
356 REGION_FreeRgnByHandle(hRgnWindow);
357 }
358 else
359 {
360 NtGdiOffsetRgn( hRgn,
361 -Wnd->rcWindow.left,
362 -Wnd->rcWindow.top);
363 RgnType = NtGdiCombineRgn(hRgn, hRgn, Window->hrgnClip, RGN_AND);
364 NtGdiOffsetRgn( hRgn,
365 Wnd->rcWindow.left,
366 Wnd->rcWindow.top);
367 }
368
369 /*
370 * Save current state of pending updates
371 */
372
373 HadPaintMessage = Window->hrgnUpdate != NULL ||
374 Wnd->state & WNDS_INTERNALPAINT;
375 HadNCPaintMessage = Window->state & WINDOWOBJECT_NEED_NCPAINT;
376
377 /*
378 * Update the region and flags
379 */
380
381 if (Flags & RDW_INVALIDATE && RgnType != NULLREGION)
382 {
383 if (Window->hrgnUpdate == NULL)
384 {
385 Window->hrgnUpdate = IntSysCreateRectRgn(0, 0, 0, 0);
386 IntGdiSetRegionOwner(Window->hrgnUpdate, GDI_OBJ_HMGR_PUBLIC);
387 }
388
389 if (NtGdiCombineRgn(Window->hrgnUpdate, Window->hrgnUpdate,
390 hRgn, RGN_OR) == NULLREGION)
391 {
392 IntGdiSetRegionOwner(Window->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
393 REGION_FreeRgnByHandle(Window->hrgnUpdate);
394 Window->hrgnUpdate = NULL;
395 }
396
397 if (Flags & RDW_FRAME)
398 Window->state |= WINDOWOBJECT_NEED_NCPAINT;
399 if (Flags & RDW_ERASE)
400 Window->state |= WINDOWOBJECT_NEED_ERASEBKGND;
401
402 Flags |= RDW_FRAME;
403 }
404
405 if (Flags & RDW_VALIDATE && RgnType != NULLREGION)
406 {
407 if (Window->hrgnUpdate != NULL)
408 {
409 if (NtGdiCombineRgn(Window->hrgnUpdate, Window->hrgnUpdate,
410 hRgn, RGN_DIFF) == NULLREGION)
411 {
412 IntGdiSetRegionOwner(Window->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
413 REGION_FreeRgnByHandle(Window->hrgnUpdate);
414 Window->hrgnUpdate = NULL;
415 }
416 }
417
418 if (Window->hrgnUpdate == NULL)
419 Window->state &= ~WINDOWOBJECT_NEED_ERASEBKGND;
420 if (Flags & RDW_NOFRAME)
421 Window->state &= ~WINDOWOBJECT_NEED_NCPAINT;
422 if (Flags & RDW_NOERASE)
423 Window->state &= ~WINDOWOBJECT_NEED_ERASEBKGND;
424 }
425
426 if (Flags & RDW_INTERNALPAINT)
427 {
428 Wnd->state |= WNDS_INTERNALPAINT;
429 }
430
431 if (Flags & RDW_NOINTERNALPAINT)
432 {
433 Wnd->state &= ~WNDS_INTERNALPAINT;
434 }
435
436 /*
437 * Process children if needed
438 */
439
440 if (!(Flags & RDW_NOCHILDREN) && !(Wnd->style & WS_MINIMIZE) &&
441 ((Flags & RDW_ALLCHILDREN) || !(Wnd->style & WS_CLIPCHILDREN)))
442 {
443 PWINDOW_OBJECT Child;
444
445 for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
446 {
447 if (Child->Wnd->style & WS_VISIBLE)
448 {
449 /*
450 * Recursive call to update children hrgnUpdate
451 */
452 HRGN hRgnTemp = IntSysCreateRectRgn(0, 0, 0, 0);
453 NtGdiCombineRgn(hRgnTemp, hRgn, 0, RGN_COPY);
454 IntInvalidateWindows(Child, hRgnTemp, Flags);
455 REGION_FreeRgnByHandle(hRgnTemp);
456 }
457
458 }
459 }
460
461 /*
462 * Fake post paint messages to window message queue if needed
463 */
464
465 HasPaintMessage = Window->hrgnUpdate != NULL ||
466 Wnd->state & WNDS_INTERNALPAINT;
467 HasNCPaintMessage = Window->state & WINDOWOBJECT_NEED_NCPAINT;
468
469 if (HasPaintMessage != HadPaintMessage)
470 {
471 if (HadPaintMessage)
472 MsqDecPaintCountQueue(Window->pti->MessageQueue);
473 else
474 MsqIncPaintCountQueue(Window->pti->MessageQueue);
475 }
476
477 if (HasNCPaintMessage != HadNCPaintMessage)
478 {
479 if (HadNCPaintMessage)
480 MsqDecPaintCountQueue(Window->pti->MessageQueue);
481 else
482 MsqIncPaintCountQueue(Window->pti->MessageQueue);
483 }
484 DPRINT("IntInvalidateWindows exit\n");
485 }
486
487 /*
488 * IntIsWindowDrawable
489 *
490 * Remarks
491 * Window is drawable when it is visible and all parents are not
492 * minimized.
493 */
494
495 BOOL FASTCALL
496 IntIsWindowDrawable(PWINDOW_OBJECT Window)
497 {
498 PWINDOW_OBJECT WndObject;
499 PWND Wnd;
500
501 for (WndObject = Window; WndObject != NULL; WndObject = WndObject->spwndParent)
502 {
503 Wnd = WndObject->Wnd;
504 if ( Window->state & WINDOWSTATUS_DESTROYING || // state2
505 Window->state & WINDOWSTATUS_DESTROYED ||
506 !Wnd ||
507 !(Wnd->style & WS_VISIBLE) ||
508 ((Wnd->style & WS_MINIMIZE) && (WndObject != Window)))
509 {
510 return FALSE;
511 }
512 }
513
514 return TRUE;
515 }
516
517 /*
518 * IntRedrawWindow
519 *
520 * Internal version of NtUserRedrawWindow that takes WINDOW_OBJECT as
521 * first parameter.
522 */
523
524 BOOL FASTCALL
525 co_UserRedrawWindow(
526 PWINDOW_OBJECT Window,
527 const RECTL* UpdateRect,
528 HRGN UpdateRgn,
529 ULONG Flags)
530 {
531 HRGN hRgn = NULL;
532 DPRINT("co_UserRedrawWindow start\n");
533
534 /*
535 * Step 1.
536 * Validation of passed parameters.
537 */
538
539 if (!IntIsWindowDrawable(Window))
540 {
541 return TRUE; // Just do nothing!!!
542 }
543
544 /*
545 * Step 2.
546 * Transform the parameters UpdateRgn and UpdateRect into
547 * a region hRgn specified in screen coordinates.
548 */
549
550 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE)) // Both are OKAY!
551 {
552 if (UpdateRgn != NULL)
553 {
554 hRgn = IntSysCreateRectRgn(0, 0, 0, 0);
555 if (NtGdiCombineRgn(hRgn, UpdateRgn, NULL, RGN_COPY) == NULLREGION)
556 {
557 REGION_FreeRgnByHandle(hRgn);
558 hRgn = NULL;
559 }
560 else
561 NtGdiOffsetRgn(hRgn, Window->Wnd->rcClient.left, Window->Wnd->rcClient.top);
562 }
563 else if (UpdateRect != NULL)
564 {
565 if (!RECTL_bIsEmptyRect(UpdateRect))
566 {
567 hRgn = IntSysCreateRectRgnIndirect((RECTL *)UpdateRect);
568 NtGdiOffsetRgn(hRgn, Window->Wnd->rcClient.left, Window->Wnd->rcClient.top);
569 }
570 }
571 else if ((Flags & (RDW_INVALIDATE | RDW_FRAME)) == (RDW_INVALIDATE | RDW_FRAME) ||
572 (Flags & (RDW_VALIDATE | RDW_NOFRAME)) == (RDW_VALIDATE | RDW_NOFRAME))
573 {
574 if (!RECTL_bIsEmptyRect(&Window->Wnd->rcWindow))
575 hRgn = IntSysCreateRectRgnIndirect(&Window->Wnd->rcWindow);
576 }
577 else
578 {
579 if (!RECTL_bIsEmptyRect(&Window->Wnd->rcClient))
580 hRgn = IntSysCreateRectRgnIndirect(&Window->Wnd->rcClient);
581 }
582 }
583
584 /*
585 * Step 3.
586 * Adjust the window update region depending on hRgn and flags.
587 */
588
589 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT) &&
590 hRgn != NULL)
591 {
592 IntInvalidateWindows(Window, hRgn, Flags);
593 }
594
595 /*
596 * Step 4.
597 * Repaint and erase windows if needed.
598 */
599
600 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
601 {
602 co_IntPaintWindows(Window, Flags, FALSE);
603 }
604
605 /*
606 * Step 5.
607 * Cleanup ;-)
608 */
609
610 if (hRgn != NULL)
611 {
612 REGION_FreeRgnByHandle(hRgn);
613 }
614 DPRINT("co_UserRedrawWindow exit\n");
615
616 return TRUE;
617 }
618
619 BOOL FASTCALL
620 IntIsWindowDirty(PWINDOW_OBJECT Window)
621 {
622 PWND Wnd = Window->Wnd;
623 return (Wnd->style & WS_VISIBLE) &&
624 ((Window->hrgnUpdate != NULL) ||
625 (Wnd->state & WNDS_INTERNALPAINT) ||
626 (Window->state & WINDOWOBJECT_NEED_NCPAINT));
627 }
628
629 HWND FASTCALL
630 IntFindWindowToRepaint(PWINDOW_OBJECT Window, PTHREADINFO Thread)
631 {
632 HWND hChild;
633 PWINDOW_OBJECT TempWindow;
634 PWND Wnd, TempWnd;
635
636 for (; Window != NULL; Window = Window->spwndNext)
637 {
638 Wnd = Window->Wnd;
639 if (IntWndBelongsToThread(Window, Thread) &&
640 IntIsWindowDirty(Window))
641 {
642 /* Make sure all non-transparent siblings are already drawn. */
643 if (Wnd->ExStyle & WS_EX_TRANSPARENT)
644 {
645 for (TempWindow = Window->spwndNext; TempWindow != NULL;
646 TempWindow = TempWindow->spwndNext)
647 {
648 TempWnd = TempWindow->Wnd;
649 if (!(TempWnd->ExStyle & WS_EX_TRANSPARENT) &&
650 IntWndBelongsToThread(TempWindow, Thread) &&
651 IntIsWindowDirty(TempWindow))
652 {
653 return TempWindow->hSelf;
654 }
655 }
656 }
657
658 return Window->hSelf;
659 }
660
661 if (Window->spwndChild)
662 {
663 hChild = IntFindWindowToRepaint(Window->spwndChild, Thread);
664 if (hChild != NULL)
665 return hChild;
666 }
667 }
668
669 return NULL;
670 }
671
672 BOOL FASTCALL
673 IntGetPaintMessage(
674 PWINDOW_OBJECT Window,
675 UINT MsgFilterMin,
676 UINT MsgFilterMax,
677 PTHREADINFO Thread,
678 MSG *Message,
679 BOOL Remove)
680 {
681 PUSER_MESSAGE_QUEUE MessageQueue = (PUSER_MESSAGE_QUEUE)Thread->MessageQueue;
682
683 if (!MessageQueue->PaintCount)
684 return FALSE;
685
686 if ((MsgFilterMin != 0 || MsgFilterMax != 0) &&
687 (MsgFilterMin > WM_PAINT || MsgFilterMax < WM_PAINT))
688 return FALSE;
689
690 Message->hwnd = IntFindWindowToRepaint(UserGetDesktopWindow(), PsGetCurrentThreadWin32Thread());
691
692 if (Message->hwnd == NULL)
693 {
694 DPRINT1("PAINTING BUG: Thread marked as containing dirty windows, but no dirty windows found!\n");
695 /* Hack to stop spamming the debuglog ! */
696 MessageQueue->PaintCount = 0;
697 return FALSE;
698 }
699
700 if (Window != NULL && Message->hwnd != Window->hSelf)
701 return FALSE;
702
703 Message->message = WM_PAINT;
704 Message->wParam = Message->lParam = 0;
705
706 return TRUE;
707 }
708
709 static
710 HWND FASTCALL
711 co_IntFixCaret(PWINDOW_OBJECT Window, RECTL *lprc, UINT flags)
712 {
713 PDESKTOP Desktop;
714 PTHRDCARETINFO CaretInfo;
715 HWND hWndCaret;
716 PWINDOW_OBJECT WndCaret;
717
718 ASSERT_REFS_CO(Window);
719
720 Desktop = ((PTHREADINFO)PsGetCurrentThread()->Tcb.Win32Thread)->rpdesk;
721 CaretInfo = ((PUSER_MESSAGE_QUEUE)Desktop->ActiveMessageQueue)->CaretInfo;
722 hWndCaret = CaretInfo->hWnd;
723
724 WndCaret = UserGetWindowObject(hWndCaret);
725
726 //fix: check for WndCaret can be null
727 if (WndCaret == Window ||
728 ((flags & SW_SCROLLCHILDREN) && IntIsChildWindow(Window, WndCaret)))
729 {
730 POINT pt, FromOffset, ToOffset;
731 RECTL rcCaret;
732
733 pt.x = CaretInfo->Pos.x;
734 pt.y = CaretInfo->Pos.y;
735 IntGetClientOrigin(WndCaret, &FromOffset);
736 IntGetClientOrigin(Window, &ToOffset);
737 rcCaret.left = pt.x;
738 rcCaret.top = pt.y;
739 rcCaret.right = pt.x + CaretInfo->Size.cx;
740 rcCaret.bottom = pt.y + CaretInfo->Size.cy;
741 if (RECTL_bIntersectRect(lprc, lprc, &rcCaret))
742 {
743 co_UserHideCaret(0);
744 lprc->left = pt.x;
745 lprc->top = pt.y;
746 return hWndCaret;
747 }
748 }
749
750 return 0;
751 }
752
753 BOOL
754 FASTCALL
755 IntPrintWindow(
756 PWINDOW_OBJECT Window,
757 HDC hdcBlt,
758 UINT nFlags)
759 {
760 PWND pwnd;
761 HDC hdcSrc;
762 INT cx, cy, xSrc, ySrc;
763
764 pwnd = Window->Wnd;
765
766 if (!pwnd)
767 return FALSE;
768
769 if ( nFlags & PW_CLIENTONLY)
770 {
771 cx = pwnd->rcClient.right - pwnd->rcClient.left;
772 cy = pwnd->rcClient.bottom - pwnd->rcClient.top;
773 xSrc = pwnd->rcClient.left - pwnd->rcWindow.left;
774 ySrc = pwnd->rcClient.top - pwnd->rcWindow.top;
775 }
776 else
777 {
778 cx = pwnd->rcWindow.right - pwnd->rcWindow.left;
779 cy = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
780 xSrc = 0;
781 ySrc = 0;
782 }
783
784 // TODO: Setup Redirection for Print.
785 return FALSE;
786
787 /* Update the window just incase. */
788 co_IntPaintWindows( Window, RDW_ERASENOW|RDW_UPDATENOW, FALSE);
789
790 hdcSrc = UserGetDCEx( Window, NULL, DCX_CACHE|DCX_WINDOW);
791 /* Print window to printer context. */
792 NtGdiBitBlt( hdcBlt,
793 0,
794 0,
795 cx,
796 cy,
797 hdcSrc,
798 xSrc,
799 ySrc,
800 SRCCOPY,
801 0,
802 0);
803
804 UserReleaseDC( Window, hdcSrc, FALSE);
805
806 // TODO: Release Redirection from Print.
807
808 return TRUE;
809 }
810
811 /* PUBLIC FUNCTIONS ***********************************************************/
812
813 /*
814 * NtUserBeginPaint
815 *
816 * Status
817 * @implemented
818 */
819
820 HDC APIENTRY
821 NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* UnsafePs)
822 {
823 PWINDOW_OBJECT Window = NULL;
824 PAINTSTRUCT Ps;
825 NTSTATUS Status;
826 DECLARE_RETURN(HDC);
827 USER_REFERENCE_ENTRY Ref;
828 PWND Wnd;
829
830 DPRINT("Enter NtUserBeginPaint\n");
831 UserEnterExclusive();
832
833 if (!(Window = UserGetWindowObject(hWnd)))
834 {
835 RETURN( NULL);
836 }
837
838 UserRefObjectCo(Window, &Ref);
839
840 Wnd = Window->Wnd;
841
842 co_UserHideCaret(Window);
843
844 if (Window->state & WINDOWOBJECT_NEED_NCPAINT)
845 {
846 HRGN hRgn;
847
848 hRgn = IntGetNCUpdateRgn(Window, FALSE);
849 Window->state &= ~WINDOWOBJECT_NEED_NCPAINT;
850 MsqDecPaintCountQueue(Window->pti->MessageQueue);
851 co_IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)hRgn, 0);
852 if (hRgn != (HANDLE)1 && hRgn != NULL)
853 {
854 /* NOTE: The region can already by deleted! */
855 GDIOBJ_FreeObjByHandle(hRgn, GDI_OBJECT_TYPE_REGION | GDI_OBJECT_TYPE_SILENT);
856 }
857 }
858
859 RtlZeroMemory(&Ps, sizeof(PAINTSTRUCT));
860
861 Ps.hdc = UserGetDCEx( Window,
862 Window->hrgnUpdate,
863 DCX_INTERSECTRGN | DCX_USESTYLE);
864 if (!Ps.hdc)
865 {
866 RETURN(NULL);
867 }
868
869 if (Window->hrgnUpdate != NULL)
870 {
871 MsqDecPaintCountQueue(Window->pti->MessageQueue);
872 GdiGetClipBox(Ps.hdc, &Ps.rcPaint);
873 IntGdiSetRegionOwner(Window->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
874 /* The region is part of the dc now and belongs to the process! */
875 Window->hrgnUpdate = NULL;
876 }
877 else
878 {
879 if (Wnd->state & WNDS_INTERNALPAINT)
880 MsqDecPaintCountQueue(Window->pti->MessageQueue);
881
882 IntGetClientRect(Window, &Ps.rcPaint);
883 }
884
885 Wnd->state &= ~WNDS_INTERNALPAINT;
886
887 if (Window->state & WINDOWOBJECT_NEED_ERASEBKGND)
888 {
889 Window->state &= ~WINDOWOBJECT_NEED_ERASEBKGND;
890 Ps.fErase = !co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)Ps.hdc, 0);
891 }
892 else
893 {
894 Ps.fErase = FALSE;
895 }
896 if (Window->hrgnUpdate)
897 {
898 if (!(Wnd->style & WS_CLIPCHILDREN))
899 {
900 PWINDOW_OBJECT Child;
901 for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
902 {
903 IntInvalidateWindows(Child, Window->hrgnUpdate, RDW_FRAME | RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN);
904 }
905 }
906 }
907
908 Status = MmCopyToCaller(UnsafePs, &Ps, sizeof(PAINTSTRUCT));
909 if (! NT_SUCCESS(Status))
910 {
911 SetLastNtError(Status);
912 RETURN(NULL);
913 }
914
915 RETURN(Ps.hdc);
916
917 CLEANUP:
918 if (Window) UserDerefObjectCo(Window);
919
920 DPRINT("Leave NtUserBeginPaint, ret=%i\n",_ret_);
921 UserLeave();
922 END_CLEANUP;
923
924 }
925
926 /*
927 * NtUserEndPaint
928 *
929 * Status
930 * @implemented
931 */
932
933 BOOL APIENTRY
934 NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* pUnsafePs)
935 {
936 NTSTATUS Status = STATUS_SUCCESS;
937 PWINDOW_OBJECT Window;
938 DECLARE_RETURN(BOOL);
939 USER_REFERENCE_ENTRY Ref;
940 HDC hdc = NULL;
941
942 DPRINT("Enter NtUserEndPaint\n");
943 UserEnterExclusive();
944
945 if (!(Window = UserGetWindowObject(hWnd)))
946 {
947 RETURN(FALSE);
948 }
949
950 _SEH2_TRY
951 {
952 ProbeForRead(pUnsafePs, sizeof(*pUnsafePs), 1);
953 hdc = pUnsafePs->hdc;
954 }
955 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
956 {
957 Status = _SEH2_GetExceptionCode();
958 }
959 _SEH2_END
960 if (!NT_SUCCESS(Status))
961 {
962 RETURN(FALSE);
963 }
964
965 UserReleaseDC(Window, hdc, TRUE);
966
967 UserRefObjectCo(Window, &Ref);
968 co_UserShowCaret(Window);
969 UserDerefObjectCo(Window);
970
971 RETURN(TRUE);
972
973 CLEANUP:
974 DPRINT("Leave NtUserEndPaint, ret=%i\n",_ret_);
975 UserLeave();
976 END_CLEANUP;
977 }
978
979
980 INT FASTCALL
981 co_UserGetUpdateRgn(PWINDOW_OBJECT Window, HRGN hRgn, BOOL bErase)
982 {
983 int RegionType;
984 RECTL Rect;
985
986 ASSERT_REFS_CO(Window);
987
988 if (Window->hrgnUpdate == NULL)
989 {
990 RegionType = (NtGdiSetRectRgn(hRgn, 0, 0, 0, 0) ? NULLREGION : ERROR);
991 }
992 else
993 {
994 Rect = Window->Wnd->rcClient;
995 IntIntersectWithParents(Window, &Rect);
996 NtGdiSetRectRgn(hRgn, Rect.left, Rect.top, Rect.right, Rect.bottom);
997 RegionType = NtGdiCombineRgn(hRgn, hRgn, Window->hrgnUpdate, RGN_AND);
998 NtGdiOffsetRgn(hRgn, -Window->Wnd->rcClient.left, -Window->Wnd->rcClient.top);
999 }
1000
1001 if (bErase && RegionType != NULLREGION && RegionType != ERROR)
1002 {
1003 co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
1004 }
1005
1006 return RegionType;
1007 }
1008
1009 /*
1010 * NtUserGetUpdateRgn
1011 *
1012 * Status
1013 * @implemented
1014 */
1015
1016 INT APIENTRY
1017 NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
1018 {
1019 DECLARE_RETURN(INT);
1020 PWINDOW_OBJECT Window;
1021 INT ret;
1022 USER_REFERENCE_ENTRY Ref;
1023
1024 DPRINT("Enter NtUserGetUpdateRgn\n");
1025 UserEnterExclusive();
1026
1027 if (!(Window = UserGetWindowObject(hWnd)))
1028 {
1029 RETURN(ERROR);
1030 }
1031
1032 UserRefObjectCo(Window, &Ref);
1033 ret = co_UserGetUpdateRgn(Window, hRgn, bErase);
1034 UserDerefObjectCo(Window);
1035
1036 RETURN(ret);
1037
1038 CLEANUP:
1039 DPRINT("Leave NtUserGetUpdateRgn, ret=%i\n",_ret_);
1040 UserLeave();
1041 END_CLEANUP;
1042 }
1043
1044 /*
1045 * NtUserGetUpdateRect
1046 *
1047 * Status
1048 * @implemented
1049 */
1050
1051 BOOL APIENTRY
1052 NtUserGetUpdateRect(HWND hWnd, LPRECT UnsafeRect, BOOL bErase)
1053 {
1054 PWINDOW_OBJECT Window;
1055 RECTL Rect;
1056 INT RegionType;
1057 PROSRGNDATA RgnData;
1058 NTSTATUS Status;
1059 DECLARE_RETURN(BOOL);
1060
1061 DPRINT("Enter NtUserGetUpdateRect\n");
1062 UserEnterExclusive();
1063
1064 if (!(Window = UserGetWindowObject(hWnd)))
1065 {
1066 RETURN(FALSE);
1067 }
1068
1069 if (Window->hrgnUpdate == NULL)
1070 {
1071 Rect.left = Rect.top = Rect.right = Rect.bottom = 0;
1072 }
1073 else
1074 {
1075 /* Get the update region bounding box. */
1076 if (Window->hrgnUpdate == (HRGN)1)
1077 {
1078 Rect = Window->Wnd->rcClient;
1079 }
1080 else
1081 {
1082 RgnData = RGNOBJAPI_Lock(Window->hrgnUpdate, NULL);
1083 ASSERT(RgnData != NULL);
1084 RegionType = REGION_GetRgnBox(RgnData, &Rect);
1085 RGNOBJAPI_Unlock(RgnData);
1086
1087 if (RegionType != ERROR && RegionType != NULLREGION)
1088 RECTL_bIntersectRect(&Rect, &Rect, &Window->Wnd->rcClient);
1089 }
1090
1091 if (IntIntersectWithParents(Window, &Rect))
1092 {
1093 RECTL_vOffsetRect(&Rect,
1094 -Window->Wnd->rcClient.left,
1095 -Window->Wnd->rcClient.top);
1096 } else
1097 {
1098 Rect.left = Rect.top = Rect.right = Rect.bottom = 0;
1099 }
1100 }
1101
1102 if (bErase && !RECTL_bIsEmptyRect(&Rect))
1103 {
1104 USER_REFERENCE_ENTRY Ref;
1105 UserRefObjectCo(Window, &Ref);
1106 co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
1107 UserDerefObjectCo(Window);
1108 }
1109
1110 if (UnsafeRect != NULL)
1111 {
1112 Status = MmCopyToCaller(UnsafeRect, &Rect, sizeof(RECTL));
1113 if (!NT_SUCCESS(Status))
1114 {
1115 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1116 RETURN(FALSE);
1117 }
1118 }
1119
1120 RETURN(!RECTL_bIsEmptyRect(&Rect));
1121
1122 CLEANUP:
1123 DPRINT("Leave NtUserGetUpdateRect, ret=%i\n",_ret_);
1124 UserLeave();
1125 END_CLEANUP;
1126 }
1127
1128 /*
1129 * NtUserRedrawWindow
1130 *
1131 * Status
1132 * @implemented
1133 */
1134
1135 BOOL APIENTRY
1136 NtUserRedrawWindow(
1137 HWND hWnd,
1138 CONST RECT *lprcUpdate,
1139 HRGN hrgnUpdate,
1140 UINT flags)
1141 {
1142 RECTL SafeUpdateRect;
1143 PWINDOW_OBJECT Wnd;
1144 BOOL Ret;
1145 USER_REFERENCE_ENTRY Ref;
1146 NTSTATUS Status = STATUS_SUCCESS;
1147 DECLARE_RETURN(BOOL);
1148
1149 DPRINT("Enter NtUserRedrawWindow\n");
1150 UserEnterExclusive();
1151
1152 if (!(Wnd = UserGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow())))
1153 {
1154 RETURN( FALSE);
1155 }
1156
1157 if (lprcUpdate)
1158 {
1159 _SEH2_TRY
1160 {
1161 ProbeForRead(lprcUpdate, sizeof(RECTL), 1);
1162 RtlCopyMemory(&SafeUpdateRect, lprcUpdate, sizeof(RECTL));
1163 }
1164 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1165 {
1166 Status = _SEH2_GetExceptionCode();
1167 }
1168 _SEH2_END
1169 if (!NT_SUCCESS(Status))
1170 {
1171 SetLastWin32Error(RtlNtStatusToDosError(Status));
1172 RETURN( FALSE);
1173 }
1174 }
1175
1176 if ( flags & ~(RDW_ERASE|RDW_FRAME|RDW_INTERNALPAINT|RDW_INVALIDATE|
1177 RDW_NOERASE|RDW_NOFRAME|RDW_NOINTERNALPAINT|RDW_VALIDATE|
1178 RDW_ERASENOW|RDW_UPDATENOW|RDW_ALLCHILDREN|RDW_NOCHILDREN) )
1179 {
1180 /* RedrawWindow fails only in case that flags are invalid */
1181 SetLastWin32Error(ERROR_INVALID_FLAGS);
1182 RETURN( FALSE);
1183 }
1184
1185 UserRefObjectCo(Wnd, &Ref);
1186
1187 Ret = co_UserRedrawWindow( Wnd,
1188 lprcUpdate ? &SafeUpdateRect : NULL,
1189 hrgnUpdate,
1190 flags);
1191
1192 UserDerefObjectCo(Wnd);
1193
1194 RETURN( Ret);
1195
1196 CLEANUP:
1197 DPRINT("Leave NtUserRedrawWindow, ret=%i\n",_ret_);
1198 UserLeave();
1199 END_CLEANUP;
1200 }
1201
1202 static
1203 INT FASTCALL
1204 UserScrollDC(
1205 HDC hDC,
1206 INT dx,
1207 INT dy,
1208 const RECTL *prcScroll,
1209 const RECTL *prcClip,
1210 HRGN hrgnUpdate,
1211 RECTL *prcUpdate)
1212 {
1213 PDC pDC;
1214 RECTL rcScroll, rcClip, rcSrc, rcDst;
1215 INT Result;
1216
1217 GdiGetClipBox(hDC, &rcClip);
1218 rcScroll = rcClip;
1219 if (prcClip)
1220 {
1221 RECTL_bIntersectRect(&rcClip, &rcClip, prcClip);
1222 }
1223
1224 if (prcScroll)
1225 {
1226 rcScroll = *prcScroll;
1227 RECTL_bIntersectRect(&rcSrc, &rcClip, prcScroll);
1228 }
1229 else
1230 {
1231 rcSrc = rcClip;
1232 }
1233
1234 rcDst = rcSrc;
1235 RECTL_vOffsetRect(&rcDst, dx, dy);
1236 RECTL_bIntersectRect(&rcDst, &rcDst, &rcClip);
1237
1238 if (!NtGdiBitBlt( hDC,
1239 rcDst.left,
1240 rcDst.top,
1241 rcDst.right - rcDst.left,
1242 rcDst.bottom - rcDst.top,
1243 hDC,
1244 rcDst.left - dx,
1245 rcDst.top - dy,
1246 SRCCOPY,
1247 0,
1248 0))
1249 {
1250 return ERROR;
1251 }
1252
1253 /* Calculate the region that was invalidated by moving or
1254 could not be copied, because it was not visible */
1255 if (hrgnUpdate || prcUpdate)
1256 {
1257 HRGN hrgnOwn, hrgnVisible, hrgnTmp;
1258
1259 pDC = DC_LockDc(hDC);
1260 if (!pDC)
1261 {
1262 return FALSE;
1263 }
1264 hrgnVisible = ((PROSRGNDATA)pDC->prgnVis)->BaseObject.hHmgr; // pDC->prgnRao?
1265
1266 /* Begin with the shifted and then clipped scroll rect */
1267 rcDst = rcScroll;
1268 RECTL_vOffsetRect(&rcDst, dx, dy);
1269 RECTL_bIntersectRect(&rcDst, &rcDst, &rcClip);
1270 if (hrgnUpdate)
1271 {
1272 hrgnOwn = hrgnUpdate;
1273 if (!NtGdiSetRectRgn(hrgnOwn, rcDst.left, rcDst.top, rcDst.right, rcDst.bottom))
1274 {
1275 return ERROR;
1276 }
1277 }
1278 else
1279 {
1280 hrgnOwn = IntSysCreateRectRgnIndirect(&rcDst);
1281 }
1282
1283 /* Add the source rect */
1284 hrgnTmp = IntSysCreateRectRgnIndirect(&rcSrc);
1285 NtGdiCombineRgn(hrgnOwn, hrgnOwn, hrgnTmp, RGN_OR);
1286
1287 /* Substract the part of the dest that was visible in source */
1288 NtGdiCombineRgn(hrgnTmp, hrgnTmp, hrgnVisible, RGN_AND);
1289 NtGdiOffsetRgn(hrgnTmp, dx, dy);
1290 Result = NtGdiCombineRgn(hrgnOwn, hrgnOwn, hrgnTmp, RGN_DIFF);
1291
1292 /* DO NOT Unlock DC while messing with prgnVis! */
1293 DC_UnlockDc(pDC);
1294
1295 REGION_FreeRgnByHandle(hrgnTmp);
1296
1297 if (prcUpdate)
1298 {
1299 IntGdiGetRgnBox(hrgnOwn, prcUpdate);
1300 }
1301
1302 if (!hrgnUpdate)
1303 {
1304 REGION_FreeRgnByHandle(hrgnOwn);
1305 }
1306 }
1307 else
1308 Result = NULLREGION;
1309
1310 return Result;
1311 }
1312
1313 /*
1314 * NtUserScrollDC
1315 *
1316 * Status
1317 * @implemented
1318 */
1319 BOOL APIENTRY
1320 NtUserScrollDC(
1321 HDC hDC,
1322 INT dx,
1323 INT dy,
1324 const RECT *prcUnsafeScroll,
1325 const RECT *prcUnsafeClip,
1326 HRGN hrgnUpdate,
1327 LPRECT prcUnsafeUpdate)
1328 {
1329 DECLARE_RETURN(DWORD);
1330 RECTL rcScroll, rcClip, rcUpdate;
1331 NTSTATUS Status = STATUS_SUCCESS;
1332 DWORD Result;
1333
1334 DPRINT("Enter NtUserScrollDC\n");
1335 UserEnterExclusive();
1336
1337 _SEH2_TRY
1338 {
1339 if (prcUnsafeScroll)
1340 {
1341 ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
1342 rcScroll = *prcUnsafeScroll;
1343 }
1344 if (prcUnsafeClip)
1345 {
1346 ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
1347 rcClip = *prcUnsafeClip;
1348 }
1349 if (prcUnsafeUpdate)
1350 {
1351 ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
1352 }
1353 }
1354 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1355 {
1356 Status = _SEH2_GetExceptionCode();
1357 }
1358 _SEH2_END
1359 if (!NT_SUCCESS(Status))
1360 {
1361 SetLastNtError(Status);
1362 RETURN(FALSE);
1363 }
1364
1365 Result = UserScrollDC( hDC,
1366 dx,
1367 dy,
1368 prcUnsafeScroll? &rcScroll : 0,
1369 prcUnsafeClip? &rcClip : 0,
1370 hrgnUpdate,
1371 prcUnsafeUpdate? &rcUpdate : NULL);
1372 if(Result == ERROR)
1373 {
1374 /* FIXME: Only if hRgnUpdate is invalid we should SetLastError(ERROR_INVALID_HANDLE) */
1375 RETURN(FALSE);
1376 }
1377
1378 if (prcUnsafeUpdate)
1379 {
1380 _SEH2_TRY
1381 {
1382 *prcUnsafeUpdate = rcUpdate;
1383 }
1384 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1385 {
1386 Status = _SEH2_GetExceptionCode();
1387 }
1388 _SEH2_END
1389 if (!NT_SUCCESS(Status))
1390 {
1391 /* FIXME: SetLastError? */
1392 /* FIXME: correct? We have already scrolled! */
1393 RETURN(FALSE);
1394 }
1395 }
1396
1397 RETURN(TRUE);
1398
1399 CLEANUP:
1400 DPRINT("Leave NtUserScrollDC, ret=%i\n",_ret_);
1401 UserLeave();
1402 END_CLEANUP;
1403 }
1404
1405 /*
1406 * NtUserScrollWindowEx
1407 *
1408 * Status
1409 * @implemented
1410 */
1411
1412 DWORD APIENTRY
1413 NtUserScrollWindowEx(
1414 HWND hWnd,
1415 INT dx,
1416 INT dy,
1417 const RECT *prcUnsafeScroll,
1418 const RECT *prcUnsafeClip,
1419 HRGN hrgnUpdate,
1420 LPRECT prcUnsafeUpdate,
1421 UINT flags)
1422 {
1423 RECTL rcScroll, rcClip, rcCaret, rcUpdate;
1424 INT Result;
1425 PWINDOW_OBJECT Window = NULL, CaretWnd;
1426 HDC hDC;
1427 HRGN hrgnOwn = NULL, hrgnTemp;
1428 HWND hwndCaret;
1429 NTSTATUS Status = STATUS_SUCCESS;
1430 DECLARE_RETURN(DWORD);
1431 USER_REFERENCE_ENTRY Ref, CaretRef;
1432
1433 DPRINT("Enter NtUserScrollWindowEx\n");
1434 UserEnterExclusive();
1435
1436 Window = UserGetWindowObject(hWnd);
1437 if (!Window || !IntIsWindowDrawable(Window))
1438 {
1439 Window = NULL; /* prevent deref at cleanup */
1440 RETURN( ERROR);
1441 }
1442 UserRefObjectCo(Window, &Ref);
1443
1444 IntGetClientRect(Window, &rcClip);
1445
1446 _SEH2_TRY
1447 {
1448 if (prcUnsafeScroll)
1449 {
1450 ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
1451 RECTL_bIntersectRect(&rcScroll, &rcClip, prcUnsafeScroll);
1452 }
1453 else
1454 rcScroll = rcClip;
1455
1456 if (prcUnsafeClip)
1457 {
1458 ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
1459 RECTL_bIntersectRect(&rcClip, &rcClip, prcUnsafeClip);
1460 }
1461 }
1462 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1463 {
1464 Status = _SEH2_GetExceptionCode();
1465 }
1466 _SEH2_END
1467
1468 if (!NT_SUCCESS(Status))
1469 {
1470 SetLastNtError(Status);
1471 RETURN(ERROR);
1472 }
1473
1474 if (rcClip.right <= rcClip.left || rcClip.bottom <= rcClip.top ||
1475 (dx == 0 && dy == 0))
1476 {
1477 RETURN(NULLREGION);
1478 }
1479
1480 if (hrgnUpdate)
1481 hrgnOwn = hrgnUpdate;
1482 else
1483 hrgnOwn = IntSysCreateRectRgn(0, 0, 0, 0);
1484
1485 hDC = UserGetDCEx(Window, 0, DCX_CACHE | DCX_USESTYLE);
1486 if (!hDC)
1487 {
1488 /* FIXME: SetLastError? */
1489 RETURN(ERROR);
1490 }
1491
1492 rcCaret = rcScroll;
1493 hwndCaret = co_IntFixCaret(Window, &rcCaret, flags);
1494
1495 Result = UserScrollDC( hDC,
1496 dx,
1497 dy,
1498 &rcScroll,
1499 &rcClip,
1500 hrgnOwn,
1501 prcUnsafeUpdate? &rcUpdate : NULL);
1502
1503 UserReleaseDC(Window, hDC, FALSE);
1504
1505 /*
1506 * Take into account the fact that some damage may have occurred during
1507 * the scroll.
1508 */
1509
1510 hrgnTemp = IntSysCreateRectRgn(0, 0, 0, 0);
1511 if (co_UserGetUpdateRgn(Window, hrgnTemp, FALSE) != NULLREGION)
1512 {
1513 HRGN hrgnClip = IntSysCreateRectRgnIndirect(&rcClip);
1514 NtGdiOffsetRgn(hrgnTemp, dx, dy);
1515 NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnClip, RGN_AND);
1516 co_UserRedrawWindow(Window, NULL, hrgnTemp, RDW_INVALIDATE | RDW_ERASE);
1517 REGION_FreeRgnByHandle(hrgnClip);
1518 }
1519 REGION_FreeRgnByHandle(hrgnTemp);
1520
1521 if (flags & SW_SCROLLCHILDREN)
1522 {
1523 PWINDOW_OBJECT Child;
1524 RECTL rcChild;
1525 POINT ClientOrigin;
1526 USER_REFERENCE_ENTRY WndRef;
1527 RECTL rcDummy;
1528
1529 IntGetClientOrigin(Window, &ClientOrigin);
1530 for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
1531 {
1532 rcChild = Child->Wnd->rcWindow;
1533 rcChild.left -= ClientOrigin.x;
1534 rcChild.top -= ClientOrigin.y;
1535 rcChild.right -= ClientOrigin.x;
1536 rcChild.bottom -= ClientOrigin.y;
1537
1538 if (! prcUnsafeScroll || RECTL_bIntersectRect(&rcDummy, &rcChild, &rcScroll))
1539 {
1540 UserRefObjectCo(Child, &WndRef);
1541 co_WinPosSetWindowPos(Child, 0, rcChild.left + dx, rcChild.top + dy, 0, 0,
1542 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
1543 SWP_NOREDRAW);
1544 UserDerefObjectCo(Child);
1545 }
1546 }
1547 }
1548
1549 if (flags & (SW_INVALIDATE | SW_ERASE))
1550 {
1551 co_UserRedrawWindow(Window, NULL, hrgnOwn, RDW_INVALIDATE | RDW_ERASE |
1552 ((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
1553 ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0));
1554 }
1555
1556 if ((CaretWnd = UserGetWindowObject(hwndCaret)))
1557 {
1558 UserRefObjectCo(CaretWnd, &CaretRef);
1559
1560 co_IntSetCaretPos(rcCaret.left + dx, rcCaret.top + dy);
1561 co_UserShowCaret(CaretWnd);
1562
1563 UserDerefObjectCo(CaretWnd);
1564 }
1565
1566 if (prcUnsafeUpdate)
1567 {
1568 _SEH2_TRY
1569 {
1570 /* Probe here, to not fail on invalid pointer before scrolling */
1571 ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
1572 *prcUnsafeUpdate = rcUpdate;
1573 }
1574 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1575 {
1576 Status = _SEH2_GetExceptionCode();
1577 }
1578 _SEH2_END
1579
1580 if (!NT_SUCCESS(Status))
1581 {
1582 SetLastNtError(Status);
1583 RETURN(ERROR);
1584 }
1585 }
1586
1587 RETURN(Result);
1588
1589 CLEANUP:
1590 if (hrgnOwn && !hrgnUpdate)
1591 {
1592 REGION_FreeRgnByHandle(hrgnOwn);
1593 }
1594
1595 if (Window)
1596 UserDerefObjectCo(Window);
1597
1598 DPRINT("Leave NtUserScrollWindowEx, ret=%i\n",_ret_);
1599 UserLeave();
1600 END_CLEANUP;
1601 }
1602
1603
1604 BOOL
1605 UserDrawSysMenuButton(
1606 PWINDOW_OBJECT pWnd,
1607 HDC hDc,
1608 RECTL *lpRc,
1609 BOOL Down)
1610 {
1611 HICON hIcon;
1612 PCURICON_OBJECT pIcon;
1613
1614 ASSERT(pWnd && lpRc);
1615
1616 /* Get the icon to draw. We don't care about WM_GETICON here. */
1617
1618 hIcon = pWnd->Wnd->pcls->hIconSm;
1619
1620 if(!hIcon)
1621 {
1622 DPRINT("Wnd class has no small icon.\n");
1623 hIcon = pWnd->Wnd->pcls->hIcon;
1624 }
1625
1626 if(!hIcon)
1627 {
1628 DPRINT("Wnd class hasn't any icon.\n");
1629 //FIXME: Draw "winlogo" icon.
1630 return FALSE;
1631 }
1632
1633 if(!(pIcon = UserGetCurIconObject(hIcon)))
1634 {
1635 DPRINT1("UserGetCurIconObject() failed!\n");
1636 return FALSE;
1637 }
1638
1639 return UserDrawIconEx(hDc, lpRc->left, lpRc->top, pIcon,
1640 UserGetSystemMetrics(SM_CXSMICON),
1641 UserGetSystemMetrics(SM_CYSMICON),
1642 0, NULL, DI_NORMAL);
1643 }
1644
1645 BOOL
1646 UserDrawCaptionText(
1647 HDC hDc,
1648 const PUNICODE_STRING Text,
1649 const RECTL *lpRc,
1650 UINT uFlags)
1651 {
1652 HFONT hOldFont = NULL, hFont = NULL;
1653 COLORREF OldTextColor;
1654 NONCLIENTMETRICSW nclm;
1655 NTSTATUS Status;
1656 #ifndef NDEBUG
1657 INT i;
1658 DPRINT("%s:", __FUNCTION__);
1659 for(i = 0; i < Text->Length/sizeof(WCHAR); i++)
1660 DbgPrint("%C", Text->Buffer[i]);
1661 DbgPrint(", %d\n", Text->Length/sizeof(WCHAR));
1662 #endif
1663
1664 nclm.cbSize = sizeof(nclm);
1665 if(!UserSystemParametersInfo(SPI_GETNONCLIENTMETRICS,
1666 sizeof(NONCLIENTMETRICS), &nclm, 0))
1667 {
1668 DPRINT1("%s: UserSystemParametersInfo() failed!\n", __FUNCTION__);
1669 return FALSE;
1670 }
1671
1672 IntGdiSetBkMode(hDc, TRANSPARENT);
1673
1674 if(uFlags & DC_SMALLCAP)
1675 Status = TextIntCreateFontIndirect(&nclm.lfSmCaptionFont, &hFont);
1676 else Status = TextIntCreateFontIndirect(&nclm.lfCaptionFont, &hFont);
1677
1678 if(!NT_SUCCESS(Status))
1679 {
1680 DPRINT1("%s: TextIntCreateFontIndirect() failed! Status: 0x%x\n",
1681 __FUNCTION__, Status);
1682 return FALSE;
1683 }
1684
1685 hOldFont = NtGdiSelectFont(hDc, hFont);
1686 if(!hOldFont)
1687 {
1688 DPRINT1("%s: SelectFont() failed!\n", __FUNCTION__);
1689 GreDeleteObject(hFont);
1690 return FALSE;
1691 }
1692
1693 if(uFlags & DC_INBUTTON)
1694 OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(COLOR_BTNTEXT));
1695 else OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(uFlags & DC_ACTIVE
1696 ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT));
1697
1698 //FIXME: If string doesn't fit to rc, truncate it and add ellipsis.
1699
1700 GreExtTextOutW(hDc, lpRc->left,
1701 lpRc->top, 0, NULL, Text->Buffer,
1702 Text->Length/sizeof(WCHAR), NULL, 0);
1703
1704 IntGdiSetTextColor(hDc, OldTextColor);
1705 NtGdiSelectFont(hDc, hOldFont);
1706 GreDeleteObject(hFont);
1707
1708 return TRUE;
1709 }
1710
1711 BOOL UserDrawCaption(
1712 PWINDOW_OBJECT pWnd,
1713 HDC hDc,
1714 RECTL *lpRc,
1715 HFONT hFont,
1716 HICON hIcon,
1717 const PUNICODE_STRING str,
1718 UINT uFlags)
1719 {
1720 BOOL Ret = FALSE;
1721 HBITMAP hMemBmp = NULL, hOldBmp = NULL;
1722 HBRUSH hOldBrush = NULL;
1723 HDC hMemDc = NULL;
1724 ULONG Height;
1725 UINT VCenter = 0, Padding = 0;
1726 RECTL r = *lpRc;
1727 LONG ButtonWidth, IconWidth;
1728 BOOL HasIcon;
1729 PWND Wnd = NULL;
1730
1731 //ASSERT(pWnd != NULL);
1732
1733 if (pWnd)
1734 Wnd = pWnd->Wnd;
1735
1736 RECTL_vMakeWellOrdered(lpRc);
1737 hMemBmp = NtGdiCreateCompatibleBitmap(hDc,
1738 lpRc->right - lpRc->left,
1739 lpRc->bottom - lpRc->top);
1740
1741 if(!hMemBmp)
1742 {
1743 DPRINT1("%s: NtGdiCreateCompatibleBitmap() failed!\n", __FUNCTION__);
1744 return FALSE;
1745 }
1746
1747 hMemDc = NtGdiCreateCompatibleDC(hDc);
1748 if(!hMemDc)
1749 {
1750 DPRINT1("%s: NtGdiCreateCompatibleDC() failed!\n", __FUNCTION__);
1751 goto cleanup;
1752 }
1753
1754 hOldBmp = NtGdiSelectBitmap(hMemDc, hMemBmp);
1755 if(!hOldBmp)
1756 {
1757 DPRINT1("%s: NtGdiSelectBitmap() failed!\n", __FUNCTION__);
1758 goto cleanup;
1759 }
1760
1761 Height = UserGetSystemMetrics(SM_CYCAPTION) - 1;
1762 VCenter = (lpRc->bottom - lpRc->top) / 2;
1763 Padding = VCenter - (Height / 2);
1764
1765 if ((!hIcon) && (Wnd != NULL))
1766 {
1767 HasIcon = (uFlags & DC_ICON) && (Wnd->style & WS_SYSMENU)
1768 && !(uFlags & DC_SMALLCAP) && !(Wnd->ExStyle & WS_EX_DLGMODALFRAME)
1769 && !(Wnd->ExStyle & WS_EX_TOOLWINDOW);
1770 }
1771 else
1772 HasIcon = (hIcon != 0);
1773
1774 IconWidth = UserGetSystemMetrics(SM_CXSIZE) + Padding;
1775
1776 r.left = Padding;
1777 r.right = r.left + (lpRc->right - lpRc->left);
1778 r.top = Padding;
1779 r.bottom = r.top + (Height / 2);
1780
1781 // Draw the caption background
1782 if(uFlags & DC_INBUTTON)
1783 {
1784 hOldBrush = NtGdiSelectBrush(hMemDc,
1785 IntGetSysColorBrush(COLOR_3DFACE));
1786
1787 if(!hOldBrush)
1788 {
1789 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1790 goto cleanup;
1791 }
1792
1793 if(!NtGdiPatBlt(hMemDc, 0, 0,
1794 lpRc->right - lpRc->left,
1795 lpRc->bottom - lpRc->top,
1796 PATCOPY))
1797 {
1798 DPRINT1("%s: NtGdiPatBlt() failed!\n", __FUNCTION__);
1799 goto cleanup;
1800 }
1801
1802 if(HasIcon) r.left+=IconWidth;
1803 }
1804 else
1805 {
1806 r.right = (lpRc->right - lpRc->left);
1807 if(uFlags & DC_SMALLCAP)
1808 ButtonWidth = UserGetSystemMetrics(SM_CXSMSIZE) - 2;
1809 else ButtonWidth = UserGetSystemMetrics(SM_CXSIZE) - 2;
1810
1811 hOldBrush = NtGdiSelectBrush(hMemDc,
1812 IntGetSysColorBrush(uFlags & DC_ACTIVE ?
1813 COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION));
1814
1815 if(!hOldBrush)
1816 {
1817 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1818 goto cleanup;
1819 }
1820
1821 if(HasIcon && (uFlags & DC_GRADIENT))
1822 {
1823 NtGdiPatBlt(hMemDc, 0, 0,
1824 IconWidth+1,
1825 lpRc->bottom - lpRc->top,
1826 PATCOPY);
1827 r.left+=IconWidth;
1828 }
1829 else
1830 {
1831 NtGdiPatBlt(hMemDc, 0, 0,
1832 lpRc->right - lpRc->left,
1833 lpRc->bottom - lpRc->top,
1834 PATCOPY);
1835 }
1836
1837 if(uFlags & DC_GRADIENT)
1838 {
1839 static GRADIENT_RECT gcap = {0, 1};
1840 TRIVERTEX vert[2];
1841 COLORREF Colors[2];
1842
1843 if (Wnd != NULL)
1844 {
1845 if(Wnd->style & WS_SYSMENU)
1846 {
1847 r.right -= 3 + ButtonWidth;
1848 if(!(uFlags & DC_SMALLCAP))
1849 {
1850 if(Wnd->style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX))
1851 r.right -= 2 + 2 * ButtonWidth;
1852 else r.right -= 2;
1853 r.right -= 2;
1854 }
1855
1856 //Draw buttons background
1857 if(!NtGdiSelectBrush(hMemDc,
1858 IntGetSysColorBrush(uFlags & DC_ACTIVE ?
1859 COLOR_GRADIENTACTIVECAPTION:COLOR_GRADIENTINACTIVECAPTION)))
1860 {
1861 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1862 goto cleanup;
1863 }
1864
1865 NtGdiPatBlt(hMemDc,
1866 r.right,
1867 0,
1868 lpRc->right - lpRc->left - r.right,
1869 lpRc->bottom - lpRc->top,
1870 PATCOPY);
1871 }
1872 }
1873
1874 Colors[0] = IntGetSysColor((uFlags & DC_ACTIVE) ?
1875 COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION);
1876
1877 Colors[1] = IntGetSysColor((uFlags & DC_ACTIVE) ?
1878 COLOR_GRADIENTACTIVECAPTION : COLOR_GRADIENTINACTIVECAPTION);
1879
1880 vert[0].x = r.left;
1881 vert[0].y = 0;
1882 vert[0].Red = (WORD)Colors[0]<<8;
1883 vert[0].Green = (WORD)Colors[0] & 0xFF00;
1884 vert[0].Blue = (WORD)(Colors[0]>>8) & 0xFF00;
1885 vert[0].Alpha = 0;
1886
1887 vert[1].x = r.right;
1888 vert[1].y = lpRc->bottom - lpRc->top;
1889 vert[1].Red = (WORD)Colors[1]<<8;
1890 vert[1].Green = (WORD)Colors[1] & 0xFF00;
1891 vert[1].Blue = (WORD)(Colors[1]>>8) & 0xFF00;
1892 vert[1].Alpha = 0;
1893
1894 if(!GreGradientFill(hMemDc, vert, 2, &gcap,
1895 1, GRADIENT_FILL_RECT_H))
1896 {
1897 DPRINT1("%s: IntGdiGradientFill() failed!\n", __FUNCTION__);
1898 }
1899
1900 } //if(uFlags & DC_GRADIENT)
1901 }
1902
1903 if(HasIcon)
1904 {
1905 r.top ++;
1906 r.left -= --IconWidth;
1907 /* FIXME: Draw the Icon when pWnd == NULL but hIcon is valid */
1908 if (pWnd != NULL)
1909 UserDrawSysMenuButton(pWnd, hMemDc, &r, FALSE);
1910
1911 r.left += IconWidth;
1912 r.top --;
1913 }
1914
1915 r.top ++;
1916 r.left += 2;
1917
1918 r.bottom = r.top + Height;
1919
1920 if((uFlags & DC_TEXT))
1921 {
1922 if(!(uFlags & DC_GRADIENT))
1923 {
1924 r.right = (lpRc->right - lpRc->left);
1925
1926 if(uFlags & DC_SMALLCAP)
1927 ButtonWidth = UserGetSystemMetrics(SM_CXSMSIZE) - 2;
1928 else ButtonWidth = UserGetSystemMetrics(SM_CXSIZE) - 2;
1929
1930 if ((Wnd != NULL) && (Wnd->style & WS_SYSMENU))
1931 {
1932 r.right -= 3 + ButtonWidth;
1933 if(! (uFlags & DC_SMALLCAP))
1934 {
1935 if(Wnd->style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX))
1936 r.right -= 2 + 2 * ButtonWidth;
1937 else r.right -= 2;
1938 r.right -= 2;
1939 }
1940 }
1941 }
1942
1943 /* FIXME: hFont isn't handled */
1944 if (str)
1945 UserDrawCaptionText(hMemDc, str, &r, uFlags);
1946 else if (pWnd != NULL)
1947 {
1948 UNICODE_STRING ustr;
1949 ustr.Buffer = pWnd->Wnd->strName.Buffer;
1950 ustr.Length = pWnd->Wnd->strName.Length;
1951 ustr.MaximumLength = pWnd->Wnd->strName.MaximumLength;
1952 UserDrawCaptionText(hMemDc, &ustr, &r, uFlags);
1953 }
1954 }
1955
1956 if(!NtGdiBitBlt(hDc, lpRc->left, lpRc->top,
1957 lpRc->right - lpRc->left, lpRc->bottom - lpRc->top,
1958 hMemDc, 0, 0, SRCCOPY, 0, 0))
1959 {
1960 DPRINT1("%s: NtGdiBitBlt() failed!\n", __FUNCTION__);
1961 goto cleanup;
1962 }
1963
1964 Ret = TRUE;
1965
1966 cleanup:
1967 if (hOldBrush) NtGdiSelectBrush(hMemDc, hOldBrush);
1968 if (hOldBmp) NtGdiSelectBitmap(hMemDc, hOldBmp);
1969 if (hMemBmp) GreDeleteObject(hMemBmp);
1970 if (hMemDc) NtGdiDeleteObjectApp(hMemDc);
1971
1972 return Ret;
1973 }
1974
1975 INT
1976 FASTCALL
1977 UserRealizePalette(HDC hdc)
1978 {
1979 HWND hWnd;
1980 DWORD Ret;
1981
1982 Ret = IntGdiRealizePalette(hdc);
1983 if (Ret) // There was a change.
1984 {
1985 hWnd = IntWindowFromDC(hdc);
1986 if (hWnd) // Send broadcast if dc is associated with a window.
1987 { // FYI: Thread locked in CallOneParam.
1988 co_IntSendMessage((HWND)HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)hWnd, 0);
1989 }
1990 }
1991 return Ret;
1992 }
1993
1994 BOOL
1995 APIENTRY
1996 NtUserDrawCaptionTemp(
1997 HWND hWnd,
1998 HDC hDC,
1999 LPCRECT lpRc,
2000 HFONT hFont,
2001 HICON hIcon,
2002 const PUNICODE_STRING str,
2003 UINT uFlags)
2004 {
2005 PWINDOW_OBJECT pWnd = NULL;
2006 UNICODE_STRING SafeStr = {0};
2007 NTSTATUS Status = STATUS_SUCCESS;
2008 RECTL SafeRect;
2009 BOOL Ret;
2010
2011 UserEnterExclusive();
2012
2013 if (hWnd != NULL)
2014 {
2015 if(!(pWnd = UserGetWindowObject(hWnd)))
2016 {
2017 UserLeave();
2018 return FALSE;
2019 }
2020 }
2021
2022 _SEH2_TRY
2023 {
2024 ProbeForRead(lpRc, sizeof(RECTL), sizeof(ULONG));
2025 RtlCopyMemory(&SafeRect, lpRc, sizeof(RECTL));
2026 if (str != NULL)
2027 {
2028 SafeStr = ProbeForReadUnicodeString(str);
2029 if (SafeStr.Length != 0)
2030 {
2031 ProbeForRead( SafeStr.Buffer,
2032 SafeStr.Length,
2033 sizeof(WCHAR));
2034 }
2035 }
2036 }
2037 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2038 {
2039 Status = _SEH2_GetExceptionCode();
2040 }
2041 _SEH2_END;
2042
2043 if (Status != STATUS_SUCCESS)
2044 {
2045 SetLastNtError(Status);
2046 UserLeave();
2047 return FALSE;
2048 }
2049
2050 if (str != NULL)
2051 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, &SafeStr, uFlags);
2052 else
2053 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, NULL, uFlags);
2054
2055 UserLeave();
2056 return Ret;
2057 }
2058
2059 BOOL
2060 APIENTRY
2061 NtUserDrawCaption(HWND hWnd,
2062 HDC hDC,
2063 LPCRECT lpRc,
2064 UINT uFlags)
2065 {
2066 return NtUserDrawCaptionTemp(hWnd, hDC, lpRc, 0, 0, NULL, uFlags);
2067 }
2068
2069 BOOL
2070 APIENTRY
2071 NtUserInvalidateRect(
2072 HWND hWnd,
2073 CONST RECT *lpUnsafeRect,
2074 BOOL bErase)
2075 {
2076 return NtUserRedrawWindow(hWnd, lpUnsafeRect, NULL, RDW_INVALIDATE | (bErase? RDW_ERASE : 0));
2077 }
2078
2079 BOOL
2080 APIENTRY
2081 NtUserInvalidateRgn(
2082 HWND hWnd,
2083 HRGN hRgn,
2084 BOOL bErase)
2085 {
2086 return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_INVALIDATE | (bErase? RDW_ERASE : 0));
2087 }
2088
2089 BOOL
2090 APIENTRY
2091 NtUserPrintWindow(
2092 HWND hwnd,
2093 HDC hdcBlt,
2094 UINT nFlags)
2095 {
2096 PWINDOW_OBJECT Window;
2097 BOOL Ret = FALSE;
2098
2099 UserEnterExclusive();
2100
2101 if (hwnd)
2102 {
2103 Window = UserGetWindowObject(hwnd);
2104 // TODO: Add Desktop and MessageBox check via FNID's.
2105 if ( Window )
2106 {
2107 /* Validate flags and check it as a mask for 0 or 1. */
2108 if ( (nFlags & PW_CLIENTONLY) == nFlags)
2109 Ret = IntPrintWindow( Window, hdcBlt, nFlags);
2110 else
2111 SetLastWin32Error(ERROR_INVALID_PARAMETER);
2112 }
2113 }
2114
2115 UserLeave();
2116 return Ret;
2117 }
2118
2119 /* ValidateRect gets redirected to NtUserValidateRect:
2120 http://blog.csdn.net/ntdll/archive/2005/10/19/509299.aspx */
2121 BOOL
2122 APIENTRY
2123 NtUserValidateRect(
2124 HWND hWnd,
2125 const RECT *lpRect)
2126 {
2127 if (hWnd)
2128 {
2129 return NtUserRedrawWindow(hWnd, lpRect, NULL, RDW_VALIDATE );
2130 }
2131 return NtUserRedrawWindow(hWnd, lpRect, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ERASENOW|RDW_ALLCHILDREN);
2132 }
2133
2134 /* EOF */