//************************************************************************
//  The Logitech LCD SDK, including all acompanying documentation,
//  is protected by intellectual property laws.  All use of the Logitech
//  LCD SDK is subject to the License Agreement found in the
//  "Logitech LCD SDK License Agreement" file and in the Reference Manual.  
//  All rights not expressly granted by Logitech are reserved.
//************************************************************************
 
//************************************************************************
//
// LCDConnection.cpp
//
// The CLCDConnection class manages connections to all LCD devices
// including color and monochrome
// 
// Logitech LCD SDK
//
// Copyright 2010 Logitech Inc.
//************************************************************************
 
#include "LCDUI.h"
 
// Add the lgLcd.lib to the linker
#pragma comment(lib, "lgLcd.lib")
 
// to keep track of clients that use multiple CLCDOutput instances
// within the same app
LONG CLCDConnection::g_lInitCount = 0;
 
//************************************************************************
//
// CLCDConnection::CLCDConnection
//
//************************************************************************
 
CLCDConnection::CLCDConnection(void)
{
    m_hConnection = LGLCD_INVALID_CONNECTION;
 
    ZeroMemory(&m_AppletState, sizeof(m_AppletState));
 
    m_plcdSoftButtonsChangedCtx = NULL;
 
    InitializeCriticalSection(&m_csCallback);
}
 
 
//************************************************************************
//
// CLCDConnection::~CLCDConnection
//
//************************************************************************
 
CLCDConnection::~CLCDConnection(void)
{
    Disconnect();
 
    FreeMonoOutput();
    FreeColorOutput();
 
    if (m_AppletState.Mono.pGfx)
    {
        delete m_AppletState.Mono.pGfx;
        m_AppletState.Mono.pGfx = NULL;
    }
    if (m_AppletState.Color.pGfx)
    {
        delete m_AppletState.Color.pGfx;
        m_AppletState.Color.pGfx = NULL;
    }
    DeleteCriticalSection(&m_csCallback);
}
 
 
//************************************************************************
//
// CLCDConnection::Initialize
//
//************************************************************************
 
BOOL CLCDConnection::Initialize(lgLcdConnectContext & ConnectContext,
                                lgLcdSoftbuttonsChangedContext* pSoftButtonChangedContext)
{
    // Assume only BW is supported
    m_AppletState.Mono.pOutput = new CLCDOutput();
    m_AppletState.Mono.pGfx = new CLCDGfxMono();
    m_AppletState.Mono.pGfx->Initialize();
    m_AppletState.Mono.pOutput->SetGfx(m_AppletState.Mono.pGfx);
 
    lgLcdConnectContextEx ConnectContextEx;
    memset(&ConnectContextEx, 0, sizeof(ConnectContextEx));
    ConnectContextEx.appFriendlyName = ConnectContext.appFriendlyName;
    ConnectContextEx.isPersistent = ConnectContext.isPersistent;
    ConnectContextEx.isAutostartable = ConnectContext.isAutostartable;
    ConnectContextEx.connection = LGLCD_INVALID_CONNECTION;
    ConnectContextEx.onConfigure = ConnectContext.onConfigure;
    ConnectContextEx.dwAppletCapabilitiesSupported = LGLCD_APPLET_CAP_BW;
 
    return Initialize(ConnectContextEx, pSoftButtonChangedContext);
}
 
 
//************************************************************************
//
// CLCDConnection::Initialize
//
//************************************************************************
 
