diff --git a/src/xpra/client/client_tray.py b/src/xpra/client/client_tray.py index 8a306ef07a..cd7f6b5003 100644 --- a/src/xpra/client/client_tray.py +++ b/src/xpra/client/client_tray.py @@ -127,7 +127,7 @@ def new_backing(self, w, h): def update_metadata(self, metadata): log("%s.update_metadata(%s)", self, metadata) - def update_icon(self, width, height, coding, data): + def update_icon(self, img): #this is the window icon... not the tray icon! pass diff --git a/src/xpra/client/client_window_base.py b/src/xpra/client/client_window_base.py index cf9b4104cb..053dc4de5d 100644 --- a/src/xpra/client/client_window_base.py +++ b/src/xpra/client/client_window_base.py @@ -153,24 +153,24 @@ def reset_icon(self): if current_icon: self.update_icon(*current_icon) - def update_icon(self, width, height, coding, data): - raise Exception("override me!") + def update_icon(self, img): + raise NotImplementedError("override me!") def is_realized(self): - raise Exception("override me!") + raise NotImplementedError("override me!") def apply_transient_for(self, wid): - raise Exception("override me!") + raise NotImplementedError("override me!") def paint_spinner(self, context, area): - raise Exception("override me!") + raise NotImplementedError("override me!") def _pointer_modifiers(self, event): - raise Exception("override me!") + raise NotImplementedError("override me!") def xget_u32_property(self, target, name): - raise Exception("override me!") + raise NotImplementedError("override me!") def is_OR(self): diff --git a/src/xpra/client/gtk_base/gtk_client_window_base.py b/src/xpra/client/gtk_base/gtk_client_window_base.py index bb2eb619a8..e1a650851a 100644 --- a/src/xpra/client/gtk_base/gtk_client_window_base.py +++ b/src/xpra/client/gtk_base/gtk_client_window_base.py @@ -25,7 +25,7 @@ grablog = Logger("grab") -from xpra.os_util import memoryview_to_bytes, bytestostr, WIN32, OSX, POSIX, PYTHON3 +from xpra.os_util import bytestostr, WIN32, OSX, POSIX, PYTHON3 from xpra.util import (AdHocStruct, typedict, envint, envbool, nonl, WORKSPACE_UNSET, WORKSPACE_ALL, WORKSPACE_NAMES, MOVERESIZE_DIRECTION_STRING, SOURCE_INDICATION_STRING, MOVERESIZE_CANCEL, @@ -41,7 +41,6 @@ from xpra.gtk_common.keymap import KEY_TRANSLATIONS from xpra.client.client_window_base import ClientWindowBase from xpra.platform.gui import set_fullscreen_monitors, set_shaded -from xpra.codecs.argb.argb import unpremultiply_argb, bgra_to_rgba #@UnresolvedImport from xpra.platform.gui import add_window_hooks, remove_window_hooks gtk = import_gtk() @@ -83,6 +82,7 @@ BREAK_MOVERESIZE = os.environ.get("XPRA_BREAK_MOVERESIZE", "Escape").split(",") MOVERESIZE_X11 = envbool("XPRA_MOVERESIZE_X11", POSIX) +ICON_OVERLAY = envbool("XPRA_ICON_OVERLAY", True) OSX_FOCUS_WORKAROUND = envbool("XPRA_OSX_FOCUS_WORKAROUND", True) SAVE_WINDOW_ICONS = envbool("XPRA_SAVE_WINDOW_ICONS", False) UNDECORATED_TRANSIENT_IS_OR = envint("XPRA_UNDECORATED_TRANSIENT_IS_OR", 1) @@ -1519,22 +1519,11 @@ def do_scroll_event(self, event): self._button_action(button_mapping, event, False) - def update_icon(self, width, height, coding, data): - self._current_icon = (width, height, coding, data) - coding = bytestostr(coding) - iconlog("%s.update_icon(%s, %s, %s, %s bytes)", self, width, height, coding, len(data)) - if coding == "premult_argb32": #we usually cannot do in-place and this is not performance critical - data = unpremultiply_argb(data) - rgba = memoryview_to_bytes(memoryview(bgra_to_rgba(data))) - pixbuf = get_pixbuf_from_data(rgba, True, width, height, width*4) - else: - loader = PixbufLoader() - loader.write(data) - loader.close() - pixbuf = loader.get_pixbuf() + def update_icon(self, img): + self._current_icon = img + 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) iconlog("%s.set_icon(%s)", self, pixbuf) self.set_icon(pixbuf) - if SAVE_WINDOW_ICONS: - filename = "client-window-%i-icon-%i.png" % (self._id, int(time.time())) - pixbuf.save(filename, "png") - iconlog("client window icon saved to %s", filename) diff --git a/src/xpra/client/ui_client_base.py b/src/xpra/client/ui_client_base.py index 5a530858ad..e2ff75e2d6 100644 --- a/src/xpra/client/ui_client_base.py +++ b/src/xpra/client/ui_client_base.py @@ -63,7 +63,7 @@ from xpra.net.compression import Compressed from xpra.child_reaper import reaper_cleanup from xpra.make_thread import make_thread -from xpra.os_util import BytesIOClass, Queue, platform_name, get_machine_id, get_user_uuid, bytestostr, monotonic_time, strtobytes, OSX, POSIX +from xpra.os_util import BytesIOClass, Queue, platform_name, get_machine_id, get_user_uuid, bytestostr, monotonic_time, strtobytes, memoryview_to_bytes, OSX, POSIX from xpra.util import nonl, std, iround, envint, envfloat, envbool, AtomicInteger, log_screen_sizes, typedict, updict, csv, engs, CLIENT_EXIT, XPRA_APP_ID from xpra.version_util import get_version_info_full, get_platform_info try: @@ -111,6 +111,9 @@ def add_legacy_names(codecs): TRAY_DELAY = envint("XPRA_TRAY_DELAY", 0) +ICON_OVERLAY = envbool("XPRA_ICON_OVERLAY", True) +SAVE_WINDOW_ICONS = envbool("XPRA_SAVE_WINDOW_ICONS", False) + WEBCAM_ALLOW_VIRTUAL = envbool("XPRA_WEBCAM_ALLOW_VIRTUAL", False) WEBCAM_TARGET_FPS = max(1, min(50, envint("XPRA_WEBCAM_FPS", 20))) @@ -3440,11 +3443,46 @@ def _process_window_metadata(self, packet): window.update_metadata(metadata) def _process_window_icon(self, packet): - wid, w, h, pixel_format, data = packet[1:6] + wid, w, h, coding, data = packet[1:6] + img = self._window_icon_image(wid, w, h, coding, data) window = self._id_to_window.get(wid) - iconlog("_process_window_icon(%s, %s, %s, %s, %s bytes) window=%s", wid, w, h, pixel_format, len(data), window) - if window: - window.update_icon(w, h, pixel_format, data) + iconlog("_process_window_icon(%s, %s, %s, %s, %s bytes) image=%s, window=%s", wid, w, h, coding, len(data), img, window) + if window and img: + window.update_icon(img) + + def _window_icon_image(self, wid, width, height, coding, data): + #convert the data into a pillow image, + #adding the icon overlay (if enabled) + PIL = get_codec("PIL") + assert PIL.Image, "PIL.Image not found" + coding = bytestostr(coding) + iconlog("%s.update_icon(%s, %s, %s, %s bytes)", self, width, height, coding, len(data)) + if coding == "premult_argb32": #we usually cannot do in-place and this is not performance critical + from xpra.codecs.argb.argb import unpremultiply_argb #@UnresolvedImport + data = unpremultiply_argb(data) + rowstride = width*4 + img = PIL.Image.frombytes("RGBA", (width,height), memoryview_to_bytes(data), "raw", "BGRA", rowstride, 1) + has_alpha = True + else: + buf = BytesIOClass(data) + img = PIL.Image.open(buf) + assert img.mode in ("RGB", "RGBA"), "invalid image mode: %s" % img.mode + has_alpha = img.mode=="RGBA" + rowstride = width * (3+int(has_alpha)) + if ICON_OVERLAY: + xpra_icon_filename = get_icon_filename("xpra") + xpra_icon = PIL.Image.open(xpra_icon_filename) + half = xpra_icon.resize((width//2, height//2)) + xpra_corner = PIL.Image.new("RGBA", (width, height)) + xpra_corner.paste(half, (width//2, height//2, width, height)) + composite = PIL.Image.alpha_composite(img, xpra_corner) + img = composite + if SAVE_WINDOW_ICONS: + filename = "client-window-%i-icon-%i.png" % (wid, int(time.time())) + img.save(filename, "png") + iconlog("client window icon saved to %s", filename) + return img + def _process_configure_override_redirect(self, packet): wid, x, y, w, h = packet[1:6] diff --git a/src/xpra/server/window/window_source.py b/src/xpra/server/window/window_source.py index 07d1e229d1..1f0fb25d38 100644 --- a/src/xpra/server/window/window_source.py +++ b/src/xpra/server/window/window_source.py @@ -33,6 +33,9 @@ MAX_PIXELS_PREFER_RGB = envint("XPRA_MAX_PIXELS_PREFER_RGB", 4096) +ARGB_ICONS = envbool("XPRA_ARGB_ICONS", True) +PNG_ICONS = envbool("XPRA_PNG_ICONS", True) + DELTA = envbool("XPRA_DELTA", True) MIN_DELTA_SIZE = envint("XPRA_MIN_DELTA_SIZE", 1024) MAX_DELTA_SIZE = envint("XPRA_MAX_DELTA_SIZE", 32768) @@ -614,8 +617,8 @@ def compress_and_send_window_icon(self): #use png if supported and if "premult_argb32" is not supported by the client (ie: html5) #or if we must downscale it (bigger than what the client is willing to deal with), #or if we want to save window icons - has_png = PIL and ("png" in self.window_icon_encodings) - has_premult = "premult_argb32" in self.window_icon_encodings + has_png = PIL and PNG_ICONS and ("png" in self.window_icon_encodings) + has_premult = ARGB_ICONS and "premult_argb32" in self.window_icon_encodings use_png = has_png and (SAVE_WINDOW_ICONS or w>max_w or h>max_h or w*h>=1024 or (not has_premult) or (pixel_format!="BGRA")) iconlog("compress_and_send_window_icon: %sx%s (max-size=%s, standard-size=%s), sending as png=%s, has_png=%s, has_premult=%s, pixel_format=%s", w, h, self.window_icon_max_size, self.window_icon_size, use_png, has_png, has_premult, pixel_format) if use_png: