diff --git a/release/c/cs_pinyin/HISTORY.md b/release/c/cs_pinyin/HISTORY.md
new file mode 100644
index 0000000000..c33c55e1e1
--- /dev/null
+++ b/release/c/cs_pinyin/HISTORY.md
@@ -0,0 +1,11 @@
+CS Pinyin Change History
+=======================
+
+1.4 (1 Aug 2024)
+-----------------
+* Improve IM window handling when using more than one application
+* Improve multiple monitor support
+* Add build script and reorganize files for Keyman 17
+
+* Published under MIT license to keymanapp/keyboards GitHub repository
+
diff --git a/release/c/cs_pinyin/LICENSE.md b/release/c/cs_pinyin/LICENSE.md
new file mode 100644
index 0000000000..a467b169c3
--- /dev/null
+++ b/release/c/cs_pinyin/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2003-2024 SIL International
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/release/c/cs_pinyin/README.md b/release/c/cs_pinyin/README.md
new file mode 100644
index 0000000000..981810b833
--- /dev/null
+++ b/release/c/cs_pinyin/README.md
@@ -0,0 +1,28 @@
+CS-Pinyin keyboard
+==================
+
+Description
+-----------
+
+This input method extension for Keyman Desktop provides input mapping for more
+than 100,000 Han characters, multi-character words, proper names, and place name
+abbreviations. Supported input conventions include Pinyin, RAD-RSC and 4-corner
+index (4CI).
+
+Notes
+-----
+
+The source/build.bat for this keyboard builds the Windows executables and DLLs
+required to make this keyboard work, and places the binaries in the source
+folder. The binaries must be checked in.
+
+build.bat requires VC++2019.
+
+Links
+-----
+
+ * Contact: support@keyman.com
+
+Supported Platforms
+-------------------
+ * Windows
diff --git a/release/c/cs_pinyin/cs_pinyin.kpj b/release/c/cs_pinyin/cs_pinyin.kpj
new file mode 100644
index 0000000000..e623a6059a
--- /dev/null
+++ b/release/c/cs_pinyin/cs_pinyin.kpj
@@ -0,0 +1,8 @@
+
+
+
+ 2.0
+ True
+ True
+
+
diff --git a/release/c/cs_pinyin/extra/CS-Pinyin.doc b/release/c/cs_pinyin/extra/CS-Pinyin.doc
new file mode 100644
index 0000000000..af3b85b703
Binary files /dev/null and b/release/c/cs_pinyin/extra/CS-Pinyin.doc differ
diff --git a/release/c/cs_pinyin/extra/CS-Pinyin/CS-Pinyin.cfg b/release/c/cs_pinyin/extra/CS-Pinyin/CS-Pinyin.cfg
new file mode 100644
index 0000000000..dfb67c6df3
Binary files /dev/null and b/release/c/cs_pinyin/extra/CS-Pinyin/CS-Pinyin.cfg differ
diff --git a/release/c/cs_pinyin/extra/CS-Pinyin/Make Full CFG.bat b/release/c/cs_pinyin/extra/CS-Pinyin/Make Full CFG.bat
new file mode 100644
index 0000000000..ca32fea1c2
--- /dev/null
+++ b/release/c/cs_pinyin/extra/CS-Pinyin/Make Full CFG.bat
@@ -0,0 +1,8 @@
+copy /b "Part 1 - Header and Punctuation.txt" CS-Pinyin.cfg
+copy /b CS-Pinyin.cfg + "Part 2 - Single characters.txt"
+copy /b CS-Pinyin.cfg + "Part 3 - Binomes and Polynomes.txt"
+copy /b CS-Pinyin.cfg + "Part 4 - Acronyms.txt"
+copy /b CS-Pinyin.cfg + "Part 5 - RAD-RSC.txt"
+copy /b CS-Pinyin.cfg + "Part 6 - 4CI.txt"
+
+
diff --git a/release/c/cs_pinyin/extra/CS-Pinyin/Part 1 - Header and Punctuation.txt b/release/c/cs_pinyin/extra/CS-Pinyin/Part 1 - Header and Punctuation.txt
new file mode 100644
index 0000000000..b1730380fd
Binary files /dev/null and b/release/c/cs_pinyin/extra/CS-Pinyin/Part 1 - Header and Punctuation.txt differ
diff --git a/release/c/cs_pinyin/extra/CS-Pinyin/Part 2 - Single characters.txt b/release/c/cs_pinyin/extra/CS-Pinyin/Part 2 - Single characters.txt
new file mode 100644
index 0000000000..c76de054d3
Binary files /dev/null and b/release/c/cs_pinyin/extra/CS-Pinyin/Part 2 - Single characters.txt differ
diff --git a/release/c/cs_pinyin/extra/CS-Pinyin/Part 3 - Binomes and Polynomes.txt b/release/c/cs_pinyin/extra/CS-Pinyin/Part 3 - Binomes and Polynomes.txt
new file mode 100644
index 0000000000..69765926c6
Binary files /dev/null and b/release/c/cs_pinyin/extra/CS-Pinyin/Part 3 - Binomes and Polynomes.txt differ
diff --git a/release/c/cs_pinyin/extra/CS-Pinyin/Part 4 - Acronyms.txt b/release/c/cs_pinyin/extra/CS-Pinyin/Part 4 - Acronyms.txt
new file mode 100644
index 0000000000..5ffa3b534f
Binary files /dev/null and b/release/c/cs_pinyin/extra/CS-Pinyin/Part 4 - Acronyms.txt differ
diff --git a/release/c/cs_pinyin/extra/CS-Pinyin/Part 5 - RAD-RSC.txt b/release/c/cs_pinyin/extra/CS-Pinyin/Part 5 - RAD-RSC.txt
new file mode 100644
index 0000000000..9f80a7980e
Binary files /dev/null and b/release/c/cs_pinyin/extra/CS-Pinyin/Part 5 - RAD-RSC.txt differ
diff --git a/release/c/cs_pinyin/extra/CS-Pinyin/Part 6 - 4CI.txt b/release/c/cs_pinyin/extra/CS-Pinyin/Part 6 - 4CI.txt
new file mode 100644
index 0000000000..0a614847c0
Binary files /dev/null and b/release/c/cs_pinyin/extra/CS-Pinyin/Part 6 - 4CI.txt differ
diff --git a/release/c/cs_pinyin/extra/CS-Pinyin/database.zip b/release/c/cs_pinyin/extra/CS-Pinyin/database.zip
new file mode 100644
index 0000000000..7a3647dde6
Binary files /dev/null and b/release/c/cs_pinyin/extra/CS-Pinyin/database.zip differ
diff --git a/release/c/cs_pinyin/extra/Simplified Chinese.doc b/release/c/cs_pinyin/extra/Simplified Chinese.doc
new file mode 100644
index 0000000000..aafdea5244
Binary files /dev/null and b/release/c/cs_pinyin/extra/Simplified Chinese.doc differ
diff --git a/release/c/cs_pinyin/extra/The Four Corner Index Lookup Method.doc b/release/c/cs_pinyin/extra/The Four Corner Index Lookup Method.doc
new file mode 100644
index 0000000000..049d6f33a0
Binary files /dev/null and b/release/c/cs_pinyin/extra/The Four Corner Index Lookup Method.doc differ
diff --git a/release/c/cs_pinyin/extra/cjkdict.zip b/release/c/cs_pinyin/extra/cjkdict.zip
new file mode 100644
index 0000000000..e5c7902d05
Binary files /dev/null and b/release/c/cs_pinyin/extra/cjkdict.zip differ
diff --git a/release/c/cs_pinyin/source/.gitignore b/release/c/cs_pinyin/source/.gitignore
new file mode 100644
index 0000000000..5c2cec7580
--- /dev/null
+++ b/release/c/cs_pinyin/source/.gitignore
@@ -0,0 +1,2 @@
+.vs/
+**/*.vcxproj.user
\ No newline at end of file
diff --git a/release/c/cs_pinyin/source/KeymnIMX.dll b/release/c/cs_pinyin/source/KeymnIMX.dll
new file mode 100644
index 0000000000..dc3bba15a0
Binary files /dev/null and b/release/c/cs_pinyin/source/KeymnIMX.dll differ
diff --git a/release/c/cs_pinyin/source/KeymnIMX.x64.dll b/release/c/cs_pinyin/source/KeymnIMX.x64.dll
new file mode 100644
index 0000000000..6f36a07edd
Binary files /dev/null and b/release/c/cs_pinyin/source/KeymnIMX.x64.dll differ
diff --git a/release/c/cs_pinyin/source/build.bat b/release/c/cs_pinyin/source/build.bat
new file mode 100644
index 0000000000..4e3d9a7617
--- /dev/null
+++ b/release/c/cs_pinyin/source/build.bat
@@ -0,0 +1,15 @@
+@echo off
+
+if "%1" == "--debug" (
+ set CONFIG=Debug
+) else (
+ set CONFIG=Release
+)
+
+msbuild keymanimx/KeymanIMX.vcxproj /p:Platform=Win32 /p:Configuration=%CONFIG%
+msbuild keymanimx/KeymanIMX.vcxproj /p:Platform=x64 /p:Configuration=%CONFIG%
+msbuild imxconfig/imxconfig.vcxproj /p:Platform=Win32 /p:Configuration=%CONFIG%
+msbuild imxreload/imxreload.vcxproj /p:Platform=Win32 /p:Configuration=%CONFIG%
+copy ..\build\KeymanIMX\Win32\%CONFIG%\KeymnIMX.dll ..\source
+copy ..\build\KeymanIMX\x64\%CONFIG%\KeymnIMX.x64.dll ..\source
+copy ..\build\imxconfig\Win32\%CONFIG%\imxconfig.exe ..\source
diff --git a/release/c/cs_pinyin/source/cs_pinyin.cfg b/release/c/cs_pinyin/source/cs_pinyin.cfg
new file mode 100644
index 0000000000..dfb67c6df3
Binary files /dev/null and b/release/c/cs_pinyin/source/cs_pinyin.cfg differ
diff --git a/release/c/cs_pinyin/source/cs_pinyin.cfx b/release/c/cs_pinyin/source/cs_pinyin.cfx
new file mode 100644
index 0000000000..d3b19677a2
Binary files /dev/null and b/release/c/cs_pinyin/source/cs_pinyin.cfx differ
diff --git a/release/c/cs_pinyin/source/cs_pinyin.kmn b/release/c/cs_pinyin/source/cs_pinyin.kmn
new file mode 100644
index 0000000000..91ca8913d6
--- /dev/null
+++ b/release/c/cs_pinyin/source/cs_pinyin.kmn
@@ -0,0 +1,40 @@
+store(&VERSION) '9.0'
+store(&HOTKEY) '[Ctrl Alt K_S]'
+store(&BITMAP) 'images\CS-Pinyin Icon.bmp'
+
+store(&mnemoniclayout) '1'
+store(©RIGHT) 'SIL International'
+store(&NAME) 'Simplified Chinese'
+store(&KEYBOARDVERSION) '1.4'
+
+begin Unicode > use(main)
+
+c Use an 8.3 name for compatibility with Win 9x
+store(DLLFunction) "KeymnIMX.DLL:FindGlyph"
+
+store(VKeys) [K_ESC][' '][K_BKSP][K_PGUP][K_PGDN][K_LEFT][K_RIGHT][K_UP][K_DOWN][K_ENTER][K_HOME][K_END][K_F24]
+store(NPKeys) [K_NP0][K_NP1][K_NP2][K_NP3][K_NP4][K_NP5][K_NP6][K_NP7][K_NP8][K_NP9][K_NPSTAR][K_NPPLUS][K_NPMINUS][K_NPDOT]
+
+store(FKeys) [K_F1][K_F2][K_F3][K_F4][K_F5][K_F6][K_F7][K_F8][K_F9][K_F10][K_F11][K_F12]
+store(SFKeys) [SHIFT K_F1][SHIFT K_F2][SHIFT K_F3][SHIFT K_F4][SHIFT K_F5][SHIFT K_F6] \
+ [SHIFT K_F7][SHIFT K_F8][SHIFT K_F9][SHIFT K_F10][SHIFT K_F11][SHIFT K_F12]
+store(CFKeys) [CTRL K_F1][CTRL K_F2][CTRL K_F3][CTRL K_F4][CTRL K_F5][CTRL K_F6] \
+ [CTRL K_F7][CTRL K_F8][CTRL K_F9][CTRL K_F10][CTRL K_F11][CTRL K_F12]
+
+group(main) using keys
+
+c any virtual keys that need to be processed must be explicitly matched
+ + any(VKeys) > call(DLLFunction)
+ + any(NPKeys) > call(DLLFunction)
+
+c enable the following lines to allow Function keys, with Shift or Ctrl modifiers
+c to be passed to the IMX DLL. Comment them out otherwise.
+ + any(FKeys) > call(DLLFunction)
+ + any(SFKeys) > call(DLLFunction)
+ + any(CFKeys) > call(DLLFunction)
+
+c the configuration hotkey must also be explicitly matched
+ + [CTRL SHIFT '`'] > call(DLLFunction)
+
+c normal ASCII keys will match on the nomatch rule
+ nomatch > call(DLLFunction)
diff --git a/release/c/cs_pinyin/source/cs_pinyin.kps b/release/c/cs_pinyin/source/cs_pinyin.kps
new file mode 100644
index 0000000000..b9c0b78bdf
--- /dev/null
+++ b/release/c/cs_pinyin/source/cs_pinyin.kps
@@ -0,0 +1,133 @@
+
+
+
+ 17.0.328.0
+ 7.0
+
+
+
+ docs\readme.htm
+ images\CS-Pinyin Install.bmp
+ ..\LICENSE.md
+ docs\welcome.htm
+
+
+
+
+
+ Simplified Chinese
+
+
+ -
+
Using Simplified Chinese (PDF)
+ Using Simplified Chinese.pdf
+
+
+ psmelStartMenu
+
+ -
+
Welcome
+ CS-Pinyin ReadMe.html
+
+
+ psmelStartMenu
+
+ -
+
About the Four Corner Method
+ About the Four Corner Method.pdf
+
+
+ psmelStartMenu
+
+
+
+
+ Simplified Chinese
+
+ © SIL International
+ SIL International
+ https://keyman.com/
+
+
+
+ ..\build\cs_pinyin.kmx
+ Keyboard CS-Pinyin
+ 0
+ .kmx
+
+
+ images\CS-Pinyin Install.bmp
+ File CS-Pinyin Install.bmp
+ 0
+ .bmp
+
+
+ docs\welcome.htm
+ File welcome.htm
+ 0
+ .htm
+
+
+ docs\About the Four Corner Method.pdf
+ File About the Four Corner Method.pdf
+ 0
+ .pdf
+
+
+ docs\Using Simplified Chinese.pdf
+ File Using Simplified Chinese.pdf
+ 0
+ .pdf
+
+
+ cs_pinyin.cfx
+ File cs_pinyin.cfx
+ 0
+ .cfx
+
+
+ ..\source\KeymnIMX.x64.dll
+ File KeymnIMX.x64.dll
+ 0
+ .dll
+
+
+ ..\source\imxconfig.exe
+ File imxconfig.exe
+ 0
+ .exe
+
+
+ ..\LICENSE.md
+ File LICENSE.md
+ 0
+ .md
+
+
+ ..\source\KeymnIMX.dll
+ File KeymnIMX.dll
+ 0
+ .dll
+
+
+ docs\readme.htm
+ File readme.htm
+ 0
+ .htm
+
+
+
+
+ Simplified Chinese
+ cs_pinyin
+ 1.4
+
+ Chinese
+
+
+
+
+
+
+
+
diff --git a/release/c/cs_pinyin/source/cs_pinyin.sln b/release/c/cs_pinyin/source/cs_pinyin.sln
new file mode 100644
index 0000000000..1acd1d583c
--- /dev/null
+++ b/release/c/cs_pinyin/source/cs_pinyin.sln
@@ -0,0 +1,51 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31313.79
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeymanIMX", "keymanimx\KeymanIMX.vcxproj", "{F00D56AD-FFA9-4012-95D4-4ED8FA00DDEC}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "imxconfig", "imxconfig\imxconfig.vcxproj", "{4F2ED8DE-8B20-46E4-96F5-F8DD0BF26504}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "imxreload", "imxreload\imxreload.vcxproj", "{838D3D6C-F6E5-461E-ABF1-DC7053AC77D2}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {F00D56AD-FFA9-4012-95D4-4ED8FA00DDEC}.Debug|Win32.ActiveCfg = Debug|Win32
+ {F00D56AD-FFA9-4012-95D4-4ED8FA00DDEC}.Debug|Win32.Build.0 = Debug|Win32
+ {F00D56AD-FFA9-4012-95D4-4ED8FA00DDEC}.Debug|x64.ActiveCfg = Debug|x64
+ {F00D56AD-FFA9-4012-95D4-4ED8FA00DDEC}.Debug|x64.Build.0 = Debug|x64
+ {F00D56AD-FFA9-4012-95D4-4ED8FA00DDEC}.Release|Win32.ActiveCfg = Release|Win32
+ {F00D56AD-FFA9-4012-95D4-4ED8FA00DDEC}.Release|Win32.Build.0 = Release|Win32
+ {F00D56AD-FFA9-4012-95D4-4ED8FA00DDEC}.Release|x64.ActiveCfg = Release|x64
+ {F00D56AD-FFA9-4012-95D4-4ED8FA00DDEC}.Release|x64.Build.0 = Release|x64
+ {4F2ED8DE-8B20-46E4-96F5-F8DD0BF26504}.Debug|Win32.ActiveCfg = Debug|Win32
+ {4F2ED8DE-8B20-46E4-96F5-F8DD0BF26504}.Debug|Win32.Build.0 = Debug|Win32
+ {4F2ED8DE-8B20-46E4-96F5-F8DD0BF26504}.Debug|x64.ActiveCfg = Debug|x64
+ {4F2ED8DE-8B20-46E4-96F5-F8DD0BF26504}.Debug|x64.Build.0 = Debug|x64
+ {4F2ED8DE-8B20-46E4-96F5-F8DD0BF26504}.Release|Win32.ActiveCfg = Release|Win32
+ {4F2ED8DE-8B20-46E4-96F5-F8DD0BF26504}.Release|Win32.Build.0 = Release|Win32
+ {4F2ED8DE-8B20-46E4-96F5-F8DD0BF26504}.Release|x64.ActiveCfg = Release|x64
+ {4F2ED8DE-8B20-46E4-96F5-F8DD0BF26504}.Release|x64.Build.0 = Release|x64
+ {838D3D6C-F6E5-461E-ABF1-DC7053AC77D2}.Debug|Win32.ActiveCfg = Debug|Win32
+ {838D3D6C-F6E5-461E-ABF1-DC7053AC77D2}.Debug|Win32.Build.0 = Debug|Win32
+ {838D3D6C-F6E5-461E-ABF1-DC7053AC77D2}.Debug|x64.ActiveCfg = Debug|x64
+ {838D3D6C-F6E5-461E-ABF1-DC7053AC77D2}.Debug|x64.Build.0 = Debug|x64
+ {838D3D6C-F6E5-461E-ABF1-DC7053AC77D2}.Release|Win32.ActiveCfg = Release|Win32
+ {838D3D6C-F6E5-461E-ABF1-DC7053AC77D2}.Release|Win32.Build.0 = Release|Win32
+ {838D3D6C-F6E5-461E-ABF1-DC7053AC77D2}.Release|x64.ActiveCfg = Release|x64
+ {838D3D6C-F6E5-461E-ABF1-DC7053AC77D2}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {A5469622-41A5-4208-B8F8-B7DD2821F94E}
+ EndGlobalSection
+EndGlobal
diff --git a/release/c/cs_pinyin/source/docs/About the Four Corner Method.pdf b/release/c/cs_pinyin/source/docs/About the Four Corner Method.pdf
new file mode 100644
index 0000000000..f546fc66ba
Binary files /dev/null and b/release/c/cs_pinyin/source/docs/About the Four Corner Method.pdf differ
diff --git a/release/c/cs_pinyin/source/docs/CS-Pinyin.pdf b/release/c/cs_pinyin/source/docs/CS-Pinyin.pdf
new file mode 100644
index 0000000000..5923814a0f
Binary files /dev/null and b/release/c/cs_pinyin/source/docs/CS-Pinyin.pdf differ
diff --git a/release/c/cs_pinyin/source/docs/The Four Corner Index Lookup Method.pdf b/release/c/cs_pinyin/source/docs/The Four Corner Index Lookup Method.pdf
new file mode 100644
index 0000000000..f546fc66ba
Binary files /dev/null and b/release/c/cs_pinyin/source/docs/The Four Corner Index Lookup Method.pdf differ
diff --git a/release/c/cs_pinyin/source/docs/Using Simplified Chinese.pdf b/release/c/cs_pinyin/source/docs/Using Simplified Chinese.pdf
new file mode 100644
index 0000000000..f0d6ce90e1
Binary files /dev/null and b/release/c/cs_pinyin/source/docs/Using Simplified Chinese.pdf differ
diff --git a/release/c/cs_pinyin/source/docs/Using the CS-Pinyin IMX.pdf b/release/c/cs_pinyin/source/docs/Using the CS-Pinyin IMX.pdf
new file mode 100644
index 0000000000..f0d6ce90e1
Binary files /dev/null and b/release/c/cs_pinyin/source/docs/Using the CS-Pinyin IMX.pdf differ
diff --git a/release/c/cs_pinyin/source/docs/readme.htm b/release/c/cs_pinyin/source/docs/readme.htm
new file mode 100644
index 0000000000..fc7b50af33
--- /dev/null
+++ b/release/c/cs_pinyin/source/docs/readme.htm
@@ -0,0 +1,53 @@
+
Simplified Chinese Input Method
+
+
+
+
+Simplified Chinese
+An Input Method for Keyman Desktop
+Description
+This input method extension for
+Keyman Desktop provides input mapping for more than
+100,000 Han characters, multi-character
+words, proper names, and place name abbreviations.
+Supported input conventions include Pinyin,
+RAD-RSC and 4-corner index (4CI).
+
+
+Copyright
+
+
+This input method extension is copyright 2003-2024
+by SIL International, and can be freely redistributed,
+but no part may be re-packaged, reverse-engineered,
+or sold. Wordlists used in this input mapping
+extension have been prepared by and are copyright
+by Linguasoft.
+
+
+
+For further information, refer to the user
+documentation included (and installed) with the
+package, visit the Keyman website
+https://keyman.com/ , or email us
+at support@keyman.com .
+
+
+
diff --git a/release/c/cs_pinyin/source/docs/welcome.htm b/release/c/cs_pinyin/source/docs/welcome.htm
new file mode 100644
index 0000000000..509b93f855
--- /dev/null
+++ b/release/c/cs_pinyin/source/docs/welcome.htm
@@ -0,0 +1,50 @@
+
+
+Simplified Chinese
+
+
+
+
+Simplified Chinese
+An Input Method for Keyman Desktop
+
+Description
+This input method extension for Keyman Desktop provides input mapping
+for more than 100,000 Han characters, multi-character words, proper names,
+and place name abbreviations. Supported input conventions include Pinyin,
+RAD-RSC and 4-corner index (4CI).
+
+
+How to use
+
+
+
+Copyright
+
+
+This input method extension is copyright SIL International and is distributed under the MIT license.
+Wordlists used in this input mapping extension have been prepared by and are copyright Linguasoft.
+
+
+
diff --git a/release/c/cs_pinyin/source/images/CS Logo Blue.bmp b/release/c/cs_pinyin/source/images/CS Logo Blue.bmp
new file mode 100644
index 0000000000..33d009b036
Binary files /dev/null and b/release/c/cs_pinyin/source/images/CS Logo Blue.bmp differ
diff --git a/release/c/cs_pinyin/source/images/CS Logo Red.bmp b/release/c/cs_pinyin/source/images/CS Logo Red.bmp
new file mode 100644
index 0000000000..149f0f6015
Binary files /dev/null and b/release/c/cs_pinyin/source/images/CS Logo Red.bmp differ
diff --git a/release/c/cs_pinyin/source/images/CS-Pinyin Icon.bmp b/release/c/cs_pinyin/source/images/CS-Pinyin Icon.bmp
new file mode 100644
index 0000000000..8c2ae261ae
Binary files /dev/null and b/release/c/cs_pinyin/source/images/CS-Pinyin Icon.bmp differ
diff --git a/release/c/cs_pinyin/source/images/CS-Pinyin Install.bmp b/release/c/cs_pinyin/source/images/CS-Pinyin Install.bmp
new file mode 100644
index 0000000000..d3bcb7fbac
Binary files /dev/null and b/release/c/cs_pinyin/source/images/CS-Pinyin Install.bmp differ
diff --git a/release/c/cs_pinyin/source/images/CS-Pinyin.bmp b/release/c/cs_pinyin/source/images/CS-Pinyin.bmp
new file mode 100644
index 0000000000..149f0f6015
Binary files /dev/null and b/release/c/cs_pinyin/source/images/CS-Pinyin.bmp differ
diff --git a/release/c/cs_pinyin/source/images/Direction_H.bmp b/release/c/cs_pinyin/source/images/Direction_H.bmp
new file mode 100644
index 0000000000..d9902b67d5
Binary files /dev/null and b/release/c/cs_pinyin/source/images/Direction_H.bmp differ
diff --git a/release/c/cs_pinyin/source/images/Direction_V.bmp b/release/c/cs_pinyin/source/images/Direction_V.bmp
new file mode 100644
index 0000000000..4be2b3d633
Binary files /dev/null and b/release/c/cs_pinyin/source/images/Direction_V.bmp differ
diff --git a/release/c/cs_pinyin/source/images/Image1.bmp b/release/c/cs_pinyin/source/images/Image1.bmp
new file mode 100644
index 0000000000..194128c38c
Binary files /dev/null and b/release/c/cs_pinyin/source/images/Image1.bmp differ
diff --git a/release/c/cs_pinyin/source/images/Key.bmp b/release/c/cs_pinyin/source/images/Key.bmp
new file mode 100644
index 0000000000..a0bc2999b9
Binary files /dev/null and b/release/c/cs_pinyin/source/images/Key.bmp differ
diff --git a/release/c/cs_pinyin/source/images/T_green.bmp b/release/c/cs_pinyin/source/images/T_green.bmp
new file mode 100644
index 0000000000..42e25ff4a1
Binary files /dev/null and b/release/c/cs_pinyin/source/images/T_green.bmp differ
diff --git a/release/c/cs_pinyin/source/images/T_red.bmp b/release/c/cs_pinyin/source/images/T_red.bmp
new file mode 100644
index 0000000000..2b74627563
Binary files /dev/null and b/release/c/cs_pinyin/source/images/T_red.bmp differ
diff --git a/release/c/cs_pinyin/source/images/T_yellow.bmp b/release/c/cs_pinyin/source/images/T_yellow.bmp
new file mode 100644
index 0000000000..2d7f0cb682
Binary files /dev/null and b/release/c/cs_pinyin/source/images/T_yellow.bmp differ
diff --git a/release/c/cs_pinyin/source/images/down0.bmp b/release/c/cs_pinyin/source/images/down0.bmp
new file mode 100644
index 0000000000..971885cf6e
Binary files /dev/null and b/release/c/cs_pinyin/source/images/down0.bmp differ
diff --git a/release/c/cs_pinyin/source/images/down1.bmp b/release/c/cs_pinyin/source/images/down1.bmp
new file mode 100644
index 0000000000..19b2cad7f1
Binary files /dev/null and b/release/c/cs_pinyin/source/images/down1.bmp differ
diff --git a/release/c/cs_pinyin/source/images/horizont.bmp b/release/c/cs_pinyin/source/images/horizont.bmp
new file mode 100644
index 0000000000..1609ca8efd
Binary files /dev/null and b/release/c/cs_pinyin/source/images/horizont.bmp differ
diff --git a/release/c/cs_pinyin/source/images/left2.bmp b/release/c/cs_pinyin/source/images/left2.bmp
new file mode 100644
index 0000000000..b2a9191b83
Binary files /dev/null and b/release/c/cs_pinyin/source/images/left2.bmp differ
diff --git a/release/c/cs_pinyin/source/images/master/CS Logo Blue.psp b/release/c/cs_pinyin/source/images/master/CS Logo Blue.psp
new file mode 100644
index 0000000000..3b2e2ae678
Binary files /dev/null and b/release/c/cs_pinyin/source/images/master/CS Logo Blue.psp differ
diff --git a/release/c/cs_pinyin/source/images/master/CS Logo Red.psp b/release/c/cs_pinyin/source/images/master/CS Logo Red.psp
new file mode 100644
index 0000000000..15a2599a38
Binary files /dev/null and b/release/c/cs_pinyin/source/images/master/CS Logo Red.psp differ
diff --git a/release/c/cs_pinyin/source/images/master/CS-Pinyin Install.psp b/release/c/cs_pinyin/source/images/master/CS-Pinyin Install.psp
new file mode 100644
index 0000000000..81c46a210c
Binary files /dev/null and b/release/c/cs_pinyin/source/images/master/CS-Pinyin Install.psp differ
diff --git a/release/c/cs_pinyin/source/images/master/CS-Pinyin.psp b/release/c/cs_pinyin/source/images/master/CS-Pinyin.psp
new file mode 100644
index 0000000000..621184bb3e
Binary files /dev/null and b/release/c/cs_pinyin/source/images/master/CS-Pinyin.psp differ
diff --git a/release/c/cs_pinyin/source/images/master/Circle.psp b/release/c/cs_pinyin/source/images/master/Circle.psp
new file mode 100644
index 0000000000..0357ee9650
Binary files /dev/null and b/release/c/cs_pinyin/source/images/master/Circle.psp differ
diff --git a/release/c/cs_pinyin/source/images/right0.bmp b/release/c/cs_pinyin/source/images/right0.bmp
new file mode 100644
index 0000000000..53578843a5
Binary files /dev/null and b/release/c/cs_pinyin/source/images/right0.bmp differ
diff --git a/release/c/cs_pinyin/source/images/right1.bmp b/release/c/cs_pinyin/source/images/right1.bmp
new file mode 100644
index 0000000000..86f506c3fa
Binary files /dev/null and b/release/c/cs_pinyin/source/images/right1.bmp differ
diff --git a/release/c/cs_pinyin/source/images/up0.bmp b/release/c/cs_pinyin/source/images/up0.bmp
new file mode 100644
index 0000000000..b34eafc07c
Binary files /dev/null and b/release/c/cs_pinyin/source/images/up0.bmp differ
diff --git a/release/c/cs_pinyin/source/images/up1.bmp b/release/c/cs_pinyin/source/images/up1.bmp
new file mode 100644
index 0000000000..100921108f
Binary files /dev/null and b/release/c/cs_pinyin/source/images/up1.bmp differ
diff --git a/release/c/cs_pinyin/source/images/vertical.bmp b/release/c/cs_pinyin/source/images/vertical.bmp
new file mode 100644
index 0000000000..86cb794659
Binary files /dev/null and b/release/c/cs_pinyin/source/images/vertical.bmp differ
diff --git a/release/c/cs_pinyin/source/imxconfig.exe b/release/c/cs_pinyin/source/imxconfig.exe
new file mode 100644
index 0000000000..da7f15a447
Binary files /dev/null and b/release/c/cs_pinyin/source/imxconfig.exe differ
diff --git a/release/c/cs_pinyin/source/imxconfig/Key.bmp b/release/c/cs_pinyin/source/imxconfig/Key.bmp
new file mode 100644
index 0000000000..a0bc2999b9
Binary files /dev/null and b/release/c/cs_pinyin/source/imxconfig/Key.bmp differ
diff --git a/release/c/cs_pinyin/source/imxconfig/icon1.ico b/release/c/cs_pinyin/source/imxconfig/icon1.ico
new file mode 100644
index 0000000000..55b46264be
Binary files /dev/null and b/release/c/cs_pinyin/source/imxconfig/icon1.ico differ
diff --git a/release/c/cs_pinyin/source/imxconfig/imxconfig.cpp b/release/c/cs_pinyin/source/imxconfig/imxconfig.cpp
new file mode 100644
index 0000000000..b228bc2f0c
--- /dev/null
+++ b/release/c/cs_pinyin/source/imxconfig/imxconfig.cpp
@@ -0,0 +1,273 @@
+#include
+#include
+#include
+
+#include "resource.h"
+#include "../keymanimx/keymanimx.h"
+
+#define MAXWAIT 60
+
+PSTR CAPTION="Keyman IMX Configuration";
+
+BOOL Vertical, EnableTracking;
+UINT DisplayMode;
+KEYBOARD Kbd={0};
+
+char drive[_MAX_DRIVE],dir[_MAX_DIR];
+
+void WriteRegSetting(PSTR text, int value)
+{
+ HKEY hkey;
+ if(RegCreateKeyEx(HKEY_CURRENT_USER, REGSZ_KeymanIMX, 0, NULL, NULL, KEY_ALL_ACCESS, NULL, &hkey, NULL) == ERROR_SUCCESS)
+ {
+ RegSetValueEx(hkey, text, 0, REG_DWORD, (PBYTE) &value, 4);
+ RegCloseKey(hkey);
+ }
+}
+
+int ReadRegSetting(PSTR text, int dflt)
+{
+ HKEY hkey;
+ DWORD value, sz=4, tp=REG_DWORD;
+
+ if(RegOpenKeyEx(HKEY_CURRENT_USER, REGSZ_KeymanIMX,
+ 0, KEY_ALL_ACCESS | KEY_WOW64_32KEY, &hkey) == ERROR_SUCCESS)
+ {
+ if(RegQueryValueEx(hkey, text, 0, &tp, (PBYTE) &value, &sz) == ERROR_SUCCESS)
+ dflt = value;
+ RegCloseKey(hkey);
+ }
+ return dflt;
+}
+
+BOOL ReadConfiguration(void)
+{
+ char key[128]="IMX: ";
+
+ Vertical = ReadRegSetting("IMX z",FALSE) ? TRUE : FALSE;
+ EnableTracking = ReadRegSetting("IMX track",TRUE) ? TRUE : FALSE;
+ DisplayMode = ReadRegSetting("IMX match",DM_LIMITED);
+
+ if(*Kbd.h.name)
+ {
+ strcat_s(key, sizeof key, Kbd.h.name);
+ Kbd.h.dwOptions = ReadRegSetting(key,Kbd.h.dwOptions);
+ }
+
+ return TRUE;
+}
+
+BOOL SaveConfiguration(void)
+{
+ char key[128]="IMX: ";
+
+ WriteRegSetting("IMX track", EnableTracking);
+ WriteRegSetting("IMX z", Vertical);
+ WriteRegSetting("IMX match", DisplayMode);
+
+ if(*Kbd.h.name)
+ {
+ strcat_s(key, sizeof key, Kbd.h.name);
+ WriteRegSetting(key,Kbd.h.dwOptions);
+ }
+ return TRUE;
+}
+
+INT_PTR CALLBACK IMXConfigProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ PSTR p=(PSTR)lParam;
+ int n, dMode, sMode, tMode, sBits=0, tBits=0, uBits=0, Unique, Duplicates;
+ int ctrlID[] = {IDC_KBDNAME,IDC_SELECTION,IDC_SELECT1,IDC_SELECT2,IDC_SELECT3,
+ IDC_SELECT4,IDC_TONES,IDC_TONE1,IDC_TONE2,IDC_TONE3,IDC_SELECTUNIQUE};
+
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ dMode = DisplayMode == DM_LIMITED ? IDC_MATCH1 : IDC_MATCH2;
+ CheckDlgButton(hwnd,IDC_VERTICAL,Vertical?BST_CHECKED:BST_UNCHECKED);
+ CheckDlgButton(hwnd,IDC_HORIZONTAL,Vertical?BST_UNCHECKED:BST_CHECKED);
+ CheckDlgButton(hwnd,IDC_TRACK,EnableTracking?BST_CHECKED:BST_UNCHECKED);
+ CheckRadioButton(hwnd,IDC_MATCH1,IDC_MATCH2,dMode);
+
+ // Set dialog buttons according to currently saved options
+ if(*Kbd.h.name)
+ {
+ char tkeys[32]="Tone keys";
+
+ for(n=0; n>2))
+ {
+ case TM_NEVER:
+ tMode =IDC_TONE1; break;
+ case TM_ALWAYS:
+ tMode =IDC_TONE2; break;
+ case TM_OPTIONAL:
+ tMode =IDC_TONE3; break;
+ }
+
+ Unique = (Kbd.h.dwOptions & SELECTIFUNIQUE) ? BST_CHECKED : BST_UNCHECKED;
+ Duplicates = (Kbd.h.dwOptions & SHOWDUPLICATES) ? BST_CHECKED : BST_UNCHECKED;
+
+ CheckRadioButton(hwnd,IDC_SELECT1,IDC_SELECT4,sMode);
+ CheckRadioButton(hwnd,IDC_TONE1,IDC_TONE3,tMode);
+ CheckDlgButton(hwnd,IDC_SELECTUNIQUE,Unique);
+ CheckDlgButton(hwnd,IDC_SHOWDUPLICATES,Duplicates);
+
+ // Set other constraints
+ if(Kbd.h.dwFKeys == 0) DisableDlgItem(hwnd,IDC_SELECT4);
+ }
+ else
+ {
+ for(n=0; nToneChar);
+ Kbd.h.dwOptions = pkbh->dwOptions;
+ Kbd.h.dwFKeys = pkbh->dwFKeys;
+ UnmapViewOfFile(pkbh); CloseHandle(hMapObject);
+ }
+ }
+
+ ReadConfiguration();
+ if(DialogBox(hInst,"CONFIGURE",NULL,IMXConfigProc)) SaveConfiguration();
+
+ return 0;
+}
diff --git a/release/c/cs_pinyin/source/imxconfig/imxconfig.rc b/release/c/cs_pinyin/source/imxconfig/imxconfig.rc
new file mode 100644
index 0000000000..7646ae8c3a
--- /dev/null
+++ b/release/c/cs_pinyin/source/imxconfig/imxconfig.rc
@@ -0,0 +1,190 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (Australia) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+CONFIGURE DIALOG DISCARDABLE 0, 0, 287, 159
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "Keyman IMX Configuration"
+FONT 8, "MS Sans Serif"
+BEGIN
+ GROUPBOX "Display options",IDC_STATIC,7,7,216,56
+ GROUPBOX "&Input matching",IDC_STATIC,13,16,76,36,WS_GROUP
+ CONTROL "First syllable",IDC_MATCH1,"Button",BS_AUTORADIOBUTTON |
+ WS_TABSTOP,21,27,59,10
+ CONTROL "Start of string",IDC_MATCH2,"Button",BS_AUTORADIOBUTTON,
+ 21,38,59,10
+ GROUPBOX "&Orientation",IDC_STATIC,92,16,61,36,WS_GROUP
+ CONTROL "Horizontal",IDC_HORIZONTAL,"Button",BS_AUTORADIOBUTTON |
+ WS_TABSTOP,100,27,47,10
+ CONTROL "Vertical",IDC_VERTICAL,"Button",BS_AUTORADIOBUTTON,100,
+ 38,47,10
+ GROUPBOX "&Cursor tracking",IDC_STATIC,156,16,61,36,WS_GROUP
+ CONTROL "&Enabled",IDC_TRACK,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,164,28,49,10
+ GROUPBOX "Active keyboard options",IDC_ACTIVE1,7,58,216,92
+ CTEXT "(Keyboard name)",IDC_KBDNAME,13,67,204,8,NOT WS_GROUP
+ GROUPBOX "&Selection",IDC_SELECTION,13,77,99,59,WS_GROUP
+ CONTROL "Keyboard (1-9)",IDC_SELECT1,"Button",BS_AUTORADIOBUTTON |
+ WS_TABSTOP,21,88,74,10
+ CONTROL "Keypad (1-9)",IDC_SELECT2,"Button",BS_AUTORADIOBUTTON,
+ 21,99,75,10
+ CONTROL "Either (1-9)",IDC_SELECT3,"Button",BS_AUTORADIOBUTTON,
+ 21,110,82,10
+ CONTROL "Function keys (F1-F12)",IDC_SELECT4,"Button",
+ BS_AUTORADIOBUTTON,21,121,87,10
+ GROUPBOX "&Tone keys",IDC_TONES,115,77,102,59,WS_GROUP
+ CONTROL "Never typed",IDC_TONE1,"Button",BS_AUTORADIOBUTTON |
+ WS_TABSTOP,124,88,64,10
+ CONTROL "Must be typed",IDC_TONE2,"Button",BS_AUTORADIOBUTTON,
+ 124,99,64,10
+ CONTROL "Optional",IDC_TONE3,"Button",BS_AUTORADIOBUTTON,124,110,
+ 64,10
+ CONTROL "Auto-select if &unique",IDC_SELECTUNIQUE,"Button",
+ BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,21,137,80,10
+ CONTROL "Show &duplicates",IDC_SHOWDUPLICATES,"Button",
+ BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_GROUP | WS_TABSTOP,
+ 124,137,68,10
+ DEFPUSHBUTTON "OK",IDOK,230,14,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,230,35,50,14
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ "CONFIGURE", DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 280
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 152
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+LOGO ICON DISCARDABLE "icon1.ico"
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 6,0,161,0
+ PRODUCTVERSION 6,0,161,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "0c0904b0"
+ BEGIN
+ VALUE "Comments", "\0"
+ VALUE "CompanyName", "Tavultesoft\0"
+ VALUE "FileDescription", "KeymanIMX Configuration\0"
+ VALUE "FileVersion", "6.0.161.0\0"
+ VALUE "InternalName", "KeymanIMX Configuration\0"
+ VALUE "LegalCopyright", "Copyright © 2003 Tavultesoft\0"
+ VALUE "LegalTrademarks", "\0"
+ VALUE "OriginalFilename", "imxconfig\0"
+ VALUE "PrivateBuild", "\0"
+ VALUE "ProductName", "Tavultesoft KeymanIMX Configuration\0"
+ VALUE "ProductVersion", "6.0.161.0\0"
+ VALUE "SpecialBuild", "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0xc09, 1200
+ END
+END
+
+#endif // !_MAC
+
+#endif // English (Australia) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/release/c/cs_pinyin/source/imxconfig/imxconfig.sln b/release/c/cs_pinyin/source/imxconfig/imxconfig.sln
new file mode 100644
index 0000000000..f6d2a9c5ef
--- /dev/null
+++ b/release/c/cs_pinyin/source/imxconfig/imxconfig.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26430.12
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "imxconfig", "imxconfig.vcxproj", "{4F2ED8DE-8B20-46E4-96F5-F8DD0BF26504}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x86 = Debug|x86
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {4F2ED8DE-8B20-46E4-96F5-F8DD0BF26504}.Debug|x86.ActiveCfg = Debug|Win32
+ {4F2ED8DE-8B20-46E4-96F5-F8DD0BF26504}.Debug|x86.Build.0 = Debug|Win32
+ {4F2ED8DE-8B20-46E4-96F5-F8DD0BF26504}.Release|x86.ActiveCfg = Release|Win32
+ {4F2ED8DE-8B20-46E4-96F5-F8DD0BF26504}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/release/c/cs_pinyin/source/imxconfig/imxconfig.vcxproj b/release/c/cs_pinyin/source/imxconfig/imxconfig.vcxproj
new file mode 100644
index 0000000000..1a1f593547
--- /dev/null
+++ b/release/c/cs_pinyin/source/imxconfig/imxconfig.vcxproj
@@ -0,0 +1,295 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ Win32
+
+
+ Release
+ x64
+
+
+
+ {4F2ED8DE-8B20-46E4-96F5-F8DD0BF26504}
+ 10.0
+
+
+
+ Application
+ false
+ MultiByte
+ v142
+
+
+ Application
+ false
+ MultiByte
+ v142
+
+
+ Application
+ false
+ MultiByte
+ v142
+
+
+ Application
+ false
+ MultiByte
+ v142
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_ProjectFileVersion>10.0.30319.1
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ true
+ true
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ false
+ false
+ AllRules.ruleset
+ AllRules.ruleset
+
+
+
+
+ AllRules.ruleset
+ AllRules.ruleset
+
+
+
+
+
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ Win32
+ .\Debug/imxconfig.tlb
+
+
+
+
+ Disabled
+ WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+ EnableFastChecks
+ MultiThreadedDebug
+ .\Debug/imxconfig.pch
+ $(outdir)\
+ $(outdir)\
+ $(outdir)\
+ Level3
+ true
+ EditAndContinue
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ 0x0c09
+
+
+ odbc32.lib;odbccp32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;%(AdditionalDependencies)
+ true
+ true
+ $(OutDir)$(TargetName).pdb
+ Windows
+ MachineX86
+ false
+
+
+ true
+ .\Debug/imxconfig.bsc
+
+
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ .\Debug/imxconfig.tlb
+
+
+
+
+ Disabled
+ WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ EnableFastChecks
+ MultiThreadedDebug
+ .\Debug/imxconfig.pch
+ $(outdir)\
+ $(outdir)\
+ $(outdir)\
+ Level3
+ true
+ ProgramDatabase
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ 0x0c09
+
+
+ odbc32.lib;odbccp32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;%(AdditionalDependencies)
+ $(OutDir)$(TargetName)$(TargetExt)
+ true
+ true
+ $(OutDir)$(TargetName).pdb
+ Windows
+
+
+ true
+ .\Debug/imxconfig.bsc
+
+
+
+
+
+
+
+
+
+
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ Win32
+ .\Release/imxconfig.tlb
+
+
+
+
+ MaxSpeed
+ OnlyExplicitInline
+ WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+ MultiThreaded
+ true
+ .\Release/imxconfig.pch
+ $(outdir)\
+ $(outdir)\
+ $(outdir)\
+ Level3
+ true
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ 0x0c09
+
+
+ odbc32.lib;odbccp32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;%(AdditionalDependencies)
+ true
+ $(OutDir)$(TargetName).pdb
+ Windows
+ MachineX86
+
+
+ true
+ .\Release/imxconfig.bsc
+
+
+
+
+ Signing $(ProjectPath)
+ signtool sign /f "s:\tavultesoft\certificates\tavultesoft.pfx" /t "http://timestamp.verisign.com/scripts/timstamp.dll" /du "http://www.tavultesoft.com/" /v $(TargetPath)
+ $(TargetName);%(Outputs)
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ .\Release/imxconfig.tlb
+
+
+
+
+ MaxSpeed
+ OnlyExplicitInline
+ WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+ MultiThreaded
+ true
+ .\Release/imxconfig.pch
+ $(outdir)\
+ $(outdir)\
+ $(outdir)\
+ Level3
+ true
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ 0x0c09
+
+
+ odbc32.lib;odbccp32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;%(AdditionalDependencies)
+ true
+ $(OutDir)$(TargetName).pdb
+ Windows
+
+
+ true
+ .\Release/imxconfig.bsc
+
+
+
+
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+
+
+
+
+
+
+
+
+
+
+
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+
+
+
+
+
+
\ No newline at end of file
diff --git a/release/c/cs_pinyin/source/imxconfig/imxconfig.vcxproj.filters b/release/c/cs_pinyin/source/imxconfig/imxconfig.vcxproj.filters
new file mode 100644
index 0000000000..c0b832379d
--- /dev/null
+++ b/release/c/cs_pinyin/source/imxconfig/imxconfig.vcxproj.filters
@@ -0,0 +1,40 @@
+
+
+
+
+ {6da7e89f-d321-46c1-b511-283f60162c90}
+ cpp;c;cxx;rc;def;r;odl;idl;hpj;bat
+
+
+ {cc000632-2215-4a12-9526-300de206ce1a}
+ h;hpp;hxx;hm;inl
+
+
+ {a0eb051e-7826-4b33-b3f3-535092cc71d6}
+ ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe
+
+
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+
+
+ Resource Files
+
+
+
\ No newline at end of file
diff --git a/release/c/cs_pinyin/source/imxconfig/resource.h b/release/c/cs_pinyin/source/imxconfig/resource.h
new file mode 100644
index 0000000000..5f2f6aaeff
--- /dev/null
+++ b/release/c/cs_pinyin/source/imxconfig/resource.h
@@ -0,0 +1,34 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by imxconfig.rc
+//
+#define IDD_DIALOG1 101
+#define IDC_HORIZONTAL 1000
+#define IDC_VERTICAL 1001
+#define IDC_TRACK 1002
+#define IDC_KBDNAME 1006
+#define IDC_SELECT1 1012
+#define IDC_SELECT2 1013
+#define IDC_SELECT3 1014
+#define IDC_SELECT4 1015
+#define IDC_TONE1 1017
+#define IDC_TONE2 1018
+#define IDC_TONE3 1019
+#define IDC_TONES 1020
+#define IDC_SELECTUNIQUE 1021
+#define IDC_SELECTION 1022
+#define IDC_ACTIVE1 1023
+#define IDC_SHOWDUPLICATES 1024
+#define IDC_MATCH1 1025
+#define IDC_MATCH2 1026
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 104
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1026
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/release/c/cs_pinyin/source/imxreload/Key.bmp b/release/c/cs_pinyin/source/imxreload/Key.bmp
new file mode 100644
index 0000000000..a0bc2999b9
Binary files /dev/null and b/release/c/cs_pinyin/source/imxreload/Key.bmp differ
diff --git a/release/c/cs_pinyin/source/imxreload/icon1.ico b/release/c/cs_pinyin/source/imxreload/icon1.ico
new file mode 100644
index 0000000000..55b46264be
Binary files /dev/null and b/release/c/cs_pinyin/source/imxreload/icon1.ico differ
diff --git a/release/c/cs_pinyin/source/imxreload/imxreload.cpp b/release/c/cs_pinyin/source/imxreload/imxreload.cpp
new file mode 100644
index 0000000000..d82d86b5af
--- /dev/null
+++ b/release/c/cs_pinyin/source/imxreload/imxreload.cpp
@@ -0,0 +1,588 @@
+#include
+#include
+#include
+#include
+
+#include "resource.h"
+#include "../keymanimx/keymanimx.h"
+
+#define MAXWAIT 60
+
+DWORD GetCRC32(LPVOID pv,UINT cb);
+
+PSTR CAPTION="Keyman IMX Configuration Compiler";
+
+KBHEADER Keyboard={0};
+HWND hDlg;
+
+DWORD Index[MAXINDEX];
+PSTR IndexString=INDEXSTRING;
+char CFGPath[MAX_PATH]={0}, CFXPath[MAX_PATH]={0};
+
+int GetLogPixelsY()
+{
+ HDC hDC = GetDC(GetDesktopWindow());
+ SetMapMode(hDC, MM_TEXT);
+ int lfpixelsy = GetDeviceCaps(hDC, LOGPIXELSY);
+ ReleaseDC(GetDesktopWindow(), hDC);
+ return lfpixelsy;
+}
+
+void SetFont(LPLOGFONT plf, LPSTR name, int size, int style)
+{
+ plf->lfHeight = -MulDiv(size, GetLogPixelsY(), 72);
+ plf->lfWidth = 0;
+ plf->lfEscapement = 0;
+ plf->lfOrientation = 0;
+ plf->lfWeight = (style & FS_BOLD) ? FW_BOLD : FW_NORMAL;
+ plf->lfItalic = (style & FS_ITALIC) ? TRUE: FALSE;
+ plf->lfUnderline = FALSE;
+ plf->lfStrikeOut = FALSE;
+// plf->lfCharSet = DEFAULT_CHARSET; //SHIFTJIS_CHARSET;
+ plf->lfCharSet = (style & FC_SYMBOL) ? SYMBOL_CHARSET : 0;
+ plf->lfOutPrecision = OUT_DEFAULT_PRECIS;
+ plf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ plf->lfQuality = DEFAULT_QUALITY;
+ plf->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+ strcpy_s(plf->lfFaceName, sizeof plf->lfFaceName, name);
+}
+
+void AdjustFont(LPLOGFONT plf, PSTR q, PSTR sz, PSTR st)
+{
+ int n;
+
+ if(!q) return;
+
+ strncpy_s(plf->lfFaceName, sizeof plf->lfFaceName, q, LF_FACESIZE-1);
+ plf->lfFaceName[LF_FACESIZE-1] = 0;
+ if(sz)
+ {
+ if((n=atoi(sz)) <= 0) n = 10;
+ plf->lfHeight = -MulDiv(n, GetLogPixelsY(), 72);
+ }
+ if(st)
+ {
+ if((n=atoi(st)) < 0) n = 0;
+ plf->lfWeight = (n & FS_BOLD ? FW_BOLD : FW_NORMAL);
+ plf->lfItalic = (n & FS_ITALIC ? TRUE : FALSE);
+ plf->lfCharSet = (n & FC_SYMBOL ? SYMBOL_CHARSET : 0);
+ }
+}
+
+BOOL unpackstr(PWCHAR p,PWCHAR pw,int pwlen)
+{
+ WCHAR qt=0, *pq;
+ int tlen=0;
+
+ while(*p)
+ {
+ switch(*p)
+ {
+ case ' ':
+ p++;
+ break;
+ case 34:
+ case 39:
+ qt = *p++;
+ pq = wcschr(p,qt);
+ if((!pq) || (tlen+pq-p >= pwlen)) return FALSE;
+ wcsncpy_s(pw+tlen,pwlen-tlen,p,pq-p);
+ tlen += (int)(pq-p);
+ p = pq+1;
+ break;
+ case 'U':
+ case 'u':
+ p++;
+ if(*p != '+') return FALSE;
+ case 'X':
+ case 'x':
+ p++;
+ *(pw+tlen) = (WORD)wcstoul(p, &p, 16);
+ tlen++;
+ break;
+ case 'd':
+ case 'D':
+ p++;
+ *(pw+tlen) = (WORD)wcstoul(p, &p, 10);
+ tlen++;
+ break;
+ default:
+ return FALSE;
+ }
+ if(tlen >= pwlen-1) break;
+ }
+ *(pw+tlen) = 0;
+ return TRUE;
+}
+
+void UpdateMeter(int x)
+{
+ SendDlgItemMessage(hDlg,IDC_RELOADPROGRESS,PBM_SETPOS,x,0);
+}
+
+void SetMeterRange(int xmin,int xmax)
+{
+ SendDlgItemMessage(hDlg,IDC_RELOADPROGRESS,PBM_SETRANGE32,xmin,xmax);
+}
+
+void SetMeterCaption(PSTR pLabel, PSTR pText)
+{
+ char buf[256];
+ wsprintf(buf,"%s %s",pLabel,pText);
+ SetDlgItemText(hDlg,IDC_KBDNAME,buf);
+}
+
+int ParseCFG(KBHEADER *kbi, char *cfgfile, int *psize, FILE *ftemp)
+{
+ FILE *fp, *ft0;
+ BOOL FGroups=FALSE, FRules=FALSE, FConfig=FALSE;
+ WCHAR wc1, wstr[512];
+ PWSTR pinput, poutput, ptag;
+ int k, k1, n, nread=0, nrules=0, Config=CFG_NONE;
+ char q0, *p, *q, *sz, *st, str[512];
+ PBYTE pBOM=(PBYTE)&str[0];
+ TEMPRULE tmprule;
+
+ // TODO: Consider doing all this in memory and avoiding temp files
+ tmpfile_s(&ft0);
+
+ // Set defaults
+ strncpy_s(kbi->Flag, 5, "KCFX", 4);
+ kbi->dwCfxVersion = CFXVERSION;
+ kbi->nrules = 0;
+ kbi->gridx = CELL_X;
+ kbi->gridy = CELL_Y;
+ kbi->dwFKeys = 1; // allow use of function keys
+ kbi->dwOptions = SM_FKEYS | (TM_OPTIONAL<<2); // use function keys by default, optional tones
+ *kbi->ToneChar = 0;
+ kbi->cfgKey = MAKELONG(0xC0,0x11); //default configuration hotkey is LCtrl Shift Backquote
+
+ // Set default font properties
+ SetFont(&kbi->lfMain, "Arial Unicode MS", 10, FS_NONE);
+ SetFont(&kbi->lfInput, "MS Sans Serif", 8, FS_BOLD);
+ SetFont(&kbi->lfTag, "MS Sans Serif", 8, FS_NONE);
+
+ *psize = nrules = 0;
+
+ // Open the CFG file, abort on error
+ if(fopen_s(&fp, cfgfile, "rb") != 0) return FALSE;
+
+ // must determine whether or not the config file is Unicode
+ fread(pBOM,4,1,fp);
+
+ if(*pBOM == '[')
+ {
+ Config = CFG_ANSI; fseek(fp,0,SEEK_SET);
+ }
+ else if((*pBOM == 0xEF) && (*(pBOM+1) == 0xBB) && (*(pBOM+2) == 0xBF))
+ {
+ Config = CFG_UTF8; fseek(fp,3,SEEK_SET);
+ }
+ else if((*pBOM == 0xFF) && (*(pBOM+1) == 0xFE))
+ {
+ Config = CFG_UNICODE; fseek(fp,2,SEEK_SET);
+ }
+
+ else return FALSE;
+
+ if(ftemp)
+ {
+ SetMeterRange(0,3*_filelength(_fileno(fp)));
+ SetMeterCaption("Parsing rules for ",kbi->name);
+ }
+
+ do
+ {
+ if(Config == CFG_ANSI)
+ {
+ if(!fgets(str, 512, fp)) break;
+ MultiByteToWideChar(CP_ACP, 0, str, -1, wstr, 512);
+ }
+ else if(Config == CFG_UTF8)
+ {
+ if(!fgets(str, 512, fp)) break;
+ MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, 512);
+ }
+ else
+ {
+ if(!fgetws(wstr, 512, fp)) break;
+ }
+
+ if(wcschr(wstr, L'\r')) *wcschr(wstr, L'\r') = 0;
+ if(wcschr(wstr, L'\n')) *wcschr(wstr, L'\n') = 0;
+
+ if(wstr[0] == L'#' || wstr[0] == 0) continue;
+
+ if(wstr[0] == L'[')
+ {
+ FConfig = FALSE; FRules = FALSE;
+ if(!_wcsicmp(wstr, L"[config]")) FConfig = TRUE;
+ else if(!_wcsicmp(wstr, L"[rules]")) FRules = TRUE;
+ }
+ else if(FConfig)
+ {
+ WideCharToMultiByte( CP_ACP, 0, wstr, -1, str, 512, NULL, NULL );
+
+ char *sp;
+ p = strtok_s(str, "= ", &sp); if(!p) continue;
+ q = strtok_s(NULL, ",\r\n", &sp); if(!q) continue;
+ sz = strtok_s(NULL, ",\r\n", &sp);
+ st = strtok_s(NULL, ",\r\n", &sp);
+
+ if(!_stricmp(p, "Name")) // this must match, if a keyboard is selected.
+ strcpy_s(kbi->name, sizeof kbi->name, q);
+ else if(!_stricmp(p, "MainFont"))
+ AdjustFont( &kbi->lfMain, q, sz, st);
+ else if(!_stricmp(p, "InputFont"))
+ AdjustFont( &kbi->lfInput, q, sz, st);
+ else if(!_stricmp(p, "TagFont"))
+ AdjustFont( &kbi->lfTag, q, sz, st);
+ else if(!_stricmp(p, "Options"))
+ kbi->dwOptions = atoi(q);
+ else if(!_stricmp(p, "FKeys"))
+ kbi->dwFKeys = atoi(q);
+ else if(!_stricmp(p, "Wild") || !_stricmp(p,"WildCard"))
+ {
+ if(strchr(q,'?')) kbi->dwWild |= WC_QUERY;
+ if(strchr(q,'*')) kbi->dwWild |= WC_STAR;
+ if(strchr(q,'\'')) kbi->dwWild |= WC_QUOTE;
+ if(strchr(q,'-')) kbi->dwWild |= WC_DASH;
+ }
+ else if(!_stricmp(p, "Input"))
+ {
+ k = atoi(q); kbi->InputSize = min(max(k,16),100);
+ }
+ else if(!_stricmp(p, "Grid") || !_stricmp(p,"Cell"))
+ {
+ kbi->gridx = atoi(q);
+ if(sz) kbi->gridy = atoi(sz);
+ else kbi->gridy = kbi->gridx;
+ }
+ else if(!_stricmp(p, "ToneChar") || !_stricmp(p, "ToneChars"))
+ strcpy_s(kbi->ToneChar,sizeof kbi->ToneChar, q);
+ else if(!_stricmp(p,"CfgHotKey"))
+ kbi->cfgKey = MAKELONG(atoi(q),atoi(sz));
+ }
+ else if(FRules)
+ {
+ // exit if just reading configuration parameters
+ if(ftemp == NULL) break;
+
+ // parsing will not allow commas in strings at present
+ wchar_t *wp;
+ pinput = wcstok_s(wstr, L",\t", &wp); if(!pinput) continue;
+ poutput = wcstok_s(NULL, L",\t\n", &wp); if(!poutput) continue;
+ ptag = wcstok_s(NULL, L",\t\n", &wp);
+
+ // copy the rules to a temporary file and determine the memory needed
+ ZeroMemory(&tmprule,sizeof tmprule);
+ if(!unpackstr(pinput,tmprule.input,sizeof(tmprule.input)/sizeof(WCHAR))) continue;
+ if(!unpackstr(poutput,tmprule.output,sizeof(tmprule.output)/sizeof(WCHAR))) continue;
+ *psize += (int)(2*(wcslen(tmprule.input) + wcslen(tmprule.output)));
+ if(ptag)
+ {
+ unpackstr(ptag,tmprule.tag,sizeof(tmprule.tag)/sizeof(WCHAR));
+ *psize += 2*(int)wcslen(tmprule.tag);
+ }
+ fwrite(&tmprule,sizeof tmprule,1,ft0);
+
+ nrules++;
+
+ }
+ if(nrules % 10 == 0) UpdateMeter(ftell(fp));
+ } while(TRUE);
+
+ fclose(fp);
+
+ kbi->nrules = nrules;
+
+ // Now merge the rules and index them
+ if(ftemp)
+ {
+ k1=(int)strlen(IndexString);
+ SetMeterRange(-k1,2*k1); UpdateMeter(0);
+ SetMeterCaption("Sorting rules for ",kbi->name);
+
+ // add all indexed rules
+ for(k=n=0; kdwStrings;
+ p1 = p0 + pk->cbStrings;
+
+
+ for(p=p0,n=0; pname, sizeof kbi->name, NULL, 0);
+
+ // Read the Name configuration field, if any
+ if(!ParseCFG(kbi,CFGPath,&stsize,NULL))
+ {
+ MessageBox(hwnd,"Invalid configuration file!",CAPTION,MB_ICONSTOP);
+ return FALSE;
+ }
+
+ wsprintf(buf,"Reloading %s",kbi->name);
+ SetDlgItemText(hwnd,IDC_KBDNAME,buf);
+ EnableDlgItem(hwnd,IDC_KBDNAME);
+ EnableDlgItem(hwnd,IDC_RELOADPROGRESS);
+ DisableDlgItem(hwnd,IDOK);
+ DisableDlgItem(hwnd,IDCANCEL);
+
+ wsprintf(buf,"Reloading %s failed!",kbi->name);
+
+ _makepath_s(CFXPath, sizeof CFXPath, drive, dir, kbi->name, ".cfx");
+
+ // Build the temporary file
+ if(ParseCFG(kbi,CFGPath,&stsize,ftemp) && (kbi->nrules > 0))
+ {
+ nrules = kbi->nrules;
+
+ fseek(ftemp,0,SEEK_SET);
+
+ kbi->dwIndex = sizeof(KBHEADER);
+ kbi->dwRules = kbi->dwIndex + (sizeof Index);
+ kbi->dwStrings = kbi->dwRules + (nrules+1)*sizeof(RULE);
+ kbi->cbStrings = stsize;
+
+ totalsize = kbi->dwStrings + kbi->cbStrings;
+
+ hFile = CreateFile(CFXPath,GENERIC_READ+GENERIC_WRITE,FILE_SHARE_READ+FILE_SHARE_WRITE,NULL,
+ OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);
+
+ if(hFile != INVALID_HANDLE_VALUE)
+ {
+ // Create an un-named temporary file-mapping object (allow for checksum)
+ hMapObject = CreateFileMapping(hFile,NULL,PAGE_READWRITE,0,totalsize+4,NULL);
+ if(hMapObject != NULL)
+ {
+ SetMeterRange(-2*nrules,nrules); UpdateMeter(0);
+ SetMeterCaption("Compiling",kbi->name);
+
+
+ // Get a pointer to file-mapped shared memory
+ pbmm = (PBYTE)MapViewOfFile(hMapObject,FILE_MAP_WRITE,0,0,0);
+ if( pbmm != NULL )
+ {
+ // Copy the loaded keyboard parameters to the memory mapped file
+ *(KBHEADER *)pbmm = *kbi;
+
+ ip0 = (DWORD *)(pbmm + sizeof(KBHEADER));
+
+ // Copy the index
+ memcpy(ip0,&Index[0],sizeof Index);
+
+ // Copy the rules
+ rp = rp0 = (RULE *)(pbmm + sizeof(KBHEADER) + (sizeof Index));
+ pw = pw0 = (PWCHAR)(rp + nrules + 1);
+
+ for(i=0; iilen = (BYTE)wcslen(tmprule.input);
+ rp->olen = (BYTE)wcslen(tmprule.output);
+ rp->tlen = (BYTE)wcslen(tmprule.tag);
+ rp->input = (DWORD)(pw-pw0);
+ wcscpy_s(pw0+rp->input, rp->ilen + 1, tmprule.input); pw += rp->ilen;
+ rp->output = (DWORD)(pw-pw0);
+ wcscpy_s(pw0+rp->output, rp->olen + 1, tmprule.output); pw += rp->olen;
+ rp->tag = (DWORD)(pw-pw0);
+ wcscpy_s(pw0+rp->tag, rp->tlen + 1, tmprule.tag); pw += rp->tlen;
+ if(!*tmprule.tag) rp->tag = rp->input;
+ }
+
+ // And encrypt the string table
+ EncryptStrings((KBHEADER *)pbmm);
+
+ // Calculate the checksum
+ dwChecksum = GetCRC32(pbmm,totalsize);
+ memcpy(pbmm+totalsize,&dwChecksum,4);
+
+ // Unmap the memory object
+ UnmapViewOfFile(pbmm);
+
+ wsprintf(buf,"%s recompiled, %d rules processed",kbi->name,kbi->nrules);
+ }
+ CloseHandle(hMapObject);
+ }
+ SetFileToCurrentTime(hFile);
+ CloseHandle(hFile);
+ }
+ }
+
+ _rmtmp();
+
+ SetDlgItemText(hwnd,IDC_KBDNAME,buf);
+
+ DisableDlgItem(hwnd,IDC_RELOADPROGRESS);
+ EnableDlgItem(hwnd,IDOK);
+ EnableDlgItem(hwnd,IDCANCEL);
+
+ return (nrules > 0);
+}
+
+INT_PTR CALLBACK IMXRebuildProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ hDlg = hwnd;
+ DisableDlgItem(hwnd,IDC_RELOADPROGRESS);
+ DisableDlgItem(hwnd,IDC_KBDNAME);
+
+ return TRUE;
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam))
+ {
+ case IDOK:
+ RebuildAll(hwnd,&Keyboard);
+ break;
+ case IDCANCEL:
+ EndDialog(hwnd,0);
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int cmdShow)
+{
+ INITCOMMONCONTROLSEX Controls;
+ HANDLE hMutex=0;
+
+ char buf[MAX_PATH]={0};
+
+ hMutex = CreateMutex(NULL,FALSE,"KeymanIMXRebuildMutex");
+ if(hMutex == NULL || GetLastError() == ERROR_ALREADY_EXISTS) return 0;
+
+ Controls.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ Controls.dwICC = ICC_PROGRESS_CLASS;
+ InitCommonControlsEx(&Controls);
+
+ DialogBox(hInst,"CONFIGURE",NULL,IMXRebuildProc);
+
+ return 0;
+}
+
+
diff --git a/release/c/cs_pinyin/source/imxreload/imxreload.rc b/release/c/cs_pinyin/source/imxreload/imxreload.rc
new file mode 100644
index 0000000000..04be835a29
--- /dev/null
+++ b/release/c/cs_pinyin/source/imxreload/imxreload.rc
@@ -0,0 +1,157 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (Australia) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+CONFIGURE DIALOG DISCARDABLE 0, 0, 205, 62
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "Keyman IMX Configuration Compiler"
+FONT 8, "MS Sans Serif"
+BEGIN
+ CTEXT "Rebuilding ",IDC_KBDNAME,7,7,191,8,
+ WS_DISABLED | NOT WS_GROUP
+ CONTROL "Progress1",IDC_RELOADPROGRESS,"msctls_progress32",
+ PBS_SMOOTH | WS_DISABLED | WS_BORDER,14,20,177,11
+ DEFPUSHBUTTON "Rebuild",IDOK,41,40,50,14
+ PUSHBUTTON "Close",IDCANCEL,114,40,50,14
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ "CONFIGURE", DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 198
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 55
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+LOGO ICON DISCARDABLE "icon1.ico"
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 6,0,161,1
+ PRODUCTVERSION 6,0,161,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "0c0904b0"
+ BEGIN
+ VALUE "Comments", "\0"
+ VALUE "CompanyName", "Tavultesoft\0"
+ VALUE "FileDescription", "KeymanIMX Compiler\0"
+ VALUE "FileVersion", "6.0.161.1\0"
+ VALUE "InternalName", "KeymanIMX Compiler\0"
+ VALUE "LegalCopyright", "Copyright © 2003 Tavultesoft\0"
+ VALUE "LegalTrademarks", "\0"
+ VALUE "OriginalFilename", "imxreload\0"
+ VALUE "PrivateBuild", "\0"
+ VALUE "ProductName", "Tavultesoft KeymanIMX Compiler\0"
+ VALUE "ProductVersion", "6.0.161.1\0"
+ VALUE "SpecialBuild", "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0xc09, 1200
+ END
+END
+
+#endif // !_MAC
+
+#endif // English (Australia) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/release/c/cs_pinyin/source/imxreload/imxreload.sln b/release/c/cs_pinyin/source/imxreload/imxreload.sln
new file mode 100644
index 0000000000..c4a1f0f750
--- /dev/null
+++ b/release/c/cs_pinyin/source/imxreload/imxreload.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26430.12
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "imxreload", "imxreload.vcxproj", "{838D3D6C-F6E5-461E-ABF1-DC7053AC77D2}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {838D3D6C-F6E5-461E-ABF1-DC7053AC77D2}.Debug|x64.ActiveCfg = Debug|x64
+ {838D3D6C-F6E5-461E-ABF1-DC7053AC77D2}.Debug|x64.Build.0 = Debug|x64
+ {838D3D6C-F6E5-461E-ABF1-DC7053AC77D2}.Debug|x86.ActiveCfg = Debug|Win32
+ {838D3D6C-F6E5-461E-ABF1-DC7053AC77D2}.Debug|x86.Build.0 = Debug|Win32
+ {838D3D6C-F6E5-461E-ABF1-DC7053AC77D2}.Release|x64.ActiveCfg = Release|x64
+ {838D3D6C-F6E5-461E-ABF1-DC7053AC77D2}.Release|x64.Build.0 = Release|x64
+ {838D3D6C-F6E5-461E-ABF1-DC7053AC77D2}.Release|x86.ActiveCfg = Release|Win32
+ {838D3D6C-F6E5-461E-ABF1-DC7053AC77D2}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/release/c/cs_pinyin/source/imxreload/imxreload.vcxproj b/release/c/cs_pinyin/source/imxreload/imxreload.vcxproj
new file mode 100644
index 0000000000..6296bd19ad
--- /dev/null
+++ b/release/c/cs_pinyin/source/imxreload/imxreload.vcxproj
@@ -0,0 +1,300 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ Win32
+
+
+ Release
+ x64
+
+
+
+ {838D3D6C-F6E5-461E-ABF1-DC7053AC77D2}
+ 10.0
+
+
+
+ Application
+ false
+ MultiByte
+ v142
+
+
+ Application
+ false
+ MultiByte
+ v142
+
+
+ Application
+ false
+ MultiByte
+ v142
+
+
+ Application
+ false
+ MultiByte
+ v142
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_ProjectFileVersion>10.0.30319.1
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ true
+ true
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ false
+ false
+ AllRules.ruleset
+ AllRules.ruleset
+
+
+
+
+ AllRules.ruleset
+ AllRules.ruleset
+
+
+
+
+
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ Win32
+ .\Debug/imxreload.tlb
+
+
+
+
+ Disabled
+ WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+ EnableFastChecks
+ MultiThreadedDebug
+ $(outdir)\imxreload.pch
+ $(outdir)\
+ $(outdir)\
+ $(outdir)\
+ Level3
+ true
+ EditAndContinue
+ StdCall
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ 0x0c09
+
+
+ odbc32.lib;odbccp32.lib;comctl32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;%(AdditionalDependencies)
+ true
+ true
+ $(outdir)\$(MSBuildProjectName).pdb
+ Windows
+ MachineX86
+ false
+
+
+ true
+ .\Debug/imxreload.bsc
+
+
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ .\Debug/imxreload.tlb
+
+
+
+
+ Disabled
+ WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ EnableFastChecks
+ MultiThreadedDebug
+ $(outdir)\imxreload.pch
+ $(outdir)\
+ $(outdir)\
+ $(outdir)\
+ Level3
+ true
+ ProgramDatabase
+ StdCall
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ 0x0c09
+
+
+ odbc32.lib;odbccp32.lib;comctl32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;%(AdditionalDependencies)
+ $(OutDir)$(TargetName)$(TargetExt)
+ true
+ true
+ $(outdir)\$(MSBuildProjectName).pdb
+ Windows
+
+
+ true
+ .\Debug/imxreload.bsc
+
+
+
+
+
+
+
+
+
+
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ Win32
+ .\Release/imxreload.tlb
+
+
+
+
+ MaxSpeed
+ OnlyExplicitInline
+ WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+ MultiThreaded
+ true
+ $(outdir)\imxreload.pch
+ $(outdir)\
+ $(outdir)\
+ $(outdir)\
+ Level3
+ true
+ StdCall
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ 0x0c09
+
+
+ odbc32.lib;odbccp32.lib;comctl32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;%(AdditionalDependencies)
+ true
+ $(outdir)\$(MSBuildProjectName).pdb
+ Windows
+ MachineX86
+
+
+ true
+ .\Release/imxreload.bsc
+
+
+
+
+ Signing $(ProjectPath)
+ signtool sign /f "s:\tavultesoft\certificates\tavultesoft.pfx" /t "http://timestamp.verisign.com/scripts/timstamp.dll" /du "http://www.tavultesoft.com/" /v $(TargetPath)
+ $(TargetName);%(Outputs)
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ .\Release/imxreload.tlb
+
+
+
+
+ MaxSpeed
+ OnlyExplicitInline
+ WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+ MultiThreaded
+ true
+ $(outdir)\imxreload.pch
+ $(outdir)\
+ $(outdir)\
+ $(outdir)\
+ Level3
+ true
+ StdCall
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ 0x0c09
+
+
+ odbc32.lib;odbccp32.lib;comctl32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;%(AdditionalDependencies)
+ true
+ $(outdir)\$(MSBuildProjectName).pdb
+ Windows
+
+
+ true
+ .\Release/imxreload.bsc
+
+
+
+
+
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+
+
+
+
+
+
+
+
+
+
+
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+
+
+
+
+
+
\ No newline at end of file
diff --git a/release/c/cs_pinyin/source/imxreload/imxreload.vcxproj.filters b/release/c/cs_pinyin/source/imxreload/imxreload.vcxproj.filters
new file mode 100644
index 0000000000..aa644fc57c
--- /dev/null
+++ b/release/c/cs_pinyin/source/imxreload/imxreload.vcxproj.filters
@@ -0,0 +1,43 @@
+
+
+
+
+ {577d83e9-7cf8-4ab8-86a1-1138a859b171}
+ cpp;c;cxx;rc;def;r;odl;idl;hpj;bat
+
+
+ {a4e96bdb-b00a-4e7e-b775-98fb4291d586}
+ h;hpp;hxx;hm;inl
+
+
+ {f7385744-043f-4d65-a11f-54612ca8d94e}
+ ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+
+
+ Resource Files
+
+
+
\ No newline at end of file
diff --git a/release/c/cs_pinyin/source/imxreload/resource.h b/release/c/cs_pinyin/source/imxreload/resource.h
new file mode 100644
index 0000000000..5a2649c5b2
--- /dev/null
+++ b/release/c/cs_pinyin/source/imxreload/resource.h
@@ -0,0 +1,36 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by imxreload.rc
+//
+#define IDD_DIALOG1 101
+#define IDC_HORIZONTAL 1000
+#define IDC_VERTICAL 1001
+#define IDC_TRACK 1002
+#define IDC_KBDNAME 1006
+#define IDC_RELOAD 1008
+#define IDC_RELOADPROGRESS 1010
+#define IDC_SELECT1 1012
+#define IDC_SELECT2 1013
+#define IDC_SELECT3 1014
+#define IDC_SELECT4 1015
+#define IDC_TONE1 1017
+#define IDC_TONE2 1018
+#define IDC_TONE3 1019
+#define IDC_TONES 1020
+#define IDC_SELECTUNIQUE 1021
+#define IDC_SELECTION 1022
+#define IDC_ACTIVE2 1023
+#define IDC_ACTIVE1 1024
+#define IDC_MATCH1 1025
+#define IDC_MATCH2 1026
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 103
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1026
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/release/c/cs_pinyin/source/keymanimx/KeymanIMX.cpp b/release/c/cs_pinyin/source/keymanimx/KeymanIMX.cpp
new file mode 100644
index 0000000000..4e296229f6
--- /dev/null
+++ b/release/c/cs_pinyin/source/keymanimx/KeymanIMX.cpp
@@ -0,0 +1,2061 @@
+#define STRICT
+
+#include
+#include
+#include
+#include "imxlib.h"
+#include "keymanimx.h"
+
+#define WS_EX_NOACTIVATE 0x08000000L
+#define WM_REDRAW (WM_USER+200)
+
+#define ELLIPSIS 0x2026 // ellipsis (Unicode) character
+
+//#define TESTING
+
+#ifdef TESTING
+#define returnErr(value) {Log("Error at line ",__LINE__,GetLastError());return(value);}
+char tdbg[256];
+void dbgLog(void)
+{
+ FILE *fp;
+
+ if((fp=fopen("c:\\Windows\\Temp\\KeymanIMX.log", "at")))
+ {
+ fprintf(fp, "%s\n",tdbg);
+ fclose(fp);
+ }
+}
+
+// Overload the debug reporter to simplify use
+void Log(char *s)
+{
+ wsprintf(tdbg, "%s\n",s);
+ dbgLog();
+}
+void Log(char *s, char *t)
+{
+ wsprintf(tdbg, "%s %s\n",s,t);
+ dbgLog();
+}
+void Log(char *s, char *t, int n)
+{
+ wsprintf(tdbg, "%s %s (%x)\n",s,t,n);
+ dbgLog();
+}
+void Log(char *s, int n)
+{
+ wsprintf(tdbg, "%s %d (%x)\n",s,n,n);
+ dbgLog();
+}
+void Log(char *s, int m, int n)
+{
+ wsprintf(tdbg, "%s %d (%x)\n",s,m,n);
+ dbgLog();
+}
+void Log(PWCHAR s, PWCHAR t)
+{
+ char s1[48], t1[48];
+ WideCharToMultiByte(CP_ACP,0,s,-1,s1,48,NULL,NULL);
+ WideCharToMultiByte(CP_ACP,0,t,-1,t1,48,NULL,NULL);
+ wsprintf(tdbg, "%s %s\n",s1,t1);
+ dbgLog();
+}
+void Log(char *p, PWCHAR s, int n)
+{
+ char s1[48];
+ WideCharToMultiByte(CP_ACP,0,s,n,s1,48,NULL,NULL);
+ wsprintf(tdbg, "%s %s\n",p,s1);
+ dbgLog();
+}
+
+#else
+#define returnErr(value) {return (value);}
+void Log(char *s){}
+void Log(char *s, char *t){}
+void Log(char *s, int n){}
+void Log(char *s, int m, int n){}
+void Log(PWCHAR s, PWCHAR t){}
+void Log(char *p, PWCHAR s, int n){}
+#endif
+
+thread_local UINT Error=ERR_UNKNOWN;
+thread_local HWND hwnd=0, hwndChild=0;
+HINSTANCE hDllInst=0;
+thread_local UINT wm_keymanim_close = 0, wm_keymanim_contextchanged;
+thread_local HBITMAP hbmp[MAXBMP]={0};
+thread_local BOOL EnableTracking=TRUE, Tracking=TRUE;
+thread_local UINT DisplayMode=DM_LIMITED;
+thread_local int CellStart[MAXACTIVERULES+1]={0};
+thread_local BOOL UserResize=TRUE, Vertical=FALSE, NumericMode=FALSE;
+
+thread_local POINTS IMPos={0,0}, IMSize={IM_WIDTH,IM_HEIGHT}, CellSize={CELL_X,CELL_Y};
+thread_local int IMBorders=IMBORDERS, CWBorders=CWBORDERS, InputSize=INPUTWIDTH, IMWidth=0;
+
+enum tagRect {RECT_UP=0,RECT_DOWN,RECT_TRACK,RECT_ORIENT,RECT_CONFIG,RECT_LOGO};
+
+enum tagBmp {BMP_LOGO=0,BMP_VERT,BMP_HORIZ,BMP_CANT_TRACK,BMP_DONT_TRACK,BMP_TRACK,
+ BMP_LEFT0,BMP_LEFT1,BMP_RIGHT0,BMP_RIGHT1,BMP_UP0,BMP_UP1,BMP_DOWN0,BMP_DOWN1,BMP_CONFIG};
+
+const PSTR rnBmp[MAXBMP] = {"LOGO","VERTICAL","HORIZONTAL","T_GRAY","T_RED","T_GREEN",
+ "LEFT0","LEFT1","RIGHT0","RIGHT1","UP0","UP1","DOWN0","DOWN1","CONFIG"};
+
+thread_local int ScrollPos=0, MaxScrollBoxes=0, nCells, MaxCells=9;
+thread_local RULE *ActiveRule[MAXACTIVERULES] = {NULL};
+thread_local KEYBOARD *Keyboards = NULL, *CurKbd = NULL;
+thread_local WCHAR ContextBuf[16]={0};
+thread_local PSTR IndexString = INDEXSTRING;
+thread_local BOOL MouseSelection=TRUE,CountAll=FALSE,ExtendMatch=FALSE;
+thread_local int nKeyboards;
+
+LRESULT CALLBACK IMXWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+LRESULT CALLBACK IMXChildWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+extern "C" BOOL __declspec(dllexport) WINAPI KeymanIMReloadConfig(PSTR KeyboardName);
+void WriteRegSetting(PSTR text, int value);
+int ReadRegSetting(PSTR text, int dflt);
+void UnloadRules(PSTR KeyboardName);
+BOOL LoadRules(PSTR KeyboardName);
+void DeleteKeyboard(PSTR KeyboardName);
+void CreateKeyboard(PSTR KeyboardName);
+int FillActiveRules(WCHAR KeyChar);
+BOOL IsHexString(PWSTR pContext, UINT lBuf, WCHAR wChar);
+BOOL OutputUnicode(PWSTR pContext, UINT lBuf);
+BOOL CompleteRule(RULE *r,int Mode);
+BOOL GetIMDimensions(int nCells,PWCHAR pin);
+BOOL ReadConfiguration(BOOL All);
+BOOL SaveIMXPosition(void);
+void SendKey(int vk);
+BOOL IsRegistered(UINT keyID);
+DWORD GetCRC32(LPVOID pv,UINT cb);
+
+/************************************************************************************************************
+ * DLL entry functions
+ ************************************************************************************************************/
+
+BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, LPVOID lpvReserved)
+{
+ switch(fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ hDllInst = hInst;
+ break;
+
+ case DLL_PROCESS_DETACH:
+ hDllInst = NULL;
+ break;
+ }
+ return TRUE;
+}
+
+extern "C" BOOL __declspec(dllexport) WINAPI KeymanIMDestroy(PSTR KeyboardName)
+{
+ int n;
+
+ for(n=0; nh.nrules == 0 && hwnd) (*KMDisplayIM)(hwnd,TRUE);
+
+ return(TRUE);
+}
+
+extern "C" BOOL __declspec(dllexport) WINAPI KeymanIMDeactivate(PSTR KeyboardName)
+{
+
+ CurKbd = NULL;
+
+ if(!PrepIM(TRUE)) return FALSE;
+
+ SaveIMXPosition();
+ (*KMHideIM)();
+
+ return TRUE;
+}
+
+/************************************************************************************************************
+ * Window management functions
+ ************************************************************************************************************/
+
+// Calculate the cell and window dimensions for the IM window
+// For the horizontal window, each cell wifth can change,
+// but for the vertical window, only the window width varies
+BOOL GetIMDimensions(int nCells,PWCHAR pin)
+{
+ HDC hDC;
+ int i, pwlen, CellWidth[MAXACTIVERULES];
+ PWCHAR pw;
+ SIZE Size={0};
+ char Index[4];
+
+ HFONT hfMain, hfInput, hfTag;
+
+ // Create logical fonts
+ hfMain = CreateFontIndirect(&CurKbd->h.lfMain);
+ hfInput = CreateFontIndirect(&CurKbd->h.lfInput);
+ hfTag = CreateFontIndirect(&CurKbd->h.lfTag);
+
+ // Set default cell widths
+ for(i=0; ipst+ActiveRule[i]->output; pwlen = ActiveRule[i]->olen;
+ GetTextExtentPoint32W(hDC, pw, pwlen, &Size);
+ CellWidth[i] = Size.cx + 2; // add whitespace of 2 px
+ }
+
+ SelectObject(hDC, hfTag);
+ for(i=0; ipst+ActiveRule[i]->tag;
+ pwlen = ActiveRule[i]->tlen ? ActiveRule[i]->tlen : ActiveRule[i]->ilen;
+ if(pwlen >= MAXTAGLEN)
+ {
+ WCHAR tag[MAXTAGLEN];
+ wcsncpy_s(tag, MAXTAGLEN, pw,MAXTAGLEN-1); tag[MAXTAGLEN-1] = ELLIPSIS;
+ GetTextExtentPoint32W(hDC, tag, MAXTAGLEN, &Size);
+ }
+ else
+ {
+ GetTextExtentPoint32W(hDC, pw, pwlen, &Size);
+ }
+ Size.cx += 8; // minimum whitespace of 4 px
+ if(Size.cx > CellWidth[i]) CellWidth[i] = Size.cx;
+ }
+ ReleaseDC(hwndChild,hDC);
+
+ for(i=0; i IMWidth) IMWidth = CellWidth[i]+CWBorders;
+ }
+ }
+
+ for(i=0, CellStart[0]=0; i MaxCells ? MaxCells : nCells);
+ GetIMDimensions(nCellsShown,ContextBuf);
+
+ if(nCells > 0)
+ {
+ nCellsShown = (nCells > MaxCells ? MaxCells : nCells);
+ GetIMDimensions(nCellsShown,ContextBuf);
+ if(Vertical)
+ {
+ dx = IMWidth + IMBorders;
+ dy = (nCellsShown+1)*CellSize.y + 3*(IMBorders + CWBorders)/2;
+ }
+ else
+ {
+ dx = InputSize + IMBorders + CWBorders + CellStart[nCellsShown];
+ dy = CellSize.y + IMBorders + CWBorders;
+ }
+ }
+ else
+ {
+ dx = InputSize + IMBorders + CWBorders;
+ dy = CellSize.y + IMBorders + CWBorders;
+ }
+
+ Tracking = FALSE;
+
+ // Don't attempt to track for a vertical selection window
+ if(EnableTracking && !Vertical)
+ {
+ GetCaretPos(&Caret);
+ hFocus = GetFocus();
+ GetWindowRect(hFocus,&RectApp);
+
+ if(Caret.x != 0 || Caret.y != 0)
+ {
+ Tracking = EnableTracking;
+ x = Caret.x+RectApp.left;
+ y = Caret.y+RectApp.top+24;
+ if(x + RectIM.right - RectIM.left > RectApp.right) x = RectApp.right - (RectIM.right - RectIM.left);
+
+ if(y + RectIM.bottom - RectIM.top > RectApp.bottom) y = RectApp.top + Caret.y - (RectIM.bottom-RectIM.top);
+
+ }
+ }
+ else
+ { // need to make sure the relative values are correct for the current monitor
+ hFocus = GetFocus();
+ HMONITOR hMonitor = MonitorFromWindow(hFocus, MONITOR_DEFAULTTONEAREST);
+ if (hMonitor != NULL) {
+ MONITORINFO monitor_info;
+ monitor_info.cbSize = sizeof(monitor_info);
+ GetMonitorInfo(hMonitor, &monitor_info); // No error checking
+ x = monitor_info.rcMonitor.left + IMPos.x;
+ y = monitor_info.rcMonitor.top + IMPos.y;
+ }
+ else { // Default as if there is one monitor
+ x = IMPos.x; y = IMPos.y;
+ Log("UpdateIMXWindow monitor lookup fail ");
+ }
+ }
+
+ UserResize = FALSE;
+ InvalidateRect(hwnd,NULL,TRUE);
+ SetWindowPos(hwnd,0,x,y,dx,dy,SWP_NOZORDER+SWP_NOACTIVATE);
+ UserResize = TRUE;
+}
+
+LRESULT CALLBACK IMXWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ RECT rect, rectOuter;
+ POINT pts;
+ LRESULT lResult;
+ PAINTSTRUCT ps;
+ HBITMAP hOldBmp;
+ HDC hDC, hSrcDC;
+ HFONT hfInput, hfOld;
+ LPMINMAXINFO pMMI;
+ thread_local static RECT rp[6] = { {0} };
+
+ int i, x, y, x1, y1, x2, y2, dx, dy, n1, n2, PossibleCells;
+
+ // Use default processing if no keyboard loaded
+ if(!PrepIM(FALSE) || !CurKbd)
+ {
+ switch(msg)
+ {
+ case WM_CREATE:
+ case WM_DESTROY:
+ break;
+ default:
+ return DefWindowProc(hwnd,msg,wParam,lParam);
+ }
+ }
+
+ switch(msg)
+ {
+ case WM_LBUTTONDBLCLK:
+ case WM_LBUTTONDOWN:
+ pts.x = GET_X_LPARAM(lParam); pts.y = GET_Y_LPARAM(lParam);
+ GetWindowRect(hwnd,&rect);
+ x=rect.left; y = rect.top;
+
+ // Process all button clicks on hotspots
+ if(ScrollPos > 0 && PtInRect(&rp[RECT_UP],pts))
+ {
+ SendKey(VK_PRIOR);
+ }
+ else if(ScrollPos+MaxCells < nCells && PtInRect(&rp[RECT_DOWN],pts))
+ {
+ SendKey(VK_NEXT);
+ }
+ else if(!Vertical && PtInRect(&rp[RECT_TRACK],pts)) // on tracking hotspot
+ {
+ EnableTracking = !EnableTracking;
+ WriteRegSetting("IMX track", EnableTracking ? 1 : 0);
+ }
+ else if(PtInRect(&rp[RECT_ORIENT],pts)) // on orientation hotspot
+ {
+ Vertical = !Vertical;
+ if(Vertical) EnableTracking = Tracking = FALSE;
+ WriteRegSetting("IMX z", Vertical ? 1 : 0);
+ WriteRegSetting("IMX track", EnableTracking ? 1 : 0);
+ }
+ else if(PtInRect(&rp[RECT_CONFIG],pts)) // on configuration hotspot
+ {
+ SendKey(VK_F24); // do not update IMX window
+ break;
+ }
+ else break;
+
+ // Update the IMX window if any changes made
+ UpdateIMXWindow();
+ break;
+
+ case WM_USER_NCHITTEST:
+
+ if(!Tracking) // Allow dragging except when tracking the insertion point
+ {
+ pts.x = GET_X_LPARAM(lParam); pts.y = GET_Y_LPARAM(lParam);
+ ScreenToClient(hwnd, &pts);
+ GetClientRect(hwnd, &rect);
+ rect.right = rect.left + InputSize;
+ if (PtInRect(&rect, pts)) {
+ return HTCAPTION;
+ }
+ // Alternate code: drag only with logo
+// if(PtInRect(&rp[RECT_LOGO], pts)) return HTCAPTION;
+ }
+ break;
+ }
+
+ // Handle standard IM functions -- such as window activation, etc.
+ if(IMDefWindowProc(hwnd, msg, wParam, lParam, &lResult)) return lResult;
+
+ if(msg == wm_keymanim_contextchanged) PostMessage(hwndChild,WM_REDRAW,0,0);
+
+ switch(msg)
+ {
+ case WM_CREATE:
+
+ // Read saved IM window settings
+ ReadConfiguration(TRUE);
+
+ // Centre the window if an error message is being displayed
+ if(Error != ERR_NONE)
+ {
+ IMPos.x = GetSystemMetrics(SM_CXSCREEN)/2 - 200;
+ IMPos.y = GetSystemMetrics(SM_CYSCREEN)/2 - 28;
+ }
+
+ UserResize = FALSE;
+ SetWindowPos(hwnd,0,IMPos.x,IMPos.y,0,0,SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
+ UserResize = TRUE;
+
+ GetWindowRect(hwnd,&rectOuter); GetClientRect(hwnd, &rect);
+ IMBorders = ((rectOuter.right-rectOuter.left)-(rect.right-rect.left));
+
+ // Note that the actual child window dimensions will be set when the window is first displayed,
+ // except when no valid configuration is loaded
+ hwndChild = CreateWindowEx(WS_EX_CLIENTEDGE, "KM_IMXChild", "",
+ WS_BORDER | WS_CHILD | WS_VISIBLE, InputSize, 0,
+ rect.right-rect.left-InputSize,rect.bottom, hwnd, 0, hDllInst, NULL);
+
+ GetWindowRect(hwndChild,&rectOuter); GetClientRect(hwndChild, &rect); //default is probably OK
+ CWBorders = ((rectOuter.right-rectOuter.left)-(rect.right-rect.left));
+
+ // Update the child window
+ PostMessage(hwndChild,WM_REDRAW,0,0);
+
+ return 0;
+
+ // set resizing limits, according to orientation of IM window
+ case WM_GETMINMAXINFO:
+
+ pMMI=(LPMINMAXINFO)lParam;
+
+ // Set the window width for error messages
+ if(Error != ERR_NONE || (CurKbd->h.nrules == 0))
+ {
+ pMMI->ptMinTrackSize.x = pMMI->ptMaxTrackSize.x = InputSize + 12*CELL_X;
+ pMMI->ptMinTrackSize.y = pMMI->ptMaxTrackSize.y = CELL_Y + IMBorders + CWBorders;
+ }
+
+ else if(Vertical)
+ {
+ pMMI->ptMinTrackSize.x = IMWidth+IMBorders;
+ pMMI->ptMaxTrackSize.x = max(InputSize,IMWidth) + IMBorders; //IMWidth+IMBorders;
+ pMMI->ptMinTrackSize.y = CellSize.y + 3*(IMBorders + CWBorders)/2;
+ pMMI->ptMaxTrackSize.y = pMMI->ptMinTrackSize.y + CellSize.y*MAXACTIVERULES;
+ }
+ else
+ {
+ pMMI->ptMaxTrackSize.x = InputSize;
+ for(i=1; i<=MAXACTIVERULES; i++)
+ {
+ if(CellStart[i])
+ pMMI->ptMaxTrackSize.x = InputSize + CellStart[i];
+ else
+ pMMI->ptMaxTrackSize.x += CellSize.x;
+ }
+ pMMI->ptMaxTrackSize.x += IMBorders + CWBorders + 2;
+ pMMI->ptMinTrackSize.x = InputSize + IMBorders + CWBorders;
+ pMMI->ptMinTrackSize.y = pMMI->ptMaxTrackSize.y = CellSize.y + IMBorders + CWBorders;
+ }
+
+ return 0;
+
+ case WM_SHOWWINDOW:
+ if(wParam) PostMessage(hwndChild,WM_REDRAW,0,0);
+ break;
+
+ case WM_DESTROY:
+ return 0;
+
+ case WM_MOVE:
+ if(!Tracking)
+ {
+ RECT IMrect;
+ if (!GetWindowRect(hwnd, &IMrect)) {
+ return 0;
+ }
+ // Get Absolute X and Y, as if top corner is (0, 0)
+ HMONITOR hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
+ if (hMonitor == NULL) {
+ return 0;
+ }
+
+ MONITORINFO monitor_info;
+ monitor_info.cbSize = sizeof(monitor_info);
+ if (!GetMonitorInfo(hMonitor, &monitor_info)) {
+ return 0;
+ }
+
+ long absoluteX = IMrect.left - monitor_info.rcMonitor.left;
+ long absoluteY = IMrect.top - monitor_info.rcMonitor.top;
+
+ IMPos.x = (short)absoluteX; IMPos.y = (short)absoluteY;
+
+ SaveIMXPosition();
+ }
+ return 0;
+
+ case WM_SIZE:
+ if(UserResize)
+ {
+
+ GetWindowRect(hwnd, &rect);
+ IMSize.x = (short)(rect.right - rect.left);
+ IMSize.y = (short)(rect.bottom - rect.top);
+
+ if(Vertical)
+ {
+ PossibleCells = (IMSize.y - CellSize.y)/CellSize.y;
+ }
+ else
+ {
+ PossibleCells = (IMSize.x - InputSize + CellSize.x)/CellSize.x;
+ }
+
+ // Since SetWindowPos also sends a WM_SIZE, must only reset if non-zero
+ if(PossibleCells >= 2)
+ {
+ MaxCells = ((CurKbd->h.dwOptions & SELECTIONBITS)==SM_FKEYS ? 12 : 9);
+ MaxCells = min(MaxCells,PossibleCells);
+ WriteRegSetting("IMX cells", MaxCells);
+ ScrollPos = 0; // always reset scrolling when resizing
+
+ }
+ }
+
+ if(Vertical && (CurKbd->h.nrules > 0))
+ MoveWindow(hwndChild,0,CellSize.y+(IMBorders+CWBorders)/2,IMWidth, GET_Y_LPARAM(lParam)-CellSize.y,TRUE);
+ else
+ MoveWindow(hwndChild,InputSize,0, GET_X_LPARAM(lParam)-InputSize, GET_Y_LPARAM(lParam),TRUE);
+
+ // Refill cells if width changed
+ if(UserResize) FillActiveRules(0);
+
+ GetIMDimensions(MaxCells,ContextBuf);
+ return 0;
+
+ case WM_PAINT:
+ hDC = BeginPaint(hwnd, &ps);
+
+ hfInput = CreateFontIndirect(&CurKbd->h.lfInput);
+ hfOld = (HFONT)SelectObject(hDC,hfInput);
+
+ GetClientRect(hwnd, &rect);
+ SetBkMode(hDC, TRANSPARENT);
+ FillRect(hDC, &rect, GetSysColorBrush(COLOR_BTNFACE));
+
+ if(Vertical) // scroll up/down hotspots
+ {
+ x1 = rect.right-14; y1 = CellSize.y+(IMBorders+CWBorders)/2-17;
+ x2 = x1; y2 = y1+8;
+ dx = 13; dy = 8;
+ n1 = ScrollPos>0 ? BMP_UP1 : BMP_UP0;
+ n2 = ScrollPos+MaxCells0 ? BMP_LEFT1 : BMP_LEFT0;
+ n2 = ScrollPos+MaxCellsh.dwOptions & SELECTIONBITS)
+ {
+ case SM_DEFAULT:
+ case SM_ALLDIGITS:
+ KeyStroke = i + '1'; break;
+ case SM_KEYPAD:
+ KeyStroke = i + VK_NUMPAD1; break;
+ case SM_FKEYS:
+ KeyStroke = i + VK_F1; break;
+ }
+
+ // Flag simulated keystroke as result of mouse and send keystroke
+ MouseSelection = TRUE;
+ SendKey(KeyStroke);
+ break;
+ }
+ }
+ return 0;
+
+ case WM_NCHITTEST:
+ lResult = DefWindowProc(hwndChild, msg, wParam, lParam);
+ if(lResult != HTCLIENT && lResult != HTVSCROLL && lResult != HTHSCROLL) return HTNOWHERE;
+
+ return lResult;
+
+ case WM_MOUSEACTIVATE:
+ return MA_NOACTIVATE;
+
+ case WM_SIZE:
+ case WM_REDRAW: // This is a user message, not a standard message
+ InvalidateRect(hwndChild, NULL, TRUE);
+ return 0;
+
+ case WM_PAINT:
+ hDC = BeginPaint(hwndChild, &ps);
+ hfTag = CreateFontIndirect(&CurKbd->h.lfTag);
+ hfMain = CreateFontIndirect(&CurKbd->h.lfMain);
+ GetClientRect(hwndChild, &rect);
+ if(CurKbd->h.nrules>0)
+ {
+ hfOld = (HFONT) SelectObject(hDC, hfTag);
+
+ for(i=0; ipst+ActiveRule[i]->tag;
+ pwlen = ActiveRule[i]->tlen ? ActiveRule[i]->tlen : ActiveRule[i]->ilen;
+
+ // Use an ellipsis if tag is too long
+ if(pwlen >= MAXTAGLEN)
+ {
+ wcsncpy_s(tag, MAXTAGLEN, pw, MAXTAGLEN-1);
+ tag[MAXTAGLEN-1] = ELLIPSIS;
+ pw = tag; pwlen = MAXTAGLEN;
+ }
+
+ SetTextAlign(hDC,TA_LEFT);
+ TextOut(hDC, x1, y1, Index, (i<9?1:2));
+
+ SetTextAlign(hDC,TA_CENTER);
+ TextOutW(hDC, x2, y2, pw, pwlen);
+
+ SelectObject(hDC, hfMain);
+ SetTextAlign(hDC,TA_CENTER); SetTextColor(hDC,0);
+
+ pw = CurKbd->pst+ActiveRule[i]->output;
+ pwlen = ActiveRule[i]->olen;
+ TextOutW(hDC, x3, y3, pw, pwlen);
+ }
+
+ // Draw cell boundaries last, to write over text borders
+ SetTextColor(hDC,0);
+ for(i=0; ih.name);
+ TextOut(hDC,10,2,eMsg,(int)strlen(eMsg));
+ TextOut(hDC,10,22,p2,(int)strlen(p2));
+ SelectObject(hDC,hOldBrush);
+ }
+ DeleteObject(hBrush);
+ }
+ SelectObject(hDC,hfOld);
+ }
+ DeleteObject(hfDflt);
+ }
+ }
+ DeleteObject(hfMain); DeleteObject(hfTag);
+ EndPaint(hwndChild, &ps);
+ return 0;
+ }
+
+ return DefWindowProc(hwndChild, msg, wParam, lParam);
+}
+
+/************************************************************************************************************
+ * IM Configuration
+ ************************************************************************************************************/
+
+extern "C" BOOL __declspec(dllexport) WINAPI KeymanIMConfigure(PSTR KeyboardName, HWND hwndParent)
+{
+
+ char *p, buf1[MAX_PATH];
+
+ Log("KeymanIMConfigure - ENTER");
+ Log(KeyboardName);
+
+ buf1[0] = '"';
+ if(!(*KMGetKeyboardPath)(KeyboardName, buf1+1, MAX_PATH))
+ {
+ Log("KeymanIMConfigure - fail to get path");
+ return FALSE;
+ }
+ Log(buf1);
+
+
+ if((p=strrchr(buf1,'\\')) != NULL)
+ {
+ rsize_t bufLimit=(sizeof buf1) - (p+1-buf1);
+ strcpy_s(p+1, bufLimit, "imxconfig.exe\" "); strcat_s(p+1, bufLimit, KeyboardName);
+ Log(buf1);
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ memset(&pi, 0, sizeof(pi));
+ CreateProcess(NULL, buf1, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
+ //WinExec(buf1,SW_SHOW);
+ return TRUE;
+ }
+ else
+ {
+ Log("KeymanIMConfigure - Fail to find \\");
+ return FALSE;
+ }
+}
+
+// Compare rule input string to context buffer
+int cmpspecial(RULE *prule,PWCHAR pInput)
+{
+ PWCHAR pw1, pw1max, lastTone=NULL;
+ int matchMode=MM_NOMATCH;
+
+ register PWCHAR pw2=pInput;
+
+ if(!prule) return MM_NOMATCH;
+
+ pw1 = CurKbd->pst+prule->input;
+ pw1max = pw1+prule->ilen;
+
+ // The first character must match, no wild cards allowed
+ if(*pw2 != *pw1) return MM_NOMATCH;
+
+ // Test first for alternate inputs (flagged by final |, no numeric mode allowed)
+ if(*(pw1+prule->ilen-1) == L'|' && wcschr(pw2,L'|') == NULL)
+ {
+ while(pw1 < pw1max)
+ {
+ pw2 = pInput; lastTone = NULL;
+ while(*pw2)
+ {
+ if(strchr(CurKbd->h.ToneChar,*pw1)) lastTone = pw1;
+
+ if(*pw2 == *pw1 || ((*pw2 == '?') && ((CurKbd->h.dwWild&WC_QUERY) != 0)))
+ {
+ pw1++; pw2++; continue;
+ }
+ else if((*pw2 == '\'') && (lastTone == pw1) && ((CurKbd->h.dwWild&WC_QUOTE) != 0))
+ {
+ pw1++; pw2++; continue;
+ }
+ else if((*pw1!=L'|') && strchr(CurKbd->h.ToneChar,*pw1))
+ {
+ pw1++; continue;
+ }
+ else break;
+ }
+
+ if(*pw2 == 0)
+ {
+ if(matchMode == MM_NOMATCH) matchMode = MM_START;
+
+ if(*pw1 == L'|') matchMode = MM_PERFECT;
+
+ if(*CurKbd->h.ToneChar != 0)
+ {
+ // Test if only trailing character is a tone character
+ if(strchr(CurKbd->h.ToneChar,*pw1) && (*(pw1+1) == L'|')
+ && (matchMode < MM_EXCEPTTONE)) matchMode = MM_EXCEPTTONE;
+
+ // Test if first syllable matches, and the input matches the start of the rest
+ if(lastTone && (pw1 > lastTone)
+ && (matchMode < MM_MORETHANONE)) matchMode = MM_MORETHANONE;
+ }
+ }
+ if(matchMode == MM_PERFECT) break;
+
+ else {
+ while(pw1 < pw1max)
+ {
+ if(*pw1++ == L'|') break;
+ }
+ }
+ }
+ return matchMode;
+ }
+
+ // Test for standard, single input rules (with exceptions for numeric mode rules)
+ else while(*pw2)
+ {
+ if(strchr(CurKbd->h.ToneChar,*pw1)) lastTone = pw1;
+
+ if(*pw2 == *pw1 || ((*pw2 == '?') && ((CurKbd->h.dwWild&WC_QUERY) != 0)))
+ {
+ pw1++; pw2++; continue;
+ }
+ else if((*pw2 == '\'') && (lastTone == pw1) && ((CurKbd->h.dwWild&WC_QUOTE) != 0))
+ {
+ pw1++; pw2++; continue;
+ }
+ else if(!NumericMode && (pw1h.ToneChar,*pw1))
+ {
+ pw1++; continue;
+ }
+ else
+ {
+ return MM_NOMATCH; // no match
+ }
+ }
+
+ // Test for a complete match with the rule input (no trailing characters in rule input)
+ if(pw1 == pw1max) return MM_PERFECT;
+
+ if(!NumericMode && (*CurKbd->h.ToneChar != 0))
+ {
+ // Test if only trailing character is a tone character
+ if(strchr(CurKbd->h.ToneChar,*pw1) && (pw1+1==pw1max)) return MM_EXCEPTTONE;
+
+ // Test if first syllable matches, and the input matches the start of the rest
+ if(lastTone && (pw1 > lastTone)) return MM_MORETHANONE;
+ }
+
+ return MM_START; // matches start of rule input string
+}
+
+// Select those rules that match the input string. Stop searching when the
+// required number of rules has been filled, unless CountAll set (for End key)
+int FillActiveRules(WCHAR KeyChar)
+{
+ int i, n, n0, n1, lBuf, Match;
+ WCHAR buf2[18];
+ PWSTR p,p1;
+ PSTR q;
+ RULE *prule;
+ BOOL Matched=FALSE, SearchToEnd=CountAll;
+
+ if(!CurKbd || (CurKbd->h.nrules == 0)) return (-1);
+
+ // By default, just copy the entire string
+ wcscpy_s(buf2, _countof(buf2), ContextBuf); p = wcschr(buf2, 0);
+
+ // Test if dashes are being used to select a particular output character
+ if(((CurKbd->h.dwWild & WC_DASH) != 0) && ((p1=wcsrchr(ContextBuf,L'-')) != NULL))
+ {
+ if(*(p1+1) == 0) // and filter the string if last character in context is a dash
+ {
+ for(p=buf2,p1=ContextBuf; *p1; p1++)
+ {
+ if(*p1 != L'-') *p++ = *p1;
+ }
+ }
+ }
+
+ // Append the current keystroke
+ if(KeyChar != 0)
+ {
+ if((KeyChar != L'-') || ((CurKbd->h.dwWild & WC_DASH) == 0)) *p++ = KeyChar;
+ }
+ *p = 0;
+
+ CountAll = FALSE;
+
+ lBuf = (int)wcslen(buf2);
+
+ for(i=0; iindex+n);
+ n1 = *(CurKbd->index+n+1);
+ }
+ else
+ {
+ n = (int)strlen(IndexString);
+ n0 = *(CurKbd->index+n);
+ n1 = CurKbd->h.nrules;
+ }
+
+ for(i=n0,n=0, prule=CurKbd->rules+n0; i MM_START) || (DisplayMode == DM_FULL) || ExtendMatch )
+ {
+ if(n >= ScrollPos)
+ {
+ if(n-ScrollPos < MaxCells) ActiveRule[n-ScrollPos] = prule;
+ else if((n-ScrollPos > MaxCells) && !SearchToEnd) break;
+ }
+ n++;
+ }
+ }
+ }
+
+ // Return the number of possible ActiveRules
+ return Matched ? n : (-1);
+}
+
+BOOL InsertChar(WCHAR ch)
+{
+ WCHAR buf[2]={0};
+
+ buf[0] = ch;
+ (*KMSetOutput)(buf, 0);
+ return TRUE;
+}
+
+BOOL CompleteRule(RULE *r,int Mode)
+{
+ int i,n,n1=0,n2=0,ncSelect;
+ WCHAR tOut[MAXOUTLEN+2]={0};
+ PWCHAR p;
+ PCHAR pt=CurKbd->h.ToneChar;
+
+ if(Mode & OM_TAG)
+ {
+ n = r->tlen ? r->tlen : r->ilen;
+ wcsncpy_s(tOut, _countof(tOut), CurKbd->pst+r->tag, min(n,MAXOUTLEN-1));
+ wcscat_s(tOut, _countof(tOut), L" ");
+ }
+ else if(Mode & OM_BOTH)
+ {
+ wcsncpy_s(tOut, _countof(tOut), CurKbd->pst+r->output, min(r->olen,MAXOUTLEN-1));
+ n = r->tlen ? r->tlen : r->ilen;
+ if(r->olen+n+4 < MAXOUTLEN)
+ {
+ wcscat_s(tOut, _countof(tOut), L" (");
+ wcsncat_s(tOut, _countof(tOut), CurKbd->pst+r->tag, n);
+ wcscat_s(tOut, _countof(tOut), L") ");
+ }
+ }
+ else
+ {
+ if(((CurKbd->h.dwWild & WC_DASH) != 0) && ((p=wcsrchr(ContextBuf,L'-')) != NULL)
+ && (r->olen > 1) && (*(p+1) == 0))
+ {
+ p = wcschr(ContextBuf,L'-') + 1;
+ for(ncSelect=0;*p==L'-' && ncSelectolen-1;p++,ncSelect++);
+ *tOut = *(CurKbd->pst+r->output + ncSelect);
+ }
+ else
+ {
+ wcsncpy_s(tOut, _countof(tOut), CurKbd->pst+r->output, min(r->olen,MAXOUTLEN-1));
+ }
+ }
+
+ (*KMSetOutput)(tOut, 0);
+
+ for(i=0; ih.nrules == 0)
+ {
+ if(IsVisible)
+ (*KMHideIM)();
+ else
+ (*KMDisplayIM)(hwnd,TRUE);
+
+ return(FALSE);
+ }
+
+ ReadConfiguration(TRUE);
+
+ lBuf = (int)wcslen(ContextBuf);
+ sMode = (CurKbd->h.dwOptions & SELECTIONBITS);
+ tMode = (CurKbd->h.dwOptions & TONEBITS)>>2;
+ AutoSelect = (CurKbd->h.dwOptions & SELECTIFUNIQUE);
+
+ // Save current wild card matching
+ ExtendMode = ExtendMatch;
+
+ // Test if a simulated keystroke, and save special mode flags
+ outMode = MouseSelection; MouseSelection = FALSE;
+
+ // Check first character (to allow 4-corner index selection for Han)
+ FirstChar = (char)(lBuf>0 ? *ContextBuf : KeyChar);
+ NumericMode = (strchr("0123456789#$!@%&*",FirstChar) != NULL);
+
+ // Test first for configuration hotkey (or VK_F24 sent by click on hotspot)
+ if((KeyStroke == VK_F24) || ((KeyStroke == LOWORD(CurKbd->h.cfgKey)) &&
+ ((shiftFlags & HK_MODIFIERS) == (unsigned)(HIWORD(CurKbd->h.cfgKey) & HK_MODIFIERS))))
+ {
+ SaveIMXPosition();
+ *ContextBuf = 0;
+ (*KMHideIM)();
+ KeymanIMConfigure(CurKbd->h.name,hwndFocus);
+ return TRUE;
+ }
+
+ // If tone characters must be entered, check if the current character is
+ // a matching tone character before testing for selection
+ if((lBuf > 0) && *CurKbd->h.ToneChar && KeyChar && !outMode
+ && (tMode != TM_NEVER) && (strchr(CurKbd->h.ToneChar,(char)KeyStroke) > 0))
+ {
+ if(FillActiveRules(KeyChar) >= 0) SkipSelectionTesting = TRUE;
+ }
+
+ // If numeric mode, then check that the digit does not form part of a valid input string
+ if((lBuf > 0) && NumericMode && (KeyChar != 0) && iswdigit(KeyStroke))
+ {
+ if(FillActiveRules(KeyChar) >= 0) SkipSelectionTesting = TRUE;
+ }
+
+ // Test next for rule completion
+ if(lBuf > 0 && !SkipSelectionTesting)
+ {
+ // Test selection by space first, then according to configured selection mode
+ if(KeyChar == VK_SPACE || KeyStroke == VK_RETURN)
+ {
+ // Check first if input string is U+xxxxx (direct Unicode, incl. surrogate pairs)
+ if(OutputUnicode(ContextBuf,lBuf)) return TRUE;
+
+ // Otherwise space or return is same as selecting left most (or top) cell
+ nSelect = 1;
+ }
+ else
+ {
+ switch(sMode)
+ {
+ case SM_DEFAULT:
+ nSelect = (UINT)(KeyStroke-'1'+1);
+ break;
+ case SM_ALLDIGITS:
+ if(KeyStroke < VK_NUMPAD1)
+ nSelect = (UINT)(KeyChar-'1'+1);
+ else
+ nSelect = (UINT)(KeyStroke-VK_NUMPAD1+1);
+ break;
+ case SM_KEYPAD://must test shiftstate
+ nSelect = (UINT)(KeyStroke-VK_NUMPAD1+1);
+ break;
+ case SM_FKEYS:
+ nSelect = (UINT)(KeyStroke-VK_F1+1);
+ if(shiftFlags & HK_SHIFT) outMode = OM_TAG;
+ if(shiftFlags & HK_CTRL) outMode = OM_BOTH;
+ break;
+ }
+ }
+
+ if(nSelect > 0)
+ {
+ nCells = FillActiveRules(0); // otherwise, re-fill the matching rules
+ if(nSelect <= nCells) // selection from menu by Fn key
+ {
+ if(ActiveRule[nSelect-1]) // use selected character
+ {
+ CompleteRule(ActiveRule[nSelect-1],outMode);
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ // Process non-printing characters next - first, for empty buffer
+ if(!isprint(KeyChar) && lBuf == 0)
+ {
+ switch(KeyStroke)
+ {
+ case VK_ESCAPE:
+ (*KMQueueAction)(QIT_CHAR, KeyStroke);
+ break;
+
+ case VK_BACK:
+ (*KMQueueAction)(QIT_BACK, 0);
+ break;
+
+ default:
+ (*KMQueueAction)(QIT_VKEYDOWN, KeyStroke);
+ (*KMQueueAction)(QIT_VKEYUP, KeyStroke);
+ }
+
+ SaveIMXPosition();
+ *ContextBuf = 0;
+ (*KMHideIM)();
+ return TRUE;
+ }
+
+ // then process non-printing characters when buffer non-empty
+ else if(!isprint(KeyChar) && lBuf > 0)
+ {
+ // drop buffer and close IMX window
+ if(KeyStroke == VK_ESCAPE || ((KeyStroke == VK_BACK) && (lBuf == 1)))
+ {
+ SaveIMXPosition();
+ *ContextBuf = 0;
+ (*KMHideIM)();
+ return TRUE;
+ }
+
+ // translate number pad keys to characters
+ switch(KeyStroke)
+ {
+ case VK_MULTIPLY:
+ KeyChar = '*'; break;
+
+ case VK_ADD:
+ KeyChar = '+'; break;
+
+ case VK_SUBTRACT:
+ KeyChar = '-'; break;
+
+ case VK_DECIMAL:
+ KeyChar = '.'; break;
+
+ case VK_DIVIDE:
+ KeyChar = '/'; break;
+ }
+
+ // adjust display according to cursor movement keys
+ switch(KeyStroke)
+ {
+ case VK_BACK: // drop the previously entered character
+ Refresh = TRUE; ScrollPos = 0;
+ pCB = wcschr(ContextBuf,0);
+ if(pCB > ContextBuf) *(pCB-1) = 0;
+ break;
+
+ case VK_HOME: //scroll to first match
+ if(ActiveRule[0])
+ {
+ ScrollPos = 0; Refresh = TRUE;
+ }
+ break;
+
+ case VK_END: //scroll to last match
+ if(ActiveRule[0])
+ {
+ Refresh = TRUE; ScrollPos = 0; CountAll = TRUE;
+ nCells = FillActiveRules(0);
+ ScrollPos = max(0,nCells-MaxCells);
+ }
+ break;
+
+ case VK_LEFT: //scroll backward through the menu (Cursor Left/Up)
+ case VK_UP:
+ if(ActiveRule[0])
+ {
+ Refresh = TRUE;
+ if(ScrollPos-1 >= 0) ScrollPos -= 1; else ScrollPos = 0;
+ }
+ break;
+
+ case VK_RIGHT: //scroll forward through menu (Cursor Right/Down)
+ case VK_DOWN:
+ if(ActiveRule[1])
+ {
+ Refresh = TRUE;
+ if(ScrollPos+1 < nCells) ScrollPos += 1;
+ }
+ break;
+
+ case VK_NEXT: //scroll forward through menu (Page Down)
+ if(ActiveRule[MaxCells-1])
+ {
+ Refresh = TRUE;
+ if(ScrollPos+MaxCells < nCells) ScrollPos += MaxCells;
+ }
+ break;
+
+ case VK_PRIOR: //scroll backward through the menu (Page Up)
+ if(ActiveRule[0])
+ {
+ Refresh = TRUE;
+ if(ScrollPos-MaxCells >= 0) ScrollPos -= MaxCells; else ScrollPos = 0;
+ }
+ break;
+ }
+
+ if(Refresh)
+ {
+ nCells = FillActiveRules(0); DropCharacter = FALSE;
+ }
+ }
+
+ // Check for wild card matching
+ if((CurKbd->h.dwWild & WC_STAR) && (KeyChar == '*') && (lBuf > 0))
+ {
+ ExtendMatch = TRUE;
+ nCells = FillActiveRules(0);
+ DropCharacter = FALSE;
+ }
+ // Match the entered character plus the context with allowed sequences
+ else if(isprint(KeyChar) && (lBuf<(sizeof ContextBuf)/sizeof(WCHAR)))
+ {
+ if(IsHexString(ContextBuf,lBuf, KeyChar))
+ {
+ pCB = wcschr(ContextBuf,0); *pCB++ = KeyChar; *pCB = 0;
+ DropCharacter = FALSE;
+ }
+
+ else if(NumericMode || !(tMode == TM_NEVER && *CurKbd->h.ToneChar // always drop if no tone characters
+ && (strchr(CurKbd->h.ToneChar,(char)KeyChar)>0)))
+ {
+ ScrollPos = 0; ExtendMatch = FALSE;
+ switch((nCells=FillActiveRules(KeyChar)))
+ {
+ case (-1):
+ if(lBuf == 0) // empty context
+ {
+ InsertChar(KeyChar); // so don't do anything with IM window
+ return TRUE;
+ }
+ else
+ {
+ ExtendMatch = ExtendMode;
+ nCells=FillActiveRules(0);
+ DropCharacter = TRUE;
+ }
+ break;
+
+ case 0: // no exact match, but starts a match
+ pCB = wcschr(ContextBuf,0); *pCB++ = KeyChar; *pCB = 0;
+ DropCharacter = FALSE;
+ break;
+
+ case 1: // check if this is a unique and complete match
+ if(AutoSelect && ActiveRule[0])
+ {
+ BOOL TrulyUnique=TRUE;
+
+ if(!ExtendMatch && (DisplayMode!=DM_FULL))
+ {
+ RULE *TempRule[MAXACTIVERULES];
+ ExtendMatch = TRUE;
+ memcpy(TempRule,ActiveRule,MAXACTIVERULES*sizeof(RULE *));
+ TrulyUnique = (FillActiveRules(KeyChar) == 1);
+ memcpy(ActiveRule,TempRule,MAXACTIVERULES*sizeof(RULE *));
+ ExtendMatch = FALSE;
+ }
+
+ if(TrulyUnique)
+ {
+ pCB = wcschr(ContextBuf,0); *pCB++ = KeyChar; *pCB = 0;
+ if(cmpspecial(ActiveRule[0],ContextBuf) > 1)
+ {
+ CompleteRule(ActiveRule[0],outMode); // send the result to the app, and hide IM window
+ return TRUE;
+ }
+ DropCharacter = FALSE;
+ break;
+ }
+ } // else fall through and add to context
+
+ default: // multiple matches possible, so just extend the context
+ pCB = wcschr(ContextBuf,0); *pCB++ = KeyChar; *pCB = 0;
+ DropCharacter = FALSE;
+ }
+ }
+ }
+
+ // Drop the character and beep if the IMX window is already open and it cannot be matched
+ if(DropCharacter)
+ {
+ ExtendMatch = ExtendMode; // restore wild card match
+ (*KMQueueAction)(QIT_BELL, 0);
+ return TRUE;
+ }
+
+ // Show the IM window, then update its position and size
+ (*KMDisplayIM)(hwnd, TRUE);
+ UpdateIMXWindow();
+
+ return TRUE;
+}
+
+void UnloadRules(PSTR KeyboardName)
+{
+ int nkbi;
+ KEYBOARD *kbi;
+
+ if((nkbi=KeyboardIndex(KeyboardName)) == nKeyboards) return;
+
+ if((kbi=&Keyboards[nkbi]) == NULL) return;
+
+ // Unmap shared memory from the process's address space
+ if(kbi->BaseAddress) UnmapViewOfFile(kbi->BaseAddress);
+
+ // Close the process's handle to the file-mapping object and backing file
+ if(kbi->hMapObject) CloseHandle(kbi->hMapObject);
+ if(kbi->hFile) CloseHandle(kbi->hFile);
+
+ // Zero pointers and handles
+ kbi->h.nrules = 0; kbi->BaseAddress = NULL;
+ kbi->hFile = 0; kbi->hMapObject = 0;
+}
+
+// Decrypt the string table
+void DecryptStrings(KBHEADER *pk)
+{
+ BYTE *p0, *p1, *p;
+ int n;
+
+ p0 = (BYTE *)pk + pk->dwStrings;
+ p1 = p0 + pk->cbStrings;
+
+ for(p=p0,n=0; ph.nrules = 0;
+
+ // Test first if the saved memory-mapped file image exists
+ wsprintf(mapfile, "keyman_imx_%s", KeyboardName);
+
+ // Open mapping object (if already created by another application)
+ hMapObject = OpenFileMapping(FILE_MAP_WRITE,FALSE,mapfile);
+ if(hMapObject)
+ {
+ // Get a pointer to file-mapped shared memory
+ if((pbmm=(PBYTE)MapViewOfFile(hMapObject,FILE_MAP_WRITE,0,0,0)) == NULL )
+ {
+ Error = ERR_SHARING;
+ CloseHandle(hMapObject); return FALSE;
+ }
+ pkbh = (KBHEADER *)pbmm;
+ }
+
+ // Otherwise, create the file-mapping object from the backing file (*.CFX)
+ else
+ {
+ if(!(*KMGetKeyboardPath)(KeyboardName, buf1, MAX_PATH)) return FALSE;
+ _splitpath_s(buf1, drive, sizeof drive, dir, sizeof dir, NULL, 0, NULL, 0);
+ _makepath_s(buf1, sizeof buf1, drive, dir, KeyboardName, ".cfx");
+
+ hFile = CreateFile(buf1,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
+
+ // Return FALSE if the CFX is not found
+ if(hFile == INVALID_HANDLE_VALUE)
+ {
+ Error = ERR_CFGMISSING; return FALSE;
+ }
+
+ Error = ERR_CFGINVALID;
+ dwFileSize[0] = GetFileSize(hFile,&dwFileSize[1]);
+
+ // Create the named file-mapping object from the backing file
+ if((hMapObject=CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,dwFileSize[1],dwFileSize[0],mapfile)) == NULL )
+ {
+ CloseHandle(hFile); return FALSE;
+ }
+
+ // Get a pointer to file-mapped shared memory
+ if((pbmm=(PBYTE)MapViewOfFile(hMapObject,FILE_MAP_WRITE,0,0,0)) != NULL )
+ {
+ pkbh = (KBHEADER *)pbmm;
+ }
+ else
+ {
+ CloseHandle(hMapObject); CloseHandle(hFile); return FALSE;
+ }
+
+ // Read the keyboard header and check the file type flag and version
+ ReadFile(hFile,pkbh,sizeof(KBHEADER),&nRead,NULL);
+ if(nRead < sizeof(KBHEADER) || memcmp(pkbh->Flag,"KCFX",4) != 0
+ || pkbh->dwCfxVersion != CFXVERSION)
+ {
+ UnmapViewOfFile(pbmm);
+ CloseHandle(hMapObject); CloseHandle(hFile); return FALSE;
+ }
+
+ // Check the file size
+ totalsize = pkbh->dwStrings + pkbh->cbStrings;
+ if(dwFileSize[0] < totalsize+4)
+ {
+ UnmapViewOfFile(pbmm);
+ CloseHandle(hMapObject); CloseHandle(hFile); return FALSE;
+ }
+
+/*
+ // If more than rule limit, test if Keyman is registered
+ if((pkbh->nrules > MAXRULESFREE) &&
+ !(IsRegistered(KEY_KEYMAN) || IsRegistered(KEY_KEYMANDEVELOPER)))
+ {
+ Error = ERR_UNREGISTERED; UnmapViewOfFile(pbmm);
+ CloseHandle(hMapObject); CloseHandle(hFile); return FALSE;
+ }
+*/
+
+ // Can read and decrypt file here
+ SetFilePointer(hFile,0,NULL,FILE_BEGIN);
+ ReadFile(hFile,pbmm,dwFileSize[0],&nRead,NULL);
+
+ CloseHandle(hFile); hFile = NULL;
+
+ // Check if file read correctly (verify checksum)
+ if(nRead >= dwFileSize[0])
+ {
+ dwChecksum = GetCRC32(pbmm,totalsize);
+ if(memcmp(pbmm+totalsize,&dwChecksum,4) == 0) Error = ERR_NONE;
+ }
+
+ if(Error == ERR_CFGINVALID)
+ {
+ UnmapViewOfFile(pbmm); CloseHandle(hMapObject); return FALSE;
+ }
+
+ // Decrypt the string table
+ DecryptStrings(pkbh);
+ }
+
+ // Copy the keyboard parameters (not the variables)
+ *(KBHEADER *)kbi = *pkbh;
+
+ // Correct the table base addresses
+ kbi->BaseAddress = pbmm;
+ kbi->index = (DWORD *)(pbmm + pkbh->dwIndex);
+ kbi->rules = (RULE *)(pbmm + pkbh->dwRules);
+ kbi->pst = (PWCHAR)(pbmm + pkbh->dwStrings);
+
+ // Save the file and mapping object handles for destruction on exit
+ kbi->hFile = hFile;
+ kbi->hMapObject = hMapObject;
+
+ CellSize.x = kbi->h.gridx; CellSize.y = kbi->h.gridy;
+
+ // Read saved options from registry
+ wsprintf(key,"IMX: %s",kbi->h.name);
+ kbi->h.dwOptions = ReadRegSetting(key,kbi->h.dwOptions);
+
+ Error = ERR_NONE;
+ return TRUE;
+}
+
+void DeleteKeyboard(PSTR KeyboardName)
+{
+ int nkbi;
+ KEYBOARD *kbi;
+
+ if((nkbi=KeyboardIndex(KeyboardName)) == nKeyboards) return;
+
+ kbi = &Keyboards[nkbi];
+ memmove(kbi, kbi+1, sizeof(KEYBOARD)*(nKeyboards-nkbi-1));
+
+ if(--nKeyboards == 0)
+ {
+ delete Keyboards;
+ Keyboards = NULL;
+ }
+}
+
+void CreateKeyboard(PSTR KeyboardName)
+{
+ int nkbi;
+
+ if((nkbi=KeyboardIndex(KeyboardName)) == nKeyboards)
+ {
+ KEYBOARD *kb2 = new KEYBOARD[nKeyboards + 1];
+ if(nKeyboards > 0)
+ {
+ memcpy(kb2, Keyboards, sizeof(KEYBOARD) * nKeyboards);
+ delete Keyboards;
+ }
+ Keyboards = kb2;
+ ZeroMemory(&Keyboards[nKeyboards],sizeof(KEYBOARD));
+ strncpy_s(Keyboards[nKeyboards].h.name, sizeof Keyboards[nKeyboards].h.name, KeyboardName, 127);
+ nKeyboards++;
+ }
+}
+
+void WriteRegSetting(PSTR text, int value)
+{
+ HKEY hkey;
+ if(RegCreateKeyEx(HKEY_CURRENT_USER, REGSZ_KeymanIMX, 0, NULL, NULL, KEY_ALL_ACCESS | KEY_WOW64_32KEY, NULL, &hkey, NULL) == ERROR_SUCCESS)
+ {
+ RegSetValueEx(hkey, text, 0, REG_DWORD, (PBYTE) &value, 4);
+ RegCloseKey(hkey);
+ }
+}
+
+int ReadRegSetting(PSTR text, int dflt)
+{
+ HKEY hkey;
+ if(RegOpenKeyEx(HKEY_CURRENT_USER, REGSZ_KeymanIMX,
+ 0, KEY_ALL_ACCESS | KEY_WOW64_32KEY, &hkey) == ERROR_SUCCESS)
+ {
+ unsigned long value, sz = 4, tp = REG_DWORD;
+ if(RegQueryValueEx(hkey, text, 0, &tp, (PBYTE) &value, &sz) == ERROR_SUCCESS)
+ {
+ RegCloseKey(hkey);
+ return value;
+ }
+ RegCloseKey(hkey);
+ }
+ return dflt;
+}
+
+// Read (or default) saved configuration parameters, and limit to safe values
+BOOL ReadConfiguration(BOOL All)
+{
+ int xmax, ymax, dfltCells=9;
+ char key[128];
+
+ DisplayMode = ReadRegSetting("IMX match",DM_LIMITED);
+ Vertical = ReadRegSetting("IMX z",FALSE) ? TRUE : FALSE;
+ EnableTracking = ReadRegSetting("IMX track",TRUE) ? TRUE : FALSE;
+ Tracking = EnableTracking && (!Vertical);
+
+ if(CurKbd)
+ {
+ wsprintf(key,"IMX: %s",CurKbd->h.name);
+ CurKbd->h.dwOptions = ReadRegSetting(key,0);
+ if((CurKbd->h.dwOptions & SELECTIONBITS) == SM_FKEYS) dfltCells = 12;
+ }
+
+ if(All)
+ {
+ // Read position for non-tracking window (use by default when creating window)
+ IMPos.x = ReadRegSetting("IMX x",0);
+ IMPos.y = ReadRegSetting("IMX y",0);
+
+ xmax = GetSystemMetrics(SM_CXSCREEN);
+ ymax = GetSystemMetrics(SM_CYSCREEN);
+
+ if(Vertical)
+ {
+ if(IMPos.x > xmax - 8) IMPos.x = 0;
+ if(IMPos.y > ymax - 80) IMPos.y = ymax - 80;
+ }
+ else
+ {
+ if(IMPos.x > xmax - 40) IMPos.x = 0;
+ if(IMPos.y == 0 || IMPos.y > ymax - 8) IMPos.y = ymax - 80;
+ }
+
+ MaxCells = ReadRegSetting("IMX cells",dfltCells);
+ MaxCells = min(max(MaxCells,2),dfltCells);
+ }
+
+ return TRUE;
+}
+
+BOOL SaveIMXPosition(void)
+{
+ RECT IMrect;
+ if(hwnd && !Tracking)
+ {
+ if (!GetWindowRect(hwnd, &IMrect)) {
+ return FALSE;
+ }
+ // Adjust for a mulitple monitor display
+
+ HMONITOR hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
+ if (hMonitor == NULL) {
+ return FALSE;
+ }
+
+ MONITORINFO monitor_info;
+ monitor_info.cbSize = sizeof(monitor_info);
+ if (!GetMonitorInfo(hMonitor, &monitor_info)) {
+ return FALSE;
+ }
+
+ long absoluteX = IMrect.left - monitor_info.rcMonitor.left;
+ long absoluteY = IMrect.top - monitor_info.rcMonitor.top;
+ WriteRegSetting("IMX x", absoluteX); WriteRegSetting("IMX y", absoluteY);
+
+ }
+ return TRUE;
+}
+
+// Simulate a (virtual) key. Note that modified keys cannot be handled this way.
+void SendKey(int vk)
+{
+ keybd_event(vk,0,0,0); keybd_event(vk,0,KEYEVENTF_KEYUP,0);
+}
+
+// Check for existence of key - don't need to check validity, as Keyman will do that
+/*BOOL IsRegistered(UINT regKeyID)
+{
+ HKEY hkey;
+ DWORD sz, tp = REG_BINARY;
+ BOOL Exists=FALSE;
+ PSTR pKey;
+
+ switch(regKeyID)
+ {
+ case KEY_KEYMAN:
+ pKey = "SOFTWARE\\Tavultesoft\\Keyman\\6.0";
+ break;
+ case KEY_KEYMANDEVELOPER:
+ pKey = "SOFTWARE\\Tavultesoft\\Keyman Developer\\6.0";
+ break;
+ default:
+ return TRUE;
+ }
+
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,pKey,0,KEY_READ,&hkey) == ERROR_SUCCESS)
+ {
+ if((RegQueryValueEx(hkey, "registration key", 0, &tp, NULL, &sz) == ERROR_SUCCESS)
+ && (tp == REG_BINARY)) Exists = TRUE;
+ if((RegQueryValueEx(hkey, "licence blob", 0, &tp, NULL, &sz) == ERROR_SUCCESS)
+ && (tp == REG_BINARY)) Exists = TRUE;
+ if((RegQueryValueEx(hkey, "evaluation", 0, &tp, NULL, &sz) == ERROR_SUCCESS)
+ && (tp == REG_BINARY)) Exists = TRUE;
+ RegCloseKey(hkey);
+ }
+
+ if(!Exists && regKeyID == KEY_KEYMAN)
+ {
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Tavultesoft\\Keyman Engine\\7.0",0,KEY_READ,&hkey) == ERROR_SUCCESS)
+ {
+ Exists = TRUE;
+ RegCloseKey(hkey);
+ }
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Tavultesoft\\Keyman Engine\\8.0",0,KEY_READ,&hkey) == ERROR_SUCCESS)
+ {
+ Exists = TRUE;
+ RegCloseKey(hkey);
+ }
+ }
+
+ return Exists;
+}*/
+
+BOOL IsHexString(PWSTR pContext, UINT lBuf, WCHAR wChar)
+{
+ switch(lBuf)
+ {
+ case 0:
+ return (wChar==L'U');
+ case 1:
+ return (*pContext == L'U' && wChar == L'+');
+ default:
+ if(*pContext != L'U' || *(pContext+1) != '+') return FALSE;
+ return (wcschr(L"0123456789abcdefABCDEF",wChar) > 0);
+ }
+}
+
+BOOL OutputUnicode(PWSTR pContext,UINT lBuf)
+{
+ WCHAR tOut[4]={0};
+ DWORD dwChar;
+ int i;
+
+ if(lBuf < 4) return FALSE;
+
+ if(*pContext != L'U' || *(pContext+1) != L'+') return FALSE;
+
+ dwChar = wcstoul(pContext+2,NULL,16);
+
+ if(dwChar < 0x10000)
+ {
+ tOut[0] = (WCHAR)dwChar;
+ }
+ else
+ {
+ tOut[0] = (WCHAR)Uni_UTF32ToSurrogate1(dwChar);
+ tOut[1] = (WCHAR)Uni_UTF32ToSurrogate2(dwChar);
+ }
+
+ (*KMSetOutput)(tOut, 0);
+
+ for(i=0; i
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (Australia) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 6,0,161,0
+ PRODUCTVERSION 6,0,161,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "0c0904b0"
+ BEGIN
+ VALUE "Comments", "\0"
+ VALUE "CompanyName", "Tavultesoft\0"
+ VALUE "FileDescription", "KeymanIMX\0"
+ VALUE "FileVersion", "6.0.161.0\0"
+ VALUE "InternalName", "KeymanIMX\0"
+ VALUE "LegalCopyright", "Copyright © 2003 Tavultesoft\0"
+ VALUE "LegalTrademarks", "\0"
+ VALUE "OriginalFilename", "KeymanIMX\0"
+ VALUE "PrivateBuild", "\0"
+ VALUE "ProductName", "Tavultesoft KeymanIMX\0"
+ VALUE "ProductVersion", "6.0.161.0\0"
+ VALUE "SpecialBuild", "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0xc09, 1200
+ END
+END
+
+#endif // !_MAC
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+LOGO BITMAP DISCARDABLE "../images/Key.bmp"
+VERTICAL BITMAP DISCARDABLE "../images/Direction_V.bmp"
+CONFIG BITMAP DISCARDABLE "../images/Direction_H.bmp"
+T_GREEN BITMAP DISCARDABLE "../images/T_green.bmp"
+T_RED BITMAP DISCARDABLE "../images/T_red.bmp"
+T_GRAY BITMAP DISCARDABLE "../images/T_yellow.bmp"
+LEFT1 BITMAP DISCARDABLE "../images/vertical.bmp"
+LEFT0 BITMAP DISCARDABLE "../images/left2.bmp"
+RIGHT1 BITMAP DISCARDABLE "../images/right1.bmp"
+RIGHT0 BITMAP DISCARDABLE "../images/right0.bmp"
+DOWN1 BITMAP DISCARDABLE "../images/down1.bmp"
+DOWN0 BITMAP DISCARDABLE "../images/down0.bmp"
+UP1 BITMAP DISCARDABLE "../images/up1.bmp"
+UP0 BITMAP DISCARDABLE "../images/up0.bmp"
+HORIZONTAL BITMAP DISCARDABLE "../images/horizont.bmp"
+#endif // English (Australia) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/release/c/cs_pinyin/source/keymanimx/KeymanIMX.vcxproj b/release/c/cs_pinyin/source/keymanimx/KeymanIMX.vcxproj
new file mode 100644
index 0000000000..56eb5ae510
--- /dev/null
+++ b/release/c/cs_pinyin/source/keymanimx/KeymanIMX.vcxproj
@@ -0,0 +1,340 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ Win32
+
+
+ Release
+ x64
+
+
+
+ {F00D56AD-FFA9-4012-95D4-4ED8FA00DDEC}
+ KeymanIMX
+ 10.0.17763.0
+
+
+
+ DynamicLibrary
+ false
+ MultiByte
+ v142
+
+
+ DynamicLibrary
+ false
+ MultiByte
+ v142
+
+
+ DynamicLibrary
+ false
+ MultiByte
+ v142
+
+
+ DynamicLibrary
+ false
+ MultiByte
+ v142
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_ProjectFileVersion>10.0.30319.1
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ true
+ true
+ false
+ false
+ AllRules.ruleset
+ AllRules.ruleset
+
+
+
+
+ AllRules.ruleset
+ AllRules.ruleset
+
+
+
+
+ KeymnIMX.x64
+ KeymnIMX
+ true
+ KeymnIMX
+
+
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+
+
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+ ..\..\build\$(MSBuildProjectName)\$(Platform)\$(Configuration)\
+
+
+ KeymnIMX.x64
+
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ Win32
+ .\Debug/KeymanIMX.tlb
+
+
+
+
+ Disabled
+ WIN32;_DEBUG;_WINDOWS;_USRDLL;KEYMANIMX_EXPORTS;%(PreprocessorDefinitions)
+ true
+ EnableFastChecks
+ MultiThreadedDebug
+ $(outdir)\KeymanIMX.pch
+ $(outdir)\
+ $(outdir)\
+ $(outdir)\
+ Level3
+ true
+ EditAndContinue
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ 0x0c09
+
+
+ odbc32.lib;odbccp32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;%(AdditionalDependencies)
+ true
+ .\KeymanIMX.def
+ true
+ $(OutDir)$(TargetName).pdb
+ $(OutDir)$(TargetName).lib
+ MachineX86
+ false
+
+
+ true
+ .\Debug/KeymanIMX.bsc
+
+
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ .\Debug/KeymanIMX.tlb
+
+
+
+
+ Disabled
+ WIN32;_DEBUG;_WINDOWS;_USRDLL;KEYMANIMX_EXPORTS;%(PreprocessorDefinitions)
+ EnableFastChecks
+ MultiThreadedDebug
+ $(outdir)\KeymanIMX.pch
+ $(outdir)\
+ $(outdir)\
+ $(outdir)\
+ Level3
+ true
+ ProgramDatabase
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ 0x0c09
+
+
+ odbc32.lib;odbccp32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;%(AdditionalDependencies)
+ $(OutDir)$(TargetName)$(TargetExt)
+ true
+ .\KeymanIMX.def
+ true
+ $(OutDir)$(TargetName).pdb
+ $(OutDir)$(TargetName).lib
+
+
+ true
+ .\Debug/KeymanIMX.bsc
+
+
+
+
+
+
+
+
+
+
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ Win32
+ .\Release/KeymanIMX.tlb
+
+
+
+
+ MaxSpeed
+ OnlyExplicitInline
+ WIN32;NDEBUG;_WINDOWS;_USRDLL;KEYMANIMX_EXPORTS;%(PreprocessorDefinitions)
+ true
+ MultiThreaded
+ true
+ Level3
+ true
+ $(outdir)\
+ $(outdir)\
+ $(outdir)\
+ $(outdir)\KeymanIMX.pch
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ 0x0c09
+
+
+ odbc32.lib;odbccp32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;%(AdditionalDependencies)
+ true
+ .\KeymanIMX.def
+ MachineX86
+ $(OutDir)$(TargetName).lib
+ $(OutDir)$(TargetName).pdb
+
+
+ true
+ .\Release/KeymanIMX.bsc
+
+
+
+
+
+
+
+
+
+
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ .\Release/KeymanIMX.tlb
+
+
+
+
+ MaxSpeed
+ OnlyExplicitInline
+ WIN32;NDEBUG;_WINDOWS;_USRDLL;KEYMANIMX_EXPORTS;%(PreprocessorDefinitions)
+ true
+ MultiThreaded
+ true
+ $(outdir)\KeymanIMX.pch
+ $(outdir)\
+ $(outdir)\
+ $(outdir)\
+ Level3
+ true
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ 0x0c09
+
+
+ odbc32.lib;odbccp32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;%(AdditionalDependencies)
+ true
+
+
+ $(OutDir)$(TargetName).pdb
+ $(OutDir)$(TargetName).lib
+
+
+ true
+ .\Release/KeymanIMX.bsc
+
+
+
+
+
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+
+
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
+
+
+
+
+
+
\ No newline at end of file
diff --git a/release/c/cs_pinyin/source/keymanimx/KeymanIMX.vcxproj.filters b/release/c/cs_pinyin/source/keymanimx/KeymanIMX.vcxproj.filters
new file mode 100644
index 0000000000..22101d8c77
--- /dev/null
+++ b/release/c/cs_pinyin/source/keymanimx/KeymanIMX.vcxproj.filters
@@ -0,0 +1,100 @@
+
+
+
+
+ {f4392622-ece5-41a9-a789-52cb0cde2de8}
+ cpp;c;cxx;rc;def;r;odl;idl;hpj;bat
+
+
+ {7105faa7-0424-40f3-ae5e-ae3a9d5f4115}
+ h;hpp;hxx;hm;inl
+
+
+ {15f7159a-5116-4def-8889-aa7d3f31b9c6}
+ ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Source Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Resource Files
+
+
+
\ No newline at end of file
diff --git a/release/c/cs_pinyin/source/keymanimx/ReadMDB.cpp b/release/c/cs_pinyin/source/keymanimx/ReadMDB.cpp
new file mode 100644
index 0000000000..8779e71c32
--- /dev/null
+++ b/release/c/cs_pinyin/source/keymanimx/ReadMDB.cpp
@@ -0,0 +1,42 @@
+#import "c:\Program Files\Common Files\System\ADO\msado15.dll" \
+no_namespace rename("EOF", "EndOfFile")
+// This code comes from : www.geocities.com/mokarrabin
+#include
+#include
+
+int ReadMDB(PSTR pDBPath,PSTR pUser,PSTR pPwd,PSTR pTable)
+
+
+ {
+ CoInitialize(NULL);
+ try
+
+
+ {
+ _RecordsetPtr pRst("ADODB.Recordset");
+ // Connection String
+ _bstr_t strCnn("DRIVER={Microsoft Access Driver (*.mdb)};UID=admin;DBQ=GBOOK.mdb");
+ // Open table
+ pRst->Open("SELECT * FROM ProductService where ProductService like '%samir%';", strCnn, adOpenStatic, adLockReadOnly, adCmdText);
+
+ pRst->MoveFirst();
+
+
+ while (!pRst->EndOfFile) {
+ cout<<(char*) ((_bstr_t) pRst->GetFields()->GetItem("ProductService")->GetValue())<MoveNext();
+ }
+ pRst->Close();
+
+ }
+ catch (_com_error &e)
+
+
+ {
+ cout<<(char*) e.Description();
+ }
+ ::CoUninitialize();
+
+return TRUE;
+ }
+
\ No newline at end of file
diff --git a/release/c/cs_pinyin/source/keymanimx/crc32.cpp b/release/c/cs_pinyin/source/keymanimx/crc32.cpp
new file mode 100644
index 0000000000..2b6fbc543b
--- /dev/null
+++ b/release/c/cs_pinyin/source/keymanimx/crc32.cpp
@@ -0,0 +1,72 @@
+/**************************************************************************
+
+Description: CRC32 algorithm
+
+Copyright (c) 2017 SIL International
+
+**************************************************************************/
+
+#include
+
+// 32 bit CRC checksum calculation
+
+static const DWORD crc_t[] = { /* CRC polynomial 0xedb88320 */
+0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+#define UPDC32(octet, crc) (crc_t[((crc)^(octet)) & 0xff] ^ ((crc)>>8))
+
+DWORD GetCRC32(LPVOID pv, UINT cb)
+{
+ register DWORD crc32;
+ register LPBYTE pb;
+
+ for(pb=(LPBYTE)pv, crc32=0xFFFFFFFF; cb>0; cb--, pb++)
+ {
+ crc32 = UPDC32(*pb,crc32);
+ }
+
+ return crc32;
+}
diff --git a/release/c/cs_pinyin/source/keymanimx/imxlib.cpp b/release/c/cs_pinyin/source/keymanimx/imxlib.cpp
new file mode 100644
index 0000000000..b0952bf654
--- /dev/null
+++ b/release/c/cs_pinyin/source/keymanimx/imxlib.cpp
@@ -0,0 +1,236 @@
+
+#define STRICT
+#include
+#include "imxlib.h"
+
+HMODULE hModuleKeyman32 = 0;
+KMSetOutputProc KMSetOutput = NULL;
+KMGetContextProc KMGetContext = NULL;
+KMQueueActionProc KMQueueAction = NULL;
+KMDisplayIMProc KMDisplayIM = NULL;
+KMHideIMProc KMHideIM = NULL;
+KMGetActiveKeyboardProc KMGetActiveKeyboard = NULL;
+KMGetKeyboardPathProc KMGetKeyboardPath = NULL;
+
+thread_local BOOL FMoveWindow=FALSE, FSizeWindow=FALSE;
+
+extern BOOL Vertical;
+
+// I don't know why this is defined here???????????????
+BOOL WINAPI IntKMGetKeyboardPath(PSTR kbdname, PSTR buf, DWORD len)
+{
+ *kbdname = 0;
+ return FALSE;
+}
+
+BOOL PrepIM(BOOL Reset)
+{
+ if(Reset) FMoveWindow = FSizeWindow = FALSE; // clear when initializing
+
+ if(hModuleKeyman32 != 0) return TRUE; // already prepared
+#ifdef _WIN64
+ hModuleKeyman32 = GetModuleHandle("keyman64.dll");
+#else
+ hModuleKeyman32 = GetModuleHandle("keyman32.dll");
+#endif
+ if(hModuleKeyman32 == 0)
+ {
+ KMGetKeyboardPath = IntKMGetKeyboardPath; // I don't understand this?????????
+ return TRUE;
+ }
+
+ KMSetOutput = (KMSetOutputProc) GetProcAddress(hModuleKeyman32, "KMSetOutput");
+ if(!KMSetOutput) return FALSE;
+
+ KMGetContext = (KMGetContextProc) GetProcAddress(hModuleKeyman32, "KMGetContext");
+ if(!KMGetContext) return FALSE;
+
+ KMQueueAction = (KMQueueActionProc) GetProcAddress(hModuleKeyman32, "KMQueueAction");
+ if(!KMQueueAction) return FALSE;
+
+ KMDisplayIM = (KMDisplayIMProc) GetProcAddress(hModuleKeyman32, "KMDisplayIM");
+ if(!KMDisplayIM) return FALSE;
+
+ KMHideIM = (KMHideIMProc) GetProcAddress(hModuleKeyman32, "KMHideIM");
+ if(!KMHideIM) return FALSE;
+
+ KMGetActiveKeyboard = (KMGetActiveKeyboardProc) GetProcAddress(hModuleKeyman32, "KMGetActiveKeyboard");
+ if(!KMGetActiveKeyboard) return FALSE;
+
+ KMGetKeyboardPath = (KMGetKeyboardPathProc) GetProcAddress(hModuleKeyman32, "KMGetKeyboardPath");
+ if(!KMGetKeyboardPath) return FALSE;
+
+ return TRUE;
+}
+
+// IMDefWindowProc returns TRUE if the window procedure should just return lResult,
+// and not process further.
+
+// The cursor control has been altered to only show sizing arrows for sizable edges, and
+// return the normal pointer in all other cases, to correct behaviour when used with Word
+HCURSOR GetHitTestCursor(HWND hwnd, LRESULT ht, LPRECT sizerect)
+{
+ LPSTR cr = NULL;
+
+ memset(sizerect, 0, sizeof(RECT));
+
+ if(Vertical) switch(ht)
+ {
+ case HTBOTTOMLEFT:
+ case HTBOTTOMRIGHT:
+ case HTBOTTOM:
+ cr = IDC_SIZENS;
+ sizerect->bottom = 1;
+ break;
+ default:
+ cr = IDC_ARROW;
+ break;
+ }
+ else switch(ht)
+ {
+ case HTTOPRIGHT:
+ case HTBOTTOMRIGHT:
+ case HTRIGHT:
+ cr = IDC_SIZEWE;
+ sizerect->right = 1;
+ break;
+ default:
+ cr = IDC_ARROW;
+ break;
+ }
+
+ return LoadCursor(NULL, cr);
+}
+
+void Log(char *p, int n);
+
+BOOL IMDefWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
+{
+ thread_local static POINT FLastPoint;
+ thread_local static RECT sizerect;
+ thread_local static HCURSOR FHitTestCursor;
+
+ RECT rect;
+ POINTS pt;
+ POINT pt2;
+ LRESULT ht;
+
+ switch(msg)
+ {
+ case WM_CREATE:
+ PostMessage(hwnd, WM_NCACTIVATE, TRUE, 0);
+ return FALSE;
+
+ case WM_NCACTIVATE:
+ *lResult = DefWindowProc(hwnd, msg, TRUE, lParam);
+ return TRUE;
+
+ case WM_USER_NCHITTEST:
+ *lResult = DefWindowProc(hwnd, WM_NCHITTEST, wParam, lParam);
+ return TRUE;
+
+ case WM_NCHITTEST:
+ *lResult = HTCLIENT;
+
+ if(FMoveWindow || FSizeWindow) return TRUE;
+
+ ht = DefWindowProc(hwnd, msg, wParam, lParam);
+ SetCursor(FHitTestCursor = GetHitTestCursor(hwnd, ht, &sizerect));
+ return TRUE; //this is equivalent to returning HTCLIENT, which seems odd
+
+ case WM_MOUSEACTIVATE:
+ *lResult = MA_NOACTIVATE;
+ return TRUE;
+
+ case WM_LBUTTONDOWN:
+ pt = MAKEPOINTS(lParam); pt2.x = pt.x; pt2.y = pt.y;
+ ClientToScreen(hwnd, &pt2);
+
+ FMoveWindow = FSizeWindow = FALSE;
+
+ switch((ht=SendMessage(hwnd,WM_USER_NCHITTEST,0,MAKELONG(pt2.x,pt2.y))))
+ {
+ case HTCAPTION:
+ *lResult = 0;
+ FMoveWindow = TRUE;
+ SetCapture(hwnd);
+ FLastPoint = pt2;
+ SetCursor(FHitTestCursor);
+ return TRUE;
+
+ case HTTOP:
+ case HTTOPLEFT:
+ case HTTOPRIGHT:
+ case HTBOTTOM:
+ case HTBOTTOMLEFT:
+ case HTBOTTOMRIGHT:
+ case HTLEFT:
+ case HTRIGHT:
+ *lResult = 0;
+ FSizeWindow = TRUE;
+ SetCapture(hwnd);
+ FLastPoint = pt2;
+ SetCursor(FHitTestCursor);
+ return TRUE;
+
+ default:
+ *lResult = 1;
+ return (ht != HTCLIENT);
+ }
+
+ case WM_MOUSEMOVE:
+
+ GetWindowRect(hwnd, &rect);
+ pt = MAKEPOINTS(lParam); pt2.x = pt.x; pt2.y = pt.y;
+ ClientToScreen(hwnd, &pt2);
+
+ if(FMoveWindow)
+ {
+ MoveWindow(hwnd,rect.left+pt2.x-FLastPoint.x,rect.top+pt2.y-FLastPoint.y,
+ rect.right-rect.left,rect.bottom-rect.top,TRUE);
+ FLastPoint = pt2;
+ SetCursor(FHitTestCursor);
+ return TRUE;
+ }
+ else if(FSizeWindow)
+ {
+ POINT pt3 = pt2; pt3.x -= FLastPoint.x; pt3.y -= FLastPoint.y;
+ RECT r2;
+ r2.left = rect.left + pt3.x*sizerect.left;
+ r2.top = rect.top + pt3.y*sizerect.top;
+ r2.right = rect.right + pt3.x*sizerect.right;
+ r2.bottom = rect.bottom + pt3.y*sizerect.bottom;
+ if(r2.right - r2.left < 16)
+ {
+ r2.right = rect.right; r2.left = rect.left;
+ }
+ else FLastPoint.x = pt2.x;
+ if(r2.bottom - r2.top < 10)
+ {
+ r2.bottom = rect.bottom; r2.top = rect.top;
+ }
+ else FLastPoint.y = pt2.y;
+
+ MoveWindow(hwnd,r2.left,r2.top,r2.right-r2.left,r2.bottom-r2.top,TRUE);
+
+ SetCursor(FHitTestCursor);
+ return TRUE;
+ }
+ return SendMessage(hwnd, WM_USER_NCHITTEST, 0, MAKELONG(pt2.x, pt2.y)) != HTCLIENT;
+
+ case WM_LBUTTONUP:
+// if(FMoveWindow || FSizeWindow)
+// {
+ FMoveWindow = FSizeWindow = FALSE;
+ ReleaseCapture();
+ return TRUE;
+// }
+
+// pt = MAKEPOINTS(lParam); pt2.x = pt.x; pt2.y = pt.y;
+// ClientToScreen(hwnd, &pt2);
+
+// return SendMessage(hwnd, WM_USER_NCHITTEST, 0, MAKELONG(pt2.x, pt2.y)) != HTCLIENT;
+ }
+
+ return FALSE;
+}
diff --git a/release/c/cs_pinyin/source/keymanimx/imxlib.h b/release/c/cs_pinyin/source/keymanimx/imxlib.h
new file mode 100644
index 0000000000..0f79a46fff
--- /dev/null
+++ b/release/c/cs_pinyin/source/keymanimx/imxlib.h
@@ -0,0 +1,36 @@
+
+#ifndef _IMLIB_H
+#define _IMLIB_H
+
+#define QIT_VKEYDOWN 0
+#define QIT_VKEYUP 1
+#define QIT_VSHIFTDOWN 2
+#define QIT_VSHIFTUP 3
+#define QIT_CHAR 4
+#define QIT_DEADKEY 5
+#define QIT_BELL 6
+#define QIT_BACK 7
+
+#define WM_USER_NCHITTEST WM_USER+400
+
+typedef BOOL (WINAPI *KMSetOutputProc)(PWSTR buf, DWORD backlen);
+typedef BOOL (WINAPI *KMGetContextProc)(PWSTR buf, DWORD len);
+typedef BOOL (WINAPI *KMQueueActionProc)(int action, DWORD dwData);
+typedef BOOL (WINAPI *KMDisplayIMProc)(HWND hwnd, BOOL FStayOpen);
+typedef BOOL (WINAPI *KMGetActiveKeyboardProc)(PSTR kbdname, DWORD len);
+typedef BOOL (WINAPI *KMHideIMProc)();
+typedef BOOL (WINAPI *KMGetKeyboardPathProc)(PSTR kbdname, PSTR buf, DWORD len);
+
+extern HMODULE hModuleKeyman32;
+extern KMSetOutputProc KMSetOutput;
+extern KMGetContextProc KMGetContext;
+extern KMQueueActionProc KMQueueAction;
+extern KMDisplayIMProc KMDisplayIM;
+extern KMHideIMProc KMHideIM;
+extern KMGetActiveKeyboardProc KMGetActiveKeyboard;
+extern KMGetKeyboardPathProc KMGetKeyboardPath;
+
+BOOL PrepIM(BOOL Reset);
+BOOL IMDefWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *lResult);
+
+#endif
diff --git a/release/c/cs_pinyin/source/keymanimx/keymanimx.h b/release/c/cs_pinyin/source/keymanimx/keymanimx.h
new file mode 100644
index 0000000000..afa89647a3
--- /dev/null
+++ b/release/c/cs_pinyin/source/keymanimx/keymanimx.h
@@ -0,0 +1,115 @@
+// Shared header for KeymanIMX, used also by imxconfig
+
+#define CFXVERSION 1001 // CFX file version
+
+#define MAXINDEX 72 // max number of characters that can be indexed
+
+#define MAXRULESFREE 6000 // max number of rules allowed for unregistered Keyman
+
+#define MAXOUTLEN 64 // required when passing strings to Keyman
+#define MAXTAGLEN 15 // will be truncated with ellipsis if longer
+
+#define HK_LCTRL 1 // hot key shift state bit masks
+#define HK_RCTRL 2
+#define HK_LALT 4
+#define HK_RALT 8
+#define HK_SHIFT 16
+#define HK_CTRL (HK_LCTRL+HK_RCTRL)
+#define HK_ALT (HK_LALT+HK_RALT)
+#define HK_MODIFIERS (HK_SHIFT+HK_CTRL+HK_ALT)
+
+#define OM_DEFAULT 0 // default output mode
+#define OM_TAG 2 // output tag strings
+#define OM_BOTH 4 // output both character and tag (parenthesized)
+
+#define DM_LIMITED 0 // limited display mode
+#define DM_FULL 1 // full display mode
+
+#define WC_QUERY 1 // wild card options
+#define WC_STAR 2
+#define WC_QUOTE 4
+#define WC_DASH 8
+
+#define EnableDlgItem(hwnd,item) EnableWindow(GetDlgItem((hwnd),(item)),TRUE)
+#define DisableDlgItem(hwnd,item) EnableWindow(GetDlgItem((hwnd),(item)),FALSE)
+#define ShowDlgItem(hwnd,item) ShowWindow(GetDlgItem((hwnd),(item)),SW_SHOW)
+#define HideDlgItem(hwnd,item) ShowWindow(GetDlgItem((hwnd),(item)),SW_HIDE)
+
+// First character of a string that can be indexed (count must be < MAXINDEX)
+#define INDEXSTRING "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%&*"
+
+// Location for Registry settings
+#define REGSZ_KeymanIMX "Software\\Keyman\\KeymanIMX\\10.0"
+
+struct RULE
+{
+ DWORD input,tag,output;
+ BYTE ilen,tlen,olen,frequency;
+};
+
+struct TEMPRULE
+{
+ WCHAR input[32],output[16],tag[32];
+};
+
+// Structure to hold all constant parameters and default keyboard options
+// dwIndex, dwRules, dwStrings are byte offsets into the memory mapped file
+struct KBHEADER
+{
+ char Flag[4]; // must be KCFX
+ DWORD dwCfxVersion; // CFX file version
+ char name[128], ToneChar[16];
+ int InputSize, gridx, gridy, nrules;
+ LOGFONT lfMain, lfInput, lfTag;
+ DWORD cfgKey, dwWild, dwFKeys, dwOptions;
+ DWORD dwIndex, dwRules, dwStrings, cbStrings;
+};
+
+// Structure to hold keyboard parameters, pointers, and local variables
+struct KEYBOARD
+{
+ KBHEADER h;
+ HANDLE hFile, hMapObject;
+ LPVOID BaseAddress; // pointer to start of shared mapped memory
+ DWORD *index; // pointer to start of index (must be corrected for each instance)
+ RULE *rules; // pointer to start of rule table (ditto)
+ WCHAR *pst; // pointer to start of string table (ditto)
+};
+
+// Option bits
+#define SELECTIONBITS 3
+#define TONEBITS 12
+#define SELECTIFUNIQUE 16
+#define SHOWDUPLICATES 32
+
+enum tagConfig {CFG_NONE=0,CFG_ANSI,CFG_UNICODE,CFG_UTF8};
+
+enum tagFontStyle {FS_NONE=0,FS_BOLD,FS_ITALIC,FS_ITALICBOLD,FC_SYMBOL};
+
+enum tagSelectionMode {SM_DEFAULT=0,SM_KEYPAD,SM_ALLDIGITS,SM_FKEYS};
+
+enum tagToneMode {TM_NEVER=0,TM_ALWAYS,TM_OPTIONAL};
+
+enum tagFKeyMode {FK_NONE=0,FK_NORMAL,FK_SHIFT,FK_CTRL,FK_CTRLSHIFT};
+
+enum tagMatchMode {MM_NOMATCH=0,MM_START,MM_EXCEPTTONE,MM_MORETHANONE,MM_PERFECT};
+
+enum tagErrors {ERR_NONE,ERR_UNKNOWN,ERR_UNREGISTERED,ERR_SHARING,ERR_CFGMISSING,ERR_CFGINVALID};
+
+enum tagRegKeys {KEY_KEYMAN=1,KEY_KEYMANDEVELOPER};
+
+#define MAXACTIVERULES 12
+
+// Default IM Window dimensions
+#define IMBORDERS 8
+#define CWBORDERS 6
+#define INPUTWIDTH 42 // this is increased dynamically as needed
+#define CELL_X 32
+#define CELL_Y 42
+#define IM_WIDTH (INPUTWIDTH+MAXACTIVERULES*CELL_X+IMBORDERS+CWBORDERS)
+#define IM_HEIGHT (CELL_Y+IMBORDERS+CWBORDERS)
+#define MAXBMP 15
+
+// Surrogate pair output
+#define Uni_UTF32ToSurrogate1(ch) (((ch) - 0x10000) / 0x400 + 0xD800)
+#define Uni_UTF32ToSurrogate2(ch) (((ch) - 0x10000) % 0x400 + 0xDC00)
diff --git a/release/c/cs_pinyin/source/keymanimx/resource.h b/release/c/cs_pinyin/source/keymanimx/resource.h
new file mode 100644
index 0000000000..d06d30d124
--- /dev/null
+++ b/release/c/cs_pinyin/source/keymanimx/resource.h
@@ -0,0 +1,16 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by KeymanIMX.rc
+//
+#define IDB_BITMAP1 101
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 114
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1002
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/release/c/cs_pinyin/source/keymanimx/version.rc b/release/c/cs_pinyin/source/keymanimx/version.rc
new file mode 100644
index 0000000000..36192bb371
--- /dev/null
+++ b/release/c/cs_pinyin/source/keymanimx/version.rc
@@ -0,0 +1,51 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+/////////////////////////////////////////////////////////////////////////////
+// English (Australia) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (Australia) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+