1use openxr_sys::pfn::{
2 EnumerateDisplayRefreshRatesFB, EnumerateEnvironmentBlendModes, GetDisplayRefreshRateFB,
3 RequestDisplayRefreshRateFB,
4};
5use openxr_sys::{EnvironmentBlendMode, Instance, Result, Session, SystemId, ViewConfigurationType};
6
7use crate::sk::SkInfo;
8use crate::system::{Backend, BackendOpenXR, BackendXRType, Log};
9use std::ffi::OsString;
10use std::fs::File;
11use std::path::Path;
12use std::path::PathBuf;
13use std::{cell::RefCell, rc::Rc};
14
15#[derive(Debug, Clone, PartialEq, Eq)]
17pub enum PathEntry {
18 File(OsString),
19 Dir(OsString),
20}
21
22pub fn get_shaders_source_dir() -> String {
24 std::env::var("SK_RUST_SHADERS_SOURCE_DIR").unwrap_or("shaders_src".into())
25}
26
27pub fn get_shaders_sks_dir() -> String {
29 std::env::var("SK_RUST_SHADERS_SKS_DIR").unwrap_or("shaders".into())
30}
31
32pub fn get_assets_dir() -> String {
34 std::env::var("SK_RUST_ASSETS_DIR").unwrap_or("assets".into())
35}
36
37#[cfg(target_os = "android")]
44pub fn get_assets(
45 sk_info: &Option<Rc<RefCell<SkInfo>>>,
46 sub_dir: PathBuf,
47 file_extensions: &Vec<String>,
48) -> Vec<PathEntry> {
49 use std::ffi::CString;
50
51 if sk_info.is_none() {
52 Log::err("get_assets, sk_info is None");
53 return vec![];
54 }
55
56 let sk_i = sk_info.as_ref().unwrap().borrow_mut();
57 let app = sk_i.get_android_app();
58 let mut exts = vec![];
59 for extension in file_extensions {
60 let extension = extension[1..].to_string();
61 exts.push(OsString::from(extension));
62 }
63 let mut vec = vec![];
64 if let Ok(cstring) = CString::new(sub_dir.to_str().unwrap_or("Error!!!")) {
65 if let Some(asset_dir) = app.asset_manager().open_dir(cstring.as_c_str()) {
66 for entry in asset_dir {
67 if let Ok(entry_string) = entry.into_string() {
68 let path = PathBuf::from(entry_string.clone());
69
70 if exts.is_empty() {
71 if let Some(file_name) = path.file_name() {
72 vec.push(PathEntry::File(file_name.into()))
73 } else {
74 Log::err(format!("get_assets, path {:?} don't have a file_name", path));
75 }
76 } else if let Some(extension) = path.extension() {
77 if exts.contains(&extension.to_os_string()) {
78 if let Some(file_name) = path.file_name() {
79 vec.push(PathEntry::File(file_name.into()))
80 }
81 }
82 }
83 }
84 }
85 }
86 }
87 vec
88}
89
90#[cfg(not(target_os = "android"))]
127pub fn get_assets(
128 _sk_info: &Option<Rc<RefCell<SkInfo>>>,
129 sub_dir: PathBuf,
130 file_extensions: &Vec<String>,
131) -> Vec<PathEntry> {
132 use std::{env, fs::read_dir};
133
134 let sub_dir = sub_dir.to_str().unwrap_or("");
135 let mut exts = vec![];
136 for extension in file_extensions {
137 let extension = extension[1..].to_string();
138 exts.push(OsString::from(extension));
139 }
140
141 let path_text = env::current_dir().unwrap().to_owned().join(get_assets_dir());
142 let path_asset = path_text.join(sub_dir);
143 let mut vec = vec![];
144
145 if path_asset.exists() {
146 if path_asset.is_dir() {
147 match read_dir(&path_asset) {
148 Ok(read_dir) => {
149 for file in read_dir.flatten() {
150 let path = file.path();
151
152 if file.path().is_file() {
153 if exts.is_empty() {
154 vec.push(PathEntry::File(file.file_name()))
155 } else if let Some(extension) = path.extension() {
156 if exts.is_empty() || exts.contains(&extension.to_os_string()) {
157 vec.push(PathEntry::File(file.file_name()))
158 }
159 }
160 }
161 }
162 }
163 Err(err) => {
164 Log::diag(format!("Unable to read {path_asset:?}: {err}"));
165 }
166 }
167 } else {
168 Log::diag(format!("{path_asset:?} is not a dir"));
169 }
170 } else {
171 Log::diag(format!("{path_asset:?} do not exists"));
172 }
173
174 vec
175}
176
177#[cfg(target_os = "android")]
179pub fn get_internal_path(sk_info: &Option<Rc<RefCell<SkInfo>>>) -> Option<PathBuf> {
180 if sk_info.is_none() {
181 Log::err("get_internal_path, sk_info is None");
182 return None;
183 }
184
185 let sk_i = sk_info.as_ref().unwrap().borrow_mut();
186 let app = sk_i.get_android_app();
187 app.internal_data_path()
188}
189
190#[cfg(not(target_os = "android"))]
192pub fn get_internal_path(_sk_info: &Option<Rc<RefCell<SkInfo>>>) -> Option<PathBuf> {
193 None
194}
195
196#[cfg(target_os = "android")]
198pub fn get_external_path(sk_info: &Option<Rc<RefCell<SkInfo>>>) -> Option<PathBuf> {
199 if sk_info.is_none() {
200 Log::err("get_external_path, sk_info is None");
201 return None;
202 }
203
204 let sk_i = sk_info.as_ref().unwrap().borrow_mut();
205 let app = sk_i.get_android_app();
206 app.external_data_path()
207}
208
209#[cfg(not(target_os = "android"))]
211pub fn get_external_path(_sk_info: &Option<Rc<RefCell<SkInfo>>>) -> Option<PathBuf> {
212 use std::env;
213
214 let path_assets = env::current_dir().unwrap().join(get_assets_dir());
215 Some(path_assets)
216}
217
218#[cfg(target_os = "android")]
220pub fn open_asset(sk_info: &Option<Rc<RefCell<SkInfo>>>, asset_path: impl AsRef<Path>) -> Option<File> {
221 use std::ffi::CString;
222
223 if sk_info.is_none() {
224 Log::err("open_asset, sk_info is None");
225 return None;
226 }
227
228 let sk_i = sk_info.as_ref().unwrap().borrow_mut();
229 let app = sk_i.get_android_app();
230
231 if let Ok(cstring) = CString::new(asset_path.as_ref().to_str().unwrap_or("Error!!!")) {
232 if let Some(asset) = app.asset_manager().open(cstring.as_c_str()) {
233 if let Ok(o_file_desc) = asset.open_file_descriptor() {
234 Some(File::from(o_file_desc.fd))
235 } else {
236 Log::err(format!("open_asset, {:?} cannot get a new file_descriptor", asset_path.as_ref()));
237 None
238 }
239 } else {
240 Log::err(format!("open_asset, path {:?} cannot be a opened", asset_path.as_ref()));
241 None
242 }
243 } else {
244 Log::err(format!("open_asset, path {:?} cannot be a cstring", asset_path.as_ref()));
245 None
246 }
247}
248
249#[cfg(not(target_os = "android"))]
272pub fn open_asset(_sk_info: &Option<Rc<RefCell<SkInfo>>>, asset_path: impl AsRef<Path>) -> Option<File> {
273 use std::env;
274
275 let path_assets = env::current_dir().unwrap().join(get_assets_dir());
276 let path_asset = path_assets.join(asset_path);
277 File::open(path_asset).ok()
278}
279
280#[cfg(target_os = "android")]
282pub fn read_asset(sk_info: &Option<Rc<RefCell<SkInfo>>>, asset_path: impl AsRef<Path>) -> Option<Vec<u8>> {
283 use std::ffi::CString;
284
285 if sk_info.is_none() {
286 Log::err("open_asset, sk_info is None");
287 return None;
288 }
289
290 let sk_i = sk_info.as_ref().unwrap().borrow_mut();
291 let app = sk_i.get_android_app();
292
293 if let Ok(cstring) = CString::new(asset_path.as_ref().to_str().unwrap_or("Error!!!")) {
294 if let Some(mut asset) = app.asset_manager().open(cstring.as_c_str()) {
295 if let Ok(o_buffer) = asset.buffer() {
296 Some(o_buffer.to_vec())
297 } else {
298 Log::err(format!("open_asset, {:?} cannot get the buffer", asset_path.as_ref()));
299 None
300 }
301 } else {
302 Log::err(format!("open_asset, path {:?} cannot be a opened", asset_path.as_ref()));
303 None
304 }
305 } else {
306 Log::err(format!("open_asset, path {:?} cannot be a cstring", asset_path.as_ref()));
307 None
308 }
309}
310
311#[cfg(not(target_os = "android"))]
331pub fn read_asset(_sk_info: &Option<Rc<RefCell<SkInfo>>>, asset_path: impl AsRef<Path>) -> Option<Vec<u8>> {
332 use std::{env, io::Read};
333
334 let path_assets = env::current_dir().unwrap().join(get_assets_dir());
335 let path_asset = path_assets.join(&asset_path);
336 let mut fd = match File::open(path_asset).ok() {
337 Some(file) => file,
338 None => {
339 Log::err(format!("open_asset, path {:?} cannot be opened", asset_path.as_ref()));
340 return None;
341 }
342 };
343 let mut o_buffer = vec![];
344 match fd.read_to_end(&mut o_buffer) {
345 Ok(_) => Some(o_buffer),
346 Err(err) => {
347 Log::err(format!("open_asset, path {:?} cannot be read: {}", asset_path.as_ref(), err));
348 None
349 }
350 }
351}
352
353pub fn get_files(
389 _sk_info: &Option<Rc<RefCell<SkInfo>>>,
390 dir: PathBuf,
391 file_extensions: &Vec<String>,
392 show_sub_dirs: bool,
393) -> Vec<PathEntry> {
394 use std::fs::read_dir;
395 let mut exts = vec![];
396 for extension in file_extensions {
397 let extension = extension[1..].to_string();
398 exts.push(OsString::from(extension));
399 }
400 let mut vec = vec![];
401
402 if dir.exists() && dir.is_dir() {
403 if let Ok(read_dir) = read_dir(dir) {
404 for file in read_dir.flatten() {
405 let path = file.path();
406
407 if file.path().is_file() {
408 if exts.is_empty() {
409 vec.push(PathEntry::File(file.file_name()))
410 } else if let Some(extension) = path.extension() {
411 if exts.is_empty() || exts.contains(&extension.to_os_string()) {
412 vec.push(PathEntry::File(file.file_name()))
413 }
414 }
415 } else if show_sub_dirs && file.path().is_dir() {
416 vec.push(PathEntry::Dir(file.file_name()))
417 }
418 }
419 }
420 }
421 vec
422}
423
424#[cfg(target_os = "android")]
426pub fn show_soft_input_ime(sk_info: &Option<Rc<RefCell<SkInfo>>>, show: bool) -> bool {
427 if sk_info.is_none() {
428 Log::err("show_soft_input_ime, sk_info is None");
429 return false;
430 }
431
432 let sk_i = sk_info.as_ref().unwrap().borrow_mut();
433 let app = sk_i.get_android_app();
434 if show {
435 app.show_soft_input(false);
436 } else {
437 app.hide_soft_input(false);
438 }
439 true
440}
441#[cfg(not(target_os = "android"))]
443pub fn show_soft_input_ime(_sk_info: &Option<Rc<RefCell<SkInfo>>>, _show: bool) -> bool {
444 false
445}
446
447#[cfg(target_os = "android")]
449pub fn show_soft_input(show: bool) -> bool {
450 use jni::objects::JValue;
451
452 let ctx = ndk_context::android_context();
453 let vm = match unsafe { jni::JavaVM::from_raw(ctx.vm() as _) } {
454 Ok(value) => value,
455 Err(e) => {
456 Log::err(format!("virtual_kbd : no vm !! : {:?}", e));
457 return false;
458 }
459 };
460 let activity = unsafe { jni::objects::JObject::from_raw(ctx.context() as _) };
461 let mut env = match vm.attach_current_thread() {
462 Ok(value) => value,
463 Err(e) => {
464 Log::err(format!("virtual_kbd : no env !! : {:?}", e));
465 return false;
466 }
467 };
468
469 let class_ctxt = match env.find_class("android/content/Context") {
470 Ok(value) => value,
471 Err(e) => {
472 Log::err(format!("virtual_kbd : no class_ctxt !! : {:?}", e));
473 return false;
474 }
475 };
476 let ims = match env.get_static_field(class_ctxt, "INPUT_METHOD_SERVICE", "Ljava/lang/String;") {
477 Ok(value) => value,
478 Err(e) => {
479 Log::err(format!("virtual_kbd : no ims !! : {:?}", e));
480 return false;
481 }
482 };
483
484 let im_manager = match env
485 .call_method(&activity, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;", &[ims.borrow()])
486 .unwrap()
487 .l()
488 {
489 Ok(value) => value,
490 Err(e) => {
491 Log::err(format!("virtual_kbd : no im_manager !! : {:?}", e));
492 return false;
493 }
494 };
495
496 let jni_window = match env.call_method(&activity, "getWindow", "()Landroid/view/Window;", &[]).unwrap().l() {
497 Ok(value) => value,
498 Err(e) => {
499 Log::err(format!("virtual_kbd : no jni_window !! : {:?}", e));
500 return false;
501 }
502 };
503
504 let view = match env.call_method(jni_window, "getDecorView", "()Landroid/view/View;", &[]).unwrap().l() {
505 Ok(value) => value,
506 Err(e) => {
507 Log::err(format!("virtual_kbd : no view !! : {:?}", e));
508 return false;
509 }
510 };
511
512 if show {
513 let result = env
514 .call_method(im_manager, "showSoftInput", "(Landroid/view/View;I)Z", &[JValue::Object(&view), 0i32.into()])
515 .unwrap()
516 .z()
517 .unwrap();
518 result
519 } else {
520 let window_token = env.call_method(view, "getWindowToken", "()Landroid/os/IBinder;", &[]).unwrap().l().unwrap();
521 let jvalue_window_token = jni::objects::JValueGen::Object(&window_token);
522
523 let result = env
524 .call_method(
525 im_manager,
526 "hideSoftInputFromWindow",
527 "(Landroid/os/IBinder;I)Z",
528 &[jvalue_window_token, 0i32.into()],
529 )
530 .unwrap()
531 .z()
532 .unwrap();
533 result
534 }
535}
536
537#[cfg(not(target_os = "android"))]
539pub fn show_soft_input(_show: bool) -> bool {
540 false
541}
542
543pub const USUAL_FPS_SUSPECTS: [i32; 12] = [30, 60, 72, 80, 90, 100, 110, 120, 144, 165, 240, 360];
544
545pub fn get_all_display_refresh_rates(with_log: bool) -> Vec<f32> {
577 let mut array = [0.0; 40];
578 let mut count = 5u32;
579 if BackendOpenXR::ext_enabled("XR_FB_display_refresh_rate") {
580 if let Some(rate_display) =
581 BackendOpenXR::get_function::<EnumerateDisplayRefreshRatesFB>("xrEnumerateDisplayRefreshRatesFB")
582 {
583 match unsafe {
584 rate_display(Session::from_raw(BackendOpenXR::session()), 0, &mut count, array.as_mut_ptr())
585 } {
586 Result::SUCCESS => {
587 if count > 40 {
588 count = 40
589 }
590 match unsafe {
591 rate_display(Session::from_raw(BackendOpenXR::session()), count, &mut count, array.as_mut_ptr())
592 } {
593 Result::SUCCESS => {
594 if with_log {
595 Log::info(format!("There are {count} display rate:"));
596 for (i, iter) in array.iter().enumerate() {
597 if i >= count as usize {
598 break;
599 }
600 Log::info(format!(" {iter:?} "));
601 }
602 }
603 }
604 otherwise => {
605 Log::err(format!("xrEnumerateDisplayRefreshRatesFB failed: {otherwise}"));
606 }
607 }
608 }
609 otherwise => {
610 Log::err(format!("xrEnumerateDisplayRefreshRatesFB failed: {otherwise}"));
611 }
612 }
613 } else {
614 Log::err("xrEnumerateDisplayRefreshRatesFB binding function error !")
615 }
616 }
617 array[0..(count as usize)].into()
618}
619
620pub fn get_display_refresh_rates(fps_to_get: &[i32], with_log: bool) -> Vec<f32> {
626 let default_refresh_rate = get_display_refresh_rate();
627 let mut available_rates = vec![];
628 for rate in fps_to_get {
629 if set_display_refresh_rate(*rate as f32, false) {
630 available_rates.push(*rate as f32);
631 }
632 }
633 if let Some(rate) = default_refresh_rate {
634 set_display_refresh_rate(rate, with_log);
635 }
636 if with_log {
637 Log::info(format!("There are {} display rate from the given selection:", available_rates.len()));
638 for iter in &available_rates {
639 Log::info(format!(" {iter:?} "));
640 }
641 }
642
643 available_rates
644}
645
646pub fn get_display_refresh_rate() -> Option<f32> {
651 if BackendOpenXR::ext_enabled("XR_FB_display_refresh_rate") {
652 if let Some(get_default_rate) =
653 BackendOpenXR::get_function::<GetDisplayRefreshRateFB>("xrGetDisplayRefreshRateFB")
654 {
655 let mut default_rate = 0.0;
656 match unsafe { get_default_rate(Session::from_raw(BackendOpenXR::session()), &mut default_rate) } {
657 Result::SUCCESS => Some(default_rate),
658 otherwise => {
659 Log::err(format!("xrGetDisplayRefreshRateFB failed: {otherwise}"));
660 None
661 }
662 }
663 } else {
664 Log::err("xrRequestDisplayRefreshRateFB binding function error !");
665 None
666 }
667 } else {
668 None
669 }
670}
671
672pub fn set_display_refresh_rate(rate: f32, with_log: bool) -> bool {
680 if BackendOpenXR::ext_enabled("XR_FB_display_refresh_rate") {
681 if let Some(set_new_rate) =
683 BackendOpenXR::get_function::<RequestDisplayRefreshRateFB>("xrRequestDisplayRefreshRateFB")
684 {
685 match unsafe { set_new_rate(Session::from_raw(BackendOpenXR::session()), rate) } {
686 Result::SUCCESS => true,
687 otherwise => {
688 if with_log {
689 Log::err(format!("xrRequestDisplayRefreshRateFB failed: {otherwise}"));
690 }
691 false
692 }
693 }
694 } else {
695 Log::err("xrRequestDisplayRefreshRateFB binding function error !");
696 false
697 }
698 } else {
699 false
700 }
701}
702
703pub fn get_env_blend_modes(with_log: bool) -> Vec<EnvironmentBlendMode> {
741 let mut count = 0u32;
743 let mut modes = [EnvironmentBlendMode::OPAQUE; 20];
744 if Backend::xr_type() != BackendXRType::OpenXR {
745 return vec![];
746 }
747 if let Some(get_modes) =
748 BackendOpenXR::get_function::<EnumerateEnvironmentBlendModes>("xrEnumerateEnvironmentBlendModes")
749 {
750 match unsafe {
751 get_modes(
752 Instance::from_raw(BackendOpenXR::instance()),
753 SystemId::from_raw(BackendOpenXR::system_id()),
754 ViewConfigurationType::PRIMARY_STEREO,
755 0,
756 &mut count,
757 modes.as_mut_ptr(),
758 )
759 } {
760 Result::SUCCESS => {
761 if with_log {
762 if count > 20 {
763 count = 20
764 }
765 match unsafe {
766 get_modes(
767 Instance::from_raw(BackendOpenXR::instance()),
768 SystemId::from_raw(BackendOpenXR::system_id()),
769 ViewConfigurationType::PRIMARY_STEREO,
770 count,
771 &mut count,
772 modes.as_mut_ptr(),
773 )
774 } {
775 Result::SUCCESS => {
776 if with_log {
777 Log::info(format!("There are {count} env blend modes:"));
778 for (i, iter) in modes.iter().enumerate() {
779 if i >= count as usize {
780 break;
781 }
782 Log::info(format!(" {iter:?} "));
783 }
784 }
785 }
786 otherwise => {
787 if with_log {
788 Log::err(format!("xrEnumerateEnvironmentBlendModes failed: {otherwise}"));
789 }
790 }
791 }
792 }
793 }
794 otherwise => {
795 if with_log {
796 Log::err(format!("xrEnumerateEnvironmentBlendModes failed: {otherwise}"));
797 }
798 }
799 }
800 } else {
801 Log::err("xrEnumerateEnvironmentBlendModes binding function error !");
802 }
803 modes[0..(count as usize)].into()
804}