2024-04-10 02:47:21 +08:00
|
|
|
|
from PIL import Image
|
|
|
|
|
import subprocess
|
2024-04-09 11:19:43 +08:00
|
|
|
|
import copy
|
|
|
|
|
import time
|
|
|
|
|
import threading
|
|
|
|
|
import queue
|
|
|
|
|
import serial
|
2024-04-10 02:47:21 +08:00
|
|
|
|
|
2024-04-13 02:17:34 +08:00
|
|
|
|
# 编辑好的图片路径
|
|
|
|
|
IMAGE_PATH = "./image/image_monitor.png"
|
2024-04-10 10:38:00 +08:00
|
|
|
|
# 串口号
|
2024-04-10 08:56:04 +08:00
|
|
|
|
COM_PORT = "COM33"
|
2024-04-10 10:38:00 +08:00
|
|
|
|
# 比特率
|
|
|
|
|
COM_BAUDRATE = 9600
|
|
|
|
|
# Android 多点触控数量
|
2024-04-10 08:56:04 +08:00
|
|
|
|
MAX_SLOT = 12
|
2024-04-10 10:38:00 +08:00
|
|
|
|
# 检测区域的像素值范围
|
2024-04-12 10:48:21 +08:00
|
|
|
|
AREA_SCOPE = 65
|
2024-04-14 03:36:26 +08:00
|
|
|
|
# Android 设备实际屏幕大小 (单位:像素)
|
|
|
|
|
ANDROID_ABS_MONITOR_SIZE = (1600, 2560)
|
|
|
|
|
# Android 设备触控屏幕大小 (单位:像素)
|
|
|
|
|
ANDROID_ABS_INPUT_SIZE = (1600, 2560)
|
|
|
|
|
# 是否开启屏幕反转(充电口朝上时开启该配置)
|
|
|
|
|
ANDROID_REVERSE_MONITOR = False
|
2024-04-13 00:26:26 +08:00
|
|
|
|
# touch_thread 是否启用sleep, 默认开启, 如果程序 CPU 占用较高则开启, 如果滑动时延迟极大请关闭
|
|
|
|
|
TOUCH_THREAD_SLEEP_MODE = True
|
|
|
|
|
# 每次 sleep 的延迟, 单位: 微秒, 默认 100 微秒
|
|
|
|
|
TOUCH_THREAD_SLEEP_DELAY = 100
|
2024-04-09 11:19:43 +08:00
|
|
|
|
|
|
|
|
|
exp_list = [
|
|
|
|
|
["A1", "A2", "A3", "A4", "A5", ],
|
|
|
|
|
["A6", "A7", "A8", "B1", "B2", ],
|
|
|
|
|
["B3", "B4", "B5", "B6", "B7", ],
|
|
|
|
|
["B8", "C1", "C2", "D1", "D2", ],
|
|
|
|
|
["D3", "D4", "D5", "D6", "D7", ],
|
|
|
|
|
["D8", "E1", "E2", "E3", "E4", ],
|
|
|
|
|
["E5", "E6", "E7", "E8", ],
|
|
|
|
|
]
|
2024-04-10 02:47:21 +08:00
|
|
|
|
exp_image_dict = {
|
|
|
|
|
"61": "A1", "65": "A2", "71": "A3", "75": "A4", "81": "A5", "85": "A6", "91": "A7", "95": "A8",
|
|
|
|
|
"101": "B1", "105": "B2", "111": "B3", "115": "B4", "121": "B5", "125": "B6", "130": "B7", "135": "B8",
|
|
|
|
|
"140": "C1", "145": "C2",
|
|
|
|
|
"150": "D1", "155": "D2", "160": "D3", "165": "D4", "170": "D5", "175": "D6", "180": "D7", "185": "D8",
|
|
|
|
|
"190": "E1", "195": "E2", "200": "E3", "205": "E4", "210": "E5", "215": "E6", "220": "E7", "225": "E8",
|
|
|
|
|
}
|
2024-04-09 11:19:43 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SerialManager:
|
2024-04-10 08:56:04 +08:00
|
|
|
|
p1Serial = serial.Serial(COM_PORT, COM_BAUDRATE)
|
2024-04-09 11:19:43 +08:00
|
|
|
|
settingPacket = bytearray([40, 0, 0, 0, 0, 41])
|
|
|
|
|
startUp = False
|
|
|
|
|
recvData = ""
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.touchQueue = queue.Queue()
|
2024-04-10 08:56:04 +08:00
|
|
|
|
self.data_lock = threading.Lock()
|
2024-04-09 11:19:43 +08:00
|
|
|
|
self.touchThread = threading.Thread(target=self.touch_thread)
|
2024-04-10 05:35:18 +08:00
|
|
|
|
self.writeThread = threading.Thread(target=self.write_thread)
|
|
|
|
|
self.now_touch_data = b''
|
|
|
|
|
self.now_touch_keys = []
|
2024-04-09 11:19:43 +08:00
|
|
|
|
self.ping_touch_thread()
|
|
|
|
|
|
|
|
|
|
def start(self):
|
2024-04-14 01:09:03 +08:00
|
|
|
|
print(f"开始监听 {COM_PORT} 串口...")
|
2024-04-09 11:19:43 +08:00
|
|
|
|
self.touchThread.start()
|
2024-04-10 05:35:18 +08:00
|
|
|
|
self.writeThread.start()
|
2024-04-09 11:19:43 +08:00
|
|
|
|
|
|
|
|
|
def ping_touch_thread(self):
|
2024-04-10 08:56:04 +08:00
|
|
|
|
self.touchQueue.put([self.build_touch_package(exp_list), []])
|
2024-04-09 11:19:43 +08:00
|
|
|
|
|
|
|
|
|
def touch_thread(self):
|
|
|
|
|
while True:
|
2024-04-13 00:26:26 +08:00
|
|
|
|
# start_time = time.perf_counter()
|
2024-04-09 11:19:43 +08:00
|
|
|
|
if self.p1Serial.is_open:
|
|
|
|
|
self.read_data(self.p1Serial)
|
|
|
|
|
if not self.touchQueue.empty():
|
|
|
|
|
# print("touchQueue 不为空,开始执行")
|
|
|
|
|
s_temp = self.touchQueue.get()
|
|
|
|
|
self.update_touch(s_temp)
|
2024-04-12 10:16:53 +08:00
|
|
|
|
# 延迟防止消耗 CPU 时间过长
|
2024-04-13 00:26:26 +08:00
|
|
|
|
if TOUCH_THREAD_SLEEP_MODE:
|
|
|
|
|
microsecond_sleep(TOUCH_THREAD_SLEEP_DELAY)
|
|
|
|
|
# print("单次执行时间:", (time.perf_counter() - start_time) * 1e3, "毫秒")
|
2024-04-09 11:19:43 +08:00
|
|
|
|
|
2024-04-10 05:35:18 +08:00
|
|
|
|
def write_thread(self):
|
|
|
|
|
while True:
|
|
|
|
|
# 延迟匹配波特率
|
2024-04-13 00:26:26 +08:00
|
|
|
|
time.sleep(0.0075) # 9600
|
2024-04-10 10:38:00 +08:00
|
|
|
|
# time.sleep(0.002) # 115200
|
2024-04-10 05:35:18 +08:00
|
|
|
|
if not self.startUp:
|
|
|
|
|
# print("当前没有启动")
|
|
|
|
|
continue
|
|
|
|
|
# print(self.now_touch_data)
|
2024-04-10 08:56:04 +08:00
|
|
|
|
with self.data_lock:
|
|
|
|
|
self.send_touch(self.p1Serial, self.now_touch_data)
|
2024-04-10 05:35:18 +08:00
|
|
|
|
|
2024-04-09 11:19:43 +08:00
|
|
|
|
def destroy(self):
|
|
|
|
|
self.touchThread.join()
|
|
|
|
|
self.p1Serial.close()
|
|
|
|
|
|
|
|
|
|
def read_data(self, ser):
|
|
|
|
|
if ser.in_waiting == 6:
|
|
|
|
|
self.recvData = ser.read(6).decode()
|
2024-04-10 02:47:21 +08:00
|
|
|
|
# print(self.recvData)
|
2024-04-09 11:19:43 +08:00
|
|
|
|
self.touch_setup(ser, self.recvData)
|
|
|
|
|
|
|
|
|
|
def touch_setup(self, ser, data):
|
|
|
|
|
byte_data = ord(data[3])
|
|
|
|
|
if byte_data in [76, 69]:
|
|
|
|
|
self.startUp = False
|
|
|
|
|
elif byte_data in [114, 107]:
|
|
|
|
|
for i in range(1, 5):
|
|
|
|
|
self.settingPacket[i] = ord(data[i])
|
|
|
|
|
ser.write(self.settingPacket)
|
|
|
|
|
elif byte_data == 65:
|
|
|
|
|
self.startUp = True
|
2024-04-13 01:55:18 +08:00
|
|
|
|
print("已连接到游戏")
|
2024-04-09 11:19:43 +08:00
|
|
|
|
|
|
|
|
|
def send_touch(self, ser, data):
|
|
|
|
|
ser.write(data)
|
|
|
|
|
|
2024-04-13 00:26:26 +08:00
|
|
|
|
# def build_touch_package(self, sl):
|
|
|
|
|
# sum_list = [0, 0, 0, 0, 0, 0, 0]
|
|
|
|
|
# for i in range(len(sl)):
|
|
|
|
|
# for j in range(len(sl[i])):
|
|
|
|
|
# if sl[i][j] == 1:
|
|
|
|
|
# sum_list[i] += (2 ** j)
|
|
|
|
|
# s = "28 "
|
|
|
|
|
# for i in sum_list:
|
|
|
|
|
# s += hex(i)[2:].zfill(2).upper() + " "
|
|
|
|
|
# s += "29"
|
|
|
|
|
# # print(s)
|
|
|
|
|
# return bytes.fromhex(s)
|
|
|
|
|
|
2024-04-09 11:19:43 +08:00
|
|
|
|
def build_touch_package(self, sl):
|
2024-04-13 00:26:26 +08:00
|
|
|
|
sum_list = [sum(2 ** j for j, val in enumerate(row) if val == 1) for row in sl]
|
|
|
|
|
hex_list = [hex(i)[2:].zfill(2).upper() for i in sum_list]
|
|
|
|
|
s = "28 " + " ".join(hex_list) + " 29"
|
2024-04-10 02:47:21 +08:00
|
|
|
|
# print(s)
|
2024-04-09 11:19:43 +08:00
|
|
|
|
return bytes.fromhex(s)
|
|
|
|
|
|
|
|
|
|
def update_touch(self, s_temp):
|
|
|
|
|
# if not self.startUp:
|
|
|
|
|
# print("当前没有启动")
|
|
|
|
|
# return
|
2024-04-10 08:56:04 +08:00
|
|
|
|
with self.data_lock:
|
|
|
|
|
self.now_touch_data = s_temp[0]
|
|
|
|
|
self.send_touch(self.p1Serial, s_temp[0])
|
|
|
|
|
self.now_touch_keys = s_temp[1]
|
|
|
|
|
print("Touch Keys:", s_temp[1])
|
2024-04-09 11:19:43 +08:00
|
|
|
|
# else:
|
2024-04-10 08:56:04 +08:00
|
|
|
|
# self.send_touch(self.p2Serial, s_temp[0])
|
2024-04-09 11:19:43 +08:00
|
|
|
|
|
2024-04-10 08:56:04 +08:00
|
|
|
|
def change_touch(self, sl, touch_keys):
|
|
|
|
|
self.touchQueue.put([self.build_touch_package(sl), touch_keys])
|
2024-04-09 11:19:43 +08:00
|
|
|
|
|
|
|
|
|
|
2024-04-13 00:26:26 +08:00
|
|
|
|
def microsecond_sleep(sleep_time):
|
|
|
|
|
end_time = time.perf_counter() + (sleep_time - 1.0) / 1e6 # 1.0是时间补偿,需要根据自己PC的性能去实测
|
|
|
|
|
while time.perf_counter() < end_time:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# def get_colors_in_area(x, y):
|
|
|
|
|
# colors = set() # 使用集合来存储颜色值,以避免重复
|
|
|
|
|
# for dx in [-AREA_SCOPE, 0, AREA_SCOPE]:
|
|
|
|
|
# for dy in [-AREA_SCOPE, 0, AREA_SCOPE]:
|
|
|
|
|
# if 0 <= (x + dx) < exp_image_width and 0 <= (y + dy) < exp_image_height:
|
|
|
|
|
# colors.add(str(exp_image.getpixel((x + dx, y + dy))[0]))
|
|
|
|
|
# return list(colors)
|
|
|
|
|
|
|
|
|
|
|
2024-04-10 10:38:00 +08:00
|
|
|
|
def get_colors_in_area(x, y):
|
2024-04-13 00:26:26 +08:00
|
|
|
|
colors = {str(exp_image.getpixel((x + dx, y + dy))[0]) for dx in [-AREA_SCOPE, 0, AREA_SCOPE] for dy in
|
|
|
|
|
[-AREA_SCOPE, 0, AREA_SCOPE] if 0 <= (x + dx) < exp_image_width and 0 <= (y + dy) < exp_image_height}
|
2024-04-10 10:38:00 +08:00
|
|
|
|
return list(colors)
|
|
|
|
|
|
|
|
|
|
|
2024-04-10 02:47:21 +08:00
|
|
|
|
def convert(touch_data):
|
|
|
|
|
copy_exp_list = copy.deepcopy(exp_list)
|
2024-04-13 00:26:26 +08:00
|
|
|
|
touch_keys = {exp_image_dict[r_str] for i in touch_data if i["p"] for r_str in get_colors_in_area(i["x"], i["y"]) if
|
|
|
|
|
r_str in exp_image_dict}
|
2024-04-10 05:35:18 +08:00
|
|
|
|
# print("Touch Keys:", touch_keys)
|
2024-04-13 00:26:26 +08:00
|
|
|
|
# touched = sum(1 for i in touch_data if i["p"])
|
|
|
|
|
# print("Touched:", touched)
|
2024-04-10 10:38:00 +08:00
|
|
|
|
touch_keys_list = list(touch_keys)
|
2024-04-13 00:26:26 +08:00
|
|
|
|
copy_exp_list = [[1 if item in touch_keys_list else 0 for item in sublist] for sublist in copy_exp_list]
|
2024-04-10 02:47:21 +08:00
|
|
|
|
# print(copy_exp_list)
|
2024-04-10 10:38:00 +08:00
|
|
|
|
serial_manager.change_touch(copy_exp_list, touch_keys_list)
|
2024-04-10 02:47:21 +08:00
|
|
|
|
|
|
|
|
|
|
2024-04-13 00:26:26 +08:00
|
|
|
|
# def convert(touch_data):
|
|
|
|
|
# copy_exp_list = copy.deepcopy(exp_list)
|
|
|
|
|
# touch_keys = set()
|
|
|
|
|
# touched = 0
|
|
|
|
|
# for i in touch_data:
|
|
|
|
|
# if not i["p"]:
|
|
|
|
|
# continue
|
|
|
|
|
# touched += 1
|
|
|
|
|
# x = i["x"]
|
|
|
|
|
# y = i["y"]
|
|
|
|
|
# for r_str in get_colors_in_area(x, y):
|
|
|
|
|
# if not r_str in exp_image_dict:
|
|
|
|
|
# continue
|
|
|
|
|
# touch_keys.add(exp_image_dict[r_str])
|
|
|
|
|
# # print("Touched:", touched)
|
|
|
|
|
# # print("Touch Keys:", touch_keys)
|
|
|
|
|
# touch_keys_list = list(touch_keys)
|
|
|
|
|
# for i in range(len(copy_exp_list)):
|
|
|
|
|
# for j in range(len(copy_exp_list[i])):
|
|
|
|
|
# if copy_exp_list[i][j] in touch_keys_list:
|
|
|
|
|
# copy_exp_list[i][j] = 1
|
|
|
|
|
# else:
|
|
|
|
|
# copy_exp_list[i][j] = 0
|
|
|
|
|
# # print(copy_exp_list)
|
|
|
|
|
# serial_manager.change_touch(copy_exp_list, touch_keys_list)
|
|
|
|
|
|
|
|
|
|
|
2024-04-14 03:36:26 +08:00
|
|
|
|
def calc_abs_x_y():
|
|
|
|
|
return (ANDROID_ABS_MONITOR_SIZE[0] / ANDROID_ABS_INPUT_SIZE[0] + ANDROID_ABS_MONITOR_SIZE[1] /
|
|
|
|
|
ANDROID_ABS_INPUT_SIZE[1]) / 2
|
|
|
|
|
|
|
|
|
|
|
2024-04-10 02:47:21 +08:00
|
|
|
|
def getevent():
|
|
|
|
|
# 存储多点触控数据的列表
|
2024-04-10 08:56:04 +08:00
|
|
|
|
touch_data = [{"p": False, "x": 0, "y": 0} for _ in range(MAX_SLOT)]
|
2024-04-10 02:47:21 +08:00
|
|
|
|
# 记录当前按下的触控点数目
|
|
|
|
|
touch_sum = 0
|
|
|
|
|
# 记录当前选择的 SLOT 作为索引
|
|
|
|
|
touch_index = 0
|
|
|
|
|
|
|
|
|
|
# 执行 adb shell getevent 命令并捕获输出
|
|
|
|
|
process = subprocess.Popen(['adb', 'shell', 'getevent', '-l'], stdout=subprocess.PIPE)
|
2024-04-10 05:35:18 +08:00
|
|
|
|
key_is_changed = False
|
2024-04-10 02:47:21 +08:00
|
|
|
|
|
|
|
|
|
# 读取实时输出
|
|
|
|
|
for line in iter(process.stdout.readline, b''):
|
|
|
|
|
try:
|
|
|
|
|
event = line.decode('utf-8').strip()
|
|
|
|
|
_, _, event_type, event_value = event.split()
|
|
|
|
|
if event_type == 'ABS_MT_POSITION_X':
|
2024-04-10 05:35:18 +08:00
|
|
|
|
key_is_changed = True
|
2024-04-14 03:36:26 +08:00
|
|
|
|
if not ANDROID_REVERSE_MONITOR:
|
|
|
|
|
touch_data[touch_index]["x"] = int(int(event_value, 16) * abs_multi)
|
2024-04-14 01:05:07 +08:00
|
|
|
|
else:
|
2024-04-14 03:36:26 +08:00
|
|
|
|
touch_data[touch_index]["x"] = ANDROID_ABS_MONITOR_SIZE[0] - int(int(event_value, 16) * abs_multi)
|
2024-04-10 02:47:21 +08:00
|
|
|
|
elif event_type == 'ABS_MT_POSITION_Y':
|
2024-04-10 05:35:18 +08:00
|
|
|
|
key_is_changed = True
|
2024-04-14 03:36:26 +08:00
|
|
|
|
if not ANDROID_REVERSE_MONITOR:
|
|
|
|
|
touch_data[touch_index]["y"] = int(int(event_value, 16) * abs_multi)
|
2024-04-14 01:05:07 +08:00
|
|
|
|
else:
|
2024-04-14 03:36:26 +08:00
|
|
|
|
touch_data[touch_index]["y"] = ANDROID_ABS_MONITOR_SIZE[1] - int(int(event_value, 16) * abs_multi)
|
2024-04-10 02:47:21 +08:00
|
|
|
|
elif event_type == 'SYN_REPORT':
|
2024-04-10 05:35:18 +08:00
|
|
|
|
if not key_is_changed:
|
|
|
|
|
continue
|
2024-04-10 02:47:21 +08:00
|
|
|
|
# print("Touch Data:", touch_data)
|
|
|
|
|
# 向 convert 函数发送数据
|
2024-04-10 05:35:18 +08:00
|
|
|
|
key_is_changed = False
|
2024-04-13 00:26:26 +08:00
|
|
|
|
# start_time = time.perf_counter()
|
2024-04-10 02:47:21 +08:00
|
|
|
|
convert(touch_data)
|
2024-04-13 00:26:26 +08:00
|
|
|
|
# print("单次执行时间:", (time.perf_counter() - start_time) * 1e3, "毫秒")
|
2024-04-10 02:47:21 +08:00
|
|
|
|
elif event_type == 'ABS_MT_SLOT':
|
2024-04-10 05:35:18 +08:00
|
|
|
|
key_is_changed = True
|
2024-04-10 02:47:21 +08:00
|
|
|
|
touch_index = int(event_value, 16)
|
|
|
|
|
if touch_index >= touch_sum:
|
|
|
|
|
touch_sum = touch_index + 1
|
|
|
|
|
elif event_type == 'ABS_MT_TRACKING_ID':
|
2024-04-10 05:35:18 +08:00
|
|
|
|
key_is_changed = True
|
2024-04-10 02:47:21 +08:00
|
|
|
|
if event_value == "ffffffff":
|
|
|
|
|
touch_data[touch_index]['p'] = False
|
2024-04-13 00:26:26 +08:00
|
|
|
|
touch_sum = max(0, touch_sum - 1)
|
2024-04-10 02:47:21 +08:00
|
|
|
|
else:
|
|
|
|
|
touch_data[touch_index]['p'] = True
|
|
|
|
|
touch_sum += 1
|
|
|
|
|
else:
|
|
|
|
|
continue
|
|
|
|
|
except Exception:
|
|
|
|
|
event_error_output = line.decode('utf-8')
|
|
|
|
|
if "name" in event_error_output:
|
|
|
|
|
continue
|
|
|
|
|
print(event_error_output)
|
|
|
|
|
|
|
|
|
|
|
2024-04-13 02:17:34 +08:00
|
|
|
|
exp_image = Image.open(IMAGE_PATH)
|
2024-04-12 10:16:53 +08:00
|
|
|
|
exp_image_width, exp_image_height = exp_image.size
|
2024-04-14 03:36:26 +08:00
|
|
|
|
abs_multi = 1
|
2024-04-12 10:16:53 +08:00
|
|
|
|
|
2024-04-09 11:19:43 +08:00
|
|
|
|
if __name__ == "__main__":
|
2024-04-14 03:36:26 +08:00
|
|
|
|
abs_multi = calc_abs_x_y()
|
|
|
|
|
print("当前触控区域大小倍数:", abs_multi)
|
|
|
|
|
print(('已' if ANDROID_REVERSE_MONITOR else '未') + "开启屏幕反转")
|
2024-04-09 11:19:43 +08:00
|
|
|
|
serial_manager = SerialManager()
|
|
|
|
|
serial_manager.start()
|
2024-04-13 01:55:18 +08:00
|
|
|
|
threading.Thread(target=getevent).start()
|
|
|
|
|
while True:
|
|
|
|
|
input_str = input()
|
|
|
|
|
if input_str == 'start':
|
|
|
|
|
serial_manager.startUp = True
|
|
|
|
|
print("已连接到游戏")
|
2024-04-14 03:36:26 +08:00
|
|
|
|
elif input_str == 'reverse':
|
|
|
|
|
ANDROID_REVERSE_MONITOR = not ANDROID_REVERSE_MONITOR
|
|
|
|
|
print("已" + ('开启' if ANDROID_REVERSE_MONITOR else '关闭') + "屏幕反转")
|
2024-04-13 01:55:18 +08:00
|
|
|
|
else:
|
|
|
|
|
print("未知的输入")
|