Skip to content

Commit

Permalink
Use named pipe to implement IPC and using rapidjson to handle json fo…
Browse files Browse the repository at this point in the history
…rmat.
  • Loading branch information
PCMan committed Jul 25, 2015
1 parent c80a467 commit c1eb0f5
Show file tree
Hide file tree
Showing 14 changed files with 228 additions and 91 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "libchewing"]
path = libchewing
url = https://github.com/chewing/libchewing.git
[submodule "rapidjson"]
path = rapidjson
url = https://github.com/miloyip/rapidjson.git
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# windows-chewing-tsf
# NodeIME

Implement chewing in Windows via Text Services Framework:
Implement input methods easily for Windows via Text Services Framework:
* LibIME contains a library which aims to be a simple wrapper for Windows Text Service Framework (TSF).
* ChewingTextService contains an implementation of Windows text service for libchewing using libIME.
* chewingwrapper contains a C++ wrapper for libchewing.
* NodeTextService contains an backbone implementation of Windows text service for using libIME.
* casablanca contains Microsoft REST C++ SDK (https://casablanca.codeplex.com/) licensed under Apache 2 license.

All parts are licensed under GNU LGPL v2.1 license.

Expand All @@ -13,13 +13,12 @@ All parts are licensed under GNU LGPL v2.1 license.
* [CMake](http://www.cmake.org/) >= 2.8.11
* [Visual Studio Express 2012](http://www.microsoft.com/visualstudio/eng/products/visual-studio-express-products)
* [git](http://windows.github.com/)
* Editor with [EditorConfig](http://editorconfig.org/) supported

## How to Build
* Get source from github

git clone https://github.com/chewing/windows-chewing-tsf.git
cd windows-chewing-tsf
cd NodeIme
git submodule init
git submodule update

Expand Down
8 changes: 4 additions & 4 deletions cmake/c_flag_overrides.cmake
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
if(MSVC)
set(CMAKE_C_FLAGS_DEBUG_INIT "/D_DEBUG /MTd /Zi /Ob0 /Od /RTC1")
set(CMAKE_C_FLAGS_MINSIZEREL_INIT "/MT /O1 /Ob1 /D NDEBUG")
set(CMAKE_C_FLAGS_RELEASE_INIT "/MT /O2 /Ob2 /D NDEBUG")
set(CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "/MT /Zi /O2 /Ob1 /D NDEBUG")
set(CMAKE_C_FLAGS_DEBUG_INIT "/D_DEBUG /MDd /Zi /Ob0 /Od /RTC1")
set(CMAKE_C_FLAGS_MINSIZEREL_INIT "/MD /O1 /Ob1 /D NDEBUG")
set(CMAKE_C_FLAGS_RELEASE_INIT "/MD /O2 /Ob2 /D NDEBUG")
set(CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "/MD /Zi /O2 /Ob1 /D NDEBUG")
endif()
8 changes: 4 additions & 4 deletions cmake/cxx_flag_overrides.cmake
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
if(MSVC)
set(CMAKE_CXX_FLAGS_DEBUG_INIT "/D_DEBUG /MTd /Zi /Ob0 /Od /RTC1")
set(CMAKE_CXX_FLAGS_MINSIZEREL_INIT "/MT /O1 /Ob1 /D NDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE_INIT "/MT /O2 /Ob2 /D NDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "/MT /Zi /O2 /Ob1 /D NDEBUG")
set(CMAKE_CXX_FLAGS_DEBUG_INIT "/D_DEBUG /MDd /Zi /Ob0 /Od /RTC1")
set(CMAKE_CXX_FLAGS_MINSIZEREL_INIT "/MD /O1 /Ob1 /D NDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE_INIT "/MD /O2 /Ob2 /D NDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "/MD /Zi /O2 /Ob1 /D NDEBUG")
endif()
38 changes: 23 additions & 15 deletions libIME/ImeModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,25 @@ HRESULT ImeModule::getClassObject(REFCLSID rclsid, REFIID riid, void **ppvObj) {
return CLASS_E_CLASSNOTAVAILABLE;
}

HRESULT ImeModule::registerServer(wchar_t* name, const GUID& profileGuid, LANGID languageId, int iconIndex) {
HRESULT ImeModule::registerLangProfiles(LangProfileInfo* langs, int count) {
// register the language profile
ComPtr<ITfInputProcessorProfiles> inputProcessProfiles;
if(CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER, IID_ITfInputProcessorProfiles, (void**)&inputProcessProfiles) == S_OK) {
for(int i = 0; i < count; ++i) {
LangProfileInfo& lang = langs[i];
if(inputProcessProfiles->Register(textServiceClsid_) == S_OK) {
if(inputProcessProfiles->AddLanguageProfile(textServiceClsid_, lang.languageId, lang.profileGuid,
lang.name.c_str(), -1, lang.iconFile.empty() ? NULL : lang.iconFile.c_str(),
lang.iconFile.length(), lang.iconIndex) != S_OK) {
return E_FAIL;
}
}
}
}
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
Expand All @@ -130,7 +148,7 @@ HRESULT ImeModule::registerServer(wchar_t* name, const GUID& profileGuid, LANGID
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*)name, sizeof(wchar_t) * (wcslen(name) + 1));
::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) {
Expand All @@ -148,19 +166,9 @@ HRESULT ImeModule::registerServer(wchar_t* name, const GUID& profileGuid, LANGID
else
result = E_FAIL;

// register the language profile
// register language profiles
if(result == S_OK) {
result = E_FAIL;
ITfInputProcessorProfiles *inputProcessProfiles = NULL;
if(CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER, IID_ITfInputProcessorProfiles, (void**)&inputProcessProfiles) == S_OK) {
if(inputProcessProfiles->Register(textServiceClsid_) == S_OK) {
if(inputProcessProfiles->AddLanguageProfile(textServiceClsid_, languageId, profileGuid,
name, -1, modulePath, modulePathLen, iconIndex) == S_OK) {
result = S_OK;
}
}
inputProcessProfiles->Release();
}
result = registerLangProfiles(langs, count);
}

// register category
Expand Down Expand Up @@ -206,7 +214,7 @@ HRESULT ImeModule::registerServer(wchar_t* name, const GUID& profileGuid, LANGID
return result;
}

HRESULT ImeModule::unregisterServer(const GUID& profileGuid) {
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) {
Expand Down
17 changes: 15 additions & 2 deletions libIME/ImeModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,23 @@
#include <Ctffunc.h>
#include <list>
#include "WindowsVersion.h"
#include <string>

namespace Ime {

class TextService;
class DisplayAttributeInfo;

// language profile info, used to register new language profiles
struct LangProfileInfo {
std::wstring name; // should not exceed 32 chars
GUID profileGuid;
LANGID languageId;
std::wstring iconFile;
int iconIndex;
};


class ImeModule:
public IClassFactory,
public ITfFnConfigure {
Expand Down Expand Up @@ -58,8 +69,10 @@ class ImeModule:
// Dll entry points implementations
HRESULT canUnloadNow();
HRESULT getClassObject(REFCLSID rclsid, REFIID riid, void **ppvObj);
HRESULT registerServer(wchar_t* name, const GUID& profileGuid, LANGID languageId, int iconIndex);
HRESULT unregisterServer(const GUID& profileGuid);

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;
Expand Down
35 changes: 32 additions & 3 deletions libIME/TextService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ TextService::TextService(ImeModule* module):
keyboardOpenEventSinkCookie_(TF_INVALID_COOKIE),
globalCompartmentEventSinkCookie_(TF_INVALID_COOKIE),
langBarSinkCookie_(TF_INVALID_COOKIE),
activateLanguageProfileNotifySinkCookie_(TF_INVALID_COOKIE),
composition_(NULL),
candidateWindow_(NULL),
refCount_(1) {
Expand Down Expand Up @@ -551,6 +552,14 @@ void TextService::onKeyboardStatusChanged(bool opened) {
void TextService::onCompositionTerminated(bool forced) {
}

// called when a language profile is activated (only useful for text services that supports multiple language profiles)
void TextService::onLangProfileActivated(REFGUID guidProfile) {
}

// called when a language profile is deactivated
void TextService::onLangProfileDeactivated(REFGUID guidProfile) {
}

// COM stuff

// IUnknown
Expand All @@ -573,6 +582,8 @@ STDMETHODIMP TextService::QueryInterface(REFIID riid, void **ppvObj) {
*ppvObj = (ITfCompartmentEventSink*)this;
else if(IsEqualIID(riid, IID_ITfLangBarEventSink))
*ppvObj = (ITfLangBarEventSink*)this;
else if(IsEqualIID(riid, IID_ITfActiveLanguageProfileNotifySink))
*ppvObj = (ITfActiveLanguageProfileNotifySink*)this;
else
*ppvObj = NULL;

Expand Down Expand Up @@ -613,10 +624,12 @@ STDMETHODIMP TextService::Activate(ITfThreadMgr *pThreadMgr, TfClientId tfClient

// advice event sinks (set up event listeners)

// ITfThreadMgrEventSink
// ITfThreadMgrEventSink, ITfActiveLanguageProfileNotifySink
ComQIPtr<ITfSource> source = threadMgr_;
if(source)
if(source) {
source->AdviseSink(IID_ITfThreadMgrEventSink, (ITfThreadMgrEventSink *)this, &threadMgrEventSinkCookie_);
source->AdviseSink(IID_ITfActiveLanguageProfileNotifySink, (ITfActiveLanguageProfileNotifySink *)this, &activateLanguageProfileNotifySinkCookie_);
}

// ITfTextEditSink,

Expand Down Expand Up @@ -675,11 +688,12 @@ STDMETHODIMP TextService::Activate(ITfThreadMgr *pThreadMgr, TfClientId tfClient
}

onActivate();

::MessageBox(0, L"onActivate", 0, 0);
return S_OK;
}

STDMETHODIMP TextService::Deactivate() {
::MessageBox(0, L"Deactivate", 0, 0);
// terminate composition properly
if(isComposing()) {
ITfContext* context = currentContext();
Expand Down Expand Up @@ -713,7 +727,9 @@ STDMETHODIMP TextService::Deactivate() {
ComQIPtr<ITfSource> source = threadMgr_;
if(source) {
source->UnadviseSink(threadMgrEventSinkCookie_);
source->UnadviseSink(activateLanguageProfileNotifySinkCookie_);
threadMgrEventSinkCookie_ = TF_INVALID_COOKIE;
activateLanguageProfileNotifySinkCookie_ = TF_INVALID_COOKIE;
}

// ITfTextEditSink,
Expand Down Expand Up @@ -973,6 +989,19 @@ STDMETHODIMP TextService::GetItemFloatingRect(DWORD dwThreadId, REFGUID rguid, R
}


// ITfActiveLanguageProfileNotifySink
STDMETHODIMP TextService::OnActivated(REFCLSID clsid, REFGUID guidProfile, BOOL fActivated) {
// we only support one text service, so clsid must be the same as that of our text service.
// otherwise it's not the notification for our text service, just ignore the event.
if(clsid == module_->textServiceClsid()) {
if(fActivated)
onLangProfileActivated(guidProfile);
else
onLangProfileDeactivated(guidProfile);
}
return S_OK;
}

// edit session handling
STDMETHODIMP TextService::KeyEditSession::DoEditSession(TfEditCookie ec) {
EditSession::DoEditSession(ec);
Expand Down
13 changes: 12 additions & 1 deletion libIME/TextService.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ class TextService:
public ITfKeyEventSink,
public ITfCompositionSink,
public ITfCompartmentEventSink,
public ITfLangBarEventSink {
public ITfLangBarEventSink,
public ITfActiveLanguageProfileNotifySink {
public:

enum CommandType { // used in onCommand()
Expand Down Expand Up @@ -178,6 +179,12 @@ class TextService:
// if forced is false, the composition is terminated gracefully by endComposition().
virtual void onCompositionTerminated(bool forced);

// called when a language profile is activated (only useful for text services that supports multiple language profiles)
virtual void onLangProfileActivated(REFGUID guidProfile);

// called when a language profile is deactivated
virtual void onLangProfileDeactivated(REFGUID guidProfile);

// COM related stuff
public:
friend class DisplayAttributeInfoEnum;
Expand Down Expand Up @@ -226,6 +233,9 @@ class TextService:
STDMETHODIMP ShowFloating(DWORD dwFlags);
STDMETHODIMP GetItemFloatingRect(DWORD dwThreadId, REFGUID rguid, RECT *prc);

// ITfActiveLanguageProfileNotifySink
STDMETHODIMP OnActivated(REFCLSID clsid, REFGUID guidProfile, BOOL fActivated);

protected:
// edit session classes, used with TSF
class KeyEditSession: public EditSession {
Expand Down Expand Up @@ -289,6 +299,7 @@ class TextService:
DWORD keyboardOpenEventSinkCookie_;
DWORD globalCompartmentEventSinkCookie_;
DWORD langBarSinkCookie_;
DWORD activateLanguageProfileNotifySinkCookie_;

ITfComposition* composition_; // acquired when starting composition, released when ending composition
CandidateWindow* candidateWindow_;
Expand Down
4 changes: 4 additions & 0 deletions node-ime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ add_definitions(/D_UNICODE=1 /DUNICODE=1)

include_directories(
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/rapidjson/include
)

add_library(NodeTextService SHARED
Expand All @@ -16,10 +17,13 @@ add_library(NodeTextService SHARED
NodeTextService.cpp
NodeTextService.def
NodeTextService.h
NodeClient.cpp
NodeClient.h
DllEntry.cpp
# resources
NodeTextService.rc
)

target_link_libraries(NodeTextService
libIME_static
)
Loading

0 comments on commit c1eb0f5

Please sign in to comment.