[WIN32K]
[reactos.git] / reactos / win32ss / user / ntuser / painting.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Window painting function
5 * FILE: subsystems/win32/win32k/ntuser/painting.c
6 * PROGRAMER: Filip Navara (xnavara@volny.cz)
7 */
8
9 #include <win32k.h>
10 DBG_DEFAULT_CHANNEL(UserPainting);
11
12 /* PRIVATE FUNCTIONS **********************************************************/
13
14 /**
15 * @name IntIntersectWithParents
16 *
17 * Intersect window rectangle with all parent client rectangles.
18 *
19 * @param Child
20 * Pointer to child window to start intersecting from.
21 * @param WindowRect
22 * Pointer to rectangle that we want to intersect in screen
23 * coordinates on input and intersected rectangle on output (if TRUE
24 * is returned).
25 *
26 * @return
27 * If any parent is minimized or invisible or the resulting rectangle
28 * is empty then FALSE is returned. Otherwise TRUE is returned.
29 */
30
31 BOOL FASTCALL
32 IntIntersectWithParents(PWND Child, RECTL *WindowRect)
33 {
34 PWND ParentWnd;
35
36 ParentWnd = Child->spwndParent;
37 while (ParentWnd != NULL)
38 {
39 if (!(ParentWnd->style & WS_VISIBLE) ||
40 (ParentWnd->style & WS_MINIMIZE))
41 {
42 return FALSE;
43 }
44
45 if (!RECTL_bIntersectRect(WindowRect, WindowRect, &ParentWnd->rcClient))
46 {
47 return FALSE;
48 }
49
50 /* FIXME: Layered windows. */
51
52 ParentWnd = ParentWnd->spwndParent;
53 }
54
55 return TRUE;
56 }
57
58 BOOL FASTCALL
59 IntValidateParent(PWND Child, PREGION ValidateRgn, BOOL Recurse)
60 {
61 PWND ParentWnd = Child;
62
63 if (ParentWnd->style & WS_CHILD)
64 {
65 do
66 ParentWnd = ParentWnd->spwndParent;
67 while (ParentWnd->style & WS_CHILD);
68 }
69 // Hax out for drawing issues.
70 // if (!(ParentWnd->state & WNDS_SYNCPAINTPENDING)) Recurse = FALSE;
71
72 ParentWnd = Child->spwndParent;
73 while (ParentWnd)
74 {
75 if (ParentWnd->style & WS_CLIPCHILDREN)
76 break;
77
78 if (ParentWnd->hrgnUpdate != 0)
79 {
80 if (Recurse)
81 return FALSE;
82
83 IntInvalidateWindows( ParentWnd,
84 ValidateRgn,
85 RDW_VALIDATE | RDW_NOCHILDREN);
86 }
87
88 ParentWnd = ParentWnd->spwndParent;
89 }
90
91 return TRUE;
92 }
93
94 /*
95 Synchronize painting to the top-level windows of other threads.
96 */
97 VOID FASTCALL
98 IntSendSyncPaint(PWND Wnd, ULONG Flags)
99 {
100 PTHREADINFO ptiCur, ptiWnd;
101 PUSER_SENT_MESSAGE Message;
102 PLIST_ENTRY Entry;
103 BOOL bSend = TRUE;
104
105 ptiWnd = Wnd->head.pti;
106 ptiCur = PsGetCurrentThreadWin32Thread();
107 /*
108 Not the current thread, Wnd is in send Nonclient paint also in send erase background and it is visiable.
109 */
110 if ( Wnd->head.pti != ptiCur &&
111 Wnd->state & WNDS_SENDNCPAINT &&
112 Wnd->state & WNDS_SENDERASEBACKGROUND &&
113 Wnd->style & WS_VISIBLE)
114 {
115 // For testing, if you see this, break out the Champagne and have a party!
116 ERR("SendSyncPaint Wnd in State!\n");
117 if (!IsListEmpty(&ptiWnd->SentMessagesListHead))
118 {
119 // Scan sent queue messages to see if we received sync paint messages.
120 Entry = ptiWnd->SentMessagesListHead.Flink;
121 Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
122 do
123 {
124 ERR("LOOP it\n");
125 if (Message->Msg.message == WM_SYNCPAINT &&
126 Message->Msg.hwnd == UserHMGetHandle(Wnd))
127 { // Already received so exit out.
128 ERR("SendSyncPaint Found one in the Sent Msg Queue!\n");
129 bSend = FALSE;
130 break;
131 }
132 Entry = Message->ListEntry.Flink;
133 Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
134 }
135 while (Entry != &ptiWnd->SentMessagesListHead);
136 }
137 if (bSend)
138 {
139 ERR("Sending WM_SYNCPAINT\n");
140 // This message has no parameters. But it does! Pass Flags along.
141 co_IntSendMessageNoWait(UserHMGetHandle(Wnd), WM_SYNCPAINT, Flags, 0);
142 Wnd->state |= WNDS_SYNCPAINTPENDING;
143 }
144 }
145
146 // Send to all the children if this is the desktop window.
147 if ( Wnd == UserGetDesktopWindow() )
148 {
149 if ( Flags & RDW_ALLCHILDREN ||
150 ( !(Flags & RDW_NOCHILDREN) && Wnd->style & WS_CLIPCHILDREN))
151 {
152 PWND spwndChild = Wnd->spwndChild;
153 while(spwndChild)
154 {
155 if ( spwndChild->style & WS_CHILD &&
156 spwndChild->head.pti != ptiCur)
157 {
158 spwndChild = spwndChild->spwndNext;
159 continue;
160 }
161 IntSendSyncPaint( spwndChild, Flags );
162 spwndChild = spwndChild->spwndNext;
163 }
164 }
165 }
166 }
167
168 /**
169 * @name IntCalcWindowRgn
170 *
171 * Get a window or client region.
172 */
173
174 HRGN FASTCALL
175 IntCalcWindowRgn(PWND Wnd, BOOL Client)
176 {
177 HRGN hRgnWindow;
178
179 if (Client)
180 {
181 hRgnWindow = NtGdiCreateRectRgn(
182 Wnd->rcClient.left,
183 Wnd->rcClient.top,
184 Wnd->rcClient.right,
185 Wnd->rcClient.bottom);
186 }
187 else
188 {
189 hRgnWindow = NtGdiCreateRectRgn(
190 Wnd->rcWindow.left,
191 Wnd->rcWindow.top,
192 Wnd->rcWindow.right,
193 Wnd->rcWindow.bottom);
194 }
195
196 if (Wnd->hrgnClip != NULL && !(Wnd->style & WS_MINIMIZE))
197 {
198 NtGdiOffsetRgn(hRgnWindow,
199 -Wnd->rcWindow.left,
200 -Wnd->rcWindow.top);
201 NtGdiCombineRgn(hRgnWindow, hRgnWindow, Wnd->hrgnClip, RGN_AND);
202 NtGdiOffsetRgn(hRgnWindow,
203 Wnd->rcWindow.left,
204 Wnd->rcWindow.top);
205 }
206
207 return hRgnWindow;
208 }
209
210 /**
211 * @name IntGetNCUpdateRgn
212 *
213 * Get non-client update region of a window and optionally validate it.
214 *
215 * @param Window
216 * Pointer to window to get the NC update region from.
217 * @param Validate
218 * Set to TRUE to force validating the NC update region.
219 *
220 * @return
221 * Handle to NC update region. The caller is responsible for deleting
222 * it.
223 */
224
225 HRGN FASTCALL
226 IntGetNCUpdateRgn(PWND Window, BOOL Validate)
227 {
228 HRGN hRgnNonClient;
229 HRGN hRgnWindow;
230 UINT RgnType;
231
232 if (Window->hrgnUpdate != NULL &&
233 Window->hrgnUpdate != HRGN_WINDOW)
234 {
235 hRgnNonClient = IntCalcWindowRgn(Window, FALSE);
236
237 /*
238 * If region creation fails it's safe to fallback to whole
239 * window region.
240 */
241 if (hRgnNonClient == NULL)
242 {
243 return HRGN_WINDOW;
244 }
245
246 hRgnWindow = IntCalcWindowRgn(Window, TRUE);
247 if (hRgnWindow == NULL)
248 {
249 GreDeleteObject(hRgnNonClient);
250 return HRGN_WINDOW;
251 }
252
253 RgnType = NtGdiCombineRgn(hRgnNonClient, hRgnNonClient,
254 hRgnWindow, RGN_DIFF);
255 if (RgnType == ERROR)
256 {
257 GreDeleteObject(hRgnWindow);
258 GreDeleteObject(hRgnNonClient);
259 return HRGN_WINDOW;
260 }
261 else if (RgnType == NULLREGION)
262 {
263 GreDeleteObject(hRgnWindow);
264 GreDeleteObject(hRgnNonClient);
265 Window->state &= ~WNDS_UPDATEDIRTY;
266 return NULL;
267 }
268
269 /*
270 * Remove the nonclient region from the standard update region if
271 * we were asked for it.
272 */
273
274 if (Validate)
275 {
276 if (NtGdiCombineRgn(Window->hrgnUpdate, Window->hrgnUpdate,
277 hRgnWindow, RGN_AND) == NULLREGION)
278 {
279 IntGdiSetRegionOwner(Window->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
280 GreDeleteObject(Window->hrgnUpdate);
281 Window->state &= ~WNDS_UPDATEDIRTY;
282 Window->hrgnUpdate = NULL;
283 if (!(Window->state & WNDS_INTERNALPAINT))
284 MsqDecPaintCountQueue(Window->head.pti);
285 }
286 }
287
288 GreDeleteObject(hRgnWindow);
289
290 return hRgnNonClient;
291 }
292 else
293 {
294 return Window->hrgnUpdate;
295 }
296 }
297
298 /*
299 * IntPaintWindows
300 *
301 * Internal function used by IntRedrawWindow.
302 */
303
304 VOID FASTCALL
305 co_IntPaintWindows(PWND Wnd, ULONG Flags, BOOL Recurse)
306 {
307 HDC hDC;
308 HWND hWnd = Wnd->head.h;
309 HRGN TempRegion;
310
311 Wnd->state &= ~WNDS_PAINTNOTPROCESSED;
312
313 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
314 {
315 if (Wnd->hrgnUpdate)
316 {
317 PREGION RgnUpdate = REGION_LockRgn(Wnd->hrgnUpdate);
318 if (RgnUpdate)
319 {
320 if (!IntValidateParent(Wnd, RgnUpdate, Recurse))
321 {
322 REGION_UnlockRgn(RgnUpdate);
323 return;
324 }
325 REGION_UnlockRgn(RgnUpdate);
326 }
327 }
328
329 if (Flags & RDW_UPDATENOW)
330 {
331 if ((Wnd->hrgnUpdate != NULL ||
332 Wnd->state & WNDS_INTERNALPAINT))
333 {
334 Wnd->state2 |= WNDS2_WMPAINTSENT;
335 co_IntSendMessage(hWnd, WM_PAINT, 0, 0);
336 }
337 }
338 else if (Wnd->head.pti == PsGetCurrentThreadWin32Thread())
339 {
340 if (Wnd->state & WNDS_SENDNCPAINT)
341 {
342 TempRegion = IntGetNCUpdateRgn(Wnd, TRUE);
343 Wnd->state &= ~WNDS_SENDNCPAINT;
344 if ( Wnd == GetW32ThreadInfo()->MessageQueue->spwndActive &&
345 !(Wnd->state & WNDS_ACTIVEFRAME))
346 {
347 Wnd->state |= WNDS_ACTIVEFRAME;
348 Wnd->state &= ~WNDS_NONCPAINT;
349 }
350 if (TempRegion) co_IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)TempRegion, 0);
351 }
352
353 if (Wnd->state & WNDS_SENDERASEBACKGROUND)
354 {
355 if (Wnd->hrgnUpdate)
356 {
357 hDC = UserGetDCEx( Wnd,
358 Wnd->hrgnUpdate,
359 DCX_CACHE|DCX_USESTYLE|DCX_INTERSECTRGN|DCX_KEEPCLIPRGN);
360
361 Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
362 // Kill the loop, so Clear before we send.
363 if (!co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)hDC, 0))
364 {
365 Wnd->state |= (WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
366 }
367 UserReleaseDC(Wnd, hDC, FALSE);
368 }
369 }
370 }
371 }
372 else
373 {
374 Wnd->state &= ~(WNDS_SENDNCPAINT|WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
375 }
376
377 /*
378 * Check that the window is still valid at this point
379 */
380 if (!IntIsWindow(hWnd))
381 {
382 return;
383 }
384
385 /*
386 * Paint child windows.
387 */
388 if (!(Flags & RDW_NOCHILDREN) &&
389 !(Wnd->style & WS_MINIMIZE) &&
390 ((Flags & RDW_ALLCHILDREN) || !(Wnd->style & WS_CLIPCHILDREN)) )
391 {
392 HWND *List, *phWnd;
393
394 if ((List = IntWinListChildren(Wnd)))
395 {
396 /* FIXME: Handle WS_EX_TRANSPARENT */
397 for (phWnd = List; *phWnd; ++phWnd)
398 {
399 Wnd = UserGetWindowObject(*phWnd);
400 if (Wnd && (Wnd->style & WS_VISIBLE))
401 {
402 USER_REFERENCE_ENTRY Ref;
403 UserRefObjectCo(Wnd, &Ref);
404 co_IntPaintWindows(Wnd, Flags, TRUE);
405 UserDerefObjectCo(Wnd);
406 }
407 }
408 ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
409 }
410 }
411 }
412
413 /*
414 * IntInvalidateWindows
415 *
416 * Internal function used by IntRedrawWindow, UserRedrawDesktop,
417 * co_WinPosSetWindowPos, IntValidateParent, co_UserRedrawWindow.
418 */
419 VOID FASTCALL
420 IntInvalidateWindows(PWND Wnd, PREGION Rgn, ULONG Flags)
421 {
422 INT RgnType = NULLREGION;
423 BOOL HadPaintMessage;
424
425 TRACE("IntInvalidateWindows start\n");
426
427 Wnd->state |= WNDS_PAINTNOTPROCESSED;
428
429 /*
430 * If the nonclient is not to be redrawn, clip the region to the client
431 * rect
432 */
433 if (0 != (Flags & RDW_INVALIDATE) && 0 == (Flags & RDW_FRAME))
434 {
435 PREGION RgnClient;
436
437 RgnClient = IntSysCreateRectpRgnIndirect(&Wnd->rcClient);
438 if (RgnClient)
439 {
440 RgnType = IntGdiCombineRgn(Rgn, Rgn, RgnClient, RGN_AND);
441 REGION_Delete(RgnClient);
442 }
443 }
444
445 /*
446 * Clip the given region with window rectangle (or region)
447 */
448
449 if (!Wnd->hrgnClip || (Wnd->style & WS_MINIMIZE))
450 {
451 PREGION RgnWindow = IntSysCreateRectpRgnIndirect(&Wnd->rcWindow);
452 if (RgnWindow)
453 {
454 RgnType = IntGdiCombineRgn(Rgn, Rgn, RgnWindow, RGN_AND);
455 REGION_Delete(RgnWindow);
456 }
457 }
458 else
459 {
460 PREGION RgnClip = REGION_LockRgn(Wnd->hrgnClip);
461 if (RgnClip)
462 {
463 REGION_bOffsetRgn(Rgn,
464 -Wnd->rcWindow.left,
465 -Wnd->rcWindow.top);
466 RgnType = IntGdiCombineRgn(Rgn, Rgn, RgnClip, RGN_AND);
467 REGION_bOffsetRgn(Rgn,
468 Wnd->rcWindow.left,
469 Wnd->rcWindow.top);
470 REGION_UnlockRgn(RgnClip);
471 }
472 }
473
474 /*
475 * Save current state of pending updates
476 */
477
478 HadPaintMessage = IntIsWindowDirty(Wnd);
479
480 /*
481 * Update the region and flags
482 */
483
484 // The following flags are used to invalidate the window.
485 if (Flags & (RDW_INVALIDATE|RDW_INTERNALPAINT|RDW_ERASE|RDW_FRAME))
486 {
487 if (Flags & RDW_INTERNALPAINT)
488 {
489 Wnd->state |= WNDS_INTERNALPAINT;
490 }
491
492 if (Flags & RDW_INVALIDATE && RgnType != NULLREGION)
493 {
494 PREGION RgnUpdate;
495
496 Wnd->state &= ~WNDS_NONCPAINT;
497
498 /* If not the same thread set it dirty. */
499 if (Wnd->head.pti != PsGetCurrentThreadWin32Thread())
500 {
501 Wnd->state |= WNDS_UPDATEDIRTY;
502 if (Wnd->state2 & WNDS2_WMPAINTSENT)
503 Wnd->state2 |= WNDS2_ENDPAINTINVALIDATE;
504 }
505
506 if (Flags & RDW_FRAME)
507 Wnd->state |= WNDS_SENDNCPAINT;
508 if (Flags & RDW_ERASE)
509 Wnd->state |= WNDS_SENDERASEBACKGROUND;
510
511 if (Wnd->hrgnUpdate == NULL)
512 {
513 Wnd->hrgnUpdate = NtGdiCreateRectRgn(0, 0, 0, 0);
514 IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_PUBLIC);
515 }
516
517 RgnUpdate = REGION_LockRgn(Wnd->hrgnUpdate);
518 if (RgnUpdate)
519 {
520 RgnType = IntGdiCombineRgn(RgnUpdate, RgnUpdate, Rgn, RGN_OR);
521 REGION_UnlockRgn(RgnUpdate);
522 if (RgnType == NULLREGION)
523 {
524 IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
525 GreDeleteObject(Wnd->hrgnUpdate);
526 Wnd->hrgnUpdate = NULL;
527 }
528 }
529 Flags |= RDW_FRAME; // For children.
530 }
531 } // The following flags are used to validate the window.
532 else if (Flags & (RDW_VALIDATE|RDW_NOINTERNALPAINT|RDW_NOERASE|RDW_NOFRAME))
533 {
534 /* FIXME: Handle WNDS_UPDATEDIRTY */
535
536 if (Flags & RDW_NOINTERNALPAINT)
537 {
538 Wnd->state &= ~WNDS_INTERNALPAINT;
539 }
540
541 if (Flags & RDW_VALIDATE && RgnType != NULLREGION)
542 {
543 if (Flags & RDW_NOFRAME)
544 Wnd->state &= ~WNDS_SENDNCPAINT;
545 if (Flags & RDW_NOERASE)
546 Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
547
548 if (Wnd->hrgnUpdate != NULL)
549 {
550 PREGION RgnUpdate = REGION_LockRgn(Wnd->hrgnUpdate);
551
552 if (RgnUpdate)
553 {
554 RgnType = IntGdiCombineRgn(RgnUpdate, RgnUpdate, Rgn, RGN_DIFF);
555 REGION_UnlockRgn(RgnUpdate);
556
557 if(RgnType == NULLREGION)
558 {
559 IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
560 GreDeleteObject(Wnd->hrgnUpdate);
561 Wnd->hrgnUpdate = NULL;
562 }
563 }
564 }
565
566 if (Wnd->hrgnUpdate == NULL)
567 Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
568 }
569 }
570
571 /*
572 * Process children if needed
573 */
574
575 if (!(Flags & RDW_NOCHILDREN) && !(Wnd->style & WS_MINIMIZE) &&
576 ((Flags & RDW_ALLCHILDREN) || !(Wnd->style & WS_CLIPCHILDREN)))
577 {
578 PWND Child;
579
580 for (Child = Wnd->spwndChild; Child; Child = Child->spwndNext)
581 {
582 if (Child->style & WS_VISIBLE)
583 {
584 /*
585 * Recursive call to update children hrgnUpdate
586 */
587 PREGION RgnTemp = IntSysCreateRectpRgn(0, 0, 0, 0);
588 if (RgnTemp)
589 {
590 IntGdiCombineRgn(RgnTemp, Rgn, 0, RGN_COPY);
591 IntInvalidateWindows(Child, RgnTemp, Flags);
592 REGION_Delete(RgnTemp);
593 }
594 }
595 }
596 }
597
598 /*
599 * Fake post paint messages to window message queue if needed
600 */
601
602 if (HadPaintMessage != IntIsWindowDirty(Wnd))
603 {
604 if (HadPaintMessage)
605 MsqDecPaintCountQueue(Wnd->head.pti);
606 else
607 MsqIncPaintCountQueue(Wnd->head.pti);
608 }
609 TRACE("IntInvalidateWindows exit\n");
610 }
611
612 /*
613 * IntIsWindowDrawable
614 *
615 * Remarks
616 * Window is drawable when it is visible and all parents are not
617 * minimized.
618 */
619
620 BOOL FASTCALL
621 IntIsWindowDrawable(PWND Wnd)
622 {
623 PWND WndObject;
624
625 for (WndObject = Wnd; WndObject != NULL; WndObject = WndObject->spwndParent)
626 {
627 if ( WndObject->state2 & WNDS2_INDESTROY ||
628 WndObject->state & WNDS_DESTROYED ||
629 !WndObject ||
630 !(WndObject->style & WS_VISIBLE) ||
631 ((WndObject->style & WS_MINIMIZE) && (WndObject != Wnd)))
632 {
633 return FALSE;
634 }
635 }
636
637 return TRUE;
638 }
639
640 /*
641 * IntRedrawWindow
642 *
643 * Internal version of NtUserRedrawWindow that takes WND as
644 * first parameter.
645 */
646
647 BOOL FASTCALL
648 co_UserRedrawWindow(
649 PWND Window,
650 const RECTL* UpdateRect,
651 PREGION UpdateRgn,
652 ULONG Flags)
653 {
654 PREGION TmpRgn = NULL;
655 TRACE("co_UserRedrawWindow start\n");
656
657 /*
658 * Step 1.
659 * Validation of passed parameters.
660 */
661
662 if (!IntIsWindowDrawable(Window))
663 {
664 return TRUE; // Just do nothing!!!
665 }
666
667 /*
668 * Step 2.
669 * Transform the parameters UpdateRgn and UpdateRect into
670 * a region hRgn specified in screen coordinates.
671 */
672
673 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE)) // Both are OKAY!
674 {
675 if (UpdateRgn)
676 {
677 TmpRgn = IntSysCreateRectpRgn(0, 0, 0, 0);
678 if (IntGdiCombineRgn(TmpRgn, UpdateRgn, NULL, RGN_COPY) == NULLREGION)
679 {
680 REGION_Delete(TmpRgn);
681 TmpRgn = NULL;
682 }
683 else
684 {
685 REGION_bOffsetRgn(TmpRgn, Window->rcClient.left, Window->rcClient.top);
686 }
687 }
688 else if (UpdateRect != NULL)
689 {
690 if (!RECTL_bIsEmptyRect(UpdateRect))
691 {
692 TmpRgn = IntSysCreateRectpRgnIndirect(UpdateRect);
693 REGION_bOffsetRgn(TmpRgn, Window->rcClient.left, Window->rcClient.top);
694 }
695 }
696 else if ((Flags & (RDW_INVALIDATE | RDW_FRAME)) == (RDW_INVALIDATE | RDW_FRAME) ||
697 (Flags & (RDW_VALIDATE | RDW_NOFRAME)) == (RDW_VALIDATE | RDW_NOFRAME))
698 {
699 if (!RECTL_bIsEmptyRect(&Window->rcWindow))
700 TmpRgn = IntSysCreateRectpRgnIndirect(&Window->rcWindow);
701 }
702 else
703 {
704 if (!RECTL_bIsEmptyRect(&Window->rcClient))
705 TmpRgn = IntSysCreateRectpRgnIndirect(&Window->rcClient);
706 }
707 }
708
709 /*
710 * Step 3.
711 * Adjust the window update region depending on hRgn and flags.
712 */
713
714 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT) &&
715 TmpRgn != NULL)
716 {
717 IntInvalidateWindows(Window, TmpRgn, Flags);
718 }
719
720 /*
721 * Step 4.
722 * Repaint and erase windows if needed.
723 */
724
725 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
726 {
727 if (Flags & RDW_ERASENOW) IntSendSyncPaint(Window, Flags);
728 co_IntPaintWindows(Window, Flags, FALSE);
729 }
730
731 /*
732 * Step 5.
733 * Cleanup ;-)
734 */
735
736 if (TmpRgn != NULL)
737 {
738 REGION_Delete(TmpRgn);
739 }
740 TRACE("co_UserRedrawWindow exit\n");
741
742 return TRUE;
743 }
744
745 BOOL FASTCALL
746 IntIsWindowDirty(PWND Wnd)
747 {
748 return ( Wnd->style & WS_VISIBLE &&
749 ( Wnd->hrgnUpdate != NULL ||
750 Wnd->state & WNDS_INTERNALPAINT ) );
751 }
752
753 PWND FASTCALL
754 IntFindWindowToRepaint(PWND Window, PTHREADINFO Thread)
755 {
756 PWND hChild;
757 PWND TempWindow;
758
759 for (; Window != NULL; Window = Window->spwndNext)
760 {
761 if (IntWndBelongsToThread(Window, Thread) &&
762 IntIsWindowDirty(Window))
763 {
764 /* Make sure all non-transparent siblings are already drawn. */
765 if (Window->ExStyle & WS_EX_TRANSPARENT)
766 {
767 for (TempWindow = Window->spwndNext; TempWindow != NULL;
768 TempWindow = TempWindow->spwndNext)
769 {
770 if (!(TempWindow->ExStyle & WS_EX_TRANSPARENT) &&
771 IntWndBelongsToThread(TempWindow, Thread) &&
772 IntIsWindowDirty(TempWindow))
773 {
774 return TempWindow;
775 }
776 }
777 }
778
779 return Window;
780 }
781
782 if (Window->spwndChild)
783 {
784 hChild = IntFindWindowToRepaint(Window->spwndChild, Thread);
785 if (hChild != NULL)
786 return hChild;
787 }
788 }
789 return Window;
790 }
791
792 BOOL FASTCALL
793 IntGetPaintMessage(
794 PWND Window,
795 UINT MsgFilterMin,
796 UINT MsgFilterMax,
797 PTHREADINFO Thread,
798 MSG *Message,
799 BOOL Remove)
800 {
801 PWND PaintWnd;
802
803 if ((MsgFilterMin != 0 || MsgFilterMax != 0) &&
804 (MsgFilterMin > WM_PAINT || MsgFilterMax < WM_PAINT))
805 return FALSE;
806
807 if (Thread->TIF_flags & TIF_SYSTEMTHREAD )
808 {
809 ERR("WM_PAINT is in a System Thread!\n");
810 }
811
812 PaintWnd = IntFindWindowToRepaint(UserGetDesktopWindow(), Thread);
813
814 Message->hwnd = PaintWnd ? UserHMGetHandle(PaintWnd) : NULL;
815
816 if (Message->hwnd == NULL)
817 {
818 ERR("PAINTING BUG: Thread marked as containing dirty windows, but no dirty windows found! Counts %u\n",Thread->cPaintsReady);
819 /* Hack to stop spamming the debuglog ! */
820 Thread->cPaintsReady = 0;
821 return FALSE;
822 }
823
824 if (Window != NULL && PaintWnd != Window)
825 return FALSE;
826
827 if (PaintWnd->state & WNDS_INTERNALPAINT)
828 {
829 PaintWnd->state &= ~WNDS_INTERNALPAINT;
830 if (!PaintWnd->hrgnUpdate)
831 MsqDecPaintCountQueue(Thread);
832 }
833 PaintWnd->state2 &= ~WNDS2_WMPAINTSENT;
834 PaintWnd->state &= ~WNDS_UPDATEDIRTY;
835 Message->wParam = Message->lParam = 0;
836 Message->message = WM_PAINT;
837 return TRUE;
838 }
839
840 static
841 HWND FASTCALL
842 co_IntFixCaret(PWND Window, RECTL *lprc, UINT flags)
843 {
844 PDESKTOP Desktop;
845 PTHRDCARETINFO CaretInfo;
846 PTHREADINFO pti;
847 PUSER_MESSAGE_QUEUE ActiveMessageQueue;
848 HWND hWndCaret;
849 PWND WndCaret;
850
851 ASSERT_REFS_CO(Window);
852
853 pti = PsGetCurrentThreadWin32Thread();
854 Desktop = pti->rpdesk;
855 ActiveMessageQueue = Desktop->ActiveMessageQueue;
856 if (!ActiveMessageQueue) return 0;
857 CaretInfo = ActiveMessageQueue->CaretInfo;
858 hWndCaret = CaretInfo->hWnd;
859
860 WndCaret = ValidateHwndNoErr(hWndCaret);
861
862 // FIXME: Check for WndCaret can be NULL
863 if (WndCaret == Window ||
864 ((flags & SW_SCROLLCHILDREN) && IntIsChildWindow(Window, WndCaret)))
865 {
866 POINT pt, FromOffset, ToOffset;
867 RECTL rcCaret;
868
869 pt.x = CaretInfo->Pos.x;
870 pt.y = CaretInfo->Pos.y;
871 IntGetClientOrigin(WndCaret, &FromOffset);
872 IntGetClientOrigin(Window, &ToOffset);
873 rcCaret.left = pt.x;
874 rcCaret.top = pt.y;
875 rcCaret.right = pt.x + CaretInfo->Size.cx;
876 rcCaret.bottom = pt.y + CaretInfo->Size.cy;
877 if (RECTL_bIntersectRect(lprc, lprc, &rcCaret))
878 {
879 co_UserHideCaret(0);
880 lprc->left = pt.x;
881 lprc->top = pt.y;
882 return hWndCaret;
883 }
884 }
885
886 return 0;
887 }
888
889 BOOL
890 FASTCALL
891 IntPrintWindow(
892 PWND pwnd,
893 HDC hdcBlt,
894 UINT nFlags)
895 {
896 HDC hdcSrc;
897 INT cx, cy, xSrc, ySrc;
898
899 if ( nFlags & PW_CLIENTONLY)
900 {
901 cx = pwnd->rcClient.right - pwnd->rcClient.left;
902 cy = pwnd->rcClient.bottom - pwnd->rcClient.top;
903 xSrc = pwnd->rcClient.left - pwnd->rcWindow.left;
904 ySrc = pwnd->rcClient.top - pwnd->rcWindow.top;
905 }
906 else
907 {
908 cx = pwnd->rcWindow.right - pwnd->rcWindow.left;
909 cy = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
910 xSrc = 0;
911 ySrc = 0;
912 }
913
914 // TODO: Setup Redirection for Print.
915 return FALSE;
916
917 /* Update the window just incase. */
918 co_IntPaintWindows( pwnd, RDW_ERASENOW|RDW_UPDATENOW, FALSE);
919
920 hdcSrc = UserGetDCEx( pwnd, NULL, DCX_CACHE|DCX_WINDOW);
921 /* Print window to printer context. */
922 NtGdiBitBlt( hdcBlt,
923 0,
924 0,
925 cx,
926 cy,
927 hdcSrc,
928 xSrc,
929 ySrc,
930 SRCCOPY,
931 0,
932 0);
933
934 UserReleaseDC( pwnd, hdcSrc, FALSE);
935
936 // TODO: Release Redirection from Print.
937
938 return TRUE;
939 }
940
941 BOOL
942 FASTCALL
943 IntFlashWindowEx(PWND pWnd, PFLASHWINFO pfwi)
944 {
945 DWORD FlashState;
946 UINT uCount = pfwi->uCount;
947 BOOL Activate = FALSE, Ret = FALSE;
948
949 ASSERT(pfwi);
950
951 FlashState = (DWORD)UserGetProp(pWnd, AtomFlashWndState);
952
953 if (FlashState == FLASHW_FINISHED)
954 {
955 // Cycle has finished, kill timer and set this to Stop.
956 FlashState |= FLASHW_KILLSYSTIMER;
957 pfwi->dwFlags = FLASHW_STOP;
958 }
959 else
960 {
961 if (FlashState)
962 {
963 if (pfwi->dwFlags == FLASHW_SYSTIMER)
964 {
965 // Called from system timer, restore flags, counts and state.
966 pfwi->dwFlags = LOWORD(FlashState);
967 uCount = HIWORD(FlashState);
968 FlashState = MAKELONG(LOWORD(FlashState),0);
969 }
970 else
971 {
972 // Clean out the trash! Fix SeaMonkey crash after restart.
973 FlashState = 0;
974 }
975 }
976
977 if (FlashState == 0)
978 { // First time in cycle, setup flash state.
979 if ( pWnd->state & WNDS_ACTIVEFRAME ||
980 (pfwi->dwFlags & FLASHW_CAPTION && pWnd->style & (WS_BORDER|WS_DLGFRAME)))
981 {
982 FlashState = FLASHW_STARTED|FLASHW_ACTIVE;
983 }
984 }
985
986 // Set previous window state.
987 Ret = !!(FlashState & FLASHW_ACTIVE);
988
989 if ( pfwi->dwFlags & FLASHW_TIMERNOFG &&
990 gpqForeground == pWnd->head.pti->MessageQueue )
991 {
992 // Flashing until foreground, set this to Stop.
993 pfwi->dwFlags = FLASHW_STOP;
994 }
995 }
996
997 // Toggle activate flag.
998 if ( pfwi->dwFlags == FLASHW_STOP )
999 {
1000 if (gpqForeground && gpqForeground->spwndActive == pWnd)
1001 Activate = TRUE;
1002 else
1003 Activate = FALSE;
1004 }
1005 else
1006 {
1007 Activate = (FlashState & FLASHW_ACTIVE) == 0;
1008 }
1009
1010 if ( pfwi->dwFlags == FLASHW_STOP || pfwi->dwFlags & FLASHW_CAPTION )
1011 {
1012 co_IntSendMessage(UserHMGetHandle(pWnd), WM_NCACTIVATE, Activate, 0);
1013 }
1014
1015 // FIXME: Check for a Stop Sign here.
1016 if ( pfwi->dwFlags & FLASHW_TRAY )
1017 {
1018 // Need some shell work here too.
1019 TRACE("FIXME: Flash window no Tray support!\n");
1020 }
1021
1022 if ( pfwi->dwFlags == FLASHW_STOP )
1023 {
1024 if (FlashState & FLASHW_KILLSYSTIMER)
1025 {
1026 IntKillTimer(pWnd, ID_EVENT_SYSTIMER_FLASHWIN, TRUE);
1027 }
1028
1029 IntRemoveProp(pWnd, AtomFlashWndState);
1030 }
1031 else
1032 { // Have a count and started, set timer.
1033 if ( uCount )
1034 {
1035 FlashState |= FLASHW_COUNT;
1036
1037 if (!(Activate ^ !!(FlashState & FLASHW_STARTED)))
1038 uCount--;
1039
1040 if (!(FlashState & FLASHW_KILLSYSTIMER))
1041 pfwi->dwFlags |= FLASHW_TIMER;
1042 }
1043
1044 if (pfwi->dwFlags & FLASHW_TIMER)
1045 {
1046 FlashState |= FLASHW_KILLSYSTIMER;
1047
1048 IntSetTimer( pWnd,
1049 ID_EVENT_SYSTIMER_FLASHWIN,
1050 pfwi->dwTimeout ? pfwi->dwTimeout : gpsi->dtCaretBlink,
1051 SystemTimerProc,
1052 TMRF_SYSTEM );
1053 }
1054
1055 if (FlashState & FLASHW_COUNT && uCount == 0)
1056 {
1057 // Keep spinning? Nothing else to do.
1058 FlashState = FLASHW_FINISHED;
1059 }
1060 else
1061 {
1062 // Save state and flags so this can be restored next time through.
1063 FlashState ^= (FlashState ^ -!!(Activate)) & FLASHW_ACTIVE;
1064 FlashState ^= (FlashState ^ pfwi->dwFlags) & (FLASHW_MASK & ~FLASHW_TIMER);
1065 }
1066 FlashState = MAKELONG(LOWORD(FlashState),uCount);
1067 IntSetProp(pWnd, AtomFlashWndState, (HANDLE) FlashState);
1068 }
1069 return Ret;
1070 }
1071
1072 HDC FASTCALL
1073 IntBeginPaint(PWND Window, PPAINTSTRUCT Ps)
1074 {
1075 co_UserHideCaret(Window);
1076
1077 Window->state2 |= WNDS2_STARTPAINT;
1078 Window->state &= ~WNDS_PAINTNOTPROCESSED;
1079
1080 if (Window->state & WNDS_SENDNCPAINT)
1081 {
1082 HRGN hRgn;
1083
1084 Window->state &= ~WNDS_UPDATEDIRTY;
1085 hRgn = IntGetNCUpdateRgn(Window, FALSE);
1086 Window->state &= ~WNDS_SENDNCPAINT;
1087 co_IntSendMessage(UserHMGetHandle(Window), WM_NCPAINT, (WPARAM)hRgn, 0);
1088 if (hRgn != HRGN_WINDOW && hRgn != NULL && GreIsHandleValid(hRgn))
1089 {
1090 /* NOTE: The region can already be deleted! */
1091 GreDeleteObject(hRgn);
1092 }
1093 }
1094 else
1095 {
1096 Window->state &= ~WNDS_UPDATEDIRTY;
1097 }
1098
1099 RtlZeroMemory(Ps, sizeof(PAINTSTRUCT));
1100
1101 Ps->hdc = UserGetDCEx( Window,
1102 Window->hrgnUpdate,
1103 DCX_INTERSECTRGN | DCX_USESTYLE);
1104 if (!Ps->hdc)
1105 {
1106 return NULL;
1107 }
1108
1109 if (Window->hrgnUpdate != NULL)
1110 {
1111 MsqDecPaintCountQueue(Window->head.pti);
1112 GdiGetClipBox(Ps->hdc, &Ps->rcPaint);
1113 IntGdiSetRegionOwner(Window->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
1114 /* The region is part of the dc now and belongs to the process! */
1115 Window->hrgnUpdate = NULL;
1116 }
1117 else
1118 {
1119 if (Window->state & WNDS_INTERNALPAINT)
1120 MsqDecPaintCountQueue(Window->head.pti);
1121
1122 IntGetClientRect(Window, &Ps->rcPaint);
1123 }
1124
1125 Window->state &= ~WNDS_INTERNALPAINT;
1126
1127 if (Window->state & WNDS_SENDERASEBACKGROUND)
1128 {
1129 Window->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
1130 Ps->fErase = !co_IntSendMessage(UserHMGetHandle(Window), WM_ERASEBKGND, (WPARAM)Ps->hdc, 0);
1131 if ( Ps->fErase )
1132 {
1133 Window->state |= (WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
1134 }
1135 }
1136 else
1137 {
1138 Ps->fErase = FALSE;
1139 }
1140 if (Window->hrgnUpdate)
1141 {
1142 if (!(Window->style & WS_CLIPCHILDREN))
1143 {
1144 PWND Child;
1145 for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
1146 {
1147 if (Child->hrgnUpdate == NULL && Child->state & WNDS_SENDNCPAINT) // Helped fixing test_redrawnow.
1148 IntInvalidateWindows(Child, NULL, RDW_FRAME | RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN);
1149 }
1150 }
1151 }
1152 return Ps->hdc;
1153 }
1154
1155 BOOL FASTCALL
1156 IntEndPaint(PWND Wnd, PPAINTSTRUCT Ps)
1157 {
1158 HDC hdc = NULL;
1159
1160 hdc = Ps->hdc;
1161
1162 UserReleaseDC(Wnd, hdc, TRUE);
1163
1164 Wnd->state2 &= ~(WNDS2_WMPAINTSENT|WNDS2_STARTPAINT);
1165
1166 co_UserShowCaret(Wnd);
1167
1168 return TRUE;
1169 }
1170
1171 /* PUBLIC FUNCTIONS ***********************************************************/
1172
1173 /*
1174 * NtUserBeginPaint
1175 *
1176 * Status
1177 * @implemented
1178 */
1179
1180 HDC APIENTRY
1181 NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* UnsafePs)
1182 {
1183 PWND Window = NULL;
1184 PAINTSTRUCT Ps;
1185 NTSTATUS Status;
1186 HDC hDC;
1187 USER_REFERENCE_ENTRY Ref;
1188 DECLARE_RETURN(HDC);
1189
1190 TRACE("Enter NtUserBeginPaint\n");
1191 UserEnterExclusive();
1192
1193 if (!(Window = UserGetWindowObject(hWnd)))
1194 {
1195 RETURN( NULL);
1196 }
1197
1198 UserRefObjectCo(Window, &Ref);
1199
1200 hDC = IntBeginPaint(Window, &Ps);
1201
1202 Status = MmCopyToCaller(UnsafePs, &Ps, sizeof(PAINTSTRUCT));
1203 if (! NT_SUCCESS(Status))
1204 {
1205 SetLastNtError(Status);
1206 RETURN(NULL);
1207 }
1208
1209 RETURN(hDC);
1210
1211 CLEANUP:
1212 if (Window) UserDerefObjectCo(Window);
1213
1214 TRACE("Leave NtUserBeginPaint, ret=%p\n",_ret_);
1215 UserLeave();
1216 END_CLEANUP;
1217
1218 }
1219
1220 /*
1221 * NtUserEndPaint
1222 *
1223 * Status
1224 * @implemented
1225 */
1226
1227 BOOL APIENTRY
1228 NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* pUnsafePs)
1229 {
1230 NTSTATUS Status = STATUS_SUCCESS;
1231 PWND Window = NULL;
1232 PAINTSTRUCT Ps;
1233 USER_REFERENCE_ENTRY Ref;
1234 DECLARE_RETURN(BOOL);
1235
1236 TRACE("Enter NtUserEndPaint\n");
1237 UserEnterExclusive();
1238
1239 if (!(Window = UserGetWindowObject(hWnd)))
1240 {
1241 RETURN(FALSE);
1242 }
1243
1244 UserRefObjectCo(Window, &Ref); // Here for the exception.
1245
1246 _SEH2_TRY
1247 {
1248 ProbeForRead(pUnsafePs, sizeof(*pUnsafePs), 1);
1249 RtlCopyMemory(&Ps, pUnsafePs, sizeof(PAINTSTRUCT));
1250 }
1251 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1252 {
1253 Status = _SEH2_GetExceptionCode();
1254 }
1255 _SEH2_END
1256 if (!NT_SUCCESS(Status))
1257 {
1258 RETURN(FALSE);
1259 }
1260
1261 RETURN(IntEndPaint(Window, &Ps));
1262
1263 CLEANUP:
1264 if (Window) UserDerefObjectCo(Window);
1265
1266 TRACE("Leave NtUserEndPaint, ret=%i\n",_ret_);
1267 UserLeave();
1268 END_CLEANUP;
1269 }
1270
1271 /*
1272 * @implemented
1273 */
1274 BOOL APIENTRY
1275 NtUserFlashWindowEx(IN PFLASHWINFO pfwi)
1276 {
1277 PWND pWnd;
1278 FLASHWINFO finfo = {0};
1279 BOOL Ret = TRUE;
1280
1281 UserEnterExclusive();
1282
1283 _SEH2_TRY
1284 {
1285 ProbeForRead(pfwi, sizeof(FLASHWINFO), sizeof(ULONG));
1286 RtlCopyMemory(&finfo, pfwi, sizeof(FLASHWINFO));
1287 }
1288 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1289 {
1290 SetLastNtError(_SEH2_GetExceptionCode());
1291 Ret = FALSE;
1292 }
1293 _SEH2_END
1294
1295 if (!Ret) goto Exit;
1296
1297 if (!( pWnd = (PWND)UserGetObject(gHandleTable, finfo.hwnd, TYPE_WINDOW)) ||
1298 finfo.cbSize != sizeof(FLASHWINFO) ||
1299 finfo.dwFlags & ~(FLASHW_ALL|FLASHW_TIMER|FLASHW_TIMERNOFG) )
1300 {
1301 EngSetLastError(ERROR_INVALID_PARAMETER);
1302 Ret = FALSE;
1303 goto Exit;
1304 }
1305
1306 Ret = IntFlashWindowEx(pWnd, &finfo);
1307
1308 Exit:
1309 UserLeave();
1310 return Ret;
1311 }
1312
1313 INT FASTCALL
1314 co_UserGetUpdateRgn(PWND Window, PREGION Rgn, BOOL bErase)
1315 {
1316 int RegionType;
1317 RECTL Rect;
1318 PREGION UpdateRgn;
1319
1320 ASSERT_REFS_CO(Window);
1321
1322 Window->state &= ~WNDS_UPDATEDIRTY;
1323
1324 if (Window->hrgnUpdate == NULL)
1325 {
1326 REGION_SetRectRgn(Rgn, 0, 0, 0, 0);
1327 return NULLREGION;
1328 }
1329
1330 UpdateRgn = REGION_LockRgn(Window->hrgnUpdate);
1331 if (!UpdateRgn)
1332 return ERROR;
1333
1334 Rect = Window->rcClient;
1335 IntIntersectWithParents(Window, &Rect);
1336 REGION_SetRectRgn(Rgn, Rect.left, Rect.top, Rect.right, Rect.bottom);
1337 RegionType = IntGdiCombineRgn(Rgn, Rgn, UpdateRgn, RGN_AND);
1338 REGION_bOffsetRgn(Rgn, -Window->rcClient.left, -Window->rcClient.top);
1339 REGION_UnlockRgn(UpdateRgn);
1340
1341 if (bErase && RegionType != NULLREGION && RegionType != ERROR)
1342 {
1343 co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
1344 }
1345
1346 return RegionType;
1347 }
1348
1349 /*
1350 * NtUserGetUpdateRgn
1351 *
1352 * Status
1353 * @implemented
1354 */
1355
1356 INT APIENTRY
1357 NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
1358 {
1359 DECLARE_RETURN(INT);
1360 PWND Window;
1361 INT ret;
1362 USER_REFERENCE_ENTRY Ref;
1363 PREGION Rgn = NULL;
1364
1365 TRACE("Enter NtUserGetUpdateRgn\n");
1366 UserEnterExclusive();
1367
1368 if (!(Window = UserGetWindowObject(hWnd)))
1369 {
1370 RETURN(ERROR);
1371 }
1372
1373 /* Use a system region, we can't hold GDI locks when doing roundtrips to user mode */
1374 Rgn = IntSysCreateRectpRgn(0, 0, 0, 0);
1375 if (!Rgn)
1376 RETURN(ERROR);
1377
1378 UserRefObjectCo(Window, &Ref);
1379 ret = co_UserGetUpdateRgn(Window, Rgn, bErase);
1380 UserDerefObjectCo(Window);
1381
1382 RETURN(ret);
1383
1384 CLEANUP:
1385 if (Rgn && (_ret_ != ERROR))
1386 {
1387 PREGION TheRgn = REGION_LockRgn(hRgn);
1388 if (!TheRgn)
1389 {
1390 EngSetLastError(ERROR_INVALID_HANDLE);
1391 _ret_ = ERROR;
1392 }
1393 else
1394 {
1395 IntGdiCombineRgn(TheRgn, Rgn, NULL, RGN_COPY);
1396 REGION_UnlockRgn(TheRgn);
1397 }
1398 }
1399
1400 if (Rgn)
1401 REGION_Delete(Rgn);
1402
1403 TRACE("Leave NtUserGetUpdateRgn, ret=%i\n",_ret_);
1404 UserLeave();
1405 END_CLEANUP;
1406 }
1407
1408 /*
1409 * NtUserGetUpdateRect
1410 *
1411 * Status
1412 * @implemented
1413 */
1414
1415 BOOL APIENTRY
1416 NtUserGetUpdateRect(HWND hWnd, LPRECT UnsafeRect, BOOL bErase)
1417 {
1418 PWND Window;
1419 RECTL Rect;
1420 INT RegionType;
1421 PREGION RgnData;
1422 NTSTATUS Status;
1423 DECLARE_RETURN(BOOL);
1424
1425 TRACE("Enter NtUserGetUpdateRect\n");
1426 UserEnterExclusive();
1427
1428 if (!(Window = UserGetWindowObject(hWnd)))
1429 {
1430 RETURN(FALSE);
1431 }
1432
1433 Window->state &= ~WNDS_UPDATEDIRTY;
1434
1435 if (Window->hrgnUpdate == NULL)
1436 {
1437 Rect.left = Rect.top = Rect.right = Rect.bottom = 0;
1438 }
1439 else
1440 {
1441 /* Get the update region bounding box. */
1442 if (Window->hrgnUpdate == HRGN_WINDOW)
1443 {
1444 Rect = Window->rcClient;
1445 }
1446 else
1447 {
1448 RgnData = REGION_LockRgn(Window->hrgnUpdate);
1449 ASSERT(RgnData != NULL);
1450 RegionType = REGION_GetRgnBox(RgnData, &Rect);
1451 REGION_UnlockRgn(RgnData);
1452
1453 if (RegionType != ERROR && RegionType != NULLREGION)
1454 RECTL_bIntersectRect(&Rect, &Rect, &Window->rcClient);
1455 }
1456
1457 if (IntIntersectWithParents(Window, &Rect))
1458 {
1459 RECTL_vOffsetRect(&Rect,
1460 -Window->rcClient.left,
1461 -Window->rcClient.top);
1462 } else
1463 {
1464 Rect.left = Rect.top = Rect.right = Rect.bottom = 0;
1465 }
1466 }
1467
1468 if (bErase && !RECTL_bIsEmptyRect(&Rect))
1469 {
1470 USER_REFERENCE_ENTRY Ref;
1471 UserRefObjectCo(Window, &Ref);
1472 co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
1473 UserDerefObjectCo(Window);
1474 }
1475
1476 if (UnsafeRect != NULL)
1477 {
1478 Status = MmCopyToCaller(UnsafeRect, &Rect, sizeof(RECTL));
1479 if (!NT_SUCCESS(Status))
1480 {
1481 EngSetLastError(ERROR_INVALID_PARAMETER);
1482 RETURN(FALSE);
1483 }
1484 }
1485
1486 RETURN(!RECTL_bIsEmptyRect(&Rect));
1487
1488 CLEANUP:
1489 TRACE("Leave NtUserGetUpdateRect, ret=%i\n",_ret_);
1490 UserLeave();
1491 END_CLEANUP;
1492 }
1493
1494 /*
1495 * NtUserRedrawWindow
1496 *
1497 * Status
1498 * @implemented
1499 */
1500
1501 BOOL APIENTRY
1502 NtUserRedrawWindow(
1503 HWND hWnd,
1504 CONST RECT *lprcUpdate,
1505 HRGN hrgnUpdate,
1506 UINT flags)
1507 {
1508 RECTL SafeUpdateRect;
1509 PWND Wnd;
1510 BOOL Ret;
1511 USER_REFERENCE_ENTRY Ref;
1512 NTSTATUS Status = STATUS_SUCCESS;
1513 PREGION RgnUpdate = NULL;
1514 DECLARE_RETURN(BOOL);
1515
1516 TRACE("Enter NtUserRedrawWindow\n");
1517 UserEnterExclusive();
1518
1519 if (!(Wnd = UserGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow())))
1520 {
1521 RETURN( FALSE);
1522 }
1523
1524 if (lprcUpdate)
1525 {
1526 _SEH2_TRY
1527 {
1528 ProbeForRead(lprcUpdate, sizeof(RECTL), 1);
1529 RtlCopyMemory(&SafeUpdateRect, lprcUpdate, sizeof(RECTL));
1530 }
1531 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1532 {
1533 Status = _SEH2_GetExceptionCode();
1534 }
1535 _SEH2_END
1536 if (!NT_SUCCESS(Status))
1537 {
1538 EngSetLastError(RtlNtStatusToDosError(Status));
1539 RETURN( FALSE);
1540 }
1541 }
1542
1543 if ( flags & ~(RDW_ERASE|RDW_FRAME|RDW_INTERNALPAINT|RDW_INVALIDATE|
1544 RDW_NOERASE|RDW_NOFRAME|RDW_NOINTERNALPAINT|RDW_VALIDATE|
1545 RDW_ERASENOW|RDW_UPDATENOW|RDW_ALLCHILDREN|RDW_NOCHILDREN) )
1546 {
1547 /* RedrawWindow fails only in case that flags are invalid */
1548 EngSetLastError(ERROR_INVALID_FLAGS);
1549 RETURN( FALSE);
1550 }
1551
1552 /* We can't hold lock on GDI obects while doing roundtrips to user mode,
1553 * so use a copy instead */
1554 if (hrgnUpdate)
1555 {
1556 PREGION RgnTemp;
1557
1558 RgnUpdate = IntSysCreateRectpRgn(0, 0, 0, 0);
1559 if (!RgnUpdate)
1560 {
1561 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1562 RETURN(FALSE);
1563 }
1564
1565 RgnTemp = REGION_LockRgn(hrgnUpdate);
1566 if (!RgnTemp)
1567 {
1568 EngSetLastError(ERROR_INVALID_HANDLE);
1569 RETURN(FALSE);
1570 }
1571 IntGdiCombineRgn(RgnUpdate, RgnTemp, NULL, RGN_COPY);
1572 REGION_UnlockRgn(RgnTemp);
1573 }
1574
1575 UserRefObjectCo(Wnd, &Ref);
1576
1577 Ret = co_UserRedrawWindow( Wnd,
1578 lprcUpdate ? &SafeUpdateRect : NULL,
1579 RgnUpdate,
1580 flags);
1581
1582 UserDerefObjectCo(Wnd);
1583
1584 RETURN( Ret);
1585
1586 CLEANUP:
1587 if (RgnUpdate)
1588 REGION_Delete(RgnUpdate);
1589 TRACE("Leave NtUserRedrawWindow, ret=%i\n",_ret_);
1590 UserLeave();
1591 END_CLEANUP;
1592 }
1593
1594 static
1595 INT FASTCALL
1596 UserScrollDC(
1597 HDC hDC,
1598 INT dx,
1599 INT dy,
1600 const RECTL *prcScroll,
1601 const RECTL *prcClip,
1602 HRGN hrgnUpdate,
1603 PREGION RgnUpdate,
1604 RECTL *prcUpdate)
1605 {
1606 PDC pDC;
1607 RECTL rcScroll, rcClip, rcSrc, rcDst;
1608 INT Result;
1609
1610 if (GdiGetClipBox(hDC, &rcClip) == ERROR)
1611 {
1612 ERR("GdiGetClipBox failed for HDC %p\n", hDC);
1613 return ERROR;
1614 }
1615
1616 rcScroll = rcClip;
1617 if (prcClip)
1618 {
1619 RECTL_bIntersectRect(&rcClip, &rcClip, prcClip);
1620 }
1621
1622 if (prcScroll)
1623 {
1624 rcScroll = *prcScroll;
1625 RECTL_bIntersectRect(&rcSrc, &rcClip, prcScroll);
1626 }
1627 else
1628 {
1629 rcSrc = rcClip;
1630 }
1631
1632 rcDst = rcSrc;
1633 RECTL_vOffsetRect(&rcDst, dx, dy);
1634 RECTL_bIntersectRect(&rcDst, &rcDst, &rcClip);
1635
1636 if (!NtGdiBitBlt( hDC,
1637 rcDst.left,
1638 rcDst.top,
1639 rcDst.right - rcDst.left,
1640 rcDst.bottom - rcDst.top,
1641 hDC,
1642 rcDst.left - dx,
1643 rcDst.top - dy,
1644 SRCCOPY,
1645 0,
1646 0))
1647 {
1648 return ERROR;
1649 }
1650
1651 /* Calculate the region that was invalidated by moving or
1652 could not be copied, because it was not visible */
1653 if (RgnUpdate || hrgnUpdate || prcUpdate)
1654 {
1655 PREGION RgnOwn, RgnTmp;
1656
1657 pDC = DC_LockDc(hDC);
1658 if (!pDC)
1659 {
1660 return ERROR;
1661 }
1662
1663 if (hrgnUpdate)
1664 {
1665 NT_ASSERT(RgnUpdate == NULL);
1666 RgnUpdate = REGION_LockRgn(hrgnUpdate);
1667 if (!RgnUpdate)
1668 {
1669 DC_UnlockDc(pDC);
1670 return ERROR;
1671 }
1672 }
1673
1674 /* Begin with the shifted and then clipped scroll rect */
1675 rcDst = rcScroll;
1676 RECTL_vOffsetRect(&rcDst, dx, dy);
1677 RECTL_bIntersectRect(&rcDst, &rcDst, &rcClip);
1678 if (RgnUpdate)
1679 {
1680 RgnOwn = RgnUpdate;
1681 REGION_SetRectRgn(RgnOwn, rcDst.left, rcDst.top, rcDst.right, rcDst.bottom);
1682 }
1683 else
1684 {
1685 RgnOwn = IntSysCreateRectpRgnIndirect(&rcDst);
1686 }
1687
1688 /* Add the source rect */
1689 RgnTmp = IntSysCreateRectpRgnIndirect(&rcSrc);
1690 IntGdiCombineRgn(RgnOwn, RgnOwn, RgnTmp, RGN_OR);
1691
1692 /* Substract the part of the dest that was visible in source */
1693 IntGdiCombineRgn(RgnTmp, RgnTmp, pDC->prgnVis, RGN_AND);
1694 REGION_bOffsetRgn(RgnTmp, dx, dy);
1695 Result = IntGdiCombineRgn(RgnOwn, RgnOwn, RgnTmp, RGN_DIFF);
1696
1697 /* DO NOT Unlock DC while messing with prgnVis! */
1698 DC_UnlockDc(pDC);
1699
1700 REGION_Delete(RgnTmp);
1701
1702 if (prcUpdate)
1703 {
1704 REGION_GetRgnBox(RgnOwn, prcUpdate);
1705 }
1706
1707 if (hrgnUpdate)
1708 {
1709 REGION_UnlockRgn(RgnUpdate);
1710 }
1711 else if (!RgnUpdate)
1712 {
1713 REGION_Delete(RgnOwn);
1714 }
1715 }
1716 else
1717 Result = NULLREGION;
1718
1719 return Result;
1720 }
1721
1722 /*
1723 * NtUserScrollDC
1724 *
1725 * Status
1726 * @implemented
1727 */
1728 BOOL APIENTRY
1729 NtUserScrollDC(
1730 HDC hDC,
1731 INT dx,
1732 INT dy,
1733 const RECT *prcUnsafeScroll,
1734 const RECT *prcUnsafeClip,
1735 HRGN hrgnUpdate,
1736 LPRECT prcUnsafeUpdate)
1737 {
1738 DECLARE_RETURN(DWORD);
1739 RECTL rcScroll, rcClip, rcUpdate;
1740 NTSTATUS Status = STATUS_SUCCESS;
1741 DWORD Result;
1742
1743 TRACE("Enter NtUserScrollDC\n");
1744 UserEnterExclusive();
1745
1746 _SEH2_TRY
1747 {
1748 if (prcUnsafeScroll)
1749 {
1750 ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
1751 rcScroll = *prcUnsafeScroll;
1752 }
1753 if (prcUnsafeClip)
1754 {
1755 ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
1756 rcClip = *prcUnsafeClip;
1757 }
1758 if (prcUnsafeUpdate)
1759 {
1760 ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
1761 }
1762 }
1763 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1764 {
1765 Status = _SEH2_GetExceptionCode();
1766 }
1767 _SEH2_END
1768 if (!NT_SUCCESS(Status))
1769 {
1770 SetLastNtError(Status);
1771 RETURN(FALSE);
1772 }
1773
1774 Result = UserScrollDC( hDC,
1775 dx,
1776 dy,
1777 prcUnsafeScroll? &rcScroll : 0,
1778 prcUnsafeClip? &rcClip : 0,
1779 hrgnUpdate,
1780 NULL,
1781 prcUnsafeUpdate? &rcUpdate : NULL);
1782 if(Result == ERROR)
1783 {
1784 /* FIXME: Only if hRgnUpdate is invalid we should SetLastError(ERROR_INVALID_HANDLE) */
1785 RETURN(FALSE);
1786 }
1787
1788 if (prcUnsafeUpdate)
1789 {
1790 _SEH2_TRY
1791 {
1792 *prcUnsafeUpdate = rcUpdate;
1793 }
1794 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1795 {
1796 Status = _SEH2_GetExceptionCode();
1797 }
1798 _SEH2_END
1799 if (!NT_SUCCESS(Status))
1800 {
1801 /* FIXME: SetLastError? */
1802 /* FIXME: correct? We have already scrolled! */
1803 RETURN(FALSE);
1804 }
1805 }
1806
1807 RETURN(TRUE);
1808
1809 CLEANUP:
1810 TRACE("Leave NtUserScrollDC, ret=%lu\n",_ret_);
1811 UserLeave();
1812 END_CLEANUP;
1813 }
1814
1815 /*
1816 * NtUserScrollWindowEx
1817 *
1818 * Status
1819 * @implemented
1820 */
1821
1822 DWORD APIENTRY
1823 NtUserScrollWindowEx(
1824 HWND hWnd,
1825 INT dx,
1826 INT dy,
1827 const RECT *prcUnsafeScroll,
1828 const RECT *prcUnsafeClip,
1829 HRGN hrgnUpdate,
1830 LPRECT prcUnsafeUpdate,
1831 UINT flags)
1832 {
1833 RECTL rcScroll, rcClip, rcCaret, rcUpdate;
1834 INT Result;
1835 PWND Window = NULL, CaretWnd;
1836 HDC hDC;
1837 PREGION RgnUpdate = NULL, RgnTemp, RgnWinupd = NULL;
1838 HWND hwndCaret;
1839 DWORD dcxflags = 0;
1840 int rdw_flags;
1841 NTSTATUS Status = STATUS_SUCCESS;
1842 DECLARE_RETURN(DWORD);
1843 USER_REFERENCE_ENTRY Ref, CaretRef;
1844
1845 TRACE("Enter NtUserScrollWindowEx\n");
1846 UserEnterExclusive();
1847
1848 Window = UserGetWindowObject(hWnd);
1849 if (!Window || !IntIsWindowDrawable(Window))
1850 {
1851 Window = NULL; /* prevent deref at cleanup */
1852 RETURN( ERROR);
1853 }
1854 UserRefObjectCo(Window, &Ref);
1855
1856 IntGetClientRect(Window, &rcClip);
1857
1858 _SEH2_TRY
1859 {
1860 if (prcUnsafeScroll)
1861 {
1862 ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
1863 RECTL_bIntersectRect(&rcScroll, &rcClip, prcUnsafeScroll);
1864 }
1865 else
1866 rcScroll = rcClip;
1867
1868 if (prcUnsafeClip)
1869 {
1870 ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
1871 RECTL_bIntersectRect(&rcClip, &rcClip, prcUnsafeClip);
1872 }
1873 }
1874 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1875 {
1876 Status = _SEH2_GetExceptionCode();
1877 }
1878 _SEH2_END
1879
1880 if (!NT_SUCCESS(Status))
1881 {
1882 SetLastNtError(Status);
1883 RETURN(ERROR);
1884 }
1885
1886 if (rcClip.right <= rcClip.left || rcClip.bottom <= rcClip.top ||
1887 (dx == 0 && dy == 0))
1888 {
1889 RETURN(NULLREGION);
1890 }
1891
1892 /* We must use a copy of the region, as we can't hold an exclusive lock
1893 * on it while doing callouts to user-mode */
1894 RgnUpdate = IntSysCreateRectpRgn(0, 0, 0, 0);
1895 if(!RgnUpdate)
1896 {
1897 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1898 RETURN(ERROR);
1899 }
1900
1901 if (hrgnUpdate)
1902 {
1903 RgnTemp = REGION_LockRgn(hrgnUpdate);
1904 if (!RgnTemp)
1905 {
1906 EngSetLastError(ERROR_INVALID_HANDLE);
1907 RETURN(ERROR);
1908 }
1909 IntGdiCombineRgn(RgnUpdate, RgnTemp, NULL, RGN_COPY);
1910 REGION_UnlockRgn(RgnTemp);
1911 }
1912
1913 /* ScrollWindow uses the window DC, ScrollWindowEx doesn't */
1914 if (flags & SW_SCROLLWNDDCE)
1915 {
1916 dcxflags = DCX_USESTYLE;
1917
1918 if (!(Window->pcls->style & (CS_OWNDC|CS_CLASSDC)))
1919 dcxflags |= DCX_CACHE; // AH??? wine~ If not Powned or with Class go Cheap!
1920
1921 if (flags & SW_SCROLLCHILDREN && Window->style & WS_CLIPCHILDREN)
1922 dcxflags |= DCX_CACHE|DCX_NOCLIPCHILDREN;
1923 }
1924 else
1925 {
1926 /* So in this case ScrollWindowEx uses Cache DC. */
1927 dcxflags = DCX_CACHE|DCX_USESTYLE;
1928 if (flags & SW_SCROLLCHILDREN) dcxflags |= DCX_NOCLIPCHILDREN;
1929 }
1930
1931 hDC = UserGetDCEx(Window, 0, dcxflags);
1932 if (!hDC)
1933 {
1934 /* FIXME: SetLastError? */
1935 RETURN(ERROR);
1936 }
1937
1938 rdw_flags = (flags & SW_ERASE) && (flags & SW_INVALIDATE) ? RDW_INVALIDATE | RDW_ERASE : RDW_INVALIDATE ;
1939
1940 rcCaret = rcScroll;
1941 hwndCaret = co_IntFixCaret(Window, &rcCaret, flags);
1942
1943 Result = UserScrollDC( hDC,
1944 dx,
1945 dy,
1946 &rcScroll,
1947 &rcClip,
1948 NULL,
1949 RgnUpdate,
1950 prcUnsafeUpdate? &rcUpdate : NULL);
1951
1952 UserReleaseDC(Window, hDC, FALSE);
1953
1954 /*
1955 * Take into account the fact that some damage may have occurred during
1956 * the scroll. Keep a copy in hrgnWinupd to be added to hrngUpdate at the end.
1957 */
1958
1959 RgnTemp = IntSysCreateRectpRgn(0, 0, 0, 0);
1960 if (!RgnTemp)
1961 {
1962 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1963 RETURN(ERROR);
1964 }
1965
1966 if (co_UserGetUpdateRgn(Window, RgnTemp, FALSE) != NULLREGION)
1967 {
1968 PREGION RgnClip = IntSysCreateRectpRgnIndirect(&rcClip);
1969 if (RgnClip)
1970 {
1971 if (hrgnUpdate)
1972 {
1973 RgnWinupd = IntSysCreateRectpRgn( 0, 0, 0, 0);
1974 IntGdiCombineRgn( RgnWinupd, RgnTemp, 0, RGN_COPY);
1975 }
1976 REGION_bOffsetRgn(RgnTemp, dx, dy);
1977 IntGdiCombineRgn(RgnTemp, RgnTemp, RgnClip, RGN_AND);
1978 if (hrgnUpdate)
1979 IntGdiCombineRgn( RgnWinupd, RgnWinupd, RgnTemp, RGN_OR );
1980 co_UserRedrawWindow(Window, NULL, RgnTemp, rdw_flags );
1981 REGION_Delete(RgnClip);
1982 }
1983 }
1984 REGION_Delete(RgnTemp);
1985
1986 if (flags & SW_SCROLLCHILDREN)
1987 {
1988 PWND Child;
1989 RECTL rcChild;
1990 POINT ClientOrigin;
1991 USER_REFERENCE_ENTRY WndRef;
1992 RECTL rcDummy;
1993
1994 IntGetClientOrigin(Window, &ClientOrigin);
1995 for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
1996 {
1997 rcChild = Child->rcWindow;
1998 rcChild.left -= ClientOrigin.x;
1999 rcChild.top -= ClientOrigin.y;
2000 rcChild.right -= ClientOrigin.x;
2001 rcChild.bottom -= ClientOrigin.y;
2002
2003 if (! prcUnsafeScroll || RECTL_bIntersectRect(&rcDummy, &rcChild, &rcScroll))
2004 {
2005 UserRefObjectCo(Child, &WndRef);
2006 co_WinPosSetWindowPos(Child, 0, rcChild.left + dx, rcChild.top + dy, 0, 0,
2007 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
2008 SWP_NOREDRAW | SWP_DEFERERASE);
2009 UserDerefObjectCo(Child);
2010 }
2011 }
2012 }
2013
2014 if (flags & (SW_INVALIDATE | SW_ERASE))
2015 {
2016 co_UserRedrawWindow(Window, NULL, RgnUpdate, rdw_flags |
2017 ((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
2018 ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0));
2019 }
2020
2021 if (hwndCaret && (CaretWnd = UserGetWindowObject(hwndCaret)))
2022 {
2023 UserRefObjectCo(CaretWnd, &CaretRef);
2024
2025 co_IntSetCaretPos(rcCaret.left + dx, rcCaret.top + dy);
2026 co_UserShowCaret(CaretWnd);
2027
2028 UserDerefObjectCo(CaretWnd);
2029 }
2030
2031 if (prcUnsafeUpdate)
2032 {
2033 _SEH2_TRY
2034 {
2035 /* Probe here, to not fail on invalid pointer before scrolling */
2036 ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
2037 *prcUnsafeUpdate = rcUpdate;
2038 }
2039 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2040 {
2041 Status = _SEH2_GetExceptionCode();
2042 }
2043 _SEH2_END
2044
2045 if (!NT_SUCCESS(Status))
2046 {
2047 SetLastNtError(Status);
2048 RETURN(ERROR);
2049 }
2050 }
2051
2052 RETURN(Result);
2053
2054 CLEANUP:
2055 if (hrgnUpdate && (_ret_ != ERROR))
2056 {
2057 /* Give everything back to the caller */
2058 RgnTemp = REGION_LockRgn(hrgnUpdate);
2059 /* The handle should still be valid */
2060 ASSERT(RgnTemp);
2061 if (RgnWinupd)
2062 IntGdiCombineRgn(RgnTemp, RgnUpdate, RgnWinupd, RGN_OR);
2063 else
2064 IntGdiCombineRgn(RgnTemp, RgnUpdate, NULL, RGN_COPY);
2065 REGION_UnlockRgn(RgnTemp);
2066 }
2067
2068 if (RgnWinupd)
2069 {
2070 REGION_Delete(RgnWinupd);
2071 }
2072
2073 if (RgnUpdate)
2074 {
2075 REGION_Delete(RgnUpdate);
2076 }
2077
2078 if (Window)
2079 UserDerefObjectCo(Window);
2080
2081 TRACE("Leave NtUserScrollWindowEx, ret=%lu\n",_ret_);
2082 UserLeave();
2083 END_CLEANUP;
2084 }
2085
2086 BOOL
2087 UserDrawCaptionText(
2088 HDC hDc,
2089 const PUNICODE_STRING Text,
2090 const RECTL *lpRc,
2091 UINT uFlags,
2092 HFONT hFont)
2093 {
2094 HFONT hOldFont = NULL;
2095 COLORREF OldTextColor;
2096 NONCLIENTMETRICSW nclm;
2097 NTSTATUS Status;
2098 BOOLEAN bDeleteFont = FALSE;
2099 SIZE Size;
2100
2101 TRACE("UserDrawCaptionText: %wZ\n", Text);
2102
2103 nclm.cbSize = sizeof(nclm);
2104 if(!UserSystemParametersInfo(SPI_GETNONCLIENTMETRICS,
2105 sizeof(NONCLIENTMETRICS), &nclm, 0))
2106 {
2107 ERR("UserSystemParametersInfo() failed!\n");
2108 return FALSE;
2109 }
2110
2111 if (!hFont)
2112 {
2113 if(uFlags & DC_SMALLCAP)
2114 Status = TextIntCreateFontIndirect(&nclm.lfSmCaptionFont, &hFont);
2115 else
2116 Status = TextIntCreateFontIndirect(&nclm.lfCaptionFont, &hFont);
2117
2118 if(!NT_SUCCESS(Status))
2119 {
2120 ERR("TextIntCreateFontIndirect() failed! Status: 0x%x\n", Status);
2121 return FALSE;
2122 }
2123
2124 bDeleteFont = TRUE;
2125 }
2126
2127 IntGdiSetBkMode(hDc, TRANSPARENT);
2128
2129 hOldFont = NtGdiSelectFont(hDc, hFont);
2130 if(!hOldFont)
2131 {
2132 ERR("SelectFont() failed!\n");
2133 /* Don't fail */
2134 }
2135
2136 if(uFlags & DC_INBUTTON)
2137 OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(COLOR_BTNTEXT));
2138 else
2139 OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(uFlags & DC_ACTIVE
2140 ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT));
2141
2142 // FIXME: If string doesn't fit to rc, truncate it and add ellipsis.
2143 GreGetTextExtentW(hDc, Text->Buffer, Text->Length/sizeof(WCHAR), &Size, 0);
2144 GreExtTextOutW(hDc,
2145 lpRc->left, (lpRc->top + lpRc->bottom)/2 - Size.cy/2,
2146 0, NULL, Text->Buffer, Text->Length/sizeof(WCHAR), NULL, 0);
2147
2148 IntGdiSetTextColor(hDc, OldTextColor);
2149 if (hOldFont)
2150 NtGdiSelectFont(hDc, hOldFont);
2151 if (bDeleteFont)
2152 GreDeleteObject(hFont);
2153
2154 return TRUE;
2155 }
2156
2157 BOOL UserDrawCaption(
2158 PWND pWnd,
2159 HDC hDc,
2160 RECTL *lpRc,
2161 HFONT hFont,
2162 HICON hIcon,
2163 const PUNICODE_STRING Str,
2164 UINT uFlags)
2165 {
2166 BOOL Ret = FALSE;
2167 HBRUSH hBgBrush, hOldBrush = NULL;
2168 RECTL Rect = *lpRc;
2169 BOOL HasIcon;
2170
2171 RECTL_vMakeWellOrdered(lpRc);
2172
2173 if (!hIcon && pWnd != NULL)
2174 {
2175 HasIcon = (uFlags & DC_ICON) && (pWnd->style & WS_SYSMENU)
2176 && !(uFlags & DC_SMALLCAP) && !(pWnd->ExStyle & WS_EX_DLGMODALFRAME)
2177 && !(pWnd->ExStyle & WS_EX_TOOLWINDOW);
2178 }
2179 else
2180 HasIcon = (hIcon != 0);
2181
2182 // Draw the caption background
2183 if((uFlags & DC_GRADIENT) && !(uFlags & DC_INBUTTON))
2184 {
2185 static GRADIENT_RECT gcap = {0, 1};
2186 TRIVERTEX Vertices[2];
2187 COLORREF Colors[2];
2188
2189 Colors[0] = IntGetSysColor((uFlags & DC_ACTIVE) ?
2190 COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION);
2191
2192 Colors[1] = IntGetSysColor((uFlags & DC_ACTIVE) ?
2193 COLOR_GRADIENTACTIVECAPTION : COLOR_GRADIENTINACTIVECAPTION);
2194
2195 Vertices[0].x = Rect.left;
2196 Vertices[0].y = Rect.top;
2197 Vertices[0].Red = (WORD)Colors[0]<<8;
2198 Vertices[0].Green = (WORD)Colors[0] & 0xFF00;
2199 Vertices[0].Blue = (WORD)(Colors[0]>>8) & 0xFF00;
2200 Vertices[0].Alpha = 0;
2201
2202 Vertices[1].x = Rect.right;
2203 Vertices[1].y = Rect.bottom;
2204 Vertices[1].Red = (WORD)Colors[1]<<8;
2205 Vertices[1].Green = (WORD)Colors[1] & 0xFF00;
2206 Vertices[1].Blue = (WORD)(Colors[1]>>8) & 0xFF00;
2207 Vertices[1].Alpha = 0;
2208
2209 if(!GreGradientFill(hDc, Vertices, 2, &gcap, 1, GRADIENT_FILL_RECT_H))
2210 {
2211 ERR("GreGradientFill() failed!\n");
2212 goto cleanup;
2213 }
2214 }
2215 else
2216 {
2217 if(uFlags & DC_INBUTTON)
2218 hBgBrush = IntGetSysColorBrush(COLOR_3DFACE);
2219 else if(uFlags & DC_ACTIVE)
2220 hBgBrush = IntGetSysColorBrush(COLOR_ACTIVECAPTION);
2221 else
2222 hBgBrush = IntGetSysColorBrush(COLOR_INACTIVECAPTION);
2223
2224 hOldBrush = NtGdiSelectBrush(hDc, hBgBrush);
2225
2226 if(!hOldBrush)
2227 {
2228 ERR("NtGdiSelectBrush() failed!\n");
2229 goto cleanup;
2230 }
2231
2232 if(!NtGdiPatBlt(hDc, Rect.left, Rect.top,
2233 Rect.right - Rect.left,
2234 Rect.bottom - Rect.top,
2235 PATCOPY))
2236 {
2237 ERR("NtGdiPatBlt() failed!\n");
2238 goto cleanup;
2239 }
2240 }
2241
2242 /* Draw icon */
2243 if (HasIcon)
2244 {
2245 PCURICON_OBJECT pIcon = NULL;
2246
2247 if (hIcon)
2248 {
2249 pIcon = UserGetCurIconObject(hIcon);
2250 }
2251 else if (pWnd)
2252 {
2253 pIcon = NC_IconForWindow(pWnd);
2254 // FIXME: NC_IconForWindow should reference it for us */
2255 if (pIcon)
2256 UserReferenceObject(pIcon);
2257 }
2258
2259 if (pIcon)
2260 {
2261 LONG cx = UserGetSystemMetrics(SM_CXSMICON);
2262 LONG cy = UserGetSystemMetrics(SM_CYSMICON);
2263 LONG x = Rect.left - cx/2 + 1 + (Rect.bottom - Rect.top)/2; // this is really what Window does
2264 LONG y = (Rect.top + Rect.bottom)/2 - cy/2; // center
2265 UserDrawIconEx(hDc, x, y, pIcon, cx, cy, 0, NULL, DI_NORMAL);
2266 UserDereferenceObject(pIcon);
2267 }
2268 else
2269 {
2270 HasIcon = FALSE;
2271 }
2272 }
2273
2274 if (HasIcon)
2275 Rect.left += Rect.bottom - Rect.top;
2276
2277 if((uFlags & DC_TEXT))
2278 {
2279 Rect.left += 2;
2280
2281 if (Str)
2282 UserDrawCaptionText(hDc, Str, &Rect, uFlags, hFont);
2283 else if (pWnd != NULL) // FIXME: Windows does not do that
2284 {
2285 UNICODE_STRING ustr;
2286 ustr.Buffer = pWnd->strName.Buffer; // FIXME: LARGE_STRING truncated!
2287 ustr.Length = (USHORT)min(pWnd->strName.Length, MAXUSHORT);
2288 ustr.MaximumLength = (USHORT)min(pWnd->strName.MaximumLength, MAXUSHORT);
2289 UserDrawCaptionText(hDc, &ustr, &Rect, uFlags, hFont);
2290 }
2291 }
2292
2293 Ret = TRUE;
2294
2295 cleanup:
2296 if (hOldBrush) NtGdiSelectBrush(hDc, hOldBrush);
2297
2298 return Ret;
2299 }
2300
2301 INT
2302 FASTCALL
2303 UserRealizePalette(HDC hdc)
2304 {
2305 HWND hWnd, hWndDesktop;
2306 DWORD Ret;
2307
2308 Ret = IntGdiRealizePalette(hdc);
2309 if (Ret) // There was a change.
2310 {
2311 hWnd = IntWindowFromDC(hdc);
2312 if (hWnd) // Send broadcast if dc is associated with a window.
2313 { // FYI: Thread locked in CallOneParam.
2314 hWndDesktop = IntGetDesktopWindow();
2315 if ( hWndDesktop != hWnd )
2316 {
2317 PWND pWnd = UserGetWindowObject(hWndDesktop);
2318 ERR("RealizePalette Desktop.");
2319 hdc = UserGetWindowDC(pWnd);
2320 IntPaintDesktop(hdc);
2321 UserReleaseDC(pWnd,hdc,FALSE);
2322 }
2323 UserSendNotifyMessage((HWND)HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)hWnd, 0);
2324 }
2325 }
2326 return Ret;
2327 }
2328
2329 BOOL
2330 APIENTRY
2331 NtUserDrawCaptionTemp(
2332 HWND hWnd,
2333 HDC hDC,
2334 LPCRECT lpRc,
2335 HFONT hFont,
2336 HICON hIcon,
2337 const PUNICODE_STRING str,
2338 UINT uFlags)
2339 {
2340 PWND pWnd = NULL;
2341 UNICODE_STRING SafeStr = {0};
2342 NTSTATUS Status = STATUS_SUCCESS;
2343 RECTL SafeRect;
2344 BOOL Ret;
2345
2346 UserEnterExclusive();
2347
2348 if (hWnd != NULL)
2349 {
2350 if(!(pWnd = UserGetWindowObject(hWnd)))
2351 {
2352 UserLeave();
2353 return FALSE;
2354 }
2355 }
2356
2357 _SEH2_TRY
2358 {
2359 ProbeForRead(lpRc, sizeof(RECTL), sizeof(ULONG));
2360 RtlCopyMemory(&SafeRect, lpRc, sizeof(RECTL));
2361 if (str != NULL)
2362 {
2363 SafeStr = ProbeForReadUnicodeString(str);
2364 if (SafeStr.Length != 0)
2365 {
2366 ProbeForRead( SafeStr.Buffer,
2367 SafeStr.Length,
2368 sizeof(WCHAR));
2369 }
2370 }
2371 }
2372 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2373 {
2374 Status = _SEH2_GetExceptionCode();
2375 }
2376 _SEH2_END;
2377
2378 if (Status != STATUS_SUCCESS)
2379 {
2380 SetLastNtError(Status);
2381 UserLeave();
2382 return FALSE;
2383 }
2384
2385 if (str != NULL)
2386 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, &SafeStr, uFlags);
2387 else
2388 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, NULL, uFlags);
2389
2390 UserLeave();
2391 return Ret;
2392 }
2393
2394 BOOL
2395 APIENTRY
2396 NtUserDrawCaption(HWND hWnd,
2397 HDC hDC,
2398 LPCRECT lpRc,
2399 UINT uFlags)
2400 {
2401 return NtUserDrawCaptionTemp(hWnd, hDC, lpRc, 0, 0, NULL, uFlags);
2402 }
2403
2404 BOOL
2405 APIENTRY
2406 NtUserInvalidateRect(
2407 HWND hWnd,
2408 CONST RECT *lpUnsafeRect,
2409 BOOL bErase)
2410 {
2411 UINT flags = RDW_INVALIDATE | (bErase ? RDW_ERASE : 0);
2412 if (!hWnd)
2413 {
2414 flags = RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW;
2415 lpUnsafeRect = NULL;
2416 }
2417 return NtUserRedrawWindow(hWnd, lpUnsafeRect, NULL, flags);
2418 }
2419
2420 BOOL
2421 APIENTRY
2422 NtUserInvalidateRgn(
2423 HWND hWnd,
2424 HRGN hRgn,
2425 BOOL bErase)
2426 {
2427 if (!hWnd)
2428 {
2429 EngSetLastError( ERROR_INVALID_WINDOW_HANDLE );
2430 return FALSE;
2431 }
2432 return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_INVALIDATE | (bErase? RDW_ERASE : 0));
2433 }
2434
2435 BOOL
2436 APIENTRY
2437 NtUserPrintWindow(
2438 HWND hwnd,
2439 HDC hdcBlt,
2440 UINT nFlags)
2441 {
2442 PWND Window;
2443 BOOL Ret = FALSE;
2444
2445 UserEnterExclusive();
2446
2447 if (hwnd)
2448 {
2449 if (!(Window = UserGetWindowObject(hwnd)) || // FIXME:
2450 Window == UserGetDesktopWindow() || // pWnd->fnid == FNID_DESKTOP
2451 Window == UserGetMessageWindow() ) // pWnd->fnid == FNID_MESSAGEWND
2452 {
2453 goto Exit;
2454 }
2455
2456 if ( Window )
2457 {
2458 /* Validate flags and check it as a mask for 0 or 1. */
2459 if ( (nFlags & PW_CLIENTONLY) == nFlags)
2460 Ret = IntPrintWindow( Window, hdcBlt, nFlags);
2461 else
2462 EngSetLastError(ERROR_INVALID_PARAMETER);
2463 }
2464 }
2465 Exit:
2466 UserLeave();
2467 return Ret;
2468 }
2469
2470 /* ValidateRect gets redirected to NtUserValidateRect:
2471 http://blog.csdn.net/ntdll/archive/2005/10/19/509299.aspx */
2472 BOOL
2473 APIENTRY
2474 NtUserValidateRect(
2475 HWND hWnd,
2476 const RECT *lpRect)
2477 {
2478 UINT flags = RDW_VALIDATE;
2479 if (!hWnd)
2480 {
2481 flags = RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW;
2482 lpRect = NULL;
2483 }
2484 return NtUserRedrawWindow(hWnd, lpRect, NULL, flags);
2485 }
2486
2487 /* EOF */