Esempio n. 1
0
 def process_client_message_event(self, event):
     # FIXME
     # Need to listen for:
     #   _NET_CURRENT_DESKTOP
     #   _NET_WM_PING responses
     # and maybe:
     #   _NET_RESTACK_WINDOW
     #   _NET_WM_STATE (more fully)
     if event.message_type == "_NET_CLOSE_WINDOW":
         log.info("_NET_CLOSE_WINDOW received by %s", self)
         self.request_close()
         return True
     if event.message_type == "_NET_REQUEST_FRAME_EXTENTS":
         framelog("_NET_REQUEST_FRAME_EXTENTS")
         self._handle_frame_changed()
         return True
     if event.message_type == "_NET_MOVERESIZE_WINDOW":
         #this is overriden in WindowModel, skipped everywhere else:
         geomlog("_NET_MOVERESIZE_WINDOW skipped on %s (data=%s)", self,
                 event.data)
         return True
     if event.message_type == "":
         log("empty message type: %s", event)
         if first_time("empty-x11-window-message-type-%#x" %
                       event.window.get_xid()):
             log.warn(
                 "Warning: empty message type received for window %#x:",
                 event.window.get_xid())
             log.warn(" %s", event)
             log.warn(" further messages will be silently ignored")
         return True
     #not handled:
     return False
Esempio n. 2
0
def get_root_size():
    if OSX:
        #the easy way:
        root = get_default_root_window()
        w, h = root.get_geometry()[2:4]
    else:
        #GTK3 on win32 triggers this warning:
        #"GetClientRect failed: Invalid window handle."
        #if we try to use the root window,
        #and on Linux with Wayland, we get bogus values...
        screen = Gdk.Screen.get_default()
        if screen is None:
            return 1920, 1024
        w = screen.get_width()
        h = screen.get_height()
    if w <= 0 or h <= 0 or w > 32768 or h > 32768:
        if first_time("Gtk root window dimensions"):
            log.warn(
                "Warning: Gdk returned invalid root window dimensions: %ix%i",
                w, h)
            w, h = 1920, 1080
            log.warn(" using %ix%i instead", w, h)
            if WIN32:
                log.warn(" no access to the display?")
    return w, h
Esempio n. 3
0
def get_root_size():
    if WIN32 or (POSIX and not OSX):
        #FIXME: hopefully, we can remove this code once GTK3 on win32 is fixed?
        #we do it the hard way because the root window geometry is invalid on win32:
        #and even just querying it causes this warning:
        #"GetClientRect failed: Invalid window handle."
        screen = Gdk.Screen.get_default()
        if screen is None:
            return 1920, 1024
        w = screen.get_width()
        h = screen.get_height()
    else:
        #the easy way for platforms that work out of the box:
        root = get_default_root_window()
        w, h = root.get_geometry()[2:4]
    if w <= 0 or h <= 0 or w > 32768 or h > 32768:
        if first_time("Gtk root window dimensions"):
            log.warn(
                "Warning: Gdk returned invalid root window dimensions: %ix%i",
                w, h)
            w, h = 1920, 1080
            log.warn(" using %ix%i instead", w, h)
            if WIN32:
                log.warn(" no access to the display?")
    return w, h
Esempio n. 4
0
def set_visual(window, alpha: bool = True) -> bool:
    screen = window.get_screen()
    if alpha:
        visual = screen.get_rgba_visual()
    else:
        visual = screen.get_system_visual()
    alphalog("set_visual(%s, %s) screen=%s, visual=%s", window, alpha, screen,
             visual)
    #we can't do alpha on win32 with plain GTK,
    #(though we handle it in the opengl backend)
    if WIN32 or not first_time("no-rgba"):
        l = alphalog
    else:
        l = alphalog.warn
    if alpha and visual is None or (not WIN32 and not screen.is_composited()):
        l("Warning: cannot handle window transparency")
        if visual is None:
            l(" no RGBA visual")
        else:
            assert not screen.is_composited()
            l(" screen is not composited")
        return None
    alphalog("set_visual(%s, %s) using visual %s", window, alpha, visual)
    if visual:
        window.set_visual(visual)
    return visual
Esempio n. 5
0
def select_device(preferred_device_id=-1, min_compute=0):
    log("select_device(%s, %s)", preferred_device_id, min_compute)
    for device_id in (preferred_device_id, get_pref("device-id")):
        if device_id is not None and device_id >= 0:
            dct = make_device_context(device_id)
            if dct:
                device, context, tpct = dct
                context.pop()
                context.detach()
                if min_compute > 0:
                    compute = compute_capability(device)
                    if compute < min_compute:
                        log.warn(
                            "Warning: GPU device %i only supports compute %#x",
                            device_id, compute)
                if tpct < MIN_FREE_MEMORY:
                    log.warn("Warning: GPU device %i is low on memory: %i%%",
                             device_id, tpct)
                return device_id, device
    load_balancing = get_pref("load-balancing")
    log("load-balancing=%s", load_balancing)
    if load_balancing == "round-robin":
        return select_round_robin(min_compute)
    if load_balancing != "memory" and first_time("cuda-load-balancing"):
        log.warn("Warning: invalid load balancing value '%s'", load_balancing)
    return select_best_free_memory(min_compute)
Esempio n. 6
0
 def gravity_copy_coords(self, oldw, oldh, bw, bh):
     sx = sy = dx = dy = 0
     def center_y():
         if bh>=oldh:
             #take the whole source, paste it in the middle
             return 0, (bh-oldh)//2
         #skip the edges of the source, paste all of it
         return (oldh-bh)//2, 0
     def center_x():
         if bw>=oldw:
             return 0, (bw-oldw)//2
         return (oldw-bw)//2, 0
     def east_x():
         if bw>=oldw:
             return 0, bw-oldw
         return oldw-bw, 0
     def west_x():
         return 0, 0
     def north_y():
         return 0, 0
     def south_y():
         if bh>=oldh:
             return 0, bh-oldh
         return oldh-bh, 0
     g = self.gravity
     if not g or g==NorthWestGravity:
         #undefined (or 0), use NW
         sx, dx = west_x()
         sy, dy = north_y()
     elif g==NorthGravity:
         sx, dx = center_x()
         sy, dy = north_y()
     elif g==NorthEastGravity:
         sx, dx = east_x()
         sy, dy = north_y()
     elif g==WestGravity:
         sx, dx = west_x()
         sy, dy = center_y()
     elif g==CenterGravity:
         sx, dx = center_x()
         sy, dy = center_y()
     elif g==EastGravity:
         sx, dx = east_x()
         sy, dy = center_y()
     elif g==SouthWestGravity:
         sx, dx = west_x()
         sy, dy = south_y()
     elif g==SouthGravity:
         sx, dx = center_x()
         sy, dy = south_y()
     elif g==SouthEastGravity:
         sx, dx = east_x()
         sy, dy = south_y()
     elif g==StaticGravity:
         if first_time("StaticGravity-%i" % self.wid):
             log.warn("Warning: static gravity is not handled")
     w = min(bw, oldw)
     h = min(bh, oldh)
     return sx, sy, dx, dy, w, h
