Hello community,

here is the log from the commit of package xpra for openSUSE:Factory checked in 
at 2020-06-05 20:21:51
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/xpra (Old)
 and      /work/SRC/openSUSE:Factory/.xpra.new.3606 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "xpra"

Fri Jun  5 20:21:51 2020 rev:21 rq:811775 version:4.0.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/xpra/xpra.changes        2020-05-17 
23:43:36.217110119 +0200
+++ /work/SRC/openSUSE:Factory/.xpra.new.3606/xpra.changes      2020-06-05 
20:28:37.129243311 +0200
@@ -1,0 +2,41 @@
+Fri Jun  5 11:25:10 UTC 2020 - Luigi Baldoni <[email protected]>
+
+- Update to version 4.0.2
+  * fix encryption not honoured with TCP sockets upgraded to
+    WebSocket
+  * fix xpra top client refresh rate via timer
+  * fix opengl client info format shown in 'xpra top'
+  * fix format of attributes given to glXChooseVisual
+  * fix crashes in OpenGL context setup on X11
+  * fix race condition in window statistics
+  * fix server errors when non-interactive clients are connected
+  * fix tray toolbox app
+  * fix X11 server key symbol lookup via Xkb
+  * html5 fixes:
+    + fix missing desktop background
+    + client errors painting rgb32 data with a padded rowstride
+    + clipboard wrongly clearing data on failures
+    + compatibility issues with Internet Explorer
+    + missing transparency for windows in focus
+    + no windows focused after close
+    + connection errors caused by spurious packets
+    + error in invalid packet handler
+    + AES encrypted connections
+    + connection errors with AES and lz4 (disable lz4 for now)
+    + packet error with very small paint packets
+    + 'insecure passwords' option shown in the wrong cases
+    + handle window iconification messages
+    + update version in about page
+  * fix connection errors with 'None' values in bencoder (ie:
+    html5)
+  * fix connection errors with websocket connections and AES
+    encryption
+  * fix duplicate clipboard token sent with MS Windows servers
+  * fix window initialization errors causing server startup
+    failures
+  * remove invalid extra strings from mdns service name
+  * workaround bugs in pyxdg / menu configuration
+  * add 'terminator' to the 'text' application hint
+  * more explicit error message when trying to use python2
+
+-------------------------------------------------------------------

Old:
----
  xpra-4.0.1.tar.xz

New:
----
  xpra-4.0.2.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ xpra.spec ++++++
--- /var/tmp/diff_new_pack.lyA4q2/_old  2020-06-05 20:28:37.973245804 +0200
+++ /var/tmp/diff_new_pack.lyA4q2/_new  2020-06-05 20:28:37.973245804 +0200
@@ -19,7 +19,7 @@
 
 %global __requires_exclude 
^typelib\\(GtkosxApplication\\)|typelib\\(GdkGLExt\\)|typelib\\(GtkGLExt\\).*$
 Name:           xpra
-Version:        4.0.1
+Version:        4.0.2
 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-4.0.1.tar.xz -> xpra-4.0.2.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/NEWS new/xpra-4.0.2/NEWS
--- old/xpra-4.0.1/NEWS 2020-05-17 18:12:15.000000000 +0200
+++ new/xpra-4.0.2/NEWS 2020-06-05 11:40:08.000000000 +0200
@@ -1,3 +1,43 @@
+v4.0.2 (2020-06-04)
+======================
+       -- fix encryption not honoured with TCP sockets upgraded to WebSocket
+       -- fix xpra top client refresh rate via timer
+       -- fix opengl client info format shown in 'xpra top'
+       -- fix format of attributes given to glXChooseVisual
+       -- fix crashes in OpenGL context setup on X11
+       -- fix race condition in window statistics
+       -- fix server errors when non-interactive clients are connected
+       -- fix tray toolbox app
+       -- fix X11 server key symbol lookup via Xkb
+       -- fix python-lz4 packaging on MS Windows
+       -- html5 fixes:
+               missing 'AltGr' on MacOS
+               fix missing desktop background
+               client errors painting rgb32 data with a padded rowstride
+               clipboard wrongly clearing data on failures
+               compatibility issues with Internet Explorer
+               missing transparency for windows in focus
+               no windows focused after close
+               connection errors caused by spurious packets
+               error in invalid packet handler
+               AES encrypted connections
+               connection errors with AES and lz4 (disable lz4 for now)
+               packet error with very small paint packets
+               'insecure passwords' option shown in the wrong cases
+               handle window iconification messages
+               update version in about page
+       -- fix connection errors with 'None' values in bencoder (ie: html5)
+       -- fix connection errors with websocket connections and AES encryption
+       -- fix duplicate clipboard token sent with MS Windows servers
+       -- fix window initialization errors causing server startup failures
+       -- remove invalid extra strings from mdns service name
+       -- change DLL search order to prevent conflict (MS Windows)
+       -- workaround bugs in pyxdg / menu configuration
+       -- add 'terminator' to the 'text' application hint
+       -- don't recommend installing Apple's 'bonjour' on MS Windows (not 
needed)
+       -- more explicit error message when trying to use python2
+
+
 v4.0 (2020-05-17)
 ======================
        -- fix missing content-type for some windows
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/content-type/50_class.conf 
new/xpra-4.0.2/content-type/50_class.conf
--- old/xpra-4.0.1/content-type/50_class.conf   2020-05-10 19:00:48.000000000 
+0200
+++ new/xpra-4.0.2/content-type/50_class.conf   2020-06-05 11:40:08.000000000 
+0200
@@ -104,6 +104,7 @@
 class-instance:simple-scan=picture
 class-instance:Steam=browser
 class-instance:system-config-printer=text
+class-instance:terminator=text
 class-instance:tkdiff=text
 class-instance:Thunderbird=browser
 class-instance:totem=video
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/cups/xpraforwarder 
new/xpra-4.0.2/cups/xpraforwarder
--- old/xpra-4.0.1/cups/xpraforwarder   2020-05-17 18:12:15.000000000 +0200
+++ new/xpra-4.0.2/cups/xpraforwarder   2020-06-05 11:40:08.000000000 +0200
@@ -38,7 +38,7 @@
 from urllib.parse import urlparse, parse_qs
 
 
-__version__ = "4.0.1"
+__version__ = "4.0.2"
 
 
 #Writes a syslog entry (msg) at the default facility:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/html5/connect.html 
new/xpra-4.0.2/html5/connect.html
--- old/xpra-4.0.1/html5/connect.html   2020-05-10 19:00:48.000000000 +0200
+++ new/xpra-4.0.2/html5/connect.html   2020-06-05 11:40:09.000000000 +0200
@@ -387,7 +387,7 @@
        const ssl_input = document.getElementById("ssl");
        const insecure_input = document.getElementById("insecure");
        const has_session_storage = Utilities.hasSessionStorage();
