QTS3native 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 l-S4M-l
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,90 @@
1
+ Metadata-Version: 2.4
2
+ Name: QTS3native
3
+ Version: 0.1.1
4
+ Summary: PyQt menu, key listener, and RPCS3 hook helpers for Skate 3 native tooling
5
+ Author: S4M
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/l-S4M-l/QTS3native
8
+ Project-URL: Bug Tracker, https://github.com/l-S4M-l/QTS3native/issues
9
+ Project-URL: Ko-fi, https://ko-fi.com/chillsam2
10
+ Keywords: rpcs3,skate3,pyqt,modding,pymem
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: Microsoft :: Windows
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENCE
18
+ Requires-Dist: PyQt5
19
+ Requires-Dist: keyboard
20
+ Requires-Dist: pymem
21
+ Requires-Dist: clipboard
22
+ Dynamic: license-file
23
+
24
+ # QTS3native
25
+
26
+ QTS3native is a Python library for building Skate 3 native-style menus and input-driven tooling on top of RPCS3.
27
+
28
+ It bundles three main parts:
29
+
30
+ - a PyQt-based menu and UI system
31
+ - an RPCS3 controller and memory hook layer
32
+ - a global keyboard listener for menu input and text entry
33
+
34
+ The library is aimed at Windows-based Skate 3 tooling where you want a native-feeling in-game style menu driven by controller input, keyboard input, or both.
35
+
36
+ ## Features
37
+
38
+ - PyQt menu item system with buttons, toggles, sliders, option items, labels, and typing items
39
+ - controller polling thread for reading Skate 3 / RPCS3 input values
40
+ - bind handler for opening or triggering menu actions from controller input
41
+ - global keyboard listener for typing and keyboard-driven interactions
42
+ - internal Skate 3 menu text writer for rendering menu text into RPCS3 memory
43
+ - AOB scan helper for searching memory regions
44
+
45
+ ## Modules
46
+
47
+ ### `qtElements.py`
48
+
49
+ Contains the main menu and UI logic, including:
50
+
51
+ - `menu_controller`
52
+ - `sub_menu`
53
+ - menu item classes
54
+ - `bind_handler`
55
+ - controller-to-menu input routing
56
+
57
+ ### `rpcs3Hooks.py`
58
+
59
+ Contains the RPCS3-side helpers, including:
60
+
61
+ - `Controller`
62
+ - `ControllerThread`
63
+ - `skate_3_internal_menu_controller`
64
+
65
+ This module reads controller state from RPCS3 memory, applies deadzones, and exposes the values to the menu system. It also writes menu text into the Skate 3 internal menu text area and includes an AOB scanning helper.
66
+
67
+ ### `keylistener.py`
68
+
69
+ Contains the global keyboard listener used for keyboard input, text entry, and menu interaction.
70
+
71
+ ## Requirements
72
+
73
+ - Python 3.10+
74
+ - Windows
75
+ - RPCS3 running
76
+ - Skate 3 running in RPCS3
77
+ - permission to read and write process memory
78
+
79
+ ## Dependencies
80
+
81
+ - `PyQt5`
82
+ - `pymem`
83
+ - `clipboard`
84
+ - `keyboard`
85
+
86
+ ## Install
87
+
88
+ ```bash
89
+ pip install QTS3native
90
+ ```
@@ -0,0 +1,67 @@
1
+ # QTS3native
2
+
3
+ QTS3native is a Python library for building Skate 3 native-style menus and input-driven tooling on top of RPCS3.
4
+
5
+ It bundles three main parts:
6
+
7
+ - a PyQt-based menu and UI system
8
+ - an RPCS3 controller and memory hook layer
9
+ - a global keyboard listener for menu input and text entry
10
+
11
+ The library is aimed at Windows-based Skate 3 tooling where you want a native-feeling in-game style menu driven by controller input, keyboard input, or both.
12
+
13
+ ## Features
14
+
15
+ - PyQt menu item system with buttons, toggles, sliders, option items, labels, and typing items
16
+ - controller polling thread for reading Skate 3 / RPCS3 input values
17
+ - bind handler for opening or triggering menu actions from controller input
18
+ - global keyboard listener for typing and keyboard-driven interactions
19
+ - internal Skate 3 menu text writer for rendering menu text into RPCS3 memory
20
+ - AOB scan helper for searching memory regions
21
+
22
+ ## Modules
23
+
24
+ ### `qtElements.py`
25
+
26
+ Contains the main menu and UI logic, including:
27
+
28
+ - `menu_controller`
29
+ - `sub_menu`
30
+ - menu item classes
31
+ - `bind_handler`
32
+ - controller-to-menu input routing
33
+
34
+ ### `rpcs3Hooks.py`
35
+
36
+ Contains the RPCS3-side helpers, including:
37
+
38
+ - `Controller`
39
+ - `ControllerThread`
40
+ - `skate_3_internal_menu_controller`
41
+
42
+ This module reads controller state from RPCS3 memory, applies deadzones, and exposes the values to the menu system. It also writes menu text into the Skate 3 internal menu text area and includes an AOB scanning helper.
43
+
44
+ ### `keylistener.py`
45
+
46
+ Contains the global keyboard listener used for keyboard input, text entry, and menu interaction.
47
+
48
+ ## Requirements
49
+
50
+ - Python 3.10+
51
+ - Windows
52
+ - RPCS3 running
53
+ - Skate 3 running in RPCS3
54
+ - permission to read and write process memory
55
+
56
+ ## Dependencies
57
+
58
+ - `PyQt5`
59
+ - `pymem`
60
+ - `clipboard`
61
+ - `keyboard`
62
+
63
+ ## Install
64
+
65
+ ```bash
66
+ pip install QTS3native
67
+ ```
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["setuptools>=69", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "QTS3native"
7
+ version = "0.1.1"
8
+ description = "PyQt menu, key listener, and RPCS3 hook helpers for Skate 3 native tooling"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [
13
+ { name = "S4M" }
14
+ ]
15
+ keywords = ["rpcs3", "skate3", "pyqt", "modding", "pymem"]
16
+ classifiers = [
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3 :: Only",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Operating System :: Microsoft :: Windows",
21
+ ]
22
+
23
+ dependencies = [
24
+ "PyQt5",
25
+ "keyboard",
26
+ "pymem",
27
+ "clipboard"
28
+ ]
29
+
30
+ [tool.setuptools]
31
+ package-dir = {"" = "src"}
32
+
33
+ [tool.setuptools.packages.find]
34
+ where = ["src"]
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/l-S4M-l/QTS3native"
38
+ "Bug Tracker" = "https://github.com/l-S4M-l/QTS3native/issues"
39
+ Ko-fi = "https://ko-fi.com/chillsam2"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,5 @@
1
+ from .qtElements import *
2
+ from .rpcs3Hooks import *
3
+ from .keylistener import *
4
+
5
+ __version__ = "0.1.1"
@@ -0,0 +1,236 @@
1
+ import keyboard
2
+ from PyQt5 import QtCore
3
+
4
+
5
+ class GlobalKeyboardListener(QtCore.QThread):
6
+ keys_pressed = QtCore.pyqtSignal(tuple)
7
+
8
+ NON_SYMBOL_KEYS = [
9
+ "BACKSPACE",
10
+ "TAB",
11
+ "ENTER",
12
+ "SHIFT",
13
+ "CTRL",
14
+ "ALT",
15
+ "ALT_GR",
16
+ "PAUSE",
17
+ "CAPSLOCK",
18
+ "ESCAPE",
19
+ "SPACE",
20
+ "PAGE_UP",
21
+ "PAGE_DOWN",
22
+ "END",
23
+ "HOME",
24
+ "LEFT",
25
+ "UP",
26
+ "RIGHT",
27
+ "DOWN",
28
+ "INSERT",
29
+ "DELETE",
30
+ "PRINT_SCREEN",
31
+ "SCROLLLOCK",
32
+ "NUMLOCK",
33
+ "LWIN",
34
+ "RWIN",
35
+ "MENU",
36
+ "APPS",
37
+ "CLEAR",
38
+ "HELP",
39
+ "SELECT",
40
+ "EXECUTE",
41
+ "SLEEP",
42
+ "DECIMAL",
43
+ "SEPARATOR",
44
+ "SUBTRACT",
45
+ "ADD",
46
+ "MULTIPLY",
47
+ "DIVIDE",
48
+ "NUMPAD_0",
49
+ "NUMPAD_1",
50
+ "NUMPAD_2",
51
+ "NUMPAD_3",
52
+ "NUMPAD_4",
53
+ "NUMPAD_5",
54
+ "NUMPAD_6",
55
+ "NUMPAD_7",
56
+ "NUMPAD_8",
57
+ "NUMPAD_9",
58
+ "F1",
59
+ "F2",
60
+ "F3",
61
+ "F4",
62
+ "F5",
63
+ "F6",
64
+ "F7",
65
+ "F8",
66
+ "F9",
67
+ "F10",
68
+ "F11",
69
+ "F12",
70
+ "F13",
71
+ "F14",
72
+ "F15",
73
+ "F16",
74
+ "F17",
75
+ "F18",
76
+ "F19",
77
+ "F20",
78
+ "F21",
79
+ "F22",
80
+ "F23",
81
+ "F24",
82
+ "MEDIA_PLAY_PAUSE",
83
+ "MEDIA_STOP",
84
+ "MEDIA_NEXT_TRACK",
85
+ "MEDIA_PREV_TRACK",
86
+ "VOLUME_UP",
87
+ "VOLUME_DOWN",
88
+ "VOLUME_MUTE",
89
+ "BROWSER_BACK",
90
+ "BROWSER_FORWARD",
91
+ "BROWSER_REFRESH",
92
+ "BROWSER_STOP",
93
+ "BROWSER_SEARCH",
94
+ "BROWSER_FAVORITES",
95
+ "BROWSER_HOME",
96
+ "LAUNCH_MAIL",
97
+ "LAUNCH_MEDIA_SELECT",
98
+ "LAUNCH_APP1",
99
+ "LAUNCH_APP2",
100
+ ]
101
+
102
+ MODIFIER_KEYS = {"CTRL", "ALT", "SHIFT", "ALT_GR"}
103
+ MODIFIER_ORDER = {
104
+ "CTRL": 0,
105
+ "ALT": 1,
106
+ "ALT_GR": 2,
107
+ "SHIFT": 3,
108
+ }
109
+
110
+ KEY_NAME_MAP = {
111
+ "backspace": "BACKSPACE",
112
+ "tab": "TAB",
113
+ "enter": "ENTER",
114
+ "return": "ENTER",
115
+ "shift": "SHIFT",
116
+ "left shift": "SHIFT",
117
+ "right shift": "SHIFT",
118
+ "ctrl": "CTRL",
119
+ "left ctrl": "CTRL",
120
+ "right ctrl": "CTRL",
121
+ "alt": "ALT",
122
+ "left alt": "ALT",
123
+ "right alt": "ALT",
124
+ "alt gr": "ALT_GR",
125
+ "pause": "PAUSE",
126
+ "caps lock": "CAPSLOCK",
127
+ "esc": "ESCAPE",
128
+ "escape": "ESCAPE",
129
+ "space": "SPACE",
130
+ "page up": "PAGE_UP",
131
+ "page down": "PAGE_DOWN",
132
+ "end": "END",
133
+ "home": "HOME",
134
+ "left": "LEFT",
135
+ "up": "UP",
136
+ "right": "RIGHT",
137
+ "down": "DOWN",
138
+ "insert": "INSERT",
139
+ "delete": "DELETE",
140
+ "print screen": "PRINT_SCREEN",
141
+ "scroll lock": "SCROLLLOCK",
142
+ "num lock": "NUMLOCK",
143
+ "windows": "LWIN",
144
+ "left windows": "LWIN",
145
+ "right windows": "RWIN",
146
+ "menu": "MENU",
147
+ "apps": "APPS",
148
+ "decimal": "DECIMAL",
149
+ "separator": "SEPARATOR",
150
+ "subtract": "SUBTRACT",
151
+ "add": "ADD",
152
+ "multiply": "MULTIPLY",
153
+ "divide": "DIVIDE",
154
+ "play/pause media": "MEDIA_PLAY_PAUSE",
155
+ "stop media": "MEDIA_STOP",
156
+ "next track": "MEDIA_NEXT_TRACK",
157
+ "previous track": "MEDIA_PREV_TRACK",
158
+ "volume up": "VOLUME_UP",
159
+ "volume down": "VOLUME_DOWN",
160
+ "volume mute": "VOLUME_MUTE",
161
+ "browser back": "BROWSER_BACK",
162
+ "browser forward": "BROWSER_FORWARD",
163
+ "browser refresh": "BROWSER_REFRESH",
164
+ "browser stop": "BROWSER_STOP",
165
+ "browser search": "BROWSER_SEARCH",
166
+ "browser favorites": "BROWSER_FAVORITES",
167
+ "browser home": "BROWSER_HOME",
168
+ "launch mail": "LAUNCH_MAIL",
169
+ "launch media select": "LAUNCH_MEDIA_SELECT",
170
+ "launch app 1": "LAUNCH_APP1",
171
+ "launch app 2": "LAUNCH_APP2",
172
+ }
173
+
174
+ def __init__(self, parent=None):
175
+ super().__init__(parent)
176
+ self._pressed_keys = set()
177
+ self._hook = None
178
+ self._running = False
179
+
180
+ def normalise_key_name(self, key_name):
181
+ if key_name is None:
182
+ return None
183
+
184
+ key_name = str(key_name).strip().lower()
185
+
186
+ if key_name in self.KEY_NAME_MAP:
187
+ return self.KEY_NAME_MAP[key_name]
188
+
189
+ if key_name.startswith("numpad "):
190
+ suffix = key_name.replace("numpad ", "")
191
+ if suffix.isdigit():
192
+ return f"NUMPAD_{suffix}"
193
+
194
+ if key_name.startswith("f") and key_name[1:].isdigit():
195
+ return key_name.upper()
196
+
197
+ if len(key_name) == 1:
198
+ return key_name
199
+
200
+ return key_name.upper().replace(" ", "_")
201
+
202
+ def build_key_tuple(self, current_key):
203
+ held_modifiers = [key for key in self._pressed_keys if key in self.MODIFIER_KEYS]
204
+ held_modifiers.sort(key=lambda key: self.MODIFIER_ORDER.get(key, 999))
205
+
206
+ if current_key in self.MODIFIER_KEYS:
207
+ return tuple(held_modifiers)
208
+
209
+ return tuple(held_modifiers + [current_key])
210
+
211
+ def _keyboard_callback(self, event):
212
+ key_name = self.normalise_key_name(event.name)
213
+ if key_name is None:
214
+ return
215
+
216
+ if event.event_type == "down":
217
+ self._pressed_keys.add(key_name)
218
+ self.keys_pressed.emit(self.build_key_tuple(key_name))
219
+
220
+ elif event.event_type == "up":
221
+ self._pressed_keys.discard(key_name)
222
+
223
+ def run(self):
224
+ self._running = True
225
+ self._hook = keyboard.hook(self._keyboard_callback)
226
+
227
+ while self._running:
228
+ self.msleep(10)
229
+
230
+ if self._hook is not None:
231
+ keyboard.unhook(self._hook)
232
+ self._hook = None
233
+
234
+ def stop(self):
235
+ self._running = False
236
+ self.wait()