/*++
    Copyright (c) 2000 Microsoft Corporation

    Module Name:
        display.c

    Abstract: Tablet PC Display Property Sheet module.

    Environment:
        User mode

    Author:
        Michael Tsang (MikeTs) 14-Jun-2000

    Revision History:
--*/

#include "pch.h"

#define BRIGHTSCALE_MIN         0
#define BRIGHTSCALE_MAX         15

#define BRIGHTSCALE_LO          1
#define BRIGHTSCALE_HI          15

HANDLE  ghBackLight = INVALID_HANDLE_VALUE;
HMODULE ghmodSMAPI = NULL;
FARPROC gpfnCallSMAPI = NULL;
ERRMAP SMAPIErrMap[] =
{
    RT_ERR_INSTRETCHMODE,       IDSERR_SMAPI_INSTRETCHMODE,
    RT_ERR_INDUALAPPMODE,       IDSERR_SMAPI_INDUALAPPMODE,
    RT_ERR_INDUALVIEWMODE,      IDSERR_SMAPI_INDUALVIEWMODE,
    RT_ERR_INRGBPROJMODE,       IDSERR_SMAPI_INRGBPROJMODE,
    RT_ERR_INVIRTUALSCREEN,     IDSERR_SMAPI_INVIRTUALSCREEN,
    RT_ERR_INVIRTUALREFRESH,    IDSERR_SMAPI_INVIRTUALREFRESH,
    RT_ERR_INROTATEMODE,        IDSERR_SMAPI_INROTATEMODE,
    RT_ERR_2NDMONITORON,        IDSERR_SMAPI_2NDMONITORON,
    RT_ERR_WRONGHW,             IDSERR_SMAPI_WRONGHW,
    RT_ERR_CLRDEPTH,            IDSERR_SMAPI_CLRDEPTH,
    RT_ERR_MEMORY,              IDSERR_SMAPI_MEMORY,
    RT_ERR_SETMODE,             IDSERR_SMAPI_SETMODE,
    RT_ERR_DIRECTION,           IDSERR_SMAPI_DIRECTION,
    RT_ERR_CAPTUREON,           IDSERR_SMAPI_CAPTUREON,
    RT_ERR_VIDEOON,             IDSERR_SMAPI_VIDEOON,
    RT_ERR_DDRAWON,             IDSERR_SMAPI_DDRAWON,
    RT_ERR_ALREADYSET,          IDSERR_SMAPI_ALREADYSET,
    0,                          0
};

SMBLITE_BRIGHTNESS gBrightness = {0};
BOOL  gfPortraitMode = FALSE;
DWORD gDisplayHelpIDs[] =
{
    IDC_ROTATE_GROUPBOX,        IDH_ROTATE,
    0,                          0
};

/*++
    @doc    EXTERNAL

    @func   INT_PTR | DisplayDlgProc |
            Dialog procedure for the display page.

    @parm   IN HWND | hwnd | Window handle.
    @parm   IN UINT | uMsg | Message.
    @parm   IN WPARAM | wParam | Word Parameter.
    @parm   IN LPARAM | lParam | Long Parameter.

    @rvalue Return value depends on the message.
--*/

