refactor: clean VPS code, keep only host solution in main\n
- Removed all VPS-specific scripts and configs from main branch\n- VPS solution archived in archive/vps-rustdesk-server\n- Rewrote README.md to focus on Intel Alder Lake-N host setup\n- Added note about archive branch and host-only scope\n Fixes #6 (wontfix for VPS GUI, host solution restored)
This commit is contained in:
312
README.md
312
README.md
@@ -1,139 +1,93 @@
|
|||||||
# RDtop — RustDesk Headless Display Setup
|
# RDtop — RustDesk Headless Display Setup for Intel iGPU
|
||||||
|
|
||||||
**Автоматический виртуальный дисплей для RustDesk без подключенного HDMI монитора. Работает на любой Linux машине (Intel iGPU, AMD, NVIDIA, CPU-only VPS).**
|
**Виртуальный дисплей для RustDesk без подключенного HDMI на Intel Alder Lake-N.**
|
||||||
|
|
||||||
---
|
Ветка `archive/vps-rustdesk-server` содержит архивное решение для VPS (Hbbr/Hbbs relay). Main теперь только для хоста.
|
||||||
|
|
||||||
## Содержание
|
|
||||||
|
|
||||||
1. [Проблема](#проблема)
|
|
||||||
2. [Решение](#решение)
|
|
||||||
3. [Файлы проекта](#файлы-проекта)
|
|
||||||
4. [Установка](#установка)
|
|
||||||
5. [Как это работает](#как-это-работает)
|
|
||||||
6. [Проверка](#проверка)
|
|
||||||
7. [Troubleshooting](#troubleshooting)
|
|
||||||
8. [Откат](#откат)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Проблема
|
## Проблема
|
||||||
|
|
||||||
Оборудование: **Intel Alder Lake-N (i915)**, Ubuntu 24.04, X11 (GDM/GNOME).
|
Оборудование: **Intel Alder Lake-N (i915)**, Debian 12, X11 (GDM/GNOME).
|
||||||
|
|
||||||
### Что происходит
|
| Сценарий | Результат |
|
||||||
|
|----------|-----------|
|
||||||
1. При подключенном HDMI — Xorg инициализирует `HDMI-1` на отдельный CRTC.
|
| HDMI подключен | Xorg инициализирует `HDMI-1`, RustDesk работает |
|
||||||
2. Когда кабель отключают — **CRTC уничтожается**.
|
| HDMI отключен при загрузке | CRTC уничтожается, Xorg не стартует, RustDesk не видит экран |
|
||||||
3. У Xorg не остается активного выхода. Может остаться черный экран или fallback 8×8.
|
| `Option "VirtualHeads" "1"` (intel driver) | **Работает только если HDMI был при загрузке.** Без HDMI — VIRTUAL1 не создается |
|
||||||
4. RustDesk все еще подключен, но захватывает `Screen 0` — и видит либо черный экран, либо 8×8 пикселей.
|
|
||||||
|
|
||||||
### Почему стандартные способы не помогли
|
|
||||||
|
|
||||||
| Способ | Результат |
|
|
||||||
|--------|-----------|
|
|
||||||
| `Option "VirtualHeads" "1"` (intel driver) | **Работает только с кабелем на момент загрузки.** Без HDMI — VIRTUAL1 не создается. |
|
|
||||||
| modesetting driver | Создает VIRTUAL1 через randr, но после reboot пропадает. |
|
|
||||||
| `NoOutputInitialSize` | Требует пересборки xorg или патчей. |
|
|
||||||
| Dummy plug (аппаратный) | Работает, но нужно покупать. |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Решение
|
## Решение: Dummy Driver + Kernel EDID Fallback
|
||||||
|
|
||||||
### Хост: Dummy driver + kernel EDID fallback
|
Dummy driver создает `DUMMY0` сразу при старте Xorg, **независимо от HDMI**.
|
||||||
|
|
||||||
Работает **без кабеля HDMI на момент загрузки** — dummy driver не зависит от физических выходов.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
+------------------------------------------------------------------------+
|
+------------------------------------------------------------------------+
|
||||||
| Xorg Dummy Driver (1920×1080) |
|
| Xorg Dummy Driver (1920×1080) |
|
||||||
| +-------------------------+ |
|
| +-------------------------+ |
|
||||||
| | DUMMY0 = primary | ← RustDesk --server захватывает |
|
| | DUMMY0 = primary | ← RustDesk захватывает этот экран |
|
||||||
| | 1920×1080 @ 60Hz | |
|
| | 1920×1080 @ 60Hz | |
|
||||||
| +-------------------------+ |
|
| +-------------------------+ |
|
||||||
| |
|
| |
|
||||||
| При подключении HDMI кабеля: |
|
| При подключении HDMI: hdmi-fallback.sh клонирует HDMI1 на DUMMY0 |
|
||||||
| скрипт hdmi-fallback.sh клонирует HDMI1 на DUMMY0 |
|
| При отключении HDMI: HDMI1 off, DUMMY0 остается primary |
|
||||||
| При отключении: HDMI1 off, DUMMY0 остается primary |
|
|
||||||
+------------------------------------------------------------------------+
|
+------------------------------------------------------------------------+
|
||||||
```
|
```
|
||||||
|
|
||||||
### Kernel EDID fallback (дополнительно)
|
### Kernel EDID Fallback (страховка)
|
||||||
|
|
||||||
Параметры ядра:
|
Если dummy по какой-то причине не загрузится, ядро `i915` создаст виртуальный framebuffer через EDID:
|
||||||
```
|
|
||||||
|
```bash
|
||||||
video=HDMI-A-1:1920x1080@60 drm.edid_firmware=HDMI-A-1:edid/samsung.bin
|
video=HDMI-A-1:1920x1080@60 drm.edid_firmware=HDMI-A-1:edid/samsung.bin
|
||||||
```
|
```
|
||||||
|
|
||||||
Это заставляет ядро `i915` инициализировать HDMI-A-1 с сохраненным EDID даже без кабеля. Если dummy driver не сработает (редкий случай), ядро все равно создаст виртуальный framebuffer.
|
---
|
||||||
|
|
||||||
### VPS (CPU-only): Dummy driver + Xorg + RustDesk
|
## Файлы
|
||||||
|
|
||||||
Тот же dummy driver, но без GPU. Работает на любом VPS.
|
| Файл репозитория | Назначение | Установка |
|
||||||
|
|------------------|------------|-----------|
|
||||||
```
|
| `bin/hdmi-fallback.sh` | Мониторинг HDMI-A-1 и auto-fallback | `~/.local/bin/hdmi-fallback.sh` |
|
||||||
+------------------------------------------------------------------------+
|
| `config/x11-host/20-dummy-headless.conf` | Dummy driver Xorg config | `/etc/X11/xorg.conf.d/20-dummy-headless.conf` |
|
||||||
| Xorg Dummy Driver (1920×1080) |
|
| `config/systemd/hdmi-fallback.service` | systemd user unit | `~/.config/systemd/user/hdmi-fallback.service` |
|
||||||
| +-------------------------+ |
|
| `scripts/host/setup-host-rustdesk.sh` | Полная настройка RustDesk ID на хосте | Запустить от root |
|
||||||
| | DUMMY0 = primary | ← RustDesk --server захватывает |
|
| `install.sh` | One-liner установщик | `bash install.sh` |
|
||||||
| | 1920×1080 @ 60Hz | |
|
|
||||||
| +-------------------------+ |
|
|
||||||
| Нет GPU, нет DRM — работает на любом VPS |
|
|
||||||
+------------------------------------------------------------------------+
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Файлы проекта
|
## Установка (Хост)
|
||||||
|
|
||||||
| Файл репозитория | Назначение | Системный путь |
|
### Автоматическая
|
||||||
|------------------|------------|----------------|
|
|
||||||
| `bin/hdmi-fallback.sh` | Мониторинг HDMI-A-1 и авто-fallback на DUMMY0/VIRTUAL1 | `~/.local/bin/hdmi-fallback.sh` |
|
|
||||||
| `bin/vps-start-xorg.sh` | Стартер Xorg с dummy driver на VPS | `/usr/local/bin/start-xorg-dummy.sh` |
|
|
||||||
| `config/x11-host/20-dummy-headless.conf` | Dummy driver headless config | `/etc/X11/xorg.conf.d/20-dummy-headless.conf` |
|
|
||||||
| `config/x11-host/20-intel-virtual.conf.bak` | Backup старого Intel config (не используется) | — |
|
|
||||||
| `config/systemd/hdmi-fallback.service` | systemd user unit (host) | `~/.config/systemd/user/hdmi-fallback.service` |
|
|
||||||
| `config/vps/xorg-dummy.service` | systemd system unit (VPS Xorg) | `/etc/systemd/system/xorg-dummy.service` |
|
|
||||||
| `config/vps/rustdesk-dummy.service` | systemd system unit (VPS RustDesk) | `/etc/systemd/system/rustdesk-dummy.service` |
|
|
||||||
| `install.sh` | Установщик для хоста | (запускается один раз) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Установка
|
|
||||||
|
|
||||||
### Хост (Desktop с GPU)
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash -c "$(curl -fsSL https://git.softuniq.eu/NW/RDtop/raw/branch/main/install.sh)"
|
bash -c "$(curl -fsSL https://git.softuniq.eu/NW/RDtop/raw/branch/main/install.sh)"
|
||||||
```
|
|
||||||
|
|
||||||
После установки **перезагрузить компьютер**:
|
|
||||||
```bash
|
|
||||||
sudo reboot
|
sudo reboot
|
||||||
```
|
```
|
||||||
|
|
||||||
**Вручную:**
|
### Вручную
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Клонируем
|
# 1. Клонировать
|
||||||
git clone https://git.softuniq.eu/NW/RDtop.git ~/RDtop && cd ~/RDtop
|
git clone https://git.softuniq.eu/NW/RDtop.git ~/RDtop && cd ~/RDtop
|
||||||
|
|
||||||
# 2. Dummy driver config (главный способ)
|
# 2. Dummy driver config
|
||||||
sudo cp config/x11-host/20-dummy-headless.conf /etc/X11/xorg.conf.d/
|
sudo cp config/x11-host/20-dummy-headless.conf /etc/X11/xorg.conf.d/
|
||||||
|
|
||||||
# 3. Удалить старые конфиги если были
|
# 3. Удалить старые конфиги
|
||||||
sudo rm -f /etc/X11/xorg.conf.d/20-intel-virtual.conf
|
sudo rm -f /etc/X11/xorg.conf.d/20-intel-virtual.conf
|
||||||
sudo rm -f /etc/X11/xorg.conf.d/90-fallback.conf
|
sudo rm -f /etc/X11/xorg.conf.d/90-fallback.conf
|
||||||
|
|
||||||
# 4. Сохранить EDID для kernel fallback (опционально)
|
# 4. EDID fallback (опционально)
|
||||||
sudo mkdir -p /lib/firmware/edid
|
sudo mkdir -p /lib/firmware/edid
|
||||||
sudo cp /sys/class/drm/card0-HDMI-A-1/edid /lib/firmware/edid/samsung.bin
|
sudo cp /sys/class/drm/card0-HDMI-A-1/edid /lib/firmware/edid/samsung.bin
|
||||||
|
|
||||||
# 5. Обновить GRUB для kernel EDID fallback
|
# 5. GRUB
|
||||||
sudo sed -i 's|GRUB_CMDLINE_LINUX_DEFAULT=".*"|GRUB_CMDLINE_LINUX_DEFAULT="quiet splash video=HDMI-A-1:1920x1080@60 drm.edid_firmware=HDMI-A-1:edid/samsung.bin"|' /etc/default/grub
|
sudo sed -i 's|GRUB_CMDLINE_LINUX_DEFAULT=".*"|GRUB_CMDLINE_LINUX_DEFAULT="quiet splash video=HDMI-A-1:1920x1080@60 drm.edid_firmware=HDMI-A-1:edid/samsung.bin"|' /etc/default/grub
|
||||||
sudo update-grub
|
sudo update-grub
|
||||||
|
|
||||||
# 6. Скрипт мониторинга
|
# 6. Fallback скрипт
|
||||||
chmod +x bin/hdmi-fallback.sh
|
chmod +x bin/hdmi-fallback.sh
|
||||||
cp bin/hdmi-fallback.sh ~/.local/bin/
|
cp bin/hdmi-fallback.sh ~/.local/bin/
|
||||||
|
|
||||||
@@ -143,141 +97,45 @@ cp config/systemd/hdmi-fallback.service ~/.config/systemd/user/
|
|||||||
systemctl --user daemon-reload
|
systemctl --user daemon-reload
|
||||||
systemctl --user enable --now hdmi-fallback.service
|
systemctl --user enable --now hdmi-fallback.service
|
||||||
|
|
||||||
# 8. Перезагрузка
|
# 8. RustDesk ID fix (защита ID от смены после перезагрузки)
|
||||||
|
sudo bash scripts/host/setup-host-rustdesk.sh
|
||||||
|
|
||||||
|
# 9. Перезагрузка
|
||||||
sudo reboot
|
sudo reboot
|
||||||
```
|
```
|
||||||
|
|
||||||
### Вариант Б: CPU-only VPS
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Установить Xorg + dummy
|
|
||||||
apt-get update
|
|
||||||
apt-get install -y xserver-xorg xserver-xorg-video-dummy rustdesk
|
|
||||||
|
|
||||||
# 2. Копировать конфиги
|
|
||||||
git clone https://git.softuniq.eu/NW/RDtop.git /tmp/rdtop
|
|
||||||
cd /tmp/rdtop
|
|
||||||
sudo cp config/vps/20-dummy-headless.conf /etc/X11/xorg.conf.d/
|
|
||||||
sudo cp config/vps/xorg-dummy.service /etc/systemd/system/
|
|
||||||
sudo cp config/vps/rustdesk-dummy.service /etc/systemd/system/
|
|
||||||
sudo cp bin/vps-start-xorg.sh /usr/local/bin/start-xorg-dummy.sh
|
|
||||||
chmod +x /usr/local/bin/start-xorg-dummy.sh
|
|
||||||
|
|
||||||
# 3. Остановить конфликтующий gdm
|
|
||||||
systemctl stop gdm.service 2>/dev/null || true
|
|
||||||
systemctl disable gdm.service 2>/dev/null || true
|
|
||||||
|
|
||||||
# 4. Запустить
|
|
||||||
systemctl daemon-reload
|
|
||||||
systemctl enable --now xorg-dummy.service
|
|
||||||
systemctl enable --now rustdesk-dummy.service
|
|
||||||
|
|
||||||
# 5. Проверить
|
|
||||||
export DISPLAY=:0
|
|
||||||
xrandr --listmonitors
|
|
||||||
# Должно показать: DUMMY0 primary 1920x1080
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Как это работает
|
|
||||||
|
|
||||||
### Хост (Intel iGPU)
|
|
||||||
|
|
||||||
1. **Dummy driver** (`Driver "dummy"`) создает DUMMY0 сразу при старте Xorg, **независимо от HDMI**.
|
|
||||||
2. **Kernel EDID fallback** (`drm.edid_firmware`) — если dummy каким-то образом не сработает, ядро `i915` инициализирует HDMI-A-1 с сохраненным EDID.
|
|
||||||
3. **Скрипт** `hdmi-fallback.sh` при старте:
|
|
||||||
- Всегда активирует DUMMY0 как primary
|
|
||||||
- Если HDMI connected — делает HDMI1 clone (`--same-as`)
|
|
||||||
- Если HDMI disconnected — выключает HDMI1, DUMMY0 остается primary
|
|
||||||
4. **RustDesk** захватывает DUMMY0 (primary).
|
|
||||||
|
|
||||||
### VPS (CPU-only)
|
|
||||||
|
|
||||||
**Архитектура:** Xvfb :99 + xfce4-session + xfwm4 + xfce4-panel + RustDesk client
|
|
||||||
|
|
||||||
1. **Xvfb** — виртуальный framebuffer на `:99` 1920×1080x24, доступен для захвата X11
|
|
||||||
2. **xfce4-session** — полноценный XFCE рабочий стол (окна, панель, иконки)
|
|
||||||
3. **RustDesk** — клиент подключён к публичному relay `rs-ny.rustdesk.com:21116`
|
|
||||||
4. **Вход** — ID из 9 цифр + пароль
|
|
||||||
|
|
||||||
```
|
|
||||||
+------------------------------------------------------------------------+
|
|
||||||
| Xvfb :99 (1920x1080x24) |
|
|
||||||
| +-------------------------------------------------------+ |
|
|
||||||
| | XFCE Desktop (xfwm4 + xfce4-panel + xfdesktop) | |
|
|
||||||
| | +---------------------------------------------+ | |
|
|
||||||
| | | [Панель XFCE] [Иконки] [Контекстное меню]| | |
|
|
||||||
| | +---------------------------------------------+ | |
|
|
||||||
| | ← RustDesk client захватывает экран через X11 | |
|
|
||||||
| +-------------------------------------------------------+ |
|
|
||||||
| SSH / tmux (персистентность) |
|
|
||||||
+------------------------------------------------------------------------+
|
|
||||||
```
|
|
||||||
|
|
||||||
**Персистентность:** `tmux` (не systemd) — `pkill` в ExecStartPre убивал Xvfb и xfce.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Проверка
|
## Проверка
|
||||||
|
|
||||||
### Хост (после reboot без HDMI)
|
|
||||||
```bash
|
```bash
|
||||||
# Должно показать DUMMY0 как primary
|
# После reboot без HDMI
|
||||||
xrandr --listmonitors
|
$ xrandr --listmonitors
|
||||||
# Ожидаемый вывод:
|
0: +*DUMMY0 1920/508x1080/286+0+0 DUMMY0 [PRIMARY]
|
||||||
# 0: +*DUMMY0 1920/508x1080/286+0+0 DUMMY0 [PRIMARY]
|
|
||||||
|
|
||||||
# Если HDMI подключен:
|
$ rustdesk --get-id
|
||||||
# 1: +HDMI-1 1920/508x1080/286+0+0 HDMI-1 [CLONE]
|
158 356 564 # или ваш текущий ID — он не должен меняться
|
||||||
```
|
|
||||||
|
|
||||||
### Сервис
|
|
||||||
```bash
|
|
||||||
systemctl --user status hdmi-fallback.service
|
|
||||||
```
|
|
||||||
|
|
||||||
### Логи
|
|
||||||
```bash
|
|
||||||
cat ~/.local/logs/drm-hotplug.log
|
|
||||||
```
|
|
||||||
|
|
||||||
### VPS
|
|
||||||
```bash
|
|
||||||
export DISPLAY=:0
|
|
||||||
xrandr --listmonitors
|
|
||||||
# Должно быть:
|
|
||||||
# 0: +*DUMMY0 1920x... DUMMY0 [PRIMARY]
|
|
||||||
|
|
||||||
# RustDesk
|
|
||||||
ps aux | grep rustdesk
|
|
||||||
# /usr/lib/rustdesk/rustdesk --server должен быть запущен
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### DUMMY0 не появляется после reboot
|
### ID меняется после перезагрузки
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Проверить dummy driver:
|
# Проверить защиту файла
|
||||||
grep "Loading.*dummy" /var/log/Xorg.0.log
|
lsattr ~/.config/rustdesk/RustDesk.toml
|
||||||
# Должно быть: (II) Loading /usr/lib/xorg/modules/drivers/dummy_drv.so
|
# Должно показать ---- i --------
|
||||||
|
|
||||||
# Проверить конфиг:
|
# Если нет — запустить setup-host-rustdesk.sh от root
|
||||||
ls /etc/X11/xorg.conf.d/20-dummy-headless.conf
|
sudo bash scripts/host/setup-host-rustdesk.sh
|
||||||
# Должен существовать
|
|
||||||
|
|
||||||
# Если intel driver загружается вместо dummy:
|
|
||||||
# Убедиться что нет других конфликтующих xorg.conf:
|
|
||||||
ls /etc/X11/xorg.conf 2>/dev/null || echo "OK: no xorg.conf"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### RustDesk черный экран
|
### Черный экран в RustDesk
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Проверить primary:
|
# Проверить primary
|
||||||
xrandr --verbose | grep primary
|
xrandr --verbose | grep primary
|
||||||
# Должно быть: DUMMY0 connected primary
|
# Должно быть: DUMMY0 connected primary
|
||||||
|
|
||||||
@@ -286,69 +144,19 @@ xrandr --output DUMMY0 --mode "1920x1080" --primary
|
|||||||
xrandr --output HDMI-1 --auto --same-as DUMMY0
|
xrandr --output HDMI-1 --auto --same-as DUMMY0
|
||||||
```
|
```
|
||||||
|
|
||||||
### VPS: RustDesk не видит DUMMY0
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Проверить что Xorg запущен:
|
|
||||||
ls /tmp/.X11-unix/X0
|
|
||||||
# Должен быть сокет
|
|
||||||
|
|
||||||
# Если нет — запустить вручную:
|
|
||||||
/usr/local/bin/start-xorg-dummy.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Откат
|
## Архив
|
||||||
|
|
||||||
|
Решение для VPS (Hbbr/Hbbs + Xvfb + XFCE) заархивировано:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Хост
|
git checkout archive/vps-rustdesk-server
|
||||||
sudo rm -f /etc/X11/xorg.conf.d/20-dummy-headless.conf
|
|
||||||
sudo rm -f /etc/X11/xorg.conf.d/20-intel-virtual.conf
|
|
||||||
sudo rm -f /etc/X11/xorg.conf.d/90-fallback.conf
|
|
||||||
systemctl --user disable --now hdmi-fallback.service
|
|
||||||
rm -f ~/.local/bin/hdmi-fallback.sh
|
|
||||||
|
|
||||||
# Вернуть стандартный GRUB:
|
|
||||||
sudo sed -i 's|GRUB_CMDLINE_LINUX_DEFAULT=".*"|GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"|' /etc/default/grub
|
|
||||||
sudo update-grub
|
|
||||||
|
|
||||||
# VPS
|
|
||||||
systemctl disable --now xorg-dummy.service
|
|
||||||
systemctl disable --now rustdesk-dummy.service
|
|
||||||
sudo rm -f /etc/systemd/system/xorg-dummy.service
|
|
||||||
sudo rm -f /etc/systemd/system/rustdesk-dummy.service
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Технические детали
|
|
||||||
|
|
||||||
### Почему dummy driver, а не intel с VirtualHeads?
|
|
||||||
|
|
||||||
`intel` driver + `VirtualHeads=1` работает **только если HDMI подключен при загрузке**. Без кабеля — ядро не создает connector/CRTC для HDMI-A-1, и `VirtualHeads` не помогает. Dummy driver не зависит от физических выходов.
|
|
||||||
|
|
||||||
### Почему kernel EDID fallback?
|
|
||||||
|
|
||||||
Параметры `video=HDMI-A-1:1920x1080@60` и `drm.edid_firmware=HDMI-A-1:edid/samsung.bin` заставляют ядро `i915` инициализировать connector с фиктивным EDID. Это "страховка": если dummy driver по какой-то причине не загрузится (например, обновление Xorg удалит его), ядро все равно создаст выход.
|
|
||||||
|
|
||||||
### Почему клон, а не extended desktop?
|
|
||||||
|
|
||||||
`--same-as` клонирует framebuffer. При disconnect HDMI clone просто перестает рендериться, но primary (DUMMY0) не затрагивается — Xorg не теряет framebuffer.
|
|
||||||
|
|
||||||
### Dummy driver на VPS
|
|
||||||
|
|
||||||
Не требует GPU, не использует DRM. Работает на любом Linux с xorg-server. RustDesk использует software capture через X11 API.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Авторы
|
## Авторы
|
||||||
|
|
||||||
- Deploy via AI Agent (Kilo Code Orchestrator)
|
- Intel Alder Lake-N (i915), Debian 12 / Ubuntu 24.04
|
||||||
- Target hardware: Intel Alder Lake-N (i915), Ubuntu 24.04
|
- Решение: AI Agent (Kilo Code Orchestrator)
|
||||||
- VPS: CPU-only, Ubuntu 24.04, Hetzner/DigitalOcean/Linode
|
|
||||||
|
|
||||||
## Ссылки
|
|
||||||
|
|
||||||
- Репозиторий: `https://git.softuniq.eu/NW/RDtop`
|
|
||||||
- Установщик: `bash -c "$(curl -fsSL https://git.softuniq.eu/NW/RDtop/raw/branch/main/install.sh)"`
|
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Start Xorg with dummy driver for headless VPS
|
|
||||||
export DISPLAY=:0
|
|
||||||
export XAUTHORITY=${XAUTHORITY:-/run/user/0/.Xauthority}
|
|
||||||
|
|
||||||
# Generate MIT magic cookie if needed
|
|
||||||
if [ ! -s "$XAUTHORITY" ]; then
|
|
||||||
mkdir -p "$(dirname "$XAUTHORITY")"
|
|
||||||
touch "$XAUTHORITY"
|
|
||||||
xauth -f "$XAUTHORITY" generate :0 MIT-MAGIC-COOKIE-1 2>/dev/null || \
|
|
||||||
xauth -f "$XAUTHORITY" add :0 MIT-MAGIC-COOKIE-1 "$(xxd -l 16 -p /dev/urandom)" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Ensure socket directory
|
|
||||||
mkdir -p /tmp/.X11-unix
|
|
||||||
|
|
||||||
# Check if already running
|
|
||||||
if pgrep -x "Xorg" >/dev/null 2>&1; then
|
|
||||||
echo "Xorg already running"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec /usr/lib/xorg/Xorg :0 \
|
|
||||||
-nolisten tcp \
|
|
||||||
-nolisten local \
|
|
||||||
-config /etc/X11/xorg.conf.d/99-dummy.conf \
|
|
||||||
+extension GLX \
|
|
||||||
+extension RANDR \
|
|
||||||
+extension RENDER \
|
|
||||||
-novtswitch \
|
|
||||||
-keeptty
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
Section "Device"
|
|
||||||
Identifier "DummyHeadless"
|
|
||||||
Driver "dummy"
|
|
||||||
Option "ConstantDPI" "true"
|
|
||||||
VideoRam 256000
|
|
||||||
EndSection
|
|
||||||
|
|
||||||
Section "Monitor"
|
|
||||||
Identifier "DummyMonitor"
|
|
||||||
HorizSync 28-80
|
|
||||||
VertRefresh 48-75
|
|
||||||
EndSection
|
|
||||||
|
|
||||||
Section "Screen"
|
|
||||||
Identifier "DummyScreen"
|
|
||||||
Device "DummyHeadless"
|
|
||||||
Monitor "DummyMonitor"
|
|
||||||
DefaultDepth 24
|
|
||||||
SubSection "Display"
|
|
||||||
Depth 24
|
|
||||||
Modes "1920x1080" "1280x720"
|
|
||||||
EndSubSection
|
|
||||||
EndSection
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=RustDesk with Dummy Display
|
|
||||||
After=xorg-dummy.service
|
|
||||||
Requires=xorg-dummy.service
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
User=root
|
|
||||||
Environment="DISPLAY=:0"
|
|
||||||
Environment="XAUTHORITY=/root/.Xauthority"
|
|
||||||
ExecStartPre=/bin/sleep 3
|
|
||||||
ExecStart=/usr/share/rustdesk/rustdesk --server
|
|
||||||
Restart=always
|
|
||||||
RestartSec=10
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# RustDesk VPS Headless Client Startup Script
|
|
||||||
# Starts Xvfb :99, waits for display, then launches RustDesk client via DBus
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Kill old Xvfb
|
|
||||||
pkill -9 Xvfb 2>/dev/null || true
|
|
||||||
sleep 1
|
|
||||||
|
|
||||||
# Start Xvfb
|
|
||||||
/usr/bin/Xvfb :99 -screen 0 1920x1080x24 -ac -nolisten tcp -nolisten unix &
|
|
||||||
XVFB_PID=$!
|
|
||||||
sleep 2
|
|
||||||
|
|
||||||
# Wait for display to be ready
|
|
||||||
for i in $(seq 1 30); do
|
|
||||||
if xdpyinfo -display :99 > /dev/null 2>&1; then
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
|
|
||||||
# Verify display is up
|
|
||||||
if ! xdpyinfo -display :99 > /dev/null 2>&1; then
|
|
||||||
echo "ERROR: Xvfb failed to start on :99"
|
|
||||||
kill $XVFB_PID 2>/dev/null || true
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
export DISPLAY=:99
|
|
||||||
|
|
||||||
# Start RustDesk client inside DBus session (required for headless GUI apps)
|
|
||||||
exec /usr/bin/dbus-run-session -- /usr/share/rustdesk/rustdesk --server
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=RustDesk + XFCE Desktop on VPS
|
|
||||||
After=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=forking
|
|
||||||
User=root
|
|
||||||
Environment="DISPLAY=:99"
|
|
||||||
Environment="XAUTHORITY=/root/.Xauthority"
|
|
||||||
|
|
||||||
# Start Xvfb, wait for display, then launch XFCE + RustDesk
|
|
||||||
ExecStartPre=/bin/bash -c 'pkill -9 -f rustdesk 2>/dev/null || true; pkill -9 -f xfce4-session 2>/dev/null || true; sleep 1; pkill -9 Xvfb 2>/dev/null || true; rm -f /tmp/.X11-unix/X99; sleep 1'
|
|
||||||
ExecStart=/bin/bash -c 'Xvfb :99 -screen 0 1920x1080x24 -ac +extension RANDR > /dev/null 2>&1 &; sleep 3; for i in $(seq 1 30); do export DISPLAY=:99; export XAUTHORITY=/root/.Xauthority; xrandr --listmonitors > /dev/null 2>&1 && break; sleep 1; done; nohup dbus-run-session -- xfce4-session > /tmp/xfce-session.log 2>&1 &; sleep 5; nohup dbus-run-session -- /usr/share/rustdesk/rustdesk --server > /tmp/rustdesk-session.log 2>&1 &; sleep 2; exit 0'
|
|
||||||
ExecStop=/bin/bash -c 'pkill -9 -f rustdesk; pkill -9 -f xfce4-session; pkill -9 Xvfb; rm -rf /tmp/RustDesk'
|
|
||||||
Restart=always
|
|
||||||
RestartSec=10
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# RustDesk + XFCE Desktop Auto-Start for VPS
|
|
||||||
# Systemd runs this script; script waits for Xvfb to exit
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Kill old
|
|
||||||
pkill -9 -f rustdesk 2>/dev/null || true
|
|
||||||
pkill -9 -f xfce4-session 2>/dev/null || true
|
|
||||||
pkill -9 Xvfb 2>/dev/null || true
|
|
||||||
rm -f /tmp/.X11-unix/X99 /tmp/.X*-lock
|
|
||||||
sleep 2
|
|
||||||
|
|
||||||
# Start Xvfb :99
|
|
||||||
Xvfb :99 -screen 0 1920x1080x24 -ac +extension RANDR +extension RENDER +extension GLX > /dev/null 2>&1 &
|
|
||||||
XVFB_PID=$!
|
|
||||||
sleep 3
|
|
||||||
|
|
||||||
# Wait for display ready
|
|
||||||
export DISPLAY=:99
|
|
||||||
export XAUTHORITY=/root/.Xauthority
|
|
||||||
for i in $(seq 1 30); do
|
|
||||||
xrandr --listmonitors > /dev/null 2>&1 && break
|
|
||||||
if ! kill -0 $XVFB_PID 2>/dev/null; then
|
|
||||||
echo "ERROR: Xvfb died"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
|
|
||||||
xrandr --listmonitors > /dev/null 2>&1 || { echo "ERROR: Display :99 not ready"; exit 1; }
|
|
||||||
|
|
||||||
# Start XFCE desktop in background
|
|
||||||
nohup dbus-run-session -- xfce4-session > /tmp/xfce-session.log 2>&1 &
|
|
||||||
echo "XFCE PID: $!"
|
|
||||||
sleep 5
|
|
||||||
|
|
||||||
# Wait for window manager
|
|
||||||
for i in $(seq 1 20); do
|
|
||||||
ps aux | grep xfwm4 | grep -v grep > /dev/null 2>&1 && break
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
|
|
||||||
# Start RustDesk client in background
|
|
||||||
nohup dbus-run-session -- /usr/share/rustdesk/rustdesk --server > /tmp/rustdesk-session.log 2>&1 &
|
|
||||||
echo "RustDesk PID: $!"
|
|
||||||
sleep 3
|
|
||||||
|
|
||||||
# Wait for Xvfb (main process) - systemd tracks this script
|
|
||||||
# If Xvfb dies, systemd will restart the service
|
|
||||||
wait $XVFB_PID
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# RustDesk + XFCE Desktop Auto-Start for VPS
|
|
||||||
# Systemd runs this script; script waits for Xvfb to exit
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Kill old
|
|
||||||
pkill -9 -f rustdesk 2>/dev/null || true
|
|
||||||
pkill -9 -f xfce4-session 2>/dev/null || true
|
|
||||||
pkill -9 Xvfb 2>/dev/null || true
|
|
||||||
rm -f /tmp/.X11-unix/X99 /tmp/.X*-lock
|
|
||||||
sleep 2
|
|
||||||
|
|
||||||
# Start Xvfb :99
|
|
||||||
Xvfb :99 -screen 0 1920x1080x24 -ac +extension RANDR +extension RENDER +extension GLX > /dev/null 2>&1 &
|
|
||||||
XVFB_PID=$!
|
|
||||||
sleep 3
|
|
||||||
|
|
||||||
# Wait for display ready
|
|
||||||
export DISPLAY=:99
|
|
||||||
export XAUTHORITY=/root/.Xauthority
|
|
||||||
for i in $(seq 1 30); do
|
|
||||||
xrandr --listmonitors > /dev/null 2>&1 && break
|
|
||||||
if ! kill -0 $XVFB_PID 2>/dev/null; then
|
|
||||||
echo "ERROR: Xvfb died"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
|
|
||||||
xrandr --listmonitors > /dev/null 2>&1 || { echo "ERROR: Display :99 not ready"; exit 1; }
|
|
||||||
|
|
||||||
# Start XFCE desktop in background
|
|
||||||
nohup dbus-run-session -- xfce4-session > /tmp/xfce-session.log 2>&1 &
|
|
||||||
echo "XFCE PID: $!"
|
|
||||||
sleep 5
|
|
||||||
|
|
||||||
# Wait for window manager
|
|
||||||
for i in $(seq 1 20); do
|
|
||||||
ps aux | grep xfwm4 | grep -v grep > /dev/null 2>&1 && break
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
|
|
||||||
# Start RustDesk client in background
|
|
||||||
nohup dbus-run-session -- /usr/share/rustdesk/rustdesk --server > /tmp/rustdesk-session.log 2>&1 &
|
|
||||||
echo "RustDesk PID: $!"
|
|
||||||
sleep 3
|
|
||||||
|
|
||||||
# Wait for Xvfb (main process) - systemd tracks this script
|
|
||||||
# If Xvfb dies, systemd will restart the service
|
|
||||||
wait $XVFB_PID
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Xorg Dummy Display
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
User=root
|
|
||||||
Environment="DISPLAY=:0"
|
|
||||||
ExecStartPre=/bin/bash -c 'test -f /root/.Xauthority || (xauth -f /root/.Xauthority add :0 . $(mcookie) \u0026\u0026 chmod 600 /root/.Xauthority)'
|
|
||||||
ExecStartPre=/bin/mkdir -p /tmp/.X11-unix
|
|
||||||
ExecStart=/usr/lib/xorg/Xorg :0 -auth /root/.Xauthority -nolisten tcp -config /etc/X11/xorg.conf.d/20-dummy-headless.conf +extension GLX +extension RANDR +extension RENDER -novtswitch -keeptty
|
|
||||||
Restart=always
|
|
||||||
RestartSec=5
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
|
||||||
hbbs:
|
|
||||||
image: rustdesk/rustdesk-server:latest
|
|
||||||
container_name: rustdesk-hbbs
|
|
||||||
command: hbbs -r rustdesk-hbbr:21117
|
|
||||||
volumes:
|
|
||||||
- ./data:/root
|
|
||||||
network_mode: host
|
|
||||||
restart: unless-stopped
|
|
||||||
depends_on:
|
|
||||||
- hbbr
|
|
||||||
|
|
||||||
hbbr:
|
|
||||||
image: rustdesk/rustdesk-server:latest
|
|
||||||
container_name: rustdesk-hbbr
|
|
||||||
command: hbbr
|
|
||||||
volumes:
|
|
||||||
- ./data:/root
|
|
||||||
network_mode: host
|
|
||||||
restart: unless-stopped
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# RustDesk Client Launcher for VPS (Manual Run)
|
|
||||||
# Run this script when you want to connect to host from VPS
|
|
||||||
|
|
||||||
# 1. Ensure no conflicting X servers
|
|
||||||
pkill -f Xvfb
|
|
||||||
pkill -f Xorg
|
|
||||||
|
|
||||||
# 2. Start Xvfb on :0
|
|
||||||
rm -f /tmp/.X11-unix/X0 /tmp/.X0-lock 2>/dev/null
|
|
||||||
/usr/bin/Xvfb :0 -screen 0 1920x1080x24 +extension GLX +extension RANDR +extension RENDER -ac &
|
|
||||||
echo "Xvfb started on :0"
|
|
||||||
sleep 2
|
|
||||||
|
|
||||||
# 3. Configure RustDesk
|
|
||||||
cat > ~/.config/rustdesk/RustDesk2.toml <<'EOF'
|
|
||||||
rendezvous_server = 'localhost:21116'
|
|
||||||
custom-rendezvous-server = 'localhost:21116'
|
|
||||||
key = 'JDSXd9sxN6Y7mIzu3krSpPTTc4yjChfqpzgIH5fl9Iw='
|
|
||||||
nat_type = 1
|
|
||||||
serial = 0
|
|
||||||
unlock_pin = ''
|
|
||||||
|
|
||||||
[options]
|
|
||||||
local-ip-addr = '2.59.219.61'
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# 4. Start RustDesk client
|
|
||||||
export DISPLAY=:0
|
|
||||||
export XAUTHORITY=/root/.Xauthority
|
|
||||||
export LIBVA_DRIVER_NAME=none
|
|
||||||
export VDPAU_DRIVER=none
|
|
||||||
|
|
||||||
# Add xauth for display :0
|
|
||||||
xauth -f "$XAUTHORITY" add :0 . $(mcookie) 2>/dev/null || true
|
|
||||||
|
|
||||||
echo "Starting RustDesk..."
|
|
||||||
nohup /usr/share/rustdesk/rustdesk &
|
|
||||||
|
|
||||||
echo "RustDesk started. Check ID: DISPLAY=:0 /usr/share/rustdesk/rustdesk --get-id"
|
|
||||||
sleep 2
|
|
||||||
echo "Current ID:"
|
|
||||||
DISPLAY=:0 /usr/share/rustdesk/rustdesk --get-id
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# RustDesk VPS - запуск с Xvfb + xcompmgr для корректного GUI рендеринга
|
|
||||||
|
|
||||||
export DISPLAY=:0
|
|
||||||
export XAUTHORITY=/root/.Xauthority
|
|
||||||
export LIBGL_ALWAYS_SOFTWARE=1
|
|
||||||
export LIBVA_DRIVER_NAME=none
|
|
||||||
export VDPAU_DRIVER=none
|
|
||||||
|
|
||||||
# 1. Очищаем старые сессии
|
|
||||||
pkill -f Xvfb
|
|
||||||
pkill -f rustdesk
|
|
||||||
sleep 1
|
|
||||||
rm -f /tmp/.X11-unix/X0 /tmp/.X0-lock 2>/dev/null
|
|
||||||
|
|
||||||
# 2. Запускаем Xvfb с COMPOSITE extension
|
|
||||||
echo "Starting Xvfb..."
|
|
||||||
/usr/bin/Xvfb :0 -screen 0 1920x1080x24 +extension GLX +extension RANDR +extension RENDER +extension COMPOSITE -ac &
|
|
||||||
echo "Xvfb PID: $!"
|
|
||||||
sleep 2
|
|
||||||
|
|
||||||
# 3. Проверяем запуск Xvfb
|
|
||||||
if ! pgrep -f "Xvfb :0" >/dev/null; then
|
|
||||||
echo "ERROR: Xvfb failed to start"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Xvfb OK"
|
|
||||||
|
|
||||||
# 4. Запускаем композитор
|
|
||||||
echo "Starting xcompmgr..."
|
|
||||||
if command -v xcompmgr >/dev/null 2>&1; then
|
|
||||||
/usr/bin/xcompmgr -d :0 &
|
|
||||||
else
|
|
||||||
echo "WARNING: xcompmgr not found. Install with: apt-get install -y xcompmgr"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 5. Настраиваем xauth
|
|
||||||
xauth -f "$XAUTHORITY" add :0 . $(mcookie) 2>/dev/null || true
|
|
||||||
chmod 600 "$XAUTHORITY"
|
|
||||||
|
|
||||||
# 6. Пишем конфиг
|
|
||||||
/root/write-rustdesk-config.sh
|
|
||||||
|
|
||||||
# 7. Запускаем RustDesk
|
|
||||||
echo "Starting RustDesk..."
|
|
||||||
nohup /usr/share/rustdesk/rustdesk &
|
|
||||||
|
|
||||||
sleep 5
|
|
||||||
echo "=== Status ==="
|
|
||||||
echo "Xvfb: $(pgrep -a Xvfb | grep :0 || echo 'NOT RUNNING')"
|
|
||||||
echo "RustDesk:"
|
|
||||||
pgrep -a rustdesk || echo 'NOT RUNNING'
|
|
||||||
echo ""
|
|
||||||
echo "RustDesk ID: $(/usr/share/rustdesk/rustdesk --get-id 2>/dev/null || echo 'N/A')"
|
|
||||||
echo ""
|
|
||||||
echo "To connect to host: rustdesk <host-id>"
|
|
||||||
Reference in New Issue
Block a user