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