4e124f36e56929284ea0e529d85585c68cc3057c
[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 VOID FASTCALL
1008 PaintSuspendedWindow(PWND pwnd, HRGN hrgnOrig)
1009 {
1010 if (pwnd->hrgnUpdate)
1011 {
1012 HDC hDC;
1013 INT Flags = DC_NC|DC_NOSENDMSG;
1014 HRGN hrgnTemp;
1015 RECT Rect;
1016 INT type;
1017 PREGION prgn;
1018
1019 if (pwnd->hrgnUpdate > HRGN_WINDOW)
1020 {
1021 hrgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
1022 type = NtGdiCombineRgn( hrgnTemp, pwnd->hrgnUpdate, 0, RGN_COPY);
1023 if (type == ERROR)
1024 {
1025 GreDeleteObject(hrgnTemp);
1026 hrgnTemp = HRGN_WINDOW;
1027 }
1028 }
1029 else
1030 {
1031 hrgnTemp = GreCreateRectRgnIndirect(&pwnd->rcWindow);
1032 }
1033
1034 if ( hrgnOrig &&
1035 hrgnTemp > HRGN_WINDOW &&
1036 NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnOrig, RGN_AND) == NULLREGION)
1037 {
1038 GreDeleteObject(hrgnTemp);
1039 return;
1040 }
1041
1042 hDC = UserGetDCEx(pwnd, hrgnTemp, DCX_WINDOW|DCX_INTERSECTRGN|DCX_USESTYLE|DCX_KEEPCLIPRGN);
1043
1044 Rect = pwnd->rcWindow;
1045 RECTL_vOffsetRect(&Rect, -pwnd->rcWindow.left, -pwnd->rcWindow.top);
1046
1047 // Clear out client area!
1048 FillRect(hDC, &Rect, IntGetSysColorBrush(COLOR_WINDOW));
1049
1050 NC_DoNCPaint(pwnd, hDC, Flags); // Redraw without MENUs.
1051
1052 UserReleaseDC(pwnd, hDC, FALSE);
1053
1054 prgn = REGION_LockRgn(hrgnTemp);
1055 IntInvalidateWindows(pwnd, prgn, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN);
1056 REGION_UnlockRgn(prgn);
1057
1058 // Set updates for this window.
1059 pwnd->state |= WNDS_SENDNCPAINT|WNDS_SENDERASEBACKGROUND|WNDS_UPDATEDIRTY;
1060
1061 // DCX_KEEPCLIPRGN is set. Check it anyway.
1062 if (hrgnTemp > HRGN_WINDOW && GreIsHandleValid(hrgnTemp)) GreDeleteObject(hrgnTemp);
1063 }
1064 }
1065
1066 VOID FASTCALL
1067 UpdateTheadChildren(PWND pWnd, HRGN hRgn)
1068 {
1069 PaintSuspendedWindow( pWnd, hRgn );
1070
1071 if (!(pWnd->style & WS_CLIPCHILDREN))
1072 return;
1073
1074 pWnd = pWnd->spwndChild; // invalidate children if any.
1075 while (pWnd)
1076 {
1077 UpdateTheadChildren( pWnd, hRgn );
1078 pWnd = pWnd->spwndNext;
1079 }
1080 }
1081
1082 VOID FASTCALL
1083 UpdateThreadWindows(PWND pWnd, PTHREADINFO pti, HRGN hRgn)
1084 {
1085 PWND pwndTemp;
1086
1087 for ( pwndTemp = pWnd;
1088 pwndTemp;
1089 pwndTemp = pwndTemp->spwndNext )
1090 {
1091 if (pwndTemp->head.pti == pti)
1092 {
1093 UserUpdateWindows(pwndTemp, RDW_ALLCHILDREN);
1094 }
1095 else
1096 {
1097 if (IsThreadSuspended(pwndTemp->head.pti))
1098 {
1099 UpdateTheadChildren(pwndTemp, hRgn);
1100 }
1101 else
1102 UserUpdateWindows(pwndTemp, RDW_ALLCHILDREN);
1103 }
1104 }
1105 }
1106
1107 BOOL FASTCALL
1108 IntIsWindowDirty(PWND Wnd)
1109 {
1110 return ( Wnd->style & WS_VISIBLE &&
1111 ( Wnd->hrgnUpdate != NULL ||
1112 Wnd->state & WNDS_INTERNALPAINT ) );
1113 }
1114
1115 /*
1116 Conditions to paint any window:
1117
1118 1. Update region is not null.
1119 2. Internal paint flag is set.
1120 3. Paint count is not zero.
1121
1122 */
1123 PWND FASTCALL
1124 IntFindWindowToRepaint(PWND Window, PTHREADINFO Thread)
1125 {
1126 PWND hChild;
1127 PWND TempWindow;
1128
1129 for (; Window != NULL; Window = Window->spwndNext)
1130 {
1131 if (IntWndBelongsToThread(Window, Thread))
1132 {
1133 if (IntIsWindowDirty(Window))
1134 {
1135 /* Make sure all non-transparent siblings are already drawn. */
1136 if (Window->ExStyle & WS_EX_TRANSPARENT)
1137 {
1138 for (TempWindow = Window->spwndNext; TempWindow != NULL;
1139 TempWindow = TempWindow->spwndNext)
1140 {
1141 if (!(TempWindow->ExStyle & WS_EX_TRANSPARENT) &&
1142 IntWndBelongsToThread(TempWindow, Thread) &&
1143 IntIsWindowDirty(TempWindow))
1144 {
1145 return TempWindow;
1146 }
1147 }
1148 }
1149 return Window;
1150 }
1151 }
1152 /* find a child of the specified window that needs repainting */
1153 if (Window->spwndChild)
1154 {
1155 hChild = IntFindWindowToRepaint(Window->spwndChild, Thread);
1156 if (hChild != NULL)
1157 return hChild;
1158 }
1159 }
1160 return Window;
1161 }
1162
1163 BOOL FASTCALL
1164 IntGetPaintMessage(
1165 PWND Window,
1166 UINT MsgFilterMin,
1167 UINT MsgFilterMax,
1168 PTHREADINFO Thread,
1169 MSG *Message,
1170 BOOL Remove)
1171 {
1172 PWND PaintWnd, StartWnd;
1173
1174 if ((MsgFilterMin != 0 || MsgFilterMax != 0) &&
1175 (MsgFilterMin > WM_PAINT || MsgFilterMax < WM_PAINT))
1176 return FALSE;
1177
1178 if (Thread->TIF_flags & TIF_SYSTEMTHREAD )
1179 {
1180 ERR("WM_PAINT is in a System Thread!\n");
1181 }
1182
1183 StartWnd = UserGetDesktopWindow();
1184 PaintWnd = IntFindWindowToRepaint(StartWnd, Thread);
1185
1186 Message->hwnd = PaintWnd ? UserHMGetHandle(PaintWnd) : NULL;
1187
1188 if (Message->hwnd == NULL && Thread->cPaintsReady)
1189 {
1190 // Find note in window.c:"PAINTING BUG".
1191 ERR("WARNING SOMETHING HAS GONE WRONG: Thread marked as containing dirty windows, but no dirty windows found! Counts %u\n",Thread->cPaintsReady);
1192 /* Hack to stop spamming the debug log ! */
1193 Thread->cPaintsReady = 0;
1194 return FALSE;
1195 }
1196
1197 if (Message->hwnd == NULL)
1198 return FALSE;
1199
1200 if (!(Window == NULL ||
1201 PaintWnd == Window ||
1202 IntIsChildWindow(Window, PaintWnd))) /* check that it is a child of the specified parent */
1203 return FALSE;
1204
1205 if (PaintWnd->state & WNDS_INTERNALPAINT)
1206 {
1207 PaintWnd->state &= ~WNDS_INTERNALPAINT;
1208 if (!PaintWnd->hrgnUpdate)
1209 MsqDecPaintCountQueue(Thread);
1210 }
1211 PaintWnd->state2 &= ~WNDS2_STARTPAINT;
1212 PaintWnd->state &= ~WNDS_UPDATEDIRTY;
1213
1214 Window = PaintWnd;
1215 while( Window && Window != UserGetDesktopWindow())
1216 {
1217 // Role back and check for clip children, do not set if any.
1218 if (Window->spwndParent && !(Window->spwndParent->style & WS_CLIPCHILDREN))
1219 {
1220 PaintWnd->state2 |= WNDS2_WMPAINTSENT;
1221 }
1222 Window = Window->spwndParent;
1223 }
1224
1225 Message->wParam = Message->lParam = 0;
1226 Message->message = WM_PAINT;
1227 return TRUE;
1228 }
1229
1230 BOOL
1231 FASTCALL
1232 IntPrintWindow(
1233 PWND pwnd,
1234 HDC hdcBlt,
1235 UINT nFlags)
1236 {
1237 HDC hdcSrc;
1238 INT cx, cy, xSrc, ySrc;
1239
1240 if ( nFlags & PW_CLIENTONLY)
1241 {
1242 cx = pwnd->rcClient.right - pwnd->rcClient.left;
1243 cy = pwnd->rcClient.bottom - pwnd->rcClient.top;
1244 xSrc = pwnd->rcClient.left - pwnd->rcWindow.left;
1245 ySrc = pwnd->rcClient.top - pwnd->rcWindow.top;
1246 }
1247 else
1248 {
1249 cx = pwnd->rcWindow.right - pwnd->rcWindow.left;
1250 cy = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
1251 xSrc = 0;
1252 ySrc = 0;
1253 }
1254
1255 // TODO: Setup Redirection for Print.
1256 return FALSE;
1257
1258 /* Update the window just incase. */
1259 co_IntUpdateWindows( pwnd, RDW_ALLCHILDREN, FALSE);
1260
1261 hdcSrc = UserGetDCEx( pwnd, NULL, DCX_CACHE|DCX_WINDOW);
1262 /* Print window to printer context. */
1263 NtGdiBitBlt( hdcBlt,
1264 0,
1265 0,
1266 cx,
1267 cy,
1268 hdcSrc,
1269 xSrc,
1270 ySrc,
1271 SRCCOPY,
1272 0,
1273 0);
1274
1275 UserReleaseDC( pwnd, hdcSrc, FALSE);
1276
1277 // TODO: Release Redirection from Print.
1278
1279 return TRUE;
1280 }
1281
1282 BOOL
1283 FASTCALL
1284 IntFlashWindowEx(PWND pWnd, PFLASHWINFO pfwi)
1285 {
1286 DWORD FlashState;
1287 UINT uCount = pfwi->uCount;
1288 BOOL Activate = FALSE, Ret = FALSE;
1289
1290 ASSERT(pfwi);
1291
1292 FlashState = (DWORD)UserGetProp(pWnd, AtomFlashWndState, TRUE);
1293
1294 if (FlashState == FLASHW_FINISHED)
1295 {
1296 // Cycle has finished, kill timer and set this to Stop.
1297 FlashState |= FLASHW_KILLSYSTIMER;
1298 pfwi->dwFlags = FLASHW_STOP;
1299 }
1300 else
1301 {
1302 if (FlashState)
1303 {
1304 if (pfwi->dwFlags == FLASHW_SYSTIMER)
1305 {
1306 // Called from system timer, restore flags, counts and state.
1307 pfwi->dwFlags = LOWORD(FlashState);
1308 uCount = HIWORD(FlashState);
1309 FlashState = MAKELONG(LOWORD(FlashState),0);
1310 }
1311 else
1312 {
1313 // Clean out the trash! Fix SeaMonkey crash after restart.
1314 FlashState = 0;
1315 }
1316 }
1317
1318 if (FlashState == 0)
1319 { // First time in cycle, setup flash state.
1320 if ( pWnd->state & WNDS_ACTIVEFRAME ||
1321 (pfwi->dwFlags & FLASHW_CAPTION && pWnd->style & (WS_BORDER|WS_DLGFRAME)))
1322 {
1323 FlashState = FLASHW_STARTED|FLASHW_ACTIVE;
1324 }
1325 }
1326
1327 // Set previous window state.
1328 Ret = !!(FlashState & FLASHW_ACTIVE);
1329
1330 if ( (pfwi->dwFlags & FLASHW_TIMERNOFG) == FLASHW_TIMERNOFG &&
1331 gpqForeground == pWnd->head.pti->MessageQueue )
1332 {
1333 // Flashing until foreground, set this to Stop.
1334 pfwi->dwFlags = FLASHW_STOP;
1335 }
1336 }
1337
1338 // Toggle activate flag.
1339 if ( pfwi->dwFlags == FLASHW_STOP )
1340 {
1341 if (gpqForeground && gpqForeground->spwndActive == pWnd)
1342 Activate = TRUE;
1343 else
1344 Activate = FALSE;
1345 }
1346 else
1347 {
1348 Activate = (FlashState & FLASHW_ACTIVE) == 0;
1349 }
1350
1351 if ( pfwi->dwFlags == FLASHW_STOP || pfwi->dwFlags & FLASHW_CAPTION )
1352 {
1353 co_IntSendMessage(UserHMGetHandle(pWnd), WM_NCACTIVATE, Activate, 0);
1354 }
1355
1356 // FIXME: Check for a Stop Sign here.
1357 if ( pfwi->dwFlags & FLASHW_TRAY )
1358 {
1359 // Need some shell work here too.
1360 TRACE("FIXME: Flash window no Tray support!\n");
1361 }
1362
1363 if ( pfwi->dwFlags == FLASHW_STOP )
1364 {
1365 if (FlashState & FLASHW_KILLSYSTIMER)
1366 {
1367 IntKillTimer(pWnd, ID_EVENT_SYSTIMER_FLASHWIN, TRUE);
1368 }
1369
1370 UserRemoveProp(pWnd, AtomFlashWndState, TRUE);
1371 }
1372 else
1373 { // Have a count and started, set timer.
1374 if ( uCount )
1375 {
1376 FlashState |= FLASHW_COUNT;
1377
1378 if (!(Activate ^ !!(FlashState & FLASHW_STARTED)))
1379 uCount--;
1380
1381 if (!(FlashState & FLASHW_KILLSYSTIMER))
1382 pfwi->dwFlags |= FLASHW_TIMER;
1383 }
1384
1385 if (pfwi->dwFlags & FLASHW_TIMER)
1386 {
1387 FlashState |= FLASHW_KILLSYSTIMER;
1388
1389 IntSetTimer( pWnd,
1390 ID_EVENT_SYSTIMER_FLASHWIN,
1391 pfwi->dwTimeout ? pfwi->dwTimeout : gpsi->dtCaretBlink,
1392 SystemTimerProc,
1393 TMRF_SYSTEM );
1394 }
1395
1396 if (FlashState & FLASHW_COUNT && uCount == 0)
1397 {
1398 // Keep spinning? Nothing else to do.
1399 FlashState = FLASHW_FINISHED;
1400 }
1401 else
1402 {
1403 // Save state and flags so this can be restored next time through.
1404 FlashState ^= (FlashState ^ -!!(Activate)) & FLASHW_ACTIVE;
1405 FlashState ^= (FlashState ^ pfwi->dwFlags) & (FLASHW_MASK & ~FLASHW_TIMER);
1406 }
1407 FlashState = MAKELONG(LOWORD(FlashState),uCount);
1408 UserSetProp(pWnd, AtomFlashWndState, (HANDLE)FlashState, TRUE);
1409 }
1410 return Ret;
1411 }
1412
1413 HDC FASTCALL
1414 IntBeginPaint(PWND Window, PPAINTSTRUCT Ps)
1415 {
1416 RECT Rect;
1417 INT type;
1418 BOOL Erase = FALSE;
1419
1420 co_UserHideCaret(Window);
1421
1422 Window->state2 |= WNDS2_STARTPAINT;
1423 Window->state &= ~WNDS_PAINTNOTPROCESSED;
1424
1425 if (Window->state & WNDS_SENDNCPAINT)
1426 {
1427 HRGN hRgn;
1428 // Application can keep update dirty.
1429 do
1430 {
1431 Window->state &= ~WNDS_UPDATEDIRTY;
1432 hRgn = IntGetNCUpdateRgn(Window, FALSE);
1433 IntSendNCPaint(Window, hRgn);
1434 if (hRgn > HRGN_WINDOW && GreIsHandleValid(hRgn))
1435 {
1436 /* NOTE: The region can already be deleted! */
1437 GreDeleteObject(hRgn);
1438 }
1439 }
1440 while(Window->state & WNDS_UPDATEDIRTY);
1441 }
1442 else
1443 {
1444 Window->state &= ~WNDS_UPDATEDIRTY;
1445 }
1446
1447 RtlZeroMemory(Ps, sizeof(PAINTSTRUCT));
1448
1449 if (Window->state2 & WNDS2_ENDPAINTINVALIDATE)
1450 {
1451 ERR("BP: Another thread invalidated this window\n");
1452 }
1453
1454 Ps->hdc = UserGetDCEx( Window,
1455 Window->hrgnUpdate,
1456 DCX_INTERSECTRGN | DCX_USESTYLE);
1457 if (!Ps->hdc)
1458 {
1459 return NULL;
1460 }
1461
1462 // If set, always clear flags out due to the conditions later on for sending the message.
1463 if (Window->state & WNDS_SENDERASEBACKGROUND)
1464 {
1465 Window->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
1466 Erase = TRUE;
1467 }
1468
1469 if (Window->hrgnUpdate != NULL)
1470 {
1471 MsqDecPaintCountQueue(Window->head.pti);
1472 IntGdiSetRegionOwner(Window->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
1473 /* The region is part of the dc now and belongs to the process! */
1474 Window->hrgnUpdate = NULL;
1475 }
1476 else
1477 {
1478 if (Window->state & WNDS_INTERNALPAINT)
1479 MsqDecPaintCountQueue(Window->head.pti);
1480 }
1481
1482 type = GdiGetClipBox(Ps->hdc, &Ps->rcPaint);
1483
1484 IntGetClientRect(Window, &Rect);
1485
1486 Window->state &= ~WNDS_INTERNALPAINT;
1487
1488 if ( Erase && // Set to erase,
1489 type != NULLREGION && // don't erase if the clip box is empty,
1490 (!(Window->pcls->style & CS_PARENTDC) || // not parent dc or
1491 RECTL_bIntersectRect( &Rect, &Rect, &Ps->rcPaint) ) ) // intersecting.
1492 {
1493 Ps->fErase = !co_IntSendMessage(UserHMGetHandle(Window), WM_ERASEBKGND, (WPARAM)Ps->hdc, 0);
1494 if ( Ps->fErase )
1495 {
1496 Window->state |= (WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
1497 }
1498 }
1499 else
1500 {
1501 Ps->fErase = FALSE;
1502 }
1503
1504 IntSendChildNCPaint(Window);
1505
1506 return Ps->hdc;
1507 }
1508
1509 BOOL FASTCALL
1510 IntEndPaint(PWND Wnd, PPAINTSTRUCT Ps)
1511 {
1512 HDC hdc = NULL;
1513
1514 hdc = Ps->hdc;
1515
1516 UserReleaseDC(Wnd, hdc, TRUE);
1517
1518 if (Wnd->state2 & WNDS2_ENDPAINTINVALIDATE)
1519 {
1520 ERR("EP: Another thread invalidated this window\n");
1521 Wnd->state2 &= ~WNDS2_ENDPAINTINVALIDATE;
1522 }
1523
1524 Wnd->state2 &= ~(WNDS2_WMPAINTSENT|WNDS2_STARTPAINT);
1525
1526 co_UserShowCaret(Wnd);
1527
1528 return TRUE;
1529 }
1530
1531 /* PUBLIC FUNCTIONS ***********************************************************/
1532
1533 /*
1534 * NtUserBeginPaint
1535 *
1536 * Status
1537 * @implemented
1538 */
1539
1540 HDC APIENTRY
1541 NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* UnsafePs)
1542 {
1543 PWND Window = NULL;
1544 PAINTSTRUCT Ps;
1545 NTSTATUS Status;
1546 HDC hDC;
1547 USER_REFERENCE_ENTRY Ref;
1548 DECLARE_RETURN(HDC);
1549
1550 TRACE("Enter NtUserBeginPaint\n");
1551 UserEnterExclusive();
1552
1553 if (!(Window = UserGetWindowObject(hWnd)))
1554 {
1555 RETURN( NULL);
1556 }
1557
1558 UserRefObjectCo(Window, &Ref);
1559
1560 hDC = IntBeginPaint(Window, &Ps);
1561
1562 Status = MmCopyToCaller(UnsafePs, &Ps, sizeof(PAINTSTRUCT));
1563 if (! NT_SUCCESS(Status))
1564 {
1565 SetLastNtError(Status);
1566 RETURN(NULL);
1567 }
1568
1569 RETURN(hDC);
1570
1571 CLEANUP:
1572 if (Window) UserDerefObjectCo(Window);
1573
1574 TRACE("Leave NtUserBeginPaint, ret=%p\n",_ret_);
1575 UserLeave();
1576 END_CLEANUP;
1577
1578 }
1579
1580 /*
1581 * NtUserEndPaint
1582 *
1583 * Status
1584 * @implemented
1585 */
1586
1587 BOOL APIENTRY
1588 NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* pUnsafePs)
1589 {
1590 NTSTATUS Status = STATUS_SUCCESS;
1591 PWND Window = NULL;
1592 PAINTSTRUCT Ps;
1593 USER_REFERENCE_ENTRY Ref;
1594 DECLARE_RETURN(BOOL);
1595
1596 TRACE("Enter NtUserEndPaint\n");
1597 UserEnterExclusive();
1598
1599 if (!(Window = UserGetWindowObject(hWnd)))
1600 {
1601 RETURN(FALSE);
1602 }
1603
1604 UserRefObjectCo(Window, &Ref); // Here for the exception.
1605
1606 _SEH2_TRY
1607 {
1608 ProbeForRead(pUnsafePs, sizeof(*pUnsafePs), 1);
1609 RtlCopyMemory(&Ps, pUnsafePs, sizeof(PAINTSTRUCT));
1610 }
1611 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1612 {
1613 Status = _SEH2_GetExceptionCode();
1614 }
1615 _SEH2_END
1616 if (!NT_SUCCESS(Status))
1617 {
1618 RETURN(FALSE);
1619 }
1620
1621 RETURN(IntEndPaint(Window, &Ps));
1622
1623 CLEANUP:
1624 if (Window) UserDerefObjectCo(Window);
1625
1626 TRACE("Leave NtUserEndPaint, ret=%i\n",_ret_);
1627 UserLeave();
1628 END_CLEANUP;
1629 }
1630
1631 /*
1632 * @implemented
1633 */
1634 BOOL APIENTRY
1635 NtUserFlashWindowEx(IN PFLASHWINFO pfwi)
1636 {
1637 PWND pWnd;
1638 FLASHWINFO finfo = {0};
1639 BOOL Ret = FALSE;
1640
1641 UserEnterExclusive();
1642
1643 _SEH2_TRY
1644 {
1645 ProbeForRead(pfwi, sizeof(FLASHWINFO), 1);
1646 RtlCopyMemory(&finfo, pfwi, sizeof(FLASHWINFO));
1647 }
1648 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1649 {
1650 SetLastNtError(_SEH2_GetExceptionCode());
1651 _SEH2_YIELD(goto Exit);
1652 }
1653 _SEH2_END
1654
1655 if (!( pWnd = ValidateHwndNoErr(finfo.hwnd)) ||
1656 finfo.cbSize != sizeof(FLASHWINFO) ||
1657 finfo.dwFlags & ~(FLASHW_ALL|FLASHW_TIMER|FLASHW_TIMERNOFG) )
1658 {
1659 EngSetLastError(ERROR_INVALID_PARAMETER);
1660 goto Exit;
1661 }
1662
1663 Ret = IntFlashWindowEx(pWnd, &finfo);
1664
1665 Exit:
1666 UserLeave();
1667 return Ret;
1668 }
1669
1670 /*
1671 GetUpdateRgn, this fails the same as the old one.
1672 */
1673 INT FASTCALL
1674 co_UserGetUpdateRgn(PWND Window, HRGN hRgn, BOOL bErase)
1675 {
1676 int RegionType;
1677 BOOL Type;
1678 RECTL Rect;
1679
1680 ASSERT_REFS_CO(Window);
1681
1682 if (bErase)
1683 {
1684 USER_REFERENCE_ENTRY Ref;
1685 UserRefObjectCo(Window, &Ref);
1686 co_IntPaintWindows(Window, RDW_NOCHILDREN, FALSE);
1687 UserDerefObjectCo(Window);
1688 }
1689
1690 Window->state &= ~WNDS_UPDATEDIRTY;
1691
1692 if (Window->hrgnUpdate == NULL)
1693 {
1694 NtGdiSetRectRgn(hRgn, 0, 0, 0, 0);
1695 return NULLREGION;
1696 }
1697
1698 Rect = Window->rcClient;
1699 Type = IntIntersectWithParents(Window, &Rect);
1700
1701 if (Window->hrgnUpdate == HRGN_WINDOW)
1702 {
1703 // Trap it out.
1704 ERR("GURn: Caller is passing Window Region 1\n");
1705 if (!Type)
1706 {
1707 NtGdiSetRectRgn(hRgn, 0, 0, 0, 0);
1708 return NULLREGION;
1709 }
1710
1711 RegionType = SIMPLEREGION;
1712
1713 if (Window != UserGetDesktopWindow()) // Window->fnid == FNID_DESKTOP
1714 {
1715 RECTL_vOffsetRect(&Rect,
1716 -Window->rcClient.left,
1717 -Window->rcClient.top);
1718 }
1719 GreSetRectRgnIndirect(hRgn, &Rect);
1720 }
1721 else
1722 {
1723 HRGN hrgnTemp = GreCreateRectRgnIndirect(&Rect);
1724
1725 RegionType = NtGdiCombineRgn(hRgn, hrgnTemp, Window->hrgnUpdate, RGN_AND);
1726
1727 if (RegionType == ERROR || RegionType == NULLREGION)
1728 {
1729 if (hrgnTemp) GreDeleteObject(hrgnTemp);
1730 NtGdiSetRectRgn(hRgn, 0, 0, 0, 0);
1731 return RegionType;
1732 }
1733
1734 if (Window != UserGetDesktopWindow()) // Window->fnid == FNID_DESKTOP
1735 {
1736 NtGdiOffsetRgn(hRgn,
1737 -Window->rcClient.left,
1738 -Window->rcClient.top);
1739 }
1740 if (hrgnTemp) GreDeleteObject(hrgnTemp);
1741 }
1742 return RegionType;
1743 }
1744
1745 BOOL FASTCALL
1746 co_UserGetUpdateRect(PWND Window, PRECT pRect, BOOL bErase)
1747 {
1748 INT RegionType;
1749 BOOL Ret = TRUE;
1750
1751 if (bErase)
1752 {
1753 USER_REFERENCE_ENTRY Ref;
1754 UserRefObjectCo(Window, &Ref);
1755 co_IntPaintWindows(Window, RDW_NOCHILDREN, FALSE);
1756 UserDerefObjectCo(Window);
1757 }
1758
1759 Window->state &= ~WNDS_UPDATEDIRTY;
1760
1761 if (Window->hrgnUpdate == NULL)
1762 {
1763 pRect->left = pRect->top = pRect->right = pRect->bottom = 0;
1764 Ret = FALSE;
1765 }
1766 else
1767 {
1768 /* Get the update region bounding box. */
1769 if (Window->hrgnUpdate == HRGN_WINDOW)
1770 {
1771 *pRect = Window->rcClient;
1772 ERR("GURt: Caller is retrieving Window Region 1\n");
1773 }
1774 else
1775 {
1776 RegionType = IntGdiGetRgnBox(Window->hrgnUpdate, pRect);
1777
1778 if (RegionType != ERROR && RegionType != NULLREGION)
1779 RECTL_bIntersectRect(pRect, pRect, &Window->rcClient);
1780 }
1781
1782 if (IntIntersectWithParents(Window, pRect))
1783 {
1784 if (Window != UserGetDesktopWindow()) // Window->fnid == FNID_DESKTOP
1785 {
1786 RECTL_vOffsetRect(pRect,
1787 -Window->rcClient.left,
1788 -Window->rcClient.top);
1789 }
1790 if (Window->pcls->style & CS_OWNDC)
1791 {
1792 HDC hdc;
1793 //DWORD layout;
1794 hdc = UserGetDCEx(Window, NULL, DCX_USESTYLE);
1795 //layout = NtGdiSetLayout(hdc, -1, 0);
1796 //IntMapWindowPoints( 0, Window, (LPPOINT)pRect, 2 );
1797 GreDPtoLP( hdc, (LPPOINT)pRect, 2 );
1798 //NtGdiSetLayout(hdc, -1, layout);
1799 UserReleaseDC(Window, hdc, FALSE);
1800 }
1801 }
1802 else
1803 {
1804 pRect->left = pRect->top = pRect->right = pRect->bottom = 0;
1805 }
1806 }
1807 return Ret;
1808 }
1809
1810 /*
1811 * NtUserGetUpdateRgn
1812 *
1813 * Status
1814 * @implemented
1815 */
1816
1817 INT APIENTRY
1818 NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
1819 {
1820 DECLARE_RETURN(INT);
1821 PWND Window;
1822 INT ret;
1823
1824 TRACE("Enter NtUserGetUpdateRgn\n");
1825 UserEnterExclusive();
1826
1827 if (!(Window = UserGetWindowObject(hWnd)))
1828 {
1829 RETURN(ERROR);
1830 }
1831
1832 ret = co_UserGetUpdateRgn(Window, hRgn, bErase);
1833
1834 RETURN(ret);
1835
1836 CLEANUP:
1837 TRACE("Leave NtUserGetUpdateRgn, ret=%i\n",_ret_);
1838 UserLeave();
1839 END_CLEANUP;
1840 }
1841
1842 /*
1843 * NtUserGetUpdateRect
1844 *
1845 * Status
1846 * @implemented
1847 */
1848
1849 BOOL APIENTRY
1850 NtUserGetUpdateRect(HWND hWnd, LPRECT UnsafeRect, BOOL bErase)
1851 {
1852 PWND Window;
1853 RECTL Rect;
1854 NTSTATUS Status;
1855 BOOL Ret;
1856 DECLARE_RETURN(BOOL);
1857
1858 TRACE("Enter NtUserGetUpdateRect\n");
1859 UserEnterExclusive();
1860
1861 if (!(Window = UserGetWindowObject(hWnd)))
1862 {
1863 RETURN(FALSE);
1864 }
1865
1866 Ret = co_UserGetUpdateRect(Window, &Rect, bErase);
1867
1868 if (UnsafeRect != NULL)
1869 {
1870 Status = MmCopyToCaller(UnsafeRect, &Rect, sizeof(RECTL));
1871 if (!NT_SUCCESS(Status))
1872 {
1873 EngSetLastError(ERROR_INVALID_PARAMETER);
1874 RETURN(FALSE);
1875 }
1876 }
1877
1878 RETURN(Ret);
1879
1880 CLEANUP:
1881 TRACE("Leave NtUserGetUpdateRect, ret=%i\n",_ret_);
1882 UserLeave();
1883 END_CLEANUP;
1884 }
1885
1886 /*
1887 * NtUserRedrawWindow
1888 *
1889 * Status
1890 * @implemented
1891 */
1892
1893 BOOL APIENTRY
1894 NtUserRedrawWindow(
1895 HWND hWnd,
1896 CONST RECT *lprcUpdate,
1897 HRGN hrgnUpdate,
1898 UINT flags)
1899 {
1900 RECTL SafeUpdateRect;
1901 PWND Wnd;
1902 BOOL Ret;
1903 USER_REFERENCE_ENTRY Ref;
1904 NTSTATUS Status = STATUS_SUCCESS;
1905 PREGION RgnUpdate = NULL;
1906 DECLARE_RETURN(BOOL);
1907
1908 TRACE("Enter NtUserRedrawWindow\n");
1909 UserEnterExclusive();
1910
1911 if (!(Wnd = UserGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow())))
1912 {
1913 RETURN( FALSE);
1914 }
1915
1916 if (lprcUpdate)
1917 {
1918 _SEH2_TRY
1919 {
1920 ProbeForRead(lprcUpdate, sizeof(RECTL), 1);
1921 RtlCopyMemory(&SafeUpdateRect, lprcUpdate, sizeof(RECTL));
1922 }
1923 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1924 {
1925 Status = _SEH2_GetExceptionCode();
1926 }
1927 _SEH2_END
1928 if (!NT_SUCCESS(Status))
1929 {
1930 EngSetLastError(RtlNtStatusToDosError(Status));
1931 RETURN( FALSE);
1932 }
1933 }
1934
1935 if ( flags & ~(RDW_ERASE|RDW_FRAME|RDW_INTERNALPAINT|RDW_INVALIDATE|
1936 RDW_NOERASE|RDW_NOFRAME|RDW_NOINTERNALPAINT|RDW_VALIDATE|
1937 RDW_ERASENOW|RDW_UPDATENOW|RDW_ALLCHILDREN|RDW_NOCHILDREN) )
1938 {
1939 /* RedrawWindow fails only in case that flags are invalid */
1940 EngSetLastError(ERROR_INVALID_FLAGS);
1941 RETURN( FALSE);
1942 }
1943
1944 /* We can't hold lock on GDI objects while doing roundtrips to user mode,
1945 * so it will be copied.
1946 */
1947 if (hrgnUpdate > HRGN_WINDOW)
1948 {
1949 RgnUpdate = REGION_LockRgn(hrgnUpdate);
1950 if (!RgnUpdate)
1951 {
1952 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1953 RETURN(FALSE);
1954 }
1955 REGION_UnlockRgn(RgnUpdate);
1956 }
1957 else if (hrgnUpdate == HRGN_WINDOW) // Trap it out.
1958 {
1959 ERR("NTRW: Caller is passing Window Region 1\n");
1960 }
1961
1962 UserRefObjectCo(Wnd, &Ref);
1963
1964 Ret = co_UserRedrawWindow( Wnd,
1965 lprcUpdate ? &SafeUpdateRect : NULL,
1966 RgnUpdate,
1967 flags);
1968
1969 UserDerefObjectCo(Wnd);
1970
1971 RETURN( Ret);
1972
1973 CLEANUP:
1974 TRACE("Leave NtUserRedrawWindow, ret=%i\n",_ret_);
1975 UserLeave();
1976 END_CLEANUP;
1977 }
1978
1979 BOOL
1980 UserDrawCaptionText(
1981 PWND pWnd,
1982 HDC hDc,
1983 const PUNICODE_STRING Text,
1984 const RECTL *lpRc,
1985 UINT uFlags,
1986 HFONT hFont)
1987 {
1988 HFONT hOldFont = NULL;
1989 COLORREF OldTextColor;
1990 NONCLIENTMETRICSW nclm;
1991 NTSTATUS Status;
1992 BOOLEAN bDeleteFont = FALSE;
1993 SIZE Size;
1994 BOOL Ret = TRUE;
1995 ULONG fit = 0, Length;
1996 RECTL r = *lpRc;
1997
1998 TRACE("UserDrawCaptionText: %wZ\n", Text);
1999
2000 nclm.cbSize = sizeof(nclm);
2001 if(!UserSystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &nclm, 0))
2002 {
2003 ERR("UserSystemParametersInfo() failed!\n");
2004 return FALSE;
2005 }
2006
2007 if (!hFont)
2008 {
2009 if(uFlags & DC_SMALLCAP)
2010 Status = TextIntCreateFontIndirect(&nclm.lfSmCaptionFont, &hFont);
2011 else
2012 Status = TextIntCreateFontIndirect(&nclm.lfCaptionFont, &hFont);
2013
2014 if(!NT_SUCCESS(Status))
2015 {
2016 ERR("TextIntCreateFontIndirect() failed! Status: 0x%x\n", Status);
2017 return FALSE;
2018 }
2019
2020 bDeleteFont = TRUE;
2021 }
2022
2023 IntGdiSetBkMode(hDc, TRANSPARENT);
2024
2025 hOldFont = NtGdiSelectFont(hDc, hFont);
2026
2027 if(uFlags & DC_INBUTTON)
2028 OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(COLOR_BTNTEXT));
2029 else
2030 OldTextColor = IntGdiSetTextColor(hDc,
2031 IntGetSysColor(uFlags & DC_ACTIVE ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT));
2032
2033 // Adjust for system menu.
2034 if (pWnd && pWnd->style & WS_SYSMENU)
2035 {
2036 r.right -= UserGetSystemMetrics(SM_CYCAPTION) - 1;
2037 if ((pWnd->style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX)) && !(pWnd->ExStyle & WS_EX_TOOLWINDOW))
2038 {
2039 r.right -= UserGetSystemMetrics(SM_CXSIZE) + 1;
2040 r.right -= UserGetSystemMetrics(SM_CXSIZE) + 1;
2041 }
2042 }
2043
2044 GreGetTextExtentExW(hDc, Text->Buffer, Text->Length/sizeof(WCHAR), r.right - r.left, &fit, 0, &Size, 0);
2045
2046 Length = (Text->Length/sizeof(WCHAR) == fit ? fit : fit+1);
2047
2048 if (Text->Length/sizeof(WCHAR) > Length)
2049 {
2050 Ret = FALSE;
2051 }
2052
2053 if (Ret)
2054 { // Faster while in setup.
2055 GreExtTextOutW( hDc,
2056 lpRc->left,
2057 lpRc->top + (lpRc->bottom - lpRc->top) / 2 - Size.cy / 2, // DT_SINGLELINE && DT_VCENTER
2058 ETO_CLIPPED,
2059 (RECTL *)lpRc,
2060 Text->Buffer,
2061 Length,
2062 NULL,
2063 0 );
2064 }
2065 else
2066 {
2067 DrawTextW( hDc,
2068 Text->Buffer,
2069 Text->Length/sizeof(WCHAR),
2070 (RECTL *)&r,
2071 DT_END_ELLIPSIS|DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX|DT_LEFT);
2072 }
2073
2074 IntGdiSetTextColor(hDc, OldTextColor);
2075
2076 if (hOldFont)
2077 NtGdiSelectFont(hDc, hOldFont);
2078
2079 if (bDeleteFont)
2080 GreDeleteObject(hFont);
2081
2082 return Ret;
2083 }
2084
2085 //
2086 // This draws Buttons, Icons and Text...
2087 //
2088 BOOL UserDrawCaption(
2089 PWND pWnd,
2090 HDC hDc,
2091 RECTL *lpRc,
2092 HFONT hFont,
2093 HICON hIcon,
2094 const PUNICODE_STRING Str,
2095 UINT uFlags)
2096 {
2097 BOOL Ret = FALSE;
2098 HBRUSH hBgBrush, hOldBrush = NULL;
2099 RECTL Rect = *lpRc;
2100 BOOL HasIcon;
2101
2102 RECTL_vMakeWellOrdered(lpRc);
2103
2104 /* Determine whether the icon needs to be displayed */
2105 if (!hIcon && pWnd != NULL)
2106 {
2107 HasIcon = (uFlags & DC_ICON) && !(uFlags & DC_SMALLCAP) &&
2108 (pWnd->style & WS_SYSMENU) && !(pWnd->ExStyle & WS_EX_TOOLWINDOW);
2109 }
2110 else
2111 HasIcon = (hIcon != NULL);
2112
2113 // Draw the caption background
2114 if((uFlags & DC_GRADIENT) && !(uFlags & DC_INBUTTON))
2115 {
2116 static GRADIENT_RECT gcap = {0, 1};
2117 TRIVERTEX Vertices[2];
2118 COLORREF Colors[2];
2119
2120 Colors[0] = IntGetSysColor((uFlags & DC_ACTIVE) ?
2121 COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION);
2122
2123 Colors[1] = IntGetSysColor((uFlags & DC_ACTIVE) ?
2124 COLOR_GRADIENTACTIVECAPTION : COLOR_GRADIENTINACTIVECAPTION);
2125
2126 Vertices[0].x = Rect.left;
2127 Vertices[0].y = Rect.top;
2128 Vertices[0].Red = (WORD)Colors[0]<<8;
2129 Vertices[0].Green = (WORD)Colors[0] & 0xFF00;
2130 Vertices[0].Blue = (WORD)(Colors[0]>>8) & 0xFF00;
2131 Vertices[0].Alpha = 0;
2132
2133 Vertices[1].x = Rect.right;
2134 Vertices[1].y = Rect.bottom;
2135 Vertices[1].Red = (WORD)Colors[1]<<8;
2136 Vertices[1].Green = (WORD)Colors[1] & 0xFF00;
2137 Vertices[1].Blue = (WORD)(Colors[1]>>8) & 0xFF00;
2138 Vertices[1].Alpha = 0;
2139
2140 if(!GreGradientFill(hDc, Vertices, 2, &gcap, 1, GRADIENT_FILL_RECT_H))
2141 {
2142 ERR("GreGradientFill() failed!\n");
2143 goto cleanup;
2144 }
2145 }
2146 else
2147 {
2148 if(uFlags & DC_INBUTTON)
2149 hBgBrush = IntGetSysColorBrush(COLOR_3DFACE);
2150 else if(uFlags & DC_ACTIVE)
2151 hBgBrush = IntGetSysColorBrush(COLOR_ACTIVECAPTION);
2152 else
2153 hBgBrush = IntGetSysColorBrush(COLOR_INACTIVECAPTION);
2154
2155 hOldBrush = NtGdiSelectBrush(hDc, hBgBrush);
2156
2157 if(!hOldBrush)
2158 {
2159 ERR("NtGdiSelectBrush() failed!\n");
2160 goto cleanup;
2161 }
2162
2163 if(!NtGdiPatBlt(hDc, Rect.left, Rect.top,
2164 Rect.right - Rect.left,
2165 Rect.bottom - Rect.top,
2166 PATCOPY))
2167 {
2168 ERR("NtGdiPatBlt() failed!\n");
2169 goto cleanup;
2170 }
2171 }
2172
2173 /* Draw icon */
2174 if (HasIcon)
2175 {
2176 PCURICON_OBJECT pIcon = NULL;
2177
2178 if (hIcon)
2179 {
2180 pIcon = UserGetCurIconObject(hIcon);
2181 }
2182 else if (pWnd)
2183 {
2184 pIcon = NC_IconForWindow(pWnd);
2185 // FIXME: NC_IconForWindow should reference it for us */
2186 if (pIcon)
2187 UserReferenceObject(pIcon);
2188 }
2189
2190 if (pIcon)
2191 {
2192 LONG cx = UserGetSystemMetrics(SM_CXSMICON);
2193 LONG cy = UserGetSystemMetrics(SM_CYSMICON);
2194 LONG x = Rect.left - cx/2 + 1 + (Rect.bottom - Rect.top)/2; // this is really what Window does
2195 LONG y = (Rect.top + Rect.bottom - cy)/2; // center
2196 UserDrawIconEx(hDc, x, y, pIcon, cx, cy, 0, NULL, DI_NORMAL);
2197 UserDereferenceObject(pIcon);
2198 }
2199 else
2200 {
2201 HasIcon = FALSE;
2202 }
2203 }
2204
2205 if (HasIcon)
2206 Rect.left += Rect.bottom - Rect.top;
2207
2208 if((uFlags & DC_TEXT))
2209 {
2210 BOOL Set = FALSE;
2211 Rect.left += 2;
2212
2213 if (Str)
2214 Set = UserDrawCaptionText(pWnd, hDc, Str, &Rect, uFlags, hFont);
2215 else if (pWnd != NULL) // FIXME: Windows does not do that
2216 {
2217 UNICODE_STRING ustr;
2218 ustr.Buffer = pWnd->strName.Buffer; // FIXME: LARGE_STRING truncated!
2219 ustr.Length = (USHORT)min(pWnd->strName.Length, MAXUSHORT);
2220 ustr.MaximumLength = (USHORT)min(pWnd->strName.MaximumLength, MAXUSHORT);
2221 Set = UserDrawCaptionText(pWnd, hDc, &ustr, &Rect, uFlags, hFont);
2222 }
2223 if (pWnd)
2224 {
2225 if (Set)
2226 pWnd->state2 &= ~WNDS2_CAPTIONTEXTTRUNCATED;
2227 else
2228 pWnd->state2 |= WNDS2_CAPTIONTEXTTRUNCATED;
2229 }
2230 }
2231
2232 Ret = TRUE;
2233
2234 cleanup:
2235 if (hOldBrush) NtGdiSelectBrush(hDc, hOldBrush);
2236
2237 return Ret;
2238 }
2239
2240 INT
2241 FASTCALL
2242 UserRealizePalette(HDC hdc)
2243 {
2244 HWND hWnd, hWndDesktop;
2245 DWORD Ret;
2246
2247 Ret = IntGdiRealizePalette(hdc);
2248 if (Ret) // There was a change.
2249 {
2250 hWnd = IntWindowFromDC(hdc);
2251 if (hWnd) // Send broadcast if dc is associated with a window.
2252 { // FYI: Thread locked in CallOneParam.
2253 hWndDesktop = IntGetDesktopWindow();
2254 if ( hWndDesktop != hWnd )
2255 {
2256 PWND pWnd = UserGetWindowObject(hWndDesktop);
2257 ERR("RealizePalette Desktop.");
2258 hdc = UserGetWindowDC(pWnd);
2259 IntPaintDesktop(hdc);
2260 UserReleaseDC(pWnd,hdc,FALSE);
2261 }
2262 UserSendNotifyMessage((HWND)HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)hWnd, 0);
2263 }
2264 }
2265 return Ret;
2266 }
2267
2268 BOOL
2269 APIENTRY
2270 NtUserDrawCaptionTemp(
2271 HWND hWnd,
2272 HDC hDC,
2273 LPCRECT lpRc,
2274 HFONT hFont,
2275 HICON hIcon,
2276 const PUNICODE_STRING str,
2277 UINT uFlags)
2278 {
2279 PWND pWnd = NULL;
2280 UNICODE_STRING SafeStr = {0};
2281 NTSTATUS Status = STATUS_SUCCESS;
2282 RECTL SafeRect;
2283 BOOL Ret;
2284
2285 UserEnterExclusive();
2286
2287 if (hWnd != NULL)
2288 {
2289 if(!(pWnd = UserGetWindowObject(hWnd)))
2290 {
2291 UserLeave();
2292 return FALSE;
2293 }
2294 }
2295
2296 _SEH2_TRY
2297 {
2298 ProbeForRead(lpRc, sizeof(RECTL), sizeof(ULONG));
2299 RtlCopyMemory(&SafeRect, lpRc, sizeof(RECTL));
2300 if (str != NULL)
2301 {
2302 SafeStr = ProbeForReadUnicodeString(str);
2303 if (SafeStr.Length != 0)
2304 {
2305 ProbeForRead( SafeStr.Buffer,
2306 SafeStr.Length,
2307 sizeof(WCHAR));
2308 }
2309 }
2310 }
2311 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2312 {
2313 Status = _SEH2_GetExceptionCode();
2314 }
2315 _SEH2_END;
2316
2317 if (Status != STATUS_SUCCESS)
2318 {
2319 SetLastNtError(Status);
2320 UserLeave();
2321 return FALSE;
2322 }
2323
2324 if (str != NULL)
2325 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, &SafeStr, uFlags);
2326 else
2327 {
2328 if ( RECTL_bIsEmptyRect(&SafeRect) && hFont == 0 && hIcon == 0 )
2329 {
2330 Ret = TRUE;
2331 if (uFlags & DC_DRAWCAPTIONMD)
2332 {
2333 ERR("NC Caption Mode\n");
2334 UserDrawCaptionBar(pWnd, hDC, uFlags);
2335 goto Exit;
2336 }
2337 else if (uFlags & DC_DRAWFRAMEMD)
2338 {
2339 ERR("NC Paint Mode\n");
2340 NC_DoNCPaint(pWnd, hDC, uFlags); // Update Menus too!
2341 goto Exit;
2342 }
2343 }
2344 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, NULL, uFlags);
2345 }
2346 Exit:
2347 UserLeave();
2348 return Ret;
2349 }
2350
2351 BOOL
2352 APIENTRY
2353 NtUserDrawCaption(HWND hWnd,
2354 HDC hDC,
2355 LPCRECT lpRc,
2356 UINT uFlags)
2357 {
2358 return NtUserDrawCaptionTemp(hWnd, hDC, lpRc, 0, 0, NULL, uFlags);
2359 }
2360
2361 BOOL
2362 APIENTRY
2363 NtUserInvalidateRect(
2364 HWND hWnd,
2365 CONST RECT *lpUnsafeRect,
2366 BOOL bErase)
2367 {
2368 UINT flags = RDW_INVALIDATE | (bErase ? RDW_ERASE : 0);
2369 if (!hWnd)
2370 {
2371 flags = RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW;
2372 lpUnsafeRect = NULL;
2373 }
2374 return NtUserRedrawWindow(hWnd, lpUnsafeRect, NULL, flags);
2375 }
2376
2377 BOOL
2378 APIENTRY
2379 NtUserInvalidateRgn(
2380 HWND hWnd,
2381 HRGN hRgn,
2382 BOOL bErase)
2383 {
2384 if (!hWnd)
2385 {
2386 EngSetLastError( ERROR_INVALID_WINDOW_HANDLE );
2387 return FALSE;
2388 }
2389 return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_INVALIDATE | (bErase? RDW_ERASE : 0));
2390 }
2391
2392 BOOL
2393 APIENTRY
2394 NtUserPrintWindow(
2395 HWND hwnd,
2396 HDC hdcBlt,
2397 UINT nFlags)
2398 {
2399 PWND Window;
2400 BOOL Ret = FALSE;
2401
2402 UserEnterExclusive();
2403
2404 if (hwnd)
2405 {
2406 if (!(Window = UserGetWindowObject(hwnd)) || // FIXME:
2407 Window == UserGetDesktopWindow() || // pWnd->fnid == FNID_DESKTOP
2408 Window == UserGetMessageWindow() ) // pWnd->fnid == FNID_MESSAGEWND
2409 {
2410 goto Exit;
2411 }
2412
2413 if ( Window )
2414 {
2415 /* Validate flags and check it as a mask for 0 or 1. */
2416 if ( (nFlags & PW_CLIENTONLY) == nFlags)
2417 Ret = IntPrintWindow( Window, hdcBlt, nFlags);
2418 else
2419 EngSetLastError(ERROR_INVALID_PARAMETER);
2420 }
2421 }
2422 Exit:
2423 UserLeave();
2424 return Ret;
2425 }
2426
2427 /* ValidateRect gets redirected to NtUserValidateRect:
2428 http://blog.csdn.net/ntdll/archive/2005/10/19/509299.aspx */
2429 BOOL
2430 APIENTRY
2431 NtUserValidateRect(
2432 HWND hWnd,
2433 const RECT *lpRect)
2434 {
2435 UINT flags = RDW_VALIDATE;
2436 if (!hWnd)
2437 {
2438 flags = RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW;
2439 lpRect = NULL;
2440 }
2441 return NtUserRedrawWindow(hWnd, lpRect, NULL, flags);
2442 }
2443
2444 /* EOF */