771 lines
26 KiB
C
771 lines
26 KiB
C
#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 match_success;
|
||
} InputDeviceInfo;
|
||
|
||
// 配置结构体
|
||
typedef struct {
|
||
char display_name[50];
|
||
char touchscreen_name[50];
|
||
char vid[10];
|
||
char pid[10];
|
||
char usb_path[100];
|
||
} TouchConfig;
|
||
|
||
// 屏幕信息结构体
|
||
typedef struct {
|
||
char name[50];
|
||
int width;
|
||
int height;
|
||
int x;
|
||
int y;
|
||
} 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 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].match_success = 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));
|
||
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;
|
||
|
||
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;
|
||
count++;
|
||
|
||
log_message("屏幕 %s: 分辨率 %dx%d, 位置 (%d,%d)\n", name, width, height, x, y);
|
||
}
|
||
|
||
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 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 (strcmp(devices[i].name, config.touchscreen_name) == 0) {
|
||
// match_indices[match_count++] = i;
|
||
// log_message("名称完全匹配: %s\n", devices[i].name);
|
||
// }
|
||
|
||
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) {
|
||
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];
|
||
}
|
||
|
||
// 如果还有多个匹配项,按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; // 返回第一个匹配的
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果还是无法确定,返回第一个匹配项
|
||
if (vid_pid_match_count > 0) {
|
||
log_message("仍有多个匹配项,返回第一个\n");
|
||
return vid_pid_match_indices[0];
|
||
}
|
||
}
|
||
|
||
// // 如果没有名称匹配项,尝试按vid:pid匹配
|
||
// log_message("未找到名称匹配项,尝试VID:PID匹配\n");
|
||
// for (int i = 0; i < device_count; i++) {
|
||
// 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];
|
||
// }
|
||
|
||
// // 如果还有多个匹配项,按usb路径匹配
|
||
// if (match_count > 1 && config.usb_path[0] != '\0') {
|
||
// log_message("找到多个VID:PID匹配项,按USB路径进一步筛选\n");
|
||
// for (int i = 0; i < match_count; i++) {
|
||
// int idx = match_indices[i];
|
||
// if (strcmp(devices[idx].usb_path, config.usb_path) == 0) {
|
||
// log_message("USB路径匹配: %s\n", devices[idx].usb_path);
|
||
// return idx;
|
||
// }
|
||
// }
|
||
// }
|
||
|
||
// // 如果还是无法确定,返回第一个匹配项或-1
|
||
// if (match_count > 0) {
|
||
// log_message("仍有多个匹配项,返回第一个\n");
|
||
// return match_indices[0];
|
||
// }
|
||
|
||
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]) {
|
||
float scale_x = (float)screen.width / total_width;
|
||
float scale_y = (float)screen.height / total_height;
|
||
float offset_x = (float)screen.x / total_width;
|
||
float offset_y = (float)screen.y / total_height;
|
||
|
||
// 设置变换矩阵
|
||
matrix[0] = scale_x; matrix[1] = 0.0f; matrix[2] = offset_x;
|
||
matrix[3] = 0.0f; matrix[4] = scale_y; matrix[5] = offset_y;
|
||
matrix[6] = 0.0f; matrix[7] = 0.0f; matrix[8] = 1.0f;
|
||
}
|
||
|
||
// 设置设备的坐标转换矩阵
|
||
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;
|
||
}
|
||
|
||
// // 释放设备信息结构体内存
|
||
// void free_input_devices(struct InputDeviceInfo *devices, int num_devices) {
|
||
// for (int i = 0; i < num_devices; i++) {
|
||
// free(devices[i].name);
|
||
// free(devices[i].device_node);
|
||
// free(devices[i].vid);
|
||
// free(devices[i].pid);
|
||
// free(devices[i].usb_path);
|
||
// }
|
||
// free(devices);
|
||
// }
|
||
|
||
// 示例使用
|
||
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);
|
||
}
|
||
|
||
// 对每个配置项查找匹配的设备
|
||
for (int i = 0; i < config_count; i++) {
|
||
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);
|
||
if (device_index == -1) {
|
||
log_message("未找到匹配 %s 的设备\n", configs[i].touchscreen_name);
|
||
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].match_success = 1;
|
||
|
||
// 检查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)\n",
|
||
matched_screen->name, matched_screen->width,
|
||
matched_screen->height, matched_screen->x, matched_screen->y);
|
||
|
||
// 计算并设置矩阵
|
||
float matrix[9];
|
||
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);
|
||
}
|
||
|
||
// 保存到文件
|
||
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++) {
|
||
wcount++;
|
||
if(devices[i].match_success == 0){
|
||
continue;
|
||
}
|
||
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);
|
||
}
|
||
}
|
||
|
||
log_message("触摸屏配置完成\n");
|
||
|
||
if (log_file) {
|
||
fclose(log_file);
|
||
}
|
||
|
||
return 0;
|
||
|
||
} |