BOOL CLCDConnection::Initialize(lgLcdConnectContextEx & ConnectContext,
                                lgLcdSoftbuttonsChangedContext* pSoftButtonChangedContext)
{
    DWORD res = ERROR_SUCCESS;
 
    if ((LGLCD_APPLET_CAP_BASIC == ConnectContext.dwAppletCapabilitiesSupported) ||
         ConnectContext.dwAppletCapabilitiesSupported & LGLCD_APPLET_CAP_BW)
    {
        m_AppletState.Mono.pOutput = AllocMonoOutput();
        m_AppletState.Mono.pGfx = new CLCDGfxMono();
        m_AppletState.Mono.pGfx->Initialize();
        m_AppletState.Mono.pOutput->SetGfx(m_AppletState.Mono.pGfx);
    }
 
    if (ConnectContext.dwAppletCapabilitiesSupported & LGLCD_APPLET_CAP_QVGA)
    {
        m_AppletState.Color.pOutput = AllocColorOutput();
        m_AppletState.Color.pGfx = new CLCDGfxColor();
        m_AppletState.Color.pGfx->Initialize();
        m_AppletState.Color.pOutput->SetGfx(m_AppletState.Color.pGfx);
    }
 
    //Assure we only call the lib's init once
    LCDUIASSERT(g_lInitCount >= 0);
    if(1 == InterlockedIncrement(&g_lInitCount))
    {
        // need to call lgLcdInit once
        res = lgLcdInit();
        if (ERROR_SUCCESS != res)
        {
            InterlockedDecrement(&g_lInitCount);
            LCDUITRACE(_T("WARNING: lgLcdInit failed\n"));
            return FALSE;
        }
    }
 
    memset(&m_lcdConnectCtxEx, 0, sizeof(m_lcdConnectCtxEx));
    m_lcdConnectCtxEx.appFriendlyName = NULL;
    m_lcdConnectCtxEx.isPersistent = FALSE;
    m_lcdConnectCtxEx.isAutostartable = FALSE;
    m_lcdConnectCtxEx.connection = LGLCD_INVALID_CONNECTION;
 
    // Initialize the added version 3.0 API fields
    m_lcdConnectCtxEx.dwAppletCapabilitiesSupported = LGLCD_APPLET_CAP_BASIC;
    m_lcdConnectCtxEx.dwReserved1 = 0;
    m_lcdConnectCtxEx.onNotify.notificationCallback = NULL;
    m_lcdConnectCtxEx.onNotify.notifyContext = NULL;
 
    memcpy(&m_lcdConnectCtxEx, &ConnectContext, sizeof(lgLcdConnectContextEx));
 
    m_plcdSoftButtonsChangedCtx = pSoftButtonChangedContext;
 
    // Add internal callback handlers if not specified
    if (NULL == m_lcdConnectCtxEx.onNotify.notificationCallback)
    {
        m_lcdConnectCtxEx.onNotify.notificationCallback = _OnNotificationCallback;
        m_lcdConnectCtxEx.onNotify.notifyContext = this;
    }
 
    Connect();
 
    if (LGLCD_INVALID_CONNECTION != m_hConnection)
    {
        // At this point, the asynchronous events have already been sent.
        // Let's allow a small amount of time for the callback thread to insert the events 
        // into the shared queue.
        // If you don't care about knowing about devices after Initialize
        // completes, you can remove these 2 lines
        Sleep(50);
        Update();
    }
 
    return TRUE;
}
 
 
//************************************************************************
//
// CLCDConnection::Shutdown
//
//************************************************************************
 
void CLCDConnection::Shutdown(void)
{
    Disconnect();
 
    if(0 == InterlockedDecrement(&g_lInitCount))
    {
        lgLcdDeInit();
    }
 
    m_plcdSoftButtonsChangedCtx = NULL;
}
 
 
//************************************************************************
//
// CLCDConnection::Connect
// 
// This will attempt a connection to LCDMon
//************************************************************************
 
void CLCDConnection::Connect(void)
{
    //Close previous connections
    Disconnect();
 
    if (LGLCD_INVALID_CONNECTION == m_hConnection)
    {
        DWORD retval = lgLcdConnectEx(&m_lcdConnectCtxEx);
        if (ERROR_SUCCESS == retval)
        {
            m_hConnection = m_lcdConnectCtxEx.connection;
        }
        else
        {
            if( ERROR_SERVICE_NOT_ACTIVE == retval )
            {
                LCDUITRACE( _T("lgLcdConnectEx ---> ERROR_INVALID_PARAMETER\n") );
                m_hConnection = LGLCD_INVALID_CONNECTION;
            }
 
            m_hConnection = LGLCD_INVALID_CONNECTION;
        }
    }
}
 
 
//************************************************************************
//
// CLCDConnection::Disconnect
//
//************************************************************************
 
