我决定暂时离开科技领域,尝试一些新东西,比如数字绘画。于是我从柜子底部翻出了绘图板(它在那里积灰好几年了)。
果不其然,我的 Linux 配置一开始就拒绝识别它。
我想用 Krita 画画,于是通过 USB 把绘图板连接到笔记本电脑。绘图板上的指示灯亮了起来,我天真地以为这是个好兆头。于是我拿起手写笔开始画。
瞬间失望。
我在绘图板上用手写笔做的任何操作,都没有反映到显示器上。我想到故障可能出在两个环节之一。
- Linux 没有检测到绘图板。
- 绘图板的输入没有被检测到。
我通过 lsusb确认了第一种情况,并清楚地看到:
Bus 003 Device 006: ID 2feb:0006 Beijing Veikk E-Commerce Co., Ltd. A15PRO
这样就把问题缩小到了第二种情况。
为了确认调试方向正确,我追踪了手写笔输入的路径:从笔尖接触绘图板开始,到输入最终在 Krita 中呈现为止。
下面有些细节可能只适用于我的系统,配置如下:
操作系统:Arch Linux 内核:7.1.5-zen1-1-zen libinput:1.31.3 sway 版本:1.12 绘图板:VEIKK A15 Pro
下面说说它应该如何工作:
- 手写笔检测位置(X、Y)和压力的变化,并通过 USB 发送这些输入。
- 绘图板使用 USB HID(人机接口设备)协议进行通信。因此,设备连接时会向 Linux 提供 HID 报告描述符。这个描述符说明数据包的布局,规定每个位或字节代表什么。
- Linux 有一个名为 usbhid 的内核级驱动,负责处理 USB HID 通信。绘图板通过 USB 发送 HID 输入报告,Linux HID 子系统则根据设备提供的 HID 报告描述符接收并解析这些报告。
- Linux 有一个通用输入子系统,会将这些 HID 报告转换为标准化输入事件。对我的绘图板来说,它会提供 ABS_X、ABS_Y、ABS_PRESSURE、BTN_TOOL_PEN、BTN_TOUCH、BTN_STYLUS 等事件。
- Linux 输入子系统通过 evdev 接口提供这些标准化事件。Evdev(事件设备)是 Linux 内核提供的通用接口,用于向用户空间提供输入事件。在本例中,手写笔以 /dev/input/event19 的形式暴露给用户空间,因此 evtest 等程序可以读取这些输入事件。
- libinput 使用这些输入设备,并根据设备类型进行处理。libinput 是一个计算机库,用于在 Linux 上为显示服务器和图形环境处理输入设备。它充当 Linux 内核与桌面界面之间的桥梁。
- libinput 用户空间库会解析这些事件,然后将其发送给 Sway 合成器。随后,这些事件作为输入事件传递给 Krita 应用程序。
那么,我们就逐层调试这个错误。
首先,检查 USB HID 设备是否绑定到了内核的 usbhid 驱动。
要检查任何内容,首先需要找到存放手写笔输入设备文件的位置(Linux 中一切皆文件!)。因此,我们用下面的命令检查 Linux 当前检测到的所有输入设备:
cat /proc/bus/input/devices
我找到了 Linux 能看到的所有输入设备的信息,其中包括手写笔:

这表明输入手写笔的处理程序是“event19”。
那么,如何访问与这个特定输入处理程序相关的文件呢?
Linux 有一个名为 /dev 的设备文件系统。内核暴露硬件接口时,会在其中创建设备节点。对于输入设备,内核的 evdev 使用以下命名约定:
/dev/input/event19
接下来,我用下面的命令验证 usbhid:
udevadm info /dev/input/event19
结果如下:
ID_USB_DRIVER=usbhid
由此确认,USB HID 设备已绑定到内核的 usbhid 驱动。
接下来是位于 /sys/class/input/event19/device/device/report_descriptor 的 HID 描述符。

绘图板的 HID 描述符实际上告诉 Linux 这是支笔,并描述了 X、Y、压力、按键等信息。
接下来是 evdev 层,我们用下面的命令检查:
sudo evtest /dev/input/event19
运行此命令后,移动手写笔会产生不断变化的 X、Y 和压力事件。

