Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/client/mixins/display.py : 54%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of Xpra.
2# Copyright (C) 2010-2020 Antoine Martin <antoine@xpra.org>
3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
4# later version. See the file COPYING for details.
6import os
8from xpra.exit_codes import EXIT_INTERNAL_ERROR
9from xpra.platform.features import REINIT_WINDOWS
10from xpra.platform.gui import (
11 get_antialias_info, get_icc_info, get_display_icc_info, show_desktop, get_cursor_size,
12 get_xdpi, get_ydpi, get_number_of_desktops, get_desktop_names, get_wm_name,
13 )
14from xpra.scripts.main import check_display
15from xpra.scripts.config import FALSE_OPTIONS
16from xpra.net.common import MAX_PACKET_SIZE
17from xpra.os_util import monotonic_time
18from xpra.util import (
19 iround, envint, envfloat, envbool, log_screen_sizes, engs, flatten_dict, typedict,
20 XPRA_SCALING_NOTIFICATION_ID,
21 )
22from xpra.client.mixins.stub_client_mixin import StubClientMixin
23from xpra.log import Logger
25log = Logger("screen")
26workspacelog = Logger("client", "workspace")
27scalinglog = Logger("scaling")
29MONITOR_CHANGE_REINIT = envint("XPRA_MONITOR_CHANGE_REINIT")
32MIN_SCALING = envfloat("XPRA_MIN_SCALING", "0.1")
33MAX_SCALING = envfloat("XPRA_MAX_SCALING", "8")
34SCALING_OPTIONS = [float(x) for x in os.environ.get("XPRA_TRAY_SCALING_OPTIONS", "0.25,0.5,0.666,1,1.25,1.5,2.0,3.0,4.0,5.0").split(",") if float(x)>=MIN_SCALING and float(x)<=MAX_SCALING]
35SCALING_EMBARGO_TIME = int(os.environ.get("XPRA_SCALING_EMBARGO_TIME", "1000"))/1000.0
36SYNC_ICC = envbool("XPRA_SYNC_ICC", True)
39def r4cmp(v, rounding=1000.0): #ignore small differences in floats for scale values
40 return iround(v*rounding)
41def fequ(v1, v2):
42 return r4cmp(v1)==r4cmp(v2)
45"""
46Utility superclass for clients that handle a desktop / display
47Adds client-side scaling handling
48"""
49class DisplayClient(StubClientMixin):
50 __signals__ = ["scaling-changed"]
52 def __init__(self):
53 check_display()
54 StubClientMixin.__init__(self)
55 self.dpi = 0
56 self.can_scale = False
57 self.initial_scaling = 1, 1
58 self.xscale, self.yscale = self.initial_scaling
59 self.scale_change_embargo = float("inf")
60 self.desktop_fullscreen = False
61 self.desktop_scaling = False
62 self.screen_size_change_timer = None
64 self.server_desktop_size = None
65 self.server_actual_desktop_size = None
66 self.server_max_desktop_size = None
67 self.server_display = None
68 self.server_randr = False
69 self.server_opengl = None
72 def init(self, opts):
73 self.desktop_fullscreen = opts.desktop_fullscreen
74 self.desktop_scaling = opts.desktop_scaling
75 self.dpi = int(opts.dpi)
76 self.can_scale = opts.desktop_scaling not in FALSE_OPTIONS
77 if self.can_scale:
78 self.parse_scaling(opts.desktop_scaling)
80 def parse_scaling(self, desktop_scaling):
81 root_w, root_h = self.get_root_size()
82 from xpra.client.scaling_parser import parse_scaling
83 self.initial_scaling = parse_scaling(desktop_scaling, root_w, root_h, MIN_SCALING, MAX_SCALING)
84 self.xscale, self.yscale = self.initial_scaling
86 def cleanup(self):
87 ssct = self.screen_size_change_timer
88 if ssct:
89 self.screen_size_change_timer = None
90 self.source_remove(ssct)
93 def get_screen_sizes(self, xscale=1, yscale=1):
94 raise NotImplementedError()
96 def get_root_size(self):
97 raise NotImplementedError()
100 def get_info(self):
101 screen = self.get_screen_caps()
102 screen["scaling"] = self.get_scaling_caps()
103 screen["dpi"] = self.get_dpi_caps()
104 info = {
105 "screen" : screen,
106 }
107 return info
109 ######################################################################
110 # hello:
111 def get_caps(self) -> dict:
112 caps = {
113 "randr_notify" : True,
114 "show-desktop" : True,
115 }
116 wm_name = get_wm_name()
117 if wm_name:
118 caps["wm_name"] = wm_name.encode("utf8")
120 self._last_screen_settings = self.get_screen_settings()
121 root_w, root_h, sss, ndesktops, desktop_names, u_root_w, u_root_h, _, _ = self._last_screen_settings
122 if u_root_w and u_root_h:
123 caps["desktop_size"] = self.cp(u_root_w, u_root_h)
124 if ndesktops:
125 caps["desktops"] = ndesktops
126 caps["desktop.names"] = tuple(x.encode("utf8") for x in desktop_names)
128 ss = self.get_screen_sizes()
129 self._current_screen_sizes = ss
131 log.info(" desktop size is %sx%s with %s screen%s:", u_root_w, u_root_h, len(ss), engs(ss))
132 log_screen_sizes(u_root_w, u_root_h, ss)
133 if self.xscale!=1 or self.yscale!=1:
134 caps["screen_sizes.unscaled"] = ss
135 caps["desktop_size.unscaled"] = u_root_w, u_root_h
136 root_w, root_h = self.cp(u_root_w, u_root_h)
137 if fequ(self.xscale, self.yscale):
138 sinfo = "%i%%" % iround(self.xscale*100)
139 else:
140 sinfo = "%i%% x %i%%" % (iround(self.xscale*100), iround(self.yscale*100))
141 scaled_up = u_root_w>root_w or u_root_h>root_h
142 log.info(" %sscaled to %s, virtual screen size: %ix%i",
143 "up" if scaled_up else "down", sinfo, root_w, root_h)
144 log_screen_sizes(root_w, root_h, sss)
145 else:
146 root_w, root_h = u_root_w, u_root_h
147 sss = ss
148 caps["screen_sizes"] = sss
150 caps.update(self.get_screen_caps())
151 caps.update(flatten_dict({
152 "dpi" : self.get_dpi_caps(),
153 "screen-scaling" : self.get_scaling_caps(),
154 }))
155 return caps
157 def get_dpi_caps(self) -> dict:
158 #command line (or config file) override supplied:
159 caps = {}
160 dpi = 0
161 if self.dpi>0:
162 #scale it:
163 dpi = iround((self.cx(self.dpi) + self.cy(self.dpi))/2.0)
164 else:
165 #not supplied, use platform detection code:
166 #platforms may also provide per-axis dpi (later win32 versions do)
167 xdpi = self.get_xdpi()
168 ydpi = self.get_ydpi()
169 log("xdpi=%i, ydpi=%i", xdpi, ydpi)
170 if xdpi>0 and ydpi>0:
171 xdpi = self.cx(xdpi)
172 ydpi = self.cy(ydpi)
173 dpi = iround((xdpi+ydpi)/2.0)
174 caps = {
175 "x" : xdpi,
176 "y" : ydpi,
177 }
178 if dpi:
179 caps[""] = dpi
180 log("get_dpi_caps()=%s", caps)
181 return caps
183 def get_scaling_caps(self) -> dict:
184 return {
185 "" : True,
186 "enabled" : self.xscale!=1 or self.yscale!=1,
187 "values" : (int(1000*self.xscale), int(1000*self.yscale)),
188 }
190 def get_screen_caps(self) -> dict:
191 caps = {
192 "antialias" : get_antialias_info(),
193 "cursor" : {
194 "size" : int(2*get_cursor_size()/(self.xscale+self.yscale)),
195 },
196 }
197 if SYNC_ICC:
198 caps.update({
199 "icc" : self.get_icc_info(),
200 "display-icc" : self.get_display_icc_info(),
201 })
202 return caps
204 #this is the format we should be moving towards
205 #with proper namespace:
206 #def get_info(self) -> dict:
207 # sinfo = self.get_screen_caps()
208 # sinfo["scaling"] = self.get_scaling_caps()
209 # sinfo["dpi"] = self.get_dpi_caps()
210 # return {
211 # "desktop" : self.get_desktop_caps(),
212 # "screen" : sinfo,
213 # }
215 #def get_desktop_info(self):
216 # caps = {
217 # "show" : True,
218 # }
219 # wm_name = get_wm_name()
220 # if wm_name:
221 # caps["wm_name"] = wm_name
222 # _, _, sss, ndesktops, desktop_names, u_root_w, u_root_h, xdpi, ydpi = self._last_screen_settings
223 # caps["unscaled-size"] = u_root_w, u_root_h
224 # caps["size"] = self.cp(u_root_w, u_root_h)
225 # caps["dpi"] = (xdpi, ydpi)
226 # caps["count"] = ndesktops
227 # caps["names"] = desktop_names
228 # caps["screens"] = len(sss)
231 def parse_server_capabilities(self, c : typedict) -> bool:
232 self.server_display = c.strget("display")
233 self.server_desktop_size = c.intpair("desktop_size")
234 log("server desktop size=%s", self.server_desktop_size)
235 self.server_max_desktop_size = c.intpair("max_desktop_size")
236 self.server_actual_desktop_size = c.intpair("actual_desktop_size")
237 log("server actual desktop size=%s", self.server_actual_desktop_size)
238 self.server_randr = c.boolget("resize_screen")
239 log("server has randr: %s", self.server_randr)
240 self.server_opengl = c.dictget("opengl")
241 return True
243 def process_ui_capabilities(self, c : typedict):
244 self.server_is_desktop = c.boolget("shadow") or c.boolget("desktop")
245 skip_vfb_size_check = False #if we decide not to use scaling, skip warnings
246 if not fequ(self.xscale, 1.0) or not fequ(self.yscale, 1.0):
247 #scaling is used, make sure that we need it and that the server can support it
248 #(without rounding support, size-hints can cause resize loops)
249 if self.server_is_desktop and not self.desktop_fullscreen:
250 #don't honour auto mode in this case
251 if self.desktop_scaling=="auto":
252 log.info(" not scaling a %s server", c.strget("type", "shadow"))
253 skip_vfb_size_check = self.xscale>1 or self.yscale>1
254 self.scale_change_embargo = 0
255 self.scalingoff()
256 if self.can_scale:
257 self.may_adjust_scaling()
258 if not self.server_is_desktop and not skip_vfb_size_check and self.server_max_desktop_size:
259 avail_w, avail_h = self.server_max_desktop_size
260 root_w, root_h = self.get_root_size()
261 log("validating server_max_desktop_size=%s vs root size=%s",
262 self.server_max_desktop_size, (root_w, root_h))
263 if self.cx(root_w)!=root_w or self.cy(root_h)!=root_h:
264 log(" root size scaled to %s", (self.cx(root_w), self.cy(root_h)))
265 if self.cx(root_w)>(avail_w+1) or self.cy(root_h)>(avail_h+1):
266 log.warn("Server's virtual screen is too small")
267 log.warn(" server: %sx%s vs client: %sx%s", avail_w, avail_h, self.cx(root_w), self.cy(root_h))
268 log.warn(" you may see strange behavior,")
269 log.warn(" please see https://xpra.org/trac/wiki/Xdummy#Configuration")
270 #now that we have the server's screen info, allow scale changes:
271 self.scale_change_embargo = 0
272 self.set_max_packet_size()
274 def set_max_packet_size(self):
275 root_w, root_h = self.cp(*self.get_root_size())
276 maxw, maxh = root_w, root_h
277 try:
278 server_w, server_h = self.server_actual_desktop_size
279 maxw = max(root_w, server_w)
280 maxh = max(root_h, server_h)
281 except ValueError:
282 pass
283 if maxw<=0 or maxh<=0 or maxw>=32768 or maxh>=32768:
284 message = "invalid maximum desktop size: %ix%i" % (maxw, maxh)
285 log(message)
286 self.quit(EXIT_INTERNAL_ERROR)
287 raise SystemExit(message)
288 if maxw>=16384 or maxh>=16384:
289 log.warn("Warning: the desktop size is extremely large: %ix%i", maxw, maxh)
290 #max packet size to accomodate
291 # a full screen RGBX (32 bits) uncompressed image
292 # also with enough headroom for some metadata (4k)
293 p = self._protocol
294 if p:
295 #we can't assume to have a real ClientConnection object:
296 p.max_packet_size = max(MAX_PACKET_SIZE, maxw*maxh*4 + 4*1024)
297 p.abs_max_packet_size = maxw*maxh*4*4 + 4*1024
298 log("maximum packet size set to %i", p.max_packet_size)
301 def has_transparency(self) -> bool:
302 return False
304 def get_icc_info(self) -> dict:
305 return get_icc_info()
307 def get_display_icc_info(self) -> dict:
308 return get_display_icc_info()
310 def _process_show_desktop(self, packet):
311 show = packet[1]
312 log("calling %s(%s)", show_desktop, show)
313 show_desktop(show)
315 def _process_desktop_size(self, packet):
316 root_w, root_h, max_w, max_h = packet[1:5]
317 log("server has resized the desktop to: %sx%s (max %sx%s)", root_w, root_h, max_w, max_h)
318 self.server_max_desktop_size = max_w, max_h
319 self.server_actual_desktop_size = root_w, root_h
320 if self.can_scale:
321 self.may_adjust_scaling()
324 def may_adjust_scaling(self):
325 log("may_adjust_scaling() server_is_desktop=%s, desktop_fullscreen=%s",
326 self.server_is_desktop, self.desktop_fullscreen)
327 if self.server_is_desktop and not self.desktop_fullscreen:
328 #don't try to make it fit
329 return
330 assert self.can_scale
331 max_w, max_h = self.server_max_desktop_size #ie: server limited to 8192x4096?
332 w, h = self.get_root_size() #ie: 5760, 2160
333 sw, sh = self.cp(w, h) #ie: upscaled to: 11520x4320
334 scalinglog("may_adjust_scaling() server max desktop size=%s, server actual desktop size=%s",
335 self.server_max_desktop_size, self.server_actual_desktop_size)
336 scalinglog("may_adjust_scaling() client root size=%s", self.get_root_size())
337 scalinglog(" scaled client root size using %sx%s: %s", self.xscale, self.yscale, (sw, sh))
338 #server size is too small for the client screen size with the current scaling value,
339 #calculate the minimum scaling to fit it:
340 def clamp(v):
341 return max(MIN_SCALING, min(MAX_SCALING, v))
342 if self.desktop_fullscreen:
343 sw, sh = self.server_actual_desktop_size
344 x = clamp(w/sw)
345 y = clamp(h/sh)
346 else:
347 if sw<(max_w+1) and sh<(max_h+1):
348 #no change needed
349 return
350 x = clamp(w/max_w)
351 y = clamp(h/max_h)
352 #avoid wonky scaling:
353 if not 0.75<x/y<1.25:
354 x = y = min(x, y)
355 def mint(v):
356 #prefer int over float,
357 #and even tolerate a 0.1% difference to get it:
358 if iround(v)*1000==iround(v*1000):
359 return int(v)
360 return v
361 self.xscale = mint(x)
362 self.yscale = mint(y)
363 scalinglog(" xscale=%s, yscale=%s", self.xscale, self.yscale)
364 #to use the same scale for both axes:
365 #self.xscale = mint(max(x, y))
366 #self.yscale = self.xscale
367 summary = "Desktop scaling adjusted to accomodate the server"
368 xstr = ("%.3f" % self.xscale).rstrip("0")
369 ystr = ("%.3f" % self.yscale).rstrip("0")
370 messages = [
371 "server desktop size is %ix%i" % (max_w, max_h),
372 "using scaling factor %s x %s" % (xstr, ystr),
373 ]
374 self.may_notify(XPRA_SCALING_NOTIFICATION_ID, summary, "\n".join(messages), icon_name="scaling")
375 scalinglog.warn("Warning: %s", summary)
376 for m in messages:
377 scalinglog.warn(" %s", m)
378 self.emit("scaling-changed")
381 ######################################################################
382 # screen scaling:
383 def fsx(self, v):
384 """ convert X coordinate from server to client """
385 return v*self.xscale
386 def fsy(self, v):
387 """ convert Y coordinate from server to client """
388 return v*self.yscale
389 def sx(self, v) -> int:
390 """ convert X coordinate from server to client """
391 return iround(self.fsx(v))
392 def sy(self, v) -> int:
393 """ convert Y coordinate from server to client """
394 return iround(self.fsy(v))
395 def srect(self, x, y, w, h):
396 """ convert rectangle coordinates from server to client """
397 return self.sx(x), self.sy(y), self.sx(w), self.sy(h)
398 def sp(self, x, y):
399 """ convert X,Y coordinates from server to client """
400 return self.sx(x), self.sy(y)
402 def cx(self, v) -> int:
403 """ convert X coordinate from client to server """
404 return iround(v/self.xscale)
405 def cy(self, v) -> int:
406 """ convert Y coordinate from client to server """
407 return iround(v/self.yscale)
408 def crect(self, x, y, w, h):
409 """ convert rectangle coordinates from client to server """
410 return self.cx(x), self.cy(y), self.cx(w), self.cy(h)
411 def cp(self, x, y):
412 """ convert X,Y coordinates from client to server """
413 return self.cx(x), self.cy(y)
416 ######################################################################
417 # desktop, screen and scaling:
418 def desktops_changed(self, *args):
419 workspacelog("desktops_changed%s", args)
420 self.screen_size_changed(*args)
422 def workspace_changed(self, *args):
423 workspacelog("workspace_changed%s", args)
424 for win in self._id_to_window.values():
425 win.workspace_changed()
427 def screen_size_changed(self, *args):
428 log("screen_size_changed(%s) timer=%s", args, self.screen_size_change_timer)
429 if self.screen_size_change_timer:
430 return
431 #update via timer so the data is more likely to be final (up to date) when we query it,
432 #some properties (like _NET_WORKAREA for X11 clients via xposix "ClientExtras") may
433 #trigger multiple calls to screen_size_changed, delayed by some amount
434 #(sometimes up to 1s..)
435 delay = 1000
436 #if we are suspending, wait longer:
437 #(better chance that the suspend-resume cycle will have completed)
438 if self._suspended_at>0 and self._suspended_at-monotonic_time()<5*1000:
439 delay = 5*1000
440 self.screen_size_change_timer = self.timeout_add(delay, self.do_process_screen_size_change)
442 def do_process_screen_size_change(self):
443 self.screen_size_change_timer = None
444 self.update_screen_size()
445 log("do_process_screen_size_change() MONITOR_CHANGE_REINIT=%s, REINIT_WINDOWS=%s",
446 MONITOR_CHANGE_REINIT, REINIT_WINDOWS)
447 if MONITOR_CHANGE_REINIT and MONITOR_CHANGE_REINIT=="0":
448 return
449 if MONITOR_CHANGE_REINIT or REINIT_WINDOWS:
450 log.info("screen size change: will reinit the windows")
451 self.reinit_windows()
452 self.reinit_window_icons()
455 def get_screen_settings(self):
456 u_root_w, u_root_h = self.get_root_size()
457 root_w, root_h = self.cp(u_root_w, u_root_h)
458 self._current_screen_sizes = self.get_screen_sizes()
459 sss = self.get_screen_sizes(self.xscale, self.yscale)
460 ndesktops = get_number_of_desktops()
461 desktop_names = get_desktop_names()
462 log("get_screen_settings() sizes=%s, %s desktops: %s", sss, ndesktops, desktop_names)
463 if self.dpi>0:
464 #use command line value supplied, but scale it:
465 xdpi = ydpi = self.dpi
466 log("get_screen_settings() dpi=%s", self.dpi)
467 else:
468 #not supplied, use platform detection code:
469 xdpi = self.get_xdpi()
470 ydpi = self.get_ydpi()
471 log("get_screen_settings() xdpi=%s, ydpi=%s", get_xdpi(), get_ydpi())
472 xdpi = self.cx(xdpi)
473 ydpi = self.cy(ydpi)
474 log("get_screen_settings() scaled: xdpi=%s, ydpi=%s", xdpi, ydpi)
475 return (root_w, root_h, sss, ndesktops, desktop_names, u_root_w, u_root_h, xdpi, ydpi)
477 def update_screen_size(self):
478 self.screen_size_change_timer = None
479 screen_settings = self.get_screen_settings()
480 log("update_screen_size() new settings=%s", screen_settings)
481 log("update_screen_size() current settings=%s", self._last_screen_settings)
482 if self._last_screen_settings==screen_settings:
483 log("screen size unchanged")
484 return
485 root_w, root_h, sss = screen_settings[:3]
486 log.info("sending updated screen size to server: %sx%s with %s screens", root_w, root_h, len(sss))
487 log_screen_sizes(root_w, root_h, sss)
488 if self.server_desktop_size:
489 self.send("desktop_size", *screen_settings)
490 self._last_screen_settings = screen_settings
491 #update the max packet size (may have gone up):
492 self.set_max_packet_size()
494 def get_xdpi(self) -> int:
495 return get_xdpi()
497 def get_ydpi(self) -> int:
498 return get_ydpi()
501 def scaleup(self):
502 scaling = max(self.xscale, self.yscale)
503 options = [v for v in SCALING_OPTIONS if r4cmp(v, 10)>r4cmp(scaling, 10)]
504 scalinglog("scaleup() options>%s : %s", r4cmp(scaling, 1000)/1000.0, options)
505 if options:
506 self._scaleto(min(options))
508 def scaledown(self):
509 scaling = max(self.xscale, self.yscale)
510 options = [v for v in SCALING_OPTIONS if r4cmp(v, 10)<r4cmp(scaling, 10)]
511 scalinglog("scaledown() options<%s : %s", r4cmp(scaling, 1000)/1000.0, options)
512 if options:
513 self._scaleto(max(options))
515 def _scaleto(self, new_scaling):
516 scaling = max(self.xscale, self.yscale)
517 scalinglog("_scaleto(%s) current value=%s", r4cmp(new_scaling, 1000)/1000.0, r4cmp(scaling, 1000)/1000.0)
518 if new_scaling>0:
519 self.scale_change(new_scaling/scaling, new_scaling/scaling)
521 def scalingoff(self):
522 self.scaleset(1, 1)
524 def scalereset(self):
525 self.scaleset(*self.initial_scaling)
527 def scaleset(self, xscale=1, yscale=1):
528 scalinglog("scaleset(%s, %s) current scaling: %s, %s", xscale, yscale, self.xscale, self.yscale)
529 self.scale_change(xscale/self.xscale, yscale/self.yscale)
531 def scale_change(self, xchange=1, ychange=1):
532 scalinglog("scale_change(%s, %s)", xchange, ychange)
533 if self.server_is_desktop and self.desktop_fullscreen:
534 scalinglog("scale_change(%s, %s) ignored, fullscreen shadow mode is active", xchange, ychange)
535 return
536 if not self.can_scale:
537 scalinglog("scale_change(%s, %s) ignored, scaling is disabled", xchange, ychange)
538 return
539 if self.screen_size_change_timer:
540 scalinglog("scale_change(%s, %s) screen size change is already pending", xchange, ychange)
541 return
542 if monotonic_time()<self.scale_change_embargo:
543 scalinglog("scale_change(%s, %s) screen size change not permitted during embargo time - try again",
544 xchange, ychange)
545 return
546 def clamp(v):
547 return max(MIN_SCALING, min(MAX_SCALING, v))
548 xscale = clamp(self.xscale*xchange)
549 yscale = clamp(self.yscale*ychange)
550 scalinglog("scale_change xscale: clamp(%s*%s)=%s", self.xscale, xchange, xscale)
551 scalinglog("scale_change yscale: clamp(%s*%s)=%s", self.yscale, ychange, yscale)
552 if fequ(xscale, self.xscale) and fequ(yscale, self.yscale):
553 scalinglog("scaling unchanged: %sx%s", self.xscale, self.yscale)
554 return
555 #re-calculate change values against clamped scale:
556 xchange = xscale / self.xscale
557 ychange = yscale / self.yscale
558 #check against maximum server supported size:
559 maxw, maxh = self.server_max_desktop_size
560 root_w, root_h = self.get_root_size()
561 sw = int(root_w / xscale)
562 sh = int(root_h / yscale)
563 scalinglog("scale_change root size=%s x %s, scaled to %s x %s", root_w, root_h, sw, sh)
564 scalinglog("scale_change max server desktop size=%s x %s", maxw, maxh)
565 if not self.server_is_desktop and (sw>(maxw+1) or sh>(maxh+1)):
566 #would overflow..
567 summary = "Invalid Scale Factor"
568 messages = [
569 "cannot scale by %i%% x %i%% or lower" % ((100*xscale), (100*yscale)),
570 "the scaled client screen %i x %i -> %i x %i" % (root_w, root_h, sw, sh),
571 " would overflow the server's screen: %i x %i" % (maxw, maxh),
572 ]
573 self.may_notify(XPRA_SCALING_NOTIFICATION_ID, summary, "\n".join(messages), icon_name="scaling")
574 scalinglog.warn("Warning: %s", summary)
575 for m in messages:
576 scalinglog.warn(" %s", m)
577 return
578 self.xscale = xscale
579 self.yscale = yscale
580 scalinglog("scale_change new scaling: %sx%s, change: %sx%s", self.xscale, self.yscale, xchange, ychange)
581 self.scale_reinit(xchange, ychange)
583 def scale_reinit(self, xchange=1.0, ychange=1.0):
584 #wait at least one second before changing again:
585 self.scale_change_embargo = monotonic_time()+SCALING_EMBARGO_TIME
586 if fequ(self.xscale, self.yscale):
587 scalinglog.info("setting scaling to %i%%:", iround(100*self.xscale))
588 else:
589 scalinglog.info("setting scaling to %i%% x %i%%:", iround(100*self.xscale), iround(100*self.yscale))
590 self.update_screen_size()
591 #re-initialize all the windows with their new size
592 def new_size_fn(w, h):
593 minx, miny = 16384, 16384
594 if self.max_window_size!=(0, 0):
595 minx, miny = self.max_window_size
596 return max(1, min(minx, iround(w*xchange))), max(1, min(miny, iround(h*ychange)))
597 self.resize_windows(new_size_fn)
598 self.reinit_window_icons()
599 self.emit("scaling-changed")
602 def init_authenticated_packet_handlers(self):
603 self.add_packet_handler("show-desktop", self._process_show_desktop)
604 self.add_packet_handler("desktop_size", self._process_desktop_size)