Plan 9 from Bell Labs’s /usr/web/sources/contrib/maht/limbo/appl/lib/vnc.b

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


implement Vnc;


include "sys.m";
	sys: Sys;
include "draw.m";
	Context: import Draw;

include "bufio.m";
	bufio : Bufio;
	Iobuf: import bufio;

include "math.m";
	math : Math;

include "ppm.m";
	images : Images;
	Image : import images;
	
include "vnc.m";


ServerIO.read(s : self ref ServerIO, n : int) : array of byte
{
	data := array[n] of byte;
	if (s.in.read(data, n) != n) raise "bad read";
	return data;
}

ServerIO.write(s : self ref ServerIO, data : array of byte, n : int)
{
	if (s.out.write(data, n) != n) raise "bad write";
}

ServerIO.read_int(s : self ref ServerIO, byte_count : int) : int
{
	if(byte_count== 1 && (byte_count = s.in.getc()) > 0)
		return byte_count;

	sh := 8 * (byte_count - 1);
	data := s.read(byte_count);
	i := 0;
	for(k := 0; k < byte_count; k++) {
		i += (int data[k]) << sh;
		sh -= 8;
	}
	return i;
}

ServerIO.read_rect(s : self ref ServerIO, byte_count : int) : ref Rect
{
	return ref Rect(s.read_int(byte_count), s.read_int(byte_count), s.read_int(byte_count), s.read_int(byte_count));
}
ServerIO.read_string(s : self ref ServerIO) : string
{
	return string s.read(s.read_int(4));
}
 
ServerIO.read_sint(s : self ref ServerIO, byte_count : int) : int
{
	return s.read_int(byte_count); # TODO signed
}

ServerIO.write_rect(s : self ref ServerIO, r : ref Rect, byte_count : int)
{
	s.write_int(r.x, byte_count);
	s.write_int(r.y, byte_count);
	s.write_int(r.w, byte_count);
	s.write_int(r.h, byte_count);
}

ServerIO.write_int(s : self ref ServerIO, i, byte_count : int)
{
	if(byte_count == 1) {
		if(s.out.putc(i) < 0) raise "bad write";
	} else {
		data := array[4] of byte;
		sh := 8 * (4 - byte_count);
		ints := array[] of {i << sh};
		math->export_int(data, ints);
		s.write(data, byte_count);
	}
}

Server.send_framebuffer_request(s : self ref Server, incremental : int, r : ref Rect)
{
	s.io.write_int(3, 1);
	s.io.write_int(incremental, 1);
	s.io.write_rect(r, 2);
	s.io.out.flush();
}

Server.init(s : self ref Server)
{
	s.width = s.io.read_int(2);
	s.height = s.io.read_int(2);
	s.bpp = s.io.read_int(1);
	s.depth = s.io.read_int(1);
	s.big_endian = s.io.read_int(1); # ignored
	s.truecolour = s.io.read_int(1);
	s.redmax = s.io.read_int(2);
	s.greenmax = s.io.read_int(2);
	s.bluemax = s.io.read_int(2);
	s.redshift = s.io.read_int(1);
	s.greenshift = s.io.read_int(1);
	s.blueshift = s.io.read_int(1);
	s.io.read(3);
	s.name = string s.io.read(s.io.read_int(4));

	s.updates = chan of ref Rect;

#	if(s.bpp == 32 && 255 == s.redmax == s.greenmax == s.bluemax)
		s.image = images->new_rgba8(s.width, s.height) ;
#	else
#		raise "only 32bpp 8bit images coped with, sorry n that";
}

Server.to_string(s : self ref Server) : string
{
	txt := "Desktop Name : " + s.name + "\n" + string s.width + "x" + string s.height + "x" + string s.depth + " (" + string s.bpp + ")\n\tbig endian: " + string s.big_endian + "\n\ttruecolor: " + string s.truecolour + "\n\tmax : " + string s.redmax + "," + string s.greenmax + "," + string s.bluemax + "\n\tshift : " + string s.redshift + "," + string s.greenshift + "," + string s.blueshift;
#	s.framebuffer = array[s.height] of { * => array[s.width] of Pixel } ;

	return txt;
}

Server.set_exclusive(s : self ref Server, exclusive : int)
{
	if(exclusive == 0)
		s.io.write_int(0, 1);
	else
		s.io.write_int(1, 1);

	s.io.out.flush();
}

exec_cmd(cmd : string) : ref ServerIO
{
	n := array[15] of byte; # too lazy to find out proper length
	clone := sys->open("/cmd/clone", Sys->OREAD);
	if(clone == nil) raise "perhaps you didn't do bind -a \"#C\" /";
	n[sys->read(clone, n, len n -1)] = byte 0;
	in := bufio->open("/cmd/" + string n + "/data", Bufio->OREAD);
	out := bufio->open("/cmd/" + string n + "/data", Bufio->OWRITE);
	ctl := sys->open("/cmd/" + string n + "/ctl", Bufio->ORDWR);
	sys->write(ctl, array of byte cmd, len array of byte cmd);
	return ref ServerIO.cmd(in, out, clone, ctl);
}

dial_svr(addr : string) : ref ServerIO
{
	(s, c) := sys->dial(addr, nil);
	if(s == -1) raise "dial failed";
	
	in := bufio->fopen(c.dfd, Bufio->OREAD);
	out := bufio->fopen(c.dfd, Bufio->OWRITE);

	return ref ServerIO.conn(in, out, c);
}

