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(),



Reply via email to