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
)
286 GDIOBJ_SetOwnership(Monitor
->hrgnMonitor
, PsGetCurrentProcess());
287 REGION_FreeRgnByHandle(Monitor
->hrgnMonitor
);
290 Monitor
->hrgnMonitor
= IntSysCreateRectRgnIndirect( &Monitor
->rcMonitor
);
292 IntGdiSetRegionOwner(Monitor
->hrgnMonitor
, GDI_OBJ_HMGR_PUBLIC
);
294 return STATUS_SUCCESS
;
297 /* IntGetPrimaryMonitor
299 * Returns a PMONITOR for the primary monitor
306 IntGetPrimaryMonitor()
310 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
312 /* FIXME: I guess locking the monitor is not neccessary to read 1 int */
313 if (Monitor
->IsPrimary
)
320 /* IntGetMonitorsFromRect
322 * Returns a list of monitor handles/rectangles. The rectangles in the list are
323 * the areas of intersection with the monitors.
328 * Rectangle in desktop coordinates. If this is NULL all monitors are
329 * returned and the rect list is filled with the sizes of the monitors.
332 * Pointer to an array of HMONITOR which is filled with monitor handles.
336 * Pointer to an array of RECT which is filled with intersection rects in
337 * desktop coordinates.
338 * Can be NULL, will be ignored if no intersecting monitor is found and
339 * flags is MONITOR_DEFAULTTONEAREST
342 * Size of the hMonitorList and monitorRectList arguments. If this is zero
343 * hMonitorList and monitorRectList are ignored.
346 * Either 0 or MONITOR_DEFAULTTONEAREST (ignored if rect is NULL)
349 * The number of monitors which intersect the specified region.
353 IntGetMonitorsFromRect(OPTIONAL IN LPCRECTL pRect
,
354 OPTIONAL OUT HMONITOR
*hMonitorList
,
355 OPTIONAL OUT PRECTL monitorRectList
,
356 OPTIONAL IN DWORD listSize
,
357 OPTIONAL IN DWORD flags
)
359 PMONITOR Monitor
, NearestMonitor
= NULL
, PrimaryMonitor
= NULL
;
361 ULONG iNearestDistance
= 0xffffffff;
363 /* Find monitors which intersect the rectangle */
364 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
366 RECTL MonitorRect
, IntersectionRect
;
368 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&Monitor
->Lock
);
369 MonitorRect
= Monitor
->rcMonitor
;
370 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&Monitor
->Lock
);
372 DPRINT("MonitorRect: left = %d, top = %d, right = %d, bottom = %d\n",
373 MonitorRect
.left
, MonitorRect
.top
, MonitorRect
.right
, MonitorRect
.bottom
);
375 if (flags
== MONITOR_DEFAULTTOPRIMARY
&& Monitor
->IsPrimary
)
377 PrimaryMonitor
= Monitor
;
380 /* Check if a rect is given */
383 /* No rect given, so use the full monitor rect */
384 IntersectionRect
= MonitorRect
;
387 /* We have a rect, calculate intersection */
388 else if (!RECTL_bIntersectRect(&IntersectionRect
, &MonitorRect
, pRect
))
390 /* Rects did not intersect */
391 if (flags
== MONITOR_DEFAULTTONEAREST
)
393 ULONG cx
, cy
, iDistance
;
395 /* Get x and y distance */
396 cx
= min(abs(MonitorRect
.left
- pRect
->right
),
397 abs(pRect
->left
- MonitorRect
.right
));
398 cy
= min(abs(MonitorRect
.top
- pRect
->bottom
),
399 abs(pRect
->top
- MonitorRect
.bottom
));
401 /* Calculate distance square */
402 iDistance
= cx
* cx
+ cy
* cy
;
404 /* Check if this is the new nearest monitor */
405 if (iDistance
< iNearestDistance
)
407 iNearestDistance
= iDistance
;
408 NearestMonitor
= Monitor
;
415 /* Check if there's space in the buffer */
416 if (iCount
< listSize
)
418 if (hMonitorList
!= NULL
)
419 hMonitorList
[iCount
] = UserHMGetHandle(Monitor
);
420 if (monitorRectList
!= NULL
)
421 monitorRectList
[iCount
] = IntersectionRect
;
424 /* Increase count of found monitors */
428 /* Found nothing intersecting? */
431 /* Check if we shall default to the nearest monitor */
432 if (flags
== MONITOR_DEFAULTTONEAREST
&& NearestMonitor
)
434 if (hMonitorList
&& listSize
> 0)
435 hMonitorList
[iCount
] = UserHMGetHandle(NearestMonitor
);
438 /* Check if we shall default to the primary monitor */
439 else if (flags
== MONITOR_DEFAULTTOPRIMARY
&& PrimaryMonitor
)
441 if (hMonitorList
!= NULL
&& listSize
> 0)
442 hMonitorList
[iCount
] = UserHMGetHandle(PrimaryMonitor
);
450 /* PUBLIC FUNCTIONS ***********************************************************/
452 /* NtUserEnumDisplayMonitors
454 * Enumerates display monitors which intersect the given HDC/cliprect
459 * Handle to a DC for which to enum intersecting monitors. If this is NULL
460 * it returns all monitors which are part of the current virtual screen.
463 * Clipping rectangle with coordinate system origin at the DCs origin if the
464 * given HDC is not NULL or in virtual screen coordinated if it is NULL.
468 * Pointer to an array of HMONITOR which is filled with monitor handles.
472 * Pointer to an array of RECT which is filled with intersection rectangles.
476 * Size of the hMonitorList and monitorRectList arguments. If this is zero
477 * hMonitorList and monitorRectList are ignored.
480 * The number of monitors which intersect the specified region or -1 on failure.
484 NtUserEnumDisplayMonitors(
486 OPTIONAL IN LPCRECTL pRect
,
487 OPTIONAL OUT HMONITOR
*hMonitorList
,
488 OPTIONAL OUT PRECTL monitorRectList
,
489 OPTIONAL IN DWORD listSize
)
492 HMONITOR
*safeHMonitorList
= NULL
;
493 PRECTL safeRectList
= NULL
;
501 status
= MmCopyFromCaller(&rect
, pRect
, sizeof (RECT
));
502 if (!NT_SUCCESS(status
))
504 DPRINT("MmCopyFromCaller() failed!\n");
505 SetLastNtError(status
);
515 /* get visible region bounding rect */
519 DPRINT("DC_LockDc() failed!\n");
520 /* FIXME: setlasterror? */
523 regionType
= REGION_GetRgnBox(dc
->prgnVis
, &dcRect
);
528 DPRINT("NtGdiGetRgnBox() failed!\n");
531 if (regionType
== NULLREGION
)
533 if (regionType
== COMPLEXREGION
)
538 /* if hDC and pRect are given the area of interest is pRect with
539 coordinate origin at the DC position */
542 rect
.left
+= dcRect
.left
;
543 rect
.right
+= dcRect
.left
;
544 rect
.top
+= dcRect
.top
;
545 rect
.bottom
+= dcRect
.top
;
547 /* if hDC is given and pRect is not the area of interest is the
548 bounding rect of hDC */
555 if (hDC
== NULL
&& pRect
== NULL
)
560 /* find intersecting monitors */
561 numMonitors
= IntGetMonitorsFromRect(myRect
, NULL
, NULL
, 0, 0);
562 if (numMonitors
== 0 || listSize
== 0 ||
563 (hMonitorList
== NULL
&& monitorRectList
== NULL
))
565 DPRINT("numMonitors = %d\n", numMonitors
);
569 if (hMonitorList
!= NULL
&& listSize
!= 0)
571 safeHMonitorList
= ExAllocatePoolWithTag(PagedPool
, sizeof (HMONITOR
) * listSize
, USERTAG_MONITORRECTS
);
572 if (safeHMonitorList
== NULL
)
574 /* FIXME: EngSetLastError? */
578 if (monitorRectList
!= NULL
&& listSize
!= 0)
580 safeRectList
= ExAllocatePoolWithTag(PagedPool
, sizeof (RECT
) * listSize
, USERTAG_MONITORRECTS
);
581 if (safeRectList
== NULL
)
583 ExFreePoolWithTag(safeHMonitorList
, USERTAG_MONITORRECTS
);
584 /* FIXME: EngSetLastError? */
589 /* get intersecting monitors */
590 numMonitors
= IntGetMonitorsFromRect(myRect
, safeHMonitorList
, safeRectList
,
593 if (hDC
!= NULL
&& pRect
!= NULL
&& safeRectList
!= NULL
)
594 for (i
= 0; i
< numMonitors
; i
++)
596 safeRectList
[i
].left
-= dcRect
.left
;
597 safeRectList
[i
].right
-= dcRect
.left
;
598 safeRectList
[i
].top
-= dcRect
.top
;
599 safeRectList
[i
].bottom
-= dcRect
.top
;
603 if (hMonitorList
!= NULL
&& listSize
!= 0)
605 status
= MmCopyToCaller(hMonitorList
, safeHMonitorList
, sizeof (HMONITOR
) * listSize
);
606 ExFreePool(safeHMonitorList
);
607 if (!NT_SUCCESS(status
))
609 ExFreePoolWithTag(safeRectList
, USERTAG_MONITORRECTS
);
610 SetLastNtError(status
);
614 if (monitorRectList
!= NULL
&& listSize
!= 0)
616 status
= MmCopyToCaller(monitorRectList
, safeRectList
, sizeof (RECT
) * listSize
);
617 ExFreePoolWithTag(safeRectList
, USERTAG_MONITORRECTS
);
618 if (!NT_SUCCESS(status
))
620 SetLastNtError(status
);
628 /* NtUserGetMonitorInfo
630 * Retrieves information about a given monitor
635 * Handle to a monitor for which to get information
638 * Pointer to a MONITORINFO struct which is filled with the information.
639 * The cbSize member must be set to sizeof(MONITORINFO) or
640 * sizeof(MONITORINFOEX). Even if set to sizeof(MONITORINFOEX) only parts
641 * from MONITORINFO will be filled.
644 * Pointer to a UNICODE_STRING which will recieve the device's name. The
645 * length should be CCHDEVICENAME
649 * TRUE on success; FALSE on failure (calls SetLastNtError())
654 NtUserGetMonitorInfo(
655 IN HMONITOR hMonitor
,
656 OUT LPMONITORINFO pMonitorInfo
)
659 MONITORINFOEXW MonitorInfo
;
661 DECLARE_RETURN(BOOL
);
663 DPRINT("Enter NtUserGetMonitorInfo\n");
666 /* get monitor object */
667 if (!(Monitor
= UserGetMonitorObject(hMonitor
)))
669 DPRINT("Couldnt find monitor 0x%lx\n", hMonitor
);
673 if(pMonitorInfo
== NULL
)
675 SetLastNtError(STATUS_INVALID_PARAMETER
);
679 /* get size of pMonitorInfo */
680 Status
= MmCopyFromCaller(&MonitorInfo
.cbSize
, &pMonitorInfo
->cbSize
, sizeof (MonitorInfo
.cbSize
));
681 if (!NT_SUCCESS(Status
))
683 SetLastNtError(Status
);
686 if ((MonitorInfo
.cbSize
!= sizeof (MONITORINFO
)) &&
687 (MonitorInfo
.cbSize
!= sizeof (MONITORINFOEXW
)))
689 SetLastNtError(STATUS_INVALID_PARAMETER
);
693 /* fill monitor info */
694 MonitorInfo
.rcMonitor
= Monitor
->rcMonitor
;
695 MonitorInfo
.rcWork
= Monitor
->rcWork
;
696 MonitorInfo
.dwFlags
= 0;
698 if (Monitor
->IsPrimary
)
699 MonitorInfo
.dwFlags
|= MONITORINFOF_PRIMARY
;
701 /* fill device name */
702 if (MonitorInfo
.cbSize
== sizeof (MONITORINFOEXW
))
705 INT len
= Monitor
->DeviceName
.Length
;
706 if (len
>= CCHDEVICENAME
* sizeof (WCHAR
))
707 len
= (CCHDEVICENAME
- 1) * sizeof (WCHAR
);
709 memcpy(MonitorInfo
.szDevice
, Monitor
->DeviceName
.Buffer
, len
);
710 memcpy(MonitorInfo
.szDevice
+ (len
/ sizeof (WCHAR
)), &nul
, sizeof (WCHAR
));
714 Status
= MmCopyToCaller(pMonitorInfo
, &MonitorInfo
, MonitorInfo
.cbSize
);
715 if (!NT_SUCCESS(Status
))
717 DPRINT("GetMonitorInfo: MmCopyToCaller failed\n");
718 SetLastNtError(Status
);
722 DPRINT("GetMonitorInfo: success\n");
727 DPRINT("Leave NtUserGetMonitorInfo, ret=%i\n",_ret_
);
732 /* NtUserMonitorFromPoint
734 * Returns a handle to the monitor containing the given point.
739 * Point for which to find monitor
742 * Specifies the behaviour if the point isn't on any of the monitors.
745 * If the point is found a handle to the monitor is returned; if not the
746 * return value depends on dwFlags
750 NtUserMonitorFromPoint(
756 HMONITOR hMonitor
= NULL
;
758 /* fill inRect (bottom-right exclusive) */
759 InRect
.left
= point
.x
;
760 InRect
.right
= point
.x
+ 1;
761 InRect
.top
= point
.y
;
762 InRect
.bottom
= point
.y
+ 1;
764 /* find intersecting monitor */
765 NumMonitors
= IntGetMonitorsFromRect(&InRect
, &hMonitor
, NULL
, 1, dwFlags
);
768 return (HMONITOR
)NULL
;
774 /* NtUserMonitorFromRect
776 * Returns a handle to the monitor having the largest intersection with a
782 * Pointer to a RECT for which to find monitor
785 * Specifies the behaviour if no monitor intersects the given rect
788 * If a monitor intersects the rect a handle to it is returned; if not the
789 * return value depends on dwFlags
793 NtUserMonitorFromRect(
797 ULONG numMonitors
, iLargestArea
= 0, i
;
799 HMONITOR
*hMonitorList
;
800 HMONITOR hMonitor
= NULL
;
805 status
= MmCopyFromCaller(&rect
, pRect
, sizeof (RECT
));
806 if (!NT_SUCCESS(status
))
808 SetLastNtError(status
);
809 return (HMONITOR
)NULL
;
812 /* find intersecting monitors */
813 numMonitors
= IntGetMonitorsFromRect(&rect
, &hMonitor
, NULL
, 1, dwFlags
);
814 if (numMonitors
<= 1)
819 hMonitorList
= ExAllocatePoolWithTag(PagedPool
,
820 sizeof(HMONITOR
) * numMonitors
,
821 USERTAG_MONITORRECTS
);
822 if (hMonitorList
== NULL
)
824 /* FIXME: EngSetLastError? */
825 return (HMONITOR
)NULL
;
828 rectList
= ExAllocatePoolWithTag(PagedPool
,
829 sizeof(RECT
) * numMonitors
,
830 USERTAG_MONITORRECTS
);
831 if (rectList
== NULL
)
833 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
834 /* FIXME: EngSetLastError? */
835 return (HMONITOR
)NULL
;
838 /* get intersecting monitors */
839 numMonitors
= IntGetMonitorsFromRect(&rect
, hMonitorList
, rectList
,
841 if (numMonitors
== 0)
843 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
844 ExFreePoolWithTag(rectList
, USERTAG_MONITORRECTS
);
845 return (HMONITOR
)NULL
;
848 /* find largest intersection */
849 for (i
= 0; i
< numMonitors
; i
++)
851 ULONG area
= (rectList
[i
].right
- rectList
[i
].left
) *
852 (rectList
[i
].bottom
- rectList
[i
].top
);
853 if (area
>= iLargestArea
)
855 hMonitor
= hMonitorList
[i
];
859 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
860 ExFreePoolWithTag(rectList
, USERTAG_MONITORRECTS
);
868 NtUserMonitorFromWindow(
873 HMONITOR hMonitor
= NULL
;
875 DECLARE_RETURN(HMONITOR
);
877 DPRINT("Enter NtUserMonitorFromWindow\n");
880 if (!(Window
= UserGetWindowObject(hWnd
)))
882 if (dwFlags
== MONITOR_DEFAULTTONULL
)
886 IntGetMonitorsFromRect(NULL
, &hMonitor
, NULL
, 1, dwFlags
);
890 Rect
.left
= Rect
.right
= Window
->rcWindow
.left
;
891 Rect
.top
= Rect
.bottom
= Window
->rcWindow
.bottom
;
893 IntGetMonitorsFromRect(&Rect
, &hMonitor
, NULL
, 1, dwFlags
);
898 DPRINT("Leave NtUserMonitorFromWindow, ret=%i\n",_ret_
);