#![allow(clippy::too_many_arguments)]
#![allow(clippy::doc_overindented_list_items)]
pub mod draw;
pub mod proto;
#[link(wasm_import_module = "oxide")]
extern "C" {
#[link_name = "api_log"]
fn _api_log(ptr: u32, len: u32);
#[link_name = "api_warn"]
fn _api_warn(ptr: u32, len: u32);
#[link_name = "api_error"]
fn _api_error(ptr: u32, len: u32);
#[link_name = "api_get_location"]
fn _api_get_location(out_ptr: u32, out_cap: u32) -> u32;
#[link_name = "api_upload_file"]
fn _api_upload_file(name_ptr: u32, name_cap: u32, data_ptr: u32, data_cap: u32) -> u64;
#[link_name = "api_canvas_clear"]
fn _api_canvas_clear(r: u32, g: u32, b: u32, a: u32);
#[link_name = "api_canvas_rect"]
fn _api_canvas_rect(x: f32, y: f32, w: f32, h: f32, r: u32, g: u32, b: u32, a: u32);
#[link_name = "api_canvas_circle"]
fn _api_canvas_circle(cx: f32, cy: f32, radius: f32, r: u32, g: u32, b: u32, a: u32);
#[link_name = "api_canvas_text"]
fn _api_canvas_text(
x: f32,
y: f32,
size: f32,
r: u32,
g: u32,
b: u32,
a: u32,
ptr: u32,
len: u32,
);
#[link_name = "api_canvas_line"]
fn _api_canvas_line(
x1: f32,
y1: f32,
x2: f32,
y2: f32,
r: u32,
g: u32,
b: u32,
a: u32,
thickness: f32,
);
#[link_name = "api_canvas_dimensions"]
fn _api_canvas_dimensions() -> u64;
#[link_name = "api_canvas_image"]
fn _api_canvas_image(x: f32, y: f32, w: f32, h: f32, data_ptr: u32, data_len: u32);
#[link_name = "api_canvas_rounded_rect"]
fn _api_canvas_rounded_rect(
x: f32,
y: f32,
w: f32,
h: f32,
radius: f32,
r: u32,
g: u32,
b: u32,
a: u32,
);
#[link_name = "api_canvas_arc"]
fn _api_canvas_arc(
cx: f32,
cy: f32,
radius: f32,
start_angle: f32,
end_angle: f32,
r: u32,
g: u32,
b: u32,
a: u32,
thickness: f32,
);
#[link_name = "api_canvas_bezier"]
fn _api_canvas_bezier(
x1: f32,
y1: f32,
cp1x: f32,
cp1y: f32,
cp2x: f32,
cp2y: f32,
x2: f32,
y2: f32,
r: u32,
g: u32,
b: u32,
a: u32,
thickness: f32,
);
#[link_name = "api_canvas_gradient"]
fn _api_canvas_gradient(
x: f32,
y: f32,
w: f32,
h: f32,
kind: u32,
ax: f32,
ay: f32,
bx: f32,
by: f32,
stops_ptr: u32,
stops_len: u32,
);
#[link_name = "api_canvas_save"]
fn _api_canvas_save();
#[link_name = "api_canvas_restore"]
fn _api_canvas_restore();
#[link_name = "api_canvas_transform"]
fn _api_canvas_transform(a: f32, b: f32, c: f32, d: f32, tx: f32, ty: f32);
#[link_name = "api_canvas_clip"]
fn _api_canvas_clip(x: f32, y: f32, w: f32, h: f32);
#[link_name = "api_canvas_opacity"]
fn _api_canvas_opacity(alpha: f32);
#[link_name = "api_storage_set"]
fn _api_storage_set(key_ptr: u32, key_len: u32, val_ptr: u32, val_len: u32);
#[link_name = "api_storage_get"]
fn _api_storage_get(key_ptr: u32, key_len: u32, out_ptr: u32, out_cap: u32) -> u32;
#[link_name = "api_storage_remove"]
fn _api_storage_remove(key_ptr: u32, key_len: u32);
#[link_name = "api_clipboard_write"]
fn _api_clipboard_write(ptr: u32, len: u32);
#[link_name = "api_clipboard_read"]
fn _api_clipboard_read(out_ptr: u32, out_cap: u32) -> u32;
#[link_name = "api_time_now_ms"]
fn _api_time_now_ms() -> u64;
#[link_name = "api_set_timeout"]
fn _api_set_timeout(callback_id: u32, delay_ms: u32) -> u32;
#[link_name = "api_set_interval"]
fn _api_set_interval(callback_id: u32, interval_ms: u32) -> u32;
#[link_name = "api_clear_timer"]
fn _api_clear_timer(timer_id: u32);
#[link_name = "api_request_animation_frame"]
fn _api_request_animation_frame(callback_id: u32) -> u32;
#[link_name = "api_cancel_animation_frame"]
fn _api_cancel_animation_frame(request_id: u32);
#[link_name = "api_random"]
fn _api_random() -> u64;
#[link_name = "api_notify"]
fn _api_notify(title_ptr: u32, title_len: u32, body_ptr: u32, body_len: u32);
#[link_name = "api_fetch"]
fn _api_fetch(
method_ptr: u32,
method_len: u32,
url_ptr: u32,
url_len: u32,
ct_ptr: u32,
ct_len: u32,
body_ptr: u32,
body_len: u32,
out_ptr: u32,
out_cap: u32,
) -> i64;
#[link_name = "api_fetch_begin"]
fn _api_fetch_begin(
method_ptr: u32,
method_len: u32,
url_ptr: u32,
url_len: u32,
ct_ptr: u32,
ct_len: u32,
body_ptr: u32,
body_len: u32,
) -> u32;
#[link_name = "api_fetch_state"]
fn _api_fetch_state(id: u32) -> u32;
#[link_name = "api_fetch_status"]
fn _api_fetch_status(id: u32) -> u32;
#[link_name = "api_fetch_recv"]
fn _api_fetch_recv(id: u32, out_ptr: u32, out_cap: u32) -> i64;
#[link_name = "api_fetch_error"]
fn _api_fetch_error(id: u32, out_ptr: u32, out_cap: u32) -> i32;
#[link_name = "api_fetch_abort"]
fn _api_fetch_abort(id: u32) -> i32;
#[link_name = "api_fetch_remove"]
fn _api_fetch_remove(id: u32);
#[link_name = "api_load_module"]
fn _api_load_module(url_ptr: u32, url_len: u32) -> i32;
#[link_name = "api_hash_sha256"]
fn _api_hash_sha256(data_ptr: u32, data_len: u32, out_ptr: u32) -> u32;
#[link_name = "api_base64_encode"]
fn _api_base64_encode(data_ptr: u32, data_len: u32, out_ptr: u32, out_cap: u32) -> u32;
#[link_name = "api_base64_decode"]
fn _api_base64_decode(data_ptr: u32, data_len: u32, out_ptr: u32, out_cap: u32) -> u32;
#[link_name = "api_kv_store_set"]
fn _api_kv_store_set(key_ptr: u32, key_len: u32, val_ptr: u32, val_len: u32) -> i32;
#[link_name = "api_kv_store_get"]
fn _api_kv_store_get(key_ptr: u32, key_len: u32, out_ptr: u32, out_cap: u32) -> i32;
#[link_name = "api_kv_store_delete"]
fn _api_kv_store_delete(key_ptr: u32, key_len: u32) -> i32;
#[link_name = "api_navigate"]
fn _api_navigate(url_ptr: u32, url_len: u32) -> i32;
#[link_name = "api_push_state"]
fn _api_push_state(
state_ptr: u32,
state_len: u32,
title_ptr: u32,
title_len: u32,
url_ptr: u32,
url_len: u32,
);
#[link_name = "api_replace_state"]
fn _api_replace_state(
state_ptr: u32,
state_len: u32,
title_ptr: u32,
title_len: u32,
url_ptr: u32,
url_len: u32,
);
#[link_name = "api_get_url"]
fn _api_get_url(out_ptr: u32, out_cap: u32) -> u32;
#[link_name = "api_get_state"]
fn _api_get_state(out_ptr: u32, out_cap: u32) -> i32;
#[link_name = "api_history_length"]
fn _api_history_length() -> u32;
#[link_name = "api_history_back"]
fn _api_history_back() -> i32;
#[link_name = "api_history_forward"]
fn _api_history_forward() -> i32;
#[link_name = "api_register_hyperlink"]
fn _api_register_hyperlink(x: f32, y: f32, w: f32, h: f32, url_ptr: u32, url_len: u32) -> i32;
#[link_name = "api_clear_hyperlinks"]
fn _api_clear_hyperlinks();
#[link_name = "api_mouse_position"]
fn _api_mouse_position() -> u64;
#[link_name = "api_mouse_button_down"]
fn _api_mouse_button_down(button: u32) -> u32;
#[link_name = "api_mouse_button_clicked"]
fn _api_mouse_button_clicked(button: u32) -> u32;
#[link_name = "api_key_down"]
fn _api_key_down(key: u32) -> u32;
#[link_name = "api_key_pressed"]
fn _api_key_pressed(key: u32) -> u32;
#[link_name = "api_scroll_delta"]
fn _api_scroll_delta() -> u64;
#[link_name = "api_modifiers"]
fn _api_modifiers() -> u32;
#[link_name = "api_ui_button"]
fn _api_ui_button(
id: u32,
x: f32,
y: f32,
w: f32,
h: f32,
label_ptr: u32,
label_len: u32,
) -> u32;
#[link_name = "api_ui_checkbox"]
fn _api_ui_checkbox(
id: u32,
x: f32,
y: f32,
label_ptr: u32,
label_len: u32,
initial: u32,
) -> u32;
#[link_name = "api_ui_slider"]
fn _api_ui_slider(id: u32, x: f32, y: f32, w: f32, min: f32, max: f32, initial: f32) -> f32;
#[link_name = "api_ui_text_input"]
fn _api_ui_text_input(
id: u32,
x: f32,
y: f32,
w: f32,
init_ptr: u32,
init_len: u32,
out_ptr: u32,
out_cap: u32,
) -> u32;
#[link_name = "api_audio_play"]
fn _api_audio_play(data_ptr: u32, data_len: u32) -> i32;
#[link_name = "api_audio_play_url"]
fn _api_audio_play_url(url_ptr: u32, url_len: u32) -> i32;
#[link_name = "api_audio_detect_format"]
fn _api_audio_detect_format(data_ptr: u32, data_len: u32) -> u32;
#[link_name = "api_audio_play_with_format"]
fn _api_audio_play_with_format(data_ptr: u32, data_len: u32, format_hint: u32) -> i32;
#[link_name = "api_audio_last_url_content_type"]
fn _api_audio_last_url_content_type(out_ptr: u32, out_cap: u32) -> u32;
#[link_name = "api_audio_pause"]
fn _api_audio_pause();
#[link_name = "api_audio_resume"]
fn _api_audio_resume();
#[link_name = "api_audio_stop"]
fn _api_audio_stop();
#[link_name = "api_audio_set_volume"]
fn _api_audio_set_volume(level: f32);
#[link_name = "api_audio_get_volume"]
fn _api_audio_get_volume() -> f32;
#[link_name = "api_audio_is_playing"]
fn _api_audio_is_playing() -> u32;
#[link_name = "api_audio_position"]
fn _api_audio_position() -> u64;
#[link_name = "api_audio_seek"]
fn _api_audio_seek(position_ms: u64) -> i32;
#[link_name = "api_audio_duration"]
fn _api_audio_duration() -> u64;
#[link_name = "api_audio_set_loop"]
fn _api_audio_set_loop(enabled: u32);
#[link_name = "api_audio_channel_play"]
fn _api_audio_channel_play(channel: u32, data_ptr: u32, data_len: u32) -> i32;
#[link_name = "api_audio_channel_play_with_format"]
fn _api_audio_channel_play_with_format(
channel: u32,
data_ptr: u32,
data_len: u32,
format_hint: u32,
) -> i32;
#[link_name = "api_audio_channel_stop"]
fn _api_audio_channel_stop(channel: u32);
#[link_name = "api_audio_channel_set_volume"]
fn _api_audio_channel_set_volume(channel: u32, level: f32);
#[link_name = "api_video_detect_format"]
fn _api_video_detect_format(data_ptr: u32, data_len: u32) -> u32;
#[link_name = "api_video_load"]
fn _api_video_load(data_ptr: u32, data_len: u32, format_hint: u32) -> i32;
#[link_name = "api_video_load_url"]
fn _api_video_load_url(url_ptr: u32, url_len: u32) -> i32;
#[link_name = "api_video_last_url_content_type"]
fn _api_video_last_url_content_type(out_ptr: u32, out_cap: u32) -> u32;
#[link_name = "api_video_hls_variant_count"]
fn _api_video_hls_variant_count() -> u32;
#[link_name = "api_video_hls_variant_url"]
fn _api_video_hls_variant_url(index: u32, out_ptr: u32, out_cap: u32) -> u32;
#[link_name = "api_video_hls_open_variant"]
fn _api_video_hls_open_variant(index: u32) -> i32;
#[link_name = "api_video_play"]
fn _api_video_play();
#[link_name = "api_video_pause"]
fn _api_video_pause();
#[link_name = "api_video_stop"]
fn _api_video_stop();
#[link_name = "api_video_seek"]
fn _api_video_seek(position_ms: u64) -> i32;
#[link_name = "api_video_position"]
fn _api_video_position() -> u64;
#[link_name = "api_video_duration"]
fn _api_video_duration() -> u64;
#[link_name = "api_video_render"]
fn _api_video_render(x: f32, y: f32, w: f32, h: f32) -> i32;
#[link_name = "api_video_set_volume"]
fn _api_video_set_volume(level: f32);
#[link_name = "api_video_get_volume"]
fn _api_video_get_volume() -> f32;
#[link_name = "api_video_set_loop"]
fn _api_video_set_loop(enabled: u32);
#[link_name = "api_video_set_pip"]
fn _api_video_set_pip(enabled: u32);
#[link_name = "api_subtitle_load_srt"]
fn _api_subtitle_load_srt(ptr: u32, len: u32) -> i32;
#[link_name = "api_subtitle_load_vtt"]
fn _api_subtitle_load_vtt(ptr: u32, len: u32) -> i32;
#[link_name = "api_subtitle_clear"]
fn _api_subtitle_clear();
#[link_name = "api_camera_open"]
fn _api_camera_open() -> i32;
#[link_name = "api_camera_close"]
fn _api_camera_close();
#[link_name = "api_camera_capture_frame"]
fn _api_camera_capture_frame(out_ptr: u32, out_cap: u32) -> u32;
#[link_name = "api_camera_frame_dimensions"]
fn _api_camera_frame_dimensions() -> u64;
#[link_name = "api_microphone_open"]
fn _api_microphone_open() -> i32;
#[link_name = "api_microphone_close"]
fn _api_microphone_close();
#[link_name = "api_microphone_sample_rate"]
fn _api_microphone_sample_rate() -> u32;
#[link_name = "api_microphone_read_samples"]
fn _api_microphone_read_samples(out_ptr: u32, max_samples: u32) -> u32;
#[link_name = "api_screen_capture"]
fn _api_screen_capture(out_ptr: u32, out_cap: u32) -> i32;
#[link_name = "api_screen_capture_dimensions"]
fn _api_screen_capture_dimensions() -> u64;
#[link_name = "api_media_pipeline_stats"]
fn _api_media_pipeline_stats() -> u64;
#[link_name = "api_gpu_create_buffer"]
fn _api_gpu_create_buffer(size_lo: u32, size_hi: u32, usage: u32) -> u32;
#[link_name = "api_gpu_create_texture"]
fn _api_gpu_create_texture(width: u32, height: u32) -> u32;
#[link_name = "api_gpu_create_shader"]
fn _api_gpu_create_shader(src_ptr: u32, src_len: u32) -> u32;
#[link_name = "api_gpu_create_render_pipeline"]
fn _api_gpu_create_render_pipeline(
shader: u32,
vs_ptr: u32,
vs_len: u32,
fs_ptr: u32,
fs_len: u32,
) -> u32;
#[link_name = "api_gpu_create_compute_pipeline"]
fn _api_gpu_create_compute_pipeline(shader: u32, ep_ptr: u32, ep_len: u32) -> u32;
#[link_name = "api_gpu_write_buffer"]
fn _api_gpu_write_buffer(
handle: u32,
offset_lo: u32,
offset_hi: u32,
data_ptr: u32,
data_len: u32,
) -> u32;
#[link_name = "api_gpu_draw"]
fn _api_gpu_draw(pipeline: u32, target: u32, vertex_count: u32, instance_count: u32) -> u32;
#[link_name = "api_gpu_dispatch_compute"]
fn _api_gpu_dispatch_compute(pipeline: u32, x: u32, y: u32, z: u32) -> u32;
#[link_name = "api_gpu_destroy_buffer"]
fn _api_gpu_destroy_buffer(handle: u32) -> u32;
#[link_name = "api_gpu_destroy_texture"]
fn _api_gpu_destroy_texture(handle: u32) -> u32;
#[link_name = "api_rtc_create_peer"]
fn _api_rtc_create_peer(stun_ptr: u32, stun_len: u32) -> u32;
#[link_name = "api_rtc_close_peer"]
fn _api_rtc_close_peer(peer_id: u32) -> u32;
#[link_name = "api_rtc_create_offer"]
fn _api_rtc_create_offer(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
#[link_name = "api_rtc_create_answer"]
fn _api_rtc_create_answer(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
#[link_name = "api_rtc_set_local_description"]
fn _api_rtc_set_local_description(
peer_id: u32,
sdp_ptr: u32,
sdp_len: u32,
is_offer: u32,
) -> i32;
#[link_name = "api_rtc_set_remote_description"]
fn _api_rtc_set_remote_description(
peer_id: u32,
sdp_ptr: u32,
sdp_len: u32,
is_offer: u32,
) -> i32;
#[link_name = "api_rtc_add_ice_candidate"]
fn _api_rtc_add_ice_candidate(peer_id: u32, cand_ptr: u32, cand_len: u32) -> i32;
#[link_name = "api_rtc_connection_state"]
fn _api_rtc_connection_state(peer_id: u32) -> u32;
#[link_name = "api_rtc_poll_ice_candidate"]
fn _api_rtc_poll_ice_candidate(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
#[link_name = "api_rtc_create_data_channel"]
fn _api_rtc_create_data_channel(
peer_id: u32,
label_ptr: u32,
label_len: u32,
ordered: u32,
) -> u32;
#[link_name = "api_rtc_send"]
fn _api_rtc_send(
peer_id: u32,
channel_id: u32,
data_ptr: u32,
data_len: u32,
is_binary: u32,
) -> i32;
#[link_name = "api_rtc_recv"]
fn _api_rtc_recv(peer_id: u32, channel_id: u32, out_ptr: u32, out_cap: u32) -> i64;
#[link_name = "api_rtc_poll_data_channel"]
fn _api_rtc_poll_data_channel(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
#[link_name = "api_rtc_add_track"]
fn _api_rtc_add_track(peer_id: u32, kind: u32) -> u32;
#[link_name = "api_rtc_poll_track"]
fn _api_rtc_poll_track(peer_id: u32, out_ptr: u32, out_cap: u32) -> i32;
#[link_name = "api_rtc_signal_connect"]
fn _api_rtc_signal_connect(url_ptr: u32, url_len: u32) -> u32;
#[link_name = "api_rtc_signal_join_room"]
fn _api_rtc_signal_join_room(room_ptr: u32, room_len: u32) -> i32;
#[link_name = "api_rtc_signal_send"]
fn _api_rtc_signal_send(data_ptr: u32, data_len: u32) -> i32;
#[link_name = "api_rtc_signal_recv"]
fn _api_rtc_signal_recv(out_ptr: u32, out_cap: u32) -> i32;
#[link_name = "api_ws_connect"]
fn _api_ws_connect(url_ptr: u32, url_len: u32) -> u32;
#[link_name = "api_ws_send_text"]
fn _api_ws_send_text(id: u32, data_ptr: u32, data_len: u32) -> i32;
#[link_name = "api_ws_send_binary"]
fn _api_ws_send_binary(id: u32, data_ptr: u32, data_len: u32) -> i32;
#[link_name = "api_ws_recv"]
fn _api_ws_recv(id: u32, out_ptr: u32, out_cap: u32) -> i64;
#[link_name = "api_ws_ready_state"]
fn _api_ws_ready_state(id: u32) -> u32;
#[link_name = "api_ws_close"]
fn _api_ws_close(id: u32) -> i32;
#[link_name = "api_ws_remove"]
fn _api_ws_remove(id: u32);
#[link_name = "api_midi_input_count"]
fn _api_midi_input_count() -> u32;
#[link_name = "api_midi_output_count"]
fn _api_midi_output_count() -> u32;
#[link_name = "api_midi_input_name"]
fn _api_midi_input_name(index: u32, out_ptr: u32, out_cap: u32) -> u32;
#[link_name = "api_midi_output_name"]
fn _api_midi_output_name(index: u32, out_ptr: u32, out_cap: u32) -> u32;
#[link_name = "api_midi_open_input"]
fn _api_midi_open_input(index: u32) -> u32;
#[link_name = "api_midi_open_output"]
fn _api_midi_open_output(index: u32) -> u32;
#[link_name = "api_midi_send"]
fn _api_midi_send(handle: u32, data_ptr: u32, data_len: u32) -> i32;
#[link_name = "api_midi_recv"]
fn _api_midi_recv(handle: u32, out_ptr: u32, out_cap: u32) -> i32;
#[link_name = "api_midi_close"]
fn _api_midi_close(handle: u32);
#[link_name = "api_url_resolve"]
fn _api_url_resolve(
base_ptr: u32,
base_len: u32,
rel_ptr: u32,
rel_len: u32,
out_ptr: u32,
out_cap: u32,
) -> i32;
#[link_name = "api_url_encode"]
fn _api_url_encode(input_ptr: u32, input_len: u32, out_ptr: u32, out_cap: u32) -> u32;
#[link_name = "api_url_decode"]
fn _api_url_decode(input_ptr: u32, input_len: u32, out_ptr: u32, out_cap: u32) -> u32;
}
pub fn log(msg: &str) {
unsafe { _api_log(msg.as_ptr() as u32, msg.len() as u32) }
}
pub fn warn(msg: &str) {
unsafe { _api_warn(msg.as_ptr() as u32, msg.len() as u32) }
}
pub fn error(msg: &str) {
unsafe { _api_error(msg.as_ptr() as u32, msg.len() as u32) }
}
pub fn get_location() -> String {
let mut buf = [0u8; 128];
let len = unsafe { _api_get_location(buf.as_mut_ptr() as u32, buf.len() as u32) };
String::from_utf8_lossy(&buf[..len as usize]).to_string()
}
pub struct UploadedFile {
pub name: String,
pub data: Vec<u8>,
}
pub fn upload_file() -> Option<UploadedFile> {
let mut name_buf = [0u8; 256];
let mut data_buf = vec![0u8; 1024 * 1024];
let result = unsafe {
_api_upload_file(
name_buf.as_mut_ptr() as u32,
name_buf.len() as u32,
data_buf.as_mut_ptr() as u32,
data_buf.len() as u32,
)
};
if result == 0 {
return None;
}
let name_len = (result >> 32) as usize;
let data_len = (result & 0xFFFF_FFFF) as usize;
Some(UploadedFile {
name: String::from_utf8_lossy(&name_buf[..name_len]).to_string(),
data: data_buf[..data_len].to_vec(),
})
}
pub fn canvas_clear(r: u8, g: u8, b: u8, a: u8) {
unsafe { _api_canvas_clear(r as u32, g as u32, b as u32, a as u32) }
}
pub fn canvas_rect(x: f32, y: f32, w: f32, h: f32, r: u8, g: u8, b: u8, a: u8) {
unsafe { _api_canvas_rect(x, y, w, h, r as u32, g as u32, b as u32, a as u32) }
}
pub fn canvas_circle(cx: f32, cy: f32, radius: f32, r: u8, g: u8, b: u8, a: u8) {
unsafe { _api_canvas_circle(cx, cy, radius, r as u32, g as u32, b as u32, a as u32) }
}
pub fn canvas_text(x: f32, y: f32, size: f32, r: u8, g: u8, b: u8, a: u8, text: &str) {
unsafe {
_api_canvas_text(
x,
y,
size,
r as u32,
g as u32,
b as u32,
a as u32,
text.as_ptr() as u32,
text.len() as u32,
)
}
}
pub fn canvas_line(x1: f32, y1: f32, x2: f32, y2: f32, r: u8, g: u8, b: u8, a: u8, thickness: f32) {
unsafe {
_api_canvas_line(
x1, y1, x2, y2, r as u32, g as u32, b as u32, a as u32, thickness,
)
}
}
pub fn canvas_dimensions() -> (u32, u32) {
let packed = unsafe { _api_canvas_dimensions() };
((packed >> 32) as u32, (packed & 0xFFFF_FFFF) as u32)
}
pub fn canvas_image(x: f32, y: f32, w: f32, h: f32, data: &[u8]) {
unsafe { _api_canvas_image(x, y, w, h, data.as_ptr() as u32, data.len() as u32) }
}
pub fn canvas_rounded_rect(
x: f32,
y: f32,
w: f32,
h: f32,
radius: f32,
r: u8,
g: u8,
b: u8,
a: u8,
) {
unsafe { _api_canvas_rounded_rect(x, y, w, h, radius, r as u32, g as u32, b as u32, a as u32) }
}
pub fn canvas_arc(
cx: f32,
cy: f32,
radius: f32,
start_angle: f32,
end_angle: f32,
r: u8,
g: u8,
b: u8,
a: u8,
thickness: f32,
) {
unsafe {
_api_canvas_arc(
cx,
cy,
radius,
start_angle,
end_angle,
r as u32,
g as u32,
b as u32,
a as u32,
thickness,
)
}
}
pub fn canvas_bezier(
x1: f32,
y1: f32,
cp1x: f32,
cp1y: f32,
cp2x: f32,
cp2y: f32,
x2: f32,
y2: f32,
r: u8,
g: u8,
b: u8,
a: u8,
thickness: f32,
) {
unsafe {
_api_canvas_bezier(
x1, y1, cp1x, cp1y, cp2x, cp2y, x2, y2, r as u32, g as u32, b as u32, a as u32,
thickness,
)
}
}
pub const GRADIENT_LINEAR: u32 = 0;
pub const GRADIENT_RADIAL: u32 = 1;
pub fn canvas_gradient(
x: f32,
y: f32,
w: f32,
h: f32,
kind: u32,
ax: f32,
ay: f32,
bx: f32,
by: f32,
stops: &[(f32, u8, u8, u8, u8)],
) {
let mut buf = Vec::with_capacity(stops.len() * 8);
for &(offset, r, g, b, a) in stops {
buf.extend_from_slice(&offset.to_le_bytes());
buf.push(r);
buf.push(g);
buf.push(b);
buf.push(a);
}
unsafe {
_api_canvas_gradient(
x,
y,
w,
h,
kind,
ax,
ay,
bx,
by,
buf.as_ptr() as u32,
buf.len() as u32,
)
}
}
pub fn canvas_save() {
unsafe { _api_canvas_save() }
}
pub fn canvas_restore() {
unsafe { _api_canvas_restore() }
}
pub fn canvas_transform(a: f32, b: f32, c: f32, d: f32, tx: f32, ty: f32) {
unsafe { _api_canvas_transform(a, b, c, d, tx, ty) }
}
pub fn canvas_clip(x: f32, y: f32, w: f32, h: f32) {
unsafe { _api_canvas_clip(x, y, w, h) }
}
pub fn canvas_opacity(alpha: f32) {
unsafe { _api_canvas_opacity(alpha) }
}
pub mod gpu_usage {
pub const VERTEX: u32 = 0x0020;
pub const INDEX: u32 = 0x0010;
pub const UNIFORM: u32 = 0x0040;
pub const STORAGE: u32 = 0x0080;
}
pub fn gpu_create_buffer(size: u64, usage: u32) -> u32 {
unsafe { _api_gpu_create_buffer(size as u32, (size >> 32) as u32, usage) }
}
pub fn gpu_create_texture(width: u32, height: u32) -> u32 {
unsafe { _api_gpu_create_texture(width, height) }
}
pub fn gpu_create_shader(source: &str) -> u32 {
unsafe { _api_gpu_create_shader(source.as_ptr() as u32, source.len() as u32) }
}
pub fn gpu_create_pipeline(shader: u32, vertex_entry: &str, fragment_entry: &str) -> u32 {
unsafe {
_api_gpu_create_render_pipeline(
shader,
vertex_entry.as_ptr() as u32,
vertex_entry.len() as u32,
fragment_entry.as_ptr() as u32,
fragment_entry.len() as u32,
)
}
}
pub fn gpu_create_compute_pipeline(shader: u32, entry_point: &str) -> u32 {
unsafe {
_api_gpu_create_compute_pipeline(
shader,
entry_point.as_ptr() as u32,
entry_point.len() as u32,
)
}
}
pub fn gpu_write_buffer(handle: u32, offset: u64, data: &[u8]) -> bool {
unsafe {
_api_gpu_write_buffer(
handle,
offset as u32,
(offset >> 32) as u32,
data.as_ptr() as u32,
data.len() as u32,
) != 0
}
}
pub fn gpu_draw(
pipeline: u32,
target_texture: u32,
vertex_count: u32,
instance_count: u32,
) -> bool {
unsafe { _api_gpu_draw(pipeline, target_texture, vertex_count, instance_count) != 0 }
}
pub fn gpu_dispatch_compute(pipeline: u32, x: u32, y: u32, z: u32) -> bool {
unsafe { _api_gpu_dispatch_compute(pipeline, x, y, z) != 0 }
}
pub fn gpu_destroy_buffer(handle: u32) -> bool {
unsafe { _api_gpu_destroy_buffer(handle) != 0 }
}
pub fn gpu_destroy_texture(handle: u32) -> bool {
unsafe { _api_gpu_destroy_texture(handle) != 0 }
}
pub fn storage_set(key: &str, value: &str) {
unsafe {
_api_storage_set(
key.as_ptr() as u32,
key.len() as u32,
value.as_ptr() as u32,
value.len() as u32,
)
}
}
pub fn storage_get(key: &str) -> String {
let mut buf = [0u8; 4096];
let len = unsafe {
_api_storage_get(
key.as_ptr() as u32,
key.len() as u32,
buf.as_mut_ptr() as u32,
buf.len() as u32,
)
};
String::from_utf8_lossy(&buf[..len as usize]).to_string()
}
pub fn storage_remove(key: &str) {
unsafe { _api_storage_remove(key.as_ptr() as u32, key.len() as u32) }
}
pub fn clipboard_write(text: &str) {
unsafe { _api_clipboard_write(text.as_ptr() as u32, text.len() as u32) }
}
pub fn clipboard_read() -> String {
let mut buf = [0u8; 4096];
let len = unsafe { _api_clipboard_read(buf.as_mut_ptr() as u32, buf.len() as u32) };
String::from_utf8_lossy(&buf[..len as usize]).to_string()
}
pub fn time_now_ms() -> u64 {
unsafe { _api_time_now_ms() }
}
pub fn set_timeout(callback_id: u32, delay_ms: u32) -> u32 {
unsafe { _api_set_timeout(callback_id, delay_ms) }
}
pub fn set_interval(callback_id: u32, interval_ms: u32) -> u32 {
unsafe { _api_set_interval(callback_id, interval_ms) }
}
pub fn clear_timer(timer_id: u32) {
unsafe { _api_clear_timer(timer_id) }
}
pub fn request_animation_frame(callback_id: u32) -> u32 {
unsafe { _api_request_animation_frame(callback_id) }
}
pub fn cancel_animation_frame(request_id: u32) {
unsafe { _api_cancel_animation_frame(request_id) }
}
pub fn random_u64() -> u64 {
unsafe { _api_random() }
}
pub fn random_f64() -> f64 {
(random_u64() >> 11) as f64 / (1u64 << 53) as f64
}
pub fn notify(title: &str, body: &str) {
unsafe {
_api_notify(
title.as_ptr() as u32,
title.len() as u32,
body.as_ptr() as u32,
body.len() as u32,
)
}
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AudioFormat {
Unknown = 0,
Wav = 1,
Mp3 = 2,
Ogg = 3,
Flac = 4,
}
impl From<u32> for AudioFormat {
fn from(code: u32) -> Self {
match code {
1 => AudioFormat::Wav,
2 => AudioFormat::Mp3,
3 => AudioFormat::Ogg,
4 => AudioFormat::Flac,
_ => AudioFormat::Unknown,
}
}
}
impl From<AudioFormat> for u32 {
fn from(f: AudioFormat) -> u32 {
f as u32
}
}
pub fn audio_play(data: &[u8]) -> i32 {
unsafe { _api_audio_play(data.as_ptr() as u32, data.len() as u32) }
}
pub fn audio_detect_format(data: &[u8]) -> AudioFormat {
let code = unsafe { _api_audio_detect_format(data.as_ptr() as u32, data.len() as u32) };
AudioFormat::from(code)
}
pub fn audio_play_with_format(data: &[u8], format: AudioFormat) -> i32 {
unsafe {
_api_audio_play_with_format(data.as_ptr() as u32, data.len() as u32, u32::from(format))
}
}
pub fn audio_play_url(url: &str) -> i32 {
unsafe { _api_audio_play_url(url.as_ptr() as u32, url.len() as u32) }
}
pub fn audio_last_url_content_type() -> String {
let mut buf = [0u8; 512];
let len =
unsafe { _api_audio_last_url_content_type(buf.as_mut_ptr() as u32, buf.len() as u32) };
let n = (len as usize).min(buf.len());
String::from_utf8_lossy(&buf[..n]).to_string()
}
pub fn audio_pause() {
unsafe { _api_audio_pause() }
}
pub fn audio_resume() {
unsafe { _api_audio_resume() }
}
pub fn audio_stop() {
unsafe { _api_audio_stop() }
}
pub fn audio_set_volume(level: f32) {
unsafe { _api_audio_set_volume(level) }
}
pub fn audio_get_volume() -> f32 {
unsafe { _api_audio_get_volume() }
}
pub fn audio_is_playing() -> bool {
unsafe { _api_audio_is_playing() != 0 }
}
pub fn audio_position() -> u64 {
unsafe { _api_audio_position() }
}
pub fn audio_seek(position_ms: u64) -> i32 {
unsafe { _api_audio_seek(position_ms) }
}
pub fn audio_duration() -> u64 {
unsafe { _api_audio_duration() }
}
pub fn audio_set_loop(enabled: bool) {
unsafe { _api_audio_set_loop(if enabled { 1 } else { 0 }) }
}
pub fn audio_channel_play(channel: u32, data: &[u8]) -> i32 {
unsafe { _api_audio_channel_play(channel, data.as_ptr() as u32, data.len() as u32) }
}
pub fn audio_channel_play_with_format(channel: u32, data: &[u8], format: AudioFormat) -> i32 {
unsafe {
_api_audio_channel_play_with_format(
channel,
data.as_ptr() as u32,
data.len() as u32,
u32::from(format),
)
}
}
pub fn audio_channel_stop(channel: u32) {
unsafe { _api_audio_channel_stop(channel) }
}
pub fn audio_channel_set_volume(channel: u32, level: f32) {
unsafe { _api_audio_channel_set_volume(channel, level) }
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum VideoFormat {
Unknown = 0,
Mp4 = 1,
Webm = 2,
Av1 = 3,
}
impl From<u32> for VideoFormat {
fn from(code: u32) -> Self {
match code {
1 => VideoFormat::Mp4,
2 => VideoFormat::Webm,
3 => VideoFormat::Av1,
_ => VideoFormat::Unknown,
}
}
}
impl From<VideoFormat> for u32 {
fn from(f: VideoFormat) -> u32 {
f as u32
}
}
pub fn video_detect_format(data: &[u8]) -> VideoFormat {
let code = unsafe { _api_video_detect_format(data.as_ptr() as u32, data.len() as u32) };
VideoFormat::from(code)
}
pub fn video_load(data: &[u8]) -> i32 {
unsafe {
_api_video_load(
data.as_ptr() as u32,
data.len() as u32,
VideoFormat::Unknown as u32,
)
}
}
pub fn video_load_with_format(data: &[u8], format: VideoFormat) -> i32 {
unsafe { _api_video_load(data.as_ptr() as u32, data.len() as u32, u32::from(format)) }
}
pub fn video_load_url(url: &str) -> i32 {
unsafe { _api_video_load_url(url.as_ptr() as u32, url.len() as u32) }
}
pub fn video_last_url_content_type() -> String {
let mut buf = [0u8; 512];
let len =
unsafe { _api_video_last_url_content_type(buf.as_mut_ptr() as u32, buf.len() as u32) };
let n = (len as usize).min(buf.len());
String::from_utf8_lossy(&buf[..n]).to_string()
}
pub fn video_hls_variant_count() -> u32 {
unsafe { _api_video_hls_variant_count() }
}
pub fn video_hls_variant_url(index: u32) -> String {
let mut buf = [0u8; 2048];
let len =
unsafe { _api_video_hls_variant_url(index, buf.as_mut_ptr() as u32, buf.len() as u32) };
let n = (len as usize).min(buf.len());
String::from_utf8_lossy(&buf[..n]).to_string()
}
pub fn video_hls_open_variant(index: u32) -> i32 {
unsafe { _api_video_hls_open_variant(index) }
}
pub fn video_play() {
unsafe { _api_video_play() }
}
pub fn video_pause() {
unsafe { _api_video_pause() }
}
pub fn video_stop() {
unsafe { _api_video_stop() }
}
pub fn video_seek(position_ms: u64) -> i32 {
unsafe { _api_video_seek(position_ms) }
}
pub fn video_position() -> u64 {
unsafe { _api_video_position() }
}
pub fn video_duration() -> u64 {
unsafe { _api_video_duration() }
}
pub fn video_render(x: f32, y: f32, w: f32, h: f32) -> i32 {
unsafe { _api_video_render(x, y, w, h) }
}
pub fn video_set_volume(level: f32) {
unsafe { _api_video_set_volume(level) }
}
pub fn video_get_volume() -> f32 {
unsafe { _api_video_get_volume() }
}
pub fn video_set_loop(enabled: bool) {
unsafe { _api_video_set_loop(if enabled { 1 } else { 0 }) }
}
pub fn video_set_pip(enabled: bool) {
unsafe { _api_video_set_pip(if enabled { 1 } else { 0 }) }
}
pub fn subtitle_load_srt(text: &str) -> i32 {
unsafe { _api_subtitle_load_srt(text.as_ptr() as u32, text.len() as u32) }
}
pub fn subtitle_load_vtt(text: &str) -> i32 {
unsafe { _api_subtitle_load_vtt(text.as_ptr() as u32, text.len() as u32) }
}
pub fn subtitle_clear() {
unsafe { _api_subtitle_clear() }
}
pub fn camera_open() -> i32 {
unsafe { _api_camera_open() }
}
pub fn camera_close() {
unsafe { _api_camera_close() }
}
pub fn camera_capture_frame(out: &mut [u8]) -> u32 {
unsafe { _api_camera_capture_frame(out.as_mut_ptr() as u32, out.len() as u32) }
}
pub fn camera_frame_dimensions() -> (u32, u32) {
let packed = unsafe { _api_camera_frame_dimensions() };
let w = (packed >> 32) as u32;
let h = packed as u32;
(w, h)
}
pub fn microphone_open() -> i32 {
unsafe { _api_microphone_open() }
}
pub fn microphone_close() {
unsafe { _api_microphone_close() }
}
pub fn microphone_sample_rate() -> u32 {
unsafe { _api_microphone_sample_rate() }
}
pub fn microphone_read_samples(out: &mut [f32]) -> u32 {
unsafe { _api_microphone_read_samples(out.as_mut_ptr() as u32, out.len() as u32) }
}
pub fn screen_capture(out: &mut [u8]) -> Result<usize, i32> {
let n = unsafe { _api_screen_capture(out.as_mut_ptr() as u32, out.len() as u32) };
if n >= 0 {
Ok(n as usize)
} else {
Err(n)
}
}
pub fn screen_capture_dimensions() -> (u32, u32) {
let packed = unsafe { _api_screen_capture_dimensions() };
let w = (packed >> 32) as u32;
let h = packed as u32;
(w, h)
}
pub fn media_pipeline_stats() -> (u64, u32) {
let packed = unsafe { _api_media_pipeline_stats() };
let camera_frames = packed >> 32;
let mic_ring = packed as u32;
(camera_frames, mic_ring)
}
pub const RTC_STATE_NEW: u32 = 0;
pub const RTC_STATE_CONNECTING: u32 = 1;
pub const RTC_STATE_CONNECTED: u32 = 2;
pub const RTC_STATE_DISCONNECTED: u32 = 3;
pub const RTC_STATE_FAILED: u32 = 4;
pub const RTC_STATE_CLOSED: u32 = 5;
pub const RTC_TRACK_AUDIO: u32 = 0;
pub const RTC_TRACK_VIDEO: u32 = 1;
pub struct RtcMessage {
pub channel_id: u32,
pub is_binary: bool,
pub data: Vec<u8>,
}
impl RtcMessage {
pub fn text(&self) -> String {
String::from_utf8_lossy(&self.data).to_string()
}
}
pub struct RtcDataChannelInfo {
pub channel_id: u32,
pub label: String,
}
pub fn rtc_create_peer(stun_servers: &str) -> u32 {
unsafe { _api_rtc_create_peer(stun_servers.as_ptr() as u32, stun_servers.len() as u32) }
}
pub fn rtc_close_peer(peer_id: u32) -> bool {
unsafe { _api_rtc_close_peer(peer_id) != 0 }
}
pub fn rtc_create_offer(peer_id: u32) -> Result<String, i32> {
let mut buf = vec![0u8; 16 * 1024];
let n = unsafe { _api_rtc_create_offer(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
if n < 0 {
Err(n)
} else {
Ok(String::from_utf8_lossy(&buf[..n as usize]).to_string())
}
}
pub fn rtc_create_answer(peer_id: u32) -> Result<String, i32> {
let mut buf = vec![0u8; 16 * 1024];
let n = unsafe { _api_rtc_create_answer(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
if n < 0 {
Err(n)
} else {
Ok(String::from_utf8_lossy(&buf[..n as usize]).to_string())
}
}
pub fn rtc_set_local_description(peer_id: u32, sdp: &str, is_offer: bool) -> i32 {
unsafe {
_api_rtc_set_local_description(
peer_id,
sdp.as_ptr() as u32,
sdp.len() as u32,
if is_offer { 1 } else { 0 },
)
}
}
pub fn rtc_set_remote_description(peer_id: u32, sdp: &str, is_offer: bool) -> i32 {
unsafe {
_api_rtc_set_remote_description(
peer_id,
sdp.as_ptr() as u32,
sdp.len() as u32,
if is_offer { 1 } else { 0 },
)
}
}
pub fn rtc_add_ice_candidate(peer_id: u32, candidate_json: &str) -> i32 {
unsafe {
_api_rtc_add_ice_candidate(
peer_id,
candidate_json.as_ptr() as u32,
candidate_json.len() as u32,
)
}
}
pub fn rtc_connection_state(peer_id: u32) -> u32 {
unsafe { _api_rtc_connection_state(peer_id) }
}
pub fn rtc_poll_ice_candidate(peer_id: u32) -> Option<String> {
let mut buf = vec![0u8; 4096];
let n =
unsafe { _api_rtc_poll_ice_candidate(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
if n <= 0 {
None
} else {
Some(String::from_utf8_lossy(&buf[..n as usize]).to_string())
}
}
pub fn rtc_create_data_channel(peer_id: u32, label: &str, ordered: bool) -> u32 {
unsafe {
_api_rtc_create_data_channel(
peer_id,
label.as_ptr() as u32,
label.len() as u32,
if ordered { 1 } else { 0 },
)
}
}
pub fn rtc_send_text(peer_id: u32, channel_id: u32, text: &str) -> i32 {
unsafe {
_api_rtc_send(
peer_id,
channel_id,
text.as_ptr() as u32,
text.len() as u32,
0,
)
}
}
pub fn rtc_send_binary(peer_id: u32, channel_id: u32, data: &[u8]) -> i32 {
unsafe {
_api_rtc_send(
peer_id,
channel_id,
data.as_ptr() as u32,
data.len() as u32,
1,
)
}
}
pub fn rtc_send(peer_id: u32, channel_id: u32, data: &[u8], is_binary: bool) -> i32 {
unsafe {
_api_rtc_send(
peer_id,
channel_id,
data.as_ptr() as u32,
data.len() as u32,
if is_binary { 1 } else { 0 },
)
}
}
pub fn rtc_recv(peer_id: u32, channel_id: u32) -> Option<RtcMessage> {
let mut buf = vec![0u8; 64 * 1024];
let packed = unsafe {
_api_rtc_recv(
peer_id,
channel_id,
buf.as_mut_ptr() as u32,
buf.len() as u32,
)
};
if packed <= 0 {
return None;
}
let packed = packed as u64;
let data_len = (packed & 0xFFFF_FFFF) as usize;
let is_binary = (packed >> 32) & 1 != 0;
let ch = (packed >> 48) as u32;
Some(RtcMessage {
channel_id: ch,
is_binary,
data: buf[..data_len].to_vec(),
})
}
pub fn rtc_poll_data_channel(peer_id: u32) -> Option<RtcDataChannelInfo> {
let mut buf = vec![0u8; 1024];
let n =
unsafe { _api_rtc_poll_data_channel(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
if n <= 0 {
return None;
}
let info = String::from_utf8_lossy(&buf[..n as usize]).to_string();
let (id_str, label) = info.split_once(':').unwrap_or(("0", ""));
Some(RtcDataChannelInfo {
channel_id: id_str.parse().unwrap_or(0),
label: label.to_string(),
})
}
pub fn rtc_add_track(peer_id: u32, kind: u32) -> u32 {
unsafe { _api_rtc_add_track(peer_id, kind) }
}
pub struct RtcTrackInfo {
pub kind: u32,
pub id: String,
pub stream_id: String,
}
pub fn rtc_poll_track(peer_id: u32) -> Option<RtcTrackInfo> {
let mut buf = vec![0u8; 1024];
let n = unsafe { _api_rtc_poll_track(peer_id, buf.as_mut_ptr() as u32, buf.len() as u32) };
if n <= 0 {
return None;
}
let info = String::from_utf8_lossy(&buf[..n as usize]).to_string();
let mut parts = info.splitn(3, ':');
let kind = parts.next().unwrap_or("2").parse().unwrap_or(2);
let id = parts.next().unwrap_or("").to_string();
let stream_id = parts.next().unwrap_or("").to_string();
Some(RtcTrackInfo {
kind,
id,
stream_id,
})
}
pub fn rtc_signal_connect(url: &str) -> bool {
unsafe { _api_rtc_signal_connect(url.as_ptr() as u32, url.len() as u32) != 0 }
}
pub fn rtc_signal_join_room(room: &str) -> i32 {
unsafe { _api_rtc_signal_join_room(room.as_ptr() as u32, room.len() as u32) }
}
pub fn rtc_signal_send(data: &[u8]) -> i32 {
unsafe { _api_rtc_signal_send(data.as_ptr() as u32, data.len() as u32) }
}
pub fn rtc_signal_recv() -> Option<Vec<u8>> {
let mut buf = vec![0u8; 16 * 1024];
let n = unsafe { _api_rtc_signal_recv(buf.as_mut_ptr() as u32, buf.len() as u32) };
if n <= 0 {
None
} else {
Some(buf[..n as usize].to_vec())
}
}
pub const WS_CONNECTING: u32 = 0;
pub const WS_OPEN: u32 = 1;
pub const WS_CLOSING: u32 = 2;
pub const WS_CLOSED: u32 = 3;
pub struct WsMessage {
pub is_binary: bool,
pub data: Vec<u8>,
}
impl WsMessage {
pub fn text(&self) -> String {
String::from_utf8_lossy(&self.data).to_string()
}
}
pub fn ws_connect(url: &str) -> u32 {
unsafe { _api_ws_connect(url.as_ptr() as u32, url.len() as u32) }
}
pub fn ws_send_text(id: u32, text: &str) -> i32 {
unsafe { _api_ws_send_text(id, text.as_ptr() as u32, text.len() as u32) }
}
pub fn ws_send_binary(id: u32, data: &[u8]) -> i32 {
unsafe { _api_ws_send_binary(id, data.as_ptr() as u32, data.len() as u32) }
}
pub fn ws_recv(id: u32) -> Option<WsMessage> {
let mut buf = vec![0u8; 64 * 1024];
let result = unsafe { _api_ws_recv(id, buf.as_mut_ptr() as u32, buf.len() as u32) };
if result < 0 {
return None;
}
let len = (result & 0xFFFF_FFFF) as usize;
let is_binary = (result >> 32) & 1 == 1;
Some(WsMessage {
is_binary,
data: buf[..len].to_vec(),
})
}
pub fn ws_ready_state(id: u32) -> u32 {
unsafe { _api_ws_ready_state(id) }
}
pub fn ws_close(id: u32) -> i32 {
unsafe { _api_ws_close(id) }
}
pub fn ws_remove(id: u32) {
unsafe { _api_ws_remove(id) }
}
pub fn midi_input_count() -> u32 {
unsafe { _api_midi_input_count() }
}
pub fn midi_output_count() -> u32 {
unsafe { _api_midi_output_count() }
}
pub fn midi_input_name(index: u32) -> String {
let mut buf = [0u8; 128];
let len = unsafe { _api_midi_input_name(index, buf.as_mut_ptr() as u32, buf.len() as u32) };
String::from_utf8_lossy(&buf[..len as usize]).to_string()
}
pub fn midi_output_name(index: u32) -> String {
let mut buf = [0u8; 128];
let len = unsafe { _api_midi_output_name(index, buf.as_mut_ptr() as u32, buf.len() as u32) };
String::from_utf8_lossy(&buf[..len as usize]).to_string()
}
pub fn midi_open_input(index: u32) -> u32 {
unsafe { _api_midi_open_input(index) }
}
pub fn midi_open_output(index: u32) -> u32 {
unsafe { _api_midi_open_output(index) }
}
pub fn midi_send(handle: u32, data: &[u8]) -> i32 {
unsafe { _api_midi_send(handle, data.as_ptr() as u32, data.len() as u32) }
}
pub fn midi_recv(handle: u32) -> Option<Vec<u8>> {
let mut buf = [0u8; 256];
let n = unsafe { _api_midi_recv(handle, buf.as_mut_ptr() as u32, buf.len() as u32) };
if n >= 0 {
return Some(buf[..n as usize].to_vec());
}
if n == -2 {
let mut big = vec![0u8; 64 * 1024];
let n2 = unsafe { _api_midi_recv(handle, big.as_mut_ptr() as u32, big.len() as u32) };
if n2 >= 0 {
big.truncate(n2 as usize);
return Some(big);
}
}
None
}
pub fn midi_close(handle: u32) {
unsafe { _api_midi_close(handle) }
}
pub struct FetchResponse {
pub status: u32,
pub body: Vec<u8>,
}
impl FetchResponse {
pub fn text(&self) -> String {
String::from_utf8_lossy(&self.body).to_string()
}
}
pub fn fetch(
method: &str,
url: &str,
content_type: &str,
body: &[u8],
) -> Result<FetchResponse, i64> {
let mut out_buf = vec![0u8; 4 * 1024 * 1024]; let result = unsafe {
_api_fetch(
method.as_ptr() as u32,
method.len() as u32,
url.as_ptr() as u32,
url.len() as u32,
content_type.as_ptr() as u32,
content_type.len() as u32,
body.as_ptr() as u32,
body.len() as u32,
out_buf.as_mut_ptr() as u32,
out_buf.len() as u32,
)
};
if result < 0 {
return Err(result);
}
let status = (result >> 32) as u32;
let body_len = (result & 0xFFFF_FFFF) as usize;
Ok(FetchResponse {
status,
body: out_buf[..body_len].to_vec(),
})
}
pub fn fetch_get(url: &str) -> Result<FetchResponse, i64> {
fetch("GET", url, "", &[])
}
pub fn fetch_post(url: &str, content_type: &str, body: &[u8]) -> Result<FetchResponse, i64> {
fetch("POST", url, content_type, body)
}
pub fn fetch_post_proto(url: &str, msg: &proto::ProtoEncoder) -> Result<FetchResponse, i64> {
fetch("POST", url, "application/protobuf", msg.as_bytes())
}
pub fn fetch_put(url: &str, content_type: &str, body: &[u8]) -> Result<FetchResponse, i64> {
fetch("PUT", url, content_type, body)
}
pub fn fetch_delete(url: &str) -> Result<FetchResponse, i64> {
fetch("DELETE", url, "", &[])
}
pub const FETCH_PENDING: u32 = 0;
pub const FETCH_STREAMING: u32 = 1;
pub const FETCH_DONE: u32 = 2;
pub const FETCH_ERROR: u32 = 3;
pub const FETCH_ABORTED: u32 = 4;
pub enum FetchChunk {
Data(Vec<u8>),
Pending,
End,
Error,
}
pub fn fetch_begin(method: &str, url: &str, content_type: &str, body: &[u8]) -> u32 {
unsafe {
_api_fetch_begin(
method.as_ptr() as u32,
method.len() as u32,
url.as_ptr() as u32,
url.len() as u32,
content_type.as_ptr() as u32,
content_type.len() as u32,
body.as_ptr() as u32,
body.len() as u32,
)
}
}
pub fn fetch_begin_get(url: &str) -> u32 {
fetch_begin("GET", url, "", &[])
}
pub fn fetch_state(handle: u32) -> u32 {
unsafe { _api_fetch_state(handle) }
}
pub fn fetch_status(handle: u32) -> u32 {
unsafe { _api_fetch_status(handle) }
}
pub fn fetch_recv_into(handle: u32, buf: &mut [u8]) -> i64 {
unsafe { _api_fetch_recv(handle, buf.as_mut_ptr() as u32, buf.len() as u32) }
}
pub fn fetch_recv(handle: u32) -> FetchChunk {
let mut buf = vec![0u8; 64 * 1024];
let n = fetch_recv_into(handle, &mut buf);
match n {
-1 => FetchChunk::Pending,
-2 => FetchChunk::End,
-3 | -4 => FetchChunk::Error,
n if n >= 0 => {
buf.truncate(n as usize);
FetchChunk::Data(buf)
}
_ => FetchChunk::Error,
}
}
pub fn fetch_error(handle: u32) -> Option<String> {
let mut buf = [0u8; 512];
let n = unsafe { _api_fetch_error(handle, buf.as_mut_ptr() as u32, buf.len() as u32) };
if n < 0 {
None
} else {
Some(String::from_utf8_lossy(&buf[..n as usize]).into_owned())
}
}
pub fn fetch_abort(handle: u32) -> bool {
unsafe { _api_fetch_abort(handle) != 0 }
}
pub fn fetch_remove(handle: u32) {
unsafe { _api_fetch_remove(handle) }
}
pub fn load_module(url: &str) -> i32 {
unsafe { _api_load_module(url.as_ptr() as u32, url.len() as u32) }
}
pub fn hash_sha256(data: &[u8]) -> [u8; 32] {
let mut out = [0u8; 32];
unsafe {
_api_hash_sha256(
data.as_ptr() as u32,
data.len() as u32,
out.as_mut_ptr() as u32,
);
}
out
}
pub fn hash_sha256_hex(data: &[u8]) -> String {
let hash = hash_sha256(data);
let mut hex = String::with_capacity(64);
for byte in &hash {
hex.push(HEX_CHARS[(*byte >> 4) as usize]);
hex.push(HEX_CHARS[(*byte & 0x0F) as usize]);
}
hex
}
const HEX_CHARS: [char; 16] = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
];
pub fn base64_encode(data: &[u8]) -> String {
let mut buf = vec![0u8; data.len() * 4 / 3 + 8];
let len = unsafe {
_api_base64_encode(
data.as_ptr() as u32,
data.len() as u32,
buf.as_mut_ptr() as u32,
buf.len() as u32,
)
};
String::from_utf8_lossy(&buf[..len as usize]).to_string()
}
pub fn base64_decode(encoded: &str) -> Vec<u8> {
let mut buf = vec![0u8; encoded.len()];
let len = unsafe {
_api_base64_decode(
encoded.as_ptr() as u32,
encoded.len() as u32,
buf.as_mut_ptr() as u32,
buf.len() as u32,
)
};
buf[..len as usize].to_vec()
}
pub fn kv_store_set(key: &str, value: &[u8]) -> bool {
let rc = unsafe {
_api_kv_store_set(
key.as_ptr() as u32,
key.len() as u32,
value.as_ptr() as u32,
value.len() as u32,
)
};
rc == 0
}
pub fn kv_store_set_str(key: &str, value: &str) -> bool {
kv_store_set(key, value.as_bytes())
}
pub fn kv_store_get(key: &str) -> Option<Vec<u8>> {
let mut buf = vec![0u8; 64 * 1024]; let rc = unsafe {
_api_kv_store_get(
key.as_ptr() as u32,
key.len() as u32,
buf.as_mut_ptr() as u32,
buf.len() as u32,
)
};
if rc < 0 {
return None;
}
Some(buf[..rc as usize].to_vec())
}
pub fn kv_store_get_str(key: &str) -> Option<String> {
kv_store_get(key).map(|v| String::from_utf8_lossy(&v).into_owned())
}
pub fn kv_store_delete(key: &str) -> bool {
let rc = unsafe { _api_kv_store_delete(key.as_ptr() as u32, key.len() as u32) };
rc == 0
}
pub fn navigate(url: &str) -> i32 {
unsafe { _api_navigate(url.as_ptr() as u32, url.len() as u32) }
}
pub fn push_state(state: &[u8], title: &str, url: &str) {
unsafe {
_api_push_state(
state.as_ptr() as u32,
state.len() as u32,
title.as_ptr() as u32,
title.len() as u32,
url.as_ptr() as u32,
url.len() as u32,
)
}
}
pub fn replace_state(state: &[u8], title: &str, url: &str) {
unsafe {
_api_replace_state(
state.as_ptr() as u32,
state.len() as u32,
title.as_ptr() as u32,
title.len() as u32,
url.as_ptr() as u32,
url.len() as u32,
)
}
}
pub fn get_url() -> String {
let mut buf = [0u8; 4096];
let len = unsafe { _api_get_url(buf.as_mut_ptr() as u32, buf.len() as u32) };
String::from_utf8_lossy(&buf[..len as usize]).to_string()
}
pub fn get_state() -> Option<Vec<u8>> {
let mut buf = vec![0u8; 64 * 1024]; let rc = unsafe { _api_get_state(buf.as_mut_ptr() as u32, buf.len() as u32) };
if rc < 0 {
return None;
}
Some(buf[..rc as usize].to_vec())
}
pub fn history_length() -> u32 {
unsafe { _api_history_length() }
}
pub fn history_back() -> bool {
unsafe { _api_history_back() == 1 }
}
pub fn history_forward() -> bool {
unsafe { _api_history_forward() == 1 }
}
pub fn register_hyperlink(x: f32, y: f32, w: f32, h: f32, url: &str) -> i32 {
unsafe { _api_register_hyperlink(x, y, w, h, url.as_ptr() as u32, url.len() as u32) }
}
pub fn clear_hyperlinks() {
unsafe { _api_clear_hyperlinks() }
}
pub fn url_resolve(base: &str, relative: &str) -> Option<String> {
let mut buf = [0u8; 4096];
let rc = unsafe {
_api_url_resolve(
base.as_ptr() as u32,
base.len() as u32,
relative.as_ptr() as u32,
relative.len() as u32,
buf.as_mut_ptr() as u32,
buf.len() as u32,
)
};
if rc < 0 {
return None;
}
Some(String::from_utf8_lossy(&buf[..rc as usize]).to_string())
}
pub fn url_encode(input: &str) -> String {
let mut buf = vec![0u8; input.len() * 3 + 4];
let len = unsafe {
_api_url_encode(
input.as_ptr() as u32,
input.len() as u32,
buf.as_mut_ptr() as u32,
buf.len() as u32,
)
};
String::from_utf8_lossy(&buf[..len as usize]).to_string()
}
pub fn url_decode(input: &str) -> String {
let mut buf = vec![0u8; input.len() + 4];
let len = unsafe {
_api_url_decode(
input.as_ptr() as u32,
input.len() as u32,
buf.as_mut_ptr() as u32,
buf.len() as u32,
)
};
String::from_utf8_lossy(&buf[..len as usize]).to_string()
}
pub fn mouse_position() -> (f32, f32) {
let packed = unsafe { _api_mouse_position() };
let x = f32::from_bits((packed >> 32) as u32);
let y = f32::from_bits((packed & 0xFFFF_FFFF) as u32);
(x, y)
}
pub fn mouse_button_down(button: u32) -> bool {
unsafe { _api_mouse_button_down(button) != 0 }
}
pub fn mouse_button_clicked(button: u32) -> bool {
unsafe { _api_mouse_button_clicked(button) != 0 }
}
pub fn key_down(key: u32) -> bool {
unsafe { _api_key_down(key) != 0 }
}
pub fn key_pressed(key: u32) -> bool {
unsafe { _api_key_pressed(key) != 0 }
}
pub fn scroll_delta() -> (f32, f32) {
let packed = unsafe { _api_scroll_delta() };
let x = f32::from_bits((packed >> 32) as u32);
let y = f32::from_bits((packed & 0xFFFF_FFFF) as u32);
(x, y)
}
pub fn modifiers() -> u32 {
unsafe { _api_modifiers() }
}
pub fn shift_held() -> bool {
modifiers() & 1 != 0
}
pub fn ctrl_held() -> bool {
modifiers() & 2 != 0
}
pub fn alt_held() -> bool {
modifiers() & 4 != 0
}
pub const KEY_A: u32 = 0;
pub const KEY_B: u32 = 1;
pub const KEY_C: u32 = 2;
pub const KEY_D: u32 = 3;
pub const KEY_E: u32 = 4;
pub const KEY_F: u32 = 5;
pub const KEY_G: u32 = 6;
pub const KEY_H: u32 = 7;
pub const KEY_I: u32 = 8;
pub const KEY_J: u32 = 9;
pub const KEY_K: u32 = 10;
pub const KEY_L: u32 = 11;
pub const KEY_M: u32 = 12;
pub const KEY_N: u32 = 13;
pub const KEY_O: u32 = 14;
pub const KEY_P: u32 = 15;
pub const KEY_Q: u32 = 16;
pub const KEY_R: u32 = 17;
pub const KEY_S: u32 = 18;
pub const KEY_T: u32 = 19;
pub const KEY_U: u32 = 20;
pub const KEY_V: u32 = 21;
pub const KEY_W: u32 = 22;
pub const KEY_X: u32 = 23;
pub const KEY_Y: u32 = 24;
pub const KEY_Z: u32 = 25;
pub const KEY_0: u32 = 26;
pub const KEY_1: u32 = 27;
pub const KEY_2: u32 = 28;
pub const KEY_3: u32 = 29;
pub const KEY_4: u32 = 30;
pub const KEY_5: u32 = 31;
pub const KEY_6: u32 = 32;
pub const KEY_7: u32 = 33;
pub const KEY_8: u32 = 34;
pub const KEY_9: u32 = 35;
pub const KEY_ENTER: u32 = 36;
pub const KEY_ESCAPE: u32 = 37;
pub const KEY_TAB: u32 = 38;
pub const KEY_BACKSPACE: u32 = 39;
pub const KEY_DELETE: u32 = 40;
pub const KEY_SPACE: u32 = 41;
pub const KEY_UP: u32 = 42;
pub const KEY_DOWN: u32 = 43;
pub const KEY_LEFT: u32 = 44;
pub const KEY_RIGHT: u32 = 45;
pub const KEY_HOME: u32 = 46;
pub const KEY_END: u32 = 47;
pub const KEY_PAGE_UP: u32 = 48;
pub const KEY_PAGE_DOWN: u32 = 49;
pub fn ui_button(id: u32, x: f32, y: f32, w: f32, h: f32, label: &str) -> bool {
unsafe { _api_ui_button(id, x, y, w, h, label.as_ptr() as u32, label.len() as u32) != 0 }
}
pub fn ui_checkbox(id: u32, x: f32, y: f32, label: &str, initial: bool) -> bool {
unsafe {
_api_ui_checkbox(
id,
x,
y,
label.as_ptr() as u32,
label.len() as u32,
if initial { 1 } else { 0 },
) != 0
}
}
pub fn ui_slider(id: u32, x: f32, y: f32, w: f32, min: f32, max: f32, initial: f32) -> f32 {
unsafe { _api_ui_slider(id, x, y, w, min, max, initial) }
}
pub fn ui_text_input(id: u32, x: f32, y: f32, w: f32, initial: &str) -> String {
let mut buf = [0u8; 4096];
let len = unsafe {
_api_ui_text_input(
id,
x,
y,
w,
initial.as_ptr() as u32,
initial.len() as u32,
buf.as_mut_ptr() as u32,
buf.len() as u32,
)
};
String::from_utf8_lossy(&buf[..len as usize]).to_string()
}