connect(addr : string) : ref Server
{
	s := ref Server;
	(i, bits) := sys->tokenize(addr, "!");
	if(i < 2) raise "VNC Address required  xxx!yyyy[!zzzz]";
	case hd bits {
	"cmd" =>
		s.io = exec_cmd(hd tl bits);
	* =>
		s.io = dial_svr(addr);
	}
	return s;
}

init(nil: ref Context, nil: list of string)
{
	sys = load Sys Sys->PATH;
	math = load Math Math->PATH;
	bufio = load Bufio Bufio->PATH;
	images = load Images Images->PATH;
	images->init(nil, nil);
}

new_server(addr, password : string, protocol_ver, client_security_type, exclusive : int) : ref Server
{
	s := connect(addr);
	s.handshake(password, protocol_ver, client_security_type, exclusive);
	s.init();
	spawn listen_to_server(s);
	return s;
}


Server.handshake(s : self ref Server, password : string, protocol_ver, client_security_type, exclusive : int) {
	protomsg := s.io.read(8);
	if(string protomsg != "RFB 003.") raise "unsupported protocol";
	proto := int string s.io.read(3);
	s.io.read(1); # \n

	if(proto < protocol_ver) raise "server doesn't support high enough protocol ver.";
	if(proto > protocol_ver)
		s.protocol = protocol_ver;
	else
		s.protocol = proto;

	s.io.out.puts(sys->sprint("RFB 003.%03d\n", s.protocol));
	s.io.out.flush();

	chosen_security_type : int;
	if(s.protocol < 7) {
		case s.io.read_int(4) { # security type 
		0 =>
			raise "Invalid Security Type 0";
		1 => # None
			chosen_security_type = 1;
		2 =>
			password = password;
			raise "VNC Security Type is not supported"; 
		5 =>
			raise "RA2 Security Type is not supported"; 
		6 =>
			raise "RA2ne Security Type is not supported";
		16 =>
			raise "Tight Security Type is not supported";
		17 =>
			raise "Ultra Security Type is not supported";
		18 =>
			raise "TLS Security Type is not supported";
		19 =>
			raise "VeNCrypt Security Type is not supported";  
		* =>
			raise "Unknown Security Type is not supported";
		}
	} else {
		numsec := s.io.read_int(1);
		reason := "";
		if(numsec == 0)
			reason = s.io.read_string();
		else {
			for(i := 0; i < numsec; i++)
				if(client_security_type == s.io.read_int(1))
					chosen_security_type = client_security_type;
			if(chosen_security_type == client_security_type) 
				s.io.write_int(chosen_security_type, 1);
			else
				reason = "Security type unavailable";
		}
		if(reason != "")
			raise reason;

	}
	security_result := "";
	case chosen_security_type {
	1 => # None
		;
	* =>
		if(s.protocol < 8) {
			case s.io.read_int(4) {
			0 => #ok
				;
			1 => security_result = "failed";
				* => security_result = "invalid failure code";
			}
		} else {
			security_result = s.io.read_string();
		}
	}

	if(security_result != "") raise security_result;

	s.set_exclusive(exclusive);
}

Server.update_fb_raw(s : self ref Server, r : ref Rect)
{
	pick bm := s.image.bitmap {
		rgba8 =>
			rowbytes := s.width * 4;
			offset := r.x * 4 + r.y * rowbytes;
			for(j := 0; j < r.h; j++) {
				s.io.in.read(bm.pixels[offset:], r.w * 4);
				offset += rowbytes;
			}
		* =>
			raise "only rgb 8bit clients supported";
	}
}

Server.update_framebuffer(s : self ref Server, num_rectangles : int)
{
	while(num_rectangles--) {
		r := s.io.read_rect(2);
		case s.io.read_sint(4) {
		0 => # Raw
			s.update_fb_raw(r);
		1 => # CopyRect
			;
		2 => # RRE
			;
		}
		s.updates <- = r;
	}
}

Server.key_event(s : self ref Server, k, down : int)
{
	s.io.write_int(4, 1);
	s.io.write_int(down, 1);
	s.io.write_int(0, 2);
	s.io.write_int(k, 4);
}

Server.key_press(s : self ref Server, k : int)
{
	s.key_event(k, 1);
	s.key_event(k, 0);
}

Server.alt_key_press(s : self ref Server, k : int)
{
	s.key_event(16rffe9, 1); # left alt
	s.key_event(k, 1);
	s.key_event(k, 0);
	s.key_event(16rffe9, 0);
}

Server.ctrl_key_press(s : self ref Server, k : int)
{
	s.key_event(16rffe3, 1); # left ctrl
	s.key_event(k, 1);
	s.key_event(k, 0);
	s.key_event(16rffe3, 0);
}

Server.string_press(s : self ref Server, txt : string)
{
	for(i := 0; i < len txt; i++)
		s.key_press(txt[i]);
}

Rect.to_string(r : self ref Rect) : string
{
	return "x " + string r.x + " y " + string r.y + " w " + string r.w + " h " + string r.h;
}

listen_to_server(s : ref Server)
{
	msgtype, num_rectangles : int;
	for(msgtype = s.io.in.getc(); msgtype > -1; msgtype = s.io.in.getc()) {
		if(s.io.in.getc() < 0) raise "bad padding read";
		case (msgtype) {
		0 => # Framebuffer_Update
			if((num_rectangles = s.io.read_int(2)) > 0)
				s.update_framebuffer(num_rectangles);
		1 =># Set_Colour_Map_Entries
			;
		2 =># Bell 
			;
		3 =>	 # Server_Cut_Text
			;
		255 =>  # Anthony_Liguori
			;
		254 or 127 =># VMWare
			;
		253 => # gii
			;
		}

	}
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].