diff --git a/.cargo/config.toml b/.cargo/config.toml index 35049cb..58fdfbb 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,2 +1,8 @@ [alias] xtask = "run --package xtask --" + +[target.i686-pc-windows-msvc] +rustflags = ["-C", "target-feature=+crt-static"] + +[target.x86_64-pc-windows-msvc] +rustflags = ["-C", "target-feature=+crt-static"] diff --git a/CMakeLists.txt b/CMakeLists.txt index 11e1aaf..7d896ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,25 +2,7 @@ cmake_minimum_required(VERSION 3.24.0) project(windows-chewing-tsf LANGUAGES CXX) -option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) - -# http://www.utf8everywhere.org/ -add_definitions( - /D_UNICODE=1 /DUNICODE=1 # do Unicode build - /D_CRT_SECURE_NO_WARNINGS # disable warnings about old libc functions -) -set(CMAKE_CXX_STANDARD 17) - -if (MSVC) - add_compile_options(/utf-8) -endif() - -# Static link MSVC runtime -set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded") - add_subdirectory(libchewing) -add_subdirectory(libIME) -add_subdirectory(ChewingTextService) add_subdirectory(ChewingPreferences) set(CPACK_PACKAGE_CHECKSUM SHA256) diff --git a/Cargo.lock b/Cargo.lock index 3c81944..6b8ef0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,47 +2,77 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "anyhow" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] -name = "indoc" -version = "2.0.5" +name = "bitflags" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] -name = "jiff" -version = "0.1.15" +name = "cc" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db69f08d4fb10524cacdb074c10b296299d71274ddbc830a8ee65666867002e9" +checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" dependencies = [ - "jiff-tzdb-platform", - "windows-sys", + "shlex", ] [[package]] -name = "jiff-tzdb" -version = "0.1.1" +name = "cfg-if" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91335e575850c5c4c673b9bd467b0e025f164ca59d0564f69d0c2ee0ffad4653" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "jiff-tzdb-platform" -version = "0.1.1" +name = "chewing" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9835f0060a626fe59f160437bc725491a6af23133ea906500027d1bd2f8f4329" +checksum = "990478084d4eb9ca92513d75e780be2062dbab9dc46a3a656f622639a697d31c" dependencies = [ - "jiff-tzdb", + "der", + "directories", + "log", + "rusqlite", ] [[package]] -name = "libime2" +name = "chewing_capi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf423f10275a7ccb3f268a35c4e736fa37b71485b12dc65f870e0fb0bda389f" +dependencies = [ + "chewing", + "env_logger", + "log", +] + +[[package]] +name = "chewing_tip" version = "24.10.1" dependencies = [ + "anyhow", + "cc", + "chewing_capi", + "embed-resource", + "getrandom", "log", "nine_patch_drawable", "win_dbg_logger", @@ -50,18 +80,206 @@ dependencies = [ "windows-core", ] +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "zeroize", +] + +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys", +] + +[[package]] +name = "embed-resource" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4762ce03154ba57ebaeee60cc631901ceae4f18219cbb874e464347471594742" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml", + "vswhom", + "winreg", +] + +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", +] + +[[package]] +name = "env_logger" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" +dependencies = [ + "env_filter", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "indexmap" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", +] + +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + +[[package]] +name = "jiff" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db69f08d4fb10524cacdb074c10b296299d71274ddbc830a8ee65666867002e9" + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "pkg-config", + "vcpkg", +] + [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + [[package]] name = "nine_patch_drawable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d96e2f356f3fbb4179ee2a3d76ed2881553b93213a421a28975b2ea0c8d81d15" +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + [[package]] name = "proc-macro2" version = "1.0.92" @@ -80,6 +298,87 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "rusqlite" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" +dependencies = [ + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" + +[[package]] +name = "serde" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + [[package]] name = "syn" version = "2.0.91" @@ -91,6 +390,60 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tsfreg" version = "24.10.1" @@ -104,6 +457,44 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b17ae1f6c8a2b28506cd96d412eebf83b4a0ff2cbefeeb952f2f9dfa44ba18" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "win_dbg_logger" version = "0.1.0" @@ -120,7 +511,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ "windows-core", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -133,7 +524,7 @@ dependencies = [ "windows-interface", "windows-result", "windows-strings", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -164,7 +555,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -174,16 +565,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ "windows-result", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] name = "windows-sys" -version = "0.59.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -192,28 +598,46 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -226,30 +650,73 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys", +] + [[package]] name = "xflags" version = "0.3.2" @@ -274,3 +741,29 @@ dependencies = [ "jiff", "xflags", ] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index 54d16f4..eaa0581 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,8 @@ [workspace] -members = ["libIME", "tsfreg", "xtask"] +members = ["chewing_tip", "tsfreg", "xtask"] resolver = "2" + +[profile.release] +lto = "thin" +panic = "abort" +strip = true diff --git a/ChewingPreferences/CMakeLists.txt b/ChewingPreferences/CMakeLists.txt index f7ab42e..b90bae3 100644 --- a/ChewingPreferences/CMakeLists.txt +++ b/ChewingPreferences/CMakeLists.txt @@ -1,15 +1,25 @@ +cmake_minimum_required(VERSION 3.24.0) project(ChewingPreferences LANGUAGES CXX) -include_directories( - ${CMAKE_SOURCE_DIR} +# http://www.utf8everywhere.org/ +add_definitions( + /D_UNICODE=1 /DUNICODE=1 # do Unicode build + /D_CRT_SECURE_NO_WARNINGS # disable warnings about old libc functions ) +if (MSVC) + add_compile_options(/utf-8) +endif() + +# Static link MSVC runtime +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded") add_executable(ChewingPreferences WIN32 # Entry point ${PROJECT_SOURCE_DIR}/ChewingPreferences.cpp # Configurations - ${CMAKE_SOURCE_DIR}/ChewingTextService/ChewingConfig.cpp - ${CMAKE_SOURCE_DIR}/ChewingTextService/ChewingConfig.h + ../chewing_tip/ChewingConfig.cpp + ../chewing_tip/ChewingConfig.h # Configuration dialog ${PROJECT_SOURCE_DIR}/Dialog.cpp ${PROJECT_SOURCE_DIR}/Dialog.h @@ -31,9 +41,12 @@ add_executable(ChewingPreferences WIN32 # resources ${PROJECT_SOURCE_DIR}/ChewingPreferences.rc ${PROJECT_SOURCE_DIR}/ChewingPreferences.exe.manifest - ${CMAKE_SOURCE_DIR}/ChewingTextService/mainicon.ico - ${CMAKE_SOURCE_DIR}/ChewingTextService/logo.bmp + ../chewing_tip/mainicon.ico + ../chewing_tip/logo.bmp +) +target_include_directories(ChewingPreferences + PRIVATE ../chewing_tip ) target_link_libraries(ChewingPreferences comctl32.lib -) \ No newline at end of file +) diff --git a/ChewingPreferences/ChewingPreferences.cpp b/ChewingPreferences/ChewingPreferences.cpp index 796412f..56d714c 100644 --- a/ChewingPreferences/ChewingPreferences.cpp +++ b/ChewingPreferences/ChewingPreferences.cpp @@ -21,7 +21,6 @@ #include "UiPropertyPage.h" #include "KeyboardPropertyPage.h" #include "SymbolsPropertyPage.h" -#include "Dialog.h" #include "PropertyDialog.h" #include "AboutDialog.h" #include "resource.h" @@ -33,10 +32,6 @@ namespace Chewing { -// {F4D1E543-FB2C-48D7-B78D-20394F355381} // global compartment GUID for config change notification -static const GUID g_configChangedGuid = -{ 0xf4d1e543, 0xfb2c, 0x48d7, { 0xb7, 0x8d, 0x20, 0x39, 0x4f, 0x35, 0x53, 0x81 } }; - static void initControls() { INITCOMMONCONTROLSEX ic; ic.dwSize = sizeof(ic); @@ -65,35 +60,7 @@ static void configDialog(HINSTANCE hInstance) { dlg.addPage(symbolsPage); INT_PTR ret = dlg.showModal(hInstance, (LPCTSTR)IDS_CONFIG_TITLE, 0, HWND_DESKTOP); if(ret) { // the user clicks OK button - // get current time stamp and set the value to global compartment to notify all - // text service instances to reload their config. - // TextService::onCompartmentChanged() of all other instances will be triggered. config.save(); - - DWORD stamp = ::GetTickCount(); - if(stamp == Config::INVALID_TIMESTAMP) // this is almost impossible - stamp = 0; - // set global compartment value to notify existing ChewingTextService instances - ::CoInitialize(NULL); // initialize COM - winrt::com_ptr threadMgr; - if(CoCreateInstance(CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, threadMgr.put_void()) == S_OK) { - TfClientId clientId = 0; - if(threadMgr->Activate(&clientId) == S_OK) { - winrt::com_ptr compartmentMgr; - if(threadMgr->GetGlobalCompartment(compartmentMgr.put()) == S_OK) { - winrt::com_ptr compartment; - if(compartmentMgr->GetCompartment(g_configChangedGuid, compartment.put()) == S_OK) { - VARIANT var; - VariantInit(&var); - var.vt = VT_I4; - var.lVal = ::GetTickCount(); // set current timestamp - compartment->SetValue(clientId, &var); - } - } - threadMgr->Deactivate(); - } - } - ::CoUninitialize(); } } diff --git a/ChewingPreferences/ChewingPreferences.rc b/ChewingPreferences/ChewingPreferences.rc index 88c6192..f0e0b38 100644 --- a/ChewingPreferences/ChewingPreferences.rc +++ b/ChewingPreferences/ChewingPreferences.rc @@ -51,7 +51,7 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. -IDI_ICON ICON "../ChewingTextService/im.chewing.Chewing.ico" +IDI_ICON ICON "../chewing_tip/im.chewing.Chewing.ico" ///////////////////////////////////////////////////////////////////////////// // @@ -97,7 +97,7 @@ END // Bitmap // -IDB_COOL BITMAP "../ChewingTextService/logo.bmp" +IDB_COOL BITMAP "../chewing_tip/logo.bmp" ///////////////////////////////////////////////////////////////////////////// // diff --git a/ChewingPreferences/KeyboardPropertyPage.h b/ChewingPreferences/KeyboardPropertyPage.h index e902209..cae2c82 100644 --- a/ChewingPreferences/KeyboardPropertyPage.h +++ b/ChewingPreferences/KeyboardPropertyPage.h @@ -22,7 +22,7 @@ #pragma once #include "PropertyPage.h" -#include +#include "ChewingConfig.h" namespace Chewing { diff --git a/ChewingPreferences/SymbolsPropertyPage.h b/ChewingPreferences/SymbolsPropertyPage.h index 079a888..5bbb141 100644 --- a/ChewingPreferences/SymbolsPropertyPage.h +++ b/ChewingPreferences/SymbolsPropertyPage.h @@ -22,7 +22,7 @@ #pragma once #include "PropertyPage.h" -#include +#include "ChewingConfig.h" #include namespace Chewing { diff --git a/ChewingPreferences/TypingPropertyPage.h b/ChewingPreferences/TypingPropertyPage.h index 8575f2d..18ebd02 100644 --- a/ChewingPreferences/TypingPropertyPage.h +++ b/ChewingPreferences/TypingPropertyPage.h @@ -21,7 +21,7 @@ #define CHEWING_TYPING_PROPERTY_PAGE_H #include "PropertyPage.h" -#include +#include "ChewingConfig.h" namespace Chewing { diff --git a/ChewingPreferences/UiPropertyPage.h b/ChewingPreferences/UiPropertyPage.h index 46fc835..dff3423 100644 --- a/ChewingPreferences/UiPropertyPage.h +++ b/ChewingPreferences/UiPropertyPage.h @@ -22,7 +22,7 @@ #pragma once #include "PropertyPage.h" -#include +#include "ChewingConfig.h" namespace Chewing { diff --git a/ChewingTextService/CMakeLists.txt b/ChewingTextService/CMakeLists.txt deleted file mode 100644 index 6ff8448..0000000 --- a/ChewingTextService/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -project(ChewingTextService) - -include_directories( - ${CMAKE_SOURCE_DIR} -) - -add_library(ChewingTextService SHARED - # core IME implementation - ${PROJECT_SOURCE_DIR}/ChewingTextService.cpp - ${PROJECT_SOURCE_DIR}/ChewingTextService.def - ${PROJECT_SOURCE_DIR}/ChewingTextService.h - ${PROJECT_SOURCE_DIR}/DllEntry.cpp - ${PROJECT_SOURCE_DIR}/CClassFactory.cpp - ${PROJECT_SOURCE_DIR}/CClassFactory.h - ${PROJECT_SOURCE_DIR}/ChewingConfig.cpp - ${PROJECT_SOURCE_DIR}/ChewingConfig.h - # resources - ${PROJECT_SOURCE_DIR}/ChewingTextService.rc - ${PROJECT_SOURCE_DIR}/mainicon.ico - # manifest - ${PROJECT_SOURCE_DIR}/ChewingTextService.manifest -) - -target_link_libraries(ChewingTextService - libchewing - libIME_static -) -target_link_options(ChewingTextService - PRIVATE /NODEFAULTLIB:MSVCRT - PRIVATE /NODEFAULTLIB:MSVCRTD - PRIVATE /NODEFAULTLIB:LIBCMTD -) \ No newline at end of file diff --git a/ChewingTextService/ChewingTextService.def b/ChewingTextService/ChewingTextService.def deleted file mode 100644 index bbbf609..0000000 --- a/ChewingTextService/ChewingTextService.def +++ /dev/null @@ -1,4 +0,0 @@ -LIBRARY ChewingTextService.dll - -EXPORTS - DllGetClassObject PRIVATE \ No newline at end of file diff --git a/ChewingTextService/CClassFactory.cpp b/chewing_tip/CClassFactory.cpp similarity index 98% rename from ChewingTextService/CClassFactory.cpp rename to chewing_tip/CClassFactory.cpp index 50ec36b..84caaa5 100644 --- a/ChewingTextService/CClassFactory.cpp +++ b/chewing_tip/CClassFactory.cpp @@ -60,4 +60,4 @@ STDMETHODIMP CClassFactory::LockServer(BOOL fLock) { return S_OK; } -} // namespace Chewing \ No newline at end of file +} // namespace Chewing diff --git a/ChewingTextService/CClassFactory.h b/chewing_tip/CClassFactory.h similarity index 90% rename from ChewingTextService/CClassFactory.h rename to chewing_tip/CClassFactory.h index 140e021..341e66a 100644 --- a/ChewingTextService/CClassFactory.h +++ b/chewing_tip/CClassFactory.h @@ -19,9 +19,9 @@ class CClassFactory : public IClassFactory { STDMETHODIMP LockServer(BOOL fLock); private: - ~CClassFactory() {} + virtual ~CClassFactory() {} unsigned long refCount_; }; -} // namespace Chewing \ No newline at end of file +} // namespace Chewing diff --git a/libIME/Cargo.toml b/chewing_tip/Cargo.toml similarity index 75% rename from libIME/Cargo.toml rename to chewing_tip/Cargo.toml index 87e6ae3..ca86d3b 100644 --- a/libIME/Cargo.toml +++ b/chewing_tip/Cargo.toml @@ -1,16 +1,19 @@ [package] -name = "libime2" +name = "chewing_tip" version = "24.10.1" edition = "2021" +build = "build.rs" + [lib] -crate-type = ["staticlib"] +crate-type = ["cdylib"] [dependencies] log = "0.4.22" nine_patch_drawable = "0.1.0" win_dbg_logger = "0.1.0" windows-core = "0.58.0" +chewing_capi = { version = "0.9.1", features = ["sqlite"] } windows = { version = "0.58.0", features = [ "implement", "Foundation_Numerics", @@ -29,3 +32,9 @@ windows = { version = "0.58.0", features = [ "Win32_UI_TextServices", "Win32_UI_WindowsAndMessaging", ] } +getrandom = "0.2.15" + +[build-dependencies] +anyhow = "1.0.95" +cc = "1.2.5" +embed-resource = "3.0.1" diff --git a/ChewingTextService/ChewingConfig.cpp b/chewing_tip/ChewingConfig.cpp similarity index 79% rename from ChewingTextService/ChewingConfig.cpp rename to chewing_tip/ChewingConfig.cpp index 7496eef..d6d8bdc 100644 --- a/ChewingTextService/ChewingConfig.cpp +++ b/chewing_tip/ChewingConfig.cpp @@ -92,8 +92,6 @@ Config::Config(): easySymbolsWithShift = 1; easySymbolsWithCtrl = 0; upperCaseWithShift = 0; - - stamp = INVALID_TIMESTAMP; } Config::~Config(void) { @@ -163,33 +161,35 @@ void Config::save() { BOOL isWow64 = FALSE; ::IsWow64Process(process, &isWow64); DWORD regFlags = isWow64 ? KEY_WOW64_64KEY : 0; + DWORD timestamp = GetTickCount(); HKEY hk = NULL; LSTATUS ret = ::RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\ChewingTextService", 0, NULL, 0, regFlags|KEY_READ|KEY_WRITE , NULL, &hk, NULL); if(ERROR_SUCCESS == ret) { - ::RegSetValueEx(hk, L"KeyboardLayout", 0, REG_DWORD, (LPBYTE)&keyboardLayout, sizeof(DWORD)); - ::RegSetValueEx(hk, L"CandPerRow", 0, REG_DWORD, (LPBYTE)&candPerRow, sizeof(DWORD)); - ::RegSetValueEx(hk, L"DefaultEnglish", 0, REG_DWORD, (LPBYTE)&defaultEnglish, sizeof(DWORD)); - ::RegSetValueEx(hk, L"DefaultFullSpace", 0, REG_DWORD, (LPBYTE)&defaultFullSpace, sizeof(DWORD)); - ::RegSetValueEx(hk, L"ShowCandWithSpaceKey", 0, REG_DWORD, (LPBYTE)&showCandWithSpaceKey, sizeof(DWORD)); - ::RegSetValueEx(hk, L"SwitchLangWithShift", 0, REG_DWORD, (LPBYTE)&switchLangWithShift, sizeof(DWORD)); - ::RegSetValueEx(hk, L"OutputSimpChinese", 0, REG_DWORD, (LPBYTE)&outputSimpChinese, sizeof(DWORD)); - ::RegSetValueEx(hk, L"AddPhraseForward", 0, REG_DWORD, (LPBYTE)&addPhraseForward, sizeof(DWORD)); - ::RegSetValueEx(hk, L"ColorCandWnd", 0, REG_DWORD, (LPBYTE)&colorCandWnd, sizeof(DWORD)); - ::RegSetValueEx(hk, L"AdvanceAfterSelection", 0, REG_DWORD, (LPBYTE)&advanceAfterSelection, sizeof(DWORD)); - ::RegSetValueEx(hk, L"DefFontSize", 0, REG_DWORD, (LPBYTE)&fontSize, sizeof(DWORD)); - ::RegSetValueEx(hk, L"SelKeyType", 0, REG_DWORD, (LPBYTE)&selKeyType, sizeof(DWORD)); - ::RegSetValueEx(hk, L"ConvEngine", 0, REG_DWORD, (LPBYTE)&convEngine, sizeof(DWORD)); - ::RegSetValueEx(hk, L"SelAreaLen", 0, REG_DWORD, (LPBYTE)&candPerPage, sizeof(DWORD)); - ::RegSetValueEx(hk, L"CursorCandList", 0, REG_DWORD, (LPBYTE)&cursorCandList, sizeof(DWORD)); - ::RegSetValueEx(hk, L"EnableCapsLock", 0, REG_DWORD, (LPBYTE)&enableCapsLock, sizeof(DWORD)); - ::RegSetValueEx(hk, L"FullShapeSymbols", 0, REG_DWORD, (LPBYTE)&fullShapeSymbols, sizeof(DWORD)); - ::RegSetValueEx(hk, L"PhraseMark", 0, REG_DWORD, (LPBYTE)&phraseMark, sizeof(DWORD)); - ::RegSetValueEx(hk, L"EscCleanAllBuf", 0, REG_DWORD, (LPBYTE)&escCleanAllBuf, sizeof(DWORD)); - ::RegSetValueEx(hk, L"EasySymbolsWithShift", 0, REG_DWORD, (LPBYTE)&easySymbolsWithShift, sizeof(DWORD)); - ::RegSetValueEx(hk, L"EasySymbolsWithCtrl", 0, REG_DWORD, (LPBYTE)&easySymbolsWithCtrl, sizeof(DWORD)); - ::RegSetValueEx(hk, L"UpperCaseWithShift", 0, REG_DWORD, (LPBYTE)&upperCaseWithShift, sizeof(DWORD)); + ::RegSetValueExW(hk, L"KeyboardLayout", 0, REG_DWORD, (LPBYTE)&keyboardLayout, sizeof(DWORD)); + ::RegSetValueExW(hk, L"CandPerRow", 0, REG_DWORD, (LPBYTE)&candPerRow, sizeof(DWORD)); + ::RegSetValueExW(hk, L"DefaultEnglish", 0, REG_DWORD, (LPBYTE)&defaultEnglish, sizeof(DWORD)); + ::RegSetValueExW(hk, L"DefaultFullSpace", 0, REG_DWORD, (LPBYTE)&defaultFullSpace, sizeof(DWORD)); + ::RegSetValueExW(hk, L"ShowCandWithSpaceKey", 0, REG_DWORD, (LPBYTE)&showCandWithSpaceKey, sizeof(DWORD)); + ::RegSetValueExW(hk, L"SwitchLangWithShift", 0, REG_DWORD, (LPBYTE)&switchLangWithShift, sizeof(DWORD)); + ::RegSetValueExW(hk, L"OutputSimpChinese", 0, REG_DWORD, (LPBYTE)&outputSimpChinese, sizeof(DWORD)); + ::RegSetValueExW(hk, L"AddPhraseForward", 0, REG_DWORD, (LPBYTE)&addPhraseForward, sizeof(DWORD)); + ::RegSetValueExW(hk, L"ColorCandWnd", 0, REG_DWORD, (LPBYTE)&colorCandWnd, sizeof(DWORD)); + ::RegSetValueExW(hk, L"AdvanceAfterSelection", 0, REG_DWORD, (LPBYTE)&advanceAfterSelection, sizeof(DWORD)); + ::RegSetValueExW(hk, L"DefFontSize", 0, REG_DWORD, (LPBYTE)&fontSize, sizeof(DWORD)); + ::RegSetValueExW(hk, L"SelKeyType", 0, REG_DWORD, (LPBYTE)&selKeyType, sizeof(DWORD)); + ::RegSetValueExW(hk, L"ConvEngine", 0, REG_DWORD, (LPBYTE)&convEngine, sizeof(DWORD)); + ::RegSetValueExW(hk, L"SelAreaLen", 0, REG_DWORD, (LPBYTE)&candPerPage, sizeof(DWORD)); + ::RegSetValueExW(hk, L"CursorCandList", 0, REG_DWORD, (LPBYTE)&cursorCandList, sizeof(DWORD)); + ::RegSetValueExW(hk, L"EnableCapsLock", 0, REG_DWORD, (LPBYTE)&enableCapsLock, sizeof(DWORD)); + ::RegSetValueExW(hk, L"FullShapeSymbols", 0, REG_DWORD, (LPBYTE)&fullShapeSymbols, sizeof(DWORD)); + ::RegSetValueExW(hk, L"PhraseMark", 0, REG_DWORD, (LPBYTE)&phraseMark, sizeof(DWORD)); + ::RegSetValueExW(hk, L"EscCleanAllBuf", 0, REG_DWORD, (LPBYTE)&escCleanAllBuf, sizeof(DWORD)); + ::RegSetValueExW(hk, L"EasySymbolsWithShift", 0, REG_DWORD, (LPBYTE)&easySymbolsWithShift, sizeof(DWORD)); + ::RegSetValueExW(hk, L"EasySymbolsWithCtrl", 0, REG_DWORD, (LPBYTE)&easySymbolsWithCtrl, sizeof(DWORD)); + ::RegSetValueExW(hk, L"UpperCaseWithShift", 0, REG_DWORD, (LPBYTE)&upperCaseWithShift, sizeof(DWORD)); + ::RegSetValueExW(hk, L"ModifiedTimestamp", 0, REG_DWORD, (LPBYTE)×tamp, sizeof(DWORD)); ::RegCloseKey(hk); @@ -199,17 +199,11 @@ void Config::save() { } } -void Config::reloadIfNeeded(DWORD timestamp) { - if(stamp != timestamp) { - load(); - stamp = timestamp; - } -} - bool Config::reloadIfNeeded() { if (hChangeEvent) { DWORD result = WaitForSingleObject(hChangeEvent, 0); if (WAIT_OBJECT_0 == result) { + OutputDebugStringW(L"[chewing] config change detected, reload.\n"); load(); watchChanges(); return true; diff --git a/ChewingTextService/ChewingConfig.h b/chewing_tip/ChewingConfig.h similarity index 92% rename from ChewingTextService/ChewingConfig.h rename to chewing_tip/ChewingConfig.h index 07cef19..dea7cc5 100644 --- a/ChewingTextService/ChewingConfig.h +++ b/chewing_tip/ChewingConfig.h @@ -34,8 +34,6 @@ class Config { void load(); void save(); - // reload configurations if changes are detected (if timestamp is different from this->stamp) - void reloadIfNeeded(DWORD timestamp); // Returns true if config was reloaded bool reloadIfNeeded(); void watchChanges(); @@ -46,10 +44,6 @@ class Config { static bool createSecurityDesc(SECURITY_DESCRIPTOR& sd); public: - enum { - INVALID_TIMESTAMP = (DWORD)-1 - }; - // Configuration DWORD keyboardLayout; // keyboard type DWORD candPerRow; // candidate string per row (not supported yet) @@ -78,7 +72,6 @@ class Config { static const wchar_t* convEngines[]; private: - DWORD stamp; // timestamp used to check if the config values are up to date HANDLE hChangeEvent; HKEY monitorHkey; }; diff --git a/ChewingTextService/ChewingTextService.cpp b/chewing_tip/ChewingTextService.cpp similarity index 90% rename from ChewingTextService/ChewingTextService.cpp rename to chewing_tip/ChewingTextService.cpp index e002bf2..9be4cc1 100644 --- a/ChewingTextService/ChewingTextService.cpp +++ b/chewing_tip/ChewingTextService.cpp @@ -24,13 +24,16 @@ #include #include +#include +#include #include -#include -#include +#include #include +#include #include #include #include +#include #include #include #include @@ -38,6 +41,8 @@ #include #include +#include "TextService.h" +#include "Utils.h" #include "resource.h" #include "libime2.h" @@ -64,10 +69,6 @@ static const GUID g_settingsButtonGuid = // settings button/menu static const GUID g_shiftSpaceGuid = // shift + space { 0xc77a44f5, 0xdb21, 0x474e, { 0xa2, 0xa2, 0xa1, 0x72, 0x42, 0x21, 0x7a, 0xb3 } }; -// {F4D1E543-FB2C-48D7-B78D-20394F355381} // global compartment GUID for config change notification -static const GUID g_configChangedGuid = -{ 0xf4d1e543, 0xfb2c, 0x48d7, { 0xb7, 0x8d, 0x20, 0x39, 0x4f, 0x35, 0x53, 0x81 } }; - // this is the GUID of the IME mode icon in Windows 8 // the value is not available in older SDK versions, so let's define it ourselves. static const GUID _GUID_LBI_INPUTMODE = @@ -86,50 +87,15 @@ TextService::TextService(): outputSimpChinese_(false), lastKeyDownCode_(0), messageTimerId_(0), - imeModeIcon_(NULL), symbolsFileTime_(0), chewingContext_(NULL) { + OutputDebugStringW(L"[chewing] Load config and start watching changes\n"); config_.load(); config_.watchChanges(); // add preserved keys addPreservedKey(VK_SPACE, TF_MOD_SHIFT, g_shiftSpaceGuid); // shift + space - - // add language bar buttons - // siwtch Chinese/English modes - switchLangButton_ = new Ime::LangBarButton(this, g_modeButtonGuid, ID_SWITCH_LANG); - switchLangButton_->setTooltip(IDS_SWITCH_LANG); - addButton(switchLangButton_); - - // toggle full shape/half shape - switchShapeButton_ = new Ime::LangBarButton(this, g_shapeTypeButtonGuid, ID_SWITCH_SHAPE); - switchShapeButton_->setTooltip(IDS_SWITCH_SHAPE); - addButton(switchShapeButton_); - - // settings and others, may open a popup menu - Ime::LangBarButton* button = new Ime::LangBarButton(this, g_settingsButtonGuid); - button->setTooltip(IDS_SETTINGS); - button->setIcon(IDI_CONFIG); - HMENU menu = ::LoadMenuW(g_hInstance, LPCTSTR(IDR_MENU)); - popupMenu_ = ::GetSubMenu(menu, 0); - button->setMenu(popupMenu_); - addButton(button); - button->Release(); - - // Windows 8 systray IME mode icon - if(IsWindows8OrGreater()) { - imeModeIcon_ = new Ime::LangBarButton(this, _GUID_LBI_INPUTMODE, ID_MODE_ICON); - if (isLightTheme()) { - imeModeIcon_->setIcon(IDI_ENG); - } else { - imeModeIcon_->setIcon(IDI_ENG_DARK); - } - addButton(imeModeIcon_); - } - - // global compartment stuff - addCompartmentMonitor(g_configChangedGuid, true); } TextService::~TextService(void) { @@ -139,32 +105,103 @@ TextService::~TextService(void) { if(messageWindow_) hideMessage(); - if(switchLangButton_) - switchLangButton_->Release(); - if(switchShapeButton_) - switchShapeButton_->Release(); - if(imeModeIcon_) - imeModeIcon_->Release(); - freeChewingContext(); -} - -CLSID TextService::clsid() { - return g_textServiceClsid; + OutputDebugStringW(L"[chewing] Unloaded\n"); } // virtual void TextService::onActivate() { - DWORD configStamp = globalCompartmentValue(g_configChangedGuid); - config().reloadIfNeeded(configStamp); + // add language bar buttons + // siwtch Chinese/English modes + TF_LANGBARITEMINFO info = { + g_textServiceClsid, + g_modeButtonGuid, + TF_LBI_STYLE_BTN_BUTTON, + 0, + {} + }; + LPCWSTR tooltip; + int len; + len = LoadStringW(g_hInstance, IDS_SWITCH_LANG, (LPWSTR)&tooltip, 0); + wcsncpy_s(info.szDescription, sizeof(info.szDescription), tooltip, len); + CreateLangBarButton( + info, + SysAllocStringLen(tooltip, len), + LoadIconW(g_hInstance, MAKEINTRESOURCEW(IDI_CHI)), + NULL, + ID_SWITCH_LANG, + this, + switchLangButton_.put_void() + ); + addButton(switchLangButton_.get()); + + // toggle full shape/half shape + len = LoadStringW(g_hInstance, IDS_SWITCH_SHAPE, (LPWSTR)&tooltip, 0); + info.guidItem = g_shapeTypeButtonGuid; + wcsncpy_s(info.szDescription, sizeof(info.szDescription), tooltip, len); + CreateLangBarButton( + info, + SysAllocStringLen(tooltip, len), + LoadIconW(g_hInstance, MAKEINTRESOURCEW(IDI_HALF_SHAPE)), + NULL, + ID_SWITCH_SHAPE, + this, + switchShapeButton_.put_void() + ); + addButton(switchShapeButton_.get()); + + // settings and others, may open a popup menu + len = LoadStringW(g_hInstance, IDS_SETTINGS, (LPWSTR)&tooltip, 0); + info.guidItem = g_settingsButtonGuid; + info.dwStyle = TF_LBI_STYLE_BTN_MENU; + wcsncpy_s(info.szDescription, sizeof(info.szDescription), tooltip, len); + HMENU menu = ::LoadMenuW(g_hInstance, MAKEINTRESOURCEW(IDR_MENU)); + popupMenu_ = ::GetSubMenu(menu, 0); + CreateLangBarButton( + info, + SysAllocStringLen(tooltip, len), + LoadIconW(g_hInstance, MAKEINTRESOURCEW(IDI_CONFIG)), + popupMenu_, + 0, + this, + settingsMenuButton_.put_void() + ); + addButton(settingsMenuButton_.get()); + + // Windows 8 systray IME mode icon + if(IsWindows8OrGreater()) { + len = LoadStringW(g_hInstance, IDS_SWITCH_SHAPE, (LPWSTR)&tooltip, 0); + info.guidItem = _GUID_LBI_INPUTMODE; + info.dwStyle = TF_LBI_STYLE_BTN_BUTTON; + wcsncpy_s(info.szDescription, sizeof(info.szDescription), tooltip, len); + CreateLangBarButton( + info, + SysAllocStringLen(tooltip, len), + LoadIconW(g_hInstance, MAKEINTRESOURCEW(isLightTheme() ? IDI_ENG : IDI_ENG_DARK)), + NULL, + ID_MODE_ICON, + this, + imeModeIcon_.put_void() + ); + addButton(imeModeIcon_.get()); + } + + config().reloadIfNeeded(); initChewingContext(); updateLangButtons(); + if(imeModeIcon_) // windows 8 IME mode icon imeModeIcon_->setEnabled(isKeyboardOpened()); } // virtual void TextService::onDeactivate() { + // Remove all buttons to avoid reference cycles + switchLangButton_ = nullptr; + switchShapeButton_ = nullptr; + settingsMenuButton_ = nullptr; + imeModeIcon_ = nullptr; + lastKeyDownCode_ = 0; freeChewingContext(); @@ -172,10 +209,6 @@ void TextService::onDeactivate() { hideCandidates(); } -// virtual -void TextService::onFocus() { -} - // virtual void TextService::onKillFocus() { if (isComposing()) { @@ -194,7 +227,24 @@ void TextService::onKillFocus() { bool TextService::filterKeyDown(Ime::KeyEvent& keyEvent) { Config& cfg = config(); if (cfg.reloadIfNeeded()) { - applyConfig(); + // check if chewing context needs to be reloaded + bool chewingNeedsReload = false; + // check if symbols.dat file is changed + // get last mtime of symbols.dat file + std::wstring file = userDir() + L"\\symbols.dat"; + struct _stat64 stbuf; + if(_wstat64(file.c_str(), &stbuf) == 0 && symbolsFileTime_ != stbuf.st_mtime) { + symbolsFileTime_ = stbuf.st_mtime; + chewingNeedsReload = true; + } + // re-create a new chewing context if needed + if(chewingNeedsReload && chewingContext_) { + freeChewingContext(); + initChewingContext(); + } + else { + applyConfig(); // apply the latest config + } } lastKeyDownCode_ = keyEvent.keyCode(); // return false if we don't need this key @@ -540,7 +590,7 @@ bool TextService::onPreservedKey(const GUID& guid) { // virtual -bool TextService::onCommand(UINT id, CommandType type) { +STDMETHODIMP TextService::onCommand(UINT id, CommandType type) { assert(chewingContext_); if(type == COMMAND_RIGHT_CLICK) { if(id == ID_MODE_ICON) { // Windows 8 IME mode icon @@ -564,7 +614,7 @@ bool TextService::onCommand(UINT id, CommandType type) { } else { // we only handle right click in Windows 8 for the IME mode icon - return false; + return S_FALSE; } } else { @@ -628,41 +678,27 @@ bool TextService::onCommand(UINT id, CommandType type) { // Need to update the old ChewingIME docs break; default: - return false; + return S_FALSE; } } - return true; + return S_OK; } -// virtual -void TextService::onCompartmentChanged(const GUID& key) { - if(::IsEqualGUID(key, g_configChangedGuid)) { - // changes of configuration are detected - DWORD stamp = globalCompartmentValue(g_configChangedGuid); - config().reloadIfNeeded(stamp); +STDMETHODIMP TextService::QueryInterface(REFIID riid, void **ppvObj) { + if (IsEqualIID(riid, IID_IRunCommand)) { + *ppvObj = (IRunCommand*)this; + AddRef(); + return S_OK; + } + return Ime::TextService::QueryInterface(riid, ppvObj); +} - // check if chewing context needs to be reloaded - bool chewingNeedsReload = false; - // check if symbols.dat file is changed - // get last mtime of symbols.dat file - std::wstring file = userDir() + L"\\symbols.dat"; - struct _stat64 stbuf; - if(_wstat64(file.c_str(), &stbuf) == 0 && symbolsFileTime_ != stbuf.st_mtime) { - symbolsFileTime_ = stbuf.st_mtime; - chewingNeedsReload = true; - } +STDMETHODIMP_(ULONG) TextService::AddRef() { + return Ime::TextService::AddRef(); +} - // re-create a new chewing context if needed - if(chewingNeedsReload && chewingContext_) { - freeChewingContext(); - initChewingContext(); - } - else { - applyConfig(); // apply the latest config - } - return; - } - Ime::TextService::onCompartmentChanged(key); +STDMETHODIMP_(ULONG) TextService::Release() { + return Ime::TextService::Release(); } // called when the keyboard is opened or closed @@ -969,26 +1005,19 @@ void TextService::updateLangButtons() { int langMode = ::chewing_get_ChiEngMode(chewingContext_); if(langMode != langMode_) { langMode_ = langMode; - if (isLightTheme()) { - switchLangButton_->setIcon(langMode == CHINESE_MODE ? IDI_CHI : IDI_ENG); - } else { - switchLangButton_->setIcon(langMode == CHINESE_MODE ? IDI_CHI_DARK : IDI_ENG_DARK); - } + UINT iconId = isLightTheme() ? langMode == CHINESE_MODE ? IDI_CHI : IDI_ENG + : langMode == CHINESE_MODE ? IDI_CHI_DARK : IDI_ENG_DARK; + switchLangButton_->setIcon(LoadIconW(g_hInstance, MAKEINTRESOURCEW(iconId))); if(imeModeIcon_) { - // FIXME: we need a better set of icons to meet the - // WIndows 8 IME guideline and UX guidelines. - if (isLightTheme()) { - imeModeIcon_->setIcon(langMode == CHINESE_MODE ? IDI_CHI : IDI_ENG); - } else { - imeModeIcon_->setIcon(langMode == CHINESE_MODE ? IDI_CHI_DARK : IDI_ENG_DARK); - } + imeModeIcon_->setIcon(LoadIconW(g_hInstance, MAKEINTRESOURCEW(iconId))); } } int shapeMode = ::chewing_get_ShapeMode(chewingContext_); if(shapeMode != shapeMode_) { shapeMode_ = shapeMode; - switchShapeButton_->setIcon(shapeMode == FULLSHAPE_MODE ? IDI_FULL_SHAPE : IDI_HALF_SHAPE); + UINT iconId = shapeMode == FULLSHAPE_MODE ? IDI_FULL_SHAPE : IDI_HALF_SHAPE; + switchShapeButton_->setIcon(LoadIconW(g_hInstance, MAKEINTRESOURCEW(iconId))); } } diff --git a/ChewingTextService/ChewingTextService.h b/chewing_tip/ChewingTextService.h similarity index 84% rename from ChewingTextService/ChewingTextService.h rename to chewing_tip/ChewingTextService.h index ee9a3ba..4c39f94 100644 --- a/ChewingTextService/ChewingTextService.h +++ b/chewing_tip/ChewingTextService.h @@ -20,31 +20,32 @@ #ifndef CHEWING_TEXT_SERVICE_H #define CHEWING_TEXT_SERVICE_H -#include -#include -#include +#include +#include +#include +#include #include -#include "ChewingConfig.h" #include +#include -#include -#include +#include "TextService.h" +#include "EditSession.h" +#include "ChewingConfig.h" #include "libime2.h" namespace Chewing { -class TextService: public Ime::TextService { +class TextService: + public IRunCommand, + public Ime::TextService { public: TextService(); virtual ~TextService(void); - virtual CLSID clsid(); - virtual void onActivate(); virtual void onDeactivate(); - virtual void onFocus(); virtual void onKillFocus(); virtual bool filterKeyDown(Ime::KeyEvent& keyEvent); @@ -55,11 +56,6 @@ class TextService: public Ime::TextService { virtual bool onPreservedKey(const GUID& guid); - virtual bool onCommand(UINT id, CommandType type); - - // called when a compartment value is changed - virtual void onCompartmentChanged(const GUID& key); - // called when the keyboard is opened or closed virtual void onKeyboardStatusChanged(bool opened); @@ -77,6 +73,15 @@ class TextService: public Ime::TextService { return config_; } +public: + // IRunCommand + STDMETHODIMP onCommand(UINT id, CommandType type); + + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + private: void initChewingContext(); // initialize chewing context void freeChewingContext(); // free chewing context @@ -123,9 +128,10 @@ class TextService: public Ime::TextService { bool showingCandidates_; UINT messageTimerId_; - Ime::LangBarButton* switchLangButton_; - Ime::LangBarButton* switchShapeButton_; - Ime::LangBarButton* imeModeIcon_; // IME mode icon, a special language button (Windows 8 only) + winrt::com_ptr switchLangButton_; + winrt::com_ptr switchShapeButton_; + winrt::com_ptr settingsMenuButton_; + winrt::com_ptr imeModeIcon_; // IME mode icon, a special language button (Windows 8 only) HMENU popupMenu_; bool outputSimpChinese_; // output simplified Chinese diff --git a/ChewingTextService/ChewingTextService.manifest b/chewing_tip/ChewingTextService.manifest similarity index 100% rename from ChewingTextService/ChewingTextService.manifest rename to chewing_tip/ChewingTextService.manifest diff --git a/ChewingTextService/ChewingTextService.rc b/chewing_tip/ChewingTextService.rc similarity index 100% rename from ChewingTextService/ChewingTextService.rc rename to chewing_tip/ChewingTextService.rc diff --git a/ChewingTextService/DllEntry.cpp b/chewing_tip/DllEntry.cpp similarity index 77% rename from ChewingTextService/DllEntry.cpp rename to chewing_tip/DllEntry.cpp index 5645101..3c5cec3 100644 --- a/ChewingTextService/DllEntry.cpp +++ b/chewing_tip/DllEntry.cpp @@ -12,8 +12,8 @@ // DLL module handle HINSTANCE g_hInstance = nullptr; -BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, - LPVOID lpReserved) { +extern "C" BOOL DllMain_cpp(HMODULE hModule, DWORD ul_reason_for_call, + LPVOID lpReserved) { OutputDebugStringW(L"DllMain called\n"); switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: @@ -26,7 +26,8 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, return TRUE; } -STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppvObj) { +extern "C" HRESULT DllGetClassObject_cpp(REFCLSID rclsid, REFIID riid, + void** ppvObj) { OutputDebugStringW(L"DllGetClassObject Called\n"); Chewing::CClassFactory* pFactory = new Chewing::CClassFactory(); @@ -35,4 +36,4 @@ STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppvObj) { pFactory->Release(); return hr; -} \ No newline at end of file +} diff --git a/libIME/EditSession.cpp b/chewing_tip/EditSession.cpp similarity index 100% rename from libIME/EditSession.cpp rename to chewing_tip/EditSession.cpp diff --git a/libIME/EditSession.h b/chewing_tip/EditSession.h similarity index 100% rename from libIME/EditSession.h rename to chewing_tip/EditSession.h diff --git a/libIME/KeyEvent.cpp b/chewing_tip/KeyEvent.cpp similarity index 100% rename from libIME/KeyEvent.cpp rename to chewing_tip/KeyEvent.cpp diff --git a/libIME/KeyEvent.h b/chewing_tip/KeyEvent.h similarity index 100% rename from libIME/KeyEvent.h rename to chewing_tip/KeyEvent.h diff --git a/libIME/TextService.cpp b/chewing_tip/TextService.cpp similarity index 67% rename from libIME/TextService.cpp rename to chewing_tip/TextService.cpp index 302f1d3..51492cb 100644 --- a/libIME/TextService.cpp +++ b/chewing_tip/TextService.cpp @@ -19,11 +19,12 @@ #include "TextService.h" #include "EditSession.h" -#include "LangBarButton.h" #include "libime2.h" #include +#include #include +#include #include #include @@ -47,16 +48,10 @@ TextService::TextService(): activateFlags_(0), isKeyboardOpened_(false), threadMgrEventSinkCookie_(TF_INVALID_COOKIE), - textEditSinkCookie_(TF_INVALID_COOKIE), - compositionSinkCookie_(TF_INVALID_COOKIE), - keyboardOpenEventSinkCookie_(TF_INVALID_COOKIE), - globalCompartmentEventSinkCookie_(TF_INVALID_COOKIE), - langBarSinkCookie_(TF_INVALID_COOKIE), - activateLanguageProfileNotifySinkCookie_(TF_INVALID_COOKIE), composition_(NULL), input_atom_(TF_INVALID_GUIDATOM), refCount_(1) { - addCompartmentMonitor(GUID_COMPARTMENT_KEYBOARD_OPENCLOSE, false); + addCompartmentMonitor(GUID_COMPARTMENT_KEYBOARD_OPENCLOSE); // FIXME we should only initialize once LibIME2Init(); @@ -79,51 +74,21 @@ TextService::~TextService(void) { if(!compartmentMonitors_.empty()) { vector::iterator it; for(it = compartmentMonitors_.begin(); it != compartmentMonitors_.end(); ++it) { - winrt::com_ptr source; - if(it->isGlobal) - source = globalCompartment(it->guid).as(); - else - source = threadCompartment(it->guid).as(); + winrt::com_ptr source = threadCompartment(it->guid).as(); if (source) { source->UnadviseSink(it->cookie); } } } - - if(langBarMgr_) { - langBarMgr_->UnadviseEventSink(langBarSinkCookie_); - } - langBarMgr_ = NULL; -} - -// public methods - -ITfThreadMgr* TextService::threadMgr() const { - return threadMgr_.get(); -} - -TfClientId TextService::clientId() const { - return clientId_; -} - - -// language bar -DWORD TextService::langBarStatus() const { - if(langBarMgr_) { - DWORD status; - if(langBarMgr_->GetShowFloatingStatus(&status) == S_OK) { - return status; - } - } - return 0; } -void TextService::addButton(LangBarButton* button) { +void TextService::addButton(ITfLangBarItemButton* button) { if(button) { - winrt::com_ptr btn; + winrt::com_ptr btn; btn.copy_from(button); + langBarButtons_.emplace_back(btn); - if(isActivated()) { + if (threadMgr_) { winrt::com_ptr langBarItemMgr; if(threadMgr_->QueryInterface(IID_ITfLangBarItemMgr, langBarItemMgr.put_void()) == S_OK) { langBarItemMgr->AddItem(button); @@ -132,23 +97,6 @@ void TextService::addButton(LangBarButton* button) { } } -void TextService::removeButton(LangBarButton* button) { - if(button) { - winrt::com_ptr btn; - btn.copy_from(button); - auto it = find(langBarButtons_.begin(), langBarButtons_.end(), btn); - if(it != langBarButtons_.end()) { - if(isActivated()) { - winrt::com_ptr langBarItemMgr; - if(threadMgr_->QueryInterface(IID_ITfLangBarItemMgr, langBarItemMgr.put_void()) == S_OK) { - langBarItemMgr->RemoveItem(button); - } - } - langBarButtons_.erase(it); - } - } -} - // preserved key void TextService::addPreservedKey(UINT keyCode, UINT modifiers, const GUID& guid) { PreservedKey preservedKey; @@ -156,7 +104,7 @@ void TextService::addPreservedKey(UINT keyCode, UINT modifiers, const GUID& guid preservedKey.uVKey = keyCode; preservedKey.uModifiers = modifiers; preservedKeys_.push_back(preservedKey); - if(threadMgr_) { // our text service is activated + if (threadMgr_) { // our text service is activated ITfKeystrokeMgr *keystrokeMgr; if (threadMgr_->QueryInterface(IID_ITfKeystrokeMgr, (void **)&keystrokeMgr) == S_OK) { keystrokeMgr->PreserveKey(clientId_, guid, &preservedKey, NULL, 0); @@ -165,25 +113,6 @@ void TextService::addPreservedKey(UINT keyCode, UINT modifiers, const GUID& guid } } -void TextService::removePreservedKey(const GUID& guid) { - vector::iterator it; - for(it = preservedKeys_.begin(); it != preservedKeys_.end(); ++it) { - PreservedKey& preservedKey = *it; - if(::IsEqualIID(preservedKey.guid, guid)) { - if(threadMgr_) { // our text service is activated - ITfKeystrokeMgr *keystrokeMgr; - if (threadMgr_->QueryInterface(IID_ITfKeystrokeMgr, (void **)&keystrokeMgr) == S_OK) { - keystrokeMgr->UnpreserveKey(preservedKey.guid, &preservedKey); - keystrokeMgr->Release(); - } - } - preservedKeys_.erase(it); - break; - } - } -} - - // text composition bool TextService::isComposing() { @@ -207,35 +136,6 @@ void TextService::setKeyboardOpen(bool open) { } } - -// check if current insertion point is in the range of composition. -// if not in range, insertion is now allowed -bool TextService::isInsertionAllowed(EditSession* session) { - TfEditCookie cookie = session->editCookie(); - TF_SELECTION selection; - ULONG selectionNum; - if(isComposing()) { - if(session->context()->GetSelection(cookie, TF_DEFAULT_SELECTION, 1, &selection, &selectionNum) == S_OK) { - ITfRange* compositionRange; - if(composition_->GetRange(&compositionRange) == S_OK) { - bool allowed = false; - // check if current selection is covered by composition range - LONG compareResult1; - LONG compareResult2; - if(selection.range->CompareStart(cookie, compositionRange, TF_ANCHOR_START, &compareResult1) == S_OK - && selection.range->CompareStart(cookie, compositionRange, TF_ANCHOR_END, &compareResult2) == S_OK) { - if(compareResult1 == -1 && compareResult2 == +1) - allowed = true; - } - compositionRange->Release(); - } - if(selection.range) - selection.range->Release(); - } - } - return false; -} - void TextService::startComposition(ITfContext* context) { assert(context); HRESULT sessionResult; @@ -252,27 +152,6 @@ void TextService::endComposition(ITfContext* context) { session->Release(); } -std::wstring TextService::compositionString(EditSession* session) { - if (composition_) { - winrt::com_ptr compositionRange; - if (composition_->GetRange(compositionRange.put()) == S_OK) { - TfEditCookie editCookie = session->editCookie(); - // FIXME: the TSF API is really stupid here and it provides no way to know the size of the range. - // we cannot even get the actual position of start and end to calculate by ourselves. - // So, just use a huge buffer and assume that it's enough. :-( - // this should be quite enough for most of the IME on the earth as most composition strings - // only contain dozens of characters. - wchar_t buf[4096]; - ULONG len = 0; - if (compositionRange->GetText(editCookie, 0, buf, 4096, &len) == S_OK) { - buf[len] = '\0'; - return std::wstring(buf); - } - } - } - return std::wstring(); -} - void TextService::setCompositionString(EditSession* session, const wchar_t* str, int len) { ITfContext* context = session->context(); if (context) { @@ -321,18 +200,6 @@ void TextService::setCompositionCursor(EditSession* session, int pos) { } // compartment handling -winrt::com_ptr TextService::globalCompartment(const GUID& key) { - if(threadMgr_) { - winrt::com_ptr compartmentMgr; - if(threadMgr_->GetGlobalCompartment(compartmentMgr.put()) == S_OK) { - winrt::com_ptr compartment; - compartmentMgr->GetCompartment(key, compartment.put()); - return compartment; - } - } - return NULL; -} - winrt::com_ptr TextService::threadCompartment(const GUID& key) { if(threadMgr_) { winrt::com_ptr compartmentMgr = threadMgr_.as(); @@ -360,23 +227,9 @@ winrt::com_ptr TextService::contextCompartment(const GUID& key, return compartment; } } - if(curContext) - curContext->Release(); return NULL; } - -DWORD TextService::globalCompartmentValue(const GUID& key) { - winrt::com_ptr compartment = globalCompartment(key); - if(compartment) { - VARIANT var; - if(compartment->GetValue(&var) == S_OK && var.vt == VT_I4) { - return (DWORD)var.lVal; - } - } - return 0; -} - DWORD TextService::threadCompartmentValue(const GUID& key) { winrt::com_ptr compartment = threadCompartment(key); if(compartment) { @@ -402,43 +255,6 @@ DWORD TextService::contextCompartmentValue(const GUID& key, ITfContext* context) return 0; } -void TextService::setGlobalCompartmentValue(const GUID& key, DWORD value) { - if(threadMgr_) { - winrt::com_ptr compartment = globalCompartment(key); - if(compartment) { - VARIANT var; - ::VariantInit(&var); - var.vt = VT_I4; - var.lVal = value; - compartment->SetValue(clientId_, &var); - } - } - else { - // if we don't have a thread manager (this is possible when we try to set - // a global compartment value while the text service is not activated) - winrt::com_ptr threadMgr; - if(::CoCreateInstance(CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, threadMgr.put_void()) == S_OK) { - if(threadMgr) { - winrt::com_ptr compartmentMgr; - if(threadMgr->GetGlobalCompartment(compartmentMgr.put()) == S_OK) { - winrt::com_ptr compartment; - if(compartmentMgr->GetCompartment(key, compartment.put()) == S_OK && compartment) { - TfClientId id; - if(threadMgr->Activate(&id) == S_OK) { - VARIANT var; - ::VariantInit(&var); - var.vt = VT_I4; - var.lVal = value; - compartment->SetValue(id, &var); - threadMgr->Deactivate(); - } - } - } - } - } - } -} - void TextService::setThreadCompartmentValue(const GUID& key, DWORD value) { winrt::com_ptr compartment = threadCompartment(key); if(compartment) { @@ -450,30 +266,13 @@ void TextService::setThreadCompartmentValue(const GUID& key, DWORD value) { } } -void TextService::setContextCompartmentValue(const GUID& key, DWORD value, ITfContext* context) { - winrt::com_ptr compartment = contextCompartment(key, context); - if(compartment) { - VARIANT var; - ::VariantInit(&var); - var.vt = VT_I4; - var.lVal = value; - compartment->SetValue(clientId_, &var); - } -} - - -void TextService::addCompartmentMonitor(const GUID key, bool isGlobal) { +void TextService::addCompartmentMonitor(const GUID key) { CompartmentMonitor monitor; monitor.guid = key; monitor.cookie = 0; - monitor.isGlobal = isGlobal; // if the text service is activated if(threadMgr_) { - winrt::com_ptr source; - if(isGlobal) - source = globalCompartment(key).as(); - else - source = threadCompartment(key).as(); + winrt::com_ptr source = threadCompartment(key).as(); if(source) { source->AdviseSink(IID_ITfCompartmentEventSink, (ITfCompartmentEventSink*)this, &monitor.cookie); } @@ -481,68 +280,6 @@ void TextService::addCompartmentMonitor(const GUID key, bool isGlobal) { compartmentMonitors_.push_back(monitor); } -void TextService::removeCompartmentMonitor(const GUID key) { - vector::iterator it; - it = find(compartmentMonitors_.begin(), compartmentMonitors_.end(), key); - if(it != compartmentMonitors_.end()) { - if(threadMgr_) { - winrt::com_ptr source; - if(it->isGlobal) - source = globalCompartment(key).as(); - else - source = threadCompartment(key).as(); - source->UnadviseSink(it->cookie); - } - compartmentMonitors_.erase(it); - } -} - - -// virtual -void TextService::onActivate() { -} - -// virtual -void TextService::onDeactivate() { -} - -// virtual -bool TextService::filterKeyDown(KeyEvent& keyEvent) { - return false; -} - -// virtual -bool TextService::onKeyDown(KeyEvent& keyEvent, EditSession* session) { - return false; -} - -// virtual -bool TextService::filterKeyUp(KeyEvent& keyEvent) { - return false; -} - -// virtual -bool TextService::onKeyUp(KeyEvent& keyEvent, EditSession* session) { - return false; -} - -// virtual -bool TextService::onPreservedKey(const GUID& guid) { - return false; -} - -// virtual -void TextService::onSetFocus() { -} - -// virtual -void TextService::onKillFocus() { -} - -bool TextService::onCommand(UINT id, CommandType type) { - return false; -} - // virtual void TextService::onCompartmentChanged(const GUID& key) { // keyboard status changed, this is threadMgr specific @@ -554,31 +291,6 @@ void TextService::onCompartmentChanged(const GUID& key) { } } -// virtual -void TextService::onLangBarStatusChanged(int newStatus) { -} - -// called when the keyboard is opened or closed -// virtual -void TextService::onKeyboardStatusChanged(bool opened) { -} - -// called just before current composition is terminated for doing cleanup. -// if forced is true, the composition is terminated by others, such as -// the input focus is grabbed by another application. -// if forced is false, the composition is terminated gracefully by endComposition(). -// virtual -void TextService::onCompositionTerminated(bool forced) { -} - -// called when a language profile is activated (only useful for text services that supports multiple language profiles) -void TextService::onLangProfileActivated(REFGUID guidProfile) { -} - -// called when a language profile is deactivated -void TextService::onLangProfileDeactivated(REFGUID guidProfile) { -} - // COM stuff // IUnknown @@ -611,10 +323,6 @@ STDMETHODIMP TextService::QueryInterface(REFIID riid, void **ppvObj) { *ppvObj = (ITfCompositionSink*)this; else if(IsEqualIID(riid, IID_ITfCompartmentEventSink)) *ppvObj = (ITfCompartmentEventSink*)this; - else if(IsEqualIID(riid, IID_ITfLangBarEventSink)) - *ppvObj = (ITfLangBarEventSink*)this; - else if(IsEqualIID(riid, IID_ITfActiveLanguageProfileNotifySink)) - *ppvObj = (ITfActiveLanguageProfileNotifySink*)this; else *ppvObj = NULL; @@ -653,11 +361,10 @@ STDMETHODIMP TextService::Activate(ITfThreadMgr *pThreadMgr, TfClientId tfClient // advice event sinks (set up event listeners) - // ITfThreadMgrEventSink, ITfActiveLanguageProfileNotifySink + // ITfThreadMgrEventSink winrt::com_ptr source = threadMgr_.as(); if(source) { source->AdviseSink(IID_ITfThreadMgrEventSink, (ITfThreadMgrEventSink *)this, &threadMgrEventSinkCookie_); - source->AdviseSink(IID_ITfActiveLanguageProfileNotifySink, (ITfActiveLanguageProfileNotifySink *)this, &activateLanguageProfileNotifySinkCookie_); } // ITfTextEditSink, @@ -683,12 +390,8 @@ STDMETHODIMP TextService::Activate(ITfThreadMgr *pThreadMgr, TfClientId tfClient if(!compartmentMonitors_.empty()) { vector::iterator it; for(it = compartmentMonitors_.begin(); it != compartmentMonitors_.end(); ++it) { - winrt::com_ptr compartmentSource; - if(it->isGlobal) // global compartment - compartmentSource = globalCompartment(it->guid).as(); - else // thread specific compartment - compartmentSource = threadCompartment(it->guid).as(); - compartmentSource->AdviseSink(IID_ITfCompartmentEventSink, (ITfCompartmentEventSink*)this, &it->cookie); + winrt::com_ptr source = threadCompartment(it->guid).as(); + source->AdviseSink(IID_ITfCompartmentEventSink, (ITfCompartmentEventSink*)this, &it->cookie); } } isKeyboardOpened_ = threadCompartmentValue(GUID_COMPARTMENT_KEYBOARD_OPENCLOSE) != 0; @@ -699,12 +402,6 @@ STDMETHODIMP TextService::Activate(ITfThreadMgr *pThreadMgr, TfClientId tfClient if(!isKeyboardOpened_) setKeyboardOpen(true); - // initialize language bar - ::CoCreateInstance(CLSID_TF_LangBarMgr, NULL, CLSCTX_INPROC_SERVER, - IID_ITfLangBarMgr, (void**)&langBarMgr_); - if(langBarMgr_) { - langBarMgr_->AdviseEventSink(this, NULL, 0, &langBarSinkCookie_); - } // Note: language bar has no effects in Win 8 immersive mode if(!langBarButtons_.empty()) { winrt::com_ptr langBarItemMgr; @@ -736,17 +433,13 @@ STDMETHODIMP TextService::Deactivate() { // uninitialize language bar if(!langBarButtons_.empty()) { winrt::com_ptr langBarItemMgr; - if(threadMgr_->QueryInterface(IID_ITfLangBarItemMgr, langBarItemMgr.put_void()) == S_OK) { - for(auto& button: langBarButtons_) { + if (threadMgr_->QueryInterface(IID_ITfLangBarItemMgr, langBarItemMgr.put_void()) == S_OK) { + for (auto& button: langBarButtons_) { langBarItemMgr->RemoveItem(button.get()); } } } - if(langBarMgr_) { - langBarMgr_->UnadviseEventSink(langBarSinkCookie_); - langBarSinkCookie_ = TF_INVALID_COOKIE; - langBarMgr_ = NULL; - } + langBarButtons_.clear(); // unadvice event sinks @@ -754,9 +447,7 @@ STDMETHODIMP TextService::Deactivate() { winrt::com_ptr source = threadMgr_.as(); if(source) { source->UnadviseSink(threadMgrEventSinkCookie_); - source->UnadviseSink(activateLanguageProfileNotifySinkCookie_); threadMgrEventSinkCookie_ = TF_INVALID_COOKIE; - activateLanguageProfileNotifySinkCookie_ = TF_INVALID_COOKIE; } // ITfTextEditSink, @@ -779,24 +470,13 @@ STDMETHODIMP TextService::Deactivate() { // ITfCompartmentEventSink // thread specific compartment - winrt::com_ptr compartment = threadCompartment(GUID_COMPARTMENT_KEYBOARD_OPENCLOSE); - if(compartment) { - winrt::com_ptr compartmentSource = compartment.as(); - if(compartmentSource) - compartmentSource->UnadviseSink(keyboardOpenEventSinkCookie_); - keyboardOpenEventSinkCookie_ = TF_INVALID_COOKIE; - } - -/* - // global compartment - compartment = globalCompartment(XXX_GUID); - if(compartment) { - ComQIPtr compartmentSource = compartment; - if(compartmentSource) - compartmentSource->UnadviseSink(globalCompartmentEventSinkCookie_); - globalCompartmentEventSinkCookie_ = TF_INVALID_COOKIE; + if(!compartmentMonitors_.empty()) { + vector::iterator it; + for(it = compartmentMonitors_.begin(); it != compartmentMonitors_.end(); ++it) { + winrt::com_ptr source = threadCompartment(it->guid).as(); + source->UnadviseSink(it->cookie); + } } -*/ threadMgr_ = NULL; clientId_ = TF_CLIENTID_NULL; @@ -990,46 +670,6 @@ STDMETHODIMP TextService::OnChange(REFGUID rguid) { return S_OK; } -// ITfLangBarEventSink -STDMETHODIMP TextService::OnSetFocus(DWORD dwThreadId) { - return E_NOTIMPL; -} - -STDMETHODIMP TextService::OnThreadTerminate(DWORD dwThreadId) { - return E_NOTIMPL; -} - -STDMETHODIMP TextService::OnThreadItemChange(DWORD dwThreadId) { - return E_NOTIMPL; -} - -STDMETHODIMP TextService::OnModalInput(DWORD dwThreadId, UINT uMsg, WPARAM wParam, LPARAM lParam) { - return E_NOTIMPL; -} - -STDMETHODIMP TextService::ShowFloating(DWORD dwFlags) { - onLangBarStatusChanged(dwFlags); - return S_OK; -} - -STDMETHODIMP TextService::GetItemFloatingRect(DWORD dwThreadId, REFGUID rguid, RECT *prc) { - return E_NOTIMPL; -} - - -// ITfActiveLanguageProfileNotifySink -STDMETHODIMP TextService::OnActivated(REFCLSID clsid, REFGUID guidProfile, BOOL fActivated) { - // we only support one text service, so clsid must be the same as that of our text service. - // otherwise it's not the notification for our text service, just ignore the event. - if(clsid == this->clsid()) { - if(fActivated) - onLangProfileActivated(guidProfile); - else - onLangProfileDeactivated(guidProfile); - } - return S_OK; -} - // edit session handling STDMETHODIMP TextService::KeyEditSession::DoEditSession(TfEditCookie ec) { EditSession::DoEditSession(ec); @@ -1132,22 +772,6 @@ ITfContext* TextService::currentContext() { return context; } -bool TextService::compositionRect(EditSession* session, RECT* rect) { - bool ret = false; - if(isComposing()) { - winrt::com_ptr view; - if(session->context()->GetActiveView(view.put()) == S_OK) { - BOOL clipped; - winrt::com_ptr range; - if(composition_->GetRange(range.put()) == S_OK) { - if(view->GetTextExt(session->editCookie(), range.get(), rect, &clipped) == S_OK) - ret = true; - } - } - } - return ret; -} - bool TextService::selectionRect(EditSession* session, RECT* rect) { bool ret = false; winrt::com_ptr view; diff --git a/libIME/TextService.h b/chewing_tip/TextService.h similarity index 67% rename from libIME/TextService.h rename to chewing_tip/TextService.h index 2fc2084..e283c8c 100644 --- a/libIME/TextService.h +++ b/chewing_tip/TextService.h @@ -20,7 +20,7 @@ #ifndef IME_TEXT_SERVICE_H #define IME_TEXT_SERVICE_H -#include "libIME.h" +#include #include #include "EditSession.h" #include "KeyEvent.h" @@ -50,63 +50,24 @@ class TextService: public ITfTextEditSink, public ITfKeyEventSink, public ITfCompositionSink, - public ITfCompartmentEventSink, - public ITfLangBarEventSink, - public ITfActiveLanguageProfileNotifySink { + public ITfCompartmentEventSink { public: - enum CommandType { // used in onCommand() - COMMAND_LEFT_CLICK, - COMMAND_RIGHT_CLICK, - COMMAND_MENU - }; - TextService(); - // public methods - ITfThreadMgr* threadMgr() const; - - TfClientId clientId() const; - ITfContext* currentContext(); - bool isActivated() const { - return (threadMgr() != NULL); - } - - DWORD activateFlags() const { - return activateFlags_; - } - // running in Windows 8 app mode bool isImmersive() const { return (activateFlags_ & TF_TMF_IMMERSIVEMODE) != 0; } - // alias of isImmersive() - bool isMetroApp() const { - return isImmersive(); - } - - // UI less mode is enabled (for ex: in fullscreen games) - bool isUiLess() const { - return (activateFlags_ & TF_TMF_UIELEMENTENABLEDONLY) != 0; - } - - // is in console mode - bool isConsole() const { - return (activateFlags_ & TF_TMF_CONSOLE) != 0; - } - - DWORD langBarStatus() const; - // language bar buttons - void addButton(LangBarButton* button); - void removeButton(LangBarButton* button); + void addButton(ITfLangBarItemButton* button); + void removeButton(ITfLangBarItemButton* button); // preserved keys void addPreservedKey(UINT keyCode, UINT modifiers, const GUID& guid); - void removePreservedKey(const GUID& guid); // text composition handling bool isComposing(); @@ -118,79 +79,55 @@ class TextService: bool isKeyboardOpened(); void setKeyboardOpen(bool open); - bool isInsertionAllowed(EditSession* session); void startComposition(ITfContext* context); void endComposition(ITfContext* context); - bool compositionRect(EditSession* session, RECT* rect); bool selectionRect(EditSession* session, RECT* rect); HWND compositionWindow(EditSession* session); - std::wstring compositionString(EditSession* session); void setCompositionString(EditSession* session, const wchar_t* str, int len); void setCompositionCursor(EditSession* session, int pos); // compartment handling - // XXX if registry monitor works well we can stop using compartments for RPC - winrt::com_ptr globalCompartment(const GUID& key); winrt::com_ptr threadCompartment(const GUID& key); winrt::com_ptr contextCompartment(const GUID& key, ITfContext* context = NULL); - DWORD globalCompartmentValue(const GUID& key); DWORD threadCompartmentValue(const GUID& key); DWORD contextCompartmentValue(const GUID& key, ITfContext* context = NULL); - void setGlobalCompartmentValue(const GUID& key, DWORD value); void setThreadCompartmentValue(const GUID& key, DWORD value); - void setContextCompartmentValue(const GUID& key, DWORD value, ITfContext* context = NULL); // manage sinks to global or thread compartment (context specific compartment is not used) - void addCompartmentMonitor(const GUID key, bool isGlobal = false); - void removeCompartmentMonitor(const GUID key); + void addCompartmentMonitor(const GUID key); // virtual functions that IME implementors may need to override - virtual CLSID clsid() = 0; + virtual void onActivate() {} + virtual void onDeactivate() {} - virtual void onActivate(); - virtual void onDeactivate(); + virtual void onSetFocus() {} + virtual void onKillFocus() {} - virtual void onSetFocus(); - virtual void onKillFocus(); + virtual bool filterKeyDown(KeyEvent& keyEvent) { return false; } + virtual bool onKeyDown(KeyEvent& keyEvent, EditSession* session) { return false; } - virtual bool filterKeyDown(KeyEvent& keyEvent); - virtual bool onKeyDown(KeyEvent& keyEvent, EditSession* session); + virtual bool filterKeyUp(KeyEvent& keyEvent) { return false; } + virtual bool onKeyUp(KeyEvent& keyEvent, EditSession* session) { return false; } - virtual bool filterKeyUp(KeyEvent& keyEvent); - virtual bool onKeyUp(KeyEvent& keyEvent, EditSession* session); - - virtual bool onPreservedKey(const GUID& guid); - - // called when a language button or menu item is clicked - virtual bool onCommand(UINT id, CommandType type); + virtual bool onPreservedKey(const GUID& guid) { return false; } // called when a value in the global or thread compartment changed. virtual void onCompartmentChanged(const GUID& key); - virtual void onLangBarStatusChanged(int newStatus); - // called when the keyboard is opened or closed - virtual void onKeyboardStatusChanged(bool opened); + virtual void onKeyboardStatusChanged(bool opened) {} // called just before current composition is terminated for doing cleanup. // if forced is true, the composition is terminated by others, such as // the input focus is grabbed by another application. // if forced is false, the composition is terminated gracefully by endComposition(). - virtual void onCompositionTerminated(bool forced); - - // called when a language profile is activated (only useful for text services that supports multiple language profiles) - virtual void onLangProfileActivated(REFGUID guidProfile); - - // called when a language profile is deactivated - virtual void onLangProfileDeactivated(REFGUID guidProfile); + virtual void onCompositionTerminated(bool forced) {} // COM related stuff public: - friend class DisplayAttributeInfoEnum; - // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef(void); @@ -227,17 +164,6 @@ class TextService: // ITfCompartmentEventSink STDMETHODIMP OnChange(REFGUID rguid); - // ITfLangBarEventSink - STDMETHODIMP OnSetFocus(DWORD dwThreadId); - STDMETHODIMP OnThreadTerminate(DWORD dwThreadId); - STDMETHODIMP OnThreadItemChange(DWORD dwThreadId); - STDMETHODIMP OnModalInput(DWORD dwThreadId, UINT uMsg, WPARAM wParam, LPARAM lParam); - STDMETHODIMP ShowFloating(DWORD dwFlags); - STDMETHODIMP GetItemFloatingRect(DWORD dwThreadId, REFGUID rguid, RECT *prc); - - // ITfActiveLanguageProfileNotifySink - STDMETHODIMP OnActivated(REFCLSID clsid, REFGUID guidProfile, BOOL fActivated); - protected: // edit session classes, used with TSF class KeyEditSession: public EditSession { @@ -280,7 +206,6 @@ class TextService: struct CompartmentMonitor { GUID guid; DWORD cookie; - bool isGlobal; bool operator == (const GUID& other) const { return bool(::IsEqualGUID(guid, other)); @@ -300,16 +225,9 @@ class TextService: // event sink cookies DWORD threadMgrEventSinkCookie_; - DWORD textEditSinkCookie_; - DWORD compositionSinkCookie_; - DWORD keyboardOpenEventSinkCookie_; - DWORD globalCompartmentEventSinkCookie_; - DWORD langBarSinkCookie_; - DWORD activateLanguageProfileNotifySinkCookie_; ITfComposition* composition_; // acquired when starting composition, released when ending composition - winrt::com_ptr langBarMgr_; - std::vector> langBarButtons_; + std::vector> langBarButtons_; std::vector preservedKeys_; std::vector compartmentMonitors_; diff --git a/libIME/Utils.cpp b/chewing_tip/Utils.cpp similarity index 80% rename from libIME/Utils.cpp rename to chewing_tip/Utils.cpp index dc7883d..30e92e7 100644 --- a/libIME/Utils.cpp +++ b/chewing_tip/Utils.cpp @@ -32,17 +32,6 @@ std::wstring utf8ToUtf16(const char* text) { return wtext; } -std::string utf16ToUtf8(const wchar_t* wtext) { - std::string text; - int len = ::WideCharToMultiByte(CP_UTF8, 0, wtext, -1, NULL, 0, NULL, NULL); - if(len > 1) { // length includes terminating null - text.resize(len - 1); - // well, this is a little bit dirty, but it works! - len = ::WideCharToMultiByte(CP_UTF8, 0, wtext, -1, &text[0], len, NULL, NULL); - } - return text; -} - std::wstring tradToSimpChinese(const std::wstring& trad) { int len = ::LCMapStringW(0x0404, LCMAP_SIMPLIFIED_CHINESE, trad.c_str(), trad.length(), NULL, 0); std::wstring simp; diff --git a/libIME/Utils.h b/chewing_tip/Utils.h similarity index 95% rename from libIME/Utils.h rename to chewing_tip/Utils.h index 5171d3e..3dee254 100644 --- a/libIME/Utils.h +++ b/chewing_tip/Utils.h @@ -25,8 +25,6 @@ std::wstring utf8ToUtf16(const char* text); -std::string utf16ToUtf8(const wchar_t* wtext); - // convert traditional Chinese to simplified Chinese std::wstring tradToSimpChinese(const std::wstring& trad); diff --git a/chewing_tip/build.rs b/chewing_tip/build.rs new file mode 100644 index 0000000..b084f18 --- /dev/null +++ b/chewing_tip/build.rs @@ -0,0 +1,60 @@ +use std::{env, path::PathBuf, process::Command}; + +fn main() -> anyhow::Result<()> { + let out_dir = env::var("OUT_DIR").unwrap(); + let target = env::var("TARGET").unwrap(); + let midl = embed_resource::find_windows_sdk_tool("midl.exe").expect("cannot find midl.exe"); + let cl = cc::windows_registry::find_tool(&target, "cl.exe").expect("cannot find cl.exe"); + + let _ = Command::new(midl) + .envs(cl.env().iter().cloned()) + .arg("/client") + .arg("none") + .arg("/server") + .arg("none") + .arg("/out") + .arg(&out_dir) + .arg("idl/libime2.idl") + .status()?; + let idl_client = PathBuf::from(&out_dir).join("libime2_i.c"); + + embed_resource::compile("ChewingTextService.rc", embed_resource::NONE).manifest_required()?; + cc::Build::new() + .cpp(true) + .std("c++20") + .define("_UNICODE", "1") + .define("UNICODE", "1") + .define("_CRT_SECURE_NO_WARNINGS", "1") + .flag("/utf-8") + .flag("/EHsc") + .file("CClassFactory.cpp") + .file("ChewingConfig.cpp") + .file("ChewingTextService.cpp") + .file("DllEntry.cpp") + .file("EditSession.cpp") + .file("KeyEvent.cpp") + .file("TextService.cpp") + .file("Utils.cpp") + .file(idl_client) + .include("../libchewing/include") + .include(&out_dir) + .compile("chewing_tip"); + + println!("cargo::rerun-if-changed=idl/libime2.idl"); + println!("cargo::rerun-if-changed=CClassFactory.cpp"); + println!("cargo::rerun-if-changed=CClassFactory.h"); + println!("cargo::rerun-if-changed=ChewingConfig.cpp"); + println!("cargo::rerun-if-changed=ChewingConfig.h"); + println!("cargo::rerun-if-changed=ChewingTextService.cpp"); + println!("cargo::rerun-if-changed=ChewingTextService.h"); + println!("cargo::rerun-if-changed=DllEntry.cpp"); + println!("cargo::rerun-if-changed=EditSession.cpp"); + println!("cargo::rerun-if-changed=EditSession.h"); + println!("cargo::rerun-if-changed=KeyEvent.cpp"); + println!("cargo::rerun-if-changed=KeyEvent.h"); + println!("cargo::rerun-if-changed=TextService.cpp"); + println!("cargo::rerun-if-changed=TextService.h"); + println!("cargo::rerun-if-changed=Utils.cpp"); + println!("cargo::rerun-if-changed=Utils.h"); + Ok(()) +} diff --git a/ChewingTextService/chi.ico b/chewing_tip/chi.ico similarity index 100% rename from ChewingTextService/chi.ico rename to chewing_tip/chi.ico diff --git a/ChewingTextService/chi_dark.ico b/chewing_tip/chi_dark.ico similarity index 100% rename from ChewingTextService/chi_dark.ico rename to chewing_tip/chi_dark.ico diff --git a/ChewingTextService/config.ico b/chewing_tip/config.ico similarity index 100% rename from ChewingTextService/config.ico rename to chewing_tip/config.ico diff --git a/ChewingTextService/eng.ico b/chewing_tip/eng.ico similarity index 100% rename from ChewingTextService/eng.ico rename to chewing_tip/eng.ico diff --git a/ChewingTextService/eng_dark.ico b/chewing_tip/eng_dark.ico similarity index 100% rename from ChewingTextService/eng_dark.ico rename to chewing_tip/eng_dark.ico diff --git a/ChewingTextService/full.ico b/chewing_tip/full.ico similarity index 100% rename from ChewingTextService/full.ico rename to chewing_tip/full.ico diff --git a/ChewingTextService/half.ico b/chewing_tip/half.ico similarity index 100% rename from ChewingTextService/half.ico rename to chewing_tip/half.ico diff --git a/libIME/idl/libime2.idl b/chewing_tip/idl/libime2.idl similarity index 72% rename from libIME/idl/libime2.idl rename to chewing_tip/idl/libime2.idl index b5fc0e1..584f185 100644 --- a/libIME/idl/libime2.idl +++ b/chewing_tip/idl/libime2.idl @@ -63,6 +63,33 @@ interface IMessageWindow : IWindow void setText(LPCWSTR text); } +enum CommandType { + COMMAND_LEFT_CLICK, + COMMAND_RIGHT_CLICK, + COMMAND_MENU, +}; + +[ +object, +uuid(f320f835-b95d-4d3f-89d5-fd4ab7b9d7bb), +local +] +interface IRunCommand : IUnknown +{ + HRESULT onCommand(UINT id, enum CommandType cmdType); +} + +[ +object, +uuid(4db963b1-ced3-42b7-8f87-937534740e7a), +local +] +interface ILangBarButton : ITfLangBarItemButton +{ + HRESULT setIcon(HICON icon); + HRESULT setEnabled(boolean enabled); +} + [local] void LibIME2Init(); [local] void CreateImeWindow([out] void **window); [local] void CreateMessageWindow(HWND parent, [in] LPCWSTR image_path, [out] void **messagewindow); @@ -70,4 +97,13 @@ interface IMessageWindow : IWindow [local] IWindow *ImeWindowFromHwnd(HWND hwnd); [local] boolean ImeWindowRegisterClass(HINSTANCE hinstance); [local] void CreateDisplayAttributeProvider([out] void **provider); -[local] HRESULT RegisterDisplayAttribute([in] const GUID *guid, [in] TF_DISPLAYATTRIBUTE da, [out] UINT32 *atom); \ No newline at end of file +[local] HRESULT RegisterDisplayAttribute([in] const GUID *guid, [in] TF_DISPLAYATTRIBUTE da, [out] UINT32 *atom); +[local] void CreateLangBarButton( + TF_LANGBARITEMINFO info, + [in] BSTR tooltip, + [in] HICON icon, + [in] HMENU menu, + DWORD commandId, + [in] IRunCommand *pRunCommand, + [out] void **ppLangBarButton +); diff --git a/ChewingTextService/im.chewing.Chewing.bmp b/chewing_tip/im.chewing.Chewing.bmp similarity index 100% rename from ChewingTextService/im.chewing.Chewing.bmp rename to chewing_tip/im.chewing.Chewing.bmp diff --git a/ChewingTextService/im.chewing.Chewing.ico b/chewing_tip/im.chewing.Chewing.ico similarity index 100% rename from ChewingTextService/im.chewing.Chewing.ico rename to chewing_tip/im.chewing.Chewing.ico diff --git a/ChewingTextService/logo.bmp b/chewing_tip/logo.bmp similarity index 100% rename from ChewingTextService/logo.bmp rename to chewing_tip/logo.bmp diff --git a/ChewingTextService/mainicon.ico b/chewing_tip/mainicon.ico similarity index 100% rename from ChewingTextService/mainicon.ico rename to chewing_tip/mainicon.ico diff --git a/ChewingTextService/mainicon2.ico b/chewing_tip/mainicon2.ico similarity index 100% rename from ChewingTextService/mainicon2.ico rename to chewing_tip/mainicon2.ico diff --git a/ChewingTextService/resource.h b/chewing_tip/resource.h similarity index 100% rename from ChewingTextService/resource.h rename to chewing_tip/resource.h diff --git a/libIME/src/display_attribute.rs b/chewing_tip/src/display_attribute.rs similarity index 100% rename from libIME/src/display_attribute.rs rename to chewing_tip/src/display_attribute.rs diff --git a/libIME/src/gfx.rs b/chewing_tip/src/gfx.rs similarity index 100% rename from libIME/src/gfx.rs rename to chewing_tip/src/gfx.rs diff --git a/chewing_tip/src/lang_bar.rs b/chewing_tip/src/lang_bar.rs new file mode 100644 index 0000000..cb2b7e2 --- /dev/null +++ b/chewing_tip/src/lang_bar.rs @@ -0,0 +1,262 @@ +use std::{cell::Cell, collections::BTreeMap, ffi::c_void, sync::RwLock}; + +use windows::Win32::{ + Foundation::{BOOL, E_FAIL, E_INVALIDARG, POINT, RECT}, + UI::{ + TextServices::{ + ITfLangBarItem, ITfLangBarItemButton, ITfLangBarItemButton_Impl, + ITfLangBarItemButton_Vtbl, ITfLangBarItemSink, ITfLangBarItem_Impl, ITfMenu, ITfSource, + ITfSource_Impl, TfLBIClick, TF_LANGBARITEMINFO, TF_LBI_CLK_RIGHT, TF_LBI_ICON, + TF_LBI_STATUS, TF_LBI_STATUS_DISABLED, TF_LBI_STATUS_HIDDEN, TF_LBMENUF_CHECKED, + TF_LBMENUF_GRAYED, TF_LBMENUF_SEPARATOR, TF_LBMENUF_SUBMENU, + }, + WindowsAndMessaging::{ + CopyIcon, DestroyIcon, GetMenuItemCount, GetMenuItemInfoW, HICON, HMENU, MENUITEMINFOW, + MENU_ITEM_STATE, MFS_CHECKED, MFS_DISABLED, MFS_GRAYED, MFT_SEPARATOR, MFT_STRING, + MIIM_FTYPE, MIIM_ID, MIIM_STATE, MIIM_STRING, MIIM_SUBMENU, + }, + }, +}; +use windows_core::{ + implement, interface, ComObjectInner, IUnknown, IUnknown_Vtbl, Interface, Result, BSTR, GUID, + PWSTR, +}; + +#[repr(C)] +enum CommandType { + LeftClick, + RightClick, + Menu, +} + +#[interface("f320f835-b95d-4d3f-89d5-fd4ab7b9d7bb")] +pub(crate) unsafe trait IRunCommand: IUnknown { + fn on_command(&self, id: u32, cmd_type: CommandType); +} + +#[interface("4db963b1-ced3-42b7-8f87-937534740e7a")] +pub(crate) unsafe trait ILangBarButton: ITfLangBarItemButton { + fn set_icon(&self, icon: HICON) -> Result<()>; + fn set_enabled(&self, enabled: bool) -> Result<()>; +} + +#[implement(ITfLangBarItem, ITfLangBarItemButton, ITfSource, ILangBarButton)] +struct LangBarButton { + info: TF_LANGBARITEMINFO, + status: Cell, + tooltip: BSTR, + icon: Cell, + /// borrowed - we don't own this menu + menu: HMENU, + command_id: u32, + run_command: IRunCommand, + sinks: RwLock>, +} + +impl Drop for LangBarButton { + fn drop(&mut self) { + if !self.icon.get().is_invalid() { + let _ = unsafe { DestroyIcon(self.icon.get()) }; + } + } +} + +#[no_mangle] +unsafe extern "C" fn CreateLangBarButton( + info: TF_LANGBARITEMINFO, + tooltip: BSTR, + icon: HICON, + menu: HMENU, + command_id: u32, + run_command: *mut IRunCommand, + ret: *mut *mut c_void, +) { + let binding = run_command.cast(); + let run_command_ref = + IRunCommand::from_raw_borrowed(&binding).expect("invalid IRunCommand pointer"); + let run_command: IRunCommand = run_command_ref.cast().expect("invalid IRunCommand pointer"); + let lang_bar_btn = LangBarButton { + info, + status: Cell::new(0), + tooltip, + icon: Cell::new(icon), + menu, + command_id, + run_command, + sinks: RwLock::new(BTreeMap::new()), + } + .into_object(); + ret.write(lang_bar_btn.into_interface::().into_raw()); +} + +impl ILangBarButton_Impl for LangBarButton_Impl { + unsafe fn set_icon(&self, icon: HICON) -> Result<()> { + if !self.icon.get().is_invalid() { + DestroyIcon(self.icon.get())?; + } + self.icon.set(icon); + self.update_sinks(TF_LBI_ICON)?; + Ok(()) + } + + unsafe fn set_enabled(&self, enabled: bool) -> Result<()> { + if enabled { + self.status.set(self.status.get() & !TF_LBI_STATUS_DISABLED); + } else { + self.status.set(self.status.get() | TF_LBI_STATUS_DISABLED); + } + self.update_sinks(TF_LBI_STATUS)?; + Ok(()) + } +} + +impl ITfLangBarItem_Impl for LangBarButton_Impl { + fn GetInfo(&self, pinfo: *mut TF_LANGBARITEMINFO) -> Result<()> { + if pinfo.is_null() { + return Err(E_INVALIDARG.into()); + } + unsafe { + pinfo.write(self.info); + } + Ok(()) + } + + fn GetStatus(&self) -> Result { + Ok(self.status.get()) + } + + fn Show(&self, fshow: BOOL) -> Result<()> { + if fshow.as_bool() { + self.status.set(self.status.get() & !TF_LBI_STATUS_HIDDEN); + } else { + self.status.set(self.status.get() | TF_LBI_STATUS_HIDDEN); + } + self.update_sinks(TF_LBI_STATUS)?; + Ok(()) + } + + fn GetTooltipString(&self) -> Result { + Ok(self.tooltip.clone()) + } +} + +impl ITfLangBarItemButton_Impl for LangBarButton_Impl { + fn OnClick(&self, click: TfLBIClick, _pt: &POINT, _prcareaa: *const RECT) -> Result<()> { + let cmd_type = if click == TF_LBI_CLK_RIGHT { + CommandType::RightClick + } else { + CommandType::LeftClick + }; + unsafe { self.run_command.on_command(self.command_id, cmd_type) }; + Ok(()) + } + + fn InitMenu(&self, pmenu: Option<&ITfMenu>) -> Result<()> { + if self.menu.is_invalid() { + return Err(E_FAIL.into()); + } + if let Some(menu) = pmenu { + build_menu(menu, self.menu) + } else { + Err(E_FAIL.into()) + } + } + + fn OnMenuSelect(&self, wid: u32) -> Result<()> { + unsafe { self.run_command.on_command(wid, CommandType::Menu) }; + Ok(()) + } + + fn GetIcon(&self) -> Result { + unsafe { CopyIcon(self.icon.get()) } + } + + fn GetText(&self) -> Result { + BSTR::from_wide(&self.info.szDescription) + } +} + +impl ITfSource_Impl for LangBarButton_Impl { + fn AdviseSink(&self, riid: *const GUID, punk: Option<&IUnknown>) -> Result { + if riid.is_null() || punk.is_none() { + return Err(E_INVALIDARG.into()); + } + if unsafe { *riid == ITfLangBarItemSink::IID } { + let mut cookie = [0; 4]; + if let Err(_) = getrandom::getrandom(&mut cookie) { + return Err(E_FAIL.into()); + } + let cookie = u32::from_ne_bytes(cookie); + let sink: ITfLangBarItemSink = punk.unwrap().cast()?; + if let Ok(mut sinks) = self.sinks.write() { + sinks.insert(cookie, sink); + return Ok(cookie); + } + } + Err(E_FAIL.into()) + } + + fn UnadviseSink(&self, dwcookie: u32) -> Result<()> { + if let Ok(mut sinks) = self.sinks.write() { + sinks.remove(&dwcookie); + return Ok(()); + } + Err(E_FAIL.into()) + } +} + +impl LangBarButton { + fn update_sinks(&self, dwflags: u32) -> Result<()> { + if let Ok(sinks) = self.sinks.read() { + for sink in sinks.values() { + unsafe { sink.OnUpdate(dwflags)? }; + } + } + Ok(()) + } +} + +fn build_menu(menu: &ITfMenu, hmenu: HMENU) -> Result<()> { + for i in 0..unsafe { GetMenuItemCount(hmenu) } { + let mut text_buffer = [0_u16; 256]; + let mut mi = MENUITEMINFOW { + cbSize: size_of::() as u32, + fMask: MIIM_FTYPE | MIIM_ID | MIIM_STATE | MIIM_STRING | MIIM_SUBMENU, + dwTypeData: PWSTR::from_raw(text_buffer.as_mut_ptr()), + cch: 255, + ..Default::default() + }; + if let Ok(()) = unsafe { GetMenuItemInfoW(hmenu, i as u32, true, &mut mi) } { + let mut flags = 0; + let mut sub_menu = None; + if !mi.hSubMenu.is_invalid() { + flags |= TF_LBMENUF_SUBMENU; + } + if mi.fType == MFT_SEPARATOR { + flags |= TF_LBMENUF_SEPARATOR; + } else if mi.fType != MFT_STRING { + // other types of menu are not supported + continue; + } + if mi.fState.contains(MFS_CHECKED) { + flags |= TF_LBMENUF_CHECKED; + } + // Despite the document says MFS_GRAYED and MFS_DISABLED has the same value 0x3 + // Inactive menu item actually has value 0x2, same as MF_DISABLED + if mi.fState.contains(MFS_GRAYED) + || mi.fState.contains(MFS_DISABLED) + || mi.fState.contains(MENU_ITEM_STATE(0x2)) + { + flags |= TF_LBMENUF_GRAYED; + } + if let Ok(()) = + unsafe { menu.AddMenuItem(mi.wID, flags, None, None, &text_buffer, &mut sub_menu) } + { + if let Some(sub_menu) = sub_menu { + build_menu(&sub_menu, mi.hSubMenu)?; + } + } + } + } + Ok(()) +} diff --git a/chewing_tip/src/lib.rs b/chewing_tip/src/lib.rs new file mode 100644 index 0000000..b6064cc --- /dev/null +++ b/chewing_tip/src/lib.rs @@ -0,0 +1,47 @@ +use std::ffi::{c_int, c_long, c_void}; + +mod display_attribute; +mod gfx; +mod lang_bar; +mod window; + +// Force linking chewing_capi +#[allow(unused_imports)] +use chewing_capi::setup::chewing_new; + +#[no_mangle] +unsafe extern "C" fn LibIME2Init() { + win_dbg_logger::rust_win_dbg_logger_init_info(); + log::debug!("libIME2 initialized"); +} + +extern "C" { + fn DllMain_cpp( + hmodule: *const c_void, + ul_reason_for_call: u32, + reserved: *const c_void, + ) -> c_int; + fn DllGetClassObject_cpp( + rclsid: *const c_void, + riid: *const c_void, + ppv_obj: *mut *mut c_void, + ) -> c_long; +} + +#[no_mangle] +pub unsafe extern "stdcall" fn DllMain( + hmodule: *const c_void, + ul_reason_for_call: u32, + reserved: *const c_void, +) -> c_int { + DllMain_cpp(hmodule, ul_reason_for_call, reserved) +} + +#[no_mangle] +pub unsafe extern "stdcall" fn DllGetClassObject( + rclsid: *const c_void, + riid: *const c_void, + ppv_obj: *mut *mut c_void, +) -> c_long { + DllGetClassObject_cpp(rclsid, riid, ppv_obj) +} diff --git a/libIME/src/window/candidate_window.rs b/chewing_tip/src/window/candidate_window.rs similarity index 100% rename from libIME/src/window/candidate_window.rs rename to chewing_tip/src/window/candidate_window.rs diff --git a/libIME/src/window/message_window.rs b/chewing_tip/src/window/message_window.rs similarity index 100% rename from libIME/src/window/message_window.rs rename to chewing_tip/src/window/message_window.rs diff --git a/libIME/src/window/mod.rs b/chewing_tip/src/window/mod.rs similarity index 100% rename from libIME/src/window/mod.rs rename to chewing_tip/src/window/mod.rs diff --git a/installer/windows-chewing-tsf.wxs b/installer/windows-chewing-tsf.wxs index 1241386..5da5c98 100644 --- a/installer/windows-chewing-tsf.wxs +++ b/installer/windows-chewing-tsf.wxs @@ -25,9 +25,8 @@ - - + @@ -42,9 +41,8 @@ - - + diff --git a/libIME/CMakeLists.txt b/libIME/CMakeLists.txt deleted file mode 100644 index 087878b..0000000 --- a/libIME/CMakeLists.txt +++ /dev/null @@ -1,63 +0,0 @@ -find_package(Corrosion QUIET) -if(NOT Corrosion_FOUND) - FetchContent_Declare( - Corrosion - GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git - GIT_TAG 64289b1d79d6d19cd2e241db515381a086bb8407 # v0.5 - FIND_PACKAGE_ARGS - ) - FetchContent_MakeAvailable(Corrosion) -endif() - -corrosion_import_crate(MANIFEST_PATH Cargo.toml CRATES libime2 CRATE_TYPES staticlib) -corrosion_add_target_rustflags(libime2 "-C target-feature=+crt-static") - -if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_SIMULATE_ID MATCHES "MSVC") - corrosion_set_env_vars(libime2 "CFLAGS=-EHsc" "CXXFLAGS=-EHsc") -endif() - -find_program(MIDL midl) -add_custom_command( - OUTPUT - dlldata.c - libime2.h - libime2_i.c - libime2_p.c - COMMAND MIDL ${CMAKE_CURRENT_SOURCE_DIR}/idl/libime2.idl - MAIN_DEPENDENCY idl/libime2.idl -) - -add_library(libIME_static STATIC - # Core TSF part - libIME.cpp - libIME.h - TextService.cpp - TextService.h - KeyEvent.cpp - KeyEvent.h - EditSession.cpp - EditSession.h - LangBarButton.cpp - LangBarButton.h - Utils.cpp - Utils.h - # Rust interop - dlldata.c - libime2.h - libime2_i.c - libime2_p.c -) - -target_include_directories(libIME_static PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) - -target_link_libraries(libIME_static - PUBLIC libime2 - PUBLIC shlwapi.lib - PUBLIC d2d1.lib - PUBLIC d3d11.lib - PUBLIC dwrite.lib - PUBLIC dcomp.lib - - PUBLIC Propsys.lib - PUBLIC RuntimeObject.lib -) \ No newline at end of file diff --git a/libIME/LICENSE.txt b/libIME/LICENSE.txt deleted file mode 100644 index f166cc5..0000000 --- a/libIME/LICENSE.txt +++ /dev/null @@ -1,502 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! \ No newline at end of file diff --git a/libIME/LangBarButton.cpp b/libIME/LangBarButton.cpp deleted file mode 100644 index 4353f0c..0000000 --- a/libIME/LangBarButton.cpp +++ /dev/null @@ -1,369 +0,0 @@ -// -// Copyright (C) 2013 Hong Jen Yee (PCMan) -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Library General Public -// License as published by the Free Software Foundation; either -// version 2 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Library General Public License for more details. -// -// You should have received a copy of the GNU Library General Public -// License along with this library; if not, write to the -// Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -// Boston, MA 02110-1301, USA. -// - -#include "LangBarButton.h" -#include "TextService.h" -#include -#include -#include -#include - -extern HINSTANCE g_hInstance; - -namespace Ime { - -LangBarButton::LangBarButton(TextService* service, const GUID& guid, UINT commandId, const wchar_t* text, DWORD style): - textService_(service), - tooltip_(), - commandId_(commandId), - menu_(NULL), - icon_(NULL), - status_(0), - refCount_(1) { - - assert(service); - - textService_->AddRef(); - info_.clsidService = service->clsid(); - info_.guidItem = guid; - info_.dwStyle = style; - info_.ulSort = 0; - setText(text); -} - -LangBarButton::~LangBarButton(void) { - if(textService_) - textService_->Release(); - if(menu_) - ::DestroyMenu(menu_); -} - -const wchar_t* LangBarButton::text() const { - return info_.szDescription; -} - -void LangBarButton::setText(const wchar_t* text) { - if (text && text[0] != '\0') { - wcsncpy(info_.szDescription, text, TF_LBI_DESC_MAXLEN - 1); - info_.szDescription[TF_LBI_DESC_MAXLEN - 1] = 0; - } - else { - // NOTE: The language button text should NOT be empty. - // Otherwise, when the button status or icon is changed after its creation, - // the button will disappear temporarily in Windows 10 for unknown reason. - // This can be considered a bug of Windows 10 and there does not seem to be a way to fix it. - // So we need to avoid empty button text otherwise the language button won't work properly. - // Here we use a space character to make the text non-empty to workaround the problem. - wcscpy(info_.szDescription, L" "); - } - update(TF_LBI_TEXT); -} - -void LangBarButton::setText(UINT stringId) { - const wchar_t* str; - int len = ::LoadStringW(g_hInstance, stringId, (LPTSTR)&str, 0); - if(str) { - if(len > (TF_LBI_DESC_MAXLEN - 1)) - len = TF_LBI_DESC_MAXLEN - 1; - wcsncpy(info_.szDescription, str, len); - info_.szDescription[len] = 0; - update(TF_LBI_TEXT); - } -} - -// public methods -const wchar_t* LangBarButton::tooltip() const { - return tooltip_.c_str(); -} - -void LangBarButton::setTooltip(const wchar_t* tooltip) { - tooltip_ = tooltip; - update(TF_LBI_TOOLTIP); -} - -void LangBarButton::setTooltip(UINT tooltipId) { - const wchar_t* str; - // If this parameter is 0, then lpBuffer receives a read-only pointer to the resource itself. - auto len = ::LoadStringW(g_hInstance, tooltipId, (LPTSTR)&str, 0); - if(str) { - tooltip_ = std::wstring(str, len); - update(TF_LBI_TOOLTIP); - } -} - -HICON LangBarButton::icon() const { - return icon_; -} - -// The language button does not take owner ship of the icon -// That means, when the button is destroyed, it will not destroy -// the icon automatically. -void LangBarButton::setIcon(HICON icon) { - icon_ = icon; - update(TF_LBI_ICON); -} - -void LangBarButton::setIcon(UINT iconId) { - HICON icon = ::LoadIconW(g_hInstance, (LPCTSTR)iconId); - if(icon) - setIcon(icon); -} - -UINT LangBarButton::commandId() const { - return commandId_; -} - -void LangBarButton::setCommandId(UINT id) { - commandId_ = id; -} - -HMENU LangBarButton::menu() const { - return menu_; -} - -void LangBarButton::setMenu(HMENU menu) { - if(menu_) { - ::DestroyMenu(menu_); - } - menu_ = menu; - // FIXME: how to handle toggle buttons? - if(menu) - info_.dwStyle = TF_LBI_STYLE_BTN_MENU; - else - info_.dwStyle = TF_LBI_STYLE_BTN_BUTTON; -} - -bool LangBarButton::enabled() const { - return !(status_ & TF_LBI_STATUS_DISABLED); -} - -void LangBarButton::setEnabled(bool enable) { - if(enabled() != enable) { - if(enable) - status_ &= ~TF_LBI_STATUS_DISABLED; - else - status_ |= TF_LBI_STATUS_DISABLED; - update(TF_LBI_STATUS); - } -} - -// need to create the button with TF_LBI_STYLE_BTN_TOGGLE style -bool LangBarButton::toggled() const { - return (status_ & TF_LBI_STATUS_BTN_TOGGLED) ? true : false; -} - -void LangBarButton::setToggled(bool toggle) { - if(toggled() != toggle) { - if(toggle) - status_ |= TF_LBI_STATUS_BTN_TOGGLED; - else - status_ &= ~TF_LBI_STATUS_BTN_TOGGLED; - update(TF_LBI_STATUS); - } -} - - -DWORD LangBarButton::style() const { - return info_.dwStyle; -} - -void LangBarButton::setStyle(DWORD style) { - info_.dwStyle = style; -} - - -// COM stuff - -// IUnknown -STDMETHODIMP LangBarButton::QueryInterface(REFIID riid, void **ppvObj) { - if (ppvObj == NULL) - return E_INVALIDARG; - - if(IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfLangBarItem) || IsEqualIID(riid, IID_ITfLangBarItemButton)) - *ppvObj = (ITfLangBarItemButton*)this; - else if(IsEqualIID(riid, IID_ITfSource)) - *ppvObj = (ITfSource*)this; - else - *ppvObj = NULL; - - if(*ppvObj) { - AddRef(); - return S_OK; - } - return E_NOINTERFACE; -} - -// IUnknown implementation -STDMETHODIMP_(ULONG) LangBarButton::AddRef(void) { - return ++refCount_; -} - -STDMETHODIMP_(ULONG) LangBarButton::Release(void) { - assert(refCount_ > 0); - const ULONG newCount = --refCount_; - if (0 == refCount_) - delete this; - return newCount; -} - -// ITfLangBarItem -STDMETHODIMP LangBarButton::GetInfo(TF_LANGBARITEMINFO *pInfo) { - *pInfo = info_; - return S_OK; -} - -STDMETHODIMP LangBarButton::GetStatus(DWORD *pdwStatus) { - *pdwStatus = status_; - return S_OK; -} - -STDMETHODIMP LangBarButton::Show(BOOL fShow) { - return E_NOTIMPL; -} - -STDMETHODIMP LangBarButton::GetTooltipString(BSTR *pbstrToolTip) { - *pbstrToolTip = ::SysAllocString(tooltip_.c_str()); - return *pbstrToolTip ? S_OK : E_FAIL; -} - -// ITfLangBarItemButton -STDMETHODIMP LangBarButton::OnClick(TfLBIClick click, POINT pt, const RECT *prcArea) { - TextService::CommandType type; - if(click == TF_LBI_CLK_RIGHT) - type = TextService::COMMAND_RIGHT_CLICK; - else - type = TextService::COMMAND_LEFT_CLICK; - textService_->onCommand(commandId_, type); - return S_OK; -} - -STDMETHODIMP LangBarButton::InitMenu(ITfMenu *pMenu) { - if(!menu_) - return E_FAIL; - buildITfMenu(pMenu, menu_); - return S_OK; -} - -STDMETHODIMP LangBarButton::OnMenuSelect(UINT wID) { - textService_->onCommand(wID, TextService::COMMAND_MENU); - return S_OK; -} - -STDMETHODIMP LangBarButton::GetIcon(HICON *phIcon) { - // https://msdn.microsoft.com/zh-tw/library/windows/desktop/ms628718%28v=vs.85%29.aspx - // The caller will delete the icon when it's no longer needed. - // However, we might still need it. So let's return a copy here. - *phIcon = (HICON)CopyImage(icon_, IMAGE_ICON, 0, 0, 0); - return S_OK; -} - -STDMETHODIMP LangBarButton::GetText(BSTR *pbstrText) { - *pbstrText = ::SysAllocString(info_.szDescription); - return *pbstrText ? S_OK : S_FALSE; -} - -// ITfSource -STDMETHODIMP LangBarButton::AdviseSink(REFIID riid, IUnknown *punk, DWORD *pdwCookie) { - if(IsEqualIID(riid, IID_ITfLangBarItemSink)) { - ITfLangBarItemSink* langBarItemSink; - if(punk->QueryInterface(IID_ITfLangBarItemSink, (void **)&langBarItemSink) == S_OK) { - *pdwCookie = (DWORD)rand(); - sinks_[*pdwCookie] = langBarItemSink; - return S_OK; - } - else - return E_NOINTERFACE; - } - return CONNECT_E_CANNOTCONNECT; -} - -STDMETHODIMP LangBarButton::UnadviseSink(DWORD dwCookie) { - std::map::iterator it = sinks_.find(dwCookie); - if(it != sinks_.end()) { - ITfLangBarItemSink* langBarItemSink = (ITfLangBarItemSink*)it->second; - langBarItemSink->Release(); - sinks_.erase(it); - return S_OK; - } - return CONNECT_E_NOCONNECTION; -} - - -// build ITfMenu according to the content of HMENU -void LangBarButton::buildITfMenu(ITfMenu* menu, HMENU templ) { - int n = ::GetMenuItemCount(templ); - for(int i = 0; i < n; ++i) { - MENUITEMINFO mi; - wchar_t textBuffer[256]; - memset(&mi, 0, sizeof(mi)); - mi.cbSize = sizeof(mi); - mi.dwTypeData = (LPTSTR)textBuffer; - mi.cch = 255; - mi.fMask = MIIM_FTYPE|MIIM_ID|MIIM_STATE|MIIM_STRING|MIIM_SUBMENU; - if(::GetMenuItemInfoW(templ, i, TRUE, &mi)) { - UINT flags = 0; - wchar_t* text = nullptr; - ULONG textLen = 0; - ITfMenu* subMenu = NULL; - ITfMenu** pSubMenu = NULL; - if(mi.hSubMenu) { // has submenu - pSubMenu = &subMenu; - flags |= TF_LBMENUF_SUBMENU; - } - if(mi.fType == MFT_STRING) { // text item - text = (wchar_t*)mi.dwTypeData; - textLen = mi.cch; - } - else if(mi.fType == MFT_SEPARATOR) { // separator item - flags |= TF_LBMENUF_SEPARATOR; - } - else // other types are not supported - continue; - - if(mi.fState & MFS_CHECKED) // checked - flags |= TF_LBMENUF_CHECKED; - if(mi.fState & (MFS_GRAYED|MFS_DISABLED)) // disabled - flags |= TF_LBMENUF_GRAYED; - - if(menu->AddMenuItem(mi.wID, flags, NULL, 0, text, textLen, pSubMenu) == S_OK) { - if(subMenu) { - buildITfMenu(subMenu, mi.hSubMenu); - subMenu->Release(); - } - } - } - else { - DWORD error = ::GetLastError(); - } - } -} - -// call all sinks to generate update notifications -void LangBarButton::update(DWORD flags) { - if(!sinks_.empty()) { - std::map::iterator it; - for(it = sinks_.begin(); it != sinks_.end(); ++it) { - ITfLangBarItemSink* sink = it->second; - sink->OnUpdate(flags); - } - } -} - -} // namespace Ime - diff --git a/libIME/LangBarButton.h b/libIME/LangBarButton.h deleted file mode 100644 index aac7934..0000000 --- a/libIME/LangBarButton.h +++ /dev/null @@ -1,118 +0,0 @@ -// -// Copyright (C) 2013 Hong Jen Yee (PCMan) -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Library General Public -// License as published by the Free Software Foundation; either -// version 2 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Library General Public License for more details. -// -// You should have received a copy of the GNU Library General Public -// License along with this library; if not, write to the -// Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -// Boston, MA 02110-1301, USA. -// - -#ifndef IME_LANGUAGE_BAR_BUTTON_H -#define IME_LANGUAGE_BAR_BUTTON_H - -#include -#include -#include -#include -#include - -namespace Ime { - -class TextService; - -class LangBarButton: - public ITfLangBarItemButton, - public ITfSource { -public: - LangBarButton(TextService* service, const GUID& guid, UINT commandId = 0, const wchar_t* text = NULL, DWORD style = TF_LBI_STYLE_BTN_BUTTON); - - // public methods - const wchar_t* text() const; - void setText(const wchar_t* text); - void setText(UINT stringId); - - const wchar_t* tooltip() const; - void setTooltip(const wchar_t* tooltip); - void setTooltip(UINT stringId); - - HICON icon() const; - void setIcon(HICON icon); - void setIcon(UINT iconId); - - UINT commandId() const; - void setCommandId(UINT id); - - HMENU menu() const; - void setMenu(HMENU menu); - - bool enabled() const; - void setEnabled(bool enable); - - // need to create the button with TF_LBI_STYLE_BTN_TOGGLE style - bool toggled() const; - void setToggled(bool toggle); - - DWORD style() const; - void setStyle(DWORD style); - - // COM-related stuff - - // IUnknown - STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); - STDMETHODIMP_(ULONG) AddRef(void); - STDMETHODIMP_(ULONG) Release(void); - - // ITfLangBarItem - STDMETHODIMP GetInfo(TF_LANGBARITEMINFO *pInfo); - STDMETHODIMP GetStatus(DWORD *pdwStatus); - STDMETHODIMP Show(BOOL fShow); - STDMETHODIMP GetTooltipString(BSTR *pbstrToolTip); - - // ITfLangBarItemButton - STDMETHODIMP OnClick(TfLBIClick click, POINT pt, const RECT *prcArea); - STDMETHODIMP InitMenu(ITfMenu *pMenu); - STDMETHODIMP OnMenuSelect(UINT wID); - STDMETHODIMP GetIcon(HICON *phIcon); - STDMETHODIMP GetText(BSTR *pbstrText); - - // ITfSource - STDMETHODIMP AdviseSink(REFIID riid, IUnknown *punk, DWORD *pdwCookie); - STDMETHODIMP UnadviseSink(DWORD dwCookie); - - void update(DWORD flags = TF_LBI_BTNALL); - - TextService* textService() const { - return textService_; - }; - -protected: // COM object should not be deleted directly. calling Release() instead. - virtual ~LangBarButton(void); - -private: - void buildITfMenu(ITfMenu* menu, HMENU templ); - -private: - int refCount_; - TextService* textService_; - TF_LANGBARITEMINFO info_; - UINT commandId_; - std::wstring tooltip_; - HICON icon_; - HMENU menu_; - std::map sinks_; - DWORD status_; -}; - -} - -#endif diff --git a/libIME/libIME.cpp b/libIME/libIME.cpp deleted file mode 100644 index e690ab4..0000000 --- a/libIME/libIME.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright (C) 2013 Hong Jen Yee (PCMan) -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Library General Public -// License as published by the Free Software Foundation; either -// version 2 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Library General Public License for more details. -// -// You should have received a copy of the GNU Library General Public -// License along with this library; if not, write to the -// Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -// Boston, MA 02110-1301, USA. -// - -// libIME.cpp : Defines the exported functions for the DLL application. - -#include "libIME.h" diff --git a/libIME/libIME.h b/libIME/libIME.h deleted file mode 100644 index 68bc423..0000000 --- a/libIME/libIME.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// Copyright (C) 2013 Hong Jen Yee (PCMan) -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Library General Public -// License as published by the Free Software Foundation; either -// version 2 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Library General Public License for more details. -// -// You should have received a copy of the GNU Library General Public -// License along with this library; if not, write to the -// Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -// Boston, MA 02110-1301, USA. -// - -#ifndef LIB_IME_H -#define LIB_IME_H - -// The following ifdef block is the standard way of creating macros which make exporting -// from a DLL simpler. All files within this DLL are compiled with the LIBIME_EXPORTS -// symbol defined on the command line. This symbol should not be defined on any project -// that uses this DLL. This way any other project whose source files include this file see -// LIBIME_API functions as being imported from a DLL, whereas this DLL sees symbols -// defined with this macro as being exported. -#ifdef LIBIME_EXPORTS -#define LIBIME_API __declspec(dllexport) -#else -#define LIBIME_API __declspec(dllimport) -#endif - -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers -// Windows Header Files: -#include - - -#endif diff --git a/libIME/libIME.rc b/libIME/libIME.rc deleted file mode 100644 index cce0b14..0000000 Binary files a/libIME/libIME.rc and /dev/null differ diff --git a/libIME/resource.h b/libIME/resource.h deleted file mode 100644 index 243a088..0000000 --- a/libIME/resource.h +++ /dev/null @@ -1,14 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by libIME.rc - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 101 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/libIME/src/lib.rs b/libIME/src/lib.rs deleted file mode 100644 index da76a35..0000000 --- a/libIME/src/lib.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod display_attribute; -mod gfx; -mod window; - -#[no_mangle] -unsafe extern "C" fn LibIME2Init() { - win_dbg_logger::rust_win_dbg_logger_init_debug(); - log::debug!("libIME2 initialized"); -} diff --git a/scripts/build_installer.bat b/scripts/build_installer.bat index da0dc24..eb13ccd 100644 --- a/scripts/build_installer.bat +++ b/scripts/build_installer.bat @@ -2,31 +2,30 @@ set RUSTFLAGS=-Ctarget-feature=+crt-static set SQLITE3_STATIC=1 cmake -B build\x86 -A Win32 -DBUILD_TESTING=OFF -DVCPKG_TARGET_TRIPLET=x86-windows-static -cmake --build build\x86 --config Release -cmake -B build\x64 -A x64 -DBUILD_TESTING=OFF -DVCPKG_TARGET_TRIPLET=x64-windows-static -cmake --build build\x64 --config Release -pushd tsfreg -cargo build --release -popd +cmake --build build\x86 -t libchewing\data\data --config Release +cmake --build build\x86 -t ChewingPreferences --config Release + +cargo build -p chewing_tip --release --target x86_64-pc-windows-msvc +cargo build -p chewing_tip --release --target i686-pc-windows-msvc +cargo build -p tsfreg --release --target i686-pc-windows-msvc + mkdir dist mkdir build\installer mkdir build\installer\assets copy assets\* build\installer\assets\ copy installer\* build\installer\ -copy ChewingTextService\im.chewing.Chewing.ico build\installer\chewing.ico +copy chewing_tip\im.chewing.Chewing.ico build\installer\chewing.ico mkdir build\installer\Dictionary copy libchewing\data\*.dat build\installer\Dictionary\ -copy build\x64\libchewing\data\*.dat build\installer\Dictionary\ +copy build\x86\libchewing\data\*.dat build\installer\Dictionary\ mkdir build\installer\x86 -copy build\x86\ChewingTextService\Release\*.dll build\installer\x86\ -copy build\x86\libchewing\Release\*.dll build\installer\x86\ +copy target\i686-pc-windows-msvc\release\chewing_tip.dll build\installer\x86\ copy build\x86\ChewingPreferences\Release\*.exe build\installer\ copy build\x86\libchewing\chewing-cli.exe build\installer\ mkdir build\installer\x64 -copy build\x64\ChewingTextService\Release\*.dll build\installer\x64\ -copy build\x64\libchewing\Release\*.dll build\installer\x64\ -copy target\release\tsfreg.exe build\installer\ +copy target\x86_64-pc-windows-msvc\release\chewing_tip.dll build\installer\x64 +copy target\i686-pc-windows-msvc\release\tsfreg.exe build\installer\ pushd build\installer msbuild -p:Configuration=Release -restore windows-chewing-tsf.wixproj popd -copy build\installer\bin\Release\zh-TW\windows-chewing-tsf.msi dist\windows-chewing-tsf-unsigned.msi \ No newline at end of file +copy build\installer\bin\Release\zh-TW\windows-chewing-tsf.msi dist\windows-chewing-tsf-unsigned.msi diff --git a/scripts/build_installer_debug.bat b/scripts/build_installer_debug.bat deleted file mode 100644 index 6bc3d78..0000000 --- a/scripts/build_installer_debug.bat +++ /dev/null @@ -1,32 +0,0 @@ -set RUSTFLAGS=-Ctarget-feature=+crt-static -set SQLITE3_STATIC=1 - -cmake -B build\x86 -A Win32 -DBUILD_TESTING=OFF -DVCPKG_TARGET_TRIPLET=x86-windows-static -cmake --build build\x86 --config Debug -cmake -B build\x64 -A x64 -DBUILD_TESTING=OFF -DVCPKG_TARGET_TRIPLET=x64-windows-static -cmake --build build\x64 --config Debug -pushd tsfreg -cargo build --release -popd -mkdir dist -mkdir build\installer -mkdir build\installer\assets -copy assets\* build\installer\assets\ -copy installer\* build\installer\ -copy ChewingTextService\im.chewing.Chewing.ico build\installer\chewing.ico -mkdir build\installer\Dictionary -copy libchewing\data\*.dat build\installer\Dictionary\ -copy build\x64\libchewing\data\*.dat build\installer\Dictionary\ -mkdir build\installer\x86 -copy build\x86\ChewingTextService\Debug\*.dll build\installer\x86\ -copy build\x86\libchewing\Debug\*.dll build\installer\x86\ -copy build\x86\ChewingPreferences\Debug\*.exe build\installer\ -copy build\x86\libchewing\chewing-cli.exe build\installer\ -mkdir build\installer\x64 -copy build\x64\ChewingTextService\Debug\*.dll build\installer\x64\ -copy build\x64\libchewing\Debug\*.dll build\installer\x64\ -copy target\release\tsfreg.exe build\installer\ -pushd build\installer -msbuild -p:Configuration=Release -restore windows-chewing-tsf.wixproj -popd -copy build\installer\bin\Release\zh-TW\windows-chewing-tsf.msi dist\windows-chewing-tsf-unsigned.msi \ No newline at end of file diff --git a/version.rc b/version.rc index 3d104f2..998f665 100644 --- a/version.rc +++ b/version.rc @@ -4,5 +4,5 @@ #define VER_PRODUCTVERSION_STR "24.10.1.0\0" #define ABOUT_CAPTION_WITH_VER "關於新酷音輸入法 (24.10.1.0)\0" #define ABOUT_VERSION_STR "版本:24.10.1.0\0" -#define ABOUT_RELEASE_DATE_STR "發行日期:2024 年 12 月 22 日\0" +#define ABOUT_RELEASE_DATE_STR "發行日期:2024 年 12 月 24 日\0" #define PREFS_TITLE_WITH_VER "設定新酷音輸入法 (24.10.1.0)\0" diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 18633d7..610f18d 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" [dependencies] anyhow = "1.0.94" indoc = "2.0.5" -jiff = "0.1.15" +jiff = { version = "0.1.15", default-features = false, features = ["std"] } xflags = "0.3.2"