summaryrefslogtreecommitdiffstats
path: root/widget/windows/InkCollector.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/windows/InkCollector.cpp')
-rw-r--r--widget/windows/InkCollector.cpp247
1 files changed, 247 insertions, 0 deletions
diff --git a/widget/windows/InkCollector.cpp b/widget/windows/InkCollector.cpp
new file mode 100644
index 0000000000..2fcd2ea77d
--- /dev/null
+++ b/widget/windows/InkCollector.cpp
@@ -0,0 +1,247 @@
+/* -*- 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"
+#include "nsDebug.h"
+#include "nsISupportsUtils.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>
+
+mozilla::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);
+ }
+}