nandi/jolt-nativepublic Fork 0
5145c3b332ec94862a49f0de18056217f55483d7
Commits
Clone
git clone https://git.rickub.com/nandi/jolt-native.git
git clone ssh://git@rickub.com/nandi/jolt-native.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

jolt_main.c · 245 lines · 9.1 KBC Blame HistoryRaw
Build libvidya for the phone, and hold the glue that boots it d942053 nandi 19d ago1/*
2 * The Jolt half of the Android app: an embedded Chez runtime and the boot image
3 * built from the Jolt sources, reached from `libvidya.so`'s `android_main`.
4 *
5 * This is not the NativeActivity. On Android the activity's library must be the
6 * one holding android-activity's glue, which is `libvidya.so` — see
7 * `crates/jolt-vidya/src/android.rs`. That glue dlopens this library and calls
8 * `vidya_jolt_main`, so the entry point below is an ordinary C function rather
9 * than a `main`.
10 *
Let the media plane cross to the phone, camera and all fd0e21a nandi 18d ago11 * Every foreign symbol is registered with Chez explicitly. Jolt resolves a
Build libvidya for the phone, and hold the glue that boots it d942053 nandi 19d ago12 * `defcfn` through Chez's foreign-entry table, and on Android nothing populates
Let the media plane cross to the phone, camera and all fd0e21a nandi 18d ago13 * that table from the dynamic loader: the symbols live in *different* shared
14 * objects (`libvidya.so` and `libjoltmoq.so`, both DT_NEEDEDs of this one),
15 * which is exactly the case the loader will not answer for. Registering them by
16 * hand is what makes all three ABIs — the push/pop one, the retained tree one
17 * glimmer binds, and the media plane — reachable.
18 *
19 * The media plane needs one thing more than a symbol table. Its camera is
20 * Camera2, reached over JNI, and the handles that takes only arrive in
21 * android-activity's glue inside libvidya. This file is the only place both
22 * objects are in scope, so it is where they are handed across.
Build libvidya for the phone, and hold the glue that boots it d942053 nandi 19d ago23 */
24
25#include <stddef.h>
26#include <stdio.h>
27#include <unistd.h>
28
29#include <android/log.h>
30#include <pthread.h>
31
Let the Android glue be built without the media plane 2bd393e nandi 9d ago32/* JOLT_WITHOUT_MOQ builds this glue for an app that does not link
33 * libjoltmoq. frq is one: its media plane is jolt now — MoQ through
34 * libmoq_ffi, Opus, H.264, V4L2 and ALSA — so the Rust one is an object it
35 * never opens, and seventeen megabytes of APK for nothing. Everything moq
36 * below is behind this, because --no-undefined turns an unreferenced symbol
37 * into a link error rather than a surprise on the phone. */
38#ifndef JOLT_WITHOUT_MOQ
Let the media plane cross to the phone, camera and all fd0e21a nandi 18d ago39#include "joltmoq.h"
Let the Android glue be built without the media plane 2bd393e nandi 9d ago40#endif
Build libvidya for the phone, and hold the glue that boots it d942053 nandi 19d ago41#include "scheme.h"
42#include "vidya.h"
43#include "vidya_tree.h"
44
45/* The boot image, linked in as a binary blob by llvm-objcopy. */
46extern const unsigned char _binary_jolt_boot_start[];
47extern const unsigned char _binary_jolt_boot_end[];
48
49#define REGISTER_VIDYA(name) Sregister_symbol(#name, (void *)&name)
Let the media plane cross to the phone, camera and all fd0e21a nandi 18d ago50#define REGISTER_JOLTMOQ(name) Sregister_symbol(#name, (void *)&name)
Build libvidya for the phone, and hold the glue that boots it d942053 nandi 19d ago51
52static void register_vidya_api(void) {
53 /* vidya.h */
54 REGISTER_VIDYA(vidya_open);
55 REGISTER_VIDYA(vidya_close);
56 REGISTER_VIDYA(vidya_should_close);
57 REGISTER_VIDYA(vidya_set_target_fps);
58 REGISTER_VIDYA(vidya_set_mode);
59 REGISTER_VIDYA(vidya_get_mode);
60 REGISTER_VIDYA(vidya_load_font);
61 REGISTER_VIDYA(vidya_begin_frame);
62 REGISTER_VIDYA(vidya_end_frame);
63 REGISTER_VIDYA(vidya_page_begin);
64 REGISTER_VIDYA(vidya_page_end);
65 REGISTER_VIDYA(vidya_card_begin);
66 REGISTER_VIDYA(vidya_card_end);
67 REGISTER_VIDYA(vidya_gap);
68 REGISTER_VIDYA(vidya_separator);
69 REGISTER_VIDYA(vidya_title);
70 REGISTER_VIDYA(vidya_title_2);
71 REGISTER_VIDYA(vidya_body);
72 REGISTER_VIDYA(vidya_dim_label);
73 REGISTER_VIDYA(vidya_button);
74 REGISTER_VIDYA(vidya_checkbox);
75 REGISTER_VIDYA(vidya_checkbox_value);
76 REGISTER_VIDYA(vidya_status);
77 REGISTER_VIDYA(vidya_text_field);
78
79 /* vidya_tree.h */
80 REGISTER_VIDYA(vidya_tree_root);
81 REGISTER_VIDYA(vidya_node_new);
82 REGISTER_VIDYA(vidya_node_free);
83 REGISTER_VIDYA(vidya_node_exists);
84 REGISTER_VIDYA(vidya_node_set_str);
85 REGISTER_VIDYA(vidya_node_set_num);
86 REGISTER_VIDYA(vidya_node_set_bool);
87 REGISTER_VIDYA(vidya_node_clear_props);
88 REGISTER_VIDYA(vidya_node_get_str);
89 REGISTER_VIDYA(vidya_node_get_num);
90 REGISTER_VIDYA(vidya_node_get_bool);
91 REGISTER_VIDYA(vidya_node_tag);
92 REGISTER_VIDYA(vidya_node_child_count);
93 REGISTER_VIDYA(vidya_node_child_at);
94 REGISTER_VIDYA(vidya_node_append);
95 REGISTER_VIDYA(vidya_node_remove);
96 REGISTER_VIDYA(vidya_node_insert_after);
97 REGISTER_VIDYA(vidya_node_replace);
98 REGISTER_VIDYA(vidya_tree_frame);
99 REGISTER_VIDYA(vidya_tree_poll_event);
100 REGISTER_VIDYA(vidya_tree_event_node);
101 REGISTER_VIDYA(vidya_tree_event_name);
102 REGISTER_VIDYA(vidya_tree_event_text);
103 REGISTER_VIDYA(vidya_tree_event_num);
104
105 /* Neither of these paints anything; they are the platform reached through
106 the same ABI. `vidya_open_url` is the one that matters here — signing in
107 leaves for a browser and comes back. */
108 REGISTER_VIDYA(vidya_clipboard_image_png);
109 REGISTER_VIDYA(vidya_open_url);
110 REGISTER_VIDYA(vidya_pick_image);
111 REGISTER_VIDYA(vidya_picked_image);
112
113 /* Live pixels, the window's own size, and the tree dump. Nothing here is
114 Android-specific; they are on this list because the list is the whole of
115 what an application can reach on Android, and a symbol left off it is not
116 a link error but a call that fails at the moment a screen needs it. */
Register vidya_set_title, which the phone cannot call without it 726f7c6 nandi 18d ago117 REGISTER_VIDYA(vidya_set_title);
Build libvidya for the phone, and hold the glue that boots it d942053 nandi 19d ago118 REGISTER_VIDYA(vidya_frame_rgba);
119 REGISTER_VIDYA(vidya_frame_drop);
120 REGISTER_VIDYA(vidya_screen_width);
121 REGISTER_VIDYA(vidya_screen_height);
122 REGISTER_VIDYA(vidya_tree_dump);
123}
124
Let the Android glue be built without the media plane 2bd393e nandi 9d ago125#ifndef JOLT_WITHOUT_MOQ
Let the media plane cross to the phone, camera and all fd0e21a nandi 18d ago126/*
127 * The media plane's symbols, registered for the same reason the Vidya ones
128 * above are: they live in libjoltmoq.so, a third object, and Android's loader
129 * will not answer a foreign-entry lookup that crosses one.
130 *
131 * A call reaches none of this without it — `joltmoq_start` would be a defcfn
132 * that resolves to nothing at the moment someone dials.
133 */
134static void register_joltmoq_api(void) {
135 REGISTER_JOLTMOQ(joltmoq_init_logging);
136
137 /* Lifecycle. */
138 REGISTER_JOLTMOQ(joltmoq_start);
139 REGISTER_JOLTMOQ(joltmoq_stop);
140 REGISTER_JOLTMOQ(joltmoq_is_live);
141
142 /* In-call controls. */
143 REGISTER_JOLTMOQ(joltmoq_set_muted);
144 REGISTER_JOLTMOQ(joltmoq_set_speaker_muted);
145 REGISTER_JOLTMOQ(joltmoq_set_camera);
146 REGISTER_JOLTMOQ(joltmoq_set_camera_device);
147 REGISTER_JOLTMOQ(joltmoq_set_mic_device);
148 REGISTER_JOLTMOQ(joltmoq_set_speaker_device);
149 REGISTER_JOLTMOQ(joltmoq_mic_level);
150
151 /* Status, drained rather than delivered. */
152 REGISTER_JOLTMOQ(joltmoq_poll_status);
153 REGISTER_JOLTMOQ(joltmoq_status_text);
154 REGISTER_JOLTMOQ(joltmoq_status_has_camera);
155 REGISTER_JOLTMOQ(joltmoq_status_has_mic);
156
157 /* Remote video, borrowed a frame at a time. */
158 REGISTER_JOLTMOQ(joltmoq_frame_poll);
159 REGISTER_JOLTMOQ(joltmoq_frame_key);
160 REGISTER_JOLTMOQ(joltmoq_frame_width);
161 REGISTER_JOLTMOQ(joltmoq_frame_height);
162 REGISTER_JOLTMOQ(joltmoq_frame_rgba);
163 REGISTER_JOLTMOQ(joltmoq_video_keys);
164
165 /* Devices. On the phone the cameras come from Camera2 and the microphone
166 and speaker lists are empty — the platform picks those itself. */
167 REGISTER_JOLTMOQ(joltmoq_cameras);
168 REGISTER_JOLTMOQ(joltmoq_microphones);
169 REGISTER_JOLTMOQ(joltmoq_speakers);
170
171 /* Dialling. */
172 REGISTER_JOLTMOQ(joltmoq_sfu_url);
173 REGISTER_JOLTMOQ(joltmoq_can_dial);
174 REGISTER_JOLTMOQ(joltmoq_new_instance);
175}
Let the Android glue be built without the media plane 2bd393e nandi 9d ago176#endif /* JOLT_WITHOUT_MOQ */
Let the media plane cross to the phone, camera and all fd0e21a nandi 18d ago177
Build libvidya for the phone, and hold the glue that boots it d942053 nandi 19d ago178/*
179 * Chez writes to stdout/stderr, which on Android goes nowhere. Pump both into
180 * logcat so a Scheme-level error is visible rather than a silent exit.
181 */
182static void *log_scheme_output(void *context) {
183 int fd = *(int *)context;
184 char buffer[1024];
185 FILE *stream = fdopen(fd, "r");
186 if (stream == NULL) return NULL;
187
188 while (fgets(buffer, sizeof(buffer), stream) != NULL) {
189 __android_log_write(ANDROID_LOG_ERROR, "VidyaJolt", buffer);
190 }
191 fclose(stream);
192 return NULL;
193}
194
195int vidya_jolt_main(void) {
196 static const char *argv[] = {"vidya", NULL};
197 int output_pipe[2];
198 pthread_t logger;
199
200 if (pipe(output_pipe) == 0) {
201 dup2(output_pipe[1], STDOUT_FILENO);
202 dup2(output_pipe[1], STDERR_FILENO);
203 close(output_pipe[1]);
204 pthread_create(&logger, NULL, log_scheme_output, &output_pipe[0]);
205 pthread_detach(logger);
206 }
207
208 __android_log_print(ANDROID_LOG_INFO, "VidyaJolt",
209 "initializing embedded Chez runtime");
210 Sscheme_init(NULL);
211 Sregister_boot_file_bytes(
212 "jolt",
213 (void *)_binary_jolt_boot_start,
214 (iptr)(_binary_jolt_boot_end - _binary_jolt_boot_start));
215 Sbuild_heap(NULL, NULL);
216 register_vidya_api();
Let the Android glue be built without the media plane 2bd393e nandi 9d ago217#ifndef JOLT_WITHOUT_MOQ
Let the media plane cross to the phone, camera and all fd0e21a nandi 18d ago218 register_joltmoq_api();
Let the Android glue be built without the media plane 2bd393e nandi 9d ago219#endif
Let the media plane cross to the phone, camera and all fd0e21a nandi 18d ago220
Let the Android glue be built without the media plane 2bd393e nandi 9d ago221#ifndef JOLT_WITHOUT_MOQ
Let the media plane cross to the phone, camera and all fd0e21a nandi 18d ago222 /* The media plane's own handles, which it cannot get for itself: they arrive
223 in android-activity's glue, and the glue is in libvidya.so. This is the
224 only place both objects are in scope. Before Scheme starts, so no call can
225 reach the camera ahead of it.
226
227 Logged because there is otherwise no way to see it happen: joltmoq's own
228 logging goes through env_logger, which nothing has initialised this early,
229 so a failure here would be silent until a camera failed to open much later
230 for reasons that would look like anything but this. */
231 void *moq_vm = vidya_android_vm();
232 void *moq_activity = vidya_android_activity();
233 __android_log_print(moq_vm && moq_activity ? ANDROID_LOG_INFO : ANDROID_LOG_ERROR,
234 "VidyaJolt", "media plane JNI handles: vm=%p activity=%p",
235 moq_vm, moq_activity);
236 joltmoq_android_init(moq_vm, moq_activity);
Let the Android glue be built without the media plane 2bd393e nandi 9d ago237#endif
Build libvidya for the phone, and hold the glue that boots it d942053 nandi 19d ago238
239 __android_log_print(ANDROID_LOG_INFO, "VidyaJolt", "starting Jolt application");
240 int status = Sscheme_start(1, argv);
241 __android_log_print(ANDROID_LOG_ERROR, "VidyaJolt",
242 "Jolt application exited with status %d", status);
243 Sscheme_deinit();
244 return status;
245}