Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rfb"
version = "0.1.0"
version = "0.2.0"
description = "Implementation of the RFB protocol (RFC 6143), including a server implementation."
repository = "https://github.com/oxidecomputer/rfb"
readme = "README.md"
Expand All @@ -23,10 +23,12 @@ ascii = { version = "1.0", default-features = false }
async-trait = "0.1.53"
bitflags = "1.3.2"
env_logger = "0.9.0"
flate2 = "1.0.32"
futures = "0.3.21"
log = "0.4.17"
thiserror = "1.0"
tokio = { version = "1", features = ["full"] }
cancel-safe-futures = "0.1"

[dev-dependencies]
anyhow = "1.0"
Expand Down
169 changes: 55 additions & 114 deletions examples/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@
//
// Copyright 2022 Oxide Computer Company

use anyhow::{bail, Result};
use anyhow::Result;
use async_trait::async_trait;
use clap::{Parser, ValueEnum};
use env_logger;
use image::io::Reader as ImageReader;
use image::GenericImageView;
use log::info;
use rfb::encodings::RawEncoding;
use rfb::encodings::{RawEncodingRef, ZRLEncoding};
use rfb::pixel_formats::fourcc::FourCC;
use rfb::pixel_formats::transform;
use rfb::rfb::{
FramebufferUpdate, KeyEvent, PixelFormat, ProtoVersion, Rectangle, SecurityType, SecurityTypes,
};
use rfb::{
pixel_formats::rgb_888,
server::{Server, VncServer, VncServerConfig, VncServerData},
ConnectionContext, FramebufferUpdate, KeyEvent, PixelFormat, ProtoVersion, Rectangle,
SecurityType, SecurityTypes,
};
use rfb::server::{Server, VncServer, VncServerConfig, VncServerData};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};

const WIDTH: usize = 1024;
Expand All @@ -43,21 +43,9 @@ struct Args {
#[clap(value_enum, short, long, default_value_t = Image::Oxide)]
image: Image,

/// Pixel endianness
#[clap(long, default_value_t = false, action = clap::ArgAction::Set)]
big_endian: bool,

/// Byte mapping to red (4-byte RGB pixel, endian-agnostic)
#[clap(short, long, default_value_t = 0)]
red_order: u8,

/// Byte mapping to green (4-byte RGB pixel, endian-agnostic)
#[clap(short, long, default_value_t = 1)]
green_order: u8,

/// Byte mapping to blue (4-byte RGB pixel, endian-agnostic)
#[clap(short, long, default_value_t = 2)]
blue_order: u8,
/// Pixel format
#[clap(short, long, default_value = "XR24", action = clap::ArgAction::Set)]
fourcc: FourCC,
}