因此,直到这一层为止,我们都能清楚地收到输入。
接下来是 libinput 层。我们要检查 libinput 是否将它识别为可用的绘图板。
为此,我们运行了:
sudo libinput list-devices
运行后,终端输出了:
VEIKK.INC A15PRO Stylus:
libinput bug: missing tablet capabilities: resolution.
Ignoring this device.
现在我们找到了问题所在。
Libinput 需要某些元数据,如果缺少这些信息,就会忽略设备。本例中缺少的是“resolution”(分辨率)。
在此语境下,轴分辨率指每毫米的物理移动对应多少坐标单位。例如,分辨率为 129 单位/毫米,意味着手写笔在绘图板上移动 1 毫米,对应 ABS_X 坐标范围内大约 129 个单位。
那么,分辨率去哪儿了?
我们再回头看看之前的报告描述符,因为所有元数据最初都来自那里。
再看一遍,似乎有些地方不对劲。
“Unit = 0x33, Unit Exponent = -3”是什么意思?
hid-decode 将其解读为:
Unit (EnglishLinear: in³)
所以绘图板用立方单位测量 X 和 Y?这听起来不太对。
0x33 是一个打包的 HID 单位值。低位半字节描述单位系统,后续位则编码各维度的指数。在这种编码中,0x13 表示英寸,而 0x33 表示长度指数为 3 的英寸,hid-decode 将其显示为 EnglishLinear: in³。
这对 X/Y 坐标来说很可疑:X 坐标是长度,因此立方单位在这里毫无道理。HID 规范在 6.2.2.7 节定义了这种单位编码,内核源码引用的也是这一节。
但关键问题不只是 0x33 看起来奇怪。Linux 内核实际上会如何处理它?
我们怎么会因此得到没有分辨率的结果?
为此,我们需要看看 Linux 如何根据这个 HID 描述符计算分辨率。
hidinput_calc_abs_res() - 此函数负责根据 HID 描述符提供的信息计算绝对输入轴的分辨率。
内核要求 X/Y 单位必须是它支持的长度表示形式之一。我的绘图板报告的单位是 0x33,hid-decode 将其解读为 EnglishLinear: in³。内核的 X/Y 分辨率计算不接受这种单位表示形式,因此计算结果为零。
switch (code) {
case ABS_X:
case ABS_Y:
case ABS_Z:
case ABS_MT_POSITION_X:
case ABS_MT_POSITION_Y:
case ABS_MT_TOOL_X:
case ABS_MT_TOOL_Y:
case ABS_MT_TOUCH_MAJOR:
case ABS_MT_TOUCH_MINOR:
if (field->unit == 0x11) { /* If centimeters */
/* Convert to millimeters */
unit_exponent += 1;
} else if (field->unit == 0x13) { /* If inches */
/* Convert to millimeters */
prev = physical_extents;
physical_extents *= 254;
if (physical_extents < prev)
return 0;
unit_exponent -= 1;
} else {
return 0;
}
break;
}
我们可以用下面的命令交叉验证 Linux 看到的分辨率:
sudo evemu-describe /dev/input/event19
结果如下:
(原图获取失败,本处不展示图片。)
两个轴的分辨率都报告为 0,这证实了我们的追踪结果。那么,该如何修复?
一种常用解决方案是另找一种途径,绕过 libinput 向 Krita 应用程序提供输入。我在这里使用 OpenTabletDriver 作为替代方案。
OTD 直接与绘图板通信,并通过自身的绘图板支持来解析其报告。
我成功安装了驱动,问题也因此解决。
不过,如果我们直接向用户空间提供修正后的设备元数据,会怎么样?
我可以从这里提供的硬件规格中获取正确的分辨率:https://veikk.com/en-in/products/creator-a15pro
我们可以根据官方给出的 10x6 英寸有效区域(254 x 152.4 毫米)以及 0-32767 的逻辑范围来设置分辨率(逻辑范围来自描述符):
X:32767 / 254 = 129 单位/毫米
Y:32767 / 152.4 = 215 单位/毫米
设备出现时,如何向 libinput 提供这些元数据?
通常作为第一个用户空间进程(PID 1)运行的 systemd,包含一个硬件专用元数据修正数据库,udev 会在设备出现时将其中的信息应用到设备上。这个数据库叫作 systemd-hwdb。
于是我创建了这个文件的本地版本:/etc/udev/hwdb.d/61-evdev-local.hwdb,并在其中添加了以下内容:
evdev:input:b0003v2FEBp0006*
EVDEV_ABS_00=::129
EVDEV_ABS_01=::215
各部分的含义如下:
evdev:input: - 匹配一个 evdev 输入设备
b0003 - USB 总线
v2FEB - VEIKK 厂商 ID
p0006 - A15 Pro 产品 ID
* - 其余 modalias 字段可以是任意值
EVDEV_ABS_00=::129 表示最小值和最大值保持不变,分辨率设为 129。
然后我重建并重新加载了数据库:
sudo systemd-hwdb update
sudo udevadm trigger /dev/input/event19
完成这些操作后,Krita 就能检测到手写笔输入。分辨率的变化可以在 udev 层看到,libinput 也不再抛出之前的错误。
确认它在我的机器上正常工作后,我向 systemd 提交了包含相同条目的拉取请求。该请求已合并:https://github.com/systemd/systemd/pull/43962.
因此,如果你有 VEIKK A15 Pro,那么只要包含此修复的 systemd 版本发布,你就不需要做这些操作了。
回头看,问题本身比排查过程简单得多。绘图板的描述符将 X/Y 轴声明为内核不接受的单位,因此分辨率变成了零,libinput 也就拒绝了这个设备。这甚至不是改一个字节就能解决的。描述符还给两个轴都设置了相同的物理最大值 8000;即使单位修正了,那也会对应一个边长为 8 英寸的正方形,而实际绘画区域是 10Ã6。因此我不能只修补单位,而必须从规格表中查出真实尺寸。
如果你的另一款绘图板也有相同症状
通用处理步骤如下:
用 lsusb 获取厂商 ID 和产品 ID。
从制造商的规格中找到真实的有效区域尺寸,单位为毫米。
将每个轴的逻辑最大值(来自 evtest 或描述符)除以该轴的毫米尺寸。这就是以单位/毫米表示的分辨率。
在匹配设备的 hwdb 条目中填入这些数值,然后运行 sudo systemd-hwdb update 并触发设备。
我仍然不知道 Windows 为什么从未遇到这个问题。它可能会容忍这个描述符,也可能是 VEIKK 自己的驱动绕过了问题。我没有测试过是哪一种,所以就把它留作一个疑问。
这块在柜子底部待了好几年的绘图板表现还不错。现在我只需要学会怎么画画了。