def make_cursor(self, cursor_data): #if present, try cursor ny name: display = display_get_default() if len(cursor_data)>=9 and cursor_types: cursor_name = bytestostr(cursor_data[8]) if cursor_name: gdk_cursor = cursor_types.get(cursor_name.upper()) if gdk_cursor is not None: cursorlog("setting new cursor by name: %s=%s", cursor_name, gdk_cursor) return new_Cursor_for_display(display, gdk_cursor) else: global missing_cursor_names if cursor_name not in missing_cursor_names: cursorlog("cursor name '%s' not found", cursor_name) missing_cursor_names.add(cursor_name) #create cursor from the pixel data: w, h, xhot, yhot, serial, pixels = cursor_data[2:8] if len(pixels)<w*h*4: import binascii cursorlog.warn("not enough pixels provided in cursor data: %s needed and only %s bytes found (%s)", w*h*4, len(pixels), binascii.hexlify(pixels)[:100]) return pixbuf = get_pixbuf_from_data(pixels, True, w, h, w*4) x = max(0, min(xhot, w-1)) y = max(0, min(yhot, h-1)) csize = display.get_default_cursor_size() cmaxw, cmaxh = display.get_maximal_cursor_size() if len(cursor_data)>=11: ssize = cursor_data[9] smax = cursor_data[10] cursorlog("server cursor sizes: default=%s, max=%s", ssize, smax) cursorlog("new cursor at %s,%s with serial=%s, dimensions: %sx%s, len(pixels)=%s, default cursor size is %s, maximum=%s", xhot,yhot, serial, w,h, len(pixels), csize, (cmaxw, cmaxh)) fw, fh = get_fixed_cursor_size() if fw>0 and fh>0 and (w!=fw or h!=fh): #OS wants a fixed cursor size! (win32 does, and GTK doesn't do this for us) if w<=fw and h<=fh: cursorlog("pasting cursor of size %ix%i onto clear pixbuf of size %ix%i", w, h, fw, fh) cursor_pixbuf = get_pixbuf_from_data("\0"*fw*fh*4, True, fw, fh, fw*4) pixbuf.copy_area(0, 0, w, h, cursor_pixbuf, 0, 0) else: cursorlog("scaling cursor from %ix%i to fixed OS size %ix%i", w, h, fw, fh) cursor_pixbuf = pixbuf.scale_simple(fw, fh, INTERP_BILINEAR) xratio, yratio = float(w)/fw, float(h)/fh x, y = int(x/xratio), int(y/yratio) elif w>cmaxw or h>cmaxh or (csize>0 and (csize<w or csize<h)): ratio = max(float(w)/cmaxw, float(h)/cmaxh, float(max(w,h))/csize) x, y, w, h = int(x/ratio), int(y/ratio), int(w/ratio), int(h/ratio) cursorlog("downscaling cursor %s by %.2f: %sx%s", pixbuf, ratio, w, h) cursor_pixbuf = pixbuf.scale_simple(w, h, INTERP_BILINEAR) else: cursor_pixbuf = pixbuf #clamp to pixbuf size: w = cursor_pixbuf.get_width() h = cursor_pixbuf.get_height() x = max(0, min(x, w-1)) y = max(0, min(y, h-1)) return new_Cursor_from_pixbuf(display, cursor_pixbuf, x, y)
def set_command(self, command): if not HAS_X11_BINDINGS: return v = command if type(command)!=unicode: v = bytestostr(command) try: v = v.decode("utf8") except: pass def do_set_command(): metalog("do_set_command() str(%s)=%s (type=%s)", command, v, type(command)) prop_set(self.get_window(), "WM_COMMAND", "latin1", v) self.when_realized("command", do_set_command)
def update_icon(self, width, height, coding, data): log("%s.update_icon(%s, %s, %s, %s bytes)", self, width, height, coding, len(data)) coding = bytestostr(coding) if coding == "premult_argb32": if unpremultiply_argb is None: #we could use PIL here with mode 'RGBa' log.warn("cannot process premult_argb32 icon without the argb module") return #we usually cannot do in-place and this is not performance critical data = byte_buffer_to_buffer(unpremultiply_argb(data)) pixbuf = pixbuf_new_from_data(data, COLORSPACE_RGB, True, 8, width, height, width*4) else: loader = PixbufLoader() loader.write(data) loader.close() pixbuf = loader.get_pixbuf() log("%s.set_icon(%s)", self, pixbuf) self.set_icon(pixbuf)
def pver(v): if type(v) in (tuple, list): s = "" for i in range(len(v)): if i>0: #dot seperated numbers if type(v[i-1])==int: s += "." else: s += ", " s += str(v[i]) return s if type(v)==bytes: from xpra.util import bytestostr v = bytestostr(v) if type(v)==str and v.startswith("v"): return v[1:] return str(v)
def pver(v): if type(v) in (tuple, list): s = "" for i in range(len(v)): if i > 0: #dot seperated numbers if type(v[i - 1]) == int: s += "." else: s += ", " s += str(v[i]) return s if type(v) == bytes: from xpra.util import bytestostr v = bytestostr(v) if type(v) == str and v.startswith("v"): return v[1:] return str(v)
def update_icon(self, width, height, coding, data): coding = bytestostr(coding) iconlog("%s.update_icon(%s, %s, %s, %s bytes)", self, width, height, coding, len(data)) if PYTHON3 and WIN32: iconlog("not setting icon to prevent crashes..") return if coding == "premult_argb32": #we usually cannot do in-place and this is not performance critical data = unpremultiply_argb(data) rgba = memoryview_to_bytes(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() #for debugging, save to a file so we can see it: #pixbuf.save("C-%s-%s.png" % (self._id, int(time.time())), "png") iconlog("%s.set_icon(%s)", self, pixbuf) self.set_icon(pixbuf)
def update_icon(self, width, height, coding, data): coding = bytestostr(coding) iconlog("%s.update_icon(%s, %s, %s, %s bytes)", self, width, height, coding, len(data)) if PYTHON3 and WIN32: iconlog("not setting icon to prevent crashes..") return if coding == "premult_argb32": #we usually cannot do in-place and this is not performance critical data = unpremultiply_argb(data) rgba = memoryview_to_bytes(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() #for debugging, save to a file so we can see it: #pixbuf.save("C-%s-%s.png" % (self._id, int(time.time())), "png") iconlog("%s.set_icon(%s)", self, pixbuf) self.set_icon(pixbuf)
def update_icon(self, width, height, coding, data): log("%s.update_icon(%s, %s, %s, %s bytes)", self, width, height, coding, len(data)) coding = bytestostr(coding) if coding == "premult_argb32": if unpremultiply_argb is None: #we could use PIL here with mode 'RGBa' log.warn( "cannot process premult_argb32 icon without the argb module" ) return #we usually cannot do in-place and this is not performance critical data = unpremultiply_argb(data) rgba = byte_buffer_to_buffer(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() log("%s.set_icon(%s)", self, pixbuf) self.set_icon(pixbuf)
def make_cursor(self, cursor_data): #if present, try cursor ny name: display = display_get_default() cursorlog( "make_cursor: has-name=%s, has-cursor-types=%s, xscale=%s, yscale=%s, USE_LOCAL_CURSORS=%s", len(cursor_data) >= 9, bool(cursor_types), self.xscale, self.yscale, USE_LOCAL_CURSORS) #named cursors cannot be scaled (round to 10 to compare so 0.95 and 1.05 are considered the same as 1.0, no scaling): if len(cursor_data) >= 9 and cursor_types and iround( self.xscale * 10) == 10 and iround(self.yscale * 10) == 10: cursor_name = bytestostr(cursor_data[8]) if cursor_name and USE_LOCAL_CURSORS: gdk_cursor = cursor_types.get(cursor_name.upper()) if gdk_cursor is not None: cursorlog("setting new cursor by name: %s=%s", cursor_name, gdk_cursor) return new_Cursor_for_display(display, gdk_cursor) else: global missing_cursor_names if cursor_name not in missing_cursor_names: cursorlog("cursor name '%s' not found", cursor_name) missing_cursor_names.add(cursor_name) #create cursor from the pixel data: w, h, xhot, yhot, serial, pixels = cursor_data[2:8] if len(pixels) < w * h * 4: import binascii cursorlog.warn( "not enough pixels provided in cursor data: %s needed and only %s bytes found (%s)", w * h * 4, len(pixels), binascii.hexlify(pixels)[:100]) return pixbuf = get_pixbuf_from_data(pixels, True, w, h, w * 4) x = max(0, min(xhot, w - 1)) y = max(0, min(yhot, h - 1)) csize = display.get_default_cursor_size() cmaxw, cmaxh = display.get_maximal_cursor_size() if len(cursor_data) >= 11: ssize = cursor_data[9] smax = cursor_data[10] cursorlog("server cursor sizes: default=%s, max=%s", ssize, smax) cursorlog( "new cursor at %s,%s with serial=%s, dimensions: %sx%s, len(pixels)=%s, default cursor size is %s, maximum=%s", xhot, yhot, serial, w, h, len(pixels), csize, (cmaxw, cmaxh)) fw, fh = get_fixed_cursor_size() if fw > 0 and fh > 0 and (w != fw or h != fh): #OS wants a fixed cursor size! (win32 does, and GTK doesn't do this for us) if w <= fw and h <= fh: cursorlog( "pasting cursor of size %ix%i onto clear pixbuf of size %ix%i", w, h, fw, fh) cursor_pixbuf = get_pixbuf_from_data("\0" * fw * fh * 4, True, fw, fh, fw * 4) pixbuf.copy_area(0, 0, w, h, cursor_pixbuf, 0, 0) else: cursorlog("scaling cursor from %ix%i to fixed OS size %ix%i", w, h, fw, fh) cursor_pixbuf = pixbuf.scale_simple(fw, fh, INTERP_BILINEAR) xratio, yratio = float(w) / fw, float(h) / fh x, y = iround(x / xratio), iround(y / yratio) else: sx, sy, sw, sh = x, y, w, h #scale the cursors: if self.xscale != 1 or self.yscale != 1: sx, sy, sw, sh = self.srect(x, y, w, h) sw = max(1, sw) sh = max(1, sh) #ensure we honour the max size if there is one: if (cmaxw > 0 and sw > cmaxw) or (cmaxh > 0 and sh > cmaxh): ratio = 1.0 if cmaxw > 0: ratio = max(ratio, float(w) / cmaxw) if cmaxh > 0: ratio = max(ratio, float(h) / cmaxh) cursorlog("clamping cursor size to %ix%i using ratio=%s", cmaxw, cmaxh, ratio) sx, sy, sw, sh = iround(x / ratio), iround(y / ratio), min( cmaxw, iround(w / ratio)), min(cmaxh, iround(h / ratio)) if sw != w or sh != h: cursorlog( "scaling cursor from %ix%i hotspot at %ix%i to %ix%i hotspot at %ix%i", w, h, x, y, sw, sh, sx, sy) cursor_pixbuf = pixbuf.scale_simple(sw, sh, INTERP_BILINEAR) x, y = sx, sy else: cursor_pixbuf = pixbuf #clamp to pixbuf size: w = cursor_pixbuf.get_width() h = cursor_pixbuf.get_height() x = max(0, min(x, w - 1)) y = max(0, min(y, h - 1)) try: c = new_Cursor_from_pixbuf(display, cursor_pixbuf, x, y) except RuntimeError as e: log.error("Error: failed to create cursor:") log.error(" %s", e) log.error(" using %s of size %ix%i with hotspot at %ix%i", cursor_pixbuf, w, h, x, y) c = None return c
def setup_dbus_window_menu(add, wid, menus, application_action_callback=None, window_action_callback=None): def nomenu(): #tell caller to clear all properties if they exist: return { "_GTK_APP_MENU_OBJECT_PATH" : None, "_GTK_WINDOW_OBJECT_PATH" : None, "_GTK_APPLICATION_OBJECT_PATH" : None, "_GTK_UNIQUE_BUS_NAME" : None, "_GTK_APPLICATION_ID" : None } if add is False: return nomenu() global window_menu_services, window_menus, fallback_menus if len(menus)==0 and fallback_menus: menus = fallback_menus #ie: menu = { # 'enabled': True, # 'application-id': 'org.xpra.ExampleMenu', # 'application-actions': {'quit': (True, '', ()), 'about': (True, '', ()), 'help': (True, '', ()), 'custom': (True, '', ()), 'activate-tab': (True, 's', ()), 'preferences': (True, '', ())}, # 'window-actions': {'edit-profile': (True, 's', ()), 'reset': (True, 'b', ()), 'about': (True, '', ()), 'help': (True, '', ()), 'fullscreen': (True, '', (0,)), 'detach-tab': (True, '', ()), 'save-contents': (True, '', ()), 'zoom': (True, 'i', ()), 'move-tab': (True, 'i', ()), 'new-terminal': (True, '(ss)', ()), 'switch-tab': (True, 'i', ()), 'new-profile': (True, '', ()), 'close': (True, 's', ()), 'show-menubar': (True, '', (1,)), 'select-all': (True, '', ()), 'copy': (True, '', ()), 'paste': (True, 's', ()), 'find': (True, 's', ()), 'preferences': (True, '', ())}, # 'window-menu': {0: # {0: ({':section': (0, 1)}, {':section': (0, 2)}, {':section': (0, 3)}), # 1: ({'action': 'win.new-terminal', 'target': ('default', 'default'), 'label': '_New Terminal'},), # 2: ({'action': 'app.preferences', 'label': '_Preferences'},), # 3: ({'action': 'app.help', 'label': '_Help'}, {'action': 'app.about', 'label': '_About'}, {'action': 'app.quit', 'label': '_Quit'}), # } # } # } enabled = menus.get("enabled", False) app_actions_service, window_actions_service, window_menu_service = None, None, None def remove_services(*args): """ removes all the services if they are not longer used by any windows """ for x in (app_actions_service, window_actions_service, window_menu_service): if x: if x not in window_menu_services.values(): try: x.remove_from_connection() except Exception as e: log.warn("Error removing %s: %s", x, e) try: del window_menus[wid] except: pass if enabled: m = typedict(menus) app_id = bytestostr(m.strget("application-id", b"org.xpra.Window%i" % wid)).decode() app_actions = m.dictget("application-actions") window_actions = m.dictget("window-actions") window_menu = m.dictget("window-menu") if wid in window_menus: #update, destroy or re-create the services: app_actions_service, window_actions_service, window_menu_service, cur_app_id = window_menus[wid] if not enabled or cur_app_id!=app_id: remove_services() #falls through to re-create them if enabled is True app_actions_service, window_actions_service, window_menu_service = None, None, None else: #update them: app_actions_service.set_actions(app_actions) window_actions_service.set_actions(window_actions) window_menu_service.set_menus(window_menu) return if not enabled: #tell caller to clear all properties if they exist: return nomenu() #make or re-use services: try: NAME_PREFIX = "org.xpra." from xpra.util import strtobytes from xpra.dbus.common import init_session_bus from xpra.dbus.gtk_menuactions import Menus, Actions session_bus = init_session_bus() bus_name = session_bus.get_unique_name().decode() name = app_id for strip in ("org.", "gtk.", "xpra.", "gnome."): if name.startswith(strip): name = name[len(strip):] name = NAME_PREFIX + name log("normalized named(%s)=%s", app_id, name) def get_service(service_class, name, path, *args): """ find the service by name and path, or create one """ service = window_menu_services.get((service_class, name, path)) if service is None: service = service_class(name, path, session_bus, *args) window_menu_services[(service_class, name, path)] = service return service app_path = strtobytes("/"+name.replace(".", "/")).decode() app_actions_service = get_service(Actions, name, app_path, app_actions, application_action_callback) #this one should be unique and therefore not re-used? (only one "window_action_callback"..) window_path = u"%s/window/%s" % (app_path, wid) window_actions_service = get_service(Actions, name, window_path, window_actions, window_action_callback) menu_path = u"%s/menus/appmenu" % app_path window_menu_service = get_service(Menus, app_id, menu_path, window_menu) window_menus[wid] = app_actions_service, window_actions_service, window_menu_service, app_id return { "_GTK_APP_MENU_OBJECT_PATH" : ("utf8", menu_path), "_GTK_WINDOW_OBJECT_PATH" : ("utf8", window_path), "_GTK_APPLICATION_OBJECT_PATH" : ("utf8", app_path), "_GTK_UNIQUE_BUS_NAME" : ("utf8", bus_name), "_GTK_APPLICATION_ID" : ("utf8", app_id), } except Exception: log.error("Error: cannot parse or apply menu:", exc_info=True) remove_services() return nomenu()
def set_metadata(self, metadata): metalog("set_metadata(%s)", metadata) #WARNING: "class-instance" needs to go first because others may realize the window #(and GTK doesn't set the "class-instance" once the window is realized) if b"class-instance" in metadata: self.set_class_instance( *self._metadata.strlistget("class-instance", ("xpra", "Xpra"))) if b"title" in metadata: try: title = bytestostr(self._client.title).replace("\0", "") if title.find("@") >= 0: #perform metadata variable substitutions: #full of py3k unicode headaches that don't need to be default_values = { "title": "<untitled window>", "client-machine": "<unknown machine>" } def metadata_replace(match): atvar = match.group(0) #ie: '@title@' var = atvar[1:len(atvar) - 1] #ie: 'title' default_value = default_values.get( var, "<unknown %s>" % var) value = self._metadata.strget(var, default_value) if sys.version < '3': value = value.decode("utf-8") return value title = re.sub("@[\w\-]*@", metadata_replace, title) if sys.version < '3': utf8_title = title.encode("utf-8") else: utf8_title = title except Exception as e: log.error("error parsing window title: %s", e) utf8_title = b"" self.set_title(utf8_title) if b"icon-title" in metadata: icon_title = metadata.strget("icon-title") self.set_icon_name(icon_title) if b"size-constraints" in metadata: self.size_constraints = typedict( metadata.dictget("size-constraints")) self.set_size_constraints(self.size_constraints, self.max_window_size) if b"transient-for" in metadata: wid = metadata.intget("transient-for", -1) self.apply_transient_for(wid) if b"modal" in metadata: modal = metadata.boolget("modal") self.set_modal(modal) #apply window-type hint if window has not been mapped yet: if b"window-type" in metadata and not self.is_mapped(): window_types = metadata.strlistget("window-type") self.set_window_type(window_types) if b"role" in metadata: role = metadata.strget("role") self.set_role(role) if b"xid" in metadata: xid = metadata.strget("xid") self.set_xid(xid) if b"opacity" in metadata: opacity = metadata.intget("opacity", -1) if opacity < 0: opacity = 1 else: opacity = min(1, opacity / float(0xffffffff)) #requires gtk>=2.12! if hasattr(self, "set_opacity"): self.set_opacity(opacity) if b"has-alpha" in metadata: new_alpha = metadata.boolget("has-alpha") if new_alpha != self._has_alpha: log.warn( "window %s changed its alpha flag from %s to %s (unsupported)", self._id, self._has_alpha, new_alpha) self._has_alpha = new_alpha if b"maximized" in metadata: maximized = metadata.boolget("maximized") if maximized != self._maximized: self._maximized = maximized if maximized: self.maximize() else: self.unmaximize() if b"fullscreen" in metadata: fullscreen = metadata.boolget("fullscreen") if self._fullscreen is None or self._fullscreen != fullscreen: self._fullscreen = fullscreen self.set_fullscreen(fullscreen) if b"iconic" in metadata: iconified = metadata.boolget("iconic") if self._iconified != iconified: self._iconified = iconified if iconified: self.iconify() else: self.deiconify() if b"decorations" in metadata: self.set_decorated(metadata.boolget("decorations")) self.apply_geometry_hints(self.geometry_hints) if b"above" in metadata: above = metadata.boolget("above") if self._above != above: self._above = above self.set_keep_above(above) if b"below" in metadata: below = metadata.boolget("below") if self._below != below: self._below = below self.set_keep_below(below) if b"shaded" in metadata: shaded = metadata.boolget("shaded") if self._shaded != shaded: self._shaded = shaded self.set_shaded(shaded) if b"sticky" in metadata: sticky = metadata.boolget("sticky") if self._sticky != sticky: self._sticky = sticky if sticky: self.stick() else: self.unstick() if b"skip-taskbar" in metadata: skip_taskbar = metadata.boolget("skip-taskbar") if self._skip_taskbar != skip_taskbar: self._skip_taskbar = skip_taskbar self.set_skip_taskbar_hint(skip_taskbar) if b"skip-pager" in metadata: skip_pager = metadata.boolget("skip-pager") if self._skip_pager != skip_pager: self._skip_pager = skip_pager self.set_skip_taskbar_hint(skip_pager) if b"workspace" in metadata: self.set_workspace(metadata.intget("workspace")) if b"bypass-compositor" in metadata: self.set_bypass_compositor(metadata.intget("bypass-compositor")) if b"strut" in metadata: self.set_strut(metadata.dictget("strut")) if b"fullscreen-monitors" in metadata: self.set_fullscreen_monitors( metadata.intlistget("fullscreen-monitors")) if b"shape" in metadata: self.set_shape(metadata.dictget("shape")) if b"command" in metadata: self.set_command(metadata.strget("command")) if b"menu" in metadata: self.set_menu(metadata.dictget("menu"))
def set_metadata(self, metadata): metalog("set_metadata(%s)", metadata) #WARNING: "class-instance" needs to go first because others may realize the window #(and GTK doesn't set the "class-instance" once the window is realized) if b"class-instance" in metadata: self.set_class_instance(*self._metadata.strlistget("class-instance", ("xpra", "Xpra"))) if b"title" in metadata: try: title = bytestostr(self._client.title).replace("\0", "") if title.find("@")>=0: #perform metadata variable substitutions: #full of py3k unicode headaches that don't need to be default_values = {"title" : "<untitled window>", "client-machine" : "<unknown machine>"} def metadata_replace(match): atvar = match.group(0) #ie: '@title@' var = atvar[1:len(atvar)-1] #ie: 'title' default_value = default_values.get(var, "<unknown %s>" % var) value = self._metadata.strget(var, default_value) if sys.version<'3': value = value.decode("utf-8") return value title = re.sub("@[\w\-]*@", metadata_replace, title) if sys.version<'3': utf8_title = title.encode("utf-8") else: utf8_title = title except Exception as e: log.error("error parsing window title: %s", e) utf8_title = b"" self.set_title(utf8_title) if b"icon-title" in metadata: icon_title = metadata.strget("icon-title") self.set_icon_name(icon_title) if b"size-constraints" in metadata: self.size_constraints = typedict(metadata.dictget("size-constraints")) self.set_size_constraints(self.size_constraints, self.max_window_size) if b"transient-for" in metadata: wid = metadata.intget("transient-for", -1) self.apply_transient_for(wid) if b"modal" in metadata: modal = metadata.boolget("modal") self.set_modal(modal) #apply window-type hint if window has not been mapped yet: if b"window-type" in metadata and not self.is_mapped(): window_types = metadata.strlistget("window-type") self.set_window_type(window_types) if b"role" in metadata: role = metadata.strget("role") self.set_role(role) if b"xid" in metadata: xid = metadata.strget("xid") self.set_xid(xid) if b"opacity" in metadata: opacity = metadata.intget("opacity", -1) if opacity<0: opacity = 1 else: opacity = min(1, opacity/float(0xffffffff)) #requires gtk>=2.12! if hasattr(self, "set_opacity"): self.set_opacity(opacity) if b"has-alpha" in metadata: new_alpha = metadata.boolget("has-alpha") if new_alpha!=self._has_alpha: log.warn("window %s changed its alpha flag from %s to %s (unsupported)", self._id, self._has_alpha, new_alpha) self._has_alpha = new_alpha if b"maximized" in metadata: maximized = metadata.boolget("maximized") if maximized!=self._maximized: self._maximized = maximized if maximized: self.maximize() else: self.unmaximize() if b"fullscreen" in metadata: fullscreen = metadata.boolget("fullscreen") if self._fullscreen is None or self._fullscreen!=fullscreen: self._fullscreen = fullscreen self.set_fullscreen(fullscreen) if b"iconic" in metadata: iconified = metadata.boolget("iconic") if self._iconified!=iconified: self._iconified = iconified if iconified: self.iconify() else: self.deiconify() if b"decorations" in metadata: self.set_decorated(metadata.boolget("decorations")) self.apply_geometry_hints(self.geometry_hints) if b"above" in metadata: above = metadata.boolget("above") if self._above!=above: self._above = above self.set_keep_above(above) if b"below" in metadata: below = metadata.boolget("below") if self._below!=below: self._below = below self.set_keep_below(below) if b"shaded" in metadata: shaded = metadata.boolget("shaded") if self._shaded!=shaded: self._shaded = shaded self.set_shaded(shaded) if b"sticky" in metadata: sticky = metadata.boolget("sticky") if self._sticky!=sticky: self._sticky = sticky if sticky: self.stick() else: self.unstick() if b"skip-taskbar" in metadata: skip_taskbar = metadata.boolget("skip-taskbar") if self._skip_taskbar!=skip_taskbar: self._skip_taskbar = skip_taskbar self.set_skip_taskbar_hint(skip_taskbar) if b"skip-pager" in metadata: skip_pager = metadata.boolget("skip-pager") if self._skip_pager!=skip_pager: self._skip_pager = skip_pager self.set_skip_taskbar_hint(skip_pager) if b"workspace" in metadata: self.set_workspace(metadata.intget("workspace")) if b"bypass-compositor" in metadata: self.set_bypass_compositor(metadata.intget("bypass-compositor")) if b"strut" in metadata: self.set_strut(metadata.dictget("strut")) if b"fullscreen-monitors" in metadata: self.set_fullscreen_monitors(metadata.intlistget("fullscreen-monitors")) if b"shape" in metadata: self.set_shape(metadata.dictget("shape")) if b"command" in metadata: self.set_command(metadata.strget("command")) if b"menu" in metadata: self.set_menu(metadata.dictget("menu"))
def make_cursor(self, cursor_data): #if present, try cursor ny name: display = display_get_default() cursorlog("make_cursor: has-name=%s, has-cursor-types=%s, xscale=%s, yscale=%s, USE_LOCAL_CURSORS=%s", len(cursor_data)>=9, bool(cursor_types), self.xscale, self.yscale, USE_LOCAL_CURSORS) #named cursors cannot be scaled (round to 10 to compare so 0.95 and 1.05 are considered the same as 1.0, no scaling): if len(cursor_data)>=9 and cursor_types and iround(self.xscale*10)==10 and iround(self.yscale*10)==10: cursor_name = bytestostr(cursor_data[8]) if cursor_name and USE_LOCAL_CURSORS: gdk_cursor = cursor_types.get(cursor_name.upper()) if gdk_cursor is not None: cursorlog("setting new cursor by name: %s=%s", cursor_name, gdk_cursor) return new_Cursor_for_display(display, gdk_cursor) else: global missing_cursor_names if cursor_name not in missing_cursor_names: cursorlog("cursor name '%s' not found", cursor_name) missing_cursor_names.add(cursor_name) #create cursor from the pixel data: w, h, xhot, yhot, serial, pixels = cursor_data[2:8] if len(pixels)<w*h*4: import binascii cursorlog.warn("not enough pixels provided in cursor data: %s needed and only %s bytes found (%s)", w*h*4, len(pixels), binascii.hexlify(pixels)[:100]) return pixbuf = get_pixbuf_from_data(pixels, True, w, h, w*4) x = max(0, min(xhot, w-1)) y = max(0, min(yhot, h-1)) csize = display.get_default_cursor_size() cmaxw, cmaxh = display.get_maximal_cursor_size() if len(cursor_data)>=11: ssize = cursor_data[9] smax = cursor_data[10] cursorlog("server cursor sizes: default=%s, max=%s", ssize, smax) cursorlog("new cursor at %s,%s with serial=%s, dimensions: %sx%s, len(pixels)=%s, default cursor size is %s, maximum=%s", xhot,yhot, serial, w,h, len(pixels), csize, (cmaxw, cmaxh)) fw, fh = get_fixed_cursor_size() if fw>0 and fh>0 and (w!=fw or h!=fh): #OS wants a fixed cursor size! (win32 does, and GTK doesn't do this for us) if w<=fw and h<=fh: cursorlog("pasting cursor of size %ix%i onto clear pixbuf of size %ix%i", w, h, fw, fh) cursor_pixbuf = get_pixbuf_from_data("\0"*fw*fh*4, True, fw, fh, fw*4) pixbuf.copy_area(0, 0, w, h, cursor_pixbuf, 0, 0) else: cursorlog("scaling cursor from %ix%i to fixed OS size %ix%i", w, h, fw, fh) cursor_pixbuf = pixbuf.scale_simple(fw, fh, INTERP_BILINEAR) xratio, yratio = float(w)/fw, float(h)/fh x, y = iround(x/xratio), iround(y/yratio) else: sx, sy, sw, sh = x, y, w, h #scale the cursors: if self.xscale!=1 or self.yscale!=1: sx, sy, sw, sh = self.srect(x, y, w, h) sw = max(1, sw) sh = max(1, sh) #ensure we honour the max size if there is one: if (cmaxw>0 and sw>cmaxw) or (cmaxh>0 and sh>cmaxh): ratio = 1.0 if cmaxw>0: ratio = max(ratio, float(w)/cmaxw) if cmaxh>0: ratio = max(ratio, float(h)/cmaxh) cursorlog("clamping cursor size to %ix%i using ratio=%s", cmaxw, cmaxh, ratio) sx, sy, sw, sh = iround(x/ratio), iround(y/ratio), min(cmaxw, iround(w/ratio)), min(cmaxh, iround(h/ratio)) if sw!=w or sh!=h: cursorlog("scaling cursor from %ix%i hotspot at %ix%i to %ix%i hotspot at %ix%i", w, h, x, y, sw, sh, sx, sy) cursor_pixbuf = pixbuf.scale_simple(sw, sh, INTERP_BILINEAR) x, y = sx, sy else: cursor_pixbuf = pixbuf #clamp to pixbuf size: w = cursor_pixbuf.get_width() h = cursor_pixbuf.get_height() x = max(0, min(x, w-1)) y = max(0, min(y, h-1)) try: c = new_Cursor_from_pixbuf(display, cursor_pixbuf, x, y) except RuntimeError as e: log.error("Error: failed to create cursor:") log.error(" %s", e) log.error(" using %s of size %ix%i with hotspot at %ix%i", cursor_pixbuf, w, h, x, y) c = None return c
def make_cursor(self, cursor_data): #if present, try cursor ny name: display = display_get_default() if len(cursor_data) >= 9 and cursor_types: cursor_name = bytestostr(cursor_data[8]) if cursor_name: gdk_cursor = cursor_types.get(cursor_name.upper()) if gdk_cursor is not None: cursorlog("setting new cursor by name: %s=%s", cursor_name, gdk_cursor) return new_Cursor_for_display(display, gdk_cursor) else: global missing_cursor_names if cursor_name not in missing_cursor_names: cursorlog("cursor name '%s' not found", cursor_name) missing_cursor_names.add(cursor_name) #create cursor from the pixel data: w, h, xhot, yhot, serial, pixels = cursor_data[2:8] if len(pixels) < w * h * 4: import binascii cursorlog.warn( "not enough pixels provided in cursor data: %s needed and only %s bytes found (%s)", w * h * 4, len(pixels), binascii.hexlify(pixels)[:100]) return pixbuf = get_pixbuf_from_data(pixels, True, w, h, w * 4) x = max(0, min(xhot, w - 1)) y = max(0, min(yhot, h - 1)) csize = display.get_default_cursor_size() cmaxw, cmaxh = display.get_maximal_cursor_size() if len(cursor_data) >= 11: ssize = cursor_data[9] smax = cursor_data[10] cursorlog("server cursor sizes: default=%s, max=%s", ssize, smax) cursorlog( "new cursor at %s,%s with serial=%s, dimensions: %sx%s, len(pixels)=%s, default cursor size is %s, maximum=%s", xhot, yhot, serial, w, h, len(pixels), csize, (cmaxw, cmaxh)) fw, fh = get_fixed_cursor_size() if fw > 0 and fh > 0 and (w != fw or h != fh): #OS wants a fixed cursor size! (win32 does, and GTK doesn't do this for us) if w <= fw and h <= fh: cursorlog( "pasting cursor of size %ix%i onto clear pixbuf of size %ix%i", w, h, fw, fh) cursor_pixbuf = get_pixbuf_from_data("\0" * fw * fh * 4, True, fw, fh, fw * 4) pixbuf.copy_area(0, 0, w, h, cursor_pixbuf, 0, 0) else: cursorlog("scaling cursor from %ix%i to fixed OS size %ix%i", w, h, fw, fh) cursor_pixbuf = pixbuf.scale_simple(fw, fh, INTERP_BILINEAR) xratio, yratio = float(w) / fw, float(h) / fh x, y = int(x / xratio), int(y / yratio) elif w > cmaxw or h > cmaxh or (csize > 0 and (csize < w or csize < h)): ratio = max( float(w) / cmaxw, float(h) / cmaxh, float(max(w, h)) / csize) x, y, w, h = int(x / ratio), int(y / ratio), int(w / ratio), int( h / ratio) cursorlog("downscaling cursor %s by %.2f: %sx%s", pixbuf, ratio, w, h) cursor_pixbuf = pixbuf.scale_simple(w, h, INTERP_BILINEAR) else: cursor_pixbuf = pixbuf #clamp to pixbuf size: w = cursor_pixbuf.get_width() h = cursor_pixbuf.get_height() x = max(0, min(x, w - 1)) y = max(0, min(y, h - 1)) return new_Cursor_from_pixbuf(display, cursor_pixbuf, x, y)
def setup_dbus_window_menu(add, wid, menus, application_action_callback=None, window_action_callback=None): def nomenu(): #tell caller to clear all properties if they exist: return { "_GTK_APP_MENU_OBJECT_PATH": None, "_GTK_WINDOW_OBJECT_PATH": None, "_GTK_APPLICATION_OBJECT_PATH": None, "_GTK_UNIQUE_BUS_NAME": None, "_GTK_APPLICATION_ID": None } if add is False: return nomenu() global window_menu_services, window_menus, fallback_menus if len(menus) == 0 and fallback_menus: menus = fallback_menus #ie: menu = { # 'enabled': True, # 'application-id': 'org.xpra.ExampleMenu', # 'application-actions': {'quit': (True, '', ()), 'about': (True, '', ()), 'help': (True, '', ()), 'custom': (True, '', ()), 'activate-tab': (True, 's', ()), 'preferences': (True, '', ())}, # 'window-actions': {'edit-profile': (True, 's', ()), 'reset': (True, 'b', ()), 'about': (True, '', ()), 'help': (True, '', ()), 'fullscreen': (True, '', (0,)), 'detach-tab': (True, '', ()), 'save-contents': (True, '', ()), 'zoom': (True, 'i', ()), 'move-tab': (True, 'i', ()), 'new-terminal': (True, '(ss)', ()), 'switch-tab': (True, 'i', ()), 'new-profile': (True, '', ()), 'close': (True, 's', ()), 'show-menubar': (True, '', (1,)), 'select-all': (True, '', ()), 'copy': (True, '', ()), 'paste': (True, 's', ()), 'find': (True, 's', ()), 'preferences': (True, '', ())}, # 'window-menu': {0: # {0: ({':section': (0, 1)}, {':section': (0, 2)}, {':section': (0, 3)}), # 1: ({'action': 'win.new-terminal', 'target': ('default', 'default'), 'label': '_New Terminal'},), # 2: ({'action': 'app.preferences', 'label': '_Preferences'},), # 3: ({'action': 'app.help', 'label': '_Help'}, {'action': 'app.about', 'label': '_About'}, {'action': 'app.quit', 'label': '_Quit'}), # } # } # } enabled = menus.get("enabled", False) app_actions_service, window_actions_service, window_menu_service = None, None, None def remove_services(*args): """ removes all the services if they are not longer used by any windows """ for x in (app_actions_service, window_actions_service, window_menu_service): if x: if x not in window_menu_services.values(): try: x.remove_from_connection() except Exception as e: log.warn("Error removing %s: %s", x, e) try: del window_menus[wid] except: pass if enabled: m = typedict(menus) app_id = bytestostr( m.strget("application-id", b"org.xpra.Window%i" % wid)).decode() app_actions = m.dictget("application-actions") window_actions = m.dictget("window-actions") window_menu = m.dictget("window-menu") if wid in window_menus: #update, destroy or re-create the services: app_actions_service, window_actions_service, window_menu_service, cur_app_id = window_menus[ wid] if not enabled or cur_app_id != app_id: remove_services( ) #falls through to re-create them if enabled is True app_actions_service, window_actions_service, window_menu_service = None, None, None else: #update them: app_actions_service.set_actions(app_actions) window_actions_service.set_actions(window_actions) window_menu_service.set_menus(window_menu) return if not enabled: #tell caller to clear all properties if they exist: return nomenu() #make or re-use services: try: NAME_PREFIX = "org.xpra." from xpra.util import strtobytes from xpra.dbus.common import init_session_bus from xpra.dbus.gtk_menuactions import Menus, Actions session_bus = init_session_bus() bus_name = session_bus.get_unique_name().decode() name = app_id for strip in ("org.", "gtk.", "xpra.", "gnome."): if name.startswith(strip): name = name[len(strip):] name = NAME_PREFIX + name log("normalized named(%s)=%s", app_id, name) def get_service(service_class, name, path, *args): """ find the service by name and path, or create one """ service = window_menu_services.get((service_class, name, path)) if service is None: service = service_class(name, path, session_bus, *args) window_menu_services[(service_class, name, path)] = service return service app_path = strtobytes("/" + name.replace(".", "/")).decode() app_actions_service = get_service(Actions, name, app_path, app_actions, application_action_callback) #this one should be unique and therefore not re-used? (only one "window_action_callback"..) window_path = u"%s/window/%s" % (app_path, wid) window_actions_service = get_service(Actions, name, window_path, window_actions, window_action_callback) menu_path = u"%s/menus/appmenu" % app_path window_menu_service = get_service(Menus, app_id, menu_path, window_menu) window_menus[ wid] = app_actions_service, window_actions_service, window_menu_service, app_id return { "_GTK_APP_MENU_OBJECT_PATH": ("utf8", menu_path), "_GTK_WINDOW_OBJECT_PATH": ("utf8", window_path), "_GTK_APPLICATION_OBJECT_PATH": ("utf8", app_path), "_GTK_UNIQUE_BUS_NAME": ("utf8", bus_name), "_GTK_APPLICATION_ID": ("utf8", app_id), } except Exception: log.error("Error: cannot parse or apply menu:", exc_info=True) remove_services() return nomenu()