pyglet 2.1.13__py3-none-any.whl → 3.0.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. pyglet/__init__.py +67 -61
  2. pyglet/__init__.pyi +15 -8
  3. pyglet/app/__init__.py +22 -13
  4. pyglet/app/async_app.py +212 -0
  5. pyglet/app/base.py +2 -1
  6. pyglet/app/{xlib.py → linux.py} +3 -3
  7. pyglet/config/__init__.py +101 -0
  8. pyglet/config/gl/__init__.py +30 -0
  9. pyglet/config/gl/egl.py +120 -0
  10. pyglet/config/gl/macos.py +262 -0
  11. pyglet/config/gl/windows.py +267 -0
  12. pyglet/config/gl/x11.py +142 -0
  13. pyglet/customtypes.py +43 -2
  14. pyglet/display/__init__.py +8 -6
  15. pyglet/display/base.py +3 -63
  16. pyglet/display/cocoa.py +12 -17
  17. pyglet/display/emscripten.py +39 -0
  18. pyglet/display/headless.py +23 -30
  19. pyglet/display/wayland.py +157 -0
  20. pyglet/display/win32.py +5 -21
  21. pyglet/display/xlib.py +19 -27
  22. pyglet/display/xlib_vidmoderestore.py +2 -2
  23. pyglet/enums.py +183 -0
  24. pyglet/event.py +0 -1
  25. pyglet/experimental/geoshader_sprite.py +15 -13
  26. pyglet/experimental/hidraw.py +6 -15
  27. pyglet/experimental/multitexture_sprite.py +31 -19
  28. pyglet/experimental/particles.py +13 -35
  29. pyglet/font/__init__.py +251 -85
  30. pyglet/font/base.py +116 -61
  31. pyglet/font/dwrite/__init__.py +349 -204
  32. pyglet/font/dwrite/dwrite_lib.py +27 -5
  33. pyglet/font/fontconfig.py +14 -6
  34. pyglet/font/freetype.py +138 -87
  35. pyglet/font/freetype_lib.py +19 -0
  36. pyglet/font/group.py +179 -0
  37. pyglet/font/harfbuzz/__init__.py +3 -3
  38. pyglet/font/pyodide_js.py +310 -0
  39. pyglet/font/quartz.py +319 -126
  40. pyglet/font/ttf.py +45 -3
  41. pyglet/font/user.py +14 -19
  42. pyglet/font/win32.py +45 -21
  43. pyglet/graphics/__init__.py +8 -787
  44. pyglet/graphics/allocation.py +115 -1
  45. pyglet/graphics/api/__init__.py +77 -0
  46. pyglet/graphics/api/base.py +299 -0
  47. pyglet/graphics/api/gl/__init__.py +58 -0
  48. pyglet/graphics/api/gl/base.py +24 -0
  49. pyglet/graphics/{vertexbuffer.py → api/gl/buffer.py} +104 -159
  50. pyglet/graphics/api/gl/cocoa/context.py +76 -0
  51. pyglet/graphics/api/gl/context.py +391 -0
  52. pyglet/graphics/api/gl/default_shaders.py +0 -0
  53. pyglet/graphics/api/gl/draw.py +627 -0
  54. pyglet/graphics/api/gl/egl/__init__.py +0 -0
  55. pyglet/graphics/api/gl/egl/context.py +92 -0
  56. pyglet/graphics/api/gl/enums.py +76 -0
  57. pyglet/graphics/api/gl/framebuffer.py +315 -0
  58. pyglet/graphics/api/gl/gl.py +5463 -0
  59. pyglet/graphics/api/gl/gl_info.py +188 -0
  60. pyglet/graphics/api/gl/global_opengl.py +226 -0
  61. pyglet/{gl → graphics/api/gl}/lib.py +34 -18
  62. pyglet/graphics/api/gl/shader.py +1476 -0
  63. pyglet/graphics/api/gl/shapes.py +55 -0
  64. pyglet/graphics/api/gl/sprite.py +102 -0
  65. pyglet/graphics/api/gl/state.py +219 -0
  66. pyglet/graphics/api/gl/text.py +190 -0
  67. pyglet/graphics/api/gl/texture.py +1526 -0
  68. pyglet/graphics/{vertexarray.py → api/gl/vertexarray.py} +11 -13
  69. pyglet/graphics/api/gl/vertexdomain.py +751 -0
  70. pyglet/graphics/api/gl/win32/__init__.py +0 -0
  71. pyglet/graphics/api/gl/win32/context.py +108 -0
  72. pyglet/graphics/api/gl/win32/wgl_info.py +24 -0
  73. pyglet/graphics/api/gl/xlib/__init__.py +0 -0
  74. pyglet/graphics/api/gl/xlib/context.py +174 -0
  75. pyglet/{gl → graphics/api/gl/xlib}/glx_info.py +26 -31
  76. pyglet/graphics/api/gl1/__init__.py +0 -0
  77. pyglet/{gl → graphics/api/gl1}/gl_compat.py +3 -2
  78. pyglet/graphics/api/gl2/__init__.py +0 -0
  79. pyglet/graphics/api/gl2/buffer.py +320 -0
  80. pyglet/graphics/api/gl2/draw.py +600 -0
  81. pyglet/graphics/api/gl2/global_opengl.py +122 -0
  82. pyglet/graphics/api/gl2/shader.py +200 -0
  83. pyglet/graphics/api/gl2/shapes.py +51 -0
  84. pyglet/graphics/api/gl2/sprite.py +79 -0
  85. pyglet/graphics/api/gl2/text.py +175 -0
  86. pyglet/graphics/api/gl2/vertexdomain.py +364 -0
  87. pyglet/graphics/api/webgl/__init__.py +233 -0
  88. pyglet/graphics/api/webgl/buffer.py +302 -0
  89. pyglet/graphics/api/webgl/context.py +234 -0
  90. pyglet/graphics/api/webgl/draw.py +590 -0
  91. pyglet/graphics/api/webgl/enums.py +76 -0
  92. pyglet/graphics/api/webgl/framebuffer.py +360 -0
  93. pyglet/graphics/api/webgl/gl.py +1537 -0
  94. pyglet/graphics/api/webgl/gl_info.py +130 -0
  95. pyglet/graphics/api/webgl/shader.py +1346 -0
  96. pyglet/graphics/api/webgl/shapes.py +92 -0
  97. pyglet/graphics/api/webgl/sprite.py +102 -0
  98. pyglet/graphics/api/webgl/state.py +227 -0
  99. pyglet/graphics/api/webgl/text.py +187 -0
  100. pyglet/graphics/api/webgl/texture.py +1227 -0
  101. pyglet/graphics/api/webgl/vertexarray.py +54 -0
  102. pyglet/graphics/api/webgl/vertexdomain.py +616 -0
  103. pyglet/graphics/api/webgl/webgl_js.pyi +307 -0
  104. pyglet/{image → graphics}/atlas.py +33 -32
  105. pyglet/graphics/base.py +10 -0
  106. pyglet/graphics/buffer.py +245 -0
  107. pyglet/graphics/draw.py +578 -0
  108. pyglet/graphics/framebuffer.py +26 -0
  109. pyglet/graphics/instance.py +178 -69
  110. pyglet/graphics/shader.py +267 -1553
  111. pyglet/graphics/state.py +83 -0
  112. pyglet/graphics/texture.py +703 -0
  113. pyglet/graphics/vertexdomain.py +695 -538
  114. pyglet/gui/ninepatch.py +10 -10
  115. pyglet/gui/widgets.py +120 -10
  116. pyglet/image/__init__.py +20 -1973
  117. pyglet/image/animation.py +12 -12
  118. pyglet/image/base.py +730 -0
  119. pyglet/image/codecs/__init__.py +9 -0
  120. pyglet/image/codecs/bmp.py +53 -30
  121. pyglet/image/codecs/dds.py +53 -31
  122. pyglet/image/codecs/gdiplus.py +38 -14
  123. pyglet/image/codecs/gdkpixbuf2.py +0 -2
  124. pyglet/image/codecs/js_image.py +99 -0
  125. pyglet/image/codecs/ktx2.py +161 -0
  126. pyglet/image/codecs/pil.py +1 -1
  127. pyglet/image/codecs/png.py +1 -1
  128. pyglet/image/codecs/wic.py +11 -2
  129. pyglet/info.py +26 -24
  130. pyglet/input/__init__.py +8 -0
  131. pyglet/input/base.py +163 -105
  132. pyglet/input/controller.py +13 -19
  133. pyglet/input/controller_db.py +39 -24
  134. pyglet/input/emscripten/__init__.py +18 -0
  135. pyglet/input/emscripten/gamepad_js.py +397 -0
  136. pyglet/input/linux/__init__.py +11 -5
  137. pyglet/input/linux/evdev.py +10 -11
  138. pyglet/input/linux/x11_xinput.py +2 -2
  139. pyglet/input/linux/x11_xinput_tablet.py +1 -1
  140. pyglet/input/macos/__init__.py +7 -2
  141. pyglet/input/macos/darwin_gc.py +559 -0
  142. pyglet/input/win32/__init__.py +1 -1
  143. pyglet/input/win32/directinput.py +34 -29
  144. pyglet/input/win32/xinput.py +11 -61
  145. pyglet/lib.py +3 -3
  146. pyglet/libs/__init__.py +1 -1
  147. pyglet/{gl → libs/darwin}/agl.py +1 -1
  148. pyglet/libs/darwin/cocoapy/__init__.py +2 -2
  149. pyglet/libs/darwin/cocoapy/cocoahelpers.py +181 -0
  150. pyglet/libs/darwin/cocoapy/cocoalibs.py +31 -0
  151. pyglet/libs/darwin/cocoapy/cocoatypes.py +27 -0
  152. pyglet/libs/darwin/cocoapy/runtime.py +81 -45
  153. pyglet/libs/darwin/coreaudio.py +4 -4
  154. pyglet/{gl → libs/darwin}/lib_agl.py +9 -8
  155. pyglet/libs/darwin/quartzkey.py +1 -3
  156. pyglet/libs/egl/__init__.py +2 -0
  157. pyglet/libs/egl/egl_lib.py +576 -0
  158. pyglet/libs/egl/eglext.py +51 -5
  159. pyglet/libs/linux/__init__.py +0 -0
  160. pyglet/libs/linux/egl/__init__.py +0 -0
  161. pyglet/libs/linux/egl/eglext.py +22 -0
  162. pyglet/libs/linux/glx/__init__.py +0 -0
  163. pyglet/{gl → libs/linux/glx}/glx.py +13 -14
  164. pyglet/{gl → libs/linux/glx}/glxext_arb.py +408 -192
  165. pyglet/{gl → libs/linux/glx}/glxext_mesa.py +1 -1
  166. pyglet/{gl → libs/linux/glx}/glxext_nv.py +345 -164
  167. pyglet/{gl → libs/linux/glx}/lib_glx.py +3 -2
  168. pyglet/libs/linux/wayland/__init__.py +0 -0
  169. pyglet/libs/linux/wayland/client.py +1068 -0
  170. pyglet/libs/linux/wayland/lib_wayland.py +207 -0
  171. pyglet/libs/linux/wayland/wayland_egl.py +38 -0
  172. pyglet/libs/{wayland → linux/wayland}/xkbcommon.py +26 -0
  173. pyglet/libs/{x11 → linux/x11}/xf86vmode.py +4 -4
  174. pyglet/libs/{x11 → linux/x11}/xinerama.py +2 -2
  175. pyglet/libs/{x11 → linux/x11}/xinput.py +10 -10
  176. pyglet/libs/linux/x11/xrandr.py +0 -0
  177. pyglet/libs/{x11 → linux/x11}/xrender.py +1 -1
  178. pyglet/libs/shared/__init__.py +0 -0
  179. pyglet/libs/shared/spirv/__init__.py +0 -0
  180. pyglet/libs/shared/spirv/lib_shaderc.py +85 -0
  181. pyglet/libs/shared/spirv/lib_spirv_cross.py +126 -0
  182. pyglet/libs/win32/__init__.py +28 -8
  183. pyglet/libs/win32/constants.py +59 -48
  184. pyglet/libs/win32/context_managers.py +20 -3
  185. pyglet/libs/win32/dinput.py +105 -88
  186. pyglet/{gl → libs/win32}/lib_wgl.py +52 -26
  187. pyglet/libs/win32/types.py +58 -23
  188. pyglet/{gl → libs/win32}/wgl.py +32 -25
  189. pyglet/{gl → libs/win32}/wglext_arb.py +364 -2
  190. pyglet/media/__init__.py +9 -10
  191. pyglet/media/codecs/__init__.py +12 -1
  192. pyglet/media/codecs/base.py +99 -96
  193. pyglet/media/codecs/ffmpeg.py +2 -2
  194. pyglet/media/codecs/ffmpeg_lib/libavformat.py +3 -8
  195. pyglet/media/codecs/webaudio_pyodide.py +111 -0
  196. pyglet/media/drivers/__init__.py +9 -4
  197. pyglet/media/drivers/base.py +4 -4
  198. pyglet/media/drivers/openal/__init__.py +1 -1
  199. pyglet/media/drivers/openal/adaptation.py +3 -3
  200. pyglet/media/drivers/pulse/__init__.py +1 -1
  201. pyglet/media/drivers/pulse/adaptation.py +3 -3
  202. pyglet/media/drivers/pyodide_js/__init__.py +8 -0
  203. pyglet/media/drivers/pyodide_js/adaptation.py +288 -0
  204. pyglet/media/drivers/xaudio2/adaptation.py +3 -3
  205. pyglet/media/player.py +276 -193
  206. pyglet/media/player_worker_thread.py +1 -1
  207. pyglet/model/__init__.py +39 -29
  208. pyglet/model/codecs/base.py +4 -4
  209. pyglet/model/codecs/gltf.py +3 -3
  210. pyglet/model/codecs/obj.py +71 -43
  211. pyglet/resource.py +129 -78
  212. pyglet/shapes.py +154 -194
  213. pyglet/sprite.py +47 -164
  214. pyglet/text/__init__.py +44 -54
  215. pyglet/text/caret.py +12 -7
  216. pyglet/text/document.py +19 -17
  217. pyglet/text/formats/html.py +2 -2
  218. pyglet/text/formats/structured.py +10 -40
  219. pyglet/text/layout/__init__.py +20 -13
  220. pyglet/text/layout/base.py +176 -287
  221. pyglet/text/layout/incremental.py +9 -10
  222. pyglet/text/layout/scrolling.py +7 -95
  223. pyglet/window/__init__.py +183 -172
  224. pyglet/window/cocoa/__init__.py +62 -51
  225. pyglet/window/cocoa/pyglet_delegate.py +2 -25
  226. pyglet/window/cocoa/pyglet_view.py +9 -8
  227. pyglet/window/dialog/__init__.py +184 -0
  228. pyglet/window/dialog/base.py +99 -0
  229. pyglet/window/dialog/darwin.py +121 -0
  230. pyglet/window/dialog/linux.py +72 -0
  231. pyglet/window/dialog/windows.py +194 -0
  232. pyglet/window/emscripten/__init__.py +779 -0
  233. pyglet/window/headless/__init__.py +44 -28
  234. pyglet/window/key.py +2 -0
  235. pyglet/window/mouse.py +2 -2
  236. pyglet/window/wayland/__init__.py +377 -0
  237. pyglet/window/win32/__init__.py +101 -46
  238. pyglet/window/xlib/__init__.py +104 -66
  239. {pyglet-2.1.13.dist-info → pyglet-3.0.dev1.dist-info}/METADATA +2 -3
  240. pyglet-3.0.dev1.dist-info/RECORD +322 -0
  241. {pyglet-2.1.13.dist-info → pyglet-3.0.dev1.dist-info}/WHEEL +1 -1
  242. pyglet/gl/__init__.py +0 -208
  243. pyglet/gl/base.py +0 -499
  244. pyglet/gl/cocoa.py +0 -309
  245. pyglet/gl/gl.py +0 -4625
  246. pyglet/gl/gl.pyi +0 -2320
  247. pyglet/gl/gl_compat.pyi +0 -3097
  248. pyglet/gl/gl_info.py +0 -190
  249. pyglet/gl/headless.py +0 -166
  250. pyglet/gl/wgl_info.py +0 -36
  251. pyglet/gl/wglext_nv.py +0 -1096
  252. pyglet/gl/win32.py +0 -268
  253. pyglet/gl/xlib.py +0 -295
  254. pyglet/image/buffer.py +0 -274
  255. pyglet/image/codecs/s3tc.py +0 -354
  256. pyglet/libs/x11/xrandr.py +0 -166
  257. pyglet-2.1.13.dist-info/RECORD +0 -234
  258. /pyglet/{libs/wayland → graphics/api/gl/cocoa}/__init__.py +0 -0
  259. /pyglet/libs/{egl → linux/egl}/egl.py +0 -0
  260. /pyglet/libs/{egl → linux/egl}/lib.py +0 -0
  261. /pyglet/libs/{ioctl.py → linux/ioctl.py} +0 -0
  262. /pyglet/libs/{wayland → linux/wayland}/gbm.py +0 -0
  263. /pyglet/libs/{x11 → linux/x11}/__init__.py +0 -0
  264. /pyglet/libs/{x11 → linux/x11}/cursorfont.py +0 -0
  265. /pyglet/libs/{x11 → linux/x11}/xlib.py +0 -0
  266. /pyglet/libs/{x11 → linux/x11}/xsync.py +0 -0
  267. {pyglet-2.1.13.dist-info/licenses → pyglet-3.0.dev1.dist-info}/LICENSE +0 -0