void CLCDConnection::Disconnect(void)
{   
    //Close your devices, too
    if (m_AppletState.Color.pOutput)
    {
        m_AppletState.Color.pOutput->Close();
    }
    if (m_AppletState.Mono.pOutput)
    {
        m_AppletState.Mono.pOutput->Close();
    }
 
    if( LGLCD_INVALID_CONNECTION != m_hConnection )
    {
        lgLcdDisconnect(m_hConnection);
        m_hConnection = LGLCD_INVALID_CONNECTION;
    }
}
 
 
//************************************************************************
//
// CLCDConnection::IsConnected
//
//************************************************************************
 
BOOL CLCDConnection::IsConnected(void)
{
    return LGLCD_INVALID_CONNECTION != m_hConnection;
}
 
 
//************************************************************************
//
// CLCDConnection::GetConnectionId
//
//************************************************************************
 
int CLCDConnection::GetConnectionId(void)
{
    return m_hConnection;
}
 
 
//************************************************************************
//
// CLCDConnection::ColorOutput
//
//************************************************************************
 
CLCDOutput *CLCDConnection::ColorOutput(void)
{
    return m_AppletState.Color.pOutput;
}
 
 
//************************************************************************
//
// CLCDConnection::MonoOutput
//
//************************************************************************
 
CLCDOutput *CLCDConnection::MonoOutput(void)
{
    return m_AppletState.Mono.pOutput;
}
 
 
//************************************************************************
//
// CLCDConnection::_OnNotificationCallback
//
//************************************************************************
 
DWORD CALLBACK CLCDConnection::_OnNotificationCallback(
    IN int connection,
    IN const PVOID pContext,
    IN DWORD notificationCode,
    IN DWORD notifyParm1,
    IN DWORD notifyParm2,
    IN DWORD notifyParm3,
    IN DWORD notifyParm4)
{
 
    UNREFERENCED_PARAMETER(connection);
    UNREFERENCED_PARAMETER(notifyParm3);
    UNREFERENCED_PARAMETER(notifyParm4);
 
    CLCDConnection* pThis = (CLCDConnection*)pContext;
    
    CB_EVENT Event;
    memset(&Event, 0, sizeof(Event));
    EnterCriticalSection(&pThis->m_csCallback);
    Event.Type = CBT_NOTIFICATION;
    Event.CallbackCode = notificationCode;
    Event.CallbackParam1 = notifyParm1;
    Event.CallbackParam2 = notifyParm2;
    pThis->m_CallbackEventQueue.push(Event);
    LeaveCriticalSection(&pThis->m_csCallback);
 
    pThis->OnCallbackEvent();
 
    return 0;
}
 
 
//************************************************************************
//
// CLCDConnection::_OnSoftButtonsCallback
//
//************************************************************************
 
DWORD CALLBACK CLCDConnection::_OnSoftButtonsCallback(
    IN int device,
    IN DWORD dwButtons,
    IN const PVOID pContext)
{
    CLCDConnection* pThis = (CLCDConnection*)pContext;
 
    CB_EVENT Event;
    memset(&Event, 0, sizeof(Event));
    EnterCriticalSection(&pThis->m_csCallback);
    Event.Type = CBT_BUTTON;
    Event.CallbackCode = device;
    Event.CallbackParam1 = dwButtons;
    pThis->m_CallbackEventQueue.push(Event);
    LeaveCriticalSection(&pThis->m_csCallback);
 
    pThis->OnCallbackEvent();
 
    return 0;
}
 
 
//************************************************************************
//
// CLCDConnection::_OnConfigureCallback
//
//************************************************************************
 
DWORD CALLBACK CLCDConnection::_OnConfigureCallback(
    IN int connection,
    IN const PVOID pContext)
{
    UNREFERENCED_PARAMETER(connection);
 
    CLCDConnection* pThis = (CLCDConnection*)pContext;
 
    CB_EVENT Event;
    memset(&Event, 0, sizeof(Event));
    EnterCriticalSection(&pThis->m_csCallback);
    Event.Type = CBT_CONFIG;
    pThis->m_CallbackEventQueue.push(Event);
    LeaveCriticalSection(&pThis->m_csCallback);
 
    return 0;
}
 
 
//************************************************************************
//
// CLCDConnection::GetNextCallbackEvent
//
//************************************************************************
 
