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 ****************************************************/
34 DPRINT("Initializing monitor implementation...\n");
36 return STATUS_SUCCESS
;
42 DPRINT("Cleaning up monitor implementation...\n");
43 /* FIXME: Destroy monitor objects? */
45 return STATUS_SUCCESS
;
48 /* PRIVATE FUNCTIONS **********************************************************/
50 /* IntCreateMonitorObject
55 * If the function succeeds a pointer to a MONITOR is returned. On failure
60 IntCreateMonitorObject()
65 Monitor
= UserCreateObject(gHandleTable
, NULL
, &Handle
, otMonitor
, sizeof (MONITOR
));
71 ExInitializeFastMutex(&Monitor
->Lock
);
76 /* IntDestroyMonitorObject
79 * You have to be the owner of the monitors lock to safely destroy it.
84 * Pointer to the MONITOR which shall be deleted
88 IntDestroyMonitorObject(IN PMONITOR pMonitor
)
90 RtlFreeUnicodeString(&pMonitor
->DeviceName
);
91 UserDereferenceObject(pMonitor
);
96 UserGetMonitorObject(IN HMONITOR hMonitor
)
102 SetLastWin32Error(ERROR_INVALID_MONITOR_HANDLE
);
106 Monitor
= (PMONITOR
)UserGetObject(gHandleTable
, hMonitor
, otMonitor
);
109 SetLastWin32Error(ERROR_INVALID_MONITOR_HANDLE
);
113 ASSERT(Monitor
->head
.cLockObj
>= 0);
121 * Creates a new MONITOR and appends it to the list of monitors.
125 * pGdiDevice Pointer to the PDEVOBJ onto which the monitor was attached
126 * DisplayNumber Display Number (starting with 0)
132 IntAttachMonitor(IN PDEVOBJ
*pGdiDevice
,
133 IN ULONG DisplayNumber
)
136 WCHAR Buffer
[CCHDEVICENAME
];
138 DPRINT("Attaching monitor...\n");
140 /* create new monitor object */
141 Monitor
= IntCreateMonitorObject();
144 DPRINT("Couldnt create monitor object\n");
145 return STATUS_INSUFFICIENT_RESOURCES
;
148 _snwprintf(Buffer
, CCHDEVICENAME
, L
"\\\\.\\DISPLAY%d", DisplayNumber
+ 1);
149 if (!RtlCreateUnicodeString(&Monitor
->DeviceName
, Buffer
))
151 DPRINT("Couldn't duplicate monitor name!\n");
152 UserDereferenceObject(Monitor
);
153 UserDeleteObject(UserHMGetHandle(Monitor
), otMonitor
);
154 return STATUS_INSUFFICIENT_RESOURCES
;
157 Monitor
->GdiDevice
= pGdiDevice
;
158 Monitor
->rcMonitor
.left
= 0;
159 Monitor
->rcMonitor
.top
= 0;
160 Monitor
->rcMonitor
.right
= Monitor
->rcMonitor
.left
+ pGdiDevice
->gdiinfo
.ulHorzRes
;
161 Monitor
->rcMonitor
.bottom
= Monitor
->rcMonitor
.top
+ pGdiDevice
->gdiinfo
.ulVertRes
;
162 Monitor
->rcWork
= Monitor
->rcMonitor
;
163 Monitor
->cWndStack
= 0;
165 Monitor
->hrgnMonitor
= IntSysCreateRectRgnIndirect( &Monitor
->rcMonitor
);
167 IntGdiSetRegionOwner(Monitor
->hrgnMonitor
, GDI_OBJ_HMGR_PUBLIC
);
169 if (gMonitorList
== NULL
)
171 DPRINT("Primary monitor is beeing attached\n");
172 Monitor
->IsPrimary
= TRUE
;
173 gMonitorList
= Monitor
;
178 DPRINT("Additional monitor is beeing attached\n");
179 for (p
= gMonitorList
; p
->Next
!= NULL
; p
= p
->Next
)
186 return STATUS_SUCCESS
;
191 * Deletes a MONITOR and removes it from the list of monitors.
195 * pGdiDevice Pointer to the PDEVOBJ from which the monitor was detached
201 IntDetachMonitor(IN PDEVOBJ
*pGdiDevice
)
205 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
207 if (Monitor
->GdiDevice
== pGdiDevice
)
213 /* no monitor for given device found */
214 return STATUS_INVALID_PARAMETER
;
217 if (Monitor
->IsPrimary
&& (Monitor
->Next
!= NULL
|| Monitor
->Prev
!= NULL
))
219 PMONITOR NewPrimaryMonitor
= (Monitor
->Prev
!= NULL
) ? (Monitor
->Prev
) : (Monitor
->Next
);
221 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&NewPrimaryMonitor
->Lock
);
222 NewPrimaryMonitor
->IsPrimary
= TRUE
;
223 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&NewPrimaryMonitor
->Lock
);
226 if (gMonitorList
== Monitor
)
228 gMonitorList
= Monitor
->Next
;
229 if (Monitor
->Next
!= NULL
)
230 Monitor
->Next
->Prev
= NULL
;
234 Monitor
->Prev
->Next
= Monitor
->Next
;
235 if (Monitor
->Next
!= NULL
)
236 Monitor
->Next
->Prev
= Monitor
->Prev
;
239 if (Monitor
->hrgnMonitor
)
240 REGION_FreeRgnByHandle(Monitor
->hrgnMonitor
);
242 IntDestroyMonitorObject(Monitor
);
244 return STATUS_SUCCESS
;
247 /* IntGetPrimaryMonitor
249 * Returns a PMONITOR for the primary monitor
256 IntGetPrimaryMonitor()
260 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
262 /* FIXME: I guess locking the monitor is not neccessary to read 1 int */
263 if (Monitor
->IsPrimary
)
270 /* IntGetMonitorsFromRect
272 * Returns a list of monitor handles/rectangles. The rectangles in the list are
273 * the areas of intersection with the monitors.
278 * Rectangle in desktop coordinates. If this is NULL all monitors are
279 * returned and the rect list is filled with the sizes of the monitors.
282 * Pointer to an array of HMONITOR which is filled with monitor handles.
286 * Pointer to an array of RECT which is filled with intersection rects in
287 * desktop coordinates.
288 * Can be NULL, will be ignored if no intersecting monitor is found and
289 * flags is MONITOR_DEFAULTTONEAREST
292 * Size of the hMonitorList and monitorRectList arguments. If this is zero
293 * hMonitorList and monitorRectList are ignored.
296 * Either 0 or MONITOR_DEFAULTTONEAREST (ignored if rect is NULL)
299 * The number of monitors which intersect the specified region.
303 IntGetMonitorsFromRect(OPTIONAL IN LPCRECTL pRect
,
304 OPTIONAL OUT HMONITOR
*hMonitorList
,
305 OPTIONAL OUT PRECTL monitorRectList
,
306 OPTIONAL IN DWORD listSize
,
307 OPTIONAL IN DWORD flags
)
309 PMONITOR Monitor
, NearestMonitor
= NULL
, PrimaryMonitor
= NULL
;
311 ULONG iNearestDistance
= 0xffffffff;
313 /* Find monitors which intersect the rectangle */
314 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
316 RECTL MonitorRect
, IntersectionRect
;
318 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&Monitor
->Lock
);
319 MonitorRect
= Monitor
->rcMonitor
;
320 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&Monitor
->Lock
);
322 DPRINT("MonitorRect: left = %d, top = %d, right = %d, bottom = %d\n",
323 MonitorRect
.left
, MonitorRect
.top
, MonitorRect
.right
, MonitorRect
.bottom
);
325 if (flags
== MONITOR_DEFAULTTOPRIMARY
&& Monitor
->IsPrimary
)
327 PrimaryMonitor
= Monitor
;
330 /* Check if a rect is given */
333 /* No rect given, so use the full monitor rect */
334 IntersectionRect
= MonitorRect
;
337 /* We have a rect, calculate intersection */
338 else if (!RECTL_bIntersectRect(&IntersectionRect
, &MonitorRect
, pRect
))
340 /* Rects did not intersect */
341 if (flags
== MONITOR_DEFAULTTONEAREST
)
343 ULONG cx
, cy
, iDistance
;
345 /* Get x and y distance */
346 cx
= min(abs(MonitorRect
.left
- pRect
->right
),
347 abs(pRect
->left
- MonitorRect
.right
));
348 cy
= min(abs(MonitorRect
.top
- pRect
->bottom
),
349 abs(pRect
->top
- MonitorRect
.bottom
));
351 /* Calculate distance square */
352 iDistance
= cx
* cx
+ cy
* cy
;
354 /* Check if this is the new nearest monitor */
355 if (iDistance
< iNearestDistance
)
357 iNearestDistance
= iDistance
;
358 NearestMonitor
= Monitor
;
365 /* Check if there's space in the buffer */
366 if (iCount
< listSize
)
368 if (hMonitorList
!= NULL
)
369 hMonitorList
[iCount
] = UserHMGetHandle(Monitor
);
370 if (monitorRectList
!= NULL
)
371 monitorRectList
[iCount
] = IntersectionRect
;
374 /* Increase count of found monitors */
378 /* Found nothing intersecting? */
381 /* Check if we shall default to the nearest monitor */
382 if (flags
== MONITOR_DEFAULTTONEAREST
&& NearestMonitor
)
384 if (hMonitorList
&& listSize
> 0)
385 hMonitorList
[iCount
] = UserHMGetHandle(NearestMonitor
);
388 /* Check if we shall default to the primary monitor */
389 else if (flags
== MONITOR_DEFAULTTOPRIMARY
&& PrimaryMonitor
)
391 if (hMonitorList
!= NULL
&& listSize
> 0)
392 hMonitorList
[iCount
] = UserHMGetHandle(PrimaryMonitor
);
400 /* PUBLIC FUNCTIONS ***********************************************************/
402 /* NtUserEnumDisplayMonitors
404 * Enumerates display monitors which intersect the given HDC/cliprect
409 * Handle to a DC for which to enum intersecting monitors. If this is NULL
410 * it returns all monitors which are part of the current virtual screen.
413 * Clipping rectangle with coordinate system origin at the DCs origin if the
414 * given HDC is not NULL or in virtual screen coordinated if it is NULL.
418 * Pointer to an array of HMONITOR which is filled with monitor handles.
422 * Pointer to an array of RECT which is filled with intersection rectangles.
426 * Size of the hMonitorList and monitorRectList arguments. If this is zero
427 * hMonitorList and monitorRectList are ignored.
430 * The number of monitors which intersect the specified region or -1 on failure.
434 NtUserEnumDisplayMonitors(
436 OPTIONAL IN LPCRECTL pRect
,
437 OPTIONAL OUT HMONITOR
*hMonitorList
,
438 OPTIONAL OUT PRECTL monitorRectList
,
439 OPTIONAL IN DWORD listSize
)
442 HMONITOR
*safeHMonitorList
= NULL
;
443 PRECTL safeRectList
= NULL
;
451 status
= MmCopyFromCaller(&rect
, pRect
, sizeof (RECT
));
452 if (!NT_SUCCESS(status
))
454 DPRINT("MmCopyFromCaller() failed!\n");
455 SetLastNtError(status
);
465 /* get visible region bounding rect */
469 DPRINT("DC_LockDc() failed!\n");
470 /* FIXME: setlasterror? */
473 regionType
= REGION_GetRgnBox(dc
->prgnVis
, &dcRect
);
478 DPRINT("NtGdiGetRgnBox() failed!\n");
481 if (regionType
== NULLREGION
)
483 if (regionType
== COMPLEXREGION
)
488 /* if hDC and pRect are given the area of interest is pRect with
489 coordinate origin at the DC position */
492 rect
.left
+= dcRect
.left
;
493 rect
.right
+= dcRect
.left
;
494 rect
.top
+= dcRect
.top
;
495 rect
.bottom
+= dcRect
.top
;
497 /* if hDC is given and pRect is not the area of interest is the
498 bounding rect of hDC */
505 if (hDC
== NULL
&& pRect
== NULL
)
510 /* find intersecting monitors */
511 numMonitors
= IntGetMonitorsFromRect(myRect
, NULL
, NULL
, 0, 0);
512 if (numMonitors
== 0 || listSize
== 0 ||
513 (hMonitorList
== NULL
&& monitorRectList
== NULL
))
515 DPRINT("numMonitors = %d\n", numMonitors
);
519 if (hMonitorList
!= NULL
&& listSize
!= 0)
521 safeHMonitorList
= ExAllocatePoolWithTag(PagedPool
, sizeof (HMONITOR
) * listSize
, USERTAG_MONITORRECTS
);
522 if (safeHMonitorList
== NULL
)
524 /* FIXME: SetLastWin32Error? */
528 if (monitorRectList
!= NULL
&& listSize
!= 0)
530 safeRectList
= ExAllocatePoolWithTag(PagedPool
, sizeof (RECT
) * listSize
, USERTAG_MONITORRECTS
);
531 if (safeRectList
== NULL
)
533 ExFreePoolWithTag(safeHMonitorList
, USERTAG_MONITORRECTS
);
534 /* FIXME: SetLastWin32Error? */
539 /* get intersecting monitors */
540 numMonitors
= IntGetMonitorsFromRect(myRect
, safeHMonitorList
, safeRectList
,
543 if (hDC
!= NULL
&& pRect
!= NULL
&& safeRectList
!= NULL
)
544 for (i
= 0; i
< numMonitors
; i
++)
546 safeRectList
[i
].left
-= dcRect
.left
;
547 safeRectList
[i
].right
-= dcRect
.left
;
548 safeRectList
[i
].top
-= dcRect
.top
;
549 safeRectList
[i
].bottom
-= dcRect
.top
;
553 if (hMonitorList
!= NULL
&& listSize
!= 0)
555 status
= MmCopyToCaller(hMonitorList
, safeHMonitorList
, sizeof (HMONITOR
) * listSize
);
556 ExFreePool(safeHMonitorList
);
557 if (!NT_SUCCESS(status
))
559 ExFreePoolWithTag(safeRectList
, USERTAG_MONITORRECTS
);
560 SetLastNtError(status
);
564 if (monitorRectList
!= NULL
&& listSize
!= 0)
566 status
= MmCopyToCaller(monitorRectList
, safeRectList
, sizeof (RECT
) * listSize
);
567 ExFreePoolWithTag(safeRectList
, USERTAG_MONITORRECTS
);
568 if (!NT_SUCCESS(status
))
570 SetLastNtError(status
);
578 /* NtUserGetMonitorInfo
580 * Retrieves information about a given monitor
585 * Handle to a monitor for which to get information
588 * Pointer to a MONITORINFO struct which is filled with the information.
589 * The cbSize member must be set to sizeof(MONITORINFO) or
590 * sizeof(MONITORINFOEX). Even if set to sizeof(MONITORINFOEX) only parts
591 * from MONITORINFO will be filled.
594 * Pointer to a UNICODE_STRING which will recieve the device's name. The
595 * length should be CCHDEVICENAME
599 * TRUE on success; FALSE on failure (calls SetLastNtError())
604 NtUserGetMonitorInfo(
605 IN HMONITOR hMonitor
,
606 OUT LPMONITORINFO pMonitorInfo
)
609 MONITORINFOEXW MonitorInfo
;
611 DECLARE_RETURN(BOOL
);
613 DPRINT("Enter NtUserGetMonitorInfo\n");
616 /* get monitor object */
617 if (!(Monitor
= UserGetMonitorObject(hMonitor
)))
619 DPRINT("Couldnt find monitor 0x%lx\n", hMonitor
);
623 if(pMonitorInfo
== NULL
)
625 SetLastNtError(STATUS_INVALID_PARAMETER
);
629 /* get size of pMonitorInfo */
630 Status
= MmCopyFromCaller(&MonitorInfo
.cbSize
, &pMonitorInfo
->cbSize
, sizeof (MonitorInfo
.cbSize
));
631 if (!NT_SUCCESS(Status
))
633 SetLastNtError(Status
);
636 if ((MonitorInfo
.cbSize
!= sizeof (MONITORINFO
)) &&
637 (MonitorInfo
.cbSize
!= sizeof (MONITORINFOEXW
)))
639 SetLastNtError(STATUS_INVALID_PARAMETER
);
643 /* fill monitor info */
644 MonitorInfo
.rcMonitor
= Monitor
->rcMonitor
;
645 MonitorInfo
.rcWork
= Monitor
->rcWork
;
646 MonitorInfo
.dwFlags
= 0;
648 if (Monitor
->IsPrimary
)
649 MonitorInfo
.dwFlags
|= MONITORINFOF_PRIMARY
;
651 /* fill device name */
652 if (MonitorInfo
.cbSize
== sizeof (MONITORINFOEXW
))
655 INT len
= Monitor
->DeviceName
.Length
;
656 if (len
>= CCHDEVICENAME
* sizeof (WCHAR
))
657 len
= (CCHDEVICENAME
- 1) * sizeof (WCHAR
);
659 memcpy(MonitorInfo
.szDevice
, Monitor
->DeviceName
.Buffer
, len
);
660 memcpy(MonitorInfo
.szDevice
+ (len
/ sizeof (WCHAR
)), &nul
, sizeof (WCHAR
));
664 Status
= MmCopyToCaller(pMonitorInfo
, &MonitorInfo
, MonitorInfo
.cbSize
);
665 if (!NT_SUCCESS(Status
))
667 DPRINT("GetMonitorInfo: MmCopyToCaller failed\n");
668 SetLastNtError(Status
);
672 DPRINT("GetMonitorInfo: success\n");
677 DPRINT("Leave NtUserGetMonitorInfo, ret=%i\n",_ret_
);
682 /* NtUserMonitorFromPoint
684 * Returns a handle to the monitor containing the given point.
689 * Point for which to find monitor
692 * Specifies the behaviour if the point isn't on any of the monitors.
695 * If the point is found a handle to the monitor is returned; if not the
696 * return value depends on dwFlags
700 NtUserMonitorFromPoint(
706 HMONITOR hMonitor
= NULL
;
708 /* fill inRect (bottom-right exclusive) */
709 InRect
.left
= point
.x
;
710 InRect
.right
= point
.x
+ 1;
711 InRect
.top
= point
.y
;
712 InRect
.bottom
= point
.y
+ 1;
714 /* find intersecting monitor */
715 NumMonitors
= IntGetMonitorsFromRect(&InRect
, &hMonitor
, NULL
, 1, dwFlags
);
718 return (HMONITOR
)NULL
;
724 /* NtUserMonitorFromRect
726 * Returns a handle to the monitor having the largest intersection with a
732 * Pointer to a RECT for which to find monitor
735 * Specifies the behaviour if no monitor intersects the given rect
738 * If a monitor intersects the rect a handle to it is returned; if not the
739 * return value depends on dwFlags
743 NtUserMonitorFromRect(
747 ULONG numMonitors
, iLargestArea
= 0, i
;
749 HMONITOR
*hMonitorList
;
750 HMONITOR hMonitor
= NULL
;
755 status
= MmCopyFromCaller(&rect
, pRect
, sizeof (RECT
));
756 if (!NT_SUCCESS(status
))
758 SetLastNtError(status
);
759 return (HMONITOR
)NULL
;
762 /* find intersecting monitors */
763 numMonitors
= IntGetMonitorsFromRect(&rect
, &hMonitor
, NULL
, 1, dwFlags
);
764 if (numMonitors
<= 1)
769 hMonitorList
= ExAllocatePoolWithTag(PagedPool
,
770 sizeof(HMONITOR
) * numMonitors
,
771 USERTAG_MONITORRECTS
);
772 if (hMonitorList
== NULL
)
774 /* FIXME: SetLastWin32Error? */
775 return (HMONITOR
)NULL
;
778 rectList
= ExAllocatePoolWithTag(PagedPool
,
779 sizeof(RECT
) * numMonitors
,
780 USERTAG_MONITORRECTS
);
781 if (rectList
== NULL
)
783 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
784 /* FIXME: SetLastWin32Error? */
785 return (HMONITOR
)NULL
;
788 /* get intersecting monitors */
789 numMonitors
= IntGetMonitorsFromRect(&rect
, hMonitorList
, rectList
,
791 if (numMonitors
== 0)
793 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
794 ExFreePoolWithTag(rectList
, USERTAG_MONITORRECTS
);
795 return (HMONITOR
)NULL
;
798 /* find largest intersection */
799 for (i
= 0; i
< numMonitors
; i
++)
801 ULONG area
= (rectList
[i
].right
- rectList
[i
].left
) *
802 (rectList
[i
].bottom
- rectList
[i
].top
);
803 if (area
>= iLargestArea
)
805 hMonitor
= hMonitorList
[i
];
809 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
810 ExFreePoolWithTag(rectList
, USERTAG_MONITORRECTS
);
818 NtUserMonitorFromWindow(
823 HMONITOR hMonitor
= NULL
;
825 DECLARE_RETURN(HMONITOR
);
827 DPRINT("Enter NtUserMonitorFromWindow\n");
830 if (!(Window
= UserGetWindowObject(hWnd
)))
832 if (dwFlags
== MONITOR_DEFAULTTONULL
)
836 IntGetMonitorsFromRect(NULL
, &hMonitor
, NULL
, 1, dwFlags
);
840 Rect
.left
= Rect
.right
= Window
->rcWindow
.left
;
841 Rect
.top
= Rect
.bottom
= Window
->rcWindow
.bottom
;
843 IntGetMonitorsFromRect(&Rect
, &hMonitor
, NULL
, 1, dwFlags
);
848 DPRINT("Leave NtUserMonitorFromWindow, ret=%i\n",_ret_
);