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 "/usr/maht/rooster/ppm.dis"; 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 => 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 ; } } }