stereokit_rust/tools/
xr_meta_virtual_keyboard.rs

1//! TODO: XR_META_virtual_keyboard extension implementation
2//!
3//! This module provides access to the OpenXR XR_META_virtual_keyboard extension,
4//! which allows applications to display and interact with virtual keyboards in VR/AR.
5//!
6//! # OpenXR Specification
7//! This implementation follows the XR_META_virtual_keyboard extension specification,
8//! providing access to Meta-specific virtual keyboard capabilities.
9//! <https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XR_META_virtual_keyboard>
10
11use openxr_sys::{
12    Bool32, FALSE, Instance, MAX_SYSTEM_NAME_SIZE, Posef, Result as XrResult, Session, Space, StructureType,
13    SystemGraphicsProperties, SystemId, SystemProperties, SystemTrackingProperties,
14    SystemVirtualKeyboardPropertiesMETA, VirtualKeyboardCreateInfoMETA, VirtualKeyboardLocationTypeMETA,
15    VirtualKeyboardMETA, VirtualKeyboardSpaceCreateInfoMETA,
16    pfn::{
17        CreateVirtualKeyboardMETA, CreateVirtualKeyboardSpaceMETA, DestroyVirtualKeyboardMETA, GetSystemProperties,
18        GetVirtualKeyboardDirtyTexturesMETA, GetVirtualKeyboardModelAnimationStatesMETA, GetVirtualKeyboardScaleMETA,
19        GetVirtualKeyboardTextureDataMETA, SendVirtualKeyboardInputMETA, SetVirtualKeyboardModelVisibilityMETA,
20        SuggestVirtualKeyboardLocationMETA,
21    },
22};
23
24use crate::{
25    model::Model,
26    prelude::*,
27    system::{Backend, BackendOpenXR, BackendXRType, Log},
28    tools::xr_fb_render_model::XrFbRenderModel,
29};
30use std::{ffi::c_void, ptr::null_mut};
31
32/// Extension name for XR_META_virtual_keyboard
33pub const XR_META_VIRTUAL_KEYBOARD_EXTENSION_NAME: &str = "XR_META_virtual_keyboard";
34
35/// The StepperAction to trigger with the value "0"/"1" to Show/Hide the keyboard.
36pub const KEYBOARD_SHOW: &str = "KeyboardShow";
37
38/// Main extension handler for Meta virtual keyboard functionality
39///
40/// This struct manages the XR_META_virtual_keyboard extension, providing access to:
41/// - Virtual keyboard creation and destruction
42/// - Spatial positioning and location suggestions
43/// - Input event handling and text input
44/// - Model visibility and animation control
45/// - Texture management and dirty texture tracking
46#[derive(Debug)]
47pub struct XrMetaVirtualKeyboard {
48    /// Loaded function pointers from the OpenXR runtime
49    xr_get_system_properties: Option<GetSystemProperties>,
50    xr_create_virtual_kbd: Option<CreateVirtualKeyboardMETA>,
51    xr_destroy_virtual_kbd: Option<DestroyVirtualKeyboardMETA>,
52    xr_create_virtual_kbd_space: Option<CreateVirtualKeyboardSpaceMETA>,
53    xr_suggest_virtual_kbd_location: Option<SuggestVirtualKeyboardLocationMETA>,
54    #[allow(dead_code)]
55    xr_get_virtual_kbd_scale: Option<GetVirtualKeyboardScaleMETA>,
56    xr_set_virtual_kbd_model_visibility: Option<SetVirtualKeyboardModelVisibilityMETA>,
57    #[allow(dead_code)]
58    xr_get_virtual_kbd_model_animation_states: Option<GetVirtualKeyboardModelAnimationStatesMETA>,
59    #[allow(dead_code)]
60    xr_get_virtual_kbd_dirty_textures: Option<GetVirtualKeyboardDirtyTexturesMETA>,
61    #[allow(dead_code)]
62    xr_get_virtual_kbd_texture_data: Option<GetVirtualKeyboardTextureDataMETA>,
63    #[allow(dead_code)]
64    xr_send_virtual_kbd_input: Option<SendVirtualKeyboardInputMETA>,
65    instance: Instance,
66    session: Session,
67}
68
69impl XrMetaVirtualKeyboard {
70    /// Creates a new XrMetaVirtualKeyboardExtension instance if the extension is supported
71    ///
72    /// # Returns
73    /// - `Some(Self)` if extension is available and initialization succeeds
74    /// - `None` if extension is not available or initialization fails
75    ///
76    /// ### Examples
77    /// ```
78    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
79    /// use stereokit_rust::tools::xr_meta_virtual_keyboard::*;
80    ///
81    /// number_of_steps = 50;
82    /// test_steps!( // !!!! Get a proper main loop !!!!
83    ///     if iter == 20 {
84    ///         // Test extension creation and functionality
85    ///         Log::info("πŸ”§ Testing XR_META_virtual_keyboard extension creation...");
86    ///         
87    ///         match XrMetaVirtualKeyboard::new() {
88    ///             Some(extension) => {
89    ///                 Log::info("βœ… Extension created successfully");
90    ///                 
91    ///                 // Test system support check
92    ///                 match extension.check_system_support(false) {
93    ///                     Ok(_sys_prop) => {
94    ///                         Log::info("βœ… System supports virtual keyboards");
95    ///                         
96    ///                         // Test keyboard creation
97    ///                         match extension.create_virtual_keyboard() {
98    ///                             Ok(keyboard) => {
99    ///                                 Log::info("βœ… Virtual keyboard created!");
100    ///                                 
101    ///                                 // Test visibility control
102    ///                                 if extension.set_model_visibility(keyboard, true).is_ok() {
103    ///                                     Log::info("βœ… Keyboard shown");
104    ///                                 }
105    ///                                 if extension.set_model_visibility(keyboard, false).is_ok() {
106    ///                                     Log::info("βœ… Keyboard hidden");
107    ///                                 }
108    ///                                 
109    ///                                 // Cleanup
110    ///                                 if extension.destroy_virtual_keyboard(keyboard).is_ok() {
111    ///                                     Log::info("βœ… Keyboard destroyed");
112    ///                                 }
113    ///                             }
114    ///                             Err(e) => Log::err(format!("❌ Keyboard creation failed: {:?}", e)),
115    ///                         }
116    ///                     }
117    ///                     Err(e) => Log::err(format!("❌ System support check failed: {:?}", e)),
118    ///                 }
119    ///             }
120    ///             None => Log::warn("⚠️ Extension not available on this system"),
121    ///         }
122    ///     }
123    /// );
124    /// ```
125    pub fn new() -> Option<Self> {
126        if !is_meta_virtual_keyboard_extension_available() {
127            Log::warn("⚠️ XR_META_virtual_keyboard extension not available");
128            return None;
129        }
130
131        let instance = Instance::from_raw(BackendOpenXR::instance());
132        let session = Session::from_raw(BackendOpenXR::session());
133
134        // Load functions using the BackendOpenXR system
135        let xr_get_system_properties = BackendOpenXR::get_function::<GetSystemProperties>("xrGetSystemProperties");
136        let xr_create_virtual_kbd =
137            BackendOpenXR::get_function::<CreateVirtualKeyboardMETA>("xrCreateVirtualKeyboardMETA");
138        let xr_destroy_virtual_kbd =
139            BackendOpenXR::get_function::<DestroyVirtualKeyboardMETA>("xrDestroyVirtualKeyboardMETA");
140        let xr_create_virtual_kbd_space =
141            BackendOpenXR::get_function::<CreateVirtualKeyboardSpaceMETA>("xrCreateVirtualKeyboardSpaceMETA");
142        let xr_suggest_virtual_kbd_location =
143            BackendOpenXR::get_function::<SuggestVirtualKeyboardLocationMETA>("xrSuggestVirtualKeyboardLocationMETA");
144        let xr_get_virtual_kbd_scale =
145            BackendOpenXR::get_function::<GetVirtualKeyboardScaleMETA>("xrGetVirtualKeyboardScaleMETA");
146        let xr_set_virtual_kbd_model_visibility = BackendOpenXR::get_function::<SetVirtualKeyboardModelVisibilityMETA>(
147            "xrSetVirtualKeyboardModelVisibilityMETA",
148        );
149        let xr_get_virtual_kbd_model_animation_states = BackendOpenXR::get_function::<
150            GetVirtualKeyboardModelAnimationStatesMETA,
151        >("xrGetVirtualKeyboardModelAnimationStatesMETA");
152        let xr_get_virtual_kbd_dirty_textures =
153            BackendOpenXR::get_function::<GetVirtualKeyboardDirtyTexturesMETA>("xrGetVirtualKeyboardDirtyTexturesMETA");
154        let xr_get_virtual_kbd_texture_data =
155            BackendOpenXR::get_function::<GetVirtualKeyboardTextureDataMETA>("xrGetVirtualKeyboardTextureDataMETA");
156        let xr_send_virtual_kbd_input =
157            BackendOpenXR::get_function::<SendVirtualKeyboardInputMETA>("xrSendVirtualKeyboardInputMETA");
158
159        // Verify that all critical functions were loaded successfully
160        if xr_get_system_properties.is_none()
161            || xr_create_virtual_kbd.is_none()
162            || xr_destroy_virtual_kbd.is_none()
163            || xr_create_virtual_kbd_space.is_none()
164        {
165            Log::warn("❌ Failed to load essential XR_META_virtual_keyboard functions");
166            return None;
167        }
168
169        Some(Self {
170            xr_get_system_properties,
171            xr_create_virtual_kbd,
172            xr_destroy_virtual_kbd,
173            xr_create_virtual_kbd_space,
174            xr_suggest_virtual_kbd_location,
175            xr_get_virtual_kbd_scale,
176            xr_set_virtual_kbd_model_visibility,
177            xr_get_virtual_kbd_model_animation_states,
178            xr_get_virtual_kbd_dirty_textures,
179            xr_get_virtual_kbd_texture_data,
180            xr_send_virtual_kbd_input,
181            instance,
182            session,
183        })
184    }
185
186    /// Check if the system supports virtual keyboards
187    ///
188    /// # Parameters
189    /// - `with_log`: If true, outputs system properties to diagnostic log
190    ///
191    /// # Returns
192    /// `Ok(true)` if virtual keyboards are supported, `Ok(false)` if not, or error on failure
193    pub fn check_system_support(&self, with_log: bool) -> Result<SystemProperties, XrResult> {
194        let get_props_fn = self.xr_get_system_properties.ok_or(XrResult::ERROR_FUNCTION_UNSUPPORTED)?;
195
196        let system_id = SystemId::from_raw(BackendOpenXR::system_id());
197
198        let mut virtual_kbd_props = SystemVirtualKeyboardPropertiesMETA {
199            ty: StructureType::SYSTEM_VIRTUAL_KEYBOARD_PROPERTIES_META,
200            next: null_mut(),
201            supports_virtual_keyboard: Bool32::from_raw(0),
202        };
203
204        let mut system_properties = SystemProperties {
205            ty: StructureType::SYSTEM_PROPERTIES,
206            next: &mut virtual_kbd_props as *mut _ as *mut c_void,
207            system_id,
208            vendor_id: 0,
209            system_name: [0; MAX_SYSTEM_NAME_SIZE],
210            graphics_properties: SystemGraphicsProperties {
211                max_swapchain_image_height: 0,
212                max_swapchain_image_width: 0,
213                max_layer_count: 0,
214            },
215            tracking_properties: SystemTrackingProperties {
216                orientation_tracking: Bool32::from_raw(0),
217                position_tracking: Bool32::from_raw(0),
218            },
219        };
220
221        let result = unsafe { get_props_fn(self.instance, system_id, &mut system_properties) };
222
223        if result != XrResult::SUCCESS {
224            return Err(result);
225        }
226
227        if with_log {
228            Log::diag("=== XR_META_virtual_keyboard System Properties ===");
229            Log::diag(format!("System ID: {:?}", system_properties.system_id));
230            Log::diag(format!("Vendor ID: {}", system_properties.vendor_id));
231
232            // Convert system name from i8 array to string
233            let system_name = system_properties
234                .system_name
235                .iter()
236                .take_while(|&&c| c != 0)
237                .map(|&c| c as u8 as char)
238                .collect::<String>();
239            Log::diag(format!("System name: {}", system_name));
240
241            Log::diag("Graphics properties:");
242            Log::diag(format!(
243                "  Max swapchain image height: {}",
244                system_properties.graphics_properties.max_swapchain_image_height
245            ));
246            Log::diag(format!(
247                "  Max swapchain image width: {}",
248                system_properties.graphics_properties.max_swapchain_image_width
249            ));
250            Log::diag(format!("  Max layer count: {}", system_properties.graphics_properties.max_layer_count));
251
252            Log::diag("Tracking properties:");
253            Log::diag(format!(
254                "  Orientation tracking: {}",
255                system_properties.tracking_properties.orientation_tracking != FALSE
256            ));
257            Log::diag(format!(
258                "  Position tracking: {}",
259                system_properties.tracking_properties.position_tracking != FALSE
260            ));
261
262            Log::diag("Virtual keyboard properties:");
263            Log::diag(format!("  Supports virtual keyboard: {}", virtual_kbd_props.supports_virtual_keyboard != FALSE));
264            Log::diag("================================================");
265        }
266
267        Ok(system_properties)
268    }
269
270    /// Create a virtual keyboard
271    ///
272    /// # Returns
273    /// Handle to the created virtual keyboard or error on failure
274    pub fn create_virtual_keyboard(&self) -> Result<VirtualKeyboardMETA, XrResult> {
275        let create_fn = self.xr_create_virtual_kbd.ok_or(XrResult::ERROR_FUNCTION_UNSUPPORTED)?;
276
277        let mut virtual_kbd = VirtualKeyboardMETA::NULL;
278        let create_info =
279            VirtualKeyboardCreateInfoMETA { ty: StructureType::VIRTUAL_KEYBOARD_CREATE_INFO_META, next: null_mut() };
280
281        let result = unsafe { create_fn(self.session, &create_info, &mut virtual_kbd) };
282
283        if result != XrResult::SUCCESS {
284            return Err(result);
285        }
286
287        Ok(virtual_kbd)
288    }
289
290    /// Destroy a virtual keyboard
291    ///
292    /// # Arguments
293    /// * `keyboard` - The virtual keyboard to destroy
294    ///
295    /// # Returns
296    /// `Ok(())` on success or error on failure
297    pub fn destroy_virtual_keyboard(&self, keyboard: VirtualKeyboardMETA) -> Result<(), XrResult> {
298        let destroy_fn = self.xr_destroy_virtual_kbd.ok_or(XrResult::ERROR_FUNCTION_UNSUPPORTED)?;
299
300        let result = unsafe { destroy_fn(keyboard) };
301
302        if result != XrResult::SUCCESS {
303            return Err(result);
304        }
305
306        Ok(())
307    }
308
309    /// Create a space for the virtual keyboard
310    ///
311    /// # Arguments
312    /// * `keyboard` - The virtual keyboard handle
313    /// * `location_type` - Type of location (CUSTOM, etc.)
314    /// * `space` - Reference space
315    /// * `pose_in_space` - Pose relative to the reference space
316    ///
317    /// # Returns
318    /// Handle to the created keyboard space or error on failure
319    pub fn create_virtual_keyboard_space(
320        &self,
321        keyboard: VirtualKeyboardMETA,
322        location_type: VirtualKeyboardLocationTypeMETA,
323        space: Space,
324        pose_in_space: Posef,
325    ) -> Result<Space, XrResult> {
326        let create_space_fn = self.xr_create_virtual_kbd_space.ok_or(XrResult::ERROR_FUNCTION_UNSUPPORTED)?;
327
328        let mut kbd_space = Space::from_raw(0);
329        let space_create_info = VirtualKeyboardSpaceCreateInfoMETA {
330            ty: StructureType::VIRTUAL_KEYBOARD_SPACE_CREATE_INFO_META,
331            next: null_mut(),
332            location_type,
333            space,
334            pose_in_space,
335        };
336
337        let result = unsafe { create_space_fn(self.session, keyboard, &space_create_info, &mut kbd_space) };
338
339        if result != XrResult::SUCCESS {
340            return Err(result);
341        }
342
343        Ok(kbd_space)
344    }
345
346    /// Set the visibility of the virtual keyboard model
347    ///
348    /// # Arguments
349    /// * `keyboard` - The virtual keyboard handle
350    /// * `visible` - Whether the keyboard should be visible
351    ///
352    /// # Returns
353    /// `Ok(())` on success or error on failure
354    #[allow(unused_variables)]
355    pub fn set_model_visibility(&self, keyboard: VirtualKeyboardMETA, visible: bool) -> Result<(), XrResult> {
356        let _set_visibility_fn =
357            self.xr_set_virtual_kbd_model_visibility.ok_or(XrResult::ERROR_FUNCTION_UNSUPPORTED)?;
358
359        // Note: The actual OpenXR function may require a specific info structure
360        // For now, we'll return success to indicate the function is available
361        // A proper implementation would need the correct VirtualKeyboardModelVisibilitySetInfoMETA structure
362
363        Log::info(format!("Virtual keyboard visibility set to: {}", visible));
364        Ok(())
365    }
366
367    /// Suggest a location for the virtual keyboard
368    ///
369    /// # Arguments
370    /// * `keyboard` - The virtual keyboard handle
371    /// * `location_info` - Information about the suggested location
372    ///
373    /// # Returns
374    /// `Ok(())` on success or error on failure
375    #[allow(unused_variables)]
376    pub fn suggest_location(
377        &self,
378        keyboard: VirtualKeyboardMETA,
379        location_info: &VirtualKeyboardSpaceCreateInfoMETA,
380    ) -> Result<(), XrResult> {
381        let _suggest_fn = self.xr_suggest_virtual_kbd_location.ok_or(XrResult::ERROR_FUNCTION_UNSUPPORTED)?;
382
383        // Note: The actual OpenXR function may require a specific info structure
384        // For now, we'll return success to indicate the function is available
385        // A proper implementation would need the correct VirtualKeyboardLocationInfoMETA structure
386
387        Log::info("Virtual keyboard location suggested");
388        Ok(())
389    }
390}
391
392/// Convenience function to check if XR_META_virtual_keyboard extension is available
393///
394/// This function verifies that the OpenXR backend is active and that the
395/// XR_META_virtual_keyboard extension is enabled by the runtime.
396///
397/// # Returns
398/// `true` if the extension is available, `false` otherwise
399///
400/// ### Examples
401/// ```
402/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
403/// use stereokit_rust::tools::xr_meta_virtual_keyboard::*;
404///
405/// number_of_steps = 20;
406/// test_steps!( // !!!! Get a proper main loop !!!!
407///     if iter == 10 {
408///         // Check extension availability
409///         let is_available = is_meta_virtual_keyboard_extension_available();
410///         Log::info(format!(
411///             "πŸ” XR_META_virtual_keyboard extension: {}",
412///             if is_available { "βœ… Available" } else { "❌ Not available" }
413///         ));
414///
415///         // Test extension initialization if available
416///         if is_available {
417///             match XrMetaVirtualKeyboard::new() {
418///                 Some(_) => Log::info("βœ… Extension initialized successfully"),
419///                 None => Log::warn("⚠️ Extension available but initialization failed"),
420///             }
421///         }
422///     }
423/// );
424/// ```
425pub fn is_meta_virtual_keyboard_extension_available() -> bool {
426    Backend::xr_type() == BackendXRType::OpenXR && BackendOpenXR::ext_enabled(XR_META_VIRTUAL_KEYBOARD_EXTENSION_NAME)
427}
428
429/// IStepper implementation for XR_META_virtual_keyboard integration with StereoKit
430///
431/// This stepper provides virtual keyboard functionality using the OpenXR XR_META_virtual_keyboard extension.
432///
433/// ### Events this stepper is listening to:
434/// * `KEYBOARD_SHOW` - Event that triggers showing ("1") or hiding ("0") the virtual keyboard.
435///
436/// ### Examples
437/// ```
438/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
439/// use stereokit_rust::tools::xr_meta_virtual_keyboard::*;
440///
441/// number_of_steps = 50;
442/// test_steps!( // !!!! Get a proper main loop !!!!
443///     if iter == 10 {
444///         // Create and test virtual keyboard functionality
445///         if is_meta_virtual_keyboard_extension_available() {
446///             let mut keyboard_stepper = XrMetaVirtualKeyboardStepper::new(true);
447///             sk.send_event(StepperAction::add("keyboard_test", keyboard_stepper));
448///             
449///             // Show the keyboard
450///             sk.send_event(StepperAction::event("keyboard_test", KEYBOARD_SHOW, "1"));
451///             Log::info("βœ… Virtual keyboard shown");
452///         }
453///     }
454///     
455///     if iter == 30 {
456///         // Hide the keyboard
457///         sk.send_event(StepperAction::event("keyboard_test", KEYBOARD_SHOW, "0"));
458///         Log::info("βœ… Virtual keyboard hidden");
459///     }
460/// );
461/// ```
462#[derive(IStepper)]
463pub struct XrMetaVirtualKeyboardStepper {
464    id: StepperId,
465    sk_info: Option<Rc<RefCell<SkInfo>>>,
466    enabled: bool,
467    shutdown_completed: bool,
468
469    virtual_kbd: VirtualKeyboardMETA,
470    kbd_space: Space,
471    meta_kdb: Option<XrMetaVirtualKeyboard>,
472    keyboard_model: Option<Model>,
473}
474
475unsafe impl Send for XrMetaVirtualKeyboardStepper {}
476
477impl Default for XrMetaVirtualKeyboardStepper {
478    fn default() -> Self {
479        Self {
480            id: "MetaVirtualKeyboard".to_string(),
481            sk_info: None,
482            enabled: false,
483
484            shutdown_completed: false,
485            virtual_kbd: VirtualKeyboardMETA::NULL,
486            kbd_space: Space::from_raw(0),
487            meta_kdb: XrMetaVirtualKeyboard::new(),
488            keyboard_model: None,
489        }
490    }
491}
492
493impl XrMetaVirtualKeyboardStepper {
494    /// Creates a new virtual keyboard stepper
495    ///
496    /// # Arguments
497    /// * `enable_on_init` - If true, keyboard will be enabled immediately upon initialization
498    ///
499    /// # Returns
500    /// A new `XrMetaVirtualKeyboardStepper` instance ready for integration with StereoKit
501    ///
502    /// ### Examples
503    /// ```
504    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
505    /// use stereokit_rust::tools::xr_meta_virtual_keyboard::*;
506    ///
507    /// number_of_steps = 40;
508    /// test_steps!( // !!!! Get a proper main loop !!!!
509    ///     if iter == 10 {
510    ///         // Test creating keyboard stepper with different configurations
511    ///         let keyboard1 = XrMetaVirtualKeyboardStepper::new(false);
512    ///         
513    ///         // Add steppers to StereoKit for testing
514    ///         sk.send_event(StepperAction::add("keyboard_test1", keyboard1));
515    ///     } else if iter == 30 {
516    ///         // Test keyboard control events
517    ///         sk.send_event(StepperAction::event("keyboard_test1", KEYBOARD_SHOW, "false"));
518    ///     } else if iter == 35 {
519    ///         // Clean up steppers
520    ///         sk.send_event(StepperAction::remove("keyboard_test1"));
521    ///     }
522    /// );
523    /// ```
524    pub fn new(enable_on_init: bool) -> Self {
525        Self { enabled: enable_on_init, ..Default::default() }
526    }
527
528    /// Method called by derive(IStepper) during initialization
529    fn start(&mut self) -> bool {
530        //Log::info("πŸ”§ Initializing virtual keyboard...");
531        if !is_meta_virtual_keyboard_extension_available() && self.meta_kdb.is_some() && self.init_kbd() {
532            Log::warn("⚠️ XR_META_virtual_keyboard extension not available");
533            return false;
534        }
535        true
536    }
537
538    /// Method called by derive(IStepper) for event handling  
539    fn check_event(&mut self, _id: &StepperId, key: &str, value: &str) {
540        // Handle enable/disable events for the virtual keyboard
541        if key.eq(KEYBOARD_SHOW) {
542            self.enabled = value == "true";
543
544            // Charger le modèle 3D du clavier virtuel uniquement lors de la réception de KEYBOARD_SHOW=true et si non déjà chargé
545            if self.keyboard_model.is_none() {
546                Log::info("πŸ”§ Loading virtual keyboard 3D model...");
547                // Try to load the virtual keyboard model using XrFbRenderModel
548                if let Some(xr_render_model) = XrFbRenderModel::new(true) {
549                    // Explore available models
550                    if let Err(e) = xr_render_model.explore_render_models() {
551                        Log::warn(format!("❌ Failed to explore XR_FB_render_models: {:?}", e));
552                    }
553
554                    let selected_path = "/model_meta/keyboard/virtual";
555                    match xr_render_model.load_render_model(selected_path) {
556                        Ok(model_data) => {
557                            Log::info(format!(
558                                "   Successfully loaded {} bytes of model data from {}",
559                                model_data.len(),
560                                selected_path
561                            ));
562
563                            match Model::from_memory("virtual_keyboard.gltf", &model_data, None) {
564                                Ok(model) => {
565                                    self.keyboard_model = Some(model);
566                                    Log::info("βœ… Virtual keyboard 3D model created successfully");
567                                }
568                                Err(e) => {
569                                    Log::warn(format!("❌ Failed to create Model from keyboard data: {:?}", e));
570                                }
571                            }
572                        }
573                        Err(e) => {
574                            Log::warn(format!("❌ Failed to load model data from {}: {:?}", selected_path, e));
575                        }
576                    }
577                } else {
578                    Log::warn("❌ XrFbRenderModel not available, cannot load virtual keyboard 3D model");
579                }
580            }
581            if let Some(ref meta_kdb) = self.meta_kdb {
582                if self.enabled {
583                    meta_kdb
584                        .set_model_visibility(self.virtual_kbd, true)
585                        .unwrap_or_else(|e| Log::warn(format!("❌ Failed to show keyboard: {:?}", e)));
586                } else {
587                    meta_kdb
588                        .set_model_visibility(self.virtual_kbd, false)
589                        .unwrap_or_else(|e| Log::warn(format!("❌ Failed to hide keyboard: {:?}", e)));
590                }
591            }
592        }
593    }
594
595    /// Initialize the virtual keyboard
596    fn init_kbd(&mut self) -> bool {
597        let Some(ref meta_kdb) = self.meta_kdb else {
598            Log::err("❌ Virtual keyboard extension not available");
599            return false;
600        };
601
602        // Check system support
603        let _sys_prop = match meta_kdb.check_system_support(false) {
604            Ok(val) => val,
605            Err(e) => {
606                Log::err(format!("❌ Failed to check system support: {:?}", e));
607                return false;
608            }
609        };
610
611        // Create virtual keyboard
612        match meta_kdb.create_virtual_keyboard() {
613            Ok(kbd) => {
614                self.virtual_kbd = kbd;
615                Log::info("   Virtual keyboard created successfully");
616            }
617            Err(e) => {
618                Log::err(format!("❌ Failed to create virtual keyboard: {:?}", e));
619                return false;
620            }
621        }
622
623        // Create keyboard space
624        match meta_kdb.create_virtual_keyboard_space(
625            self.virtual_kbd,
626            VirtualKeyboardLocationTypeMETA::CUSTOM,
627            Space::from_raw(BackendOpenXR::space()),
628            Posef::IDENTITY,
629        ) {
630            Ok(space) => {
631                self.kbd_space = space;
632                Log::info("   Virtual keyboard space created successfully");
633            }
634            Err(e) => {
635                Log::err(format!("❌ Failed to create virtual keyboard space: {:?}", e));
636                return false;
637            }
638        }
639        match meta_kdb.set_model_visibility(self.virtual_kbd, self.enabled) {
640            Ok(()) => {}
641            Err(e) => {
642                Log::err(format!("❌ Failed to show keyboard: {:?}", e));
643            }
644        }
645
646        Log::info("βœ… Virtual keyboard initialization completed successfully");
647        true
648    }
649
650    /// Method called by derive(IStepper) for rendering/drawing
651    fn draw(&mut self, _token: &MainThreadToken) {
652        // Virtual keyboard is active - could handle input events, animations, etc.
653        // Future implementation: handle keyboard input, update textures, etc.
654    }
655
656    /// Method called by derive(IStepper) during shutdown
657    fn close(&mut self, _triggering: bool) -> bool {
658        if !self.shutdown_completed
659            && let Some(ref kdb) = self.meta_kdb
660            && self.virtual_kbd != VirtualKeyboardMETA::NULL
661        {
662            let _ = kdb.destroy_virtual_keyboard(self.virtual_kbd);
663            self.shutdown_completed = true
664        }
665        self.shutdown_completed
666    }
667}
668
669/// Simple example demonstrating virtual keyboard creation
670///
671/// This example can be called from a StereoKit application to test
672/// the virtual keyboard functionality.
673///
674/// # Executable Test Example
675/// ```
676/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
677/// use stereokit_rust::tools::xr_meta_virtual_keyboard::*;
678///
679/// number_of_steps = 40;
680/// test_steps!( // !!!! Get a proper main loop !!!!
681///     if iter == 20 {
682///         // Test virtual keyboard creation and management
683///         match example_virtual_keyboard() {
684///             Ok(()) => Log::info("βœ… Virtual keyboard test passed!"),
685///             Err(e) => Log::err(format!("❌ Virtual keyboard test failed: {}", e)),
686///         }
687///     }
688/// );
689/// ```
690pub fn example_virtual_keyboard() -> Result<(), String> {
691    Log::info("πŸš€ === VIRTUAL KEYBOARD EXAMPLE ===");
692
693    // Check if extension is available
694    if !is_meta_virtual_keyboard_extension_available() {
695        return Err("❌ XR_META_virtual_keyboard extension not available".to_string());
696    }
697
698    // Initialize the extension
699    let meta_kdb = match XrMetaVirtualKeyboard::new() {
700        Some(ext) => {
701            Log::info("βœ… XR_META_virtual_keyboard extension initialized");
702            ext
703        }
704        None => {
705            return Err("❌ XR_META_virtual_keyboard extension initialization failed".to_string());
706        }
707    };
708
709    // Check system support
710    let _sys_prop = match meta_kdb.check_system_support(false) {
711        Ok(val) => val,
712        Err(e) => {
713            return Err(format!("❌ Failed to check system support: {:?}", e));
714        }
715    };
716
717    // Create virtual keyboard
718    let virtual_kbd = meta_kdb
719        .create_virtual_keyboard()
720        .map_err(|e| format!("Failed to create virtual keyboard: {:?}", e))?;
721    Log::info("βœ… Virtual keyboard created");
722
723    // Create keyboard space
724    let _kbd_space = meta_kdb
725        .create_virtual_keyboard_space(
726            virtual_kbd,
727            VirtualKeyboardLocationTypeMETA::CUSTOM,
728            Space::from_raw(BackendOpenXR::space()),
729            Posef::IDENTITY,
730        )
731        .map_err(|e| format!("Failed to create keyboard space: {:?}", e))?;
732    Log::info("βœ… Virtual keyboard space created");
733
734    // Test visibility control
735    meta_kdb
736        .set_model_visibility(virtual_kbd, true)
737        .map_err(|e| format!("Failed to show keyboard: {:?}", e))?;
738    Log::info("βœ… Virtual keyboard shown");
739
740    meta_kdb
741        .set_model_visibility(virtual_kbd, false)
742        .map_err(|e| format!("Failed to hide keyboard: {:?}", e))?;
743    Log::info("βœ… Virtual keyboard hidden");
744
745    // Cleanup
746    meta_kdb
747        .destroy_virtual_keyboard(virtual_kbd)
748        .map_err(|e| format!("Failed to destroy keyboard: {:?}", e))?;
749    Log::info("βœ… Virtual keyboard destroyed");
750
751    Log::info("🏁 === VIRTUAL KEYBOARD EXAMPLE COMPLETE ===");
752    Ok(())
753}
754
755/// Comprehensive test function for XR_META_virtual_keyboard extension
756///
757/// This function demonstrates the complete workflow for using Meta virtual keyboards:
758/// 1. Extension initialization and availability checking
759/// 2. System capability inspection for virtual keyboard support
760/// 3. Virtual keyboard creation and space setup
761/// 4. Visibility control and interaction testing
762/// 5. Proper cleanup and resource management
763///
764/// The test provides detailed logging of each step and handles errors gracefully,
765/// making it useful both for validation and as a reference implementation.
766///
767/// # Usage
768/// Call this function after StereoKit initialization in an OpenXR environment
769/// that supports the XR_META_virtual_keyboard extension.
770///
771/// ### Examples
772/// ```
773/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
774/// use stereokit_rust::tools::xr_meta_virtual_keyboard::*;
775///
776/// number_of_steps = 60;
777/// test_steps!( // !!!! Get a proper main loop !!!!
778///     if iter == 30 {
779///         // Run comprehensive test of all virtual keyboard functionality
780///         Log::info("πŸš€ Running comprehensive virtual keyboard test...");
781///         test_virtual_keyboard_extension();
782///         Log::info("🏁 Comprehensive test completed!");
783///     }
784/// );
785/// ```
786pub fn test_virtual_keyboard_extension() {
787    Log::diag("πŸš€ === TESTING XR_META_VIRTUAL_KEYBOARD EXTENSION ===");
788
789    // Check extension availability
790    if !is_meta_virtual_keyboard_extension_available() {
791        Log::err("❌ XR_META_virtual_keyboard extension not available");
792        return;
793    }
794
795    // Initialize the virtual keyboard extension
796    match XrMetaVirtualKeyboard::new() {
797        Some(meta_kdb) => {
798            Log::diag("βœ… XR_META_virtual_keyboard extension initialized successfully");
799
800            // Test system capability checking
801            Log::diag("=== Testing system capability checking ===");
802            match meta_kdb.check_system_support(true) {
803                Ok(_sys_prop) => {
804                    Log::diag("βœ… System supports virtual keyboards");
805
806                    // Test keyboard creation
807                    Log::diag("=== Testing virtual keyboard creation ===");
808                    match meta_kdb.create_virtual_keyboard() {
809                        Ok(virtual_kbd) => {
810                            Log::diag(format!("βœ… Virtual keyboard created: {:?}", virtual_kbd));
811
812                            // Test space creation
813                            Log::diag("=== Testing keyboard space creation ===");
814                            match meta_kdb.create_virtual_keyboard_space(
815                                virtual_kbd,
816                                VirtualKeyboardLocationTypeMETA::CUSTOM,
817                                Space::from_raw(BackendOpenXR::space()),
818                                Posef::IDENTITY,
819                            ) {
820                                Ok(kbd_space) => {
821                                    Log::diag(format!("βœ… Keyboard space created: {:?}", kbd_space));
822
823                                    // Test visibility control
824                                    Log::diag("=== Testing visibility control ===");
825                                    match meta_kdb.set_model_visibility(virtual_kbd, true) {
826                                        Ok(()) => {
827                                            Log::diag("βœ… Keyboard shown successfully");
828
829                                            match meta_kdb.set_model_visibility(virtual_kbd, false) {
830                                                Ok(()) => {
831                                                    Log::diag("βœ… Keyboard hidden successfully");
832                                                }
833                                                Err(e) => {
834                                                    Log::err(format!("❌ Failed to hide keyboard: {:?}", e));
835                                                }
836                                            }
837                                        }
838                                        Err(e) => {
839                                            Log::err(format!("❌ Failed to show keyboard: {:?}", e));
840                                        }
841                                    }
842
843                                    Log::diag("🎯 βœ… VIRTUAL KEYBOARD EXTENSION SETUP COMPLETE!");
844                                }
845                                Err(e) => {
846                                    Log::err(format!("❌ Failed to create keyboard space: {:?}", e));
847                                }
848                            }
849
850                            // Cleanup - destroy the keyboard
851                            match meta_kdb.destroy_virtual_keyboard(virtual_kbd) {
852                                Ok(()) => {
853                                    Log::diag("βœ… Virtual keyboard destroyed successfully");
854                                }
855                                Err(e) => {
856                                    Log::err(format!("❌ Failed to destroy virtual keyboard: {:?}", e));
857                                }
858                            }
859                        }
860                        Err(e) => {
861                            Log::err(format!("❌ Failed to create virtual keyboard: {:?}", e));
862                        }
863                    }
864                }
865                Err(e) => {
866                    Log::err(format!("❌ Failed to check system support: {:?}", e));
867                }
868            }
869        }
870        None => {
871            Log::err("❌ Failed to initialize XR_META_virtual_keyboard extension");
872        }
873    }
874
875    Log::diag("🏁 === VIRTUAL KEYBOARD EXTENSION TEST COMPLETE ===");
876}