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
->rcMonitor
.left
= 0;
161 Monitor
->rcMonitor
.top
= 0;
162 Monitor
->rcMonitor
.right
= Monitor
->rcMonitor
.left
+ pGdiDevice
->gdiinfo
.ulHorzRes
;
163 Monitor
->rcMonitor
.bottom
= Monitor
->rcMonitor
.top
+ pGdiDevice
->gdiinfo
.ulVertRes
;
164 Monitor
->rcWork
= Monitor
->rcMonitor
;
165 Monitor
->cWndStack
= 0;
167 Monitor
->hrgnMonitor
= IntSysCreateRectRgnIndirect( &Monitor
->rcMonitor
);
169 IntGdiSetRegionOwner(Monitor
->hrgnMonitor
, GDI_OBJ_HMGR_PUBLIC
);
171 if (gMonitorList
== NULL
)
173 DPRINT("Primary monitor is beeing attached\n");
174 Monitor
->IsPrimary
= TRUE
;
175 gMonitorList
= Monitor
;
180 DPRINT("Additional monitor is beeing attached\n");
181 for (p
= gMonitorList
; p
->Next
!= NULL
; p
= p
->Next
)
188 return STATUS_SUCCESS
;
193 * Deletes a MONITOR and removes it from the list of monitors.
197 * pGdiDevice Pointer to the PDEVOBJ from which the monitor was detached
203 IntDetachMonitor(IN PDEVOBJ
*pGdiDevice
)
207 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
209 if (Monitor
->GdiDevice
== pGdiDevice
)
215 /* no monitor for given device found */
216 return STATUS_INVALID_PARAMETER
;
219 if (Monitor
->IsPrimary
&& (Monitor
->Next
!= NULL
|| Monitor
->Prev
!= NULL
))
221 PMONITOR NewPrimaryMonitor
= (Monitor
->Prev
!= NULL
) ? (Monitor
->Prev
) : (Monitor
->Next
);
223 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&NewPrimaryMonitor
->Lock
);
224 NewPrimaryMonitor
->IsPrimary
= TRUE
;
225 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&NewPrimaryMonitor
->Lock
);
228 if (gMonitorList
== Monitor
)
230 gMonitorList
= Monitor
->Next
;
231 if (Monitor
->Next
!= NULL
)
232 Monitor
->Next
->Prev
= NULL
;
236 Monitor
->Prev
->Next
= Monitor
->Next
;
237 if (Monitor
->Next
!= NULL
)
238 Monitor
->Next
->Prev
= Monitor
->Prev
;
241 if (Monitor
->hrgnMonitor
)
242 REGION_FreeRgnByHandle(Monitor
->hrgnMonitor
);
244 IntDestroyMonitorObject(Monitor
);
246 return STATUS_SUCCESS
;
249 /* IntResetMonitorSize
251 * Reset size of the monitor using atached device
256 * pGdiDevice Pointer to the PDEVOBJ, which size has changed
262 IntResetMonitorSize(IN PDEVOBJ
*pGdiDevice
)
266 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
268 if (Monitor
->GdiDevice
== pGdiDevice
)
274 /* no monitor for given device found */
275 return STATUS_INVALID_PARAMETER
;
278 Monitor
->rcMonitor
.left
= 0;
279 Monitor
->rcMonitor
.top
= 0;
280 Monitor
->rcMonitor
.right
= Monitor
->rcMonitor
.left
+ Monitor
->GdiDevice
->gdiinfo
.ulHorzRes
;
281 Monitor
->rcMonitor
.bottom
= Monitor
->rcMonitor
.top
+ Monitor
->GdiDevice
->gdiinfo
.ulVertRes
;
282 Monitor
->rcWork
= Monitor
->rcMonitor
;
284 if (Monitor
->hrgnMonitor
)
285 REGION_FreeRgnByHandle(Monitor
->hrgnMonitor
);
287 Monitor
->hrgnMonitor
= IntSysCreateRectRgnIndirect( &Monitor
->rcMonitor
);
289 IntGdiSetRegionOwner(Monitor
->hrgnMonitor
, GDI_OBJ_HMGR_PUBLIC
);
291 return STATUS_SUCCESS
;
294 /* IntGetPrimaryMonitor
296 * Returns a PMONITOR for the primary monitor
303 IntGetPrimaryMonitor()
307 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
309 /* FIXME: I guess locking the monitor is not neccessary to read 1 int */
310 if (Monitor
->IsPrimary
)
317 /* IntGetMonitorsFromRect
319 * Returns a list of monitor handles/rectangles. The rectangles in the list are
320 * the areas of intersection with the monitors.
325 * Rectangle in desktop coordinates. If this is NULL all monitors are
326 * returned and the rect list is filled with the sizes of the monitors.
329 * Pointer to an array of HMONITOR which is filled with monitor handles.
333 * Pointer to an array of RECT which is filled with intersection rects in
334 * desktop coordinates.
335 * Can be NULL, will be ignored if no intersecting monitor is found and
336 * flags is MONITOR_DEFAULTTONEAREST
339 * Size of the hMonitorList and monitorRectList arguments. If this is zero
340 * hMonitorList and monitorRectList are ignored.
343 * Either 0 or MONITOR_DEFAULTTONEAREST (ignored if rect is NULL)
346 * The number of monitors which intersect the specified region.
350 IntGetMonitorsFromRect(OPTIONAL IN LPCRECTL pRect
,
351 OPTIONAL OUT HMONITOR
*hMonitorList
,
352 OPTIONAL OUT PRECTL monitorRectList
,
353 OPTIONAL IN DWORD listSize
,
354 OPTIONAL IN DWORD flags
)
356 PMONITOR Monitor
, NearestMonitor
= NULL
, PrimaryMonitor
= NULL
;
358 ULONG iNearestDistance
= 0xffffffff;
360 /* Find monitors which intersect the rectangle */
361 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
363 RECTL MonitorRect
, IntersectionRect
;
365 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&Monitor
->Lock
);
366 MonitorRect
= Monitor
->rcMonitor
;
367 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&Monitor
->Lock
);
369 DPRINT("MonitorRect: left = %d, top = %d, right = %d, bottom = %d\n",
370 MonitorRect
.left
, MonitorRect
.top
, MonitorRect
.right
, MonitorRect
.bottom
);
372 if (flags
== MONITOR_DEFAULTTOPRIMARY
&& Monitor
->IsPrimary
)
374 PrimaryMonitor
= Monitor
;
377 /* Check if a rect is given */
380 /* No rect given, so use the full monitor rect */
381 IntersectionRect
= MonitorRect
;
384 /* We have a rect, calculate intersection */
385 else if (!RECTL_bIntersectRect(&IntersectionRect
, &MonitorRect
, pRect
))
387 /* Rects did not intersect */
388 if (flags
== MONITOR_DEFAULTTONEAREST
)
390 ULONG cx
, cy
, iDistance
;
392 /* Get x and y distance */
393 cx
= min(abs(MonitorRect
.left
- pRect
->right
),
394 abs(pRect
->left
- MonitorRect
.right
));
395 cy
= min(abs(MonitorRect
.top
- pRect
->bottom
),
396 abs(pRect
->top
- MonitorRect
.bottom
));
398 /* Calculate distance square */
399 iDistance
= cx
* cx
+ cy
* cy
;
401 /* Check if this is the new nearest monitor */
402 if (iDistance
< iNearestDistance
)
404 iNearestDistance
= iDistance
;
405 NearestMonitor
= Monitor
;
412 /* Check if there's space in the buffer */
413 if (iCount
< listSize
)
415 if (hMonitorList
!= NULL
)
416 hMonitorList
[iCount
] = UserHMGetHandle(Monitor
);
417 if (monitorRectList
!= NULL
)
418 monitorRectList
[iCount
] = IntersectionRect
;
421 /* Increase count of found monitors */
425 /* Found nothing intersecting? */
428 /* Check if we shall default to the nearest monitor */
429 if (flags
== MONITOR_DEFAULTTONEAREST
&& NearestMonitor
)
431 if (hMonitorList
&& listSize
> 0)
432 hMonitorList
[iCount
] = UserHMGetHandle(NearestMonitor
);
435 /* Check if we shall default to the primary monitor */
436 else if (flags
== MONITOR_DEFAULTTOPRIMARY
&& PrimaryMonitor
)
438 if (hMonitorList
!= NULL
&& listSize
> 0)
439 hMonitorList
[iCount
] = UserHMGetHandle(PrimaryMonitor
);
447 /* PUBLIC FUNCTIONS ***********************************************************/
449 /* NtUserEnumDisplayMonitors
451 * Enumerates display monitors which intersect the given HDC/cliprect
456 * Handle to a DC for which to enum intersecting monitors. If this is NULL
457 * it returns all monitors which are part of the current virtual screen.
460 * Clipping rectangle with coordinate system origin at the DCs origin if the
461 * given HDC is not NULL or in virtual screen coordinated if it is NULL.
465 * Pointer to an array of HMONITOR which is filled with monitor handles.
469 * Pointer to an array of RECT which is filled with intersection rectangles.
473 * Size of the hMonitorList and monitorRectList arguments. If this is zero
474 * hMonitorList and monitorRectList are ignored.
477 * The number of monitors which intersect the specified region or -1 on failure.
481 NtUserEnumDisplayMonitors(
483 OPTIONAL IN LPCRECTL pRect
,
484 OPTIONAL OUT HMONITOR
*hMonitorList
,
485 OPTIONAL OUT PRECTL monitorRectList
,
486 OPTIONAL IN DWORD listSize
)
489 HMONITOR
*safeHMonitorList
= NULL
;
490 PRECTL safeRectList
= NULL
;
498 status
= MmCopyFromCaller(&rect
, pRect
, sizeof (RECT
));
499 if (!NT_SUCCESS(status
))
501 DPRINT("MmCopyFromCaller() failed!\n");
502 SetLastNtError(status
);
512 /* get visible region bounding rect */
516 DPRINT("DC_LockDc() failed!\n");
517 /* FIXME: setlasterror? */
520 regionType
= REGION_GetRgnBox(dc
->prgnVis
, &dcRect
);
525 DPRINT("NtGdiGetRgnBox() failed!\n");
528 if (regionType
== NULLREGION
)
530 if (regionType
== COMPLEXREGION
)
535 /* if hDC and pRect are given the area of interest is pRect with
536 coordinate origin at the DC position */
539 rect
.left
+= dcRect
.left
;
540 rect
.right
+= dcRect
.left
;
541 rect
.top
+= dcRect
.top
;
542 rect
.bottom
+= dcRect
.top
;
544 /* if hDC is given and pRect is not the area of interest is the
545 bounding rect of hDC */
552 if (hDC
== NULL
&& pRect
== NULL
)
557 /* find intersecting monitors */
558 numMonitors
= IntGetMonitorsFromRect(myRect
, NULL
, NULL
, 0, 0);
559 if (numMonitors
== 0 || listSize
== 0 ||
560 (hMonitorList
== NULL
&& monitorRectList
== NULL
))
562 DPRINT("numMonitors = %d\n", numMonitors
);
566 if (hMonitorList
!= NULL
&& listSize
!= 0)
568 safeHMonitorList
= ExAllocatePoolWithTag(PagedPool
, sizeof (HMONITOR
) * listSize
, USERTAG_MONITORRECTS
);
569 if (safeHMonitorList
== NULL
)
571 /* FIXME: EngSetLastError? */
575 if (monitorRectList
!= NULL
&& listSize
!= 0)
577 safeRectList
= ExAllocatePoolWithTag(PagedPool
, sizeof (RECT
) * listSize
, USERTAG_MONITORRECTS
);
578 if (safeRectList
== NULL
)
580 ExFreePoolWithTag(safeHMonitorList
, USERTAG_MONITORRECTS
);
581 /* FIXME: EngSetLastError? */
586 /* get intersecting monitors */
587 numMonitors
= IntGetMonitorsFromRect(myRect
, safeHMonitorList
, safeRectList
,
590 if (hDC
!= NULL
&& pRect
!= NULL
&& safeRectList
!= NULL
)
591 for (i
= 0; i
< numMonitors
; i
++)
593 safeRectList
[i
].left
-= dcRect
.left
;
594 safeRectList
[i
].right
-= dcRect
.left
;
595 safeRectList
[i
].top
-= dcRect
.top
;
596 safeRectList
[i
].bottom
-= dcRect
.top
;
600 if (hMonitorList
!= NULL
&& listSize
!= 0)
602 status
= MmCopyToCaller(hMonitorList
, safeHMonitorList
, sizeof (HMONITOR
) * listSize
);
603 ExFreePool(safeHMonitorList
);
604 if (!NT_SUCCESS(status
))
606 ExFreePoolWithTag(safeRectList
, USERTAG_MONITORRECTS
);
607 SetLastNtError(status
);
611 if (monitorRectList
!= NULL
&& listSize
!= 0)
613 status
= MmCopyToCaller(monitorRectList
, safeRectList
, sizeof (RECT
) * listSize
);
614 ExFreePoolWithTag(safeRectList
, USERTAG_MONITORRECTS
);
615 if (!NT_SUCCESS(status
))
617 SetLastNtError(status
);
625 /* NtUserGetMonitorInfo
627 * Retrieves information about a given monitor
632 * Handle to a monitor for which to get information
635 * Pointer to a MONITORINFO struct which is filled with the information.
636 * The cbSize member must be set to sizeof(MONITORINFO) or
637 * sizeof(MONITORINFOEX). Even if set to sizeof(MONITORINFOEX) only parts
638 * from MONITORINFO will be filled.
641 * Pointer to a UNICODE_STRING which will recieve the device's name. The
642 * length should be CCHDEVICENAME
646 * TRUE on success; FALSE on failure (calls SetLastNtError())
651 NtUserGetMonitorInfo(
652 IN HMONITOR hMonitor
,
653 OUT LPMONITORINFO pMonitorInfo
)
656 MONITORINFOEXW MonitorInfo
;
658 DECLARE_RETURN(BOOL
);
660 DPRINT("Enter NtUserGetMonitorInfo\n");
663 /* get monitor object */
664 if (!(Monitor
= UserGetMonitorObject(hMonitor
)))
666 DPRINT("Couldnt find monitor 0x%lx\n", hMonitor
);
670 if(pMonitorInfo
== NULL
)
672 SetLastNtError(STATUS_INVALID_PARAMETER
);
676 /* get size of pMonitorInfo */
677 Status
= MmCopyFromCaller(&MonitorInfo
.cbSize
, &pMonitorInfo
->cbSize
, sizeof (MonitorInfo
.cbSize
));
678 if (!NT_SUCCESS(Status
))
680 SetLastNtError(Status
);
683 if ((MonitorInfo
.cbSize
!= sizeof (MONITORINFO
)) &&
684 (MonitorInfo
.cbSize
!= sizeof (MONITORINFOEXW
)))
686 SetLastNtError(STATUS_INVALID_PARAMETER
);
690 /* fill monitor info */
691 MonitorInfo
.rcMonitor
= Monitor
->rcMonitor
;
692 MonitorInfo
.rcWork
= Monitor
->rcWork
;
693 MonitorInfo
.dwFlags
= 0;
695 if (Monitor
->IsPrimary
)
696 MonitorInfo
.dwFlags
|= MONITORINFOF_PRIMARY
;
698 /* fill device name */
699 if (MonitorInfo
.cbSize
== sizeof (MONITORINFOEXW
))
702 INT len
= Monitor
->DeviceName
.Length
;
703 if (len
>= CCHDEVICENAME
* sizeof (WCHAR
))
704 len
= (CCHDEVICENAME
- 1) * sizeof (WCHAR
);
706 memcpy(MonitorInfo
.szDevice
, Monitor
->DeviceName
.Buffer
, len
);
707 memcpy(MonitorInfo
.szDevice
+ (len
/ sizeof (WCHAR
)), &nul
, sizeof (WCHAR
));
711 Status
= MmCopyToCaller(pMonitorInfo
, &MonitorInfo
, MonitorInfo
.cbSize
);
712 if (!NT_SUCCESS(Status
))
714 DPRINT("GetMonitorInfo: MmCopyToCaller failed\n");
715 SetLastNtError(Status
);
719 DPRINT("GetMonitorInfo: success\n");
724 DPRINT("Leave NtUserGetMonitorInfo, ret=%i\n",_ret_
);
729 /* NtUserMonitorFromPoint
731 * Returns a handle to the monitor containing the given point.
736 * Point for which to find monitor
739 * Specifies the behaviour if the point isn't on any of the monitors.
742 * If the point is found a handle to the monitor is returned; if not the
743 * return value depends on dwFlags
747 NtUserMonitorFromPoint(
753 HMONITOR hMonitor
= NULL
;
755 /* fill inRect (bottom-right exclusive) */
756 InRect
.left
= point
.x
;
757 InRect
.right
= point
.x
+ 1;
758 InRect
.top
= point
.y
;
759 InRect
.bottom
= point
.y
+ 1;
761 /* find intersecting monitor */
762 NumMonitors
= IntGetMonitorsFromRect(&InRect
, &hMonitor
, NULL
, 1, dwFlags
);
765 return (HMONITOR
)NULL
;
771 /* NtUserMonitorFromRect
773 * Returns a handle to the monitor having the largest intersection with a
779 * Pointer to a RECT for which to find monitor
782 * Specifies the behaviour if no monitor intersects the given rect
785 * If a monitor intersects the rect a handle to it is returned; if not the
786 * return value depends on dwFlags
790 NtUserMonitorFromRect(
794 ULONG numMonitors
, iLargestArea
= 0, i
;
796 HMONITOR
*hMonitorList
;
797 HMONITOR hMonitor
= NULL
;
802 status
= MmCopyFromCaller(&rect
, pRect
, sizeof (RECT
));
803 if (!NT_SUCCESS(status
))
805 SetLastNtError(status
);
806 return (HMONITOR
)NULL
;
809 /* find intersecting monitors */
810 numMonitors
= IntGetMonitorsFromRect(&rect
, &hMonitor
, NULL
, 1, dwFlags
);
811 if (numMonitors
<= 1)
816 hMonitorList
= ExAllocatePoolWithTag(PagedPool
,
817 sizeof(HMONITOR
) * numMonitors
,
818 USERTAG_MONITORRECTS
);
819 if (hMonitorList
== NULL
)
821 /* FIXME: EngSetLastError? */
822 return (HMONITOR
)NULL
;
825 rectList
= ExAllocatePoolWithTag(PagedPool
,
826 sizeof(RECT
) * numMonitors
,
827 USERTAG_MONITORRECTS
);
828 if (rectList
== NULL
)
830 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
831 /* FIXME: EngSetLastError? */
832 return (HMONITOR
)NULL
;
835 /* get intersecting monitors */
836 numMonitors
= IntGetMonitorsFromRect(&rect
, hMonitorList
, rectList
,
838 if (numMonitors
== 0)
840 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
841 ExFreePoolWithTag(rectList
, USERTAG_MONITORRECTS
);
842 return (HMONITOR
)NULL
;
845 /* find largest intersection */
846 for (i
= 0; i
< numMonitors
; i
++)
848 ULONG area
= (rectList
[i
].right
- rectList
[i
].left
) *
849 (rectList
[i
].bottom
- rectList
[i
].top
);
850 if (area
>= iLargestArea
)
852 hMonitor
= hMonitorList
[i
];
856 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
857 ExFreePoolWithTag(rectList
, USERTAG_MONITORRECTS
);
865 NtUserMonitorFromWindow(
870 HMONITOR hMonitor
= NULL
;
872 DECLARE_RETURN(HMONITOR
);
874 DPRINT("Enter NtUserMonitorFromWindow\n");
877 if (!(Window
= UserGetWindowObject(hWnd
)))
879 if (dwFlags
== MONITOR_DEFAULTTONULL
)
883 IntGetMonitorsFromRect(NULL
, &hMonitor
, NULL
, 1, dwFlags
);
887 Rect
.left
= Rect
.right
= Window
->rcWindow
.left
;
888 Rect
.top
= Rect
.bottom
= Window
->rcWindow
.bottom
;
890 IntGetMonitorsFromRect(&Rect
, &hMonitor
, NULL
, 1, dwFlags
);
895 DPRINT("Leave NtUserMonitorFromWindow, ret=%i\n",_ret_
);