use futures::TryStreamExt;
use tokio_stream::wrappers::ReceiverStream;
use std::{
future::Future,
sync::{
atomic::{AtomicU32, Ordering},
Arc,
},
};
use tokio::{
io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt},
sync::{
mpsc::{channel, error::TryRecvError, Receiver, Sender},
oneshot, Mutex,
},
};
use tokio_util::compat::*;
use tracing::*;
use crate::{codec, PixelFormat, Rect, VncEncoding, VncError, VncEvent, X11Event};
const NETWORK_CHANNEL_SIZE: usize = 4096;
const INPUT_CHANNEL_SIZE: usize = 4096;
const OUTPUT_CHANNEL_SIZE: usize = 2;
mod output;
#[cfg(not(target_arch = "wasm32"))]
use tokio::spawn;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_futures::spawn_local as spawn;
use super::messages::{ClientMsg, ServerMsg};
use super::resize::DesktopState;
struct ImageRect {
rect: Rect,
encoding: VncEncoding,
}
impl TryFrom<[u8; 12]> for ImageRect {
type Error = VncError;
fn try_from(buf: [u8; 12]) -> Result<Self, VncError> {
Ok(Self {
rect: Rect {
x: ((buf[0] as u16) << 8) | buf[1] as u16,
y: ((buf[2] as u16) << 8) | buf[3] as u16,
width: ((buf[4] as u16) << 8) | buf[5] as u16,
height: ((buf[6] as u16) << 8) | buf[7] as u16,
},
encoding: VncEncoding::from_wire(
((buf[8] as u32) << 24)
| ((buf[9] as u32) << 16)
| ((buf[10] as u32) << 8)
| (buf[11] as u32),
)?,
})
}
}
impl ImageRect {
async fn read<S>(reader: &mut S) -> Result<Self, VncError>
where
S: AsyncRead + Unpin,
{
let mut rect_buf = [0_u8; 12];
reader.read_exact(&mut rect_buf).await?;
rect_buf.try_into()
}
}
fn pack_screen((width, height): (u16, u16)) -> u32 {
(u32::from(width) << 16) | u32::from(height)
}
fn unpack_screen(packed: u32) -> (u16, u16) {
((packed >> 16) as u16, packed as u16)
}
struct VncInner {
name: String,
screen: Arc<AtomicU32>,
desktop: Arc<DesktopState>,
input_ch: Sender<ClientMsg>,
output_ch: Receiver<VncEvent>,
decoding_stop: Option<oneshot::Sender<()>>,
net_conn_stop: Option<oneshot::Sender<()>>,
closed: bool,
}
impl VncInner {
async fn new<S>(
mut stream: S,
shared: bool,
mut pixel_format: Option<PixelFormat>,
encodings: Vec<VncEncoding>,
) -> Result<Self, VncError>
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
let (conn_ch_tx, conn_ch_rx) = channel(NETWORK_CHANNEL_SIZE);
let (input_ch_tx, input_ch_rx) = channel(INPUT_CHANNEL_SIZE);
let (output_ch_tx, output_ch_rx) = channel(OUTPUT_CHANNEL_SIZE);
let (decoding_stop_tx, mut decoding_stop_rx) = oneshot::channel();
let (net_conn_stop_tx, net_conn_stop_rx) = oneshot::channel();
trace!("client init msg");
send_client_init(&mut stream, shared).await?;
trace!("server init msg");
let (name, (width, height)) =
read_server_init(&mut stream, &mut pixel_format, &|e| async {
output_ch_tx.send(e).await?;
Ok(())
})
.await?;
let screen = Arc::new(AtomicU32::new(pack_screen((width, height))));
let decoder_screen = Arc::clone(&screen);
let desktop = Arc::new(DesktopState::default());
let decoder_desktop = Arc::clone(&desktop);
let network_desktop = Arc::clone(&desktop);
trace!("client encodings: {:?}", encodings);
send_client_encoding(&mut stream, encodings.clone()).await?;
trace!("Require the first frame");
input_ch_tx
.send(ClientMsg::FramebufferUpdateRequest(
Rect {
x: 0,
y: 0,
width,
height,
},
0,
))
.await?;
spawn(async move {
trace!("Decoding thread starts");
let mut conn_ch_rx = {
let conn_ch_rx = ReceiverStream::new(conn_ch_rx).into_async_read();
FuturesAsyncReadCompatExt::compat(conn_ch_rx)
};
let output_func = |e| async {
output_ch_tx.send(e).await?;
Ok(())
};
let pf = pixel_format.as_ref().unwrap();
let result = asycn_vnc_read_loop(
&mut conn_ch_rx,
pf,
&output_func,
&mut decoding_stop_rx,
&encodings,
&decoder_screen,
&decoder_desktop,
)
.await;
drop(conn_ch_rx);
decoder_desktop.close();
if let Err(error) = result {
output::report_error(error, &output_ch_tx, &mut decoding_stop_rx).await;
}
trace!("Decoding thread stops");
});
spawn(async move {
trace!("Net Connection thread starts");
let _ =
async_connection_process_loop(stream, input_ch_rx, conn_ch_tx, net_conn_stop_rx)
.await;
network_desktop.close();
trace!("Net Connection thread stops");
});
info!("VNC Client {name} starts");
Ok(Self {
name,
screen,
desktop,
input_ch: input_ch_tx,
output_ch: output_ch_rx,
decoding_stop: Some(decoding_stop_tx),
net_conn_stop: Some(net_conn_stop_tx),
closed: false,
})
}
fn input_message(&self, event: X11Event) -> Result<ClientMsg, VncError> {
if self.closed {
Err(VncError::ClientNotRunning)
} else {
let (width, height) = unpack_screen(self.screen.load(Ordering::Acquire));
let msg = match event {
X11Event::Refresh => ClientMsg::FramebufferUpdateRequest(
Rect {
x: 0,
y: 0,
width,
height,
},
1,
),
X11Event::FullRefresh => ClientMsg::FramebufferUpdateRequest(
Rect {
x: 0,
y: 0,
width,
height,
},
0, ),
X11Event::KeyEvent(key) => ClientMsg::KeyEvent(key.keycode, key.down),
X11Event::PointerEvent(mouse) => {
ClientMsg::PointerEvent(mouse.position_x, mouse.position_y, mouse.bottons)
}
X11Event::CopyText(text) => {
if text.len() > crate::limits::MAX_TEXT {
return Err(VncError::InvalidImageData);
}
ClientMsg::ClientCutText(text)
}
};
Ok(msg)
}
}
async fn recv_event(&mut self) -> Result<VncEvent, VncError> {
if self.closed {
Err(VncError::ClientNotRunning)
} else {
match self.output_ch.recv().await {
Some(e) => Ok(e),
None => {
self.closed = true;
Err(VncError::ClientNotRunning)
}
}
}
}
async fn poll_event(&mut self) -> Result<Option<VncEvent>, VncError> {
if self.closed {
Err(VncError::ClientNotRunning)
} else {
match self.output_ch.try_recv() {
Err(TryRecvError::Disconnected) => {
self.closed = true;
Err(VncError::ClientNotRunning)
}
Err(TryRecvError::Empty) => Ok(None),
Ok(e) => Ok(Some(e)),
}
}
}
fn close(&mut self) -> Result<(), VncError> {
self.desktop.close();
if self.net_conn_stop.is_some() {
let net_conn_stop: oneshot::Sender<()> = self.net_conn_stop.take().unwrap();
let _ = net_conn_stop.send(());
}
if self.decoding_stop.is_some() {
let decoding_stop = self.decoding_stop.take().unwrap();
let _ = decoding_stop.send(());
}
self.closed = true;
Ok(())
}
}
impl Drop for VncInner {
fn drop(&mut self) {
info!("VNC Client {} stops", self.name);
let _ = self.close();
}
}
pub struct VncClient {
inner: Arc<Mutex<VncInner>>,
desktop: Arc<DesktopState>,
input_ch: Sender<ClientMsg>,
}
impl VncClient {
pub(super) async fn new<S>(
stream: S,
shared: bool,
pixel_format: Option<PixelFormat>,
encodings: Vec<VncEncoding>,
) -> Result<Self, VncError>
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
let inner = VncInner::new(stream, shared, pixel_format, encodings).await?;
Ok(Self {
desktop: Arc::clone(&inner.desktop),
input_ch: inner.input_ch.clone(),
inner: Arc::new(Mutex::new(inner)),
})
}
pub fn desktop_layout(&self) -> Option<crate::DesktopLayout> {
self.desktop.layout()
}
#[cfg(not(target_arch = "wasm32"))]
pub async fn resize_desktop(
&self,
width: u16,
height: u16,
) -> Result<crate::DesktopLayout, crate::ResizeError> {
self.desktop.request(&self.input_ch, width, height).await
}
pub async fn input(&self, event: X11Event) -> Result<(), VncError> {
let sender = {
let inner = self.inner.lock().await;
if inner.closed {
return Err(VncError::ClientNotRunning);
}
inner.input_ch.clone()
};
let permit = sender.reserve().await?;
let inner = self.inner.lock().await;
permit.send(inner.input_message(event)?);
Ok(())
}
pub async fn recv_event(&self) -> Result<VncEvent, VncError> {
self.inner.lock().await.recv_event().await
}
pub async fn poll_event(&self) -> Result<Option<VncEvent>, VncError> {
self.inner.lock().await.poll_event().await
}
pub async fn close(&self) -> Result<(), VncError> {
self.inner.lock().await.close()
}
}
impl Clone for VncClient {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
desktop: Arc::clone(&self.desktop),
input_ch: self.input_ch.clone(),
}
}
}
async fn send_client_init<S>(stream: &mut S, shared: bool) -> Result<(), VncError>
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
trace!("Send shared flag: {}", shared);
stream.write_u8(shared as u8).await?;
Ok(())
}
async fn read_server_init<S, F, Fut>(
stream: &mut S,
pf: &mut Option<PixelFormat>,
output_func: &F,
) -> Result<(String, (u16, u16)), VncError>
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
F: Fn(VncEvent) -> Fut,
Fut: Future<Output = Result<(), VncError>>,
{
let screen_width = stream.read_u16().await?;
let screen_height = stream.read_u16().await?;
crate::limits::dimensions(screen_width, screen_height)?;
let mut send_our_pf = false;
output_func(VncEvent::SetResolution(
(screen_width, screen_height).into(),
))
.await?;
let pixel_format = PixelFormat::read(stream).await?;
if pf.is_none() {
output_func(VncEvent::SetPixelFormat(pixel_format)).await?;
let _ = pf.insert(pixel_format);
} else {
send_our_pf = true;
}
let name = crate::limits::string(stream, crate::limits::MAX_NAME).await?;
if send_our_pf {
trace!("Send customized pixel format {:#?}", pf);
ClientMsg::SetPixelFormat(*pf.as_ref().unwrap())
.write(stream)
.await?;
}
Ok((name, (screen_width, screen_height)))
}
async fn send_client_encoding<S>(
stream: &mut S,
encodings: Vec<VncEncoding>,
) -> Result<(), VncError>
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
ClientMsg::SetEncodings(encodings).write(stream).await?;
Ok(())
}
async fn asycn_vnc_read_loop<S, F, Fut>(
stream: &mut S,
pf: &PixelFormat,
output_func: &F,
stop_ch: &mut oneshot::Receiver<()>,
encodings: &[VncEncoding],
screen: &AtomicU32,
desktop: &DesktopState,
) -> Result<(), VncError>
where
S: AsyncRead + Unpin,
F: Fn(VncEvent) -> Fut,
Fut: Future<Output = Result<(), VncError>>,
{
tokio::select! {
biased;
_ = stop_ch => Ok(()),
result = read_vnc_messages(stream, pf, output_func, encodings, screen, desktop) => result,
}
}
async fn read_vnc_messages<S, F, Fut>(
stream: &mut S,
pf: &PixelFormat,
output_func: &F,
encodings: &[VncEncoding],
shared_screen: &AtomicU32,
desktop: &DesktopState,
) -> Result<(), VncError>
where
S: AsyncRead + Unpin,
F: Fn(VncEvent) -> Fut,
Fut: Future<Output = Result<(), VncError>>,
{
let mut raw_decoder = codec::RawDecoder::new();
let mut zrle_decoder = codec::ZrleDecoder::new();
let mut tight_decoder = codec::TightDecoder::new();
let mut trle_decoder = codec::TrleDecoder::new();
let mut cursor = codec::CursorDecoder::new();
let mut screen = unpack_screen(shared_screen.load(Ordering::Acquire));
loop {
let server_msg = ServerMsg::read(stream).await?;
trace!("Server message got: {:?}", server_msg);
match server_msg {
ServerMsg::FramebufferUpdate(rect_num) => {
let mut updates = crate::desktop::UpdateBatch::default();
let mut framebuffer_changed = false;
for _ in 0..rect_num {
let rect = ImageRect::read(stream).await?;
if rect.encoding != VncEncoding::Raw && !encodings.contains(&rect.encoding) {
return Err(VncError::InvalidImageData);
}
if !matches!(
rect.encoding,
VncEncoding::DesktopSizePseudo
| VncEncoding::ExtendedDesktopSizePseudo
| VncEncoding::LastRectPseudo
| VncEncoding::CursorPseudo
) {
crate::limits::rectangle(&rect.rect, screen)?;
}
if !matches!(
rect.encoding,
VncEncoding::CursorPseudo
| VncEncoding::ExtendedDesktopSizePseudo
| VncEncoding::LastRectPseudo
) {
if !updates.is_empty() {
return Err(VncError::InvalidImageData);
}
framebuffer_changed = true;
}
match rect.encoding {
VncEncoding::Raw => {
raw_decoder
.decode(pf, &rect.rect, stream, output_func)
.await?;
}
VncEncoding::CopyRect => {
let source_x = stream.read_u16().await?;
let source_y = stream.read_u16().await?;
let mut src_rect = rect.rect;
src_rect.x = source_x;
src_rect.y = source_y;
crate::limits::rectangle(&src_rect, screen)?;
output_func(VncEvent::Copy(rect.rect, src_rect)).await?;
}
VncEncoding::Tight => {
tight_decoder
.decode(pf, &rect.rect, stream, output_func)
.await?;
}
VncEncoding::Trle => {
trle_decoder
.decode(pf, &rect.rect, stream, output_func)
.await?;
}
VncEncoding::Zrle => {
zrle_decoder
.decode(pf, &rect.rect, stream, output_func)
.await?;
}
VncEncoding::CursorPseudo => {
cursor.decode(pf, &rect.rect, stream, output_func).await?;
}
VncEncoding::ExtendedDesktopSizePseudo => {
if framebuffer_changed {
return Err(VncError::InvalidImageData);
}
updates.push(crate::DesktopUpdate::read(stream, rect.rect).await?)?;
}
VncEncoding::DesktopSizePseudo => {
crate::limits::dimensions(rect.rect.width, rect.rect.height)?;
if rect.rect.x != 0 || rect.rect.y != 0 {
return Err(VncError::InvalidImageData);
}
screen = (rect.rect.width, rect.rect.height);
shared_screen.store(pack_screen(screen), Ordering::Release);
desktop.legacy_resize();
output_func(VncEvent::SetResolution(
(rect.rect.width, rect.rect.height).into(),
))
.await?;
}
VncEncoding::LastRectPseudo => {
break;
}
}
}
for update in updates.into_updates() {
if let Some(layout) = &update.layout {
screen = (layout.width, layout.height);
shared_screen.store(pack_screen(screen), Ordering::Release);
}
desktop.observe(&update);
output_func(VncEvent::DesktopUpdate(update)).await?;
}
}
ServerMsg::Bell => {
output_func(VncEvent::Bell).await?;
}
ServerMsg::ServerCutText(text) => {
output_func(VncEvent::Text(text)).await?;
}
}
}
}
async fn async_connection_process_loop<S>(
mut stream: S,
mut input_ch: Receiver<ClientMsg>,
conn_ch: Sender<std::io::Result<Vec<u8>>>,
mut stop_ch: oneshot::Receiver<()>,
) -> Result<(), VncError>
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
let mut buffer = [0; 65535];
let mut pending = 0;
loop {
tokio::select! {
_ = &mut stop_ch => break,
_ = conn_ch.closed() => break,
permit = conn_ch.reserve(), if pending > 0 => {
match permit {
Ok(permit) => {
permit.send(Ok(buffer[..pending].to_vec()));
pending = 0;
}
Err(_) => break,
}
}
result = stream.read(&mut buffer), if pending == 0 => {
match result {
Ok(0) | Err(_) => break,
Ok(length) => pending = length,
}
}
message = input_ch.recv() => {
let Some(message) = message else { break; };
tokio::select! {
biased;
_ = &mut stop_ch => break,
result = message.write(&mut stream) => result?,
}
}
}
}
Ok(())
}
#[cfg(test)]
mod tests;
#[cfg(test)]
mod queue_tests;
#[cfg(test)]
mod resize_tests;