Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef TSFStaticSink_h
#define TSFStaticSink_h
#include <msctf.h>
#include <windows.h>
#include <winuser.h>
#include "TSFTextInputProcessorList.h"
#include "WinUtils.h"
#include "mozilla/RefPtr.h"
#include "mozilla/StaticPtr.h"
namespace mozilla::widget {
class TSFStaticSink final : public ITfInputProcessorProfileActivationSink {
public:
static TSFStaticSink* GetInstance();
static void Shutdown() {
if (sInstance) {
sInstance->Destroy();
sInstance = nullptr;
}
}
bool Init(ITfThreadMgr* aThreadMgr,
ITfInputProcessorProfiles* aInputProcessorProfiles);
STDMETHODIMP QueryInterface(REFIID riid, void** ppv) {
*ppv = nullptr;
if (IID_IUnknown == riid ||
IID_ITfInputProcessorProfileActivationSink == riid) {
*ppv = static_cast<ITfInputProcessorProfileActivationSink*>(this);
}
if (*ppv) {
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
NS_INLINE_DECL_IUNKNOWN_REFCOUNTING(TSFStaticSink)
[[nodiscard]] const nsString& GetActiveTIPKeyboardDescription() const {
return mActiveTIPKeyboardDescription;
}
[[nodiscard]] static bool IsIMM_IMEActive() {
// Use IMM API until TSFStaticSink starts to work.
if (!sInstance || !sInstance->EnsureInitActiveTIPKeyboard()) {
return IsIMM_IME(::GetKeyboardLayout(0));
}
return sInstance->mIsIMM_IME;
}
[[nodiscard]] static bool IsIMM_IME(HKL aHKL) {
return (::ImmGetIMEFileNameW(aHKL, nullptr, 0) > 0);
}
[[nodiscard]] static bool IsTraditionalChinese() {
EnsureInstance();
return sInstance && sInstance->IsTraditionalChineseInternal();
}
[[nodiscard]] static bool IsSimplifiedChinese() {
EnsureInstance();
return sInstance && sInstance->IsSimplifiedChineseInternal();
}
[[nodiscard]] static bool IsJapanese() {
EnsureInstance();
return sInstance && sInstance->IsJapaneseInternal();
}
[[nodiscard]] static bool IsKorean() {
EnsureInstance();
return sInstance && sInstance->IsKoreanInternal();
}
/**
* ActiveTIP() returns an ID for currently active TIP.
* Please note that this method is expensive due to needs a lot of GUID
* comparisons if active language ID is one of CJKT. If you need to
* check TIPs for a specific language, you should check current language
* first.
*/
[[nodiscard]] static TextInputProcessorID ActiveTIP() {
EnsureInstance();
if (!sInstance || !sInstance->EnsureInitActiveTIPKeyboard()) {
return TextInputProcessorID::Unknown;
}
sInstance->ComputeActiveTextInputProcessor();
if (NS_WARN_IF(sInstance->mActiveTIP ==
TextInputProcessorID::NotComputed)) {
return TextInputProcessorID::Unknown;
}
return sInstance->mActiveTIP;
}
static bool GetActiveTIPNameForTelemetry(nsAString& aName);
static bool IsMSChangJieOrMSQuickActive();
static bool IsMSPinyinOrMSWubiActive();
static bool IsMSJapaneseIMEActive();
static bool IsGoogleJapaneseInputActive();
static bool IsATOKActive();
// Note that ATOK 2011 - 2016 refers native caret position for deciding its
// popup window position.
static bool IsATOKReferringNativeCaretActive();
private:
static void EnsureInstance() {
if (!sInstance) {
RefPtr<TSFStaticSink> staticSink = GetInstance();
Unused << staticSink;
}
}
[[nodiscard]] bool IsTraditionalChineseInternal() const {
return mLangID == 0x0404;
}
[[nodiscard]] bool IsSimplifiedChineseInternal() const {
return mLangID == 0x0804;
}
[[nodiscard]] bool IsJapaneseInternal() const { return mLangID == 0x0411; }
[[nodiscard]] bool IsKoreanInternal() const { return mLangID == 0x0412; }
[[nodiscard]] bool IsATOKActiveInternal();
void ComputeActiveTextInputProcessor();
[[nodiscard]] TextInputProcessorID ComputeActiveTIPAsJapanese();
[[nodiscard]] TextInputProcessorID ComputeActiveTIPAsTraditionalChinese();
[[nodiscard]] TextInputProcessorID ComputeActiveTIPAsSimplifiedChinese();
[[nodiscard]] TextInputProcessorID ComputeActiveTIPAsKorean();
public: // ITfInputProcessorProfileActivationSink
STDMETHODIMP OnActivated(DWORD, LANGID, REFCLSID, REFGUID, REFGUID, HKL,
DWORD);
private:
TSFStaticSink() = default;
virtual ~TSFStaticSink() = default;
bool EnsureInitActiveTIPKeyboard();
void Destroy();
void GetTIPDescription(REFCLSID aTextService, LANGID aLangID,
REFGUID aProfile, nsAString& aDescription);
[[nodiscard]] bool IsTIPCategoryKeyboard(REFCLSID aTextService,
LANGID aLangID, REFGUID aProfile);
TextInputProcessorID mActiveTIP = TextInputProcessorID::NotComputed;
// Cookie of installing ITfInputProcessorProfileActivationSink
DWORD mIPProfileCookie = TF_INVALID_COOKIE;
LANGID mLangID = 0;
// True if current IME is implemented with IMM.
bool mIsIMM_IME = false;
// True if OnActivated() is already called
bool mOnActivatedCalled = false;
RefPtr<ITfThreadMgr> mThreadMgr;
RefPtr<ITfInputProcessorProfiles> mInputProcessorProfiles;
// Active TIP keyboard's description. If active language profile isn't TIP,
// i.e., IMM-IME or just a keyboard layout, this is empty.
nsString mActiveTIPKeyboardDescription;
// Active TIP's GUID and CLSID
GUID mActiveTIPGUID = GUID_NULL;
CLSID mActiveTIPCLSID = CLSID_NULL;
static StaticRefPtr<TSFStaticSink> sInstance;
};
} // namespace mozilla::widget
#endif // #ifndef TSFStaticSink_h