Esempio n. 7
0
 def parse_ws_frame(self, buf):
     if not buf:
         self._read_queue_put(buf)
         return
     if self.ws_data:
         ws_data = self.ws_data+buf
         self.ws_data = b""
     else:
         ws_data = buf
     log("parse_ws_frame(%i bytes) total buffer is %i bytes", len(buf), len(ws_data))
     while ws_data and not self._closed:
         parsed = decode_hybi(ws_data)
         if parsed is None:
             log("parse_ws_frame(%i bytes) not enough data", len(ws_data))
             #not enough data to get a full websocket frame,
             #save it for later:
             self.ws_data = ws_data
             return
         opcode, payload, processed, fin = parsed
         ws_data = ws_data[processed:]
         log("parse_ws_frame(%i bytes) payload=%i bytes, processed=%i, remaining=%i, opcode=%s, fin=%s",
             len(buf), len(payload), processed, len(ws_data), OPCODES.get(opcode, opcode), fin)
         if opcode==OPCODE_CONTINUE:
             assert self.ws_payload_opcode and self.ws_payload, "continuation frame does not follow a partial frame"
             self.ws_payload.append(payload)
             if not fin:
                 #wait for more
                 continue
             #join all the frames and process the payload:
             full_payload = b"".join(memoryview_to_bytes(v) for v in self.ws_payload)
             self.ws_payload = []
             opcode = self.ws_payload_opcode
             self.ws_payload_opcode = 0
         else:
             if self.ws_payload and self.ws_payload_opcode:
                 raise Exception("expected a continuation frame not %s" % OPCODES.get(opcode, opcode))
             full_payload = payload
             if not fin:
                 if opcode not in (OPCODE_BINARY, OPCODE_TEXT):
                     raise Exception("cannot handle fragmented '%s' frames" % OPCODES.get(opcode, opcode))
                 #fragmented, keep this payload for later
                 self.ws_payload_opcode = opcode
                 self.ws_payload.append(payload)
                 continue
         if opcode==OPCODE_BINARY:
             self._read_queue_put(full_payload)
         elif opcode==OPCODE_TEXT:
             if first_time("ws-text-frame-from-%s" % self._conn):
                 log.warn("Warning: handling text websocket frame as binary")
             self._read_queue_put(full_payload)
         elif opcode==OPCODE_CLOSE:
             self._process_ws_close(full_payload)
         elif opcode==OPCODE_PING:
             self._process_ws_ping(full_payload)
         elif opcode==OPCODE_PONG:
             self._process_ws_pong(full_payload)
         else:
             log.warn("Warning unhandled websocket opcode '%s'", OPCODES.get(opcode, "%#x" % opcode))
             log("payload=%r", payload)
Esempio n. 8
0
    def do_selection_get(self, selection_data, info, time):
        # Either call selection_data.set() or don't, and then return.
        # In practice, send a call across the wire, then block in a recursive
        # main loop.
        def nodata():
            selectiondata_set(selection_data, "STRING", 8, "")

        if not self._enabled or not self._can_receive:
            nodata()
            return
        if not self._have_token:
            try:
                return gtk.Invisible.do_selection_get(self, selection_data,
                                                      info, time)
            except Exception as e:
                log("gtk.Invisible.do_selection_get", exc_info=True)
                if first_time("selection-%s-not-implemented" %
                              self._selection):
                    log.warn("Warning: limited clipboard support for %s",
                             self._selection)
                    if is_Wayland():
                        log.warn(
                            " looks like a Wayland implementation limitation")
                    log.warn(" %s", e)
                nodata()
                return
        selection = selectiondata_get_selection(selection_data)
        target = selectiondata_get_target(selection_data)
        log("do_selection_get(%s, %s, %s) selection=%s", selection_data, info,
            time, selection)
        self._selection_get_events += 1
        assert str(selection) == self._selection, "expected %s but got %s" % (
            selection, self._selection)
        self._request_contents_events += 1
        result = self.emit("get-clipboard-from-remote", self._selection,
                           target)
        if result is None or result["type"] is None:
            log("remote selection fetch timed out or empty")
            nodata()
            return
        data = result["data"]
        dformat = result["format"]
        dtype = result["type"]
        if dtype:
            dtype = bytestostr(dtype)
        log(
            "do_selection_get(%s,%s,%s) calling selection_data.set(%s, %s, %s:%s)",
            selection_data, info, time, dtype, dformat, type(data),
            len(data or ""))
        boc = self._block_owner_change
        self._block_owner_change = True
        if is_gtk3() and dtype in ("UTF8_STRING", "STRING") and dformat == 8:
            #GTK3 workaround: can only use set_text and only on the clipboard?
            s = bytestostr(data)
            self._clipboard.set_text(s, len(s))
        else:
            selectiondata_set(selection_data, dtype, dformat, data)
        if boc is False:
            glib.idle_add(self.remove_block)
Esempio n. 9
0
 def check_auth_publickey(self, username, key):
     log("check_auth_publickey(%s, %r) pubkey_auth=%s", username, key,
         self.pubkey_auth)
     if not self.pubkey_auth:
         return paramiko.AUTH_FAILED
     if not POSIX or getuid() != 0:
         import getpass
         sysusername = getpass.getuser()
         if sysusername != username:
             log.warn("Warning: ssh password authentication failed,")
             log.warn(" username does not match:")
             log.warn(" expected '%s', got '%s'", sysusername, username)
             return paramiko.AUTH_FAILED
     authorized_keys_filename = osexpand(AUTHORIZED_KEYS)
     if not os.path.exists(authorized_keys_filename) or not os.path.isfile(
             authorized_keys_filename):
         log("file '%s' does not exist", authorized_keys_filename)
         return paramiko.AUTH_FAILED
     import base64
     import binascii
     fingerprint = key.get_fingerprint()
     hex_fingerprint = binascii.hexlify(fingerprint)
     log("looking for key fingerprint '%s' in '%s'", hex_fingerprint,
         authorized_keys_filename)
     count = 0
     with open(authorized_keys_filename, "rb") as f:
         for line in f:
             if line.startswith("#"):
                 continue
             line = line.strip("\n\r")
             try:
                 key = base64.b64decode(
                     line.strip().split()[1].encode('ascii'))
             except Exception as e:
                 log("ignoring line '%s': %s", line, e)
                 continue
             import hashlib
             for hash_algo in AUTHORIZED_KEYS_HASHES:
                 hash_instance = None
                 try:
                     hash_class = getattr(hashlib,
                                          hash_algo)  #ie: hashlib.md5
                     hash_instance = hash_class(
                         key
                     )  #can raise ValueError (ie: on FIPS compliant systems)
                 except ValueError:
                     hash_instance = None
                 if not hash_instance:
                     if first_time("hash-%s-missing" % hash_algo):
                         log.warn("Warning: unsupported hash '%s'",
                                  hash_algo)
                     continue
                 fp_plain = hash_instance.hexdigest()
                 log("%s(%s)=%s", hash_algo, line, fp_plain)
                 if fp_plain == hex_fingerprint:
                     return paramiko.OPEN_SUCCEEDED
             count += 1
     log("no match in %i keys from '%s'", count, authorized_keys_filename)
     return paramiko.AUTH_FAILED
