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