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
;
}
}
}
|