-       if (has_session_storage) {
+       if (!has_session_storage) {
                //passwords would be sent on URL,
                //so show insecure checkbox whenever ssl is off:
                ssl_input.onchange = function() {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/html5/css/client.css 
new/xpra-4.0.2/html5/css/client.css
--- old/xpra-4.0.1/html5/css/client.css 2020-05-10 19:00:48.000000000 +0200
+++ new/xpra-4.0.2/html5/css/client.css 2020-06-05 11:40:09.000000000 +0200
@@ -147,8 +147,11 @@
 }
 div.windowinfocus {
        z-index: 5000;
-       background-color: white;
 }
+div.windowinfocus div.windowhead {
+       background-color: #A0A0A0;
+}
+
 .modal {
        z-index: 20000;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/html5/index.html 
new/xpra-4.0.2/html5/index.html
--- old/xpra-4.0.1/html5/index.html     2020-05-10 19:00:48.000000000 +0200
+++ new/xpra-4.0.2/html5/index.html     2020-06-05 11:40:10.000000000 +0200
@@ -129,9 +129,9 @@
 
                <div id="about">
                        <h2>Xpra HTML5 Client</h2>
-                       <h3>Version 3.0</h3>
+                       <h3>Version 4.0.2</h3>
                        <span>
-                               Copyright (c) 2013-2019 Antoine Martin 
&lt;[email protected]&gt;
+                               Copyright (c) 2013-2020 Antoine Martin 
&lt;[email protected]&gt;
                                <br />
                                Copyright (c) 2014 Joshua Higgins 
&lt;[email protected]&gt;
                        </span>
@@ -432,7 +432,7 @@
                                const port = getparam("port") || 
window.location.port;
                                const ssl = getboolparam("ssl", https);
                                const path = getparam("path") || 
window.location.pathname;
-                               const encryption = getboolparam("encryption", 
false);
+                               const encryption = getparam("encryption") || 
null;
                                const key = getparam("key") || null;
                                const keyboard_layout = 
getparam("keyboard_layout") || null;
                                const start = getparam("start");
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/html5/js/Client.js 
new/xpra-4.0.2/html5/js/Client.js
--- old/xpra-4.0.1/html5/js/Client.js   2020-05-17 18:12:15.000000000 +0200
+++ new/xpra-4.0.2/html5/js/Client.js   2020-06-05 11:40:10.000000000 +0200
@@ -514,8 +514,8 @@
        ctx.debug("network", "received a", packet_type, "packet");
        const fn = ctx.packet_handlers[packet_type];
        if (fn==undefined) {
-               this.cerror("no packet handler for ", packet_type);
-               this.clog(packet);
+               ctx.cerror("no packet handler for ", packet_type);
+               ctx.clog(packet);
        } else {
                fn(packet, ctx);
        }
@@ -523,6 +523,9 @@
 
 XpraClient.prototype._screen_resized = function(event, ctx) {
        // send the desktop_size packet so server knows we changed size
+       if (!this.connected) {
+               return;
+       }
        if (this.container.clientWidth==this.desktop_width && 
this.container.clientHeight==this.desktop_height) {
                return;
        }
@@ -745,7 +748,7 @@
                keyname = keyname.replace("_L", "_R");
 
        //AltGr: keep track of pressed state
-       if (str=="AltGraph" || (keyname=="Alt_R" && Utilities.isWindows())) {
+       if (str=="AltGraph" || (keyname=="Alt_R" && (Utilities.isWindows() || 
Utilities.isMacOS()))) {
                this.altgr_state = pressed;
                keyname = "ISO_Level3_Shift";
                str = "AltGraph";
@@ -1089,13 +1092,15 @@
                        "connection-data"       : ci,
                });
        }
-       const LZ4 = require('lz4');
-       if(LZ4) {
-               this._update_capabilities({
-                       "lz4"                                           : true,
-                       "lz4.js.version"                        : LZ4.version,
-                       "encoding.rgb_lz4"                      : true,
-               });
+       if (!this.encryption) {
+               const LZ4 = require('lz4');
+               if(LZ4) {
+                       this._update_capabilities({
+                               "lz4"                                           
: true,
+                               "lz4.js.version"                        : 
LZ4.version,
+                               "encoding.rgb_lz4"                      : true,
+                       });
+               }
        }
 
        if(typeof BrotliDecode != "undefined" && !Utilities.isIE()) {
@@ -1147,7 +1152,7 @@
                "server-window-resize"          : true,
                "screen-resize-bigger"          : false,
                "metadata.supported"            : [
-                                                                               
"fullscreen", "maximized", "above", "below",
+                                                                               
"fullscreen", "maximized", "iconic", "above", "below",
                                                                                
//"set-initial-position", "group-leader",
                                                                                
"title", "size-hints", "class-instance", "transient-for", "window-type", 
"has-alpha",
                                                                                
"decorations", "override-redirect", "tray", "modal", "opacity",
@@ -1474,7 +1479,11 @@
        if (Utilities.isIE()) {
                datatype = "Text";
        }
-       const clipboard_buffer = 
unescape(encodeURIComponent(clipboardData.getData(datatype)));
+       const raw_clipboard_buffer = clipboardData.getData(datatype);
+       if (raw_clipboard_buffer===null) {
+               return false;
+       }
+       const clipboard_buffer = 
unescape(encodeURIComponent(raw_clipboard_buffer));
        this.debug("clipboard", "paste event, data=", clipboard_buffer);
        if (clipboard_buffer==this.clipboard_buffer) {
                return false;
@@ -1498,6 +1507,10 @@
        if (win.override_redirect || win.tray) {
                return;
        }
+       if (win.minimized) {
+               //tell server to map it:
+               win.toggle_minimized();
+       }
        const client = win.client;
        const wid = win.wid;
        if (client.focus == wid) {
@@ -2024,7 +2037,7 @@
 
 
 XpraClient.prototype._send_ping = function() {
-       if (this.reconnect_in_progress) {
+       if (this.reconnect_in_progress || !this.connected) {
                return;
        }
        const me = this;
@@ -2381,6 +2394,21 @@
        if (Object.keys(ctx.id_to_window).length==0) {
                ctx.on_last_window();
        }
+       else if (win && win.focused) {
+               //it had focus, find the next highest:
+               let highest_window = null;
+               let highest_stacking = -1;
+               for (const i in client.id_to_window) {
+                       let iwin = client.id_to_window[i];
+                       if (iwin.stacking_layer>highest_stacking && !iwin.tray) 
{
+                               highest_window = iwin;
+                               highest_stacking = iwin.stacking_layer;
+                       }
+               }
+               if (highest_window) {
+                       ctx._window_set_focus(highest_window);
+               }
+       }
 };
 
 XpraClient.prototype._process_raise_window = function(packet, ctx) {
@@ -3194,7 +3222,7 @@
 };
 
 XpraClient.prototype.send_clipboard_token = function(data) {
-       if (!this.clipboard_enabled) {
+       if (!this.clipboard_enabled || !this.connected) {
                return;
        }
        this.debug("clipboard", "sending clipboard token with data:", data);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/html5/js/Protocol.js 
new/xpra-4.0.2/html5/js/Protocol.js
--- old/xpra-4.0.1/html5/js/Protocol.js 2020-05-10 19:00:49.000000000 +0200
+++ new/xpra-4.0.2/html5/js/Protocol.js 2020-06-05 11:40:10.000000000 +0200
@@ -48,11 +48,11 @@
                                }
                                break;
                        case 'l':
-                               console.log(data.t);
+                               this.log(data.t);
                                break;
                default:
-                       console.error("got unknown command from worker");
-                       console.error(e.data);
+                       this.error("got unknown command from worker");
+                       this.error(e.data);
                }
        }, false);
 };
@@ -153,7 +153,7 @@
 };
 
 XpraProtocol.prototype.protocol_error = function(msg) {
-       console.error("protocol error:", msg);
+       this.error("protocol error:", msg);
        //make sure we stop processing packets and events:
        this.websocket.onopen = null;
        this.websocket.onclose = null;
@@ -170,6 +170,18 @@
        }
 };
 
+
+XpraProtocol.prototype.error = function() {
+       if (console) {
+               console.error.apply(console, arguments);
+       }
+}
+XpraProtocol.prototype.log  = function() {
+       if (console) {
+               console.log.apply(console, arguments);
+       }
+}
+
 XpraProtocol.prototype.do_process_receive_queue = function() {
        let i = 0, j = 0;
        if (this.header.length<8 && this.rQ.length>0) {
@@ -301,9 +313,8 @@
                        // lz4
                        // python-lz4 inserts the length of the uncompressed 
data as an int
                        // at the start of the stream
-                       const d = packet_data.subarray(0, 4);
                        // output buffer length is stored as little endian
-                       const length = d[0] | (d[1] << 8) | (d[2] << 16) | 
(d[3] << 24);
+                       const length = packet_data[0] | (packet_data[1] << 8) | 
(packet_data[2] << 16) | (packet_data[3] << 24);
                        // decode the LZ4 block
                        // console.log("lz4 decompress packet size", 
packet_size, ", lz4 length=", length);
                        inflated = new Uint8Array(length);
@@ -339,8 +350,8 @@
                }
                catch (e) {
                        //FIXME: maybe we should error out and disconnect here?
-                       console.error("error decoding packet " + e);
-                       //console.error("packet_data="+packet_data);
+                       this.error("error decoding packet " + e);
+                       //this.error("packet_data="+packet_data);
                        return this.rQ.length>0;
                }
                try {
@@ -367,8 +378,8 @@
                }
                catch (e) {
                        //FIXME: maybe we should error out and disconnect here?
-                       console.error("error processing packet " + packet[0]+": 
" + e);
-                       //console.error("packet_data="+packet_data);
+                       this.error("error processing packet " + packet[0]+": " 
+ e);
+                       //this.error("packet_data="+packet_data);
                }
        }
        return this.rQ.length>0;
@@ -387,7 +398,7 @@
                        bdata = bencode(packet);
                }
                catch (e) {
-                       console.error("Error: failed to bencode packet:", 
packet);
+                       this.error("Error: failed to bencode packet:", packet);
                        continue;
                }
                let proto_flags = 0;
@@ -437,7 +448,7 @@
                        return;
                }
 
-               const raw_draw_buffer = (packet[0] === 'draw') && (packet[6] 
!== 'scroll');
+               const raw_draw_buffer = (packet[0] === 'draw') && (packet[6] 
!== 'scroll') && (packet[7].buffer);
                postMessage({'c': 'p', 'p': packet}, raw_draw_buffer ? 
[packet[7].buffer] : []);
        }
 };
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/html5/js/Utilities.js 
new/xpra-4.0.2/html5/js/Utilities.js
--- old/xpra-4.0.1/html5/js/Utilities.js        2020-05-17 18:12:15.000000000 
+0200
+++ new/xpra-4.0.2/html5/js/Utilities.js        2020-06-05 11:40:10.000000000 
+0200
@@ -10,7 +10,7 @@
 'use strict';
 
 const Utilities = {
-       VERSION : "4.0.1",
+       VERSION : "4.0.2",
        REVISION : "0",
        LOCAL_MODIFICATIONS : "0",
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/html5/js/Window.js 
new/xpra-4.0.2/html5/js/Window.js
--- old/xpra-4.0.1/html5/js/Window.js   2020-05-10 19:00:49.000000000 +0200
+++ new/xpra-4.0.2/html5/js/Window.js   2020-06-05 11:40:10.000000000 +0200
@@ -66,6 +66,7 @@
        this.windowtype = null;
        this.fullscreen = false;
        this.saved_geometry = null;
+       this.minimized = false;
        this.maximized = false;
        this.focused = false;
        this.decorations = true;
@@ -499,6 +500,10 @@
                }
                jQuery(this.div).css('opacity', ''+opacity);
        }
