Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial port to wasm #65

Merged
merged 2 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ endif()

find_package(PkgConfig REQUIRED)
pkg_check_modules(NlohmannJson REQUIRED IMPORTED_TARGET "nlohmann_json")
find_package(CURL REQUIRED)

if(NOT EMSCRIPTEN)
find_package(CURL REQUIRED)
endif()

if(LINUX)
pkg_check_modules(webkit2gtk REQUIRED IMPORTED_TARGET "webkit2gtk-4.1")
Expand All @@ -25,6 +28,8 @@ elseif(APPLE)
find_library(COCOA_LIB Cocoa REQUIRED)
find_library(WEBKIT_LIB WebKit REQUIRED)
set(LIBS PkgConfig::NlohmannJson ${COCOA_LIB} ${WEBKIT_LIB} CURL::libcurl)
elseif(EMSCRIPTEN)
set(LIBS PkgConfig::NlohmannJson)
endif()

include_directories(pugixml/src)
Expand Down
28 changes: 25 additions & 3 deletions include/webview_candidate_window.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@

#include "candidate_window.hpp"
#include "utility.hpp"
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#else
#include "webview.h"
#endif
#include <iostream>
#include <nlohmann/json.hpp>
#include <sstream>
Expand All @@ -12,6 +16,11 @@ namespace candidate_window {

enum CustomAPI : uint64_t { kCurl = 1 };

#ifdef __EMSCRIPTEN__
extern std::unordered_map<std::string, std::function<std::string(std::string)>>
handlers;
#endif

class WebviewCandidateWindow : public CandidateWindow {
public:
WebviewCandidateWindow();
Expand All @@ -36,12 +45,16 @@ class WebviewCandidateWindow : public CandidateWindow {
void set_accent_color();
void copy_html();

#ifndef __EMSCRIPTEN__
void set_api(uint64_t apis);
void load_plugins(const std::vector<std::string> &names);
void unload_plugins();
#endif

private:
#ifndef __EMSCRIPTEN__
std::shared_ptr<webview::webview> w_;
#endif
double cursor_x_ = 0;
double cursor_y_ = 0;
double x_ = 0;
Expand Down Expand Up @@ -74,19 +87,23 @@ class WebviewCandidateWindow : public CandidateWindow {
template <typename Ret = void, bool debug = false, typename... Args>
inline Ret invoke_js(const char *name, Args... args) {
std::stringstream ss;
ss << name << "(";
ss << "fcitx." << name << "(";
build_js_args(ss, args...);
ss << ");";
if constexpr (debug) {
std::cerr << ss.str() << "\n";
}
auto s = ss.str();
#ifdef __EMSCRIPTEN__
emscripten_run_script(s.c_str());
#else
std::weak_ptr<webview::webview> weak_w = w_;
async_on_main([=] {
if (auto w = weak_w.lock()) {
w->eval(s);
}
});
#endif
}

template <typename T>
Expand All @@ -109,7 +126,7 @@ class WebviewCandidateWindow : public CandidateWindow {
template <typename F> inline void bind(const std::string &name, F f) {
using Ret = typename function_traits<F>::return_type;
using ArgsTp = typename function_traits<F>::args_tuple;
w_->bind(name, [=](std::string args_json) -> std::string {
auto handler = [=](std::string args_json) -> std::string {
auto j = nlohmann::json::parse(args_json);
ArgsTp args;
if (std::tuple_size<ArgsTp>() > j.size()) {
Expand All @@ -134,7 +151,12 @@ class WebviewCandidateWindow : public CandidateWindow {
auto ret = std::apply(f, args);
return nlohmann::json(ret).dump();
}
});
};
#ifdef __EMSCRIPTEN__
handlers[name] = handler;
#else
w_->bind(name, handler);
#endif
}

template <typename Tuple, size_t... Is>
Expand Down
71 changes: 50 additions & 21 deletions page/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@ import {
unloadPlugins
} from './plugin'

window.fcitxLog = fcitxLog
window._onload && window._onload()

function escapeWS (s: string) {
// XXX: &emsp; is broken in Safari
return s.replaceAll(' ', '&nbsp;').replaceAll('\n', '<br>').replaceAll('\t', '&emsp;')
Expand Down Expand Up @@ -246,7 +243,7 @@ function updateInputPanel (preeditHTML: string, auxUpHTML: string, auxDownHTML:

function copyHTML () {
const html = document.documentElement.outerHTML
window._copyHTML(html)
fcitx._copyHTML(html)
}

hoverables.addEventListener('mouseleave', () => {
Expand All @@ -262,32 +259,64 @@ hoverables.addEventListener('wheel', e => {
if (getScrollState() === 2) {
return
}
window._page((<WheelEvent>e).deltaY > 0)
fcitx._page((<WheelEvent>e).deltaY > 0)
})

setTheme(0)
const distribution = process.env.FCITX_DISTRIBUTION

let fcitx: FCITX

if (distribution === 'fcitx5-js') {
fcitx = window.fcitx
} else {
// @ts-expect-error f5m binds C++ function to JS global function, but we want to call fcitx._select for both f5m and f5j.
fcitx = window
window.fcitx = fcitx

/*
Don't pollute page's style for f5j
background: transparent, draw panel as you wish
margin: default is 8px
overflow: no scrollbar
width, height: big enough, disregard window size
*/
const style = document.createElement('style')
style.innerHTML =
`body {
background: rgb(0 0 0 / 0%);
margin: 0;
overflow: hidden;
width: 1920px;
height: 1080px;
}`
document.head.append(style)
}

// JavaScript APIs that webview_candidate_window.cpp calls
window.setCandidates = setCandidates
window.setLayout = setLayout
window.updateInputPanel = updateInputPanel
window.resize = resize
window.setTheme = setTheme
window.setAccentColor = setAccentColor
window.setStyle = setStyle
window.setWritingMode = setWritingMode
window.copyHTML = copyHTML
window.scrollKeyAction = scrollKeyAction
window.answerActions = answerActions

Object.defineProperty(window, 'pluginManager', {
fcitx.setCandidates = setCandidates
fcitx.setLayout = setLayout
fcitx.updateInputPanel = updateInputPanel
fcitx.resize = resize
fcitx.setTheme = setTheme
fcitx.setAccentColor = setAccentColor
fcitx.setStyle = setStyle
fcitx.setWritingMode = setWritingMode
fcitx.copyHTML = copyHTML
fcitx.scrollKeyAction = scrollKeyAction
fcitx.answerActions = answerActions

Object.defineProperty(fcitx, 'pluginManager', {
value: pluginManager
})

Object.defineProperty(window, 'loadPlugins', {
Object.defineProperty(fcitx, 'loadPlugins', {
value: loadPlugins
})

Object.defineProperty(window, 'unloadPlugins', {
Object.defineProperty(fcitx, 'unloadPlugins', {
value: unloadPlugins
})

fcitx.fcitxLog = fcitxLog
fcitx._onload && fcitx._onload()
setTheme(0)
7 changes: 1 addition & 6 deletions page/generic.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
body {
background: rgb(0 0 0 / 0%); /* transparent, draw panel as you wish */
margin: 0; /* default is 8px */
overflow: hidden; /* no scrollbar */
width: 1920px; /* big enough, disregard window size */
height: 1080px;
#fcitx-theme {
user-select: none; /* disable text select */
font-family: sans-serif;
}
Expand Down
6 changes: 5 additions & 1 deletion page/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ declare global {
unload: () => void
}

interface Window {
type FCITX = {
// C++ APIs that api.ts calls
_onload?: () => void
_log: (s: string) => void
Expand Down Expand Up @@ -55,6 +55,10 @@ declare global {
register: (plugin: FcitxPlugin) => void
}
}

interface Window {
fcitx: FCITX
}
}

export {}
6 changes: 3 additions & 3 deletions page/log.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export function fcitxLog (...args: unknown[]) {
for (let i = 0; i < args.length; ++i) {
if (i > 0) {
window._log(' ')
window.fcitx._log(' ')
}
const arg = args[i]
let serialized = ''
Expand All @@ -10,7 +10,7 @@ export function fcitxLog (...args: unknown[]) {
serialized = JSON.stringify(arg)
} catch {}
}
window._log(serialized || String(arg))
window.fcitx._log(serialized || String(arg))
}
window._log('\n')
window.fcitx._log('\n')
}
8 changes: 4 additions & 4 deletions page/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ const pluginManager = {}
Object.defineProperty(pluginManager, 'register', {
value: (plugin: FcitxPlugin) => {
if (typeof plugin.load !== 'function') {
return window.fcitxLog('Plugin must have a load function')
return window.fcitx.fcitxLog('Plugin must have a load function')
}
if (typeof plugin.unload !== 'function') {
return window.fcitxLog('Plugin must have an unload function')
return window.fcitx.fcitxLog('Plugin must have an unload function')
}
unloaders.push(plugin.unload)
plugin.load()
Expand All @@ -16,7 +16,7 @@ Object.defineProperty(pluginManager, 'register', {

function loadPlugins (names: string[]) {
for (const name of names) {
window.fcitxLog(`Loading plugin ${name}`)
window.fcitx.fcitxLog(`Loading plugin ${name}`)
const script = document.createElement('script')
script.src = `fcitx:///file/plugin/${name}/dist/index.js`
script.classList.add('fcitx-plugin')
Expand All @@ -29,7 +29,7 @@ function unloadPlugins () {
try {
unloader()
} catch (e) {
window.fcitxLog(`Error unloading plugin: ${e}`)
window.fcitx.fcitxLog(`Error unloading plugin: ${e}`)
}
}
unloaders.splice(0, unloaders.length)
Expand Down
14 changes: 7 additions & 7 deletions page/scroll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ export function fetchComplete () {
}

export function expand () {
window._scroll(0, (MAX_ROW + 1) * MAX_COLUMN) // visible rows plus 1 hidden row
window.fcitx._scroll(0, (MAX_ROW + 1) * MAX_COLUMN) // visible rows plus 1 hidden row
}

function collapse () {
window._scroll(-1, 0)
window.fcitx._scroll(-1, 0)
}

let rowItemCount: number[] = []
Expand Down Expand Up @@ -204,7 +204,7 @@ export function scrollKeyAction (action: SCROLL_KEY_ACTION) {
if (action > n) {
return
}
return window._select(itemCountInFirstNRows(highlightedRow) + action - 1)
return window.fcitx._select(itemCountInFirstNRows(highlightedRow) + action - 1)
}
switch (action) {
case 10:
Expand All @@ -217,14 +217,14 @@ export function scrollKeyAction (action: SCROLL_KEY_ACTION) {
case 17: {
const newHighlighted = getNeighborCandidate(highlighted, action)
if (newHighlighted >= 0) {
window._highlight(newHighlighted)
window.fcitx._highlight(newHighlighted)
renderHighlightAndLabels(newHighlighted, true)
scrollForHighlight()
if (!scrollEnd && !fetching) {
const newHighlightedRow = getHighlightedRow()
if (rowItemCount.length - newHighlightedRow <= MAX_ROW) {
fetching = true
window._scroll(itemCountInFirstNRows(rowItemCount.length), MAX_ROW * MAX_COLUMN)
window.fcitx._scroll(itemCountInFirstNRows(rowItemCount.length), MAX_ROW * MAX_COLUMN)
}
}
} else if ([10, 16].includes(action) && getHighlightedRow() === 0) {
Expand All @@ -233,7 +233,7 @@ export function scrollKeyAction (action: SCROLL_KEY_ACTION) {
break
}
case 20:
window._select(highlighted)
window.fcitx._select(highlighted)
break
}
}
Expand All @@ -248,6 +248,6 @@ hoverables.addEventListener('scroll', () => {
const bottomRight = candidates[bottomRightIndex]
if (distanceToTop(bottomRight, 'top') < hoverables.clientHeight) {
fetching = true
window._scroll(candidates.length, MAX_ROW * MAX_COLUMN)
window.fcitx._scroll(candidates.length, MAX_ROW * MAX_COLUMN)
}
})
Loading