update pyglet

This commit is contained in:
shenjack 2022-08-16 13:25:37 +08:00
parent c79a0dbbb4
commit 76d591f2d9
13 changed files with 1003 additions and 731 deletions

View File

@ -44,11 +44,14 @@ import sys
from typing import TYPE_CHECKING
#: The release version
version = '2.0.dev20'
version = '2.0.dev22'
__version__ = version
if sys.version_info < (3, 6):
raise Exception('pyglet %s requires Python 3.6 or newer.' % version)
MIN_PYTHON_VERSION = 3, 7
MIN_PYTHON_VERSION_STR = '.'.join([str(v) for v in MIN_PYTHON_VERSION])
if sys.version_info < MIN_PYTHON_VERSION:
raise Exception(f"pyglet {version} requires Python {MIN_PYTHON_VERSION_STR} or newer.")
if 'sphinx' in sys.modules:
setattr(sys, 'is_pyglet_doc_run', True)

View File

@ -65,11 +65,13 @@ class Win32Screen(Screen):
self._handle = handle
def get_matching_configs(self, template):
canvas = Win32Canvas(self.display, 0, _user32.GetDC(0))
hdc = _user32.GetDC(0)
canvas = Win32Canvas(self.display, 0, hdc)
configs = template.match(canvas)
# XXX deprecate config's being screen-specific
for config in configs:
config.screen = self
_user32.ReleaseDC(0, hdc)
return configs
def get_device_name(self):

View File

