From 4978bd1ac36279a7f793fa1dbcc8edceb77e548a Mon Sep 17 00:00:00 2001 From: Kan-Ru Chen Date: Sat, 9 Nov 2024 09:26:24 +0900 Subject: [PATCH 1/2] chore: remove unused ImeEngine files --- libIME/ImeEngine.cpp | 531 ------------------------------------------- libIME/ImeEngine.h | 161 ------------- 2 files changed, 692 deletions(-) delete mode 100644 libIME/ImeEngine.cpp delete mode 100644 libIME/ImeEngine.h diff --git a/libIME/ImeEngine.cpp b/libIME/ImeEngine.cpp deleted file mode 100644 index 27bcbf7..0000000 --- a/libIME/ImeEngine.cpp +++ /dev/null @@ -1,531 +0,0 @@ -#include "ImeEngine.h" -#include "EditSession.h" -#include "LangBarButton.h" -#include "DisplayAttributeInfoEnum.h" -#include "ImeModule.h" - -#include -#include -#include - -using namespace std; - -namespace Ime { - -ImeEngine::ImeEngine(ImeModule* module): - module_(module), - threadMgr_(NULL), - clientId_(TF_CLIENTID_NULL), - activateFlags_(0), - isKeyboardOpened_(false), - threadMgrEventSinkCookie_(TF_INVALID_COOKIE), - textEditSinkCookie_(TF_INVALID_COOKIE), - compositionSinkCookie_(TF_INVALID_COOKIE), - keyboardOpenEventSinkCookie_(TF_INVALID_COOKIE), - globalCompartmentEventSinkCookie_(TF_INVALID_COOKIE), - langBarSinkCookie_(TF_INVALID_COOKIE), - composition_(NULL) { - - addCompartmentMonitor(GUID_COMPARTMENT_KEYBOARD_OPENCLOSE, false); -} - -ImeEngine::~ImeEngine() { - - // This should only happen in rare cases - if(!compartmentMonitors_.empty()) { - vector::iterator it; - for(it = compartmentMonitors_.begin(); it != compartmentMonitors_.end(); ++it) { - ComQIPtr source; - if(it->isGlobal) - source = globalCompartment(it->guid); - else - source = threadCompartment(it->guid); - source->UnadviseSink(it->cookie); - } - } - - if(!langBarButtons_.empty()) { - for(vector::iterator it = langBarButtons_.begin(); it != langBarButtons_.end(); ++it) { - LangBarButton* button = *it; - button->Release(); - } - } - if(langBarMgr_) { - langBarMgr_->UnadviseEventSink(langBarSinkCookie_); - } - langBarMgr_ = NULL; -} - - -// public methods - -ImeModule* ImeEngine::imeModule() const { - return module_; -} - -ITfThreadMgr* ImeEngine::threadMgr() const { - return threadMgr_; -} - -TfClientId ImeEngine::clientId() const { - return clientId_; -} - - -// language bar -DWORD ImeEngine::langBarStatus() const { - if(langBarMgr_) { - DWORD status; - if(langBarMgr_->GetShowFloatingStatus(&status) == S_OK) { - return status; - } - } - return 0; -} - -void ImeEngine::addButton(LangBarButton* button) { - if(button) { - langBarButtons_.push_back(button); - button->AddRef(); - if(isActivated()) { - ITfLangBarItemMgr* langBarItemMgr; - if(threadMgr_->QueryInterface(IID_ITfLangBarItemMgr, (void**)&langBarItemMgr) == S_OK) { - langBarItemMgr->AddItem(button); - langBarItemMgr->Release(); - } - } - } -} - -void ImeEngine::removeButton(LangBarButton* button) { - if(button) { - vector::iterator it; - it = find(langBarButtons_.begin(), langBarButtons_.end(), button); - if(it != langBarButtons_.end()) { - if(isActivated()) { - ITfLangBarItemMgr* langBarItemMgr; - if(threadMgr_->QueryInterface(IID_ITfLangBarItemMgr, (void**)&langBarItemMgr) == S_OK) { - langBarItemMgr->RemoveItem(button); - langBarItemMgr->Release(); - } - } - button->Release(); - langBarButtons_.erase(it); - } - } -} - -// preserved key -void ImeEngine::addPreservedKey(UINT keyCode, UINT modifiers, const GUID& guid) { - PreservedKey preservedKey; - preservedKey.guid = guid; - preservedKey.uVKey = keyCode; - preservedKey.uModifiers = modifiers; - preservedKeys_.push_back(preservedKey); - if(threadMgr_) { // our text service is activated - ITfKeystrokeMgr *keystrokeMgr; - if (threadMgr_->QueryInterface(IID_ITfKeystrokeMgr, (void **)&keystrokeMgr) == S_OK) { - keystrokeMgr->PreserveKey(clientId_, guid, &preservedKey, NULL, 0); - keystrokeMgr->Release(); - } - } -} - -void ImeEngine::removePreservedKey(const GUID& guid) { - vector::iterator it; - for(it = preservedKeys_.begin(); it != preservedKeys_.end(); ++it) { - PreservedKey& preservedKey = *it; - if(::IsEqualIID(preservedKey.guid, guid)) { - if(threadMgr_) { // our text service is activated - ITfKeystrokeMgr *keystrokeMgr; - if (threadMgr_->QueryInterface(IID_ITfKeystrokeMgr, (void **)&keystrokeMgr) == S_OK) { - keystrokeMgr->UnpreserveKey(preservedKey.guid, &preservedKey); - keystrokeMgr->Release(); - } - } - preservedKeys_.erase(it); - break; - } - } -} - - -// text composition - -bool ImeEngine::isComposing() { - return (composition_ != NULL); -} - -// is keyboard disabled for the context (NULL means current context) -bool ImeEngine::isKeyboardDisabled(ITfContext* context) { - return (contextCompartmentValue(GUID_COMPARTMENT_KEYBOARD_DISABLED, context) - || contextCompartmentValue(GUID_COMPARTMENT_EMPTYCONTEXT, context)); -} - -// is keyboard opened for the whole thread -bool ImeEngine::isKeyboardOpened() { - return isKeyboardOpened_; -} - -void ImeEngine::setKeyboardOpen(bool open) { - if(open != isKeyboardOpened_) { - setThreadCompartmentValue(GUID_COMPARTMENT_KEYBOARD_OPENCLOSE, (DWORD)open); - } -} - - -// check if current insertion point is in the range of composition. -// if not in range, insertion is now allowed -bool ImeEngine::isInsertionAllowed(EditSession* session) { - TfEditCookie cookie = session->editCookie(); - TF_SELECTION selection; - ULONG selectionNum; - if(isComposing()) { - if(session->context()->GetSelection(cookie, TF_DEFAULT_SELECTION, 1, &selection, &selectionNum) == S_OK) { - ITfRange* compositionRange; - if(composition_->GetRange(&compositionRange) == S_OK) { - bool allowed = false; - // check if current selection is covered by composition range - LONG compareResult1; - LONG compareResult2; - if(selection.range->CompareStart(cookie, compositionRange, TF_ANCHOR_START, &compareResult1) == S_OK - && selection.range->CompareStart(cookie, compositionRange, TF_ANCHOR_END, &compareResult2) == S_OK) { - if(compareResult1 == -1 && compareResult2 == +1) - allowed = true; - } - compositionRange->Release(); - } - if(selection.range) - selection.range->Release(); - } - } - return false; -} - -void ImeEngine::startComposition(ITfContext* context) { - assert(context); - HRESULT sessionResult; - StartCompositionEditSession* session = new StartCompositionEditSession(this, context); - context->RequestEditSession(clientId_, session, TF_ES_SYNC|TF_ES_READWRITE, &sessionResult); - session->Release(); -} - -void ImeEngine::endComposition(ITfContext* context) { - assert(context); - HRESULT sessionResult; - EndCompositionEditSession* session = new EndCompositionEditSession(this, context); - context->RequestEditSession(clientId_, session, TF_ES_SYNC|TF_ES_READWRITE, &sessionResult); - session->Release(); -} - -void ImeEngine::setCompositionString(EditSession* session, const wchar_t* str, int len) { - ITfContext* context = session->context(); - if(context) { - TfEditCookie editCookie = session->editCookie(); - TF_SELECTION selection; - ULONG selectionNum; - // get current selection/insertion point - if(context->GetSelection(editCookie, TF_DEFAULT_SELECTION, 1, &selection, &selectionNum) == S_OK) { - ComPtr compositionRange; - if(composition_->GetRange(&compositionRange) == S_OK) { - bool selPosInComposition = true; - // if current insertion point is not covered by composition, we cannot insert text here. - if(selPosInComposition) { - // replace context of composion area with the new string. - compositionRange->SetText(editCookie, TF_ST_CORRECTION, str, len); - - // move the insertion point to end of the composition string - selection.range->Collapse(editCookie, TF_ANCHOR_END); - context->SetSelection(editCookie, 1, &selection); - } - - // set display attribute to the composition range - ComPtr dispAttrProp; - if(context->GetProperty(GUID_PROP_ATTRIBUTE, &dispAttrProp) == S_OK) { - VARIANT val; - val.vt = VT_I4; - val.lVal = module_->inputAttrib()->atom(); - dispAttrProp->SetValue(editCookie, compositionRange, &val); - } - } - selection.range->Release(); - } - } -} - -// set cursor position in the composition area -// 0 means the start pos of composition string -void ImeEngine::setCompositionCursor(EditSession* session, int pos) { - TF_SELECTION selection; - ULONG selectionNum; - // get current selection - if(session->context()->GetSelection(session->editCookie(), TF_DEFAULT_SELECTION, 1, &selection, &selectionNum) == S_OK) { - // get composition range - ITfRange* compositionRange; - if(composition_->GetRange(&compositionRange) == S_OK) { - // make the start of selectionRange the same as that of compositionRange - selection.range->ShiftStartToRange(session->editCookie(), compositionRange, TF_ANCHOR_START); - selection.range->Collapse(session->editCookie(), TF_ANCHOR_START); - LONG moved; - // move the start anchor to right - selection.range->ShiftStart(session->editCookie(), (LONG)pos, &moved, NULL); - selection.range->Collapse(session->editCookie(), TF_ANCHOR_START); - // set the new selection to the context - session->context()->SetSelection(session->editCookie(), 1, &selection); - compositionRange->Release(); - } - selection.range->Release(); - } -} - -// compartment handling -ComPtr ImeEngine::globalCompartment(const GUID& key) { - if(threadMgr_) { - ComQIPtr compartmentMgr; - if(threadMgr_->GetGlobalCompartment(&compartmentMgr) == S_OK) { - ComPtr compartment; - compartmentMgr->GetCompartment(key, &compartment); - return compartment; - } - } - return NULL; -} - -ComPtr ImeEngine::threadCompartment(const GUID& key) { - if(threadMgr_) { - ComQIPtr compartmentMgr = threadMgr_; - if(compartmentMgr) { - ComPtr compartment; - compartmentMgr->GetCompartment(key, &compartment); - return compartment; - } - } - return NULL; -} - -ComPtr ImeEngine::contextCompartment(const GUID& key, ITfContext* context) { - ITfContext* curContext = NULL; - if(!context) { - curContext = currentContext(); - context = curContext; - } - if(context) { - ComQIPtr compartmentMgr = context; - if(compartmentMgr) { - ComPtr compartment; - compartmentMgr->GetCompartment(key, &compartment); - return compartment; - } - } - if(curContext) - curContext->Release(); - return NULL; -} - - -DWORD ImeEngine::globalCompartmentValue(const GUID& key) { - ComPtr compartment = globalCompartment(key); - if(compartment) { - VARIANT var; - if(compartment->GetValue(&var) == S_OK && var.vt == VT_I4) { - return (DWORD)var.lVal; - } - } - return 0; -} - -DWORD ImeEngine::threadCompartmentValue(const GUID& key) { - ComPtr compartment = threadCompartment(key); - if(compartment) { - VARIANT var; - ::VariantInit(&var); - HRESULT r = compartment->GetValue(&var); - if(r == S_OK) { - if(var.vt == VT_I4) - return (DWORD)var.lVal; - } - } - return 0; -} - -DWORD ImeEngine::contextCompartmentValue(const GUID& key, ITfContext* context) { - ComPtr compartment = contextCompartment(key, context); - if(compartment) { - VARIANT var; - if(compartment->GetValue(&var) == S_OK && var.vt == VT_I4) { - return (DWORD)var.lVal; - } - } - return 0; -} - -void ImeEngine::setGlobalCompartmentValue(const GUID& key, DWORD value) { - if(threadMgr_) { - ComPtr compartment = globalCompartment(key); - if(compartment) { - VARIANT var; - ::VariantInit(&var); - var.vt = VT_I4; - var.lVal = value; - compartment->SetValue(clientId_, &var); - } - } - else { - // if we don't have a thread manager (this is possible when we try to set - // a global compartment value while the text service is not activated) - ComPtr threadMgr; - if(::CoCreateInstance(CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, (void**)&threadMgr) == S_OK) { - if(threadMgr) { - ComPtr compartmentMgr; - if(threadMgr->GetGlobalCompartment(&compartmentMgr) == S_OK) { - ComPtr compartment; - if(compartmentMgr->GetCompartment(key, &compartment) == S_OK && compartment) { - TfClientId id; - if(threadMgr->Activate(&id) == S_OK) { - VARIANT var; - ::VariantInit(&var); - var.vt = VT_I4; - var.lVal = value; - compartment->SetValue(id, &var); - threadMgr->Deactivate(); - } - } - } - } - } - } -} - -void ImeEngine::setThreadCompartmentValue(const GUID& key, DWORD value) { - ComPtr compartment = threadCompartment(key); - if(compartment) { - VARIANT var; - ::VariantInit(&var); - var.vt = VT_I4; - var.lVal = value; - compartment->SetValue(clientId_, &var); - } -} - -void ImeEngine::setContextCompartmentValue(const GUID& key, DWORD value, ITfContext* context) { - ComPtr compartment = contextCompartment(key, context); - if(compartment) { - VARIANT var; - ::VariantInit(&var); - var.vt = VT_I4; - var.lVal = value; - compartment->SetValue(clientId_, &var); - } -} - - -void ImeEngine::addCompartmentMonitor(const GUID key, bool isGlobal) { - CompartmentMonitor monitor; - monitor.guid = key; - monitor.cookie = 0; - monitor.isGlobal = isGlobal; - // if the text service is activated - if(threadMgr_) { - ComQIPtr source; - if(isGlobal) - source = globalCompartment(key); - else - source = threadCompartment(key); - if(source) { - source->AdviseSink(IID_ITfCompartmentEventSink, (ITfCompartmentEventSink*)this, &monitor.cookie); - } - } - compartmentMonitors_.push_back(monitor); -} - -void ImeEngine::removeCompartmentMonitor(const GUID key) { - vector::iterator it; - it = find(compartmentMonitors_.begin(), compartmentMonitors_.end(), key); - if(it != compartmentMonitors_.end()) { - if(threadMgr_) { - ComQIPtr source; - if(it->isGlobal) - source = globalCompartment(key); - else - source = threadCompartment(key); - source->UnadviseSink(it->cookie); - } - compartmentMonitors_.erase(it); - } -} - - -// virtual -void ImeEngine::onActivate() { -} - -// virtual -void ImeEngine::onDeactivate() { -} - -// virtual -bool ImeEngine::filterKeyDown(KeyEvent& keyEvent) { - return false; -} - -// virtual -bool ImeEngine::onKeyDown(KeyEvent& keyEvent, EditSession* session) { - return false; -} - -// virtual -bool ImeEngine::filterKeyUp(KeyEvent& keyEvent) { - return false; -} - -// virtual -bool ImeEngine::onKeyUp(KeyEvent& keyEvent, EditSession* session) { - return false; -} - -// virtual -bool ImeEngine::onPreservedKey(const GUID& guid) { - return false; -} - -// virtual -void ImeEngine::onSetFocus() { -} - -// virtual -void ImeEngine::onKillFocus() { -} - -bool ImeEngine::onCommand(UINT id, CommandType type) { - return false; -} - -// virtual -void ImeEngine::onCompartmentChanged(const GUID& key) { - // keyboard status changed, this is threadMgr specific - // See explanations on TSF aware blog: - // http://blogs.msdn.com/b/tsfaware/archive/2007/05/30/what-is-a-keyboard.aspx - if(::IsEqualGUID(key, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE)) { - isKeyboardOpened_ = threadCompartmentValue(key) ? true : false; - onKeyboardStatusChanged(isKeyboardOpened_); - } -} - -// virtual -void ImeEngine::onLangBarStatusChanged(int newStatus) { -} - -// called when the keyboard is opened or closed -// virtual -void ImeEngine::onKeyboardStatusChanged(bool opened) { -} - -// called just before current composition is terminated for doing cleanup. -// if forced is true, the composition is terminated by others, such as -// the input focus is grabbed by another application. -// if forced is false, the composition is terminated gracefully by endComposition(). -// virtual -void ImeEngine::onCompositionTerminated(bool forced) { -} - -} diff --git a/libIME/ImeEngine.h b/libIME/ImeEngine.h deleted file mode 100644 index 098b86a..0000000 --- a/libIME/ImeEngine.h +++ /dev/null @@ -1,161 +0,0 @@ -#ifndef IME_IME_ENGINE_H -#define IME_IME_ENGINE_H - -#include "libIME.h" -#include -#include "EditSession.h" -#include "KeyEvent.h" - -#include -#include - -#include -#include - -// for Windows 8 support -#ifndef TF_TMF_IMMERSIVEMODE // this is defined in Win 8 SDK -#define TF_TMF_IMMERSIVEMODE 0x40000000 -#endif - -namespace Ime { - -class ImeModule; -class LangBarButton; - -class ImeEngine { -public: - enum CommandType { // used in onCommand() - COMMAND_LEFT_CLICK, - COMMAND_RIGHT_CLICK, - COMMAND_MENU - }; - - ImeEngine(ImeModule* module); - virtual ~ImeEngine(); - - // public methods - ImeModule* imeModule() const; - - ITfThreadMgr* threadMgr() const; - - TfClientId clientId() const; - - ITfContext* currentContext(); - - bool isActivated() const { - return (threadMgr() != NULL); - } - - DWORD activateFlags() const { - return activateFlags_; - } - - // running in Windows 8 app mode - bool isImmersive() const { - return (activateFlags_ & TF_TMF_IMMERSIVEMODE) != 0; - } - - DWORD langBarStatus() const; - - // language bar buttons - void addButton(LangBarButton* button); - void removeButton(LangBarButton* button); - - // preserved keys - void addPreservedKey(UINT keyCode, UINT modifiers, const GUID& guid); - void removePreservedKey(const GUID& guid); - - // text composition handling - bool isComposing(); - - // is keyboard disabled for the context (NULL means current context) - bool isKeyboardDisabled(ITfContext* context = NULL); - - // is keyboard opened for the whole thread - bool isKeyboardOpened(); - void setKeyboardOpen(bool open); - - bool isInsertionAllowed(EditSession* session); - void startComposition(ITfContext* context); - void endComposition(ITfContext* context); - bool compositionRect(EditSession* session, RECT* rect); - bool selectionRect(EditSession* session, RECT* rect); - HWND compositionWindow(EditSession* session); - - void setCompositionString(EditSession* session, const wchar_t* str, int len); - void setCompositionCursor(EditSession* session, int pos); - - // compartment handling - winrt::com_ptr globalCompartment(const GUID& key); - winrt::com_ptr threadCompartment(const GUID& key); - winrt::com_ptr contextCompartment(const GUID& key, ITfContext* context = NULL); - - DWORD globalCompartmentValue(const GUID& key); - DWORD threadCompartmentValue(const GUID& key); - DWORD contextCompartmentValue(const GUID& key, ITfContext* context = NULL); - - void setGlobalCompartmentValue(const GUID& key, DWORD value); - void setThreadCompartmentValue(const GUID& key, DWORD value); - void setContextCompartmentValue(const GUID& key, DWORD value, ITfContext* context = NULL); - - // manage sinks to global or thread compartment (context specific compartment is not used) - void addCompartmentMonitor(const GUID key, bool isGlobal = false); - void removeCompartmentMonitor(const GUID key); - - // virtual functions that IME implementors may need to override - virtual void onActivate(); - virtual void onDeactivate(); - - virtual void onSetFocus(); - virtual void onKillFocus(); - - virtual bool filterKeyDown(KeyEvent& keyEvent); - virtual bool onKeyDown(KeyEvent& keyEvent, EditSession* session); - - virtual bool filterKeyUp(KeyEvent& keyEvent); - virtual bool onKeyUp(KeyEvent& keyEvent, EditSession* session); - - virtual bool onPreservedKey(const GUID& guid); - - // called when a language button or menu item is clicked - virtual bool onCommand(UINT id, CommandType type); - - // called when a value in the global or thread compartment changed. - virtual void onCompartmentChanged(const GUID& key); - - virtual void onLangBarStatusChanged(int newStatus); - - // called when the keyboard is opened or closed - virtual void onKeyboardStatusChanged(bool opened); - - // called just before current composition is terminated for doing cleanup. - // if forced is true, the composition is terminated by others, such as - // the input focus is grabbed by another application. - // if forced is false, the composition is terminated gracefully by endComposition(). - virtual void onCompositionTerminated(bool forced); - -private: - winrt::com_ptr module_; - winrt::com_ptr threadMgr_; - TfClientId clientId_; - DWORD activateFlags_; - bool isKeyboardOpened_; - - // event sink cookies - DWORD threadMgrEventSinkCookie_; - DWORD textEditSinkCookie_; - DWORD compositionSinkCookie_; - DWORD keyboardOpenEventSinkCookie_; - DWORD globalCompartmentEventSinkCookie_; - DWORD langBarSinkCookie_; - - ITfComposition* composition_; // acquired when starting composition, released when ending composition - winrt::com_ptr langBarMgr_; - std::vector langBarButtons_; - std::vector preservedKeys_; - std::vector compartmentMonitors_; -}; - -} - -#endif From 782a1dd502953d280f241df0d698de5772b14f7d Mon Sep 17 00:00:00 2001 From: Kan-Ru Chen Date: Sat, 9 Nov 2024 10:59:49 +0900 Subject: [PATCH 2/2] refactor: remove unused DLL registration code (moved to tsfreg) --- ChewingTextService/ChewingTextService.def | 13 +- ChewingTextService/DllEntry.cpp | 45 +-- libIME/ImeModule.cpp | 351 ---------------------- libIME/ImeModule.h | 4 - 4 files changed, 6 insertions(+), 407 deletions(-) diff --git a/ChewingTextService/ChewingTextService.def b/ChewingTextService/ChewingTextService.def index f83f69b..922a494 100644 --- a/ChewingTextService/ChewingTextService.def +++ b/ChewingTextService/ChewingTextService.def @@ -1,8 +1,5 @@ -LIBRARY ChewingTextService.dll - -EXPORTS - DllGetClassObject PRIVATE - DllCanUnloadNow PRIVATE - DllRegisterServer PRIVATE - DllUnregisterServer PRIVATE - ChewingSetup PRIVATE +LIBRARY ChewingTextService.dll + +EXPORTS + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE \ No newline at end of file diff --git a/ChewingTextService/DllEntry.cpp b/ChewingTextService/DllEntry.cpp index 9eb0220..215bdf9 100644 --- a/ChewingTextService/DllEntry.cpp +++ b/ChewingTextService/DllEntry.cpp @@ -2,17 +2,10 @@ #include #include "ChewingImeModule.h" -#include "ChewingConfig.h" #include "resource.h" Chewing::ImeModule* g_imeModule = NULL; -// GUID of our language profile -// {CE45F71D-CE79-41D1-967D-640B65A380E3} -static const GUID g_profileGuid = { - 0xce45f71d, 0xce79, 0x41d1, { 0x96, 0x7d, 0x64, 0xb, 0x65, 0xa3, 0x80, 0xe3 } -}; - BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: @@ -35,40 +28,4 @@ STDAPI DllCanUnloadNow(void) { STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppvObj) { return g_imeModule->getClassObject(rclsid, riid, ppvObj); -} - -STDAPI DllUnregisterServer(void) { - return g_imeModule->unregisterServer(); -} - -STDAPI DllRegisterServer(void) { - wchar_t name[32]; - ::LoadStringW(g_imeModule->hInstance(), IDS_CHEWING, name, 32); - - // get path of our module - wchar_t modulePath[MAX_PATH]; - DWORD modulePathLen = GetModuleFileNameW(g_imeModule->hInstance(), modulePath, MAX_PATH); - - int iconIndex = 0; // use classic icon - if(IsWindows8OrGreater()) - iconIndex = 1; // use Windows 8 style IME icon - - Ime::LangProfileInfo info; - info.name = name; - info.profileGuid = g_profileGuid; - info.locale = L"zh-Hant-TW"; - info.fallbackLocale = L"zh-TW"; - info.iconIndex = iconIndex; - info.iconFile = modulePath; - - return g_imeModule->registerServer(name, &info, 1); -} - -STDAPI ChewingSetup() { - // The directory is already created when the ImeModule object is constructed. - if(IsWindows8OrGreater()) { - // Grant permission to app containers - Chewing::Config::grantAppContainerAccess(g_imeModule->userDir().c_str(), SE_FILE_OBJECT, GENERIC_READ|GENERIC_WRITE|GENERIC_EXECUTE|DELETE); - } - return S_OK; -} +} \ No newline at end of file diff --git a/libIME/ImeModule.cpp b/libIME/ImeModule.cpp index 47cc0e3..c688e6d 100644 --- a/libIME/ImeModule.cpp +++ b/libIME/ImeModule.cpp @@ -21,7 +21,6 @@ #include #include "ImeModule.h" -#include #include #include #include @@ -36,17 +35,6 @@ using namespace std; namespace Ime { -// these values are not defined in older TSF SDK (windows xp) -#ifndef TF_IPP_CAPS_IMMERSIVESUPPORT -// for Windows 8 -// GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT {13A016DF-560B-46CD-947A-4C3AF1E0E35D} -static const GUID GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT = -{ 0x13A016DF, 0x560B, 0x46CD, { 0x94, 0x7A, 0x4C, 0x3A, 0xF1, 0xE0, 0xE3, 0x5D } }; -// GUID_TFCAT_TIPCAP_SYSTRAYSUPPORT {25504FB4-7BAB-4BC1-9C69-CF81890F0EF5} -static const GUID GUID_TFCAT_TIPCAP_SYSTRAYSUPPORT = -{ 0x25504FB4, 0x7BAB, 0x4BC1, { 0x9C, 0x69, 0xCF, 0x81, 0x89, 0x0F, 0x0E, 0xF5 } }; -#endif - // display attribute GUIDs // {05814A20-00B3-4B73-A3D0-2C521EFA8BE5} @@ -109,345 +97,6 @@ HRESULT ImeModule::getClassObject(REFCLSID rclsid, REFIID riid, void **ppvObj) { return CLASS_E_CLASSNOTAVAILABLE; } -#ifndef _WIN64 // only do this for the 32-bit version dll -static void loadDefaultUserRegistry(const wchar_t* defaultUserRegKey) { - // The registry settings of all newly created users are based on the content of - // "C:\Users\Default User\ntuser.dat", so we need to write our settings to this file so - // the HKEY_CURRENT_USER key of newly created users can also contain our settings. - // In order to do this, we need to load the default "hive" to registry first. - // Reference: https://msdn.microsoft.com/zh-tw/library/windows/desktop/ms724889(v=vs.85).aspx - wchar_t *userProfilesDir = nullptr; - if (SUCCEEDED(::SHGetKnownFolderPath(FOLDERID_UserProfiles, 0, NULL, &userProfilesDir))) { - // get the path of the default ntuser.dat file - std::wstring defaultRegFile = userProfilesDir; - ::CoTaskMemFree(userProfilesDir); - defaultRegFile += L"\\Default User\\ntuser.dat"; - - // loading registry file requires special privileges SE_RESTORE_NAME and SE_BACKUP_NAME. - // So let's do privilege elevation for our process. - HANDLE processToken = NULL; - ::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &processToken); - DWORD bufLen = sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES); - std::unique_ptr buf(new char[bufLen]); - TOKEN_PRIVILEGES* privileges = reinterpret_cast(buf.get()); - privileges->PrivilegeCount = 2; - ::LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &privileges->Privileges[0].Luid); - privileges->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - ::LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &privileges->Privileges[1].Luid); - privileges->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED; - ::AdjustTokenPrivileges(processToken, FALSE, privileges, bufLen, NULL, NULL); - ::CloseHandle(processToken); - - // load the default registry hive under the specified key name - ::RegLoadKeyW(HKEY_USERS, defaultUserRegKey, defaultRegFile.c_str()); - } -} -#endif // #ifndef _WIN64 - -HRESULT ImeModule::registerLangProfiles(LangProfileInfo* langs, int langsCount) { - // register the language profile - winrt::com_ptr inputProcessProfiles; - if(CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER, IID_ITfInputProcessorProfiles, inputProcessProfiles.put_void()) == S_OK) { - for(int i = 0; i < langsCount; ++i) { - LangProfileInfo& lang = langs[i]; - if(inputProcessProfiles->Register(textServiceClsid_) == S_OK) { - LCID lcid = LocaleNameToLCID(lang.locale.c_str(), 0); - if (lcid == 0 && !lang.fallbackLocale.empty()) { // the conversion fails - // The new RFC4646 locale names are not well-supported in Windows 7/Vista, so - // here we provide a fallback locale which uses the deprecated RFC 1766 format instead. - lcid = LocaleNameToLCID(lang.fallbackLocale.c_str(), 0); - } - if (lcid != 0) { - LANGID langId = LANGIDFROMLCID(lcid); - if (inputProcessProfiles->AddLanguageProfile(textServiceClsid_, langId, lang.profileGuid, - lang.name.c_str(), lang.name.length(), lang.iconFile.empty() ? NULL : lang.iconFile.c_str(), - lang.iconFile.length(), lang.iconIndex) != S_OK) { - return E_FAIL; - } - } - else { - return E_FAIL; - } - } - } - } - - // NOTE: For Windows newer than Windows 8, we have to manually write some settings - // to the registry so the input methods can appear in the Windows control panel. - // - // Registry path: "HKEY_CURRENT_USER\Control Panel\International\User Profile\" - // Sub key: ":{text service GUID}{input module GUID}" - // - // Unfortunately, this is not documented officially by Microsoft. - // We found the values with some registry monitor tools: - // These settings are user-specific so they should be written to HKEY_CURRENT_USER of all users. - // This might be achieved by Microsoft Acitve Setup, yet another undocumented feature. - // https://helgeklein.com/blog/2010/04/active-setup-explained/ - // - // However, there is no way to uninstall keys installed with Active Setup. So let's avoid it. - // References: https://support.microsoft.com/en-us/kb/284193 - // https://blogs.technet.microsoft.com/deploymentguys/2009/10/29/configuring-default-user-settings-full-update-for-windows-7-and-windows-server-2008-r2/ -#ifndef _WIN64 // only do this for the 32-bit version dll - // The keys under HKCU\Control Panel\ is shared between the x86 and x64 versions and - // are not affected by WOW64 redirection. So doing this inside the 32-bit version is enough. - - if (IsWindows8OrGreater()) { - DWORD sidCount = 0; - if (::RegQueryInfoKeyW(HKEY_USERS, NULL, NULL, NULL, &sidCount, NULL, NULL, NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) - return E_FAIL; - wchar_t* textServiceClsIdStr = nullptr; - if (FAILED(::StringFromCLSID(textServiceClsid_, &textServiceClsIdStr))) - return E_FAIL; - - const wchar_t* defaultUserRegKey = L"__PIME_Default_user__"; - loadDefaultUserRegistry(defaultUserRegKey); - - // write the language settings to user-specific registry. - wchar_t sid[256]; - for (DWORD iSid = 0; iSid < sidCount; ++iSid) { - DWORD sidLen = sizeof(sid) / sizeof(wchar_t); - if (::RegEnumKeyExW(HKEY_USERS, iSid, sid, &sidLen, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { - // write settings of each input module to the user's registry - for (int i = 0; i < langsCount; ++i) { - auto& lang = langs[i]; - std::wstring localeRegPath = sid; - localeRegPath += L"\\Control Panel\\International\\User Profile\\"; - localeRegPath += lang.locale; - HKEY localeRegKey = NULL; - DWORD err = ::RegCreateKeyExW(HKEY_USERS, localeRegPath.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &localeRegKey, NULL); - if (err == ERROR_SUCCESS) { - LCID lcid = LocaleNameToLCID(lang.locale.c_str(), 0); - if (lcid == 0 && !lang.fallbackLocale.empty()) { // the conversion fails - lcid = LocaleNameToLCID(lang.fallbackLocale.c_str(), 0); // try the fallback locale name - } - wchar_t lcid_hex[16]; - wsprintf(lcid_hex, L"%04x", lcid); - std::wstring valueName = lcid_hex; - valueName += L":"; - valueName += textServiceClsIdStr; - wchar_t* profileClsIdStr = nullptr; - if (SUCCEEDED(::StringFromCLSID(lang.profileGuid, &profileClsIdStr))) { - valueName += profileClsIdStr; - ::CoTaskMemFree(profileClsIdStr); - DWORD profileCount = 1; - if (::RegQueryInfoKeyW(localeRegKey, NULL, NULL, NULL, NULL, NULL, NULL, &profileCount, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { - // ::MessageBoxW(0, std::to_wstring(profileCount).c_str(), 0, 0); - ++profileCount; - } - ::RegSetKeyValueW(localeRegKey, NULL, valueName.c_str(), REG_DWORD, &profileCount, sizeof(DWORD)); - } - ::RegCloseKey(localeRegKey); - } - } - } - } - ::CoTaskMemFree(textServiceClsIdStr); - - // unload the default user registry hive - ::RegUnLoadKeyW(HKEY_USERS, defaultUserRegKey); - } -#endif // #ifndef _WIN64 - return S_OK; -} - -HRESULT ImeModule::registerServer(wchar_t* imeName, LangProfileInfo* langs, int count) { - // write info of our COM text service component to the registry - // path: HKEY_CLASS_ROOT\\CLSID\\{xxxx-xxxx-xxxx-xx....} - // This reguires Administrator permimssion to write to the registery - // regsvr32 should be run with Administrator - // For 64 bit dll, it seems that we need to write the key to - // a different path to make it coexist with 32 bit version: - // HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Classes\CLSID\{xxx-xxx-...} - // Reference: http://stackoverflow.com/questions/1105031/can-my-32-bit-and-64-bit-com-components-co-reside-on-the-same-machine - - HRESULT result = S_OK; - - // get path of our module - wchar_t modulePath[MAX_PATH]; - DWORD modulePathLen = GetModuleFileNameW(hInstance_, modulePath, MAX_PATH); - - wstring regPath = L"CLSID\\"; - LPOLESTR clsidStr = NULL; - if(StringFromCLSID(textServiceClsid_, &clsidStr) != ERROR_SUCCESS) - return E_FAIL; - regPath += clsidStr; - CoTaskMemFree(clsidStr); - - HKEY hkey = NULL; - if(::RegCreateKeyExW(HKEY_CLASSES_ROOT, regPath.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkey, NULL) == ERROR_SUCCESS) { - // write name of our IME - ::RegSetValueExW(hkey, NULL, 0, REG_SZ, (BYTE*)imeName, sizeof(wchar_t) * (wcslen(imeName) + 1)); - - HKEY inProcServer32Key; - if(::RegCreateKeyExW(hkey, L"InprocServer32", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &inProcServer32Key, NULL) == ERROR_SUCCESS) { - // store the path of our dll module in the registry - ::RegSetValueExW(inProcServer32Key, NULL, 0, REG_SZ, (BYTE*)modulePath, (modulePathLen + 1) * sizeof(wchar_t)); - // write threading model - wchar_t apartmentStr[] = L"Apartment"; - ::RegSetValueExW(inProcServer32Key, L"ThreadingModel", 0, REG_SZ, (BYTE*)apartmentStr, 10 * sizeof(wchar_t)); - ::RegCloseKey(inProcServer32Key); - } - else - result = E_FAIL; - ::RegCloseKey(hkey); - } - else - result = E_FAIL; - - // register language profiles - if(result == S_OK) { - result = registerLangProfiles(langs, count); - } - - // register category - if(result == S_OK) { - ITfCategoryMgr *categoryMgr = NULL; - if(CoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, (void**)&categoryMgr) == S_OK) { - if(categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_TIP_KEYBOARD, textServiceClsid_) != S_OK) { - result = E_FAIL; - } - - // register ourself as a display attribute provider - // so later we can set change the look and feels of composition string. - if(categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER, textServiceClsid_) != S_OK) { - result = E_FAIL; - } - - // enable UI less mode - if(categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_TIPCAP_INPUTMODECOMPARTMENT, textServiceClsid_) != S_OK || - categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_TIPCAP_UIELEMENTENABLED, textServiceClsid_) != S_OK) { - result = E_FAIL; - } - - if(IsWindows8OrGreater()) { - // for Windows 8 store app support - // TODO: according to a exhaustive Google search, I found that - // TF_IPP_CAPS_IMMERSIVESUPPORT is required to make the IME work with Windows 8. - // http://social.msdn.microsoft.com/Forums/windowsapps/en-US/4c422cf1-ceb4-413b-8a7c-6881946a4c63/how-to-set-a-flag-indicating-tsf-components-compatibility - // Quote from the page: "To indicate that your IME is compatible with Windows Store apps, call RegisterCategory with GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT." - - // declare supporting immersive mode - if(categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT, textServiceClsid_) != S_OK) { - result = E_FAIL; - } - - // declare compatibility with Windows 8 system tray - if(categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_TIPCAP_SYSTRAYSUPPORT, textServiceClsid_) != S_OK) { - result = E_FAIL; - } - } - - categoryMgr->Release(); - } - } - return result; -} - -HRESULT ImeModule::unregisterServer() { - // unregister the language profile - ITfInputProcessorProfiles *inputProcessProfiles = NULL; - if(CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER, IID_ITfInputProcessorProfiles, (void**)&inputProcessProfiles) == S_OK) { - inputProcessProfiles->Unregister(textServiceClsid_); - inputProcessProfiles->Release(); - } - - // unregister categories - ITfCategoryMgr *categoryMgr = NULL; - if(CoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, (void**)&categoryMgr) == S_OK) { - categoryMgr->UnregisterCategory(textServiceClsid_, GUID_TFCAT_TIP_KEYBOARD, textServiceClsid_); - categoryMgr->UnregisterCategory(textServiceClsid_, GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER, textServiceClsid_); - // UI less mode - categoryMgr->UnregisterCategory(textServiceClsid_, GUID_TFCAT_TIPCAP_INPUTMODECOMPARTMENT, textServiceClsid_); - - if(IsWindows8OrGreater()) { - // Windows 8 support - categoryMgr->UnregisterCategory(textServiceClsid_, GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT, textServiceClsid_); - categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_TIPCAP_SYSTRAYSUPPORT, textServiceClsid_); - } - - categoryMgr->Release(); - } - - // delete the registry key - wstring regPath = L"CLSID\\"; - LPOLESTR clsidStr = NULL; - if(StringFromCLSID(textServiceClsid_, &clsidStr) == ERROR_SUCCESS) { - regPath += clsidStr; - CoTaskMemFree(clsidStr); - ::SHDeleteKey(HKEY_CLASSES_ROOT, regPath.c_str()); - } - -#ifndef _WIN64 // only do this for the 32-bit version dll - // The keys under HKCU\Control Panel\ is shared between the x86 and x64 versions and - // are not affected by WOW64 redirection. So doing this inside the 32-bit version is enough. - - // delete settings under "HKEY_CURRENT_USER\Control Panel\International\User Profile\" for all users - if (IsWindows8OrGreater()) { - DWORD sidCount = 0; - if (::RegQueryInfoKeyW(HKEY_USERS, NULL, NULL, NULL, &sidCount, NULL, NULL, NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) - return E_FAIL; - wchar_t* textServiceClsIdStr = nullptr; - if (FAILED(::StringFromCLSID(textServiceClsid_, &textServiceClsIdStr))) - return E_FAIL; - - const wchar_t* defaultUserRegKey = L"__PIME_Default_user__"; - loadDefaultUserRegistry(defaultUserRegKey); - - // delete the language settings from user-specific registry. - wchar_t sid[256]; - for (DWORD iSid = 0; iSid < sidCount; ++iSid) { - DWORD sidLen = sizeof(sid) / sizeof(wchar_t); - if (::RegEnumKeyExW(HKEY_USERS, iSid, sid, &sidLen, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { - // remove settings of each input module to the user's registry - std::wstring userRegPath = sid; - userRegPath += L"\\Control Panel\\International\\User Profile"; - HKEY userKey = NULL; - if (::RegOpenKeyExW(HKEY_USERS, userRegPath.c_str(), 0, KEY_READ, &userKey) == ERROR_SUCCESS) { - DWORD localeCount = 0; - if (::RegQueryInfoKeyW(userKey, NULL, NULL, NULL, &localeCount, NULL, NULL, NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { - // list all locales under this user - wchar_t locale[100]; - for (DWORD iLocale = 0; iLocale < localeCount; ++iLocale) { - DWORD localeLen = sizeof(locale) / sizeof(wchar_t); - if (::RegEnumKeyExW(userKey, iLocale, locale, &localeLen, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { - HKEY localeKey = NULL; - if (::RegOpenKeyExW(userKey, locale, 0, KEY_ALL_ACCESS | KEY_READ, &localeKey) == ERROR_SUCCESS) { - DWORD profileCount = 0; - ::RegQueryInfoKeyW(localeKey, NULL, NULL, NULL, NULL, NULL, NULL, &profileCount, NULL, NULL, NULL, NULL); - // list all language profiles under this locale - std::vector profiles; - for (DWORD iProfile = 0; iProfile < profileCount; ++iProfile) { - wchar_t profile[128]; - DWORD profileLen = sizeof(profile) / sizeof(wchar_t); - if (::RegEnumValueW(localeKey, iProfile, profile, &profileLen, 0, NULL, NULL, NULL) == ERROR_SUCCESS) { - if (wcsstr(profile, textServiceClsIdStr)) { // this profile is registered by us - profiles.push_back(profile); - } - } - } - // delete these language profiles beloning to us - for (const auto& profile : profiles) { - ::RegDeleteValueW(localeKey, profile.c_str()); - } - ::RegCloseKey(localeKey); - } - } - } - } - ::RegCloseKey(userKey); - } - } - } - ::CoTaskMemFree(textServiceClsIdStr); - - // unload the default user registry hive - ::RegUnLoadKeyW(HKEY_USERS, defaultUserRegKey); - } -#endif // #ifndef _WIN64 - return S_OK; -} - - // display attributes stuff bool ImeModule::registerDisplayAttributeInfos() { diff --git a/libIME/ImeModule.h b/libIME/ImeModule.h index 18bb195..2c0a170 100644 --- a/libIME/ImeModule.h +++ b/libIME/ImeModule.h @@ -61,10 +61,6 @@ class ImeModule: HRESULT canUnloadNow(); HRESULT getClassObject(REFCLSID rclsid, REFIID riid, void **ppvObj); - HRESULT registerServer(wchar_t* imeName, LangProfileInfo* langs, int count); - HRESULT registerLangProfiles(LangProfileInfo* langs, int count); - HRESULT unregisterServer(); - // should be override by IME implementors virtual TextService* createTextService() = 0; void removeTextService(TextService* service);