Skip to content

Commit

Permalink
build with fcitx5 (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
eagleoflqj authored Sep 29, 2024
1 parent 34c3008 commit c9b1973
Show file tree
Hide file tree
Showing 14 changed files with 390 additions and 9 deletions.
14 changes: 12 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ jobs:
- uses: actions/checkout@v4

- name: Install dependencies
run: brew install swift-format
run: brew install clang-format swift-format

- name: Lint
run: |
find src keyboard -name '*.cpp' -o -name '*.h' | xargs clang-format -Werror --dry-run
swift-format lint -rs src keyboard
build:
Expand All @@ -29,10 +30,19 @@ jobs:

steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- name: Install dependencies
run: |
brew install \
extra-cmake-modules
IOS_PLATFORM=SIMULATOR ./install-deps.sh
- name: Build
run: |
cmake -B build -G Xcode \
git apply --directory=fcitx5 patches/fcitx5.patch
PKG_CONFIG_PATH=build/sysroot/usr/lib/pkgconfig cmake -B build -G Xcode \
-DCMAKE_TOOLCHAIN_FILE=cmake/ios.cmake \
-DIOS_PLATFORM=SIMULATOR
cmake --build build --config Release
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.DS_Store
build
*.tar.bz2
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "fcitx5"]
path = fcitx5
url = https://github.com/fcitx/fcitx5
27 changes: 27 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING INTERNAL FORCE)
project(fcitx5-ios VERSION 0.1.0 LANGUAGES CXX Swift)
set(CMAKE_OSX_DEPLOYMENT_TARGET 15)
set(CMAKE_Swift_LANGUAGE_VERSION 5.9)
set(CMAKE_CXX_STANDARD 17)

set(BUNDLE_IDENTIFIER "org.fcitx.Fcitx5")
set(BUNDLE_NAME "Fcitx5")
Expand All @@ -25,5 +26,31 @@ include(AddSwift)
set(KEYBOARD_NAME "Keyboard")
set(KEYBOARD_LANGUAGE "zh-Hans")

# For dependencies not to be find via pkg-config
set(LibIntl_DIR "build/sysroot/usr/lib/cmake")
find_package(LibIntl)
set(fmt_DIR "build/sysroot/usr/lib/cmake/fmt")
find_package(fmt)

option(ENABLE_TEST "" OFF)
option(ENABLE_COVERAGE "" OFF)
option(ENABLE_ENCHANT "" OFF)
option(ENABLE_X11 "" OFF)
option(ENABLE_WAYLAND "" OFF)
option(ENABLE_DBUS "" OFF)
option(ENABLE_DOC "" OFF)
option(ENABLE_SERVER "" OFF)
option(ENABLE_KEYBOARD "" OFF)
option(USE_SYSTEMD "" OFF)
option(ENABLE_XDGAUTOSTART "" OFF)
option(ENABLE_EMOJI "" OFF)
option(ENABLE_LIBUUID "" OFF)
option(BUILD_SPELL_DICT "" OFF)

find_host_package(PkgConfig)
find_host_package(Gettext)

add_subdirectory(fcitx5)

