tabby-rdp 0.2.7 → 0.3.0

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.
Files changed (57) hide show
  1. package/CHANGELOG.md +61 -2
  2. package/README.md +181 -21
  3. package/dist/connectionStatus.js +140 -0
  4. package/dist/connectionStatus.js.map +1 -0
  5. package/dist/desk.js +21 -10
  6. package/dist/desk.js.map +1 -1
  7. package/dist/deskScript.js +3 -0
  8. package/dist/deskScript.js.map +1 -1
  9. package/dist/desktop.service.js +993 -53
  10. package/dist/desktop.service.js.map +1 -1
  11. package/dist/desktopForm.js +64 -19
  12. package/dist/desktopForm.js.map +1 -1
  13. package/dist/desktops.js +60 -21
  14. package/dist/desktops.js.map +1 -1
  15. package/dist/fileTransfer.js +152 -5
  16. package/dist/fileTransfer.js.map +1 -1
  17. package/dist/header.js +22 -7
  18. package/dist/header.js.map +1 -1
  19. package/dist/help.js +235 -0
  20. package/dist/help.js.map +1 -0
  21. package/dist/index.js +14 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/keyboard.js +142 -5
  24. package/dist/keyboard.js.map +1 -1
  25. package/dist/microphone.js +295 -0
  26. package/dist/microphone.js.map +1 -0
  27. package/dist/osd.js +79 -0
  28. package/dist/osd.js.map +1 -0
  29. package/dist/probe.js +33 -0
  30. package/dist/probe.js.map +1 -0
  31. package/dist/rdcleanpath.js +132 -7
  32. package/dist/rdcleanpath.js.map +1 -1
  33. package/dist/rdpFile.js +47 -0
  34. package/dist/rdpFile.js.map +1 -0
  35. package/dist/rdpProfile.js +367 -0
  36. package/dist/rdpProfile.js.map +1 -0
  37. package/dist/remoteSetup.js +90 -11
  38. package/dist/remoteSetup.js.map +1 -1
  39. package/dist/settingsPage.js +538 -0
  40. package/dist/settingsPage.js.map +1 -0
  41. package/dist/signin.js +21 -2
  42. package/dist/signin.js.map +1 -1
  43. package/dist/ssh.js +28 -0
  44. package/dist/ssh.js.map +1 -1
  45. package/dist/targets.js +148 -8
  46. package/dist/targets.js.map +1 -1
  47. package/dist/ui.js +154 -24
  48. package/dist/ui.js.map +1 -1
  49. package/dist/vms.js +90 -0
  50. package/dist/vms.js.map +1 -0
  51. package/dist/wake.js +124 -0
  52. package/dist/wake.js.map +1 -0
  53. package/package.json +2 -1
  54. package/vendor/IRONRDP-LICENSE +3 -3
  55. package/vendor/iron-remote-desktop-rdp.js +698 -451
  56. package/vendor/iron-remote-desktop.js +87 -81
  57. package/vendor/ironrdp_web_bg.wasm +0 -0
package/CHANGELOG.md CHANGED
@@ -1,9 +1,68 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0
4
+
5
+ **New**
6
+ - **Settings › Remote Desktop:** getting started, every setting with a line on what it does, keys and tips, the
7
+ desktops and certificates the plugin keeps, troubleshooting, and each open desktop's log to copy for a bug report.
8
+ A tip the first time a desktop connects, and **What does this mean?** under a message that ends a connection.
9
+ - **Several desktops in one tab:** **Paste to all desktops in this tab** (text, pictures, or files copied here),
10
+ **Type into all desktops in this tab** (a label on each says so while it's on), and **Make panes even**. As you
11
+ click into a desktop, its name shows in its corner for a moment; when, and its font, size, place and color, are in
12
+ the settings, with a preview.
13
+ - **Files copied in Finder** (Explorer, Files) paste on the desktop with ⌘V (Ctrl+V), and the paste waits until the
14
+ remote has taken them in.
15
+ - **VMs on a host:** the libvirt VMs with a desktop on an SSH host (RDP answering, or Windows and shut off) show up in
16
+ its menu, ready to open, or to start and open. **Find virtual machines on SSH hosts** turns it off.
17
+ - **`ssh` typed in an SSH tab:** **Desktop** and `desk` there open the machine it went to, through the tab's host, and
18
+ Reconnect stays with it.
19
+ - **Remote desktop (RDP) profiles:** desktops as Tabby profiles, in their own tab: connect directly to an RDP server
20
+ this machine reaches (LAN, VPN), or through an SSH profile. They show up in the profile list, quick connect
21
+ (`user@host:port`), recent profiles and tab recovery. **Import an .rdp file…** turns a saved Remote Desktop
22
+ connection into one.
23
+ - **Linux desktops through xrdp** (KDE, XFCE, MATE, …): the SSH host's own desktop is xrdp's when GNOME isn't there,
24
+ and offered next to GNOME when both are; `kind: xrdp` for desktops behind a host. Signs in with the Linux account.
25
+ - **Windows as the SSH host:** an SSH host that is itself Windows (OpenSSH server) opens its own RDP desktop.
26
+ - **H.264 video decoding** (setting **Video decoding**, on by default), hardware-accelerated where Tabby's engine can:
27
+ Windows desktops now use the graphics pipeline with H.264 for what changes like video, instead of bitmaps; GNOME
28
+ uses it when its machine has a hardware encoder.
29
+ - **Microphone** (off by default): an app on the remote desktop that records gets your microphone, only while it
30
+ records, with an indicator.
31
+ - **Send keys:** Ctrl+Alt+Del, Win+R, Win+L, Task Manager and more for Windows; Super, the app grid, Alt+F2 for GNOME.
32
+ From a Mac, ⌃⌘ with a key is the Windows key with it (⌃⌘R for Win+R).
33
+ - **View only**, **Save a screenshot** (to Downloads and the clipboard), and **actual size** with scroll bars for a
34
+ fixed resolution.
35
+ - **Certificates:** GNOME desktops accept only the certificate the plugin made for them; other desktops remember theirs
36
+ on first use and stop to ask, before signing in, if it changes.
37
+ - **Edit a desktop** behind a host from the menus (name, address, account, kind).
38
+ - **Wake before connecting:** `wake: { vm }` starts a libvirt VM on the SSH host, `wake: { mac }` sends Wake-on-LAN,
39
+ when the desktop doesn't answer.
40
+ - **Connection status** (off by default): throughput, frames per second, SSH round trip and how it's connected, in the
41
+ desktop's corner.
42
+
43
+ **Fixed**
44
+ - `desk` run on another machine than the tab's (after `ssh` there, or with a shared home folder) opened a terminal on
45
+ the wrong desktop; it's refused now, with a note that says why.
46
+ - Windows over the graphics pipeline: IronRDP's RemoteFX Progressive decoder rejected Windows' refinement passes and
47
+ ended the session (patch 0010, for upstream).
48
+
49
+ **Package**
50
+ - IronRDP's WebAssembly ships as its own file instead of a 6.6 MB base64 string inside the JavaScript: the package is
51
+ about 2 MB instead of 2.7 MB.
52
+ - Releases are built and staged on npm by GitHub Actions, with provenance.
53
+
54
+ **Changed**
55
+ - Hosts are called by their SSH profile's name (not its address) in messages and the on-screen name.
56
+ - **Remove a desktop** asks first.
57
+ - **Menus:** the plugin's items in a terminal's or tab's menu start with a **Remote Desktop** heading (naming the
58
+ desktop while one is connected), so they read as the plugin's rather than Tabby's. Under it the items are shorter:
59
+ **Send files…**, **Disconnect** and **Settings**, and the host's other desktops (and VMs found there) and
60
+ **Add a desktop behind \<host\>…** are under **Desktops**.
61
+
3
62
  ## 0.2.7