INT_PTR APIENTRY
DisplayDlgProc(
    IN HWND   hwnd,
    IN UINT   uMsg,
    IN WPARAM wParam,
    IN LPARAM lParam
    )
{
    TRACEPROC("DisplayDlgProc", 2)
    INT_PTR rc = FALSE;
    static BOOL fPortrait = FALSE;
    static SMBLITE_BRIGHTNESS Brightness = {0};

    TRACEENTER(("(hwnd=%p,Msg=%s,wParam=%x,lParam=%x)\n",
                hwnd, LookupName(uMsg, WMMsgNames) , wParam, lParam));

    switch (uMsg)
    {
        case WM_INITDIALOG:
            rc = InitDisplayPage(hwnd);
            if (rc)
            {
                fPortrait = gfPortraitMode;
                Brightness = gBrightness;
            }
            else
            {
                EnableWindow(hwnd, FALSE);
            }
            break;

        case WM_DESTROY:
            if (ghmodSMAPI != NULL)
            {
                FreeLibrary(ghmodSMAPI);
                ghmodSMAPI = NULL;
                gpfnCallSMAPI = NULL;
            }

            if (ghBackLight != INVALID_HANDLE_VALUE)
            {
                if ((Brightness.bACValue != gBrightness.bACValue) ||
                    (Brightness.bDCValue != gBrightness.bDCValue))
                {
                    //
                    // User must have canceled the setting, so restore
                    // brightness to the original values.
                    //
                    SetBrightness(&gBrightness, FALSE);
                }

                CloseHandle(ghBackLight);
                ghBackLight = INVALID_HANDLE_VALUE;
            }
            break;

        case WM_DISPLAYCHANGE:
            gfPortraitMode = (LOWORD(lParam) < HIWORD(lParam));
            CheckRadioButton(hwnd,
                             IDC_ROTATE_LANDSCAPE,
                             IDC_ROTATE_PORTRAIT,
                             gfPortraitMode? IDC_ROTATE_PORTRAIT:
                                             IDC_ROTATE_LANDSCAPE);
            break;

        case WM_NOTIFY:
        {
            NMHDR FAR *lpnm = (NMHDR FAR *)lParam;

            switch (lpnm->code)
            {
                case PSN_APPLY:
                {
                    if (fPortrait ^ gfPortraitMode)
                    {
                        DWORD smrc;

                        smrc = RotateScreen(fPortrait? RT_CLOCKWISE: 0);
                        if (smrc == SMAPI_OK)
                        {
                            gfPortraitMode = fPortrait;
                        }
                        else
                        {
                            DWORD dwErr = MapError(smrc, SMAPIErrMap, FALSE);

                            CheckRadioButton(hwnd,
                                             IDC_ROTATE_LANDSCAPE,
                                             IDC_ROTATE_PORTRAIT,
                                             gfPortraitMode?
                                                 IDC_ROTATE_PORTRAIT:
                                                 IDC_ROTATE_LANDSCAPE);
                            if (dwErr == 0)
                            {
                                ErrorMsg(IDSERR_SMAPI_CALLFAILED, smrc);
                            }
                            else
                            {
                                ErrorMsg(dwErr);
                            }
                        }
                    }

                    if ((Brightness.bACValue != gBrightness.bACValue) ||
                        (Brightness.bDCValue != gBrightness.bDCValue))
                    {
                        //
                        // User has committed to the new values, save them.
                        //
                        if (SetBrightness(&Brightness, TRUE))
                        {
                            gBrightness = Brightness;
                        }
                    }

                    break;
                }
            }
            break;
        }

        case WM_COMMAND:
        {
            switch (LOWORD(wParam))
            {
                case IDC_ROTATE_LANDSCAPE:
                    fPortrait = FALSE;
                    goto CheckRotation;

                case IDC_ROTATE_PORTRAIT:
                    fPortrait = TRUE;

                CheckRotation:
                    if ((HIWORD(wParam) == BN_CLICKED) &&
                        !IsDlgButtonChecked(hwnd, LOWORD(wParam)))
                    {
                        //
                        // Rotation has changed, mark the property
                        // sheet dirty.
                        //
                        CheckRadioButton(hwnd,
                                         IDC_ROTATE_LANDSCAPE,
                                         IDC_ROTATE_PORTRAIT,
                                         LOWORD(wParam));
                        SendMessage(GetParent(hwnd),
                                    PSM_CHANGED,
                                    (WPARAM)hwnd,
                                    0);
                        rc = TRUE;
                    }
                    break;
            }
            break;
        }

#ifdef BACKLIGHT
        case WM_HSCROLL:
        {
            UCHAR BrightScale;

            BrightScale = (UCHAR)SendDlgItemMessage(hwnd,
                                                    IDC_BRIGHTNESS_AC,
                                                    TBM_GETPOS,
                                                    0,
                                                    0);
            Brightness.bACValue = BrightScale*(BRIGHTNESS_MAX + 1)/
                                  (BRIGHTSCALE_MAX + 1);
            BrightScale = (UCHAR)SendDlgItemMessage(hwnd,
                                                    IDC_BRIGHTNESS_DC,
                                                    TBM_GETPOS,
                                                    0,
                                                    0);
            Brightness.bDCValue = BrightScale*(BRIGHTNESS_MAX + 1)/
                                  (BRIGHTSCALE_MAX + 1);

            if ((Brightness.bACValue != gBrightness.bACValue) ||
                (Brightness.bDCValue != gBrightness.bDCValue))
            {
                if (SetBrightness(&Brightness, FALSE))
                {
                    SendMessage(GetParent(hwnd), PSM_CHANGED, (WPARAM)hwnd, 0);
                }
            }
            break;
        }
#endif

        case WM_HELP:
            WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle,
                    TEXT("tabletpc.hlp"),
                    HELP_WM_HELP,
                    (DWORD_PTR)gDisplayHelpIDs);
            break;

        case WM_CONTEXTMENU:
            WinHelp((HWND)wParam,
                    TEXT("tabletpc.hlp"),
                    HELP_CONTEXTMENU,
                    (DWORD_PTR)gDisplayHelpIDs);
            break;
    }

    TRACEEXIT(("=%x\n", rc));
    return rc;
}       //DisplayDlgProc