#[derive(ValueEnum, Debug, Copy, Clone)]
Expand All @@ -74,31 +62,19 @@ enum Image {
#[derive(Clone)]
struct ExampleServer {
display: Image,
rgb_order: (u8, u8, u8),
big_endian: bool,
pixfmt: PixelFormat,
}

#[tokio::main]
async fn main() -> Result<()> {
env_logger::init();

let args = Args::parse();
validate_order(args.red_order, args.green_order, args.blue_order)?;

let pf = PixelFormat::new_colorformat(
rgb_888::BITS_PER_PIXEL,
rgb_888::DEPTH,
args.big_endian,
order_to_shift(args.red_order),
rgb_888::MAX_VALUE,
order_to_shift(args.green_order),
rgb_888::MAX_VALUE,
order_to_shift(args.blue_order),
rgb_888::MAX_VALUE,
);

let pixfmt = PixelFormat::from(&args.fourcc);
info!(
"Starting server: image: {:?}, pixel format; {:#?}",
args.image, pf
args.image, pixfmt
);

let config = VncServerConfig {
Expand All @@ -110,110 +86,70 @@ async fn main() -> Result<()> {
let data = VncServerData {
width: WIDTH as u16,
height: HEIGHT as u16,
input_pixel_format: pf.clone(),
input_pixel_format: pixfmt.clone(),
connection_context: ConnectionContext::default().into(),
};
let server = ExampleServer {
display: args.image,
rgb_order: (args.red_order, args.green_order, args.blue_order),
big_endian: args.big_endian,
pixfmt,
};
let s = VncServer::new(server, config, data);
s.start().await?;

Ok(())
}

fn validate_order(r: u8, g: u8, b: u8) -> Result<()> {
if r > 3 || g > 3 || b > 3 {
bail!("r/g/b must have ordering of 0, 1, 2, or 3");
}

if r == g || r == b || g == b {
bail!("r/g/b must have unique orderings");
}

Ok(())
}

fn order_to_shift(order: u8) -> u8 {
assert!(order <= 3);
(3 - order) * rgb_888::BITS_PER_COLOR
}

fn order_to_index(order: u8, big_endian: bool) -> u8 {
assert!(order <= 3);

if big_endian {
order
} else {
4 - order - 1
}
}

fn generate_color(index: u8, big_endian: bool) -> Vec<u8> {
const LEN: usize = WIDTH * HEIGHT * rgb_888::BYTES_PER_PIXEL;
let mut pixels = vec![0x0u8; LEN];

let idx = order_to_index(index, big_endian);

let mut x = 0;
for i in 0..pixels.len() {
if x == idx {
pixels[i] = 0xff;
}
fn generate_color(img: Image, pixfmt: &PixelFormat) -> Vec<u8> {
let bytes_pp = pixfmt.bits_per_pixel as usize / 8;
let len = WIDTH * HEIGHT * bytes_pp;
let mut pixels = Vec::with_capacity(len);

let color = match img {
Image::Red => 0xFF000000u32.to_le_bytes(),
Image::Green => 0x00FF0000u32.to_le_bytes(),
Image::Blue => 0x0000FF00u32.to_le_bytes(),
Image::White => 0xFFFFFF00u32.to_le_bytes(),
Image::Black => 0u32.to_le_bytes(),
_ => unreachable!(),
};
let bytes = transform(&color, &PixelFormat::from(&FourCC::RX24), pixfmt);

if x == 3 {
x = 0;
} else {
x += 1;
}
while pixels.len() < len {
pixels.extend(&bytes);
}

pixels
}

fn generate_image(name: &str, big_endian: bool, rgb_order: (u8, u8, u8)) -> Vec<u8> {
const LEN: usize = WIDTH * HEIGHT * rgb_888::BYTES_PER_PIXEL;
fn generate_image(name: &str, pixfmt: &PixelFormat) -> Vec<u8> {
const RGBX24_BYTES_PP: usize = 4;
const LEN: usize = WIDTH * HEIGHT * RGBX24_BYTES_PP;

let mut pixels = vec![0xffu8; LEN];

let img = ImageReader::open(name).unwrap().decode().unwrap();

let (r, g, b) = rgb_order;
let r_idx = order_to_index(r, big_endian) as usize;
let g_idx = order_to_index(g, big_endian) as usize;
let b_idx = order_to_index(b, big_endian) as usize;
let x_idx = rgb_888::unused_index(r_idx, g_idx, b_idx);

// Convert the input image pixels to the requested pixel format.
for (x, y, pixel) in img.pixels() {
let ux = x as usize;
let uy = y as usize;

let y_offset = WIDTH * rgb_888::BYTES_PER_PIXEL;
let x_offset = ux * rgb_888::BYTES_PER_PIXEL;
let y_offset = WIDTH * RGBX24_BYTES_PP;
let x_offset = ux * RGBX24_BYTES_PP;
let offset = uy * y_offset + x_offset;

pixels[uy * y_offset + x_offset + r_idx] = pixel[0];
pixels[uy * y_offset + x_offset + g_idx] = pixel[1];
pixels[uy * y_offset + x_offset + b_idx] = pixel[2];
pixels[uy * y_offset + x_offset + x_idx] = pixel[3];
pixels[offset..offset + 4].copy_from_slice(&pixel.0);
}

pixels
transform(&pixels, &PixelFormat::from(&FourCC::XB24), pixfmt)
}

fn generate_pixels(img: Image, big_endian: bool, rgb_order: (u8, u8, u8)) -> Vec<u8> {
const LEN: usize = WIDTH * HEIGHT * rgb_888::BYTES_PER_PIXEL;

let (r, g, b) = rgb_order;

fn generate_pixels(img: Image, pixfmt: &PixelFormat) -> Vec<u8> {
match img {
Image::Oxide => generate_image("example-images/oxide.jpg", big_endian, rgb_order),
Image::TestTubes => generate_image("example-images/test-tubes.jpg", big_endian, rgb_order),
Image::Red => generate_color(r, big_endian),
Image::Green => generate_color(g, big_endian),
Image::Blue => generate_color(b, big_endian),
Image::White => vec![0xffu8; LEN],
Image::Black => vec![0x0u8; LEN],
Image::Oxide => generate_image("example-images/oxide.jpg", pixfmt),
Image::TestTubes => generate_image("example-images/test-tubes.jpg", pixfmt),
Image::Red | Image::Green | Image::Blue | Image::White | Image::Black => {
generate_color(img, pixfmt)
}
}
}

Expand All @@ -222,13 +158,18 @@ impl Server for ExampleServer {
async fn get_framebuffer_update(&self) -> FramebufferUpdate {
let pixels_width = 1024;
let pixels_height = 768;
let pixels = generate_pixels(self.display, self.big_endian, self.rgb_order);
let pixels = generate_pixels(self.display, &self.pixfmt);
let r = Rectangle::new(
0,
0,
pixels_width,
pixels_height,
Box::new(RawEncoding::new(pixels)),
Box::new(ZRLEncoding::from(&RawEncodingRef::new(
&pixels,
pixels_width,
pixels_height,
&self.pixfmt,
))),
);
FramebufferUpdate::new(vec![r])
}
Expand Down
Loading