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 /* IntGetPrimaryMonitor
251 * Returns a PMONITOR for the primary monitor
258 IntGetPrimaryMonitor()
262 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
264 /* FIXME: I guess locking the monitor is not neccessary to read 1 int */
265 if (Monitor
->IsPrimary
)
272 /* IntGetMonitorsFromRect
274 * Returns a list of monitor handles/rectangles. The rectangles in the list are
275 * the areas of intersection with the monitors.
280 * Rectangle in desktop coordinates. If this is NULL all monitors are
281 * returned and the rect list is filled with the sizes of the monitors.
284 * Pointer to an array of HMONITOR which is filled with monitor handles.
288 * Pointer to an array of RECT which is filled with intersection rects in
289 * desktop coordinates.
290 * Can be NULL, will be ignored if no intersecting monitor is found and
291 * flags is MONITOR_DEFAULTTONEAREST
294 * Size of the hMonitorList and monitorRectList arguments. If this is zero
295 * hMonitorList and monitorRectList are ignored.
298 * Either 0 or MONITOR_DEFAULTTONEAREST (ignored if rect is NULL)
301 * The number of monitors which intersect the specified region.
305 IntGetMonitorsFromRect(OPTIONAL IN LPCRECTL pRect
,
306 OPTIONAL OUT HMONITOR
*hMonitorList
,
307 OPTIONAL OUT PRECTL monitorRectList
,
308 OPTIONAL IN DWORD listSize
,
309 OPTIONAL IN DWORD flags
)
311 PMONITOR Monitor
, NearestMonitor
= NULL
, PrimaryMonitor
= NULL
;
313 ULONG iNearestDistance
= 0xffffffff;
315 /* Find monitors which intersect the rectangle */
316 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
318 RECTL MonitorRect
, IntersectionRect
;
320 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&Monitor
->Lock
);
321 MonitorRect
= Monitor
->rcMonitor
;
322 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&Monitor
->Lock
);
324 DPRINT("MonitorRect: left = %d, top = %d, right = %d, bottom = %d\n",
325 MonitorRect
.left
, MonitorRect
.top
, MonitorRect
.right
, MonitorRect
.bottom
);
327 if (flags
== MONITOR_DEFAULTTOPRIMARY
&& Monitor
->IsPrimary
)
329 PrimaryMonitor
= Monitor
;
332 /* Check if a rect is given */
335 /* No rect given, so use the full monitor rect */
336 IntersectionRect
= MonitorRect
;
339 /* We have a rect, calculate intersection */
340 else if (!RECTL_bIntersectRect(&IntersectionRect
, &MonitorRect
, pRect
))
342 /* Rects did not intersect */
343 if (flags
== MONITOR_DEFAULTTONEAREST
)
345 ULONG cx
, cy
, iDistance
;
347 /* Get x and y distance */
348 cx
= min(abs(MonitorRect
.left
- pRect
->right
),
349 abs(pRect
->left
- MonitorRect
.right
));
350 cy
= min(abs(MonitorRect
.top
- pRect
->bottom
),
351 abs(pRect
->top
- MonitorRect
.bottom
));
353 /* Calculate distance square */
354 iDistance
= cx
* cx
+ cy
* cy
;
356 /* Check if this is the new nearest monitor */
357 if (iDistance
< iNearestDistance
)
359 iNearestDistance
= iDistance
;
360 NearestMonitor
= Monitor
;
367 /* Check if there's space in the buffer */
368 if (iCount
< listSize
)
370 if (hMonitorList
!= NULL
)
371 hMonitorList
[iCount
] = UserHMGetHandle(Monitor
);
372 if (monitorRectList
!= NULL
)
373 monitorRectList
[iCount
] = IntersectionRect
;
376 /* Increase count of found monitors */
380 /* Found nothing intersecting? */
383 /* Check if we shall default to the nearest monitor */
384 if (flags
== MONITOR_DEFAULTTONEAREST
&& NearestMonitor
)
386 if (hMonitorList
&& listSize
> 0)
387 hMonitorList
[iCount
] = UserHMGetHandle(NearestMonitor
);
390 /* Check if we shall default to the primary monitor */
391 else if (flags
== MONITOR_DEFAULTTOPRIMARY
&& PrimaryMonitor
)
393 if (hMonitorList
!= NULL
&& listSize
> 0)
394 hMonitorList
[iCount
] = UserHMGetHandle(PrimaryMonitor
);
402 /* PUBLIC FUNCTIONS ***********************************************************/
404 /* NtUserEnumDisplayMonitors
406 * Enumerates display monitors which intersect the given HDC/cliprect
411 * Handle to a DC for which to enum intersecting monitors. If this is NULL
412 * it returns all monitors which are part of the current virtual screen.
415 * Clipping rectangle with coordinate system origin at the DCs origin if the
416 * given HDC is not NULL or in virtual screen coordinated if it is NULL.
420 * Pointer to an array of HMONITOR which is filled with monitor handles.
424 * Pointer to an array of RECT which is filled with intersection rectangles.
428 * Size of the hMonitorList and monitorRectList arguments. If this is zero
429 * hMonitorList and monitorRectList are ignored.
432 * The number of monitors which intersect the specified region or -1 on failure.
436 NtUserEnumDisplayMonitors(
438 OPTIONAL IN LPCRECTL pRect
,
439 OPTIONAL OUT HMONITOR
*hMonitorList
,
440 OPTIONAL OUT PRECTL monitorRectList
,
441 OPTIONAL IN DWORD listSize
)
444 HMONITOR
*safeHMonitorList
= NULL
;
445 PRECTL safeRectList
= NULL
;
453 status
= MmCopyFromCaller(&rect
, pRect
, sizeof (RECT
));
454 if (!NT_SUCCESS(status
))
456 DPRINT("MmCopyFromCaller() failed!\n");
457 SetLastNtError(status
);
467 /* get visible region bounding rect */
471 DPRINT("DC_LockDc() failed!\n");
472 /* FIXME: setlasterror? */
475 regionType
= REGION_GetRgnBox(dc
->prgnVis
, &dcRect
);
480 DPRINT("NtGdiGetRgnBox() failed!\n");
483 if (regionType
== NULLREGION
)
485 if (regionType
== COMPLEXREGION
)
490 /* if hDC and pRect are given the area of interest is pRect with
491 coordinate origin at the DC position */
494 rect
.left
+= dcRect
.left
;
495 rect
.right
+= dcRect
.left
;
496 rect
.top
+= dcRect
.top
;
497 rect
.bottom
+= dcRect
.top
;
499 /* if hDC is given and pRect is not the area of interest is the
500 bounding rect of hDC */
507 if (hDC
== NULL
&& pRect
== NULL
)
512 /* find intersecting monitors */
513 numMonitors
= IntGetMonitorsFromRect(myRect
, NULL
, NULL
, 0, 0);
514 if (numMonitors
== 0 || listSize
== 0 ||
515 (hMonitorList
== NULL
&& monitorRectList
== NULL
))
517 DPRINT("numMonitors = %d\n", numMonitors
);
521 if (hMonitorList
!= NULL
&& listSize
!= 0)
523 safeHMonitorList
= ExAllocatePoolWithTag(PagedPool
, sizeof (HMONITOR
) * listSize
, USERTAG_MONITORRECTS
);
524 if (safeHMonitorList
== NULL
)
526 /* FIXME: EngSetLastError? */
530 if (monitorRectList
!= NULL
&& listSize
!= 0)
532 safeRectList
= ExAllocatePoolWithTag(PagedPool
, sizeof (RECT
) * listSize
, USERTAG_MONITORRECTS
);
533 if (safeRectList
== NULL
)
535 ExFreePoolWithTag(safeHMonitorList
, USERTAG_MONITORRECTS
);
536 /* FIXME: EngSetLastError? */
541 /* get intersecting monitors */
542 numMonitors
= IntGetMonitorsFromRect(myRect
, safeHMonitorList
, safeRectList
,
545 if (hDC
!= NULL
&& pRect
!= NULL
&& safeRectList
!= NULL
)
546 for (i
= 0; i
< numMonitors
; i
++)
548 safeRectList
[i
].left
-= dcRect
.left
;
549 safeRectList
[i
].right
-= dcRect
.left
;
550 safeRectList
[i
].top
-= dcRect
.top
;
551 safeRectList
[i
].bottom
-= dcRect
.top
;
555 if (hMonitorList
!= NULL
&& listSize
!= 0)
557 status
= MmCopyToCaller(hMonitorList
, safeHMonitorList
, sizeof (HMONITOR
) * listSize
);
558 ExFreePool(safeHMonitorList
);
559 if (!NT_SUCCESS(status
))
561 ExFreePoolWithTag(safeRectList
, USERTAG_MONITORRECTS
);
562 SetLastNtError(status
);
566 if (monitorRectList
!= NULL
&& listSize
!= 0)
568 status
= MmCopyToCaller(monitorRectList
, safeRectList
, sizeof (RECT
) * listSize
);
569 ExFreePoolWithTag(safeRectList
, USERTAG_MONITORRECTS
);
570 if (!NT_SUCCESS(status
))
572 SetLastNtError(status
);
580 /* NtUserGetMonitorInfo
582 * Retrieves information about a given monitor
587 * Handle to a monitor for which to get information
590 * Pointer to a MONITORINFO struct which is filled with the information.
591 * The cbSize member must be set to sizeof(MONITORINFO) or
592 * sizeof(MONITORINFOEX). Even if set to sizeof(MONITORINFOEX) only parts
593 * from MONITORINFO will be filled.
596 * Pointer to a UNICODE_STRING which will recieve the device's name. The
597 * length should be CCHDEVICENAME
601 * TRUE on success; FALSE on failure (calls SetLastNtError())
606 NtUserGetMonitorInfo(
607 IN HMONITOR hMonitor
,
608 OUT LPMONITORINFO pMonitorInfo
)
611 MONITORINFOEXW MonitorInfo
;
613 DECLARE_RETURN(BOOL
);
615 DPRINT("Enter NtUserGetMonitorInfo\n");
618 /* get monitor object */
619 if (!(Monitor
= UserGetMonitorObject(hMonitor
)))
621 DPRINT("Couldnt find monitor 0x%lx\n", hMonitor
);
625 if(pMonitorInfo
== NULL
)
627 SetLastNtError(STATUS_INVALID_PARAMETER
);
631 /* get size of pMonitorInfo */
632 Status
= MmCopyFromCaller(&MonitorInfo
.cbSize
, &pMonitorInfo
->cbSize
, sizeof (MonitorInfo
.cbSize
));
633 if (!NT_SUCCESS(Status
))
635 SetLastNtError(Status
);
638 if ((MonitorInfo
.cbSize
!= sizeof (MONITORINFO
)) &&
639 (MonitorInfo
.cbSize
!= sizeof (MONITORINFOEXW
)))
641 SetLastNtError(STATUS_INVALID_PARAMETER
);
645 /* fill monitor info */
646 MonitorInfo
.rcMonitor
= Monitor
->rcMonitor
;
647 MonitorInfo
.rcWork
= Monitor
->rcWork
;
648 MonitorInfo
.dwFlags
= 0;
650 if (Monitor
->IsPrimary
)
651 MonitorInfo
.dwFlags
|= MONITORINFOF_PRIMARY
;
653 /* fill device name */
654 if (MonitorInfo
.cbSize
== sizeof (MONITORINFOEXW
))
657 INT len
= Monitor
->DeviceName
.Length
;
658 if (len
>= CCHDEVICENAME
* sizeof (WCHAR
))
659 len
= (CCHDEVICENAME
- 1) * sizeof (WCHAR
);
661 memcpy(MonitorInfo
.szDevice
, Monitor
->DeviceName
.Buffer
, len
);
662 memcpy(MonitorInfo
.szDevice
+ (len
/ sizeof (WCHAR
)), &nul
, sizeof (WCHAR
));
666 Status
= MmCopyToCaller(pMonitorInfo
, &MonitorInfo
, MonitorInfo
.cbSize
);
667 if (!NT_SUCCESS(Status
))
669 DPRINT("GetMonitorInfo: MmCopyToCaller failed\n");
670 SetLastNtError(Status
);
674 DPRINT("GetMonitorInfo: success\n");
679 DPRINT("Leave NtUserGetMonitorInfo, ret=%i\n",_ret_
);
684 /* NtUserMonitorFromPoint
686 * Returns a handle to the monitor containing the given point.
691 * Point for which to find monitor
694 * Specifies the behaviour if the point isn't on any of the monitors.
697 * If the point is found a handle to the monitor is returned; if not the
698 * return value depends on dwFlags
702 NtUserMonitorFromPoint(
708 HMONITOR hMonitor
= NULL
;
710 /* fill inRect (bottom-right exclusive) */
711 InRect
.left
= point
.x
;
712 InRect
.right
= point
.x
+ 1;
713 InRect
.top
= point
.y
;
714 InRect
.bottom
= point
.y
+ 1;
716 /* find intersecting monitor */
717 NumMonitors
= IntGetMonitorsFromRect(&InRect
, &hMonitor
, NULL
, 1, dwFlags
);
720 return (HMONITOR
)NULL
;
726 /* NtUserMonitorFromRect
728 * Returns a handle to the monitor having the largest intersection with a
734 * Pointer to a RECT for which to find monitor
737 * Specifies the behaviour if no monitor intersects the given rect
740 * If a monitor intersects the rect a handle to it is returned; if not the
741 * return value depends on dwFlags
745 NtUserMonitorFromRect(
749 ULONG numMonitors
, iLargestArea
= 0, i
;
751 HMONITOR
*hMonitorList
;
752 HMONITOR hMonitor
= NULL
;
757 status
= MmCopyFromCaller(&rect
, pRect
, sizeof (RECT
));
758 if (!NT_SUCCESS(status
))
760 SetLastNtError(status
);
761 return (HMONITOR
)NULL
;
764 /* find intersecting monitors */
765 numMonitors
= IntGetMonitorsFromRect(&rect
, &hMonitor
, NULL
, 1, dwFlags
);
766 if (numMonitors
<= 1)
771 hMonitorList
= ExAllocatePoolWithTag(PagedPool
,
772 sizeof(HMONITOR
) * numMonitors
,
773 USERTAG_MONITORRECTS
);
774 if (hMonitorList
== NULL
)
776 /* FIXME: EngSetLastError? */
777 return (HMONITOR
)NULL
;
780 rectList
= ExAllocatePoolWithTag(PagedPool
,
781 sizeof(RECT
) * numMonitors
,
782 USERTAG_MONITORRECTS
);
783 if (rectList
== NULL
)
785 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
786 /* FIXME: EngSetLastError? */
787 return (HMONITOR
)NULL
;
790 /* get intersecting monitors */
791 numMonitors
= IntGetMonitorsFromRect(&rect
, hMonitorList
, rectList
,
793 if (numMonitors
== 0)
795 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
796 ExFreePoolWithTag(rectList
, USERTAG_MONITORRECTS
);
797 return (HMONITOR
)NULL
;
800 /* find largest intersection */
801 for (i
= 0; i
< numMonitors
; i
++)
803 ULONG area
= (rectList
[i
].right
- rectList
[i
].left
) *
804 (rectList
[i
].bottom
- rectList
[i
].top
);
805 if (area
>= iLargestArea
)
807 hMonitor
= hMonitorList
[i
];
811 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
812 ExFreePoolWithTag(rectList
, USERTAG_MONITORRECTS
);
820 NtUserMonitorFromWindow(
825 HMONITOR hMonitor
= NULL
;
827 DECLARE_RETURN(HMONITOR
);
829 DPRINT("Enter NtUserMonitorFromWindow\n");
832 if (!(Window
= UserGetWindowObject(hWnd
)))
834 if (dwFlags
== MONITOR_DEFAULTTONULL
)
838 IntGetMonitorsFromRect(NULL
, &hMonitor
, NULL
, 1, dwFlags
);
842 Rect
.left
= Rect
.right
= Window
->rcWindow
.left
;
843 Rect
.top
= Rect
.bottom
= Window
->rcWindow
.bottom
;
845 IntGetMonitorsFromRect(&Rect
, &hMonitor
, NULL
, 1, dwFlags
);
850 DPRINT("Leave NtUserMonitorFromWindow, ret=%i\n",_ret_
);