pyglet/__init__.py CHANGED
@@ -7,15 +7,16 @@ from __future__ import annotations
7
7
  import os
8
8
  import sys
9
9
  import warnings
10
- from collections.abc import Sequence
11
- from dataclasses import dataclass
12
- from typing import TYPE_CHECKING, Literal
10
+
11
+ from dataclasses import dataclass, field
12
+ from typing import TYPE_CHECKING, Literal, Sequence
13
+
13
14
  if TYPE_CHECKING:
14
15
  from types import FrameType
15
16
  from typing import Any, Callable, ItemsView, Sized
16
17
 
17
18
  #: The release version
18
- version = '2.1.13'
19
+ version = '3.0.dev1'
19
20
  __version__ = version
20
21
 
21
22
  MIN_PYTHON_VERSION = 3, 8
@@ -42,6 +43,18 @@ if compat_platform == "cygwin":
42
43
  ctypes.HRESULT = ctypes.c_long
43
44
 
44
45
 
46
+ @dataclass
47
+ class PyodideOptions:
48
+ """Dataclass for Pyodide related options."""
49
+
50
+ canvas_id: str = "pygletCanvas"
51
+ """Pyglet will need to target a specific canvas ID to use for javascript canvas detection.
52
+
53
+ If the ID is not detected, a canvas will be created with the above. If you have a canvas already embedded in your
54
+ page, and do not want to alter your code, then modify this option.
55
+ """
56
+
57
+
45
58
  @dataclass
