def _may_compress(self, dtype, dformat, wire_data): if len(wire_data)>256: wire_data = compressed_wrapper("clipboard: %s / %s" % (dtype, dformat), wire_data) if len(wire_data)>self.max_clipboard_packet_size: log.warn("even compressed, clipboard contents are too big and have not been sent:" " %s compressed bytes dropped (maximum is %s)", len(wire_data), self.max_clipboard_packet_size) return None return wire_data
def _may_compress(self, dtype, dformat, wire_data): if len(wire_data) > 256: wire_data = compressed_wrapper( "clipboard: %s / %s" % (dtype, dformat), wire_data) if len(wire_data) > self.max_clipboard_packet_size: log.warn( "even compressed, clipboard contents are too big and have not been sent:" " %s compressed bytes dropped (maximum is %s)", len(wire_data), self.max_clipboard_packet_size) return None return wire_data
def process_server_packet(self, proto, packet): packet_type = packet[0] debug("process_server_packet: %s", packet_type) if packet_type==Protocol.CONNECTION_LOST: self.stop("server connection lost", proto) return elif packet_type=="hello": c = typedict(packet[1]) maxw, maxh = c.intpair("max_desktop_size", (4096, 4096)) proto.max_packet_size = maxw*maxh*4 caps = self.filter_server_caps(c) #add new encryption caps: if self.cipher: auth_caps = new_cipher_caps(self.client_protocol, self.cipher, self.encryption_key) caps.update(auth_caps) packet = ("hello", caps) elif packet_type=="info-response": #adds proxy info: #note: this is only seen by the client application #"xpra info" is a new connection, which talks to the proxy server... info = packet[1] info.update(get_server_info("proxy.")) info.update(get_thread_info("proxy.", proto)) info.update(self.get_encoder_info()) elif packet_type=="lost-window": wid = packet[1] #mark it as lost so we can drop any current/pending frames self.lost_windows.add(wid) #queue it so it gets cleaned safely (for video encoders mostly): self.encode_queue.put(packet) #and fall through so tell the client immediately elif packet_type=="draw": #use encoder thread: self.encode_queue.put(packet) #which will queue the packet itself when done: return elif packet_type=="cursor": #packet = ["cursor", x, y, width, height, xhot, yhot, serial, pixels, name] #or: #packet = ["cursor", ""] if len(packet)>=9: pixels = packet[8] if len(pixels)<64: packet[8] = str(pixels) else: packet[8] = compressed_wrapper("cursor", pixels) self.queue_client_packet(packet)
def process_server_packet(self, proto, packet): packet_type = packet[0] log("process_server_packet: %s", packet_type) if packet_type == Protocol.CONNECTION_LOST: self.stop("server connection lost", proto) return elif packet_type == "hello": c = typedict(packet[1]) maxw, maxh = c.intpair("max_desktop_size", (4096, 4096)) proto.max_packet_size = maxw * maxh * 4 * 4 caps = self.filter_server_caps(c) #add new encryption caps: if self.cipher: auth_caps = new_cipher_caps(self.client_protocol, self.cipher, self.encryption_key) caps.update(auth_caps) packet = ("hello", caps) elif packet_type == "info-response": #adds proxy info: #note: this is only seen by the client application #"xpra info" is a new connection, which talks to the proxy server... info = packet[1] info.update(self.get_proxy_info(proto)) elif packet_type == "lost-window": wid = packet[1] #mark it as lost so we can drop any current/pending frames self.lost_windows.add(wid) #queue it so it gets cleaned safely (for video encoders mostly): self.encode_queue.put(packet) #and fall through so tell the client immediately elif packet_type == "draw": #use encoder thread: self.encode_queue.put(packet) #which will queue the packet itself when done: return elif packet_type == "cursor": #packet = ["cursor", x, y, width, height, xhot, yhot, serial, pixels, name] #or: #packet = ["cursor", ""] if len(packet) >= 9: pixels = packet[8] if len(pixels) < 512: packet[8] = str(pixels) else: packet[8] = compressed_wrapper("cursor", pixels) self.queue_client_packet(packet)
def process_server_packet(self, proto, packet): packet_type = packet[0] debug("process_server_packet: %s", packet_type) if packet_type==Protocol.CONNECTION_LOST: self.stop("server connection lost", proto) return elif packet_type=="hello": c = typedict(packet[1]) maxw, maxh = c.intpair("max_desktop_size", (4096, 4096)) proto.max_packet_size = maxw*maxh*4 caps = self.filter_server_caps(c) #add new encryption caps: if self.cipher: auth_caps = new_cipher_caps(self.client_protocol, self.cipher, self.encryption_key) caps.update(auth_caps) packet = ("hello", caps) elif packet_type=="info-response": #adds proxy info: #note: this is only seen by the client application #"xpra info" is a new connection, which talks to the proxy server... info = packet[1] info.update(get_server_info("proxy.")) info.update(get_thread_info("proxy.", proto)) info.update(self.get_encoder_info()) elif packet_type=="lost-window": wid = packet[1] ve = self.video_encoders.get(wid) if ve: ve.clean() del self.video_encoders[wid] elif packet_type=="draw": self.process_draw(packet) elif packet_type=="cursor": #packet = ["cursor", x, y, width, height, xhot, yhot, serial, pixels, name] #or: #packet = ["cursor", ""] if len(packet)>=9: pixels = packet[8] if len(pixels)<64: packet[8] = str(pixels) else: packet[8] = compressed_wrapper("cursor", pixels) self.queue_client_packet(packet)
def process_server_packet(self, proto, packet): packet_type = packet[0] debug("process_server_packet: %s", packet_type) if packet_type == Protocol.CONNECTION_LOST: self.stop("server connection lost", proto) return elif packet_type == "hello": c = typedict(packet[1]) maxw, maxh = c.intpair("max_desktop_size", (4096, 4096)) proto.max_packet_size = maxw * maxh * 4 caps = self.filter_server_caps(c) #add new encryption caps: if self.cipher: auth_caps = new_cipher_caps(self.client_protocol, self.cipher, self.encryption_key) caps.update(auth_caps) packet = ("hello", caps) elif packet_type == "info-response": #adds proxy info: info = packet[1] info.update(get_server_info("proxy.")) info.update(get_thread_info("proxy.", proto)) elif packet_type == "draw": #packet = ["draw", wid, x, y, outw, outh, coding, data, self._damage_packet_sequence, outstride, client_options] #ensure we don't try to re-compress the pixel data in the network layer: #(re-add the "compressed" marker that gets lost when we re-assemble packets) coding = packet[6] if coding != "mmap": data = packet[7] packet[7] = Compressed("%s pixels" % coding, data) elif packet_type == "cursor": #packet = ["cursor", x, y, width, height, xhot, yhot, serial, pixels, name] #or: #packet = ["cursor", ""] if len(packet) >= 9: pixels = packet[8] if len(pixels) < 64: packet[8] = str(pixels) else: packet[8] = compressed_wrapper("cursor", pixels) self.queue_client_packet(packet)
def process_server_packet(self, proto, packet): packet_type = packet[0] debug("process_server_packet: %s", packet_type) if packet_type==Protocol.CONNECTION_LOST: self.stop("server connection lost", proto) return elif packet_type=="hello": c = typedict(packet[1]) maxw, maxh = c.intpair("max_desktop_size", (4096, 4096)) proto.max_packet_size = maxw*maxh*4 caps = self.filter_server_caps(c) #add new encryption caps: if self.cipher: auth_caps = new_cipher_caps(self.client_protocol, self.cipher, self.encryption_key) caps.update(auth_caps) packet = ("hello", caps) elif packet_type=="info-response": #adds proxy info: info = packet[1] info.update(get_server_info("proxy.")) info.update(get_thread_info("proxy.", proto)) elif packet_type=="draw": #packet = ["draw", wid, x, y, outw, outh, coding, data, self._damage_packet_sequence, outstride, client_options] #ensure we don't try to re-compress the pixel data in the network layer: #(re-add the "compressed" marker that gets lost when we re-assemble packets) coding = packet[6] if coding!="mmap": data = packet[7] packet[7] = Compressed("%s pixels" % coding, data) elif packet_type=="cursor": #packet = ["cursor", x, y, width, height, xhot, yhot, serial, pixels, name] #or: #packet = ["cursor", ""] if len(packet)>=9: pixels = packet[8] if len(pixels)<64: packet[8] = str(pixels) else: packet[8] = compressed_wrapper("cursor", pixels) self.queue_client_packet(packet)
def send_cursor(self): self.send_cursor_pending = False self.cursor_data = X11Keyboard.get_cursor_image() if self.cursor_data is not None: pixels = self.cursor_data[7] log("send_cursor() cursor=%s", self.cursor_data[:7]+["%s bytes" % len(pixels)]+self.cursor_data[8:]) if self.default_cursor_data is not None and str(pixels)==str(self.default_cursor_data[7]): log("send_cursor(): default cursor - clearing it") self.cursor_data = None elif pixels is not None: #convert bytearray to string: pixels = str(pixels) if len(pixels)<64: self.cursor_data[7] = pixels else: self.cursor_data[7] = compressed_wrapper("cursor", pixels) else: log("send_cursor() failed to get cursor image") for ss in self._server_sources.values(): ss.send_cursor(self.cursor_data) return False
def rgb_encode(coding, image, rgb_formats, supports_transparency, speed, rgb_zlib, rgb_lz4, encoding_client_options, supports_rgb24zlib): pixel_format = image.get_pixel_format() #log("rgb_encode%s pixel_format=%s, rgb_formats=%s", (coding, image, rgb_formats, supports_transparency, speed, rgb_zlib, rgb_lz4, encoding_client_options, supports_rgb24zlib), pixel_format, rgb_formats) if pixel_format not in rgb_formats: if not rgb_reformat(image, rgb_formats, supports_transparency): raise Exception( "cannot find compatible rgb format to use for %s! (supported: %s)" % (pixel_format, rgb_formats)) #get the new format: pixel_format = image.get_pixel_format() #switch encoding if necessary: if len(pixel_format) == 4 and coding == "rgb24": coding = "rgb32" elif len(pixel_format) == 3 and coding == "rgb32": coding = "rgb24" #always tell client which pixel format we are sending: options = {"rgb_format": pixel_format} #compress here and return a wrapper so network code knows it is already zlib compressed: pixels = image.get_pixels() #special case for when rowstride is so much bigger than the width #that we would end up sending large chunks of padding with each row of pixels #this happens with XShm pixel data (the default) stride = image.get_rowstride() width = image.get_width() rstride = roundup(width * len(pixel_format), 4) #a reasonable stride: rounded up to 4 height = image.get_height() if stride > 8 and rstride < stride: al = len(pixels) #current buffer size el = rstride * height #desirable size we could have if al - el > 1024 and el * 110 / 100 < al: #is it worth re-striding to save space? #we'll save at least 1KB and 10%, do it #Note: we could also change the pixel format whilst we're at it # and convert BGRX to RGB for example (assuming RGB is also supported by the client) rows = [] for y in range(height): rows.append(pixels[stride * y:stride * y + rstride]) pixels = "".join(rows) log( "rgb_encode: %s pixels re-stride saving %i%% from %s (%s bytes) to %s (%s bytes)", pixel_format, 100 - 100 * el / al, stride, al, rstride, el) stride = rstride #compression #by default, wire=raw: raw_data = str(pixels) wire_data = raw_data level = 0 algo = "not" if rgb_zlib or rgb_lz4: level = max(0, min(5, int(115 - speed) / 20)) if len(pixels) < 1024: #fewer pixels, make it more likely we won't bother compressing: level = level / 2 if level > 0: lz4 = use_lz4 and rgb_lz4 and level <= 3 wire_data = compressed_wrapper(coding, pixels, level=level, lz4=lz4) raw_data = wire_data.data #log("%s/%s data compressed from %s bytes down to %s (%s%%) with lz4=%s", # coding, pixel_format, len(pixels), len(raw_data), int(100.0*len(raw_data)/len(pixels)), self.rgb_lz4) if len(raw_data) >= (len(pixels) - 32): #compressed is actually bigger! (use uncompressed) level = 0 wire_data = str(pixels) raw_data = wire_data else: if lz4: options["lz4"] = True algo = "lz4" else: options["zlib"] = level algo = "zlib" if pixel_format.upper().find("A") >= 0 or pixel_format.upper().find( "X") >= 0: bpp = 32 else: bpp = 24 log( "rgb_encode using level=%s, %s compressed %sx%s in %s/%s: %s bytes down to %s", level, algo, image.get_width(), image.get_height(), coding, pixel_format, len(pixels), len(raw_data)) if not encoding_client_options or not supports_rgb24zlib: return coding, wire_data, {}, width, height, stride, bpp #wrap it using "Compressed" so the network layer receiving it #won't decompress it (leave it to the client's draw thread) return coding, Compressed(coding, raw_data), options, width, height, stride, bpp
def rgb_encode(coding, image, rgb_formats, supports_transparency, speed, rgb_zlib=True, rgb_lz4=True, encoding_client_options=True, supports_rgb24zlib=True): pixel_format = image.get_pixel_format() #log("rgb_encode%s pixel_format=%s, rgb_formats=%s", (coding, image, rgb_formats, supports_transparency, speed, rgb_zlib, rgb_lz4, encoding_client_options, supports_rgb24zlib), pixel_format, rgb_formats) if pixel_format not in rgb_formats: if not rgb_reformat(image, rgb_formats, supports_transparency): raise Exception("cannot find compatible rgb format to use for %s! (supported: %s)" % (pixel_format, rgb_formats)) #get the new format: pixel_format = image.get_pixel_format() #switch encoding if necessary: if len(pixel_format)==4 and coding=="rgb24": coding = "rgb32" elif len(pixel_format)==3 and coding=="rgb32": coding = "rgb24" #always tell client which pixel format we are sending: options = {"rgb_format" : pixel_format} #compress here and return a wrapper so network code knows it is already zlib compressed: pixels = image.get_pixels() #special case for when rowstride is so much bigger than the width #that we would end up sending large chunks of padding with each row of pixels #this happens with XShm pixel data (the default) stride = image.get_rowstride() width = image.get_width() rstride = roundup(width*len(pixel_format), 4) #a reasonable stride: rounded up to 4 height = image.get_height() if stride>8 and rstride<stride: al = len(pixels) #current buffer size el = rstride*height #desirable size we could have if al-el>1024 and el*110/100<al: #is it worth re-striding to save space? #we'll save at least 1KB and 10%, do it #Note: we could also change the pixel format whilst we're at it # and convert BGRX to RGB for example (assuming RGB is also supported by the client) rows = [] for y in range(height): rows.append(pixels[stride*y:stride*y+rstride]) pixels = "".join(rows) log("rgb_encode: %s pixels re-stride saving %i%% from %s (%s bytes) to %s (%s bytes)", pixel_format, 100-100*el/al, stride, al, rstride, el) stride = rstride #compression #by default, wire=raw: raw_data = str(pixels) wire_data = raw_data level = 0 algo = "not" if rgb_zlib or rgb_lz4: level = max(0, min(5, int(115-speed)/20)) if len(pixels)<1024: #fewer pixels, make it more likely we won't bother compressing: level = level / 2 if level>0: lz4 = use_lz4 and rgb_lz4 and level<=3 wire_data = compressed_wrapper(coding, pixels, level=level, lz4=lz4) raw_data = wire_data.data #log("%s/%s data compressed from %s bytes down to %s (%s%%) with lz4=%s", # coding, pixel_format, len(pixels), len(raw_data), int(100.0*len(raw_data)/len(pixels)), self.rgb_lz4) if len(raw_data)>=(len(pixels)-32): #compressed is actually bigger! (use uncompressed) level = 0 wire_data = str(pixels) raw_data = wire_data else: if lz4: options["lz4"] = True algo = "lz4" else: options["zlib"] = level algo = "zlib" if pixel_format.upper().find("A")>=0 or pixel_format.upper().find("X")>=0: bpp = 32 else: bpp = 24 log("rgb_encode using level=%s, %s compressed %sx%s in %s/%s: %s bytes down to %s", level, algo, image.get_width(), image.get_height(), coding, pixel_format, len(pixels), len(raw_data)) if not encoding_client_options or not supports_rgb24zlib: return coding, wire_data, {}, width, height, stride, bpp #wrap it using "Compressed" so the network layer receiving it #won't decompress it (leave it to the client's draw thread) return coding, Compressed(coding, raw_data), options, width, height, stride, bpp