4
63
 
5
- (0.2.6 was never released: its staged publish on npm didn't go through, and npm doesn't reuse a version
6
- number. 0.2.7 is the same change.)
64
+ (0.2.6 carries the same change: its staged publish on npm looked like it had failed and only appeared later, so the
65
+ change was released again as 0.2.7.)
7
66
 
8
67
  **Security**
9
68
  - Files copied on the remote desktop are saved inside the folder you save them to (Downloads). Their names come from
package/README.md CHANGED
@@ -5,13 +5,15 @@
5
5
  # tabby-rdp
6
6
 
7
7
  **Remote desktops in your Tabby SSH tabs.**<br>
8
- Linux (GNOME) and Windows, through the SSH connection you already have.
8
+ Linux and Windows, side by side, through the SSH connections you already have.
9
9
 
10
10
  [![npm](https://img.shields.io/npm/v/tabby-rdp?color=3b82f6)](https://www.npmjs.com/package/tabby-rdp)
11
11
  [![License: MIT](https://img.shields.io/badge/license-MIT-3b82f6)](LICENSE)
12
12
  [![Tabby plugin](https://img.shields.io/badge/Tabby-plugin-6366f1)](https://tabby.sh)
13
13
 
14
- <img src="docs/images/desktop.png" width="860" alt="A GNOME desktop in a Tabby SSH tab">
14
+ <a href="https://github.com/meanaverage/tabby-rdp/releases/download/v0.3.0/tabby-rdp-demo.mp4"><img src="docs/images/demo.jpg" width="860" alt="Four desktops in one Tabby tab (GNOME, Windows, xrdp), a file pasted to all of them, and desk (demo video)"></a>
15
+
16
+ <sub>▶ <a href="https://github.com/meanaverage/tabby-rdp/releases/download/v0.3.0/tabby-rdp-demo.mp4">Watch the 30-second demo</a></sub>
15
17
 
16
18
  </div>
17
19
 
@@ -23,12 +25,24 @@ nothing is opened on the network, and nothing has to be installed on the remote
23
25
 
24
26
  - **Linux desktops without a monitor or a login.** The plugin starts a headless GNOME session for your SSH user with
25
27
  GNOME Remote Desktop, on the fly, without root. A server in a rack works as well as a workstation.
26
- - **Windows desktops** behind any SSH host, such as a VM on your build server.
27
- - **A proper desktop experience:** clipboard and files both ways, sound, live resize to the pane, sharp Retina
28
- rendering, Mac keyboard shortcuts, and automatic reconnects after sleep.
28
+ - **Other Linux desktops** (KDE, XFCE, MATE, Cinnamon, …) through xrdp, where it runs: the plugin finds it by itself.
29
+ - **Windows desktops** behind any SSH host, such as a VM on your build server, and the desktop of a Windows machine
30
+ you SSH into.
31
+ - **Remote desktop profiles** for machines you reach without SSH (LAN, VPN): in Tabby's profile list, in a tab of their
32
+ own.
33
+ - **Several machines at once:** split a tab into a grid of desktops, then paste to all of them or type into all of them
34
+ at once. A short on-screen name says which one you're in.
35
+ - **VMs found for you:** the libvirt VMs on an SSH host that have a desktop show up in its menu, ready to open, or to
36
+ start and open.
37
+ - **`ssh` on from a host:** in an SSH tab where you typed `ssh` to another machine, the desktop is that machine's.
38
+ - **A proper desktop experience:** clipboard and files both ways, sound and microphone, live resize to the pane,
39
+ sharp Retina rendering, Mac keyboard shortcuts, and automatic reconnects after sleep.
29
40
  - **`desk`:** type `desk` in the console and the same shell, with its history and running programs, moves into a
30
41
  terminal on the desktop.
31
42
 
43
+ Everything is in **Settings › Remote Desktop**: getting started, every setting with a line on what it does, the keys,
44
+ the desktops and certificates the plugin keeps, and troubleshooting.
45
+
32
46
  The remote desktop itself is [IronRDP](https://github.com/Devolutions/IronRDP)'s web client, running inside Tabby.
33
47
  Making it work with GNOME took fixes in IronRDP, which we contribute upstream ([below](#built-on-ironrdp)).
34
48
 
@@ -38,6 +52,9 @@ Making it work with GNOME took fixes in IronRDP, which we contribute upstream ([
38
52
  - [Use](#use)
39
53
  - [Features](#features)
40
54
  - [Windows and other desktops behind a host](#windows-and-other-desktops-behind-a-host)
55
+ - [Remote desktop profiles](#remote-desktop-profiles)
56
+ - [Several desktops in one tab](#several-desktops-in-one-tab)
57
+ - [VMs on a host](#vms-on-a-host)
41
58
  - [desk: the console on the desktop](#desk-the-console-on-the-desktop)
42
59
  - [Settings](#settings)
43
60
  - [Requirements](#requirements)
@@ -63,14 +80,24 @@ Open an SSH tab to a Linux machine with GNOME, and:
63
80
 
64
81
  - press **⌘⇧G** (Ctrl+Shift+G on Windows and Linux), or
65
82
  - click **Desktop** in the SSH tab's toolbar, or the desktop button in Tabby's header, or
66
- - right-click the terminal or the tab: **Open remote desktop**.
83
+ - right-click the terminal or the tab: **Open remote desktop**, under the **Remote Desktop** heading.
84
+
85
+ <p align="center"><img src="docs/images/desktop.png" width="760" alt="A GNOME desktop in a Tabby SSH tab"></p>
67
86
 
68
87
  The first connection to a machine takes a few seconds while it prepares GNOME Remote Desktop and a session; later
69
88
  ones take well under a second. The same shortcut switches between the desktop and the console, and the desktop keeps
70
89
  running in the background until you disconnect or close the tab.
71
90
 
91
+ A machine without GNOME but with [xrdp](https://github.com/neutrinolabs/xrdp) running (the usual RDP server for KDE,
92
+ XFCE, MATE and the rest) gets xrdp's desktop instead: sign in with your Linux password, which the plugin can remember
93
+ in the system keychain. A machine with both keeps GNOME as the tab's desktop and, once that has been opened, also
94
+ offers **Open \<host\> desktop (xrdp)** in its menus.
95
+
72
96
  It also works in a plain local terminal where you typed `ssh host` (macOS and Linux), as long as that host accepts
73
- your key without a password prompt.
97
+ your key without a password prompt. And in an SSH tab where you typed `ssh` on to another machine: **Desktop** there
98
+ opens that machine's desktop, going through the first, as long as the first can log in to it by itself (a key there,
99
+ or agent forwarding). So one pane of a split can show the first machine's desktop, and the other, after `ssh other`,
100
+ that machine's.
74
101
 
75
102
  One desktop per account: a second tab to the same `user@host` switches to the tab that has it open, rather than
76
103
  connecting again.
@@ -80,22 +107,32 @@ connecting again.
80
107
  | | |
81
108
  |---|---|
82
109
  | **Clipboard** | Copy and paste text and images in both directions. |
83
- | **Files** | Drop files from Finder onto the desktop, or use **Send files to the remote desktop…**, then paste them in Files or Explorer. Files copied on the remote offer **Save to Downloads**. |
110
+ | **Files** | Copy files or folders in Finder and press ⌘V on the desktop, or drop them onto it, or use **Remote Desktop › Send files…** in the pane's menu; they paste in Files or Explorer. Files copied on the remote offer **Save to Downloads**. |
111
+ | **Several desktops in one tab** | Paste to all of them, type into all of them, and see which is which: [below](#several-desktops-in-one-tab). |
112
+ | **VMs on a host** | The host's VMs with a desktop, ready to open: [below](#vms-on-a-host). |
84
113
  | **Sound** | The remote desktop's sound plays locally. |
85
- | **Resize** | The remote resolution follows the pane as you resize the window or split it. Or reconnect at the new size, or keep a fixed resolution. |
114
+ | **Microphone** | Optional (off by default): an app on the remote desktop that records, such as a call, gets your microphone. It is only captured while the app records, with an indicator in the corner of the desktop. |
115
+ | **Resize** | The remote resolution follows the pane as you resize the window or split it. Or reconnect at the new size, or keep a fixed resolution, scaled to fit or at actual size with scroll bars. |
86
116
  | **Retina** | Optionally renders at device pixels, with the remote's UI scaled to match (GNOME and Windows alike), for sharp text. For all desktops, or only some. |
87
- | **Keyboard on macOS** | ⌘C, ⌘V, ⌘Z and the rest work as on a Mac; tapping ⌘ alone is the Windows key. Tabby's own shortcuts stay out of the way while a desktop is showing, except switching tabs and returning to the console. |
117
+ | **Keyboard on macOS** | ⌘C, ⌘V, ⌘Z and the rest work as on a Mac; tapping ⌘ alone is the Windows key, and ⌃⌘ with a key is the Windows key with it (⌃⌘R for Win+R). Tabby's own shortcuts stay out of the way while a desktop is showing, except switching tabs and returning to the console. |
118
+ | **Send keys** | In the desktop's menus: Ctrl+Alt+Del, Win+R, Win+L, Ctrl+Shift+Esc, Print Screen and more for Windows; Super, Super+A, Alt+F2 and more for GNOME. |
119
+ | **View only** | In the desktop's menus: watch a desktop without touching it. No keys or clicks go to it while a "View only" label shows; the picture, clipboard and sound carry on. Kept across reconnects, until the desktop is closed. |
120
+ | **Screenshots** | **Save a screenshot** in the desktop's menus saves the remote screen at its full resolution as a PNG in Downloads, and copies it to the clipboard. |
121
+ | **Connection status** | Optionally, a small line in the corner of the desktop with the throughput each way, frames per second, the SSH round trip, and how it is connected (desktop, host, resolution, graphics mode, sharpness). |
88
122
  | **Reconnecting** | After sleep or a network change, the desktop reconnects by itself once the connection is back. |
89
- | **Sign-in** | GNOME desktops need none: the plugin manages their credentials. Windows desktops ask for the account once and can remember it in the system keychain. |
123
+ | **Sign-in** | GNOME desktops need none: the plugin manages their credentials. Windows and xrdp desktops ask for the account once and can remember it in the system keychain; **Sign in again…** in the menu replaces it. |
124
+ | **Certificates** | GNOME desktops accept only the certificate the plugin made for them. Windows and xrdp desktops remember theirs on first use and ask before accepting a different one. |
90
125
 
91
126
  <p align="center"><img src="docs/images/files.png" width="760" alt="A file copied on the remote desktop, offered for saving"></p>
92
127
 
93
128
  ## Windows and other desktops behind a host
94
129
 
95
130
  An SSH host can lead to other desktops it can reach, such as a Windows VM on the same machine. In that host's menu,
96
- choose **Add a desktop behind \<host\>…** and enter a name, the address as seen from the host, and optionally the
97
- user name. It opens right away, and from then on appears next to the host's own desktop in its menus. **Remote
98
- desktop settings › Remove a desktop** removes one, along with its saved password.
131
+ choose **Desktops › Add a desktop behind \<host\>…** and enter a name, the address as seen from the host, and optionally the
132
+ user name and domain. It opens right away, and from then on appears under **Desktops** in the host's menu.
133
+ **Settings › Edit a desktop** changes one (its saved password, sharpness and remembered certificate
134
+ follow a new address; a new user name forgets the saved password), and **Remove a desktop** removes one, along with
135
+ its saved password and remembered certificate.
99
136
 
100
137
  <p align="center"><img src="docs/images/windows.png" width="760" alt="A Windows 11 desktop, reached through an SSH host, in a Tabby tab"></p>
101
138
 
@@ -108,13 +145,108 @@ remoteDesktop:
108
145
  via: buildhost # the SSH host: alias, hostname, user@hostname or user@hostname:port
109
146
  host: 192.168.122.20 # the RDP server as seen from that host
110
147
  port: 3389
111
- kind: windows # or gnome
148
+ kind: windows # or xrdp (a Linux desktop served by xrdp), or gnome
112
149
  username: alice # optional; DOMAIN\user works too
150
+ domain: CORP # optional
151
+ wake: { vm: win11 } # optional: start it when it's off (see below)
113
152
  ```
114
153
 
115
154
  The connection runs through the same SSH connection; the Windows machine needs Remote Desktop turned on, and nothing
116
155
  else.
117
156
 
157
+ The first connection to a desktop remembers its TLS certificate, without asking (in Tabby's config, under
158
+ `remoteDesktop.trustedCertificates`). If a later connection meets a different one, the plugin stops before signing in
159
+ and shows both fingerprints, with **Trust the new certificate** and **Cancel**. Reinstalling Windows or renewing its
160
+ certificate changes it; if neither happened, something else is answering at that address. Automatic reconnects stop at
161
+ the same question.
162
+
163
+ **An SSH host that is itself Windows** (with OpenSSH Server): its own desktop is Windows' Remote Desktop, at
164
+ 127.0.0.1:3389 as seen from the host. The plugin finds this out the first time you open the desktop there, and asks
165
+ for the Windows account instead (your SSH user name is filled in), with the same keychain option.
166
+
167
+ ### Starting a desktop that is off
168
+
169
+ A VM on the SSH host, or a machine next to it, may be shut down when you want its desktop. With `wake`, the plugin
170
+ checks from the SSH host whether the desktop answers, and if it doesn't, starts it, shows **Starting \<name\>…** with
171
+ the time so far, and connects as soon as it answers (it gives up after 3 minutes):
172
+
173
+ - `wake: { vm: win11 }` starts the libvirt VM `win11` on the SSH host with `virsh start`, as your SSH user: first in
174
+ `qemu:///system` (you need to be in the `libvirt` group, or allowed by polkit), then in your own `qemu:///session`.
175
+ A paused VM is resumed.
176
+ - `wake: { mac: "aa:bb:cc:dd:ee:ff" }` sends a Wake-on-LAN packet from the SSH host (with `python3`, to the broadcast
177
+ address, UDP port 9). Add `broadcast: 192.168.1.255` for a particular network, or `port: 7`.
178
+
179
+ In the add and edit forms, the last field takes a VM name or a MAC address. Automatic reconnects never start a desktop,
180
+ since it may have been shut down on purpose; **Reconnect** does.
181
+
182
+ ## Remote desktop profiles
183
+
184
+ For an RDP server this computer reaches by itself (on the LAN, or over a VPN), there is a Tabby profile type:
185
+ **Settings › Profiles & connections › New profile › Remote desktop (RDP)**. Give it the address, the kind (Windows,
186
+ xrdp or GNOME Remote Desktop), and optionally the user name and domain. It then shows up in Tabby's profile list like
187
+ SSH profiles do, and opens in a tab of its own that the desktop fills; there is no console under it. Sign-in, the
188
+ keychain, certificates, resize, sharpness, clipboard, files, sound, microphone, **Send keys**, view only, screenshots
189
+ and reconnecting work as in SSH tabs. After **Disconnect**, the tab offers **Connect**. In the profile selector you can
190
+ also type `user@host:port` and pick **Quick connect (REMOTE DESKTOP (RDP))**.
191
+
192
+ **Import an .rdp file…** (in the menu's **Settings**, or **Remote desktop: import an .rdp file…** in Tabby's
193
+ command palette) makes such a profile from a file saved by Remote Desktop Connection or handed out by an admin: its
194
+ address, port, user name and domain, named after the file. Other settings in the file (screen size, drives, gateway)
195
+ are ignored.
196
+
197
+ Its **Connect** setting can name an SSH profile instead of connecting directly. The address is then as that host sees
198
+ it, and opening the profile opens that SSH tab and shows the desktop over it once SSH is connected. Such a desktop is
199
+ also offered in the menus of that SSH profile's tabs, next to the host's own desktop, so it works like a desktop
200
+ added with **Desktops › Add a desktop behind \<host\>…**, but with a place in the profile list.
201
+
202
+ In Tabby's config file:
203
+
204
+ ```yaml
205
+ profiles:
206
+ - type: rdp
207
+ name: Office PC
208
+ options:
209
+ host: 192.168.1.20 # as this computer sees it (or as the SSH host sees it, with via)
210
+ port: 3389
211
+ kind: windows # or xrdp, or gnome
212
+ username: alice # optional
213
+ via: '' # or the id of an SSH profile to go through
214
+ ```
215
+
216
+ A direct connection is a plain TCP connection from this computer, encrypted with TLS like any RDP client's. The
217
+ server's certificate is remembered on the first connection and checked on every later one, as for desktops behind a
218
+ host (above).
219
+
220
+ ## Several desktops in one tab
221
+
222
+ Split a tab (Tabby's **Split** in the tab's menu, or drag one tab onto another) and each pane can show a desktop: one
223
+ machine's desktop next to another's, or next to its own console. Right-click any of them:
224
+
225
+ - **Paste to all \<n\> desktops in this tab** pastes what's on your clipboard on every desktop in the tab, in whichever
226
+ window is active on each: text, a picture, or files and folders copied in Finder (or Explorer, or Files).
227
+ - **Type into all \<n\> desktops in this tab** sends every key you type on one desktop to the others too, shortcuts and
228
+ **Send keys** included, until you turn it off. An orange label on each desktop says so while it's on. The mouse stays
229
+ per desktop, and desktops in view-only mode are left out.
230
+ - **Make panes even** evens out the split's panes, which Tabby does itself but offers nowhere else.
231
+
232
+ As you click into a desktop, its name shows in its corner for a moment, like a TV naming its input ("BUILDBOX", or "WIN-11 ·
233
+ via buildhost · 1920×1080"). It shows by default where it helps (in a split, and for a desktop other than the tab's own
234
+ connection); **Settings › Remote Desktop › Desktop name overlay** sets when, and its font, size, place and color, with a
235
+ preview. Desktops are named after their SSH profile's name.
236
+
237
+ ## VMs on a host
238
+
239
+ When you right-click an SSH host, the plugin looks at the host's libvirt VMs (`virsh`, as your SSH user; read-only, at
240
+ most once a minute) and lists the ones with a desktop under **Desktops**, with nothing to set up:
241
+
242
+ - a running VM whose RDP port answers: Windows, or Linux with xrdp;
243
+ - a Windows VM that's shut off: **Start and open**, which starts it (`virsh start`) and connects as soon as it answers.
244
+
245
+ A Linux VM that runs GNOME is left out: its desktop is reached by SSH-ing to it (its own desktop, or `ssh` typed in a
246
+ console on the host, [above](#use)). A VM found this way asks for its account the first time, like any Windows or xrdp
247
+ desktop; **Save \<name\> to this host's desktops** in its menu keeps it, for a name of your own and other settings.
248
+ **Settings › Remote Desktop › Find virtual machines on SSH hosts** turns this off.
249
+
118
250
  ## desk: the console on the desktop
119
251
 
120
252
  Type `desk` in an SSH console, and the tab switches to that machine's desktop with a terminal attached to the very same
@@ -126,24 +258,30 @@ shell: its scrollback, working directory, jobs and running programs. Type on eit
126
258
  </p>
127
259
 
128
260
  `desk` is off by default, because it makes interactive SSH logins on that machine start inside a shareable session.
129
- Turn it on in **Remote desktop settings**. The sessions are handled by a small helper, `trd-pty`, rather than tmux, so
261
+ Turn it on in the pane's menu under **Remote Desktop › Settings**, or in Settings › Remote Desktop. The sessions are handled by a small helper, `trd-pty`, rather than tmux, so
130
262
  your terminal's scrollback, colors and shortcuts stay as they are. Turning it off removes it again. Details:
131
263
  [docs/desk.md](docs/desk.md).
132
264
 
133
265
  ## Settings
134
266
 
135
- Right-click the desktop button in Tabby's header, or open **Remote desktop settings** in a terminal's or tab's menu.
267
+ Open **Settings › Remote Desktop** in Tabby, right-click the desktop button in Tabby's header, or open **Settings** in the Remote Desktop section of a terminal's or tab's menu.
136
268
  They are stored in Tabby's config under `remoteDesktop`:
137
269
 
138
270
  | Setting | Default | |
139
271
  |---|---|---|
140
- | When the pane is resized (`resize`) | Resize the remote desktop to fit (`live`) | Or reconnect at the new size (`reconnect`), or keep the resolution, scaled to fit (`off`). |
272
+ | When the pane is resized (`resize`) | Resize the remote desktop to fit (`live`) | Or reconnect at the new size (`reconnect`), or keep the resolution (`off`). |
273
+ | Keep the resolution: scale to fit or actual size (`zoom`) | Scale to fit (`fit`) | With `resize: off`: `actual` shows one remote pixel per point, with scroll bars when the desktop is larger than the pane (a Retina-sized desktop shows at twice the size). |
141
274
  | Sharpness (`sharpness`) | Standard (`standard`) | Retina (`retina`): device pixels, with the remote's scale set to match. |
142
- | For *this desktop* only (`desktopSharpness`) | As above | In the menu of a tab with a desktop open: Standard or Retina for that desktop, whatever the default. Kept by desktop (`user@host`, or `user@host#address` for one behind a host). |
275
+ | For *this desktop* only (`desktopSharpness`) | As above | In the menu of a tab with a desktop open: Standard or Retina for that desktop, whatever the default. Kept by desktop (`user@host`, `user@host#address` for one behind a host, or `rdp#address` for a remote desktop profile's own tab). |
143
276
  | Sound (`sound`) | On | Applies on the next connection. |
277
+ | Video decoding (`h264`) | On | H.264 decoded by Tabby's browser engine (hardware-accelerated where available), for what the remote sends as video; applies on the next connection. |
278
+ | Microphone (`microphone`) | Off | Send your microphone while an app on the remote desktop records. Applies on the next connection. |
279
+ | Show connection status (`connectionStatus`) | Off | The indicator in the desktop's corner; fades when the pointer comes near. |
144
280
  | Mac shortcuts (`macShortcuts`) | On | macOS. Off: ⌘ is the Windows key. |
145
281
  | Bring the console along with `desk` (`desk`) | Off | Installs `desk` and a login line on each machine you open a desktop on; applies on the next connection there. |
146
282
  | Session backend (`sessionBackend`) | `native` | For `desk`: `native` (trd-pty) or `tmux`. Config file only. |
283
+ | Find virtual machines on SSH hosts (`discoverVMs`) | On | Lists a host's libvirt VMs with a desktop in its menu ([VMs on a host](#vms-on-a-host)). |
284
+ | Desktop name overlay (`osd`) | When it helps | `show` (`auto`, `always`, `off`), `font`, `size`, `position`, `color` (empty: white) and `seconds`; the settings page previews it. |
147
285
 
148
286
  ## Requirements
149
287
 
@@ -153,8 +291,12 @@ They are stored in Tabby's config under `remoteDesktop`:
153
291
  - **Linux desktops:** GNOME Shell and GNOME Remote Desktop 46 or newer (Ubuntu 24.04, for example), a systemd user
154
292
  session, and SSH access with a key or agent. `desk` also needs `python3` and GNOME Terminal. No root, no display, no
155
293
  login screen.
294
+ - **Other Linux desktops:** xrdp running on the machine, with a desktop for its sessions (on Debian and Ubuntu:
295
+ `sudo apt install xrdp xfce4`, or KDE, MATE, …), and an account with a password. xrdp's default settings work
296
+ (`security_layer=negotiate`, its own certificate); the port is read from `/etc/xrdp/xrdp.ini`. Sound needs
297
+ `pipewire-module-xrdp` or `pulseaudio-module-xrdp`.
156
298
  - **Windows desktops:** Windows 10 or 11 Pro, or Windows Server, with Remote Desktop turned on, reachable from an SSH
157
- host.
299
+ host or running OpenSSH Server itself.
158
300
 
159
301
  Tested with Ubuntu 24.04 (GNOME Remote Desktop 46.3) and Windows 11 (Pro and Enterprise), from Tabby on macOS,
160
302
  Windows 11 and Ubuntu 24.04 (arm64).
@@ -177,6 +319,9 @@ tabby-rdp ships IronRDP with a short series of patches ([ironrdp/](ironrdp)). Th
177
319
  | Echo the correct size in the sound channel's Training Confirm | [Devolutions/IronRDP#2019](https://github.com/Devolutions/IronRDP/pull/2019) |
178
320
  | The graphics pipeline in the web client, following its resets | Covered by [Devolutions/IronRDP#1977](https://github.com/Devolutions/IronRDP/pull/1977) (not ours; [tested with GNOME](https://github.com/Devolutions/IronRDP/pull/1977#issuecomment-5851624036)) |
179
321
  | Sound in the web client | [Devolutions/IronRDP#2020](https://github.com/Devolutions/IronRDP/pull/2020) |
322
+ | H.264 in the web client, decoded by the browser (WebCodecs) | Not submitted yet |
323
+ | Decode Windows' RemoteFX Progressive refinements | Not submitted yet |
324
+ | Microphone in the web client | Not submitted yet |
180
325
 
181
326
  The aim is to make IronRDP's browser client work well with both Linux and Windows desktops, for everyone who embeds
182
327
  it, not just this plugin.
@@ -210,7 +355,22 @@ npm run build:ironrdp # rebuild vendor/ from IronRDP and ironrdp/patches:
210
355
  - **GNOME Remote Desktop listens on all interfaces** (password-protected, TLS); it has no setting to listen on
211
356
  loopback only. See [docs/architecture.md](docs/architecture.md#security-notes) to restrict it.
212
357
  - **Windows-key combinations** such as Win+R don't come through from macOS. Tapping ⌘ for the Windows key does.
213
- - **Desktops behind a host** can be added and removed from the menus, but not edited there.
358
+ - **The microphone** needs the system's permission: macOS asks the first time a remote app records (if it was denied,
359
+ allow Tabby in System Settings › Privacy & Security › Microphone, then restart Tabby), and Windows needs "Let desktop
360
+ apps access your microphone". On GNOME, apps record from GNOME Remote Desktop's "Remoteaudio Source", which is the
361
+ default input on a machine without a microphone of its own; otherwise pick it in Settings › Sound. On Windows, the
362
+ remote machine must allow audio recording redirection (the Remote Desktop Session Host policy "Allow audio recording
363
+ redirection").
364
+ - **xrdp sessions are separate X sessions**, not the machine's screen, and use whatever `~/.xsession` or the system
365
+ default starts. Don't have them start GNOME for a user who also has a GNOME session (the plugin's headless one, or
366
+ a login at the machine): GNOME runs once per user.
367
+ - **xrdp and a wrong password:** xrdp doesn't refuse the connection, it shows its own login window instead, where you
368
+ can sign in. A remembered password that no longer works leads there each time; **Sign in again…** replaces it.
369
+ - **What works on xrdp** depends on its channels: the clipboard through `xrdp-chansrv` (text; images and files as
370
+ far as that xrdp version supports them), sound through the PipeWire or PulseAudio module. Live resize needs xrdp
371
+ 0.10 or newer; with 0.9 (Ubuntu 24.04), the desktop keeps its size, scaled to fit, or choose **Reconnect at the new
372
+ size**. xrdp's standard RDP security without TLS (`security_layer=rdp`) isn't supported. `desk` needs GNOME.
373
+ - **Waking a desktop** starts it but never shuts it down again; that is up to you (or the VM's own settings).
214
374
 
215
375
  ## License
216
376
 
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConnectionStatus = exports.STYLE = void 0;
4
+ exports.STYLE = `
5
+ .trd-stats { position: absolute; left: 8px; bottom: 8px; z-index: 2; padding: 3px 7px; border-radius: 4px;
6
+ background: rgba(20, 20, 20, 0.7); color: #ddd; font: 11px/1.45 ui-monospace, Menlo, Consolas, monospace;
7
+ white-space: pre; pointer-events: none; transition: opacity 0.15s; }
8
+ .trd-stats-path { color: #999; }
9
+ .trd-stats.trd-stats-away { opacity: 0.1; }
10
+ `;
11
+ // Rates are per second; the round trip is measured less often, since it opens a channel on the SSH server.
12
+ const TICK_MS = 1000;
13
+ const PING_EVERY_TICKS = 5;
14
+ function rate(bytesPerSecond) {
15
+ if (bytesPerSecond < 1000) {
16
+ return `${Math.round(bytesPerSecond)} B/s`;
17
+ }
18
+ if (bytesPerSecond < 1e6) {
19
+ return `${(bytesPerSecond / 1e3).toFixed(bytesPerSecond < 1e4 ? 1 : 0)} kB/s`;
20
+ }
21
+ return `${(bytesPerSecond / 1e6).toFixed(1)} MB/s`;
22
+ }
23
+ /**
24
+ * The connection-status indicator in a desktop layer: throughput, frames per second, SSH round trip, and the
25
+ * connection path. Runs only while active (the setting is on and the desktop shows and is connected); its timers
26
+ * and listeners stay outside Angular's zone, like everything that runs on its own (see docs/architecture.md).
27
+ */
28
+ class ConnectionStatus {
29
+ constructor(overlay, zone, info) {
30
+ this.overlay = overlay;
31
+ this.zone = zone;
32
+ this.info = info;
33
+ this.timer = null;
34
+ this.ticks = 0;
35
+ this.last = null;
36
+ this.frames = 0;
37
+ this.hooked = null;
38
+ this.latency = null;
39
+ this.pinging = false;
40
+ this.active = false;
41
+ /** The indicator fades while the pointer is near it, so it never hides what is under it for long. */
42
+ this.onMouseMove = (event) => {
43
+ const r = this.el.getBoundingClientRect();
44
+ const near = event.clientX > r.left - 24 && event.clientX < r.right + 24 && event.clientY > r.top - 24 && event.clientY < r.bottom + 24;
45
+ if (near !== this.el.classList.contains('trd-stats-away')) {
46
+ this.el.classList.toggle('trd-stats-away', near);
47
+ }
48
+ };
49
+ this.el = document.createElement('div');
50
+ this.el.className = 'trd-stats';
51
+ this.el.innerHTML = '<div class="trd-stats-numbers"></div><div class="trd-stats-path"></div>';
52
+ this.numbersEl = this.el.querySelector('.trd-stats-numbers');
53
+ this.pathEl = this.el.querySelector('.trd-stats-path');
54
+ }
55
+ /** Starts or stops the indicator. `canvas`: the remote display, whose updates are counted as frames. */
56
+ setActive(active, canvas) {
57
+ if (active && canvas) {
58
+ this.countFrames(canvas);
59
+ }
60
+ if (active === this.active) {
61
+ return;
62
+ }
63
+ this.active = active;
64
+ if (!active) {
65
+ clearInterval(this.timer ?? undefined);
66
+ this.timer = null;
67
+ this.overlay.removeEventListener('mousemove', this.onMouseMove, true);
68
+ this.el.remove();
69
+ return;
70
+ }
71
+ this.last = null;
72
+ this.ticks = 0;
73
+ this.latency = null;
74
+ this.numbersEl.textContent = '…';
75
+ this.pathEl.textContent = this.info.path();
76
+ this.el.classList.remove('trd-stats-away');
77
+ this.overlay.appendChild(this.el);
78
+ this.zone.runOutsideAngular(() => {
79
+ this.timer = setInterval(() => this.tick(), TICK_MS);
80
+ this.overlay.addEventListener('mousemove', this.onMouseMove, { capture: true, passive: true });
81
+ this.tick();
82
+ });
83
+ }
84
+ dispose() {
85
+ this.setActive(false);
86
+ }
87
+ /**
88
+ * IronRDP's web client blits each changed region with putImageData and tells nobody, so the canvas context's
89
+ * putImageData is wrapped (on that one context object). A frame is one synchronous batch of blits.
90
+ */
91
+ countFrames(canvas) {
92
+ const ctx = canvas.getContext('2d');
93
+ if (!ctx || ctx === this.hooked) {
94
+ return;
95
+ }
96
+ this.hooked = ctx;
97
+ const original = ctx.putImageData;
98
+ let batch = false;
99
+ ctx.putImageData = ((...args) => {
100
+ if (!batch) {
101
+ batch = true;
102
+ this.frames++;
103
+ queueMicrotask(() => { batch = false; });
104
+ }
105
+ return original.apply(ctx, args);
106
+ });
107
+ }
108
+ tick() {
109
+ const now = performance.now();
110
+ const bytes = this.info.bytes() ?? { bytesIn: 0, bytesOut: 0 };
111
+ const current = { at: now, bytesIn: bytes.bytesIn, bytesOut: bytes.bytesOut, frames: this.frames };
112
+ const parts = [];
113
+ if (this.last && now > this.last.at) {
114
+ const seconds = (now - this.last.at) / 1000;
115
+ parts.push(`↓ ${rate((current.bytesIn - this.last.bytesIn) / seconds)}`, `↑ ${rate((current.bytesOut - this.last.bytesOut) / seconds)}`, `${Math.round((current.frames - this.last.frames) / seconds)} fps`);
116
+ }
117
+ else {
118
+ parts.push('↓ …', '↑ …', '… fps');
119
+ }
120
+ if (this.info.ping) {
121
+ parts.push(this.latency === null ? 'ping …' : `ping ${this.latency < 10 ? this.latency.toFixed(1) : Math.round(this.latency)} ms`);
122
+ if (this.ticks++ % PING_EVERY_TICKS === 0) {
123
+ this.measureLatency();
124
+ }
125
+ }
126
+ this.last = current;
127
+ this.numbersEl.textContent = parts.join(' ');
128
+ this.pathEl.textContent = this.info.path();
129
+ }
130
+ measureLatency() {
131
+ // Nothing to show while the window is hidden; and never two at once on a slow link.
132
+ if (this.pinging || document.hidden) {
133
+ return;
134
+ }
135
+ this.pinging = true;
136
+ this.info.ping().then(ms => { this.latency = ms; }, () => { this.latency = null; }).finally(() => { this.pinging = false; });
137
+ }
138
+ }
139
+ exports.ConnectionStatus = ConnectionStatus;
140
+ //# sourceMappingURL=connectionStatus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connectionStatus.js","sourceRoot":"","sources":["../src/connectionStatus.ts"],"names":[],"mappings":"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"}
package/dist/desk.js CHANGED
@@ -15,19 +15,24 @@ const tabby_core_1 = require("tabby-core");
15
15
  const tabby_terminal_1 = require("tabby-terminal");
16
16
  const desktop_service_1 = require("./desktop.service");
17
17
  const targets_1 = require("./targets");
18
- const PREFIX = Buffer.from('\x1b]7777;desk;');
18
+ const probe_1 = require("./probe");
19
+ const PREFIX = Buffer.from('\x1b]7777;');
19
20
  const BEL = 0x07;
20
21
  const ST = Buffer.from('\x1b\\');
21
22
  function parse(payload) {
22
- const [session, socket, cwd, kind] = payload.split(';').map(f => Buffer.from(f ?? '', 'base64').toString('utf8'));
23
- // Older `desk` scripts sent no kind: they only knew tmux.
24
- return { session: session ?? '', socket: socket ?? '', cwd: cwd ?? '', kind: kind ?? (session ? 'tmux' : '') };
23
+ const [session, socket, cwd, kind, machine, hostname] = payload.split(';').map(f => Buffer.from(f ?? '', 'base64').toString('utf8'));
24
+ // Older `desk` scripts sent no kind (they only knew tmux), and no machine.
25
+ return {
26
+ session: session ?? '', socket: socket ?? '', cwd: cwd ?? '', kind: kind ?? (session ? 'tmux' : ''),
27
+ machine: machine?.trim() ?? '', hostname: hostname?.trim() ?? '',
28
+ };
25
29
  }
26
- /** Strips `desk` requests out of a session's output and reports them. */
30
+ /** Strips `desk` requests (and probes, see probe.ts) out of a session's output and reports them. */
27
31
  class DeskTrigger extends tabby_terminal_1.SessionMiddleware {
28
- constructor(onDesk) {
32
+ constructor(onDesk, onProbe) {
29
33
  super();
30
34
  this.onDesk = onDesk;
35
+ this.onProbe = onProbe;
31
36
  }
32
37
  feedFromSession(data) {
33
38
  // Tabby's OSC processor runs first and passes unknown OSCs through whole, so each request
@@ -41,9 +46,15 @@ class DeskTrigger extends tabby_terminal_1.SessionMiddleware {
41
46
  if (end === -1) {
42
47
  break;
43
48
  }
44
- const request = parse(data.subarray(body, end).toString('utf8'));
49
+ const payload = data.subarray(body, end).toString('utf8');
45
50
  data = Buffer.concat([data.subarray(0, start), data.subarray(end + (end === st ? ST.length : 1))]);
46
- setTimeout(() => this.onDesk(request));
51
+ if (payload.startsWith('desk;')) {
52
+ const request = parse(payload.slice('desk;'.length));
53
+ setTimeout(() => this.onDesk(request));
54
+ }
55
+ else if (payload.startsWith('probe;')) {
56
+ this.onProbe(payload.slice('probe;'.length));
57
+ }
47
58
  start = data.indexOf(PREFIX, start);
48
59
  }
49
60
  if (data.length) {
@@ -68,11 +79,11 @@ let DeskTriggerDecorator = class DeskTriggerDecorator extends tabby_terminal_1.T
68
79
  }
69
80
  if (!this.desktop.settings().desk) {
70
81
  // A hook left from when it was on; it goes away the next time a desktop opens there.
71
- this.notifications.info('`desk` is off: turn it on in Remote desktop settings');
82
+ this.notifications.info('`desk` is off: turn it on in the menu under Remote Desktop › Settings');
72
83
  return;
73
84
  }
74
85
  this.desktop.openConsole(pane, request);
75
- })));
86
+ }), id => (0, probe_1.probeArrived)(id, terminal)));
76
87
  };
77
88
  install();
78
89
  this.subscribeUntilDetached(terminal, terminal.sessionChanged$.subscribe(() => install()));
package/dist/desk.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"desk.js","sourceRoot":"","sources":["../src/desk.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"desk.js","sourceRoot":"","sources":["../src/desk.ts"],"names":[],"mappings":"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"}