def init_video_decoder_option(self, decoder_name): decoder_module = get_codec(decoder_name) log("init_video_decoder_option(%s)", decoder_name) log(" module=%s", decoder_module) if not decoder_module: log(" video decoder %s could not be loaded:", decoder_name) log(" %s", get_codec_error(decoder_name)) return decoder_type = decoder_module.get_type() try: decoder_module.init_module() self._cleanup_modules.append(decoder_module) except Exception as e: log("exception in %s module initialization %s: %s", decoder_type, decoder_module.init_module, e, exc_info=True) log.warn("Warning: cannot use %s module %s: %s", decoder_type, decoder_module, e, exc_info=True) return encodings = decoder_module.get_encodings() log(" %s encodings=%s", decoder_type, csv(encodings)) for encoding in encodings: colorspaces = decoder_module.get_input_colorspaces(encoding) log(" %s input colorspaces for %s: %s", decoder_type, encoding, csv(colorspaces)) for colorspace in colorspaces: output_colorspace = decoder_module.get_output_colorspace(encoding, colorspace) log(" %s output colorspace for %s/%s: %s", decoder_type, encoding, colorspace, output_colorspace) try: assert decoder_module.Decoder self.add_decoder(encoding, colorspace, decoder_name, decoder_module) except Exception as e: log.warn("failed to add decoder %s: %s", decoder_module, e)
def run(self, *args): if len(args)==1 and args[0]=="status": from xpra.log import get_all_loggers return "logging is enabled for: %s" % str(list([str(x) for x in get_all_loggers() if x.is_debug_enabled()])) if len(args)<2: self.raise_error("not enough arguments") log_cmd = args[0] if log_cmd not in ("enable", "disable"): self.raise_error("only 'enable' and 'disable' verbs are supported") #support both separate arguments and csv: categories = [] for x in args[1:]: categories += [v.strip() for v in x.split(",")] from xpra.log import add_debug_category, add_disabled_category, enable_debug_for, disable_debug_for if log_cmd=="enable": add_debug_category(*categories) loggers = enable_debug_for(*categories) else: assert log_cmd=="disable" add_disabled_category(*categories) loggers = disable_debug_for(*categories) if not loggers: log.info("no loggers matching: %s", csv(categories)) else: log.info("%sd debugging for: %s", log_cmd, csv(loggers)) return "logging %sd for %s" % (log_cmd, csv(loggers))
def select_clipboard_menu_option(self, item=None, label=None, labels=[]): #ensure that only the matching menu item is selected, #(can be specified as a menuitem object, or using its label) #all the other menu items whose labels are specified will be made inactive #(we use a flag to prevent reentry) clipboardlog("select_clipboard_menu_option(%s, %s, %s) clipboard_change_pending=%s", item, label, labels, self._clipboard_change_pending) if self._clipboard_change_pending: return None clipboard = self.get_menu("Clipboard") if not clipboard: log.error("Error: cannot locate Clipboard menu object!") return None all_items = [x for x in clipboard.get_submenu().get_children() if x.get_label() in labels] selected_items = [x for x in all_items if x==item] + [x for x in all_items if x.get_label()==label] if not selected_items: log.error("Error: cannot find any clipboard menu options to match '%s'", label) log.error(" all menu items: %s", csv(all_items)) log.error(" selected: %s", csv(selected_items)) return None self._clipboard_change_pending = True sel = selected_items[0] if not label: label = sel.get_label() for x in all_items: active = x.get_label()==label if x.get_active()!=active: x.set_active(active) self._clipboard_change_pending = False return label
def _print_file(self, filename, mimetype, options): printer = options.strget("printer") title = options.strget("title") copies = options.intget("copies", 1) if title: printlog.info(" sending '%s' to printer '%s'", title, printer) else: printlog.info(" sending to printer '%s'", printer) from xpra.platform.printing import print_files, printing_finished, get_printers printers = get_printers() def delfile(): if not DELETE_PRINTER_FILE: return try: os.unlink(filename) except: printlog("failed to delete print job file '%s'", filename) return False if not printer: printlog.error("Error: the printer name is missing") printlog.error(" printers available: %s", csv(printers.keys()) or "none") delfile() return if printer not in printers: printlog.error("Error: printer '%s' does not exist!", printer) printlog.error(" printers available: %s", csv(printers.keys()) or "none") delfile() return try: job_options = options.get("options") job_options["copies"] = copies job = print_files(printer, [filename], title, job_options) except Exception as e: printlog("print_files%s", (printer, [filename], title, options), exc_info=True) printlog.error("Error: cannot print file '%s'", os.path.basename(filename)) printlog.error(" %s", e) delfile() return printlog("printing %s, job=%s", filename, job) if job<=0: printlog("printing failed and returned %i", job) delfile() return start = time.time() def check_printing_finished(): done = printing_finished(job) printlog("printing_finished(%s)=%s", job, done) if done: delfile() return False if time.time()-start>10*60: printlog.warn("print job %s timed out", job) delfile() return False return True #try again.. if check_printing_finished(): self.timeout_add(10000, check_printing_finished)
def set_modules(self, video_encoders=[], csc_modules=[], video_decoders=[]): assert not self._initialized, "too late to set modules, the helper is already initialized!" def filt(name, inlist, all_list): notfound = [x for x in inlist if x and x not in all_list] if notfound: log.warn("ignoring unknown %s: %s", name, ", ".join(notfound)) return [x for x in inlist if x in all_list] self.video_encoders = filt("video encoders" , video_encoders, ALL_VIDEO_ENCODER_OPTIONS) self.csc_modules = filt("csc modules" , csc_modules, ALL_CSC_MODULE_OPTIONS) self.video_decoders = filt("video decoders" , video_decoders, ALL_VIDEO_DECODER_OPTIONS) log("VideoHelper.set_modules(%s, %s, %s) video encoders=%s, csc=%s, video decoders=%s", csv(video_encoders), csv(csc_modules), csv(video_decoders), csv(self.video_encoders), csv(self.csc_modules), csv(self.video_decoders))
def init_csc_options(self): log("init_csc_options()") log(" will try csc modules: %s", csv(self.csc_modules)) for x in self.csc_modules: try: mod = get_csc_module_name(x) self.init_csc_option(mod) except: log.warn("init_csc_options() cannot add %s csc", x, exc_info=True) log(" csc specs: %s", csv(self._csc_encoder_specs)) for src_format, d in sorted(self._csc_encoder_specs.items()): log(" %s - %s options:", src_format, len(d)) for dst_format, specs in sorted(d.items()): log(" * %s via: %s", dst_format, csv(sorted(spec.codec_type for spec in specs)))
def main(): global pygst_version, gst_version, gst_vinfo from xpra.platform import program_context from xpra.log import enable_color with program_context("GStreamer-Info", "GStreamer Information"): enable_color() if "-v" in sys.argv or "--verbose" in sys.argv: log.enable_debug() import_gst() print("Loaded Python GStreamer version %s for Python %s.%s" % (gst_vinfo, sys.version_info[0], sys.version_info[1])) apn = get_all_plugin_names() print("GStreamer plugins found: %s" % csv(apn)) print("") print("GStreamer version: %s" % ".".join([str(x) for x in gst_version])) print("PyGStreamer version: %s" % ".".join([str(x) for x in pygst_version])) print("") encs = [x for x in CODEC_ORDER if has_encoder(x)] decs = [x for x in CODEC_ORDER if has_decoder(x)] print("encoders: %s" % csv(encs)) print("decoders: %s" % csv(decs)) print("muxers: %s" % csv(get_muxers())) print("demuxers: %s" % csv(get_demuxers())) print("stream compressors: %s" % csv(get_stream_compressors())) print("source plugins: %s" % csv([x for x in get_source_plugins() if x in apn])) print("sink plugins: %s" % csv([x for x in get_sink_plugins() if x in apn])) print("default sink: %s" % get_default_sink())
def filter_mappings(mappings, drop_extra_keys=False): filtered = {} invalid_keysyms = set() for keycode, entries in mappings.items(): mods = modifiers_for(entries) if len(mods)>1: log.warn("Warning: keymapping removed an invalid keycode entry:") log.warn(" keycode %s points to %i modifiers: %s", keycode, len(mods), csv(list(mods))) log.warn(" from definition: %s", csv(list(entries))) continue #now remove entries for keysyms we don't have: f_entries = set([(keysym, index) for keysym, index in entries if keysym and X11Keyboard.parse_keysym(keysym) is not None]) for keysym, _ in entries: if keysym and X11Keyboard.parse_keysym(keysym) is None: invalid_keysyms.add(keysym) if len(f_entries)==0: log("keymapping removed invalid keycode entry %s pointing to only unknown keysyms: %s", keycode, entries) continue if drop_extra_keys: noopt = [keysym for keysym, index in entries if (X11Keyboard.parse_keysym(keysym) is not None and keysym not in OPTIONAL_KEYS)] if len(noopt)==0: log("keymapping removed keycode entry %s pointing to optional keys: %s", keycode, entries) continue filtered[keycode] = f_entries if invalid_keysyms: log.warn("the following keysyms are invalid and have not been mapped: %s", ", ".join((str(x) for x in invalid_keysyms))) return filtered
def enable_selections(self, selections): #when clients first connect or later through the "clipboard-enable-selections" packet, #they can tell us which clipboard selections they want enabled #(ie: OSX and win32 only use "CLIPBOARD" by default, and not "PRIMARY" or "SECONDARY") log("enabling selections: %s", csv(selections)) for selection, proxy in self._clipboard_proxies.items(): proxy.set_enabled(selection in selections)
def get_layout_spec(self): layout = None layouts = [] variant = None variants = None try: l = win32api.GetKeyboardLayoutList() log("win32api.GetKeyboardLayoutList()=%s", csv(hex(v) for v in l)) for hkl in l: kbid = hkl & 0xffff if kbid in WIN32_LAYOUTS: code, _, _, _, _layout, _variants = WIN32_LAYOUTS.get(kbid) log("found keyboard layout '%s' with variants=%s, code '%s' for kbid=%i (%#x)", _layout, _variants, code, kbid, hkl) if _layout not in layouts: layouts.append(_layout) except Exception as e: log.error("Error: failed to detect keyboard layouts: %s", e) try: hkl = win32api.GetKeyboardLayout(0) log("win32api.GetKeyboardLayout(0)=%#x", hkl) kbid = hkl & 0xffff if kbid in WIN32_LAYOUTS: code, _, _, _, layout, variants = WIN32_LAYOUTS.get(kbid) log("found keyboard layout '%s' with variants=%s, code '%s' for kbid=%i (%#x)", layout, variants, code, kbid, hkl) if not layout: log("unknown keyboard layout for kbid: %i (%#x)", kbid, hkl) else: layouts.append(layout) except Exception as e: log.error("Error: failed to detect keyboard layout: %s", e) return layout,layouts,variant,variants
def get_client_backlog(self): packets_backlog, pixels_backlog, bytes_backlog = 0, 0, 0 if len(self.damage_ack_pending)>0: sent_before = time.time()-(self.target_latency+0.020) dropped_acks_time = time.time()-60 #1 minute drop_missing_acks = [] for sequence, (start_send_at, start_bytes, end_send_at, end_bytes, pixels) in self.damage_ack_pending.items(): if end_send_at==0 or start_send_at>sent_before: continue if start_send_at<dropped_acks_time: drop_missing_acks.append(sequence) else: packets_backlog += 1 pixels_backlog += pixels bytes_backlog += (end_bytes - start_bytes) log("get_client_backlog missing acks: %s", drop_missing_acks) #this should never happen... if len(drop_missing_acks)>0: log.error("Error: expiring %i missing damage ACK%s,", len(drop_missing_acks), engs(drop_missing_acks)) log.error(" connection may be closed or closing,") log.error(" sequence numbers missing: %s", csv(drop_missing_acks)) for sequence in drop_missing_acks: try: del self.damage_ack_pending[sequence] except: pass return packets_backlog, pixels_backlog, bytes_backlog
def stop(self): log("BonjourPublishers.stop(): %s" % csv(self.publishers)) for publisher in self.publishers: try: publisher.stop() except Exception as e: log.error("Error stopping publisher %s:", publisher) log.error(" %s" % publisher, e)
def init_video_encoders_options(self): log("init_video_encoders_options()") log(" will try video encoders: %s", csv(self.video_encoders)) for x in self.video_encoders: try: mods = get_encoder_module_names(x) log(" modules for %s: %s", x, csv(mods)) for mod in mods: try: self.init_video_encoder_option(mod) break except Exception as e: log(" init_video_encoder_option(%s) error", mod, exc_info=True) log.warn("Warning: cannot load %s video encoder:", mod) log.warn(" %s", e) except Exception as e: log.warn("Warning: cannot add %s encoder: %s", x, e) log("found %i video encoder%s: %s", len(self._video_encoder_specs), engs(self._video_encoder_specs), csv(self._video_encoder_specs))
def get_keymap_spec(self): _print, query, query_struct = self.keyboard.get_keymap_spec() if self.layout_option: query_struct["layout"] = self.layout_option if self.layouts_option: query_struct["layouts"] = csv(self.layouts_option) if self.variant_option: query_struct["variant"] = self.variant_option if self.variants_option: query_struct["variants"] = csv(self.variants_option) if self.options: if self.options.lower()=="none": query_struct["options"] = "" else: query_struct["options"] = self.options if self.layout_option or self.layouts_option or self.variant_option or self.variants_option or self.options: query = xkbmap_query_tostring(query_struct) return _print, query, query_struct
def init_video_decoders_options(self): log("init_video_decoders_options()") log(" will try video decoders: %s", csv(self.video_decoders)) for x in self.video_decoders: try: mod = get_decoder_module_name(x) self.init_video_decoder_option(mod) except: log.warn("Warning: cannot add %s decoder", x, exc_info=True) log("found %s video decoder%s: %s", len(self._video_decoder_specs), engs(self._video_decoder_specs), csv(self._video_decoder_specs))
def sound_option_or_all(name, options, all_values): if not options: v = all_values #not specified on command line: use default else: v = [] invalid_options = [] for x in options: #options is a list, but it may have csv embedded: for o in x.split(","): o = o.strip() if o not in all_values: invalid_options.append(o) else: v.append(o) if len(invalid_options)>0: if all_values: log.warn("Warning: invalid value%s for %s: %s", engs(invalid_options), name, csv(invalid_options)) log.warn(" valid option%s: %s", engs(all_values), csv(all_values)) else: log.warn("Warning: no %ss available", name) log("%s=%s", name, csv(v)) return v
def filtered_modifiers_set(modifiers): m = set() for modifier in modifiers: if self.xkbmap_mod_managed and modifier in self.xkbmap_mod_managed: log("modifier is server managed: %s", modifier) continue keynames = self.keynames_for_mod.get(modifier) if is_ignored(modifier, keynames): log("modifier %s ignored (in ignored keynames=%s)", modifier, keynames) continue m.add(modifier) log("filtered_modifiers_set(%s)=%s", modifiers, csv(list(m))) return m
def get_codecs(): global CODECS, gst_major_version if CODECS is not None or not has_gst: return CODECS or {} #populate CODECS: CODECS = {} for elements in CODEC_OPTIONS: encoding = elements[0] if encoding in CODECS: #we already have one for this encoding continue if force_enabled(encoding): log.info("sound codec %s force enabled", encoding) elif len([x for x in elements if (x and (x.find("matroska")>=0 or x.find("gdp")>=0))])>0 and get_gst_version()<(0, 10, 36): #outdated versions of gstreamer cause problems with the gdp and matroskademux muxers, #the receiver may not be able to process the data #and we have no way of knowing what version they have at this point, so just disable those: log("avoiding %s with gdp muxer - gstreamer version %s is too old", encoding, get_gst_version()) continue elif encoding==OPUS_GDP and get_gst_version()>=(1, 8): log("avoiding %s with gstreamer version %s", encoding, get_gst_version()) continue elif encoding in (OPUS_GDP, OPUS) and OSX: log("avoiding %s on Mac OS X", encoding) continue elif encoding==FLAC: #flac problems: if WIN32 and gst_major_version==0: #the gstreamer 0.10 builds on win32 use the outdated oss build, #which includes outdated flac libraries with known CVEs, #so avoid using those: log("avoiding outdated flac module (likely buggy on win32 with gstreamer 0.10)") continue elif gst_major_version==1: log("skipping flac with GStreamer 1.x to avoid obscure 'not-negotiated' errors I do not have time for") continue elif encoding==OPUS: if gst_major_version<1: log("skipping opus with GStreamer 0.10") continue #verify we have all the elements needed: if has_plugins(*elements[1:]): #ie: FLAC, "flacenc", "oggmux", "flacdec", "oggdemux" = elements encoding, encoder, muxer, decoder, demuxer = elements CODECS[encoding] = (encoder, muxer, decoder, demuxer) log("initialized sound codecs:") for k in [x for x in CODEC_ORDER if x in CODECS]: def ci(v): return "%-12s" % v log("* %-10s : %s", k, csv([ci(v) for v in CODECS[k]])) return CODECS
def init_video_encoder_option(self, encoder_name): encoder_module = get_codec(encoder_name) log("init_video_encoder_option(%s)", encoder_name) log(" module=%s", encoder_module) if not encoder_module: log(" video encoder '%s' could not be loaded:", encoder_name) log(" %s", get_codec_error(encoder_name)) return encoder_type = encoder_module.get_type() try: encoder_module.init_module() self._cleanup_modules.append(encoder_module) except Exception as e: log(" exception in %s module %s initialization %s: %s", encoder_type, encoder_module.__name__, encoder_module.init_module, e, exc_info=True) raise encodings = encoder_module.get_encodings() log(" %s encodings=%s", encoder_type, csv(encodings)) for encoding in encodings: colorspaces = encoder_module.get_input_colorspaces(encoding) log(" %s input colorspaces for %s: %s", encoder_type, encoding, csv(colorspaces)) for colorspace in colorspaces: spec = encoder_module.get_spec(encoding, colorspace) self.add_encoder_spec(encoding, colorspace, spec)
def load_video_decoders(): global VIDEO_DECODERS if VIDEO_DECODERS is None: VIDEO_DECODERS = {} vh = getVideoHelper() for encoding in vh.get_decodings(): specs = vh.get_decoder_specs(encoding) for colorspace, decoders in specs.items(): log("%-5s decoders for %7s: %s", encoding, colorspace, csv([d.get_type() for _, d in decoders])) assert len(decoders) > 0 # use the first one: _, decoder_module = decoders[0] VIDEO_DECODERS[encoding] = decoder_module log("video decoders: %s", dict((e, d.get_type()) for e, d in VIDEO_DECODERS.items())) return VIDEO_DECODERS
def import_gst(): global gst, has_gst, gst_vinfo if has_gst is not None: return gst #hacks to locate gstreamer plugins on win32 and osx: if WIN32: frozen = hasattr(sys, "frozen") and sys.frozen in ("windows_exe", "console_exe", True) log("gstreamer_util: frozen=%s", frozen) if frozen: #on win32, we keep separate trees #because GStreamer 0.10 and 1.x were built using different and / or incompatible version of the same libraries: from xpra.platform.paths import get_app_dir gst_dir = os.path.join(get_app_dir(), "gstreamer-1.0") #ie: C:\Program Files\Xpra\gstreamer-0.10 os.environ["GST_PLUGIN_PATH"] = gst_dir gst_bin_dir = os.path.join(gst_dir, "bin") #ie: C:\Program Files\Xpra\gstreamer-0.10\bin os.environ["PATH"] = os.pathsep.join(x for x in (gst_bin_dir, os.environ.get("PATH", "")) if x) sys.path.insert(0, gst_bin_dir) scanner = os.path.join(gst_bin_dir, "gst-plugin-scanner.exe") if os.path.exists(scanner): os.environ["GST_PLUGIN_SCANNER"] = scanner gi_dir = os.path.join(get_app_dir(), "girepository-1.0") os.environ["GI_TYPELIB_PATH"] = gi_dir elif OSX: bundle_contents = os.environ.get("GST_BUNDLE_CONTENTS") log("OSX: GST_BUNDLE_CONTENTS=%s", bundle_contents) if bundle_contents: os.environ["GST_PLUGIN_PATH"] = os.path.join(bundle_contents, "Resources", "lib", "gstreamer-1.0") os.environ["GST_PLUGIN_SCANNER"] = os.path.join(bundle_contents, "Resources", "bin", "gst-plugin-scanner-1.0") gi_dir = os.path.join(bundle_contents, "Resources", "lib", "girepository-1.0") os.environ["GI_TYPELIB_PATH"] = gi_dir log("GStreamer 1.x environment: %s", dict((k,v) for k,v in os.environ.items() if (k.startswith("GST") or k.startswith("GI") or k=="PATH"))) log("GStreamer 1.x sys.path=%s", csv(sys.path)) try: _gst = import_gst1() v = _gst.version() if v[-1]==0: v = v[:-1] gst_vinfo = ".".join((str(x) for x in v)) gst = _gst except Exception as e: log("Warning failed to import GStreamer 1.x", exc_info=True) log.warn("Warning: failed to import GStreamer 1.x:") log.warn(" %s", e) return None has_gst = gst is not None return gst
def get_default_sink(): DEFAULT_SINK = os.environ.get("XPRA_SOUND_SINK") if DEFAULT_SINK: SINKS = get_sink_plugins() if DEFAULT_SINK not in SINKS: log.error("invalid default sound sink: '%s' is not in %s", DEFAULT_SINK, csv(SINKS)) else: return DEFAULT_SINK try: from xpra.sound.pulseaudio.pulseaudio_util import has_pa if has_pa(): return "pulsesink" except ImportError as e: log("get_default_sink() no pulsesink: %s", e) SINKS = get_sink_plugins() return SINKS[0]
def parse_message0(self, message): #message parsing code for GStreamer 0.10 structure = message.structure found = False if structure.has_field("bitrate"): new_bitrate = int(structure["bitrate"]) self.update_bitrate(new_bitrate) found = True if structure.has_field("codec"): desc = structure["codec"] self.new_codec_description(desc) found = True if structure.has_field("audio-codec"): desc = structure["audio-codec"] self.new_codec_description(desc) found = True if structure.has_field("mode"): mode = structure["mode"] if self.codec_mode!=mode: gstlog("mode: %s", mode) self.codec_mode = mode self.info["codec_mode"] = self.codec_mode found = True if structure.has_field("type"): if structure["type"]=="volume-changed": self.gstloginfo("volumes=%s", csv("%i%%" % (v*100/2**16) for v in structure["volumes"])) found = True self.info["volume"] = self.get_volume() else: self.gstloginfo("type=%s", structure["type"]) if structure.has_field("container-format"): v = structure["container-format"] self.new_container_description(v) found = True if not found: #these, we know about, so we just log them: for x in ("minimum-bitrate", "maximum-bitrate", "channel-mode"): if structure.has_field(x): v = structure[x] gstlog("tag message: %s = %s", x, v) return #handled self.gstloginfo("unknown sound pipeline message %s: %s", message, structure) self.gstloginfo(" %s", structure.keys())
def get_default_sink(): sink = os.environ.get("XPRA_SOUND_SINK") sinks = get_sink_plugins() if sink: if sink not in sinks: log.error("invalid default sound sink: '%s' is not in %s", sink, csv(sinks)) else: return sink try: from xpra.sound.pulseaudio.pulseaudio_util import has_pa, get_pactl_server if has_pa(): s = get_pactl_server() if not s: log("cannot connect to pulseaudio server?") else: return "pulsesink" except ImportError as e: log("get_default_sink() no pulsesink: %s", e) return sinks[0]
def video_init(self): enclog("video_init() loading codecs") load_codecs(decoders=False) enclog("video_init() will try video encoders: %s", csv(self.video_encoder_modules) or "none") self.video_helper = getVideoHelper() #only use video encoders (no CSC supported in proxy) self.video_helper.set_modules(video_encoders=self.video_encoder_modules) self.video_helper.init() self.video_encoding_defs = {} self.video_encoders = {} self.video_encoders_dst_formats = [] self.video_encoders_last_used_time = {} self.video_encoder_types = [] #figure out which encoders we want to proxy for (if any): encoder_types = set() for encoding in self.video_helper.get_encodings(): colorspace_specs = self.video_helper.get_encoder_specs(encoding) for colorspace, especs in colorspace_specs.items(): if colorspace not in ("BGRX", "BGRA", "RGBX", "RGBA"): #only deal with encoders that can handle plain RGB directly continue for spec in especs: #ie: video_spec("x264") spec_props = spec.to_dict() del spec_props["codec_class"] #not serializable! spec_props["score_boost"] = 50 #we want to win scoring so we get used ahead of other encoders spec_props["max_instances"] = 3 #limit to 3 video streams we proxy for (we really want 2, # but because of races with garbage collection, we need to allow more) #store it in encoding defs: self.video_encoding_defs.setdefault(encoding, {}).setdefault(colorspace, []).append(spec_props) encoder_types.add(spec.codec_type) enclog("encoder types found: %s", tuple(encoder_types)) #remove duplicates and use preferred order: order = PREFERRED_ENCODER_ORDER[:] for x in list(encoder_types): if x not in order: order.append(x) self.video_encoder_types = [x for x in order if x in encoder_types] enclog.info("proxy video encoders: %s", ", ".join(self.video_encoder_types))
def handle(self, packet) -> bool: digest = bytestostr(packet[3]) if not digest.startswith("gss:"): #not a gss challenge log("%s is not a gss challenge", digest) return False try: import gssapi #@UnresolvedImport self.gssapi = gssapi if OSX and False: from gssapi.raw import (cython_converters, cython_types, oids) # @UnresolvedImport assert cython_converters and cython_types and oids except ImportError as e: log.warn("Warning: cannot use gss authentication handler") log.warn(" %s", e) return False service = bytestostr(digest.split(b":", 1)[1]) if service not in self.services and "*" not in self.services: log.warn("Warning: invalid GSS request for service '%s'", service) log.warn(" services supported: %s", csv(self.services)) return False log("gss service=%s", service) service_name = self.gssapi.Name(service) try: ctx = self.gssapi.SecurityContext(name=service_name, usage="initiate") token = ctx.step() except Exception as e: log("gssapi failure", exc_info=True) log.error("Error: gssapi client authentication failure:") try: for x in str(e).split(":", 2): log.error(" %s", x.lstrip(" ")) except Exception: log.error(" %s", e) return False log("gss token=%s", repr(token)) self.client.send_challenge_reply(packet, token) return True
def set_actions(self, actions): oldactions = self.actions self.actions = actions #build the change list for emitting the signal: # Four separate types of changes are possible, # and the 4 parameters of the change signal reflect these possibilities: # - as a list of removed actions # - a{sb} a list of actions that had their enabled flag changed # - a{sv} a list of actions that had their state changed # - a{s(bgav)} a list of new actions added in the same format as the return value of the DescribeAll method""" removed = dbus.Array(signature="s") enabled_changed = dbus.Array(signature="a{sb}") state_changed = dbus.Array(signature="a{sv}") added = dbus.Array(signature="a{s(bgav)}") all_actions = list(set(oldactions.keys() + actions.keys())) self.log(".set_actions(..) all actions=%s", csv(all_actions)) for action in all_actions: if action not in actions: removed.append(ds(action)) self.log(".set_actions(..) removed %s", action) elif action not in oldactions: action_def = actions[action] a = dbus.Struct(self._make_action(*action_def)) v = dbus.Dictionary({ds(action) : a}, signature="s(bgav)") added.append(v) self.log(".set_actions(..) added %s=%s", action, action_def) else: #maybe changed state? oldaction = oldactions.get(action, [False, None, None]) #default value should be redundant newaction = actions.get(action, [False, None, None]) #default value should be redundant if oldaction[0]!=newaction[0]: v = dbus.Dictionary({ds(action) : dbus.Boolean(newaction[0])}, signature="sb") enabled_changed.append(v) self.log(".set_actions(..) enabled changed for %s from %s to %s", action, oldaction[0], newaction[0]) if oldaction[2]!=newaction[2]: v = dbus.Dictionary({ds(action) : newaction[2]}, signature="sv") state_changed.append(v) self.log(".set_actions(..) state changed for %s from %s to %s", action, oldaction[2], newaction[2]) self.log(".set_actions(..) changes: %s", (removed, enabled_changed, state_changed, added)) if removed or enabled_changed or state_changed or added: self.Changed(removed, enabled_changed, state_changed, added)
def start_sound_source(self, device=None): log("start_sound_source(%s)", device) assert self.sound_source is None def sound_source_state_changed(*_args): self.emit("microphone-changed") #find the matching codecs: matching_codecs = self.get_matching_codecs(self.microphone_codecs, self.server_sound_decoders) log("start_sound_source(%s) matching codecs: %s", device, csv(matching_codecs)) if not matching_codecs: self.no_matching_codec_error("microphone", self.server_sound_decoders, self.microphone_codecs) return False try: from xpra.sound.wrapper import start_sending_sound plugins = self.sound_properties.get("plugins") ss = start_sending_sound(plugins, self.sound_source_plugin, device or self.microphone_device, None, 1.0, False, matching_codecs, self.server_pulseaudio_server, self.server_pulseaudio_id) if not ss: return False self.sound_source = ss ss.connect("new-buffer", self.new_sound_buffer) ss.connect("state-changed", sound_source_state_changed) ss.connect("new-stream", self.new_stream) ss.start() log("start_sound_source(%s) sound source %s started", device, ss) return True except Exception as e: self.may_notify_audio("Failed to start microphone forwarding", "%s" % e) log.error("Error setting up microphone forwarding:") log.error(" %s", e) return False
def process_challenge_gss(self, packet): digest = packet[3] if not digest.startswith(b"gss:"): #not a gss challenge authlog("%s is not a gss challenge", digest) return False try: import gssapi if OSX and False: from gssapi.raw import (cython_converters, cython_types, oids) assert cython_converters and cython_types and oids except ImportError as e: authlog("import gssapi", exc_info=True) if first_time("no-kerberos"): authlog.warn("Warning: gss authentication not supported:") authlog.warn(" %s", e) return False service = bytestostr(digest.split(b":", 1)[1]) if service not in GSS_SERVICES and "*" not in GSS_SERVICES: authlog.warn("Warning: invalid GSS request for service '%s'", service) authlog.warn(" services supported: %s", csv(GSS_SERVICES)) return False authlog("gss service=%s", service) service_name = gssapi.Name(service) try: ctx = gssapi.SecurityContext(name=service_name, usage="initiate") token = ctx.step() except Exception as e: authlog("gssapi failure", exc_info=True) authlog.error("Error: gssapi client authentication failure:") try: #split on colon for x in str(e).split(":", 2): authlog.error(" %s", x.lstrip(" ")) except: authlog.error(" %s", e) return False authlog("gss token=%s", repr(token)) self.send_challenge_reply(packet, token) return True
def main(): from xpra.platform import program_context from xpra.log import enable_color with program_context("GStreamer-Info", "GStreamer Information"): enable_color() if "-v" in sys.argv or "--verbose" in sys.argv: log.enable_debug() import_gst() v = get_gst_version() if not v: print("no gstreamer version information") else: if v[-1] == 0: v = v[:-1] gst_vinfo = ".".join((str(x) for x in v)) print("Loaded Python GStreamer version %s for Python %s.%s" % (gst_vinfo, sys.version_info[0], sys.version_info[1])) apn = get_all_plugin_names() print("GStreamer plugins found: %s" % csv(apn)) print("") print("GStreamer version: %s" % ".".join([str(x) for x in get_gst_version()])) print("PyGStreamer version: %s" % ".".join([str(x) for x in get_pygst_version()])) print("") encs = [x for x in CODEC_ORDER if has_encoder(x)] decs = [x for x in CODEC_ORDER if has_decoder(x)] print("encoders: %s" % csv(encs)) print("decoders: %s" % csv(decs)) print("muxers: %s" % csv(get_muxers())) print("demuxers: %s" % csv(get_demuxers())) print("stream compressors: %s" % csv(get_stream_compressors())) print("source plugins: %s" % csv([x for x in get_source_plugins() if x in apn])) print("sink plugins: %s" % csv([x for x in get_sink_plugins() if x in apn])) print("default sink: %s" % get_default_sink_plugin())
def main(): import sys from xpra.os_util import WIN32, OSX, POSIX, bytestostr from xpra.util import print_nested_dict, csv from xpra.platform import program_context from xpra.log import enable_color, enable_debug_for with program_context("Keyboard-Tool", "Keyboard Tool"): #use the logger for the platform module we import from enable_color() verbose = "-v" in sys.argv or "--verbose" in sys.argv or \ (WIN32 and not ("-q" in sys.argv or "--quiet")) if verbose: enable_debug_for("keyboard") #naughty, but how else can I hook this up? if POSIX and not OSX: try: from xpra.x11.bindings.posix_display_source import init_posix_display_source #@UnresolvedImport init_posix_display_source() except Exception as e: print("failed to connect to the X11 server:") print(" %s" % e) #hope for the best.. keyboard = Keyboard() #pylint: disable=not-callable mod_meanings, mod_managed, mod_pointermissing = keyboard.get_keymap_modifiers() print("Modifiers:") print_nested_dict(mod_meanings) print("") print("Server Managed : %s" % (csv(mod_managed) or "None")) print("Missing from pointer events : %s" % (csv(mod_pointermissing) or "None")) print("") layout,layouts,variant,variants, options = keyboard.get_layout_spec() print("Layout: '%s'" % bytestostr(layout or b"")) print("Layouts: %s" % csv("'%s'" % bytestostr(x) for x in (layouts or []))) print("Variant: '%s'" % bytestostr(variant or b"")) print("Variants: %s" % csv("'%s'" % bytestostr(x) for x in (variants or []))) print("Options: %s" % (options)) print("") print("Repeat: %s" % csv(keyboard.get_keyboard_repeat())) if verbose and POSIX: keysyms = keyboard.get_x11_keymap() if keysyms: print("Keysyms:") for keycode,keysyms in keysyms.items(): print(" %3i : %s" % (keycode, csv(keysyms))) return 0
def get_default_source(): source = os.environ.get("XPRA_SOUND_SRC") sources = get_source_plugins() if source: if source not in sources: log.error("invalid default sound source: '%s' is not in %s", source, csv(sources)) else: return source try: from xpra.sound.pulseaudio.pulseaudio_util import has_pa, get_pactl_server if has_pa(): s = get_pactl_server() if not s: log("cannot connect to pulseaudio server?") else: return "pulsesrc" except ImportError as e: log("get_default_source() no pulsesrc: %s", e) for source in sources: if has_plugins(source): return source return None
def parse_message0(self, message): #message parsing code for GStreamer 0.10 structure = message.structure found = False if structure.has_field("bitrate"): new_bitrate = int(structure["bitrate"]) self.update_bitrate(new_bitrate) found = True if structure.has_field("codec"): desc = structure["codec"] if self.codec_description!=desc: log.info("codec: %s", desc) self.codec_description = desc found = True if structure.has_field("audio-codec"): desc = structure["audio-codec"] self.new_codec_description(desc) found = True if structure.has_field("mode"): mode = structure["mode"] if self.codec_mode!=mode: log("mode: %s", mode) self.codec_mode = mode found = True if structure.has_field("type"): if structure["type"]=="volume-changed": log.info("volumes=%s", csv("%i%%" % (v*100/2**16) for v in structure["volumes"])) found = True else: log.info("type=%s", structure["type"]) if not found: #these, we know about, so we just log them: for x in ("minimum-bitrate", "maximum-bitrate", "channel-mode", "container-format"): if structure.has_field(x): v = structure[x] log("tag message: %s = %s", x, v) return #handled log.info("unknown sound pipeline message %s: %s", message, structure) log.info(" %s", structure.keys())
def process_packet(self, proto, packet): command = bytestostr(packet[0]) if command == CONNECTION_LOST: log("connection-lost: %s, calling stop", packet[1:]) self.net_stop() return if command == GIBBERISH: log.warn("gibberish received:") log.warn(" %s", repr_ellipsized(packet[1], limit=80)) log.warn(" stopping") self.net_stop() return if command == "stop": log("received stop message") self.net_stop() return if command == "exit": log("received exit message") sys.exit(0) return #make it easier to hookup signals to methods: attr = command.replace("-", "_") if self.method_whitelist is not None and attr not in self.method_whitelist: log.warn("invalid command %r, not in whitelist: %s", attr, csv(self.method_whitelist)) return wo = self.wrapped_object if not wo: log("wrapped object is no more, ignoring method call '%s'", attr) return method = getattr(wo, attr, None) if not method: log.warn("unknown command: '%s'", attr) log.warn(" packet: '%s'", repr_ellipsized(str(packet))) return if DEBUG_WRAPPER: log("calling %s.%s%s", wo, attr, str(tuple(packet[1:]))[:128]) self.idle_add(method, *packet[1:]) INJECT_FAULT(proto)
def get_targets_callback(self, _c, targets, *_args): self.log("got targets: %s" % csv(str(x) for x in targets)) if hasattr(targets, "name"): self.log("target is atom: %s" % targets.name()) targets = [] filtered = [x for x in (targets or []) if x not in ("MULTIPLE", "TARGETS")] ct = self.get_targets.get_active_text() if not ct: #choose a good default target: for x in ("STRING", "UTF8_STRING"): if x in filtered: ct = x break self.get_targets.get_model().clear() self.get_targets.set_sensitive(True) i = 0 for t in filtered: self.get_targets.append_text(t) if t==ct: self.get_targets.set_active(i) i += 1 self.get_targets.show_all()
def filter_mappings(mappings, drop_extra_keys=False): filtered = {} invalid_keysyms = set() def estr(entries): try: return csv(list(set(x[0] for x in entries))) except: return csv(list(entries)) for keycode, entries in mappings.items(): mods = modifiers_for(entries) if len(mods)>1: log.warn("Warning: keymapping changed:") log.warn(" keycode %s points to %i modifiers: %s", keycode, len(mods), csv(list(mods))) log.warn(" from definition: %s", estr(entries)) for mod in mods: emod = [entry for entry in entries if modifiers_for([entry])] log.warn(" %s: %s", mod, estr(emod)) #keep just the first one: mods = list(mods)[:1] entries = [entry for entry in entries if mods[0] in modifiers_for([entry])] log.warn(" keeping: %s for %s", estr(entries), mods[0]) #now remove entries for keysyms we don't have: f_entries = set([(keysym, index) for keysym, index in entries if keysym and X11Keyboard.parse_keysym(keysym) is not None]) for keysym, _ in entries: if keysym and X11Keyboard.parse_keysym(keysym) is None: invalid_keysyms.add(keysym) if len(f_entries)==0: log("keymapping removed invalid keycode entry %s pointing to only unknown keysyms: %s", keycode, entries) continue if drop_extra_keys: noopt = [keysym for keysym, index in entries if (X11Keyboard.parse_keysym(keysym) is not None and keysym not in OPTIONAL_KEYS)] if len(noopt)==0: log("keymapping removed keycode entry %s pointing to optional keys: %s", keycode, entries) continue filtered[keycode] = f_entries if invalid_keysyms: log.warn("Warning: the following keysyms are invalid and have not been mapped") log.warn(" %s", csv(invalid_keysyms)) return filtered
def get_default_sink_plugin(): sink = os.environ.get("XPRA_SOUND_SINK") sinks = get_sink_plugins() if sink: if sink not in sinks: log.error("invalid default sound sink: '%s' is not in %s", sink, csv(sinks)) else: return sink try: from xpra.sound.pulseaudio.pulseaudio_util import has_pa, get_pactl_server if has_pa(): s = get_pactl_server() if not s: log("cannot connect to pulseaudio server?") else: return "pulsesink" except ImportError as e: log("get_default_sink_plugin() no pulsesink: %s", e) for sink in sinks: if has_plugins(sink): return sink return None
def get_layout_spec(self): layout = None layouts = [] variant = None variants = None options = "" try: l = _GetKeyboardLayoutList() log("GetKeyboardLayoutList()=%s", csv(hex(v) for v in l)) for hkl in l: kbid = hkl & 0xffff if kbid in WIN32_LAYOUTS: code, _, _, _, _layout, _variants = WIN32_LAYOUTS.get(kbid) log( "found keyboard layout '%s' with variants=%s, code '%s' for kbid=%i (%#x)", _layout, _variants, code, kbid, hkl) if _layout not in layouts: layouts.append(_layout) except Exception as e: log.error("Error: failed to detect keyboard layouts:") log.error(" %s", e) try: hkl = GetKeyboardLayout(0) log("GetKeyboardLayout(0)=%#x", hkl) kbid = hkl & 0xffff if kbid in WIN32_LAYOUTS: code, _, _, _, layout, variants = WIN32_LAYOUTS.get(kbid) log( "found keyboard layout '%s' with variants=%s, code '%s' for kbid=%i (%#x)", layout, variants, code, kbid, hkl) if not layout: log("unknown keyboard layout for kbid: %i (%#x)", kbid, hkl) else: layouts.append(layout) except Exception as e: log.error("Error: failed to detect keyboard layout:") log.error(" %s", e) return layout, layouts, variant, variants, options
def handle_socket_error(sockpath, sperms, e): log = get_network_logger() log("socket creation error", exc_info=True) if sockpath.startswith("/var/run/xpra") or sockpath.startswith( "/run/xpra"): log.info("cannot create group socket '%s'", sockpath) log.info(" %s", e) dirname = sockpath[:sockpath.find("xpra") + len("xpra")] if not os.path.exists(dirname): log.info(" %s does not exist", dirname) #only show extra information if the socket permissions #would have been accessible by the group: elif POSIX and (sperms & 0o40): uid = getuid() username = get_username_for_uid(uid) groups = get_groups(username) log.info(" user '%s' is a member of groups: %s", username, csv(groups) or "no groups!") if "xpra" not in groups: log.info( " add 'xpra' group membership to enable group socket sharing" ) for x in path_permission_info(dirname): log.info(" %s", x) elif sockpath.startswith("/var/run/user") or sockpath.startswith( "/run/user"): log.warn("Warning: cannot create socket '%s':", sockpath) log.warn(" %s", e) run_user = sockpath.split("/user")[0] + "/user" if not os.path.exists(run_user): log.warn(" %s does not exist", run_user) else: log.warn(" ($XDG_RUNTIME_DIR has not been created?)") else: log.error("Error: failed to create socket '%s':", sockpath) log.error(" %s", e) raise InitExit(EXIT_SOCKET_CREATION_ERROR, "failed to create socket %s" % sockpath)
def do_import_gst(): global gst if gst is not None: return gst #hacks to locate gstreamer plugins on win32 and osx: if WIN32: frozen = getattr(sys, "frozen", None) in ("windows_exe", "console_exe", True) log("gstreamer_util: frozen=%s", frozen) if frozen: from xpra.platform.paths import get_app_dir gst_dir = os.path.join(get_app_dir(), "lib", "gstreamer-1.0") #ie: C:\Program Files\Xpra\lib\gstreamer-1.0 os.environ["GST_PLUGIN_PATH"] = gst_dir elif OSX: bundle_contents = os.environ.get("GST_BUNDLE_CONTENTS") log("OSX: GST_BUNDLE_CONTENTS=%s", bundle_contents) if bundle_contents: rsc_dir = os.path.join(bundle_contents, "Resources") os.environ["GST_PLUGIN_PATH"] = os.path.join(rsc_dir, "lib", "gstreamer-1.0") os.environ["GST_PLUGIN_SCANNER"] = os.path.join(rsc_dir, "bin", "gst-plugin-scanner-1.0") log("GStreamer 1.x environment: %s", dict((k,v) for k,v in os.environ.items() if (k.startswith("GST") or k.startswith("GI") or k=="PATH"))) log("GStreamer 1.x sys.path=%s", csv(sys.path)) try: log("import gi") import gi gi.require_version('Gst', '1.0') from gi.repository import Gst #@UnresolvedImport log("Gst=%s", Gst) Gst.init(None) gst = Gst except Exception as e: log("Warning failed to import GStreamer 1.x", exc_info=True) log.warn("Warning: failed to import GStreamer 1.x:") log.warn(" %s", e) return None return gst
def init_csc_option(self, csc_name): csc_module = get_codec(csc_name) log("init_csc_option(%s)", csc_name) log(" module=%s", csc_module) if csc_module is None: log(" csc module %s could not be loaded:", csc_name) log(" %s", get_codec_error(csc_name)) return csc_type = csc_module.get_type() try: csc_module.init_module() self._cleanup_modules.append(csc_module) except Exception as e: log("exception in %s module initialization %s: %s", csc_type, csc_module.init_module, e, exc_info=True) log.warn("Warning: cannot use %s module %s: %s", csc_type, csc_module, e) return in_cscs = csc_module.get_input_colorspaces() for in_csc in in_cscs: out_cscs = csc_module.get_output_colorspaces(in_csc) log("%s output colorspaces for %s: %s", csc_module.get_type(), in_csc, csv(out_cscs)) for out_csc in out_cscs: spec = csc_module.get_spec(in_csc, out_csc) self.add_csc_spec(in_csc, out_csc, spec)
def parse_server_capabilities(self, c : typedict) -> bool: if self.remote_logging.lower() in ("send", "both", "yes", "true", "on") and ( #'remote-logging.receive' was only added in v4.1 so check both: c.boolget("remote-logging") or c.boolget("remote-logging.receive")): #check for debug: from xpra.log import is_debug_enabled conflict = tuple(v for v in ("network", "crypto", "udp", "websocket") if is_debug_enabled(v)) if conflict: log.warn("Warning: cannot enable remote logging") log.warn(" because debug logging is enabled for: %s", csv(conflict)) return True log.info("enabled remote logging") if not self.log_both: log.info(" see server log file for further output") self.local_logging = set_global_logging_handler(self.remote_logging_handler) elif self.remote_logging.lower()=="receive": self.request_server_log = c.boolget("remote-logging.send") if not self.request_server_log: log.warn("Warning: cannot receive log output from the server") log.warn(" the feature is not enabled or not supported by the server") else: self.after_handshake(self.start_receiving_logging) return True
def control_command_key(self, keycode_str, press=True): if self.readonly: return try: if keycode_str.startswith("0x"): keycode = int(keycode_str, 16) else: keycode = int(keycode_str) assert keycode > 0 and keycode <= 255 except: raise ControlError( "invalid keycode specified: '%s' (must be a number between 1 and 255)" % keycode_str) from None if press is not True: if press in ("1", "press"): press = True elif press in ("0", "unpress"): press = False else: raise ControlError( "if present, the press argument must be one of: %s" % csv( ("1", "press", "0", "unpress"))) self.fake_key(keycode, press)
def main(): from xpra.codecs.loader import log as loader_log, load_codecs from xpra.log import enable_color from xpra.platform import init, clean try: init("Video Helper") enable_color() if "-v" in sys.argv or "--verbose" in sys.argv: loader_log.enable_debug() log.enable_debug() load_codecs() vh = getVideoHelper() vh.set_modules(ALL_VIDEO_ENCODER_OPTIONS, ALL_CSC_MODULE_OPTIONS, ALL_VIDEO_DECODER_OPTIONS) vh.init() log.info("VideoHelper.get_info():") info = vh.get_info() for k in sorted(info.keys()): v = info.get(k) if type(v) in (list, tuple): v = csv(v) log.info("%s=%s", k, v) finally: clean()
def get_sockopt_tcp_info(sock, TCP_INFO, attributes=POSIX_TCP_INFO): def get_tcpinfo_class(fields): class TCPInfo(Structure): _fields_ = tuple(fields) def __repr__(self): return "TCPInfo(%s)" % self.getdict() def getdict(self): return {k[0] : getattr(self, k[0]) for k in self._fields_} return TCPInfo #calculate full structure size with all the fields defined: tcpinfo_class = get_tcpinfo_class(attributes) tcpinfo_size = sizeof(tcpinfo_class) data = sock.getsockopt(socket.SOL_TCP, TCP_INFO, tcpinfo_size) data_size = len(data) #but only define fields in the ctypes.Structure #if they are actually present in the returned data: while tcpinfo_size>data_size: #trim it down: attributes = attributes[:-1] tcpinfo_class = get_tcpinfo_class(attributes) tcpinfo_size = sizeof(tcpinfo_class) log = get_network_logger() if tcpinfo_size==0: log("getsockopt(SOL_TCP, TCP_INFO, %i) no data", tcpinfo_size) return {} #log("total size=%i for fields: %s", size, csv(fdef[0] for fdef in fields)) try: tcpinfo = tcpinfo_class.from_buffer_copy(data[:tcpinfo_size]) except ValueError as e: log("getsockopt(SOL_TCP, TCP_INFO, %i)", tcpinfo_size, exc_info=True) log("TCPInfo fields=%s", csv(tcpinfo_class._fields_)) log.warn("Warning: failed to get TCP_INFO for %s", sock) log.warn(" %s", e) return {} d = tcpinfo.getdict() log("getsockopt(SOL_TCP, TCP_INFO, %i)=%s", tcpinfo_size, d) return d
def start_receiving_sound(self): """ ask the server to start sending sound and emit the client signal """ log("start_receiving_sound() sound sink=%s", self.sound_sink) enabled = False try: if self.sound_sink is not None: log("start_receiving_sound: we already have a sound sink") enabled = True return if not self.server_sound_send: log.error("Error receiving sound: support not enabled on the server") return if not self.audio_loop_check("speaker"): return #choose a codec: matching_codecs = self.get_matching_codecs(self.speaker_codecs, self.server_sound_encoders) log("start_receiving_sound() matching codecs: %s", csv(matching_codecs)) if not matching_codecs: self.no_matching_codec_error("speaker", self.server_sound_encoders, self.speaker_codecs) return codec = matching_codecs[0] if not self.server_ogg_latency_fix and codec in ("flac", "opus", "speex"): log.warn("Warning: this server's sound support is out of date") log.warn(" the sound latency with the %s codec will be high", codec) def sink_ready(*args): scodec = codec log("sink_ready(%s) codec=%s (server codec name=%s)", args, codec, scodec) self.send("sound-control", "start", scodec) return False self.on_sink_ready = sink_ready enabled = self.start_sound_sink(codec) finally: if self.speaker_enabled!=enabled: self.speaker_enabled = enabled self.emit("speaker-changed") log("start_receiving_sound() done, speaker_enabled=%s", enabled)
def _print_file(self, filename, mimetype, printer, title, options): import time from xpra.platform.printing import print_files, printing_finished, get_printers printers = get_printers() if printer not in printers: printlog.error("Error: printer '%s' does not exist!", printer) printlog.error(" printers available: %s", csv(printers.keys()) or "none") return def delfile(): if not DELETE_PRINTER_FILE: return try: os.unlink(filename) except: printlog("failed to delete print job file '%s'", filename) return False job = print_files(printer, [filename], title, options) printlog("printing %s, job=%s", filename, job) if job<=0: printlog("printing failed and returned %i", job) delfile() return start = time.time() def check_printing_finished(): done = printing_finished(job) printlog("printing_finished(%s)=%s", job, done) if done: delfile() return False if time.time()-start>10*60: printlog.warn("print job %s timed out", job) delfile() return False return True #try again.. if check_printing_finished(): self.timeout_add(10000, check_printing_finished)
def set_modules(self, video_encoders=(), csc_modules=(), video_decoders=()): log("set_modules%s", (video_encoders, csc_modules, video_decoders)) assert not self._initialized, "too late to set modules, the helper is already initialized!" def filt(name, inlist, all_list): exclist = list(x[1:] for x in inlist if x and x.startswith("-")) inclist = list(x for x in inlist if x and not x.startswith("-")) if "all" in inclist: inclist = all_list else: notfound = tuple(x for x in (exclist+inclist) if x and x not in all_list) if notfound: log.warn("Warning: ignoring unknown %s: %s", name, csv(notfound)) return tuple(x for x in inclist if x not in exclist) self.video_encoders = filt("video encoders" , video_encoders, ALL_VIDEO_ENCODER_OPTIONS) self.csc_modules = filt("csc modules" , csc_modules, ALL_CSC_MODULE_OPTIONS) self.video_decoders = filt("video decoders" , video_decoders, ALL_VIDEO_DECODER_OPTIONS) log("VideoHelper.set_modules(%r, %r, %r) video encoders=%s, csc=%s, video decoders=%s", csv(video_encoders), csv(csc_modules), csv(video_decoders), csv(self.video_encoders), csv(self.csc_modules), csv(self.video_decoders))
def copy_to_clipboard(self, *_args): w = self._window if not w: return info = { "wid": w._id, "title": w.get_title(), "override-redirect": w._override_redirect, "state": get_window_state(w), "attributes": get_window_attributes(w), "focused": w._focused, "buttons": csv(b for b, s in w.button_state.items() if s) or "none", "gravity": GRAVITY_STR.get(w.window_gravity, "invalid"), "content-type": w.content_type or "unknown", "pixel-depth": w.pixel_depth or 24, "alpha": w._window_alpha, "opengl": w.is_GL(), "geometry": geom_str(list(w._pos) + list(w._size)), "outer-geometry": geom_str(list(w.get_position()) + list(w.get_size())), "inner-geometry": geom_str(w.get_drawing_area_geometry()), "offsets": csv(str(x) for x in (w.window_offset or ())) or "none", "frame-extents": csv(w._current_frame_extents or []) or "none", "max-size": csv(w.max_window_size), "size-constraints": hsc(w.size_constraints), } #backing: b = w._backing if b: info.update({ "size": csv(b.size), "render-size": csv(b.render_size), "backing-offsets": csv(b.offsets), }) text = "\n".join("%s=%s" % (k, v) for k, v in info.items()) clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) clipboard.set_text(text, len(text))
def set_modules(self, video_encoders=(), csc_modules=(), video_decoders=()): assert not self._initialized, "too late to set modules, the helper is already initialized!" def filt(name, inlist, all_list): notfound = tuple(x for x in inlist if x and x not in all_list) if notfound: log.warn("Warning: ignoring unknown %s: %s", name, csv(notfound)) return tuple(x for x in inlist if x in all_list) self.video_encoders = filt("video encoders", video_encoders, ALL_VIDEO_ENCODER_OPTIONS) self.csc_modules = filt("csc modules", csc_modules, ALL_CSC_MODULE_OPTIONS) self.video_decoders = filt("video decoders", video_decoders, ALL_VIDEO_DECODER_OPTIONS) log( "VideoHelper.set_modules(%s, %s, %s) video encoders=%s, csc=%s, video decoders=%s", csv(video_encoders), csv(csc_modules), csv(video_decoders), csv(self.video_encoders), csv(self.csc_modules), csv(self.video_decoders))
def filt(name, inlist, all_list): notfound = [x for x in inlist if x and x not in all_list] if notfound: log.warn("ignoring unknown %s: %s", name, csv(notfound)) return [x for x in inlist if x in all_list]
def init(self, opts): self.av_sync = opts.av_sync self.sound_properties = typedict() self.speaker_allowed = sound_option(opts.speaker) in ("on", "off") #ie: "on", "off", "on:Some Device", "off:Some Device" mic = [x.strip() for x in opts.microphone.split(":", 1)] self.microphone_allowed = sound_option(mic[0]) in ("on", "off") self.microphone_device = None if self.microphone_allowed and len(mic) == 2: self.microphone_device = mic[1] self.sound_source_plugin = opts.sound_source def sound_option_or_all(*_args): return [] if self.speaker_allowed or self.microphone_allowed: try: from xpra.sound import common assert common except ImportError as e: self.may_notify_audio( "No Audio", "audio subsystem is not installed\n" + " speaker and microphone forwarding are disabled") self.speaker_allowed = False self.microphone_allowed = False else: try: from xpra.sound.common import sound_option_or_all from xpra.sound.wrapper import query_sound self.sound_properties = query_sound() assert self.sound_properties, "query did not return any data" def vinfo(k): val = self.sound_properties.strtupleget(k) assert val, "%s not found in sound properties" % k return ".".join(val[:3]) bits = self.sound_properties.intget("python.bits", 32) log.info("GStreamer version %s for Python %s %s-bit", vinfo("gst.version"), vinfo("python.version"), bits) except Exception as e: log("failed to query sound", exc_info=True) log.error("Error: failed to query sound subsystem:") log.error(" %s", e) self.speaker_allowed = False self.microphone_allowed = False encoders = self.sound_properties.strtupleget("encoders") decoders = self.sound_properties.strtupleget("decoders") self.speaker_codecs = sound_option_or_all("speaker-codec", opts.speaker_codec, decoders) self.microphone_codecs = sound_option_or_all("microphone-codec", opts.microphone_codec, encoders) if not self.speaker_codecs: self.speaker_allowed = False if not self.microphone_codecs: self.microphone_allowed = False self.speaker_enabled = self.speaker_allowed and sound_option( opts.speaker) == "on" self.microphone_enabled = self.microphone_allowed and opts.microphone.lower( ) == "on" log("speaker: codecs=%s, allowed=%s, enabled=%s", encoders, self.speaker_allowed, csv(self.speaker_codecs)) log("microphone: codecs=%s, allowed=%s, enabled=%s, default device=%s", decoders, self.microphone_allowed, csv(self.microphone_codecs), self.microphone_device) log("av-sync=%s", self.av_sync) if POSIX and not OSX: try: from xpra.sound.pulseaudio.pulseaudio_util import get_info as get_pa_info pa_info = get_pa_info() log("pulseaudio info=%s", pa_info) self.sound_properties.update(pa_info) except ImportError as e: log.warn("Warning: no pulseaudio information available") log.warn(" %s", e) except Exception: log.error("failed to add pulseaudio info", exc_info=True) #audio tagging: self.init_audio_tagging(opts.tray_icon)
def __init__(self, sink_type=None, sink_options={}, codecs=get_decoders(), codec_options={}, volume=1.0): if not sink_type: sink_type = get_default_sink() if sink_type not in get_sink_plugins(): raise InitExit(1, "invalid sink: %s" % sink_type) matching = [ x for x in CODEC_ORDER if (x in codecs and x in get_decoders()) ] log("SoundSink(..) found matching codecs %s", matching) if not matching: raise InitExit( 1, "no matching codecs between arguments '%s' and supported list '%s'" % (csv(codecs), csv(get_decoders().keys()))) codec = matching[0] decoder, parser, stream_compressor = get_decoder_elements(codec) SoundPipeline.__init__(self, codec) self.container_format = (parser or "").replace("demux", "").replace("depay", "") self.sink_type = sink_type self.stream_compressor = stream_compressor log("container format=%s, stream_compressor=%s, sink type=%s", self.container_format, self.stream_compressor, self.sink_type) self.levels = deque(maxlen=100) self.volume = None self.src = None self.queue = None self.normal_volume = volume self.target_volume = volume self.volume_timer = 0 self.overruns = 0 self.underruns = 0 self.overrun_events = deque(maxlen=100) self.queue_state = "starting" self.last_data = None self.last_underrun = 0 self.last_overrun = 0 self.refill = True self.last_max_update = monotonic_time() self.last_min_update = monotonic_time() self.level_lock = Lock() pipeline_els = [] appsrc_el = [ "appsrc", #"do-timestamp=1", "name=src", "emit-signals=0", "block=0", "is-live=0", "stream-type=%s" % STREAM_TYPE, "format=%s" % BUFFER_FORMAT ] pipeline_els.append(" ".join(appsrc_el)) if parser: pipeline_els.append(parser) if decoder: decoder_str = plugin_str(decoder, codec_options) pipeline_els.append(decoder_str) pipeline_els.append("audioconvert") pipeline_els.append("audioresample") if QUEUE_TIME > 0: pipeline_els.append(" ".join([ "queue", "name=queue", "min-threshold-time=0", "max-size-buffers=0", "max-size-bytes=0", "max-size-time=%s" % QUEUE_TIME, "leaky=%s" % QUEUE_LEAK ])) pipeline_els.append("volume name=volume volume=0") sink_attributes = SINK_SHARED_DEFAULT_ATTRIBUTES.copy() #anything older than this may cause problems (ie: centos 6.x) #because the attributes may not exist sink_attributes.update(SINK_DEFAULT_ATTRIBUTES.get(sink_type, {})) get_options_cb = DEFAULT_SINK_PLUGIN_OPTIONS.get( sink_type.replace("sink", "")) if get_options_cb: v = get_options_cb() log("%s()=%s", get_options_cb, v) sink_attributes.update(v) sink_attributes.update(sink_options) sink_str = plugin_str(sink_type, sink_attributes) pipeline_els.append(sink_str) if not self.setup_pipeline_and_bus(pipeline_els): return self.volume = self.pipeline.get_by_name("volume") self.src = self.pipeline.get_by_name("src") self.queue = self.pipeline.get_by_name("queue") if self.queue: if QUEUE_SILENT: self.queue.set_property("silent", False) else: self.queue.connect("overrun", self.queue_overrun) self.queue.connect("underrun", self.queue_underrun) self.queue.connect("running", self.queue_running) self.queue.connect("pushing", self.queue_pushing)
"NETWORK": 64, "EXPAND": 16384, "CONTAINER": 32768, "ICON1": 65536 * 1, "ICON2": 65536 * 2, "ICON3": 65536 * 3, "ICON4": 65536 * 4, "ICON5": 65536 * 5, "ICON6": 65536 * 6, "ICON7": 65536 * 7, "ICON8": 65536 * 8, } PRINTER_ENUM_NAMES = dict((v, k) for k, v in PRINTER_ENUM_VALUES.items()) log("PRINTER_ENUM_VALUES: %s", PRINTER_ENUM_VALUES) log("PRINTER_FLAGS=%s", csv(PRINTER_FLAGS)) VALID_PRINTER_FLAGS = ("LOCAL", "SHARED", "CONNECTIONS", "NETWORK", "REMOTE") PRINTER_ENUMS = [] for v in PRINTER_FLAGS: #ie: "SHARED+NETWORK+CONNECTIONS" flags = v.replace('|', '+').split( "+") #ie: ["SHARED", "NETWORK", "CONNECTIONS"] values = [] for flag in flags: #ie: "SHARED" if flag not in VALID_PRINTER_FLAGS: log.warn( "Warning: the following printer flag is invalid and will be ignored: %s", flag) else: values.append(flag) #ie: "SHARED" PRINTER_ENUMS.append(values) log("PRINTER_ENUMS=%s", PRINTER_ENUMS)
def _print_file(self, filename, mimetype, options): printlog("print_file%s", (filename, mimetype, options)) printer = options.strget("printer") title = options.strget("title") copies = options.intget("copies", 1) if title: printlog.info(" sending '%s' to printer '%s'", title, printer) else: printlog.info(" sending to printer '%s'", printer) from xpra.platform.printing import print_files, printing_finished, get_printers printers = get_printers() def delfile(): if not DELETE_PRINTER_FILE: return try: os.unlink(filename) except: printlog("failed to delete print job file '%s'", filename) return False if not printer: printlog.error("Error: the printer name is missing") printlog.error(" printers available: %s", csv(printers.keys()) or "none") delfile() return if printer not in printers: printlog.error("Error: printer '%s' does not exist!", printer) printlog.error(" printers available: %s", csv(printers.keys()) or "none") delfile() return try: job_options = options.get("options") job_options["copies"] = copies job = print_files(printer, [filename], title, job_options) except Exception as e: printlog("print_files%s", (printer, [filename], title, options), exc_info=True) printlog.error("Error: cannot print file '%s'", os.path.basename(filename)) printlog.error(" %s", e) delfile() return printlog("printing %s, job=%s", filename, job) if job <= 0: printlog("printing failed and returned %i", job) delfile() return start = monotonic_time() def check_printing_finished(): done = printing_finished(job) printlog("printing_finished(%s)=%s", job, done) if done: delfile() return False if monotonic_time() - start >= PRINT_JOB_TIMEOUT: printlog.warn("Warning: print job %s timed out", job) delfile() return False return True #try again.. if check_printing_finished(): #check every 10 seconds: self.timeout_add(10000, check_printing_finished)
def keymap_to_xmodmap(trans_keycodes): """ Given a dict with keycodes as keys and lists of keyboard entries as values, (keysym, keycode, index) produce a list of xmodmap instructions to set the x11 keyboard to match it, in the form: ("keycode", keycode, [keysyms]) """ missing_keysyms = [] #the keysyms lookups which failed instructions = [] all_entries = [] for entries in trans_keycodes.values(): all_entries += entries keysyms_per_keycode = max([index for _, index in all_entries]) + 1 for server_keycode, entries in trans_keycodes.items(): keysyms = [None] * keysyms_per_keycode names = [""] * keysyms_per_keycode sentries = sorted(entries, key=lambda x: x[1]) for name, index in sentries: assert 0 <= index and index < keysyms_per_keycode try: keysym = X11Keyboard.parse_keysym(name) except: keysym = None if keysym is None: if name != "": missing_keysyms.append(name) else: if keysyms[index] is not None: #if the client provides multiple keysyms for the same index, #replace with the new one if the old one exists elsewhere, #or skip it if we have another entry for it can_override = any(True for i, v in enumerate(keysyms) if i < index and v == keysyms[index]) can_skip = any(True for i, v in enumerate(keysyms) if i != index and v == keysym) if can_override or can_skip: l = log.debug else: l = log.warn l( "Warning: more than one keysym for keycode %-3i at index %i:", server_keycode, index) l(" entries=%s", csv(tuple(sentries))) l(" keysyms=%s", csv(keysyms)) l(" assigned keysym=%s", names[index]) l(" wanted keysym=%s", name) if can_override: l(" current value also found at another index, overriding it" ) elif can_skip: l(" new value also found elsewhere, skipping it") else: continue names[index] = name keysyms[index] = keysym if name in DEBUG_KEYSYMS: log.info("keymap_to_xmodmap: keysyms[%s]=%s (%s)", index, keysym, name) #remove empty keysyms: while len(keysyms) > 0 and keysyms[0] is None: keysyms = keysyms[1:] l = log if [k for k in keysyms if k in DEBUG_KEYSYMS]: l = log.info l("%s: %s -> %s", server_keycode, names, keysyms) instructions.append(("keycode", server_keycode, keysyms)) if len(missing_keysyms) > 0: log.error("cannot find the X11 keysym for the following key names: %s", set(missing_keysyms)) log("instructions=%s", instructions) return instructions
def estr(entries): try: return csv(tuple(set(x[0] for x in entries))) except: return csv(tuple(entries))