Skip to content

Commit cc39c2e

Browse files
committed
#4064 move shadow server configuration to its own gui
1 parent 7f49604 commit cc39c2e

File tree

8 files changed

+120
-109
lines changed

8 files changed

+120
-109
lines changed

fs/share/xpra/icons/shadow.png

6.54 KB
Loading

xpra/gtk/configure/gstreamer.py

Lines changed: 4 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,16 @@
33
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
44
# later version. See the file COPYING for details.
55

6-
import os
76
from time import sleep
87
from textwrap import wrap
98

10-
from xpra.os_util import gi_import, POSIX, OSX, WIN32
9+
from xpra.os_util import gi_import, OSX, WIN32
1110
from xpra.util.env import envint
1211
from xpra.util.str_fn import csv
1312
from xpra.util.thread import start_thread
14-
from xpra.gtk.configure.common import DISCLAIMER, run_gui, get_config_env, update_config_env
13+
from xpra.gtk.configure.common import DISCLAIMER, run_gui
1514
from xpra.gtk.dialogs.base_gui_window import BaseGUIWindow
16-
from xpra.gtk.widget import label, setfont
15+
from xpra.gtk.widget import label
1716
from xpra.platform.paths import get_image
1817
from xpra.log import Logger
1918

@@ -192,99 +191,7 @@ def configure_decoding(self, *_args) -> None:
192191
pass
193192

194193
def configure_shadow(self, *_args) -> None:
195-
self.clear_vbox()
196-
self.set_box_margin()
197-
self.layout = None
198-
messages = (
199-
"Configuring shadow mode",
200-
)
201-
202-
def has(*els):
203-
return all(el in self.elements for el in els)
204-
205-
backends = []
206-
if POSIX and not OSX:
207-
from xpra.platform.posix.shadow_server import SHADOW_OPTIONS
208-
backends = SHADOW_OPTIONS
209-
options = (
210-
(
211-
True, "auto", "Automatic runtime detection",
212-
"this is the default behaviour,",
213-
"this option should always find a suitable capture strategy",
214-
"and it may not choose to use a video stream",
215-
),
216-
(
217-
has("ximagesrc"), "X11", "X11 image capture",
218-
"GStreamer will capture the session's contents using 'ximagesrc'",
219-
"the pixel data will be compressed using a stream encoder",
220-
"eg: h264, hevc, av1, etc",
221-
"this option is only available for shadowing existing X11 sessions",
222-
),
223-
(
224-
has("pipewiresrc"), "pipewire", "pipewire capture",
225-
"GStreamer pipewire source from the RemoteDesktop interface",
226-
"the pixel data will be compressed using a stream encoder",
227-
"eg: h264, hevc, av1, etc",
228-
"your desktop sessions must support the 'RemoteDesktop' dbus interface",
229-
),
230-
)
231-
else:
232-
raise RuntimeError(f"unsupported platform {os.name}")
233-
for i, message in enumerate(messages):
234-
lbl = label(message, font="Sans 22")
235-
self.vbox.add(lbl)
236-
current_setting = get_config_env("XPRA_SHADOW_BACKEND")
237-
self.buttons = []
238-
for available, backend, description, *details in options:
239-
btn = Gtk.CheckButton(label=description)
240-
btn.set_sensitive(available)
241-
btn.set_active(backend == current_setting)
242-
btn._backend = backend
243-
setfont(btn, font="sans 14")
244-
self.vbox.add(btn)
245-
for detail in details:
246-
lbl = label(detail)
247-
lbl.set_halign(Gtk.Align.START)
248-
lbl.set_margin_start(32)
249-
self.vbox.add(lbl)
250-
self.buttons.append(btn)
251-
btn_box = Gtk.HBox(homogeneous=True, spacing=40)
252-
btn_box.set_vexpand(True)
253-
btn_box.set_valign(Gtk.Align.END)
254-
self.vbox.add(btn_box)
255-
cancel_btn = Gtk.Button.new_with_label("Cancel")
256-
cancel_btn.connect("clicked", self.dismiss)
257-
btn_box.add(cancel_btn)
258-
confirm_btn = Gtk.Button.new_with_label("Confirm")
259-
260-
def save_shadow(*_args):
261-
active = [button for button in self.buttons if button.get_active()]
262-
assert len(active) == 1
263-
setting = active[0]._backend.lower()
264-
if setting not in backends:
265-
raise RuntimeError(f"invalid backend selected: {setting}")
266-
log.info(f"saving XPRA_SHADOW_BACKEND={setting}")
267-
update_config_env("XPRA_SHADOW_BACKEND", setting)
268-
self.dismiss()
269-
270-
confirm_btn.connect("clicked", save_shadow)
271-
confirm_btn.set_sensitive(False)
272-
btn_box.add(confirm_btn)
273-
274-
# only enable the confirm button once an option has been chosen:
275-
def option_toggled(toggled_btn, *_args):
276-
if toggled_btn.get_active():
277-
for button in self.buttons:
278-
if button != toggled_btn:
279-
button.set_active(False)
280-
else:
281-
if not any(button.get_active() for button in self.buttons):
282-
self.buttons[0].set_active(True)
283-
confirm_btn.set_sensitive(any(button.get_active() for button in self.buttons))
284-
285-
for btn in self.buttons:
286-
btn.connect("toggled", option_toggled)
287-
self.vbox.show_all()
194+
pass
288195

