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 /* FIXME: find include file for these */
16 #define MONITORINFOF_PRIMARY 1
17 #define MONITOR_DEFAULTTONULL 0
18 #define MONITOR_DEFAULTTOPRIMARY 1
19 #define MONITOR_DEFAULTTONEAREST 2
24 /* GLOBALS *******************************************************************/
26 /* list of monitors */
27 static PMONITOR gMonitorList
= NULL
;
29 /* INITALIZATION FUNCTIONS ****************************************************/
36 DPRINT("Initializing monitor implementation...\n");
38 return STATUS_SUCCESS
;
44 DPRINT("Cleaning up monitor implementation...\n");
45 /* FIXME: Destroy monitor objects? */
47 return STATUS_SUCCESS
;
50 /* PRIVATE FUNCTIONS **********************************************************/
52 /* IntCreateMonitorObject
57 * If the function succeeds a pointer to a MONITOR is returned. On failure
62 IntCreateMonitorObject()
67 Monitor
= UserCreateObject(gHandleTable
, NULL
, &Handle
, otMonitor
, sizeof (MONITOR
));
73 ExInitializeFastMutex(&Monitor
->Lock
);
78 /* IntDestroyMonitorObject
81 * You have to be the owner of the monitors lock to safely destroy it.
86 * Pointer to the MONITOR which shall be deleted
90 IntDestroyMonitorObject(IN PMONITOR pMonitor
)
92 RtlFreeUnicodeString(&pMonitor
->DeviceName
);
93 UserDereferenceObject(pMonitor
);
98 UserGetMonitorObject(IN HMONITOR hMonitor
)
104 EngSetLastError(ERROR_INVALID_MONITOR_HANDLE
);
108 Monitor
= (PMONITOR
)UserGetObject(gHandleTable
, hMonitor
, otMonitor
);
111 EngSetLastError(ERROR_INVALID_MONITOR_HANDLE
);
115 ASSERT(Monitor
->head
.cLockObj
>= 0);
123 * Creates a new MONITOR and appends it to the list of monitors.
127 * pGdiDevice Pointer to the PDEVOBJ onto which the monitor was attached
128 * DisplayNumber Display Number (starting with 0)
134 IntAttachMonitor(IN PDEVOBJ
*pGdiDevice
,
135 IN ULONG DisplayNumber
)
138 WCHAR Buffer
[CCHDEVICENAME
];
140 DPRINT("Attaching monitor...\n");
142 /* create new monitor object */
143 Monitor
= IntCreateMonitorObject();
146 DPRINT("Couldnt create monitor object\n");
147 return STATUS_INSUFFICIENT_RESOURCES
;
150 _snwprintf(Buffer
, CCHDEVICENAME
, L
"\\\\.\\DISPLAY%d", DisplayNumber
+ 1);
151 if (!RtlCreateUnicodeString(&Monitor
->DeviceName
, Buffer
))
153 DPRINT("Couldn't duplicate monitor name!\n");
154 UserDereferenceObject(Monitor
);
155 UserDeleteObject(UserHMGetHandle(Monitor
), otMonitor
);
156 return STATUS_INSUFFICIENT_RESOURCES
;
159 Monitor
->GdiDevice
= pGdiDevice
;
160 Monitor
->cWndStack
= 0;
162 if (gMonitorList
== NULL
)
164 DPRINT("Primary monitor is beeing attached\n");
165 Monitor
->IsPrimary
= TRUE
;
166 gMonitorList
= Monitor
;
171 DPRINT("Additional monitor is beeing attached\n");
172 for (p
= gMonitorList
; p
->Next
!= NULL
; p
= p
->Next
)
179 IntUpdateMonitorSize(pGdiDevice
);
181 return STATUS_SUCCESS
;
186 * Deletes a MONITOR and removes it from the list of monitors.
190 * pGdiDevice Pointer to the PDEVOBJ from which the monitor was detached
196 IntDetachMonitor(IN PDEVOBJ
*pGdiDevice
)
200 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
202 if (Monitor
->GdiDevice
== pGdiDevice
)
208 /* no monitor for given device found */
209 return STATUS_INVALID_PARAMETER
;
212 if (Monitor
->IsPrimary
&& (Monitor
->Next
!= NULL
|| Monitor
->Prev
!= NULL
))
214 PMONITOR NewPrimaryMonitor
= (Monitor
->Prev
!= NULL
) ? (Monitor
->Prev
) : (Monitor
->Next
);
216 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&NewPrimaryMonitor
->Lock
);
217 NewPrimaryMonitor
->IsPrimary
= TRUE
;
218 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&NewPrimaryMonitor
->Lock
);
221 if (gMonitorList
== Monitor
)
223 gMonitorList
= Monitor
->Next
;
224 if (Monitor
->Next
!= NULL
)
225 Monitor
->Next
->Prev
= NULL
;
229 Monitor
->Prev
->Next
= Monitor
->Next
;
230 if (Monitor
->Next
!= NULL
)
231 Monitor
->Next
->Prev
= Monitor
->Prev
;
234 if (Monitor
->hrgnMonitor
)
235 REGION_FreeRgnByHandle(Monitor
->hrgnMonitor
);
237 IntDestroyMonitorObject(Monitor
);
239 return STATUS_SUCCESS
;
242 /* IntUpdateMonitorSize
244 * Reset size of the monitor using atached device
249 * pGdiDevice Pointer to the PDEVOBJ, which size has changed
255 IntUpdateMonitorSize(IN PDEVOBJ
*pGdiDevice
)
259 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
261 if (Monitor
->GdiDevice
== pGdiDevice
)
267 /* no monitor for given device found */
268 return STATUS_INVALID_PARAMETER
;
271 Monitor
->rcMonitor
.left
= 0;
272 Monitor
->rcMonitor
.top
= 0;
273 Monitor
->rcMonitor
.right
= Monitor
->rcMonitor
.left
+ Monitor
->GdiDevice
->gdiinfo
.ulHorzRes
;
274 Monitor
->rcMonitor
.bottom
= Monitor
->rcMonitor
.top
+ Monitor
->GdiDevice
->gdiinfo
.ulVertRes
;
275 Monitor
->rcWork
= Monitor
->rcMonitor
;
277 if (Monitor
->hrgnMonitor
)
279 GDIOBJ_SetOwnership(Monitor
->hrgnMonitor
, PsGetCurrentProcess());
280 REGION_FreeRgnByHandle(Monitor
->hrgnMonitor
);
283 Monitor
->hrgnMonitor
= IntSysCreateRectRgnIndirect( &Monitor
->rcMonitor
);
285 IntGdiSetRegionOwner(Monitor
->hrgnMonitor
, GDI_OBJ_HMGR_PUBLIC
);
287 return STATUS_SUCCESS
;
290 /* IntGetPrimaryMonitor
292 * Returns a PMONITOR for the primary monitor
299 IntGetPrimaryMonitor()
303 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
305 /* FIXME: I guess locking the monitor is not neccessary to read 1 int */
306 if (Monitor
->IsPrimary
)
313 /* IntGetMonitorsFromRect
315 * Returns a list of monitor handles/rectangles. The rectangles in the list are
316 * the areas of intersection with the monitors.
321 * Rectangle in desktop coordinates. If this is NULL all monitors are
322 * returned and the rect list is filled with the sizes of the monitors.
325 * Pointer to an array of HMONITOR which is filled with monitor handles.
329 * Pointer to an array of RECT which is filled with intersection rects in
330 * desktop coordinates.
331 * Can be NULL, will be ignored if no intersecting monitor is found and
332 * flags is MONITOR_DEFAULTTONEAREST
335 * Size of the hMonitorList and monitorRectList arguments. If this is zero
336 * hMonitorList and monitorRectList are ignored.
339 * Either 0 or MONITOR_DEFAULTTONEAREST (ignored if rect is NULL)
342 * The number of monitors which intersect the specified region.
346 IntGetMonitorsFromRect(OPTIONAL IN LPCRECTL pRect
,
347 OPTIONAL OUT HMONITOR
*hMonitorList
,
348 OPTIONAL OUT PRECTL monitorRectList
,
349 OPTIONAL IN DWORD listSize
,
350 OPTIONAL IN DWORD flags
)
352 PMONITOR Monitor
, NearestMonitor
= NULL
, PrimaryMonitor
= NULL
;
354 ULONG iNearestDistance
= 0xffffffff;
356 /* Find monitors which intersect the rectangle */
357 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
359 RECTL MonitorRect
, IntersectionRect
;
361 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&Monitor
->Lock
);
362 MonitorRect
= Monitor
->rcMonitor
;
363 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&Monitor
->Lock
);
365 DPRINT("MonitorRect: left = %d, top = %d, right = %d, bottom = %d\n",
366 MonitorRect
.left
, MonitorRect
.top
, MonitorRect
.right
, MonitorRect
.bottom
);
368 if (flags
== MONITOR_DEFAULTTOPRIMARY
&& Monitor
->IsPrimary
)
370 PrimaryMonitor
= Monitor
;
373 /* Check if a rect is given */
376 /* No rect given, so use the full monitor rect */
377 IntersectionRect
= MonitorRect
;
380 /* We have a rect, calculate intersection */
381 else if (!RECTL_bIntersectRect(&IntersectionRect
, &MonitorRect
, pRect
))
383 /* Rects did not intersect */
384 if (flags
== MONITOR_DEFAULTTONEAREST
)
386 ULONG cx
, cy
, iDistance
;
388 /* Get x and y distance */
389 cx
= min(abs(MonitorRect
.left
- pRect
->right
),
390 abs(pRect
->left
- MonitorRect
.right
));
391 cy
= min(abs(MonitorRect
.top
- pRect
->bottom
),
392 abs(pRect
->top
- MonitorRect
.bottom
));
394 /* Calculate distance square */
395 iDistance
= cx
* cx
+ cy
* cy
;
397 /* Check if this is the new nearest monitor */
398 if (iDistance
< iNearestDistance
)
400 iNearestDistance
= iDistance
;
401 NearestMonitor
= Monitor
;
408 /* Check if there's space in the buffer */
409 if (iCount
< listSize
)
411 if (hMonitorList
!= NULL
)
412 hMonitorList
[iCount
] = UserHMGetHandle(Monitor
);
413 if (monitorRectList
!= NULL
)
414 monitorRectList
[iCount
] = IntersectionRect
;
417 /* Increase count of found monitors */
421 /* Found nothing intersecting? */
424 /* Check if we shall default to the nearest monitor */
425 if (flags
== MONITOR_DEFAULTTONEAREST
&& NearestMonitor
)
427 if (hMonitorList
&& listSize
> 0)
428 hMonitorList
[iCount
] = UserHMGetHandle(NearestMonitor
);
431 /* Check if we shall default to the primary monitor */
432 else if (flags
== MONITOR_DEFAULTTOPRIMARY
&& PrimaryMonitor
)
434 if (hMonitorList
!= NULL
&& listSize
> 0)
435 hMonitorList
[iCount
] = UserHMGetHandle(PrimaryMonitor
);
443 /* PUBLIC FUNCTIONS ***********************************************************/
445 /* NtUserEnumDisplayMonitors
447 * Enumerates display monitors which intersect the given HDC/cliprect
452 * Handle to a DC for which to enum intersecting monitors. If this is NULL
453 * it returns all monitors which are part of the current virtual screen.
456 * Clipping rectangle with coordinate system origin at the DCs origin if the
457 * given HDC is not NULL or in virtual screen coordinated if it is NULL.
461 * Pointer to an array of HMONITOR which is filled with monitor handles.
465 * Pointer to an array of RECT which is filled with intersection rectangles.
469 * Size of the hMonitorList and monitorRectList arguments. If this is zero
470 * hMonitorList and monitorRectList are ignored.
473 * The number of monitors which intersect the specified region or -1 on failure.
477 NtUserEnumDisplayMonitors(
479 OPTIONAL IN LPCRECTL pRect
,
480 OPTIONAL OUT HMONITOR
*hMonitorList
,
481 OPTIONAL OUT PRECTL monitorRectList
,
482 OPTIONAL IN DWORD listSize
)
485 HMONITOR
*safeHMonitorList
= NULL
;
486 PRECTL safeRectList
= NULL
;
494 status
= MmCopyFromCaller(&rect
, pRect
, sizeof (RECT
));
495 if (!NT_SUCCESS(status
))
497 DPRINT("MmCopyFromCaller() failed!\n");
498 SetLastNtError(status
);
508 /* get visible region bounding rect */
512 DPRINT("DC_LockDc() failed!\n");
513 /* FIXME: setlasterror? */
516 regionType
= REGION_GetRgnBox(dc
->prgnVis
, &dcRect
);
521 DPRINT("NtGdiGetRgnBox() failed!\n");
524 if (regionType
== NULLREGION
)
526 if (regionType
== COMPLEXREGION
)
531 /* if hDC and pRect are given the area of interest is pRect with
532 coordinate origin at the DC position */
535 rect
.left
+= dcRect
.left
;
536 rect
.right
+= dcRect
.left
;
537 rect
.top
+= dcRect
.top
;
538 rect
.bottom
+= dcRect
.top
;
540 /* if hDC is given and pRect is not the area of interest is the
541 bounding rect of hDC */
548 if (hDC
== NULL
&& pRect
== NULL
)
553 /* find intersecting monitors */
554 numMonitors
= IntGetMonitorsFromRect(myRect
, NULL
, NULL
, 0, 0);
555 if (numMonitors
== 0 || listSize
== 0 ||
556 (hMonitorList
== NULL
&& monitorRectList
== NULL
))
558 DPRINT("numMonitors = %d\n", numMonitors
);
562 if (hMonitorList
!= NULL
&& listSize
!= 0)
564 safeHMonitorList
= ExAllocatePoolWithTag(PagedPool
, sizeof (HMONITOR
) * listSize
, USERTAG_MONITORRECTS
);
565 if (safeHMonitorList
== NULL
)
567 /* FIXME: EngSetLastError? */
571 if (monitorRectList
!= NULL
&& listSize
!= 0)
573 safeRectList
= ExAllocatePoolWithTag(PagedPool
, sizeof (RECT
) * listSize
, USERTAG_MONITORRECTS
);
574 if (safeRectList
== NULL
)
576 ExFreePoolWithTag(safeHMonitorList
, USERTAG_MONITORRECTS
);
577 /* FIXME: EngSetLastError? */
582 /* get intersecting monitors */
583 numMonitors
= IntGetMonitorsFromRect(myRect
, safeHMonitorList
, safeRectList
,
586 if (hDC
!= NULL
&& pRect
!= NULL
&& safeRectList
!= NULL
)
587 for (i
= 0; i
< numMonitors
; i
++)
589 safeRectList
[i
].left
-= dcRect
.left
;
590 safeRectList
[i
].right
-= dcRect
.left
;
591 safeRectList
[i
].top
-= dcRect
.top
;
592 safeRectList
[i
].bottom
-= dcRect
.top
;
596 if (hMonitorList
!= NULL
&& listSize
!= 0)
598 status
= MmCopyToCaller(hMonitorList
, safeHMonitorList
, sizeof (HMONITOR
) * listSize
);
599 ExFreePool(safeHMonitorList
);
600 if (!NT_SUCCESS(status
))
602 ExFreePoolWithTag(safeRectList
, USERTAG_MONITORRECTS
);
603 SetLastNtError(status
);
607 if (monitorRectList
!= NULL
&& listSize
!= 0)
609 status
= MmCopyToCaller(monitorRectList
, safeRectList
, sizeof (RECT
) * listSize
);
610 ExFreePoolWithTag(safeRectList
, USERTAG_MONITORRECTS
);
611 if (!NT_SUCCESS(status
))
613 SetLastNtError(status
);
621 /* NtUserGetMonitorInfo
623 * Retrieves information about a given monitor
628 * Handle to a monitor for which to get information
631 * Pointer to a MONITORINFO struct which is filled with the information.
632 * The cbSize member must be set to sizeof(MONITORINFO) or
633 * sizeof(MONITORINFOEX). Even if set to sizeof(MONITORINFOEX) only parts
634 * from MONITORINFO will be filled.
637 * Pointer to a UNICODE_STRING which will recieve the device's name. The
638 * length should be CCHDEVICENAME
642 * TRUE on success; FALSE on failure (calls SetLastNtError())
647 NtUserGetMonitorInfo(
648 IN HMONITOR hMonitor
,
649 OUT LPMONITORINFO pMonitorInfo
)
652 MONITORINFOEXW MonitorInfo
;
654 DECLARE_RETURN(BOOL
);
656 DPRINT("Enter NtUserGetMonitorInfo\n");
659 /* get monitor object */
660 if (!(Monitor
= UserGetMonitorObject(hMonitor
)))
662 DPRINT("Couldnt find monitor 0x%lx\n", hMonitor
);
666 if(pMonitorInfo
== NULL
)
668 SetLastNtError(STATUS_INVALID_PARAMETER
);
672 /* get size of pMonitorInfo */
673 Status
= MmCopyFromCaller(&MonitorInfo
.cbSize
, &pMonitorInfo
->cbSize
, sizeof (MonitorInfo
.cbSize
));
674 if (!NT_SUCCESS(Status
))
676 SetLastNtError(Status
);
679 if ((MonitorInfo
.cbSize
!= sizeof (MONITORINFO
)) &&
680 (MonitorInfo
.cbSize
!= sizeof (MONITORINFOEXW
)))
682 SetLastNtError(STATUS_INVALID_PARAMETER
);
686 /* fill monitor info */
687 MonitorInfo
.rcMonitor
= Monitor
->rcMonitor
;
688 MonitorInfo
.rcWork
= Monitor
->rcWork
;
689 MonitorInfo
.dwFlags
= 0;
691 if (Monitor
->IsPrimary
)
692 MonitorInfo
.dwFlags
|= MONITORINFOF_PRIMARY
;
694 /* fill device name */
695 if (MonitorInfo
.cbSize
== sizeof (MONITORINFOEXW
))
697 RtlStringCbCopyNW(MonitorInfo
.szDevice
,
698 sizeof(MonitorInfo
.szDevice
),
699 Monitor
->DeviceName
.Buffer
,
700 Monitor
->DeviceName
.Length
);
704 Status
= MmCopyToCaller(pMonitorInfo
, &MonitorInfo
, MonitorInfo
.cbSize
);
705 if (!NT_SUCCESS(Status
))
707 DPRINT("GetMonitorInfo: MmCopyToCaller failed\n");
708 SetLastNtError(Status
);
712 DPRINT("GetMonitorInfo: success\n");
717 DPRINT("Leave NtUserGetMonitorInfo, ret=%i\n",_ret_
);
722 /* NtUserMonitorFromPoint
724 * Returns a handle to the monitor containing the given point.
729 * Point for which to find monitor
732 * Specifies the behaviour if the point isn't on any of the monitors.
735 * If the point is found a handle to the monitor is returned; if not the
736 * return value depends on dwFlags
740 NtUserMonitorFromPoint(
746 HMONITOR hMonitor
= NULL
;
748 /* fill inRect (bottom-right exclusive) */
749 InRect
.left
= point
.x
;
750 InRect
.right
= point
.x
+ 1;
751 InRect
.top
= point
.y
;
752 InRect
.bottom
= point
.y
+ 1;
754 /* find intersecting monitor */
755 NumMonitors
= IntGetMonitorsFromRect(&InRect
, &hMonitor
, NULL
, 1, dwFlags
);
758 return (HMONITOR
)NULL
;
764 /* NtUserMonitorFromRect
766 * Returns a handle to the monitor having the largest intersection with a
772 * Pointer to a RECT for which to find monitor
775 * Specifies the behaviour if no monitor intersects the given rect
778 * If a monitor intersects the rect a handle to it is returned; if not the
779 * return value depends on dwFlags
783 NtUserMonitorFromRect(
787 ULONG numMonitors
, iLargestArea
= 0, i
;
789 HMONITOR
*hMonitorList
;
790 HMONITOR hMonitor
= NULL
;
795 status
= MmCopyFromCaller(&rect
, pRect
, sizeof (RECT
));
796 if (!NT_SUCCESS(status
))
798 SetLastNtError(status
);
799 return (HMONITOR
)NULL
;
802 /* find intersecting monitors */
803 numMonitors
= IntGetMonitorsFromRect(&rect
, &hMonitor
, NULL
, 1, dwFlags
);
804 if (numMonitors
<= 1)
809 hMonitorList
= ExAllocatePoolWithTag(PagedPool
,
810 sizeof(HMONITOR
) * numMonitors
,
811 USERTAG_MONITORRECTS
);
812 if (hMonitorList
== NULL
)
814 /* FIXME: EngSetLastError? */
815 return (HMONITOR
)NULL
;
818 rectList
= ExAllocatePoolWithTag(PagedPool
,
819 sizeof(RECT
) * numMonitors
,
820 USERTAG_MONITORRECTS
);
821 if (rectList
== NULL
)
823 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
824 /* FIXME: EngSetLastError? */
825 return (HMONITOR
)NULL
;
828 /* get intersecting monitors */
829 numMonitors
= IntGetMonitorsFromRect(&rect
, hMonitorList
, rectList
,
831 if (numMonitors
== 0)
833 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
834 ExFreePoolWithTag(rectList
, USERTAG_MONITORRECTS
);
835 return (HMONITOR
)NULL
;
838 /* find largest intersection */
839 for (i
= 0; i
< numMonitors
; i
++)
841 ULONG area
= (rectList
[i
].right
- rectList
[i
].left
) *
842 (rectList
[i
].bottom
- rectList
[i
].top
);
843 if (area
>= iLargestArea
)
845 hMonitor
= hMonitorList
[i
];
849 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
850 ExFreePoolWithTag(rectList
, USERTAG_MONITORRECTS
);
858 NtUserMonitorFromWindow(
863 HMONITOR hMonitor
= NULL
;
865 DECLARE_RETURN(HMONITOR
);
867 DPRINT("Enter NtUserMonitorFromWindow\n");
870 if (!(Window
= UserGetWindowObject(hWnd
)))
872 if (dwFlags
== MONITOR_DEFAULTTONULL
)
876 IntGetMonitorsFromRect(NULL
, &hMonitor
, NULL
, 1, dwFlags
);
880 Rect
.left
= Rect
.right
= Window
->rcWindow
.left
;
881 Rect
.top
= Rect
.bottom
= Window
->rcWindow
.bottom
;
883 IntGetMonitorsFromRect(&Rect
, &hMonitor
, NULL
, 1, dwFlags
);
888 DPRINT("Leave NtUserMonitorFromWindow, ret=%i\n",_ret_
);