Ubuntu 无显示器远程桌面完美方案

Ubuntu 无显示器远程桌面完美方案

适用环境:Ubuntu 20.04 + Intel 集成显卡 + GNOME 桌面 + ToDesk / RustDesk 等远程工具

问题描述

Linux 主机不接物理显示器时,远程桌面工具(ToDesk、RustDesk、向日葵等)提示 "No display" 或画面分辨率极低(如 1024x768),无法正常远程操作。

根本原因

Intel 集成显卡在没有物理显示器连接时,所有视频输出口(DP、HDMI)均为 disconnected 状态。X11/GNOME 检测不到任何可用的显示输出,远程桌面工具自然无法捕获屏幕画面。

解决思路

整个方案分为两步:

  1. 让虚拟显示器"存在" --- 通过内核启动参数强制启用一个 HDMI 输出
  2. 让虚拟显示器"好用" --- 通过后台服务自动将分辨率设为 1920x1080

第一步:内核参数 --- 强制启用虚拟 HDMI 输出

1.1 确认你的 HDMI 接口名称

bash 复制代码
ls /sys/class/drm/

输出类似:card0-DP-1 card0-DP-2 card0-HDMI-A-1 card0-HDMI-A-2 ...

选一个你不常用 的 HDMI 口,比如 HDMI-A-1

1.2 修改 GRUB 配置

bash 复制代码
sudo cp /etc/default/grub /etc/default/grub.bak
sudo nano /etc/default/grub

找到 GRUB_CMDLINE_LINUX_DEFAULT 行,添加 video=HDMI-A-1:1920x1080e

复制代码
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash video=HDMI-A-1:1920x1080e"

参数说明HDMI-A-1 是内核中的接口名,1920x1080 是虚拟分辨率,末尾的 e 表示 force enable(强制启用,即使没有接显示器)。

1.3 更新 GRUB 并重启

bash 复制代码
sudo update-grub
sudo reboot

1.4 验证

重启后执行:

bash 复制代码
cat /proc/cmdline | grep video
xrandr | grep HDMI-1

应该能看到 HDMI-1 connected,即使没接物理显示器。

第二步:后台服务 --- 自动设置 1920x1080 分辨率

虽然第一步让 HDMI-1 显示为"已连接",但由于没有真实显示器提供 EDID 信息,GNOME 只会给它分配一个极低的默认分辨率(通常 1024x768)。需要一个后台服务来自动修正。

2.1 创建监听脚本

bash 复制代码
sudo nano /usr/local/bin/display-monitor.sh

写入以下内容(纯 bash,无需 Python):

bash 复制代码
#!/bin/bash
export DISPLAY=:0
export XAUTHORITY=/run/user/$(id -u)/gdm/Xauthority
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus"

LAST_HANDLE_TIME=0

# 获取 GNOME 显示配置的完整状态
get_display_state() {
    gdbus call --session \
        --dest org.gnome.Mutter.DisplayConfig \
        --object-path /org/gnome/Mutter/DisplayConfig \
        --method org.gnome.Mutter.DisplayConfig.GetCurrentState 2>/dev/null
}

# 通过 GNOME D-Bus 接口应用显示配置(method=1 临时模式,无需确认)
apply_config() {
    local serial="$1"
    local monitors="$2"
    gdbus call --session \
        --dest org.gnome.Mutter.DisplayConfig \
        --object-path /org/gnome/Mutter/DisplayConfig \
        --method org.gnome.Mutter.DisplayConfig.ApplyMonitorsConfig \
        "$serial" 1 "$monitors" "{}" >/dev/null 2>&1
}

handle_display_change() {
    # 防抖:5 秒内不重复处理(一次插拔可能触发多个事件)
    local now=$(date +%s)
    if (( now - LAST_HANDLE_TIME < 5 )); then
        return
    fi
    LAST_HANDLE_TIME=$now

    sleep 2

    # 为虚拟 HDMI-1 添加 1920x1080 模式(已存在则忽略)
    xrandr --newmode "1920x1080_60.00" 173.00 1920 2048 2248 2576 \
        1080 1083 1088 1120 -hsync +vsync 2>/dev/null
    xrandr --addmode HDMI-1 "1920x1080_60.00" 2>/dev/null

    # 检查是否有物理显示器连接(排除虚拟的 HDMI-1)
    PHYSICAL=$(xrandr 2>/dev/null \
        | grep -E "^(DP-|HDMI-[^1])" \
        | grep " connected " \
        | head -1 | awk '{print $1}')

    # 一次 D-Bus 调用获取全部状态,避免重复请求
    STATE=$(get_display_state)
    SERIAL=$(echo "$STATE" | grep -oP '\(uint32 \K\d+')
    [ -z "$SERIAL" ] && return

    if [ -n "$PHYSICAL" ]; then
        # ---- 有物理显示器:物理屏做唯一主屏,关闭虚拟屏 ----
        MODE=$(echo "$STATE" \
            | grep -oP "'${PHYSICAL}'.*?'is-preferred': <true>" \
            | grep -oP "'\d+x\d+@[\d.]+'" | head -1 | tr -d "'")
        [ -z "$MODE" ] && return
        apply_config "$SERIAL" \
            "[(0, 0, 1.0, 0, true, [('${PHYSICAL}', '${MODE}', {})])]"
    else
        # ---- 无物理显示器:虚拟屏做唯一主屏 ----
        CURRENT_RES=$(xrandr 2>/dev/null \
            | grep "HDMI-1 connected" | grep -oP "\d+x\d+")
        if [ -n "$CURRENT_RES" ] && [ "$CURRENT_RES" != "1920x1080" ]; then
            apply_config "$SERIAL" \
                "[(0, 0, 1.0, 0, true, [('HDMI-1', '1920x1080@59.962844848632812', {})])]"
        fi
    fi
}

