[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 recieve 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 0x%lx\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 INT cMonitors;
790 RECTL rc;
791 HMONITOR hMonitor = NULL;
792
793 /* Check if flags are valid */
794 if (dwFlags != MONITOR_DEFAULTTONULL &&
795 dwFlags != MONITOR_DEFAULTTOPRIMARY &&
796 dwFlags != MONITOR_DEFAULTTONEAREST)
797 {
798 EngSetLastError(ERROR_INVALID_FLAGS);
799 return NULL;
800 }
801
802 /* Fill rect (bottom-right exclusive) */
803 rc.left = pt.x;
804 rc.right = pt.x + 1;
805 rc.top = pt.y;
806 rc.bottom = pt.y + 1;
807
808 UserEnterShared();
809
810 /* Find intersecting monitor */
811 cMonitors = IntGetMonitorsFromRect(&rc, &hMonitor, NULL, 1, dwFlags);
812
813 UserLeave();
814 return hMonitor;
815 }
816
817 /* NtUserMonitorFromRect
818 *
819 * Returns a handle to the monitor having the largest intersection with a
820 * given rectangle
821 *
822 * Arguments
823 *
824 * pRectUnsafe
825 * Pointer to a RECT for which to find monitor
826 *
827 * dwFlags
828 * Specifies the behaviour if no monitor intersects the given rect
829 *
830 * Return value
831 * If a monitor intersects the rect a handle to it is returned; if not the
832 * return value depends on dwFlags
833 */
834 HMONITOR
835 APIENTRY
836 NtUserMonitorFromRect(
837 IN LPCRECTL pRectUnsafe,
838 IN DWORD dwFlags)
839 {
840 ULONG cMonitors, LargestArea = 0, i;
841 PRECTL prcMonitorList = NULL;
842 HMONITOR *phMonitorList = NULL;
843 HMONITOR hMonitor = NULL;
844 RECTL Rect;
845 NTSTATUS Status;
846
847 /* Check if flags are valid */
848 if (dwFlags != MONITOR_DEFAULTTONULL &&
849 dwFlags != MONITOR_DEFAULTTOPRIMARY &&
850 dwFlags != MONITOR_DEFAULTTONEAREST)
851 {
852 EngSetLastError(ERROR_INVALID_FLAGS);
853 return NULL;
854 }
855
856 /* Copy rectangle to safe buffer */
857 Status = MmCopyFromCaller(&Rect, pRectUnsafe, sizeof (RECT));
858 if (!NT_SUCCESS(Status))
859 {
860 SetLastNtError(Status);
861 return NULL;
862 }
863
864 UserEnterShared();
865
866 /* Find intersecting monitors */
867 cMonitors = IntGetMonitorsFromRect(&Rect, &hMonitor, NULL, 1, dwFlags);
868 if (cMonitors <= 1)
869 {
870 /* No or one monitor found. Just return handle. */
871 goto cleanup;
872 }
873
874 /* There is more than one monitor. Find monitor with largest intersection.
875 Temporary reset hMonitor */
876 hMonitor = NULL;
877
878 /* Allocate helper buffers */
879 phMonitorList = ExAllocatePoolWithTag(PagedPool,
880 sizeof(HMONITOR) * cMonitors,
881 USERTAG_MONITORRECTS);
882 if (phMonitorList == NULL)
883 {
884 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
885 goto cleanup;
886 }
887
888 prcMonitorList = ExAllocatePoolWithTag(PagedPool,
889 sizeof(RECT) * cMonitors,
890 USERTAG_MONITORRECTS);
891 if (prcMonitorList == NULL)
892 {
893 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
894 goto cleanup;
895 }
896
897 /* Get intersecting monitors again but now with rectangle list */
898 cMonitors = IntGetMonitorsFromRect(&Rect, phMonitorList, prcMonitorList,
899 cMonitors, 0);
900
901 /* Find largest intersection */
902 for (i = 0; i < cMonitors; i++)
903 {
904 ULONG Area = (prcMonitorList[i].right - prcMonitorList[i].left) *
905 (prcMonitorList[i].bottom - prcMonitorList[i].top);
906 if (Area >= LargestArea)
907 {
908 hMonitor = phMonitorList[i];
909 LargestArea = Area;
910 }
911 }
912
913 cleanup:
914 if (phMonitorList)
915 ExFreePoolWithTag(phMonitorList, USERTAG_MONITORRECTS);
916 if (prcMonitorList)
917 ExFreePoolWithTag(prcMonitorList, USERTAG_MONITORRECTS);
918 UserLeave();
919
920 return hMonitor;
921 }
922
923
924 HMONITOR
925 APIENTRY
926 NtUserMonitorFromWindow(
927 IN HWND hWnd,
928 IN DWORD dwFlags)
929 {
930 PWND pWnd;
931 HMONITOR hMonitor = NULL;
932 RECTL Rect = {0, 0, 0, 0};
933
934 TRACE("Enter NtUserMonitorFromWindow\n");
935
936 /* Check if flags are valid */
937 if (dwFlags != MONITOR_DEFAULTTONULL &&
938 dwFlags != MONITOR_DEFAULTTOPRIMARY &&
939 dwFlags != MONITOR_DEFAULTTONEAREST)
940 {
941 EngSetLastError(ERROR_INVALID_FLAGS);
942 return NULL;
943 }
944
945 UserEnterShared();
946
947 /* If window is given, use it first */
948 if (hWnd)
949 {
950 /* Get window object */
951 pWnd = UserGetWindowObject(hWnd);
952 if (!pWnd)
953 goto cleanup;
954
955 /* Find only monitors which have intersection with given window */
956 Rect.left = Rect.right = pWnd->rcWindow.left;
957 Rect.top = Rect.bottom = pWnd->rcWindow.bottom;
958 }
959
960 /* Find monitors now */
961 IntGetMonitorsFromRect(&Rect, &hMonitor, NULL, 1, dwFlags);
962
963 cleanup:
964 TRACE("Leave NtUserMonitorFromWindow, ret=%p\n", hMonitor);
965 UserLeave();
966 return hMonitor;
967 }