Skip to content

Commit 2d3495a

Browse files
author
mahaotian
committed
Merge pull request '[DeviceShare] V1.0' (#12) from dev into master
2 parents 2d284d8 + 7536812 commit 2d3495a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1982
-475
lines changed

.DS_Store

-6 KB
Binary file not shown.

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,8 @@
33
./devices.json
44
/dist/
55
/build/
6-
*.spec
6+
*.spec
7+
**/*.key
8+
**/*.pyc
9+
**/*.db
10+
.DS_Store

cliptest.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import pyperclip
2+
3+
pyperclip.copy("123")
4+
print(pyperclip.paste())

deviceShare.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import sys
2+
3+
4+
from PyQt5.QtWidgets import QApplication, QDialog, QVBoxLayout, QPushButton, QLabel
5+
import qt_material
6+
from src.sharer.client import Client
7+
from src.sharer.server import Server
8+
9+
10+
class RoleSelectionDialog(QDialog):
11+
def __init__(self):
12+
super().__init__()
13+
14+
self.setFixedSize(400, 200)
15+
16+
self.setWindowTitle("身份选择")
17+
self.layout = QVBoxLayout()
18+
19+
self.server_button = QPushButton("主控机")
20+
self.client_button = QPushButton("被控机")
21+
22+
self.layout.addWidget(self.server_button)
23+
self.layout.addWidget(self.client_button)
24+
25+
self.setLayout(self.layout)
26+
27+
self.server_button.clicked.connect(self.select_server)
28+
self.client_button.clicked.connect(self.select_client)
29+
30+
self.selected_role = None
31+
32+
def select_server(self):
33+
self.selected_role = "server"
34+
self.accept()
35+
36+
def select_client(self):
37+
self.selected_role = "client"
38+
self.accept()
39+
40+
41+
def main():
42+
app = QApplication(sys.argv)
43+
qt_material.apply_stylesheet(app, theme='dark_blue.xml')
44+
selected_role = 'client'
45+
# 显示身份选择弹窗
46+
role_dialog = RoleSelectionDialog()
47+
if role_dialog.exec_() == QDialog.Accepted:
48+
selected_role = role_dialog.selected_role
49+
# 销毁 GUI
50+
app.exit()
51+
if selected_role == 'server':
52+
Server()
53+
elif selected_role == 'client':
54+
Client()
55+
56+
57+
if __name__ == "__main__":
58+
main()

devices.json

Lines changed: 0 additions & 1 deletion
This file was deleted.

keyboard_controller.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import threading
2+
import time
3+
4+
from evdev import InputDevice, categorize, ecodes, list_devices, UInput
5+
import os
6+
7+
8+
class KeyboardController:
9+
def __init__(self):
10+
devices = [InputDevice(path) for path in list_devices()]
11+
# 打印所有设备以便调试
12+
# for device in devices:
13+
# print(f"设备名: {device.name}, 设备路径: {device.path}")
14+
# 找到在线的键盘设备
15+
self.stop_event = threading.Event()
16+
17+
self.keyboard_devices = []
18+
for device in devices:
19+
if not os.path.exists(device.path):
20+
continue
21+
capabilities = device.capabilities()
22+
if ecodes.EV_KEY in capabilities and ecodes.EV_SYN in capabilities:
23+
if ecodes.KEY_A in capabilities[ecodes.EV_KEY]: # 检查是否有键盘按键
24+
self.keyboard_devices.append(device)
25+
capabilities = {ecodes.EV_KEY: list(ecodes.keys.keys())}
26+
self.ui = UInput(capabilities, name="virtual_keyboard")
27+
28+
def press(self, key):
29+
self.ui.write(ecodes.EV_KEY, key, 1)
30+
self.ui.syn()
31+
32+
def release(self, key):
33+
self.ui.write(ecodes.EV_KEY, ecodes.KEY_A, 0)
34+
self.ui.syn()
35+
36+
def click(self, click_type, keyData):
37+
key = self.keyFactory.outPut(keyData)
38+
if click_type == 'press':
39+
self.press(key)
40+
elif click_type == 'release':
41+
self.release(key)
42+
43+
def run_keyboard_listener(self, keyboard, on_press, on_release, suppress=False):
44+
if suppress:
45+
keyboard.grab()
46+
try:
47+
while not self.stop_event.is_set():
48+
event = keyboard.read_one() # 非阻塞读取事件
49+
if event:
50+
if event.type == ecodes.EV_KEY:
51+
key_event = categorize(event)
52+
if key_event.keystate == key_event.key_down:
53+
on_press(key_event.keycode)
54+
elif key_event.keystate == key_event.key_up:
55+
on_release(key_event.keycode)
56+
else:
57+
time.sleep(0.01) # 如果没有事件,休眠一段时间,减少 CPU 使用率
58+
except KeyboardInterrupt:
59+
print("Stopped listening for events.")
60+
except Exception as e:
61+
print(f"发生错误: {e}")
62+
finally:
63+
if suppress:
64+
keyboard.ungrab()
65+
66+
def keyboard_listener(self, on_press, on_release, suppress=False):
67+
self.listener = []
68+
for keyboard in self.keyboard_devices:
69+
print(f"监听设备: {keyboard.name} at {keyboard.path}")
70+
self.listener.append(
71+
threading.Thread(target=self.run_keyboard_listener, args=(keyboard, on_press, on_release, suppress)))
72+
for i in self.listener:
73+
i.start()
74+
return self.listener
75+
76+
def stop_listener(self):
77+
self.stop_event.set()
78+
self.listener.clear()
79+
80+
def __del__(self):
81+
self.ui.close()
82+
83+
84+
if __name__ == '__main__':
85+
def on_press(key):
86+
print(f"按下: {key}")
87+
88+
def on_release(key):
89+
print(f"释放: {key}")
90+
91+
keyboardController = KeyboardController()
92+
keyboardController.keyboard_listener(on_press, on_release, suppress=False)
93+
for i in range(10):
94+
keyboardController.press(ecodes.KEY_A)
95+
time.sleep(0.01)
96+
keyboardController.release(ecodes.KEY_A)
97+
time.sleep(1)
98+
keyboardController.stop_listener()

mouse_controller.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import threading
2+
import time
3+
4+
from evdev import InputDevice, categorize, ecodes, list_devices, UInput
5+
import subprocess
6+
7+
8+
class MouseController:
9+
10+
def __init__(self):
11+
devices = [InputDevice(path) for path in list_devices()]
12+
self.stop_event = threading.Event()
13+
self.mouse_devices = []
14+
for device in devices:
15+
capabilities = device.capabilities()
16+
if ecodes.EV_REL in capabilities and ecodes.EV_KEY in capabilities:
17+
if ecodes.REL_X in capabilities[ecodes.EV_REL] and ecodes.REL_Y in capabilities[ecodes.EV_REL]:
18+
if ecodes.BTN_LEFT in capabilities[ecodes.EV_KEY] or ecodes.BTN_RIGHT in capabilities[
19+
ecodes.EV_KEY]:
20+
self.mouse_devices.append(device)
21+
capabilities = {ecodes.EV_REL: [ecodes.REL_X, ecodes.REL_Y, ecodes.REL_WHEEL],
22+
ecodes.EV_KEY: [ecodes.BTN_LEFT, ecodes.BTN_RIGHT, ecodes.BTN_MIDDLE]}
23+
self.ui = UInput(capabilities, name="virtual_mouse")
24+
25+
def update_last_position(self):
26+
pass
27+
28+
def get_last_position(self):
29+
pass
30+
31+
def get_position(self):
32+
result = subprocess.run(['xdotool', 'getmouselocation'], stdout=subprocess.PIPE, stderr=subprocess.PIPE,
33+
text=True)
34+
output = result.stdout
35+
# 解析输出
36+
location = {}
37+
for item in output.split():
38+
key, value = item.split(':')
39+
location[key] = int(value)
40+
return location['x'], location['y']
41+
42+
def move_to(self, position: tuple):
43+
pass
44+
45+
def move(self, dx, dy):
46+
pass
47+
48+
def scroll(self, dx, dy):
49+
pass
50+
51+
def click(self, button, pressed):
52+
pass
53+
54+
def run_mouse_listener(self, mouse, on_click, on_move, on_scroll, suppress=False):
55+
if suppress:
56+
mouse.grab()
57+
try:
58+
while not self.stop_event.is_set():
59+
event = mouse.read_one() # 非阻塞读取事件
60+
if event:
61+
if event.type == ecodes.EV_REL:
62+
# if event.code == ecodes.REL_X:
63+
# on_move(event.value, 0)
64+
# elif event.code == ecodes.REL_Y:
65+
# on_move(0, event.value)
66+
# elif event.code == ecodes.REL_WHEEL:
67+
# on_scroll(0, event.value)
68+
print(f"REL: {event}")
69+
elif event.type == ecodes.EV_KEY:
70+
if event.code == ecodes.BTN_LEFT:
71+
if event.value == 1:
72+
on_click('Button.left', 'press')
73+
elif event.value == 0:
74+
on_click('Button.left', 'release')
75+
elif event.code == ecodes.BTN_RIGHT:
76+
if event.value == 1:
77+
on_click('Button.right', 'press')
78+
elif event.value == 0:
79+
on_click('Button.right', 'release')
80+
else:
81+
time.sleep(0.01)
82+
except KeyboardInterrupt:
83+
print("Stopped listening for events.")
84+
except Exception as e:
85+
print(f"发生错误: {e}")
86+
finally:
87+
if suppress:
88+
mouse.ungrab()
89+
90+
def mouse_listener(self, on_click, on_move, on_scroll, suppress=False):
91+
self.listener = []
92+
for mouse in self.mouse_devices:
93+
print(f"监听设备: {mouse.name} at {mouse.path}")
94+
self.listener.append(threading.Thread(target=self.run_mouse_listener,args=(mouse, on_click, on_move, on_scroll, suppress)))
95+
for i in self.listener:
96+
i.start()
97+
return self.listener
98+
99+
def stop_listener(self):
100+
self.stop_event.set()
101+
self.listener.clear()
102+
103+
def __del__(self):
104+
self.ui.close()
105+
106+
107+
if __name__ == '__main__':
108+
109+
# mouseController = MouseController()
110+
# mouseController.mouse_listener(None, None, None, suppress=True)
111+
# for i in range(10):
112+
# mouseController.click('Button.left', 'press')
113+
# time.sleep(0.01)
114+
# mouseController.click('Button.left', 'release')
115+
# time.sleep(1)
116+
# mouseController.stop_listener()
117+
ui = UInput()
118+
def move_mouse(dx,dy):
119+
ui.write(ecodes.EV_REL, ecodes.REL_X, dx)
120+
ui.write(ecodes.EV_REL, ecodes.REL_Y, dy)
121+
ui.syn()
122+
123+
move_mouse(100, 100)
124+
ui.close()

requirements.txt

96 Bytes
Binary file not shown.

resources/.DS_Store

-6 KB
Binary file not shown.

resources/devicelink.ico

2.4 KB
Binary file not shown.

0 commit comments

Comments
 (0)