主页 HomePage

260818-debian13-xorg-xrdp-dwm

目标:Debian 13 最小化安装后,本机保持纯 TTY,不启动本地图形桌面;仅在 RDP 登录时启动 xorgxrdp + dwm。普通窗口使用 monocle 布局铺满远程桌面,dialog / transient / fixed-size 窗口由 dwm 按 floating 方式处理。

本文以普通 RDP 登录用户执行用户级命令;需要系统权限的命令使用 sudo。

1. 更新系统并安装基础组件

sudo apt update
sudo apt upgrade -y

为了保持最小化,不直接安装完整 xorg 元包,而是显式安装 xrdp、xorgxrdp、xterm 和需要的 X11 工具:

sudo apt install --no-install-recommends \
    xrdp \
    xorgxrdp \
    xterm \
    x11-utils \
    x11-xserver-utils \
    dbus-user-session

如果不在意安装更多 X.Org 驱动和工具,也可以安装完整 xorg 元包;本文采用上面的最小化方案。

2. 保持本机为文本模式

sudo systemctl set-default multi-user.target
systemctl get-default

预期输出:

multi-user.target

启用 xrdp:

sudo systemctl enable --now xrdp

3. 安装 dwm 编译依赖

sudo apt install --no-install-recommends \
    build-essential \
    wget \
    ca-certificates \
    libx11-dev \
    libxft-dev \
    libxinerama-dev \
    fonts-dejavu-core

4. 下载 dwm 6.8

mkdir -p ~/src
cd ~/src
wget https://dl.suckless.org/dwm/dwm-6.8.tar.gz
tar xf dwm-6.8.tar.gz
cd dwm-6.8

4.1 修改 config.mk

nano config.mk

将:

X11INC = /usr/X11R6/include
X11LIB = /usr/X11R6/lib

修改为:

X11INC = /usr/include
X11LIB = /usr/lib

4.2 创建极简 config.h

cd ~/src/dwm-6.8
cp config.def.h config.h
nano config.h

将整个 config.h 替换为:

/* See LICENSE file for copyright and license details. */

/* appearance */
static const unsigned int borderpx  = 0;
static const unsigned int snap      = 0;
static const int showbar            = 0;
static const int topbar             = 1;

static const char *fonts[]          = { "monospace:size=10" };

static const char col_black[]       = "#000000";
static const char col_white[]       = "#ffffff";

static const char *colors[][3] = {
    /*               fg         bg         border */
    [SchemeNorm] = { col_white, col_black, col_black },
    [SchemeSel]  = { col_white, col_black, col_black },
};

/* one tag only */
static const char *tags[] = {
    "1"
};

/* no application-specific rule yet */
static const Rule rules[] = {
    /* class  instance  title  tags mask  isfloating  monitor */
    { NULL,   NULL,     NULL,  0,         0,          -1 },
};

/* layout */
static const float mfact         = 0.55;
static const int nmaster         = 1;

/*
 * Important for our use case:
 * ignore application size hints when tiled/monocle.
 */
static const int resizehints     = 0;

/* keep focus on real EWMH fullscreen clients */
static const int lockfullscreen  = 1;

static const int refreshrate     = 120;

/*
 * Only one layout:
 *
 * normal windows -> monocle
 * dialog/transient/floating windows -> floating
 */
static const Layout layouts[] = {
    /* symbol  arrange function */
    { "[M]",   monocle },
};

/*
 * dwm.c:spawn() references these two symbols unconditionally.
 * We don't use dmenu, but keep dummy definitions for vanilla dwm.
 */
static char dmenumon[2] = "0";
static const char *dmenucmd[] = { NULL };

static const char *termcmd[] = {
    "xterm",
    "-ls",
    NULL
};

/*
 * Keyboard shortcuts.
 *
 * Alt + Shift + Enter : new xterm
 * Alt + Tab           : next client
 * Alt + Shift + C     : close current client
 * Alt + Shift + Q     : quit dwm
 */
#define MODKEY Mod1Mask

static const Key keys[] = {
    /* modifier           key        function    argument */
    { MODKEY|ShiftMask,   XK_Return, spawn,      {.v = termcmd } },
    { MODKEY|ShiftMask,   XK_c,      killclient, {0} },
    { MODKEY|ShiftMask,   XK_q,      quit,       {0} },
    { MODKEY,             XK_Tab,    focusstack, {.i = +1 } },
};

/*
 * Debug/maintenance only.
 *
 * Alt + left mouse  -> move floating window
 * Alt + right mouse -> resize floating window
 *
 * They can be removed later if not needed.
 */
static const Button buttons[] = {
    /* click         event mask  button   function     argument */
    { ClkClientWin,  MODKEY,     Button1, movemouse,   {0} },
    { ClkClientWin,  MODKEY,     Button3, resizemouse, {0} },
};

说明:MODKEY 为 Mod1Mask,因此这里的修饰键是 Alt,不是 Super。

注意:dwm 会自动将标准 EWMH dialog、transient 和 fixed-size 窗口作为 floating 处理,但某些应用若没有正确声明窗口类型,仍可能需要通过 xprop 获取 WM_CLASS / title 后在 rules[] 中增加专用规则。

4.3 编译并安装 dwm

