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