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