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