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