Esempio n. 10
0
    def process_challenge_kerberos(self, packet):
        digest = packet[3]
        if not digest.startswith(b"kerberos:"):
            authlog("%s is not a kerberos challenge", digest)
            #not a kerberos challenge
            return False
        try:
            if WIN32:
                import winkerberos as kerberos
            else:
                import kerberos
        except ImportError as e:
            authlog("import (win)kerberos", exc_info=True)
            if first_time("no-kerberos"):
                authlog.warn(
                    "Warning: kerberos challenge handler is not supported:")
                authlog.warn(" %s", e)
            return False
        service = bytestostr(digest.split(b":", 1)[1])
        if service not in KERBEROS_SERVICES and "*" not in KERBEROS_SERVICES:
            authlog.warn("Warning: invalid kerberos request for service '%s'",
                         service)
            authlog.warn(" services supported: %s", csv(KERBEROS_SERVICES))
            return False
        authlog("kerberos service=%s", service)

        def log_kerberos_exception(e):
            try:
                for x in e.args:
                    if isinstance(x, (list, tuple)):
                        try:
                            log.error(" %s", csv(x))
                            continue
                        except:
                            pass
                    authlog.error(" %s", x)
            except Exception:
                authlog.error(" %s", e)

        try:
            r, ctx = kerberos.authGSSClientInit(service)
            assert r == 1, "return code %s" % r
        except Exception as e:
            authlog("kerberos.authGSSClientInit(%s)", service, exc_info=True)
            authlog.error("Error: cannot initialize kerberos client:")
            log_kerberos_exception(e)
            return False
        try:
            kerberos.authGSSClientStep(ctx, "")
        except Exception as e:
            authlog("kerberos.authGSSClientStep", exc_info=True)
            authlog.error("Error: kerberos client authentication failure:")
            log_kerberos_exception(e)
            return False
        token = kerberos.authGSSClientResponse(ctx)
        authlog("kerberos token=%s", token)
        self.send_challenge_reply(packet, token)
        return True
Esempio n. 11
0
 def notify_dpi_warning(self, body):
     sources = tuple(self._server_sources.values())
     if len(sources) == 1:
         ss = sources[0]
         if first_time("DPI-warning-%s" % ss.uuid):
             sources[0].may_notify(XPRA_DPI_NOTIFICATION_ID,
                                   "DPI Issue",
                                   body,
                                   icon_name="font")
Esempio n. 12
0
def rgb_reformat(image, rgb_formats, supports_transparency):
    """ convert the RGB pixel data into a format supported by the client """
    #need to convert to a supported format!
    PIL = get_codec("PIL")
    pixel_format = bytestostr(image.get_pixel_format())
    pixels = image.get_pixels()
    assert pixels, "failed to get pixels from %s" % image
    if not PIL or pixel_format in ("r210", "BGR565"):
        #try to fallback to argb module
        #(required for r210 which is not handled by PIL directly)
        assert argb_swap, "no argb codec"
        log("rgb_reformat: using argb_swap for %s", image)
        return argb_swap(image, rgb_formats, supports_transparency)
    if supports_transparency:
        modes = PIL_conv.get(pixel_format)
    else:
        modes = PIL_conv_noalpha.get(pixel_format)
    assert modes, "no PIL conversion from %s" % (pixel_format)
    target_rgb = [(im, om) for (im, om) in modes if om in rgb_formats]
    if len(target_rgb) == 0:
        log("rgb_reformat: no matching target modes for converting %s to %s",
            image, rgb_formats)
        #try argb module:
        assert argb_swap, "no argb codec"
        if argb_swap(image, rgb_formats, supports_transparency):
            return True
        warning_key = "rgb_reformat(%s, %s, %s)" % (pixel_format, rgb_formats,
                                                    supports_transparency)
        if first_time(warning_key):
            log.warn("Warning: cannot convert %s to one of: %s" %
                     (pixel_format, rgb_formats))
        return False
    input_format, target_format = target_rgb[0]
    start = monotonic_time()
    w = image.get_width()
    h = image.get_height()
    #PIL cannot use the memoryview directly:
    if isinstance(pixels, memoryview):
        pixels = pixels.tobytes()
    #log("rgb_reformat: converting %s from %s to %s using PIL, %i bytes", image, input_format, target_format, len(pixels))
    img = Image.frombuffer(target_format, (w, h), pixels, "raw", input_format,
                           image.get_rowstride())
    rowstride = w * len(
        target_format)  #number of characters is number of bytes per pixel!
    data = img.tobytes("raw", target_format)
    assert len(
        data) == rowstride * h, "expected %s bytes in %s format but got %s" % (
            rowstride * h, len(data))
    image.set_pixels(data)
    image.set_rowstride(rowstride)
    image.set_pixel_format(target_format)
    end = monotonic_time()
    log(
        "rgb_reformat(%s, %s, %s) converted from %s (%s bytes) to %s (%s bytes) in %.1fms, rowstride=%s",
        image, rgb_formats, supports_transparency, pixel_format, len(pixels),
        target_format, len(data), (end - start) * 1000.0, rowstride)
    return True
Esempio n. 13
0
 def hello_request(self):
     if first_time("hello-request"):
         #this can be called again if we receive a challenge,
         #but only print this message once:
         errwrite("requesting new session, please wait")
     self.timeout_add(1 * 1000, self.dots)
     return {
         "start-new-session": self.start_new_session,
         #tells proxy servers we don't want to connect to the real / new instance:
         "connect": False,
     }
Esempio n. 14
0
def mmap_send(mmap, mmap_size, image, rgb_formats, supports_transparency):
    if mmap_write is None:
        if first_time("mmap_write missing"):
            log.warn("Warning: cannot use mmap, no write method support")
        return None
    if image.get_pixel_format() not in rgb_formats:
        if not rgb_reformat(image, rgb_formats, supports_transparency):
            warning_key = "mmap_send(%s)" % image.get_pixel_format()
            if first_time(warning_key):
                log.warn("Waening: cannot use mmap to send %s" % image.get_pixel_format())
            return None
    start = monotonic_time()
    data = image.get_pixels()
    assert data, "failed to get pixels from %s" % image
    mmap_data, mmap_free_size = mmap_write(mmap, mmap_size, data)
    elapsed = monotonic_time()-start+0.000000001 #make sure never zero!
    log("%s MBytes/s - %s bytes written to mmap in %.1f ms", int(len(data)/elapsed/1024/1024), len(data), 1000*elapsed)
    if mmap_data is None:
        return None
    #replace pixels with mmap info:
    return mmap_data, mmap_free_size, len(data)
Esempio n. 15
0
def _get_qrencode_fn():
    try:
        from PIL import Image
        from qrencode import encode
        def qrencode_fn(s):
            return encode(s)[2]
        return qrencode_fn
    except ImportError:
        try:
            from PIL import Image
            from ctypes import (
                Structure, POINTER,
                cdll, create_string_buffer, c_int, c_char_p,
                )
            from ctypes.util import find_library
            class QRCode(Structure):
                _fields_ = (
                    ("version", c_int),
                    ("width", c_int),
                    ("data", c_char_p),
                    )
            PQRCode = POINTER(QRCode)
            lib_file = None
            for lib_name in ("libqrencode", "qrencode"):
                lib_file = find_library(lib_name)
                if lib_file:
                    break
            if not lib_file:
                if first_time("libqrencode"):
                    log.warn("Warning: libqrencode not found")
                return None
            libqrencode = cdll.LoadLibrary(lib_file)
            encodeString8bit = libqrencode.QRcode_encodeString8bit
            encodeString8bit.argtypes = (c_char_p, c_int, c_int)
            encodeString8bit.restype = PQRCode
            QRcode_free = libqrencode.QRcode_free
            QRcode_free.argtypes = (PQRCode,)
            def qrencode_ctypes_fn(s):
                data = create_string_buffer(s.encode("latin1"))
                qrcode = encodeString8bit(data, 0, 0).contents
                try:
                    size = qrcode.width
                    pixels = bytearray(size*size)
                    pdata = qrcode.data
                    for i in range(size*size):
                        pixels[i] = 0 if (pdata[i] & 0x1) else 255
                    return Image.frombytes('L', (size, size), bytes(pixels))
                finally:
                    QRcode_free(qrcode)
            return qrencode_ctypes_fn
        except Exception:
            log.error("failed to load qrencode via ctypes", exc_info=True)
    return None
