pyglet 2.1.12__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.
- pyglet/__init__.py +67 -61
- pyglet/__init__.pyi +15 -8
- pyglet/app/__init__.py +22 -13
- pyglet/app/async_app.py +212 -0
- pyglet/app/base.py +2 -1
- pyglet/app/{xlib.py → linux.py} +3 -3
- pyglet/config/__init__.py +101 -0
- pyglet/config/gl/__init__.py +30 -0
- pyglet/config/gl/egl.py +120 -0
- pyglet/config/gl/macos.py +262 -0
- pyglet/config/gl/windows.py +267 -0
- pyglet/config/gl/x11.py +142 -0
- pyglet/customtypes.py +43 -2
- pyglet/display/__init__.py +8 -6
- pyglet/display/base.py +3 -63
- pyglet/display/cocoa.py +12 -17
- pyglet/display/emscripten.py +39 -0
- pyglet/display/headless.py +23 -30
- pyglet/display/wayland.py +157 -0
- pyglet/display/win32.py +4 -17
- pyglet/display/xlib.py +19 -27
- pyglet/display/xlib_vidmoderestore.py +2 -2
- pyglet/enums.py +183 -0
- pyglet/event.py +0 -1
- pyglet/experimental/geoshader_sprite.py +15 -13
- pyglet/experimental/hidraw.py +6 -15
- pyglet/experimental/multitexture_sprite.py +31 -19
- pyglet/experimental/particles.py +13 -35
- pyglet/font/__init__.py +251 -85
- pyglet/font/base.py +116 -61
- pyglet/font/dwrite/__init__.py +349 -204
- pyglet/font/dwrite/dwrite_lib.py +27 -5
- pyglet/font/fontconfig.py +14 -6
- pyglet/font/freetype.py +138 -87
- pyglet/font/freetype_lib.py +19 -0
- pyglet/font/group.py +179 -0
- pyglet/font/harfbuzz/__init__.py +3 -3
- pyglet/font/pyodide_js.py +310 -0
- pyglet/font/quartz.py +319 -126
- pyglet/font/ttf.py +45 -3
- pyglet/font/user.py +14 -19
- pyglet/font/win32.py +45 -21
- pyglet/graphics/__init__.py +8 -787
- pyglet/graphics/allocation.py +115 -1
- pyglet/graphics/api/__init__.py +77 -0
- pyglet/graphics/api/base.py +299 -0
- pyglet/graphics/api/gl/__init__.py +58 -0
- pyglet/graphics/api/gl/base.py +24 -0
- pyglet/graphics/{vertexbuffer.py → api/gl/buffer.py} +104 -159
- pyglet/graphics/api/gl/cocoa/context.py +76 -0
- pyglet/graphics/api/gl/context.py +391 -0
- pyglet/graphics/api/gl/default_shaders.py +0 -0
- pyglet/graphics/api/gl/draw.py +627 -0
- pyglet/graphics/api/gl/egl/__init__.py +0 -0
- pyglet/graphics/api/gl/egl/context.py +92 -0
- pyglet/graphics/api/gl/enums.py +76 -0
- pyglet/graphics/api/gl/framebuffer.py +315 -0
- pyglet/graphics/api/gl/gl.py +5463 -0
- pyglet/graphics/api/gl/gl_info.py +188 -0
- pyglet/graphics/api/gl/global_opengl.py +226 -0
- pyglet/{gl → graphics/api/gl}/lib.py +34 -18
- pyglet/graphics/api/gl/shader.py +1476 -0
- pyglet/graphics/api/gl/shapes.py +55 -0
- pyglet/graphics/api/gl/sprite.py +102 -0
- pyglet/graphics/api/gl/state.py +219 -0
- pyglet/graphics/api/gl/text.py +190 -0
- pyglet/graphics/api/gl/texture.py +1526 -0
- pyglet/graphics/{vertexarray.py → api/gl/vertexarray.py} +11 -13
- pyglet/graphics/api/gl/vertexdomain.py +751 -0
- pyglet/graphics/api/gl/win32/__init__.py +0 -0
- pyglet/graphics/api/gl/win32/context.py +108 -0
- pyglet/graphics/api/gl/win32/wgl_info.py +24 -0
- pyglet/graphics/api/gl/xlib/__init__.py +0 -0
- pyglet/graphics/api/gl/xlib/context.py +174 -0
- pyglet/{gl → graphics/api/gl/xlib}/glx_info.py +26 -31
- pyglet/graphics/api/gl1/__init__.py +0 -0
- pyglet/{gl → graphics/api/gl1}/gl_compat.py +3 -2
- pyglet/graphics/api/gl2/__init__.py +0 -0
- pyglet/graphics/api/gl2/buffer.py +320 -0
- pyglet/graphics/api/gl2/draw.py +600 -0
- pyglet/graphics/api/gl2/global_opengl.py +122 -0
- pyglet/graphics/api/gl2/shader.py +200 -0
- pyglet/graphics/api/gl2/shapes.py +51 -0
- pyglet/graphics/api/gl2/sprite.py +79 -0
- pyglet/graphics/api/gl2/text.py +175 -0
- pyglet/graphics/api/gl2/vertexdomain.py +364 -0
- pyglet/graphics/api/webgl/__init__.py +233 -0
- pyglet/graphics/api/webgl/buffer.py +302 -0
- pyglet/graphics/api/webgl/context.py +234 -0
- pyglet/graphics/api/webgl/draw.py +590 -0
- pyglet/graphics/api/webgl/enums.py +76 -0
- pyglet/graphics/api/webgl/framebuffer.py +360 -0
- pyglet/graphics/api/webgl/gl.py +1537 -0
- pyglet/graphics/api/webgl/gl_info.py +130 -0
- pyglet/graphics/api/webgl/shader.py +1346 -0
- pyglet/graphics/api/webgl/shapes.py +92 -0
- pyglet/graphics/api/webgl/sprite.py +102 -0
- pyglet/graphics/api/webgl/state.py +227 -0
- pyglet/graphics/api/webgl/text.py +187 -0
- pyglet/graphics/api/webgl/texture.py +1227 -0
- pyglet/graphics/api/webgl/vertexarray.py +54 -0
- pyglet/graphics/api/webgl/vertexdomain.py +616 -0
- pyglet/graphics/api/webgl/webgl_js.pyi +307 -0
- pyglet/{image → graphics}/atlas.py +33 -32
- pyglet/graphics/base.py +10 -0
- pyglet/graphics/buffer.py +245 -0
- pyglet/graphics/draw.py +578 -0
- pyglet/graphics/framebuffer.py +26 -0
- pyglet/graphics/instance.py +178 -69
- pyglet/graphics/shader.py +267 -1553
- pyglet/graphics/state.py +83 -0
- pyglet/graphics/texture.py +703 -0
- pyglet/graphics/vertexdomain.py +695 -538
- pyglet/gui/ninepatch.py +10 -10
- pyglet/gui/widgets.py +120 -10
- pyglet/image/__init__.py +20 -1973
- pyglet/image/animation.py +12 -12
- pyglet/image/base.py +730 -0
- pyglet/image/codecs/__init__.py +9 -0
- pyglet/image/codecs/bmp.py +53 -30
- pyglet/image/codecs/dds.py +53 -31
- pyglet/image/codecs/gdiplus.py +38 -14
- pyglet/image/codecs/gdkpixbuf2.py +0 -2
- pyglet/image/codecs/js_image.py +99 -0
- pyglet/image/codecs/ktx2.py +161 -0
- pyglet/image/codecs/pil.py +1 -1
- pyglet/image/codecs/png.py +1 -1
- pyglet/image/codecs/wic.py +11 -2
- pyglet/info.py +26 -24
- pyglet/input/__init__.py +8 -0
- pyglet/input/base.py +163 -105
- pyglet/input/controller.py +13 -19
- pyglet/input/controller_db.py +39 -24
- pyglet/input/emscripten/__init__.py +18 -0
- pyglet/input/emscripten/gamepad_js.py +397 -0
- pyglet/input/linux/__init__.py +11 -5
- pyglet/input/linux/evdev.py +10 -11
- pyglet/input/linux/x11_xinput.py +2 -2
- pyglet/input/linux/x11_xinput_tablet.py +1 -1
- pyglet/input/macos/__init__.py +7 -2
- pyglet/input/macos/darwin_gc.py +559 -0
- pyglet/input/win32/__init__.py +1 -1
- pyglet/input/win32/directinput.py +34 -29
- pyglet/input/win32/xinput.py +11 -61
- pyglet/lib.py +3 -3
- pyglet/libs/__init__.py +1 -1
- pyglet/{gl → libs/darwin}/agl.py +1 -1
- pyglet/libs/darwin/cocoapy/__init__.py +2 -2
- pyglet/libs/darwin/cocoapy/cocoahelpers.py +181 -0
- pyglet/libs/darwin/cocoapy/cocoalibs.py +31 -0
- pyglet/libs/darwin/cocoapy/cocoatypes.py +27 -0
- pyglet/libs/darwin/cocoapy/runtime.py +81 -45
- pyglet/libs/darwin/coreaudio.py +4 -4
- pyglet/{gl → libs/darwin}/lib_agl.py +9 -8
- pyglet/libs/darwin/quartzkey.py +1 -3
- pyglet/libs/egl/__init__.py +2 -0
- pyglet/libs/egl/egl_lib.py +576 -0
- pyglet/libs/egl/eglext.py +51 -5
- pyglet/libs/linux/__init__.py +0 -0
- pyglet/libs/linux/egl/__init__.py +0 -0
- pyglet/libs/linux/egl/eglext.py +22 -0
- pyglet/libs/linux/glx/__init__.py +0 -0
- pyglet/{gl → libs/linux/glx}/glx.py +13 -14
- pyglet/{gl → libs/linux/glx}/glxext_arb.py +408 -192
- pyglet/{gl → libs/linux/glx}/glxext_mesa.py +1 -1
- pyglet/{gl → libs/linux/glx}/glxext_nv.py +345 -164
- pyglet/{gl → libs/linux/glx}/lib_glx.py +3 -2
- pyglet/libs/linux/wayland/__init__.py +0 -0
- pyglet/libs/linux/wayland/client.py +1068 -0
- pyglet/libs/linux/wayland/lib_wayland.py +207 -0
- pyglet/libs/linux/wayland/wayland_egl.py +38 -0
- pyglet/libs/{wayland → linux/wayland}/xkbcommon.py +26 -0
- pyglet/libs/{x11 → linux/x11}/xf86vmode.py +4 -4
- pyglet/libs/{x11 → linux/x11}/xinerama.py +2 -2
- pyglet/libs/{x11 → linux/x11}/xinput.py +10 -10
- pyglet/libs/linux/x11/xrandr.py +0 -0
- pyglet/libs/{x11 → linux/x11}/xrender.py +1 -1
- pyglet/libs/shared/__init__.py +0 -0
- pyglet/libs/shared/spirv/__init__.py +0 -0
- pyglet/libs/shared/spirv/lib_shaderc.py +85 -0
- pyglet/libs/shared/spirv/lib_spirv_cross.py +126 -0
- pyglet/libs/win32/__init__.py +27 -5
- pyglet/libs/win32/constants.py +59 -48
- pyglet/libs/win32/context_managers.py +20 -3
- pyglet/libs/win32/dinput.py +105 -88
- pyglet/{gl → libs/win32}/lib_wgl.py +52 -26
- pyglet/libs/win32/types.py +58 -23
- pyglet/{gl → libs/win32}/wgl.py +32 -25
- pyglet/{gl → libs/win32}/wglext_arb.py +364 -2
- pyglet/media/__init__.py +9 -10
- pyglet/media/codecs/__init__.py +12 -1
- pyglet/media/codecs/base.py +99 -96
- pyglet/media/codecs/ffmpeg.py +2 -2
- pyglet/media/codecs/ffmpeg_lib/libavformat.py +3 -8
- pyglet/media/codecs/webaudio_pyodide.py +111 -0
- pyglet/media/drivers/__init__.py +9 -4
- pyglet/media/drivers/base.py +4 -4
- pyglet/media/drivers/openal/__init__.py +1 -1
- pyglet/media/drivers/openal/adaptation.py +3 -3
- pyglet/media/drivers/pulse/__init__.py +1 -1
- pyglet/media/drivers/pulse/adaptation.py +3 -3
- pyglet/media/drivers/pyodide_js/__init__.py +8 -0
- pyglet/media/drivers/pyodide_js/adaptation.py +288 -0
- pyglet/media/drivers/xaudio2/adaptation.py +3 -3
- pyglet/media/player.py +276 -193
- pyglet/media/player_worker_thread.py +1 -1
- pyglet/model/__init__.py +39 -29
- pyglet/model/codecs/base.py +4 -4
- pyglet/model/codecs/gltf.py +3 -3
- pyglet/model/codecs/obj.py +71 -43
- pyglet/resource.py +129 -78
- pyglet/shapes.py +147 -177
- pyglet/sprite.py +47 -164
- pyglet/text/__init__.py +44 -54
- pyglet/text/caret.py +12 -7
- pyglet/text/document.py +19 -17
- pyglet/text/formats/html.py +2 -2
- pyglet/text/formats/structured.py +10 -40
- pyglet/text/layout/__init__.py +20 -13
- pyglet/text/layout/base.py +176 -287
- pyglet/text/layout/incremental.py +9 -10
- pyglet/text/layout/scrolling.py +7 -95
- pyglet/window/__init__.py +183 -172
- pyglet/window/cocoa/__init__.py +62 -51
- pyglet/window/cocoa/pyglet_delegate.py +2 -25
- pyglet/window/cocoa/pyglet_view.py +9 -8
- pyglet/window/dialog/__init__.py +184 -0
- pyglet/window/dialog/base.py +99 -0
- pyglet/window/dialog/darwin.py +121 -0
- pyglet/window/dialog/linux.py +72 -0
- pyglet/window/dialog/windows.py +194 -0
- pyglet/window/emscripten/__init__.py +779 -0
- pyglet/window/headless/__init__.py +44 -28
- pyglet/window/key.py +2 -0
- pyglet/window/mouse.py +2 -2
- pyglet/window/wayland/__init__.py +377 -0
- pyglet/window/win32/__init__.py +101 -46
- pyglet/window/xlib/__init__.py +104 -66
- {pyglet-2.1.12.dist-info → pyglet-3.0.dev1.dist-info}/METADATA +2 -3
- pyglet-3.0.dev1.dist-info/RECORD +322 -0
- {pyglet-2.1.12.dist-info → pyglet-3.0.dev1.dist-info}/WHEEL +1 -1
- pyglet/gl/__init__.py +0 -208
- pyglet/gl/base.py +0 -499
- pyglet/gl/cocoa.py +0 -309
- pyglet/gl/gl.py +0 -4625
- pyglet/gl/gl.pyi +0 -2320
- pyglet/gl/gl_compat.pyi +0 -3097
- pyglet/gl/gl_info.py +0 -190
- pyglet/gl/headless.py +0 -166
- pyglet/gl/wgl_info.py +0 -36
- pyglet/gl/wglext_nv.py +0 -1096
- pyglet/gl/win32.py +0 -268
- pyglet/gl/xlib.py +0 -295
- pyglet/image/buffer.py +0 -274
- pyglet/image/codecs/s3tc.py +0 -354
- pyglet/libs/x11/xrandr.py +0 -166
- pyglet-2.1.12.dist-info/RECORD +0 -234
- /pyglet/{libs/wayland → graphics/api/gl/cocoa}/__init__.py +0 -0
- /pyglet/libs/{egl → linux/egl}/egl.py +0 -0
- /pyglet/libs/{egl → linux/egl}/lib.py +0 -0
- /pyglet/libs/{ioctl.py → linux/ioctl.py} +0 -0
- /pyglet/libs/{wayland → linux/wayland}/gbm.py +0 -0
- /pyglet/libs/{x11 → linux/x11}/__init__.py +0 -0
- /pyglet/libs/{x11 → linux/x11}/cursorfont.py +0 -0
- /pyglet/libs/{x11 → linux/x11}/xlib.py +0 -0
- /pyglet/libs/{x11 → linux/x11}/xsync.py +0 -0
- {pyglet-2.1.12.dist-info/licenses → pyglet-3.0.dev1.dist-info}/LICENSE +0 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ctypes
|
|
4
|
+
import sys
|
|
5
|
+
from typing import Sequence, TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
import pyglet
|
|
8
|
+
from pyglet.graphics.api.gl.global_opengl import OpenGLBackend
|
|
9
|
+
from pyglet.graphics.api.base import WindowTransformations
|
|
10
|
+
from pyglet.graphics.api.gl2.shader import ShaderProgram, Shader
|
|
11
|
+
|
|
12
|
+
from pyglet.math import Mat4
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from _ctypes import Array
|
|
16
|
+
from pyglet.graphics.shader import ShaderType
|
|
17
|
+
from pyglet.window import Window
|
|
18
|
+
|
|
19
|
+
_is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class OpenGL2_Matrices(WindowTransformations):
|
|
23
|
+
def __init__(self, window: Window, backend: OpenGL2Backend):
|
|
24
|
+
self.backend = backend
|
|
25
|
+
self._viewport = (0, 0, *window.get_framebuffer_size())
|
|
26
|
+
|
|
27
|
+
width, height = window.get_size()
|
|
28
|
+
super().__init__(window, Mat4.orthogonal_projection(0, width, 0, height, -255, 255), Mat4(), Mat4())
|
|
29
|
+
|
|
30
|
+
self.projection = self._projection
|
|
31
|
+
self.view = self._view
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def _get_mat_float_array(matrix: Mat4) -> Array[ctypes.c_float]:
|
|
35
|
+
return (ctypes.c_float * 16)(*matrix)
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def projection(self) -> Mat4:
|
|
39
|
+
return self._projection
|
|
40
|
+
|
|
41
|
+
@projection.setter
|
|
42
|
+
def projection(self, projection: Mat4) -> None:
|
|
43
|
+
self._projection = projection
|
|
44
|
+
|
|
45
|
+
projection_array = tuple(self._projection)
|
|
46
|
+
for program in self.backend.current_context.cached_programs.values():
|
|
47
|
+
with program:
|
|
48
|
+
program["u_projection"] = projection_array
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def view(self) -> Mat4:
|
|
52
|
+
return self._view
|
|
53
|
+
|
|
54
|
+
@view.setter
|
|
55
|
+
def view(self, view: Mat4) -> None:
|
|
56
|
+
self._view = view
|
|
57
|
+
view_array = tuple(self._view)
|
|
58
|
+
for program in self.backend.current_context.cached_programs.values():
|
|
59
|
+
with program:
|
|
60
|
+
program["u_view"] = view_array
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def model(self) -> Mat4:
|
|
64
|
+
return self._model
|
|
65
|
+
|
|
66
|
+
@model.setter
|
|
67
|
+
def model(self, model: Mat4) -> None:
|
|
68
|
+
self._model = model
|
|
69
|
+
|
|
70
|
+
class OpenGL2Backend(OpenGLBackend):
|
|
71
|
+
|
|
72
|
+
def get_default_configs(self) -> Sequence[pyglet.config.OpenGLConfig]:
|
|
73
|
+
"""A sequence of configs to use if the user does not specify any.
|
|
74
|
+
|
|
75
|
+
These will be used during Window creation.
|
|
76
|
+
"""
|
|
77
|
+
return [
|
|
78
|
+
pyglet.config.OpenGLConfig(double_buffer=True, depth_size=24, major_version=2, minor_version=0,
|
|
79
|
+
opengl_api=self.gl_api),
|
|
80
|
+
pyglet.config.OpenGLConfig(double_buffer=True, depth_size=16, major_version=2, minor_version=0,
|
|
81
|
+
opengl_api=self.gl_api),
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
def get_cached_shader(self, name: str, *sources: tuple[str, ShaderType]) -> ShaderProgram:
|
|
85
|
+
"""Create a ShaderProgram from OpenGL GLSL source.
|
|
86
|
+
|
|
87
|
+
This is a convenience method that takes one or more tuples of
|
|
88
|
+
(source_string, shader_type), and returns a
|
|
89
|
+
:py:class:`~pyglet.graphics.ShaderProgram` instance.
|
|
90
|
+
|
|
91
|
+
``source_string`` is OpenGL GLSL source code as a str, and ``shader_type``
|
|
92
|
+
is the OpenGL shader type, such as "vertex" or "fragment". See
|
|
93
|
+
:py:class:`~pyglet.graphics.Shader` for more information.
|
|
94
|
+
|
|
95
|
+
.. note:: This method is cached. Given the same shader sources, the
|
|
96
|
+
same ShaderProgram instance will be returned. For more
|
|
97
|
+
control over the ShaderProgram lifecycle, it is recommended
|
|
98
|
+
to manually create Shaders and link ShaderPrograms.
|
|
99
|
+
|
|
100
|
+
.. versionadded:: 2.0.10
|
|
101
|
+
"""
|
|
102
|
+
assert self.current_context
|
|
103
|
+
assert isinstance(name, str), "First argument must be a string name for the shader."
|
|
104
|
+
|
|
105
|
+
if program := self.current_context.cached_programs.get(name):
|
|
106
|
+
return program
|
|
107
|
+
|
|
108
|
+
shaders = (Shader(src, srctype) for (src, srctype) in sources)
|
|
109
|
+
program = ShaderProgram(*shaders)
|
|
110
|
+
self.current_context.cached_programs[name] = program
|
|
111
|
+
|
|
112
|
+
# With GLES it doesn't support glMatrixMode's and no UBO's.
|
|
113
|
+
# Set the projection for each shader manually.
|
|
114
|
+
if not self.current_context.window._shadow: # Do not set if it's the shadow window? # noqa: SLF001
|
|
115
|
+
with program:
|
|
116
|
+
program["u_projection"] = tuple(self.current_context.window._matrices.projection)
|
|
117
|
+
program["u_view"] = tuple(self.current_context.window._matrices.view)
|
|
118
|
+
|
|
119
|
+
return program
|
|
120
|
+
|
|
121
|
+
def initialize_matrices(self, window: Window) -> OpenGL2_Matrices:
|
|
122
|
+
return OpenGL2_Matrices(window, self)
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from ctypes import (
|
|
4
|
+
Structure,
|
|
5
|
+
c_byte,
|
|
6
|
+
c_double,
|
|
7
|
+
c_float,
|
|
8
|
+
c_int,
|
|
9
|
+
c_short,
|
|
10
|
+
c_ubyte,
|
|
11
|
+
c_uint,
|
|
12
|
+
c_ushort,
|
|
13
|
+
)
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
15
|
+
|
|
16
|
+
import pyglet
|
|
17
|
+
import pyglet.graphics.api.gl.gl as gl
|
|
18
|
+
from pyglet.graphics.api.gl.shader import GLDataType
|
|
19
|
+
from pyglet.graphics.api.gl.shader import Shader as GLShader
|
|
20
|
+
from pyglet.graphics.api.gl.shader import ShaderProgram as GLShaderProgram
|
|
21
|
+
from pyglet.graphics.shader import ShaderException, ShaderSource, ShaderType
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from pyglet.graphics.api.gl import OpenGLSurfaceContext
|
|
25
|
+
from pyglet.customtypes import CType, DataTypes
|
|
26
|
+
|
|
27
|
+
_debug_api_shaders = pyglet.options.debug_api_shaders
|
|
28
|
+
|
|
29
|
+
_c_types: dict[int, CType] = {
|
|
30
|
+
gl.GL_BYTE: c_byte,
|
|
31
|
+
gl.GL_UNSIGNED_BYTE: c_ubyte,
|
|
32
|
+
gl.GL_SHORT: c_short,
|
|
33
|
+
gl.GL_UNSIGNED_SHORT: c_ushort,
|
|
34
|
+
gl.GL_INT: c_int,
|
|
35
|
+
gl.GL_UNSIGNED_INT: c_uint,
|
|
36
|
+
gl.GL_FLOAT: c_float,
|
|
37
|
+
gl.GL_DOUBLE: c_double,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
_data_type_to_gl_type: dict[DataTypes, int] = {
|
|
41
|
+
'b': gl.GL_BYTE, # signed byte
|
|
42
|
+
'B': gl.GL_UNSIGNED_BYTE, # unsigned byte
|
|
43
|
+
'h': gl.GL_SHORT, # signed short
|
|
44
|
+
'H': gl.GL_UNSIGNED_SHORT, # unsigned short
|
|
45
|
+
'i': gl.GL_INT, # signed int
|
|
46
|
+
'I': gl.GL_UNSIGNED_INT, # unsigned int
|
|
47
|
+
'f': gl.GL_FLOAT, # float
|
|
48
|
+
'q': gl.GL_INT, # signed long long (Requires GL_INT64_NV if available. Just use int.)
|
|
49
|
+
'Q': gl.GL_UNSIGNED_INT, # unsigned long long (Requires GL_UNSIGNED_INT64_NV if available. Just use uint.)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class UniformBlock:
|
|
54
|
+
"""Not supported by OpenGL 2.0."""
|
|
55
|
+
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
program: ShaderProgram,
|
|
59
|
+
name: str,
|
|
60
|
+
index: int,
|
|
61
|
+
size: int,
|
|
62
|
+
binding: int,
|
|
63
|
+
uniforms: dict[int, tuple[str, GLDataType, int, int]],
|
|
64
|
+
uniform_count: int,
|
|
65
|
+
) -> None:
|
|
66
|
+
raise NotImplementedError
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class UniformBufferObject:
|
|
70
|
+
"""Not supported by OpenGL 2.0."""
|
|
71
|
+
|
|
72
|
+
def __init__(self, view_class: type[Structure], buffer_size: int, binding: int) -> None:
|
|
73
|
+
"""Initialize the Uniform Buffer Object with the specified Structure."""
|
|
74
|
+
raise NotImplementedError
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# Shader & program classes:
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class GLShaderSource(ShaderSource):
|
|
83
|
+
"""GLSL source container for making source parsing simpler.
|
|
84
|
+
|
|
85
|
+
We support locating out attributes and applying #defines values.
|
|
86
|
+
|
|
87
|
+
.. note:: We do assume the source is neat enough to be parsed this way and doesn't contain several statements in
|
|
88
|
+
one line.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
_type: gl.GLenum
|
|
92
|
+
_lines: list[str]
|
|
93
|
+
|
|
94
|
+
def __init__(self, source: str, source_type: gl.GLenum | int) -> None:
|
|
95
|
+
"""Create a shader source wrapper."""
|
|
96
|
+
self._lines = source.strip().splitlines()
|
|
97
|
+
self._type = source_type
|
|
98
|
+
|
|
99
|
+
if not self._lines:
|
|
100
|
+
msg = "Shader source is empty"
|
|
101
|
+
raise ShaderException(msg)
|
|
102
|
+
|
|
103
|
+
self._version = self._find_glsl_version()
|
|
104
|
+
|
|
105
|
+
if "es" in pyglet.options.backend:
|
|
106
|
+
self._patch_gles()
|
|
107
|
+
|
|
108
|
+
self._patch_legacy()
|
|
109
|
+
|
|
110
|
+
def _patch_legacy(self) -> None:
|
|
111
|
+
"""Replace any legacy GL2 matrices with our uniform implementation."""
|
|
112
|
+
if self._type == gl.GL_VERTEX_SHADER:
|
|
113
|
+
replaced = False
|
|
114
|
+
for idx, line in enumerate(self._lines):
|
|
115
|
+
if "gl_ProjectionMatrix" in line or "gl_ModelViewMatrix" in line:
|
|
116
|
+
self._lines[idx] = line.replace("gl_ProjectionMatrix", "u_projection").replace(
|
|
117
|
+
"gl_ModelViewMatrix",
|
|
118
|
+
"u_view",
|
|
119
|
+
)
|
|
120
|
+
replaced = True
|
|
121
|
+
|
|
122
|
+
if replaced:
|
|
123
|
+
self._lines.insert(1, "uniform mat4 u_projection;")
|
|
124
|
+
self._lines.insert(2, "uniform mat4 u_view;")
|
|
125
|
+
|
|
126
|
+
def _patch_gles(self) -> None:
|
|
127
|
+
"""Patch the built-in shaders for GLES support.
|
|
128
|
+
|
|
129
|
+
Probably will need better handling to not affect user shaders.
|
|
130
|
+
"""
|
|
131
|
+
if self._lines[0].strip().startswith("#version"):
|
|
132
|
+
self._lines[0] = ""
|
|
133
|
+
|
|
134
|
+
self._lines.insert(0, "precision mediump float;")
|
|
135
|
+
|
|
136
|
+
self._version = "es 1.00"
|
|
137
|
+
|
|
138
|
+
def validate(self) -> str:
|
|
139
|
+
"""Return the validated shader source."""
|
|
140
|
+
return "\n".join(self._lines)
|
|
141
|
+
|
|
142
|
+
def _find_glsl_version(self) -> int:
|
|
143
|
+
if self._lines[0].strip().startswith("#version"):
|
|
144
|
+
try:
|
|
145
|
+
return int(self._lines[0].split()[1])
|
|
146
|
+
except (ValueError, IndexError):
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
source = "\n".join(f"{str(i + 1).zfill(3)}: {line} " for i, line in enumerate(self._lines))
|
|
150
|
+
|
|
151
|
+
msg = (
|
|
152
|
+
"Cannot find #version flag in shader source. "
|
|
153
|
+
"A #version statement is required on the first line.\n"
|
|
154
|
+
"------------------------------------\n"
|
|
155
|
+
f"{source}"
|
|
156
|
+
)
|
|
157
|
+
raise ShaderException(msg)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class Shader(GLShader):
|
|
161
|
+
"""OpenGL shader.
|
|
162
|
+
|
|
163
|
+
Shader objects are compiled on instantiation.
|
|
164
|
+
You can reuse a Shader object in multiple ShaderPrograms.
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
_context: OpenGLSurfaceContext | None
|
|
168
|
+
_id: int | None
|
|
169
|
+
type: ShaderType
|
|
170
|
+
|
|
171
|
+
@classmethod
|
|
172
|
+
def supported_shaders(cls) -> tuple[ShaderType, ...]:
|
|
173
|
+
return 'vertex', 'fragment'
|
|
174
|
+
|
|
175
|
+
@staticmethod
|
|
176
|
+
def get_string_class() -> type[GLShaderSource]:
|
|
177
|
+
return GLShaderSource
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class ShaderProgram(GLShaderProgram): # noqa: D101
|
|
181
|
+
_uniform_blocks: None
|
|
182
|
+
__slots__ = '_attributes', '_context', '_id', '_uniform_blocks', '_uniforms'
|
|
183
|
+
|
|
184
|
+
def __init__(self, *shaders: Shader) -> None:
|
|
185
|
+
"""Initialize the ShaderProgram using at least two Shader instances."""
|
|
186
|
+
super().__init__(*shaders)
|
|
187
|
+
|
|
188
|
+
def _get_uniform_blocks(self) -> None:
|
|
189
|
+
"""Return Uniform Block information."""
|
|
190
|
+
return
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class ComputeShaderProgram:
|
|
194
|
+
"""OpenGL Compute Shader Program.
|
|
195
|
+
|
|
196
|
+
Not supported by OpenGL 2.0
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
def __init__(self, source: str) -> None:
|
|
200
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import pyglet
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from pyglet.graphics.api import ShaderProgram
|
|
7
|
+
|
|
8
|
+
vertex_source = """#version 110
|
|
9
|
+
attribute vec2 position;
|
|
10
|
+
attribute vec3 translation;
|
|
11
|
+
attribute vec4 colors;
|
|
12
|
+
attribute float rotation;
|
|
13
|
+
|
|
14
|
+
varying vec4 vertex_colors;
|
|
15
|
+
|
|
16
|
+
mat4 m_rotation = mat4(1.0);
|
|
17
|
+
mat4 m_translate = mat4(1.0);
|
|
18
|
+
|
|
19
|
+
uniform mat4 u_projection;
|
|
20
|
+
uniform mat4 u_view;
|
|
21
|
+
|
|
22
|
+
void main()
|
|
23
|
+
{
|
|
24
|
+
m_translate[3][0] = translation.x;
|
|
25
|
+
m_translate[3][1] = translation.y;
|
|
26
|
+
m_rotation[0][0] = cos(-radians(rotation));
|
|
27
|
+
m_rotation[0][1] = sin(-radians(rotation));
|
|
28
|
+
m_rotation[1][0] = -sin(-radians(rotation));
|
|
29
|
+
m_rotation[1][1] = cos(-radians(rotation));
|
|
30
|
+
|
|
31
|
+
gl_Position = u_projection * u_view * m_translate * m_rotation * vec4(position, translation.z, 1.0);
|
|
32
|
+
vertex_colors = colors;
|
|
33
|
+
}
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
fragment_source = """#version 150 core
|
|
37
|
+
varying vec4 vertex_colors;
|
|
38
|
+
|
|
39
|
+
void main()
|
|
40
|
+
{
|
|
41
|
+
gl_FragColor = vertex_colors;
|
|
42
|
+
}
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_default_shader() -> ShaderProgram:
|
|
47
|
+
return pyglet.graphics.api.core.get_cached_shader(
|
|
48
|
+
"default_shapes",
|
|
49
|
+
(vertex_source, 'vertex'),
|
|
50
|
+
(fragment_source, 'fragment'),
|
|
51
|
+
)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
import pyglet
|
|
7
|
+
|
|
8
|
+
_is_pyglet_doc_run = hasattr(sys, 'is_pyglet_doc_run') and sys.is_pyglet_doc_run
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from pyglet.graphics import ShaderProgram
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
vertex_source: str = """#version 110
|
|
15
|
+
attribute vec3 translate;
|
|
16
|
+
attribute vec4 colors;
|
|
17
|
+
attribute vec3 tex_coords;
|
|
18
|
+
attribute vec2 scale;
|
|
19
|
+
attribute vec3 position;
|
|
20
|
+
attribute float rotation;
|
|
21
|
+
|
|
22
|
+
varying vec4 vertex_colors;
|
|
23
|
+
varying vec3 texture_coords;
|
|
24
|
+
|
|
25
|
+
uniform mat4 u_projection;
|
|
26
|
+
uniform mat4 u_view;
|
|
27
|
+
|
|
28
|
+
mat4 m_scale = mat4(1.0);
|
|
29
|
+
mat4 m_rotation = mat4(1.0);
|
|
30
|
+
mat4 m_translate = mat4(1.0);
|
|
31
|
+
|
|
32
|
+
void main()
|
|
33
|
+
{
|
|
34
|
+
m_scale[0][0] = scale.x;
|
|
35
|
+
m_scale[1][1] = scale.y;
|
|
36
|
+
m_translate[3][0] = translate.x;
|
|
37
|
+
m_translate[3][1] = translate.y;
|
|
38
|
+
m_translate[3][2] = translate.z;
|
|
39
|
+
m_rotation[0][0] = cos(-radians(rotation));
|
|
40
|
+
m_rotation[0][1] = sin(-radians(rotation));
|
|
41
|
+
m_rotation[1][0] = -sin(-radians(rotation));
|
|
42
|
+
m_rotation[1][1] = cos(-radians(rotation));
|
|
43
|
+
|
|
44
|
+
gl_Position = u_projection * u_view * m_translate * m_rotation * m_scale * vec4(position, 1.0);
|
|
45
|
+
|
|
46
|
+
vertex_colors = colors;
|
|
47
|
+
texture_coords = tex_coords;
|
|
48
|
+
}
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
fragment_source: str = """#version 110
|
|
52
|
+
varying vec4 vertex_colors;
|
|
53
|
+
varying vec3 texture_coords;
|
|
54
|
+
|
|
55
|
+
uniform sampler2D sprite_texture;
|
|
56
|
+
|
|
57
|
+
void main()
|
|
58
|
+
{
|
|
59
|
+
gl_FragColor = texture2D(sprite_texture, texture_coords.xy) * vertex_colors;
|
|
60
|
+
}
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def get_default_shader() -> ShaderProgram:
|
|
64
|
+
"""Create and return the default sprite shader.
|
|
65
|
+
|
|
66
|
+
This method allows the module to be imported without an OpenGL Context.
|
|
67
|
+
"""
|
|
68
|
+
return pyglet.graphics.api.core.get_cached_shader(
|
|
69
|
+
"default_sprite",
|
|
70
|
+
(vertex_source, 'vertex'),
|
|
71
|
+
(fragment_source, 'fragment'),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def get_default_array_shader() -> ShaderProgram:
|
|
75
|
+
"""Create and return the default array sprite shader.
|
|
76
|
+
|
|
77
|
+
This method allows the module to be imported without an OpenGL Context.
|
|
78
|
+
"""
|
|
79
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import pyglet
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from pyglet.graphics import ShaderProgram
|
|
9
|
+
|
|
10
|
+
layout_vertex_source = """#version 110
|
|
11
|
+
attribute vec3 position;
|
|
12
|
+
attribute vec4 colors;
|
|
13
|
+
attribute vec3 tex_coords;
|
|
14
|
+
attribute vec3 translation;
|
|
15
|
+
attribute vec3 view_translation;
|
|
16
|
+
attribute vec2 anchor;
|
|
17
|
+
attribute float rotation;
|
|
18
|
+
attribute float visible;
|
|
19
|
+
|
|
20
|
+
uniform mat4 u_projection;
|
|
21
|
+
uniform mat4 u_view;
|
|
22
|
+
|
|
23
|
+
varying vec4 text_colors;
|
|
24
|
+
varying vec2 texture_coords;
|
|
25
|
+
varying vec4 vert_position;
|
|
26
|
+
|
|
27
|
+
void main()
|
|
28
|
+
{
|
|
29
|
+
mat4 m_rotation = mat4(1.0);
|
|
30
|
+
vec3 v_anchor = vec3(anchor.x, anchor.y, 0);
|
|
31
|
+
mat4 m_anchor = mat4(1.0);
|
|
32
|
+
mat4 m_translate = mat4(1.0);
|
|
33
|
+
|
|
34
|
+
m_translate[3][0] = translation.x;
|
|
35
|
+
m_translate[3][1] = translation.y;
|
|
36
|
+
m_translate[3][2] = translation.z;
|
|
37
|
+
|
|
38
|
+
m_rotation[0][0] = cos(-radians(rotation));
|
|
39
|
+
m_rotation[0][1] = sin(-radians(rotation));
|
|
40
|
+
m_rotation[1][0] = -sin(-radians(rotation));
|
|
41
|
+
m_rotation[1][1] = cos(-radians(rotation));
|
|
42
|
+
|
|
43
|
+
gl_Position = u_projection * u_view * m_translate * m_anchor * m_rotation * vec4(position +
|
|
44
|
+
view_translation + v_anchor, 1.0) * visible;
|
|
45
|
+
|
|
46
|
+
vert_position = vec4(position + translation + view_translation + v_anchor, 1.0);
|
|
47
|
+
text_colors = colors;
|
|
48
|
+
texture_coords = tex_coords.xy;
|
|
49
|
+
}
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
layout_fragment_source = """#version 110
|
|
53
|
+
varying vec4 text_colors;
|
|
54
|
+
varying vec2 texture_coords;
|
|
55
|
+
varying vec4 vert_position;
|
|
56
|
+
|
|
57
|
+
uniform sampler2D text;
|
|
58
|
+
uniform bool scissor;
|
|
59
|
+
uniform vec4 scissor_area;
|
|
60
|
+
|
|
61
|
+
void main()
|
|
62
|
+
{
|
|
63
|
+
gl_FragColor = vec4(text_colors.rgb, texture2D(text, texture_coords).a * text_colors.a);
|
|
64
|
+
if (scissor == true) {
|
|
65
|
+
if (vert_position.x < scissor_area[0]) discard; // left
|
|
66
|
+
if (vert_position.y < scissor_area[1]) discard; // bottom
|
|
67
|
+
if (vert_position.x > scissor_area[0] + scissor_area[2]) discard; // right
|
|
68
|
+
if (vert_position.y > scissor_area[1] + scissor_area[3]) discard; // top
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
"""
|
|
72
|
+
layout_fragment_image_source = """#version 110
|
|
73
|
+
varying vec4 text_colors;
|
|
74
|
+
varying vec2 texture_coords;
|
|
75
|
+
varying vec4 vert_position;
|
|
76
|
+
|
|
77
|
+
uniform sampler2D image_texture;
|
|
78
|
+
|
|
79
|
+
uniform bool scissor;
|
|
80
|
+
uniform vec4 scissor_area;
|
|
81
|
+
|
|
82
|
+
void main()
|
|
83
|
+
{
|
|
84
|
+
gl_FragColor = texture2D(image_texture, texture_coords.xy);
|
|
85
|
+
if (scissor == true) {
|
|
86
|
+
if (vert_position.x < scissor_area[0]) discard; // left
|
|
87
|
+
if (vert_position.y < scissor_area[1]) discard; // bottom
|
|
88
|
+
if (vert_position.x > scissor_area[0] + scissor_area[2]) discard; // right
|
|
89
|
+
if (vert_position.y > scissor_area[1] + scissor_area[3]) discard; // top
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
"""
|
|
93
|
+
decoration_vertex_source = """#version 110
|
|
94
|
+
attribute vec3 position;
|
|
95
|
+
attribute vec4 colors;
|
|
96
|
+
attribute vec3 translation;
|
|
97
|
+
attribute vec3 view_translation;
|
|
98
|
+
attribute vec2 anchor;
|
|
99
|
+
attribute float rotation;
|
|
100
|
+
attribute float visible;
|
|
101
|
+
|
|
102
|
+
varying vec4 vert_colors;
|
|
103
|
+
varying vec4 vert_position;
|
|
104
|
+
|
|
105
|
+
uniform mat4 u_projection;
|
|
106
|
+
uniform mat4 u_view;
|
|
107
|
+
|
|
108
|
+
void main()
|
|
109
|
+
{
|
|
110
|
+
mat4 m_rotation = mat4(1.0);
|
|
111
|
+
vec3 v_anchor = vec3(anchor.x, anchor.y, 0);
|
|
112
|
+
mat4 m_anchor = mat4(1.0);
|
|
113
|
+
mat4 m_translate = mat4(1.0);
|
|
114
|
+
|
|
115
|
+
m_translate[3][0] = translation.x;
|
|
116
|
+
m_translate[3][1] = translation.y;
|
|
117
|
+
m_translate[3][2] = translation.z;
|
|
118
|
+
|
|
119
|
+
m_rotation[0][0] = cos(-radians(rotation));
|
|
120
|
+
m_rotation[0][1] = sin(-radians(rotation));
|
|
121
|
+
m_rotation[1][0] = -sin(-radians(rotation));
|
|
122
|
+
m_rotation[1][1] = cos(-radians(rotation));
|
|
123
|
+
|
|
124
|
+
gl_Position = u_projection * u_view * m_translate * m_anchor * m_rotation * vec4(position +
|
|
125
|
+
view_translation + v_anchor, 1.0) * visible;
|
|
126
|
+
|
|
127
|
+
vert_position = vec4(position + translation + view_translation + v_anchor, 1.0);
|
|
128
|
+
vert_colors = colors;
|
|
129
|
+
}
|
|
130
|
+
"""
|
|
131
|
+
decoration_fragment_source = """#version 110
|
|
132
|
+
varying vec4 vert_colors;
|
|
133
|
+
varying vec4 vert_position;
|
|
134
|
+
|
|
135
|
+
uniform bool scissor;
|
|
136
|
+
uniform vec4 scissor_area;
|
|
137
|
+
|
|
138
|
+
void main()
|
|
139
|
+
{
|
|
140
|
+
gl_FragColor = vert_colors;
|
|
141
|
+
if (scissor == true) {
|
|
142
|
+
if (vert_position.x < scissor_area[0]) discard; // left
|
|
143
|
+
if (vert_position.y < scissor_area[1]) discard; // bottom
|
|
144
|
+
if (vert_position.x > scissor_area[0] + scissor_area[2]) discard; // right
|
|
145
|
+
if (vert_position.y > scissor_area[1] + scissor_area[3]) discard; // top
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def get_default_layout_shader() -> ShaderProgram:
|
|
152
|
+
"""The default shader used for all glyphs in the layout."""
|
|
153
|
+
return pyglet.graphics.api.core.get_cached_shader(
|
|
154
|
+
"default_text_layout",
|
|
155
|
+
(layout_vertex_source, "vertex"),
|
|
156
|
+
(layout_fragment_source, "fragment"),
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def get_default_image_layout_shader() -> ShaderProgram:
|
|
161
|
+
"""The default shader used for an InlineElement image. Used for HTML Labels that insert images via <img> tag."""
|
|
162
|
+
return pyglet.graphics.api.core.get_cached_shader(
|
|
163
|
+
"default_text_image",
|
|
164
|
+
(layout_vertex_source, "vertex"),
|
|
165
|
+
(layout_fragment_image_source, "fragment"),
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def get_default_decoration_shader() -> ShaderProgram:
|
|
170
|
+
"""The default shader for underline and background decoration effects in the layout."""
|
|
171
|
+
return pyglet.graphics.api.core.get_cached_shader(
|
|
172
|
+
"default_text_decoration",
|
|
173
|
+
(decoration_vertex_source, "vertex"),
|
|
174
|
+
(decoration_fragment_source, "fragment"),
|
|
175
|
+
)
|