@ -35,6 +35,7 @@ except OSError as err:
_debug_font = debug_print('debug_font')
def DWRITE_MAKE_OPENTYPE_TAG(a, b, c, d):
return ord(d) << 24 | ord(c) << 16 | ord(b) << 8 | ord(a)
@ -171,8 +172,6 @@ class D2D1_COLOR_F(Structure):
)
class DWRITE_TEXT_METRICS(ctypes.Structure):
_fields_ = (
('left', FLOAT),
@ -316,6 +315,7 @@ class DWRITE_GLYPH_RUN(ctypes.Structure):
('bidiLevel', UINT32),
)
DWRITE_SCRIPT_SHAPES = UINT
DWRITE_SCRIPT_SHAPES_DEFAULT = 0
@ -1061,6 +1061,21 @@ class LegacyCollectionLoader(com.COMObject):
IID_IDWriteFactory = com.GUID(0xb859ee5a, 0xd838, 0x4b5b, 0xa2, 0xe8, 0x1a, 0xdc, 0x7d, 0x93, 0xdb, 0x48)
class IDWriteRenderingParams(com.pIUnknown):
_methods_ = [
('GetGamma',
com.METHOD(FLOAT)),
('GetEnhancedContrast',
com.METHOD(FLOAT)),
('GetClearTypeLevel',
com.METHOD(FLOAT)),
('GetPixelGeometry',
com.METHOD(UINT)),
('GetRenderingMode',
com.METHOD(UINT)),
]
class IDWriteFactory(com.pIUnknown):
_methods_ = [
('GetSystemFontCollection',
@ -1078,11 +1093,11 @@ class IDWriteFactory(com.pIUnknown):
('CreateFontFace',
com.STDMETHOD()),
('CreateRenderingParams',
com.STDMETHOD()),
com.STDMETHOD(POINTER(IDWriteRenderingParams))),
('CreateMonitorRenderingParams',
com.STDMETHOD()),
('CreateCustomRenderingParams',
com.STDMETHOD()),
com.STDMETHOD(FLOAT, FLOAT, FLOAT, UINT, UINT, POINTER(IDWriteRenderingParams))),
('RegisterFontFileLoader',
com.STDMETHOD(c_void_p)), # Ambigious as newer is a pIUnknown and legacy is IUnknown.
('UnregisterFontFileLoader',
@ -1116,7 +1131,7 @@ class IDWriteFactory1(IDWriteFactory, com.pIUnknown):
_methods_ = [
('GetEudcFontCollection',
com.STDMETHOD()),
('CreateCustomRenderingParams',
('CreateCustomRenderingParams1',
com.STDMETHOD()),
]
@ -1139,7 +1154,7 @@ class IDWriteFactory2(IDWriteFactory1, com.pIUnknown):
com.STDMETHOD()),
('TranslateColorGlyphRun',
com.STDMETHOD()),
('CreateCustomRenderingParams',
('CreateCustomRenderingParams2',
com.STDMETHOD()),
('CreateGlyphRunAnalysis',
com.STDMETHOD()),
@ -1191,7 +1206,7 @@ class IDWriteFactory3(IDWriteFactory2, com.pIUnknown):
_methods_ = [
('CreateGlyphRunAnalysis',
com.STDMETHOD()),
('CreateCustomRenderingParams',
('CreateCustomRenderingParams3',
com.STDMETHOD()),
('CreateFontFaceReference',
com.STDMETHOD()),
@ -1207,7 +1222,7 @@ class IDWriteFactory3(IDWriteFactory2, com.pIUnknown):
com.STDMETHOD()),
('GetFontDownloadQueue',
com.STDMETHOD()),
#('GetSystemFontSet',
# ('GetSystemFontSet',
# com.STDMETHOD()),
]
@ -1220,10 +1235,12 @@ class IDWriteColorGlyphRunEnumerator1(com.pIUnknown):
com.STDMETHOD()),
]
class IDWriteFactory4(IDWriteFactory3, com.pIUnknown):
_methods_ = [
('TranslateColorGlyphRun4', # Renamed to prevent clash from previous factories.
com.STDMETHOD(D2D_POINT_2F, DWRITE_GLYPH_RUN, c_void_p, DWRITE_GLYPH_IMAGE_FORMATS, DWRITE_MEASURING_MODE, c_void_p, UINT32, POINTER(IDWriteColorGlyphRunEnumerator1))),
com.STDMETHOD(D2D_POINT_2F, DWRITE_GLYPH_RUN, c_void_p, DWRITE_GLYPH_IMAGE_FORMATS, DWRITE_MEASURING_MODE,
c_void_p, UINT32, POINTER(IDWriteColorGlyphRunEnumerator1))),
('ComputeGlyphOrigins_',
com.STDMETHOD()),
('ComputeGlyphOrigins',
@ -1259,7 +1276,6 @@ class IDWriteFactory5(IDWriteFactory4, IDWriteFactory3, IDWriteFactory2, IDWrite
]
DWriteCreateFactory = dwrite_lib.DWriteCreateFactory
DWriteCreateFactory.restype = HRESULT
DWriteCreateFactory.argtypes = [DWRITE_FACTORY_TYPE, com.REFIID, POINTER(com.pIUnknown)]
@ -1424,7 +1440,7 @@ class ID2D1RenderTarget(ID2D1Resource, com.pIUnknown):
('GetTransform',
com.STDMETHOD()),
('SetAntialiasMode',
com.STDMETHOD()),
com.METHOD(c_void, D2D1_TEXT_ANTIALIAS_MODE)),
('GetAntialiasMode',
com.STDMETHOD()),
('SetTextAntialiasMode',
@ -1432,7 +1448,7 @@ class ID2D1RenderTarget(ID2D1Resource, com.pIUnknown):
('GetTextAntialiasMode',
com.STDMETHOD()),
('SetTextRenderingParams',
com.STDMETHOD()),
com.STDMETHOD(IDWriteRenderingParams)),
('GetTextRenderingParams',
com.STDMETHOD()),
('SetTags',
@ -1444,7 +1460,7 @@ class ID2D1RenderTarget(ID2D1Resource, com.pIUnknown):
('PopLayer',
com.STDMETHOD()),
('Flush',
com.STDMETHOD()),
com.STDMETHOD(c_void_p, c_void_p)),
('SaveDrawingState',
com.STDMETHOD()),
('RestoreDrawingState',
@ -1535,6 +1551,7 @@ if not wic_decoder:
class DirectWriteGlyphRenderer(base.GlyphRenderer):
antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT
draw_options = D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT
measuring_mode = DWRITE_MEASURING_MODE_NATURAL
def __init__(self, font):
self._render_target = None
@ -1679,7 +1696,6 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer):
font_face.GetDesignGlyphMetrics(indices, count, glyph_metrics, False)
metrics_out = []
i = 0
for metric in glyph_metrics:
glyph_width = (metric.advanceWidth - metric.leftSideBearing - metric.rightSideBearing)
@ -1696,7 +1712,6 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer):
advance_width = metric.advanceWidth
metrics_out.append((glyph_width, glyph_height, lsb, advance_width, bsb))
i += 1
return metrics_out
@ -1721,7 +1736,7 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer):
run,
None,
DWRITE_GLYPH_IMAGE_FORMATS_ALL,
DWRITE_MEASURING_MODE_NATURAL,
self.measuring_mode,
None,
0,
enumerator)
@ -1735,7 +1750,7 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer):
def render_single_glyph(self, font_face, indice, advance, offset, metrics):
"""Renders a single glyph using D2D DrawGlyphRun"""
glyph_width, glyph_height, lsb, font_advance, bsb = metrics # We use a shaped advance instead of the fonts.
glyph_width, glyph_height, glyph_lsb, glyph_advance, glyph_bsb = metrics # We use a shaped advance instead of the fonts.
# Slicing an array turns it into a python object. Maybe a better way to keep it a ctypes value?
new_indice = (UINT16 * 1)(indice)
@ -1748,19 +1763,24 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer):
new_advance, # advance,
pointer(offset), # offset,
False,
False
0
)
# If it's colored, return to render it using layout.
if self.draw_options & D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT and self.is_color_run(run):
return None
render_width = int(math.ceil((glyph_width) * self.font.font_scale_ratio))
render_offset_x = int(math.floor(abs(lsb * self.font.font_scale_ratio)))
if lsb < 0:
# Negative LSB: we shift the layout rect to the right
# Otherwise we will cut the left part of the glyph
render_offset_x = -(render_offset_x)
# Use the glyph's advance as a width as bitmap width.
# Some characters such as diacritics (̃) may have 0 advance width. In that case, just use glyph_width
if glyph_advance:
render_width = int(math.ceil(glyph_advance * self.font.font_scale_ratio))
else:
render_width = int(math.ceil(glyph_width * self.font.font_scale_ratio))
render_offset_x = 0
if glyph_lsb < 0:
# Negative LSB: we shift the offset, otherwise the glyph will be cut off.
render_offset_x = glyph_lsb * self.font.font_scale_ratio
# Create new bitmap.
# TODO: We can probably adjust bitmap/baseline to reduce the whitespace and save a lot of texture space.
@ -1769,11 +1789,9 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer):
int(math.ceil(self.font.max_glyph_height)))
# Glyphs are drawn at the baseline, and with LSB, so we need to offset it based on top left position.
# Offsets are actually based on pixels somehow???
baseline_offset = D2D_POINT_2F(-render_offset_x - offset.advanceOffset,
self.font.ascent + offset.ascenderOffset)
self._render_target.BeginDraw()
self._render_target.Clear(transparent)
@ -1781,17 +1799,17 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer):
self._render_target.DrawGlyphRun(baseline_offset,
run,
self._brush,
DWRITE_MEASURING_MODE_NATURAL)
self.measuring_mode)
self._render_target.EndDraw(None, None)
image = wic_decoder.get_image(self._bitmap)
glyph = self.font.create_glyph(image)
glyph.set_bearings(self.font.descent, render_offset_x,
advance * self.font.font_scale_ratio,
offset.advanceOffset * self.font.font_scale_ratio,
offset.ascenderOffset * self.font.font_scale_ratio)
glyph.set_bearings(-self.font.descent, render_offset_x,
advance,
offset.advanceOffset,
offset.ascenderOffset)
return glyph
@ -1829,7 +1847,17 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer):
image = wic_decoder.get_image(self._bitmap)
glyph = self.font.create_glyph(image)
glyph.set_bearings(self.font.descent, 0, int(math.ceil(layout_metrics.width)))
glyph.set_bearings(-self.font.descent, 0, int(math.ceil(layout_metrics.width)))
return glyph
def create_zero_glyph(self):
"""Zero glyph is a 1x1 image that has a -1 advance. This is to fill in for ligature substitutions since
font system requires 1 glyph per character in a string."""
self._create_bitmap(1, 1)
image = wic_decoder.get_image(self._bitmap)
glyph = self.font.create_glyph(image)
glyph.set_bearings(-self.font.descent, 0, -1)
return glyph
def _create_bitmap(self, width, height):
@ -1878,6 +1906,7 @@ class Win32DirectWriteFont(base.Font):
_glyph_renderer = None
_empty_glyph = None
_zero_glyph = None
glyph_renderer_class = DirectWriteGlyphRenderer
texture_internalformat = pyglet.gl.GL_RGBA
@ -1973,10 +2002,10 @@ class Win32DirectWriteFont(base.Font):
self.font_scale_ratio = (self._real_size / self._font_metrics.designUnitsPerEm)
self.ascent = self._font_metrics.ascent * self.font_scale_ratio
self.descent = self._font_metrics.descent * self.font_scale_ratio
self.ascent = math.ceil(self._font_metrics.ascent * self.font_scale_ratio)
self.descent = -round(self._font_metrics.descent * self.font_scale_ratio)
self.max_glyph_height = (self._font_metrics.ascent + self._font_metrics.descent) * self.font_scale_ratio
self.line_gap = self._font_metrics.lineGap * self.font_scale_ratio
self._fallback = None
@ -1986,7 +2015,6 @@ class Win32DirectWriteFont(base.Font):
else:
assert _debug_font("Windows 8.1+ is required for font fallback. Colored glyphs cannot be omitted.")
@property
def name(self):
return self._name
@ -2015,21 +2043,19 @@ class Win32DirectWriteFont(base.Font):
new_glyph.set_bearings(
glyph.baseline,
glyph.lsb,
advance * self.font_scale_ratio,
offset.advanceOffset * self.font_scale_ratio,
offset.ascenderOffset * self.font_scale_ratio
advance,
offset.advanceOffset,
offset.ascenderOffset
)
return new_glyph
def _render_layout_glyph(self, text_buffer, i, clusters, check_color=True):
formatted_clusters = clusters[:]
# Some glyphs can be more than 1 char. We use the clusters to determine how many of an index exist.
text_length = formatted_clusters.count(i)
text_length = clusters.count(i)
# Amount of glyphs don't always match 1:1 with text as some can be substituted or omitted. Get
# actual text buffer index.
text_index = formatted_clusters.index(i)
text_index = clusters.index(i)
# Get actual text based on the index and length.
actual_text = text_buffer[text_index:text_index + text_length]
@ -2125,25 +2151,48 @@ class Win32DirectWriteFont(base.Font):
if not self._glyph_renderer:
self._glyph_renderer = self.glyph_renderer_class(self)
self._empty_glyph = self._glyph_renderer.render_using_layout(" ")
self._zero_glyph = self._glyph_renderer.create_zero_glyph()
text_buffer, actual_count, indices, advances, offsets, clusters = self._glyph_renderer.get_string_info(text, self.font_face)
text_buffer, actual_count, indices, advances, offsets, clusters = self._glyph_renderer.get_string_info(text,
self.font_face)
metrics = self._glyph_renderer.get_glyph_metrics(self.font_face, indices, actual_count)
formatted_clusters = list(clusters)
# Convert to real sizes.
for i in range(actual_count):
advances[i] *= self.font_scale_ratio
for i in range(actual_count):
offsets[i].advanceOffset *= self.font_scale_ratio
offsets[i].ascenderOffset *= self.font_scale_ratio
glyphs = []
# Pyglet expects 1 glyph for every string. However, ligatures can combine 1 or more glyphs, leading
# to issues with multilines producing wrong output.
substitutions = {}
for idx in clusters:
ct = formatted_clusters.count(idx)
if ct > 1:
substitutions[idx] = ct-1
for i in range(actual_count):
indice = indices[i]
if indice == 0:
# If an indice is 0, it will return no glyph. In this case we attempt to render leveraging
# the built in text layout from MS. Which depending on version can use fallback fonts and other tricks
# to possibly get something of use.
glyph = self._render_layout_glyph(text_buffer, i, clusters)
glyph = self._render_layout_glyph(text_buffer, i, formatted_clusters)
glyphs.append(glyph)
else:
advance_key = (indice, advances[i], offsets[i].advanceOffset, offsets[i].ascenderOffset)
# Glyphs can vary depending on shaping. We will cache it by indice, advance, and offset.
# Possible to just cache without offset and set them each time. This may be faster?
if indice in self.glyphs:
advance_key = (indice, advances[i], offsets[i].advanceOffset, offsets[i].ascenderOffset)
if advance_key in self._advance_cache:
glyph = self._advance_cache[advance_key]
else:
@ -2153,14 +2202,18 @@ class Win32DirectWriteFont(base.Font):
glyph = self._glyph_renderer.render_single_glyph(self.font_face, indice, advances[i], offsets[i],
metrics[i])
if glyph is None: # Will only return None if a color glyph is found. Use DW to render it directly.
glyph = self._render_layout_glyph(text_buffer, i, clusters, check_color=False)
glyph = self._render_layout_glyph(text_buffer, i, formatted_clusters, check_color=False)
glyph.colored = True
self.glyphs[indice] = glyph
self._advance_cache[(indice, advances[i], offsets[i].advanceOffset, offsets[i].ascenderOffset)] = glyph
self._advance_cache[advance_key] = glyph
glyphs.append(glyph)
if i in substitutions:
for _ in range(substitutions[i]):
glyphs.append(self._zero_glyph)
return glyphs
def create_text_layout(self, text):

