
上周处理了几台双屏工控一体机的触摸错位问题现象基本一致系统是Ubuntu 22.04一块主屏加一块触摸副屏开机后手指点在副屏上鼠标光标却跑到主屏某个位置乱晃点在屏幕下方光标反而出现在上方。做自助终端、会议平板、工业HMI的朋友遇到这个场景太常见了有人第一反应是触摸屏坏了二话不说申请售后换屏。实际上触摸屏和排线基本都是好的问题出在系统把触摸输入坐标和显示输出坐标映射错了也就是触摸点与响应位置不一致。这篇文章把Ubuntu 22.04双屏显示触屏设置的排查思路、核心命令和持久化配置完整梳理一遍内置触摸一体机、USB外接触摸屏、笔记本加扩展屏都能套用按步骤走基本一遍解决。1. 双屏触屏错位的根因一张“虚拟大桌面”引发的坐标错乱1.1 一个典型故障现象先描述一下最常见的故障画面。设备是一台工控一体机内部自带一块触摸屏通过HDMI外接了一块副屏。系统装的是Ubuntu 22.04桌面默认扩展模式主屏在左副屏在右。触摸操作副屏时光标没有落在副屏对应按钮上而是跑到主屏的对应位置或者干脆落在两个屏幕之间的某个位置。很多朋友此时会去系统设置里翻“触屏校准”选项但Ubuntu的GNOME桌面默认根本没有这个入口设置里只有显示排列、分辨率、方向这些常规项翻半天也找不到头绪。我处理过的类似设备里大约七成是这种情况触摸屏本身和显示输出不是同一个物理屏幕但系统在驱动层默认把触摸坐标映射到了整个显示桌面而不是映射到某个具体的显示输出。换句话说系统以为“手指点在‘所有屏幕’上的这个比例位置”而实际上手指是点在某一块具体的物理屏幕上。1.2 X Server的坐标空间与触摸设备坐标要彻底搞明白这个问题必须理解Linux图形栈里X Server是如何组织多屏幕坐标的。在Xorg环境下屏幕不是一个个独立的坐标空间而是被组合成一个大的“虚拟桌面”。比如主屏分辨率是2560x1440在左侧副屏分辨率是1920x1080在右侧那么整个虚拟桌面的范围就是宽4480、高1440。每个显示输出在这个虚拟桌面里都有自己的起点坐标左侧主屏的起点是(0, 0)右侧副屏的起点是(2560, 0)。触摸屏上报坐标时驱动层拿到的是触摸控制器的“设备坐标”这个坐标是触摸屏自己报告的原始值跟物理分辨率并不一定是同一套数。libinput驱动会在驱动内部把设备坐标归一化到0到1之间然后交给X ServerX Server再按照归一化坐标映射到整个虚拟桌面。也就是说系统默认逻辑是你触摸的位置在触摸屏上的比例位置就是光标在整个虚拟桌面上的比例位置。问题就出在这里当只有一个显示输出时虚拟桌面的尺寸和这个输出完全一致比例位置等于绝对位置自然没有问题。但一旦有两个及以上显示输出虚拟桌面变大了触摸设备却仍然把坐标映射到整个虚拟桌面手指点在副屏的中央光标就会落在整个虚拟桌面宽度的50%附近这个位置大概率不是副屏中央甚至可能落在主屏上。1.3 设备坐标系为什么不能直接用还有一个隐藏因素容易被忽略触摸控制器的设备坐标范围并不等于屏幕物理分辨率。有的控制器上报范围是0到4095有的是0到32767甚至有的触摸IC上报范围是不对称的比如X方向0到32767Y方向0到16383。如果驱动没有做归一化处理直接用物理分辨率去对应设备坐标值必然产生系统性偏移。这也是为什么有些朋友照着网上教程用xinput_calibrator或者手动改坐标矩阵后触摸点依然有固定偏移的原因——没有先确认设备坐标范围与归一化情况。在Ubuntu 22.04默认的libinput驱动下一般会自动归一化但如果设备比较特殊或者安装的是evdev驱动就要留意这个变量。后面第5章会专门展开这个问题。2. 动手前必做的三步排查设备名、输出布局、矩阵原始状态在修改任何配置之前先把当前系统的实际状态摸清楚。这一步非常关键很多朋友跳过排查直接照着别人的命令抄结果设备名对不上、输出方向不对越改越乱。我每次处理触屏问题固定按下面三步走。2.1 用xinput找到触摸设备的真实名字第一步是列出当前系统里的所有输入设备找到触摸屏对应的设备名和设备ID。xinput list输出会显示一大串设备分为“Virtual core pointer”和“Virtual core keyboard”两大类。触摸屏一定在pointer类目下而且设备名里通常带有Touchscreen、Touch Panel、或者具体的厂商型号字样比如⎡ Virtual core pointer id2 [master pointer (3)] ⎜ ↳ Virtual core XTEST pointer id4 [slave pointer (2)] ⎜ ↳ ELAN2514:00 04F3:2A56 Touchscreen id18 [slave pointer (2)] ⎜ ↳ Logitech USB Receiver id20 [slave pointer (2)]上面这个例子里触摸设备就是ELAN2514:00 04F3:2A56 TouchscreenID是18。如果你的设备名里有中文或者空格后面用命令操作时一定要记得用双引号把设备名包起来否则xinput会把空格后面的单词当成额外参数导致报错。如果你是USB外接触摸屏插上后设备名通常会包含厂商名或USB接口描述比如HID 04D9:8009 TouchScreen。对不上设备名时可以用xinput list --long查看更详细的信息甚至插拔设备观察哪个设备消失了来确认。2.2 用xrandr确认各显示输出的名称和位置第二步是确认当前显示输出的名称、分辨率和在虚拟桌面中的位置偏移量。xrandr输出会列出所有显卡输出接口和当前接入的显示器关键信息是接口名称、分辨率、坐标偏移。典型的双屏输出大概是这样Screen 0: minimum 320 x 200, current 4480 x 1440, maximum 16384 x 16384 HDMI-1 connected 1920x108000 (normal left inverted right x axis y axis) 477mm x 268mm DP-1 connected 2560x144019200 (normal left inverted right x axis y axis) 597mm x 336mm这里有两个关键信息1920x108000表示HDMI-1这块屏的分辨率是1920x1080起点在虚拟桌面坐标的(0,0)2560x144019200表示DP-1的分辨率是2560x1440起点在x1920的位置。那么虚拟桌面总宽度就是192025604480总高度为1440。拿到这个信息我们才能计算坐标变换矩阵里的比例因子。特别提醒两个屏的物理排列顺序不一定等于系统设置里的排列顺序一切以xrandr输出的偏移量为准。如果副屏的号偏移是0那它在虚拟桌面里的位置就是左边哪怕它物理上放在右边。我遇到过客户把物理副屏放在右边但系统设置里拖成了左排列结果xrandr里偏移量是0这就需要在桌面设置里先调整屏的排列顺序再回来算坐标矩阵。2.3 记录当前坐标变换矩阵的“原始状态”第三步是查看触摸设备当前的坐标变换矩阵确认是不是默认状态好为后续修改和恢复留一个底。xinput --list-props 设备名输出里找Coordinate Transformation Matrix这一行默认情况下是单位矩阵Coordinate Transformation Matrix (500): 1.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 1.000000如果你看到的矩阵不是单位矩阵说明之前有人改过或者系统里存在其他配置文件这种情况要先记录下来别急着覆盖。我的习惯是把这一行完整复制到终端里保存或者用笔记记下来。万一改错了还能一条命令恢复原状xinput set-prop 设备名 Coordinate Transformation Matrix 1 0 0 0 1 0 0 0 1另外注意属性后面的数字编号(500)每个设备可能不同使用属性名操作最稳妥尽量别直接用编号。3. 核心解法Coordinate Transformation Matrix怎么算、怎么用3.1 这个3x3矩阵到底在算什么Coordinate Transformation Matrix是Xorg给绝对坐标输入设备提供的一个通用属性作用是把设备上报的归一化坐标做一个二维仿射变换变换后的结果才是光标在虚拟桌面上的实际位置。矩阵是3x3结构实际起作用的参数是前两行的三个值参数作用默认值c0X方向缩放比例1c1X方向旋转/斜切系数0c2X方向平移量0c3Y方向旋转/斜切系数0c4Y方向缩放比例1c5Y方向平移量0变换公式实际上就是光标X坐标 c0 × 触摸X归一化坐标 c1 × 触摸Y归一化坐标 c2光标Y坐标 c3 × 触摸X归一化坐标 c4 × 触摸Y归一化坐标 c5对大多数双屏左右排列的场景我们不涉及旋转和斜切c1和c3始终设为0。核心要算的就是c0X方向缩放、c2X方向平移、c4Y方向缩放、c5Y方向平移这四个值。理解了矩阵的作用就能明白为什么单位矩阵会出问题单位矩阵意味着触摸归一化坐标完全不缩放、不平移地映射到整个虚拟桌面。如果虚拟桌面宽度是4480而触摸屏对应的输出宽度只有1920那手指滑动整个触摸屏的宽度光标却要滑过4480的宽度比例自然错了。3.2 左右双屏的计算方法与完整命令现在以最常见的左右双屏布局为例逐步计算并设置矩阵。假设xrandr输出结果是HDMI-11920x1080偏移为00物理上在左侧且这正是触摸屏对应的输出DP-12560x1440偏移为19200在右侧虚拟桌面总宽度 1920 2560 4480总高度 max(1080, 1440) 1440左右排列时高度不叠加。触摸屏对应左侧的HDMI-1要把触摸坐标映射到虚拟桌面x0到x1920这个区间内c0X缩放 目标宽度 / 虚拟桌面总宽度 1920 / 4480 ≈ 0.4286c2X平移 触摸屏在虚拟桌面中的起点X / 总宽度 0 / 4480 0c4Y缩放 目标高度 / 虚拟桌面总高度 1080 / 1440 0.75c5Y平移 触摸屏起点Y偏移量 / 总高度 0 / 1440 0对应命令xinput set-prop 设备名 Coordinate Transformation Matrix 0.4286 0 0 0 0.75 0 0 0 1执行完这一条命令触摸屏会立即生效。用手指点触摸屏右上角光标应该落在左边屏幕的右上角点右下角光标落在左边屏幕的右下角。如果触摸屏对应的是右侧的DP-1公式稍有变化c0 2560 / 4480 ≈ 0.5714c2 1920 / 4480 ≈ 0.4286因为右侧屏的起点坐标是1920需要把触摸坐标整体平移到这个位置c4 1440 / 1440 1因为右侧屏高度和虚拟桌面高度一致c5 0命令xinput set-prop 设备名 Coordinate Transformation Matrix 0.5714 0 0.4286 0 1 0 0 0 1注意这里的写法9个数字的顺序是矩阵按行展开第1行3个、第2行3个、第3行3个不要写错位置。3.3 副屏在右、在上下位置时的矩阵调整再看副屏在右侧的情况假设xrandr输出是DP-12560x1440偏移00HDMI-11920x1080偏移25600虚拟桌面宽度仍然是4480触摸屏对应右侧HDMI-1时c0 1920 / 4480 ≈ 0.4286c2 2560 / 4480 ≈ 0.5714c4 1080 / 1440 0.75c5 0命令xinput set-prop 设备名 Coordinate Transformation Matrix 0.4286 0 0.5714 0 0.75 0 0 0 1上下排列的情况也不难。假设主屏是下方的2560x1440副屏在上方是1920x1080虚拟桌面宽度取最大值2560总高度是108014402520。副屏对应触摸屏起点在y1440c0 1920 / 2560 0.75c2 0c4 1080 / 2520 ≈ 0.4286c5 1440 / 2520 ≈ 0.5714命令xinput set-prop 设备名 Coordinate Transformation Matrix 0.75 0 0 0 0.4286 0.5714 0 0 1如果触摸屏对应下方的主屏则c0不变c20c41440/2520≈0.5714c50。总结成口诀就是缩放比例 目标输出尺寸 / 虚拟桌面对应方向总尺寸平移量 目标输出起点偏移 / 虚拟桌面对应方向总尺寸。3.4 触摸屏对不上显示输出时如何判断对应关系有些设备接线比较乱搞不清楚哪个触摸屏对应哪个显示输出。最直接的办法是“触摸四角探路法”先用xinput给触摸设备设置一个占满全桌面的映射比如单位矩阵确保触摸坐标能映射到虚拟桌面。用手指轻轻点触摸屏的左上角观察光标落在哪个屏幕的什么位置。再用手指点触摸屏的右下角观察光标落点。如果光标落在主屏左上角说明触摸屏对应的输出就是主屏如果光标落在副屏的某个位置说明对应关系是副屏。这个方法虽然土但非常有效尤其是接口转接多次、线序不明的情况下。判断出触摸屏对应的输出后再用xrandr去查这个输出的分辨率和偏移量代入上面的公式计算即可。4. 让配置在重启后依然生效三种持久化方式对比xinput set-prop只对当前会话生效重启系统后一切回到解放前。要让配置在每次开机时自动应用有三种常见方案各有适用场景。4.1 启动脚本方案适合快速投用最简单直接的方式把配置命令写进一个开机自启脚本。创建一个脚本文件比如~/touchscreen-map.sh#!/bin/bash xinput set-prop ELAN2514:00 04F3:2A56 Touchscreen Coordinate Transformation Matrix 0.4286 0 0 0 0.75 0 0 0 1给脚本加执行权限chmod x ~/touchscreen-map.sh然后在GNOME的“启动应用程序偏好设置”里添加一条启动项命令填脚本的绝对路径。或者在~/.config/autostart/目录下创建一个.desktop文件[Desktop Entry] TypeApplication NameTouchscreen Map Exec/home/你的用户名/touchscreen-map.sh X-GNOME-Autostart-enabledtrue这个方案的好处是只影响当前用户不会改动系统全局配置出问题时删掉自启动项即可恢复。缺点是登录界面GDM里触摸映射不会生效另外如果脚本执行时X服务还没完全初始化偶尔会出现命令执行失败的情况可以在脚本里加一个简短的延迟#!/bin/bash sleep 2 xinput set-prop ...4.2 xorg.conf.d方案连登录界面也照顾到如果希望登录界面和所有用户都生效最好的方式是写Xorg配置文件。在/etc/X11/xorg.conf.d/目录下新建一个配置文件比如50-touchscreen.confSection InputClass Identifier touchscreen map MatchIsTouchscreen on MatchProduct ELAN2514:00 04F3:2A56 Option TransformationMatrix 0.4286 0 0 0 0.75 0 0 0 1 EndSection修改后保存重启X服务或者重启系统生效。这个方案有几点需要注意MatchProduct里的设备名是xinput list显示名称的一部分不用写全但要确保能唯一匹配。如果系统里只有一块触摸屏只写MatchIsTouchscreen on就够了如果有多块触摸屏且需要不同映射每一块都要分别写一个Section用MatchProduct区分。Option TransformationMatrix的值和xinput set-prop的9个参数是一一对应的直接复制即可。还要提醒一下使用这个方案后不要再同时使用启动脚本方案否则Xorg里的配置会被登录后的xinput命令再覆盖一次反而可能出现两套配置打架的情况。4.3 udev方案适合外接触摸屏如果你是USB外接触摸屏希望插入设备时自动应用配置可以用udev规则。新建规则文件/etc/udev/rules.d/99-touchscreen.rulesACTIONadd, SUBSYSTEMinput, ATTRS{name}HID 04D9:8009 TouchScreen, RUN/usr/local/bin/touchscreen-map.sh然后在/usr/local/bin/touchscreen-map.sh里写#!/bin/bash export DISPLAY:0 export XAUTHORITY/home/你的用户名/.Xauthority sleep 3 xinput set-prop HID 04D9:8009 TouchScreen Coordinate Transformation Matrix 0.4286 0 0 0 0.75 0 0 0 1脚本里的DISPLAY和XAUTHORITY必须设置正确否则udev环境下xinput命令无法连接到X服务。sleep 3是为了等待X服务和输入设备完全初始化。这个方案适合触摸屏经常热插拔的场景但对内置触摸屏作用不大而且GDM登录界面同样不生效。新手朋友我建议优先用方案一或方案二udev方案适合有一定Linux经验的人折腾。4.4 持久化前必须知道的优先级问题实际项目里最头疼的不是怎么配置而是配置之间互相干扰。如果系统里同时存在xorg.conf.d配置和用户自启动脚本最终生效的是后执行的那个。xorg.conf.d在X启动时加载xinput脚本在用户登录后才执行所以登录后桌面里的触摸映射以xinput脚本为准登录界面以xorg.conf.d为准。如果你期望两边一致内容必须保持一致。另外部分桌面环境特别是GNOME在登录后会根据显示器设置重置输入设备的坐标变换矩阵。如果重启后发现映射又变乱了优先检查系统显示设置里屏幕的排列方向是否和xrandr输出一致以及是否有“显示器配置”相关的自启动工具在后台运行。5. 实测踩坑复盘五个容易被忽视的问题5.1 Wayland会话下的“无效命令”陷阱这是很多朋友会卡住的第一步。Ubuntu 22.04默认登录界面可以选择“Ubuntu”Wayland会话和“Ubuntu on Xorg”Xorg会话。在Wayland会话下xrandr输出可能正常但xinput某些属性设置不会生效甚至报unable to find device错误。如果你在终端执行xinput set-prop后没有任何报错但触摸映射完全没变先检查当前会话类型echo $XDG_SESSION_TYPE输出是wayland说明你在Wayland下。最简单的方法是注销在登录界面点击用户名后右下角齿轮图标选择“Ubuntu on Xorg”再登录。本篇文章的所有方案都以Xorg为基础Wayland下坐标映射机制由合成器管理行为完全不同建议先切到Xorg再处理。5.2 设备坐标范围和校准矩阵的区别有时候坐标变换矩阵算得完全正确公式也对但触摸点位总是有固定的几毫米偏移点按钮时总觉得偏左或偏下。这通常是触摸屏本身的设备坐标没有和物理屏幕完全对齐属于设备校准层面的问题而不是映射问题。libinput驱动下可以查看是否支持校准矩阵属性xinput --list-props 设备名 | grep -i calibration如果输出里有libinput Calibration Matrix属性可以尝试用xinput_calibrator工具做一次精确校准。安装方式sudo apt install xinput-calibrator校准完会输出几行建议配置把输出的校准矩阵写入xorg.conf.d即可。需要说明的是这个校准矩阵与坐标变换矩阵是两回事前者负责把不规范的设备坐标修正到0-1区间后者负责把归一化坐标映射到正确的显示输出。两步不冲突可以先校准再映射。但注意xinput_calibrator主要针对evdev驱动libinput下部分场景兼容性一般如果校准工具不识别设备说明厂家驱动已经做了归一化此时不要强行校准。5.3 两台触摸屏设备同时存在时笔记本外接触摸显示器是最典型的双触屏场景笔记本内置触摸屏对应笔记本屏幕外接触摸显示器对应另一个输出。这时候必须分别设置两个触摸设备的坐标变换矩阵每个设备对应各自的输出。先用xinput list确认两个触摸设备的名称再用上面第3章的公式分别计算矩阵。比如内置触摸屏对应内置屏幕偏移为00矩阵通常保持默认即可外接触摸屏对应副屏就按副屏在虚拟桌面中的位置计算平移和缩放。配置文件里写两个Section分别匹配Section InputClass Identifier builtin touchscreen MatchIsTouchscreen on MatchProduct ELAN2514 Option TransformationMatrix 1 0 0 0 1 0 0 0 1 EndSection Section InputClass Identifier external touchscreen MatchIsTouchscreen on MatchProduct HID 04D9 Option TransformationMatrix 0.4286 0 0.5714 0 1 0 0 0 1 EndSection这个场景下最容易犯的错是把两个设备都写成一个矩阵配置导致一块屏正常、另一块屏错乱。5.4 用“故意设错”的方式验证矩阵思路设置完成后如果还是不对我推荐一个很实用的验证方法故意设置一个明显错误的映射比如只把触摸屏映射到虚拟桌面左侧四分之一的区域xinput set-prop 设备名 Coordinate Transformation Matrix 0.25 0 0 0 0.25 0 0 0 1然后在触摸屏上滑动手指如果光标只在虚拟桌面左上四分之一区域移动说明驱动和属性链路完全正常问题纯粹是数值计算错误重新按公式算即可。如果改成这个值后光标依然在整个桌面乱跑说明矩阵属性根本没有生效问题出在设备名错误、会话类型不对、或者驱动不响应这个属性。我自己处理问题时经常用这个方法快速定位故障层比一遍遍猜矩阵参数高效得多。5.5 旋转屏幕和热插拔后配置失效最后说一下两个容易忽略的场景。如果副屏是竖屏通过xrandr旋转了90度坐标变换矩阵的计算就不仅仅是平移缩放了还需要考虑旋转公式会复杂很多。建议这种场景先把屏幕改成正常方向或在xrandr里旋转输出后重新读取偏移量再按偏移量设置矩阵。对于必须竖屏的工控设备建议在代码里单独处理不做通用化配置。热插拔问题出现在HDMI线松动、屏电源重启后显示输出重新枚举的场景这时xrandr里的输出名称和偏移可能变化之前固定的矩阵配置会失配。解决方法是在生产环境里把配置脚本做成轮询检测模式开机后循环检查xrandr的输出布局检测到偏移量变化就重新计算并设置矩阵脚本里用sleep循环即可实现不需要装额外软件。结束前再分享一点个人经验处理双屏触屏问题多了之后我的习惯已经固定成一套流程先查环境变量确认Xorg再用xinput确认设备和原始矩阵用xrandr确认输出偏移用手指四角探路确认触摸屏对应的输出最后套公式算矩阵。这个流程看着繁琐但每一步都在排除一类变量实际操作起来不到十分钟。比起盲目抄网上命令、反复重启测试这个流程节省的时间不是一点半点。另外强烈建议每台设备改完配置后把xrandr输出和最终的矩阵配置记在设备标签上方便后期维护——尤其是做过很多台同样设备的项目设备间接口顺序可能不同靠记忆极易搞混。祝大家都能一次配通。