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