-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
113 lines (89 loc) · 3.78 KB
/
Copy pathmain.py
File metadata and controls
113 lines (89 loc) · 3.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
"""
R9Tools - Gaming Accessibility Toolkit
Run as administrator (required for Interception driver).
"""
import sys
import subprocess
from PySide6.QtCore import qInstallMessageHandler, QtMsgType
from PySide6.QtWidgets import QApplication
# Interception kernel filter driver service names
_INTERCEPTION_SERVICES = ["keyboard_filter", "mouse_filter"]
def _interception_driver(start: bool) -> None:
"""Start or stop the Interception kernel filter driver services.
Requires administrator privileges (enforced by the OS)."""
action = "start" if start else "stop"
for svc in _INTERCEPTION_SERVICES:
subprocess.run(
["sc", action, svc],
capture_output=True, # suppress console output
)
import profiles as prof
from recoil import RecoilEngine
from macro_engine import MacroEngine
from bridge import UIBridge
from overlay_window import OverlayWindow
from stats_poller import StatsPoller
from stats_overlay import StatsOverlayWindow
from panel_window import PanelWindow
def _qt_message_filter(msg_type, _context, msg):
if "Unable to set geometry" in msg:
return
if msg_type == QtMsgType.QtWarningMsg:
print(f"Qt warning: {msg}", file=sys.stderr)
elif msg_type == QtMsgType.QtCriticalMsg:
print(f"Qt critical: {msg}", file=sys.stderr)
elif msg_type == QtMsgType.QtFatalMsg:
print(f"Qt fatal: {msg}", file=sys.stderr)
def main():
_interception_driver(start=True)
qInstallMessageHandler(_qt_message_filter)
app = QApplication(sys.argv)
profileData = prof.load()
cfg = prof.activeSettings(profileData)
cfg["recoil"]["enabled"] = False # always start disabled
cfg["crosshair"]["enabled"] = False
cfg["remapper"]["enabled"] = False
cfg.setdefault("stats", {})["enabled"] = False
engine = RecoilEngine(cfg)
macro_engine = MacroEngine(cfg)
engine.setMacroEngine(macro_engine)
# Overlays need cfg before onSettingsChanged is defined
overlay_win = OverlayWindow(cfg, engine)
stats_overlay = StatsOverlayWindow(cfg)
stats_poller = StatsPoller(cfg)
def onSettingsChanged(updated: dict):
engine.updateSettings(updated)
macro_engine.updateSettings(updated)
stats_poller.updateSettings(updated)
overlay_win.refresh()
stats_overlay.applySettings()
# Signal bridge — lives on the main thread; engine stores .emit references.
# Cross-thread emissions are automatically delivered via QueuedConnection.
bridge = UIBridge()
panel_win = PanelWindow(cfg, profileData, engine, macro_engine, onSettingsChanged)
# Bridge → UI
bridge.overlayToggled.connect(panel_win.toggleOverlay)
bridge.recoilToggled.connect(panel_win.onRecoilToggled)
bridge.recoilToggled.connect(lambda _: overlay_win.refresh())
bridge.strengthChanged.connect(panel_win.onStrengthChanged)
bridge.strengthChanged.connect(overlay_win.showStrengthIndicator)
bridge.statsUpdated.connect(stats_overlay.updateStats)
bridge.quitRequested.connect(app.quit)
# Engine → bridge (interception thread calls these directly)
engine.setOverlayCallback(bridge.overlayToggled.emit)
engine.setToggleCallback(bridge.recoilToggled.emit)
engine.setStrengthCallback(bridge.strengthChanged.emit)
engine.setQuitCallback(bridge.quitRequested.emit)
# StatsPoller → bridge (poller thread → main thread via QueuedConnection)
stats_poller.setCallback(bridge.statsUpdated.emit)
engine.start()
stats_poller.start()
# overlay_win and stats_overlay start hidden; shown on demand
# panel_win starts hidden; overlay hotkey shows/hides it
app.exec()
engine.stop()
macro_engine.stop()
stats_poller.stop()
_interception_driver(start=False)
if __name__ == "__main__":
main()