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