/*++
    @doc    INTERNAL

    @func   BOOL | InitDisplayPage |
            Initialize the display property page.

    @parm   IN HWND | hwnd | Window handle.

    @rvalue SUCCESS | Returns TRUE.
    @rvalue FAILURE | Returns FALSE.
--*/

BOOL
InitDisplayPage(
    IN HWND hwnd
    )
{
    TRACEPROC("InitDisplayPage", 2)
    BOOL rc = FALSE;

    TRACEENTER(("(hwnd=%x)\n", hwnd));

#ifdef BACKLIGHT
    SendDlgItemMessage(hwnd,
                       IDC_BRIGHTNESS_AC,
                       TBM_SETRANGE,
                       TRUE,
                       MAKELONG(BRIGHTSCALE_LO, BRIGHTSCALE_HI));
    SendDlgItemMessage(hwnd,
                       IDC_BRIGHTNESS_DC,
                       TBM_SETRANGE,
                       TRUE,
                       MAKELONG(BRIGHTSCALE_LO, BRIGHTSCALE_HI));
#endif
    ghmodSMAPI = LoadLibrary(TEXT("smhook.dll"));
    if (ghmodSMAPI != NULL)
    {
        gpfnCallSMAPI = GetProcAddress(ghmodSMAPI, "CallSMAPI");

        if (gpfnCallSMAPI != NULL)
        {
            LONG cxScreen, cyScreen;

            cxScreen = GetSystemMetrics(SM_CXSCREEN);
            cyScreen = GetSystemMetrics(SM_CYSCREEN);
            gfPortraitMode = (cxScreen < cyScreen);
            CheckRadioButton(hwnd,
                             IDC_ROTATE_LANDSCAPE,
                             IDC_ROTATE_PORTRAIT,
                             gfPortraitMode? IDC_ROTATE_PORTRAIT:
                                             IDC_ROTATE_LANDSCAPE);

#ifdef BACKLIGHT
            ghBackLight = CreateFile(SMBLITE_IOCTL_DEVNAME,
                                     GENERIC_READ | GENERIC_WRITE,
                                     FILE_SHARE_READ | FILE_SHARE_WRITE,
                                     NULL,
                                     OPEN_EXISTING,
                                     FILE_ATTRIBUTE_NORMAL,
                                     NULL);

            if (ghBackLight == INVALID_HANDLE_VALUE)
            {
                ErrorMsg(IDSERR_SMBLITE_OPENDEV, GetLastError());
            }
            else if (GetBrightness(&gBrightness))
            {
                LPARAM lParam;

                lParam = gBrightness.bACValue*(BRIGHTSCALE_MAX + 1)/
                         (BRIGHTNESS_MAX + 1);
                SendDlgItemMessage(hwnd,
                                   IDC_BRIGHTNESS_AC,
                                   TBM_SETPOS,
                                   TRUE,
                                   lParam);
                lParam = gBrightness.bDCValue*(BRIGHTSCALE_MAX + 1)/
                         (BRIGHTNESS_MAX + 1);
                SendDlgItemMessage(hwnd,
                                   IDC_BRIGHTNESS_DC,
                                   TBM_SETPOS,
                                   TRUE,
                                   lParam);
            }
#endif

            rc = TRUE;
        }
        else
        {
            FreeLibrary(ghmodSMAPI);
            ghmodSMAPI = NULL;
            ErrorMsg(IDSERR_GETPROCADDR_SMAPI);
        }
    }
    else
    {
        ErrorMsg(IDSERR_LOADLIBRARY_SMAPI);
    }

    TRACEEXIT(("=%x\n", rc));
    return rc;
}       //InitDisplayPage