BOOL CLCDConnection::GetNextCallbackEvent(CB_EVENT& rEvent)
{
    memset(&rEvent, 0, sizeof(rEvent));
    EnterCriticalSection(&m_csCallback);
    if(0 < m_CallbackEventQueue.size())
    {
        std::swap(rEvent, m_CallbackEventQueue.front());
        m_CallbackEventQueue.pop();
        LeaveCriticalSection(&m_csCallback);
        return TRUE;
    }
    LeaveCriticalSection(&m_csCallback);
    return FALSE;
}
 
 
//************************************************************************
//
// CLCDConnection::Update
//
//************************************************************************
 
void CLCDConnection::Update(void)
{
    // If we're not connected, connect
    if (LGLCD_INVALID_CONNECTION == m_hConnection)
    {
        Connect();
    }
 
    // Get events
    CB_EVENT Event;
    while(GetNextCallbackEvent(Event))
    {
        switch(Event.Type)
        {
        case CBT_NOTIFICATION:
            OnNotification(Event.CallbackCode, Event.CallbackParam1);
            break;
 
        case CBT_CONFIG:
            OnConfigure();
            break;
 
        case CBT_BUTTON:
            OnSoftButtonEvent(Event.CallbackCode, Event.CallbackParam1);
            break;
 
        default:
            break;
        }
    }
 
    // For each display type
    for (int i = 0; i < 2; i++)
    {
        LCD_DEVICE_STATE* pDevice = (i == 0) ? &m_AppletState.Mono : &m_AppletState.Color;
 
        if (NULL == pDevice->pOutput)
        {
            // Output not supported
            continue;
        }
 
        if (pDevice->pOutput->IsOpened())
        {
            pDevice->pOutput->OnUpdate(GetTickCount());
            pDevice->pOutput->OnDraw();
        }
 
        // If the device is closed, but it was opened by OpenByType(),
        // we can try to open it again...
        if (!pDevice->pOutput->IsOpened() && pDevice->pOutput->HasBeenOpenedByDeviceType())
        {
            pDevice->pOutput->ReOpenDeviceType();
        }
    }
}
 
 
//************************************************************************
//
// CLCDConnection::OnConfigure
//
//************************************************************************
 
void CLCDConnection::OnConfigure(void)
{
    LCDUITRACE(_T("OnConfigure\n"));
}
 
 
//************************************************************************
//
// CLCDConnection::OnDeviceArrival
//
//************************************************************************
 
void CLCDConnection::OnDeviceArrival(DWORD dwDisplayType)
{
    LCD_DEVICE_STATE* pDevice = NULL;
 
    switch(dwDisplayType)
    {
    case LGLCD_DEVICE_BW:
        pDevice = &m_AppletState.Mono;
        break;
 
    case LGLCD_DEVICE_QVGA:
        pDevice = &m_AppletState.Color;
        break;
        
    default:
        LCDUITRACE(_T("Unhandled DisplayType in OnDeviceArrival()!\n"));
        return;
    }
 
    // Ensure that we have a valid output
    if (NULL == pDevice->pOutput)
    {
        LCDUITRACE(_T("Device arrival on unsupported device\n"));
        return;
    }
 
    lgLcdOpenByTypeContext OpenCtx;
    memset(&OpenCtx, 0, sizeof(OpenCtx));
 
    OpenCtx.connection = m_hConnection;
    OpenCtx.deviceType = dwDisplayType;
    OpenCtx.onSoftbuttonsChanged.softbuttonsChangedCallback = _OnSoftButtonsCallback;
    OpenCtx.onSoftbuttonsChanged.softbuttonsChangedContext = this;
    OpenCtx.device = LGLCD_INVALID_DEVICE;
 
    // If user has specified the soft button context, allow user to override
    if (NULL != m_plcdSoftButtonsChangedCtx)
    {
        OpenCtx.onSoftbuttonsChanged = *m_plcdSoftButtonsChangedCtx;
    }
 
    pDevice->pOutput->OpenByType(OpenCtx);
}
 
 
//************************************************************************
//
// CLCDConnection::OnDeviceRemoval
//
//************************************************************************
 
