最近一直在折腾一台 UOS 20ARM64 版本的桌面终端处理器是麒麟 9000C显示协议走的是 X11。设备本身倒没什么问题真正让我花了不少时间的是让 KeymouseGo 这种开源的鼠标键盘录制回放工具在上面稳定跑起来。你可能会觉得这种小工具不就拿来即用其实放到国产 ARM 平台上坑比想象中多得多。这篇文章就把我整个适配和排查过程记录下来从环境准备、依赖安装、原理拆解到实战操作都有。如果你手头也有一台 UOS 20 的 ARM64 机器想在 X11 桌面下做重复性操作的自动化这篇文章应该能帮你少踩不少弯路。无论是想快速跑通一个录制脚本还是想搞清楚它背后是怎么工作的下面这些内容都值得一看。1. 项目目标与适用场景拆解1.1 KeymouseGo 到底解决什么问题KeymouseGo 是一个用 Python 写的鼠标键盘录制回放工具。核心逻辑很简单你手动操作一遍电脑它把每一次移动鼠标、点击按钮、敲击键盘的动作连同时间间隔都记录下来存成一个脚本文件。以后需要重复同样操作时一键回放它就能把刚才那套操作原样重跑一遍。我听不少朋友第一反应是“这不就是按键精灵吗”。方向类似但 KeymouseGo 在 Linux 桌面环境下有它独特的优势轻量、开源、基于系统底层输入事件实现比模拟窗口消息的兼容性方案更“硬核”一些。它特别适合下面这些场景软件测试人员做回归测试反复填同一个表单、点同一个保存按钮运维人员批量登录内部平台重复执行某些固化操作办公场景里每天固定处理同一批文档比如改格式、重命名、归档到指定目录培训演示前需要把一段 PPT 操作录下来不断回放给设备自己“讲”一遍。说白了任何一次重复到让你觉得“电脑为什么不自己动手”的操作都适合交给这类工具。不过我得先提个醒这类自动化工具应该用在正当的效率提升、工作流精简场景别拿去搞游戏脚本或者刷量之类的事那是完全不同的思路而且很容易翻车。1.2 从 x86 到 ARM64适配难度在哪里之所以要把“UOS 20ARM64 / 麒麟 9000C”单独拿出来说是因为很多通用教程默认你跑在 x86 的 Ubuntu 或者 Debian 上但国产 ARM 终端的情况不太一样。UOS 20 本身基于 Debian 体系桌面环境用的是 DDE深度桌面环境整体软件源跟 Debian 系很像。但 ARM64 版本的源里包的丰富程度和更新速度通常不如 x86 版本来得及时。很多在 x86 上直接用 apt 就能装好的图形库、输入库在 ARM64 上要么版本偏老要么需要在配置里额外指定架构。麒麟 9000C 是一颗 ARM 架构的桌面处理器64 位指令集没问题跑普通的 Python 程序没有任何障碍。麻烦的地方主要在这几点部分 pip 包会依赖本地动态库比如 libGL.so.1、libX11.so.6如果系统里没有对应 ARM64 版本的库安装或者运行就会报错PyQt5 这类重量级图形库在 ARM64 源里有可能缺失需要换用 Tkinter 之类更基础的方案输入设备相关的事件接口在 ARM 平台上和 x86 平台差别不大但不同内核版本上的权限策略会有差异。所以适配 KeymouseGo 的第一步不是写代码而是先把系统依赖和 Python 依赖对齐到 ARM64 平台能用的状态。1.3 X11 与 Wayland先把基础协议搞对这个工具能不能跑起来一个重要前提是桌面会话走的是 X11 而不是 Wayland。为什么因为 KeymouseGo 在 Linux 下实现回放依赖的是 X11 协议里的 XTest 扩展。XTest 扩展允许程序主动向 X 服务器注入合成输入事件。简单理解为系统内核收到这些注入事件时会认为是一个真实的外接设备发了信号于是目标窗口正常响应跟真人操作几乎一模一样。这就是它能稳定控制第三方软件的根本原因。Wayland 的桌面环境出于窗口隔离和输入安全考虑对程序模拟全局鼠标键盘事件限制得非常严格普通用户态程序基本拿不到 XTest 那种能力。所以如果系统默认登录进的是 Wayland 会话这类录制回放工具基本就废了。UOS 20 桌面默认登录的会话类型我实测是 X11这一点对 KeymouseGo 来说非常关键。如果你拿到的机器或者是未来某个版本默认换了 Wayland就需要先切回 X11 会话再继续下面的操作。可以用一行命令快速确认当前会话类型echo $XDG_SESSION_TYPE输出 X11 就没问题。2. 环境准备把依赖装到能干活的状态2.1 先把 Python 基础环境拉齐UOS 20 通常自带 Python 3.7 或更高版本直接验证一下python3 --version如果版本在 3.7 以上基本够用。接下来装 pip 和 Tkintersudo apt update sudo apt install python3-pip python3-tk千万不要用 update-alternatives 把系统的 python3 软链到别的版本UOS 的桌面组件很多都依赖系统默认 Python乱切容易把桌面搞挂。我在这台机器上就见到过有人切了 Python 版本后DDE 桌面直接起不来最后只能重装。别踩这个坑。装完后可以顺手验证一下 Tkinter 是否可用python3 -c import tkinter; print(ok)如果能打印 ok说明图形库没问题。pip 源的问题也要注意。ARM64 环境下默认源下载速度可能不稳定我个人建议直接把 pip 的索引源切换成一个速度快的社区镜像能省不少时间。具体配置方式就是把~/.pip/pip.conf里的index-url改掉。这里不写具体地址你选择一个自己在网络环境里实测快的源就行。2.2 安装 X11 相关系统包KeymouseGo 在 Linux 上做输入注入底层要跟 X 服务器打交道所以需要装 X11 的开发库还有 XTest 扩展相关的系统包。执行sudo apt install xdotool python3-xlib libx11-dev libxtst-dev这几个包的作用分别是xdotool命令行下调试 XTest 注入是否正常的最快工具python3-xlibPython 访问 X11 协议的接口层libx11-devX11 客户端库的开发头文件部分 Python 包编译时需要libxtst-devXTest 扩展的开发库提供额外输入事件模拟能力。装完之后可以用这样一个命令确认 XTest 扩展在当前 X 服务器里有没有被启用xdpyinfo -display :0 | grep XTEST如果能看到 XTEST说明当前 X 服务器支持输入注入。如果搜不到任何关键字那就要检查 Xorg 的配置或者显卡驱动里面是不是把扩展关掉了。实际运营环境中常见的是某些优化脚本或安全策略会禁用 XTEST所以提前验证是值得的。2.3 获取源码并确认入口KeymouseGo 这类开源工具的源码获取方式很常规从项目主页把代码打包下载下来就能用。下载后先看一下目录里的 README依赖通常写在 requirements.txt 里面。我在这台 ARM64 机器上实测核心依赖其实就两个方向一个是 pynput 这类输入事件监听库另一个是 python-xlib 或者 pyautogui 这类回放注入库。在 ARM64 上直接 pip 装就行不需要编译任何 C 扩展基本没有架构方面的障碍。启动入口一般是main.py。如果项目里带 GUI 界面运行python3 main.py如果遇到某个图形依赖在 ARM64 源里找不到我的习惯是优先搜索系统源里的同名包能用 apt 装就别硬用 pip。你可以在源里这样搜索apt-cache search pyqt5有的话直接sudo apt install python3-pyqt5会比 pip 在 ARM 平台上省心很多。没有的话就用 Tkinter 版本功能上完全够用。3. 核心实现原理录制回放是怎么做到的3.1 事件监听与脚本记录格式KeymouseGo 的录制阶段核心是一个全局事件监听器。它在后台不停接收系统的输入事件然后给每个事件打上毫秒级时间戳。记录的事件类型大致包括鼠标移动move左键按下leftdown左键释放leftup右键按下rightdown右键释放rightup键盘按键keydown / keyup它记录时间的方式不是直接记录绝对时间而是计算相邻两个事件之间间隔了多少毫秒把这个差值作为 delay 字段存下来。回放的时候就靠这些间隔还原出原始操作节奏。所以你在录制过程中停顿多久、点击多快回放时都会原样重现。一个典型的脚本片段内容大致是这样的结构delay, 280 move, 960, 540 leftdown leftup delay, 150 keydown, ctrl keydown, v keyup, v keyup, ctrl这个文本格式的好处是可以直接用编辑器手工改。比如把某个坐标从 960 改成 1024或者把某个多余的 delay 删掉都不需要重新录制。这个特性在后期调参时极其有用。3.2 回放时序与双线程设计回放阶段有意思的地方在时序控制。程序会先加载脚本把每一行解析成一个事件对象然后启动一个独立的工作线程来处理。工作线程的逻辑其实就是一个循环读取当前事件如果事件类型是 delay就sleep对应毫秒数如果是鼠标移动或点击就通过 XTest 扩展注入对应事件继续读下一个事件直到脚本结束。录制和回放之间的时间差主要靠 delay 来控制。所以 KeymouseGo 界面上通常会有“速度倍率”这个参数实际就是给所有 delay 统一乘一个系数。倍率设为 0.5整个操作就变成原来的两倍速设为 2就是半速回放如果设成 0所有等待基本被忽略脚本就会变成一轮纯操作风暴。某些性能测试场景里这个功能非常好用。关键的一点是录制和回放必须在单独的线程里跑。为什么因为 GUI 主线程在回放时还需要处理界面事件比如你用热键请求停止、修改循环次数等等。如果回放在主线程里执行界面会直接假死想停都停不下来。3.3 坐标换算与界面参数KeymouseGo 在 X11 下默认的坐标原点在屏幕左上角单位是像素。这个坐标系本身很直观但 UOS 20 桌面默认开了缩放之后会带来麻烦。举个例子你在显示设置里把缩放设为 150%界面里看起来某个按钮在逻辑坐标 (960, 540) 的位置但在 X11 的物理坐标体系里它实际对应的可能是 (1440, 810)。如果 KeymouseGo 录制时记录的是逻辑坐标回放的时候却直接往物理坐标注入那么点击位置就会整体偏移。解决办法有两个录制和回放时保持相同的缩放比例不要中途切换录制时不要依赖缩放后的逻辑坐标直接以物理全屏坐标为目标。另外还要留意多显示器的情况。X11 的全局坐标会跨多个显示器平铺计算如果你把窗口从主屏拖到副屏那么同样的按钮坐标已经变了之前录好的脚本自然就失效。这种情况我建议直接重新录一遍别想着通过改坐标硬凑效率太低。4. 实战记录完整跑通一个循环点击任务4.1 录制开始前的准备前面原理讲了那么多下面我把自己在这台 UOS 20ARM64 / 麒麟 9000C上实际跑通的一个任务完整记录下来方便你照着操作。我选择的演示任务是在某个内部应用的列表页手动点击“导出报表”按钮然后在弹出的对话框里确认把报表文件保存到固定目录。整个过程大概十几秒之后我想让它自动循环执行 20 次。录制前有几步准备工作要做关掉所有可能弹窗干扰的程序包括系统更新提醒、输入法切换浮窗等。任何出现的弹窗都会成为录制内容的一部分真实回放时反而会干扰流程把目标软件打开到需要的页面窗口位置固定好中途别移动确认会话类型是 X11启动 KeymouseGo主界面保持打开状态。准备好之后我先把界面上的“录制”按钮点一下然后迅速切换到目标应用窗口。4.2 录制过程与脚本检查录制开始后我手动执行了一遍完整操作鼠标移到列表第一行的“导出”按钮左键单击等待页面弹出确认对话框鼠标移到“确认”按钮左键单击在弹出的文件保存窗口里用键盘输入文件名report_0716按下回车等待保存完成页面回到列表页。操作完成后我切回 KeymouseGo 窗口点了“停止录制”脚本保存到了一个文本文件里。用文本编辑器打开看内容大致是delay, 1020 move, 1280, 460 leftdown leftup delay, 950 move, 1024, 640 leftdown leftup delay, 380 keydown, r keydown, e ...这里有几个细节值得注意。首先是 delay 的分布操作之间如果停顿了中间就会插入一个较大的 delay 值一般在 300 到 1000 毫秒之间这正常。其次是鼠标在移动的过程中如果路径比较长会被拆成很多个连续的 move 事件每个之间间隔几十毫秒这也是正常的。如果录制过程中手抖了或者鼠标点击错了位置不要急着停下来重录。我通常的做法是先把脚本文件放到文本编辑器里把明显错误的 move 行删掉或者把点击坐标改一下比重新录制更快。4.3 参数调整与正式回放脚本确认无误后我在 KeymouseGo 界面里设置了这样几个参数循环次数20速度倍率1然后点“开始回放”。回放过程中我刻意不去动鼠标和键盘因为任何人工输入都会跟脚本注入的事件混在一起轻则打乱节奏重则让后续操作全部错位。大概 5 分钟左右20 轮循环全部跑完。打开目标目录里面已经生成了 20 个带编号的报表文件任务顺利完成。如果回放过程中发现点击位置和录制时不一样或者节奏太快导致页面响应不过来我的调整顺序是先确认缩放设置有没有变化再检查是不是有些 delay 太短。把明显偏短的 delay 手动调大一点通常就能解决。5. 踩坑排查ARM64 UOS 上常见问题速查5.1 动态库与 Python 依赖报错最常见的一类问题是启动时直接报ImportError比如找不到Xlib模块。不要急着去 pip 重装先在系统里搜一下apt-cache search python3-xlib如果有直接 apt 安装。UOS 20 的 ARM64 源里这个包通常是有的而且系统版本跟桌面环境匹配度更好不会出现 pip 版和系统库冲突的情况。另一个高频错误是ImportError: libGL.so.1: cannot open shared object file这说明某个图形相关的依赖库没有装全。解决办法是sudo apt install libgl1-mesa-glx如果装了 PyQt5 后报qt.qpa.plugin: Could not find the Qt platform plugin xcb也是类似逻辑需要补sudo apt install libxcb-xinerama0这类问题的排查思路就是看到 which 库缺失就用apt-file或者apt-cache search找到对应 ARM64 包装上之后一般就正常了。5.2 DISPLAY 与 Xauthority 问题如果你是通过 SSH 登录到这台 UOS 设备然后尝试直接运行 KeymouseGo很可能会遇到No protocol specified _x11.GetImage: Could not connect to display None原因是没有把当前会话的显示环境变量传过来。需要在运行前手动指定export DISPLAY:0如果还是不行还要把 Xauthority 文件指出来export XAUTHORITY~/.Xauthority注意这里的~对应的是正在运行图形桌面的那个用户不是 SSH 登录用户。如果两个用户不一致直接写绝对路径更稳妥。5.3 XTEST 扩展不可用或者注入无效运行 xdotool 模拟点击但目标窗口毫无反应大概率是 XTEST 扩展在当前会话里没启用。验证命令前面已经写过了xdpyinfo -display :0 | grep XTEST如果是远程桌面或者某些经过安全加固的 Xorg 配置XTEST 可能会被显式禁用。另外UOS 20 上如果启用了某些录屏或者安全管控组件也可能拦截 XTEST 注入事件。这种情况我遇到过一次最后是在系统设置的安全中心里把对应程序的输入监控权限关掉才解决。不同版本的 UOS 设置项名称可能不一样你找跟“输入监控”“进程保护”相关的开关关掉试试。5.4 高 DPI 与多显示器导致的坐标偏移这类问题在项目里出现的频率最高。录制时鼠标明明停在按钮上回放时却点到旁边去了基本就是缩放比例变了或者显示器布局变了。排查方法很简单看回放的实际落点是否偏移量固定。如果每次都是同样的偏移说明坐标坐标系不一致如果只在某个显示器上偏移说明主屏设置变了。我的经验是录制前先确认显示设置为固定档位别用自动缩放多个显示器时尽量在同一个屏幕内录制和回放。偶尔需要跨屏操作就老老实实重新录一遍别迷信手改坐标。5.5 回放停不下来怎么办循环次数设得太多或者脚本里有异常的长 delay回放线程会把主界面卡住此时鼠标点击“停止”按钮可能没反应。遇到这种情况我通常直接在工作线程所在的终端窗口按CtrlC如果无效就开一个新终端用进程名把整个程序杀掉再重启。所以每次跑长时间回放前我会提前开一个终端窗口随时准备用命令终止进程避免被迫直接重启设备。最后再分享一点经验整个环境折腾下来我的体会是KeymouseGo 本身没什么高深技术但能不能在你的 ARM64 UOS 设备上跑起来很大程度上取决于系统依赖装没装对、X11 会话对不对、缩放配置统不统一。这三件事只要有一件没做好你就会在莫名其妙的地方浪费半天时间。最后分享一个我实际用下来的小技巧回放任务正式开始前先用xdotool获取目标窗口的位置信息例如用xwininfo确认窗口所在区域这样能在录脚本之前就知道目标坐标会不会落在窗口之外。特别是调整过分辨率或者屏幕布局后这个检查能帮你省去一次重录的麻烦。如果你手头的 UOS 20 ARM64 设备也要跑 KeymouseGo照着这篇文章的顺序走一遍基本半小时内就能把环境整好。真正跑通之后你会发现那些每天重复到烦躁的操作终于可以放心交给机器去执行了。