/*++
    @doc    EXTERNAL

    @func   DWORD | SetRotation | Set display rotation mode.

    @parm   IN DWORD | dwRotation | Specified the rotation.

    @rvalue SUCCESS | Returns TRUE.
    @rvalue FAILURE | Returns FALSE.
--*/

BOOL
__stdcall
SetRotation(
    IN DWORD dwRotation
    )
{
    TRACEPROC("SetRotation", 2)
    BOOL rc = FALSE;
    BOOL fNeedUnload = FALSE;

    TRACEENTER(("(Rotation=%x)\n", dwRotation));

    if (gpfnCallSMAPI == NULL)
    {
        ghmodSMAPI = LoadLibrary(TEXT("smhook.dll"));
        if (ghmodSMAPI != NULL)
        {
            gpfnCallSMAPI = GetProcAddress(ghmodSMAPI, "CallSMAPI");

            if (gpfnCallSMAPI != NULL)
            {
                fNeedUnload = TRUE;
            }
            else
            {
                FreeLibrary(ghmodSMAPI);
                ghmodSMAPI = NULL;
                ErrorMsg(IDSERR_GETPROCADDR_SMAPI);
            }
        }
        else
        {
            ErrorMsg(IDSERR_LOADLIBRARY_SMAPI);
        }
    }

    if (gpfnCallSMAPI != NULL)
    {
        DWORD smrc, dwErr;

        smrc = RotateScreen(dwRotation);
        if (smrc == SMAPI_OK)
        {
            rc = TRUE;
        }
        else
        {
            dwErr = MapError(smrc, SMAPIErrMap, FALSE);
            if (dwErr == 0)
            {
                ErrorMsg(IDSERR_SMAPI_CALLFAILED, smrc);
            }
            else
            {
                ErrorMsg(dwErr);
            }
        }
    }

    if (fNeedUnload)
    {
        gpfnCallSMAPI = NULL;
        FreeLibrary(ghmodSMAPI);
        ghmodSMAPI = NULL;
    }

    TRACEEXIT(("=%x\n", rc));
    return rc;
}       //SetRotation

/*++
    @doc    INTERNAL

    @func   DWORD | RotateScreen |
            Rotate the screen to the given orientation.

    @parm   IN DWORD | dwRotation | Specified the rotation.

    @rvalue SUCCESS | Returns SMAPI_OK.
    @rvalue FAILURE | Returns SMAPI error code.
--*/

DWORD
RotateScreen(
    IN DWORD dwRotation
    )
{
    TRACEPROC("RotateScreen", 2)
    DWORD rc;

    TRACEENTER(("(Rotation=%d)\n", dwRotation));

    if (gpfnCallSMAPI != NULL)
    {
        if (dwRotation == 0)
        {
            rc = gpfnCallSMAPI(ROTATE_EXIT, 0, 0, 0);
        }
        else
        {
            if (gpfnCallSMAPI(ROTATE_STATUS, 0, 0, 0))
            {
                gpfnCallSMAPI(ROTATE_EXIT, 0, 0, 0);
            }

            rc = gpfnCallSMAPI(ROTATE_INIT, dwRotation, 0, 0);
            if (rc == SMAPI_OK)
            {
                //
                // The SMI driver has this dynamic mode table feature that
                // it may not report portrait display modes unless we
                // enumerate them.  Therefore, we do display mode enumeration
                // just to force the SMI driver to load a display mode tablet
                // that support portrait modes.
                //
                EnumDisplayModes();
                rc = gpfnCallSMAPI(ROTATE_EXECUTE, NULL, dwRotation, 0);
            }
        }
    }
    else
    {
        rc = SMAPI_ERR_NODRV;
    }

    TRACEEXIT(("=%x\n", rc));
    return rc;
}       //RotateScreen