Esempio n. 16
0
def do_load_xdg_menu_data():
    try:
        from xdg.Menu import parse, Menu, ParsingError
    except ImportError:
        log("do_load_xdg_menu_data()", exc_info=True)
        if first_time("no-python-xdg"):
            log.warn("Warning: cannot use application menu data:")
            log.warn(" no python-xdg module")
        return None
    menu = None
    error = None
    with OSEnvContext():
        #see ticket #2340,
        #invalid values for XDG_CONFIG_DIRS can cause problems,
        #so try unsetting it if we can't load the menus with it:
        for cd in (False, True):
            if cd:
                try:
                    del os.environ["XDG_CONFIG_DIRS"]
                except KeyError:
                    continue
            #see ticket #2174,
            #things may break if the prefix is not set,
            #and it isn't set when logging in via ssh
            for prefix in (None, "", "gnome-", "kde-"):
                if prefix is not None:
                    os.environ["XDG_MENU_PREFIX"] = prefix
                try:
                    menu = parse()
                    break
                except Exception as e:
                    log("do_load_xdg_menu_data()", exc_info=True)
                    error = e
                    menu = None
    if menu is None:
        if error:
            log.error("Error parsing xdg menu data:")
            log.error(" %s", error)
            log.error(" this is either a bug in python-xdg,")
            log.error(" or an invalid system menu configuration")
        return None
    menu_data = {}
    for submenu in menu.getEntries():
        if isinstance(submenu, Menu) and submenu.Visible:
            name = submenu.getName()
            try:
                menu_data[name] = load_xdg_menu(submenu)
            except Exception as e:
                log("load_xdg_menu_data()", exc_info=True)
                log.error("Error loading submenu '%s':", name)
                log.error(" %s", e)
    return menu_data
Esempio n. 17
0
 def get_element_properties(self, element, *properties):
     info = {}
     for x in properties:
         try:
             v = element.get_property(x)
             if v >= 0:
                 info[x] = v
         except TypeError as e:
             if first_time("gst-property-%s" % x):
                 log("'%s' not found in %r", x, self)
                 log.warn("Warning: %s", e)
         except Exception as e:
             log.warn("Warning: %s (%s)", e, type(e))
     return info
Esempio n. 18
0
def load_icon_from_file(filename):
    log("load_icon_from_file(%s)", filename)
    if filename.endswith("xpm"):
        from PIL import Image
        try:
            img = Image.open(filename)
            buf = BytesIO()
            img.save(buf, "PNG")
            pngicondata = buf.getvalue()
            buf.close()
            return pngicondata, "png"
        except ValueError as e:
            log("Image.open(%s)", filename, exc_info=True)
        except Exception as e:
            log("Image.open(%s)", filename, exc_info=True)
            log.error("Error loading '%s':", filename)
            log.error(" %s", e)
        #fallback to PixbufLoader:
        try:
            from xpra.gtk_common.gtk_util import pixbuf_save_to_memory
            data = load_binary_file(filename)
            from gi.repository import GdkPixbuf
            loader = GdkPixbuf.PixbufLoader()
            loader.write(data)
            loader.close()
            pixbuf = loader.get_pixbuf()
            pngicondata = pixbuf_save_to_memory(pixbuf, "png")
            return pngicondata, "png"
        except Exception as e:
            log("pixbuf error loading %s", filename, exc_info=True)
            log.error("Error loading '%s':", filename)
            log.error(" %s", e)
    icondata = load_binary_file(filename)
    if not icondata:
        return None
    if filename.endswith("svg") and len(icondata)>MAX_ICON_SIZE//2:
        #try to resize it
        size = len(icondata)
        pngdata = svg_to_png(filename, icondata)
        if pngdata:
            log("reduced size of SVG icon %s, from %i bytes to %i bytes as PNG",
                     filename, size, len(pngdata))
            icondata = pngdata
            filename = filename[:-3]+"png"
    log("got icon data from '%s': %i bytes", filename, len(icondata))
    if len(icondata)>MAX_ICON_SIZE and first_time("icon-size-warning-%s" % filename):
        global large_icons
        large_icons.append((filename, len(icondata)))
    return icondata, os.path.splitext(filename)[1].lstrip(".")
Esempio n. 19
0
def load_Rsvg():
    global _Rsvg
    if _Rsvg is None:
        import gi  #pylint: disable=import-outside-toplevel
        try:
            gi.require_version('Rsvg', '2.0')
            from gi.repository import Rsvg
            log("load_Rsvg() Rsvg=%s", Rsvg)
            _Rsvg = Rsvg
        except (ValueError, ImportError) as e:
            if first_time("no-rsvg"):
                log.warn("Warning: cannot resize svg icons,")
                log.warn(" the Rsvg bindings were not found:")
                log.warn(" %s", e)
            _Rsvg = False
    return _Rsvg
Esempio n. 20
0
def get_interface_info(_fd, iface):
    log = logger()
    from xpra.platform.win32.comtypes_util import QuietenLogging
    with QuietenLogging():
        try:
            from comtypes import CoInitialize  #@UnresolvedImport
            CoInitialize()
            from comtypes.client import CreateObject  #@UnresolvedImport
            o = CreateObject("WbemScripting.SWbemLocator")
            s = o.ConnectServer(".", "root\\cimv2")
            query = "SELECT * FROM Win32_NetworkAdapter WHERE GUID='%s'" % iface
            res = s.ExecQuery(query)
            log("ExecQuery(%s) returned %i rows", query, res.Count)
            if res.Count == 1:
                for r in res:
                    props = {}
                    for k, ik, conv in (
                        ("AdapterType", "adapter-type", str),
                        ("Caption", "caption", str),
                        ("Description", "description", str),
                        ("DeviceID", "id", int),
                        ("GUID", "GUID", str),
                        ("Index", "index", int),
                        ("Name", "name", str),
                        ("ProductName", "product-name", str),
                        ("Speed", "speed", int),
                    ):
                        try:
                            v = conv(r.Properties_[k].Value)
                        except Exception as e:
                            log.error(
                                "Error retrieving '%s' from network adapter record:",
                                k)
                            log.error(" %s", e)
                        else:
                            props[ik] = v
                    log("get_interface_info(%s)=%s" % (iface, props))
                    return props
        except Exception as e:
            log("get_interface_info(%s)", iface, exc_info=True)
            from xpra.util import first_time
            if first_time("win32-network-query"):
                log.error("Error: failed to query network interface:")
                log.error(" %s", e)
        return {}