View File

@ -120,7 +120,18 @@ class FreeTypeGlyphRenderer(base.GlyphRenderer):
'A',
self._data,
abs(self._pitch))
# HACK: Get text working in GLES until image data can be converted properly
# GLES don't support coversion during pixel transfer so we have to
# force specify the glyph format to be GL_ALPHA. This format is not
# supported in 3.3+ core, but are present in ES because of pixel transfer
# limitations.
if pyglet.gl.current_context.get_info().get_opengl_api() == "gles":
GL_ALPHA = 0x1906
glyph = self.font.create_glyph(img, fmt=GL_ALPHA)
else:
glyph = self.font.create_glyph(img)
glyph.set_bearings(self._baseline, self._lsb, self._advance_x)
if self._pitch > 0:
t = list(glyph.tex_coords)

View File

@ -824,14 +824,14 @@ class Controller(EventDispatcher):
# Input Event types:
def on_stick_motion(self, controller, axis, xvalue, yvalue):
def on_stick_motion(self, controller, stick, xvalue, yvalue):
"""The value of a controller analogue stick changed.
:Parameters:
`controller` : `Controller`
The controller whose analogue stick changed.
`axis` : string
The name of the axis that changed.
`stick` : string
The name of the stick that changed.
`xvalue` : float
The current x axis value, normalized to [-1, 1].
`yvalue` : float

View File

@ -55,12 +55,13 @@ To query which GameControllers are available, call :py:func:`get_controllers`.
.. versionadded:: 2.0
"""
import os
import os as _os
import warnings as _warnings
from .controller_db import mapping_list
_env_config = os.environ.get('SDL_GAMECONTROLLERCONFIG')
_env_config = _os.environ.get('SDL_GAMECONTROLLERCONFIG')
if _env_config:
# insert at the front of the list
mapping_list.insert(0, _env_config)
@ -101,12 +102,11 @@ def _parse_mapping(mapping_string):
if ':' not in item:
continue
key, relation_string = item.split(':')
key, relation_string, *etc = item.split(':')
if key not in valid_keys:
continue
inverted = False
# Look for specific flags to signify inverted axis:
if "+" in relation_string:
relation_string = relation_string.strip('+')
@ -114,9 +114,11 @@ def _parse_mapping(mapping_string):
elif "-" in relation_string:
relation_string = relation_string.strip('-')
inverted = True
if "~" in relation_string:
elif "~" in relation_string:
relation_string = relation_string.strip('~')
inverted = True
else:
inverted = False
# All relations will be one of (Button, Axis, or Hat).
if relation_string.startswith("b"): # Button
@ -141,7 +143,11 @@ def get_mapping(guid):
"""
for mapping in mapping_list:
if mapping.startswith(guid):
try:
return _parse_mapping(mapping)
except ValueError:
_warnings.warn(f"Unable to parse Controller mapping: {mapping}")
continue
def add_mappings_from_file(filename) -> None:

View File

@ -2,7 +2,7 @@ from pyglet import compat_platform
# This file is automatically generated by 'pyglet/tools/gen_controller_db.py'
# Generated on: Thu Jul 7 09:31:08 2022
# Generated on: Wed Aug 3 10:51:56 2022
if compat_platform.startswith("linux"):
mapping_list = [
@ -27,6 +27,10 @@ if compat_platform.startswith("linux"):
"05000000203800000900000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"05000000c82d00002038000000010000,8BitDo NES30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"05000000c82d00002038000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00000660000011010000,8BitDo Pro 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00000660000011010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"05000000c82d00000660000000010000,8BitDo Pro 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"05000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"05000000c82d00000061000000010000,8BitDo SF30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"05000000c82d00000061000000010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"05000000102800000900000000010000,8BitDo SFC30 Gamepad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
@ -105,6 +109,8 @@ if compat_platform.startswith("linux"):
"030000000d0f00005001000009040000,HORI Fighting Commander OCTA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
"030000000d0f00008400000011010000,HORI Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
"030000000d0f00008500000010010000,HORI Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
"030000000d0f00008800000011010000,HORI Fighting Stick mini 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,",
"030000000d0f00008700000011010000,HORI Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,rightshoulder:b5,rightstick:b11,righttrigger:a4,start:b9,x:b0,y:b3,",
"030000000d0f0000d800000072056800,HORI Real Arcade Pro S,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
"030000000d0f0000aa00000011010000,HORI Real Arcade Pro,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
"030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
@ -171,8 +177,26 @@ if compat_platform.startswith("linux"):
"03000000790000004318000010010000,Nintendo GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000790000004318000010010000,Nintendo GameCube Controller,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"030000007e0500003703000000016800,Nintendo GameCube Controller,a:b0,b:b2,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b1,y:b3,",
"050000004c69632050726f20436f6e00,Nintendo Switch Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000004c69632050726f20436f6e00,Nintendo Switch Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000007e0500000620000001800000,Nintendo Switch Joy-Con (L),a:b15,b:b16,guide:b4,leftshoulder:b6,leftstick:b12,leftx:a1,lefty:a0~,rightshoulder:b8,start:b9,x:b17,y:b14,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000007e0500000620000001800000,Nintendo Switch Joy-Con (L),a:b16,b:b15,guide:b4,leftshoulder:b6,leftstick:b12,leftx:a1,lefty:a0~,rightshoulder:b8,start:b9,x:b14,y:b17,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"060000007e0500000620000000000000,Nintendo Switch Joy-Con (L/R),a:b1,b:b0,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b2,y:b3,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"060000007e0500000620000000000000,Nintendo Switch Joy-Con (L/R),a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"060000007e0500000820000000000000,Nintendo Switch Joy-Con (L/R),a:b1,b:b0,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b2,y:b3,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"060000007e0500000820000000000000,Nintendo Switch Joy-Con (L/R),a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000007e0500000720000001800000,Nintendo Switch Joy-Con (R),a:b2,b:b1,guide:b9,leftshoulder:b4,leftstick:b10,leftx:a1~,lefty:a0,rightshoulder:b6,start:b8,x:b3,y:b0,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000007e0500000720000001800000,Nintendo Switch Joy-Con (R),a:b1,b:b2,guide:b9,leftshoulder:b4,leftstick:b10,leftx:a1~,lefty:a0,rightshoulder:b6,start:b8,x:b0,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000d620000013a7000011010000,Nintendo Switch PowerA Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000d620000013a7000011010000,Nintendo Switch PowerA Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000d620000011a7000011010000,Nintendo Switch PowerA Core Plus Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000d620000011a7000011010000,Nintendo Switch PowerA Core Plus Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b1,b:b0,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b2,y:b3,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000007e0500000920000001800000,Nintendo Switch Pro Controller,a:b1,b:b0,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b2,y:b3,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000007e0500000920000001800000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,",
"05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
"030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,",
@ -338,6 +362,8 @@ elif compat_platform.startswith("darwin"):
"03000000022000000090000001000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000203800000900000000010000,8BitDo NES30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000203800000900000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00000660000000020000,8BitDo Pro 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00000660000000020000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000102800000900000000000000,8BitDo SFC30 Gamepad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000102800000900000000000000,8BitDo SFC30 Gamepad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00001290000001000000,8BitDo SN30 Gamepad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
@ -375,6 +401,8 @@ elif compat_platform.startswith("darwin"):
"03000000d11800000094000000010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,",
"030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
"030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
"030000000d0f00008800000000010000,HORI Fighting Stick mini 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,",
"030000000d0f00008700000000010000,HORI Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,",
"030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
"030000000d0f0000aa00000072050000,HORI Real Arcade Pro,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
"030000000d0f00006e00000000010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
@ -490,6 +518,8 @@ elif compat_platform.startswith("win"):
"03000000203800000900000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00002038000000000000,8BitDo NES30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00002038000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00000660000000000000,8BitDo Pro 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00000660000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00000060000000000000,8BitDo SF30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00000060000000000000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"03000000c82d00000061000000000000,8BitDo SF30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
@ -554,8 +584,6 @@ elif compat_platform.startswith("win"):
"03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,",
"03000000852100000201000000000000,FF-GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
"030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
"030000000d0f00008700000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
"030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,",
"78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,",
"03000000151900004000000000000000,Flydigi Vader 2,a:b11,b:b10,back:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,leftstick:b1,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b0,righttrigger:b4,rightx:a3,righty:a4,start:b2,x:b9,y:b8,",
"03000000b40400001124000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b4,paddle2:b5,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,y:b3,",
@ -582,6 +610,8 @@ elif compat_platform.startswith("win"):
"03000000632500002605000000000000,HJD-X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
"030000000d0f00008400000000000000,HORI Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
"030000000d0f00008500000000000000,HORI Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
"030000000d0f00008800000000000000,HORI Fighting Stick mini 4 (PS3),a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,",
"030000000d0f00008700000000000000,HORI Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
"030000000d0f00006e00000000000000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
"030000000d0f00006600000000000000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
"030000000d0f0000ee00000000000000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",

View File

@ -46,13 +46,16 @@ for creating orthographic and perspective projection matrixes.
the object is not updated in-place.
"""
from __future__ import annotations
import math as _math
import warnings as _warnings
from collections.abc import Iterable, Iterator
from operator import mul as _mul
from typing import NoReturn, TypeVar, cast, overload, Tuple
def clamp(num, min_val, max_val):
def clamp(num: float, min_val: float, max_val: float) -> float:
return max(min(num, max_val), min_val)
@ -70,57 +73,65 @@ class Vec2:
"""
def __init__(self, x=0.0, y=0.0):
def __init__(self, x: float = 0.0, y: float = 0.0) -> None:
self.x = x
self.y = y
def __iter__(self):
def __iter__(self) -> Iterator[float]:
yield self.x
yield self.y
def __len__(self):
def __len__(self) -> int:
return 2
@overload
def __getitem__(self, item: int) -> float:
...
@overload
def __getitem__(self, item: slice) -> tuple[float, ...]:
...
def __getitem__(self, item):
return (self.x, self.y)[item]
def __add__(self, other):
def __add__(self, other: Vec2) -> Vec2:
return Vec2(self.x + other.x, self.y + other.y)
def __sub__(self, other):
def __sub__(self, other: Vec2) -> Vec2:
return Vec2(self.x - other.x, self.y - other.y)
def __mul__(self, other):
def __mul__(self, other: Vec2) -> Vec2:
return Vec2(self.x * other.x, self.y * other.y)
def __truediv__(self, other):
def __truediv__(self, other: Vec2) -> Vec2:
return Vec2(self.x / other.x, self.y / other.y)
def __abs__(self):
def __abs__(self) -> float:
return _math.sqrt(self.x ** 2 + self.y ** 2)
def __neg__(self):
def __neg__(self) -> Vec2:
return Vec2(-self.x, -self.y)
def __round__(self, ndigits=None):
def __round__(self, ndigits: int | None = None) -> Vec2:
return Vec2(*(round(v, ndigits) for v in self))
def __radd__(self, other):
def __radd__(self, other: Vec2 | int) -> Vec2:
"""Reverse add. Required for functionality with sum()
"""
if other == 0:
return self
else:
return self.__add__(other)
return self.__add__(cast(Vec2, other))
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __eq__(self, other: object) -> bool:
return isinstance(other, Vec2) and self.x == other.x and self.y == other.y
def __ne__(self, other):
return self.x != other.x or self.y != other.y
def __ne__(self, other: object) -> bool:
return not isinstance(other, Vec2) or self.x != other.x or self.y != other.y
@staticmethod
def from_polar(mag, angle):
def from_polar(mag: float, angle: float) -> Vec2:
"""Create a new vector from the given polar coordinates.
:parameters:
@ -134,7 +145,7 @@ class Vec2:
"""
return Vec2(mag * _math.cos(angle), mag * _math.sin(angle))
def from_magnitude(self, magnitude):
def from_magnitude(self, magnitude: float) -> Vec2:
"""Create a new Vector of the given magnitude by normalizing,
then scaling the vector. The heading remains unchanged.
@ -147,7 +158,7 @@ class Vec2:
"""
return self.normalize().scale(magnitude)
def from_heading(self, heading):
def from_heading(self, heading: float) -> Vec2:
"""Create a new vector of the same magnitude with the given heading. I.e. Rotate the vector to the heading.
:parameters:
@ -161,7 +172,7 @@ class Vec2:
return Vec2(mag * _math.cos(heading), mag * _math.sin(heading))
@property
def heading(self):
def heading(self) -> float:
"""The angle of the vector in radians.
:type: float
@ -169,7 +180,7 @@ class Vec2:
return _math.atan2(self.y, self.x)
@property
def mag(self):
def mag(self) -> float:
"""The magnitude, or length of the vector. The distance between the coordinates and the origin.
Alias of abs(self).
@ -178,7 +189,7 @@ class Vec2:
"""
return self.__abs__()
def limit(self, maximum):
def limit(self, maximum: float) -> Vec2:
"""Limit the magnitude of the vector to the value used for the max parameter.
:parameters:
@ -192,12 +203,12 @@ class Vec2:
return self.from_magnitude(maximum)
return self
def lerp(self, other, alpha):
"""Create a new vector lineraly interpolated between this vector and another vector.
def lerp(self, other: Vec2, alpha: float) -> Vec2:
"""Create a new Vec2 linearly interpolated between this vector and another Vec2.
:parameters:
`other` : Vec2 :
The vector to be linerly interpolated to.
The vector to linearly interpolate with.
`alpha` : float or int :
The amount of interpolation.
Some value between 0.0 (this vector) and 1.0 (other vector).
@ -209,19 +220,19 @@ class Vec2:
return Vec2(self.x + (alpha * (other.x - self.x)),
self.y + (alpha * (other.y - self.y)))
def scale(self, value):
def scale(self, value: float) -> Vec2:
"""Multiply the vector by a scalar value.
:parameters:
`value` : int or float :
The ammount to be scaled by
The value to scale the vector by.
:returns: A new vector scaled by the value.
:rtype: Vec2
"""
return Vec2(self.x * value, self.y * value)
def rotate(self, angle):
def rotate(self, angle: float) -> Vec2:
"""Create a new Vector rotated by the angle. The magnitude remains unchanged.
:parameters:
@ -233,9 +244,9 @@ class Vec2:
"""
mag = self.mag
heading = self.heading
return Vec2(mag * _math.cos(heading + angle), mag * _math.sin(heading+angle))
return Vec2(mag * _math.cos(heading + angle), mag * _math.sin(heading + angle))
def distance(self, other):
def distance(self, other: Vec2) -> float:
"""Calculate the distance between this vector and another 2D vector.
:parameters:
@ -247,7 +258,7 @@ class Vec2:
"""
return _math.sqrt(((other.x - self.x) ** 2) + ((other.y - self.y) ** 2))
def normalize(self):
def normalize(self) -> Vec2:
"""Normalize the vector to have a magnitude of 1. i.e. make it a unit vector.
:returns: A unit vector with the same heading.
@ -258,7 +269,7 @@ class Vec2:
return Vec2(self.x / d, self.y / d)
return self
def clamp(self, min_val, max_val):
def clamp(self, min_val: float, max_val: float) -> Vec2:
"""Restrict the value of the X and Y components of the vector to be within the given values.
:parameters:
@ -272,7 +283,7 @@ class Vec2:
"""
return Vec2(clamp(self.x, min_val, max_val), clamp(self.y, min_val, max_val))
def dot(self, other):
def dot(self, other: Vec2) -> float:
"""Calculate the dot product of this vector and another 2D vector.
:parameters:
@ -284,15 +295,17 @@ class Vec2:
"""
return self.x * other.x + self.y * other.y
def __getattr__(self, attrs):
def __getattr__(self, attrs: str) -> Vec2 | Vec3 | Vec4:
try:
# Allow swizzed getting of attrs
vec_class = {2: Vec2, 3: Vec3, 4: Vec4}.get(len(attrs))
# Allow swizzled getting of attrs
vec_class = {2: Vec2, 3: Vec3, 4: Vec4}[len(attrs)]
return vec_class(*(self['xy'.index(c)] for c in attrs))
except Exception:
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{attrs}'")
raise AttributeError(
f"'{self.__class__.__name__}' object has no attribute '{attrs}'"
) from None
def __repr__(self):
def __repr__(self) -> str:
return f"Vec2({self.x}, {self.y})"
@ -312,24 +325,32 @@ class Vec3:
"""
def __init__(self, x=0.0, y=0.0, z=0.0):
def __init__(self, x: float = 0.0, y: float = 0.0, z: float = 0.0) -> None:
self.x = x
self.y = y
self.z = z
def __iter__(self):
def __iter__(self) -> Iterator[float]:
yield self.x
yield self.y
yield self.z
@overload
def __getitem__(self, item: int) -> float:
...
@overload
def __getitem__(self, item: slice) -> tuple[float, ...]:
...
def __getitem__(self, item):
return (self.x, self.y, self.z)[item]
def __len__(self):
def __len__(self) -> int:
return 3
@property
def mag(self):
def mag(self) -> float:
"""The magnitude, or length of the vector. The distance between the coordinates and the origin.
Alias of abs(self).
@ -338,42 +359,42 @@ class Vec3:
"""
return self.__abs__()
def __add__(self, other):
def __add__(self, other: Vec3) -> Vec3:
return Vec3(self.x + other.x, self.y + other.y, self.z + other.z)
def __sub__(self, other):
def __sub__(self, other: Vec3) -> Vec3:
return Vec3(self.x - other.x, self.y - other.y, self.z - other.z)
def __mul__(self, other):
def __mul__(self, other: Vec3) -> Vec3:
return Vec3(self.x * other.x, self.y * other.y, self.z * other.z)
def __truediv__(self, other):
def __truediv__(self, other: Vec3) -> Vec3:
return Vec3(self.x / other.x, self.y / other.y, self.z / other.z)
def __abs__(self):
def __abs__(self) -> float:
return _math.sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2)
def __neg__(self):
def __neg__(self) -> Vec3:
return Vec3(-self.x, -self.y, -self.z)
def __round__(self, ndigits=None):
def __round__(self, ndigits: int | None = None) -> Vec3:
return Vec3(*(round(v, ndigits) for v in self))
def __radd__(self, other):
def __radd__(self, other: Vec3 | int) -> Vec3:
"""Reverse add. Required for functionality with sum()
"""
if other == 0:
return self
else:
return self.__add__(other)
return self.__add__(cast(Vec3, other))
def __eq__(self, other):
return self.x == other.x and self.y == other.y and self.z == other.z
def __eq__(self, other: object) -> bool:
return isinstance(object, Vec3) and self.x == other.x and self.y == other.y and self.z == other.z
def __ne__(self, other):
return self.x != other.x or self.y != other.y or self.z != other.z
def __ne__(self, other: object) -> bool:
return not isinstance(object, Vec3) or self.x != other.x or self.y != other.y or self.z != other.z
def from_magnitude(self, magnitude):
def from_magnitude(self, magnitude: float) -> Vec3:
"""Create a new Vector of the given magnitude by normalizing,
then scaling the vector. The rotation remains unchanged.
@ -386,7 +407,7 @@ class Vec3:
"""
return self.normalize().scale(magnitude)
def limit(self, maximum):
def limit(self, maximum: float) -> Vec3:
"""Limit the magnitude of the vector to the value used for the max parameter.
:parameters:
@ -397,10 +418,10 @@ class Vec3:
:rtype: Vec3
"""
if self.x ** 2 + self.y ** 2 + self.z ** 2 > maximum * maximum * maximum:
return self.from_magnitude(max)
return self.from_magnitude(maximum)
return self
def cross(self, other):
def cross(self, other: Vec3) -> Vec3:
"""Calculate the cross product of this vector and another 3D vector.
:parameters:
@ -414,7 +435,7 @@ class Vec3:
(self.z * other.x) - (self.x * other.z),
(self.x * other.y) - (self.y * other.x))
def dot(self, other):
def dot(self, other: Vec3) -> float:
"""Calculate the dot product of this vector and another 3D vector.
:parameters:
@ -426,12 +447,12 @@ class Vec3:
"""
return self.x * other.x + self.y * other.y + self.z * other.z
def lerp(self, other, alpha):
"""Create a new vector lineraly interpolated between this vector and another vector.
def lerp(self, other: Vec3, alpha: float) -> Vec3:
"""Create a new Vec3 linearly interpolated between this vector and another Vec3.
:parameters:
`other` : Vec3 :
The vector to be linerly interpolated to.
The vector to linearly interpolate with.
`alpha` : float or int :
The amount of interpolation.
Some value between 0.0 (this vector) and 1.0 (other vector).
@ -444,19 +465,19 @@ class Vec3:
self.y + (alpha * (other.y - self.y)),
self.z + (alpha * (other.z - self.z)))
def scale(self, value):
def scale(self, value: float) -> Vec3:
"""Multiply the vector by a scalar value.
:parameters:
`value` : int or float :
The ammount to be scaled by
The value to scale the vector by.
:returns: A new vector scaled by the value.
:rtype: Vec3
"""
return Vec3(self.x * value, self.y * value, self.z * value)
def distance(self, other):
def distance(self, other: Vec3) -> float:
"""Calculate the distance between this vector and another 3D vector.
:parameters:
@ -470,7 +491,7 @@ class Vec3:
((other.y - self.y) ** 2) +
((other.z - self.z) ** 2))
def normalize(self):
def normalize(self) -> Vec3:
"""Normalize the vector to have a magnitude of 1. i.e. make it a unit vector.
:returns: A unit vector with the same rotation.
@ -481,7 +502,7 @@ class Vec3:
return Vec3(self.x / d, self.y / d, self.z / d)
return self
def clamp(self, min_val, max_val):
def clamp(self, min_val: float, max_val: float) -> Vec3:
"""Restrict the value of the X, Y and Z components of the vector to be within the given values.
:parameters:
@ -497,15 +518,17 @@ class Vec3:
clamp(self.y, min_val, max_val),
clamp(self.z, min_val, max_val))
def __getattr__(self, attrs):
def __getattr__(self, attrs: str) -> Vec2 | Vec3 | Vec4:
try:
# Allow swizzed getting of attrs
vec_class = {2: Vec2, 3: Vec3, 4: Vec4}.get(len(attrs))
# Allow swizzled getting of attrs
vec_class = {2: Vec2, 3: Vec3, 4: Vec4}[len(attrs)]
return vec_class(*(self['xyz'.index(c)] for c in attrs))
except Exception:
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{attrs}'")
raise AttributeError(
f"'{self.__class__.__name__}' object has no attribute '{attrs}'"
) from None
def __repr__(self):
def __repr__(self) -> str:
return f"Vec3({self.x}, {self.y}, {self.z})"
@ -527,100 +550,144 @@ class Vec4:
"""
def __init__(self, x=0.0, y=0.0, z=0.0, w=0.0):
def __init__(self, x: float = 0.0, y: float = 0.0, z: float = 0.0, w: float = 0.0) -> None:
self.x = x
self.y = y
self.z = z
self.w = w
def __iter__(self):
def __iter__(self) -> Iterator[float]:
yield self.x
yield self.y
yield self.z
yield self.w
@overload
def __getitem__(self, item: int) -> float:
...
@overload
def __getitem__(self, item: slice) -> tuple[float, ...]:
...
def __getitem__(self, item):
return (self.x, self.y, self.z, self.w)[item]
def __len__(self):
def __len__(self) -> int:
return 4
def __add__(self, other):
def __add__(self, other: Vec4) -> Vec4:
return Vec4(self.x + other.x, self.y + other.y, self.z + other.z, self.w + other.w)
def __sub__(self, other):
def __sub__(self, other: Vec4) -> Vec4:
return Vec4(self.x - other.x, self.y - other.y, self.z - other.z, self.w - other.w)
def __mul__(self, other):
def __mul__(self, other: Vec4) -> Vec4:
return Vec4(self.x * other.x, self.y * other.y, self.z * other.z, self.w * other.w)
def __truediv__(self, other):
def __truediv__(self, other: Vec4) -> Vec4:
return Vec4(self.x / other.x, self.y / other.y, self.z / other.z, self.w / other.w)
def __abs__(self):
def __abs__(self) -> float:
return _math.sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2 + self.w ** 2)
def __neg__(self):
def __neg__(self) -> Vec4:
return Vec4(-self.x, -self.y, -self.z, -self.w)
def __round__(self, ndigits=None):
def __round__(self, ndigits: int | None = None) -> Vec4:
return Vec4(*(round(v, ndigits) for v in self))
def __radd__(self, other):
def __radd__(self, other: Vec4 | int) -> Vec4:
if other == 0:
return self
else:
return self.__add__(other)
return self.__add__(cast(Vec4, other))
def __eq__(self, other):
return self.x == other.x and self.y == other.y and self.z == other.z and self.w == other.w
def __eq__(self, other: object) -> bool:
return (
isinstance(other, Vec4)
and self.x == other.x
and self.y == other.y
and self.z == other.z
and self.w == other.w
)
def __ne__(self, other):
return self.x != other.x or self.y != other.y or self.z != other.z or self.w != other.w
def __ne__(self, other: object) -> bool:
return (
not isinstance(other, Vec4)
or self.x != other.x
or self.y != other.y
or self.z != other.z
or self.w != other.w
)
def lerp(self, other, alpha):
def lerp(self, other: Vec4, alpha: float) -> Vec4:
"""Create a new Vec4 linearly interpolated between this one and another Vec4.
:parameters:
`other` : Vec4 :
The vector to linearly interpolate with.
`alpha` : float or int :
The amount of interpolation.
Some value between 0.0 (this vector) and 1.0 (other vector).
0.5 is halfway inbetween.
:returns: A new interpolated vector.
:rtype: Vec4
"""
return Vec4(self.x + (alpha * (other.x - self.x)),
self.y + (alpha * (other.y - self.y)),
self.z + (alpha * (other.z - self.z)),
self.w + (alpha * (other.w - self.w)))
def scale(self, value):
def scale(self, value: float) -> Vec4:
"""Multiply the vector by a scalar value.
:parameters:
`value` : int or float :
The value to scale the vector by.
:returns: A new vector scaled by the value.
:rtype: Vec4
"""
return Vec4(self.x * value, self.y * value, self.z * value, self.w * value)
def distance(self, other):
def distance(self, other: Vec4) -> float:
return _math.sqrt(((other.x - self.x) ** 2) +
((other.y - self.y) ** 2) +
((other.z - self.z) ** 2) +
((other.w - self.w) ** 2))
def normalize(self):
def normalize(self) -> Vec4:
d = self.__abs__()
if d:
return Vec4(self.x / d, self.y / d, self.z / d, self.w / d)
return self
def clamp(self, min_val, max_val):
def clamp(self, min_val: float, max_val: float) -> Vec4:
return Vec4(clamp(self.x, min_val, max_val),
clamp(self.y, min_val, max_val),
clamp(self.z, min_val, max_val),
clamp(self.w, min_val, max_val))
def dot(self, other):
def dot(self, other: Vec4) -> float:
return self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w
def __getattr__(self, attrs):
def __getattr__(self, attrs: str) -> Vec2 | Vec3 | Vec4:
try:
# Allow swizzed getting of attrs
vec_class = {2: Vec2, 3: Vec3, 4: Vec4}.get(len(attrs))
# Allow swizzled getting of attrs
vec_class = {2: Vec2, 3: Vec3, 4: Vec4}[len(attrs)]
return vec_class(*(self['xyzw'.index(c)] for c in attrs))
except Exception:
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{attrs}'")
raise AttributeError(
f"'{self.__class__.__name__}' object has no attribute '{attrs}'"
) from None
def __repr__(self):
def __repr__(self) -> str:
return f"Vec4({self.x}, {self.y}, {self.z}, {self.w})"
class Mat3(tuple):
class Mat3(Tuple[float, float, float, float, float, float, float, float, float]):
"""A 3x3 Matrix class
`Mat3` is an immutable 3x3 Matrix, including most common
@ -628,7 +695,9 @@ class Mat3(tuple):
the "@" operator.
"""
def __new__(cls, values=None) -> 'Mat3':
def __new__(
cls, values: Iterable[float] = (1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0)
) -> Mat3:
"""Create a 3x3 Matrix
A Mat3 can be created with a list or tuple of 9 values.
@ -640,49 +709,56 @@ class Mat3(tuple):
`values` : tuple of float or int
A tuple or list containing 9 floats or ints.
"""
assert values is None or len(values) == 9, "A 3x3 Matrix requires 9 values"
return super().__new__(Mat3, values or (1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0))
new = super().__new__(Mat3, values)
assert len(new) == 9, "A 3x3 Matrix requires 9 values"
return new
def scale(self, sx: float, sy: float):
return self @ (1.0 / sx, 0.0, 0.0, 0.0, 1.0 / sy, 0.0, 0.0, 0.0, 1.0)
def scale(self, sx: float, sy: float) -> Mat3:
return self @ Mat3((1.0 / sx, 0.0, 0.0, 0.0, 1.0 / sy, 0.0, 0.0, 0.0, 1.0))
def translate(self, tx: float, ty: float):
return self @ (1.0, 0.0, 0.0, 0.0, 1.0, 0.0, -tx, ty, 1.0)
def translate(self, tx: float, ty: float) -> Mat3:
return self @ Mat3((1.0, 0.0, 0.0, 0.0, 1.0, 0.0, -tx, ty, 1.0))
def rotate(self, phi: float):
def rotate(self, phi: float) -> Mat3:
s = _math.sin(_math.radians(phi))
c = _math.cos(_math.radians(phi))
return self @ (c, s, 0.0, -s, c, 0.0, 0.0, 0.0, 1.0)
return self @ Mat3((c, s, 0.0, -s, c, 0.0, 0.0, 0.0, 1.0))
def shear(self, sx: float, sy: float):
return self @ (1.0, sy, 0.0, sx, 1.0, 0.0, 0.0, 0.0, 1.0)
def shear(self, sx: float, sy: float) -> Mat3:
return self @ Mat3((1.0, sy, 0.0, sx, 1.0, 0.0, 0.0, 0.0, 1.0))
def __add__(self, other) -> 'Mat3':
assert len(other) == 9, "Can only add to other Mat3 types"
return Mat3(tuple(s + o for s, o in zip(self, other)))
def __add__(self, other: Mat3) -> Mat3:
if not isinstance(other, Mat3):
raise TypeError("Can only add to other Mat3 types")
return Mat3(s + o for s, o in zip(self, other))
def __sub__(self, other) -> 'Mat3':
assert len(other) == 9, "Can only subtract from other Mat3 types"
return Mat3(tuple(s - o for s, o in zip(self, other)))
def __sub__(self, other: Mat3) -> Mat3:
if not isinstance(other, Mat3):
raise TypeError("Can only subtract from other Mat3 types")
return Mat3(s - o for s, o in zip(self, other))
def __pos__(self):
def __pos__(self) -> Mat3:
return self
def __neg__(self) -> 'Mat3':
return Mat3(tuple(-v for v in self))
def __neg__(self) -> Mat3:
return Mat3(-v for v in self)
def __round__(self, ndigits=None) -> 'Mat3':
return Mat3(tuple(round(v, ndigits) for v in self))
def __round__(self, ndigits: int | None = None) -> Mat3:
return Mat3(round(v, ndigits) for v in self)
def __mul__(self, other):
def __mul__(self, other: object) -> NoReturn:
raise NotImplementedError("Please use the @ operator for Matrix multiplication.")
def __matmul__(self, other) -> 'Mat3' or 'Vec3':
assert len(other) in (3, 9), "Can only multiply with Mat3 or Vec3 types"
@overload
def __matmul__(self, other: Vec3) -> Vec3:
...
if type(other) is Vec3:
@overload
def __matmul__(self, other: Mat3) -> Mat3:
...
def __matmul__(self, other):
if isinstance(other, Vec3):
# Columns:
c0 = self[0::3]
c1 = self[1::3]
@ -691,6 +767,9 @@ class Mat3(tuple):
sum(map(_mul, c1, other)),
sum(map(_mul, c2, other)))
if not isinstance(other, Mat3):
raise TypeError("Can only multiply with Mat3 or Vec3 types")
# Rows:
r0 = self[0:3]
r1 = self[3:6]
@ -717,7 +796,17 @@ class Mat3(tuple):
return f"{self.__class__.__name__}{self[0:3]}\n {self[3:6]}\n {self[6:9]}"
class Mat4(tuple):
Mat4T = TypeVar("Mat4T", bound="Mat4")
class Mat4(
Tuple[
float, float, float, float,
float, float, float, float,
float, float, float, float,
float, float, float, float,
]
):
"""A 4x4 Matrix class
`Mat4` is an immutable 4x4 Matrix, including most common
@ -727,7 +816,15 @@ class Mat4(tuple):
and perspective projections matrixes.
"""
def __new__(cls, values=None) -> 'Mat4':
def __new__(
cls,
values: Iterable[float] = (
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
),
) -> Mat4:
"""Create a 4x4 Matrix
A Matrix can be created with a list or tuple of 16 values.
@ -739,14 +836,21 @@ class Mat4(tuple):
`values` : tuple of float or int
A tuple or list containing 16 floats or ints.
"""
assert values is None or len(values) == 16, "A 4x4 Matrix requires 16 values"
return super().__new__(Mat4, values or (1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0))
new = super().__new__(Mat4, values)
assert len(new) == 16, "A 4x4 Matrix requires 16 values"
return new
@classmethod
def orthogonal_projection(cls, left, right, bottom, top, z_near, z_far) -> 'Mat4':
def orthogonal_projection(
cls: type[Mat4T],
left: float,
right: float,
bottom: float,
top: float,
z_near: float,
z_far: float
) -> Mat4T:
"""Create a Mat4 orthographic projection matrix."""
width = right - left
height = top - bottom
@ -766,7 +870,13 @@ class Mat4(tuple):
tx, ty, tz, 1.0))
@classmethod
def perspective_projection(cls, aspect, z_near, z_far, fov=60) -> 'Mat4':
def perspective_projection(
cls: type[Mat4T],
aspect: float,
z_near: float,
z_far: float,
fov: float = 60
) -> Mat4T:
"""
Create a Mat4 perspective projection matrix.
@ -796,7 +906,7 @@ class Mat4(tuple):
0, 0, qn, 0))
@classmethod
def from_translation(cls, vector: Vec3) -> 'Mat4':
def from_translation(cls: type[Mat4T], vector: Vec3) -> Mat4T:
"""Create a translation matrix from a Vec3.
:Parameters:
@ -809,7 +919,7 @@ class Mat4(tuple):
vector[0], vector[1], vector[2], 1.0))
@classmethod
def from_rotation(cls, angle: float, vector: Vec3) -> 'Mat4':
def from_rotation(cls, angle: float, vector: Vec3) -> Mat4:
"""Create a rotation matrix from an angle and Vec3.
:Parameters:
@ -821,10 +931,10 @@ class Mat4(tuple):
return cls().rotate(angle, vector)
@classmethod
def look_at_direction(cls, direction: Vec3, up: Vec3) -> 'Mat4':
def look_at_direction(cls: type[Mat4T], direction: Vec3, up: Vec3) -> Mat4T:
vec_z = direction.normalize()
vec_x = direction.cross_product(up).normalize()
vec_y = direction.cross_product(vec_z).normalize()
vec_x = direction.cross(up).normalize()
vec_y = direction.cross(vec_z).normalize()
return cls((vec_x.x, vec_y.x, vec_z.x, 0.0,
vec_x.y, vec_y.y, vec_z.y, 0.0,
@ -832,21 +942,21 @@ class Mat4(tuple):
0.0, 0.0, 0.0, 1.0))
@classmethod
def look_at(cls, position: Vec3, target: Vec3, up: Vec3) -> 'Mat4':
def look_at(cls, position: Vec3, target: Vec3, up: Vec3) -> Mat4:
direction = target - position
direction_mat4 = cls.look_at_direction(direction, up)
position_mat4 = cls.from_translation(position.negate())
position_mat4 = cls.from_translation(-position)
return direction_mat4 @ position_mat4
def row(self, index: int):
def row(self, index: int) -> tuple:
"""Get a specific row as a tuple."""
return self[index*4:index*4+4]
return self[index * 4 : index * 4 + 4]
def column(self, index: int):
def column(self, index: int) -> tuple:
"""Get a specific column as a tuple."""
return self[index::4]
def scale(self, vector: Vec3) -> 'Mat4':
def scale(self, vector: Vec3) -> Mat4:
"""Get a scale Matrix on x, y, or z axis."""
temp = list(self)
temp[0] *= vector[0]
@ -854,13 +964,14 @@ class Mat4(tuple):
temp[10] *= vector[2]
return Mat4(temp)
def translate(self, vector: Vec3) -> 'Mat4':
def translate(self, vector: Vec3) -> Mat4:
"""Get a translation Matrix along x, y, and z axis."""
return Mat4(self) @ Mat4((1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, *vector, 1))
return self @ Mat4((1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, *vector, 1))
def rotate(self, angle: float, vector: Vec3) -> 'Mat4':
def rotate(self, angle: float, vector: Vec3) -> Mat4:
"""Get a rotation Matrix on x, y, or z axis."""
assert all(abs(n) <= 1 for n in vector), "vector must be normalized (<=1)"
if not all(abs(n) <= 1 for n in vector):
raise ValueError("vector must be normalized (<=1)")
x, y, z = vector
c = _math.cos(angle)
s = _math.sin(angle)
@ -884,25 +995,27 @@ class Mat4(tuple):
return Mat4(self) @ Mat4((ra, rb, rc, 0, re, rf, rg, 0, ri, rj, rk, 0, 0, 0, 0, 1))
def transpose(self) -> 'Mat4':
def transpose(self) -> Mat4:
"""Get a transpose of this Matrix."""
return Mat4(self[0::4] + self[1::4] + self[2::4] + self[3::4])
def __add__(self, other) -> 'Mat4':
assert len(other) == 16, "Can only add to other Mat4 types"
return Mat4(tuple(s + o for s, o in zip(self, other)))
def __add__(self, other: Mat4) -> Mat4:
if not isinstance(other, Mat4):
raise TypeError("Can only add to other Mat4 types")
return Mat4(s + o for s, o in zip(self, other))
def __sub__(self, other) -> 'Mat4':
assert len(other) == 16, "Can only subtract from other Mat4 types"
return Mat4(tuple(s - o for s, o in zip(self, other)))
def __sub__(self, other: Mat4) -> Mat4:
if not isinstance(other, Mat4):
raise TypeError("Can only subtract from other Mat4 types")
return Mat4(s - o for s, o in zip(self, other))
def __pos__(self):
def __pos__(self) -> Mat4:
return self
def __neg__(self) -> 'Mat4':
return Mat4(tuple(-v for v in self))
def __neg__(self) -> Mat4:
return Mat4(-v for v in self)
def __invert__(self) -> 'Mat4':
def __invert__(self) -> Mat4:
a = self[10] * self[15] - self[11] * self[14]
b = self[9] * self[15] - self[11] * self[13]
c = self[9] * self[14] - self[10] * self[13]
@ -951,16 +1064,22 @@ class Mat4(tuple):
ndet * (self[0] * i - self[1] * n + self[2] * q),
pdet * (self[0] * l - self[1] * p + self[2] * r)))
def __round__(self, ndigits=None) -> 'Mat4':
return Mat4(tuple(round(v, ndigits) for v in self))
def __round__(self, ndigits: int | None = None) -> Mat4:
return Mat4(round(v, ndigits) for v in self)
def __mul__(self, other):
def __mul__(self, other: int) -> NoReturn:
raise NotImplementedError("Please use the @ operator for Matrix multiplication.")
def __matmul__(self, other) -> 'Mat4' or 'Vec4':
assert len(other) in (4, 16), "Can only multiply with Mat4 or Vec4 types"
@overload
def __matmul__(self, other: Vec4) -> Vec4:
...
if type(other) is Vec4:
@overload
def __matmul__(self, other: Mat4) -> Mat4:
...
def __matmul__(self, other):
if isinstance(other, Vec4):
# Columns:
c0 = self[0::4]
c1 = self[1::4]
@ -971,6 +1090,8 @@ class Mat4(tuple):
sum(map(_mul, c2, other)),
sum(map(_mul, c3, other)))
if not isinstance(other, Mat4):
raise TypeError("Can only multiply with Mat4 or Vec4 types")
# Rows:
r0 = self[0:4]
r1 = self[4:8]