289196

290197
def main(_args) -> int:

xpra/gtk/configure/home.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def populate(self):
3131
self.sub("Features", "features.png", "Enable or disable feature groups", "features")
3232
self.sub("Picture compression", "encoding.png", "Encodings, speed and quality", "encodings")
3333
self.sub("GStreamer", "gstreamer.png", "Configure the GStreamer codecs", "gstreamer")
34+
self.sub("Shadow Server", "shadow.png", "Configure the Shadow Server", "shadow")
3435
self.sub("OpenGL acceleration", "opengl.png", "Test and validate OpenGL renderer", "opengl")
3536

3637
def sub(self, title="", icon_name="browse.png", tooltip="", configure: str = "") -> None:

xpra/platform/darwin/shadow_server.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,21 @@
2222
log = Logger("shadow", "osx")
2323

2424
USE_TIMER = envbool("XPRA_OSX_SHADOW_USE_TIMER", False)
25+
GSTREAMER = envbool("XPRA_SHADOW_GSTREAMER", True)
26+
27+
28+
def check_gstreamer() -> bool:
29+
if not GSTREAMER:
30+
return False
31+
from xpra.gstreamer.common import has_plugins, import_gst
32+
import_gst()
33+
return has_plugins("avfvideosrc")
34+
35+
36+
SHADOW_OPTIONS = {
37+
"auto": lambda: True,
38+
"gstreamer": check_gstreamer,
39+
}
2540