+       if ("iconic" in metadata) {
+               this.set_minimized(metadata["iconic"]==1);
+       }
+       
        //if the attribute is set, add the corresponding css class:
        const attrs = ["modal", "above", "below"];
        for (let i = 0; i < attrs.length; i++) {
@@ -669,7 +674,16 @@
  * Minimizes / unminimizes the window.
  */
 XpraWindow.prototype.set_minimized = function(minimized) {
-       jQuery(this.div).toggle(200);
+       if (this.minimized==minimized) {
+               return;
+       }
+       this.minimized = minimized;
+       if (minimized) {
+               jQuery(this.div).hide(200);
+       }
+       else {
+               jQuery(this.div).show(200);
+       }
 };
 
 
@@ -677,6 +691,13 @@
  * Toggle minimized state
  */
 XpraWindow.prototype.toggle_minimized = function() {
+       if (!this.minimized) {
+               this.client.send(["unmap-window", this.wid, True]);
+       }
+       else {
+               const geom = this.get_internal_geometry();
+               this.client.send(["map-window", this.wid, geom.x, geom.y, 
geom.w, geom.h, this.client._get_client_properties(this)]);
+       }
        this.set_minimized(!this.minimized);
 };
 
@@ -1331,15 +1352,23 @@
                                img_data = inflated.slice(0, uncompressedSize);
                        }
                        // set the imagedata rgb32 method
