1#![allow(clippy::too_many_arguments)]
2#![allow(clippy::doc_overindented_list_items)]
3
4pub mod draw;
131pub mod proto;
132
133#[link(wasm_import_module = "oxide")]
136extern "C" {
137 #[link_name = "api_log"]
138 fn _api_log(ptr: u32, len: u32);
139
140 #[link_name = "api_warn"]
141 fn _api_warn(ptr: u32, len: u32);
142
143 #[link_name = "api_error"]
144 fn _api_error(ptr: u32, len: u32);
145
146 #[link_name = "api_get_location"]
147 fn _api_get_location(out_ptr: u32, out_cap: u32) -> u32;
148
149 #[link_name = "api_upload_file"]
150 fn _api_upload_file(name_ptr: u32, name_cap: u32, data_ptr: u32, data_cap: u32) -> u64;
151
152 #[link_name = "api_file_pick"]
153 fn _api_file_pick(
154 title_ptr: u32,
155 title_len: u32,
156 filters_ptr: u32,
157 filters_len: u32,
158 multiple: u32,
159 out_ptr: u32,
160 out_cap: u32,
161 ) -> i32;
162
163 #[link_name = "api_folder_pick"]
164 fn _api_folder_pick(title_ptr: u32, title_len: u32) -> u32;
165
166 #[link_name = "api_folder_entries"]
167 fn _api_folder_entries(handle: u32, out_ptr: u32, out_cap: u32) -> i32;
168
169 #[link_name = "api_file_read"]
170 fn _api_file_read(handle: u32, out_ptr: u32, out_cap: u32) -> i64;
171
172 #[link_name = "api_file_read_range"]
173 fn _api_file_read_range(
174 handle: u32,
175 offset_lo: u32,
176 offset_hi: u32,
177 len: u32,
178 out_ptr: u32,
179 out_cap: u32,
180 ) -> i64;
181
182 #[link_name = "api_file_metadata"]
183 fn _api_file_metadata(handle: u32, out_ptr: u32, out_cap: u32) -> i32;
184
185 #[link_name = "api_canvas_clear"]
186 fn _api_canvas_clear(r: u32, g: u32, b: u32, a: u32);
187
188 #[link_name = "api_canvas_rect"]
189 fn _api_canvas_rect(x: f32, y: f32, w: f32, h: f32, r: u32, g: u32, b: u32, a: u32);
190
191 #[link_name = "api_canvas_circle"]
192 fn _api_canvas_circle(cx: f32, cy: f32, radius: f32, r: u32, g: u32, b: u32, a: u32);
193
194 #[link_name = "api_canvas_text"]
195 fn _api_canvas_text(
196 x: f32,
197 y: f32,
198 size: f32,
199 r: u32,
200 g: u32,
201 b: u32,
202 a: u32,
203 ptr: u32,
204 len: u32,
205 );
206
207 #[link_name = "api_canvas_text_ex"]
208 #[allow(clippy::too_many_arguments)]
209 fn _api_canvas_text_ex(
210 x: f32,
211 y: f32,
212 size: f32,
213 r: u32,
214 g: u32,
215 b: u32,
216 a: u32,
217 family_ptr: u32,
218 family_len: u32,
219 weight: u32,
220 style: u32,
221 align: u32,
222 text_ptr: u32,
223 text_len: u32,
224 );
225
226 #[link_name = "api_canvas_measure_text"]
227 fn _api_canvas_measure_text(
228 size: f32,
229 family_ptr: u32,
230 family_len: u32,
231 weight: u32,
232 style: u32,
233 text_ptr: u32,
234 text_len: u32,
235 out_ptr: u32,
236 ) -> u32;
237
238 #[link_name = "api_canvas_line"]
239 fn _api_canvas_line(
240 x1: f32,
241 y1: f32,
242 x2: f32,
243 y2: f32,
244 r: u32,
245 g: u32,
246 b: u32,
247 a: u32,
248 thickness: f32,
249 );
250
251 #[link_name = "api_canvas_dimensions"]
252 fn _api_canvas_dimensions() -> u64;
253
254 #[link_name = "api_canvas_image"]
255 fn _api_canvas_image(x: f32, y: f32, w: f32, h: f32, data_ptr: u32, data_len: u32);
256
257 #[link_name = "api_canvas_rounded_rect"]
260 fn _api_canvas_rounded_rect(
261 x: f32,
262 y: f32,
263 w: f32,
264 h: f32,
265 radius: f32,
266 r: u32,
267 g: u32,
268 b: u32,
269 a: u32,
270 );
271
272 #[link_name = "api_canvas_arc"]
273 fn _api_canvas_arc(
274 cx: f32,
275 cy: f32,
276 radius: f32,
277 start_angle: f32,
278 end_angle: f32,
279 r: u32,
280 g: u32,
281 b: u32,
282 a: u32,
283 thickness: f32,
284 );
285
286 #[link_name = "api_canvas_bezier"]
287 fn _api_canvas_bezier(
288 x1: f32,
289 y1: f32,
290 cp1x: f32,
291 cp1y: f32,
292 cp2x: f32,
293 cp2y: f32,
294 x2: f32,
295 y2: f32,
296 r: u32,
297 g: u32,
298 b: u32,
299 a: u32,
300 thickness: f32,
301 );
302
303 #[link_name = "api_canvas_gradient"]
304 fn _api_canvas_gradient(
305 x: f32,
306 y: f32,
307 w: f32,
308 h: f32,
309 kind: u32,
310 ax: f32,
311 ay: f32,
312 bx: f32,
313 by: f32,
314 stops_ptr: u32,
315 stops_len: u32,
316 );
317
318 #[link_name = "api_canvas_save"]
321 fn _api_canvas_save();
322
323 #[link_name = "api_canvas_restore"]
324 fn _api_canvas_restore();
325
326 #[link_name = "api_canvas_transform"]
327 fn _api_canvas_transform(a: f32, b: f32, c: f32, d: f32, tx: f32, ty: f32);
328
329 #[link_name = "api_canvas_clip"]
330 fn _api_canvas_clip(x: f32, y: f32, w: f32, h: f32);
331
332 #[link_name = "api_canvas_opacity"]
333 fn _api_canvas_opacity(alpha: f32);
334
335 #[link_name = "api_storage_set"]
336 fn _api_storage_set(key_ptr: u32, key_len: u32, val_ptr: u32, val_len: u32);
337
338 #[link_name = "api_storage_get"]
339 fn _api_storage_get(key_ptr: u32, key_len: u32, out_ptr: u32, out_cap: u32) -> u32;
340
341 #[link_name = "api_storage_remove"]
342 fn _api_storage_remove(key_ptr: u32, key_len: u32);
343
344 #[link_name = "api_clipboard_write"]
345 fn _api_clipboard_write(ptr: u32, len: u32);
346
347 #[link_name = "api_clipboard_read"]
348 fn _api_clipboard_read(out_ptr: u32, out_cap: u32) -> u32;
349
350 #[link_name = "api_time_now_ms"]
351 fn _api_time_now_ms() -> u64;
352
353 #[link_name = "api_set_timeout"]
354 fn _api_set_timeout(callback_id: u32, delay_ms: u32) -> u32;
355
356 #[link_name = "api_set_interval"]
357 fn _api_set_interval(callback_id: u32, interval_ms: u32) -> u32;
358
359 #[link_name = "api_clear_timer"]
360 fn _api_clear_timer(timer_id: u32);
361
362 #[link_name = "api_request_animation_frame"]
363 fn _api_request_animation_frame(callback_id: u32) -> u32;
364
365 #[link_name = "api_cancel_animation_frame"]
366 fn _api_cancel_animation_frame(request_id: u32);
367
368 #[link_name = "api_on_event"]
369 fn _api_on_event(type_ptr: u32, type_len: u32, callback_id: u32) -> u32;
370
371 #[link_name = "api_off_event"]
372 fn _api_off_event(listener_id: u32) -> u32;
373
374 #[link_name = "api_emit_event"]
375 fn _api_emit_event(type_ptr: u32, type_len: u32, data_ptr: u32, data_len: u32);
376
377 #[link_name = "api_event_type_len"]
378 fn _api_event_type_len() -> u32;
379
380 #[link_name = "api_event_type_read"]
381 fn _api_event_type_read(out_ptr: u32, out_cap: u32) -> u32;
382
383 #[link_name = "api_event_data_len"]
384 fn _api_event_data_len() -> u32;
385
386 #[link_name = "api_event_data_read"]
387 fn _api_event_data_read(out_ptr: u32, out_cap: u32) -> u32;
388
389 #[link_name = "api_random"]
390 fn _api_random() -> u64;
391
392 #[link_name = "api_notify"]
393 fn _api_notify(title_ptr: u32, title_len: u32, body_ptr: u32, body_len: u32);
394
395 #[link_name = "api_fetch"]
396 fn _api_fetch(
397 method_ptr: u32,
398 method_len: u32,
399 url_ptr: u32,
400 url_len: u32,
401 ct_ptr: u32,
402 ct_len: u32,
403 body_ptr: u32,
404 body_len: u32,
405 out_ptr: u32,
406 out_cap: u32,
407 ) -> i64;
408
409 #[link_name = "api_fetch_begin"]
410 fn _api_fetch_begin(
411 method_ptr: u32,
412 method_len: u32,
413 url_ptr: u32,
414 url_len: u32,
415 ct_ptr: u32,
416 ct_len: u32,
417 body_ptr: u32,
418 body_len: u32,
419 ) -> u32;
420
421 #[link_name = "api_fetch_state"]
422 fn _api_fetch_state(id: u32) -> u32;
423
424 #[link_name = "api_fetch_status"]
425 fn _api_fetch_status(id: u32) -> u32;
426
427 #[link_name = "api_fetch_recv"]
428 fn _api_fetch_recv(id: u32, out_ptr: u32, out_cap: u32) -> i64;
429
430 #[link_name = "api_fetch_error"]
431 fn _api_fetch_error(id: u32, out_ptr: u32, out_cap: u32) -> i32;
432
433 #[link_name = "api_fetch_abort"]
434 fn _api_fetch_abort(id: u32) -> i32;
435
436 #[link_name = "api_fetch_remove"]
437 fn _api_fetch_remove(id: u32);
438
439 #[link_name = "api_load_module"]
440 fn _api_load_module(url_ptr: u32, url_len: u32) -> i32;
441
442 #[link_name = "api_hash_sha256"]
443 fn _api_hash_sha256(data_ptr: u32, data_len: u32, out_ptr: u32) -> u32;
444
445 #[link_name = "api_base64_encode"]
446 fn _api_base64_encode(data_ptr: u32, data_len: u32, out_ptr: u32, out_cap: u32) -> u32;
447
448 #[link_name = "api_base64_decode"]
449 fn _api_base64_decode(data_ptr: u32, data_len: u32, out_ptr: u32, out_cap: u32) -> u32;
450
451 #[link_name = "api_kv_store_set"]
452 fn _api_kv_store_set(key_ptr: u32, key_len: u32, val_ptr: u32, val_len: u32) -> i32;
453
454 #[link_name = "api_kv_store_get"]
455 fn _api_kv_store_get(key_ptr: u32, key_len: u32, out_ptr: u32, out_cap: u32) -> i32;
456
457 #[link_name = "api_kv_store_delete"]
458 fn _api_kv_store_delete(key_ptr: u32, key_len: u32) -> i32;
459
460 #[link_name = "api_navigate"]
463 fn _api_navigate(url_ptr: u32, url_len: u32) -> i32;
464
465 #[link_name = "api_push_state"]
466 fn _api_push_state(
467 state_ptr: u32,
468 state_len: u32,
469 title_ptr: u32,
470 title_len: u32,
471 url_ptr: u32,
472 url_len: u32,
473 );
474
475 #[link_name = "api_replace_state"]
476 fn _api_replace_state(
477 state_ptr: u32,
478 state_len: u32,
479 title_ptr: u32,
480 title_len: u32,
481 url_ptr: u32,
482 url_len: u32,
483 );
484
485 #[link_name = "api_get_url"]
486 fn _api_get_url(out_ptr: u32, out_cap: u32) -> u32;
487
488 #[link_name = "api_get_state"]
489 fn _api_get_state(out_ptr: u32, out_cap: u32) -> i32;
490
491 #[link_name = "api_history_length"]
492 fn _api_history_length() -> u32;
493
494 #[link_name = "api_history_back"]
495 fn _api_history_back() -> i32;
496
497 #[link_name = "api_history_forward"]
498 fn _api_history_forward() -> i32;
499
500 #[link_name = "api_register_hyperlink"]
503 fn _api_register_hyperlink(x: f32, y: f32, w: f32, h: f32, url_ptr: u32, url_len: u32) -> i32;
504
505 #[link_name = "api_clear_hyperlinks"]
506 fn _api_clear_hyperlinks();
507
508 #[link_name = "api_mouse_position"]
511 fn _api_mouse_position() -> u64;
512
513 #[link_name = "api_mouse_button_down"]
514 fn _api_mouse_button_down(button: u32) -> u32;
515
516 #[link_name = "api_mouse_button_clicked"]
517 fn _api_mouse_button_clicked(button: u32) -> u32;
518
519 #[link_name = "api_key_down"]
520 fn _api_key_down(key: u32) -> u32;
521
522 #[link_name = "api_key_pressed"]
523 fn _api_key_pressed(key: u32) -> u32;
524
525 #[link_name = "api_scroll_delta"]
526 fn _api_scroll_delta() -> u64;
527
528 #[link_name = "api_modifiers"]
529 fn _api_modifiers() -> u32;
530
531 #[link_name = "api_ui_button"]
534 fn _api_ui_button(
535 id: u32,
536 x: f32,
537 y: f32,
538 w: f32,
539 h: f32,
540 label_ptr: u32,
541 label_len: u32,
542 ) -> u32;
543
544 #[link_name = "api_ui_checkbox"]
545 fn _api_ui_checkbox(
546 id: u32,
547 x: f32,
548 y: f32,
549 label_ptr: u32,
550 label_len: u32,
551 initial: u32,
552 ) -> u32;
553
554 #[link_name = "api_ui_slider"]
555 fn _api_ui_slider(id: u32, x: f32, y: f32, w: f32, min: f32, max: f32, initial: f32) -> f32;
556
557 #[link_name = "api_ui_text_input"]
558 fn _api_ui_text_input(
559 id: u32,
560 x: f32,
561 y: f32,
562 w: f32,
563 init_ptr: u32,
564 init_len: u32,
565 out_ptr: u32,
566 out_cap: u32,
567 ) -> u32;
568
569 #[link_name = "api_audio_play"]
572 fn _api_audio_play(data_ptr: u32, data_len: u32) -> i32;
573
574 #[link_name = "api_audio_play_url"]
575 fn _api_audio_play_url(url_ptr: u32, url_len: u32) -> i32;
576
577 #[link_name = "api_audio_detect_format"]
578 fn _api_audio_detect_format(data_ptr: u32, data_len: u32) -> u32;
579
580 #[link_name = "api_audio_play_with_format"]
581 fn _api_audio_play_with_format(data_ptr: u32, data_len: u32, format_hint: u32) -> i32;
582
583 #[link_name = "api_audio_last_url_content_type"]
584 fn _api_audio_last_url_content_type(out_ptr: u32, out_cap: u32) -> u32;
585
586 #[link_name = "api_audio_pause"]
587 fn _api_audio_pause();
588
589 #[link_name = "api_audio_resume"]
590 fn _api_audio_resume();
591
592 #[link_name = "api_audio_stop"]
593 fn _api_audio_stop();
594
595 #[link_name = "api_audio_set_volume"]
596 fn _api_audio_set_volume(level: f32);
597
598 #[link_name = "api_audio_get_volume"]
599 fn _api_audio_get_volume() -> f32;
600
601 #[link_name = "api_audio_is_playing"]
602 fn _api_audio_is_playing() -> u32;
603
604 #[link_name = "api_audio_position"]
605 fn _api_audio_position() -> u64;
606
607 #[link_name = "api_audio_seek"]
608 fn _api_audio_seek(position_ms: u64) -> i32;
609
610 #[link_name = "api_audio_duration"]
611 fn _api_audio_duration() -> u64;
612
613 #[link_name = "api_audio_set_loop"]
614 fn _api_audio_set_loop(enabled: u32);
615
616 #[link_name = "api_audio_channel_play"]
617 fn _api_audio_channel_play(channel: u32, data_ptr: u32, data_len: u32) -> i32;
618
619 #[link_name = "api_audio_channel_play_with_format"]
620 fn _api_audio_channel_play_with_format(
621 channel: u32,
622 data_ptr: u32,
623 data_len: u32,
624 format_hint: u32,
625 ) -> i32;
626
627 #[link_name = "api_audio_channel_stop"]
628 fn _api_audio_channel_stop(channel: u32);
629
630 #[link_name = "api_audio_channel_set_volume"]
631 fn _api_audio_channel_set_volume(channel: u32, level: f32);
632
633 #[link_name = "api_video_detect_format"]
636 fn _api_video_detect_format(data_ptr: u32, data_len: u32) -> u32;
637
638 #[link_name = "api_video_load"]
639 fn _api_video_load(data_ptr: u32, data_len: u32, format_hint: u32) -> i32;
640
641 #[link_name = "api_video_load_url"]
642 fn _api_video_load_url(url_ptr: u32, url_len: u32) -> i32;
643
644 #[link_name = "api_video_last_url_content_type"]
645 fn _api_video_last_url_content_type(out_ptr: u32, out_cap: u32) -> u32;
646
647 #[link_name = "api_video_hls_variant_count"]
648 fn _api_video_hls_variant_count() -> u32;
649
650 #[link_name = "api_video_hls_variant_url"]
651 fn _api_video_hls_variant_url(index: u32, out_ptr: u32, out_cap: u32) -> u32;
652
653 #[link_name = "api_video_hls_open_variant"]
654 fn _api_video_hls_open_variant(index: u32) -> i32;
655
656 #[link_name = "api_video_play"]
657 fn _api_video_play();
658
659 #[link_name = "api_video_pause"]
660 fn _api_video_pause();
661
662 #[link_name = "api_video_stop"]
663 fn _api_video_stop();
664
665 #[link_name = "api_video_seek"]
666 fn _api_video_seek(position_ms: u64) -> i32;
667
668 #[link_name = "api_video_position"]
669 fn _api_video_position() -> u64;
670
671 #[link_name = "api_video_duration"]
672 fn _api_video_duration() -> u64;
673
674 #[link_name = "api_video_render"]
675 fn _api_video_render(x: f32, y: f32, w: f32, h: f32) -> i32;
676
677 #[link_name = "api_video_set_volume"]
678 fn _api_video_set_volume(level: f32);
679
680 #[link_name = "api_video_get_volume"]
681 fn _api_video_get_volume() -> f32;
682
683 #[link_name = "api_video_set_loop"]
684 fn _api_video_set_loop(enabled: u32);
685
686 #[link_name = "api_video_set_pip"]
687 fn _api_video_set_pip(enabled: u32);
688
689 #[link_name = "api_subtitle_load_srt"]
690 fn _api_subtitle_load_srt(ptr: u32, len: u32) -> i32;
691
692 #[link_name = "api_subtitle_load_vtt"]
693 fn _api_subtitle_load_vtt(ptr: u32, len: u32) -> i32;
694
695 #[link_name = "api_subtitle_clear"]
696 fn _api_subtitle_clear();
697
698 #[link_name = "api_camera_open"]
701 fn _api_camera_open() -> i32;
702
703 #[link_name = "api_camera_close"]
704 fn _api_camera_close();
705
706 #[link_name = "api_camera_capture_frame"]
707 fn _api_camera_capture_frame(out_ptr: u32, out_cap: u32) -> u32;
708
709 #[link_name = "api_camera_frame_dimensions"]
710 fn _api_camera_frame_dimensions() -> u64;
711
712 #[link_name = "api_microphone_open"]
713 fn _api_microphone_open() -> i32;
714
715 #[link_name = "api_microphone_close"]
716 fn _api_microphone_close();
717
718 #[link_name = "api_microphone_sample_rate"]
719 fn _api_microphone_sample_rate() -> u32;
720
721 #[link_name = "api_microphone_read_samples"]
722 fn _api_microphone_read_samples(out_ptr: u32, max_samples: u32) -> u32;
723
724 #[link_name = "api_screen_capture"]
725 fn _api_screen_capture(out_ptr: u32, out_cap: u32) -> i32;
726
727 #[link_name = "api_screen_capture_dimensions"]
728 fn _api_screen_capture_dimensions() -> u64;
729
730 #[link_name = "api_media_pipeline_stats"]
731 fn _api_media_pipeline_stats() -> u64;
732
733 #[link_name = "api_gpu_create_buffer"]
736 fn _api_gpu_create_buffer(size_lo: u32, size_hi: u32, usage: u32) -> u32;
737
738 #[link_name = "api_gpu_create_texture"]
739 fn _api_gpu_create_texture(width: u32, height: u32) -> u32;
740
741 #[link_name = "api_gpu_create_shader"]
742 fn _api_gpu_create_shader(src_ptr: u32, src_len: u32) -> u32;
743
744 #[link_name = "api_gpu_create_render_pipeline"]
745 fn _api_gpu_create_render_pipeline(
746 shader: u32,
747 vs_ptr: u32,
748 vs_len: u32,
749 fs_ptr: u32,
750 fs_len: u32,
751 ) -> u32;
752
753 #[link_name = "api_gpu_create_compute_pipeline"]
754 fn _api_gpu_create_compute_pipeline(shader: u32, ep_ptr: u32, ep_len: u32) -> u32;
755
756 #[link_name = "api_gpu_write_buffer"]
757 fn _api_gpu_write_buffer(
758 handle: u32,
759 offset_lo: u32,
760 offset_hi: u32,
761 data_ptr: u32,
762 data_len: u32,
763 ) -> u32;
764
765 #[link_name = "api_gpu_draw"]
766 fn _api_gpu_draw(pipeline: u32, target: u32, vertex_count: u32, instance_count: u32) -> u32;
767
768 #[link_name = "api_gpu_dispatch_compute"]
769 fn _api_gpu_dispatch_compute(pipeline: u32, x: u32, y: u32, z: u32) -> u32;
770
771 #[link_name = "api_gpu_destroy_buffer"]
772 fn _api_gpu_destroy_buffer(handle: u32) -> u32;
773
774 #[link_name = "api_gpu_destroy_texture"]
775 fn _api_gpu_destroy_texture(handle: u32) -> u32;
776
777 #[link_name = "api_rtc_create_peer"]
780 fn _api_rtc_create_peer(stun_ptr: u32, stun_len: u32) -> u32;
781
782 #[link_name = "api_rtc_close_peer"]
783 fn _api_rtc_close_peer(peer_id: u32) -> u32;
784
785 #[link_name = "api_rtc_create_offer"]
786 fn _api_rtc_create_offer(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
787
788 #[link_name = "api_rtc_create_answer"]
789 fn _api_rtc_create_answer(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
790
791 #[link_name = "api_rtc_set_local_description"]
792 fn _api_rtc_set_local_description(
793 peer_id: u32,
794 sdp_ptr: u32,
795 sdp_len: u32,
796 is_offer: u32,
797 ) -> i32;
798
799 #[link_name = "api_rtc_set_remote_description"]
800 fn _api_rtc_set_remote_description(
801 peer_id: u32,
802 sdp_ptr: u32,
803 sdp_len: u32,
804 is_offer: u32,
805 ) -> i32;
806
807 #[link_name = "api_rtc_add_ice_candidate"]
808 fn _api_rtc_add_ice_candidate(peer_id: u32, cand_ptr: u32, cand_len: u32) -> i32;
809
810 #[link_name = "api_rtc_connection_state"]
811 fn _api_rtc_connection_state(peer_id: u32) -> u32;
812
813 #[link_name = "api_rtc_poll_ice_candidate"]
814 fn _api_rtc_poll_ice_candidate(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
815
816 #[link_name = "api_rtc_create_data_channel"]
817 fn _api_rtc_create_data_channel(
818 peer_id: u32,
819 label_ptr: u32,
820 label_len: u32,
821 ordered: u32,
822 ) -> u32;
823
824 #[link_name = "api_rtc_send"]
825 fn _api_rtc_send(
826 peer_id: u32,
827 channel_id: u32,
828 data_ptr: u32,
829 data_len: u32,
830 is_binary: u32,
831 ) -> i32;
832
833 #[link_name = "api_rtc_recv"]
834 fn _api_rtc_recv(peer_id: u32, channel_id: u32, out_ptr: u32, out_cap: u32) -> i64;
835
836 #[link_name = "api_rtc_poll_data_channel"]
837 fn _api_rtc_poll_data_channel(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
838
839 #[link_name = "api_rtc_add_track"]
840 fn _api_rtc_add_track(peer_id: u32, kind: u32) -> u32;
841
842 #[link_name = "api_rtc_poll_track"]
843 fn _api_rtc_poll_track(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
844
845 #[link_name = "api_rtc_signal_connect"]
846 fn _api_rtc_signal_connect(url_ptr: u32, url_len: u32) -> u32;
847
848 #[link_name = "api_rtc_signal_join_room"]
849 fn _api_rtc_signal_join_room(room_ptr: u32, room_len: u32) -> i32;
850
851 #[link_name = "api_rtc_signal_send"]
852 fn _api_rtc_signal_send(data_ptr: u32, data_len: u32) -> i32;
853
854 #[link_name = "api_rtc_signal_recv"]
855 fn _api_rtc_signal_recv(out_ptr: u32, out_cap: u32) -> i32;
856
857 #[link_name = "api_ws_connect"]
860 fn _api_ws_connect(url_ptr: u32, url_len: u32) -> u32;
861
862 #[link_name = "api_ws_send_text"]
863 fn _api_ws_send_text(id: u32, data_ptr: u32, data_len: u32) -> i32;
864
865 #[link_name = "api_ws_send_binary"]
866 fn _api_ws_send_binary(id: u32, data_ptr: u32, data_len: u32) -> i32;
867
868 #[link_name = "api_ws_recv"]
869 fn _api_ws_recv(id: u32, out_ptr: u32, out_cap: u32) -> i64;
870
871 #[link_name = "api_ws_ready_state"]
872 fn _api_ws_ready_state(id: u32) -> u32;
873
874 #[link_name = "api_ws_close"]
875 fn _api_ws_close(id: u32) -> i32;
876
877 #[link_name = "api_ws_remove"]
878 fn _api_ws_remove(id: u32);
879
880 #[link_name = "api_midi_input_count"]
883 fn _api_midi_input_count() -> u32;
884
885 #[link_name = "api_midi_output_count"]
886 fn _api_midi_output_count() -> u32;
887
888 #[link_name = "api_midi_input_name"]
889 fn _api_midi_input_name(index: u32, out_ptr: u32, out_cap: u32) -> u32;
890
891 #[link_name = "api_midi_output_name"]
892 fn _api_midi_output_name(index: u32, out_ptr: u32, out_cap: u32) -> u32;
893
894 #[link_name = "api_midi_open_input"]
895 fn _api_midi_open_input(index: u32) -> u32;
896
897 #[link_name = "api_midi_open_output"]
898 fn _api_midi_open_output(index: u32) -> u32;
899
900 #[link_name = "api_midi_send"]
901 fn _api_midi_send(handle: u32, data_ptr: u32, data_len: u32) -> i32;
902
903 #[link_name = "api_midi_recv"]
904 fn _api_midi_recv(handle: u32, out_ptr: u32, out_cap: u32) -> i32;
905
906 #[link_name = "api_midi_close"]
907 fn _api_midi_close(handle: u32);
908
909 #[link_name = "api_url_resolve"]
912 fn _api_url_resolve(
913 base_ptr: u32,
914 base_len: u32,
915 rel_ptr: u32,
916 rel_len: u32,
917 out_ptr: u32,
918 out_cap: u32,
919 ) -> i32;
920
921 #[link_name = "api_url_encode"]
922 fn _api_url_encode(input_ptr: u32, input_len: u32, out_ptr: u32, out_cap: u32) -> u32;
923
924 #[link_name = "api_url_decode"]
925 fn _api_url_decode(input_ptr: u32, input_len: u32, out_ptr: u32, out_cap: u32) -> u32;
926}
927
928pub fn log(msg: &str) {
932 unsafe { _api_log(msg.as_ptr() as u32, msg.len() as u32) }
933}
934
935pub fn warn(msg: &str) {
937 unsafe { _api_warn(msg.as_ptr() as u32, msg.len() as u32) }
938}
939
940pub fn error(msg: &str) {
942 unsafe { _api_error(msg.as_ptr() as u32, msg.len() as u32) }
943}
944
945pub fn get_location() -> String {
949 let mut buf = [0u8; 128];
950 let len = unsafe { _api_get_location(buf.as_mut_ptr() as u32, buf.len() as u32) };
951 String::from_utf8_lossy(&buf[..len as usize]).to_string()
952}
953
954pub struct UploadedFile {
958 pub name: String,
959 pub data: Vec<u8>,
960}
961
962pub fn upload_file() -> Option<UploadedFile> {
965 let mut name_buf = [0u8; 256];
966 let mut data_buf = vec![0u8; 1024 * 1024]; let result = unsafe {
969 _api_upload_file(
970 name_buf.as_mut_ptr() as u32,
971 name_buf.len() as u32,
972 data_buf.as_mut_ptr() as u32,
973 data_buf.len() as u32,
974 )
975 };
976
977 if result == 0 {
978 return None;
979 }
980
981 let name_len = (result >> 32) as usize;
982 let data_len = (result & 0xFFFF_FFFF) as usize;
983
984 Some(UploadedFile {
985 name: String::from_utf8_lossy(&name_buf[..name_len]).to_string(),
986 data: data_buf[..data_len].to_vec(),
987 })
988}
989
990pub struct FileMetadata {
999 pub name: String,
1000 pub size: u64,
1001 pub mime: String,
1002 pub modified_ms: u64,
1003 pub is_dir: bool,
1004}
1005
1006pub struct FolderEntry {
1008 pub name: String,
1009 pub size: u64,
1010 pub is_dir: bool,
1011 pub handle: u32,
1012}
1013
1014pub fn file_pick(title: &str, filters: &str, multiple: bool) -> Vec<u32> {
1020 let mut buf = [0u32; 64];
1021 let n = unsafe {
1022 _api_file_pick(
1023 title.as_ptr() as u32,
1024 title.len() as u32,
1025 filters.as_ptr() as u32,
1026 filters.len() as u32,
1027 if multiple { 1 } else { 0 },
1028 buf.as_mut_ptr() as u32,
1029 (buf.len() * 4) as u32,
1030 )
1031 };
1032 if n <= 0 {
1033 return Vec::new();
1034 }
1035 buf[..n as usize].to_vec()
1036}
1037
1038pub fn folder_pick(title: &str) -> Option<u32> {
1043 let h = unsafe { _api_folder_pick(title.as_ptr() as u32, title.len() as u32) };
1044 if h == 0 {
1045 None
1046 } else {
1047 Some(h)
1048 }
1049}
1050
1051fn read_json_len(handle: u32, call: impl Fn(u32, u32, u32) -> i32) -> Option<Vec<u8>> {
1052 let mut buf = vec![0u8; 8 * 1024];
1053 let n = call(handle, buf.as_mut_ptr() as u32, buf.len() as u32);
1054 if n >= 0 {
1055 buf.truncate(n as usize);
1056 return Some(buf);
1057 }
1058 if n < -1 {
1060 let required = (-n) as usize;
1061 let mut big = vec![0u8; required];
1062 let n2 = call(handle, big.as_mut_ptr() as u32, big.len() as u32);
1063 if n2 >= 0 {
1064 big.truncate(n2 as usize);
1065 return Some(big);
1066 }
1067 }
1068 None
1069}
1070
1071pub fn folder_entries(handle: u32) -> Vec<FolderEntry> {
1077 let bytes = match read_json_len(handle, |h, p, c| unsafe { _api_folder_entries(h, p, c) }) {
1078 Some(b) => b,
1079 None => return Vec::new(),
1080 };
1081 parse_folder_entries(&bytes)
1082}
1083
1084fn parse_folder_entries(bytes: &[u8]) -> Vec<FolderEntry> {
1085 let s = core::str::from_utf8(bytes).unwrap_or("");
1088 let mut out = Vec::new();
1089 let mut rest = s.trim();
1090 if !rest.starts_with('[') {
1091 return out;
1092 }
1093 rest = &rest[1..];
1094 loop {
1095 rest = rest.trim_start_matches(|c: char| c.is_whitespace() || c == ',');
1096 if rest.starts_with(']') || rest.is_empty() {
1097 break;
1098 }
1099 let Some(end) = rest.find('}') else { break };
1100 let obj = &rest[..=end];
1101 rest = &rest[end + 1..];
1102 let name = json_str_field(obj, "\"name\":").unwrap_or_default();
1103 let size = json_num_field(obj, "\"size\":").unwrap_or(0);
1104 let is_dir = json_bool_field(obj, "\"is_dir\":").unwrap_or(false);
1105 let handle = json_num_field(obj, "\"handle\":").unwrap_or(0) as u32;
1106 out.push(FolderEntry {
1107 name,
1108 size,
1109 is_dir,
1110 handle,
1111 });
1112 }
1113 out
1114}
1115
1116fn json_str_field(obj: &str, key: &str) -> Option<String> {
1117 let idx = obj.find(key)?;
1118 let after = &obj[idx + key.len()..];
1119 let start = after.find('"')? + 1;
1120 let mut out = String::new();
1121 let bytes = after.as_bytes();
1122 let mut i = start;
1123 while i < bytes.len() {
1124 let c = bytes[i];
1125 if c == b'\\' && i + 1 < bytes.len() {
1126 match bytes[i + 1] {
1127 b'"' => out.push('"'),
1128 b'\\' => out.push('\\'),
1129 b'n' => out.push('\n'),
1130 b'r' => out.push('\r'),
1131 b't' => out.push('\t'),
1132 _ => out.push(bytes[i + 1] as char),
1133 }
1134 i += 2;
1135 } else if c == b'"' {
1136 return Some(out);
1137 } else {
1138 out.push(c as char);
1139 i += 1;
1140 }
1141 }
1142 None
1143}
1144
1145fn json_num_field(obj: &str, key: &str) -> Option<u64> {
1146 let idx = obj.find(key)?;
1147 let after = obj[idx + key.len()..].trim_start();
1148 let end = after
1149 .find(|c: char| !c.is_ascii_digit())
1150 .unwrap_or(after.len());
1151 after[..end].parse().ok()
1152}
1153
1154fn json_bool_field(obj: &str, key: &str) -> Option<bool> {
1155 let idx = obj.find(key)?;
1156 let after = obj[idx + key.len()..].trim_start();
1157 if after.starts_with("true") {
1158 Some(true)
1159 } else if after.starts_with("false") {
1160 Some(false)
1161 } else {
1162 None
1163 }
1164}
1165
1166pub fn file_read(handle: u32) -> Option<Vec<u8>> {
1171 let mut buf = vec![0u8; 64 * 1024];
1172 let n = unsafe { _api_file_read(handle, buf.as_mut_ptr() as u32, buf.len() as u32) };
1173 if n >= 0 {
1174 buf.truncate(n as usize);
1175 return Some(buf);
1176 }
1177 if n < -1 {
1178 let required = (-n) as usize;
1179 if required > 64 * 1024 * 1024 {
1180 return None;
1181 }
1182 let mut big = vec![0u8; required];
1183 let n2 = unsafe { _api_file_read(handle, big.as_mut_ptr() as u32, big.len() as u32) };
1184 if n2 >= 0 {
1185 big.truncate(n2 as usize);
1186 return Some(big);
1187 }
1188 }
1189 None
1190}
1191
1192pub fn file_read_range(handle: u32, offset: u64, len: u32) -> Option<Vec<u8>> {
1197 let mut buf = vec![0u8; len as usize];
1198 let n = unsafe {
1199 _api_file_read_range(
1200 handle,
1201 offset as u32,
1202 (offset >> 32) as u32,
1203 len,
1204 buf.as_mut_ptr() as u32,
1205 buf.len() as u32,
1206 )
1207 };
1208 if n < 0 {
1209 return None;
1210 }
1211 buf.truncate(n as usize);
1212 Some(buf)
1213}
1214
1215pub fn file_metadata(handle: u32) -> Option<FileMetadata> {
1217 let bytes = read_json_len(handle, |h, p, c| unsafe { _api_file_metadata(h, p, c) })?;
1218 let s = core::str::from_utf8(&bytes).ok()?;
1219 Some(FileMetadata {
1220 name: json_str_field(s, "\"name\":").unwrap_or_default(),
1221 size: json_num_field(s, "\"size\":").unwrap_or(0),
1222 mime: json_str_field(s, "\"mime\":").unwrap_or_default(),
1223 modified_ms: json_num_field(s, "\"modified_ms\":").unwrap_or(0),
1224 is_dir: json_bool_field(s, "\"is_dir\":").unwrap_or(false),
1225 })
1226}
1227
1228pub fn canvas_clear(r: u8, g: u8, b: u8, a: u8) {
1232 unsafe { _api_canvas_clear(r as u32, g as u32, b as u32, a as u32) }
1233}
1234
1235pub fn canvas_rect(x: f32, y: f32, w: f32, h: f32, r: u8, g: u8, b: u8, a: u8) {
1237 unsafe { _api_canvas_rect(x, y, w, h, r as u32, g as u32, b as u32, a as u32) }
1238}
1239
1240pub fn canvas_circle(cx: f32, cy: f32, radius: f32, r: u8, g: u8, b: u8, a: u8) {
1242 unsafe { _api_canvas_circle(cx, cy, radius, r as u32, g as u32, b as u32, a as u32) }
1243}
1244
1245pub fn canvas_text(x: f32, y: f32, size: f32, r: u8, g: u8, b: u8, a: u8, text: &str) {
1247 unsafe {
1248 _api_canvas_text(
1249 x,
1250 y,
1251 size,
1252 r as u32,
1253 g as u32,
1254 b as u32,
1255 a as u32,
1256 text.as_ptr() as u32,
1257 text.len() as u32,
1258 )
1259 }
1260}
1261
1262pub const FONT_STYLE_NORMAL: u32 = 0;
1264pub const FONT_STYLE_ITALIC: u32 = 1;
1266pub const FONT_STYLE_OBLIQUE: u32 = 2;
1268
1269pub const TEXT_ALIGN_LEFT: u32 = 0;
1271pub const TEXT_ALIGN_CENTER: u32 = 1;
1273pub const TEXT_ALIGN_RIGHT: u32 = 2;
1275
1276#[derive(Clone, Copy, Debug, Default)]
1278pub struct TextMetrics {
1279 pub width: f32,
1281 pub ascent: f32,
1283 pub descent: f32,
1285}
1286
1287#[allow(clippy::too_many_arguments)]
1296pub fn canvas_text_ex(
1297 x: f32,
1298 y: f32,
1299 size: f32,
1300 r: u8,
1301 g: u8,
1302 b: u8,
1303 a: u8,
1304 family: &str,
1305 weight: u32,
1306 style: u32,
1307 align: u32,
1308 text: &str,
1309) {
1310 unsafe {
1311 _api_canvas_text_ex(
1312 x,
1313 y,
1314 size,
1315 r as u32,
1316 g as u32,
1317 b as u32,
1318 a as u32,
1319 family.as_ptr() as u32,
1320 family.len() as u32,
1321 weight,
1322 style,
1323 align,
1324 text.as_ptr() as u32,
1325 text.len() as u32,
1326 )
1327 }
1328}
1329
1330pub fn canvas_measure_text(
1337 size: f32,
1338 family: &str,
1339 weight: u32,
1340 style: u32,
1341 text: &str,
1342) -> TextMetrics {
1343 let mut out = [0u8; 12];
1344 let ok = unsafe {
1345 _api_canvas_measure_text(
1346 size,
1347 family.as_ptr() as u32,
1348 family.len() as u32,
1349 weight,
1350 style,
1351 text.as_ptr() as u32,
1352 text.len() as u32,
1353 out.as_mut_ptr() as u32,
1354 )
1355 };
1356 if ok == 0 {
1357 return TextMetrics::default();
1358 }
1359 TextMetrics {
1360 width: f32::from_le_bytes([out[0], out[1], out[2], out[3]]),
1361 ascent: f32::from_le_bytes([out[4], out[5], out[6], out[7]]),
1362 descent: f32::from_le_bytes([out[8], out[9], out[10], out[11]]),
1363 }
1364}
1365
1366pub fn canvas_line(x1: f32, y1: f32, x2: f32, y2: f32, r: u8, g: u8, b: u8, a: u8, thickness: f32) {
1368 unsafe {
1369 _api_canvas_line(
1370 x1, y1, x2, y2, r as u32, g as u32, b as u32, a as u32, thickness,
1371 )
1372 }
1373}
1374
1375pub fn canvas_dimensions() -> (u32, u32) {
1377 let packed = unsafe { _api_canvas_dimensions() };
1378 ((packed >> 32) as u32, (packed & 0xFFFF_FFFF) as u32)
1379}
1380
1381pub fn canvas_image(x: f32, y: f32, w: f32, h: f32, data: &[u8]) {
1384 unsafe { _api_canvas_image(x, y, w, h, data.as_ptr() as u32, data.len() as u32) }
1385}
1386
1387pub fn canvas_rounded_rect(
1391 x: f32,
1392 y: f32,
1393 w: f32,
1394 h: f32,
1395 radius: f32,
1396 r: u8,
1397 g: u8,
1398 b: u8,
1399 a: u8,
1400) {
1401 unsafe { _api_canvas_rounded_rect(x, y, w, h, radius, r as u32, g as u32, b as u32, a as u32) }
1402}
1403
1404pub fn canvas_arc(
1406 cx: f32,
1407 cy: f32,
1408 radius: f32,
1409 start_angle: f32,
1410 end_angle: f32,
1411 r: u8,
1412 g: u8,
1413 b: u8,
1414 a: u8,
1415 thickness: f32,
1416) {
1417 unsafe {
1418 _api_canvas_arc(
1419 cx,
1420 cy,
1421 radius,
1422 start_angle,
1423 end_angle,
1424 r as u32,
1425 g as u32,
1426 b as u32,
1427 a as u32,
1428 thickness,
1429 )
1430 }
1431}
1432
1433pub fn canvas_bezier(
1435 x1: f32,
1436 y1: f32,
1437 cp1x: f32,
1438 cp1y: f32,
1439 cp2x: f32,
1440 cp2y: f32,
1441 x2: f32,
1442 y2: f32,
1443 r: u8,
1444 g: u8,
1445 b: u8,
1446 a: u8,
1447 thickness: f32,
1448) {
1449 unsafe {
1450 _api_canvas_bezier(
1451 x1, y1, cp1x, cp1y, cp2x, cp2y, x2, y2, r as u32, g as u32, b as u32, a as u32,
1452 thickness,
1453 )
1454 }
1455}
1456
1457pub const GRADIENT_LINEAR: u32 = 0;
1459pub const GRADIENT_RADIAL: u32 = 1;
1460
1461pub fn canvas_gradient(
1468 x: f32,
1469 y: f32,
1470 w: f32,
1471 h: f32,
1472 kind: u32,
1473 ax: f32,
1474 ay: f32,
1475 bx: f32,
1476 by: f32,
1477 stops: &[(f32, u8, u8, u8, u8)],
1478) {
1479 let mut buf = Vec::with_capacity(stops.len() * 8);
1480 for &(offset, r, g, b, a) in stops {
1481 buf.extend_from_slice(&offset.to_le_bytes());
1482 buf.push(r);
1483 buf.push(g);
1484 buf.push(b);
1485 buf.push(a);
1486 }
1487 unsafe {
1488 _api_canvas_gradient(
1489 x,
1490 y,
1491 w,
1492 h,
1493 kind,
1494 ax,
1495 ay,
1496 bx,
1497 by,
1498 buf.as_ptr() as u32,
1499 buf.len() as u32,
1500 )
1501 }
1502}
1503
1504pub fn canvas_save() {
1509 unsafe { _api_canvas_save() }
1510}
1511
1512pub fn canvas_restore() {
1514 unsafe { _api_canvas_restore() }
1515}
1516
1517pub fn canvas_transform(a: f32, b: f32, c: f32, d: f32, tx: f32, ty: f32) {
1528 unsafe { _api_canvas_transform(a, b, c, d, tx, ty) }
1529}
1530
1531pub fn canvas_clip(x: f32, y: f32, w: f32, h: f32) {
1534 unsafe { _api_canvas_clip(x, y, w, h) }
1535}
1536
1537pub fn canvas_opacity(alpha: f32) {
1540 unsafe { _api_canvas_opacity(alpha) }
1541}
1542
1543pub mod gpu_usage {
1547 pub const VERTEX: u32 = 0x0020;
1548 pub const INDEX: u32 = 0x0010;
1549 pub const UNIFORM: u32 = 0x0040;
1550 pub const STORAGE: u32 = 0x0080;
1551}
1552
1553pub fn gpu_create_buffer(size: u64, usage: u32) -> u32 {
1557 unsafe { _api_gpu_create_buffer(size as u32, (size >> 32) as u32, usage) }
1558}
1559
1560pub fn gpu_create_texture(width: u32, height: u32) -> u32 {
1562 unsafe { _api_gpu_create_texture(width, height) }
1563}
1564
1565pub fn gpu_create_shader(source: &str) -> u32 {
1567 unsafe { _api_gpu_create_shader(source.as_ptr() as u32, source.len() as u32) }
1568}
1569
1570pub fn gpu_create_pipeline(shader: u32, vertex_entry: &str, fragment_entry: &str) -> u32 {
1574 unsafe {
1575 _api_gpu_create_render_pipeline(
1576 shader,
1577 vertex_entry.as_ptr() as u32,
1578 vertex_entry.len() as u32,
1579 fragment_entry.as_ptr() as u32,
1580 fragment_entry.len() as u32,
1581 )
1582 }
1583}
1584
1585pub fn gpu_create_compute_pipeline(shader: u32, entry_point: &str) -> u32 {
1587 unsafe {
1588 _api_gpu_create_compute_pipeline(
1589 shader,
1590 entry_point.as_ptr() as u32,
1591 entry_point.len() as u32,
1592 )
1593 }
1594}
1595
1596pub fn gpu_write_buffer(handle: u32, offset: u64, data: &[u8]) -> bool {
1598 unsafe {
1599 _api_gpu_write_buffer(
1600 handle,
1601 offset as u32,
1602 (offset >> 32) as u32,
1603 data.as_ptr() as u32,
1604 data.len() as u32,
1605 ) != 0
1606 }
1607}
1608
1609pub fn gpu_draw(
1611 pipeline: u32,
1612 target_texture: u32,
1613 vertex_count: u32,
1614 instance_count: u32,
1615) -> bool {
1616 unsafe { _api_gpu_draw(pipeline, target_texture, vertex_count, instance_count) != 0 }
1617}
1618
1619pub fn gpu_dispatch_compute(pipeline: u32, x: u32, y: u32, z: u32) -> bool {
1621 unsafe { _api_gpu_dispatch_compute(pipeline, x, y, z) != 0 }
1622}
1623
1624pub fn gpu_destroy_buffer(handle: u32) -> bool {
1626 unsafe { _api_gpu_destroy_buffer(handle) != 0 }
1627}
1628
1629pub fn gpu_destroy_texture(handle: u32) -> bool {
1631 unsafe { _api_gpu_destroy_texture(handle) != 0 }
1632}
1633
1634pub fn storage_set(key: &str, value: &str) {
1638 unsafe {
1639 _api_storage_set(
1640 key.as_ptr() as u32,
1641 key.len() as u32,
1642 value.as_ptr() as u32,
1643 value.len() as u32,
1644 )
1645 }
1646}
1647
1648pub fn storage_get(key: &str) -> String {
1650 let mut buf = [0u8; 4096];
1651 let len = unsafe {
1652 _api_storage_get(
1653 key.as_ptr() as u32,
1654 key.len() as u32,
1655 buf.as_mut_ptr() as u32,
1656 buf.len() as u32,
1657 )
1658 };
1659 String::from_utf8_lossy(&buf[..len as usize]).to_string()
1660}
1661
1662pub fn storage_remove(key: &str) {
1664 unsafe { _api_storage_remove(key.as_ptr() as u32, key.len() as u32) }
1665}
1666
1667pub fn clipboard_write(text: &str) {
1671 unsafe { _api_clipboard_write(text.as_ptr() as u32, text.len() as u32) }
1672}
1673
1674pub fn clipboard_read() -> String {
1676 let mut buf = [0u8; 4096];
1677 let len = unsafe { _api_clipboard_read(buf.as_mut_ptr() as u32, buf.len() as u32) };
1678 String::from_utf8_lossy(&buf[..len as usize]).to_string()
1679}
1680
1681pub fn time_now_ms() -> u64 {
1685 unsafe { _api_time_now_ms() }
1686}
1687
1688pub fn set_timeout(callback_id: u32, delay_ms: u32) -> u32 {
1692 unsafe { _api_set_timeout(callback_id, delay_ms) }
1693}
1694
1695pub fn set_interval(callback_id: u32, interval_ms: u32) -> u32 {
1699 unsafe { _api_set_interval(callback_id, interval_ms) }
1700}
1701
1702pub fn clear_timer(timer_id: u32) {
1704 unsafe { _api_clear_timer(timer_id) }
1705}
1706
1707pub fn request_animation_frame(callback_id: u32) -> u32 {
1713 unsafe { _api_request_animation_frame(callback_id) }
1714}
1715
1716pub fn cancel_animation_frame(request_id: u32) {
1718 unsafe { _api_cancel_animation_frame(request_id) }
1719}
1720
1721pub fn on_event(event_type: &str, callback_id: u32) -> u32 {
1752 unsafe {
1753 _api_on_event(
1754 event_type.as_ptr() as u32,
1755 event_type.len() as u32,
1756 callback_id,
1757 )
1758 }
1759}
1760
1761pub fn off_event(listener_id: u32) -> bool {
1764 unsafe { _api_off_event(listener_id) != 0 }
1765}
1766
1767pub fn emit_event(event_type: &str, data: &[u8]) {
1771 unsafe {
1772 _api_emit_event(
1773 event_type.as_ptr() as u32,
1774 event_type.len() as u32,
1775 data.as_ptr() as u32,
1776 data.len() as u32,
1777 )
1778 }
1779}
1780
1781pub fn event_type() -> String {
1784 let len = unsafe { _api_event_type_len() } as usize;
1785 if len == 0 {
1786 return String::new();
1787 }
1788 let mut buf = vec![0u8; len];
1789 let written = unsafe { _api_event_type_read(buf.as_mut_ptr() as u32, len as u32) } as usize;
1790 buf.truncate(written);
1791 String::from_utf8_lossy(&buf).into_owned()
1792}
1793
1794pub fn event_data(out: &mut [u8]) -> usize {
1797 let cap = out.len() as u32;
1798 if cap == 0 {
1799 return 0;
1800 }
1801 unsafe { _api_event_data_read(out.as_mut_ptr() as u32, cap) as usize }
1802}
1803
1804pub fn event_data_into() -> Vec<u8> {
1806 let len = unsafe { _api_event_data_len() } as usize;
1807 if len == 0 {
1808 return Vec::new();
1809 }
1810 let mut buf = vec![0u8; len];
1811 let written = unsafe { _api_event_data_read(buf.as_mut_ptr() as u32, len as u32) } as usize;
1812 buf.truncate(written);
1813 buf
1814}
1815
1816pub fn random_u64() -> u64 {
1820 unsafe { _api_random() }
1821}
1822
1823pub fn random_f64() -> f64 {
1825 (random_u64() >> 11) as f64 / (1u64 << 53) as f64
1826}
1827
1828pub fn notify(title: &str, body: &str) {
1832 unsafe {
1833 _api_notify(
1834 title.as_ptr() as u32,
1835 title.len() as u32,
1836 body.as_ptr() as u32,
1837 body.len() as u32,
1838 )
1839 }
1840}
1841
1842#[repr(u32)]
1846#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1847pub enum AudioFormat {
1848 Unknown = 0,
1850 Wav = 1,
1851 Mp3 = 2,
1852 Ogg = 3,
1853 Flac = 4,
1854}
1855
1856impl From<u32> for AudioFormat {
1857 fn from(code: u32) -> Self {
1858 match code {
1859 1 => AudioFormat::Wav,
1860 2 => AudioFormat::Mp3,
1861 3 => AudioFormat::Ogg,
1862 4 => AudioFormat::Flac,
1863 _ => AudioFormat::Unknown,
1864 }
1865 }
1866}
1867
1868impl From<AudioFormat> for u32 {
1869 fn from(f: AudioFormat) -> u32 {
1870 f as u32
1871 }
1872}
1873
1874pub fn audio_play(data: &[u8]) -> i32 {
1877 unsafe { _api_audio_play(data.as_ptr() as u32, data.len() as u32) }
1878}
1879
1880pub fn audio_detect_format(data: &[u8]) -> AudioFormat {
1882 let code = unsafe { _api_audio_detect_format(data.as_ptr() as u32, data.len() as u32) };
1883 AudioFormat::from(code)
1884}
1885
1886pub fn audio_play_with_format(data: &[u8], format: AudioFormat) -> i32 {
1889 unsafe {
1890 _api_audio_play_with_format(data.as_ptr() as u32, data.len() as u32, u32::from(format))
1891 }
1892}
1893
1894pub fn audio_play_url(url: &str) -> i32 {
1899 unsafe { _api_audio_play_url(url.as_ptr() as u32, url.len() as u32) }
1900}
1901
1902pub fn audio_last_url_content_type() -> String {
1904 let mut buf = [0u8; 512];
1905 let len =
1906 unsafe { _api_audio_last_url_content_type(buf.as_mut_ptr() as u32, buf.len() as u32) };
1907 let n = (len as usize).min(buf.len());
1908 String::from_utf8_lossy(&buf[..n]).to_string()
1909}
1910
1911pub fn audio_pause() {
1913 unsafe { _api_audio_pause() }
1914}
1915
1916pub fn audio_resume() {
1918 unsafe { _api_audio_resume() }
1919}
1920
1921pub fn audio_stop() {
1923 unsafe { _api_audio_stop() }
1924}
1925
1926pub fn audio_set_volume(level: f32) {
1928 unsafe { _api_audio_set_volume(level) }
1929}
1930
1931pub fn audio_get_volume() -> f32 {
1933 unsafe { _api_audio_get_volume() }
1934}
1935
1936pub fn audio_is_playing() -> bool {
1938 unsafe { _api_audio_is_playing() != 0 }
1939}
1940
1941pub fn audio_position() -> u64 {
1943 unsafe { _api_audio_position() }
1944}
1945
1946pub fn audio_seek(position_ms: u64) -> i32 {
1948 unsafe { _api_audio_seek(position_ms) }
1949}
1950
1951pub fn audio_duration() -> u64 {
1954 unsafe { _api_audio_duration() }
1955}
1956
1957pub fn audio_set_loop(enabled: bool) {
1960 unsafe { _api_audio_set_loop(if enabled { 1 } else { 0 }) }
1961}
1962
1963pub fn audio_channel_play(channel: u32, data: &[u8]) -> i32 {
1969 unsafe { _api_audio_channel_play(channel, data.as_ptr() as u32, data.len() as u32) }
1970}
1971
1972pub fn audio_channel_play_with_format(channel: u32, data: &[u8], format: AudioFormat) -> i32 {
1974 unsafe {
1975 _api_audio_channel_play_with_format(
1976 channel,
1977 data.as_ptr() as u32,
1978 data.len() as u32,
1979 u32::from(format),
1980 )
1981 }
1982}
1983
1984pub fn audio_channel_stop(channel: u32) {
1986 unsafe { _api_audio_channel_stop(channel) }
1987}
1988
1989pub fn audio_channel_set_volume(channel: u32, level: f32) {
1991 unsafe { _api_audio_channel_set_volume(channel, level) }
1992}
1993
1994#[repr(u32)]
1998#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1999pub enum VideoFormat {
2000 Unknown = 0,
2001 Mp4 = 1,
2002 Webm = 2,
2003 Av1 = 3,
2004}
2005
2006impl From<u32> for VideoFormat {
2007 fn from(code: u32) -> Self {
2008 match code {
2009 1 => VideoFormat::Mp4,
2010 2 => VideoFormat::Webm,
2011 3 => VideoFormat::Av1,
2012 _ => VideoFormat::Unknown,
2013 }
2014 }
2015}
2016
2017impl From<VideoFormat> for u32 {
2018 fn from(f: VideoFormat) -> u32 {
2019 f as u32
2020 }
2021}
2022
2023pub fn video_detect_format(data: &[u8]) -> VideoFormat {
2025 let code = unsafe { _api_video_detect_format(data.as_ptr() as u32, data.len() as u32) };
2026 VideoFormat::from(code)
2027}
2028
2029pub fn video_load(data: &[u8]) -> i32 {
2032 unsafe {
2033 _api_video_load(
2034 data.as_ptr() as u32,
2035 data.len() as u32,
2036 VideoFormat::Unknown as u32,
2037 )
2038 }
2039}
2040
2041pub fn video_load_with_format(data: &[u8], format: VideoFormat) -> i32 {
2043 unsafe { _api_video_load(data.as_ptr() as u32, data.len() as u32, u32::from(format)) }
2044}
2045
2046pub fn video_load_url(url: &str) -> i32 {
2048 unsafe { _api_video_load_url(url.as_ptr() as u32, url.len() as u32) }
2049}
2050
2051pub fn video_last_url_content_type() -> String {
2053 let mut buf = [0u8; 512];
2054 let len =
2055 unsafe { _api_video_last_url_content_type(buf.as_mut_ptr() as u32, buf.len() as u32) };
2056 let n = (len as usize).min(buf.len());
2057 String::from_utf8_lossy(&buf[..n]).to_string()
2058}
2059
2060pub fn video_hls_variant_count() -> u32 {
2062 unsafe { _api_video_hls_variant_count() }
2063}
2064
2065pub fn video_hls_variant_url(index: u32) -> String {
2067 let mut buf = [0u8; 2048];
2068 let len =
2069 unsafe { _api_video_hls_variant_url(index, buf.as_mut_ptr() as u32, buf.len() as u32) };
2070 let n = (len as usize).min(buf.len());
2071 String::from_utf8_lossy(&buf[..n]).to_string()
2072}
2073
2074pub fn video_hls_open_variant(index: u32) -> i32 {
2076 unsafe { _api_video_hls_open_variant(index) }
2077}
2078
2079pub fn video_play() {
2080 unsafe { _api_video_play() }
2081}
2082
2083pub fn video_pause() {
2084 unsafe { _api_video_pause() }
2085}
2086
2087pub fn video_stop() {
2088 unsafe { _api_video_stop() }
2089}
2090
2091pub fn video_seek(position_ms: u64) -> i32 {
2092 unsafe { _api_video_seek(position_ms) }
2093}
2094
2095pub fn video_position() -> u64 {
2096 unsafe { _api_video_position() }
2097}
2098
2099pub fn video_duration() -> u64 {
2100 unsafe { _api_video_duration() }
2101}
2102
2103pub fn video_render(x: f32, y: f32, w: f32, h: f32) -> i32 {
2105 unsafe { _api_video_render(x, y, w, h) }
2106}
2107
2108pub fn video_set_volume(level: f32) {
2110 unsafe { _api_video_set_volume(level) }
2111}
2112
2113pub fn video_get_volume() -> f32 {
2114 unsafe { _api_video_get_volume() }
2115}
2116
2117pub fn video_set_loop(enabled: bool) {
2118 unsafe { _api_video_set_loop(if enabled { 1 } else { 0 }) }
2119}
2120
2121pub fn video_set_pip(enabled: bool) {
2123 unsafe { _api_video_set_pip(if enabled { 1 } else { 0 }) }
2124}
2125
2126pub fn subtitle_load_srt(text: &str) -> i32 {
2128 unsafe { _api_subtitle_load_srt(text.as_ptr() as u32, text.len() as u32) }
2129}
2130
2131pub fn subtitle_load_vtt(text: &str) -> i32 {
2133 unsafe { _api_subtitle_load_vtt(text.as_ptr() as u32, text.len() as u32) }
2134}
2135
2136pub fn subtitle_clear() {
2137 unsafe { _api_subtitle_clear() }
2138}
2139
2140pub fn camera_open() -> i32 {
2146 unsafe { _api_camera_open() }
2147}
2148
2149pub fn camera_close() {
2151 unsafe { _api_camera_close() }
2152}
2153
2154pub fn camera_capture_frame(out: &mut [u8]) -> u32 {
2157 unsafe { _api_camera_capture_frame(out.as_mut_ptr() as u32, out.len() as u32) }
2158}
2159
2160pub fn camera_frame_dimensions() -> (u32, u32) {
2162 let packed = unsafe { _api_camera_frame_dimensions() };
2163 let w = (packed >> 32) as u32;
2164 let h = packed as u32;
2165 (w, h)
2166}
2167
2168pub fn microphone_open() -> i32 {
2172 unsafe { _api_microphone_open() }
2173}
2174
2175pub fn microphone_close() {
2176 unsafe { _api_microphone_close() }
2177}
2178
2179pub fn microphone_sample_rate() -> u32 {
2181 unsafe { _api_microphone_sample_rate() }
2182}
2183
2184pub fn microphone_read_samples(out: &mut [f32]) -> u32 {
2187 unsafe { _api_microphone_read_samples(out.as_mut_ptr() as u32, out.len() as u32) }
2188}
2189
2190pub fn screen_capture(out: &mut [u8]) -> Result<usize, i32> {
2194 let n = unsafe { _api_screen_capture(out.as_mut_ptr() as u32, out.len() as u32) };
2195 if n >= 0 {
2196 Ok(n as usize)
2197 } else {
2198 Err(n)
2199 }
2200}
2201
2202pub fn screen_capture_dimensions() -> (u32, u32) {
2204 let packed = unsafe { _api_screen_capture_dimensions() };
2205 let w = (packed >> 32) as u32;
2206 let h = packed as u32;
2207 (w, h)
2208}
2209
2210pub fn media_pipeline_stats() -> (u64, u32) {
2213 let packed = unsafe { _api_media_pipeline_stats() };
2214 let camera_frames = packed >> 32;
2215 let mic_ring = packed as u32;
2216 (camera_frames, mic_ring)
2217}
2218
2219pub const RTC_STATE_NEW: u32 = 0;
2223pub const RTC_STATE_CONNECTING: u32 = 1;
2225pub const RTC_STATE_CONNECTED: u32 = 2;
2227pub const RTC_STATE_DISCONNECTED: u32 = 3;
2229pub const RTC_STATE_FAILED: u32 = 4;
2231pub const RTC_STATE_CLOSED: u32 = 5;
2233
2234pub const RTC_TRACK_AUDIO: u32 = 0;
2236pub const RTC_TRACK_VIDEO: u32 = 1;
2238
2239pub struct RtcMessage {
2241 pub channel_id: u32,
2243 pub is_binary: bool,
2245 pub data: Vec<u8>,
2247}
2248
2249impl RtcMessage {
2250 pub fn text(&self) -> String {
2252 String::from_utf8_lossy(&self.data).to_string()
2253 }
2254}
2255
2256pub struct RtcDataChannelInfo {
2258 pub channel_id: u32,
2260 pub label: String,
2262}
2263
2264pub fn rtc_create_peer(stun_servers: &str) -> u32 {
2271 unsafe { _api_rtc_create_peer(stun_servers.as_ptr() as u32, stun_servers.len() as u32) }
2272}
2273
2274pub fn rtc_close_peer(peer_id: u32) -> bool {
2276 unsafe { _api_rtc_close_peer(peer_id) != 0 }
2277}
2278
2279pub fn rtc_create_offer(peer_id: u32) -> Result<String, i32> {
2283 let mut buf = vec![0u8; 16 * 1024];
2284 let n = unsafe { _api_rtc_create_offer(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
2285 if n < 0 {
2286 Err(n)
2287 } else {
2288 Ok(String::from_utf8_lossy(&buf[..n as usize]).to_string())
2289 }
2290}
2291
2292pub fn rtc_create_answer(peer_id: u32) -> Result<String, i32> {
2294 let mut buf = vec![0u8; 16 * 1024];
2295 let n = unsafe { _api_rtc_create_answer(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
2296 if n < 0 {
2297 Err(n)
2298 } else {
2299 Ok(String::from_utf8_lossy(&buf[..n as usize]).to_string())
2300 }
2301}
2302
2303pub fn rtc_set_local_description(peer_id: u32, sdp: &str, is_offer: bool) -> i32 {
2307 unsafe {
2308 _api_rtc_set_local_description(
2309 peer_id,
2310 sdp.as_ptr() as u32,
2311 sdp.len() as u32,
2312 if is_offer { 1 } else { 0 },
2313 )
2314 }
2315}
2316
2317pub fn rtc_set_remote_description(peer_id: u32, sdp: &str, is_offer: bool) -> i32 {
2319 unsafe {
2320 _api_rtc_set_remote_description(
2321 peer_id,
2322 sdp.as_ptr() as u32,
2323 sdp.len() as u32,
2324 if is_offer { 1 } else { 0 },
2325 )
2326 }
2327}
2328
2329pub fn rtc_add_ice_candidate(peer_id: u32, candidate_json: &str) -> i32 {
2331 unsafe {
2332 _api_rtc_add_ice_candidate(
2333 peer_id,
2334 candidate_json.as_ptr() as u32,
2335 candidate_json.len() as u32,
2336 )
2337 }
2338}
2339
2340pub fn rtc_connection_state(peer_id: u32) -> u32 {
2342 unsafe { _api_rtc_connection_state(peer_id) }
2343}
2344
2345pub fn rtc_poll_ice_candidate(peer_id: u32) -> Option<String> {
2348 let mut buf = vec![0u8; 4096];
2349 let n =
2350 unsafe { _api_rtc_poll_ice_candidate(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
2351 if n <= 0 {
2352 None
2353 } else {
2354 Some(String::from_utf8_lossy(&buf[..n as usize]).to_string())
2355 }
2356}
2357
2358pub fn rtc_create_data_channel(peer_id: u32, label: &str, ordered: bool) -> u32 {
2363 unsafe {
2364 _api_rtc_create_data_channel(
2365 peer_id,
2366 label.as_ptr() as u32,
2367 label.len() as u32,
2368 if ordered { 1 } else { 0 },
2369 )
2370 }
2371}
2372
2373pub fn rtc_send_text(peer_id: u32, channel_id: u32, text: &str) -> i32 {
2375 unsafe {
2376 _api_rtc_send(
2377 peer_id,
2378 channel_id,
2379 text.as_ptr() as u32,
2380 text.len() as u32,
2381 0,
2382 )
2383 }
2384}
2385
2386pub fn rtc_send_binary(peer_id: u32, channel_id: u32, data: &[u8]) -> i32 {
2388 unsafe {
2389 _api_rtc_send(
2390 peer_id,
2391 channel_id,
2392 data.as_ptr() as u32,
2393 data.len() as u32,
2394 1,
2395 )
2396 }
2397}
2398
2399pub fn rtc_send(peer_id: u32, channel_id: u32, data: &[u8], is_binary: bool) -> i32 {
2401 unsafe {
2402 _api_rtc_send(
2403 peer_id,
2404 channel_id,
2405 data.as_ptr() as u32,
2406 data.len() as u32,
2407 if is_binary { 1 } else { 0 },
2408 )
2409 }
2410}
2411
2412pub fn rtc_recv(peer_id: u32, channel_id: u32) -> Option<RtcMessage> {
2417 let mut buf = vec![0u8; 64 * 1024];
2418 let packed = unsafe {
2419 _api_rtc_recv(
2420 peer_id,
2421 channel_id,
2422 buf.as_mut_ptr() as u32,
2423 buf.len() as u32,
2424 )
2425 };
2426 if packed <= 0 {
2427 return None;
2428 }
2429 let packed = packed as u64;
2430 let data_len = (packed & 0xFFFF_FFFF) as usize;
2431 let is_binary = (packed >> 32) & 1 != 0;
2432 let ch = (packed >> 48) as u32;
2433 Some(RtcMessage {
2434 channel_id: ch,
2435 is_binary,
2436 data: buf[..data_len].to_vec(),
2437 })
2438}
2439
2440pub fn rtc_poll_data_channel(peer_id: u32) -> Option<RtcDataChannelInfo> {
2444 let mut buf = vec![0u8; 1024];
2445 let n =
2446 unsafe { _api_rtc_poll_data_channel(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
2447 if n <= 0 {
2448 return None;
2449 }
2450 let info = String::from_utf8_lossy(&buf[..n as usize]).to_string();
2451 let (id_str, label) = info.split_once(':').unwrap_or(("0", ""));
2452 Some(RtcDataChannelInfo {
2453 channel_id: id_str.parse().unwrap_or(0),
2454 label: label.to_string(),
2455 })
2456}
2457
2458pub fn rtc_add_track(peer_id: u32, kind: u32) -> u32 {
2463 unsafe { _api_rtc_add_track(peer_id, kind) }
2464}
2465
2466pub struct RtcTrackInfo {
2468 pub kind: u32,
2470 pub id: String,
2472 pub stream_id: String,
2474}
2475
2476pub fn rtc_poll_track(peer_id: u32) -> Option<RtcTrackInfo> {
2480 let mut buf = vec![0u8; 1024];
2481 let n = unsafe { _api_rtc_poll_track(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
2482 if n <= 0 {
2483 return None;
2484 }
2485 let info = String::from_utf8_lossy(&buf[..n as usize]).to_string();
2486 let mut parts = info.splitn(3, ':');
2487 let kind = parts.next().unwrap_or("2").parse().unwrap_or(2);
2488 let id = parts.next().unwrap_or("").to_string();
2489 let stream_id = parts.next().unwrap_or("").to_string();
2490 Some(RtcTrackInfo {
2491 kind,
2492 id,
2493 stream_id,
2494 })
2495}
2496
2497pub fn rtc_signal_connect(url: &str) -> bool {
2501 unsafe { _api_rtc_signal_connect(url.as_ptr() as u32, url.len() as u32) != 0 }
2502}
2503
2504pub fn rtc_signal_join_room(room: &str) -> i32 {
2506 unsafe { _api_rtc_signal_join_room(room.as_ptr() as u32, room.len() as u32) }
2507}
2508
2509pub fn rtc_signal_send(data: &[u8]) -> i32 {
2511 unsafe { _api_rtc_signal_send(data.as_ptr() as u32, data.len() as u32) }
2512}
2513
2514pub fn rtc_signal_recv() -> Option<Vec<u8>> {
2516 let mut buf = vec![0u8; 16 * 1024];
2517 let n = unsafe { _api_rtc_signal_recv(buf.as_mut_ptr() as u32, buf.len() as u32) };
2518 if n <= 0 {
2519 None
2520 } else {
2521 Some(buf[..n as usize].to_vec())
2522 }
2523}
2524
2525pub const WS_CONNECTING: u32 = 0;
2529pub const WS_OPEN: u32 = 1;
2531pub const WS_CLOSING: u32 = 2;
2533pub const WS_CLOSED: u32 = 3;
2535
2536pub struct WsMessage {
2538 pub is_binary: bool,
2540 pub data: Vec<u8>,
2542}
2543
2544impl WsMessage {
2545 pub fn text(&self) -> String {
2547 String::from_utf8_lossy(&self.data).to_string()
2548 }
2549}
2550
2551pub fn ws_connect(url: &str) -> u32 {
2557 unsafe { _api_ws_connect(url.as_ptr() as u32, url.len() as u32) }
2558}
2559
2560pub fn ws_send_text(id: u32, text: &str) -> i32 {
2564 unsafe { _api_ws_send_text(id, text.as_ptr() as u32, text.len() as u32) }
2565}
2566
2567pub fn ws_send_binary(id: u32, data: &[u8]) -> i32 {
2571 unsafe { _api_ws_send_binary(id, data.as_ptr() as u32, data.len() as u32) }
2572}
2573
2574pub fn ws_recv(id: u32) -> Option<WsMessage> {
2580 let mut buf = vec![0u8; 64 * 1024];
2581 let result = unsafe { _api_ws_recv(id, buf.as_mut_ptr() as u32, buf.len() as u32) };
2582 if result < 0 {
2583 return None;
2584 }
2585 let len = (result & 0xFFFF_FFFF) as usize;
2586 let is_binary = (result >> 32) & 1 == 1;
2587 Some(WsMessage {
2588 is_binary,
2589 data: buf[..len].to_vec(),
2590 })
2591}
2592
2593pub fn ws_ready_state(id: u32) -> u32 {
2597 unsafe { _api_ws_ready_state(id) }
2598}
2599
2600pub fn ws_close(id: u32) -> i32 {
2607 unsafe { _api_ws_close(id) }
2608}
2609
2610pub fn ws_remove(id: u32) {
2615 unsafe { _api_ws_remove(id) }
2616}
2617
2618pub fn midi_input_count() -> u32 {
2622 unsafe { _api_midi_input_count() }
2623}
2624
2625pub fn midi_output_count() -> u32 {
2627 unsafe { _api_midi_output_count() }
2628}
2629
2630pub fn midi_input_name(index: u32) -> String {
2634 let mut buf = [0u8; 128];
2635 let len = unsafe { _api_midi_input_name(index, buf.as_mut_ptr() as u32, buf.len() as u32) };
2636 String::from_utf8_lossy(&buf[..len as usize]).to_string()
2637}
2638
2639pub fn midi_output_name(index: u32) -> String {
2643 let mut buf = [0u8; 128];
2644 let len = unsafe { _api_midi_output_name(index, buf.as_mut_ptr() as u32, buf.len() as u32) };
2645 String::from_utf8_lossy(&buf[..len as usize]).to_string()
2646}
2647
2648pub fn midi_open_input(index: u32) -> u32 {
2653 unsafe { _api_midi_open_input(index) }
2654}
2655
2656pub fn midi_open_output(index: u32) -> u32 {
2660 unsafe { _api_midi_open_output(index) }
2661}
2662
2663pub fn midi_send(handle: u32, data: &[u8]) -> i32 {
2667 unsafe { _api_midi_send(handle, data.as_ptr() as u32, data.len() as u32) }
2668}
2669
2670pub fn midi_recv(handle: u32) -> Option<Vec<u8>> {
2677 let mut buf = [0u8; 256];
2678 let n = unsafe { _api_midi_recv(handle, buf.as_mut_ptr() as u32, buf.len() as u32) };
2679 if n >= 0 {
2680 return Some(buf[..n as usize].to_vec());
2681 }
2682 if n == -2 {
2684 let mut big = vec![0u8; 64 * 1024];
2685 let n2 = unsafe { _api_midi_recv(handle, big.as_mut_ptr() as u32, big.len() as u32) };
2686 if n2 >= 0 {
2687 big.truncate(n2 as usize);
2688 return Some(big);
2689 }
2690 }
2691 None
2692}
2693
2694pub fn midi_close(handle: u32) {
2696 unsafe { _api_midi_close(handle) }
2697}
2698
2699pub struct FetchResponse {
2703 pub status: u32,
2704 pub body: Vec<u8>,
2705}
2706
2707impl FetchResponse {
2708 pub fn text(&self) -> String {
2710 String::from_utf8_lossy(&self.body).to_string()
2711 }
2712}
2713
2714pub fn fetch(
2720 method: &str,
2721 url: &str,
2722 content_type: &str,
2723 body: &[u8],
2724) -> Result<FetchResponse, i64> {
2725 let mut out_buf = vec![0u8; 4 * 1024 * 1024]; let result = unsafe {
2727 _api_fetch(
2728 method.as_ptr() as u32,
2729 method.len() as u32,
2730 url.as_ptr() as u32,
2731 url.len() as u32,
2732 content_type.as_ptr() as u32,
2733 content_type.len() as u32,
2734 body.as_ptr() as u32,
2735 body.len() as u32,
2736 out_buf.as_mut_ptr() as u32,
2737 out_buf.len() as u32,
2738 )
2739 };
2740 if result < 0 {
2741 return Err(result);
2742 }
2743 let status = (result >> 32) as u32;
2744 let body_len = (result & 0xFFFF_FFFF) as usize;
2745 Ok(FetchResponse {
2746 status,
2747 body: out_buf[..body_len].to_vec(),
2748 })
2749}
2750
2751pub fn fetch_get(url: &str) -> Result<FetchResponse, i64> {
2753 fetch("GET", url, "", &[])
2754}
2755
2756pub fn fetch_post(url: &str, content_type: &str, body: &[u8]) -> Result<FetchResponse, i64> {
2758 fetch("POST", url, content_type, body)
2759}
2760
2761pub fn fetch_post_proto(url: &str, msg: &proto::ProtoEncoder) -> Result<FetchResponse, i64> {
2763 fetch("POST", url, "application/protobuf", msg.as_bytes())
2764}
2765
2766pub fn fetch_put(url: &str, content_type: &str, body: &[u8]) -> Result<FetchResponse, i64> {
2768 fetch("PUT", url, content_type, body)
2769}
2770
2771pub fn fetch_delete(url: &str) -> Result<FetchResponse, i64> {
2773 fetch("DELETE", url, "", &[])
2774}
2775
2776pub const FETCH_PENDING: u32 = 0;
2786pub const FETCH_STREAMING: u32 = 1;
2788pub const FETCH_DONE: u32 = 2;
2790pub const FETCH_ERROR: u32 = 3;
2792pub const FETCH_ABORTED: u32 = 4;
2794
2795pub enum FetchChunk {
2797 Data(Vec<u8>),
2800 Pending,
2803 End,
2805 Error,
2808}
2809
2810pub fn fetch_begin(method: &str, url: &str, content_type: &str, body: &[u8]) -> u32 {
2819 unsafe {
2820 _api_fetch_begin(
2821 method.as_ptr() as u32,
2822 method.len() as u32,
2823 url.as_ptr() as u32,
2824 url.len() as u32,
2825 content_type.as_ptr() as u32,
2826 content_type.len() as u32,
2827 body.as_ptr() as u32,
2828 body.len() as u32,
2829 )
2830 }
2831}
2832
2833pub fn fetch_begin_get(url: &str) -> u32 {
2835 fetch_begin("GET", url, "", &[])
2836}
2837
2838pub fn fetch_state(handle: u32) -> u32 {
2840 unsafe { _api_fetch_state(handle) }
2841}
2842
2843pub fn fetch_status(handle: u32) -> u32 {
2845 unsafe { _api_fetch_status(handle) }
2846}
2847
2848pub fn fetch_recv_into(handle: u32, buf: &mut [u8]) -> i64 {
2858 unsafe { _api_fetch_recv(handle, buf.as_mut_ptr() as u32, buf.len() as u32) }
2859}
2860
2861pub fn fetch_recv(handle: u32) -> FetchChunk {
2866 let mut buf = vec![0u8; 64 * 1024];
2867 let n = fetch_recv_into(handle, &mut buf);
2868 match n {
2869 -1 => FetchChunk::Pending,
2870 -2 => FetchChunk::End,
2871 -3 | -4 => FetchChunk::Error,
2872 n if n >= 0 => {
2873 buf.truncate(n as usize);
2874 FetchChunk::Data(buf)
2875 }
2876 _ => FetchChunk::Error,
2877 }
2878}
2879
2880pub fn fetch_error(handle: u32) -> Option<String> {
2882 let mut buf = [0u8; 512];
2883 let n = unsafe { _api_fetch_error(handle, buf.as_mut_ptr() as u32, buf.len() as u32) };
2884 if n < 0 {
2885 None
2886 } else {
2887 Some(String::from_utf8_lossy(&buf[..n as usize]).into_owned())
2888 }
2889}
2890
2891pub fn fetch_abort(handle: u32) -> bool {
2896 unsafe { _api_fetch_abort(handle) != 0 }
2897}
2898
2899pub fn fetch_remove(handle: u32) {
2904 unsafe { _api_fetch_remove(handle) }
2905}
2906
2907pub fn load_module(url: &str) -> i32 {
2913 unsafe { _api_load_module(url.as_ptr() as u32, url.len() as u32) }
2914}
2915
2916pub fn hash_sha256(data: &[u8]) -> [u8; 32] {
2920 let mut out = [0u8; 32];
2921 unsafe {
2922 _api_hash_sha256(
2923 data.as_ptr() as u32,
2924 data.len() as u32,
2925 out.as_mut_ptr() as u32,
2926 );
2927 }
2928 out
2929}
2930
2931pub fn hash_sha256_hex(data: &[u8]) -> String {
2933 let hash = hash_sha256(data);
2934 let mut hex = String::with_capacity(64);
2935 for byte in &hash {
2936 hex.push(HEX_CHARS[(*byte >> 4) as usize]);
2937 hex.push(HEX_CHARS[(*byte & 0x0F) as usize]);
2938 }
2939 hex
2940}
2941
2942const HEX_CHARS: [char; 16] = [
2943 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
2944];
2945
2946pub fn base64_encode(data: &[u8]) -> String {
2950 let mut buf = vec![0u8; data.len() * 4 / 3 + 8];
2951 let len = unsafe {
2952 _api_base64_encode(
2953 data.as_ptr() as u32,
2954 data.len() as u32,
2955 buf.as_mut_ptr() as u32,
2956 buf.len() as u32,
2957 )
2958 };
2959 String::from_utf8_lossy(&buf[..len as usize]).to_string()
2960}
2961
2962pub fn base64_decode(encoded: &str) -> Vec<u8> {
2964 let mut buf = vec![0u8; encoded.len()];
2965 let len = unsafe {
2966 _api_base64_decode(
2967 encoded.as_ptr() as u32,
2968 encoded.len() as u32,
2969 buf.as_mut_ptr() as u32,
2970 buf.len() as u32,
2971 )
2972 };
2973 buf[..len as usize].to_vec()
2974}
2975
2976pub fn kv_store_set(key: &str, value: &[u8]) -> bool {
2981 let rc = unsafe {
2982 _api_kv_store_set(
2983 key.as_ptr() as u32,
2984 key.len() as u32,
2985 value.as_ptr() as u32,
2986 value.len() as u32,
2987 )
2988 };
2989 rc == 0
2990}
2991
2992pub fn kv_store_set_str(key: &str, value: &str) -> bool {
2994 kv_store_set(key, value.as_bytes())
2995}
2996
2997pub fn kv_store_get(key: &str) -> Option<Vec<u8>> {
3000 let mut buf = vec![0u8; 64 * 1024]; let rc = unsafe {
3002 _api_kv_store_get(
3003 key.as_ptr() as u32,
3004 key.len() as u32,
3005 buf.as_mut_ptr() as u32,
3006 buf.len() as u32,
3007 )
3008 };
3009 if rc < 0 {
3010 return None;
3011 }
3012 Some(buf[..rc as usize].to_vec())
3013}
3014
3015pub fn kv_store_get_str(key: &str) -> Option<String> {
3017 kv_store_get(key).map(|v| String::from_utf8_lossy(&v).into_owned())
3018}
3019
3020pub fn kv_store_delete(key: &str) -> bool {
3022 let rc = unsafe { _api_kv_store_delete(key.as_ptr() as u32, key.len() as u32) };
3023 rc == 0
3024}
3025
3026pub fn navigate(url: &str) -> i32 {
3032 unsafe { _api_navigate(url.as_ptr() as u32, url.len() as u32) }
3033}
3034
3035pub fn push_state(state: &[u8], title: &str, url: &str) {
3043 unsafe {
3044 _api_push_state(
3045 state.as_ptr() as u32,
3046 state.len() as u32,
3047 title.as_ptr() as u32,
3048 title.len() as u32,
3049 url.as_ptr() as u32,
3050 url.len() as u32,
3051 )
3052 }
3053}
3054
3055pub fn replace_state(state: &[u8], title: &str, url: &str) {
3058 unsafe {
3059 _api_replace_state(
3060 state.as_ptr() as u32,
3061 state.len() as u32,
3062 title.as_ptr() as u32,
3063 title.len() as u32,
3064 url.as_ptr() as u32,
3065 url.len() as u32,
3066 )
3067 }
3068}
3069
3070pub fn get_url() -> String {
3072 let mut buf = [0u8; 4096];
3073 let len = unsafe { _api_get_url(buf.as_mut_ptr() as u32, buf.len() as u32) };
3074 String::from_utf8_lossy(&buf[..len as usize]).to_string()
3075}
3076
3077pub fn get_state() -> Option<Vec<u8>> {
3080 let mut buf = vec![0u8; 64 * 1024]; let rc = unsafe { _api_get_state(buf.as_mut_ptr() as u32, buf.len() as u32) };
3082 if rc < 0 {
3083 return None;
3084 }
3085 Some(buf[..rc as usize].to_vec())
3086}
3087
3088pub fn history_length() -> u32 {
3090 unsafe { _api_history_length() }
3091}
3092
3093pub fn history_back() -> bool {
3095 unsafe { _api_history_back() == 1 }
3096}
3097
3098pub fn history_forward() -> bool {
3100 unsafe { _api_history_forward() == 1 }
3101}
3102
3103pub fn register_hyperlink(x: f32, y: f32, w: f32, h: f32, url: &str) -> i32 {
3111 unsafe { _api_register_hyperlink(x, y, w, h, url.as_ptr() as u32, url.len() as u32) }
3112}
3113
3114pub fn clear_hyperlinks() {
3116 unsafe { _api_clear_hyperlinks() }
3117}
3118
3119pub fn url_resolve(base: &str, relative: &str) -> Option<String> {
3124 let mut buf = [0u8; 4096];
3125 let rc = unsafe {
3126 _api_url_resolve(
3127 base.as_ptr() as u32,
3128 base.len() as u32,
3129 relative.as_ptr() as u32,
3130 relative.len() as u32,
3131 buf.as_mut_ptr() as u32,
3132 buf.len() as u32,
3133 )
3134 };
3135 if rc < 0 {
3136 return None;
3137 }
3138 Some(String::from_utf8_lossy(&buf[..rc as usize]).to_string())
3139}
3140
3141pub fn url_encode(input: &str) -> String {
3143 let mut buf = vec![0u8; input.len() * 3 + 4];
3144 let len = unsafe {
3145 _api_url_encode(
3146 input.as_ptr() as u32,
3147 input.len() as u32,
3148 buf.as_mut_ptr() as u32,
3149 buf.len() as u32,
3150 )
3151 };
3152 String::from_utf8_lossy(&buf[..len as usize]).to_string()
3153}
3154
3155pub fn url_decode(input: &str) -> String {
3157 let mut buf = vec![0u8; input.len() + 4];
3158 let len = unsafe {
3159 _api_url_decode(
3160 input.as_ptr() as u32,
3161 input.len() as u32,
3162 buf.as_mut_ptr() as u32,
3163 buf.len() as u32,
3164 )
3165 };
3166 String::from_utf8_lossy(&buf[..len as usize]).to_string()
3167}
3168
3169pub fn mouse_position() -> (f32, f32) {
3173 let packed = unsafe { _api_mouse_position() };
3174 let x = f32::from_bits((packed >> 32) as u32);
3175 let y = f32::from_bits((packed & 0xFFFF_FFFF) as u32);
3176 (x, y)
3177}
3178
3179pub fn mouse_button_down(button: u32) -> bool {
3182 unsafe { _api_mouse_button_down(button) != 0 }
3183}
3184
3185pub fn mouse_button_clicked(button: u32) -> bool {
3187 unsafe { _api_mouse_button_clicked(button) != 0 }
3188}
3189
3190pub fn key_down(key: u32) -> bool {
3193 unsafe { _api_key_down(key) != 0 }
3194}
3195
3196pub fn key_pressed(key: u32) -> bool {
3198 unsafe { _api_key_pressed(key) != 0 }
3199}
3200
3201pub fn scroll_delta() -> (f32, f32) {
3203 let packed = unsafe { _api_scroll_delta() };
3204 let x = f32::from_bits((packed >> 32) as u32);
3205 let y = f32::from_bits((packed & 0xFFFF_FFFF) as u32);
3206 (x, y)
3207}
3208
3209pub fn modifiers() -> u32 {
3211 unsafe { _api_modifiers() }
3212}
3213
3214pub fn shift_held() -> bool {
3216 modifiers() & 1 != 0
3217}
3218
3219pub fn ctrl_held() -> bool {
3221 modifiers() & 2 != 0
3222}
3223
3224pub fn alt_held() -> bool {
3226 modifiers() & 4 != 0
3227}
3228
3229pub const KEY_A: u32 = 0;
3232pub const KEY_B: u32 = 1;
3233pub const KEY_C: u32 = 2;
3234pub const KEY_D: u32 = 3;
3235pub const KEY_E: u32 = 4;
3236pub const KEY_F: u32 = 5;
3237pub const KEY_G: u32 = 6;
3238pub const KEY_H: u32 = 7;
3239pub const KEY_I: u32 = 8;
3240pub const KEY_J: u32 = 9;
3241pub const KEY_K: u32 = 10;
3242pub const KEY_L: u32 = 11;
3243pub const KEY_M: u32 = 12;
3244pub const KEY_N: u32 = 13;
3245pub const KEY_O: u32 = 14;
3246pub const KEY_P: u32 = 15;
3247pub const KEY_Q: u32 = 16;
3248pub const KEY_R: u32 = 17;
3249pub const KEY_S: u32 = 18;
3250pub const KEY_T: u32 = 19;
3251pub const KEY_U: u32 = 20;
3252pub const KEY_V: u32 = 21;
3253pub const KEY_W: u32 = 22;
3254pub const KEY_X: u32 = 23;
3255pub const KEY_Y: u32 = 24;
3256pub const KEY_Z: u32 = 25;
3257pub const KEY_0: u32 = 26;
3258pub const KEY_1: u32 = 27;
3259pub const KEY_2: u32 = 28;
3260pub const KEY_3: u32 = 29;
3261pub const KEY_4: u32 = 30;
3262pub const KEY_5: u32 = 31;
3263pub const KEY_6: u32 = 32;
3264pub const KEY_7: u32 = 33;
3265pub const KEY_8: u32 = 34;
3266pub const KEY_9: u32 = 35;
3267pub const KEY_ENTER: u32 = 36;
3268pub const KEY_ESCAPE: u32 = 37;
3269pub const KEY_TAB: u32 = 38;
3270pub const KEY_BACKSPACE: u32 = 39;
3271pub const KEY_DELETE: u32 = 40;
3272pub const KEY_SPACE: u32 = 41;
3273pub const KEY_UP: u32 = 42;
3274pub const KEY_DOWN: u32 = 43;
3275pub const KEY_LEFT: u32 = 44;
3276pub const KEY_RIGHT: u32 = 45;
3277pub const KEY_HOME: u32 = 46;
3278pub const KEY_END: u32 = 47;
3279pub const KEY_PAGE_UP: u32 = 48;
3280pub const KEY_PAGE_DOWN: u32 = 49;
3281
3282pub fn ui_button(id: u32, x: f32, y: f32, w: f32, h: f32, label: &str) -> bool {
3290 unsafe { _api_ui_button(id, x, y, w, h, label.as_ptr() as u32, label.len() as u32) != 0 }
3291}
3292
3293pub fn ui_checkbox(id: u32, x: f32, y: f32, label: &str, initial: bool) -> bool {
3297 unsafe {
3298 _api_ui_checkbox(
3299 id,
3300 x,
3301 y,
3302 label.as_ptr() as u32,
3303 label.len() as u32,
3304 if initial { 1 } else { 0 },
3305 ) != 0
3306 }
3307}
3308
3309pub fn ui_slider(id: u32, x: f32, y: f32, w: f32, min: f32, max: f32, initial: f32) -> f32 {
3313 unsafe { _api_ui_slider(id, x, y, w, min, max, initial) }
3314}
3315
3316pub fn ui_text_input(id: u32, x: f32, y: f32, w: f32, initial: &str) -> String {
3320 let mut buf = [0u8; 4096];
3321 let len = unsafe {
3322 _api_ui_text_input(
3323 id,
3324 x,
3325 y,
3326 w,
3327 initial.as_ptr() as u32,
3328 initial.len() as u32,
3329 buf.as_mut_ptr() as u32,
3330 buf.len() as u32,
3331 )
3332 };
3333 String::from_utf8_lossy(&buf[..len as usize]).to_string()
3334}