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_ui_text_area"]
570 fn _api_ui_text_area(
571 id: u32,
572 x: f32,
573 y: f32,
574 w: f32,
575 h: f32,
576 init_ptr: u32,
577 init_len: u32,
578 out_ptr: u32,
579 out_cap: u32,
580 ) -> u32;
581
582 #[link_name = "api_audio_play"]
585 fn _api_audio_play(data_ptr: u32, data_len: u32) -> i32;
586
587 #[link_name = "api_audio_play_url"]
588 fn _api_audio_play_url(url_ptr: u32, url_len: u32) -> i32;
589
590 #[link_name = "api_audio_detect_format"]
591 fn _api_audio_detect_format(data_ptr: u32, data_len: u32) -> u32;
592
593 #[link_name = "api_audio_play_with_format"]
594 fn _api_audio_play_with_format(data_ptr: u32, data_len: u32, format_hint: u32) -> i32;
595
596 #[link_name = "api_audio_last_url_content_type"]
597 fn _api_audio_last_url_content_type(out_ptr: u32, out_cap: u32) -> u32;
598
599 #[link_name = "api_audio_pause"]
600 fn _api_audio_pause();
601
602 #[link_name = "api_audio_resume"]
603 fn _api_audio_resume();
604
605 #[link_name = "api_audio_stop"]
606 fn _api_audio_stop();
607
608 #[link_name = "api_audio_set_volume"]
609 fn _api_audio_set_volume(level: f32);
610
611 #[link_name = "api_audio_get_volume"]
612 fn _api_audio_get_volume() -> f32;
613
614 #[link_name = "api_audio_is_playing"]
615 fn _api_audio_is_playing() -> u32;
616
617 #[link_name = "api_audio_position"]
618 fn _api_audio_position() -> u64;
619
620 #[link_name = "api_audio_seek"]
621 fn _api_audio_seek(position_ms: u64) -> i32;
622
623 #[link_name = "api_audio_duration"]
624 fn _api_audio_duration() -> u64;
625
626 #[link_name = "api_audio_set_loop"]
627 fn _api_audio_set_loop(enabled: u32);
628
629 #[link_name = "api_audio_channel_play"]
630 fn _api_audio_channel_play(channel: u32, data_ptr: u32, data_len: u32) -> i32;
631
632 #[link_name = "api_audio_channel_play_with_format"]
633 fn _api_audio_channel_play_with_format(
634 channel: u32,
635 data_ptr: u32,
636 data_len: u32,
637 format_hint: u32,
638 ) -> i32;
639
640 #[link_name = "api_audio_channel_stop"]
641 fn _api_audio_channel_stop(channel: u32);
642
643 #[link_name = "api_audio_channel_set_volume"]
644 fn _api_audio_channel_set_volume(channel: u32, level: f32);
645
646 #[link_name = "api_video_detect_format"]
649 fn _api_video_detect_format(data_ptr: u32, data_len: u32) -> u32;
650
651 #[link_name = "api_video_load"]
652 fn _api_video_load(data_ptr: u32, data_len: u32, format_hint: u32) -> i32;
653
654 #[link_name = "api_video_load_url"]
655 fn _api_video_load_url(url_ptr: u32, url_len: u32) -> i32;
656
657 #[link_name = "api_video_last_url_content_type"]
658 fn _api_video_last_url_content_type(out_ptr: u32, out_cap: u32) -> u32;
659
660 #[link_name = "api_video_hls_variant_count"]
661 fn _api_video_hls_variant_count() -> u32;
662
663 #[link_name = "api_video_hls_variant_url"]
664 fn _api_video_hls_variant_url(index: u32, out_ptr: u32, out_cap: u32) -> u32;
665
666 #[link_name = "api_video_hls_open_variant"]
667 fn _api_video_hls_open_variant(index: u32) -> i32;
668
669 #[link_name = "api_video_play"]
670 fn _api_video_play();
671
672 #[link_name = "api_video_pause"]
673 fn _api_video_pause();
674
675 #[link_name = "api_video_stop"]
676 fn _api_video_stop();
677
678 #[link_name = "api_video_seek"]
679 fn _api_video_seek(position_ms: u64) -> i32;
680
681 #[link_name = "api_video_position"]
682 fn _api_video_position() -> u64;
683
684 #[link_name = "api_video_duration"]
685 fn _api_video_duration() -> u64;
686
687 #[link_name = "api_video_render"]
688 fn _api_video_render(x: f32, y: f32, w: f32, h: f32) -> i32;
689
690 #[link_name = "api_video_set_volume"]
691 fn _api_video_set_volume(level: f32);
692
693 #[link_name = "api_video_get_volume"]
694 fn _api_video_get_volume() -> f32;
695
696 #[link_name = "api_video_set_loop"]
697 fn _api_video_set_loop(enabled: u32);
698
699 #[link_name = "api_video_set_pip"]
700 fn _api_video_set_pip(enabled: u32);
701
702 #[link_name = "api_subtitle_load_srt"]
703 fn _api_subtitle_load_srt(ptr: u32, len: u32) -> i32;
704
705 #[link_name = "api_subtitle_load_vtt"]
706 fn _api_subtitle_load_vtt(ptr: u32, len: u32) -> i32;
707
708 #[link_name = "api_subtitle_clear"]
709 fn _api_subtitle_clear();
710
711 #[link_name = "api_camera_open"]
714 fn _api_camera_open() -> i32;
715
716 #[link_name = "api_camera_close"]
717 fn _api_camera_close();
718
719 #[link_name = "api_camera_capture_frame"]
720 fn _api_camera_capture_frame(out_ptr: u32, out_cap: u32) -> u32;
721
722 #[link_name = "api_camera_frame_dimensions"]
723 fn _api_camera_frame_dimensions() -> u64;
724
725 #[link_name = "api_microphone_open"]
726 fn _api_microphone_open() -> i32;
727
728 #[link_name = "api_microphone_close"]
729 fn _api_microphone_close();
730
731 #[link_name = "api_microphone_sample_rate"]
732 fn _api_microphone_sample_rate() -> u32;
733
734 #[link_name = "api_microphone_read_samples"]
735 fn _api_microphone_read_samples(out_ptr: u32, max_samples: u32) -> u32;
736
737 #[link_name = "api_screen_capture"]
738 fn _api_screen_capture(out_ptr: u32, out_cap: u32) -> i32;
739
740 #[link_name = "api_screen_capture_dimensions"]
741 fn _api_screen_capture_dimensions() -> u64;
742
743 #[link_name = "api_media_pipeline_stats"]
744 fn _api_media_pipeline_stats() -> u64;
745
746 #[link_name = "api_gpu_create_buffer"]
749 fn _api_gpu_create_buffer(size_lo: u32, size_hi: u32, usage: u32) -> u32;
750
751 #[link_name = "api_gpu_create_texture"]
752 fn _api_gpu_create_texture(width: u32, height: u32) -> u32;
753
754 #[link_name = "api_gpu_create_shader"]
755 fn _api_gpu_create_shader(src_ptr: u32, src_len: u32) -> u32;
756
757 #[link_name = "api_gpu_create_render_pipeline"]
758 fn _api_gpu_create_render_pipeline(
759 shader: u32,
760 vs_ptr: u32,
761 vs_len: u32,
762 fs_ptr: u32,
763 fs_len: u32,
764 ) -> u32;
765
766 #[link_name = "api_gpu_create_compute_pipeline"]
767 fn _api_gpu_create_compute_pipeline(shader: u32, ep_ptr: u32, ep_len: u32) -> u32;
768
769 #[link_name = "api_gpu_write_buffer"]
770 fn _api_gpu_write_buffer(
771 handle: u32,
772 offset_lo: u32,
773 offset_hi: u32,
774 data_ptr: u32,
775 data_len: u32,
776 ) -> u32;
777
778 #[link_name = "api_gpu_draw"]
779 fn _api_gpu_draw(pipeline: u32, target: u32, vertex_count: u32, instance_count: u32) -> u32;
780
781 #[link_name = "api_gpu_dispatch_compute"]
782 fn _api_gpu_dispatch_compute(pipeline: u32, x: u32, y: u32, z: u32) -> u32;
783
784 #[link_name = "api_gpu_destroy_buffer"]
785 fn _api_gpu_destroy_buffer(handle: u32) -> u32;
786
787 #[link_name = "api_gpu_destroy_texture"]
788 fn _api_gpu_destroy_texture(handle: u32) -> u32;
789
790 #[link_name = "api_rtc_create_peer"]
793 fn _api_rtc_create_peer(stun_ptr: u32, stun_len: u32) -> u32;
794
795 #[link_name = "api_rtc_close_peer"]
796 fn _api_rtc_close_peer(peer_id: u32) -> u32;
797
798 #[link_name = "api_rtc_create_offer"]
799 fn _api_rtc_create_offer(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
800
801 #[link_name = "api_rtc_create_answer"]
802 fn _api_rtc_create_answer(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
803
804 #[link_name = "api_rtc_set_local_description"]
805 fn _api_rtc_set_local_description(
806 peer_id: u32,
807 sdp_ptr: u32,
808 sdp_len: u32,
809 is_offer: u32,
810 ) -> i32;
811
812 #[link_name = "api_rtc_set_remote_description"]
813 fn _api_rtc_set_remote_description(
814 peer_id: u32,
815 sdp_ptr: u32,
816 sdp_len: u32,
817 is_offer: u32,
818 ) -> i32;
819
820 #[link_name = "api_rtc_add_ice_candidate"]
821 fn _api_rtc_add_ice_candidate(peer_id: u32, cand_ptr: u32, cand_len: u32) -> i32;
822
823 #[link_name = "api_rtc_connection_state"]
824 fn _api_rtc_connection_state(peer_id: u32) -> u32;
825
826 #[link_name = "api_rtc_poll_ice_candidate"]
827 fn _api_rtc_poll_ice_candidate(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
828
829 #[link_name = "api_rtc_create_data_channel"]
830 fn _api_rtc_create_data_channel(
831 peer_id: u32,
832 label_ptr: u32,
833 label_len: u32,
834 ordered: u32,
835 ) -> u32;
836
837 #[link_name = "api_rtc_send"]
838 fn _api_rtc_send(
839 peer_id: u32,
840 channel_id: u32,
841 data_ptr: u32,
842 data_len: u32,
843 is_binary: u32,
844 ) -> i32;
845
846 #[link_name = "api_rtc_recv"]
847 fn _api_rtc_recv(peer_id: u32, channel_id: u32, out_ptr: u32, out_cap: u32) -> i64;
848
849 #[link_name = "api_rtc_poll_data_channel"]
850 fn _api_rtc_poll_data_channel(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
851
852 #[link_name = "api_rtc_add_track"]
853 fn _api_rtc_add_track(peer_id: u32, kind: u32) -> u32;
854
855 #[link_name = "api_rtc_poll_track"]
856 fn _api_rtc_poll_track(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
857
858 #[link_name = "api_rtc_signal_connect"]
859 fn _api_rtc_signal_connect(url_ptr: u32, url_len: u32) -> u32;
860
861 #[link_name = "api_rtc_signal_join_room"]
862 fn _api_rtc_signal_join_room(room_ptr: u32, room_len: u32) -> i32;
863
864 #[link_name = "api_rtc_signal_send"]
865 fn _api_rtc_signal_send(data_ptr: u32, data_len: u32) -> i32;
866
867 #[link_name = "api_rtc_signal_recv"]
868 fn _api_rtc_signal_recv(out_ptr: u32, out_cap: u32) -> i32;
869
870 #[link_name = "api_ws_connect"]
873 fn _api_ws_connect(url_ptr: u32, url_len: u32) -> u32;
874
875 #[link_name = "api_ws_send_text"]
876 fn _api_ws_send_text(id: u32, data_ptr: u32, data_len: u32) -> i32;
877
878 #[link_name = "api_ws_send_binary"]
879 fn _api_ws_send_binary(id: u32, data_ptr: u32, data_len: u32) -> i32;
880
881 #[link_name = "api_ws_recv"]
882 fn _api_ws_recv(id: u32, out_ptr: u32, out_cap: u32) -> i64;
883
884 #[link_name = "api_ws_ready_state"]
885 fn _api_ws_ready_state(id: u32) -> u32;
886
887 #[link_name = "api_ws_close"]
888 fn _api_ws_close(id: u32) -> i32;
889
890 #[link_name = "api_ws_remove"]
891 fn _api_ws_remove(id: u32);
892
893 #[link_name = "api_midi_input_count"]
896 fn _api_midi_input_count() -> u32;
897
898 #[link_name = "api_midi_output_count"]
899 fn _api_midi_output_count() -> u32;
900
901 #[link_name = "api_midi_input_name"]
902 fn _api_midi_input_name(index: u32, out_ptr: u32, out_cap: u32) -> u32;
903
904 #[link_name = "api_midi_output_name"]
905 fn _api_midi_output_name(index: u32, out_ptr: u32, out_cap: u32) -> u32;
906
907 #[link_name = "api_midi_open_input"]
908 fn _api_midi_open_input(index: u32) -> u32;
909
910 #[link_name = "api_midi_open_output"]
911 fn _api_midi_open_output(index: u32) -> u32;
912
913 #[link_name = "api_midi_send"]
914 fn _api_midi_send(handle: u32, data_ptr: u32, data_len: u32) -> i32;
915
916 #[link_name = "api_midi_recv"]
917 fn _api_midi_recv(handle: u32, out_ptr: u32, out_cap: u32) -> i32;
918
919 #[link_name = "api_midi_close"]
920 fn _api_midi_close(handle: u32);
921
922 #[link_name = "api_url_resolve"]
925 fn _api_url_resolve(
926 base_ptr: u32,
927 base_len: u32,
928 rel_ptr: u32,
929 rel_len: u32,
930 out_ptr: u32,
931 out_cap: u32,
932 ) -> i32;
933
934 #[link_name = "api_url_encode"]
935 fn _api_url_encode(input_ptr: u32, input_len: u32, out_ptr: u32, out_cap: u32) -> u32;
936
937 #[link_name = "api_url_decode"]
938 fn _api_url_decode(input_ptr: u32, input_len: u32, out_ptr: u32, out_cap: u32) -> u32;
939}
940
941pub fn log(msg: &str) {
945 unsafe { _api_log(msg.as_ptr() as u32, msg.len() as u32) }
946}
947
948pub fn warn(msg: &str) {
950 unsafe { _api_warn(msg.as_ptr() as u32, msg.len() as u32) }
951}
952
953pub fn error(msg: &str) {
955 unsafe { _api_error(msg.as_ptr() as u32, msg.len() as u32) }
956}
957
958pub fn get_location() -> String {
962 let mut buf = [0u8; 128];
963 let len = unsafe { _api_get_location(buf.as_mut_ptr() as u32, buf.len() as u32) };
964 String::from_utf8_lossy(&buf[..len as usize]).to_string()
965}
966
967pub struct UploadedFile {
971 pub name: String,
972 pub data: Vec<u8>,
973}
974
975pub fn upload_file() -> Option<UploadedFile> {
978 let mut name_buf = [0u8; 256];
979 let mut data_buf = vec![0u8; 1024 * 1024]; let result = unsafe {
982 _api_upload_file(
983 name_buf.as_mut_ptr() as u32,
984 name_buf.len() as u32,
985 data_buf.as_mut_ptr() as u32,
986 data_buf.len() as u32,
987 )
988 };
989
990 if result == 0 {
991 return None;
992 }
993
994 let name_len = (result >> 32) as usize;
995 let data_len = (result & 0xFFFF_FFFF) as usize;
996
997 Some(UploadedFile {
998 name: String::from_utf8_lossy(&name_buf[..name_len]).to_string(),
999 data: data_buf[..data_len].to_vec(),
1000 })
1001}
1002
1003pub struct FileMetadata {
1012 pub name: String,
1013 pub size: u64,
1014 pub mime: String,
1015 pub modified_ms: u64,
1016 pub is_dir: bool,
1017}
1018
1019pub struct FolderEntry {
1021 pub name: String,
1022 pub size: u64,
1023 pub is_dir: bool,
1024 pub handle: u32,
1025}
1026
1027pub fn file_pick(title: &str, filters: &str, multiple: bool) -> Vec<u32> {
1033 let mut buf = [0u32; 64];
1034 let n = unsafe {
1035 _api_file_pick(
1036 title.as_ptr() as u32,
1037 title.len() as u32,
1038 filters.as_ptr() as u32,
1039 filters.len() as u32,
1040 if multiple { 1 } else { 0 },
1041 buf.as_mut_ptr() as u32,
1042 (buf.len() * 4) as u32,
1043 )
1044 };
1045 if n <= 0 {
1046 return Vec::new();
1047 }
1048 buf[..n as usize].to_vec()
1049}
1050
1051pub fn folder_pick(title: &str) -> Option<u32> {
1056 let h = unsafe { _api_folder_pick(title.as_ptr() as u32, title.len() as u32) };
1057 if h == 0 {
1058 None
1059 } else {
1060 Some(h)
1061 }
1062}
1063
1064fn read_json_len(handle: u32, call: impl Fn(u32, u32, u32) -> i32) -> Option<Vec<u8>> {
1065 let mut buf = vec![0u8; 8 * 1024];
1066 let n = call(handle, buf.as_mut_ptr() as u32, buf.len() as u32);
1067 if n >= 0 {
1068 buf.truncate(n as usize);
1069 return Some(buf);
1070 }
1071 if n < -1 {
1073 let required = (-n) as usize;
1074 let mut big = vec![0u8; required];
1075 let n2 = call(handle, big.as_mut_ptr() as u32, big.len() as u32);
1076 if n2 >= 0 {
1077 big.truncate(n2 as usize);
1078 return Some(big);
1079 }
1080 }
1081 None
1082}
1083
1084pub fn folder_entries(handle: u32) -> Vec<FolderEntry> {
1090 let bytes = match read_json_len(handle, |h, p, c| unsafe { _api_folder_entries(h, p, c) }) {
1091 Some(b) => b,
1092 None => return Vec::new(),
1093 };
1094 parse_folder_entries(&bytes)
1095}
1096
1097fn parse_folder_entries(bytes: &[u8]) -> Vec<FolderEntry> {
1098 let s = core::str::from_utf8(bytes).unwrap_or("");
1101 let mut out = Vec::new();
1102 let mut rest = s.trim();
1103 if !rest.starts_with('[') {
1104 return out;
1105 }
1106 rest = &rest[1..];
1107 loop {
1108 rest = rest.trim_start_matches(|c: char| c.is_whitespace() || c == ',');
1109 if rest.starts_with(']') || rest.is_empty() {
1110 break;
1111 }
1112 let Some(end) = rest.find('}') else { break };
1113 let obj = &rest[..=end];
1114 rest = &rest[end + 1..];
1115 let name = json_str_field(obj, "\"name\":").unwrap_or_default();
1116 let size = json_num_field(obj, "\"size\":").unwrap_or(0);
1117 let is_dir = json_bool_field(obj, "\"is_dir\":").unwrap_or(false);
1118 let handle = json_num_field(obj, "\"handle\":").unwrap_or(0) as u32;
1119 out.push(FolderEntry {
1120 name,
1121 size,
1122 is_dir,
1123 handle,
1124 });
1125 }
1126 out
1127}
1128
1129fn json_str_field(obj: &str, key: &str) -> Option<String> {
1130 let idx = obj.find(key)?;
1131 let after = &obj[idx + key.len()..];
1132 let start = after.find('"')? + 1;
1133 let mut out = String::new();
1134 let bytes = after.as_bytes();
1135 let mut i = start;
1136 while i < bytes.len() {
1137 let c = bytes[i];
1138 if c == b'\\' && i + 1 < bytes.len() {
1139 match bytes[i + 1] {
1140 b'"' => out.push('"'),
1141 b'\\' => out.push('\\'),
1142 b'n' => out.push('\n'),
1143 b'r' => out.push('\r'),
1144 b't' => out.push('\t'),
1145 _ => out.push(bytes[i + 1] as char),
1146 }
1147 i += 2;
1148 } else if c == b'"' {
1149 return Some(out);
1150 } else {
1151 out.push(c as char);
1152 i += 1;
1153 }
1154 }
1155 None
1156}
1157
1158fn json_num_field(obj: &str, key: &str) -> Option<u64> {
1159 let idx = obj.find(key)?;
1160 let after = obj[idx + key.len()..].trim_start();
1161 let end = after
1162 .find(|c: char| !c.is_ascii_digit())
1163 .unwrap_or(after.len());
1164 after[..end].parse().ok()
1165}
1166
1167fn json_bool_field(obj: &str, key: &str) -> Option<bool> {
1168 let idx = obj.find(key)?;
1169 let after = obj[idx + key.len()..].trim_start();
1170 if after.starts_with("true") {
1171 Some(true)
1172 } else if after.starts_with("false") {
1173 Some(false)
1174 } else {
1175 None
1176 }
1177}
1178
1179pub fn file_read(handle: u32) -> Option<Vec<u8>> {
1184 let mut buf = vec![0u8; 64 * 1024];
1185 let n = unsafe { _api_file_read(handle, buf.as_mut_ptr() as u32, buf.len() as u32) };
1186 if n >= 0 {
1187 buf.truncate(n as usize);
1188 return Some(buf);
1189 }
1190 if n < -1 {
1191 let required = (-n) as usize;
1192 if required > 64 * 1024 * 1024 {
1193 return None;
1194 }
1195 let mut big = vec![0u8; required];
1196 let n2 = unsafe { _api_file_read(handle, big.as_mut_ptr() as u32, big.len() as u32) };
1197 if n2 >= 0 {
1198 big.truncate(n2 as usize);
1199 return Some(big);
1200 }
1201 }
1202 None
1203}
1204
1205pub fn file_read_range(handle: u32, offset: u64, len: u32) -> Option<Vec<u8>> {
1210 let mut buf = vec![0u8; len as usize];
1211 let n = unsafe {
1212 _api_file_read_range(
1213 handle,
1214 offset as u32,
1215 (offset >> 32) as u32,
1216 len,
1217 buf.as_mut_ptr() as u32,
1218 buf.len() as u32,
1219 )
1220 };
1221 if n < 0 {
1222 return None;
1223 }
1224 buf.truncate(n as usize);
1225 Some(buf)
1226}
1227
1228pub fn file_metadata(handle: u32) -> Option<FileMetadata> {
1230 let bytes = read_json_len(handle, |h, p, c| unsafe { _api_file_metadata(h, p, c) })?;
1231 let s = core::str::from_utf8(&bytes).ok()?;
1232 Some(FileMetadata {
1233 name: json_str_field(s, "\"name\":").unwrap_or_default(),
1234 size: json_num_field(s, "\"size\":").unwrap_or(0),
1235 mime: json_str_field(s, "\"mime\":").unwrap_or_default(),
1236 modified_ms: json_num_field(s, "\"modified_ms\":").unwrap_or(0),
1237 is_dir: json_bool_field(s, "\"is_dir\":").unwrap_or(false),
1238 })
1239}
1240
1241pub fn canvas_clear(r: u8, g: u8, b: u8, a: u8) {
1245 unsafe { _api_canvas_clear(r as u32, g as u32, b as u32, a as u32) }
1246}
1247
1248pub fn canvas_rect(x: f32, y: f32, w: f32, h: f32, r: u8, g: u8, b: u8, a: u8) {
1250 unsafe { _api_canvas_rect(x, y, w, h, r as u32, g as u32, b as u32, a as u32) }
1251}
1252
1253pub fn canvas_circle(cx: f32, cy: f32, radius: f32, r: u8, g: u8, b: u8, a: u8) {
1255 unsafe { _api_canvas_circle(cx, cy, radius, r as u32, g as u32, b as u32, a as u32) }
1256}
1257
1258pub fn canvas_text(x: f32, y: f32, size: f32, r: u8, g: u8, b: u8, a: u8, text: &str) {
1260 unsafe {
1261 _api_canvas_text(
1262 x,
1263 y,
1264 size,
1265 r as u32,
1266 g as u32,
1267 b as u32,
1268 a as u32,
1269 text.as_ptr() as u32,
1270 text.len() as u32,
1271 )
1272 }
1273}
1274
1275pub const FONT_STYLE_NORMAL: u32 = 0;
1277pub const FONT_STYLE_ITALIC: u32 = 1;
1279pub const FONT_STYLE_OBLIQUE: u32 = 2;
1281
1282pub const TEXT_ALIGN_LEFT: u32 = 0;
1284pub const TEXT_ALIGN_CENTER: u32 = 1;
1286pub const TEXT_ALIGN_RIGHT: u32 = 2;
1288
1289#[derive(Clone, Copy, Debug, Default)]
1291pub struct TextMetrics {
1292 pub width: f32,
1294 pub ascent: f32,
1296 pub descent: f32,
1298}
1299
1300#[allow(clippy::too_many_arguments)]
1309pub fn canvas_text_ex(
1310 x: f32,
1311 y: f32,
1312 size: f32,
1313 r: u8,
1314 g: u8,
1315 b: u8,
1316 a: u8,
1317 family: &str,
1318 weight: u32,
1319 style: u32,
1320 align: u32,
1321 text: &str,
1322) {
1323 unsafe {
1324 _api_canvas_text_ex(
1325 x,
1326 y,
1327 size,
1328 r as u32,
1329 g as u32,
1330 b as u32,
1331 a as u32,
1332 family.as_ptr() as u32,
1333 family.len() as u32,
1334 weight,
1335 style,
1336 align,
1337 text.as_ptr() as u32,
1338 text.len() as u32,
1339 )
1340 }
1341}
1342
1343pub fn canvas_measure_text(
1350 size: f32,
1351 family: &str,
1352 weight: u32,
1353 style: u32,
1354 text: &str,
1355) -> TextMetrics {
1356 let mut out = [0u8; 12];
1357 let ok = unsafe {
1358 _api_canvas_measure_text(
1359 size,
1360 family.as_ptr() as u32,
1361 family.len() as u32,
1362 weight,
1363 style,
1364 text.as_ptr() as u32,
1365 text.len() as u32,
1366 out.as_mut_ptr() as u32,
1367 )
1368 };
1369 if ok == 0 {
1370 return TextMetrics::default();
1371 }
1372 TextMetrics {
1373 width: f32::from_le_bytes([out[0], out[1], out[2], out[3]]),
1374 ascent: f32::from_le_bytes([out[4], out[5], out[6], out[7]]),
1375 descent: f32::from_le_bytes([out[8], out[9], out[10], out[11]]),
1376 }
1377}
1378
1379pub fn canvas_line(x1: f32, y1: f32, x2: f32, y2: f32, r: u8, g: u8, b: u8, a: u8, thickness: f32) {
1381 unsafe {
1382 _api_canvas_line(
1383 x1, y1, x2, y2, r as u32, g as u32, b as u32, a as u32, thickness,
1384 )
1385 }
1386}
1387
1388pub fn canvas_dimensions() -> (u32, u32) {
1390 let packed = unsafe { _api_canvas_dimensions() };
1391 ((packed >> 32) as u32, (packed & 0xFFFF_FFFF) as u32)
1392}
1393
1394pub fn canvas_image(x: f32, y: f32, w: f32, h: f32, data: &[u8]) {
1397 unsafe { _api_canvas_image(x, y, w, h, data.as_ptr() as u32, data.len() as u32) }
1398}
1399
1400pub fn canvas_rounded_rect(
1404 x: f32,
1405 y: f32,
1406 w: f32,
1407 h: f32,
1408 radius: f32,
1409 r: u8,
1410 g: u8,
1411 b: u8,
1412 a: u8,
1413) {
1414 unsafe { _api_canvas_rounded_rect(x, y, w, h, radius, r as u32, g as u32, b as u32, a as u32) }
1415}
1416
1417pub fn canvas_arc(
1419 cx: f32,
1420 cy: f32,
1421 radius: f32,
1422 start_angle: f32,
1423 end_angle: f32,
1424 r: u8,
1425 g: u8,
1426 b: u8,
1427 a: u8,
1428 thickness: f32,
1429) {
1430 unsafe {
1431 _api_canvas_arc(
1432 cx,
1433 cy,
1434 radius,
1435 start_angle,
1436 end_angle,
1437 r as u32,
1438 g as u32,
1439 b as u32,
1440 a as u32,
1441 thickness,
1442 )
1443 }
1444}
1445
1446pub fn canvas_bezier(
1448 x1: f32,
1449 y1: f32,
1450 cp1x: f32,
1451 cp1y: f32,
1452 cp2x: f32,
1453 cp2y: f32,
1454 x2: f32,
1455 y2: f32,
1456 r: u8,
1457 g: u8,
1458 b: u8,
1459 a: u8,
1460 thickness: f32,
1461) {
1462 unsafe {
1463 _api_canvas_bezier(
1464 x1, y1, cp1x, cp1y, cp2x, cp2y, x2, y2, r as u32, g as u32, b as u32, a as u32,
1465 thickness,
1466 )
1467 }
1468}
1469
1470pub const GRADIENT_LINEAR: u32 = 0;
1472pub const GRADIENT_RADIAL: u32 = 1;
1473
1474pub fn canvas_gradient(
1481 x: f32,
1482 y: f32,
1483 w: f32,
1484 h: f32,
1485 kind: u32,
1486 ax: f32,
1487 ay: f32,
1488 bx: f32,
1489 by: f32,
1490 stops: &[(f32, u8, u8, u8, u8)],
1491) {
1492 let mut buf = Vec::with_capacity(stops.len() * 8);
1493 for &(offset, r, g, b, a) in stops {
1494 buf.extend_from_slice(&offset.to_le_bytes());
1495 buf.push(r);
1496 buf.push(g);
1497 buf.push(b);
1498 buf.push(a);
1499 }
1500 unsafe {
1501 _api_canvas_gradient(
1502 x,
1503 y,
1504 w,
1505 h,
1506 kind,
1507 ax,
1508 ay,
1509 bx,
1510 by,
1511 buf.as_ptr() as u32,
1512 buf.len() as u32,
1513 )
1514 }
1515}
1516
1517pub fn canvas_save() {
1522 unsafe { _api_canvas_save() }
1523}
1524
1525pub fn canvas_restore() {
1527 unsafe { _api_canvas_restore() }
1528}
1529
1530pub fn canvas_transform(a: f32, b: f32, c: f32, d: f32, tx: f32, ty: f32) {
1541 unsafe { _api_canvas_transform(a, b, c, d, tx, ty) }
1542}
1543
1544pub fn canvas_clip(x: f32, y: f32, w: f32, h: f32) {
1547 unsafe { _api_canvas_clip(x, y, w, h) }
1548}
1549
1550pub fn canvas_opacity(alpha: f32) {
1553 unsafe { _api_canvas_opacity(alpha) }
1554}
1555
1556pub mod gpu_usage {
1560 pub const VERTEX: u32 = 0x0020;
1561 pub const INDEX: u32 = 0x0010;
1562 pub const UNIFORM: u32 = 0x0040;
1563 pub const STORAGE: u32 = 0x0080;
1564}
1565
1566pub fn gpu_create_buffer(size: u64, usage: u32) -> u32 {
1570 unsafe { _api_gpu_create_buffer(size as u32, (size >> 32) as u32, usage) }
1571}
1572
1573pub fn gpu_create_texture(width: u32, height: u32) -> u32 {
1575 unsafe { _api_gpu_create_texture(width, height) }
1576}
1577
1578pub fn gpu_create_shader(source: &str) -> u32 {
1580 unsafe { _api_gpu_create_shader(source.as_ptr() as u32, source.len() as u32) }
1581}
1582
1583pub fn gpu_create_pipeline(shader: u32, vertex_entry: &str, fragment_entry: &str) -> u32 {
1587 unsafe {
1588 _api_gpu_create_render_pipeline(
1589 shader,
1590 vertex_entry.as_ptr() as u32,
1591 vertex_entry.len() as u32,
1592 fragment_entry.as_ptr() as u32,
1593 fragment_entry.len() as u32,
1594 )
1595 }
1596}
1597
1598pub fn gpu_create_compute_pipeline(shader: u32, entry_point: &str) -> u32 {
1600 unsafe {
1601 _api_gpu_create_compute_pipeline(
1602 shader,
1603 entry_point.as_ptr() as u32,
1604 entry_point.len() as u32,
1605 )
1606 }
1607}
1608
1609pub fn gpu_write_buffer(handle: u32, offset: u64, data: &[u8]) -> bool {
1611 unsafe {
1612 _api_gpu_write_buffer(
1613 handle,
1614 offset as u32,
1615 (offset >> 32) as u32,
1616 data.as_ptr() as u32,
1617 data.len() as u32,
1618 ) != 0
1619 }
1620}
1621
1622pub fn gpu_draw(
1624 pipeline: u32,
1625 target_texture: u32,
1626 vertex_count: u32,
1627 instance_count: u32,
1628) -> bool {
1629 unsafe { _api_gpu_draw(pipeline, target_texture, vertex_count, instance_count) != 0 }
1630}
1631
1632pub fn gpu_dispatch_compute(pipeline: u32, x: u32, y: u32, z: u32) -> bool {
1634 unsafe { _api_gpu_dispatch_compute(pipeline, x, y, z) != 0 }
1635}
1636
1637pub fn gpu_destroy_buffer(handle: u32) -> bool {
1639 unsafe { _api_gpu_destroy_buffer(handle) != 0 }
1640}
1641
1642pub fn gpu_destroy_texture(handle: u32) -> bool {
1644 unsafe { _api_gpu_destroy_texture(handle) != 0 }
1645}
1646
1647pub fn storage_set(key: &str, value: &str) {
1651 unsafe {
1652 _api_storage_set(
1653 key.as_ptr() as u32,
1654 key.len() as u32,
1655 value.as_ptr() as u32,
1656 value.len() as u32,
1657 )
1658 }
1659}
1660
1661pub fn storage_get(key: &str) -> String {
1663 let mut buf = [0u8; 4096];
1664 let len = unsafe {
1665 _api_storage_get(
1666 key.as_ptr() as u32,
1667 key.len() as u32,
1668 buf.as_mut_ptr() as u32,
1669 buf.len() as u32,
1670 )
1671 };
1672 String::from_utf8_lossy(&buf[..len as usize]).to_string()
1673}
1674
1675pub fn storage_remove(key: &str) {
1677 unsafe { _api_storage_remove(key.as_ptr() as u32, key.len() as u32) }
1678}
1679
1680pub fn clipboard_write(text: &str) {
1684 unsafe { _api_clipboard_write(text.as_ptr() as u32, text.len() as u32) }
1685}
1686
1687pub fn clipboard_read() -> String {
1689 let mut buf = [0u8; 4096];
1690 let len = unsafe { _api_clipboard_read(buf.as_mut_ptr() as u32, buf.len() as u32) };
1691 String::from_utf8_lossy(&buf[..len as usize]).to_string()
1692}
1693
1694pub fn time_now_ms() -> u64 {
1698 unsafe { _api_time_now_ms() }
1699}
1700
1701pub fn set_timeout(callback_id: u32, delay_ms: u32) -> u32 {
1705 unsafe { _api_set_timeout(callback_id, delay_ms) }
1706}
1707
1708pub fn set_interval(callback_id: u32, interval_ms: u32) -> u32 {
1712 unsafe { _api_set_interval(callback_id, interval_ms) }
1713}
1714
1715pub fn clear_timer(timer_id: u32) {
1717 unsafe { _api_clear_timer(timer_id) }
1718}
1719
1720pub fn request_animation_frame(callback_id: u32) -> u32 {
1726 unsafe { _api_request_animation_frame(callback_id) }
1727}
1728
1729pub fn cancel_animation_frame(request_id: u32) {
1731 unsafe { _api_cancel_animation_frame(request_id) }
1732}
1733
1734pub fn on_event(event_type: &str, callback_id: u32) -> u32 {
1765 unsafe {
1766 _api_on_event(
1767 event_type.as_ptr() as u32,
1768 event_type.len() as u32,
1769 callback_id,
1770 )
1771 }
1772}
1773
1774pub fn off_event(listener_id: u32) -> bool {
1777 unsafe { _api_off_event(listener_id) != 0 }
1778}
1779
1780pub fn emit_event(event_type: &str, data: &[u8]) {
1784 unsafe {
1785 _api_emit_event(
1786 event_type.as_ptr() as u32,
1787 event_type.len() as u32,
1788 data.as_ptr() as u32,
1789 data.len() as u32,
1790 )
1791 }
1792}
1793
1794pub fn event_type() -> String {
1797 let len = unsafe { _api_event_type_len() } as usize;
1798 if len == 0 {
1799 return String::new();
1800 }
1801 let mut buf = vec![0u8; len];
1802 let written = unsafe { _api_event_type_read(buf.as_mut_ptr() as u32, len as u32) } as usize;
1803 buf.truncate(written);
1804 String::from_utf8_lossy(&buf).into_owned()
1805}
1806
1807pub fn event_data(out: &mut [u8]) -> usize {
1810 let cap = out.len() as u32;
1811 if cap == 0 {
1812 return 0;
1813 }
1814 unsafe { _api_event_data_read(out.as_mut_ptr() as u32, cap) as usize }
1815}
1816
1817pub fn event_data_into() -> Vec<u8> {
1819 let len = unsafe { _api_event_data_len() } as usize;
1820 if len == 0 {
1821 return Vec::new();
1822 }
1823 let mut buf = vec![0u8; len];
1824 let written = unsafe { _api_event_data_read(buf.as_mut_ptr() as u32, len as u32) } as usize;
1825 buf.truncate(written);
1826 buf
1827}
1828
1829pub fn random_u64() -> u64 {
1833 unsafe { _api_random() }
1834}
1835
1836pub fn random_f64() -> f64 {
1838 (random_u64() >> 11) as f64 / (1u64 << 53) as f64
1839}
1840
1841pub fn notify(title: &str, body: &str) {
1845 unsafe {
1846 _api_notify(
1847 title.as_ptr() as u32,
1848 title.len() as u32,
1849 body.as_ptr() as u32,
1850 body.len() as u32,
1851 )
1852 }
1853}
1854
1855#[repr(u32)]
1859#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1860pub enum AudioFormat {
1861 Unknown = 0,
1863 Wav = 1,
1864 Mp3 = 2,
1865 Ogg = 3,
1866 Flac = 4,
1867}
1868
1869impl From<u32> for AudioFormat {
1870 fn from(code: u32) -> Self {
1871 match code {
1872 1 => AudioFormat::Wav,
1873 2 => AudioFormat::Mp3,
1874 3 => AudioFormat::Ogg,
1875 4 => AudioFormat::Flac,
1876 _ => AudioFormat::Unknown,
1877 }
1878 }
1879}
1880
1881impl From<AudioFormat> for u32 {
1882 fn from(f: AudioFormat) -> u32 {
1883 f as u32
1884 }
1885}
1886
1887pub fn audio_play(data: &[u8]) -> i32 {
1890 unsafe { _api_audio_play(data.as_ptr() as u32, data.len() as u32) }
1891}
1892
1893pub fn audio_detect_format(data: &[u8]) -> AudioFormat {
1895 let code = unsafe { _api_audio_detect_format(data.as_ptr() as u32, data.len() as u32) };
1896 AudioFormat::from(code)
1897}
1898
1899pub fn audio_play_with_format(data: &[u8], format: AudioFormat) -> i32 {
1902 unsafe {
1903 _api_audio_play_with_format(data.as_ptr() as u32, data.len() as u32, u32::from(format))
1904 }
1905}
1906
1907pub fn audio_play_url(url: &str) -> i32 {
1912 unsafe { _api_audio_play_url(url.as_ptr() as u32, url.len() as u32) }
1913}
1914
1915pub fn audio_last_url_content_type() -> String {
1917 let mut buf = [0u8; 512];
1918 let len =
1919 unsafe { _api_audio_last_url_content_type(buf.as_mut_ptr() as u32, buf.len() as u32) };
1920 let n = (len as usize).min(buf.len());
1921 String::from_utf8_lossy(&buf[..n]).to_string()
1922}
1923
1924pub fn audio_pause() {
1926 unsafe { _api_audio_pause() }
1927}
1928
1929pub fn audio_resume() {
1931 unsafe { _api_audio_resume() }
1932}
1933
1934pub fn audio_stop() {
1936 unsafe { _api_audio_stop() }
1937}
1938
1939pub fn audio_set_volume(level: f32) {
1941 unsafe { _api_audio_set_volume(level) }
1942}
1943
1944pub fn audio_get_volume() -> f32 {
1946 unsafe { _api_audio_get_volume() }
1947}
1948
1949pub fn audio_is_playing() -> bool {
1951 unsafe { _api_audio_is_playing() != 0 }
1952}
1953
1954pub fn audio_position() -> u64 {
1956 unsafe { _api_audio_position() }
1957}
1958
1959pub fn audio_seek(position_ms: u64) -> i32 {
1961 unsafe { _api_audio_seek(position_ms) }
1962}
1963
1964pub fn audio_duration() -> u64 {
1967 unsafe { _api_audio_duration() }
1968}
1969
1970pub fn audio_set_loop(enabled: bool) {
1973 unsafe { _api_audio_set_loop(if enabled { 1 } else { 0 }) }
1974}
1975
1976pub fn audio_channel_play(channel: u32, data: &[u8]) -> i32 {
1982 unsafe { _api_audio_channel_play(channel, data.as_ptr() as u32, data.len() as u32) }
1983}
1984
1985pub fn audio_channel_play_with_format(channel: u32, data: &[u8], format: AudioFormat) -> i32 {
1987 unsafe {
1988 _api_audio_channel_play_with_format(
1989 channel,
1990 data.as_ptr() as u32,
1991 data.len() as u32,
1992 u32::from(format),
1993 )
1994 }
1995}
1996
1997pub fn audio_channel_stop(channel: u32) {
1999 unsafe { _api_audio_channel_stop(channel) }
2000}
2001
2002pub fn audio_channel_set_volume(channel: u32, level: f32) {
2004 unsafe { _api_audio_channel_set_volume(channel, level) }
2005}
2006
2007#[repr(u32)]
2011#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2012pub enum VideoFormat {
2013 Unknown = 0,
2014 Mp4 = 1,
2015 Webm = 2,
2016 Av1 = 3,
2017}
2018
2019impl From<u32> for VideoFormat {
2020 fn from(code: u32) -> Self {
2021 match code {
2022 1 => VideoFormat::Mp4,
2023 2 => VideoFormat::Webm,
2024 3 => VideoFormat::Av1,
2025 _ => VideoFormat::Unknown,
2026 }
2027 }
2028}
2029
2030impl From<VideoFormat> for u32 {
2031 fn from(f: VideoFormat) -> u32 {
2032 f as u32
2033 }
2034}
2035
2036pub fn video_detect_format(data: &[u8]) -> VideoFormat {
2038 let code = unsafe { _api_video_detect_format(data.as_ptr() as u32, data.len() as u32) };
2039 VideoFormat::from(code)
2040}
2041
2042pub fn video_load(data: &[u8]) -> i32 {
2045 unsafe {
2046 _api_video_load(
2047 data.as_ptr() as u32,
2048 data.len() as u32,
2049 VideoFormat::Unknown as u32,
2050 )
2051 }
2052}
2053
2054pub fn video_load_with_format(data: &[u8], format: VideoFormat) -> i32 {
2056 unsafe { _api_video_load(data.as_ptr() as u32, data.len() as u32, u32::from(format)) }
2057}
2058
2059pub fn video_load_url(url: &str) -> i32 {
2061 unsafe { _api_video_load_url(url.as_ptr() as u32, url.len() as u32) }
2062}
2063
2064pub fn video_last_url_content_type() -> String {
2066 let mut buf = [0u8; 512];
2067 let len =
2068 unsafe { _api_video_last_url_content_type(buf.as_mut_ptr() as u32, buf.len() as u32) };
2069 let n = (len as usize).min(buf.len());
2070 String::from_utf8_lossy(&buf[..n]).to_string()
2071}
2072
2073pub fn video_hls_variant_count() -> u32 {
2075 unsafe { _api_video_hls_variant_count() }
2076}
2077
2078pub fn video_hls_variant_url(index: u32) -> String {
2080 let mut buf = [0u8; 2048];
2081 let len =
2082 unsafe { _api_video_hls_variant_url(index, buf.as_mut_ptr() as u32, buf.len() as u32) };
2083 let n = (len as usize).min(buf.len());
2084 String::from_utf8_lossy(&buf[..n]).to_string()
2085}
2086
2087pub fn video_hls_open_variant(index: u32) -> i32 {
2089 unsafe { _api_video_hls_open_variant(index) }
2090}
2091
2092pub fn video_play() {
2093 unsafe { _api_video_play() }
2094}
2095
2096pub fn video_pause() {
2097 unsafe { _api_video_pause() }
2098}
2099
2100pub fn video_stop() {
2101 unsafe { _api_video_stop() }
2102}
2103
2104pub fn video_seek(position_ms: u64) -> i32 {
2105 unsafe { _api_video_seek(position_ms) }
2106}
2107
2108pub fn video_position() -> u64 {
2109 unsafe { _api_video_position() }
2110}
2111
2112pub fn video_duration() -> u64 {
2113 unsafe { _api_video_duration() }
2114}
2115
2116pub fn video_render(x: f32, y: f32, w: f32, h: f32) -> i32 {
2118 unsafe { _api_video_render(x, y, w, h) }
2119}
2120
2121pub fn video_set_volume(level: f32) {
2123 unsafe { _api_video_set_volume(level) }
2124}
2125
2126pub fn video_get_volume() -> f32 {
2127 unsafe { _api_video_get_volume() }
2128}
2129
2130pub fn video_set_loop(enabled: bool) {
2131 unsafe { _api_video_set_loop(if enabled { 1 } else { 0 }) }
2132}
2133
2134pub fn video_set_pip(enabled: bool) {
2136 unsafe { _api_video_set_pip(if enabled { 1 } else { 0 }) }
2137}
2138
2139pub fn subtitle_load_srt(text: &str) -> i32 {
2141 unsafe { _api_subtitle_load_srt(text.as_ptr() as u32, text.len() as u32) }
2142}
2143
2144pub fn subtitle_load_vtt(text: &str) -> i32 {
2146 unsafe { _api_subtitle_load_vtt(text.as_ptr() as u32, text.len() as u32) }
2147}
2148
2149pub fn subtitle_clear() {
2150 unsafe { _api_subtitle_clear() }
2151}
2152
2153pub fn camera_open() -> i32 {
2159 unsafe { _api_camera_open() }
2160}
2161
2162pub fn camera_close() {
2164 unsafe { _api_camera_close() }
2165}
2166
2167pub fn camera_capture_frame(out: &mut [u8]) -> u32 {
2170 unsafe { _api_camera_capture_frame(out.as_mut_ptr() as u32, out.len() as u32) }
2171}
2172
2173pub fn camera_frame_dimensions() -> (u32, u32) {
2175 let packed = unsafe { _api_camera_frame_dimensions() };
2176 let w = (packed >> 32) as u32;
2177 let h = packed as u32;
2178 (w, h)
2179}
2180
2181pub fn microphone_open() -> i32 {
2185 unsafe { _api_microphone_open() }
2186}
2187
2188pub fn microphone_close() {
2189 unsafe { _api_microphone_close() }
2190}
2191
2192pub fn microphone_sample_rate() -> u32 {
2194 unsafe { _api_microphone_sample_rate() }
2195}
2196
2197pub fn microphone_read_samples(out: &mut [f32]) -> u32 {
2200 unsafe { _api_microphone_read_samples(out.as_mut_ptr() as u32, out.len() as u32) }
2201}
2202
2203pub fn screen_capture(out: &mut [u8]) -> Result<usize, i32> {
2207 let n = unsafe { _api_screen_capture(out.as_mut_ptr() as u32, out.len() as u32) };
2208 if n >= 0 {
2209 Ok(n as usize)
2210 } else {
2211 Err(n)
2212 }
2213}
2214
2215pub fn screen_capture_dimensions() -> (u32, u32) {
2217 let packed = unsafe { _api_screen_capture_dimensions() };
2218 let w = (packed >> 32) as u32;
2219 let h = packed as u32;
2220 (w, h)
2221}
2222
2223pub fn media_pipeline_stats() -> (u64, u32) {
2226 let packed = unsafe { _api_media_pipeline_stats() };
2227 let camera_frames = packed >> 32;
2228 let mic_ring = packed as u32;
2229 (camera_frames, mic_ring)
2230}
2231
2232pub const RTC_STATE_NEW: u32 = 0;
2236pub const RTC_STATE_CONNECTING: u32 = 1;
2238pub const RTC_STATE_CONNECTED: u32 = 2;
2240pub const RTC_STATE_DISCONNECTED: u32 = 3;
2242pub const RTC_STATE_FAILED: u32 = 4;
2244pub const RTC_STATE_CLOSED: u32 = 5;
2246
2247pub const RTC_TRACK_AUDIO: u32 = 0;
2249pub const RTC_TRACK_VIDEO: u32 = 1;
2251
2252pub struct RtcMessage {
2254 pub channel_id: u32,
2256 pub is_binary: bool,
2258 pub data: Vec<u8>,
2260}
2261
2262impl RtcMessage {
2263 pub fn text(&self) -> String {
2265 String::from_utf8_lossy(&self.data).to_string()
2266 }
2267}
2268
2269pub struct RtcDataChannelInfo {
2271 pub channel_id: u32,
2273 pub label: String,
2275}
2276
2277pub fn rtc_create_peer(stun_servers: &str) -> u32 {
2284 unsafe { _api_rtc_create_peer(stun_servers.as_ptr() as u32, stun_servers.len() as u32) }
2285}
2286
2287pub fn rtc_close_peer(peer_id: u32) -> bool {
2289 unsafe { _api_rtc_close_peer(peer_id) != 0 }
2290}
2291
2292pub fn rtc_create_offer(peer_id: u32) -> Result<String, i32> {
2296 let mut buf = vec![0u8; 16 * 1024];
2297 let n = unsafe { _api_rtc_create_offer(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
2298 if n < 0 {
2299 Err(n)
2300 } else {
2301 Ok(String::from_utf8_lossy(&buf[..n as usize]).to_string())
2302 }
2303}
2304
2305pub fn rtc_create_answer(peer_id: u32) -> Result<String, i32> {
2307 let mut buf = vec![0u8; 16 * 1024];
2308 let n = unsafe { _api_rtc_create_answer(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
2309 if n < 0 {
2310 Err(n)
2311 } else {
2312 Ok(String::from_utf8_lossy(&buf[..n as usize]).to_string())
2313 }
2314}
2315
2316pub fn rtc_set_local_description(peer_id: u32, sdp: &str, is_offer: bool) -> i32 {
2320 unsafe {
2321 _api_rtc_set_local_description(
2322 peer_id,
2323 sdp.as_ptr() as u32,
2324 sdp.len() as u32,
2325 if is_offer { 1 } else { 0 },
2326 )
2327 }
2328}
2329
2330pub fn rtc_set_remote_description(peer_id: u32, sdp: &str, is_offer: bool) -> i32 {
2332 unsafe {
2333 _api_rtc_set_remote_description(
2334 peer_id,
2335 sdp.as_ptr() as u32,
2336 sdp.len() as u32,
2337 if is_offer { 1 } else { 0 },
2338 )
2339 }
2340}
2341
2342pub fn rtc_add_ice_candidate(peer_id: u32, candidate_json: &str) -> i32 {
2344 unsafe {
2345 _api_rtc_add_ice_candidate(
2346 peer_id,
2347 candidate_json.as_ptr() as u32,
2348 candidate_json.len() as u32,
2349 )
2350 }
2351}
2352
2353pub fn rtc_connection_state(peer_id: u32) -> u32 {
2355 unsafe { _api_rtc_connection_state(peer_id) }
2356}
2357
2358pub fn rtc_poll_ice_candidate(peer_id: u32) -> Option<String> {
2361 let mut buf = vec![0u8; 4096];
2362 let n =
2363 unsafe { _api_rtc_poll_ice_candidate(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
2364 if n <= 0 {
2365 None
2366 } else {
2367 Some(String::from_utf8_lossy(&buf[..n as usize]).to_string())
2368 }
2369}
2370
2371pub fn rtc_create_data_channel(peer_id: u32, label: &str, ordered: bool) -> u32 {
2376 unsafe {
2377 _api_rtc_create_data_channel(
2378 peer_id,
2379 label.as_ptr() as u32,
2380 label.len() as u32,
2381 if ordered { 1 } else { 0 },
2382 )
2383 }
2384}
2385
2386pub fn rtc_send_text(peer_id: u32, channel_id: u32, text: &str) -> i32 {
2388 unsafe {
2389 _api_rtc_send(
2390 peer_id,
2391 channel_id,
2392 text.as_ptr() as u32,
2393 text.len() as u32,
2394 0,
2395 )
2396 }
2397}
2398
2399pub fn rtc_send_binary(peer_id: u32, channel_id: u32, data: &[u8]) -> 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 1,
2408 )
2409 }
2410}
2411
2412pub fn rtc_send(peer_id: u32, channel_id: u32, data: &[u8], is_binary: bool) -> i32 {
2414 unsafe {
2415 _api_rtc_send(
2416 peer_id,
2417 channel_id,
2418 data.as_ptr() as u32,
2419 data.len() as u32,
2420 if is_binary { 1 } else { 0 },
2421 )
2422 }
2423}
2424
2425pub fn rtc_recv(peer_id: u32, channel_id: u32) -> Option<RtcMessage> {
2430 let mut buf = vec![0u8; 64 * 1024];
2431 let packed = unsafe {
2432 _api_rtc_recv(
2433 peer_id,
2434 channel_id,
2435 buf.as_mut_ptr() as u32,
2436 buf.len() as u32,
2437 )
2438 };
2439 if packed <= 0 {
2440 return None;
2441 }
2442 let packed = packed as u64;
2443 let data_len = (packed & 0xFFFF_FFFF) as usize;
2444 let is_binary = (packed >> 32) & 1 != 0;
2445 let ch = (packed >> 48) as u32;
2446 Some(RtcMessage {
2447 channel_id: ch,
2448 is_binary,
2449 data: buf[..data_len].to_vec(),
2450 })
2451}
2452
2453pub fn rtc_poll_data_channel(peer_id: u32) -> Option<RtcDataChannelInfo> {
2457 let mut buf = vec![0u8; 1024];
2458 let n =
2459 unsafe { _api_rtc_poll_data_channel(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
2460 if n <= 0 {
2461 return None;
2462 }
2463 let info = String::from_utf8_lossy(&buf[..n as usize]).to_string();
2464 let (id_str, label) = info.split_once(':').unwrap_or(("0", ""));
2465 Some(RtcDataChannelInfo {
2466 channel_id: id_str.parse().unwrap_or(0),
2467 label: label.to_string(),
2468 })
2469}
2470
2471pub fn rtc_add_track(peer_id: u32, kind: u32) -> u32 {
2476 unsafe { _api_rtc_add_track(peer_id, kind) }
2477}
2478
2479pub struct RtcTrackInfo {
2481 pub kind: u32,
2483 pub id: String,
2485 pub stream_id: String,
2487}
2488
2489pub fn rtc_poll_track(peer_id: u32) -> Option<RtcTrackInfo> {
2493 let mut buf = vec![0u8; 1024];
2494 let n = unsafe { _api_rtc_poll_track(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
2495 if n <= 0 {
2496 return None;
2497 }
2498 let info = String::from_utf8_lossy(&buf[..n as usize]).to_string();
2499 let mut parts = info.splitn(3, ':');
2500 let kind = parts.next().unwrap_or("2").parse().unwrap_or(2);
2501 let id = parts.next().unwrap_or("").to_string();
2502 let stream_id = parts.next().unwrap_or("").to_string();
2503 Some(RtcTrackInfo {
2504 kind,
2505 id,
2506 stream_id,
2507 })
2508}
2509
2510pub fn rtc_signal_connect(url: &str) -> bool {
2514 unsafe { _api_rtc_signal_connect(url.as_ptr() as u32, url.len() as u32) != 0 }
2515}
2516
2517pub fn rtc_signal_join_room(room: &str) -> i32 {
2519 unsafe { _api_rtc_signal_join_room(room.as_ptr() as u32, room.len() as u32) }
2520}
2521
2522pub fn rtc_signal_send(data: &[u8]) -> i32 {
2524 unsafe { _api_rtc_signal_send(data.as_ptr() as u32, data.len() as u32) }
2525}
2526
2527pub fn rtc_signal_recv() -> Option<Vec<u8>> {
2529 let mut buf = vec![0u8; 16 * 1024];
2530 let n = unsafe { _api_rtc_signal_recv(buf.as_mut_ptr() as u32, buf.len() as u32) };
2531 if n <= 0 {
2532 None
2533 } else {
2534 Some(buf[..n as usize].to_vec())
2535 }
2536}
2537
2538pub const WS_CONNECTING: u32 = 0;
2542pub const WS_OPEN: u32 = 1;
2544pub const WS_CLOSING: u32 = 2;
2546pub const WS_CLOSED: u32 = 3;
2548
2549pub struct WsMessage {
2551 pub is_binary: bool,
2553 pub data: Vec<u8>,
2555}
2556
2557impl WsMessage {
2558 pub fn text(&self) -> String {
2560 String::from_utf8_lossy(&self.data).to_string()
2561 }
2562}
2563
2564pub fn ws_connect(url: &str) -> u32 {
2570 unsafe { _api_ws_connect(url.as_ptr() as u32, url.len() as u32) }
2571}
2572
2573pub fn ws_send_text(id: u32, text: &str) -> i32 {
2577 unsafe { _api_ws_send_text(id, text.as_ptr() as u32, text.len() as u32) }
2578}
2579
2580pub fn ws_send_binary(id: u32, data: &[u8]) -> i32 {
2584 unsafe { _api_ws_send_binary(id, data.as_ptr() as u32, data.len() as u32) }
2585}
2586
2587pub fn ws_recv(id: u32) -> Option<WsMessage> {
2593 let mut buf = vec![0u8; 64 * 1024];
2594 let result = unsafe { _api_ws_recv(id, buf.as_mut_ptr() as u32, buf.len() as u32) };
2595 if result < 0 {
2596 return None;
2597 }
2598 let len = (result & 0xFFFF_FFFF) as usize;
2599 let is_binary = (result >> 32) & 1 == 1;
2600 Some(WsMessage {
2601 is_binary,
2602 data: buf[..len].to_vec(),
2603 })
2604}
2605
2606pub fn ws_ready_state(id: u32) -> u32 {
2610 unsafe { _api_ws_ready_state(id) }
2611}
2612
2613pub fn ws_close(id: u32) -> i32 {
2620 unsafe { _api_ws_close(id) }
2621}
2622
2623pub fn ws_remove(id: u32) {
2628 unsafe { _api_ws_remove(id) }
2629}
2630
2631pub fn midi_input_count() -> u32 {
2635 unsafe { _api_midi_input_count() }
2636}
2637
2638pub fn midi_output_count() -> u32 {
2640 unsafe { _api_midi_output_count() }
2641}
2642
2643pub fn midi_input_name(index: u32) -> String {
2647 let mut buf = [0u8; 128];
2648 let len = unsafe { _api_midi_input_name(index, buf.as_mut_ptr() as u32, buf.len() as u32) };
2649 String::from_utf8_lossy(&buf[..len as usize]).to_string()
2650}
2651
2652pub fn midi_output_name(index: u32) -> String {
2656 let mut buf = [0u8; 128];
2657 let len = unsafe { _api_midi_output_name(index, buf.as_mut_ptr() as u32, buf.len() as u32) };
2658 String::from_utf8_lossy(&buf[..len as usize]).to_string()
2659}
2660
2661pub fn midi_open_input(index: u32) -> u32 {
2666 unsafe { _api_midi_open_input(index) }
2667}
2668
2669pub fn midi_open_output(index: u32) -> u32 {
2673 unsafe { _api_midi_open_output(index) }
2674}
2675
2676pub fn midi_send(handle: u32, data: &[u8]) -> i32 {
2680 unsafe { _api_midi_send(handle, data.as_ptr() as u32, data.len() as u32) }
2681}
2682
2683pub fn midi_recv(handle: u32) -> Option<Vec<u8>> {
2690 let mut buf = [0u8; 256];
2691 let n = unsafe { _api_midi_recv(handle, buf.as_mut_ptr() as u32, buf.len() as u32) };
2692 if n >= 0 {
2693 return Some(buf[..n as usize].to_vec());
2694 }
2695 if n == -2 {
2697 let mut big = vec![0u8; 64 * 1024];
2698 let n2 = unsafe { _api_midi_recv(handle, big.as_mut_ptr() as u32, big.len() as u32) };
2699 if n2 >= 0 {
2700 big.truncate(n2 as usize);
2701 return Some(big);
2702 }
2703 }
2704 None
2705}
2706
2707pub fn midi_close(handle: u32) {
2709 unsafe { _api_midi_close(handle) }
2710}
2711
2712pub struct FetchResponse {
2716 pub status: u32,
2717 pub body: Vec<u8>,
2718}
2719
2720impl FetchResponse {
2721 pub fn text(&self) -> String {
2723 String::from_utf8_lossy(&self.body).to_string()
2724 }
2725}
2726
2727pub fn fetch(
2733 method: &str,
2734 url: &str,
2735 content_type: &str,
2736 body: &[u8],
2737) -> Result<FetchResponse, i64> {
2738 let mut out_buf = vec![0u8; 4 * 1024 * 1024]; let result = unsafe {
2740 _api_fetch(
2741 method.as_ptr() as u32,
2742 method.len() as u32,
2743 url.as_ptr() as u32,
2744 url.len() as u32,
2745 content_type.as_ptr() as u32,
2746 content_type.len() as u32,
2747 body.as_ptr() as u32,
2748 body.len() as u32,
2749 out_buf.as_mut_ptr() as u32,
2750 out_buf.len() as u32,
2751 )
2752 };
2753 if result < 0 {
2754 return Err(result);
2755 }
2756 let status = (result >> 32) as u32;
2757 let body_len = (result & 0xFFFF_FFFF) as usize;
2758 Ok(FetchResponse {
2759 status,
2760 body: out_buf[..body_len].to_vec(),
2761 })
2762}
2763
2764pub fn fetch_get(url: &str) -> Result<FetchResponse, i64> {
2766 fetch("GET", url, "", &[])
2767}
2768
2769pub fn fetch_post(url: &str, content_type: &str, body: &[u8]) -> Result<FetchResponse, i64> {
2771 fetch("POST", url, content_type, body)
2772}
2773
2774pub fn fetch_post_proto(url: &str, msg: &proto::ProtoEncoder) -> Result<FetchResponse, i64> {
2776 fetch("POST", url, "application/protobuf", msg.as_bytes())
2777}
2778
2779pub fn fetch_put(url: &str, content_type: &str, body: &[u8]) -> Result<FetchResponse, i64> {
2781 fetch("PUT", url, content_type, body)
2782}
2783
2784pub fn fetch_delete(url: &str) -> Result<FetchResponse, i64> {
2786 fetch("DELETE", url, "", &[])
2787}
2788
2789pub const FETCH_PENDING: u32 = 0;
2799pub const FETCH_STREAMING: u32 = 1;
2801pub const FETCH_DONE: u32 = 2;
2803pub const FETCH_ERROR: u32 = 3;
2805pub const FETCH_ABORTED: u32 = 4;
2807
2808pub enum FetchChunk {
2810 Data(Vec<u8>),
2813 Pending,
2816 End,
2818 Error,
2821}
2822
2823pub fn fetch_begin(method: &str, url: &str, content_type: &str, body: &[u8]) -> u32 {
2832 unsafe {
2833 _api_fetch_begin(
2834 method.as_ptr() as u32,
2835 method.len() as u32,
2836 url.as_ptr() as u32,
2837 url.len() as u32,
2838 content_type.as_ptr() as u32,
2839 content_type.len() as u32,
2840 body.as_ptr() as u32,
2841 body.len() as u32,
2842 )
2843 }
2844}
2845
2846pub fn fetch_begin_get(url: &str) -> u32 {
2848 fetch_begin("GET", url, "", &[])
2849}
2850
2851pub fn fetch_state(handle: u32) -> u32 {
2853 unsafe { _api_fetch_state(handle) }
2854}
2855
2856pub fn fetch_status(handle: u32) -> u32 {
2858 unsafe { _api_fetch_status(handle) }
2859}
2860
2861pub fn fetch_recv_into(handle: u32, buf: &mut [u8]) -> i64 {
2871 unsafe { _api_fetch_recv(handle, buf.as_mut_ptr() as u32, buf.len() as u32) }
2872}
2873
2874pub fn fetch_recv(handle: u32) -> FetchChunk {
2879 let mut buf = vec![0u8; 64 * 1024];
2880 let n = fetch_recv_into(handle, &mut buf);
2881 match n {
2882 -1 => FetchChunk::Pending,
2883 -2 => FetchChunk::End,
2884 -3 | -4 => FetchChunk::Error,
2885 n if n >= 0 => {
2886 buf.truncate(n as usize);
2887 FetchChunk::Data(buf)
2888 }
2889 _ => FetchChunk::Error,
2890 }
2891}
2892
2893pub fn fetch_error(handle: u32) -> Option<String> {
2895 let mut buf = [0u8; 512];
2896 let n = unsafe { _api_fetch_error(handle, buf.as_mut_ptr() as u32, buf.len() as u32) };
2897 if n < 0 {
2898 None
2899 } else {
2900 Some(String::from_utf8_lossy(&buf[..n as usize]).into_owned())
2901 }
2902}
2903
2904pub fn fetch_abort(handle: u32) -> bool {
2909 unsafe { _api_fetch_abort(handle) != 0 }
2910}
2911
2912pub fn fetch_remove(handle: u32) {
2917 unsafe { _api_fetch_remove(handle) }
2918}
2919
2920pub fn load_module(url: &str) -> i32 {
2926 unsafe { _api_load_module(url.as_ptr() as u32, url.len() as u32) }
2927}
2928
2929pub fn hash_sha256(data: &[u8]) -> [u8; 32] {
2933 let mut out = [0u8; 32];
2934 unsafe {
2935 _api_hash_sha256(
2936 data.as_ptr() as u32,
2937 data.len() as u32,
2938 out.as_mut_ptr() as u32,
2939 );
2940 }
2941 out
2942}
2943
2944pub fn hash_sha256_hex(data: &[u8]) -> String {
2946 let hash = hash_sha256(data);
2947 let mut hex = String::with_capacity(64);
2948 for byte in &hash {
2949 hex.push(HEX_CHARS[(*byte >> 4) as usize]);
2950 hex.push(HEX_CHARS[(*byte & 0x0F) as usize]);
2951 }
2952 hex
2953}
2954
2955const HEX_CHARS: [char; 16] = [
2956 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
2957];
2958
2959pub fn base64_encode(data: &[u8]) -> String {
2963 let mut buf = vec![0u8; data.len() * 4 / 3 + 8];
2964 let len = unsafe {
2965 _api_base64_encode(
2966 data.as_ptr() as u32,
2967 data.len() as u32,
2968 buf.as_mut_ptr() as u32,
2969 buf.len() as u32,
2970 )
2971 };
2972 String::from_utf8_lossy(&buf[..len as usize]).to_string()
2973}
2974
2975pub fn base64_decode(encoded: &str) -> Vec<u8> {
2977 let mut buf = vec![0u8; encoded.len()];
2978 let len = unsafe {
2979 _api_base64_decode(
2980 encoded.as_ptr() as u32,
2981 encoded.len() as u32,
2982 buf.as_mut_ptr() as u32,
2983 buf.len() as u32,
2984 )
2985 };
2986 buf[..len as usize].to_vec()
2987}
2988
2989pub fn kv_store_set(key: &str, value: &[u8]) -> bool {
2994 let rc = unsafe {
2995 _api_kv_store_set(
2996 key.as_ptr() as u32,
2997 key.len() as u32,
2998 value.as_ptr() as u32,
2999 value.len() as u32,
3000 )
3001 };
3002 rc == 0
3003}
3004
3005pub fn kv_store_set_str(key: &str, value: &str) -> bool {
3007 kv_store_set(key, value.as_bytes())
3008}
3009
3010pub fn kv_store_get(key: &str) -> Option<Vec<u8>> {
3013 let mut buf = vec![0u8; 64 * 1024]; let rc = unsafe {
3015 _api_kv_store_get(
3016 key.as_ptr() as u32,
3017 key.len() as u32,
3018 buf.as_mut_ptr() as u32,
3019 buf.len() as u32,
3020 )
3021 };
3022 if rc < 0 {
3023 return None;
3024 }
3025 Some(buf[..rc as usize].to_vec())
3026}
3027
3028pub fn kv_store_get_str(key: &str) -> Option<String> {
3030 kv_store_get(key).map(|v| String::from_utf8_lossy(&v).into_owned())
3031}
3032
3033pub fn kv_store_delete(key: &str) -> bool {
3035 let rc = unsafe { _api_kv_store_delete(key.as_ptr() as u32, key.len() as u32) };
3036 rc == 0
3037}
3038
3039pub fn navigate(url: &str) -> i32 {
3045 unsafe { _api_navigate(url.as_ptr() as u32, url.len() as u32) }
3046}
3047
3048pub fn push_state(state: &[u8], title: &str, url: &str) {
3056 unsafe {
3057 _api_push_state(
3058 state.as_ptr() as u32,
3059 state.len() as u32,
3060 title.as_ptr() as u32,
3061 title.len() as u32,
3062 url.as_ptr() as u32,
3063 url.len() as u32,
3064 )
3065 }
3066}
3067
3068pub fn replace_state(state: &[u8], title: &str, url: &str) {
3071 unsafe {
3072 _api_replace_state(
3073 state.as_ptr() as u32,
3074 state.len() as u32,
3075 title.as_ptr() as u32,
3076 title.len() as u32,
3077 url.as_ptr() as u32,
3078 url.len() as u32,
3079 )
3080 }
3081}
3082
3083pub fn get_url() -> String {
3085 let mut buf = [0u8; 4096];
3086 let len = unsafe { _api_get_url(buf.as_mut_ptr() as u32, buf.len() as u32) };
3087 String::from_utf8_lossy(&buf[..len as usize]).to_string()
3088}
3089
3090pub fn get_state() -> Option<Vec<u8>> {
3093 let mut buf = vec![0u8; 64 * 1024]; let rc = unsafe { _api_get_state(buf.as_mut_ptr() as u32, buf.len() as u32) };
3095 if rc < 0 {
3096 return None;
3097 }
3098 Some(buf[..rc as usize].to_vec())
3099}
3100
3101pub fn history_length() -> u32 {
3103 unsafe { _api_history_length() }
3104}
3105
3106pub fn history_back() -> bool {
3108 unsafe { _api_history_back() == 1 }
3109}
3110
3111pub fn history_forward() -> bool {
3113 unsafe { _api_history_forward() == 1 }
3114}
3115
3116pub fn register_hyperlink(x: f32, y: f32, w: f32, h: f32, url: &str) -> i32 {
3124 unsafe { _api_register_hyperlink(x, y, w, h, url.as_ptr() as u32, url.len() as u32) }
3125}
3126
3127pub fn clear_hyperlinks() {
3129 unsafe { _api_clear_hyperlinks() }
3130}
3131
3132pub fn url_resolve(base: &str, relative: &str) -> Option<String> {
3137 let mut buf = [0u8; 4096];
3138 let rc = unsafe {
3139 _api_url_resolve(
3140 base.as_ptr() as u32,
3141 base.len() as u32,
3142 relative.as_ptr() as u32,
3143 relative.len() as u32,
3144 buf.as_mut_ptr() as u32,
3145 buf.len() as u32,
3146 )
3147 };
3148 if rc < 0 {
3149 return None;
3150 }
3151 Some(String::from_utf8_lossy(&buf[..rc as usize]).to_string())
3152}
3153
3154pub fn url_encode(input: &str) -> String {
3156 let mut buf = vec![0u8; input.len() * 3 + 4];
3157 let len = unsafe {
3158 _api_url_encode(
3159 input.as_ptr() as u32,
3160 input.len() as u32,
3161 buf.as_mut_ptr() as u32,
3162 buf.len() as u32,
3163 )
3164 };
3165 String::from_utf8_lossy(&buf[..len as usize]).to_string()
3166}
3167
3168pub fn url_decode(input: &str) -> String {
3170 let mut buf = vec![0u8; input.len() + 4];
3171 let len = unsafe {
3172 _api_url_decode(
3173 input.as_ptr() as u32,
3174 input.len() as u32,
3175 buf.as_mut_ptr() as u32,
3176 buf.len() as u32,
3177 )
3178 };
3179 String::from_utf8_lossy(&buf[..len as usize]).to_string()
3180}
3181
3182pub fn mouse_position() -> (f32, f32) {
3186 let packed = unsafe { _api_mouse_position() };
3187 let x = f32::from_bits((packed >> 32) as u32);
3188 let y = f32::from_bits((packed & 0xFFFF_FFFF) as u32);
3189 (x, y)
3190}
3191
3192pub fn mouse_button_down(button: u32) -> bool {
3195 unsafe { _api_mouse_button_down(button) != 0 }
3196}
3197
3198pub fn mouse_button_clicked(button: u32) -> bool {
3200 unsafe { _api_mouse_button_clicked(button) != 0 }
3201}
3202
3203pub fn key_down(key: u32) -> bool {
3206 unsafe { _api_key_down(key) != 0 }
3207}
3208
3209pub fn key_pressed(key: u32) -> bool {
3211 unsafe { _api_key_pressed(key) != 0 }
3212}
3213
3214pub fn scroll_delta() -> (f32, f32) {
3216 let packed = unsafe { _api_scroll_delta() };
3217 let x = f32::from_bits((packed >> 32) as u32);
3218 let y = f32::from_bits((packed & 0xFFFF_FFFF) as u32);
3219 (x, y)
3220}
3221
3222pub fn modifiers() -> u32 {
3224 unsafe { _api_modifiers() }
3225}
3226
3227pub fn shift_held() -> bool {
3229 modifiers() & 1 != 0
3230}
3231
3232pub fn ctrl_held() -> bool {
3234 modifiers() & 2 != 0
3235}
3236
3237pub fn alt_held() -> bool {
3239 modifiers() & 4 != 0
3240}
3241
3242pub const KEY_A: u32 = 0;
3245pub const KEY_B: u32 = 1;
3246pub const KEY_C: u32 = 2;
3247pub const KEY_D: u32 = 3;
3248pub const KEY_E: u32 = 4;
3249pub const KEY_F: u32 = 5;
3250pub const KEY_G: u32 = 6;
3251pub const KEY_H: u32 = 7;
3252pub const KEY_I: u32 = 8;
3253pub const KEY_J: u32 = 9;
3254pub const KEY_K: u32 = 10;
3255pub const KEY_L: u32 = 11;
3256pub const KEY_M: u32 = 12;
3257pub const KEY_N: u32 = 13;
3258pub const KEY_O: u32 = 14;
3259pub const KEY_P: u32 = 15;
3260pub const KEY_Q: u32 = 16;
3261pub const KEY_R: u32 = 17;
3262pub const KEY_S: u32 = 18;
3263pub const KEY_T: u32 = 19;
3264pub const KEY_U: u32 = 20;
3265pub const KEY_V: u32 = 21;
3266pub const KEY_W: u32 = 22;
3267pub const KEY_X: u32 = 23;
3268pub const KEY_Y: u32 = 24;
3269pub const KEY_Z: u32 = 25;
3270pub const KEY_0: u32 = 26;
3271pub const KEY_1: u32 = 27;
3272pub const KEY_2: u32 = 28;
3273pub const KEY_3: u32 = 29;
3274pub const KEY_4: u32 = 30;
3275pub const KEY_5: u32 = 31;
3276pub const KEY_6: u32 = 32;
3277pub const KEY_7: u32 = 33;
3278pub const KEY_8: u32 = 34;
3279pub const KEY_9: u32 = 35;
3280pub const KEY_ENTER: u32 = 36;
3281pub const KEY_ESCAPE: u32 = 37;
3282pub const KEY_TAB: u32 = 38;
3283pub const KEY_BACKSPACE: u32 = 39;
3284pub const KEY_DELETE: u32 = 40;
3285pub const KEY_SPACE: u32 = 41;
3286pub const KEY_UP: u32 = 42;
3287pub const KEY_DOWN: u32 = 43;
3288pub const KEY_LEFT: u32 = 44;
3289pub const KEY_RIGHT: u32 = 45;
3290pub const KEY_HOME: u32 = 46;
3291pub const KEY_END: u32 = 47;
3292pub const KEY_PAGE_UP: u32 = 48;
3293pub const KEY_PAGE_DOWN: u32 = 49;
3294
3295pub fn ui_button(id: u32, x: f32, y: f32, w: f32, h: f32, label: &str) -> bool {
3303 unsafe { _api_ui_button(id, x, y, w, h, label.as_ptr() as u32, label.len() as u32) != 0 }
3304}
3305
3306pub fn ui_checkbox(id: u32, x: f32, y: f32, label: &str, initial: bool) -> bool {
3310 unsafe {
3311 _api_ui_checkbox(
3312 id,
3313 x,
3314 y,
3315 label.as_ptr() as u32,
3316 label.len() as u32,
3317 if initial { 1 } else { 0 },
3318 ) != 0
3319 }
3320}
3321
3322pub fn ui_slider(id: u32, x: f32, y: f32, w: f32, min: f32, max: f32, initial: f32) -> f32 {
3326 unsafe { _api_ui_slider(id, x, y, w, min, max, initial) }
3327}
3328
3329pub fn ui_text_input(id: u32, x: f32, y: f32, w: f32, initial: &str) -> String {
3333 let mut buf = [0u8; 4096];
3334 let len = unsafe {
3335 _api_ui_text_input(
3336 id,
3337 x,
3338 y,
3339 w,
3340 initial.as_ptr() as u32,
3341 initial.len() as u32,
3342 buf.as_mut_ptr() as u32,
3343 buf.len() as u32,
3344 )
3345 };
3346 String::from_utf8_lossy(&buf[..len as usize]).to_string()
3347}
3348
3349pub fn ui_text_area(id: u32, x: f32, y: f32, w: f32, h: f32, initial: &str) -> String {
3356 const OUT_CAP: usize = 131072;
3357 thread_local! {
3358 static BUF: std::cell::RefCell<Vec<u8>> = std::cell::RefCell::new(vec![0u8; OUT_CAP]);
3359 }
3360 BUF.with(|cell| {
3361 let mut buf = cell.borrow_mut();
3362 let len = unsafe {
3363 _api_ui_text_area(
3364 id,
3365 x,
3366 y,
3367 w,
3368 h,
3369 initial.as_ptr() as u32,
3370 initial.len() as u32,
3371 buf.as_mut_ptr() as u32,
3372 OUT_CAP as u32,
3373 )
3374 };
3375 let end = (len as usize).min(buf.len());
3376 String::from_utf8_lossy(&buf[..end]).into_owned()
3377 })
3378}