- Fixed deadlock in IntFindWindowToRepaint.
[reactos.git] / reactos / subsys / win32k / ntuser / painting.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 * $Id: painting.c,v 1.71 2004/02/21 23:30:18 navaraf Exp $
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * PURPOSE: Window painting function
24 * FILE: subsys/win32k/ntuser/painting.c
25 * PROGRAMER: Filip Navara (xnavara@volny.cz)
26 * REVISION HISTORY:
27 * 06/06/2001 Created (?)
28 * 18/11/2003 Complete rewrite
29 */
30
31 /* INCLUDES ******************************************************************/
32
33 #include <ddk/ntddk.h>
34 #include <internal/safe.h>
35 #include <win32k/win32k.h>
36 #include <include/object.h>
37 #include <include/guicheck.h>
38 #include <include/window.h>
39 #include <include/desktop.h>
40 #include <include/winpos.h>
41 #include <include/class.h>
42 #include <include/caret.h>
43 #include <include/error.h>
44 #include <include/winsta.h>
45 #include <windows.h>
46 #include <include/painting.h>
47 #include <user32/wininternal.h>
48 #include <include/rect.h>
49 #include <win32k/coord.h>
50 #include <win32k/region.h>
51 #include <include/vis.h>
52 #include <include/intgdi.h>
53
54 #define NDEBUG
55 #include <win32k/debug1.h>
56
57 /* PRIVATE FUNCTIONS **********************************************************/
58
59 VOID FASTCALL
60 IntValidateParent(PWINDOW_OBJECT Child, HRGN ValidRegion)
61 {
62 HWND Parent;
63 PWINDOW_OBJECT ParentWindow;
64
65 Parent = NtUserGetAncestor(Child->Self, GA_PARENT);
66 while (Parent)
67 {
68 ParentWindow = IntGetWindowObject(Parent);
69 if (ParentWindow && !(ParentWindow->Style & WS_CLIPCHILDREN))
70 {
71 ExAcquireFastMutex(&ParentWindow->UpdateLock);
72 if (ParentWindow->UpdateRegion != 0)
73 {
74 INT OffsetX, OffsetY;
75
76 /*
77 * We must offset the child region by the offset of the
78 * child rect in the parent.
79 */
80 OffsetX = Child->WindowRect.left - ParentWindow->WindowRect.left;
81 OffsetY = Child->WindowRect.top - ParentWindow->WindowRect.top;
82 NtGdiOffsetRgn(ValidRegion, OffsetX, OffsetY);
83 NtGdiCombineRgn(ParentWindow->UpdateRegion, ParentWindow->UpdateRegion,
84 ValidRegion, RGN_DIFF);
85 /* FIXME: If the resulting region is empty, remove fake posted paint message */
86 NtGdiOffsetRgn(ValidRegion, -OffsetX, -OffsetY);
87 }
88 ExReleaseFastMutex(&ParentWindow->UpdateLock);
89 }
90 if (ParentWindow)
91 {
92 IntReleaseWindowObject(ParentWindow);
93 Parent = NtUserGetAncestor(Parent, GA_PARENT);
94 }
95 else
96 {
97 return;
98 }
99 }
100 }
101
102 /*
103 * IntPaintWindows
104 *
105 * Internal function used by IntRedrawWindow.
106 */
107
108 STATIC VOID FASTCALL
109 IntPaintWindows(PWINDOW_OBJECT Window, ULONG Flags)
110 {
111 HDC hDC;
112 HWND hWnd = Window->Self;
113 HRGN TempRegion;
114
115 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
116 {
117 if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
118 {
119 ExAcquireFastMutex(&Window->UpdateLock);
120 if (Window->NCUpdateRegion)
121 {
122 IntValidateParent(Window, Window->NCUpdateRegion);
123 }
124 TempRegion = Window->NCUpdateRegion;
125 Window->NCUpdateRegion = NULL;
126 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
127 MsqDecPaintCountQueue(Window->MessageQueue);
128 ExReleaseFastMutex(&Window->UpdateLock);
129 IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)TempRegion, 0);
130 }
131
132 if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
133 {
134 if (Window->UpdateRegion)
135 {
136 IntValidateParent(Window, Window->UpdateRegion);
137 hDC = NtUserGetDCEx(hWnd, 0, DCX_CACHE | DCX_USESTYLE |
138 DCX_INTERSECTUPDATE);
139 if (hDC != NULL)
140 {
141 if (IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)hDC, 0))
142 {
143 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
144 }
145 NtUserReleaseDC(hWnd, hDC);
146 }
147 }
148 }
149
150 if (Flags & RDW_UPDATENOW)
151 {
152 if (Window->UpdateRegion != NULL ||
153 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
154 {
155 IntSendMessage(hWnd, WM_PAINT, 0, 0);
156 ExAcquireFastMutex(&Window->UpdateLock);
157 if (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
158 {
159 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
160 if (Window->UpdateRegion == NULL)
161 {
162 MsqDecPaintCountQueue(Window->MessageQueue);
163 }
164 }
165 ExReleaseFastMutex(&Window->UpdateLock);
166 }
167 }
168 }
169
170 /*
171 * Check that the window is still valid at this point
172 */
173
174 if (! IntIsWindow(hWnd))
175 {
176 return;
177 }
178
179 /*
180 * Paint child windows.
181 */
182 if (!(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
183 ((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN)))
184 {
185 HWND *List, *phWnd;
186
187 if ((List = IntWinListChildren(Window)))
188 {
189 for (phWnd = List; *phWnd; ++phWnd)
190 {
191 Window = IntGetWindowObject(*phWnd);
192 if (Window && (Window->Style & WS_VISIBLE))
193 {
194 IntPaintWindows(Window, Flags);
195 IntReleaseWindowObject(Window);
196 }
197 }
198 ExFreePool(List);
199 }
200 }
201 }
202
203 /*
204 * IntInvalidateWindows
205 *
206 * Internal function used by IntRedrawWindow.
207 */
208
209 VOID FASTCALL
210 IntInvalidateWindows(PWINDOW_OBJECT Window, HRGN hRgn, ULONG Flags)
211 {
212 INT RgnType;
213 BOOL HadPaintMessage, HadNCPaintMessage;
214 BOOL HasPaintMessage, HasNCPaintMessage;
215
216 /*
217 * Clip the given region with window rectangle (or region)
218 */
219
220 #ifdef TODO
221 if (!Window->WindowRegion)
222 #endif
223 {
224 HRGN hRgnWindow;
225
226 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
227 NtGdiOffsetRgn(hRgnWindow,
228 -Window->WindowRect.left,
229 -Window->WindowRect.top);
230 RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnWindow, RGN_AND);
231 NtGdiDeleteObject(hRgnWindow);
232 }
233 #ifdef TODO
234 else
235 {
236 RgnType = NtGdiCombineRgn(hRgn, hRgn, Window->WindowRegion, RGN_AND);
237 }
238 #endif
239
240 /*
241 * Save current state of pending updates
242 */
243
244 ExAcquireFastMutex(&Window->UpdateLock);
245 HadPaintMessage = Window->UpdateRegion != NULL ||
246 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
247 HadNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
248
249 /*
250 * Update the region and flags
251 */
252
253 if (Flags & RDW_INVALIDATE && RgnType != NULLREGION)
254 {
255 if (Window->UpdateRegion == NULL)
256 {
257 Window->UpdateRegion = NtGdiCreateRectRgn(0, 0, 0, 0);
258 }
259
260 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
261 hRgn, RGN_OR) == NULLREGION)
262 {
263 NtGdiDeleteObject(Window->UpdateRegion);
264 Window->UpdateRegion = NULL;
265 }
266
267 if (Flags & RDW_FRAME)
268 Window->Flags |= WINDOWOBJECT_NEED_NCPAINT;
269 if (Flags & RDW_ERASE)
270 Window->Flags |= WINDOWOBJECT_NEED_ERASEBKGND;
271
272 Flags |= RDW_FRAME;
273 }
274
275 if (Flags & RDW_VALIDATE && RgnType != NULLREGION)
276 {
277 if (Window->UpdateRegion != NULL)
278 {
279 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
280 hRgn, RGN_DIFF) == NULLREGION)
281 {
282 NtGdiDeleteObject(Window->UpdateRegion);
283 Window->UpdateRegion = NULL;
284 }
285 }
286
287 if (Window->UpdateRegion == NULL)
288 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
289 if (Flags & RDW_NOFRAME)
290 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
291 if (Flags & RDW_NOERASE)
292 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
293 }
294
295 if (Flags & RDW_INTERNALPAINT)
296 {
297 Window->Flags |= WINDOWOBJECT_NEED_INTERNALPAINT;
298 }
299
300 if (Flags & RDW_NOINTERNALPAINT)
301 {
302 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
303 }
304
305 /*
306 * Split the nonclient update region.
307 */
308
309 {
310 HRGN hRgnWindow, hRgnNonClient;
311
312 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
313 NtGdiOffsetRgn(hRgnWindow,
314 -Window->WindowRect.left,
315 -Window->WindowRect.top);
316
317 hRgnNonClient = NtGdiCreateRectRgn(0, 0, 0, 0);
318 if (NtGdiCombineRgn(hRgnNonClient, Window->UpdateRegion,
319 hRgnWindow, RGN_DIFF) == NULLREGION)
320 {
321 NtGdiDeleteObject(hRgnNonClient);
322 hRgnNonClient = NULL;
323 }
324
325 /*
326 * Remove the nonclient region from the standard update region.
327 */
328
329 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
330 hRgnWindow, RGN_AND) == NULLREGION)
331 {
332 NtGdiDeleteObject(Window->UpdateRegion);
333 Window->UpdateRegion = NULL;
334 }
335
336 if (Window->NCUpdateRegion == NULL)
337 {
338 Window->NCUpdateRegion = hRgnNonClient;
339 }
340 else
341 {
342 NtGdiCombineRgn(Window->NCUpdateRegion, Window->NCUpdateRegion,
343 hRgnNonClient, RGN_OR);
344 }
345
346 NtGdiDeleteObject(hRgnWindow);
347 }
348
349 /*
350 * Process children if needed
351 */
352
353 if (!(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
354 ((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN)))
355 {
356 HWND *List, *phWnd;
357 PWINDOW_OBJECT Child;
358
359 if ((List = IntWinListChildren(Window)))
360 {
361 for (phWnd = List; *phWnd; ++phWnd)
362 {
363 Child = IntGetWindowObject(*phWnd);
364 if(!Child)
365 {
366 continue;
367 }
368 if (Child->Style & WS_VISIBLE)
369 {
370 /*
371 * Recursive call to update children UpdateRegion
372 */
373 HRGN hRgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
374 NtGdiCombineRgn(hRgnTemp, hRgn, 0, RGN_COPY);
375 NtGdiOffsetRgn(hRgnTemp,
376 Window->WindowRect.left - Child->WindowRect.left,
377 Window->WindowRect.top - Child->WindowRect.top);
378 IntInvalidateWindows(Child, hRgnTemp, Flags);
379 NtGdiDeleteObject(hRgnTemp);
380 }
381 IntReleaseWindowObject(Child);
382 }
383 ExFreePool(List);
384 }
385 }
386
387 /*
388 * Fake post paint messages to window message queue if needed
389 */
390
391 HasPaintMessage = Window->UpdateRegion != NULL ||
392 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
393 HasNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
394
395 if (HasPaintMessage != HadPaintMessage)
396 {
397 if (HadPaintMessage)
398 MsqDecPaintCountQueue(Window->MessageQueue);
399 else
400 MsqIncPaintCountQueue(Window->MessageQueue);
401 }
402
403 if (HasNCPaintMessage != HadNCPaintMessage)
404 {
405 if (HadNCPaintMessage)
406 MsqDecPaintCountQueue(Window->MessageQueue);
407 else
408 MsqIncPaintCountQueue(Window->MessageQueue);
409 }
410
411 ExReleaseFastMutex(&Window->UpdateLock);
412 }
413
414 /*
415 * IntIsWindowDrawable
416 *
417 * Remarks
418 * Window is drawable when it is visible and all parents are not
419 * minimized.
420 */
421
422 BOOL FASTCALL
423 IntIsWindowDrawable(PWINDOW_OBJECT Window)
424 {
425 PWINDOW_OBJECT Wnd = Window;
426
427 for (; Wnd; Wnd = Wnd->Parent)
428 {
429 if (!(Wnd->Style & WS_VISIBLE) ||
430 ((Wnd->Style & WS_MINIMIZE) && (Wnd != Window)))
431 return FALSE;
432 }
433
434 return TRUE;
435 }
436
437 /*
438 * IntRedrawWindow
439 *
440 * Internal version of NtUserRedrawWindow that takes WINDOW_OBJECT as
441 * first parameter.
442 */
443
444 BOOL FASTCALL
445 IntRedrawWindow(PWINDOW_OBJECT Window, const RECT* UpdateRect, HRGN UpdateRgn,
446 ULONG Flags)
447 {
448 HRGN hRgn = NULL;
449
450 /*
451 * Step 1.
452 * Validation of passed parameters.
453 */
454
455 if (!IntIsWindowDrawable(Window) ||
456 (Flags & (RDW_VALIDATE | RDW_INVALIDATE)) ==
457 (RDW_VALIDATE | RDW_INVALIDATE))
458 {
459 return FALSE;
460 }
461
462 /*
463 * Step 2.
464 * Transform the parameters UpdateRgn and UpdateRect into
465 * a region hRgn specified in window coordinates.
466 */
467
468 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE))
469 {
470 if (UpdateRgn != NULL)
471 {
472 hRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
473 NtGdiCombineRgn(hRgn, UpdateRgn, NULL, RGN_COPY);
474 NtGdiOffsetRgn(hRgn,
475 Window->ClientRect.left - Window->WindowRect.left,
476 Window->ClientRect.top - Window->WindowRect.top);
477 } else
478 if (UpdateRect != NULL)
479 {
480 hRgn = UnsafeIntCreateRectRgnIndirect((RECT *)UpdateRect);
481 NtGdiOffsetRgn(hRgn,
482 Window->ClientRect.left - Window->WindowRect.left,
483 Window->ClientRect.top - Window->WindowRect.top);
484 } else
485 if ((Flags & (RDW_INVALIDATE | RDW_FRAME)) == (RDW_INVALIDATE | RDW_FRAME) ||
486 (Flags & (RDW_VALIDATE | RDW_NOFRAME)) == (RDW_VALIDATE | RDW_NOFRAME))
487 {
488 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
489 NtGdiOffsetRgn(hRgn,
490 -Window->WindowRect.left,
491 -Window->WindowRect.top);
492 }
493 else
494 {
495 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
496 NtGdiOffsetRgn(hRgn,
497 -Window->WindowRect.left,
498 -Window->WindowRect.top);
499 }
500 }
501
502 /*
503 * Step 3.
504 * Adjust the window update region depending on hRgn and flags.
505 */
506
507 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT))
508 {
509 IntInvalidateWindows(Window, hRgn, Flags);
510 }
511
512 /*
513 * Step 4.
514 * Repaint and erase windows if needed.
515 */
516
517 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
518 {
519 IntPaintWindows(Window, Flags);
520 }
521
522 /*
523 * Step 5.
524 * Cleanup ;-)
525 */
526
527 if (hRgn != NULL)
528 {
529 NtGdiDeleteObject(hRgn);
530 }
531
532 return TRUE;
533 }
534
535 BOOL FASTCALL
536 IntIsWindowDirty(PWINDOW_OBJECT Window)
537 {
538 return (Window->Style & WS_VISIBLE) &&
539 ((Window->UpdateRegion != NULL) ||
540 (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT) ||
541 (Window->Flags & WINDOWOBJECT_NEED_NCPAINT));
542 }
543
544 HWND STDCALL
545 IntFindWindowToRepaint(HWND hWnd, PW32THREAD Thread)
546 {
547 PWINDOW_OBJECT Window;
548 PWINDOW_OBJECT Child;
549 HWND hFoundWnd = NULL;
550
551 Window = IntGetWindowObject(hWnd);
552 if (Window == NULL)
553 return NULL;
554
555 if (IntIsWindowDirty(Window) &&
556 IntWndBelongsToThread(Window, Thread))
557 {
558 IntReleaseWindowObject(Window);
559 return hWnd;
560 }
561
562 ExAcquireFastMutex(&Window->ChildrenListLock);
563 for (Child = Window->LastChild; Child; Child = Child->PrevSibling)
564 {
565 if (IntIsWindowDirty(Child) &&
566 IntWndBelongsToThread(Child, Thread))
567 {
568 hFoundWnd = Child->Self;
569 break;
570 }
571 }
572 ExReleaseFastMutex(&Window->ChildrenListLock);
573
574 if (hFoundWnd == NULL)
575 {
576 HWND *List;
577 INT i;
578
579 List = IntWinListChildren(Window);
580 if (List != NULL)
581 {
582 for (i = 0; List[i]; i++)
583 {
584 hFoundWnd = IntFindWindowToRepaint(List[i], Thread);
585 if (hFoundWnd != NULL)
586 break;
587 }
588 ExFreePool(List);
589 }
590 }
591
592 IntReleaseWindowObject(Window);
593
594 return hFoundWnd;
595 }
596
597 BOOL FASTCALL
598 IntGetPaintMessage(HWND hWnd, PW32THREAD Thread, MSG *Message,
599 BOOL Remove)
600 {
601 PWINDOW_OBJECT Window;
602 PUSER_MESSAGE_QUEUE MessageQueue = (PUSER_MESSAGE_QUEUE)Thread->MessageQueue;
603
604 if (!MessageQueue->PaintPosted)
605 return FALSE;
606
607 if (hWnd)
608 Message->hwnd = IntFindWindowToRepaint(hWnd, PsGetWin32Thread());
609 else
610 Message->hwnd = IntFindWindowToRepaint(IntGetDesktopWindow(), PsGetWin32Thread());
611
612 if (Message->hwnd == NULL)
613 {
614 #if 0
615 DPRINT1("PAINTING BUG: Thread marked as containing dirty windows, but no dirty windows found!\n");
616 #endif
617 ExAcquireFastMutex(&MessageQueue->Lock);
618 MessageQueue->PaintPosted = 0;
619 MessageQueue->PaintCount = 0;
620 ExReleaseFastMutex(&MessageQueue->Lock);
621 return FALSE;
622 }
623
624 Window = IntGetWindowObject(Message->hwnd);
625 if (Window != NULL)
626 {
627 ExAcquireFastMutex(&Window->UpdateLock);
628 if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
629 {
630 Message->message = WM_NCPAINT;
631 Message->wParam = (WPARAM)Window->NCUpdateRegion;
632 Message->lParam = 0;
633 if (Remove)
634 {
635 IntValidateParent(Window, Window->NCUpdateRegion);
636 Window->NCUpdateRegion = NULL;
637 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
638 MsqDecPaintCountQueue(Window->MessageQueue);
639 }
640 } else
641 {
642 Message->message = WM_PAINT;
643 Message->wParam = Message->lParam = 0;
644 if (Remove && Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
645 {
646 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
647 if (Window->UpdateRegion == NULL)
648 {
649 MsqDecPaintCountQueue(Window->MessageQueue);
650 }
651 }
652 }
653 ExReleaseFastMutex(&Window->UpdateLock);
654
655 IntReleaseWindowObject(Window);
656 return TRUE;
657 }
658
659 return FALSE;
660 }
661
662 HWND FASTCALL
663 IntFixCaret(HWND hWnd, LPRECT lprc, UINT flags)
664 {
665 PDESKTOP_OBJECT Desktop;
666 PTHRDCARETINFO CaretInfo;
667 HWND hWndCaret;
668
669 Desktop = PsGetCurrentThread()->Win32Thread->Desktop;
670 CaretInfo = ((PUSER_MESSAGE_QUEUE)Desktop->ActiveMessageQueue)->CaretInfo;
671 hWndCaret = CaretInfo->hWnd;
672 if (hWndCaret == hWnd ||
673 ((flags & SW_SCROLLCHILDREN) && IntIsChildWindow(hWnd, hWndCaret)))
674 {
675 POINT pt, FromOffset, ToOffset, Offset;
676 RECT rcCaret;
677
678 pt.x = CaretInfo->Pos.x;
679 pt.y = CaretInfo->Pos.y;
680 IntGetClientOrigin(hWndCaret, &FromOffset);
681 IntGetClientOrigin(hWnd, &ToOffset);
682 Offset.x = FromOffset.x - ToOffset.x;
683 Offset.y = FromOffset.y - ToOffset.y;
684 rcCaret.left = pt.x;
685 rcCaret.top = pt.y;
686 rcCaret.right = pt.x + CaretInfo->Size.cx;
687 rcCaret.bottom = pt.y + CaretInfo->Size.cy;
688 if (NtGdiIntersectRect(lprc, lprc, &rcCaret))
689 {
690 NtUserHideCaret(0);
691 lprc->left = pt.x;
692 lprc->top = pt.y;
693 return hWndCaret;
694 }
695 }
696
697 return 0;
698 }
699
700 /* PUBLIC FUNCTIONS ***********************************************************/
701
702 /*
703 * NtUserBeginPaint
704 *
705 * Status
706 * @implemented
707 */
708
709 HDC STDCALL
710 NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* lPs)
711 {
712 PWINDOW_OBJECT Window;
713
714 if (!(Window = IntGetWindowObject(hWnd)))
715 {
716 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
717 return NULL;
718 }
719
720 NtUserHideCaret(hWnd);
721
722 lPs->hdc = NtUserGetDCEx(hWnd, 0, DCX_INTERSECTUPDATE | DCX_WINDOWPAINT |
723 DCX_USESTYLE);
724
725 if (!lPs->hdc)
726 {
727 IntReleaseWindowObject(Window);
728 return NULL;
729 }
730
731 ExAcquireFastMutex(&Window->UpdateLock);
732 if (Window->UpdateRegion != NULL)
733 {
734 MsqDecPaintCountQueue(Window->MessageQueue);
735 IntValidateParent(Window, Window->UpdateRegion);
736 NtGdiGetRgnBox(Window->UpdateRegion, &lPs->rcPaint);
737 NtGdiOffsetRect(&lPs->rcPaint,
738 Window->WindowRect.left - Window->ClientRect.left,
739 Window->WindowRect.top - Window->ClientRect.top);
740 NtGdiDeleteObject(Window->UpdateRegion);
741 Window->UpdateRegion = NULL;
742 }
743 else
744 {
745 NtUserGetClientRect(Window, &lPs->rcPaint);
746 }
747 ExReleaseFastMutex(&Window->UpdateLock);
748
749 if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
750 {
751 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
752 lPs->fErase = !IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)lPs->hdc, 0);
753 }
754 else
755 {
756 lPs->fErase = FALSE;
757 }
758
759 IntReleaseWindowObject(Window);
760
761 return lPs->hdc;
762 }
763
764 /*
765 * NtUserEndPaint
766 *
767 * Status
768 * @implemented
769 */
770
771 BOOL STDCALL
772 NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* lPs)
773 {
774 NtUserReleaseDC(hWnd, lPs->hdc);
775 NtUserShowCaret(hWnd);
776
777 return TRUE;
778 }
779
780 /*
781 * NtUserInvalidateRect
782 *
783 * Status
784 * @implemented
785 */
786
787 DWORD STDCALL
788 NtUserInvalidateRect(HWND hWnd, CONST RECT *Rect, BOOL Erase)
789 {
790 return NtUserRedrawWindow(hWnd, Rect, 0, RDW_INVALIDATE | (Erase ? RDW_ERASE : 0));
791 }
792
793 /*
794 * NtUserInvalidateRgn
795 *
796 * Status
797 * @implemented
798 */
799
800 DWORD STDCALL
801 NtUserInvalidateRgn(HWND hWnd, HRGN Rgn, BOOL Erase)
802 {
803 return NtUserRedrawWindow(hWnd, NULL, Rgn, RDW_INVALIDATE | (Erase ? RDW_ERASE : 0));
804 }
805
806 /*
807 * NtUserValidateRgn
808 *
809 * Status
810 * @implemented
811 */
812
813 BOOL STDCALL
814 NtUserValidateRgn(HWND hWnd, HRGN hRgn)
815 {
816 return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_VALIDATE | RDW_NOCHILDREN);
817 }
818
819 /*
820 * NtUserUpdateWindow
821 *
822 * Status
823 * @implemented
824 */
825
826 BOOL STDCALL
827 NtUserUpdateWindow(HWND hWnd)
828 {
829 return NtUserRedrawWindow(hWnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN);
830 }
831
832 /*
833 * NtUserGetUpdateRgn
834 *
835 * Status
836 * @implemented
837 */
838
839 INT STDCALL
840 NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
841 {
842 PWINDOW_OBJECT Window;
843 int RegionType;
844
845 if (!(Window = IntGetWindowObject(hWnd)))
846 {
847 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
848 return ERROR;
849 }
850
851 if (Window->UpdateRegion == NULL)
852 {
853 RegionType = (NtGdiSetRectRgn(hRgn, 0, 0, 0, 0) ? NULLREGION : ERROR);
854 }
855 else
856 {
857 RegionType = NtGdiCombineRgn(hRgn, Window->UpdateRegion, hRgn, RGN_COPY);
858 NtGdiOffsetRgn(
859 hRgn,
860 Window->WindowRect.left - Window->ClientRect.left,
861 Window->WindowRect.top - Window->ClientRect.top);
862 }
863
864 IntReleaseWindowObject(Window);
865
866 if (bErase && RegionType != NULLREGION && RegionType != ERROR)
867 {
868 NtUserRedrawWindow(hWnd, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
869 }
870
871 return RegionType;
872 }
873
874 /*
875 * NtUserGetUpdateRect
876 *
877 * Status
878 * @implemented
879 */
880
881 BOOL STDCALL
882 NtUserGetUpdateRect(HWND hWnd, LPRECT lpRect, BOOL fErase)
883 {
884 HRGN hRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
885
886 if (!lpRect)
887 {
888 SetLastWin32Error(ERROR_INVALID_PARAMETER);
889 return FALSE;
890 }
891
892 NtUserGetUpdateRgn(hWnd, hRgn, fErase);
893 NtGdiGetRgnBox(hRgn, lpRect);
894 NtGdiDeleteObject(hRgn);
895
896 return lpRect->left < lpRect->right && lpRect->top < lpRect->bottom;
897 }
898
899 /*
900 * NtUserRedrawWindow
901 *
902 * Status
903 * @implemented
904 */
905
906 BOOL STDCALL
907 NtUserRedrawWindow(HWND hWnd, CONST RECT *lprcUpdate, HRGN hrgnUpdate,
908 UINT flags)
909 {
910 RECT SafeUpdateRect;
911 NTSTATUS Status;
912 PWINDOW_OBJECT Wnd;
913
914 if (!(Wnd = IntGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow())))
915 {
916 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
917 return FALSE;
918 }
919
920 if (lprcUpdate != NULL)
921 {
922 Status = MmCopyFromCaller(&SafeUpdateRect, (PRECT)lprcUpdate,
923 sizeof(RECT));
924
925 if (!NT_SUCCESS(Status))
926 {
927 SetLastWin32Error(ERROR_INVALID_PARAMETER);
928 IntReleaseWindowObject(Wnd);
929 return FALSE;
930 }
931 }
932
933 Status = IntRedrawWindow(Wnd, NULL == lprcUpdate ? NULL : &SafeUpdateRect,
934 hrgnUpdate, flags);
935
936 if (!NT_SUCCESS(Status))
937 {
938 /* IntRedrawWindow fails only in case that flags are invalid */
939 SetLastWin32Error(ERROR_INVALID_PARAMETER);
940 IntReleaseWindowObject(Wnd);
941 return FALSE;
942 }
943
944 IntReleaseWindowObject(Wnd);
945 return TRUE;
946 }
947
948 /*
949 * NtUserScrollDC
950 *
951 * Status
952 * @implemented
953 */
954
955 DWORD STDCALL
956 NtUserScrollDC(HDC hDC, INT dx, INT dy, const RECT *lprcScroll,
957 const RECT *lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate)
958 {
959 RECT rSrc, rClipped_src, rClip, rDst, offset;
960 PDC DC;
961
962 /*
963 * Compute device clipping region (in device coordinates).
964 */
965
966 DC = DC_LockDc(hDC);
967 if (NULL == DC)
968 {
969 return FALSE;
970 }
971 if (lprcScroll)
972 rSrc = *lprcScroll;
973 else
974 IntGdiGetClipBox(hDC, &rSrc);
975 IntLPtoDP(DC, (LPPOINT)&rSrc, 2);
976
977 if (lprcClip)
978 rClip = *lprcClip;
979 else
980 IntGdiGetClipBox(hDC, &rClip);
981 IntLPtoDP(DC, (LPPOINT)&rClip, 2);
982
983 NtGdiIntersectRect(&rClipped_src, &rSrc, &rClip);
984
985 rDst = rClipped_src;
986 NtGdiSetRect(&offset, 0, 0, dx, dy);
987 IntLPtoDP(DC, (LPPOINT)&offset, 2);
988 NtGdiOffsetRect(&rDst, offset.right - offset.left, offset.bottom - offset.top);
989 NtGdiIntersectRect(&rDst, &rDst, &rClip);
990
991 /*
992 * Copy bits, if possible.
993 */
994
995 if (rDst.bottom > rDst.top && rDst.right > rDst.left)
996 {
997 RECT rDst_lp = rDst, rSrc_lp = rDst;
998
999 NtGdiOffsetRect(&rSrc_lp, offset.left - offset.right, offset.top - offset.bottom);
1000 IntDPtoLP(DC, (LPPOINT)&rDst_lp, 2);
1001 IntDPtoLP(DC, (LPPOINT)&rSrc_lp, 2);
1002 DC_UnlockDc(hDC);
1003
1004 if (!NtGdiBitBlt(hDC, rDst_lp.left, rDst_lp.top, rDst_lp.right - rDst_lp.left,
1005 rDst_lp.bottom - rDst_lp.top, hDC, rSrc_lp.left, rSrc_lp.top,
1006 SRCCOPY))
1007 return FALSE;
1008 }
1009 else
1010 {
1011 DC_UnlockDc(hDC);
1012 }
1013
1014 /*
1015 * Compute update areas. This is the clipped source or'ed with the
1016 * unclipped source translated minus the clipped src translated (rDst)
1017 * all clipped to rClip.
1018 */
1019
1020 if (hrgnUpdate || lprcUpdate)
1021 {
1022 HRGN hRgn = hrgnUpdate, hRgn2;
1023
1024 if (hRgn)
1025 NtGdiSetRectRgn(hRgn, rClipped_src.left, rClipped_src.top, rClipped_src.right, rClipped_src.bottom);
1026 else
1027 hRgn = NtGdiCreateRectRgn(rClipped_src.left, rClipped_src.top, rClipped_src.right, rClipped_src.bottom);
1028
1029 hRgn2 = UnsafeIntCreateRectRgnIndirect(&rSrc);
1030 NtGdiOffsetRgn(hRgn2, offset.right - offset.left, offset.bottom - offset.top);
1031 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_OR);
1032
1033 NtGdiSetRectRgn(hRgn2, rDst.left, rDst.top, rDst.right, rDst.bottom);
1034 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_DIFF);
1035
1036 NtGdiSetRectRgn(hRgn2, rClip.left, rClip.top, rClip.right, rClip.bottom);
1037 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_AND);
1038
1039 if (lprcUpdate)
1040 {
1041 NtGdiGetRgnBox(hRgn, lprcUpdate);
1042
1043 /* Put the lprcUpdate in logical coordinate */
1044 NtGdiDPtoLP(hDC, (LPPOINT)lprcUpdate, 2);
1045 }
1046 if (!hrgnUpdate)
1047 NtGdiDeleteObject(hRgn);
1048 NtGdiDeleteObject(hRgn2);
1049 }
1050 return TRUE;
1051 }
1052
1053 /*
1054 * NtUserScrollWindowEx
1055 *
1056 * Status
1057 * @implemented
1058 */
1059
1060 DWORD STDCALL
1061 NtUserScrollWindowEx(HWND hWnd, INT dx, INT dy, const RECT *rect,
1062 const RECT *clipRect, HRGN hrgnUpdate, LPRECT rcUpdate, UINT flags)
1063 {
1064 RECT rc, cliprc, caretrc;
1065 INT Result;
1066 PWINDOW_OBJECT Window;
1067 HDC hDC;
1068 HRGN hrgnTemp;
1069 HWND hwndCaret;
1070 BOOL bUpdate = (rcUpdate || hrgnUpdate || flags & (SW_INVALIDATE | SW_ERASE));
1071 BOOL bOwnRgn = TRUE;
1072
1073 Window = IntGetWindowObject(hWnd);
1074 if (!Window || !IntIsWindowDrawable(Window))
1075 {
1076 IntReleaseWindowObject(Window);
1077 return ERROR;
1078 }
1079
1080 IntGetClientRect(Window, &rc);
1081 if (rect)
1082 NtGdiIntersectRect(&rc, &rc, rect);
1083
1084 if (clipRect)
1085 NtGdiIntersectRect(&cliprc, &rc, clipRect);
1086 else
1087 cliprc = rc;
1088
1089 if (cliprc.right <= cliprc.left || cliprc.bottom <= cliprc.top ||
1090 (dx == 0 && dy == 0))
1091 {
1092 return NULLREGION;
1093 }
1094
1095 caretrc = rc;
1096 hwndCaret = IntFixCaret(hWnd, &caretrc, flags);
1097
1098 if (hrgnUpdate)
1099 bOwnRgn = FALSE;
1100 else if (bUpdate)
1101 hrgnUpdate = NtGdiCreateRectRgn(0, 0, 0, 0);
1102
1103 hDC = NtUserGetDCEx(hWnd, 0, DCX_CACHE | DCX_USESTYLE);
1104 if (hDC)
1105 {
1106 NtUserScrollDC(hDC, dx, dy, &rc, &cliprc, hrgnUpdate, rcUpdate);
1107 NtUserReleaseDC(hWnd, hDC);
1108 }
1109
1110 /*
1111 * Take into account the fact that some damage may have occurred during
1112 * the scroll.
1113 */
1114
1115 hrgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
1116 Result = NtUserGetUpdateRgn(hWnd, hrgnTemp, FALSE);
1117 if (Result != NULLREGION)
1118 {
1119 HRGN hrgnClip = UnsafeIntCreateRectRgnIndirect(&cliprc);
1120 NtGdiOffsetRgn(hrgnTemp, dx, dy);
1121 NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnClip, RGN_AND);
1122 NtUserRedrawWindow(hWnd, NULL, hrgnTemp, RDW_INVALIDATE | RDW_ERASE);
1123 NtGdiDeleteObject(hrgnClip);
1124 }
1125 NtGdiDeleteObject(hrgnTemp);
1126
1127 if (flags & SW_SCROLLCHILDREN)
1128 {
1129 HWND *List = IntWinListChildren(Window);
1130 if (List)
1131 {
1132 int i;
1133 RECT r, dummy;
1134 POINT ClientOrigin;
1135
1136 for (i = 0; List[i]; i++)
1137 {
1138 NtUserGetWindowRect(List[i], &r);
1139 IntGetClientOrigin(hWnd, &ClientOrigin);
1140 r.left -= ClientOrigin.x;
1141 r.top -= ClientOrigin.y;
1142 r.right -= ClientOrigin.x;
1143 r.bottom -= ClientOrigin.y;
1144 if (!rect || NtGdiIntersectRect(&dummy, &r, &rc))
1145 WinPosSetWindowPos(List[i], 0, r.left + dx, r.top + dy, 0, 0,
1146 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
1147 SWP_NOREDRAW);
1148 }
1149 ExFreePool(List);
1150 }
1151 }
1152
1153 if (flags & (SW_INVALIDATE | SW_ERASE))
1154 NtUserRedrawWindow(hWnd, NULL, hrgnUpdate, RDW_INVALIDATE | RDW_ERASE |
1155 ((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
1156 ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0));
1157
1158 if (bOwnRgn && hrgnUpdate)
1159 NtGdiDeleteObject(hrgnUpdate);
1160
1161 if (hwndCaret)
1162 {
1163 IntSetCaretPos(caretrc.left + dx, caretrc.top + dy);
1164 NtUserShowCaret(hwndCaret);
1165 }
1166
1167 IntReleaseWindowObject(Window);
1168
1169 return Result;
1170 }
1171
1172 /* EOF */