[WIN32K / USER32]
[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 = pDC->rosdc.hVisRgn; // pDC->prgnRao?
1210 DC_UnlockDc(pDC);
1211
1212 /* Begin with the shifted and then clipped scroll rect */
1213 rcDst = rcScroll;
1214 RECTL_vOffsetRect(&rcDst, dx, dy);
1215 RECTL_bIntersectRect(&rcDst, &rcDst, &rcClip);
1216 if (hrgnUpdate)
1217 {
1218 hrgnOwn = hrgnUpdate;
1219 if (!NtGdiSetRectRgn(hrgnOwn, rcDst.left, rcDst.top, rcDst.right, rcDst.bottom))
1220 {
1221 return ERROR;
1222 }
1223 }
1224 else
1225 {
1226 hrgnOwn = IntSysCreateRectRgnIndirect(&rcDst);
1227 }
1228
1229 /* Add the source rect */
1230 hrgnTmp = IntSysCreateRectRgnIndirect(&rcSrc);
1231 NtGdiCombineRgn(hrgnOwn, hrgnOwn, hrgnTmp, RGN_OR);
1232
1233 /* Substract the part of the dest that was visible in source */
1234 NtGdiCombineRgn(hrgnTmp, hrgnTmp, hrgnVisible, RGN_AND);
1235 NtGdiOffsetRgn(hrgnTmp, dx, dy);
1236 Result = NtGdiCombineRgn(hrgnOwn, hrgnOwn, hrgnTmp, RGN_DIFF);
1237
1238 REGION_FreeRgnByHandle(hrgnTmp);
1239
1240 if (prcUpdate)
1241 {
1242 IntGdiGetRgnBox(hrgnOwn, prcUpdate);
1243 }
1244
1245 if (!hrgnUpdate)
1246 {
1247 REGION_FreeRgnByHandle(hrgnOwn);
1248 }
1249 }
1250 else
1251 Result = NULLREGION;
1252
1253 return Result;
1254 }
1255
1256 /*
1257 * NtUserScrollDC
1258 *
1259 * Status
1260 * @implemented
1261 */
1262 BOOL APIENTRY
1263 NtUserScrollDC(
1264 HDC hDC,
1265 INT dx,
1266 INT dy,
1267 const RECT *prcUnsafeScroll,
1268 const RECT *prcUnsafeClip,
1269 HRGN hrgnUpdate,
1270 LPRECT prcUnsafeUpdate)
1271 {
1272 DECLARE_RETURN(DWORD);
1273 RECTL rcScroll, rcClip, rcUpdate;
1274 NTSTATUS Status = STATUS_SUCCESS;
1275 DWORD Result;
1276
1277 DPRINT("Enter NtUserScrollDC\n");
1278 UserEnterExclusive();
1279
1280 _SEH2_TRY
1281 {
1282 if (prcUnsafeScroll)
1283 {
1284 ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
1285 rcScroll = *prcUnsafeScroll;
1286 }
1287 if (prcUnsafeClip)
1288 {
1289 ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
1290 rcClip = *prcUnsafeClip;
1291 }
1292 if (prcUnsafeUpdate)
1293 {
1294 ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
1295 }
1296 }
1297 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1298 {
1299 Status = _SEH2_GetExceptionCode();
1300 }
1301 _SEH2_END
1302 if (!NT_SUCCESS(Status))
1303 {
1304 SetLastNtError(Status);
1305 RETURN(FALSE);
1306 }
1307
1308 Result = UserScrollDC( hDC,
1309 dx,
1310 dy,
1311 prcUnsafeScroll? &rcScroll : 0,
1312 prcUnsafeClip? &rcClip : 0,
1313 hrgnUpdate,
1314 prcUnsafeUpdate? &rcUpdate : NULL);
1315 if(Result == ERROR)
1316 {
1317 /* FIXME: Only if hRgnUpdate is invalid we should SetLastError(ERROR_INVALID_HANDLE) */
1318 RETURN(FALSE);
1319 }
1320
1321 if (prcUnsafeUpdate)
1322 {
1323 _SEH2_TRY
1324 {
1325 *prcUnsafeUpdate = rcUpdate;
1326 }
1327 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1328 {
1329 Status = _SEH2_GetExceptionCode();
1330 }
1331 _SEH2_END
1332 if (!NT_SUCCESS(Status))
1333 {
1334 /* FIXME: SetLastError? */
1335 /* FIXME: correct? We have already scrolled! */
1336 RETURN(FALSE);
1337 }
1338 }
1339
1340 RETURN(TRUE);
1341
1342 CLEANUP:
1343 DPRINT("Leave NtUserScrollDC, ret=%i\n",_ret_);
1344 UserLeave();
1345 END_CLEANUP;
1346 }
1347
1348 /*
1349 * NtUserScrollWindowEx
1350 *
1351 * Status
1352 * @implemented
1353 */
1354
1355 DWORD APIENTRY
1356 NtUserScrollWindowEx(
1357 HWND hWnd,
1358 INT dx,
1359 INT dy,
1360 const RECT *prcUnsafeScroll,
1361 const RECT *prcUnsafeClip,
1362 HRGN hrgnUpdate,
1363 LPRECT prcUnsafeUpdate,
1364 UINT flags)
1365 {
1366 RECTL rcScroll, rcClip, rcCaret, rcUpdate;
1367 INT Result;
1368 PWINDOW_OBJECT Window = NULL, CaretWnd;
1369 HDC hDC;
1370 HRGN hrgnOwn = NULL, hrgnTemp;
1371 HWND hwndCaret;
1372 NTSTATUS Status = STATUS_SUCCESS;
1373 DECLARE_RETURN(DWORD);
1374 USER_REFERENCE_ENTRY Ref, CaretRef;
1375
1376 DPRINT("Enter NtUserScrollWindowEx\n");
1377 UserEnterExclusive();
1378
1379 Window = UserGetWindowObject(hWnd);
1380 if (!Window || !IntIsWindowDrawable(Window))
1381 {
1382 Window = NULL; /* prevent deref at cleanup */
1383 RETURN( ERROR);
1384 }
1385 UserRefObjectCo(Window, &Ref);
1386
1387 IntGetClientRect(Window, &rcClip);
1388
1389 _SEH2_TRY
1390 {
1391 if (prcUnsafeScroll)
1392 {
1393 ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
1394 RECTL_bIntersectRect(&rcScroll, &rcClip, prcUnsafeScroll);
1395 }
1396 else
1397 rcScroll = rcClip;
1398
1399 if (prcUnsafeClip)
1400 {
1401 ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
1402 RECTL_bIntersectRect(&rcClip, &rcClip, prcUnsafeClip);
1403 }
1404 }
1405 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1406 {
1407 Status = _SEH2_GetExceptionCode();
1408 }
1409 _SEH2_END
1410
1411 if (!NT_SUCCESS(Status))
1412 {
1413 SetLastNtError(Status);
1414 RETURN(ERROR);
1415 }
1416
1417 if (rcClip.right <= rcClip.left || rcClip.bottom <= rcClip.top ||
1418 (dx == 0 && dy == 0))
1419 {
1420 RETURN(NULLREGION);
1421 }
1422
1423 if (hrgnUpdate)
1424 hrgnOwn = hrgnUpdate;
1425 else
1426 hrgnOwn = IntSysCreateRectRgn(0, 0, 0, 0);
1427
1428 hDC = UserGetDCEx(Window, 0, DCX_CACHE | DCX_USESTYLE);
1429 if (!hDC)
1430 {
1431 /* FIXME: SetLastError? */
1432 RETURN(ERROR);
1433 }
1434
1435 rcCaret = rcScroll;
1436 hwndCaret = co_IntFixCaret(Window, &rcCaret, flags);
1437
1438 Result = UserScrollDC( hDC,
1439 dx,
1440 dy,
1441 &rcScroll,
1442 &rcClip,
1443 hrgnOwn,
1444 prcUnsafeUpdate? &rcUpdate : NULL);
1445
1446 UserReleaseDC(Window, hDC, FALSE);
1447
1448 /*
1449 * Take into account the fact that some damage may have occurred during
1450 * the scroll.
1451 */
1452
1453 hrgnTemp = IntSysCreateRectRgn(0, 0, 0, 0);
1454 if (co_UserGetUpdateRgn(Window, hrgnTemp, FALSE) != NULLREGION)
1455 {
1456 HRGN hrgnClip = IntSysCreateRectRgnIndirect(&rcClip);
1457 NtGdiOffsetRgn(hrgnTemp, dx, dy);
1458 NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnClip, RGN_AND);
1459 co_UserRedrawWindow(Window, NULL, hrgnTemp, RDW_INVALIDATE | RDW_ERASE);
1460 REGION_FreeRgnByHandle(hrgnClip);
1461 }
1462 REGION_FreeRgnByHandle(hrgnTemp);
1463
1464 if (flags & SW_SCROLLCHILDREN)
1465 {
1466 PWINDOW_OBJECT Child;
1467 RECTL rcChild;
1468 POINT ClientOrigin;
1469 USER_REFERENCE_ENTRY WndRef;
1470 RECTL rcDummy;
1471
1472 IntGetClientOrigin(Window, &ClientOrigin);
1473 for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
1474 {
1475 rcChild = Child->Wnd->rcWindow;
1476 rcChild.left -= ClientOrigin.x;
1477 rcChild.top -= ClientOrigin.y;
1478 rcChild.right -= ClientOrigin.x;
1479 rcChild.bottom -= ClientOrigin.y;
1480
1481 if (! prcUnsafeScroll || RECTL_bIntersectRect(&rcDummy, &rcChild, &rcScroll))
1482 {
1483 UserRefObjectCo(Child, &WndRef);
1484 co_WinPosSetWindowPos(Child, 0, rcChild.left + dx, rcChild.top + dy, 0, 0,
1485 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
1486 SWP_NOREDRAW);
1487 UserDerefObjectCo(Child);
1488 }
1489 }
1490 }
1491
1492 if (flags & (SW_INVALIDATE | SW_ERASE))
1493 {
1494 co_UserRedrawWindow(Window, NULL, hrgnOwn, RDW_INVALIDATE | RDW_ERASE |
1495 ((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
1496 ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0));
1497 }
1498
1499 if ((CaretWnd = UserGetWindowObject(hwndCaret)))
1500 {
1501 UserRefObjectCo(CaretWnd, &CaretRef);
1502
1503 co_IntSetCaretPos(rcCaret.left + dx, rcCaret.top + dy);
1504 co_UserShowCaret(CaretWnd);
1505
1506 UserDerefObjectCo(CaretWnd);
1507 }
1508
1509 if (prcUnsafeUpdate)
1510 {
1511 _SEH2_TRY
1512 {
1513 /* Probe here, to not fail on invalid pointer before scrolling */
1514 ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
1515 *prcUnsafeUpdate = rcUpdate;
1516 }
1517 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1518 {
1519 Status = _SEH2_GetExceptionCode();
1520 }
1521 _SEH2_END
1522
1523 if (!NT_SUCCESS(Status))
1524 {
1525 SetLastNtError(Status);
1526 RETURN(ERROR);
1527 }
1528 }
1529
1530 RETURN(Result);
1531
1532 CLEANUP:
1533 if (hrgnOwn && !hrgnUpdate)
1534 {
1535 REGION_FreeRgnByHandle(hrgnOwn);
1536 }
1537
1538 if (Window)
1539 UserDerefObjectCo(Window);
1540
1541 DPRINT("Leave NtUserScrollWindowEx, ret=%i\n",_ret_);
1542 UserLeave();
1543 END_CLEANUP;
1544 }
1545
1546
1547 BOOL
1548 UserDrawSysMenuButton(
1549 PWINDOW_OBJECT pWnd,
1550 HDC hDc,
1551 RECTL *lpRc,
1552 BOOL Down)
1553 {
1554 HICON hIcon;
1555 PCURICON_OBJECT pIcon;
1556
1557 ASSERT(pWnd && lpRc);
1558
1559 /* Get the icon to draw. We don't care about WM_GETICON here. */
1560
1561 hIcon = pWnd->Wnd->pcls->hIconSm;
1562
1563 if(!hIcon)
1564 {
1565 DPRINT("Wnd class has no small icon.\n");
1566 hIcon = pWnd->Wnd->pcls->hIcon;
1567 }
1568
1569 if(!hIcon)
1570 {
1571 DPRINT("Wnd class hasn't any icon.\n");
1572 //FIXME: Draw "winlogo" icon.
1573 return FALSE;
1574 }
1575
1576 if(!(pIcon = UserGetCurIconObject(hIcon)))
1577 {
1578 DPRINT1("UserGetCurIconObject() failed!\n");
1579 return FALSE;
1580 }
1581
1582 return UserDrawIconEx(hDc, lpRc->left, lpRc->top, pIcon,
1583 UserGetSystemMetrics(SM_CXSMICON),
1584 UserGetSystemMetrics(SM_CYSMICON),
1585 0, NULL, DI_NORMAL);
1586 }
1587
1588 BOOL
1589 UserDrawCaptionText(
1590 HDC hDc,
1591 const PUNICODE_STRING Text,
1592 const RECTL *lpRc,
1593 UINT uFlags)
1594 {
1595 HFONT hOldFont = NULL, hFont = NULL;
1596 COLORREF OldTextColor;
1597 NONCLIENTMETRICSW nclm;
1598 NTSTATUS Status;
1599 #ifndef NDEBUG
1600 INT i;
1601 DPRINT("%s:", __FUNCTION__);
1602 for(i = 0; i < Text->Length/sizeof(WCHAR); i++)
1603 DbgPrint("%C", Text->Buffer[i]);
1604 DbgPrint(", %d\n", Text->Length/sizeof(WCHAR));
1605 #endif
1606
1607 nclm.cbSize = sizeof(nclm);
1608 if(!UserSystemParametersInfo(SPI_GETNONCLIENTMETRICS,
1609 sizeof(NONCLIENTMETRICS), &nclm, 0))
1610 {
1611 DPRINT1("%s: UserSystemParametersInfo() failed!\n", __FUNCTION__);
1612 return FALSE;
1613 }
1614
1615 IntGdiSetBkMode(hDc, TRANSPARENT);
1616
1617 if(uFlags & DC_SMALLCAP)
1618 Status = TextIntCreateFontIndirect(&nclm.lfSmCaptionFont, &hFont);
1619 else Status = TextIntCreateFontIndirect(&nclm.lfCaptionFont, &hFont);
1620
1621 if(!NT_SUCCESS(Status))
1622 {
1623 DPRINT1("%s: TextIntCreateFontIndirect() failed! Status: 0x%x\n",
1624 __FUNCTION__, Status);
1625 return FALSE;
1626 }
1627
1628 hOldFont = NtGdiSelectFont(hDc, hFont);
1629 if(!hOldFont)
1630 {
1631 DPRINT1("%s: SelectFont() failed!\n", __FUNCTION__);
1632 GreDeleteObject(hFont);
1633 return FALSE;
1634 }
1635
1636 if(uFlags & DC_INBUTTON)
1637 OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(COLOR_BTNTEXT));
1638 else OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(uFlags & DC_ACTIVE
1639 ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT));
1640
1641 //FIXME: If string doesn't fit to rc, truncate it and add ellipsis.
1642
1643 GreExtTextOutW(hDc, lpRc->left,
1644 lpRc->top, 0, NULL, Text->Buffer,
1645 Text->Length/sizeof(WCHAR), NULL, 0);
1646
1647 IntGdiSetTextColor(hDc, OldTextColor);
1648 NtGdiSelectFont(hDc, hOldFont);
1649 GreDeleteObject(hFont);
1650
1651 return TRUE;
1652 }
1653
1654 BOOL UserDrawCaption(
1655 PWINDOW_OBJECT pWnd,
1656 HDC hDc,
1657 RECTL *lpRc,
1658 HFONT hFont,
1659 HICON hIcon,
1660 const PUNICODE_STRING str,
1661 UINT uFlags)
1662 {
1663 BOOL Ret = FALSE;
1664 HBITMAP hMemBmp = NULL, hOldBmp = NULL;
1665 HBRUSH hOldBrush = NULL;
1666 HDC hMemDc = NULL;
1667 ULONG Height;
1668 UINT VCenter = 0, Padding = 0;
1669 RECTL r = *lpRc;
1670 LONG ButtonWidth, IconWidth;
1671 BOOL HasIcon;
1672 PWND Wnd = NULL;
1673
1674 //ASSERT(pWnd != NULL);
1675
1676 if (pWnd)
1677 Wnd = pWnd->Wnd;
1678
1679 RECTL_vMakeWellOrdered(lpRc);
1680 hMemBmp = NtGdiCreateCompatibleBitmap(hDc,
1681 lpRc->right - lpRc->left,
1682 lpRc->bottom - lpRc->top);
1683
1684 if(!hMemBmp)
1685 {
1686 DPRINT1("%s: NtGdiCreateCompatibleBitmap() failed!\n", __FUNCTION__);
1687 return FALSE;
1688 }
1689
1690 hMemDc = NtGdiCreateCompatibleDC(hDc);
1691 if(!hMemDc)
1692 {
1693 DPRINT1("%s: NtGdiCreateCompatibleDC() failed!\n", __FUNCTION__);
1694 goto cleanup;
1695 }
1696
1697 hOldBmp = NtGdiSelectBitmap(hMemDc, hMemBmp);
1698 if(!hOldBmp)
1699 {
1700 DPRINT1("%s: NtGdiSelectBitmap() failed!\n", __FUNCTION__);
1701 goto cleanup;
1702 }
1703
1704 Height = UserGetSystemMetrics(SM_CYCAPTION) - 1;
1705 VCenter = (lpRc->bottom - lpRc->top) / 2;
1706 Padding = VCenter - (Height / 2);
1707
1708 if ((!hIcon) && (Wnd != NULL))
1709 {
1710 HasIcon = (uFlags & DC_ICON) && (Wnd->style & WS_SYSMENU)
1711 && !(uFlags & DC_SMALLCAP) && !(Wnd->ExStyle & WS_EX_DLGMODALFRAME)
1712 && !(Wnd->ExStyle & WS_EX_TOOLWINDOW);
1713 }
1714 else
1715 HasIcon = (hIcon != 0);
1716
1717 IconWidth = UserGetSystemMetrics(SM_CXSIZE) + Padding;
1718
1719 r.left = Padding;
1720 r.right = r.left + (lpRc->right - lpRc->left);
1721 r.top = Padding;
1722 r.bottom = r.top + (Height / 2);
1723
1724 // Draw the caption background
1725 if(uFlags & DC_INBUTTON)
1726 {
1727 hOldBrush = NtGdiSelectBrush(hMemDc,
1728 IntGetSysColorBrush(COLOR_3DFACE));
1729
1730 if(!hOldBrush)
1731 {
1732 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1733 goto cleanup;
1734 }
1735
1736 if(!NtGdiPatBlt(hMemDc, 0, 0,
1737 lpRc->right - lpRc->left,
1738 lpRc->bottom - lpRc->top,
1739 PATCOPY))
1740 {
1741 DPRINT1("%s: NtGdiPatBlt() failed!\n", __FUNCTION__);
1742 goto cleanup;
1743 }
1744
1745 if(HasIcon) r.left+=IconWidth;
1746 }
1747 else
1748 {
1749 r.right = (lpRc->right - lpRc->left);
1750 if(uFlags & DC_SMALLCAP)
1751 ButtonWidth = UserGetSystemMetrics(SM_CXSMSIZE) - 2;
1752 else ButtonWidth = UserGetSystemMetrics(SM_CXSIZE) - 2;
1753
1754 hOldBrush = NtGdiSelectBrush(hMemDc,
1755 IntGetSysColorBrush(uFlags & DC_ACTIVE ?
1756 COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION));
1757
1758 if(!hOldBrush)
1759 {
1760 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1761 goto cleanup;
1762 }
1763
1764 if(HasIcon && (uFlags & DC_GRADIENT))
1765 {
1766 NtGdiPatBlt(hMemDc, 0, 0,
1767 IconWidth+1,
1768 lpRc->bottom - lpRc->top,
1769 PATCOPY);
1770 r.left+=IconWidth;
1771 }
1772 else
1773 {
1774 NtGdiPatBlt(hMemDc, 0, 0,
1775 lpRc->right - lpRc->left,
1776 lpRc->bottom - lpRc->top,
1777 PATCOPY);
1778 }
1779
1780 if(uFlags & DC_GRADIENT)
1781 {
1782 static GRADIENT_RECT gcap = {0, 1};
1783 TRIVERTEX vert[2];
1784 COLORREF Colors[2];
1785 PDC pMemDc;
1786
1787 if (Wnd != NULL)
1788 {
1789 if(Wnd->style & WS_SYSMENU)
1790 {
1791 r.right -= 3 + ButtonWidth;
1792 if(!(uFlags & DC_SMALLCAP))
1793 {
1794 if(Wnd->style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX))
1795 r.right -= 2 + 2 * ButtonWidth;
1796 else r.right -= 2;
1797 r.right -= 2;
1798 }
1799
1800 //Draw buttons background
1801 if(!NtGdiSelectBrush(hMemDc,
1802 IntGetSysColorBrush(uFlags & DC_ACTIVE ?
1803 COLOR_GRADIENTACTIVECAPTION:COLOR_GRADIENTINACTIVECAPTION)))
1804 {
1805 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1806 goto cleanup;
1807 }
1808
1809 NtGdiPatBlt(hMemDc,
1810 r.right,
1811 0,
1812 lpRc->right - lpRc->left - r.right,
1813 lpRc->bottom - lpRc->top,
1814 PATCOPY);
1815 }
1816 }
1817
1818 Colors[0] = IntGetSysColor((uFlags & DC_ACTIVE) ?
1819 COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION);
1820
1821 Colors[1] = IntGetSysColor((uFlags & DC_ACTIVE) ?
1822 COLOR_GRADIENTACTIVECAPTION : COLOR_GRADIENTINACTIVECAPTION);
1823
1824 vert[0].x = r.left;
1825 vert[0].y = 0;
1826 vert[0].Red = (WORD)Colors[0]<<8;
1827 vert[0].Green = (WORD)Colors[0] & 0xFF00;
1828 vert[0].Blue = (WORD)(Colors[0]>>8) & 0xFF00;
1829 vert[0].Alpha = 0;
1830
1831 vert[1].x = r.right;
1832 vert[1].y = lpRc->bottom - lpRc->top;
1833 vert[1].Red = (WORD)Colors[1]<<8;
1834 vert[1].Green = (WORD)Colors[1] & 0xFF00;
1835 vert[1].Blue = (WORD)(Colors[1]>>8) & 0xFF00;
1836 vert[1].Alpha = 0;
1837
1838 pMemDc = DC_LockDc(hMemDc);
1839 if(!pMemDc)
1840 {
1841 DPRINT1("%s: Can't lock dc!\n", __FUNCTION__);
1842 goto cleanup;
1843 }
1844
1845 if(!IntGdiGradientFill(pMemDc, vert, 2, &gcap,
1846 1, GRADIENT_FILL_RECT_H))
1847 {
1848 DPRINT1("%s: IntGdiGradientFill() failed!\n", __FUNCTION__);
1849 }
1850
1851 DC_UnlockDc(pMemDc);
1852 } //if(uFlags & DC_GRADIENT)
1853 }
1854
1855 if(HasIcon)
1856 {
1857 r.top ++;
1858 r.left -= --IconWidth;
1859 /* FIXME: Draw the Icon when pWnd == NULL but hIcon is valid */
1860 if (pWnd != NULL)
1861 UserDrawSysMenuButton(pWnd, hMemDc, &r, FALSE);
1862
1863 r.left += IconWidth;
1864 r.top --;
1865 }
1866
1867 r.top ++;
1868 r.left += 2;
1869
1870 r.bottom = r.top + Height;
1871
1872 if((uFlags & DC_TEXT))
1873 {
1874 if(!(uFlags & DC_GRADIENT))
1875 {
1876 r.right = (lpRc->right - lpRc->left);
1877
1878 if(uFlags & DC_SMALLCAP)
1879 ButtonWidth = UserGetSystemMetrics(SM_CXSMSIZE) - 2;
1880 else ButtonWidth = UserGetSystemMetrics(SM_CXSIZE) - 2;
1881
1882 if ((Wnd != NULL) && (Wnd->style & WS_SYSMENU))
1883 {
1884 r.right -= 3 + ButtonWidth;
1885 if(! (uFlags & DC_SMALLCAP))
1886 {
1887 if(Wnd->style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX))
1888 r.right -= 2 + 2 * ButtonWidth;
1889 else r.right -= 2;
1890 r.right -= 2;
1891 }
1892 }
1893 }
1894
1895 /* FIXME: hFont isn't handled */
1896 if (str)
1897 UserDrawCaptionText(hMemDc, str, &r, uFlags);
1898 else if (pWnd != NULL)
1899 {
1900 UNICODE_STRING ustr;
1901 ustr.Buffer = pWnd->Wnd->strName.Buffer;
1902 ustr.Length = pWnd->Wnd->strName.Length;
1903 ustr.MaximumLength = pWnd->Wnd->strName.MaximumLength;
1904 UserDrawCaptionText(hMemDc, &ustr, &r, uFlags);
1905 }
1906 }
1907
1908 if(!NtGdiBitBlt(hDc, lpRc->left, lpRc->top,
1909 lpRc->right - lpRc->left, lpRc->bottom - lpRc->top,
1910 hMemDc, 0, 0, SRCCOPY, 0, 0))
1911 {
1912 DPRINT1("%s: NtGdiBitBlt() failed!\n", __FUNCTION__);
1913 goto cleanup;
1914 }
1915
1916 Ret = TRUE;
1917
1918 cleanup:
1919 if (hOldBrush) NtGdiSelectBrush(hMemDc, hOldBrush);
1920 if (hOldBmp) NtGdiSelectBitmap(hMemDc, hOldBmp);
1921 if (hMemBmp) GreDeleteObject(hMemBmp);
1922 if (hMemDc) NtGdiDeleteObjectApp(hMemDc);
1923
1924 return Ret;
1925 }
1926
1927 INT
1928 FASTCALL
1929 UserRealizePalette(HDC hdc)
1930 {
1931 HWND hWnd;
1932 DWORD Ret;
1933
1934 Ret = IntGdiRealizePalette(hdc);
1935 if (Ret) // There was a change.
1936 {
1937 hWnd = IntWindowFromDC(hdc);
1938 if (hWnd) // Send broadcast if dc is associated with a window.
1939 { // FYI: Thread locked in CallOneParam.
1940 co_IntSendMessage((HWND)HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)hWnd, 0);
1941 }
1942 }
1943 return Ret;
1944 }
1945
1946 BOOL
1947 APIENTRY
1948 NtUserDrawCaptionTemp(
1949 HWND hWnd,
1950 HDC hDC,
1951 LPCRECT lpRc,
1952 HFONT hFont,
1953 HICON hIcon,
1954 const PUNICODE_STRING str,
1955 UINT uFlags)
1956 {
1957 PWINDOW_OBJECT pWnd = NULL;
1958 UNICODE_STRING SafeStr = {0};
1959 NTSTATUS Status = STATUS_SUCCESS;
1960 RECTL SafeRect;
1961 BOOL Ret;
1962
1963 UserEnterExclusive();
1964
1965 if (hWnd != NULL)
1966 {
1967 if(!(pWnd = UserGetWindowObject(hWnd)))
1968 {
1969 UserLeave();
1970 return FALSE;
1971 }
1972 }
1973
1974 _SEH2_TRY
1975 {
1976 ProbeForRead(lpRc, sizeof(RECTL), sizeof(ULONG));
1977 RtlCopyMemory(&SafeRect, lpRc, sizeof(RECTL));
1978 if (str != NULL)
1979 {
1980 SafeStr = ProbeForReadUnicodeString(str);
1981 if (SafeStr.Length != 0)
1982 {
1983 ProbeForRead( SafeStr.Buffer,
1984 SafeStr.Length,
1985 sizeof(WCHAR));
1986 }
1987 }
1988 }
1989 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1990 {
1991 Status = _SEH2_GetExceptionCode();
1992 }
1993 _SEH2_END;
1994
1995 if (Status != STATUS_SUCCESS)
1996 {
1997 SetLastNtError(Status);
1998 UserLeave();
1999 return FALSE;
2000 }
2001
2002 if (str != NULL)
2003 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, &SafeStr, uFlags);
2004 else
2005 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, NULL, uFlags);
2006
2007 UserLeave();
2008 return Ret;
2009 }
2010
2011 BOOL
2012 APIENTRY
2013 NtUserDrawCaption(HWND hWnd,
2014 HDC hDC,
2015 LPCRECT lpRc,
2016 UINT uFlags)
2017 {
2018 return NtUserDrawCaptionTemp(hWnd, hDC, lpRc, 0, 0, NULL, uFlags);
2019 }
2020
2021 BOOL
2022 APIENTRY
2023 NtUserInvalidateRect(
2024 HWND hWnd,
2025 CONST RECT *lpUnsafeRect,
2026 BOOL bErase)
2027 {
2028 return NtUserRedrawWindow(hWnd, lpUnsafeRect, NULL, RDW_INVALIDATE | (bErase? RDW_ERASE : 0));
2029 }
2030
2031 BOOL
2032 APIENTRY
2033 NtUserInvalidateRgn(
2034 HWND hWnd,
2035 HRGN hRgn,
2036 BOOL bErase)
2037 {
2038 return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_INVALIDATE | (bErase? RDW_ERASE : 0));
2039 }
2040
2041 /* ValidateRect gets redirected to NtUserValidateRect:
2042 http://blog.csdn.net/ntdll/archive/2005/10/19/509299.aspx */
2043 BOOL
2044 APIENTRY
2045 NtUserValidateRect(
2046 HWND hWnd,
2047 const RECT *lpRect)
2048 {
2049 if (hWnd)
2050 {
2051 return NtUserRedrawWindow(hWnd, lpRect, NULL, RDW_VALIDATE );
2052 }
2053 return NtUserRedrawWindow(hWnd, lpRect, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ERASENOW|RDW_ALLCHILDREN);
2054 }
2055
2056 /* EOF */