def show_dpi(): wmm, hmm = RandR.get_screen_size_mm() #ie: (1280, 1024) screenlog("RandR.get_screen_size_mm=%s,%s", wmm, hmm) actual_xdpi = iround(root_w * 25.4 / wmm) actual_ydpi = iround(root_h * 25.4 / hmm) if abs(actual_xdpi - xdpi) <= 1 and abs(actual_ydpi - ydpi) <= 1: screenlog.info("DPI set to %s x %s", actual_xdpi, actual_ydpi) screenlog("wanted: %s x %s", xdpi, ydpi) else: #should this be a warning: l = screenlog.info maxdelta = max(abs(actual_xdpi - xdpi), abs(actual_ydpi - ydpi)) if maxdelta >= 10: l = log.warn messages = [ "DPI set to %s x %s (wanted %s x %s)" % (actual_xdpi, actual_ydpi, xdpi, ydpi), ] if maxdelta >= 10: messages.append( "you may experience scaling problems, such as huge or small fonts, etc" ) messages.append( "to fix this issue, try the dpi switch, or use a patched Xorg dummy driver" ) self.notify_dpi_warning("\n".join(messages)) for i, message in enumerate(messages): l("%s%s", ["", " "][i > 0], message)
def scale_reinit(self, xchange=1.0, ychange=1.0): #wait at least one second before changing again: self.scale_change_embargo = monotonic_time() + SCALING_EMBARGO_TIME if fequ(self.xscale, self.yscale): scalinglog.info("setting scaling to %i%%:", iround(100 * self.xscale)) else: scalinglog.info("setting scaling to %i%% x %i%%:", iround(100 * self.xscale), iround(100 * self.yscale)) self.update_screen_size() #re-initialize all the windows with their new size def new_size_fn(w, h): minx, miny = 16384, 16384 if self.max_window_size != (0, 0): minx, miny = self.max_window_size return max(1, min(minx, int(w * xchange))), max(1, min(miny, int(h * ychange))) self.reinit_windows(new_size_fn) self.reinit_window_icons() self.emit("scaling-changed")
def get_dpi_caps(self): #command line (or config file) override supplied: caps = {} dpi = 0 if self.dpi > 0: #scale it: dpi = iround((self.cx(self.dpi) + self.cy(self.dpi)) / 2.0) else: #not supplied, use platform detection code: #platforms may also provide per-axis dpi (later win32 versions do) xdpi = self.get_xdpi() ydpi = self.get_ydpi() log("xdpi=%i, ydpi=%i", xdpi, ydpi) if xdpi > 0 and ydpi > 0: xdpi = self.cx(xdpi) ydpi = self.cy(ydpi) dpi = iround((xdpi + ydpi) / 2.0) caps = { "x": xdpi, "y": ydpi, } if dpi: caps[""] = dpi log("get_gpi_caps()=%s", caps) return caps
def draw(): v = pix.increase() img_data = bytes([v % 256]*stride*h) options["flush"] = 1 window.draw_region(0, 0, w, h, coding, img_data, stride, v, options, [paint_callback]) options["flush"] = 0 mx = w//2-icon_w//2 my = h//2-icon_h//2 x = iround(mx*(1+sin(v/100))) y = iround(my*(1+cos(v/100))) window.draw_region(x, y, icon_w, icon_h, icon_format, icon_data, icon_stride, v, options, [paint_callback]) return REPAINT_DELAY>0
def _get_randr_dpi(): try: from xpra.x11.bindings.randr_bindings import RandRBindings #@UnresolvedImport randr_bindings = RandRBindings() wmm, hmm = randr_bindings.get_screen_size_mm() w, h = randr_bindings.get_screen_size() dpix = iround(w * 25.4 / wmm) dpiy = iround(h * 25.4 / hmm) screenlog("xdpi=%s, ydpi=%s - size-mm=%ix%i, size=%ix%i", dpix, dpiy, wmm, hmm, w, h) return dpix, dpiy except Exception as e: screenlog.warn("failed to get dpi: %s", e) return -1, -1
def _get_randr_dpi(): try: from xpra.x11.bindings.randr_bindings import RandRBindings #@UnresolvedImport randr_bindings = RandRBindings() wmm, hmm = randr_bindings.get_screen_size_mm() w, h = randr_bindings.get_screen_size() dpix = iround(w * 25.4 / wmm) dpiy = iround(h * 25.4 / hmm) screenlog("dpix=%s, dpiy=%s", dpix, dpiy) return dpix, dpiy except Exception as e: screenlog.warn("failed to get dpi: %s", e) return -1, -1
def _get_randr_dpi(): if RANDR_DPI and not is_Wayland(): from xpra.gtk_common.error import xlog from xpra.x11.bindings.randr_bindings import RandRBindings #@UnresolvedImport with xlog: randr_bindings = RandRBindings() wmm, hmm = randr_bindings.get_screen_size_mm() w, h = randr_bindings.get_screen_size() dpix = iround(w * 25.4 / wmm) dpiy = iround(h * 25.4 / hmm) screenlog("xdpi=%s, ydpi=%s - size-mm=%ix%i, size=%ix%i", dpix, dpiy, wmm, hmm, w, h) return dpix, dpiy return -1, -1
def get_caps(self) -> dict: caps = { "randr_notify": True, "show-desktop": True, "vrefresh": self.get_vrefresh(), } wm_name = get_wm_name() if wm_name: caps["wm_name"] = wm_name self._last_screen_settings = self.get_screen_settings() root_w, root_h, sss, ndesktops, desktop_names, u_root_w, u_root_h = self._last_screen_settings[: 7] if u_root_w and u_root_h: caps["desktop_size"] = self.cp(u_root_w, u_root_h) if ndesktops: caps["desktops"] = ndesktops caps["desktop.names"] = tuple(desktop_names) ss = self.get_screen_sizes() self._current_screen_sizes = ss log.info(" desktop size is %sx%s:", u_root_w, u_root_h) log_screen_sizes(u_root_w, u_root_h, ss) if self.xscale != 1 or self.yscale != 1: caps["screen_sizes.unscaled"] = ss caps["desktop_size.unscaled"] = u_root_w, u_root_h root_w, root_h = self.cp(u_root_w, u_root_h) if fequ(self.xscale, self.yscale): sinfo = "%i%%" % iround(self.xscale * 100) else: sinfo = "%i%% x %i%%" % (iround( self.xscale * 100), iround(self.yscale * 100)) scaled_up = u_root_w > root_w or u_root_h > root_h log.info(" %sscaled to %s, virtual screen size: %ix%i", "up" if scaled_up else "down", sinfo, root_w, root_h) log_screen_sizes(root_w, root_h, sss) else: root_w, root_h = u_root_w, u_root_h sss = ss caps["screen_sizes"] = sss caps.update(self.get_screen_caps()) caps.update( flatten_dict({ "dpi": self.get_dpi_caps(), "screen-scaling": self.get_scaling_caps(), })) return caps
def get_caps(self): caps = { "randr_notify": True, "show-desktop": True, } wm_name = get_wm_name() if wm_name: caps["wm_name"] = wm_name self._last_screen_settings = self.get_screen_settings() root_w, root_h, sss, ndesktops, desktop_names, u_root_w, u_root_h, _, _ = self._last_screen_settings if u_root_w and u_root_h: caps["desktop_size"] = self.cp(u_root_w, u_root_h) if ndesktops: caps["desktops"] = ndesktops caps["desktop.names"] = desktop_names ss = self.get_screen_sizes() self._current_screen_sizes = ss log.info(" desktop size is %sx%s with %s screen%s:", u_root_w, u_root_h, len(ss), engs(ss)) log_screen_sizes(u_root_w, u_root_h, ss) if self.xscale != 1 or self.yscale != 1: caps["screen_sizes.unscaled"] = ss caps["desktop_size.unscaled"] = u_root_w, u_root_h root_w, root_h = self.cp(u_root_w, u_root_h) if fequ(self.xscale, self.yscale): sinfo = "%i%%" % iround(self.xscale * 100) else: sinfo = "%i%% x %i%%" % (iround( self.xscale * 100), iround(self.yscale * 100)) log.info(" %sscaled by %s, virtual screen size: %ix%i", ["down", "up"][int(u_root_w > root_w or u_root_h > root_h)], sinfo, root_w, root_h) log_screen_sizes(root_w, root_h, sss) else: root_w, root_h = u_root_w, u_root_h sss = ss caps["screen_sizes"] = sss caps.update(self.get_screen_caps()) caps.update( flatten_dict({ "dpi": self.get_dpi_caps(), "screen-scaling": self.get_scaling_caps(), })) return caps
def _get_randr_dpi(): if RANDR_DPI and not is_Wayland(): from xpra.gtk_common.error import xlog with xlog: randr_bindings = X11RandRBindings() if randr_bindings and randr_bindings.has_randr(): wmm, hmm = randr_bindings.get_screen_size_mm() if wmm > 0 and hmm > 0: w, h = randr_bindings.get_screen_size() dpix = iround(w * 25.4 / wmm) dpiy = iround(h * 25.4 / hmm) screenlog("xdpi=%s, ydpi=%s - size-mm=%ix%i, size=%ix%i", dpix, dpiy, wmm, hmm, w, h) return dpix, dpiy return -1, -1
def _process_desktop_size(self, proto, packet): width, height = packet[1:3] ss = self._server_sources.get(proto) if ss is None: return ss.desktop_size = (width, height) if len(packet) >= 10: #added in 0.16 for scaled client displays: xdpi, ydpi = packet[8:10] if xdpi != self.xdpi or ydpi != self.ydpi: self.xdpi, self.ydpi = xdpi, ydpi log("new dpi: %ix%i", self.xdpi, self.ydpi) self.dpi = iround((self.xdpi + self.ydpi) / 2.0) self.dpi_changed() if len(packet) >= 8: #added in 0.16 for scaled client displays: ss.desktop_size_unscaled = packet[6:8] if len(packet) >= 6: desktops, desktop_names = packet[4:6] ss.set_desktops(desktops, desktop_names) self.calculate_desktops() if len(packet) >= 4: ss.set_screen_sizes(packet[3]) log("client requesting new size: %sx%s", width, height) self.set_screen_size(width, height) if len(packet) >= 4: log.info("received updated display dimensions") log.info("client display size is %sx%s with %s screen%s:", width, height, len(ss.screen_sizes), engs(ss.screen_sizes)) log_screen_sizes(width, height, ss.screen_sizes) self.calculate_workarea(width, height) #ensures that DPI and antialias information gets reset: self.update_all_server_settings()
def _get_randr_dpi(): try: from xpra.gtk_common.error import xsync from xpra.x11.bindings.randr_bindings import RandRBindings #@UnresolvedImport with xsync: randr_bindings = RandRBindings() wmm, hmm = randr_bindings.get_screen_size_mm() w, h = randr_bindings.get_screen_size() dpix = iround(w * 25.4 / wmm) dpiy = iround(h * 25.4 / hmm) screenlog("xdpi=%s, ydpi=%s - size-mm=%ix%i, size=%ix%i", dpix, dpiy, wmm, hmm, w, h) return dpix, dpiy except Exception as e: screenlog.warn("failed to get dpi: %s", e) return -1, -1
def t(v, ea, era): a, ra = cystats.calculate_size_weighted_average(v) self.assertEqual( iround(a), iround(ea), "average should be %s, got %s" % (iround(ea), iround(a))) self.assertEqual( iround(ra), iround(era), "recent average should be %s, got %s" % (iround(era), iround(ra)))
def show_dpi(): wmm, hmm = RandR.get_screen_size_mm() #ie: (1280, 1024) screenlog("RandR.get_screen_size_mm=%s,%s", wmm, hmm) actual_xdpi = iround(root_w * 25.4 / wmm) actual_ydpi = iround(root_h * 25.4 / hmm) if abs(actual_xdpi-xdpi)<=1 and abs(actual_ydpi-ydpi)<=1: screenlog.info("DPI set to %s x %s", xdpi, ydpi) else: #should this be a warning: l = screenlog.info maxdelta = max(abs(actual_xdpi-xdpi), abs(actual_ydpi-ydpi)) if maxdelta>=10: l = log.warn l("DPI set to %s x %s (wanted %s x %s)", actual_xdpi, actual_ydpi, xdpi, ydpi) if maxdelta>=10: l(" you may experience scaling problems, such as huge or small fonts, etc") l(" to fix this issue, try the dpi switch, or use a patched Xorg dummy driver")
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)
def scalingitem(scalingvalue=1.0): pct = iround(100.0*scalingvalue) label = {100 : "None"}.get(pct, "%i%%" % pct) c = CheckMenuItem(label) c.scalingvalue = scalingvalue c.set_draw_as_radio(True) c.set_active(scalecmp(scalingvalue)) def scaling_activated(item): if scaling_submenu.updating: return ensure_item_selected(scaling_submenu, item) self.client.scaleset(item.scalingvalue, item.scalingvalue) c.connect('activate', scaling_activated) return c
def get_icon_size(): xdpi = get_xdpi() ydpi = get_ydpi() if xdpi > 0 and ydpi > 0: from xpra.util import iround dpi = iround((xdpi + ydpi) / 2.0) else: dpi = 96 if dpi > 144: return 48 if dpi > 120: return 32 if dpi > 96: return 24 return 16
def get_icon_size(): xdpi = get_xdpi() ydpi = get_ydpi() if xdpi>0 and ydpi>0: from xpra.util import iround dpi = iround((xdpi + ydpi)/2.0) else: dpi = 96 if dpi > 144: return 48 elif dpi > 120: return 32 elif dpi > 96: return 24 else: return 16
def _process_desktop_size(self, proto, packet): log("new desktop size from %s: %s", proto, packet) width, height = packet[1:3] ss = self.get_server_source(proto) if ss is None: return ss.desktop_size = (width, height) if len(packet)>=11: vrefresh = packet[10] log("new vrefresh=%s", vrefresh) #update clientdisplay mixin: if hasattr(ss, "vrefresh") and getattr(ss, "refresh")!=vrefresh: ss.vrefresh = vrefresh #update all batch configs: if hasattr(ss, "all_window_sources"): for window_source in ss.all_window_sources(): bc = window_source.batch_config if bc: bc.match_vrefresh(vrefresh) if len(packet)>=10: #added in 0.16 for scaled client displays: xdpi, ydpi = packet[8:10] if xdpi!=self.xdpi or ydpi!=self.ydpi: self.xdpi, self.ydpi = xdpi, ydpi log("new dpi: %ix%i", self.xdpi, self.ydpi) self.dpi = iround((self.xdpi + self.ydpi)/2.0) self.dpi_changed() if len(packet)>=8: #added in 0.16 for scaled client displays: ss.desktop_size_unscaled = packet[6:8] if len(packet)>=6: desktops, desktop_names = packet[4:6] ss.set_desktops(desktops, desktop_names) self.calculate_desktops() if len(packet)>=4: ss.set_screen_sizes(packet[3]) bigger = ss.screen_resize_bigger log("client requesting new size: %sx%s (bigger=%s)", width, height, bigger) self.set_screen_size(width, height, bigger) if len(packet)>=4: log.info("received updated display dimensions") log.info("client display size is %sx%s", width, height) log_screen_sizes(width, height, ss.screen_sizes) self.calculate_workarea(width, height) #ensures that DPI and antialias information gets reset: self.update_all_server_settings()
def sx(self, v): """ convert X coordinate from server to client """ return iround(v * self.xscale)
def xs(v): return iround(v/xscale)
def do_update_server_settings(self, settings, reset=False, dpi=0, double_click_time=0, double_click_distance=(-1, -1), antialias={}, cursor_size=-1): if not self._xsettings_enabled: log("ignoring xsettings update: %s", settings) return if reset: #FIXME: preserve serial? (what happens when we change values which had the same serial?) self.reset_settings() self._settings = {} if self._default_xsettings: #try to parse default xsettings into a dict: try: for _, prop_name, value, _ in self._default_xsettings[1]: self._settings[prop_name] = value except Exception as e: log("failed to parse %s", self._default_xsettings) log.warn("Warning: failed to parse default XSettings:") log.warn(" %s", e) old_settings = dict(self._settings) log("server_settings: old=%s, updating with=%s", nonl(old_settings), nonl(settings)) log( "overrides: dpi=%s, double click time=%s, double click distance=%s", dpi, double_click_time, double_click_distance) log("overrides: antialias=%s", antialias) self._settings.update(settings) for k, v in settings.items(): #cook the "resource-manager" value to add the DPI and/or antialias values: if k == b"resource-manager" and (dpi > 0 or antialias or cursor_size > 0): value = bytestostr(v) #parse the resources into a dict: values = {} options = value.split("\n") for option in options: if not option: continue parts = option.split(":\t", 1) if len(parts) != 2: log("skipped invalid option: '%s'", option) continue if parts[0] in BLACKLISTED_XSETTINGS: log("skipped blacklisted option: '%s'", option) continue values[parts[0]] = parts[1] if cursor_size > 0: values["Xcursor.size"] = cursor_size if dpi > 0: values["Xft.dpi"] = dpi values["Xft/DPI"] = dpi * 1024 values["gnome.Xft/DPI"] = dpi * 1024 if antialias: ad = typedict(antialias) subpixel_order = "none" sss = tuple(self._server_sources.values()) if len(sss) == 1: #only honour sub-pixel hinting if a single client is connected #and only when it is not using any scaling (or overriden with SCALED_FONT_ANTIALIAS): ss = sss[0] ds_unscaled = getattr(ss, "desktop_size_unscaled", None) ds_scaled = getattr(ss, "desktop_size", None) if SCALED_FONT_ANTIALIAS or (not ds_unscaled or ds_unscaled == ds_scaled): subpixel_order = ad.strget("orientation", "none").lower() values.update({ "Xft.antialias": ad.intget("enabled", -1), "Xft.hinting": ad.intget("hinting", -1), "Xft.rgba": subpixel_order, "Xft.hintstyle": _get_antialias_hintstyle(ad) }) log("server_settings: resource-manager values=%s", nonl(values)) #convert the dict back into a resource string: value = '' for vk, vv in values.items(): value += "%s:\t%s\n" % (vk, vv) #record the actual value used self._settings[b"resource-manager"] = value v = value.encode("utf-8") #cook xsettings to add various settings: #(as those may not be present in xsettings on some platforms.. like win32 and osx) if k==b"xsettings-blob" and \ (self.double_click_time>0 or self.double_click_distance!=(-1, -1) or antialias or dpi>0): #start by removing blacklisted options: def filter_blacklisted(): serial, values = v new_values = [] for _t, _n, _v, _s in values: if bytestostr(_n) in BLACKLISTED_XSETTINGS: log("skipped blacklisted option %s", (_t, _n, _v, _s)) else: new_values.append((_t, _n, _v, _s)) return serial, new_values v = filter_blacklisted() def set_xsettings_value(name, value_type, value): #remove existing one, if any: serial, values = v new_values = [(_t, _n, _v, _s) for (_t, _n, _v, _s) in values if _n != name] new_values.append((value_type, name, value, 0)) return serial, new_values def set_xsettings_int(name, value): if value < 0: #not set, return v unchanged return v return set_xsettings_value(name, XSettingsTypeInteger, value) if dpi > 0: v = set_xsettings_int(b"Xft/DPI", dpi * 1024) if double_click_time > 0: v = set_xsettings_int(b"Net/DoubleClickTime", self.double_click_time) if antialias: ad = typedict(antialias) v = set_xsettings_int(b"Xft/Antialias", ad.intget("enabled", -1)) v = set_xsettings_int(b"Xft/Hinting", ad.intget("hinting", -1)) v = set_xsettings_value( b"Xft/RGBA", XSettingsTypeString, ad.strget("orientation", "none").lower()) v = set_xsettings_value(b"Xft/HintStyle", XSettingsTypeString, _get_antialias_hintstyle(ad)) if double_click_distance != (-1, -1): #some platforms give us a value for each axis, #but X11 only has one, so take the average try: x, y = double_click_distance if x > 0 and y > 0: d = iround((x + y) / 2.0) d = max(1, min(128, d)) #sanitize it a bit v = set_xsettings_int(b"Net/DoubleClickDistance", d) except Exception as e: log.warn( "error setting double click distance from %s: %s", double_click_distance, e) if k not in old_settings or v != old_settings[k]: if k == b"xsettings-blob": self.set_xsettings(v) elif k == b"resource-manager": from xpra.x11.gtk_x11.prop import prop_set p = "RESOURCE_MANAGER" log("server_settings: setting %s to %s", nonl(p), nonl(v)) prop_set(self.root_window, p, "latin1", strtobytes(v).decode("latin1")) else: log.warn("Warning: unexpected setting '%s'", bytestostr(k))
def dpi(size_pixels, size_mm): if size_mm == 0: return 0 return iround(size_pixels * 254 / size_mm / 10)
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 set_screen_size(self, desired_w, desired_h, bigger=True): screenlog("set_screen_size%s", (desired_w, desired_h, bigger)) root_w, root_h = self.root_window.get_geometry()[2:4] if not self.randr: return root_w,root_h if desired_w==root_w and desired_h==root_h and not self.fake_xinerama: return root_w,root_h #unlikely: perfect match already! #clients may supply "xdpi" and "ydpi" (v0.15 onwards), or just "dpi", or nothing... xdpi = self.xdpi or self.dpi ydpi = self.ydpi or self.dpi screenlog("set_screen_size(%s, %s, %s) xdpi=%s, ydpi=%s", desired_w, desired_h, bigger, xdpi, ydpi) if xdpi<=0 or ydpi<=0: #use some sane defaults: either the command line option, or fallback to 96 #(96 is better than nothing, because we do want to set the dpi # to avoid Xdummy setting a crazy dpi from the virtual screen dimensions) xdpi = self.default_dpi or 96 ydpi = self.default_dpi or 96 #find the "physical" screen dimensions, so we can calculate the required dpi #(and do this before changing the resolution) wmm, hmm = 0, 0 client_w, client_h = 0, 0 sss = self._server_sources.values() for ss in sss: for s in ss.screen_sizes: if len(s)>=10: #(display_name, width, height, width_mm, height_mm, monitors, # work_x, work_y, work_width, work_height) client_w = max(client_w, s[1]) client_h = max(client_h, s[2]) wmm = max(wmm, s[3]) hmm = max(hmm, s[4]) if wmm>0 and hmm>0 and client_w>0 and client_h>0: #calculate "real" dpi: xdpi = iround(client_w * 25.4 / wmm) ydpi = iround(client_h * 25.4 / hmm) screenlog("calculated DPI: %s x %s (from w: %s / %s, h: %s / %s)", xdpi, ydpi, client_w, wmm, client_h, hmm) self.set_dpi(xdpi, ydpi) #try to find the best screen size to resize to: w, h = self.get_best_screen_size(desired_w, desired_h, bigger) #fakeXinerama: ui_clients = [s for s in self._server_sources.values() if s.ui_client] source = None screen_sizes = [] if len(ui_clients)==1: source = ui_clients[0] screen_sizes = source.screen_sizes else: screenlog("fakeXinerama can only be enabled for a single client (found %s)" % len(ui_clients)) xinerama_changed = save_fakeXinerama_config(self.fake_xinerama and len(ui_clients)==1, source, screen_sizes) #we can only keep things unchanged if xinerama was also unchanged #(many apps will only query xinerama again if they get a randr notification) if (w==root_w and h==root_h) and not xinerama_changed: screenlog.info("best resolution matching %sx%s is unchanged: %sx%s", desired_w, desired_h, w, h) return root_w, root_h try: if (w==root_w and h==root_h) and xinerama_changed: #xinerama was changed, but the RandR resolution will not be... #and we need a RandR change to force applications to re-query it #so we temporarily switch to another resolution to force #the change! (ugly! but this works) with xsync: temp = {} for tw,th in self.get_all_screen_sizes(): if tw!=w or th!=h: #use the number of extra pixels as key: #(so we can choose the closest resolution) temp[abs((tw*th) - (w*h))] = (tw, th) if not temp: screenlog.warn("cannot find a temporary resolution for Xinerama workaround!") else: k = sorted(temp.keys())[0] tw, th = temp[k] screenlog.info("temporarily switching to %sx%s as a Xinerama workaround", tw, th) with xsync: RandR.set_screen_size(tw, th) with xsync: RandR.get_screen_size() #Xdummy with randr 1.2: screenlog("using XRRSetScreenConfigAndRate with %ix%i", w, h) with xsync: RandR.set_screen_size(w, h) if self.randr_exact_size: #Xvfb with randr > 1.2: the resolution has been added #we can use XRRSetScreenSize: try: with xsync: RandR.xrr_set_screen_size(w, h, self.xdpi or self.dpi or 96, self.ydpi or self.dpi or 96) except XError: screenlog("XRRSetScreenSize failed", exc_info=True) screenlog("calling RandR.get_screen_size()") with xsync: root_w, root_h = RandR.get_screen_size() screenlog("RandR.get_screen_size()=%s,%s", root_w, root_h) screenlog("RandR.get_vrefresh()=%s", RandR.get_vrefresh()) if root_w!=w or root_h!=h: screenlog.warn("Warning: tried to set resolution to %ix%i", w, h) screenlog.warn(" and ended up with %ix%i", root_w, root_h) else: msg = "server virtual display now set to %sx%s" % (root_w, root_h) if desired_w!=root_w or desired_h!=root_h: msg += " (best match for %sx%s)" % (desired_w, desired_h) screenlog.info(msg) def show_dpi(): wmm, hmm = RandR.get_screen_size_mm() #ie: (1280, 1024) screenlog("RandR.get_screen_size_mm=%s,%s", wmm, hmm) actual_xdpi = iround(root_w * 25.4 / wmm) actual_ydpi = iround(root_h * 25.4 / hmm) if abs(actual_xdpi-xdpi)<=1 and abs(actual_ydpi-ydpi)<=1: screenlog.info("DPI set to %s x %s", actual_xdpi, actual_ydpi) screenlog("wanted: %s x %s", xdpi, ydpi) else: #should this be a warning: l = screenlog.info maxdelta = max(abs(actual_xdpi-xdpi), abs(actual_ydpi-ydpi)) if maxdelta>=10: l = log.warn messages = [ "DPI set to %s x %s (wanted %s x %s)" % (actual_xdpi, actual_ydpi, xdpi, ydpi), ] if maxdelta>=10: messages.append("you may experience scaling problems, such as huge or small fonts, etc") messages.append("to fix this issue, try the dpi switch, or use a patched Xorg dummy driver") self.notify_dpi_warning("\n".join(messages)) for i,message in enumerate(messages): l("%s%s", ["", " "][i>0], message) #show dpi via idle_add so server has time to change the screen size (mm) self.idle_add(show_dpi) except Exception as e: screenlog.error("ouch, failed to set new resolution: %s", e, exc_info=True) return root_w, root_h
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 iround(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_info = [ plug_name, xs(geom.x), ys(geom.y), xs(geom.width), ys(geom.height), wmm, hmm ] screenlog(" monitor %s: %s", j, monitor) if GTK_WORKAREA and hasattr(monitor, "get_workarea"): rect = monitor.get_workarea() monitor_info += list( swork(rect.x, rect.y, rect.width, rect.height)) elif workareas: w = workareas[j] monitor_info += list(swork(*w)) monitors.append(tuple(monitor_info)) 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: log("ignoring invalid screen size %ix%imm", wmm, hmm) if os.environ.get("WAYLAND_DISPLAY"): log(" (wayland display?)") 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) log(" 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
def cy(self, v): """ convert Y coordinate from client to server """ return iround(v / self.yscale)
def test_calculate_timesize_weighted_average(self): #event_time, size, elapsed_time now = monotonic() sample_size = 1000 data = [] ts = now - sample_size for _ in range(sample_size): s = random.randint(1000, 10000) v = random.random() data.append((ts, s, v)) ts += 1 a, ra = cystats.calculate_timesize_weighted_average(data) assert a > 0 and ra > 0 #the calculations use the ratio of the size divided by the elapsed time, #so check that a predictable ratio gives the expected value: for x in (5, 1000): v = [(now, i * x, x) for i in range(1, 1000)] a, ra = cystats.calculate_size_weighted_average(v) #but we need to round to an int to compare self.assertEqual(x, iround(a), "average should be %i, got %i" % (x, a)) self.assertEqual(x, iround(ra), "recent average should be %i, got %i" % (x, ra)) def t(v, ea, era): a, ra = cystats.calculate_size_weighted_average(v) self.assertEqual( iround(a), iround(ea), "average should be %s, got %s" % (iround(ea), iround(a))) self.assertEqual( iround(ra), iround(era), "recent average should be %s, got %s" % (iround(era), iround(ra))) #an old record won't make any difference #compared with one that was taken just now: for v in (1, 10, 1000): #1 day ago: if now > 60 * 60 * 24: t([(now - 60 * 60 * 24, 1000, 1000), (now, 1000, v)], v, v) t([(now - 60 * 60 * 24, 2 * 1000, 1000), (now, 1000, v)], v, v) #1 hour ago: if now > 60 * 60: t([(now - 60 * 60, 1000, 10), (now, 1000, v)], v, v) #but 100s ago starts to make a difference: t([(now - 100, 1000, 1), (now, 1000, 100)], 99, 100) #with only 10s: t([(now - 10, 1000, 1), (now, 1000, 100)], 92, 100) #1 second: t([(now - 1, 1000, 1), (now, 1000, 100)], 67, 92) #if using the same time, then size matters more: v = [(now, 100 * 1000, 1000), (now, 50 * 1000, 1000)] a, ra = cystats.calculate_size_weighted_average(v) #recent is the same as "normal" average: self.assertEqual(iround(a), iround(ra)) self.assertGreater(a, 75) #real data: T = monotonic() v = [(T - 21.557095, 157684, 9110), (T - 22.23345, 3744, 1279), (T - 22.376621, 3744, 706), (T - 22.515456, 3744, 1302), (T - 23.013887, 78, 1342), (T - 43.707768, 78, 920), (T - 44.043399, 78, 1558), (T - 44.046686, 78, 1119), (T - 44.048169, 78, 1007), (T - 44.049807, 1716, 626), (T - 44.053967, 78, 2841), (T - 44.23714, 78, 1393), (T - 44.238555, 78, 2903), (T - 44.242623, 78, 1167), (T - 44.244426, 1716, 1032), (T - 44.245675, 78, 720), (T - 44.392009, 78, 784), (T - 44.392771, 78, 737), (T - 44.396293, 78, 911), (T - 44.397466, 1716, 772), (T - 44.398027, 78, 1234), (T - 44.538323, 78, 1200), (T - 44.539683, 78, 586), (T - 44.542575, 78, 1203), (T - 44.544646, 1716, 1129), (T - 44.546205, 78, 979), (T - 44.701881, 78, 901), (T - 44.703987, 78, 448), (T - 44.708965, 78, 474), (T - 44.711481, 1716, 1444), (T - 44.713157, 78, 1033), (T - 44.848487, 78, 860), (T - 44.850604, 78, 1172), (T - 44.857039, 78, 1367), (T - 44.858723, 1716, 1078), (T - 44.859743, 78, 1876), (T - 44.993883, 78, 824), (T - 44.99714, 78, 796), (T - 45.001942, 78, 714), (T - 45.002884, 1716, 744), (T - 45.004841, 78, 652), (T - 45.772856, 78, 652)] raw_v = [x[2] for x in v] min_v = min(raw_v) max_v = max(raw_v) a, ra = cystats.calculate_size_weighted_average(v) self.assertLess(a, max_v) self.assertLess(ra, max_v) self.assertGreater(a, min_v) self.assertGreater(ra, min_v)
def set_screen_size(self, desired_w, desired_h): root_w, root_h = self.root_window.get_size() if not self.randr: return root_w,root_h if desired_w==root_w and desired_h==root_h and not self.fake_xinerama: return root_w,root_h #unlikely: perfect match already! #clients may supply "xdpi" and "ydpi" (v0.15 onwards), or just "dpi", or nothing... xdpi = self.xdpi or self.dpi ydpi = self.ydpi or self.dpi screenlog("set_screen_size(%s, %s) xdpi=%s, ydpi=%s", desired_w, desired_h, xdpi, ydpi) if xdpi<=0 or ydpi<=0: #use some sane defaults: either the command line option, or fallback to 96 #(96 is better than nothing, because we do want to set the dpi # to avoid Xdummy setting a crazy dpi from the virtual screen dimensions) xdpi = self.default_dpi or 96 ydpi = self.default_dpi or 96 #find the "physical" screen dimensions, so we can calculate the required dpi #(and do this before changing the resolution) wmm, hmm = 0, 0 client_w, client_h = 0, 0 sss = self._server_sources.values() for ss in sss: for s in ss.screen_sizes: if len(s)>=10: #display_name, width, height, width_mm, height_mm, monitors, work_x, work_y, work_width, work_height client_w = max(client_w, s[1]) client_h = max(client_h, s[2]) wmm = max(wmm, s[3]) hmm = max(hmm, s[4]) if wmm>0 and hmm>0 and client_w>0 and client_h>0: #calculate "real" dpi: xdpi = iround(client_w * 25.4 / wmm) ydpi = iround(client_h * 25.4 / hmm) screenlog("calculated DPI: %s x %s (from w: %s / %s, h: %s / %s)", xdpi, ydpi, client_w, wmm, client_h, hmm) self.set_dpi(xdpi, ydpi) #try to find the best screen size to resize to: new_size = None closest = {} for w,h in RandR.get_screen_sizes(): if w<desired_w or h<desired_h: distance = abs(w-desired_w)*abs(h-desired_h) closest[distance] = (w, h) continue #size is too small for client if new_size: ew,eh = new_size if ew*eh<w*h: continue #we found a better (smaller) candidate already new_size = w,h if not new_size: screenlog.warn("Warning: no matching resolution found for %sx%s", desired_w, desired_h) if len(closest)>0: new_size = sorted(closest.items())[0] screenlog.warn(" using %sx%s instead", *new_size) else: return root_w, root_h screenlog("best resolution for client(%sx%s) is: %s", desired_w, desired_h, new_size) #now actually apply the new settings: w, h = new_size #fakeXinerama: ui_clients = [s for s in self._server_sources.values() if s.ui_client] source = None screen_sizes = [] if len(ui_clients)==1: source = ui_clients[0] screen_sizes = source.screen_sizes else: screenlog("fakeXinerama can only be enabled for a single client (found %s)" % len(ui_clients)) xinerama_changed = save_fakeXinerama_config(self.fake_xinerama and len(ui_clients)==1, source, screen_sizes) #we can only keep things unchanged if xinerama was also unchanged #(many apps will only query xinerama again if they get a randr notification) if (w==root_w and h==root_h) and not xinerama_changed: screenlog.info("best resolution matching %sx%s is unchanged: %sx%s", desired_w, desired_h, w, h) return root_w, root_h try: if (w==root_w and h==root_h) and xinerama_changed: #xinerama was changed, but the RandR resolution will not be... #and we need a RandR change to force applications to re-query it #so we temporarily switch to another resolution to force #the change! (ugly! but this works) temp = {} for tw,th in RandR.get_screen_sizes(): if tw!=w or th!=h: #use the number of extra pixels as key: #(so we can choose the closest resolution) temp[abs((tw*th) - (w*h))] = (tw, th) if len(temp)==0: screenlog.warn("cannot find a temporary resolution for Xinerama workaround!") else: k = sorted(temp.keys())[0] tw, th = temp[k] screenlog.info("temporarily switching to %sx%s as a Xinerama workaround", tw, th) RandR.set_screen_size(tw, th) screenlog("calling RandR.set_screen_size(%s, %s)", w, h) with xsync: RandR.set_screen_size(w, h) screenlog("calling RandR.get_screen_size()") root_w, root_h = RandR.get_screen_size() screenlog("RandR.get_screen_size()=%s,%s", root_w, root_h) screenlog("RandR.get_vrefresh()=%s", RandR.get_vrefresh()) if root_w!=w or root_h!=h: screenlog.error("odd, failed to set the new resolution, " "tried to set it to %sx%s and ended up with %sx%s", w, h, root_w, root_h) else: msg = "server virtual display now set to %sx%s" % (root_w, root_h) if desired_w!=root_w or desired_h!=root_h: msg += " (best match for %sx%s)" % (desired_w, desired_h) screenlog.info(msg) def show_dpi(): wmm, hmm = RandR.get_screen_size_mm() #ie: (1280, 1024) screenlog("RandR.get_screen_size_mm=%s,%s", wmm, hmm) actual_xdpi = iround(root_w * 25.4 / wmm) actual_ydpi = iround(root_h * 25.4 / hmm) if abs(actual_xdpi-xdpi)<=1 and abs(actual_ydpi-ydpi)<=1: screenlog.info("DPI set to %s x %s", xdpi, ydpi) else: #should this be a warning: l = screenlog.info maxdelta = max(abs(actual_xdpi-xdpi), abs(actual_ydpi-ydpi)) if maxdelta>=10: l = log.warn l("DPI set to %s x %s (wanted %s x %s)", actual_xdpi, actual_ydpi, xdpi, ydpi) if maxdelta>=10: l(" you may experience scaling problems, such as huge or small fonts, etc") l(" to fix this issue, try the dpi switch, or use a patched Xorg dummy driver") #show dpi via idle_add so server has time to change the screen size (mm) self.idle_add(show_dpi) except Exception as e: screenlog.error("ouch, failed to set new resolution: %s", e, exc_info=True) return root_w, root_h
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 xs(v): return iround(v / xscale)
def r4cmp( v, rounding=1000.0): #ignore small differences in floats for scale values return iround(v * rounding)
def mint(v): #prefer int over float, #and even tolerate a 0.1% difference to get it: if iround(v) * 1000 == iround(v * 1000): return int(v) return v
def ys(v): return iround(v / yscale)
def sy(self, v): """ convert Y coordinate from server to client """ return iround(v * self.yscale)
def cx(self, v): """ convert X coordinate from client to server """ return iround(v / self.xscale)
def ys(v): return iround(v/yscale)