add_subdirectory(keyboard)
add_subdirectory(src)
3 changes: 3 additions & 0 deletions cache/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This directory caches tarball of build dependencies
(downloaded from [prebuilder](https://github.com/fcitx-contrib/fcitx5-ios-prebuilder/releases)),
which will be extracted to `build/sysroot/usr`.
11 changes: 11 additions & 0 deletions cmake/ios.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ set (CMAKE_AR ar CACHE FILEPATH "" FORCE)
set (CMAKE_CXX_COMPILER_WORKS TRUE)
set (CMAKE_C_COMPILER_WORKS TRUE)

# Touch the toolchain variable to suppress the "unused variable" warning.
# This happens if CMake is invoked with the same command line the second time.
if(CMAKE_TOOLCHAIN_FILE)
endif()

# Fix for PThread library not in path
set(CMAKE_THREAD_LIBS_INIT "-lpthread")
set(CMAKE_HAVE_THREADS_LIBRARY 1)
set(CMAKE_USE_WIN32_THREADS_INIT 0)
set(CMAKE_USE_PTHREADS_INIT 1)

# All iOS/Darwin specific settings - some may be redundant
set (CMAKE_SHARED_LIBRARY_PREFIX "lib")
set (CMAKE_SHARED_LIBRARY_SUFFIX ".dylib")
Expand Down
1 change: 1 addition & 0 deletions fcitx5
Submodule fcitx5 added at 9ab829
23 changes: 23 additions & 0 deletions install-deps.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
deps=(
fmt
libintl
libuv
)

EXTRACT_DIR=build/sysroot/usr
mkdir -p $EXTRACT_DIR

if [[ $IOS_PLATFORM == "SIMULATOR" ]]; then
POSTFIX=-$(uname -m)
else
POSTFIX=""
fi

for dep in "${deps[@]}"; do
file=$dep$POSTFIX.tar.bz2
[[ -f cache/$file ]] || wget -P cache https://github.com/fcitx-contrib/fcitx5-ios-prebuilder/releases/download/latest/$file
tar xjvf cache/$file -C $EXTRACT_DIR
done

sed -i '' "s|=/usr/include|=$(pwd)/$EXTRACT_DIR/include|" $EXTRACT_DIR/lib/pkgconfig/libuv.pc
sed -i '' "s|-L\${libdir} -luv|$(pwd)/$EXTRACT_DIR/lib/libuv.a|" $EXTRACT_DIR/lib/pkgconfig/libuv.pc
8 changes: 7 additions & 1 deletion keyboard/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ set_target_properties(keyboard PROPERTIES
XCODE_PRODUCT_TYPE "com.apple.product-type.app-extension"
)

target_include_directories(keyboard PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
target_include_directories(keyboard PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}"
"${CMAKE_CURRENT_BINARY_DIR}/../fcitx5"
)

target_compile_options(keyboard PUBLIC
"$<$<COMPILE_LANGUAGE:Swift>:-cxx-interoperability-mode=default>"
)

target_link_libraries(keyboard PRIVATE Fcitx5::Core)
2 changes: 1 addition & 1 deletion keyboard/KeyboardViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class KeyboardViewController: UIInputViewController {
override func viewDidLoad() {
super.viewDidLoad()

logger.error("\(getName())")
startFcitx()

// Perform custom UI setup here
self.nextKeyboardButton = UIButton(type: .system)
Expand Down
20 changes: 18 additions & 2 deletions keyboard/fcitx.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
#include "nativestreambuf.h"
#include <fcitx/instance.h>
#include <thread>

#include "fcitx.h"

std::string getName() {
return "f5i";
native_streambuf log_streambuf;
std::unique_ptr<fcitx::Instance> instance;
std::ostream stream(&log_streambuf);
std::thread fcitx_thread;

void startFcitx() {
if (instance) {
return;
}
fcitx::Log::setLogStream(stream);
fcitx::Log::setLogRule("*=5,notimedate");
instance = std::make_unique<fcitx::Instance>(0, nullptr);
fcitx_thread = std::thread([] { instance->exec(); });
return;
}
4 changes: 1 addition & 3 deletions keyboard/fcitx.h
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
#include <string>

std::string getName();
void startFcitx();
118 changes: 118 additions & 0 deletions keyboard/nativestreambuf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* SPDX-FileCopyrightText: Copyright 2021-2023 Fcitx5 for Android Contributors
* SPDX-License-Identifier: GPL-3.0-only
* SPDX-FileCopyrightText: Copyright 2023 Qijia Liu
*/
#ifndef FCITX5_IOS_NATIVESTREAMBUF_H
#define FCITX5_IOS_NATIVESTREAMBUF_H

#include <array>
#include <iostream>
#include <os/log.h>
#include <streambuf>

// Dynamic content is limited to 256B, see log.h
template <std::size_t SIZE = 256>
class native_streambuf : public std::streambuf {
public:
using Base = std::streambuf;
using char_type = typename Base::char_type;
using int_type = typename Base::int_type;

native_streambuf()
: _buffer{}, logger(os_log_create("org.fcitx.Fcitx5", "FcitxLog")) {
Base::setp(_buffer.begin(), _buffer.end() - 1);
}

// buffer is full but current "line" of log hasn't finished
int_type overflow(int_type ch) override {
// append terminate character to the buffer (usually _buffer.end() when
// overflow)
*Base::pptr() = '\0';
const char *text = Base::pbase();
if (should_offset) {
// it's the first write of this "line", guess priority
update_log_priority(text[0]);
// this write would skip first character
write_log(text);
// consequence write of this "line" should use same priority and
// should not skip characters
should_offset = false;
} else {
// it's not the first write of this "line", so just write
write_log(text);
}
// mark buffer as available, since it's content has been written to
// android log but we need to preserve the last position for '\0' in
// case it overflows
Base::setp(_buffer.begin(), _buffer.end() - 1);
// write 'ch' as char if it's not eof
if (!Base::traits_type::eq_int_type(ch, Base::traits_type::eof())) {
const char_type c = Base::traits_type::to_char_type(ch);
Base::xsputn(&c, 1);
}
return 0;
}

// current "line" of log has finished, and buffer is not full
int_type sync() override {
*Base::pptr() = '\0';
const char *text = Base::pbase();
if (should_offset) {
// it's the first write of this "line", guess priority
update_log_priority(text[0]);
}
write_log(text);
// this "line" has finished and written to NS log,
// reset state for next "line"
should_offset = true;
// mark buffer as available and preserve last position for '\0'
Base::setp(_buffer.begin(), _buffer.end() - 1);
return 0;
}

private:
std::array<char_type, SIZE> _buffer;
os_log_t logger;
os_log_type_t prio;
/**
* whether the first character in buffer represents log level or not
*/
bool should_offset = true;

void update_log_priority(const char_type first) {
switch (first) {
case 'D':
prio = OS_LOG_TYPE_DEBUG;
break;
case 'I':
prio = OS_LOG_TYPE_INFO;
break;
case 'W':
prio = OS_LOG_TYPE_ERROR;
break;
case 'E':
case 'F':
prio = OS_LOG_TYPE_FAULT;
break;
default:
break;
}
}

void write_log(const char_type *text) const {
#ifdef NDEBUG
if (prio != OS_LOG_TYPE_DEBUG) {
std::cerr << text;
}
#else
os_log_with_type(logger, prio, "%{public}s",
text + (should_offset ? 1 : 0));
std::cerr << text;
#endif
}
};

#endif // FCITX5_IOS_NATIVESTREAMBUF_H
Loading

0 comments on commit c9b1973

Please sign in to comment.