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 <[email protected]> + Copyright (c) 2013-2020 Antoine Martin <[email protected]> <br /> Copyright (c) 2014 Joshua Higgins <[email protected]> </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'],