46
59
  class Options:
47
60
  """Dataclass for global pyglet options."""
@@ -65,7 +78,7 @@ class Options:
65
78
  debug_font: bool = False
66
79
  """If ``True``, will print more verbose information when :py:class:`~pyglet.font.base.Font`'s are loaded."""
67
80
 
68
- debug_gl: bool = True
81
+ debug_api: bool = True
69
82
  """If ``True``, all calls to OpenGL functions are checked afterwards for
70
83
  errors using ``glGetError``. This will severely impact performance,
71
84
  but provides useful exceptions at the point of failure. By default,
@@ -73,22 +86,22 @@ class Options:
73
86
  with the -O option). It is disabled by default when pyglet is "frozen", such as
74
87
  within pyinstaller or nuitka."""
75
88
 
76
- debug_gl_trace: bool = False
89
+ debug_api_trace: bool = False
77
90
  """If ``True``, will print the names of OpenGL calls being executed. For example, ``glBlendFunc``"""
78
91
 
79
- debug_gl_trace_args: bool = False
92
+ debug_api_trace_args: bool = False
80
93
  """If ``True``, in addition to printing the names of OpenGL calls, it will also print the arguments passed
81
94
  into those calls. For example, ``glBlendFunc(770, 771)``
82
95
 
83
- .. note:: Requires ``debug_gl_trace`` to be enabled."""
96
+ .. note:: Requires ``debug_api_trace`` to be enabled."""
84
97
 
85
- debug_gl_shaders: bool = False
98
+ debug_api_shaders: bool = False
86
99
  """If ``True``, prints shader compilation information such as creation and deletion of shader's. Also includes
