1#![allow(clippy::too_many_arguments)]
2#![allow(clippy::doc_overindented_list_items)]
3
4pub mod draw;
124pub mod proto;
125
126#[link(wasm_import_module = "oxide")]
129extern "C" {
130 #[link_name = "api_log"]
131 fn _api_log(ptr: u32, len: u32);
132
133 #[link_name = "api_warn"]
134 fn _api_warn(ptr: u32, len: u32);
135
136 #[link_name = "api_error"]
137 fn _api_error(ptr: u32, len: u32);
138
139 #[link_name = "api_get_location"]
140 fn _api_get_location(out_ptr: u32, out_cap: u32) -> u32;
141
142 #[link_name = "api_upload_file"]
143 fn _api_upload_file(name_ptr: u32, name_cap: u32, data_ptr: u32, data_cap: u32) -> u64;
144
145 #[link_name = "api_canvas_clear"]
146 fn _api_canvas_clear(r: u32, g: u32, b: u32, a: u32);
147
148 #[link_name = "api_canvas_rect"]
149 fn _api_canvas_rect(x: f32, y: f32, w: f32, h: f32, r: u32, g: u32, b: u32, a: u32);
150
151 #[link_name = "api_canvas_circle"]
152 fn _api_canvas_circle(cx: f32, cy: f32, radius: f32, r: u32, g: u32, b: u32, a: u32);
153
154 #[link_name = "api_canvas_text"]
155 fn _api_canvas_text(
156 x: f32,
157 y: f32,
158 size: f32,
159 r: u32,
160 g: u32,
161 b: u32,
162 a: u32,
163 ptr: u32,
164 len: u32,
165 );
166
167 #[link_name = "api_canvas_line"]
168 fn _api_canvas_line(
169 x1: f32,
170 y1: f32,
171 x2: f32,
172 y2: f32,
173 r: u32,
174 g: u32,
175 b: u32,
176 a: u32,
177 thickness: f32,
178 );
179
180 #[link_name = "api_canvas_dimensions"]
181 fn _api_canvas_dimensions() -> u64;
182
183 #[link_name = "api_canvas_image"]
184 fn _api_canvas_image(x: f32, y: f32, w: f32, h: f32, data_ptr: u32, data_len: u32);
185
186 #[link_name = "api_canvas_rounded_rect"]
189 fn _api_canvas_rounded_rect(
190 x: f32,
191 y: f32,
192 w: f32,
193 h: f32,
194 radius: f32,
195 r: u32,
196 g: u32,
197 b: u32,
198 a: u32,
199 );
200
201 #[link_name = "api_canvas_arc"]
202 fn _api_canvas_arc(
203 cx: f32,
204 cy: f32,
205 radius: f32,
206 start_angle: f32,
207 end_angle: f32,
208 r: u32,
209 g: u32,
210 b: u32,
211 a: u32,
212 thickness: f32,
213 );
214
215 #[link_name = "api_canvas_bezier"]
216 fn _api_canvas_bezier(
217 x1: f32,
218 y1: f32,
219 cp1x: f32,
220 cp1y: f32,
221 cp2x: f32,
222 cp2y: f32,
223 x2: f32,
224 y2: f32,
225 r: u32,
226 g: u32,
227 b: u32,
228 a: u32,
229 thickness: f32,
230 );
231
232 #[link_name = "api_canvas_gradient"]
233 fn _api_canvas_gradient(
234 x: f32,
235 y: f32,
236 w: f32,
237 h: f32,
238 kind: u32,
239 ax: f32,
240 ay: f32,
241 bx: f32,
242 by: f32,
243 stops_ptr: u32,
244 stops_len: u32,
245 );
246
247 #[link_name = "api_canvas_save"]
250 fn _api_canvas_save();
251
252 #[link_name = "api_canvas_restore"]
253 fn _api_canvas_restore();
254
255 #[link_name = "api_canvas_transform"]
256 fn _api_canvas_transform(a: f32, b: f32, c: f32, d: f32, tx: f32, ty: f32);
257
258 #[link_name = "api_canvas_clip"]
259 fn _api_canvas_clip(x: f32, y: f32, w: f32, h: f32);
260
261 #[link_name = "api_canvas_opacity"]
262 fn _api_canvas_opacity(alpha: f32);
263
264 #[link_name = "api_storage_set"]
265 fn _api_storage_set(key_ptr: u32, key_len: u32, val_ptr: u32, val_len: u32);
266
267 #[link_name = "api_storage_get"]
268 fn _api_storage_get(key_ptr: u32, key_len: u32, out_ptr: u32, out_cap: u32) -> u32;
269
270 #[link_name = "api_storage_remove"]
271 fn _api_storage_remove(key_ptr: u32, key_len: u32);
272
273 #[link_name = "api_clipboard_write"]
274 fn _api_clipboard_write(ptr: u32, len: u32);
275
276 #[link_name = "api_clipboard_read"]
277 fn _api_clipboard_read(out_ptr: u32, out_cap: u32) -> u32;
278
279 #[link_name = "api_time_now_ms"]
280 fn _api_time_now_ms() -> u64;
281
282 #[link_name = "api_set_timeout"]
283 fn _api_set_timeout(callback_id: u32, delay_ms: u32) -> u32;
284
285 #[link_name = "api_set_interval"]
286 fn _api_set_interval(callback_id: u32, interval_ms: u32) -> u32;
287
288 #[link_name = "api_clear_timer"]
289 fn _api_clear_timer(timer_id: u32);
290
291 #[link_name = "api_random"]
292 fn _api_random() -> u64;
293
294 #[link_name = "api_notify"]
295 fn _api_notify(title_ptr: u32, title_len: u32, body_ptr: u32, body_len: u32);
296
297 #[link_name = "api_fetch"]
298 fn _api_fetch(
299 method_ptr: u32,
300 method_len: u32,
301 url_ptr: u32,
302 url_len: u32,
303 ct_ptr: u32,
304 ct_len: u32,
305 body_ptr: u32,
306 body_len: u32,
307 out_ptr: u32,
308 out_cap: u32,
309 ) -> i64;
310
311 #[link_name = "api_load_module"]
312 fn _api_load_module(url_ptr: u32, url_len: u32) -> i32;
313
314 #[link_name = "api_hash_sha256"]
315 fn _api_hash_sha256(data_ptr: u32, data_len: u32, out_ptr: u32) -> u32;
316
317 #[link_name = "api_base64_encode"]
318 fn _api_base64_encode(data_ptr: u32, data_len: u32, out_ptr: u32, out_cap: u32) -> u32;
319
320 #[link_name = "api_base64_decode"]
321 fn _api_base64_decode(data_ptr: u32, data_len: u32, out_ptr: u32, out_cap: u32) -> u32;
322
323 #[link_name = "api_kv_store_set"]
324 fn _api_kv_store_set(key_ptr: u32, key_len: u32, val_ptr: u32, val_len: u32) -> i32;
325
326 #[link_name = "api_kv_store_get"]
327 fn _api_kv_store_get(key_ptr: u32, key_len: u32, out_ptr: u32, out_cap: u32) -> i32;
328
329 #[link_name = "api_kv_store_delete"]
330 fn _api_kv_store_delete(key_ptr: u32, key_len: u32) -> i32;
331
332 #[link_name = "api_navigate"]
335 fn _api_navigate(url_ptr: u32, url_len: u32) -> i32;
336
337 #[link_name = "api_push_state"]
338 fn _api_push_state(
339 state_ptr: u32,
340 state_len: u32,
341 title_ptr: u32,
342 title_len: u32,
343 url_ptr: u32,
344 url_len: u32,
345 );
346
347 #[link_name = "api_replace_state"]
348 fn _api_replace_state(
349 state_ptr: u32,
350 state_len: u32,
351 title_ptr: u32,
352 title_len: u32,
353 url_ptr: u32,
354 url_len: u32,
355 );
356
357 #[link_name = "api_get_url"]
358 fn _api_get_url(out_ptr: u32, out_cap: u32) -> u32;
359
360 #[link_name = "api_get_state"]
361 fn _api_get_state(out_ptr: u32, out_cap: u32) -> i32;
362
363 #[link_name = "api_history_length"]
364 fn _api_history_length() -> u32;
365
366 #[link_name = "api_history_back"]
367 fn _api_history_back() -> i32;
368
369 #[link_name = "api_history_forward"]
370 fn _api_history_forward() -> i32;
371
372 #[link_name = "api_register_hyperlink"]
375 fn _api_register_hyperlink(x: f32, y: f32, w: f32, h: f32, url_ptr: u32, url_len: u32) -> i32;
376
377 #[link_name = "api_clear_hyperlinks"]
378 fn _api_clear_hyperlinks();
379
380 #[link_name = "api_mouse_position"]
383 fn _api_mouse_position() -> u64;
384
385 #[link_name = "api_mouse_button_down"]
386 fn _api_mouse_button_down(button: u32) -> u32;
387
388 #[link_name = "api_mouse_button_clicked"]
389 fn _api_mouse_button_clicked(button: u32) -> u32;
390
391 #[link_name = "api_key_down"]
392 fn _api_key_down(key: u32) -> u32;
393
394 #[link_name = "api_key_pressed"]
395 fn _api_key_pressed(key: u32) -> u32;
396
397 #[link_name = "api_scroll_delta"]
398 fn _api_scroll_delta() -> u64;
399
400 #[link_name = "api_modifiers"]
401 fn _api_modifiers() -> u32;
402
403 #[link_name = "api_ui_button"]
406 fn _api_ui_button(
407 id: u32,
408 x: f32,
409 y: f32,
410 w: f32,
411 h: f32,
412 label_ptr: u32,
413 label_len: u32,
414 ) -> u32;
415
416 #[link_name = "api_ui_checkbox"]
417 fn _api_ui_checkbox(
418 id: u32,
419 x: f32,
420 y: f32,
421 label_ptr: u32,
422 label_len: u32,
423 initial: u32,
424 ) -> u32;
425
426 #[link_name = "api_ui_slider"]
427 fn _api_ui_slider(id: u32, x: f32, y: f32, w: f32, min: f32, max: f32, initial: f32) -> f32;
428
429 #[link_name = "api_ui_text_input"]
430 fn _api_ui_text_input(
431 id: u32,
432 x: f32,
433 y: f32,
434 w: f32,
435 init_ptr: u32,
436 init_len: u32,
437 out_ptr: u32,
438 out_cap: u32,
439 ) -> u32;
440
441 #[link_name = "api_audio_play"]
444 fn _api_audio_play(data_ptr: u32, data_len: u32) -> i32;
445
446 #[link_name = "api_audio_play_url"]
447 fn _api_audio_play_url(url_ptr: u32, url_len: u32) -> i32;
448
449 #[link_name = "api_audio_detect_format"]
450 fn _api_audio_detect_format(data_ptr: u32, data_len: u32) -> u32;
451
452 #[link_name = "api_audio_play_with_format"]
453 fn _api_audio_play_with_format(data_ptr: u32, data_len: u32, format_hint: u32) -> i32;
454
455 #[link_name = "api_audio_last_url_content_type"]
456 fn _api_audio_last_url_content_type(out_ptr: u32, out_cap: u32) -> u32;
457
458 #[link_name = "api_audio_pause"]
459 fn _api_audio_pause();
460
461 #[link_name = "api_audio_resume"]
462 fn _api_audio_resume();
463
464 #[link_name = "api_audio_stop"]
465 fn _api_audio_stop();
466
467 #[link_name = "api_audio_set_volume"]
468 fn _api_audio_set_volume(level: f32);
469
470 #[link_name = "api_audio_get_volume"]
471 fn _api_audio_get_volume() -> f32;
472
473 #[link_name = "api_audio_is_playing"]
474 fn _api_audio_is_playing() -> u32;
475
476 #[link_name = "api_audio_position"]
477 fn _api_audio_position() -> u64;
478
479 #[link_name = "api_audio_seek"]
480 fn _api_audio_seek(position_ms: u64) -> i32;
481
482 #[link_name = "api_audio_duration"]
483 fn _api_audio_duration() -> u64;
484
485 #[link_name = "api_audio_set_loop"]
486 fn _api_audio_set_loop(enabled: u32);
487
488 #[link_name = "api_audio_channel_play"]
489 fn _api_audio_channel_play(channel: u32, data_ptr: u32, data_len: u32) -> i32;
490
491 #[link_name = "api_audio_channel_play_with_format"]
492 fn _api_audio_channel_play_with_format(
493 channel: u32,
494 data_ptr: u32,
495 data_len: u32,
496 format_hint: u32,
497 ) -> i32;
498
499 #[link_name = "api_audio_channel_stop"]
500 fn _api_audio_channel_stop(channel: u32);
501
502 #[link_name = "api_audio_channel_set_volume"]
503 fn _api_audio_channel_set_volume(channel: u32, level: f32);
504
505 #[link_name = "api_video_detect_format"]
508 fn _api_video_detect_format(data_ptr: u32, data_len: u32) -> u32;
509
510 #[link_name = "api_video_load"]
511 fn _api_video_load(data_ptr: u32, data_len: u32, format_hint: u32) -> i32;
512
513 #[link_name = "api_video_load_url"]
514 fn _api_video_load_url(url_ptr: u32, url_len: u32) -> i32;
515
516 #[link_name = "api_video_last_url_content_type"]
517 fn _api_video_last_url_content_type(out_ptr: u32, out_cap: u32) -> u32;
518
519 #[link_name = "api_video_hls_variant_count"]
520 fn _api_video_hls_variant_count() -> u32;
521
522 #[link_name = "api_video_hls_variant_url"]
523 fn _api_video_hls_variant_url(index: u32, out_ptr: u32, out_cap: u32) -> u32;
524
525 #[link_name = "api_video_hls_open_variant"]
526 fn _api_video_hls_open_variant(index: u32) -> i32;
527
528 #[link_name = "api_video_play"]
529 fn _api_video_play();
530
531 #[link_name = "api_video_pause"]
532 fn _api_video_pause();
533
534 #[link_name = "api_video_stop"]
535 fn _api_video_stop();
536
537 #[link_name = "api_video_seek"]
538 fn _api_video_seek(position_ms: u64) -> i32;
539
540 #[link_name = "api_video_position"]
541 fn _api_video_position() -> u64;
542
543 #[link_name = "api_video_duration"]
544 fn _api_video_duration() -> u64;
545
546 #[link_name = "api_video_render"]
547 fn _api_video_render(x: f32, y: f32, w: f32, h: f32) -> i32;
548
549 #[link_name = "api_video_set_volume"]
550 fn _api_video_set_volume(level: f32);
551
552 #[link_name = "api_video_get_volume"]
553 fn _api_video_get_volume() -> f32;
554
555 #[link_name = "api_video_set_loop"]
556 fn _api_video_set_loop(enabled: u32);
557
558 #[link_name = "api_video_set_pip"]
559 fn _api_video_set_pip(enabled: u32);
560
561 #[link_name = "api_subtitle_load_srt"]
562 fn _api_subtitle_load_srt(ptr: u32, len: u32) -> i32;
563
564 #[link_name = "api_subtitle_load_vtt"]
565 fn _api_subtitle_load_vtt(ptr: u32, len: u32) -> i32;
566
567 #[link_name = "api_subtitle_clear"]
568 fn _api_subtitle_clear();
569
570 #[link_name = "api_camera_open"]
573 fn _api_camera_open() -> i32;
574
575 #[link_name = "api_camera_close"]
576 fn _api_camera_close();
577
578 #[link_name = "api_camera_capture_frame"]
579 fn _api_camera_capture_frame(out_ptr: u32, out_cap: u32) -> u32;
580
581 #[link_name = "api_camera_frame_dimensions"]
582 fn _api_camera_frame_dimensions() -> u64;
583
584 #[link_name = "api_microphone_open"]
585 fn _api_microphone_open() -> i32;
586
587 #[link_name = "api_microphone_close"]
588 fn _api_microphone_close();
589
590 #[link_name = "api_microphone_sample_rate"]
591 fn _api_microphone_sample_rate() -> u32;
592
593 #[link_name = "api_microphone_read_samples"]
594 fn _api_microphone_read_samples(out_ptr: u32, max_samples: u32) -> u32;
595
596 #[link_name = "api_screen_capture"]
597 fn _api_screen_capture(out_ptr: u32, out_cap: u32) -> i32;
598
599 #[link_name = "api_screen_capture_dimensions"]
600 fn _api_screen_capture_dimensions() -> u64;
601
602 #[link_name = "api_media_pipeline_stats"]
603 fn _api_media_pipeline_stats() -> u64;
604
605 #[link_name = "api_gpu_create_buffer"]
608 fn _api_gpu_create_buffer(size_lo: u32, size_hi: u32, usage: u32) -> u32;
609
610 #[link_name = "api_gpu_create_texture"]
611 fn _api_gpu_create_texture(width: u32, height: u32) -> u32;
612
613 #[link_name = "api_gpu_create_shader"]
614 fn _api_gpu_create_shader(src_ptr: u32, src_len: u32) -> u32;
615
616 #[link_name = "api_gpu_create_render_pipeline"]
617 fn _api_gpu_create_render_pipeline(
618 shader: u32,
619 vs_ptr: u32,
620 vs_len: u32,
621 fs_ptr: u32,
622 fs_len: u32,
623 ) -> u32;
624
625 #[link_name = "api_gpu_create_compute_pipeline"]
626 fn _api_gpu_create_compute_pipeline(shader: u32, ep_ptr: u32, ep_len: u32) -> u32;
627
628 #[link_name = "api_gpu_write_buffer"]
629 fn _api_gpu_write_buffer(
630 handle: u32,
631 offset_lo: u32,
632 offset_hi: u32,
633 data_ptr: u32,
634 data_len: u32,
635 ) -> u32;
636
637 #[link_name = "api_gpu_draw"]
638 fn _api_gpu_draw(pipeline: u32, target: u32, vertex_count: u32, instance_count: u32) -> u32;
639
640 #[link_name = "api_gpu_dispatch_compute"]
641 fn _api_gpu_dispatch_compute(pipeline: u32, x: u32, y: u32, z: u32) -> u32;
642
643 #[link_name = "api_gpu_destroy_buffer"]
644 fn _api_gpu_destroy_buffer(handle: u32) -> u32;
645
646 #[link_name = "api_gpu_destroy_texture"]
647 fn _api_gpu_destroy_texture(handle: u32) -> u32;
648
649 #[link_name = "api_rtc_create_peer"]
652 fn _api_rtc_create_peer(stun_ptr: u32, stun_len: u32) -> u32;
653
654 #[link_name = "api_rtc_close_peer"]
655 fn _api_rtc_close_peer(peer_id: u32) -> u32;
656
657 #[link_name = "api_rtc_create_offer"]
658 fn _api_rtc_create_offer(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
659
660 #[link_name = "api_rtc_create_answer"]
661 fn _api_rtc_create_answer(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
662
663 #[link_name = "api_rtc_set_local_description"]
664 fn _api_rtc_set_local_description(
665 peer_id: u32,
666 sdp_ptr: u32,
667 sdp_len: u32,
668 is_offer: u32,
669 ) -> i32;
670
671 #[link_name = "api_rtc_set_remote_description"]
672 fn _api_rtc_set_remote_description(
673 peer_id: u32,
674 sdp_ptr: u32,
675 sdp_len: u32,
676 is_offer: u32,
677 ) -> i32;
678
679 #[link_name = "api_rtc_add_ice_candidate"]
680 fn _api_rtc_add_ice_candidate(peer_id: u32, cand_ptr: u32, cand_len: u32) -> i32;
681
682 #[link_name = "api_rtc_connection_state"]
683 fn _api_rtc_connection_state(peer_id: u32) -> u32;
684
685 #[link_name = "api_rtc_poll_ice_candidate"]
686 fn _api_rtc_poll_ice_candidate(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
687
688 #[link_name = "api_rtc_create_data_channel"]
689 fn _api_rtc_create_data_channel(
690 peer_id: u32,
691 label_ptr: u32,
692 label_len: u32,
693 ordered: u32,
694 ) -> u32;
695
696 #[link_name = "api_rtc_send"]
697 fn _api_rtc_send(
698 peer_id: u32,
699 channel_id: u32,
700 data_ptr: u32,
701 data_len: u32,
702 is_binary: u32,
703 ) -> i32;
704
705 #[link_name = "api_rtc_recv"]
706 fn _api_rtc_recv(peer_id: u32, channel_id: u32, out_ptr: u32, out_cap: u32) -> i64;
707
708 #[link_name = "api_rtc_poll_data_channel"]
709 fn _api_rtc_poll_data_channel(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
710
711 #[link_name = "api_rtc_add_track"]
712 fn _api_rtc_add_track(peer_id: u32, kind: u32) -> u32;
713
714 #[link_name = "api_rtc_poll_track"]
715 fn _api_rtc_poll_track(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
716
717 #[link_name = "api_rtc_signal_connect"]
718 fn _api_rtc_signal_connect(url_ptr: u32, url_len: u32) -> u32;
719
720 #[link_name = "api_rtc_signal_join_room"]
721 fn _api_rtc_signal_join_room(room_ptr: u32, room_len: u32) -> i32;
722
723 #[link_name = "api_rtc_signal_send"]
724 fn _api_rtc_signal_send(data_ptr: u32, data_len: u32) -> i32;
725
726 #[link_name = "api_rtc_signal_recv"]
727 fn _api_rtc_signal_recv(out_ptr: u32, out_cap: u32) -> i32;
728
729 #[link_name = "api_url_resolve"]
732 fn _api_url_resolve(
733 base_ptr: u32,
734 base_len: u32,
735 rel_ptr: u32,
736 rel_len: u32,
737 out_ptr: u32,
738 out_cap: u32,
739 ) -> i32;
740
741 #[link_name = "api_url_encode"]
742 fn _api_url_encode(input_ptr: u32, input_len: u32, out_ptr: u32, out_cap: u32) -> u32;
743
744 #[link_name = "api_url_decode"]
745 fn _api_url_decode(input_ptr: u32, input_len: u32, out_ptr: u32, out_cap: u32) -> u32;
746}
747
748pub fn log(msg: &str) {
752 unsafe { _api_log(msg.as_ptr() as u32, msg.len() as u32) }
753}
754
755pub fn warn(msg: &str) {
757 unsafe { _api_warn(msg.as_ptr() as u32, msg.len() as u32) }
758}
759
760pub fn error(msg: &str) {
762 unsafe { _api_error(msg.as_ptr() as u32, msg.len() as u32) }
763}
764
765pub fn get_location() -> String {
769 let mut buf = [0u8; 128];
770 let len = unsafe { _api_get_location(buf.as_mut_ptr() as u32, buf.len() as u32) };
771 String::from_utf8_lossy(&buf[..len as usize]).to_string()
772}
773
774pub struct UploadedFile {
778 pub name: String,
779 pub data: Vec<u8>,
780}
781
782pub fn upload_file() -> Option<UploadedFile> {
785 let mut name_buf = [0u8; 256];
786 let mut data_buf = vec![0u8; 1024 * 1024]; let result = unsafe {
789 _api_upload_file(
790 name_buf.as_mut_ptr() as u32,
791 name_buf.len() as u32,
792 data_buf.as_mut_ptr() as u32,
793 data_buf.len() as u32,
794 )
795 };
796
797 if result == 0 {
798 return None;
799 }
800
801 let name_len = (result >> 32) as usize;
802 let data_len = (result & 0xFFFF_FFFF) as usize;
803
804 Some(UploadedFile {
805 name: String::from_utf8_lossy(&name_buf[..name_len]).to_string(),
806 data: data_buf[..data_len].to_vec(),
807 })
808}
809
810pub fn canvas_clear(r: u8, g: u8, b: u8, a: u8) {
814 unsafe { _api_canvas_clear(r as u32, g as u32, b as u32, a as u32) }
815}
816
817pub fn canvas_rect(x: f32, y: f32, w: f32, h: f32, r: u8, g: u8, b: u8, a: u8) {
819 unsafe { _api_canvas_rect(x, y, w, h, r as u32, g as u32, b as u32, a as u32) }
820}
821
822pub fn canvas_circle(cx: f32, cy: f32, radius: f32, r: u8, g: u8, b: u8, a: u8) {
824 unsafe { _api_canvas_circle(cx, cy, radius, r as u32, g as u32, b as u32, a as u32) }
825}
826
827pub fn canvas_text(x: f32, y: f32, size: f32, r: u8, g: u8, b: u8, a: u8, text: &str) {
829 unsafe {
830 _api_canvas_text(
831 x,
832 y,
833 size,
834 r as u32,
835 g as u32,
836 b as u32,
837 a as u32,
838 text.as_ptr() as u32,
839 text.len() as u32,
840 )
841 }
842}
843
844pub fn canvas_line(x1: f32, y1: f32, x2: f32, y2: f32, r: u8, g: u8, b: u8, a: u8, thickness: f32) {
846 unsafe {
847 _api_canvas_line(
848 x1, y1, x2, y2, r as u32, g as u32, b as u32, a as u32, thickness,
849 )
850 }
851}
852
853pub fn canvas_dimensions() -> (u32, u32) {
855 let packed = unsafe { _api_canvas_dimensions() };
856 ((packed >> 32) as u32, (packed & 0xFFFF_FFFF) as u32)
857}
858
859pub fn canvas_image(x: f32, y: f32, w: f32, h: f32, data: &[u8]) {
862 unsafe { _api_canvas_image(x, y, w, h, data.as_ptr() as u32, data.len() as u32) }
863}
864
865pub fn canvas_rounded_rect(
869 x: f32,
870 y: f32,
871 w: f32,
872 h: f32,
873 radius: f32,
874 r: u8,
875 g: u8,
876 b: u8,
877 a: u8,
878) {
879 unsafe { _api_canvas_rounded_rect(x, y, w, h, radius, r as u32, g as u32, b as u32, a as u32) }
880}
881
882pub fn canvas_arc(
884 cx: f32,
885 cy: f32,
886 radius: f32,
887 start_angle: f32,
888 end_angle: f32,
889 r: u8,
890 g: u8,
891 b: u8,
892 a: u8,
893 thickness: f32,
894) {
895 unsafe {
896 _api_canvas_arc(
897 cx,
898 cy,
899 radius,
900 start_angle,
901 end_angle,
902 r as u32,
903 g as u32,
904 b as u32,
905 a as u32,
906 thickness,
907 )
908 }
909}
910
911pub fn canvas_bezier(
913 x1: f32,
914 y1: f32,
915 cp1x: f32,
916 cp1y: f32,
917 cp2x: f32,
918 cp2y: f32,
919 x2: f32,
920 y2: f32,
921 r: u8,
922 g: u8,
923 b: u8,
924 a: u8,
925 thickness: f32,
926) {
927 unsafe {
928 _api_canvas_bezier(
929 x1, y1, cp1x, cp1y, cp2x, cp2y, x2, y2, r as u32, g as u32, b as u32, a as u32,
930 thickness,
931 )
932 }
933}
934
935pub const GRADIENT_LINEAR: u32 = 0;
937pub const GRADIENT_RADIAL: u32 = 1;
938
939pub fn canvas_gradient(
946 x: f32,
947 y: f32,
948 w: f32,
949 h: f32,
950 kind: u32,
951 ax: f32,
952 ay: f32,
953 bx: f32,
954 by: f32,
955 stops: &[(f32, u8, u8, u8, u8)],
956) {
957 let mut buf = Vec::with_capacity(stops.len() * 8);
958 for &(offset, r, g, b, a) in stops {
959 buf.extend_from_slice(&offset.to_le_bytes());
960 buf.push(r);
961 buf.push(g);
962 buf.push(b);
963 buf.push(a);
964 }
965 unsafe {
966 _api_canvas_gradient(
967 x,
968 y,
969 w,
970 h,
971 kind,
972 ax,
973 ay,
974 bx,
975 by,
976 buf.as_ptr() as u32,
977 buf.len() as u32,
978 )
979 }
980}
981
982pub fn canvas_save() {
987 unsafe { _api_canvas_save() }
988}
989
990pub fn canvas_restore() {
992 unsafe { _api_canvas_restore() }
993}
994
995pub fn canvas_transform(a: f32, b: f32, c: f32, d: f32, tx: f32, ty: f32) {
1006 unsafe { _api_canvas_transform(a, b, c, d, tx, ty) }
1007}
1008
1009pub fn canvas_clip(x: f32, y: f32, w: f32, h: f32) {
1012 unsafe { _api_canvas_clip(x, y, w, h) }
1013}
1014
1015pub fn canvas_opacity(alpha: f32) {
1018 unsafe { _api_canvas_opacity(alpha) }
1019}
1020
1021pub mod gpu_usage {
1025 pub const VERTEX: u32 = 0x0020;
1026 pub const INDEX: u32 = 0x0010;
1027 pub const UNIFORM: u32 = 0x0040;
1028 pub const STORAGE: u32 = 0x0080;
1029}
1030
1031pub fn gpu_create_buffer(size: u64, usage: u32) -> u32 {
1035 unsafe { _api_gpu_create_buffer(size as u32, (size >> 32) as u32, usage) }
1036}
1037
1038pub fn gpu_create_texture(width: u32, height: u32) -> u32 {
1040 unsafe { _api_gpu_create_texture(width, height) }
1041}
1042
1043pub fn gpu_create_shader(source: &str) -> u32 {
1045 unsafe { _api_gpu_create_shader(source.as_ptr() as u32, source.len() as u32) }
1046}
1047
1048pub fn gpu_create_pipeline(shader: u32, vertex_entry: &str, fragment_entry: &str) -> u32 {
1052 unsafe {
1053 _api_gpu_create_render_pipeline(
1054 shader,
1055 vertex_entry.as_ptr() as u32,
1056 vertex_entry.len() as u32,
1057 fragment_entry.as_ptr() as u32,
1058 fragment_entry.len() as u32,
1059 )
1060 }
1061}
1062
1063pub fn gpu_create_compute_pipeline(shader: u32, entry_point: &str) -> u32 {
1065 unsafe {
1066 _api_gpu_create_compute_pipeline(
1067 shader,
1068 entry_point.as_ptr() as u32,
1069 entry_point.len() as u32,
1070 )
1071 }
1072}
1073
1074pub fn gpu_write_buffer(handle: u32, offset: u64, data: &[u8]) -> bool {
1076 unsafe {
1077 _api_gpu_write_buffer(
1078 handle,
1079 offset as u32,
1080 (offset >> 32) as u32,
1081 data.as_ptr() as u32,
1082 data.len() as u32,
1083 ) != 0
1084 }
1085}
1086
1087pub fn gpu_draw(
1089 pipeline: u32,
1090 target_texture: u32,
1091 vertex_count: u32,
1092 instance_count: u32,
1093) -> bool {
1094 unsafe { _api_gpu_draw(pipeline, target_texture, vertex_count, instance_count) != 0 }
1095}
1096
1097pub fn gpu_dispatch_compute(pipeline: u32, x: u32, y: u32, z: u32) -> bool {
1099 unsafe { _api_gpu_dispatch_compute(pipeline, x, y, z) != 0 }
1100}
1101
1102pub fn gpu_destroy_buffer(handle: u32) -> bool {
1104 unsafe { _api_gpu_destroy_buffer(handle) != 0 }
1105}
1106
1107pub fn gpu_destroy_texture(handle: u32) -> bool {
1109 unsafe { _api_gpu_destroy_texture(handle) != 0 }
1110}
1111
1112pub fn storage_set(key: &str, value: &str) {
1116 unsafe {
1117 _api_storage_set(
1118 key.as_ptr() as u32,
1119 key.len() as u32,
1120 value.as_ptr() as u32,
1121 value.len() as u32,
1122 )
1123 }
1124}
1125
1126pub fn storage_get(key: &str) -> String {
1128 let mut buf = [0u8; 4096];
1129 let len = unsafe {
1130 _api_storage_get(
1131 key.as_ptr() as u32,
1132 key.len() as u32,
1133 buf.as_mut_ptr() as u32,
1134 buf.len() as u32,
1135 )
1136 };
1137 String::from_utf8_lossy(&buf[..len as usize]).to_string()
1138}
1139
1140pub fn storage_remove(key: &str) {
1142 unsafe { _api_storage_remove(key.as_ptr() as u32, key.len() as u32) }
1143}
1144
1145pub fn clipboard_write(text: &str) {
1149 unsafe { _api_clipboard_write(text.as_ptr() as u32, text.len() as u32) }
1150}
1151
1152pub fn clipboard_read() -> String {
1154 let mut buf = [0u8; 4096];
1155 let len = unsafe { _api_clipboard_read(buf.as_mut_ptr() as u32, buf.len() as u32) };
1156 String::from_utf8_lossy(&buf[..len as usize]).to_string()
1157}
1158
1159pub fn time_now_ms() -> u64 {
1163 unsafe { _api_time_now_ms() }
1164}
1165
1166pub fn set_timeout(callback_id: u32, delay_ms: u32) -> u32 {
1170 unsafe { _api_set_timeout(callback_id, delay_ms) }
1171}
1172
1173pub fn set_interval(callback_id: u32, interval_ms: u32) -> u32 {
1177 unsafe { _api_set_interval(callback_id, interval_ms) }
1178}
1179
1180pub fn clear_timer(timer_id: u32) {
1182 unsafe { _api_clear_timer(timer_id) }
1183}
1184
1185pub fn random_u64() -> u64 {
1189 unsafe { _api_random() }
1190}
1191
1192pub fn random_f64() -> f64 {
1194 (random_u64() >> 11) as f64 / (1u64 << 53) as f64
1195}
1196
1197pub fn notify(title: &str, body: &str) {
1201 unsafe {
1202 _api_notify(
1203 title.as_ptr() as u32,
1204 title.len() as u32,
1205 body.as_ptr() as u32,
1206 body.len() as u32,
1207 )
1208 }
1209}
1210
1211#[repr(u32)]
1215#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1216pub enum AudioFormat {
1217 Unknown = 0,
1219 Wav = 1,
1220 Mp3 = 2,
1221 Ogg = 3,
1222 Flac = 4,
1223}
1224
1225impl From<u32> for AudioFormat {
1226 fn from(code: u32) -> Self {
1227 match code {
1228 1 => AudioFormat::Wav,
1229 2 => AudioFormat::Mp3,
1230 3 => AudioFormat::Ogg,
1231 4 => AudioFormat::Flac,
1232 _ => AudioFormat::Unknown,
1233 }
1234 }
1235}
1236
1237impl From<AudioFormat> for u32 {
1238 fn from(f: AudioFormat) -> u32 {
1239 f as u32
1240 }
1241}
1242
1243pub fn audio_play(data: &[u8]) -> i32 {
1246 unsafe { _api_audio_play(data.as_ptr() as u32, data.len() as u32) }
1247}
1248
1249pub fn audio_detect_format(data: &[u8]) -> AudioFormat {
1251 let code = unsafe { _api_audio_detect_format(data.as_ptr() as u32, data.len() as u32) };
1252 AudioFormat::from(code)
1253}
1254
1255pub fn audio_play_with_format(data: &[u8], format: AudioFormat) -> i32 {
1258 unsafe {
1259 _api_audio_play_with_format(data.as_ptr() as u32, data.len() as u32, u32::from(format))
1260 }
1261}
1262
1263pub fn audio_play_url(url: &str) -> i32 {
1268 unsafe { _api_audio_play_url(url.as_ptr() as u32, url.len() as u32) }
1269}
1270
1271pub fn audio_last_url_content_type() -> String {
1273 let mut buf = [0u8; 512];
1274 let len =
1275 unsafe { _api_audio_last_url_content_type(buf.as_mut_ptr() as u32, buf.len() as u32) };
1276 let n = (len as usize).min(buf.len());
1277 String::from_utf8_lossy(&buf[..n]).to_string()
1278}
1279
1280pub fn audio_pause() {
1282 unsafe { _api_audio_pause() }
1283}
1284
1285pub fn audio_resume() {
1287 unsafe { _api_audio_resume() }
1288}
1289
1290pub fn audio_stop() {
1292 unsafe { _api_audio_stop() }
1293}
1294
1295pub fn audio_set_volume(level: f32) {
1297 unsafe { _api_audio_set_volume(level) }
1298}
1299
1300pub fn audio_get_volume() -> f32 {
1302 unsafe { _api_audio_get_volume() }
1303}
1304
1305pub fn audio_is_playing() -> bool {
1307 unsafe { _api_audio_is_playing() != 0 }
1308}
1309
1310pub fn audio_position() -> u64 {
1312 unsafe { _api_audio_position() }
1313}
1314
1315pub fn audio_seek(position_ms: u64) -> i32 {
1317 unsafe { _api_audio_seek(position_ms) }
1318}
1319
1320pub fn audio_duration() -> u64 {
1323 unsafe { _api_audio_duration() }
1324}
1325
1326pub fn audio_set_loop(enabled: bool) {
1329 unsafe { _api_audio_set_loop(if enabled { 1 } else { 0 }) }
1330}
1331
1332pub fn audio_channel_play(channel: u32, data: &[u8]) -> i32 {
1338 unsafe { _api_audio_channel_play(channel, data.as_ptr() as u32, data.len() as u32) }
1339}
1340
1341pub fn audio_channel_play_with_format(channel: u32, data: &[u8], format: AudioFormat) -> i32 {
1343 unsafe {
1344 _api_audio_channel_play_with_format(
1345 channel,
1346 data.as_ptr() as u32,
1347 data.len() as u32,
1348 u32::from(format),
1349 )
1350 }
1351}
1352
1353pub fn audio_channel_stop(channel: u32) {
1355 unsafe { _api_audio_channel_stop(channel) }
1356}
1357
1358pub fn audio_channel_set_volume(channel: u32, level: f32) {
1360 unsafe { _api_audio_channel_set_volume(channel, level) }
1361}
1362
1363#[repr(u32)]
1367#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1368pub enum VideoFormat {
1369 Unknown = 0,
1370 Mp4 = 1,
1371 Webm = 2,
1372 Av1 = 3,
1373}
1374
1375impl From<u32> for VideoFormat {
1376 fn from(code: u32) -> Self {
1377 match code {
1378 1 => VideoFormat::Mp4,
1379 2 => VideoFormat::Webm,
1380 3 => VideoFormat::Av1,
1381 _ => VideoFormat::Unknown,
1382 }
1383 }
1384}
1385
1386impl From<VideoFormat> for u32 {
1387 fn from(f: VideoFormat) -> u32 {
1388 f as u32
1389 }
1390}
1391
1392pub fn video_detect_format(data: &[u8]) -> VideoFormat {
1394 let code = unsafe { _api_video_detect_format(data.as_ptr() as u32, data.len() as u32) };
1395 VideoFormat::from(code)
1396}
1397
1398pub fn video_load(data: &[u8]) -> i32 {
1401 unsafe {
1402 _api_video_load(
1403 data.as_ptr() as u32,
1404 data.len() as u32,
1405 VideoFormat::Unknown as u32,
1406 )
1407 }
1408}
1409
1410pub fn video_load_with_format(data: &[u8], format: VideoFormat) -> i32 {
1412 unsafe { _api_video_load(data.as_ptr() as u32, data.len() as u32, u32::from(format)) }
1413}
1414
1415pub fn video_load_url(url: &str) -> i32 {
1417 unsafe { _api_video_load_url(url.as_ptr() as u32, url.len() as u32) }
1418}
1419
1420pub fn video_last_url_content_type() -> String {
1422 let mut buf = [0u8; 512];
1423 let len =
1424 unsafe { _api_video_last_url_content_type(buf.as_mut_ptr() as u32, buf.len() as u32) };
1425 let n = (len as usize).min(buf.len());
1426 String::from_utf8_lossy(&buf[..n]).to_string()
1427}
1428
1429pub fn video_hls_variant_count() -> u32 {
1431 unsafe { _api_video_hls_variant_count() }
1432}
1433
1434pub fn video_hls_variant_url(index: u32) -> String {
1436 let mut buf = [0u8; 2048];
1437 let len =
1438 unsafe { _api_video_hls_variant_url(index, buf.as_mut_ptr() as u32, buf.len() as u32) };
1439 let n = (len as usize).min(buf.len());
1440 String::from_utf8_lossy(&buf[..n]).to_string()
1441}
1442
1443pub fn video_hls_open_variant(index: u32) -> i32 {
1445 unsafe { _api_video_hls_open_variant(index) }
1446}
1447
1448pub fn video_play() {
1449 unsafe { _api_video_play() }
1450}
1451
1452pub fn video_pause() {
1453 unsafe { _api_video_pause() }
1454}
1455
1456pub fn video_stop() {
1457 unsafe { _api_video_stop() }
1458}
1459
1460pub fn video_seek(position_ms: u64) -> i32 {
1461 unsafe { _api_video_seek(position_ms) }
1462}
1463
1464pub fn video_position() -> u64 {
1465 unsafe { _api_video_position() }
1466}
1467
1468pub fn video_duration() -> u64 {
1469 unsafe { _api_video_duration() }
1470}
1471
1472pub fn video_render(x: f32, y: f32, w: f32, h: f32) -> i32 {
1474 unsafe { _api_video_render(x, y, w, h) }
1475}
1476
1477pub fn video_set_volume(level: f32) {
1479 unsafe { _api_video_set_volume(level) }
1480}
1481
1482pub fn video_get_volume() -> f32 {
1483 unsafe { _api_video_get_volume() }
1484}
1485
1486pub fn video_set_loop(enabled: bool) {
1487 unsafe { _api_video_set_loop(if enabled { 1 } else { 0 }) }
1488}
1489
1490pub fn video_set_pip(enabled: bool) {
1492 unsafe { _api_video_set_pip(if enabled { 1 } else { 0 }) }
1493}
1494
1495pub fn subtitle_load_srt(text: &str) -> i32 {
1497 unsafe { _api_subtitle_load_srt(text.as_ptr() as u32, text.len() as u32) }
1498}
1499
1500pub fn subtitle_load_vtt(text: &str) -> i32 {
1502 unsafe { _api_subtitle_load_vtt(text.as_ptr() as u32, text.len() as u32) }
1503}
1504
1505pub fn subtitle_clear() {
1506 unsafe { _api_subtitle_clear() }
1507}
1508
1509pub fn camera_open() -> i32 {
1515 unsafe { _api_camera_open() }
1516}
1517
1518pub fn camera_close() {
1520 unsafe { _api_camera_close() }
1521}
1522
1523pub fn camera_capture_frame(out: &mut [u8]) -> u32 {
1526 unsafe { _api_camera_capture_frame(out.as_mut_ptr() as u32, out.len() as u32) }
1527}
1528
1529pub fn camera_frame_dimensions() -> (u32, u32) {
1531 let packed = unsafe { _api_camera_frame_dimensions() };
1532 let w = (packed >> 32) as u32;
1533 let h = packed as u32;
1534 (w, h)
1535}
1536
1537pub fn microphone_open() -> i32 {
1541 unsafe { _api_microphone_open() }
1542}
1543
1544pub fn microphone_close() {
1545 unsafe { _api_microphone_close() }
1546}
1547
1548pub fn microphone_sample_rate() -> u32 {
1550 unsafe { _api_microphone_sample_rate() }
1551}
1552
1553pub fn microphone_read_samples(out: &mut [f32]) -> u32 {
1556 unsafe { _api_microphone_read_samples(out.as_mut_ptr() as u32, out.len() as u32) }
1557}
1558
1559pub fn screen_capture(out: &mut [u8]) -> Result<usize, i32> {
1563 let n = unsafe { _api_screen_capture(out.as_mut_ptr() as u32, out.len() as u32) };
1564 if n >= 0 {
1565 Ok(n as usize)
1566 } else {
1567 Err(n)
1568 }
1569}
1570
1571pub fn screen_capture_dimensions() -> (u32, u32) {
1573 let packed = unsafe { _api_screen_capture_dimensions() };
1574 let w = (packed >> 32) as u32;
1575 let h = packed as u32;
1576 (w, h)
1577}
1578
1579pub fn media_pipeline_stats() -> (u64, u32) {
1582 let packed = unsafe { _api_media_pipeline_stats() };
1583 let camera_frames = packed >> 32;
1584 let mic_ring = packed as u32;
1585 (camera_frames, mic_ring)
1586}
1587
1588pub const RTC_STATE_NEW: u32 = 0;
1592pub const RTC_STATE_CONNECTING: u32 = 1;
1594pub const RTC_STATE_CONNECTED: u32 = 2;
1596pub const RTC_STATE_DISCONNECTED: u32 = 3;
1598pub const RTC_STATE_FAILED: u32 = 4;
1600pub const RTC_STATE_CLOSED: u32 = 5;
1602
1603pub const RTC_TRACK_AUDIO: u32 = 0;
1605pub const RTC_TRACK_VIDEO: u32 = 1;
1607
1608pub struct RtcMessage {
1610 pub channel_id: u32,
1612 pub is_binary: bool,
1614 pub data: Vec<u8>,
1616}
1617
1618impl RtcMessage {
1619 pub fn text(&self) -> String {
1621 String::from_utf8_lossy(&self.data).to_string()
1622 }
1623}
1624
1625pub struct RtcDataChannelInfo {
1627 pub channel_id: u32,
1629 pub label: String,
1631}
1632
1633pub fn rtc_create_peer(stun_servers: &str) -> u32 {
1640 unsafe { _api_rtc_create_peer(stun_servers.as_ptr() as u32, stun_servers.len() as u32) }
1641}
1642
1643pub fn rtc_close_peer(peer_id: u32) -> bool {
1645 unsafe { _api_rtc_close_peer(peer_id) != 0 }
1646}
1647
1648pub fn rtc_create_offer(peer_id: u32) -> Result<String, i32> {
1652 let mut buf = vec![0u8; 16 * 1024];
1653 let n = unsafe { _api_rtc_create_offer(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
1654 if n < 0 {
1655 Err(n)
1656 } else {
1657 Ok(String::from_utf8_lossy(&buf[..n as usize]).to_string())
1658 }
1659}
1660
1661pub fn rtc_create_answer(peer_id: u32) -> Result<String, i32> {
1663 let mut buf = vec![0u8; 16 * 1024];
1664 let n = unsafe { _api_rtc_create_answer(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
1665 if n < 0 {
1666 Err(n)
1667 } else {
1668 Ok(String::from_utf8_lossy(&buf[..n as usize]).to_string())
1669 }
1670}
1671
1672pub fn rtc_set_local_description(peer_id: u32, sdp: &str, is_offer: bool) -> i32 {
1676 unsafe {
1677 _api_rtc_set_local_description(
1678 peer_id,
1679 sdp.as_ptr() as u32,
1680 sdp.len() as u32,
1681 if is_offer { 1 } else { 0 },
1682 )
1683 }
1684}
1685
1686pub fn rtc_set_remote_description(peer_id: u32, sdp: &str, is_offer: bool) -> i32 {
1688 unsafe {
1689 _api_rtc_set_remote_description(
1690 peer_id,
1691 sdp.as_ptr() as u32,
1692 sdp.len() as u32,
1693 if is_offer { 1 } else { 0 },
1694 )
1695 }
1696}
1697
1698pub fn rtc_add_ice_candidate(peer_id: u32, candidate_json: &str) -> i32 {
1700 unsafe {
1701 _api_rtc_add_ice_candidate(
1702 peer_id,
1703 candidate_json.as_ptr() as u32,
1704 candidate_json.len() as u32,
1705 )
1706 }
1707}
1708
1709pub fn rtc_connection_state(peer_id: u32) -> u32 {
1711 unsafe { _api_rtc_connection_state(peer_id) }
1712}
1713
1714pub fn rtc_poll_ice_candidate(peer_id: u32) -> Option<String> {
1717 let mut buf = vec![0u8; 4096];
1718 let n =
1719 unsafe { _api_rtc_poll_ice_candidate(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
1720 if n <= 0 {
1721 None
1722 } else {
1723 Some(String::from_utf8_lossy(&buf[..n as usize]).to_string())
1724 }
1725}
1726
1727pub fn rtc_create_data_channel(peer_id: u32, label: &str, ordered: bool) -> u32 {
1732 unsafe {
1733 _api_rtc_create_data_channel(
1734 peer_id,
1735 label.as_ptr() as u32,
1736 label.len() as u32,
1737 if ordered { 1 } else { 0 },
1738 )
1739 }
1740}
1741
1742pub fn rtc_send_text(peer_id: u32, channel_id: u32, text: &str) -> i32 {
1744 unsafe {
1745 _api_rtc_send(
1746 peer_id,
1747 channel_id,
1748 text.as_ptr() as u32,
1749 text.len() as u32,
1750 0,
1751 )
1752 }
1753}
1754
1755pub fn rtc_send_binary(peer_id: u32, channel_id: u32, data: &[u8]) -> i32 {
1757 unsafe {
1758 _api_rtc_send(
1759 peer_id,
1760 channel_id,
1761 data.as_ptr() as u32,
1762 data.len() as u32,
1763 1,
1764 )
1765 }
1766}
1767
1768pub fn rtc_send(peer_id: u32, channel_id: u32, data: &[u8], is_binary: bool) -> i32 {
1770 unsafe {
1771 _api_rtc_send(
1772 peer_id,
1773 channel_id,
1774 data.as_ptr() as u32,
1775 data.len() as u32,
1776 if is_binary { 1 } else { 0 },
1777 )
1778 }
1779}
1780
1781pub fn rtc_recv(peer_id: u32, channel_id: u32) -> Option<RtcMessage> {
1786 let mut buf = vec![0u8; 64 * 1024];
1787 let packed = unsafe {
1788 _api_rtc_recv(
1789 peer_id,
1790 channel_id,
1791 buf.as_mut_ptr() as u32,
1792 buf.len() as u32,
1793 )
1794 };
1795 if packed <= 0 {
1796 return None;
1797 }
1798 let packed = packed as u64;
1799 let data_len = (packed & 0xFFFF_FFFF) as usize;
1800 let is_binary = (packed >> 32) & 1 != 0;
1801 let ch = (packed >> 48) as u32;
1802 Some(RtcMessage {
1803 channel_id: ch,
1804 is_binary,
1805 data: buf[..data_len].to_vec(),
1806 })
1807}
1808
1809pub fn rtc_poll_data_channel(peer_id: u32) -> Option<RtcDataChannelInfo> {
1813 let mut buf = vec![0u8; 1024];
1814 let n =
1815 unsafe { _api_rtc_poll_data_channel(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
1816 if n <= 0 {
1817 return None;
1818 }
1819 let info = String::from_utf8_lossy(&buf[..n as usize]).to_string();
1820 let (id_str, label) = info.split_once(':').unwrap_or(("0", ""));
1821 Some(RtcDataChannelInfo {
1822 channel_id: id_str.parse().unwrap_or(0),
1823 label: label.to_string(),
1824 })
1825}
1826
1827pub fn rtc_add_track(peer_id: u32, kind: u32) -> u32 {
1832 unsafe { _api_rtc_add_track(peer_id, kind) }
1833}
1834
1835pub struct RtcTrackInfo {
1837 pub kind: u32,
1839 pub id: String,
1841 pub stream_id: String,
1843}
1844
1845pub fn rtc_poll_track(peer_id: u32) -> Option<RtcTrackInfo> {
1849 let mut buf = vec![0u8; 1024];
1850 let n = unsafe { _api_rtc_poll_track(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
1851 if n <= 0 {
1852 return None;
1853 }
1854 let info = String::from_utf8_lossy(&buf[..n as usize]).to_string();
1855 let mut parts = info.splitn(3, ':');
1856 let kind = parts.next().unwrap_or("2").parse().unwrap_or(2);
1857 let id = parts.next().unwrap_or("").to_string();
1858 let stream_id = parts.next().unwrap_or("").to_string();
1859 Some(RtcTrackInfo {
1860 kind,
1861 id,
1862 stream_id,
1863 })
1864}
1865
1866pub fn rtc_signal_connect(url: &str) -> bool {
1870 unsafe { _api_rtc_signal_connect(url.as_ptr() as u32, url.len() as u32) != 0 }
1871}
1872
1873pub fn rtc_signal_join_room(room: &str) -> i32 {
1875 unsafe { _api_rtc_signal_join_room(room.as_ptr() as u32, room.len() as u32) }
1876}
1877
1878pub fn rtc_signal_send(data: &[u8]) -> i32 {
1880 unsafe { _api_rtc_signal_send(data.as_ptr() as u32, data.len() as u32) }
1881}
1882
1883pub fn rtc_signal_recv() -> Option<Vec<u8>> {
1885 let mut buf = vec![0u8; 16 * 1024];
1886 let n = unsafe { _api_rtc_signal_recv(buf.as_mut_ptr() as u32, buf.len() as u32) };
1887 if n <= 0 {
1888 None
1889 } else {
1890 Some(buf[..n as usize].to_vec())
1891 }
1892}
1893
1894pub struct FetchResponse {
1898 pub status: u32,
1899 pub body: Vec<u8>,
1900}
1901
1902impl FetchResponse {
1903 pub fn text(&self) -> String {
1905 String::from_utf8_lossy(&self.body).to_string()
1906 }
1907}
1908
1909pub fn fetch(
1915 method: &str,
1916 url: &str,
1917 content_type: &str,
1918 body: &[u8],
1919) -> Result<FetchResponse, i64> {
1920 let mut out_buf = vec![0u8; 4 * 1024 * 1024]; let result = unsafe {
1922 _api_fetch(
1923 method.as_ptr() as u32,
1924 method.len() as u32,
1925 url.as_ptr() as u32,
1926 url.len() as u32,
1927 content_type.as_ptr() as u32,
1928 content_type.len() as u32,
1929 body.as_ptr() as u32,
1930 body.len() as u32,
1931 out_buf.as_mut_ptr() as u32,
1932 out_buf.len() as u32,
1933 )
1934 };
1935 if result < 0 {
1936 return Err(result);
1937 }
1938 let status = (result >> 32) as u32;
1939 let body_len = (result & 0xFFFF_FFFF) as usize;
1940 Ok(FetchResponse {
1941 status,
1942 body: out_buf[..body_len].to_vec(),
1943 })
1944}
1945
1946pub fn fetch_get(url: &str) -> Result<FetchResponse, i64> {
1948 fetch("GET", url, "", &[])
1949}
1950
1951pub fn fetch_post(url: &str, content_type: &str, body: &[u8]) -> Result<FetchResponse, i64> {
1953 fetch("POST", url, content_type, body)
1954}
1955
1956pub fn fetch_post_proto(url: &str, msg: &proto::ProtoEncoder) -> Result<FetchResponse, i64> {
1958 fetch("POST", url, "application/protobuf", msg.as_bytes())
1959}
1960
1961pub fn fetch_put(url: &str, content_type: &str, body: &[u8]) -> Result<FetchResponse, i64> {
1963 fetch("PUT", url, content_type, body)
1964}
1965
1966pub fn fetch_delete(url: &str) -> Result<FetchResponse, i64> {
1968 fetch("DELETE", url, "", &[])
1969}
1970
1971pub fn load_module(url: &str) -> i32 {
1977 unsafe { _api_load_module(url.as_ptr() as u32, url.len() as u32) }
1978}
1979
1980pub fn hash_sha256(data: &[u8]) -> [u8; 32] {
1984 let mut out = [0u8; 32];
1985 unsafe {
1986 _api_hash_sha256(
1987 data.as_ptr() as u32,
1988 data.len() as u32,
1989 out.as_mut_ptr() as u32,
1990 );
1991 }
1992 out
1993}
1994
1995pub fn hash_sha256_hex(data: &[u8]) -> String {
1997 let hash = hash_sha256(data);
1998 let mut hex = String::with_capacity(64);
1999 for byte in &hash {
2000 hex.push(HEX_CHARS[(*byte >> 4) as usize]);
2001 hex.push(HEX_CHARS[(*byte & 0x0F) as usize]);
2002 }
2003 hex
2004}
2005
2006const HEX_CHARS: [char; 16] = [
2007 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
2008];
2009
2010pub fn base64_encode(data: &[u8]) -> String {
2014 let mut buf = vec![0u8; data.len() * 4 / 3 + 8];
2015 let len = unsafe {
2016 _api_base64_encode(
2017 data.as_ptr() as u32,
2018 data.len() as u32,
2019 buf.as_mut_ptr() as u32,
2020 buf.len() as u32,
2021 )
2022 };
2023 String::from_utf8_lossy(&buf[..len as usize]).to_string()
2024}
2025
2026pub fn base64_decode(encoded: &str) -> Vec<u8> {
2028 let mut buf = vec![0u8; encoded.len()];
2029 let len = unsafe {
2030 _api_base64_decode(
2031 encoded.as_ptr() as u32,
2032 encoded.len() as u32,
2033 buf.as_mut_ptr() as u32,
2034 buf.len() as u32,
2035 )
2036 };
2037 buf[..len as usize].to_vec()
2038}
2039
2040pub fn kv_store_set(key: &str, value: &[u8]) -> bool {
2045 let rc = unsafe {
2046 _api_kv_store_set(
2047 key.as_ptr() as u32,
2048 key.len() as u32,
2049 value.as_ptr() as u32,
2050 value.len() as u32,
2051 )
2052 };
2053 rc == 0
2054}
2055
2056pub fn kv_store_set_str(key: &str, value: &str) -> bool {
2058 kv_store_set(key, value.as_bytes())
2059}
2060
2061pub fn kv_store_get(key: &str) -> Option<Vec<u8>> {
2064 let mut buf = vec![0u8; 64 * 1024]; let rc = unsafe {
2066 _api_kv_store_get(
2067 key.as_ptr() as u32,
2068 key.len() as u32,
2069 buf.as_mut_ptr() as u32,
2070 buf.len() as u32,
2071 )
2072 };
2073 if rc < 0 {
2074 return None;
2075 }
2076 Some(buf[..rc as usize].to_vec())
2077}
2078
2079pub fn kv_store_get_str(key: &str) -> Option<String> {
2081 kv_store_get(key).map(|v| String::from_utf8_lossy(&v).into_owned())
2082}
2083
2084pub fn kv_store_delete(key: &str) -> bool {
2086 let rc = unsafe { _api_kv_store_delete(key.as_ptr() as u32, key.len() as u32) };
2087 rc == 0
2088}
2089
2090pub fn navigate(url: &str) -> i32 {
2096 unsafe { _api_navigate(url.as_ptr() as u32, url.len() as u32) }
2097}
2098
2099pub fn push_state(state: &[u8], title: &str, url: &str) {
2107 unsafe {
2108 _api_push_state(
2109 state.as_ptr() as u32,
2110 state.len() as u32,
2111 title.as_ptr() as u32,
2112 title.len() as u32,
2113 url.as_ptr() as u32,
2114 url.len() as u32,
2115 )
2116 }
2117}
2118
2119pub fn replace_state(state: &[u8], title: &str, url: &str) {
2122 unsafe {
2123 _api_replace_state(
2124 state.as_ptr() as u32,
2125 state.len() as u32,
2126 title.as_ptr() as u32,
2127 title.len() as u32,
2128 url.as_ptr() as u32,
2129 url.len() as u32,
2130 )
2131 }
2132}
2133
2134pub fn get_url() -> String {
2136 let mut buf = [0u8; 4096];
2137 let len = unsafe { _api_get_url(buf.as_mut_ptr() as u32, buf.len() as u32) };
2138 String::from_utf8_lossy(&buf[..len as usize]).to_string()
2139}
2140
2141pub fn get_state() -> Option<Vec<u8>> {
2144 let mut buf = vec![0u8; 64 * 1024]; let rc = unsafe { _api_get_state(buf.as_mut_ptr() as u32, buf.len() as u32) };
2146 if rc < 0 {
2147 return None;
2148 }
2149 Some(buf[..rc as usize].to_vec())
2150}
2151
2152pub fn history_length() -> u32 {
2154 unsafe { _api_history_length() }
2155}
2156
2157pub fn history_back() -> bool {
2159 unsafe { _api_history_back() == 1 }
2160}
2161
2162pub fn history_forward() -> bool {
2164 unsafe { _api_history_forward() == 1 }
2165}
2166
2167pub fn register_hyperlink(x: f32, y: f32, w: f32, h: f32, url: &str) -> i32 {
2175 unsafe { _api_register_hyperlink(x, y, w, h, url.as_ptr() as u32, url.len() as u32) }
2176}
2177
2178pub fn clear_hyperlinks() {
2180 unsafe { _api_clear_hyperlinks() }
2181}
2182
2183pub fn url_resolve(base: &str, relative: &str) -> Option<String> {
2188 let mut buf = [0u8; 4096];
2189 let rc = unsafe {
2190 _api_url_resolve(
2191 base.as_ptr() as u32,
2192 base.len() as u32,
2193 relative.as_ptr() as u32,
2194 relative.len() as u32,
2195 buf.as_mut_ptr() as u32,
2196 buf.len() as u32,
2197 )
2198 };
2199 if rc < 0 {
2200 return None;
2201 }
2202 Some(String::from_utf8_lossy(&buf[..rc as usize]).to_string())
2203}
2204
2205pub fn url_encode(input: &str) -> String {
2207 let mut buf = vec![0u8; input.len() * 3 + 4];
2208 let len = unsafe {
2209 _api_url_encode(
2210 input.as_ptr() as u32,
2211 input.len() as u32,
2212 buf.as_mut_ptr() as u32,
2213 buf.len() as u32,
2214 )
2215 };
2216 String::from_utf8_lossy(&buf[..len as usize]).to_string()
2217}
2218
2219pub fn url_decode(input: &str) -> String {
2221 let mut buf = vec![0u8; input.len() + 4];
2222 let len = unsafe {
2223 _api_url_decode(
2224 input.as_ptr() as u32,
2225 input.len() as u32,
2226 buf.as_mut_ptr() as u32,
2227 buf.len() as u32,
2228 )
2229 };
2230 String::from_utf8_lossy(&buf[..len as usize]).to_string()
2231}
2232
2233pub fn mouse_position() -> (f32, f32) {
2237 let packed = unsafe { _api_mouse_position() };
2238 let x = f32::from_bits((packed >> 32) as u32);
2239 let y = f32::from_bits((packed & 0xFFFF_FFFF) as u32);
2240 (x, y)
2241}
2242
2243pub fn mouse_button_down(button: u32) -> bool {
2246 unsafe { _api_mouse_button_down(button) != 0 }
2247}
2248
2249pub fn mouse_button_clicked(button: u32) -> bool {
2251 unsafe { _api_mouse_button_clicked(button) != 0 }
2252}
2253
2254pub fn key_down(key: u32) -> bool {
2257 unsafe { _api_key_down(key) != 0 }
2258}
2259
2260pub fn key_pressed(key: u32) -> bool {
2262 unsafe { _api_key_pressed(key) != 0 }
2263}
2264
2265pub fn scroll_delta() -> (f32, f32) {
2267 let packed = unsafe { _api_scroll_delta() };
2268 let x = f32::from_bits((packed >> 32) as u32);
2269 let y = f32::from_bits((packed & 0xFFFF_FFFF) as u32);
2270 (x, y)
2271}
2272
2273pub fn modifiers() -> u32 {
2275 unsafe { _api_modifiers() }
2276}
2277
2278pub fn shift_held() -> bool {
2280 modifiers() & 1 != 0
2281}
2282
2283pub fn ctrl_held() -> bool {
2285 modifiers() & 2 != 0
2286}
2287
2288pub fn alt_held() -> bool {
2290 modifiers() & 4 != 0
2291}
2292
2293pub const KEY_A: u32 = 0;
2296pub const KEY_B: u32 = 1;
2297pub const KEY_C: u32 = 2;
2298pub const KEY_D: u32 = 3;
2299pub const KEY_E: u32 = 4;
2300pub const KEY_F: u32 = 5;
2301pub const KEY_G: u32 = 6;
2302pub const KEY_H: u32 = 7;
2303pub const KEY_I: u32 = 8;
2304pub const KEY_J: u32 = 9;
2305pub const KEY_K: u32 = 10;
2306pub const KEY_L: u32 = 11;
2307pub const KEY_M: u32 = 12;
2308pub const KEY_N: u32 = 13;
2309pub const KEY_O: u32 = 14;
2310pub const KEY_P: u32 = 15;
2311pub const KEY_Q: u32 = 16;
2312pub const KEY_R: u32 = 17;
2313pub const KEY_S: u32 = 18;
2314pub const KEY_T: u32 = 19;
2315pub const KEY_U: u32 = 20;
2316pub const KEY_V: u32 = 21;
2317pub const KEY_W: u32 = 22;
2318pub const KEY_X: u32 = 23;
2319pub const KEY_Y: u32 = 24;
2320pub const KEY_Z: u32 = 25;
2321pub const KEY_0: u32 = 26;
2322pub const KEY_1: u32 = 27;
2323pub const KEY_2: u32 = 28;
2324pub const KEY_3: u32 = 29;
2325pub const KEY_4: u32 = 30;
2326pub const KEY_5: u32 = 31;
2327pub const KEY_6: u32 = 32;
2328pub const KEY_7: u32 = 33;
2329pub const KEY_8: u32 = 34;
2330pub const KEY_9: u32 = 35;
2331pub const KEY_ENTER: u32 = 36;
2332pub const KEY_ESCAPE: u32 = 37;
2333pub const KEY_TAB: u32 = 38;
2334pub const KEY_BACKSPACE: u32 = 39;
2335pub const KEY_DELETE: u32 = 40;
2336pub const KEY_SPACE: u32 = 41;
2337pub const KEY_UP: u32 = 42;
2338pub const KEY_DOWN: u32 = 43;
2339pub const KEY_LEFT: u32 = 44;
2340pub const KEY_RIGHT: u32 = 45;
2341pub const KEY_HOME: u32 = 46;
2342pub const KEY_END: u32 = 47;
2343pub const KEY_PAGE_UP: u32 = 48;
2344pub const KEY_PAGE_DOWN: u32 = 49;
2345
2346pub fn ui_button(id: u32, x: f32, y: f32, w: f32, h: f32, label: &str) -> bool {
2354 unsafe { _api_ui_button(id, x, y, w, h, label.as_ptr() as u32, label.len() as u32) != 0 }
2355}
2356
2357pub fn ui_checkbox(id: u32, x: f32, y: f32, label: &str, initial: bool) -> bool {
2361 unsafe {
2362 _api_ui_checkbox(
2363 id,
2364 x,
2365 y,
2366 label.as_ptr() as u32,
2367 label.len() as u32,
2368 if initial { 1 } else { 0 },
2369 ) != 0
2370 }
2371}
2372
2373pub fn ui_slider(id: u32, x: f32, y: f32, w: f32, min: f32, max: f32, initial: f32) -> f32 {
2377 unsafe { _api_ui_slider(id, x, y, w, min, max, initial) }
2378}
2379
2380pub fn ui_text_input(id: u32, x: f32, y: f32, w: f32, initial: &str) -> String {
2384 let mut buf = [0u8; 4096];
2385 let len = unsafe {
2386 _api_ui_text_input(
2387 id,
2388 x,
2389 y,
2390 w,
2391 initial.as_ptr() as u32,
2392 initial.len() as u32,
2393 buf.as_mut_ptr() as u32,
2394 buf.len() as u32,
2395 )
2396 };
2397 String::from_utf8_lossy(&buf[..len as usize]).to_string()
2398}