/*
 *  COPYRIGHT:        See COPYING in the top level directory
 *  PROJECT:          ReactOS kernel
 *  PURPOSE:          pMonitor support
 *  FILE:             subsys/win32k/ntuser/monitor.c
 *  PROGRAMERS:       Anich Gregor (blight@blight.eu.org)
 *                    Rafal Harabien (rafalh@reactos.org)
 */

#include <win32k.h>
DBG_DEFAULT_CHANNEL(UserMonitor);

#define NDEBUG
#include <debug.h>

/* GLOBALS *******************************************************************/

/* List of monitors */
static PMONITOR gMonitorList = NULL;

/* PRIVATE FUNCTIONS **********************************************************/

/* IntCreateMonitorObject
 *
 * Creates a MONITOR
 *
 * Return value
 *   If the function succeeds a pointer to a MONITOR is returned. On failure
 *   NULL is returned.
 */
static
PMONITOR
IntCreateMonitorObject()
{
    return UserCreateObject(gHandleTable, NULL, NULL, otMonitor, sizeof(MONITOR));
}

/* IntDestroyMonitorObject
 *
 * Destroys a MONITOR
 * You have to be the owner of the monitors lock to safely destroy it.
 *
 * Arguments
 *
 *   pMonitor
 *      Pointer to the MONITOR which shall be deleted
 */
static
void
IntDestroyMonitorObject(IN PMONITOR pMonitor)
{
    /* Free monitor name */
    RtlFreeUnicodeString(&pMonitor->DeviceName);

    /* Remove monitor region */
    if (pMonitor->hrgnMonitor)
    {
        GreSetObjectOwner(pMonitor->hrgnMonitor, GDI_OBJ_HMGR_POWNED);
        GreDeleteObject(pMonitor->hrgnMonitor);
    }

    /* Destroy monitor object */
    UserDereferenceObject(pMonitor);
    UserDeleteObject(UserHMGetHandle(pMonitor), otMonitor);
}

/* UserGetMonitorObject
 *
 * Returns monitor object from handle or sets last error if handle is invalid
 *
 * Arguments
 *
 *   hMonitor
 *      Handle of MONITOR object
 */
PMONITOR FASTCALL
UserGetMonitorObject(IN HMONITOR hMonitor)
{
    PMONITOR pMonitor;

    if (!hMonitor)
    {
        EngSetLastError(ERROR_INVALID_MONITOR_HANDLE);
        return NULL;
    }

    pMonitor = (PMONITOR)UserGetObject(gHandleTable, hMonitor, otMonitor);
    if (!pMonitor)
    {
        EngSetLastError(ERROR_INVALID_MONITOR_HANDLE);
        return NULL;
    }

    return pMonitor;
}

/* IntGetPrimaryMonitor
 *
 * Returns a PMONITOR for the primary monitor
 *
 * Return value
 *   PMONITOR
 */
PMONITOR
FASTCALL
IntGetPrimaryMonitor()
{
    PMONITOR pMonitor;

    /* Find primary monitor */
    for (pMonitor = gMonitorList; pMonitor != NULL; pMonitor = pMonitor->pMonitorNext)
    {
        if (pMonitor->IsPrimary)
            break;
    }

    return pMonitor;
}

/* IntAttachMonitor
 *
 * Creates a new MONITOR and appends it to the list of monitors.
 *
 * Arguments
 *
 *   pGdiDevice     Pointer to the PDEVOBJ onto which the monitor was attached
 *   DisplayNumber  Display Number (starting with 0)
 *
 * Return value
 *   Returns a NTSTATUS
 */
