elsewindow 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- elsewindow/__init__.py +33 -0
- elsewindow/__main__.py +8 -0
- elsewindow/cli.py +204 -0
- elsewindow/config.py +152 -0
- elsewindow/live-cli.yml +94 -0
- elsewindow/live_config.py +429 -0
- elsewindow/profiles.yml +41 -0
- elsewindow/py.typed +1 -0
- elsewindow/session.py +788 -0
- elsewindow-0.1.0.dist-info/METADATA +176 -0
- elsewindow-0.1.0.dist-info/RECORD +15 -0
- elsewindow-0.1.0.dist-info/WHEEL +5 -0
- elsewindow-0.1.0.dist-info/entry_points.txt +2 -0
- elsewindow-0.1.0.dist-info/licenses/LICENSE +21 -0
- elsewindow-0.1.0.dist-info/top_level.txt +1 -0
elsewindow/__init__.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Copyright (c) 2026 kogeler
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""Xpra application runner over one owned SSH master."""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _resolve_version() -> str:
|
|
10
|
+
"""Resolve the source-tree or installed distribution version."""
|
|
11
|
+
try:
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
root = Path(__file__).resolve().parent.parent
|
|
15
|
+
if (root / "pyproject.toml").is_file():
|
|
16
|
+
return (root / ".version").read_text(encoding="utf-8").strip()
|
|
17
|
+
bundled = Path(__file__).resolve().parent / ".version"
|
|
18
|
+
if bundled.is_file():
|
|
19
|
+
return bundled.read_text(encoding="utf-8").strip()
|
|
20
|
+
except OSError:
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
25
|
+
|
|
26
|
+
return version("elsewindow")
|
|
27
|
+
except PackageNotFoundError:
|
|
28
|
+
return "0.0.0+unknown"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
__version__ = _resolve_version()
|
|
32
|
+
|
|
33
|
+
__all__ = ["__version__"]
|
elsewindow/__main__.py
ADDED
elsewindow/cli.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Copyright (c) 2026 kogeler
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""Command-line interface for one owned remote Xpra application."""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
import asyncio
|
|
10
|
+
import hashlib
|
|
11
|
+
import shutil
|
|
12
|
+
import signal
|
|
13
|
+
import sys
|
|
14
|
+
from collections.abc import Sequence
|
|
15
|
+
from contextlib import suppress
|
|
16
|
+
from importlib.metadata import PackageNotFoundError
|
|
17
|
+
from importlib.metadata import version as distribution_version
|
|
18
|
+
|
|
19
|
+
from ssh_wrapper.errors import SSHError
|
|
20
|
+
|
|
21
|
+
from . import __version__
|
|
22
|
+
from .config import (
|
|
23
|
+
DEFAULT_CONNECT_TIMEOUT,
|
|
24
|
+
DEFAULT_ENCODING_PROFILE,
|
|
25
|
+
DEFAULT_GRACE_TIMEOUT,
|
|
26
|
+
DEFAULT_HEARTBEAT_INTERVAL,
|
|
27
|
+
DEFAULT_LEASE_TIMEOUT,
|
|
28
|
+
DEFAULT_NETWORK_PROFILE,
|
|
29
|
+
DEFAULT_POLL_INTERVAL,
|
|
30
|
+
DEFAULT_PROBE_TIMEOUT,
|
|
31
|
+
DEFAULT_READY_TIMEOUT,
|
|
32
|
+
SUPPORTED_ENCODING_PROFILES,
|
|
33
|
+
SUPPORTED_NETWORK_PROFILES,
|
|
34
|
+
XpraConfig,
|
|
35
|
+
)
|
|
36
|
+
from .session import XpraSession
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
40
|
+
"""Build the public command-line contract."""
|
|
41
|
+
parser = argparse.ArgumentParser(
|
|
42
|
+
prog="elsewindow",
|
|
43
|
+
description="Run one new remote GUI application through Xpra over owned SSH.",
|
|
44
|
+
)
|
|
45
|
+
parser.add_argument(
|
|
46
|
+
"--version", action="version", version=f"%(prog)s {__version__}"
|
|
47
|
+
)
|
|
48
|
+
parser.add_argument(
|
|
49
|
+
"--diagnose",
|
|
50
|
+
action="store_true",
|
|
51
|
+
help="report bundled versions, profile digests, and local prerequisites",
|
|
52
|
+
)
|
|
53
|
+
authority = parser.add_mutually_exclusive_group()
|
|
54
|
+
authority.add_argument("--ssh-alias", help="trusted OpenSSH host alias")
|
|
55
|
+
authority.add_argument("--host", help="direct SSH host or address")
|
|
56
|
+
parser.add_argument("--user", help="remote user required with --host")
|
|
57
|
+
parser.add_argument("--port", type=int, default=22, help="direct SSH port")
|
|
58
|
+
parser.add_argument(
|
|
59
|
+
"--encoding-profile",
|
|
60
|
+
choices=SUPPORTED_ENCODING_PROFILES,
|
|
61
|
+
default=DEFAULT_ENCODING_PROFILE,
|
|
62
|
+
help=(
|
|
63
|
+
"reviewed pixel transport profile; h264 uses native VA-API, "
|
|
64
|
+
"libyuv, OpenGL, and alpha-capable fallback"
|
|
65
|
+
),
|
|
66
|
+
)
|
|
67
|
+
parser.add_argument(
|
|
68
|
+
"--network-profile",
|
|
69
|
+
choices=SUPPORTED_NETWORK_PROFILES,
|
|
70
|
+
default=DEFAULT_NETWORK_PROFILE,
|
|
71
|
+
help="reviewed client quality/network profile (default: %(default)s)",
|
|
72
|
+
)
|
|
73
|
+
parser.add_argument(
|
|
74
|
+
"--connect-timeout", type=float, default=DEFAULT_CONNECT_TIMEOUT
|
|
75
|
+
)
|
|
76
|
+
parser.add_argument("--ready-timeout", type=float, default=DEFAULT_READY_TIMEOUT)
|
|
77
|
+
parser.add_argument("--probe-timeout", type=float, default=DEFAULT_PROBE_TIMEOUT)
|
|
78
|
+
parser.add_argument("--poll-interval", type=float, default=DEFAULT_POLL_INTERVAL)
|
|
79
|
+
parser.add_argument(
|
|
80
|
+
"--heartbeat-interval", type=float, default=DEFAULT_HEARTBEAT_INTERVAL
|
|
81
|
+
)
|
|
82
|
+
parser.add_argument("--lease-timeout", type=float, default=DEFAULT_LEASE_TIMEOUT)
|
|
83
|
+
parser.add_argument("--cleanup-grace", type=float, default=DEFAULT_GRACE_TIMEOUT)
|
|
84
|
+
parser.add_argument(
|
|
85
|
+
"application",
|
|
86
|
+
nargs=argparse.REMAINDER,
|
|
87
|
+
metavar="APP [ARG ...]",
|
|
88
|
+
help="application argv after --",
|
|
89
|
+
)
|
|
90
|
+
return parser
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _diagnose() -> int:
|
|
94
|
+
"""Report immutable bundled inputs and actionable host prerequisites."""
|
|
95
|
+
from .live_config import LIVE_CLI_PATH, NETWORK_PROFILES_PATH
|
|
96
|
+
|
|
97
|
+
failed = False
|
|
98
|
+
print(f"elsewindow: {__version__}")
|
|
99
|
+
try:
|
|
100
|
+
wrapper_version = distribution_version("ssh-wrapper")
|
|
101
|
+
except PackageNotFoundError:
|
|
102
|
+
wrapper_version = "missing"
|
|
103
|
+
failed = True
|
|
104
|
+
print(f"ssh-wrapper: {wrapper_version}")
|
|
105
|
+
for label, path in (
|
|
106
|
+
("live-cli.yml", LIVE_CLI_PATH),
|
|
107
|
+
("profiles.yml", NETWORK_PROFILES_PATH),
|
|
108
|
+
):
|
|
109
|
+
try:
|
|
110
|
+
digest = hashlib.sha256(path.read_bytes()).hexdigest()
|
|
111
|
+
except OSError as error:
|
|
112
|
+
print(f"elsewindow: invalid_resource: {label}: {error}", file=sys.stderr)
|
|
113
|
+
failed = True
|
|
114
|
+
else:
|
|
115
|
+
print(f"{label}: sha256:{digest}")
|
|
116
|
+
if not sys.platform.startswith("linux"):
|
|
117
|
+
print(
|
|
118
|
+
f"elsewindow: unsupported_platform: Linux is required, found {sys.platform}",
|
|
119
|
+
file=sys.stderr,
|
|
120
|
+
)
|
|
121
|
+
failed = True
|
|
122
|
+
for command in ("ssh", "false", "xpra"):
|
|
123
|
+
resolved = shutil.which(command)
|
|
124
|
+
if resolved is None:
|
|
125
|
+
print(
|
|
126
|
+
"elsewindow: missing_dependency: "
|
|
127
|
+
f"required command not found on PATH: {command}",
|
|
128
|
+
file=sys.stderr,
|
|
129
|
+
)
|
|
130
|
+
failed = True
|
|
131
|
+
else:
|
|
132
|
+
print(f"{command}: {resolved}")
|
|
133
|
+
return int(failed)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
async def _run_with_signals(config: XpraConfig) -> int:
|
|
137
|
+
session = XpraSession(config)
|
|
138
|
+
loop = asyncio.get_running_loop()
|
|
139
|
+
interrupted: asyncio.Future[int] = loop.create_future()
|
|
140
|
+
|
|
141
|
+
def stop(exit_code: int) -> None:
|
|
142
|
+
if not interrupted.done():
|
|
143
|
+
interrupted.set_result(exit_code)
|
|
144
|
+
|
|
145
|
+
installed: list[signal.Signals] = []
|
|
146
|
+
for selected in (signal.SIGINT, signal.SIGTERM):
|
|
147
|
+
try:
|
|
148
|
+
loop.add_signal_handler(selected, stop, 128 + selected)
|
|
149
|
+
except NotImplementedError:
|
|
150
|
+
continue
|
|
151
|
+
installed.append(selected)
|
|
152
|
+
|
|
153
|
+
run_task = asyncio.create_task(session.run())
|
|
154
|
+
try:
|
|
155
|
+
done, _pending = await asyncio.wait(
|
|
156
|
+
{run_task, interrupted}, return_when=asyncio.FIRST_COMPLETED
|
|
157
|
+
)
|
|
158
|
+
if interrupted in done:
|
|
159
|
+
run_task.cancel()
|
|
160
|
+
with suppress(asyncio.CancelledError):
|
|
161
|
+
await run_task
|
|
162
|
+
return interrupted.result()
|
|
163
|
+
return run_task.result()
|
|
164
|
+
finally:
|
|
165
|
+
if not interrupted.done():
|
|
166
|
+
interrupted.cancel()
|
|
167
|
+
for selected in installed:
|
|
168
|
+
loop.remove_signal_handler(selected)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
172
|
+
"""Validate configuration, run one session, and return a stable exit code."""
|
|
173
|
+
parser = build_parser()
|
|
174
|
+
args = parser.parse_args(argv)
|
|
175
|
+
if args.diagnose:
|
|
176
|
+
if (
|
|
177
|
+
args.ssh_alias is not None
|
|
178
|
+
or args.host is not None
|
|
179
|
+
or args.user is not None
|
|
180
|
+
or args.port != 22
|
|
181
|
+
or args.application
|
|
182
|
+
):
|
|
183
|
+
parser.error("--diagnose cannot be combined with a session authority")
|
|
184
|
+
return _diagnose()
|
|
185
|
+
if args.ssh_alias is None and args.host is None:
|
|
186
|
+
parser.error("provide either --ssh-alias or --host")
|
|
187
|
+
try:
|
|
188
|
+
config = XpraConfig.from_namespace(args)
|
|
189
|
+
except SSHError as error:
|
|
190
|
+
parser.error(error.message)
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
return asyncio.run(_run_with_signals(config))
|
|
194
|
+
except KeyboardInterrupt:
|
|
195
|
+
return 130
|
|
196
|
+
except SSHError as error:
|
|
197
|
+
print(f"elsewindow: {error.code}: {error.message}", file=sys.stderr)
|
|
198
|
+
return 1
|
|
199
|
+
except Exception as error: # noqa: BLE001 - public diagnostics stay sanitized.
|
|
200
|
+
print(
|
|
201
|
+
f"elsewindow: session_failed: {type(error).__name__}",
|
|
202
|
+
file=sys.stderr,
|
|
203
|
+
)
|
|
204
|
+
return 1
|
elsewindow/config.py
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Copyright (c) 2026 kogeler
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""Validated immutable configuration for one remote Xpra session."""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
import math
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from ssh_wrapper.connection import ConnectionSpec, resolve_program
|
|
14
|
+
from ssh_wrapper.errors import SSHError
|
|
15
|
+
|
|
16
|
+
from . import live_config
|
|
17
|
+
|
|
18
|
+
DEFAULT_CONNECT_TIMEOUT = 120.0
|
|
19
|
+
DEFAULT_READY_TIMEOUT = 45.0
|
|
20
|
+
DEFAULT_PROBE_TIMEOUT = 8.0
|
|
21
|
+
DEFAULT_POLL_INTERVAL = 1.0
|
|
22
|
+
DEFAULT_HEARTBEAT_INTERVAL = 10.0
|
|
23
|
+
DEFAULT_LEASE_TIMEOUT = 45.0
|
|
24
|
+
DEFAULT_GRACE_TIMEOUT = 5.0
|
|
25
|
+
MAX_TIMEOUT = 900.0
|
|
26
|
+
MAX_APPLICATION_ARGUMENTS = 256
|
|
27
|
+
MAX_APPLICATION_BYTES = 16 * 1024
|
|
28
|
+
DEFAULT_ENCODING_PROFILE = live_config.DEFAULT_ENCODING_PROFILE
|
|
29
|
+
DEFAULT_NETWORK_PROFILE = live_config.load_network_profiles()[0]
|
|
30
|
+
SUPPORTED_ENCODING_PROFILES = live_config.encoding_profile_names()
|
|
31
|
+
SUPPORTED_NETWORK_PROFILES = live_config.network_profile_names()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _bounded_float(name: str, value: float, minimum: float = 0.1) -> float:
|
|
35
|
+
if not math.isfinite(value) or not minimum <= value <= MAX_TIMEOUT:
|
|
36
|
+
raise SSHError(
|
|
37
|
+
"invalid_configuration",
|
|
38
|
+
f"{name} must be between {minimum:g} and {MAX_TIMEOUT:g} seconds",
|
|
39
|
+
)
|
|
40
|
+
return value
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass(frozen=True, slots=True)
|
|
44
|
+
class XpraConfig:
|
|
45
|
+
"""All startup policy for one independently owned GUI session."""
|
|
46
|
+
|
|
47
|
+
connection: ConnectionSpec
|
|
48
|
+
application: tuple[str, ...]
|
|
49
|
+
encoding_profile: str
|
|
50
|
+
network_profile: str
|
|
51
|
+
connect_timeout: float
|
|
52
|
+
ready_timeout: float
|
|
53
|
+
probe_timeout: float
|
|
54
|
+
poll_interval: float
|
|
55
|
+
heartbeat_interval: float
|
|
56
|
+
lease_timeout: float
|
|
57
|
+
grace_timeout: float
|
|
58
|
+
ssh_path: Path
|
|
59
|
+
false_path: Path
|
|
60
|
+
xpra_path: Path
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
def from_namespace(cls, args: argparse.Namespace) -> XpraConfig:
|
|
64
|
+
"""Validate parsed arguments and resolve required local executables."""
|
|
65
|
+
if args.ssh_alias is not None:
|
|
66
|
+
if args.host is not None or args.user is not None or args.port != 22:
|
|
67
|
+
raise SSHError(
|
|
68
|
+
"invalid_connection",
|
|
69
|
+
"ssh alias cannot be combined with host, user, or a direct port",
|
|
70
|
+
)
|
|
71
|
+
connection = ConnectionSpec.from_alias(args.ssh_alias)
|
|
72
|
+
else:
|
|
73
|
+
if args.host is None or args.user is None:
|
|
74
|
+
raise SSHError(
|
|
75
|
+
"invalid_connection",
|
|
76
|
+
"provide either ssh alias or both host and user",
|
|
77
|
+
)
|
|
78
|
+
connection = ConnectionSpec.from_direct(args.host, args.user, args.port)
|
|
79
|
+
|
|
80
|
+
application = tuple(args.application)
|
|
81
|
+
if application and application[0] == "--":
|
|
82
|
+
application = application[1:]
|
|
83
|
+
if not application or not application[0]:
|
|
84
|
+
raise SSHError(
|
|
85
|
+
"invalid_application", "provide an application argv after --"
|
|
86
|
+
)
|
|
87
|
+
if len(application) > MAX_APPLICATION_ARGUMENTS:
|
|
88
|
+
raise SSHError(
|
|
89
|
+
"invalid_application",
|
|
90
|
+
f"application argv is limited to {MAX_APPLICATION_ARGUMENTS} items",
|
|
91
|
+
)
|
|
92
|
+
if any("\x00" in item for item in application):
|
|
93
|
+
raise SSHError("invalid_application", "application argv contains a NUL")
|
|
94
|
+
if (
|
|
95
|
+
sum(len(item.encode("utf-8")) for item in application)
|
|
96
|
+
> MAX_APPLICATION_BYTES
|
|
97
|
+
):
|
|
98
|
+
raise SSHError(
|
|
99
|
+
"invalid_application",
|
|
100
|
+
f"application argv is limited to {MAX_APPLICATION_BYTES} UTF-8 bytes",
|
|
101
|
+
)
|
|
102
|
+
if args.encoding_profile not in SUPPORTED_ENCODING_PROFILES:
|
|
103
|
+
raise SSHError(
|
|
104
|
+
"invalid_configuration",
|
|
105
|
+
"encoding profile must be one of: "
|
|
106
|
+
f"{', '.join(SUPPORTED_ENCODING_PROFILES)}",
|
|
107
|
+
)
|
|
108
|
+
if args.network_profile not in SUPPORTED_NETWORK_PROFILES:
|
|
109
|
+
raise SSHError(
|
|
110
|
+
"invalid_configuration",
|
|
111
|
+
"network profile must be one of: "
|
|
112
|
+
f"{', '.join(SUPPORTED_NETWORK_PROFILES)}",
|
|
113
|
+
)
|
|
114
|
+
heartbeat_interval = _bounded_float(
|
|
115
|
+
"heartbeat interval", args.heartbeat_interval
|
|
116
|
+
)
|
|
117
|
+
lease_timeout = _bounded_float("lease timeout", args.lease_timeout)
|
|
118
|
+
if lease_timeout <= heartbeat_interval * 2:
|
|
119
|
+
raise SSHError(
|
|
120
|
+
"invalid_configuration",
|
|
121
|
+
"lease timeout must be greater than twice the heartbeat interval",
|
|
122
|
+
)
|
|
123
|
+
return cls(
|
|
124
|
+
connection=connection,
|
|
125
|
+
application=application,
|
|
126
|
+
encoding_profile=args.encoding_profile,
|
|
127
|
+
network_profile=args.network_profile,
|
|
128
|
+
connect_timeout=_bounded_float("connect timeout", args.connect_timeout),
|
|
129
|
+
ready_timeout=_bounded_float("ready timeout", args.ready_timeout),
|
|
130
|
+
probe_timeout=_bounded_float("probe timeout", args.probe_timeout),
|
|
131
|
+
poll_interval=_bounded_float("poll interval", args.poll_interval),
|
|
132
|
+
heartbeat_interval=heartbeat_interval,
|
|
133
|
+
lease_timeout=lease_timeout,
|
|
134
|
+
grace_timeout=_bounded_float("cleanup grace", args.cleanup_grace),
|
|
135
|
+
ssh_path=resolve_program("ssh"),
|
|
136
|
+
false_path=resolve_program("false"),
|
|
137
|
+
xpra_path=resolve_program("xpra"),
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def authority_uri(self) -> str:
|
|
142
|
+
"""Return the SSH URI prefix consumed by the local Xpra client."""
|
|
143
|
+
connection = self.connection
|
|
144
|
+
if connection.ssh_alias is not None:
|
|
145
|
+
authority = connection.ssh_alias
|
|
146
|
+
else:
|
|
147
|
+
assert connection.host is not None
|
|
148
|
+
assert connection.user is not None
|
|
149
|
+
assert connection.port is not None
|
|
150
|
+
host = f"[{connection.host}]" if ":" in connection.host else connection.host
|
|
151
|
+
authority = f"{connection.user}@{host}:{connection.port}"
|
|
152
|
+
return f"ssh://{authority}"
|
elsewindow/live-cli.yml
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Static Xpra CLI blocks used by the live server and client command builders.
|
|
2
|
+
# Values are grouped role -> concern/command/transport -> encoding -> policy;
|
|
3
|
+
# per-run network/quality values live in profiles.yml rather than here.
|
|
4
|
+
# Loader and assembly: infra/live/live_config.py and infra/live/run.py.
|
|
5
|
+
# Generic schema tests: infra/live/test_job.py. Contract and usage:
|
|
6
|
+
# CONTRACT.md and docs/runbooks/live-tests.md.
|
|
7
|
+
schema: 1
|
|
8
|
+
server:
|
|
9
|
+
base:
|
|
10
|
+
- "--minimal"
|
|
11
|
+
- "--backend=wayland"
|
|
12
|
+
- "--daemon=no"
|
|
13
|
+
- "--displayfd=1"
|
|
14
|
+
- "--socket-dir=/tmp/server-runtime/xpra-sockets"
|
|
15
|
+
- "--socket-dirs=/tmp/server-runtime/xpra-sockets"
|
|
16
|
+
- "--sessions-dir=/tmp/server-runtime/xpra-sessions"
|
|
17
|
+
lifecycle:
|
|
18
|
+
- "--use-display=no"
|
|
19
|
+
- "--exit-with-client=no"
|
|
20
|
+
- "--exit-with-children=yes"
|
|
21
|
+
- "--terminate-children=yes"
|
|
22
|
+
- "--video-scaling=0"
|
|
23
|
+
- "--html=off"
|
|
24
|
+
diagnostics:
|
|
25
|
+
- "-d"
|
|
26
|
+
- "wayland,damage,encoding,encoder,argb"
|
|
27
|
+
commands:
|
|
28
|
+
version:
|
|
29
|
+
- "--version"
|
|
30
|
+
info:
|
|
31
|
+
- "wayland-0"
|
|
32
|
+
- "--socket-dir=/tmp/server-runtime/xpra-sockets"
|
|
33
|
+
transports:
|
|
34
|
+
rgb:
|
|
35
|
+
common:
|
|
36
|
+
- "--video-encoders=none"
|
|
37
|
+
- "--csc-modules=none"
|
|
38
|
+
policies:
|
|
39
|
+
strict:
|
|
40
|
+
- "--encodings=rgb"
|
|
41
|
+
h264:
|
|
42
|
+
common:
|
|
43
|
+
- "--video=yes"
|
|
44
|
+
- "--video-encoders=libva"
|
|
45
|
+
- "--csc-modules=libyuv"
|
|
46
|
+
policies:
|
|
47
|
+
strict:
|
|
48
|
+
- "--encodings=h264"
|
|
49
|
+
adaptive-alpha:
|
|
50
|
+
- "--encodings=h264,webp,rgb"
|
|
51
|
+
fallback-auto:
|
|
52
|
+
- "--encodings=h264,rgb"
|
|
53
|
+
fallback-h264:
|
|
54
|
+
- "--encodings=h264,rgb"
|
|
55
|
+
client:
|
|
56
|
+
base:
|
|
57
|
+
- "--minimal"
|
|
58
|
+
- "--compressors=none"
|
|
59
|
+
- "--reconnect=no"
|
|
60
|
+
- "--bandwidth-detection=no"
|
|
61
|
+
diagnostics:
|
|
62
|
+
- "-d"
|
|
63
|
+
- "draw,paint,cairo,window,gtk,alpha,libva"
|
|
64
|
+
commands:
|
|
65
|
+
version:
|
|
66
|
+
- "--version"
|
|
67
|
+
detach:
|
|
68
|
+
- "--compressors=none"
|
|
69
|
+
transports:
|
|
70
|
+
rgb:
|
|
71
|
+
common:
|
|
72
|
+
- "--opengl=no"
|
|
73
|
+
- "--video-decoders=none"
|
|
74
|
+
- "--csc-modules=none"
|
|
75
|
+
policies:
|
|
76
|
+
strict:
|
|
77
|
+
- "--encodings=rgb"
|
|
78
|
+
h264:
|
|
79
|
+
common:
|
|
80
|
+
- "--video=yes"
|
|
81
|
+
- "--opengl=force:native"
|
|
82
|
+
- "--video-decoders=libva"
|
|
83
|
+
- "--csc-modules=none"
|
|
84
|
+
policies:
|
|
85
|
+
strict:
|
|
86
|
+
- "--encodings=h264"
|
|
87
|
+
adaptive-alpha:
|
|
88
|
+
- "--encodings=h264,webp,rgb"
|
|
89
|
+
- "--encoding=h264"
|
|
90
|
+
fallback-auto:
|
|
91
|
+
- "--encodings=h264,rgb"
|
|
92
|
+
fallback-h264:
|
|
93
|
+
- "--encodings=h264,rgb"
|
|
94
|
+
- "--encoding=h264"
|