cd ~/src/dwm-6.8
make clean
make
sudo make install
/usr/local/bin/dwm -v

5. 配置 xrdp 自动使用 Xorg

sudo nano /etc/xrdp/xrdp.ini

在 [Globals] 中加入或修改:

autorun=Xorg

并确认文件中的 [Xorg] 段类似下面内容,重点是 code=20:

[Xorg]
name=Xorg
lib=libxup.so
username=ask
password=ask
ip=127.0.0.1
port=-1
code=20

6. 配置 xrdp 的用户专用启动脚本

sudo nano /etc/xrdp/sesman.ini

在 [Globals] 中确保只保留一组相关配置,不要重复定义:

[Globals]
EnableUserWindowManager=true
UserWindowManager=.xrdp-session
DefaultWindowManager=startwm.sh

UserWindowManager=.xrdp-session 表示 xrdp-sesman 会从 RDP 用户的 home 目录执行 ~/.xrdp-session。如果脚本不可执行,会回退到 /etc/xrdp/startwm.sh。

7. 配置 xterm 外观

先创建 ~/.Xresources:

nano ~/.Xresources

写入:

XTerm*renderFont: true
XTerm*faceName: DejaVu Sans Mono
XTerm*faceSize: 24

XTerm*background: #1e1e1e
XTerm*foreground: #d4d4d4

XTerm*cursorColor: #aeafad
XTerm*cursorBlink: true

XTerm*highlightColor: #264f78
XTerm*highlightTextColor: #ffffff

XTerm*scrollBar: false

XTerm*color0:  #1e1e1e
XTerm*color1:  #cd3131
XTerm*color2:  #0dbc79
XTerm*color3:  #e5e510
XTerm*color4:  #2472c8
XTerm*color5:  #bc3fbc
XTerm*color6:  #11a8cd
XTerm*color7:  #e5e5e5
XTerm*color8:  #808080
XTerm*color9:  #f14c4c
XTerm*color10: #23d18b
XTerm*color11: #f5f543
XTerm*color12: #3b8eea
XTerm*color13: #d670d6
XTerm*color14: #29b8db
XTerm*color15: #ffffff

XTerm*utf8: 1
XTerm*saveLines: 10000

如需立即测试字体,也可以运行:

xterm -fa "DejaVu Sans Mono" -fs 24

在已有 X 会话中立即重新加载资源:

xrdb -load ~/.Xresources

验证:

xrdb -query | grep '^XTerm'

已经打开的 xterm 不一定会应用新资源,应新开一个 xterm 测试。

8. 创建 ~/.xrdp-session

nano ~/.xrdp-session

写入:

#!/bin/sh

export XDG_SESSION_TYPE=x11
export XDG_CURRENT_DESKTOP=dwm
export DESKTOP_SESSION=dwm

# Load X resources before starting the first xterm.
if [ -r "$HOME/.Xresources" ]; then
    /usr/bin/xrdb -load "$HOME/.Xresources"
fi

# Start dwm.
/usr/local/bin/dwm &
DWM_PID=$!

# Start the initial terminal.
/usr/bin/xterm -ls &

# Ending dwm ends the RDP graphical session.
wait "$DWM_PID"

8.1 修正文件所有权和权限

这一步非常重要。~/.xrdp-session 必须能够由实际 RDP 登录用户执行,不能是 root:root 0700。

sudo chown "$USER":"$USER" "$HOME/.xrdp-session" "$HOME/.Xresources"
chmod 700 ~/.xrdp-session
chmod 644 ~/.Xresources
ls -l ~/.xrdp-session ~/.Xresources

预期 ~/.xrdp-session 的 owner/group 是当前 RDP 用户。

9. 重启 xrdp

sudo systemctl restart xrdp
sudo systemctl restart xrdp-sesman

首次验证或修改会话启动逻辑后,建议直接重启虚拟机,以避免 mstsc 重新连接到之前仍然存在的旧 xrdp session:

sudo reboot

10. Windows RDP 登录

使用 Windows mstsc 连接虚拟机。正常情况下:

11. 快捷键

快捷键功能
Alt + Shift + Enter新建 xterm
Alt + Tab切换到下一个已有 client;没有窗口时不会自动创建 xterm
Alt + Shift + C关闭当前 client
Alt + Shift + Q退出 dwm,并结束当前 RDP 图形会话
Alt + 鼠标左键移动 floating 窗口
Alt + 鼠标右键调整 floating 窗口大小

12. 常用排障

12.1 RDP 登录后没有执行 ~/.xrdp-session

sudo grep -Ei 'window manager|xrdp-session|startwm|exec' /var/log/xrdp-sesman.log | tail -100

如果看到 errno: 13 / Permission denied,优先检查:

ls -l ~/.xrdp-session
stat ~/.xrdp-session

脚本应归 RDP 登录用户所有,并具有执行权限。

12.2 检查 Xresources

xrdb -query | grep -iE 'XTerm.*(renderFont|faceName|faceSize)'

12.3 检查异常弹窗

xprop | grep -E 'WM_CLASS|WM_NAME|_NET_WM_WINDOW_TYPE|WM_TRANSIENT_FOR'

点击异常窗口,根据输出结果在 dwm 的 rules[] 中补充 isfloating=1 的规则,然后重新编译安装 dwm。

13. 参考