# 启动时先执行一次,处理开机就没接物理显示器的情况
handle_display_change

# 然后持续监听热插拔事件
udevadm monitor --kernel --subsystem-match=drm 2>/dev/null | while read line; do
    if echo "$line" | grep -q "change"; then
        handle_display_change
    fi
done
bash 复制代码
sudo chmod +x /usr/local/bin/display-monitor.sh

2.2 创建 systemd 服务

bash 复制代码
sudo nano /etc/systemd/system/fix-virtual-display.service

写入:

ini 复制代码
[Unit]
Description=Auto fix virtual display on monitor hotplug
After=gdm.service graphical.target

[Service]
Type=simple
User=你的用户名
ExecStart=/usr/local/bin/display-monitor.sh
Restart=always
RestartSec=5

[Install]
WantedBy=graphical.target

User=你的用户名 替换为你的实际用户名。

2.3 启用服务

bash 复制代码
sudo systemctl daemon-reload
sudo systemctl enable fix-virtual-display.service
sudo systemctl start fix-virtual-display.service

验证运行状态:

bash 复制代码
sudo systemctl status fix-virtual-display.service

最终效果

场景 行为
接了物理显示器 纯物理屏,无虚拟屏干扰
拔掉物理显示器 约 2 秒后自动切换为 1920x1080 虚拟屏
换了一个物理接口 自动识别新接口并设为主屏
重启(无物理显示器) 开机后自动启用 1920x1080 虚拟屏

全程无需手动操作。

技术要点

为什么不能只用 xrandr?

GNOME 的窗口管理器 Mutter 会覆盖 xrandr 的设置。必须通过 Mutter 的 D-Bus 接口 org.gnome.Mutter.DisplayConfig.ApplyMonitorsConfig 来配置,GNOME 才会认可。

为什么用 method=1(临时模式)?

D-Bus 的 ApplyMonitorsConfig 有两种模式:

  • method=1(临时):立即生效,无需确认
  • method=2(持久):弹出确认对话框,20 秒不点击会自动回退

必须用临时模式,原因有二:

  1. 持久模式会弹确认框。切换发生在无物理显示器时(人不在电脑前),没人能点确认,20 秒后就会回退。
  2. 临时模式不影响可靠性。虽然叫"临时",只是说不写入 GNOME 的持久配置文件,但我们的后台服务常驻运行,每次开机、每次热插拔都会自动重新设置,效果等同于持久。

为什么内核参数不可省略?

没有 video=HDMI-A-1:1920x1080e,HDMI-1 在无显示器时状态为 disconnected,GNOME 根本不会把它视为一个可用的显示输出,后续所有操作都无从谈起。

故障排除

虚拟屏分辨率不对:

bash 复制代码
# 手动触发修复
xrandr --newmode "1920x1080_60.00" 173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync
xrandr --addmode HDMI-1 "1920x1080_60.00"
# 然后通过 D-Bus 应用(参考脚本中的 gdbus 部分)

服务未运行:

bash 复制代码
sudo systemctl restart fix-virtual-display.service
journalctl -u fix-virtual-display.service -f

想恢复原状:

bash 复制代码
sudo systemctl disable --now fix-virtual-display.service
sudo rm /usr/local/bin/display-monitor.sh
sudo rm /etc/systemd/system/fix-virtual-display.service
sudo cp /etc/default/grub.bak /etc/default/grub
sudo update-grub
sudo reboot
相关推荐
峥嵘life2 小时前
Android16 【CTS】CtsWindowManagerDeviceAnimations存在fail项
android·linux·学习
VermouthSp2 小时前
上下文切换
linux·rust
我爱学习好爱好爱3 小时前
Logstash 数据管道测试案例:从 Filebeat 接收日志并输出至黑屏幕与 Elasticsearch(基于Rocky Linux 9.6)
大数据·linux·elasticsearch
桌面运维家3 小时前
Windows VHD虚拟磁盘技术详解与应用指南
linux·运维·服务器
hy____1234 小时前
Linux_网络基础2
linux·服务器·网络
微露清风4 小时前
系统性学习Linux-第六讲-Ext文件系统
linux·服务器·学习
喵叔哟4 小时前
6. 【Blazor全栈开发实战指南】--组件通信与共享状态
linux·网络·windows
桌面运维家4 小时前
云桌面vDisk解决方案:Windows/Linux高效部署与优化
linux·运维·服务器
wsoz4 小时前
GCC编译
linux·c语言·嵌入式·gcc