pyglet 2.1.dev2__py3-none-any.whl → 2.1.dev4__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.
Files changed (120) hide show
  1. pyglet/__init__.py +355 -231
  2. pyglet/__init__.pyi +29 -6
  3. pyglet/app/__init__.py +6 -8
  4. pyglet/app/base.py +86 -88
  5. pyglet/app/win32.py +1 -1
  6. pyglet/clock.py +93 -117
  7. pyglet/display/__init__.py +26 -29
  8. pyglet/display/base.py +44 -100
  9. pyglet/display/cocoa.py +4 -4
  10. pyglet/display/headless.py +1 -1
  11. pyglet/display/win32.py +22 -2
  12. pyglet/display/xlib.py +13 -9
  13. pyglet/event.py +119 -105
  14. pyglet/experimental/hidraw.py +28 -87
  15. pyglet/experimental/particles.py +423 -0
  16. pyglet/extlibs/earcut.py +714 -0
  17. pyglet/font/__init__.py +56 -45
  18. pyglet/font/base.py +147 -133
  19. pyglet/font/directwrite.py +658 -600
  20. pyglet/font/fontconfig.py +109 -91
  21. pyglet/font/freetype.py +102 -71
  22. pyglet/font/freetype_lib.py +346 -306
  23. pyglet/font/quartz.py +42 -36
  24. pyglet/font/ttf.py +238 -239
  25. pyglet/font/user.py +166 -57
  26. pyglet/font/win32.py +32 -36
  27. pyglet/gl/__init__.py +23 -16
  28. pyglet/gl/agl.py +1 -1
  29. pyglet/gl/base.py +184 -216
  30. pyglet/gl/cocoa.py +69 -61
  31. pyglet/gl/gl.py +5 -2
  32. pyglet/gl/gl_compat.py +18 -3
  33. pyglet/gl/gl_info.py +47 -64
  34. pyglet/gl/glx.py +10 -19
  35. pyglet/gl/glx_info.py +38 -19
  36. pyglet/gl/glxext_arb.py +6 -10
  37. pyglet/gl/glxext_mesa.py +1 -1
  38. pyglet/gl/glxext_nv.py +4 -8
  39. pyglet/gl/headless.py +54 -40
  40. pyglet/gl/lib.py +21 -17
  41. pyglet/gl/lib_agl.py +10 -4
  42. pyglet/gl/lib_glx.py +16 -8
  43. pyglet/gl/lib_wgl.py +17 -11
  44. pyglet/gl/wgl.py +175 -199
  45. pyglet/gl/wgl_info.py +10 -12
  46. pyglet/gl/win32.py +77 -56
  47. pyglet/gl/xlib.py +63 -32
  48. pyglet/graphics/__init__.py +255 -161
  49. pyglet/graphics/allocation.py +61 -81
  50. pyglet/graphics/instance.py +77 -0
  51. pyglet/graphics/shader.py +732 -445
  52. pyglet/graphics/vertexarray.py +20 -11
  53. pyglet/graphics/vertexbuffer.py +106 -78
  54. pyglet/graphics/vertexdomain.py +556 -196
  55. pyglet/gui/__init__.py +1 -0
  56. pyglet/gui/frame.py +68 -38
  57. pyglet/gui/ninepatch.py +269 -0
  58. pyglet/gui/widgets.py +137 -129
  59. pyglet/image/__init__.py +148 -91
  60. pyglet/image/animation.py +38 -77
  61. pyglet/image/atlas.py +74 -110
  62. pyglet/image/buffer.py +91 -50
  63. pyglet/image/codecs/__init__.py +9 -8
  64. pyglet/input/__init__.py +27 -38
  65. pyglet/input/base.py +379 -427
  66. pyglet/input/controller.py +33 -37
  67. pyglet/input/linux/evdev.py +12 -64
  68. pyglet/input/win32/__init__.py +4 -4
  69. pyglet/input/win32/wintab.py +8 -10
  70. pyglet/lib.py +53 -55
  71. pyglet/libs/__init__.py +18 -0
  72. pyglet/libs/darwin/cocoapy/runtime.py +262 -31
  73. pyglet/libs/darwin/coreaudio.py +86 -19
  74. pyglet/libs/darwin/quartzkey.py +32 -13
  75. pyglet/libs/ioctl.py +129 -0
  76. pyglet/libs/win32/__init__.py +1 -1
  77. pyglet/libs/win32/dinput.py +12 -10
  78. pyglet/libs/x11/xlib.py +1480 -947
  79. pyglet/math.py +973 -460
  80. pyglet/media/__init__.py +22 -17
  81. pyglet/media/codecs/__init__.py +4 -1
  82. pyglet/media/player.py +34 -67
  83. pyglet/media/player_worker_thread.py +31 -21
  84. pyglet/media/synthesis.py +105 -81
  85. pyglet/model/__init__.py +106 -58
  86. pyglet/model/codecs/__init__.py +12 -3
  87. pyglet/resource.py +295 -363
  88. pyglet/shapes.py +898 -499
  89. pyglet/sprite.py +255 -260
  90. pyglet/text/__init__.py +237 -244
  91. pyglet/text/caret.py +49 -96
  92. pyglet/text/document.py +220 -214
  93. pyglet/text/formats/__init__.py +1 -4
  94. pyglet/text/formats/attributed.py +32 -29
  95. pyglet/text/formats/html.py +166 -166
  96. pyglet/text/formats/plaintext.py +10 -4
  97. pyglet/text/formats/structured.py +99 -87
  98. pyglet/text/layout/__init__.py +28 -16
  99. pyglet/text/layout/base.py +415 -407
  100. pyglet/text/layout/incremental.py +189 -258
  101. pyglet/text/layout/scrolling.py +77 -60
  102. pyglet/text/runlist.py +91 -84
  103. pyglet/window/__init__.py +461 -580
  104. pyglet/window/cocoa/__init__.py +66 -68
  105. pyglet/window/cocoa/pyglet_delegate.py +49 -36
  106. pyglet/window/cocoa/pyglet_textview.py +89 -69
  107. pyglet/window/cocoa/pyglet_view.py +75 -55
  108. pyglet/window/cocoa/pyglet_window.py +31 -18
  109. pyglet/window/cocoa/systemcursor.py +4 -2
  110. pyglet/window/event.py +57 -52
  111. pyglet/window/headless/__init__.py +39 -31
  112. pyglet/window/key.py +20 -33
  113. pyglet/window/mouse.py +48 -34
  114. pyglet/window/win32/__init__.py +375 -350
  115. pyglet/window/xlib/__init__.py +257 -221
  116. {pyglet-2.1.dev2.dist-info → pyglet-2.1.dev4.dist-info}/METADATA +7 -3
  117. pyglet-2.1.dev4.dist-info/RECORD +219 -0
  118. pyglet-2.1.dev2.dist-info/RECORD +0 -214
  119. {pyglet-2.1.dev2.dist-info → pyglet-2.1.dev4.dist-info}/LICENSE +0 -0
  120. {pyglet-2.1.dev2.dist-info → pyglet-2.1.dev4.dist-info}/WHEEL +0 -0