2641
ALPHA: dict[int, str] = {
2742
CG.kCGImageAlphaNone: "AlphaNone",

xpra/platform/posix/fd_portal_shadow.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def __init__(self, multi_window=True):
6060
self.capture: Capture | None = None
6161
self.portal_interface = get_portal_interface()
6262
self.input_devices = 0
63-
log(f"setup_capture() self.portal_interface={self.portal_interface}")
63+
log(f"PortalShadow({multi_window}) portal_interface={self.portal_interface}")
6464

6565
def get_server_mode(self) -> str:
6666
return "portal shadow"

xpra/platform/posix/shadow_server.py

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@
66
import os
77

88
from xpra.log import Logger
9+
from xpra.util.env import envbool
910

10-
SHADOW_OPTIONS = ("auto", "x11", "pipewire", "screencast", "remotedesktop", "wayland")
11+
12+
XSHM: bool = envbool("XPRA_SHADOW_XSHM", True)
13+
NVFBC: bool = envbool("XPRA_SHADOW_NVFBC", True)
14+
GSTREAMER: bool = envbool("XPRA_SHADOW_GSTREAMER", True)
15+
PIPEWIRE: bool = envbool("XPRA_SHADOW_PIPEWIRE", True)
1116

1217

1318
def warn(*messages) -> None:
@@ -50,7 +55,7 @@ def load_wayland(display: str = "") -> type | None:
5055
return c
5156

5257

53-
def load_x11(display: str = "") -> type | None:
58+
def load_xshm(display: str = "") -> type | None:
5459
gdkb = os.environ.get("GDK_BACKEND")
5560
try:
5661
os.environ["GDK_BACKEND"] = "x11"
@@ -68,8 +73,8 @@ def load_auto(display: str = "") -> type | None:
6873
if display.startswith("wayland-") or os.path.isabs(display):
6974
c = load_wayland(display)
7075
elif display.startswith(":"):
71-
c = load_x11(display)
72-
return c or load_remotedesktop(display) or load_screencast(display) or load_x11(display)
76+
c = load_xshm(display)
77+
return c or load_remotedesktop(display) or load_screencast(display) or load_xshm(display)
7378

7479

7580
def ShadowServer(display: str = "", multi_window: bool = True):
@@ -83,3 +88,43 @@ def ShadowServer(display: str = "", multi_window: bool = True):
8388
log = Logger("server", "shadow")
8489
log(f"ShadowServer({display}, {multi_window}) {env_setting}={shadow_server}")
8590
return shadow_server(multi_window)
91+
92+
93+
def check_nvfbc() -> bool:
94+
return NVFBC
95+
96+
97+
def check_gstreamer() -> bool:
98+
if not GSTREAMER:
99+
return False
100+
from xpra.gstreamer.common import has_plugins, import_gst
101+
import_gst()
102+
return has_plugins("ximagesrc")
103+
104+
105+
def check_pipewire() -> bool:
106+
if not PIPEWIRE:
107+
return False
108+
from xpra.gstreamer.common import has_plugins, import_gst
109+
import_gst()
110+
return has_plugins("pipewiresrc")
111+
112+
113+
def check_xshm() -> bool:
114+
from xpra.x11.bindings.ximage import XImageBindings # pylint: disable=import-outside-toplevel
115+
assert XImageBindings
116+
return True
117+
118+
119+
def nocheck() -> bool:
120+
return True
121+
122+
123+
SHADOW_OPTIONS = {
124+
"auto": nocheck,
125+
"nvfbc": check_nvfbc,
126+
"gstreamer": check_gstreamer,
127+
"pipewire": check_pipewire,
128+
"xshm": check_xshm,
129+
"gtk": nocheck,
130+
}

xpra/platform/shadow_server.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
# This file is part of Xpra.
2-
# Copyright (C) 2011-2020 Antoine Martin <antoine@xpra.org>
2+
# Copyright (C) 2011-2024 Antoine Martin <antoine@xpra.org>
33
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
44
# later version. See the file COPYING for details.
55

6+
from collections.abc import Callable
7+
68
from xpra.platform import platform_import
79

10+
SHADOW_OPTIONS: dict[str, Callable[[], bool]] = {}
11+
812

913
def ShadowServer(*_args): # pragma: no cover
1014
raise NotImplementedError()
1115

1216

13-
platform_import(globals(), "shadow_server", True, "ShadowServer")
17+
platform_import(globals(), "shadow_server", True,
18+
"ShadowServer",
19+
"SHADOW_OPTIONS")

xpra/platform/win32/shadow_server.py

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,48 @@
4646
keylog = Logger("keyboard")
4747
screenlog = Logger("screen")
4848

49+
50+
SEAMLESS = envbool("XPRA_WIN32_SEAMLESS", False)
51+
NVFBC = envbool("XPRA_SHADOW_NVFBC", True)
52+
GDI = envbool("XPRA_SHADOW_GDI", True)
53+
GSTREAMER = envbool("XPRA_SHADOW_GSTREAMER", True)
54+
55+
56+
def check_gstreamer() -> bool:
57+
if not GSTREAMER:
58+
return False
59+
from xpra.gstreamer.common import has_plugins, import_gst
60+
import_gst()
61+
return has_plugins("d3d11screencapturesrc") or has_plugins("dx9screencapsrc") or has_plugins("gdiscreencapsrc")
62+
63+
64+
def check_nvfbc() -> bool:
65+
if not NVFBC:
66+
return False
67+
from xpra.codecs.nvidia.nvfbc.capture import get_capture_instance
68+
assert get_capture_instance
69+
return NVFBC
70+
71+
72+
def check_gdi() -> bool:
73+
return GDI
74+
75+
76+
def check_gtk() -> bool:
77+
from xpra.gtk import signals
78+
assert signals
79+
return True
80+
81+
82+
SHADOW_OPTIONS = {
83+
"auto": lambda: True,
84+
"nvfbc": check_nvfbc,
85+
"gstreamer": check_gstreamer,
86+
"gdi": check_gdi,
87+
"gtk": check_gtk,
88+
}
89+
90+
4991
NOEVENT = object()
5092
BUTTON_EVENTS = {
5193
# (button,up-or-down) : win-event-name
@@ -69,11 +111,6 @@
69111
(9, False): (win32con.MOUSEEVENTF_XUP, win32con.XBUTTON2),
70112
}
71113

72-
SEAMLESS = envbool("XPRA_WIN32_SEAMLESS", False)
73-
NVFBC = envbool("XPRA_SHADOW_NVFBC", True)
74-
GDI = envbool("XPRA_SHADOW_GDI", True)
75-
GSTREAMER = envbool("XPRA_SHADOW_GSTREAMER", True)
76-
77114

78115
def get_root_window_size() -> tuple[int, int]:
79116
w = GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN)

0 commit comments

Comments
 (0)