/*++
    @doc    INTERNAL

    @func   VOID | EnumDisplayModes | Enumerate display modes to force
            SMI driver to dynamically load a mode table that supports
            Portrait modes.

    @parm   None.

    @rvalue None.
--*/

VOID
EnumDisplayModes(
    VOID
    )
{
    TRACEPROC("EnumDisplayModes", 3)
    DWORD i;
    DEVMODE DevMode;

    TRACEENTER(("()\n"));

    for (i = 0; EnumDisplaySettings(NULL, i, &DevMode); ++i)
    {
        //
        // Don't have to do anything.
        //
    }

    TRACEEXIT(("!\n"));
    return;
}       //EnumDisplayModes

/*++
    @doc    INTERNAL

    @func   BOOL | GetBrightness | Call the backlight driver to get
            LCD brightness.

    @parm   OUT PSMBLITE_BRIGHTNESS | Brightness | Brightness values.

    @rvalue SUCCESS | Returnes TRUE.
    @rvalue FAILURE | Returnes FALSE.
--*/

BOOL
GetBrightness(
    OUT PSMBLITE_BRIGHTNESS Brightness
    )
{
    TRACEPROC("GetBrightness", 3)
    BOOL rc = FALSE;

    TRACEENTER(("(Brightness=%p)\n", Brightness));

    if (ghBackLight != INVALID_HANDLE_VALUE)
    {
        DWORD dwcbReturned;

        rc = DeviceIoControl(ghBackLight,
                             IOCTL_SMBLITE_GETBRIGHTNESS,
                             NULL,
                             0,
                             Brightness,
                             sizeof(*Brightness),
                             &dwcbReturned,
                             NULL);
        if (rc == FALSE)
        {
            ErrorMsg(IDSERR_SMBLITE_DEVIOCTL, GetLastError());
        }
    }
    else
    {
        ErrorMsg(IDSERR_SMBLITE_NODEVICE);
    }

    TRACEEXIT(("=%x (AC=%d,DC=%d)\n",
               rc, Brightness->bACValue, Brightness->bDCValue));
    return rc;
}       //GetBrightness

/*++
    @doc    INTERNAL

    @func   BOOL | SetBrightness | Call the backlight driver to set
            LCD brightness.

    @parm   IN PSMBLITE_BRIGHTNESS | Brightness | Brightness values.'
    @parm   IN BOOLEAN | fSaveSettings | If TRUE, save new brightness in the
            registry.

    @rvalue SUCCESS | Returnes TRUE.
    @rvalue FAILURE | Returnes FALSE.
--*/

BOOL
SetBrightness(
    IN PSMBLITE_BRIGHTNESS Brightness,
    IN BOOLEAN             fSaveSettings
    )
{
    TRACEPROC("SetBrightness", 3)
    BOOL rc = FALSE;

    TRACEENTER(("(Brightness=%p,ACValue=%d,DCValue=%d)\n",
                Brightness, Brightness->bACValue, Brightness->bDCValue));

    if (ghBackLight != INVALID_HANDLE_VALUE)
    {
        SMBLITE_SETBRIGHTNESS SetLCD;
        DWORD dwcbReturned;

        SetLCD.Brightness = *Brightness;
        SetLCD.fSaveSettings = fSaveSettings;
        rc = DeviceIoControl(ghBackLight,
                             IOCTL_SMBLITE_SETBRIGHTNESS,
                             &SetLCD,
                             sizeof(SetLCD),
                             NULL,
                             0,
                             &dwcbReturned,
                             NULL);
        if (rc == FALSE)
        {
            ErrorMsg(IDSERR_SMBLITE_DEVIOCTL, GetLastError());
        }
    }
    else
    {
        ErrorMsg(IDSERR_SMBLITE_NODEVICE);
    }

    TRACEEXIT(("=%x\n", rc));
    return rc;
}       //SetBrightness