pyglet/__init__.py CHANGED
@@ -2,25 +2,31 @@
2
2
 
3
3
  More information is available at http://www.pyglet.org
4
4
  """
5
+ from __future__ import annotations
5
6
 
6
7
  import os
7
8
  import sys
9
+ from collections.abc import ItemsView, Sequence
10
+ from dataclasses import dataclass
11
+ from typing import TYPE_CHECKING
8
12
 
9
- from typing import TYPE_CHECKING, Dict
13
+ if TYPE_CHECKING:
14
+ from types import FrameType
15
+ from typing import Any, Callable, ItemsView, Sized
10
16
 
11
17
  #: The release version
12
- version = '2.1.dev2'
18
+ version = '2.1.dev4'
13
19
  __version__ = version
14
20
 
15
21
  MIN_PYTHON_VERSION = 3, 8
16
- MIN_PYTHON_VERSION_STR = '.'.join([str(v) for v in MIN_PYTHON_VERSION])
22
+ MIN_PYTHON_VERSION_STR = ".".join([str(v) for v in MIN_PYTHON_VERSION])
17
23
 
18
24
  if sys.version_info < MIN_PYTHON_VERSION:
19
- raise Exception(f"pyglet {version} requires Python {MIN_PYTHON_VERSION_STR} or newer.")
25
+ msg = f"pyglet {version} requires Python {MIN_PYTHON_VERSION_STR} or newer."
26
+ raise Exception(msg)
20
27
 
21
- if 'sphinx' in sys.modules:
22
- setattr(sys, 'is_pyglet_doc_run', True)
23
- _is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run
28
+ if "sphinx" in sys.modules:
29
+ sys.is_pyglet_doc_run = True
24
30
 
25
31
  # pyglet platform treats *BSD systems as Linux
26
32
  compat_platform = sys.platform
@@ -28,192 +34,308 @@ if "bsd" in compat_platform:
28
34
  compat_platform = "linux-compat"
29
35
 
30
36
  _enable_optimisations = not __debug__
31
- if getattr(sys, 'frozen', None):
37
+ if getattr(sys, "frozen", None):
32
38
  _enable_optimisations = True
33
39
 
34
- #: Global dict of pyglet options.
35
- #:
36
- #: To change an option from its default, you must import
37
- #: ``pyglet`` before any sub-packages. For example::
38
- #:
39
- #: import pyglet
40
- #: pyglet.options['debug_gl'] = False
41
- #:
42
- #: The default options can be overridden from the OS environment. The
43
- #: corresponding environment variable for each option key is prefaced by
44
- #: ``PYGLET_``. For example, in Bash you can set the ``debug_gl`` option with::
45
- #:
46
- #: PYGLET_DEBUG_GL=True; export PYGLET_DEBUG_GL
47
- #:
48
- #: For options requiring a tuple of values, separate each value with a comma.
49
- #:
50
- #: The non-development options are:
51
- #:
52
- #: audio
53
- #: A :py:class:`~typing.Sequence` of valid audio modules names. They will
54
- #: be tried from first to last until either a driver loads or no entries
55
- #: remain. See :ref:`guide-audio-driver-order` for more information.
56
- #:
57
- #: Valid driver names are:
58
- #:
59
- #: * ``'xaudio2'``, the Windows Xaudio2 audio module (Windows only)
60
- #: * ``'directsound'``, the Windows DirectSound audio module (Windows only)
61
- #: * ``'pulse'``, the :ref:`guide-audio-driver-pulseaudio` module
62
- #: (Linux only, otherwise nearly ubiquitous. Limited features; use
63
- #: ``'openal'`` for more.)
64
- #: * ``'openal'``, the :ref:`guide-audio-driver-openal` audio module
65
- #: (A library may need to be installed on Windows and Linux)
66
- #: * ``'silent'``, no audio
67
- #:
68
- #: debug_lib
69
- #: If True, prints the path of each dynamic library loaded.
70
- #: debug_gl
71
- #: If True, all calls to OpenGL functions are checked afterwards for
72
- #: errors using ``glGetError``. This will severely impact performance,
73
- #: but provides useful exceptions at the point of failure. By default,
74
- #: this option is enabled if ``__debug__`` is (i.e., if Python was not run
75
- #: with the -O option). It is disabled by default when pyglet is "frozen"
76
- #: within a py2exe or py2app library archive.
77
- #: shadow_window
78
- #: By default, pyglet creates a hidden window with a GL context when
79
- #: pyglet.gl is imported. This allows resources to be loaded before
80
- #: the application window is created, and permits GL objects to be
81
- #: shared between windows even after they've been closed. You can
82
- #: disable the creation of the shadow window by setting this option to
83
- #: False.
84
- #:
85
- #: Some OpenGL driver implementations may not support shared OpenGL
86
- #: contexts and may require disabling the shadow window (and all resources
87
- #: must be loaded after the window using them was created). Recommended
88
- #: for advanced developers only.
89
- #:
90
- #: .. versionadded:: 1.1
91
- #: vsync
92
- #: If set, the `pyglet.window.Window.vsync` property is ignored, and
93
- #: this option overrides it (to either force vsync on or off). If unset,
94
- #: or set to None, the `pyglet.window.Window.vsync` property behaves
95
- #: as documented.
96
- #: search_local_libs
97
- #: If False, pyglet won't try to search for libraries in the script
98
- #: directory and its `lib` subdirectory. This is useful to load a local
99
- #: library instead of the system installed version. This option is set
100
- #: to True by default.
101
- #:
102
- #: .. versionadded:: 1.2
103
- #:
104
- options = {
105
- 'audio': ('xaudio2', 'directsound', 'openal', 'pulse', 'silent'),
106
- 'debug_font': False,
107
- 'debug_gl': not _enable_optimisations,
108
- 'debug_gl_trace': False,
109
- 'debug_gl_trace_args': False,
110
- 'debug_gl_shaders': False,
111
- 'debug_graphics_batch': False,
112
- 'debug_lib': False,
113
- 'debug_media': False,
114
- 'debug_texture': False,
115
- 'debug_trace': False,
116
- 'debug_trace_args': False,
117
- 'debug_trace_depth': 1,
118
- 'debug_trace_flush': True,
119
- 'debug_win32': False,
120
- 'debug_input': False,
121
- 'debug_x11': False,
122
- 'shadow_window': True,
123
- 'vsync': None,
124
- 'search_local_libs': True,
125
- 'win32_gdi_font': False,
126
- 'scale_with_dpi': False,
127
- 'headless': False,
128
- 'headless_device': 0,
129
- 'win32_disable_shaping': False,
130
- 'dw_legacy_naming': False,
131
- 'win32_disable_xinput': False,
132
- 'com_mta': False,
133
- 'osx_alt_loop': False
40
+
41
+ _SPECIAL_OPTION_VALIDATORS = {
42
+ "audio": lambda x: isinstance(x, Sequence),
43
+ "vsync": lambda x: x is None or isinstance(x, bool),
134
44
  }
135
45
 
136
- _option_types = {
137
- 'audio': tuple,
138
- 'debug_font': bool,
139
- 'debug_gl': bool,
140
- 'debug_gl_trace': bool,
141
- 'debug_gl_trace_args': bool,
142
- 'debug_gl_shaders': bool,
143
- 'debug_graphics_batch': bool,
144
- 'debug_lib': bool,
145
- 'debug_media': bool,
146
- 'debug_texture': bool,
147
- 'debug_trace': bool,
148
- 'debug_trace_args': bool,
149
- 'debug_trace_depth': int,
150
- 'debug_trace_flush': bool,
151
- 'debug_win32': bool,
152
- 'debug_input': bool,
153
- 'debug_x11': bool,
154
- 'shadow_window': bool,
155
- 'vsync': bool,
156
- 'search_local_libs': bool,
157
- 'win32_gdi_font': bool,
158
- 'scale_with_dpi': bool,
159
- 'headless': bool,
160
- 'headless_device': int,
161
- 'win32_disable_shaping': bool,
162
- 'dw_legacy_naming': bool,
163
- 'win32_disable_xinput': bool,
164
- 'com_mta': bool,
165
- 'osx_alt_loop': bool,
46
+ _OPTION_TYPE_VALIDATORS = {
47
+ "bool": lambda x: isinstance(x, bool),
48
+ "int": lambda x: isinstance(x, int),
166
49
  }
167
50
 
51
+ @dataclass
52
+ class Options:
53
+ """Dataclass for global pyglet options."""
54
+
55
+ audio: Sequence[str] = ("xaudio2", "directsound", "openal", "pulse", "silent")
56
+ """A :py:class:`~typing.Sequence` of valid audio modules names. They will
57
+ be tried from first to last until either a driver loads or no entries
58
+ remain. See :ref:`guide-audio-driver-order` for more information.
59
+
60
+ Valid driver names are:
61
+
62
+ * ``'xaudio2'``, the Windows Xaudio2 audio module (Windows only)
63
+ * ``'directsound'``, the Windows DirectSound audio module (Windows only)
64
+ * ``'pulse'``, the :ref:`guide-audio-driver-pulseaudio` module
65
+ (Linux only, otherwise nearly ubiquitous. Limited features; use
66
+ ``'openal'`` for more.)
67
+ * ``'openal'``, the :ref:`guide-audio-driver-openal` audio module
68
+ (A library may need to be installed on Windows and Linux)
69
+ * ``'silent'``, no audio"""
70
+
71
+ debug_font: bool = False
72
+ """If ``True``, will print more verbose information when :py:class:`~pyglet.font.base.Font`'s are loaded."""
73
+
74
+ debug_gl: bool = True
75
+ """If ``True``, all calls to OpenGL functions are checked afterwards for
76
+ errors using ``glGetError``. This will severely impact performance,
77
+ but provides useful exceptions at the point of failure. By default,
78
+ this option is enabled if ``__debug__`` is enabled (i.e., if Python was not run
79
+ with the -O option). It is disabled by default when pyglet is "frozen", such as
80
+ within pyinstaller or nuitka."""
81
+
82
+ debug_gl_trace: bool = False
83
+ """If ``True``, will print the names of OpenGL calls being executed. For example, ``glBlendFunc``"""
84
+
85
+ debug_gl_trace_args: bool = False
86
+ """If ``True``, in addition to printing the names of OpenGL calls, it will also print the arguments passed
87
+ into those calls. For example, ``glBlendFunc(770, 771)``
88
+
89
+ .. note:: Requires ``debug_gl_trace`` to be enabled."""
90
+
91
+ debug_gl_shaders: bool = False
92
+ """If ``True``, prints shader compilation information such as creation and deletion of shader's. Also includes
93
+ information on shader ID's, attributes, and uniforms."""
94
+
95
+ debug_graphics_batch: bool = False
96
+ """If ``True``, prints batch information being drawn, including :py:class:`~pyglet.graphics.Group`'s, VertexDomains,
97
+ and :py:class:`~pyglet.image.Texture` information. This can be useful to see how many Group's are being
98
+ consolidated."""
99
+
100
+ debug_lib: bool = False
101
+ """If ``True``, prints the path of each dynamic library loaded."""
102
+
103
+ debug_media: bool = False
104
+ """If ``True``, prints more detailed media information for audio codecs and drivers. Will be very verbose."""
105
+
106
+ debug_texture: bool = False
107
+ """If ``True``, prints information on :py:class:`~pyglet.image.Texture` size (in bytes) when they are allocated and
108
+ deleted."""
109
+
110
+ debug_trace: bool = False
111
+ debug_trace_args: bool = False
112
+ debug_trace_depth: int = 1
113
+ debug_trace_flush: bool = True
114
+
115
+ debug_win32: bool = False
116
+ """If ``True``, prints error messages related to Windows library calls. Usually get's information from
117
+ ``Kernel32.GetLastError``. This information is output to a file called ``debug_win32.log``."""
118
+
119
+ debug_input: bool = False
120
+ """If ``True``, prints information on input devices such as controllers, tablets, and more."""
121
+
122
+ debug_x11: bool = False
123
+ """If ``True``, prints information related to Linux X11 calls. This can potentially help narrow down driver or
124
+ operating system issues."""
125
+
126
+ shadow_window: bool = True
127
+ """By default, pyglet creates a hidden window with a GL context when
128
+ pyglet.gl is imported. This allows resources to be loaded before
129
+ the application window is created, and permits GL objects to be
130
+ shared between windows even after they've been closed. You can
131
+ disable the creation of the shadow window by setting this option to
132
+ False.
133
+
134
+ Some OpenGL driver implementations may not support shared OpenGL
135
+ contexts and may require disabling the shadow window (and all resources
136
+ must be loaded after the window using them was created). Recommended
137
+ for advanced developers only.
138
+
139
+ .. versionadded:: 1.1
140
+ """
141
+
142
+ vsync: bool | None = None
143
+ """If set, the `pyglet.window.Window.vsync` property is ignored, and
144
+ this option overrides it (to either force vsync on or off). If unset,
145
+ or set to None, the `pyglet.window.Window.vsync` property behaves
146
+ as documented.
147
+ """
148
+
149
+ xsync: bool = True
150
+ """If ``True`` (the default), pyglet will attempt to synchronise the
151
+ drawing of double-buffered windows to the border updates of the X11
152
+ window manager. This improves the appearance of the window during
153
+ resize operations. This option only affects double-buffered windows on
154
+ X11 servers supporting the Xsync extension with a window manager that
155
+ implements the _NET_WM_SYNC_REQUEST protocol.
156
+
157
+ .. versionadded:: 1.1
158
+ """
159
+
160
+ xlib_fullscreen_override_redirect: bool = False
161
+ """If ``True``, pass the xlib.CWOverrideRedirect flag when creating a fullscreen window.
162
+ This option is generally not necessary anymore and is considered deprecated.
163
+ """
164
+
165
+ search_local_libs: bool = True
166
+ """If ``False``, pyglet won't try to search for libraries in the script
167
+ directory and its ``lib`` subdirectory. This is useful to load a local
168
+ library instead of the system installed version."""
169
+
170
+ win32_gdi_font: bool = False
171
+ """If ``True``, pyglet will fallback to the legacy ``GDIPlus`` font renderer for Windows. This may provide
172
+ better font compatibility for older fonts. The legacy renderer does not support shaping, colored fonts,
173
+ substitutions, or other OpenType features. It may also have issues with certain languages.
174
+
175
+ Due to those lack of features, it can potentially be more performant.
176
+
177
+ .. versionadded:: 2.0
178
+ """
179
+
180
+ headless: bool = False
181
+ """If ``True``, visible Windows are not created and a running desktop environment is not required. This option
182
+ is useful when running pyglet on a headless server, or compute cluster. OpenGL drivers with ``EGL`` support are
183
+ required for this mode.
184
+ """
185
+
186
+ headless_device: int = 0
187
+ """If using ``headless`` mode (``pyglet.options['headless'] = True``), this option allows you to set which
188
+ GPU to use. This is only useful on multi-GPU systems.
189
+ """
190
+
191
+ win32_disable_shaping: bool = False
192
+ """If ``True``, will disable the shaping process for the default Windows font renderer to offer a performance
193
+ speed up. If your font is simple, monospaced, or you require no advanced OpenType features, this option may be
194
+ useful. You can try enabling this to see if there is any impact on clarity for your font. The advance will be
195
+ determined by the glyph width.
196
+
197
+ .. note:: Shaping is the process of determining which character glyphs to use and specific placements of those
198
+ glyphs when given a full string of characters.
199
+
200
+ .. versionadded:: 2.0
201
+ """
202
+
203
+ dw_legacy_naming: bool = False
204
+ """If ``True``, will enable legacy naming support for the default Windows font renderer (``DirectWrite``).
205
+ Attempt to parse fonts by the passed name, to best match legacy RBIZ naming.
206
+
207
+ :see: https://learn.microsoft.com/en-us/windows/win32/directwrite/font-selection#rbiz-font-family-model
208
+
209
+ For example, this allows specifying ``"Arial Narrow"`` rather than ``"Arial"`` with a ``"condensed"`` stretch or
210
+ ``"Arial Black"`` instead of ``"Arial"`` with a weight of ``black``. This may enhance naming compatibility
211
+ cross-platform for select fonts as older font renderers went by this naming scheme.
212
+
213
+ Starts by parsing the string for any known style names, and searches all font collections for a matching RBIZ name.
214
+ If a perfect match is not found, it will choose a second best match.
215
+
216
+ .. note:: Due to the high variation of styles and limited capability of some fonts, there is no guarantee the
217
+ second closest match will be exactly what the user wants.
218
+
219
+ .. note:: The ``debug_font`` option can provide information on what settings are being selected.
220
+
221
+ .. versionadded:: 2.0.3
222
+ """
223
+
224
+ win32_disable_xinput: bool = False
225
+ """If ``True``, this will disable the ``XInput`` controller usage in Windows and fallback to ``DirectInput``. Can
226
+ be useful for debugging or special uses cases. A controller can only be controlled by either ``XInput`` or
227
+ ``DirectInput``, not both.
228
+
229
+ .. versionadded:: 2.0
230
+ """
231
+
232
+ com_mta: bool = False
233
+ """If ``True``, this will enforce COM Multithreaded Apartment Mode for Windows applications. By default, pyglet
234
+ has opted to go for Single-Threaded Apartment (STA) for compatibility reasons. Many other third party libraries
235
+ used with Python explicitly set STA. However, Windows recommends MTA with a lot of their API's such as Windows
236
+ Media Foundation (WMF).
237
+
238
+ :see: https://learn.microsoft.com/en-us/windows/win32/cossdk/com--threading-models
239
+
240
+ .. versionadded:: 2.0.5
241
+ """
168
242
 
