Skip to content

Commit c45e16a

Browse files
committed
refactor(jni): pack rime proto marshaling as rime c api
1 parent e34584c commit c45e16a

File tree

4 files changed

+174
-110
lines changed

4 files changed

+174
-110
lines changed
Lines changed: 2 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,14 @@
11
/*
2-
* SPDX-FileCopyrightText: 2024 Rime community
3-
*
2+
* SPDX-FileCopyrightText: 2015 - 2025 Rime community
43
* SPDX-License-Identifier: GPL-3.0-or-later
54
*/
6-
7-
// SPDX-FileCopyrightText: 2015 - 2024 Rime community
8-
//
9-
// SPDX-License-Identifier: GPL-3.0-or-later
10-
11-
#ifndef TRIME_OBJCONV_H
12-
#define TRIME_OBJCONV_H
5+
#pragma once
136

147
#include <rime_api.h>
158

169
#include "helper-types.h"
1710
#include "jni-utils.h"
1811

19-
inline jobject rimeCommitToJObject(JNIEnv *env, const RimeCommit &commit) {
20-
return env->NewObject(GlobalRef->CommitProto, GlobalRef->CommitProtoInit,
21-
*JString(env, commit.text));
22-
}
23-
24-
inline jobject rimeContextToJObject(JNIEnv *env, const RimeContext &context,
25-
std::string_view input, int caretPos) {
26-
jobject composition = env->NewObject(GlobalRef->CompositionProto,
27-
GlobalRef->CompositionProtoDefault);
28-
if (RIME_STRUCT_HAS_MEMBER(context, context.composition)) {
29-
composition = env->NewObject(
30-
GlobalRef->CompositionProto, GlobalRef->CompositionProtoInit,
31-
context.composition.length, context.composition.cursor_pos,
32-
context.composition.sel_start, context.composition.sel_end,
33-
*JString(env, context.composition.preedit),
34-
*JString(env, context.commit_text_preview));
35-
}
36-
37-
jobject menu =
38-
env->NewObject(GlobalRef->MenuProto, GlobalRef->MenuProtoDefault);
39-
if (RIME_STRUCT_HAS_MEMBER(context, context.menu)) {
40-
const auto &src = context.menu;
41-
const auto numCandidates = src.num_candidates;
42-
const auto selectKeysSize = src.select_keys ? strlen(src.select_keys) : 0;
43-
auto destSelectLabels = JRef<jobjectArray>(
44-
env,
45-
env->NewObjectArray(src.num_candidates, GlobalRef->String, nullptr));
46-
auto destCandidates = JRef<jobjectArray>(
47-
env, env->NewObjectArray(src.num_candidates, GlobalRef->CandidateProto,
48-
nullptr));
49-
for (int i = 0; i < numCandidates; ++i) {
50-
std::string label;
51-
if (i < src.page_size && RIME_PROVIDED(&context, select_labels)) {
52-
label = context.select_labels[i];
53-
} else if (i < selectKeysSize) {
54-
label = std::string(1, src.select_keys[i]);
55-
} else {
56-
label = std::to_string((i + 1) % 10);
57-
}
58-
label.append(" ");
59-
env->SetObjectArrayElement(destSelectLabels, i, JString(env, label));
60-
const auto &item = src.candidates[i];
61-
auto candidate = JRef<>(env, env->NewObject(GlobalRef->CandidateProto,
62-
GlobalRef->CandidateProtoInit,
63-
*JString(env, item.text),
64-
*JString(env, item.comment),
65-
*JString(env, label)));
66-
env->SetObjectArrayElement(destCandidates, i, candidate);
67-
}
68-
menu = env->NewObject(GlobalRef->MenuProto, GlobalRef->MenuProtoInit,
69-
src.page_size, src.page_no, src.is_last_page,
70-
src.highlighted_candidate_index, *destCandidates,
71-
*JString(env, src.select_keys), *destSelectLabels);
72-
}
73-
74-
return env->NewObject(GlobalRef->ContextProto, GlobalRef->ContextProtoInit,
75-
*JRef<>(env, composition), *JRef<>(env, menu),
76-
*JString(env, input.data()), caretPos);
77-
}
78-
79-
inline jobject rimeStatusToJObject(JNIEnv *env, const RimeStatus &status) {
80-
return env->NewObject(GlobalRef->StatusProto, GlobalRef->StatusProtoInit,
81-
*JString(env, status.schema_id),
82-
*JString(env, status.schema_name), status.is_disabled,
83-
status.is_composing, status.is_ascii_mode,
84-
status.is_full_shape, status.is_simplified,
85-
status.is_traditional, status.is_ascii_punct);
86-
}
87-
8812
inline jobject rimeSchemaListItemToJObject(JNIEnv *env,
8913
const SchemaItem &item) {
9014
return env->NewObject(GlobalRef->SchemaListItem,
@@ -134,5 +58,3 @@ inline jobjectArray rimeCandidateListToJObjectArray(
13458
}
13559
return array;
13660
}
137-
138-
#endif // TRIME_OBJCONV_H

app/src/main/jni/librime_jni/proto.cc

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#include "proto.h"
2+
3+
#include <rime/component.h>
4+
#include <rime/composition.h>
5+
#include <rime/context.h>
6+
#include <rime/menu.h>
7+
#include <rime/schema.h>
8+
#include <rime/service.h>
9+
#include <rime_api.h>
10+
11+
#include "jni-utils.h"
12+
13+
using namespace rime;
14+
15+
RIME_PROTO_OBJ rime_commit_proto(RimeSessionId session_id) {
16+
an<Session> session(Service::instance().GetSession(session_id));
17+
if (!session) return nullptr;
18+
auto env = GlobalRef->AttachEnv();
19+
const string &commit_text(session->commit_text());
20+
RIME_PROTO_OBJ commit =
21+
env->NewObject(GlobalRef->CommitProto, GlobalRef->CommitProtoInit,
22+
*JString(env, commit_text));
23+
session->ResetCommitText();
24+
return commit;
25+
}
26+
27+
RIME_PROTO_OBJ rime_context_proto(RimeSessionId session_id) {
28+
an<Session> session = Service::instance().GetSession(session_id);
29+
if (!session) return nullptr;
30+
Context *ctx = session->context();
31+
if (!ctx) return nullptr;
32+
auto env = GlobalRef->AttachEnv();
33+
jobject composition = env->NewObject(GlobalRef->CompositionProto,
34+
GlobalRef->CompositionProtoDefault);
35+
if (ctx->IsComposing()) {
36+
const Preedit &preedit = ctx->GetPreedit();
37+
composition = env->NewObject(
38+
GlobalRef->CompositionProto, GlobalRef->CompositionProtoInit,
39+
preedit.text.length(), preedit.caret_pos, preedit.sel_start,
40+
preedit.sel_end, *JString(env, preedit.text),
41+
*JString(env, ctx->GetCommitText()));
42+
}
43+
jobject menu =
44+
env->NewObject(GlobalRef->MenuProto, GlobalRef->MenuProtoDefault);
45+
if (ctx->HasMenu()) {
46+
Segment &seg = ctx->composition().back();
47+
Schema *schema = session->schema();
48+
int page_size = schema ? schema->page_size() : 5;
49+
int selected_index = seg.selected_index;
50+
int page_number = selected_index / page_size;
51+
int highlighted_index = selected_index % page_size;
52+
const string &select_keys = schema ? schema->select_keys() : "";
53+
the<Page> page(seg.menu->CreatePage(page_size, page_number));
54+
if (page) {
55+
vector<string> labels;
56+
auto dest_labels =
57+
env->NewObjectArray(page_size, GlobalRef->String, nullptr);
58+
if (schema) {
59+
Config *config = schema->config();
60+
auto src_labels = config->GetList("menu/alternative_select_labels");
61+
if (src_labels && (size_t)page_size <= src_labels->size()) {
62+
for (int i = 0; i < page_size; ++i) {
63+
if (an<ConfigValue> value = src_labels->GetValueAt(i)) {
64+
env->SetObjectArrayElement(dest_labels, i,
65+
*JString(env, value->str()));
66+
labels.emplace_back(value->str());
67+
}
68+
}
69+
} else if (!select_keys.empty()) {
70+
for (const char key : select_keys) {
71+
labels.emplace_back(1, key);
72+
if (labels.size() >= page_size) break;
73+
}
74+
}
75+
}
76+
int num_candidates = page->candidates.size();
77+
auto dest_candidates = env->NewObjectArray(
78+
num_candidates, GlobalRef->CandidateProto, nullptr);
79+
int index = 0;
80+
for (const an<Candidate> &src : page->candidates) {
81+
const string &label =
82+
index < labels.size() ? labels[index] : std::to_string(index + 1);
83+
auto dest = JRef(env, env->NewObject(GlobalRef->CandidateProto,
84+
GlobalRef->CandidateProtoInit,
85+
*JString(env, src->text()),
86+
*JString(env, src->comment()),
87+
*JString(env, label)));
88+
env->SetObjectArrayElement(dest_candidates, index++, *dest);
89+
}
90+
menu = env->NewObject(
91+
GlobalRef->MenuProto, GlobalRef->MenuProtoInit, page_size,
92+
page_number, page->is_last_page, highlighted_index,
93+
*JRef<jobjectArray>(env, dest_candidates), *JString(env, select_keys),
94+
*JRef<jobjectArray>(env, dest_labels));
95+
}
96+
}
97+
return env->NewObject(GlobalRef->ContextProto, GlobalRef->ContextProtoInit,
98+
*JRef(env, composition), *JRef(env, menu),
99+
*JString(env, ctx->input()), ctx->caret_pos());
100+
}
101+
102+
RIME_PROTO_OBJ rime_status_proto(RimeSessionId session_id) {
103+
an<Session> session(Service::instance().GetSession(session_id));
104+
if (!session) return nullptr;
105+
Schema *schema = session->schema();
106+
Context *ctx = session->context();
107+
if (!schema || !ctx) return nullptr;
108+
auto env = GlobalRef->AttachEnv();
109+
return env->NewObject(
110+
GlobalRef->StatusProto, GlobalRef->StatusProtoInit,
111+
*JString(env, schema->schema_id()), *JString(env, schema->schema_name()),
112+
Service::instance().disabled(), ctx->IsComposing(),
113+
ctx->get_option("ascii_mode"), ctx->get_option("full_shape"),
114+
ctx->get_option("simplification"), ctx->get_option("traditional"),
115+
ctx->get_option("ascii_punct"));
116+
}
117+
118+
static void rime_proto_initialize() {}
119+
120+
static void rime_proto_finalize() {}
121+
122+
static RimeCustomApi *rime_proto_get_api() {
123+
static RimeProtoApi s_api = {0};
124+
if (!s_api.data_size) {
125+
RIME_STRUCT_INIT(RimeProtoApi, s_api);
126+
s_api.commit_proto = &rime_commit_proto;
127+
s_api.context_proto = &rime_context_proto;
128+
s_api.status_proto = &rime_status_proto;
129+
}
130+
return (RimeCustomApi *)&s_api;
131+
}
132+
133+
RIME_REGISTER_CUSTOM_MODULE(proto) { module->get_api = &rime_proto_get_api; }

app/src/main/jni/librime_jni/proto.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#pragma once
2+
3+
#include <jni.h>
4+
#include <rime_api.h>
5+
6+
#ifdef __cplusplus
7+
extern "C" {
8+
#endif
9+
10+
//! For passing pointer to jni object as opaque pointer through C API.
11+
#define RIME_PROTO_OBJ jobject
12+
13+
typedef struct rime_proto_api_t {
14+
int data_size;
15+
16+
RIME_PROTO_OBJ (*commit_proto)(RimeSessionId session_id);
17+
RIME_PROTO_OBJ (*context_proto)(RimeSessionId session_id);
18+
RIME_PROTO_OBJ (*status_proto)(RimeSessionId session_id);
19+
} RimeProtoApi;
20+
21+
#ifdef __cplusplus
22+
}
23+
#endif

app/src/main/jni/librime_jni/rime_jni.cc

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,27 @@
99

1010
#include "jni-utils.h"
1111
#include "objconv.h"
12+
#include "proto.h"
1213

1314
#define MAX_BUFFER_LENGTH 2048
1415

1516
extern void rime_require_module_lua();
1617
extern void rime_require_module_octagram();
1718
extern void rime_require_module_predict();
19+
extern void rime_require_module_proto();
1820
// librime is compiled as a static library, we have to link modules explicitly
1921
static void declare_librime_module_dependencies() {
2022
rime_require_module_lua();
2123
rime_require_module_octagram();
2224
rime_require_module_predict();
25+
rime_require_module_proto();
2326
}
2427

2528
class Rime {
2629
public:
27-
Rime() : rime(rime_get_api()) {}
30+
Rime() : rime(rime_get_api()) {
31+
proto = (RimeProtoApi *)rime->find_module("proto")->get_api();
32+
}
2833
Rime(Rime const &) = delete;
2934
void operator=(Rime const &) = delete;
3035

@@ -77,6 +82,12 @@ class Rime {
7782

7883
void clearComposition() { rime->clear_composition(session); }
7984

85+
RIME_PROTO_OBJ commitProto() { return proto->commit_proto(session); }
86+
87+
RIME_PROTO_OBJ contextProto() { return proto->context_proto(session); }
88+
89+
RIME_PROTO_OBJ statusProto() { return proto->status_proto(session); }
90+
8091
void setOption(const std::string &key, bool value) {
8192
rime->set_option(session, key.c_str(), value);
8293
}
@@ -158,10 +169,9 @@ class Rime {
158169

159170
bool sync() { return rime->sync_user_data(); }
160171

161-
[[nodiscard]] RimeSessionId sessionId() const { return session; }
162-
163172
private:
164173
RimeApi *rime;
174+
RimeProtoApi *proto;
165175
RimeSessionId session = 0;
166176

167177
bool firstRun = true;
@@ -256,41 +266,17 @@ Java_com_osfans_trime_core_Rime_clearRimeComposition(JNIEnv *env,
256266
// output
257267
extern "C" JNIEXPORT jobject JNICALL
258268
Java_com_osfans_trime_core_Rime_getRimeCommit(JNIEnv *env, jclass /* thiz */) {
259-
RIME_STRUCT(RimeCommit, commit)
260-
auto rime = rime_get_api();
261-
jobject obj = nullptr;
262-
if (rime->get_commit(Rime::Instance().sessionId(), &commit)) {
263-
obj = rimeCommitToJObject(env, commit);
264-
rime->free_commit(&commit);
265-
}
266-
return obj;
269+
return Rime::Instance().commitProto();
267270
}
268271

269272
extern "C" JNIEXPORT jobject JNICALL
270273
Java_com_osfans_trime_core_Rime_getRimeContext(JNIEnv *env, jclass /* thiz */) {
271-
RIME_STRUCT(RimeContext, context)
272-
auto rime = rime_get_api();
273-
auto session = Rime::Instance().sessionId();
274-
jobject obj = nullptr;
275-
if (rime->get_context(session, &context)) {
276-
std::string_view input(rime->get_input(session));
277-
int caretPos = static_cast<int>(rime->get_caret_pos(session));
278-
obj = rimeContextToJObject(env, context, input, caretPos);
279-
rime->free_context(&context);
280-
}
281-
return obj;
274+
return Rime::Instance().contextProto();
282275
}
283276

284277
extern "C" JNIEXPORT jobject JNICALL
285278
Java_com_osfans_trime_core_Rime_getRimeStatus(JNIEnv *env, jclass /* thiz */) {
286-
RIME_STRUCT(RimeStatus, status)
287-
auto rime = rime_get_api();
288-
jobject obj = nullptr;
289-
if (rime->get_status(Rime::Instance().sessionId(), &status)) {
290-
obj = rimeStatusToJObject(env, status);
291-
rime->free_status(&status);
292-
}
293-
return obj;
279+
return Rime::Instance().statusProto();
294280
}
295281

296282
// runtime options

0 commit comments

Comments
 (0)