Esempio n. 21
0
    def set_screen_sizes(self, screen_sizes):
        log("set_screen_sizes(%s)", screen_sizes)
        self.screen_sizes = screen_sizes or []
        #validate dpi / screen size in mm
        #(ticket 2480: GTK3 on macos can return bogus values)
        MIN_DPI = envint("XPRA_MIN_DPI", 10)
        MAX_DPI = envint("XPRA_MIN_DPI", 500)

        def dpi(size_pixels, size_mm):
            if size_mm == 0:
                return 0
            return int(size_pixels * 254 / size_mm / 10)

        for i, screen in enumerate(screen_sizes):
            if len(screen) < 10:
                continue
            sw, sh, wmm, hmm, monitors = screen[1:6]
            xdpi = dpi(sw, wmm)
            ydpi = dpi(sh, hmm)
            if xdpi < MIN_DPI or xdpi > MAX_DPI or ydpi < MIN_DPI or ydpi > MAX_DPI:
                warn = first_time("invalid-screen-size-%ix%i" % (wmm, hmm))
                if warn:
                    log.warn(
                        "Warning: sanitizing invalid screen size %ix%i mm",
                        wmm, hmm)
                if monitors:
                    #[plug_name, xs(geom.x), ys(geom.y), xs(geom.width), ys(geom.height), wmm, hmm]
                    wmm = sum(monitor[5] for monitor in monitors)
                    hmm = sum(monitor[6] for monitor in monitors)
                    xdpi = dpi(sw, wmm)
                    ydpi = dpi(sh, hmm)
                if xdpi < MIN_DPI or xdpi > MAX_DPI or ydpi < MIN_DPI or ydpi > MAX_DPI:
                    #still invalid, generate one from DPI=96
                    wmm = iround(sw * 254 / 10 / 96.0)
                    hmm = iround(sh * 254 / 10 / 96.0)
                if warn:
                    log.warn(" using %ix%i mm", wmm, hmm)
                screen = list(screen)
                screen[3] = wmm
                screen[4] = hmm
                screen_sizes[i] = tuple(screen)
        log("client validated screen sizes: %s", screen_sizes)