169
- for _key in options:
243
+ osx_alt_loop: bool = False
244
+ """If ``True``, this will enable an alternative loop for Mac OSX. This is enforced for all ARM64 architecture Mac's.
245
+
246
+ Due to various issues with the ctypes interface with Objective C, Python, and Mac ARM64 processors, the standard
247
+ event loop eventually starts breaking down to where inputs are either missed or delayed. Even on Intel based Mac's
248
+ other odd behavior can be seen with the standard event loop such as randomly crashing from events.
249
+
250
+ .. versionadded:: 2.0.5"""
251
+
252
+ scale_with_dpi: bool = False
253
+ """For 'HiDPI' ('Retina') displays, scale Window creation size with desktop scaling. Defaults to ``False``.
254
+
255
+ For high pixel density displays, it is common for the desktop to have some form of application window scaling.
256
+ Setting this option to ``True`` will make pyglet aware of these settings when Windows are created. For instance,
257
+ if the desktop scaling factor is set to 150%, a Window created with a resolution of 1000x1000 will have an actual
258
+ framebuffer size of 1500x1500. Keep in mind that pyglet objects may not be scaled proportionately, so this is left
259
+ up to the developer. The :py:attr:`~pyglet.window.Window.scale` & :py:attr:`~pyglet.window.Window.dpi` attributes
260
+ can be queried as a reference when determining object creation. By default, windows are created with the actual
261
+ number of pixels requested.
262
+ """
263
+
264
+ shader_bind_management: bool = True
265
+ """If ``True``, this will enable internal management of Uniform Block bindings for
266
+ :py:class:`~pyglet.graphics.shader.ShaderProgram`'s.
267
+
268
+ If ``False``, bindings will not be managed by Pyglet. The user will be responsible for either setting the binding
269
+ points through GLSL layouts (4.2 required) or manually through ``UniformBlock.set_binding``.
270
+
271
+ .. versionadded:: 2.0.16
272
+ """
273
+
274
+ def get(self, item: str, default: Any = None) -> Any:
275
+ return self.__dict__.get(item, default)
276
+
277
+ def items(self) -> ItemsView[str, Any]:
278
+ return self.__dict__.items()
279
+
280
+ def __getitem__(self, item: str) -> Any:
281
+ return self.__dict__[item]
282
+
283
+ def __setitem__(self, key: str, value: Any) -> None:
284
+ assert key in self.__annotations__, f"Invalid option name: '{key}'"
285
+ assert (_SPECIAL_OPTION_VALIDATORS.get(key, None) or _OPTION_TYPE_VALIDATORS[self.__annotations__[key]])(value), \
286
+ f"Invalid type: '{type(value)}' for '{key}'"
287
+ self.__dict__[key] = value
288
+
289
+
290
+ #: Instance of :py:class:`~pyglet.Options` used to set runtime options.
291
+ options: Options = Options()
292
+
293
+
294
+ for _key, _type in options.__annotations__.items():
170
295
  """Check Environment Variables for pyglet options"""