87
100
  information on shader ID's, attributes, and uniforms."""
88
101
 
89
102
  debug_graphics_batch: bool = False
90
103
  """If ``True``, prints batch information being drawn, including :py:class:`~pyglet.graphics.Group`'s, VertexDomains,
91
- and :py:class:`~pyglet.image.Texture` information. This can be useful to see how many Group's are being
104
+ and :py:class:`~pyglet.graphics.Texture` information. This can be useful to see how many Group's are being
92
105
  consolidated."""
93
106
 
94
107
  debug_lib: bool = False
@@ -97,19 +110,11 @@ class Options:
97
110
  debug_media: bool = False
98
111
  """If ``True``, prints more detailed media information for audio codecs and drivers. Will be very verbose."""
99
112
 
100
- debug_texture: bool = False
101
- """If ``True``, prints information on :py:class:`~pyglet.image.Texture` size (in bytes) when they are allocated and
102
- deleted."""
103
-
104
113
  debug_trace: bool = False
105
114
  debug_trace_args: bool = False
106
115
  debug_trace_depth: int = 1
107
116
  debug_trace_flush: bool = True
108
117
 
109
- debug_com: bool = False
110
- """If ``True``, prints information on COM calls. This can potentially help narrow down issues with certain libraries
111
- that utilize COM calls. Only applies to the Windows platform."""
112
-
113
118
  debug_win32: bool = False
114
119
  """If ``True``, prints error messages related to Windows library calls. Usually gets information from
115
120
  ``Kernel32.GetLastError``. This information is output to a file called ``debug_win32.log``."""
@@ -121,21 +126,12 @@ class Options:
121
126
  """If ``True``, prints information related to Linux X11 calls. This can potentially help narrow down driver or
122
127
  operating system issues."""
123
128
 
124
- shadow_window: bool = True
125
- """By default, pyglet creates a hidden window with a GL context when
126
- pyglet.gl is imported. This allows resources to be loaded before
127
- the application window is created, and permits GL objects to be
128
- shared between windows even after they've been closed. You can
129
- disable the creation of the shadow window by setting this option to
130
- False.
131
-
132
- Some OpenGL driver implementations may not support shared OpenGL
133
- contexts and may require disabling the shadow window (and all resources
134
- must be loaded after the window using them was created). Recommended
135
- for advanced developers only.
129
+ debug_wayland: bool = False
130
+ """If ``True``, prints information related to communications with the Wayland compositor."""
136
131
 
137
- .. versionadded:: 1.1
138
- """
132
+ debug_com: bool = False
133
+ """If ``True``, prints information on COM calls. This can potentially help narrow down issues with certain libraries
134
+ that utilize COM calls. Only applies to the Windows platform."""
139
135
 
140
136
  vsync: bool | None = None
141
137
  """If set, the `pyglet.window.Window.vsync` property is ignored, and
@@ -187,7 +183,7 @@ class Options:
187
183
  """
