V2-202509

This commit is contained in:
tang1219
2026-06-12 16:54:32 +08:00
commit a821392ee4
121 changed files with 11161 additions and 0 deletions
+53
View File
@@ -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后端)
+53
View File
@@ -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扩展
+26
View File
@@ -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)
```
+89
View File
@@ -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/`设备
+70
View File
@@ -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扩展
+50
View File
@@ -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扩展
+73
View File
@@ -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
+99
View File
@@ -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权限访问输入设备
+152
View File
@@ -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
+77
View File
@@ -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信号处理