def parse_hints(self, h) -> dict: hints = {} for x in ("action-icons", "category", "desktop-entry", "resident", "transient", "x", "y", "urgency"): v = h.get(x) if v is not None: hints[x] = native_to_dbus(v) image_data = h.get("image-data") if image_data and bytestostr(image_data[0]) == "png": try: from xpra.codecs.pillow.decoder import open_only img_data = image_data[3] img = open_only(img_data, ("png", )) w, h = img.size channels = len(img.mode) rowstride = w * channels has_alpha = img.mode == "RGBA" pixel_data = bytearray(img.tobytes("raw", img.mode)) log.info("pixel_data=%s", type(pixel_data)) args = w, h, rowstride, has_alpha, 8, channels, pixel_data hints["image-data"] = tuple(native_to_dbus(x) for x in args) #hints["image-data"] = args except Exception as e: log("parse_hints(%s) error on image-data=%s", h, image_data, exc_info=True) log.error("Error parsing notification image:") log.error(" %s", e) log("parse_hints(%s)=%s", h, hints) #return dbus.types.Dictionary(hints, signature="sv") return hints
def set_image_data(self, dtype, data): img_type = dtype.split("/")[1] #ie: "png" from xpra.codecs.pillow.decoder import open_only img = open_only(data, (img_type, )) for img_type, macos_types in { "png": [NSPasteboardTypePNG, "image/png"], "tiff": [NSTIFFPboardType, "image/tiff"], "jpeg": ["public.jpeg", "image/jpeg"], }.items(): try: save_img = img if img_type == "jpeg" and img.mode == "RGBA": save_img = img.convert("RGB") buf = BytesIO() save_img.save(buf, img_type) data = buf.getvalue() buf.close() self.pasteboard.clearContents() nsdata = NSData.dataWithData_(data) for t in macos_types: r = self.pasteboard.setData_forType_(nsdata, t) log("set '%s' data type: %s", t, r) except Exception as e: log("set_image_data(%s, ..)", dtype, exc_info=True) log.error("Error: failed to copy %s image to clipboard", img_type) log.error(" %s", e)
def filter_data(self, dtype=None, dformat=None, data=None, trusted=False, output_dtype=None): log("filter_data(%s, %s, %i %s, %s, %s)", dtype, dformat, len(data), type(data), trusted, output_dtype) if not data: return data IMAGE_OVERLAY = os.environ.get("XPRA_CLIPBOARD_IMAGE_OVERLAY", None) if IMAGE_OVERLAY and not os.path.exists(IMAGE_OVERLAY): IMAGE_OVERLAY = None IMAGE_STAMP = envbool("XPRA_CLIPBOARD_IMAGE_STAMP", False) SANITIZE_IMAGES = envbool("XPRA_SANITIZE_IMAGES", True) if dtype in ("image/png", "image/jpeg", "image/tiff") and ( (output_dtype is not None and dtype != output_dtype) or IMAGE_STAMP or IMAGE_OVERLAY or (SANITIZE_IMAGES and not trusted)): from xpra.codecs.pillow.decoder import open_only img_type = dtype.split("/")[-1] img = open_only(data, (img_type, )) has_alpha = img.mode == "RGBA" if not has_alpha and IMAGE_OVERLAY: img = img.convert("RGBA") w, h = img.size if IMAGE_OVERLAY: from PIL import Image #@UnresolvedImport overlay = Image.open(IMAGE_OVERLAY) if overlay.mode != "RGBA": log.warn("Warning: cannot use overlay image '%s'", IMAGE_OVERLAY) log.warn(" invalid mode '%s'", overlay.mode) else: log("adding clipboard image overlay to %s", dtype) overlay_resized = overlay.resize((w, h), Image.ANTIALIAS) composite = Image.alpha_composite(img, overlay_resized) if not has_alpha and img.mode == "RGBA": composite = composite.convert("RGB") img = composite if IMAGE_STAMP: log("adding clipboard image stamp to %s", dtype) from datetime import datetime from PIL import ImageDraw img_draw = ImageDraw.Draw(img) w, h = img.size img_draw.text((10, max(0, h // 2 - 16)), 'via Xpra, %s' % datetime.now().isoformat(), fill='black') #now save it: img_type = (output_dtype or dtype).split("/")[-1] buf = BytesIO() img.save(buf, img_type.upper()) #ie: "PNG" data = buf.getvalue() buf.close() return data
def notify(hwnd, app_id, title, message, timeout=5000, icon=None): log("notify%s", (hwnd, app_id, title, message, timeout, icon)) if timeout <= 0: timeout = 5000 szInfo = chop_string(message, 255, False) #prevent overflow szInfoTitle = chop_string(title, 63) hicon = 0 if icon: try: from PIL import Image from xpra.codecs.pillow.decoder import open_only w, h, data = icon[1:4] img = open_only(data) from xpra.platform.win32.win32_NotifyIcon import image_to_ICONINFO iw = GetSystemMetrics(SM_CXSMICON) ih = GetSystemMetrics(SM_CYSMICON) if w != iw or h != ih: img = img.resize((iw, ih), Image.ANTIALIAS) log("notification icon resized to %s", img.size) hicon = image_to_ICONINFO(img) log("notify: image_to_ICONINFO(%s)=%#x", img, hicon) except Exception as e: log("notify%s", (hwnd, app_id, title, message, timeout, icon), exc_info=True) log.error("Error: failed to set notification icon:") log.error(" %s", e) from xpra.platform.win32.win32_NotifyIcon import Shell_NotifyIconA, XPRA_GUID, getNOTIFYICONDATAClass nc = getNOTIFYICONDATAClass(tip_size=128) nid = nc() nid.cbSize = sizeof(nc) nid.hWnd = hwnd nid.uID = app_id nid.uFlags = NIF_INFO nid.guidItem = XPRA_GUID try: nid.szInfo = szInfo except: nid.szInfo = szInfo.decode() v = chop_string(title, 63) try: nid.szInfoTitle = szInfoTitle except: nid.szInfoTitle = szInfoTitle.decode() nid.uVersion = timeout nid.dwInfoFlags = NIIF_INFO if hicon: nid.hIcon = nid.hBalloonIcon = hicon nid.dwInfoFlags = NIIF_USER Shell_NotifyIconA(NIM_MODIFY, byref(nid)) log("notify using %s", Shell_NotifyIconA)
def get_appimage(app_name, icondata=None, menu_icon_size=24): pixbuf = None if app_name and not icondata: #try to load from our icons: nstr = bytestostr(app_name).lower() icon_filename = os.path.join(get_icon_dir(), "%s.png" % nstr) if os.path.exists(icon_filename): pixbuf = GdkPixbuf.Pixbuf.new_from_file(icon_filename) def err(e): log("failed to load icon", exc_info=True) log.error("Error: failed to load icon data for '%s':", bytestostr(app_name)) log.error(" %s", e) log.error(" data=%s", repr_ellipsized(icondata)) if not pixbuf and icondata: #gtk pixbuf loader: try: pixbuf = load_pixbuf(icondata) except Exception as e: log("pixbuf loader failed", exc_info=True) if re.findall(INKSCAPE_RE, icondata): try: pixbuf = load_pixbuf(re.sub(INKSCAPE_RE, b"", icondata)) e = None except Exception: #there is almost no chance pillow will be able to load it #(it doesn't even have svg support at time of writing) #so don't bother showing another error for the same data: icondata = None if e: err(e) if not pixbuf and icondata: #let's try pillow: try: from xpra.codecs.pillow.decoder import open_only #pylint: disable=import-outside-toplevel img = open_only(icondata) has_alpha = img.mode == "RGBA" width, height = img.size rowstride = width * (3 + int(has_alpha)) pixbuf = get_pixbuf_from_data(img.tobytes(), has_alpha, width, height, rowstride) return scaled_image(pixbuf, icon_size=menu_icon_size) except Exception: err(e) if pixbuf: return scaled_image(pixbuf, icon_size=menu_icon_size) return None
def filter_data(self, target, dtype=None, dformat=None, data=None): log("filter_data(%s, %s, %s, ..)", target, dtype, dformat) IMAGE_OVERLAY = os.environ.get("XPRA_CLIPBOARD_IMAGE_OVERLAY", None) if IMAGE_OVERLAY and not os.path.exists(IMAGE_OVERLAY): IMAGE_OVERLAY = None IMAGE_STAMP = envbool("XPRA_CLIPBOARD_IMAGE_STAMP", True) if dtype in ("image/png", ) and (IMAGE_STAMP or IMAGE_OVERLAY): from xpra.codecs.pillow.decoder import open_only img = open_only(data, ("png", )) has_alpha = img.mode == "RGBA" if not has_alpha and IMAGE_OVERLAY: img = img.convert("RGBA") w, h = img.size if IMAGE_OVERLAY: from PIL import Image #@UnresolvedImport overlay = Image.open(IMAGE_OVERLAY) if overlay.mode != "RGBA": log.warn("Warning: cannot use overlay image '%s'", IMAGE_OVERLAY) log.warn(" invalid mode '%s'", overlay.mode) else: log("adding clipboard image overlay to %s", dtype) overlay_resized = overlay.resize((w, h), Image.ANTIALIAS) composite = Image.alpha_composite(img, overlay_resized) if not has_alpha and img.mode == "RGBA": composite = composite.convert("RGB") img = composite if IMAGE_STAMP: log("adding clipboard image stamp to %s", dtype) from datetime import datetime from PIL import ImageDraw img_draw = ImageDraw.Draw(img) w, h = img.size img_draw.text((10, max(0, h // 2 - 16)), 'via Xpra, %s' % datetime.now().isoformat(), fill='black') buf = BytesIO() img.save(buf, "PNG") data = buf.getvalue() buf.close() return data
def notify(hwnd, app_id, title, message, timeout=5000, icon=None): nid = PyNOTIFYICONDATA() nid.hWnd = hwnd nid.uID = app_id nid.uFlags = NIF_INFO nid.guidItem = XPRA_GUID_BYTES nid.szInfo = chop_string(message, 255, False) #prevent overflow nid.szInfoTitle = chop_string(title, 63) if timeout <= 0: timeout = 5000 nid.uTimeoutOrVersion = timeout #if no icon is supplied, we can use: # NIIF_INFO, NIIF_WARNING or NIIF_ERROR nid.dwInfoFlags = NIIF_INFO if icon: try: from PIL import Image from xpra.codecs.pillow.decoder import open_only w, h, data = icon[1:4] img = open_only(data) from xpra.platform.win32.win32_NotifyIcon import image_to_ICONINFO iw = GetSystemMetrics(SM_CXSMICON) ih = GetSystemMetrics(SM_CYSMICON) if w != iw or h != ih: img = img.resize((iw, ih), Image.ANTIALIAS) log("notification icon resized to %s", img.size) hicon = image_to_ICONINFO(img) log("notify: image_to_ICONINFO(%s)=%#x", img, hicon) nid.hIcon = hicon nid.hBalloonIcon = hicon except Exception as e: log("notify%s", (hwnd, app_id, title, message, timeout, icon), exc_info=True) log.error("Error: failed to set notification icon:") log.error(" %s", e) else: nid.dwInfoFlags = NIIF_USER Shell_NotifyIcon = windll.shell32.Shell_NotifyIcon Shell_NotifyIcon(NIM_MODIFY, nid.pack()) log("notify using %s", Shell_NotifyIcon)
def get_appimage(app_name, icondata=None, menu_icon_size=24): pixbuf = None if app_name and not icondata: #try to load from our icons: nstr = bytestostr(app_name).lower() icon_filename = os.path.join(get_icon_dir(), "%s.png" % nstr) if os.path.exists(icon_filename): pixbuf = GdkPixbuf.Pixbuf.new_from_file(icon_filename) if not pixbuf and icondata: #gtk pixbuf loader: try: loader = GdkPixbuf.PixbufLoader() loader.write(icondata) loader.close() pixbuf = loader.get_pixbuf() except Exception as e: log("pixbuf loader failed", exc_info=True) log.error("Error: failed to load icon data for '%s':", bytestostr(app_name)) log.error(" %s", e) log.error(" data=%s", repr_ellipsized(icondata)) if not pixbuf and icondata: #let's try pillow: try: from xpra.codecs.pillow.decoder import open_only img = open_only(icondata) has_alpha = img.mode == "RGBA" width, height = img.size rowstride = width * (3 + int(has_alpha)) pixbuf = get_pixbuf_from_data(img.tobytes(), has_alpha, width, height, rowstride) return scaled_image(pixbuf, icon_size=menu_icon_size) except Exception: log.error("Error: failed to load icon data for %s", bytestostr(app_name), exc_info=True) log.error(" data=%s", repr_ellipsized(icondata)) if pixbuf: return scaled_image(pixbuf, icon_size=menu_icon_size) return None
def process_webcam_frame(self, device_id, frame_no, encoding, w, h, data): webcam = self.webcam_forwarding_devices.get(device_id) log( "process_webcam_frame: device %s, frame no %i: %s %ix%i, %i bytes, webcam=%s", device_id, frame_no, encoding, w, h, len(data), webcam) assert encoding and w and h and data if not webcam: log.error("Error: webcam forwarding is not active, dropping frame") self.send_webcam_stop(device_id, "not started") return False try: from xpra.codecs.pillow.decoder import open_only assert encoding in self.webcam_encodings, "invalid encoding specified: %s (must be one of %s)" % ( encoding, self.webcam_encodings) rgb_pixel_format = "BGRX" #BGRX img = open_only(data, (encoding, )) pixels = img.tobytes('raw', rgb_pixel_format) from xpra.codecs.image_wrapper import ImageWrapper bgrx_image = ImageWrapper(0, 0, w, h, pixels, rgb_pixel_format, 32, w * 4, planes=ImageWrapper.PACKED) src_format = webcam.get_src_format() if not src_format: #closed / closing return False #one of those two should be present try: csc_mod = "csc_libyuv" from xpra.codecs.csc_libyuv.colorspace_converter import ( #@UnresolvedImport get_input_colorspaces, get_output_colorspaces, ColorspaceConverter, ) except ImportError: self.send_webcam_stop(device_id, "no csc module") return False try: assert rgb_pixel_format in get_input_colorspaces( ), "unsupported RGB pixel format %s" % rgb_pixel_format assert src_format in get_output_colorspaces( rgb_pixel_format ), "unsupported output colourspace format %s" % src_format except Exception as e: log.error("Error: cannot convert %s to %s using %s:", rgb_pixel_format, src_format, csc_mod) log.error(" input-colorspaces: %s", csv(get_input_colorspaces())) log.error(" output-colorspaces: %s", csv(get_output_colorspaces(rgb_pixel_format))) self.send_webcam_stop(device_id, "csc format error") return False tw = webcam.get_width() th = webcam.get_height() csc = ColorspaceConverter() csc.init_context(w, h, rgb_pixel_format, tw, th, src_format) image = csc.convert_image(bgrx_image) webcam.push_image(image) #tell the client all is good: self.send_webcam_ack(device_id, frame_no) return True except Exception as e: log("error on %ix%i frame %i using encoding %s", w, h, frame_no, encoding, exc_info=True) log.error("Error processing webcam frame:") msg = str(e) if not msg: msg = "unknown error" log.error(" %s error" % webcam, exc_info=True) log.error(" %s", msg) self.send_webcam_stop(device_id, msg) self.stop_virtual_webcam(device_id) return False