Skip to content

Commit

Permalink
Add text transmission
Browse files Browse the repository at this point in the history
Enable sending text to the VNC server
  • Loading branch information
ByteOtter committed Jul 23, 2024
1 parent b710382 commit 61caae3
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 7 deletions.
1 change: 1 addition & 0 deletions isototest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ repository = "https://github.com/ByteOtter/isotest-ng/tree/main/isototest"
license = "GPL-2.0"

[dependencies]
async-winit = "0.2.1"
mockito = "1.4.0"
tokio = "1.38.1"
vnc-rs = "0.5.1"
151 changes: 151 additions & 0 deletions isototest/src/action.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// SPDX-FileCopyrightText: Christopher Hock <[email protected]>
// SPDX-LicenseIdentifier: GPL-2.0-or-later
//! # Action
//!
//! This module handles interactions between the VncClient and VncServer.
use async_winit::event::VirtualKeyCode;
use vnc::{client::VncClient, ClientKeyEvent, VncError, X11Event};

/// Write given text to console
///
/// Uses `X11Event`s to send keypresses to the server. According to the [RFC](https://www.rfc-editor.org/rfc/rfc6143.html#section-7.5.4)
/// it does not matter whether the X-Window System is running or not.
///
/// # Parameters
///
/// * client: `&VncClient` - The client to be used for connections
/// * text: `String` - The text to write.
///
/// # Returns
///
/// * `Ok(())` - If the transaction has been successfully completed.
/// * `VncError` - If the transaction fails.
pub async fn write_to_console(client: &VncClient, text: &str) -> Result<(), VncError> {
// Translate each character to a keycode
let mut keycode: Option<u32>;
for ch in text.chars() {
// Translate each character to its corresponding keycode.
keycode = char_to_keycode(ch);
// Return error if key is not supported.
// TODO: This may be removed, as soon as special keys are implemented.
if keycode.is_none() {
let e: VncError = VncError::General(format!("Unable to identify character '{}'!", ch));
return Err(e);
}
// Setup press events.
let mut keyevent: ClientKeyEvent = ClientKeyEvent {
keycode: keycode.unwrap(),
down: true,
};
let mut x_event: X11Event = X11Event::KeyEvent(keyevent);

// Send individual keypresses.
match client.input(x_event).await {
Ok(_) => {}
Err(e) => return Err(e),
}

// NOTE: Is this really necessary?
// Setup key release events.
keyevent = ClientKeyEvent {
keycode: keycode.unwrap(),
down: false,
};
x_event = X11Event::KeyEvent(keyevent);

// Send key releases.
match client.input(x_event).await {
Ok(_) => {}
Err(e) => return Err(e),
}
}
Ok(())
}