void CLCDConnection::OnDeviceRemoval(DWORD dwDisplayType)
{
    LCD_DEVICE_STATE* pDevice = NULL;
 
    switch(dwDisplayType)
    {
    case LGLCD_DEVICE_BW:
        pDevice = &m_AppletState.Mono;
        break;
 
    case LGLCD_DEVICE_QVGA:
        pDevice = &m_AppletState.Color;
        break;
 
    default:
        LCDUITRACE(_T("Unhandled DisplayType in OnDeviceArrival()!\n"));
        return;
    }
 
    if (pDevice->pOutput)
    {
        pDevice->pOutput->StopOpeningByDeviceType();
        pDevice->pOutput->Close();
    }
}
 
 
//************************************************************************
//
// CLCDConnection::OnAppletEnabled
//
//************************************************************************
 
void CLCDConnection::OnAppletEnabled(void)
{
}
 
 
//************************************************************************
//
// CLCDConnection::OnAppletDisabled
//
//************************************************************************
 
void CLCDConnection::OnAppletDisabled(void)
{
}
 
 
//************************************************************************
//
// CLCDConnection::OnNotification
//
//************************************************************************
 
void CLCDConnection::OnNotification(DWORD dwNotification, DWORD dwParam1)
{
    switch(dwNotification)
    {
    case LGLCD_NOTIFICATION_DEVICE_ARRIVAL:
        LCDUITRACE(_T("LGLCD_NOTIFICATION_DEVICE_ARRIVAL\n"));
        OnDeviceArrival(dwParam1);
        break;
 
    case LGLCD_NOTIFICATION_DEVICE_REMOVAL:
        LCDUITRACE(_T("LGLCD_NOTIFICATION_DEVICE_REMOVAL\n"));
        OnDeviceRemoval(dwParam1);
        break;
 
    case LGLCD_NOTIFICATION_CLOSE_CONNECTION:
        LCDUITRACE(_T("LGLCD_NOTIFICATION_CLOSE_CONNECTION\n"));
        Disconnect();
        break;
 
    case LGLCD_NOTIFICATION_APPLET_DISABLED:
        LCDUITRACE(_T("LGLCD_NOTIFICATION_APPLET_DISABLED\n"));
        OnAppletDisabled();
        break;
 
    case LGLCD_NOTIFICATION_APPLET_ENABLED:
        LCDUITRACE(_T("LGLCD_NOTIFICATION_APPLET_ENABLED\n"));
        OnAppletEnabled();
        break;
 
    case LGLCD_NOTIFICATION_TERMINATE_APPLET:
        break;
    }
}
 
 
//************************************************************************
//
// CLCDConnection::OnSoftButtonEvent
//
//************************************************************************
 
void CLCDConnection::OnSoftButtonEvent(int nDeviceId, DWORD dwButtonState)
{
    // Forward this to the appropriate display
    if (m_AppletState.Mono.pOutput && (nDeviceId == m_AppletState.Mono.pOutput->GetDeviceId()))
    {
        m_AppletState.Mono.pOutput->OnSoftButtonEvent(dwButtonState);
    }
    else if (m_AppletState.Color.pOutput && (nDeviceId == m_AppletState.Color.pOutput->GetDeviceId()))
    {
        m_AppletState.Color.pOutput->OnSoftButtonEvent(dwButtonState);
    }
}
 
 
//************************************************************************
//
// CLCDConnection::AllocMonoOutput
//
//************************************************************************
 
CLCDOutput* CLCDConnection::AllocMonoOutput(void)
{
    return new CLCDOutput();
}
 
 
//************************************************************************
//
// CLCDConnection::AllocColorOutput
//
//************************************************************************
 
CLCDOutput* CLCDConnection::AllocColorOutput(void)
{
    return new CLCDOutput();
}
 
 
//************************************************************************
//
// CLCDConnection::FreeMonoOutput
//
//************************************************************************
 
void CLCDConnection::FreeMonoOutput(void)
{
    if (NULL != m_AppletState.Mono.pOutput)
    {
        delete m_AppletState.Mono.pOutput;
        m_AppletState.Mono.pOutput = NULL;
    }
}
 
 
//************************************************************************
//
// CLCDConnection::FreeColorOutput
//
//************************************************************************
 
void CLCDConnection::FreeColorOutput(void)
{
    if (NULL != m_AppletState.Color.pOutput)
    {
        delete m_AppletState.Color.pOutput;
        m_AppletState.Color.pOutput = NULL;
    }
}
 
//** end of LCDConnection.cpp ********************************************

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: m_lcdConnectCtxEx.

V519 The 'm_hConnection' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 228, 231.