View File

@ -123,7 +123,7 @@ class AudioData:
length (int): Size of sample data, in bytes.
timestamp (float): Time of the first sample, in seconds.
duration (float): Total data duration, in seconds.
events (List[:class:`pyglet.media.events.MediaEvent`]): List of events
events (List[:class:`pyglet.media.drivers.base.MediaEvent`]): List of events
contained within this packet. Events are timestamped relative to
this audio packet.
"""

File diff suppressed because it is too large Load Diff

View File

@ -356,6 +356,7 @@ class Caret:
m2 = len(self._layout.document.text)
else:
m2 = m2.start()
self._position = m2
self._update(line=line)
self._next_attributes.clear()
@ -436,6 +437,7 @@ class Caret:
elif self._position > 0:
self._position -= 1
self._layout.document.delete_text(self._position, self._position + 1)
self._update()
elif motion == key.MOTION_DELETE:
if self.mark is not None:
self._delete_selection()

View File

@ -347,7 +347,7 @@ class _GlyphBox(_AbstractBox):
v2 += x1
v1 += y + baseline
v3 += y + baseline
vertices.extend(map(int, [v0, v1, v2, v1, v2, v3, v0, v3]))
vertices.extend(map(round, [v0, v1, v2, v1, v2, v3, v0, v3]))
t = glyph.tex_coords
tex_coords.extend(t)
x1 += glyph.advance
@ -412,17 +412,19 @@ class _GlyphBox(_AbstractBox):
if background_vertices:
background_indices = []
bg_count = len(background_vertices) // 2
for glyph_idx in range(bg_count):
background_indices.extend([element + (glyph_idx * 4) for element in [0, 1, 2, 0, 2, 3]])
decoration_program = get_default_decoration_shader()
for bg_idx in range(bg_count):
background_indices.extend([element + (bg_idx * 4) for element in [0, 1, 2, 0, 2, 3]])
background_list = program.vertex_list_indexed(bg_count, GL_TRIANGLES, background_indices,
background_list = decoration_program.vertex_list_indexed(bg_count * 4, GL_TRIANGLES, background_indices,
layout.batch, layout.background_decoration_group,
position=('f', background_vertices),
colors=('Bn', background_colors))
context.add_list(background_list)
if underline_vertices:
underline_list = program.vertex_list(len(underline_vertices) // 2, GL_LINES,
decoration_program = get_default_decoration_shader()
underline_list = decoration_program.vertex_list(len(underline_vertices) // 2, GL_LINES,
layout.batch, layout.foreground_decoration_group,
position=('f',underline_vertices),
colors=('Bn', underline_colors))
@ -778,6 +780,14 @@ class ScrollableTextDecorationGroup(graphics.Group):
glDisable(GL_BLEND)
self.program.stop()
def __repr__(self):
return f"{self.__class__.__name__}(scissor={self.scissor_area})"
def __eq__(self, other):
return self is other
def __hash__(self):
return id(self)
class IncrementalTextDecorationGroup(ScrollableTextDecorationGroup):
# Subclass so that the scissor_area isn't shared with the
@ -1797,6 +1807,9 @@ class ScrollableTextLayout(TextLayout):
for group in self.group_cache.values():
group.scissor_area = area
self.background_decoration_group.scissor_area = area
self.foreground_decoration_group.scissor_area = area
def _update(self):
super()._update()
self._update_scissor_area()
@ -1951,6 +1964,8 @@ class IncrementalTextLayout(TextLayout, EventDispatcher):
area = self._get_left(), self._get_bottom(self._get_lines()), self._width, self._height
for group in self.group_cache.values():
group.scissor_area = area
self.background_decoration_group.scissor_area = area
self.foreground_decoration_group.scissor_area = area
def _init_document(self):
assert self._document, 'Cannot remove document from IncrementalTextLayout'
@ -2532,7 +2547,7 @@ class IncrementalTextLayout(TextLayout, EventDispatcher):
position -= box.length
x += box.advance
return x + self._translate_x, line.y + self._translate_y + baseline
return x - self._translate_x, line.y + self._translate_y + baseline
def get_line_from_point(self, x, y):
"""Get the closest line index to a point.
@ -2610,6 +2625,8 @@ class IncrementalTextLayout(TextLayout, EventDispatcher):
:rtype: int
"""
line = self.lines[line]
x += self._translate_x
x -= self._x
if x < line.x:
@ -2659,6 +2676,8 @@ class IncrementalTextLayout(TextLayout, EventDispatcher):
X coordinate
"""
x += self.view_x - self._x
if x <= self.view_x + 10:
self.view_x = x - 10
elif x >= self.view_x + self.width:

View File

@ -321,6 +321,7 @@ class Win32Window(BaseWindow):
return
_user32.DestroyWindow(self._hwnd)
_user32.UnregisterClassW(self._view_window_class.lpszClassName, 0)
_user32.UnregisterClassW(self._window_class.lpszClassName, 0)
self._window_class = None