NTSTATUS
IntAttachMonitor(IN PDEVOBJ *pGdiDevice,
                 IN ULONG DisplayNumber)
{
    PMONITOR pMonitor;
    WCHAR Buffer[CCHDEVICENAME];

    TRACE("Attaching monitor...\n");

    /* Create new monitor object */
    pMonitor = IntCreateMonitorObject();
    if (pMonitor == NULL)
    {
        TRACE("Couldnt create monitor object\n");
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    _snwprintf(Buffer, CCHDEVICENAME, L"\\\\.\\DISPLAY%d", DisplayNumber + 1);
    if (!RtlCreateUnicodeString(&pMonitor->DeviceName, Buffer))
    {
        TRACE("Couldn't duplicate monitor name!\n");
        UserDereferenceObject(pMonitor);
        UserDeleteObject(UserHMGetHandle(pMonitor), otMonitor);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    pMonitor->GdiDevice = pGdiDevice;
    pMonitor->cWndStack = 0;

    if (gMonitorList == NULL)
    {
        TRACE("Primary monitor is beeing attached\n");
        pMonitor->IsPrimary = TRUE;
        gMonitorList = pMonitor;
    }
    else
    {
        PMONITOR pmonLast = gMonitorList;
        TRACE("Additional monitor is beeing attached\n");
        while (pmonLast->pMonitorNext != NULL)
            pmonLast = pmonLast->pMonitorNext;

        pmonLast->pMonitorNext = pMonitor;
    }

    IntUpdateMonitorSize(pGdiDevice);

    return STATUS_SUCCESS;
}

/* IntDetachMonitor
 *
 * Deletes a MONITOR and removes it from the list of monitors.
 *
 * Arguments
 *
 *   pGdiDevice  Pointer to the PDEVOBJ from which the monitor was detached
 *
 * Return value
 *   Returns a NTSTATUS
 */
NTSTATUS
IntDetachMonitor(IN PDEVOBJ *pGdiDevice)
{
    PMONITOR pMonitor = gMonitorList, *pLink = &gMonitorList;

    /* Find monitor attached to given device */
    while (pMonitor != NULL)
    {
        if (pMonitor->GdiDevice == pGdiDevice)
            break;
    
        pLink = &pMonitor->pMonitorNext;
        pMonitor = pMonitor->pMonitorNext;
    }

    if (pMonitor == NULL)
    {
        /* No monitor has been found */
        return STATUS_INVALID_PARAMETER;
    }

    /* We destroy primary monitor - set next as primary */
    if (pMonitor->IsPrimary && pMonitor->pMonitorNext != NULL)
        pMonitor->pMonitorNext->IsPrimary = TRUE;

    /* Update Next ptr in previous monitor */
    *pLink = pMonitor->pMonitorNext;

    /* Finally destroy monitor */
    IntDestroyMonitorObject(pMonitor);

    return STATUS_SUCCESS;
}

/* IntUpdateMonitorSize
 *
 * Reset size of the monitor using atached device
 *
 * Arguments
 *
 *   PMONITOR
 *      pGdiDevice  Pointer to the PDEVOBJ, which size has changed
 *
 * Return value
 *   Returns a NTSTATUS
 */
NTSTATUS
IntUpdateMonitorSize(IN PDEVOBJ *pGdiDevice)
{
	PMONITOR pMonitor;

    /* Find monitor attached to given device */
    for (pMonitor = gMonitorList; pMonitor != NULL; pMonitor = pMonitor->pMonitorNext)
    {
        if (pMonitor->GdiDevice == pGdiDevice)
            break;
    }

    if (pMonitor == NULL)
    {
        /* No monitor has been found */
        return STATUS_INVALID_PARAMETER;
    }

    /* Update monitor size */
    pMonitor->rcMonitor.left  = 0;
    pMonitor->rcMonitor.top   = 0;
    pMonitor->rcMonitor.right  = pMonitor->rcMonitor.left + pMonitor->GdiDevice->gdiinfo.ulHorzRes;
    pMonitor->rcMonitor.bottom = pMonitor->rcMonitor.top + pMonitor->GdiDevice->gdiinfo.ulVertRes;
    pMonitor->rcWork = pMonitor->rcMonitor;

    /* Destroy monitor region... */
    if (pMonitor->hrgnMonitor)
    {
        GreSetObjectOwner(pMonitor->hrgnMonitor, GDI_OBJ_HMGR_POWNED);
        GreDeleteObject(pMonitor->hrgnMonitor);
    }

    /* ...and create new one */
    pMonitor->hrgnMonitor = IntSysCreateRectRgnIndirect(&pMonitor->rcMonitor);
    if (pMonitor->hrgnMonitor)
        IntGdiSetRegionOwner(pMonitor->hrgnMonitor, GDI_OBJ_HMGR_PUBLIC);

    return STATUS_SUCCESS;
}

/* IntGetMonitorsFromRect
 *
 * Returns a list of monitor handles/rectangles. The rectangles in the list are
 * the areas of intersection with the monitors.
 *
 * Arguments
 *
 *   pRect
 *      Rectangle in desktop coordinates. If this is NULL all monitors are
 *      returned and the rect list is filled with the sizes of the monitors.
 *
 *   phMonitorList
 *      Pointer to an array of HMONITOR which is filled with monitor handles.
 *      Can be NULL
 *
 *   prcMonitorList
 *      Pointer to an array of RECT which is filled with intersection rects in
 *      desktop coordinates.
 *      Can be NULL, will be ignored if no intersecting monitor is found and
 *      flags is MONITOR_DEFAULTTONEAREST
 *
 *   dwListSize
 *      Size of the phMonitorList and prcMonitorList arguments. If this is zero
 *      phMonitorList and prcMonitorList are ignored.
 *
 *   dwFlags
 *      Either 0 or MONITOR_DEFAULTTONEAREST (ignored if rect is NULL)
 *
 * Returns
 *   The number of monitors which intersect the specified region.
 */
static
UINT
IntGetMonitorsFromRect(OPTIONAL IN LPCRECTL pRect,
                       OPTIONAL OUT HMONITOR *phMonitorList,
                       OPTIONAL OUT PRECTL prcMonitorList,
                       OPTIONAL IN DWORD dwListSize,
                       OPTIONAL IN DWORD dwFlags)
{
    PMONITOR pMonitor, pNearestMonitor = NULL, pPrimaryMonitor = NULL;
    UINT cMonitors = 0;
    ULONG iNearestDistance = 0xffffffff;

    /* Find monitors which intersects the rectangle */
    for (pMonitor = gMonitorList; pMonitor != NULL; pMonitor = pMonitor->pMonitorNext)
    {
        RECTL MonitorRect, IntersectionRect;

        MonitorRect = pMonitor->rcMonitor;

        TRACE("MonitorRect: left = %d, top = %d, right = %d, bottom = %d\n",
               MonitorRect.left, MonitorRect.top, MonitorRect.right, MonitorRect.bottom);

        /* Save primary monitor for later usage */
        if (dwFlags == MONITOR_DEFAULTTOPRIMARY && pMonitor->IsPrimary)
            pPrimaryMonitor = pMonitor;

        /* Check if a rect is given */
        if (pRect == NULL)
        {
            /* No rect given, so use the full monitor rect */
            IntersectionRect = MonitorRect;
        }
        /* We have a rect, calculate intersection */
        else if (!RECTL_bIntersectRect(&IntersectionRect, &MonitorRect, pRect))
        {
            /* Rects did not intersect */
            if (dwFlags == MONITOR_DEFAULTTONEAREST)
            {
                ULONG cx, cy, iDistance;

                /* Get x and y distance */
                cx = min(abs(MonitorRect.left - pRect->right),
                         abs(pRect->left - MonitorRect.right));
                cy = min(abs(MonitorRect.top - pRect->bottom),
                         abs(pRect->top - MonitorRect.bottom));

                /* Calculate distance square */
                iDistance = cx * cx + cy * cy;

                /* Check if this is the new nearest monitor */
                if (iDistance < iNearestDistance)
                {
                    iNearestDistance = iDistance;
                    pNearestMonitor = pMonitor;
                }
            }

            continue;
        }

        /* Check if there's space in the buffer */
        if (cMonitors < dwListSize)
        {
            /* Save monitor data */
            if (phMonitorList != NULL)
                phMonitorList[cMonitors] = UserHMGetHandle(pMonitor);
            if (prcMonitorList != NULL)
                prcMonitorList[cMonitors] = IntersectionRect;
        }

        /* Increase count of found monitors */
        cMonitors++;
    }

    /* Nothing has been found? */
    if (cMonitors == 0)
    {
        /* Check if we shall default to the nearest monitor */
        if (dwFlags == MONITOR_DEFAULTTONEAREST && pNearestMonitor)
        {
            if (phMonitorList && dwListSize > 0)
                phMonitorList[cMonitors] = UserHMGetHandle(pNearestMonitor);
            cMonitors++;
        }
        /* Check if we shall default to the primary monitor */
        else if (dwFlags == MONITOR_DEFAULTTOPRIMARY && pPrimaryMonitor)
        {
            if (phMonitorList != NULL && dwListSize > 0)
                phMonitorList[cMonitors] = UserHMGetHandle(pPrimaryMonitor);
            cMonitors++;
        }
    }

    return cMonitors;
}

PMONITOR
FASTCALL
IntMonitorFromRect(
    PRECTL pRect,
    DWORD dwFlags)
{
    ULONG cMonitors, LargestArea = 0, i;
    PRECTL prcMonitorList = NULL;
    HMONITOR *phMonitorList = NULL;
    HMONITOR hMonitor = NULL;

    /* Check if flags are valid */
    if (dwFlags != MONITOR_DEFAULTTONULL &&
        dwFlags != MONITOR_DEFAULTTOPRIMARY &&
        dwFlags != MONITOR_DEFAULTTONEAREST)
    {
        EngSetLastError(ERROR_INVALID_FLAGS);
        return NULL;
    }

    /* Find intersecting monitors */
    cMonitors = IntGetMonitorsFromRect(pRect, &hMonitor, NULL, 1, dwFlags);
    if (cMonitors <= 1)
    {
        /* No or one monitor found. Just return handle. */
        goto cleanup;
    }

    /* There is more than one monitor. Find monitor with largest intersection.
       Temporary reset hMonitor */
    hMonitor = NULL;

    /* Allocate helper buffers */
    phMonitorList = ExAllocatePoolWithTag(PagedPool,
                                          sizeof(HMONITOR) * cMonitors,
                                          USERTAG_MONITORRECTS);
    if (phMonitorList == NULL)
    {
        EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
        goto cleanup;
    }

    prcMonitorList = ExAllocatePoolWithTag(PagedPool,
                                           sizeof(RECT) * cMonitors,
                                           USERTAG_MONITORRECTS);
    if (prcMonitorList == NULL)
    {
        EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
        goto cleanup;
    }

    /* Get intersecting monitors again but now with rectangle list */
    cMonitors = IntGetMonitorsFromRect(pRect, phMonitorList, prcMonitorList,
                                       cMonitors, 0);

    /* Find largest intersection */
    for (i = 0; i < cMonitors; i++)
    {
        ULONG Area = (prcMonitorList[i].right - prcMonitorList[i].left) *
                     (prcMonitorList[i].bottom - prcMonitorList[i].top);
        if (Area >= LargestArea)
        {
            hMonitor = phMonitorList[i];
            LargestArea = Area;
        }
    }

cleanup:
    if (phMonitorList)
        ExFreePoolWithTag(phMonitorList, USERTAG_MONITORRECTS);
    if (prcMonitorList)
        ExFreePoolWithTag(prcMonitorList, USERTAG_MONITORRECTS);

    return UserGetMonitorObject(hMonitor);
}


/* PUBLIC FUNCTIONS ***********************************************************/

/* NtUserEnumDisplayMonitors
 *
 * Enumerates display monitors which intersect the given HDC/cliprect
 *
 * Arguments
 *
 *   hdc
 *      Handle to a DC for which to enum intersecting monitors. If this is NULL
 *      it returns all monitors which are part of the current virtual screen.
 *
 *   pUnsafeRect
 *      Clipping rectangle with coordinate system origin at the DCs origin if the
 *      given HDC is not NULL or in virtual screen coordinated if it is NULL.
 *      Can be NULL
 *
 *   phUnsafeMonitorList
 *      Pointer to an array of HMONITOR which is filled with monitor handles.
 *      Can be NULL
 *
 *   prcUnsafeMonitorList
 *      Pointer to an array of RECT which is filled with intersection rectangles.
 *      Can be NULL
 *
 *   dwListSize
 *      Size of the hMonitorList and monitorRectList arguments. If this is zero
 *      hMonitorList and monitorRectList are ignored.
 *
 * Returns
 *   The number of monitors which intersect the specified region or -1 on failure.
 */
INT
APIENTRY
NtUserEnumDisplayMonitors(
    OPTIONAL IN HDC hdc,
    OPTIONAL IN LPCRECTL pUnsafeRect,
    OPTIONAL OUT HMONITOR *phUnsafeMonitorList,
    OPTIONAL OUT PRECTL prcUnsafeMonitorList,
    OPTIONAL IN DWORD dwListSize)
{
    INT cMonitors, iRet = -1, i;
    HMONITOR *phMonitorList = NULL;
    PRECTL prcMonitorList = NULL;
    RECTL rc, *pRect;
    RECTL DcRect = {0};
    NTSTATUS Status;

    /* Get rectangle */
    if (pUnsafeRect != NULL)
    {
        Status = MmCopyFromCaller(&rc, pUnsafeRect, sizeof(RECT));
        if (!NT_SUCCESS(Status))
        {
            TRACE("MmCopyFromCaller() failed!\n");
            SetLastNtError(Status);
            return -1;
        }
    }

    if (hdc != NULL)
    {
        PDC pDc;
        INT iRgnType;

        /* Get visible region bounding rect */
        pDc = DC_LockDc(hdc);
        if (pDc == NULL)
        {
            TRACE("DC_LockDc() failed!\n");
            /* FIXME: setlasterror? */
            return -1;
        }
        iRgnType = REGION_GetRgnBox(pDc->prgnVis, &DcRect);
        DC_UnlockDc(pDc);

        if (iRgnType == 0)
        {
            TRACE("NtGdiGetRgnBox() failed!\n");
            return -1;
        }
        if (iRgnType == NULLREGION)
            return 0;
        if (iRgnType == COMPLEXREGION)
        {
            /* TODO: Warning */
        }

        /* If hdc and pRect are given the area of interest is pRect with
           coordinate origin at the DC position */
        if (pUnsafeRect != NULL)
        {
            rc.left += DcRect.left;
            rc.right += DcRect.left;
            rc.top += DcRect.top;
            rc.bottom += DcRect.top;
        }
        /* If hdc is given and pRect is not the area of interest is the
           bounding rect of hdc */
        else
        {
            rc = DcRect;
        }
    }

    if (hdc == NULL && pUnsafeRect == NULL)
        pRect = NULL;
    else
        pRect = &rc;

    UserEnterShared();

    /* Find intersecting monitors */
    cMonitors = IntGetMonitorsFromRect(pRect, NULL, NULL, 0, MONITOR_DEFAULTTONULL);
    if (cMonitors == 0 || dwListSize == 0 ||
        (phUnsafeMonitorList == NULL && prcUnsafeMonitorList == NULL))
    {
        /* Simple case - just return monitors count */
        TRACE("cMonitors = %d\n", cMonitors);
        iRet = cMonitors;
        goto cleanup;
    }

    /* Allocate safe buffers */
    if (phUnsafeMonitorList != NULL && dwListSize != 0)
    {
        phMonitorList = ExAllocatePoolWithTag(PagedPool, sizeof (HMONITOR) * dwListSize, USERTAG_MONITORRECTS);
        if (phMonitorList == NULL)
        {
            EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
            goto cleanup;
        }
    }
    if (prcUnsafeMonitorList != NULL && dwListSize != 0)
    {
        prcMonitorList = ExAllocatePoolWithTag(PagedPool, sizeof (RECT) * dwListSize, USERTAG_MONITORRECTS);
        if (prcMonitorList == NULL)
        {
            EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
            goto cleanup;
        }
    }

    /* Get intersecting monitors */
    cMonitors = IntGetMonitorsFromRect(pRect, phMonitorList, prcMonitorList,
                                       dwListSize, MONITOR_DEFAULTTONULL);

    if (hdc != NULL && pRect != NULL && prcMonitorList != NULL)
        for (i = 0; i < cMonitors; i++)
        {
            prcMonitorList[i].left -= DcRect.left;
            prcMonitorList[i].right -= DcRect.left;
            prcMonitorList[i].top -= DcRect.top;
            prcMonitorList[i].bottom -= DcRect.top;
        }

    /* Output result */
    if (phUnsafeMonitorList != NULL && dwListSize != 0)
    {
        Status = MmCopyToCaller(phUnsafeMonitorList, phMonitorList, sizeof(HMONITOR) * dwListSize);
        if (!NT_SUCCESS(Status))
        {
            SetLastNtError(Status);
            goto cleanup;
        }
    }
    if (prcUnsafeMonitorList != NULL && dwListSize != 0)
    {
        Status = MmCopyToCaller(prcUnsafeMonitorList, prcMonitorList, sizeof(RECT) * dwListSize);
        if (!NT_SUCCESS(Status))
        {
            SetLastNtError(Status);
            goto cleanup;
        }
    }

    /* Return monitors count on success */
    iRet = cMonitors;

cleanup:
    if (phMonitorList)
        ExFreePoolWithTag(phMonitorList, USERTAG_MONITORRECTS);
    if (prcMonitorList)
        ExFreePoolWithTag(prcMonitorList, USERTAG_MONITORRECTS);

    UserLeave();
    return iRet;
}

/* NtUserGetMonitorInfo
 *
 * Retrieves information about a given monitor
 *
 * Arguments
 *
 *   hMonitor
 *      Handle to a monitor for which to get information
 *
 *   pMonitorInfoUnsafe
 *      Pointer to a MONITORINFO struct which is filled with the information.
 *      The cbSize member must be set to sizeof(MONITORINFO) or
 *      sizeof(MONITORINFOEX). Even if set to sizeof(MONITORINFOEX) only parts
 *      from MONITORINFO will be filled.
 *
 *   pDevice
 *      Pointer to a UNICODE_STRING which will recieve the device's name. The
 *      length should be CCHDEVICENAME
 *      Can be NULL
 *
 * Return value
 *   TRUE on success; FALSE on failure (calls SetLastNtError())
 *
 */
BOOL
APIENTRY
NtUserGetMonitorInfo(
    IN HMONITOR hMonitor,
    OUT LPMONITORINFO pMonitorInfoUnsafe)
{
    PMONITOR pMonitor;
    MONITORINFOEXW MonitorInfo;
    NTSTATUS Status;
    BOOL bRet = FALSE;

    TRACE("Enter NtUserGetMonitorInfo\n");
    UserEnterShared();

    /* Get monitor object */
    pMonitor = UserGetMonitorObject(hMonitor);
    if (!pMonitor)
    {
        TRACE("Couldnt find monitor 0x%lx\n", hMonitor);
        goto cleanup;
    }

    /* Check if pMonitorInfoUnsafe is valid */
    if(pMonitorInfoUnsafe == NULL)
    {
        SetLastNtError(STATUS_INVALID_PARAMETER);
        goto cleanup;
    }

    /* Get size of pMonitorInfoUnsafe */
    Status = MmCopyFromCaller(&MonitorInfo.cbSize, &pMonitorInfoUnsafe->cbSize, sizeof(MonitorInfo.cbSize));
    if (!NT_SUCCESS(Status))
    {
        SetLastNtError(Status);
        goto cleanup;
    }

    /* Check if size of struct is valid */
    if (MonitorInfo.cbSize != sizeof(MONITORINFO) &&
        MonitorInfo.cbSize != sizeof(MONITORINFOEXW))
    {
        SetLastNtError(STATUS_INVALID_PARAMETER);
        goto cleanup;
    }

    /* Fill monitor info */
    MonitorInfo.rcMonitor = pMonitor->rcMonitor;
    MonitorInfo.rcWork = pMonitor->rcWork;
    MonitorInfo.dwFlags = 0;
    if (pMonitor->IsPrimary)
        MonitorInfo.dwFlags |= MONITORINFOF_PRIMARY;

    /* Fill device name */
    if (MonitorInfo.cbSize == sizeof(MONITORINFOEXW))
    {
        RtlStringCbCopyNExW(MonitorInfo.szDevice,
                          sizeof(MonitorInfo.szDevice),
                          pMonitor->DeviceName.Buffer,
                          pMonitor->DeviceName.Length,
                          NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
    }

    /* Output data */
    Status = MmCopyToCaller(pMonitorInfoUnsafe, &MonitorInfo, MonitorInfo.cbSize);
    if (!NT_SUCCESS(Status))
    {
        TRACE("GetMonitorInfo: MmCopyToCaller failed\n");
        SetLastNtError(Status);
        goto cleanup;
    }

    TRACE("GetMonitorInfo: success\n");
    bRet = TRUE;

cleanup:
    TRACE("Leave NtUserGetMonitorInfo, ret=%i\n", bRet);
    UserLeave();
    return bRet;
}

/* NtUserMonitorFromPoint
 *
 * Returns a handle to the monitor containing the given point.
 *
 * Arguments
 *
 *   pt
 *     Point for which to find monitor
 *
 *   dwFlags
 *     Specifies the behaviour if the point isn't on any of the monitors.
 *
 * Return value
 *   If the point is found a handle to the monitor is returned; if not the
 *   return value depends on dwFlags
 */
HMONITOR
APIENTRY
NtUserMonitorFromPoint(
    IN POINT pt,
    IN DWORD dwFlags)
{
    INT cMonitors;
    RECTL rc;
    HMONITOR hMonitor = NULL;

    /* Check if flags are valid */
    if (dwFlags != MONITOR_DEFAULTTONULL &&
        dwFlags != MONITOR_DEFAULTTOPRIMARY &&
        dwFlags != MONITOR_DEFAULTTONEAREST)
    {
        EngSetLastError(ERROR_INVALID_FLAGS);
        return NULL;
    }

    /* Fill rect (bottom-right exclusive) */
    rc.left = pt.x;
    rc.right = pt.x + 1;
    rc.top = pt.y;
    rc.bottom = pt.y + 1;

    UserEnterShared();

    /* Find intersecting monitor */
    cMonitors = IntGetMonitorsFromRect(&rc, &hMonitor, NULL, 1, dwFlags);

    UserLeave();
    return hMonitor;
}

/* NtUserMonitorFromRect
 *
 * Returns a handle to the monitor having the largest intersection with a
 * given rectangle
 *
 * Arguments
 *
 *   pRectUnsafe
 *     Pointer to a RECT for which to find monitor
 *
 *   dwFlags
 *     Specifies the behaviour if no monitor intersects the given rect
 *
 * Return value
 *   If a monitor intersects the rect a handle to it is returned; if not the
 *   return value depends on dwFlags
 */
HMONITOR
APIENTRY
NtUserMonitorFromRect(
    IN LPCRECTL pRectUnsafe,
    IN DWORD dwFlags)
{
    ULONG cMonitors, LargestArea = 0, i;
    PRECTL prcMonitorList = NULL;
    HMONITOR *phMonitorList = NULL;
    HMONITOR hMonitor = NULL;
    RECTL Rect;
    NTSTATUS Status;

    /* Check if flags are valid */
    if (dwFlags != MONITOR_DEFAULTTONULL &&
        dwFlags != MONITOR_DEFAULTTOPRIMARY &&
        dwFlags != MONITOR_DEFAULTTONEAREST)
    {
        EngSetLastError(ERROR_INVALID_FLAGS);
        return NULL;
    }

    /* Copy rectangle to safe buffer */
    Status = MmCopyFromCaller(&Rect, pRectUnsafe, sizeof (RECT));
    if (!NT_SUCCESS(Status))
    {
        SetLastNtError(Status);
        return NULL;
    }

    UserEnterShared();

    /* Find intersecting monitors */
    cMonitors = IntGetMonitorsFromRect(&Rect, &hMonitor, NULL, 1, dwFlags);
    if (cMonitors <= 1)
    {
        /* No or one monitor found. Just return handle. */
        goto cleanup;
    }

    /* There is more than one monitor. Find monitor with largest intersection.
       Temporary reset hMonitor */
    hMonitor = NULL;

    /* Allocate helper buffers */
    phMonitorList = ExAllocatePoolWithTag(PagedPool,
                                          sizeof(HMONITOR) * cMonitors,
                                          USERTAG_MONITORRECTS);
    if (phMonitorList == NULL)
    {
        EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
        goto cleanup;
    }

    prcMonitorList = ExAllocatePoolWithTag(PagedPool,
                                           sizeof(RECT) * cMonitors,
                                           USERTAG_MONITORRECTS);
    if (prcMonitorList == NULL)
    {
        EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
        goto cleanup;
    }

    /* Get intersecting monitors again but now with rectangle list */
    cMonitors = IntGetMonitorsFromRect(&Rect, phMonitorList, prcMonitorList,
                                       cMonitors, 0);

    /* Find largest intersection */
    for (i = 0; i < cMonitors; i++)
    {
        ULONG Area = (prcMonitorList[i].right - prcMonitorList[i].left) *
                     (prcMonitorList[i].bottom - prcMonitorList[i].top);
        if (Area >= LargestArea)
        {
            hMonitor = phMonitorList[i];
            LargestArea = Area;
        }
    }

cleanup:
    if (phMonitorList)
        ExFreePoolWithTag(phMonitorList, USERTAG_MONITORRECTS);
    if (prcMonitorList)
        ExFreePoolWithTag(prcMonitorList, USERTAG_MONITORRECTS);
    UserLeave();

    return hMonitor;
}


HMONITOR
APIENTRY
NtUserMonitorFromWindow(
    IN HWND hWnd,
    IN DWORD dwFlags)
{
    PWND pWnd;
    HMONITOR hMonitor = NULL;
    RECTL Rect = {0, 0, 0, 0};

    TRACE("Enter NtUserMonitorFromWindow\n");

    /* Check if flags are valid */
    if (dwFlags != MONITOR_DEFAULTTONULL &&
        dwFlags != MONITOR_DEFAULTTOPRIMARY &&
        dwFlags != MONITOR_DEFAULTTONEAREST)
    {
        EngSetLastError(ERROR_INVALID_FLAGS);
        return NULL;
    }

    UserEnterShared();

    /* If window is given, use it first */
    if (hWnd)
    {
        /* Get window object */
        pWnd = UserGetWindowObject(hWnd);
        if (!pWnd)
            goto cleanup;

        /* Find only monitors which have intersection with given window */
        Rect.left = Rect.right = pWnd->rcWindow.left;
        Rect.top = Rect.bottom = pWnd->rcWindow.bottom;
    }

    /* Find monitors now */
    IntGetMonitorsFromRect(&Rect, &hMonitor, NULL, 1, dwFlags);

cleanup:
    TRACE("Leave NtUserMonitorFromWindow, ret=%p\n", hMonitor);
    UserLeave();
    return hMonitor;
}
