Skip to content

Commit

Permalink
register dll as input method (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
eagleoflqj authored Jan 20, 2025
1 parent d400476 commit c16b14f
Show file tree
Hide file tree
Showing 21 changed files with 389 additions and 2 deletions.
35 changes: 33 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ on:
workflow_dispatch:

jobs:
build:
runs-on: windows-latest
build-fcitx5:
runs-on: windows-2025
steps:
- uses: actions/checkout@v4
with:
Expand Down Expand Up @@ -39,3 +39,34 @@ jobs:
- name: Setup tmate session
if: ${{ failure() }}
uses: mxschmitt/action-tmate@v3

build-win32:
runs-on: windows-2025
defaults:
run:
working-directory: win32
steps:
- name: Disable CRLF
working-directory: .
run: git config --global core.autocrlf false

- uses: actions/checkout@v4

- name: Lint
run: ./scripts/lint.ps1

- name: Build
run: |
cmake -B build -G Ninja `
-DCMAKE_BUILD_TYPE=Release
cmake --build build
- name: Test
run: ctest --test-dir build --output-on-failure

- name: Check validity
run: md5sum -c checksum

- name: Setup tmate session
if: ${{ failure() }}
uses: mxschmitt/action-tmate@v3
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Fcitx5 Windows
## Credits
* [fcitx5](https://github.com/fcitx/fcitx5): LGPL-2.1-or-later
* [weasel](https://github.com/rime/weasel): GPL-3.0-only
1 change: 1 addition & 0 deletions win32/.clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
IndentWidth: 4
5 changes: 5 additions & 0 deletions win32/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"recommendations": [
"llvm-vs-code-extensions.vscode-clangd"
]
}
6 changes: 6 additions & 0 deletions win32/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"clangd.arguments": [
"-background-index",
"-compile-commands-dir=build"
]
}
53 changes: 53 additions & 0 deletions win32/.vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "Configure",
"command": "cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug",
"group": {
"kind": "build"
}
},
{
"type": "shell",
"label": "Build",
"command": "cmake --build build",
"group": {
"kind": "build"
}
},
{
"type": "shell",
"label": "Install",
"command": "./scripts/install.ps1",
"group": {
"kind": "build"
}
},
{
"type": "shell",
"label": "Format",
"command": "./scripts/format.ps1",
"group": {
"kind": "build"
}
},
{
"type": "shell",
"label": "Lint",
"command": "./scripts/lint.ps1",
"group": {
"kind": "build"
}
},
{
"type": "shell",
"label": "Test",
"command": "ctest --test-dir build --output-on-failure",
"group": {
"kind": "build"
}
}
]
}
16 changes: 16 additions & 0 deletions win32/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.27)

set(CMAKE_C_COMPILER clang)
set(CMAKE_CXX_COMPILER clang++)

project(fcitx5-win32 VERSION 0.1.0)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

set(CMAKE_CXX_STANDARD 20)

add_subdirectory(assets)
add_subdirectory(dll)

enable_testing()
add_subdirectory(tests)
10 changes: 10 additions & 0 deletions win32/assets/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
set(PENGUIN_SVG "${CMAKE_CURRENT_SOURCE_DIR}/penguin.svg")
set(PENGUIN_ICO "${PROJECT_BINARY_DIR}/dll/penguin.ico")
add_custom_command(
OUTPUT "${PENGUIN_ICO}"
COMMAND magick -background none "${PENGUIN_SVG}" "${PENGUIN_ICO}"
DEPENDS "${PENGUIN_SVG}"
)
add_custom_target(GenerateIco ALL
DEPENDS "${PENGUIN_ICO}"
)
6 changes: 6 additions & 0 deletions win32/assets/penguin.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions win32/checksum
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cfcc8496f7382fc59392adf23179fdb1 *build/dll/penguin.ico
7 changes: 7 additions & 0 deletions win32/dll/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
add_library(fcitx5-x86_64 SHARED
main.cpp
register.cpp
util.cpp
)

target_compile_options(fcitx5-x86_64 PRIVATE "-Wno-dll-attribute-on-redeclaration")
34 changes: 34 additions & 0 deletions win32/dll/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "register.h"

CRITICAL_SECTION CS;

__declspec(dllexport) STDAPI DllUnregisterServer() {
fcitx::UnregisterCategories();
fcitx::UnregisterProfiles();
fcitx::UnregisterServer();
return S_OK;
}

__declspec(dllexport) STDAPI DllRegisterServer() {
if (fcitx::RegisterServer() && fcitx::RegisterProfiles() &&
fcitx::RegisterCategories()) {
return S_OK;
}
DllUnregisterServer();
return E_FAIL;
}

BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID pvReserved) {
switch (dwReason) {
case DLL_PROCESS_ATTACH:
fcitx::dllInstance = hInstance;
if (!InitializeCriticalSectionAndSpinCount(&CS, 0)) {
return FALSE;
}
break;
case DLL_PROCESS_DETACH:
DeleteCriticalSection(&CS);
break;
}
return TRUE;
}
121 changes: 121 additions & 0 deletions win32/dll/register.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#include "register.h"
#include "util.h"
#include <atlcomcli.h>
#include <filesystem>
#include <msctf.h>

namespace fs = std::filesystem;

#define FCITX5 "Fcitx5"
#define THREADING_MODEL "ThreadingModel"
#define APARTMENT "Apartment"

namespace fcitx {
HINSTANCE dllInstance; // Set by DllMain.

/*
HKEY_CLASSES_ROOT\CLSID\{FC3869BA-51E3-4078-8EE2-5FE49493A1F4}: Fcitx5
- InprocServer32: C:\Windows\system32
ThreadingModel: Apartment
*/
BOOL RegisterServer() {
DWORD dw;
HKEY hKey = nullptr;
HKEY hSubKey = nullptr;
WCHAR dllPath[MAX_PATH];
auto achIMEKey = "CLSID\\" + guidToString(FCITX_CLSID);
BOOL ret = RegCreateKeyExA(HKEY_CLASSES_ROOT, achIMEKey.c_str(), 0, nullptr,
REG_OPTION_NON_VOLATILE, KEY_WRITE, nullptr,
&hKey, &dw);
ret |=
RegSetValueExA(hKey, nullptr, 0, REG_SZ,
reinterpret_cast<const BYTE *>(FCITX5), sizeof FCITX5);
ret |= RegCreateKeyExA(hKey, "InprocServer32", 0, nullptr,
REG_OPTION_NON_VOLATILE, KEY_WRITE, nullptr,
&hSubKey, &dw);
auto hr = GetModuleFileNameW(dllInstance, dllPath, MAX_PATH);
ret |= RegSetValueExW(hSubKey, nullptr, 0, REG_SZ,
reinterpret_cast<const BYTE *>(dllPath),
hr * sizeof(WCHAR));
ret |= RegSetValueExA(hSubKey, THREADING_MODEL, 0, REG_SZ,
reinterpret_cast<const BYTE *>(APARTMENT),
sizeof APARTMENT);
RegCloseKey(hSubKey);
RegCloseKey(hKey);
return ret == ERROR_SUCCESS;
}

void UnregisterServer() {}

/*
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\CTF\TIP\{FC3869BA-51E3-4078-8EE2-5FE49493A1F4}
- LanguageProfile
- 0x00000804
- {9A92B895-29B9-4F19-9627-9F626C9490F2}
Description: Fcitx5
Enable: 0x00000001
IconFile: /path/to/icon in the same directory with dll
IconIndex: 0x00000000
*/
BOOL RegisterProfiles() {
std::wstring pchDesc = stringToWString(FCITX5, CP_UTF8);
WCHAR dllPath[MAX_PATH];
GetModuleFileNameW(dllInstance, dllPath, MAX_PATH);
fs::path path = dllPath;
path = path.remove_filename().append("penguin.ico");
CComPtr<ITfInputProcessorProfileMgr> mgr;
mgr.CoCreateInstance(CLSID_TF_InputProcessorProfiles, nullptr, CLSCTX_ALL);
auto hr = mgr->RegisterProfile(
FCITX_CLSID, TEXTSERVICE_LANGID_HANS, PROFILE_GUID, pchDesc.c_str(),
pchDesc.size() * sizeof(WCHAR), path.c_str(),
path.wstring().size() * sizeof(WCHAR), 0, nullptr, 0, 1, 0);
mgr.Release();
return hr == S_OK;
}

void UnregisterProfiles() {}

// No documentation about what they means.
const GUID Categories[] = {GUID_TFCAT_CATEGORY_OF_TIP,
GUID_TFCAT_TIP_KEYBOARD,
GUID_TFCAT_TIPCAP_SECUREMODE,
GUID_TFCAT_TIPCAP_UIELEMENTENABLED,
GUID_TFCAT_TIPCAP_INPUTMODECOMPARTMENT,
GUID_TFCAT_TIPCAP_COMLESS,
GUID_TFCAT_TIPCAP_WOW16,
GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT,
GUID_TFCAT_TIPCAP_SYSTRAYSUPPORT,
GUID_TFCAT_PROP_AUDIODATA,
GUID_TFCAT_PROP_INKDATA,
GUID_TFCAT_PROPSTYLE_CUSTOM,
GUID_TFCAT_PROPSTYLE_STATIC,
GUID_TFCAT_PROPSTYLE_STATICCOMPACT,
GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER,
GUID_TFCAT_DISPLAYATTRIBUTEPROPERTY};

/*
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\CTF\TIP\{FC3869BA-51E3-4078-8EE2-5FE49493A1F4}
- Category
- Category
- GUID of categories
- {FC3869BA-51E3-4078-8EE2-5FE49493A1F4}
- ...
- Item
- {FC3869BA-51E3-4078-8EE2-5FE49493A1F4}
- GUID of categories
- ...
*/
BOOL RegisterCategories() {
ITfCategoryMgr *mgr;
CoCreateInstance(CLSID_TF_CategoryMgr, nullptr, CLSCTX_INPROC_SERVER,
IID_ITfCategoryMgr, reinterpret_cast<void **>(&mgr));
HRESULT hr = S_OK;
for (const auto &guid : Categories) {
hr |= mgr->RegisterCategory(FCITX_CLSID, guid, FCITX_CLSID);
}
mgr->Release();
return hr == S_OK;
}

void UnregisterCategories() {}
} // namespace fcitx
14 changes: 14 additions & 0 deletions win32/dll/register.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <Windows.h>

namespace fcitx {
extern HINSTANCE dllInstance;

BOOL RegisterServer();
void UnregisterServer();
BOOL RegisterProfiles();
void UnregisterProfiles();
BOOL RegisterCategories();
void UnregisterCategories();
} // namespace fcitx
36 changes: 36 additions & 0 deletions win32/dll/util.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include "util.h"
#include <format>

namespace fcitx {
// {FC3869BA-51E3-4078-8EE2-5FE49493A1F4}
const GUID FCITX_CLSID = {0xfc3869ba,
0x51e3,
0x4078,
{0x8e, 0xe2, 0x5f, 0xe4, 0x94, 0x93, 0xa1, 0xf4}};

// {9A92B895-29B9-4F19-9627-9F626C9490F2}
const GUID PROFILE_GUID{0x9a92b895,
0x29b9,
0x4f19,
{0x96, 0x27, 0x9f, 0x62, 0x6c, 0x94, 0x90, 0xf2}};

std::string guidToString(REFGUID guid) {
const auto *p = reinterpret_cast<const unsigned char *>(&guid);
return std::format("{{{:02X}{:02X}{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}-{:"
"02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}}}",
p[3], p[2], p[1], p[0], p[5], p[4], p[7], p[6], p[8],
p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
}

std::wstring stringToWString(const std::string &str, int codePage) {
int len =
MultiByteToWideChar(codePage, 0, str.c_str(), str.size(), nullptr, 0);
WCHAR *buf = new WCHAR[len + 1];
MultiByteToWideChar(codePage, 0, str.c_str(), str.size(), buf, len);
buf[len] = '\0';
std::wstring wstr;
wstr.append(buf);
delete[] buf;
return wstr;
}
} // namespace fcitx
14 changes: 14 additions & 0 deletions win32/dll/util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <Windows.h>
#include <string>

#define TEXTSERVICE_LANGID_HANS \
MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED)

namespace fcitx {
extern const GUID FCITX_CLSID;
extern const GUID PROFILE_GUID;
std::string guidToString(REFGUID guid);
std::wstring stringToWString(const std::string &str, int codePage);
} // namespace fcitx
1 change: 1 addition & 0 deletions win32/scripts/format.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
git ls-files -- '*.cpp' '*.h' | ForEach-Object { clang-format -i $_ }
1 change: 1 addition & 0 deletions win32/scripts/install.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Start-Process -Verb RunAs powershell.exe -Args "-executionpolicy bypass -command Set-Location \`"$PWD\`"; regsvr32 build/dll/fcitx5-x86_64.dll"
6 changes: 6 additions & 0 deletions win32/scripts/lint.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
git ls-files -- '*.cpp' '*.h' | ForEach-Object {
clang-format -Werror --dry-run $_
if ($LASTEXITCODE -ne 0) {
exit 1
}
}
Loading

0 comments on commit c16b14f

Please sign in to comment.