remotedesktop 2.1.0__py3-none-any.whl → 2.2.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.
- remotedesktop/__init__.py +1 -1
- remotedesktop/app.py +131 -2
- remotedesktop/client.py +3 -0
- remotedesktop/discovery.py +16 -2
- remotedesktop/server.py +7 -1
- {remotedesktop-2.1.0.dist-info → remotedesktop-2.2.0.dist-info}/METADATA +14 -8
- {remotedesktop-2.1.0.dist-info → remotedesktop-2.2.0.dist-info}/RECORD +10 -10
- {remotedesktop-2.1.0.dist-info → remotedesktop-2.2.0.dist-info}/WHEEL +1 -1
- {remotedesktop-2.1.0.dist-info → remotedesktop-2.2.0.dist-info}/entry_points.txt +0 -0
- {remotedesktop-2.1.0.dist-info → remotedesktop-2.2.0.dist-info}/licenses/LICENSE +0 -0
remotedesktop/__init__.py
CHANGED
remotedesktop/app.py
CHANGED
|
@@ -15,7 +15,9 @@ import logging
|
|
|
15
15
|
import re
|
|
16
16
|
import sqlite3
|
|
17
17
|
import sys
|
|
18
|
+
import threading
|
|
18
19
|
import time
|
|
20
|
+
from collections.abc import Callable
|
|
19
21
|
from pathlib import Path
|
|
20
22
|
|
|
21
23
|
from PySide6.QtCore import QObject, QProcess, QStandardPaths, Qt, QTimer, Signal
|
|
@@ -45,10 +47,15 @@ from PySide6.QtWidgets import (
|
|
|
45
47
|
from remotedesktop import __version__, compat, db, icon, logs, window_state
|
|
46
48
|
from remotedesktop.about import AboutDialog
|
|
47
49
|
from remotedesktop.autostart import Autostart, installed_launcher
|
|
48
|
-
from remotedesktop.client import DiscoveryPanel, ServerSession
|
|
50
|
+
from remotedesktop.client import DiscoveryPanel, ServerSession, _broadcast_hosts
|
|
49
51
|
from remotedesktop.clipboard import ClipboardSync
|
|
50
52
|
from remotedesktop.config import KnownServers, Settings, default_db_path, load_client_identity
|
|
51
|
-
from remotedesktop.discovery import
|
|
53
|
+
from remotedesktop.discovery import (
|
|
54
|
+
DEFAULT_CONNECT_PORT,
|
|
55
|
+
DISCOVERY_PORT,
|
|
56
|
+
ServerInfo,
|
|
57
|
+
discover_servers,
|
|
58
|
+
)
|
|
52
59
|
from remotedesktop.inventory import ConnectionInventory, InventoryTab
|
|
53
60
|
from remotedesktop.logs import PeerLogDialog, read_log_tail
|
|
54
61
|
from remotedesktop.modal_loop import HTCLOSE, HTMAXBUTTON, HTMINBUTTON, ModalLoopPump
|
|
@@ -73,6 +80,14 @@ _log = logging.getLogger("remotedesktop.app")
|
|
|
73
80
|
# Auto-reconnect backoff never waits longer than this between attempts.
|
|
74
81
|
_RECONNECT_CAP_SECONDS = 30.0
|
|
75
82
|
|
|
83
|
+
# Once this many direct reconnect attempts at the last known address have
|
|
84
|
+
# failed, each further failure also starts a discovery scan: the server may
|
|
85
|
+
# have come back on a different address (DHCP after a reboot), and sessions,
|
|
86
|
+
# tokens, and pins are all keyed by host:port. A re-discovered server is
|
|
87
|
+
# matched by certificate fingerprint (name as the fallback) and the session
|
|
88
|
+
# migrates to the new address — token included, so no re-approval.
|
|
89
|
+
_REDISCOVER_AFTER_ATTEMPTS = 2
|
|
90
|
+
|
|
76
91
|
|
|
77
92
|
class _SessionsTable(ViewersTable):
|
|
78
93
|
"""The client-side twin of the viewers table: one row per connected
|
|
@@ -121,6 +136,9 @@ class _ConnectedServersSource(QObject):
|
|
|
121
136
|
|
|
122
137
|
|
|
123
138
|
class MainWindow(QMainWindow):
|
|
139
|
+
# A re-discovery scan finished on its worker thread: (session, servers).
|
|
140
|
+
_rediscoveryFinished = Signal(object, list)
|
|
141
|
+
|
|
124
142
|
def __init__(
|
|
125
143
|
self,
|
|
126
144
|
*,
|
|
@@ -132,10 +150,19 @@ class MainWindow(QMainWindow):
|
|
|
132
150
|
connect_port: int = DEFAULT_CONNECT_PORT,
|
|
133
151
|
tray_available: bool | None = None,
|
|
134
152
|
reconnect_base_seconds: float = 2.0,
|
|
153
|
+
rediscover: Callable[[], list[ServerInfo]] | None = None,
|
|
135
154
|
effects_reducer: VisualEffectsReducer | None = None,
|
|
136
155
|
) -> None:
|
|
137
156
|
super().__init__()
|
|
138
157
|
self._reconnect_base = reconnect_base_seconds
|
|
158
|
+
# The blocking scan auto-reconnect uses to find a moved server; tests
|
|
159
|
+
# inject a fake so window tests never broadcast on the LAN.
|
|
160
|
+
self._rediscover = (
|
|
161
|
+
rediscover
|
|
162
|
+
if rediscover is not None
|
|
163
|
+
else lambda: discover_servers(broadcast_hosts=_broadcast_hosts())
|
|
164
|
+
)
|
|
165
|
+
self._rediscoveryFinished.connect(self._on_rediscovery_finished)
|
|
139
166
|
self._auto_scan = auto_scan
|
|
140
167
|
self.setWindowIcon(icon.app_icon("app"))
|
|
141
168
|
# Tests inject a connection to a temp database; the app uses the default.
|
|
@@ -1370,6 +1397,10 @@ class MainWindow(QMainWindow):
|
|
|
1370
1397
|
session.viewer.clear(message)
|
|
1371
1398
|
self._set_session_status(session, f"{session.name}: {message}")
|
|
1372
1399
|
self.log(f"[{session.name}] {message}")
|
|
1400
|
+
# The direct address keeps failing — the server may be back under a
|
|
1401
|
+
# different one. Scan alongside the backoff timer, not instead of it.
|
|
1402
|
+
if session.reconnect_attempts >= _REDISCOVER_AFTER_ATTEMPTS:
|
|
1403
|
+
self._start_rediscovery(session)
|
|
1373
1404
|
|
|
1374
1405
|
def _attempt_reconnect(self, session: ServerSession) -> None:
|
|
1375
1406
|
if (
|
|
@@ -1392,6 +1423,104 @@ class MainWindow(QMainWindow):
|
|
|
1392
1423
|
session.reconnect_timer.stop()
|
|
1393
1424
|
session.reconnect_attempts = 0
|
|
1394
1425
|
|
|
1426
|
+
# -------------------------------------------- reconnect re-discovery
|
|
1427
|
+
|
|
1428
|
+
def _start_rediscovery(self, session: ServerSession) -> None:
|
|
1429
|
+
"""Scan the LAN for a server that moved to a new address.
|
|
1430
|
+
|
|
1431
|
+
Runs the blocking scan on a worker thread (the DiscoveryPanel
|
|
1432
|
+
pattern) and delivers the result through a queued signal. This is
|
|
1433
|
+
not background polling: it only runs while a session is actively
|
|
1434
|
+
failing to reconnect, and stops the moment it succeeds.
|
|
1435
|
+
"""
|
|
1436
|
+
if session.rediscovering:
|
|
1437
|
+
return
|
|
1438
|
+
session.rediscovering = True
|
|
1439
|
+
_log.debug("Re-discovery scan for %s (%s)", session.name, session.key)
|
|
1440
|
+
scan = self._rediscover
|
|
1441
|
+
|
|
1442
|
+
def run() -> None:
|
|
1443
|
+
try:
|
|
1444
|
+
servers = scan()
|
|
1445
|
+
except OSError as error:
|
|
1446
|
+
_log.warning("Re-discovery scan failed: %s", error)
|
|
1447
|
+
servers = []
|
|
1448
|
+
try:
|
|
1449
|
+
self._rediscoveryFinished.emit(session, servers)
|
|
1450
|
+
except RuntimeError:
|
|
1451
|
+
pass # the window was torn down while the scan ran
|
|
1452
|
+
|
|
1453
|
+
threading.Thread(target=run, name="reconnect-rediscovery", daemon=True).start()
|
|
1454
|
+
|
|
1455
|
+
def _on_rediscovery_finished(self, session: ServerSession, servers: list) -> None:
|
|
1456
|
+
session.rediscovering = False
|
|
1457
|
+
if (
|
|
1458
|
+
session not in self._sessions
|
|
1459
|
+
or session.connected
|
|
1460
|
+
or session.denied
|
|
1461
|
+
or not session.auto_reconnect
|
|
1462
|
+
or self._quitting
|
|
1463
|
+
):
|
|
1464
|
+
return
|
|
1465
|
+
match = self._match_rediscovered(session, servers)
|
|
1466
|
+
if match is None:
|
|
1467
|
+
_log.debug("Re-discovery: %s not found among %d server(s)", session.name, len(servers))
|
|
1468
|
+
return
|
|
1469
|
+
if f"{match.host}:{match.port}" == session.key:
|
|
1470
|
+
return # still advertised at the failing address; keep direct retries
|
|
1471
|
+
self._migrate_session(session, match)
|
|
1472
|
+
|
|
1473
|
+
def _match_rediscovered(self, session: ServerSession, servers: list) -> ServerInfo | None:
|
|
1474
|
+
"""The discovered server that is `session`'s peer, if any.
|
|
1475
|
+
|
|
1476
|
+
The pinned certificate fingerprint is the identity; the display name
|
|
1477
|
+
is the fallback for servers too old to advertise one (soft matching,
|
|
1478
|
+
like the soft cert pin — robust connections over strict identity).
|
|
1479
|
+
"""
|
|
1480
|
+
record = self._known_servers.get(session.key)
|
|
1481
|
+
pinned = (record or {}).get("fingerprint") or ""
|
|
1482
|
+
candidates = [s for s in servers if not self._is_own_server(s)]
|
|
1483
|
+
if pinned:
|
|
1484
|
+
for server in candidates:
|
|
1485
|
+
if server.fingerprint == pinned:
|
|
1486
|
+
return server
|
|
1487
|
+
for server in candidates:
|
|
1488
|
+
if server.name == session.name:
|
|
1489
|
+
return server
|
|
1490
|
+
return None
|
|
1491
|
+
|
|
1492
|
+
def _migrate_session(self, session: ServerSession, server: ServerInfo) -> None:
|
|
1493
|
+
"""Move a session (and its stored pairing) to the server's new
|
|
1494
|
+
address, then reconnect immediately."""
|
|
1495
|
+
old_key, new_key = session.key, f"{server.host}:{server.port}"
|
|
1496
|
+
if self._session_for_key(new_key) is not None:
|
|
1497
|
+
return # the user already opened a session at the new address
|
|
1498
|
+
record = self._known_servers.get(old_key)
|
|
1499
|
+
if record is not None:
|
|
1500
|
+
# Re-key the pairing so the stored token still applies — the
|
|
1501
|
+
# whole point: no fresh approval just because DHCP moved the
|
|
1502
|
+
# server.
|
|
1503
|
+
self._known_servers.remember(
|
|
1504
|
+
new_key, record.get("fingerprint") or "", record["token"]
|
|
1505
|
+
)
|
|
1506
|
+
self._known_servers.forget(old_key)
|
|
1507
|
+
session.key = new_key
|
|
1508
|
+
session.host, session.port = server.host, server.port
|
|
1509
|
+
# The peer's inventory row moves with it; the stale address would
|
|
1510
|
+
# otherwise linger as a permanently-unreachable duplicate.
|
|
1511
|
+
self.client_inventory.remove(old_key)
|
|
1512
|
+
self.client_inventory.record(
|
|
1513
|
+
new_key, "attempt", name=session.name, address=new_key, detail=new_key
|
|
1514
|
+
)
|
|
1515
|
+
self._persist_sessions()
|
|
1516
|
+
message = f"Found {session.name} at new address {new_key} (was {old_key}) — reconnecting"
|
|
1517
|
+
self._set_session_status(session, message)
|
|
1518
|
+
self.log(f"[{session.name}] {message}")
|
|
1519
|
+
# A new address deserves a fresh backoff; the connect failure path
|
|
1520
|
+
# re-arms it (and further re-discovery) if this address fails too.
|
|
1521
|
+
self._cancel_reconnect(session)
|
|
1522
|
+
session.client.connect_to(server.host, server.port)
|
|
1523
|
+
|
|
1395
1524
|
def _request_server_log(self) -> None:
|
|
1396
1525
|
session = self._session_for_page(self._tabs.currentWidget())
|
|
1397
1526
|
if session is None:
|
remotedesktop/client.py
CHANGED
|
@@ -233,5 +233,8 @@ class ServerSession:
|
|
|
233
233
|
self.auto_reconnect = False
|
|
234
234
|
self.reconnect_attempts = 0
|
|
235
235
|
self.reconnect_timer: QTimer | None = None
|
|
236
|
+
# True while a re-discovery scan for this session runs on a worker
|
|
237
|
+
# thread (the server may have come back on a different address).
|
|
238
|
+
self.rediscovering = False
|
|
236
239
|
# What the status bar shows while this session's tab is current.
|
|
237
240
|
self.status_text = ""
|
remotedesktop/discovery.py
CHANGED
|
@@ -28,6 +28,10 @@ class ServerInfo:
|
|
|
28
28
|
name: str
|
|
29
29
|
host: str
|
|
30
30
|
port: int
|
|
31
|
+
# The server's TLS certificate fingerprint, when it advertises one —
|
|
32
|
+
# a stable identity that survives address changes (DHCP gave the server
|
|
33
|
+
# a new IP after a reboot). Empty from servers too old to send it.
|
|
34
|
+
fingerprint: str = ""
|
|
31
35
|
|
|
32
36
|
|
|
33
37
|
def _parse(data: bytes, expected_type: str) -> dict | None:
|
|
@@ -58,10 +62,14 @@ class DiscoveryResponder:
|
|
|
58
62
|
name: str,
|
|
59
63
|
connect_port: int,
|
|
60
64
|
*,
|
|
65
|
+
fingerprint: str = "",
|
|
61
66
|
discovery_port: int = DISCOVERY_PORT,
|
|
62
67
|
bind_host: str = "",
|
|
63
68
|
) -> None:
|
|
64
|
-
|
|
69
|
+
# The fingerprint (`fp`) is optional in the reply: clients that
|
|
70
|
+
# predate it simply ignore the extra field.
|
|
71
|
+
extra = {"fp": fingerprint} if fingerprint else {}
|
|
72
|
+
self._reply = _encode("reply", name=name, port=connect_port, **extra)
|
|
65
73
|
self._discovery_port = discovery_port
|
|
66
74
|
self._bind_host = bind_host
|
|
67
75
|
self._socket: socket.socket | None = None
|
|
@@ -181,6 +189,12 @@ def discover_servers(
|
|
|
181
189
|
if not isinstance(name, str) or not isinstance(port, int):
|
|
182
190
|
_log.debug("Ignoring malformed reply from %s: %r", host, message)
|
|
183
191
|
continue
|
|
184
|
-
|
|
192
|
+
fingerprint = message.get("fp")
|
|
193
|
+
if not isinstance(fingerprint, str):
|
|
194
|
+
fingerprint = ""
|
|
195
|
+
found.setdefault(
|
|
196
|
+
(host, port),
|
|
197
|
+
ServerInfo(name=name, host=host, port=port, fingerprint=fingerprint),
|
|
198
|
+
)
|
|
185
199
|
_log.debug("Discovery scan finished: %d server(s) found", len(found))
|
|
186
200
|
return list(found.values())
|
remotedesktop/server.py
CHANGED
|
@@ -295,8 +295,14 @@ class SharingTab(QWidget):
|
|
|
295
295
|
self._listening = server.listen(self._connect_port)
|
|
296
296
|
self._discoverable = False
|
|
297
297
|
if self._listening:
|
|
298
|
+
# Advertising the cert fingerprint lets clients recognize this
|
|
299
|
+
# server by identity when its IP changes (auto-reconnect
|
|
300
|
+
# re-discovery), not just by address.
|
|
298
301
|
responder = DiscoveryResponder(
|
|
299
|
-
self._name,
|
|
302
|
+
self._name,
|
|
303
|
+
server.port,
|
|
304
|
+
fingerprint=tls.certificate_fingerprint(self._credentials[0]),
|
|
305
|
+
discovery_port=self._discovery_port,
|
|
300
306
|
)
|
|
301
307
|
try:
|
|
302
308
|
responder.start()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: remotedesktop
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Remote desktop app for Windows computers on the same LAN, with autodiscovery. View other computers and optionally share this one — screen, keyboard, mouse, and clipboard, without RDP or Microsoft authentication.
|
|
5
5
|
Project-URL: Homepage, https://github.com/jamesabel/remotedesktop
|
|
6
6
|
Project-URL: Repository, https://github.com/jamesabel/remotedesktop
|
|
@@ -84,10 +84,10 @@ one-click *Forget* / *Revoke*, and the live connection log.
|
|
|
84
84
|
- 🔒 **TLS + approve-once pairing** — every connection is encrypted; the server user approves a new client once, after which it reconnects with a stored token and no prompt.
|
|
85
85
|
- 🔐 **Honest lock-screen behavior** — a locked server tells viewers so with a clear on-screen notice instead of a frozen frame, and streaming resumes by itself once someone signs in at the machine (see [The Windows lock screen](#the-windows-lock-screen)).
|
|
86
86
|
- 📊 **Built-in performance monitoring** — live bandwidth and round-trip-time graphs with window statistics (mean/min/max/p99/jitter), plus a per-viewer table on the server.
|
|
87
|
-
- 🔁 **Robust connections** — dead connections are detected within seconds, and dropped sessions reconnect automatically with backoff; a server restart heals by itself, no clicks needed. Connections that were open when the app closed are restored on the next start.
|
|
87
|
+
- 🔁 **Robust connections** — dead connections are detected within seconds, and dropped sessions reconnect automatically with backoff; a server restart heals by itself, no clicks needed. Even a server that comes back on a **different IP address** (a new DHCP lease after a reboot) is found again: reconnection re-scans the LAN, recognizes the server by its certificate identity, and follows it to the new address — pairing intact, so still no prompt. Connections that were open when the app closed are restored on the next start.
|
|
88
88
|
- 🚀 **Hands-off operation** — start-at-login (per-user, no admin rights) with a choice of minimized (the default and recommended — sharing resumes after a reboot with no clicks), normal, maximized, or not starting at all; close-to-tray while sharing (the screen stays available with the window closed); and a *Restart app* button usable from the remote session itself, so you can update the software without visiting the machine.
|
|
89
89
|
- 🗃️ **Persistent peer inventory** — a SQLite-backed history of every peer seen on the LAN, with one-click *Revoke* / *Forget*.
|
|
90
|
-
- 🧭 **Desktop-app niceties** — a real menu bar with standard shortcuts, a status-bar sharing indicator, a confirmation before quitting with viewers connected, and window/panel layout that persists across restarts.
|
|
90
|
+
- 🧭 **Desktop-app niceties** — a real menu bar with standard shortcuts (Preferences on *File ▸ Preferences*, Ctrl+,; About on the Help menu), a status-bar sharing indicator, a confirmation before quitting with viewers connected, and window/panel layout that persists across restarts.
|
|
91
91
|
|
|
92
92
|
In scope: screen, keyboard, mouse, and clipboard. Out of scope: shared
|
|
93
93
|
drives, devices, and audio — and smooth playback of fast-changing
|
|
@@ -119,8 +119,8 @@ it prepares the environment on first use and launches the app.
|
|
|
119
119
|
## Quick start
|
|
120
120
|
|
|
121
121
|
1. Run `remotedesktop` on both computers.
|
|
122
|
-
2. On the computer to share, open *Preferences* and set
|
|
123
|
-
to one of the *Shared* modes.
|
|
122
|
+
2. On the computer to share, open *File ▸ Preferences* (Ctrl+,) and set
|
|
123
|
+
*Server (sharing)* to one of the *Shared* modes.
|
|
124
124
|
3. On the viewing computer, the shared computer appears in the panel on
|
|
125
125
|
the left — double-click it (or select it and click *Connect*).
|
|
126
126
|
4. Approve the connection in the dialog that pops up on the shared
|
|
@@ -228,7 +228,8 @@ certificate the server generates and keeps, then simple length-prefixed
|
|
|
228
228
|
messages on top — JSON for control (hello/welcome, input, clipboard,
|
|
229
229
|
ping/pong for the round-trip-time graphs, log exchange) and binary
|
|
230
230
|
payloads for frames and deltas. Discovery is a UDP broadcast probe that
|
|
231
|
-
every server answers with its name and
|
|
231
|
+
every server answers with its name, port, and certificate fingerprint
|
|
232
|
+
(see below).
|
|
232
233
|
|
|
233
234
|
## Versioning
|
|
234
235
|
|
|
@@ -248,7 +249,12 @@ not guaranteed. Keep both computers on the same version for best results.
|
|
|
248
249
|
## How discovery works
|
|
249
250
|
|
|
250
251
|
The client broadcasts a small JSON probe over UDP (port 48653); each server
|
|
251
|
-
on the LAN replies with its hostname
|
|
252
|
+
on the LAN replies with its hostname, connection port, and TLS certificate
|
|
253
|
+
fingerprint. The fingerprint is the server's stable identity: when a
|
|
254
|
+
reconnecting client can no longer reach a server at its last known address
|
|
255
|
+
(typically a new DHCP lease after a reboot), it re-scans and matches the
|
|
256
|
+
fingerprint to follow the server to its new address, keeping the stored
|
|
257
|
+
pairing token so no fresh approval is needed. Windows Firewall
|
|
252
258
|
must allow Python to receive inbound UDP on that port for a server to be
|
|
253
259
|
discoverable from other machines.
|
|
254
260
|
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
remotedesktop/__init__.py,sha256
|
|
1
|
+
remotedesktop/__init__.py,sha256=u2ClDvV49VmCDrIduAONdKUtfp5pewVgOStW3k8-2eU,100
|
|
2
2
|
remotedesktop/__main__.py,sha256=m4gvhT01Wd3_kNl8lNQihy7eG2O3mYBbYP__AwBnyos,158
|
|
3
3
|
remotedesktop/about.py,sha256=gofXchMgQ2Sikv1kFBIl42M4L-LY4bFKMARd5HeEyio,3701
|
|
4
|
-
remotedesktop/app.py,sha256=
|
|
4
|
+
remotedesktop/app.py,sha256=7_TakYMXX0ZRD74MRQWK6ni2fpAtlANGfmB_1cj7kTM,74790
|
|
5
5
|
remotedesktop/autostart.py,sha256=pxpKpm_4IvH73ZbAbzM1TOQVKyRnm7DxtcqRDWvpw2Q,4539
|
|
6
|
-
remotedesktop/client.py,sha256=
|
|
6
|
+
remotedesktop/client.py,sha256=PgmGDHgrWWeiyrT8OZQr9E7hFQV_NoS7mfK1YsnXlkM,10086
|
|
7
7
|
remotedesktop/clipboard.py,sha256=x2OBCn8xtLFyvAjvLflJYr85aB0tiqWN30ZhlMHc6QU,14013
|
|
8
8
|
remotedesktop/compat.py,sha256=i9_iobDCluK-7QXm-ZkXBCU3Qt0GTb0LobZ8XuyK9uk,1694
|
|
9
9
|
remotedesktop/config.py,sha256=biU9FQHxB9oXFivIAhZVM2eeL6lUEpbB7cEcCGfWt_8,5022
|
|
10
10
|
remotedesktop/cursor_shape.py,sha256=kgcAeM-4CebkmtF3c_quhZ9ZIq3sn7sw_YvydzW6LBU,3369
|
|
11
11
|
remotedesktop/db.py,sha256=ppV366kZjEpqNXTuRrrBIE976cSoUddx3WSroygXeow,1732
|
|
12
|
-
remotedesktop/discovery.py,sha256=
|
|
12
|
+
remotedesktop/discovery.py,sha256=CzLOUo6L1xwmEmgIBs1WKFRZAyCMg6iqZNliwQdbo0g,7540
|
|
13
13
|
remotedesktop/dxgi.py,sha256=-2fFsBcmbE3ps3_HBdP42D0LUKpZyqBjCZqdEgWFkAU,13573
|
|
14
14
|
remotedesktop/frames.py,sha256=aUdOoWG7HoozKfD4Z1pVNBnmNoP7DAGGQEYgokSL0rM,4617
|
|
15
15
|
remotedesktop/icon.py,sha256=6Gfqj2liZIj7wC_vdv-gMHr2uFupRYbQMntHXXjTBgU,2476
|
|
@@ -20,7 +20,7 @@ remotedesktop/modal_loop.py,sha256=eb7S5mGffaYYVOOrSrQulqCUPP7t3mINBj7R-hv5C9A,7
|
|
|
20
20
|
remotedesktop/performance.py,sha256=2DvGYP39URSm3fgzYWnLJAdnmWc3NkCAHr2636nZguk,25337
|
|
21
21
|
remotedesktop/preferences.py,sha256=mVKnR8fgWliyM5b5V7FXVBQH7jW15V4W2L93xwA9LMw,16716
|
|
22
22
|
remotedesktop/protocol.py,sha256=Q-5n-s0YmZB7yzlICHzyc73JLvax-SO__5c7Bjg5xGw,3975
|
|
23
|
-
remotedesktop/server.py,sha256=
|
|
23
|
+
remotedesktop/server.py,sha256=wlE-sQtuveW97ErcB-tl0TfSNqtyLxzLQnqvRzeflT4,17769
|
|
24
24
|
remotedesktop/session_lock.py,sha256=W5_MGYVgn3fnWT7Nzgxtt-aqJsnvyrOyfG2aCZTFcnM,1881
|
|
25
25
|
remotedesktop/sharing.py,sha256=HDFCWnONftVbp7s_R9-_DUzj_q1Q7PTKRYGSJPt2u0c,49282
|
|
26
26
|
remotedesktop/single_instance.py,sha256=G14dXIKctGFv0CvyCjdbeqkXTdW2y88MS5FEgxZNFz8,3002
|
|
@@ -28,8 +28,8 @@ remotedesktop/tls.py,sha256=AGrBpDq1gdjQm7TZ74y2W5-RxyEcbsFDPlNdRvadaAk,3934
|
|
|
28
28
|
remotedesktop/viewer.py,sha256=arrIpQIazqhbWlElAbXyqeHaHZ0JqxlNbPwavpPixyo,14545
|
|
29
29
|
remotedesktop/visual_effects.py,sha256=NQPb6qpwlW6SUycYbw-7mZOOmlPzC5YsjolwcxNukGI,7468
|
|
30
30
|
remotedesktop/window_state.py,sha256=LvTGg5ZFSuDmISxYU0xotrGZbL89oVqKo8LI7xQGKok,1579
|
|
31
|
-
remotedesktop-2.
|
|
32
|
-
remotedesktop-2.
|
|
33
|
-
remotedesktop-2.
|
|
34
|
-
remotedesktop-2.
|
|
35
|
-
remotedesktop-2.
|
|
31
|
+
remotedesktop-2.2.0.dist-info/METADATA,sha256=I7vDfRLUDtj-9KflaEYJHTYMdJc9KvlkNaJ4WCNSym8,17766
|
|
32
|
+
remotedesktop-2.2.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
33
|
+
remotedesktop-2.2.0.dist-info/entry_points.txt,sha256=t079osY5GlKUJP9JU5JVXlKrSrplLalw2koAslXWQWc,53
|
|
34
|
+
remotedesktop-2.2.0.dist-info/licenses/LICENSE,sha256=6rWgMfDohGLDuJ0UFOyLphM28Nn0fuN26MczIQldMkc,1067
|
|
35
|
+
remotedesktop-2.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|