4f0f19d5f3482bf5c91353b72320fb1a260da812
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / monitor.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Monitor support
5 * FILE: subsys/win32k/ntuser/monitor.c
6 * PROGRAMER: Anich Gregor (blight@blight.eu.org)
7 * REVISION HISTORY:
8 * 26-02-2004 Created
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include <win32k.h>
14
15 DBG_DEFAULT_CHANNEL(UserMonitor);
16
17 /* FIXME: find include file for these */
18 #define MONITORINFOF_PRIMARY 1
19 #define MONITOR_DEFAULTTONULL 0
20 #define MONITOR_DEFAULTTOPRIMARY 1
21 #define MONITOR_DEFAULTTONEAREST 2
22
23 #define NDEBUG
24 #include <debug.h>
25
26 /* GLOBALS *******************************************************************/
27
28 /* list of monitors */
29 static PMONITOR gMonitorList = NULL;
30
31 /* PRIVATE FUNCTIONS **********************************************************/
32
33 /* IntCreateMonitorObject
34 *
35 * Creates a MONITOR
36 *
37 * Return value
38 * If the function succeeds a pointer to a MONITOR is returned. On failure
39 * NULL is returned.
40 */
41 static
42 PMONITOR
43 IntCreateMonitorObject()
44 {
45 HANDLE Handle;
46 PMONITOR Monitor;
47
48 Monitor = UserCreateObject(gHandleTable, NULL, &Handle, otMonitor, sizeof (MONITOR));
49 if (Monitor == NULL)
50 {
51 return NULL;
52 }
53
54 ExInitializeFastMutex(&Monitor->Lock);
55
56 return Monitor;
57 }
58
59 /* IntDestroyMonitorObject
60 *
61 * Destroys a MONITOR
62 * You have to be the owner of the monitors lock to safely destroy it.
63 *
64 * Arguments
65 *
66 * pMonitor
67 * Pointer to the MONITOR which shall be deleted
68 */
69 static
70 void
71 IntDestroyMonitorObject(IN PMONITOR pMonitor)
72 {
73 RtlFreeUnicodeString(&pMonitor->DeviceName);
74 UserDereferenceObject(pMonitor);
75 }
76
77
78 PMONITOR FASTCALL
79 UserGetMonitorObject(IN HMONITOR hMonitor)
80 {
81 PMONITOR Monitor;
82
83 if (!hMonitor)
84 {
85 EngSetLastError(ERROR_INVALID_MONITOR_HANDLE);
86 return NULL;
87 }
88
89 Monitor = (PMONITOR)UserGetObject(gHandleTable, hMonitor, otMonitor);
90 if (!Monitor)
91 {
92 EngSetLastError(ERROR_INVALID_MONITOR_HANDLE);
93 return NULL;
94 }
95
96 return Monitor;
97 }
98
99
100 /* IntAttachMonitor
101 *
102 * Creates a new MONITOR and appends it to the list of monitors.
103 *
104 * Arguments
105 *
106 * pGdiDevice Pointer to the PDEVOBJ onto which the monitor was attached
107 * DisplayNumber Display Number (starting with 0)
108 *
109 * Return value
110 * Returns a NTSTATUS
111 */
112 NTSTATUS
113 IntAttachMonitor(IN PDEVOBJ *pGdiDevice,
114 IN ULONG DisplayNumber)
115 {
116 PMONITOR Monitor;
117 WCHAR Buffer[CCHDEVICENAME];
118
119 TRACE("Attaching monitor...\n");
120
121 /* create new monitor object */
122 Monitor = IntCreateMonitorObject();
123 if (Monitor == NULL)
124 {
125 TRACE("Couldnt create monitor object\n");
126 return STATUS_INSUFFICIENT_RESOURCES;
127 }
128
129 _snwprintf(Buffer, CCHDEVICENAME, L"\\\\.\\DISPLAY%d", DisplayNumber + 1);
130 if (!RtlCreateUnicodeString(&Monitor->DeviceName, Buffer))
131 {
132 TRACE("Couldn't duplicate monitor name!\n");
133 UserDereferenceObject(Monitor);
134 UserDeleteObject(UserHMGetHandle(Monitor), otMonitor);
135 return STATUS_INSUFFICIENT_RESOURCES;
136 }
137
138 Monitor->GdiDevice = pGdiDevice;
139 Monitor->cWndStack = 0;
140
141 if (gMonitorList == NULL)
142 {
143 TRACE("Primary monitor is beeing attached\n");
144 Monitor->IsPrimary = TRUE;
145 gMonitorList = Monitor;
146 }
147 else
148 {
149 PMONITOR p;
150 TRACE("Additional monitor is beeing attached\n");
151 for (p = gMonitorList; p->Next != NULL; p = p->Next)
152 {
153 p->Next = Monitor;
154 }
155 Monitor->Prev = p;
156 }
157
158 IntUpdateMonitorSize(pGdiDevice);
159
160 return STATUS_SUCCESS;
161 }
162
163 /* IntDetachMonitor
164 *
165 * Deletes a MONITOR and removes it from the list of monitors.
166 *
167 * Arguments
168 *
169 * pGdiDevice Pointer to the PDEVOBJ from which the monitor was detached
170 *
171 * Return value
172 * Returns a NTSTATUS
173 */
174 NTSTATUS
175 IntDetachMonitor(IN PDEVOBJ *pGdiDevice)
176 {
177 PMONITOR Monitor;
178
179 for (Monitor = gMonitorList; Monitor != NULL; Monitor = Monitor->Next)
180 {
181 if (Monitor->GdiDevice == pGdiDevice)
182 break;
183 }
184
185 if (Monitor == NULL)
186 {
187 /* no monitor for given device found */
188 return STATUS_INVALID_PARAMETER;
189 }
190
191 if (Monitor->IsPrimary && (Monitor->Next != NULL || Monitor->Prev != NULL))
192 {
193 PMONITOR NewPrimaryMonitor = (Monitor->Prev != NULL) ? (Monitor->Prev) : (Monitor->Next);
194
195 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&NewPrimaryMonitor->Lock);
196 NewPrimaryMonitor->IsPrimary = TRUE;
197 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&NewPrimaryMonitor->Lock);
198 }
199
200 if (gMonitorList == Monitor)
201 {
202 gMonitorList = Monitor->Next;
203 if (Monitor->Next != NULL)
204 Monitor->Next->Prev = NULL;
205 }
206 else
207 {
208 Monitor->Prev->Next = Monitor->Next;
209 if (Monitor->Next != NULL)
210 Monitor->Next->Prev = Monitor->Prev;
211 }
212
213 if (Monitor->hrgnMonitor)
214 GreDeleteObject(Monitor->hrgnMonitor);
215
216 IntDestroyMonitorObject(Monitor);
217
218 return STATUS_SUCCESS;
219 }
220
221 /* IntUpdateMonitorSize
222 *
223 * Reset size of the monitor using atached device
224 *
225 * Arguments
226 *
227 * PMONITOR
228 * pGdiDevice Pointer to the PDEVOBJ, which size has changed
229 *
230 * Return value
231 * Returns a NTSTATUS
232 */
233 NTSTATUS
234 IntUpdateMonitorSize(IN PDEVOBJ *pGdiDevice)
235 {
236 PMONITOR Monitor;
237
238 for (Monitor = gMonitorList; Monitor != NULL; Monitor = Monitor->Next)
239 {
240 if (Monitor->GdiDevice == pGdiDevice)
241 break;
242 }
243
244 if (Monitor == NULL)
245 {
246 /* no monitor for given device found */
247 return STATUS_INVALID_PARAMETER;
248 }
249
250 Monitor->rcMonitor.left = 0;
251 Monitor->rcMonitor.top = 0;
252 Monitor->rcMonitor.right = Monitor->rcMonitor.left + Monitor->GdiDevice->gdiinfo.ulHorzRes;
253 Monitor->rcMonitor.bottom = Monitor->rcMonitor.top + Monitor->GdiDevice->gdiinfo.ulVertRes;
254 Monitor->rcWork = Monitor->rcMonitor;
255
256 if (Monitor->hrgnMonitor)
257 {
258 GreSetObjectOwner(Monitor->hrgnMonitor, GDI_OBJ_HMGR_POWNED);
259 GreDeleteObject(Monitor->hrgnMonitor);
260 }
261
262 Monitor->hrgnMonitor = IntSysCreateRectRgnIndirect( &Monitor->rcMonitor );
263
264 IntGdiSetRegionOwner(Monitor->hrgnMonitor, GDI_OBJ_HMGR_PUBLIC);
265
266 return STATUS_SUCCESS;
267 }
268
269 /* IntGetPrimaryMonitor
270 *
271 * Returns a PMONITOR for the primary monitor
272 *
273 * Return value
274 * PMONITOR
275 */
276 PMONITOR
277 FASTCALL
278 IntGetPrimaryMonitor()
279 {
280 PMONITOR Monitor;
281
282 for (Monitor = gMonitorList; Monitor != NULL; Monitor = Monitor->Next)
283 {
284 /* FIXME: I guess locking the monitor is not neccessary to read 1 int */
285 if (Monitor->IsPrimary)
286 break;
287 }
288
289 return Monitor;
290 }
291
292 /* IntGetMonitorsFromRect
293 *
294 * Returns a list of monitor handles/rectangles. The rectangles in the list are
295 * the areas of intersection with the monitors.
296 *
297 * Arguments
298 *
299 * pRect
300 * Rectangle in desktop coordinates. If this is NULL all monitors are
301 * returned and the rect list is filled with the sizes of the monitors.
302 *
303 * hMonitorList
304 * Pointer to an array of HMONITOR which is filled with monitor handles.
305 * Can be NULL
306 *
307 * monitorRectList
308 * Pointer to an array of RECT which is filled with intersection rects in
309 * desktop coordinates.
310 * Can be NULL, will be ignored if no intersecting monitor is found and
311 * flags is MONITOR_DEFAULTTONEAREST
312 *
313 * listSize
314 * Size of the hMonitorList and monitorRectList arguments. If this is zero
315 * hMonitorList and monitorRectList are ignored.
316 *
317 * flags
318 * Either 0 or MONITOR_DEFAULTTONEAREST (ignored if rect is NULL)
319 *
320 * Returns
321 * The number of monitors which intersect the specified region.
322 */
323 static
324 UINT
325 IntGetMonitorsFromRect(OPTIONAL IN LPCRECTL pRect,
326 OPTIONAL OUT HMONITOR *hMonitorList,
327 OPTIONAL OUT PRECTL monitorRectList,
328 OPTIONAL IN DWORD listSize,
329 OPTIONAL IN DWORD flags)
330 {
331 PMONITOR Monitor, NearestMonitor = NULL, PrimaryMonitor = NULL;
332 UINT iCount = 0;
333 ULONG iNearestDistance = 0xffffffff;
334
335 /* Find monitors which intersect the rectangle */
336 for (Monitor = gMonitorList; Monitor != NULL; Monitor = Monitor->Next)
337 {
338 RECTL MonitorRect, IntersectionRect;
339
340 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&Monitor->Lock);
341 MonitorRect = Monitor->rcMonitor;
342 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&Monitor->Lock);
343
344 TRACE("MonitorRect: left = %d, top = %d, right = %d, bottom = %d\n",
345 MonitorRect.left, MonitorRect.top, MonitorRect.right, MonitorRect.bottom);
346
347 if (flags == MONITOR_DEFAULTTOPRIMARY && Monitor->IsPrimary)
348 {
349 PrimaryMonitor = Monitor;
350 }
351
352 /* Check if a rect is given */
353 if (pRect == NULL)
354 {
355 /* No rect given, so use the full monitor rect */
356 IntersectionRect = MonitorRect;
357 }
358
359 /* We have a rect, calculate intersection */
360 else if (!RECTL_bIntersectRect(&IntersectionRect, &MonitorRect, pRect))
361 {
362 /* Rects did not intersect */
363 if (flags == MONITOR_DEFAULTTONEAREST)
364 {
365 ULONG cx, cy, iDistance;
366
367 /* Get x and y distance */
368 cx = min(abs(MonitorRect.left - pRect->right),
369 abs(pRect->left - MonitorRect.right));
370 cy = min(abs(MonitorRect.top - pRect->bottom),
371 abs(pRect->top - MonitorRect.bottom));
372
373 /* Calculate distance square */
374 iDistance = cx * cx + cy * cy;
375
376 /* Check if this is the new nearest monitor */
377 if (iDistance < iNearestDistance)
378 {
379 iNearestDistance = iDistance;
380 NearestMonitor = Monitor;
381 }
382 }
383
384 continue;
385 }
386
387 /* Check if there's space in the buffer */
388 if (iCount < listSize)
389 {
390 if (hMonitorList != NULL)
391 hMonitorList[iCount] = UserHMGetHandle(Monitor);
392 if (monitorRectList != NULL)
393 monitorRectList[iCount] = IntersectionRect;
394 }
395
396 /* Increase count of found monitors */
397 iCount++;
398 }
399
400 /* Found nothing intersecting? */
401 if (iCount == 0)
402 {
403 /* Check if we shall default to the nearest monitor */
404 if (flags == MONITOR_DEFAULTTONEAREST && NearestMonitor)
405 {
406 if (hMonitorList && listSize > 0)
407 hMonitorList[iCount] = UserHMGetHandle(NearestMonitor);
408 iCount++;
409 }
410 /* Check if we shall default to the primary monitor */
411 else if (flags == MONITOR_DEFAULTTOPRIMARY && PrimaryMonitor)
412 {
413 if (hMonitorList != NULL && listSize > 0)
414 hMonitorList[iCount] = UserHMGetHandle(PrimaryMonitor);
415 iCount++;
416 }
417 }
418
419 return iCount;
420 }
421
422 /* PUBLIC FUNCTIONS ***********************************************************/
423
424 /* NtUserEnumDisplayMonitors
425 *
426 * Enumerates display monitors which intersect the given HDC/cliprect
427 *
428 * Arguments
429 *
430 * hDC
431 * Handle to a DC for which to enum intersecting monitors. If this is NULL
432 * it returns all monitors which are part of the current virtual screen.
433 *
434 * pRect
435 * Clipping rectangle with coordinate system origin at the DCs origin if the
436 * given HDC is not NULL or in virtual screen coordinated if it is NULL.
437 * Can be NULL
438 *
439 * hMonitorList
440 * Pointer to an array of HMONITOR which is filled with monitor handles.
441 * Can be NULL
442 *
443 * monitorRectList
444 * Pointer to an array of RECT which is filled with intersection rectangles.
445 * Can be NULL
446 *
447 * listSize
448 * Size of the hMonitorList and monitorRectList arguments. If this is zero
449 * hMonitorList and monitorRectList are ignored.
450 *
451 * Returns
452 * The number of monitors which intersect the specified region or -1 on failure.
453 */
454 INT
455 APIENTRY
456 NtUserEnumDisplayMonitors(
457 OPTIONAL IN HDC hDC,
458 OPTIONAL IN LPCRECTL pRect,
459 OPTIONAL OUT HMONITOR *hMonitorList,
460 OPTIONAL OUT PRECTL monitorRectList,
461 OPTIONAL IN DWORD listSize)
462 {
463 INT numMonitors, i;
464 HMONITOR *safeHMonitorList = NULL;
465 PRECTL safeRectList = NULL;
466 RECTL rect, *myRect;
467 RECTL dcRect;
468 NTSTATUS status;
469
470 /* get rect */
471 if (pRect != NULL)
472 {
473 status = MmCopyFromCaller(&rect, pRect, sizeof (RECT));
474 if (!NT_SUCCESS(status))
475 {
476 TRACE("MmCopyFromCaller() failed!\n");
477 SetLastNtError(status);
478 return -1;
479 }
480 }
481
482 if (hDC != NULL)
483 {
484 PDC dc;
485 INT regionType;
486
487 /* get visible region bounding rect */
488 dc = DC_LockDc(hDC);
489 if (dc == NULL)
490 {
491 TRACE("DC_LockDc() failed!\n");
492 /* FIXME: setlasterror? */
493 return -1;
494 }
495 regionType = REGION_GetRgnBox(dc->prgnVis, &dcRect);
496 DC_UnlockDc(dc);
497
498 if (regionType == 0)
499 {
500 TRACE("NtGdiGetRgnBox() failed!\n");
501 return -1;
502 }
503 if (regionType == NULLREGION)
504 return 0;
505 if (regionType == COMPLEXREGION)
506 {
507 /* TODO: warning */
508 }
509
510 /* if hDC and pRect are given the area of interest is pRect with
511 coordinate origin at the DC position */
512 if (pRect != NULL)
513 {
514 rect.left += dcRect.left;
515 rect.right += dcRect.left;
516 rect.top += dcRect.top;
517 rect.bottom += dcRect.top;
518 }
519 /* if hDC is given and pRect is not the area of interest is the
520 bounding rect of hDC */
521 else
522 {
523 rect = dcRect;
524 }
525 }
526
527 if (hDC == NULL && pRect == NULL)
528 myRect = NULL;
529 else
530 myRect = &rect;
531
532 /* find intersecting monitors */
533 numMonitors = IntGetMonitorsFromRect(myRect, NULL, NULL, 0, 0);
534 if (numMonitors == 0 || listSize == 0 ||
535 (hMonitorList == NULL && monitorRectList == NULL))
536 {
537 TRACE("numMonitors = %d\n", numMonitors);
538 return numMonitors;
539 }
540
541 if (hMonitorList != NULL && listSize != 0)
542 {
543 safeHMonitorList = ExAllocatePoolWithTag(PagedPool, sizeof (HMONITOR) * listSize, USERTAG_MONITORRECTS);
544 if (safeHMonitorList == NULL)
545 {
546 /* FIXME: EngSetLastError? */
547 return -1;
548 }
549 }
550 if (monitorRectList != NULL && listSize != 0)
551 {
552 safeRectList = ExAllocatePoolWithTag(PagedPool, sizeof (RECT) * listSize, USERTAG_MONITORRECTS);
553 if (safeRectList == NULL)
554 {
555 ExFreePoolWithTag(safeHMonitorList, USERTAG_MONITORRECTS);
556 /* FIXME: EngSetLastError? */
557 return -1;
558 }
559 }
560
561 /* get intersecting monitors */
562 numMonitors = IntGetMonitorsFromRect(myRect, safeHMonitorList, safeRectList,
563 listSize, 0 );
564
565 if (hDC != NULL && pRect != NULL && safeRectList != NULL)
566 for (i = 0; i < numMonitors; i++)
567 {
568 safeRectList[i].left -= dcRect.left;
569 safeRectList[i].right -= dcRect.left;
570 safeRectList[i].top -= dcRect.top;
571 safeRectList[i].bottom -= dcRect.top;
572 }
573
574 /* output result */
575 if (hMonitorList != NULL && listSize != 0)
576 {
577 status = MmCopyToCaller(hMonitorList, safeHMonitorList, sizeof (HMONITOR) * listSize);
578 ExFreePool(safeHMonitorList);
579 if (!NT_SUCCESS(status))
580 {
581 ExFreePoolWithTag(safeRectList, USERTAG_MONITORRECTS);
582 SetLastNtError(status);
583 return -1;
584 }
585 }
586 if (monitorRectList != NULL && listSize != 0)
587 {
588 status = MmCopyToCaller(monitorRectList, safeRectList, sizeof (RECT) * listSize);
589 ExFreePoolWithTag(safeRectList, USERTAG_MONITORRECTS);
590 if (!NT_SUCCESS(status))
591 {
592 SetLastNtError(status);
593 return -1;
594 }
595 }
596
597 return numMonitors;
598 }
599
600 /* NtUserGetMonitorInfo
601 *
602 * Retrieves information about a given monitor
603 *
604 * Arguments
605 *
606 * hMonitor
607 * Handle to a monitor for which to get information
608 *
609 * pMonitorInfo
610 * Pointer to a MONITORINFO struct which is filled with the information.
611 * The cbSize member must be set to sizeof(MONITORINFO) or
612 * sizeof(MONITORINFOEX). Even if set to sizeof(MONITORINFOEX) only parts
613 * from MONITORINFO will be filled.
614 *
615 * pDevice
616 * Pointer to a UNICODE_STRING which will recieve the device's name. The
617 * length should be CCHDEVICENAME
618 * Can be NULL
619 *
620 * Return value
621 * TRUE on success; FALSE on failure (calls SetLastNtError())
622 *
623 */
624 BOOL
625 APIENTRY
626 NtUserGetMonitorInfo(
627 IN HMONITOR hMonitor,
628 OUT LPMONITORINFO pMonitorInfo)
629 {
630 PMONITOR Monitor;
631 MONITORINFOEXW MonitorInfo;
632 NTSTATUS Status;
633 DECLARE_RETURN(BOOL);
634
635 TRACE("Enter NtUserGetMonitorInfo\n");
636 UserEnterShared();
637
638 /* get monitor object */
639 if (!(Monitor = UserGetMonitorObject(hMonitor)))
640 {
641 TRACE("Couldnt find monitor 0x%lx\n", hMonitor);
642 RETURN(FALSE);
643 }
644
645 if(pMonitorInfo == NULL)
646 {
647 SetLastNtError(STATUS_INVALID_PARAMETER);
648 RETURN(FALSE);
649 }
650
651 /* get size of pMonitorInfo */
652 Status = MmCopyFromCaller(&MonitorInfo.cbSize, &pMonitorInfo->cbSize, sizeof (MonitorInfo.cbSize));
653 if (!NT_SUCCESS(Status))
654 {
655 SetLastNtError(Status);
656 RETURN(FALSE);
657 }
658 if ((MonitorInfo.cbSize != sizeof (MONITORINFO)) &&
659 (MonitorInfo.cbSize != sizeof (MONITORINFOEXW)))
660 {
661 SetLastNtError(STATUS_INVALID_PARAMETER);
662 RETURN(FALSE);
663 }
664
665 /* fill monitor info */
666 MonitorInfo.rcMonitor = Monitor->rcMonitor;
667 MonitorInfo.rcWork = Monitor->rcWork;
668 MonitorInfo.dwFlags = 0;
669
670 if (Monitor->IsPrimary)
671 MonitorInfo.dwFlags |= MONITORINFOF_PRIMARY;
672
673 /* fill device name */
674 if (MonitorInfo.cbSize == sizeof (MONITORINFOEXW))
675 {
676 RtlStringCbCopyNW(MonitorInfo.szDevice,
677 sizeof(MonitorInfo.szDevice),
678 Monitor->DeviceName.Buffer,
679 Monitor->DeviceName.Length);
680 }
681
682 /* output data */
683 Status = MmCopyToCaller(pMonitorInfo, &MonitorInfo, MonitorInfo.cbSize);
684 if (!NT_SUCCESS(Status))
685 {
686 TRACE("GetMonitorInfo: MmCopyToCaller failed\n");
687 SetLastNtError(Status);
688 RETURN(FALSE);
689 }
690
691 TRACE("GetMonitorInfo: success\n");
692
693 RETURN(TRUE);
694
695 CLEANUP:
696 TRACE("Leave NtUserGetMonitorInfo, ret=%i\n",_ret_);
697 UserLeave();
698 END_CLEANUP;
699 }
700
701 /* NtUserMonitorFromPoint
702 *
703 * Returns a handle to the monitor containing the given point.
704 *
705 * Arguments
706 *
707 * point
708 * Point for which to find monitor
709 *
710 * dwFlags
711 * Specifies the behaviour if the point isn't on any of the monitors.
712 *
713 * Return value
714 * If the point is found a handle to the monitor is returned; if not the
715 * return value depends on dwFlags
716 */
717 HMONITOR
718 APIENTRY
719 NtUserMonitorFromPoint(
720 IN POINT point,
721 IN DWORD dwFlags)
722 {
723 INT NumMonitors;
724 RECTL InRect;
725 HMONITOR hMonitor = NULL;
726
727 /* fill inRect (bottom-right exclusive) */
728 InRect.left = point.x;
729 InRect.right = point.x + 1;
730 InRect.top = point.y;
731 InRect.bottom = point.y + 1;
732
733 /* find intersecting monitor */
734 NumMonitors = IntGetMonitorsFromRect(&InRect, &hMonitor, NULL, 1, dwFlags);
735 if (NumMonitors < 0)
736 {
737 return (HMONITOR)NULL;
738 }
739
740 return hMonitor;
741 }
742
743 /* NtUserMonitorFromRect
744 *
745 * Returns a handle to the monitor having the largest intersection with a
746 * given rectangle
747 *
748 * Arguments
749 *
750 * pRect
751 * Pointer to a RECT for which to find monitor
752 *
753 * dwFlags
754 * Specifies the behaviour if no monitor intersects the given rect
755 *
756 * Return value
757 * If a monitor intersects the rect a handle to it is returned; if not the
758 * return value depends on dwFlags
759 */
760 HMONITOR
761 APIENTRY
762 NtUserMonitorFromRect(
763 IN LPCRECTL pRect,
764 IN DWORD dwFlags)
765 {
766 ULONG numMonitors, iLargestArea = 0, i;
767 PRECTL rectList;
768 HMONITOR *hMonitorList;
769 HMONITOR hMonitor = NULL;
770 RECTL rect;
771 NTSTATUS status;
772
773 /* get rect */
774 status = MmCopyFromCaller(&rect, pRect, sizeof (RECT));
775 if (!NT_SUCCESS(status))
776 {
777 SetLastNtError(status);
778 return (HMONITOR)NULL;
779 }
780
781 /* find intersecting monitors */
782 numMonitors = IntGetMonitorsFromRect(&rect, &hMonitor, NULL, 1, dwFlags);
783 if (numMonitors <= 1)
784 {
785 return hMonitor;
786 }
787
788 hMonitorList = ExAllocatePoolWithTag(PagedPool,
789 sizeof(HMONITOR) * numMonitors,
790 USERTAG_MONITORRECTS);
791 if (hMonitorList == NULL)
792 {
793 /* FIXME: EngSetLastError? */
794 return (HMONITOR)NULL;
795 }
796
797 rectList = ExAllocatePoolWithTag(PagedPool,
798 sizeof(RECT) * numMonitors,
799 USERTAG_MONITORRECTS);
800 if (rectList == NULL)
801 {
802 ExFreePoolWithTag(hMonitorList, USERTAG_MONITORRECTS);
803 /* FIXME: EngSetLastError? */
804 return (HMONITOR)NULL;
805 }
806
807 /* get intersecting monitors */
808 numMonitors = IntGetMonitorsFromRect(&rect, hMonitorList, rectList,
809 numMonitors, 0);
810 if (numMonitors == 0)
811 {
812 ExFreePoolWithTag(hMonitorList, USERTAG_MONITORRECTS);
813 ExFreePoolWithTag(rectList, USERTAG_MONITORRECTS);
814 return (HMONITOR)NULL;
815 }
816
817 /* find largest intersection */
818 for (i = 0; i < numMonitors; i++)
819 {
820 ULONG area = (rectList[i].right - rectList[i].left) *
821 (rectList[i].bottom - rectList[i].top);
822 if (area >= iLargestArea)
823 {
824 hMonitor = hMonitorList[i];
825 }
826 }
827
828 ExFreePoolWithTag(hMonitorList, USERTAG_MONITORRECTS);
829 ExFreePoolWithTag(rectList, USERTAG_MONITORRECTS);
830
831 return hMonitor;
832 }
833
834
835 HMONITOR
836 APIENTRY
837 NtUserMonitorFromWindow(
838 IN HWND hWnd,
839 IN DWORD dwFlags)
840 {
841 PWND Window;
842 HMONITOR hMonitor = NULL;
843 RECTL Rect;
844 DECLARE_RETURN(HMONITOR);
845
846 TRACE("Enter NtUserMonitorFromWindow\n");
847 UserEnterShared();
848
849 if (!(Window = UserGetWindowObject(hWnd)))
850 {
851 if (dwFlags == MONITOR_DEFAULTTONULL)
852 {
853 RETURN(hMonitor);
854 }
855 IntGetMonitorsFromRect(NULL, &hMonitor, NULL, 1, dwFlags);
856 RETURN(hMonitor);
857 }
858
859 Rect.left = Rect.right = Window->rcWindow.left;
860 Rect.top = Rect.bottom = Window->rcWindow.bottom;
861
862 IntGetMonitorsFromRect(&Rect, &hMonitor, NULL, 1, dwFlags);
863
864 RETURN(hMonitor);
865
866 CLEANUP:
867 TRACE("Leave NtUserMonitorFromWindow, ret=%i\n",_ret_);
868 UserLeave();
869 END_CLEANUP;
870 }