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