/// Assign a given character its corresponding `VirtualKeyCode`.
///
/// NOTE: This is only to be used in combination with sending text. Special characters and command
/// sequences are not yet implemented.
///
/// # Parameters
///
/// * c: `char` - The character to look up.
///
/// # Returns
///
/// * `Some(VirtualKeyCode)` - The `VirtualKeyCode` corresponding to the character.
/// * `None` - If the character is not supported.
fn char_to_keycode(c: char) -> Option<u32> {
let keycode = match c {
'1' => Some(VirtualKeyCode::Key1),
'2' => Some(VirtualKeyCode::Key2),
'3' => Some(VirtualKeyCode::Key3),
'4' => Some(VirtualKeyCode::Key4),
'5' => Some(VirtualKeyCode::Key5),
'6' => Some(VirtualKeyCode::Key6),
'7' => Some(VirtualKeyCode::Key7),
'8' => Some(VirtualKeyCode::Key8),
'9' => Some(VirtualKeyCode::Key9),
'0' => Some(VirtualKeyCode::Key0),
'a' | 'A' => Some(VirtualKeyCode::A),
'b' | 'B' => Some(VirtualKeyCode::B),
'c' | 'C' => Some(VirtualKeyCode::C),
'd' | 'D' => Some(VirtualKeyCode::D),
'e' | 'E' => Some(VirtualKeyCode::E),
'f' | 'F' => Some(VirtualKeyCode::F),
'g' | 'G' => Some(VirtualKeyCode::G),
'h' | 'H' => Some(VirtualKeyCode::H),
'i' | 'I' => Some(VirtualKeyCode::I),
'j' | 'J' => Some(VirtualKeyCode::J),
'k' | 'K' => Some(VirtualKeyCode::K),
'l' | 'L' => Some(VirtualKeyCode::L),
'm' | 'M' => Some(VirtualKeyCode::M),
'n' | 'N' => Some(VirtualKeyCode::N),
'o' | 'O' => Some(VirtualKeyCode::O),
'p' | 'P' => Some(VirtualKeyCode::P),
'q' | 'Q' => Some(VirtualKeyCode::Q),
'r' | 'R' => Some(VirtualKeyCode::R),
's' | 'S' => Some(VirtualKeyCode::S),
't' | 'T' => Some(VirtualKeyCode::T),
'u' | 'U' => Some(VirtualKeyCode::U),
'v' | 'V' => Some(VirtualKeyCode::V),
'w' | 'W' => Some(VirtualKeyCode::W),
'x' | 'X' => Some(VirtualKeyCode::X),
'y' | 'Y' => Some(VirtualKeyCode::Y),
'z' | 'Z' => Some(VirtualKeyCode::Z),
' ' => Some(VirtualKeyCode::Space),
'!' => Some(VirtualKeyCode::Key1),
'@' => Some(VirtualKeyCode::Key2),
'#' => Some(VirtualKeyCode::Key3),
'$' => Some(VirtualKeyCode::Key4),
'%' => Some(VirtualKeyCode::Key5),
'^' => Some(VirtualKeyCode::Caret),
'&' => Some(VirtualKeyCode::Key7),
'*' => Some(VirtualKeyCode::Key8),
'(' => Some(VirtualKeyCode::Key9),
')' => Some(VirtualKeyCode::Key0),
'-' => Some(VirtualKeyCode::Minus),
'_' => Some(VirtualKeyCode::Underline),
'=' => Some(VirtualKeyCode::Equals),
'+' => Some(VirtualKeyCode::Plus),
'[' => Some(VirtualKeyCode::LBracket),
']' => Some(VirtualKeyCode::RBracket),
'{' => Some(VirtualKeyCode::LBracket),
'}' => Some(VirtualKeyCode::RBracket),
'\\' => Some(VirtualKeyCode::Backslash),
'|' => Some(VirtualKeyCode::Backslash),
';' => Some(VirtualKeyCode::Semicolon),
':' => Some(VirtualKeyCode::Colon),
'\'' => Some(VirtualKeyCode::Apostrophe),
'"' => Some(VirtualKeyCode::Apostrophe),
',' => Some(VirtualKeyCode::Comma),
'.' => Some(VirtualKeyCode::Period),
'/' => Some(VirtualKeyCode::Slash),
'<' => Some(VirtualKeyCode::Comma),
'>' => Some(VirtualKeyCode::Period),
'?' => Some(VirtualKeyCode::Slash),
_ => None,
};

keycode.map(|kc| kc as u32)
}
4 changes: 2 additions & 2 deletions isototest/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ pub async fn create_vnc_client(
/// returns an error.
pub async fn kill_client(client: VncClient) -> Result<(), VncError> {
match client.close().await {
Ok(_) => {},
Err(e) => return Err(e)
Ok(_) => {}
Err(e) => return Err(e),
};
drop(client);
Ok(())
Expand Down
1 change: 1 addition & 0 deletions isototest/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod action;
pub mod connection;
3 changes: 1 addition & 2 deletions isototest/tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub async fn start_mock_vnc_srv() -> Result<TcpListener, VncError> {
Ok(srv) => {
eprintln!("Mock VNC server started on {:?}", srv.local_addr());
Ok(srv)
},
}
Err(e) => return Err(VncError::IoError(e)),
};
srv
Expand Down Expand Up @@ -62,7 +62,6 @@ pub async fn mock_vnc_handshake(mut socket: TcpStream) -> Result<(), io::Error>
}
eprintln!("Sent security type response.");


// Read the client's security type message (8 bytes)
let mut buf = [0; 8];
eprintln!("Created security receiving buffer '{:?}'.", &buf);
Expand Down
10 changes: 7 additions & 3 deletions isototest/tests/test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::net::SocketAddr;
use isototest::connection::kill_client;
use std::net::SocketAddr;
use tokio::net::TcpListener;
use vnc::client;
use vnc::PixelFormat;
Expand Down Expand Up @@ -87,10 +87,14 @@ async fn test_create_connect_fail() {
#[tokio::test]
#[ignore = "Broken, needs to be fixed."]
async fn test_client_kill() {
let srv = common::start_mock_vnc_srv().await.expect("Failed to start VNC server");
let srv = common::start_mock_vnc_srv()
.await
.expect("Failed to start VNC server");
let addr = srv.local_addr().unwrap().to_string();

let client = create_vnc_client(addr, None).await.expect("[Error] Test 'kill_client' failed. Unable to create client.");
let client = create_vnc_client(addr, None)
.await
.expect("[Error] Test 'kill_client' failed. Unable to create client.");
let result = kill_client(client).await;

assert!(result.is_ok());
Expand Down

0 comments on commit 61caae3

Please sign in to comment.