Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
201 changes: 201 additions & 0 deletions examples/input-injection/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
# Keyboard Input Injection Demo

This example demonstrates how host applications can inject keyboard events into JSAR Runtime using the InputManager module.

## Overview

The InputManager module provides a clean separation between event capture and event management, allowing different platforms (Unity, Unreal, native, etc.) to implement their own input event listeners and inject events into JSAR.

## Architecture

```
Host Application (Unity/Unreal/Native)
↓ (captures input)
InputManager
↓ (IPC via InputEvent)
JSAR Client Process
↓ (DOM events)
Web Application
```

## Files

- `keyboard_demo.cpp` - C++ demo showing how to inject keyboard events
- `keyboard_demo.html` - HTML page that receives and displays keyboard events
- `README.md` - This file

## Usage

### 1. Host-Side Event Injection

```cpp
#include "runtime/embedder.hpp"
#include "runtime/input-manager/input_manager.hpp"

class MyApp : public TrEmbedder {
public:
MyApp() : TrEmbedder(TrHostEngine::Unity) {}

void onKeyPress(const std::string& key, const std::string& code) {
// Create keyboard event data
auto eventData = input_manager::keyboard_events::createKeyDownEvent(
key, code, false, false, false, false);

// Inject to all active content runtimes
broadcastKeyboardEvent(eventData);

// Or inject to specific content
// injectKeyboardEvent(contentId, eventData);
}

bool onEvent(events_comm::TrNativeEvent &event,
std::shared_ptr<TrContentRuntime> content) override {
return true;
}
};
```

### 2. Client-Side Event Handling

The injected events are automatically converted to standard DOM KeyboardEvents and dispatched to the appropriate elements:

```javascript
// Standard DOM event listeners work automatically
document.addEventListener('keydown', function(event) {
console.log('Key pressed:', event.key, event.code);
console.log('Modifiers:', {
alt: event.altKey,
ctrl: event.ctrlKey,
meta: event.metaKey,
shift: event.shiftKey
});
});

// Events are dispatched to focused elements
inputElement.addEventListener('keydown', function(event) {
// Handle input-specific keyboard events
});
```

## API Reference

### KeyboardEvent Helper Functions

```cpp
// Create key down event
auto keyDown = input_manager::keyboard_events::createKeyDownEvent(
"a", // key value
"KeyA", // physical key code
false, // altKey
true, // ctrlKey
false, // metaKey
false // shiftKey
);

// Create key up event
auto keyUp = input_manager::keyboard_events::createKeyUpEvent(
"Enter", "Enter", false, false, false, false);

// Create key press event
auto keyPress = input_manager::keyboard_events::createKeyPressEvent(
"Space", "Space", false, false, false, false);
```

### InputManager Methods

```cpp
// Inject event to specific content runtime
bool injectKeyboardEvent(uint32_t contentId, const TrKeyboardEventData& eventData);

// Broadcast event to all active content runtimes
int broadcastKeyboardEvent(const TrKeyboardEventData& eventData);

// Get list of active content runtime IDs
std::vector<uint32_t> getActiveContentIds() const;
```

## Key Mapping Examples

| Key | Value | Code | Description |
|-----|-------|------|-------------|
| a-z | "a" | "KeyA" | Letter keys |
| 0-9 | "0" | "Digit0" | Number keys |
| Space | "Space" | "Space" | Space bar |
| Enter | "Enter" | "Enter" | Enter key |
| Escape | "Escape" | "Escape" | Escape key |
| Control | "Control" | "ControlLeft" | Ctrl key |
| Alt | "Alt" | "AltLeft" | Alt key |
| Shift | "Shift" | "ShiftLeft" | Shift key |

## Event Flow

1. **Host Capture**: Host application (Unity, Unreal, etc.) captures input events using platform-specific APIs
2. **Event Creation**: Host creates `TrKeyboardEventData` using helper functions
3. **Injection**: Host calls `injectKeyboardEvent()` or `broadcastKeyboardEvent()`
4. **IPC Transport**: InputManager sends `InputEvent` via IPC to client process
5. **DOM Conversion**: Client converts to standard `KeyboardEvent`
6. **Dispatch**: Event is dispatched to focused element or document body
7. **Application Handling**: Web application handles event using standard DOM APIs

## Integration Guide

### Unity Integration Example

```csharp
// Unity C# script
using UnityEngine;

public class JSARInputForwarder : MonoBehaviour {
private JSARRuntime jsar;

void Start() {
jsar = GetComponent<JSARRuntime>();
}

void Update() {
foreach (KeyCode key in System.Enum.GetValues(typeof(KeyCode))) {
if (Input.GetKeyDown(key)) {
string keyValue = KeyCodeToString(key);
string keyCode = KeyCodeToCode(key);
jsar.InjectKeyDown(keyValue, keyCode);
}
}
}
}
```

### Native Application Example

```cpp
// Platform-specific input capture
void OnPlatformKeyEvent(int virtualKey, bool isDown) {
std::string key = VirtualKeyToString(virtualKey);
std::string code = VirtualKeyToCode(virtualKey);

if (isDown) {
auto eventData = input_manager::keyboard_events::createKeyDownEvent(
key, code, GetModifierState());
embedder->broadcastKeyboardEvent(eventData);
}
}
```

## Benefits

- **Platform Independence**: Host applications retain control over input capture
- **Standard Compliance**: Generates standard DOM KeyboardEvents
- **Performance**: Efficient IPC mechanism for event forwarding
- **Flexibility**: Supports both targeted and broadcast event injection
- **Accessibility**: Enables keyboard accessibility features in spatial applications

