pyglet 3.0.dev9__py3-none-any.whl → 3.0.dev11__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 +13 -2
- pyglet/__init__.pyi +3 -0
- pyglet/app/__init__.py +10 -3
- pyglet/app/base.py +2 -3
- pyglet/app/cocoa.py +88 -39
- pyglet/app/ios.py +168 -0
- pyglet/config/__init__.py +10 -2
- pyglet/config/gl/__init__.py +21 -12
- 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 +5 -3
- pyglet/display/base.py +23 -12
- pyglet/display/cocoa.py +4 -0
- pyglet/display/emscripten.py +11 -1
- pyglet/display/headless.py +5 -1
- pyglet/display/ios.py +53 -0
- pyglet/display/wayland.py +15 -8
- pyglet/display/win32.py +13 -15
- pyglet/display/xlib.py +17 -14
- pyglet/enums.py +38 -0
- pyglet/event.py +5 -5
- pyglet/experimental/geoshader_sprite.py +158 -129
- pyglet/experimental/particles.py +226 -42
- pyglet/font/__init__.py +3 -4
- pyglet/font/base.py +27 -22
- pyglet/font/dwrite/__init__.py +3 -3
- pyglet/font/freetype.py +51 -76
- pyglet/font/freetype_lib.py +1 -1
- pyglet/font/group.py +18 -4
- pyglet/font/harfbuzz.py +3 -3
- pyglet/font/quartz.py +48 -48
- pyglet/font/user.py +26 -12
- pyglet/graphics/__init__.py +9 -2
- pyglet/graphics/allocation.py +109 -2
- pyglet/graphics/api/__init__.py +16 -6
- pyglet/graphics/api/base.py +96 -2
- pyglet/graphics/api/gl/__init__.py +0 -3
- pyglet/graphics/api/gl/buffer.py +5 -5
- pyglet/graphics/api/gl/cocoa/context.py +1 -1
- pyglet/graphics/api/gl/context.py +28 -1
- pyglet/graphics/api/gl/framebuffer.py +164 -28
- 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/gl_info.py +4 -0
- pyglet/graphics/api/gl/gles.py +3693 -0
- pyglet/graphics/api/gl/global_opengl.py +19 -11
- 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 +18 -41
- pyglet/graphics/api/gl/shapes.py +3 -2
- pyglet/graphics/api/gl/sprite.py +9 -8
- pyglet/graphics/api/gl/state.py +4 -0
- pyglet/graphics/api/gl/text.py +185 -54
- pyglet/graphics/api/gl/texture.py +3 -13
- pyglet/graphics/api/gl/vertexdomain.py +155 -267
- pyglet/graphics/api/gl/win32/context.py +7 -7
- pyglet/graphics/api/gl/xlib/context.py +15 -15
- pyglet/graphics/api/gl2/buffer.py +3 -3
- pyglet/graphics/api/gl2/framebuffer.py +7 -2
- pyglet/graphics/api/gl2/global_opengl.py +13 -9
- pyglet/graphics/api/gl2/shader.py +5 -2
- pyglet/graphics/api/gl2/shapes.py +3 -2
- pyglet/graphics/api/gl2/sprite.py +6 -6
- pyglet/graphics/api/gl2/text.py +171 -49
- pyglet/graphics/api/gl2/vertexdomain.py +93 -49
- pyglet/graphics/api/webgl/__init__.py +22 -14
- pyglet/graphics/api/webgl/buffer.py +17 -17
- pyglet/graphics/api/webgl/context.py +3 -3
- pyglet/graphics/api/webgl/draw.py +2 -2
- pyglet/graphics/api/webgl/framebuffer.py +39 -13
- pyglet/graphics/api/webgl/gl.py +1 -1
- pyglet/graphics/api/webgl/shader.py +22 -57
- pyglet/graphics/api/webgl/shapes.py +3 -2
- pyglet/graphics/api/webgl/sprite.py +9 -8
- pyglet/graphics/api/webgl/state.py +4 -0
- pyglet/graphics/api/webgl/text.py +116 -98
- pyglet/graphics/api/webgl/texture.py +18 -28
- pyglet/graphics/api/webgl/vertexarray.py +3 -3
- pyglet/graphics/api/webgl/vertexdomain.py +141 -62
- pyglet/graphics/api/webgl/webgl_js.pyi +3 -0
- pyglet/graphics/atlas.py +8 -7
- pyglet/graphics/attributes.py +221 -0
- pyglet/graphics/draw.py +684 -204
- pyglet/graphics/framebuffer.py +163 -5
- pyglet/graphics/instance.py +310 -2
- pyglet/graphics/resource.py +2 -2
- pyglet/graphics/shader.py +181 -267
- pyglet/graphics/state.py +5 -1
- pyglet/graphics/texture.py +26 -36
- pyglet/graphics/vertexdomain.py +251 -333
- pyglet/graphics/vertexstorage.py +555 -0
- pyglet/gui/__init__.py +6 -2
- pyglet/gui/layout.py +806 -0
- pyglet/gui/manager.py +348 -0
- pyglet/gui/ninepatch.py +91 -54
- pyglet/gui/styles.py +130 -0
- pyglet/gui/widgets.py +610 -115
- pyglet/image/__init__.py +9 -14
- pyglet/image/animation.py +5 -10
- pyglet/image/base.py +52 -43
- pyglet/image/codecs/__init__.py +8 -0
- pyglet/image/codecs/qoi.py +275 -0
- pyglet/image/codecs/quartz.py +14 -14
- pyglet/input/__init__.py +2 -3
- pyglet/input/base.py +10 -11
- pyglet/input/macos/darwin_gc.py +11 -23
- pyglet/lib.py +13 -18
- 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/math.py +8 -8
- pyglet/media/__init__.py +64 -5
- pyglet/media/codecs/__init__.py +1 -3
- pyglet/media/codecs/base.py +15 -14
- pyglet/media/codecs/ffmpeg_lib/compat.py +1 -1
- pyglet/media/drivers/__init__.py +1 -3
- pyglet/media/player.py +161 -103
- pyglet/media/synthesis.py +64 -44
- pyglet/model/__init__.py +2 -2
- pyglet/model/codecs/obj.py +3 -4
- pyglet/resource.py +148 -40
- pyglet/shapes.py +255 -79
- pyglet/sprite.py +133 -56
- pyglet/text/__init__.py +33 -13
- pyglet/text/caret.py +17 -8
- pyglet/text/document.py +7 -5
- pyglet/text/formats/attributed.py +3 -2
- pyglet/text/formats/html.py +18 -14
- pyglet/text/formats/structured.py +62 -31
- pyglet/text/layout/base.py +157 -41
- pyglet/text/layout/boxes.py +63 -32
- pyglet/text/layout/flow.py +81 -61
- pyglet/text/layout/incremental.py +42 -23
- pyglet/text/layout/scrolling.py +40 -15
- pyglet/text/runlist.py +8 -7
- pyglet/util.py +2 -2
- pyglet/window/__init__.py +75 -50
- 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 +41 -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 +41 -11
- pyglet/window/camera/camera2d.py +8 -7
- pyglet/window/camera/camera3d.py +4 -3
- pyglet/window/dialog/__init__.py +3 -4
- pyglet/window/emscripten/__init__.py +1 -0
- pyglet/window/key.py +1 -0
- pyglet/window/wayland/__init__.py +1 -0
- pyglet/window/win32/__init__.py +1 -0
- pyglet/window/xlib/__init__.py +5 -4
- {pyglet-3.0.dev9.dist-info → pyglet-3.0.dev11.dist-info}/METADATA +3 -2
- {pyglet-3.0.dev9.dist-info → pyglet-3.0.dev11.dist-info}/RECORD +378 -364
- {pyglet-3.0.dev9.dist-info → pyglet-3.0.dev11.dist-info}/WHEEL +1 -1
- pyglet/graphics/api/gl1/gl_compat.py +0 -6182
- pyglet/gui/frame.py +0 -205
- 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.dev9.dist-info → pyglet-3.0.dev11.dist-info}/licenses/LICENSE +0 -0
pyglet/__init__.py
CHANGED
|
@@ -8,6 +8,8 @@ import os
|
|
|
8
8
|
import sys
|
|
9
9
|
import warnings
|
|
10
10
|
|
|
11
|
+
IS_DOC_BUILD = os.environ.get("PYGLET_DOC_BUILD") == "1"
|
|
12
|
+
|
|
11
13
|
from dataclasses import dataclass, field
|
|
12
14
|
from typing import TYPE_CHECKING, Literal, Sequence
|
|
13
15
|
|
|
@@ -18,7 +20,7 @@ if TYPE_CHECKING:
|
|
|
18
20
|
from typing import Any, Callable, ItemsView, Sized
|
|
19
21
|
|
|
20
22
|
#: The release version
|
|
21
|
-
version = '3.0.
|
|
23
|
+
version = '3.0.dev11'
|
|
22
24
|
__version__ = version
|
|
23
25
|
|
|
24
26
|
MIN_PYTHON_VERSION = 3, 10
|
|
@@ -264,6 +266,15 @@ class Options:
|
|
|
264
266
|
|
|
265
267
|
.. versionadded:: 2.0.5"""
|
|
266
268
|
|
|
269
|
+
osx_displaylink: bool = True
|
|
270
|
+
"""If ``True`` (the default), macOS 14 and later uses ``CADisplayLink`` to schedule window draws.
|
|
271
|
+
|
|
272
|
+
Set this to ``False`` before calling :func:`pyglet.app.run` to retain the clock-driven redraw behavior used by
|
|
273
|
+
earlier pyglet versions. This is useful for applications that need a timer defined frame cadence, such as a
|
|
274
|
+
deterministic capture pipeline or an application that coordinates drawing with an external scheduler.
|
|
275
|
+
|
|
276
|
+
.. versionadded:: 3.0.0"""
|
|
277
|
+
|
|
267
278
|
dpi_scaling: Literal["platform", "stretch"] = "platform"
|
|
268
279
|
"""For 'HiDPI' displays, Window behavior can differ between operating systems. Defaults to `'platform'`.
|
|
269
280
|
|
|
@@ -354,7 +365,7 @@ for _option_name, _type_str in options.__annotations__.items():
|
|
|
354
365
|
|
|
355
366
|
|
|
356
367
|
if (__debug__ is False) or getattr(sys, "frozen", False):
|
|
357
|
-
options.
|
|
368
|
+
options.debug_api = False
|
|
358
369
|
|
|
359
370
|
|
|
360
371
|
# Call tracing
|
pyglet/__init__.pyi
CHANGED
|
@@ -24,6 +24,8 @@ from . import sprite as sprite
|
|
|
24
24
|
from . import text as text
|
|
25
25
|
from . import window as window
|
|
26
26
|
|
|
27
|
+
IS_DOC_BUILD: bool
|
|
28
|
+
|
|
27
29
|
version: str
|
|
28
30
|
MIN_PYTHON_VERSION: tuple[int, int]
|
|
29
31
|
MIN_PYTHON_VERSION_STR: str
|
|
@@ -68,6 +70,7 @@ class Options:
|
|
|
68
70
|
win32_disable_xinput: bool
|
|
69
71
|
com_mta: bool
|
|
70
72
|
osx_alt_loop: bool
|
|
73
|
+
osx_displaylink: bool
|
|
71
74
|
dpi_scaling: Literal["platform", "stretch"]
|
|
72
75
|
shader_bind_management: bool
|
|
73
76
|
wayland: bool
|
pyglet/app/__init__.py
CHANGED
|
@@ -31,17 +31,17 @@ default policy is to wait until all windows are closed)::
|
|
|
31
31
|
|
|
32
32
|
from __future__ import annotations
|
|
33
33
|
|
|
34
|
-
import platform
|
|
35
34
|
import sys
|
|
35
|
+
import signal
|
|
36
|
+
import platform
|
|
36
37
|
import weakref
|
|
37
38
|
|
|
38
39
|
import pyglet
|
|
39
40
|
from pyglet import compat_platform
|
|
40
41
|
from pyglet.app.base import EventLoop
|
|
41
42
|
|
|
42
|
-
_is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run
|
|
43
43
|
|
|
44
|
-
if
|
|
44
|
+
if pyglet.IS_DOC_BUILD:
|
|
45
45
|
from pyglet.app.base import PlatformEventLoop
|
|
46
46
|
else:
|
|
47
47
|
if compat_platform == 'darwin':
|
|
@@ -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':
|
|
@@ -79,7 +82,11 @@ def run(interval: float | None = 1 / 60) -> None:
|
|
|
79
82
|
|
|
80
83
|
pyglet.app.event_loop.run(interval)
|
|
81
84
|
|
|
85
|
+
..note: This convenience function also adds a SIGINT signal handler
|
|
86
|
+
to automatically call ``pyglet.app.exit`` when this signal
|
|
87
|
+
is detected.
|
|
82
88
|
"""
|
|
89
|
+
signal.signal(signal.SIGINT, lambda _s, _f: event_loop.exit())
|
|
83
90
|
event_loop.run(interval)
|
|
84
91
|
|
|
85
92
|
|
pyglet/app/base.py
CHANGED
|
@@ -7,13 +7,12 @@ from abc import ABC, abstractmethod
|
|
|
7
7
|
from queue import Queue
|
|
8
8
|
from typing import TYPE_CHECKING, Any, Callable
|
|
9
9
|
|
|
10
|
-
from pyglet import app, clock, event
|
|
10
|
+
from pyglet import app, clock, event, IS_DOC_BUILD
|
|
11
11
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
13
13
|
from pyglet.event import EventDispatcher
|
|
14
14
|
from pyglet.window import BaseWindow
|
|
15
15
|
|
|
16
|
-
_is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
class WindowDrawSource(ABC):
|
|
@@ -406,7 +405,7 @@ class EventLoop(event.EventDispatcher):
|
|
|
406
405
|
if len(app.windows) == 0:
|
|
407
406
|
self.exit()
|
|
408
407
|
|
|
409
|
-
if
|
|
408
|
+
if IS_DOC_BUILD:
|
|
410
409
|
# Events
|
|
411
410
|
|
|
412
411
|
def on_window_close(self, window: BaseWindow) -> None:
|
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/__init__.py
CHANGED
|
@@ -4,6 +4,8 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from typing import TYPE_CHECKING, NamedTuple
|
|
6
6
|
|
|
7
|
+
import pyglet
|
|
8
|
+
|
|
7
9
|
from pyglet.enums import GraphicsAPI
|
|
8
10
|
|
|
9
11
|
from .base import SurfaceConfig, UserConfig # noqa: TC001
|
|
@@ -23,7 +25,7 @@ class Config:
|
|
|
23
25
|
|
|
24
26
|
__slots__ = 'gl2', 'gles2', 'gles3', 'opengl', 'vulkan', 'webgl'
|
|
25
27
|
|
|
26
|
-
def __init__(self):
|
|
28
|
+
def __init__(self) -> None: # noqa: D107
|
|
27
29
|
self.opengl: OpenGLUserConfig = OpenGLUserConfig(major_version=3, minor_version=3, api=GraphicsAPI.OPENGL)
|
|
28
30
|
self.gl2: OpenGLUserConfig = OpenGLUserConfig(major_version=2, minor_version=0, api=GraphicsAPI.OPENGL_2)
|
|
29
31
|
self.gles2: OpenGLUserConfig = OpenGLUserConfig(major_version=2, minor_version=0, api=GraphicsAPI.OPENGL_ES_2)
|
|
@@ -33,9 +35,15 @@ class Config:
|
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
def match_surface_config(config: UserConfig, surface: Window) -> SurfaceConfig | None:
|
|
36
|
-
if isinstance(config,
|
|
38
|
+
if isinstance(config, OpenGLUserConfig):
|
|
37
39
|
from pyglet.config.gl import get_surface_config # noqa: PLC0415
|
|
38
40
|
return get_surface_config(config, surface)
|
|
39
41
|
|
|
42
|
+
if isinstance(config, WebGLUserConfig):
|
|
43
|
+
if pyglet.compat_platform == 'emscripten':
|
|
44
|
+
from pyglet.config.gl.webgl import match # noqa: PLC0415
|
|
45
|
+
return match(config, surface)
|
|
46
|
+
return None
|
|
47
|
+
|
|
40
48
|
msg = f"Matching for '{config.__class__}' is not yet implemented."
|
|
41
49
|
raise NotImplementedError(msg)
|
pyglet/config/gl/__init__.py
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING, cast
|
|
4
5
|
|
|
5
6
|
import pyglet
|
|
6
7
|
|
|
7
8
|
from pyglet.enums import GraphicsAPI
|
|
8
9
|
from pyglet.config.base import UserConfig, SurfaceConfig
|
|
9
10
|
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from pyglet.window.apple.cocoa import CocoaWindow
|
|
13
|
+
from pyglet.window.apple.ios import IOSWindow
|
|
14
|
+
from pyglet.window.headless import HeadlessWindow
|
|
15
|
+
from pyglet.window.wayland import WaylandWindow
|
|
16
|
+
from pyglet.window.win32 import Win32Window
|
|
17
|
+
from pyglet.window.xlib import XlibWindow
|
|
18
|
+
|
|
10
19
|
|
|
11
20
|
class GLSurfaceConfig(SurfaceConfig):
|
|
12
21
|
config: OpenGLUserConfig
|
|
@@ -98,31 +107,31 @@ class WebGLUserConfig(UserConfig):
|
|
|
98
107
|
return False
|
|
99
108
|
|
|
100
109
|
|
|
101
|
-
def get_surface_config(user_config:
|
|
110
|
+
def get_surface_config(user_config: OpenGLUserConfig, surface: pyglet.window.Window) -> SurfaceConfig | None:
|
|
102
111
|
|
|
103
112
|
if pyglet.options.headless or pyglet.options.wayland:
|
|
104
|
-
from pyglet.config.gl.egl import match
|
|
113
|
+
from pyglet.config.gl.egl import match as match_egl # noqa: PLC0415
|
|
105
114
|
|
|
106
|
-
return
|
|
115
|
+
return match_egl(user_config, cast("HeadlessWindow | WaylandWindow", surface))
|
|
107
116
|
|
|
108
117
|
if pyglet.compat_platform == "win32":
|
|
109
|
-
from pyglet.config.gl.windows import match # noqa: PLC0415
|
|
118
|
+
from pyglet.config.gl.windows import match as match_windows # noqa: PLC0415
|
|
110
119
|
|
|
111
|
-
return
|
|
120
|
+
return match_windows(user_config, cast("Win32Window", surface))
|
|
112
121
|
|
|
113
122
|
if pyglet.compat_platform.startswith("linux"):
|
|
114
|
-
from pyglet.config.gl.x11 import match # noqa: PLC0415
|
|
123
|
+
from pyglet.config.gl.x11 import match as match_x11 # noqa: PLC0415
|
|
115
124
|
|
|
116
|
-
return
|
|
125
|
+
return match_x11(user_config, cast("XlibWindow", surface))
|
|
117
126
|
|
|
118
127
|
if pyglet.compat_platform == "darwin":
|
|
119
|
-
from pyglet.config.gl.macos import match # noqa: PLC0415
|
|
128
|
+
from pyglet.config.gl.macos import match as match_macos # noqa: PLC0415
|
|
120
129
|
|
|
121
|
-
return
|
|
130
|
+
return match_macos(user_config, cast("CocoaWindow", surface))
|
|
122
131
|
|
|
123
|
-
if pyglet.compat_platform == "
|
|
124
|
-
from pyglet.config.gl.
|
|
132
|
+
if pyglet.compat_platform == "ios":
|
|
133
|
+
from pyglet.config.gl.ios import match as match_ios # noqa: PLC0415
|
|
125
134
|
|
|
126
|
-
return
|
|
135
|
+
return match_ios(user_config, cast("IOSWindow", surface))
|
|
127
136
|
|
|
128
137
|
return None
|