188
184
 
189
185
  headless_device: int = 0
190
- """If using ``headless`` mode (``pyglet.options['headless'] = True``), this option allows you to set which
186
+ """If using ``headless`` mode (``pyglet.options.headless = True``), this option allows you to set which
191
187
  GPU to use. This is only useful on multi-GPU systems.
192
188
  """
193
189
 
@@ -255,41 +251,28 @@ class Options:
255
251
 
256
252
  .. versionadded:: 2.0.5"""
257
253
 
258
- dpi_scaling: Literal["real", "scaled", "stretch", "platform"] = "platform"
254
+ dpi_scaling: Literal["platform", "stretch"] = "platform"
259
255
  """For 'HiDPI' displays, Window behavior can differ between operating systems. Defaults to `'platform'`.
260
256
 
261
257
  The current options are an attempt to create consistent behavior across all of the operating systems.
262
258
 
263
- `'real'` (default): Provides a 1:1 pixel for Window frame size and framebuffer. Primarily used for game applications
264
- to ensure you are getting the exact pixels for the resolution. If you provide an 800x600 window, you can ensure it
265
- will be 800x600 pixels when the user chooses it.
259
+ `'platform'`: A DPI aware window is created. Framebuffer and window sizes are dictated by the platform the window
260
+ was created on. In most systems, the window size will be in DIPs (Device Independent Pixels). It is up to the user
261
+ to make any further adjustments to the framebuffer or window size for their application.
266
262
 
267
- `'scaled'`: Window size is scaled based on the DPI ratio. Window size and content (projection) size matches the full
268
- framebuffer. Primarily used for any applications that wish to become DPI aware. You must rescale and reposition your
269
- content to take advantage of the larger framebuffer. An 800x600 with a 150% DPI scaling would be changed to
270
- 1200x900 for both `window.get_size` and `window.get_framebuffer_size()`.
263
+ On Windows and X11, the framebuffer and the requested window size will always match 1:1. On MacOS, depending
264
+ on a Hi-DPI display, you may get a larger sized framebuffer than the window size.
271
265
 
272
- Keep in mind that pyglet objects may not be scaled proportionately, so this is left up to the developer.
273
- The :py:attr:`~pyglet.window.Window.scale` & :py:attr:`~pyglet.window.Window.dpi` attributes can be queried as a
274
- reference when determining object creation.
275
-
276
- `'stretch'`: Window is scaled based on the DPI ratio. However, content size matches original requested size of the
277
- window, and is stretched to fit the full framebuffer. This mimics behavior of having no DPI scaling at all. No
278
- rescaling and repositioning of content will be necessary, but at the cost of blurry content depending on the extent
279
- of the stretch. For example, 800x600 at 150% DPI will be 800x600 for `window.get_size()` and 1200x900 for
266
+ `'stretch'`: This mimics behavior of having no DPI scaling at all. Window is scaled based on the DPI ratio.
267
+ However, content size matches original requested size of the window, and is stretched to fit the full framebuffer.
268
+ No rescaling and repositioning of content will be necessary, but at the cost of blurry content depending on the
269
+ extent of the stretch. For example, 800x600 at 150% DPI will be 800x600 for `window.get_size()` and 1200x900 for
280
270
  `window.get_framebuffer_size()`.
281
-
282
- `'platform'`: A DPI aware window is created, however window sizing and framebuffer sizing is not interfered with
283
- by Pyglet. Final sizes are dictated by the platform the window was created on. It is up to the user to make any
284
- platform adjustments themselves such as sizing on a platform, mouse coordinate adjustments, or framebuffer size
285
- handling. On Windows and X11, the framebuffer and the requested window size will always match in pixels 1:1. On
286
- MacOS, depending on a Hi-DPI display, you may get a different sized framebuffer than the window size. This option
287
- does allow `window.dpi` and `window.scale` to return their respective values.
288
271
  """
289
272
 
290
273
  shader_bind_management: bool = True
291
274
  """If ``True``, this will enable internal management of Uniform Block bindings for
292
- :py:class:`~pyglet.graphics.shader.ShaderProgram`'s.
275
+ :py:class:`~pyglet.graphics.ShaderProgram`'s.
293
276
 
294
277
  If ``False``, bindings will not be managed by Pyglet. The user will be responsible for either setting the binding
295
278
  points through GLSL layouts (4.2 required) or manually through ``UniformBlock.set_binding``.
@@ -297,6 +280,27 @@ class Options:
297
280
  .. versionadded:: 2.0.16
298
281
  """
299
282
 
283
+ wayland: bool = False
284
+ """If ``True``, use Wayland instead of Xlib on Linux.
285
+
286
+ .. versionadded:: 3.0.0
287
+ """
288
+
289
+ backend: Literal["opengl", "gl2", "gles3", "gles2", "webgl"] = "opengl"
290
+ """Specify the graphics API backend."""
291
+
292
+ optimize_states: bool = True
293
+ """Runs a second pass on the draw list to remove any redundant states.
294
+
295
+ This option is mostly meant for debugging, as this should not significantly impact the draw list creation time
296
+ or impact drawing states.
297
+
298
+ .. versionadded:: 3.0.0
299
+ """
300
+
301
+ pyodide: PyodideOptions = field(default_factory=PyodideOptions)
302
+ """Pyodide specific options."""
303
+
300
304
  def get(self, item: str, default: Any = None) -> Any:
301
305
  return self.__dict__.get(item, default)
302
306
 
@@ -323,7 +327,7 @@ for _option_name, _type_str in options.__annotations__.items():
323
327
  setattr(options, _option_name, _value in ("true", "TRUE", "True", "1"))
324
328
  elif 'int' in _type_str:
325
329
  setattr(options, _option_name, int(_value))
326
- elif 'Literal' in _type_str and _value in _type_str:
330
+ elif 'str' in _type_str or ('Literal' in _type_str and _value in _type_str):
327
331
  setattr(options, _option_name, _value)
328
332
  else:
329
333
  warnings.warn(f"Invalid value '{_value}' for {_option_name}. Expecting {_type_str}")
@@ -434,7 +438,7 @@ class _ModuleProxy:
434
438
  def __init__(self, name: str) -> None:
435
439
  self.__dict__["_module_name"] = name
436
440
 
437
- def __getattr__(self, name: str): # noqa: ANN204
441
+ def __getattr__(self, name: str): # noqa: ANN204
438
442
  try:
439
443
  return getattr(self._module, name)
440
444
  except AttributeError:
@@ -468,11 +472,12 @@ if TYPE_CHECKING:
468
472
  from . import (
469
473
  app,
470
474
  clock,
475
+ config,
471
476
  customtypes,
472
477
  display,
478
+ enums,
473
479
  event,
474
480
  font,
475
- gl,
476
481
  graphics,
477
482
  gui,
478
483
  image,
@@ -489,12 +494,13 @@ if TYPE_CHECKING:
489
494
  )
490
495
  else:
491
496
  app = _ModuleProxy("app") # type: ignore
497
+ config = _ModuleProxy("config") # type: ignore
492
498
  clock = _ModuleProxy("clock") # type: ignore
493
499
  customtypes = _ModuleProxy("customtypes") # type: ignore
494
500
  display = _ModuleProxy("display") # type: ignore
501
+ enums = _ModuleProxy("enums") # type: ignore
495
502
  event = _ModuleProxy("event") # type: ignore
496
503
  font = _ModuleProxy("font") # type: ignore
497
- gl = _ModuleProxy("gl") # type: ignore
498
504
  graphics = _ModuleProxy("graphics") # type: ignore
499
505
  gui = _ModuleProxy("gui") # type: ignore
500
506
  image = _ModuleProxy("image") # type: ignore
pyglet/__init__.pyi CHANGED
@@ -4,11 +4,11 @@ from typing import Any, ItemsView, Literal, Sequence
4
4
 
5
5
  from . import app as app
6
6
  from . import clock as clock
7
+ from . import config as config
7
8
  from . import customtypes as customtypes
8
9
  from . import display as display
9
10
  from . import event as event
10
11
  from . import font as font
11
- from . import gl as gl
12
12
  from . import graphics as graphics
13
13
  from . import gui as gui
14
14
  from . import image as image
@@ -30,18 +30,21 @@ compat_platform: str
30
30
  env: str
31
31
  value: str
32
32
 
33
+ @dataclass
34
+ class PyodideOptions:
35
+ canvas_id: str
36
+
33
37
  @dataclass
34
38
  class Options:
35
39
  audio: Sequence[str]
36
40
  debug_font: bool
37
- debug_gl: bool
38
- debug_gl_trace: bool
39
- debug_gl_trace_args: bool
40
- debug_gl_shaders: bool
41
+ debug_api: bool
42
+ debug_api_trace: bool
43
+ debug_api_trace_args: bool
44
+ debug_api_shaders: bool
41
45
  debug_graphics_batch: bool
42
46
  debug_lib: bool
43
47
  debug_media: bool
44
- debug_texture: bool
45
48
  debug_trace: bool
46
49
  debug_trace_args: bool
47
50
  debug_trace_depth: int
@@ -49,8 +52,8 @@ class Options:
49
52
  debug_win32: bool
50
53
  debug_input: bool
51
54
  debug_x11: bool
55
+ debug_wayland: bool
52
56
  debug_com: bool
53
- shadow_window: bool
54
57
  vsync: bool | None
55
58
  xsync: bool
56
59
  xlib_fullscreen_override_redirect: bool
@@ -64,8 +67,12 @@ class Options:
64
67
  win32_disable_xinput: bool
65
68
  com_mta: bool
66
69
  osx_alt_loop: bool
67
- dpi_scaling: Literal["real", "scaled", "stretch"]
70
+ dpi_scaling: Literal["platform", "stretch"]
68
71
  shader_bind_management: bool
72
+ wayland: bool
73
+ backend: str | None
74
+ optimize_states: bool
75
+ pyodide: PyodideOptions
69
76
 
70
77
  def get(self, item: str, default: Any = None) -> Any:
71
78
  ...
pyglet/app/__init__.py CHANGED
@@ -3,8 +3,8 @@
3
3
  Applications
4
4
  ------------
5
5
 
6
- Most applications need only call :func:`run` after creating one or more
7
- windows to begin processing events. For example, a simple application
6
+ Most applications need only call :func:`run` after creating one or more
7
+ windows to begin processing events. For example, a simple application
8
8
  consisting of one window is::
9
9
 
10
10
  import pyglet
@@ -28,18 +28,17 @@ default policy is to wait until all windows are closed)::
28
28
 
29
29
  .. versionadded:: 1.1
30
30
  """
31
+
31
32
  from __future__ import annotations
32
33
 
34
+ import platform
33
35
  import sys
34
36
  import weakref
35
- import platform
36
37
 
37
38
  import pyglet
38
-
39
39
  from pyglet import compat_platform
40
40
  from pyglet.app.base import EventLoop
41
41
 
42
-
43
42
  _is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run
44
43
 
45
44
  if _is_pyglet_doc_run:
@@ -47,15 +46,16 @@ if _is_pyglet_doc_run:
47
46
  else:
48
47
  if compat_platform == 'darwin':
49
48
  from pyglet.app.cocoa import CocoaPlatformEventLoop as PlatformEventLoop
50
- from pyglet.libs.darwin.cocoapy.runtime import get_chip_model
51
49
 
52
- # Use alternate loop only if forced, or using an M1 chip.
53
- if (platform.machine() == 'arm64' and "M1" in get_chip_model()) or pyglet.options.osx_alt_loop:
50
+ if platform.machine() == 'arm64' or pyglet.options.osx_alt_loop:
54
51
  from pyglet.app.cocoa import CocoaAlternateEventLoop as EventLoop
55
52
  elif compat_platform in ('win32', 'cygwin'):
56
53
  from pyglet.app.win32 import Win32EventLoop as PlatformEventLoop
57
- else:
58
- from pyglet.app.xlib import XlibEventLoop as PlatformEventLoop
54
+ elif compat_platform == 'linux':
55
+ from pyglet.app.linux import LinuxEventLoop as PlatformEventLoop
56
+ elif compat_platform == 'emscripten':
57
+ from pyglet.app.async_app import AsyncEventLoop as EventLoop
58
+ from pyglet.app.async_app import AsyncPlatformEventLoop as PlatformEventLoop
59
59
 
60
60
 
61
61
  class AppException(Exception):
@@ -78,7 +78,11 @@ def run(interval: float | None = 1 / 60) -> None:
78
78
  pyglet.app.event_loop.run(interval)
79
79
 
80
80
  """
