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 **********************************************************/
51 # define MIN(a, b) ((a) < (b) ? (a) : (b))
54 # define MAX(a, b) ((a) > (b) ? (a) : (b))
57 # define ABS(a) ((a) < (0) ? (-(a)) : (a))
60 /* IntCreateMonitorObject
65 * If the function succeeds a pointer to a MONITOR is returned. On failure
70 IntCreateMonitorObject()
75 Monitor
= UserCreateObject(gHandleTable
, NULL
, &Handle
, otMonitor
, sizeof (MONITOR
));
81 ExInitializeFastMutex(&Monitor
->Lock
);
86 /* IntDestroyMonitorObject
89 * You have to be the owner of the monitors lock to safely destroy it.
94 * Pointer to the MONITOR which shall be deleted
98 IntDestroyMonitorObject(IN PMONITOR pMonitor
)
100 RtlFreeUnicodeString(&pMonitor
->DeviceName
);
101 UserDereferenceObject(pMonitor
);
106 UserGetMonitorObject(IN HMONITOR hMonitor
)
112 SetLastWin32Error(ERROR_INVALID_MONITOR_HANDLE
);
116 Monitor
= (PMONITOR
)UserGetObject(gHandleTable
, hMonitor
, otMonitor
);
119 SetLastWin32Error(ERROR_INVALID_MONITOR_HANDLE
);
123 ASSERT(Monitor
->head
.cLockObj
>= 0);
131 * Creates a new MONITOR and appends it to the list of monitors.
135 * pGdiDevice Pointer to the PDEVOBJ onto which the monitor was attached
136 * DisplayNumber Display Number (starting with 0)
142 IntAttachMonitor(IN PDEVOBJ
*pGdiDevice
,
143 IN ULONG DisplayNumber
)
146 WCHAR Buffer
[CCHDEVICENAME
];
148 DPRINT("Attaching monitor...\n");
150 /* create new monitor object */
151 Monitor
= IntCreateMonitorObject();
154 DPRINT("Couldnt create monitor object\n");
155 return STATUS_INSUFFICIENT_RESOURCES
;
158 _snwprintf(Buffer
, CCHDEVICENAME
, L
"\\\\.\\DISPLAY%d", DisplayNumber
+ 1);
159 if (!RtlCreateUnicodeString(&Monitor
->DeviceName
, Buffer
))
161 DPRINT("Couldn't duplicate monitor name!\n");
162 UserDereferenceObject(Monitor
);
163 UserDeleteObject(UserHMGetHandle(Monitor
), otMonitor
);
164 return STATUS_INSUFFICIENT_RESOURCES
;
167 Monitor
->GdiDevice
= pGdiDevice
;
168 Monitor
->rcMonitor
.left
= 0;
169 Monitor
->rcMonitor
.top
= 0;
170 Monitor
->rcMonitor
.right
= Monitor
->rcMonitor
.left
+ pGdiDevice
->gdiinfo
.ulHorzRes
;
171 Monitor
->rcMonitor
.bottom
= Monitor
->rcMonitor
.top
+ pGdiDevice
->gdiinfo
.ulVertRes
;
172 Monitor
->rcWork
= Monitor
->rcMonitor
;
173 Monitor
->cWndStack
= 0;
175 Monitor
->hrgnMonitor
= IntSysCreateRectRgnIndirect( &Monitor
->rcMonitor
);
177 IntGdiSetRegionOwner(Monitor
->hrgnMonitor
, GDI_OBJ_HMGR_PUBLIC
);
179 if (gMonitorList
== NULL
)
181 DPRINT("Primary monitor is beeing attached\n");
182 Monitor
->IsPrimary
= TRUE
;
183 gMonitorList
= Monitor
;
188 DPRINT("Additional monitor is beeing attached\n");
189 for (p
= gMonitorList
; p
->Next
!= NULL
; p
= p
->Next
)
196 return STATUS_SUCCESS
;
201 * Deletes a MONITOR and removes it from the list of monitors.
205 * pGdiDevice Pointer to the PDEVOBJ from which the monitor was detached
211 IntDetachMonitor(IN PDEVOBJ
*pGdiDevice
)
215 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
217 if (Monitor
->GdiDevice
== pGdiDevice
)
223 /* no monitor for given device found */
224 return STATUS_INVALID_PARAMETER
;
227 if (Monitor
->IsPrimary
&& (Monitor
->Next
!= NULL
|| Monitor
->Prev
!= NULL
))
229 PMONITOR NewPrimaryMonitor
= (Monitor
->Prev
!= NULL
) ? (Monitor
->Prev
) : (Monitor
->Next
);
231 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&NewPrimaryMonitor
->Lock
);
232 NewPrimaryMonitor
->IsPrimary
= TRUE
;
233 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&NewPrimaryMonitor
->Lock
);
236 if (gMonitorList
== Monitor
)
238 gMonitorList
= Monitor
->Next
;
239 if (Monitor
->Next
!= NULL
)
240 Monitor
->Next
->Prev
= NULL
;
244 Monitor
->Prev
->Next
= Monitor
->Next
;
245 if (Monitor
->Next
!= NULL
)
246 Monitor
->Next
->Prev
= Monitor
->Prev
;
249 if (Monitor
->hrgnMonitor
)
250 REGION_FreeRgnByHandle(Monitor
->hrgnMonitor
);
252 IntDestroyMonitorObject(Monitor
);
254 return STATUS_SUCCESS
;
257 /* IntGetPrimaryMonitor
259 * Returns a PMONITOR for the primary monitor
266 IntGetPrimaryMonitor()
270 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
272 /* FIXME: I guess locking the monitor is not neccessary to read 1 int */
273 if (Monitor
->IsPrimary
)
280 /* IntGetMonitorsFromRect
282 * Returns a list of monitor handles/rectangles. The rectangles in the list are
283 * the areas of intersection with the monitors.
288 * Rectangle in desktop coordinates. If this is NULL all monitors are
289 * returned and the rect list is filled with the sizes of the monitors.
292 * Pointer to an array of HMONITOR which is filled with monitor handles.
296 * Pointer to an array of RECT which is filled with intersection rects in
297 * desktop coordinates.
298 * Can be NULL, will be ignored if no intersecting monitor is found and
299 * flags is MONITOR_DEFAULTTONEAREST
302 * Size of the hMonitorList and monitorRectList arguments. If this is zero
303 * hMonitorList and monitorRectList are ignored.
306 * Either 0 or MONITOR_DEFAULTTONEAREST (ignored if rect is NULL)
309 * The number of monitors which intersect the specified region.
313 IntGetMonitorsFromRect(OPTIONAL IN LPCRECTL pRect
,
314 OPTIONAL OUT HMONITOR
*hMonitorList
,
315 OPTIONAL OUT PRECTL monitorRectList
,
316 OPTIONAL IN DWORD listSize
,
317 OPTIONAL IN DWORD flags
)
319 PMONITOR Monitor
, NearestMonitor
= NULL
, PrimaryMonitor
= NULL
;
321 LONG iNearestDistanceX
= 0x7fffffff, iNearestDistanceY
= 0x7fffffff;
323 /* find monitors which intersect the rectangle */
324 for (Monitor
= gMonitorList
; Monitor
!= NULL
; Monitor
= Monitor
->Next
)
326 RECTL MonitorRect
, IntersectionRect
;
328 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&Monitor
->Lock
);
329 MonitorRect
= Monitor
->rcMonitor
;
330 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&Monitor
->Lock
);
332 DPRINT("MonitorRect: left = %d, top = %d, right = %d, bottom = %d\n",
333 MonitorRect
.left
, MonitorRect
.top
, MonitorRect
.right
, MonitorRect
.bottom
);
335 if (flags
== MONITOR_DEFAULTTOPRIMARY
&& Monitor
->IsPrimary
)
337 PrimaryMonitor
= Monitor
;
342 BOOL intersects
= TRUE
;
344 /* check if the rect intersects the monitor */
345 if ((pRect
->right
< MonitorRect
.left
) || (pRect
->left
> MonitorRect
.right
) ||
346 (pRect
->bottom
< MonitorRect
.top
) || (pRect
->top
> MonitorRect
.bottom
))
351 if (flags
== MONITOR_DEFAULTTONEAREST
&& !intersects
)
353 INT distanceX
, distanceY
;
355 distanceX
= MIN(ABS(MonitorRect
.left
- pRect
->right
),
356 ABS(pRect
->left
- MonitorRect
.right
));
357 distanceY
= MIN(ABS(MonitorRect
.top
- pRect
->bottom
),
358 ABS(pRect
->top
- MonitorRect
.bottom
));
360 if (((distanceX
< iNearestDistanceX
) && (distanceY
<= iNearestDistanceY
)) ||
361 ((distanceX
<= iNearestDistanceX
) && (distanceY
< iNearestDistanceY
)))
363 iNearestDistanceX
= distanceX
;
364 iNearestDistanceY
= distanceY
;
365 NearestMonitor
= Monitor
;
372 /* calculate intersection */
373 IntersectionRect
.left
= MAX(MonitorRect
.left
, pRect
->left
);
374 IntersectionRect
.top
= MAX(MonitorRect
.top
, pRect
->top
);
375 IntersectionRect
.right
= MIN(MonitorRect
.right
, pRect
->right
);
376 IntersectionRect
.bottom
= MIN(MonitorRect
.bottom
, pRect
->bottom
);
380 IntersectionRect
= MonitorRect
;
383 if (iCount
< listSize
)
385 if (hMonitorList
!= NULL
)
386 hMonitorList
[iCount
] = UserHMGetHandle(Monitor
);
387 if (monitorRectList
!= NULL
)
388 monitorRectList
[iCount
] = IntersectionRect
;
393 if (iCount
== 0 && flags
== MONITOR_DEFAULTTONEAREST
)
395 if (iCount
< listSize
)
397 if (hMonitorList
!= NULL
)
398 hMonitorList
[iCount
] = UserHMGetHandle(NearestMonitor
);
402 else if (iCount
== 0 && flags
== MONITOR_DEFAULTTOPRIMARY
)
404 if (iCount
< listSize
)
406 if (hMonitorList
!= NULL
)
407 hMonitorList
[iCount
] = UserHMGetHandle(PrimaryMonitor
);
414 /* PUBLIC FUNCTIONS ***********************************************************/
416 /* NtUserEnumDisplayMonitors
418 * Enumerates display monitors which intersect the given HDC/cliprect
423 * Handle to a DC for which to enum intersecting monitors. If this is NULL
424 * it returns all monitors which are part of the current virtual screen.
427 * Clipping rectangle with coordinate system origin at the DCs origin if the
428 * given HDC is not NULL or in virtual screen coordinated if it is NULL.
432 * Pointer to an array of HMONITOR which is filled with monitor handles.
436 * Pointer to an array of RECT which is filled with intersection rectangles.
440 * Size of the hMonitorList and monitorRectList arguments. If this is zero
441 * hMonitorList and monitorRectList are ignored.
444 * The number of monitors which intersect the specified region or -1 on failure.
448 NtUserEnumDisplayMonitors(
450 OPTIONAL IN LPCRECTL pRect
,
451 OPTIONAL OUT HMONITOR
*hMonitorList
,
452 OPTIONAL OUT PRECTL monitorRectList
,
453 OPTIONAL IN DWORD listSize
)
456 HMONITOR
*safeHMonitorList
= NULL
;
457 PRECTL safeRectList
= NULL
;
465 status
= MmCopyFromCaller(&rect
, pRect
, sizeof (RECT
));
466 if (!NT_SUCCESS(status
))
468 DPRINT("MmCopyFromCaller() failed!\n");
469 SetLastNtError(status
);
479 /* get visible region bounding rect */
483 DPRINT("DC_LockDc() failed!\n");
484 /* FIXME: setlasterror? */
487 regionType
= REGION_GetRgnBox(dc
->prgnVis
, &dcRect
);
492 DPRINT("NtGdiGetRgnBox() failed!\n");
495 if (regionType
== NULLREGION
)
497 if (regionType
== COMPLEXREGION
)
502 /* if hDC and pRect are given the area of interest is pRect with
503 coordinate origin at the DC position */
506 rect
.left
+= dcRect
.left
;
507 rect
.right
+= dcRect
.left
;
508 rect
.top
+= dcRect
.top
;
509 rect
.bottom
+= dcRect
.top
;
511 /* if hDC is given and pRect is not the area of interest is the
512 bounding rect of hDC */
519 if (hDC
== NULL
&& pRect
== NULL
)
524 /* find intersecting monitors */
525 numMonitors
= IntGetMonitorsFromRect(myRect
, NULL
, NULL
, 0, 0);
526 if (numMonitors
== 0 || listSize
== 0 ||
527 (hMonitorList
== NULL
&& monitorRectList
== NULL
))
529 DPRINT("numMonitors = %d\n", numMonitors
);
533 if (hMonitorList
!= NULL
&& listSize
!= 0)
535 safeHMonitorList
= ExAllocatePoolWithTag(PagedPool
, sizeof (HMONITOR
) * listSize
, USERTAG_MONITORRECTS
);
536 if (safeHMonitorList
== NULL
)
538 /* FIXME: SetLastWin32Error? */
542 if (monitorRectList
!= NULL
&& listSize
!= 0)
544 safeRectList
= ExAllocatePoolWithTag(PagedPool
, sizeof (RECT
) * listSize
, USERTAG_MONITORRECTS
);
545 if (safeRectList
== NULL
)
547 ExFreePoolWithTag(safeHMonitorList
, USERTAG_MONITORRECTS
);
548 /* FIXME: SetLastWin32Error? */
553 /* get intersecting monitors */
554 numMonitors
= IntGetMonitorsFromRect(myRect
, safeHMonitorList
, safeRectList
,
557 if (hDC
!= NULL
&& pRect
!= NULL
&& safeRectList
!= NULL
)
558 for (i
= 0; i
< numMonitors
; i
++)
560 safeRectList
[i
].left
-= dcRect
.left
;
561 safeRectList
[i
].right
-= dcRect
.left
;
562 safeRectList
[i
].top
-= dcRect
.top
;
563 safeRectList
[i
].bottom
-= dcRect
.top
;
567 if (hMonitorList
!= NULL
&& listSize
!= 0)
569 status
= MmCopyToCaller(hMonitorList
, safeHMonitorList
, sizeof (HMONITOR
) * listSize
);
570 ExFreePool(safeHMonitorList
);
571 if (!NT_SUCCESS(status
))
573 ExFreePoolWithTag(safeRectList
, USERTAG_MONITORRECTS
);
574 SetLastNtError(status
);
578 if (monitorRectList
!= NULL
&& listSize
!= 0)
580 status
= MmCopyToCaller(monitorRectList
, safeRectList
, sizeof (RECT
) * listSize
);
581 ExFreePoolWithTag(safeRectList
, USERTAG_MONITORRECTS
);
582 if (!NT_SUCCESS(status
))
584 SetLastNtError(status
);
592 /* NtUserGetMonitorInfo
594 * Retrieves information about a given monitor
599 * Handle to a monitor for which to get information
602 * Pointer to a MONITORINFO struct which is filled with the information.
603 * The cbSize member must be set to sizeof(MONITORINFO) or
604 * sizeof(MONITORINFOEX). Even if set to sizeof(MONITORINFOEX) only parts
605 * from MONITORINFO will be filled.
608 * Pointer to a UNICODE_STRING which will recieve the device's name. The
609 * length should be CCHDEVICENAME
613 * TRUE on success; FALSE on failure (calls SetLastNtError())
618 NtUserGetMonitorInfo(
619 IN HMONITOR hMonitor
,
620 OUT LPMONITORINFO pMonitorInfo
)
623 MONITORINFOEXW MonitorInfo
;
625 DECLARE_RETURN(BOOL
);
627 DPRINT("Enter NtUserGetMonitorInfo\n");
630 /* get monitor object */
631 if (!(Monitor
= UserGetMonitorObject(hMonitor
)))
633 DPRINT("Couldnt find monitor 0x%lx\n", hMonitor
);
637 if(pMonitorInfo
== NULL
)
639 SetLastNtError(STATUS_INVALID_PARAMETER
);
643 /* get size of pMonitorInfo */
644 Status
= MmCopyFromCaller(&MonitorInfo
.cbSize
, &pMonitorInfo
->cbSize
, sizeof (MonitorInfo
.cbSize
));
645 if (!NT_SUCCESS(Status
))
647 SetLastNtError(Status
);
650 if ((MonitorInfo
.cbSize
!= sizeof (MONITORINFO
)) &&
651 (MonitorInfo
.cbSize
!= sizeof (MONITORINFOEXW
)))
653 SetLastNtError(STATUS_INVALID_PARAMETER
);
657 /* fill monitor info */
658 MonitorInfo
.rcMonitor
= Monitor
->rcMonitor
;
659 MonitorInfo
.rcWork
= Monitor
->rcWork
;
660 MonitorInfo
.dwFlags
= 0;
662 if (Monitor
->IsPrimary
)
663 MonitorInfo
.dwFlags
|= MONITORINFOF_PRIMARY
;
665 /* fill device name */
666 if (MonitorInfo
.cbSize
== sizeof (MONITORINFOEXW
))
669 INT len
= Monitor
->DeviceName
.Length
;
670 if (len
>= CCHDEVICENAME
* sizeof (WCHAR
))
671 len
= (CCHDEVICENAME
- 1) * sizeof (WCHAR
);
673 memcpy(MonitorInfo
.szDevice
, Monitor
->DeviceName
.Buffer
, len
);
674 memcpy(MonitorInfo
.szDevice
+ (len
/ sizeof (WCHAR
)), &nul
, sizeof (WCHAR
));
678 Status
= MmCopyToCaller(pMonitorInfo
, &MonitorInfo
, MonitorInfo
.cbSize
);
679 if (!NT_SUCCESS(Status
))
681 DPRINT("GetMonitorInfo: MmCopyToCaller failed\n");
682 SetLastNtError(Status
);
686 DPRINT("GetMonitorInfo: success\n");
691 DPRINT("Leave NtUserGetMonitorInfo, ret=%i\n",_ret_
);
696 /* NtUserMonitorFromPoint
698 * Returns a handle to the monitor containing the given point.
703 * Point for which to find monitor
706 * Specifies the behaviour if the point isn't on any of the monitors.
709 * If the point is found a handle to the monitor is returned; if not the
710 * return value depends on dwFlags
714 NtUserMonitorFromPoint(
720 HMONITOR hMonitor
= NULL
;
723 InRect
.left
= InRect
.right
= point
.x
;
724 InRect
.top
= InRect
.bottom
= point
.y
;
726 /* find intersecting monitor */
727 NumMonitors
= IntGetMonitorsFromRect(&InRect
, &hMonitor
, NULL
, 1, 0);
730 return (HMONITOR
)NULL
;
733 if (hMonitor
== NULL
)
735 if (dwFlags
== MONITOR_DEFAULTTOPRIMARY
)
737 PMONITOR MonitorObj
= IntGetPrimaryMonitor();
739 hMonitor
= UserHMGetHandle(MonitorObj
);
741 else if (dwFlags
== MONITOR_DEFAULTTONEAREST
)
743 NumMonitors
= IntGetMonitorsFromRect(&InRect
, &hMonitor
, NULL
,
744 1, MONITOR_DEFAULTTONEAREST
);
745 /*ASSERT( (numMonitors > 0) && (hMonitor != NULL) );*/
747 /* else flag is DEFAULTTONULL */
753 /* NtUserMonitorFromRect
755 * Returns a handle to the monitor having the largest intersection with a
761 * Pointer to a RECT for which to find monitor
764 * Specifies the behaviour if no monitor intersects the given rect
767 * If a monitor intersects the rect a handle to it is returned; if not the
768 * return value depends on dwFlags
772 NtUserMonitorFromRect(
776 INT numMonitors
, iLargestArea
= -1, i
;
778 HMONITOR
*hMonitorList
;
779 HMONITOR hMonitor
= NULL
;
784 status
= MmCopyFromCaller(&rect
, pRect
, sizeof (RECT
));
785 if (!NT_SUCCESS(status
))
787 SetLastNtError(status
);
788 return (HMONITOR
)NULL
;
791 /* find intersecting monitors */
792 numMonitors
= IntGetMonitorsFromRect(&rect
, NULL
, NULL
, 0, 0);
795 return (HMONITOR
)NULL
;
798 if (numMonitors
== 0)
800 if (dwFlags
== MONITOR_DEFAULTTOPRIMARY
)
802 PMONITOR monitorObj
= IntGetPrimaryMonitor();
804 return UserHMGetHandle(monitorObj
);
806 else if (dwFlags
== MONITOR_DEFAULTTONEAREST
)
808 numMonitors
= IntGetMonitorsFromRect(&rect
, &hMonitor
, NULL
,
809 1, MONITOR_DEFAULTTONEAREST
);
810 if (numMonitors
<= 0)
813 return (HMONITOR
)NULL
;
819 /* else flag is DEFAULTTONULL */
820 return (HMONITOR
)NULL
;
823 hMonitorList
= ExAllocatePoolWithTag(PagedPool
, sizeof (HMONITOR
) * numMonitors
, USERTAG_MONITORRECTS
);
824 if (hMonitorList
== NULL
)
826 /* FIXME: SetLastWin32Error? */
827 return (HMONITOR
)NULL
;
829 rectList
= ExAllocatePoolWithTag(PagedPool
, sizeof (RECT
) * numMonitors
, USERTAG_MONITORRECTS
);
830 if (rectList
== NULL
)
832 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
833 /* FIXME: SetLastWin32Error? */
834 return (HMONITOR
)NULL
;
837 /* get intersecting monitors */
838 numMonitors
= IntGetMonitorsFromRect(&rect
, hMonitorList
, rectList
,
840 if (numMonitors
<= 0)
842 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
843 ExFreePoolWithTag(rectList
, USERTAG_MONITORRECTS
);
844 return (HMONITOR
)NULL
;
847 /* find largest intersection */
848 for (i
= 0; i
< numMonitors
; i
++)
850 INT area
= (rectList
[i
].right
- rectList
[i
].left
) *
851 (rectList
[i
].bottom
- rectList
[i
].top
);
852 if (area
> iLargestArea
)
854 hMonitor
= hMonitorList
[i
];
858 ExFreePoolWithTag(hMonitorList
, USERTAG_MONITORRECTS
);
859 ExFreePoolWithTag(rectList
, USERTAG_MONITORRECTS
);
867 NtUserMonitorFromWindow(
872 HMONITOR hMonitor
= NULL
;
874 DECLARE_RETURN(HMONITOR
);
876 DPRINT("Enter NtUserMonitorFromWindow\n");
879 if (!(Window
= UserGetWindowObject(hWnd
)))
881 if (dwFlags
== MONITOR_DEFAULTTONULL
)
885 IntGetMonitorsFromRect(NULL
, &hMonitor
, NULL
, 1, dwFlags
);
889 Rect
.left
= Rect
.right
= Window
->rcWindow
.left
;
890 Rect
.top
= Rect
.bottom
= Window
->rcWindow
.bottom
;
892 IntGetMonitorsFromRect(&Rect
, &hMonitor
, NULL
, 1, dwFlags
);
897 DPRINT("Leave NtUserMonitorFromWindow, ret=%i\n",_ret_
);