1use openxr_sys::pfn::EnumerateEnvironmentBlendModes;
2use openxr_sys::{EnvironmentBlendMode, Instance, Result, SystemId, ViewConfigurationType};
3use std::ffi::OsString;
4use std::fs::File;
5use std::path::Path;
6use std::path::PathBuf;
7use std::{cell::RefCell, rc::Rc};
8
9use crate::sk::SkInfo;
10use crate::system::{Backend, BackendOpenXR, BackendXRType, Log};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum PathEntry {
15 File(OsString),
16 Dir(OsString),
17}
18
19pub fn get_shaders_source_dir() -> String {
21 std::env::var("SK_RUST_SHADERS_SOURCE_DIR").unwrap_or("shaders_src".into())
22}
23
24pub fn get_shaders_sks_dir() -> String {
26 std::env::var("SK_RUST_SHADERS_SKS_DIR").unwrap_or("shaders".into())
27}
28
29pub fn get_assets_dir() -> String {
31 std::env::var("SK_RUST_ASSETS_DIR").unwrap_or("assets".into())
32}
33
34#[cfg(target_os = "android")]
41pub fn get_assets(
42 sk_info: &Option<Rc<RefCell<SkInfo>>>,
43 sub_dir: PathBuf,
44 file_extensions: &Vec<String>,
45) -> Vec<PathEntry> {
46 use std::ffi::CString;
47
48 if sk_info.is_none() {
49 Log::err("get_assets, sk_info is None");
50 return vec![];
51 }
52
53 let sk_i = sk_info.as_ref().unwrap().borrow_mut();
54 let app = sk_i.get_android_app();
55 let mut exts = vec![];
56 for extension in file_extensions {
57 let extension = extension[1..].to_string();
58 exts.push(OsString::from(extension));
59 }
60 let mut vec = vec![];
61 if let Ok(cstring) = CString::new(sub_dir.to_str().unwrap_or("Error!!!")) {
62 if let Some(asset_dir) = app.asset_manager().open_dir(cstring.as_c_str()) {
63 for entry in asset_dir {
64 if let Ok(entry_string) = entry.into_string() {
65 let path = PathBuf::from(entry_string.clone());
66
67 if exts.is_empty() {
68 if let Some(file_name) = path.file_name() {
69 vec.push(PathEntry::File(file_name.into()))
70 } else {
71 Log::err(format!("get_assets, path {:?} don't have a file_name", path));
72 }
73 } else if let Some(extension) = path.extension() {
74 if exts.contains(&extension.to_os_string()) {
75 if let Some(file_name) = path.file_name() {
76 vec.push(PathEntry::File(file_name.into()))
77 }
78 }
79 }
80 }
81 }
82 }
83 }
84 vec
85}
86
87#[cfg(not(target_os = "android"))]
124pub fn get_assets(
125 _sk_info: &Option<Rc<RefCell<SkInfo>>>,
126 sub_dir: PathBuf,
127 file_extensions: &Vec<String>,
128) -> Vec<PathEntry> {
129 use std::{env, fs::read_dir};
130
131 let sub_dir = sub_dir.to_str().unwrap_or("");
132 let mut exts = vec![];
133 for extension in file_extensions {
134 let extension = extension[1..].to_string();
135 exts.push(OsString::from(extension));
136 }
137
138 let path_text = env::current_dir().unwrap().to_owned().join(get_assets_dir());
139 let path_asset = path_text.join(sub_dir);
140 let mut vec = vec![];
141
142 if path_asset.exists() {
143 if path_asset.is_dir() {
144 match read_dir(&path_asset) {
145 Ok(read_dir) => {
146 for file in read_dir.flatten() {
147 let path = file.path();
148
149 if file.path().is_file() {
150 if exts.is_empty() {
151 vec.push(PathEntry::File(file.file_name()))
152 } else if let Some(extension) = path.extension()
153 && (exts.is_empty() || exts.contains(&extension.to_os_string()))
154 {
155 vec.push(PathEntry::File(file.file_name()))
156 }
157 }
158 }
159 }
160 Err(err) => {
161 Log::diag(format!("Unable to read {path_asset:?}: {err}"));
162 }
163 }
164 } else {
165 Log::diag(format!("{path_asset:?} is not a dir"));
166 }
167 } else {
168 Log::diag(format!("{path_asset:?} do not exists"));
169 }
170
171 vec
172}
173
174#[cfg(target_os = "android")]
176pub fn get_internal_path(sk_info: &Option<Rc<RefCell<SkInfo>>>) -> Option<PathBuf> {
177 if sk_info.is_none() {
178 Log::err("get_internal_path, sk_info is None");
179 return None;
180 }
181
182 let sk_i = sk_info.as_ref().unwrap().borrow_mut();
183 let app = sk_i.get_android_app();
184 app.internal_data_path()
185}
186
187#[cfg(not(target_os = "android"))]
189pub fn get_internal_path(_sk_info: &Option<Rc<RefCell<SkInfo>>>) -> Option<PathBuf> {
190 None
191}
192
193#[cfg(target_os = "android")]
195pub fn get_external_path(sk_info: &Option<Rc<RefCell<SkInfo>>>) -> Option<PathBuf> {
196 if sk_info.is_none() {
197 Log::err("get_external_path, sk_info is None");
198 return None;
199 }
200
201 let sk_i = sk_info.as_ref().unwrap().borrow_mut();
202 let app = sk_i.get_android_app();
203 app.external_data_path()
204}
205
206#[cfg(not(target_os = "android"))]
208pub fn get_external_path(_sk_info: &Option<Rc<RefCell<SkInfo>>>) -> Option<PathBuf> {
209 use std::env;
210
211 let path_assets = env::current_dir().unwrap().join(get_assets_dir());
212 Some(path_assets)
213}
214
215#[cfg(target_os = "android")]
217pub fn open_asset(sk_info: &Option<Rc<RefCell<SkInfo>>>, asset_path: impl AsRef<Path>) -> Option<File> {
218 use std::ffi::CString;
219
220 if sk_info.is_none() {
221 Log::err("open_asset, sk_info is None");
222 return None;
223 }
224
225 let sk_i = sk_info.as_ref().unwrap().borrow_mut();
226 let app = sk_i.get_android_app();
227
228 if let Ok(cstring) = CString::new(asset_path.as_ref().to_str().unwrap_or("Error!!!")) {
229 if let Some(asset) = app.asset_manager().open(cstring.as_c_str()) {
230 if let Ok(o_file_desc) = asset.open_file_descriptor() {
231 Some(File::from(o_file_desc.fd))
232 } else {
233 Log::err(format!("open_asset, {:?} cannot get a new file_descriptor", asset_path.as_ref()));
234 None
235 }
236 } else {
237 Log::err(format!("open_asset, path {:?} cannot be a opened", asset_path.as_ref()));
238 None
239 }
240 } else {
241 Log::err(format!("open_asset, path {:?} cannot be a cstring", asset_path.as_ref()));
242 None
243 }
244}
245
246#[cfg(not(target_os = "android"))]
269pub fn open_asset(_sk_info: &Option<Rc<RefCell<SkInfo>>>, asset_path: impl AsRef<Path>) -> Option<File> {
270 use std::env;
271
272 let path_assets = env::current_dir().unwrap().join(get_assets_dir());
273 let path_asset = path_assets.join(asset_path);
274 File::open(path_asset).ok()
275}
276
277#[cfg(target_os = "android")]
279pub fn read_asset(sk_info: &Option<Rc<RefCell<SkInfo>>>, asset_path: impl AsRef<Path>) -> Option<Vec<u8>> {
280 use std::ffi::CString;
281
282 if sk_info.is_none() {
283 Log::err("open_asset, sk_info is None");
284 return None;
285 }
286
287 let sk_i = sk_info.as_ref().unwrap().borrow_mut();
288 let app = sk_i.get_android_app();
289
290 if let Ok(cstring) = CString::new(asset_path.as_ref().to_str().unwrap_or("Error!!!")) {
291 if let Some(mut asset) = app.asset_manager().open(cstring.as_c_str()) {
292 if let Ok(o_buffer) = asset.buffer() {
293 Some(o_buffer.to_vec())
294 } else {
295 Log::err(format!("open_asset, {:?} cannot get the buffer", asset_path.as_ref()));
296 None
297 }
298 } else {
299 Log::err(format!("open_asset, path {:?} cannot be a opened", asset_path.as_ref()));
300 None
301 }
302 } else {
303 Log::err(format!("open_asset, path {:?} cannot be a cstring", asset_path.as_ref()));
304 None
305 }
306}
307
308#[cfg(not(target_os = "android"))]
328pub fn read_asset(_sk_info: &Option<Rc<RefCell<SkInfo>>>, asset_path: impl AsRef<Path>) -> Option<Vec<u8>> {
329 use std::{env, io::Read};
330
331 let path_assets = env::current_dir().unwrap().join(get_assets_dir());
332 let path_asset = path_assets.join(&asset_path);
333 let mut fd = match File::open(path_asset).ok() {
334 Some(file) => file,
335 None => {
336 Log::err(format!("open_asset, path {:?} cannot be opened", asset_path.as_ref()));
337 return None;
338 }
339 };
340 let mut o_buffer = vec![];
341 match fd.read_to_end(&mut o_buffer) {
342 Ok(_) => Some(o_buffer),
343 Err(err) => {
344 Log::err(format!("open_asset, path {:?} cannot be read: {}", asset_path.as_ref(), err));
345 None
346 }
347 }
348}
349
350pub fn get_files(
386 _sk_info: &Option<Rc<RefCell<SkInfo>>>,
387 dir: PathBuf,
388 file_extensions: &Vec<String>,
389 show_sub_dirs: bool,
390) -> Vec<PathEntry> {
391 use std::fs::read_dir;
392 let mut exts = vec![];
393 for extension in file_extensions {
394 let extension = extension[1..].to_string();
395 exts.push(OsString::from(extension));
396 }
397 let mut vec = vec![];
398
399 if dir.exists()
400 && dir.is_dir()
401 && let Ok(read_dir) = read_dir(dir)
402 {
403 for file in read_dir.flatten() {
404 let path = file.path();
405
406 if file.path().is_file() {
407 if exts.is_empty() {
408 vec.push(PathEntry::File(file.file_name()))
409 } else if let Some(extension) = path.extension()
410 && (exts.is_empty() || exts.contains(&extension.to_os_string()))
411 {
412 vec.push(PathEntry::File(file.file_name()))
413 }
414 } else if show_sub_dirs && file.path().is_dir() {
415 vec.push(PathEntry::Dir(file.file_name()))
416 }
417 }
418 }
419 vec
420}
421
422#[cfg(target_os = "android")]
424pub fn show_soft_input_ime(sk_info: &Option<Rc<RefCell<SkInfo>>>, show: bool) -> bool {
425 if sk_info.is_none() {
426 Log::err("show_soft_input_ime, sk_info is None");
427 return false;
428 }
429
430 let sk_i = sk_info.as_ref().unwrap().borrow_mut();
431 let app = sk_i.get_android_app();
432 if show {
433 app.show_soft_input(false);
434 } else {
435 app.hide_soft_input(false);
436 }
437 true
438}
439#[cfg(not(target_os = "android"))]
441pub fn show_soft_input_ime(_sk_info: &Option<Rc<RefCell<SkInfo>>>, _show: bool) -> bool {
442 false
443}
444
445#[cfg(target_os = "android")]
447pub fn show_soft_input(show: bool) -> bool {
448 use jni::objects::JValue;
449
450 let ctx = ndk_context::android_context();
451 let vm = match unsafe { jni::JavaVM::from_raw(ctx.vm() as _) } {
452 Ok(value) => value,
453 Err(e) => {
454 Log::err(format!("virtual_kbd : no vm !! : {:?}", e));
455 return false;
456 }
457 };
458 let activity = unsafe { jni::objects::JObject::from_raw(ctx.context() as _) };
459 let mut env = match vm.attach_current_thread() {
460 Ok(value) => value,
461 Err(e) => {
462 Log::err(format!("virtual_kbd : no env !! : {:?}", e));
463 return false;
464 }
465 };
466
467 let class_ctxt = match env.find_class("android/content/Context") {
468 Ok(value) => value,
469 Err(e) => {
470 Log::err(format!("virtual_kbd : no class_ctxt !! : {:?}", e));
471 return false;
472 }
473 };
474 let ims = match env.get_static_field(class_ctxt, "INPUT_METHOD_SERVICE", "Ljava/lang/String;") {
475 Ok(value) => value,
476 Err(e) => {
477 Log::err(format!("virtual_kbd : no ims !! : {:?}", e));
478 return false;
479 }
480 };
481
482 let im_manager = match env
483 .call_method(&activity, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;", &[ims.borrow()])
484 .unwrap()
485 .l()
486 {
487 Ok(value) => value,
488 Err(e) => {
489 Log::err(format!("virtual_kbd : no im_manager !! : {:?}", e));
490 return false;
491 }
492 };
493
494 let jni_window = match env.call_method(&activity, "getWindow", "()Landroid/view/Window;", &[]).unwrap().l() {
495 Ok(value) => value,
496 Err(e) => {
497 Log::err(format!("virtual_kbd : no jni_window !! : {:?}", e));
498 return false;
499 }
500 };
501
502 let view = match env.call_method(jni_window, "getDecorView", "()Landroid/view/View;", &[]).unwrap().l() {
503 Ok(value) => value,
504 Err(e) => {
505 Log::err(format!("virtual_kbd : no view !! : {:?}", e));
506 return false;
507 }
508 };
509
510 if show {
511 let result = env
512 .call_method(im_manager, "showSoftInput", "(Landroid/view/View;I)Z", &[JValue::Object(&view), 0i32.into()])
513 .unwrap()
514 .z()
515 .unwrap();
516 result
517 } else {
518 let window_token = env.call_method(view, "getWindowToken", "()Landroid/os/IBinder;", &[]).unwrap().l().unwrap();
519 let jvalue_window_token = jni::objects::JValueGen::Object(&window_token);
520
521 let result = env
522 .call_method(
523 im_manager,
524 "hideSoftInputFromWindow",
525 "(Landroid/os/IBinder;I)Z",
526 &[jvalue_window_token, 0i32.into()],
527 )
528 .unwrap()
529 .z()
530 .unwrap();
531 result
532 }
533}
534
535#[cfg(not(target_os = "android"))]
537pub fn show_soft_input(_show: bool) -> bool {
538 false
539}
540
541#[cfg(target_os = "android")]
543pub fn launch_browser_android(url: &str) -> bool {
544 use jni::objects::{JObject, JValue};
545
546 Log::diag(format!("launch_browser_android: Attempting to open URL: {}", url));
547
548 let ctx = ndk_context::android_context();
550 let vm = match unsafe { jni::JavaVM::from_raw(ctx.vm() as _) } {
551 Ok(value) => value,
552 Err(e) => {
553 Log::err(format!("launch_browser_android: no vm !! : {:?}", e));
554 return false;
555 }
556 };
557
558 let activity = unsafe { jni::objects::JObject::from_raw(ctx.context() as _) };
559 let mut env = match vm.attach_current_thread() {
560 Ok(value) => value,
561 Err(e) => {
562 Log::err(format!("launch_browser_android: no env !! : {:?}", e));
563 return false;
564 }
565 };
566
567 let intent_class = match env.find_class("android/content/Intent") {
569 Ok(value) => value,
570 Err(e) => {
571 Log::err(format!("launch_browser_android: no intent_class !! : {:?}", e));
572 return false;
573 }
574 };
575 let action_view = match env.get_static_field(&intent_class, "ACTION_VIEW", "Ljava/lang/String;") {
576 Ok(value) => value,
577 Err(e) => {
578 Log::err(format!("launch_browser_android: no action_view !! : {:?}", e));
579 return false;
580 }
581 };
582
583 let uri_class = match env.find_class("android/net/Uri") {
585 Ok(value) => value,
586 Err(e) => {
587 Log::err(format!("launch_browser_android: no uri_class !! : {:?}", e));
588 return false;
589 }
590 };
591 let url_string = match env.new_string(url) {
592 Ok(value) => value,
593 Err(e) => {
594 Log::err(format!("launch_browser_android: no url_string !! : {:?}", e));
595 return false;
596 }
597 };
598 let uri = match env
599 .call_static_method(
600 &uri_class,
601 "parse",
602 "(Ljava/lang/String;)Landroid/net/Uri;",
603 &[JValue::Object(&JObject::from(url_string))],
604 )
605 .unwrap()
606 .l()
607 {
608 Ok(value) => value,
609 Err(e) => {
610 Log::err(format!("launch_browser_android: no uri !! : {:?}", e));
611 return false;
612 }
613 };
614
615 let intent = match env.alloc_object(&intent_class) {
617 Ok(value) => value,
618 Err(e) => {
619 Log::err(format!("launch_browser_android: no intent !! : {:?}", e));
620 return false;
621 }
622 };
623 if let Err(e) = env.call_method(
624 &intent,
625 "<init>",
626 "(Ljava/lang/String;Landroid/net/Uri;)V",
627 &[action_view.borrow(), JValue::Object(&uri)],
628 ) {
629 Log::err(format!("launch_browser_android: intent init failed !! : {:?}", e));
630 return false;
631 }
632
633 match env.call_method(&activity, "startActivity", "(Landroid/content/Intent;)V", &[JValue::Object(&intent)]) {
635 Ok(_) => {
636 if env.exception_check().unwrap_or(false) {
638 let _ = env.exception_clear();
639 Log::err("launch_browser_android: Activity exception occurred (cleared)");
640 return false;
641 }
642 true
643 }
644 Err(e) => {
645 Log::err(format!("launch_browser_android: startActivity failed: {} | URL: {}", e, url));
646 if env.exception_check().unwrap_or(false) {
648 let _ = env.exception_clear();
649 }
650 false
651 }
652 }
653}
654
655#[cfg(not(target_os = "android"))]
657pub fn launch_browser_android(_url: &str) -> bool {
658 false
659}
660
661#[derive(Debug, Clone, PartialEq)]
663pub enum SystemAction {
664 Browser { url: String },
667 Store { app_id: Option<String> },
670 Settings { setting: Option<String> },
685 FileManager { path: Option<String> },
688 BugReport,
691}
692
693#[cfg(target_os = "android")]
774pub fn system_deep_link(action: SystemAction) -> bool {
775 use crate::system::Log;
776 use jni::objects::JValue;
777
778 let ctx = ndk_context::android_context();
780 let vm = match unsafe { jni::JavaVM::from_raw(ctx.vm() as _) } {
781 Ok(value) => value,
782 Err(e) => {
783 Log::err(format!("system_deep_link: Failed to get VM: {:?}", e));
784 return false;
785 }
786 };
787
788 let activity = unsafe { jni::objects::JObject::from_raw(ctx.context() as _) };
789 let mut env = match vm.attach_current_thread() {
790 Ok(env) => env,
791 Err(e) => {
792 Log::err(format!("system_deep_link: Failed to attach to JVM thread: {:?}", e));
793 return false;
794 }
795 };
796
797 let (intent_data, uri_value, display_data) = match &action {
799 SystemAction::Browser { url } => {
800 ("systemux://browser", url.clone(), format!("Opening browser with URL: {}", url))
801 }
802 SystemAction::Store { app_id } => {
803 let uri = match app_id {
804 Some(id) => format!("/item/{}", id),
805 None => String::new(),
806 };
807 (
808 "systemux://store",
809 uri,
810 format!(
811 "Opening store{}",
812 if app_id.is_some() { format!(" for app: {}", app_id.as_ref().unwrap()) } else { String::new() }
813 ),
814 )
815 }
816 SystemAction::Settings { setting } => {
817 let uri = setting.as_deref().unwrap_or("");
818 (
819 "systemux://settings",
820 uri.to_string(),
821 format!("Opening settings{}", if !uri.is_empty() { format!(": {}", uri) } else { String::new() }),
822 )
823 }
824 SystemAction::FileManager { path } => {
825 let uri = path.as_deref().unwrap_or("");
826 (
827 "systemux://file-manager",
828 uri.to_string(),
829 format!("Opening file manager{}", if !uri.is_empty() { format!(": {}", uri) } else { String::new() }),
830 )
831 }
832 SystemAction::BugReport => ("systemux://bug_report", String::new(), "Opening bug report".to_string()),
833 };
834
835 Log::info(format!("system_deep_link: {}", display_data));
836 Log::diag(format!(
837 "system_deep_link: Attempting to launch VR Shell with intent_data='{}', uri='{}'",
838 intent_data, uri_value
839 ));
840
841 let package_manager =
843 match env.call_method(&activity, "getPackageManager", "()Landroid/content/pm/PackageManager;", &[]) {
844 Ok(pm) => match pm.l() {
845 Ok(pm_obj) => pm_obj,
846 Err(e) => {
847 Log::err(format!("system_deep_link: Failed to extract PackageManager object: {}", e));
848 return false;
849 }
850 },
851 Err(e) => {
852 Log::err(format!("system_deep_link: Failed to get PackageManager: {}", e));
853 return false;
854 }
855 };
856
857 let package_name = match env.new_string("com.oculus.vrshell") {
859 Ok(s) => s,
860 Err(e) => {
861 Log::err(format!("system_deep_link: Failed to create package name string: {}", e));
862 return false;
863 }
864 };
865
866 let intent = match env.call_method(
867 &package_manager,
868 "getLaunchIntentForPackage",
869 "(Ljava/lang/String;)Landroid/content/Intent;",
870 &[JValue::Object(&package_name.into())],
871 ) {
872 Ok(intent_result) => match intent_result.l() {
873 Ok(intent_obj) if !intent_obj.is_null() => intent_obj,
874 Ok(_) => {
875 Log::err("system_deep_link: getLaunchIntentForPackage returned null");
876 return false;
877 }
878 Err(e) => {
879 Log::err(format!("system_deep_link: Failed to extract Intent object: {}", e));
880 return false;
881 }
882 },
883 Err(e) => {
884 Log::err(format!("system_deep_link: Failed to get launch intent: {}", e));
885 return false;
886 }
887 };
888
889 let intent_data_key = match env.new_string("intent_data") {
891 Ok(s) => s,
892 Err(e) => {
893 Log::err(format!("system_deep_link: Failed to create intent_data key: {}", e));
894 return false;
895 }
896 };
897
898 let intent_data_value = match env.new_string(intent_data) {
899 Ok(s) => s,
900 Err(e) => {
901 Log::err(format!("system_deep_link: Failed to create intent_data value: {}", e));
902 return false;
903 }
904 };
905
906 if let Err(e) = env.call_method(
907 &intent,
908 "putExtra",
909 "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;",
910 &[JValue::Object(&intent_data_key.into()), JValue::Object(&intent_data_value.into())],
911 ) {
912 Log::err(format!("system_deep_link: Failed to add intent_data extra: {}", e));
913 return false;
914 }
915
916 if !uri_value.is_empty() {
918 let uri_key = match env.new_string("uri") {
919 Ok(s) => s,
920 Err(e) => {
921 Log::err(format!("system_deep_link: Failed to create uri key: {}", e));
922 return false;
923 }
924 };
925
926 let uri_value_string = match env.new_string(&uri_value) {
927 Ok(s) => s,
928 Err(e) => {
929 Log::err(format!("system_deep_link: Failed to create uri value: {}", e));
930 return false;
931 }
932 };
933
934 if let Err(e) = env.call_method(
935 &intent,
936 "putExtra",
937 "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;",
938 &[JValue::Object(&uri_key.into()), JValue::Object(&uri_value_string.into())],
939 ) {
940 Log::err(format!("system_deep_link: Failed to add uri extra: {}", e));
941 return false;
942 }
943 }
944
945 match env.call_method(&activity, "startActivity", "(Landroid/content/Intent;)V", &[JValue::Object(&intent)]) {
947 Ok(_) => {
948 if env.exception_check().unwrap_or(false) {
950 let _ = env.exception_clear();
951 Log::err("system_deep_link: Activity exception occurred (cleared)");
952 return false;
953 }
954 Log::info(format!("system_deep_link: Successfully executed: {}", display_data));
955 true
956 }
957 Err(e) => {
958 Log::err(format!(
959 "system_deep_link: Failed to start activity: {} | Action: {:?} | Intent data: {} | URI: {}",
960 e, action, intent_data, uri_value
961 ));
962 if env.exception_check().unwrap_or(false) {
964 let _ = env.exception_clear();
965 }
966 false
967 }
968 }
969}
970
971#[cfg(not(target_os = "android"))]
972pub fn system_deep_link(_action: SystemAction) -> bool {
973 use crate::system::Log;
974 Log::warn("system_deep_link: Not supported on non-Android platforms");
975 false
976}
977
978pub fn get_env_blend_modes(with_log: bool) -> Vec<EnvironmentBlendMode> {
1016 let mut count = 0u32;
1018 let mut modes = [EnvironmentBlendMode::OPAQUE; 20];
1019 if Backend::xr_type() != BackendXRType::OpenXR {
1020 return vec![];
1021 }
1022 if let Some(get_modes) =
1023 BackendOpenXR::get_function::<EnumerateEnvironmentBlendModes>("xrEnumerateEnvironmentBlendModes")
1024 {
1025 match unsafe {
1026 get_modes(
1027 Instance::from_raw(BackendOpenXR::instance()),
1028 SystemId::from_raw(BackendOpenXR::system_id()),
1029 ViewConfigurationType::PRIMARY_STEREO,
1030 0,
1031 &mut count,
1032 modes.as_mut_ptr(),
1033 )
1034 } {
1035 Result::SUCCESS => {
1036 if with_log {
1037 if count > 20 {
1038 count = 20
1039 }
1040 match unsafe {
1041 get_modes(
1042 Instance::from_raw(BackendOpenXR::instance()),
1043 SystemId::from_raw(BackendOpenXR::system_id()),
1044 ViewConfigurationType::PRIMARY_STEREO,
1045 count,
1046 &mut count,
1047 modes.as_mut_ptr(),
1048 )
1049 } {
1050 Result::SUCCESS => {
1051 if with_log {
1052 Log::info(format!("✅ There are {count} env blend modes:"));
1053 for (i, iter) in modes.iter().enumerate() {
1054 if i >= count as usize {
1055 break;
1056 }
1057 Log::info(format!(" {iter:?} "));
1058 }
1059 }
1060 }
1061 otherwise => {
1062 if with_log {
1063 Log::err(format!("❌ xrEnumerateEnvironmentBlendModes failed: {otherwise}"));
1064 }
1065 }
1066 }
1067 }
1068 }
1069 otherwise => {
1070 if with_log {
1071 Log::err(format!("❌ xrEnumerateEnvironmentBlendModes failed: {otherwise}"));
1072 }
1073 }
1074 }
1075 } else {
1076 Log::err("❌ xrEnumerateEnvironmentBlendModes binding function error !");
1077 }
1078 modes[0..(count as usize)].into()
1079}