81
- event_loop.run(interval)
81
+ if pyglet.compat_platform == "emscripten":
82
+ import asyncio
83
+ asyncio.create_task(event_loop.run(interval)) # noqa: RUF006
84
+ else:
85
+ event_loop.run(interval)
82
86
 
83
87
 
84
88
  def exit() -> None:
@@ -93,11 +97,16 @@ def exit() -> None:
93
97
  pyglet.app.event_loop.exit()
94
98
 
95
99
  """
96
- event_loop.exit()
100
+ if pyglet.compat_platform == "emscripten":
101
+ import asyncio
102
+
103
+ asyncio.create_task(event_loop.exit())
104
+ else:
105
+ event_loop.exit()
97
106
 
98
107
 
99
108
  #: The global event loop. Applications can replace this
100
- #: with their own subclass of :class:`EventLoop` before calling
109
+ #: with their own subclass of :class:`EventLoop` before calling
101
110
  #: :meth:`EventLoop.run`.
102
111
  event_loop = EventLoop()
103
112
 
@@ -0,0 +1,212 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import sys
5
+ from typing import Any, Callable, TYPE_CHECKING
6
+
7
+ import pyglet
8
+ from pyglet import app, clock, event
9
+
10
+ if TYPE_CHECKING:
11
+ from pyglet.event import EventDispatcher
12
+ from pyglet.window import BaseWindow
13
+
14
+ _is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run
15
+ try:
16
+ from js import requestAnimationFrame, performance
17
+ from pyodide.ffi import create_proxy
18
+ except ImportError:
19
+ raise ImportError('Pyodide not available.')
20
+
21
+
22
+ class AsyncEventLoop(event.EventDispatcher):
23
+ """Asyncio-based event loop for Pyglet."""
24
+
25
+ def __init__(self) -> None:
26
+ self._has_exit_event = asyncio.Event()
27
+ # Change the default clock to use the javascript performance timer.
28
+ self.clock = clock.Clock(self._get_js_time)
29
+ pyglet.clock.set_default(self.clock)
30
+ self.is_running = False
31
+ self._interval = None
32
+ self._last_ts = 0.0
33
+ self._frame_dt = 0.0
34
+
35
+ self._frame_future = None
36
+ self._frame_proxy = create_proxy(self._frame_cb)
37
+
38
+ def _get_js_time(self) -> float:
39
+ """Use Javascripts performance.now for the clock accuracy.
40
+
41
+ May not be needed, but just to be accurate.
42
+ """
43
+ return performance.now() / 1000
44
+
45
+ @staticmethod
46
+ def _redraw_windows(dt: float) -> None:
47
+ # Redraw all windows
48
+ for window in app.windows:
49
+ window.draw(dt)
50
+
51
+ def _frame_cb(self, ts: float) -> None:
52
+ """Callback for the requestAnimationFrame."""
53
+ now = ts / 1000.0
54
+ _frame_dt = now - self._last_ts
55
+
56
+ self._last_ts = now
57
+
58
+ if self._frame_future:
59
+ self._frame_future.set_result(_frame_dt)
60
+ self._frame_future = None
61
+
62
+ async def _next_animation_frame(self) -> float:
63
+ self._frame_future = asyncio.Future()
64
+ requestAnimationFrame(self._frame_proxy)
65
+ return await self._frame_future
66
+
67
+ async def draw_frame(self) -> None:
68
+ frame_dt = await self._next_animation_frame()
69
+ self._redraw_windows(frame_dt)
70
+
71
+ async def run(self, interval: float | None = 1/60) -> None:
72
+ try:
73
+ """Begin processing events asynchronously."""
74
+ self._interval = interval
75
+
76
+ self._has_exit_event.clear()
77
+ platform_event_loop = app.platform_event_loop
78
+
79
+ await platform_event_loop.start()
80
+ self.dispatch_event('on_enter')
81
+ self.is_running = True
82
+
83
+ # Use the browser's requestAnimationFrame for the loop if None is used.
84
+ if self._interval is None:
85
+ while not self._has_exit_event.is_set():
86
+ await self.draw_frame()
87
+ self.idle() # Ticks clock and runs scheduled functions.
88
+ await platform_event_loop.dispatch_posted_events()
89
+ else:
90
+ _schedule_rate = self._interval / 1000.0
91
+
92
+ # Separate draw loop so clock timeout doesn't block rendering if it's slower.
93
+ async def _draw_loop() -> None:
94
+ while not self._has_exit_event.is_set():
95
+ await self.draw_frame()
96
+
97
+ asyncio.create_task(_draw_loop()) # noqa: RUF006
98
+
99
+ while not self._has_exit_event.is_set():
100
+ timeout = self.idle()
101
+ await platform_event_loop.dispatch_posted_events()
102
+ await asyncio.sleep(0 if timeout is None else timeout)
103
+
104
+ self.is_running = False
105
+ self.dispatch_event('on_exit')
106
+ await platform_event_loop.stop()
107
+ except Exception as err:
108
+ import traceback
109
+ print("Error in wrapped task", err)
110
+ traceback.print_exc()
111
+
112
+ async def enter_blocking(self) -> None:
113
+ """Ensure `idle()` continues to be called during blocking operations."""
114
+ timeout = self.idle()
115
+ await app.platform_event_loop.set_timer(self._blocking_timer, timeout)
116
+
117
+ async def exit_blocking(self) -> None:
118
+ """Stop the blocking timer."""
119
+ await app.platform_event_loop.set_timer(None, None)
120
+
121
+ async def _blocking_timer(self) -> None:
122
+ """Called to process scheduled events during blocking operations."""
123
+ dt = self.clock.update_time()
124
+ self.clock.call_scheduled_functions(dt)
125
+ if self._interval is None:
126
+ self._redraw_windows(dt)
127
+
128
+ timeout = self.clock.get_sleep_time(True)
129
+ await app.platform_event_loop.set_timer(self._blocking_timer, timeout)
130
+
131
+ def idle(self) -> float | None:
132
+ """Called each iteration to process events."""
133
+ dt = self.clock.update_time()
134
+ self.clock.call_scheduled_functions(dt)
135
+ return self.clock.get_sleep_time(True)
136
+
137
+ async def exit(self) -> None:
138
+ """Exit the event loop asynchronously."""
139
+ self._has_exit_event.set()
140
+ app.platform_event_loop.notify()
141
+
142
+ async def sleep(self, timeout: float) -> bool:
143
+ """Pause execution for a given time unless `exit()` is called."""
144
+ try:
145
+ await asyncio.wait_for(self._has_exit_event.wait(), timeout)
146
+ return True
147
+ except asyncio.TimeoutError:
148
+ return False
149
+
150
+ async def on_window_close(self, _window: BaseWindow) -> None:
151
+ """Exit when the last window is closed."""
152
+ if len(app.windows) == 0:
153
+ await self.exit()
154
+
155
+ AsyncEventLoop.register_event_type('on_window_close')
156
+ AsyncEventLoop.register_event_type('on_enter')
157
+ AsyncEventLoop.register_event_type('on_exit')
158
+
159
+ class AsyncPlatformEventLoop:
160
+ """An asyncio-based platform event loop."""
161
+
162
+ def __init__(self) -> None:
163
+ """Initialize the event loop with an asyncio queue and event."""
164
+ self._event_queue = asyncio.Queue()
165
+ self._is_running = asyncio.Event()
166
+
167
+ def is_running(self) -> bool:
168
+ """Check if the event loop is currently processing."""
169
+ return self._is_running.is_set()
170
+
171
+ async def post_event(self, dispatcher: EventDispatcher, event: str, *args: Any) -> None:
172
+ """Post an event into the main application thread asynchronously."""
173
+ await self._event_queue.put((dispatcher, event, args))
174
+ self.notify()
175
+
176
+ async def dispatch_posted_events(self) -> None:
177
+ """Dispatch all pending events asynchronously."""
178
+ while not self._event_queue.empty():
179
+ try:
180
+ dispatcher, evnt, args = await self._event_queue.get()
181
+ dispatcher.dispatch_event(evnt, *args)
182
+ except asyncio.CancelledError:
183
+ break
184
+ except ReferenceError:
185
+ # weakly-referenced object no longer exists
186
+ pass
187
+
188
+ def notify(self) -> None:
189
+ """Trigger an iteration of the asyncio event loop."""
190
+ if self.is_running():
191
+ asyncio.create_task(self.dispatch_posted_events())
192
+
193
+ async def start(self) -> None:
194
+ """Start the asyncio-based event loop."""
195
+ self._is_running.set()
196
+
197
+ async def step(self, timeout: float | None = None) -> None:
198
+ """Run one iteration of the asyncio event loop with a timeout."""
199
+ try:
200
+ await asyncio.wait_for(self.dispatch_posted_events(), timeout)
201
+ except asyncio.TimeoutError:
202
+ pass
203
+
204
+ async def set_timer(self, func: Callable, interval: float) -> None:
205
+ """Schedule a timer function asynchronously."""
206
+ while self.is_running():
207
+ await asyncio.sleep(interval)
208
+ func()
209
+
210
+ async def stop(self) -> None:
211
+ """Stop the asyncio-based event loop."""
212
+ self._is_running.clear()
pyglet/app/base.py CHANGED
@@ -152,7 +152,7 @@ class EventLoop(event.EventDispatcher):
152
152
 
153
153
  # Dispatch pending events
154
154
  for window in app.windows:
155
- window.switch_to()
155
+ #window.switch_to()
156
156
  window.dispatch_pending_events()
157
157
 
158
158
  platform_event_loop = app.platform_event_loop
@@ -264,6 +264,7 @@ class EventLoop(event.EventDispatcher):
264
264
  interrupted (see :py:meth:`sleep`).
265
265
  """
