pyglet 3.0.dev8__py3-none-any.whl → 3.0.dev10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyglet/__init__.py +11 -2
- pyglet/__init__.pyi +1 -0
- pyglet/app/__init__.py +3 -0
- pyglet/app/cocoa.py +88 -39
- pyglet/app/ios.py +168 -0
- pyglet/config/gl/__init__.py +5 -0
- pyglet/config/gl/ios.py +45 -0
- pyglet/config/gl/macos.py +1 -1
- pyglet/config/gl/windows.py +17 -17
- pyglet/display/__init__.py +3 -0
- pyglet/display/headless.py +1 -1
- pyglet/display/ios.py +49 -0
- pyglet/enums.py +38 -0
- pyglet/experimental/geoshader_sprite.py +157 -125
- pyglet/experimental/particles.py +225 -39
- pyglet/font/__init__.py +2 -2
- pyglet/font/quartz.py +45 -45
- pyglet/graphics/__init__.py +10 -1
- pyglet/graphics/api/__init__.py +12 -6
- pyglet/graphics/api/base.py +38 -66
- pyglet/graphics/api/gl/__init__.py +1 -1
- pyglet/graphics/api/gl/base.py +42 -11
- pyglet/graphics/api/gl/buffer.py +25 -25
- pyglet/graphics/api/gl/cocoa/context.py +1 -1
- pyglet/graphics/api/gl/context.py +19 -20
- pyglet/graphics/api/gl/framebuffer.py +18 -22
- pyglet/graphics/api/gl/gl.py +3051 -2622
- pyglet/graphics/api/gl/gl_compat.py +7795 -0
- pyglet/graphics/api/gl/gl_fallback.py +98 -0
- pyglet/graphics/api/gl/gles.py +3693 -0
- pyglet/graphics/api/gl/global_opengl.py +20 -16
- pyglet/graphics/api/gl/ios/__init__.py +3 -0
- pyglet/graphics/api/gl/ios/context.py +131 -0
- pyglet/graphics/api/gl/lib.py +2 -18
- pyglet/graphics/api/gl/shader.py +31 -29
- pyglet/graphics/api/gl/shapes.py +3 -2
- pyglet/graphics/api/gl/sprite.py +9 -6
- pyglet/graphics/api/gl/state.py +13 -9
- pyglet/graphics/api/gl/text.py +22 -17
- pyglet/graphics/api/gl/texture.py +72 -47
- pyglet/graphics/api/gl/vertexarray.py +8 -7
- pyglet/graphics/api/gl/vertexdomain.py +13 -208
- pyglet/graphics/api/gl/win32/context.py +7 -7
- pyglet/graphics/api/gl/xlib/context.py +15 -15
- pyglet/graphics/api/gl2/buffer.py +2 -2
- pyglet/graphics/api/gl2/framebuffer.py +1 -3
- pyglet/graphics/api/gl2/global_opengl.py +13 -7
- pyglet/graphics/api/gl2/shader.py +5 -2
- pyglet/graphics/api/gl2/shapes.py +3 -2
- pyglet/graphics/api/gl2/sprite.py +6 -4
- pyglet/graphics/api/gl2/text.py +22 -13
- pyglet/graphics/api/webgl/__init__.py +17 -26
- pyglet/graphics/api/webgl/buffer.py +16 -16
- pyglet/graphics/api/webgl/context.py +9 -54
- pyglet/graphics/api/webgl/framebuffer.py +16 -22
- pyglet/graphics/api/webgl/gl.py +1 -1
- pyglet/graphics/api/webgl/gl_info.py +1 -1
- pyglet/graphics/api/webgl/shader.py +23 -21
- pyglet/graphics/api/webgl/shapes.py +3 -2
- pyglet/graphics/api/webgl/sprite.py +9 -6
- pyglet/graphics/api/webgl/state.py +13 -15
- pyglet/graphics/api/webgl/text.py +22 -13
- pyglet/graphics/api/webgl/texture.py +70 -51
- pyglet/graphics/api/webgl/vertexarray.py +8 -7
- pyglet/graphics/api/webgl/vertexdomain.py +7 -4
- pyglet/graphics/atlas.py +4 -4
- pyglet/graphics/buffer.py +19 -4
- pyglet/graphics/draw.py +50 -30
- pyglet/graphics/framebuffer.py +3 -3
- pyglet/graphics/instance.py +308 -1
- pyglet/graphics/resource.py +124 -0
- pyglet/graphics/shader.py +142 -48
- pyglet/graphics/state.py +5 -1
- pyglet/graphics/texture.py +32 -41
- pyglet/graphics/vertexdomain.py +63 -8
- pyglet/gui/ninepatch.py +25 -15
- pyglet/gui/widgets.py +4 -3
- pyglet/image/__init__.py +9 -14
- pyglet/image/animation.py +4 -9
- pyglet/image/base.py +2 -20
- pyglet/image/codecs/quartz.py +14 -14
- pyglet/input/macos/darwin_gc.py +11 -23
- pyglet/lib.py +5 -10
- pyglet/libs/__init__.py +25 -0
- pyglet/libs/darwin/__init__.py +18 -1
- pyglet/libs/darwin/agl.py +3 -1
- pyglet/libs/darwin/cocoapy/__init__.py +19 -7
- pyglet/libs/darwin/cocoapy/cocoatypes.py +17 -6
- pyglet/libs/darwin/cocoapy/lib_appkit.py +148 -0
- pyglet/libs/darwin/cocoapy/lib_cocoa.py +76 -0
- pyglet/libs/darwin/cocoapy/lib_corefoundation.py +253 -0
- pyglet/libs/darwin/cocoapy/lib_coregraphics.py +182 -0
- pyglet/libs/darwin/cocoapy/lib_coretext.py +123 -0
- pyglet/libs/darwin/cocoapy/lib_quartz.py +74 -0
- pyglet/libs/darwin/cocoapy/lib_uikit.py +7 -0
- pyglet/libs/darwin/cocoapy/runtime.py +227 -64
- pyglet/libs/darwin/coreaudio.py +3 -7
- pyglet/libs/darwin/lib_agl.py +13 -3
- pyglet/libs/egl/__init__.py +1 -2
- pyglet/libs/egl/egl.py +728 -565
- pyglet/libs/egl/lib.py +15 -8
- pyglet/libs/linux/glx/glx.py +439 -568
- pyglet/libs/linux/glx/lib_glx.py +3 -2
- pyglet/libs/win32/lib_wgl.py +3 -2
- pyglet/libs/win32/types.py +66 -0
- pyglet/libs/win32/wgl.py +359 -258
- pyglet/media/__init__.py +64 -5
- pyglet/media/codecs/base.py +0 -8
- pyglet/media/player.py +161 -103
- pyglet/model/__init__.py +2 -0
- pyglet/model/codecs/obj.py +2 -2
- pyglet/resource.py +1 -1
- pyglet/shapes.py +254 -78
- pyglet/sprite.py +102 -22
- pyglet/text/__init__.py +22 -4
- pyglet/text/caret.py +3 -2
- pyglet/text/formats/structured.py +8 -7
- pyglet/text/layout/base.py +47 -6
- pyglet/text/layout/boxes.py +40 -10
- pyglet/text/layout/incremental.py +3 -2
- pyglet/text/layout/scrolling.py +3 -2
- pyglet/window/__init__.py +4 -2
- pyglet/window/{cocoa → apple/cocoa}/__init__.py +72 -9
- pyglet/window/{cocoa → apple/cocoa}/pyglet_delegate.py +17 -21
- pyglet/window/{cocoa → apple/cocoa}/pyglet_textview.py +37 -40
- pyglet/window/{cocoa → apple/cocoa}/pyglet_view.py +37 -35
- pyglet/window/{cocoa → apple/cocoa}/pyglet_window.py +10 -15
- pyglet/window/apple/ios.py +212 -0
- pyglet/window/camera/base.py +11 -1
- {pyglet-3.0.dev8.dist-info → pyglet-3.0.dev10.dist-info}/METADATA +1 -1
- {pyglet-3.0.dev8.dist-info → pyglet-3.0.dev10.dist-info}/RECORD +135 -125
- pyglet/graphics/api/gl1/gl_compat.py +0 -6182
- pyglet/libs/darwin/cocoapy/cocoalibs.py +0 -827
- pyglet/libs/egl/eglext.py +0 -43
- pyglet/libs/linux/glx/glxext_arb.py +0 -1168
- pyglet/libs/linux/glx/glxext_mesa.py +0 -8
- pyglet/libs/linux/glx/glxext_nv.py +0 -980
- pyglet/libs/win32/wglext_arb.py +0 -1606
- /pyglet/{graphics/api/gl1 → window/apple}/__init__.py +0 -0
- /pyglet/window/{cocoa → apple/cocoa}/systemcursor.py +0 -0
- {pyglet-3.0.dev8.dist-info → pyglet-3.0.dev10.dist-info}/WHEEL +0 -0
- {pyglet-3.0.dev8.dist-info → pyglet-3.0.dev10.dist-info}/licenses/LICENSE +0 -0
pyglet/__init__.py
CHANGED
|
@@ -18,7 +18,7 @@ if TYPE_CHECKING:
|
|
|
18
18
|
from typing import Any, Callable, ItemsView, Sized
|
|
19
19
|
|
|
20
20
|
#: The release version
|
|
21
|
-
version = '3.0.
|
|
21
|
+
version = '3.0.dev10'
|
|
22
22
|
__version__ = version
|
|
23
23
|
|
|
24
24
|
MIN_PYTHON_VERSION = 3, 10
|
|
@@ -264,6 +264,15 @@ class Options:
|
|
|
264
264
|
|
|
265
265
|
.. versionadded:: 2.0.5"""
|
|
266
266
|
|
|
267
|
+
osx_displaylink: bool = True
|
|
268
|
+
"""If ``True`` (the default), macOS 14 and later uses ``CADisplayLink`` to schedule window draws.
|
|
269
|
+
|
|
270
|
+
Set this to ``False`` before calling :func:`pyglet.app.run` to retain the clock-driven redraw behavior used by
|
|
271
|
+
earlier pyglet versions. This is useful for applications that need a timer defined frame cadence, such as a
|
|
272
|
+
deterministic capture pipeline or an application that coordinates drawing with an external scheduler.
|
|
273
|
+
|
|
274
|
+
.. versionadded:: 3.0.0"""
|
|
275
|
+
|
|
267
276
|
dpi_scaling: Literal["platform", "stretch"] = "platform"
|
|
268
277
|
"""For 'HiDPI' displays, Window behavior can differ between operating systems. Defaults to `'platform'`.
|
|
269
278
|
|
|
@@ -354,7 +363,7 @@ for _option_name, _type_str in options.__annotations__.items():
|
|
|
354
363
|
|
|
355
364
|
|
|
356
365
|
if (__debug__ is False) or getattr(sys, "frozen", False):
|
|
357
|
-
options.
|
|
366
|
+
options.debug_api = False
|
|
358
367
|
|
|
359
368
|
|
|
360
369
|
# Call tracing
|
pyglet/__init__.pyi
CHANGED
pyglet/app/__init__.py
CHANGED
|
@@ -51,6 +51,9 @@ else:
|
|
|
51
51
|
# Use alternate loop only if forced, or using an M1 chip.
|
|
52
52
|
if (platform.machine() == 'arm64' and "M1" in get_chip_model()) or pyglet.options.osx_alt_loop:
|
|
53
53
|
from pyglet.app.cocoa import CocoaAlternateEventLoop as EventLoop
|
|
54
|
+
elif compat_platform == 'ios':
|
|
55
|
+
from pyglet.app.ios import IOSEventLoop as EventLoop
|
|
56
|
+
from pyglet.app.ios import IOSPlatformEventLoop as PlatformEventLoop
|
|
54
57
|
elif compat_platform in ('win32', 'cygwin'):
|
|
55
58
|
from pyglet.app.win32 import Win32EventLoop as PlatformEventLoop
|
|
56
59
|
elif compat_platform == 'linux':
|
pyglet/app/cocoa.py
CHANGED
|
@@ -2,13 +2,16 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import signal
|
|
4
4
|
import time
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
5
6
|
|
|
7
|
+
import pyglet
|
|
6
8
|
from pyglet import app
|
|
7
|
-
from pyglet.app.base import EventLoop, PlatformEventLoop
|
|
8
|
-
from pyglet.libs.darwin import cocoapy, AutoReleasePool,
|
|
9
|
-
ObjCClass, get_selector, set_realtime_thread_policy
|
|
9
|
+
from pyglet.app.base import EventLoop, PlatformEventLoop, WindowDrawSource
|
|
10
|
+
from pyglet.libs.darwin import cocoapy, AutoReleasePool, PyObjectEncoding, ObjCInstance, send_super, \
|
|
11
|
+
ObjCClass, get_selector, objc_method, set_realtime_thread_policy
|
|
10
12
|
|
|
11
13
|
NSApplication = cocoapy.ObjCClass('NSApplication')
|
|
14
|
+
NSObject = cocoapy.ObjCClass('NSObject')
|
|
12
15
|
NSMenu = cocoapy.ObjCClass('NSMenu')
|
|
13
16
|
NSMenuItem = cocoapy.ObjCClass('NSMenuItem')
|
|
14
17
|
NSDate = cocoapy.ObjCClass('NSDate')
|
|
@@ -17,6 +20,9 @@ NSUserDefaults = cocoapy.ObjCClass('NSUserDefaults')
|
|
|
17
20
|
NSTimer = cocoapy.ObjCClass('NSTimer')
|
|
18
21
|
NSRunningApplication = cocoapy.ObjCClass('NSRunningApplication')
|
|
19
22
|
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from pyglet.window.apple.cocoa import CocoaWindow
|
|
25
|
+
|
|
20
26
|
def add_menu_item(menu, title, action, key):
|
|
21
27
|
with AutoReleasePool():
|
|
22
28
|
title = cocoapy.CFSTR(title)
|
|
@@ -54,25 +60,24 @@ def create_menu():
|
|
|
54
60
|
appMenuItem.release()
|
|
55
61
|
|
|
56
62
|
|
|
57
|
-
class
|
|
58
|
-
_AppDelegate = ObjCSubclass('NSObject', '_AppDelegate')
|
|
63
|
+
class _AppDelegate(NSObject):
|
|
59
64
|
|
|
60
|
-
@
|
|
65
|
+
@objc_method(b'@' + PyObjectEncoding)
|
|
61
66
|
def init(self, pyglet_loop):
|
|
62
67
|
objc = ObjCInstance(send_super(self, 'init'))
|
|
63
68
|
self._pyglet_loop = pyglet_loop
|
|
64
69
|
return objc # objc is self
|
|
65
70
|
|
|
66
|
-
@
|
|
71
|
+
@objc_method('v')
|
|
67
72
|
def updatePyglet_(self):
|
|
68
73
|
self._pyglet_loop.nsapp_step()
|
|
69
74
|
|
|
70
|
-
@
|
|
75
|
+
@objc_method('v@')
|
|
71
76
|
def applicationWillTerminate_(self, notification):
|
|
72
77
|
self._pyglet_loop.is_running = False
|
|
73
78
|
self._pyglet_loop.has_exit = True
|
|
74
79
|
|
|
75
|
-
@
|
|
80
|
+
@objc_method('v@')
|
|
76
81
|
def applicationDidFinishLaunching_(self, notification):
|
|
77
82
|
self._pyglet_loop._finished_launching = True
|
|
78
83
|
|
|
@@ -96,38 +101,32 @@ class _AppDelegate_Implementation:
|
|
|
96
101
|
|
|
97
102
|
NSApp.activateIgnoringOtherApps_(True)
|
|
98
103
|
|
|
99
|
-
@
|
|
104
|
+
@objc_method('v@')
|
|
100
105
|
def applicationWillFinishLaunching_(self, notification):
|
|
101
106
|
pass
|
|
102
107
|
|
|
103
|
-
@
|
|
108
|
+
@objc_method('B')
|
|
104
109
|
def applicationSupportsSecureRestorableState_(self):
|
|
105
110
|
return True
|
|
106
111
|
|
|
107
|
-
_AppDelegate = ObjCClass('_AppDelegate') # the actual class
|
|
108
|
-
|
|
109
112
|
class CocoaAlternateEventLoop(EventLoop):
|
|
110
113
|
"""This is an alternate loop developed mainly for ARM64 variants of macOS.
|
|
111
|
-
|
|
112
|
-
|
|
114
|
+
|
|
115
|
+
nextEventMatchingMask_untilDate_inMode_dequeue_ is very broken with ctypes calls on M1.
|
|
116
|
+
|
|
117
|
+
Events eventually stop working properly after X returns. This event loop
|
|
118
|
+
differs in that it uses the built-in NSApplication event
|
|
113
119
|
loop. We tie our schedule into it via timer.
|
|
114
120
|
"""
|
|
115
121
|
def __init__(self):
|
|
116
122
|
super().__init__()
|
|
117
123
|
self.platform_event_loop = None
|
|
118
124
|
|
|
119
|
-
def run(self, interval: float | None = 1/60):
|
|
120
|
-
if interval is None:
|
|
121
|
-
pass # do not schedule redraws
|
|
122
|
-
elif not interval:
|
|
123
|
-
self.clock.schedule(self._redraw_windows)
|
|
124
|
-
else:
|
|
125
|
-
self.clock.schedule_interval(self._redraw_windows, interval)
|
|
126
|
-
|
|
125
|
+
def run(self, interval: float | None = 1/60) -> None:
|
|
127
126
|
self.has_exit = False
|
|
128
127
|
|
|
129
|
-
from pyglet.window import Window
|
|
130
|
-
Window._enable_event_queue = False
|
|
128
|
+
from pyglet.window import Window # noqa: PLC0415
|
|
129
|
+
Window._enable_event_queue = False # noqa: SLF001
|
|
131
130
|
|
|
132
131
|
# Dispatch pending events
|
|
133
132
|
for window in app.windows:
|
|
@@ -135,13 +134,18 @@ class CocoaAlternateEventLoop(EventLoop):
|
|
|
135
134
|
window.dispatch_pending_events()
|
|
136
135
|
|
|
137
136
|
self.platform_event_loop = app.platform_event_loop
|
|
137
|
+
self._schedule_window_draw(interval)
|
|
138
138
|
|
|
139
139
|
self.dispatch_event('on_enter')
|
|
140
140
|
self.is_running = True
|
|
141
141
|
|
|
142
|
-
|
|
142
|
+
try:
|
|
143
|
+
self.platform_event_loop.nsapp_start(interval or 0)
|
|
144
|
+
finally:
|
|
145
|
+
self._unschedule_window_draw()
|
|
146
|
+
self._raise_window_draw_exception()
|
|
143
147
|
|
|
144
|
-
def exit(self):
|
|
148
|
+
def exit(self) -> None:
|
|
145
149
|
"""Safely exit the event loop at the end of the current iteration.
|
|
146
150
|
|
|
147
151
|
This method is a thread-safe equivalent for setting
|
|
@@ -159,12 +163,52 @@ class CocoaAlternateEventLoop(EventLoop):
|
|
|
159
163
|
self.platform_event_loop.nsapp_stop()
|
|
160
164
|
|
|
161
165
|
|
|
166
|
+
class CocoaWindowDrawSource(WindowDrawSource):
|
|
167
|
+
"""Display driven drawing backed by one CADisplayLink."""
|
|
168
|
+
|
|
169
|
+
def __init__(self) -> None:
|
|
170
|
+
self._active = False
|
|
171
|
+
self._interval = None
|
|
172
|
+
|
|
173
|
+
def start(self, interval: float) -> bool:
|
|
174
|
+
started_windows = []
|
|
175
|
+
for window in app.windows:
|
|
176
|
+
if not window._start_display_link(interval): # noqa: SLF001
|
|
177
|
+
for started_window in started_windows:
|
|
178
|
+
started_window._stop_display_link() # noqa: SLF001
|
|
179
|
+
return False
|
|
180
|
+
started_windows.append(window)
|
|
181
|
+
|
|
182
|
+
self._interval = interval
|
|
183
|
+
self._active = True
|
|
184
|
+
return True
|
|
185
|
+
|
|
186
|
+
def stop(self) -> None:
|
|
187
|
+
if not self._active:
|
|
188
|
+
return
|
|
189
|
+
|
|
190
|
+
self._active = False
|
|
191
|
+
self._interval = None
|
|
192
|
+
for window in app.windows:
|
|
193
|
+
window._stop_display_link() # noqa: SLF001
|
|
194
|
+
|
|
195
|
+
def add_window(self, window: CocoaWindow) -> bool:
|
|
196
|
+
return not self._active or window._start_display_link(self._interval) # noqa: SLF001
|
|
197
|
+
|
|
198
|
+
def remove_window(self, window: CocoaWindow) -> None:
|
|
199
|
+
window._stop_display_link() # noqa: SLF001
|
|
200
|
+
|
|
201
|
+
|
|
162
202
|
class CocoaPlatformEventLoop(PlatformEventLoop):
|
|
163
203
|
|
|
164
204
|
def __init__(self):
|
|
165
205
|
super().__init__()
|
|
166
206
|
set_realtime_thread_policy()
|
|
167
207
|
self._timer = None
|
|
208
|
+
nsview = ObjCClass('NSView')
|
|
209
|
+
self._display_link_supported = bool(
|
|
210
|
+
nsview.instancesRespondToSelector_(get_selector('displayLinkWithTarget:selector:'))
|
|
211
|
+
)
|
|
168
212
|
|
|
169
213
|
with AutoReleasePool():
|
|
170
214
|
# Prepare the default application.
|
|
@@ -198,7 +242,7 @@ class CocoaPlatformEventLoop(PlatformEventLoop):
|
|
|
198
242
|
signal.signal(signal.SIGINT, term_received)
|
|
199
243
|
signal.signal(signal.SIGTERM, term_received)
|
|
200
244
|
|
|
201
|
-
def start(self):
|
|
245
|
+
def start(self) -> None:
|
|
202
246
|
with AutoReleasePool():
|
|
203
247
|
if not self.NSApp.isRunning() and not self._finished_launching:
|
|
204
248
|
# finishLaunching should be called only once. However isRunning will not
|
|
@@ -207,9 +251,14 @@ class CocoaPlatformEventLoop(PlatformEventLoop):
|
|
|
207
251
|
self.NSApp.activateIgnoringOtherApps_(True)
|
|
208
252
|
self._finished_launching = True
|
|
209
253
|
|
|
210
|
-
def
|
|
211
|
-
|
|
212
|
-
|
|
254
|
+
def create_window_draw_source(self, _event_loop: EventLoop) -> WindowDrawSource | None:
|
|
255
|
+
if pyglet.options.osx_displaylink and self._display_link_supported:
|
|
256
|
+
return CocoaWindowDrawSource()
|
|
257
|
+
return None
|
|
258
|
+
|
|
259
|
+
def nsapp_start(self, interval: float) -> None:
|
|
260
|
+
"""Used only for CocoaAlternateEventLoop."""
|
|
261
|
+
from pyglet.app import event_loop # noqa: PLC0415
|
|
213
262
|
self._event_loop = event_loop
|
|
214
263
|
|
|
215
264
|
assert self._timer is None
|
|
@@ -219,26 +268,26 @@ class CocoaPlatformEventLoop(PlatformEventLoop):
|
|
|
219
268
|
self.appdelegate,
|
|
220
269
|
get_selector('updatePyglet:'),
|
|
221
270
|
False,
|
|
222
|
-
True
|
|
271
|
+
True,
|
|
223
272
|
)
|
|
224
273
|
|
|
225
274
|
self.NSApp.run()
|
|
226
275
|
|
|
227
|
-
def nsapp_step(self):
|
|
228
|
-
"""Used only for CocoaAlternateEventLoop"""
|
|
276
|
+
def nsapp_step(self) -> None:
|
|
277
|
+
"""Used only for CocoaAlternateEventLoop."""
|
|
229
278
|
self._event_loop.idle()
|
|
230
279
|
with AutoReleasePool():
|
|
231
280
|
self.dispatch_posted_events()
|
|
232
281
|
|
|
233
|
-
def nsapp_stop(self):
|
|
234
|
-
"""Used only for CocoaAlternateEventLoop"""
|
|
282
|
+
def nsapp_stop(self) -> None:
|
|
283
|
+
"""Used only for CocoaAlternateEventLoop."""
|
|
235
284
|
self.NSApp.stop_(None)
|
|
236
285
|
|
|
237
286
|
if self._timer:
|
|
238
287
|
self._timer.invalidate()
|
|
239
288
|
self._timer = None
|
|
240
289
|
|
|
241
|
-
def step(self, timeout=None):
|
|
290
|
+
def step(self, timeout: float | None=None) -> bool: # noqa: ANN201
|
|
242
291
|
with AutoReleasePool():
|
|
243
292
|
self.dispatch_posted_events()
|
|
244
293
|
|
|
@@ -274,10 +323,10 @@ class CocoaPlatformEventLoop(PlatformEventLoop):
|
|
|
274
323
|
|
|
275
324
|
return did_time_out
|
|
276
325
|
|
|
277
|
-
def stop(self):
|
|
326
|
+
def stop(self) -> None:
|
|
278
327
|
pass
|
|
279
328
|
|
|
280
|
-
def notify(self):
|
|
329
|
+
def notify(self) -> None:
|
|
281
330
|
with AutoReleasePool():
|
|
282
331
|
notifyEvent = NSEvent.otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(
|
|
283
332
|
cocoapy.NSApplicationDefined, # type
|
pyglet/app/ios.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""UIKit app loop integration on iOS."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
from pyglet import app
|
|
7
|
+
from pyglet.app.base import EventLoop, PlatformEventLoop, WindowDrawSource
|
|
8
|
+
from pyglet.libs.darwin import get_display_link_dt
|
|
9
|
+
from pyglet.libs.darwin.cocoapy import (
|
|
10
|
+
CAFrameRateRange,
|
|
11
|
+
NSRunLoopCommonModes,
|
|
12
|
+
ObjCClass,
|
|
13
|
+
ObjCInstance,
|
|
14
|
+
PyObjectEncoding,
|
|
15
|
+
get_selector,
|
|
16
|
+
objc_method,
|
|
17
|
+
send_super,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
_XCTEST_ENVIRONMENT = (
|
|
21
|
+
"XCTestBundlePath",
|
|
22
|
+
"XCTestConfigurationFilePath",
|
|
23
|
+
"XCTestSessionIdentifier",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
def _is_xctest() -> bool:
|
|
27
|
+
return any(name in os.environ for name in _XCTEST_ENVIRONMENT)
|
|
28
|
+
|
|
29
|
+
NSObject = ObjCClass('NSObject')
|
|
30
|
+
NSRunLoop = ObjCClass('NSRunLoop')
|
|
31
|
+
CADisplayLink = ObjCClass('CADisplayLink')
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class _PygletIOSDisplayLinkTarget(NSObject):
|
|
35
|
+
|
|
36
|
+
@objc_method(b'@' + PyObjectEncoding)
|
|
37
|
+
def initWithDrawSource_(self, draw_source: IOSDisplayLinkDrawSource) -> ObjCInstance | None:
|
|
38
|
+
self = ObjCInstance(send_super(self, 'init'))
|
|
39
|
+
if not self:
|
|
40
|
+
return None
|
|
41
|
+
self._draw_source = draw_source
|
|
42
|
+
return self
|
|
43
|
+
|
|
44
|
+
@objc_method('v@')
|
|
45
|
+
def displayLinkFired_(self, display_link: ObjCInstance) -> None:
|
|
46
|
+
self._draw_source.tick(display_link)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class IOSDisplayLinkDrawSource(WindowDrawSource):
|
|
50
|
+
"""One UIKit display link that paces all pyglet window drawing."""
|
|
51
|
+
|
|
52
|
+
def __init__(self, event_loop: IOSEventLoop) -> None:
|
|
53
|
+
self._event_loop = event_loop
|
|
54
|
+
self._display_link = None
|
|
55
|
+
self._target = None
|
|
56
|
+
self._last_timestamp: float | None = None
|
|
57
|
+
|
|
58
|
+
def start(self, interval: float) -> bool:
|
|
59
|
+
if self._display_link is not None:
|
|
60
|
+
return True
|
|
61
|
+
|
|
62
|
+
self._target = _PygletIOSDisplayLinkTarget.alloc().initWithDrawSource_(self)
|
|
63
|
+
display_link = CADisplayLink.displayLinkWithTarget_selector_(
|
|
64
|
+
self._target,
|
|
65
|
+
get_selector('displayLinkFired:'),
|
|
66
|
+
)
|
|
67
|
+
if display_link is None:
|
|
68
|
+
self._target.release()
|
|
69
|
+
self._target = None
|
|
70
|
+
return False
|
|
71
|
+
|
|
72
|
+
if interval:
|
|
73
|
+
frame_rate = 1 / interval
|
|
74
|
+
if CADisplayLink.instancesRespondToSelector_(get_selector('setPreferredFrameRateRange:')):
|
|
75
|
+
display_link.setPreferredFrameRateRange_(
|
|
76
|
+
CAFrameRateRange(frame_rate, frame_rate, frame_rate),
|
|
77
|
+
)
|
|
78
|
+
else:
|
|
79
|
+
display_link.setPreferredFramesPerSecond_(round(frame_rate))
|
|
80
|
+
|
|
81
|
+
self._display_link = display_link
|
|
82
|
+
self._last_timestamp = None
|
|
83
|
+
display_link.addToRunLoop_forMode_(NSRunLoop.mainRunLoop(), NSRunLoopCommonModes)
|
|
84
|
+
return True
|
|
85
|
+
|
|
86
|
+
def stop(self) -> None:
|
|
87
|
+
if self._display_link is not None:
|
|
88
|
+
self._display_link.invalidate()
|
|
89
|
+
self._display_link = None
|
|
90
|
+
if self._target is not None:
|
|
91
|
+
self._target.release()
|
|
92
|
+
self._target = None
|
|
93
|
+
self._last_timestamp = None
|
|
94
|
+
|
|
95
|
+
def tick(self, display_link: ObjCInstance) -> None:
|
|
96
|
+
if self._display_link is None or not self._event_loop.is_running:
|
|
97
|
+
return
|
|
98
|
+
self._last_timestamp, dt = get_display_link_dt(display_link, self._last_timestamp)
|
|
99
|
+
self._event_loop._tick_app(dt) # noqa: SLF001
|
|
100
|
+
|
|
101
|
+
class IOSPlatformEventLoop(PlatformEventLoop):
|
|
102
|
+
"""Schedules pyglet work from UIKit's already-running main run loop."""
|
|
103
|
+
|
|
104
|
+
def __init__(self) -> None:
|
|
105
|
+
super().__init__()
|
|
106
|
+
|
|
107
|
+
def notify(self) -> None:
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
def start(self) -> None:
|
|
111
|
+
self._is_running.set()
|
|
112
|
+
|
|
113
|
+
def step(self, timeout: float | None = None) -> None:
|
|
114
|
+
self.dispatch_posted_events()
|
|
115
|
+
|
|
116
|
+
def stop(self) -> None:
|
|
117
|
+
self._is_running.clear()
|
|
118
|
+
|
|
119
|
+
def create_window_draw_source(self, event_loop: EventLoop) -> WindowDrawSource:
|
|
120
|
+
return IOSDisplayLinkDrawSource(event_loop)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class IOSEventLoop(EventLoop):
|
|
124
|
+
"""Event loop similar to what we did for alt macos loop.."""
|
|
125
|
+
|
|
126
|
+
def run(self, interval: float | None = 1 / 60) -> None:
|
|
127
|
+
# Check for XCode environment as it owns the app lifetime and finalizes its embedded
|
|
128
|
+
# interpreter after the test returns. It must not receive native
|
|
129
|
+
# callbacks from a pyglet timer after that point.
|
|
130
|
+
if _is_xctest():
|
|
131
|
+
# Tick app once during test to ensure at least one draw.
|
|
132
|
+
self._tick_app()
|
|
133
|
+
self.has_exit = True
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
self.has_exit = False
|
|
137
|
+
from pyglet.window import Window # noqa: PLC0415
|
|
138
|
+
Window._enable_event_queue = False # noqa: SLF001
|
|
139
|
+
for window in app.windows:
|
|
140
|
+
window.dispatch_pending_events()
|
|
141
|
+
|
|
142
|
+
self._interval = interval
|
|
143
|
+
self.dispatch_event('on_enter')
|
|
144
|
+
self.is_running = True
|
|
145
|
+
app.platform_event_loop.start()
|
|
146
|
+
self._schedule_display_link(interval)
|
|
147
|
+
|
|
148
|
+
def _schedule_display_link(self, interval: float | None) -> None:
|
|
149
|
+
self._window_draw_source = app.platform_event_loop.create_window_draw_source(self)
|
|
150
|
+
if not self._window_draw_source.start(1 / 60 if interval is None else interval):
|
|
151
|
+
raise RuntimeError("Unable to create the iOS CADisplayLink.")
|
|
152
|
+
|
|
153
|
+
def _tick_app(self, draw_dt: float | None = None) -> None:
|
|
154
|
+
app.platform_event_loop.dispatch_posted_events()
|
|
155
|
+
clock_dt = self.clock.update_time()
|
|
156
|
+
self.clock.call_scheduled_functions(clock_dt)
|
|
157
|
+
if self._interval is not None:
|
|
158
|
+
for window in app.windows:
|
|
159
|
+
self._dispatch_platform_draw(window, clock_dt if draw_dt is None else draw_dt)
|
|
160
|
+
|
|
161
|
+
def exit(self) -> None:
|
|
162
|
+
if not self.is_running:
|
|
163
|
+
return
|
|
164
|
+
self.has_exit = True
|
|
165
|
+
self.is_running = False
|
|
166
|
+
self._unschedule_window_draw()
|
|
167
|
+
app.platform_event_loop.stop()
|
|
168
|
+
self.dispatch_event('on_exit')
|
pyglet/config/gl/__init__.py
CHANGED
|
@@ -120,6 +120,11 @@ def get_surface_config(user_config: UserConfig, surface: pyglet.window.Window) -
|
|
|
120
120
|
|
|
121
121
|
return match(user_config, surface)
|
|
122
122
|
|
|
123
|
+
if pyglet.compat_platform == "ios":
|
|
124
|
+
from pyglet.config.gl.ios import match # noqa: PLC0415
|
|
125
|
+
|
|
126
|
+
return match(user_config, surface)
|
|
127
|
+
|
|
123
128
|
if pyglet.compat_platform == "emscripten":
|
|
124
129
|
from pyglet.config.gl.webgl import match # noqa: PLC0415
|
|
125
130
|
|
pyglet/config/gl/ios.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""OpenGL ES surface configuration for UIKit windows."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from pyglet.config.gl import GLSurfaceConfig
|
|
7
|
+
from pyglet.enums import GraphicsAPI
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from pyglet.config import OpenGLUserConfig
|
|
11
|
+
from pyglet.graphics.api.gl.global_opengl import OpenGLBackend
|
|
12
|
+
from pyglet.graphics.api.gl.ios.context import IOSContext
|
|
13
|
+
from pyglet.window.apple.ios import IOSWindow
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def match(config: OpenGLUserConfig, window: IOSWindow) -> IOSGLSurfaceConfig | None:
|
|
17
|
+
# iOS only supports GLES2 or 3.
|
|
18
|
+
if config.api not in (GraphicsAPI.OPENGL_ES_2, GraphicsAPI.OPENGL_ES_3):
|
|
19
|
+
return None
|
|
20
|
+
if config.api == GraphicsAPI.OPENGL_ES_3 and (config.major_version or 3) > 3:
|
|
21
|
+
return None
|
|
22
|
+
return IOSGLSurfaceConfig(window, config)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class IOSGLSurfaceConfig(GLSurfaceConfig):
|
|
26
|
+
|
|
27
|
+
def __init__(self, window: IOSWindow, config: OpenGLUserConfig) -> None:
|
|
28
|
+
super().__init__(window, config, handle=None)
|
|
29
|
+
self.double_buffer = True
|
|
30
|
+
self.red_size = self.green_size = self.blue_size = self.alpha_size = 8
|
|
31
|
+
self.buffer_size = 32
|
|
32
|
+
self.depth_size = config.depth_size or 16
|
|
33
|
+
self.stencil_size = config.stencil_size or 0
|
|
34
|
+
self.sample_buffers = self.samples = 0
|
|
35
|
+
self.stereo = self.aux_buffers = 0
|
|
36
|
+
self.major_version = config.major_version or (3 if config.api == GraphicsAPI.OPENGL_ES_3 else 2)
|
|
37
|
+
self.minor_version = config.minor_version or 0
|
|
38
|
+
|
|
39
|
+
def create_context(self, opengl_backend: OpenGLBackend, share: IOSContext | None) -> IOSContext:
|
|
40
|
+
from pyglet.graphics.api.gl.ios.context import IOSContext # noqa: PLC0415
|
|
41
|
+
|
|
42
|
+
return IOSContext(opengl_backend, self._window, self, share)
|
|
43
|
+
|
|
44
|
+
def apply_format(self) -> None:
|
|
45
|
+
return None
|
pyglet/config/gl/macos.py
CHANGED
|
@@ -12,7 +12,7 @@ from typing import TYPE_CHECKING
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
13
13
|
from pyglet.config import OpenGLUserConfig
|
|
14
14
|
from pyglet.graphics.api.gl.cocoa.context import NSOpenGLPixelFormat, CocoaContext
|
|
15
|
-
from pyglet.window.cocoa import CocoaWindow
|
|
15
|
+
from pyglet.window.apple.cocoa import CocoaWindow
|
|
16
16
|
from pyglet.graphics.api import OpenGLBackend
|
|
17
17
|
|
|
18
18
|
# Version info, needed as OpenGL different Lion and onward
|
pyglet/config/gl/windows.py
CHANGED
|
@@ -6,7 +6,7 @@ from dataclasses import asdict
|
|
|
6
6
|
|
|
7
7
|
from typing import TYPE_CHECKING
|
|
8
8
|
from pyglet.config.gl import GLSurfaceConfig
|
|
9
|
-
from pyglet.libs.win32 import PIXELFORMATDESCRIPTOR, _gdi32,
|
|
9
|
+
from pyglet.libs.win32 import PIXELFORMATDESCRIPTOR, _gdi32, wgl
|
|
10
10
|
from pyglet.libs.win32.constants import PFD_DRAW_TO_WINDOW, PFD_SUPPORT_OPENGL, PFD_DOUBLEBUFFER, \
|
|
11
11
|
PFD_DOUBLEBUFFER_DONTCARE, PFD_STEREO, PFD_STEREO_DONTCARE, PFD_DEPTH_DONTCARE, PFD_TYPE_RGBA
|
|
12
12
|
from pyglet.libs.win32.wgl import WGLFunctions
|
|
@@ -221,22 +221,22 @@ class GLLegacyConfig(GLSurfaceConfig):
|
|
|
221
221
|
|
|
222
222
|
class GLSurfaceConfig(GLSurfaceConfig):
|
|
223
223
|
attribute_ids = { # noqa: RUF012
|
|
224
|
-
'double_buffer':
|
|
225
|
-
'stereo':
|
|
226
|
-
'buffer_size':
|
|
227
|
-
'aux_buffers':
|
|
228
|
-
'sample_buffers':
|
|
229
|
-
'samples':
|
|
230
|
-
'red_size':
|
|
231
|
-
'green_size':
|
|
232
|
-
'blue_size':
|
|
233
|
-
'alpha_size':
|
|
234
|
-
'depth_size':
|
|
235
|
-
'stencil_size':
|
|
236
|
-
'accum_red_size':
|
|
237
|
-
'accum_green_size':
|
|
238
|
-
'accum_blue_size':
|
|
239
|
-
'accum_alpha_size':
|
|
224
|
+
'double_buffer': wgl.WGL_DOUBLE_BUFFER_ARB,
|
|
225
|
+
'stereo': wgl.WGL_STEREO_ARB,
|
|
226
|
+
'buffer_size': wgl.WGL_COLOR_BITS_ARB,
|
|
227
|
+
'aux_buffers': wgl.WGL_AUX_BUFFERS_ARB,
|
|
228
|
+
'sample_buffers': wgl.WGL_SAMPLE_BUFFERS_ARB,
|
|
229
|
+
'samples': wgl.WGL_SAMPLES_ARB,
|
|
230
|
+
'red_size': wgl.WGL_RED_BITS_ARB,
|
|
231
|
+
'green_size': wgl.WGL_GREEN_BITS_ARB,
|
|
232
|
+
'blue_size': wgl.WGL_BLUE_BITS_ARB,
|
|
233
|
+
'alpha_size': wgl.WGL_ALPHA_BITS_ARB,
|
|
234
|
+
'depth_size': wgl.WGL_DEPTH_BITS_ARB,
|
|
235
|
+
'stencil_size': wgl.WGL_STENCIL_BITS_ARB,
|
|
236
|
+
'accum_red_size': wgl.WGL_ACCUM_RED_BITS_ARB,
|
|
237
|
+
'accum_green_size': wgl.WGL_ACCUM_GREEN_BITS_ARB,
|
|
238
|
+
'accum_blue_size': wgl.WGL_ACCUM_BLUE_BITS_ARB,
|
|
239
|
+
'accum_alpha_size': wgl.WGL_ACCUM_ALPHA_BITS_ARB,
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
def __init__(self, window: Win32Window, pf: int, user_config: OpenGLUserConfig) -> None:
|
pyglet/display/__init__.py
CHANGED
|
@@ -38,6 +38,9 @@ else:
|
|
|
38
38
|
elif compat_platform == 'darwin':
|
|
39
39
|
from pyglet.display.cocoa import CocoaDisplay as Display
|
|
40
40
|
from pyglet.display.cocoa import CocoaScreen as Screen
|
|
41
|
+
elif compat_platform == 'ios':
|
|
42
|
+
from pyglet.display.ios import IOSDisplay as Display
|
|
43
|
+
from pyglet.display.ios import IOSScreen as Screen
|
|
41
44
|
elif compat_platform in ('win32', 'cygwin'):
|
|
42
45
|
from pyglet.display.win32 import Win32Display as Display
|
|
43
46
|
from pyglet.display.win32 import Win32Screen as Screen
|
pyglet/display/headless.py
CHANGED
|
@@ -41,7 +41,7 @@ class EGLHeadlessDisplay(HeadlessDisplay):
|
|
|
41
41
|
num_devices = egl.EGLint()
|
|
42
42
|
try:
|
|
43
43
|
egl.eglQueryDevicesEXT(0, None, byref(num_devices))
|
|
44
|
-
except pyglet.libs.
|
|
44
|
+
except pyglet.libs.MissingFunctionException:
|
|
45
45
|
warnings.warn('No device available for EGL device platform. Using native display type.')
|
|
46
46
|
display = egl.EGLNativeDisplayType()
|
|
47
47
|
self._display_connection = egl.eglGetDisplay(display)
|
pyglet/display/ios.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pyglet.libs.darwin.cocoapy import ObjCClass
|
|
4
|
+
|
|
5
|
+
from .base import Display, Screen, ScreenMode
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
UIScreen = ObjCClass('UIScreen')
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class IOSDisplay(Display):
|
|
12
|
+
"""The single display from UIKit."""
|
|
13
|
+
|
|
14
|
+
def get_screens(self) -> list[IOSScreen]:
|
|
15
|
+
return [IOSScreen(self, UIScreen.mainScreen())]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class IOSScreen(Screen):
|
|
19
|
+
def __init__(self, display: IOSDisplay, ui_screen) -> None:
|
|
20
|
+
self._ui_screen = ui_screen
|
|
21
|
+
bounds = ui_screen.bounds()
|
|
22
|
+
scale = ui_screen.nativeScale()
|
|
23
|
+
super().__init__(display, 0, 0, round(bounds.size.width * scale), round(bounds.size.height * scale))
|
|
24
|
+
|
|
25
|
+
def get_modes(self) -> list[ScreenMode]:
|
|
26
|
+
return [self.get_mode()]
|
|
27
|
+
|
|
28
|
+
def get_mode(self) -> ScreenMode:
|
|
29
|
+
mode = ScreenMode(self)
|
|
30
|
+
mode.width, mode.height, mode.depth, mode.rate = self.width, self.height, None, None
|
|
31
|
+
return mode
|
|
32
|
+
|
|
33
|
+
def set_mode(self, mode: ScreenMode) -> None:
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
def restore_mode(self) -> None:
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
def get_dpi(self) -> int:
|
|
40
|
+
return round(96 * self.get_scale())
|
|
41
|
+
|
|
42
|
+
def get_scale(self) -> float:
|
|
43
|
+
return float(self._ui_screen.nativeScale())
|
|
44
|
+
|
|
45
|
+
def get_display_id(self) -> str:
|
|
46
|
+
return 'main'
|
|
47
|
+
|
|
48
|
+
def get_monitor_name(self) -> str:
|
|
49
|
+
return 'iOS Display'
|