Hello community, here is the log from the commit of package xpra for openSUSE:Leap:15.2 checked in at 2020-04-05 17:07:27 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Leap:15.2/xpra (Old) and /work/SRC/openSUSE:Leap:15.2/.xpra.new.3248 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "xpra" Sun Apr 5 17:07:27 2020 rev:2 rq:790674 version:3.0.8 Changes: -------- --- /work/SRC/openSUSE:Leap:15.2/xpra/xpra.changes 2020-03-21 16:46:30.905607793 +0100 +++ /work/SRC/openSUSE:Leap:15.2/.xpra.new.3248/xpra.changes 2020-04-05 17:07:38.654250052 +0200 @@ -1,0 +2,41 @@ +Tue Mar 31 21:08:47 UTC 2020 - aloi...@gmx.com + +- Update to version 3.0.8 + * fix handling of dpi command line switch (correctly this + time?) + * fix bug report window not getting focus on MacOS + * fix spurious ssh key warnings with newer versions of paramiko + * fix AltGr mode with non-X11 clients, layout-group changes + * fix rare unexpected client exit on MS Windows + * fix MS Windows clipboard: + + update failures + + convert CRLF line endings + * Clipboard: + + fix selection not shown as active in menus (MS Windows and + MacOS) + + fix spurious warnings when sharing a session + + fix clipboard reset with python2 builds + + selection translation for outbound data + + support client applications that don't use TARGETS (ie: + Motif) + + reject invalid targets + * fix 'xpra upgrade' wrongly updating non-xpra displays + * fix logging error in client geometry debugging output + * fix spurious de-iconifications + * fix handling of server control commands with python3 clients + * fix UDP backport bug sending control packets + * fix vfb getting killed on upgrade failure + * fix proxy server cleanup: force forwarders to terminate + * fix session info errors during client exit + * fix printer cleanup errors with invalid UTF8 printer names + * fix transient-for popup window workaround + * fix unicode errors saving xpra runner shell script with + python3 + * better detection of Wayland environments + * use python3 (if installed) by default on Ubuntu Xenial + * don't use Xdummy on arm, too slow + * don't show 'Download' button that we can't honour + * show all pressed keys according to X11 server in 'xpra info' + * try harder not to use video for tiny areas + +------------------------------------------------------------------- Old: ---- xpra-3.0.7.tar.xz New: ---- xpra-3.0.8.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ xpra.spec ++++++ --- /var/tmp/diff_new_pack.QX5A0g/_old 2020-04-05 17:07:39.030250453 +0200 +++ /var/tmp/diff_new_pack.QX5A0g/_new 2020-04-05 17:07:39.034250457 +0200 @@ -19,7 +19,7 @@ %global __requires_exclude ^typelib\\(GtkosxApplication\\)|typelib\\(GdkGLExt\\)|typelib\\(GtkGLExt\\).*$ Name: xpra -Version: 3.0.7 +Version: 3.0.8 Release: 0 Summary: Remote display server for applications and desktops License: GPL-2.0-or-later AND BSD-3-Clause AND LGPL-3.0-or-later AND MIT ++++++ xpra-3.0.7.tar.xz -> xpra-3.0.8.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/NEWS new/xpra-3.0.8/NEWS --- old/xpra-3.0.7/NEWS 2020-03-13 17:51:18.000000000 +0100 +++ new/xpra-3.0.8/NEWS 2020-03-31 13:39:15.000000000 +0200 @@ -1,3 +1,42 @@ +v3.0.8 (2020-03-31) +====================== + -- fix handling of dpi command line switch (correctly this time?) + -- fix bug report window not getting focus on MacOS + -- fix spurious ssh key warnings with newer versions of paramiko + -- fix AltGr mode with non-X11 clients, layout-group changes + -- fix rare unexpected client exit on MS Windows + -- fix MS Windows clipboard: + update failures + convert CRLF line endings + -- Clipboard: + fix selection not shown as active in menus (MS Windows and MacOS) + fix spurious warnings when sharing a session + fix clipboard reset with python2 builds + selection translation for outbound data + support client applications that don't use TARGETS (ie: Motif) + reject invalid targets + -- fix 'xpra upgrade' wrongly updating non-xpra displays + -- fix logging error in client geometry debugging output + -- fix pulseaudio start command with Ubuntu Xenial + -- fix spurious de-iconifications + -- fix handling of server control commands with python3 clients + -- fix UDP backport bug sending control packets + -- fix vfb getting killed on upgrade failure + -- fix proxy server cleanup: force forwarders to terminate + -- fix session info errors during client exit + -- fix printer cleanup errors with invalid UTF8 printer names + -- fix transient-for popup window workaround + -- fix missing libyuv csc module on MacOS + -- fix unicode errors saving xpra runner shell script with python3 + -- try to prevent conflicts with Fedora's packages + -- better detection of Wayland environments + -- use python3 (if installed) by default on Ubuntu Xenial + -- don't use Xdummy on arm, too slow + -- don't show 'Download' button that we can't honour + -- show all pressed keys according to X11 server in 'xpra info' + -- try harder not to use video for tiny areas + + v3.0.7 (2020-03-02) ====================== -- fix avcodec2 race condition crash diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/cups/xpraforwarder new/xpra-3.0.8/cups/xpraforwarder --- old/xpra-3.0.7/cups/xpraforwarder 2020-02-18 06:58:41.000000000 +0100 +++ new/xpra-3.0.8/cups/xpraforwarder 2020-03-21 14:16:55.000000000 +0100 @@ -42,7 +42,7 @@ from urllib.parse import urlparse, parse_qs -__version__ = "3.0.7" +__version__ = "3.0.8" #Writes a syslog entry (msg) at the default facility: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/html5/js/Utilities.js new/xpra-3.0.8/html5/js/Utilities.js --- old/xpra-3.0.7/html5/js/Utilities.js 2020-02-18 06:58:41.000000000 +0100 +++ new/xpra-3.0.8/html5/js/Utilities.js 2020-03-21 14:16:55.000000000 +0100 @@ -10,7 +10,7 @@ 'use strict'; var Utilities = { - VERSION : "3.0.7", + VERSION : "3.0.8", exc : function() { console.error.apply(console, arguments); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/svn-info new/xpra-3.0.8/svn-info --- old/xpra-3.0.7/svn-info 2020-03-13 18:03:50.000000000 +0100 +++ new/xpra-3.0.8/svn-info 2020-03-31 13:40:32.000000000 +0200 @@ -4,10 +4,10 @@ Relative URL: ^/tags/v3.0.x/src Repository Root: file:///var/svn/repos/Xpra Repository UUID: 3bb7dfac-3a0b-4e04-842a-767bc560f471 -Revision: 25629 +Revision: 25881 Node Kind: directory Schedule: normal Last Changed Author: antoine -Last Changed Rev: 25627 -Last Changed Date: 2020-03-12 15:57:01 +0000 (Thu, 12 Mar 2020) +Last Changed Rev: 25879 +Last Changed Date: 2020-03-31 10:03:56 +0100 (Tue, 31 Mar 2020) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/svn-version new/xpra-3.0.8/svn-version --- old/xpra-3.0.7/svn-version 2020-03-13 18:03:50.000000000 +0100 +++ new/xpra-3.0.8/svn-version 2020-03-31 13:40:32.000000000 +0200 @@ -1 +1 @@ -25629 +25881 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/win32/xpra.iss new/xpra-3.0.8/win32/xpra.iss --- old/xpra-3.0.7/win32/xpra.iss 2020-02-18 06:58:42.000000000 +0100 +++ new/xpra-3.0.8/win32/xpra.iss 2020-03-21 14:16:55.000000000 +0100 @@ -1,9 +1,9 @@ [Setup] AppName=Xpra AppId=Xpra_is1 -AppVersion=3.0.7 -AppVerName=Xpra 3.0.7 -UninstallDisplayName=Xpra 3.0.7 +AppVersion=3.0.8 +AppVerName=Xpra 3.0.8 +UninstallDisplayName=Xpra 3.0.8 AppPublisher=xpra.org AppPublisherURL=http:;xpra.org/ DefaultDirName={pf}\Xpra @@ -16,7 +16,7 @@ Compression=lzma2/max SolidCompression=yes AllowUNCPath=false -VersionInfoVersion=3.0.7 +VersionInfoVersion=3.0.8 VersionInfoCompany=xpra.org VersionInfoDescription=multi-platform screen and application forwarding system WizardImageFile=win32\xpra-logo.bmp diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/__init__.py new/xpra-3.0.8/xpra/__init__.py --- old/xpra-3.0.7/xpra/__init__.py 2020-03-13 18:03:50.000000000 +0100 +++ new/xpra-3.0.8/xpra/__init__.py 2020-03-31 13:40:33.000000000 +0200 @@ -4,4 +4,4 @@ # Xpra is released under the terms of the GNU GPL v2, or, at your option, any # later version. See the file COPYING for details. -__version__ = "3.0.7" +__version__ = "3.0.8" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/client/gtk_base/bug_report.py new/xpra-3.0.8/xpra/client/gtk_base/bug_report.py --- old/xpra-3.0.7/xpra/client/gtk_base/bug_report.py 2020-03-08 03:21:09.000000000 +0100 +++ new/xpra-3.0.8/xpra/client/gtk_base/bug_report.py 2020-03-21 14:16:55.000000000 +0100 @@ -15,6 +15,7 @@ choose_file, get_gtk_version_info, JUSTIFY_LEFT, WIN_POS_CENTER, FILE_CHOOSER_ACTION_SAVE, ) +from xpra.platform.gui import force_focus from xpra.util import nonl, envint, repr_ellipsized from xpra.os_util import strtobytes, hexstr, PYTHON3 from xpra.log import Logger @@ -228,6 +229,7 @@ log("show()") if not self.window: self.setup_window() + force_focus() self.window.show_all() self.window.present() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/client/gtk_base/gtk_client_window_base.py new/xpra-3.0.8/xpra/client/gtk_base/gtk_client_window_base.py --- old/xpra-3.0.7/xpra/client/gtk_base/gtk_client_window_base.py 2020-02-07 12:19:08.000000000 +0100 +++ new/xpra-3.0.8/xpra/client/gtk_base/gtk_client_window_base.py 2020-03-21 14:16:55.000000000 +0100 @@ -551,7 +551,7 @@ #this generates a configure event which ensures the server has the correct window position wfs = self._client.get_window_frame_sizes() if wfs and decorated and not was_decorated: - geomlog("set_decorated(%s) re-adjusting window location using %s", wfs) + geomlog("set_decorated(%s) re-adjusting window location using %s", decorated, wfs) normal = wfs.get("normal") fixed = wfs.get("fixed") if normal and fixed: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/client/gtk_base/gtk_tray_menu_base.py new/xpra-3.0.8/xpra/client/gtk_base/gtk_tray_menu_base.py --- old/xpra-3.0.7/xpra/client/gtk_base/gtk_tray_menu_base.py 2019-09-24 15:53:59.000000000 +0200 +++ new/xpra-3.0.8/xpra/client/gtk_base/gtk_tray_menu_base.py 2020-03-24 06:22:25.000000000 +0100 @@ -626,8 +626,8 @@ clipboardlog("set_new_remote_clipboard(%s)", remote_clipboard) ch = self.client.clipboard_helper local_clipboard = "CLIPBOARD" - ch._local_to_remote[local_clipboard] = remote_clipboard - ch._remote_to_local[remote_clipboard] = local_clipboard + ch._local_to_remote = {local_clipboard : remote_clipboard} + ch._remote_to_local = {remote_clipboard : local_clipboard} selections = [remote_clipboard] clipboardlog.info("server clipboard synchronization changed to %s selection", remote_clipboard) #tell the server what to look for: @@ -641,12 +641,14 @@ selection_menu = self.menuitem("Selection", None, "Choose which remote clipboard to connect to") selection_submenu = gtk.Menu() selection_menu.set_submenu(selection_submenu) + rc_setting = None + if len(ch._local_to_remote)==1: + rc_setting = tuple(ch._local_to_remote.values())[0] self.popup_menu_workaround(selection_submenu) for label in CLIPBOARD_LABELS: remote_clipboard = CLIPBOARD_LABEL_TO_NAME[label] selection_item = CheckMenuItem(label) - active = getattr(ch, "remote_clipboard", "CLIPBOARD")==remote_clipboard - selection_item.set_active(active) + selection_item.set_active(remote_clipboard==rc_setting) selection_item.set_draw_as_radio(True) def remote_clipboard_changed(item): self.remote_clipboard_changed(item, selection_submenu) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/client/gtk_base/open_requests.py new/xpra-3.0.8/xpra/client/gtk_base/open_requests.py --- old/xpra-3.0.7/xpra/client/gtk_base/open_requests.py 2019-12-08 12:59:03.000000000 +0100 +++ new/xpra-3.0.8/xpra/client/gtk_base/open_requests.py 2020-03-21 14:16:55.000000000 +0100 @@ -168,10 +168,11 @@ elif printit: hbox.pack_start(self.btn("Print", None, ok, "printer.png")) else: - hbox.pack_start(self.btn("Download", None, ok, "download.png")) if openit: hbox.pack_start(self.btn("Download and Open", None, ok, "open.png")) hbox.pack_start(self.btn("Open on server", None, remote)) + else: + hbox.pack_start(self.btn("Download", None, ok, "download.png")) return hbox def schedule_timer(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/client/gtk_base/session_info.py new/xpra-3.0.8/xpra/client/gtk_base/session_info.py --- old/xpra-3.0.7/xpra/client/gtk_base/session_info.py 2019-12-23 17:11:42.000000000 +0100 +++ new/xpra-3.0.8/xpra/client/gtk_base/session_info.py 2020-03-30 18:52:50.000000000 +0200 @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # This file is part of Xpra. -# Copyright (C) 2011-2019 Antoine Martin <anto...@xpra.org> +# Copyright (C) 2011-2020 Antoine Martin <anto...@xpra.org> # Copyright (C) 2010 Nathaniel Smith <n...@pobox.com> # Xpra is released under the terms of the GNU GPL v2, or, at your option, any # later version. See the file COPYING for details. @@ -597,7 +597,8 @@ return not self.is_closed def populate(self, *_args): - if self.is_closed or not self.connection: + conn = self.connection + if self.is_closed or not conn: return False self.client.send_ping() self.last_populate_time = monotonic_time() @@ -605,8 +606,8 @@ self.show_opengl_state() self.show_window_renderers() #record bytecount every second: - self.net_in_bytecount.append(self.connection.input_bytecount) - self.net_out_bytecount.append(self.connection.output_bytecount) + self.net_in_bytecount.append(conn.input_bytecount) + self.net_out_bytecount.append(conn.output_bytecount) if mixin_features.audio and SHOW_SOUND_STATS: if self.client.sound_in_bytecount>0: self.sound_in_bitcount.append(self.client.sound_in_bytecount * 8) @@ -840,10 +841,19 @@ #no longer connected! return False c = p._conn - self.input_packets_label.set_text(std_unit_dec(p.input_packetcount)) - self.input_bytes_label.set_text(std_unit_dec(c.input_bytecount)) - self.output_packets_label.set_text(std_unit_dec(p.output_packetcount)) - self.output_bytes_label.set_text(std_unit_dec(c.output_bytecount)) + if c: + self.input_packets_label.set_text(std_unit_dec(p.input_packetcount)) + self.input_bytes_label.set_text(std_unit_dec(c.input_bytecount)) + self.output_packets_label.set_text(std_unit_dec(p.output_packetcount)) + self.output_bytes_label.set_text(std_unit_dec(c.output_bytecount)) + else: + for l in ( + self.input_packets_label, + self.input_bytes_label, + self.output_packets_label, + self.output_bytes_label, + ): + l.set_text("n/a") if mixin_features.audio: def get_sound_info(supported, prop): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/client/mixins/display.py new/xpra-3.0.8/xpra/client/mixins/display.py --- old/xpra-3.0.7/xpra/client/mixins/display.py 2020-01-20 20:48:07.000000000 +0100 +++ new/xpra-3.0.8/xpra/client/mixins/display.py 2020-03-21 14:16:55.000000000 +0100 @@ -143,7 +143,7 @@ dpi = 0 if self.dpi>0: #scale it: - dpi = self.cx(self.cy(self.dpi*2)) + dpi = iround((self.cx(self.dpi) + self.cy(self.dpi))/2.0) else: #not supplied, use platform detection code: #platforms may also provide per-axis dpi (later win32 versions do) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/client/mixins/window_manager.py new/xpra-3.0.8/xpra/client/mixins/window_manager.py --- old/xpra-3.0.7/xpra/client/mixins/window_manager.py 2020-03-08 03:21:09.000000000 +0100 +++ new/xpra-3.0.8/xpra/client/mixins/window_manager.py 2020-03-30 18:52:50.000000000 +0200 @@ -743,7 +743,7 @@ #if possible, choosing the currently focused window (if there is one..) pid = metadata.intget("pid", 0) watcher_pid = self.assign_signal_watcher_pid(wid, pid) - if override_redirect and pid>0 and metadata.intget("transient-for", 0)>0 and metadata.strget("role")=="popup": + if override_redirect and pid>0 and metadata.intget("transient-for", 0)==0 and metadata.strget("role")=="popup": tfor = None for twid, twin in self._id_to_window.items(): if not twin._override_redirect and twin._metadata.intget("pid", -1)==pid: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/client/ui_client_base.py new/xpra-3.0.8/xpra/client/ui_client_base.py --- old/xpra-3.0.7/xpra/client/ui_client_base.py 2020-01-08 12:52:06.000000000 +0100 +++ new/xpra-3.0.8/xpra/client/ui_client_base.py 2020-03-24 06:22:25.000000000 +0100 @@ -539,7 +539,7 @@ return {"" : caps} def _process_control(self, packet): - command = packet[1] + command = bytestostr(packet[1]) if command=="show_session_info": args = packet[2:] log("calling show_session_info%s on server request", args) @@ -564,11 +564,11 @@ if len(args)<2: log.warn("not enough arguments for debug control command") return - log_cmd = args[0] + log_cmd = bytestostr(args[0]) if log_cmd not in ("enable", "disable"): log.warn("invalid debug control mode: '%s' (must be 'enable' or 'disable')", log_cmd) return - categories = args[1:] + categories = tuple(bytestostr(x) for x in args[1:]) from xpra.log import add_debug_category, add_disabled_category, enable_debug_for, disable_debug_for if log_cmd=="enable": add_debug_category(*categories) @@ -578,7 +578,6 @@ add_disabled_category(*categories) loggers = disable_debug_for(*categories) log.info("%sd debugging for: %s", log_cmd, loggers) - return else: log.warn("received invalid control command from server: %s", command) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/clipboard/clipboard_timeout_helper.py new/xpra-3.0.8/xpra/clipboard/clipboard_timeout_helper.py --- old/xpra-3.0.7/xpra/clipboard/clipboard_timeout_helper.py 2020-03-13 17:51:18.000000000 +0100 +++ new/xpra-3.0.8/xpra/clipboard/clipboard_timeout_helper.py 2020-03-21 14:16:55.000000000 +0100 @@ -56,7 +56,8 @@ def _send_clipboard_token_handler(self, proxy, packet_data=()): if log.is_debug_enabled(): log("_send_clipboard_token_handler(%s, %s)", proxy, repr_ellipsized(str(packet_data))) - packet = ["clipboard-token", proxy._selection] + remote = self.local_to_remote(proxy._selection) + packet = ["clipboard-token", remote] if packet_data: #append 'TARGETS' unchanged: packet.append(packet_data[0]) @@ -115,7 +116,7 @@ proxy.got_contents(target, dtype, dformat, data) def client_reset(self): - super().client_reset() + ClipboardProtocolHelperCore.client_reset(self) #timeout all pending requests cor = self._clipboard_outstanding_requests if cor: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/net/ssh.py new/xpra-3.0.8/xpra/net/ssh.py --- old/xpra-3.0.7/xpra/net/ssh.py 2020-02-10 16:32:04.000000000 +0100 +++ new/xpra-3.0.8/xpra/net/ssh.py 2020-03-21 14:16:55.000000000 +0100 @@ -530,13 +530,6 @@ log("no keyfile at '%s'", keyfile_path) continue log("trying '%s'", keyfile_path) - key_data = load_binary_file(keyfile_path) - if key_data and key_data.find(b"BEGIN OPENSSH PRIVATE KEY")>=0: - log.warn("Warning: private key '%s'", keyfile_path) - log.warn(" this file seems to be using OpenSSH's own format") - log.warn(" please convert it to something more standard (ie: PEM)") - log.warn(" so it can be used with the paramiko backend") - log.warn(" or switch to the OpenSSH backend with '--ssh=ssh'") key = None import paramiko for pkey_classname in ("RSA", "DSS", "ECDSA", "Ed25519"): @@ -563,6 +556,13 @@ break except Exception as e: log("auth_publickey() loading as %s", pkey_classname, exc_info=True) + key_data = load_binary_file(keyfile_path) + if key_data and key_data.find(b"BEGIN OPENSSH PRIVATE KEY")>=0 and paramiko.__version__<"2.7": + log.warn("Warning: private key '%s'", keyfile_path) + log.warn(" this file seems to be using OpenSSH's own format") + log.warn(" please convert it to something more standard (ie: PEM)") + log.warn(" so it can be used with the paramiko backend") + log.warn(" or switch to the OpenSSH backend with '--ssh=ssh'") if key: log("auth_publickey using %s as %s: %s", keyfile_path, pkey_classname, keymd5(key)) try: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/net/udp_protocol.py new/xpra-3.0.8/xpra/net/udp_protocol.py --- old/xpra-3.0.7/xpra/net/udp_protocol.py 2020-01-20 20:48:07.000000000 +0100 +++ new/xpra-3.0.8/xpra/net/udp_protocol.py 2020-03-30 18:52:50.000000000 +0200 @@ -181,7 +181,7 @@ #resend a new one self.cancel_control_timer() self.send_control() - self._add_packet_to_queue(packet, fail_cb=self.send_control_failed, synchronous=False) + self._add_packet_to_queue(packet, fail_cb=send_control_failed, synchronous=False) self.cancel = set() self.schedule_control() return False @@ -345,14 +345,15 @@ else: ip.last_time = now ip.chunks[chunk] = data - if seqno!=self.last_sequence+1: + if seqno>self.last_sequence+1: #we're waiting for a packet and this is not it, #make sure any gaps are marked as incomplete: for i in range(self.last_sequence+1, seqno): if i not in self.pending_packets and i not in self.can_skip: self.pending_packets[i] = PendingPacket(i, now) #make sure we request the missing packets: - self.schedule_control(self.jitter) + mcount = seqno-self.last_sequence + self.schedule_control(self.jitter//mcount) if synchronous: #we have to wait for the missing chunks / packets log("process_udp_data: queuing %i as we're still waiting for %i", seqno, self.last_sequence+1) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/os_util.py new/xpra-3.0.8/xpra/os_util.py --- old/xpra-3.0.7/xpra/os_util.py 2020-01-01 15:09:30.000000000 +0100 +++ new/xpra-3.0.8/xpra/os_util.py 2020-03-30 18:52:50.000000000 +0200 @@ -264,11 +264,14 @@ return True def is_Wayland(): - backend = os.environ.get("GDK_BACKEND", "") + return _is_Wayland(os.environ) + +def _is_Wayland(env): + backend = env.get("GDK_BACKEND", "") if backend=="wayland": return True return backend!="x11" and ( - os.environ.get("WAYLAND_DISPLAY") or os.environ.get("XDG_SESSION_TYPE")=="wayland" + bool(env.get("WAYLAND_DISPLAY")) or env.get("XDG_SESSION_TYPE")=="wayland" ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/platform/darwin/keyboard_config.py new/xpra-3.0.8/xpra/platform/darwin/keyboard_config.py --- old/xpra-3.0.7/xpra/platform/darwin/keyboard_config.py 2020-01-25 11:03:51.000000000 +0100 +++ new/xpra-3.0.8/xpra/platform/darwin/keyboard_config.py 2020-03-24 06:22:26.000000000 +0100 @@ -28,8 +28,8 @@ keycode = KEYCODES.get(keyname, -1) if keycode==-1: keycode = KEYCODES.get(keyname.upper(), -1) - log("get_keycode%s=%s", (client_keycode, keyname, pressed, modifiers), keycode) - return keycode + log("get_keycode%s=%s, %s", (client_keycode, keyname, pressed, modifiers), keycode, group) + return keycode, group #we currently assume that all key events are sent using X11 names, #so we need to translate them to osx keys diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/platform/darwin/osx_clipboard.py new/xpra-3.0.8/xpra/platform/darwin/osx_clipboard.py --- old/xpra-3.0.7/xpra/platform/darwin/osx_clipboard.py 2019-09-24 15:54:00.000000000 +0200 +++ new/xpra-3.0.8/xpra/platform/darwin/osx_clipboard.py 2020-03-30 18:52:50.000000000 +0200 @@ -119,12 +119,18 @@ return str(text) def get_contents(self, target, got_contents): + log("get_contents%s", (target, got_contents)) if target=="TARGETS": #we only support text at the moment: - got_contents("ATOM", 32, ["text/plain", "text/plain;charset=utf-8", "UTF8_STRING"]) + got_contents("ATOM", 32, ["TEXT", "STRING", "text/plain", "text/plain;charset=utf-8", "UTF8_STRING"]) + return + if target not in ("TEXT", "STRING", "text/plain", "text/plain;charset=utf-8", "UTF8_STRING"): + #we don't know how to handle this target, + #return an empty response: + got_contents(target, 8, b"") return text = self.get_clipboard_text() - got_contents("bytes", 8, text) + got_contents(target, 8, text) def got_token(self, targets, target_data=None, claim=True, _synchronous_client=False): # the remote end now owns the clipboard diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/platform/darwin/osx_menu.py new/xpra-3.0.8/xpra/platform/darwin/osx_menu.py --- old/xpra-3.0.7/xpra/platform/darwin/osx_menu.py 2019-09-24 15:54:00.000000000 +0200 +++ new/xpra-3.0.8/xpra/platform/darwin/osx_menu.py 2020-03-24 06:22:26.000000000 +0100 @@ -328,7 +328,11 @@ #find the menu item matching the current settings, #and select it try: - label = CLIPBOARD_NAME_TO_LABEL.get(self.client.clipboard_helper.remote_clipboard) + ch = self.client.clipboard_helper + rc_setting = "Clipboard" + if len(ch._local_to_remote)==1: + rc_setting = tuple(ch._local_to_remote.values())[0] + label = CLIPBOARD_NAME_TO_LABEL.get(rc_setting) self.select_clipboard_menu_option(None, label, CLIPBOARD_LABELS) except Exception: clipboardlog("failed to select remote clipboard option in menu", exc_info=True) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/platform/win32/clipboard.py new/xpra-3.0.8/xpra/platform/win32/clipboard.py --- old/xpra-3.0.7/xpra/platform/win32/clipboard.py 2020-03-08 03:21:09.000000000 +0100 +++ new/xpra-3.0.8/xpra/platform/win32/clipboard.py 2020-03-30 18:52:50.000000000 +0200 @@ -24,7 +24,7 @@ ClipboardProxyCore, log, _filter_targets, TEXT_TARGETS, MAX_CLIPBOARD_PACKET_SIZE, ) -from xpra.util import csv, repr_ellipsized +from xpra.util import csv, repr_ellipsized, envbool from xpra.os_util import bytestostr, strtobytes from xpra.gtk_common.gobject_compat import import_glib @@ -55,6 +55,9 @@ win32con.WM_VSCROLLCLIPBOARD : "WM_VSCROLLCLIPBOARD", } +CONVERT_LINE_ENDINGS = envbool("XPRA_CONVERT_LINE_ENDINGS", True) + + #initialize the window we will use #for communicating with the OS clipboard API: @@ -183,9 +186,15 @@ self.send_clipboard_token_handler(self) def get_contents(self, target, got_contents): + log("get_contents%s", (target, got_contents)) if target=="TARGETS": #we only support text at the moment: - got_contents("ATOM", 32, ["text/plain", "text/plain;charset=utf-8", "UTF8_STRING"]) + got_contents("ATOM", 32, ["TEXT", "STRING", "text/plain", "text/plain;charset=utf-8", "UTF8_STRING"]) + return + if target not in ("TEXT", "STRING", "text/plain", "text/plain;charset=utf-8", "UTF8_STRING"): + #we don't know how to handle this target, + #return an empty response: + got_contents(target, 8, b"") return def got_text(text): log("got_text(%s)", repr_ellipsized(bytestostr(text))) @@ -194,7 +203,7 @@ log.error("Error: failed to get clipboard data") if error_text: log.error(" %s", error_text) - got_contents("text/plain", 8, b"") + got_contents(target, 8, b"") self.get_clipboard_text(got_text, errback) @@ -262,8 +271,12 @@ s = buf.raw[:l-1] else: s = buf.raw[:l] + if CONVERT_LINE_ENDINGS: + v = s.decode("utf8").replace("\r\n", "\n").encode("utf8") + else: + v = strtobytes(s) log("got %i bytes of data: %s", len(s), repr_ellipsized(str(s))) - callback(strtobytes(s)) + callback(v) else: errback("failed to convert to UTF8: %s" % FormatError(get_last_error())) finally: @@ -287,6 +300,8 @@ def do_set_clipboard_text(self, text): #convert to wide char #get the length in wide chars: + if CONVERT_LINE_ENDINGS: + text = text.decode("utf8").replace("\n", "\r\n").encode("utf8") wlen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, len(text), None, 0) if not wlen: self.set_err("failed to prepare to convert to wide char") @@ -319,7 +334,6 @@ # self.set_err("failed to empty the clipboard") #self.with_clipboard_lock(EmptyClipboard, empty_error) def cleanup(): - GlobalFree(buf) glib.idle_add(self.remove_block) ret = [False] def do_set_data(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/platform/win32/keyboard_config.py new/xpra-3.0.8/xpra/platform/win32/keyboard_config.py --- old/xpra-3.0.7/xpra/platform/win32/keyboard_config.py 2020-01-25 11:03:51.000000000 +0100 +++ new/xpra-3.0.8/xpra/platform/win32/keyboard_config.py 2020-03-24 06:22:26.000000000 +0100 @@ -39,7 +39,7 @@ def do_get_keycode(self, client_keycode, keyname, pressed, modifiers, group): keycode = KEYCODES.get(keyname, -1) log("get_keycode%s=%s", (client_keycode, keyname, pressed, modifiers, group), keycode) - return keycode + return keycode, group def make_keymask_match(self, modifier_list, ignored_modifier_keycode=None, ignored_modifier_keynames=None): log("make_keymask_match%s", (modifier_list, ignored_modifier_keycode, ignored_modifier_keynames)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/platform/win32/win32_events.py new/xpra-3.0.8/xpra/platform/win32/win32_events.py --- old/xpra-3.0.7/xpra/platform/win32/win32_events.py 2019-09-24 15:54:00.000000000 +0200 +++ new/xpra-3.0.8/xpra/platform/win32/win32_events.py 2020-03-24 06:22:26.000000000 +0100 @@ -199,10 +199,10 @@ else: ut = "/ unexpected" l("unknown %s message: %s / %#x / %#x", ut, event_name, int(wParam), int(lParam)) + if msg==win32con.WM_DESTROY: + self.cleanup() elif self.hwnd and hWnd!=None: log.warn("invalid hwnd: %s (expected %s)", hWnd, self.hwnd) - if msg==win32con.WM_DESTROY: - self.cleanup() r = DefWindowProcW(hWnd, msg, wParam, lParam) log("DefWindowProc%s=%s", (hWnd, msg, wParam, lParam), r) return r diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/scripts/config.py new/xpra-3.0.8/xpra/scripts/config.py --- old/xpra-3.0.7/xpra/scripts/config.py 2020-02-07 12:19:09.000000000 +0100 +++ new/xpra-3.0.8/xpra/scripts/config.py 2020-03-21 14:16:55.000000000 +0100 @@ -132,6 +132,10 @@ if sys.platform.find("bsd")>=0 and Xdummy_ENABLED is None: warn("Warning: sorry, no support for Xdummy on %s" % sys.platform) return get_Xvfb_command() + import platform + if platform.uname()[4].startswith("arm"): + #arm struggles to launch Xdummy, so use Xvfb: + return get_Xvfb_command() xorg_bin = get_xorg_bin() def Xorg_suid_check(): @@ -815,7 +819,7 @@ #we just don't disable it (because the option does not exist!): from xpra.util import envbool MEMFD = envbool("XPRA_PULSEAUDIO_MEMFD", False) - if not MEMFD: + if not MEMFD and (not is_Ubuntu() or getUbuntuVersion()>=(18, 4)): cmd.append("--enable-memfd=no") return cmd diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/scripts/main.py new/xpra-3.0.8/xpra/scripts/main.py --- old/xpra-3.0.7/xpra/scripts/main.py 2020-03-13 17:51:19.000000000 +0100 +++ new/xpra-3.0.8/xpra/scripts/main.py 2020-03-31 13:39:15.000000000 +0200 @@ -62,7 +62,11 @@ return DISPLAY +saved_env = {} + def main(script_file, cmdline): + global saved_env + saved_env = os.environ.copy() ml = envint("XPRA_MEM_USAGE_LOGGER") if ml>0: from xpra.util import start_mem_watcher @@ -548,6 +552,8 @@ from xpra.client.gtk_base.example import transparent_window return transparent_window.main() elif mode == "initenv": + if not POSIX: + raise InitExit(EXIT_UNSUPPORTED, "initenv is not supported on this OS") from xpra.server.server_util import xpra_runner_shell_script, write_runner_shell_scripts script = xpra_runner_shell_script(script_file, os.getcwd(), options.socket_dir) write_runner_shell_scripts(script, False) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/scripts/server.py new/xpra-3.0.8/xpra/scripts/server.py --- old/xpra-3.0.7/xpra/scripts/server.py 2020-01-01 15:09:30.000000000 +0100 +++ new/xpra-3.0.8/xpra/scripts/server.py 2020-03-30 18:52:50.000000000 +0200 @@ -373,7 +373,7 @@ upgrading_desktop = mode == "upgrade-desktop" shadowing = mode == "shadow" proxying = mode == "proxy" - clobber = upgrading or upgrading_desktop or opts.use_display + clobber = int(upgrading or upgrading_desktop) | int(opts.use_display)*2 start_vfb = not (shadowing or proxying or clobber) if not proxying and PYTHON3 and POSIX and not OSX: @@ -418,7 +418,7 @@ error_cb("too many extra arguments (%i): only expected a display number" % len(extra_args)) if len(extra_args) == 1: display_name = extra_args[0] - if not shadowing and not proxying and not opts.use_display: + if not shadowing and not proxying and not upgrading and not opts.use_display: display_name_check(display_name) else: if proxying: @@ -859,6 +859,11 @@ log.error("Error: cannot start the %s server", app.session_type, exc_info=True) log.error(str(e)) log.info("") + if upgrading or upgrading_desktop: + #something abnormal occurred, + #don't kill the vfb on exit: + from xpra.server import EXITING_CODE + app._upgrading = EXITING_CODE app.cleanup() return 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/server/keyboard_config_base.py new/xpra-3.0.8/xpra/server/keyboard_config_base.py --- old/xpra-3.0.7/xpra/server/keyboard_config_base.py 2020-01-25 11:03:51.000000000 +0100 +++ new/xpra-3.0.8/xpra/server/keyboard_config_base.py 2020-03-24 06:22:26.000000000 +0100 @@ -45,17 +45,17 @@ def get_keycode(self, client_keycode, keyname, pressed, modifiers, group): if not keyname and client_keycode<0: - return -1 + return -1, group if not pressed: - keycode = self.pressed_translation.get(client_keycode) - if keycode: + r = self.pressed_translation.get(client_keycode) + if r: #del self.pressed_translation[client_keycode] - return keycode - keycode = self.do_get_keycode(client_keycode, keyname, pressed, modifiers, group) + return r + keycode, group = self.do_get_keycode(client_keycode, keyname, pressed, modifiers, group) if pressed not in (None, -1): #keep track of it so we can unpress the same key: - self.pressed_translation[client_keycode] = keycode - return keycode + self.pressed_translation[client_keycode] = keycode, group + return keycode, group def do_get_keycode(self, _client_keycode, _keyname, _pressed, _modifiers, _group): from xpra.log import Logger diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/server/mixins/clipboard_server.py new/xpra-3.0.8/xpra/server/mixins/clipboard_server.py --- old/xpra-3.0.7/xpra/server/mixins/clipboard_server.py 2020-03-13 17:51:19.000000000 +0100 +++ new/xpra-3.0.8/xpra/server/mixins/clipboard_server.py 2020-03-24 06:22:26.000000000 +0100 @@ -219,14 +219,16 @@ log.warn(" but we do not support clipboard at all! Ignoring it.") return cc = self._clipboard_client + cc.clipboard_enabled = clipboard_enabled + log("toggled clipboard to %s for %s", clipboard_enabled, ss.protocol) if cc!=ss or ss is None: - log.warn("Warning: received a request to change the clipboard status,") - log.warn(" but it does not come from the clipboard owner! Ignoring it.") + log("received a request to change the clipboard status,") + log(" but it does not come from the clipboard owner! Ignoring it.") + log(" from %s", cc) + log(" owner is %s", self._clipboard_client) return - cc.clipboard_enabled = clipboard_enabled if not clipboard_enabled: ch.enable_selections([]) - log("toggled clipboard to %s for %s", clipboard_enabled, ss.protocol) def clipboard_progress(self, local_requests, _remote_requests): assert self.clipboard diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/server/mixins/input_server.py new/xpra-3.0.8/xpra/server/mixins/input_server.py --- old/xpra-3.0.7/xpra/server/mixins/input_server.py 2020-03-08 03:21:09.000000000 +0100 +++ new/xpra-3.0.8/xpra/server/mixins/input_server.py 2020-03-24 06:22:26.000000000 +0100 @@ -156,10 +156,10 @@ keyname = bytestostr(keyname) modifiers = list(bytestostr(x) for x in modifiers) self.set_ui_driver(ss) + keycode, group = self.get_keycode(ss, client_keycode, keyname, pressed, modifiers, group) + keylog("process_key_action(%s) server keycode=%s, group=%i", packet, keycode, group) if group>=0: self.set_keyboard_layout_group(group) - keycode = self.get_keycode(ss, client_keycode, keyname, pressed, modifiers, group) - keylog("process_key_action(%s) server keycode=%s", packet, keycode) #currently unused: (group, is_modifier) = packet[8:10] self._focus(ss, wid, None) ss.make_keymask_match(modifiers, keycode, ignored_modifier_keynames=[keyname]) @@ -257,7 +257,9 @@ group = 0 if len(packet)>=7: group = packet[6] - keycode = ss.get_keycode(client_keycode, keyname, modifiers, group) + keycode, group = ss.get_keycode(client_keycode, keyname, modifiers, group) + if group>=0: + self.set_keyboard_layout_group(group) #key repeat uses modifiers from a pointer event, so ignore mod_pointermissing: ss.make_keymask_match(modifiers) if not ss.keyboard_config.sync: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/server/proxy/proxy_server.py new/xpra-3.0.8/xpra/server/proxy/proxy_server.py --- old/xpra-3.0.7/xpra/server/proxy/proxy_server.py 2020-03-13 17:51:19.000000000 +0100 +++ new/xpra-3.0.8/xpra/server/proxy/proxy_server.py 2020-03-30 18:52:50.000000000 +0200 @@ -1,11 +1,12 @@ # This file is part of Xpra. -# Copyright (C) 2013-2019 Antoine Martin <anto...@xpra.org> +# Copyright (C) 2013-2020 Antoine Martin <anto...@xpra.org> # Copyright (C) 2008 Nathaniel Smith <n...@pobox.com> # Xpra is released under the terms of the GNU GPL v2, or, at your option, any # later version. See the file COPYING for details. import os import sys +import time from multiprocessing import Queue as MQueue, freeze_support #@UnresolvedImport from xpra.gtk_common.gobject_compat import import_glib @@ -16,6 +17,7 @@ from xpra.os_util import ( get_username_for_uid, get_groups, get_home_for_uid, bytestostr, getuid, getgid, WIN32, POSIX, + monotonic_time, register_SIGUSR_signals, ) from xpra.server.server_core import ServerCore @@ -41,6 +43,7 @@ CAN_STOP_PROXY = envbool("XPRA_CAN_STOP_PROXY", getuid()!=0) STOP_PROXY_SOCKET_TYPES = os.environ.get("XPRA_STOP_PROXY_SOCKET_TYPES", "unix-domain,named-pipe").split(",") PROXY_INSTANCE_THREADED = envbool("XPRA_PROXY_INSTANCE_THREADED", False) +PROXY_CLEANUP_GRACE_PERIOD = envfloat("XPRA_PROXY_CLEANUP_GRACE_PERIOD", "0.5") MAX_CONCURRENT_CONNECTIONS = envint("XPRA_PROXY_MAX_CONCURRENT_CONNECTIONS", 200) if WIN32: @@ -156,28 +159,30 @@ return "stopped proxy instance for display %s" % display raise ControlError("no proxy found for display %s" % display) - def stop_all_proxies(self): + def stop_all_proxies(self, force=False): instances = self.instances log("stop_all_proxies() will stop proxy instances: %s", instances) for instance in tuple(instances.keys()): - self.stop_proxy(instance) - self.instances = {} + self.stop_proxy(instance, force) log("stop_all_proxies() done") - def stop_proxy(self, instance): + def stop_proxy(self, instance, force=False): v = self.instances.get(instance) if not v: log.error("Error: proxy instance not found for %s", instance) return log("stop_proxy(%s) is_alive=%s", instance, instance.is_alive()) - if not instance.is_alive(): + if not instance.is_alive() and not force: return isprocess, _, mq = v log("stop_proxy(%s) %s", instance, v) #different ways of stopping for process vs threaded implementations: if isprocess: - #send message: - mq.put("stop") + if force: + instance.terminate() + else: + #send message: + mq.put("stop") else: #direct method call: instance.stop(None, "proxy server request") @@ -186,6 +191,20 @@ def cleanup(self): self.stop_all_proxies() ServerCore.cleanup(self) + start = monotonic_time() + live = True + log("cleanup() proxy instances: %s", self.instances) + while monotonic_time()-start<PROXY_CLEANUP_GRACE_PERIOD and live: + live = tuple(x for x in tuple(self.instances.keys()) if x.is_alive()) + if live: + log("cleanup() still %i proxies alive: %s", len(live), live) + time.sleep(0.1) + if live: + self.stop_all_proxies(True) + log("cleanup() frames remaining:") + from xpra.util import dump_all_frames + dump_all_frames(log) + def do_quit(self): self.main_loop.quit() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/server/rfb/rfb_server.py new/xpra-3.0.8/xpra/server/rfb/rfb_server.py --- old/xpra-3.0.7/xpra/server/rfb/rfb_server.py 2020-01-25 11:03:51.000000000 +0100 +++ new/xpra-3.0.8/xpra/server/rfb/rfb_server.py 2020-03-24 06:22:26.000000000 +0100 @@ -157,8 +157,8 @@ return modifiers = [] keyval = 0 - keycode = source.keyboard_config.get_keycode(0, keyname, pressed, modifiers, 0) - log("rfb keycode(%s)=%s", keyname, keycode) + keycode, group = source.keyboard_config.get_keycode(0, keyname, pressed, modifiers, 0) + log("rfb keycode(%s)=%s, %s", keyname, keycode, group) if keycode: is_mod = source.keyboard_config.is_modifier(keycode) self._handle_key(wid, bool(pressed), keyname, keyval, keycode, modifiers, is_mod, True) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/server/server_util.py new/xpra-3.0.8/xpra/server/server_util.py --- old/xpra-3.0.7/xpra/server/server_util.py 2019-09-24 15:54:01.000000000 +0200 +++ new/xpra-3.0.8/xpra/server/server_util.py 2020-03-31 13:39:15.000000000 +0200 @@ -7,93 +7,181 @@ import os.path from xpra.util import envbool -from xpra.os_util import OSX, shellsub, getuid, get_util_logger, osexpand, umask_context +from xpra.os_util import OSX, shellsub, getuid, get_util_logger, osexpand, umask_context, PYTHON3 from xpra.platform.dotxpra import norm_makepath from xpra.scripts.config import InitException -def sh_quotemeta(s): - return "'" + s.replace("'", "'\\''") + "'" +if PYTHON3: + def sh_quotemeta(s): + return b"'" + s.replace(b"'", b"'\\''") + b"'" + + def xpra_runner_shell_script(xpra_file, starting_dir, socket_dir): + script = [] + script.append(b"#!/bin/sh\n") + for var, value in os.environb.items(): + # these aren't used by xpra, and some should not be exposed + # as they are either irrelevant or simply do not match + # the new environment used by xpra + # TODO: use a whitelist + if var in (b"XDG_SESSION_COOKIE", b"LS_COLORS", b"DISPLAY"): + continue + #XPRA_SOCKET_DIR is a special case, it is handled below + if var==b"XPRA_SOCKET_DIR": + continue + if var==b"XPRA_ALT_PYTHON_RETRY": + #the environment might have changed, + #and we may need to retry with a different interpreter + #different from the one that created this script + continue + if var.startswith(b"BASH_FUNC"): + #some versions of bash will apparently generate functions + #that cannot be reloaded using this script + continue + # :-separated envvars that people might change while their server is + # going: + if var in (b"PATH", b"LD_LIBRARY_PATH", b"PYTHONPATH"): + #prevent those paths from accumulating the same values multiple times, + #only keep the first one: + pathsep = os.pathsep.encode() + pval = value.split(pathsep) #ie: ["/usr/bin", "/usr/local/bin", "/usr/bin"] + seen = set() + value = pathsep.join(x for x in pval if not (x in seen or seen.add(x))) + script.append(b"%s=%s:\"$%s\"; export %s\n" + % (var, sh_quotemeta(value), var, var)) + else: + script.append(b"%s=%s; export %s\n" + % (var, sh_quotemeta(value), var)) + #XPRA_SOCKET_DIR is a special case, we want to honour it + #when it is specified, but the client may override it: + if socket_dir: + script.append(b'if [ -z "${XPRA_SOCKET_DIR}" ]; then\n') + script.append(b' XPRA_SOCKET_DIR=%s; export XPRA_SOCKET_DIR\n' % sh_quotemeta(os.path.expanduser(socket_dir).encode())) + script.append(b'fi\n') + # We ignore failures in cd'ing, b/c it's entirely possible that we were + # started from some temporary directory and all paths are absolute. + script.append(b"cd %s\n" % sh_quotemeta(starting_dir.encode())) + if OSX: + #OSX contortions: + #The executable is the python interpreter, + #which is execed by a shell script, which we have to find.. + sexec = sys.executable + bini = sexec.rfind("Resources/bin/") + if bini>0: + sexec = os.path.join(sexec[:bini], "Resources", "MacOS", "Xpra") + script.append(b"_XPRA_SCRIPT=%s\n" % (sh_quotemeta(sexec.encode()),)) + script.append(b""" + if which "$_XPRA_SCRIPT" > /dev/null; then + # Happypath: + exec "$_XPRA_SCRIPT" "$@" + else + # Hope for the best: + exec Xpra "$@" + fi + """) + else: + script.append(b"_XPRA_PYTHON=%s\n" % (sh_quotemeta(sys.executable.encode()),)) + script.append(b"_XPRA_SCRIPT=%s\n" % (sh_quotemeta(xpra_file.encode()),)) + script.append(b""" + if which "$_XPRA_PYTHON" > /dev/null && [ -e "$_XPRA_SCRIPT" ]; then + # Happypath: + exec "$_XPRA_PYTHON" "$_XPRA_SCRIPT" "$@" + else + cat >&2 <<END + Could not find one or both of '$_XPRA_PYTHON' and '$_XPRA_SCRIPT' + Perhaps your environment has changed since the xpra server was started? + I'll just try executing 'xpra' with current PATH, and hope... + END + exec xpra "$@" + fi + """) + return b"".join(script) -def xpra_runner_shell_script(xpra_file, starting_dir, socket_dir): - script = [] - script.append("#!/bin/sh\n") - for var, value in os.environ.items(): - # these aren't used by xpra, and some should not be exposed - # as they are either irrelevant or simply do not match - # the new environment used by xpra - # TODO: use a whitelist - if var in ["XDG_SESSION_COOKIE", "LS_COLORS", "DISPLAY"]: - continue - #XPRA_SOCKET_DIR is a special case, it is handled below - if var=="XPRA_SOCKET_DIR": - continue - if var=="XPRA_ALT_PYTHON_RETRY": - #the environment might have changed, - #and we may need to retry with a different interpreter - #different from the one that created this script - continue - if var.startswith("BASH_FUNC"): - #some versions of bash will apparently generate functions - #that cannot be reloaded using this script - continue - # :-separated envvars that people might change while their server is - # going: - if var in ("PATH", "LD_LIBRARY_PATH", "PYTHONPATH"): - #prevent those paths from accumulating the same values multiple times, - #only keep the first one: - pval = value.split(os.pathsep) #ie: ["/usr/bin", "/usr/local/bin", "/usr/bin"] - seen = set() - value = os.pathsep.join(x for x in pval if not (x in seen or seen.add(x))) - script.append("%s=%s:\"$%s\"; export %s\n" - % (var, sh_quotemeta(value), var, var)) +else: + #PYTHON2: + + def sh_quotemeta(s): + return "'" + s.replace("'", "'\\''") + "'" + + def xpra_runner_shell_script(xpra_file, starting_dir, socket_dir): + script = [] + script.append("#!/bin/sh\n") + for var, value in os.environ.items(): + # these aren't used by xpra, and some should not be exposed + # as they are either irrelevant or simply do not match + # the new environment used by xpra + # TODO: use a whitelist + if var in ["XDG_SESSION_COOKIE", "LS_COLORS", "DISPLAY"]: + continue + #XPRA_SOCKET_DIR is a special case, it is handled below + if var=="XPRA_SOCKET_DIR": + continue + if var=="XPRA_ALT_PYTHON_RETRY": + #the environment might have changed, + #and we may need to retry with a different interpreter + #different from the one that created this script + continue + if var.startswith("BASH_FUNC"): + #some versions of bash will apparently generate functions + #that cannot be reloaded using this script + continue + # :-separated envvars that people might change while their server is + # going: + if var in ("PATH", "LD_LIBRARY_PATH", "PYTHONPATH"): + #prevent those paths from accumulating the same values multiple times, + #only keep the first one: + pval = value.split(os.pathsep) #ie: ["/usr/bin", "/usr/local/bin", "/usr/bin"] + seen = set() + value = os.pathsep.join(x for x in pval if not (x in seen or seen.add(x))) + script.append("%s=%s:\"$%s\"; export %s\n" + % (var, sh_quotemeta(value), var, var)) + else: + script.append("%s=%s; export %s\n" + % (var, sh_quotemeta(value), var)) + #XPRA_SOCKET_DIR is a special case, we want to honour it + #when it is specified, but the client may override it: + if socket_dir: + script.append('if [ -z "${XPRA_SOCKET_DIR}" ]; then\n') + script.append(' XPRA_SOCKET_DIR=%s; export XPRA_SOCKET_DIR\n' % sh_quotemeta(os.path.expanduser(socket_dir))) + script.append('fi\n') + # We ignore failures in cd'ing, b/c it's entirely possible that we were + # started from some temporary directory and all paths are absolute. + script.append("cd %s\n" % sh_quotemeta(starting_dir)) + if OSX: + #OSX contortions: + #The executable is the python interpreter, + #which is execed by a shell script, which we have to find.. + sexec = sys.executable + bini = sexec.rfind("Resources/bin/") + if bini>0: + sexec = os.path.join(sexec[:bini], "Resources", "MacOS", "Xpra") + script.append("_XPRA_SCRIPT=%s\n" % (sh_quotemeta(sexec),)) + script.append(""" + if which "$_XPRA_SCRIPT" > /dev/null; then + # Happypath: + exec "$_XPRA_SCRIPT" "$@" + else + # Hope for the best: + exec Xpra "$@" + fi + """) else: - script.append("%s=%s; export %s\n" - % (var, sh_quotemeta(value), var)) - #XPRA_SOCKET_DIR is a special case, we want to honour it - #when it is specified, but the client may override it: - if socket_dir: - script.append('if [ -z "${XPRA_SOCKET_DIR}" ]; then\n') - script.append(' XPRA_SOCKET_DIR=%s; export XPRA_SOCKET_DIR\n' % sh_quotemeta(os.path.expanduser(socket_dir))) - script.append('fi\n') - # We ignore failures in cd'ing, b/c it's entirely possible that we were - # started from some temporary directory and all paths are absolute. - script.append("cd %s\n" % sh_quotemeta(starting_dir)) - if OSX: - #OSX contortions: - #The executable is the python interpreter, - #which is execed by a shell script, which we have to find.. - sexec = sys.executable - bini = sexec.rfind("Resources/bin/") - if bini>0: - sexec = os.path.join(sexec[:bini], "Resources", "MacOS", "Xpra") - script.append("_XPRA_SCRIPT=%s\n" % (sh_quotemeta(sexec),)) - script.append(""" -if which "$_XPRA_SCRIPT" > /dev/null; then - # Happypath: - exec "$_XPRA_SCRIPT" "$@" -else - # Hope for the best: - exec Xpra "$@" -fi -""") - else: - script.append("_XPRA_PYTHON=%s\n" % (sh_quotemeta(sys.executable),)) - script.append("_XPRA_SCRIPT=%s\n" % (sh_quotemeta(xpra_file),)) - script.append(""" -if which "$_XPRA_PYTHON" > /dev/null && [ -e "$_XPRA_SCRIPT" ]; then - # Happypath: - exec "$_XPRA_PYTHON" "$_XPRA_SCRIPT" "$@" -else - cat >&2 <<END - Could not find one or both of '$_XPRA_PYTHON' and '$_XPRA_SCRIPT' - Perhaps your environment has changed since the xpra server was started? - I'll just try executing 'xpra' with current PATH, and hope... -END - exec xpra "$@" -fi -""") - return "".join(script) + script.append("_XPRA_PYTHON=%s\n" % (sh_quotemeta(sys.executable),)) + script.append("_XPRA_SCRIPT=%s\n" % (sh_quotemeta(xpra_file),)) + script.append(""" + if which "$_XPRA_PYTHON" > /dev/null && [ -e "$_XPRA_SCRIPT" ]; then + # Happypath: + exec "$_XPRA_PYTHON" "$_XPRA_SCRIPT" "$@" + else + cat >&2 <<END + Could not find one or both of '$_XPRA_PYTHON' and '$_XPRA_SCRIPT' + Perhaps your environment has changed since the xpra server was started? + I'll just try executing 'xpra' with current PATH, and hope... + END + exec xpra "$@" + fi + """) + return "".join(script).encode() def write_runner_shell_scripts(contents, overwrite=True): # This used to be given a display-specific name, but now we give it a @@ -127,7 +215,7 @@ with umask_context(0o022): h = os.open(scriptpath, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0o700) try: - os.write(h, contents.encode()) + os.write(h, contents) finally: os.close(h) except Exception as e: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/server/source/fileprint_mixin.py new/xpra-3.0.8/xpra/server/source/fileprint_mixin.py --- old/xpra-3.0.7/xpra/server/source/fileprint_mixin.py 2019-09-24 15:54:01.000000000 +0200 +++ new/xpra-3.0.8/xpra/server/source/fileprint_mixin.py 2020-03-30 18:52:51.000000000 +0200 @@ -137,7 +137,10 @@ #ie: on FOO (via xpra) location = "on %s (%s)" % (self.hostname, PRINTER_LOCATION_STRING) try: - printer = name.decode("utf8") + try: + printer = name.decode("utf8") + except UnicodeDecodeError: + printer = name.decode("latin1") def printer_added(): #once the printer has been added, register it in the list #(so it will be removed on exit) @@ -164,11 +167,14 @@ log("not removing printer '%s' - since we didn't add it", name) else: try: - printer = name.decode("utf8") + try: + printer = name.decode("utf8") + except UnicodeDecodeError: + printer = name.decode("latin1") from xpra.platform.pycups_printing import remove_printer remove_printer(printer) log.info("removed remote printer '%s'", printer) except Exception as e: log("remove_printer(%s)", printer, exc_info=True) - log.error("Error: failed to remove printer %s:", printer) + log.error("Error: failed to remove printer '%s':", name) log.error(" %s", e) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/server/window/window_video_source.py new/xpra-3.0.8/xpra/server/window/window_video_source.py --- old/xpra-3.0.7/xpra/server/window/window_video_source.py 2019-10-28 16:04:05.000000000 +0100 +++ new/xpra-3.0.8/xpra/server/window/window_video_source.py 2020-03-21 14:16:56.000000000 +0100 @@ -455,7 +455,7 @@ if current_encoding!="auto" and current_encoding not in self.common_video_encodings: return nonvideo(info="%s not a supported video encoding" % current_encoding) - if cww*cwh<=MAX_NONVIDEO_PIXELS: + if cww*cwh<=MAX_NONVIDEO_PIXELS or cww<16 or cwh<16: return nonvideo(quality+30, "window is too small") if cww<self.min_w or cww>self.max_w or cwh<self.min_h or cwh>self.max_h: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/src_info.py new/xpra-3.0.8/xpra/src_info.py --- old/xpra-3.0.7/xpra/src_info.py 2020-03-13 18:03:50.000000000 +0100 +++ new/xpra-3.0.8/xpra/src_info.py 2020-03-31 13:40:32.000000000 +0200 @@ -1,2 +1,2 @@ LOCAL_MODIFICATIONS=0 -REVISION=25629 +REVISION=25881 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/x11/bindings/keyboard_bindings.pyx new/xpra-3.0.8/xpra/x11/bindings/keyboard_bindings.pyx --- old/xpra-3.0.7/xpra/x11/bindings/keyboard_bindings.pyx 2020-01-08 12:52:07.000000000 +0100 +++ new/xpra-3.0.8/xpra/x11/bindings/keyboard_bindings.pyx 2020-03-21 14:16:56.000000000 +0100 @@ -860,13 +860,19 @@ cdef KeySym keysym keycodes = self._get_keycodes_down() keys = {} + def get_keysyms(keycode): + keysyms = [] + for group in (0, 1): + for level in (0, 1): + keysym = XkbKeycodeToKeysym(self.display, keycode, 0, 0) + if keysym==NoSymbol: + continue + key = XKeysymToString(keysym) + if key!=NULL: + keysyms.append(bytestostr(key)) + return keysyms for keycode in keycodes: - keysym = XkbKeycodeToKeysym(self.display, keycode, 0, 0) - if keysym==NoSymbol: - continue - key = XKeysymToString(keysym) - if key!=NULL: - keys[keycode] = bytestostr(key) + keys[keycode] = get_keysyms(keycode) return keys def unpress_all_keys(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/x11/gtk_x11/clipboard.py new/xpra-3.0.8/xpra/x11/gtk_x11/clipboard.py --- old/xpra-3.0.7/xpra/x11/gtk_x11/clipboard.py 2020-03-08 03:21:09.000000000 +0100 +++ new/xpra-3.0.8/xpra/x11/gtk_x11/clipboard.py 2020-03-30 18:52:51.000000000 +0200 @@ -64,8 +64,15 @@ dst_targets = parts[1].split(",") trans[src_target] = dst_targets return trans -TRANSLATED_TARGETS = parse_translated_targets(os.environ.get("XPRA_CLIPBOARD_TRANSLATED_TARGETS", - "text/plain;charset=utf-8:UTF8_STRING,text/plain")) +DEFAULT_TRANSLATED_TARGETS = "#".join(( + "text/plain;charset=utf-8:UTF8_STRING,text/plain,public.utf8-plain-text", + "TEXT:text/plain,text/plain;charset=utf-8,UTF8_STRING,public.utf8-plain-text", + "STRING:text/plain,text/plain;charset=utf-8,UTF8_STRING,public.utf8-plain-text", + "UTF8_STRING:text/plain;charset=utf-8,text/plain,public.utf8-plain-text", + "GTK_TEXT_BUFFER_CONTENTS:UTF8_STRING,text/plain", + )) +TRANSLATED_TARGETS = parse_translated_targets(os.environ.get("XPRA_CLIPBOARD_TRANSLATED_TARGETS", DEFAULT_TRANSLATED_TARGETS)) +log("TRANSLATED_TARGETS=%s", TRANSLATED_TARGETS) def xatoms_to_strings(data): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/x11/gtk_x11/wm_check.py new/xpra-3.0.8/xpra/x11/gtk_x11/wm_check.py --- old/xpra-3.0.7/xpra/x11/gtk_x11/wm_check.py 2019-09-24 15:54:02.000000000 +0200 +++ new/xpra-3.0.8/xpra/x11/gtk_x11/wm_check.py 2020-03-21 14:16:56.000000000 +0100 @@ -1,6 +1,6 @@ # This file is part of Xpra. # Copyright (C) 2008, 2009 Nathaniel Smith <n...@pobox.com> -# Copyright (C) 2012-2019 Antoine Martin <anto...@xpra.org> +# Copyright (C) 2012-2020 Antoine Martin <anto...@xpra.org> # Xpra is released under the terms of the GNU GPL v2, or, at your option, any # later version. See the file COPYING for details. @@ -18,6 +18,7 @@ FORCE_REPLACE_WM = envbool("XPRA_FORCE_REPLACE_WM", False) def wm_check(wm_name, upgrading=False): + found_name = False with xsync: display = display_get_default() #there should only be one screen... but let's check all of them @@ -42,6 +43,10 @@ name = prop_get(ewmh_wm, "_NET_WM_NAME", "utf8", ignore_errors=True, raise_xerrors=False) if upgrading and name and name==wm_name: log.info("found previous Xpra instance") + found_name = True + elif not name: + log.warn("Warning: no window manager found") + log.warn(" on screen %s using window %#x", i, ewmh_wm.get_xid()) else: log.warn("Warning: found an existing window manager") log.warn(" on screen %s using window %#x: %s", i, get_xwindow(ewmh_wm), name or "unknown") @@ -57,4 +62,7 @@ log.warn(" you may set XPRA_FORCE_REPLACE_WM=1 to force xpra to continue") log.warn(" at your own risk") return False + if upgrading and not found_name: + log.error("Error: xpra server not found") + return False return True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/x11/server.py new/xpra-3.0.8/xpra/x11/server.py --- old/xpra-3.0.7/xpra/x11/server.py 2020-02-10 16:32:04.000000000 +0100 +++ new/xpra-3.0.8/xpra/x11/server.py 2020-03-24 06:22:26.000000000 +0100 @@ -233,7 +233,7 @@ return False #check for an existing window manager: from xpra.x11.gtk_x11.wm_check import wm_check - if not wm_check(self.wm_name, self.clobber): + if not wm_check(self.wm_name, self.clobber & 0x1): return False return True @@ -865,7 +865,7 @@ frame = nws.intlistget("frame", (0, 0, 0, 0)) window.set_property("frame", frame) #boolean: but not a wm_state and renamed in the model... (iconic vs inconified!) - iconified = nws.boolget("iconified") + iconified = nws.boolget("iconified", None) if iconified is not None: if window.is_OR(): log("ignoring iconified=%s on OR window %s", iconified, window) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/x11/server_keyboard_config.py new/xpra-3.0.8/xpra/x11/server_keyboard_config.py --- old/xpra-3.0.7/xpra/x11/server_keyboard_config.py 2020-03-13 17:51:19.000000000 +0100 +++ new/xpra-3.0.8/xpra/x11/server_keyboard_config.py 2020-03-24 06:22:26.000000000 +0100 @@ -448,11 +448,11 @@ def do_get_keycode(self, client_keycode, keyname, pressed, modifiers, group): if not self.enabled: log("ignoring keycode since keyboard is turned off") - return -1 + return -1, group if keyname=="0xffffff": - return -1 + return -1, group if self.xkbmap_raw: - return client_keycode + return client_keycode, group keycode = None if self.xkbmap_query: keycode = self.keycode_translation.get((client_keycode, keyname)) or client_keycode @@ -488,7 +488,7 @@ if keycode is None: keycode = self.keycode_translation.get(keyname, -1) log("get_keycode(%s, %s)=%i (keyname translation)", client_keycode, keyname, keycode) - return keycode + return keycode, group def do_get_keycode_new(self, client_keycode, keyname, pressed, modifiers, group): #non-native: try harder to find matching keysym @@ -511,21 +511,16 @@ break level = int(shift) + int(mode)*2 levels = [] - #first, match with group if set: - if group: - levels.append(level+4) - #then try exact match without group: - levels.append(level) - #try default group: - for i in range(2): - level = int(shift) + i*2 - if level not in levels: - levels.append(level) - #catch all: - for level in range(8): - if level not in levels: - levels.append(level) + #try to preserve the mode (harder to toggle): + for m in (int(bool(mode)), int(not mode)): + #try to preserve shift state: + for s in (int(bool(shift)), int(not shift)): + #group is comparatively easier to toggle (one function call): + for g in (int(bool(group)), int(not group)): + level = int(g)*4 + int(m)*2 + int(s)*1 + levels.append(level) kmlog("will try levels: %s", levels) + rgroup = group for level in levels: keycode = self.keycode_translation.get((keyname, level)) if keycode: @@ -555,19 +550,22 @@ if (level & 1) ^ shift: #shift state does not match toggle_modifier("shift") - if (level & 2) ^ mode: + if int(bool(level & 2)) ^ mode: #try to set / unset mode: for mod, keynames in self.keynames_for_mod.items(): if "ISO_Level3_Shift" in keynames or "Mode_switch" in keynames: #found mode switch modified toggle_modifier(mod) break + rgroup = level//4 + if rgroup!=group: + kmlog("switching group from %i to %i", group, rgroup) break - #this should not find anything new?: - if keycode is None: - keycode = self.keycode_translation.get(keyname, -1) - klog("=%i (keyname translation)", keycode) - return keycode + #this should not find anything new?: + if keycode is None: + keycode = self.keycode_translation.get(keyname, -1) + klog("=%i (keyname translation)", keycode) + return keycode, rgroup def get_current_mask(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/x11/shadow_x11_server.py new/xpra-3.0.8/xpra/x11/shadow_x11_server.py --- old/xpra-3.0.7/xpra/x11/shadow_x11_server.py 2019-09-24 15:54:02.000000000 +0200 +++ new/xpra-3.0.8/xpra/x11/shadow_x11_server.py 2020-03-30 18:52:51.000000000 +0200 @@ -6,13 +6,14 @@ # later version. See the file COPYING for details. from xpra.x11.x11_server_core import X11ServerCore -from xpra.os_util import monotonic_time, is_Wayland +from xpra.os_util import monotonic_time, _is_Wayland from xpra.util import envbool, envint from xpra.gtk_common.gtk_util import get_xwindow, is_gtk3 from xpra.server.shadow.gtk_shadow_server_base import GTKShadowServerBase from xpra.server.shadow.gtk_root_window_model import GTKImageCapture from xpra.x11.bindings.ximage import XImageBindings #@UnresolvedImport from xpra.gtk_common.error import xsync, xlog +from xpra.scripts.main import saved_env from xpra.log import Logger log = Logger("x11", "shadow") @@ -39,7 +40,7 @@ self.xshm = None self.xwindow = xwindow assert USE_XSHM and XImage.has_XShm(), "no XShm support" - if is_Wayland(): + if _is_Wayland(saved_env): log.warn("Warning: shadow servers do not support Wayland") log.warn(" switch to X11") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xpra-3.0.7/xpra/x11/x11_server_core.py new/xpra-3.0.8/xpra/x11/x11_server_core.py --- old/xpra-3.0.7/xpra/x11/x11_server_core.py 2020-03-08 03:21:10.000000000 +0100 +++ new/xpra-3.0.8/xpra/x11/x11_server_core.py 2020-03-30 18:52:51.000000000 +0200 @@ -53,6 +53,7 @@ ALWAYS_NOTIFY_MOTION = envbool("XPRA_ALWAYS_NOTIFY_MOTION", False) +FAKE_X11_INIT_ERROR = envbool("XPRA_FAKE_X11_INIT_ERROR", False) class XTestPointerDevice(object): @@ -120,6 +121,8 @@ def x11_init(self): + if FAKE_X11_INIT_ERROR: + raise Exception("fake x11 init error") clean_keyboard_state() if self.fake_xinerama in FALSE_OPTIONS: self.libfakeXinerama_so = None @@ -395,7 +398,8 @@ with xlog: info.setdefault("keyboard", {}).update({ "state" : { - "keys_pressed" : tuple(self.keys_pressed.keys()) + "keys_pressed" : tuple(self.keys_pressed.keys()), + "keycodes-down" : X11Keyboard.get_keycodes_down(), }, "fast-switching" : True, "layout-group" : X11Keyboard.get_layout_group(),