171
- assert _key in _option_types, f"Option '{_key}' must have a type set in _option_types."
172
-
173
- if _value := os.environ.get(f'PYGLET_{_key.upper()}'):
174
- if _option_types[_key] is tuple:
175
- options[_key] = _value.split(',')
176
- elif _option_types[_key] is bool:
177
- options[_key] = _value in ('true', 'TRUE', 'True', '1')
178
- elif _option_types[_key] is int:
296
+ if _value := os.environ.get(f"PYGLET_{_key.upper()}"):
297
+ if _type == 'tuple':
298
+ options[_key] = _value.split(",")
299
+ elif _type == 'bool':
300
+ options[_key] = _value in ("true", "TRUE", "True", "1")
301
+ elif _type == 'int':
179
302
  options[_key] = int(_value)
180
303
 
181
304
 
182
- if compat_platform == 'cygwin':
305
+ if compat_platform == "cygwin":
183
306
  # This hack pretends that the posix-like ctypes provides windows
184
307
  # functionality. COM does not work with this hack, so there is no
185
308
  # DirectSound support.
186
309
  import ctypes
187
310
 
188
- ctypes.windll = ctypes.cdll # type: ignore
189
- ctypes.oledll = ctypes.cdll # type: ignore
311
+ ctypes.windll = ctypes.cdll
312
+ ctypes.oledll = ctypes.cdll
190
313
  ctypes.WINFUNCTYPE = ctypes.CFUNCTYPE
