/*++
    Copyright (c) 2000  Microsoft Corporation.  All rights reserved.

    Module Name:
        tsrpc.cpp

    Abstract:
        This module contains the TabSrv RPC services.

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

    Environment:
        User mode

    Revision History:
--*/

#include "pch.h"

/*++
    @doc    INTERNAL

    @func   unsigned | RPCServerThread | RPC Server thread.

    @parm   IN PVOID | param | Not used.

    @rvalue Always returns 0.
--*/

unsigned __stdcall
RPCServerThread(
    IN PVOID param
    )
{
    TRACEPROC("RPCServerThread", 2)
    RPC_STATUS status;
    RPC_BINDING_VECTOR *BindingVector = NULL;

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

    if ((status = RpcServerUseProtseq((unsigned char *)"ncalrpc",
                                      RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
                                      NULL)) != RPC_S_OK)
    {
        TABSRVERR(("RpcServerUseProtSeqEp failed (status=%d)\n", status));
    }
    else if ((status = RpcServerRegisterIf(TabSrv_v1_0_s_ifspec,
                                           NULL,
                                           NULL)) != RPC_S_OK)
    {
        TABSRVERR(("RpcServerRegisterIf failed (status=%d)\n", status));
    }
    else if ((status = RpcServerInqBindings(&BindingVector)) != RPC_S_OK)
    {
        TABSRVERR(("RpcServerInqBindings failed (status=%d)\n", status));
    }
    else if ((status = RpcEpRegister(TabSrv_v1_0_s_ifspec,
                                     BindingVector,
                                     NULL,
                                     NULL)) != RPC_S_OK)
    {
        RpcBindingVectorFree(&BindingVector);
        TABSRVERR(("RpcEpRegister failed (status=%d)\n", status));
    }
    else if ((status = RpcServerRegisterAuthInfo(NULL,
                                                 RPC_C_AUTHN_WINNT,
                                                 NULL,
                                                 NULL)) != RPC_S_OK)
    {
        RpcEpUnregister(TabSrv_v1_0_s_ifspec, BindingVector, NULL);
        RpcBindingVectorFree(&BindingVector);
        TABSRVERR(("RpcServerRegisterAuthInfo failed (status=%d)\n", status));
    }
    else
    {
        status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, FALSE);
        TRACEASSERT(status == RPC_S_OK);
        RpcEpUnregister(TabSrv_v1_0_s_ifspec, BindingVector, NULL);
        RpcBindingVectorFree(&BindingVector);
    }
    TRACEINFO(1, ("RPC thread exiting...\n"));

    TRACEEXIT(("=0\n"));
    return 0;
}       //RPCServerThread

/*++
    @doc    EXTERNAL

    @func   HEVTNOTIFY | TabSrvRegisterEventNotify |
            Register event notification.

    @parm   IN handle_t | hBinding | RPC binding handle.
    @parm   IN EVTNOTIFY | Notify event.
    @parm   IN HWIN | hwnd | Handle to window to be notified.
    @parm   IN UINT | uiMsg | Notification message used.

    @rvalue SUCCESS | Returns notification handle.
    @rvalue FAILURE | Returns NULL.
--*/

HEVTNOTIFY
TabSrvRegisterEventNotify(
    IN handle_t  hBinding,
    IN EVTNOTIFY Event,
    IN HWIN      hwnd,
    IN UINT      uiMsg
    )
{
    TRACEPROC("TabSrvRegisterEventNotify", 2)
    PNOTIFYCLIENT Client;

    TRACEENTER(("(hBinding=%x,Event=%x,hwnd=%x,Msg=%d)\n",
                hBinding, Event, hwnd, uiMsg));

    Client = (PNOTIFYCLIENT)malloc(sizeof(NOTIFYCLIENT));
    if (Client != NULL)
    {
        DWORD rcWait;

        Client->dwSig = SIG_NOTIFYCLIENT;
        Client->Event = Event;
        Client->hwnd = (HWND)hwnd;
        Client->uiMsg = uiMsg;
        rcWait = WaitForSingleObject(ghmutNotifyList, INFINITE);
        if (rcWait == WAIT_OBJECT_0)
        {
            InsertTailList(&glistNotifyClients, &Client->list);
            ReleaseMutex(ghmutNotifyList);
        }
        else
        {
            TABSRVERR(("failed to wait for client list Mutex (rcWait=%x,err=%d).\n",
                       rcWait, GetLastError()));
        }
    }
    else
    {
        TABSRVERR(("Failed to allocate notification client.\n"));
    }

    TRACEEXIT(("=%p\n", Client));
    return (HEVTNOTIFY)Client;
}       //TabSrvRegisterEventNotify