-                       if(img_data.length > img.data.length) {
-                               paint_error("data size mismatch: wanted 
"+img.data.length+", got "+img_data.length+", stride="+rowstride);
+                       let target_stride = width*4;
+                       this.debug("draw", "got ", img_data.length, "to paint 
with stride", rowstride, ", target stride", target_stride);
+                       if (rowstride>target_stride) {
+                               //we have to set line by line to honour the 
rowstride
+                               for (let i = 0; i < height; i++) {
+                                       //we want to slice one line from the 
img_data buffer
+                                       //(but without using slice or subarray 
because the resulting array is too big!?)
+                                       let line = new 
Uint8Array(img_data.buffer, i*rowstride, target_stride);
+                                       img.data.set(line, i*target_stride);
+                               }
                        }
                        else {
-                               this.debug("draw", "got ", img_data.length, "to 
paint with stride", rowstride);
                                img.data.set(img_data);
-                               this.offscreen_canvas_ctx.putImageData(img, x, 
y);
-                               painted();
                        }
+                       this.offscreen_canvas_ctx.clearRect(x, y, width, 
height);
+                       this.offscreen_canvas_ctx.putImageData(img, x, y);
+                       painted();
                        this.may_paint_now();
                }
                else if (coding=="jpeg" || coding=="png" || coding=="webp") {
@@ -1350,6 +1379,7 @@
                                        paint_error("invalid image size: 
"+j.width+"x"+j.height);
                                }
                                else {
+                                       me.offscreen_canvas_ctx.clearRect(x, y, 
j.width, j.height);
                                        me.offscreen_canvas_ctx.drawImage(j, x, 
y);
                                        painted();
                                }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/setup.py new/xpra-4.0.2/setup.py
--- old/xpra-4.0.1/setup.py     2020-05-17 18:12:15.000000000 +0200
+++ new/xpra-4.0.2/setup.py     2020-06-05 11:40:10.000000000 +0200
@@ -11,6 +11,9 @@
 
 import ssl
 import sys
+if sys.version_info<(3, 6):
+    raise Exception("xpra no longer supports Python versions older than 3.6")
+
 import glob
 import shutil
 import os.path
@@ -27,8 +30,6 @@
     is_Ubuntu, is_Debian, is_Fedora, is_CentOS, is_RedHat,
     )
 
-if sys.version_info<(3, 6):
-    raise Exception("xpra no longer supports Python versions older than 3.6")
 #we don't support versions of Python without the new ssl code:
 if not hasattr(ssl, "SSLContext"):
     print("Warning: xpra requires a Python version with ssl.SSLContext 
support")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/setup_html5.py 
new/xpra-4.0.2/setup_html5.py
--- old/xpra-4.0.1/setup_html5.py       2020-05-17 18:12:15.000000000 +0200
+++ new/xpra-4.0.2/setup_html5.py       2020-06-05 11:40:10.000000000 +0200
@@ -86,6 +86,8 @@
         if k!="":
             k = os.sep+k
         for fname in files:
+            if fname.endswith(".tmp"):
+                continue
             src = os.path.join(os.getcwd(), fname)
             parts = fname.split(os.path.sep)
             if parts[0]=="html5":
@@ -107,10 +109,11 @@
             bname = os.path.basename(src)
 
             fsrc = src
-            if ftype=="js":
+            if ftype=="js" or fname.endswith("index.html"):
                 #save to a temporary file after replacing strings:
                 with open(src, mode='br') as f:
-                    data = f.read().decode("latin1")
+                    odata = f.read().decode("latin1")
+                data = odata
                 if bname=="Utilities.js":
                     print("adding revision info to %s" % (bname,))
                     if REVISION:
@@ -131,46 +134,45 @@
                         newdata.append(p.sub(replacewith, line))
                     data = "\n".join(newdata)
 
-                if minifier:
+                if data!=odata:
                     fsrc = src+".tmp"
                     with open(fsrc, "wb") as f:
                         f.write(data.encode("latin1"))
                     os.chmod(fsrc, 0o644)
 
-                    if minifier=="uglifyjs":
-                        minify_cmd = ["uglifyjs",
-                                      fsrc,
-                                      "-o", dst,
-                                      "--compress",
-                                      ]
-                    else:
-                        assert minifier=="yuicompressor"
-                        import yuicompressor        #@UnresolvedImport
-                        jar = yuicompressor.get_jar_filename()
-                        java_cmd = os.environ.get("JAVA", "java")
-                        minify_cmd = [java_cmd, "-jar", jar,
-                                      fsrc,
-                                      "--nomunge",
-                                      "--line-break", "400",
-                                      "--type", ftype,
-                                      "-o", dst,
-                                      ]
-                    try:
-                        r = get_status_output(minify_cmd)[0]
-                        if r!=0:
-                            raise Exception("Error: failed to minify '%s', 
command %s returned error %i" % (
-                                bname, minify_cmd, r))
-                    finally:
-                        os.unlink(fsrc)
-                    os.chmod(dst, 0o644)
-                    print("minified %s" % (fname, ))
+            if minifier and ftype=="js":
+                if minifier=="uglifyjs":
+                    minify_cmd = ["uglifyjs",
+                                  fsrc,
+                                  "-o", dst,
+                                  "--compress",
+                                  ]
                 else:
-                    with open(dst, mode="bw") as f:
-                        f.write(data.encode("latin1"))
+                    assert minifier=="yuicompressor"
+                    import yuicompressor        #@UnresolvedImport
+                    jar = yuicompressor.get_jar_filename()
+                    java_cmd = os.environ.get("JAVA", "java")
+                    minify_cmd = [java_cmd, "-jar", jar,
+                                  fsrc,
+                                  "--nomunge",
+                                  "--line-break", "400",
+                                  "--type", ftype,
+                                  "-o", dst,
+                                  ]
+                r = get_status_output(minify_cmd)[0]
+                if r!=0:
+                    raise Exception("Error: failed to minify '%s', command %s 
returned error %i" % (
+                        bname, minify_cmd, r))
+                os.chmod(dst, 0o644)
+                print("minified %s" % (fname, ))
             else:
-                shutil.copyfile(src, dst)
+                print("copied %s" % (fname,))
+                shutil.copyfile(fsrc, dst)
                 os.chmod(dst, 0o644)
 
+            if fsrc!=src:
+                os.unlink(fsrc)
+
             if ftype not in ("png", ):
                 if gzip:
                     gzip_dst = "%s.gz" % dst
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/svn-info new/xpra-4.0.2/svn-info
--- old/xpra-4.0.1/svn-info     2020-05-17 18:12:20.000000000 +0200
+++ new/xpra-4.0.2/svn-info     2020-06-05 11:40:38.000000000 +0200
@@ -4,10 +4,10 @@
 Relative URL: ^/tags/v4.0.x/src
 Repository Root: file:///var/svn/repos/Xpra
 Repository UUID: 3bb7dfac-3a0b-4e04-842a-767bc560f471
-Revision: 26380
+Revision: 26625
 Node Kind: directory
 Schedule: normal
 Last Changed Author: antoine
-Last Changed Rev: 26379
-Last Changed Date: 2020-05-17 16:07:55 +0100 (Sun, 17 May 2020)
+Last Changed Rev: 26625
+Last Changed Date: 2020-06-05 04:14:36 +0100 (Fri, 05 Jun 2020)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/svn-version new/xpra-4.0.2/svn-version
--- old/xpra-4.0.1/svn-version  2020-05-17 18:12:20.000000000 +0200
+++ new/xpra-4.0.2/svn-version  2020-06-05 11:40:38.000000000 +0200
@@ -1 +1 @@
-26380
+26625
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/win32/MINGW_BUILD.sh 
new/xpra-4.0.2/win32/MINGW_BUILD.sh
--- old/xpra-4.0.1/win32/MINGW_BUILD.sh 2020-05-10 19:00:52.000000000 +0200
+++ new/xpra-4.0.2/win32/MINGW_BUILD.sh 2020-06-05 11:40:10.000000000 +0200
@@ -294,6 +294,9 @@
 for prefix in lib avcodec avformat avutil swscale swresample zlib1 xvidcore; do
        find lib/Xpra -name "${prefix}*dll" -exec mv {} ./lib/ \;
 done
+#liblz4 ends up in the wrong place and duplicated,
+#keep just one copy in ./lib
+find lib/lz4 -name "liblz4.dll" -exec mv {} ./lib/ \;
 if [ "${CLIENT_ONLY}" == "1" ]; then
        rm -fr ./lib/numpy
 else
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/win32/xpra.iss 
new/xpra-4.0.2/win32/xpra.iss
--- old/xpra-4.0.1/win32/xpra.iss       2020-05-17 18:12:15.000000000 +0200
+++ new/xpra-4.0.2/win32/xpra.iss       2020-06-05 11:40:10.000000000 +0200
@@ -1,9 +1,9 @@
 [Setup]
 AppName=Xpra
 AppId=Xpra_is1
-AppVersion=4.0.1
-AppVerName=Xpra 4.0.1
-UninstallDisplayName=Xpra 4.0.1
+AppVersion=4.0.2
+AppVerName=Xpra 4.0.2
+UninstallDisplayName=Xpra 4.0.2
 AppPublisher=xpra.org
 AppPublisherURL=http:;xpra.org/
 DefaultDirName={pf}\Xpra
@@ -16,7 +16,7 @@
 Compression=lzma2/max
 SolidCompression=yes
 AllowUNCPath=false
-VersionInfoVersion=4.0.1
+VersionInfoVersion=4.0.2
 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-4.0.1/xpra/__init__.py 
new/xpra-4.0.2/xpra/__init__.py
--- old/xpra-4.0.1/xpra/__init__.py     2020-05-17 18:12:22.000000000 +0200
+++ new/xpra-4.0.2/xpra/__init__.py     2020-06-05 11:40:41.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__ = "4.0.1"
+__version__ = "4.0.2"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/xpra/client/gtk_base/example/tray.py 
new/xpra-4.0.2/xpra/client/gtk_base/example/tray.py
--- old/xpra-4.0.1/xpra/client/gtk_base/example/tray.py 2020-05-10 
19:00:53.000000000 +0200
+++ new/xpra-4.0.2/xpra/client/gtk_base/example/tray.py 2020-06-05 
11:40:10.000000000 +0200
@@ -81,6 +81,8 @@
         self.quality = 80
         self.speed = 50
         self.encoding = "png"
+        self.send_download_request = None
+        self._remote_subcommands = ()
         def noop(*_args):
             pass
         self._process_encodings = noop
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/xpra/client/gtk_base/mdns_gui.py 
new/xpra-4.0.2/xpra/client/gtk_base/mdns_gui.py
--- old/xpra-4.0.1/xpra/client/gtk_base/mdns_gui.py     2020-05-10 
19:00:53.000000000 +0200
+++ new/xpra-4.0.2/xpra/client/gtk_base/mdns_gui.py     2020-06-05 
11:40:10.000000000 +0200
@@ -53,26 +53,23 @@
         if HIDE_IPV6 and address.find(":")>=0:
             return
         text = text or {}
+        #strip service from hostname:
+        #(win32 servers add it? why!?)
+        if host:
+            if stype and host.endswith(stype):
+                host = host[:-len(stype)]
+            elif stype and domain and host.endswith(stype+"."+domain):
+                host = host[:-len(stype+"."+domain)]
+            if text:
+                mode = text.get("mode")
+                if mode and host.endswith(mode+"."):
+                    host = host[:-len(mode+".")]
+            if host.endswith(".local."):
+                host = host[:-len(".local.")]
         self.records.append((interface, protocol, name, stype, domain, host, 
address, port, text))
         GLib.idle_add(self.populate_table)
 
 
-def check_mdns(gui):
-    import os
-    from xpra.os_util import WIN32
-    if WIN32:
-        for e in ("programfiles", "programfiles(x86)"):
-            d = os.environ.get(e)
-            if not d:
-                continue
-            dll_file = os.path.join(d, "Bonjour", "mdnsNSP.dll")
-            if os.path.exists(dll_file):
-                log("check_mdns() found bonjour DLL: %s", dll_file)
-                return True
-        #bonjour not found:
-        GLib.timeout_add(1000, win32_bonjour_download_warning, gui)
-    return True
-
 def win32_bonjour_download_warning(gui):
     import gi
     gi.require_version("Pango", "1.0")
@@ -121,7 +118,6 @@
         mdns = opts.mdns
         if mdns:
             gui = mdns_sessions(opts)
-            mdns = check_mdns(gui)
         else:
             gui = SessionsGUI(opts)
         Gtk.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/xpra/client/top_client.py 
new/xpra-4.0.2/xpra/client/top_client.py
--- old/xpra-4.0.1/xpra/client/top_client.py    2020-05-10 19:00:53.000000000 
+0200
+++ new/xpra-4.0.2/xpra/client/top_client.py    2020-06-05 11:40:10.000000000 
+0200
@@ -587,9 +587,9 @@
             if isinstance(v, (tuple, list)):
                 return sep.join(bytestostr(x) for x in v)
             return bytestostr(v)
-        if not gli.boolget("enabled", True):
-            return "OpenGL disabled %s" % gli.strget("message")
-        gl_info = "OpenGL %s enabled: %s" % (strget("opengl"), 
gli.strget("renderer") or gli.strget("vendor"))
+        if not gli.boolget("enabled", False):
+            return "OpenGL disabled %s" % gli.strget("message", "")
+        gl_info = "OpenGL %s enabled: %s" % (strget("opengl", "."), 
gli.strget("renderer") or gli.strget("vendor"))
         depth = gli.intget("depth")
         if depth not in (0, 24):
             gl_info += ", %ibits" % depth
@@ -615,7 +615,7 @@
             window_ids = ()    #no longer used or supported by servers
             self.send("info-request", [self.uuid], window_ids, categories)
         if not self.info_timer:
-            self.info_timer = self.timeout_add(REFRESH_RATE+2, 
self.info_timeout)
+            self.info_timer = self.timeout_add((REFRESH_RATE+2)*1000, 
self.info_timeout)
         return True
 
     def init_packet_handlers(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/xpra/net/protocol.py 
new/xpra-4.0.2/xpra/net/protocol.py
--- old/xpra-4.0.1/xpra/net/protocol.py 2020-05-10 19:00:54.000000000 +0200
+++ new/xpra-4.0.2/xpra/net/protocol.py 2020-06-05 11:40:11.000000000 +0200
@@ -424,7 +424,7 @@
             else:
                 #the xpra packet header:
                 #(WebSocketProtocol may also add a websocket header too)
-                header = self.make_chunk_header(packet_type, proto_flags, 
level, index, payload_size)
+                header = self.make_chunk_header(packet_type, proto_flags, 
level, index, payload_size, actual_size)
                 if actual_size<PACKET_JOIN_SIZE:
                     if not isinstance(data, bytes):
                         data = memoryview_to_bytes(data)
@@ -444,7 +444,7 @@
                 items.insert(0, frame_header)
         self.raw_write(packet_type, items, start_send_cb, end_send_cb, 
fail_cb, synchronous, more)
 
-    def make_xpra_header(self, _packet_type, proto_flags, level, index, 
payload_size) -> bytes:
+    def make_xpra_header(self, _packet_type, proto_flags, level, index, 
payload_size, actual_size) -> bytes:
         return pack_header(proto_flags, level, index, payload_size)
 
     def noframe_header(self, _packet_type, _items):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/xpra/net/websockets/protocol.py 
new/xpra-4.0.2/xpra/net/websockets/protocol.py
--- old/xpra-4.0.1/xpra/net/websockets/protocol.py      2020-05-10 
19:00:54.000000000 +0200
+++ new/xpra-4.0.2/xpra/net/websockets/protocol.py      2020-06-05 
11:40:11.000000000 +0200
@@ -49,7 +49,11 @@
         self.ws_mask = MASK
         self._process_read = self.parse_ws_frame
         self.legacy_frame_per_chunk = LEGACY_FRAME_PER_CHUNK in (None, True)
-        self.make_chunk_header = self.make_wschunk_header
+        if self.legacy_frame_per_chunk:
+            self.make_chunk_header = self.make_wschunk_header
+        else:
+            self.make_chunk_header = self.make_xpra_header
+            self.make_frame_header = self.make_wsframe_header
 
     def __repr__(self):
         return "WebSocket(%s)" % self._conn
@@ -86,10 +90,10 @@
             #and one websocket header for all the chunks:
             self.make_frame_header = self.make_wsframe_header
 
-    def make_wschunk_header(self, packet_type, proto_flags, level, index, 
payload_size):
-        header = super().make_xpra_header(packet_type, proto_flags, level, 
index, payload_size)
+    def make_wschunk_header(self, packet_type, proto_flags, level, index, 
payload_size, total_size):
+        header = super().make_xpra_header(packet_type, proto_flags, level, 
index, payload_size, total_size)
         log("make_wschunk_header(%s)", (packet_type, proto_flags, level, 
index, payload_size))
-        return encode_hybi_header(OPCODE_BINARY, 
payload_size+len(header))+header
+        return encode_hybi_header(OPCODE_BINARY, total_size+len(header))+header
 
     def make_wsframe_header(self, packet_type, items):
         payload_len = sum(len(item) for item in items)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/xpra/platform/win32/__init__.py 
new/xpra-4.0.2/xpra/platform/win32/__init__.py
--- old/xpra-4.0.1/xpra/platform/win32/__init__.py      2020-05-10 
19:00:55.000000000 +0200
+++ new/xpra-4.0.2/xpra/platform/win32/__init__.py      2020-06-05 
11:40:11.000000000 +0200
@@ -48,13 +48,13 @@
     PATH = os.environ.get("PATH", "").split(os.pathsep)
     edir = os.path.abspath(os.path.dirname(sys.executable))
     libdir = os.path.join(edir, "lib")
-    for d in (edir, libdir):
+    for d in (libdir, edir):
         if not os.path.exists(d) or not os.path.isdir(d):
             continue
         if d not in sys.path:
-            sys.path.append(d)
+            sys.path.insert(0, d)
         if d not in PATH:
-            PATH.append(d)
+            PATH.insert(0, d)
     os.environ['GI_TYPELIB_PATH'] = os.path.join(libdir, "girepository-1.0")
     os.environ["PATH"] = os.pathsep.join(PATH)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/xpra/platform/win32/clipboard.py 
new/xpra-4.0.2/xpra/platform/win32/clipboard.py
--- old/xpra-4.0.1/xpra/platform/win32/clipboard.py     2020-05-10 
19:00:55.000000000 +0200
+++ new/xpra-4.0.2/xpra/platform/win32/clipboard.py     2020-06-05 
11:40:11.000000000 +0200
@@ -253,7 +253,8 @@
                 packet_data = ([target], (target, dtype, dformat, data))
                 self.send_clipboard_token_handler(self, packet_data)
             self.get_contents(target, got_contents)
-        self.send_clipboard_token_handler(self)
+        else:
+            self.send_clipboard_token_handler(self)
 
     def get_contents(self, target, got_contents):
         log("get_contents%s", (target, got_contents))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/xpra/platform/xposix/gl_context.py 
new/xpra-4.0.2/xpra/platform/xposix/gl_context.py
--- old/xpra-4.0.1/xpra/platform/xposix/gl_context.py   2020-05-10 
19:00:55.000000000 +0200
+++ new/xpra-4.0.2/xpra/platform/xposix/gl_context.py   2020-06-05 
11:40:11.000000000 +0200
@@ -10,6 +10,7 @@
 from xpra.util import envbool
 from xpra.client.gl.gl_check import check_PyOpenGL_support
 from xpra.x11.bindings.display_source import get_display_ptr        
#@UnresolvedImport
+from xpra.gtk_common.error import xsync
 from xpra.gtk_common.gtk_util import enable_alpha
 from xpra.log import Logger
 
@@ -42,7 +43,10 @@
 def c_attrs(props):
     attrs = []
     for k,v in props.items():
-        attrs += [k, v]
+        if v is None:
+            attrs += [k]
+        else:
+            attrs += [k, v]
     attrs += [0, 0]
     return (c_int * len(attrs))(*attrs)
 
@@ -63,8 +67,9 @@
 
     def __enter__(self):
         log("glXMakeCurrent: xid=%#x, context=%s", self.xid, self.context)
-        if not GLX.glXMakeCurrent(self.xdisplay, self.xid, self.context):
-            raise Exception("glXMakeCurrent failed")
+        with xsync:
+            if not GLX.glXMakeCurrent(self.xdisplay, self.xid, self.context):
+                raise Exception("glXMakeCurrent failed")
         self.valid = True
 
     def __exit__(self, *_args):
@@ -100,14 +105,17 @@
             return
         screen = display.get_default_screen()
         bpc = 8
-        attrs = c_attrs({
-            GLX.GLX_RGBA            : True,
+        pyattrs = {
+            GLX.GLX_RGBA            : None,
             GLX.GLX_RED_SIZE        : bpc,
             GLX.GLX_GREEN_SIZE      : bpc,
             GLX.GLX_BLUE_SIZE       : bpc,
-            GLX.GLX_ALPHA_SIZE      : int(alpha)*bpc,
-            GLX.GLX_DOUBLEBUFFER    : int(DOUBLE_BUFFERED),
-            })
+            }
+        if alpha:
+            pyattrs[GLX.GLX_ALPHA_SIZE] = int(alpha)*bpc
+        if DOUBLE_BUFFERED:
+            pyattrs[GLX.GLX_DOUBLEBUFFER] = None
+        attrs = c_attrs(pyattrs)
         self.xdisplay = get_xdisplay()
         xvinfo = GLX.glXChooseVisual(self.xdisplay, screen.get_number(), attrs)
         def getconfig(attrib):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/xpra/platform/xposix/xdg_helper.py 
new/xpra-4.0.2/xpra/platform/xposix/xdg_helper.py
--- old/xpra-4.0.1/xpra/platform/xposix/xdg_helper.py   2020-05-10 
19:00:55.000000000 +0200
+++ new/xpra-4.0.2/xpra/platform/xposix/xdg_helper.py   2020-06-05 
11:40:11.000000000 +0200
@@ -263,7 +263,7 @@
                 try:
                     menu = parse()
                     break
-                except ParsingError as e:
+                except (ParsingError, AttributeError) as e:
                     log("do_load_xdg_menu_data()", exc_info=True)
                     error = e
                     menu = None
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/xpra/server/http_handler.py 
new/xpra-4.0.2/xpra/server/http_handler.py
--- old/xpra-4.0.1/xpra/server/http_handler.py  2020-05-10 19:00:55.000000000 
+0200
+++ new/xpra-4.0.2/xpra/server/http_handler.py  2020-06-05 11:40:12.000000000 
+0200
@@ -87,7 +87,7 @@
                 if matches:
                     path = matches[0]
                     break
-            if not os.path.exists(path) or True:
+            if not os.path.exists(path):
                 #better send something than a 404,
                 #use a transparent 1x1 image:
                 path = os.path.join(self.web_root, "icons", "empty.png")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/xpra/server/mixins/input_server.py 
new/xpra-4.0.2/xpra/server/mixins/input_server.py
--- old/xpra-4.0.1/xpra/server/mixins/input_server.py   2020-05-10 
19:00:55.000000000 +0200
+++ new/xpra-4.0.2/xpra/server/mixins/input_server.py   2020-06-05 
11:40:12.000000000 +0200
@@ -293,7 +293,8 @@
     def _keys_changed(self, *_args):
         if not self.keymap_changing:
             for ss in self._server_sources.values():
-                ss.keys_changed()
+                if hasattr(ss, "keys_changed"):
+                    ss.keys_changed()
 
     def clear_keys_pressed(self):
         pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/xpra/server/server_core.py 
new/xpra-4.0.2/xpra/server/server_core.py
--- old/xpra-4.0.1/xpra/server/server_core.py   2020-05-10 19:00:56.000000000 
+0200
+++ new/xpra-4.0.2/xpra/server/server_core.py   2020-06-05 11:40:12.000000000 
+0200
@@ -77,6 +77,7 @@
 CHALLENGE_TIMEOUT = envint("XPRA_CHALLENGE_TIMEOUT", 120)
 SYSCONFIG = envbool("XPRA_SYSCONFIG", True)
 
+ENCRYPTED_SOCKET_TYPES = os.environ.get("XPRA_ENCRYPTED_SOCKET_TYPES", 
"tcp,ws")
 
 HTTP_UNSUPORTED = b"""HTTP/1.1 400 Bad request syntax or unsupported method
 
@@ -1261,7 +1262,7 @@
         protocol.authenticators = ()
         protocol.encryption = None
         protocol.keyfile = None
-        if socktype=="tcp":
+        if socktype in ENCRYPTED_SOCKET_TYPES:
             #special case for legacy encryption code:
             protocol.encryption = socket_options.get("encryption", 
self.tcp_encryption)
             protocol.keyfile = socket_options.get("encryption-keyfile", 
self.tcp_encryption_keyfile)
@@ -1696,9 +1697,9 @@
         if not username:
             import getpass
             username = getpass.getuser()
+        conn = proto._conn
         #authenticator:
         if not proto.authenticators:
-            conn = proto._conn
             socktype = conn.socktype_wrapped
             try:
                 proto.authenticators = self.make_authenticators(socktype, 
username, conn)
@@ -1740,7 +1741,7 @@
             auth_caps = new_cipher_caps(proto, cipher, encryption_key, 
padding_options)
             authlog("server cipher=%s", auth_caps)
         else:
-            if proto.encryption:
+            if proto.encryption and conn.socktype in ENCRYPTED_SOCKET_TYPES:
                 authlog("client does not provide encryption tokens")
                 auth_failed("missing encryption tokens")
                 return
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/xpra/server/source/clientinfo_mixin.py 
new/xpra-4.0.2/xpra/server/source/clientinfo_mixin.py
--- old/xpra-4.0.1/xpra/server/source/clientinfo_mixin.py       2020-05-10 
19:00:56.000000000 +0200
+++ new/xpra-4.0.2/xpra/server/source/clientinfo_mixin.py       2020-06-05 
11:40:12.000000000 +0200
@@ -138,13 +138,13 @@
                 "version"           : self.client_version or "unknown",
                 "revision"          : self.client_revision or "unknown",
                 "platform_name"     : platform_name(self.client_platform, 
self.client_release),
-                "session-type"      : self.client_session_type,
-                "session-type.full" : self.client_session_type_full,
-                "session-id"        : self.session_id,
-                "uuid"              : self.uuid,
+                "session-type"      : self.client_session_type or "",
+                "session-type.full" : self.client_session_type_full or "",
+                "session-id"        : self.session_id or "",
+                "uuid"              : self.uuid or "",
                 "hostname"          : self.hostname or "",
-                "argv"              : self.argv,
-                "sharing"           : self.sharing,
+                "argv"              : self.argv or (),
+                "sharing"           : bool(self.sharing),
                 }
 
         def addattr(k, name):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/xpra/server/window/window_stats.py 
new/xpra-4.0.2/xpra/server/window/window_stats.py
--- old/xpra-4.0.1/xpra/server/window/window_stats.py   2020-05-10 
19:00:56.000000000 +0200
+++ new/xpra-4.0.2/xpra/server/window/window_stats.py   2020-06-05 
11:40:12.000000000 +0200
@@ -234,7 +234,7 @@
             sent_before = 
monotonic_time()-(self.target_latency+TARGET_LATENCY_TOLERANCE)
             dropped_acks_time = monotonic_time()-60      #1 minute
             drop_missing_acks = []
-            for sequence, item in self.damage_ack_pending.items():
+            for sequence, item in tuple(self.damage_ack_pending.items()):
                 start_send_at = item[0]
                 end_send_at = item[3]
                 if end_send_at==0 or start_send_at>sent_before:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/xpra/src_info.py 
new/xpra-4.0.2/xpra/src_info.py
--- old/xpra-4.0.1/xpra/src_info.py     2020-05-17 18:12:20.000000000 +0200
+++ new/xpra-4.0.2/xpra/src_info.py     2020-06-05 11:40:38.000000000 +0200
@@ -1,2 +1,2 @@
 LOCAL_MODIFICATIONS=0
-REVISION=26380
+REVISION=26625
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/xpra/x11/bindings/keyboard_bindings.pyx 
new/xpra-4.0.2/xpra/x11/bindings/keyboard_bindings.pyx
--- old/xpra-4.0.1/xpra/x11/bindings/keyboard_bindings.pyx      2020-05-10 
19:00:56.000000000 +0200
+++ new/xpra-4.0.2/xpra/x11/bindings/keyboard_bindings.pyx      2020-06-05 
11:40:12.000000000 +0200
@@ -102,6 +102,18 @@
                        int *win_x_return, int *win_y_return, unsigned int 
*mask_return)
 
 
+
+DEF XkbKeyTypesMask             = 1<<0
+DEF XkbKeySymsMask              = 1<<1
+DEF XkbModifierMapMask          = 1<<2
+DEF XkbExplicitComponentsMask   = 1<<3
+DEF XkbKeyActionsMask           = 1<<4
+DEF XkbKeyBehaviorsMask         = 1<<5
+DEF XkbVirtualModsMask          = 1<<6
+DEF XkbAllComponentsMask        = 1<<7
+
+DEF XkbNumKbdGroups = 4
+
 cdef extern from "X11/extensions/XKB.h":
     unsigned long XkbUseCoreKbd
     unsigned long XkbDfltXIId
@@ -120,8 +132,44 @@
         char *                   symbols
         char *                   geometry
     ctypedef XkbComponentNamesRec*  XkbComponentNamesPtr
+    ctypedef void * XkbKeyTypePtr
+    ctypedef struct XkbSymMapRec:
+        unsigned char    kt_index[XkbNumKbdGroups]
+        unsigned char    group_info
+        unsigned char    width
+        unsigned short   offset
+    ctypedef XkbSymMapRec* XkbSymMapPtr
+    ctypedef struct XkbClientMapRec:
+        unsigned char            size_types
+        unsigned char            num_types
+        XkbKeyTypePtr            types
+        unsigned short           size_syms
+        unsigned short           num_syms
+        KeySym                  *syms
+        XkbSymMapPtr             key_sym_map
+        unsigned char           *modmap
+    ctypedef XkbClientMapRec* XkbClientMapPtr
+
+    ctypedef void * XkbControlsPtr
+    ctypedef void * XkbServerMapPtr
+    ctypedef void * XkbIndicatorPtr
+    ctypedef void * XkbNamesPtr
+    ctypedef void * XkbCompatMapPtr
+    ctypedef void * XkbGeometryPtr
     ctypedef struct XkbDescRec:
-        pass
+        Display                 *dpy
+        unsigned short          flags
+        unsigned short          device_spec
+        KeyCode                 min_key_code
+        KeyCode                 max_key_code
+
+        XkbControlsPtr          ctrls
+        XkbServerMapPtr         server
+        XkbClientMapPtr         map
+        XkbIndicatorPtr         indicators
+        XkbNamesPtr             names
+        XkbCompatMapPtr         compat
+        XkbGeometryPtr          geom
     ctypedef XkbDescRec* XkbDescPtr
     ctypedef struct _XkbStateRec:
         unsigned char   group
@@ -170,6 +218,11 @@
                                     unsigned int want, unsigned int need, Bool 
load)
     Status XkbGetState(Display *dpy, unsigned int deviceSpec, XkbStatePtr 
statePtr)
     Bool   XkbLockGroup(Display *dpy, unsigned int deviceSpec, unsigned int 
group)
+    XkbDescPtr XkbGetKeyboard(Display *display, unsigned int which, unsigned 
int device_spec)
+
+    int XkbKeyNumSyms(XkbDescPtr xkb, KeyCode keycode)
+    XkbDescPtr XkbGetMap(Display *display, unsigned int which, unsigned int 
device_spec)
+    void XkbFreeKeyboard(XkbDescPtr xkb, unsigned int which, Bool free_all)
 
 
 cdef extern from "X11/extensions/XTest.h":
@@ -490,6 +543,34 @@
             XFreeModifiermap(xmodmap)
 
 
+    def get_xkb_keycode_mappings(self):
+        if not self.hasXkb():
+            return {}
+        mask = 255
+        cdef XkbDescPtr xkb = XkbGetMap(self.display, mask, XkbUseCoreKbd)
+        if xkb==NULL:
+            return {}
+        cdef KeySym sym
+        keysyms = {}
+        for keycode in range(xkb.min_key_code, xkb.max_key_code):
+            sym_map = xkb.map.key_sym_map[keycode]
+            width = sym_map.width
+            offset = sym_map.offset
+            num_groups = sym_map.group_info
+            group_width = 0
+            syms = []
+            for i in range(width):
+                sym = xkb.map.syms[offset+i]
+                syms.append(sym)
+            #log("%3i: width=%i, offset=%3i, num_groups=%i, syms=%s / %s",
+            #keycode, width, offset, num_groups, syms, symstrs)
+            keynames = self.keysyms_to_strings(syms)
+            if len(keynames)>0:
+                keysyms[keycode] = keynames
+        XkbFreeKeyboard(xkb, 0, 1)
+        return keysyms
+
+
     def get_minmax_keycodes(self):
         if self.min_keycode==-1 and self.max_keycode==-1:
             self._get_minmax_keycodes()
@@ -678,21 +759,25 @@
         raw_mappings = self._get_raw_keycode_mappings()
         mappings = {}
         for keycode, keysyms in raw_mappings.items():
-            keynames = []
-            for keysym in keysyms:
-                key = ""
-                if keysym!=NoSymbol:
-                    keyname = XKeysymToString(keysym)
-                    if keyname!=NULL:
-                        key = bytestostr(keyname)
-                keynames.append(key)
-            #now remove trailing empty entries:
-            while len(keynames)>0 and keynames[-1]=="":
-                keynames = keynames[:-1]
+            keynames = self.keysyms_to_strings(keysyms)
             if len(keynames)>0:
                 mappings[keycode] = keynames
         return mappings
 
+    def keysyms_to_strings(self, keysyms):
+        keynames = []
+        for keysym in keysyms:
+            key = ""
+            if keysym!=NoSymbol:
+                keyname = XKeysymToString(keysym)
+                if keyname!=NULL:
+                    key = bytestostr(keyname)
+            keynames.append(key)
+        #now remove trailing empty entries:
+        while len(keynames)>0 and keynames[-1]=="":
+            keynames = keynames[:-1]
+        return keynames
+
 
     def get_keycodes(self, keyname):
         codes = []
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/xpra/x11/server.py 
new/xpra-4.0.2/xpra/x11/server.py
--- old/xpra-4.0.1/xpra/x11/server.py   2020-05-10 19:00:56.000000000 +0200
+++ new/xpra-4.0.2/xpra/x11/server.py   2020-06-05 11:40:13.000000000 +0200
@@ -443,10 +443,14 @@
 
         root = get_default_root_window()
         with xsync:
-            for window in get_children(root):
-                xid = window.get_xid()
-                if X11Window.is_override_redirect(xid) and 
X11Window.is_mapped(xid):
-                    self._add_new_or_window(window)
+            children = get_children(root)
+        for window in children:
+            xid = window.get_xid()
+            can_add = False
+            with xlog:
+                can_add = X11Window.is_override_redirect(xid) and 
X11Window.is_mapped(xid)
+            if can_add:
+                self._add_new_or_window(window)
 
     def _lookup_window(self, wid):
         assert isinstance(wid, int), "window id value '%s' is a %s and not a 
number" % (wid, type(wid))
@@ -608,7 +612,8 @@
             return
         x, y, nw, nh = self._desktop_manager.window_geometry(window)
         resize_counter = self._desktop_manager.get_resize_counter(window, 1)
-        for ss in self._server_sources.values():
+        wsources = [ss for ss in self._server_sources.values() if 
isinstance(ss, WindowsMixin)]
+        for ss in wsources:
             ss.move_resize_window(wid, window, x, y, nw, nh, resize_counter)
             #refresh to ensure the client gets the new window contents:
             #TODO: to save bandwidth, we should compare the dimensions and 
skip the refresh
@@ -1025,7 +1030,7 @@
                         #try to ensure this won't trigger a resizing loop:
                         counter = max(0, resize_counter-1)
                         for s in self._server_sources.values():
-                            if s!=ss:
+                            if s!=ss and isinstance(ss, WindowsMixin):
                                 s.resize_window(wid, window, aw, ah, 
resize_counter=counter)
                     damage |= owx!=ax or owy!=ay or resized
             if not shown and not skip_geometry:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/xpra/x11/server_keyboard_config.py 
new/xpra-4.0.2/xpra/x11/server_keyboard_config.py
--- old/xpra-4.0.1/xpra/x11/server_keyboard_config.py   2020-05-10 
19:00:56.000000000 +0200
+++ new/xpra-4.0.2/xpra/x11/server_keyboard_config.py   2020-06-05 
11:40:13.000000000 +0200
@@ -8,7 +8,7 @@
 
 from gi.repository import Gdk
 
-from xpra.util import csv, nonl, envbool
+from xpra.util import csv, nonl, envbool, repr_ellipsized
 from xpra.os_util import bytestostr
 from xpra.gtk_common.keymap import get_gtk_keymap
 from xpra.gtk_common.gtk_util import get_default_root_window
@@ -20,7 +20,7 @@
     do_set_keymap, set_all_keycodes, set_keycode_translation,
     get_modifiers_from_meanings, get_modifiers_from_keycodes,
     clear_modifiers, set_modifiers, map_missing_modifiers,
-    clean_keyboard_state,
+    clean_keyboard_state, get_keycode_mappings,
     DEBUG_KEYSYMS,
     )
 from xpra.x11.bindings.keyboard_bindings import X11KeyboardBindings 
#@UnresolvedImport
@@ -390,7 +390,7 @@
         with xsync:
             clean_keyboard_state()
             #keycodes:
-            keycode_to_keynames = X11Keyboard.get_keycode_mappings()
+            keycode_to_keynames = get_keycode_mappings()
             self.keycode_translation = {}
             #prefer keycodes that don't use the lowest level+mode:
             default_for_keyname = {}
@@ -428,7 +428,7 @@
             self.update_keycode_mappings()
 
     def update_keycode_mappings(self):
-        self.keycode_mappings = X11Keyboard.get_keycode_mappings()
+        self.keycode_mappings = get_keycode_mappings()
 
 
     def do_get_keycode(self, client_keycode, keyname, pressed, modifiers, 
group):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xpra-4.0.1/xpra/x11/xkbhelper.py 
new/xpra-4.0.2/xpra/x11/xkbhelper.py
--- old/xpra-4.0.1/xpra/x11/xkbhelper.py        2020-05-10 19:00:57.000000000 
+0200
+++ new/xpra-4.0.2/xpra/x11/xkbhelper.py        2020-06-05 11:40:13.000000000 
+0200
@@ -8,7 +8,7 @@
 
 #ensure that we use gtk as display source:
 from xpra.x11.gtk_x11.gdk_display_source import init_gdk_display_source
-from xpra.util import std, csv
+from xpra.util import std, csv, envbool
 from xpra.os_util import bytestostr
 from xpra.gtk_common.error import xsync, xlog
 from xpra.x11.bindings.keyboard_bindings import X11KeyboardBindings 
#@UnresolvedImport
@@ -19,6 +19,7 @@
 
 log = Logger("x11", "keyboard")
 
+XKB = envbool("XPRA_XKB", True)
 DEBUG_KEYSYMS = [x for x in os.environ.get("XPRA_DEBUG_KEYSYMS", 
"").split(",") if len(x)>0]
 
 #keys we choose not to map if the free space in the keymap is too limited
@@ -152,6 +153,11 @@
     return unset
 
 
+def get_keycode_mappings():
+    if XKB and X11Keyboard.hasXkb():
+        return X11Keyboard.get_xkb_keycode_mappings()
+    return X11Keyboard.get_keycode_mappings()
+
 def set_keycode_translation(xkbmap_x11_keycodes, xkbmap_keycodes):
     """
         Translate the given keycodes into the existing keymap
@@ -167,7 +173,7 @@
     #keycodes = {
     #     9: set([('', 1), ('Escape', 4), ('', 3), ('Escape', 0), ('Escape', 
2)]),
     #     10: set([('onesuperior', 4), ('onesuperior', 8), ('exclam', 1), 
('1', 6), ('exclam', 3), ('1', 2), ('exclamdown', 9), ('exclamdown', 5), ('1', 
0), ('exclam', 7)]),
-    x11_keycodes = X11Keyboard.get_keycode_mappings()
+    x11_keycodes = get_keycode_mappings()
     log(" x11_keycodes=%s", x11_keycodes)
     #x11_keycodes = {
     #    8: ['Mode_switch', '', 'Mode_switch', '', 'Mode_switch'],



Reply via email to