xref: /reactos/win32ss/user/ntuser/monitor.c (revision 50cf16b3)
1 /*
2  *  COPYRIGHT:        See COPYING in the top level directory
3  *  PROJECT:          ReactOS kernel
4  *  PURPOSE:          pMonitor support
5  *  FILE:             subsys/win32k/ntuser/monitor.c
6  *  PROGRAMERS:       Anich Gregor (blight@blight.eu.org)
7  *                    Rafal Harabien (rafalh@reactos.org)
8  */
9 
10 #include <win32k.h>
11 DBG_DEFAULT_CHANNEL(UserMonitor);
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* GLOBALS *******************************************************************/
17 
18 /* List of monitors */
19 static PMONITOR gMonitorList = NULL;
20 
21 /* PRIVATE FUNCTIONS **********************************************************/
22 
23 /* IntCreateMonitorObject
24  *
25  * Creates a MONITOR
26  *
27  * Return value
28  *   If the function succeeds a pointer to a MONITOR is returned. On failure
29  *   NULL is returned.
30  */
31 static
32 PMONITOR
33 IntCreateMonitorObject(VOID)
34 {
35     return UserCreateObject(gHandleTable, NULL, NULL, NULL, TYPE_MONITOR, sizeof(MONITOR));
36 }
37 
38 /* IntDestroyMonitorObject
39  *
40  * Destroys a MONITOR
41  * You have to be the owner of the monitors lock to safely destroy it.
42  *
43  * Arguments
44  *
45  *   pMonitor
46  *      Pointer to the MONITOR which shall be deleted
47  */
48 static
49 void
50 IntDestroyMonitorObject(IN PMONITOR pMonitor)
51 {
52     /* Remove monitor region */
53     if (pMonitor->hrgnMonitor)
54     {
55         GreSetObjectOwner(pMonitor->hrgnMonitor, GDI_OBJ_HMGR_POWNED);
56         GreDeleteObject(pMonitor->hrgnMonitor);
57     }
58 
59     /* Destroy monitor object */
60     UserDereferenceObject(pMonitor);
61     UserDeleteObject(UserHMGetHandle(pMonitor), TYPE_MONITOR);
62 }
63 
64 /* UserGetMonitorObject
65  *
66  * Returns monitor object from handle or sets last error if handle is invalid
67  *
68  * Arguments
69  *
70  *   hMonitor
71  *      Handle of MONITOR object
72  */
73 PMONITOR NTAPI
74 UserGetMonitorObject(IN HMONITOR hMonitor)
75 {
76     PMONITOR pMonitor;
77 
78     if (!hMonitor)
79     {
80         EngSetLastError(ERROR_INVALID_MONITOR_HANDLE);
81         return NULL;
82     }
83 
84     pMonitor = (PMONITOR)UserGetObject(gHandleTable, hMonitor, TYPE_MONITOR);
85     if (!pMonitor)
86     {
87         EngSetLastError(ERROR_INVALID_MONITOR_HANDLE);
88         return NULL;
89     }
90 
91     return pMonitor;
92 }
93 
94 /* UserGetPrimaryMonitor
95  *
96  * Returns a PMONITOR for the primary monitor
97  *
98  * Return value
99  *   PMONITOR
100  */
101 PMONITOR NTAPI
102 UserGetPrimaryMonitor(VOID)
103 {
104     PMONITOR pMonitor;
105 
106     /* Find primary monitor */
107     for (pMonitor = gMonitorList; pMonitor != NULL; pMonitor = pMonitor->pMonitorNext)
108     {
109         if (pMonitor->IsPrimary)
110             break;
111     }
112 
113     return pMonitor;
114 }
115 
116 /* UserAttachMonitor
117  *
118  * Creates a new MONITOR and appends it to the list of monitors.
119  *
120  * Arguments
121  *
122  *   pGdiDevice     Pointer to the PDEVOBJ onto which the monitor was attached
123  *   DisplayNumber  Display Number (starting with 0)
124  *
125  * Return value
126  *   Returns a NTSTATUS
127  */
128 NTSTATUS NTAPI
129 UserAttachMonitor(IN HDEV hDev)
130 {
131     PMONITOR pMonitor;
132 
133     TRACE("Attaching monitor...\n");
134 
135     /* Create new monitor object */
136     pMonitor = IntCreateMonitorObject();
137     if (pMonitor == NULL)
138     {
139         TRACE("Couldnt create monitor object\n");
140         return STATUS_INSUFFICIENT_RESOURCES;
141     }
142 
143     pMonitor->hDev = hDev;
144     pMonitor->cWndStack = 0;
145 
146     if (gMonitorList == NULL)
147     {
148         TRACE("Primary monitor is beeing attached\n");
149         pMonitor->IsPrimary = TRUE;
150         gMonitorList = pMonitor;
151     }
152     else
153     {
154         PMONITOR pmonLast = gMonitorList;
155         TRACE("Additional monitor is beeing attached\n");
156         while (pmonLast->pMonitorNext != NULL)
157             pmonLast = pmonLast->pMonitorNext;
158 
159         pmonLast->pMonitorNext = pMonitor;
160     }
161 
162     UserUpdateMonitorSize(hDev);
163 
164     return STATUS_SUCCESS;
165 }
166 
167 /* UserDetachMonitor
168  *
169  * Deletes a MONITOR and removes it from the list of monitors.
170  *
171  * Arguments
172  *
173  *   pGdiDevice  Pointer to the PDEVOBJ from which the monitor was detached
174  *
175  * Return value
176  *   Returns a NTSTATUS
177  */
178 NTSTATUS NTAPI
179 UserDetachMonitor(IN HDEV hDev)
180 {
181     PMONITOR pMonitor = gMonitorList, *pLink = &gMonitorList;
182 
183     /* Find monitor attached to given device */
184     while (pMonitor != NULL)
185     {
186         if (pMonitor->hDev == hDev)
187             break;
188 
189         pLink = &pMonitor->pMonitorNext;
190         pMonitor = pMonitor->pMonitorNext;
191     }
192 
193     if (pMonitor == NULL)
194     {
195         /* No monitor has been found */
196         return STATUS_INVALID_PARAMETER;
197     }
198 
199     /* We destroy primary monitor - set next as primary */
200     if (pMonitor->IsPrimary && pMonitor->pMonitorNext != NULL)
201         pMonitor->pMonitorNext->IsPrimary = TRUE;
202 
203     /* Update Next ptr in previous monitor */
204     *pLink = pMonitor->pMonitorNext;
205 
206     /* Finally destroy monitor */
207     IntDestroyMonitorObject(pMonitor);
208 
209     return STATUS_SUCCESS;
210 }
211 
212 /* UserUpdateMonitorSize
213  *
214  * Reset size of the monitor using atached device
215  *
216  * Arguments
217  *
218  *   PMONITOR
219  *      pGdiDevice  Pointer to the PDEVOBJ, which size has changed
220  *
221  * Return value
222  *   Returns a NTSTATUS
223  */
224 NTSTATUS NTAPI
225 UserUpdateMonitorSize(IN HDEV hDev)
226 {
227 	PMONITOR pMonitor;
228     SIZEL DeviceSize;
229 
230     /* Find monitor attached to given device */
231     for (pMonitor = gMonitorList; pMonitor != NULL; pMonitor = pMonitor->pMonitorNext)
232     {
233         if (pMonitor->hDev == hDev)
234             break;
235     }
236 
237     if (pMonitor == NULL)
238     {
239         /* No monitor has been found */
240         return STATUS_INVALID_PARAMETER;
241     }
242 
243     /* Get the size of the hdev */
244     PDEVOBJ_sizl((PPDEVOBJ)hDev, &DeviceSize);
245 
246     /* Update monitor size */
247     pMonitor->rcMonitor.left  = 0;
248     pMonitor->rcMonitor.top   = 0;
249     pMonitor->rcMonitor.right  = pMonitor->rcMonitor.left + DeviceSize.cx;
250     pMonitor->rcMonitor.bottom = pMonitor->rcMonitor.top + DeviceSize.cy;
251     pMonitor->rcWork = pMonitor->rcMonitor;
252 
253     /* Destroy monitor region... */
254     if (pMonitor->hrgnMonitor)
255     {
256         GreSetObjectOwner(pMonitor->hrgnMonitor, GDI_OBJ_HMGR_POWNED);
257         GreDeleteObject(pMonitor->hrgnMonitor);
258     }
259 
260     /* ...and create new one */
261     pMonitor->hrgnMonitor = NtGdiCreateRectRgn(
262         pMonitor->rcMonitor.left,
263         pMonitor->rcMonitor.top,
264         pMonitor->rcMonitor.right,
265         pMonitor->rcMonitor.bottom);
266     if (pMonitor->hrgnMonitor)
267         IntGdiSetRegionOwner(pMonitor->hrgnMonitor, GDI_OBJ_HMGR_PUBLIC);
268 
269     //
270     // Should be Virtual accumulation of all the available monitors.
271     //
272     gpsi->rcScreenReal = pMonitor->rcMonitor;
273 
274     return STATUS_SUCCESS;
275 }
276 
277 /* IntGetMonitorsFromRect
278  *
279  * Returns a list of monitor handles/rectangles. The rectangles in the list are
280  * the areas of intersection with the monitors.
281  *
282  * Arguments
283  *
284  *   pRect
285  *      Rectangle in desktop coordinates. If this is NULL all monitors are
286  *      returned and the rect list is filled with the sizes of the monitors.
287  *
288  *   phMonitorList
289  *      Pointer to an array of HMONITOR which is filled with monitor handles.
290  *      Can be NULL
291  *
292  *   prcMonitorList
293  *      Pointer to an array of RECT which is filled with intersection rects in
294  *      desktop coordinates.
295  *      Can be NULL, will be ignored if no intersecting monitor is found and
296  *      flags is MONITOR_DEFAULTTONEAREST
297  *
298  *   dwListSize
299  *      Size of the phMonitorList and prcMonitorList arguments. If this is zero
300  *      phMonitorList and prcMonitorList are ignored.
301  *
302  *   dwFlags
303  *      Either 0 or MONITOR_DEFAULTTONEAREST (ignored if rect is NULL)
304  *
305  * Returns
306  *   The number of monitors which intersect the specified region.
307  */
308 static
309 UINT
310 IntGetMonitorsFromRect(OPTIONAL IN LPCRECTL pRect,
311                        OPTIONAL OUT HMONITOR *phMonitorList,
312                        OPTIONAL OUT PRECTL prcMonitorList,
313                        OPTIONAL IN DWORD dwListSize,
314                        OPTIONAL IN DWORD dwFlags)
315 {
316     PMONITOR pMonitor, pNearestMonitor = NULL, pPrimaryMonitor = NULL;
317     UINT cMonitors = 0;
318     ULONG iNearestDistance = 0xffffffff;
319 
320     /* Find monitors which intersects the rectangle */
321     for (pMonitor = gMonitorList; pMonitor != NULL; pMonitor = pMonitor->pMonitorNext)
322     {
323         RECTL MonitorRect, IntersectionRect;
324 
325         MonitorRect = pMonitor->rcMonitor;
326 
327         TRACE("MonitorRect: left = %d, top = %d, right = %d, bottom = %d\n",
328                MonitorRect.left, MonitorRect.top, MonitorRect.right, MonitorRect.bottom);
329 
330         /* Save primary monitor for later usage */
331         if (dwFlags == MONITOR_DEFAULTTOPRIMARY && pMonitor->IsPrimary)
332             pPrimaryMonitor = pMonitor;
333 
334         /* Check if a rect is given */
335         if (pRect == NULL)
336         {
337             /* No rect given, so use the full monitor rect */
338             IntersectionRect = MonitorRect;
339         }
340         /* We have a rect, calculate intersection */
341         else if (!RECTL_bIntersectRect(&IntersectionRect, &MonitorRect, pRect))
342         {
343             /* Rects did not intersect */
344             if (dwFlags == MONITOR_DEFAULTTONEAREST)
345             {
346                 ULONG cx, cy, iDistance;
347 
348                 /* Get x and y distance */
349                 cx = min(abs(MonitorRect.left - pRect->right),
350                          abs(pRect->left - MonitorRect.right));
351                 cy = min(abs(MonitorRect.top - pRect->bottom),
352                          abs(pRect->top - MonitorRect.bottom));
353 
354                 /* Calculate distance square */
355                 iDistance = cx * cx + cy * cy;
356 
357                 /* Check if this is the new nearest monitor */
358                 if (iDistance < iNearestDistance)
359                 {
360                     iNearestDistance = iDistance;
361                     pNearestMonitor = pMonitor;
362                 }
363             }
364 
365             continue;
366         }
367 
368         /* Check if there's space in the buffer */
369         if (cMonitors < dwListSize)
370         {
371             /* Save monitor data */
372             if (phMonitorList != NULL)
373                 phMonitorList[cMonitors] = UserHMGetHandle(pMonitor);
374             if (prcMonitorList != NULL)
375                 prcMonitorList[cMonitors] = IntersectionRect;
376         }
377 
378         /* Increase count of found monitors */
379         cMonitors++;
380     }
381 
382     /* Nothing has been found? */
383     if (cMonitors == 0)
384     {
385         /* Check if we shall default to the nearest monitor */
386         if (dwFlags == MONITOR_DEFAULTTONEAREST && pNearestMonitor)
387         {
388             if (phMonitorList && dwListSize > 0)
389                 phMonitorList[cMonitors] = UserHMGetHandle(pNearestMonitor);
390             cMonitors++;
391         }
392         /* Check if we shall default to the primary monitor */
393         else if (dwFlags == MONITOR_DEFAULTTOPRIMARY && pPrimaryMonitor)
394         {
395             if (phMonitorList != NULL && dwListSize > 0)
396                 phMonitorList[cMonitors] = UserHMGetHandle(pPrimaryMonitor);
397             cMonitors++;
398         }
399     }
400 
401     return cMonitors;
402 }
403 
404 PMONITOR NTAPI
405 UserMonitorFromRect(
406     PRECTL pRect,
407     DWORD dwFlags)
408 {
409     ULONG cMonitors, LargestArea = 0, i;
410     PRECTL prcMonitorList = NULL;
411     HMONITOR *phMonitorList = NULL;
412     HMONITOR hMonitor = NULL;
413 
414     /* Check if flags are valid */
415     if (dwFlags != MONITOR_DEFAULTTONULL &&
416         dwFlags != MONITOR_DEFAULTTOPRIMARY &&
417         dwFlags != MONITOR_DEFAULTTONEAREST)
418     {
419         EngSetLastError(ERROR_INVALID_FLAGS);
420         return NULL;
421     }
422 
423     /* Find intersecting monitors */
424     cMonitors = IntGetMonitorsFromRect(pRect, &hMonitor, NULL, 1, dwFlags);
425     if (cMonitors <= 1)
426     {
427         /* No or one monitor found. Just return handle. */
428         goto cleanup;
429     }
430 
431     /* There is more than one monitor. Find monitor with largest intersection.
432        Temporary reset hMonitor */
433     hMonitor = NULL;
434 
435     /* Allocate helper buffers */
436     phMonitorList = ExAllocatePoolWithTag(PagedPool,
437                                           sizeof(HMONITOR) * cMonitors,
438                                           USERTAG_MONITORRECTS);
439     if (phMonitorList == NULL)
440     {
441         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
442         goto cleanup;
443     }
444 
445     prcMonitorList = ExAllocatePoolWithTag(PagedPool,
446                                            sizeof(RECT) * cMonitors,
447                                            USERTAG_MONITORRECTS);
448     if (prcMonitorList == NULL)
449     {
450         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
451         goto cleanup;
452     }
453 
454     /* Get intersecting monitors again but now with rectangle list */
455     cMonitors = IntGetMonitorsFromRect(pRect, phMonitorList, prcMonitorList,
456                                        cMonitors, 0);
457 
458     /* Find largest intersection */
459     for (i = 0; i < cMonitors; i++)
460     {
461         ULONG Area = (prcMonitorList[i].right - prcMonitorList[i].left) *
462                      (prcMonitorList[i].bottom - prcMonitorList[i].top);
463         if (Area >= LargestArea)
464         {
465             hMonitor = phMonitorList[i];
466             LargestArea = Area;
467         }
468     }
469 
470 cleanup:
471     if (phMonitorList)
472         ExFreePoolWithTag(phMonitorList, USERTAG_MONITORRECTS);
473     if (prcMonitorList)
474         ExFreePoolWithTag(prcMonitorList, USERTAG_MONITORRECTS);
475 
476     return UserGetMonitorObject(hMonitor);
477 }
478 
479 PMONITOR
480 FASTCALL
481 UserMonitorFromPoint(
482     IN POINT pt,
483     IN DWORD dwFlags)
484 {
485     RECTL rc;
486     HMONITOR hMonitor = NULL;
487 
488     /* Check if flags are valid */
489     if (dwFlags != MONITOR_DEFAULTTONULL &&
490         dwFlags != MONITOR_DEFAULTTOPRIMARY &&
491         dwFlags != MONITOR_DEFAULTTONEAREST)
492     {
493         EngSetLastError(ERROR_INVALID_FLAGS);
494         return NULL;
495     }
496 
497     /* Fill rect (bottom-right exclusive) */
498     rc.left = pt.x;
499     rc.right = pt.x + 1;
500     rc.top = pt.y;
501     rc.bottom = pt.y + 1;
502 
503     /* Find intersecting monitor */
504     IntGetMonitorsFromRect(&rc, &hMonitor, NULL, 1, dwFlags);
505 
506     return UserGetMonitorObject(hMonitor);
507 }
508 
509 /* PUBLIC FUNCTIONS ***********************************************************/
510 
511 /* NtUserEnumDisplayMonitors
512  *
513  * Enumerates display monitors which intersect the given HDC/cliprect
514  *
515  * Arguments
516  *
517  *   hdc
518  *      Handle to a DC for which to enum intersecting monitors. If this is NULL
519  *      it returns all monitors which are part of the current virtual screen.
520  *
521  *   pUnsafeRect
522  *      Clipping rectangle with coordinate system origin at the DCs origin if the
523  *      given HDC is not NULL or in virtual screen coordinated if it is NULL.
524  *      Can be NULL
525  *
526  *   phUnsafeMonitorList
527  *      Pointer to an array of HMONITOR which is filled with monitor handles.
528  *      Can be NULL
529  *
530  *   prcUnsafeMonitorList
531  *      Pointer to an array of RECT which is filled with intersection rectangles.
532  *      Can be NULL
533  *
534  *   dwListSize
535  *      Size of the hMonitorList and monitorRectList arguments. If this is zero
536  *      hMonitorList and monitorRectList are ignored.
537  *
538  * Returns
539  *   The number of monitors which intersect the specified region or -1 on failure.
540  */
541 INT
542 APIENTRY
543 NtUserEnumDisplayMonitors(
544     OPTIONAL IN HDC hdc,
545     OPTIONAL IN LPCRECTL pUnsafeRect,
546     OPTIONAL OUT HMONITOR *phUnsafeMonitorList,
547     OPTIONAL OUT PRECTL prcUnsafeMonitorList,
548     OPTIONAL IN DWORD dwListSize)
549 {
550     UINT cMonitors, i;
551     INT iRet = -1;
552     HMONITOR *phMonitorList = NULL;
553     PRECTL prcMonitorList = NULL;
554     RECTL rc, *pRect;
555     RECTL DcRect = {0};
556     NTSTATUS Status;
557 
558     /* Get rectangle */
559     if (pUnsafeRect != NULL)
560     {
561         Status = MmCopyFromCaller(&rc, pUnsafeRect, sizeof(RECT));
562         if (!NT_SUCCESS(Status))
563         {
564             TRACE("MmCopyFromCaller() failed!\n");
565             SetLastNtError(Status);
566             return -1;
567         }
568     }
569 
570     if (hdc != NULL)
571     {
572         PDC pDc;
573         INT iRgnType;
574 
575         /* Get visible region bounding rect */
576         pDc = DC_LockDc(hdc);
577         if (pDc == NULL)
578         {
579             TRACE("DC_LockDc() failed!\n");
580             /* FIXME: setlasterror? */
581             return -1;
582         }
583         iRgnType = REGION_GetRgnBox(pDc->prgnVis, &DcRect);
584         DC_UnlockDc(pDc);
585 
586         if (iRgnType == 0)
587         {
588             TRACE("NtGdiGetRgnBox() failed!\n");
589             return -1;
590         }
591         if (iRgnType == NULLREGION)
592             return 0;
593         if (iRgnType == COMPLEXREGION)
594         {
595             /* TODO: Warning */
596         }
597 
598         /* If hdc and pRect are given the area of interest is pRect with
599            coordinate origin at the DC position */
600         if (pUnsafeRect != NULL)
601         {
602             rc.left += DcRect.left;
603             rc.right += DcRect.left;
604             rc.top += DcRect.top;
605             rc.bottom += DcRect.top;
606         }
607         /* If hdc is given and pRect is not the area of interest is the
608            bounding rect of hdc */
609         else
610         {
611             rc = DcRect;
612         }
613     }
614 
615     if (hdc == NULL && pUnsafeRect == NULL)
616         pRect = NULL;
617     else
618         pRect = &rc;
619 
620     UserEnterShared();
621 
622     /* Find intersecting monitors */
623     cMonitors = IntGetMonitorsFromRect(pRect, NULL, NULL, 0, MONITOR_DEFAULTTONULL);
624     if (cMonitors == 0 || dwListSize == 0 ||
625         (phUnsafeMonitorList == NULL && prcUnsafeMonitorList == NULL))
626     {
627         /* Simple case - just return monitors count */
628         TRACE("cMonitors = %u\n", cMonitors);
629         iRet = cMonitors;
630         goto cleanup;
631     }
632 
633     /* Allocate safe buffers */
634     if (phUnsafeMonitorList != NULL && dwListSize != 0)
635     {
636         phMonitorList = ExAllocatePoolWithTag(PagedPool, sizeof (HMONITOR) * dwListSize, USERTAG_MONITORRECTS);
637         if (phMonitorList == NULL)
638         {
639             EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
640             goto cleanup;
641         }
642     }
643     if (prcUnsafeMonitorList != NULL && dwListSize != 0)
644     {
645         prcMonitorList = ExAllocatePoolWithTag(PagedPool, sizeof(RECT) * dwListSize,USERTAG_MONITORRECTS);
646         if (prcMonitorList == NULL)
647         {
648             EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
649             goto cleanup;
650         }
651     }
652 
653     /* Get intersecting monitors */
654     cMonitors = IntGetMonitorsFromRect(pRect, phMonitorList, prcMonitorList,
655                                        dwListSize, MONITOR_DEFAULTTONULL);
656 
657     if (hdc != NULL && pRect != NULL && prcMonitorList != NULL)
658     {
659         for (i = 0; i < min(cMonitors, dwListSize); i++)
660         {
661             _Analysis_assume_(i < dwListSize);
662             prcMonitorList[i].left -= DcRect.left;
663             prcMonitorList[i].right -= DcRect.left;
664             prcMonitorList[i].top -= DcRect.top;
665             prcMonitorList[i].bottom -= DcRect.top;
666         }
667     }
668 
669     /* Output result */
670     if (phUnsafeMonitorList != NULL && dwListSize != 0)
671     {
672         Status = MmCopyToCaller(phUnsafeMonitorList, phMonitorList, sizeof(HMONITOR) * dwListSize);
673         if (!NT_SUCCESS(Status))
674         {
675             SetLastNtError(Status);
676             goto cleanup;
677         }
678     }
679     if (prcUnsafeMonitorList != NULL && dwListSize != 0)
680     {
681         Status = MmCopyToCaller(prcUnsafeMonitorList, prcMonitorList, sizeof(RECT) * dwListSize);
682         if (!NT_SUCCESS(Status))
683         {
684             SetLastNtError(Status);
685             goto cleanup;
686         }
687     }
688 
689     /* Return monitors count on success */
690     iRet = cMonitors;
691 
692 cleanup:
693     if (phMonitorList)
694         ExFreePoolWithTag(phMonitorList, USERTAG_MONITORRECTS);
695     if (prcMonitorList)
696         ExFreePoolWithTag(prcMonitorList, USERTAG_MONITORRECTS);
697 
698     UserLeave();
699     return iRet;
700 }
701 
702 /* NtUserGetMonitorInfo
703  *
704  * Retrieves information about a given monitor
705  *
706  * Arguments
707  *
708  *   hMonitor
709  *      Handle to a monitor for which to get information
710  *
711  *   pMonitorInfoUnsafe
712  *      Pointer to a MONITORINFO struct which is filled with the information.
713  *      The cbSize member must be set to sizeof(MONITORINFO) or
714  *      sizeof(MONITORINFOEX). Even if set to sizeof(MONITORINFOEX) only parts
715  *      from MONITORINFO will be filled.
716  *
717  *   pDevice
718  *      Pointer to a UNICODE_STRING which will receive the device's name. The
719  *      length should be CCHDEVICENAME
720  *      Can be NULL
721  *
722  * Return value
723  *   TRUE on success; FALSE on failure (calls SetLastNtError())
724  *
725  */
726 BOOL
727 APIENTRY
728 NtUserGetMonitorInfo(
729     IN HMONITOR hMonitor,
730     OUT LPMONITORINFO pMonitorInfoUnsafe)
731 {
732     PMONITOR pMonitor;
733     MONITORINFOEXW MonitorInfo;
734     NTSTATUS Status;
735     BOOL bRet = FALSE;
736     PWCHAR pwstrDeviceName;
737 
738     TRACE("Enter NtUserGetMonitorInfo\n");
739     UserEnterShared();
740 
741     /* Get monitor object */
742     pMonitor = UserGetMonitorObject(hMonitor);
743     if (!pMonitor)
744     {
745         TRACE("Couldnt find monitor %p\n", hMonitor);
746         goto cleanup;
747     }
748 
749     /* Check if pMonitorInfoUnsafe is valid */
750     if(pMonitorInfoUnsafe == NULL)
751     {
752         SetLastNtError(STATUS_INVALID_PARAMETER);
753         goto cleanup;
754     }
755 
756     pwstrDeviceName = ((PPDEVOBJ)(pMonitor->hDev))->pGraphicsDevice->szWinDeviceName;
757 
758     /* Get size of pMonitorInfoUnsafe */
759     Status = MmCopyFromCaller(&MonitorInfo.cbSize, &pMonitorInfoUnsafe->cbSize, sizeof(MonitorInfo.cbSize));
760     if (!NT_SUCCESS(Status))
761     {
762         SetLastNtError(Status);
763         goto cleanup;
764     }
765 
766     /* Check if size of struct is valid */
767     if (MonitorInfo.cbSize != sizeof(MONITORINFO) &&
768         MonitorInfo.cbSize != sizeof(MONITORINFOEXW))
769     {
770         SetLastNtError(STATUS_INVALID_PARAMETER);
771         goto cleanup;
772     }
773 
774     /* Fill monitor info */
775     MonitorInfo.rcMonitor = pMonitor->rcMonitor;
776     MonitorInfo.rcWork = pMonitor->rcWork;
777     MonitorInfo.dwFlags = 0;
778     if (pMonitor->IsPrimary)
779         MonitorInfo.dwFlags |= MONITORINFOF_PRIMARY;
780 
781     /* Fill device name */
782     if (MonitorInfo.cbSize == sizeof(MONITORINFOEXW))
783     {
784         RtlStringCbCopyNExW(MonitorInfo.szDevice,
785                           sizeof(MonitorInfo.szDevice),
786                           pwstrDeviceName,
787                           (wcslen(pwstrDeviceName)+1) * sizeof(WCHAR),
788                           NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
789     }
790 
791     /* Output data */
792     Status = MmCopyToCaller(pMonitorInfoUnsafe, &MonitorInfo, MonitorInfo.cbSize);
793     if (!NT_SUCCESS(Status))
794     {
795         TRACE("GetMonitorInfo: MmCopyToCaller failed\n");
796         SetLastNtError(Status);
797         goto cleanup;
798     }
799 
800     TRACE("GetMonitorInfo: success\n");
801     bRet = TRUE;
802 
803 cleanup:
804     TRACE("Leave NtUserGetMonitorInfo, ret=%i\n", bRet);
805     UserLeave();
806     return bRet;
807 }
808 
809 /* NtUserMonitorFromPoint
810  *
811  * Returns a handle to the monitor containing the given point.
812  *
813  * Arguments
814  *
815  *   pt
816  *     Point for which to find monitor
817  *
818  *   dwFlags
819  *     Specifies the behaviour if the point isn't on any of the monitors.
820  *
821  * Return value
822  *   If the point is found a handle to the monitor is returned; if not the
823  *   return value depends on dwFlags
824  */
825 HMONITOR
826 APIENTRY
827 NtUserMonitorFromPoint(
828     IN POINT pt,
829     IN DWORD dwFlags)
830 {
831     RECTL rc;
832     HMONITOR hMonitor = NULL;
833 
834     /* Check if flags are valid */
835     if (dwFlags != MONITOR_DEFAULTTONULL &&
836         dwFlags != MONITOR_DEFAULTTOPRIMARY &&
837         dwFlags != MONITOR_DEFAULTTONEAREST)
838     {
839         EngSetLastError(ERROR_INVALID_FLAGS);
840         return NULL;
841     }
842 
843     /* Fill rect (bottom-right exclusive) */
844     rc.left = pt.x;
845     rc.right = pt.x + 1;
846     rc.top = pt.y;
847     rc.bottom = pt.y + 1;
848 
849     UserEnterShared();
850 
851     /* Find intersecting monitor */
852     IntGetMonitorsFromRect(&rc, &hMonitor, NULL, 1, dwFlags);
853 
854     UserLeave();
855     return hMonitor;
856 }
857 
858 /* NtUserMonitorFromRect
859  *
860  * Returns a handle to the monitor having the largest intersection with a
861  * given rectangle
862  *
863  * Arguments
864  *
865  *   pRectUnsafe
866  *     Pointer to a RECT for which to find monitor
867  *
868  *   dwFlags
869  *     Specifies the behaviour if no monitor intersects the given rect
870  *
871  * Return value
872  *   If a monitor intersects the rect a handle to it is returned; if not the
873  *   return value depends on dwFlags
874  */
875 HMONITOR
876 APIENTRY
877 NtUserMonitorFromRect(
878     IN LPCRECTL pRectUnsafe,
879     IN DWORD dwFlags)
880 {
881     ULONG cMonitors, LargestArea = 0, i;
882     PRECTL prcMonitorList = NULL;
883     HMONITOR *phMonitorList = NULL;
884     HMONITOR hMonitor = NULL;
885     RECTL Rect;
886     NTSTATUS Status;
887 
888     /* Check if flags are valid */
889     if (dwFlags != MONITOR_DEFAULTTONULL &&
890         dwFlags != MONITOR_DEFAULTTOPRIMARY &&
891         dwFlags != MONITOR_DEFAULTTONEAREST)
892     {
893         EngSetLastError(ERROR_INVALID_FLAGS);
894         return NULL;
895     }
896 
897     /* Copy rectangle to safe buffer */
898     Status = MmCopyFromCaller(&Rect, pRectUnsafe, sizeof (RECT));
899     if (!NT_SUCCESS(Status))
900     {
901         SetLastNtError(Status);
902         return NULL;
903     }
904 
905     UserEnterShared();
906 
907     /* Find intersecting monitors */
908     cMonitors = IntGetMonitorsFromRect(&Rect, &hMonitor, NULL, 1, dwFlags);
909     if (cMonitors <= 1)
910     {
911         /* No or one monitor found. Just return handle. */
912         goto cleanup;
913     }
914 
915     /* There is more than one monitor. Find monitor with largest intersection.
916        Temporary reset hMonitor */
917     hMonitor = NULL;
918 
919     /* Allocate helper buffers */
920     phMonitorList = ExAllocatePoolWithTag(PagedPool,
921                                           sizeof(HMONITOR) * cMonitors,
922                                           USERTAG_MONITORRECTS);
923     if (phMonitorList == NULL)
924     {
925         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
926         goto cleanup;
927     }
928 
929     prcMonitorList = ExAllocatePoolWithTag(PagedPool,
930                                            sizeof(RECT) * cMonitors,
931                                            USERTAG_MONITORRECTS);
932     if (prcMonitorList == NULL)
933     {
934         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
935         goto cleanup;
936     }
937 
938     /* Get intersecting monitors again but now with rectangle list */
939     cMonitors = IntGetMonitorsFromRect(&Rect, phMonitorList, prcMonitorList,
940                                        cMonitors, 0);
941 
942     /* Find largest intersection */
943     for (i = 0; i < cMonitors; i++)
944     {
945         ULONG Area = (prcMonitorList[i].right - prcMonitorList[i].left) *
946                      (prcMonitorList[i].bottom - prcMonitorList[i].top);
947         if (Area >= LargestArea)
948         {
949             hMonitor = phMonitorList[i];
950             LargestArea = Area;
951         }
952     }
953 
954 cleanup:
955     if (phMonitorList)
956         ExFreePoolWithTag(phMonitorList, USERTAG_MONITORRECTS);
957     if (prcMonitorList)
958         ExFreePoolWithTag(prcMonitorList, USERTAG_MONITORRECTS);
959     UserLeave();
960 
961     return hMonitor;
962 }
963 
964 
965 HMONITOR
966 APIENTRY
967 NtUserMonitorFromWindow(
968     IN HWND hWnd,
969     IN DWORD dwFlags)
970 {
971     PWND pWnd;
972     HMONITOR hMonitor = NULL;
973     RECTL Rect = {0, 0, 0, 0};
974 
975     TRACE("Enter NtUserMonitorFromWindow\n");
976 
977     /* Check if flags are valid */
978     if (dwFlags != MONITOR_DEFAULTTONULL &&
979         dwFlags != MONITOR_DEFAULTTOPRIMARY &&
980         dwFlags != MONITOR_DEFAULTTONEAREST)
981     {
982         EngSetLastError(ERROR_INVALID_FLAGS);
983         return NULL;
984     }
985 
986     UserEnterShared();
987 
988     /* If window is given, use it first */
989     if (hWnd)
990     {
991         /* Get window object */
992         pWnd = UserGetWindowObject(hWnd);
993         if (!pWnd)
994             goto cleanup;
995 
996         /* Find only monitors which have intersection with given window */
997         Rect.left = Rect.right = pWnd->rcWindow.left;
998         Rect.top = Rect.bottom = pWnd->rcWindow.bottom;
999     }
1000 
1001     /* Find monitors now */
1002     IntGetMonitorsFromRect(&Rect, &hMonitor, NULL, 1, dwFlags);
1003 
1004 cleanup:
1005     TRACE("Leave NtUserMonitorFromWindow, ret=%p\n", hMonitor);
1006     UserLeave();
1007     return hMonitor;
1008 }
1009