191
- ctypes.HRESULT = ctypes.c_long # type: ignore
314
+ ctypes.HRESULT = ctypes.c_long
192
315
 
193
316
  # Call tracing
194
317
  # ------------
195
318
 
196
- _trace_filename_abbreviations: Dict[str, str] = {}
319
+ _trace_filename_abbreviations: dict[str, str] = {}
197
320
  _trace_thread_count = 0
198
- _trace_args = options['debug_trace_args']
199
- _trace_depth = options['debug_trace_depth']
200
- _trace_flush = options['debug_trace_flush']
321
+ _trace_args = options["debug_trace_args"]
322
+ _trace_depth = options["debug_trace_depth"]
323
+ _trace_flush = options["debug_trace_flush"]
201
324
 
202
325
 
203
- def _trace_repr(value, size=40):
326
+ def _trace_repr(value: Sized, size: int=40) -> str:
204
327
  value = repr(value)
205
328
  if len(value) > size:
206
- value = value[:size // 2 - 2] + '...' + value[-size // 2 - 1:]
329
+ value = value[:size // 2 - 2] + "..." + value[-size // 2 - 1:]
207
330
  return value
208
331
 
209
332
 
210
- def _trace_frame(thread, frame, indent):
211
-
212
- if frame.f_code is lib._TraceFunction.__call__.__code__:
333
+ def _trace_frame(thread: int, frame: FrameType, indent: str) -> None:
334
+ if frame.f_code is lib._TraceFunction.__call__.__code__: # noqa: SLF001
213
335
  is_ctypes = True
214
- func = frame.f_locals['self']._func
336
+ func = frame.f_locals["self"]._func # noqa: SLF001
215
337
  name = func.__name__
216
- location = '[ctypes]'
338
+ location = "[ctypes]"
217
339
  else:
218
340
  is_ctypes = False
219
341
  code = frame.f_code
@@ -225,7 +347,7 @@ def _trace_frame(thread, frame, indent):
225
347
  filename = _trace_filename_abbreviations[path]
226
348
  except KeyError:
227
349
  # Trim path down
228
- directory = ''
350
+ directory = ""
229
351
  path, filename = os.path.split(path)
230
352
 
231
353
  while len(directory + filename) < 30:
@@ -234,51 +356,51 @@ def _trace_frame(thread, frame, indent):
234
356
  if not directory:
235
357
  break
236
358
  else:
237
- filename = os.path.join('...', filename)
359
+ filename = os.path.join("...", filename)
238
360
  _trace_filename_abbreviations[path] = filename
239
361
 
240
- location = f'({filename}:{line})'
362
+ location = f"({filename}:{line})"
241
363
 
242
364
  if indent:
243
- name = f'Called from {name}'
244
- print(f'[{thread}] {indent}{name} {location}')
365
+ name = f"Called from {name}"
366
+ print(f"[{thread}] {indent}{name} {location}")
245
367
 
246
368
  if _trace_args:
247
369
  if is_ctypes:
248
- args = [_trace_repr(arg) for arg in frame.f_locals['args']]
370
+ args = [_trace_repr(arg) for arg in frame.f_locals["args"]]
249
371
  print(f' {indent}args=({", ".join(args)})')
250
372
  else:
251
373
  for argname in code.co_varnames[:code.co_argcount]:
252
374
  try:
253
375
  argvalue = _trace_repr(frame.f_locals[argname])
254
- print(f' {indent}{argname}={argvalue}')
255
- except:
376
+ print(f" {indent}{argname}={argvalue}")
377
+ except: # noqa: S110, E722, PERF203
256
378
  pass
257
379
 
258
380
  if _trace_flush:
259
381
  sys.stdout.flush()
260
382
 
261
383
 
262
- def _thread_trace_func(thread):
263
- def _trace_func(frame, event, arg):
264
- if event == 'call':
265
- indent = ''
266
- for i in range(_trace_depth):
384
+ def _thread_trace_func(thread: int) -> Callable[[FrameType, str, Any], object]:
385
+ def _trace_func(frame: FrameType, event: str, arg: Any) -> None:
386
+ if event == "call":
387
+ indent = ""
388
+ for _ in range(_trace_depth):
267
389
  _trace_frame(thread, frame, indent)
268
- indent += ' '
269
- frame = frame.f_back
270
- if not frame:
390
+ indent += " "
391
+ if frame.f_back is None:
271
392
  break
393
+ frame = frame.f_back
272
394
 
273
- elif event == 'exception':
395
+ elif event == "exception":
274
396
  (exception, value, traceback) = arg
275
- print('First chance exception raised:', repr(exception))
397
+ print("First chance exception raised:", repr(exception))
276
398
 
277
399
  return _trace_func
278
400
 
279
401
 
280
- def _install_trace():
281
- global _trace_thread_count
402
+ def _install_trace() -> None:
403
+ global _trace_thread_count # noqa: PLW0603
282
404
  sys.setprofile(_thread_trace_func(_trace_thread_count))
283
405
  _trace_thread_count += 1
284
406
 
@@ -289,82 +411,84 @@ def _install_trace():
289
411
  class _ModuleProxy:
290
412
  _module = None
291
413
 
292
- def __init__(self, name: str):
293
- self.__dict__['_module_name'] = name
414
+ def __init__(self, name: str) -> None:
415
+ self.__dict__["_module_name"] = name
294
416
 
295
- def __getattr__(self, name):
417
+ def __getattr__(self, name: str): # noqa: ANN204
296
418
  try:
297
419
  return getattr(self._module, name)
298
420
  except AttributeError:
299
421
  if self._module is not None:
300
422
  raise
301
423
 
302
- import_name = f'pyglet.{self._module_name}'
424
+ import_name = f"pyglet.{self._module_name}"
303
425
  __import__(import_name)
304
426
  module = sys.modules[import_name]
305
- object.__setattr__(self, '_module', module)
427
+ object.__setattr__(self, "_module", module)
306
428
  globals()[self._module_name] = module
307
429
  return getattr(module, name)
308
430
 
309
- def __setattr__(self, name, value):
431
+ def __setattr__(self, name: str, value: Any): # noqa: ANN204
310
432
  try:
311
433
  setattr(self._module, name, value)
312
434
  except AttributeError:
313
435
  if self._module is not None:
314
436
  raise
315
437
 
316
- import_name = f'pyglet.{self._module_name}'
438
+ import_name = f"pyglet.{self._module_name}"
317
439
  __import__(import_name)
318
440
  module = sys.modules[import_name]
319
- object.__setattr__(self, '_module', module)
441
+ object.__setattr__(self, "_module", module)
320
442
  globals()[self._module_name] = module
321
443
  setattr(module, name, value)
322
444
 
323
445
 
324
446
  # Lazily load all modules, except if performing type checking or code inspection.
325
447
  if TYPE_CHECKING:
326
- from . import app
327
- from . import clock
328
- from . import customtypes
329
- from . import display
330
- from . import event
331
- from . import font
332
- from . import gl
333
- from . import graphics
334
- from . import gui
335
- from . import image
336
- from . import input
337
- from . import lib
338
- from . import math
339
- from . import media
340
- from . import model
341
- from . import resource
342
- from . import sprite
343
- from . import shapes
344
- from . import text
345
- from . import window
448
+ from . import (
449
+ app,
450
+ clock,
451
+ customtypes,
452
+ display,
453
+ event,
454
+ font,
455
+ gl,
456
+ graphics,
457
+ gui,
458
+ image,
459
+ input,
460
+ lib,
461
+ math,
462
+ media,
463
+ model,
464
+ resource,
465
+ shapes,
466
+ sprite,
467
+ text,
468
+ window,
469
+ )
346
470
  else:
347
- app = _ModuleProxy('app') # type: ignore
348
- clock = _ModuleProxy('clock') # type: ignore
349
- customtypes = _ModuleProxy('customtypes') # type: ignore
350
- display = _ModuleProxy('display')
351
- event = _ModuleProxy('event') # type: ignore
352
- font = _ModuleProxy('font') # type: ignore
353
- gl = _ModuleProxy('gl') # type: ignore
354
- graphics = _ModuleProxy('graphics') # type: ignore
355
- gui = _ModuleProxy('gui') # type: ignore
356
- image = _ModuleProxy('image') # type: ignore
357
- input = _ModuleProxy('input') # type: ignore
358
- lib = _ModuleProxy('lib') # type: ignore
359
- math = _ModuleProxy('math') # type: ignore
360
- media = _ModuleProxy('media') # type: ignore
361
- model = _ModuleProxy('model') # type: ignore
362
- resource = _ModuleProxy('resource') # type: ignore
363
- sprite = _ModuleProxy('sprite') # type: ignore
364
- shapes = _ModuleProxy('shapes') # type: ignore
365
- text = _ModuleProxy('text') # type: ignore
366
- window = _ModuleProxy('window') # type: ignore
471
+ app = _ModuleProxy("app") # type: ignore
472
+ clock = _ModuleProxy("clock") # type: ignore
473
+ customtypes = _ModuleProxy("customtypes") # type: ignore
474
+ display = _ModuleProxy("display") # type: ignore
475
+ event = _ModuleProxy("event") # type: ignore
476
+ font = _ModuleProxy("font") # type: ignore
477
+ gl = _ModuleProxy("gl") # type: ignore
478
+ graphics = _ModuleProxy("graphics") # type: ignore
479
+ gui = _ModuleProxy("gui") # type: ignore
480
+ image = _ModuleProxy("image") # type: ignore
481
+ input = _ModuleProxy("input") # type: ignore
482
+ lib = _ModuleProxy("lib") # type: ignore
483
+ math = _ModuleProxy("math") # type: ignore
484
+ media = _ModuleProxy("media") # type: ignore
485
+ model = _ModuleProxy("model") # type: ignore
486
+ resource = _ModuleProxy("resource") # type: ignore
487
+ sprite = _ModuleProxy("sprite") # type: ignore
488
+ shapes = _ModuleProxy("shapes") # type: ignore
489
+ text = _ModuleProxy("text") # type: ignore
490
+ window = _ModuleProxy("window") # type: ignore
367
491
 
368
492
  # Call after creating proxies:
369
- if options['debug_trace']:
493
+ if options.debug_trace is True:
370
494
  _install_trace()