[Win32k]
[reactos.git] / reactos / win32ss / user / 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, 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()
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 = IntSysCreateRectRgnIndirect(&pMonitor->rcMonitor);
262 if (pMonitor->hrgnMonitor)
263 IntGdiSetRegionOwner(pMonitor->hrgnMonitor, GDI_OBJ_HMGR_PUBLIC);
264
265 return STATUS_SUCCESS;
266 }
267
268 /* IntGetMonitorsFromRect
269 *
270 * Returns a list of monitor handles/rectangles. The rectangles in the list are
271 * the areas of intersection with the monitors.
272 *
273 * Arguments
274 *
275 * pRect
276 * Rectangle in desktop coordinates. If this is NULL all monitors are
277 * returned and the rect list is filled with the sizes of the monitors.
278 *
279 * phMonitorList
280 * Pointer to an array of HMONITOR which is filled with monitor handles.
281 * Can be NULL
282 *
283 * prcMonitorList
284 * Pointer to an array of RECT which is filled with intersection rects in
285 * desktop coordinates.
286 * Can be NULL, will be ignored if no intersecting monitor is found and
287 * flags is MONITOR_DEFAULTTONEAREST
288 *
289 * dwListSize
290 * Size of the phMonitorList and prcMonitorList arguments. If this is zero
291 * phMonitorList and prcMonitorList are ignored.
292 *
293 * dwFlags
294 * Either 0 or MONITOR_DEFAULTTONEAREST (ignored if rect is NULL)
295 *
296 * Returns
297 * The number of monitors which intersect the specified region.
298 */
299 static
300 UINT
301 IntGetMonitorsFromRect(OPTIONAL IN LPCRECTL pRect,
302 OPTIONAL OUT HMONITOR *phMonitorList,
303 OPTIONAL OUT PRECTL prcMonitorList,
304 OPTIONAL IN DWORD dwListSize,
305 OPTIONAL IN DWORD dwFlags)
306 {
307 PMONITOR pMonitor, pNearestMonitor = NULL, pPrimaryMonitor = NULL;
308 UINT cMonitors = 0;
309 ULONG iNearestDistance = 0xffffffff;
310
311 /* Find monitors which intersects the rectangle */
312 for (pMonitor = gMonitorList; pMonitor != NULL; pMonitor = pMonitor->pMonitorNext)
313 {
314 RECTL MonitorRect, IntersectionRect;
315
316 MonitorRect = pMonitor->rcMonitor;
317
318 TRACE("MonitorRect: left = %d, top = %d, right = %d, bottom = %d\n",
319 MonitorRect.left, MonitorRect.top, MonitorRect.right, MonitorRect.bottom);
320
321 /* Save primary monitor for later usage */
322 if (dwFlags == MONITOR_DEFAULTTOPRIMARY && pMonitor->IsPrimary)
323 pPrimaryMonitor = pMonitor;
324
325 /* Check if a rect is given */
326 if (pRect == NULL)
327 {
328 /* No rect given, so use the full monitor rect */
329 IntersectionRect = MonitorRect;
330 }
331 /* We have a rect, calculate intersection */
332 else if (!RECTL_bIntersectRect(&IntersectionRect, &MonitorRect, pRect))
333 {
334 /* Rects did not intersect */
335 if (dwFlags == MONITOR_DEFAULTTONEAREST)
336 {
337 ULONG cx, cy, iDistance;
338
339 /* Get x and y distance */
340 cx = min(abs(MonitorRect.left - pRect->right),
341 abs(pRect->left - MonitorRect.right));
342 cy = min(abs(MonitorRect.top - pRect->bottom),
343 abs(pRect->top - MonitorRect.bottom));
344
345 /* Calculate distance square */
346 iDistance = cx * cx + cy * cy;
347
348 /* Check if this is the new nearest monitor */
349 if (iDistance < iNearestDistance)
350 {
351 iNearestDistance = iDistance;
352 pNearestMonitor = pMonitor;
353 }
354 }
355
356 continue;
357 }
358
359 /* Check if there's space in the buffer */
360 if (cMonitors < dwListSize)
361 {
362 /* Save monitor data */
363 if (phMonitorList != NULL)
364 phMonitorList[cMonitors] = UserHMGetHandle(pMonitor);
365 if (prcMonitorList != NULL)
366 prcMonitorList[cMonitors] = IntersectionRect;
367 }
368
369 /* Increase count of found monitors */
370 cMonitors++;
371 }
372
373 /* Nothing has been found? */
374 if (cMonitors == 0)
375 {
376 /* Check if we shall default to the nearest monitor */
377 if (dwFlags == MONITOR_DEFAULTTONEAREST && pNearestMonitor)
378 {
379 if (phMonitorList && dwListSize > 0)
380 phMonitorList[cMonitors] = UserHMGetHandle(pNearestMonitor);
381 cMonitors++;
382 }
383 /* Check if we shall default to the primary monitor */
384 else if (dwFlags == MONITOR_DEFAULTTOPRIMARY && pPrimaryMonitor)
385 {
386 if (phMonitorList != NULL && dwListSize > 0)
387 phMonitorList[cMonitors] = UserHMGetHandle(pPrimaryMonitor);
388 cMonitors++;
389 }
390 }
391
392 return cMonitors;
393 }
394
395 PMONITOR NTAPI
396 UserMonitorFromRect(
397 PRECTL pRect,
398 DWORD dwFlags)
399 {
400 ULONG cMonitors, LargestArea = 0, i;
401 PRECTL prcMonitorList = NULL;
402 HMONITOR *phMonitorList = NULL;
403 HMONITOR hMonitor = NULL;
404
405 /* Check if flags are valid */
406 if (dwFlags != MONITOR_DEFAULTTONULL &&
407 dwFlags != MONITOR_DEFAULTTOPRIMARY &&
408 dwFlags != MONITOR_DEFAULTTONEAREST)
409 {
410 EngSetLastError(ERROR_INVALID_FLAGS);
411 return NULL;
412 }
413
414 /* Find intersecting monitors */
415 cMonitors = IntGetMonitorsFromRect(pRect, &hMonitor, NULL, 1, dwFlags);
416 if (cMonitors <= 1)
417 {
418 /* No or one monitor found. Just return handle. */
419 goto cleanup;
420 }
421
422 /* There is more than one monitor. Find monitor with largest intersection.
423 Temporary reset hMonitor */
424 hMonitor = NULL;
425
426 /* Allocate helper buffers */
427 phMonitorList = ExAllocatePoolWithTag(PagedPool,
428 sizeof(HMONITOR) * cMonitors,
429 USERTAG_MONITORRECTS);
430 if (phMonitorList == NULL)
431 {
432 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
433 goto cleanup;
434 }
435
436 prcMonitorList = ExAllocatePoolWithTag(PagedPool,
437 sizeof(RECT) * cMonitors,
438 USERTAG_MONITORRECTS);
439 if (prcMonitorList == NULL)
440 {
441 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
442 goto cleanup;
443 }
444
445 /* Get intersecting monitors again but now with rectangle list */
446 cMonitors = IntGetMonitorsFromRect(pRect, phMonitorList, prcMonitorList,
447 cMonitors, 0);
448
449 /* Find largest intersection */
450 for (i = 0; i < cMonitors; i++)
451 {
452 ULONG Area = (prcMonitorList[i].right - prcMonitorList[i].left) *
453 (prcMonitorList[i].bottom - prcMonitorList[i].top);
454 if (Area >= LargestArea)
455 {
456 hMonitor = phMonitorList[i];
457 LargestArea = Area;
458 }
459 }
460
461 cleanup:
462 if (phMonitorList)
463 ExFreePoolWithTag(phMonitorList, USERTAG_MONITORRECTS);
464 if (prcMonitorList)
465 ExFreePoolWithTag(prcMonitorList, USERTAG_MONITORRECTS);
466
467 return UserGetMonitorObject(hMonitor);
468 }
469
470
471 /* PUBLIC FUNCTIONS ***********************************************************/
472
473 /* NtUserEnumDisplayMonitors
474 *
475 * Enumerates display monitors which intersect the given HDC/cliprect
476 *
477 * Arguments
478 *
479 * hdc
480 * Handle to a DC for which to enum intersecting monitors. If this is NULL
481 * it returns all monitors which are part of the current virtual screen.
482 *
483 * pUnsafeRect
484 * Clipping rectangle with coordinate system origin at the DCs origin if the
485 * given HDC is not NULL or in virtual screen coordinated if it is NULL.
486 * Can be NULL
487 *
488 * phUnsafeMonitorList
489 * Pointer to an array of HMONITOR which is filled with monitor handles.
490 * Can be NULL
491 *
492 * prcUnsafeMonitorList
493 * Pointer to an array of RECT which is filled with intersection rectangles.
494 * Can be NULL
495 *
496 * dwListSize
497 * Size of the hMonitorList and monitorRectList arguments. If this is zero
498 * hMonitorList and monitorRectList are ignored.
499 *
500 * Returns
501 * The number of monitors which intersect the specified region or -1 on failure.
502 */
503 INT
504 APIENTRY
505 NtUserEnumDisplayMonitors(
506 OPTIONAL IN HDC hdc,
507 OPTIONAL IN LPCRECTL pUnsafeRect,
508 OPTIONAL OUT HMONITOR *phUnsafeMonitorList,
509 OPTIONAL OUT PRECTL prcUnsafeMonitorList,
510 OPTIONAL IN DWORD dwListSize)
511 {
512 INT cMonitors, iRet = -1, i;
513 HMONITOR *phMonitorList = NULL;
514 PRECTL prcMonitorList = NULL;
515 RECTL rc, *pRect;
516 RECTL DcRect = {0};
517 NTSTATUS Status;
518
519 /* Get rectangle */
520 if (pUnsafeRect != NULL)
521 {
522 Status = MmCopyFromCaller(&rc, pUnsafeRect, sizeof(RECT));
523 if (!NT_SUCCESS(Status))
524 {
525 TRACE("MmCopyFromCaller() failed!\n");
526 SetLastNtError(Status);
527 return -1;
528 }
529 }
530
531 if (hdc != NULL)
532 {
533 PDC pDc;
534 INT iRgnType;
535
536 /* Get visible region bounding rect */
537 pDc = DC_LockDc(hdc);
538 if (pDc == NULL)
539 {
540 TRACE("DC_LockDc() failed!\n");
541 /* FIXME: setlasterror? */
542 return -1;
543 }
544 iRgnType = REGION_GetRgnBox(pDc->prgnVis, &DcRect);
545 DC_UnlockDc(pDc);
546
547 if (iRgnType == 0)
548 {
549 TRACE("NtGdiGetRgnBox() failed!\n");
550 return -1;
551 }
552 if (iRgnType == NULLREGION)
553 return 0;
554 if (iRgnType == COMPLEXREGION)
555 {
556 /* TODO: Warning */
557 }
558
559 /* If hdc and pRect are given the area of interest is pRect with
560 coordinate origin at the DC position */
561 if (pUnsafeRect != NULL)
562 {
563 rc.left += DcRect.left;
564 rc.right += DcRect.left;
565 rc.top += DcRect.top;
566 rc.bottom += DcRect.top;
567 }
568 /* If hdc is given and pRect is not the area of interest is the
569 bounding rect of hdc */
570 else
571 {
572 rc = DcRect;
573 }
574 }
575
576 if (hdc == NULL && pUnsafeRect == NULL)
577 pRect = NULL;
578 else
579 pRect = &rc;
580
581 UserEnterShared();
582
583 /* Find intersecting monitors */
584 cMonitors = IntGetMonitorsFromRect(pRect, NULL, NULL, 0, MONITOR_DEFAULTTONULL);
585 if (cMonitors == 0 || dwListSize == 0 ||
586 (phUnsafeMonitorList == NULL && prcUnsafeMonitorList == NULL))
587 {
588 /* Simple case - just return monitors count */
589 TRACE("cMonitors = %d\n", cMonitors);
590 iRet = cMonitors;
591 goto cleanup;
592 }
593
594 /* Allocate safe buffers */
595 if (phUnsafeMonitorList != NULL && dwListSize != 0)
596 {
597 phMonitorList = ExAllocatePoolWithTag(PagedPool, sizeof (HMONITOR) * dwListSize, USERTAG_MONITORRECTS);
598 if (phMonitorList == NULL)
599 {
600 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
601 goto cleanup;
602 }
603 }
604 if (prcUnsafeMonitorList != NULL && dwListSize != 0)
605 {
606 prcMonitorList = ExAllocatePoolWithTag(PagedPool, sizeof (RECT) * dwListSize, USERTAG_MONITORRECTS);
607 if (prcMonitorList == NULL)
608 {
609 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
610 goto cleanup;
611 }
612 }
613
614 /* Get intersecting monitors */
615 cMonitors = IntGetMonitorsFromRect(pRect, phMonitorList, prcMonitorList,
616 dwListSize, MONITOR_DEFAULTTONULL);
617
618 if (hdc != NULL && pRect != NULL && prcMonitorList != NULL)
619 for (i = 0; i < cMonitors; i++)
620 {
621 prcMonitorList[i].left -= DcRect.left;
622 prcMonitorList[i].right -= DcRect.left;
623 prcMonitorList[i].top -= DcRect.top;
624 prcMonitorList[i].bottom -= DcRect.top;
625 }
626
627 /* Output result */
628 if (phUnsafeMonitorList != NULL && dwListSize != 0)
629 {
630 Status = MmCopyToCaller(phUnsafeMonitorList, phMonitorList, sizeof(HMONITOR) * dwListSize);
631 if (!NT_SUCCESS(Status))
632 {
633 SetLastNtError(Status);
634 goto cleanup;
635 }
636 }
637 if (prcUnsafeMonitorList != NULL && dwListSize != 0)
638 {
639 Status = MmCopyToCaller(prcUnsafeMonitorList, prcMonitorList, sizeof(RECT) * dwListSize);
640 if (!NT_SUCCESS(Status))
641 {
642 SetLastNtError(Status);
643 goto cleanup;
644 }
645 }
646
647 /* Return monitors count on success */
648 iRet = cMonitors;
649
650 cleanup:
651 if (phMonitorList)
652 ExFreePoolWithTag(phMonitorList, USERTAG_MONITORRECTS);
653 if (prcMonitorList)
654 ExFreePoolWithTag(prcMonitorList, USERTAG_MONITORRECTS);
655
656 UserLeave();
657 return iRet;
658 }
659
660 /* NtUserGetMonitorInfo
661 *
662 * Retrieves information about a given monitor
663 *
664 * Arguments
665 *
666 * hMonitor
667 * Handle to a monitor for which to get information
668 *
669 * pMonitorInfoUnsafe
670 * Pointer to a MONITORINFO struct which is filled with the information.
671 * The cbSize member must be set to sizeof(MONITORINFO) or
672 * sizeof(MONITORINFOEX). Even if set to sizeof(MONITORINFOEX) only parts
673 * from MONITORINFO will be filled.
674 *
675 * pDevice
676 * Pointer to a UNICODE_STRING which will receive the device's name. The
677 * length should be CCHDEVICENAME
678 * Can be NULL
679 *
680 * Return value
681 * TRUE on success; FALSE on failure (calls SetLastNtError())
682 *
683 */
684 BOOL
685 APIENTRY
686 NtUserGetMonitorInfo(
687 IN HMONITOR hMonitor,
688 OUT LPMONITORINFO pMonitorInfoUnsafe)
689 {
690 PMONITOR pMonitor;
691 MONITORINFOEXW MonitorInfo;
692 NTSTATUS Status;
693 BOOL bRet = FALSE;
694 PWCHAR pwstrDeviceName;
695
696 TRACE("Enter NtUserGetMonitorInfo\n");
697 UserEnterShared();
698
699 /* Get monitor object */
700 pMonitor = UserGetMonitorObject(hMonitor);
701 if (!pMonitor)
702 {
703 TRACE("Couldnt find monitor %p\n", hMonitor);
704 goto cleanup;
705 }
706
707 /* Check if pMonitorInfoUnsafe is valid */
708 if(pMonitorInfoUnsafe == NULL)
709 {
710 SetLastNtError(STATUS_INVALID_PARAMETER);
711 goto cleanup;
712 }
713
714 pwstrDeviceName = ((PPDEVOBJ)(pMonitor->hDev))->pGraphicsDevice->szWinDeviceName;
715
716 /* Get size of pMonitorInfoUnsafe */
717 Status = MmCopyFromCaller(&MonitorInfo.cbSize, &pMonitorInfoUnsafe->cbSize, sizeof(MonitorInfo.cbSize));
718 if (!NT_SUCCESS(Status))
719 {
720 SetLastNtError(Status);
721 goto cleanup;
722 }
723
724 /* Check if size of struct is valid */
725 if (MonitorInfo.cbSize != sizeof(MONITORINFO) &&
726 MonitorInfo.cbSize != sizeof(MONITORINFOEXW))
727 {
728 SetLastNtError(STATUS_INVALID_PARAMETER);
729 goto cleanup;
730 }
731
732 /* Fill monitor info */
733 MonitorInfo.rcMonitor = pMonitor->rcMonitor;
734 MonitorInfo.rcWork = pMonitor->rcWork;
735 MonitorInfo.dwFlags = 0;
736 if (pMonitor->IsPrimary)
737 MonitorInfo.dwFlags |= MONITORINFOF_PRIMARY;
738
739 /* Fill device name */
740 if (MonitorInfo.cbSize == sizeof(MONITORINFOEXW))
741 {
742 RtlStringCbCopyNExW(MonitorInfo.szDevice,
743 sizeof(MonitorInfo.szDevice),
744 pwstrDeviceName,
745 (wcslen(pwstrDeviceName)+1) * sizeof(WCHAR),
746 NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
747 }
748
749 /* Output data */
750 Status = MmCopyToCaller(pMonitorInfoUnsafe, &MonitorInfo, MonitorInfo.cbSize);
751 if (!NT_SUCCESS(Status))
752 {
753 TRACE("GetMonitorInfo: MmCopyToCaller failed\n");
754 SetLastNtError(Status);
755 goto cleanup;
756 }
757
758 TRACE("GetMonitorInfo: success\n");
759 bRet = TRUE;
760
761 cleanup:
762 TRACE("Leave NtUserGetMonitorInfo, ret=%i\n", bRet);
763 UserLeave();
764 return bRet;
765 }
766
767 /* NtUserMonitorFromPoint
768 *
769 * Returns a handle to the monitor containing the given point.
770 *
771 * Arguments
772 *
773 * pt
774 * Point for which to find monitor
775 *
776 * dwFlags
777 * Specifies the behaviour if the point isn't on any of the monitors.
778 *
779 * Return value
780 * If the point is found a handle to the monitor is returned; if not the
781 * return value depends on dwFlags
782 */
783 HMONITOR
784 APIENTRY
785 NtUserMonitorFromPoint(
786 IN POINT pt,
787 IN DWORD dwFlags)
788 {
789 RECTL rc;
790 HMONITOR hMonitor = NULL;
791
792 /* Check if flags are valid */
793 if (dwFlags != MONITOR_DEFAULTTONULL &&
794 dwFlags != MONITOR_DEFAULTTOPRIMARY &&
795 dwFlags != MONITOR_DEFAULTTONEAREST)
796 {
797 EngSetLastError(ERROR_INVALID_FLAGS);
798 return NULL;
799 }
800
801 /* Fill rect (bottom-right exclusive) */
802 rc.left = pt.x;
803 rc.right = pt.x + 1;
804 rc.top = pt.y;
805 rc.bottom = pt.y + 1;
806
807 UserEnterShared();
808
809 /* Find intersecting monitor */
810 IntGetMonitorsFromRect(&rc, &hMonitor, NULL, 1, dwFlags);
811
812 UserLeave();
813 return hMonitor;
814 }
815
816 /* NtUserMonitorFromRect
817 *
818 * Returns a handle to the monitor having the largest intersection with a
819 * given rectangle
820 *
821 * Arguments
822 *
823 * pRectUnsafe
824 * Pointer to a RECT for which to find monitor
825 *
826 * dwFlags
827 * Specifies the behaviour if no monitor intersects the given rect
828 *
829 * Return value
830 * If a monitor intersects the rect a handle to it is returned; if not the
831 * return value depends on dwFlags
832 */
833 HMONITOR
834 APIENTRY
835 NtUserMonitorFromRect(
836 IN LPCRECTL pRectUnsafe,
837 IN DWORD dwFlags)
838 {
839 ULONG cMonitors, LargestArea = 0, i;
840 PRECTL prcMonitorList = NULL;
841 HMONITOR *phMonitorList = NULL;
842 HMONITOR hMonitor = NULL;
843 RECTL Rect;
844 NTSTATUS Status;
845
846 /* Check if flags are valid */
847 if (dwFlags != MONITOR_DEFAULTTONULL &&
848 dwFlags != MONITOR_DEFAULTTOPRIMARY &&
849 dwFlags != MONITOR_DEFAULTTONEAREST)
850 {
851 EngSetLastError(ERROR_INVALID_FLAGS);
852 return NULL;
853 }
854
855 /* Copy rectangle to safe buffer */
856 Status = MmCopyFromCaller(&Rect, pRectUnsafe, sizeof (RECT));
857 if (!NT_SUCCESS(Status))
858 {
859 SetLastNtError(Status);
860 return NULL;
861 }
862
863 UserEnterShared();
864
865 /* Find intersecting monitors */
866 cMonitors = IntGetMonitorsFromRect(&Rect, &hMonitor, NULL, 1, dwFlags);
867 if (cMonitors <= 1)
868 {
869 /* No or one monitor found. Just return handle. */
870 goto cleanup;
871 }
872
873 /* There is more than one monitor. Find monitor with largest intersection.
874 Temporary reset hMonitor */
875 hMonitor = NULL;
876
877 /* Allocate helper buffers */
878 phMonitorList = ExAllocatePoolWithTag(PagedPool,
879 sizeof(HMONITOR) * cMonitors,
880 USERTAG_MONITORRECTS);
881 if (phMonitorList == NULL)
882 {
883 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
884 goto cleanup;
885 }
886
887 prcMonitorList = ExAllocatePoolWithTag(PagedPool,
888 sizeof(RECT) * cMonitors,
889 USERTAG_MONITORRECTS);
890 if (prcMonitorList == NULL)
891 {
892 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
893 goto cleanup;
894 }
895
896 /* Get intersecting monitors again but now with rectangle list */
897 cMonitors = IntGetMonitorsFromRect(&Rect, phMonitorList, prcMonitorList,
898 cMonitors, 0);
899
900 /* Find largest intersection */
901 for (i = 0; i < cMonitors; i++)
902 {
903 ULONG Area = (prcMonitorList[i].right - prcMonitorList[i].left) *
904 (prcMonitorList[i].bottom - prcMonitorList[i].top);
905 if (Area >= LargestArea)
906 {
907 hMonitor = phMonitorList[i];
908 LargestArea = Area;
909 }
910 }
911
912 cleanup:
913 if (phMonitorList)
914 ExFreePoolWithTag(phMonitorList, USERTAG_MONITORRECTS);
915 if (prcMonitorList)
916 ExFreePoolWithTag(prcMonitorList, USERTAG_MONITORRECTS);
917 UserLeave();
918
919 return hMonitor;
920 }
921
922
923 HMONITOR
924 APIENTRY
925 NtUserMonitorFromWindow(
926 IN HWND hWnd,
927 IN DWORD dwFlags)
928 {
929 PWND pWnd;
930 HMONITOR hMonitor = NULL;
931 RECTL Rect = {0, 0, 0, 0};
932
933 TRACE("Enter NtUserMonitorFromWindow\n");
934
935 /* Check if flags are valid */
936 if (dwFlags != MONITOR_DEFAULTTONULL &&
937 dwFlags != MONITOR_DEFAULTTOPRIMARY &&
938 dwFlags != MONITOR_DEFAULTTONEAREST)
939 {
940 EngSetLastError(ERROR_INVALID_FLAGS);
941 return NULL;
942 }
943
944 UserEnterShared();
945
946 /* If window is given, use it first */
947 if (hWnd)
948 {
949 /* Get window object */
950 pWnd = UserGetWindowObject(hWnd);
951 if (!pWnd)
952 goto cleanup;
953
954 /* Find only monitors which have intersection with given window */
955 Rect.left = Rect.right = pWnd->rcWindow.left;
956 Rect.top = Rect.bottom = pWnd->rcWindow.bottom;
957 }
958
959 /* Find monitors now */
960 IntGetMonitorsFromRect(&Rect, &hMonitor, NULL, 1, dwFlags);
961
962 cleanup:
963 TRACE("Leave NtUserMonitorFromWindow, ret=%p\n", hMonitor);
964 UserLeave();
965 return hMonitor;
966 }