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)
11 /* INCLUDES ******************************************************************/
15 DBG_DEFAULT_CHANNEL(UserMonitor
);
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
26 /* GLOBALS *******************************************************************/
28 /* list of monitors */
29 static PMONITOR gMonitorList
= NULL
;
31 /* PRIVATE FUNCTIONS **********************************************************/
33 /* IntCreateMonitorObject
38 * If the function succeeds a pointer to a MONITOR is returned. On failure
43 IntCreateMonitorObject()
48 Monitor
= UserCreateObject(gHandleTable
, NULL
, &Handle
, otMonitor
, sizeof (MONITOR
));
54 ExInitializeFastMutex(&Monitor
->Lock
);
59 /* IntDestroyMonitorObject
62 * You have to be the owner of the monitors lock to safely destroy it.
67 * Pointer to the MONITOR which shall be deleted
71 IntDestroyMonitorObject(IN PMONITOR pMonitor
)
73 RtlFreeUnicodeString(&pMonitor
->DeviceName
);
74 UserDereferenceObject(pMonitor
);
79 UserGetMonitorObject(IN HMONITOR hMonitor
)
85 EngSetLastError(ERROR_INVALID_MONITOR_HANDLE
);
89 Monitor
= (PMONITOR
)UserGetObject(gHandleTable
, hMonitor
, otMonitor
);
92 EngSetLastError(ERROR_INVALID_MONITOR_HANDLE
);
102 * Creates a new MONITOR and appends it to the list of monitors.
106 * pGdiDevice Pointer to the PDEVOBJ onto which the monitor was attached
107 * DisplayNumber Display Number (starting with 0)
113 IntAttachMonitor(IN PDEVOBJ
*pGdiDevice
,
114 IN ULONG DisplayNumber
)
117 WCHAR Buffer
[CCHDEVICENAME
];
119 TRACE("Attaching monitor...\n");
121 /* create new monitor object */
122 Monitor
= IntCreateMonitorObject();
125 TRACE("Couldnt create monitor object\n");
126 return STATUS_INSUFFICIENT_RESOURCES
;
129 _snwprintf(Buffer
, CCHDEVICENAME
, L
"\\\\.\\DISPLAY%d", DisplayNumber
+ 1);
130 if (!RtlCreateUnicodeString(&Monitor
->DeviceName
, Buffer
))
132 TRACE("Couldn't duplicate monitor name!\n");
133 UserDereferenceObject(Monitor
);
134 UserDeleteObject(UserHMGetHandle(Monitor
), otMonitor
);
135 return STATUS_INSUFFICIENT_RESOURCES
;
138 Monitor
->GdiDevice
= pGdiDevice
;
139 Monitor
->cWndStack
= 0;
141 if (gMonitorList
== NULL
)
143 TRACE("Primary monitor is beeing attached\n");
144 Monitor
->IsPrimary
= TRUE
;
145 gMonitorList
= Monitor
;
150 TRACE("Additional monitor is beeing attached\n");
151 for (p
= gMonitorList
; p
->Next
!= NULL
; p
= p
->Next
)
158 IntUpdateMonitorSize(pGdiDevice
);
160 return STATUS_SUCCESS
;
165 * Deletes a MONITOR and removes it from the list of monitors.
169 * pGdiDevice Pointer to the PDEVOBJ from which the monitor was detached
175 IntDetachMonitor(IN PDEVOBJ
*pGdiDevice
)
179 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
181 if (Monitor
->GdiDevice
== pGdiDevice
)
187 /* no monitor for given device found */
188 return STATUS_INVALID_PARAMETER
;
191 if (Monitor
->IsPrimary
&& (Monitor
->Next
!= NULL
|| Monitor
->Prev
!= NULL
))
193 PMONITOR NewPrimaryMonitor
= (Monitor
->Prev
!= NULL
) ? (Monitor
->Prev
) : (Monitor
->Next
);
195 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&NewPrimaryMonitor
->Lock
);
196 NewPrimaryMonitor
->IsPrimary
= TRUE
;
197 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&NewPrimaryMonitor
->Lock
);
200 if (gMonitorList
== Monitor
)
202 gMonitorList
= Monitor
->Next
;
203 if (Monitor
->Next
!= NULL
)
204 Monitor
->Next
->Prev
= NULL
;
208 Monitor
->Prev
->Next
= Monitor
->Next
;
209 if (Monitor
->Next
!= NULL
)
210 Monitor
->Next
->Prev
= Monitor
->Prev
;
213 if (Monitor
->hrgnMonitor
)
214 GreDeleteObject(Monitor
->hrgnMonitor
);
216 IntDestroyMonitorObject(Monitor
);
218 return STATUS_SUCCESS
;
221 /* IntUpdateMonitorSize
223 * Reset size of the monitor using atached device
228 * pGdiDevice Pointer to the PDEVOBJ, which size has changed
234 IntUpdateMonitorSize(IN PDEVOBJ
*pGdiDevice
)
238 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
240 if (Monitor
->GdiDevice
== pGdiDevice
)
246 /* no monitor for given device found */
247 return STATUS_INVALID_PARAMETER
;
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
;
256 if (Monitor
->hrgnMonitor
)
258 GreSetObjectOwner(Monitor
->hrgnMonitor
, GDI_OBJ_HMGR_POWNED
);
259 GreDeleteObject(Monitor
->hrgnMonitor
);
262 Monitor
->hrgnMonitor
= IntSysCreateRectRgnIndirect( &Monitor
->rcMonitor
);
264 IntGdiSetRegionOwner(Monitor
->hrgnMonitor
, GDI_OBJ_HMGR_PUBLIC
);
266 return STATUS_SUCCESS
;
269 /* IntGetPrimaryMonitor
271 * Returns a PMONITOR for the primary monitor
278 IntGetPrimaryMonitor()
282 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
284 /* FIXME: I guess locking the monitor is not neccessary to read 1 int */
285 if (Monitor
->IsPrimary
)
292 /* IntGetMonitorsFromRect
294 * Returns a list of monitor handles/rectangles. The rectangles in the list are
295 * the areas of intersection with the monitors.
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.
304 * Pointer to an array of HMONITOR which is filled with monitor handles.
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
314 * Size of the hMonitorList and monitorRectList arguments. If this is zero
315 * hMonitorList and monitorRectList are ignored.
318 * Either 0 or MONITOR_DEFAULTTONEAREST (ignored if rect is NULL)
321 * The number of monitors which intersect the specified region.
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
)
331 PMONITOR Monitor
, NearestMonitor
= NULL
, PrimaryMonitor
= NULL
;
333 ULONG iNearestDistance
= 0xffffffff;
335 /* Find monitors which intersect the rectangle */
336 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
338 RECTL MonitorRect
, IntersectionRect
;
340 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&Monitor
->Lock
);
341 MonitorRect
= Monitor
->rcMonitor
;
342 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&Monitor
->Lock
);
344 TRACE("MonitorRect: left = %d, top = %d, right = %d, bottom = %d\n",
345 MonitorRect
.left
, MonitorRect
.top
, MonitorRect
.right
, MonitorRect
.bottom
);
347 if (flags
== MONITOR_DEFAULTTOPRIMARY
&& Monitor
->IsPrimary
)
349 PrimaryMonitor
= Monitor
;
352 /* Check if a rect is given */
355 /* No rect given, so use the full monitor rect */
356 IntersectionRect
= MonitorRect
;
359 /* We have a rect, calculate intersection */
360 else if (!RECTL_bIntersectRect(&IntersectionRect
, &MonitorRect
, pRect
))
362 /* Rects did not intersect */
363 if (flags
== MONITOR_DEFAULTTONEAREST
)
365 ULONG cx
, cy
, iDistance
;
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
));
373 /* Calculate distance square */
374 iDistance
= cx
* cx
+ cy
* cy
;
376 /* Check if this is the new nearest monitor */
377 if (iDistance
< iNearestDistance
)
379 iNearestDistance
= iDistance
;
380 NearestMonitor
= Monitor
;
387 /* Check if there's space in the buffer */
388 if (iCount
< listSize
)
390 if (hMonitorList
!= NULL
)
391 hMonitorList
[iCount
] = UserHMGetHandle(Monitor
);
392 if (monitorRectList
!= NULL
)
393 monitorRectList
[iCount
] = IntersectionRect
;
396 /* Increase count of found monitors */
400 /* Found nothing intersecting? */
403 /* Check if we shall default to the nearest monitor */
404 if (flags
== MONITOR_DEFAULTTONEAREST
&& NearestMonitor
)
406 if (hMonitorList
&& listSize
> 0)
407 hMonitorList
[iCount
] = UserHMGetHandle(NearestMonitor
);
410 /* Check if we shall default to the primary monitor */
411 else if (flags
== MONITOR_DEFAULTTOPRIMARY
&& PrimaryMonitor
)
413 if (hMonitorList
!= NULL
&& listSize
> 0)
414 hMonitorList
[iCount
] = UserHMGetHandle(PrimaryMonitor
);
422 /* PUBLIC FUNCTIONS ***********************************************************/
424 /* NtUserEnumDisplayMonitors
426 * Enumerates display monitors which intersect the given HDC/cliprect
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.
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.
440 * Pointer to an array of HMONITOR which is filled with monitor handles.
444 * Pointer to an array of RECT which is filled with intersection rectangles.
448 * Size of the hMonitorList and monitorRectList arguments. If this is zero
449 * hMonitorList and monitorRectList are ignored.
452 * The number of monitors which intersect the specified region or -1 on failure.
456 NtUserEnumDisplayMonitors(
458 OPTIONAL IN LPCRECTL pRect
,
459 OPTIONAL OUT HMONITOR
*hMonitorList
,
460 OPTIONAL OUT PRECTL monitorRectList
,
461 OPTIONAL IN DWORD listSize
)
464 HMONITOR
*safeHMonitorList
= NULL
;
465 PRECTL safeRectList
= NULL
;
473 status
= MmCopyFromCaller(&rect
, pRect
, sizeof (RECT
));
474 if (!NT_SUCCESS(status
))
476 TRACE("MmCopyFromCaller() failed!\n");
477 SetLastNtError(status
);
487 /* get visible region bounding rect */
491 TRACE("DC_LockDc() failed!\n");
492 /* FIXME: setlasterror? */
495 regionType
= REGION_GetRgnBox(dc
->prgnVis
, &dcRect
);
500 TRACE("NtGdiGetRgnBox() failed!\n");
503 if (regionType
== NULLREGION
)
505 if (regionType
== COMPLEXREGION
)
510 /* if hDC and pRect are given the area of interest is pRect with
511 coordinate origin at the DC position */
514 rect
.left
+= dcRect
.left
;
515 rect
.right
+= dcRect
.left
;
516 rect
.top
+= dcRect
.top
;
517 rect
.bottom
+= dcRect
.top
;
519 /* if hDC is given and pRect is not the area of interest is the
520 bounding rect of hDC */
527 if (hDC
== NULL
&& pRect
== NULL
)
532 /* find intersecting monitors */
533 numMonitors
= IntGetMonitorsFromRect(myRect
, NULL
, NULL
, 0, 0);
534 if (numMonitors
== 0 || listSize
== 0 ||
535 (hMonitorList
== NULL
&& monitorRectList
== NULL
))
537 TRACE("numMonitors = %d\n", numMonitors
);
541 if (hMonitorList
!= NULL
&& listSize
!= 0)
543 safeHMonitorList
= ExAllocatePoolWithTag(PagedPool
, sizeof (HMONITOR
) * listSize
, USERTAG_MONITORRECTS
);
544 if (safeHMonitorList
== NULL
)
546 /* FIXME: EngSetLastError? */
550 if (monitorRectList
!= NULL
&& listSize
!= 0)
552 safeRectList
= ExAllocatePoolWithTag(PagedPool
, sizeof (RECT
) * listSize
, USERTAG_MONITORRECTS
);
553 if (safeRectList
== NULL
)
555 ExFreePoolWithTag(safeHMonitorList
, USERTAG_MONITORRECTS
);
556 /* FIXME: EngSetLastError? */
561 /* get intersecting monitors */
562 numMonitors
= IntGetMonitorsFromRect(myRect
, safeHMonitorList
, safeRectList
,
565 if (hDC
!= NULL
&& pRect
!= NULL
&& safeRectList
!= NULL
)
566 for (i
= 0; i
< numMonitors
; i
++)
568 safeRectList
[i
].left
-= dcRect
.left
;
569 safeRectList
[i
].right
-= dcRect
.left
;
570 safeRectList
[i
].top
-= dcRect
.top
;
571 safeRectList
[i
].bottom
-= dcRect
.top
;
575 if (hMonitorList
!= NULL
&& listSize
!= 0)
577 status
= MmCopyToCaller(hMonitorList
, safeHMonitorList
, sizeof (HMONITOR
) * listSize
);
578 ExFreePool(safeHMonitorList
);
579 if (!NT_SUCCESS(status
))
581 ExFreePoolWithTag(safeRectList
, USERTAG_MONITORRECTS
);
582 SetLastNtError(status
);
586 if (monitorRectList
!= NULL
&& listSize
!= 0)
588 status
= MmCopyToCaller(monitorRectList
, safeRectList
, sizeof (RECT
) * listSize
);
589 ExFreePoolWithTag(safeRectList
, USERTAG_MONITORRECTS
);
590 if (!NT_SUCCESS(status
))
592 SetLastNtError(status
);
600 /* NtUserGetMonitorInfo
602 * Retrieves information about a given monitor
607 * Handle to a monitor for which to get information
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.
616 * Pointer to a UNICODE_STRING which will recieve the device's name. The
617 * length should be CCHDEVICENAME
621 * TRUE on success; FALSE on failure (calls SetLastNtError())
626 NtUserGetMonitorInfo(
627 IN HMONITOR hMonitor
,
628 OUT LPMONITORINFO pMonitorInfo
)
631 MONITORINFOEXW MonitorInfo
;
633 DECLARE_RETURN(BOOL
);
635 TRACE("Enter NtUserGetMonitorInfo\n");
638 /* get monitor object */
639 if (!(Monitor
= UserGetMonitorObject(hMonitor
)))
641 TRACE("Couldnt find monitor 0x%lx\n", hMonitor
);
645 if(pMonitorInfo
== NULL
)
647 SetLastNtError(STATUS_INVALID_PARAMETER
);
651 /* get size of pMonitorInfo */
652 Status
= MmCopyFromCaller(&MonitorInfo
.cbSize
, &pMonitorInfo
->cbSize
, sizeof (MonitorInfo
.cbSize
));
653 if (!NT_SUCCESS(Status
))
655 SetLastNtError(Status
);
658 if ((MonitorInfo
.cbSize
!= sizeof (MONITORINFO
)) &&
659 (MonitorInfo
.cbSize
!= sizeof (MONITORINFOEXW
)))
661 SetLastNtError(STATUS_INVALID_PARAMETER
);
665 /* fill monitor info */
666 MonitorInfo
.rcMonitor
= Monitor
->rcMonitor
;
667 MonitorInfo
.rcWork
= Monitor
->rcWork
;
668 MonitorInfo
.dwFlags
= 0;
670 if (Monitor
->IsPrimary
)
671 MonitorInfo
.dwFlags
|= MONITORINFOF_PRIMARY
;
673 /* fill device name */
674 if (MonitorInfo
.cbSize
== sizeof (MONITORINFOEXW
))
676 RtlStringCbCopyNW(MonitorInfo
.szDevice
,
677 sizeof(MonitorInfo
.szDevice
),
678 Monitor
->DeviceName
.Buffer
,
679 Monitor
->DeviceName
.Length
);
683 Status
= MmCopyToCaller(pMonitorInfo
, &MonitorInfo
, MonitorInfo
.cbSize
);
684 if (!NT_SUCCESS(Status
))
686 TRACE("GetMonitorInfo: MmCopyToCaller failed\n");
687 SetLastNtError(Status
);
691 TRACE("GetMonitorInfo: success\n");
696 TRACE("Leave NtUserGetMonitorInfo, ret=%i\n",_ret_
);
701 /* NtUserMonitorFromPoint
703 * Returns a handle to the monitor containing the given point.
708 * Point for which to find monitor
711 * Specifies the behaviour if the point isn't on any of the monitors.
714 * If the point is found a handle to the monitor is returned; if not the
715 * return value depends on dwFlags
719 NtUserMonitorFromPoint(
725 HMONITOR hMonitor
= NULL
;
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;
733 /* find intersecting monitor */
734 NumMonitors
= IntGetMonitorsFromRect(&InRect
, &hMonitor
, NULL
, 1, dwFlags
);
737 return (HMONITOR
)NULL
;
743 /* NtUserMonitorFromRect
745 * Returns a handle to the monitor having the largest intersection with a
751 * Pointer to a RECT for which to find monitor
754 * Specifies the behaviour if no monitor intersects the given rect
757 * If a monitor intersects the rect a handle to it is returned; if not the
758 * return value depends on dwFlags
762 NtUserMonitorFromRect(
766 ULONG numMonitors
, iLargestArea
= 0, i
;
768 HMONITOR
*hMonitorList
;
769 HMONITOR hMonitor
= NULL
;
774 status
= MmCopyFromCaller(&rect
, pRect
, sizeof (RECT
));
775 if (!NT_SUCCESS(status
))
777 SetLastNtError(status
);
778 return (HMONITOR
)NULL
;
781 /* find intersecting monitors */
782 numMonitors
= IntGetMonitorsFromRect(&rect
, &hMonitor
, NULL
, 1, dwFlags
);
783 if (numMonitors
<= 1)
788 hMonitorList
= ExAllocatePoolWithTag(PagedPool
,
789 sizeof(HMONITOR
) * numMonitors
,
790 USERTAG_MONITORRECTS
);
791 if (hMonitorList
== NULL
)
793 /* FIXME: EngSetLastError? */
794 return (HMONITOR
)NULL
;
797 rectList
= ExAllocatePoolWithTag(PagedPool
,
798 sizeof(RECT
) * numMonitors
,
799 USERTAG_MONITORRECTS
);
800 if (rectList
== NULL
)
802 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
803 /* FIXME: EngSetLastError? */
804 return (HMONITOR
)NULL
;
807 /* get intersecting monitors */
808 numMonitors
= IntGetMonitorsFromRect(&rect
, hMonitorList
, rectList
,
810 if (numMonitors
== 0)
812 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
813 ExFreePoolWithTag(rectList
, USERTAG_MONITORRECTS
);
814 return (HMONITOR
)NULL
;
817 /* find largest intersection */
818 for (i
= 0; i
< numMonitors
; i
++)
820 ULONG area
= (rectList
[i
].right
- rectList
[i
].left
) *
821 (rectList
[i
].bottom
- rectList
[i
].top
);
822 if (area
>= iLargestArea
)
824 hMonitor
= hMonitorList
[i
];
828 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
829 ExFreePoolWithTag(rectList
, USERTAG_MONITORRECTS
);
837 NtUserMonitorFromWindow(
842 HMONITOR hMonitor
= NULL
;
844 DECLARE_RETURN(HMONITOR
);
846 TRACE("Enter NtUserMonitorFromWindow\n");
849 if (!(Window
= UserGetWindowObject(hWnd
)))
851 if (dwFlags
== MONITOR_DEFAULTTONULL
)
855 IntGetMonitorsFromRect(NULL
, &hMonitor
, NULL
, 1, dwFlags
);
859 Rect
.left
= Rect
.right
= Window
->rcWindow
.left
;
860 Rect
.top
= Rect
.bottom
= Window
->rcWindow
.bottom
;
862 IntGetMonitorsFromRect(&Rect
, &hMonitor
, NULL
, 1, dwFlags
);
867 TRACE("Leave NtUserMonitorFromWindow, ret=%i\n",_ret_
);