pyglet 2.1.12__py3-none-any.whl → 3.0.dev1__py3-none-any.whl
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.
- pyglet/__init__.py +67 -61
- pyglet/__init__.pyi +15 -8
- pyglet/app/__init__.py +22 -13
- pyglet/app/async_app.py +212 -0
- pyglet/app/base.py +2 -1
- pyglet/app/{xlib.py → linux.py} +3 -3
- pyglet/config/__init__.py +101 -0
- pyglet/config/gl/__init__.py +30 -0
- pyglet/config/gl/egl.py +120 -0
- pyglet/config/gl/macos.py +262 -0
- pyglet/config/gl/windows.py +267 -0
- pyglet/config/gl/x11.py +142 -0
- pyglet/customtypes.py +43 -2
- pyglet/display/__init__.py +8 -6
- pyglet/display/base.py +3 -63
- pyglet/display/cocoa.py +12 -17
- pyglet/display/emscripten.py +39 -0
- pyglet/display/headless.py +23 -30
- pyglet/display/wayland.py +157 -0
- pyglet/display/win32.py +4 -17
- pyglet/display/xlib.py +19 -27
- pyglet/display/xlib_vidmoderestore.py +2 -2
- pyglet/enums.py +183 -0
- pyglet/event.py +0 -1
- pyglet/experimental/geoshader_sprite.py +15 -13
- pyglet/experimental/hidraw.py +6 -15
- pyglet/experimental/multitexture_sprite.py +31 -19
- pyglet/experimental/particles.py +13 -35
- pyglet/font/__init__.py +251 -85
- pyglet/font/base.py +116 -61
- pyglet/font/dwrite/__init__.py +349 -204
- pyglet/font/dwrite/dwrite_lib.py +27 -5
- pyglet/font/fontconfig.py +14 -6
- pyglet/font/freetype.py +138 -87
- pyglet/font/freetype_lib.py +19 -0
- pyglet/font/group.py +179 -0
- pyglet/font/harfbuzz/__init__.py +3 -3
- pyglet/font/pyodide_js.py +310 -0
- pyglet/font/quartz.py +319 -126
- pyglet/font/ttf.py +45 -3
- pyglet/font/user.py +14 -19
- pyglet/font/win32.py +45 -21
- pyglet/graphics/__init__.py +8 -787
- pyglet/graphics/allocation.py +115 -1
- pyglet/graphics/api/__init__.py +77 -0
- pyglet/graphics/api/base.py +299 -0
- pyglet/graphics/api/gl/__init__.py +58 -0
- pyglet/graphics/api/gl/base.py +24 -0
- pyglet/graphics/{vertexbuffer.py → api/gl/buffer.py} +104 -159
- pyglet/graphics/api/gl/cocoa/context.py +76 -0
- pyglet/graphics/api/gl/context.py +391 -0
- pyglet/graphics/api/gl/default_shaders.py +0 -0
- pyglet/graphics/api/gl/draw.py +627 -0
- pyglet/graphics/api/gl/egl/__init__.py +0 -0
- pyglet/graphics/api/gl/egl/context.py +92 -0
- pyglet/graphics/api/gl/enums.py +76 -0
- pyglet/graphics/api/gl/framebuffer.py +315 -0
- pyglet/graphics/api/gl/gl.py +5463 -0
- pyglet/graphics/api/gl/gl_info.py +188 -0
- pyglet/graphics/api/gl/global_opengl.py +226 -0
- pyglet/{gl → graphics/api/gl}/lib.py +34 -18
- pyglet/graphics/api/gl/shader.py +1476 -0
- pyglet/graphics/api/gl/shapes.py +55 -0
- pyglet/graphics/api/gl/sprite.py +102 -0
- pyglet/graphics/api/gl/state.py +219 -0
- pyglet/graphics/api/gl/text.py +190 -0
- pyglet/graphics/api/gl/texture.py +1526 -0
- pyglet/graphics/{vertexarray.py → api/gl/vertexarray.py} +11 -13
- pyglet/graphics/api/gl/vertexdomain.py +751 -0
- pyglet/graphics/api/gl/win32/__init__.py +0 -0
- pyglet/graphics/api/gl/win32/context.py +108 -0
- pyglet/graphics/api/gl/win32/wgl_info.py +24 -0
- pyglet/graphics/api/gl/xlib/__init__.py +0 -0
- pyglet/graphics/api/gl/xlib/context.py +174 -0
- pyglet/{gl → graphics/api/gl/xlib}/glx_info.py +26 -31
- pyglet/graphics/api/gl1/__init__.py +0 -0
- pyglet/{gl → graphics/api/gl1}/gl_compat.py +3 -2
- pyglet/graphics/api/gl2/__init__.py +0 -0
- pyglet/graphics/api/gl2/buffer.py +320 -0
- pyglet/graphics/api/gl2/draw.py +600 -0
- pyglet/graphics/api/gl2/global_opengl.py +122 -0
- pyglet/graphics/api/gl2/shader.py +200 -0
- pyglet/graphics/api/gl2/shapes.py +51 -0
- pyglet/graphics/api/gl2/sprite.py +79 -0
- pyglet/graphics/api/gl2/text.py +175 -0
- pyglet/graphics/api/gl2/vertexdomain.py +364 -0
- pyglet/graphics/api/webgl/__init__.py +233 -0
- pyglet/graphics/api/webgl/buffer.py +302 -0
- pyglet/graphics/api/webgl/context.py +234 -0
- pyglet/graphics/api/webgl/draw.py +590 -0
- pyglet/graphics/api/webgl/enums.py +76 -0
- pyglet/graphics/api/webgl/framebuffer.py +360 -0
- pyglet/graphics/api/webgl/gl.py +1537 -0
- pyglet/graphics/api/webgl/gl_info.py +130 -0
- pyglet/graphics/api/webgl/shader.py +1346 -0
- pyglet/graphics/api/webgl/shapes.py +92 -0
- pyglet/graphics/api/webgl/sprite.py +102 -0
- pyglet/graphics/api/webgl/state.py +227 -0
- pyglet/graphics/api/webgl/text.py +187 -0
- pyglet/graphics/api/webgl/texture.py +1227 -0
- pyglet/graphics/api/webgl/vertexarray.py +54 -0
- pyglet/graphics/api/webgl/vertexdomain.py +616 -0
- pyglet/graphics/api/webgl/webgl_js.pyi +307 -0
- pyglet/{image → graphics}/atlas.py +33 -32
- pyglet/graphics/base.py +10 -0
- pyglet/graphics/buffer.py +245 -0
- pyglet/graphics/draw.py +578 -0
- pyglet/graphics/framebuffer.py +26 -0
- pyglet/graphics/instance.py +178 -69
- pyglet/graphics/shader.py +267 -1553
- pyglet/graphics/state.py +83 -0
- pyglet/graphics/texture.py +703 -0
- pyglet/graphics/vertexdomain.py +695 -538
- pyglet/gui/ninepatch.py +10 -10
- pyglet/gui/widgets.py +120 -10
- pyglet/image/__init__.py +20 -1973
- pyglet/image/animation.py +12 -12
- pyglet/image/base.py +730 -0
- pyglet/image/codecs/__init__.py +9 -0
- pyglet/image/codecs/bmp.py +53 -30
- pyglet/image/codecs/dds.py +53 -31
- pyglet/image/codecs/gdiplus.py +38 -14
- pyglet/image/codecs/gdkpixbuf2.py +0 -2
- pyglet/image/codecs/js_image.py +99 -0
- pyglet/image/codecs/ktx2.py +161 -0
- pyglet/image/codecs/pil.py +1 -1
- pyglet/image/codecs/png.py +1 -1
- pyglet/image/codecs/wic.py +11 -2
- pyglet/info.py +26 -24
- pyglet/input/__init__.py +8 -0
- pyglet/input/base.py +163 -105
- pyglet/input/controller.py +13 -19
- pyglet/input/controller_db.py +39 -24
- pyglet/input/emscripten/__init__.py +18 -0
- pyglet/input/emscripten/gamepad_js.py +397 -0
- pyglet/input/linux/__init__.py +11 -5
- pyglet/input/linux/evdev.py +10 -11
- pyglet/input/linux/x11_xinput.py +2 -2
- pyglet/input/linux/x11_xinput_tablet.py +1 -1
- pyglet/input/macos/__init__.py +7 -2
- pyglet/input/macos/darwin_gc.py +559 -0
- pyglet/input/win32/__init__.py +1 -1
- pyglet/input/win32/directinput.py +34 -29
- pyglet/input/win32/xinput.py +11 -61
- pyglet/lib.py +3 -3
- pyglet/libs/__init__.py +1 -1
- pyglet/{gl → libs/darwin}/agl.py +1 -1
- pyglet/libs/darwin/cocoapy/__init__.py +2 -2
- pyglet/libs/darwin/cocoapy/cocoahelpers.py +181 -0
- pyglet/libs/darwin/cocoapy/cocoalibs.py +31 -0
- pyglet/libs/darwin/cocoapy/cocoatypes.py +27 -0
- pyglet/libs/darwin/cocoapy/runtime.py +81 -45
- pyglet/libs/darwin/coreaudio.py +4 -4
- pyglet/{gl → libs/darwin}/lib_agl.py +9 -8
- pyglet/libs/darwin/quartzkey.py +1 -3
- pyglet/libs/egl/__init__.py +2 -0
- pyglet/libs/egl/egl_lib.py +576 -0
- pyglet/libs/egl/eglext.py +51 -5
- pyglet/libs/linux/__init__.py +0 -0
- pyglet/libs/linux/egl/__init__.py +0 -0
- pyglet/libs/linux/egl/eglext.py +22 -0
- pyglet/libs/linux/glx/__init__.py +0 -0
- pyglet/{gl → libs/linux/glx}/glx.py +13 -14
- pyglet/{gl → libs/linux/glx}/glxext_arb.py +408 -192
- pyglet/{gl → libs/linux/glx}/glxext_mesa.py +1 -1
- pyglet/{gl → libs/linux/glx}/glxext_nv.py +345 -164
- pyglet/{gl → libs/linux/glx}/lib_glx.py +3 -2
- pyglet/libs/linux/wayland/__init__.py +0 -0
- pyglet/libs/linux/wayland/client.py +1068 -0
- pyglet/libs/linux/wayland/lib_wayland.py +207 -0
- pyglet/libs/linux/wayland/wayland_egl.py +38 -0
- pyglet/libs/{wayland → linux/wayland}/xkbcommon.py +26 -0
- pyglet/libs/{x11 → linux/x11}/xf86vmode.py +4 -4
- pyglet/libs/{x11 → linux/x11}/xinerama.py +2 -2
- pyglet/libs/{x11 → linux/x11}/xinput.py +10 -10
- pyglet/libs/linux/x11/xrandr.py +0 -0
- pyglet/libs/{x11 → linux/x11}/xrender.py +1 -1
- pyglet/libs/shared/__init__.py +0 -0
- pyglet/libs/shared/spirv/__init__.py +0 -0
- pyglet/libs/shared/spirv/lib_shaderc.py +85 -0
- pyglet/libs/shared/spirv/lib_spirv_cross.py +126 -0
- pyglet/libs/win32/__init__.py +27 -5
- pyglet/libs/win32/constants.py +59 -48
- pyglet/libs/win32/context_managers.py +20 -3
- pyglet/libs/win32/dinput.py +105 -88
- pyglet/{gl → libs/win32}/lib_wgl.py +52 -26
- pyglet/libs/win32/types.py +58 -23
- pyglet/{gl → libs/win32}/wgl.py +32 -25
- pyglet/{gl → libs/win32}/wglext_arb.py +364 -2
- pyglet/media/__init__.py +9 -10
- pyglet/media/codecs/__init__.py +12 -1
- pyglet/media/codecs/base.py +99 -96
- pyglet/media/codecs/ffmpeg.py +2 -2
- pyglet/media/codecs/ffmpeg_lib/libavformat.py +3 -8
- pyglet/media/codecs/webaudio_pyodide.py +111 -0
- pyglet/media/drivers/__init__.py +9 -4
- pyglet/media/drivers/base.py +4 -4
- pyglet/media/drivers/openal/__init__.py +1 -1
- pyglet/media/drivers/openal/adaptation.py +3 -3
- pyglet/media/drivers/pulse/__init__.py +1 -1
- pyglet/media/drivers/pulse/adaptation.py +3 -3
- pyglet/media/drivers/pyodide_js/__init__.py +8 -0
- pyglet/media/drivers/pyodide_js/adaptation.py +288 -0
- pyglet/media/drivers/xaudio2/adaptation.py +3 -3
- pyglet/media/player.py +276 -193
- pyglet/media/player_worker_thread.py +1 -1
- pyglet/model/__init__.py +39 -29
- pyglet/model/codecs/base.py +4 -4
- pyglet/model/codecs/gltf.py +3 -3
- pyglet/model/codecs/obj.py +71 -43
- pyglet/resource.py +129 -78
- pyglet/shapes.py +147 -177
- pyglet/sprite.py +47 -164
- pyglet/text/__init__.py +44 -54
- pyglet/text/caret.py +12 -7
- pyglet/text/document.py +19 -17
- pyglet/text/formats/html.py +2 -2
- pyglet/text/formats/structured.py +10 -40
- pyglet/text/layout/__init__.py +20 -13
- pyglet/text/layout/base.py +176 -287
- pyglet/text/layout/incremental.py +9 -10
- pyglet/text/layout/scrolling.py +7 -95
- pyglet/window/__init__.py +183 -172
- pyglet/window/cocoa/__init__.py +62 -51
- pyglet/window/cocoa/pyglet_delegate.py +2 -25
- pyglet/window/cocoa/pyglet_view.py +9 -8
- pyglet/window/dialog/__init__.py +184 -0
- pyglet/window/dialog/base.py +99 -0
- pyglet/window/dialog/darwin.py +121 -0
- pyglet/window/dialog/linux.py +72 -0
- pyglet/window/dialog/windows.py +194 -0
- pyglet/window/emscripten/__init__.py +779 -0
- pyglet/window/headless/__init__.py +44 -28
- pyglet/window/key.py +2 -0
- pyglet/window/mouse.py +2 -2
- pyglet/window/wayland/__init__.py +377 -0
- pyglet/window/win32/__init__.py +101 -46
- pyglet/window/xlib/__init__.py +104 -66
- {pyglet-2.1.12.dist-info → pyglet-3.0.dev1.dist-info}/METADATA +2 -3
- pyglet-3.0.dev1.dist-info/RECORD +322 -0
- {pyglet-2.1.12.dist-info → pyglet-3.0.dev1.dist-info}/WHEEL +1 -1
- pyglet/gl/__init__.py +0 -208
- pyglet/gl/base.py +0 -499
- pyglet/gl/cocoa.py +0 -309
- pyglet/gl/gl.py +0 -4625
- pyglet/gl/gl.pyi +0 -2320
- pyglet/gl/gl_compat.pyi +0 -3097
- pyglet/gl/gl_info.py +0 -190
- pyglet/gl/headless.py +0 -166
- pyglet/gl/wgl_info.py +0 -36
- pyglet/gl/wglext_nv.py +0 -1096
- pyglet/gl/win32.py +0 -268
- pyglet/gl/xlib.py +0 -295
- pyglet/image/buffer.py +0 -274
- pyglet/image/codecs/s3tc.py +0 -354
- pyglet/libs/x11/xrandr.py +0 -166
- pyglet-2.1.12.dist-info/RECORD +0 -234
- /pyglet/{libs/wayland → graphics/api/gl/cocoa}/__init__.py +0 -0
- /pyglet/libs/{egl → linux/egl}/egl.py +0 -0
- /pyglet/libs/{egl → linux/egl}/lib.py +0 -0
- /pyglet/libs/{ioctl.py → linux/ioctl.py} +0 -0
- /pyglet/libs/{wayland → linux/wayland}/gbm.py +0 -0
- /pyglet/libs/{x11 → linux/x11}/__init__.py +0 -0
- /pyglet/libs/{x11 → linux/x11}/cursorfont.py +0 -0
- /pyglet/libs/{x11 → linux/x11}/xlib.py +0 -0
- /pyglet/libs/{x11 → linux/x11}/xsync.py +0 -0
- {pyglet-2.1.12.dist-info/licenses → pyglet-3.0.dev1.dist-info}/LICENSE +0 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import warnings
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
import js
|
|
8
|
+
from js import console, navigator
|
|
9
|
+
from pyodide.ffi import create_proxy
|
|
10
|
+
|
|
11
|
+
import pyglet
|
|
12
|
+
from pyglet.input import Button, Device
|
|
13
|
+
from pyglet.input.base import AbsoluteAxis, Button, Controller, ControllerManager, Control
|
|
14
|
+
from pyglet.math import Vec2
|
|
15
|
+
|
|
16
|
+
class TriggerButton(Control):
|
|
17
|
+
"""A combination of a Button and an Axis.
|
|
18
|
+
|
|
19
|
+
A trigger can behave like an axis between 0 and 1, but many games also want to treat it like a button press for
|
|
20
|
+
fully down or fully released.
|
|
21
|
+
|
|
22
|
+
When it reaches the maximum value, it will dispatch ``on_press``, and ``on_release`` on the minimum value.
|
|
23
|
+
"""
|
|
24
|
+
def __init__(self, name: str, minimum: float, maximum: float, raw_name: None | str = None, inverted: bool = False) -> None:
|
|
25
|
+
super().__init__(name, raw_name, inverted)
|
|
26
|
+
self.min = minimum
|
|
27
|
+
self.max = maximum
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def value(self) -> float:
|
|
31
|
+
return self._value
|
|
32
|
+
|
|
33
|
+
@value.setter
|
|
34
|
+
def value(self, newvalue: float) -> None:
|
|
35
|
+
if newvalue == self._value:
|
|
36
|
+
return
|
|
37
|
+
self._value = newvalue
|
|
38
|
+
self.dispatch_event('on_change', bool(newvalue))
|
|
39
|
+
if newvalue >= self.max:
|
|
40
|
+
self.dispatch_event('on_press')
|
|
41
|
+
elif newvalue <= self.min:
|
|
42
|
+
self.dispatch_event('on_release')
|
|
43
|
+
|
|
44
|
+
_standard_button_map = {
|
|
45
|
+
0: "a",
|
|
46
|
+
1: "b",
|
|
47
|
+
2: "x",
|
|
48
|
+
3: "y",
|
|
49
|
+
4: "leftshoulder",
|
|
50
|
+
5: "rightshoulder",
|
|
51
|
+
6: "lefttrigger",
|
|
52
|
+
7: "righttrigger",
|
|
53
|
+
8: "back",
|
|
54
|
+
9: "start",
|
|
55
|
+
10: "leftstick",
|
|
56
|
+
11: "rightstick",
|
|
57
|
+
12: "dpup",
|
|
58
|
+
13: "dpdown",
|
|
59
|
+
14: "dpleft",
|
|
60
|
+
15: "dpright",
|
|
61
|
+
16: "guide",
|
|
62
|
+
17: "touchpad", # touchpad button click for ps4.
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
_standard_axis_map = {
|
|
66
|
+
0: "leftx",
|
|
67
|
+
1: "lefty",
|
|
68
|
+
2: "rightx",
|
|
69
|
+
3: "righty",
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# It's something, but not sure how to type it.
|
|
73
|
+
JSPromise = None
|
|
74
|
+
|
|
75
|
+
class _GamepadActuator:
|
|
76
|
+
"""Typing for Javascript Gamepad Actuator object."""
|
|
77
|
+
effects: list[str]
|
|
78
|
+
|
|
79
|
+
def playEffect(self,
|
|
80
|
+
effect_type: Literal["dual-rumble", "trigger-rumble"],
|
|
81
|
+
duration: int = 0,
|
|
82
|
+
startDelay: int = 0,
|
|
83
|
+
strongMagnitude: float = 0,
|
|
84
|
+
weakMagnitude: float = 0,
|
|
85
|
+
leftTrigger: float = 0,
|
|
86
|
+
rightTrigger: float = 0) -> JSPromise:
|
|
87
|
+
"""Play a specific vibration effect."""
|
|
88
|
+
|
|
89
|
+
def pulse(self, value: float, duration: int) -> JSPromise:
|
|
90
|
+
"""Pulse at a certain intensity for a specified duration."""
|
|
91
|
+
|
|
92
|
+
def reset(self) -> None:
|
|
93
|
+
"""Stop all effects."""
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class _GamepadButton:
|
|
97
|
+
"""Typing for Javascript GamepadButton object."""
|
|
98
|
+
pressed: bool
|
|
99
|
+
touched: bool # Some browsers support this, otherwise it may always be False
|
|
100
|
+
value: float
|
|
101
|
+
|
|
102
|
+
class _Gamepad:
|
|
103
|
+
"""Typing for Javascript Gamepad object."""
|
|
104
|
+
id: str
|
|
105
|
+
index: int
|
|
106
|
+
connected: bool
|
|
107
|
+
timestamp: float
|
|
108
|
+
mapping: str # Typically "standard" or ""
|
|
109
|
+
axes: list[float]
|
|
110
|
+
buttons: list[_GamepadButton]
|
|
111
|
+
vibrationActuator: _GamepadActuator | None
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class JavascriptGamepad(Device):
|
|
115
|
+
"""Small wrapper over the Gamepad device.
|
|
116
|
+
|
|
117
|
+
Do not rely on the gamepad object, as it gets replaced every frame in an update.
|
|
118
|
+
"""
|
|
119
|
+
controls: dict[str, Button | AbsoluteAxis]
|
|
120
|
+
|
|
121
|
+
def __init__(self, gamepad: _Gamepad) -> None:
|
|
122
|
+
super().__init__(None, f"{gamepad.id}")
|
|
123
|
+
self.index = gamepad.index
|
|
124
|
+
self.connected = gamepad.connected
|
|
125
|
+
self.gamepad = gamepad # Will get replaced
|
|
126
|
+
|
|
127
|
+
self.vendor_id = 0
|
|
128
|
+
self.product_id = 0
|
|
129
|
+
self.device_name = gamepad.id
|
|
130
|
+
self.mapping_name = "standard"
|
|
131
|
+
|
|
132
|
+
# Rumble support. Only on Chrome and Edge.
|
|
133
|
+
if hasattr(gamepad, "vibrationActuator") and gamepad.vibrationActuator is not None:
|
|
134
|
+
if "dual-rumble" in gamepad.vibrationActuator.effects: # weak/strong motors.
|
|
135
|
+
self.vibration = True
|
|
136
|
+
console.log(f"Rumble supported for gamepad: {gamepad.id}")
|
|
137
|
+
else:
|
|
138
|
+
self.vibration = False
|
|
139
|
+
console.log("Trigger rumble support found, but not implemented.")
|
|
140
|
+
else:
|
|
141
|
+
self.vibration = False
|
|
142
|
+
console.log("Rumble not supported.")
|
|
143
|
+
|
|
144
|
+
self.vibration = False
|
|
145
|
+
|
|
146
|
+
# Some controllers report the Vendor and Product ID:
|
|
147
|
+
match = re.search(r'^(.*?)(?: \((.*?)(?: Vendor: ([0-9a-fA-F]{4}) Product: ([0-9a-fA-F]{4}))?\))?$', gamepad.id)
|
|
148
|
+
if match:
|
|
149
|
+
self.device_name, mapping_name, vendor_id, product_id = match.groups()
|
|
150
|
+
|
|
151
|
+
# If a vendor ID and product ID are found.
|
|
152
|
+
if vendor_id and product_id:
|
|
153
|
+
# Convert IDs to little-endian
|
|
154
|
+
vendor_le = vendor_id[2:4] + vendor_id[0:2]
|
|
155
|
+
product_le = product_id[2:4] + product_id[0:2]
|
|
156
|
+
self.guid = f"03000000{vendor_le}0000{product_le}000000000000"
|
|
157
|
+
else:
|
|
158
|
+
warnings.warn("Vendor/Product IDs not found in GamePad ID.")
|
|
159
|
+
self.guid = "STANDARD GAMEPAD"
|
|
160
|
+
|
|
161
|
+
self.mapping_name = mapping_name if mapping_name else "standard"
|
|
162
|
+
else:
|
|
163
|
+
warnings.warn(f"Gamepad information could not be parsed from: {gamepad.id}")
|
|
164
|
+
|
|
165
|
+
# See layout here: https://w3c.github.io/gamepad/#remapping
|
|
166
|
+
self.controls = {
|
|
167
|
+
'a': Button('a'), # the “south” face button # 0
|
|
168
|
+
'b': Button('b'), # the “east” face button # 1
|
|
169
|
+
'x': Button('x'), # the “west” face button # 2
|
|
170
|
+
'y': Button('y'), # the “north” face button # 3
|
|
171
|
+
'back': Button('back'), # 8
|
|
172
|
+
'start': Button('start'), # 9
|
|
173
|
+
'guide': Button('guide'), # 16
|
|
174
|
+
'leftshoulder': Button('leftshoulder'), # 4
|
|
175
|
+
'rightshoulder': Button('rightshoulder'), # 5
|
|
176
|
+
'lefttrigger': TriggerButton('lefttrigger', 0.0, 1.0), # 6
|
|
177
|
+
'righttrigger': TriggerButton('righttrigger', 0.0, 1.0), # 7
|
|
178
|
+
'leftstick': Button('leftstick'), # 10
|
|
179
|
+
'rightstick': Button('rightstick'), # 11
|
|
180
|
+
'dpup': Button('dpup'), # 12
|
|
181
|
+
'dpdown': Button('dpdown'), # 13
|
|
182
|
+
'dpleft': Button('dpleft'), # 14
|
|
183
|
+
'dpright': Button('dpright'), # 15
|
|
184
|
+
|
|
185
|
+
'leftx': AbsoluteAxis('leftx', -1, 1), # axis horizontal # 0
|
|
186
|
+
'lefty': AbsoluteAxis('lefty', -1, 1), # axis vertical # 1
|
|
187
|
+
'rightx': AbsoluteAxis('rightx', -1, 1), # axis horizontal 2
|
|
188
|
+
'righty': AbsoluteAxis('righty', -1, 1), # axis vertical 3
|
|
189
|
+
|
|
190
|
+
'touchpad': Button('touchpad'), # 17 on PS4 controller.
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
def get_controls(self) -> list[Control]:
|
|
194
|
+
return list(self.controls.values())
|
|
195
|
+
|
|
196
|
+
def get_guid(self) -> str:
|
|
197
|
+
return self.guid
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class GamepadController(Controller):
|
|
201
|
+
"""Javascript Gamepad Controller object that handles buttons and controls."""
|
|
202
|
+
device: JavascriptGamepad
|
|
203
|
+
def __init__(self, device: JavascriptGamepad, mapping: dict) -> None:
|
|
204
|
+
super().__init__(device, mapping)
|
|
205
|
+
self._last_updated = 0.0
|
|
206
|
+
|
|
207
|
+
@property
|
|
208
|
+
def gamepad_device(self) -> _Gamepad:
|
|
209
|
+
return self.device.gamepad
|
|
210
|
+
|
|
211
|
+
def _initialize_controls(self) -> None:
|
|
212
|
+
for button_name in _standard_button_map.values():
|
|
213
|
+
control = self.device.controls[button_name]
|
|
214
|
+
self._button_controls.append(control)
|
|
215
|
+
self._add_button(control, button_name)
|
|
216
|
+
|
|
217
|
+
for axis_name in _standard_axis_map.values():
|
|
218
|
+
control = self.device.controls[axis_name]
|
|
219
|
+
self._axis_controls.append(control)
|
|
220
|
+
self._add_axis(control, axis_name)
|
|
221
|
+
|
|
222
|
+
def update(self, gamepad: _Gamepad) -> None:
|
|
223
|
+
# Disconnect event should catch any disconnects.
|
|
224
|
+
# Save some CPU if event fails by avoiding trying to update the states.
|
|
225
|
+
if not gamepad.connected:
|
|
226
|
+
self.device.connected = False
|
|
227
|
+
return
|
|
228
|
+
|
|
229
|
+
# Avoid going through all buttons unless the timestamp has changed on the gamepad data.
|
|
230
|
+
if gamepad.timestamp > self._last_updated:
|
|
231
|
+
self.device.connected = True
|
|
232
|
+
|
|
233
|
+
for button_id, button in enumerate(gamepad.buttons):
|
|
234
|
+
name = _standard_button_map[button_id]
|
|
235
|
+
self.device.controls[name].value = button.pressed
|
|
236
|
+
|
|
237
|
+
for axes_id, value in enumerate(gamepad.axes):
|
|
238
|
+
name = _standard_axis_map[axes_id]
|
|
239
|
+
self.device.controls[name].value = value
|
|
240
|
+
|
|
241
|
+
self._last_updated = gamepad.timestamp
|
|
242
|
+
|
|
243
|
+
def _add_axis(self, control: Control, name: str) -> None:
|
|
244
|
+
if name == "lefttrigger":
|
|
245
|
+
@control.event
|
|
246
|
+
def on_change(value):
|
|
247
|
+
setattr(self, name, value)
|
|
248
|
+
self.dispatch_event('on_lefttrigger_motion', self, value)
|
|
249
|
+
|
|
250
|
+
@control.event
|
|
251
|
+
def on_press():
|
|
252
|
+
self.dispatch_event('on_button_press', self, name)
|
|
253
|
+
|
|
254
|
+
@control.event
|
|
255
|
+
def on_release():
|
|
256
|
+
self.dispatch_event('on_button_release', self, name)
|
|
257
|
+
|
|
258
|
+
if name == "righttrigger":
|
|
259
|
+
@control.event
|
|
260
|
+
def on_change(value):
|
|
261
|
+
setattr(self, name, value)
|
|
262
|
+
self.dispatch_event('on_righttrigger_motion', self, value)
|
|
263
|
+
|
|
264
|
+
@control.event
|
|
265
|
+
def on_press():
|
|
266
|
+
self.dispatch_event('on_button_press', self, name)
|
|
267
|
+
|
|
268
|
+
@control.event
|
|
269
|
+
def on_release():
|
|
270
|
+
self.dispatch_event('on_button_release', self, name)
|
|
271
|
+
|
|
272
|
+
elif name in ("leftx", "lefty"):
|
|
273
|
+
@control.event
|
|
274
|
+
def on_change(value):
|
|
275
|
+
normalized_value = value
|
|
276
|
+
setattr(self, name, normalized_value)
|
|
277
|
+
self.dispatch_event('on_leftstick_motion', self, Vec2(self.leftx, -self.lefty))
|
|
278
|
+
|
|
279
|
+
elif name in ("rightx", "righty"):
|
|
280
|
+
@control.event
|
|
281
|
+
def on_change(value):
|
|
282
|
+
normalized_value = value
|
|
283
|
+
setattr(self, name, normalized_value)
|
|
284
|
+
self.dispatch_event('on_rightstick_motion', self, Vec2(self.rightx, -self.righty))
|
|
285
|
+
|
|
286
|
+
def _add_button(self, control: Control, name: str) -> None:
|
|
287
|
+
|
|
288
|
+
if name in ("dpleft", "dpright", "dpup", "dpdown"):
|
|
289
|
+
@control.event
|
|
290
|
+
def on_change(value):
|
|
291
|
+
target, bias = {
|
|
292
|
+
'dpleft': ('dpadx', -1.0), 'dpright': ('dpadx', 1.0),
|
|
293
|
+
'dpdown': ('dpady', -1.0), 'dpup': ('dpady', 1.0)
|
|
294
|
+
}[name]
|
|
295
|
+
setattr(self, target, bias * value)
|
|
296
|
+
self.dispatch_event('on_dpad_motion', self, Vec2(self.dpadx, self.dpady))
|
|
297
|
+
else:
|
|
298
|
+
@control.event
|
|
299
|
+
def on_change(value):
|
|
300
|
+
setattr(self, name, value)
|
|
301
|
+
|
|
302
|
+
@control.event
|
|
303
|
+
def on_press():
|
|
304
|
+
self.dispatch_event('on_button_press', self, name)
|
|
305
|
+
|
|
306
|
+
@control.event
|
|
307
|
+
def on_release():
|
|
308
|
+
self.dispatch_event('on_button_release', self, name)
|
|
309
|
+
|
|
310
|
+
def rumble_play_weak(self, strength: float=1.0, duration: float=0.5) -> None:
|
|
311
|
+
if self.device.vibration is True:
|
|
312
|
+
strength_clamp = int(max(min(1.0, strength), 0))
|
|
313
|
+
self.gamepad_device.vibrationActuator.playEffect(
|
|
314
|
+
"dual-rumble",
|
|
315
|
+
startDelay=0,
|
|
316
|
+
duration=int(duration*1000),
|
|
317
|
+
weakMagnitude=strength_clamp,
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
def rumble_play_strong(self, strength: float=1.0, duration: float=0.5) -> None:
|
|
321
|
+
if self.device.vibration is True:
|
|
322
|
+
strength_clamp = int(max(min(1.0, strength), 0))
|
|
323
|
+
self.gamepad_device.vibrationActuator.playEffect(
|
|
324
|
+
"dual-rumble",
|
|
325
|
+
startDelay=0,
|
|
326
|
+
duration=int(duration*1000),
|
|
327
|
+
strongMagnitude=strength_clamp,
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
def rumble_stop_weak(self) -> None:
|
|
331
|
+
if self.device.vibration is True:
|
|
332
|
+
self.gamepad_device.vibrationActuator.reset()
|
|
333
|
+
|
|
334
|
+
def rumble_stop_strong(self) -> None:
|
|
335
|
+
if self.device.vibration is True:
|
|
336
|
+
self.gamepad_device.vibrationActuator.reset()
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
class JavascriptGamepadManager(ControllerManager):
|
|
340
|
+
"""Interface between Javascript/Pyodide and Pyglet to manage controllers."""
|
|
341
|
+
|
|
342
|
+
_controllers: dict[int, GamepadController]
|
|
343
|
+
|
|
344
|
+
def __init__(self):
|
|
345
|
+
self._controllers = {}
|
|
346
|
+
if hasattr(navigator, "getGamepads"):
|
|
347
|
+
console.log("Gamepad API supported")
|
|
348
|
+
else:
|
|
349
|
+
console.log("Gamepad API not supported.")
|
|
350
|
+
raise ImportError("Gamepad API not supported in this browser.")
|
|
351
|
+
|
|
352
|
+
# Premake the gamepad objects so they can be kept on the user side.
|
|
353
|
+
self._controllers = {}
|
|
354
|
+
self._connected = set()
|
|
355
|
+
|
|
356
|
+
self._proxy_connect = create_proxy(self.on_gamepad_connected)
|
|
357
|
+
self._proxy_disconnect = create_proxy(self.on_gamepad_disconnected)
|
|
358
|
+
|
|
359
|
+
# Add event listeners for gamepad connection and disconnection
|
|
360
|
+
js.window.addEventListener("gamepadconnected", self._proxy_connect)
|
|
361
|
+
js.window.addEventListener("gamepaddisconnected", self._proxy_disconnect)
|
|
362
|
+
|
|
363
|
+
pyglet.clock.schedule_interval_soft(self._query_pads, 1/60.0)
|
|
364
|
+
|
|
365
|
+
def on_gamepad_connected(self, event) -> None:
|
|
366
|
+
console.log(f"Gamepad {event.gamepad.index} connected: {event.gamepad.id}. | Buttons: {len(event.gamepad.buttons)}, | Axis: {len(event.gamepad.axes)}")
|
|
367
|
+
wrapper = JavascriptGamepad(event.gamepad)
|
|
368
|
+
controller = GamepadController(wrapper, {"name": wrapper.device_name, "guid": wrapper.get_guid() })
|
|
369
|
+
self._controllers[event.gamepad.index] = controller
|
|
370
|
+
self.dispatch_event('on_connect', controller)
|
|
371
|
+
|
|
372
|
+
def on_gamepad_disconnected(self, event) -> None:
|
|
373
|
+
console.log(f"Gamepad disconnected: {event.gamepad.id}")
|
|
374
|
+
if event.gamepad.index in self._controllers:
|
|
375
|
+
del self._controllers[event.gamepad.index]
|
|
376
|
+
|
|
377
|
+
def _query_pads(self, _dt: float) -> None:
|
|
378
|
+
gamepads = navigator.getGamepads()
|
|
379
|
+
|
|
380
|
+
for i in range(gamepads.length):
|
|
381
|
+
gamepad = gamepads[i]
|
|
382
|
+
if gamepad is not None:
|
|
383
|
+
self._controllers[i].update(gamepad)
|
|
384
|
+
|
|
385
|
+
@property
|
|
386
|
+
def maximum_controllers(self) -> int:
|
|
387
|
+
gamepads = navigator.getGamepads()
|
|
388
|
+
return gamepads.length
|
|
389
|
+
|
|
390
|
+
def get_devices(self):
|
|
391
|
+
return []
|
|
392
|
+
|
|
393
|
+
def get_controllers(self):
|
|
394
|
+
return list(self._controllers.values())
|
|
395
|
+
|
|
396
|
+
def get_controllers(display=None):
|
|
397
|
+
return []
|
pyglet/input/linux/__init__.py
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
import warnings
|
|
2
|
+
|
|
3
|
+
import pyglet
|
|
4
|
+
|
|
5
|
+
from .evdev import get_devices
|
|
2
6
|
from .evdev import get_joysticks
|
|
3
7
|
from .evdev import get_controllers
|
|
4
8
|
from .evdev import EvdevControllerManager as ControllerManager
|
|
5
|
-
from .x11_xinput_tablet import get_tablets
|
|
6
|
-
from .x11_xinput import get_devices as x11xinput_get_devices
|
|
7
9
|
|
|
10
|
+
if not (pyglet.options.headless or pyglet.options.wayland):
|
|
11
|
+
from .x11_xinput_tablet import get_tablets
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
else:
|
|
14
|
+
def get_tablets():
|
|
15
|
+
warnings.warn('Tablets are currently only supported under Xlib.')
|
|
16
|
+
return []
|
pyglet/input/linux/evdev.py
CHANGED
|
@@ -17,8 +17,8 @@ from ctypes import c_byte as _c_byte
|
|
|
17
17
|
import pyglet
|
|
18
18
|
|
|
19
19
|
from .evdev_constants import *
|
|
20
|
-
from pyglet.app.
|
|
21
|
-
from pyglet.libs.ioctl import _IOR, _IOR_str, _IOR_len, _IOW
|
|
20
|
+
from pyglet.app.linux import LinuxSelectDevice
|
|
21
|
+
from pyglet.libs.linux.ioctl import _IOR, _IOR_str, _IOR_len, _IOW
|
|
22
22
|
from pyglet.input.base import Device, RelativeAxis, AbsoluteAxis, Button, Joystick, Controller
|
|
23
23
|
from pyglet.input.base import DeviceOpenException, ControllerManager
|
|
24
24
|
from pyglet.input.controller import get_mapping, Relation, create_guid
|
|
@@ -273,7 +273,7 @@ event_types = {
|
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
|
|
276
|
-
class EvdevDevice(
|
|
276
|
+
class EvdevDevice(LinuxSelectDevice, Device):
|
|
277
277
|
_fileno: int | None
|
|
278
278
|
_poll: "select.poll | None"
|
|
279
279
|
|
|
@@ -393,7 +393,7 @@ class EvdevDevice(XlibSelectDevice, Device):
|
|
|
393
393
|
def ff_upload_effect(self, structure):
|
|
394
394
|
os.write(self._fileno, structure)
|
|
395
395
|
|
|
396
|
-
#
|
|
396
|
+
# LinuxSelectDevice interface
|
|
397
397
|
|
|
398
398
|
def fileno(self):
|
|
399
399
|
return self._fileno
|
|
@@ -498,7 +498,7 @@ class FFController(Controller):
|
|
|
498
498
|
self.device.ff_upload_effect(self._stop_strong_event)
|
|
499
499
|
|
|
500
500
|
|
|
501
|
-
class EvdevControllerManager(ControllerManager,
|
|
501
|
+
class EvdevControllerManager(ControllerManager, LinuxSelectDevice):
|
|
502
502
|
|
|
503
503
|
def __init__(self, display=None):
|
|
504
504
|
super().__init__()
|
|
@@ -631,16 +631,16 @@ def _detect_controller_mapping(device):
|
|
|
631
631
|
|
|
632
632
|
for i, control in enumerate(button_controls):
|
|
633
633
|
if name := _aliases.get(control.event_code):
|
|
634
|
-
mapping[name] = Relation('button', i)
|
|
634
|
+
mapping[name] = Relation('button', index=i)
|
|
635
635
|
|
|
636
636
|
for i, control in enumerate(axis_controls):
|
|
637
637
|
if name := _aliases.get(control.event_code):
|
|
638
|
-
mapping[name] = Relation('axis', i)
|
|
638
|
+
mapping[name] = Relation('axis', index=i)
|
|
639
639
|
|
|
640
640
|
for i, control in enumerate(hat_controls):
|
|
641
641
|
if name := _aliases.get(control.event_code):
|
|
642
642
|
index = 1 + i << 1
|
|
643
|
-
mapping[name] = Relation('hat0', index)
|
|
643
|
+
mapping[name] = Relation('hat0', index=index)
|
|
644
644
|
|
|
645
645
|
return mapping
|
|
646
646
|
|
|
@@ -654,9 +654,8 @@ def _create_controller(device) -> Controller | None:
|
|
|
654
654
|
|
|
655
655
|
mapping = get_mapping(device.get_guid())
|
|
656
656
|
if not mapping:
|
|
657
|
-
warnings.warn(f"
|
|
658
|
-
f"
|
|
659
|
-
f"Auto-detecting as defined by the 'Linux gamepad specification'")
|
|
657
|
+
warnings.warn(f"\n{device} (GUID: {device.get_guid()}) has no controller mapping.\n"
|
|
658
|
+
f"Auto-detecting as defined by the 'Linux gamepad specification'.")
|
|
660
659
|
mapping = _detect_controller_mapping(device)
|
|
661
660
|
|
|
662
661
|
if FF_RUMBLE in device.ff_types:
|
pyglet/input/linux/x11_xinput.py
CHANGED
|
@@ -2,11 +2,11 @@ import ctypes
|
|
|
2
2
|
import pyglet
|
|
3
3
|
from pyglet.input.base import Device, DeviceOpenException
|
|
4
4
|
from pyglet.input.base import Button, RelativeAxis, AbsoluteAxis
|
|
5
|
-
from pyglet.libs.x11 import xlib
|
|
5
|
+
from pyglet.libs.linux.x11 import xlib
|
|
6
6
|
from pyglet.util import asstr
|
|
7
7
|
|
|
8
8
|
try:
|
|
9
|
-
from pyglet.libs.x11 import xinput as xi
|
|
9
|
+
from pyglet.libs.linux.x11 import xinput as xi
|
|
10
10
|
|
|
11
11
|
_have_xinput = True
|
|
12
12
|
except:
|
pyglet/input/macos/__init__.py
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
from .darwin_hid import get_devices
|
|
2
2
|
from .darwin_hid import get_joysticks
|
|
3
3
|
from .darwin_hid import get_apple_remote
|
|
4
|
-
from .darwin_hid import
|
|
5
|
-
from .
|
|
4
|
+
#from .darwin_hid import DarwinControllerManager as ControllerManager
|
|
5
|
+
from .darwin_gc import AppleControllerManager as ControllerManager
|
|
6
6
|
|
|
7
|
+
def get_controllers(display=None):
|
|
8
|
+
from .darwin_gc import get_controllers as get_apple_controllers
|
|
9
|
+
from .darwin_hid import get_controllers as get_darwin_controllers
|
|
10
|
+
|
|
11
|
+
return [*get_apple_controllers(), *get_darwin_controllers()]
|
|
7
12
|
|
|
8
13
|
def get_tablets(display=None):
|
|
9
14
|
import warnings
|