Esempio n. 22
0
def load_icon_from_file(filename):
    log("load_icon_from_file(%s)", filename)
    if filename.endswith("xpm"):
        try:
            from xpra.gtk_common.gobject_compat import import_pixbufloader
            from xpra.gtk_common.gtk_util import pixbuf_save_to_memory
            data = load_binary_file(filename)
            loader = import_pixbufloader()()
            loader.write(data)
            loader.close()
            pixbuf = loader.get_pixbuf()
            pngicondata = pixbuf_save_to_memory(pixbuf, "png")
            return pngicondata, "png"
        except Exception as e:
            log("pixbuf error loading %s", filename, exc_info=True)
            log.error("Error loading '%s':", filename)
            log.error(" %s", e)
        #try PIL:
        from PIL import Image
        try:
            img = Image.open(filename)
        except Exception as e:
            log("Image.open(%s)", filename, exc_info=True)
            log.error("Error loading '%s':", filename)
            log.error(" %s", e)
            return None
        buf = BytesIO()
        img.save(buf, "PNG")
        pngicondata = buf.getvalue()
        buf.close()
        return pngicondata, "png"
    icondata = load_binary_file(filename)
    if not icondata:
        return None
    log("got icon data from '%s': %i bytes", filename, len(icondata))
    if len(icondata) > MAX_ICON_SIZE and first_time(
            "icon-size-warning-%s" % filename):
        log.warn("Warning: icon is quite large (%i KB):",
                 len(icondata) // 1024)
        log.warn(" '%s'", filename)
    return icondata, os.path.splitext(filename)[1].lstrip(".")
Esempio n. 23
0
 def process_challenge_gss(self, packet):
     digest = packet[3]
     if not digest.startswith(b"gss:"):
         #not a gss challenge
         authlog("%s is not a gss challenge", digest)
         return False
     try:
         import gssapi
         if OSX and False:
             from gssapi.raw import (cython_converters, cython_types, oids)
             assert cython_converters and cython_types and oids
     except ImportError as e:
         authlog("import gssapi", exc_info=True)
         if first_time("no-kerberos"):
             authlog.warn("Warning: gss authentication not supported:")
             authlog.warn(" %s", e)
         return False
     service = bytestostr(digest.split(b":", 1)[1])
     if service not in GSS_SERVICES and "*" not in GSS_SERVICES:
         authlog.warn("Warning: invalid GSS request for service '%s'",
                      service)
         authlog.warn(" services supported: %s", csv(GSS_SERVICES))
         return False
     authlog("gss service=%s", service)
     service_name = gssapi.Name(service)
     try:
         ctx = gssapi.SecurityContext(name=service_name, usage="initiate")
         token = ctx.step()
     except Exception as e:
         authlog("gssapi failure", exc_info=True)
         authlog.error("Error: gssapi client authentication failure:")
         try:
             #split on colon
             for x in str(e).split(":", 2):
                 authlog.error(" %s", x.lstrip(" "))
         except:
             authlog.error(" %s", e)
         return False
     authlog("gss token=%s", repr(token))
     self.send_challenge_reply(packet, token)
     return True
Esempio n. 24
0
def load_icon_from_file(filename, max_size=MAX_ICON_SIZE):
    if os.path.isdir(filename):
        log("load_icon_from_file(%s, %i) path is a directory!", filename,
            max_size)
        return None
    log("load_icon_from_file(%s, %i)", filename, max_size)
    if filename.endswith("xpm"):
        from PIL import Image  #pylint: disable=import-outside-toplevel
        try:
            img = Image.open(filename)
            buf = BytesIO()
            img.save(buf, "PNG")
            pngicondata = buf.getvalue()
            buf.close()
            return pngicondata, "png"
        except ValueError as e:
            log("Image.open(%s)", filename, exc_info=True)
        except Exception as e:
            log("Image.open(%s)", filename, exc_info=True)
            log.error("Error loading '%s':", filename)
            log.error(" %s", e)
    icondata = load_binary_file(filename)
    if not icondata:
        return None
    if filename.endswith("svg") and max_size and len(icondata) > max_size:
        #try to resize it
        size = len(icondata)
        pngdata = svg_to_png(filename, icondata)
        if pngdata:
            log(
                "reduced size of SVG icon %s, from %i bytes to %i bytes as PNG",
                filename, size, len(pngdata))
            icondata = pngdata
            filename = filename[:-3] + "png"
    log("got icon data from '%s': %i bytes", filename, len(icondata))
    if max_size > 0 and len(icondata) > max_size and first_time(
            "icon-size-warning-%s" % filename):
        global large_icons
        large_icons.append((filename, len(icondata)))
    return icondata, os.path.splitext(filename)[1].lstrip(".")
Esempio n. 25
0
def load_icon_from_file(filename):
    log("load_icon_from_file(%s)", filename)
    if filename.endswith("xpm"):
        try:
            from xpra.gtk_common.gobject_compat import import_pixbufloader
            from xpra.gtk_common.gtk_util import pixbuf_save_to_memory
            data = load_binary_file(filename)
            loader = import_pixbufloader()()
            loader.write(data)
            loader.close()
            pixbuf = loader.get_pixbuf()
            pngicondata = pixbuf_save_to_memory(pixbuf, "png")
            return pngicondata, "png"
        except Exception as e:
            log("pixbuf error loading %s", filename, exc_info=True)
            log.error("Error loading '%s':", filename)
            log.error(" %s", e)
        #try PIL:
        from PIL import Image
        try:
            img = Image.open(filename)
        except Exception as e:
            log("Image.open(%s)", filename, exc_info=True)
            log.error("Error loading '%s':", filename)
            log.error(" %s", e)
            return None
        buf = BytesIO()
        img.save(buf, "PNG")
        pngicondata = buf.getvalue()
        buf.close()
        return pngicondata, "png"
    icondata = load_binary_file(filename)
    if not icondata:
        return None
    if filename.endswith("svg") and len(icondata) > MAX_ICON_SIZE // 2:
        #try to resize it
        try:
            size = len(icondata)
            import cairo
            import gi
            try:
                gi.require_version('Rsvg', '2.0')
            except ValueError as e:
                if first_time("no-rsvg"):
                    log.warn(
                        "Warning: cannot resize svg icons, Rsvg bindings not found:"
                    )
                    log.warn(" %s", e)
            else:
                from gi.repository import Rsvg
                img = cairo.ImageSurface(cairo.FORMAT_ARGB32, 128, 128)
                ctx = cairo.Context(img)
                handle = Rsvg.Handle.new_from_data(icondata)
                handle.render_cairo(ctx)
                buf = BytesIO()
                img.write_to_png(buf)
                icondata = buf.getvalue()
                buf.close()
                log(
                    "reduced size of SVG icon %s, from %i bytes to %i bytes as PNG",
                    filename, size, len(icondata))
                filename = filename[:-3] + "png"
        except ImportError:
            log("cannot convert svg", exc_info=True)
        except Exception:
            log.error("Error: failed to convert svg icon", exc_info=True)
    log("got icon data from '%s': %i bytes", filename, len(icondata))
    if len(icondata) > MAX_ICON_SIZE and first_time(
            "icon-size-warning-%s" % filename):
        log.warn("Warning: icon is quite large (%i KB):",
                 len(icondata) // 1024)
        log.warn(" '%s'", filename)
    return icondata, os.path.splitext(filename)[1].lstrip(".")
Esempio n. 26
0
def get_screen_sizes(xscale=1, yscale=1):
    from xpra.platform.gui import get_workarea, get_workareas

    def xs(v):
        return iround(v / xscale)

    def ys(v):
        return iround(v / yscale)

    def swork(*workarea):
        return xs(workarea[0]), ys(workarea[1]), xs(workarea[2]), ys(
            workarea[3])

    display = Gdk.Display.get_default()
    if not display:
        return ()
    MIN_DPI = envint("XPRA_MIN_DPI", 10)
    MAX_DPI = envint("XPRA_MIN_DPI", 500)

    def dpi(size_pixels, size_mm):
        if size_mm == 0:
            return 0
        return int(size_pixels * 254 / size_mm / 10)

    n_screens = display.get_n_screens()
    get_n_monitors = getattr(display, "get_n_monitors", None)
    if get_n_monitors:
        #GTK 3.22: always just one screen
        n_monitors = get_n_monitors()
        workareas = get_workareas()
        if workareas and len(workareas) != n_monitors:
            screenlog(" workareas: %s", workareas)
            screenlog(
                " number of monitors does not match number of workareas!")
            workareas = []
        monitors = []
        for j in range(n_monitors):
            monitor = display.get_monitor(j)
            geom = monitor.get_geometry()
            manufacturer, model = monitor.get_manufacturer(
            ), monitor.get_model()
            if manufacturer and model:
                plug_name = "%s %s" % (manufacturer, model)
            elif manufacturer:
                plug_name = manufacturer
            elif model:
                plug_name = model
            else:
                plug_name = "%i" % j
            wmm, hmm = monitor.get_width_mm(), monitor.get_height_mm()
            monitor = [
                plug_name,
                xs(geom.x),
                ys(geom.y),
                xs(geom.width),
                ys(geom.height), wmm, hmm
            ]
            screenlog(" monitor %s: %s", j, monitor)
            if workareas:
                w = workareas[j]
                monitor += list(swork(*w))
            monitors.append(tuple(monitor))
        screen = display.get_default_screen()
        sw, sh = screen.get_width(), screen.get_height()
        work_x, work_y, work_width, work_height = swork(0, 0, sw, sh)
        workarea = get_workarea()  #pylint: disable=assignment-from-none
        if workarea:
            work_x, work_y, work_width, work_height = swork(*workarea)  #pylint: disable=not-an-iterable
        screenlog(" workarea=%s", workarea)
        wmm = screen.get_width_mm()
        hmm = screen.get_height_mm()
        xdpi = dpi(sw, wmm)
        ydpi = dpi(sh, hmm)
        if xdpi < MIN_DPI or xdpi > MAX_DPI or ydpi < MIN_DPI or ydpi > MAX_DPI:
            warn = first_time("invalid-screen-size-%ix%i" % (wmm, hmm))
            if warn:
                log.warn("Warning: ignoring invalid screen size %ix%imm", wmm,
                         hmm)
            if n_monitors > 0:
                wmm = sum(
                    display.get_monitor(i).get_width_mm()
                    for i in range(n_monitors))
                hmm = sum(
                    display.get_monitor(i).get_height_mm()
                    for i in range(n_monitors))
                xdpi = dpi(sw, wmm)
                ydpi = dpi(sh, hmm)
            if xdpi < MIN_DPI or xdpi > MAX_DPI or ydpi < MIN_DPI or ydpi > MAX_DPI:
                #still invalid, generate one from DPI=96
                wmm = iround(sw * 25.4 / 96)
                hmm = iround(sh * 25.4 / 96)
            if warn:
                log.warn(" using %ix%i mm", wmm, hmm)
        item = (screen.make_display_name(), xs(sw), ys(sh), wmm, hmm, monitors,
                work_x, work_y, work_width, work_height)
        screenlog(" screen: %s", item)
        screen_sizes = [item]
    else:
        i = 0
        screen_sizes = []
        #GTK2 or GTK3<3.22:
        screenlog("get_screen_sizes(%f, %f) found %s screens", xscale, yscale,
                  n_screens)
        while i < n_screens:
            screen = display.get_screen(i)
            j = 0
            monitors = []
            workareas = []
            #native "get_workareas()" is only valid for a single screen (but describes all the monitors)
            #and it is only implemented on win32 right now
            #other platforms only implement "get_workarea()" instead, which is reported against the screen
            n_monitors = screen.get_n_monitors()
            screenlog(" screen %s has %s monitors", i, n_monitors)
            if n_screens == 1:
                workareas = get_workareas()
                if workareas and len(workareas) != n_monitors:
                    screenlog(" workareas: %s", workareas)
                    screenlog(
                        " number of monitors does not match number of workareas!"
                    )
                    workareas = []
            while j < screen.get_n_monitors():
                geom = screen.get_monitor_geometry(j)
                plug_name = ""
                if hasattr(screen, "get_monitor_plug_name"):
                    plug_name = screen.get_monitor_plug_name(j) or ""
                wmm = -1
                if hasattr(screen, "get_monitor_width_mm"):
                    wmm = screen.get_monitor_width_mm(j)
                hmm = -1
                if hasattr(screen, "get_monitor_height_mm"):
                    hmm = screen.get_monitor_height_mm(j)
                monitor = [
                    plug_name,
                    xs(geom.x),
                    ys(geom.y),
                    xs(geom.width),
                    ys(geom.height), wmm, hmm
                ]
                screenlog(" monitor %s: %s", j, monitor)
                if workareas:
                    w = workareas[j]
                    monitor += list(swork(*w))
                monitors.append(tuple(monitor))
                j += 1
            work_x, work_y, work_width, work_height = swork(
                0, 0, screen.get_width(), screen.get_height())
            workarea = get_workarea()  #pylint: disable=assignment-from-none
            if workarea:
                work_x, work_y, work_width, work_height = swork(*workarea)  #pylint: disable=not-an-iterable
            screenlog(" workarea=%s", workarea)
            item = (screen.make_display_name(), xs(screen.get_width()),
                    ys(screen.get_height()), screen.get_width_mm(),
                    screen.get_height_mm(), monitors, work_x, work_y,
                    work_width, work_height)
            screenlog(" screen %s: %s", i, item)
            screen_sizes.append(item)
            i += 1
    return screen_sizes
Esempio n. 27
0
def load_xdg_menu_data():
    try:
        from xdg.Menu import parse, Menu  #pylint: disable=import-outside-toplevel
    except ImportError:
        log("load_xdg_menu_data()", exc_info=True)
        if first_time("no-python-xdg"):
            log.warn("Warning: cannot use application menu data:")
            log.warn(" no python-xdg module")
        return None
    menu = None
    error = None
    #see ticket #2340,
    #invalid values for XDG_CONFIG_DIRS can cause problems,
    #so try unsetting it if we can't load the menus with it:
    for cd in (False, True):
        with OSEnvContext():
            if cd:
                if not os.environ.pop("XDG_CONFIG_DIRS", ""):
                    #was already unset
                    continue
            #see ticket #2174,
            #things may break if the prefix is not set,
            #and it isn't set when logging in via ssh
            for prefix in (None, "", "gnome-", "kde-"):
                if prefix is not None:
                    os.environ["XDG_MENU_PREFIX"] = prefix
                try:
                    log(
                        "parsing xdg menu data for prefix %r with XDG_CONFIG_DIRS=%s and XDG_MENU_PREFIX=%s",
                        prefix, os.environ.get("XDG_CONFIG_DIRS"),
                        os.environ.get("XDG_MENU_PREFIX"))
                    menu = parse()
                    break
                except Exception as e:
                    log("load_xdg_menu_data()", exc_info=True)
                    error = e
                    menu = None
        if menu:
            break
    if menu is None:
        if error:
            log.error("Error parsing xdg menu data:")
            log.error(" %s", error)
            log.error(" this is either a bug in python-xdg,")
            log.error(" or an invalid system menu configuration")
        return None
    menu_data = {}
    entries = tuple(menu.getEntries())
    log("%s.getEntries()=%s", menu, entries)
    if len(entries) == 1 and entries[0].Submenus:
        entries = entries[0].Submenus
        log("using submenus %s", entries)
    for i, submenu in enumerate(entries):
        if not isinstance(submenu, Menu):
            log("entry '%s' is not a submenu", submenu)
            continue
        name = submenu.getName()
        log("* %-3i %s", i, name)
        if not submenu.Visible:
            log(" submenu '%s' is not visible", name)
            continue
        try:
            md = load_xdg_menu(submenu)
            if md:
                menu_data[name] = md
            else:
                log(" no menu data for %s", name)
        except Exception as e:
            log("load_xdg_menu_data()", exc_info=True)
            log.error("Error loading submenu '%s':", name)
            log.error(" %s", e)
    if LOAD_APPLICATIONS:
        from xdg.Menu import MenuEntry
        entries = {}
        for d in LOAD_APPLICATIONS:
            for f in os.listdir(d):
                if not f.endswith(".desktop"):
                    continue
                try:
                    me = MenuEntry(f, d)
                except Exception:
                    log("failed to load %s from %s", f, d, exc_info=True)
                else:
                    ed = load_xdg_entry(me.DesktopEntry)
                    if not ed:
                        continue
                    name = ed.get("Name")
                    #ensure we don't already have it in another submenu:
                    for menu_category in menu_data.values():
                        if name in menu_category.get("Entries", {}):
                            ed = None
                            break
                    if ed:
                        entries[name] = ed
        log("entries(%s)=%s", LOAD_APPLICATIONS, remove_icons(entries))
        if entries:
            #add an 'Applications' menu if we don't have one:
            md = menu_data.get("Applications")
            if not md:
                md = {
                    "Name": "Applications",
                }
                menu_data["Applications"] = md
            md.setdefault("Entries", {}).update(entries)
    return menu_data
Esempio n. 28
0
    def do_selection_request_event(self, event):
        #an app is requesting clipboard data from us
        log("do_selection_request_event(%s)", event)
        requestor = event.requestor
        if not requestor:
            log.warn(
                "Warning: clipboard selection request without a window, dropped"
            )
            return
        wininfo = self.get_wininfo(requestor.get_xid())
        prop = event.property
        target = str(event.target)
        log("clipboard request for %s from window %#x: %s, target=%s, prop=%s",
            self._selection, requestor.get_xid(), wininfo, target, prop)
        if not target:
            log.warn("Warning: ignoring clipboard request without a TARGET")
            log.warn(" coming from %s", wininfo)
            return
        if not prop:
            log.warn("Warning: ignoring clipboard request without a property")
            log.warn(" coming from %s", wininfo)
            return

        def nodata():
            self.set_selection_response(requestor,
                                        target,
                                        prop,
                                        "STRING",
                                        8,
                                        b"",
                                        time=event.time)

        if not self._enabled:
            nodata()
            return
        if wininfo and wininfo.strip("'") in BLACKLISTED_CLIPBOARD_CLIENTS:
            if first_time("clipboard-blacklisted:%s" % wininfo.strip("'")):
                log.warn(
                    "receiving clipboard requests from blacklisted client %s",
                    wininfo)
                log.warn(" all requests will be silently ignored")
            log("responding with nodata for blacklisted client '%s'", wininfo)
            return
        if not self.owned:
            log.warn("Warning: clipboard selection request received,")
            log.warn(" coming from %s", wininfo)
            log.warn(" but we don't own the selection,")
            log.warn(" sending an empty reply")
            nodata()
            return
        if not self._can_receive:
            log.warn("Warning: clipboard selection request received,")
            log.warn(" coming from %s", wininfo)
            log.warn(" but receiving remote data is disabled,")
            log.warn(" sending an empty reply")
            nodata()
            return
        if must_discard(target):
            log.info("clipboard %s rejecting request for invalid target '%s'",
                     self._selection, target)
            log.info(" coming from %s", wininfo)
            nodata()
            return

        if target == "TARGETS":
            if self.targets:
                log("using existing TARGETS value as response: %s",
                    self.targets)
                xatoms = strings_to_xatoms(self.targets)
                self.set_selection_response(requestor, target, prop, "ATOM",
                                            32, xatoms, event.time)
                return
            if "TARGETS" not in self.remote_requests:
                self.emit("send-clipboard-request", self._selection, "TARGETS")
            #when appending, the time may not be honoured
            #and we may reply with data from an older request
            self.remote_requests.setdefault("TARGETS", []).append(
                (requestor, target, prop, event.time))
            return

        req_target = target
        if self.targets and target not in self.targets:
            if first_time("client-%s-invalidtarget-%s" % (wininfo, target)):
                l = log.info
            else:
                l = log.debug
            l("client %s is requesting an unknown target: '%s'", wininfo,
              target)
            translated_targets = TRANSLATED_TARGETS.get(target, ())
            can_translate = tuple(x for x in translated_targets
                                  if x in self.targets)
            if can_translate:
                req_target = can_translate[0]
                l(" using '%s' instead", req_target)
            else:
                l(" valid targets: %s", csv(self.targets))
                if must_discard_extra(target):
                    l(" dropping the request")
                    nodata()
                    return

        target_data = self.target_data.get(req_target)
        if target_data and self._have_token:
            #we have it already
            dtype, dformat, data = target_data
            dtype = bytestostr(dtype)
            log("setting target data for '%s': %s, %s, %s (%s)", target, dtype,
                dformat, ellipsizer(data), type(data))
            self.set_selection_response(requestor, target, prop, dtype,
                                        dformat, data, event.time)
            return

        waiting = self.remote_requests.setdefault(req_target, [])
        if waiting:
            log("already waiting for '%s' remote request: %s", req_target,
                waiting)
        else:
            self.emit("send-clipboard-request", self._selection, req_target)
        waiting.append((requestor, target, prop, event.time))
Esempio n. 29
0
    def process_draw(self, packet):
        wid, x, y, width, height, encoding, pixels, _, rowstride, client_options = packet[
            1:11]
        #never modify mmap packets
        if encoding in (b"mmap", b"scroll"):
            return True

        client_options = typedict(client_options)
        #we have a proxy video packet:
        rgb_format = client_options.strget("rgb_format", "")
        enclog("proxy draw: encoding=%s, client_options=%s", encoding,
               client_options)

        def send_updated(encoding, compressed_data, updated_client_options):
            #update the packet with actual encoding data used:
            packet[6] = encoding
            packet[7] = compressed_data
            packet[10] = updated_client_options
            enclog("returning %s bytes from %s, options=%s",
                   len(compressed_data), len(pixels), updated_client_options)
            return wid not in self.lost_windows

        def passthrough(strip_alpha=True):
            enclog(
                "proxy draw: %s passthrough (rowstride: %s vs %s, strip alpha=%s)",
                rgb_format, rowstride, client_options.intget("rowstride",
                                                             0), strip_alpha)
            if strip_alpha:
                #passthrough as plain RGB:
                Xindex = rgb_format.upper().find("X")
                if Xindex >= 0 and len(rgb_format) == 4:
                    #force clear alpha (which may be garbage):
                    newdata = bytearray(pixels)
                    for i in range(len(pixels) / 4):
                        newdata[i * 4 + Xindex] = chr(255)
                    packet[9] = client_options.intget("rowstride", 0)
                    cdata = bytes(newdata)
                else:
                    cdata = pixels
                new_client_options = {"rgb_format": rgb_format}
            else:
                #preserve
                cdata = pixels
                new_client_options = client_options
            wrapped = Compressed("%s pixels" % encoding, cdata)
            #rgb32 is always supported by all clients:
            return send_updated("rgb32", wrapped, new_client_options)

        proxy_video = client_options.boolget("proxy", False)
        if PASSTHROUGH_RGB and (encoding in ("rgb32", "rgb24") or proxy_video):
            #we are dealing with rgb data, so we can pass it through:
            return passthrough(proxy_video)
        if not self.video_encoder_types or not client_options or not proxy_video:
            #ensure we don't try to re-compress the pixel data in the network layer:
            #(re-add the "compressed" marker that gets lost when we re-assemble packets)
            packet[7] = Compressed("%s pixels" % encoding, packet[7])
            return True

        #video encoding: find existing encoder
        ve = self.video_encoders.get(wid)
        if ve:
            if ve in self.lost_windows:
                #we cannot clean the video encoder here, there may be more frames queue up
                #"lost-window" in encode_loop will take care of it safely
                return False
            #we must verify that the encoder is still valid
            #and scrap it if not (ie: when window is resized)
            if ve.get_width() != width or ve.get_height() != height:
                enclog(
                    "closing existing video encoder %s because dimensions have changed from %sx%s to %sx%s",
                    ve, ve.get_width(), ve.get_height(), width, height)
                ve.clean()
                ve = None
            elif ve.get_encoding() != encoding:
                enclog(
                    "closing existing video encoder %s because encoding has changed from %s to %s",
                    ve.get_encoding(), encoding)
                ve.clean()
                ve = None
        #scaling and depth are proxy-encoder attributes:
        scaling = client_options.inttupleget("scaling", (1, 1))
        depth = client_options.intget("depth", 24)
        rowstride = client_options.intget("rowstride", rowstride)
        quality = client_options.intget("quality", -1)
        speed = client_options.intget("speed", -1)
        timestamp = client_options.intget("timestamp")

        image = ImageWrapper(x,
                             y,
                             width,
                             height,
                             pixels,
                             rgb_format,
                             depth,
                             rowstride,
                             planes=ImageWrapper.PACKED)
        if timestamp is not None:
            image.set_timestamp(timestamp)

        #the encoder options are passed through:
        encoder_options = client_options.dictget("options", {})
        if not ve:
            #make a new video encoder:
            spec = self._find_video_encoder(encoding, rgb_format)
            if spec is None:
                #no video encoder!
                enc_pillow = get_codec("enc_pillow")
                if not enc_pillow:
                    if first_time("no-video-no-PIL-%s" % rgb_format):
                        enclog.warn(
                            "Warning: no video encoder found for rgb format %s",
                            rgb_format)
                        enclog.warn(" sending as plain RGB")
                    return passthrough(True)
                enclog("no video encoder available: sending as jpeg")
                coding, compressed_data, client_options = enc_pillow.encode(
                    "jpeg", image, quality, speed, False)[:3]
                return send_updated(coding, compressed_data, client_options)

            enclog("creating new video encoder %s for window %s", spec, wid)
            ve = spec.make_instance()
            #dst_formats is specified with first frame only:
            dst_formats = client_options.strtupleget("dst_formats")
            if dst_formats is not None:
                #save it in case we timeout the video encoder,
                #so we can instantiate it again, even from a frame no>1
                self.video_encoders_dst_formats = dst_formats
            else:
                if not self.video_encoders_dst_formats:
                    raise Exception(
                        "BUG: dst_formats not specified for proxy and we don't have it either"
                    )
                dst_formats = self.video_encoders_dst_formats
            ve.init_context(width, height, rgb_format, dst_formats, encoding,
                            quality, speed, scaling, {})
            self.video_encoders[wid] = ve
            self.video_encoders_last_used_time[wid] = monotonic_time(
            )  #just to make sure this is always set
        #actual video compression:
        enclog("proxy compression using %s with quality=%s, speed=%s", ve,
               quality, speed)
        data, out_options = ve.compress_image(image, quality, speed,
                                              encoder_options)
        #pass through some options if we don't have them from the encoder
        #(maybe we should also use the "pts" from the real server?)
        for k in ("timestamp", "rgb_format", "depth", "csc"):
            if k not in out_options and k in client_options:
                out_options[k] = client_options[k]
        self.video_encoders_last_used_time[wid] = monotonic_time()
        return send_updated(ve.get_encoding(), Compressed(encoding, data),
                            out_options)