## Testing

Run the C++ demo to see keyboard event injection in action:

```bash
# Compile and run the demo (when build system supports it)
make examples
./build/examples/keyboard_demo
```

Open the HTML demo in JSAR Runtime to see events being received and handled.
171 changes: 171 additions & 0 deletions examples/input-injection/keyboard_demo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#include <iostream>
#include <chrono>
#include <thread>
#include "../../src/runtime/embedder.hpp"
#include "../../src/runtime/input-manager/input_manager.hpp"

/**
* Example: Keyboard Event Injection Demo
*
* This example demonstrates how a host application can inject keyboard events
* into JSAR Runtime using the InputManager module.
*/

class KeyboardInputDemo : public TrEmbedder
{
public:
KeyboardInputDemo() : TrEmbedder(TrHostEngine::None) {}

bool onEvent(events_comm::TrNativeEvent &event, std::shared_ptr<TrContentRuntime> content) override
{
// Handle events from content runtime
return true;
}

void runDemo()
{
std::cout << "JSAR Runtime - Keyboard Input Injection Demo\n";
std::cout << "=============================================\n\n";

// Configure and start the runtime
if (!configure("/tmp/jsar-demo", "", false))
{
std::cerr << "Failed to configure JSAR Runtime\n";
return;
}

if (!start())
{
std::cerr << "Failed to start JSAR Runtime\n";
return;
}

std::cout << "JSAR Runtime started successfully.\n";
std::cout << "InputManager initialized.\n\n";

// Simulate host application injecting keyboard events
std::cout << "Simulating keyboard event injection...\n";

// Example 1: Single key press simulation
simulateKeyPress();

// Example 2: Text typing simulation
simulateTextTyping("Hello JSAR!");

// Example 3: Modifier key combinations
simulateModifierCombinations();

std::cout << "\nDemo completed. Shutting down...\n";
shutdown();
}

private:
void simulateKeyPress()
{
std::cout << "\n1. Simulating single key press (Enter key):\n";

auto keyDownEvent = input_manager::keyboard_events::createKeyDownEvent(
"Enter", "Enter", false, false, false, false);

auto keyUpEvent = input_manager::keyboard_events::createKeyUpEvent(
"Enter", "Enter", false, false, false, false);

// Broadcast to all active content runtimes
int contentCount = broadcastKeyboardEvent(keyDownEvent);
std::cout << " Key down sent to " << contentCount << " content runtime(s)\n";

std::this_thread::sleep_for(std::chrono::milliseconds(100));

contentCount = broadcastKeyboardEvent(keyUpEvent);
std::cout << " Key up sent to " << contentCount << " content runtime(s)\n";
}

void simulateTextTyping(const std::string& text)
{
std::cout << "\n2. Simulating text typing: \"" << text << "\"\n";

for (char c : text)
{
std::string key(1, c);
std::string code = getCodeForChar(c);

auto keyDownEvent = input_manager::keyboard_events::createKeyDownEvent(
key, code, false, false, false, false);

auto keyUpEvent = input_manager::keyboard_events::createKeyUpEvent(
key, code, false, false, false, false);

int contentCount = broadcastKeyboardEvent(keyDownEvent);
std::this_thread::sleep_for(std::chrono::milliseconds(50));
broadcastKeyboardEvent(keyUpEvent);

std::cout << " Typed '" << c << "' to " << contentCount << " content(s)\n";
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}

void simulateModifierCombinations()
{
std::cout << "\n3. Simulating modifier key combinations (Ctrl+A):\n";

// Ctrl key down
auto ctrlDown = input_manager::keyboard_events::createKeyDownEvent(
"Control", "ControlLeft", false, true, false, false);
broadcastKeyboardEvent(ctrlDown);
std::cout << " Ctrl key down\n";

std::this_thread::sleep_for(std::chrono::milliseconds(50));

// A key down (with Ctrl modifier active)
auto aDown = input_manager::keyboard_events::createKeyDownEvent(
"a", "KeyA", false, true, false, false);
broadcastKeyboardEvent(aDown);
std::cout << " A key down (with Ctrl modifier)\n";

std::this_thread::sleep_for(std::chrono::milliseconds(50));

// A key up
auto aUp = input_manager::keyboard_events::createKeyUpEvent(
"a", "KeyA", false, true, false, false);
broadcastKeyboardEvent(aUp);
std::cout << " A key up\n";

std::this_thread::sleep_for(std::chrono::milliseconds(50));

// Ctrl key up
auto ctrlUp = input_manager::keyboard_events::createKeyUpEvent(
"Control", "ControlLeft", false, false, false, false);
int contentCount = broadcastKeyboardEvent(ctrlUp);
std::cout << " Ctrl key up - Ctrl+A sent to " << contentCount << " content(s)\n";
}

std::string getCodeForChar(char c)
{
if (c >= 'a' && c <= 'z')
return "Key" + std::string(1, c - 'a' + 'A');
if (c >= 'A' && c <= 'Z')
return "Key" + std::string(1, c);
if (c == ' ')
return "Space";
if (c >= '0' && c <= '9')
return "Digit" + std::string(1, c);

return "Unknown";
}
};

int main()
{
try
{
KeyboardInputDemo demo;
demo.runDemo();
}
catch (const std::exception& e)
{
std::cerr << "Demo failed with exception: " << e.what() << std::endl;
return 1;
}

return 0;
}
Loading