diff --git a/src/xpra/client/ui_client_base.py b/src/xpra/client/ui_client_base.py index 804833382f..621b066042 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, memoryview_to_bytes, 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, WIN32 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,7 +111,7 @@ def add_legacy_names(codecs): TRAY_DELAY = envint("XPRA_TRAY_DELAY", 0) -ICON_OVERLAY = envbool("XPRA_ICON_OVERLAY", True) +ICON_OVERLAY = envint("XPRA_ICON_OVERLAY", 50) SAVE_WINDOW_ICONS = envbool("XPRA_SAVE_WINDOW_ICONS", False) WEBCAM_ALLOW_VIRTUAL = envbool("XPRA_WEBCAM_ALLOW_VIRTUAL", False) @@ -329,6 +329,7 @@ def __init__(self): self.clipboard_helper = None self.menu_helper = None self.tray = None + self.overlay_image = None self.notifier = None self.in_remote_logging = False self.local_logging = None @@ -452,6 +453,13 @@ def setup_xpra_tray(*args): else: #show shortly after the main loop starts running: self.timeout_add(TRAY_DELAY, setup_xpra_tray) + if ICON_OVERLAY>0 and ICON_OVERLAY<=100: + icon_filename = get_icon_filename("xpra") + try: + from PIL import Image + self.overlay_image = Image.open(icon_filename) + except Exception as e: + log("init_ui failed to load overlay icon '%s': %s", icon_filename, e) notifylog("client_supports_notifications=%s", self.client_supports_notifications) if self.client_supports_notifications: @@ -3449,33 +3457,65 @@ def _process_window_icon(self, packet): 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) + self.set_tray_icon() + + def set_tray_icon(self): + #find all the window icons, + #and if they are all using the same one, then use it as tray icon + #otherwise use the default icon + if not self.tray: + return + if WIN32: + #the icon ends up looking garbled on win32, + #and we somehow also lose the settings that can keep us in the visible systray list + #so don't bother + return + windows = tuple(w for w in self._window_to_id.keys() if not w.is_tray()) + #get all the icons: + icons = tuple(getattr(w, "_current_icon", None) for w in windows) + if icons and not any(True for icon in icons if icon is None): + icon = icons[0] + for i in icons[1:]: + if i!=icon: + #found a different icon + icon = None + break + if icon: + has_alpha = icon.mode=="RGBA" + width, height = icon.size + rowstride = width * (3+int(has_alpha)) + rgb_data = icon.tobytes("raw") + self.tray.set_icon_from_data(rgb_data, has_alpha, width, height, rowstride) + return + #this sets the default icon (badly named function!) + self.tray.set_icon() 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" + from PIL import Image 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) + img = 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) + img = 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), PIL.Image.ANTIALIAS) - 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) + if self.overlay_image: + assert ICON_OVERLAY>0 and ICON_OVERLAY<=100 + overlay_width = max(1, width*ICON_OVERLAY//100) + overlay_height = max(1, height*ICON_OVERLAY//100) + half = self.overlay_image.resize((overlay_width, overlay_height), Image.ANTIALIAS) + xpra_corner = Image.new("RGBA", (width, height)) + xpra_corner.paste(half, (width-overlay_width, height-overlay_height, width, height)) + composite = Image.alpha_composite(img, xpra_corner) img = composite if SAVE_WINDOW_ICONS: filename = "client-window-%i-icon-%i.png" % (wid, int(time.time())) @@ -3503,6 +3543,8 @@ def _process_lost_window(self, packet): self.destroy_window(wid, window) if len(self._id_to_window)==0: windowlog("last window gone, clearing key repeat") + self.set_tray_icon() + def destroy_window(self, wid, window): windowlog("destroy_window(%s, %s)", wid, window)