xpra icon
Bug tracker and wiki

This bug tracker and wiki are being discontinued
please use https://github.com/Xpra-org/xpra instead.


Ticket #486: info-namespace-v3.patch

File info-namespace-v3.patch, 140.3 KB (added by Antoine Martin, 5 years ago)

updated patch - mostly complete

  • xpra/child_reaper.py

     
    1212import os, sys
    1313import signal
    1414
    15 from xpra.util import updict
    1615from xpra.log import Logger
    1716log = Logger("server", "util")
    1817
     
    186185
    187186    def get_info(self):
    188187        iv = list(self._proc_info)
    189         info = {"children"          : len(iv),
    190                 "children.dead"     : len([x for x in iv if x.dead]),
    191                 "children.ignored"  : len([x for x in iv if x.ignore])}
     188        info = {"children"  : {""           : len(iv),
     189                               "dead"       : len([x for x in iv if x.dead]),
     190                               "ignored"    : len([x for x in iv if x.ignore])}}
    192191        pi = sorted(self._proc_info, key=lambda x: x.pid, reverse=True)
     192        cinfo = {}
    193193        for i, procinfo in enumerate(pi):
    194             d = dict((k,getattr(procinfo,k)) for k in ("name", "command", "ignore", "forget", "returncode", "dead", "pid"))
    195             updict(info, "child[%i]" % i, d)
     194            d = {}
     195            for k in ("name", "command", "ignore", "forget", "returncode", "dead", "pid"):
     196                v = getattr(procinfo, k)
     197                if v is None:
     198                    continue
     199                d[k] = v
     200            cinfo[i] = d
     201        info["child"] = cinfo
    196202        return info
  • xpra/client/gl/gtk_compat.py

     
    2727
    2828    def get_info():
    2929        return {
    30                 "gdkgl.version"         : GdkGLExt._version,
    31                 "gtkgl.version"         : GtkGLExt._version,
     30                "gdkgl"     : {"version"    : GdkGLExt._version},
     31                "gtkgl"     : {"version"    : GtkGLExt._version},
    3232                }
    3333    gdkgl = GdkGLExt
    3434    gtkgl = GtkGLExt
     
    7474        return None
    7575
    7676    def get_info():
     77        def v(x):
     78            return {"version" : x}
    7779        return {
    78                  "pygdkglext.version"   : gdkgl.pygdkglext_version,
    79                  "gtkglext.version"     : gtkgl.gtkglext_version,
    80                  "gdkglext.version"     : gdkgl.gdkglext_version,
    81                  "gdkgl.version"        : gdkgl.query_version()
     80                 "pygdkglext"   : v(gdkgl.pygdkglext_version),
     81                 "gtkglext"     : v(gtkgl.gtkglext_version),
     82                 "gdkglext"     : v(gdkgl.gdkglext_version),
     83                 "gdkgl"        : v(gdkgl.query_version())
    8284                 }
    8385
    8486    class GLContextManager(object):
  • xpra/client/gobject_client_base.py

     
    1111from xpra.log import Logger
    1212log = Logger("gobject", "client")
    1313
     14import os
    1415import sys
    1516import re
    1617from xpra.util import nonl, DONE
     
    1819from xpra.client.client_base import XpraClientBase, EXTRA_TIMEOUT, \
    1920    EXIT_TIMEOUT, EXIT_OK, EXIT_UNSUPPORTED, EXIT_REMOTE_ERROR, EXIT_FILE_TOO_BIG
    2021
     22FLATTEN_INFO = os.environ.get("XPRA_FLATTEN_INFO", "1")=="1"
    2123
     24
    2225class GObjectXpraClient(XpraClientBase, gobject.GObject):
    2326    """
    2427        Utility superclass for GObject clients
     
    227230        capabilities = GObjectXpraClient.make_hello(self)
    228231        log("make_hello() adding info_request to %s", capabilities)
    229232        capabilities["info_request"] = True
     233        if not FLATTEN_INFO:
     234            capabilities["info-namespace"] = True
    230235        return capabilities
    231236
    232237
  • xpra/client/gtk_base/bug_report.py

     
    1818
    1919from xpra.gtk_common.gtk_util import gtk_main, add_close_accel, scaled_image, pixbuf_new_from_file, get_display_info, \
    2020                                    JUSTIFY_LEFT, WIN_POS_CENTER, STATE_NORMAL, FILE_CHOOSER_ACTION_SAVE, choose_file, get_gtk_version_info
    21 from xpra.util import nonl, updict, strtobytes
     21from xpra.util import nonl, strtobytes
    2222from xpra.log import Logger
    2323log = Logger("util")
    2424
     
    136136                    "env"           : os.environ,
    137137                    "config"        : read_xpra_defaults(),
    138138                    }.items():
    139                 updict(d, k, v)
     139                d[k] = v
    140140            return d
    141141        get_screenshot, take_screenshot_fn = None, None
    142142        #screenshot: may have OS-specific code
  • xpra/client/gtk_base/session_info.py

     
    6767    elapsed = now-startt
    6868    return int(total/elapsed), total_n/elapsed, mins, avgs, maxs
    6969
     70def dictlook(d, k, fallback=None):
     71    #deal with old-style non-namespaced dicts first:
     72    #"batch.delay.avg"
     73    if d is None:
     74        return fallback
     75    v = d.get(k)
     76    if v is not None:
     77        return v
     78    parts = k.split(".")
     79    #["batch", "delay", "avg"]
     80    v = d
     81    for p in parts:
     82        try:
     83            v = v.get(p)
     84        except:
     85            return fallback
     86    return v
    7087
     88
    7189class SessionInfo(gtk.Window):
    7290
    7391    def __init__(self, client, session_name, window_icon_pixbuf, conn, get_pixbuf):
     
    135153            return version or "unknown"
    136154        def server_info(*prop_names):
    137155            for x in prop_names:
    138                 v = scaps.capsget(x)
     156                v = dictlook(scaps, x)
    139157                if v is not None:
    140158                    return v
    141                 if self.client.server_last_info:
    142                     v = self.client.server_last_info.get(x)
     159                v = dictlook(self.client.server_last_info, x)
    143160                if v is not None:
    144161                    return v
    145162            return None
     
    160177        gtk_version_info = get_gtk_version_info()
    161178        def client_vinfo(prop, fallback="unknown"):
    162179            k = "%s.version" % prop
    163             return label(make_version_str(gtk_version_info.get(k, fallback)))
     180            return label(make_version_str(dictlook(gtk_version_info, k, fallback)))
    164181        def server_vinfo(prop):
    165182            k = "%s.version" % prop
    166183            fk = "%s_version" % prop
     
    521538            info = ss.get_info()
    522539            if info:
    523540                info = typedict(info)
    524                 self.sound_out_queue_cur.append(info.intget("queue.cur"))
    525                 self.sound_out_queue_min.append(info.intget("queue.min"))
    526                 self.sound_out_queue_max.append(info.intget("queue.max"))
     541                def intlookup(k):
     542                    return int(dictlook(info, k, 0))
     543                self.sound_out_queue_cur.append(intlookup("queue.cur"))
     544                self.sound_out_queue_min.append(intlookup("queue.min"))
     545                self.sound_out_queue_max.append(intlookup("queue.max"))
    527546        return not self.is_closed
    528547
    529548    def populate(self, *args):
     
    574593        if self.client.server_last_info:
    575594            #populate running averages for graphs:
    576595            def getavg(name):
    577                 return self.client.server_last_info.get("%s.avg" % name)
     596                return dictlook(self.client.server_last_info, "%s.avg" % name)
    578597            def addavg(l, name):
    579598                v = getavg(name)
    580599                if v:
     
    780799            return ""
    781800        altv = ""
    782801        if alt:
    783             altv = self.client.server_last_info.get((alt+"."+suffix).encode(), "")
    784         return self.client.server_last_info.get((prefix+"."+suffix).encode(), altv)
     802            altv = dictlook(self.client.server_last_info, (alt+"."+suffix).encode(), "")
     803        return dictlook(self.client.server_last_info, (prefix+"."+suffix).encode(), altv)
    785804
    786805    def values_from_info(self, prefix, alt=None):
    787806        def getv(suffix):
     
    799818            values = []
    800819            for wid in self.client._window_to_id.values():
    801820                for window_prop in window_props:
    802                     v = self.client.server_last_info.get("window[%s].%s.%s" % (wid, window_prop, suffix))
     821                    v = dictlook(self.client.server_last_info, "window[%s].%s.%s" % (wid, window_prop, suffix))
    803822                    if v is not None:
    804823                        values.append(v)
    805824                        break
     
    903922            #remove all the current labels:
    904923            for x in self.encoder_info_box.get_children():
    905924                self.encoder_info_box.remove(x)
    906             window_encoder_stats = {}
    907925            if self.client.server_last_info:
    908                 #We are interested in data like:
    909                 #window[1].encoder=x264
    910                 #window[1].encoder.frames=1
    911                 #window[1].encoder.fps=25
    912                 for k,v in self.client.server_last_info.items():
    913                     k = bytestostr(k)
    914                     if not k.startswith("window["):
    915                         continue
    916                     pos = k.find("].encoder")
    917                     if pos<=0:
    918                         continue
    919                     try:
    920                         wid_str = k[len("window["):pos]     #ie: "1"
    921                         wid = int(wid_str)
    922                     except:
    923                         #wid_str may be invalid, ie:
    924                         #window[1].pipeline_option[1].encoder=video_spec(xpra.codecs.enc_x264.encoder.Encoder)
    925                         # -> wid_str= "1].pipeline_option[1"
    926                         continue
    927                     ekey = k[(pos+len("].encoder")):]   #ie: "" or ".frames"
    928                     if ekey.startswith("."):
    929                         ekey = ekey[1:]
    930                     if ekey=="build_config":
    931                         continue
    932                     window_encoder_stats.setdefault(wid, {})[ekey] = v
    933                 #print("window_encoder_stats=%s" % window_encoder_stats)
     926                window_encoder_stats = self.get_window_encoder_stats()
     927                #log("window_encoder_stats=%s", window_encoder_stats)
    934928                for wid, props in window_encoder_stats.items():
    935929                    l = label("%s (%s)" % (wid, bytestostr(props.get(""))))
    936930                    l.show()
     
    939933                    self.encoder_info_box.add(l)
    940934        return True
    941935
     936    def get_window_encoder_stats(self):
     937        window_encoder_stats = {}
     938        #new-style server with namespace:
     939        window_dict = self.client.server_last_info.get("window")
     940        if window_dict and isinstance(window_dict, dict):
     941            for k,v in window_dict.items():
     942                try:
     943                    wid = int(k)
     944                    encoder_stats = v.get("encoder")
     945                    if encoder_stats:
     946                        window_encoder_stats[wid] = encoder_stats
     947                except:
     948                    pass
     949            return window_encoder_stats
     950        #fallback code, we are interested in string data like:
     951        #window[1].encoder=x264
     952        #window[1].encoder.frames=1
     953        #window[1].encoder.fps=25
     954        for k,v in self.client.server_last_info.items():
     955            k = bytestostr(k)
     956            if not k.startswith("window["):
     957                continue
     958            pos = k.find("].encoder")
     959            if pos<=0:
     960                continue
     961            try:
     962                wid_str = k[len("window["):pos]     #ie: "1"
     963                wid = int(wid_str)
     964            except:
     965                #wid_str may be invalid, ie:
     966                #window[1].pipeline_option[1].encoder=video_spec(xpra.codecs.enc_x264.encoder.Encoder)
     967                # -> wid_str= "1].pipeline_option[1"
     968                continue
     969            ekey = k[(pos+len("].encoder")):]   #ie: "" or ".frames"
     970            if ekey.startswith("."):
     971                ekey = ekey[1:]
     972            if ekey=="build_config":
     973                continue
     974            window_encoder_stats.setdefault(wid, {})[ekey] = v
     975        return window_encoder_stats
     976
     977
    942978    def populate_graphs(self, *args):
    943979        if self.client.server_info_request:
    944980            self.client.send_info_request()
  • xpra/client/ui_client_base.py

     
    14041404            "windows"                   : self.windows_enabled,
    14051405            "show-desktop"              : True,
    14061406            "system_tray"               : self.client_supports_system_tray,
     1407            "info-namespace"            : True,
    14071408            #window meta data and handling:
    14081409            "generic_window_types"      : True,
    14091410            "server-window-move-resize" : True,
     
    24202421            ss.add_data(data, metadata)
    24212422        if self.av_sync and self.server_av_sync:
    24222423            info = ss.get_info()
    2423             queue_used = info.get("queue.cur")
     2424            queue_used = info.get("queue.cur") or info.get("queue", {}).get("cur")
    24242425            if queue_used is None:
    24252426                return
    24262427            delta = (self.queue_used_sent or 0)-queue_used
  • xpra/clipboard/clipboard_base.py

     
    2323from xpra.gtk_common.gtk_util import GetClipboard, PROPERTY_CHANGE_MASK
    2424from xpra.gtk_common.nested_main import NestedMainLoop
    2525from xpra.net.compression import Uncompressed
    26 from xpra.util import csv
     26from xpra.util import csv, updict
    2727
    2828
    2929MIN_CLIPBOARD_COMPRESSION_SIZE = 512
     
    9191                "want_targets"  : self._want_targets,
    9292                }
    9393        for clipboard, proxy in self._clipboard_proxies.items():
    94             for k,v in proxy.get_info().items():
    95                 info["%s.%s" % (clipboard, k)] = v
     94            info[clipboard] = proxy.get_info()
    9695        return info
    9796
    9897    def cleanup(self):
     
    463462                "enabled"       : self._enabled,
    464463                "greedy_client" : self._greedy_client,
    465464                "blocked_owner_change" : self._block_owner_change,
    466                 "event.selection_request"   : self._selection_request_events,
    467                 "event.selection_get"       : self._selection_get_events,
    468                 "event.selection_clear"     : self._selection_clear_events,
    469                 "event.got_token"           : self._got_token_events,
    470                 "event.sent_token"          : self._sent_token_events,
    471                 "event.get_contents"        : self._get_contents_events,
    472                 "event.request_contents"    : self._request_contents_events,
    473465                }
     466        updict(info, "event", {
     467                "selection_request"     : self._selection_request_events,
     468                "selection_get"         : self._selection_get_events,
     469                "selection_clear"       : self._selection_clear_events,
     470                "got_token"             : self._got_token_events,
     471                "sent_token"            : self._sent_token_events,
     472                "get_contents"          : self._get_contents_events,
     473                "request_contents"      : self._request_contents_events})
    474474        return info
    475475
    476476    def cleanup(self):
  • xpra/codecs/csc_opencl/colorspace_converter.py

     
    1313import pyopencl             #@UnresolvedImport
    1414from pyopencl import mem_flags  #@UnresolvedImport
    1515
    16 from xpra.util import updict, engs
     16from xpra.util import engs
    1717from xpra.os_util import _memoryview
    1818
    1919PREFERRED_DEVICE_TYPE = os.environ.get("XPRA_OPENCL_DEVICE_TYPE", "GPU")
     
    486486            "version.cl_header"     : pyopencl.get_cl_header_version(),
    487487            "opengl"                : pyopencl.have_gl(),
    488488            #"kernels"               : KERNELS_DEFS.keys()
     489            "pyopencl"              : get_pyopencl_info(),
    489490            }
    490     updict(info, "pyopencl", get_pyopencl_info())
    491491    if selected_platform:
    492         updict(info, "platform", {
     492        info["platform"] = {
    493493            "name"          : selected_platform.name,
    494494            "vendor"        : selected_platform.vendor,
    495495            "devices"       : len(selected_platform.get_devices()),
    496             })
     496            }
    497497    if selected_device:
    498         if hasattr(selected_device, "opencl_c_version"):
    499             info["device.opencl_c_version"] = getattr(selected_device, "opencl_c_version")
    500         updict(info, "device", {
     498        dinfo = {
    501499            "type"                      : device_type(selected_device),
    502500            "name"                      : selected_device.name.strip(),
    503501            "version"                   : selected_device.version,
     
    504502            "max_work_group_size"       : selected_device.max_work_group_size,
    505503            "max_work_item_dimensions"  : selected_device.max_work_item_dimensions,
    506504            "max_work_item_sizes"       : selected_device.max_work_item_sizes,
    507             "max-size"                  : selected_device_max_size})
     505            "max-size"                  : selected_device_max_size}
     506        if hasattr(selected_device, "opencl_c_version"):
     507            dinfo["opencl_c_version"] = getattr(selected_device, "opencl_c_version")
     508        info["device"] = dinfo
    508509    return info
    509510
    510511
  • xpra/codecs/enc_ffmpeg/__init__.py

     
     1# This file is part of Xpra.
     2# Copyright (C) 2016 Antoine Martin <antoine@devloop.org.uk>
     3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
     4# later version. See the file COPYING for details.
  • xpra/codecs/enc_ffmpeg/encoder.pyx

     
     1# This file is part of Xpra.
     2# Copyright (C) 2012-2014 Antoine Martin <antoine@devloop.org.uk>
     3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
     4# later version. See the file COPYING for details.
     5
     6import weakref
     7from xpra.log import Logger
     8log = Logger("decoder", "ffmpeg")
     9
     10from xpra.codecs.codec_constants import get_subsampling_divs
     11from xpra.codecs.image_wrapper import ImageWrapper
     12from xpra.codecs.libav_common.av_log cimport override_logger, restore_logger #@UnresolvedImport
     13from xpra.util import bytestostr
     14
     15
     16ctypedef unsigned long size_t
     17ctypedef unsigned char uint8_t
     18
     19
     20cdef extern from "../../buffers/buffers.h":
     21    object memory_as_pybuffer(void* ptr, Py_ssize_t buf_len, int readonly)
     22    int    object_as_buffer(object obj, const void ** buffer, Py_ssize_t * buffer_len)
     23    int get_buffer_api_version()
     24
     25cdef extern from "string.h":
     26    void * memcpy(void * destination, void * source, size_t num) nogil
     27    void * memset(void * ptr, int value, size_t num) nogil
     28    void free(void * ptr) nogil
     29
     30
     31cdef extern from "../../inline.h":
     32    pass
     33
     34cdef extern from "../../buffers/memalign.h":
     35    void *xmemalign(size_t size)
     36
     37
     38cdef extern from "libavutil/mem.h":
     39    void av_free(void *ptr)
     40
     41cdef extern from "libavutil/error.h":
     42    int av_strerror(int errnum, char *errbuf, size_t errbuf_size)
     43
     44cdef extern from "libavcodec/version.h":
     45    int LIBAVCODEC_VERSION_MAJOR
     46    int LIBAVCODEC_VERSION_MINOR
     47    int LIBAVCODEC_VERSION_MICRO
     48
     49#why can't we define this inside the avcodec.h section? (beats me)
     50ctypedef unsigned int AVCodecID
     51ctypedef long AVPixelFormat
     52
     53
     54cdef extern from "libavutil/pixfmt.h":
     55    AVPixelFormat AV_PIX_FMT_NONE
     56    AVPixelFormat AV_PIX_FMT_YUV420P
     57
     58cdef extern from "libavcodec/avcodec.h":
     59    int CODEC_FLAG2_FAST
     60
     61    ctypedef struct AVFrame:
     62        uint8_t **data
     63        int *linesize
     64        int format
     65        void *opaque
     66    ctypedef struct AVCodec:
     67        pass
     68    ctypedef struct AVDictionary:
     69        pass
     70    ctypedef struct AVPacket:
     71        uint8_t *data
     72        int      size
     73
     74    ctypedef struct AVCodecContext:
     75        int width
     76        int height
     77        AVPixelFormat pix_fmt
     78        int thread_safe_callbacks
     79        int thread_count
     80        int thread_type
     81        int flags
     82        int flags2
     83        int refcounted_frames
     84
     85    AVCodecID AV_CODEC_ID_H264
     86    AVCodecID AV_CODEC_ID_H265
     87    AVCodecID AV_CODEC_ID_VP8
     88    AVCodecID AV_CODEC_ID_VP9
     89    AVCodecID AV_CODEC_ID_MPEG4
     90
     91    #init and free:
     92    void avcodec_register_all()
     93    AVCodec *avcodec_find_decoder(AVCodecID id)
     94    AVCodecContext *avcodec_alloc_context3(const AVCodec *codec)
     95    int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)
     96    AVFrame* av_frame_alloc()
     97    void av_frame_free(AVFrame **frame)
     98    int avcodec_close(AVCodecContext *avctx)
     99
     100    #actual decoding:
     101    void av_init_packet(AVPacket *pkt) nogil
     102    void avcodec_get_frame_defaults(AVFrame *frame) nogil
     103    int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
     104                                int *got_picture_ptr, const AVPacket *avpkt) nogil
     105
     106    void av_frame_unref(AVFrame *frame) nogil
     107
     108
     109FORMAT_TO_ENUM = {
     110            "YUV420P"   : AV_PIX_FMT_YUV420P,
     111            "YUV422P"   : AV_PIX_FMT_YUV422P,
     112            "YUV444P"   : AV_PIX_FMT_YUV444P,
     113            "RGB"       : AV_PIX_FMT_RGB24,
     114            "XRGB"      : AV_PIX_FMT_0RGB,
     115            "BGRX"      : AV_PIX_FMT_BGR0,
     116            "ARGB"      : AV_PIX_FMT_ARGB,
     117            "BGRA"      : AV_PIX_FMT_BGRA,
     118            "GBRP"      : AV_PIX_FMT_GBRP,
     119            }
     120
     121COLORSPACES = FORMAT_TO_ENUM.keys()
     122ENUM_TO_FORMAT = {}
     123for pix_fmt, av_enum in FORMAT_TO_ENUM.items():
     124    ENUM_TO_FORMAT[av_enum] = pix_fmt
     125
     126def get_version():
     127    return (LIBAVCODEC_VERSION_MAJOR, LIBAVCODEC_VERSION_MINOR, LIBAVCODEC_VERSION_MICRO)
     128
     129avcodec_register_all()
     130CODECS = []
     131if avcodec_find_decoder(AV_CODEC_ID_H264)!=NULL:
     132    CODECS.append("h264")
     133if avcodec_find_decoder(AV_CODEC_ID_VP8)!=NULL:
     134    CODECS.append("vp8")
     135if avcodec_find_decoder(AV_CODEC_ID_VP9)!=NULL:
     136    CODECS.append("vp9")
     137if avcodec_find_decoder(AV_CODEC_ID_H265)!=NULL:
     138    CODECS.append("h265")
     139if avcodec_find_decoder(AV_CODEC_ID_MPEG4)!=NULL:
     140    CODECS.append("mpeg4")
     141log("enc_ffmpeg.init_module: CODECS=%s", CODECS)
     142
     143
     144def init_module():
     145    log("enc_ffmpeg.init_module()")
     146    override_logger()
     147
     148def cleanup_module():
     149    log("enc_ffmpeg.cleanup_module()")
     150    restore_logger()
     151
     152def get_type():
     153    return "ffmpeg"
     154
     155def get_info():
     156    f = {}
     157    for e in get_encodings():
     158        f["formats.%s" % e] = get_input_colorspaces(e)
     159    return  {"version"      : get_version(),
     160             "encodings"    : get_encodings(),
     161             "buffer_api"   : get_buffer_api_version(),
     162             "formats"      : f,
     163             }
     164
     165def get_encodings():
     166    global CODECS
     167    return CODECS
     168
     169def get_input_colorspaces(encoding):
     170    return ["YUV420P"]
     171
     172def get_output_colorspace(encoding, csc):
     173    if encoding not in CODECS:
     174        return ""
     175    return "YUV420P"
     176
     177
     178cdef void clear_frame(AVFrame *frame):
     179    assert frame!=NULL, "frame is not set!"
     180    for i in range(4):
     181        frame.data[i] = NULL
     182
     183
     184cdef class AVFrameWrapper:
     185    """
     186        Wraps an AVFrame so we can free it
     187        once both xpra and avcodec are done with it.
     188    """
     189    cdef AVCodecContext *avctx
     190    cdef AVFrame *frame
     191    cdef int xpra_freed
     192
     193    cdef set_context(self, AVCodecContext *avctx, AVFrame *frame):
     194        self.avctx = avctx
     195        self.frame = frame
     196        log("%s.set_context(%#x, %#x)", self, <unsigned long> avctx, <unsigned long> frame)
     197
     198    def __dealloc__(self):
     199        #By the time this wrapper is garbage collected,
     200        #we must have freed it!
     201        assert self.frame==NULL and self.avctx==NULL, "frame was freed by both, but not actually freed!"
     202
     203    def __str__(self):
     204        if self.frame==NULL:
     205            return "AVFrameWrapper(NULL)"
     206        return "AVFrameWrapper(%#x)" % <unsigned long> self.frame
     207
     208    def xpra_free(self):
     209        log("%s.xpra_free()", self)
     210        self.free()
     211
     212    cdef free(self):
     213        log("%s.free() context=%#x, frame=%#x", self, <unsigned long> self.avctx, <unsigned long> self.frame)
     214        if self.avctx!=NULL and self.frame!=NULL:
     215            av_frame_unref(self.frame)
     216            self.frame = NULL
     217            self.avctx = NULL
     218
     219
     220class AVImageWrapper(ImageWrapper):
     221    """
     222        Wrapper which allows us to call xpra_free on the decoder
     223        when the image is freed, or once we have made a copy of the pixels.
     224    """
     225
     226    def __repr__(self):                          #@DuplicatedSignature
     227        return ImageWrapper.__repr__(self)+"-(%s)" % self.av_frame
     228
     229    def free(self):                             #@DuplicatedSignature
     230        log("AVImageWrapper.free()")
     231        ImageWrapper.free(self)
     232        self.xpra_free_frame()
     233
     234    def clone_pixel_data(self):
     235        log("AVImageWrapper.clone_pixel_data()")
     236        ImageWrapper.clone_pixel_data(self)
     237        self.xpra_free_frame()
     238
     239    def xpra_free_frame(self):
     240        av_frame = self.av_frame
     241        log("AVImageWrapper.xpra_free_frame() av_frame=%s", av_frame)
     242        if av_frame:
     243            self.av_frame = None
     244            av_frame.xpra_free()
     245
     246
     247cdef class Encoder:
     248    """
     249        This wraps the AVCodecContext and its configuration,
     250        also tracks AVFrames.
     251        It also handles reconstructing a single ImageWrapper
     252        constructed from 3-pass decoding (see plane_sizes).
     253    """
     254    cdef AVCodec *codec
     255    cdef AVCodecContext *codec_ctx
     256    cdef AVPixelFormat pix_fmt
     257    cdef AVPixelFormat actual_pix_fmt
     258    cdef object colorspace
     259    cdef object weakref_images
     260    cdef AVFrame *av_frame
     261    #this is the actual number of images we have returned
     262    cdef unsigned long frames
     263    cdef int width
     264    cdef int height
     265    cdef object encoding
     266
     267    cdef object __weakref__
     268
     269    def init_context(self, encoding, int width, int height, colorspace):
     270        cdef int r
     271        cdef int i
     272        assert encoding in CODECS
     273        self.encoding = encoding
     274        self.width = width
     275        self.height = height
     276        assert colorspace in COLORSPACES, "invalid colorspace: %s" % colorspace
     277        self.colorspace = ""
     278        for x in COLORSPACES:
     279            if x==colorspace:
     280                self.colorspace = x
     281                break
     282        if not self.colorspace:
     283            log.error("invalid pixel format: %s", colorspace)
     284            return  False
     285        self.pix_fmt = FORMAT_TO_ENUM.get(colorspace, AV_PIX_FMT_NONE)
     286        if self.pix_fmt==AV_PIX_FMT_NONE:
     287            log.error("invalid pixel format: %s", colorspace)
     288            return  False
     289        self.actual_pix_fmt = self.pix_fmt
     290
     291        avcodec_register_all()
     292
     293        cdef AVCodecID CodecID
     294        if self.encoding=="h264":
     295            CodecID = AV_CODEC_ID_H264
     296        elif self.encoding=="h265":
     297            CodecID = AV_CODEC_ID_H265
     298        elif self.encoding=="vp8":
     299            CodecID = AV_CODEC_ID_VP8
     300        elif self.encoding=="vp9":
     301            CodecID = AV_CODEC_ID_VP9
     302        elif self.encoding=="mpeg4":
     303            CodecID = AV_CODEC_ID_MPEG4
     304        else:
     305            raise Exception("invalid codec; %s" % self.encoding)
     306        self.codec = avcodec_find_decoder(CodecID)
     307        if self.codec==NULL:
     308            log.error("codec %s not found!" % self.encoding)
     309            return  False
     310
     311        #from here on, we have to call clean_decoder():
     312        self.codec_ctx = avcodec_alloc_context3(self.codec)
     313        if self.codec_ctx==NULL:
     314            log.error("failed to allocate codec context!")
     315            self.clean_decoder()
     316            return  False
     317
     318        self.codec_ctx.refcounted_frames = 1
     319        self.codec_ctx.width = width
     320        self.codec_ctx.height = height
     321        self.codec_ctx.pix_fmt = self.pix_fmt
     322        #self.codec_ctx.get_buffer2 = avcodec_get_buffer2
     323        #self.codec_ctx.release_buffer = avcodec_release_buffer
     324        self.codec_ctx.thread_safe_callbacks = 1
     325        self.codec_ctx.thread_type = 2      #FF_THREAD_SLICE: allow more than one thread per frame
     326        self.codec_ctx.thread_count = 0     #auto
     327        self.codec_ctx.flags2 |= CODEC_FLAG2_FAST   #may cause "no deblock across slices" - which should be fine
     328        r = avcodec_open2(self.codec_ctx, self.codec, NULL)
     329        if r<0:
     330            log.error("could not open codec: %s", self.av_error_str(r))
     331            self.clean_decoder()
     332            return  False
     333        #up to 3 AVFrame objects used:
     334        self.av_frame = av_frame_alloc()
     335        if self.av_frame==NULL:
     336            log.error("could not allocate an AVFrame for decoding")
     337            self.clean_decoder()
     338            return  False
     339        self.frames = 0
     340        #to keep track of images not freed yet:
     341        #(we want a weakref.WeakSet() but this is python2.7+ only..)
     342        self.weakref_images = []
     343        #register this decoder in the global dictionary:
     344        log("dec_avcodec.Decoder.init_context(%s, %s, %s) self=%s", width, height, colorspace, self.get_info())
     345        return True
     346
     347    def clean(self):
     348        self.clean_decoder()
     349        self.codec = NULL
     350        self.pix_fmt = 0
     351        self.actual_pix_fmt = 0
     352        self.colorspace = ""
     353        self.weakref_images = []
     354        self.av_frame = NULL                        #should be redundant
     355        self.frames = 0
     356        self.width = 0
     357        self.height = 0
     358        self.encoding = ""
     359
     360
     361    def clean_decoder(self):
     362        cdef int r, i
     363        log("%s.clean_decoder()", self)
     364        #we may have images handed out, ensure we don't reference any memory
     365        #that needs to be freed using avcodec_release_buffer(..)
     366        #as this requires the context to still be valid!
     367        #copying the pixels should ensure we free the AVFrameWrapper associated with it:
     368        if self.weakref_images:
     369            images = [y for y in [x() for x in self.weakref_images] if y is not None]
     370            self.weakref_images = []
     371            log("clean_decoder() cloning pixels for images still in use: %s", images)
     372            for img in images:
     373                if not img.freed:
     374                    img.clone_pixel_data()
     375
     376        if self.av_frame!=NULL:
     377            log("clean_decoder() freeing AVFrame: %#x", <unsigned long> self.av_frame)
     378            av_frame_free(&self.av_frame)
     379            #redundant: self.frame = NULL
     380
     381        cdef unsigned long ctx_key          #@DuplicatedSignature
     382        log("clean_decoder() freeing AVCodecContext: %#x", <unsigned long> self.codec_ctx)
     383        if self.codec_ctx!=NULL:
     384            r = avcodec_close(self.codec_ctx)
     385            if r!=0:
     386                log.warn("error closing decoder context %#x: %s", <unsigned long> self.codec_ctx, self.av_error_str(r))
     387            av_free(self.codec_ctx)
     388            self.codec_ctx = NULL
     389        log("clean_decoder() done")
     390
     391    cdef av_error_str(self, errnum):
     392        cdef char[128] err_str
     393        cdef int i = 0
     394        if av_strerror(errnum, err_str, 128)==0:
     395            while i<128 and err_str[i]!=0:
     396                i += 1
     397            return bytestostr(err_str[:i])
     398        return str(errnum)
     399
     400    def __repr__(self):                      #@DuplicatedSignature
     401        if self.is_closed():
     402            return "dec_avcodec.Decoder(*closed*)"
     403        return "dec_avcodec.Decoder(%s)" % self.get_info()
     404
     405    def get_info(self):                      #@DuplicatedSignature
     406        info = {"version"   : get_version(),
     407                "encoding"  : self.encoding,
     408                "formats"   : get_input_colorspaces(self.encoding),
     409                "type"      : self.get_type(),
     410                "frames"    : self.frames,
     411                "width"     : self.width,
     412                "height"    : self.height,
     413                }
     414        if self.colorspace:
     415            info["colorspace"] = self.colorspace
     416            info["actual_colorspace"] = self.get_actual_colorspace()
     417        if not self.is_closed():
     418            info["decoder_width"] = self.codec_ctx.width
     419            info["decoder_height"] = self.codec_ctx.height
     420        else:
     421            info["closed"] = True
     422        return info
     423
     424    def is_closed(self):
     425        return self.codec_ctx==NULL
     426
     427    def __dealloc__(self):                          #@DuplicatedSignature
     428        self.clean()
     429
     430    def get_width(self):
     431        return self.width
     432
     433    def get_height(self):
     434        return self.height
     435
     436    def get_encoding(self):
     437        return self.encoding
     438
     439    def get_type(self):                             #@DuplicatedSignature
     440        return "avcodec"
     441
     442    def decompress_image(self, input, options):
     443        cdef unsigned char * padded_buf = NULL
     444        cdef const unsigned char * buf = NULL
     445        cdef Py_ssize_t buf_len = 0
     446        cdef int size
     447        cdef int len = 0
     448        cdef int nplanes
     449        cdef int got_picture
     450        cdef AVPacket avpkt
     451        cdef unsigned long frame_key                #@DuplicatedSignature
     452        cdef AVFrameWrapper framewrapper
     453        cdef AVFrame *av_frame
     454        cdef object img
     455        assert self.codec_ctx!=NULL, "no codec context! (not initialized or already closed)"
     456        assert self.codec!=NULL
     457
     458        #copy the whole input buffer into a padded C buffer:
     459        assert object_as_buffer(input, <const void**> &buf, &buf_len)==0
     460        padded_buf = <unsigned char *> xmemalign(buf_len+128)
     461        memcpy(padded_buf, buf, buf_len)
     462        memset(padded_buf+buf_len, 0, 128)
     463
     464        #note: plain RGB output, will redefine those:
     465        out = []
     466        strides = []
     467        outsize = 0
     468
     469        #ensure we can detect if the frame buffer got allocated:
     470        clear_frame(self.av_frame)
     471        #now safe to run without gil:
     472        with nogil:
     473            av_init_packet(&avpkt)
     474            avpkt.data = <uint8_t *> (padded_buf)
     475            avpkt.size = buf_len
     476            len = avcodec_decode_video2(self.codec_ctx, self.av_frame, &got_picture, &avpkt)
     477        if len<0:
     478            av_frame_unref(self.av_frame)
     479            log("%s.decompress_image(%s:%s, %s) avcodec_decode_video2 failure: %s", self, type(input), buf_len, options, self.av_error_str(len))
     480            log.error("avcodec_decode_video2 %s decoding failure:", self.encoding)
     481            log.error(" %s", self.av_error_str(len))
     482            return None
     483        if len==0:
     484            av_frame_unref(self.av_frame)
     485            log("%s.decompress_image(%s:%s, %s) avcodec_decode_video2 failed to decode the stream", self, type(input), buf_len, options)
     486            log.error("avcodec_decode_video2 %s decoding failure - no stream", self.encoding)
     487            return None
     488
     489        if self.actual_pix_fmt!=self.av_frame.format:
     490            if self.av_frame.format==-1:
     491                log.error("avcodec error decoding %i bytes of %s data", buf_len, self.encoding)
     492                log.error(" frame %i", self.frames)
     493                log.error(" options=%s", options)
     494                log.error(" decoder state:")
     495                for k,v in self.get_info().items():
     496                    log.error("  %s = %s", k, v)
     497                return None
     498            self.actual_pix_fmt = self.av_frame.format
     499            if self.actual_pix_fmt not in ENUM_TO_FORMAT:
     500                av_frame_unref(self.av_frame)
     501                log.error("unknown output pixel format: %s, expected %s (%s)", self.actual_pix_fmt, self.pix_fmt, self.colorspace)
     502                return None
     503            log("avcodec actual output pixel format is %s (%s), expected %s (%s)", self.actual_pix_fmt, self.get_actual_colorspace(), self.pix_fmt, self.colorspace)
     504
     505        cs = self.get_actual_colorspace()
     506        if cs.endswith("P"):
     507            divs = get_subsampling_divs(cs)
     508            nplanes = 3
     509            for i in range(3):
     510                _, dy = divs[i]
     511                if dy==1:
     512                    height = self.codec_ctx.height
     513                elif dy==2:
     514                    height = (self.codec_ctx.height+1)>>1
     515                else:
     516                    av_frame_unref(self.av_frame)
     517                    raise Exception("invalid height divisor %s" % dy)
     518                stride = self.av_frame.linesize[i]
     519                size = height * stride
     520                outsize += size
     521
     522                out.append(memory_as_pybuffer(<void *>self.av_frame.data[i], size, True))
     523                strides.append(stride)
     524                log("decompress_image() read back yuv plane %s: %s bytes", i, size)
     525        else:
     526            #RGB mode: "out" is a single buffer
     527            strides = self.av_frame.linesize[0]+self.av_frame.linesize[1]+self.av_frame.linesize[2]
     528            outsize = self.codec_ctx.height * strides
     529            out = memory_as_pybuffer(<void *>self.av_frame.data[0], outsize, True)
     530            nplanes = 0
     531            log("decompress_image() read back rgb buffer: %s bytes", outsize)
     532
     533        #FIXME: we could lose track of framewrappers if an error occurs before the end:
     534        framewrapper = AVFrameWrapper()
     535        framewrapper.set_context(self.codec_ctx, self.av_frame)
     536
     537        if outsize==0:
     538            av_frame_unref(self.av_frame)
     539            raise Exception("output size is zero!")
     540
     541        free(padded_buf)
     542        assert self.codec_ctx.width>=self.width, "codec width is smaller than our width: %s<%s" % (self.codec_ctx.width, self.width)
     543        assert self.codec_ctx.height>=self.height, "codec height is smaller than our height: %s<%s" % (self.codec_ctx.height, self.height)
     544        img = AVImageWrapper(0, 0, self.width, self.height, out, cs, 24, strides, nplanes, thread_safe=False)
     545        img.av_frame = framewrapper
     546        self.frames += 1
     547        #add to weakref list after cleaning it up:
     548        self.weakref_images = [x for x in self.weakref_images if x() is not None]
     549        self.weakref_images.append(weakref.ref(img))
     550        log("%s.decompress_image(%s:%s, %s)=%s", self, type(input), buf_len, options, img)
     551        return img
     552
     553
     554    def get_colorspace(self):
     555        return self.colorspace
     556
     557    def get_actual_colorspace(self):
     558        return ENUM_TO_FORMAT.get(self.actual_pix_fmt, "unknown/invalid")
     559
     560
     561def selftest(full=False):
     562    global CODECS
     563    from xpra.codecs.codec_checks import testdecoder
     564    from xpra.codecs.dec_avcodec2 import decoder
     565    global CODECS
     566    CODECS = testdecoder(decoder, full)
  • xpra/codecs/mkv/__init__.py

     
     1# This file is part of Xpra.
     2# Copyright (C) 2016 Antoine Martin <antoine@devloop.org.uk>
     3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
     4# later version. See the file COPYING for details.
  • xpra/codecs/mkv/muxer.pyx

     
     1# This file is part of Xpra.
     2# Copyright (C) 2016 Antoine Martin <antoine@devloop.org.uk>
     3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
     4# later version. See the file COPYING for details.
     5
     6import time
     7import os
     8
     9from xpra.log import Logger
     10log = Logger("encoder", "mkv")
     11
     12from libc.stdint cimport int64_t
     13
     14cdef extern from "string.h":
     15    void *memset(void *ptr, int value, size_t num) nogil
     16    void free(void *ptr) nogil
     17
     18cdef extern from "../../buffers/memalign.h":
     19    void *xmemalign(size_t size)
     20
     21cdef extern from "../../buffers/buffers.h":
     22    int object_as_buffer(object obj, const void ** buffer, Py_ssize_t * buffer_len)
     23    int get_buffer_api_version()
     24
     25
     26cdef extern from "libmkv.h":
     27    pass
     28
     29cdef class Muxer:
     30    cdef object __weakref__
     31
     32#init_context(w, h, src_format, encoding, quality, speed, scaling, options)
     33    def init_context(self, int width, int height, src_format, dst_formats, encoding, int quality, int speed, scaling, options):    #@DuplicatedSignature
     34        pass
  • xpra/codecs/nvenc4/encoder.pyx

     
    1616from pycuda.driver import memcpy_htod
    1717from pycuda.compiler import compile
    1818
    19 from xpra.util import AtomicInteger, updict, engs, csv, pver
     19from xpra.util import AtomicInteger, engs, csv, pver
    2020from xpra.os_util import _memoryview
    2121from xpra.codecs.cuda_common.cuda_context import init_all_devices, get_devices, select_device, \
    2222                get_cuda_info, get_pycuda_info, device_info, reset_state, \
     
    15871587                "bitrate"           : self.target_bitrate,
    15881588                "quality"           : self.quality,
    15891589                "speed"             : self.speed,
    1590                 "lossless"          : self.lossless,
    1591                 "lossless.supported": LOSSLESS_ENABLED,
    1592                 "lossless.threshold": LOSSLESS_THRESHOLD,
    1593                 "yuv444.supported"  : YUV444_ENABLED,
    1594                 "yuv444.threshold"  : YUV444_THRESHOLD,
     1590                "lossless"  : {
     1591                               ""          : self.lossless,
     1592                               "supported" : LOSSLESS_ENABLED,
     1593                               "threshold" : LOSSLESS_THRESHOLD
     1594                    },
     1595                "yuv444" : {
     1596                            "supported" : YUV444_ENABLED,
     1597                            "threshold" : YUV444_THRESHOLD,
     1598                            },
     1599                "cuda-device"   : self.cuda_device_info,
     1600                "cuda"          : self.cuda_info,
     1601                "pycuda"        : self.pycuda_info,
    15951602                })
    15961603        if self.scaling!=(1,1):
    15971604            info.update({
     
    15981605                "input_width"       : self.input_width,
    15991606                "input_height"      : self.input_height,
    16001607                "scaling"           : self.scaling})
    1601         updict(info, "cuda", self.cuda_device_info)
    1602         updict(info, "cuda", self.cuda_info)
    1603         updict(info, "pycuda", self.pycuda_info)
    16041608        if self.src_format:
    16051609            info["src_format"] = self.src_format
    16061610        if self.pixel_format:
  • xpra/codecs/nvenc5/encoder.pyx

     
    1616from pycuda.driver import memcpy_htod
    1717from pycuda.compiler import compile
    1818
    19 from xpra.util import AtomicInteger, updict, engs, csv, pver
     19from xpra.util import AtomicInteger, engs, csv, pver
    2020from xpra.os_util import _memoryview
    2121from xpra.codecs.cuda_common.cuda_context import init_all_devices, get_devices, select_device, \
    2222                get_cuda_info, get_pycuda_info, device_info, reset_state, \
     
    16251625                "bitrate"           : self.target_bitrate,
    16261626                "quality"           : self.quality,
    16271627                "speed"             : self.speed,
    1628                 "lossless"          : self.lossless,
    1629                 "lossless.supported": LOSSLESS_ENABLED,
    1630                 "lossless.threshold": LOSSLESS_THRESHOLD,
    1631                 "yuv444.supported"  : YUV444_ENABLED,
    1632                 "yuv444.threshold"  : YUV444_THRESHOLD,
     1628                "lossless"  : {
     1629                               ""          : self.lossless,
     1630                               "supported" : LOSSLESS_ENABLED,
     1631                               "threshold" : LOSSLESS_THRESHOLD
     1632                    },
     1633                "yuv444" : {
     1634                            "supported" : YUV444_ENABLED,
     1635                            "threshold" : YUV444_THRESHOLD,
     1636                            },
     1637                "cuda-device"   : self.cuda_device_info,
     1638                "cuda"          : self.cuda_info,
     1639                "pycuda"        : self.pycuda_info,
    16331640                })
    16341641        if self.scaling!=(1,1):
    16351642            info.update({
     
    16361643                "input_width"       : self.input_width,
    16371644                "input_height"      : self.input_height,
    16381645                "scaling"           : self.scaling})
    1639         updict(info, "cuda", self.cuda_device_info)
    1640         updict(info, "cuda", self.cuda_info)
    1641         updict(info, "pycuda", self.pycuda_info)
    16421646        if self.src_format:
    16431647            info["src_format"] = self.src_format
    16441648        if self.pixel_format:
  • xpra/codecs/nvenc6/encoder.pyx

     
    1616from pycuda.driver import memcpy_htod
    1717from pycuda.compiler import compile
    1818
    19 from xpra.util import AtomicInteger, updict, engs, csv, pver
     19from xpra.util import AtomicInteger, engs, csv, pver
    2020from xpra.os_util import _memoryview
    2121from xpra.codecs.cuda_common.cuda_context import init_all_devices, get_devices, select_device, \
    2222                get_cuda_info, get_pycuda_info, device_info, reset_state, \
     
    16961696                "bitrate"           : self.target_bitrate,
    16971697                "quality"           : self.quality,
    16981698                "speed"             : self.speed,
    1699                 "lossless"          : self.lossless,
    1700                 "lossless.supported": LOSSLESS_ENABLED,
    1701                 "lossless.threshold": LOSSLESS_THRESHOLD,
    1702                 "yuv444.supported"  : YUV444_ENABLED,
    1703                 "yuv444.threshold"  : YUV444_THRESHOLD,
     1699                "lossless"  : {
     1700                               ""          : self.lossless,
     1701                               "supported" : LOSSLESS_ENABLED,
     1702                               "threshold" : LOSSLESS_THRESHOLD
     1703                    },
     1704                "yuv444" : {
     1705                            "supported" : YUV444_ENABLED,
     1706                            "threshold" : YUV444_THRESHOLD,
     1707                            },
     1708                "cuda-device"   : self.cuda_device_info,
     1709                "cuda"          : self.cuda_info,
     1710                "pycuda"        : self.pycuda_info,
    17041711                })
    17051712        if self.scaling!=(1,1):
    17061713            info.update({
     
    17071714                "input_width"       : self.input_width,
    17081715                "input_height"      : self.input_height,
    17091716                "scaling"           : self.scaling})
    1710         updict(info, "cuda", self.cuda_device_info)
    1711         updict(info, "cuda", self.cuda_info)
    1712         updict(info, "pycuda", self.pycuda_info)
    17131717        if self.src_format:
    17141718            info["src_format"] = self.src_format
    17151719        if self.pixel_format:
  • xpra/codecs/ogg/__init__.py

     
     1# This file is part of Xpra.
     2# Copyright (C) 2016 Antoine Martin <antoine@devloop.org.uk>
     3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
     4# later version. See the file COPYING for details.
  • xpra/codecs/ogg/muxer.pyx

     
     1# This file is part of Xpra.
     2# Copyright (C) 2016 Antoine Martin <antoine@devloop.org.uk>
     3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
     4# later version. See the file COPYING for details.
     5
     6import time
     7import os
     8
     9from xpra.log import Logger
     10log = Logger("encoder", "mkv")
     11
     12from libc.stdint cimport int64_t
     13
     14cdef extern from "string.h":
     15    void *memset(void *ptr, int value, size_t num) nogil
     16    void free(void *ptr) nogil
     17
     18cdef extern from "../../buffers/memalign.h":
     19    void *xmemalign(size_t size)
     20
     21cdef extern from "../../buffers/buffers.h":
     22    int object_as_buffer(object obj, const void ** buffer, Py_ssize_t * buffer_len)
     23    int get_buffer_api_version()
     24
     25
     26cdef extern from "ogg/ogg.h":
     27    pass
     28
     29cdef class Muxer:
     30    cdef object __weakref__
     31
     32#init_context(w, h, src_format, encoding, quality, speed, scaling, options)
     33    def init_context(self, int width, int height, src_format, dst_formats, encoding, int quality, int speed, scaling, options):    #@DuplicatedSignature
     34        pass
  • xpra/codecs/video_helper.py

     
    195195        for encoding, encoder_specs in self._video_encoder_specs.items():
    196196            for in_csc, specs in encoder_specs.items():
    197197                for spec in specs:
    198                     d.setdefault("encoding.%s_to_%s" % (in_csc, encoding), []).append(spec.codec_type)
     198                    d.setdefault("encoding", {}).setdefault("%s_to_%s" % (in_csc, encoding), []).append(spec.codec_type)
    199199        for in_csc, specs in self._csc_encoder_specs.items():
    200200            for out_csc, specs in specs.items():
    201201                d["csc.%s_to_%s" % (in_csc, out_csc)] = [spec.codec_type for spec in specs]
     
    203203            for out_csc, decoders in decoder_specs.items():
    204204                for decoder in decoders:
    205205                    decoder_name, _ = decoder
    206                     d.setdefault("decoding.%s_to_%s" % (encoding, out_csc), []).append(decoder_name)
     206                    d.setdefault("decoding", {}).setdefault("%s_to_%s" % (encoding, out_csc), []).append(decoder_name)
    207207        def modstatus(x, def_list, active_list):
    208208            #the module is present
    209209            if x in active_list:
     
    213213            else:
    214214                return "not found"
    215215        for x in ALL_VIDEO_ENCODER_OPTIONS:
    216             d["encoding.video-encoder.%s" % x] = modstatus(x, get_DEFAULT_VIDEO_ENCODERS(), self.video_encoders)
     216            d.setdefault("encoding", {}).setdefault("video-encoder", {})["%s" % x] = modstatus(x, get_DEFAULT_VIDEO_ENCODERS(), self.video_encoders)
    217217        for x in ALL_CSC_MODULE_OPTIONS:
    218             d["encoding.csc-module.%s" % x] = modstatus(x, get_DEFAULT_CSC_MODULES(), self.csc_modules)
     218            d.setdefault("encoding", {}).setdefault("csc-module", {})["%s" % x] = modstatus(x, get_DEFAULT_CSC_MODULES(), self.csc_modules)
    219219        return d
    220220
    221221    def init(self):
  • xpra/net/bytestreams.py

     
    3434TTY_WRITE = os.write
    3535
    3636
     37FAMILY_STR = {}
     38for x in ("UNIX", "INET", "INET6"):
     39    try:
     40        FAMILY_STR[getattr(socket, "AF_%s" % x)] = x
     41    except:
     42        pass
     43for x in ("STREAM", "DGRAM", "RAW", "RDM", "SEQPACKET"):
     44    try:
     45        FAMILY_STR[getattr(socket, "SOCK_%s" % x)] = x
     46    except:
     47        pass
     48
     49
    3750if sys.platform.startswith("win"):
    3851    #on win32, we have to deal with a few more odd error codes:
    3952    #(it would be nicer if those were wrapped using errno instead..)
     
    215228        d = Connection.get_info(self)
    216229        try:
    217230            d["type"] = "pipe"
    218             d["pipe.read.fd"] = self._read_fd
    219             d["pipe.write.fd"] = self._write_fd
     231            d["pipe"] = {"read"     : {"fd" : self._read_fd},
     232                         "write"    : {"fd" : self._write_fd}}
    220233        except:
    221234            pass
    222235        return d
     
    255268            d["type"] = "socket"
    256269            s = self._socket
    257270            if s:
    258                 from xpra.util import updict
    259                 updict(d, "socket", {
     271                d["socket"] = {
    260272                        "fileno"        : s.fileno(),
    261273                        "timeout"       : int(1000*(s.gettimeout() or 0)),
    262                         "family"        : s.family,
     274                        "family"        : FAMILY_STR.get(s.family, s.family),
    263275                        "proto"         : s.proto,
    264                         "type"          : s.type})
     276                        "type"          : s.type}
    265277        except:
    266278            log.warn("failed to get socket information", exc_info=True)
    267279        return d
  • xpra/net/protocol.py

     
    2020cryptolog = Logger("network", "crypto")
    2121
    2222from xpra.os_util import Queue, strtobytes
    23 from xpra.util import repr_ellipsized, updict, csv
     23from xpra.util import repr_ellipsized, csv
    2424from xpra.net import ConnectionClosedException
    2525from xpra.net.bytestreams import ABORT
    2626from xpra.net import compression
     
    210210
    211211    def get_info(self, alias_info=True):
    212212        info = {
    213             "input.packetcount"     : self.input_packetcount,
    214             "input.raw_packetcount" : self.input_raw_packetcount,
    215             "input.cipher"          : self.cipher_in_name or "",
    216             "input.cipher.padding"  : self.cipher_in_padding,
    217             "output.packetcount"    : self.output_packetcount,
    218             "output.raw_packetcount": self.output_raw_packetcount,
    219             "output.cipher"         : self.cipher_out_name or "",
    220             "output.cipher.padding" : self.cipher_out_padding,
    221213            "large_packets"         : self.large_packets,
    222214            "compression_level"     : self.compression_level,
    223             "max_packet_size"       : self.max_packet_size}
    224         updict(info, "input.count", self.input_stats)
    225         updict(info, "output.count", self.output_stats)
     215            "max_packet_size"       : self.max_packet_size,
     216            "input" : {
     217                       "packetcount"            : self.input_packetcount,
     218                       "raw_packetcount"        : self.input_raw_packetcount,
     219                       "count"                  : self.input_stats,
     220                       "cipher"                 : {"": self.cipher_in_name or "",
     221                                                   "padding"        : self.cipher_in_padding,
     222                                                   },
     223                        },
     224            "output" : {
     225                        "packetcount"           : self.output_packetcount,
     226                        "raw_packetcount"       : self.output_raw_packetcount,
     227                        "count"                 : self.output_stats,
     228                        "cipher"                : {"": self.cipher_out_name or "",
     229                                                   "padding" : self.cipher_out_padding
     230                                                   },
     231                        },
     232            }
    226233        c = self._compress
    227234        if c:
    228235            info["compressor"] = compression.get_compressor_name(self._compress)
     
    233240            else:
    234241                info["encoder"] = packet_encoding.get_encoder_name(self._encoder)
    235242        if alias_info:
    236             for k,v in self.send_aliases.items():
    237                 info["send_alias." + str(k)] = v
    238                 info["send_alias." + str(v)] = k
    239             for k,v in self.receive_aliases.items():
    240                 info["receive_alias." + str(k)] = v
    241                 info["receive_alias." + str(v)] = k
     243            info["send_alias"] = self.send_aliases
     244            info["receive_alias"] = self.receive_aliases
    242245        c = self._conn
    243246        if c:
    244247            try:
     
    248251        info["has_more"] = self._source_has_more.is_set()
    249252        for t in (self._write_thread, self._read_thread, self._read_parser_thread, self._write_format_thread):
    250253            if t:
    251                 info["thread.%s" % t.name] = t.is_alive()
     254                info.setdefault("thread", {})[t.name] = t.is_alive()
    252255        return info
    253256
    254257
  • xpra/net/pycrypto_backend.py

     
    2020    pass
    2121
    2222def get_info():
    23     caps = {"backend"           : "pycrypto",
    24             "pycrypto"          : "True",
    25             "pycrypto.version"  : Crypto.__version__}
    2623    try:
    2724        from Crypto.PublicKey import _fastmath
    2825    except:
    2926        _fastmath = None
    30     caps["pycrypto.fastmath"] = _fastmath is not None
    31     return caps
     27    return {"backend"           : "pycrypto",
     28            "pycrypto"          : {""           : True,
     29                                   "version"    : Crypto.__version__},
     30                                   "fastmath"   : _fastmath is not None}
    3231
    3332
    3433def get_key(password, key_salt, block_size, iterations):
  • xpra/net/pycryptography_backend.py

     
    6565    import cryptography
    6666    return {"backend"                       : "python-cryptography",
    6767            "backends"                      : [ci(x) for x in getattr(backend, "_backends", [])],
    68             "python-cryptography"           : True,
    69             "python-cryptography.version"   : cryptography.__version__}
     68            "python-cryptography"           : {""           : True,
     69                                               "version"    : cryptography.__version__}
     70            }
    7071
    7172def get_key(password, key_salt, block_size, iterations):
    7273    global backend
  • xpra/platform/darwin/shadow_server.py

     
    113113
    114114    def get_info(self, proto):
    115115        info = GTKServerBase.get_info(self, proto)
    116         info["features.shadow"] = True
    117         info["server.type"] = "Python/gtk2/osx-shadow"
     116        info.setdefault("features", {})["shadow"] = True
     117        info.setdefault("server", {})["type"] = "Python/gtk2/osx-shadow"
    118118        return info
  • xpra/platform/gui.py

     
    150150            return str(v)
    151151    def fnames(l):
    152152        return [fname(x) for x in l]
    153     info = {
     153    return {
    154154            "native_tray_menu_helpers"      : fnames(get_native_tray_menu_helper_classes()),
    155155            "native_trays"                  : fnames(get_native_tray_classes()),
    156156            "native_system_trays"           : fnames(get_native_system_tray_classes()),
     
    161161            "desktops"                      : get_number_of_desktops(),
    162162            "desktop_names"                 : get_desktop_names(),
    163163            "vertical-refresh"              : get_vrefresh(),
    164             "double_click.time"             : get_double_click_time(),
    165             "double_click.distance"         : get_double_click_distance(),
    166164            "fixed_cursor_size"             : get_fixed_cursor_size(),
    167165            "cursor_size"                   : get_cursor_size(),
    168             "dpi.x"                         : get_xdpi(),
    169             "dpi.y"                         : get_ydpi(),
    170166            "icon_size"                     : get_icon_size(),
     167            "double_click"                  : {
     168                                               "time"       : get_double_click_time(),
     169                                               "distance"   : get_double_click_distance(),
     170                                               },
     171            "dpi"                           : {
     172                                               "x"          : get_xdpi(),
     173                                               "y"          : get_ydpi(),
     174                                               },
     175            "antialias"                     : get_antialias_info(),
     176            "window_frame"                  : get_window_frame_sizes(),
    171177            }
    172     from xpra.util import updict
    173     updict(info, "antialias", get_antialias_info())
    174     updict(info, "window_frame", get_window_frame_sizes())
    175     return info
    176178
    177179get_info = get_info_base
    178180
  • xpra/platform/paths.py

     
    206206
    207207def get_info():
    208208    return {
    209             "install.prefix"    : get_install_prefix(),
    210             "default_conf.dirs" : get_default_conf_dirs(),
    211             "system_conf.dirs"  : get_system_conf_dirs(),
    212             "user_conf.dirs"    : get_user_conf_dirs(),
    213             "socket.dirs"       : get_socket_dirs(),
    214             "log.dir"           : get_default_log_dir(),
    215             "download.dir"      : get_download_dir(),
    216             "app.dir"           : get_app_dir(),
    217             "app.default.dir"   : default_get_app_dir(),
     209            "install"           : {"prefix" : get_install_prefix()},
     210            "default_conf"      : {"dirs"   : get_default_conf_dirs()},
     211            "system_conf"       : {"dirs"   : get_system_conf_dirs()},
     212            "user_conf"         : {"dirs"   : get_user_conf_dirs()},
     213            "socket"            : {"dirs"   : get_socket_dirs()},
     214            "log"               : {"dir"    : get_default_log_dir()},
     215            "download"          : {"dir"    : get_download_dir()},
     216            "app"               : {"dir"    : get_app_dir()},
     217            "app"               : {"default" : {"dir"   : default_get_app_dir()}},
    218218            "resources"         : get_resources_dir(),
    219219            "icons"             : get_icon_dir(),
    220220            "home"              : os.path.expanduser("~"),
  • xpra/platform/printing.py

     
    6868
    6969
    7070def get_info():
    71     return {
    72             "mimetypes"         : get_mimetypes(),
    73             "mimetypes.default" : DEFAULT_MIMETYPES,
     71    return {"mimetypes" :   {""         : get_mimetypes(),
     72                             "default"  : DEFAULT_MIMETYPES}
    7473            }
    7574
    7675
  • xpra/platform/pycups_printing.py

     
    416416
    417417def get_info():
    418418    from xpra.platform.printing import get_mimetypes, DEFAULT_MIMETYPES
    419     return {
    420             "mimetypes"         : get_mimetypes(),
    421             "mimetypes.default" : DEFAULT_MIMETYPES,
     419    return {"mimetypes"         : {""           : get_mimetypes(),
     420                                   "default"    : DEFAULT_MIMETYPES,
     421                                   "printers"   : MIMETYPE_TO_PRINTER,
     422                                   "ppd"        : MIMETYPE_TO_PPD},
     423            "mimetype"          : {"default"    : DEFAULT_MIMETYPE},
    422424            "simulate-failure"  : SIMULATE_PRINT_FAILURE,
    423425            "allow-user"        : ALLOW,
    424426            "raw-mode"          : RAW_MODE,
    425427            "generic"           : GENERIC,
    426428            "tmpdir"            : FORWARDER_TMPDIR,
    427             "mimetype.default"  : DEFAULT_MIMETYPE,
    428429            "lpadmin"           : LPADMIN,
    429430            "lpinfo"            : LPINFO,
    430431            "forwarder"         : FORWARDER_BACKEND,
     
    431432            "skipped-printers"  : SKIPPED_PRINTERS,
    432433            "add-local-printers": ADD_LOCAL_PRINTERS,
    433434            "printer-prefix"    : PRINTER_PREFIX,
    434             "cups-dbus.default" : DEFAULT_CUPS_DBUS,
    435             "cups-dbus"         : CUPS_DBUS,
    436             "cups-dbus.poll-delay" : POLLING_DELAY,
    437             "mimetypes.printers": MIMETYPE_TO_PRINTER,
    438             "mimetypes.ppd"     : MIMETYPE_TO_PPD,
     435            "cups-dbus"         : {""           : CUPS_DBUS,
     436                                   "default"    : DEFAULT_CUPS_DBUS,
     437                                   "poll-delay" : POLLING_DELAY},
    439438            "cups.default-options"  : DEFAULT_CUPS_OPTIONS,
    440             "printers.predefined" : UNPROBED_PRINTER_DEFS,
    441             "printers"          : get_printer_definitions(),
     439            "printers"          : {""           : get_printer_definitions(),
     440                                   "predefined" : UNPROBED_PRINTER_DEFS},
    442441            }
    443442
    444443
  • xpra/platform/win32/keyboard_config.py

     
    3636    def __repr__(self):
    3737        return "win32.KeyboardConfig"
    3838
    39     def get_info(self):
    40         info = KeyboardConfigBase.get_info(self)
    41         return info
    4239
    43 
    4440    def parse_options(self, props):
    4541        return KeyboardConfigBase.parse_options(self, props)
    4642
     
    122118    #no useful mapping:
    123119    "ICO_CLEAR"             : "IcoClr",
    124120    "ICO_HELP"              : "Help",
     121    "DIVIDE"                : "KP_Divide",
    125122    "MULTIPLY"              : "KP_Multiply",
     123    "SUBTRACT"              : "KP_Substract",
     124    "ADD"                   : "KP_Add",
    126125    "NONAME"                : "NoSymbol",
    127126    "NUMPAD0"               : "KP_0",
    128127    "NUMPAD1"               : "KP_1",
     
    167166    "OEM_PA1"               : "OemPa1",
    168167    "OEM_PA2"               : "OemPa2",
    169168    "OEM_PA3"               : "OemPa3",
    170     "OEM_PLUS"              : "plus",
     169    #"OEM_PLUS"              : "equal",
    171170    "OEM_RESET"             : "Reset",
    172171    "OEM_WSCTRL"            : "WsCtrl",
    173172    "PA1"                   : "Pa1",
     173    "OEM_102"               : "backslash",
    174174    #missing:?
    175175    #"PACKET"                : "Packet",
    176176    "PLAY"                  : "Play",
     
    263263    "VOLUME_UP"             : "XF86AudioRaiseVolume",
    264264    "XBUTTON1"              : "X1",
    265265    "XBUTTON2"              : "X2",
     266    "BRACKET_LEFT"          : "bracketleft",
     267    "BRACKET_RIGHT"         : "bracketright",
     268    "BRACE_LEFT"            : "braceleft",
     269    "BRACE_RIGHT"           : "braceright",
     270    "COLON"                 : "colon",
     271    "SEMICOLON"             : "semicolon",
     272    "APOSTROPHE"            : "apostrophe",
     273    "AT"                    : "at",
     274    "NUMBER_SIGN"           : "numbersign",
     275    "COMMA"                 : "comma",
     276    "LESS"                  : "less",
     277    "EQUAL"                 : "equal",
     278    "GREATER"               : "greater",
     279    "PERIOD"                : "period",
     280    "SLASH"                 : "slash",
     281    "QUESTION"              : "question",
     282    "BAR"                   : "bar",
     283    "EXCLAM"                : "exclam",
     284    "QUOTEDBL"              : "quotedbl",
     285    "STERLING"              : "sterling",
     286    "DOLLAR"                : "dollar",
     287    "PERCENT"               : "percent",
     288    "ASCIICIRCUM"           : "asciicircum",
     289    "AMPERSAND"             : "ampersand",
     290    "ASTERISK"              : "asterisk",
     291    "PARENLEFT"             : "parenleft",
     292    "PARENRIGHT"            : "parenright",
     293    "UNDERSCORE"            : "underscore",
     294    "BACKSLASH"             : "backslash",
     295    "GRAVE"                 : "grave",
    266296}
    267297
    268298#these aren't defined in win32con...
    269299DEFS = {
     300    "EXCLAM"                : 33,
     301    "QUOTEDBL"              : 34,
     302    "NUMBER_SIGN"           : 35,
     303    "DOLLAR"                : 36,
     304    "PERCENT"               : 37,
     305    "AMPERSAND"             : 38,
     306    "APOSTROPHE"            : 39,
     307    "PARENLEFT"             : 40,
     308    "PARENRIGHT"            : 41,
     309    "ASTERISK"              : 42,
     310    "COMMA"                 : 44,
     311    "PERIOD"                : 46,
     312    "SLASH"                 : 47,
     313    "COLON"                 : 58,
     314    "SEMICOLON"             : 59,
     315    "LESS"                  : 60,
     316    "EQUAL"                 : 61,
     317    "GREATER"               : 62,
     318    "QUESTION"              : 63,
     319    "AT"                    : 64,
     320    "BRACKET_LEFT"          : 91,
     321    "BACKSLASH"             : 92,
     322    "BRACKET_RIGHT"         : 93,
     323    "ASCIICIRCUM"           : 94,
     324    "UNDERSCORE"            : 95,
     325    "GRAVE"                 : 96,
     326    "BRACE_LEFT"            : 123,
     327    "BAR"                   : 124,
     328    "BRACE_RIGHT"           : 125,
     329    "ASCIITILDE"            : 126,
     330    "STERLING"              : 156,
    270331    "SLEEP"                 : 0x5F,
    271332    "OEM_FJ_JISHO"          : 0x92,
    272333    "OEM_FJ_MASSHOU"        : 0x93,
     
    313374    "OEM_FINISH"            : 0xF1,
    314375    "OEM_ENLW"              : 0xF4,
    315376    "OEM_BACKTAB"           : 0xF5,
    316     "ASCIITILDE"            : 65107,        #aka 0x00fe53
     377    "ASCIITILDE"            : 126,
     378    #"ASCIITILDE"            : 65107,        #aka 0x00fe53
    317379    "DEAD_GRAVE"            : 65104,        #aka 0x00fe50
    318380}
    319381
  • xpra/platform/win32/shadow_server.py

     
    393393
    394394    def get_info(self, proto):
    395395        info = GTKServerBase.get_info(self, proto)
    396         info["features.shadow"] = True
    397         info["server.type"] = "Python/gtk2/win32-shadow"
    398         info["server.tray"] = self.tray
    399         info["server.tray-icon"] = self.tray_icon or ""
     396        info.setdefault("features", {})["shadow"] = True
     397        info.setdefault("server", {
     398                                   "type"       : "Python/gtk2/win32-shadow",
     399                                   "tray"       : self.tray,
     400                                   "tray-icon"  :self.tray_icon or ""})
    400401        return info
    401402
    402403
  • xpra/platform/xposix/gui.py

     
    1515traylog = Logger("posix", "menu")
    1616menulog = Logger("posix", "menu")
    1717
    18 from xpra.util import iround
     18from xpra.util import iround, updict
    1919from xpra.gtk_common.gobject_compat import get_xid, is_gtk3
    2020
    2121device_bell = None
     
    456456    s = _get_xsettings()
    457457    if s:
    458458        serial, values = s
    459         i["xsettings.serial"] = serial
     459        xi = {"serial"  : serial}
    460460        for _,name,value,_ in values:
    461             i["xsettings.%s" % name] = value
    462     i["dpi.xsettings"] = _get_xsettings_dpi()
    463     i["dpi.randr"] = _get_randr_dpi()
     461            xi[name] = value
     462        i["xsettings"] = xi
     463    i.setdefault("dpi", {
     464                         "xsettings"    : _get_xsettings_dpi(),
     465                         "randr"        : _get_randr_dpi()
     466                         })
    464467    return i
    465468
    466469
  • xpra/scripts/version.py

     
    88#only imported to make sure we can get hold of a reference to the real "platform" module
    99assert python_platform
    1010from xpra.version_util import get_version_info, get_platform_info, get_host_info
    11 from xpra.util import nonl, pver
     11from xpra.util import print_nested_dict
    1212
    1313
    1414def main():
    15     def print_dict(d):
    16         for k in sorted(d.keys()):
    17             v = d[k]
    18             print("* %s : %s" % (k.ljust(32), nonl(pver(v))))
    1915    from xpra.platform import program_context
    2016    with program_context("Version-Info", "Version Info"):
    2117        print("Build:")
    22         print_dict(get_version_info())
     18        print_nested_dict(get_version_info())
    2319        print("")
    2420        print("Platform:")
    2521        pi = get_platform_info()
     
    2723        if "" in pi:
    2824            pi["sys"] = pi[""]
    2925            del pi[""]
    30         print_dict(pi)
     26        print_nested_dict(pi)
    3127        print("")
    3228        print("Host:")
    3329        d = get_host_info()
     
    3733            d.update(pvinfo())
    3834        except:
    3935            pass
    40         print_dict(d)
     36        print_nested_dict(d)
    4137
    4238
    4339if __name__ == "__main__":
  • xpra/server/gtk_server_base.py

     
    2525                           gtk_main_quit_on_fatal_exceptions_disable)
    2626from xpra.server.server_base import ServerBase
    2727from xpra.gtk_common.gtk_util import get_gtk_version_info, gtk_main
    28 from xpra.util import updict
    2928
    3029
    3130class GTKServerBase(ServerBase):
     
    8281
    8382    def get_ui_info(self, proto, *args):
    8483        info = ServerBase.get_ui_info(self, proto, *args)
    85         info["server.display"] = gtk.gdk.display_get_default().get_name()
    86         info["server.root_window_size"] = self.get_root_window_size()
     84        info.setdefault("server", {}).update({
     85                                              "display"             : gtk.gdk.display_get_default().get_name(),
     86                                              "root_window_size"    : self.get_root_window_size(),
     87                                              })
    8788        return info
    8889
    8990    def do_get_info(self, proto, *args):
    9091        info = ServerBase.do_get_info(self, proto, *args)
    91         updict(info, "server", get_gtk_version_info())
    92         info.update({
    93                      "server.type"      : "Python/gtk-x11",
    94                      "features.randr"   : self.randr})
     92        vi = get_gtk_version_info()
     93        vi["type"] = "Python/gtk-x11"
     94        info.setdefault("server", {}).update(vi)
     95        info.setdefault("features", {})["randr"] = self.randr
    9596        return info
    9697
    9798    def get_root_window_size(self):
  • xpra/server/proxy/proxy_instance_process.py

     
    352352
    353353
    354354    def get_proxy_info(self, proto):
    355         info = {"proxy.version" : local_version}
    356         def upp(d):
    357             updict(info, "proxy", d)
    358         upp(get_server_info())
    359         upp(get_thread_info(proto))
     355        sinfo = {}
     356        sinfo.update(get_server_info())
     357        sinfo.update(get_thread_info(proto))
     358        info = {"proxy" : {
     359                           "version"    : local_version,
     360                           ""           : sinfo,
     361                           }
     362                }
    360363        info.update(self.get_encoder_info())
    361364        return info
    362365
  • xpra/server/proxy/proxy_server.py

     
    257257                i = 0
    258258                for p,v in self.processes.items():
    259259                    d,_ = v
    260                     info["proxy[%s].display" % i] = d
    261                     info["proxy[%s].live" % i] = p.is_alive()
    262                     info["proxy[%s].pid" % i] = p.pid
     260                    info[i] = {"display"    : d,
     261                               "live"       : p.is_alive(),
     262                               "pid"        : p.pid}
    263263                    i += 1
    264264                info["proxies"] = len(self.processes)
    265265        return info
  • xpra/server/server_base.py

     
    932932            self.send_screenshot(proto)
    933933            return
    934934        if c.boolget("info_request", False):
    935             self.send_hello_info(proto)
     935            flatten = not c.boolget("info-namespace", False)
     936            self.send_hello_info(proto, flatten)
    936937            return
    937938
    938939        detach_request  = c.boolget("detach_request", False)
     
    17341735                ss.send_info_response(info)
    17351736            self.get_all_info(info_callback, proto, *packet[2:])
    17361737
    1737     def send_hello_info(self, proto):
    1738         log.info("processing info request from %s", proto._conn)
    1739         self.get_all_info(self.do_send_info, proto, self._id_to_window.keys())
     1738    def send_hello_info(self, proto, flatten=True):
     1739        start = time.time()
     1740        def cb(proto, info):
     1741            self.do_send_info(proto, info, flatten)
     1742            end = time.time()
     1743            log.info("processed info request from %s in %ims", proto._conn, (end-start)*1000)
     1744        self.get_all_info(cb, proto, self._id_to_window.keys())
    17401745
    17411746    def get_ui_info(self, proto, wids=None, *args):
    17421747        """ info that must be collected from the UI thread
    17431748            (ie: things that query the display)
    17441749        """
    1745         info = {"server.max_desktop_size" : self.get_max_screen_size()}
     1750        info = {"server"    : {"max_desktop_size"   : self.get_max_screen_size()}}
    17461751        if self.keyboard_config:
    1747             info["state.modifiers"] = self.keyboard_config.get_current_mask()
     1752            info["keyboard"] = {"state" : {"modifiers"          : self.keyboard_config.get_current_mask()}}
    17481753        #window info:
    17491754        self.add_windows_info(info, wids)
    17501755        return info
     
    17631768        if not wids:
    17641769            wids = self._id_to_window.keys()
    17651770        log("info-request: sources=%s, wids=%s", sources, wids)
    1766         ei = self.do_get_info(proto, sources, wids)
    1767         info.update(ei)
    1768         updict(info, "dpi", {
     1771        info.update(self.do_get_info(proto, sources, wids))
     1772        info.setdefault("dpi", {}).update({
    17691773                             "default"      : self.default_dpi,
    17701774                             "value"        : self.dpi,
    17711775                             "x"            : self.xdpi,
    17721776                             "y"            : self.ydpi
    17731777                             })
    1774         updict(info, "antialias", self.antialias)
    1775         info["cursor.size"] = self.cursor_size
    1776         log("get_info took %.1fms", 1000.0*(time.time()-start))
     1778        info.setdefault("antialias", {}).update(self.antialias)
     1779        info.setdefault("cursor", {}).update({"size" : self.cursor_size})
     1780        log("ServerBase.get_info took %.1fms", 1000.0*(time.time()-start))
    17771781        return info
    17781782
    17791783
     
    18051809             "bell"             : self.bell,
    18061810             "notifications"    : self.notifications_forwarder is not None,
    18071811             "sharing"          : self.sharing,
    1808              "pulseaudio"       : self.pulseaudio,
    1809              "pulseaudio.command" : self.pulseaudio_command,
     1812             "pulseaudio"       : {
     1813                                   ""           : self.pulseaudio,
     1814                                   "command"    : self.pulseaudio_command,
     1815                                   },
    18101816             "dbus_proxy"       : self.supports_dbus_proxy,
    18111817             "rpc-types"        : self.rpc_handlers.keys(),
    18121818             "clipboard"        : self.supports_clipboard,
     
    18311837    def get_keyboard_info(self):
    18321838        info = {
    18331839             "sync"             : self.keyboard_sync,
    1834              "repeat.delay"     : self.key_repeat_delay,
    1835              "repeat.interval"  : self.key_repeat_interval,
     1840             "repeat"           : {
     1841                                   "delay"      : self.key_repeat_delay,
     1842                                   "interval"   : self.key_repeat_interval,
     1843                                   },
    18361844             "keys_pressed"     : self.keys_pressed.values(),
    18371845             "modifiers"        : self.xkbmap_mod_meanings}
    1838         if self.keyboard_config:
    1839             for k,v in self.keyboard_config.get_info().items():
    1840                 if v is not None:
    1841                     info[k] = v
     1846        kc = self.keyboard_config
     1847        if kc:
     1848            info.update(kc.get_info())
    18421849        return info
    18431850
    18441851    def get_clipboard_info(self):
     
    18511858                "virtual-video-devices"     : self.virtual_video_devices}
    18521859
    18531860    def do_get_info(self, proto, server_sources=None, window_ids=None):
    1854         info = {"server.python.version" : python_platform.python_version()}
     1861        start = time.time()
     1862        info = {"server" : {"python" : {"version" : python_platform.python_version()}}}
    18551863
    1856         def up(prefix, d, suffix=""):
    1857             updict(info, prefix, d, suffix)
     1864        def up(prefix, d):
     1865            info[prefix] = d
    18581866
    18591867        up("webcam",    self.get_webcam_info())
    18601868        up("file",      self.get_file_transfer_info())
     
    18641872        up("clipboard", self.get_clipboard_info())
    18651873        up("keyboard",  self.get_keyboard_info())
    18661874        up("encodings", self.get_encoding_info())
    1867         up("encoding",  codec_versions, "version")
     1875        for k,v in codec_versions.items():
     1876            info.setdefault("encoding", {}).setdefault(k, {})["version"] = v
    18681877        # csc and video encoders:
    1869         info.update(getVideoHelper().get_info())
     1878        up("video",     getVideoHelper().get_info())
    18701879
    1871         info["windows"] = len([window for window in list(self._id_to_window.values()) if window.is_managed()])
     1880        info.setdefault("state", {})["windows"] = len([window for window in list(self._id_to_window.values()) if window.is_managed()])
    18721881        # other clients:
    1873         info["clients"] = len([p for p in self._server_sources.keys() if p!=proto])
    1874         info["clients.unauthenticated"] = len([p for p in self._potential_protocols if ((p is not proto) and (p not in self._server_sources.keys()))])
     1882        info["clients"] = {""                   : len([p for p in self._server_sources.keys() if p!=proto]),
     1883                           "unauthenticated"    : len([p for p in self._potential_protocols if ((p is not proto) and (p not in self._server_sources.keys()))])}
    18751884        #find the server source to report on:
    18761885        n = len(server_sources or [])
    18771886        if n==1:
     
    18791888            up("client", ss.get_info())
    18801889            info.update(ss.get_window_info(window_ids))
    18811890        elif n>1:
     1891            cinfo = {}
    18821892            for i, ss in enumerate(server_sources):
    1883                 up("client[%i]" % i, ss.get_info())
    1884                 wi = ss.get_window_info(window_ids)
    1885                 up("client[%i]" % i, wi)
    1886                 #this means that the last source overrides previous ones
    1887                 #(bad decision was made on the namespace for this..)
    1888                 info.update(wi)
     1893                sinfo = ss.get_info()
     1894                sinfo.update(ss.get_window_info(window_ids))
     1895                cinfo[i] = sinfo
     1896            up("client", cinfo)
     1897        log("ServerBase.do_get_info took %ims", (time.time()-start)*1000)
    18891898        return info
    18901899
    18911900    def add_windows_info(self, info, window_ids):
     1901        winfo = info.setdefault("window", {})
    18921902        for wid, window in self._id_to_window.items():
    18931903            if window_ids is not None and wid not in window_ids:
    18941904                continue
    1895             for k,v in self.get_window_info(window).items():
    1896                 wp = "window[%s]." % wid
    1897                 info[wp + k] = v
     1905            winfo.setdefault(wid, {}).update(self.get_window_info(window))
    18981906
    18991907    def get_window_info(self, window):
    19001908        from xpra.server.source import make_window_metadata
     
    19071915        for prop in window.get_internal_property_names():
    19081916            metadata = make_window_metadata(window, prop)
    19091917            info.update(metadata)
    1910         if "size-constraints" in info:
    1911             size_constraints = info["size-constraints"]
    1912             del info["size-constraints"]
    1913             for k,v in size_constraints.items():
    1914                 info["size-constraints.%s" % k] = v
    19151918        info.update({
    19161919             "override-redirect"    : window.is_OR(),
    19171920             "tray"                 : window.is_tray(),
  • xpra/server/server_core.py

     
    4040from xpra.make_thread import make_thread
    4141from xpra.scripts.fdproxy import XpraProxy
    4242from xpra.server.control_command import ControlError, HelloCommand, HelpCommand, DebugControl
    43 from xpra.util import csv, typedict, updict, repr_ellipsized, dump_all_frames, \
     43from xpra.util import csv, merge_dicts, typedict, notypedict, flatten_dict, repr_ellipsized, dump_all_frames, \
    4444        SERVER_SHUTDOWN, SERVER_UPGRADE, LOGIN_TIMEOUT, DONE, PROTOCOL_ERROR, SERVER_ERROR, VERSION_ERROR, CLIENT_REQUEST
    4545
    4646main_thread = threading.current_thread()
     
    5151
    5252def get_server_info():
    5353    #this function is for non UI thread info
    54     info = {}
     54    info = {
     55            "platform"  : get_platform_info(),
     56            "build"     : get_version_info_full()
     57            }
    5558    info.update(get_host_info())
    56     def up(prefix, d):
    57         updict(info, prefix, d)
    58     up("platform",  get_platform_info())
    59     up("build",     get_version_info_full())
    6059    return info
    6160
    6261def get_thread_info(proto=None, protocols=[]):
     
    7776    if w:
    7877        thread_ident[w.ident] = "worker"
    7978
     79    it = {}
    8080    #threads used by the "info" client:
    8181    for i, t in enumerate(info_threads):
    82         info["info[%s]" % i] = t.getName()
     82        it[i] = t.getName()
    8383        thread_ident[t.ident] = t.getName()
     84    info["info"] = it
    8485    for p in protocols:
    8586        try:
    8687            threads = p.get_threads()
     
    8990        except:
    9091            pass
    9192    #all non-info threads:
     93    anit = {}
    9294    for i, t in enumerate((x for x in threading.enumerate() if x not in info_threads)):
    93         info[str(i)] = t.getName()
     95        anit[i] = t.getName()
     96    info["thread"] = anit
    9497    #platform specific bits:
    9598    try:
    9699        from xpra.platform.info import get_sys_info
     
    104107                return ""
    105108            return x
    106109        frames = sys._current_frames()
     110        fi = {}
    107111        for i,frame_pair in enumerate(frames.items()):
    108112            stack = traceback.extract_stack(frame_pair[1])
    109             info["frame[%s].thread" % i] = thread_ident.get(frame_pair[0], "unknown")
    110113            #sanitize stack to prevent None values (which cause encoding errors with the bencoder)
    111114            sanestack = []
    112115            for e in stack:
    113116                sanestack.append(tuple([nn(x) for x in e]))
    114117            info["frame[%s].stack" % i] = sanestack
     118            fi[i] = {"thread"   : thread_ident.get(frame_pair[0], "unknown"),
     119                     "stack"    : sanestack}
     120        info["frame"] = fi
    115121    except Exception as e:
    116122        log.error("failed to get frame info: %s", e)
    117123    return info
     
    647653        auth_caps = self.verify_hello(proto, c)
    648654        if auth_caps is not False:
    649655            if c.boolget("info_request", False):
    650                 self.send_hello_info(proto)
     656                flatten = not c.boolget("info-namespace", False)
     657                self.send_hello_info(proto, flatten)
    651658                return
    652659            command_req = c.strlistget("command_request")
    653660            if len(command_req)>0:
     
    849856        now = time.time()
    850857        capabilities = get_network_caps()
    851858        if source.wants_versions:
    852             capabilities.update(get_server_info())
     859            capabilities.update(flatten_dict(get_server_info()))
    853860        capabilities.update({
    854861                        "version"               : xpra.__version__,
    855862                        "start_time"            : int(self.start_time),
     
    870877        return capabilities
    871878
    872879
    873     def send_hello_info(self, proto):
     880    def send_hello_info(self, proto, flatten=True):
    874881        #Note: this can be overriden in subclasses to pass arguments to get_ui_info()
    875882        #(ie: see server_base)
    876883        log.info("processing info request from %s", proto._conn)
    877         self.get_all_info(self.do_send_info, proto)
     884        def cb(proto, info):
     885            self.do_send_info(proto, info, flatten)
     886        self.get_all_info(cb, proto)
    878887
    879     def do_send_info(self, proto, info):
     888    def do_send_info(self, proto, info, flatten):
     889        if flatten:
     890            info = flatten_dict(info)
     891        else:
     892            info = notypedict(info)
    880893        proto.send_now(("hello", info))
    881894
    882895    def get_all_info(self, callback, proto=None, *args):
     896        start = time.time()
    883897        ui_info = self.get_ui_info(proto, *args)
     898        end = time.time()
     899        log("get_all_info: ui info collected in %ims", (end-start)*1000)
    884900        def in_thread(*args):
     901            start = time.time()
    885902            #this runs in a non-UI thread
    886903            try:
    887904                info = self.get_info(proto, *args)
    888                 ui_info.update(info)
     905                merge_dicts(ui_info, info)
    889906            except Exception as e:
    890907                log.error("error during info collection: %s", e, exc_info=True)
     908            end = time.time()
     909            log("get_all_info: non ui info collected in %ims", (end-start)*1000)
    891910            callback(proto, ui_info)
    892911        make_thread(in_thread, "Info", daemon=True).start()
    893912
     
    899918        return get_thread_info(proto)
    900919
    901920    def get_info(self, proto, *args):
     921        start = time.time()
    902922        #this function is for non UI thread info
    903923        info = {}
    904924        def up(prefix, d):
    905             updict(info, prefix, d)
     925            info[prefix] = d
    906926        filtered_env = os.environ.copy()
    907927        if filtered_env.get('XPRA_PASSWORD'):
    908928            filtered_env['XPRA_PASSWORD'] = "*****"
     
    928948                "executable"        : sys.executable,
    929949                })
    930950        if self.session_name:
    931             info["session.name"] = self.session_name
     951            info["session"] = {"name" : self.session_name}
    932952        if self.child_reaper:
    933953            info.update(self.child_reaper.get_info())
     954        end = time.time()
     955        log("ServerCore.get_info took %ims", (end-start)*1000)
    934956        return info
    935957
    936958    def process_packet(self, proto, packet):
  • xpra/server/source.py

     
    3333from xpra.server.window.window_video_source import WindowVideoSource
    3434from xpra.server.window.window_source import WindowSource
    3535from xpra.server.window.batch_config import DamageBatchConfig
    36 from xpra.simple_stats import add_list_stats, std_unit
     36from xpra.simple_stats import get_list_stats, std_unit
    3737from xpra.codecs.video_helper import getVideoHelper
    3838from xpra.codecs.codec_constants import video_spec
    3939from xpra.net import compression
     
    4141from xpra.make_thread import make_thread
    4242from xpra.os_util import platform_name, Queue, get_machine_id, get_user_uuid
    4343from xpra.server.background_worker import add_work_item
    44 from xpra.util import csv, std, typedict, updict, get_screen_info, CLIENT_PING_TIMEOUT, WORKSPACE_UNSET, DEFAULT_METADATA_SUPPORTED
     44from xpra.util import csv, std, typedict, updict, flatten_dict, notypedict, get_screen_info, CLIENT_PING_TIMEOUT, WORKSPACE_UNSET, DEFAULT_METADATA_SUPPORTED
    4545
    4646
    4747NOYIELD = os.environ.get("XPRA_YIELD") is None
     
    371371        self.client_release = None
    372372        self.client_proxy = False
    373373        self.auto_refresh_delay = 0
     374        self.info_namespace = False
    374375        self.send_cursors = False
    375376        self.send_bell = False
    376377        self.send_notifications = False
     
    665666        self.lzo = c.boolget("lzo", False) and compression.use_lzo
    666667        self.send_windows = self.ui_client and c.boolget("windows", True)
    667668        self.pointer_grabs = c.boolget("pointer.grabs")
     669        self.info_namespace = c.boolget("info-namespace")
    668670        self.send_cursors = self.send_windows and c.boolget("cursors")
    669671        self.send_bell = c.boolget("bell")
    670672        self.send_notifications = c.boolget("notifications")
     
    11251127            cinfo = ""
    11261128            if ss:
    11271129                try:
    1128                     encoder_latency = ss.last_info.get("queue.cur", 0)
     1130                    encoder_latency = ss.last_info.get("queue", {}).get("cur", 0)
    11291131                    avsynclog("server side queue level: %s", encoder_latency)
    11301132                    from xpra.sound.gstreamer_util import ENCODER_LATENCY
    11311133                    encoder_latency = ENCODER_LATENCY.get(ss.codec, 0)
     
    13501352                "suspended"         : self.suspended,
    13511353                }
    13521354        if self.desktop_size_unscaled:
    1353             info["desktop_size.unscaled"] = self.desktop_size_unscaled
     1355            info["desktop_size"] = {"unscaled" : self.desktop_size_unscaled}
    13541356
    13551357        def addattr(k, name):
    13561358            v = getattr(self, name)
     
    13581360                info[k] = v
    13591361        for x in ("type", "platform", "release", "machine", "processor", "proxy"):
    13601362            addattr(x, "client_"+x)
     1363        #remove very large item:
     1364        ieo = dict(self.icons_encoding_options)
     1365        try:
     1366            del ieo["default.icons"]
     1367        except:
     1368            pass
    13611369        #encoding:
    13621370        info.update({
    1363              "encodings"         : self.encodings,
    1364              "encodings.core"    : self.core_encodings,
    1365              "encoding.default"  : self.default_encoding or ""
    1366              })
    1367         def up(prefix, d):
    1368             updict(info, prefix, d)
    1369         up("encoding",      self.default_encoding_options)
    1370         up("encoding",      self.encoding_options)
    1371         up("icons",         self.icons_encoding_options)
    1372         up("connection",    self.protocol.get_info())
    1373         up("av-sync",       {"client.delay"         : self.av_sync_delay,
    1374                              "total"                : self.av_sync_delay_total,
    1375                              "delta"                : self.av_sync_delta})
     1371                     "encodings"        : {
     1372                                           ""      : self.encodings,
     1373                                           "core"  : self.core_encodings
     1374                                           },
     1375                     "icons"            : ieo,
     1376                     "connection"       : self.protocol.get_info(),
     1377                     "av-sync"          : {
     1378                                           "client"     : {"delay"  : self.av_sync_delay},
     1379                                           "total"      : self.av_sync_delay_total,
     1380                                           "delta"      : self.av_sync_delta,
     1381                                           },
     1382                     })
     1383        einfo = {"default"      : self.default_encoding or ""}
     1384        einfo.update(self.default_encoding_options)
     1385        einfo.update(self.encoding_options)
     1386        info.setdefault("encoding", {}).update(einfo)
    13761387        if self.window_frame_sizes:
    1377             up("window.frame-sizes", self.window_frame_sizes)
     1388            info.setdefault("window", {}).update({"frame-sizes" : self.window_frame_sizes})
    13781389        if self.window_filters:
    13791390            i = 0
     1391            finfo = {}
    13801392            for uuid, f in self.window_filters:
    13811393                if uuid==self.uuid:
    1382                     info["window-filter[%i]" % i] = str(f)
     1394                    finfo[i] = str(f)
    13831395                    i += 1
     1396            info["window-filter"] = finfo
    13841397        info.update(self.get_sound_info())
    13851398        info.update(self.get_features_info())
    13861399        info.update(self.get_screen_info())
     
    14201433            if prop is None:
    14211434                return {"state" : "inactive"}
    14221435            return prop.get_info()
    1423         info = {}
     1436        info = {
     1437                "speaker"       : sound_info(self.supports_speaker, self.sound_source),
     1438                "microphone"    : sound_info(self.supports_microphone, self.sound_sink),
     1439                }
    14241440        for prop in ("pulseaudio_id", "pulseaudio_server"):
    14251441            v = getattr(self, prop)
    14261442            if v is not None:
    14271443                info[prop] = v
    1428         for k,v in sound_info(self.supports_speaker, self.sound_source).items():
    1429             info["speaker.%s" % k] = v
    1430         for k,v in sound_info(self.supports_microphone, self.sound_sink).items():
    1431             info["microphone.%s" % k] = v
    14321444        return info
    14331445
    14341446    def get_window_info(self, window_ids=[]):
     
    14351447        """
    14361448            Adds encoding and window specific information
    14371449        """
    1438         info = {
    1439             "damage.compression_queue.size.current" : self.encode_work_queue.qsize(),
    1440             "damage.packet_queue.size.current"      : len(self.packet_queue),
    1441             }
    1442         qpixels = [x[2] for x in list(self.packet_queue)]
    1443         add_list_stats(info, "packet_queue_pixels",  qpixels)
    1444         if len(qpixels)>0:
    1445             info["packet_queue_pixels.current"] = qpixels[-1]
    1446 
     1450        pqpixels = [x[2] for x in list(self.packet_queue)]
     1451        pqpi = get_list_stats(pqpixels)
     1452        if len(pqpixels)>0:
     1453            pqpi["current"] = pqpixels[-1]
     1454        info = {"damage"    : {
     1455                               "compression_queue"      : {"size" : {"current" : self.encode_work_queue.qsize()}},
     1456                               "packet_queue"           : {"size" : {"current" : len(self.packet_queue)}},
     1457                               "packet_queue_pixels"    : pqpi,
     1458                               },
     1459                "batch"     : self.global_batch_config.get_info(),
     1460                }
    14471461        info.update(self.statistics.get_info())
    14481462
    14491463        if len(window_ids)>0:
     
    14501464            total_pixels = 0
    14511465            total_time = 0.0
    14521466            in_latencies, out_latencies = [], []
     1467            winfo = {}
    14531468            for wid in window_ids:
    14541469                ws = self.window_sources.get(wid)
    14551470                if ws is None:
    14561471                    continue
    14571472                #per-window source stats:
    1458                 updict(info, "window[%i]" % wid, ws.get_info())
     1473                winfo[wid] = ws.get_info()
    14591474                #collect stats for global averages:
    14601475                for _, _, pixels, _, _, encoding_time in list(ws.statistics.encoding_stats):
    14611476                    total_pixels += pixels
     
    14621477                    total_time += encoding_time
    14631478                in_latencies += [x*1000 for _, _, _, x in list(ws.statistics.damage_in_latency)]
    14641479                out_latencies += [x*1000 for _, _, _, x in list(ws.statistics.damage_out_latency)]
     1480            info["window"] = winfo
    14651481            v = 0
    14661482            if total_time>0:
    14671483                v = int(total_pixels / total_time)
    1468             info["encoding.pixels_encoded_per_second"] = v
    1469             add_list_stats(info, "damage.in_latency",  in_latencies, show_percentile=[9])
    1470             add_list_stats(info, "damage.out_latency",  out_latencies, show_percentile=[9])
    1471         updict(info, "batch", self.global_batch_config.get_info())
     1484            info.setdefault("encoding", {})["pixels_encoded_per_second"] = v
     1485            dinfo = info.setdefault("damage", {})
     1486            dinfo["in_latency"] = get_list_stats(in_latencies, show_percentile=[9])
     1487            dinfo["out_latency"] = get_list_stats(out_latencies, show_percentile=[9])
    14721488        return info
    14731489
    14741490
    14751491    def send_info_response(self, info):
    1476         self.send("info-response", info)
     1492        if self.info_namespace:
     1493            v = notypedict(info)
     1494        else:
     1495            v = flatten_dict(info)
     1496        self.send("info-response", v)
    14771497
    14781498
    14791499    def send_server_event(self, *args):
  • xpra/server/source_stats.py

     
    1414log = Logger("stats")
    1515
    1616from xpra.server.cystats import logp, calculate_time_weighted_average, calculate_for_target, queue_inspect  #@UnresolvedImport
    17 from xpra.simple_stats import add_list_stats
     17from xpra.simple_stats import get_list_stats
    1818
    1919NRECS = 500
    2020
     
    132132        return factors
    133133
    134134    def get_client_info(self):
     135        latencies = [x*1000 for (_, _, _, x) in list(self.client_latency)]
    135136        info = {
    136             "connection.mmap_bytecount"  : self.mmap_bytes_sent}
     137                "connection"        : {
     138                                       "mmap_bytecount"  : self.mmap_bytes_sent
     139                                       },
     140                "latency"           : get_list_stats(latencies),
     141                "server"            : {
     142                                       "ping_latency"   : get_list_stats(1000.0*x for _, x in list(self.server_ping_latency)),
     143                                       },
     144                "client"            : {
     145                                       "ping_latency"   : get_list_stats(1000.0*x for _, x in list(self.client_ping_latency)),
     146                                       },
     147                }
    137148        if self.min_client_latency is not None:
    138             info["latency.absmin"] = int(self.min_client_latency*1000)
    139         latencies = [x*1000 for (_, _, _, x) in list(self.client_latency)]
    140         add_list_stats(info, "latency",  latencies)
    141         add_list_stats(info, "server.ping_latency", [1000.0*x for _, x in list(self.server_ping_latency)])
    142         add_list_stats(info, "client.ping_latency", [1000.0*x for _, x in list(self.client_ping_latency)])
     149            info["latency"] = {"absmin" : int(self.min_client_latency*1000)}
    143150        return info
    144151
    145152
    146153    def get_info(self):
    147         info = {
    148             "damage.events"                     : self.damage_events_count,
    149             "damage.packets_sent"               : self.packet_count,
    150             "encoding.decode_errors"            : self.decode_errors,
     154        cwqsizes = [x for _,x in list(self.compression_work_qsizes)]
     155        pqsizes = [x for _,x in list(self.packet_qsizes)]
     156        info = {"damage" : {
     157                            "events"        : self.damage_events_count,
     158                            "packets_sent"  : self.packet_count,
     159                            "data_queue"    : {
     160                                               "size"   : get_list_stats(cwqsizes),
     161                                               },
     162                            "packet_queue"  : {
     163                                               "size"   : get_list_stats(pqsizes),
     164                                               },
     165                            },
     166                "encoding" : {"decode_errors"   : self.decode_errors},
    151167            }
    152         qsizes = [x for _,x in list(self.compression_work_qsizes)]
    153         add_list_stats(info, "damage.data_queue.size",  qsizes)
    154         qsizes = [x for _,x in list(self.packet_qsizes)]
    155         add_list_stats(info, "damage.packet_queue.size",  qsizes)
    156 
    157168        #client pixels per second:
    158169        now = time.time()
    159170        time_limit = now-30             #ignore old records (30s)
     
    174185        log("total_time=%s, total_pixels=%s", total_time, total_pixels)
    175186        if total_time>0:
    176187            pixels_decoded_per_second = int(total_pixels *1000*1000 / total_time)
    177             info["encoding.pixels_decoded_per_second"] = pixels_decoded_per_second
     188            info["encoding"]["pixels_decoded_per_second"] = pixels_decoded_per_second
    178189        if start_time:
    179190            elapsed = now-start_time
    180191            pixels_per_second = int(total_pixels/elapsed)
    181             info.update({
    182                      "encoding.pixels_per_second"       : pixels_per_second,
    183                      "encoding.regions_per_second"      : int(len(region_sizes)/elapsed),
    184                      "encoding.average_region_size"     : int(total_pixels/len(region_sizes))})
     192            info.setdefault("encoding", {}).update({
     193                                                    "pixels_per_second"     : pixels_per_second,
     194                                                    "regions_per_second"    : int(len(region_sizes)/elapsed),
     195                                                    "average_region_size"   : int(total_pixels/len(region_sizes)),
     196                                                    })
    185197        return info
  • xpra/server/window/batch_config.py

     
    1212NRECS = 100
    1313
    1414from collections import deque
    15 from xpra.simple_stats import add_list_stats
     15from xpra.simple_stats import get_list_stats
    1616
    1717import os
    1818
     
    7474            "locked"            : self.locked}
    7575        if len(self.last_delays)>0:
    7676            batch_delays = [x for _,x in list(self.last_delays)]
    77             add_list_stats(info, "delay", batch_delays)
     77            info["delay"] = get_list_stats(batch_delays)
    7878        if len(self.last_actual_delays)>0:
    7979            batch_delays = [x for _,x in list(self.last_actual_delays)]
    80             add_list_stats(info, "actual_delay", batch_delays, show_percentile=[9])
     80            info["actual_delays"] = get_list_stats(batch_delays, show_percentile=[9])
    8181        for name, details, factor, weight in self.factors:
    8282            info[name] = (int(100.0*factor), int(100.0*weight))
    8383            for k,v in details.items():
  • xpra/server/window/window_source.py

     
    4747LOG_THEME_DEFAULT_ICONS = os.environ.get("XPRA_LOG_THEME_DEFAULT_ICONS", "0")=="1"
    4848
    4949
    50 from xpra.util import updict
    5150from xpra.os_util import StringIOClass, memoryview_to_bytes
    5251from xpra.server.window.window_stats import WindowPerformanceStatistics
    5352from xpra.server.window.batch_config import DamageBatchConfig
    54 from xpra.simple_stats import add_list_stats
     53from xpra.simple_stats import get_list_stats
    5554from xpra.server.window.batch_delay_calculator import calculate_batch_delay, get_target_speed, get_target_quality
    5655from xpra.server.cystats import time_weighted_average   #@UnresolvedImport
    5756from xpra.server.window.region import rectangle, add_rectangle, remove_rectangle, merge_all   #@UnresolvedImport
     
    316315        """
    317316            Add window specific stats
    318317        """
     318        #"encoding" info:
     319        einfo = self.get_quality_speed_info()
     320        einfo.update({
     321                      ""                    : self.encoding,
     322                      "lossless_threshold"  : {
     323                                               "base"           : self._lossless_threshold_base,
     324                                               "pixel_boost"    : self._lossless_threshold_pixel_boost
     325                                               },
     326                      })
     327        try:
     328            #ie: get_strict_encoding -> "strict_encoding"
     329            einfo["selection"] = self.get_best_encoding.__name__.replace("get_", "")
     330        except:
     331            pass
     332
     333        #"encodings" info:
     334        esinfo = {
     335                  ""                : self.encodings,
     336                  "core"            : self.core_encodings,
     337                  "auto-refresh"    : self.client_refresh_encodings,
     338                  }
     339        larm = self.last_auto_refresh_message
     340        if larm:
     341            esinfo = {"auto-refresh"    : {
     342                                           "last-event" : {
     343                                                           "elapsed"    : int(1000*(time.time()-larm[0])),
     344                                                           "message"    : larm[1],
     345                                                           }
     346                                           }
     347                      }
     348
     349        now = time.time()
     350        buckets_info = {}
     351        for i,x in enumerate(self.delta_pixel_data):
     352            if x:
     353                w, h, pixel_format, coding, store, buflen, _, hits, last_used = x
     354                buckets_info[i] = w, h, pixel_format, coding, store, buflen, hits, int((now-last_used)*1000)
     355        #remove large default dict:
    319356        info = {
    320357                "dimensions"            : self.window_dimensions,
    321                 "encoding"              : self.encoding,
    322                 "suspended"             : self.suspended or False
    323                 }
    324         def up(prefix, d):
    325             updict(info, prefix, d)
    326 
    327         up("av-sync",       {"current"  : self.av_sync_delay,
    328                              "target"   : self.av_sync_delay_target})
    329         #heuristics
    330         up("encoding.lossless_threshold", {
    331                 "base"                  : self._lossless_threshold_base,
    332                 "pixel_boost"           : self._lossless_threshold_pixel_boost})
    333         up("encoding", {
     358                "suspended"             : self.suspended or False,
     359                "av-sync"               : {
     360                                           "current"    : self.av_sync_delay,
     361                                           "target"     : self.av_sync_delay_target
     362                                           },
     363                "encoding"              : einfo,
     364                "encodings"             : esinfo,
    334365                "rgb_threshold"         : self._rgb_auto_threshold,
    335366                "mmap"                  : bool(self._mmap) and (self._mmap_size>0),
    336367                "last_used"             : self.encoding_last_used or "",
    337368                "full-frames-only"      : self.full_frames_only,
    338369                "supports-transparency" : self.supports_transparency,
    339                 "delta"                 : self.supports_delta,
    340                 "delta.buckets"         : self.delta_buckets,
    341                 })
     370                "delta"                 : {""               : self.supports_delta,
     371                                           "buckets"        : self.delta_buckets,
     372                                           "bucket"         : buckets_info,
     373                                           },
     374                "property"              : self.get_property_info(),
     375                "batch"                 : self.batch_config.get_info(),
     376                "soft-timeout"          : {
     377                                           "expired"        : self.soft_expired,
     378                                           "max"            : self.max_soft_expired,
     379                                           },
     380                 "rgb_formats"          : self.rgb_formats,
     381                 #"icons"                : self.icons_encoding_options,
     382                 }
    342383        if self.pixel_format:
    343384            info["pixel-format"] = self.pixel_format
    344         now = time.time()
    345         for i,x in enumerate(self.delta_pixel_data):
    346             if x:
    347                 w, h, pixel_format, coding, store, buflen, _, hits, last_used = x
    348                 info["encoding.delta.bucket[%s]" % i] = w, h, pixel_format, coding, store, buflen, hits, int((now-last_used)*1000)
    349         up("encoding",  self.get_quality_speed_info())
    350         try:
    351             #ie: get_strict_encoding -> "strict_encoding"
    352             info["encoding.selection"] = self.get_best_encoding.__name__.replace("get_", "")
    353         except:
    354             pass
    355         up("property",  self.get_property_info())
    356         up("batch",     self.batch_config.get_info())
    357         up("soft-timeout", {
    358                             "expired"       : self.soft_expired,
    359                             "max"           : self.max_soft_expired
    360                             })
    361         up("encodings", {
    362                  ""                     : self.encodings,
    363                  "core"                 : self.core_encodings,
    364                  "auto-refresh"         : self.client_refresh_encodings,
    365                  "rgb_formats"          : self.rgb_formats,
    366                  })
    367         larm = self.last_auto_refresh_message
    368         if larm:
    369             up("encodings.auto-refresh.last-event", {
    370                 "elapsed"               : int(1000*(time.time()-larm[0])),
    371                 "message"               : larm[1],
    372                                                      })
    373         up("icons", self.icons_encoding_options)
    374385        idata = self.window_icon_data
    375386        if idata:
    376387            pixel_data, stride, w, h = idata
    377             up("icon", {
    378                     "width"     : w,
    379                     "height"    : h,
    380                     "stride"    : stride,
    381                     "bytes"     : len(pixel_data)
    382                                })
    383 
     388            info["icon"] = {
     389                            "width"     : w,
     390                            "height"    : h,
     391                            "stride"    : stride,
     392                            "bytes"     : len(pixel_data)
     393                            }
    384394        info.update(self.statistics.get_info())
    385395        return info
    386396
    387397    def get_quality_speed_info(self):
    388398        info = {}
    389         def add_last_rec_info(prefix, recs):
    390             #must make a list to work on (again!)
    391             l = list(recs)
    392             if len(l)>0:
    393                 _, descr, _ = l[-1]
    394                 for k,v in descr.items():
    395                     info[prefix+"."+k] = v
    396         quality_list = self._encoding_quality
    397         if quality_list:
    398             qp = "quality"
    399             add_list_stats(info, qp, [x for _, _, x in list(quality_list)])
    400             add_last_rec_info(qp, quality_list)
    401         speed_list = self._encoding_speed
    402         if speed_list:
    403             sp = "speed"
    404             add_list_stats(info, sp, [x for _, _, x in list(speed_list)])
    405             add_last_rec_info(sp, speed_list)
     399        def add_list_info(prefix, v):
     400            if not v:
     401                return
     402            l = list(v)
     403            if len(l)==0:
     404                return
     405            li = get_list_stats(x for _, _, x in l)
     406            #last record
     407            _, descr, _ = l[-1]
     408            li.update(descr)
     409            info[prefix] = li
     410        add_list_info("quality", self._encoding_quality)
     411        add_list_info("speed", self._encoding_speed)
    406412        return info
    407413
    408414    def get_property_info(self):
  • xpra/server/window/window_stats.py

     
    1919log = Logger("stats")
    2020
    2121from collections import deque
    22 from xpra.simple_stats import add_list_stats, add_weighted_list_stats
     22from xpra.simple_stats import get_list_stats, get_weighted_list_stats
    2323from xpra.util import engs, csv
    2424from xpra.server.cystats import (logp,      #@UnresolvedImport
    2525    calculate_time_weighted_average,        #@UnresolvedImport
     
    153153
    154154
    155155    def get_info(self):
    156         info = {
    157                 "damage.events"         : self.damage_events_count,
    158                 "damage.packets_sent"   : self.packet_count}
     156        info = {"damage"    : {"events"         : self.damage_events_count,
     157                               "packets_sent"   : self.packet_count}
     158                }
    159159        #encoding stats:
    160160        if len(self.encoding_stats)>0:
    161161            estats = list(self.encoding_stats)
    162162            encodings_used = [x[1] for x in estats]
    163             def add_compression_stats(enc_stats, suffix=""):
     163            def add_compression_stats(enc_stats, encoding=None):
    164164                comp_ratios_pct = []
    165165                comp_times_ns = []
    166166                total_pixels = 0
     
    172172                        comp_times_ns.append((1000.0*1000*1000*compression_time/pixels, pixels))
    173173                        total_pixels += pixels
    174174                        total_time += compression_time
    175                 add_weighted_list_stats(info, "encoding.ratio_pct"+suffix, comp_ratios_pct)
    176                 add_weighted_list_stats(info, "encoding.pixels_per_ns"+suffix, comp_times_ns)
     175                einfo = info.setdefault("encoding", {})
     176                if encoding:
     177                    einfo = einfo.setdefault(encoding, {})
     178                einfo["ratio_pct"] = get_weighted_list_stats(comp_ratios_pct)
     179                einfo["pixels_per_ns"] = get_weighted_list_stats(comp_times_ns)
    177180                if total_time>0:
    178                     info["encoding.pixels_encoded_per_second"+suffix] = int(total_pixels / total_time)
     181                    einfo["pixels_encoded_per_second"] = int(total_pixels / total_time)
    179182            add_compression_stats(estats)
    180183            for encoding in encodings_used:
    181184                enc_stats = [x for x in estats if x[1]==encoding]
    182                 add_compression_stats(enc_stats, suffix="[%s]" % encoding)
     185                add_compression_stats(enc_stats, encoding)
    183186
     187        dinfo = info.setdefault("damage", {})
    184188        latencies = [x*1000 for _, _, _, x in list(self.damage_in_latency)]
    185         add_list_stats(info, "damage.in_latency",  latencies, show_percentile=[9])
     189        dinfo["in_latency"]  = get_list_stats(latencies, show_percentile=[9])
    186190        latencies = [x*1000 for _, _, _, x in list(self.damage_out_latency)]
    187         add_list_stats(info, "damage.out_latency",  latencies, show_percentile=[9])
     191        dinfo["out_latency"] = get_list_stats(latencies, show_percentile=[9])
    188192        #per encoding totals:
    189193        for encoding, totals in self.encoding_totals.items():
    190194            info["total_frames[%s]" % encoding] = totals[0]
  • xpra/server/window/window_video_source.py

     
    1515from xpra.server.window.region import merge_all            #@UnresolvedImport
    1616from xpra.server.window.video_subregion import VideoSubregion
    1717from xpra.codecs.loader import PREFERED_ENCODING_ORDER, EDGE_ENCODING_ORDER
    18 from xpra.util import updict, parse_scaling_value, engs
     18from xpra.util import parse_scaling_value, engs
    1919from xpra.log import Logger
    2020
    2121log = Logger("encoding")
     
    144144
    145145    def get_info(self):
    146146        info = WindowSource.get_info(self)
    147         def up(prefix, d):
    148             updict(info, prefix, d)
    149147        sr = self.video_subregion
    150148        if sr:
    151             up("video_subregion", sr.get_info())
     149            info["video_subregion"] = sr.get_info()
    152150        info["scaling"] = self.actual_scaling
    153151        csce = self._csc_encoder
    154152        if csce:
    155             info["csc"] = csce.get_type()
    156             up("csc", csce.get_info())
     153            info["csc"] = csce.get_info()
    157154        ve = self._video_encoder
    158155        if ve:
    159             info["encoder"] = ve.get_type()
    160             up("encoder", ve.get_info())
    161         up("encoding.pipeline_param", self.get_pipeline_info())
    162         info["encodings.non-video"] = self.non_video_encodings
    163         info["encodings.edge"] = self.edge_encoding or ""
     156            info["encoder"] = ve.get_info()
     157        info.setdefault("encodings", {}).update({
     158                                                 "non-video"    : self.non_video_encodings,
     159                                                 "edge"         : self.edge_encoding or "",
     160                                                 })
     161        einfo = {"pipeline_param" : self.get_pipeline_info()}
    164162        if self._last_pipeline_check>0:
    165             info["encoding.pipeline_last_check"] = int(1000*(time.time()-self._last_pipeline_check))
     163            einfo["pipeline_last_check"] = int(1000*(time.time()-self._last_pipeline_check))
    166164        lps = self.last_pipeline_scores
    167165        if lps:
     166            popts = {}
    168167            for i, lp in enumerate(lps):
    169                 up("encoding.pipeline_option[%s]" % i, self.get_pipeline_score_info(*lp))
     168                popts[i] = self.get_pipeline_score_info(*lp)
     169            einfo["pipeline_option"] = popts
     170        info.setdefault("encoding", {}).update(einfo)
    170171        return info
    171172
    172173    def get_pipeline_info(self):
  • xpra/simple_stats.py

     
    77# Simple statistical functions
    88
    99from math import sqrt, pow
     10from xpra.util import updict
    1011
    1112def to_std_unit(v, unit=1000):
    1213    if v>=unit**3:
     
    6869def values_to_diff_scaled_values(data, scale_unit=10, min_scaled_value=10, num_values=20):
    6970    return values_to_scaled_values(absolute_to_diff_values(data), scale_unit=scale_unit, min_scaled_value=min_scaled_value, num_values=num_values)
    7071
    71 def add_weighted_list_stats(info, basename, weighted_values, show_percentile=False):
     72def get_weighted_list_stats(weighted_values, show_percentile=False):
    7273    values = [x for x, _ in weighted_values]
    7374    if len(values)==0:
    74         return
    75     info["%s.min" % basename] = int(min(values))
    76     info["%s.max" % basename] = int(max(values))
     75        return {}
    7776    #weighted mean:
    7877    tw = 0
    7978    tv = 0
     
    8180        tw += w
    8281        tv += v * w
    8382    avg = tv/tw
    84     info["%s.avg" % basename] = int(avg)
     83    stats = {
     84             "min"   : int(min(values)),
     85             "max"   : int(max(values)),
     86             "avg"   : int(avg),
     87             }
    8588    if show_percentile:
    8689        #percentile
    8790        svalues = sorted(values)
     
    8891        for i in range(1,10):
    8992            pct = i*10
    9093            index = len(values)*i//10
    91             info["%s.%sp" % (basename, pct)] = int(svalues[index])
     94            stats["%ip" % pct] = int(svalues[index])
     95    return stats
    9296
     97
    9398def find_invpow(x, n):
    9499    """Finds the integer component of the n'th root of x,
    95100    an integer such that y ** n <= x < (y + 1) ** n.
     
    108113            return mid
    109114    return mid + 1
    110115
    111 def add_list_stats(info, basename, in_values, show_percentile=[5, 8, 9], show_dev=False):
     116def get_list_stats(in_values, show_percentile=[5, 8, 9], show_dev=False):
    112117    #this may be backed by a deque/list whichi is used by other threads
    113118    #so make a copy before use:
    114119    values = list(in_values)
    115120    if len(values)==0:
    116         return
    117     info["%s.cur" % basename] = int(values[-1])
    118     info["%s.min" % basename] = int(min(values))
    119     info["%s.max" % basename] = int(max(values))
     121        return  {}
    120122    #arithmetic mean
    121123    avg = sum(values)/len(values)
    122     info["%s.avg" % basename] = int(avg)
     124    lstats = {
     125              "cur"       : int(values[-1]),
     126              "min"       : int(min(values)),
     127              "max"       : int(max(values)),
     128              "avg"       : int(avg),
     129              }
    123130    if show_dev:
    124131        p = 1           #geometric mean
    125132        h = 0           #harmonic mean
     
    133140            var += (x-avg)**2
    134141        #standard deviation:
    135142        std = sqrt(var/len(values))
    136         info["%s.std" % basename] = int(std)
     143        lstats["std"] = int(std)
    137144        if avg!=0:
    138145            #coefficient of variation
    139             info["%s.cv_pct" % basename] = int(100.0*std/avg)
     146            lstats["cv_pct"] = int(100.0*std/avg)
    140147        if counter>0 and p<float('inf'):
    141148            #geometric mean
    142149            try:
     
    143150                v = int(pow(p, 1.0/counter))
    144151            except OverflowError:
    145152                v = find_invpow(p, counter)
    146             info["%s.gm" % basename] = v
     153            lstats["gm"] = v
    147154        if h!=0:
    148155            #harmonic mean
    149             info["%s.h" % basename] = int(counter/h)
     156            lstats["h"] = int(counter/h)
    150157    if show_percentile:
    151158        #percentile
    152159        svalues = sorted(values)
     
    153160        for i in show_percentile:
    154161            pct = i*10
    155162            index = len(values)*i//10
    156             info["%s.%sp" % (basename, pct)] = int(svalues[index])
     163            lstats["%ip" % pct] = int(svalues[index])
     164    return lstats
  • xpra/sound/pulseaudio/pulseaudio_pactl_util.py

     
    174174
    175175
    176176def get_info():
    177     info = {
    178             "pulseaudio.wrapper": "pactl",
    179             "pulseaudio.found"  : has_pa(),
    180             "pulseaudio.id"     : get_pulse_id(),
    181             "pulseaudio.server" : get_pulse_server(False),
    182            }
    183177    i = 0
     178    dinfo = {}
    184179    for monitors in (True, False):
    185180        for io in (True, False):
    186181            devices = get_pa_device_options(monitors, io, log_errors=False)
    187182            for d,name in devices.items():
    188                 info["device.%s" % d] = name
     183                dinfo[d] = name
    189184                i += 1
    190     info["devices"] = i
    191     return info
     185    return {
     186            "device"        : dinfo,
     187            "devices"       : i,
     188            "pulseaudio"    : {
     189                               "wrapper"   : "pactl",
     190                               "found"     : has_pa(),
     191                               "id"        : get_pulse_id(),
     192                               "server"    : get_pulse_server(False),
     193                               }
     194            }
    192195
    193196
    194197def main():
  • xpra/sound/pulseaudio/pulseaudio_util.py

     
    2929        env_dict["PULSE_PROP_application.icon_name"] = str(icon_path)
    3030
    3131
    32 #prefer the palib option which does everything in process:
    3332try:
    3433    #use "none" on win32 and osx:
    3534    if sys.platform.startswith("win") or sys.platform.startswith("darwin"):
    3635        from xpra.sound.pulseaudio import pulseaudio_none_util as _pulseaudio_util
    3736    else:
    38         if os.environ.get("XPRA_USE_PALIB", "0")=="1":
    39             from xpra.sound.pulseaudio import pulseaudio_palib_util as _pulseaudio_util
    40         else:
    41             from xpra.sound.pulseaudio import pulseaudio_pactl_util as _pulseaudio_util
     37        from xpra.sound.pulseaudio import pulseaudio_pactl_util as _pulseaudio_util
    4238except ImportError as e:
    4339    #fallback forks a process and parses the output:
    4440    log("using pulseaudio none fallback")
  • xpra/sound/sink.py

     
    1414                                        MP3, CODEC_ORDER, gst, QUEUE_LEAK, GST_QUEUE_NO_LEAK, MS_TO_NS
    1515
    1616from xpra.scripts.config import InitExit
    17 from xpra.util import updict, csv
     17from xpra.util import csv
    1818from xpra.os_util import thread
    1919from xpra.log import Logger
    2020log = Logger("sound")
     
    251251            clt = self.queue.get_property("current-level-time")
    252252            qmax = self.queue.get_property("max-size-time")
    253253            qmin = self.queue.get_property("min-threshold-time")
    254             updict(info, "queue", {
    255                 "min"           : qmin//MS_TO_NS,
    256                 "max"           : qmax//MS_TO_NS,
    257                 "cur"           : clt//MS_TO_NS,
    258                 "pct"           : min(QUEUE_TIME, clt)*100//qmax,
    259                 "overruns"      : self.overruns,
    260                 "underruns"     : self.underruns,
    261                 "state"         : self.queue_state})
     254            info["queue"] = {
     255                             "min"          : qmin//MS_TO_NS,
     256                             "max"          : qmax//MS_TO_NS,
     257                             "cur"          : clt//MS_TO_NS,
     258                             "pct"          : min(QUEUE_TIME, clt)*100//qmax,
     259                             "overruns"     : self.overruns,
     260                             "underruns"    : self.underruns,
     261                             "state"        : self.queue_state
     262                             }
    262263        return info
    263264
    264265    def add_data(self, data, metadata=None):
  • xpra/sound/src.py

     
    173173        if self.caps:
    174174            info["caps"] = self.caps
    175175        if self.queue:
    176             info["queue.cur"] = self.queue.get_property("current-level-time")//MS_TO_NS
     176            info["queue"] = {"cur" : self.queue.get_property("current-level-time")//MS_TO_NS}
    177177        if self.buffer_latency:
    178178            for x in ("actual-buffer-time", "actual-latency-time"):
    179179                v = self.src.get_property(x)
  • xpra/util.py

     
    8282    seen_add = seen.add
    8383    return [x for x in seq if not (x in seen or seen_add(x))]
    8484
     85def merge_dicts(a, b, path=None):
     86    """ merges b into a """
     87    if path is None: path = []
     88    for key in b:
     89        if key in a:
     90            if isinstance(a[key], dict) and isinstance(b[key], dict):
     91                merge_dicts(a[key], b[key], path + [str(key)])
     92            elif a[key] == b[key]:
     93                pass # same leaf value
     94            else:
     95                raise Exception('Conflict at %s' % '.'.join(path + [str(key)]))
     96        else:
     97            a[key] = b[key]
     98    return a
    8599
     100
    86101class AtomicInteger(object):
    87102    def __init__(self, integer = 0):
    88103        self.counter = integer
     
    167182    from xpra.log import Logger
    168183    log = Logger("util")
    169184
     185    def __repr__(self):
     186        return "typedict%s" % dict.__repr__(self)
     187
    170188    def capsget(self, key, default=None):
    171189        v = self.get(key)
    172190        #py3k and bytes as keys...
     
    500518    return int(v+0.5)
    501519
    502520
     521def notypedict(info):
     522    def ntd(d):
     523        for k in list(d.keys()):
     524            v = d[k]
     525            if isinstance(v, dict):
     526                d[k] = ntd(v)
     527        return dict(d)
     528    return ntd(info)
     529
     530def flatten_dict(info):
     531    to = {}
     532    def add_dict(path, d):
     533        for k,v in d.items():
     534            if path:
     535                npath = path+"."+str(k)
     536            else:
     537                npath = str(k)
     538            if isinstance(v, dict):
     539                add_dict(npath, v)
     540            elif v is not None:
     541                to[npath] = v
     542    add_dict(None, info)
     543    return to
     544
    503545#used for merging dicts with a prefix and suffix
    504546#non-None values get added to <todict> with a prefix and optional suffix
    505547def updict(todict, prefix, d, suffix=""):
     
    516558            todict[k] = v
    517559
    518560
     561def print_nested_dict(d, prefix=""):
     562    for k in sorted(d.keys()):
     563        v = d[k]
     564        if isinstance(v, dict):
     565            print("%s* %s :" % (prefix, k.ljust(32)))
     566            print_nested_dict(v, prefix+"  ")
     567        else:
     568            print("%s* %s : %s" % (prefix, k.ljust(32), nonl(pver(v))))
     569
     570
    519571def pver(v):
    520572    #print for lists with version numbers, or CSV strings
    521573    if type(v) in (list, tuple):
  • xpra/version_util.py

     
    6666    return info
    6767
    6868def get_version_info():
    69     props = {
    70              "version"  : local_version
    71              }
    7269    try:
    7370        from xpra.src_info import LOCAL_MODIFICATIONS, REVISION
    74         props["local_modifications"]    = LOCAL_MODIFICATIONS
    75         props["revision"]               = REVISION
    7671    except ImportError as e:
    7772        log.warn("missing some source information: %s", e)
    78     return props
     73        LOCAL_MODIFICATIONS = 0
     74        REVISION = "unknown"
     75    return {
     76            "version"               : local_version,
     77            "local_modifications"   : LOCAL_MODIFICATIONS,
     78            "revision"              : REVISION,
     79             }
    7980
    8081def get_version_info_full():
    8182    props = get_version_info()
  • xpra/x11/server.py

     
    287287
    288288    def do_get_info(self, proto, server_sources, window_ids):
    289289        info = X11ServerBase.do_get_info(self, proto, server_sources, window_ids)
    290         info["focused"] = self._has_focus
    291         info["grabbed"] = self._has_grab
    292290        log("do_get_info: adding cursor=%s", self.last_cursor_data)
     291        info.setdefault("state", {}).update({
     292                                             "focused"  : self._has_focus,
     293                                             "grabbed"  : self._has_grab,
     294                                             })
     295        info.setdefault("cursor", {}).update(self.get_cursor_info())
     296        return info
     297
     298    def get_cursor_info(self):
     299        #(NOT from UI thread)
    293300        #copy to prevent race:
    294301        cd = self.last_cursor_data
    295302        if cd is None:
    296             info["cursor"] = "None"
    297         else:
    298             info["cursor.is_default"] = bool(self.default_cursor_data and len(self.default_cursor_data)>=8 and len(cd)>=8 and cd[7]==cd[7])
    299             #all but pixels:
    300             for i, x in enumerate(("x", "y", "width", "height", "xhot", "yhot", "serial", None, "name")):
    301                 if x:
    302                     v = cd[i] or ""
    303                     info["cursor." + x] = v
    304         return info
     303            return {"" : "None"}
     304        cinfo = {"is_default"   : bool(self.default_cursor_data and len(self.default_cursor_data)>=8 and len(cd)>=8 and cd[7]==cd[7])}
     305        #all but pixels:
     306        for i, x in enumerate(("x", "y", "width", "height", "xhot", "yhot", "serial", None, "name")):
     307            if x:
     308                v = cd[i] or ""
     309                cinfo[x] = v
     310        return cinfo
    305311
    306312    def get_ui_info(self, proto, wids=None, *args):
    307313        info = X11ServerBase.get_ui_info(self, proto, wids, *args)
     
    308314        #_NET_WM_NAME:
    309315        wm = self._wm
    310316        if wm:
    311             info["window-manager-name"] = wm.get_net_wm_name()
     317            info.setdefault("state", {})["window-manager-name"] = wm.get_net_wm_name()
     318        info.setdefault("cursor", {}).update(self.get_ui_cursor_info())
     319        return info
     320
     321    def get_ui_cursor_info(self):
     322        #(from UI thread)
    312323        #now cursor size info:
    313324        display = gtk.gdk.display_get_default()
    314325        pos = display.get_default_screen().get_root_window().get_pointer()[:2]
    315         info["cursor.position"] = pos
     326        cinfo = {"position" : pos}
    316327        for prop, size in {"default" : display.get_default_cursor_size(),
    317328                           "max"     : display.get_maximal_cursor_size()}.items():
    318329            if size is None:
    319330                continue
    320             info["cursor.%s_size" % prop] = size
    321         return info
     331            cinfo["%s_size" % prop] = size
     332        return cinfo
    322333
    323334
    324335    def get_window_info(self, window):
    325336        info = X11ServerBase.get_window_info(self, window)
    326         info["focused"] = self._has_focus and self._window_to_id.get(window, -1)==self._has_focus
    327         info["grabbed"] = self._has_grab and self._window_to_id.get(window, -1)==self._has_grab
    328         info["geometry"] = window.get_property("geometry")
    329         info["shown"] = self._desktop_manager.is_shown(window)
     337        info.update({
     338                     "focused"  : self._has_focus and self._window_to_id.get(window, -1)==self._has_focus,
     339                     "grabbed"  : self._has_grab and self._window_to_id.get(window, -1)==self._has_grab,
     340                     "geometry" : window.get_property("geometry"),
     341                     "shown"    : self._desktop_manager.is_shown(window),
     342                     })
    330343        try:
    331344            info["client-geometry"] = self._desktop_manager.window_geometry(window)
    332345        except:
  • xpra/x11/server_keyboard_config.py

     
    6565
    6666    def get_info(self):
    6767        info = KeyboardConfigBase.get_info(self)
    68         info["modifiers.filter"] = self.modifiers_filter
    6968        #keycodes:
    7069        if self.keycode_translation:
    7170            for kc, keycode in self.keycode_translation.items():
     
    8281                info["keymap.%s" % i] = (keyval, name, keycode, group, level)
    8382                i += 1
    8483        #modifiers:
     84        modinfo = {}
     85        modsinfo = {}
     86        modinfo["filter"] = self.modifiers_filter
    8587        if self.modifier_client_keycodes:
    8688            for mod, keys in self.modifier_client_keycodes.items():
    87                 info["modifier." + mod + ".client_keys"] = keys
     89                modinfo.setdefault(mod, {})["client_keys"] = keys
    8890        if self.keynames_for_mod:
    8991            for mod, keys in self.keynames_for_mod.items():
    90                 info["modifier." + mod + ".keys"] = tuple(keys)
     92                modinfo.setdefault(mod, {})["keys"] = tuple(keys)
    9193        if self.keycodes_for_modifier_keynames:
    9294            for mod, keys in self.keycodes_for_modifier_keynames.items():
    93                 info["modifier." + mod + ".keycodes"] = tuple(keys)
     95                modinfo.setdefault(mod, {})["keycodes"] = tuple(keys)
    9496        if self.xkbmap_mod_meanings:
    9597            for mod, mod_name in self.xkbmap_mod_meanings.items():
    96                 info["modifier." + mod ] = mod_name
    97         if self.xkbmap_x11_keycodes:
    98             for keycode, keysyms in self.xkbmap_x11_keycodes.items():
    99                 info["x11_keycode." + str(keycode) ] = keysyms
    100         for x in ("print", "layout", "variant"):
     98                modinfo[mod] = mod_name
     99        info["x11_keycode"] = self.xkbmap_x11_keycodes
     100        for x in ("print", "layout", "variant", "mod_managed", "mod_pointermissing"):
    101101            v = getattr(self, "xkbmap_"+x)
    102102            if v:
    103103                info[x] = v
    104         for x in ("nuisance", ):
    105             v = getattr(self, "xkbmap_mod_"+x)
    106             if v:
    107                 info["modifiers."+x] = list(v)
    108         for x in ("managed", "pointermissing"):
    109             v = getattr(self, "xkbmap_mod_"+x)
    110             if v:
    111                 info["modifiers."+x] = v
     104        modsinfo["nuisance"] = list(self.xkbmap_mod_nuisance or [])
     105        info["modifier"] = modinfo
     106        info["modifiers"] = modsinfo
    112107        log("keyboard info: %s", "\n".join(["%s=%s" % (k,v) for k,v in info.items()]))
    113108        return info
    114109
  • xpra/x11/shadow_x11_server.py

     
    9595
    9696    def get_info(self, proto):
    9797        info = X11ServerBase.get_info(self, proto)
    98         info["features.shadow"] = True
    99         info["server.type"] = "Python/gtk2/x11-shadow"
     98        info.setdefault("features", {})["shadow"] = True
     99        info.setdefault("server", {})["type"] = "Python/gtk2/x11-shadow"
    100100        return info
  • xpra/x11/x11_server_base.py

     
    77# later version. See the file COPYING for details.
    88
    99import os
     10import time
    1011import gtk.gdk
    1112
    1213#ensure that we use gtk as display source:
     
    127128                    v = parts[1].strip()
    128129                    self.opengl_props[k] = v
    129130            else:
    130                 self.opengl_props["error"] = str(err)
     131                self.opengl_props["error"] = str(err).strip("\n\r")
    131132        except Exception as e:
    132133            gllog.warn("Warning: failed to query OpenGL properties")
    133134            gllog.warn(" %s", e)
     
    214215        return capabilities
    215216
    216217    def do_get_info(self, proto, server_sources, window_ids):
    217         from xpra.util import updict
     218        start = time.time()
    218219        info = GTKServerBase.do_get_info(self, proto, server_sources, window_ids)
    219220        if self.opengl_props:
    220             updict(info, "opengl", self.opengl_props)
    221         info["server.type"] = "Python/gtk/x11"
     221            info["opengl"] = self.opengl_props
     222        #this is added here because the server keyboard config doesn't know about "keys_pressed"..
     223        info["keyboard"] = {
     224                            "state"           : {"keys_pressed"   : list(self.keys_pressed.keys())},
     225                            "fast-switching"  : True,
     226                            }
    222227        try:
     228            fx = find_libfakeXinerama()
     229        except:
     230            fx = None
     231        sinfo = {"type"             : "Python/gtk/x11",
     232                 "fakeXinerama"     : self.fake_xinerama and bool(fx),
     233                 "libfakeXinerama"  : fx or "",
     234                 "Xkb"              : X11Keyboard.hasXkb(),
     235                 "XTest"            : X11Keyboard.hasXTest()}
     236        try:
    223237            from xpra.x11.gtk2.composite import CompositeHelper
    224             info["server.XShm"] = CompositeHelper.XShmEnabled
     238            sinfo["XShm"] = CompositeHelper.XShmEnabled
    225239        except:
    226240            pass
    227241        #randr:
     
    228242        try:
    229243            sizes = RandR.get_screen_sizes()
    230244            if self.randr and len(sizes)>=0:
    231                 info["server.randr.options"] = list(reversed(sorted(sizes)))
     245                sinfo["randr"] = {"options" : list(reversed(sorted(sizes)))}
    232246        except:
    233247            pass
    234         try:
    235             fx = find_libfakeXinerama()
    236         except:
    237             fx = None
    238         info["server.fakeXinerama"] = self.fake_xinerama and bool(fx)
    239         info["server.libfakeXinerama"] = fx or ""
    240         #this is added here because the server keyboard config doesn't know about "keys_pressed"..
    241         info["keyboard.state.keys_pressed"] = list(self.keys_pressed.keys())
    242         info["keyboard.fast-switching"] = True
    243         info["server.Xkb"] = X11Keyboard.hasXkb()
    244         info["server.XTest"] = X11Keyboard.hasXTest()
     248        info["server"] = sinfo
     249        log.warn("X11ServerBase.do_get_info took %ims", (time.time()-start)*1000)
    245250        return info
    246251
    247252    def get_window_info(self, window):