Compare commits

..
8 Commits
Author SHA1 Message Date
kushidou 75727afd24 优化日志采集工具 2026-06-17 09:51:22 +08:00
kushidou 2e56fee25f 调整日志采集脚本 2026-06-16 17:03:16 +08:00
kushidou fef52006a0 完善日志采集功能 2026-06-16 16:49:13 +08:00
tang1219 06e250e52e 更新Readme 2026-06-16 15:13:04 +08:00
tang1219 29b4d47355 重构项目源代码存储结构 2026-06-16 14:59:58 +08:00
tang1219 2dcac48623 优化打包流程 2026-06-16 14:51:34 +08:00
tang1219 34157e19da 优化usb外设插入时重复触发的问题 2026-06-16 14:07:40 +08:00
tang1219 98390053fd 添加设备名关键词匹配 2026-06-16 13:57:42 +08:00
25 changed files with 937 additions and 706 deletions
+4 -227
View File
@@ -1,4 +1,3 @@
<<<<<<< HEAD
# ---> C++
# Prerequisites
*.d
@@ -8,28 +7,11 @@
*.lo
*.o
*.obj
=======
# ---> C
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
>>>>>>> 07cb496a46dea18227a9ad14f0f91254054d9285
# Precompiled Headers
*.gch
*.pch
<<<<<<< HEAD
# Compiled Dynamic libraries
*.so
*.dylib
@@ -44,25 +26,11 @@
*.la
*.a
*.lib
=======
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
>>>>>>> 07cb496a46dea18227a9ad14f0f91254054d9285
# Executables
*.exe
*.out
*.app
<<<<<<< HEAD
.vscode
@@ -74,200 +42,9 @@ screen_ds_once
touch_ds
touch_set
usb_ds
=======
*.i*86
*.x86_64
*.hex
check_save
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
.codewhale
.mimocode
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
# ---> Python
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
#uv.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Ruff stuff:
.ruff_cache/
# PyPI configuration file
.pypirc
>>>>>>> 07cb496a46dea18227a9ad14f0f91254054d9285
*.deb
+1 -1
View File
@@ -1,5 +1,5 @@
Package: ktouch
Version: 2.0
Version: 2.1
Architecture: arm64
Maintainer: Kushidou <kushidou@small09.top>
Depends: xinput, python3
+128 -6
View File
@@ -1,7 +1,129 @@
# ktouchV2
# ktouch V2
<<<<<<< HEAD
适配UOS和银河麒麟的触摸屏设置工具。
=======
适配UOS和麒麟的触摸屏校准设置工具。
>>>>>>> 07cb496a46dea18227a9ad14f0f91254054d9285
适配 UOS 和麒麟 V10 的触摸屏映射校准工具。支持多屏环境下自动检测触摸屏与显示器对应关系,计算校准矩阵并写入系统,解决触摸紊乱问题。
## 功能
- **图形化配置** — 全屏窗口引导用户依次点击各显示器,自动建立触摸屏与显示器的映射
- **后台守护** — systemd 服务持续监控屏幕/触摸屏/USB 变化,自动触发重新校准
- **显示器管理** — 内置显示器设置工具(kdisplay),支持分辨率、旋转方向调整
- **旋转适配** — 校准矩阵自动适配屏幕旋转(横屏/竖屏/倒置)
- **签字笔支持** — 传统方式配置签字笔设备
- **现场诊断** — kscreen-debug 脚本快速排错,kscreen-log 采集日志
## 架构
```
opt/ktouch/
├── kscreen-remap-daemon # ← 主控进程(Python),systemd 服务入口
├── kscreen-setup # 配置脚本:拉起 screen_binder → 解析结果 → 写入 config
├── kscreen-remap # 单次校准:读取 config → 计算矩阵 → 写入 libinput
├── screen_binder # GUI:全屏窗口 + 触摸检测,输出显示器-触摸屏对应表
├── screen_ds # 显示器监控:检测分辨率/坐标变化,写入 screen.txt
├── screen_ds_once # 单次更新显示器信息(screen_ds 阉割版)
├── touch_ds # 触摸屏监控:检测矩阵变化,写入 touchmap.txt
├── touch_set # 校准计算:根据配置计算变换矩阵,写入 libinput
├── usb_ds # USB 监控:检测触摸屏插拔,写入 usbadd.txt
├── check_save # 二次确认窗口:允许用户放弃本次校准
├── kdisplay # 显示器设置 GUI 工具
├── display_monitor.py # 分辨率变化监控脚本
├── kscreen-debug # 现场排错脚本
├── kscreen-log # 日志采集脚本
├── kscreen-fix-daemon # 服务修复脚本
├── desktops/ # 桌面快捷方式
├── forest-light/ # 主题文件
└── config # 触摸屏配置文件
```
**运行流程:**
1. `kscreen-remap-daemon` 作为 systemd 服务启动,拉起各监控进程
2. 高频轮询 `screen.txt`(显示器变化)、`usbadd.txt`USB 插拔)
3. 低频轮询 `touch_ds` 检测触摸矩阵变化
4. 任一条件触发 → `kscreen-remap` 执行校准
5. 用户可通过快捷方式手动触发 `kscreen-setup` 重新配置映射
## 显示器设置(UOS 专属)
UOS 环境下可通过 `kdisplay` 调用系统显示服务(`com.deepin.daemon.Display`)调整显示器参数,与触摸校准联动。
```
kdisplay ───DBus──► com.deepin.daemon.Display
│ │
│ GUI 操作 ├── 分辨率 / 旋转 / 主屏切换
│ · 刷新检测 ├── SwitchMode 合并显示模式
│ · 设置分辨率 └── Save 持久化
│ · 旋转方向
│ · 指定主显示器 display_monitor.py
│ · 自定义分辨率 后台持续检测分辨率变化
│ 通知 kscreen-remap-daemon 重新校准
└── 应用并保存 → 重启显示服务
```
**主要功能:**
| 功能 | 说明 |
|------|------|
| 分辨率设置 | 列出当前显示器支持的 60Hz 模式,支持自定义分辨率 |
| 旋转方向 | 支持正常 / 左转90° / 右转90° / 翻转 |
| 主显示器 | 可指定任意已启用的显示器为主显示器 |
| 自定义分辨率 | 非标分辨率写入 `display_config/` 目录,重启后保持 |
| 排列示意图 | 右侧画布实时展示多屏几何排列 |
自定义分辨率配置文件存放在 `opt/ktouch/display_config/`,格式为 `<显示器名>`
**用法:**
```bash
# GUI 方式
/opt/ktouch/kdisplay
# 命令行快捷方式
kdisplay
```
> 仅在 UOS 环境下可用(依赖 D-Bus 服务 `com.deepin.daemon.Display`)。
> 麒麟 V10 不支持此功能。
## 编译
```bash
# 依赖
sudo apt install libinput-tools python3-evdev python3-pyudev
sudo apt install libevdev-dev libinput-dev libmtdev-dev libudev-dev libwacom-dev
# 编译
cd src
make # 仅编译到 src/
make install # 编译并安装到 ../opt/ktouch/
make clean
```
## 打包
```bash
# 先确保二进制已编译安装
cd src && make install
# 打包为 .deb
chmod +x build_deb.sh
./build_deb.sh # 输出到当前目录
./build_deb.sh ~/dist/ # 输出到指定目录
```
脚本自动从 `DEBIAN/control` 读取包名、版本、架构,生成 `ktouch_<版本>_<架构>.deb`
## 安装
```bash
sudo dpkg -i ktouch_*.deb
# postinst 会自动配置 systemd 服务、快捷方式、用户组权限
# 安装完成后需重启以使 input 组生效
```
## License
MIT
+106
View File
@@ -0,0 +1,106 @@
#!/bin/bash
set -e
# ============ 打包 ktouch deb ============
# 用法: ./build_deb.sh [输出目录]
# 输出: ktouch_<版本>_<架构>.deb
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
OUTPUT_DIR="${1:-$(pwd)}"
BUILD_DIR="/tmp/ktouch_deb_build"
# 从 control 读取版本和架构
CONTROL_FILE="$SCRIPT_DIR/DEBIAN/control"
if [ ! -f "$CONTROL_FILE" ]; then
echo "错误: 找不到 $CONTROL_FILE"
exit 1
fi
VERSION=$(grep -i "^Version:" "$CONTROL_FILE" | awk '{print $2}')
ARCH=$(grep -i "^Architecture:" "$CONTROL_FILE" | awk '{print $2}')
PACKAGE=$(grep -i "^Package:" "$CONTROL_FILE" | awk '{print $2}')
DEB_FILE="${PACKAGE}_${VERSION}_${ARCH}.deb"
echo "============================================"
echo " 打包 $PACKAGE v$VERSION ($ARCH)"
echo "============================================"
# 1. 清理旧构建目录
if [ -d "$BUILD_DIR" ]; then
echo "[1/5] 清理旧构建目录..."
rm -rf "$BUILD_DIR"
fi
# 2. 创建目录结构并拷贝文件
echo "[2/5] 创建构建目录并拷贝文件..."
mkdir -p "$BUILD_DIR/DEBIAN"
mkdir -p "$BUILD_DIR/opt"
mkdir -p "$BUILD_DIR/usr/share/doc/ktouch"
cp -r "$SCRIPT_DIR/DEBIAN/"* "$BUILD_DIR/DEBIAN/"
cp -r "$SCRIPT_DIR/opt/ktouch" "$BUILD_DIR/opt/"
# 排除不应打包的目录
rm -rf "$BUILD_DIR/opt/ktouch/.codewhale"
[ -f "$SCRIPT_DIR/changelog" ] && cp "$SCRIPT_DIR/changelog" "$BUILD_DIR/usr/share/doc/ktouch/changelog"
[ -f "$SCRIPT_DIR/LICENSE" ] && cp "$SCRIPT_DIR/LICENSE" "$BUILD_DIR/usr/share/doc/ktouch/copyright"
# 3. 设置权限
echo "[3/5] 设置文件权限..."
chmod 755 "$BUILD_DIR/DEBIAN"
chmod 644 "$BUILD_DIR/DEBIAN/control"
for script in preinst postinst prerm postrm; do
[ -f "$BUILD_DIR/DEBIAN/$script" ] && chmod 755 "$BUILD_DIR/DEBIAN/$script"
done
# # opt 下所有文件默认 644,目录 755
# find "$BUILD_DIR/opt" -type d -exec chmod 755 {} \;
# find "$BUILD_DIR/opt" -type f -exec chmod 644 {} \;
# # 给可执行程序设置 755
# EXE_LIST=(
# check_save
# screen_binder
# screen_ds
# screen_ds_once
# touch_ds
# touch_set
# usb_ds
# kdisplay
# kscreen-remap
# kscreen-remap-daemon
# kscreen-setup
# kscreen-debug
# kscreen-log
# kscreen-fix-daemon
# display_monitor.py
# )
# for exe in "${EXE_LIST[@]}"; do
# [ -f "$BUILD_DIR/opt/ktouch/$exe" ] && chmod 755 "$BUILD_DIR/opt/ktouch/$exe"
# done
# usr 下文档 644,目录 755
find "$BUILD_DIR/usr" -type d -exec chmod 755 {} \;
find "$BUILD_DIR/usr" -type f -exec chmod 644 {} \;
# 4. dpkg 打包
echo "[4/5] 执行 dpkg-deb 打包..."
dpkg-deb -b "$BUILD_DIR" "$DEB_FILE"
# 5. 清理
echo "[5/5] 清理临时目录..."
rm -rf "$BUILD_DIR"
# 移动到输出目录
if [ "$(realpath "$OUTPUT_DIR")" != "$(realpath "$(pwd)")" ]; then
mv "$DEB_FILE" "$OUTPUT_DIR/"
fi
echo ""
echo "============================================"
echo " 打包完成: $OUTPUT_DIR/$DEB_FILE"
echo " 大小: $(du -h "$OUTPUT_DIR/$DEB_FILE" | cut -f1)"
echo "============================================"
+16
View File
@@ -0,0 +1,16 @@
ktouch (2.1) unstable; urgency=medium
* 适配只支持鼠标事件的屏幕
* 优化开发架构,发布的程序不再包含源代码
-- Kushidou <kushidou@small09.top> Tue, 16 Jun 2026 14:00:00 +0800
ktouch (2.0) unstable; urgency=medium
* 重新设计交互流程,更傻瓜易用
* 重构程序架构,采用更多原生可执行程序
* 为UOS添加显示器分辨率和位置设置工具
* 程序本体适配UOS和麒麟v10
-- Kushidou <kushidou@small09.top> Mon, 25 Sep 2025 10:00:00 +0800
+190 -46
View File
@@ -1,64 +1,208 @@
#!/bin/bash
# 搜集日志和配置文件,用于debug分析
# ============================================
# ktouch 日志采集与打包脚本
# 按类别分目录存储,方便排查触摸屏问题
# ============================================
log_path="/opt/ktouch"
now=$(date "+%m%d-%H%M%S")
set -e
LOG_PATH="/opt/ktouch"
NOW=$(date "+%m%d-%H%M%S")
TMP_BASE="/tmp/klog_$NOW"
tdir="/tmp/klog_$now"
# ---------- 子目录定义 ----------
DIR_SYSTEM="$TMP_BASE/system"
DIR_HARDWARE="$TMP_BASE/hardware"
DIR_APP="$TMP_BASE/app"
DIR_PROGRAM="$TMP_BASE/program"
mkdir $tdir && cd $tdir
mkdir -p "$DIR_SYSTEM" "$DIR_HARDWARE" "$DIR_APP" "$DIR_PROGRAM"
echo "正在集日志文件,请注意需要输入sudo密码。"
echo "正在集日志(部分操作需 sudo..."
# cp /opt/ktouch/*log* $tdir
# cp /opt/ktouch/config $tdir
# cp /opt/ktouch/*.txt $tdir
# ============================================
# 1. 系统日志 ─── system/
# ============================================
echo " 采集系统日志..."
# 采集系统信息
export DISPLAY=:0
xinput > $tdir/xinput_info.txt
for id in $(xinput list --id-only); do
xinput --list-props "$id" >> $tdir/xinput_props.txt
echo -e "\n\n" $tdir/xinput_props.txt
done
xrandr > $tdir/xrandr_info.txt
sudo dmidecode > $tdir/dmidecode.txt
lsusb >> $tdir/lsusb.txt
lsusb -t >> $tdir/lsusb.txt
lsblk -f >> $tdir/lsblk.txt
lspci >> $tdir/lspci.txt
if [ -f /etc/kylin-release ]; then
cat /etc/os-release /etc/kylin-release 2>/dev/null >> $tdir/os-version.txt
elif [ -f /etc/uos-release ]; then
cat /etc/os-release /etc/uos-release /etc/os-version 2>/dev/null >> $tdir/os-version.txt
else
cat /etc/os-release 2>/dev/null >> $tdir/os-version.txt
# journalctl(系统服务日志)
if command -v journalctl &>/dev/null; then
sudo journalctl -b -n 5000 --no-pager > "$DIR_SYSTEM/journal_boot.txt" 2>/dev/null || true
sudo journalctl -u ktouch_daemon --no-pager > "$DIR_SYSTEM/journal_ktouch.txt" 2>/dev/null || true
sudo journalctl -u lightdm --no-pager > "$DIR_SYSTEM/journal_lightdm.txt" 2>/dev/null || true
sudo journalctl -u sddm --no-pager > "$DIR_SYSTEM/journal_sddm.txt" 2>/dev/null || true
fi
# 采集系统的日志
sudo cp /var/log/messages* $tdir >/dev/null 2>&1
sudo cp /var/log/boot* $tdir >/dev/null 2>&1
sudo cp /var/log/kern* $tdir >/dev/null 2>&1
sudo cp /var/log/Xorg* $tdir >/dev/null 2>&1
sudo cp /var/log/syslog* $tdir >/dev/null 2>&1
# dmesg 内核日志
dmesg > "$DIR_SYSTEM/dmesg.txt" 2>/dev/null || true
# 传统系统日志文件
sudo cp /var/log/syslog* "$DIR_SYSTEM/" 2>/dev/null || true
sudo cp /var/log/messages* "$DIR_SYSTEM/" 2>/dev/null || true
sudo cp /var/log/kern* "$DIR_SYSTEM/" 2>/dev/null || true
sudo cp /var/log/boot* "$DIR_SYSTEM/" 2>/dev/null || true
sudo cp /var/log/Xorg* "$DIR_SYSTEM/" 2>/dev/null || true
sudo cp /var/log/dpkg* "$DIR_SYSTEM/" 2>/dev/null || true
sudo cp /var/log/apt/* "$DIR_SYSTEM/" 2>/dev/null || true
# 将程序和日志整体打包
sudo cp /etc/systemd/system/ktouch_daemon.service $tdir/ktouch_daemon.service
sudo chmod 777 -R $tdir
cp -r /opt/ktouch $tdir/ktouch
# 显示管理器日志
[ -d /var/log/lightdm ] && sudo cp -r /var/log/lightdm "$DIR_SYSTEM/" 2>/dev/null || true
[ -d /var/log/sddm ] && sudo cp -r /var/log/sddm "$DIR_SYSTEM/" 2>/dev/null || true
# 系统版本
if [ -f /etc/kylin-release ]; then
cat /etc/os-release /etc/kylin-release 2>/dev/null > "$DIR_SYSTEM/os_version.txt" || true
elif [ -f /etc/uos-release ]; then
cat /etc/os-release /etc/uos-release /etc/os-version 2>/dev/null > "$DIR_SYSTEM/os_version.txt" || true
else
cat /etc/os-release 2>/dev/null > "$DIR_SYSTEM/os_version.txt" || true
fi
ls -la ./ >> $tdir/fileinfo.txt
ls -la ./ktouch >> $tdir/fileinfo.txt
# ============================================
# 2. 硬件信息 ─── hardware/
# ============================================
echo " 采集硬件信息..."
export DISPLAY=:0
# X11 输入设备列表 + 属性(按设备分离)
xinput list > "$DIR_HARDWARE/xinput_list.txt" 2>/dev/null || true
for id in $(xinput list --id-only 2>/dev/null); do
echo "========== device id: $id ==========" >> "$DIR_HARDWARE/xinput_props.txt"
xinput list-props "$id" >> "$DIR_HARDWARE/xinput_props.txt" 2>/dev/null || true
echo "" >> "$DIR_HARDWARE/xinput_props.txt"
done
# xrandr 显示器信息
xrandr --verbose > "$DIR_HARDWARE/xrandr.txt" 2>/dev/null || true
# libinput 设备列表
libinput list-devices > "$DIR_HARDWARE/libinput_devices.txt" 2>/dev/null || true
# /proc/bus/input/devices(内核视角的输入设备)
cat /proc/bus/input/devices > "$DIR_HARDWARE/input_devices.txt" 2>/dev/null || true
# 每个 event 设备的 udev 信息
mkdir -p "$DIR_HARDWARE/evdev/"
for ev in /dev/input/event*; do
[ -e "$ev" ] || continue
name=$(basename "$ev")
echo "========== 设备路径: $ev ==========" >> "$DIR_HARDWARE/evdev/${name}_udev.txt"
udevadm info --query=all --name="$ev" >> "$DIR_HARDWARE/evdev/${name}_udev.txt" 2>/dev/null || true
# 尝试用 evtest 采集设备支持的事件类型(需要 root)
if command -v evtest &>/dev/null; then
sudo evtest --info "$ev" > "$DIR_HARDWARE/evdev/${name}_evtest.txt" 2>/dev/null || true &
fi
done
wait 2>/dev/null || true
# 每个 event 设备的能力位(capabilities
for ev in /dev/input/event*; do
[ -e "$ev" ] || continue
name=$(basename "$ev")
echo "========== $ev ==========" >> "$DIR_HARDWARE/evdev/${name}_caps.txt"
if command -v evemu-describe &>/dev/null; then
evemu-describe "$ev" >> "$DIR_HARDWARE/evdev/${name}_caps.txt" 2>/dev/null || true
fi
done
# DRM 显示器信息
mkdir -p "$DIR_HARDWARE/drm"
for card in /sys/class/drm/card*-*; do
[ -e "$card" ] || continue
card_name=$(basename "$card")
mkdir -p "$DIR_HARDWARE/drm/$card_name"
for attr in status enabled modes dpms edid; do
[ -f "$card/$attr" ] && cp "$card/$attr" "$DIR_HARDWARE/drm/$card_name/$attr" 2>/dev/null || true
done
done
# USB 设备树
lsusb > "$DIR_HARDWARE/lsusb.txt" 2>/dev/null || true
lsusb -t >> "$DIR_HARDWARE/lsusb.txt" 2>/dev/null || true
lsusb -v > "$DIR_HARDWARE/lsusb_verbose.txt" 2>/dev/null || true
# PCI 设备
lspci -k > "$DIR_HARDWARE/lspci.txt" 2>/dev/null || true
# 块设备
lsblk -f > "$DIR_HARDWARE/lsblk.txt" 2>/dev/null || true
# 硬件信息(需要 root
sudo dmidecode > "$DIR_HARDWARE/dmidecode.txt" 2>/dev/null || true
# udev 规则
[ -d /etc/udev/rules.d ] && cp -r /etc/udev/rules.d "$DIR_HARDWARE/udev_rules/" 2>/dev/null || true
[ -d /lib/udev/rules.d ] && cp -r /lib/udev/rules.d "$DIR_HARDWARE/udev_rules_lib/" 2>/dev/null || true
echo "程序版本: ktouch V2" > "$DIR_HARDWARE/ktouch_version.txt"
[ -f "$LOG_PATH/kscreen-remap-daemon" ] && head -5 "$LOG_PATH/kscreen-remap-daemon" >> "$DIR_HARDWARE/ktouch_version.txt" 2>/dev/null || true
# ============================================
# 3. 程序日志与配置 ─── app/
# ============================================
echo " 采集程序日志和配置..."
# 程序日志
[ -f "$LOG_PATH/touchscreen.log" ] && cp "$LOG_PATH/touchscreen.log" "$DIR_APP/touchscreen.log" 2>/dev/null || true
[ -f "$LOG_PATH/install.log" ] && cp "$LOG_PATH/install.log" "$DIR_APP/install.log" 2>/dev/null || true
[ -f "$LOG_PATH/sub_modules.log" ] && cp "$LOG_PATH/sub_modules.log" "$DIR_APP/sub_modules.log" 2>/dev/null || true
# 日志轮转文件
for f in "$LOG_PATH/"*.log.*; do
[ -f "$f" ] && cp "$f" "$DIR_APP/" 2>/dev/null || true
done
# 触摸屏配置
[ -f "$LOG_PATH/config" ] && cp "$LOG_PATH/config" "$DIR_APP/config" 2>/dev/null || true
[ -f "$LOG_PATH/touch_dis_table.txt" ] && cp "$LOG_PATH/touch_dis_table.txt" "$DIR_APP/" 2>/dev/null || true
# 显示器自定义分辨率配置
[ -d "$LOG_PATH/display_config" ] && cp -r "$LOG_PATH/display_config" "$DIR_APP/display_config" 2>/dev/null || true
# /tmp/ktouch 缓存文件
if [ -d /tmp/ktouch ]; then
mkdir -p "$DIR_APP/tmp_cache"
cp -r /tmp/ktouch/* "$DIR_APP/tmp_cache/" 2>/dev/null || true
fi
# systemd 服务文件
[ -f /etc/systemd/system/ktouch_daemon.service ] && sudo cp /etc/systemd/system/ktouch_daemon.service "$DIR_APP/service_file" 2>/dev/null || true
# ============================================
# 4. 程序本体 ─── program/
# ============================================
echo " 采集程序本体..."
# 完整拷贝 /opt/ktouch(检查文件完整性 + 配置文件正确性)
cp -r /opt/ktouch "$DIR_PROGRAM/ktouch"
# 同时生成 md5 清单,方便快速比对
find /opt/ktouch -type f -exec md5sum {} \; 2>/dev/null | sort > "$DIR_PROGRAM/file_md5.txt" || true
# ============================================
# 5. 整理 & 打包
# ============================================
echo " 整理并打包..."
# 统一权限
sudo chmod 755 -R "$TMP_BASE" 2>/dev/null || true
# 生成文件清单
find "$TMP_BASE" -type f | sort > "$TMP_BASE/_file_index.txt"
# 打包
echo "正在打包文件,将会生成在主目录/家目录/个人文件夹"
echo "正在生成压缩包..."
cd /tmp
tar -czf ~/ktouch_log-$now.tar.gz ./klog_$now
tar -czf ~/ktouch_log-"$NOW".tar.gz "klog_$NOW"
rm -rf /tmp/klog
# 清理
sudo rm -rf "$TMP_BASE"
echo ""
echo "============================================"
echo " 日志采集完成"
echo " 文件在 主目录 或 个人 文件夹中"
echo " 文件名: ktouch_log-${NOW}.tar.gz"
echo "============================================"
echo ""
+13 -9
View File
@@ -7,47 +7,51 @@ GTK_FLAGS = $(shell pkg-config --cflags --libs gtk+-3.0)
# 定义目标文件
TARGETS = screen_binder screen_ds touch_ds usb_ds touch_set screen_ds_once check_save
# 默认目标
# 安装目标目录
DESTDIR = ../opt/ktouch
# 默认目标:只编译,不安装
all: $(TARGETS)
# 编译screen_binder
screen_binder: screen_binder.c touch_listen.c
$(CC) -o $@ $^ $(GTK_FLAGS) -lpthread -lX11 -lXrandr
install -m 755 $@ ../
# 编译screen_ds
screen_ds: screen_ds.c
$(CC) -o $@ $^ -lX11 -lXrandr
install -m 755 $@ ../
# 编译screen_ds_once
screen_ds_once: screen_ds_once.c
$(CC) -o $@ $^ -lX11 -lXrandr
install -m 755 $@ ../
# 编译touch_set
touch_set: touch_set.c
$(CC) -o $@ $^ -lX11 -lXi -ludev
install -m 755 $@ ../
# 编译touch_ds
touch_ds: touch_ds.c
$(CC) -o $@ $^ -lX11 -lXi -ludev
install -m 755 $@ ../
# 编译usb_ds
usb_ds: usb_ds.c
$(CC) -o $@ $^ -ludev
install -m 755 $@ ../
# 编译check_save
check_save: check_save.c
$(CC) -o $@ $^ $(GTK_FLAGS)
install -m 755 $@ ../
# 安装:编译全部并复制到 opt/ktouch
install: all
@mkdir -p $(DESTDIR)
@for target in $(TARGETS); do \
install -m 755 $$target $(DESTDIR)/; \
done
@echo "已安装到 $(DESTDIR)/"
# 清理生成的文件
clean:
rm -f $(TARGETS)
# 声明伪目标
.PHONY: all clean
.PHONY: all clean install
+20 -1
View File
@@ -374,7 +374,26 @@ int is_touchscreen_device(const char* device_node, char* vid, char* pid, int buf
}
if (!is_touchscreen) {
// printf(" 未找到 ID_INPUT_TOUCHSCREEN=1 属性\n");
// 通过设备名称关键词匹配(fallback)
const char *dev_name = udev_device_get_property_value(dev, "NAME");
if (dev_name) {
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) {
is_touchscreen = 1;
const char *vendor_id = udev_device_get_property_value(dev, "ID_VENDOR_ID");
const char *model_id = udev_device_get_property_value(dev, "ID_MODEL_ID");
if (vendor_id) strncpy(vid, vendor_id, buffer_size);
if (model_id) strncpy(pid, model_id, buffer_size);
}
}
}
udev_device_unref(dev);
+18 -1
View File
@@ -214,7 +214,24 @@ int get_input_devices(InputDeviceInfo result[]) {
if (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;
@@ -212,7 +212,24 @@ int get_input_devices(InputDeviceInfo result[]) {
if (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;
+29 -20
View File
@@ -16,6 +16,7 @@
#define DEFAULT_POLL_INTERVAL 5 // 默认轮询间隔为5秒
#define MAX_RETRY_ATTEMPTS 3 // 最大重试次数
#define RETRY_DELAY 1 // 重试延迟(秒)
#define COOLDOWN_SECONDS 7 // 触摸屏检测冷却时间(秒)
FILE *log_file = NULL; // 日志文件指针
// 函数提前声明
@@ -84,6 +85,7 @@ int is_touchscreen(struct udev_device *dev) {
if (strstr(lower_name, "touchscreen") != NULL ||
strstr(lower_name, "touch") != NULL ||
strstr(lower_name, "ilitek") != NULL ||
strstr(lower_name, "tablet") != NULL) {
return 1;
}
@@ -212,6 +214,7 @@ void monitor_usb_devices(const char *control_file_path, int poll_interval) {
printf("按Ctrl+C退出\n\n");
// 主循环,监控设备事件
time_t last_trigger_time = 0; // 上次触发控制文件的时间
while (keep_running) {
fd_set fds;
struct timeval tv;
@@ -291,28 +294,34 @@ void monitor_usb_devices(const char *control_file_path, int poll_interval) {
}
}
// 一轮检测完成后,如果需要更新控制文件状态
// 一轮检测完成后,如果需要更新控制文件状态(带冷却时间防重复触发)
if (use_control_file && touchscreen_detected) {
if (write_file_content_with_retry(control_file_path, 1) == 0) {
log_message("已更新控制文件状态为1\n");
// 等待文件状态恢复为0
printf("等待控制文件状态恢复为0...\n");
while (keep_running) {
int current_status = read_file_content_with_retry(control_file_path);
if (current_status == 0) {
printf("控制文件状态已恢复为0,继续监控\n");
break;
} else if (current_status == -1) {
printf("读取控制文件失败,等待%d秒后重试\n", poll_interval);
sleep(poll_interval);
} else {
printf("当前控制文件状态: %d,等待%d秒后检查\n", current_status, poll_interval);
sleep(poll_interval);
}
}
time_t now = time(NULL);
if (difftime(now, last_trigger_time) < COOLDOWN_SECONDS) {
log_message("冷却中,跳过本次触发(距上次 %.0f 秒)\n", difftime(now, last_trigger_time));
} else {
log_message("更新控制文件状态失败\n");
if (write_file_content_with_retry(control_file_path, 1) == 0) {
last_trigger_time = time(NULL);
log_message("已更新控制文件状态为1\n");
// 等待文件状态恢复为0
printf("等待控制文件状态恢复为0...\n");
while (keep_running) {
int current_status = read_file_content_with_retry(control_file_path);
if (current_status == 0) {
printf("控制文件状态已恢复为0,继续监控\n");
break;
} else if (current_status == -1) {
printf("读取控制文件失败,等待%d秒后重试\n", poll_interval);
sleep(poll_interval);
} else {
printf("当前控制文件状态: %d,等待%d秒后检查\n", current_status, poll_interval);
sleep(poll_interval);
}
}
} else {
log_message("更新控制文件状态失败\n");
}
}
}
}