/* 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);
[[nodiscard]]
static bool IsMSChangJieOrMSQuickActive();
[[nodiscard]]
static bool IsMSPinyinOrMSWubiActive();
[[nodiscard]]
static bool IsMSJapaneseIMEActive();
[[nodiscard]]
static bool IsGoogleJapaneseInputActive();
[[nodiscard]]
static bool IsATOKActive();
[[nodiscard]]
static bool IsSogouActive();
[[nodiscard]]
static bool IsWeChatIMEActive();
// Note that ATOK 2011 - 2016 refers native caret position for deciding its
// popup window position.
[[nodiscard]]
static bool IsATOKReferringNativeCaretActive();
private:
static void EnsureInstance() {
if (!sInstance) {
RefPtr<TSFStaticSink> staticSink = GetInstance();
(
void)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