Linux Mint Cinnamon control layer for RustDesk sessions over Tailscale — optimised for Apple devices.
Remote Studio manages headless Xorg display modes, device-specific scaling profiles, a Cinnamon panel applet, and low-latency RustDesk display defaults — so your Linux machine looks right when you connect from a MacBook, iPad, or iPhone.
- Features
- Requirements
- Installation
- Device Profiles
- Usage
- Status Contracts
- Architecture
- Configuration
- Contributing
- License
- One-command profiles —
res mac,res ipad,res iphoneletc. set the right resolution, HiDPI scaling, text scale, and cursor size in a single call - Headless Xorg — generates
/etc/X11/xorg.confwith dummy/NVIDIA driver config so custom resolutions survive reboots - Session lifecycle —
res session start/res session stopapply a profile before the client connects and restore defaults when they leave - RustDesk presets —
quality,balanced,speedTOML presets merged at runtime without touching your identity or password files - Tailscale integration —
res tailnetshows your address, peer health, and direct vs relayed path detection - Interactive TUI — full whiptail dashboard when run without arguments; plain CLI when called with an argument
- Cinnamon panel applet — live connection indicator (● Direct / ◐ Relayed), user count, warnings, and a GUI menu
- Automatic watch loop —
res watch(or the included systemd user unit) detects new RustDesk connections and applies a profile automatically - Doctor & automation status —
res doctorchecks symlinks, Xorg, RustDesk, Tailscale, renderer, and more;res status --jsongives scripts a stable machine-readable snapshot - Debian package — pre-built
.debattached to every GitHub release; build your own withmake deb
| Dependency | Notes |
|---|---|
| Linux Mint 21.x+ (Cinnamon) | Other Debian/Ubuntu-based distros may work |
bash ≥ 5, xrandr, whiptail |
Pre-installed on Linux Mint |
gsettings (part of glib2) |
Pre-installed on Cinnamon |
| RustDesk | Installed and running as a service |
| Tailscale | Authenticated on your tailnet |
Optional but recommended: an HDMI dummy plug in the GPU's HDMI port to activate hardware rendering on headless machines (see GPU Setup in REMOTE_STUDIO.md).
curl -fsSL https://raw.githubusercontent.com/NicoMancinelli/remote-studio/master/install-remote-studio.sh | bashgit clone https://github.com/NicoMancinelli/remote-studio.git ~/remote-studio
cd ~/remote-studio
./install.sh install # symlinks res, applet, and login restore
./install.sh system # writes /etc/X11/xorg.conf (requires sudo)Download the pre-built .deb from the latest release, or build it yourself:
make deb # requires dpkg-deb (Linux only)
sudo dpkg -i dist/remote-studio_*.debres doctor # all checks should show OK
res mac # apply your first profileThen add the remote-studio@neek applet to your Cinnamon panel.
See INSTALL.md for the full checklist.
Built-in profiles are defined in config/profiles.conf:
| Command | Device | Resolution | Scaling | Text Scale |
|---|---|---|---|---|
res mac |
MacBook Air 13" | 2560×1664 | 1× | 1.5 |
res mac-retina |
MacBook Air 13" Retina | 3024×1964 | 1.5× | 1.2 |
res mac15 |
MacBook Air 15" | 2880×1864 | 1× | 1.5 |
res mac15-retina |
MacBook Air 15" Retina | 3456×2234 | 1.5× | 1.2 |
res ipad |
iPad Pro 11" | 2424×1664 | 2× | 1.1 |
res ipad13 |
iPad Pro 13" | 2064×2752 | 2× | 1.1 |
res iphonel |
iPhone Landscape | 2868×1320 | 2× | 1.2 |
res iphonep |
iPhone Portrait | 1320×2868 | 2× | 1.2 |
res fallback |
Fallback 1920×1200 | 1920×1200 | 1× | 1.1 |
Add your own in ~/.config/remote-studio/profiles.conf using the same key=Label|width|height|scale|text_scale|cursor format, or interactively via res custom <width> <height>.
# Profiles
res mac # Apply MacBook Air 13" profile
res mac-retina # Apply MacBook Air 13" Retina profile
res ipad # Apply iPad Pro 11" profile
res custom 1920 1080 # Apply arbitrary resolution (prompts to save)
# Session lifecycle
res session start mac # Apply profile + prep for incoming connection
res session stop # Restore pre-session state
# RustDesk
res rustdesk apply quality # Merge quality TOML preset
res rustdesk apply balanced # Merge balanced TOML preset
res rustdesk apply speed # Merge speed TOML preset
# Network
res tailnet # Show Tailscale IP + RustDesk direct address
res tailnet doctor # Check tailnet health
# Xorg
res xorg # Generate /etc/X11/xorg.conf from profiles
res xorg rollback # Restore previous xorg.conf
# Diagnostics
res doctor # Full system health check
res doctor-fix # Repair common local setup issues where possible
res self-test # Automated self-test (9 checks)
res status # Pipe-delimited stats (consumed by applet)
res status --json # Machine-readable status for automation/CI
res log [N] # Tail last N lines of the event log (default 20)
# Toggles
res speed # Toggle performance mode (strips wallpaper/animations)
res theme # Toggle OLED dark/light theme
res night # Toggle night shift (warm gamma)
res caf # Toggle caffeine (disable screen lock)
res privacy # Lock screen and blank monitor
res fix # Fix clipboard + audio + keyboard in one shot
res service # Restart RustDesk service
# Config
res config set KEY VALUE # Write a key to remote-studio.conf
res config get KEY # Read a key
res config show # Print effective config (defaults + overrides)
# Misc
res watch [interval] # Auto-apply profile on new RustDesk connections
res rotate [normal|left|right|inverted]
res profiles # List all profiles and source files
res update # Pull latest and re-run install
res version # Print version
res help # Full command referenceRun res with no arguments to open the whiptail dashboard:
┌─ Remote Studio v8.0 ──────────────────────────────────┐
│ Mode: MacBook Air 13 (2560x1664) | IP: 100.x.x.x │
├───────────────────────────────────────────────────────┤
│ profiles Display Profiles │
│ quick Quick Actions │
│ performance Session & Toggles │
│ diagnostics Diagnostics │
│ tailnet Tailscale Network │
│ system System & Tools │
│ dashboard Live Dashboard │
│ help Help │
└───────────────────────────────────────────────────────┘
Falls back to a plain numbered text menu when the terminal is too small for whiptail.
Remote Studio intentionally has two status outputs:
| Contract | Consumer | Format | Path / command |
|---|---|---|---|
| Applet status file | Cinnamon applet | Pipe-delimited single line | $XDG_RUNTIME_DIR/remote-studio/status, or /tmp/remote-studio-$UID/status when XDG_RUNTIME_DIR is unavailable |
| Automation status | Tests, scripts, CI | JSON object | res status --json |
res status writes the applet file and prints the same pipe-delimited line. The applet file uses none for the codec field when no codec is known so the field count stays stable after trimming. res status --json also refreshes that file, then prints JSON with these stable top-level fields: mode, temperature, latency, users, ram, warnings, network, ip, connection, resolution, direct_address, codec, and status_file.
The applet should continue to read the file contract. New scripts should use JSON and, when debugging stale applet state, inspect the status_file value reported by res status --json.
remote-studio/
├── res.sh # Entrypoint — CLI dispatch + TUI main loop
├── lib/
│ ├── core.sh # Colours, logging, caching, profile helpers
│ ├── engine.sh # apply_all, apply_profile, session, xorg
│ ├── diagnostics.sh # doctor, self-test, info, status, log
│ ├── services.sh # tailnet, rustdesk config merge
│ ├── config.sh # res config, init wizard, help, update
│ └── tui.sh # All whiptail TUI panels and menus
├── applet/
│ ├── applet.js # Cinnamon panel applet (GJS)
│ └── metadata.json
├── config/
│ ├── profiles.conf # Built-in device profiles
│ ├── remote-studio.conf.example # Local config defaults copied by install.sh
│ ├── xorg.conf # Headless Xorg dummy config template
│ ├── RustDesk_default.toml # Default RustDesk display settings
│ ├── RustDesk_balanced.toml
│ ├── RustDesk_quality.toml
│ ├── RustDesk_speed.toml
│ ├── logrotate.d/remote-studio
│ └── remote-studio-watch.service # Systemd user unit for watch loop
├── tests/
│ ├── test_profiles.bats
│ ├── test_config.bats
│ └── test_log.bats
├── package/
│ └── build-deb.sh
├── install.sh # Symlink + system installer
├── install-remote-studio.sh # curl-pipe-bash one-liner
├── Makefile
└── docs/
├── quickstart.md # Maintained new-machine quick start
└── quick-start.md # Compatibility pointer to quickstart.md
res.sh sources the lib/ modules at startup, resolving LIB_DIR from the repo's own lib/ directory (development) or /usr/share/remote-studio/lib (.deb install). The applet reads $XDG_RUNTIME_DIR/remote-studio/status, falling back to /tmp/remote-studio-$UID/status, and uses Gio.FileMonitor to react instantly to changes. The file remains pipe-delimited for the applet; res status --json is the automation contract.
User overrides live in ~/.config/remote-studio/:
| File | Purpose |
|---|---|
remote-studio.conf |
Key-value config copied from config/remote-studio.conf.example on first install (DEFAULT_PROFILE, DEFAULT_RUSTDESK_PRESET, XORG_DRIVER, etc.) |
profiles.conf |
Custom device profiles (appended to built-ins) |
session.state |
Active session snapshot (managed by res session) |
recent_profiles |
Last 5 used profiles (shown at top of TUI profiles menu) |
res config set DEFAULT_PROFILE mac # Set default profile
res config set DEFAULT_SESSION_PROFILE mac15 # Set session default
res config set AUTO_SESSION true # Auto-apply profile on connection
res config show # View full effective configRuntime state in $HOME:
| File | Purpose |
|---|---|
~/.res_state |
Last applied mode (read by applet and login restore) |
~/.remote_studio.log |
Event log (auto-rotated at 1 MB; logrotate weekly) |
~/.wallpaper_backup |
Saved wallpaper URI when speed mode is active |
Bug reports and pull requests are welcome. See CONTRIBUTING.md for development setup, code style, and the PR process.
Run the test suite locally:
make test # shellcheck + bats
make ci # test + syntax, applet, status JSON, installer dry-run
make release-check # CI plus system dry-run and .deb buildSee RELEASING.md for the release workflow. Security issues — please read SECURITY.md first.
MIT © 2026 Nico Mancinelli. See LICENSE.