Skip to content

Commit 1dbd150

Browse files
author
Dave Horner
committed
feat(keyboard): add send_key_combinations API and Lua binding
- Implemented a new send_key_combinations function in src/keyboard.rs to simulate key press sequences via RPC. - Introduced the KeyCombo struct (with Debug) to encapsulate key press details (modifier, keys, hold, wait). - Updated README.md with usage examples for the new function. - Added Lua examples (windows-exit_notepad.lua and updated windows-notepad-helloworld.lua) to demonstrate key combination usage.
1 parent 4612c23 commit 1dbd150

File tree

4 files changed

+114
-1
lines changed

4 files changed

+114
-1
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ delay(250)
132132
send_ctrl_v()
133133
delay(250)
134134
send_ctrl_v()
135+
send_key_combinations({
136+
{ modifier = 0x04, keys = {0x3D}, hold = 100, wait = 1000 }, -- Alt+F4
137+
{ modifier = 0, keys = {0x4F}, wait = 250 }, -- Right arrow
138+
{ modifier = 0, keys = {0x28} }, -- Return (Enter)
139+
})
135140
```
136141

137142
Check out the examples folder for additional detail.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
print("Executing Lua script...")
2+
-- for i = 1, 10 do
3+
send_key_combinations({
4+
{ modifier = 0x04, keys = {0x3D}, hold = 100, wait = 1000 } -- Alt+F4
5+
{ modifier = 0, keys = {0x4F}, hold = 100, wait = 550 }, -- Right arrow
6+
-- { modifier = 0, keys = {0x4F}, hold = 100, wait = 50 }, -- Right arrow again (cancel button)
7+
{ modifier = 0, keys = {0x28}, hold = 100, wait = 50 } -- Return (Enter)
8+
})
9+
-- end

lua-examples/windows-notepad-helloworld.lua

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,8 @@ delay(250)
1212
send_ctrl_v()
1313
delay(250)
1414
send_ctrl_v()
15-
15+
send_key_combinations({
16+
{ modifier = 0x04, keys = {0x3D}, hold = 100, wait = 1000 }, -- Alt+F4
17+
{ modifier = 0, keys = {0x4F}, wait = 250 }, -- Right arrow
18+
{ modifier = 0, keys = {0x28} }, -- Return (Enter)
19+
})

src/keyboard.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,69 @@ pub async fn send_windows_key(client: &crate::jetkvm_rpc_client::JetKvmRpcClient
280280
Ok(())
281281
}
282282

283+
/// Represents a key combination for a remote KVM device.
284+
///
285+
/// This struct defines the information required to simulate a key press event:
286+
/// - `modifier`: A bitmask representing modifier keys (e.g., Ctrl, Alt).
287+
/// - `keys`: A list of key codes to be pressed.
288+
/// - `hold`: The duration (in milliseconds) to hold the key press (defaults to 100 ms).
289+
/// - `wait`: The delay (in milliseconds) after releasing the keys before proceeding to the next combination (defaults to 10 ms).
290+
#[derive(Debug)]
291+
struct KeyCombo {
292+
modifier: u64,
293+
keys: Vec<u8>,
294+
hold: u64, // how long to hold the key press (in ms), defaults to 100 ms
295+
wait: u64, // delay after releasing keys before next combo (in ms), defaults to 10 ms
296+
}
297+
298+
/// Sends a series of key combinations to the remote KVM device via RPC.
299+
///
300+
/// This asynchronous function processes each key combination in the provided list by:
301+
/// 1. Sending a `keyboardReport` RPC that presses the specified keys along with any modifier keys.
302+
/// 2. Waiting for the duration specified in `combo.hold` to simulate holding the keys down.
303+
/// 3. Sending another `keyboardReport` RPC to release all keys.
304+
/// 4. Waiting for the duration specified in `combo.wait` before moving to the next combination.
305+
///
306+
/// # Parameters
307+
/// - `client`: A reference to the `JetKvmRpcClient` that is used to send RPC commands.
308+
/// - `combos`: A vector of `KeyCombo` structs, where each struct contains:
309+
/// - `modifier`: The modifier key(s) (like Ctrl, Alt, etc.) to apply.
310+
/// - `keys`: The list of keys to be pressed.
311+
/// - `hold`: The duration (in milliseconds) to hold the keys down.
312+
/// - `wait`: The duration (in milliseconds) to wait after releasing the keys.
313+
///
314+
/// # Returns
315+
/// Returns `Ok(())` if all key combinations are successfully processed, or an error if any RPC call fails.
316+
async fn send_key_combinations(client: &JetKvmRpcClient, combos: Vec<KeyCombo>) -> AnyResult<()> {
317+
for combo in combos {
318+
// Press the keys with the specified modifier.
319+
client
320+
.send_rpc(
321+
"keyboardReport",
322+
json!({
323+
"modifier": combo.modifier,
324+
"keys": combo.keys,
325+
}),
326+
)
327+
.await?;
328+
// Hold the key for the specified duration.
329+
sleep(Duration::from_millis(combo.hold)).await;
330+
// Release all keys.
331+
client
332+
.send_rpc(
333+
"keyboardReport",
334+
json!({
335+
"modifier": 0,
336+
"keys": [],
337+
}),
338+
)
339+
.await?;
340+
// Wait after releasing keys before proceeding.
341+
sleep(Duration::from_millis(combo.wait)).await;
342+
}
343+
Ok(())
344+
}
345+
283346
/// Registers keyboard functions to the provided Lua context.
284347
#[cfg(feature = "lua")]
285348
use mlua::prelude::*;
@@ -376,5 +439,37 @@ pub fn register_lua(lua: &Lua, client: Arc<Mutex<JetKvmRpcClient>>) -> LuaResult
376439
};
377440
lua.globals().set("send_text", send_text_fn)?;
378441

442+
let send_key_combinations_fn = {
443+
let value = client.clone();
444+
// The async function returns a Result; we unwrap it here.
445+
lua.create_async_function(move |_, combos: mlua::Table| {
446+
let client = value.clone();
447+
async move {
448+
let mut key_combos = Vec::new();
449+
// Expecting combos to be an array of tables.
450+
for pair in combos.sequence_values::<mlua::Table>() {
451+
let combo_table = pair?;
452+
let modifier: u64 = combo_table.get("modifier")?;
453+
let keys: Vec<u8> = combo_table.get("keys")?;
454+
// Optional fields: default to hold = 100 ms and wait = 10 ms.
455+
let hold: u64 = combo_table.get("hold").unwrap_or(100);
456+
let wait: u64 = combo_table.get("wait").unwrap_or(10);
457+
key_combos.push(KeyCombo {
458+
modifier,
459+
keys,
460+
hold,
461+
wait,
462+
});
463+
}
464+
send_key_combinations(&*client.lock().await, key_combos)
465+
.await
466+
.map_err(mlua::Error::external)
467+
}
468+
})
469+
}?;
470+
471+
lua.globals()
472+
.set("send_key_combinations", send_key_combinations_fn)?;
473+
379474
Ok(())
380475
}

0 commit comments

Comments
 (0)