Compare commits
8
Commits
8531b75edd
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75727afd24 | ||
|
|
2e56fee25f | ||
|
|
fef52006a0 | ||
|
|
06e250e52e | ||
|
|
29b4d47355 | ||
|
|
2dcac48623 | ||
|
|
34157e19da | ||
|
|
98390053fd |
+4
-227
@@ -1,4 +1,3 @@
|
|||||||
<<<<<<< HEAD
|
|
||||||
# ---> C++
|
# ---> C++
|
||||||
# Prerequisites
|
# Prerequisites
|
||||||
*.d
|
*.d
|
||||||
@@ -8,28 +7,11 @@
|
|||||||
*.lo
|
*.lo
|
||||||
*.o
|
*.o
|
||||||
*.obj
|
*.obj
|
||||||
=======
|
|
||||||
# ---> C
|
|
||||||
# Prerequisites
|
|
||||||
*.d
|
|
||||||
|
|
||||||
# Object files
|
|
||||||
*.o
|
|
||||||
*.ko
|
|
||||||
*.obj
|
|
||||||
*.elf
|
|
||||||
|
|
||||||
# Linker output
|
|
||||||
*.ilk
|
|
||||||
*.map
|
|
||||||
*.exp
|
|
||||||
>>>>>>> 07cb496a46dea18227a9ad14f0f91254054d9285
|
|
||||||
|
|
||||||
# Precompiled Headers
|
# Precompiled Headers
|
||||||
*.gch
|
*.gch
|
||||||
*.pch
|
*.pch
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
# Compiled Dynamic libraries
|
# Compiled Dynamic libraries
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
@@ -44,25 +26,11 @@
|
|||||||
*.la
|
*.la
|
||||||
*.a
|
*.a
|
||||||
*.lib
|
*.lib
|
||||||
=======
|
|
||||||
# Libraries
|
|
||||||
*.lib
|
|
||||||
*.a
|
|
||||||
*.la
|
|
||||||
*.lo
|
|
||||||
|
|
||||||
# Shared objects (inc. Windows DLLs)
|
|
||||||
*.dll
|
|
||||||
*.so
|
|
||||||
*.so.*
|
|
||||||
*.dylib
|
|
||||||
>>>>>>> 07cb496a46dea18227a9ad14f0f91254054d9285
|
|
||||||
|
|
||||||
# Executables
|
# Executables
|
||||||
*.exe
|
*.exe
|
||||||
*.out
|
*.out
|
||||||
*.app
|
*.app
|
||||||
<<<<<<< HEAD
|
|
||||||
|
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
@@ -74,200 +42,9 @@ screen_ds_once
|
|||||||
touch_ds
|
touch_ds
|
||||||
touch_set
|
touch_set
|
||||||
usb_ds
|
usb_ds
|
||||||
=======
|
check_save
|
||||||
*.i*86
|
|
||||||
*.x86_64
|
|
||||||
*.hex
|
|
||||||
|
|
||||||
# Debug files
|
.codewhale
|
||||||
*.dSYM/
|
.mimocode
|
||||||
*.su
|
|
||||||
*.idb
|
|
||||||
*.pdb
|
|
||||||
|
|
||||||
# Kernel Module Compile Results
|
*.deb
|
||||||
*.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
|
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
Package: ktouch
|
Package: ktouch
|
||||||
Version: 2.0
|
Version: 2.1
|
||||||
Architecture: arm64
|
Architecture: arm64
|
||||||
Maintainer: Kushidou <kushidou@small09.top>
|
Maintainer: Kushidou <kushidou@small09.top>
|
||||||
Depends: xinput, python3
|
Depends: xinput, python3
|
||||||
|
|||||||
@@ -1,7 +1,129 @@
|
|||||||
# ktouchV2
|
# ktouch V2
|
||||||
|
|
||||||
<<<<<<< HEAD
|
适配 UOS 和麒麟 V10 的触摸屏映射校准工具。支持多屏环境下自动检测触摸屏与显示器对应关系,计算校准矩阵并写入系统,解决触摸紊乱问题。
|
||||||
适配UOS和银河麒麟的触摸屏设置工具。
|
|
||||||
=======
|
## 功能
|
||||||
适配UOS和麒麟的触摸屏校准设置工具。
|
|
||||||
>>>>>>> 07cb496a46dea18227a9ad14f0f91254054d9285
|
- **图形化配置** — 全屏窗口引导用户依次点击各显示器,自动建立触摸屏与显示器的映射
|
||||||
|
- **后台守护** — 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
@@ -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 "============================================"
|
||||||
@@ -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
@@ -1,64 +1,208 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# 搜集日志和配置文件,用于debug分析
|
# ============================================
|
||||||
|
# ktouch 日志采集与打包脚本
|
||||||
|
# 按类别分目录存储,方便排查触摸屏问题
|
||||||
|
# ============================================
|
||||||
|
|
||||||
log_path="/opt/ktouch"
|
set -e
|
||||||
now=$(date "+%m%d-%H%M%S")
|
|
||||||
|
|
||||||
|
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
|
# 1. 系统日志 ─── system/
|
||||||
# cp /opt/ktouch/*.txt $tdir
|
# ============================================
|
||||||
|
echo " 采集系统日志..."
|
||||||
|
|
||||||
|
# journalctl(系统服务日志)
|
||||||
# 采集系统信息
|
if command -v journalctl &>/dev/null; then
|
||||||
export DISPLAY=:0
|
sudo journalctl -b -n 5000 --no-pager > "$DIR_SYSTEM/journal_boot.txt" 2>/dev/null || true
|
||||||
xinput > $tdir/xinput_info.txt
|
sudo journalctl -u ktouch_daemon --no-pager > "$DIR_SYSTEM/journal_ktouch.txt" 2>/dev/null || true
|
||||||
for id in $(xinput list --id-only); do
|
sudo journalctl -u lightdm --no-pager > "$DIR_SYSTEM/journal_lightdm.txt" 2>/dev/null || true
|
||||||
xinput --list-props "$id" >> $tdir/xinput_props.txt
|
sudo journalctl -u sddm --no-pager > "$DIR_SYSTEM/journal_sddm.txt" 2>/dev/null || true
|
||||||
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
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 采集系统的日志
|
# dmesg 内核日志
|
||||||
sudo cp /var/log/messages* $tdir >/dev/null 2>&1
|
dmesg > "$DIR_SYSTEM/dmesg.txt" 2>/dev/null || true
|
||||||
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
|
|
||||||
|
|
||||||
|
# 传统系统日志文件
|
||||||
|
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
|
[ -d /var/log/lightdm ] && sudo cp -r /var/log/lightdm "$DIR_SYSTEM/" 2>/dev/null || true
|
||||||
sudo chmod 777 -R $tdir
|
[ -d /var/log/sddm ] && sudo cp -r /var/log/sddm "$DIR_SYSTEM/" 2>/dev/null || true
|
||||||
cp -r /opt/ktouch $tdir/ktouch
|
|
||||||
|
|
||||||
|
# 系统版本
|
||||||
|
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
|
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 ""
|
||||||
|
|||||||
@@ -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
|
TARGETS = screen_binder screen_ds touch_ds usb_ds touch_set screen_ds_once check_save
|
||||||
|
|
||||||
# 默认目标
|
# 安装目标目录
|
||||||
|
DESTDIR = ../opt/ktouch
|
||||||
|
|
||||||
|
# 默认目标:只编译,不安装
|
||||||
all: $(TARGETS)
|
all: $(TARGETS)
|
||||||
|
|
||||||
# 编译screen_binder
|
# 编译screen_binder
|
||||||
screen_binder: screen_binder.c touch_listen.c
|
screen_binder: screen_binder.c touch_listen.c
|
||||||
$(CC) -o $@ $^ $(GTK_FLAGS) -lpthread -lX11 -lXrandr
|
$(CC) -o $@ $^ $(GTK_FLAGS) -lpthread -lX11 -lXrandr
|
||||||
install -m 755 $@ ../
|
|
||||||
|
|
||||||
# 编译screen_ds
|
# 编译screen_ds
|
||||||
screen_ds: screen_ds.c
|
screen_ds: screen_ds.c
|
||||||
$(CC) -o $@ $^ -lX11 -lXrandr
|
$(CC) -o $@ $^ -lX11 -lXrandr
|
||||||
install -m 755 $@ ../
|
|
||||||
|
|
||||||
# 编译screen_ds_once
|
# 编译screen_ds_once
|
||||||
screen_ds_once: screen_ds_once.c
|
screen_ds_once: screen_ds_once.c
|
||||||
$(CC) -o $@ $^ -lX11 -lXrandr
|
$(CC) -o $@ $^ -lX11 -lXrandr
|
||||||
install -m 755 $@ ../
|
|
||||||
|
|
||||||
# 编译touch_set
|
# 编译touch_set
|
||||||
touch_set: touch_set.c
|
touch_set: touch_set.c
|
||||||
$(CC) -o $@ $^ -lX11 -lXi -ludev
|
$(CC) -o $@ $^ -lX11 -lXi -ludev
|
||||||
install -m 755 $@ ../
|
|
||||||
|
|
||||||
# 编译touch_ds
|
# 编译touch_ds
|
||||||
touch_ds: touch_ds.c
|
touch_ds: touch_ds.c
|
||||||
$(CC) -o $@ $^ -lX11 -lXi -ludev
|
$(CC) -o $@ $^ -lX11 -lXi -ludev
|
||||||
install -m 755 $@ ../
|
|
||||||
|
|
||||||
# 编译usb_ds
|
# 编译usb_ds
|
||||||
usb_ds: usb_ds.c
|
usb_ds: usb_ds.c
|
||||||
$(CC) -o $@ $^ -ludev
|
$(CC) -o $@ $^ -ludev
|
||||||
install -m 755 $@ ../
|
|
||||||
|
|
||||||
# 编译check_save
|
# 编译check_save
|
||||||
check_save: check_save.c
|
check_save: check_save.c
|
||||||
$(CC) -o $@ $^ $(GTK_FLAGS)
|
$(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:
|
clean:
|
||||||
rm -f $(TARGETS)
|
rm -f $(TARGETS)
|
||||||
|
|
||||||
# 声明伪目标
|
# 声明伪目标
|
||||||
.PHONY: all clean
|
.PHONY: all clean install
|
||||||
+394
-394
@@ -1,395 +1,395 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
#include <gdk/gdkx.h>
|
#include <gdk/gdkx.h>
|
||||||
#include <gdk/gdkkeysyms.h>
|
#include <gdk/gdkkeysyms.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
// 添加Xrandr头文件和X11显示类型头文件
|
// 添加Xrandr头文件和X11显示类型头文件
|
||||||
#ifdef GDK_WINDOWING_X11
|
#ifdef GDK_WINDOWING_X11
|
||||||
#include <X11/extensions/Xrandr.h>
|
#include <X11/extensions/Xrandr.h>
|
||||||
#include <gdk/x11/gdkx11display.h>
|
#include <gdk/x11/gdkx11display.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int win_time_delay = 50;
|
int win_time_delay = 50;
|
||||||
|
|
||||||
// 全局变量
|
// 全局变量
|
||||||
GtkWidget **info_windows = NULL;
|
GtkWidget **info_windows = NULL;
|
||||||
int total_screens = 0;
|
int total_screens = 0;
|
||||||
int *screen_widths = NULL;
|
int *screen_widths = NULL;
|
||||||
int *screen_heights = NULL;
|
int *screen_heights = NULL;
|
||||||
int *screen_x_offsets = NULL;
|
int *screen_x_offsets = NULL;
|
||||||
int *screen_y_offsets = NULL;
|
int *screen_y_offsets = NULL;
|
||||||
char **screen_names = NULL;
|
char **screen_names = NULL;
|
||||||
FILE *log_file = NULL;
|
FILE *log_file = NULL;
|
||||||
int current_test_round = 0;
|
int current_test_round = 0;
|
||||||
int total_test_rounds = 0;
|
int total_test_rounds = 0;
|
||||||
int space_pressed = 0;
|
int space_pressed = 0;
|
||||||
guint create_timeout_id = 0;
|
guint create_timeout_id = 0;
|
||||||
int current_window_index = 0;
|
int current_window_index = 0;
|
||||||
|
|
||||||
// 函数声明
|
// 函数声明
|
||||||
void get_screen_info();
|
void get_screen_info();
|
||||||
void log_message(const char *format, ...);
|
void log_message(const char *format, ...);
|
||||||
GtkWidget* create_info_window(int screen_index);
|
GtkWidget* create_info_window(int screen_index);
|
||||||
gboolean on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data);
|
gboolean on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data);
|
||||||
gboolean create_next_window(gpointer user_data);
|
gboolean create_next_window(gpointer user_data);
|
||||||
void close_all_windows();
|
void close_all_windows();
|
||||||
void verify_windows();
|
void verify_windows();
|
||||||
void start_next_test_round();
|
void start_next_test_round();
|
||||||
void on_info_window_destroy(GtkWidget *widget, gpointer user_data);
|
void on_info_window_destroy(GtkWidget *widget, gpointer user_data);
|
||||||
|
|
||||||
// 日志函数
|
// 日志函数
|
||||||
void log_message(const char *format, ...) {
|
void log_message(const char *format, ...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
|
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
struct tm *tm_info = localtime(&now);
|
struct tm *tm_info = localtime(&now);
|
||||||
char time_str[20];
|
char time_str[20];
|
||||||
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info);
|
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info);
|
||||||
|
|
||||||
printf("[%s] screen_demo \t", time_str);
|
printf("[%s] screen_demo \t", time_str);
|
||||||
vprintf(format, args);
|
vprintf(format, args);
|
||||||
|
|
||||||
if (log_file) {
|
if (log_file) {
|
||||||
fprintf(log_file, "[%s] screen_demo \t", time_str);
|
fprintf(log_file, "[%s] screen_demo \t", time_str);
|
||||||
vfprintf(log_file, format, args);
|
vfprintf(log_file, format, args);
|
||||||
fflush(log_file);
|
fflush(log_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取屏幕信息 (使用Xrandr方法)
|
// 获取屏幕信息 (使用Xrandr方法)
|
||||||
void get_screen_info() {
|
void get_screen_info() {
|
||||||
GdkDisplay *gdk_display = gdk_display_get_default();
|
GdkDisplay *gdk_display = gdk_display_get_default();
|
||||||
|
|
||||||
#ifdef GDK_WINDOWING_X11
|
#ifdef GDK_WINDOWING_X11
|
||||||
if (!GDK_IS_X11_DISPLAY(gdk_display)) {
|
if (!GDK_IS_X11_DISPLAY(gdk_display)) {
|
||||||
log_message("错误: 非X11显示环境\n");
|
log_message("错误: 非X11显示环境\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display);
|
Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display);
|
||||||
Window xroot = GDK_WINDOW_XID(gdk_get_default_root_window());
|
Window xroot = GDK_WINDOW_XID(gdk_get_default_root_window());
|
||||||
|
|
||||||
int rr_event_base, rr_error_base;
|
int rr_event_base, rr_error_base;
|
||||||
if (!XRRQueryExtension(xdisplay, &rr_event_base, &rr_error_base)) {
|
if (!XRRQueryExtension(xdisplay, &rr_event_base, &rr_error_base)) {
|
||||||
log_message("错误: XRandR扩展不可用\n");
|
log_message("错误: XRandR扩展不可用\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
XRRScreenResources *screen_res = XRRGetScreenResourcesCurrent(xdisplay, xroot);
|
XRRScreenResources *screen_res = XRRGetScreenResourcesCurrent(xdisplay, xroot);
|
||||||
if (!screen_res) {
|
if (!screen_res) {
|
||||||
log_message("错误: 无法获取屏幕资源\n");
|
log_message("错误: 无法获取屏幕资源\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算连接的显示器数量
|
// 计算连接的显示器数量
|
||||||
total_screens = 0;
|
total_screens = 0;
|
||||||
for (int i = 0; i < screen_res->noutput; i++) {
|
for (int i = 0; i < screen_res->noutput; i++) {
|
||||||
XRROutputInfo *output_info = XRRGetOutputInfo(xdisplay, screen_res, screen_res->outputs[i]);
|
XRROutputInfo *output_info = XRRGetOutputInfo(xdisplay, screen_res, screen_res->outputs[i]);
|
||||||
if (output_info && output_info->connection == RR_Connected) {
|
if (output_info && output_info->connection == RR_Connected) {
|
||||||
total_screens++;
|
total_screens++;
|
||||||
}
|
}
|
||||||
if (output_info) XRRFreeOutputInfo(output_info);
|
if (output_info) XRRFreeOutputInfo(output_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
log_message("找到 %d 个连接的显示器\n", total_screens);
|
log_message("找到 %d 个连接的显示器\n", total_screens);
|
||||||
|
|
||||||
// 分配内存存储屏幕信息
|
// 分配内存存储屏幕信息
|
||||||
screen_widths = malloc(total_screens * sizeof(int));
|
screen_widths = malloc(total_screens * sizeof(int));
|
||||||
screen_heights = malloc(total_screens * sizeof(int));
|
screen_heights = malloc(total_screens * sizeof(int));
|
||||||
screen_x_offsets = malloc(total_screens * sizeof(int));
|
screen_x_offsets = malloc(total_screens * sizeof(int));
|
||||||
screen_y_offsets = malloc(total_screens * sizeof(int));
|
screen_y_offsets = malloc(total_screens * sizeof(int));
|
||||||
screen_names = malloc(total_screens * sizeof(char*));
|
screen_names = malloc(total_screens * sizeof(char*));
|
||||||
info_windows = malloc(total_screens * sizeof(GtkWidget*));
|
info_windows = malloc(total_screens * sizeof(GtkWidget*));
|
||||||
|
|
||||||
// 获取每个显示器的详细信息
|
// 获取每个显示器的详细信息
|
||||||
int screen_index = 0;
|
int screen_index = 0;
|
||||||
for (int i = 0; i < screen_res->noutput; i++) {
|
for (int i = 0; i < screen_res->noutput; i++) {
|
||||||
XRROutputInfo *output_info = XRRGetOutputInfo(xdisplay, screen_res, screen_res->outputs[i]);
|
XRROutputInfo *output_info = XRRGetOutputInfo(xdisplay, screen_res, screen_res->outputs[i]);
|
||||||
if (output_info && output_info->connection == RR_Connected) {
|
if (output_info && output_info->connection == RR_Connected) {
|
||||||
if (output_info->crtc) {
|
if (output_info->crtc) {
|
||||||
XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(xdisplay, screen_res, output_info->crtc);
|
XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(xdisplay, screen_res, output_info->crtc);
|
||||||
if (crtc_info) {
|
if (crtc_info) {
|
||||||
screen_names[screen_index] = strdup(output_info->name);
|
screen_names[screen_index] = strdup(output_info->name);
|
||||||
screen_widths[screen_index] = crtc_info->width;
|
screen_widths[screen_index] = crtc_info->width;
|
||||||
screen_heights[screen_index] = crtc_info->height;
|
screen_heights[screen_index] = crtc_info->height;
|
||||||
screen_x_offsets[screen_index] = crtc_info->x;
|
screen_x_offsets[screen_index] = crtc_info->x;
|
||||||
screen_y_offsets[screen_index] = crtc_info->y;
|
screen_y_offsets[screen_index] = crtc_info->y;
|
||||||
|
|
||||||
info_windows[screen_index] = NULL;
|
info_windows[screen_index] = NULL;
|
||||||
|
|
||||||
log_message("显示器 %d: %s, 分辨率: %dx%d, 位置: %dx%d\n",
|
log_message("显示器 %d: %s, 分辨率: %dx%d, 位置: %dx%d\n",
|
||||||
screen_index, screen_names[screen_index],
|
screen_index, screen_names[screen_index],
|
||||||
screen_widths[screen_index], screen_heights[screen_index],
|
screen_widths[screen_index], screen_heights[screen_index],
|
||||||
screen_x_offsets[screen_index], screen_y_offsets[screen_index]);
|
screen_x_offsets[screen_index], screen_y_offsets[screen_index]);
|
||||||
|
|
||||||
XRRFreeCrtcInfo(crtc_info);
|
XRRFreeCrtcInfo(crtc_info);
|
||||||
screen_index++;
|
screen_index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (output_info) XRRFreeOutputInfo(output_info);
|
if (output_info) XRRFreeOutputInfo(output_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
XRRFreeScreenResources(screen_res);
|
XRRFreeScreenResources(screen_res);
|
||||||
#else
|
#else
|
||||||
log_message("错误: 非X11环境,无法使用Xrandr\n");
|
log_message("错误: 非X11环境,无法使用Xrandr\n");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建信息窗口
|
// 创建信息窗口
|
||||||
GtkWidget* create_info_window(int screen_index) {
|
GtkWidget* create_info_window(int screen_index) {
|
||||||
// 计算窗口位置和大小:偏移(10,10),尺寸比屏幕小20
|
// 计算窗口位置和大小:偏移(10,10),尺寸比屏幕小20
|
||||||
int window_x = screen_x_offsets[screen_index] + 10;
|
int window_x = screen_x_offsets[screen_index] + 10;
|
||||||
int window_y = screen_y_offsets[screen_index] + 10;
|
int window_y = screen_y_offsets[screen_index] + 10;
|
||||||
int window_width = screen_widths[screen_index] - 20;
|
int window_width = screen_widths[screen_index] - 20;
|
||||||
int window_height = screen_heights[screen_index] - 20;
|
int window_height = screen_heights[screen_index] - 20;
|
||||||
|
|
||||||
// 确保窗口尺寸不会为负值
|
// 确保窗口尺寸不会为负值
|
||||||
if (window_width < 100) window_width = 100;
|
if (window_width < 100) window_width = 100;
|
||||||
if (window_height < 100) window_height = 100;
|
if (window_height < 100) window_height = 100;
|
||||||
|
|
||||||
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||||
gtk_window_set_title(GTK_WINDOW(window), "屏幕信息演示");
|
gtk_window_set_title(GTK_WINDOW(window), "屏幕信息演示");
|
||||||
gtk_window_move(GTK_WINDOW(window), window_x, window_y);
|
gtk_window_move(GTK_WINDOW(window), window_x, window_y);
|
||||||
gtk_window_set_default_size(GTK_WINDOW(window), window_width, window_height);
|
gtk_window_set_default_size(GTK_WINDOW(window), window_width, window_height);
|
||||||
gtk_window_set_decorated(GTK_WINDOW(window), TRUE);
|
gtk_window_set_decorated(GTK_WINDOW(window), TRUE);
|
||||||
gtk_window_set_keep_above(GTK_WINDOW(window), TRUE);
|
gtk_window_set_keep_above(GTK_WINDOW(window), TRUE);
|
||||||
|
|
||||||
char message[512];
|
char message[512];
|
||||||
snprintf(message, sizeof(message),
|
snprintf(message, sizeof(message),
|
||||||
"<span font='24' weight='bold'>屏幕 %d: %s</span>\n\n"
|
"<span font='24' weight='bold'>屏幕 %d: %s</span>\n\n"
|
||||||
"<span font='18'>分辨率: %dx%d</span>\n"
|
"<span font='18'>分辨率: %dx%d</span>\n"
|
||||||
"<span font='18'>位置: (%d, %d)</span>\n\n"
|
"<span font='18'>位置: (%d, %d)</span>\n\n"
|
||||||
"<span font='18'>窗口位置: (%d, %d)</span>\n"
|
"<span font='18'>窗口位置: (%d, %d)</span>\n"
|
||||||
"<span font='18'>窗口大小: %dx%d</span>\n\n"
|
"<span font='18'>窗口大小: %dx%d</span>\n\n"
|
||||||
"<span font='18'>测试轮次: %d/%d</span>\n\n"
|
"<span font='18'>测试轮次: %d/%d</span>\n\n"
|
||||||
"<span font='18' foreground='blue'>按空格键继续...</span>",
|
"<span font='18' foreground='blue'>按空格键继续...</span>",
|
||||||
screen_index, screen_names[screen_index],
|
screen_index, screen_names[screen_index],
|
||||||
screen_widths[screen_index], screen_heights[screen_index],
|
screen_widths[screen_index], screen_heights[screen_index],
|
||||||
screen_x_offsets[screen_index], screen_y_offsets[screen_index],
|
screen_x_offsets[screen_index], screen_y_offsets[screen_index],
|
||||||
window_x, window_y, window_width, window_height,
|
window_x, window_y, window_width, window_height,
|
||||||
current_test_round + 1, total_test_rounds);
|
current_test_round + 1, total_test_rounds);
|
||||||
|
|
||||||
GtkWidget *label = gtk_label_new(NULL);
|
GtkWidget *label = gtk_label_new(NULL);
|
||||||
gtk_label_set_markup(GTK_LABEL(label), message);
|
gtk_label_set_markup(GTK_LABEL(label), message);
|
||||||
gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
|
gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
|
||||||
gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
|
gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
|
||||||
|
|
||||||
// 添加容器使标签居中
|
// 添加容器使标签居中
|
||||||
GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
||||||
gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0);
|
gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0);
|
||||||
gtk_container_add(GTK_CONTAINER(window), box);
|
gtk_container_add(GTK_CONTAINER(window), box);
|
||||||
|
|
||||||
g_signal_connect(window, "key-press-event", G_CALLBACK(on_key_press), NULL);
|
g_signal_connect(window, "key-press-event", G_CALLBACK(on_key_press), NULL);
|
||||||
g_signal_connect(window, "destroy", G_CALLBACK(on_info_window_destroy), GINT_TO_POINTER(screen_index));
|
g_signal_connect(window, "destroy", G_CALLBACK(on_info_window_destroy), GINT_TO_POINTER(screen_index));
|
||||||
|
|
||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 键盘事件处理
|
// 键盘事件处理
|
||||||
gboolean on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data) {
|
gboolean on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data) {
|
||||||
if (event->keyval == GDK_KEY_space) {
|
if (event->keyval == GDK_KEY_space) {
|
||||||
log_message("用户按下空格键\n");
|
log_message("用户按下空格键\n");
|
||||||
space_pressed = 1;
|
space_pressed = 1;
|
||||||
|
|
||||||
// 关闭所有窗口并开始下一轮测试
|
// 关闭所有窗口并开始下一轮测试
|
||||||
close_all_windows();
|
close_all_windows();
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 信息窗口销毁事件处理
|
// 信息窗口销毁事件处理
|
||||||
void on_info_window_destroy(GtkWidget *widget, gpointer user_data) {
|
void on_info_window_destroy(GtkWidget *widget, gpointer user_data) {
|
||||||
int screen_index = GPOINTER_TO_INT(user_data);
|
int screen_index = GPOINTER_TO_INT(user_data);
|
||||||
log_message("屏幕 %d (%s) 的信息窗口已销毁\n", screen_index, screen_names[screen_index]);
|
log_message("屏幕 %d (%s) 的信息窗口已销毁\n", screen_index, screen_names[screen_index]);
|
||||||
info_windows[screen_index] = NULL;
|
info_windows[screen_index] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建下一个窗口(间隔3秒)
|
// 创建下一个窗口(间隔3秒)
|
||||||
gboolean create_next_window(gpointer user_data) {
|
gboolean create_next_window(gpointer user_data) {
|
||||||
if (current_window_index >= total_screens) {
|
if (current_window_index >= total_screens) {
|
||||||
log_message("所有窗口已创建完成\n");
|
log_message("所有窗口已创建完成\n");
|
||||||
verify_windows();
|
verify_windows();
|
||||||
create_timeout_id = 0;
|
create_timeout_id = 0;
|
||||||
return G_SOURCE_REMOVE;
|
return G_SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_message("正在为屏幕 %d (%s) 创建信息窗口...\n",
|
log_message("正在为屏幕 %d (%s) 创建信息窗口...\n",
|
||||||
current_window_index, screen_names[current_window_index]);
|
current_window_index, screen_names[current_window_index]);
|
||||||
|
|
||||||
info_windows[current_window_index] = create_info_window(current_window_index);
|
info_windows[current_window_index] = create_info_window(current_window_index);
|
||||||
gtk_widget_show_all(info_windows[current_window_index]);
|
gtk_widget_show_all(info_windows[current_window_index]);
|
||||||
|
|
||||||
log_message("屏幕 %d 的信息窗口已显示\n", current_window_index);
|
log_message("屏幕 %d 的信息窗口已显示\n", current_window_index);
|
||||||
|
|
||||||
current_window_index++;
|
current_window_index++;
|
||||||
|
|
||||||
if (current_window_index < total_screens) {
|
if (current_window_index < total_screens) {
|
||||||
// 设置3秒后创建下一个窗口
|
// 设置3秒后创建下一个窗口
|
||||||
// create_timeout_id = g_timeout_add_seconds(3, create_next_window, NULL);
|
// create_timeout_id = g_timeout_add_seconds(3, create_next_window, NULL);
|
||||||
create_timeout_id = g_timeout_add(win_time_delay, create_next_window, NULL);
|
create_timeout_id = g_timeout_add(win_time_delay, create_next_window, NULL);
|
||||||
} else {
|
} else {
|
||||||
log_message("所有窗口已创建完成\n");
|
log_message("所有窗口已创建完成\n");
|
||||||
verify_windows();
|
verify_windows();
|
||||||
}
|
}
|
||||||
|
|
||||||
return G_SOURCE_REMOVE;
|
return G_SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭所有窗口
|
// 关闭所有窗口
|
||||||
void close_all_windows() {
|
void close_all_windows() {
|
||||||
log_message("正在关闭所有窗口...\n");
|
log_message("正在关闭所有窗口...\n");
|
||||||
|
|
||||||
for (int i = 0; i < total_screens; i++) {
|
for (int i = 0; i < total_screens; i++) {
|
||||||
if (info_windows[i] != NULL) {
|
if (info_windows[i] != NULL) {
|
||||||
gtk_widget_destroy(info_windows[i]);
|
gtk_widget_destroy(info_windows[i]);
|
||||||
info_windows[i] = NULL;
|
info_windows[i] = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 取消超时计时器
|
// 取消超时计时器
|
||||||
if (create_timeout_id > 0) {
|
if (create_timeout_id > 0) {
|
||||||
g_source_remove(create_timeout_id);
|
g_source_remove(create_timeout_id);
|
||||||
create_timeout_id = 0;
|
create_timeout_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_message("所有窗口已关闭\n");
|
log_message("所有窗口已关闭\n");
|
||||||
|
|
||||||
// 开始下一轮测试
|
// 开始下一轮测试
|
||||||
start_next_test_round();
|
start_next_test_round();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验窗口的显示状态、位置和大小
|
// 校验窗口的显示状态、位置和大小
|
||||||
void verify_windows() {
|
void verify_windows() {
|
||||||
log_message("开始校验窗口状态...\n");
|
log_message("开始校验窗口状态...\n");
|
||||||
|
|
||||||
int all_windows_ok = 1;
|
int all_windows_ok = 1;
|
||||||
|
|
||||||
for (int i = 0; i < total_screens; i++) {
|
for (int i = 0; i < total_screens; i++) {
|
||||||
if (info_windows[i] == NULL) {
|
if (info_windows[i] == NULL) {
|
||||||
log_message("错误: 屏幕 %d 的窗口未创建\n", i);
|
log_message("错误: 屏幕 %d 的窗口未创建\n", i);
|
||||||
all_windows_ok = 0;
|
all_windows_ok = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gtk_widget_get_visible(info_windows[i])) {
|
if (!gtk_widget_get_visible(info_windows[i])) {
|
||||||
log_message("错误: 屏幕 %d 的窗口不可见\n", i);
|
log_message("错误: 屏幕 %d 的窗口不可见\n", i);
|
||||||
all_windows_ok = 0;
|
all_windows_ok = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取窗口实际位置和大小
|
// 获取窗口实际位置和大小
|
||||||
int x, y, width, height;
|
int x, y, width, height;
|
||||||
gtk_window_get_position(GTK_WINDOW(info_windows[i]), &x, &y);
|
gtk_window_get_position(GTK_WINDOW(info_windows[i]), &x, &y);
|
||||||
gtk_window_get_size(GTK_WINDOW(info_windows[i]), &width, &height);
|
gtk_window_get_size(GTK_WINDOW(info_windows[i]), &width, &height);
|
||||||
|
|
||||||
// 计算期望的位置和大小
|
// 计算期望的位置和大小
|
||||||
int expected_x = screen_x_offsets[i] + 10;
|
int expected_x = screen_x_offsets[i] + 10;
|
||||||
int expected_y = screen_y_offsets[i] + 10;
|
int expected_y = screen_y_offsets[i] + 10;
|
||||||
int expected_width = screen_widths[i] - 20;
|
int expected_width = screen_widths[i] - 20;
|
||||||
int expected_height = screen_heights[i] - 20;
|
int expected_height = screen_heights[i] - 20;
|
||||||
|
|
||||||
// 确保期望尺寸不会为负值
|
// 确保期望尺寸不会为负值
|
||||||
if (expected_width < 100) expected_width = 100;
|
if (expected_width < 100) expected_width = 100;
|
||||||
if (expected_height < 100) expected_height = 100;
|
if (expected_height < 100) expected_height = 100;
|
||||||
|
|
||||||
// 校验位置(允许±5像素的误差)
|
// 校验位置(允许±5像素的误差)
|
||||||
if (abs(x - expected_x) > 5 || abs(y - expected_y) > 5) {
|
if (abs(x - expected_x) > 5 || abs(y - expected_y) > 5) {
|
||||||
log_message("警告: 屏幕 %d 的窗口位置不匹配。预期: (%d, %d), 实际: (%d, %d)\n",
|
log_message("警告: 屏幕 %d 的窗口位置不匹配。预期: (%d, %d), 实际: (%d, %d)\n",
|
||||||
i, expected_x, expected_y, x, y);
|
i, expected_x, expected_y, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验大小(允许±5像素的误差)
|
// 校验大小(允许±5像素的误差)
|
||||||
if (abs(width - expected_width) > 5 || abs(height - expected_height) > 5) {
|
if (abs(width - expected_width) > 5 || abs(height - expected_height) > 5) {
|
||||||
log_message("警告: 屏幕 %d 的窗口大小不匹配。预期: %dx%d, 实际: %dx%d\n",
|
log_message("警告: 屏幕 %d 的窗口大小不匹配。预期: %dx%d, 实际: %dx%d\n",
|
||||||
i, expected_width, expected_height, width, height);
|
i, expected_width, expected_height, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
log_message("屏幕 %d 校验完成: 位置=(%d, %d), 大小=%dx%d\n", i, x, y, width, height);
|
log_message("屏幕 %d 校验完成: 位置=(%d, %d), 大小=%dx%d\n", i, x, y, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (all_windows_ok) {
|
if (all_windows_ok) {
|
||||||
log_message("所有窗口校验成功\n");
|
log_message("所有窗口校验成功\n");
|
||||||
} else {
|
} else {
|
||||||
log_message("部分窗口校验失败\n");
|
log_message("部分窗口校验失败\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 开始下一轮测试
|
// 开始下一轮测试
|
||||||
void start_next_test_round() {
|
void start_next_test_round() {
|
||||||
current_test_round++;
|
current_test_round++;
|
||||||
|
|
||||||
if (current_test_round >= total_test_rounds) {
|
if (current_test_round >= total_test_rounds) {
|
||||||
log_message("所有测试轮次已完成,程序结束\n");
|
log_message("所有测试轮次已完成,程序结束\n");
|
||||||
|
|
||||||
// 清理资源
|
// 清理资源
|
||||||
for (int i = 0; i < total_screens; i++) {
|
for (int i = 0; i < total_screens; i++) {
|
||||||
free(screen_names[i]);
|
free(screen_names[i]);
|
||||||
}
|
}
|
||||||
free(screen_widths);
|
free(screen_widths);
|
||||||
free(screen_heights);
|
free(screen_heights);
|
||||||
free(screen_x_offsets);
|
free(screen_x_offsets);
|
||||||
free(screen_y_offsets);
|
free(screen_y_offsets);
|
||||||
free(screen_names);
|
free(screen_names);
|
||||||
free(info_windows);
|
free(info_windows);
|
||||||
|
|
||||||
// 关闭日志文件
|
// 关闭日志文件
|
||||||
if (log_file) {
|
if (log_file) {
|
||||||
fclose(log_file);
|
fclose(log_file);
|
||||||
log_file = NULL;
|
log_file = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_main_quit();
|
gtk_main_quit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_message("开始第 %d/%d 轮测试\n", current_test_round + 1, total_test_rounds);
|
log_message("开始第 %d/%d 轮测试\n", current_test_round + 1, total_test_rounds);
|
||||||
|
|
||||||
// 重置窗口索引
|
// 重置窗口索引
|
||||||
current_window_index = 0;
|
current_window_index = 0;
|
||||||
space_pressed = 0;
|
space_pressed = 0;
|
||||||
|
|
||||||
// 开始创建窗口(第一个窗口立即创建,后续窗口间隔3秒)
|
// 开始创建窗口(第一个窗口立即创建,后续窗口间隔3秒)
|
||||||
create_timeout_id = g_timeout_add(100, create_next_window, NULL);
|
create_timeout_id = g_timeout_add(100, create_next_window, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
log_file = fopen("screen_demo.log", "a");
|
log_file = fopen("screen_demo.log", "a");
|
||||||
if (!log_file) {
|
if (!log_file) {
|
||||||
printf("无法打开日志文件,将只输出到控制台\n");
|
printf("无法打开日志文件,将只输出到控制台\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
log_message("=== 多屏幕信息窗口演示程序 ===\n");
|
log_message("=== 多屏幕信息窗口演示程序 ===\n");
|
||||||
|
|
||||||
gtk_init(&argc, &argv);
|
gtk_init(&argc, &argv);
|
||||||
|
|
||||||
// 获取屏幕信息
|
// 获取屏幕信息
|
||||||
get_screen_info();
|
get_screen_info();
|
||||||
|
|
||||||
if (total_screens == 0) {
|
if (total_screens == 0) {
|
||||||
log_message("未找到任何屏幕,程序退出\n");
|
log_message("未找到任何屏幕,程序退出\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置测试轮次为屏幕数量
|
// 设置测试轮次为屏幕数量
|
||||||
total_test_rounds = total_screens;
|
total_test_rounds = total_screens;
|
||||||
log_message("将执行 %d 轮测试(每轮在所有屏幕上创建窗口)\n", total_test_rounds);
|
log_message("将执行 %d 轮测试(每轮在所有屏幕上创建窗口)\n", total_test_rounds);
|
||||||
|
|
||||||
// 开始第一轮测试
|
// 开始第一轮测试
|
||||||
current_test_round = -1;
|
current_test_round = -1;
|
||||||
start_next_test_round();
|
start_next_test_round();
|
||||||
|
|
||||||
gtk_main();
|
gtk_main();
|
||||||
|
|
||||||
log_message("程序结束\n");
|
log_message("程序结束\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -374,7 +374,26 @@ int is_touchscreen_device(const char* device_node, char* vid, char* pid, int buf
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!is_touchscreen) {
|
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);
|
udev_device_unref(dev);
|
||||||
@@ -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);
|
// log_message("识别到触摸设备,添加到列表中\n name=%s", name);
|
||||||
// 初始化当前设备信息结构体
|
// 初始化当前设备信息结构体
|
||||||
result[count].device_id = dev->deviceid;
|
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);
|
// log_message("识别到触摸设备,添加到列表中\n name=%s", name);
|
||||||
// 初始化当前设备信息结构体
|
// 初始化当前设备信息结构体
|
||||||
result[count].device_id = dev->deviceid;
|
result[count].device_id = dev->deviceid;
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
#define DEFAULT_POLL_INTERVAL 5 // 默认轮询间隔为5秒
|
#define DEFAULT_POLL_INTERVAL 5 // 默认轮询间隔为5秒
|
||||||
#define MAX_RETRY_ATTEMPTS 3 // 最大重试次数
|
#define MAX_RETRY_ATTEMPTS 3 // 最大重试次数
|
||||||
#define RETRY_DELAY 1 // 重试延迟(秒)
|
#define RETRY_DELAY 1 // 重试延迟(秒)
|
||||||
|
#define COOLDOWN_SECONDS 7 // 触摸屏检测冷却时间(秒)
|
||||||
|
|
||||||
FILE *log_file = NULL; // 日志文件指针
|
FILE *log_file = NULL; // 日志文件指针
|
||||||
// 函数提前声明
|
// 函数提前声明
|
||||||
@@ -84,6 +85,7 @@ int is_touchscreen(struct udev_device *dev) {
|
|||||||
|
|
||||||
if (strstr(lower_name, "touchscreen") != NULL ||
|
if (strstr(lower_name, "touchscreen") != NULL ||
|
||||||
strstr(lower_name, "touch") != NULL ||
|
strstr(lower_name, "touch") != NULL ||
|
||||||
|
strstr(lower_name, "ilitek") != NULL ||
|
||||||
strstr(lower_name, "tablet") != NULL) {
|
strstr(lower_name, "tablet") != NULL) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -212,6 +214,7 @@ void monitor_usb_devices(const char *control_file_path, int poll_interval) {
|
|||||||
printf("按Ctrl+C退出\n\n");
|
printf("按Ctrl+C退出\n\n");
|
||||||
|
|
||||||
// 主循环,监控设备事件
|
// 主循环,监控设备事件
|
||||||
|
time_t last_trigger_time = 0; // 上次触发控制文件的时间
|
||||||
while (keep_running) {
|
while (keep_running) {
|
||||||
fd_set fds;
|
fd_set fds;
|
||||||
struct timeval tv;
|
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 (use_control_file && touchscreen_detected) {
|
||||||
if (write_file_content_with_retry(control_file_path, 1) == 0) {
|
time_t now = time(NULL);
|
||||||
log_message("已更新控制文件状态为1\n");
|
if (difftime(now, last_trigger_time) < COOLDOWN_SECONDS) {
|
||||||
|
log_message("冷却中,跳过本次触发(距上次 %.0f 秒)\n", difftime(now, last_trigger_time));
|
||||||
// 等待文件状态恢复为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 {
|
} 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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user