use anyhow::{bail, 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::rfb::{
FramebufferUpdate, KeyEvent, PixelFormat, ProtoVersion, Rectangle, SecurityType, SecurityTypes,
};
use rfb::{
pixel_formats::rgb_888,
server::{Server, VncServer, VncServerConfig, VncServerData},
};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
const WIDTH: usize = 1024;
const HEIGHT: usize = 768;
#[derive(Parser, Debug)]
struct Args {
#[clap(value_enum, short, long, default_value_t = Image::Oxide)]
image: Image,
#[clap(long, default_value_t = false, action = clap::ArgAction::Set)]
big_endian: bool,
#[clap(short, long, default_value_t = 0)]
red_order: u8,
#[clap(short, long, default_value_t = 1)]
green_order: u8,
#[clap(short, long, default_value_t = 2)]
blue_order: u8,
}
#[derive(ValueEnum, Debug, Copy, Clone)]
enum Image {
Oxide,
TestTubes,
Red,
Green,
Blue,
White,
Black,
}
#[derive(Clone)]
struct ExampleServer {
display: Image,
rgb_order: (u8, u8, u8),
big_endian: bool,
}
#[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,
);
info!(
"Starting server: image: {:?}, pixel format; {:#?}",
args.image, pf
);
let config = VncServerConfig {
addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9000),
version: ProtoVersion::Rfb38,
sec_types: SecurityTypes(vec![SecurityType::None, SecurityType::VncAuthentication]),
name: "rfb-example-server".to_string(),
};
let data = VncServerData {
width: WIDTH as u16,
height: HEIGHT as u16,
input_pixel_format: pf.clone(),
};
let server = ExampleServer {
display: args.image,
rgb_order: (args.red_order, args.green_order, args.blue_order),
big_endian: args.big_endian,
};
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;
}
if x == 3 {
x = 0;
} else {
x += 1;
}
}
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;
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);
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;
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
}
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;
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],
}
}
#[async_trait]
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 r = Rectangle::new(
0,
0,
pixels_width,
pixels_height,
Box::new(RawEncoding::new(pixels)),
);
FramebufferUpdate::new(vec![r])
}
async fn key_event(&self, _ke: KeyEvent) {}
}