/*++
    @doc    EXTERNAL

    @func   VOID | TabSrvDeregisterEventNotify | Deregister event notification.

    @parm   IN handle_t | hBinding | RPC binding handle.
    @parm   IN HEVTNOTIFY | Notification handle.

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

BOOL
TabSrvDeregisterEventNotify(
    IN handle_t   hBinding,
    IN HEVTNOTIFY hEventNotify
    )
{
    TRACEPROC("TabSrvRegisterEventNotify", 2)
    BOOL rc;
    PNOTIFYCLIENT Client = (PNOTIFYCLIENT)hEventNotify;
    DWORD rcWait;

    TRACEENTER(("(hBinding=%x,NotifyClient=%p)\n", Client));

    __try
    {
        rc = (Client->dwSig == SIG_NOTIFYCLIENT);
    }
    __except(1)
    {
        TABSRVERR(("Invalid Notify Client handle.\n"));
        rc = FALSE;
    }

    if (rc == TRUE)
    {
        rcWait = WaitForSingleObject(ghmutNotifyList, INFINITE);
        if (rcWait == WAIT_OBJECT_0)
        {
            RemoveEntryList(&Client->list);
            ReleaseMutex(ghmutNotifyList);
            free(Client);
        }
        else
        {
            TABSRVERR(("failed to wait for notify list Mutex (rcWait=%x,err=%d).\n",
                       rcWait, GetLastError()));
            rc = FALSE;
        }
    }

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

/*++
    @doc    EXTERNAL

    @func   VOID | TabSrvGetLastRawDigiReport | Get the last raw digitizer
            report.

    @parm   IN handle_t | hBinding | RPC binding handle.
    @parm   OUT PWORD | pwButtonState | To hold the button state data.
    @parm   OUT PWORD | pwX | To hold the X data.
    @parm   OUT PWORD | pwY | To hold the Y data.

    @rvalue None.
--*/

