1#![allow(clippy::too_many_arguments)]
2#![allow(clippy::doc_overindented_list_items)]
3
4pub mod draw;
132pub mod proto;
133
134#[link(wasm_import_module = "oxide")]
137extern "C" {
138 #[link_name = "api_log"]
139 fn _api_log(ptr: u32, len: u32);
140
141 #[link_name = "api_warn"]
142 fn _api_warn(ptr: u32, len: u32);
143
144 #[link_name = "api_error"]
145 fn _api_error(ptr: u32, len: u32);
146
147 #[link_name = "api_get_location"]
148 fn _api_get_location(out_ptr: u32, out_cap: u32) -> i32;
149
150 #[link_name = "api_upload_file"]
151 fn _api_upload_file(name_ptr: u32, name_cap: u32, data_ptr: u32, data_cap: u32) -> u64;
152
153 #[link_name = "api_file_pick"]
154 fn _api_file_pick(
155 title_ptr: u32,
156 title_len: u32,
157 filters_ptr: u32,
158 filters_len: u32,
159 multiple: u32,
160 out_ptr: u32,
161 out_cap: u32,
162 ) -> i32;
163
164 #[link_name = "api_folder_pick"]
165 fn _api_folder_pick(title_ptr: u32, title_len: u32) -> u32;
166
167 #[link_name = "api_folder_entries"]
168 fn _api_folder_entries(handle: u32, out_ptr: u32, out_cap: u32) -> i32;
169
170 #[link_name = "api_file_read"]
171 fn _api_file_read(handle: u32, out_ptr: u32, out_cap: u32) -> i64;
172
173 #[link_name = "api_file_read_range"]
174 fn _api_file_read_range(
175 handle: u32,
176 offset_lo: u32,
177 offset_hi: u32,
178 len: u32,
179 out_ptr: u32,
180 out_cap: u32,
181 ) -> i64;
182
183 #[link_name = "api_file_metadata"]
184 fn _api_file_metadata(handle: u32, out_ptr: u32, out_cap: u32) -> i32;
185
186 #[link_name = "api_canvas_clear"]
187 fn _api_canvas_clear(r: u32, g: u32, b: u32, a: u32);
188
189 #[link_name = "api_canvas_rect"]
190 fn _api_canvas_rect(x: f32, y: f32, w: f32, h: f32, r: u32, g: u32, b: u32, a: u32);
191
192 #[link_name = "api_canvas_circle"]
193 fn _api_canvas_circle(cx: f32, cy: f32, radius: f32, r: u32, g: u32, b: u32, a: u32);
194
195 #[link_name = "api_canvas_text"]
196 fn _api_canvas_text(
197 x: f32,
198 y: f32,
199 size: f32,
200 r: u32,
201 g: u32,
202 b: u32,
203 a: u32,
204 ptr: u32,
205 len: u32,
206 );
207
208 #[link_name = "api_canvas_text_ex"]
209 #[allow(clippy::too_many_arguments)]
210 fn _api_canvas_text_ex(
211 x: f32,
212 y: f32,
213 size: f32,
214 r: u32,
215 g: u32,
216 b: u32,
217 a: u32,
218 family_ptr: u32,
219 family_len: u32,
220 weight: u32,
221 style: u32,
222 align: u32,
223 text_ptr: u32,
224 text_len: u32,
225 );
226
227 #[link_name = "api_canvas_measure_text"]
228 fn _api_canvas_measure_text(
229 size: f32,
230 family_ptr: u32,
231 family_len: u32,
232 weight: u32,
233 style: u32,
234 text_ptr: u32,
235 text_len: u32,
236 out_ptr: u32,
237 ) -> u32;
238
239 #[link_name = "api_canvas_line"]
240 fn _api_canvas_line(
241 x1: f32,
242 y1: f32,
243 x2: f32,
244 y2: f32,
245 r: u32,
246 g: u32,
247 b: u32,
248 a: u32,
249 thickness: f32,
250 );
251
252 #[link_name = "api_canvas_dimensions"]
253 fn _api_canvas_dimensions() -> u64;
254
255 #[link_name = "api_set_content_size"]
256 fn _api_set_content_size(w: u32, h: u32);
257
258 #[link_name = "api_get_scroll_position"]
259 fn _api_get_scroll_position() -> u64;
260
261 #[link_name = "api_set_scroll_position"]
262 fn _api_set_scroll_position(x: f32, y: f32);
263
264 #[link_name = "api_canvas_image"]
265 fn _api_canvas_image(x: f32, y: f32, w: f32, h: f32, data_ptr: u32, data_len: u32);
266
267 #[link_name = "api_canvas_rounded_rect"]
270 fn _api_canvas_rounded_rect(
271 x: f32,
272 y: f32,
273 w: f32,
274 h: f32,
275 radius: f32,
276 r: u32,
277 g: u32,
278 b: u32,
279 a: u32,
280 );
281
282 #[link_name = "api_canvas_arc"]
283 fn _api_canvas_arc(
284 cx: f32,
285 cy: f32,
286 radius: f32,
287 start_angle: f32,
288 end_angle: f32,
289 r: u32,
290 g: u32,
291 b: u32,
292 a: u32,
293 thickness: f32,
294 );
295
296 #[link_name = "api_canvas_bezier"]
297 fn _api_canvas_bezier(
298 x1: f32,
299 y1: f32,
300 cp1x: f32,
301 cp1y: f32,
302 cp2x: f32,
303 cp2y: f32,
304 x2: f32,
305 y2: f32,
306 r: u32,
307 g: u32,
308 b: u32,
309 a: u32,
310 thickness: f32,
311 );
312
313 #[link_name = "api_canvas_gradient"]
314 fn _api_canvas_gradient(
315 x: f32,
316 y: f32,
317 w: f32,
318 h: f32,
319 kind: u32,
320 ax: f32,
321 ay: f32,
322 bx: f32,
323 by: f32,
324 stops_ptr: u32,
325 stops_len: u32,
326 );
327
328 #[link_name = "api_canvas_save"]
331 fn _api_canvas_save();
332
333 #[link_name = "api_canvas_restore"]
334 fn _api_canvas_restore();
335
336 #[link_name = "api_canvas_transform"]
337 fn _api_canvas_transform(a: f32, b: f32, c: f32, d: f32, tx: f32, ty: f32);
338
339 #[link_name = "api_canvas_clip"]
340 fn _api_canvas_clip(x: f32, y: f32, w: f32, h: f32);
341
342 #[link_name = "api_canvas_opacity"]
343 fn _api_canvas_opacity(alpha: f32);
344
345 #[link_name = "api_storage_set"]
346 fn _api_storage_set(key_ptr: u32, key_len: u32, val_ptr: u32, val_len: u32);
347
348 #[link_name = "api_storage_get"]
349 fn _api_storage_get(key_ptr: u32, key_len: u32, out_ptr: u32, out_cap: u32) -> u32;
350
351 #[link_name = "api_storage_remove"]
352 fn _api_storage_remove(key_ptr: u32, key_len: u32);
353
354 #[link_name = "api_clipboard_write"]
355 fn _api_clipboard_write(ptr: u32, len: u32);
356
357 #[link_name = "api_clipboard_read"]
358 fn _api_clipboard_read(out_ptr: u32, out_cap: u32) -> u32;
359
360 #[link_name = "api_time_now_ms"]
361 fn _api_time_now_ms() -> u64;
362
363 #[link_name = "api_set_timeout"]
364 fn _api_set_timeout(callback_id: u32, delay_ms: u32) -> u32;
365
366 #[link_name = "api_set_interval"]
367 fn _api_set_interval(callback_id: u32, interval_ms: u32) -> u32;
368
369 #[link_name = "api_clear_timer"]
370 fn _api_clear_timer(timer_id: u32);
371
372 #[link_name = "api_request_animation_frame"]
373 fn _api_request_animation_frame(callback_id: u32) -> u32;
374
375 #[link_name = "api_cancel_animation_frame"]
376 fn _api_cancel_animation_frame(request_id: u32);
377
378 #[link_name = "api_on_event"]
379 fn _api_on_event(type_ptr: u32, type_len: u32, callback_id: u32) -> u32;
380
381 #[link_name = "api_off_event"]
382 fn _api_off_event(listener_id: u32) -> u32;
383
384 #[link_name = "api_emit_event"]
385 fn _api_emit_event(type_ptr: u32, type_len: u32, data_ptr: u32, data_len: u32);
386
387 #[link_name = "api_event_type_len"]
388 fn _api_event_type_len() -> u32;
389
390 #[link_name = "api_event_type_read"]
391 fn _api_event_type_read(out_ptr: u32, out_cap: u32) -> u32;
392
393 #[link_name = "api_event_data_len"]
394 fn _api_event_data_len() -> u32;
395
396 #[link_name = "api_event_data_read"]
397 fn _api_event_data_read(out_ptr: u32, out_cap: u32) -> u32;
398
399 #[link_name = "api_random"]
400 fn _api_random() -> u64;
401
402 #[link_name = "api_notify"]
403 fn _api_notify(title_ptr: u32, title_len: u32, body_ptr: u32, body_len: u32);
404
405 #[link_name = "api_fetch"]
406 fn _api_fetch(
407 method_ptr: u32,
408 method_len: u32,
409 url_ptr: u32,
410 url_len: u32,
411 ct_ptr: u32,
412 ct_len: u32,
413 body_ptr: u32,
414 body_len: u32,
415 out_ptr: u32,
416 out_cap: u32,
417 ) -> i64;
418
419 #[link_name = "api_fetch_begin"]
420 fn _api_fetch_begin(
421 method_ptr: u32,
422 method_len: u32,
423 url_ptr: u32,
424 url_len: u32,
425 ct_ptr: u32,
426 ct_len: u32,
427 body_ptr: u32,
428 body_len: u32,
429 ) -> u32;
430
431 #[link_name = "api_fetch_state"]
432 fn _api_fetch_state(id: u32) -> u32;
433
434 #[link_name = "api_fetch_status"]
435 fn _api_fetch_status(id: u32) -> u32;
436
437 #[link_name = "api_fetch_recv"]
438 fn _api_fetch_recv(id: u32, out_ptr: u32, out_cap: u32) -> i64;
439
440 #[link_name = "api_fetch_error"]
441 fn _api_fetch_error(id: u32, out_ptr: u32, out_cap: u32) -> i32;
442
443 #[link_name = "api_fetch_abort"]
444 fn _api_fetch_abort(id: u32) -> i32;
445
446 #[link_name = "api_fetch_remove"]
447 fn _api_fetch_remove(id: u32);
448
449 #[link_name = "api_load_module"]
450 fn _api_load_module(url_ptr: u32, url_len: u32) -> i32;
451
452 #[link_name = "api_hash_sha256"]
453 fn _api_hash_sha256(data_ptr: u32, data_len: u32, out_ptr: u32) -> u32;
454
455 #[link_name = "api_base64_encode"]
456 fn _api_base64_encode(data_ptr: u32, data_len: u32, out_ptr: u32, out_cap: u32) -> u32;
457
458 #[link_name = "api_base64_decode"]
459 fn _api_base64_decode(data_ptr: u32, data_len: u32, out_ptr: u32, out_cap: u32) -> u32;
460
461 #[link_name = "api_kv_store_set"]
462 fn _api_kv_store_set(key_ptr: u32, key_len: u32, val_ptr: u32, val_len: u32) -> i32;
463
464 #[link_name = "api_kv_store_get"]
465 fn _api_kv_store_get(key_ptr: u32, key_len: u32, out_ptr: u32, out_cap: u32) -> i32;
466
467 #[link_name = "api_kv_store_delete"]
468 fn _api_kv_store_delete(key_ptr: u32, key_len: u32) -> i32;
469
470 #[link_name = "api_navigate"]
473 fn _api_navigate(url_ptr: u32, url_len: u32) -> i32;
474
475 #[link_name = "api_push_state"]
476 fn _api_push_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_replace_state"]
486 fn _api_replace_state(
487 state_ptr: u32,
488 state_len: u32,
489 title_ptr: u32,
490 title_len: u32,
491 url_ptr: u32,
492 url_len: u32,
493 );
494
495 #[link_name = "api_get_url"]
496 fn _api_get_url(out_ptr: u32, out_cap: u32) -> u32;
497
498 #[link_name = "api_get_state"]
499 fn _api_get_state(out_ptr: u32, out_cap: u32) -> i32;
500
501 #[link_name = "api_history_length"]
502 fn _api_history_length() -> u32;
503
504 #[link_name = "api_history_back"]
505 fn _api_history_back() -> i32;
506
507 #[link_name = "api_history_forward"]
508 fn _api_history_forward() -> i32;
509
510 #[link_name = "api_register_hyperlink"]
513 fn _api_register_hyperlink(x: f32, y: f32, w: f32, h: f32, url_ptr: u32, url_len: u32) -> i32;
514
515 #[link_name = "api_clear_hyperlinks"]
516 fn _api_clear_hyperlinks();
517
518 #[link_name = "api_mouse_position"]
521 fn _api_mouse_position() -> u64;
522
523 #[link_name = "api_mouse_button_down"]
524 fn _api_mouse_button_down(button: u32) -> u32;
525
526 #[link_name = "api_mouse_button_clicked"]
527 fn _api_mouse_button_clicked(button: u32) -> u32;
528
529 #[link_name = "api_key_down"]
530 fn _api_key_down(key: u32) -> u32;
531
532 #[link_name = "api_key_pressed"]
533 fn _api_key_pressed(key: u32) -> u32;
534
535 #[link_name = "api_scroll_delta"]
536 fn _api_scroll_delta() -> u64;
537
538 #[link_name = "api_modifiers"]
539 fn _api_modifiers() -> u32;
540
541 #[link_name = "api_ui_button"]
544 fn _api_ui_button(
545 id: u32,
546 x: f32,
547 y: f32,
548 w: f32,
549 h: f32,
550 label_ptr: u32,
551 label_len: u32,
552 variant: u32,
553 ) -> u32;
554
555 #[link_name = "api_ui_checkbox"]
556 fn _api_ui_checkbox(
557 id: u32,
558 x: f32,
559 y: f32,
560 label_ptr: u32,
561 label_len: u32,
562 initial: u32,
563 ) -> u32;
564
565 #[link_name = "api_ui_switch"]
566 fn _api_ui_switch(id: u32, x: f32, y: f32, label_ptr: u32, label_len: u32, initial: u32)
567 -> u32;
568
569 #[link_name = "api_ui_slider"]
570 fn _api_ui_slider(id: u32, x: f32, y: f32, w: f32, min: f32, max: f32, initial: f32) -> f32;
571
572 #[link_name = "api_ui_text_input"]
573 fn _api_ui_text_input(
574 id: u32,
575 x: f32,
576 y: f32,
577 w: f32,
578 init_ptr: u32,
579 init_len: u32,
580 placeholder_ptr: u32,
581 placeholder_len: u32,
582 out_ptr: u32,
583 out_cap: u32,
584 ) -> u32;
585
586 #[link_name = "api_ui_textarea"]
587 fn _api_ui_textarea(
588 id: u32,
589 x: f32,
590 y: f32,
591 w: f32,
592 h: f32,
593 init_ptr: u32,
594 init_len: u32,
595 placeholder_ptr: u32,
596 placeholder_len: u32,
597 out_ptr: u32,
598 out_cap: u32,
599 ) -> u32;
600
601 #[link_name = "api_ui_card"]
602 fn _api_ui_card(
603 x: f32,
604 y: f32,
605 w: f32,
606 h: f32,
607 title_ptr: u32,
608 title_len: u32,
609 desc_ptr: u32,
610 desc_len: u32,
611 );
612
613 #[link_name = "api_ui_badge"]
614 fn _api_ui_badge(x: f32, y: f32, label_ptr: u32, label_len: u32, variant: u32);
615
616 #[link_name = "api_ui_separator"]
617 fn _api_ui_separator(x: f32, y: f32, length: f32, vertical: u32);
618
619 #[link_name = "api_ui_progress"]
620 fn _api_ui_progress(x: f32, y: f32, w: f32, value: f32);
621
622 #[link_name = "api_ui_label"]
623 fn _api_ui_label(x: f32, y: f32, text_ptr: u32, text_len: u32, muted: u32, size: f32);
624
625 #[link_name = "api_audio_play"]
628 fn _api_audio_play(data_ptr: u32, data_len: u32) -> i32;
629
630 #[link_name = "api_audio_play_url"]
631 fn _api_audio_play_url(url_ptr: u32, url_len: u32) -> i32;
632
633 #[link_name = "api_audio_detect_format"]
634 fn _api_audio_detect_format(data_ptr: u32, data_len: u32) -> u32;
635
636 #[link_name = "api_audio_play_with_format"]
637 fn _api_audio_play_with_format(data_ptr: u32, data_len: u32, format_hint: u32) -> i32;
638
639 #[link_name = "api_audio_last_url_content_type"]
640 fn _api_audio_last_url_content_type(out_ptr: u32, out_cap: u32) -> u32;
641
642 #[link_name = "api_audio_pause"]
643 fn _api_audio_pause();
644
645 #[link_name = "api_audio_resume"]
646 fn _api_audio_resume();
647
648 #[link_name = "api_audio_stop"]
649 fn _api_audio_stop();
650
651 #[link_name = "api_audio_set_volume"]
652 fn _api_audio_set_volume(level: f32);
653
654 #[link_name = "api_audio_get_volume"]
655 fn _api_audio_get_volume() -> f32;
656
657 #[link_name = "api_audio_is_playing"]
658 fn _api_audio_is_playing() -> u32;
659
660 #[link_name = "api_audio_position"]
661 fn _api_audio_position() -> u64;
662
663 #[link_name = "api_audio_seek"]
664 fn _api_audio_seek(position_ms: u64) -> i32;
665
666 #[link_name = "api_audio_duration"]
667 fn _api_audio_duration() -> u64;
668
669 #[link_name = "api_audio_set_loop"]
670 fn _api_audio_set_loop(enabled: u32);
671
672 #[link_name = "api_audio_channel_play"]
673 fn _api_audio_channel_play(channel: u32, data_ptr: u32, data_len: u32) -> i32;
674
675 #[link_name = "api_audio_channel_play_with_format"]
676 fn _api_audio_channel_play_with_format(
677 channel: u32,
678 data_ptr: u32,
679 data_len: u32,
680 format_hint: u32,
681 ) -> i32;
682
683 #[link_name = "api_audio_channel_stop"]
684 fn _api_audio_channel_stop(channel: u32);
685
686 #[link_name = "api_audio_channel_set_volume"]
687 fn _api_audio_channel_set_volume(channel: u32, level: f32);
688
689 #[link_name = "api_video_detect_format"]
692 fn _api_video_detect_format(data_ptr: u32, data_len: u32) -> u32;
693
694 #[link_name = "api_video_load"]
695 fn _api_video_load(data_ptr: u32, data_len: u32, format_hint: u32) -> i32;
696
697 #[link_name = "api_video_load_url"]
698 fn _api_video_load_url(url_ptr: u32, url_len: u32) -> i32;
699
700 #[link_name = "api_video_last_url_content_type"]
701 fn _api_video_last_url_content_type(out_ptr: u32, out_cap: u32) -> u32;
702
703 #[link_name = "api_video_hls_variant_count"]
704 fn _api_video_hls_variant_count() -> u32;
705
706 #[link_name = "api_video_hls_variant_url"]
707 fn _api_video_hls_variant_url(index: u32, out_ptr: u32, out_cap: u32) -> u32;
708
709 #[link_name = "api_video_hls_open_variant"]
710 fn _api_video_hls_open_variant(index: u32) -> i32;
711
712 #[link_name = "api_video_play"]
713 fn _api_video_play();
714
715 #[link_name = "api_video_pause"]
716 fn _api_video_pause();
717
718 #[link_name = "api_video_stop"]
719 fn _api_video_stop();
720
721 #[link_name = "api_video_seek"]
722 fn _api_video_seek(position_ms: u64) -> i32;
723
724 #[link_name = "api_video_position"]
725 fn _api_video_position() -> u64;
726
727 #[link_name = "api_video_duration"]
728 fn _api_video_duration() -> u64;
729
730 #[link_name = "api_video_render"]
731 fn _api_video_render(x: f32, y: f32, w: f32, h: f32) -> i32;
732
733 #[link_name = "api_video_set_volume"]
734 fn _api_video_set_volume(level: f32);
735
736 #[link_name = "api_video_get_volume"]
737 fn _api_video_get_volume() -> f32;
738
739 #[link_name = "api_video_set_loop"]
740 fn _api_video_set_loop(enabled: u32);
741
742 #[link_name = "api_video_set_pip"]
743 fn _api_video_set_pip(enabled: u32);
744
745 #[link_name = "api_subtitle_load_srt"]
746 fn _api_subtitle_load_srt(ptr: u32, len: u32) -> i32;
747
748 #[link_name = "api_subtitle_load_vtt"]
749 fn _api_subtitle_load_vtt(ptr: u32, len: u32) -> i32;
750
751 #[link_name = "api_subtitle_clear"]
752 fn _api_subtitle_clear();
753
754 #[link_name = "api_camera_open"]
757 fn _api_camera_open() -> i32;
758
759 #[link_name = "api_camera_close"]
760 fn _api_camera_close();
761
762 #[link_name = "api_camera_capture_frame"]
763 fn _api_camera_capture_frame(out_ptr: u32, out_cap: u32) -> u32;
764
765 #[link_name = "api_camera_frame_dimensions"]
766 fn _api_camera_frame_dimensions() -> u64;
767
768 #[link_name = "api_microphone_open"]
769 fn _api_microphone_open() -> i32;
770
771 #[link_name = "api_microphone_close"]
772 fn _api_microphone_close();
773
774 #[link_name = "api_microphone_sample_rate"]
775 fn _api_microphone_sample_rate() -> u32;
776
777 #[link_name = "api_microphone_read_samples"]
778 fn _api_microphone_read_samples(out_ptr: u32, max_samples: u32) -> u32;
779
780 #[link_name = "api_screen_capture"]
781 fn _api_screen_capture(out_ptr: u32, out_cap: u32) -> i32;
782
783 #[link_name = "api_screen_capture_dimensions"]
784 fn _api_screen_capture_dimensions() -> u64;
785
786 #[link_name = "api_media_pipeline_stats"]
787 fn _api_media_pipeline_stats() -> u64;
788
789 #[link_name = "api_gpu_create_buffer"]
792 fn _api_gpu_create_buffer(size_lo: u32, size_hi: u32, usage: u32) -> u32;
793
794 #[link_name = "api_gpu_create_texture"]
795 fn _api_gpu_create_texture(width: u32, height: u32) -> u32;
796
797 #[link_name = "api_gpu_create_shader"]
798 fn _api_gpu_create_shader(src_ptr: u32, src_len: u32) -> u32;
799
800 #[link_name = "api_gpu_create_render_pipeline"]
801 fn _api_gpu_create_render_pipeline(
802 shader: u32,
803 vs_ptr: u32,
804 vs_len: u32,
805 fs_ptr: u32,
806 fs_len: u32,
807 ) -> u32;
808
809 #[link_name = "api_gpu_create_compute_pipeline"]
810 fn _api_gpu_create_compute_pipeline(shader: u32, ep_ptr: u32, ep_len: u32) -> u32;
811
812 #[link_name = "api_gpu_write_buffer"]
813 fn _api_gpu_write_buffer(
814 handle: u32,
815 offset_lo: u32,
816 offset_hi: u32,
817 data_ptr: u32,
818 data_len: u32,
819 ) -> u32;
820
821 #[link_name = "api_gpu_draw"]
822 fn _api_gpu_draw(pipeline: u32, target: u32, vertex_count: u32, instance_count: u32) -> u32;
823
824 #[link_name = "api_gpu_dispatch_compute"]
825 fn _api_gpu_dispatch_compute(pipeline: u32, x: u32, y: u32, z: u32) -> u32;
826
827 #[link_name = "api_gpu_destroy_buffer"]
828 fn _api_gpu_destroy_buffer(handle: u32) -> u32;
829
830 #[link_name = "api_gpu_destroy_texture"]
831 fn _api_gpu_destroy_texture(handle: u32) -> u32;
832
833 #[link_name = "api_rtc_create_peer"]
836 fn _api_rtc_create_peer(stun_ptr: u32, stun_len: u32) -> u32;
837
838 #[link_name = "api_rtc_close_peer"]
839 fn _api_rtc_close_peer(peer_id: u32) -> u32;
840
841 #[link_name = "api_rtc_create_offer"]
842 fn _api_rtc_create_offer(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
843
844 #[link_name = "api_rtc_create_answer"]
845 fn _api_rtc_create_answer(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
846
847 #[link_name = "api_rtc_set_local_description"]
848 fn _api_rtc_set_local_description(
849 peer_id: u32,
850 sdp_ptr: u32,
851 sdp_len: u32,
852 is_offer: u32,
853 ) -> i32;
854
855 #[link_name = "api_rtc_set_remote_description"]
856 fn _api_rtc_set_remote_description(
857 peer_id: u32,
858 sdp_ptr: u32,
859 sdp_len: u32,
860 is_offer: u32,
861 ) -> i32;
862
863 #[link_name = "api_rtc_add_ice_candidate"]
864 fn _api_rtc_add_ice_candidate(peer_id: u32, cand_ptr: u32, cand_len: u32) -> i32;
865
866 #[link_name = "api_rtc_connection_state"]
867 fn _api_rtc_connection_state(peer_id: u32) -> u32;
868
869 #[link_name = "api_rtc_poll_ice_candidate"]
870 fn _api_rtc_poll_ice_candidate(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
871
872 #[link_name = "api_rtc_create_data_channel"]
873 fn _api_rtc_create_data_channel(
874 peer_id: u32,
875 label_ptr: u32,
876 label_len: u32,
877 ordered: u32,
878 ) -> u32;
879
880 #[link_name = "api_rtc_send"]
881 fn _api_rtc_send(
882 peer_id: u32,
883 channel_id: u32,
884 data_ptr: u32,
885 data_len: u32,
886 is_binary: u32,
887 ) -> i32;
888
889 #[link_name = "api_rtc_recv"]
890 fn _api_rtc_recv(peer_id: u32, channel_id: u32, out_ptr: u32, out_cap: u32) -> i64;
891
892 #[link_name = "api_rtc_poll_data_channel"]
893 fn _api_rtc_poll_data_channel(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
894
895 #[link_name = "api_rtc_add_track"]
896 fn _api_rtc_add_track(peer_id: u32, kind: u32) -> u32;
897
898 #[link_name = "api_rtc_poll_track"]
899 fn _api_rtc_poll_track(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
900
901 #[link_name = "api_rtc_signal_connect"]
902 fn _api_rtc_signal_connect(url_ptr: u32, url_len: u32) -> u32;
903
904 #[link_name = "api_rtc_signal_join_room"]
905 fn _api_rtc_signal_join_room(room_ptr: u32, room_len: u32) -> i32;
906
907 #[link_name = "api_rtc_signal_send"]
908 fn _api_rtc_signal_send(data_ptr: u32, data_len: u32) -> i32;
909
910 #[link_name = "api_rtc_signal_recv"]
911 fn _api_rtc_signal_recv(out_ptr: u32, out_cap: u32) -> i32;
912
913 #[link_name = "api_ws_connect"]
916 fn _api_ws_connect(url_ptr: u32, url_len: u32) -> u32;
917
918 #[link_name = "api_ws_send_text"]
919 fn _api_ws_send_text(id: u32, data_ptr: u32, data_len: u32) -> i32;
920
921 #[link_name = "api_ws_send_binary"]
922 fn _api_ws_send_binary(id: u32, data_ptr: u32, data_len: u32) -> i32;
923
924 #[link_name = "api_ws_recv"]
925 fn _api_ws_recv(id: u32, out_ptr: u32, out_cap: u32) -> i64;
926
927 #[link_name = "api_ws_ready_state"]
928 fn _api_ws_ready_state(id: u32) -> u32;
929
930 #[link_name = "api_ws_close"]
931 fn _api_ws_close(id: u32) -> i32;
932
933 #[link_name = "api_ws_remove"]
934 fn _api_ws_remove(id: u32);
935
936 #[link_name = "api_spawn_worker"]
939 fn _api_spawn_worker(url_ptr: u32, url_len: u32) -> i32;
940
941 #[link_name = "api_worker_post_message"]
942 fn _api_worker_post_message(handle: u32, ptr: u32, len: u32) -> i32;
943
944 #[link_name = "api_worker_recv"]
945 fn _api_worker_recv(handle: u32, out_ptr: u32, out_cap: u32) -> i64;
946
947 #[link_name = "api_worker_terminate"]
948 fn _api_worker_terminate(handle: u32) -> i32;
949
950 #[link_name = "api_worker_post"]
951 fn _api_worker_post(ptr: u32, len: u32) -> i32;
952
953 #[link_name = "api_worker_message_read"]
954 fn _api_worker_message_read(out_ptr: u32, out_cap: u32) -> u32;
955
956 #[link_name = "api_midi_input_count"]
959 fn _api_midi_input_count() -> u32;
960
961 #[link_name = "api_midi_output_count"]
962 fn _api_midi_output_count() -> u32;
963
964 #[link_name = "api_midi_input_name"]
965 fn _api_midi_input_name(index: u32, out_ptr: u32, out_cap: u32) -> u32;
966
967 #[link_name = "api_midi_output_name"]
968 fn _api_midi_output_name(index: u32, out_ptr: u32, out_cap: u32) -> u32;
969
970 #[link_name = "api_midi_open_input"]
971 fn _api_midi_open_input(index: u32) -> u32;
972
973 #[link_name = "api_midi_open_output"]
974 fn _api_midi_open_output(index: u32) -> u32;
975
976 #[link_name = "api_midi_send"]
977 fn _api_midi_send(handle: u32, data_ptr: u32, data_len: u32) -> i32;
978
979 #[link_name = "api_midi_recv"]
980 fn _api_midi_recv(handle: u32, out_ptr: u32, out_cap: u32) -> i32;
981
982 #[link_name = "api_midi_close"]
983 fn _api_midi_close(handle: u32);
984
985 #[link_name = "api_url_resolve"]
988 fn _api_url_resolve(
989 base_ptr: u32,
990 base_len: u32,
991 rel_ptr: u32,
992 rel_len: u32,
993 out_ptr: u32,
994 out_cap: u32,
995 ) -> i32;
996
997 #[link_name = "api_url_encode"]
998 fn _api_url_encode(input_ptr: u32, input_len: u32, out_ptr: u32, out_cap: u32) -> u32;
999
1000 #[link_name = "api_url_decode"]
1001 fn _api_url_decode(input_ptr: u32, input_len: u32, out_ptr: u32, out_cap: u32) -> u32;
1002
1003 #[link_name = "api_download_data"]
1006 fn _api_download_data(
1007 data_ptr: u32,
1008 data_len: u32,
1009 filename_ptr: u32,
1010 filename_len: u32,
1011 ) -> i32;
1012
1013 #[link_name = "api_download_url"]
1014 fn _api_download_url(url_ptr: u32, url_len: u32) -> i32;
1015
1016 #[link_name = "api_canvas_print_pdf"]
1017 fn _api_canvas_print_pdf(filename_ptr: u32, filename_len: u32) -> i32;
1018}
1019
1020pub fn log(msg: &str) {
1024 unsafe { _api_log(msg.as_ptr() as u32, msg.len() as u32) }
1025}
1026
1027pub fn warn(msg: &str) {
1029 unsafe { _api_warn(msg.as_ptr() as u32, msg.len() as u32) }
1030}
1031
1032pub fn error(msg: &str) {
1034 unsafe { _api_error(msg.as_ptr() as u32, msg.len() as u32) }
1035}
1036
1037pub fn get_location() -> Result<String, i32> {
1045 let mut buf = [0u8; 128];
1046 let len = unsafe { _api_get_location(buf.as_mut_ptr() as u32, buf.len() as u32) };
1047 if len < 0 {
1048 return Err(len);
1049 }
1050 Ok(String::from_utf8_lossy(&buf[..len as usize]).to_string())
1051}
1052
1053pub struct UploadedFile {
1057 pub name: String,
1058 pub data: Vec<u8>,
1059}
1060
1061pub fn upload_file() -> Option<UploadedFile> {
1064 let mut name_buf = [0u8; 256];
1065 let mut data_buf = vec![0u8; 1024 * 1024]; let result = unsafe {
1068 _api_upload_file(
1069 name_buf.as_mut_ptr() as u32,
1070 name_buf.len() as u32,
1071 data_buf.as_mut_ptr() as u32,
1072 data_buf.len() as u32,
1073 )
1074 };
1075
1076 if result == 0 {
1077 return None;
1078 }
1079
1080 let name_len = (result >> 32) as usize;
1081 let data_len = (result & 0xFFFF_FFFF) as usize;
1082
1083 Some(UploadedFile {
1084 name: String::from_utf8_lossy(&name_buf[..name_len]).to_string(),
1085 data: data_buf[..data_len].to_vec(),
1086 })
1087}
1088
1089pub struct FileMetadata {
1098 pub name: String,
1099 pub size: u64,
1100 pub mime: String,
1101 pub modified_ms: u64,
1102 pub is_dir: bool,
1103}
1104
1105pub struct FolderEntry {
1107 pub name: String,
1108 pub size: u64,
1109 pub is_dir: bool,
1110 pub handle: u32,
1111}
1112
1113pub fn file_pick(title: &str, filters: &str, multiple: bool) -> Vec<u32> {
1119 let mut buf = [0u32; 64];
1120 let n = unsafe {
1121 _api_file_pick(
1122 title.as_ptr() as u32,
1123 title.len() as u32,
1124 filters.as_ptr() as u32,
1125 filters.len() as u32,
1126 if multiple { 1 } else { 0 },
1127 buf.as_mut_ptr() as u32,
1128 (buf.len() * 4) as u32,
1129 )
1130 };
1131 if n <= 0 {
1132 return Vec::new();
1133 }
1134 buf[..n as usize].to_vec()
1135}
1136
1137pub fn folder_pick(title: &str) -> Option<u32> {
1142 let h = unsafe { _api_folder_pick(title.as_ptr() as u32, title.len() as u32) };
1143 if h == 0 {
1144 None
1145 } else {
1146 Some(h)
1147 }
1148}
1149
1150fn read_json_len(handle: u32, call: impl Fn(u32, u32, u32) -> i32) -> Option<Vec<u8>> {
1151 let mut buf = vec![0u8; 8 * 1024];
1152 let n = call(handle, buf.as_mut_ptr() as u32, buf.len() as u32);
1153 if n >= 0 {
1154 buf.truncate(n as usize);
1155 return Some(buf);
1156 }
1157 if n < -1 {
1159 let required = (-n) as usize;
1160 let mut big = vec![0u8; required];
1161 let n2 = call(handle, big.as_mut_ptr() as u32, big.len() as u32);
1162 if n2 >= 0 {
1163 big.truncate(n2 as usize);
1164 return Some(big);
1165 }
1166 }
1167 None
1168}
1169
1170pub fn folder_entries(handle: u32) -> Vec<FolderEntry> {
1176 let bytes = match read_json_len(handle, |h, p, c| unsafe { _api_folder_entries(h, p, c) }) {
1177 Some(b) => b,
1178 None => return Vec::new(),
1179 };
1180 parse_folder_entries(&bytes)
1181}
1182
1183fn parse_folder_entries(bytes: &[u8]) -> Vec<FolderEntry> {
1184 let s = core::str::from_utf8(bytes).unwrap_or("");
1187 let mut out = Vec::new();
1188 let mut rest = s.trim();
1189 if !rest.starts_with('[') {
1190 return out;
1191 }
1192 rest = &rest[1..];
1193 loop {
1194 rest = rest.trim_start_matches(|c: char| c.is_whitespace() || c == ',');
1195 if rest.starts_with(']') || rest.is_empty() {
1196 break;
1197 }
1198 let Some(end) = rest.find('}') else { break };
1199 let obj = &rest[..=end];
1200 rest = &rest[end + 1..];
1201 let name = json_str_field(obj, "\"name\":").unwrap_or_default();
1202 let size = json_num_field(obj, "\"size\":").unwrap_or(0);
1203 let is_dir = json_bool_field(obj, "\"is_dir\":").unwrap_or(false);
1204 let handle = json_num_field(obj, "\"handle\":").unwrap_or(0) as u32;
1205 out.push(FolderEntry {
1206 name,
1207 size,
1208 is_dir,
1209 handle,
1210 });
1211 }
1212 out
1213}
1214
1215fn json_str_field(obj: &str, key: &str) -> Option<String> {
1216 let idx = obj.find(key)?;
1217 let after = &obj[idx + key.len()..];
1218 let start = after.find('"')? + 1;
1219 let mut out = String::new();
1220 let bytes = after.as_bytes();
1221 let mut i = start;
1222 while i < bytes.len() {
1223 let c = bytes[i];
1224 if c == b'\\' && i + 1 < bytes.len() {
1225 match bytes[i + 1] {
1226 b'"' => out.push('"'),
1227 b'\\' => out.push('\\'),
1228 b'n' => out.push('\n'),
1229 b'r' => out.push('\r'),
1230 b't' => out.push('\t'),
1231 _ => out.push(bytes[i + 1] as char),
1232 }
1233 i += 2;
1234 } else if c == b'"' {
1235 return Some(out);
1236 } else {
1237 out.push(c as char);
1238 i += 1;
1239 }
1240 }
1241 None
1242}
1243
1244fn json_num_field(obj: &str, key: &str) -> Option<u64> {
1245 let idx = obj.find(key)?;
1246 let after = obj[idx + key.len()..].trim_start();
1247 let end = after
1248 .find(|c: char| !c.is_ascii_digit())
1249 .unwrap_or(after.len());
1250 after[..end].parse().ok()
1251}
1252
1253fn json_bool_field(obj: &str, key: &str) -> Option<bool> {
1254 let idx = obj.find(key)?;
1255 let after = obj[idx + key.len()..].trim_start();
1256 if after.starts_with("true") {
1257 Some(true)
1258 } else if after.starts_with("false") {
1259 Some(false)
1260 } else {
1261 None
1262 }
1263}
1264
1265pub fn file_read(handle: u32) -> Option<Vec<u8>> {
1270 let mut buf = vec![0u8; 64 * 1024];
1271 let n = unsafe { _api_file_read(handle, buf.as_mut_ptr() as u32, buf.len() as u32) };
1272 if n >= 0 {
1273 buf.truncate(n as usize);
1274 return Some(buf);
1275 }
1276 if n < -1 {
1277 let required = (-n) as usize;
1278 if required > 64 * 1024 * 1024 {
1279 return None;
1280 }
1281 let mut big = vec![0u8; required];
1282 let n2 = unsafe { _api_file_read(handle, big.as_mut_ptr() as u32, big.len() as u32) };
1283 if n2 >= 0 {
1284 big.truncate(n2 as usize);
1285 return Some(big);
1286 }
1287 }
1288 None
1289}
1290
1291pub fn file_read_range(handle: u32, offset: u64, len: u32) -> Option<Vec<u8>> {
1296 let mut buf = vec![0u8; len as usize];
1297 let n = unsafe {
1298 _api_file_read_range(
1299 handle,
1300 offset as u32,
1301 (offset >> 32) as u32,
1302 len,
1303 buf.as_mut_ptr() as u32,
1304 buf.len() as u32,
1305 )
1306 };
1307 if n < 0 {
1308 return None;
1309 }
1310 buf.truncate(n as usize);
1311 Some(buf)
1312}
1313
1314pub fn file_metadata(handle: u32) -> Option<FileMetadata> {
1316 let bytes = read_json_len(handle, |h, p, c| unsafe { _api_file_metadata(h, p, c) })?;
1317 let s = core::str::from_utf8(&bytes).ok()?;
1318 Some(FileMetadata {
1319 name: json_str_field(s, "\"name\":").unwrap_or_default(),
1320 size: json_num_field(s, "\"size\":").unwrap_or(0),
1321 mime: json_str_field(s, "\"mime\":").unwrap_or_default(),
1322 modified_ms: json_num_field(s, "\"modified_ms\":").unwrap_or(0),
1323 is_dir: json_bool_field(s, "\"is_dir\":").unwrap_or(false),
1324 })
1325}
1326
1327pub fn canvas_clear(r: u8, g: u8, b: u8, a: u8) {
1331 unsafe { _api_canvas_clear(r as u32, g as u32, b as u32, a as u32) }
1332}
1333
1334pub fn canvas_rect(x: f32, y: f32, w: f32, h: f32, r: u8, g: u8, b: u8, a: u8) {
1336 unsafe { _api_canvas_rect(x, y, w, h, r as u32, g as u32, b as u32, a as u32) }
1337}
1338
1339pub fn canvas_circle(cx: f32, cy: f32, radius: f32, r: u8, g: u8, b: u8, a: u8) {
1341 unsafe { _api_canvas_circle(cx, cy, radius, r as u32, g as u32, b as u32, a as u32) }
1342}
1343
1344pub fn canvas_text(x: f32, y: f32, size: f32, r: u8, g: u8, b: u8, a: u8, text: &str) {
1346 unsafe {
1347 _api_canvas_text(
1348 x,
1349 y,
1350 size,
1351 r as u32,
1352 g as u32,
1353 b as u32,
1354 a as u32,
1355 text.as_ptr() as u32,
1356 text.len() as u32,
1357 )
1358 }
1359}
1360
1361pub const FONT_STYLE_NORMAL: u32 = 0;
1363pub const FONT_STYLE_ITALIC: u32 = 1;
1365pub const FONT_STYLE_OBLIQUE: u32 = 2;
1367
1368pub const TEXT_ALIGN_LEFT: u32 = 0;
1370pub const TEXT_ALIGN_CENTER: u32 = 1;
1372pub const TEXT_ALIGN_RIGHT: u32 = 2;
1374
1375#[derive(Clone, Copy, Debug, Default)]
1377pub struct TextMetrics {
1378 pub width: f32,
1380 pub ascent: f32,
1382 pub descent: f32,
1384}
1385
1386#[allow(clippy::too_many_arguments)]
1395pub fn canvas_text_ex(
1396 x: f32,
1397 y: f32,
1398 size: f32,
1399 r: u8,
1400 g: u8,
1401 b: u8,
1402 a: u8,
1403 family: &str,
1404 weight: u32,
1405 style: u32,
1406 align: u32,
1407 text: &str,
1408) {
1409 unsafe {
1410 _api_canvas_text_ex(
1411 x,
1412 y,
1413 size,
1414 r as u32,
1415 g as u32,
1416 b as u32,
1417 a as u32,
1418 family.as_ptr() as u32,
1419 family.len() as u32,
1420 weight,
1421 style,
1422 align,
1423 text.as_ptr() as u32,
1424 text.len() as u32,
1425 )
1426 }
1427}
1428
1429pub fn canvas_measure_text(
1436 size: f32,
1437 family: &str,
1438 weight: u32,
1439 style: u32,
1440 text: &str,
1441) -> TextMetrics {
1442 let mut out = [0u8; 12];
1443 let ok = unsafe {
1444 _api_canvas_measure_text(
1445 size,
1446 family.as_ptr() as u32,
1447 family.len() as u32,
1448 weight,
1449 style,
1450 text.as_ptr() as u32,
1451 text.len() as u32,
1452 out.as_mut_ptr() as u32,
1453 )
1454 };
1455 if ok == 0 {
1456 return TextMetrics::default();
1457 }
1458 TextMetrics {
1459 width: f32::from_le_bytes([out[0], out[1], out[2], out[3]]),
1460 ascent: f32::from_le_bytes([out[4], out[5], out[6], out[7]]),
1461 descent: f32::from_le_bytes([out[8], out[9], out[10], out[11]]),
1462 }
1463}
1464
1465pub fn canvas_line(x1: f32, y1: f32, x2: f32, y2: f32, r: u8, g: u8, b: u8, a: u8, thickness: f32) {
1467 unsafe {
1468 _api_canvas_line(
1469 x1, y1, x2, y2, r as u32, g as u32, b as u32, a as u32, thickness,
1470 )
1471 }
1472}
1473
1474pub fn canvas_dimensions() -> (u32, u32) {
1476 let packed = unsafe { _api_canvas_dimensions() };
1477 ((packed >> 32) as u32, (packed & 0xFFFF_FFFF) as u32)
1478}
1479
1480pub fn set_content_size(width: u32, height: u32) {
1484 unsafe { _api_set_content_size(width, height) }
1485}
1486
1487pub fn scroll_position() -> (f32, f32) {
1491 let packed = unsafe { _api_get_scroll_position() };
1492 let x_bits = (packed >> 32) as u32;
1493 let y_bits = (packed & 0xFFFF_FFFF) as u32;
1494 (f32::from_bits(x_bits), f32::from_bits(y_bits))
1495}
1496
1497pub fn set_scroll_position(x: f32, y: f32) {
1499 unsafe { _api_set_scroll_position(x, y) }
1500}
1501
1502pub fn canvas_image(x: f32, y: f32, w: f32, h: f32, data: &[u8]) {
1505 unsafe { _api_canvas_image(x, y, w, h, data.as_ptr() as u32, data.len() as u32) }
1506}
1507
1508pub fn canvas_rounded_rect(
1512 x: f32,
1513 y: f32,
1514 w: f32,
1515 h: f32,
1516 radius: f32,
1517 r: u8,
1518 g: u8,
1519 b: u8,
1520 a: u8,
1521) {
1522 unsafe { _api_canvas_rounded_rect(x, y, w, h, radius, r as u32, g as u32, b as u32, a as u32) }
1523}
1524
1525pub fn canvas_arc(
1527 cx: f32,
1528 cy: f32,
1529 radius: f32,
1530 start_angle: f32,
1531 end_angle: f32,
1532 r: u8,
1533 g: u8,
1534 b: u8,
1535 a: u8,
1536 thickness: f32,
1537) {
1538 unsafe {
1539 _api_canvas_arc(
1540 cx,
1541 cy,
1542 radius,
1543 start_angle,
1544 end_angle,
1545 r as u32,
1546 g as u32,
1547 b as u32,
1548 a as u32,
1549 thickness,
1550 )
1551 }
1552}
1553
1554pub fn canvas_bezier(
1556 x1: f32,
1557 y1: f32,
1558 cp1x: f32,
1559 cp1y: f32,
1560 cp2x: f32,
1561 cp2y: f32,
1562 x2: f32,
1563 y2: f32,
1564 r: u8,
1565 g: u8,
1566 b: u8,
1567 a: u8,
1568 thickness: f32,
1569) {
1570 unsafe {
1571 _api_canvas_bezier(
1572 x1, y1, cp1x, cp1y, cp2x, cp2y, x2, y2, r as u32, g as u32, b as u32, a as u32,
1573 thickness,
1574 )
1575 }
1576}
1577
1578pub const GRADIENT_LINEAR: u32 = 0;
1580pub const GRADIENT_RADIAL: u32 = 1;
1581
1582pub fn canvas_gradient(
1589 x: f32,
1590 y: f32,
1591 w: f32,
1592 h: f32,
1593 kind: u32,
1594 ax: f32,
1595 ay: f32,
1596 bx: f32,
1597 by: f32,
1598 stops: &[(f32, u8, u8, u8, u8)],
1599) {
1600 let mut buf = Vec::with_capacity(stops.len() * 8);
1601 for &(offset, r, g, b, a) in stops {
1602 buf.extend_from_slice(&offset.to_le_bytes());
1603 buf.push(r);
1604 buf.push(g);
1605 buf.push(b);
1606 buf.push(a);
1607 }
1608 unsafe {
1609 _api_canvas_gradient(
1610 x,
1611 y,
1612 w,
1613 h,
1614 kind,
1615 ax,
1616 ay,
1617 bx,
1618 by,
1619 buf.as_ptr() as u32,
1620 buf.len() as u32,
1621 )
1622 }
1623}
1624
1625pub fn canvas_save() {
1630 unsafe { _api_canvas_save() }
1631}
1632
1633pub fn canvas_restore() {
1635 unsafe { _api_canvas_restore() }
1636}
1637
1638pub fn canvas_transform(a: f32, b: f32, c: f32, d: f32, tx: f32, ty: f32) {
1649 unsafe { _api_canvas_transform(a, b, c, d, tx, ty) }
1650}
1651
1652pub fn canvas_clip(x: f32, y: f32, w: f32, h: f32) {
1655 unsafe { _api_canvas_clip(x, y, w, h) }
1656}
1657
1658pub fn canvas_opacity(alpha: f32) {
1661 unsafe { _api_canvas_opacity(alpha) }
1662}
1663
1664pub mod gpu_usage {
1668 pub const VERTEX: u32 = 0x0020;
1669 pub const INDEX: u32 = 0x0010;
1670 pub const UNIFORM: u32 = 0x0040;
1671 pub const STORAGE: u32 = 0x0080;
1672}
1673
1674pub fn gpu_create_buffer(size: u64, usage: u32) -> u32 {
1678 unsafe { _api_gpu_create_buffer(size as u32, (size >> 32) as u32, usage) }
1679}
1680
1681pub fn gpu_create_texture(width: u32, height: u32) -> u32 {
1683 unsafe { _api_gpu_create_texture(width, height) }
1684}
1685
1686pub fn gpu_create_shader(source: &str) -> u32 {
1688 unsafe { _api_gpu_create_shader(source.as_ptr() as u32, source.len() as u32) }
1689}
1690
1691pub fn gpu_create_pipeline(shader: u32, vertex_entry: &str, fragment_entry: &str) -> u32 {
1695 unsafe {
1696 _api_gpu_create_render_pipeline(
1697 shader,
1698 vertex_entry.as_ptr() as u32,
1699 vertex_entry.len() as u32,
1700 fragment_entry.as_ptr() as u32,
1701 fragment_entry.len() as u32,
1702 )
1703 }
1704}
1705
1706pub fn gpu_create_compute_pipeline(shader: u32, entry_point: &str) -> u32 {
1708 unsafe {
1709 _api_gpu_create_compute_pipeline(
1710 shader,
1711 entry_point.as_ptr() as u32,
1712 entry_point.len() as u32,
1713 )
1714 }
1715}
1716
1717pub fn gpu_write_buffer(handle: u32, offset: u64, data: &[u8]) -> bool {
1719 unsafe {
1720 _api_gpu_write_buffer(
1721 handle,
1722 offset as u32,
1723 (offset >> 32) as u32,
1724 data.as_ptr() as u32,
1725 data.len() as u32,
1726 ) != 0
1727 }
1728}
1729
1730pub fn gpu_draw(
1732 pipeline: u32,
1733 target_texture: u32,
1734 vertex_count: u32,
1735 instance_count: u32,
1736) -> bool {
1737 unsafe { _api_gpu_draw(pipeline, target_texture, vertex_count, instance_count) != 0 }
1738}
1739
1740pub fn gpu_dispatch_compute(pipeline: u32, x: u32, y: u32, z: u32) -> bool {
1742 unsafe { _api_gpu_dispatch_compute(pipeline, x, y, z) != 0 }
1743}
1744
1745pub fn gpu_destroy_buffer(handle: u32) -> bool {
1747 unsafe { _api_gpu_destroy_buffer(handle) != 0 }
1748}
1749
1750pub fn gpu_destroy_texture(handle: u32) -> bool {
1752 unsafe { _api_gpu_destroy_texture(handle) != 0 }
1753}
1754
1755pub fn storage_set(key: &str, value: &str) {
1759 unsafe {
1760 _api_storage_set(
1761 key.as_ptr() as u32,
1762 key.len() as u32,
1763 value.as_ptr() as u32,
1764 value.len() as u32,
1765 )
1766 }
1767}
1768
1769pub fn storage_get(key: &str) -> String {
1771 let mut buf = [0u8; 4096];
1772 let len = unsafe {
1773 _api_storage_get(
1774 key.as_ptr() as u32,
1775 key.len() as u32,
1776 buf.as_mut_ptr() as u32,
1777 buf.len() as u32,
1778 )
1779 };
1780 String::from_utf8_lossy(&buf[..len as usize]).to_string()
1781}
1782
1783pub fn storage_remove(key: &str) {
1785 unsafe { _api_storage_remove(key.as_ptr() as u32, key.len() as u32) }
1786}
1787
1788pub fn clipboard_write(text: &str) {
1792 unsafe { _api_clipboard_write(text.as_ptr() as u32, text.len() as u32) }
1793}
1794
1795pub fn clipboard_read() -> String {
1797 let mut buf = [0u8; 4096];
1798 let len = unsafe { _api_clipboard_read(buf.as_mut_ptr() as u32, buf.len() as u32) };
1799 String::from_utf8_lossy(&buf[..len as usize]).to_string()
1800}
1801
1802pub fn time_now_ms() -> u64 {
1806 unsafe { _api_time_now_ms() }
1807}
1808
1809pub fn set_timeout(callback_id: u32, delay_ms: u32) -> u32 {
1813 unsafe { _api_set_timeout(callback_id, delay_ms) }
1814}
1815
1816pub fn set_interval(callback_id: u32, interval_ms: u32) -> u32 {
1820 unsafe { _api_set_interval(callback_id, interval_ms) }
1821}
1822
1823pub fn clear_timer(timer_id: u32) {
1825 unsafe { _api_clear_timer(timer_id) }
1826}
1827
1828pub fn request_animation_frame(callback_id: u32) -> u32 {
1834 unsafe { _api_request_animation_frame(callback_id) }
1835}
1836
1837pub fn cancel_animation_frame(request_id: u32) {
1839 unsafe { _api_cancel_animation_frame(request_id) }
1840}
1841
1842pub fn on_event(event_type: &str, callback_id: u32) -> u32 {
1873 unsafe {
1874 _api_on_event(
1875 event_type.as_ptr() as u32,
1876 event_type.len() as u32,
1877 callback_id,
1878 )
1879 }
1880}
1881
1882pub fn off_event(listener_id: u32) -> bool {
1885 unsafe { _api_off_event(listener_id) != 0 }
1886}
1887
1888pub fn emit_event(event_type: &str, data: &[u8]) {
1892 unsafe {
1893 _api_emit_event(
1894 event_type.as_ptr() as u32,
1895 event_type.len() as u32,
1896 data.as_ptr() as u32,
1897 data.len() as u32,
1898 )
1899 }
1900}
1901
1902pub fn event_type() -> String {
1905 let len = unsafe { _api_event_type_len() } as usize;
1906 if len == 0 {
1907 return String::new();
1908 }
1909 let mut buf = vec![0u8; len];
1910 let written = unsafe { _api_event_type_read(buf.as_mut_ptr() as u32, len as u32) } as usize;
1911 buf.truncate(written);
1912 String::from_utf8_lossy(&buf).into_owned()
1913}
1914
1915pub fn event_data(out: &mut [u8]) -> usize {
1918 let cap = out.len() as u32;
1919 if cap == 0 {
1920 return 0;
1921 }
1922 unsafe { _api_event_data_read(out.as_mut_ptr() as u32, cap) as usize }
1923}
1924
1925pub fn event_data_into() -> Vec<u8> {
1927 let len = unsafe { _api_event_data_len() } as usize;
1928 if len == 0 {
1929 return Vec::new();
1930 }
1931 let mut buf = vec![0u8; len];
1932 let written = unsafe { _api_event_data_read(buf.as_mut_ptr() as u32, len as u32) } as usize;
1933 buf.truncate(written);
1934 buf
1935}
1936
1937pub fn random_u64() -> u64 {
1941 unsafe { _api_random() }
1942}
1943
1944pub fn random_f64() -> f64 {
1946 (random_u64() >> 11) as f64 / (1u64 << 53) as f64
1947}
1948
1949pub fn notify(title: &str, body: &str) {
1953 unsafe {
1954 _api_notify(
1955 title.as_ptr() as u32,
1956 title.len() as u32,
1957 body.as_ptr() as u32,
1958 body.len() as u32,
1959 )
1960 }
1961}
1962
1963#[repr(u32)]
1967#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1968pub enum AudioFormat {
1969 Unknown = 0,
1971 Wav = 1,
1972 Mp3 = 2,
1973 Ogg = 3,
1974 Flac = 4,
1975}
1976
1977impl From<u32> for AudioFormat {
1978 fn from(code: u32) -> Self {
1979 match code {
1980 1 => AudioFormat::Wav,
1981 2 => AudioFormat::Mp3,
1982 3 => AudioFormat::Ogg,
1983 4 => AudioFormat::Flac,
1984 _ => AudioFormat::Unknown,
1985 }
1986 }
1987}
1988
1989impl From<AudioFormat> for u32 {
1990 fn from(f: AudioFormat) -> u32 {
1991 f as u32
1992 }
1993}
1994
1995pub fn audio_play(data: &[u8]) -> i32 {
1998 unsafe { _api_audio_play(data.as_ptr() as u32, data.len() as u32) }
1999}
2000
2001pub fn audio_detect_format(data: &[u8]) -> AudioFormat {
2003 let code = unsafe { _api_audio_detect_format(data.as_ptr() as u32, data.len() as u32) };
2004 AudioFormat::from(code)
2005}
2006
2007pub fn audio_play_with_format(data: &[u8], format: AudioFormat) -> i32 {
2010 unsafe {
2011 _api_audio_play_with_format(data.as_ptr() as u32, data.len() as u32, u32::from(format))
2012 }
2013}
2014
2015pub fn audio_play_url(url: &str) -> i32 {
2020 unsafe { _api_audio_play_url(url.as_ptr() as u32, url.len() as u32) }
2021}
2022
2023pub fn audio_last_url_content_type() -> String {
2025 let mut buf = [0u8; 512];
2026 let len =
2027 unsafe { _api_audio_last_url_content_type(buf.as_mut_ptr() as u32, buf.len() as u32) };
2028 let n = (len as usize).min(buf.len());
2029 String::from_utf8_lossy(&buf[..n]).to_string()
2030}
2031
2032pub fn audio_pause() {
2034 unsafe { _api_audio_pause() }
2035}
2036
2037pub fn audio_resume() {
2039 unsafe { _api_audio_resume() }
2040}
2041
2042pub fn audio_stop() {
2044 unsafe { _api_audio_stop() }
2045}
2046
2047pub fn audio_set_volume(level: f32) {
2049 unsafe { _api_audio_set_volume(level) }
2050}
2051
2052pub fn audio_get_volume() -> f32 {
2054 unsafe { _api_audio_get_volume() }
2055}
2056
2057pub fn audio_is_playing() -> bool {
2059 unsafe { _api_audio_is_playing() != 0 }
2060}
2061
2062pub fn audio_position() -> u64 {
2064 unsafe { _api_audio_position() }
2065}
2066
2067pub fn audio_seek(position_ms: u64) -> i32 {
2069 unsafe { _api_audio_seek(position_ms) }
2070}
2071
2072pub fn audio_duration() -> u64 {
2075 unsafe { _api_audio_duration() }
2076}
2077
2078pub fn audio_set_loop(enabled: bool) {
2081 unsafe { _api_audio_set_loop(if enabled { 1 } else { 0 }) }
2082}
2083
2084pub fn audio_channel_play(channel: u32, data: &[u8]) -> i32 {
2090 unsafe { _api_audio_channel_play(channel, data.as_ptr() as u32, data.len() as u32) }
2091}
2092
2093pub fn audio_channel_play_with_format(channel: u32, data: &[u8], format: AudioFormat) -> i32 {
2095 unsafe {
2096 _api_audio_channel_play_with_format(
2097 channel,
2098 data.as_ptr() as u32,
2099 data.len() as u32,
2100 u32::from(format),
2101 )
2102 }
2103}
2104
2105pub fn audio_channel_stop(channel: u32) {
2107 unsafe { _api_audio_channel_stop(channel) }
2108}
2109
2110pub fn audio_channel_set_volume(channel: u32, level: f32) {
2112 unsafe { _api_audio_channel_set_volume(channel, level) }
2113}
2114
2115#[repr(u32)]
2119#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2120pub enum VideoFormat {
2121 Unknown = 0,
2122 Mp4 = 1,
2123 Webm = 2,
2124 Av1 = 3,
2125}
2126
2127impl From<u32> for VideoFormat {
2128 fn from(code: u32) -> Self {
2129 match code {
2130 1 => VideoFormat::Mp4,
2131 2 => VideoFormat::Webm,
2132 3 => VideoFormat::Av1,
2133 _ => VideoFormat::Unknown,
2134 }
2135 }
2136}
2137
2138impl From<VideoFormat> for u32 {
2139 fn from(f: VideoFormat) -> u32 {
2140 f as u32
2141 }
2142}
2143
2144pub fn video_detect_format(data: &[u8]) -> VideoFormat {
2146 let code = unsafe { _api_video_detect_format(data.as_ptr() as u32, data.len() as u32) };
2147 VideoFormat::from(code)
2148}
2149
2150pub fn video_load(data: &[u8]) -> i32 {
2153 unsafe {
2154 _api_video_load(
2155 data.as_ptr() as u32,
2156 data.len() as u32,
2157 VideoFormat::Unknown as u32,
2158 )
2159 }
2160}
2161
2162pub fn video_load_with_format(data: &[u8], format: VideoFormat) -> i32 {
2164 unsafe { _api_video_load(data.as_ptr() as u32, data.len() as u32, u32::from(format)) }
2165}
2166
2167pub fn video_load_url(url: &str) -> i32 {
2169 unsafe { _api_video_load_url(url.as_ptr() as u32, url.len() as u32) }
2170}
2171
2172pub fn video_last_url_content_type() -> String {
2174 let mut buf = [0u8; 512];
2175 let len =
2176 unsafe { _api_video_last_url_content_type(buf.as_mut_ptr() as u32, buf.len() as u32) };
2177 let n = (len as usize).min(buf.len());
2178 String::from_utf8_lossy(&buf[..n]).to_string()
2179}
2180
2181pub fn video_hls_variant_count() -> u32 {
2183 unsafe { _api_video_hls_variant_count() }
2184}
2185
2186pub fn video_hls_variant_url(index: u32) -> String {
2188 let mut buf = [0u8; 2048];
2189 let len =
2190 unsafe { _api_video_hls_variant_url(index, buf.as_mut_ptr() as u32, buf.len() as u32) };
2191 let n = (len as usize).min(buf.len());
2192 String::from_utf8_lossy(&buf[..n]).to_string()
2193}
2194
2195pub fn video_hls_open_variant(index: u32) -> i32 {
2197 unsafe { _api_video_hls_open_variant(index) }
2198}
2199
2200pub fn video_play() {
2201 unsafe { _api_video_play() }
2202}
2203
2204pub fn video_pause() {
2205 unsafe { _api_video_pause() }
2206}
2207
2208pub fn video_stop() {
2209 unsafe { _api_video_stop() }
2210}
2211
2212pub fn video_seek(position_ms: u64) -> i32 {
2213 unsafe { _api_video_seek(position_ms) }
2214}
2215
2216pub fn video_position() -> u64 {
2217 unsafe { _api_video_position() }
2218}
2219
2220pub fn video_duration() -> u64 {
2221 unsafe { _api_video_duration() }
2222}
2223
2224pub fn video_render(x: f32, y: f32, w: f32, h: f32) -> i32 {
2226 unsafe { _api_video_render(x, y, w, h) }
2227}
2228
2229pub fn video_set_volume(level: f32) {
2231 unsafe { _api_video_set_volume(level) }
2232}
2233
2234pub fn video_get_volume() -> f32 {
2235 unsafe { _api_video_get_volume() }
2236}
2237
2238pub fn video_set_loop(enabled: bool) {
2239 unsafe { _api_video_set_loop(if enabled { 1 } else { 0 }) }
2240}
2241
2242pub fn video_set_pip(enabled: bool) {
2244 unsafe { _api_video_set_pip(if enabled { 1 } else { 0 }) }
2245}
2246
2247pub fn subtitle_load_srt(text: &str) -> i32 {
2249 unsafe { _api_subtitle_load_srt(text.as_ptr() as u32, text.len() as u32) }
2250}
2251
2252pub fn subtitle_load_vtt(text: &str) -> i32 {
2254 unsafe { _api_subtitle_load_vtt(text.as_ptr() as u32, text.len() as u32) }
2255}
2256
2257pub fn subtitle_clear() {
2258 unsafe { _api_subtitle_clear() }
2259}
2260
2261pub const PERMISSION_PENDING: i32 = -5;
2268
2269pub fn camera_open() -> i32 {
2274 unsafe { _api_camera_open() }
2275}
2276
2277pub fn camera_close() {
2279 unsafe { _api_camera_close() }
2280}
2281
2282pub fn camera_capture_frame(out: &mut [u8]) -> u32 {
2285 unsafe { _api_camera_capture_frame(out.as_mut_ptr() as u32, out.len() as u32) }
2286}
2287
2288pub fn camera_frame_dimensions() -> (u32, u32) {
2290 let packed = unsafe { _api_camera_frame_dimensions() };
2291 let w = (packed >> 32) as u32;
2292 let h = packed as u32;
2293 (w, h)
2294}
2295
2296pub fn microphone_open() -> i32 {
2302 unsafe { _api_microphone_open() }
2303}
2304
2305pub fn microphone_close() {
2306 unsafe { _api_microphone_close() }
2307}
2308
2309pub fn microphone_sample_rate() -> u32 {
2311 unsafe { _api_microphone_sample_rate() }
2312}
2313
2314pub fn microphone_read_samples(out: &mut [f32]) -> u32 {
2317 unsafe { _api_microphone_read_samples(out.as_mut_ptr() as u32, out.len() as u32) }
2318}
2319
2320pub fn screen_capture(out: &mut [u8]) -> Result<usize, i32> {
2327 let n = unsafe { _api_screen_capture(out.as_mut_ptr() as u32, out.len() as u32) };
2328 if n >= 0 {
2329 Ok(n as usize)
2330 } else {
2331 Err(n)
2332 }
2333}
2334
2335pub fn screen_capture_dimensions() -> (u32, u32) {
2337 let packed = unsafe { _api_screen_capture_dimensions() };
2338 let w = (packed >> 32) as u32;
2339 let h = packed as u32;
2340 (w, h)
2341}
2342
2343pub fn media_pipeline_stats() -> (u64, u32) {
2346 let packed = unsafe { _api_media_pipeline_stats() };
2347 let camera_frames = packed >> 32;
2348 let mic_ring = packed as u32;
2349 (camera_frames, mic_ring)
2350}
2351
2352pub const RTC_STATE_NEW: u32 = 0;
2356pub const RTC_STATE_CONNECTING: u32 = 1;
2358pub const RTC_STATE_CONNECTED: u32 = 2;
2360pub const RTC_STATE_DISCONNECTED: u32 = 3;
2362pub const RTC_STATE_FAILED: u32 = 4;
2364pub const RTC_STATE_CLOSED: u32 = 5;
2366
2367pub const RTC_TRACK_AUDIO: u32 = 0;
2369pub const RTC_TRACK_VIDEO: u32 = 1;
2371
2372pub struct RtcMessage {
2374 pub channel_id: u32,
2376 pub is_binary: bool,
2378 pub data: Vec<u8>,
2380}
2381
2382impl RtcMessage {
2383 pub fn text(&self) -> String {
2385 String::from_utf8_lossy(&self.data).to_string()
2386 }
2387}
2388
2389pub struct RtcDataChannelInfo {
2391 pub channel_id: u32,
2393 pub label: String,
2395}
2396
2397pub fn rtc_create_peer(stun_servers: &str) -> u32 {
2404 unsafe { _api_rtc_create_peer(stun_servers.as_ptr() as u32, stun_servers.len() as u32) }
2405}
2406
2407pub fn rtc_close_peer(peer_id: u32) -> bool {
2409 unsafe { _api_rtc_close_peer(peer_id) != 0 }
2410}
2411
2412pub fn rtc_create_offer(peer_id: u32) -> Result<String, i32> {
2416 let mut buf = vec![0u8; 16 * 1024];
2417 let n = unsafe { _api_rtc_create_offer(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
2418 if n < 0 {
2419 Err(n)
2420 } else {
2421 Ok(String::from_utf8_lossy(&buf[..n as usize]).to_string())
2422 }
2423}
2424
2425pub fn rtc_create_answer(peer_id: u32) -> Result<String, i32> {
2427 let mut buf = vec![0u8; 16 * 1024];
2428 let n = unsafe { _api_rtc_create_answer(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
2429 if n < 0 {
2430 Err(n)
2431 } else {
2432 Ok(String::from_utf8_lossy(&buf[..n as usize]).to_string())
2433 }
2434}
2435
2436pub fn rtc_set_local_description(peer_id: u32, sdp: &str, is_offer: bool) -> i32 {
2440 unsafe {
2441 _api_rtc_set_local_description(
2442 peer_id,
2443 sdp.as_ptr() as u32,
2444 sdp.len() as u32,
2445 if is_offer { 1 } else { 0 },
2446 )
2447 }
2448}
2449
2450pub fn rtc_set_remote_description(peer_id: u32, sdp: &str, is_offer: bool) -> i32 {
2452 unsafe {
2453 _api_rtc_set_remote_description(
2454 peer_id,
2455 sdp.as_ptr() as u32,
2456 sdp.len() as u32,
2457 if is_offer { 1 } else { 0 },
2458 )
2459 }
2460}
2461
2462pub fn rtc_add_ice_candidate(peer_id: u32, candidate_json: &str) -> i32 {
2464 unsafe {
2465 _api_rtc_add_ice_candidate(
2466 peer_id,
2467 candidate_json.as_ptr() as u32,
2468 candidate_json.len() as u32,
2469 )
2470 }
2471}
2472
2473pub fn rtc_connection_state(peer_id: u32) -> u32 {
2475 unsafe { _api_rtc_connection_state(peer_id) }
2476}
2477
2478pub fn rtc_poll_ice_candidate(peer_id: u32) -> Option<String> {
2481 let mut buf = vec![0u8; 4096];
2482 let n =
2483 unsafe { _api_rtc_poll_ice_candidate(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
2484 if n <= 0 {
2485 None
2486 } else {
2487 Some(String::from_utf8_lossy(&buf[..n as usize]).to_string())
2488 }
2489}
2490
2491pub fn rtc_create_data_channel(peer_id: u32, label: &str, ordered: bool) -> u32 {
2496 unsafe {
2497 _api_rtc_create_data_channel(
2498 peer_id,
2499 label.as_ptr() as u32,
2500 label.len() as u32,
2501 if ordered { 1 } else { 0 },
2502 )
2503 }
2504}
2505
2506pub fn rtc_send_text(peer_id: u32, channel_id: u32, text: &str) -> i32 {
2508 unsafe {
2509 _api_rtc_send(
2510 peer_id,
2511 channel_id,
2512 text.as_ptr() as u32,
2513 text.len() as u32,
2514 0,
2515 )
2516 }
2517}
2518
2519pub fn rtc_send_binary(peer_id: u32, channel_id: u32, data: &[u8]) -> i32 {
2521 unsafe {
2522 _api_rtc_send(
2523 peer_id,
2524 channel_id,
2525 data.as_ptr() as u32,
2526 data.len() as u32,
2527 1,
2528 )
2529 }
2530}
2531
2532pub fn rtc_send(peer_id: u32, channel_id: u32, data: &[u8], is_binary: bool) -> i32 {
2534 unsafe {
2535 _api_rtc_send(
2536 peer_id,
2537 channel_id,
2538 data.as_ptr() as u32,
2539 data.len() as u32,
2540 if is_binary { 1 } else { 0 },
2541 )
2542 }
2543}
2544
2545pub fn rtc_recv(peer_id: u32, channel_id: u32) -> Option<RtcMessage> {
2550 let mut buf = vec![0u8; 64 * 1024];
2551 let packed = unsafe {
2552 _api_rtc_recv(
2553 peer_id,
2554 channel_id,
2555 buf.as_mut_ptr() as u32,
2556 buf.len() as u32,
2557 )
2558 };
2559 if packed <= 0 {
2560 return None;
2561 }
2562 let packed = packed as u64;
2563 let data_len = (packed & 0xFFFF_FFFF) as usize;
2564 let is_binary = (packed >> 32) & 1 != 0;
2565 let ch = (packed >> 48) as u32;
2566 Some(RtcMessage {
2567 channel_id: ch,
2568 is_binary,
2569 data: buf[..data_len].to_vec(),
2570 })
2571}
2572
2573pub fn rtc_poll_data_channel(peer_id: u32) -> Option<RtcDataChannelInfo> {
2577 let mut buf = vec![0u8; 1024];
2578 let n =
2579 unsafe { _api_rtc_poll_data_channel(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
2580 if n <= 0 {
2581 return None;
2582 }
2583 let info = String::from_utf8_lossy(&buf[..n as usize]).to_string();
2584 let (id_str, label) = info.split_once(':').unwrap_or(("0", ""));
2585 Some(RtcDataChannelInfo {
2586 channel_id: id_str.parse().unwrap_or(0),
2587 label: label.to_string(),
2588 })
2589}
2590
2591pub fn rtc_add_track(peer_id: u32, kind: u32) -> u32 {
2596 unsafe { _api_rtc_add_track(peer_id, kind) }
2597}
2598
2599pub struct RtcTrackInfo {
2601 pub kind: u32,
2603 pub id: String,
2605 pub stream_id: String,
2607}
2608
2609pub fn rtc_poll_track(peer_id: u32) -> Option<RtcTrackInfo> {
2613 let mut buf = vec![0u8; 1024];
2614 let n = unsafe { _api_rtc_poll_track(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
2615 if n <= 0 {
2616 return None;
2617 }
2618 let info = String::from_utf8_lossy(&buf[..n as usize]).to_string();
2619 let mut parts = info.splitn(3, ':');
2620 let kind = parts.next().unwrap_or("2").parse().unwrap_or(2);
2621 let id = parts.next().unwrap_or("").to_string();
2622 let stream_id = parts.next().unwrap_or("").to_string();
2623 Some(RtcTrackInfo {
2624 kind,
2625 id,
2626 stream_id,
2627 })
2628}
2629
2630pub fn rtc_signal_connect(url: &str) -> bool {
2634 unsafe { _api_rtc_signal_connect(url.as_ptr() as u32, url.len() as u32) != 0 }
2635}
2636
2637pub fn rtc_signal_join_room(room: &str) -> i32 {
2639 unsafe { _api_rtc_signal_join_room(room.as_ptr() as u32, room.len() as u32) }
2640}
2641
2642pub fn rtc_signal_send(data: &[u8]) -> i32 {
2644 unsafe { _api_rtc_signal_send(data.as_ptr() as u32, data.len() as u32) }
2645}
2646
2647pub fn rtc_signal_recv() -> Option<Vec<u8>> {
2649 let mut buf = vec![0u8; 16 * 1024];
2650 let n = unsafe { _api_rtc_signal_recv(buf.as_mut_ptr() as u32, buf.len() as u32) };
2651 if n <= 0 {
2652 None
2653 } else {
2654 Some(buf[..n as usize].to_vec())
2655 }
2656}
2657
2658pub const WS_CONNECTING: u32 = 0;
2662pub const WS_OPEN: u32 = 1;
2664pub const WS_CLOSING: u32 = 2;
2666pub const WS_CLOSED: u32 = 3;
2668
2669pub struct WsMessage {
2671 pub is_binary: bool,
2673 pub data: Vec<u8>,
2675}
2676
2677impl WsMessage {
2678 pub fn text(&self) -> String {
2680 String::from_utf8_lossy(&self.data).to_string()
2681 }
2682}
2683
2684pub fn ws_connect(url: &str) -> u32 {
2690 unsafe { _api_ws_connect(url.as_ptr() as u32, url.len() as u32) }
2691}
2692
2693pub fn ws_send_text(id: u32, text: &str) -> i32 {
2697 unsafe { _api_ws_send_text(id, text.as_ptr() as u32, text.len() as u32) }
2698}
2699
2700pub fn ws_send_binary(id: u32, data: &[u8]) -> i32 {
2704 unsafe { _api_ws_send_binary(id, data.as_ptr() as u32, data.len() as u32) }
2705}
2706
2707pub fn ws_recv(id: u32) -> Option<WsMessage> {
2713 let mut buf = vec![0u8; 64 * 1024];
2714 let result = unsafe { _api_ws_recv(id, buf.as_mut_ptr() as u32, buf.len() as u32) };
2715 if result < 0 {
2716 return None;
2717 }
2718 let len = (result & 0xFFFF_FFFF) as usize;
2719 let is_binary = (result >> 32) & 1 == 1;
2720 Some(WsMessage {
2721 is_binary,
2722 data: buf[..len].to_vec(),
2723 })
2724}
2725
2726pub fn ws_ready_state(id: u32) -> u32 {
2730 unsafe { _api_ws_ready_state(id) }
2731}
2732
2733pub fn ws_close(id: u32) -> i32 {
2740 unsafe { _api_ws_close(id) }
2741}
2742
2743pub fn ws_remove(id: u32) {
2748 unsafe { _api_ws_remove(id) }
2749}
2750
2751pub fn spawn_worker(url: &str) -> i32 {
2764 unsafe { _api_spawn_worker(url.as_ptr() as u32, url.len() as u32) }
2765}
2766
2767pub fn worker_post_message(handle: u32, data: &[u8]) -> i32 {
2771 unsafe { _api_worker_post_message(handle, data.as_ptr() as u32, data.len() as u32) }
2772}
2773
2774pub fn worker_recv(handle: u32) -> Option<Vec<u8>> {
2778 let mut buf = vec![0u8; 64 * 1024];
2779 let n = unsafe { _api_worker_recv(handle, buf.as_mut_ptr() as u32, buf.len() as u32) };
2780 if n < 0 {
2781 return None;
2782 }
2783 buf.truncate(n as usize);
2784 Some(buf)
2785}
2786
2787pub fn worker_terminate(handle: u32) -> i32 {
2791 unsafe { _api_worker_terminate(handle) }
2792}
2793
2794pub fn worker_post(data: &[u8]) -> i32 {
2798 unsafe { _api_worker_post(data.as_ptr() as u32, data.len() as u32) }
2799}
2800
2801pub fn worker_message_read(buf: &mut [u8]) -> usize {
2806 unsafe { _api_worker_message_read(buf.as_mut_ptr() as u32, buf.len() as u32) as usize }
2807}
2808
2809pub fn midi_input_count() -> u32 {
2813 unsafe { _api_midi_input_count() }
2814}
2815
2816pub fn midi_output_count() -> u32 {
2818 unsafe { _api_midi_output_count() }
2819}
2820
2821pub fn midi_input_name(index: u32) -> String {
2825 let mut buf = [0u8; 128];
2826 let len = unsafe { _api_midi_input_name(index, buf.as_mut_ptr() as u32, buf.len() as u32) };
2827 String::from_utf8_lossy(&buf[..len as usize]).to_string()
2828}
2829
2830pub fn midi_output_name(index: u32) -> String {
2834 let mut buf = [0u8; 128];
2835 let len = unsafe { _api_midi_output_name(index, buf.as_mut_ptr() as u32, buf.len() as u32) };
2836 String::from_utf8_lossy(&buf[..len as usize]).to_string()
2837}
2838
2839pub fn midi_open_input(index: u32) -> u32 {
2844 unsafe { _api_midi_open_input(index) }
2845}
2846
2847pub fn midi_open_output(index: u32) -> u32 {
2851 unsafe { _api_midi_open_output(index) }
2852}
2853
2854pub fn midi_send(handle: u32, data: &[u8]) -> i32 {
2858 unsafe { _api_midi_send(handle, data.as_ptr() as u32, data.len() as u32) }
2859}
2860
2861pub fn midi_recv(handle: u32) -> Option<Vec<u8>> {
2868 let mut buf = [0u8; 256];
2869 let n = unsafe { _api_midi_recv(handle, buf.as_mut_ptr() as u32, buf.len() as u32) };
2870 if n >= 0 {
2871 return Some(buf[..n as usize].to_vec());
2872 }
2873 if n == -2 {
2875 let mut big = vec![0u8; 64 * 1024];
2876 let n2 = unsafe { _api_midi_recv(handle, big.as_mut_ptr() as u32, big.len() as u32) };
2877 if n2 >= 0 {
2878 big.truncate(n2 as usize);
2879 return Some(big);
2880 }
2881 }
2882 None
2883}
2884
2885pub fn midi_close(handle: u32) {
2887 unsafe { _api_midi_close(handle) }
2888}
2889
2890pub struct FetchResponse {
2894 pub status: u32,
2895 pub body: Vec<u8>,
2896}
2897
2898impl FetchResponse {
2899 pub fn text(&self) -> String {
2901 String::from_utf8_lossy(&self.body).to_string()
2902 }
2903}
2904
2905pub fn fetch(
2911 method: &str,
2912 url: &str,
2913 content_type: &str,
2914 body: &[u8],
2915) -> Result<FetchResponse, i64> {
2916 let mut out_buf = vec![0u8; 4 * 1024 * 1024]; let result = unsafe {
2918 _api_fetch(
2919 method.as_ptr() as u32,
2920 method.len() as u32,
2921 url.as_ptr() as u32,
2922 url.len() as u32,
2923 content_type.as_ptr() as u32,
2924 content_type.len() as u32,
2925 body.as_ptr() as u32,
2926 body.len() as u32,
2927 out_buf.as_mut_ptr() as u32,
2928 out_buf.len() as u32,
2929 )
2930 };
2931 if result < 0 {
2932 return Err(result);
2933 }
2934 let status = (result >> 32) as u32;
2935 let body_len = (result & 0xFFFF_FFFF) as usize;
2936 Ok(FetchResponse {
2937 status,
2938 body: out_buf[..body_len].to_vec(),
2939 })
2940}
2941
2942pub fn fetch_get(url: &str) -> Result<FetchResponse, i64> {
2944 fetch("GET", url, "", &[])
2945}
2946
2947pub fn fetch_post(url: &str, content_type: &str, body: &[u8]) -> Result<FetchResponse, i64> {
2949 fetch("POST", url, content_type, body)
2950}
2951
2952pub fn fetch_post_proto(url: &str, msg: &proto::ProtoEncoder) -> Result<FetchResponse, i64> {
2954 fetch("POST", url, "application/protobuf", msg.as_bytes())
2955}
2956
2957pub fn fetch_put(url: &str, content_type: &str, body: &[u8]) -> Result<FetchResponse, i64> {
2959 fetch("PUT", url, content_type, body)
2960}
2961
2962pub fn fetch_delete(url: &str) -> Result<FetchResponse, i64> {
2964 fetch("DELETE", url, "", &[])
2965}
2966
2967pub const FETCH_PENDING: u32 = 0;
2977pub const FETCH_STREAMING: u32 = 1;
2979pub const FETCH_DONE: u32 = 2;
2981pub const FETCH_ERROR: u32 = 3;
2983pub const FETCH_ABORTED: u32 = 4;
2985
2986pub enum FetchChunk {
2988 Data(Vec<u8>),
2991 Pending,
2994 End,
2996 Error,
2999}
3000
3001pub fn fetch_begin(method: &str, url: &str, content_type: &str, body: &[u8]) -> u32 {
3010 unsafe {
3011 _api_fetch_begin(
3012 method.as_ptr() as u32,
3013 method.len() as u32,
3014 url.as_ptr() as u32,
3015 url.len() as u32,
3016 content_type.as_ptr() as u32,
3017 content_type.len() as u32,
3018 body.as_ptr() as u32,
3019 body.len() as u32,
3020 )
3021 }
3022}
3023
3024pub fn fetch_begin_get(url: &str) -> u32 {
3026 fetch_begin("GET", url, "", &[])
3027}
3028
3029pub fn fetch_state(handle: u32) -> u32 {
3031 unsafe { _api_fetch_state(handle) }
3032}
3033
3034pub fn fetch_status(handle: u32) -> u32 {
3036 unsafe { _api_fetch_status(handle) }
3037}
3038
3039pub fn fetch_recv_into(handle: u32, buf: &mut [u8]) -> i64 {
3049 unsafe { _api_fetch_recv(handle, buf.as_mut_ptr() as u32, buf.len() as u32) }
3050}
3051
3052pub fn fetch_recv(handle: u32) -> FetchChunk {
3057 let mut buf = vec![0u8; 64 * 1024];
3058 let n = fetch_recv_into(handle, &mut buf);
3059 match n {
3060 -1 => FetchChunk::Pending,
3061 -2 => FetchChunk::End,
3062 -3 | -4 => FetchChunk::Error,
3063 n if n >= 0 => {
3064 buf.truncate(n as usize);
3065 FetchChunk::Data(buf)
3066 }
3067 _ => FetchChunk::Error,
3068 }
3069}
3070
3071pub fn fetch_error(handle: u32) -> Option<String> {
3073 let mut buf = [0u8; 512];
3074 let n = unsafe { _api_fetch_error(handle, buf.as_mut_ptr() as u32, buf.len() as u32) };
3075 if n < 0 {
3076 None
3077 } else {
3078 Some(String::from_utf8_lossy(&buf[..n as usize]).into_owned())
3079 }
3080}
3081
3082pub fn fetch_abort(handle: u32) -> bool {
3087 unsafe { _api_fetch_abort(handle) != 0 }
3088}
3089
3090pub fn fetch_remove(handle: u32) {
3095 unsafe { _api_fetch_remove(handle) }
3096}
3097
3098pub fn load_module(url: &str) -> i32 {
3104 unsafe { _api_load_module(url.as_ptr() as u32, url.len() as u32) }
3105}
3106
3107pub fn hash_sha256(data: &[u8]) -> [u8; 32] {
3111 let mut out = [0u8; 32];
3112 unsafe {
3113 _api_hash_sha256(
3114 data.as_ptr() as u32,
3115 data.len() as u32,
3116 out.as_mut_ptr() as u32,
3117 );
3118 }
3119 out
3120}
3121
3122pub fn hash_sha256_hex(data: &[u8]) -> String {
3124 let hash = hash_sha256(data);
3125 let mut hex = String::with_capacity(64);
3126 for byte in &hash {
3127 hex.push(HEX_CHARS[(*byte >> 4) as usize]);
3128 hex.push(HEX_CHARS[(*byte & 0x0F) as usize]);
3129 }
3130 hex
3131}
3132
3133const HEX_CHARS: [char; 16] = [
3134 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
3135];
3136
3137pub fn base64_encode(data: &[u8]) -> String {
3141 let mut buf = vec![0u8; data.len() * 4 / 3 + 8];
3142 let len = unsafe {
3143 _api_base64_encode(
3144 data.as_ptr() as u32,
3145 data.len() as u32,
3146 buf.as_mut_ptr() as u32,
3147 buf.len() as u32,
3148 )
3149 };
3150 String::from_utf8_lossy(&buf[..len as usize]).to_string()
3151}
3152
3153pub fn base64_decode(encoded: &str) -> Vec<u8> {
3155 let mut buf = vec![0u8; encoded.len()];
3156 let len = unsafe {
3157 _api_base64_decode(
3158 encoded.as_ptr() as u32,
3159 encoded.len() as u32,
3160 buf.as_mut_ptr() as u32,
3161 buf.len() as u32,
3162 )
3163 };
3164 buf[..len as usize].to_vec()
3165}
3166
3167pub fn kv_store_set(key: &str, value: &[u8]) -> bool {
3172 let rc = unsafe {
3173 _api_kv_store_set(
3174 key.as_ptr() as u32,
3175 key.len() as u32,
3176 value.as_ptr() as u32,
3177 value.len() as u32,
3178 )
3179 };
3180 rc == 0
3181}
3182
3183pub fn kv_store_set_str(key: &str, value: &str) -> bool {
3185 kv_store_set(key, value.as_bytes())
3186}
3187
3188pub fn kv_store_get(key: &str) -> Option<Vec<u8>> {
3191 let mut buf = vec![0u8; 64 * 1024]; let rc = unsafe {
3193 _api_kv_store_get(
3194 key.as_ptr() as u32,
3195 key.len() as u32,
3196 buf.as_mut_ptr() as u32,
3197 buf.len() as u32,
3198 )
3199 };
3200 if rc < 0 {
3201 return None;
3202 }
3203 Some(buf[..rc as usize].to_vec())
3204}
3205
3206pub fn kv_store_get_str(key: &str) -> Option<String> {
3208 kv_store_get(key).map(|v| String::from_utf8_lossy(&v).into_owned())
3209}
3210
3211pub fn kv_store_delete(key: &str) -> bool {
3213 let rc = unsafe { _api_kv_store_delete(key.as_ptr() as u32, key.len() as u32) };
3214 rc == 0
3215}
3216
3217pub fn navigate(url: &str) -> i32 {
3223 unsafe { _api_navigate(url.as_ptr() as u32, url.len() as u32) }
3224}
3225
3226pub fn push_state(state: &[u8], title: &str, url: &str) {
3234 unsafe {
3235 _api_push_state(
3236 state.as_ptr() as u32,
3237 state.len() as u32,
3238 title.as_ptr() as u32,
3239 title.len() as u32,
3240 url.as_ptr() as u32,
3241 url.len() as u32,
3242 )
3243 }
3244}
3245
3246pub fn replace_state(state: &[u8], title: &str, url: &str) {
3249 unsafe {
3250 _api_replace_state(
3251 state.as_ptr() as u32,
3252 state.len() as u32,
3253 title.as_ptr() as u32,
3254 title.len() as u32,
3255 url.as_ptr() as u32,
3256 url.len() as u32,
3257 )
3258 }
3259}
3260
3261pub fn get_url() -> String {
3263 let mut buf = [0u8; 4096];
3264 let len = unsafe { _api_get_url(buf.as_mut_ptr() as u32, buf.len() as u32) };
3265 String::from_utf8_lossy(&buf[..len as usize]).to_string()
3266}
3267
3268pub fn get_state() -> Option<Vec<u8>> {
3271 let mut buf = vec![0u8; 64 * 1024]; let rc = unsafe { _api_get_state(buf.as_mut_ptr() as u32, buf.len() as u32) };
3273 if rc < 0 {
3274 return None;
3275 }
3276 Some(buf[..rc as usize].to_vec())
3277}
3278
3279pub fn history_length() -> u32 {
3281 unsafe { _api_history_length() }
3282}
3283
3284pub fn history_back() -> bool {
3286 unsafe { _api_history_back() == 1 }
3287}
3288
3289pub fn history_forward() -> bool {
3291 unsafe { _api_history_forward() == 1 }
3292}
3293
3294pub fn register_hyperlink(x: f32, y: f32, w: f32, h: f32, url: &str) -> i32 {
3302 unsafe { _api_register_hyperlink(x, y, w, h, url.as_ptr() as u32, url.len() as u32) }
3303}
3304
3305pub fn clear_hyperlinks() {
3307 unsafe { _api_clear_hyperlinks() }
3308}
3309
3310pub fn url_resolve(base: &str, relative: &str) -> Option<String> {
3315 let mut buf = [0u8; 4096];
3316 let rc = unsafe {
3317 _api_url_resolve(
3318 base.as_ptr() as u32,
3319 base.len() as u32,
3320 relative.as_ptr() as u32,
3321 relative.len() as u32,
3322 buf.as_mut_ptr() as u32,
3323 buf.len() as u32,
3324 )
3325 };
3326 if rc < 0 {
3327 return None;
3328 }
3329 Some(String::from_utf8_lossy(&buf[..rc as usize]).to_string())
3330}
3331
3332pub fn url_encode(input: &str) -> String {
3334 let mut buf = vec![0u8; input.len() * 3 + 4];
3335 let len = unsafe {
3336 _api_url_encode(
3337 input.as_ptr() as u32,
3338 input.len() as u32,
3339 buf.as_mut_ptr() as u32,
3340 buf.len() as u32,
3341 )
3342 };
3343 String::from_utf8_lossy(&buf[..len as usize]).to_string()
3344}
3345
3346pub fn url_decode(input: &str) -> String {
3348 let mut buf = vec![0u8; input.len() + 4];
3349 let len = unsafe {
3350 _api_url_decode(
3351 input.as_ptr() as u32,
3352 input.len() as u32,
3353 buf.as_mut_ptr() as u32,
3354 buf.len() as u32,
3355 )
3356 };
3357 String::from_utf8_lossy(&buf[..len as usize]).to_string()
3358}
3359
3360pub fn mouse_position() -> (f32, f32) {
3364 let packed = unsafe { _api_mouse_position() };
3365 let x = f32::from_bits((packed >> 32) as u32);
3366 let y = f32::from_bits((packed & 0xFFFF_FFFF) as u32);
3367 (x, y)
3368}
3369
3370pub fn mouse_button_down(button: u32) -> bool {
3373 unsafe { _api_mouse_button_down(button) != 0 }
3374}
3375
3376pub fn mouse_button_clicked(button: u32) -> bool {
3378 unsafe { _api_mouse_button_clicked(button) != 0 }
3379}
3380
3381pub fn key_down(key: u32) -> bool {
3384 unsafe { _api_key_down(key) != 0 }
3385}
3386
3387pub fn key_pressed(key: u32) -> bool {
3389 unsafe { _api_key_pressed(key) != 0 }
3390}
3391
3392pub fn scroll_delta() -> (f32, f32) {
3394 let packed = unsafe { _api_scroll_delta() };
3395 let x = f32::from_bits((packed >> 32) as u32);
3396 let y = f32::from_bits((packed & 0xFFFF_FFFF) as u32);
3397 (x, y)
3398}
3399
3400pub fn modifiers() -> u32 {
3402 unsafe { _api_modifiers() }
3403}
3404
3405pub fn shift_held() -> bool {
3407 modifiers() & 1 != 0
3408}
3409
3410pub fn ctrl_held() -> bool {
3412 modifiers() & 2 != 0
3413}
3414
3415pub fn alt_held() -> bool {
3417 modifiers() & 4 != 0
3418}
3419
3420pub const KEY_A: u32 = 0;
3423pub const KEY_B: u32 = 1;
3424pub const KEY_C: u32 = 2;
3425pub const KEY_D: u32 = 3;
3426pub const KEY_E: u32 = 4;
3427pub const KEY_F: u32 = 5;
3428pub const KEY_G: u32 = 6;
3429pub const KEY_H: u32 = 7;
3430pub const KEY_I: u32 = 8;
3431pub const KEY_J: u32 = 9;
3432pub const KEY_K: u32 = 10;
3433pub const KEY_L: u32 = 11;
3434pub const KEY_M: u32 = 12;
3435pub const KEY_N: u32 = 13;
3436pub const KEY_O: u32 = 14;
3437pub const KEY_P: u32 = 15;
3438pub const KEY_Q: u32 = 16;
3439pub const KEY_R: u32 = 17;
3440pub const KEY_S: u32 = 18;
3441pub const KEY_T: u32 = 19;
3442pub const KEY_U: u32 = 20;
3443pub const KEY_V: u32 = 21;
3444pub const KEY_W: u32 = 22;
3445pub const KEY_X: u32 = 23;
3446pub const KEY_Y: u32 = 24;
3447pub const KEY_Z: u32 = 25;
3448pub const KEY_0: u32 = 26;
3449pub const KEY_1: u32 = 27;
3450pub const KEY_2: u32 = 28;
3451pub const KEY_3: u32 = 29;
3452pub const KEY_4: u32 = 30;
3453pub const KEY_5: u32 = 31;
3454pub const KEY_6: u32 = 32;
3455pub const KEY_7: u32 = 33;
3456pub const KEY_8: u32 = 34;
3457pub const KEY_9: u32 = 35;
3458pub const KEY_ENTER: u32 = 36;
3459pub const KEY_ESCAPE: u32 = 37;
3460pub const KEY_TAB: u32 = 38;
3461pub const KEY_BACKSPACE: u32 = 39;
3462pub const KEY_DELETE: u32 = 40;
3463pub const KEY_SPACE: u32 = 41;
3464pub const KEY_UP: u32 = 42;
3465pub const KEY_DOWN: u32 = 43;
3466pub const KEY_LEFT: u32 = 44;
3467pub const KEY_RIGHT: u32 = 45;
3468pub const KEY_HOME: u32 = 46;
3469pub const KEY_END: u32 = 47;
3470pub const KEY_PAGE_UP: u32 = 48;
3471pub const KEY_PAGE_DOWN: u32 = 49;
3472
3473#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
3481pub enum UiVariant {
3482 #[default]
3484 Default,
3485 Secondary,
3487 Outline,
3489 Ghost,
3491 Destructive,
3493}
3494
3495impl UiVariant {
3496 fn as_u32(self) -> u32 {
3497 match self {
3498 Self::Default => 0,
3499 Self::Secondary => 1,
3500 Self::Outline => 2,
3501 Self::Ghost => 3,
3502 Self::Destructive => 4,
3503 }
3504 }
3505}
3506
3507pub fn ui_button(id: u32, x: f32, y: f32, w: f32, h: f32, label: &str, on_click: impl FnOnce()) {
3513 ui_button_variant(id, x, y, w, h, label, UiVariant::Default, on_click);
3514}
3515
3516pub fn ui_button_variant(
3519 id: u32,
3520 x: f32,
3521 y: f32,
3522 w: f32,
3523 h: f32,
3524 label: &str,
3525 variant: UiVariant,
3526 on_click: impl FnOnce(),
3527) {
3528 let clicked = unsafe {
3529 _api_ui_button(
3530 id,
3531 x,
3532 y,
3533 w,
3534 h,
3535 label.as_ptr() as u32,
3536 label.len() as u32,
3537 variant.as_u32(),
3538 ) != 0
3539 };
3540 if clicked {
3541 on_click();
3542 }
3543}
3544
3545pub fn ui_checkbox(id: u32, x: f32, y: f32, label: &str, initial: bool) -> bool {
3549 unsafe {
3550 _api_ui_checkbox(
3551 id,
3552 x,
3553 y,
3554 label.as_ptr() as u32,
3555 label.len() as u32,
3556 if initial { 1 } else { 0 },
3557 ) != 0
3558 }
3559}
3560
3561pub fn ui_switch(id: u32, x: f32, y: f32, label: &str, initial: bool) -> bool {
3565 unsafe {
3566 _api_ui_switch(
3567 id,
3568 x,
3569 y,
3570 label.as_ptr() as u32,
3571 label.len() as u32,
3572 if initial { 1 } else { 0 },
3573 ) != 0
3574 }
3575}
3576
3577pub fn ui_slider(id: u32, x: f32, y: f32, w: f32, min: f32, max: f32, initial: f32) -> f32 {
3581 unsafe { _api_ui_slider(id, x, y, w, min, max, initial) }
3582}
3583
3584pub fn ui_text_input(id: u32, x: f32, y: f32, w: f32, placeholder: &str) -> String {
3593 ui_text_input_with_value(id, x, y, w, placeholder, "")
3594}
3595
3596pub fn ui_text_input_with_value(
3599 id: u32,
3600 x: f32,
3601 y: f32,
3602 w: f32,
3603 placeholder: &str,
3604 initial: &str,
3605) -> String {
3606 let mut buf = [0u8; 4096];
3607 let len = unsafe {
3608 _api_ui_text_input(
3609 id,
3610 x,
3611 y,
3612 w,
3613 initial.as_ptr() as u32,
3614 initial.len() as u32,
3615 placeholder.as_ptr() as u32,
3616 placeholder.len() as u32,
3617 buf.as_mut_ptr() as u32,
3618 buf.len() as u32,
3619 )
3620 };
3621 String::from_utf8_lossy(&buf[..len as usize]).to_string()
3622}
3623
3624pub fn ui_textarea(id: u32, x: f32, y: f32, w: f32, h: f32, placeholder: &str) -> String {
3630 ui_textarea_with_value(id, x, y, w, h, placeholder, "")
3631}
3632
3633pub fn ui_textarea_with_value(
3636 id: u32,
3637 x: f32,
3638 y: f32,
3639 w: f32,
3640 h: f32,
3641 placeholder: &str,
3642 initial: &str,
3643) -> String {
3644 let mut buf = [0u8; 16384];
3645 let len = unsafe {
3646 _api_ui_textarea(
3647 id,
3648 x,
3649 y,
3650 w,
3651 h,
3652 initial.as_ptr() as u32,
3653 initial.len() as u32,
3654 placeholder.as_ptr() as u32,
3655 placeholder.len() as u32,
3656 buf.as_mut_ptr() as u32,
3657 buf.len() as u32,
3658 )
3659 };
3660 String::from_utf8_lossy(&buf[..len as usize]).to_string()
3661}
3662
3663pub fn ui_card(x: f32, y: f32, w: f32, h: f32, title: &str, description: &str) {
3668 unsafe {
3669 _api_ui_card(
3670 x,
3671 y,
3672 w,
3673 h,
3674 title.as_ptr() as u32,
3675 title.len() as u32,
3676 description.as_ptr() as u32,
3677 description.len() as u32,
3678 );
3679 }
3680}
3681
3682pub fn ui_badge(x: f32, y: f32, label: &str, variant: UiVariant) {
3684 unsafe {
3685 _api_ui_badge(
3686 x,
3687 y,
3688 label.as_ptr() as u32,
3689 label.len() as u32,
3690 variant.as_u32(),
3691 );
3692 }
3693}
3694
3695pub fn ui_separator(x: f32, y: f32, length: f32) {
3697 unsafe { _api_ui_separator(x, y, length, 0) }
3698}
3699
3700pub fn ui_separator_vertical(x: f32, y: f32, length: f32) {
3702 unsafe { _api_ui_separator(x, y, length, 1) }
3703}
3704
3705pub fn ui_progress(x: f32, y: f32, w: f32, value: f32) {
3707 unsafe { _api_ui_progress(x, y, w, value) }
3708}
3709
3710pub fn ui_label(x: f32, y: f32, text: &str, size: f32) {
3715 unsafe { _api_ui_label(x, y, text.as_ptr() as u32, text.len() as u32, 0, size) }
3716}
3717
3718pub fn ui_label_muted(x: f32, y: f32, text: &str, size: f32) {
3720 unsafe { _api_ui_label(x, y, text.as_ptr() as u32, text.len() as u32, 1, size) }
3721}
3722
3723pub fn download_data(data: &[u8], filename: &str) -> i32 {
3729 unsafe {
3730 _api_download_data(
3731 data.as_ptr() as u32,
3732 data.len() as u32,
3733 filename.as_ptr() as u32,
3734 filename.len() as u32,
3735 )
3736 }
3737}
3738
3739pub fn download_url(url: &str) -> i32 {
3744 unsafe { _api_download_url(url.as_ptr() as u32, url.len() as u32) }
3745}
3746
3747pub fn canvas_print_pdf(filename: &str) -> i32 {
3758 unsafe { _api_canvas_print_pdf(filename.as_ptr() as u32, filename.len() as u32) }
3759}