266
266
  self.has_exit = True
267
+ self.clock.unschedule(self._redraw_windows)
267
268
  app.platform_event_loop.notify()
268
269
 
269
270
  def sleep(self, timeout: float) -> bool:
@@ -6,7 +6,7 @@ from pyglet import app
6
6
  from pyglet.app.base import PlatformEventLoop
7
7
 
8
8
 
9
- class XlibSelectDevice:
9
+ class LinuxSelectDevice:
10
10
  def fileno(self):
11
11
  """Get the file handle for ``select()`` for this device.
12
12
 
@@ -31,7 +31,7 @@ class XlibSelectDevice:
31
31
  return False
32
32
 
33
33
 
34
- class NotificationDevice(XlibSelectDevice):
34
+ class NotificationDevice(LinuxSelectDevice):
35
35
  def __init__(self):
36
36
  self._sync_file_read, self._sync_file_write = os.pipe()
37
37
  self._event = threading.Event()
@@ -52,7 +52,7 @@ class NotificationDevice(XlibSelectDevice):
52
52
  return self._event.is_set()
53
53
 
54
54
 
55
- class XlibEventLoop(PlatformEventLoop):
55
+ class LinuxEventLoop(PlatformEventLoop):
56
56
  def __init__(self):
57
57
  super().__init__()
58
58
  self._notification_device = NotificationDevice()