VOID
TabSrvGetLastRawDigiReport(
    IN  handle_t hBinding,
    OUT PWORD    pwButtonState,
    OUT PWORD    pwX,
    OUT PWORD    pwY
    )
{
    TRACEPROC("TabSrvGetLastRawDigiReport", 2)

    TRACEENTER(("(hBinding=%x,pwButtonState=%p,pwX=%p,pwY=%p)\n",
                hBinding, pwButtonState, pwX, pwY));

    *pwButtonState = gLastRawDigiReport.wButtonState;
    *pwX = gLastRawDigiReport.wX;
    *pwY = gLastRawDigiReport.wY;

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

/*++
    @doc    EXTERNAL

    @func   VOID | TabSrvSetPenTilt | Set pen tilt compensation.

    @parm   IN handle_t | hBinding | RPC binding handle.
    @parm   IN LONG | dx | x compensation.
    @parm   IN LONG | dy | y compensation.

    @rvalue None.
--*/

VOID
TabSrvSetPenTilt(
    IN handle_t hBinding,
    IN LONG     dx,
    IN LONG     dy
    )
{
    TRACEPROC("TabSrvSetPenTilt", 2)
    LPTSTR lptstrPenTilt;

    TRACEENTER(("(hBinding=%x,dx=%d,dy=%d)\n", hBinding, dx, dy));

    lptstrPenTilt = (gdwfTabSrv & TSF_PORTRAIT_MODE)? TEXT("PenTilt_Portrait"):
                                                      TEXT("PenTilt_Landscape");
    gConfig.PenTilt.dx = dx;
    gConfig.PenTilt.dy = dy;
    WriteConfig(lptstrPenTilt,
                REG_BINARY,
                (LPBYTE)&gConfig.PenTilt,
                sizeof(gConfig.PenTilt));

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

/*++
    @doc    EXTERNAL

    @func   BOOL | TabSrvGetLinearityMap | Get linearity map.

    @parm   IN handle_t | hBinding | RPC binding handle.
    @parm   OUT PLINEARITY_MAP | LinearityMap | Points to the linearity map.

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

BOOL
TabSrvGetLinearityMap(
    IN  handle_t    hBinding,
    OUT PLINEAR_MAP LinearityMap
    )
{
    TRACEPROC("TabSrvGetLinearityMap", 2)
    BOOL rc;

    TRACEENTER(("(hBinding=%x,pLinearityMap=%p)\n", hBinding, LinearityMap));

    if (gdwfTabSrv & TSF_HAS_LINEAR_MAP)
    {
        *LinearityMap = gConfig.LinearityMap;
        rc = TRUE;
    }
    else
    {
        rc = FALSE;
    }

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

/*++
    @doc    EXTERNAL

    @func   VOID | TabSrvSetLinearityMap | Set linearity map.

    @parm   IN handle_t | hBinding | RPC binding handle.
    @parm   IN PLINEARITY_MAP | LinearityMap | Points to the linearity map.

    @rvalue None.
--*/

VOID
TabSrvSetLinearityMap(
    IN handle_t    hBinding,
    IN PLINEAR_MAP LinearityMap
    )
{
    TRACEPROC("TabSrvSetLinearityMap", 2)
    TRACEENTER(("(hBinding=%x,pLinearityMap=%p)\n", hBinding, LinearityMap));

    gConfig.LinearityMap = *LinearityMap;

    WriteConfig(gtszLinearityMap,
                REG_BINARY,
                (LPBYTE)&gConfig.LinearityMap,
                sizeof(gConfig.LinearityMap));
    gdwfTabSrv |= TSF_HAS_LINEAR_MAP;

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

/*++
    @doc    EXTERNAL

    @func   BOOL | TabSrvGetPenFeatures | Get digitizer feature report.

    @parm   IN handle_t | hBinding | RPC binding handle.
    @parm   IN WORD | wReportID | Report ID of the feature report.
    @parm   IN WORD | wUsagePage | Usage page of the digitizer.
    @parm   IN WORD | wUsage | Usage of the digitizer page.
    @parm   OUT DWORD * | pdwFeature | To hold the feature value.

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

BOOL
TabSrvGetPenFeatures(
    IN handle_t hBinding,
    IN  WORD    wReportID,
    IN  WORD    wUsagePage,
    IN  WORD    wUsage,
    OUT DWORD  *pdwFeature
    )
{
    TRACEPROC("TabSrvGetPenFeatures", 2)
    BOOL rc = FALSE;
    PCHAR buff;

    TRACEENTER(("(hBinding=%x,ReportID=%d,UsagePage=%d,Usage=%d,pdwFeature=%p)\n",
                hBinding, wReportID, wUsagePage, wUsage, pdwFeature));

    if (gdevDigitizer.hDevice != INVALID_HANDLE_VALUE)
    {
        buff = (PCHAR)calloc(gdevDigitizer.hidCaps.FeatureReportByteLength,
                             sizeof(CHAR));
        TRACEASSERT(buff != NULL);

        *buff = (CHAR)wReportID;
        rc = HidD_GetFeature(gdevDigitizer.hDevice,
                             buff,
                             gdevDigitizer.hidCaps.FeatureReportByteLength);
        if (rc == TRUE)
        {
            NTSTATUS status;

            status = HidP_GetUsageValue(
                        HidP_Feature,
                        wUsagePage,
                        0,
                        wUsage,
                        pdwFeature,
                        gdevDigitizer.pPreParsedData,
                        buff,
                        gdevDigitizer.hidCaps.FeatureReportByteLength);
            if (status != HIDP_STATUS_SUCCESS)
            {
                rc = FALSE;
                TABSRVERR(("Failed to get feature value (status=%x).\n",
                           status));
            }
        }
        else
        {
            TABSRVERR(("Failed to get device feature.\n"));
        }

        if (buff != NULL)
        {
            free(buff);
        }
    }

    TRACEEXIT(("=%x (Feature=%x)\n", *pdwFeature));
    return rc;
}       //TabSrvGetPenFeatures

/*++
    @doc    EXTERNAL

    @func   BOOL | TabSrvSetPenFeatures | Set digitizer feature report.

    @parm   IN handle_t | hBinding | RPC binding handle.
    @parm   IN WORD | wReportID | Report ID of the feature report.
    @parm   IN WORD | wUsagePage | Usage page of the digitizer.
    @parm   IN WORD | wUsage | Usage of the digitizer page.
    @parm   IN DWORD | dwFeature | Feature value.

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

BOOL
TabSrvSetPenFeatures(
    IN handle_t hBinding,
    IN WORD     wReportID,
    IN WORD     wUsagePage,
    IN WORD     wUsage,
    IN DWORD    dwFeature
    )
{
    TRACEPROC("TabSrvSetPenFeatures", 2)
    BOOL rc = FALSE;
    PCHAR buff;
    NTSTATUS status;

    TRACEENTER(("(hBinding=%x,ReportID=%d,UsagePage=%d,Usage=%d,Feature=%x)\n",
                hBinding, wReportID, wUsagePage, wUsage, dwFeature));

    if (gdevDigitizer.hDevice != INVALID_HANDLE_VALUE)
    {
        buff = (PCHAR)calloc(gdevDigitizer.hidCaps.FeatureReportByteLength,
                             sizeof(CHAR));
        TRACEASSERT(buff != NULL);

        *buff = (CHAR)wReportID;
        status = HidP_SetUsageValue(
                        HidP_Feature,
                        wUsagePage,
                        0,
                        wUsage,
                        dwFeature,
                        gdevDigitizer.pPreParsedData,
                        buff,
                        gdevDigitizer.hidCaps.FeatureReportByteLength);
        if (status == HIDP_STATUS_SUCCESS)
        {
            rc = HidD_SetFeature(gdevDigitizer.hDevice,
                                 buff,
                                 gdevDigitizer.hidCaps.FeatureReportByteLength);
            if (rc == FALSE)
            {
                TABSRVERR(("Failed to set device feature.\n"));
            }
        }
        else
        {
            TABSRVERR(("Failed to set feature value (status=%x).\n", status));
        }

        if (buff != NULL)
        {
            free(buff);
        }
    }

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

/*++
    @doc    EXTERNAL

    @func   BOOL | TabSrvGetGestureSettings | Get gesture settings.

    @parm   IN handle_t | hBinding | RPC binding handle.
    @parm   OUT PGESTURE_SETTINGS | GestureSettings | To hold the gesture
            settings.

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

BOOL
TabSrvGetGestureSettings(
    IN handle_t           hBinding,
    OUT PGESTURE_SETTINGS GestureSettings
    )
{
    TRACEPROC("TabSrvGetGestureSettings", 2)
    TRACEENTER(("(hBinding=%x,pSettings=%p)\n", hBinding, GestureSettings));

    *GestureSettings = gConfig.GestureSettings;

    TRACEEXIT(("=1 (Features=%x,Radius=%d,StopDist=%d,StopTime=%d)\n",
               GestureSettings->dwfFeatures, GestureSettings->iRadius,
               GestureSettings->iStopDist, GestureSettings->iStopTime));
    return TRUE;
}       //TabSrvGetGestureSettings

/*++
    @doc    EXTERNAL

    @func   BOOL | TabSrvSetGestureSettings | Set gesture settings.

    @parm   IN handle_t | hBinding | RPC binding handle.
    @parm   IN PGESTURE_SETTINGS | GestureSettings | Point to the gesture
            settings to set.

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

BOOL
TabSrvSetGestureSettings(
    IN handle_t          hBinding,
    IN PGESTURE_SETTINGS GestureSettings
    )
{
    TRACEPROC("TabSrvSetGestureSettings", 2)
    BOOL rc = FALSE;
    PTSTHREAD MouseThread;

    TRACEENTER(("(hBinding=%x,pSettings=%p,Features=%x,Radius=%d,StopDist=%d,StopTime=%d)\n",
                hBinding, GestureSettings, GestureSettings->dwfFeatures,
                GestureSettings->iRadius, GestureSettings->iStopDist,
                GestureSettings->iStopTime));

    gConfig.GestureSettings = *GestureSettings;

    WriteConfig(gtszGestureSettings,
                REG_BINARY,
                (LPBYTE)&gConfig.GestureSettings,
                sizeof(gConfig.GestureSettings));

    MouseThread = FindThread(TSF_MOUSETHREAD);
    if (MouseThread != NULL)
    {
        if (gConfig.GestureSettings.dwfFeatures & GESTURE_FEATURE_MOUSE_ENABLED)
        {
            MouseThread->dwfThread |= THREADF_ENABLED;
        }
        else
        {
            MouseThread->dwfThread &= ~THREADF_ENABLED;
        }

        if ((MouseThread->dwfThread & THREADF_ENABLED) &&
            (MouseThread->hThread == NULL))
        {
            //
            // Mouse thread is enabled but not running, so start it.
            //
            if (!InitThreads(MouseThread, 1))
            {
                TABSRVERR(("failed to start mouse thread.\n"));
            }
        }
        else if (!(MouseThread->dwfThread & THREADF_ENABLED) &&
                 (MouseThread->hThread != NULL))
        {
            //
            // Mouse thread is disabled but it is currently running, so kill it.
            //
            if (ghwndMouse != NULL)
            {
                DWORD rcWait;

                gdwfTabSrv |= TSF_TERMINATE;
                PostMessage(ghwndMouse, WM_CLOSE, 0, 0);
                rcWait = WaitForSingleObject(MouseThread->hThread, 1000);
                if (rcWait != WAIT_OBJECT_0)
                {
                    TABSRVERR(("failed to kill mouse thread (rcWait=%x,err=%d)\n",
                               rcWait, GetLastError()));
                }
                gdwfTabSrv &= ~TSF_TERMINATE;
            }
        }
    }

    TRACEEXIT(("=1\n"));
    return TRUE;
}       //TabSrvSetGestureSettings

/*++
    @doc    EXTERNAL

    @func   BOOL | TabSrvGetButtonSettings | Get button settings.

    @parm   IN handle_t | hBinding | RPC binding handle.
    @parm   OUT PBUTTON_SETTINGS | ButtonSettings | To hold the button
            settings.

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

BOOL
TabSrvGetButtonSettings(
    IN  handle_t         hBinding,
    OUT PBUTTON_SETTINGS ButtonSettings
    )
{
    TRACEPROC("TabSrvGetButtonSettings", 2)
    TRACEENTER(("(hBinding=%x,pSettings=%p)\n", hBinding, ButtonSettings));

    *ButtonSettings = gConfig.ButtonSettings;

    TRACEEXIT(("=1\n"));
    return TRUE;
}       //TabSrvGetButtonSettings

/*++
    @doc    EXTERNAL

    @func   BOOL | TabSrvSetButtonSettings | Set button settings.

    @parm   IN handle_t | hBinding | RPC binding handle.
    @parm   IN PBUTTON_SETTINGS | ButtonSettings | Point to the button
            settings to set.

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

BOOL
TabSrvSetButtonSettings(
    IN handle_t         hBinding,
    IN PBUTTON_SETTINGS ButtonSettings
    )
{
    TRACEPROC("TabSrvSetButtonSettings", 2)
    BOOL rc = FALSE;
    PTSTHREAD MouseThread;

    TRACEENTER(("(hBinding=%x,pSettings=%p)\n", hBinding, ButtonSettings));

    gConfig.ButtonSettings = *ButtonSettings;

    WriteConfig(gtszButtonSettings,
                REG_BINARY,
                (LPBYTE)&gConfig.ButtonSettings,
                sizeof(gConfig.ButtonSettings));

    TRACEEXIT(("=1\n"));
    return TRUE;
}       //TabSrvSetGestureSettings