V2-202509
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
# check_save.c - 保存确认对话框
|
||||
|
||||
## 概述
|
||||
这是一个GTK+图形界面程序,用于在多显示器环境下显示保存确认对话框。用户可以在任意显示器上点击"保存"或"取消"按钮,程序会返回相应的退出代码供调用方判断。
|
||||
|
||||
## 程序结构
|
||||
|
||||
### 数据结构
|
||||
```c
|
||||
typedef struct {
|
||||
GtkWidget *window; // GTK窗口指针
|
||||
int monitor_num; // 显示器编号
|
||||
int x, y; // 显示器位置
|
||||
int width, height; // 显示器分辨率
|
||||
char *name; // 显示器名称/接口名
|
||||
} MonitorInfo;
|
||||
```
|
||||
|
||||
### 全局变量
|
||||
- `monitors`: 显示器信息数组
|
||||
- `monitor_count`: 显示器数量
|
||||
- `app`: GTK应用程序实例
|
||||
- `exit_code`: 退出代码(默认1=取消)
|
||||
|
||||
## 工作原理
|
||||
|
||||
1. **初始化阶段**: 检查DISPLAY环境变量,创建GTK应用
|
||||
2. **显示器枚举**: 使用`gdk_screen_get_n_monitors()`获取所有显示器信息
|
||||
3. **窗口创建**: 为每个显示器创建一个400x200的居中确认窗口
|
||||
4. **用户交互**:
|
||||
- 点击"保存" → 打印"0",退出代码=0
|
||||
- 点击"取消并退出" → 打印"1",退出代码=1
|
||||
- 关闭窗口 → 打印"1",退出代码=1
|
||||
|
||||
## 关键函数
|
||||
|
||||
| 函数 | 功能 |
|
||||
|------|------|
|
||||
| `activate()` | 应用启动回调,枚举显示器并创建窗口 |
|
||||
| `create_monitor_window()` | 为指定显示器创建确认窗口 |
|
||||
| `on_save_clicked()` | 保存按钮回调 |
|
||||
| `on_cancel_clicked()` | 取消按钮回调 |
|
||||
| `show_error_dialog()` | 显示错误对话框 |
|
||||
|
||||
## 调用方式
|
||||
```bash
|
||||
./check_save
|
||||
# 返回值: 0=保存, 1=取消
|
||||
```
|
||||
|
||||
## 依赖
|
||||
- GTK+ 3.x
|
||||
- GDK (含X11后端)
|
||||
@@ -0,0 +1,53 @@
|
||||
# demo.c - 多屏幕信息窗口演示程序
|
||||
|
||||
## 概述
|
||||
一个基于GTK+和Xrandr的多屏幕信息演示程序。在所有连接的显示器上依次创建信息窗口,显示屏幕分辨率、位置等信息,支持多轮测试和窗口位置校验。
|
||||
|
||||
## 程序结构
|
||||
|
||||
### 全局变量
|
||||
| 变量 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `info_windows` | `GtkWidget**` | 各屏幕信息窗口指针数组 |
|
||||
| `total_screens` | `int` | 连接的显示器总数 |
|
||||
| `screen_widths/heights` | `int*` | 各屏幕宽高 |
|
||||
| `screen_x_offsets/y_offsets` | `int*` | 各屏幕坐标偏移 |
|
||||
| `screen_names` | `char**` | 屏幕接口名(如HDMI-1) |
|
||||
| `current_test_round` | `int` | 当前测试轮次 |
|
||||
| `total_test_rounds` | `int` | 总测试轮次 |
|
||||
| `win_time_delay` | `int` | 窗口创建间隔(ms),默认50 |
|
||||
|
||||
## 工作原理
|
||||
|
||||
1. **屏幕信息获取**: 使用Xrandr扩展查询所有已连接的输出设备及其CRTC信息
|
||||
2. **窗口创建**: 每个屏幕创建一个比屏幕尺寸小20像素的窗口,偏移(10,10)
|
||||
3. **定时创建**: 窗口按`win_time_delay`毫秒间隔依次创建
|
||||
4. **空格键控制**: 用户按空格键关闭所有窗口,进入下一轮测试
|
||||
5. **窗口校验**: 每轮结束后校验窗口位置和大小(允许±5像素误差)
|
||||
6. **日志记录**: 所有操作记录到`screen_demo.log`文件
|
||||
|
||||
## 关键函数
|
||||
|
||||
| 函数 | 功能 |
|
||||
|------|------|
|
||||
| `get_screen_info()` | 通过Xrandr获取所有屏幕信息 |
|
||||
| `create_info_window()` | 创建单个屏幕的信息窗口 |
|
||||
| `create_next_window()` | 定时回调,依次创建窗口 |
|
||||
| `verify_windows()` | 校验窗口位置和大小 |
|
||||
| `start_next_test_round()` | 开始新一轮测试 |
|
||||
| `on_key_press()` | 空格键事件处理 |
|
||||
|
||||
## 窗口布局
|
||||
```
|
||||
窗口位置: (screen_x + 10, screen_y + 10)
|
||||
窗口大小: (screen_width - 20) x (screen_height - 20)
|
||||
```
|
||||
|
||||
## 调用方式
|
||||
```bash
|
||||
./demo
|
||||
```
|
||||
|
||||
## 依赖
|
||||
- GTK+ 3.x
|
||||
- X11 + Xrandr扩展
|
||||
@@ -0,0 +1,26 @@
|
||||
# gdk_test.c - GDK类型测试
|
||||
|
||||
## 概述
|
||||
一个极简的编译测试程序,用于验证`GdkMonitor`类型在当前GTK/GDK版本中是否可用。
|
||||
|
||||
## 代码
|
||||
```c
|
||||
#include <gdk/gdk.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
gtk_init(&argc, &argv);
|
||||
GdkMonitor *monitor; // 仅测试该类型是否可识别
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## 用途
|
||||
该文件不包含实际功能逻辑,仅用于:
|
||||
- 验证编译环境中GDK头文件是否包含`GdkMonitor`类型定义
|
||||
- 确认GTK版本是否支持该类型(GTK 3.22+引入)
|
||||
|
||||
## 编译测试
|
||||
```bash
|
||||
gcc gdk_test.c -o gdk_test $(pkg-config --cflags --libs gtk+-3.0)
|
||||
```
|
||||
@@ -0,0 +1,89 @@
|
||||
# screen_binder.c - 触摸屏与显示器绑定程序
|
||||
|
||||
## 概述
|
||||
核心模块之一,用于将触摸屏设备与显示器进行一一绑定。程序在每个显示器上依次弹出校准窗口,用户点击触摸屏后,程序记录触摸设备与当前显示器的对应关系,最终保存到`touch_dis_table.txt`文件。
|
||||
|
||||
## 程序结构
|
||||
|
||||
### 数据结构
|
||||
```c
|
||||
// 屏幕信息
|
||||
int *screen_widths, *screen_heights; // 屏幕宽高
|
||||
int *screen_x_offsets, *screen_y_offsets; // 屏幕坐标
|
||||
char **screen_names; // 屏幕接口名
|
||||
|
||||
// 触摸绑定信息
|
||||
int *touch_bindings; // 每个屏幕绑定的触摸设备ID
|
||||
char **touch_names; // 触摸设备名称
|
||||
char **touch_paths; // 触摸设备路径
|
||||
```
|
||||
|
||||
### 线程模型
|
||||
| 线程 | 功能 |
|
||||
|------|------|
|
||||
| 主线程 | GTK事件循环,窗口管理 |
|
||||
| `touch_thread` | 触摸事件监听线程 |
|
||||
| `enter_thread` | 全局回车键监听线程(X11) |
|
||||
|
||||
### 原子变量(线程安全)
|
||||
```c
|
||||
_Atomic int global_enter_pressed; // 全局回车键按下标志
|
||||
_Atomic int program_active; // 程序活动状态
|
||||
_Atomic int calibration_active; // 校准进行中标志
|
||||
```
|
||||
|
||||
## 工作原理
|
||||
|
||||
1. **屏幕枚举**: 通过Xrandr获取所有已连接显示器
|
||||
2. **触摸初始化**: 初始化触摸监听器,启动触摸事件监听线程
|
||||
3. **全局回车监听**: 启动独立线程监听全局回车键(用于跳过非触摸屏)
|
||||
4. **信息窗口创建**: 依次为每个非当前校准屏幕创建提示窗口
|
||||
5. **校准流程**:
|
||||
- 在当前屏幕创建全屏校准窗口
|
||||
- 等待用户点击触摸屏(60秒超时)
|
||||
- 记录触摸设备ID、名称、路径
|
||||
- 用户按回车键可跳过当前屏幕
|
||||
6. **保存结果**: 将绑定关系保存到`touch_dis_table.txt`
|
||||
|
||||
## 校准窗口交互
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ 请在触摸屏上点击此屏幕。 │
|
||||
│ │
|
||||
│ 屏幕接口: HDMI-1 │
|
||||
│ │
|
||||
│ 如果此屏幕不是触摸屏, │
|
||||
│ 那么请敲击回车或者等待60秒。 │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 关键函数
|
||||
|
||||
| 函数 | 功能 |
|
||||
|------|------|
|
||||
| `get_screen_info()` | Xrandr获取屏幕信息 |
|
||||
| `init_touch_listener()` | 初始化触摸监听 |
|
||||
| `touch_callback()` | 触摸事件回调,记录绑定 |
|
||||
| `global_enter_listener_thread()` | X11全局回车键监听 |
|
||||
| `show_next_screen()` | 显示下一个校准屏幕 |
|
||||
| `save_mapping_table()` | 保存绑定关系到文件 |
|
||||
| `create_calibration_window()` | 创建校准窗口 |
|
||||
| `create_info_window()` | 创建信息提示窗口 |
|
||||
|
||||
## 输出格式
|
||||
文件`touch_dis_table.txt`格式:
|
||||
```
|
||||
屏幕接口名|触摸设备名|触摸设备ID|设备路径
|
||||
HDMI-1|USB Touchscreen|12345|/dev/input/event5
|
||||
```
|
||||
|
||||
## 调用方式
|
||||
```bash
|
||||
sudo ./screen_binder # 需要sudo权限访问触摸设备
|
||||
```
|
||||
|
||||
## 依赖
|
||||
- GTK+ 3.x
|
||||
- X11 + Xrandr扩展
|
||||
- touch_listen模块(触摸事件监听)
|
||||
- 需要root权限访问`/dev/input/`设备
|
||||
@@ -0,0 +1,70 @@
|
||||
# screen_ds.c - 显示器信息持续监控程序
|
||||
|
||||
## 概述
|
||||
一个基于Xrandr的显示器状态持续监控程序。持续监听显示器的连接/断开事件,当检测到变化时(防抖3秒后)输出所有显示器的当前信息。
|
||||
|
||||
## 程序结构
|
||||
|
||||
### 数据结构
|
||||
```c
|
||||
typedef struct {
|
||||
char* output_name; // 输出接口名(如HDMI-1)
|
||||
int width, height; // 分辨率
|
||||
int x, y; // 位置坐标
|
||||
int rotation; // 旋转方向
|
||||
} MonitorInfo;
|
||||
|
||||
typedef struct {
|
||||
MonitorInfo* monitors; // 显示器数组
|
||||
int count; // 显示器数量
|
||||
} MonitorList;
|
||||
```
|
||||
|
||||
### 旋转方向编码
|
||||
| 值 | 字母 | 说明 |
|
||||
|----|------|------|
|
||||
| RR_Rotate_0 | N | 正常方向 |
|
||||
| RR_Rotate_90 | L | 左旋转90度 |
|
||||
| RR_Rotate_180 | I | 翻转180度 |
|
||||
| RR_Rotate_270 | R | 右旋转90度 |
|
||||
|
||||
## 工作原理
|
||||
|
||||
1. **初始化**: 打开X显示连接,注册RandR事件监听
|
||||
2. **首次输出**: 获取并输出当前所有显示器信息
|
||||
3. **事件循环**:
|
||||
- 监听`RRScreenChangeNotify`和`RRNotify`事件
|
||||
- 检测到事件后设置`need_update`标志
|
||||
- 防抖3秒后(`DEBOUNCE_TIME`)重新获取并输出显示器信息
|
||||
4. **无事件时**: 休眠100ms减少CPU占用
|
||||
|
||||
## 输出格式
|
||||
```
|
||||
HDMI-1|1920x1080|0x0|N
|
||||
DP-2|1920x1080|1920x0|N
|
||||
```
|
||||
每行格式: `接口名|分辨率|位置|旋转方向`
|
||||
|
||||
## 关键函数
|
||||
|
||||
| 函数 | 功能 |
|
||||
|------|------|
|
||||
| `get_monitor_info()` | 获取所有连接的显示器信息 |
|
||||
| `output_monitor_info()` | 输出显示器信息到文件或stdout |
|
||||
| `rotation_to_char()` | 旋转值转字母表示 |
|
||||
| `current_timestamp()` | 获取毫秒级时间戳 |
|
||||
|
||||
## 调用方式
|
||||
```bash
|
||||
./screen_ds [输出文件路径]
|
||||
# 例: ./screen_ds /tmp/screen.txt
|
||||
# 不指定参数则输出到标准输出
|
||||
```
|
||||
|
||||
## 特性
|
||||
- 3秒防抖:避免频繁热插拔导致的重复输出
|
||||
- 持续运行,直到Ctrl+C退出
|
||||
- 支持输出到文件或标准输出
|
||||
|
||||
## 依赖
|
||||
- X11 + Xrandr扩展
|
||||
@@ -0,0 +1,50 @@
|
||||
# screen_ds_once.c - 显示器信息单次查询程序
|
||||
|
||||
## 概述
|
||||
screen_ds.c的简化版本,仅执行一次显示器信息查询并输出结果后退出。适用于需要快速获取当前显示器配置的场景。
|
||||
|
||||
## 与screen_ds.c的区别
|
||||
| 特性 | screen_ds | screen_ds_once |
|
||||
|------|-----------|----------------|
|
||||
| 执行模式 | 持续监控 | 单次查询 |
|
||||
| 事件监听 | 有 | 无 |
|
||||
| 退出方式 | Ctrl+C | 自动退出 |
|
||||
|
||||
## 工作原理
|
||||
|
||||
1. 打开X显示连接
|
||||
2. 查询RandR扩展是否可用
|
||||
3. 获取所有已连接显示器信息
|
||||
4. 输出到文件或标准输出
|
||||
5. 释放资源并退出
|
||||
|
||||
## 数据结构
|
||||
与screen_ds.c相同:
|
||||
```c
|
||||
typedef struct {
|
||||
char* output_name;
|
||||
int width, height;
|
||||
int x, y;
|
||||
int rotation;
|
||||
} MonitorInfo;
|
||||
```
|
||||
|
||||
## 输出格式
|
||||
```
|
||||
HDMI-1|1920x1080|0x0|N
|
||||
```
|
||||
|
||||
## 关键函数
|
||||
| 函数 | 功能 |
|
||||
|------|------|
|
||||
| `get_monitor_info()` | 获取显示器信息 |
|
||||
| `output_monitor_info()` | 输出信息 |
|
||||
| `rotation_to_char()` | 旋转方向编码转换 |
|
||||
|
||||
## 调用方式
|
||||
```bash
|
||||
./screen_ds_once [输出文件路径]
|
||||
```
|
||||
|
||||
## 依赖
|
||||
- X11 + Xrandr扩展
|
||||
@@ -0,0 +1,73 @@
|
||||
# touch_ds.c - 触摸屏坐标矩阵监控程序
|
||||
|
||||
## 概述
|
||||
持续监控所有触摸屏设备的坐标转换矩阵(Coordinate Transformation Matrix)。当检测到某个触摸设备的矩阵发生变化时,设置flag文件通知外部程序。
|
||||
|
||||
## 程序结构
|
||||
|
||||
### 数据结构
|
||||
```c
|
||||
typedef struct {
|
||||
XID deviceid; // XInput设备ID
|
||||
char name[100]; // 设备名称
|
||||
char node[100]; // 设备节点路径
|
||||
char vid_pid[20]; // VID:PID
|
||||
char matrix[120]; // 坐标转换矩阵字符串
|
||||
} TouchscreenInfo;
|
||||
```
|
||||
|
||||
### 配置常量
|
||||
```c
|
||||
int watch_time_delay = 30; // 监控间隔(秒)
|
||||
```
|
||||
|
||||
## 工作原理
|
||||
|
||||
1. **读取配置**: 从触摸屏信息文件读取设备列表(含期望的矩阵值)
|
||||
2. **X11连接**: 打开X显示连接,检查XInput扩展
|
||||
3. **主循环**:
|
||||
- 检查flag文件是否为1(暂停检测)
|
||||
- 重新读取触摸屏信息文件
|
||||
- 对每个设备获取当前矩阵值
|
||||
- 比较矩阵是否与配置值不同
|
||||
- 若变化,设置flag文件为1
|
||||
- 等待30秒后继续
|
||||
|
||||
### 矩阵获取流程
|
||||
```
|
||||
设备 → XInput属性查询 → "Coordinate Transformation Matrix"
|
||||
→ 或 "libinput Calibration Matrix"
|
||||
→ 返回3x3浮点矩阵
|
||||
```
|
||||
|
||||
## 关键函数
|
||||
|
||||
| 函数 | 功能 |
|
||||
|------|------|
|
||||
| `get_device_matrix_property()` | 获取设备的3x3矩阵属性 |
|
||||
| `get_matrix_string()` | 获取矩阵的字符串表示 |
|
||||
| `device_exists()` | 检查设备是否仍然存在 |
|
||||
| `is_touchscreen_device()` | 通过udev判断是否为触摸屏 |
|
||||
| `read_touchscreens_from_file()` | 读取触摸屏配置文件 |
|
||||
| `is_flag_set()` / `set_flag()` | flag文件读写 |
|
||||
|
||||
## 输入文件格式
|
||||
触摸屏信息文件每行格式:
|
||||
```
|
||||
设备ID|设备名|设备节点|VID:PID|矩阵值
|
||||
12345|USB Touchscreen|/dev/input/event5|046d:c52b|1.000000,0.000000,...
|
||||
```
|
||||
|
||||
## flag文件机制
|
||||
- flag文件值为`0`:正常监控
|
||||
- flag文件值为`1`:暂停监控,等待外部恢复为`0`
|
||||
|
||||
## 调用方式
|
||||
```bash
|
||||
./touch_ds <触摸屏信息文件> [flag文件路径]
|
||||
# 例: ./touch_ds /tmp/touch_info.txt /tmp/touch_matrix.flag
|
||||
```
|
||||
|
||||
## 依赖
|
||||
- X11 + XInput2扩展
|
||||
- libudev
|
||||
@@ -0,0 +1,99 @@
|
||||
# touch_listen.c / touch_listen.h - 触摸事件监听库
|
||||
|
||||
## 概述
|
||||
一个触摸屏事件监听库,通过直接读取Linux输入设备(`/dev/input/event*`)获取触摸事件。支持多点触控,提供回调函数接口供上层模块使用。
|
||||
|
||||
## 数据结构
|
||||
|
||||
### touch_device (设备信息)
|
||||
```c
|
||||
struct touch_device {
|
||||
int fd; // 文件描述符
|
||||
char name[256]; // 设备名称
|
||||
char path[256]; // 设备路径
|
||||
int min_x, max_x; // X轴范围
|
||||
int min_y, max_y; // Y轴范围
|
||||
};
|
||||
```
|
||||
|
||||
### touch_event (触摸事件)
|
||||
```c
|
||||
struct touch_event {
|
||||
int device_id; // 设备标识(路径哈希值)
|
||||
int x, y; // 坐标
|
||||
int pressure; // 压力值
|
||||
int touch_id; // 触摸点ID(多点触控)
|
||||
int event_type; // 事件类型: 0=按下, 1=移动, 2=释放
|
||||
char device_name[256]; // 设备名称
|
||||
char device_path[256]; // 设备路径
|
||||
struct timespec timestamp; // 时间戳
|
||||
};
|
||||
```
|
||||
|
||||
### 回调函数类型
|
||||
```c
|
||||
typedef void (*touch_event_callback)(struct touch_event event);
|
||||
```
|
||||
|
||||
## 工作原理
|
||||
|
||||
### 设备发现
|
||||
1. 遍历`/dev/input/`目录下所有`event*`设备
|
||||
2. 通过ioctl检查设备是否支持`EV_ABS`和`EV_KEY`
|
||||
3. 进一步检查是否支持`ABS_MT_POSITION_X`(多点)或`ABS_X`(单点)
|
||||
4. 获取设备名称和坐标范围
|
||||
|
||||
### 事件监听
|
||||
1. 使用`select()`多路复用监听所有设备文件描述符
|
||||
2. 读取`input_event`结构体
|
||||
3. 处理事件类型:
|
||||
- `EV_ABS`: 处理坐标、压力、触摸点ID
|
||||
- `EV_KEY`: 处理`BTN_TOUCH`按键事件
|
||||
- `EV_SYN`: 同步事件,触发回调
|
||||
4. 通过回调函数通知上层
|
||||
|
||||
### 事件类型映射
|
||||
| Linux事件 | 触摸事件类型 | 说明 |
|
||||
|-----------|-------------|------|
|
||||
| ABS_MT_TRACKING_ID != -1 | 0 (按下) | 新触摸点 |
|
||||
| ABS_MT_TRACKING_ID == -1 | 2 (释放) | 触摸释放 |
|
||||
| BTN_TOUCH = 1 | 0 (按下) | 触摸开始 |
|
||||
| BTN_TOUCH = 0 | 2 (释放) | 触摸结束 |
|
||||
| SYN_REPORT | 1 (移动) | 坐标更新 |
|
||||
|
||||
## API接口
|
||||
|
||||
| 函数 | 功能 |
|
||||
|------|------|
|
||||
| `init_touch_listener(callback)` | 初始化监听器,注册回调 |
|
||||
| `listen_touch_events()` | 开始监听(阻塞) |
|
||||
| `stop_touch_listener()` | 停止监听 |
|
||||
| `cleanup_touch_listener()` | 清理资源 |
|
||||
| `get_active_touch_devices()` | 获取活跃设备列表 |
|
||||
|
||||
## 调用示例
|
||||
```c
|
||||
#include "touch_listen.h"
|
||||
|
||||
void my_callback(struct touch_event event) {
|
||||
printf("设备 %s: %s at (%d,%d)\n",
|
||||
event.device_name,
|
||||
event.event_type == 0 ? "按下" :
|
||||
event.event_type == 1 ? "移动" : "释放",
|
||||
event.x, event.y);
|
||||
}
|
||||
|
||||
int main() {
|
||||
init_touch_listener(my_callback);
|
||||
listen_touch_events(); // 阻塞
|
||||
cleanup_touch_listener();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## 使用者
|
||||
- `screen_binder.c` - 触摸屏绑定程序
|
||||
|
||||
## 依赖
|
||||
- Linux输入子系统(`/dev/input/event*`)
|
||||
- 需要root权限访问输入设备
|
||||
@@ -0,0 +1,152 @@
|
||||
# touch_set.c - 触摸屏坐标转换矩阵设置程序
|
||||
|
||||
## 概述
|
||||
核心模块之一,负责根据配置文件和屏幕信息,自动计算并设置每个触摸屏设备的坐标转换矩阵(Coordinate Transformation Matrix),实现触摸屏与显示器的正确映射。
|
||||
|
||||
## 程序结构
|
||||
|
||||
### 数据结构
|
||||
```c
|
||||
// 设备信息
|
||||
typedef struct {
|
||||
int device_id; // XInput设备ID
|
||||
char name[256]; // 设备名称
|
||||
char device_node[256]; // 设备节点路径
|
||||
char vid[10]; // 供应商ID
|
||||
char pid[10]; // 产品ID
|
||||
char usb_path[100]; // 物理路径
|
||||
int is_touchscreen; // 是否为触摸屏
|
||||
float matrix[9]; // 3x3坐标转换矩阵
|
||||
int matched; // 是否已匹配
|
||||
} InputDeviceInfo;
|
||||
|
||||
// 配置项
|
||||
typedef struct {
|
||||
char display_name[50]; // 显示器名称
|
||||
char touchscreen_name[50]; // 触摸屏名称
|
||||
char vid[10]; // 供应商ID
|
||||
char pid[10]; // 产品ID
|
||||
char usb_path[100]; // USB路径
|
||||
int matched; // 是否已匹配
|
||||
} TouchConfig;
|
||||
|
||||
// 屏幕信息
|
||||
typedef struct {
|
||||
char name[50]; // 屏幕名称
|
||||
int width, height; // 分辨率
|
||||
int x, y; // 位置
|
||||
char rotation; // 旋转: N/L/R/I
|
||||
} ScreenInfo;
|
||||
```
|
||||
|
||||
## 工作原理
|
||||
|
||||
### 整体流程
|
||||
```
|
||||
读取配置文件(/opt/ktouch/config)
|
||||
↓
|
||||
读取屏幕信息(/tmp/ktouch/screen.txt)
|
||||
↓
|
||||
计算虚拟桌面大小
|
||||
↓
|
||||
获取所有触摸设备(XInput + udev)
|
||||
↓
|
||||
多轮匹配(严格→宽松)
|
||||
↓
|
||||
计算坐标转换矩阵
|
||||
↓
|
||||
通过XInput设置矩阵
|
||||
↓
|
||||
保存结果到输出文件
|
||||
```
|
||||
|
||||
### 矩阵计算原理
|
||||
|
||||
坐标转换矩阵将触摸屏的原始坐标映射到虚拟桌面坐标系:
|
||||
|
||||
**正常方向(N)**:
|
||||
```
|
||||
| sx 0 dx |
|
||||
| 0 sy dy |
|
||||
| 0 0 1 |
|
||||
```
|
||||
|
||||
**左旋转(L)**:
|
||||
```
|
||||
| 0 -sx sx+dx |
|
||||
| sy 0 dy |
|
||||
| 0 0 1 |
|
||||
```
|
||||
|
||||
**右旋转(R)**:
|
||||
```
|
||||
| 0 sx dx |
|
||||
| -sy 0 sy+dy |
|
||||
| 0 0 1 |
|
||||
```
|
||||
|
||||
**翻转(I)**:
|
||||
```
|
||||
| -sx 0 sx+dx |
|
||||
| 0 -sy sy+dy |
|
||||
| 0 0 1 |
|
||||
```
|
||||
|
||||
其中:
|
||||
- `sx = 屏幕宽度 / 虚拟桌面宽度`
|
||||
- `sy = 屏幕高度 / 虚拟桌面高度`
|
||||
- `dx = 屏幕X偏移 / 虚拟桌面宽度`
|
||||
- `dy = 屏幕Y偏移 / 虚拟桌面高度`
|
||||
|
||||
### 设备匹配策略
|
||||
|
||||
**第一轮(严格匹配)**:
|
||||
1. 设备名完全匹配(去除空格后比较)
|
||||
2. 若多个匹配,按VID:PID筛选
|
||||
3. 若仍多个,按USB路径筛选
|
||||
|
||||
**第二轮及以后(宽松匹配)**:
|
||||
1. 仅按VID:PID匹配
|
||||
2. 若多个匹配,返回失败(-1)
|
||||
|
||||
## 关键函数
|
||||
|
||||
| 函数 | 功能 |
|
||||
|------|------|
|
||||
| `get_input_devices()` | 通过XInput+udev获取所有触摸设备 |
|
||||
| `read_config()` | 读取触摸屏配置文件 |
|
||||
| `read_screen_info()` | 读取屏幕信息文件 |
|
||||
| `find_matching_device()` | 多策略设备匹配 |
|
||||
| `calculate_virtual_desktop()` | 计算虚拟桌面总尺寸 |
|
||||
| `calculate_ctm()` | 计算坐标转换矩阵 |
|
||||
| `set_ctm()` | 通过XInput设置矩阵属性 |
|
||||
| `devnode_to_syspath()` | 设备节点转sysfs路径 |
|
||||
|
||||
## 输入文件格式
|
||||
|
||||
### 配置文件(/opt/ktouch/config)
|
||||
```
|
||||
显示器名|触摸屏名|VID|PID|USB路径
|
||||
HDMI-1|USB Touchscreen|046d|c52b|usb-0000:00:14.0-1
|
||||
```
|
||||
|
||||
### 屏幕信息文件(/tmp/ktouch/screen.txt)
|
||||
```
|
||||
HDMI-1|1920x1080|0x0|N
|
||||
DP-2|1920x1080|1920x0|N
|
||||
```
|
||||
|
||||
### 输出文件格式
|
||||
```
|
||||
设备ID|设备名|设备节点|VID:PID|矩阵值
|
||||
12345|USB Touchscreen|/dev/input/event5|046d:c52b|1.000000, 0.000000, ...
|
||||
```
|
||||
|
||||
## 调用方式
|
||||
```bash
|
||||
./touch_set [输出文件路径]
|
||||
```
|
||||
|
||||
## 依赖
|
||||
- X11 + XInput2扩展
|
||||
- libudev
|
||||
@@ -0,0 +1,77 @@
|
||||
# usb_ds.c - USB触摸屏设备热插拔监控程序
|
||||
|
||||
## 概述
|
||||
通过udev监控USB设备的热插拔事件,当检测到新的触摸屏设备插入时,通过控制文件通知外部程序。支持轮询间隔配置和控制文件暂停机制。
|
||||
|
||||
## 程序结构
|
||||
|
||||
### 配置常量
|
||||
```c
|
||||
#define DEFAULT_POLL_INTERVAL 5 // 默认轮询间隔(秒)
|
||||
#define MAX_RETRY_ATTEMPTS 3 // 最大重试次数
|
||||
#define RETRY_DELAY 1 // 重试延迟(秒)
|
||||
```
|
||||
|
||||
### 控制变量
|
||||
```c
|
||||
volatile sig_atomic_t keep_running = 1; // 运行标志
|
||||
```
|
||||
|
||||
## 工作原理
|
||||
|
||||
### 监控流程
|
||||
```
|
||||
创建udev监控对象
|
||||
↓
|
||||
过滤USB和input子系统事件
|
||||
↓
|
||||
主循环:
|
||||
├── 检查控制文件状态
|
||||
│ └── 若非0,跳过本轮
|
||||
├── select等待事件(超时=轮询间隔)
|
||||
├── 有事件:
|
||||
│ ├── 获取设备信息
|
||||
│ ├── 检查是否为"add"动作
|
||||
│ ├── 检查是否为指针设备
|
||||
│ └── 检查是否为触摸屏
|
||||
└── 更新控制文件状态
|
||||
```
|
||||
|
||||
### 触摸屏判断
|
||||
```c
|
||||
int is_touchscreen(struct udev_device *dev) {
|
||||
// 1. 检查ID_INPUT_TOUCHSCREEN属性
|
||||
// 2. 检查设备名称是否包含"touch"/"touchscreen"/"tablet"
|
||||
}
|
||||
```
|
||||
|
||||
### 控制文件机制
|
||||
- `0`: 正常监控状态
|
||||
- `1`: 检测到触摸屏,等待外部处理后恢复为`0`
|
||||
|
||||
### 文件操作重试
|
||||
所有文件读写操作最多重试3次,每次间隔1秒。
|
||||
|
||||
## 关键函数
|
||||
|
||||
| 函数 | 功能 |
|
||||
|------|------|
|
||||
| `monitor_usb_devices()` | 主监控循环 |
|
||||
| `is_touchscreen()` | 判断设备是否为触摸屏 |
|
||||
| `read_file_content_with_retry()` | 带重试的文件读取 |
|
||||
| `write_file_content_with_retry()` | 带重试的文件写入 |
|
||||
| `sigint_handler()` | SIGINT信号处理 |
|
||||
|
||||
## 调用方式
|
||||
```bash
|
||||
./usb_ds [控制文件路径] [轮询间隔秒数]
|
||||
# 例: ./usb_ds /tmp/usb_monitor.ctl 5
|
||||
# 不带参数: 无控制文件,5秒轮询
|
||||
```
|
||||
|
||||
## 使用场景
|
||||
当USB触摸屏热插拔时,通过控制文件通知主程序重新配置触摸映射。
|
||||
|
||||
## 依赖
|
||||
- libudev
|
||||
- POSIX信号处理
|
||||
Reference in New Issue
Block a user