Skip to content

Commit

Permalink
#3524 fixups, cleanups, etc
Browse files Browse the repository at this point in the history
* make it possible to leave the vfb as it is when starting with `--resize-display=none`,
* try to ensure all monitors are within the boundaries of the screen,
* delete all extra monitors we don't need,
* fix monitor position adjustements when inserting one,
* refresh all windows after modifying the monitors,
* make the default list of new resolutions shown in the tray menu configurable
  • Loading branch information
totaam committed Apr 20, 2022
1 parent 9a5b3d2 commit 137a222
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 85 deletions.
12 changes: 8 additions & 4 deletions xpra/client/gtk_base/gtk_tray_menu_base.py
@@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
# This file is part of Xpra.
# Copyright (C) 2011-2021 Antoine Martin <antoine@xpra.org>
# Copyright (C) 2011-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.

import os
import re
from gi.repository import GLib, Gtk

Expand Down Expand Up @@ -56,6 +57,9 @@
MENU_ICONS = envbool("XPRA_MENU_ICONS", True)


NEW_MONITOR_RESOLUTIONS = os.environ.get("XPRA_NEW_MONITOR_RESOLUTIONS",
"640x480,1024x768,1600x1200,FHD,4K").split(",")

CLIPBOARD_LABELS = ["Clipboard", "Primary", "Secondary"]
CLIPBOARD_LABEL_TO_NAME = {
"Clipboard" : "CLIPBOARD",
Expand Down Expand Up @@ -1191,7 +1195,7 @@ def populate_monitors(*args):
for x in menu.get_children():
menu.remove(x)
def monitor_changed(mitem, index):
log("monitor_changed(%s, %s)", monitor, index)
log("monitor_changed(%s, %s)", mitem, index)
self.client.send_remove_monitor(index)
for i, monitor in self.client.server_monitors.items():
mitem = Gtk.CheckMenuItem(label=monitor.get("name", "VFB-%i" % i))
Expand All @@ -1204,11 +1208,11 @@ def monitor_changed(mitem, index):
resolutions_menu = Gtk.Menu()
add_monitor_item.set_submenu(resolutions_menu)
def add_monitor(mitem, resolution):
log("add_monitor(%s)", resolution)
log("add_monitor(%s, %s)", mitem, resolution)
#older servers may not have all the aliases:
resolution = RESOLUTION_ALIASES.get(resolution, resolution)
self.client.send_add_monitor(resolution)
for resolution in ("1024x768", "1080p", "4k"):
for resolution in NEW_MONITOR_RESOLUTIONS:
mitem = self.menuitem(resolution)
mitem.connect("activate", add_monitor, resolution)
resolutions_menu.append(mitem)
Expand Down
4 changes: 2 additions & 2 deletions xpra/client/mixins/display.py
Expand Up @@ -240,10 +240,10 @@ def parse_server_capabilities(self, c : typedict) -> bool:
self.server_randr = c.boolget("resize_screen")
log("server has randr: %s", self.server_randr)
self.server_opengl = c.dictget("opengl")
Logger("opengl")("server opengl=%s", self.server_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")
Logger("screen").warn("server multi-monitors=%s, monitors=%s",
log("server multi-monitors=%s, monitors=%s",
self.server_multi_monitors, self.server_monitors)
return True

Expand Down
12 changes: 9 additions & 3 deletions xpra/server/mixins/window_server.py
Expand Up @@ -310,10 +310,12 @@ def update_batch_config(self, proto, wid_windows, batch_props, client_properties
self._set_client_properties(proto, wid, window, client_properties)
ss.update_batch(wid, window, batch_props)

def _refresh_windows(self, proto, wid_windows, opts):
def _refresh_windows(self, proto, wid_windows, opts=None):
ss = self.get_server_source(proto)
if ss is None:
return
if ss:
self.do_refresh_windows(ss, wid_windows, opts)

def do_refresh_windows(self, ss, wid_windows, opts=None):
for wid, window in wid_windows.items():
if window is None or not window.is_managed():
continue
Expand All @@ -325,6 +327,10 @@ def _refresh_windows(self, proto, wid_windows, opts):
def _idle_refresh_all_windows(self, proto):
self.idle_add(self._refresh_windows, proto, self._id_to_window, {})

def refresh_all_windows(self):
for ss in tuple(self._server_sources.values()):
if not isinstance(ss, WindowsMixin):
self.do_refresh_windows(ss, self._id_to_window)

def get_window_position(self, _window):
#where the window is actually mapped on the server screen:
Expand Down
88 changes: 52 additions & 36 deletions xpra/x11/bindings/randr_bindings.pyx
Expand Up @@ -1026,30 +1026,29 @@ cdef class RandRBindingsInstance(X11CoreBindingsInstance):
primary = 0
try:
#re-configure all crtcs:
for mi in range(rsc.ncrtc):
m = monitor_defs.get(mi, {})
crtc = rsc.crtcs[mi]
assert rsc.noutput>mi
output = rsc.outputs[mi]
log("monitor %i is crtc %i and output %i: %s", mi, crtc, output, m)
for i in range(rsc.ncrtc):
m = monitor_defs.get(i, {})
crtc = rsc.crtcs[i]
assert rsc.noutput>i
output = rsc.outputs[i]
log("%i: crtc %i and output %i: %s", i, crtc, output, m)
crtc_info = XRRGetCrtcInfo(self.display, rsc, crtc)
if not crtc_info:
log.error("Error: crtc %i not found (%#x)", mi, crtc)
log.error("Error: crtc %i not found (%#x)", i, crtc)
continue
try:
if m.get("primary", False):
primary = mi
primary = i
width = m.get("width", 0)
height = m.get("height", 0)

output_info = XRRGetOutputInfo(self.display, rsc, output)
if not output_info:
log.error("Error: output %i not found (%#x)", mi, output)
log.error("Error: output %i not found (%#x)", i, output)
continue
if crtc_info.noutput==0 and output_info.connection==RR_Disconnected and not m:
#crtc is not enabled and the corresponding output is not connected,
#which is exactly what we want, so just leave it alone
log("crtc and output %i are already disabled", mi)
log("crtc and output %i are already disabled", i)
continue
noutput = 1
mode = 0
Expand Down Expand Up @@ -1081,7 +1080,7 @@ cdef class RandRBindingsInstance(X11CoreBindingsInstance):
mode = self.do_add_screen_size(mode_name, width, height)
assert mode!=0, "mode %ix%i not found" % (width, height)
XRRAddOutputMode(self.display, output, mode)
log("mode %r (%#x) added to output %i (%i)", mode_name, mode, mi, output)
log("mode %r (%#x) added to output %i (%i)", mode_name, mode, i, output)
else:
noutput = 0

Expand All @@ -1094,17 +1093,18 @@ cdef class RandRBindingsInstance(X11CoreBindingsInstance):
CurrentTime, x, y, mode,
RR_Rotate_0, &output, noutput)
if r:
raise Exception("failed to set crtc config for monitor %i" % mi)
raise Exception("failed to set crtc config for monitor %i" % i)
mmw = m.get("mm-width", dpi96(width))
mmh = m.get("mm-height", dpi96(height))
self.set_output_int_property(mi, "WIDTH_MM", mmw)
self.set_output_int_property(mi, "HEIGHT_MM", mmh)
self.set_output_int_property(i, "WIDTH_MM", mmw)
self.set_output_int_property(i, "HEIGHT_MM", mmh)
#this allows us to disconnect the output of this crtc:
self.set_output_int_property(mi, "SUSPENDED", not bool(m))
self.set_output_int_property(i, "SUSPENDED", not bool(m))
posinfo = ""
if x or y:
posinfo = " at %i,%i" % (x, y)
log.info("setting dummy crtc %i to %ix%i (%ix%i mm)%s", mi, width, height, mmw, mmh, posinfo)
log.info("setting dummy crtc and output %i to %ix%i (%ix%i mm)%s",
i, width, height, mmw, mmh, posinfo)
finally:
if output_info:
XRRFreeOutputInfo(output_info)
Expand All @@ -1118,18 +1118,20 @@ cdef class RandRBindingsInstance(X11CoreBindingsInstance):
if not monitors:
log.error("Error: failed to retrieve the list of monitors")
return False
#start by removing the ones we don't use,
#which makes it easier to prevent name Atom conflicts
log("got %i monitors for %s crtcs", nmonitors, len(monitor_defs))
#start by renaming the ones we do use and removing the ones we don't,
#which makes it easier to prevent name Atom conflicts:
try:
#we only need as many monitors as we have crtcs,
#delete any extra ones:
for mi in range(nmonitors, len(monitor_defs)):
name_atom = monitors[mi].name
log("deleting monitor %i: %s", mi, bytestostr(self.XGetAtomName(name_atom)))
XRRDeleteMonitor(self.display, window, name_atom)
#use a temporary name that won't conflict when we modify the monitors:
for mi in range(nmonitors):
if mi not in monitor_defs:
name_atom = monitors[mi].name
log("deleting monitor %i: %s", mi, bytestostr(self.XGetAtomName(name_atom)))
XRRDeleteMonitor(self.display, window, name_atom)
else:
#use a temporary name that won't conflict:
monitors[mi].name = self.xatom("DUMMY%i-%s" % (mi, monotonic()))
XRRSetMonitor(self.display, window, &monitors[mi])
monitors[mi].name = self.xatom("DUMMY%i-%s" % (mi, monotonic()))
XRRSetMonitor(self.display, window, &monitors[mi])
finally:
XRRFreeMonitors(monitors)
self.XSync()
Expand All @@ -1141,17 +1143,16 @@ cdef class RandRBindingsInstance(X11CoreBindingsInstance):
for mi in range(nmonitors):
names[mi] = bytestostr(self.XGetAtomName(monitors[mi].name))
log("found %i monitors still active: %s", nmonitors, csv(names.values()))
active_names = []
try:
for mi in range(nmonitors):
m = monitor_defs.get(mi, {})
if not m:
log("no monitor definition for index %i", mi)
continue
log("setting monitor %i: %s", mi, m)
name = (prettify_plug_name(m.get("name", "")) or ("DUMMY%i" % mi))
mi = 0
for i, m in monitor_defs.items():
log("matching monitor index %i to %i: %s", mi, i, m)
name = (prettify_plug_name(m.get("name", "")) or ("VFB-%i" % mi))
while name in names.values() and names.get(mi)!=name:
name += "-%i" % mi
names[mi] = name
active_names.append(name)
monitor.name = self.xatom(name)
monitor.primary = m.get("primary", primary==mi)
monitor.automatic = m.get("automatic", True)
Expand All @@ -1161,14 +1162,29 @@ cdef class RandRBindingsInstance(X11CoreBindingsInstance):
monitor.height = m.get("height", 128)
monitor.mwidth = m.get("mm-width", dpi96(monitor.width))
monitor.mheight = m.get("mm-height", dpi96(monitor.height))
assert rsc.noutput>mi, "only %i outputs, cannot set %i" % (rsc.noutput, mi)
output = rsc.outputs[mi]
assert rsc.noutput>i, "only %i outputs, cannot set %i" % (rsc.noutput, i)
output = rsc.outputs[i]
monitor.outputs = &output
monitor.noutput = 1
log("XRRSetMonitor(%#x, %#x, %#x)", <uintptr_t> self.display, <uintptr_t> window, <uintptr_t> &monitor)
XRRSetMonitor(self.display, window, &monitor)
log.info("monitor %i is %r %ix%i", mi, name, monitor.width, monitor.height)
self.XSync()
mi += 1
finally:
XRRFreeMonitors(monitors)
monitors = XRRGetMonitors(self.display, window, True, &nmonitors)
if not monitors:
log.error("Error: failed to retrieve the list of monitors")
return False
#delete inactive monitors:
try:
for mi in range(nmonitors):
name_atom = monitors[mi].name
name = bytestostr(self.XGetAtomName(name_atom))
if name not in active_names:
log("deleting monitor %i: %s", mi, name)
XRRDeleteMonitor(self.display, window, name_atom)
finally:
XRRFreeMonitors(monitors)
finally:
Expand Down

0 comments on commit 137a222

Please sign in to comment.