1pub mod proto;
82
83#[link(wasm_import_module = "oxide")]
86extern "C" {
87 #[link_name = "api_log"]
88 fn _api_log(ptr: u32, len: u32);
89
90 #[link_name = "api_warn"]
91 fn _api_warn(ptr: u32, len: u32);
92
93 #[link_name = "api_error"]
94 fn _api_error(ptr: u32, len: u32);
95
96 #[link_name = "api_get_location"]
97 fn _api_get_location(out_ptr: u32, out_cap: u32) -> u32;
98
99 #[link_name = "api_upload_file"]
100 fn _api_upload_file(name_ptr: u32, name_cap: u32, data_ptr: u32, data_cap: u32) -> u64;
101
102 #[link_name = "api_canvas_clear"]
103 fn _api_canvas_clear(r: u32, g: u32, b: u32, a: u32);
104
105 #[link_name = "api_canvas_rect"]
106 fn _api_canvas_rect(x: f32, y: f32, w: f32, h: f32, r: u32, g: u32, b: u32, a: u32);
107
108 #[link_name = "api_canvas_circle"]
109 fn _api_canvas_circle(cx: f32, cy: f32, radius: f32, r: u32, g: u32, b: u32, a: u32);
110
111 #[link_name = "api_canvas_text"]
112 fn _api_canvas_text(x: f32, y: f32, size: f32, r: u32, g: u32, b: u32, ptr: u32, len: u32);
113
114 #[link_name = "api_canvas_line"]
115 fn _api_canvas_line(x1: f32, y1: f32, x2: f32, y2: f32, r: u32, g: u32, b: u32, thickness: f32);
116
117 #[link_name = "api_canvas_dimensions"]
118 fn _api_canvas_dimensions() -> u64;
119
120 #[link_name = "api_canvas_image"]
121 fn _api_canvas_image(x: f32, y: f32, w: f32, h: f32, data_ptr: u32, data_len: u32);
122
123 #[link_name = "api_storage_set"]
124 fn _api_storage_set(key_ptr: u32, key_len: u32, val_ptr: u32, val_len: u32);
125
126 #[link_name = "api_storage_get"]
127 fn _api_storage_get(key_ptr: u32, key_len: u32, out_ptr: u32, out_cap: u32) -> u32;
128
129 #[link_name = "api_storage_remove"]
130 fn _api_storage_remove(key_ptr: u32, key_len: u32);
131
132 #[link_name = "api_clipboard_write"]
133 fn _api_clipboard_write(ptr: u32, len: u32);
134
135 #[link_name = "api_clipboard_read"]
136 fn _api_clipboard_read(out_ptr: u32, out_cap: u32) -> u32;
137
138 #[link_name = "api_time_now_ms"]
139 fn _api_time_now_ms() -> u64;
140
141 #[link_name = "api_set_timeout"]
142 fn _api_set_timeout(callback_id: u32, delay_ms: u32) -> u32;
143
144 #[link_name = "api_set_interval"]
145 fn _api_set_interval(callback_id: u32, interval_ms: u32) -> u32;
146
147 #[link_name = "api_clear_timer"]
148 fn _api_clear_timer(timer_id: u32);
149
150 #[link_name = "api_random"]
151 fn _api_random() -> u64;
152
153 #[link_name = "api_notify"]
154 fn _api_notify(title_ptr: u32, title_len: u32, body_ptr: u32, body_len: u32);
155
156 #[link_name = "api_fetch"]
157 fn _api_fetch(
158 method_ptr: u32,
159 method_len: u32,
160 url_ptr: u32,
161 url_len: u32,
162 ct_ptr: u32,
163 ct_len: u32,
164 body_ptr: u32,
165 body_len: u32,
166 out_ptr: u32,
167 out_cap: u32,
168 ) -> i64;
169
170 #[link_name = "api_load_module"]
171 fn _api_load_module(url_ptr: u32, url_len: u32) -> i32;
172
173 #[link_name = "api_hash_sha256"]
174 fn _api_hash_sha256(data_ptr: u32, data_len: u32, out_ptr: u32) -> u32;
175
176 #[link_name = "api_base64_encode"]
177 fn _api_base64_encode(data_ptr: u32, data_len: u32, out_ptr: u32, out_cap: u32) -> u32;
178
179 #[link_name = "api_base64_decode"]
180 fn _api_base64_decode(data_ptr: u32, data_len: u32, out_ptr: u32, out_cap: u32) -> u32;
181
182 #[link_name = "api_kv_store_set"]
183 fn _api_kv_store_set(key_ptr: u32, key_len: u32, val_ptr: u32, val_len: u32) -> i32;
184
185 #[link_name = "api_kv_store_get"]
186 fn _api_kv_store_get(key_ptr: u32, key_len: u32, out_ptr: u32, out_cap: u32) -> i32;
187
188 #[link_name = "api_kv_store_delete"]
189 fn _api_kv_store_delete(key_ptr: u32, key_len: u32) -> i32;
190
191 #[link_name = "api_navigate"]
194 fn _api_navigate(url_ptr: u32, url_len: u32) -> i32;
195
196 #[link_name = "api_push_state"]
197 fn _api_push_state(
198 state_ptr: u32,
199 state_len: u32,
200 title_ptr: u32,
201 title_len: u32,
202 url_ptr: u32,
203 url_len: u32,
204 );
205
206 #[link_name = "api_replace_state"]
207 fn _api_replace_state(
208 state_ptr: u32,
209 state_len: u32,
210 title_ptr: u32,
211 title_len: u32,
212 url_ptr: u32,
213 url_len: u32,
214 );
215
216 #[link_name = "api_get_url"]
217 fn _api_get_url(out_ptr: u32, out_cap: u32) -> u32;
218
219 #[link_name = "api_get_state"]
220 fn _api_get_state(out_ptr: u32, out_cap: u32) -> i32;
221
222 #[link_name = "api_history_length"]
223 fn _api_history_length() -> u32;
224
225 #[link_name = "api_history_back"]
226 fn _api_history_back() -> i32;
227
228 #[link_name = "api_history_forward"]
229 fn _api_history_forward() -> i32;
230
231 #[link_name = "api_register_hyperlink"]
234 fn _api_register_hyperlink(x: f32, y: f32, w: f32, h: f32, url_ptr: u32, url_len: u32) -> i32;
235
236 #[link_name = "api_clear_hyperlinks"]
237 fn _api_clear_hyperlinks();
238
239 #[link_name = "api_mouse_position"]
242 fn _api_mouse_position() -> u64;
243
244 #[link_name = "api_mouse_button_down"]
245 fn _api_mouse_button_down(button: u32) -> u32;
246
247 #[link_name = "api_mouse_button_clicked"]
248 fn _api_mouse_button_clicked(button: u32) -> u32;
249
250 #[link_name = "api_key_down"]
251 fn _api_key_down(key: u32) -> u32;
252
253 #[link_name = "api_key_pressed"]
254 fn _api_key_pressed(key: u32) -> u32;
255
256 #[link_name = "api_scroll_delta"]
257 fn _api_scroll_delta() -> u64;
258
259 #[link_name = "api_modifiers"]
260 fn _api_modifiers() -> u32;
261
262 #[link_name = "api_ui_button"]
265 fn _api_ui_button(
266 id: u32,
267 x: f32,
268 y: f32,
269 w: f32,
270 h: f32,
271 label_ptr: u32,
272 label_len: u32,
273 ) -> u32;
274
275 #[link_name = "api_ui_checkbox"]
276 fn _api_ui_checkbox(
277 id: u32,
278 x: f32,
279 y: f32,
280 label_ptr: u32,
281 label_len: u32,
282 initial: u32,
283 ) -> u32;
284
285 #[link_name = "api_ui_slider"]
286 fn _api_ui_slider(id: u32, x: f32, y: f32, w: f32, min: f32, max: f32, initial: f32) -> f32;
287
288 #[link_name = "api_ui_text_input"]
289 fn _api_ui_text_input(
290 id: u32,
291 x: f32,
292 y: f32,
293 w: f32,
294 init_ptr: u32,
295 init_len: u32,
296 out_ptr: u32,
297 out_cap: u32,
298 ) -> u32;
299
300 #[link_name = "api_audio_play"]
303 fn _api_audio_play(data_ptr: u32, data_len: u32) -> i32;
304
305 #[link_name = "api_audio_play_url"]
306 fn _api_audio_play_url(url_ptr: u32, url_len: u32) -> i32;
307
308 #[link_name = "api_audio_pause"]
309 fn _api_audio_pause();
310
311 #[link_name = "api_audio_resume"]
312 fn _api_audio_resume();
313
314 #[link_name = "api_audio_stop"]
315 fn _api_audio_stop();
316
317 #[link_name = "api_audio_set_volume"]
318 fn _api_audio_set_volume(level: f32);
319
320 #[link_name = "api_audio_get_volume"]
321 fn _api_audio_get_volume() -> f32;
322
323 #[link_name = "api_audio_is_playing"]
324 fn _api_audio_is_playing() -> u32;
325
326 #[link_name = "api_audio_position"]
327 fn _api_audio_position() -> u64;
328
329 #[link_name = "api_audio_seek"]
330 fn _api_audio_seek(position_ms: u64) -> i32;
331
332 #[link_name = "api_audio_duration"]
333 fn _api_audio_duration() -> u64;
334
335 #[link_name = "api_audio_set_loop"]
336 fn _api_audio_set_loop(enabled: u32);
337
338 #[link_name = "api_audio_channel_play"]
339 fn _api_audio_channel_play(channel: u32, data_ptr: u32, data_len: u32) -> i32;
340
341 #[link_name = "api_audio_channel_stop"]
342 fn _api_audio_channel_stop(channel: u32);
343
344 #[link_name = "api_audio_channel_set_volume"]
345 fn _api_audio_channel_set_volume(channel: u32, level: f32);
346
347 #[link_name = "api_url_resolve"]
350 fn _api_url_resolve(
351 base_ptr: u32,
352 base_len: u32,
353 rel_ptr: u32,
354 rel_len: u32,
355 out_ptr: u32,
356 out_cap: u32,
357 ) -> i32;
358
359 #[link_name = "api_url_encode"]
360 fn _api_url_encode(input_ptr: u32, input_len: u32, out_ptr: u32, out_cap: u32) -> u32;
361
362 #[link_name = "api_url_decode"]
363 fn _api_url_decode(input_ptr: u32, input_len: u32, out_ptr: u32, out_cap: u32) -> u32;
364}
365
366pub fn log(msg: &str) {
370 unsafe { _api_log(msg.as_ptr() as u32, msg.len() as u32) }
371}
372
373pub fn warn(msg: &str) {
375 unsafe { _api_warn(msg.as_ptr() as u32, msg.len() as u32) }
376}
377
378pub fn error(msg: &str) {
380 unsafe { _api_error(msg.as_ptr() as u32, msg.len() as u32) }
381}
382
383pub fn get_location() -> String {
387 let mut buf = [0u8; 128];
388 let len = unsafe { _api_get_location(buf.as_mut_ptr() as u32, buf.len() as u32) };
389 String::from_utf8_lossy(&buf[..len as usize]).to_string()
390}
391
392pub struct UploadedFile {
396 pub name: String,
397 pub data: Vec<u8>,
398}
399
400pub fn upload_file() -> Option<UploadedFile> {
403 let mut name_buf = [0u8; 256];
404 let mut data_buf = vec![0u8; 1024 * 1024]; let result = unsafe {
407 _api_upload_file(
408 name_buf.as_mut_ptr() as u32,
409 name_buf.len() as u32,
410 data_buf.as_mut_ptr() as u32,
411 data_buf.len() as u32,
412 )
413 };
414
415 if result == 0 {
416 return None;
417 }
418
419 let name_len = (result >> 32) as usize;
420 let data_len = (result & 0xFFFF_FFFF) as usize;
421
422 Some(UploadedFile {
423 name: String::from_utf8_lossy(&name_buf[..name_len]).to_string(),
424 data: data_buf[..data_len].to_vec(),
425 })
426}
427
428pub fn canvas_clear(r: u8, g: u8, b: u8, a: u8) {
432 unsafe { _api_canvas_clear(r as u32, g as u32, b as u32, a as u32) }
433}
434
435pub fn canvas_rect(x: f32, y: f32, w: f32, h: f32, r: u8, g: u8, b: u8, a: u8) {
437 unsafe { _api_canvas_rect(x, y, w, h, r as u32, g as u32, b as u32, a as u32) }
438}
439
440pub fn canvas_circle(cx: f32, cy: f32, radius: f32, r: u8, g: u8, b: u8, a: u8) {
442 unsafe { _api_canvas_circle(cx, cy, radius, r as u32, g as u32, b as u32, a as u32) }
443}
444
445pub fn canvas_text(x: f32, y: f32, size: f32, r: u8, g: u8, b: u8, text: &str) {
447 unsafe {
448 _api_canvas_text(
449 x,
450 y,
451 size,
452 r as u32,
453 g as u32,
454 b as u32,
455 text.as_ptr() as u32,
456 text.len() as u32,
457 )
458 }
459}
460
461pub fn canvas_line(x1: f32, y1: f32, x2: f32, y2: f32, r: u8, g: u8, b: u8, thickness: f32) {
463 unsafe { _api_canvas_line(x1, y1, x2, y2, r as u32, g as u32, b as u32, thickness) }
464}
465
466pub fn canvas_dimensions() -> (u32, u32) {
468 let packed = unsafe { _api_canvas_dimensions() };
469 ((packed >> 32) as u32, (packed & 0xFFFF_FFFF) as u32)
470}
471
472pub fn canvas_image(x: f32, y: f32, w: f32, h: f32, data: &[u8]) {
475 unsafe { _api_canvas_image(x, y, w, h, data.as_ptr() as u32, data.len() as u32) }
476}
477
478pub fn storage_set(key: &str, value: &str) {
482 unsafe {
483 _api_storage_set(
484 key.as_ptr() as u32,
485 key.len() as u32,
486 value.as_ptr() as u32,
487 value.len() as u32,
488 )
489 }
490}
491
492pub fn storage_get(key: &str) -> String {
494 let mut buf = [0u8; 4096];
495 let len = unsafe {
496 _api_storage_get(
497 key.as_ptr() as u32,
498 key.len() as u32,
499 buf.as_mut_ptr() as u32,
500 buf.len() as u32,
501 )
502 };
503 String::from_utf8_lossy(&buf[..len as usize]).to_string()
504}
505
506pub fn storage_remove(key: &str) {
508 unsafe { _api_storage_remove(key.as_ptr() as u32, key.len() as u32) }
509}
510
511pub fn clipboard_write(text: &str) {
515 unsafe { _api_clipboard_write(text.as_ptr() as u32, text.len() as u32) }
516}
517
518pub fn clipboard_read() -> String {
520 let mut buf = [0u8; 4096];
521 let len = unsafe { _api_clipboard_read(buf.as_mut_ptr() as u32, buf.len() as u32) };
522 String::from_utf8_lossy(&buf[..len as usize]).to_string()
523}
524
525pub fn time_now_ms() -> u64 {
529 unsafe { _api_time_now_ms() }
530}
531
532pub fn set_timeout(callback_id: u32, delay_ms: u32) -> u32 {
536 unsafe { _api_set_timeout(callback_id, delay_ms) }
537}
538
539pub fn set_interval(callback_id: u32, interval_ms: u32) -> u32 {
543 unsafe { _api_set_interval(callback_id, interval_ms) }
544}
545
546pub fn clear_timer(timer_id: u32) {
548 unsafe { _api_clear_timer(timer_id) }
549}
550
551pub fn random_u64() -> u64 {
555 unsafe { _api_random() }
556}
557
558pub fn random_f64() -> f64 {
560 (random_u64() >> 11) as f64 / (1u64 << 53) as f64
561}
562
563pub fn notify(title: &str, body: &str) {
567 unsafe {
568 _api_notify(
569 title.as_ptr() as u32,
570 title.len() as u32,
571 body.as_ptr() as u32,
572 body.len() as u32,
573 )
574 }
575}
576
577pub fn audio_play(data: &[u8]) -> i32 {
582 unsafe { _api_audio_play(data.as_ptr() as u32, data.len() as u32) }
583}
584
585pub fn audio_play_url(url: &str) -> i32 {
589 unsafe { _api_audio_play_url(url.as_ptr() as u32, url.len() as u32) }
590}
591
592pub fn audio_pause() {
594 unsafe { _api_audio_pause() }
595}
596
597pub fn audio_resume() {
599 unsafe { _api_audio_resume() }
600}
601
602pub fn audio_stop() {
604 unsafe { _api_audio_stop() }
605}
606
607pub fn audio_set_volume(level: f32) {
609 unsafe { _api_audio_set_volume(level) }
610}
611
612pub fn audio_get_volume() -> f32 {
614 unsafe { _api_audio_get_volume() }
615}
616
617pub fn audio_is_playing() -> bool {
619 unsafe { _api_audio_is_playing() != 0 }
620}
621
622pub fn audio_position() -> u64 {
624 unsafe { _api_audio_position() }
625}
626
627pub fn audio_seek(position_ms: u64) -> i32 {
629 unsafe { _api_audio_seek(position_ms) }
630}
631
632pub fn audio_duration() -> u64 {
635 unsafe { _api_audio_duration() }
636}
637
638pub fn audio_set_loop(enabled: bool) {
641 unsafe { _api_audio_set_loop(if enabled { 1 } else { 0 }) }
642}
643
644pub fn audio_channel_play(channel: u32, data: &[u8]) -> i32 {
650 unsafe { _api_audio_channel_play(channel, data.as_ptr() as u32, data.len() as u32) }
651}
652
653pub fn audio_channel_stop(channel: u32) {
655 unsafe { _api_audio_channel_stop(channel) }
656}
657
658pub fn audio_channel_set_volume(channel: u32, level: f32) {
660 unsafe { _api_audio_channel_set_volume(channel, level) }
661}
662
663pub struct FetchResponse {
667 pub status: u32,
668 pub body: Vec<u8>,
669}
670
671impl FetchResponse {
672 pub fn text(&self) -> String {
674 String::from_utf8_lossy(&self.body).to_string()
675 }
676}
677
678pub fn fetch(
684 method: &str,
685 url: &str,
686 content_type: &str,
687 body: &[u8],
688) -> Result<FetchResponse, i64> {
689 let mut out_buf = vec![0u8; 4 * 1024 * 1024]; let result = unsafe {
691 _api_fetch(
692 method.as_ptr() as u32,
693 method.len() as u32,
694 url.as_ptr() as u32,
695 url.len() as u32,
696 content_type.as_ptr() as u32,
697 content_type.len() as u32,
698 body.as_ptr() as u32,
699 body.len() as u32,
700 out_buf.as_mut_ptr() as u32,
701 out_buf.len() as u32,
702 )
703 };
704 if result < 0 {
705 return Err(result);
706 }
707 let status = (result >> 32) as u32;
708 let body_len = (result & 0xFFFF_FFFF) as usize;
709 Ok(FetchResponse {
710 status,
711 body: out_buf[..body_len].to_vec(),
712 })
713}
714
715pub fn fetch_get(url: &str) -> Result<FetchResponse, i64> {
717 fetch("GET", url, "", &[])
718}
719
720pub fn fetch_post(url: &str, content_type: &str, body: &[u8]) -> Result<FetchResponse, i64> {
722 fetch("POST", url, content_type, body)
723}
724
725pub fn fetch_post_proto(url: &str, msg: &proto::ProtoEncoder) -> Result<FetchResponse, i64> {
727 fetch("POST", url, "application/protobuf", msg.as_bytes())
728}
729
730pub fn fetch_put(url: &str, content_type: &str, body: &[u8]) -> Result<FetchResponse, i64> {
732 fetch("PUT", url, content_type, body)
733}
734
735pub fn fetch_delete(url: &str) -> Result<FetchResponse, i64> {
737 fetch("DELETE", url, "", &[])
738}
739
740pub fn load_module(url: &str) -> i32 {
746 unsafe { _api_load_module(url.as_ptr() as u32, url.len() as u32) }
747}
748
749pub fn hash_sha256(data: &[u8]) -> [u8; 32] {
753 let mut out = [0u8; 32];
754 unsafe {
755 _api_hash_sha256(
756 data.as_ptr() as u32,
757 data.len() as u32,
758 out.as_mut_ptr() as u32,
759 );
760 }
761 out
762}
763
764pub fn hash_sha256_hex(data: &[u8]) -> String {
766 let hash = hash_sha256(data);
767 let mut hex = String::with_capacity(64);
768 for byte in &hash {
769 hex.push(HEX_CHARS[(*byte >> 4) as usize]);
770 hex.push(HEX_CHARS[(*byte & 0x0F) as usize]);
771 }
772 hex
773}
774
775const HEX_CHARS: [char; 16] = [
776 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
777];
778
779pub fn base64_encode(data: &[u8]) -> String {
783 let mut buf = vec![0u8; data.len() * 4 / 3 + 8];
784 let len = unsafe {
785 _api_base64_encode(
786 data.as_ptr() as u32,
787 data.len() as u32,
788 buf.as_mut_ptr() as u32,
789 buf.len() as u32,
790 )
791 };
792 String::from_utf8_lossy(&buf[..len as usize]).to_string()
793}
794
795pub fn base64_decode(encoded: &str) -> Vec<u8> {
797 let mut buf = vec![0u8; encoded.len()];
798 let len = unsafe {
799 _api_base64_decode(
800 encoded.as_ptr() as u32,
801 encoded.len() as u32,
802 buf.as_mut_ptr() as u32,
803 buf.len() as u32,
804 )
805 };
806 buf[..len as usize].to_vec()
807}
808
809pub fn kv_store_set(key: &str, value: &[u8]) -> bool {
814 let rc = unsafe {
815 _api_kv_store_set(
816 key.as_ptr() as u32,
817 key.len() as u32,
818 value.as_ptr() as u32,
819 value.len() as u32,
820 )
821 };
822 rc == 0
823}
824
825pub fn kv_store_set_str(key: &str, value: &str) -> bool {
827 kv_store_set(key, value.as_bytes())
828}
829
830pub fn kv_store_get(key: &str) -> Option<Vec<u8>> {
833 let mut buf = vec![0u8; 64 * 1024]; let rc = unsafe {
835 _api_kv_store_get(
836 key.as_ptr() as u32,
837 key.len() as u32,
838 buf.as_mut_ptr() as u32,
839 buf.len() as u32,
840 )
841 };
842 if rc < 0 {
843 return None;
844 }
845 Some(buf[..rc as usize].to_vec())
846}
847
848pub fn kv_store_get_str(key: &str) -> Option<String> {
850 kv_store_get(key).map(|v| String::from_utf8_lossy(&v).into_owned())
851}
852
853pub fn kv_store_delete(key: &str) -> bool {
855 let rc = unsafe { _api_kv_store_delete(key.as_ptr() as u32, key.len() as u32) };
856 rc == 0
857}
858
859pub fn navigate(url: &str) -> i32 {
865 unsafe { _api_navigate(url.as_ptr() as u32, url.len() as u32) }
866}
867
868pub fn push_state(state: &[u8], title: &str, url: &str) {
876 unsafe {
877 _api_push_state(
878 state.as_ptr() as u32,
879 state.len() as u32,
880 title.as_ptr() as u32,
881 title.len() as u32,
882 url.as_ptr() as u32,
883 url.len() as u32,
884 )
885 }
886}
887
888pub fn replace_state(state: &[u8], title: &str, url: &str) {
891 unsafe {
892 _api_replace_state(
893 state.as_ptr() as u32,
894 state.len() as u32,
895 title.as_ptr() as u32,
896 title.len() as u32,
897 url.as_ptr() as u32,
898 url.len() as u32,
899 )
900 }
901}
902
903pub fn get_url() -> String {
905 let mut buf = [0u8; 4096];
906 let len = unsafe { _api_get_url(buf.as_mut_ptr() as u32, buf.len() as u32) };
907 String::from_utf8_lossy(&buf[..len as usize]).to_string()
908}
909
910pub fn get_state() -> Option<Vec<u8>> {
913 let mut buf = vec![0u8; 64 * 1024]; let rc = unsafe { _api_get_state(buf.as_mut_ptr() as u32, buf.len() as u32) };
915 if rc < 0 {
916 return None;
917 }
918 Some(buf[..rc as usize].to_vec())
919}
920
921pub fn history_length() -> u32 {
923 unsafe { _api_history_length() }
924}
925
926pub fn history_back() -> bool {
928 unsafe { _api_history_back() == 1 }
929}
930
931pub fn history_forward() -> bool {
933 unsafe { _api_history_forward() == 1 }
934}
935
936pub fn register_hyperlink(x: f32, y: f32, w: f32, h: f32, url: &str) -> i32 {
944 unsafe { _api_register_hyperlink(x, y, w, h, url.as_ptr() as u32, url.len() as u32) }
945}
946
947pub fn clear_hyperlinks() {
949 unsafe { _api_clear_hyperlinks() }
950}
951
952pub fn url_resolve(base: &str, relative: &str) -> Option<String> {
957 let mut buf = [0u8; 4096];
958 let rc = unsafe {
959 _api_url_resolve(
960 base.as_ptr() as u32,
961 base.len() as u32,
962 relative.as_ptr() as u32,
963 relative.len() as u32,
964 buf.as_mut_ptr() as u32,
965 buf.len() as u32,
966 )
967 };
968 if rc < 0 {
969 return None;
970 }
971 Some(String::from_utf8_lossy(&buf[..rc as usize]).to_string())
972}
973
974pub fn url_encode(input: &str) -> String {
976 let mut buf = vec![0u8; input.len() * 3 + 4];
977 let len = unsafe {
978 _api_url_encode(
979 input.as_ptr() as u32,
980 input.len() as u32,
981 buf.as_mut_ptr() as u32,
982 buf.len() as u32,
983 )
984 };
985 String::from_utf8_lossy(&buf[..len as usize]).to_string()
986}
987
988pub fn url_decode(input: &str) -> String {
990 let mut buf = vec![0u8; input.len() + 4];
991 let len = unsafe {
992 _api_url_decode(
993 input.as_ptr() as u32,
994 input.len() as u32,
995 buf.as_mut_ptr() as u32,
996 buf.len() as u32,
997 )
998 };
999 String::from_utf8_lossy(&buf[..len as usize]).to_string()
1000}
1001
1002pub fn mouse_position() -> (f32, f32) {
1006 let packed = unsafe { _api_mouse_position() };
1007 let x = f32::from_bits((packed >> 32) as u32);
1008 let y = f32::from_bits((packed & 0xFFFF_FFFF) as u32);
1009 (x, y)
1010}
1011
1012pub fn mouse_button_down(button: u32) -> bool {
1015 unsafe { _api_mouse_button_down(button) != 0 }
1016}
1017
1018pub fn mouse_button_clicked(button: u32) -> bool {
1020 unsafe { _api_mouse_button_clicked(button) != 0 }
1021}
1022
1023pub fn key_down(key: u32) -> bool {
1026 unsafe { _api_key_down(key) != 0 }
1027}
1028
1029pub fn key_pressed(key: u32) -> bool {
1031 unsafe { _api_key_pressed(key) != 0 }
1032}
1033
1034pub fn scroll_delta() -> (f32, f32) {
1036 let packed = unsafe { _api_scroll_delta() };
1037 let x = f32::from_bits((packed >> 32) as u32);
1038 let y = f32::from_bits((packed & 0xFFFF_FFFF) as u32);
1039 (x, y)
1040}
1041
1042pub fn modifiers() -> u32 {
1044 unsafe { _api_modifiers() }
1045}
1046
1047pub fn shift_held() -> bool {
1049 modifiers() & 1 != 0
1050}
1051
1052pub fn ctrl_held() -> bool {
1054 modifiers() & 2 != 0
1055}
1056
1057pub fn alt_held() -> bool {
1059 modifiers() & 4 != 0
1060}
1061
1062pub const KEY_A: u32 = 0;
1065pub const KEY_B: u32 = 1;
1066pub const KEY_C: u32 = 2;
1067pub const KEY_D: u32 = 3;
1068pub const KEY_E: u32 = 4;
1069pub const KEY_F: u32 = 5;
1070pub const KEY_G: u32 = 6;
1071pub const KEY_H: u32 = 7;
1072pub const KEY_I: u32 = 8;
1073pub const KEY_J: u32 = 9;
1074pub const KEY_K: u32 = 10;
1075pub const KEY_L: u32 = 11;
1076pub const KEY_M: u32 = 12;
1077pub const KEY_N: u32 = 13;
1078pub const KEY_O: u32 = 14;
1079pub const KEY_P: u32 = 15;
1080pub const KEY_Q: u32 = 16;
1081pub const KEY_R: u32 = 17;
1082pub const KEY_S: u32 = 18;
1083pub const KEY_T: u32 = 19;
1084pub const KEY_U: u32 = 20;
1085pub const KEY_V: u32 = 21;
1086pub const KEY_W: u32 = 22;
1087pub const KEY_X: u32 = 23;
1088pub const KEY_Y: u32 = 24;
1089pub const KEY_Z: u32 = 25;
1090pub const KEY_0: u32 = 26;
1091pub const KEY_1: u32 = 27;
1092pub const KEY_2: u32 = 28;
1093pub const KEY_3: u32 = 29;
1094pub const KEY_4: u32 = 30;
1095pub const KEY_5: u32 = 31;
1096pub const KEY_6: u32 = 32;
1097pub const KEY_7: u32 = 33;
1098pub const KEY_8: u32 = 34;
1099pub const KEY_9: u32 = 35;
1100pub const KEY_ENTER: u32 = 36;
1101pub const KEY_ESCAPE: u32 = 37;
1102pub const KEY_TAB: u32 = 38;
1103pub const KEY_BACKSPACE: u32 = 39;
1104pub const KEY_DELETE: u32 = 40;
1105pub const KEY_SPACE: u32 = 41;
1106pub const KEY_UP: u32 = 42;
1107pub const KEY_DOWN: u32 = 43;
1108pub const KEY_LEFT: u32 = 44;
1109pub const KEY_RIGHT: u32 = 45;
1110pub const KEY_HOME: u32 = 46;
1111pub const KEY_END: u32 = 47;
1112pub const KEY_PAGE_UP: u32 = 48;
1113pub const KEY_PAGE_DOWN: u32 = 49;
1114
1115pub fn ui_button(id: u32, x: f32, y: f32, w: f32, h: f32, label: &str) -> bool {
1123 unsafe { _api_ui_button(id, x, y, w, h, label.as_ptr() as u32, label.len() as u32) != 0 }
1124}
1125
1126pub fn ui_checkbox(id: u32, x: f32, y: f32, label: &str, initial: bool) -> bool {
1130 unsafe {
1131 _api_ui_checkbox(
1132 id,
1133 x,
1134 y,
1135 label.as_ptr() as u32,
1136 label.len() as u32,
1137 if initial { 1 } else { 0 },
1138 ) != 0
1139 }
1140}
1141
1142pub fn ui_slider(id: u32, x: f32, y: f32, w: f32, min: f32, max: f32, initial: f32) -> f32 {
1146 unsafe { _api_ui_slider(id, x, y, w, min, max, initial) }
1147}
1148
1149pub fn ui_text_input(id: u32, x: f32, y: f32, w: f32, initial: &str) -> String {
1153 let mut buf = [0u8; 4096];
1154 let len = unsafe {
1155 _api_ui_text_input(
1156 id,
1157 x,
1158 y,
1159 w,
1160 initial.as_ptr() as u32,
1161 initial.len() as u32,
1162 buf.as_mut_ptr() as u32,
1163 buf.len() as u32,
1164 )
1165 };
1166 String::from_utf8_lossy(&buf[..len as usize]).to_string()
1167}