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