788 lines
26 KiB
C++
788 lines
26 KiB
C++
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
// MTScratchpadRTStylus application.
|
|
// Description:
|
|
// Inside the application window, user can draw using multiple fingers
|
|
// at the same time. The trace of each finger is drawn using different
|
|
// color. The primary finger trace is always drawn in black, and the
|
|
// remaining traces are drawn by rotating through the following colors:
|
|
// red, blue, green, magenta, cyan and yellow.
|
|
//
|
|
// Purpose:
|
|
// This sample demonstrates handling of the multi-touch input inside
|
|
// a Win32 application using Real Time Stylus inking API:
|
|
// - Setting up minimal RTS object configuration that allows the window
|
|
// to be inking-enabled (this is not multi-touch specific);
|
|
// Construction and initialization of RealTimeStylus object and
|
|
// DynamicRenderer object.
|
|
// - Using IRealTimeStylus3 to register an inking-enabled window for multi-touch.
|
|
// - Deriving and initializing synchronous RTS plugin that receives
|
|
// inking notifications that are multi-touch aware.
|
|
// - Handling multi-touch aware pen-down/up/move notifications: changing
|
|
// stroke color on pen-down.
|
|
//
|
|
// MTScratchpadRTStylus.cpp : Defines the entry point for the application.
|
|
//
|
|
|
|
// Windows header files
|
|
#include <windows.h>
|
|
|
|
// C RunTime header files
|
|
#include <stdlib.h>
|
|
#include <malloc.h>
|
|
#include <memory.h>
|
|
#include <assert.h>
|
|
#define ASSERT assert
|
|
|
|
// COM header files
|
|
#include <ole2.h>
|
|
|
|
// RealTimeStylus header files
|
|
#include <rtscom.h> // RTS interface and GUID declarations
|
|
#include <rtscom_i.c> // RTS GUID definitions
|
|
|
|
// Application header files
|
|
#include "resource.h"
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define MAX_LOADSTRING 100
|
|
|
|
// Global Variables:
|
|
HINSTANCE g_hInst; // Current module instance
|
|
WCHAR g_wszTitle[MAX_LOADSTRING]; // The title bar text
|
|
WCHAR g_wszWindowClass[MAX_LOADSTRING]; // The main window class name
|
|
IRealTimeStylus* g_pRealTimeStylus = NULL; // RTS object
|
|
IDynamicRenderer* g_pDynamicRenderer = NULL; // DynamicRenderer object
|
|
IStylusSyncPlugin* g_pSyncEventHandlerRTS = NULL; // EventHandler object
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// RealTimeStylus utilities
|
|
|
|
// Creates the RealTimeStylus object, attaches it to the window, and
|
|
// enables it for multi-touch.
|
|
// in:
|
|
// hWnd handle to device window
|
|
// returns:
|
|
// RTS object through IRealTimeStylus interface, or NULL on failure
|
|
IRealTimeStylus* CreateRealTimeStylus(HWND hWnd)
|
|
{
|
|
// Check input argument
|
|
if (hWnd == NULL)
|
|
{
|
|
ASSERT(hWnd && L"CreateRealTimeStylus: invalid argument hWnd");
|
|
return NULL;
|
|
}
|
|
|
|
// Create RTS object
|
|
IRealTimeStylus* pRealTimeStylus = NULL;
|
|
HRESULT hr = CoCreateInstance(CLSID_RealTimeStylus, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pRealTimeStylus));
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(SUCCEEDED(hr) && L"CreateRealTimeStylus: failed to CoCreateInstance of RealTimeStylus");
|
|
return NULL;
|
|
}
|
|
|
|
// Attach RTS object to a window
|
|
hr = pRealTimeStylus->put_HWND((HANDLE_PTR)hWnd);
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(SUCCEEDED(hr) && L"CreateRealTimeStylus: failed to set window handle");
|
|
pRealTimeStylus->Release();
|
|
return NULL;
|
|
}
|
|
|
|
// Register RTS object for receiving multi-touch input.
|
|
IRealTimeStylus3* pRealTimeStylus3 = NULL;
|
|
hr = pRealTimeStylus->QueryInterface(&pRealTimeStylus3);
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(SUCCEEDED(hr) && L"CreateRealTimeStylus: cannot access IRealTimeStylus3");
|
|
pRealTimeStylus->Release();
|
|
return NULL;
|
|
}
|
|
hr = pRealTimeStylus3->put_MultiTouchEnabled(TRUE);
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(SUCCEEDED(hr) && L"CreateRealTimeStylus: failed to enable multi-touch");
|
|
pRealTimeStylus->Release();
|
|
pRealTimeStylus3->Release();
|
|
return NULL;
|
|
}
|
|
pRealTimeStylus3->Release();
|
|
|
|
return pRealTimeStylus;
|
|
}
|
|
|
|
// Creates the DynamicRenderer object and adds it to the RTS object as a synchronous plugin.
|
|
// in:
|
|
// pRealTimeStylus RTS object
|
|
// returns:
|
|
// DynamicRenderer object through IDynamicRenderer interface, or NULL on failure
|
|
IDynamicRenderer* CreateDynamicRenderer(IRealTimeStylus* pRealTimeStylus)
|
|
{
|
|
// Check input argument
|
|
if (pRealTimeStylus == NULL)
|
|
{
|
|
ASSERT(pRealTimeStylus && L"CreateDynamicRenderer: invalid argument RealTimeStylus");
|
|
return NULL;
|
|
}
|
|
|
|
// Get window handle from RTS object
|
|
HWND hWnd = NULL;
|
|
HRESULT hr = pRealTimeStylus->get_HWND((HANDLE_PTR*)&hWnd);
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(SUCCEEDED(hr) && L"CreateDynamicRenderer: failed to get window handle");
|
|
return NULL;
|
|
}
|
|
|
|
// Create DynamicRenderer object
|
|
IDynamicRenderer* pDynamicRenderer = NULL;
|
|
hr = CoCreateInstance(CLSID_DynamicRenderer, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pDynamicRenderer));
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(SUCCEEDED(hr) && L"CreateDynamicRenderer: failed to CoCreateInstance of DynamicRenderer");
|
|
return NULL;
|
|
}
|
|
|
|
// Add DynamicRenderer to the RTS object as a synchronous plugin
|
|
IStylusSyncPlugin* pSyncDynamicRenderer = NULL;
|
|
hr = pDynamicRenderer->QueryInterface(&pSyncDynamicRenderer);
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(SUCCEEDED(hr) && L"CreateDynamicRenderer: failed to access IStylusSyncPlugin of DynamicRenderer");
|
|
pDynamicRenderer->Release();
|
|
return NULL;
|
|
}
|
|
|
|
hr = pRealTimeStylus->AddStylusSyncPlugin(
|
|
0, // insert plugin at position 0 in the sync plugin list
|
|
pSyncDynamicRenderer); // plugin to be inserted - DynamicRenderer
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(SUCCEEDED(hr) && L"CreateDynamicRenderer: failed to add DynamicRenderer to the RealTimeStylus plugins");
|
|
pDynamicRenderer->Release();
|
|
pSyncDynamicRenderer->Release();
|
|
return NULL;
|
|
}
|
|
|
|
// Attach DynamicRenderer to the same window RTS object is attached to
|
|
hr = pDynamicRenderer->put_HWND((HANDLE_PTR)hWnd);
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(SUCCEEDED(hr) && L"CreateDynamicRenderer: failed to set window handle");
|
|
pDynamicRenderer->Release();
|
|
pSyncDynamicRenderer->Release();
|
|
return NULL;
|
|
}
|
|
|
|
pSyncDynamicRenderer->Release();
|
|
|
|
return pDynamicRenderer;
|
|
}
|
|
|
|
// Turns on RTS and DynamicRenderer object.
|
|
// in:
|
|
// pRealTimeStylus RTS object
|
|
// pDynamicRenderer DynamicRenderer object
|
|
// returns:
|
|
// flag, is initialization successful
|
|
bool EnableRealTimeStylus(IRealTimeStylus* pRealTimeStylus, IDynamicRenderer* pDynamicRenderer)
|
|
{
|
|
// Check input arguments
|
|
if (pRealTimeStylus == NULL)
|
|
{
|
|
ASSERT(pRealTimeStylus && L"EnableRealTimeStylus: invalid argument RealTimeStylus");
|
|
return NULL;
|
|
}
|
|
if (pDynamicRenderer == NULL)
|
|
{
|
|
ASSERT(pDynamicRenderer && L"EnableRealTimeStylus: invalid argument DynamicRenderer");
|
|
return false;
|
|
}
|
|
|
|
// Enable RTS object
|
|
HRESULT hr = pRealTimeStylus->put_Enabled(TRUE);
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(SUCCEEDED(hr) && L"EnableRealTimeStylus: failed to enable RealTimeStylus");
|
|
return false;
|
|
}
|
|
|
|
// Enable DynamicRenderer
|
|
hr = pDynamicRenderer->put_Enabled(TRUE);
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(SUCCEEDED(hr) && L"EnableRealTimeStylus: failed to enable DynamicRenderer");
|
|
return false;
|
|
}
|
|
|
|
// Enable DynamicRenderer's auto-redraw feature
|
|
hr = pDynamicRenderer->put_DataCacheEnabled(TRUE);
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(SUCCEEDED(hr) && L"EnableRealTimeStylus: failed to enable DynamicRenderer data cache");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// RealTimeStylus notification handlers
|
|
|
|
// OnPaint notification handler.
|
|
// in:
|
|
// pDynamicRenderer DynamicRenderer object
|
|
// returns:
|
|
// success status
|
|
bool OnPaintHandler(IDynamicRenderer* pDynamicRenderer)
|
|
{
|
|
// Check input arguments
|
|
if (pDynamicRenderer == NULL)
|
|
{
|
|
ASSERT(pDynamicRenderer && L"OnPaintHandler: invalid argument DynamicRenderer");
|
|
return false;
|
|
}
|
|
|
|
// Ask DynamicRenderer to redraw itself
|
|
HRESULT hr = pDynamicRenderer->Refresh();
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(SUCCEEDED(hr) && L"OnPaintHandler: failed to refresh DynamicRenderer");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Returns color for the newly started stroke.
|
|
// in:
|
|
// bPrimaryContact flag, whether the contact is the primary contact
|
|
// returns:
|
|
// COLORREF, color of the stroke
|
|
COLORREF GetTouchColor(bool bPrimaryContact)
|
|
{
|
|
static int g_iCurrColor = 0; // Rotating secondary color index
|
|
static COLORREF g_arrColor[] = // Secondary colors array
|
|
{
|
|
RGB(255, 0, 0), // Red
|
|
RGB(0, 255, 0), // Green
|
|
RGB(0, 0, 255), // Blue
|
|
RGB(0, 255, 255), // Cyan
|
|
RGB(255, 0, 255), // Magenta
|
|
RGB(255, 255, 0) // Yellow
|
|
};
|
|
|
|
COLORREF color;
|
|
if (bPrimaryContact)
|
|
{
|
|
// The primary contact is drawn in black.
|
|
color = RGB(0,0,0); // Black
|
|
}
|
|
else
|
|
{
|
|
// Take current secondary color.
|
|
color = g_arrColor[g_iCurrColor];
|
|
|
|
// Move to the next color in the array.
|
|
g_iCurrColor = (g_iCurrColor + 1) % (sizeof(g_arrColor)/sizeof(g_arrColor[0]));
|
|
}
|
|
|
|
return color;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Real Time Stylus sync event handler
|
|
|
|
// Synchronous plugin, notitification receiver that changes pen color.
|
|
class CSyncEventHandlerRTS : public IStylusSyncPlugin
|
|
{
|
|
CSyncEventHandlerRTS();
|
|
virtual ~CSyncEventHandlerRTS();
|
|
|
|
public:
|
|
// Factory method
|
|
static IStylusSyncPlugin* Create(IRealTimeStylus* pRealTimeStylus);
|
|
|
|
// IStylusSyncPlugin methods
|
|
|
|
// Handled IStylusSyncPlugin methods, they require nontrivial implementation
|
|
STDMETHOD(StylusDown)(IRealTimeStylus* piSrcRtp, const StylusInfo* pStylusInfo, ULONG cPropCountPerPkt, LONG* pPacket, LONG** ppInOutPkt);
|
|
STDMETHOD(StylusUp)(IRealTimeStylus* piSrcRtp, const StylusInfo* pStylusInfo, ULONG cPropCountPerPkt, LONG* pPacket, LONG** ppInOutPkt);
|
|
STDMETHOD(Packets)(IRealTimeStylus* piSrcRtp, const StylusInfo* pStylusInfo, ULONG cPktCount, ULONG cPktBuffLength, LONG* pPackets, ULONG* pcInOutPkts, LONG** ppInOutPkts);
|
|
STDMETHOD(DataInterest)(RealTimeStylusDataInterest* pEventInterest);
|
|
|
|
// IStylusSyncPlugin methods with trivial inline implementation, they all return S_OK
|
|
STDMETHOD(RealTimeStylusEnabled)(IRealTimeStylus*, ULONG, const TABLET_CONTEXT_ID*) { return S_OK; }
|
|
STDMETHOD(RealTimeStylusDisabled)(IRealTimeStylus*, ULONG, const TABLET_CONTEXT_ID*) { return S_OK; }
|
|
STDMETHOD(StylusInRange)(IRealTimeStylus*, TABLET_CONTEXT_ID, STYLUS_ID) { return S_OK; }
|
|
STDMETHOD(StylusOutOfRange)(IRealTimeStylus*, TABLET_CONTEXT_ID, STYLUS_ID) { return S_OK; }
|
|
STDMETHOD(InAirPackets)(IRealTimeStylus*, const StylusInfo*, ULONG, ULONG, LONG*, ULONG*, LONG**) { return S_OK; }
|
|
STDMETHOD(StylusButtonUp)(IRealTimeStylus*, STYLUS_ID, const GUID*, POINT*) { return S_OK; }
|
|
STDMETHOD(StylusButtonDown)(IRealTimeStylus*, STYLUS_ID, const GUID*, POINT*) { return S_OK; }
|
|
STDMETHOD(SystemEvent)(IRealTimeStylus*, TABLET_CONTEXT_ID, STYLUS_ID, SYSTEM_EVENT, SYSTEM_EVENT_DATA) { return S_OK; }
|
|
STDMETHOD(TabletAdded)(IRealTimeStylus*, IInkTablet*) { return S_OK; }
|
|
STDMETHOD(TabletRemoved)(IRealTimeStylus*, LONG) { return S_OK; }
|
|
STDMETHOD(CustomStylusDataAdded)(IRealTimeStylus*, const GUID*, ULONG, const BYTE*) { return S_OK; }
|
|
STDMETHOD(Error)(IRealTimeStylus*, IStylusPlugin*, RealTimeStylusDataInterest, HRESULT, LONG_PTR*) { return S_OK; }
|
|
STDMETHOD(UpdateMapping)(IRealTimeStylus*) { return S_OK; }
|
|
|
|
// IUnknown methods
|
|
STDMETHOD_(ULONG,AddRef)();
|
|
STDMETHOD_(ULONG,Release)();
|
|
STDMETHOD(QueryInterface)(REFIID riid, LPVOID *ppvObj);
|
|
|
|
private:
|
|
LONG m_cRefCount; // COM object reference count
|
|
IUnknown* m_punkFTMarshaller; // free-threaded marshaller
|
|
int m_nContacts; // number of fingers currently in the contact with the touch digitizer
|
|
};
|
|
|
|
// CSyncEventHandlerRTS constructor.
|
|
CSyncEventHandlerRTS::CSyncEventHandlerRTS()
|
|
: m_cRefCount(1),
|
|
m_punkFTMarshaller(NULL),
|
|
m_nContacts(0)
|
|
{
|
|
}
|
|
|
|
// CSyncEventHandlerRTS destructor.
|
|
CSyncEventHandlerRTS::~CSyncEventHandlerRTS()
|
|
{
|
|
if (m_punkFTMarshaller != NULL)
|
|
{
|
|
m_punkFTMarshaller->Release();
|
|
}
|
|
}
|
|
|
|
// CSyncEventHandlerRTS factory method: creates new CSyncEventHandlerRTS and adds it to the synchronous
|
|
// plugin list of the RTS object.
|
|
// in:
|
|
// pRealTimeStylus RTS object
|
|
// returns:
|
|
// CSyncEventHandlerRTS object through IStylusSyncPlugin interface, or NULL on failure
|
|
IStylusSyncPlugin* CSyncEventHandlerRTS::Create(IRealTimeStylus* pRealTimeStylus)
|
|
{
|
|
// Check input argument
|
|
if (pRealTimeStylus == NULL)
|
|
{
|
|
ASSERT(pRealTimeStylus != NULL && L"CSyncEventHandlerRTS::Create: invalid argument RealTimeStylus");
|
|
return NULL;
|
|
}
|
|
|
|
// Instantiate CSyncEventHandlerRTS object
|
|
CSyncEventHandlerRTS* pSyncEventHandlerRTS = new CSyncEventHandlerRTS();
|
|
if (pSyncEventHandlerRTS == NULL)
|
|
{
|
|
ASSERT(pSyncEventHandlerRTS != NULL && L"CSyncEventHandlerRTS::Create: cannot create instance of CSyncEventHandlerRTS");
|
|
return NULL;
|
|
}
|
|
|
|
// Create free-threaded marshaller for this object and aggregate it.
|
|
HRESULT hr = CoCreateFreeThreadedMarshaler(pSyncEventHandlerRTS, &pSyncEventHandlerRTS->m_punkFTMarshaller);
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(SUCCEEDED(hr) && L"CSyncEventHandlerRTS::Create: cannot create free-threaded marshaller");
|
|
pSyncEventHandlerRTS->Release();
|
|
return NULL;
|
|
}
|
|
|
|
// Add CSyncEventHandlerRTS object to the list of synchronous plugins in the RTS object.
|
|
hr = pRealTimeStylus->AddStylusSyncPlugin(
|
|
0, // insert plugin at position 0 in the sync plugin list
|
|
pSyncEventHandlerRTS); // plugin to be inserted - event handler CSyncEventHandlerRTS
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(SUCCEEDED(hr) && L"CEventHandlerRTS::Create: failed to add CSyncEventHandlerRTS to the RealTimeStylus plugins");
|
|
pSyncEventHandlerRTS->Release();
|
|
return NULL;
|
|
}
|
|
|
|
return pSyncEventHandlerRTS;
|
|
}
|
|
|
|
// Pen-down notification.
|
|
// Sets the color for the newly started stroke and increments finger-down counter.
|
|
// in:
|
|
// piRtsSrc RTS object that has sent this event
|
|
// pStylusInfo StylusInfo struct (context ID, cursor ID, etc)
|
|
// cPropCountPerPkt number of properties per packet
|
|
// pPacket packet data (layout depends on packet description set)
|
|
// in/out:
|
|
// ppInOutPkt modified packet data (same layout as pPackets)
|
|
// returns:
|
|
// HRESULT error code
|
|
HRESULT CSyncEventHandlerRTS::StylusDown(
|
|
IRealTimeStylus* /* piRtsSrc */,
|
|
const StylusInfo* /* pStylusInfo */,
|
|
ULONG /* cPropCountPerPkt */,
|
|
LONG* /* pPacket */,
|
|
LONG** /* ppInOutPkt */)
|
|
{
|
|
// Get DrawingAttributes of DynamicRenderer
|
|
IInkDrawingAttributes* pDrawingAttributesDynamicRenderer;
|
|
HRESULT hr = g_pDynamicRenderer->get_DrawingAttributes(&pDrawingAttributesDynamicRenderer);
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(SUCCEEDED(hr) && L"CSyncEventHandlerRTS::StylusDown: failed to get RTS's drawing attributes");
|
|
return hr;
|
|
}
|
|
|
|
// Set new stroke color to the DrawingAttributes of the DynamicRenderer
|
|
// If there are no fingers down, this is a primary contact
|
|
hr = pDrawingAttributesDynamicRenderer->put_Color(GetTouchColor(m_nContacts == 0));
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(SUCCEEDED(hr) && L"CSyncEventHandlerRTS::StylusDown: failed to set color");
|
|
pDrawingAttributesDynamicRenderer->Release();
|
|
return hr;
|
|
}
|
|
|
|
pDrawingAttributesDynamicRenderer->Release();
|
|
|
|
++m_nContacts; // Increment finger-down counter
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// Pen-up notification.
|
|
// Decrements finger-down counter.
|
|
// in:
|
|
// piRtsSrc RTS object that has sent this event
|
|
// pStylusInfo StylusInfo struct (context ID, cursor ID, etc)
|
|
// cPropCountPerPkt number of properties per packet
|
|
// pPacket packet data (layout depends on packet description set)
|
|
// in/out:
|
|
// ppInOutPkt modified packet data (same layout as pPackets)
|
|
// returns:
|
|
// HRESULT error code
|
|
HRESULT CSyncEventHandlerRTS::StylusUp(
|
|
IRealTimeStylus* /* piRtsSrc */,
|
|
const StylusInfo* /* pStylusInfo */,
|
|
ULONG /* cPropCountPerPkt */,
|
|
LONG* /* pPacket */,
|
|
LONG** /* ppInOutPkt */)
|
|
{
|
|
--m_nContacts; // Decrement finger-down counter
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// Pen-move notification.
|
|
// In this case, does nothing, but likely to be used in a more complex application.
|
|
// RTS framework does stroke collection and rendering for us.
|
|
// in:
|
|
// piRtsRtp RTS object that has sent this event
|
|
// pStylusInfo StylusInfo struct (context ID, cursor ID, etc)
|
|
// cPktCount number of packets
|
|
// cPktBuffLength pPacket buffer size, in elements, equal to number of packets times number of properties per packet
|
|
// pPackets packet data (layout depends on packet description set)
|
|
// in/out:
|
|
// pcInOutPkts modified number of packets
|
|
// ppInOutPkts modified packet data (same layout as pPackets)
|
|
// returns:
|
|
// HRESULT error code
|
|
HRESULT CSyncEventHandlerRTS::Packets(
|
|
IRealTimeStylus* /* piSrcRtp */,
|
|
const StylusInfo* /* pStylusInfo */,
|
|
ULONG /* cPktCount */,
|
|
ULONG /* cPktBuffLength */,
|
|
LONG* /* pPackets */,
|
|
ULONG* /* pcInOutPkts */,
|
|
LONG** /* ppInOutPkts */)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
// Defines which handlers are called by the framework. We set the flags for pen-down, pen-up and pen-move.
|
|
// in/out:
|
|
// pDataInterest flags that enable/disable notification handlers
|
|
// returns:
|
|
// HRESULT error code
|
|
HRESULT CSyncEventHandlerRTS::DataInterest(RealTimeStylusDataInterest *pDataInterest)
|
|
{
|
|
*pDataInterest = (RealTimeStylusDataInterest)(RTSDI_StylusDown | RTSDI_Packets | RTSDI_StylusUp);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// Increments reference count of the COM object.
|
|
// returns:
|
|
// reference count
|
|
ULONG CSyncEventHandlerRTS::AddRef()
|
|
{
|
|
return InterlockedIncrement(&m_cRefCount);
|
|
}
|
|
|
|
// Decrements reference count of the COM object, and deletes it
|
|
// if there are no more references left.
|
|
// returns:
|
|
// reference count
|
|
ULONG CSyncEventHandlerRTS::Release()
|
|
{
|
|
ULONG cNewRefCount = InterlockedDecrement(&m_cRefCount);
|
|
if (cNewRefCount == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
return cNewRefCount;
|
|
}
|
|
|
|
// Returns a pointer to any interface supported by this object.
|
|
// If IID_IMarshal interface is requested, delegate the call to the aggregated
|
|
// free-threaded marshaller.
|
|
// If a valid pointer is returned, COM object reference count is increased.
|
|
// returns:
|
|
// pointer to the interface requested, or NULL if the interface is not supported by this object
|
|
HRESULT CSyncEventHandlerRTS::QueryInterface(REFIID riid, LPVOID *ppvObj)
|
|
{
|
|
if ((riid == IID_IStylusSyncPlugin) || (riid == IID_IUnknown))
|
|
{
|
|
*ppvObj = this;
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
else if ((riid == IID_IMarshal) && (m_punkFTMarshaller != NULL))
|
|
{
|
|
return m_punkFTMarshaller->QueryInterface(riid, ppvObj);
|
|
}
|
|
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Application framework
|
|
|
|
// Forward declarations of functions included in this code module:
|
|
ATOM MyRegisterClass(HINSTANCE hInstance);
|
|
BOOL InitInstance(HINSTANCE, int);
|
|
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
|
|
|
|
// Win32 application main entry point function.
|
|
// This function is generated by Visual Studio app wizard; added COM
|
|
// initialization and uninitialization.
|
|
// in:
|
|
// hInstance handle of the application instance
|
|
// hPrevInstance not used, always NULL
|
|
// lpCmdLine command line for the application, null-terminated string
|
|
// nCmdShow how to show the window
|
|
int APIENTRY wWinMain(HINSTANCE hInstance,
|
|
HINSTANCE /* hPrevInstance */,
|
|
LPWSTR /* lpCmdLine */,
|
|
int nCmdShow)
|
|
{
|
|
MSG msg;
|
|
|
|
// Initialize COM
|
|
HRESULT hr = CoInitialize(NULL);
|
|
if (FAILED(hr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Initialize global strings
|
|
LoadString(hInstance, IDS_APP_TITLE, g_wszTitle, MAX_LOADSTRING);
|
|
LoadString(hInstance, IDC_MTSCRATCHPADRTSTYLUS, g_wszWindowClass, MAX_LOADSTRING);
|
|
MyRegisterClass(hInstance);
|
|
|
|
// Perform application initialization
|
|
if (!InitInstance (hInstance, nCmdShow))
|
|
{
|
|
// Uninitialize COM
|
|
CoUninitialize();
|
|
return FALSE;
|
|
}
|
|
|
|
// Main message loop
|
|
while (GetMessage(&msg, NULL, 0, 0))
|
|
{
|
|
if (!TranslateAccelerator(msg.hwnd, NULL, &msg))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
|
|
// Uninitialize COM
|
|
CoUninitialize();
|
|
|
|
return (int) msg.wParam;
|
|
}
|
|
|
|
// Registers the window class of the application.
|
|
// This function is generated by Visual Studio app wizard.
|
|
// This function and its usage are only necessary if you want this code
|
|
// to be compatible with Win32 systems prior to the 'RegisterClassEx'
|
|
// function that was added to Windows 95. It is important to call this function
|
|
// so that the application will get 'well formed' small icons associated
|
|
// with it.
|
|
// in:
|
|
// hInstance handle to the instance of the application
|
|
// returns:
|
|
// class atom that uniquely identifies the window class
|
|
ATOM MyRegisterClass(HINSTANCE hInstance)
|
|
{
|
|
WNDCLASSEX wcex;
|
|
|
|
wcex.cbSize = sizeof(WNDCLASSEX);
|
|
|
|
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
|
wcex.lpfnWndProc = WndProc;
|
|
wcex.cbClsExtra = 0;
|
|
wcex.cbWndExtra = 0;
|
|
wcex.hInstance = hInstance;
|
|
wcex.hIcon = 0;
|
|
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
|
|
wcex.lpszMenuName = NULL;
|
|
wcex.lpszClassName = g_wszWindowClass;
|
|
wcex.hIconSm = 0;
|
|
|
|
return RegisterClassEx(&wcex);
|
|
}
|
|
|
|
// Saves instance handle and creates main window
|
|
// This function is generated by Visual Studio app wizard; added RTS object
|
|
// construction and initialization.
|
|
// In this function, we save the instance handle in a global variable and
|
|
// create and display the main program window.
|
|
// in:
|
|
// hInstance handle to the instance of the application
|
|
// nCmdShow how to show the window
|
|
// returns:
|
|
// flag, succeeded or failed to create the window
|
|
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
|
|
{
|
|
HWND hWnd;
|
|
|
|
g_hInst = hInstance; // Store instance handle in our global variable
|
|
|
|
// Create the application window
|
|
hWnd = CreateWindow(g_wszWindowClass, g_wszTitle, WS_OVERLAPPEDWINDOW,
|
|
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
|
|
|
|
if (!hWnd)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Create RTS object
|
|
g_pRealTimeStylus = CreateRealTimeStylus(hWnd);
|
|
if (g_pRealTimeStylus == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Create DynamicRenderer object
|
|
g_pDynamicRenderer = CreateDynamicRenderer(g_pRealTimeStylus);
|
|
if (g_pDynamicRenderer == NULL)
|
|
{
|
|
g_pRealTimeStylus->Release();
|
|
g_pRealTimeStylus = NULL;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Create EventHandler object
|
|
g_pSyncEventHandlerRTS = CSyncEventHandlerRTS::Create(g_pRealTimeStylus);
|
|
if (g_pSyncEventHandlerRTS == NULL)
|
|
{
|
|
g_pRealTimeStylus->Release();
|
|
g_pRealTimeStylus = NULL;
|
|
|
|
g_pDynamicRenderer->Release();
|
|
g_pDynamicRenderer = NULL;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Enable RTS and DynamicRenderer
|
|
if (!EnableRealTimeStylus(g_pRealTimeStylus, g_pDynamicRenderer))
|
|
{
|
|
g_pSyncEventHandlerRTS->Release();
|
|
g_pSyncEventHandlerRTS = NULL;
|
|
|
|
g_pRealTimeStylus->Release();
|
|
g_pRealTimeStylus = NULL;
|
|
|
|
g_pDynamicRenderer->Release();
|
|
g_pDynamicRenderer = NULL;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
ShowWindow(hWnd, nCmdShow);
|
|
UpdateWindow(hWnd);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Processes messages for the main window:
|
|
// WM_COMMAND - process the application menu
|
|
// WM_PAINT - Paint the main window
|
|
// WM_DESTROY - post a quit message and return
|
|
// This function is generated by Visual Studio app wizard; added WM_PAINT
|
|
// and WM_DESTROY code.
|
|
// in:
|
|
// hWnd window handle
|
|
// message message code
|
|
// wParam message parameter (message-specific)
|
|
// lParam message parameter (message-specific)
|
|
// returns:
|
|
// the result of the message processing and depends on the message sent
|
|
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
int wmId;
|
|
PAINTSTRUCT ps;
|
|
HDC hdc;
|
|
|
|
switch (message)
|
|
{
|
|
case WM_COMMAND:
|
|
wmId = LOWORD(wParam);
|
|
// Parse the menu selections:
|
|
switch (wmId)
|
|
{
|
|
case IDM_EXIT:
|
|
DestroyWindow(hWnd);
|
|
break;
|
|
default:
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
}
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
hdc = BeginPaint(hWnd, &ps);
|
|
OnPaintHandler(g_pDynamicRenderer);
|
|
UNREFERENCED_PARAMETER(hdc);
|
|
EndPaint(hWnd, &ps);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
// Release COM objects before CoUninitialize
|
|
g_pDynamicRenderer->Release();
|
|
g_pDynamicRenderer = NULL;
|
|
|
|
g_pRealTimeStylus->Release();
|
|
g_pRealTimeStylus = NULL;
|
|
|
|
g_pSyncEventHandlerRTS->Release();
|
|
g_pSyncEventHandlerRTS = NULL;
|
|
|
|
PostQuitMessage(0);
|
|
break;
|
|
|
|
default:
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
}
|
|
return 0;
|
|
}
|