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,779 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from functools import lru_cache
|
|
4
|
+
from typing import TYPE_CHECKING, Callable
|
|
5
|
+
|
|
6
|
+
import js
|
|
7
|
+
from pyodide.ffi import create_proxy
|
|
8
|
+
|
|
9
|
+
import pyglet.app
|
|
10
|
+
from pyglet.window import BaseWindow, DefaultMouseCursor, ImageMouseCursor, MouseCursor, key, mouse
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from pyglet.display.base import Display, Screen, ScreenMode
|
|
14
|
+
from pyglet.graphics.api import GraphicsConfig
|
|
15
|
+
from pyglet.graphics.api.base import WindowGraphicsContext
|
|
16
|
+
|
|
17
|
+
# Keymap using `event.code`
|
|
18
|
+
_key_map = {
|
|
19
|
+
# Alpha
|
|
20
|
+
"a": key.A,
|
|
21
|
+
"b": key.B,
|
|
22
|
+
"c": key.C,
|
|
23
|
+
"d": key.D,
|
|
24
|
+
"e": key.E,
|
|
25
|
+
"f": key.F,
|
|
26
|
+
"g": key.G,
|
|
27
|
+
"h": key.H,
|
|
28
|
+
"i": key.I,
|
|
29
|
+
"j": key.J,
|
|
30
|
+
"k": key.K,
|
|
31
|
+
"l": key.L,
|
|
32
|
+
"m": key.M,
|
|
33
|
+
"n": key.N,
|
|
34
|
+
"o": key.O,
|
|
35
|
+
"p": key.P,
|
|
36
|
+
"q": key.Q,
|
|
37
|
+
"r": key.R,
|
|
38
|
+
"s": key.S,
|
|
39
|
+
"t": key.T,
|
|
40
|
+
"u": key.U,
|
|
41
|
+
"v": key.V,
|
|
42
|
+
"w": key.W,
|
|
43
|
+
"x": key.X,
|
|
44
|
+
"y": key.Y,
|
|
45
|
+
"z": key.Z,
|
|
46
|
+
"A": key.A,
|
|
47
|
+
"B": key.B,
|
|
48
|
+
"C": key.C,
|
|
49
|
+
"D": key.D,
|
|
50
|
+
"E": key.E,
|
|
51
|
+
"F": key.F,
|
|
52
|
+
"G": key.G,
|
|
53
|
+
"H": key.H,
|
|
54
|
+
"I": key.I,
|
|
55
|
+
"J": key.J,
|
|
56
|
+
"K": key.K,
|
|
57
|
+
"L": key.L,
|
|
58
|
+
"M": key.M,
|
|
59
|
+
"N": key.N,
|
|
60
|
+
"O": key.O,
|
|
61
|
+
"P": key.P,
|
|
62
|
+
"Q": key.Q,
|
|
63
|
+
"R": key.R,
|
|
64
|
+
"S": key.S,
|
|
65
|
+
"T": key.T,
|
|
66
|
+
"U": key.U,
|
|
67
|
+
"V": key.V,
|
|
68
|
+
"W": key.W,
|
|
69
|
+
"X": key.X,
|
|
70
|
+
"Y": key.Y,
|
|
71
|
+
"Z": key.Z,
|
|
72
|
+
# Numeric
|
|
73
|
+
"0": key._0,
|
|
74
|
+
"1": key._1,
|
|
75
|
+
"2": key._2,
|
|
76
|
+
"3": key._3,
|
|
77
|
+
"4": key._4,
|
|
78
|
+
"5": key._5,
|
|
79
|
+
"6": key._6,
|
|
80
|
+
"7": key._7,
|
|
81
|
+
"8": key._8,
|
|
82
|
+
"9": key._9,
|
|
83
|
+
# Whitespace and control keys
|
|
84
|
+
"Backspace": key.BACKSPACE,
|
|
85
|
+
"Tab": key.TAB,
|
|
86
|
+
"Enter": key.RETURN,
|
|
87
|
+
"Escape": key.ESCAPE,
|
|
88
|
+
" ": key.SPACE,
|
|
89
|
+
# Navication keys
|
|
90
|
+
"Insert": key.INSERT,
|
|
91
|
+
"Home": key.HOME,
|
|
92
|
+
"End": key.END,
|
|
93
|
+
"PageUp": key.PAGEUP,
|
|
94
|
+
"PageDown": key.PAGEDOWN,
|
|
95
|
+
"ArrowLeft": key.LEFT,
|
|
96
|
+
"ArrowUp": key.UP,
|
|
97
|
+
"ArrowRight": key.RIGHT,
|
|
98
|
+
"ArrowDown": key.DOWN,
|
|
99
|
+
# Modifier keys
|
|
100
|
+
"Shift": key.LSHIFT,
|
|
101
|
+
"ShiftLeft": key.LSHIFT,
|
|
102
|
+
"ShiftRight": key.RSHIFT,
|
|
103
|
+
"Control": key.LCTRL,
|
|
104
|
+
"ControlLeft": key.LCTRL,
|
|
105
|
+
"ControlRight": key.RCTRL,
|
|
106
|
+
"Alt": key.LALT,
|
|
107
|
+
"AltLeft": key.LALT,
|
|
108
|
+
"AltRight": key.RALT,
|
|
109
|
+
"Meta": key.LMETA,
|
|
110
|
+
"MetaLeft": key.LMETA,
|
|
111
|
+
"MetaRight": key.RMETA,
|
|
112
|
+
"Pause": key.PAUSE,
|
|
113
|
+
"PrintScreen": key.PRINT,
|
|
114
|
+
"ContextMenu": key.MENU,
|
|
115
|
+
# Locks
|
|
116
|
+
"CapsLock": key.CAPSLOCK,
|
|
117
|
+
"NumLock": key.NUMLOCK,
|
|
118
|
+
"ScrollLock": key.SCROLLLOCK,
|
|
119
|
+
# Function keys
|
|
120
|
+
"F1": key.F1,
|
|
121
|
+
"F2": key.F2,
|
|
122
|
+
"F3": key.F3,
|
|
123
|
+
"F4": key.F4,
|
|
124
|
+
"F5": key.F5,
|
|
125
|
+
"F6": key.F6,
|
|
126
|
+
"F7": key.F7,
|
|
127
|
+
"F8": key.F8,
|
|
128
|
+
"F9": key.F9,
|
|
129
|
+
"F10": key.F10,
|
|
130
|
+
"F11": key.F11,
|
|
131
|
+
"F12": key.F12,
|
|
132
|
+
"Numpad0": key.NUM_0,
|
|
133
|
+
"Numpad1": key.NUM_1,
|
|
134
|
+
"Numpad2": key.NUM_2,
|
|
135
|
+
"Numpad3": key.NUM_3,
|
|
136
|
+
"Numpad4": key.NUM_4,
|
|
137
|
+
"Numpad5": key.NUM_5,
|
|
138
|
+
"Numpad6": key.NUM_6,
|
|
139
|
+
"Numpad7": key.NUM_7,
|
|
140
|
+
"Numpad8": key.NUM_8,
|
|
141
|
+
"Numpad9": key.NUM_9,
|
|
142
|
+
"NumpadMultiply": key.NUM_MULTIPLY,
|
|
143
|
+
"NumpadAdd": key.NUM_ADD,
|
|
144
|
+
"NumpadSubtract": key.NUM_SUBTRACT,
|
|
145
|
+
"NumpadDecimal": key.NUM_DECIMAL,
|
|
146
|
+
"NumpadDivide": key.NUM_DIVIDE,
|
|
147
|
+
"NumpadEnter": key.NUM_ENTER,
|
|
148
|
+
"!": key.EXCLAMATION,
|
|
149
|
+
"@": key.AT,
|
|
150
|
+
"#": key.HASH,
|
|
151
|
+
"$": key.DOLLAR,
|
|
152
|
+
"%": key.PERCENT,
|
|
153
|
+
"^": key.ASCIICIRCUM,
|
|
154
|
+
"&": key.AMPERSAND,
|
|
155
|
+
"*": key.ASTERISK,
|
|
156
|
+
"(": key.PARENLEFT,
|
|
157
|
+
")": key.PARENRIGHT,
|
|
158
|
+
"-": key.MINUS,
|
|
159
|
+
"_": key.UNDERSCORE,
|
|
160
|
+
"=": key.EQUAL,
|
|
161
|
+
"+": key.PLUS,
|
|
162
|
+
"[": key.BRACKETLEFT,
|
|
163
|
+
"]": key.BRACKETRIGHT,
|
|
164
|
+
"{": key.BRACELEFT,
|
|
165
|
+
"}": key.BRACERIGHT,
|
|
166
|
+
";": key.SEMICOLON,
|
|
167
|
+
":": key.COLON,
|
|
168
|
+
"'": key.APOSTROPHE,
|
|
169
|
+
"\"": key.DOUBLEQUOTE,
|
|
170
|
+
"\\": key.BACKSLASH,
|
|
171
|
+
"|": key.BAR,
|
|
172
|
+
",": key.COMMA,
|
|
173
|
+
".": key.PERIOD,
|
|
174
|
+
"/": key.SLASH,
|
|
175
|
+
"?": key.QUESTION,
|
|
176
|
+
"`": key.GRAVE,
|
|
177
|
+
"~": key.ASCIITILDE,
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
# symbol,ctrl -> motion mapping
|
|
181
|
+
_motion_map: dict[tuple[int, bool], int] = {
|
|
182
|
+
(key.UP, False): key.MOTION_UP,
|
|
183
|
+
(key.RIGHT, False): key.MOTION_RIGHT,
|
|
184
|
+
(key.DOWN, False): key.MOTION_DOWN,
|
|
185
|
+
(key.LEFT, False): key.MOTION_LEFT,
|
|
186
|
+
(key.RIGHT, True): key.MOTION_NEXT_WORD,
|
|
187
|
+
(key.LEFT, True): key.MOTION_PREVIOUS_WORD,
|
|
188
|
+
(key.HOME, False): key.MOTION_BEGINNING_OF_LINE,
|
|
189
|
+
(key.END, False): key.MOTION_END_OF_LINE,
|
|
190
|
+
(key.PAGEUP, False): key.MOTION_PREVIOUS_PAGE,
|
|
191
|
+
(key.PAGEDOWN, False): key.MOTION_NEXT_PAGE,
|
|
192
|
+
(key.HOME, True): key.MOTION_BEGINNING_OF_FILE,
|
|
193
|
+
(key.END, True): key.MOTION_END_OF_FILE,
|
|
194
|
+
(key.BACKSPACE, False): key.MOTION_BACKSPACE,
|
|
195
|
+
(key.DELETE, False): key.MOTION_DELETE,
|
|
196
|
+
(key.C, True): key.MOTION_COPY,
|
|
197
|
+
(key.V, True): key.MOTION_PASTE,
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def get_modifiers(event) -> int:
|
|
202
|
+
"""Extract modifier flags from a JavaScript event."""
|
|
203
|
+
modifiers = 0
|
|
204
|
+
|
|
205
|
+
if event.ctrlKey:
|
|
206
|
+
modifiers |= key.MOD_CTRL
|
|
207
|
+
if event.shiftKey:
|
|
208
|
+
modifiers |= key.MOD_SHIFT
|
|
209
|
+
if event.altKey:
|
|
210
|
+
modifiers |= key.MOD_ALT
|
|
211
|
+
modifiers |= key.MOD_OPTION # Alt is also "Option" on macOS
|
|
212
|
+
if hasattr(event, "metaKey") and event.metaKey: # Meta = Command on macOS
|
|
213
|
+
modifiers |= key.MOD_COMMAND
|
|
214
|
+
|
|
215
|
+
return modifiers
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
# Convert JavaScript key events to your format
|
|
219
|
+
def js_key_to_pyglet(event):
|
|
220
|
+
"""Convert JS event code to the equivalent key mapping."""
|
|
221
|
+
modifiers = 0
|
|
222
|
+
|
|
223
|
+
if event.key in _key_map:
|
|
224
|
+
symbol = _key_map[event.key]
|
|
225
|
+
# Doesn't exist in either mapping. Unknown.
|
|
226
|
+
else:
|
|
227
|
+
symbol = event.code if event.code else 0
|
|
228
|
+
print(f"Warning: Unmapped key -> {symbol} ({event.key})")
|
|
229
|
+
|
|
230
|
+
# Handle modifiers
|
|
231
|
+
if event.ctrlKey:
|
|
232
|
+
modifiers |= key.MOD_CTRL
|
|
233
|
+
if event.shiftKey:
|
|
234
|
+
modifiers |= key.MOD_SHIFT
|
|
235
|
+
if event.altKey:
|
|
236
|
+
modifiers |= key.MOD_ALT
|
|
237
|
+
modifiers |= key.MOD_OPTION # Alt is also "Option" on macOS
|
|
238
|
+
if hasattr(event, "metaKey") and event.metaKey: # Meta = Command on macOS
|
|
239
|
+
modifiers |= key.MOD_COMMAND
|
|
240
|
+
|
|
241
|
+
return symbol, modifiers
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
# Javascript Buttons Bitwise
|
|
245
|
+
JS_MOUSE_LEFT_BIT = 1 << 0 # 1
|
|
246
|
+
JS_MOUSE_RIGHT_BIT = 1 << 1 # 2
|
|
247
|
+
JS_MOUSE_MIDDLE_BIT = 1 << 2 # 4
|
|
248
|
+
JS_MOUSE_BACK_BIT = 1 << 3 # 8
|
|
249
|
+
JS_MOUSE_FORWARD_BIT = 1 << 4 # 16
|
|
250
|
+
|
|
251
|
+
JS_MOUSE_LEFT = 0
|
|
252
|
+
JS_MOUSE_MIDDLE = 1
|
|
253
|
+
JS_MOUSE_RIGHT = 2
|
|
254
|
+
JS_MOUSE_BACK = 3 # Mouse 4
|
|
255
|
+
JS_MOUSE_FORWARD = 4 # Mouse 5
|
|
256
|
+
|
|
257
|
+
# Normal button presses are not bitwise.
|
|
258
|
+
_mouse_map = {
|
|
259
|
+
JS_MOUSE_LEFT: mouse.LEFT,
|
|
260
|
+
JS_MOUSE_MIDDLE: mouse.MIDDLE,
|
|
261
|
+
JS_MOUSE_RIGHT: mouse.RIGHT,
|
|
262
|
+
JS_MOUSE_BACK: mouse.MOUSE4,
|
|
263
|
+
JS_MOUSE_FORWARD: mouse.MOUSE5,
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def translate_mouse_bits(buttons: int) -> int:
|
|
268
|
+
"""Translate JavaScript mouse button values to match pyglet constants."""
|
|
269
|
+
result = 0
|
|
270
|
+
if buttons & JS_MOUSE_LEFT_BIT: # JavaScript Left button
|
|
271
|
+
result |= mouse.LEFT
|
|
272
|
+
if buttons & JS_MOUSE_RIGHT_BIT: # JavaScript Right button
|
|
273
|
+
result |= mouse.RIGHT
|
|
274
|
+
if buttons & JS_MOUSE_MIDDLE_BIT: # JavaScript Middle button (wheel)
|
|
275
|
+
result |= mouse.MIDDLE
|
|
276
|
+
if buttons & JS_MOUSE_BACK_BIT: # JavaScript Back (X1)
|
|
277
|
+
result |= mouse.MOUSE4
|
|
278
|
+
if buttons & JS_MOUSE_FORWARD_BIT: # JavaScript Forward (X2)
|
|
279
|
+
result |= mouse.MOUSE5
|
|
280
|
+
return result
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def CanvasEventHandler(name: str, passive: None| bool=None) -> Callable:
|
|
284
|
+
def _event_wrapper(f: Callable) -> Callable:
|
|
285
|
+
f._canvas = True
|
|
286
|
+
f._passive = passive
|
|
287
|
+
f._platform_event = True # noqa: SLF001
|
|
288
|
+
if not hasattr(f, '_platform_event_data'):
|
|
289
|
+
f._platform_event_data = [] # noqa: SLF001
|
|
290
|
+
f._platform_event_data.append(name) # noqa: SLF001
|
|
291
|
+
return f
|
|
292
|
+
|
|
293
|
+
return _event_wrapper
|
|
294
|
+
|
|
295
|
+
# The Canvas JS object.
|
|
296
|
+
class HTMLCanvasElement:
|
|
297
|
+
...
|
|
298
|
+
|
|
299
|
+
class JavascriptCursor(MouseCursor):
|
|
300
|
+
api_drawable: bool = False
|
|
301
|
+
hw_drawable: bool = True
|
|
302
|
+
|
|
303
|
+
def __init__(self, name: str):
|
|
304
|
+
self.name = name
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
# Temporary
|
|
308
|
+
class EmscriptenWindow(BaseWindow):
|
|
309
|
+
"""The HTML5 Canvas."""
|
|
310
|
+
|
|
311
|
+
_mouse_cursor: JavascriptCursor
|
|
312
|
+
|
|
313
|
+
def __init__(
|
|
314
|
+
self,
|
|
315
|
+
width: int | None = None,
|
|
316
|
+
height: int | None = None,
|
|
317
|
+
caption: str | None = None,
|
|
318
|
+
resizable: bool = False,
|
|
319
|
+
style: str | None = None,
|
|
320
|
+
fullscreen: bool = False,
|
|
321
|
+
visible: bool = True,
|
|
322
|
+
vsync: bool = True,
|
|
323
|
+
file_drops: bool = False,
|
|
324
|
+
display: Display | None = None,
|
|
325
|
+
screen: Screen | None = None,
|
|
326
|
+
config: GraphicsConfig | None = None,
|
|
327
|
+
context: WindowGraphicsContext | None = None,
|
|
328
|
+
mode: ScreenMode | None = None,
|
|
329
|
+
) -> None:
|
|
330
|
+
self._canvas = None
|
|
331
|
+
self._event_handlers: dict[int, Callable] = {}
|
|
332
|
+
self._canvas_event_handlers: dict[int, Callable] = {}
|
|
333
|
+
self._keys_down = set()
|
|
334
|
+
|
|
335
|
+
self._scale = js.window.devicePixelRatio
|
|
336
|
+
super().__init__(
|
|
337
|
+
width,
|
|
338
|
+
height,
|
|
339
|
+
caption,
|
|
340
|
+
resizable,
|
|
341
|
+
style,
|
|
342
|
+
fullscreen,
|
|
343
|
+
visible,
|
|
344
|
+
vsync,
|
|
345
|
+
file_drops,
|
|
346
|
+
display,
|
|
347
|
+
screen,
|
|
348
|
+
config,
|
|
349
|
+
context,
|
|
350
|
+
mode,
|
|
351
|
+
)
|
|
352
|
+
self._enable_event_queue = False
|
|
353
|
+
|
|
354
|
+
@property
|
|
355
|
+
def scale(self) -> float:
|
|
356
|
+
"""The scale of the window factoring in DPI.
|
|
357
|
+
|
|
358
|
+
Read only.
|
|
359
|
+
"""
|
|
360
|
+
return self._scale
|
|
361
|
+
|
|
362
|
+
@property
|
|
363
|
+
def dpi(self) -> int:
|
|
364
|
+
"""DPI values of the Window.
|
|
365
|
+
|
|
366
|
+
Read only.
|
|
367
|
+
"""
|
|
368
|
+
return int(self._scale * 96)
|
|
369
|
+
|
|
370
|
+
@property
|
|
371
|
+
def canvas(self) -> HTMLCanvasElement:
|
|
372
|
+
"""The underlying Javascript Canvas element, if available.
|
|
373
|
+
|
|
374
|
+
Read only.
|
|
375
|
+
"""
|
|
376
|
+
return self._canvas
|
|
377
|
+
|
|
378
|
+
def _set_event_handlers(self) -> None:
|
|
379
|
+
assert self._canvas, "Canvas has not been created."
|
|
380
|
+
for func_name in self._platform_event_names:
|
|
381
|
+
if not hasattr(self, func_name):
|
|
382
|
+
continue
|
|
383
|
+
func = getattr(self, func_name)
|
|
384
|
+
for message in func._platform_event_data: # noqa: SLF001
|
|
385
|
+
assert message not in self._event_handlers, f"event: '{message}' already exists."
|
|
386
|
+
proxy = create_proxy(func)
|
|
387
|
+
if hasattr(func, '_canvas'):
|
|
388
|
+
self._canvas_event_handlers[message] = func
|
|
389
|
+
if func._passive is not None:
|
|
390
|
+
# Not sure what behavior results if we pass None to the JS event handler.
|
|
391
|
+
self._canvas.addEventListener(message, proxy, passive=func._passive)
|
|
392
|
+
else:
|
|
393
|
+
self._canvas.addEventListener(message, proxy)
|
|
394
|
+
else:
|
|
395
|
+
self._event_handlers[message] = func
|
|
396
|
+
js.window.addEventListener(message, proxy)
|
|
397
|
+
|
|
398
|
+
self._proxy_resize = create_proxy(self._event_resized)
|
|
399
|
+
self._observer = js.ResizeObserver.new(self._proxy_resize)
|
|
400
|
+
self._observer.observe(self._canvas)
|
|
401
|
+
|
|
402
|
+
def set_fullscreen(
|
|
403
|
+
self,
|
|
404
|
+
fullscreen: bool = True,
|
|
405
|
+
_screen: Screen | None = None,
|
|
406
|
+
mode: ScreenMode | None = None,
|
|
407
|
+
width: int | None = None,
|
|
408
|
+
height: int | None = None,
|
|
409
|
+
) -> None:
|
|
410
|
+
if (
|
|
411
|
+
fullscreen == self._fullscreen
|
|
412
|
+
and (_screen is None or _screen is self._screen)
|
|
413
|
+
and (width is None or width == self._width)
|
|
414
|
+
and (height is None or height == self._height)
|
|
415
|
+
):
|
|
416
|
+
return
|
|
417
|
+
|
|
418
|
+
if not self._fullscreen:
|
|
419
|
+
self._windowed_size = self.get_size()
|
|
420
|
+
|
|
421
|
+
self._fullscreen = fullscreen
|
|
422
|
+
if self._fullscreen:
|
|
423
|
+
self._canvas.requestFullscreen()
|
|
424
|
+
else:
|
|
425
|
+
js.document.exitFullscreen()
|
|
426
|
+
|
|
427
|
+
def _enter_fullscreen(self, event):
|
|
428
|
+
self._fullscreen = True
|
|
429
|
+
scale = js.window.devicePixelRatio
|
|
430
|
+
self._canvas.width = js.window.innerWidth
|
|
431
|
+
self._canvas.height = js.window.innerHeight
|
|
432
|
+
|
|
433
|
+
def _exited_fullscreen(self, event, width: int | None = None, height: int | None = None):
|
|
434
|
+
self._fullscreen = False
|
|
435
|
+
self._width, self._height = self._windowed_size
|
|
436
|
+
self._canvas.width = self._width
|
|
437
|
+
self._canvas.height = self._height
|
|
438
|
+
if width is not None:
|
|
439
|
+
self._width = width
|
|
440
|
+
if height is not None:
|
|
441
|
+
self._height = height
|
|
442
|
+
|
|
443
|
+
def flip(self) -> None:
|
|
444
|
+
if self._context:
|
|
445
|
+
self._context.flip()
|
|
446
|
+
|
|
447
|
+
def switch_to(self) -> None:
|
|
448
|
+
pass
|
|
449
|
+
|
|
450
|
+
def before_draw(self) -> None:
|
|
451
|
+
if self._context:
|
|
452
|
+
self._context.before_draw()
|
|
453
|
+
|
|
454
|
+
def set_caption(self, caption: str) -> None:
|
|
455
|
+
js.document.title = caption
|
|
456
|
+
|
|
457
|
+
def set_minimum_size(self, width: int | str, height: int | str) -> None:
|
|
458
|
+
self._minimum_size = width, height
|
|
459
|
+
if isinstance(width, int) or isinstance(width, float):
|
|
460
|
+
width = f"{width}px"
|
|
461
|
+
|
|
462
|
+
if isinstance(width, int) or isinstance(width, float):
|
|
463
|
+
height = f"{width}px"
|
|
464
|
+
|
|
465
|
+
self._canvas.style.minWidth = width
|
|
466
|
+
self._canvas.style.minHeight = height
|
|
467
|
+
|
|
468
|
+
def set_maximum_size(self, width: int | str, height: int | str) -> None:
|
|
469
|
+
self._maximum_size = width, height
|
|
470
|
+
if isinstance(width, int) or isinstance(width, float):
|
|
471
|
+
width = f"{width}px"
|
|
472
|
+
|
|
473
|
+
if isinstance(width, int) or isinstance(width, float):
|
|
474
|
+
height = f"{width}px"
|
|
475
|
+
|
|
476
|
+
self._canvas.style.maxWidth = width
|
|
477
|
+
self._canvas.style.maxHeight = height
|
|
478
|
+
|
|
479
|
+
def set_size(self, width: int, height: int) -> None:
|
|
480
|
+
super().set_size(width, height)
|
|
481
|
+
self.adjust_scale(width, height)
|
|
482
|
+
|
|
483
|
+
def get_size(self) -> tuple[int, int]:
|
|
484
|
+
if not self._canvas:
|
|
485
|
+
return self._width, self._height
|
|
486
|
+
|
|
487
|
+
return self._canvas.width, self._canvas.height
|
|
488
|
+
|
|
489
|
+
def set_location(self, x: int, y: int) -> None:
|
|
490
|
+
# self.canvas.style.setProperty("position", "absolute")
|
|
491
|
+
self._canvas.style.setProperty("left", f"{x}px")
|
|
492
|
+
self._canvas.style.setProperty("top", f"{x}px")
|
|
493
|
+
|
|
494
|
+
def get_location(self) -> tuple[int, int]:
|
|
495
|
+
rect = self._canvas.getBoundingClientRect()
|
|
496
|
+
return rect.left, rect.top
|
|
497
|
+
|
|
498
|
+
def activate(self) -> None:
|
|
499
|
+
self._canvas.focus()
|
|
500
|
+
|
|
501
|
+
def set_visible(self, visible: bool = True) -> None:
|
|
502
|
+
if visible is False:
|
|
503
|
+
if self._canvas.style.visibility != "hidden":
|
|
504
|
+
self._canvas.visibility = "hidden"
|
|
505
|
+
else:
|
|
506
|
+
if self._canvas.style.visibility != "visible":
|
|
507
|
+
self._canvas.visibility = "visible"
|
|
508
|
+
|
|
509
|
+
def minimize(self) -> None:
|
|
510
|
+
"""While minimized, events will not occur."""
|
|
511
|
+
self._canvas.style.display = "hidden"
|
|
512
|
+
|
|
513
|
+
def maximize(self) -> None:
|
|
514
|
+
self._canvas.style.display = "block"
|
|
515
|
+
|
|
516
|
+
def set_vsync(self, vsync: bool) -> None:
|
|
517
|
+
"""A browser does not allow this."""
|
|
518
|
+
|
|
519
|
+
@lru_cache # noqa: B019
|
|
520
|
+
def _create_data_url_from_image(self, cursor: ImageMouseCursor) -> str:
|
|
521
|
+
"""Create a cursor image from the data.
|
|
522
|
+
|
|
523
|
+
Use a DATA URI as this allows converting the image into a cursor without fetching through HTTP.
|
|
524
|
+
|
|
525
|
+
This does need to be in a PNG encoded format, so a temporary canvas is created to encode.
|
|
526
|
+
"""
|
|
527
|
+
image = cursor.texture.get_image_data()
|
|
528
|
+
canvas = js.document.createElement("canvas")
|
|
529
|
+
|
|
530
|
+
canvas.width = image.width
|
|
531
|
+
canvas.height = image.height
|
|
532
|
+
data = image.get_bytes('RGBA')
|
|
533
|
+
pixel_array = js.Uint8ClampedArray.new(data)
|
|
534
|
+
|
|
535
|
+
ctx = canvas.getContext("2d")
|
|
536
|
+
image_data = ctx.createImageData(image.width, image.height)
|
|
537
|
+
image_data.data.set(pixel_array)
|
|
538
|
+
ctx.putImageData(image_data, 0, 0)
|
|
539
|
+
|
|
540
|
+
data_url = canvas.toDataURL("image/png")
|
|
541
|
+
canvas.remove()
|
|
542
|
+
return data_url
|
|
543
|
+
|
|
544
|
+
def set_mouse_cursor_platform_visible(self, platform_visible: bool | None = None) -> None:
|
|
545
|
+
if not self._canvas:
|
|
546
|
+
return
|
|
547
|
+
|
|
548
|
+
if platform_visible is None:
|
|
549
|
+
platform_visible = self._mouse_visible and (
|
|
550
|
+
not self._mouse_cursor.api_drawable or self._mouse_cursor.hw_drawable
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
if platform_visible is False:
|
|
554
|
+
cursor_name = "none"
|
|
555
|
+
elif isinstance(self._mouse_cursor, ImageMouseCursor) and self._mouse_cursor.hw_drawable:
|
|
556
|
+
# Create a custom hardware cursor.
|
|
557
|
+
cursor_name = self._create_data_url_from_image(self._mouse_cursor)
|
|
558
|
+
cursor_name = f"url({cursor_name}) {self._mouse_cursor.hot_x} {self._mouse_cursor.hot_y}, default"
|
|
559
|
+
else:
|
|
560
|
+
# Restore a standard hardware cursor
|
|
561
|
+
if isinstance(self._mouse_cursor, DefaultMouseCursor):
|
|
562
|
+
cursor_name = "default"
|
|
563
|
+
elif isinstance(self._mouse_cursor, JavascriptCursor):
|
|
564
|
+
cursor_name = self._mouse_cursor.name
|
|
565
|
+
|
|
566
|
+
self._canvas.style.cursor = cursor_name
|
|
567
|
+
|
|
568
|
+
def set_exclusive_mouse(self, exclusive: bool = True) -> None:
|
|
569
|
+
assert self._canvas
|
|
570
|
+
# Requires a user gesture to lock it like a button.
|
|
571
|
+
if exclusive is True:
|
|
572
|
+
self._canvas.requestPointerLock()
|
|
573
|
+
else:
|
|
574
|
+
self._canvas.exitPointerLock()
|
|
575
|
+
|
|
576
|
+
def set_exclusive_keyboard(self, exclusive: bool = True) -> None:
|
|
577
|
+
"""Not relevant."""
|
|
578
|
+
|
|
579
|
+
def get_system_mouse_cursor(self, name: str) -> JavascriptCursor:
|
|
580
|
+
if name == self.CURSOR_DEFAULT:
|
|
581
|
+
return DefaultMouseCursor()
|
|
582
|
+
|
|
583
|
+
return JavascriptCursor(name)
|
|
584
|
+
|
|
585
|
+
async def dispatch_events(self) -> None:
|
|
586
|
+
"""Process input events asynchronously."""
|
|
587
|
+
raise Exception("Not implemented.")
|
|
588
|
+
|
|
589
|
+
@staticmethod
|
|
590
|
+
def _event_text_motion(symbol: int, modifiers: int) -> int | None:
|
|
591
|
+
if modifiers & key.MOD_ALT:
|
|
592
|
+
return None
|
|
593
|
+
ctrl = modifiers & key.MOD_CTRL != 0
|
|
594
|
+
return _motion_map.get((symbol, ctrl), None)
|
|
595
|
+
|
|
596
|
+
@CanvasEventHandler("keydown")
|
|
597
|
+
def _event_key_down(self, event):
|
|
598
|
+
if not event.repeat:
|
|
599
|
+
text = None
|
|
600
|
+
if len(event.key) == 1:
|
|
601
|
+
text = event.key
|
|
602
|
+
symbol, modifiers = js_key_to_pyglet(event)
|
|
603
|
+
motion = self._event_text_motion(symbol, modifiers)
|
|
604
|
+
modifiers_ctrl = modifiers & (key.MOD_CTRL | key.MOD_ALT)
|
|
605
|
+
# If key A is pressed, then key B is pressed, if key A is released, key B will trigger a keydown.
|
|
606
|
+
if symbol in self._keys_down:
|
|
607
|
+
return
|
|
608
|
+
if symbol:
|
|
609
|
+
self._keys_down.add(symbol) # Keep track of pressed internally.
|
|
610
|
+
self.dispatch_event('on_key_press', symbol, modifiers)
|
|
611
|
+
if motion:
|
|
612
|
+
if modifiers & key.MOD_SHIFT:
|
|
613
|
+
motion_event = 'on_text_motion_select'
|
|
614
|
+
else:
|
|
615
|
+
motion_event = 'on_text_motion'
|
|
616
|
+
self.dispatch_event(motion_event, motion)
|
|
617
|
+
elif text and not modifiers_ctrl:
|
|
618
|
+
self.dispatch_event('on_text', text)
|
|
619
|
+
|
|
620
|
+
@CanvasEventHandler("keyup")
|
|
621
|
+
def _event_key_up(self, event):
|
|
622
|
+
symbol, modifiers = js_key_to_pyglet(event)
|
|
623
|
+
if symbol in self._keys_down:
|
|
624
|
+
self._keys_down.remove(symbol)
|
|
625
|
+
else:
|
|
626
|
+
js.console.log(f"{event.key} was released but was not down. This should not occur.")
|
|
627
|
+
|
|
628
|
+
self.dispatch_event('on_key_release', symbol, modifiers)
|
|
629
|
+
|
|
630
|
+
@CanvasEventHandler("mousedown")
|
|
631
|
+
def _event_mouse_down(self, event):
|
|
632
|
+
rect = self._canvas.getBoundingClientRect()
|
|
633
|
+
modifiers = get_modifiers(event)
|
|
634
|
+
pos_x = (event.clientX - rect.left) * self._scale
|
|
635
|
+
pos_y = self._canvas.height - (event.clientY - rect.top) * self._scale
|
|
636
|
+
self.dispatch_event(
|
|
637
|
+
'on_mouse_press', pos_x, pos_y, _mouse_map.get(event.button, 0), modifiers,
|
|
638
|
+
)
|
|
639
|
+
|
|
640
|
+
@CanvasEventHandler("mouseup")
|
|
641
|
+
def _event_mouse_up(self, event):
|
|
642
|
+
rect = self._canvas.getBoundingClientRect()
|
|
643
|
+
modifiers = get_modifiers(event)
|
|
644
|
+
pos_x = (event.clientX - rect.left) * self._scale
|
|
645
|
+
pos_y = self._canvas.height - (event.clientY - rect.top) * self._scale
|
|
646
|
+
self.dispatch_event(
|
|
647
|
+
'on_mouse_release', pos_x, pos_y, _mouse_map.get(event.button, 0), modifiers,
|
|
648
|
+
)
|
|
649
|
+
|
|
650
|
+
@CanvasEventHandler("mousemove")
|
|
651
|
+
async def _event_mouse_motion(self, event):
|
|
652
|
+
rect = self._canvas.getBoundingClientRect()
|
|
653
|
+
if event.buttons:
|
|
654
|
+
modifiers = get_modifiers(event)
|
|
655
|
+
pos_x = (event.clientX - rect.left) * self._scale
|
|
656
|
+
pos_y = self._canvas.height - (event.clientY - rect.top) * self._scale
|
|
657
|
+
self.dispatch_event(
|
|
658
|
+
'on_mouse_drag',
|
|
659
|
+
pos_x,
|
|
660
|
+
pos_y,
|
|
661
|
+
event.movementX,
|
|
662
|
+
event.movementY,
|
|
663
|
+
_mouse_map.get(event.button, 0),
|
|
664
|
+
modifiers,
|
|
665
|
+
)
|
|
666
|
+
else:
|
|
667
|
+
pos_x = (event.clientX - rect.left) * self._scale
|
|
668
|
+
pos_y = self._canvas.height - (event.clientY - rect.top) * self._scale
|
|
669
|
+
self.dispatch_event(
|
|
670
|
+
'on_mouse_motion', pos_x, pos_y, event.movementX, event.movementY,
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
@CanvasEventHandler("wheel", passive=False)
|
|
674
|
+
def _event_mouse_scroll(self, event):
|
|
675
|
+
event.preventDefault()
|
|
676
|
+
self.dispatch_event('on_mouse_scroll', event.clientX, event.clientY, event.deltaX, event.deltaY)
|
|
677
|
+
|
|
678
|
+
@CanvasEventHandler("mouseenter")
|
|
679
|
+
async def _event_mouse_enter(self, event):
|
|
680
|
+
self.dispatch_event('on_mouse_enter', event.clientX, self.height - event.clientY)
|
|
681
|
+
|
|
682
|
+
@CanvasEventHandler("mouseleave")
|
|
683
|
+
async def _event_mouse_leave(self, event):
|
|
684
|
+
self.dispatch_event('on_mouse_leave', event.clientX, self.height - event.clientY)
|
|
685
|
+
|
|
686
|
+
@CanvasEventHandler("contextlost")
|
|
687
|
+
async def _event_context_lost(self, event):
|
|
688
|
+
print("WebGL context lost!")
|
|
689
|
+
|
|
690
|
+
@CanvasEventHandler("contextrestored")
|
|
691
|
+
async def _event_context_restored(self, event):
|
|
692
|
+
print("WebGL context restored!")
|
|
693
|
+
|
|
694
|
+
@CanvasEventHandler("focus")
|
|
695
|
+
async def _event_gain_focus(self, event):
|
|
696
|
+
await pyglet.app.platform_event_loop.post_event(self, 'on_activate')
|
|
697
|
+
|
|
698
|
+
@CanvasEventHandler("blur")
|
|
699
|
+
async def _event_lose_focus(self, event):
|
|
700
|
+
await pyglet.app.platform_event_loop.post_event(self, 'on_deactivate')
|
|
701
|
+
|
|
702
|
+
@CanvasEventHandler("contextmenu", passive=False)
|
|
703
|
+
def _event_contextmenu(self, event):
|
|
704
|
+
# Some keys or mouse presses (right click) can open the browser context menu. Disable this.
|
|
705
|
+
event.preventDefault()
|
|
706
|
+
|
|
707
|
+
@CanvasEventHandler("fullscreenchange")
|
|
708
|
+
def _event_fullscreen_change(self, event):
|
|
709
|
+
if js.document.fullscreenElement == self._canvas:
|
|
710
|
+
self._enter_fullscreen(event)
|
|
711
|
+
else:
|
|
712
|
+
self._exited_fullscreen(event, *self._windowed_size)
|
|
713
|
+
|
|
714
|
+
def get_framebuffer_size(self):
|
|
715
|
+
if self._context:
|
|
716
|
+
return self._context.gl.drawingBufferWidth, self._context.gl.drawingBufferHeight
|
|
717
|
+
return self.get_size()
|
|
718
|
+
|
|
719
|
+
def _event_resized(self, entries, observer):
|
|
720
|
+
for entry in entries:
|
|
721
|
+
rect = entry.contentRect
|
|
722
|
+
self.dispatch_event('_on_internal_resize', 0, 0)
|
|
723
|
+
|
|
724
|
+
def dispatch_pending_events(self) -> None:
|
|
725
|
+
pass
|
|
726
|
+
|
|
727
|
+
# copy, cut, paste, dragenter, dragleave,dragover, drop
|
|
728
|
+
# touchmove, touchstart, touchend, touchcancel
|
|
729
|
+
|
|
730
|
+
def adjust_scale(self, width: int, height: int) -> None:
|
|
731
|
+
ratio = js.window.devicePixelRatio or 1.0
|
|
732
|
+
|
|
733
|
+
# The framebuffer size.
|
|
734
|
+
self._canvas.width = width * ratio
|
|
735
|
+
self._canvas.height = height * ratio
|
|
736
|
+
|
|
737
|
+
# How large the canvas appears visually. Browser automatically scales based on DPI.
|
|
738
|
+
self._canvas.style.width = f"{width}px"
|
|
739
|
+
self._canvas.style.height = f"{height}px"
|
|
740
|
+
|
|
741
|
+
def _create(self) -> None:
|
|
742
|
+
assert self._canvas is None
|
|
743
|
+
canvas_name = pyglet.options.pyodide.canvas_id
|
|
744
|
+
canvas = js.document.getElementById(canvas_name)
|
|
745
|
+
if not self._canvas:
|
|
746
|
+
self._canvas = js.document.createElement("canvas")
|
|
747
|
+
self._canvas.id = canvas_name
|
|
748
|
+
js.document.body.appendChild(self._canvas)
|
|
749
|
+
else:
|
|
750
|
+
self._canvas = canvas
|
|
751
|
+
|
|
752
|
+
self._canvas.width = self._width * self.scale
|
|
753
|
+
self._canvas.height = self._height * self.scale
|
|
754
|
+
self._canvas.style.width = f"{self._width}px"
|
|
755
|
+
self._canvas.style.height = f"{self._height}px"
|
|
756
|
+
|
|
757
|
+
if not js.document.getElementById(canvas_name):
|
|
758
|
+
msg = f"Canvas: {canvas_name} could not be created."
|
|
759
|
+
raise Exception(msg)
|
|
760
|
+
|
|
761
|
+
# By default, the canvas does not receive keyboard events.
|
|
762
|
+
self._canvas.setAttribute("tabindex", "0")
|
|
763
|
+
self._set_event_handlers()
|
|
764
|
+
|
|
765
|
+
# When the canvas gets focus, it gains a white outline around it. Remove it.
|
|
766
|
+
style = js.document.createElement("style")
|
|
767
|
+
style.innerHTML = "#%s:focus { outline: none; }" % canvas_name
|
|
768
|
+
js.document.head.appendChild(style)
|
|
769
|
+
|
|
770
|
+
# Context must be created after window is created.
|
|
771
|
+
self._assign_config()
|
|
772
|
+
|
|
773
|
+
self.context.attach(self)
|
|
774
|
+
|
|
775
|
+
rect = self._canvas.getBoundingClientRect()
|
|
776
|
+
self.adjust_scale(rect.width, rect.height)
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
__all__ = ['EmscriptenWindow']
|