зеркало из https://github.com/mozilla/gecko-dev.git
246 строки
8.4 KiB
C++
246 строки
8.4 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* vim: set ts=2 sw=2 et tw=78:
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include "InkCollector.h"
|
|
|
|
// Msinkaut_i.c and Msinkaut.h should both be included
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms695519.aspx
|
|
#include <msinkaut_i.c>
|
|
|
|
StaticAutoPtr<InkCollector> InkCollector::sInkCollector;
|
|
|
|
InkCollector::~InkCollector() {
|
|
Shutdown();
|
|
MOZ_ASSERT(!mCookie && !mEnabled && !mComInitialized && !mMarshaller &&
|
|
!mInkCollector && !mConnectionPoint && !mInkCollectorEvent);
|
|
}
|
|
|
|
void InkCollector::Initialize() {
|
|
// Possibly, we can use mConnectionPoint for checking,
|
|
// But if errors exist (perhaps COM object is unavailable),
|
|
// Initialize() will be called more times.
|
|
static bool sInkCollectorCreated = false;
|
|
if (sInkCollectorCreated) {
|
|
return;
|
|
}
|
|
sInkCollectorCreated = true;
|
|
|
|
// COM could get uninitialized due to previous initialization.
|
|
mComInitialized = SUCCEEDED(::CoInitialize(nullptr));
|
|
|
|
// Set up instance of InkCollectorEvent.
|
|
mInkCollectorEvent = new InkCollectorEvent();
|
|
|
|
// Set up a free threaded marshaler.
|
|
if (FAILED(::CoCreateFreeThreadedMarshaler(mInkCollectorEvent,
|
|
getter_AddRefs(mMarshaller)))) {
|
|
return;
|
|
}
|
|
|
|
// Create the ink collector.
|
|
if (FAILED(::CoCreateInstance(CLSID_InkCollector, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_IInkCollector,
|
|
getter_AddRefs(mInkCollector)))) {
|
|
return;
|
|
}
|
|
|
|
// Set up connection between sink and InkCollector.
|
|
RefPtr<IConnectionPointContainer> connPointContainer;
|
|
|
|
// Get the connection point container.
|
|
if (SUCCEEDED(mInkCollector->QueryInterface(
|
|
IID_IConnectionPointContainer, getter_AddRefs(connPointContainer)))) {
|
|
// Find the connection point for Ink Collector events.
|
|
if (SUCCEEDED(connPointContainer->FindConnectionPoint(
|
|
__uuidof(_IInkCollectorEvents),
|
|
getter_AddRefs(mConnectionPoint)))) {
|
|
// Hook up sink to connection point.
|
|
if (SUCCEEDED(mConnectionPoint->Advise(mInkCollectorEvent, &mCookie))) {
|
|
OnInitialize();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void InkCollector::Shutdown() {
|
|
Enable(false);
|
|
if (mConnectionPoint) {
|
|
// Remove the connection of the sink to the Ink Collector.
|
|
mConnectionPoint->Unadvise(mCookie);
|
|
mCookie = 0;
|
|
mConnectionPoint = nullptr;
|
|
}
|
|
mInkCollector = nullptr;
|
|
mMarshaller = nullptr;
|
|
mInkCollectorEvent = nullptr;
|
|
|
|
// Let uninitialization get handled in a place where it got inited.
|
|
if (mComInitialized) {
|
|
CoUninitialize();
|
|
mComInitialized = false;
|
|
}
|
|
}
|
|
|
|
void InkCollector::OnInitialize() {
|
|
// Suppress all events to do not allow performance decreasing.
|
|
// https://msdn.microsoft.com/en-us/library/ms820347.aspx
|
|
mInkCollector->SetEventInterest(InkCollectorEventInterest::ICEI_AllEvents,
|
|
VARIANT_FALSE);
|
|
|
|
// Sets a value that indicates whether an object or control has interest in a
|
|
// specified event.
|
|
mInkCollector->SetEventInterest(
|
|
InkCollectorEventInterest::ICEI_CursorOutOfRange, VARIANT_TRUE);
|
|
|
|
// If the MousePointer property is set to IMP_Custom and the MouseIcon
|
|
// property is NULL, Then the ink collector no longer handles mouse cursor
|
|
// settings.
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms700686.aspx
|
|
mInkCollector->put_MouseIcon(nullptr);
|
|
mInkCollector->put_MousePointer(InkMousePointer::IMP_Custom);
|
|
|
|
// This mode allows an ink collector to collect ink from any tablet attached
|
|
// to the Tablet PC. The Boolean value that indicates whether to use the mouse
|
|
// as an input device. If TRUE, the mouse is used for input.
|
|
// https://msdn.microsoft.com/en-us/library/ms820346.aspx
|
|
mInkCollector->SetAllTabletsMode(VARIANT_FALSE);
|
|
|
|
// Sets the value that specifies whether ink is rendered as it is drawn.
|
|
// VARIANT_TRUE to render ink as it is drawn on the display.
|
|
// VARIANT_FALSE to not have the ink appear on the display as strokes are
|
|
// made.
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd314598.aspx
|
|
mInkCollector->put_DynamicRendering(VARIANT_FALSE);
|
|
|
|
// Set AutoRedraw to false to prevent repainting the ink when the window is
|
|
// invalidated.
|
|
mInkCollector->put_AutoRedraw(VARIANT_FALSE);
|
|
}
|
|
|
|
// Sets a value that specifies whether the InkCollector object collects pen
|
|
// input. This property must be set to FALSE before setting or calling specific
|
|
// properties and methods of the object.
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms701721.aspx
|
|
void InkCollector::Enable(bool aNewState) {
|
|
if (aNewState != mEnabled) {
|
|
if (mInkCollector) {
|
|
if (SUCCEEDED(mInkCollector->put_Enabled(aNewState ? VARIANT_TRUE
|
|
: VARIANT_FALSE))) {
|
|
mEnabled = aNewState;
|
|
} else {
|
|
NS_WARNING("InkCollector did not change status successfully");
|
|
}
|
|
} else {
|
|
NS_WARNING("InkCollector should be exist");
|
|
}
|
|
}
|
|
}
|
|
|
|
HWND InkCollector::GetTarget() { return mTargetWindow; }
|
|
|
|
void InkCollector::SetTarget(HWND aTargetWindow) {
|
|
NS_ASSERTION(aTargetWindow, "aTargetWindow should be exist");
|
|
if (aTargetWindow && (aTargetWindow != mTargetWindow)) {
|
|
Initialize();
|
|
if (mInkCollector) {
|
|
Enable(false);
|
|
if (SUCCEEDED(mInkCollector->put_hWnd((LONG_PTR)aTargetWindow))) {
|
|
mTargetWindow = aTargetWindow;
|
|
} else {
|
|
NS_WARNING("InkCollector did not change window property successfully");
|
|
}
|
|
Enable(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void InkCollector::ClearTarget() {
|
|
if (mTargetWindow && mInkCollector) {
|
|
Enable(false);
|
|
if (SUCCEEDED(mInkCollector->put_hWnd(0))) {
|
|
mTargetWindow = 0;
|
|
} else {
|
|
NS_WARNING("InkCollector did not clear window property successfully");
|
|
}
|
|
}
|
|
}
|
|
|
|
uint16_t InkCollector::GetPointerId() { return mPointerId; }
|
|
|
|
void InkCollector::SetPointerId(uint16_t aPointerId) {
|
|
mPointerId = aPointerId;
|
|
}
|
|
|
|
void InkCollector::ClearPointerId() { mPointerId = 0; }
|
|
|
|
// The display and the digitizer have quite different properties.
|
|
// The display has CursorMustTouch, the mouse pointer alway touches the display
|
|
// surface. The digitizer lists Integrated and HardProximity. When the stylus is
|
|
// in the proximity of the tablet its movements are also detected. An external
|
|
// tablet will only list HardProximity.
|
|
bool InkCollectorEvent::IsHardProximityTablet(IInkTablet* aTablet) const {
|
|
if (aTablet) {
|
|
TabletHardwareCapabilities caps;
|
|
if (SUCCEEDED(aTablet->get_HardwareCapabilities(&caps))) {
|
|
return (TabletHardwareCapabilities::THWC_HardProximity & caps);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
HRESULT __stdcall InkCollectorEvent::QueryInterface(REFIID aRiid,
|
|
void** aObject) {
|
|
// Validate the input
|
|
if (!aObject) {
|
|
return E_POINTER;
|
|
}
|
|
HRESULT result = E_NOINTERFACE;
|
|
// This object supports IUnknown/IDispatch/IInkCollectorEvents
|
|
if ((IID_IUnknown == aRiid) || (IID_IDispatch == aRiid) ||
|
|
(DIID__IInkCollectorEvents == aRiid)) {
|
|
*aObject = this;
|
|
// AddRef should be called when we give info about interface
|
|
NS_ADDREF_THIS();
|
|
result = S_OK;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
HRESULT InkCollectorEvent::Invoke(DISPID aDispIdMember, REFIID /*aRiid*/,
|
|
LCID /*aId*/, WORD /*wFlags*/,
|
|
DISPPARAMS* aDispParams,
|
|
VARIANT* /*aVarResult*/,
|
|
EXCEPINFO* /*aExcepInfo*/,
|
|
UINT* /*aArgErr*/) {
|
|
switch (aDispIdMember) {
|
|
case DISPID_ICECursorOutOfRange: {
|
|
if (aDispParams && aDispParams->cArgs) {
|
|
CursorOutOfRange(
|
|
static_cast<IInkCursor*>(aDispParams->rgvarg[0].pdispVal));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
void InkCollectorEvent::CursorOutOfRange(IInkCursor* aCursor) const {
|
|
IInkTablet* curTablet = nullptr;
|
|
if (FAILED(aCursor->get_Tablet(&curTablet))) {
|
|
return;
|
|
}
|
|
// All events should be suppressed except
|
|
// from tablets with hard proximity.
|
|
if (!IsHardProximityTablet(curTablet)) {
|
|
return;
|
|
}
|
|
// Notify current target window.
|
|
if (HWND targetWindow = InkCollector::sInkCollector->GetTarget()) {
|
|
::SendMessage(targetWindow, MOZ_WM_PEN_LEAVES_HOVER_OF_DIGITIZER, 0, 0);
|
|
}
|
|
}
|