重构项目源代码存储结构

This commit is contained in:
tang1219
2026-06-16 14:59:58 +08:00
parent 2dcac48623
commit 29b4d47355
21 changed files with 413 additions and 406 deletions
+866
View File
@@ -0,0 +1,866 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <libudev.h>
#include <X11/Xatom.h> // 为XA_STRING, XA_FLOAT等预定义原子
#include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h>
#include <stdarg.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#define MAX_LINE_LENGTH 256
#define MAX_DEVICES 50
// 全局日志文件指针
FILE *log_file = NULL;
// 设备信息结构体
typedef struct {
int device_id; // 设备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];
int matched; // 是否已匹配成功
} InputDeviceInfo;
// 配置结构体
typedef struct {
char display_name[50];
char touchscreen_name[50];
char vid[10];
char pid[10];
char usb_path[100];
int matched; // 是否已匹配成功
} TouchConfig;
// 屏幕信息结构体
typedef struct {
char name[50];
int width;
int height;
int x;
int y;
char rotation; // 旋转方向: N(正常), L(左转), R(右转), I(翻转)
} ScreenInfo;
// 函数声明
void log_message(const char* format, ...);
int read_config(const char* filename, TouchConfig configs[], int max_configs);
int read_screen_info(const char* filename, ScreenInfo screens[], int max_screens);
int find_matching_device(TouchConfig config, InputDeviceInfo devices[], int device_count, int strict);
int parse_resolution_and_position(const char* str, int* width, int* height, int* x, int* y);
void calculate_virtual_desktop(ScreenInfo screens[], int screen_count, int *total_width, int *total_height);
void calculate_ctm(ScreenInfo screen, int total_width, int total_height, float matrix[9]);
int set_ctm(int device_id, float matrix[9]);
// 日志函数
void log_message(const char *format, ...) {
va_list args;
va_start(args, format);
// 获取当前时间
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
char time_str[20];
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info);
// 输出到控制台
printf("[%s] touch_set \t", time_str);
vprintf(format, args);
// 输出到文件
if (log_file) {
fprintf(log_file, "[%s] touch_set \t", time_str);
vfprintf(log_file, format, args);
fflush(log_file); // 确保立即写入文件
}
va_end(args);
}
// 将设备节点路径转换为 sysfs 路径
char* devnode_to_syspath(const char* devnode) {
struct stat st;
if (stat(devnode, &st) != 0) {
return NULL;
}
// 获取主设备号和次设备号
unsigned int major = major(st.st_rdev);
unsigned int minor = minor(st.st_rdev);
// 构建 sysfs 路径
char* syspath = malloc(256);
snprintf(syspath, 256, "/sys/dev/char/%u:%u", major, minor);
return syspath;
}
// 获取输入设备信息的主要函数
int get_input_devices(InputDeviceInfo result[]) {
Display *display;
int ndevices;
XIDeviceInfo *devices;
struct udev *udev_ctx;
int count = 0;
// 打开X11显示连接
display = XOpenDisplay(NULL);
if (!display) {
log_message("无法打开X显示连接\n");
return 0;
}
// 初始化udev上下文
udev_ctx = udev_new();
if (!udev_ctx) {
log_message("无法创建udev上下文\n");
XCloseDisplay(display);
return 0;
}
// 获取X输入设备列表
devices = XIQueryDevice(display, XIAllDevices, &ndevices);
if (!devices) {
log_message( "无法查询X输入设备\n");
udev_unref(udev_ctx);
XCloseDisplay(display);
return 0;
}
// 遍历所有设备
for (int i = 0; i < ndevices; i++) {
XIDeviceInfo *dev = &devices[i];
// 只关注从设备(slave devices),排除主设备和虚拟设备
// if (dev->use != XISlavePointer && dev->use != XISlaveKeyboard) {
// continue;
// }
if(dev -> use != 3) continue;
char *device_node = NULL;
// 获取设备属性
int num_props = 0;
Atom *props = XIListProperties(display, dev->deviceid, &num_props);
// log_message("正在处理设备%d:%s(use=%d)\n", dev->deviceid, dev->name, dev->use);
if (props) {
for (int j = 0; j < num_props; j++) {
Atom prop = props[j];
char *prop_name = XGetAtomName(display, prop);
if (strcmp(prop_name, "Device Node") == 0) {
// 获取设备节点路径
Atom actual_type;
int actual_format;
unsigned long nitems, bytes_after;
unsigned char *prop_data = NULL;
if (XIGetProperty(display, dev->deviceid, prop, 0, 100, False,
AnyPropertyType, &actual_type, &actual_format,
&nitems, &bytes_after, &prop_data) == Success) {
// 使用 XInternAtom 获取字符串原子类型
Atom string_atom = XInternAtom(display, "STRING", False);
if (actual_type == string_atom && actual_format == 8) {
device_node = strdup((char*)prop_data);
}
// log_message(" |- Device Node = %s\n", device_node);
XFree(prop_data);
}
}
XFree(prop_name);
}
XFree(props);
}
// 如果找到设备节点,通过udev查询更多信息
char* syspath = devnode_to_syspath(device_node);
// log_message("Device syspath = %s\n", syspath);
struct udev_device *udev_dev = udev_device_new_from_syspath(udev_ctx, syspath);
// const char *name = udev_device_get_property_value(udev_dev, "NAME");
// 获取供应商ID
const char *vendor_id = udev_device_get_property_value(udev_dev, "ID_VENDOR_ID");
// 获取模型ID
const char *model_id = udev_device_get_property_value(udev_dev, "ID_MODEL_ID");
// 获取物理路径
const char *physical_path = udev_device_get_property_value(udev_dev, "ID_PATH");
// 检查是否是触摸屏设备
const char *is_touchscreen = udev_device_get_property_value(udev_dev, "ID_INPUT_TOUCHSCREEN");
// log_message(" |- Device info: vid=%s, pid=%s, path=%s, touch=%s\n", vendor_id, model_id, physical_path, is_touchscreen);
// 通过名称关键词匹配(fallback,针对无ID_INPUT_TOUCHSCREEN属性的设备)
int name_matched = 0;
if (!is_touchscreen) {
char lower_name[256];
strncpy(lower_name, dev->name, sizeof(lower_name) - 1);
lower_name[sizeof(lower_name) - 1] = '\0';
for (int i = 0; lower_name[i]; i++) {
lower_name[i] = tolower(lower_name[i]);
}
if (strstr(lower_name, "ilitek") != NULL ||
strstr(lower_name, "touchscreen") != NULL ||
strstr(lower_name, "touch") != NULL ||
strstr(lower_name, "tablet") != NULL) {
name_matched = 1;
}
}
if (is_touchscreen || name_matched){
// log_message("识别到触摸设备,添加到列表中\n name=%s", name);
// 初始化当前设备信息结构体
result[count].device_id = dev->deviceid;
result[count].matched = 0;
strncpy(result[count].name, dev->name, sizeof(result[count].name));
strncpy(result[count].vid, vendor_id, sizeof(result[count].vid));
strncpy(result[count].pid, model_id, sizeof(result[count].pid));
strncpy(result[count].usb_path, physical_path, sizeof(result[count].usb_path));
strncpy(result[count].device_node, device_node, sizeof(result[count].device_node));
for(int i=0;i<9;i++){
result[count].matrix[i] = 0;
}
// result[count].device_node = NULL;
// result[count].name = strdup(dev->name);
// result[count].device_node = strdup(device_node);
// result[count].vid = strdup(vendor_id);
// result[count].pid = strdup(model_id);
// result[count].usb_path = strdup(physical_path);
result[count].is_touchscreen = 1;
log_message("发现触摸屏设备 %s(id=%d), %s:%s, path=%s\n",result[count].name, result[count].device_id,
result[count].vid, result[count].pid,result[count].usb_path);
count++;
}
udev_device_unref(udev_dev);
}
// 释放资源
XIFreeDeviceInfo(devices);
udev_unref(udev_ctx);
XCloseDisplay(display);
return count;
}
// 读取配置文件
int read_config(const char* filename, TouchConfig configs[], int max_configs) {
FILE* file = fopen(filename, "r");
if (!file) {
log_message("无法打开配置文件: %s\n", filename);
return -1;
}
char line[MAX_LINE_LENGTH];
int count = 0;
while (fgets(line, sizeof(line), file) && count < max_configs) {
// 移除换行符
line[strcspn(line, "\n")] = 0;
// 跳过空行和注释
if (line[0] == '\0' || line[0] == '#') continue;
// 解析行
char* tokens[5];
char* token = strtok(line, "|");
int i = 0;
while (token && i < 5) {
tokens[i++] = token;
token = strtok(NULL, "|");
}
if (i == 5) {
strncpy(configs[count].display_name, tokens[0], sizeof(configs[count].display_name));
strncpy(configs[count].touchscreen_name, tokens[1], sizeof(configs[count].touchscreen_name));
strncpy(configs[count].vid, tokens[2], sizeof(configs[count].vid));
strncpy(configs[count].pid, tokens[3], sizeof(configs[count].pid));
strncpy(configs[count].usb_path, tokens[4], sizeof(configs[count].usb_path));
configs[count].matched = 0;
count++;
} else {
log_message("无效的配置行: %s\n", line);
}
}
fclose(file);
return count;
}
// 读取屏幕信息
int read_screen_info(const char* filename, ScreenInfo screens[], int max_screens) {
FILE* file = fopen(filename, "r");
if (!file) {
log_message("无法打开屏幕信息文件: %s\n", filename);
return -1;
}
char line[MAX_LINE_LENGTH];
int count = 0;
while (fgets(line, sizeof(line), file) && count < max_screens) {
// 移除换行符
line[strcspn(line, "\n")] = 0;
// 跳过空行和注释
if (line[0] == '\0' || line[0] == '#') continue;
// 解析显示器名
char* name = strtok(line, "|");
if (!name) continue;
// 解析分辨率
char* resolution = strtok(NULL, "|");
if (!resolution) continue;
// 解析坐标
char* position = strtok(NULL, "|");
if (!position) continue;
// 解析旋转方向(可选,默认为N
char* rotation_str = strtok(NULL, "|");
char rotation = 'N'; // 默认正常方向
if (rotation_str) {
rotation = toupper(rotation_str[0]);
if (rotation != 'N' && rotation != 'L' && rotation != 'R' && rotation != 'I') {
log_message("无效的旋转方向: %s,使用默认值N\n", rotation_str);
rotation = 'N';
}
}
int width, height, x, y;
// 解析分辨率 (格式: 1920x1080)
if (sscanf(resolution, "%dx%d", &width, &height) != 2) {
log_message("无效的分辨率格式: %s\n", resolution);
continue;
}
// 解析坐标 (格式: 1920x0 或 1920,0)
if (sscanf(position, "%d%*[x,]%d", &x, &y) != 2) {
log_message("无效的坐标格式: %s\n", position);
continue;
}
strncpy(screens[count].name, name, sizeof(screens[count].name));
screens[count].width = width;
screens[count].height = height;
screens[count].x = x;
screens[count].y = y;
screens[count].rotation = rotation;
count++;
log_message("屏幕 %s: 分辨率 %dx%d, 位置 (%d,%d), 旋转方向 %c\n",
name, width, height, x, y, rotation);
}
fclose(file);
return count;
}
// 辅助函数:去除字符串中的空格
void remove_spaces(char* str) {
char* i = str;
char* j = str;
while (*j != '\0') {
*i = *j++;
if (*i != ' ') {
i++;
}
}
*i = '\0';
}
// 查找匹配的设备
int find_matching_device(TouchConfig config, InputDeviceInfo devices[], int device_count, int strict) {
int match_index = -1;
int match_count = 0;
int match_indices[MAX_DEVICES];
log_message("查找匹配设备: %s (VID:%s PID:%s 路径:%s)\n",
config.touchscreen_name, config.vid, config.pid, config.usb_path);
// 第一步:按设备名完全匹配
for (int i = 0; i < device_count; i++) {
// 跳过已匹配的设备
if (devices[i].matched) {
continue;
}
char device_name_no_space[256];
char config_name_no_space[256];
strcpy(device_name_no_space, devices[i].name);
strcpy(config_name_no_space, config.touchscreen_name);
remove_spaces(device_name_no_space);
remove_spaces(config_name_no_space);
if (strcmp(device_name_no_space, config_name_no_space) == 0) {
match_indices[match_count++] = i;
log_message("名称完全匹配(去除空格): %s -> %s\n", devices[i].name, device_name_no_space);
}
}
// 如果只有一个匹配项,直接返回
if (match_count == 1) {
return match_indices[0];
}
// 如果有多个匹配项,按vid:pid进一步匹配 // 严格模式
if (match_count > 1 && strict) {
// match_count = 0;
log_message("找到多个名称匹配项,按VID:PID进一步筛选\n");
int vid_pid_match_count = 0;
int vid_pid_match_indices[MAX_DEVICES];
for (int i = 0; i < match_count; i++) {
int idx = match_indices[i];
if (strcasecmp(devices[idx].vid, config.vid) == 0 &&
strcasecmp(devices[idx].pid, config.pid) == 0) {
vid_pid_match_indices[vid_pid_match_count++] = idx;
log_message("VID:PID匹配: %s (VID:%s PID:%s)\n",
devices[idx].name, devices[idx].vid, devices[idx].pid);
}
}
if (vid_pid_match_count == 1) {
return vid_pid_match_indices[0];
}
// 严格模式下,多个pid、vid匹配,按usb路径匹配
if (vid_pid_match_count > 1 && config.usb_path[0] != '\0') {
log_message("找到多个VID:PID匹配项,按USB路径进一步筛选\n");
for (int i = 0; i < vid_pid_match_count; i++) {
int idx = vid_pid_match_indices[i];
if (strcmp(devices[idx].usb_path, config.usb_path) == 0) {
log_message("USB路径匹配: %s\n", devices[idx].usb_path);
return idx; // 返回第一个匹配的
}
}
}
// 如果还是无法确定,则失败
log_message("都无法匹配,返回失败:-1\n");
return -1;
}
// 宽松匹配:要求名称和id都匹配的上
// 程序能跑到这里,说明有多个名称匹配了
if (!strict) {
log_message("未找到名称匹配项,尝试VID:PID匹配\n");
for (int i = 0; i < device_count; i++) {
// 跳过已匹配的设备
if (devices[i].matched) {
continue;
}
if (strcasecmp(devices[i].vid, config.vid) == 0 &&
strcasecmp(devices[i].pid, config.pid) == 0) {
match_indices[match_count++] = i;
log_message("VID:PID匹配: %s (VID:%s PID:%s)\n",
devices[i].name, devices[i].vid, devices[i].pid);
}
}
if (match_count == 1) {
return match_indices[0];
}
// 如果还是无法确定,返回第一个匹配项
if (match_count > 0) {
log_message("仍有多个匹配项,返回失败\n");
return -1;
}
}
log_message("未找到匹配设备\n");
return -1;
}
/* ==================================
= 矩阵坐标计算相关代码 =
======================================*/
// 计算虚拟桌面大小
void calculate_virtual_desktop(ScreenInfo screens[], int screen_count, int *total_width, int *total_height) {
int min_x = 0, min_y = 0;
int max_x = 0, max_y = 0;
for (int i = 0; i < screen_count; i++) {
int screen_right = screens[i].x + screens[i].width;
int screen_bottom = screens[i].y + screens[i].height;
if (screens[i].x < min_x) min_x = screens[i].x;
if (screens[i].y < min_y) min_y = screens[i].y;
if (screen_right > max_x) max_x = screen_right;
if (screen_bottom > max_y) max_y = screen_bottom;
}
*total_width = max_x - min_x;
*total_height = max_y - min_y;
log_message("虚拟桌面边界: x(%d 到 %d), y(%d 到 %d)\n",
min_x, max_x, min_y, max_y);
}
// 计算坐标转换矩阵的函数(考虑旋转方向)
void calculate_ctm(ScreenInfo screen, int total_width, int total_height, float matrix[9]) {
/*
* 屏幕旋转的计算
*
* 在逻辑处理中,系统需要将屏幕按相同方向旋转(逆时针为正向)
* 然后将图形向左平移回正坐标
* 最后进行传统缩放。
*
* 旋转矩阵
* cosθ -sinθ 0
* sinθ cosθ 0
* 0 0 1
*
* “向左旋转”后,屏幕需要逆时针旋转90度才能正确显示,同样的系统也是逆时针90度。
* 由此可以推算出90、180、270 三个旋转的矩阵
*
*/
float sx = (float)screen.width / total_width;
float sy = (float)screen.height / total_height;
float dx = (float)screen.x / total_width;
float dy = (float)screen.y / total_height;
// 根据旋转方向调整矩阵
switch (screen.rotation) {
case 'L': // 左转90度
matrix[0] = 0; matrix[1] = -sx; matrix[2] = sx+dx;
matrix[3] = sy; matrix[4] = 0.0f; matrix[5] = dy;
matrix[6] = 0.0f; matrix[7] = 0.0f; matrix[8] = 1.0f;
break;
case 'R': // 右转90
matrix[0] = 0.0f; matrix[1] = sx; matrix[2] = dx;
matrix[3] = -sy; matrix[4] = 0.0f; matrix[5] = sy + dy;
matrix[6] = 0.0f; matrix[7] = 0.0f; matrix[8] = 1.0f;
break;
case 'I': // 翻转180度
matrix[0] = -sx; matrix[1] = 0.0f; matrix[2] = sx + dx;
matrix[3] = 0.0f; matrix[4] = -sy; matrix[5] = sy + dy;
matrix[6] = 0.0f; matrix[7] = 0.0f; matrix[8] = 1.0f;
break;
case 'N': // 正常方向
default:
matrix[0] = sx; matrix[1] = 0.0f; matrix[2] = dx;
matrix[3] = 0.0f; matrix[4] = sy; matrix[5] = dy;
matrix[6] = 0.0f; matrix[7] = 0.0f; matrix[8] = 1.0f;
break;
}
}
// 设置设备的坐标转换矩阵
int set_ctm(int device_id, float matrix[9]) {
Display *display = XOpenDisplay(NULL);
if (!display) {
fprintf(stderr, "无法打开X显示连接\n");
return 0;
}
// 创建ATOM属性
Atom prop = XInternAtom(display, "Coordinate Transformation Matrix", False);
if (!prop) {
fprintf(stderr, "无法创建Coordinate Transformation Matrix属性\n");
XCloseDisplay(display);
return 0;
}
// 检查设备是否支持此属性
int num_props = 0;
Atom *props = XIListProperties(display, device_id, &num_props);
int supports_matrix = 0;
if (props) {
for (int i = 0; i < num_props; i++) {
char *prop_name = XGetAtomName(display, props[i]);
if (strcmp(prop_name, "Coordinate Transformation Matrix") == 0) {
supports_matrix = 1;
XFree(prop_name);
break;
}
XFree(prop_name);
}
XFree(props);
}
if (!supports_matrix) {
fprintf(stderr, "设备 %d 不支持坐标转换矩阵属性\n", device_id);
XCloseDisplay(display);
return 0;
}
// 创建FLOAT类型的原子
Atom float_atom = XInternAtom(display, "FLOAT", False);
if (!float_atom) {
fprintf(stderr, "无法创建FLOAT类型原子\n");
XCloseDisplay(display);
return 0;
}
// 设置属性值
XIChangeProperty(display, device_id, prop, float_atom, 32, PropModeReplace,
(unsigned char*)matrix, 9);
XFlush(display);
XCloseDisplay(display);
return 1;
}
// 示例使用
int main(int argc, char *argv[]) {
char *output_file_path = NULL;
// 处理命令行参数
if (argc > 1) {
output_file_path = argv[1];
log_message("输出文件路径: %s\n", output_file_path);
}
// 打开日志文件
log_file = fopen("/opt/ktouch/sub_modules.log", "a");
if (!log_file) {
printf("警告: 无法打开日志文件 /opt/ktouch/sub_modules.log,仅输出到控制台\n");
}
log_message("开始触摸屏配置...\n");
TouchConfig configs[MAX_DEVICES];
ScreenInfo screens[MAX_DEVICES];
InputDeviceInfo devices[MAX_DEVICES];
int config_count = read_config("/opt/ktouch/config", configs, MAX_DEVICES);
if (config_count < 0) {
log_message("读取配置文件失败\n");
if (log_file) fclose(log_file);
return 1;
}
log_message("读取了 %d 个配置项\n", config_count);
int screen_count = read_screen_info("/tmp/ktouch/screen.txt", screens, MAX_DEVICES);
if (screen_count < 0) {
log_message("读取屏幕信息文件失败\n");
if (log_file) fclose(log_file);
return 1;
}
log_message("读取了 %d 个屏幕信息\n", screen_count);
// 计算虚拟桌面大小
int total_width, total_height;
calculate_virtual_desktop(screens, screen_count, &total_width, &total_height);
log_message("虚拟桌面大小: %dx%d\n", total_width, total_height);
int device_count = 0;
device_count = get_input_devices(devices);
log_message("找到了 %d 个触摸设备\n", device_count);
// 输出所有找到的设备信息
for (int i = 0; i < device_count; i++) {
log_message("设备 %d: %s (VID:%s PID:%s 路径:%s XInput ID:%d)\n",
i, devices[i].name, devices[i].vid, devices[i].pid,
devices[i].usb_path, devices[i].device_id);
}
// 多轮匹配:首先严格匹配,然后宽松匹配,直到无法匹配
int round = 1;
int matched_in_round = 1; // 初始化为1以进入循环
while (matched_in_round > 0) {
matched_in_round = 0;
log_message("开始第 %d 轮匹配\n", round);
// 确定匹配严格程度
int strict = (round == 1); // 第一轮严格匹配,后续宽松匹配
// 对每个未匹配的配置项查找匹配的设备
for (int i = 0; i < config_count; i++) {
if (configs[i].matched) {
continue; // 跳过已匹配的配置项
}
log_message("处理配置: %s|%s|%s|%s|%s\n",
configs[i].display_name, configs[i].touchscreen_name,
configs[i].vid, configs[i].pid, configs[i].usb_path);
int device_index = find_matching_device(configs[i], devices, device_count, strict);
if (device_index == -1) {
continue; // 未找到匹配设备,继续下一个配置项
}
log_message("匹配设备: %s (VID:%s PID:%s 路径:%s XInput ID:%d)\n",
devices[device_index].name, devices[device_index].vid,
devices[device_index].pid, devices[device_index].usb_path,
devices[device_index].device_id);
// 标记设备和配置项为已匹配
devices[device_index].matched = 1;
configs[i].matched = 1;
matched_in_round++;
// 检查XInput设备ID是否有效
if (devices[device_index].device_id == -1) {
log_message("错误: 设备 %s 没有有效的XInput ID\n", devices[device_index].name);
continue;
}
// 查找对应的屏幕信息
ScreenInfo* matched_screen = NULL;
for (int j = 0; j < screen_count; j++) {
if (strcmp(screens[j].name, configs[i].display_name) == 0) {
matched_screen = &screens[j];
break;
}
}
if (matched_screen == NULL) {
log_message("未找到 %s 对应的屏幕, 将使用第一屏\n", configs[i].display_name);
// 未找到屏幕,则返回第一个屏幕
matched_screen = &screens[0];
}
log_message("匹配屏幕: %s %dx%d 位置(%d,%d) 旋转方向 %c\n",
matched_screen->name, matched_screen->width,
matched_screen->height, matched_screen->x, matched_screen->y,
matched_screen->rotation);
// 计算并设置矩阵
float matrix[9]={0};
calculate_ctm(*matched_screen, total_width, total_height, matrix);
log_message("计算矩阵: [%f, %f, %f, %f, %f, %f, %f, %f, %f]\n",
matrix[0], matrix[1], matrix[2],
matrix[3], matrix[4], matrix[5],
matrix[6], matrix[7], matrix[8]);
set_ctm(devices[device_index].device_id, matrix);
memcpy(devices[device_index].matrix, matrix, sizeof(float) * 9);
log_message("已配置 %s 为 %s 的触摸屏\n",
devices[device_index].name, configs[i].display_name);
}
log_message("第 %d 轮匹配完成,匹配了 %d 个设备\n", round, matched_in_round);
round++;
}
// 保存到文件
if (output_file_path) {
int max_retries = 3;
int retry_delay = 1; // 秒
FILE *output_file = NULL;
for (int retry = 0; retry < max_retries; retry++) {
output_file = fopen(output_file_path, "w");
if (output_file) {
break;
}
log_message("无法打开输出文件 %s (尝试 %d/%d)%d秒后重试...\n",
output_file_path, retry + 1, max_retries, retry_delay);
sleep(retry_delay);
}
if (output_file) {
int wcount = 0;
for (int i = 0; i < device_count; i++) {
if(devices[i].matched == 0){
continue;
}
wcount++;
fprintf(output_file, "%d|%s|%s|%s:%s|%.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f\n",
devices[i].device_id,
devices[i].name,
devices[i].device_node,
devices[i].vid,
devices[i].pid,
devices[i].matrix[0], devices[i].matrix[1], devices[i].matrix[2],
devices[i].matrix[3], devices[i].matrix[4], devices[i].matrix[5],
devices[i].matrix[6], devices[i].matrix[7], devices[i].matrix[8]);
}
fclose(output_file);
log_message("已成功写入 %d 条记录到文件 %s\n", wcount, output_file_path);
} else {
log_message("经过 %d 次尝试后仍无法打开输出文件 %s,放弃写入\n",
max_retries, output_file_path);
}
}
// 输出未匹配的设备信息
int unmatched_devices = 0;
for (int i = 0; i < device_count; i++) {
if (!devices[i].matched) {
unmatched_devices++;
log_message("警告: 设备 %s (VID:%s PID:%s) 未匹配到任何配置\n",
devices[i].name, devices[i].vid, devices[i].pid);
}
}
// 输出未匹配的配置信息
int unmatched_configs = 0;
for (int i = 0; i < config_count; i++) {
if (!configs[i].matched) {
unmatched_configs++;
log_message("警告: 配置 %s 未匹配到任何设备\n", configs[i].touchscreen_name);
}
}
log_message("触摸屏配置完成,%d 个设备未匹配,%d 个配置未匹配\n",
unmatched_devices, unmatched_configs);
if (log_file) {
fclose(log_file);
}
return 0;
}